fix(pm): dispatch-gates' reconciliation line enumerates every OUTSIDE block, not three of five - #16802
Merged
Merged
Conversation
…e of five The reconciliation line closes with the sentence that tells a harvester what its total does NOT cover. It named three blocks -- the pending-changeset families, the unreachable listing and the always-runs tail -- while the same run printed five: the artifact rosters and the declared WIDE population also sit below that line, each under its own heading with its own count, and the file's own conservation equation already counts wide-population as a term. A harvester who follows the enumeration and stops reaches neither. That cost a measured CI round trip: an implementer reported the union derived twice, all green, plus the rosters, plus a whole-repo lint at exit 0, and CI still failed on check:wildcard-fallthrough -- a family printed in the Declared WIDE population block, one of the two the sentence omitted. The list is now built once, as outsideBlockNames in familyReconciliationLines, from the block counts on recon -- the lengths of the very arrays artifactRosterLines and widePopulationLines render, so the enumeration cannot name a set the output does not contain. A count of 0 drops the name, because at zero rows the block is not printed at all. Both branches of the rendering use that one expression, and the docblock that used to restate the list in prose now names the expression instead: the claim exists once. The self-test case pinned only the substring `always-runs tail`, so it stayed green through the whole defect. It now asserts each name separately, asserts that neither block is named on a run that printed neither, and asserts the zero-total branch renders the same list. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P58euzUXCVJNwmhuPC9DXY
The three cases added with the fix asserted the leading name in lower case, but the first name opens the sentence and is raised there -- so they failed on a rendering that was correct. Each name is now matched case-insensitively, and the exact rendered phrase, capitalisation and print order included, is pinned once in its own case where that spelling is the thing being asserted. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P58euzUXCVJNwmhuPC9DXY
This was referenced Sep 8, 2026
hotlong
marked this pull request as ready for review
September 8, 2026 08:39
This was referenced Sep 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #16398
The reconciliation line closes with the sentence that tells a harvester what its
total does not cover. It named three blocks while the same run printed five.
What changed
familyReconciliationLinesnow builds that enumeration once, asoutsideBlockNames, from two block counts carried onrecon— the lengths ofthe very arrays
artifactRosterLinesandwidePopulationLinesare renderedfrom, so the list cannot name a set the output does not contain. A count of
0drops the name, because at zero rows those two render nothing and pointing a
reader "below" at an absent heading is the same defect facing the other way.
Three consequences, all of them the point:
branch used to spell the claim separately and named a single block; it now
renders the same list.
in prose — one claim written out twice, and both copies were wrong. It now
names
outsideBlockNamesas the only place the list exists.the blocks in the order this line promised them.
⛔ The five blocks themselves, the conservation line, every verdict and every
exit code are untouched.
familyReconciliationgains two counts that are notterms of the total and never enter its closure assertion — they sit beside
ciOnlyandnotRunnable, which are outside-the-total counts already.Readings —
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack scripts/pm/dispatch-gates.mjsBEFORE (on
origin/main941232040):AFTER (this branch):
The two new counts are the two headings the same run prints below that line:
Tests
The self-test case that pinned this sentence asked only for the substring
always-runs tail, so it stayed green through the entire defect. It now assertseach name separately, pins the whole phrase in print order, asserts that neither
block is named on a run that printed neither, and asserts the zero-total branch
renders the same list.
Ablation — the mutation is the card's own defect: delete the artifact-roster
entry from
outsideBlockNames, so the enumeration again omits a block the samerun printed.
scripts/pm/dispatch-gates.mjspnpm check:pm-dispatch-gatese933f57c47ffb2d18d42575230856b627795e9c4✗ dispatch-gates self-test: 3 of 1561 case(s) failed.HEAD)b2232d75d7cace982fedffd1686677a64b196cb3✓ dispatch-gates self-test: 1561 cases pass.Restore was
git checkout HEAD -- scripts/pm/dispatch-gates.mjs, proven by theblob hash matching HEAD's and by an empty
git status --porcelain, never by anexit code.
Gates: the 30 families
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderives for this diff were run, all exit 0 — thederived list is byte-identical to the one the dispatching seat derived, so no
family was added by the re-derivation.
pnpm exec eslint scripts/pm/dispatch-gates.mjsexits 0 over 1 file, 0 errors, 0 warnings;type-aware linting is not enabled in
eslint.config.mjs(noparserOptions.project), so this diff cannot move the verdict on a file it doesnot touch. The repo-wide
pnpm lintis CI's run, not claimed here.node scripts/pm/check-governed-merges.mjs --test scripts/pm/dispatch-gates.mjs→
✅ NOT governed — ordinary queue landing applies to a PR with exactly this file list.验收备注
are still short.
--ran(runReconciliationLines) names three of the fiveblocks;
--commands/--json(machineReadableOutput) never discloses thedeclared WIDE population at all — not as a block, not as a count, not in its
closing disclaimer, which names one block only. Filed as [finding] dispatch-gates' other two output lanes disclaim an incomplete OUTSIDE list too — --ran names three of five blocks, and --commands never discloses the WIDE population at all #16795 with a repro
rather than folded in: each lane needs the counts threaded through a different
structure, which is a different verification surface from the one this card's
claim declared. Both can read
outsideBlockNamesonce this lands.written four times in total; the two loci the ruling named are the ones this
PR owns, and the other two are [finding] dispatch-gates' other two output lanes disclaim an incomplete OUTSIDE list too — --ran names three of five blocks, and --commands never discloses the WIDE population at all #16795.
on this branch,
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --residue scripts/objectui-changeset-digest.mjs,exits 0 and still names its family —
pnpm check:objectui-changeset [lint.yml] matched via scripts/objectui-changeset-digest.mjs ⇢ gate script scripts/objectui-changeset-digest.mjs. [finding] dispatch-gates residue: after #12514 the extensionless-target reason is only reachable for a BARE hint, where it names the wrong cause — latent wrong-reason row, zero instances today #12797 and [finding] check-cross-package-test-inputs' docblock states the single-segment hint refusal without its one exception #12808 are held riders onthis surface and are not folded: a different defect shape, each another
lane's card needing its own verification.
skip-changeset: nothing published moves.scripts/pm/**is on thefast-track list — it is not in any package's
files[]and ships to noconsumer.
origin/main6d4d5d3dd, probed from a throwaway bareclone sharing the object store with no merge driver registered, so the reading
is GitHub's question and not the local
os-regendriver's.Generated by Claude Code