Changes touching this path

  • loot-first in Rust: land policy behind a Forge seam (#218) — new loot-first bin crate (ledger/forge/policy/orchestrator), in-process Workspace reads via loot-cli lib split, typed pr-map owner + shared ferry::WipState; every policy decide-tested against a fake forge. Build+test only; shadow-run, real land, and ps1 deletion remain operator-gated. 58c28888 · dbf3dbe6…
  • feat(loot-first): tag-push ferry verb — release tags reach GitHub-projected main (#256) Add `loot-first tag <version> [-m msg]`: mint an annotated release tag on the projected, sealed-free git-mirror `main` and push it to GitHub through the Forge seam so cargo-dist's release.yml fires — no break-glass raw git. - loot-cli ferry::tag_projected_main creates the tag object in the harbor mirror at refs/heads/main (sealed paths are already omitted by projection, so a release tag never widens the public boundary); refuses on an unbound mirror or an existing tag. - loot-first orchestrator::tag holds the harbor lock across project → main FF push → tag push, matching land's serialization; execute_tag_push is split out and tested against FakeForge (main first, then the tag; a diverged main aborts before the tag is pushed). Closes #256. ba29acec · dbf3dbe6…diff
  • reject unknown flags on every verb instead of silently ignoring them (#67) e4583cb7 · dbf3dbe6…diff
  • merges refuse to sign an un-described change too, closing the #174 residual (#275) #174 stopped the *deliberate* finalize from signing a nameless change, but three verbs still sealed the operator's own work in passing, to make a signed merge parent: `dock merge` and the `adopt` catch-up (via `fold_line_in`), and `ferry` over a git main that moved (via `reconcile_capture`). Only the *trigger* was mechanical — the content was authored — so the placeholder still reached main. Reproduced against the pre-fix binary: a ferry over unnamed disk work landed the literal subject `(working change)` on git main in one silent pass. Both paths now refuse an un-described merge parent, below their capture (edits held, only the signature waits) and below a redundant-capture drop (a pass with no real work to sign stays a no-op, never a nag). Two reasons the ADR gave for NOT doing this were checked and dropped: a land never reaches these paths dirty (it finalizes before it ferries), and #219's refuse-on-dirt rejection was about being forced to capture, not about being asked for a name. The merge *nodes* are untouched — they are machine-authored and already carry an honest mechanical subject. That is the line: mechanical content may be named mechanically, authored content may not. - `refuse_if_undescribed` (one rule, two messages) + `drop_capture_if_redundant` (the empty/duplicate drop, previously copied at two sites and missing at a third — which is what would have made `dock merge` nag). - Cost, documented: uncaptured dirt takes two passes, since naming *is* capturing. `loot-first review` asks for a name only when it must merge. ADR 0030 amendment (correcting the two wrong claims), CONTEXT, workflow, and concurrent.md updated. Closes #275. aced27c1 · dbf3dbe6…diff
  • Workspace accepts its clock; lane flag-gate goes pure so cmd_* tests never touch a real .loot (#322) 20ac82e1 · dbf3dbe6…diff
  • a land from the primary refuses when the git checkout is parked on another branch, so a projection can no longer write across someone else's work (#618) Level A of #618, first half. `reconcile_onto` materializes the reconciled tree over the running position's disk; from the primary that disk is the shared checkout, the one position that also carries a .git. Parked on another session's branch it takes landed content across their working copy — observed live, the primary ended up holding main's content against research/tracker-prior-art, and only luck (their work was committed and pushed) made it harmless. `projection_target` (policy.rs) decides from two facts: the position running the land, and the branch its checkout is on. A lane's projection writes its own tree and leaves the primary for its own `loot adopt` (#265), so the guard is silent there rather than crying wolf on the normal way to work. Off-branch from the primary refuses, naming the branch; --allow-off-main is the break-glass. An unreadable branch (detached HEAD, no .git, a git that failed) proceeds but says the guard did not run, so a broken git cannot mute it silently. The refuse/proceed decision is carried out on Gate::Proceed's new `note` rather than re-derived by the caller, so what gets reported cannot drift from what was decided. Level A's second half — refuse a primary tree dirty with content that is not this land's — is deliberately absent: from the primary the implicit snapshot (ADR 0030) has already captured that dirt into the landing change, and dirt arriving after the review round is already refused by review_currency (ADR 0033). The residual, recorded in policy.rs: a land with no reviewed_version takes Currency::Current unconditionally, so that path is still unguarded. Levels B and C remain open. c1148309 · dbf3dbe6…diff
  • the repo publishes README and both licenses, and land learns to sign a publication (#750) 7ddc9629 · dbf3dbe6…diff
  • the argv scan asks the spec everywhere: tag, clone and burn stop reading a flag value as their positional (#869) 95b41e53 · dbf3dbe6…diff
  • a review with no PR is named for what it keeps: --no-pr replaces the dry-run that was never dry (#861) ad590be9 · dbf3dbe6…diff
  • a land runs from a lane: the position gate replaces #618's checkout guard, and level A is deleted rather than kept beside it (#932, ADR 0050) b629f752 · dbf3dbe6…diff
  • a tag stops firing a workflow that can only fail: the GitHub release pipeline retires, and every live pointer to it says where publishing went (#1025) f9a18c18 · dbf3dbe6…diff
  • a verb run from a subdirectory finds the repo instead of advising an init that would nest a second one, path arguments rebase from the cwd the way a tester types them while view's globs stay root-relative, and the resolved root is named on stderr whenever cwd is not it eca43819 · dbf3dbe6…diff
  • the land reads its position once and hands that one corpus to three gates that still run in ADR 0065's order, undoing the two-and-a-half-fold read the previous change caused by leaving a walk inside each gate while claiming there was only one ec75201e · dbf3dbe6…diff
  • deleting a named read on Workspace stops being free, because the 377 places that reached past it into the engine and the store now go through those faces instead — repo() no longer exists, store() is module-private, and the three extracted children share their fixtures by name rather than through the parent's private test module (#1411) c454db04 · dbf3dbe6…diff
  • an extra positional stops being dropped where a subcommand reads its arguments, because every leaf in the eight families now declares its arity beside its flags and check_sub refuses the token nobody expected by name, which caught a lane new t502 in the suite that had never carried its handle (#1419) 90472de4 · dbf3dbe6…diff
  • a verb that names a file in this repo stops carrying its own copy of the #1330 rebase, because every argument slot's kind is now a required field of the table that already declared the leaf's flags and arity, so the argv door rebases from the declaration and a leaf that states nothing does not compile (#1466) 889e5821 · dbf3dbe6…diff
  • a flags value stops being read as a flag, because `FlagSpec::given` asks in flag position the way `positionals` already does and every verb-local scan in both binaries is deleted behind a census that refuses the shape — which is what had `loot tag -m "-d"` signing a retirement, `loot describe -m "--no-snapshot"` silently skipping the capture, and `loot-first land --allow-publish "--skip-tests"` skipping the gate that is its whole job (#1612) b40e1843 · dbf3dbe6…diff
  • a repo open stops materializing half a million manifest entries no verb asked for: ChangeNode.tree becomes a Manifest holding either the decoded map or a proved-decodable byte range into one shared Arc, so topology decodes eagerly because reachability needs it while a change's path manifest decodes only when something reads it - which is where the cost actually was, since framing the 37.9 MB graph without building the maps takes 6.8 ms against 507 ms to build them, and that 507 ms was the bulk of the 1050 ms every verb paid, including loot whoami whose whole job is to print 93 bytes out of id.pub. Deref carries the ~270 existing node.tree read sites unchanged and Deferred is module-private, so a deferred manifest cannot exist over bytes Manifest::walk has not already refused with the same framing the eager pass uses, which is what earns an infallible &TreeManifest return rather than a Result; and an unmaterialized manifest is pinned never to read as an empty one, the rule read_graph_required already states for an absent graph file. Verbs also declare what they read now: RepoNeed rides beside CustodyNeed through the already-pub(crate) open_at_clocked_needing, so no new public door is added and the sealing door custody_lock pins by string and call-site count is untouched, with whoami, conflicts and manifest narrowing to POSITION_ONLY, defaulting to EVERYTHING everywhere else, and a withheld graph or store aborting loudly rather than reading as empty. whoami 555 to 42 ms against a 40 ms control, conflicts 557 to 41, manifest 557 to 42, log -n 5 626 to 144, status 704 to 227, lanes 4069 to 717, peak RSS for whoami 148 to 7.1 MB, and twelve verbs byte-identical against main including 414 KB of loot log. Three of this ticket's premises were wrong and are corrected rather than worked around: the keyring it named as the second cost measures 1.1 ms and is left alone, read_graph_outline did not fit because it discards the parents reachability needs, and the object-store half it called the boolean that costs a gigabyte had already landed in #1545 - while the stat cache stays unbuilt, which is the whole of what status and lanes still spend (#1536) b923abb1 · dbf3dbe6…diff
  • the argv door stops being per-binary and becomes one module in loot-net that every binary asks, and flag_value is deleted rather than widened - zero definitions remain workspace-wide, which turns the class is closed from a claim into a deletion. the recorded reasoning this ticket pointed at refused widening a shared reader, which is a different act: it argued a reader must not be taught a new spelling because that changes parsing everywhere at once, and its own third bullet diagnoses the hole as this binary having no gate, so it points at giving the binary the gate and leaving one reader. per-server spec tables were rejected as a second door, the thing this class exists to prevent, and a new crate was rejected because every binary already has loot-net in its graph and the module is pure std, so the odd thing about the home is its name and not its coupling. the destructive half is demonstrated against real binaries and seeded stores rather than argued: a before binary built by reverting only the readers reaps a real blob under reap-grants --older-than 30 --dir store --addr --apply, because --addr is never consulted in reap mode so nothing else in the line looks wrong, and it forges the window under --addr --older-than 1 --older-than 3650 --apply, reading one day where the operator wrote 3650 - after, the first refuses with nothing has been destroyed and the second keeps the grant. the forge enters migrate mode, the superuser DDL path, under --addr --migrate and now treats it as an address. this ticket's own headline argv is corrected rather than repeated: --dir --apply is destructive only under the s3 backend, since on fs both binaries stop at is_relay, and the fs-reachable variant is the one it did not name. loot-first's private reader is in scope and gone too, because land declares two valued flags and --allow-publish --pr 12 --pr 9 landed the PR nobody typed. an early forge pin stayed green through the revert, since the bug was never in the traversal but in what main asked, so the call site was extracted and re-proved red; and two further destructive readers are reported and not fixed, one of which takes a size operand as its scratch directory and recursively deletes under it (#1628) 089f8dda · dbf3dbe6…diff
  • the shorthand whose NAME states the path axis while its SHAPE states the arity axis is DELETED, so the terse spelling is now the one that REFUSES - which was this ticket whole thesis, that the wrong declaration was cheaper to write than the right one and that is why the silent-drop class kept recurring. every site that meant it now types the open constructor out, and the only shorthand left is the one that takes nothing. option A beat option B on BOTH axes, measured rather than preferred: B would have changed the constructor signature, so EVERY open call site owed a reason string - including the path-taking and the genuinely variadic ones - and the two dozen dispatch verbs would each have written the SAME sentence, which is boilerplate that teaches nothing and is itself a hand-maintained population. so A has the smaller blast radius AND the stronger property. the blast radius is ZERO BEHAVIOURAL, because the retired constant was literally that expression: no verb declared arity, no slot kind and no refusal moved - thirty-five declaration sites, eleven imports and about twenty-five prose sites, with the workspace check clean and no new warnings. the exemption list was ALREADY down to its two legitimate names before this began, since #1569 narrowed the other twenty-one hours earlier, so nothing was added to it or taken from it, and the #545 refusal that earns those two their place is untouched by construction - pinned rather than incidental, because the mutation that hands one of them the no-arguments declaration reddens all three censuses. the rename then exposed two more counts standing beside sets that MOVE, and both are fixed rather than carried: a fixture doc claiming all FOUR verbs it exists to serve are exactly this shape, where there are FIVE production attachers and NONE of them is that shape, and a line naming the four verbs that used the retired constant. both now state the rule and count nothing. the new guard refuses BINDING the zero-slot open claim to a name, which is the single edit that would undo this, while deliberately NOT refusing a leaf that spells the claim out at its own spec - the two told apart by what PRECEDES the constructor, with both run through the predicate before its answer is read. its limits are in its own header. and the control that mattered is the second: with the comment-strip removed AND the predicate control disabled, the tree scan names the flags file itself, over the retired declaration QUOTED INSIDE THE SURVIVING CONSTANT OWN DOC - so the strip is load-bearing rather than decorative. the first and third mutations are each other discrimination, one reddening only the tree arm and the other only the binding-versus-spelling arm, and the fourth proves the floor fires at zero files rather than agreeing silently (#1675) 123fdbd4 · dbf3dbe6…diff
  • the verb position stops answering a typo with a 157-line wall and names the nearest verb instead - the same repair #1520 made at the other spelling, at the more common position, since the verb slot is the one every invocation has. the suggester is REUSED verbatim rather than reimplemented, and the only-edit-distance-in-the-repo claim was VERIFIED before being relied on rather than taken from the ticket: every workspace rust file was swept for the vocabulary and for a distance dependency in any manifest, and the one pair stands alone. it already derives its population from the completion verb names rather than a hand list, so nothing about the suggester changed. the message, though, did NOT transfer verbatim - it hard-coded the help framing - so the lead clause becomes a two-variant enum and everything after it stays shared: one printer, not two. the no-match arm is TRANSFERRED rather than re-decided, and at the dispatch position that is a real narrowing, because a word near nothing now gets no invented guess AND NO WALL, where the wall used to be the whole answer. the exit code was measured on the shipped path both ways and is UNCHANGED at one, with stdout still empty and the json envelope keeping its contract number - and it is pinned as the VALUE rather than as not-success, so a change to it cannot pass. the ticket own premise about the sibling binary is FALSE and is corrected rather than carried: its five-word suggestion list had not gone stale at all, matching its dispatch arms and its flag set exactly, so no reader was ever misdirected. the CLASS was real, since nothing held the list to the table, so the list now derives - with the pin reading the MATCH ARMS rather than the flag set, because reading the flags to check the flags would be an identity, plus a source assertion that the arm spells no verb name at all. eleven mutations with counts read. M3 is what earns the behavioural file: reverting the dispatch arm to the wall leaves BOTH unit suites green and only the spawned-binary test reddens, which is exactly the argv-door blindness two tickets in this run had to be re-closed for. and M6 is a CONTROL that stays green on purpose - a second implementation inside a test module - which makes the census scope a stated limit rather than a hole nobody mentioned, alongside the other limit that it does not parse rust and so cannot see a matrix that is neither named nor spelled like one (#1695) edfa4d62 · dbf3dbe6…diff
  • the commit that derived a list precisely so it could not go stale had typed its cardinality into the doc above it, and both counts are gone: the doc now says the refusal names the whole flag set - every verb this binary dispatches and nothing else, pinned in both directions - and that what the one-line shape rests on is READABILITY rather than a size. the obvious fix, pinning the count as well, is refused on purpose, because it would fail a land for the offence of adding a sixth verb, and that refusal is PROVED by a mutation adding one to both sides that stays green. the distance-crate check stops presenting a denylist as a property: it is widened with the five crates the review named, loses its own typed length so the constant carries no count either, and says plainly that it is a named list of common spellings and not a test for the category - naming BOTH holes in the voice its other stated limit already uses, a crate under a name nobody wrote down and a renamed dependency, the second confirmed by a mutation that stays green on purpose. matching on any dependency that merely looks like a distance crate was rejected as a guess dressed as a check. the derived method width is decided to be a TRIPWIRE and not a ledger, so its gap prose is dropped rather than extended - it was wrong both ways at once, excusing a gap that another clause already attributed while a new one had opened unnamed - and the doc now says outright that nobody keeps the per-ticket clauses summing to the figure. and the flag argument left at the vouching seam is examined rather than refactored for its own sake: the triple riding three doors IS the resolved delta still being assembled, so a new struct would only be a second name for it, and a bare bool would be the same flag one level down. the one real narrowing is taken instead - a caller that always passed Some now takes the set directly, so no door a caller can reach lets it choose to skip recording the open (#1922) 0ac07831 · dbf3dbe6…diff
  • review sweep 6 fix-up: ADR 0088 section 4 gains the divergences the sweep found, each checked against the code - the evolog E row printing a raw whole message (#1973), the status and buoy R rows and the tag name column holding free text ahead of the last column, tag and heads naming people by display name with no key in heads json, a sealed diff row printing a bare tier label, telemetry status and off printing a spool dash where V3 says null, and telemetry on beside show as report json with no contract - and says it records what was found rather than proving nothing else diverges. V7 now means no version line or preamble, and the visibility token and Delta renderer member lists point at the code that defines them. the template-flag census gains a twin in loot-first over its FLAGS table, both reading one spelling list now in loot_core flags; the twin went red with --template and with --pretty planted in loot-first tag, and the loot census with -T planted in lane gc (0 passed, 1 failed each). ADR 0023 credits the open flag set to ADR 0047, the OPEN_BUT_TAKES_NONE census is called an ordered comparison, its assertion points at the list instead of naming describe and new, a ceiling raise has one place for its reason, a stale family count and a stale entry position are dropped, rewrap leftovers in ADR 0088 and ADR 0035 are reflowed, CONTEXT.md follows, and owned_lane takes a Removal with named verb and refusing fields instead of two swappable strings. the workspace suite is green (#1974) ecd53283 · dbf3dbe6…diff
  • ADR 0076 amended by #2101: .loot/gates gains a land phase and two hand-offs, and git hook names are mapped or refused by name. Phase::ALL has three members and every sentence that spells the set derives from it (Phase::words, Phase::usage_alternatives), so the parser refusal, the loot gates usage and the --phase errors moved without a second edit. A land gate is the user-gates row of orchestrator::GATES, between the text gates and the expensive ones, PreLandAndMerged and Outside: it runs on the lane tree and again on the merged tree when the converge moved the position, --skip-tests never silences it, and loot-first land gains --skip-gates which honors honored and never required per declared posture and records the skip in the op log after each pass, because a gate holds no Workspace. A finalize or land gate may write {message}, which expands to the path of a temp file holding the message about to be signed, newline-terminated and unlinked the moment the child returns, and {paths}, the touched paths root-relative with forward slashes, sorted, deduplicated, one argument each, deletions included; a push gate that names either is malformed, since a push has no single change, by Phase::commits_one_change rather than a list. The hand-off is built by Workspace::gate_handoff only when a gate in the phase names a placeholder, so a declaration that never asks never pays the working-delta walk, and gates::run still takes no Workspace; that accessor is the 384th Workspace method and the verb tier module doc now says so. A {message} with no message is unstartable, never a pass. The spelling was decided by running the tools, not reading them: pre-commit 4.6.2 files stage hands a hook the paths as separate arguments and its commit-msg stage hands exactly one filename, and a bare script reads that file, so a file path and argv tokens are the compatible spellings; pre-commit itself needs a .git and so cannot run inside a lane, recorded in the amendment. git pre-commit and pre-push are refused naming finalize and push; any other word is refused by name with the one sentence that holds for every unmapped hook, no index, no checkout, no rebase, no merge commit, and GIT_HOOK_PHASES is the map, never a claim about git set; the optional init --from-git was not built. Measured through the real binary in a throwaway repo, since .loot/gates is shared over the store and a land line there would make every other session binary refuse its finalize phase as malformed, which the amendment and workflow.md now warn about: two Python gates over 13 paths and the message cost 77 ms and 64 ms wall for the whole phase against 23 ms for a listing, so a contended land pays about 140 ms; loot new through the binary handed the message file and 13 paths, and --skip-gates recorded skipped p in the op log. Five pins each broken once by a named mutation and confirmed red at 0 passed 1 failed, then green at 1 passed: the row demoted to PreLand, the message file cut to its first line, the sort dropped, pre-commit unmapped, and required skipped under the flag. CONTEXT.md Phase entry and workflow.md step 7 say the same. loot-cli gates 25 passed, change and sync 65 passed, loot-first lib 225 passed after the two table assertions learned the ninth row; two lands refused on censuses the targeted runs never selected, the module-doc method count and the help placeholder classification in revset_everywhere, and each fix is one line saying the new spelling. No wire, format or store byte moves, no migration (#2101) 5c4e3996 · dbf3dbe6…diff
  • the whole-word reading gets one home and a use this walk cannot follow is refused rather than skipped: #2126 landed census_text so that a .rs-text reading more than one census needs and none of them owns has one home, and the very doc saying the source_walk whole-word reader was shared rather than copied per census had a copy of it sitting in the temp-root census next door, drifted already, one asking char::is_alphanumeric and the other an ASCII byte test, so the sentence was false the day it was written. the reading moves into census_text as whole_word_matches, the offsets a word stands at as a whole identifier, with names_whole_word derived from it rather than written beside it, so the caller that wants the answer and the caller that wants the places cannot come to disagree about where a word begins, and the boundary is the Rust one and not the ASCII one, since a boundary that reads too narrowly lets a longer identifier answer as a whole word, which is a census reporting an offence that is not one. the copy the ticket found was not the only one: loot-first, loot-forge and loot-relayd each held the same closure inside the bare-flag census of its own crate, and each reads the shared file now through the same cross-package path attribute the other callers use, so one edit reddens every consumer of it. the docs stop naming callers and say instead what decides where a reading lives, which is the census_text admission rule, and the seam a reader arrives from now carries why the import reading stays in source_walk: it is keyed to that module own name and to what helpers_named can find afterwards, so it is not a flat question. that import reading also stops enumerating what it refuses, since the enumeration was already stale: a use item naming the module is the module under its own name, or names taken out of it, and everything else falls through to refuse_import, which is how use crate::source_walk as sw, outside both branches and skipped in silence, the under-count #1946 was filed on surviving the ticket that closed it, becomes a refusal without being named. the narrowing census in main.rs stops re-deciding which narrowing is in force and asks the door, since taking the first declared narrowing whose flag a shape requires and taking the narrowest part on a shape requiring two narrowing flags of different counts, latent while no verb writes that shape and now unreachable because the rule has one home, pinned where it lives. the forwarder that discards a door result is declined with the reason at sole_statement: a door too many is a site too many and the offenders are asserted empty, so it arrives red naming the call, while reading the discard would shrink the door set, which is the direction that loses a site in silence. one more of the same class was found beside the rest: code_mask said the two censuses that share this in the present tense, and it now speaks in the past about the two walks it replaced. red under mutation, counts read each time: the shared boundary widened to admit every character reddened all five consumers from the one edit (loot-cli lib 5 passed and 2 failed, loot-cli temp_root_census 1 passed and 2 failed, loot-first 0 passed and 1 failed, loot-forge 0 passed and 1 failed, loot-relayd 0 passed and 1 failed), the alias refusal put back to the silent skip (0 passed and 1 failed), the plain module import refused as well, which is the other direction (0 passed and 1 failed), and the door narrowing rule flipped from narrowest to widest (loot-core 0 passed and 1 failed, with the CLI census still green, which is the point of the move). no migration, no wire or format byte moves, and nothing outside test support and a doc comment moves, so this owes no deploy. the workspace suite is green (4049 passed over 129 binaries, 8 ignored) (#2148) cb7f057f · dbf3dbe6…diff

Renames are not followed. loot's tree maps a path to an address, so a rename is a delete and an add. This list is the history of the name, not of the bytes.