@@ -93,6 +93,34 @@ function writeAlignedTruthFixtures() {
9393 writeFile ( '.planning/ROADMAP.md' , '- [ ] **Phase 16: Framework Health & Truth Reconciliation** — [LAUNCH-07]\n' ) ;
9494}
9595
96+ function writeWorkflowInventoryReadme ( { heading = '## Workflow Surface' , rows = [ 'alpha.md' , 'beta.md' ] , treeLines } ) {
97+ const tableIntro = heading . startsWith ( '## Current Status' )
98+ ? [ '| Workflow | Status | Notes |' , '|----------|--------|-------|' ]
99+ : [ '| Workflow | What ships |' , '|----------|------------|' ] ;
100+ const tableRows = rows . map ( ( file ) => `| \`${ file } \` | x |` ) ;
101+ writeFile ( 'distilled/README.md' , [
102+ heading ,
103+ '' ,
104+ ...tableIntro ,
105+ ...tableRows ,
106+ '' ,
107+ 'Architecture notes:' ,
108+ '' ,
109+ '## Files In This Framework' ,
110+ '' ,
111+ '```' ,
112+ ...( treeLines || [
113+ 'distilled/' ,
114+ ' workflows/' ,
115+ ' alpha.md' ,
116+ ' beta.md' ,
117+ ' templates/' ,
118+ ] ) ,
119+ '```' ,
120+ '' ,
121+ ] . join ( '\n' ) ) ;
122+ }
123+
96124function writeForkHonestAlignmentFixtures ( ) {
97125 writeFile ( '.internal-research/gaps.md' , [
98126 'Historical checkpoint evidence is recorded against the active checkpoint file rather than a stale missing repo path.' ,
@@ -323,6 +351,28 @@ describe('Health — WARN: ROADMAP references nonexistent phase', () => {
323351 const json = JSON . parse ( result . output ) ;
324352 assert . ok ( ! json . warnings . some ( ( w ) => w . id === 'W4' ) , 'should ignore future planned phases' ) ;
325353 } ) ;
354+
355+ test ( 'active phase with only non-lifecycle artifacts → W4 without W5' , async ( ) => {
356+ await initWorkspace ( ) ;
357+ fs . writeFileSync (
358+ path . join ( tmpDir , '.planning' , 'ROADMAP.md' ) ,
359+ '# Roadmap\n\n- [x] **Phase 47: Synthesis And v1.7 Plan**\n'
360+ ) ;
361+ const phaseDir = path . join ( tmpDir , '.planning' , 'phases' , '47-synthesis-and-v1-7-plan' ) ;
362+ fs . mkdirSync ( phaseDir , { recursive : true } ) ;
363+ fs . writeFileSync (
364+ path . join ( phaseDir , '47-v1.7-IMPLEMENTATION-PLAN.md' ) ,
365+ '# Next Milestone Implementation Plan\n'
366+ ) ;
367+
368+ const result = await runCliAsMain ( tmpDir , [ 'health' , '--json' ] ) ;
369+ const json = JSON . parse ( result . output ) ;
370+
371+ assert . ok ( json . warnings . some ( ( w ) => w . id === 'W4' && / P h a s e 4 7 / . test ( w . message ) ) ,
372+ 'non-lifecycle artifacts must not make active phase health clean' ) ;
373+ assert . ok ( ! json . warnings . some ( ( w ) => w . id === 'W5' ) ,
374+ 'non-lifecycle artifacts must not create stale PLAN/SUMMARY warnings' ) ;
375+ } ) ;
326376} ) ;
327377
328378describe ( 'Health — WARN: phase with PLAN but no SUMMARY' , ( ) => {
@@ -404,6 +454,126 @@ describe('Health — WARN: adapter and truth drift detection', () => {
404454 assert . ok ( json . warnings . some ( ( w ) => w . id === 'W8' ) ) ;
405455 } ) ;
406456
457+ test ( 'current Workflow Surface inventory shape → no W8' , async ( ) => {
458+ await initWorkspace ( ) ;
459+ writeAlignedTruthFixtures ( ) ;
460+ writeWorkflowInventoryReadme ( { heading : '## Workflow Surface' } ) ;
461+
462+ const result = await runCliAsMain ( tmpDir , [ 'health' , '--json' ] ) ;
463+ const json = JSON . parse ( result . output ) ;
464+
465+ assert . ok ( ! json . warnings . some ( ( w ) => w . id === 'W8' ) ,
466+ 'current Workflow Surface table and plain tree shape should align with workflows dir' ) ;
467+ } ) ;
468+
469+ test ( 'legacy Current Status inventory shape → no W8' , async ( ) => {
470+ await initWorkspace ( ) ;
471+ writeAlignedTruthFixtures ( ) ;
472+ writeWorkflowInventoryReadme ( { heading : '## Current Status (updated 2026-04-10)' } ) ;
473+
474+ const result = await runCliAsMain ( tmpDir , [ 'health' , '--json' ] ) ;
475+ const json = JSON . parse ( result . output ) ;
476+
477+ assert . ok ( ! json . warnings . some ( ( w ) => w . id === 'W8' ) ,
478+ 'legacy Current Status table should remain compatible with workflows dir' ) ;
479+ } ) ;
480+
481+ test ( 'framework tree glyph inventory shape → no W8' , async ( ) => {
482+ await initWorkspace ( ) ;
483+ writeAlignedTruthFixtures ( ) ;
484+ writeWorkflowInventoryReadme ( {
485+ heading : '## Workflow Surface' ,
486+ treeLines : [
487+ 'distilled/' ,
488+ '├── workflows/' ,
489+ '│ ├── alpha.md' ,
490+ '│ └── beta.md' ,
491+ '└── templates/' ,
492+ ] ,
493+ } ) ;
494+
495+ const result = await runCliAsMain ( tmpDir , [ 'health' , '--json' ] ) ;
496+ const json = JSON . parse ( result . output ) ;
497+
498+ assert . ok ( ! json . warnings . some ( ( w ) => w . id === 'W8' ) ,
499+ 'tree glyph framework inventory should align with workflows dir' ) ;
500+ } ) ;
501+
502+ test ( 'missing workflow table entry → W8' , async ( ) => {
503+ await initWorkspace ( ) ;
504+ writeAlignedTruthFixtures ( ) ;
505+ writeWorkflowInventoryReadme ( { rows : [ 'alpha.md' ] } ) ;
506+
507+ const result = await runCliAsMain ( tmpDir , [ 'health' , '--json' ] ) ;
508+ const json = JSON . parse ( result . output ) ;
509+
510+ const warning = json . warnings . find ( ( w ) => w . id === 'W8' ) ;
511+ assert . ok ( warning , 'missing workflow table entry should warn' ) ;
512+ assert . match ( warning . message , / m i s s i n g f r o m s t a t u s t a b l e : b e t a \. m d / ) ;
513+ } ) ;
514+
515+ test ( 'canonical Workflow Surface table is not masked by legacy Current Status rows' , async ( ) => {
516+ await initWorkspace ( ) ;
517+ writeAlignedTruthFixtures ( ) ;
518+ writeFile ( 'distilled/README.md' , [
519+ '## Workflow Surface' ,
520+ '' ,
521+ '| Workflow | What ships |' ,
522+ '|----------|------------|' ,
523+ '| `alpha.md` | x |' ,
524+ '' ,
525+ 'Architecture notes:' ,
526+ '' ,
527+ '## Current Status (updated 2026-04-10)' ,
528+ '' ,
529+ '| Workflow | Status | Notes |' ,
530+ '|----------|--------|-------|' ,
531+ '| `alpha.md` | x |' ,
532+ '| `beta.md` | x |' ,
533+ '' ,
534+ 'Architecture notes:' ,
535+ '' ,
536+ '## Files In This Framework' ,
537+ '' ,
538+ '```' ,
539+ 'distilled/' ,
540+ ' workflows/' ,
541+ ' alpha.md' ,
542+ ' beta.md' ,
543+ ' templates/' ,
544+ '```' ,
545+ '' ,
546+ ] . join ( '\n' ) ) ;
547+
548+ const result = await runCliAsMain ( tmpDir , [ 'health' , '--json' ] ) ;
549+ const json = JSON . parse ( result . output ) ;
550+
551+ const warning = json . warnings . find ( ( w ) => w . id === 'W8' ) ;
552+ assert . ok ( warning , 'canonical Workflow Surface drift should warn even when legacy Current Status is complete' ) ;
553+ assert . match ( warning . message , / m i s s i n g f r o m s t a t u s t a b l e : b e t a \. m d / ) ;
554+ } ) ;
555+
556+ test ( 'missing workflow tree entry → W8' , async ( ) => {
557+ await initWorkspace ( ) ;
558+ writeAlignedTruthFixtures ( ) ;
559+ writeWorkflowInventoryReadme ( {
560+ rows : [ 'alpha.md' , 'beta.md' ] ,
561+ treeLines : [
562+ 'distilled/' ,
563+ ' workflows/' ,
564+ ' alpha.md' ,
565+ ' templates/' ,
566+ ] ,
567+ } ) ;
568+
569+ const result = await runCliAsMain ( tmpDir , [ 'health' , '--json' ] ) ;
570+ const json = JSON . parse ( result . output ) ;
571+
572+ const warning = json . warnings . find ( ( w ) => w . id === 'W8' ) ;
573+ assert . ok ( warning , 'missing workflow tree entry should warn' ) ;
574+ assert . match ( warning . message , / m i s s i n g f r o m f r a m e w o r k t r e e : b e t a \. m d / ) ;
575+ } ) ;
576+
407577 test ( 'gaps.md stale repo-local path reference → W9' , async ( ) => {
408578 await initWorkspace ( ) ;
409579 writeFile ( '.internal-research/gaps.md' , 'Missing file: `distilled/missing.md`\n' ) ;
0 commit comments