Skip to content

fix(pm): dispatch-gates' reconciliation line enumerates every OUTSIDE block, not three of five - #16802

Merged
hotlong merged 2 commits into
mainfrom
claude/issue-16398-reconciliation-outside-blocks
Sep 8, 2026
Merged

fix(pm): dispatch-gates' reconciliation line enumerates every OUTSIDE block, not three of five#16802
hotlong merged 2 commits into
mainfrom
claude/issue-16398-reconciliation-outside-blocks

Conversation

@hotlong

@hotlong hotlong commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

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

familyReconciliationLines now builds that enumeration once, as
outsideBlockNames, from two block counts carried on recon — the lengths of
the very arrays artifactRosterLines and widePopulationLines are rendered
from, so the list cannot name a set the output does not contain. A count of 0
drops 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:

  • Both branches of the rendering use that one expression. The zero-total
    branch used to spell the claim separately and named a single block; it now
    renders the same list.
  • The docblock no longer restates the list. It named the same three blocks
    in prose — one claim written out twice, and both copies were wrong. It now
    names outsideBlockNames as the only place the list exists.
  • The names are in print order, so a reader walking down the output meets
    the blocks in the order this line promised them.

⛔ The five blocks themselves, the conservation line, every verdict and every
exit code are untouched. familyReconciliation gains two counts that are not
terms of the total and never enter its closure assertion — they sit beside
ciOnly and notRunnable, which are outside-the-total counts already.

Readings — node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack scripts/pm/dispatch-gates.mjs

BEFORE (on origin/main 941232040):

  ⛔ 30 is what THIS CARD owes by path and kind — NOT a complete account of what CI runs on the PR.
  The pending-changeset families, the unreachable listing and the always-runs tail below are each
  OUTSIDE it, each with its own count.

AFTER (this branch):

  ⛔ 30 is what THIS CARD owes by path and kind — NOT a complete account of what CI runs on the PR.
  The 44 artifact-roster famil(ies), the 10 declared WIDE-population famil(ies), the
  pending-changeset families, the unreachable listing and the always-runs tail below are each
  OUTSIDE it, each with its own count.

The two new counts are the two headings the same run prints below that line:

Artifact rosters — 44 famil(ies) whose `silent` verdict is a fact about a LIST, not about your paths:
Declared WIDE population — 10 famil(ies) DECLARE that CI runs them over a population too wide to place, …

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 asserts
each 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 same
run printed.

leg blob of scripts/pm/dispatch-gates.mjs pnpm check:pm-dispatch-gates
mutated (artifact-roster entry deleted) e933f57c47ffb2d18d42575230856b627795e9c4 exit 1✗ dispatch-gates self-test: 3 of 1561 case(s) failed.
restored (HEAD) b2232d75d7cace982fedffd1686677a64b196cb3 exit 0✓ dispatch-gates self-test: 1561 cases pass.

Restore was git checkout HEAD -- scripts/pm/dispatch-gates.mjs, proven by the
blob hash matching HEAD's and by an empty git status --porcelain, never by an
exit code.

Gates: the 30 families node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derives for this diff were run, all exit 0 — the
derived 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.mjs exits 0 over 1 file, 0 errors, 0 warnings;
type-aware linting is not enabled in eslint.config.mjs (no
parserOptions.project), so this diff cannot move the verdict on a file it does
not touch. The repo-wide pnpm lint is 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.

验收备注


Generated by Claude Code

…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
@github-actions github-actions Bot added the size/m label Sep 8, 2026
@hotlong hotlong added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 8, 2026 — with Claude
@hotlong
hotlong marked this pull request as ready for review September 8, 2026 08:39
@hotlong
hotlong added this pull request to the merge queue Sep 8, 2026
Merged via the queue into main with commit 7f96e14 Sep 8, 2026
36 checks passed
@hotlong
hotlong deleted the claude/issue-16398-reconciliation-outside-blocks branch September 8, 2026 09:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants