fix(board): the column vocabulary stops being an engine constant - #911
Draft
wenzowski wants to merge 6 commits into
Draft
fix(board): the column vocabulary stops being an engine constant#911wenzowski wants to merge 6 commits into
wenzowski wants to merge 6 commits into
Conversation
Non-negotiable rule 1 binds MECHANISMS, not only string literals, and `landed` and `claim` decided over one board's COLUMN NAMES. Linear ships Todo/In Progress/In Review/Done; Jira ships To Do/In Development; a GitHub Project ships whatever the owner typed. This is rule 1's violation in its worst form, because the failure is silent in the direction that matters. Off this board every comparison is false, so `is_started` never fires and the landed-honesty sweep reports ZERO FINDINGS over a board full of dishonest columns, while `claim` never refuses. A gate that cannot fire and a gate that found nothing emit the same bytes and the same exit code. The words move to a `[board]` table. `board::Columns` resolves it, and `board_columns()` is `board_grammar()`'s sibling for the same reason CLOUD-1100 gave one table over. ## Absent is could-not-look, never a default The load-bearing decision. An undeclared column refuses BY NAME rather than falling back to this repository's words: a default would reinstate the violation with one more step in front of it and restore exactly the property that let the constants survive — the dead path and the working path answering identically. `Columns::started` therefore reads an EMPTY set as undeclared rather than as "no column means started", since a set matching nothing reports every row as not-advanced, which is the silent all-clear. `judge` refuses the RUN when the queue is undeclared, not every row: a claim gate that cannot name the queue has not decided nothing is pullable. `Reason::wants` returns `Option`, so an undeclared column costs the ` -> <column>` remedy and never the finding itself. ## The duplicate pull-request predicate, resolved `claim` required the literal `github.com/`; `landed` carried a host-free copy whose doc argues forge URL SHAPE is not consumer vocabulary. That reasoning is right, so the host-free one is now the single spelling. This also fixes a live defect: off GitHub `live_pull_request` answered `None` for every real pull request, so the open-competitor check was dead. Recorded rather than silently widened: a GitLab merge request is `/merge_requests/<n>` and still does not match. Narrower than the host literal it replaces — matching every `/<word>/<digits>` would admit `/issues/12` and turn an issue link into a claim. Two sites CLOUD-1623's body does not carry, found on re-read: a second independent `is_in_progress` in `impl Claim`, and `demand()`'s refusal prose, which named a column a consumer's board may not have. `batten.toml`'s `[board]` table was written under admission 8b378ecbdd0e721328b63d4e7661c18704d71af2534b07a81665dcdfa9280835. Refs: CLOUD-1623 Admits: 8b378ecbdd0e721328b63d4e7661c18704d71af2534b07a81665dcdfa9280835 Admits-rule: protected-mutation Admits-verdict: path write refused Admits-subject: batten.toml Admits-anchor: call:ea307944cd9ec0c1e4f52720f78ce49d08aa7761 Admits-epoch: ed0cc67ec403a5849f97cbe66586e656ab21f7b6d2015725b2d51a2a2c6f5b91 Admits-author: alec@wenzowski.com Admits-prev: - Admits-answer-lost: The engine half without the config half is a dead gate. `landed check`, the abandonment drain and `claim check` all resolve their columns through `board::Columns`, and an undeclared table is could-not-look by design — so shipping the Rust change alone turns three working gates into three that decide nothing, while `mise run verify` reports the tree clean. That is precisely the failure CLOUD-1623 exists to remove, reintroduced by landing half of its fix. Admits-answer-precondition: No batten surface writes a config table. `batten config` inspects only, and `batten init` refuses to overwrite an existing batten.toml, so neither can add a `[board]` table to a file that already exists. The change is the config half of CLOUD-1623 (claimed, receipt minted on this branch): four column names moving out of `crates/batten/src/landed.rs` and `claim.rs`, where they were non-negotiable rule 1's violation. It is a 43-line addition to one table in a draft PR, visible in the diff a reviewer reads before it lands. Admits-answer-rejected-route: `patch`. Tried and refused identically (`path write refused batten.toml patch protected-mutation`), which is correct: the guard is on the path, not the program, so routing the same bytes through a second writer would be evading the class rather than satisfying it. `config read first` does not apply either — the file was read directly and through `batten config` before this write, and reading cannot add a table. `run first` names no task that authors config. Admits: 827500be1882ead25b401784a3c9845c5120c7b9cc906f58d9fc993b3d2bec68 Admits-rule: protected-mutation Admits-verdict: path write refused Admits-subject: .serena/memories/core.md Admits-anchor: call:ea307944cd9ec0c1e4f52720f78ce49d08aa7761 Admits-epoch: 24cb2114988016256569172b8027e2e9b6019765eb89934424819c2aa9ac155b Admits-author: alec@wenzowski.com Admits-prev: - Admits-answer-lost: `module-map-check` refuses the commit outright, so CLOUD-1623 cannot land at all. Beyond that, its stated defect is the point: `mem:core` is the single authority on what each module owns, and a module added without its row leaves that authority silently incomplete and the rule pointing at it untrue — measured once already on `severity.rs`, which landed past a green gate with no row. A new module carrying the `[board]` seam is exactly the row a later reader needs. Admits-answer-precondition: The sanctioned writer for `.serena/memories/**` is the Serena MCP tools, and that server failed to connect this session (CONNECTION_CLOSED), so no surface can express the change. The change is one row naming `board.rs` in the module map, required by `module-map-check`, which refused the commit until it existed: every tracked `crates/*/src/*.rs` must appear in `mem:core`. It is 13 lines in a draft PR, visible in the diff a reviewer reads before it lands. Admits-answer-rejected-route: `patch`. Refused identically on the sibling path this commit also writes (`path write refused batten.toml patch protected-mutation`); the guard is on the path rather than the program, so routing the same bytes through a second writer evades the class instead of satisfying it. `config read first` does not apply — this file is not config, and reading cannot add a row. `run first` names no task that authors the module map; `module-map-check` only reports the gap.
Non-negotiable rule 2: a rule without a runnable gate is half a change. 32f9876 took the board's column names out of the engine; nothing yet refused the next one, and they arrived in the first place because nothing refused them. This file already carries the measured cost of that shape — twenty issue-key derivations accumulated because nothing refused the twenty-first before it was typed. ## Why this matches the comparison and not the vocabulary The obvious row is unlandable, and that is a measurement. Banning the four column WORDS under `crates/**` fires on 126 occurrences across 18 files, nearly all legitimate: integration fixtures building board payloads (`landed_check.rs` 19, `claim.rs` 25, `board_receipts.rs` 12), unit-test vocabulary, and prose recording why the released column is in the started set. That row either never lands or lands carrying an exemption list longer than itself — and an exemption is where the next violation hides. The violation was never the word. It was COMPARING A STATUS AGAINST A LITERAL, because that is the line that decides, and the line that answers false on every other board while reading as a clean sweep. So the regex is the comparison shape in both operand orders, and it reaches exactly the construct `board::Columns` replaced. It lands at ZERO with no `exclude`. The only match after the seam commit was `board.rs`'s own anti-vacuity test asserting a foreign board's set lacks this repository's word; that assertion now compares the whole set, which is stronger anyway — a negative assertion also passes for a resolver that dropped every column. `checks_green.rs`'s `status != "completed"` and `hk.rs`'s `!= "included"` are deliberately not reached. They compare a check run's state and a plan's state — a forge's and this engine's own vocabularies, not a tracker's. A row keyed on the bare field name would have caught both and taught the next reader that the ban is about the field. ## Shown able to fail The acceptance CLOUD-1626 records as missing from the last rule-1 gate. Clean tree exits 0; one planted `s == "In Progress"` in `receipt.rs` exits 2 reporting `crates/batten/src/receipt.rs:2477 no-board-column-in-core`; reverting returns it to 0. Pointer-only per rule 4 — `path:line` and the id, never the matched line. BREAKING CHANGE: `claim::judge`, `landed::decide` and `landed::drain` each take the board's column vocabulary as a parameter, introduced by the seam commit on this branch. The vocabulary cannot be defaulted inside them — an undeclared column is could-not-look, and a default would restore the dead-path-reads-as-clean property the seam exists to remove — so the caller resolves `board::Columns` and passes it in. Refs: CLOUD-1623 Admits: a953ddc5aa7b792a4f31157caa36d9f1ef8de40802c4d0d2b9c3e592b60d5c94 Admits-rule: protected-mutation Admits-verdict: path write refused Admits-subject: batten.toml Admits-anchor: call:32f98767e586a4476159245512fc3d72c618f899 Admits-epoch: 24cb2114988016256569172b8027e2e9b6019765eb89934424819c2aa9ac155b Admits-author: alec@wenzowski.com Admits-prev: 8b378ecbdd0e721328b63d4e7661c18704d71af2534b07a81665dcdfa9280835 Admits-answer-lost: CLOUD-1623 stays half-done and silently re-openable. The column literals are out of the engine as of 32f9876, but nothing refuses the next one: the constants originally arrived because no gate refused them, and non-negotiable rule 2 is explicit that a rule shipped without its mechanism is prose. Measured precedent in this file: twenty issue-key derivations accumulated for exactly this reason, and `issue-key-derivations-not-growing` exists because nothing refused the twenty-first before it was typed. Admits-answer-precondition: No batten surface authors a `[[rule]]` row. `batten config` inspects only and `batten init` refuses to overwrite an existing file, so writing `batten.toml` directly is the only route to add the gate. This is the rule-2 half of CLOUD-1623 (claimed, receipt minted on this branch): the seam landed in 32f9876 and a rule without a runnable mechanism is half a change. It is one `[[rule]]` row plus its comment, in a draft PR, visible in the diff a reviewer reads before it lands. Admits-answer-rejected-route: `patch`. Refused identically on this same path earlier in this session (`path write refused batten.toml patch protected-mutation`); the guard is on the path rather than the program, so routing the same bytes through another writer evades the class instead of satisfying it. `config read first` does not apply — the file was read directly before this write and reading cannot add a row. `run first` names no task that authors a rule; `batten check` only evaluates rows that already exist.
Three census gates refused the seam commit, all correctly and all the same class: a new module and a new config key arrived without the rows that make them decidable. None was a code defect. ## The table moves into the module that reads it `Board` was declared in `config.rs` while `Columns` — the resolved form and every predicate over it — lived in `board.rs`. That is the split `mcp::McpConfig`, `recorder::Declared` and `deferral` each avoid: a module that exists owns its own declaration, so the type and the predicate reading it cannot drift apart across a module boundary. It also settles the layering. `board` now reaches NOTHING in this crate, not even `error`. `config` reads it at load; `landed`, `claim` and `lib` read it at decision time; it reads none of them. That is `secret`'s placement arrived at from the same direction, and it is the placement rather than an accident of size: this module exists so a gate's verdict about a board is stated in the CONSUMER's words, and an edge from here to any decider would make the honesty of those words conditional on that decider's own layer. ## `trust::CENSUS` — not policy-bearing, for the structural reason `board` is read by gates, so the verdict is not that it lacks policy weight. It is that an override cannot speak to it: the key is absent from `OverrideConfig` and `resolve` reads the table from the committed authority alone, which is `contract`'s guarantee for `epoch`'s reason. That makes the obvious attack unwritable — an uncommitted layer renaming `ready` to a column every row already sits in would make every row pullable at once. A weakening row would be the wrong instrument, which is `mcp`'s point: it reports a DIRECTION, and a column name has none. Renaming a queue is not more or less permissive as config; which rows it admits depends on where the board has put them, which is tracker state rather than a bar this file sets. ## `module-layering` — the placement the rule asked for Its coverage clause states absence is an error rather than an allow, and it named `board` on the gate before landing, as it has for every module since `preset`. The entry is additive placement, not an exemption: it adds no allowance and removes no refusal, and a module violating a forbidden edge would still be refused. Written under admission 06891c5cdc3e1e55ce23c216053798934226f5904bb2ef87543efa256874e5a2. `policy/**` is protected by derivation — enabling a module protects it, so an agent's context cannot influence the rules it is judged by — and that grant is this edit alone, not a standing one. Refs: CLOUD-1623 Admits: 06891c5cdc3e1e55ce23c216053798934226f5904bb2ef87543efa256874e5a2 Admits-rule: protected-mutation Admits-verdict: path write refused Admits-subject: policy/module-layering.rego Admits-anchor: call:1aeaf9af0e5e2be0788f653ae068221893c97952 Admits-epoch: c99c0afd250a1f693c40bca4e07d5d52d391ef1ecd443fa225f2d10f4629573f Admits-author: alec@wenzowski.com Admits-prev: - Admits-answer-lost: `module-layering` refuses the tree, so CLOUD-1623 cannot land. The refusal is the rule's own coverage clause — "absence is an error, not an allow" — firing on `board.rs`, a module I added and did not place. Not overriding does not leave the rule enforcing more; it leaves a correct finding unanswered and the branch stuck, because the only answer the rule accepts is the placement it is asking for. Admits-answer-precondition: No surface writes a policy module. `policy/**` is protected by derivation rather than by the `protected` list — enabling a module protects it, so an agent's context cannot influence the rules it is judged by — and its `[[redirect]]` names a pull request, not a command. This IS that pull request: branch `claude/rule-1-seam-bundle-0nsnym`, draft, reviewed before it lands. The change is one name added to `declared_modules` plus its placement comment, and `mise run policy-test` checks it. Admits-answer-rejected-route: `patch`. Refused identically on the two sibling protected paths this branch already writes, and correctly: the guard is on the path rather than the program, so routing the same bytes through another writer evades the class instead of satisfying it. `config read first` does not apply — a policy module is not config, and reading cannot add a row. `run first` names no task that authors the table; `module-layering` only reports the gap. NOTE THE NARROWNESS: this is additive PLACEMENT, not an exemption — the same entry `preset`, `agent`, `recorder`, `secret` and `deferral` each received on arrival. It adds no allowance and removes no refusal; a module that violated a forbidden edge would still be refused.
`claim check` now refuses the run when `board.ready` is undeclared, so two cases in this suite went red against a fixture repository that declares no `[board]`. The refusal is correct and the fixture was incomplete: a repository that has declared no board HAS no ready queue, and answering "nothing is pullable" over one would be the guess the seam exists to remove. This is the same sentence the fixture already carries one table over. CLOUD-1100 made the Ready grammar the consumer's and left this comment here: "Without these rows `claim check` reports could-not-look naming the first missing id — which is the correct answer for a repository that has declared no Ready grammar, and not what this suite is about." The columns are that sentence again, and the two now sit together because neither the grammar nor the vocabulary is the engine's to assume. Refs: CLOUD-1623
…ng it clean **The seam reintroduced its own defect one layer up, and this removes it.** `claim::judge` was made to refuse when the board is undeclared; `landed` was left to decide with the columns it had. Undeclared, it has none — every comparison against a row's status is false, so `decide` returns ZERO FINDINGS and the verb exits 0 over a board it never read. That is byte-identical to a clean sweep, which is the exact failure this row exists to remove. A gate that cannot fire and a gate that found nothing emit the same bytes and the same exit code, and shipping the seam with this in place would have moved the dead path rather than closing it. Both verbs now demand the columns they decide over, before deciding. ## Both directions, not the one the payload happens to exercise `landed check` demands `in_progress` AND `started`. Demanding only the arm a given payload reaches would leave a run that answered half the disjunction and said nothing about the other half — the silently-halved sweep CLOUD-1458 already paid for once. `landed abandoned` demands `in_progress`, where the edge is sharper: every candidate is SELECTED by that column, so undeclared yields an empty candidate set and the drain reports no abandoned claims over a board full of them. An over-reporting drain gets switched off; a silently empty one is never noticed. ## The fixtures declare the board they are judged against `common::declared_board` reads the committed `[board]` table, mirroring `declared_patterns` and for its reason: this repository's columns have one authority, and a fixture holding a second copy drifts the day the board is renamed. `landed_check.rs` shadows `scratch` so all eleven cases build on a declared board rather than asserting about a missing column. Refs: CLOUD-1623
`claim check` declares the `-J` data channel, and a data channel emits its document UNCONDITIONALLY — one that is sometimes absent is unparseable for the caller that asked for it. The board refusal lands before any output, so over a fixture declaring no `[board]` the verb emitted nothing at all and the purity census went red. The fixture is the fix rather than the refusal, and this fixture's own doc already says why: it exists to be "a config every data-emitting verb can actually answer from", because "the census is about the output contract; supplying each verb's minimum input is the fixture's job". A repository that declares no board HAS no ready queue, so refusing there is correct and simply not what this census asks about. That makes `[board]` the fifth minimum input this fixture carries, beside the Ready grammar it already carried for the identical reason (CLOUD-1100): both are the consumer's vocabulary, and neither is the engine's to assume. Read from the committed table rather than re-typed, so the fixture cannot drift from the columns it exercises. Refs: CLOUD-1623
|
Warning Review limit reachedNext included review available in 35 minutes. View limit detailsLimit details: You’ve used the included review currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (15)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
❌ The last analysis has failed. |
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 free
to 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.
Closes CLOUD-1623.
Non-negotiable rule 1 binds MECHANISMS, not only string literals, and
landedand
claimdecided over one board's COLUMN NAMES. Linear shipsTodo/In Progress/In Review/Done; Jira ships To Do/In Development; a GitHub
Project ships whatever the owner typed.
This is rule 1's violation in its worst form, because the failure is silent in
the direction that matters. Off this board every comparison is false, so
is_startednever fires and the landed-honesty sweep reports zero findingsover a board full of dishonest columns, while
claimnever refuses. A gatethat cannot fire and a gate that found nothing emit the same bytes and the same
exit code.
The seam
The words move to a
[board]table read throughboard::Columns.board_columns()isboard_grammar()'s sibling, for the reason CLOUD-1100 gaveone table over.
Absent is could-not-look, never a default. An undeclared column refuses BY
NAME rather than falling back to this repository's words: a default would
reinstate the violation with one more step in front of it and restore exactly the
property that let the constants survive.
Columns::startedreads an EMPTY set asundeclared for the same reason — a set matching nothing reports every row as
not-advanced, which is the silent all-clear.
The half that nearly shipped the same defect back
claim::judgerefuses an undeclared board, butlanded checkandlanded abandonedinitially did not — every predicate answered false, so theyreturned zero findings and exit 0 over a board they had never read. That is
byte-identical to a clean sweep. Both verbs now DEMAND the columns they decide
over:
landed checkneeds bothin_progressandstarted, not merely whicheverthe payload happens to exercise, because a run answering half the disjunction
silently is the halved sweep CLOUD-1458 already paid for.
The duplicate predicate, resolved in the direction the tree already argued
claimrequired the literalgithub.com/;landedcarried a host-free copywhose doc argues forge URL SHAPE is not consumer vocabulary. That reasoning is
right, so the host-free one is now the single spelling. This also fixes a live
defect: off GitHub,
live_pull_requestansweredNonefor every real pullrequest, so the open-competitor check was dead.
Recorded rather than silently widened: a GitLab merge request is
/merge_requests/<n>and still does not match. Narrower than the host literal itreplaces — matching every
/<word>/<digits>would admit/issues/12.The gate (rule 2)
no-board-column-in-corerefuses the next column literal. It matches theCOMPARISON rather than the vocabulary, because banning the four words fires on
126 occurrences across 18 files — nearly all legitimate fixtures and prose — and
would land carrying an exemption list longer than itself. Shown able to fail: a
planted
s == "In Progress"exits 2 namingpath:line; reverting returns it to 0.Sites the row did not carry
Found on re-read: a second independent
is_in_progressinimpl Claim, anddemand()'s refusal prose, which named a column a consumer's board may not have.Fixtures
Three suites invoke these verbs and each now declares the board it is judged
against, read from the committed table via
common::declared_board()rather thanre-spelled — one authority for this repository's columns, per the same doctrine
the seam itself enforces.