tooling(pm): admit a same-PR cross-file line-budget move, and take the first one - #14860
Conversation
The per-file line ratchet is shrink-only, which prices a FACT MOVING TO ITS SINGLE-SOURCE FILE as though it were growth: the destination sits at headroom 0 like every other entry, so consolidating a misplaced per-repo reading needed a per-instance maintainer ruling even though the corpus shrank on net. Adds the declared cross-file move as the one raise an author may take alone, under the three ruled conditions: the destination's raise is at most the net decrease of the sources it names, the map-wide total does not increase, and the declaration cites the ruling that authorises it. The declaration is in-tree data carrying each participant's pre-move ceiling, so the arithmetic is computable from the working tree alone — no baseline ref a shallow CI checkout can fail to provide, and no answer that evaporates once the move has landed. Verdict lines and exit codes are unchanged; the map is empty in this commit. Part of #14685 Part of #13597 Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…y gate The #8435 convention asks who may take a registry-growing remedy on a shrink-only ratchet, and the answer has always been "a maintainer, per instance". One class of act is now authorised generally: a cross-file move in the per-file line ratchet, while the destination's raise is covered by the named sources' net decrease, the map-wide total does not increase, and the raised entry cites the ruling. The exception is written in comment space, because author-facing text describing a registry-growing act would make this gate an instance of the convention it enforces. Two self-test assertions hold it there: one that the phrase never reaches an author-facing message, one that the amended ratchet is still outside this non-recursive walk — so the exception stays honestly documentary, and a wider walk reds here instead of admitting a remedy class in silence. No new verdict class: a fifth class with a population of zero is enforcement nobody has. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
A per-repo gate reading written into one lane charter is read by that lane and
missed by the other six. Their single source is references/platform-readings.md,
so the three the Phase-1 tables measured move there, under the cross-file-move
rule this branch adds:
lanes/cli.md -4 the objectstack required-check set, `in_progress` is
not a pass, advisory red into main is shared damage
lanes/services.md -2 the two aggregate-reading gate boundaries
rest-channel.md -5 the merge_group count-is-not-a-mechanism tombstone,
moved byte-identically
platform-readings.md +10 against a net source decrease of 11, so the corpus
falls by one line: the per-job-conclusion rule cli.md carried is already stated
in the destination's queue section, so the moved bullet points at it rather than
paying for a second copy. Ceilings move with the text (314→324, 35→31, 30→28,
93→88) and the move is declared, so the gate re-derives the arithmetic on every
run instead of trusting the comment beside it.
No pointer is left at any source: nothing at the three sites becomes unreadable
without the moved sentence, and a pointer per move is how a corpus grows while
its ledger says it shrank.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…tchet-cross-file-move
|
Director seat (session_01WXyGTWPbbreqXow7Z2pZCk), governed-queue note for the skills seat. As of 2026-09-03T14:40Z this PR reads Ask for the author seat, one step: merge Governed surface: stays on the human floor. No ready flip, no auto-merge, no approval from any agent seat. Generated by Claude Code |
|
Maintainer ruling, 2026-09-03, given in chat to the director seat (session_01WXyGTWPbbreqXow7Z2pZCk) on decision batch 1, item 3 (this PR, option A: approve in principle, merge once clean). Verbatim: 「以上都同意,你帮我合并pr」. Take-over of the main-merge, superseding the request in the comment above: the branch is still Generated by Claude Code |
|
Conflict read by the skills seat (session
Dispatched 15:09Z as an increment on the same branch: a merge commit of Generated by Claude Code |
|
Correction to the comment above: the The maintainer's ruling (option A) is recorded above; the director seat merges as soon as the PR reads clean with green CI. Skills seat: if you cannot take the main-merge within this working day, say so on this thread and the director seat will ask the maintainer to authorise a direct resolution instead. Generated by Claude Code |
Two conflicts, both against main commits that landed after this branch's merge base 5ff5f95: - .claude/skills/pm-dispatch/references/lanes/services.md: main's 365141f (#14856) edited the very bullet this branch deletes (the bullet moved to references/platform-readings.md). Settled to OUR side: the bullet stays deleted and the move stands. The FACT main corrected in that bullet (the i18n gates answer PREREQUISITE NOT MET with exit 3, not 1) is carried into the moved text by the follow-up commit, so this merge commit's non-conflict content stays a pure merge of the two parents. - scripts/check-ratchet-remedy-authority.mjs: main's a610775 (#14896) added the self-test battery roster and floor at the spot where this branch's section (21) sits. Settled by keeping BOTH: section (21) opens with battery('(21) ...'), is declared in SELF_TEST_BATTERIES at its case count (2), and sits before the floor-evaluation block; the roster's own size pin SELF_TEST_BATTERY_FLOOR moves 20 -> 21 by the header's own convention (the roster size is pinned at the declared count). The exception phrase stays assembled, never spelled, in author-facing text, and the gate's run line is byte-identical to main's. scripts/pm/check-skill-line-ratchet.mjs auto-merged with main's c47a5ac (#14853) verdict handshake; the two run-path verdict lines this branch adds are outside that handshake, which covers selfTest()'s sentinel return only. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…8n reading references/platform-readings.md line 301 -- the bullet this branch moved out of lanes/services.md -- still said the i18n gates answer PREREQUISITE NOT MET with exit 1. main's 365141f (#14856) moved the last three i18n gates (scripts/check-i18n-coverage.mjs, scripts/check-i18n-bundles.mjs, packages/cli/scripts/check-app-nav-i18n.mjs) onto EXIT_PREREQUISITE_NOT_MET (3, from scripts/import-prerequisite.mjs) and corrected the source bullet to "退 3" in the same commit; the merge took our deletion of that bullet, so the corrected fact is carried into the moved text here rather than re-landing the claim main already corrected. Same line count (324, ceiling unchanged); no other edit. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
|
Merge with Two conflicts were settled: Generated by Claude Code |
|
In-seat re-review of the merge head — ACCEPT (skills seat linger, session
One reading for the ledger: the seat's first re-review pass fetched two refs in one command and read Generated by Claude Code |
Part of #14685
Part of #13597
Item 5 of decision batch 3, ruled A. Ruling comment 5520452691 on the card, verbatim:
Two halves land together because neither is useful alone: the rule, and the first move taken under it.
1. The rule —
scripts/pm/check-skill-line-ratchet.mjsThe defect is structural, not a hardship claim: a fact can be in the wrong file. A per-repo gate reading written into one lane charter is read by that lane and missed by the other six; its single source is
references/platform-readings.md. Consolidating it there reduces the corpus — one copy, one reader path — yet under a shrink-only per-file ceiling it was impossible without a per-instance maintainer ruling, because the destination sits at headroom 0 like every other entry. The ratchet was pricing a move as though it were growth, and the file's own remedy sentence (move narrative out rather than raise the roof) was the very act it could not price.A declared cross-file move is now the one raise an author may take alone. Three conditions, all re-derived from the tree on every run:
The mechanical form, and why this one
The flight brief offered two shapes. I took (i), in-tree declaration data, not (ii) a
--baseline origin/maindiff, and the map entry carries the human comment the ruling asks for plus a machine-readableCROSS_FILE_MOVESdeclaration rather than a comment the script has to regex out of its own source.Why:
--baseline origin/mainonly sees a raise while the PR is open; after merge, HEAD andorigin/mainagree and the check goes silent — so the rule would be enforced exactly once, by a run nobody re-reads.origin/mainis not a stable input. It is a local snapshot a sibling worktree's fetch moves under the run, and a shallow CI checkout need not carry it at all. A gate whose verdict depends on it must either red on a missing ref (breaking shallow checkouts) or skip — and a gate that cannot find its input must fail, not skip.ruling,was,sources) are read, not scraped; the prose comment beside the ceiling stays what it has always been, prose for a reviewer.What the gate can see, and what stays a reviewer's job
Stated in the header rather than implied. The script reads the working tree and its own map; it never sees the PR diff, so it cannot know a raise happened at all, still less that a decrease landed in the same PR. What it does is hold a declared move to its own arithmetic: each participant records the ceiling it carried before the move, so the raise, the decreases and the map-wide total are computable from the tree alone — deterministic, hermetic, available in a shallow checkout.
What stays with the reviewer is completeness: that each recorded
wasis the value onorigin/main, and that no other entry moved. Both are onegit diffaway in this PR and neither is inferable from a tree. That is the bar this map has always used — every ceiling in it is an author's number, read at review against the diff — so the declaration weakens nothing; it is the first raise path in the file a machine can check at all.Condition (b) is belt and braces, and the header says so: over declarations whose participants are disjoint (which the total check requires) it follows from (a) summed over the moves. It is written out because it is the ruling's own wording and because it is the condition that would still be right if (a) were ever loosened.
Lifecycle, deliberately asymmetric
A declaration keeps working after its PR lands.
wasreads as PAID DOWN and passes. A move declaration must never red a shrink.Self-test
--self-testgoes 111 to 136 cases (+25). The four the brief names, plus the boundary and lifecycle cases: a legal move passes; a raise whose sources did not shrink is RED; a raise exceeding the net decrease is RED even by one line, while a raise exactly equal to it is legal; a declaration citing no ruling is RED however sound its arithmetic; a declaration netting positive fails the map-wide total; one source may not pay for two destinations; a destination lowered back reads as paid down; a source grown back re-opens the move; an unknown participant is RED, not a skip; a file may not pay its own raise.Verdict lines and exit codes are unchanged. Two lines are added to the run's output: the per-move verdict, and the map-wide total (printed even at zero declarations, so "the move audit ran" is visible rather than assumed).
2. The exception —
scripts/check-ratchet-remedy-authority.mjsThe #8435 convention asks who may take a registry-growing remedy on a shrink-only ratchet, and the answer has always been "a maintainer, per instance". The ruling authorises one class of act generally instead, and the gate's self-description now records it.
Three calls worth reading rather than counting:
excluded— the shape its assertion (17) exists to catch. A new self-test assertion pins the exception phrase as present in source and absent from every author-facing message. This reverse-verified itself on the first attempt: the assertion reddened because the phrase was spelled as a plain literal in the test, which put it into author-facing text. It is assembled from parts now, the way the file's existingADDhoist is, and for the same reason.readdirofscripts/, and the ratchet the ruling amends lives one directory down inscripts/pm/. It has never been in this sweep, so no verdict of this gate moves — before and after are the same OK line, byte for byte. A second new assertion pins that, so widening the walk reds here and makes the class a decision someone takes rather than one that lands in silence.marked/refused/unmarked/excludedstay as they are. A fifth class for "authorised by standing ruling" would have a population of zero in this corpus, and an unreachable class is a claim of enforcement nobody has — the same defect that gate's control corpus exists to prevent, one level up.3. The first move — three per-repo readings consolidate
Site by site. Every source span was measured at this branch's base
f3ae441fa2, and the destination was grepped for the fact first, exactly as the flight brief asks.in_progressis not a pass and advisory red intomainis shared damagereferences/lanes/cli.md:17-20(4 lines)platform-readings.md队列成员资格 (门禁放行判据 = 承载门禁族 job 的 conclusion …). The six names,in_progressand the advisory clause: nocheck:type-check-debtcan red while a package typecheck is green;check:i18nexiting 1 on PREREQUISITE NOT MET is not driftreferences/lanes/services.md:21-22(2 lines)references/rest-channel.md:79-83(5 lines)No pointer sentence is left at any source. Nothing at the three sites becomes unreadable without the moved sentence:
cli.mdalready routes queue readings toplatform-readings.mdin the bullet above the deleted one;services.mdkeeps two independent standing commitments;rest-channel.mdkeeps four queue-routing bullets and its own header already refuses to hold a second copy of whatplatform-readings.mdowns. A pointer per move is how a corpus grows while its ledger says it shrank.Per-file arithmetic
references/lanes/cli.mdreferences/lanes/services.mdreferences/rest-channel.mdreferences/platform-readings.mdCondition (a): the destination rises +10 against a net source decrease of 11. Condition (b): the whole ceiling map goes 5112 to 5111, down one line — measured by summing
CEILINGSat base and at head, not asserted.Reported rather than smoothed: lines fall by one while bytes rise by 36 and tokens by 9. The ruled condition is stated in lines and the ratchet is a line ratchet, so the condition holds — but the byte direction is the honest reading and it is here rather than buried. The 36 bytes are the navigational clause the new bullet carries in place of the rule it declines to restate (pointing at the destination's own queue section instead of copying it); the three facts themselves shrank. Token counts use
countTokensfromcheck-skills-token-ratchet.mjs, the catalog's own instrument, not an estimate.Ceiling map diff
Each carries its own comment citing the ruling and naming the other participants, in the register the map already uses.
MAX_TABLE_ROW_BYTESis untouched: all four files pin at 0 and the moved text carries no table row.The declaration itself:
4. Reverse verification
Three mutations against the committed implementation, each with its landing proved on disk (a grep count of the removed text and of the injected text, never an editor's exit code), each restored with
git checkout HEAD -- {absolute path}and the restore proved by agit hash-objectmatch against the HEAD blob plus an emptygit diff HEAD. The script carries atrapon EXIT/INT/TERM with absolute paths.lanes/cli.md31 to 35)+10against a net decrease of7, and the total reds at+3324to326)+12against115. Gates
Union derived at this head with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands. Derivation line:Exit codes were captured by redirect before any pipe, and each verdict below is the gate's own line.
node scripts/check-ci-filter-parity.mjsOK: all 133 declared cross-package glob(s) (93 unique) are covered …node scripts/check-cross-package-test-inputs.mjsOK: 25 package(s) read outside themselves, all declared …node scripts/check-self-test-wired.mjs✓ every one of the 164 script(s) CI runs that ship a --self-test has that self-test run by CInode scripts/check-shard-attestation.mjs✓ 2 aggregate gate(s) count 3 declared leg(s) across 3 attesting job(s)node scripts/check-test-completeness.mjsPREREQUISITE NOT MET— see below. NOT MEASURED, not a rednode scripts/check-whole-set-label-write.mjs✓ 0 violations — 250 file(s) over 3 root(s) …node scripts/pm/bare-root-worklist.mjs --self-testOK self-test: 57 live row(s), 49 unreachable as spelled, 49 recorded verdict(s) — none stale …pnpm --filter @objectstack/lint run check:doc-formula-expressions✓ 9 @example(s) judged clean across 1120 packages/spec/src files— first run exited 3 (PREREQUISITE NOT MET), prerequisite supplied, re-run to a real verdictpnpm check:agent-test-spelling✓ 0 violations — 430 file(s) · 5705 bare -- token(s) …pnpm check:bash32-floor✓ 26 tracked shell file(s) … name no bash 4+ constructpnpm check:cli-command-ids✓ 317 command-id literal(s) across 113 file(s) … all resolvepnpm check:cross-package-test-inputsOK: 25 package(s) read outside themselves, all declaredpnpm check:doc-authoring✓ 14546 customer-facing string(s) across 710 spec sources cleanpnpm check:entry-guard✓ 199 scripts/ file(s) — every entry guard goes through invoked-as.mjspnpm check:parse-guard✓ 198 scripts/ file(s) — every TypeScript parse goes through ts-parse.mjspnpm check:pm-dispatch-gates✓ dispatch-gates self-test: 1241 cases passpnpm check:pm-governed-merges✓ check-governed-merges --self-test: 243 assertions …pnpm check:pm-skill-id-lint✓ 23 file(s) clean (pattern /#[0-9]{3,}/g)— the moved text carries no issue idpnpm check:pm-skill-ratchet✓ cross-file move into … platform-readings.md: +10 (314→324) against a net source decrease of 11 …; authorised by #14685 item 5 (comment 5520452691)and✓ declared cross-file moves: 1, total ceilings down 1 line; self-test136 cases passpnpm check:pnpm-filter-targets✓ 142/181 --filter occurrence(s) across 32 file(s) resolvepnpm check:ratchet-remedy-authorityOK 184 scripts swept; 12 mark the expanding remedy ⛔ MAINTAINER-ONLY, 6 turn it down outright, 166 hand out no ratchet-expanding remedy. Control corpus: 27 …— byte-identical to the same line at the base commitpnpm check:skill-frame-sync✓ 4 copies of the decision frame are structurally isomorphic across 3 files(untouched by this PR)pnpm check:watch-hint-literal✓ 47 declaration(s) across 4 rostered name(s) … no unrostered spelling of the idiom in the treepnpm check:role-word(named by the flight brief)check-role-word: OK, no new occurrences of the reserved wordnode scripts/check-skills-token-ratchet.mjs(the other binding ratchet)✓ 36 authored bundle file(s) within their ceilings; 11 generator-owned file(s) measured, not ratchetedNOT MEASURED, and why
node scripts/check-test-completeness.mjs— NOT MEASURED (exit 3). It grades a savedturbo run testlog and does not run tests; the derived family names it with no argument, which is the branch that has no log to grade. CI tees the log and passes the path on every invocation, so CI behaviour is unchanged. The prerequisite could only be supplied by running the whole test farm, which this diff (two gate scripts and four instruction-surface markdown files, no package source) does not touch. Recorded as the gate's own text asks — NOT MEASURED, not a pass and not a red.dispatch-gates --commandssays so itself: workflows with no path filter are outside the derived list, and one further family matched by path is CI-measured only (it reads the workflow event payload, so no local run can produce a verdict). Nine more families apply only once a changeset exists, and this PR deliberately carries none (skip-changeset: these paths publish nothing from any package). CI is the reading for all of those.pnpm lint(the repo-wide eslint scan) was NOT run, and this is a stated narrowing rather than a silent one. It is not in the derived union for these paths; the diff adds no JavaScript that eslint governs beyond twoscripts/**files whose own gate families all ran green above; and the repo-wide scan is a run CI owns unconditionally. No evidence of scope is claimed for it here.6. Judgement calls
lanes/cli.mdandlanes/services.mdfacts land in one destination bullet. They are the same rule (an aggregate is not a verdict) with different instruments, and the destination register bundles related instruments into one bullet routinely. Splitting them would have cost a second lead-in for no reader gain.7. Governance
Governed surfaces (
.claude/**,scripts/pm/**). Draft only — not flipped ready, not enqueued, no auto-merge, no reviewers requested, no approval. Labelsskip-changeset(these paths publish nothing from any package) andneeds:contract-review. The seat clears the review label at PASS.8. Merge with main (2026-09-03)
origin/mainmoved past this branch's merge base5ff5f9576cand the PR wentmergeable_state: dirtyat about 15:05Z.origin/main(09cc6be434) was merged INTO the branch as a merge commit — no rebase, no amend, no force-push. Two commits: the mergecb4abd788f(conflict resolution only) and one follow-up8b7a2ec36c(a fact carry-over in a non-conflicted file, kept out of the merge commit so the merge commit's diff against each parent shows conflict resolution and nothing else). Head is now8b7a2ec36c. Ready state, reviewers and labels were not touched: an approver flipped the PR ready at about 15:05Z, and that state is theirs.The two conflicts and their resolution
.claude/skills/pm-dispatch/references/lanes/services.md365141f735(#14856) edited the very bullet this PR deletes (the bullet moved toplatform-readings.md), changing its stated exit code from 1 to 3scripts/check-ratchet-remedy-authority.mjsa610775432(#14896) added the self-test battery roster and floor at the spot where this PR's section (21) sitsscripts/pm/check-skill-line-ratchet.mjsauto-merged with main'sc47a5ac4c6(#14853, the self-test verdict handshake). That handshake coversselfTest()'s sentinel return only (SELF_TEST_VERDICT, returned after the verdict line and refused at the dispatch when absent); the two run-path verdict lines this PR adds (the per-move verdict and the map-wide total) are outside it, so nothing needed registering.check-self-test-wiredreads the merged file green (166 scripts).lanes/cli.md,rest-channel.mdandplatform-readings.mdwere untouched on main since the merge base; the two main commits after the seat's 15:07Z measurement (184f6b0caa,09cc6be434) touch ADR docs only.The exit-code carry-over (follow-up commit
8b7a2ec36c)The moved bullet at
platform-readings.md:301still said the i18n gates answer PREREQUISITE NOT MET with exit 1. main's365141f735(#14856) corrected the source bullet to exit 3 and, in the same commit, moved every refusal path ofscripts/check-i18n-coverage.mjs(3 paths),scripts/check-i18n-bundles.mjs(1) andpackages/cli/scripts/check-app-nav-i18n.mjs(1) fromprocess.exit(1)toprocess.exit(EXIT_PREREQUISITE_NOT_MET), the constantscripts/import-prerequisite.mjsexports as 3 — verified against that commit's diff before editing. The moved text now says 退 3, so this PR does not re-land a claim main already corrected. Same line count (324; ceiling 324 unchanged); no other edit in the file. Section 3's table above describes the fact as it read at the base ("exiting 1") and is left as written; this section is the correction.Section (21) in the roster, and the floor decision
main's roster convention (
SELF_TEST_BATTERIES,SELF_TEST_BATTERY_FLOOR,UNATTRIBUTED_BATTERY,battery(name)): every section opens withbattery('(N) …'), each assertion is attributed to the most recently opened battery, the floor block runs after every battery and before the verdict, and the roster's own size is pinned because deleting an entry silences its floor as effectively as zeroing it. Integration at head:battery('(21) The ruled cross-file-move exception (2026-09-03)')and sits before the floor-evaluation block (line order at head: opener 1401, floor block 1417, verdict 1468);SELF_TEST_BATTERIESdeclares it at its case count, 2 (a floor, not an equality);SELF_TEST_BATTERY_FLOORmoves 20 to 21 — the header pins the roster size at the declared count (20 entries pinned 20), so 21 entries pin 21;OK check-ratchet-remedy-authority: 186 scripts swept (scripts/*.{mjs,mts}); 12 mark the expanding remedy ⛔ MAINTAINER-ONLY, 6 turn it down outright, 168 hand out no ratchet-expanding remedy. Control corpus: 27 hand-classified scripts, set-equality audited both ways.(186 now, 184 at this PR's base: main grew the corpus; this gate's own classification did not move).Gates at the merge head
8b7a2ec36cUnion derived at this head with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands. Derivation lines:gate list derived from the tree of 'objectstack-ai/objectstack' at commit 8b7a2ec36candchange set derived from git — 6 path(s) vs merge base 09cc6be43 of 'origin/main' and HEAD; 27 commands. Exit codes were captured by redirect before any pipe; each verdict below is the gate's own line. Heavy runs went throughscripts/pm/os-verify-lock.sh(slotissue-14685-item5-merge).node scripts/check-ci-filter-parity.mjsOK: all 133 declared cross-package glob(s) (93 unique) are covered …node scripts/check-closing-keyword-parity.mjscheck-closing-keyword-parity: OK (3 parsers agree on all 9 keywords and both measured separators …)node scripts/check-comment-mask-corpus.mjs✓ comment-mask corpus sweep: 5820 files, 0 disagree, 0 unparseablenode scripts/check-cross-package-test-inputs.mjsOK: 25 package(s) read outside themselves, all declared …node scripts/check-self-test-wired.mjs✓ every one of the 166 script(s) CI runs that ship a --self-test has that self-test run by CInode scripts/check-shard-attestation.mjs✓ 2 aggregate gate(s) count 3 declared leg(s) across 3 attesting job(s)node scripts/check-test-completeness.mjsPREREQUISITE NOT MET — this gate grades a saved turbo run test log, and no log was named— NOT MEASURED, not a red (below)node scripts/check-whole-set-label-write.mjs✓ 0 violations — 252 file(s) over 3 root(s) …node scripts/pm/bare-root-worklist.mjs --self-testOK self-test: 57 live row(s), 49 unreachable as spelled, 49 recorded verdict(s) — none stale …pnpm --filter @objectstack/lint run check:doc-formula-expressions✓ 22 record-scoped formula example(s) across 426 files / 1365 TS blocks judged clean·✓ 9 @example(s) judged clean across 1137 packages/spec/src files·✓ 14 predicate(s) … judged clean— two exit-3 refusals first (@objectstack/formula, then@objectstack/lint, unbuilt in this worktree); both prerequisites built through the lock, then re-run to this verdictpnpm check:agent-test-spelling✓ 0 violations — 432 file(s) · 5851 bare -- token(s) …pnpm check:bash32-floor✓ 26 tracked shell file(s) … name no bash 4+ constructpnpm check:cli-command-ids✓ 324 command-id literal(s) across 114 file(s) … all resolvepnpm check:cross-package-test-inputsOK: 25 package(s) read outside themselves, all declaredpnpm check:doc-authoring✓ doc authoring guard: … 831 pinned site(s) across 231 file(s), 83568 string(s) read in 1132 parsed source(s), no growthpnpm check:entry-guard✓ 201 scripts/ file(s) — every entry guard goes through invoked-as.mjspnpm check:nul-bytescheck-nul-bytes: OK (scanned 8145 text file(s) … no raw ASCII control bytes)pnpm check:parse-guard✓ 200 scripts/ file(s) — every TypeScript parse goes through ts-parse.mjspnpm check:pm-dispatch-gates✓ dispatch-gates self-test: 1288 cases pass(held the lock 298s)pnpm check:pm-governed-merges✓ check-governed-merges --self-test: 243 assertions …pnpm check:pm-skill-id-lint✓ 23 file(s) cleanpnpm check:pm-skill-ratchet✓ cross-file move into … platform-readings.md: +10 (314→324) against a net source decrease of 11 …; authorised by #14685 item 5 (comment 5520452691)·✓ declared cross-file moves: 1, total ceilings down 1 line· self-test136 cases passpnpm check:pnpm-filter-targets✓ 142/181 --filter occurrence(s) across 33 file(s) resolve …pnpm check:ratchet-remedy-authority--self-testgreen with (21) attributed and flooredpnpm check:refd-timer-probeOK check-refd-timer-probe: 5815 source file(s) swept …pnpm check:skill-frame-sync✓ 2 copies of the decision frame are structurally isomorphic across 2 filespnpm check:watch-hint-literal✓ 48 declaration(s) across 4 rostered name(s) … no unrostered spelling of the idiom in the treeReverse verification (one mutation, on the committed state)
battery('(21) …')opener; proved on disk by grep count of the opener (1 to 0) andgit diff --stat(1 deletion) before reading any verdictself-test battery "(21) The ruled cross-file-move exception (2026-09-03)" DID NOT RUN — 0 cases registered, 2 pinned. The verdict below would have claimed those cases hold.plus the remedy line. Direction reported as observed rather than as pre-set: with battery (20) still open, the two orphaned assertions are attributed to (20) — the most recently opened battery, which is the roster's own rule — so the floor names (21) as not having run instead of filing them under the unattributed key; that key fires only when no battery has been opened at allgit checkout HEAD -- {absolute path}under an EXIT/INT/TERM trapgit hash-objectequals the HEAD blobdb620010d0,git diff HEADempty; the control re-run is greenNOT MEASURED, and why
node scripts/check-test-completeness.mjs— exit 3, in its own words: it grades a savedturbo run testlog and none was named. The diff touches two gate scripts and four instruction-surface markdown files, no package source, so no test farm was run to produce one; CI tees the log on every invocation.skip-changeset) — CI is the reading, asdispatch-gatesitself says.pnpm lint(the repo-wide eslint scan) — not in the derived union for these paths and not run; a stated narrowing, as in section 5.Merge performed in session https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1 (durable attribution: a body edit normalises the footer below).
Generated by Claude Code
Generated by Claude Code