@@ -29,6 +29,23 @@ const validBody = [
2929 '- None.'
3030] . join ( '\n' ) ;
3131
32+ const draftBody = [
33+ 'Refs #12345' ,
34+ '' ,
35+ 'Authored by Euclid (GPT-5, Codex Desktop). Session test.' ,
36+ '' ,
37+ 'Evidence: L2 local unit coverage. Draft-only helper; no close target yet.' ,
38+ '' ,
39+ '## Deltas from ticket' ,
40+ '- Draft helper extracted before the leaf close target is complete.' ,
41+ '' ,
42+ '## Test Evidence' ,
43+ '- npm run test-unit -- test/playwright/unit/ai/buildScripts/util/agent-preflight.spec.mjs' ,
44+ '' ,
45+ '## Post-Merge Validation' ,
46+ '- None while draft.'
47+ ] . join ( '\n' ) ;
48+
3249test . describe ( 'agent-preflight utility' , ( ) => {
3350 test ( 'builds the Commander program with the expected option surface' , ( ) => {
3451 const program = createProgram ( ) ;
@@ -38,16 +55,18 @@ test.describe('agent-preflight utility', () => {
3855 expect ( help ) . toContain ( 'default mode may repair' ) ;
3956 expect ( help ) . toContain ( 'block alignment' ) ;
4057 expect ( help ) . toContain ( '--pr-body <file>' ) ;
58+ expect ( help ) . toContain ( '--pr-draft' ) ;
4159 expect ( help ) . toContain ( '--no-fix' ) ;
4260 expect ( help ) . toContain ( 'Check-only mode' )
4361 } ) ;
4462
4563 test ( 'parses files, optional PR body, and fix mode through Commander' , ( ) => {
46- expect ( parseArgs ( [ '--pr-body' , 'body.md' , '--no-fix' , 'src/a.mjs' ] ) ) . toEqual ( {
47- files : [ 'src/a.mjs' ] ,
48- fix : false ,
49- help : false ,
50- prBody : 'body.md'
64+ expect ( parseArgs ( [ '--pr-body' , 'body.md' , '--pr-draft' , '--no-fix' , 'src/a.mjs' ] ) ) . toEqual ( {
65+ files : [ 'src/a.mjs' ] ,
66+ fix : false ,
67+ help : false ,
68+ prBody : 'body.md' ,
69+ prDraft : true
5170 } ) ;
5271 } ) ;
5372
@@ -64,6 +83,28 @@ test.describe('agent-preflight utility', () => {
6483 expect ( validatePrBody ( validBody ) . valid ) . toBe ( true )
6584 } ) ;
6685
86+ test ( 'accepts a draft PR body with a non-closing reference instead of Resolves' , ( ) => {
87+ const result = validatePrBody ( draftBody , { draft : true } ) ;
88+
89+ expect ( result . valid ) . toBe ( true ) ;
90+ expect ( result . missingVisible ) . toEqual ( [ ] ) ;
91+ expect ( result . missingInvisible ) . toEqual ( [ ] )
92+ } ) ;
93+
94+ test ( 'keeps Refs-only bodies invalid for ready PR validation' , ( ) => {
95+ const result = validatePrBody ( draftBody ) ;
96+
97+ expect ( result . valid ) . toBe ( false ) ;
98+ expect ( result . missingVisible ) . toContain ( '`Resolves #N` is required' )
99+ } ) ;
100+
101+ test ( 'requires at least a non-closing issue reference for draft bodies without Resolves' , ( ) => {
102+ const result = validatePrBody ( draftBody . replace ( 'Refs #12345' , 'Draft-only note.' ) , { draft : true } ) ;
103+
104+ expect ( result . valid ) . toBe ( false ) ;
105+ expect ( result . missingVisible ) . toContain ( 'Draft PR bodies without `Resolves #N` require `Refs #N` or `Related: #N`' )
106+ } ) ;
107+
67108 test ( 'reports visible misses and forbidden close keywords without naming structural anchors' , ( ) => {
68109 const result = validatePrBody ( [
69110 'Closes #12345' ,
@@ -178,6 +219,26 @@ test.describe('agent-preflight utility', () => {
178219 expect ( stdout ) . toContain ( 'agent-preflight: PR body contains the required template anchors.' )
179220 } ) ;
180221
222+ test ( 'runs the draft PR body gate when requested' , ( ) => {
223+ let stdout = '' ;
224+ let stderr = '' ;
225+
226+ const status = runAgentPreflight ( {
227+ argv : [ '--pr-body' , 'body.md' , '--pr-draft' ] ,
228+ cwd : '/repo' ,
229+ execFileSyncImpl : cmd => cmd === 'git' ? '' : '' ,
230+ existsSyncImpl : ( ) => true ,
231+ readFileSyncImpl : ( ) => draftBody ,
232+ stderr : { write : value => { stderr += value } } ,
233+ stdout : { write : value => { stdout += value } }
234+ } ) ;
235+
236+ expect ( status ) . toBe ( 0 ) ;
237+ expect ( stderr ) . toBe ( '' ) ;
238+ expect ( stdout ) . toContain ( 'agent-preflight: 0 .mjs files in scope; skipped source gates.' ) ;
239+ expect ( stdout ) . toContain ( 'agent-preflight: PR body contains the required template anchors.' )
240+ } ) ;
241+
181242 test ( 'returns failure when the local PR body is missing required anchors' , ( ) => {
182243 let stderr = '' ;
183244
0 commit comments