Changes touching this path

  • loot-perf: the harness crate with fixture, measurement, and record seams (#634) ef9fd595 · dbf3dbe6…
  • the pre-land perf gate: counters block at 0 percent, timing warns, and a lane pins itself (#637) 52a2341e · dbf3dbe6…diff
  • the gate learns to see the wire: negotiation counters on a fixture with real history depth, and wire_bytes stops pretending (#847) 8fd4e950 · dbf3dbe6…diff
  • the gate measures the constructor, not a copy of its constants: loot-perf-gate starts from Fixtures::gated() (#915) `Fixtures::gated()` is documented as what the pre-land gate measures and had no production caller — the gate binary rebuilt an equivalent by hand out of COUNTERS_SIZE/TIMING_SIZE/NEGOTIATION_SIZE/NEGOTIATION_DEPTH. Two independent statements, and `fixture.rs`'s workload_id test — the one guarding the field #607 selects baselines on — asserted against the one the shipped gate never constructed. They agreed only because #847 edited both by hand. The binary now starts at `Fixtures::gated()` and applies `--counters` / `--timing` as modifications of it (`with_counters` / `with_timing`), so a change to the constructor reaches the gate by construction rather than by whoever edits next knowing this file exists. The negotiation half deliberately gets no setter: its depth IS the measurement (#847), and that asymmetry is the design. 5a08bb26 · dbf3dbe6…diff
  • every open of a compressed object stops building a fresh zstd context and asking for a 64 MiB buffer, because the decompressor is now thread-local and the buffer is the frame's own declared size clamped by the same zip-bomb ceiling, and the harness gains the first fixture whose objects are actually compressed - without which nothing could have seen the 72.6% it removed (#1424) 7c16d4d4 · dbf3dbe6…diff
  • a push's follow-up batches stop re-walking every change and every manifest to locate their 32 addresses, because the membership and key answers that walk produced are now taken once per transfer, which makes bundle_wanted_batched's own O(total_objects x num_batches) disclaimer true for the first time, measured at -44.1% by a new bundle_batched timing that is the only number in the harness reaching a follow-up bundle 5d7fdccf · dbf3dbe6…diff
  • loot diff stops decrypting every path on both of its sides to fill in a bool, because the visibility gate now asks the cipher-free can_open #1422 built for exactly this beside the reader-scoped expiry check it must keep asking, measured at -58.7% by the first fixture in either tier that touches diff at all (#1426) 699a58e8 · dbf3dbe6…diff
  • the harness stops being blind to history depth, because closure_complete is now timed in process over a 1024-change chain whose control refuses the run unless the walk reads the manifest 1024 parent edges back, and #1421 - parked flat inside a ~470 ms process - re-measures on it at -57.9% against a 5.3% floor (#1428) a59dd358 · dbf3dbe6…diff
  • a perf half added tomorrow cannot go undocumented, because one table now drives the gate's parse, its usage line, its module doc and both operator docs — and the figures that had drifted from whatever owns them, including a timing resolution quoted ten times better than the shipped tolerance, point at the owner instead of restating it (#1436) 4568d998 · dbf3dbe6…diff
  • one whole-tree decrypt stops being spent to answer a question the first differing path already settled, and working_preview gets the in-process sample that can see it, because the cli tier could only ever ask a 476 ms process to resolve half a percent of itself (#1423) f5ce12eb · dbf3dbe6…diff
  • loot-perf's runner stops open-coding the five opt-in halves three times over, because a half's measurement is now a field of the OPT_IN_HALVES row its batch name already came from and Fixtures::id is written from that same table, so deleting a row reddens a lib test that stayed green through exactly that deletion before (#1465) a5992206 · dbf3dbe6…diff
  • a verb stops reading every loose object in the store before it can open one, because the open now indexes the object directory and reads the file at the address it is asked for, so one complete index still answers membership, the persist's candidate set, gc's sweep and ADR 0038's burn while a burned address keeps no route back through the directory it came from, and the sixth opt-in half that can see any of this refuses a fixture whose object directory does not hold the objects it claims (#1545) 5c98eff6 · dbf3dbe6…diff
  • a push's first bundle stops asking the object store about every change it has already decided not to send, because the entry's ride decision is taken before the probe rather than after it, so an entry whose bytes and key both stay home is skipped instead of being looked up and discarded — the shape #1425 removed from the follow-up bundles only — and that arm stops being unmeasurable, because --batched passes have = &[] and can therefore never enter it, so a new --push-shape half passes the change one behind the tip and refuses outright any fixture whose have does not resolve to the history behind it (#1546) a8d1806a · dbf3dbe6…diff
  • a push stops walking its whole object store to answer one yes-or-no, because the unsigned-tip refusal now asks whether anything is offered at all and stops at the first thing it finds, instead of building the entire unfiltered offer and then discarding it — on every push whose relay declared heads — to walk again scoped to them; and that walk stops being unmeasurable, because no cli sample can reach it at all: the tier gives every push repetition a fresh relay, so have is always empty there and the discarding branch is never entered, which a new --offer-guard half over a 128-change history reads at 34.3 ms against 17.6 us (#1559) dca66add · dbf3dbe6…diff
  • a pull, a wants answer and the relay's cache restore stop faulting the whole loose object store in from disk, because the receiver's membership question is now asked of the index that put's dedup, the persist's candidate set, gc's sweep and ADR 0038's burn already answer rather than through the read #1545 made lazy, so a held address whose file vanished under a live process is no longer re-fetched, which was a five-commit-old accident that would have asked a relay to resurrect what another process had just burned, while an address absent from the object directory at open is still named and a withheld one still asks forever (#1565) 113b4588 · dbf3dbe6…diff
  • a document, an ADR and five code comments stop asserting things the code does not do, because the interval a perf doc invented for its own blind fixture is deleted rather than corrected, the store census that moved three times in 36 hours is deleted at all four sites that shipped it, and every place a rotted object's failed read becomes a silent negative is now a recorded decision instead of an accident of is_ok() - including the cross-store save that dropped a held object and returned Ok(()), which now propagates (#1566) 5c97b104 · dbf3dbe6…diff
  • the implicit-capture door stops faulting the whole object store in from disk before every bare mutating verb, because the closure walk it gates on asks the index the membership question it always meant, and that door reaches a skip only through false so a held-but-unreadable address can no longer switch it into the narrower policy #891 wrote for an incomplete closure, while the half that watches the walk stops timing a store with no files in it (#1576) 732d63fa · dbf3dbe6…diff
  • a per-change cost inside the repo open stops being unmeasurable, because the open now has a depth axis beside #1545's width axis: a --graph-load half over a chain of N changes, whose control refuses a depth-1 fixture outright instead of letting it report the absence of a workload as the absence of an effect, which is what parked #1547 and #1548 while both were correct (#1571) 10730151 · dbf3dbe6…diff
  • adding one opt-in perf half stops costing thirteen hand-edits and starts costing two, because Fixtures keys its opt-in halves by the OPT_IN_HALVES row that already described them instead of ten Option<Workload> fields with three constructors re-listing every None, and loot-perf-gate builds its whole valued-flag set and both its usage strings out of that same table, so the hand-typed [&str; 17] arity is gone and a throwaway row the strict parse refused by name before this change is accepted with no second edit after it (#1599) Subsumes #1474, whose done-bar is the whole claim and was run in both directions before anything else. A throwaway half written the old way, with all eleven structural sites edited, was still refused by the shipped binary as an unknown argument --throwaway at exit 2, which is the #1465 experience live. After this change the same half is one contiguous row in OPT_IN_HALVES, and the binary accepts the flag, prints it in the derived usage line as [--throwaway <files>], and carries on past the parse to the pin stage. What moved: - Fixtures now holds one [Option<Workload>; OPT_IN_HALF_COUNT] array in OPT_IN_HALVES order rather than ten Option<Workload> fields. gated / split / uniform stop re-listing every None; with_X, the X() accessor and the free take_X function become Fixtures::with, Fixtures::half and OptInHalf::take, all keyed by OptInHalf::slot. - OptInHalf::of and OptInHalf::take, both fn pointers, become one Axis field. Axis is #847s rule written as data: the flag carries the half own axis and the other dimension is pinned in the row, so the ten one-line take_X functions that each re-implemented that rule are gone and a reader can see which dimension is which without leaving the table. - loot-perf-gate VALUED_FLAGS array, with the hand-typed [&str; 17] that walked 13 to 17 across five halves in one night, becomes valued_flags(): the union of RESIZES_A_HALF, TOUCHES_NO_FIXTURE and the table. The union is the point, because #1403 distinction is that --counters and --timing resize a half the gate already takes while an opt-in flag adds one. Both USAGE copies are derived too: the const becomes usage(), formatted from each row flag and axis word, and the module-doc synopsis enumerates no half at all. - OPT_IN_HALF_COUNT is OPT_IN_HALVES.len(), so no count of the halves is typed by hand anywhere. The invariant did not move, verified by running the gate rather than by reading it. An unflagged dry run of a release binary built from this tree prints workload counters=small_files/200+timing=small_files/2000+negotiation=small_files/24x96, carries no opt-in metric, and reports perf: no move against its baseline. A flagged run takes --compressible, --open and --graph-load together and produces compressed_read, repo_open and graph_load in their own batches under the id tail +compressed=compressible_files/64+open=small_files/200+graph_load=small_files/200x16, which is one row per Axis variant and both axis directions. New pins, each broken before being trusted: - every_half_takes_the_fixture_it_always_did writes out all ten halves workload ids at one flag value, because this is a refactor and a fixture id is the key #607 selects baselines on. Swapping the two dimensions of Axis::Changes reddens it with the value, small_files/64x200 against small_files/200x64. - taking_one_half_takes_exactly_that_half and no_two_halves_share_a_flag_or_an_id_key cover the slot keying over every ordered pair; a slot() that always answered 0 reddens both with values. - a_flag_moves_its_own_axis_and_pins_the_other makes the claim ten per-half tests each made by hand, once over the table and in both directions. - the_module_doc_synopsis_enumerates_no_half inverts the assertion #1436 wrote: that one held a hand-kept copy equal to the table, this one holds the copy to not existing. - every_valued_flag_is_classified_exactly_once replaces the old classification test, which is now true by construction, with what derivation cannot give: that the three classes are disjoint and their union is the whole set. Adding --reps to RESIZES_A_HALF reddens it. No assertion that OPT_IN_HALF_COUNT equals the table length is written, and a comment says why: it is that length, so no edit could redden it, and a check that cannot fail is worse than no check (#1578). 8e0284b8 · dbf3dbe6…diff
  • head count and grant count stop being fixed at trivial values in every fixture, because the harness now carries a fan whose heads the union folds one complete manifest apiece and a tree whose custody manifest the per-path gate has to descend, each built only after its own ceiling was measured first at 2,000 exact heap allocations per additional head and a 30x step from an empty manifest to one grant per object, while the three axes whose ceilings came back flat are recorded on the issue instead of built and the head fixture is a correctness instrument too, because a chain admits exactly one topological order and every fixture here was a chain (#1601) b98b1efc · dbf3dbe6…diff
  • the aspect a half refuses under stops being a const in the measure module and becomes a field of the row itself, built by a macro so a row names its fixture and never the sentence, because that was edit site 9 and the census that called it fictional had missed three module-level consts and five of the eight function-local ones while #1601 went on to pay it twice more; the five defaults each Workload constructor re-listed collapse into one plain base; and the two-edit promise is restated with the scope a throwaway half on a new axis measured, seventeen structural sites down to twelve, the last silent one now failing a test because a dimension added without an arm in Workload::id makes two fixtures report one workload_id (#1633) c2a454dc · dbf3dbe6…diff
  • a timing without a control stops compiling: timed_min takes its controls as a const-sized array and a const assert refuses an empty one, every prove_ returns a Proven token whose constructor is private so the argument cannot be forged, and snapshot_delta - the one timing the gate judges and the only one of the thirteen that called no control at all - gets a first control, proved non-vacuous by breaking tree_edited to return the tree unedited and watching it go red rather than produce a happy number; the clock is handed to the body rather than wrapping the repetition because locally_missing and closure_complete re-open their store outside the timed region while for repo_open and graph_load that same call is the measurement, so a combinator that timed the whole repetition would have silently destroyed both shapes, and the holes and complete checks move inside the body so they cover every repetition instead of whichever reading happened to be last; the refusal is post-monomorphization, so cargo check passes on a tree cargo build rejects, which is written into the doc beside a compile_fail pin and its positive arm because a compile_fail example passes on any compile error; and three of this ticket's own citations were wrong and are corrected rather than worked around, since TIMING lives in gate.rs and not in the bin, the prove_ call sites number 24 and not 68, and the five blind instruments in one night that ADR 0072 was said to record are not in it at all - a mis-citation two other places in the tree already make, left to a follow-up rather than widened into this change (#1654) f03656a4 · dbf3dbe6…diff
  • six sentences that read wider than their subject are narrowed rather than deleted, because in at least two of them the sentence is the argument: the verb tier's width figure stops being typed at all and is generated off the impl blocks with the predicate written beside it, since this ticket's own 244 is not reproducible under any predicate - pub fn alone gives 202, every fn including test-gated gives 320, production-only gives 309 - which is the ticket committing a mild form of the class it was filed about, as was its claim that the figure had gone stale a second time when git log -S finds the sentence written once and never corrected; finalized_anchor is repointed to the two hops it really takes rather than dropped from the list of delegates; the duplicated surface tail is extracted and not deleted because BOTH copies are live, one a trait method the bench calls and one the escrow-promoted twin, so establish-which-is-live had the answer both; thiserror leaves two manifests that never derive it, each row replaced by why the absence is deliberate now that RepoError is loot-codec's type re-exported; the push-shape row is documented and pinned rather than renamed, because a metric string is output and this change is behaviour-neutral, and the new pin found the drift is two rows and not one; and the whole cost of opening a repo is narrowed in all three live copies rather than the single one named here, since correcting one of three is the trap #1629 records, while ADR 0012's copy stays because its subject is the loose-object slice and not the harness. one finding was simply wrong and is reported as wrong with no edit, since run_checks has a production caller and fourteen tests and the file this ticket cited does not exist. and load_from turns out not to be what the open calls at all: assemble asks load_from_needing with the verb's declared RepoNeed since #1536, so the timed call is the widest spelling rather than the live one, which is now said in both places that time it (#1656) 44b5b98a · dbf3dbe6…diff
  • bundle_impl stops visiting every manifest entry of every held-back change to answer a wants question, and this ticket's proposed fix was the wrong one: reaching the existing wanted_finalized_entries index would have moved the cost rather than removed it, because that function is the SAME O(changes times entries) walk - #1425's saving was amortising one walk across many batches, not making the walk cheaper - and it moves nothing at all on the relay /fetch path, which builds one bundle and never batches. It also cannot serve batch zero on its own terms: it has no have, so no send set and no attestations, and it is wants-scoped while bundle_impl's key lane fires for non-want entries of sending changes. The missing piece was never an index, it was a STOP RULE, so both walks got one. bundle_impl becomes two passes over one topological order, the send set unchanged and a second wants lane over held-back changes that is skipped outright when wants is absent or empty and stops as soon as every want is settled, and wanted_finalized_entries gets the same rule walked newest-first. The consequence worth naming is that held-back manifests are no longer materialized at all, which is the cost d6be741 removed from the repo open and this path was re-paying at push time; it is pinned over a saved-and-reopened repo through Manifest::is_materialized with a control that was proved to fire, since an in-memory repo defers nothing and would have made the pin vacuous. Byte-identity is differenced against one_pass_bundle, a statement-for-statement transcription of the base walk, over ten have-and-wants shapes, because the whole claim is that this is a cost change and not a behaviour change. This ticket's 512.6 ms does not reproduce: at 436 paths and 128 changes the release cost is 7.43 ms, which extrapolates in depth to about 58 ms at the ticket's 1000, roughly nine times smaller, and three confounds compound - the debug-build suspicion #1704 raised, #1701's probe memoisation landing in between, and this half keeping its objects in memory. The shape was real and the growth was real, so the fix is worth 86.0 percent at 64 by 128 and 92.5 percent at 436 by 128 on the named half, but the number that ranked this finding was wrong and saying so is worth more than repeating it. A defect this change introduced was caught by its own review and is recorded rather than quietly fixed: ride_entry returned true on a store refusal without consulting bytes_ride, so a non-want entered settled, and a sent change naming one burned or withheld address made the settled count reach the wants count with a real want still unanswered - silently skipping the #803 recovery lane and breaking byte-identity. The fixture had no unproducible address, so the headline pin was blind to it; the fixture now carries a burned one in the send set and the defect was proved red before the fix. Four loot-perf doc sites went stale by this change and are amended here rather than left: they described the held-back walk as the cost this half watches ACCUMULATE, where it is now the cost it holds GONE - the half is not blind, since re-nesting the loops returns the reading, but a future comparison is against 126 microseconds and not the number the row was born with (#1702) 68a0bded · dbf3dbe6…diff
  • ancestor_closure stops re-walking the whole ancestry once per seed, and unlike its two siblings from the same hunt this ticket's numbers REPRODUCE in release - 94.97 ms against a claimed 101.1 and 383.9x against a claimed 424x, both within ten percent - so the debug-build correction #1704 and #1702 each needed does not apply here and the ranking that placed this finding stands. What does repeat is the other family trait: this ticket also said the fix already exists one file away, and that was wrong again, though for a narrower reason than #1702's. GraphView::ancestor_closure is NOT set-identical, proved by running rather than assumed - it seeds its out set with whatever it is handed, so an unrecognized seed comes back inside its own closure where the per-seed loop skipped it through graph.get(h).is_some(). A probe printed DagRepo 0 against GraphView 1 for a stranger alone and 6 against 7 mixed, differing by exactly the stranger, and this is not a corner because sync.rs hands it a PEER's declared heads. A dangling parent reached mid-walk is kept by both, which is why the fix is the shared walk plus a SEED filter and not either one alone. The instrument was blind for a reason worth naming precisely: --push-shape passed vec![parent], which is the shape of a /haves REPLY, where sync.rs passes that id's whole closure - so the half was already not describing the code, making this #1576's extend-in-place case rather than #1425's honest-but-narrow one, and #1702 had re-based the same readings one commit earlier so there was no unbroken series to protect. Widening it moved the instrument 4.9x against the UNCHANGED engine, 273 microseconds to 1.33 ms at 200 by 128, which is the non-vacuity proof taken before any fix was trusted. After, the quadratic term is gone: per doubling of depth the cost grew 2.8x, 4.0x and 4.3x before and grows 1.2x, 1.4x and 1.6x after, and at depth 512 the half reads 22.713 ms against 0.608. The evidence is therefore a THREE-point chain and not a pair - the landing position's 273 microseconds, the widened fixture on the old engine at 1.33 ms, and the widened fixture on the new engine at 283 - and only the last two are the A/B, because the first is a different fixture; reporting the outer pair alone reads as a regression, so that rule is written into HUNT-PERF.md rather than left for the next reader to rediscover. The control on the widened have is the part most worth recording: the first version was a count-nonzero floor, and a two-element have of parent and root clears a floor of two, passes coverage, and still seeds a 127-deep walk twice - about ninety-nine percent of the blindness back with every control green. It is now set equality against the have's own closure, the fixed point sync.rs actually passes, with a negative arm proving that closure is a fixed point and a red proof that reverting to the floor fails on the two-seed case (#1700) 04859438 · dbf3dbe6…diff
  • the second review sweep's fix-up, and the item that mattered most was a correction to a correction: #1704's amendment to ADR 0072 refuted a sentence the bullet does not contain. The bullet credits an EXACT-VALUE pin, and the amendment answered that a positive-value pin is not the protection it credits - true about a greater-than-zero guard, untrue about the thing being amended, which is doc scope-drift inside the ADR whose subject is that class. Blinding the instrument settles it by running rather than by reading: with the tally back on Attributes::load, status_derives_its_policy_a_fixed_number_of_times fails on its VALUE half, and the ticket's own arithmetic was wrong in the same direction as the amendment - the blind arm reads 2 and not 1, because status still reaches Attributes::load once, while 1 is the ferry fixture's reading. Both numbers are now named in the text, the greater-than-zero finding is kept because it is real and newly demonstrated, and the head count stays at two of the four with the reason narrowed: what is still missing is a refusing FIXTURE, not a refusing pin, since two pins were each measured refusing a mis-placed tally. ferry.rs claimed the pin asserts the 2 plus term, which was wrong twice over - nothing asserted a constant at all, and the 2 being described is the per-commit coefficient rather than the pass-level term - so rather than correct the sentence the constant is now ASSERTED, as parses equals PER_PASS plus PER_COMMIT times ingested over both sweeps, fitted to measurement rather than predicted. Its red proof is the regression the old comment claimed was already pinned: a third unconditional parse per commit moves the readings to 14/14/14 and 8/14/26, where the constancy half passes AND the growth half passes and only the new assertion fires. offers_any_object's note that no address is ever reached twice is repointed because it is false in exactly the case the memo two screens up is built for - a store where every named address answers Err never returns early and re-probes each repeated address per change - and that arm is left uncovered with its cost stated as unmeasured rather than justified. The 291-against-283 disagreement turns out not to be one: four fresh runs read 296.3, 289.6, 293.2 and 283.6 microseconds, so both recorded figures sit inside the instrument's own run-to-run spread of about 4.5 percent, which is wider than the 8 microseconds they differed by. Recording a POINT was the defect, so it is now a dated spread of about 290 with its four raw readings written in one place, and measure.rs carries no number at all but points at PUSH_SHAPE - killing the duplicate rather than syncing it. PUSH_SHAPE_DEPTH's pre-#1700 pair gains the marker the three sibling sites already carried (#1717) d895d727 · dbf3dbe6…diff
  • BOTH READINGS WERE RIGHT and the contradiction was a DENOMINATOR: #1536 made a repo open stop materializing half a million manifest entries, which is the very call graph_load times, so the same absolute saving of one to two milliseconds at depth 1024 was 0.9% of a 189 ms open before that landed and 11% of a 10.4 ms open after it. measured across that one commit rather than argued - 155.89 ms against 10.36 ms at the SAME workload id, 15x from a change that touched neither the scan nor the fixture - and corroborated by the scaling signature, since before it the depth ratios are 2.01 and 2.06, LINEAR, with the manifest term burying everything, and after they are 2.25 and 2.69, superlinear, with the quadratic term finally EXPOSED. the fixture never moved. the general rule is now written where the next run will read it: A PERCENTAGE IS A RATIO, so a landing that shrinks the DENOMINATOR flips a verdict without touching the change, and same flags is necessary and not sufficient - record the absolute beside the ratio and the growth beside both. re-measured at the depths the ticket comments ASKED FOR and never got, 8192, 16384 and 32768, interleaved, thirty runs, all exit 0: minus 55.7%, minus 81.5% and minus 91.3%, disjoint at every depth, and the claim is about SHAPE rather than percentage - per doubling the old engine grows 2.70, 5.06 and 4.32 while the new one grows 2.12 and 2.02, which is the sum-over-k term leaving and a linear load remaining. nothing is claimed at 1024 and the old claim is WITHDRAWN, the point replaced by a dated spread carrying all thirty raw readings. #1547 figures are marked the same way, because they are pre-#1536 too and equally un-reproducible - their percentages stand, since both sides were taken on one engine, but their absolutes are marked NOT A BASELINE rather than silently re-used, and the ratio is deliberately NOT re-derived because that is a measurement nobody has taken. and the refcount preference is answered honestly rather than defended: the land DID reverse a recorded preference without saying so, the two shapes answer DISJOINT halves, and on the callers that keep the scan - the ingest paths, the two-sequence rewrite and a third site nobody had listed - the refcount REMAINS THE BETTER ANSWER (#1860) e3c5456c · dbf3dbe6…diff
  • the adopt and ferry re-read stops deep-copying a pool it is about to drop, and the instrument that can SEE that was built first, because NONE existed: measure.rs had no reference to ingest anywhere, and both depth-capable halves time DagRepo::load_from, which is the OPEN - ingest_shared_lineage re-reads the shared graph AFTER it, so no fixture built on load_from executes a line of it at any depth. the new opt-in half sizes #778 own reproduction: a dock owns the store and records one change of its own, then a lane over the SAME shared store lands a depth-change line into the graph file while the dock heads file stays put, so an open of the dock is filtered back to its one change and the landed line is exactly what the ingest must splice. the instrument is proved by a POSITIVE CONTROL rather than by an argument that it ought to work - the clone line made knowingly expensive moves the number 168.3 to 188.5 to 203.7 ms at one, two and five copies, while the graph-load half on the SAME knowingly-wrong binary reads 10.30 ms UNMOVED. the ceiling was measured before the percentage was trusted: a second discarded read_graph takes depth 1024 from 171.2 to 322.9 ms, so the EAGER graph decode is about 89% of the timed region, because this seam uses read_graph while the open uses the deferred read_graph_required of #1536 - a real and much larger finding, deliberately NOT made here, written down at the call site with its correctness question, which is that the deferred manifest holds an Arc into the buffer this function drops. the change itself is the #1547 arrangement at the other site of the same shape: collect the walk order as IDS, then MOVE the nodes out with remove instead of cloning them. the re-read semantics are untouched on purpose - same node set, same emitted order, same insert rather than from_parents_first, because this is a splice into a LIVE graph, and refresh_objects_loose and the keyring catch-up still run AFTER the splice, which is #778 ordering. it MOVED: interleaved, ten reps a side, every run exit 0 and carrying a VALUE rather than reading skipped, with the idle probe at 2 to 8 percent - min of ten per round goes 185.1 to 145.6 ms at depth 1024 and 375.1 to 291.9 at 2048, sets DISJOINT at both, and the graph-load half flat as the negative control. the magnitude is qualified rather than quoted flat: an earlier pair of the same two engine sides read minus 10.0 and minus 10.9 percent, and re-running all four binaries interleaved reproduces each level to within one percent, so the shift is CODEGEN LAYOUT rather than machine state - direction certain, sets never overlapping, minus 10 percent the conservative figure, recorded at the code. and one doc claim is REFUTED rather than restated: the function said the parents-before-children order is why head tracking stays exact, and the mutation that reversed the order came back GREEN, because the splice goes through insert, which is right under any arrival order - so the sentence now says what the order actually buys, and no pin was added to assert a courtesy (#1572) ff123eb9 · dbf3dbe6…diff
  • a published speedup that exceeded a ceiling measured three paragraphs below it is WITHDRAWN rather than explained, and the instrument that measured the ceiling is REFUTED BY MEASUREMENT rather than by argument: a discarded second read_graph measures the MARGINAL cost of an extra decode while the pool is still alive, not the first call share - it reads 151.5 ms against a whole region of 148.4 on one after binary and 162.5 against 157.7 on a second, over 100 percent of the quantity it is a fraction of, twice, on separately built binaries. the replacement stops the function instead of doubling the read, and its bands SUM to the region: read_graph plus pool build 154.3 ms at 89.8 percent, walk and splice 16.2, objects and keyring 1.4, against a region of 171.9 - so the ~89 percent decode finding SURVIVES the loss of the instrument that first produced it, and on that pair the change removes 13.9 of the 16.2 ms it could touch, a delta that FITS. the magnitude itself is re-measured across 156 gate runs in one sitting on an idle machine, every run exit 0, discarded 0 batches, carrying a VALUE rather than reading skipped, with the harness own probe at 1 to 9 percent and no exit 3 or 4 anywhere: three independently built before-and-after pairs read minus 13.6, minus 8.1 and minus 10.6 percent at depth 1024, disjoint at both depths in every pair, with graph-load flat across the same invocations - so the answer is published as minus 8 percent conservative and about minus 10 middle, and the spread is the BINARY rather than the machine. the residual is stated and NOT reconciled: the clean pair 22.5 ms exceeds the 16.2 ms band it should sit inside, and the clean pair own band cannot be measured without making it un-clean, with corroborating evidence that the instrument perturbs what it measures - the stop-instrumented after binary reads a read-and-pool band ABOVE its own whole region, an early return costing more than the work it skips, six rounds out of six. what would settle it is named as an exact allocation counter over the region that no half records today, and it is left as instrument work rather than done here. the copy in the fixture now quotes NO figure at all, keeping only the shape, because a figure quoted anywhere else is a copy. the in-loop guard stops checking the OTHER guard than the one its prose names and is asked every rep of the position under measurement - proved by a mutation pair where a no-op ingest is caught RED at 156 passed 2 failed and, with the same guard disabled, GREEN at 158 passed 0 failed, which demonstrates the blindness rather than arguing it. the ADR masthead stops enumerating its own amendments and points at the inline marks that already carry their tickets, the fixture stops claiming the ancestry is the real path when its root is parentless and the walk stop predicate never fires, the Err arm stops asserting a merge as the cause it never established and hands over the selector quoted refusal instead, and the shallow header stops calling a convention mechanical when nothing enforces it (#1888) dc524dd2 · dbf3dbe6…diff
  • the harness gains the dimension that dominated it, and the axis is PROVED against the binary that carried the cost rather than added after the fix and trusted: a clone at the parent commit, running the same fixture through the same timed call, reads 171.0, 388.8 and 870.6 ms at 218, 436 and 872 paths a change where the lane reads 10.5, 18.0 and 35.4 - so the axis MOVES, 16.3 then 21.4 then 24.6 times, and the pre-fix side grows 2.27 then 2.24, linear in the axis, which is the changes-times-paths term. at 436 by 1024 it is 0.87 microseconds an entry against the ~1.02 implied by the original 507 ms over 494,810 entries, so the fixture STANDS IN for the real open rather than gesturing at it. the GRAPH_LOAD question is MEASURED rather than assumed, and the ticket own correction is confirmed while its stronger framing is refuted: at its pinned width the parent binary reads 155.6 ms against the lane 9.7, a sixteen-fold step, so that half WOULD have moved - its weakness is MAGNITUDE and FRAMING, 200 paths against this repo ~436 average and an id_key that presents it as graph load, not structure. only REPO_OPEN is structurally blind, because depth one pins the dimension at its identity value and one change cannot observe a quantity summed across the graph at any width. the control has three arms and none is implied by another - floors, then INTERCEPT on what the opened graph actually binds, then RESPONSE, where the same generator at HALF the declared breadth must carry exactly that many fewer entries - and the response arm is the one a count cannot supply, since a fixture pinned at one width passes the intercept whenever the flag happens to name that width. that independence is MEASURED and not argued: a discrimination pair reddens one arm each, and a diagnostic re-order shows the first mutation still trips the intercept when the response arm is asked first. the after column is deliberately NOT called flat, because it is not: 1.73 then 1.95, since a wider manifest is more bytes to FRAME even when nothing decodes them, and the row doc says so. the negative control the comment supplied is taken and then made DETERMINISTIC rather than left as a timing - a topology-only walk holds node count, parent edges and head count IDENTICAL across a doubling that moves the open by 97 percent. no counter is folded in, because #1662 is the counter half of this territory and the two are alternatives to choose between. and the blind-instrument roster is deliberately NOT given a row: that table is scoped to one run and says so, so the new kind joins the paragraph already carrying the later members, leaving the count sentence and its pin untouched (#1670) 5334741c · dbf3dbe6…diff
  • three sweeps running, the commit that DELETES an asserted-complete list writes a new one in the sentence that replaces it - so the recurrence itself is recorded as a named shape rather than fixed a fourth time and forgotten: the replacement is where the next list gets written, because the author is mid-sentence explaining what the true set IS. and this change caught itself committing the class TWICE while writing, which is the evidence rather than an embarrassment - an amendment draft saying the page said twelve in FOUR places where it was five, and a draft of the replacement paragraph writing six of the NINETEEN artifacts. both numbers are gone. the six-item metadata list becomes the PROPERTY: the set is the StoreArtifact trait, RepoStore::read routes every implementor through one door, and what keeps the conclusion true as the set GROWS is each impl path resolving to a named file under the store root - verified across all nineteen, none into the object directory. the conclusion that carries the 0% gate is kept verbatim, because it was never the wrong part. a THIRD copy of that enumeration, which the ticket did not name, was found and folded in. the twelves take a RULE rather than a sweep: drop the numeral where the sentence is a present-tense claim about the current table, KEEP it where the sentence narrates what ADR 0073 decided or refused as an EVENT. fifteen sites fixed, six deliberately LEFT - including the ADR title, which is the name the decision was accepted under, and a dated amendment recording what one mechanical edit touched, since correcting THAT is precisely the defect this ticket warns about. one of the twelves was already off by one and now states the property instead. the durable guard refuses a cardinal standing before opt-in halves across derived directories, and its LIMITS are in its own header rather than discovered later. the mutation that matters most is M2: writing todays CORRECT number, fourteen, is still RED, which is what stops the next reader fixing it to a value that expires. and the control that mattered is M3: the first draft tokenized raw text, so a count split by an 80-column comment wrap read clean - it PASSED OVER ITS OWN DOC COMMENT, which was quoting the offending phrase - and with the comment-marker strip removed and the defect still in place the pin goes GREEN, which is what proves the strip load-bearing rather than decorative. the fourth count of the roster population is deleted and all three of its escape routes closed, and the ADR stops citing a miscounting line as its authority for the run-versus-night correction, making the landing times the authority and citing that line only for the NAME. the negative-control heading is narrowed to what was actually measured, on the fixture rather than on the metric, and the two unreconciled readings of one measurement are reconciled once - where the ticket own framing was itself slightly off, since the two are the same commit pair measured in two sessions (#1907) b781aefd · dbf3dbe6…diff
  • the policy counter is renamed PolicyParses and policy_parses because it has counted parses since #1704, and the policy.rs block and the ADR 0073 row now point at the variant doc instead of restating why; no stored perf record carried the old key, because the gate does not record this counter. PUSH_SHAPE_DEPTH, OFFER_GUARD_DEPTH and MISSING_DEPTH are literals rather than aliases because their reasons have diverged, and each is still 128. ride_entry takes one RideState instead of three maps, while the one_pass_bundle oracle keeps its own transcribed walk and memo so the byte-identity test still compares two walks, and still went red when the key arm was disabled. the gated counters read 743, 200 and 24 before and after (#1718) bbd04ef4 · 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
  • the graph rewrite a finalize pays reads the graph file back deferred and copies each manifest stored frames where Manifest::frames proves them to be what encoding its map writes (no backslash key, keys strictly ascending in Path order, UTF-8 holder names), and falls back to the eager read otherwise, so save_to no longer decodes every path of every change in history to add one change. the new --graph-rewrite opt-in half times that whole save at 200 paths; paired, three interleaved rounds, the gate reading load idle at 1 to 4 percent: 48.18 to 48.48 ms down to 12.65 to 12.72 ms at depth 256, 183.43 to 183.49 down to 39.14 to 39.47 ms at 1024, and 750.3 to 759.4 down to 179.5 to 182.6 ms at 4096. counted by the new graph_rewrite_allocations test, the save at depth 1024 allocates 6,039 and 6,061 times on two runs against 695,198. the file written is byte-identical to the decoding rewrite, pinned over every golden and a lone y//ab key (copied), the legacy separator and the #2273 shapes (not copied, rebuilt from the eager read), and a key or holder name that is not UTF-8 (not copied, and the save still refuses it). red under mutation, counts read each time: the eager read put back (1 passed and 1 failed), the holder check dropped, the ascent check dropped and the eager fallback dropped (1 and 1 each), the counts check dropped (0 and 2), restored to 2 passed each. riding along: HUNT-PERF names the new half, and the visibility census names the moved and new bindings. no migration and no format byte moves, so this owes no deploy. the workspace suite is green (4278 passed over 137 binaries, 11 ignored) (#2280) e7d666e0 · dbf3dbe6…diff
  • LineagePool::read, the one read of the shared graph file an adopt or ferry pass catches up from, reads it through read_graph_deferred, the door #2280 added, and leaves every manifest undecoded, so an ingest no longer decodes every path of every change in history to splice nodes whose manifests it never reads; before moving any node the splice asks the new Manifest::decodes, which steps the frames for a key or holder name that is not UTF-8, and refuses the ingest naming the change when a node it would splice holds one, which the eager read refused, so the #2275 panic gains no new route. paired --ingest readings, one loot-perf-gate --features count built per arm, interleaved, the gate reading load idle at 1 to 6 percent (peak 9): 162.7 to 164.6 ms down to 12.04 to 12.39 ms at depth 1024 and 331.4 to 331.5 down to 26.9 to 27.6 ms at 2048, the check costing 0.2 to 0.7 ms at 1024; counted at depth 128 and width 200 the region allocates 1,057 times against 61,604, the read band 279 against 60,826 and the splice band 778 on both arms. the memory trade the splice comment left open is measured and taken: over a copy of this repo graph file (85.7 MB) the deferred read peaks at 171.3 MB against 308.5 and holds 87.9 against 222.2 while the pool lives, and once the pool drops a splice of ten nodes keeps the 85.7 MB buffer where the eager read kept 1.2 MB, one buffer per read that splices, for the rest of the verb. two new pins: over canonical, nested, legacy separator and the three #2273 shapes a spliced node stays undecoded and answers the eager read tree, addresses and saved tree, with tier_counts answering as the open does where two backslash-free keys name one path; and a key or holder name that is not UTF-8 refuses the splice and splices nothing. lineage_allocations now pins that the ingest count does not follow the entries. red under mutation, counts read each time: the eager read put back (0 passed and 2 failed in the engine pins, 2 and 1 in lineage_allocations), the splice check dropped (1 and 1), the holder check dropped (1 and 1), restored to 2 passed and 3 passed. no migration and no format byte moves, so this owes no deploy. the workspace suite is green (4282 passed over 136 binaries, 11 ignored) (#2282) 2350f8dd · dbf3dbe6…diff
  • the review-sweep fix-up over #2280, #2281 and #2282. the closure-walk pin stored x/abc after x/ab, which a random address beginning with c completes, so its stored-once control failed 5 runs in 400; it now uses y/abc and read 400 of 400 green. the spliced-node pin claimed the tree a later save writes back, but that save writes the copy the graph file already holds, since the rewrite inserts what it reads back first and ChangeGraph::insert keeps the first node for an id; it now saves into a store with no graph file, the splice comment says why, and a splice that blanks each spliced manifest goes red on that assertion (0 passed, 1 failed). decided and pinned: an ingest past a key or holder name that is not UTF-8 in a node it does not splice returns Ok and the next rewriting save refuses, kept because the open already defers such a file and stepping every pool node reads 13.8 to 14.4 ms beside a 54.4 to 55.0 ms deferred read of this repo graph file (red when the check scans the whole pool and when the save fallback is dropped, 0 passed and 1 failed each). measured with a tracking allocator over that 85.7 MB file: the rewrite union holds 88.2 MB deferred against 222.5 eager and peaks at 193.9 against 382.0, both freed when the save returns; a pool keeps one copy of the file per read that splices, 85.7 to 342.7 MB over one to four reads against 1.2 to 6.7 eager, recorded at the splice. the move-not-clone property lost its count pin to deferred manifests (a clone costs about two allocations per spliced node, 78 against 65 at the pin shape) and is pinned by the pool ids instead (red under the clone, 0 and 1). the closure walk doc names the holder-name case where it answers and the decoding walk panicked, pinned (red when the ascent check also steps holders, 0 and 1). each mutation restored to green. list-class sentences in HUNT-PERF and the loot-perf fixture rows now name what defines the set of graph file reads, and a rewrap leftover and a stale keys_ascend reference are fixed. no migration and no format byte moves, so this owes no deploy. the workspace suite is green (4285 passed over 137 binaries, 11 ignored) (#2287) a8c863e3 · dbf3dbe6…diff
  • loot blame and loot log --path read the one path they ask of each change through the new GraphView::address_at, which answers off the manifest frames through the new Manifest::address_of where the frames provably are the map (no backslash key, keys strictly ascending in Path order, checked by the keys_ascend step Manifest::addresses uses) and from the decoded map otherwise, so a history walk no longer decodes every manifest on the lineage to read one entry of each, and blame no longer clones each tree; has_path keeps decoding, since its remaining production caller asks many paths of each of a few changes. the new --path-history opt-in half times address_at over every change of a re-opened chain at 200 paths, asking the path that orders last; paired, one loot-perf-gate --features count built per arm with the frame arm never taken as the before, interleaved, the gate reading load idle at 1 to 4 percent (peak 7): 34.87 to 34.97 ms down to 4.46 to 4.53 ms at depth 256, 138.8 to 139.3 down to 18.01 to 18.03 at 1024, and 279.2 to 280.6 down to 35.9 to 36.2 at 2048; counted by the new path_history_allocations test at width 200, the walk allocates 484,352 times at depth 1024 against 0. through the binary, against the landing position binary over a repo 1,025 changes deep and 203 paths wide, min of 15 over two rounds: loot blame 160.4 to 160.6 ms down to 33.7 to 33.8, loot log --path 164.8 to 165.8 down to 43.7 to 44.0, plain loot log 28.9 to 29.2 on both, and every output compared was byte identical with its exit code and stderr: blame plain, --porcelain, --json, with a selector and on an absent path, log --path on flat, nested, dotted and absent paths, and shortlog --path. new pins: over every golden, a file whose Path order is not its byte order, the legacy separator file and the three #2273 shapes, address_of answers what the eager map answers for every key and for probes that are not keys, decoding nothing exactly where the frames are the map, and it answers off the frames past a holder name that is not UTF-8; a spawned blame and log --path find a/b.txt beside a-b.txt and a.txt. red under mutation, counts read each time: the frame arm never taken (0 passed and 1 failed in the codec pin, 1 and 1 in path_history_allocations), the ascent check dropped (0 and 1, on the answer), the counts check dropped (0 and 1, on the control, since Windows reads a backslash as a separator), byte order in the stop (0 and 1 in the codec pin, 0 and 1 in the spawned pin), each restored to green. riding along: HUNT-PERF names the new half. no migration and no format byte moves, so this owes no deploy. the workspace suite is green (4289 passed over 137 binaries, 12 ignored) (#2283) 045c7e42 · dbf3dbe6…diff
  • the review-sweep fix-up over #2287, #2283 and #1986. a resumed pick now asks again about a path its stop asked about when the record holds no answer for it, rather than handing it to the three-way, which merged over the resolution and put back the edit it rejected: change_delta_merge takes the stopped record whole and takes every question on it out of the three-way, keeping an answer only where the line still holds it, so a resolve that recorded no answer (a loot from before #1986, or a failed answer write, which the workspace comment now truthfully says costs a re-ask) re-stops with nothing applied, keeps the restore point, and lands once the question is answered again; refusing --continue was weighed and rejected in the ADR 0080 #2291 amendment, since nothing would be left to resolve. the replay reads its direction from pick::SPECS, which now pairs each spec with it, and a record naming a version under a verb SPECS does not hold refuses instead of replaying as a cherry-pick. the usage note names no verb, so each stopping verb line is true of its own verb, and its test now fails if the note names a verb that replays. a cherry-pick replay to completion is pinned through the binary on the home position, a pinned primary and a lane. false sentences fixed: the in-progress operand reader, the pick stop comment, the resume test doc link, the continue_clause list, the CONTEXT.md in-progress entry and HUNT-PERF on graph file reads; rewrap leftovers in fixture.rs and engine.rs are fixed, and the replaying tests keep their resolution file outside the working tree. red under mutation, counts read each time: questions taken from the answers alone (16 passed and 1 failed in the loot-core pin, 31 and 2 through the binary), an unknown verb replayed as not inverted (20 and 1), the note naming cherry-pick and revert again (19 and 2, the second the census the usage lines no longer matched), each restored to green. no migration and no format byte moves, so this owes no deploy. the workspace suite is green (4309 passed over 138 binaries, 12 ignored) (#2291) 812c48c7 · 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.