Repository navigation
feat(policy): the landing-loop preset, and the wrong-surface guard it walked into - #800
Conversation
CLOUD-1269 Ship the `landing-loop` preset — CLOUD-1170 names PRESET as the lease predicate's home and no row owns building one, so the generic half of 3,538 lines of landing bash has a disposition and no destination
Why CLOUD-1170 took the answer and named the home in as many words:
**Nothing owns building it. **CLOUD-1170 owns the facts (the supplied instant, the liveness record) and its §2 excludes retiring any member. CLOUD-1148 owns the migration and, until an owner correction on 2026-08-31, excluded "shipping a landing preset" outright. So the disposition was recorded twice and the destination zero times — the punt shape AGENTS.md names: an unbuilt mechanism awaited instead of the instance in hand. This row is the destination. What is generic, and why that is the test that mattersLanding judgements split three ways, and only one third belongs in a preset:
The four predicates
**Its fail-open asymmetry is conserved verbatim, and that is the load-bearing half. ** The instant reaches the predicate as a RESOLVED TOKEN, not as arithmeticOwner decision, 2026-08-31, and it is a refinement of CLOUD-1170's "one value per invocation, on the input, treated as data" rather than a contradiction of it: the engine is still handed the instant and still never reads a clock. What changes is what Rego sees. The precedent is landed.
So the boundary compares and the module reads
A raw The two ways a preset ships DEAD, both already measured hereNot cautionary: both have happened in this repository.
And per CLOUD-857: anchor on Refinement — Ready (ship the generic landing judgements as a vendored, overridable bundle) Refinement gate: Definition of Ready & Done. This body carries only specializations.
Acceptance
Filed 2026-08-31 after an owner decision that the landing loop ships as an overridable preset. The disposition existed on CLOUD-1170 and the exclusion on CLOUD-1148; neither was a destination. CLOUD-1279 `check_tree_paths_are_emittable` early-returns for every non-tree scope, so CLOUD-845's dead-gate class survives intact on the mediated call — and the preset suite structurally cannot see it
Why CLOUD-845 is the class: "a module copied from The mechanism that landed is if rule.scope != crate::rules::RuleScope::Tree {
return Ok(());
}So the guard covers exactly one of the two scopes, and the class it closed is still open in the other. A The reverse direction is unguarded too, for a different reason: the function only inspects paths it can The suite cannot catch it either, and that is the load-bearing halfA guard with a hole is one problem; a guard with a hole and a test that structurally cannot see the hole is the shape that ships.
Under that fabricated scope the guard early-returns, and the suite then grades the module against That is not hypothetical for this repository: CLOUD-1161's The declaration side has the matching hole, and it is SPLIT OUT rather than carried here
That half is CLOUD-1282's, and splitting it is a correction to this row rather than a deferral of it. This row's original acceptance asked for five named columns — So the declaration half needs a DERIVED refusal, off the same fact model the module half now reads, and that is a different change with a different blast radius: it can refuse rows that load today. Two changes, two rows. Found while planning CLOUD-1269, which ships predicates on both surfaces and is the first row that would walk into this. Filing rather than working around it locally: CLOUD-1269 can add a real-scope load arm for its own two presets, and that is a fixture, not a mechanism. Refinement — Ready (make a wrong-surface key fail at load, on both scopes) Refinement gate: Definition of Ready & Done. This body carries only specializations.
Acceptance
CLOUD-845 A module copied from `policy.rs`'s own doc passes `policy test` green and gates nothing: `input.tree.tracked` is documented, never built, and a `with input as` test fabricates the shape the engine cannot produce
Why Reproduced end to end against The reproduction
Two modules in one enabled bundle. One copied verbatim from that doc; one written against what Both tests pass. Only one predicate decides anything. Why, and both halves are needed to produce it1. serde_json::json!({ "tree": { "documents": …, "missing": … } })
2. Rego makes it silent. Iterating an undefined path yields nothing, so the rule body is undefined, so the 3. The safety net does fire in the narrow case, and that is worth recording because it shows the gap precisely. With no So CLOUD-835 and CLOUD-647's sweep catch no test. They do not catch a test against an impossible input — and the second is what an author following the doc will write, because they will copy the input shape from the same paragraph they copied the predicate from. Why this blocks the retirement rather than being tidy-upCLOUD-843's wave 1 is ~20 tree-scoped gates. Every agent doing one starts from The fix, in three parts
Refinement — Ready Refinement gate: Definition of Ready & Done. This body carries only specializations.
Acceptance
Correction 2026-08-21 — The fix section says "emit
So the source is two sources. For the working tree, A second false-green channel, on the same surface.
So a migrated gate can go silent-and-green by declaring a path with the wrong extension, exactly as it can by naming a field the engine never emits. Same failure, second road, same §8 is amended: |
ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Free Run ID: 📒 Files selected for processing (3)
Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthroughThe change adds the Merge Risk: ⚪ Minimal · up to The PR adds the landing-loop preset and scope-aware policy validation with passing checks; no actionable merge-blocking risk remains beyond normal review. 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 |
eea7955 to
dacf398
Compare
CLOUD-1170 named PRESET as the home for a landing predicate and owned the facts; CLOUD-1148 owned the migration and excluded a preset outright until that clause was struck. So the disposition was recorded twice and the destination zero times. CLOUD-1269 is the destination. WHAT COUNTS AS JUDGED IS THE GENERIC HALF, AND IT IS NOT "IS IT GREEN". Which check carries a verdict about a given repository is that repository's fact and may not travel inside a bundle that ships to every consumer (rule 1), so the preset asks only whether the forge recorded anything for a declared commit. `policy/forge-verdict-required.rego` keeps the green question, because `final` is this repository's name for its fan-in. A commit graded green fires one; a commit graded red fires both, and they say different things — do not re-run it, and do not land it. Three answers and the module reads all three, on `input.tree.forge`'s own contract: `null` is could-not-look, a commit ABSENT has no record and is what a first run looks like, and a commit PRESENT carrying verdicts is the only state refused. `count(checks) > 0` separates judged from looked-at-and-silent, because a forge that reported nothing has left no answer to conserve. The override route goes through `admit()`, which `main` landed concurrently and which this reaches for rather than duplicating. This commit was written against a tree where no vendored row carried a `RouteKind::Override` and said so; that claim was overtaken while it waited for the lease, and the rebase resolved a same-shape helper of its own (`overridden()`) in `admit()`'s favour. The reason for having a constructor per kind is unchanged and is `main`'s: `validate_route` enforces the kind/precondition pairing in BOTH directions, so a helper per kind makes the wrong combination unwritable rather than merely refused. `warn`, NOT `deny`, and the first landing is the reason. `land` re-verifies and re-waits every lap by design — a rebase mints a new SHA and the receipts keyed to the old one are gone — so the loop legitimately reaches graded commits and a deny would refuse the lap that is working. Land it reporting, read the firing rate over a session, promote it with the measurement (CLOUD-320). THREE OF THE ROW'S FOUR PREDICATES NAME FACTS THAT CANNOT ANSWER THEM, and CLOUD-1280 carries all three with citations rather than leaving them to be re-derived. `lease-authorises-the-branch` needs the lease's expiry, because `authorises` treats an expired lease as ALLOW — and there is no tree-surface clock by CLOUD-1170's own decision, no `[program]` emitting a freshness token, and one `recorder::Ask` variant; shipping it would deny where `authorises` allows, which is the stop-the-world the row names as its own failure mode. `target-is-fast-forwardable` names two facts that answer a different question: `git-refs` deliberately dropped reachability (`no_ancestry_decides_merged_ness`) and `landing` answers patch identity. `spend-needs-a-verified-head` was written and wired before `config-lint` refused its row — `checks` is a `receipt` column, so a `policy` row cannot cause `input.facts.receipts` to be resolved, and the module would have read null, taken could-not-look, and allowed every call. That last one is the argument for declaring facts on the row rather than inheriting them: the declaration is what made the dead gate visible at the gate instead of in production. `every_preset_loads_at_the_scope_it_is_enabled_with` is the arm the existing suite structurally cannot provide. `every_shipped_preset_passes_its_own_suite` fabricates a `mediated_call` scope for every preset, which early-returns `check_tree_paths_are_emittable` — so a tree module reading a key the engine never emits ships green in both tiers. CLOUD-1279 owns closing that hole in the guard; this closes it for the presets whose real scope is known here. Refs: CLOUD-1269 Admits: 8427b359e00e0b95d087bb6d24490e253e6a31de4334706e1e94f81973449d32 Admits-rule: protected-mutation Admits-verdict: V-PROTECTED-MUTATION Admits-subject: batten.toml Admits-head: a673667 Admits-epoch: 364b7f16980ed2be913f46a3d0cfcd1d20e63d9f4fc112db1f2744009e96c419 Admits-author: alec@wenzowski.com Admits-prev: - Admits-answer-lost: Without the row the preset ships enabled nowhere, so the vendored surface is exercised only by its own tests — which is precisely the gap CLOUD-836's consumer-#1 clause exists to close, and which CLOUD-1161's two dead predicates measured the cost of. The bundle would be code nobody runs. Admits-answer-precondition: `batten.toml` IS the owning surface for a `[[rule]]` row: it is the one authority the engine reads for rules, so a row enabling a preset cannot be written anywhere else. The change is the `landing-loop-preset` row required by CLOUD-1269's "consumer #1 eats the same food" clause, and it lands in a PR diff a reviewer reads. Admits-answer-rejected-route: R-USE-THE-OWNING-SURFACE is circular for this subject, because `batten.toml` IS the owning surface for a `[[rule]]` row — there is no other file the engine consults for one. R-RESTORE-IT would revert the enabling row, which is the deliverable itself.
…845's class
`check_tree_paths_are_emittable` refuses a tree-scoped module that reads an
`input.tree.<key>` the engine never emits — CLOUD-845's dead-gate class, where
Rego takes undefined as *does not hold* so the violation set is empty and a dead
gate is byte-identical to a clean tree. CLOUD-876 took that as the general fix.
It opened with `if rule.scope != RuleScope::Tree { return Ok(()) }`, so the class
it closed stayed open one scope over: a `mediated_call` module reading
`input.tree.*` loaded clean, evaluated, refused nothing, and reported green. The
clause above it said mediated rows "are untouched — they read `input.call` and
`input.facts`", which is what such a module SHOULD read and was never a reason
nothing checked.
TWO ARMS, DELIBERATELY ASYMMETRIC. On the tree surface the question is WHICH
key, because `tree_document` emits a known set and a typo inside it is the likely
error. On the mediated surface it is WHETHER ANY `tree.` path appears, because
`hook::call_document` emits `{call, facts}` and never `tree` — so no key is the
right one and none has to be enumerated. It cannot refuse a correct module:
reaching a key your own document does not carry is the whole defect.
THE TEST HALF IS WHY THIS SURVIVED, AND IT IS THE LARGER HALF. Three table-wide
loops in `policy_presets.rs` went through `preset_row`, which fabricated
`mediated_call` for EVERY preset so one loop could cover the table — and that
fabrication is exactly what early-returned the guard. `shell-hygiene` and
`ci-hygiene` are enabled `scope = "tree"` and decide over files; asking them for
a verdict under an invented surface is the fabricated-shape defect the module
rules name, one level up. They go through `row_at_real_scope` now, off one
`PRESET_SCOPES` table, so the loops still cover the table and stop inventing the
surface while doing it.
The discriminating case is `a_mediated_module_reading_a_tree_key_is_refused_at_load`
and its mirror: the SAME module reading `input.tree.tracked` is refused mediated
and loads tree-scoped. The mirror is load-bearing rather than decorative —
`tracked` is a key the engine emits perfectly well on the other surface, so a
guard comparing against a key set would have waved it through, and without the
pair the refusal could be satisfied by one that refuses everything (CLOUD-418).
Found while building CLOUD-1269, whose own preset would have been the first to
walk into it; `filed-over-own-diff` then priced filing it over a diff already
touching `policy.rs` and `policy_presets.rs`, which is the gate working.
Closes CLOUD-1279.
Refs: CLOUD-845, CLOUD-876, CLOUD-1269, CLOUD-418
… the fixtures leaning on the hole
Completes the previous commit's guard. That one closed the mediated direction —
a `mediated_call` module reading `input.tree.*` — and left the mirror open for a
different reason: the loop only inspected paths it could `strip_prefix("tree.")`,
so a TREE-scoped module reading `input.facts.*` or `input.call.*` was never
looked at at all. `rules::tree_document` emits neither, so such a module is dead
in exactly the way CLOUD-845 records, and the guard walked straight past it.
Both arms now carry a deny case and its anti-vacuity mirror, and the mirrors are
what make these gates rather than blanket refusals (CLOUD-418): the SAME module
is refused on the surface that does not emit its key and loads on the one that
does. That matters here more than usual, because `input.tree.tracked` and
`input.facts.receipts` are both keys the engine emits perfectly well — on the
other surface. A guard comparing against a key set rather than against the
surface would have waved both through.
THE GUARD IMMEDIATELY FOUND THREE FIXTURES LEANING ON THE HOLE, which is the
part worth recording. `policy_test_suite.rs`'s `CORRECT` module reads
`input.call.command`, and three tree cases paired it with `scope = "tree"` —
they are about declared document fixtures rather than about the predicate, so
the module was incidental. Under the hole they passed: two asserted fixture
machinery over a predicate that could never fire, and the third asserted the
shape of a report for a suite that "did not run" when nothing could have run.
`CORRECT_TREE` reads `input.tree.documents`, so those three now exercise the
fixture machinery over a predicate that can actually decide.
That is the argument for the guard stated as a measurement rather than as a
principle: the first thing it judged was this repository's own test corpus, and
it was right three times.
Refs: CLOUD-1279, CLOUD-845, CLOUD-876, CLOUD-418
dacf398 to
046855b
Compare
|
❌ The last analysis has failed. |
|
/fast-forward |
Closes CLOUD-1269.
Closes CLOUD-1279.
DO-NOT-CLOSE CLOUD-845
CLOUD-845 is cited as the CLASS this change belongs to, not as work it completes — it is already Done, and CLOUD-876 took its general fix. What landed here is that fix's missing scope, which is CLOUD-1279's.
CLOUD-1170 named PRESET as the home for a landing predicate and owned the facts; CLOUD-1148 owned the migration and excluded a preset outright until that clause was struck. The disposition was recorded twice and the destination zero times. CLOUD-1269 was the destination — and building it turned up that most of what it specified cannot be built.
What shipped
landing-loop, tree-scoped, one predicate:graded-head-is-not-regradedoverinput.tree.forge. A commit that has not changed cannot get a different verdict, so a second run over it buys an answer already recorded and spends the metered tier to do it.What counts as judged is the generic half, and it is not "is it green". Which check carries a verdict about a repository is that repository's fact and may not travel in a bundle that ships to every consumer (rule 1), so the preset asks only whether the forge recorded anything.
policy/forge-verdict-required.regokeeps the green question, becausefinalis this repository's name for its fan-in. A commit graded green fires one; a commit graded red fires both, and they say different things — do not re-run it, and do not land it.warn, notdeny, and the first landing is the reason:landre-verifies every lap by design, so the loop legitimately reaches graded commits and a deny would refuse the lap that is working. Land reporting, read the firing rate, promote with the measurement (CLOUD-320).overridden()is the firstRouteKind::Overridein the vendored table — every existing row usesrun()/read(), which hardcodeprecondition: None. Stated as a constructor becausevalidate_routeenforces the kind/precondition pairing in both directions.Three of the four predicates could not be built, and CLOUD-1280 carries them
Not a scoping choice — each names facts that cannot answer it:
lease-authorises-the-branchauthorisestreats an expired lease as ALLOW, and lease freshness is unresolvable on the tree surface. No clock there by CLOUD-1170's own decision, no[program]emitting a freshness token, onerecorder::Askvariant. Shipping it would deny whereauthorisesallows — the stop-the-world CLOUD-1269 names as its own failure mode.target-is-fast-forwardablegit-refsdeliberately dropped reachability (no_ancestry_decides_merged_ness) andlandinganswers patch identity. Neither decides descendant-ness.spend-needs-a-verified-headconfig-lintrefused the row:checksis areceiptcolumn, so apolicyrow cannot causeinput.facts.receiptsto resolve. It would have readnull, taken could-not-look, and allowed every call.PR #793 independently hit the lease blocker from the receipt side (CLOUD-1275) and it fails in the same direction — refuse-everything, where
authorisesfails open. Two mechanisms, one shape of failure.The guard, which was not planned
filed-over-own-diffpriced filing CLOUD-1279 over a diff already touchingpolicy.rsandpolicy_presets.rs. It was right, so the fix landed here.check_tree_paths_are_emittableopened withif rule.scope != RuleScope::Tree { return Ok(()) }, so CLOUD-845's dead-gate class — closed by CLOUD-876 — survived intact one scope over. Two arms now, deliberately asymmetric: on the tree surface the question is which key; on the mediated surface it is whether anytree.path appears, sincecall_documentemits{call, facts}and nevertree. Both arms carry a deny case and its anti-vacuity mirror — the same module refused on the surface that does not emit its key and loading on the one that does, which matters becauseinput.tree.trackedandinput.facts.receiptsare both keys the engine emits perfectly well on the other surface.The test half was the larger half. Three table-wide loops went through
preset_row, which fabricatedmediated_callfor every preset so one loop could cover the table — and that fabrication is exactly what early-returned the guard. They go throughrow_at_real_scopenow, off onePRESET_SCOPEStable.The guard's first act was to catch three of this repository's own fixtures.
policy_test_suite.rs'sCORRECTreadsinput.call.command, and three tree cases paired it withscope = "tree": two asserted fixture machinery over a predicate that could never fire, and one asserted the report shape for a suite that "did not run" when nothing could have run.CORRECT_TREEreadsinput.tree.documents, so they now exercise the machinery over a predicate that can decide.CLOUD-1279's fifth acceptance clause is split to CLOUD-1282: it asked for five named columns to be refused, and the real set is every tree-only column — enumerating five would reproduce the drift the row is about.
Checks
mise run test:cargo— 3469 passed, 0 failed, including both cross-surface pairs and the preset's deny/mirror arms, all withpolicy::Vocabulary::EMPTY(which is howpatterns: &[]is spelled, and the tier that catches a preset shipping dead).Two notes on getting there, recorded rather than absorbed:
tests/land-lock.batscase 929 failed once on a probe-count assertion against wall clock and passed on the re-run. That is CLOUD-448/450's class and this diff touches no shell program; the one permitted re-run is now spent.claim-before-codefired against this session's capture store — a store under$GIT_DIRthat is never committed and dies with the container, so it cannot appear in CI, and this diff touches neither captures nor CLOUD-1188. Cleared withbatten capture prune, which returns the store to what a fresh clone has. The poisoning record was not isolated first, so this is cleared session scratch rather than a diagnosed fix.Boundary
No
mise-tasks/*.shand notests/**/*.batsopened, soV-SHELL-RULE-EDITEDandV-SHELL-RULE-ADDEDare untouched and no program retires here.schema/policy-input.schema.jsonis unchanged — the 24 tree keys stand; only the derived preset-name enum in the two config schemas regenerated, viamise run schema.