Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
5109350
infra: the NUL-framed sibling of readByteSafeLine, for `git log -z` s…
pt-act Oct 23, 2026
5ea40f8
gitmine: the shared NUL-framed record reader, and all five consumers …
pt-act Oct 23, 2026
97f49d6
gitmine: THE MERGE FIX — a name-bearing merge emits a prefix empty th…
pt-act Oct 23, 2026
4c0aa67
test: the git-log FRAMING gate — a committed filename can never be re…
pt-act Oct 23, 2026
fa47404
git-porcelain-framing: the dead code goes, and the churn memo's schem…
pt-act Oct 23, 2026
26f2589
docs: CHANGELOG [Unreleased] — the git-log framing fix, with its cach…
pt-act Oct 23, 2026
26361fb
fix(gitmine): the lookahead hand-over is a copy, and the dead ternary…
pt-act Oct 23, 2026
0ced897
fix(gitmine): the walkers' abandon contract rides the field read, per…
pt-act Oct 23, 2026
8cab225
test(gitframingcheck): the one-line verdict wraps — gateexitcheck G2 …
pt-act Oct 23, 2026
5c81ca5
test(gitframingcheck): pin TZ=UTC — the CI digest/golden drift was th…
pt-act Oct 23, 2026
6a6f403
test(crashsweep): the S2 registry names the one shared reader, not th…
pt-act Oct 23, 2026
ef2ba39
test(deckcheck): --dashdash is a fixture's filename, not a flag
pt-act Oct 23, 2026
b3a6453
docs: CHANGELOG [Unreleased] — the framing fix's walkers stop at thei…
pt-act Oct 23, 2026
7d8b0a5
fix(gitmine): an empty metadata field is data — and ownership stops d…
pt-act Oct 24, 2026
cc6b312
test(gitframingcheck): arm (f)'s committer rides an allowlisted synth…
pt-act Oct 24, 2026
c695c6b
Merge upstream/main (b7c56cf5, train-37b) into the framing lane
pt-act Oct 27, 2026
5bfeac3
Merge upstream/main (cab28607, train-38) into the framing lane
pt-act Oct 27, 2026
72af5aa
Merge upstream/main (12056c16, train-40) into the framing lane
pt-act Oct 28, 2026
b8d4a73
Merge upstream/main (52896f51, train-41) into the framing lane
pt-act Oct 29, 2026
f65c2a9
Merge origin/main (61ab7aac, train 41b) into community/pr433
joyful-ii-V-I Oct 29, 2026
83c8f09
review fix for #433 (nits N2, N4): credit the contributor in the frag…
joyful-ii-V-I Oct 29, 2026
fafb3f2
review fix for #433 (nit N1): gitframingcheck arm (g) pins the marker…
joyful-ii-V-I Oct 29, 2026
b66e26e
review fix for #433 (CodeRabbit 4419234625): export REAL_GIT to the -…
joyful-ii-V-I Oct 30, 2026
90acd0f
review fix for #433 (CodeRabbit 4419234614): a non-empty stream with …
joyful-ii-V-I Oct 30, 2026
0666a5e
review note for #433 (CodeRabbit 4419234609): epochUnparsed is kept b…
joyful-ii-V-I Oct 30, 2026
850cafa
Merge origin/main into community/pr433 (train 42a)
joyful-ii-V-I Oct 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2135,9 +2135,9 @@ wrong, and it has. These are the results that say so, all in-tree, all published
### In the tests

<details>
<summary><b>731 gate scripts</b>, five contracts no unit test can hold, and the house rule: write the gate before the code it measures</summary> <!-- gatecount -->
<summary><b>732 gate scripts</b>, five contracts no unit test can hold, and the house rule: write the gate before the code it measures</summary> <!-- gatecount -->

`test/regression.sh` names **731 gate scripts** and is the authoritative list; <!-- gatecount -->
`test/regression.sh` names **732 gate scripts** and is the authoritative list; <!-- gatecount -->
`python3 test/pargates.py . ./build/ripwire -j 6` runs the same set in parallel. On top of them sit the
contracts that do not fit a unit test: two runs byte-identical, warm output identical to cold, output
that pipes clean through `xmllint --noout`, a sanitizer build with `-fno-sanitize-recover=all`, and a
Expand Down
39 changes: 39 additions & 0 deletions changelog.d/433-git-log-framing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
### Fixed — a committed filename can no longer be read as commit metadata (`git log` framing) (#433, thanks @pt-act)

Every `git log --name-only` consumer in the history miners framed its stream with a line-based reader and a
`__C__` marker matched as a line prefix, so a repository containing a file whose name begins with the marker
bytes had that file read as commit metadata. Measured on a hostile fixture (files named `__C__`,
`__C__x|1694000000.cpp`, `__C__ 1694000000.cpp`, `a<LF>b.cpp`, `q"uote.cpp`, `sp ace.cpp`, `back\slash.cpp`,
`pi|pe.cpp`, `--dashdash.cpp`): `--owners` analysed 8 of 11 files, five of them owned by an author email that
appears in no commit, one file's top owner flipped to a different real author, and the newline-named file lost
its churn entirely.

One reader now parses the stream for all five consumers (`gitLogFileSets`, `gitLogNameOnlyRaw`,
`gitFileCommitCountsInDayWindow`, `gitLogDecayedFileMining`, `gitFileAuthors`):
`git log -z --name-only --format=%x00__C__%x00%ae%x00%at%x00%ct`, with the marker a whole-field sentinel a path
can never spell and one field of lookahead so a merge's prefix empty is not mistaken for a record boundary.
Any POSIX-legal filename — newlines, quotes, backslashes, pipes, leading dashes, the marker itself —
round-trips byte-exact, and ownership answers with the author, never the committer. On trees with ordinary
names the answers are byte-identical to before, pinned by a golden recorded from the pre-change binary over
`--owners`, `--hotspots` and `--cochange`.

The history-walk cache's `kQChurnCacheScheme` moves 3 → 4: a blob written by a pre-lane binary holds the old
reader's stream, which for the repositories this fix targets is the corrupted one, and no key component can
tell that blob from a fresh read. Every repository's history is walked once more after the upgrade; on trees
with ordinary names the walk produces the same stream, so the cost is one cold walk and no changed answer.

**The walkers' stop contract rides the field read.** The line readers this replaces checked the abandon flag
before every line; the first cut of the field reader checked it only between fields, so a stream whose fields
never arrive — a git answering in its default format, or a shim streaming bare lines — held the read inside one
unterminated field until end-of-stream instead of stopping at the next unit. The flag is now polled once per
byte, and the memory-guard fixture that measures it refuses in about a second where the narrowed read blocked
for the whole stream.

**One case is disclosed, not fixed:** a merge whose FIRST combined path is a file literally named `__C__`
still reads as pathless — its prefix empty is followed by the marker-spelled path, which the boundary rule
cannot tell from a real record boundary, so that merge's files lose their attribution. Its next three paths
are read as a phantom record's email and epochs (which never parse, so ownership skips it); a fifth and later
combined path rides that phantom at epoch 0, which the co-change sets count as one more commit and
`--rank-by=churn-decay` weights as if it were HEAD's. The full fix is a format sentinel change, owned by a
follow-on spec. `test/gitframingcheck.sh` pins both the fix and this collision's exact observable, including
where the two churn miners disagree by design.
2 changes: 2 additions & 0 deletions changelog.d/versions.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ before them would replay the older slot rows. 269 sits above every live branch a
peel carries 259, the alias-import records 261 and the string/literal records 263, 265 and 267 on theirs); the next train goes
above 269 and 270. A cache written by 0.6.9 is re-parsed once. `kCacheVersion` stays 46 and the quality-snapshot scheme stays 24:
no record changes shape. The session legend dictionary is `dictv=1d522ffeeb3f37c2 entries=981`.

The history-walk cache's `kQChurnCacheScheme` moves 3 → 4 (#433: a blob written by a pre-fix binary holds the old line-frame reader's stream, which for the repositories the framing fix targets is the corrupted one, and no key component can tell that blob from a fresh read; every repository's history is walked once more after the upgrade, and on trees with ordinary names the walk produces the same stream, so the cost is one cold walk and no changed answer).
6 changes: 3 additions & 3 deletions docs/EVALS.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ section, and it is not an afterthought.
| **Co-change / known-item evals** | `--eval`, `--eval-retrieval` (see `bench/ANSWERQUALITY.md`) | Whether the tool surfaces the other files a real historical commit touched; and known-item retrieval across four rankers. |
| **Ensemble calibration harness** | `bench/ensemblecal/` | Whether `--ensemble`'s four evidence families are actually orthogonal, how often each fires, how stable each is across commits — and the preset ladder derived from that (§9). |
| **Differential argv harness** | `test/argvdiffcheck.sh` | That a refactor changed *nothing observable*: two binaries, every argv vector, stdout + stderr + exit code byte-identical. |
| **The gate suite** | `test/regression.sh`, `test/pargates.py` | 731 gate scripts plus the determinism, cache-transparency and golden contracts. <!-- gatecount --> |
| **The gate suite** | `test/regression.sh`, `test/pargates.py` | 732 gate scripts plus the determinism, cache-transparency and golden contracts. <!-- gatecount --> |
| **`--quality-delta`** | `src/quality.h` | Ten measured code-quality failure modes, reported only where a change made them worse. |

### The labeling protocol (why the held-out eval is allowed to disagree with the ranker)
Expand Down Expand Up @@ -5888,7 +5888,7 @@ copy here would be exactly the dialect divergence that gate exists to catch. Com
tags, wrap, stable-order defaults), seven individually invoked standalone gates (`g1freshcheck`,
`skillscan`, `htmlexport`, `compresscheck`, `handoffcheck`, `releaseinstallcheck`,
`taskroutecheck`), and a single loop
naming **731 gate scripts**, all of which exist on disk. <!-- gatecount -->
naming **732 gate scripts**, all of which exist on disk. <!-- gatecount -->

`python3 test/pargates.py . ./build/ripwire -j 6` runs the same scripts in parallel so a full
verification fits in one sitting. It does not modify `regression.sh`.
Expand Down Expand Up @@ -7005,7 +7005,7 @@ Listed because the reason is more useful than the silence.
shipped**. See `bench/locbench/anchorhop_calib.json`. The mention anchor's reproducible numbers are
the ablations in §4.
- **A single round gate-count.** Two in-tree numbers disagree (`test/pargates.py`'s docstring says
~210; `test/argvdiffcheck.sh` says 200+), while the loop in `test/regression.sh` names 731. The <!-- gatecount -->
~210; `test/argvdiffcheck.sh` says 200+), while the loop in `test/regression.sh` names 732. The <!-- gatecount -->
loop is the authority; the stale docstrings are a known drift. Since 2026-09-10 the number is not
written by hand anywhere: `docs/gatecount_build.py` derives it from the loop and rewrites every
published site, `test/gatecountcheck.sh` fails if any of them drifts, and `test/manifestcheck.sh`
Expand Down
6 changes: 3 additions & 3 deletions present/deck5_ripwire_build.js
Original file line number Diff line number Diff line change
Expand Up @@ -1214,7 +1214,7 @@ function storyCards(s, { kick, head, stories, footText }){
kicker(s, "// how it stays true", AMBER);
title(s, "Proven, not promised");
const cards = [
["731 gate scripts", "the suite runs on every push — plus determinism, cache-transparency and golden contracts; the gate count itself is gated against the runner's own loop"], // gatecount
["732 gate scripts", "the suite runs on every push — plus determinism, cache-transparency and golden contracts; the gate count itself is gated against the runner's own loop"], // gatecount
["byte-identical, always", "two runs over the same tree produce the same bytes; warm equals cold. Enforced in CI, twice — Release AND a plain flavour, because NDEBUG once blinded a whole class of checks"],
["differential refactoring", "a refactor must prove it changed nothing observable: two binaries, hundreds of argv vectors, stdout + stderr + exit codes byte-identical"],
["held-out labels, authored blind", "eval labels were written by reading source before the ranker ever ran on them — so the eval is allowed to say the ranker is wrong. It has."],
Expand All @@ -1238,7 +1238,7 @@ function storyCards(s, { kick, head, stories, footText }){
title(s, "Claims you can trust, because we publish what failed", { size: 32 });

card(s, MX, 1.72, 3.86, 1.72);
stat(s, "731", "gate scripts named by test/regression.sh — and the COUNT itself is gated against the runner's own loop, so it cannot go stale quietly", // gatecount
stat(s, "732", "gate scripts named by test/regression.sh — and the COUNT itself is gated against the runner's own loop, so it cannot go stale quietly", // gatecount
MX+0.15, 1.86, 3.56, CYAN, { bsize: 42, bh: 0.66, lsize: 9.5 });
card(s, 4.68, 1.72, 3.86, 1.72, CARD2);
stat(s, "8", "registered NEGATIVES — changes built, gated green, measured against a band written before the code, and reverted rather than tuned",
Expand Down Expand Up @@ -1540,7 +1540,7 @@ function storyCards(s, { kick, head, stories, footText }){
["193 long flags · 36 slides", "bash test/deckclaimcheck.sh"],
["every --flag named here exists", "bash test/deckcheck.sh"],
["74.7% fewer element bytes", "bash test/showcasecapturecheck.sh"],
["731 gate scripts", "bash test/manifestcheck.sh"], // gatecount
["732 gate scripts", "bash test/manifestcheck.sh"], // gatecount
["49 repos · 71 papers · 237 surveyed","bash test/readmedriftcheck.sh"],
["the ten moments, any row", "ripwire . --callers=SYM | wc -c"],
["the head-to-head table", "bench/headtohead/r4-2026-08-06/"],
Expand Down
Loading
Loading