Uh oh!
There was an error while loading. Please reload this page.
fix(pm): reconcile dispatch-gates' family sections with a total a harvest can be asserted against - #14221
Merged
Conversation
…vest can be asserted against The human rendering places a card's runnable answer in two differently shaped sections — a path-derived matched block and a kind-derived convention block — and nothing in it stated the union. Two independent devs each harvested one section, ran it green, and reddened CI on a family the other named; a third reader misread the same output a third way. Both machine-readable modes already existed, so the gap was not a missing mode but a missing control: a partial harvest was plausible rather than detectable. - familyReconciliation derives the total and its parts from the SAME two expressions commandsFor unions, so a family added to either input moves the section and the count together. The closure identity is asserted and refuses rather than printing an untrustworthy total. - familyReconciliationLines prints it on every completed derivation, including at zero, with the arithmetic tying it to both sections and the --commands escape hatch named inline. - spellingFooterLines no longer spells its matched-block subtotal as `N families`: that was the line a dropped-section harvest reconciled against, successfully, on the wrong list. It now names its scope and forward-points to the total when families sit outside its block. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV
…fore claiming detection Measured while ablating the call site out of derive(): with no line printed reconTotal is NaN, and `NaN !== convBlock.length` is TRUE — so the case asserting that the total detects a short harvest passed while the remedy was absent. That is the instrument-cannot-fail-toward-its-target shape this card is about, inside the pin for it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV
This was referenced Sep 1, 2026
baozhoutao
marked this pull request as ready for review
September 1, 2026 14:30
Uh oh!
There was an error while loading. Please reload this page.
baozhoutao
deleted the
claude/issue-13642-family-print-reconciliation
branch
September 1, 2026 14:53
This was referenced Sep 2, 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 freeto 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#13642
Two differently-shaped sections carry a card's runnable answer in
dispatch-gates' human rendering, and nothing in that rendering stated their union. Two independent devs each harvested one section, ran it green, and reddened CI on a family the other section had named. This adds the missing control: a total a harvest can be asserted against, derived from the same structure the sections print from.Premise re-check (the card predates PR #14207's +603 lines)
The card describes "two structurally different sections". Re-derived on
24b66352, the human block now has seven sections, of which exactly two carry this card's runnable answer:Local gates for this card- CMD [wf] matched via ...N families — P pnpm, K direct node.Convention-triggered gates- CMD — why, under a per-kind headingOnce a changeset exists, N more...- CMD [wf] would match ...Unreachable — ...Always runs — ...- [wf · job] stepResidue — ...So the card's premise holds, and its taxonomy was two-thirds of the picture. The authority for "what this card owes" is
commandsFor(...)— matched UNION convention, deduped — which is exactly what--commandsand--jsonalready render.The measurement that made the defect reproducible today
On
24b66352, for the cardpackages/spec/src/foo.test.ts:⭐ The only count line adjacent to the harvestable block stated a subtotal in the vocabulary of a total. A reader who harvested the matched block, counted 40, and read the footer got a reconciliation that agreed — on the wrong list. Both incidents on the card lost the convention block specifically, and
check:engine-double-contract— occurrence 2's missing gate — is in the convention block on this very input.What this changes (one file,
scripts/pm/dispatch-gates.mjs)familyReconciliation— the total and its parts, built from the same two expressionscommandsForunions, not from a second traversal. Add a family to either input and both the section and its term move together. The identitymatched + convention − both === totalthen holds by set algebra, and is asserted anyway: a mismatch throws and refuses rather than printing a total that cannot be trusted (check:react-declaration-parity 是唯一没接进任何 workflow 的源码审计门禁,且无 MANIFEST 时静默 skip 退出 0 —— 它现在永远不可能红 #4690).familyReconciliationLines— printed on every completed derivation, including at zero (an absent number is not assertable; its absence would mean "this card owes nothing" and "this build has no reconciliation" at once — the argumentderivealready makes for the tier verdict). Carries the arithmetic, the short-harvest warning where a section really is droppable, the--commandsescape hatch inline, and a disclaimer that the total is not what CI runs.spellingFooterLines— no longer spells its matched-block subtotal asN families. It saysN matched families, and when families sit outside its block it forward-points to the total, at the harvest site, where a consumer who never scrolls further still meets it.⛔ Explicitly not done, per the triage ruling: no new output mode (
--jsonand--commandsboth already exist), noALL FAMILIES:block, no "please read more carefully" prose, and nothing in dispatch-order or teaching material — that limb is skills-lane governed territory.Rendered output, on the input measured above
Verification
Union re-run on the final commit
1644b978.pnpm check:pm-dispatch-gates— the tool's own battery, which is also the self-test surface for this file:26 new cases, all green, including the end-to-end pair that only a real run can hold:
Ablations — both legs proven on disk, both restored byte-identical
A — remove the call site from
derive. Mutation confirmed on disk (call-site occurrences 1 → 0, injected marker 1; blob6dbebbe2→b0c6ed8c), then the real battery run on the mutated tree:⭐ That ablation also found a vacuous assertion in my own pin: with the line absent,
reconTotalisNaN, andNaN !== convBlock.lengthis TRUE — so the CONTROL case asserting the total detects a short harvest passed while the remedy was gone. That is this card's own defect shape inside the pin for it. Fixed in1644b978by requiringNumber.isInteger(reconTotal)first.B — break the closure identity (replace the overlap computation with an independent
0). Declared narrowing: the assert is in a pure function one call fully exercises, so this leg drove the function directly rather than the 9-minute battery. Mutation confirmed on disk (anchor 1 → 0, marker 1; blob differs from HEAD), control call on the unmutated tree returned a closing reconciliation first:and the real human run exited 2 and printed ZERO reconciliation lines — it refuses rather than printing a wrong total.
Restore proven for both, not read off an exit code:
git diff HEADempty,git statusclean, blob hash equal to the HEAD blob,0ablation markers left in the file.Gate family, re-derived from the actual diff on
1644b978node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack(no paths — the script takes the change set off the merge base itself) derives 15 families, and its own new line reconciles them:Reconciliation — 15 famil(ies) ... 15 named by PATH + 0 named by change KIND ⇒ 15 distinct.All 15 run, all green:pnpm check:pm-dispatch-gatespnpm check:declared-population-livepnpm check:watch-hint-literalpnpm check:parse-guardpnpm check:entry-guardpnpm check:agent-test-spellingpnpm check:bash32-floorpnpm check:cli-command-idspnpm check:pnpm-filter-targetspnpm check:cross-package-test-inputsnode scripts/check-cross-package-test-inputs.mjsnode scripts/check-self-test-wired.mjsnode scripts/check-ci-filter-parity.mjsnode scripts/check-shard-attestation.mjsnode scripts/check-test-completeness.mjscheck-test-completenessexits 3 = PREREQUISITE NOT MET, its own words: it grades a savedturbo run testlog, there is none locally, and its message says the derived family names it with no argument and "the local reading for this gate is NOT MEASURED. ⛔ It is not a red." The other two exited 3 for the same class (yamlnot installed in a fresh worktree) and were re-run to a real 0 afterpnpm install.Also run, per the dispatch:
node scripts/pm/bare-root-worklist.mjs --self-test(0),node scripts/check-ratchet-remedy-authority.mjs(0),pnpm check:nul-bytes(0, plus a directgrep -naPcontrol-byte scan of the changed file — none).Every exit code above was captured before any pipe (
cmd > log 2>&1; EXIT=$?), and each verdict is quoted from the gate's own line.ESLint — a declared, measured narrowing, not a skip
The repo-wide
pnpm lintis CI's run. Locally this was narrowed to the one changed file, with the three things that make a narrowing a measurement:eslint.config.mjs, resolved by eslint itself; the run used the same--no-inline-configthe rootlintscript uses.--format json— 1 file linted, 0 errors, 0 warnings.eslint.config.mjs, theQUERY_OPTIONS_TEST_GLOBSheader) states this repo "runs oneeslint.config.mjs, which never enables type-aware linting (noparserOptions.project, no typed@typescript-eslintrules) for ANY file", measured there with a positive control. A one-file.mjsedit therefore cannot move any untouched file's verdict.Stale-tree warning, checked rather than carried
The derivation warns that
scripts/check-stack-collection-maps.mjschanged onorigin/mainafter this branch point. Read: the literals its new version names are its own file path, which does not coverscripts/pm/dispatch-gates.mjs(a barescriptsword is refused as too generic), so it does not change this card's family. Recorded as examined, not as an unread warning.Notes for review
skip-changeset— the diff isscripts/pm/**only and publishes nothing from any package.--commandsinline. The file header already says it, but the header is not where a harvesting consumer looks, and the triage's own diagnosis is that adoption — not availability — is what failed. Flagged as a deliberate choice rather than a silent one.Generated by Claude Code
Generated by Claude Code