@@ -291,6 +291,54 @@ test('N3: consuming the same directive twice is idempotent (still no pending)',
291291 }
292292} ) ;
293293
294+ test ( 'N3: consuming a directiveId that matches NO directive on the ticket is refused (404, nothing appended)' , async ( ) => {
295+ const dir = fixture ( { 'T-1' : { title : 'A' , track : 'full' , stage : 'code_review' } } ) ;
296+ try {
297+ // a real directive exists, but the caller references a different (typo) id
298+ appendComment ( dir , 'T-1' , { author : '/rev' , kind : 'directive' , body : 'do X' , target : [ '/be' ] } ) ;
299+ const file = path . join ( dir , '.aidevteam' , 'comments' , 'T-1.jsonl' ) ;
300+ const before = fs . readFileSync ( file , 'utf8' ) ;
301+ const res = await api . handle ( 'directive/consume' , { id : 'T-1' , directiveId : 'no-such-id' , by : '/be' } , dir ) ;
302+ assert . equal ( res . code , 404 , 'an unknown directiveId is refused' ) ;
303+ const after = fs . readFileSync ( file , 'utf8' ) ;
304+ assert . equal ( after , before , 'nothing was appended — the JSONL is byte-identical' ) ;
305+ } finally {
306+ fs . rmSync ( dir , { recursive : true , force : true } ) ;
307+ }
308+ } ) ;
309+
310+ test ( 'N3: consuming a real directive returns 200, appends the marker, and drops it from pending' , async ( ) => {
311+ const dir = fixture ( { 'T-1' : { title : 'A' , track : 'full' , stage : 'code_review' } } ) ;
312+ try {
313+ const d = appendComment ( dir , 'T-1' , { author : '/rev' , kind : 'directive' , body : 'do X' , target : [ '/be' ] } ) ;
314+ const res = await api . handle ( 'directive/consume' , { id : 'T-1' , directiveId : d . id , by : '/be' } , dir ) ;
315+ assert . equal ( res . code , 200 ) ;
316+ const marker = readComments ( dir , 'T-1' ) . find ( ( c ) => c . kind === 'directive-consumed' && c . ref === d . id ) ;
317+ assert . ok ( marker , 'a typed consumed marker was appended for the real directive' ) ;
318+ const t1 = buildState ( dir ) . tickets . find ( ( t ) => t . id === 'T-1' ) ;
319+ assert . equal ( t1 . pendingDirectives . length , 0 , 'the consumed directive is no longer pending' ) ;
320+ } finally {
321+ fs . rmSync ( dir , { recursive : true , force : true } ) ;
322+ }
323+ } ) ;
324+
325+ test ( 'N3: consuming an already-consumed real directive stays 200 (idempotent, no error)' , async ( ) => {
326+ const dir = fixture ( { 'T-1' : { title : 'A' , track : 'full' , stage : 'code_review' } } ) ;
327+ try {
328+ const d = appendComment ( dir , 'T-1' , { author : '/rev' , kind : 'directive' , body : 'do X' , target : [ '/be' ] } ) ;
329+ const first = await api . handle ( 'directive/consume' , { id : 'T-1' , directiveId : d . id , by : '/be' } , dir ) ;
330+ assert . equal ( first . code , 200 ) ;
331+ // the directive still EXISTS on the ticket, so a repeat consume is a harmless no-op,
332+ // NOT refused — existence is what's validated, not pending-ness
333+ const second = await api . handle ( 'directive/consume' , { id : 'T-1' , directiveId : d . id , by : '/be' } , dir ) ;
334+ assert . equal ( second . code , 200 , 'a repeat consume of an existing directive is idempotent, not a 404' ) ;
335+ const t1 = buildState ( dir ) . tickets . find ( ( t ) => t . id === 'T-1' ) ;
336+ assert . equal ( t1 . pendingDirectives . length , 0 ) ;
337+ } finally {
338+ fs . rmSync ( dir , { recursive : true , force : true } ) ;
339+ }
340+ } ) ;
341+
294342test ( 'N3b: rendering the digest mutates nothing (no auto-clear on read)' , ( ) => {
295343 const dir = fixture ( { 'T-1' : { title : 'A' , track : 'full' , stage : 'code_review' , assignee : '/rev' } } ) ;
296344 try {
0 commit comments