Changes touching this path

  • reject unknown flags on every verb instead of silently ignoring them (#67) e4583cb7 · dbf3dbe6…
  • 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
  • Gate flags per subcommand: a sibling subcommand's flag refuses instead of riding ignored (#278) The #67 gate declared flag specs per verb, so `loot lane`'s spec was the union over new/list/name/rm/gc and a flag real on a sibling subcommand (`lane new --stale-hours`, `dock rm --at x`) passed the gate silently. Each branching verb (`lane`, `dock` -- the others declare no flags) now re-checks the resolved subcommand's own spec via FlagSpec::check_sub before the workspace opens; a test pins the table's union to the subcommand specs so the two gates cannot drift. Ride-along: bare `loot lane` no longer panics slicing an empty argv. 1ada4da4 · dbf3dbe6…diff
  • Workspace accepts its clock; lane flag-gate goes pure so cmd_* tests never touch a real .loot (#322) 20ac82e1 · dbf3dbe6…diff
  • describe/new: refuse a stray positional instead of silently truncating the subject (#545); last hardcoded relay port (#569) e1ba880d · dbf3dbe6…diff
  • loot <verb> --help prints that verb's usage block plus its accepted flags, not all ~80 verbs (#585) b87eae53 · dbf3dbe6…diff
  • a flag stops arriving where a subcommand reads its positional, because loot id rotate --permanent resolved the flag as the directory a rotation writes its re-grant wave into, and all eight subcommand families now gate and read positionals through the one table completions are derived from (#1407) e880cd19 · 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
  • three claims stop outrunning the code they describe, because ADR 0007's escrow census is now the output of a grep the ADR states rather than a list someone re-derived and left two files short, `Args::any_path`'s short circuit now runs above the ancestor walk it always claimed to skip and is pinned as a walk that does not happen, and CONTEXT's settle count now names the unit it counts — which is what made a number that was right read as wrong (#1482) bd385596 · dbf3dbe6…diff
  • a shared CLI option stops being declared once per verb, because the argument table now carries option groups a leaf attaches in one line — so the pathspec four separate tickets were each going to add is stated once, and a leaf that attaches it inherits the PATH slot kinds, the cwd rebase and the empty-match refusal without restating any of them, while every flag traversal reads the derived list so help and the refusal cannot drift from what the gate accepts (#1537) 24404922 · dbf3dbe6…diff
  • loot diff and loot cat stop giving two answers about one embargoed path, because the four-rung absence ladder cat.rs already had moved into the delta seam and both renderers now match on it instead of each re-deriving burned-vs-sealed — and beside it an option group stops claiming the positionals of a leaf that never declared an arity, which is what would have turned `loot diff HEAD~1 HEAD` into a pathspec of its own selectors (#1551) 2248a1f4 · dbf3dbe6…diff
  • a verb that takes no positionals stops being able to say so by claiming the opposite, because `NO_ARGS` now sits beside `NO_PATHS` and a census reads every verb invocation shape out of USAGE to find the ones still spelling nothing as open-ended — which is how `loot heads junk` was accepted 88 minutes after the same hole was closed at `loot cat`, and beside it `HEAD~n` ends where the revset lexer already ended it so a range can be typed at all (#1562) fb140e48 · 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
  • the valued half of the argv door gets a floor: FlagSpec grows value() and message() so a valued flag's payload is stepped over by the spec that declared it rather than found by a bare scan of raw argv, and all 29 readers move onto it along with six more the ticket's list had missed, which is what left loot ferry --dock --git-dir yielding a --git-dir nobody typed while its --json twin was the single case anyone had pinned; and the census meant to stop this class recurring widens from the previous bug's exact spelling to the class itself and gains a positive control, which caught two further hand-rolled scans in help_sub and resolve_tool_path and took the crate from eight offences to zero (#1652) f86ffb25 · dbf3dbe6…diff
  • the two censuses that each carried the other's blind spot stop being two walks and become one, and the defect turns out to be four walks rather than two: flags.rs cut production at the first line starting with cfg(test) and never restored it, so 55.5 percent of loot-cli src sat exempt from its second clause at 36164 of 65168 lines - and the 60.0 percent this ticket claimed was measured with a looser predicate than the walk actually used, which is the ticket committing the very class it was filed about - while ADR 0074's guard scanned from any marker forward to the next brace and lost 635 production lines to semicolon-terminated items and to markers sitting inside doc comments; a third walk in main.rs split on the marker string, and a fourth in custody_lock broke at the first marker line and was right only by luck, because all three write_session callers happen to sit above their own files' first marker. the shared walker classifies every char as code, comment or literal before it looks for a marker at all, and ends an item at the first semicolon at depth zero while counting parens and brackets alike, because endpoint.rs's const ALL takes an array of thirteen routes whose semicolon lives inside the type and ended the item early in the first draft, keeping fifteen lines of test table as production. the floor is a discrimination rather than a count, since a count passes on both original bugs: it asserts that a known production line below a marker is kept and a known test line is cut, and it was broken in both directions to prove it can fail. every blindness was demonstrated by planting a live violation in production source and watching both censuses stay green before the change and exit 101 after it; and Clock::time now refuses a second call on one repetition rather than silently reporting only the last region measured (#1665) 12e18beb · dbf3dbe6…diff
  • the pathspec option group gets its first production consumer, and being inert turns out to have been invisible by construction: all five .groups attachments sat inside cfg(test), so Pathspec::keep returned at its is_active guard on every production call and no test could see it, because a filter that never runs changes no output - which is why delta.rs claimed in the present tense that diff, status and show were all served by that line while none of them attached the group. diff and status now attach it, diff narrowing at #1541's seam so rows, --stat and --content inherit one filter rather than each applying its own, and a -- separator arrives with it because a pathspec needs a way to say the rest are paths. Two states the ticket did not name are where the real bugs were. status builds its delta lazily, gated on the human format, so --porcelain and --json never reached the seam at all and attaching the group alone would have left both machine formats printing an unfiltered listing at exit 0 - the format an agent reads, which is the caller this class is dangerous to; and with no working change status returns before reading the tree, so loot status zzz would still have exited 0 in the primary checkout while passing every test on a seeded temp repo, since a seeded repo is never empty. Both are now answered from the seam, the second gated on is_active so pathless callers pay nothing. status also stops silently dropping its argument, which removes it from OPEN_BUT_TAKES_NONE and leaves 23 - by the other route, taking paths properly rather than declaring NO_ARGS, and the list's prose implies the latter is the fix for the names still on it, which would foreclose the better answer for surface, converge and ferry. The false sentence is narrowed rather than deleted and carries why it was written a ticket early, in delta.rs, flags.rs, the census doc and CONTEXT.md. Eight breakages proved the tests non-vacuous, planned before they were written, and dropping both attachments - the exact pre-ticket inert state - turns six of six process tests red plus the census pin (#1493) 35ed624c · 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 door's own docs stop naming the crate it left, and the count this ticket asked to derive is deleted instead - because deriving it the way the ticket suggested would have confirmed the stale number rather than contradicting it. the ticket said loot-cli's census is spelled one way and the others another, so a grep would undercount; loot-first shares loot-cli's spelling, so grepping the others name returns exactly three, which is the wrong number already written on the line. a derived four is right today and the property is right permanently, so the sentence now says every other crate that ships a binary keeps its census in its own crate and the roll call derives the set rather than listing it. the roll call sentence is rewritten as a mechanism rather than a new pointer, since a glob pub use cannot carry a cfg(test) mod: no amount of following crate::flags or loot_net::flags reaches the roll call, and a reader who tries finds nothing with no way to tell whether it moved or never existed. a third stale path the ticket did not name is repointed too, a rustdoc link into the shim, because correcting two of three is the trap this ticket itself cites - and two more staleness sites in the door's own file go with them, a doc counting four binary crates and a fifth, and an expect message still naming the crate the door left. the new pin derives the door's crate from the single is_flag definition and holds two shapes to it, that a re-export names its target in its own header and that every backticked fully-qualified path into the module names the door's crate, with backticked the discriminator between guidance and a call through a re-export both moves deliberately left working. four reverts were each proved red, and a fifth mutation exposed the pin's own vacuity - dropping the shim detector's comment guard left it green because the doc had not spelled the shape it reads, so the shape is spelled and the control now fails. this ticket's premise that both moves left stale docs is also wrong: #1628's header was correct when written and the staleness is entirely #1682's (#1685) fa895fab · dbf3dbe6…diff
  • a verb arm now receives the argv door answer instead of the raw slice: VerbRun takes an Admitted value, the rebased tail bound to the spec of the row it was dispatched from, and its readers are the only way in, so argv[0] or argv.first() in an arm fails to compile (both planted, read as E0608 and E0599). spec_of is test-only and its 104 production lookups read through the admitted spec; six of the seven raw-index arms (bundle, grant-status, embargo-status, attest, resolve, completions) read positionals through the door and relay reads its leading word through a named exception; subcommand families resolve their leaf into a new Admitted, and bisect and buoy are handed one ahead of the table. the two named exceptions, leading_word for relay and bisect and pass_through_tail for the bisect run command, plus the one process argv read, are held to a named set of functions by a new census that went red on a planted leading_word read in cmd_bundle, and the source walker consumer header now counts it. refusals and output are unchanged by every suite and by a read of each migrated arm, with one exception found on the way: resolve with a trailing --tool and no command read --tool as the file and now refuses with the usage line, pinned by a new test. CONTEXT.md gains the Admitted argv term; loot-first keeps its own spec_of and DECLARED_READS is untouched (#1929) d92e95f2 · dbf3dbe6…diff
  • the Admitted and VerbRun docs and CONTEXT.md stop saying every argv reader goes through the spec: the readers that parse positionals and flags do, the consents and relay keys now say on their own docs that they scan argv by flag name, and the named exceptions lose their count and point at the census, as bisect dispatch now does. the stale arity-census counts in main.rs and loot-core flags.rs are gone, and the owed-arity rule is one function that the top-tier census and the counting pin both call, and each still went red when burn was re-opened. the flag census and the Admitted census now share one src walk keyed by relative path, and the Admitted census went red on planted leading_word reads in cmd_bundle and verbs/mod.rs, naming the second by its path. the surface.rs privacy argument now matches Repo::surface reaching the walk unpromoted, CONTEXT.md and the resolve comment stop calling its refusal the usage line, the Verb and pick.rs doc comments are separated so rustdoc keeps them apart, and two rewrap leftovers are fixed (#1939) 7b04e214 · dbf3dbe6…diff
  • the zero-arity pin now asks owes_an_arity which verbs owe an arity instead of spelling its own narrower rule, and expects the same verbs as before; it went red when owes_an_arity alone exempted heads and when the heads row was re-opened. the OPEN_BUT_TAKES_NONE doc and CONTEXT.md stop listing or counting its readers and point at owes_an_arity. the Admitted doc and CONTEXT.md name the methods that do not read through the spec, as a reading of the impl rather than a rule, and with_leading_word now says it rewrites argv by position. the flags.rs censuses take the src half of the source_walk walk instead of a copy, and the Admitted census still went red on a planted leading_word read in verbs/change.rs; the consumer count is unchanged, and the stale main.rs consumer count in the consumer walk doc is gone. the loot-perf depth docs stop promising an equality nothing checks, change.rs points at the PolicyParses variant doc, and the rewrap leftovers the ticket lists are fixed, with a long line in the CONTEXT.md arity paragraph (#1944) c5819020 · dbf3dbe6…diff
  • migration 0017 and ADR 0078 now state the deploy window setup-forge.js leaves by migrating before it swaps the container: until the new binary is up the old one reads empty repos and has pushes refused, nothing is corrupted, and the order is kept because migrating first is what the deploy script relies on for every release (#573); the checksum pin stays green and went red on a one-token statement edit, and 0017 says statements rather than bytes. CONTEXT.md and ADR 0078 stop saying no CI runs the live suites, the rls.rs header names the key the workflow sets and says the cluster tests are the ones that return early, and the 2026-09-08 evidence entry gets a dated follow-up rather than a rewrite. binding_census now walks every file under src/pg instead of a hand-kept pair, reads fn items of any visibility, follows SQL held in a module-level const and drops cfg(test) items and files, and Pg::with_conn, with_txn and with_repo are private to pg so the compiler keeps other modules from issuing statements through them. a planted pub(crate) method in a new pg/planted.rs reading such a const went red under the new census (1 passed, 1 failed) and stayed green under the old one, and removing each widened shape turned the census red. the source_walk message on flags.rs drops its stale six-lines count, and a new assertion holds that flags.rs calls into the module on more lines than it has consumers, which went red when that premise was broken. the table counts in CONTEXT.md and pg/mod.rs point at the constants that hold those sets rather than restating a number, pg/mod.rs points at the path include instead of listing its includers, the Admitted doc says within and within_leaf read through the leaf spec they are handed, the test-main.yml provisioning comment names the grant repair it drops as a no-op on a fresh cluster, and the rewrap leftovers are fixed. the bare workspace suite is green, and the loot-forge suite is green against a throwaway cluster with the CI env block (#1953) f2cbbb4c · dbf3dbe6…diff
  • cherry-pick, revert and a surface that settles a stale-disk debt no longer destroy a case-only rename where the filesystem folds case, and the first no longer records the loss: #2011 routed two tree writers through loot_core::disk_entry and wrote that every writer which removes a path it may just have written asks that module, while two that did not were sitting in the tree as the sentence was written. Workspace::apply_change_delta, the one core cherry-pick and revert share, walked the merged actions as a BTreeMap and so wrote A.txt and then removed a.txt by name, which where case folds is the file it had just written, and it snapshots, so loot revert over a recorded case-only rename reported both paths converged over an empty directory and minted a change recording 1 deletion. Workspace::stale_debt_the_surface_left, the one scoped deletion a surface is allowed, compared the written set by name, and a case-only rename preserves the bytes, so the digest guard that exists to protect the operator matched the file the surface had just written: loot surface printed the path it had surfaced, left the directory empty, and the next loot status read it as deleted, which is the reproduction #2011 declined on the false reading that a surface prunes nothing. Each was reproduced by hand through the spawned binary first and then as a new spawned pin, and the pair went red over an empty listing before the change (4 passed, 2 failed) and is green after (6 passed); with the delta writer put back to removing by name they go red again (5 passed, 1 failed) and so does the new census (0 passed, 1 failed), and the stale-debt prune put back reddens the same two with the same counts. Both writers prune through prune_unwritten now. Which working-tree removals are weighed stops being a sentence: workspace.rs carries a census that reads every removal joined onto a working-tree root out of loot-cli/src and loot-core/src, names what each one removes and what the walk cannot see, and it named the offending function under each of those two mutations, went red at its own classifier control with ROOTS emptied and went red with a name dropped from its expected set (0 passed, 1 failed each). one_entry refuses a path that is or passes through a symbolic link now, since file_id follows links and on Windows two paths with one id were otherwise one entry, so fs::rename would have moved a link over the file it points at; the check is the symlink_on apply-patch already made, moved into the module, pinned with a junction here and red without it (5 passed, 1 failed). The hard-link pin names its links A.txt and a.txt so the fold map reaches the entry rule at all, where old.txt and new.txt never called it, and it skips a filesystem that folds case with a note; it stays unix-only and its red-first has not run here, though the body type-checks and runs on windows with the cfg lifted (7 passed). The four case probes become one loot_core::disk_entry::folds_case, and with it answering no the entry pin and two apply-patch case pins go red (5 passed, 1 failed; 18 passed, 2 failed). enclosing_fn moves into source_walk beside the #1929 census that asked it first and strips any visibility, after pub(super) made the new census name the wrong function. ADR 0022 carries the correction and its prunes-nothing claim is gone, ADR 0082 section 4 states where the probe and the link refusal live, CONTEXT.md names both writers and the census, the surface.rs and materialize_target notes stop claiming the two prunes answer the same set of failures, and the rewrap leftovers in ADR 0022, CONTEXT.md and the account page are fixed. The perf gate dry run reads every counter the same before and after and says no move both times, the site gate is green, and the workspace suite is green (3824 passed) (#2018) baec4112 · dbf3dbe6…diff
  • the source-walk consumer census sees a helper brought in by a use, and which methods the Admitted exceptions census watches is read off the marker on their own docs: the consumer walk keyed a reach on a call site spelled with the module path, so the spawn-seam census in main.rs, which calls the cut through an import, was counted by nobody and stood on no discrimination floor at all, and the src file walk #1944 added to flags.rs was invisible the same way, which is why #1944 reported the consumer count unchanged. a use item is resolved per file now, with a rename, a glob and an item that does not close on its line refused rather than read wrongly, and the floor is owed by the consumers that read the cut rather than by every consumer, derived from this module own source as the helpers that reach MARKER, the one spelling the cut keys on, so the file walks and the header readers owe it nothing while every census that cuts owes it as before. the derived header sentence is 15 consumers in 3 compilation units: 9 library, 5 binary, 1 integration test, and the paragraph that enumerated the censuses by name now says what a consumer is and leaves the files as an index the guard checks one way, saying which way. with_leading_word, which rewrites argv by position with no spec asked, was documented as doing so and was watched by nothing, so it carries the marker now and arrives in the roster with its one caller, bisect mark_by_term; whole-word matching moves into source_walk beside the walk, one spelling where flags.rs held the copy. red under mutation, counts read each time with the filter selecting the one test: the imported half of the reach dropped (0 passed and 1 failed), a planted use-imported bare call of the cut in main.rs (0 passed and 1 failed), the floor call removed from the spawn census (0 passed and 1 failed), the cut-reader derivation reduced to the direct namers of MARKER (0 passed and 1 failed), a renamed import planted (0 passed and 1 failed), the marker taken off with_leading_word (0 passed and 1 failed), and a planted with_leading_word caller in bisect dispatch (0 passed and 1 failed). item 3 of the ticket was already closed by #1974 and nothing there moved. no migration, no wire or format byte moves and nothing outside a test and a doc comment moves, so this owes no deploy. the workspace suite is green (4013 passed over 125 binaries, 7 ignored) (#1946) 47b0d164 · 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.