feat(facts): one definition of an issue key, and a gate on the twenty-first - #818
feat(facts): one definition of an issue key, and a gate on the twenty-first#818wenzowski wants to merge 3 commits into
Conversation
…-first CLOUD-761 measured twenty independent derivations of the key pattern in nine spellings, diverged on three axes, with a shipped defect behind them: a body writing the lowercase form is accepted by one gate and invisible to two others. This is the definition those sites converge on, plus the gate that stops a twenty-first arriving. THE CRATE OWNS THE AXES AND NEVER THE TOKEN. `no-tracker-key-in-core` refuses a derivation anywhere under `crates/**` — the mechanism that exists because CLOUD-1121 carried the literal in as a `const` and passed every gate — so the token stays the consumer's, read from the `[[pattern]]` registry, and `ready::Grammar` owns case, boundary and anchoring. The tests spell the prefix from parts for the same reason: a test is not exempt from the rule it is testing. THREE OPERATIONS, AND SEPARATING THEM REMOVES A DERIVATION RATHER THAN ADDING ONE. `key_of` answers "is this whole string a key" — the four shell `case` globs' question, which they get wrong because a glob cannot anchor, accepting `AB-1`, `Z-9`, `A-1foo` and a key with a trailing letter. `keys_in` answers "which keys does this text carry". The third question — "does this text carry key K" — needs no expression at all: it is `keys_in` compared for equality, so the boundary the two landed sites commented on is decided once. The boundary is read off the bytes either side of a match rather than composed as `(^|[^0-9A-Za-z-])…([^0-9]|$)`. `regex` has no lookahead, so a trailing class would CONSUME the byte after a match and make adjacent keys unfindable — and checking bytes means the crate composes no key expression, so there is nothing here for a twenty-first copy to be a copy of. THE GATE RATCHETS RATHER THAN FORBIDS, which is what makes it landable. 34 occurrences survive across the task tree; a `forbid` fires on every one. Converting them means editing governed programs, which is CLOUD-761's remaining half. Meanwhile the count cannot grow. Replayed over 400 commits of origin/main as CLOUD-1142's §7 requires, before the severity was set: 400 examined, 1 would-fire, 0 false positives. The one firing is `c64e54a3`, a revert that put four derivations back — a true positive, and the same false premise this row's own blocker rests on. Shown able to fail, and it caught a dead gate doing it: the first draft wrote the pattern in escaped regex form, but `ratchet_rule` counts with `str::matches` — a literal substring — so it counted zero at both ends and could never fire. Driving a twenty-first derivation through it is what found that; reading it did not. Admits: f3cdc5c1a49523305bc7451d2cc9bdd05a313d96a00e21164b405c6ce170bdc2 Admits-rule: protected-mutation Admits-verdict: path write refused Admits-subject: batten.toml Admits-head: 9ece058 Admits-epoch: 7dcd0bd54e1da3834c9350ebcdeb40e91dbeb1c8a268d6466d66a26b459753c2 Admits-author: alec@wenzowski.com Admits-prev: b28fd480c8126febacd3db96118787d32ebab604e9dce98f869210441887098e Admits-answer-lost: The property whose absence is how twenty derivations arrived. CLOUD-761 measured twenty independent spellings of the issue-key pattern diverged on three axes, with a shipped defect behind them — a body writing the lowercase form is accepted by one gate and invisible to two others. Without this row the count can keep growing and nothing notices, which is exactly the history: replayed over 400 commits of `origin/main` the predicate fires once, on `c64e54a3`, a revert that put four derivations back. That firing is a true positive and there are no false ones. Admits-answer-precondition: The change adds one `[[rule]]` row, `issue-key-derivations-not-growing`. A rule row IS the committed authority — no verb writes one, and CLOUD-1142's §1 names this file explicitly as where the anti-duplication gate's identity, applicability, scope and severity are declared. It lands beside `no-tracker-key-in-core`, the row that already refuses the same derivation under `crates/**`, so a reviewer reads the two halves of one predicate together: forbidden in the crate, ratcheted in the task tree. Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE is rejected because this file is the owning surface for a `[[rule]]` row and CLOUD-1142's §1 says so by name. R-RESTORE-IT is rejected because restoring the committed bytes restores the ungated state, which is the defect rather than a fault to undo. Refs: CLOUD-1142, CLOUD-761, CLOUD-1121
`mint` has always written line 1 as an id LIST and keyed its `weakens` lines per issue, so a receipt holding several keys is the shape this file was built for. Every invocation wrote a fresh one, though, so claiming a second row on one branch silently discarded the first row's record. Measured on CLOUD-1295's branch, an hour before this: re-claiming would have dropped the `weakens` lines `config lint`'s groomed half reads, which is the difference between a landable branch and a refusal nobody can explain. That work moved to a branch of its own to avoid it, which is the wrong reason to open a branch. ONLY WHEN THE BASE AGREES, and that condition is the whole safety of it. CLOUD-516's restart case is precisely a receipt outliving the branch it described: `git checkout -B <name> origin/main` discards the commits and keeps the filename. Carrying ids across that would let a restarted branch inherit claims for work it no longer holds, which is the defect that row exists to close. A differing, absent or unreadable base therefore REPLACES rather than merges — the direction that forgets rather than the one that over-claims. Four cases, and two of them are the ones that keep this honest: the restart case must still forget, and `Carried::default` must carry nothing, without which a populated default would pass every other case without reading a file at all. Refs: CLOUD-1231, CLOUD-516, CLOUD-1142
CLOUD-1142 An issue key gets one definition, in the fact producer — the three axes decided, three-valued, and a gate that fails the twenty-first copy
Why CLOUD-761 measured twenty independent derivations of the issue-key regex across nine spellings, diverged on three axes — case, boundary, and whether the project prefix is required at all — with a shipped defect behind them: a PR body writing That row decided all three axes and then named the whole remedy as one piece of work: one definition, plus nineteen consumer conversions. The two halves have very different shapes, and only one of them is landable. Seventeen of the nineteen consumer sites live in governed Nothing here is a new decision. Every axis this row builds, CLOUD-761 already decided, and its wording is carried rather than re-argued. Refinement — Ready Refinement gate: Definition of Ready & Done. This body carries only specializations.
Acceptance
Not in scope Converting the nineteen existing derivation sites, and CLOUD-1231 The claim receipt holds ONE key per branch, but `closing-key-check` expects a PR to close several — so a multi-row branch can only ever record a claim for one of them
Why Two gates disagree about how many rows a branch may serve.
The claim receipt is not. Minting a claim for a second row on the same branch overwrites the first. There is no append, and the shape has no room for a second key. Measured, 2026-08-31 on PR #770That PR closes CLOUD-1145, CLOUD-1161 and CLOUD-1218 — a shape Claiming CLOUD-1218 by hand would have erased CLOUD-1161's claim, so I did the board half (Todo → In Progress, assigned) and left the receipt alone. That is the right call for the tree and the wrong outcome for the record: the branch performed work for a row whose claim it cannot show. **Nothing failed, and that is the finding. ** Why this is not CLOUD-516's or CLOUD-733'sBoth are about the receipt being keyed by branch name — a stale claim inherited after a merge (516), and a rename stranding it (733). Both are now Done. This is a different axis: the key is fine, the arity is one where the workflow's own gate expects many. CLOUD-502 asked a related board question (a bundle landed as one PR cannot be represented) and was cancelled. This row is narrower and mechanical rather than about WIP accounting: it is about what the receipt file can hold. Refinement — Ready (let a branch's claim record every row it serves) Refinement gate: Definition of Ready & Done. This body carries only specializations.
Acceptance
Found while landing PR #770: the branch served three rows, could record a claim for one, and no gate noticed. CLOUD-761 Nineteen issue-key consumers still re-derive the key, and every one of them is a governed shell program the conversion cannot touch
Lifecycle bundle integrationExtend canonical board facts with status, lifecycle revision, semantic body digest, structured relations, claim generation, bundle roster, and PR/main/release evidence. An unreachable tracker must yield explicit unknown facts, never an empty clean payload. Board state — issue status, relations, Ready-block clauses, issue keys — is re-extracted from markdown by every checker that needs it. Measured:
Those ratios have been read before as "an engine would only improve the small half". That reading is backwards: they are the price of nothing amortising extraction, paid once per checker, forever. Twenty derivations of one regex is not evidence that regexes are expensive — it is evidence that no fact exists. And they have already diverged — on three axes, not one. Re-counted 2026-08-20 over code lines only ( Axis 1 — case. Five sites match case-insensitively and normalise up ( Axis 2 — boundary. Axis 3 — whether the project prefix is required at all. This was not in the original count, and it is the worst of the three. Four sites are shell
And The shipped defect is concrete. A PR body writing Cost class: readBoard state arrives over the tracker API — bounded, cacheable, resolvable at What this issue lands
A caution this must respect. CLOUD-623's survey classified invariants by input shape and warned that where the hard part is turning prose into structure, an engine over already-normalized facts cannot help. That warning is right about engines and is exactly why this is a fact issue: the point is to do the extraction once and well, not to move the predicate to a different language. The Ready-block clause structure is the hard case — it is markdown with a dialect — and it should be scoped honestly: some clauses are structural (§8's A trap already recorded, not to be rebuilt. CLOUD-251 — "a rule set with no relations still reports the board coherent" — is the vacuous-pass failure. A board fact that resolves to "no relations" must be distinguishable from "could not look", which is CLOUD-757's three-valued contract doing real work here. Acceptance
Two findings filed 2026-08-20 point here. CLOUD-772 holds the volume half of the same defect: 73 independently hand-rolled parsers across nine committed artifacts, Filed from a subprocess-boundary audit; the ratio data is CLOUD-623's and CLOUD-647's, read the other way round. Probe plan
Refinement — Ready (one definition of an issue key, and all three axes chosen rather than inherited) Refinement gate: Definition of Ready & Done. This body carries only specializations. The three divergences are decided here, because "resolve them deliberately" is not a predicate until the resolutions are named.
Split note, 2026-08-29 — the definition landed elsewhere; what is left here is blocked, and by what. This row asked for one definition of an issue key and the conversion of nineteen consumers. The first half is landable and the second is not, so they are now two rows and this one keeps the harder half. CLOUD-1142 carries the definition: the fact producer under THE "CANNOT RETIRE" CLAIM IS WITHDRAWN (2026-09-01) — it was false in both halves, and it has already been paid forThe paragraph below said three programs cannot retire at all. Withdrawn rather than softened, because it is disproven twice over and it has already cost a landed retirement. **Disproven by demonstration. ** Disproven by reading the module. The reasoning cites Already paid for. Replaying the key-derivation count over 400 commits of The lesson generalises past this row: a blocker asserted from one rule while the admitting rule sits beside it is a block reported as a decision, which AGENTS.md already calls a bug. No row should cite "cannot retire" again without naming which What stays here is the conversion, and it is blocked on the retirement campaign's ordinary sequencing rather than on any impossibility. Seventeen of the nineteen sites are So this row's acceptance clause — "at least one existing checker consumes the fact rather than re-extracting, with its extraction half deleted" — arrives with whichever consumer retirement lands first, and it belongs to the shell-retirement campaign's sequencing rather than to this row's own effort. It is recorded here so the next reader does not rediscover it as a surprise mid-build. The definition half has landed (CLOUD-1142): |
|
Warning Review limit reachedNext included review available in 30 minutes. View limit detailsLimit details: You’ve used the included review currently available. Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. Review configuration: ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Free Run ID: 📒 Files selected for processing (3)
Note 🎁 Summarized by CodeRabbit FreeYour organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Essentials by visiting https://app.coderabbit.ai/settings/billing. Comment |
… demand
The two helpers under `carried_claim_tests` reached for `expect()`, which
`clippy::expect_used` refuses in the lib test target as it does everywhere
else in the crate. The `let Ok(..) = .. else { panic!(..) }` form says the
same thing and is what the neighbouring test module already uses.
Refs: CLOUD-1231
|
❌ The last analysis has failed. |
Superseded by #811, which carries CLOUD-1142's commit replayed onto the same base.
The other two commits here are dropped rather than replayed, and that is the finding: they were a second, independent implementation of CLOUD-1231, written two hours after #817 had already landed one on its own branch. #817's version is the one #811 carries, with the
weakenshalf this one had and that one did not added on top.