Repository navigation
Commit 3dbb49c
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
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
70 | 70 | | |
71 | 71 | | |
72 | 72 | | |
| 73 | + | |
73 | 74 | | |
74 | 75 | | |
75 | 76 | | |
| |||
0 commit comments