Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions .serena/memories/core.md
Original file line number Diff line number Diff line change
Expand Up @@ -955,6 +955,38 @@ NotComputable`, a third answer `Option` cannot express because it cannot tell
the machine around it. Read CLOUD-780 for why a PARTIAL drop was refused —
`reclaim` was the crate's only destructive path and its safety WAS the interlock
a partial drop removes, so it was all four symbols or none.
- `patch.rs` — the in-process patch identity (CLOUD-739), and `git::landing`'s
sole supplier of one. It replaced `git log -p | git patch-id --stable` and, with
it, the twenty-six settings pinned around that pipeline — twenty `git config`
keys, six flags, two environment variables — whose whole purpose was stopping a
host's configuration from changing the answer. In process there is nothing to
pin, so all twenty-six are deleted and NOTHING replaces them.
**The deliverable is the normalisation being DECIDED, not the spawn being gone.**
A `PatchId` is only ever compared against one this same binary made in this same
run, so the requirement is _a_ canonical deterministic identity, never git's
(CLOUD-320 ruled that in writing) — and that licence is what turns four side
effects of tool choice into four choices with reasons. Line numbers stay
excluded, the one behaviour inherited deliberately, because hunk positions are
exactly what shift under the replay the primitive exists to recognise.
**Whitespace becomes SIGNIFICANT, diverging from git**: `patch-id` folds it, so
a whitespace-only difference collides, and the doc this replaced called that
collision _"the safe direction for a primitive whose failure class is a false not
landed"_ — backwards here, because a false LANDED is what suppresses
`completion.unlanded`'s finding. A spurious not-landed is noise, a spurious
landed is a lie. Binary content is identified by blob ids, which RETIRES the
`--binary` caveat (a zlib body _"deterministic for a given zlib but not
guaranteed across zlib builds"_) rather than restating it. Renames stay
undetected, now as a choice rather than as two flags that had to agree.
`imara-diff` is taken DIRECT and `gix-diff/blob` refused: `blob` is monolithic,
and its eight non-imara deps exist to run external diff drivers, clean/smudge
filters, and materialise blobs to disk — honouring `diff.<driver>.command` would
hand back the very host-configuration input the twenty keys removed.
**The rename case is the CLOUD-418 lesson worth carrying**: rename detection is a
pure function of the two trees, so no fixture built out of trees can tell a
detecting build from a non-detecting one, and the test that claimed to gate it
could not go red. It is gated on the SHAPE instead
(`patch::tests::renames_are_not_a_shape_this_identity_can_take`), where the
mutation — a fourth `Kind` — fails the build with E0004.
- `journal.rs` — the store's durable plumbing (CLOUD-78): append shards, a merged
log with `(generation, seqno)` cursors, and the store-format version. Writers
append to their **own** shard, so the concurrent path shares no mutable file and
Expand Down Expand Up @@ -1474,6 +1506,27 @@ judge_fingerprint`, its own domain tag), so a caller can reference content it
field a matched byte can occupy, and byte-stability is a property of the
request SET rather than of the schedule, which is what makes it safe under
CLOUD-850's concurrent acquisition.
- `symbols.rs` — the first `Cost::Effect` fact's acquisition (CLOUD-760). Where
a **name** resolves, asked of the compiler rather than of the text: the census
`.claude/rules/scanning.md` records three answers for — `grep` 14, a syntax
matcher 11, name resolution 9 — because `surface.rs` imports `clap::Command`
bare and no scanner can tell the two types apart. So this module delegates to
`cargo clippy --message-format=json`, with `--force-warn` overriding
`allow`/`expect` so an ENFORCEMENT lint reports as an INVENTORY without the
tree's annotations deciding what is counted.
It generalises `secrets.rs`'s adapter shape rather than copying it — binary
pinned, flags beside the parser, exit reconciled against the parse — and
carries that module's invariant verbatim: **clean is never inferred from a
stream that failed to parse**, so an unreadable stream is `CouldNotLook` and
never an empty census. `Provenance` (tool, version, invocation) travels inside
the fact because §6 byte-stability is a claim about a named producer; `Site`
is pointer-only per rule 4, a path, a line and the lint, with the path made
repository-relative so the answer does not depend on where the checkout sits.
Acquisition is the CALLER's: `rules::symbols_fact` resolves it once at the
boundary and only when a row declared it, and the projection is pure — a
projection that spawned would be the class's whole point undone. `Surface::Hook`
is refused (`tests/facts.rs`'s `no_effect_fact_is_hook_resolvable`), as a
census over `Fact::ALL` rather than an assertion about this one variant.
- `policy.rs` — the policy evaluator (CLOUD-647, CLOUD-689): a `[[rule]]` of
kind `policy` names a **registered** Rego module, and the module decides over
the resolved fact set. It exists because `run` is a flat loop where no row
Expand Down
31 changes: 29 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 39 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,45 @@ gix = { version = "0.86", default-features = false, features = [
"sha1",
"sha256",
] }
# The TREE half of the in-process patch identity (CLOUD-739): which paths a
# commit changed, and the blob ids on each side. Already resolved here at this
# exact version as a non-optional dependency of `gix` itself, so naming it
# directly adds nothing to the closure — it makes reachable what was already
# linked.
#
# `default-features = false` is the whole point: the default enables `blob`,
# whose nine `dep:` entries are the external-diff-driver and worktree-materialising
# machinery described below. `pub mod tree` is ungated, which is why the tree half
# costs nothing and the blob half is bought separately and narrowly.
gix-diff = { version = "0.66", default-features = false }
# The diff algorithm behind the in-process patch identity (CLOUD-739). Hunks in,
# nothing else: it takes two byte slices and returns the edit script. No
# filesystem, no process, no threads, no global state.
#
# TAKEN DIRECTLY RATHER THAN THROUGH `gix/blob-diff`, and that is the whole
# decision. `gix-diff`'s `blob` feature is monolithic — nine `dep:` entries, no
# sub-features — and eight of them exist to reproduce git's full diff pipeline:
# `gix-command` runs external diff drivers (`diff.<driver>.command`),
# `gix-filter` runs clean/smudge filters, and `gix-worktree`/`gix-tempfile`/
# `gix-fs` materialise blobs to disk so those external programs can read them.
# Enabling it links the machinery whose purpose is to shell out and to touch the
# worktree, which is the surface `git.rs` is removing. `gix/blob-diff` is worse
# again: it is an umbrella that also drags `attributes`, and with it
# `gix-pathspec`, `gix-submodule` and `gix-ignore`.
#
# The narrower choice is also the CORRECT one, not merely the cheaper one. The
# implementation being replaced pins 20 `git config` keys for the sole purpose of
# stopping the host's configuration changing the answer; a diff layer that
# honours `diff.<driver>.command` would hand that back. An identity a repository
# can reconfigure is not an identity.
#
# Blob CONTENTS reach it through gix's object database — the access CLOUD-738
# landed — so this adds no filesystem path of its own.
#
# Closure measured in a scratch crate outside this tree, so measuring it did not
# vendor the thing being decided: `foldhash`, `hashbrown`, `memchr`, all three
# already resolved here. Apache-2.0, already in `deny.toml`'s allow-list.
imara-diff = { version = "0.2", default-features = false }
# The policy evaluator (CLOUD-647, CLOUD-689). A `policy` row hands a REGISTERED
# Rego module the resolved fact set and reads back denials; the module decides,
# the engine never acquires. That is what CLOUD-763 admitted to `MediatedCall`
Expand Down
50 changes: 50 additions & 0 deletions batten.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2083,6 +2083,38 @@ severity = "deny"
scope = "tree"
no_fix_reason = "an IO crate reaching the evaluator is closed where it was enabled, not here: `cargo tree -i <crate>` names who turned it on, and the `regorus` feature list in Cargo.toml is where the pin is stated rather than where it is decided"

# The contract half of the config deprecation grammar (CLOUD-360). A key that
# leaves the published schema with no window breaks every consumer still carrying
# it, and breaks them SILENTLY: their config stops loading with an unknown-key
# error naming no successor and no date.
#
# A `command` row for `evaluator-closure-io-free`'s reason, one layer over. The
# subject is not this tree's text but the DIFFERENCE between two published
# surfaces, one of which lives at a git tag — no `forbid` over a line can see
# that, and the schema file itself is derived, so matching on it would gate the
# artifact rather than the change it records.
#
# Tree-scoped and spawning, so it runs under `enforce` and the hk gate and never
# on the mediated path. `glob` names the check's own file, matching the two rows
# above (CLOUD-614): the gate reads `schema/batten.schema.json` at a tag and the
# schema this build derives, so the glob decides WHEN the question is worth
# asking, and asking on a change to the gate itself is the case that must never
# be skipped.
#
# `deny` is earned rather than assumed. §7 required the predicate be replayed
# across history first, and it was: over all 112 release tags, zero would have
# reported a violation (85 clean, 27 exit 3 for tags predating the committed
# schema). A gate that would have refused past releases fires on work nobody can
# now fix; this one would not have.
[[rule]]
id = "no-key-leaves-the-schema-unannounced"
kind = "command"
glob = "mise-tasks/config-deprecations.sh"
check = "mise run config-deprecations"
severity = "deny"
scope = "tree"
no_fix_reason = "a removal is announced by declaring the window, not by editing the schema: add a row to `config::DEPRECATED_KEYS` naming the replacement and the expiry, and the derived artifact follows from the types"

# The first migrated gate of the bash-retirement campaign (CLOUD-843 track 2):
# `run-shape-guard`'s no-message-source family, as a consumer-authored module
# rather than a vendored preset. It names `git`, which a preset may not — a
Expand Down Expand Up @@ -2240,6 +2272,24 @@ use_sources = ["crates/batten/src/*.rs"]
module = "policy/module-layering.rego"
severity = "deny"

# CLOUD-760's consumer, and the reason the fact is a fact rather than a facility.
#
# `symbols = true` is what pays for it: this is the first `Cost::Effect` fact, so
# resolving it RUNS the analyser over the crate — seconds, where every other fact
# costs milliseconds. Declared per row means a `check` that does not enable this
# rule spends nothing, which is the whole reason the column exists.
#
# NO `sources`. The census is the fact's, resolved once at the boundary over the
# crate the analyser compiles; a glob here would select files the module does not
# read and skip the row when the glob matched nothing.
[[rule]]
id = "spawn-adapters"
kind = "policy"
scope = "tree"
symbols = true
module = "policy/spawn-adapters.rego"
severity = "deny"

[[rule]]
id = "opa-tracks-regorus-compliance"
kind = "policy"
Expand Down
Loading
Loading