Skip to content

Commit 3dbb49c

Browse files
os-try-charlesclaude[bot]claude
authored
chore(devx): run a budget gate against the merged tree, where the file and its ceiling come from the same side (#18338)
Fixes #16287 Clause-②: no Nothing in a published contract narrows or widens here: this adds one new tool script plus its `check:` entry, and edits no existing gate's verdict rule, no ceiling and no schema. ## What lands `scripts/check-merged-result.mjs` — a runner that materialises the MERGED result of a base ref and a head ref and runs an existing tree-reading gate against that tree, instead of against either side. ``` node scripts/check-merged-result.mjs --base origin/main --path AGENTS.md --path .claude --path scripts \ -- node scripts/pm/check-skill-line-ratchet.mjs ``` Three exit codes, never two: `0` WITHIN BUDGET, `1` FINDING, `3` NOT MEASURED. The third exists because the failure this card is about is a reading that was never taken being printed as a reading that came back clean. ## The two placements, weighed — and the one chosen | | 1. a base-ref mode that measures the merged result | 2. an outbound check on the compressing PR | |:--|:--|:--| | catches it | on the PR, before enqueue | at the moment the headroom is taken | | names | the branch that must now pay | the change that removed the budget — the right owner | | needs | two refs, no network | a live GitHub read at gate time | | failure mode | a ref it cannot resolve, which is loud | a read that fails answers "no affected PRs" | | covers | any base that makes the merged result exceed a budget | only a base that LOWERS a ceiling | Direction 2 is the one the card prefers, and its instinct — the change that breaks something should be the change that reports it — is this repo's usual one. It was still refused, on two grounds that were measured rather than argued: - **It cannot see half the class.** It keys on "a PR lowers a ceiling", while the class is "the merged result exceeds the budget". With headroom h, a sibling that merely SPENDS h lines and touches no ceiling blows the same budget for the same open branch, and nothing about that PR fires an outbound check. The self-test carries that case (`no ceiling moved`) precisely because it is the one direction 2 is structurally blind to. - **Its failure mode is under-reporting — the very reading this card is about.** Re-measured on this board today: 1 list call plus one file-listing call per open PR, 14 calls for 13 open PRs, 6.6 s wall, credentials required. 4 of the 13 open PRs touch a ratcheted path; 2 touch `AGENTS.md`. A network failure in that enumeration answers "no affected PRs" and reads exactly like a clean board. Direction 1 is what landed, with one refinement the fixture pins: **The mode lives in a runner OUTSIDE the gates, not as a `--base` flag inside each gate.** 1. The gates are deliberately hermetic; `check-skill-line-ratchet`'s own header refuses an in-gate baseline mode in writing, and that objection still holds. Keeping ref-reading in one component preserves it. 2. **The CEILING is part of the merged tree too.** A `--base` flag inside a gate compares the merged FILE against the ceiling in the RUNNING copy of the script — the branch's copy. In the incident shape the ceiling moved on the base side, so the branch's copy still carried the roomier number and such a flag would have gone green. This runner executes the MERGED TREE'S OWN COPY of the gate, so the measured file and the number it is measured against come from one tree. The self-test pins exactly this: the fixture's branch copy carries a stale, roomier ceiling, and the probe still reds. 3. The class is wider than one gate — every pinned-rather-than-derived budget in the tree. One runner covers them all with no gate edited. Nothing under `scripts/pm/**` was modified: the cross-domain exception declared at claim time was ultimately not needed. ## Evidence ### 1. The class, reproduced on today's tree with real content `origin/main` at `a09725dde` carries `AGENTS.md` at **1075 lines against a ceiling of 1075** — headroom 0 — and a widest table row of **768 bytes against a pin of 768**. Both axes of that gate are at zero headroom right now, which is why this card is not theoretical. The reproduction ran in an isolated clone on throwaway refs (never pushed, no ceiling number in this diff moves), using the REAL `AGENTS.md` and the REAL ratchet script: | ref | AGENTS.md | its copy of the ceiling | gate on that side | |:--|--:|--:|:--| | seed | 1069 | 1075 | green | | branch (adds 3 lines) | 1072 | 1075 | **green** | | base A (compresses 4 lines elsewhere, re-anchors) | 1065 | 1065 | green | | **merged(base A, branch)** | **1068** | **1065** | **RED** | `git merge-tree --write-tree` exited **0** on that pair — there is no textual conflict at all — and the merged tree reproduced the incident verbatim: ``` check-skill-line-ratchet: AGENTS.md is 1068 lines; the ratchet ceiling is 1065. ``` ### 2. Both legs measured: red, then green, differing ONLY in the base | run | base | probe exit | verdict line | |:--|:--|--:|:--| | red | base A (re-anchored) | `1` | `FINDING — the branch is GREEN on its own (gate exit 0) and RED on the merged result (exit 1)` | | green | base G (same compression, ceiling untouched) | `0` | `WITHIN BUDGET` | Same head, same gate, same paths. The only difference is whether the base re-anchored the ceiling. Also measured: unrelated histories exit `3` NOT MEASURED (`refusing to merge unrelated histories`), never 0 and never 1; and a run with no gate after the bare `--` exits `3` rather than reporting an empty run as a pass. ### 3. Ablations — three, each proved on disk and each restored No `sed -i` / `perl -i`: mutations go through an anchored replacement that exits non-zero when its anchor is absent or ambiguous, so a zero-match edit cannot read as done. Every leg: mutate, prove the marker landed and the blob moved, read the self-test, restore from `HEAD`, prove the restore by blob equality and an empty `git diff HEAD`. The restore is in an `EXIT INT TERM` trap with an absolute path. | ablation | mutated blob | self-test | what went red | |:--|:--|--:|:--| | measure the branch tree instead of the merged tree | `9d081e6d` | `1` | `the merged result is RED while the branch alone is green` | | count characters instead of bytes | `a7432452` | `1` | `so the merged result is RED — a character count would have passed it` | | rename an open battery out of the declared roster | `b08b417b` | `1` | the undeclared battery AND the declared one that registered 0 of 3 | Pristine control: `23 cases pass`, exit `0`. Restored blob `0f04ad43` equals the `HEAD` blob on all three legs, `git diff HEAD` empty each time. The third leg is the firing control for zero: a battery whose cases stop running is reported as not having run, rather than as having passed. ### 4. Bytes, not characters The card flags `LC_ALL=C` as load-bearing. Measured here, both halves separately: `/usr/bin/awk` on this image is `mawk`, and `mawk` and `/usr/bin/nawk` each answer `9` for three Han characters with `LC_ALL` unset AND with `LC_ALL=C` — so the guard is a no-op on this image and its absence is unfalsifiable here. No `gawk` is installed, so the divergent half is documented behaviour, not a local reading. That is the argument for not leaving a byte budget to `awk` at all: this runner counts with `Buffer.byteLength`, and the self-test's positive control is a 41-character Han line that is legal by characters (41) and illegal by bytes (123) against a 120-byte pin. ### 5. Live readings The probe run against the two open PRs that touch `AGENTS.md` today: both WITHIN BUDGET on the merged result (one is line-neutral at 1075; the other carries its own raised ceiling). No live finding to report — the reading was taken and it is green. ### 6. Gates, lint - `dispatch-gates --commands` derived 36 families for this change set; all 36 run, **all exit 0**. Reconciled with `--ran` carrying exit codes: **36 derived, 36 run, 0 NOT-MEASURED, 0 UNRUN** (a derived zero, not a claimed one). - `pnpm exec eslint . --no-inline-config --format json` at `8401623b1`: **6771 files, 0 errors, 0 warnings**, 115 s. The whole population was measured, so no narrowing is claimed. (`package.json` is outside eslint's configuration and reports as ignored.) - The probe itself, scoped with `--path`, is 0.9 s end to end including both gate runs; 3.7 s materialising the whole tree. ## Changeset None, and the `skip-changeset` label is applied. Sole criterion: does anything published move. The root manifest is private and ships no `files[]`, and a repo-root `scripts/` file is inside no package's published set, so nothing released moves. ## Not done here `pnpm check:merged-result` is this file's `--self-test` and nothing else — it grades the instrument, never a tree, the same idiom as the other on-demand tools whose `check:` entry is their self-test alone. Wiring that self-test into CI is one workflow step, and this card's claim did not extend to `.github/workflows/**`, so it is left to the seat rather than taken silently. The probe is deliberately not a `pull_request` step: that checkout already builds a merge commit and goes stale for the same reason the PR's own CI did, and the merge queue already runs the real thing at the one moment it is authoritative. This is the cheap early reading between the two, which is what was missing. --- _Generated by [Claude Code](https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk)_ --------- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com>
1 parent 74eaab8 commit 3dbb49c

2 files changed

Lines changed: 693 additions & 0 deletions

File tree

‎package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
"check:agent-model-declared": "node scripts/check-agent-model-declared.mjs --self-test && node scripts/check-agent-model-declared.mjs",
7171
"check:agent-test-spelling": "node scripts/check-agent-test-spelling.mjs --self-test && node scripts/check-agent-test-spelling.mjs",
7272
"check:ratchet-remedy-authority": "node scripts/check-ratchet-remedy-authority.mjs --self-test && node scripts/check-ratchet-remedy-authority.mjs",
73+
"check:merged-result": "node scripts/check-merged-result.mjs --self-test",
7374
"check:pm-skill-ratchet": "node scripts/pm/check-skill-line-ratchet.mjs --self-test && node scripts/pm/check-skill-line-ratchet.mjs",
7475
"check:pm-skill-id-lint": "node scripts/pm/check-skill-id-lint.mjs --self-test && node scripts/pm/check-skill-id-lint.mjs",
7576
"check:pm-label-desc-cap": "node scripts/pm/check-label-desc-cap.mjs --self-test && node scripts/pm/check-label-desc-cap.mjs",

0 commit comments

Comments
 (0)