Changes touching this path
- loot-perf: the harness crate with fixture, measurement, and record seams (#634)
ef9fd595 · dbf3dbe6… - Record the per-land perf point as append-only docs/benchmarks/series.jsonl (#636)
7b1c78d7 · dbf3dbe6…diff - loot-perf: the CLI tier — hyperfine-timed push, receiver-counted wire bytes, raw samples and load on the record (#635)
3588811d · dbf3dbe6…diff - loot-perf: the CLI tier — hyperfine-timed push, receiver-counted wire bytes, raw samples and load on the record (#635) (conflict resolution: crates/loot-perf/src/lib.rs)
08300a17 · dbf3dbe6…diff - the pre-land perf gate: counters block at 0 percent, timing warns, and a lane pins itself (#637)
52a2341e · dbf3dbe6…diff - loot-perf: run_cli probes the machine as found, ahead of its own build and fixture wake (#704)
5e49dfb6 · dbf3dbe6…diff - the perf record becomes a directory of one-file points, so two lands no longer collide on the last line, and a point names its PR (#740)
The record was the repo's worst conflict surface: every land appended to the tail
of docs/benchmarks/series.jsonl, so two lands always collided on the last line —
6 of the 38 conflict-resolution commits on main name that one file. It is now
docs/benchmarks/series/<change>.json, one file per point, so lands touch disjoint
paths. The pre-#740 jsonl stays where it is and is read as the older head of the
same series; migrating it would have been one last whole-file rewrite of exactly
the file this was about.
A point also carries the PR it landed under. A bounce recovery mints a new change
id, so one shipped thing can leave four points, each measuring a genuinely
different tree — none may be suppressed, and the PR is what says they are one
thing. It is also the record's only join back to git log: landed_change is the
durable change id, while the projected trailer carries the version id, so no id
in the record has ever matched anything in git.
The land's write is still a verbatim copy of the gate binary's stash (#712); the
stash itself stays a single-line jsonl, since it is a temp handoff merged by
nobody. The file-name rule is duplicated in loot-first, which may not link
loot-perf, and pinned by a test on both sides.
c393beef · 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 - a signing seam reads the working tree once and walks it once, so describe and land stop buying two whole-tree byte reads to look at file names, and loot-perf grows the capture timing that can see it (#1383)
31995cb5 · dbf3dbe6…diff - the perf gate stops reporting a run that measured nothing as a clean pass, and the load probe stops letting one bursty draw discard the measurement, because the two bracketing reads were folded by max and the second sat in the harness's own wake (#1389)
542d5442 · dbf3dbe6…diff - the implicit capture in front of every bare mutating verb reads the working tree once, so the tree it judges clean is the tree it records, and the doors already committed to capturing ask their refusals before paying for a read (#1395)
7f1fb453 · dbf3dbe6…diff - loot converge stops buying a second whole-tree read for the note it prints, because the implicit capture one seam below has already read that tree, and the series added to catch exactly this learns to fail: its fixture had neither the co-located checkout nor the standing fork the branch needs, so it could not reach the cost it was watching (#1401)
12c1207a · dbf3dbe6…diff - one bursty opening draw stops discarding a measurement that was never allowed to be taken, because the opening probe now declines a timing only where the fold has already condemned it, which also makes a load-caused skip printed beside a suppressed load line unreachable (#1402)
562376f9 · 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 three surfaces describing the perf gate stop disagreeing about what an absent metric means, because an explained absence is reported and exits 4 exactly as an unexplained one does, and each surface now points at the one that owns a fact rather than restating it — including the exit code itself, named once in loot-perf and read from that name by both the gate binary and the land (#1403)
dc3ccf75 · 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 - loot status stops reading the whole working tree twice, because the working row and the pending delta now come from one walk taken through the door #1383 built for exactly this verb, and that walk's plaintext moves into the live side rather than being copied out of a tree nobody reads again, while the read-only door finally carries a number: a cli-tier status sample whose control refuses the two shapes that read the tree fewer times, and a test-only walk counter that pins the count at one (#1549)
8a9411f2 · 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 - the land gate stops watching only what a run produces and starts watching what it does, because two work counters now ride the untimed batch it was already measuring so they cost no fixture, no repetition and no workload_id move and orphan no baseline, while the twelve opt-in timings stay ungated on the argument that a timing is dropped by the load policy exactly on the busy machine a land runs on, and the count feature stays off by default because turning it on in a manifest was measured to change the loot.exe a plain workspace build produces (#1602)
f641ea1d · dbf3dbe6…diff - the perf skill index stops stating a gated status that nothing checked, because the work-counters row now carries a sentence generated from Work::ALL and gate::COUNTERS rather than one written by hand, which is how it came to read not recorded and not gated for a whole land after #1602 gated two of them in the very commit that corrected three other passages of the same file, and the four claims from the same arc that outran their code are fixed at both copies each: the loot-count module doc now carries the amendment ADR 0073 made to its verbatim twin in ADR 0072, ADR 0072 counts five counters beside a list of five and states the allocator split without a count at all, a fabricated test citation gives way to the test that really pins the gated id, and the land-time on-cost in ADR 0073 is about 56 microseconds derived from the table 80 lines below it rather than 2.8 (#1629)
f5a642e0 · 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 - the last two readers of the deleted argv shape ask the door instead, and the blocker this ticket named was not the one holding them out: the static lifetime was never in the way, because OPT_IN_HALVES is a const whose flag fields are already static, so the valued union is a const fn fold at an arity derived from the three class lists and a half without a class becomes a build failure rather than a runtime one - what actually kept the perf binaries from naming the door is that loot-net is a dev-dependencies row in their manifest, deliberately since #847, because a real edge would link axum, reqwest and tokio into the gate binary every land builds. so the door moves once more, to loot-core, which every binary-shipping crate already names directly and which is pure std, making this #1628's own criterion applied one crate further rather than a second door - loot-net keeps a re-export shim so no caller changed. the recursive delete is demonstrated rather than argued: --size 64 pinned-checkout scratch bound the scratch slot to 64 and a file planted under a temp tree at 64/counters-repo is gone after a run driven by the real production path, proved non-vacuous by aiming the same call elsewhere and watching the assertion fail; it now binds the checkout the operator typed. the gate's poisoning is worse than this ticket described and its example argv is corrected rather than repeated, since that one is already refused by the strict single-position parse - the argv that genuinely passes needs the forged operand followed by another flag, and before this it wrote a 925 byte measured record to a file named --reps in the working directory while the land-and-stash pairing guard passed, because the stash it checked had been forged out of the message operand. after, it exits 2 naming the pairing and never measures. both call sites were extracted from main first, following #1628's remedy, so a pin can reach what main asks rather than only the door's traversal - and every pin was reverted to prove it red except one that pins fresh rather than the binding, which is documented as such and proven non-vacuous separately. the exemption list is emptied and re-adding a name now costs deleting a working census, since the roll call asserts set equality (#1682)
d7e849b0 · dbf3dbe6…diff - the cli tier header stops saying that pointing loot-perf-cli at a lane fails by design, because it never has and the lane is where the readings were taken: PinnedCheckout gained a loot arm in #637, which is the next change to touch this file after #635 wrote that refusal into the header, and it left the sentence standing, so the refusal was a later decision the header never learned about rather than an accident nobody had judged, which is what #1895 asked to be established before the words moved. the replacement states the door rather than a list of the positions it allows: the checkout is whatever PinnedCheckout::open can name a revision for, that door being the only constructor of the type a measurement is taken against, so this binary can neither widen nor narrow what is accepted, and a lane is named positively as a position it measures and the position to measure from where a reader has one, a lane being single-writer and not moving while git worktree add buys the same property for a reader who has none. the live primary stays the thing to keep out of, now with the verb that actually moves it, a catch-up in another session, since ADR 0050 took the land off that tree. two more sites in the crate carry the same narrowing, both stale since #637 rather than wrong when written, and both are corrected: the Unpinned doc called the target one git cannot name when that refusal needs the loot arm to decline as well, and the RevisionMoved message sent a reader who is already in a lane off to build a worktree, where what it wants is a position nothing else writes. #1595 recorded its before and after that way and said so, loot-perf-cli on the lane, both exit 0 and discarded 0 batches. red under mutation, counts read each time: the loot arm deleted from PinnedCheckout::open takes the pinned suite to 5 passed and 8 failed, and restoring it reads 13 passed and 0 failed. the only shipped move is the text of one refusal, with no wire byte, format or schema moving and no verb or flag added, so this owes no deploy. the workspace suite is green (4092 passed over 131 binaries, 8 ignored) (#1895)
d4c4e0ca · 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.