You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
tooling(pm): derive the line ratchet's lane roster from the lanes/ directory (#16437)
The self-test's lane-roster case was an `.every` over eight literal lane
names, so the ninth lane file was pinned by nothing: a ceiling row put
there for it kept the case green without the case ever naming it, and
deleting that row again was caught only by the map-wide cases, which say
nothing about a row that is simply gone.
The roster is now read from the directory (`readdirSync`, every `*.md`,
sorted) and held against the ceiling map, so the tree and the map are two
independent sources checked against one another. A new lane file is
pinned by construction: no list to extend, and no count in the label to
keep in step with the list.
A derived roster has one failure mode of its own, and it is the same
shape as the defect above -- `[].every(...)` is `true`, so an unreadable
directory would report perfect coverage of nothing. The verdict refuses
an empty roster outright, and each red path carries a fixture case beside
the live one, because a red path that stopped working runs green forever.
Moves named: the pinned case label and its expectation-table entry move
together (duplicate-label refusal); one case becomes three, so the
self-test runs 155 -> 157 cases and SELF_TEST_BATTERY_FLOOR moves
155 -> 157, with the two prose readings of that same number moved with
it. No ceiling row changed; no other file.
Claude-Session: https://claude.ai/code/session_019RfFHiRCSs3JXLK4cwcfox
Co-authored-by: Claude <noreply@anthropic.com>
msg: `${uncovered.map((f)=>`${LANES_DIR}${f}`).join(', ')} — lane job description(s) in the tree carrying no ceiling row, so the ratchet does not read them at all. Give each one a row keyed by that path, or take the lane file out of the tree.`,
1970
+
};
1971
+
}
1972
+
return{ok: true,msg: `all ${laneFiles.length} lane/seat job descriptions in the tree carry a ceiling row.`};
1973
+
}
1974
+
1911
1975
functionselfTest(){
1912
1976
constrel='.claude/skills/pm-dispatch/SKILL.md';
1977
+
// Fixture for the derived roster's naming red path: a tree that carries
1978
+
// triage.md, against a map holding a row for engine.md only.
['under the ceiling -> green',verdict(rel,2900,3050).ok,true],
1915
1982
['at the ceiling -> green',verdict(rel,3050,3050).ok,true],
@@ -1922,7 +1989,9 @@ function selfTest() {
1922
1989
['SKILL.md is covered',CEILINGS.has('.claude/skills/pm-dispatch/SKILL.md'),true],
1923
1990
['the dev-agent definition is covered',CEILINGS.has('.claude/agents/os-dev.md'),true],
1924
1991
['all five compressed references are covered',['dispatch-runbook','platform-readings','review-checklist','landing-operations','seat-post-protocol'].every((n)=>CEILINGS.has(`.claude/skills/pm-dispatch/references/${n}.md`)),true],
1925
-
['all eight lane/seat job descriptions are covered',['engine','services','cli','devx','skills','spec','hotcrm','director'].every((n)=>CEILINGS.has(`.claude/skills/pm-dispatch/references/lanes/${n}.md`)),true],
1992
+
['every lane/seat job description in the tree carries a ceiling row',laneRosterVerdict(laneFilesOnDisk(),CEILINGS).ok,true],
1993
+
['...and one carrying no row is RED, with the message naming that file',!laneGap.ok&&laneGap.msg.includes('triage.md'),true],
1994
+
['...and an EMPTY roster is RED, not vacuously green: a derived roster checks nothing when the read fails',laneRosterVerdict([],CEILINGS).ok,false],
1926
1995
['the other four skills are covered (#9473)',['checklist-test','checklist-author','dogfood-verification','spec-property-retirement'].every((n)=>CEILINGS.has(`.claude/skills/${n}/SKILL.md`)),true],
1927
1996
['root AGENTS.md is covered (#9792)',CEILINGS.has('AGENTS.md'),true],
1928
1997
['root CLAUDE.md is covered (#9965)',CEILINGS.has('CLAUDE.md'),true],
0 commit comments