Found while implementing #13629 (scoping check-half-states' per-window summary assertions). Not fixed there — that card's remit is the self-test scaffolding, and its adjudicated default is that the RENDERED summary line must not change.
The shape
Every window's clause on summaryLine ends with . — except H19's. H19's is assembled as:
`Blocker liveness (H19): targets resolved on ${btResolved} of ${btTargets} distinct \`Blocked-by:\` ` +
`target(s) named by open \`pm:blocked\` card(s)` +
`${ btResolved < btTargets ? ` — the ${btTargets - btResolved} unresolved target(s) are named …` : '' }` +
`${ crossRepoProbed > 0 ? ` Cross-repo reachability … ⛔ no re-run resolves them.` : '' } ` +
`Dispatch liveness (H20 + H27): …`
The terminating period exists only inside the cross-repo branch. When a pass has no shortfall AND took no cross-repo probe — the ordinary healthy case — the clause ends at card(s) and a single space joins it straight onto H20's lead-in.
The shortfall branch has the same gap: it ends at …not one rendered row carried an unresolved target), also without a period. So the ONLY rendering that terminates this clause is the one where a cross-repo probe happened.
Measured, not suspected
Live read-only patrol run 2026-08-31 against objectstack-ai/objectstack (from the #13629 worktree at commit 8813cf1, node scripts/pm/check-half-states.mjs, exit 0):
… targets resolved on 27 of 27 distinct `Blocked-by:` target(s) named by open `pm:blocked` card(s) Dispatch liveness (H20 + H27): remote branch read on 9 of 9 distinct claimed branch(es) …
Two sentences run together. Both neighbouring clauses on that same line end . normally.
Why this is more than punctuation
The summary line is the only half a report reader sees. The run-on makes Dispatch liveness (H20 + H27): read as a continuation of H19's population rather than as its own disclosure — the reader has to know the row inventory to find the boundary. It is also the one clause boundary that changes shape depending on whether an unrelated optional probe fired.
No self-test case pins H19's terminator today. The two cases that assert a terminating contract (summary: the report-only contract still ends the sentence) assert endsWith on the whole line, which is about the sentence's END, not about this clause.
Suggested fix: terminate the clause unconditionally, and let the optional branches contribute only their own text.
File surface: scripts/pm/check-half-states.mjs (summaryLine).
Filed unassigned, for triage.
Generated by Claude Code
Generated by Claude Code
Found while implementing #13629 (scoping
check-half-states' per-window summary assertions). Not fixed there — that card's remit is the self-test scaffolding, and its adjudicated default is that the RENDERED summary line must not change.The shape
Every window's clause on
summaryLineends with.— except H19's. H19's is assembled as:The terminating period exists only inside the cross-repo branch. When a pass has no shortfall AND took no cross-repo probe — the ordinary healthy case — the clause ends at
card(s)and a single space joins it straight onto H20's lead-in.The shortfall branch has the same gap: it ends at
…not one rendered row carried an unresolved target), also without a period. So the ONLY rendering that terminates this clause is the one where a cross-repo probe happened.Measured, not suspected
Live read-only patrol run 2026-08-31 against
objectstack-ai/objectstack(from the #13629 worktree at commit 8813cf1,node scripts/pm/check-half-states.mjs, exit 0):Two sentences run together. Both neighbouring clauses on that same line end
.normally.Why this is more than punctuation
The summary line is the only half a report reader sees. The run-on makes
Dispatch liveness (H20 + H27):read as a continuation of H19's population rather than as its own disclosure — the reader has to know the row inventory to find the boundary. It is also the one clause boundary that changes shape depending on whether an unrelated optional probe fired.No self-test case pins H19's terminator today. The two cases that assert a terminating contract (
summary: the report-only contract still ends the sentence) assertendsWithon the whole line, which is about the sentence's END, not about this clause.Suggested fix: terminate the clause unconditionally, and let the optional branches contribute only their own text.
File surface:
scripts/pm/check-half-states.mjs(summaryLine).Filed unassigned, for triage.
Generated by Claude Code
Generated by Claude Code