Uh oh!
There was an error while loading. Please reload this page.
fix(pm): re-sync the ported half-state sweeper and pin it against upstream - #6672
Merged
Merged
Conversation
…tream The ported `scripts/pm/check-half-states.mjs` had drifted 4,637 lines behind objectstack with nothing able to detect it. Re-synced verbatim to a named upstream commit, preserving the declared divergences, and added a parity gate so the next drift is red rather than silent. Measured before, on 2026-08-28: ported copy 9,340 lines / 1,116 self-test cases objectstack upstream 12,948 lines / 1,574 self-test cases diff between them 4,637 lines The re-sync is mechanical: upstream's blob plus 14 declared divergences, each an exact text pair, applied forward. `scripts/upstream-port-pin.json` carries the upstream commit, the SHA-256 of its blob and those pairs; `scripts/check-upstream-port-parity.mjs` reverses them out of the ported files and requires the reconstruction to hash to the pinned digest. It fetches nothing — the digest is the input. `scripts/invoked-as.mjs` needed no re-sync: its predicate is already byte-identical to upstream's. Its nine port adaptations (prose and one fixture) are declared in the same pin, so it is covered too. Three divergences are new and were DISCOVERED by the re-sync rather than authored by it: upstream's H32 self-test rows compare a seat title's `@ <repo>` suffix against the LIVE resolved sweep repo, so their specimens invert here and a verbatim copy fails its own suite. That class is upstream's to retire. Co-Authored-By: Claude <noreply@anthropic.com>
This was referenced Aug 28, 2026
os-litant
marked this pull request as ready for review
August 28, 2026 13:09
Uh oh!
There was an error while loading. Please reload this page.
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#6642
The ported
scripts/pm/check-half-states.mjsis re-synced to a named upstream commit, and a parity gate now pins it there so the next drift is red instead of silent.The drift, before and after
Measured 2026-08-28 against
objectstack-ai/objectstackat2b4178aa53ca62089f43e2cfae0b7838cf340dd1:--self-testcasesdiffvs upstreamSo 458 predicate cases and ~3,600 lines of upstream fixes had never arrived here, while the patrol went on rendering a confident anchor body with the corresponding rows simply absent. The re-synced copy is now larger than upstream (13,089 vs 12,948) because the objectui-only closed-window mechanism sits on top of it.
scripts/invoked-as.mjswas measured too, and needed no re-sync: its predicate is byte-identical to upstream's already. Only its prose and one self-test fixture diverge (nine adaptations from the port), and those are now declared in the same pin rather than left undeclared.The re-sync is mechanical, not hand-merged
The ported file is
upstream blob + 14 declared divergences applied forward, each divergence an exact text pair sliced out of the two real files. Nothing was retyped, and the pairs round-trip: reversing them out of the result reproduces upstream's blob byte for byte.Divergences preserved (the workflow header's checklist, plus what it did not know about):
DEFAULT_SWEEP_REPO = 'objectstack-ai/objectui'— a bare terminal run here sweeps THIS board. Two of upstream's own self-test rows pinned the objectstack leg against that constant (equal strings upstream, not here), so they are pinned to their literal instead and the default gets its own row.default-sweep-repo,sweep-repo-self-test.PM_SWEEP_CLOSED_WINDOW_PAGES/resolveClosedWindowPages, authored during the port and never upstreamed, with itsCLOSED_WINDOWconstant, its request-free early return, theclosedWindowDisabledcount, the UNREAD-not-clean summary branch, the exit-2 refusal of a malformed page count, and its self-test rows. Entries:closed-window-resolver,closed-window-fetch-gate,closed-window-counts,summary-unread-branch,summary-counts-typedef,closed-window-cli-refusal,closed-window-self-test,floor-window-independence,summary-disabled-beats-floor. Kept, not removed: the card's own DISABLED-branch pin still asserts it, unset it is byte-equivalent to upstream (4 pages, sourcedefault), and it is the one place the fix(types): retire VectorFieldMetadata.indexed and .distance_metric #4690 property "a disabled reader is UNREAD, not clean" lives.seatLanedecides whether a seat title's@ reposuffix names a foreign board by comparing against the live resolved sweep repo. Upstream's rows hard-code@ objectuias the foreign specimen and@ objectstackas the own-board one; here that is exactly backwards, and a verbatim copy fails its own suite on three cases. The specimens are swapped, the property is identical.The closure floor is not in that list, deliberately: it is upstream code now (objectstack#12906 / objectui#6641), so a verbatim re-sync keeps it by construction. That was verified rather than assumed, below.
Prose that upstream has since rewritten to describe this board's measurement (the
resolveClosureFloordocblock, theh22ClosedCardPmResidue@paramnote) is taken from upstream verbatim rather than kept as a divergence. Behaviour is unaffected; the divergence set is smaller for it.The parity gate
scripts/check-upstream-port-parity.mjs+scripts/upstream-port-pin.json, wired inlint.ymlbeforepnpm installnext tocheck-entry-guard, with acheck:upstream-port-parityalias andscripts/__tests__/upstream-port-parity-wiring.test.tsholding the wiring.The pin carries, per ported file: the upstream repo, the 40-character commit, the SHA-256 of the upstream blob, and the declared divergences as exact text pairs with a stated reason each. Verification runs backwards — reverse the pairs out of the ported file, hash the reconstruction, require it to equal the pinned digest. Byte equality is the assertion, so there is no threshold to tune.
Three design points worth review:
--resync upstreamfile --ref shaforward-applies the divergences, writes the ported file and rewrites the digest. Editing a digest by hand until the gate goes green is indistinguishable from baselining the drift, so the only supported path is the one that also re-applies the adaptations. A divergence whose upstream anchor vanished fails that command loudly rather than being dropped.--self-test(37 cases) drives the real comparer over fixtures: parity holds on an undrifted copy; drift outside every declared region reds as a digest mismatch and says so; drift inside one names its divergence; an ambiguous anchor is refused rather than applied to the first match; the pin-bump procedure round-trips and a vanished anchor fails it; and fourteen malformed-pin shapes are refused (exit 2) rather than read as clean. The last block drives the shipped pin, because a pin this gate cannot parse is the one state a fixture can never show.Ablation — the gate really reds
Run against the real tree, each mutation confirmed on disk by anchor count before reading any result, each restore confirmed by
git hash-objectagainst the HEAD blob (0f70eb029c5607df421b3f3b8e2822e9698abe87, matched after every leg):reconstruction does not match the pinned upstream blob…the difference is OUTSIDE all of themDEFAULT_SWEEP_REPOinside a declared regiondivergence `default-sweep-repo`: expected its ported text exactly once, found 0 — the region it declares has been editedfiles[0].upstreamSha256 is not a 64-character SHA-256 digest…A pin that cannot be read is not a clean tree.The closure floor survives the re-sync — measured, not assumed
Driven end-to-end on the re-synced copy with the value read out of
half-state-patrol.ymlrather than retyped (PM_SWEEP_CLOSED_FLOOR: '2026-08-28', noPM_SWEEP_CLOSED_WINDOW_PAGES), loading the module with that env in place so the module-scope constants resolve as they will on the runner. All 16 cases pass:2026-08-28T00:00:00.000Z;default, i.e. upstream's default rather than an env value (the property objectui#6641 relied on when it removed the: 0hold);2026-01-01) is skipped, and so is2026-08-27T23:59:59Z;2026-08-29) is reported, and the cutover day itself is judged;yesterday) and a shape-valid impossible date (2026-02-31) each exit 2 with the refusal message, while the workflow's own value is accepted (it gets as far as the transport and exits 3,PREREQUISITE NOT MET, which is the sweeper's documented split).REPLACED PIN disclosures
scripts/__tests__/check-half-states.test.tsheader said the sweeper carries a "~1,077-case" self-test. It was 1,116 by then and is 1,574 now. The figure is deleted rather than refreshed — a hand-copied count drifts by construction and a stale one reads exactly as authoritative as a fresh one. The assertion below it never read the number and still does not..github/workflows/half-state-patrol.ymlheader carried the whole divergence list and endedNothing else diverges; the predicates are untouched.Both halves were true of the three deliberate adaptations and blind to the 4,637-line gap. The enumeration is replaced by a pointer to the machine-checked pin; what stays in the header is the wiring the pin cannot see (the floor, thepaths:entry) plus the three in-script classes named for orientation.check-half-states.test.tsneeded respelling for the re-sync: all 18 of its cases pass unchanged against the re-synced copy. Added rows pin the newly-discovered H32 install-coupling, including the source line that causes it, so upstream removing that coupling is what retires the block.Gate verdicts
Union re-run on the final commit
0034eb6; exit codes captured before any pipe.node scripts/pm/check-half-states.mjs --self-test✓ check-half-states self-test: 1574 cases pass.node scripts/check-upstream-port-parity.mjs --self-test✓ check-upstream-port-parity self-test: 37 cases pass …node scripts/check-upstream-port-parity.mjs✓ check-upstream-port-parity: 2 ported file(s) match objectstack-ai/objectstack@2b4178aa5 modulo their declared divergences.node scripts/check-entry-guard.mjs --self-test✓ check-entry-guard self-test: 63 cases pass …node scripts/check-entry-guard.mjs✓ check:entry-guard: 51 scripts/ file(s) — no entry guard outside the baseline …node scripts/check-control-bytes.mjs✅ check-control-bytes: OK (scanned 5517 tracked text file(s); skipped 85 binary).node scripts/check-pre-install-import-graph.mjs✅ check-pre-install-import-graph: OK — 18 pre-install step(s) in 16 job(s) run 18 scripts/ gate(s); 20 module(s) walked, every non-relative leaf a node builtin.node scripts/check-changeset-presence.mjs✅ No source of a released package changed in this range, so no changeset is owed.node scripts/check-lint-coverage.mjs✅ lint coverage: 46/46 packages linted, 0 with outstanding errors (0 total).pnpm type-check:scriptstsc -p tsconfig.scripts.jsonnpx vitest run --maxWorkers=2 scripts/__tests__Test Files 84 passed (84) · Tests 2371 passed (2371)No changeset: the gate's own verdict, quoted above, is the authority. Nothing under a published package's
src/changed. ⛔ Noskip-changesetlabel was applied — in this repo that label is a phantom.ESLint was narrowed and the narrowing is measured, not assumed:
npx eslint --no-inline-config --format jsonover the four changed source files reports 4 files linted, 0 errors, 0 warnings, exit 0 (file count read from the JSON output, not from a summary line); none of the four was skipped as ignored (an ignored path would have produced a warning and the warning count is 0); and the resolved config for these paths sets neitherparserOptions.projectnorprojectService, so type-aware linting is off and this diff cannot move an ESLint verdict on any untouched file.pnpm lintisturbo run lint, per package —scripts/is not a workspace package, so the repo-wide run does not read these files at all.Also verified rather than assumed:
tsc --listFilesreally contains both edited test files (1 match each), so "type-check clean" is a statement about them.Not done here, on purpose
The card asked for a re-sync and a detector, not for the port to be re-litigated. The closed-window mechanism is kept dormant rather than removed; the three H32 entries are carried rather than fixed upstream from here. Both are named in the pin with their reasons, and both shrink the day the other repo moves.
Generated by Claude Code
Generated by Claude Code