Skip to content

fix(quality): a --quality-baseline pin another build computed was the floor, dead set and all - #259

Merged
joyful-ii-V-I merged 9 commits into
mainfrom
lane/qbaseline-producer-identity
Sep 17, 2026
Merged

joyful-ii-V-I merged 9 commits into
mainfrom
lane/qbaseline-producer-identity

Conversation

@joyful-ii-V-I

@joyful-ii-V-I joyful-ii-V-I commented Sep 17, 2026 •

Copy link
Copy Markdown
Collaborator

Stacked on #255 (lane/qsnap-producer-identity). Review only 5db5f9b9 (gate), c0c1125f (fix) and 3189ccb6 (a one-line follow-up the full suite caught). 2a31bc7b is the merge of #255 at ae7e1e01, which carries origin/main 31e788ce. This branch lands in the same integration train as #255, right after it. The coordinator resolves the shared products (gate loop, count, shard weights, CHANGELOG order, COMMANDS.md/printf_parity pins) once, on the merged tree.

The defect

--quality-baseline writes .ripwire_quality_baseline, and its dead <key> records depend on call resolution (isDeadCandidate reads g.inEdges). The sidecar was stamped only with head <sha>, and selectBaseline compared only that. So at one HEAD, a floor pinned by one build was compared against another build's working-tree dead set. It's the qsnap cache defect again, this time in a file the user writes on purpose. The window is ordinary: pin with the installed ripwire, then run the delta with ./build/ripwire (or after an upgrade) before the next commit.

Measured with two real builds of d8c225a2: one as built, one with graph.h's keepStdQualifiedCandidates forced to return true. Fixture: src/pool.cpp struct Pool { int launder( int x ) {…loop…} }; and src/use.cpp calling *std::launder( &v ).

direction pinned by delta by before same-build answer
phantom (tree untouched) unguarded guarded sidecar, gating dead-code on Pool::launder, exit 2 exit 0
hidden (the std::launder call deleted) guarded unguarded sidecar, exit 0 exit 2 (the real regression)

The fix

  • Format v6: the sidecar carries producer <64 hex>, the build's source identity (producerIdentity(), from the qsnap lane).
  • BaselineSource::Foreign: a pin at the current HEAD whose producer is missing or names another build is not the floor. --quality-delta falls back to the HEAD tree this build computes and reports baseline="git-HEAD (foreign sidecar ignored)". The CLI prints one stderr line and the full legend defines the marker. The pin is never unlinked on either arm, since the build that wrote it can still use it. A root with no git exits 1 naming the foreign pin. A pin at another sha is still stale first and keeps its self-heal.
  • Owner's call, recorded: fall back and keep the file, instead of keeping the pin and demoting dead-code rows. Demoting rows can't surface a regression whose row never appears, and another build can compute any kind differently.
  • Old sidecars: all existing sidecars are v5. Only a pre-stamp build can write one, so the version rule refuses them (owner's call: bump and refuse, per the v4/v5 precedent). That refusal used to be reported as absent: baseline="git-HEAD" plus a stderr line saying "no .ripwire_quality_baseline" right under the refusal naming the file (measured on d8c225a2). It now reads git-HEAD (sidecar unreadable). In a root with no git, both arms' fatal messages now say "unreadable" instead of "no ". Older binaries refuse v6 the same way.
  • --help=all states the rule and names the marker. test/qrevtokencheck.sh's hand-written sidecars move to the v6 header so its hostile head stamps still reach the head-stamp path.

On the fixed tree, the same two builds give the same-build answer in all four cross-build runs: phantom exit 0, hidden exit 2 with the real row, both marked foreign, sidecar still on disk.

Gates

  • test/qbaselineproducercheck.sh (new): 26/26 PASS. Against d8c225a2's binary 12 FAIL: format, both flipped-producer arms, unstamped, v5, no-git, MCP, legend, help. The controls pass there. Matched pairs over a real pin: the controls, with the producer kept, must change the answer and do; the flipped-producer forgeries must give the no-sidecar answer and leave the file byte-identical.
  • Targeted set (34 gates, including printffmtparity, docscommands, donelegend, legendcoverage, compactlegend, mcpattrparity, mcpclidiff, sidecarsymlink, qualitystale, qualitysignal, qoriginoracle, qsnapproducer, gateexit, manifest, gatecount, limitstable): all green after one fix. gateexitcheck (G2) caught a one-line && ok || no in the new gate.
  • ASan (built before the coordinator's sanitizer rule): the new gate, qrevtokencheck (25) and qualitystalecheck (37) pass with zero sanitizer logs, on the pre-merge and merged trees. Positive control for log capture: a deliberate heap overflow built by the same clang wrote its report to the same place.
  • Determinism and well-formedness on the foreign marker: two runs byte-identical and well-formed for full XML, compact XML and --json.
  • --quality-delta=$(git merge-base origin/main HEAD)..HEAD: gating="0". Five minor rows: readBaseline ccx 35→36, emitQualityDeltaLegend 30→31, kHelpHead LOC +2 (this lane); deserializeSnapshot ccx +2, LOC +5 (qsnap lane).
  • Full suite, one local run on 2a31bc7b (the merged tree, before 3189ccb6): gates=635 pass=632 skip=2 fail=1. The skips are the environmental two (argvdiffcheck, editchecknotecheck); g1freshcheck ran against a fresh ASan tree and passed. The one failure, adaptivecheck arm (c), was caused by this lane and was deterministic (red twice standalone). c0c1125f's mcpNoBaselineMessage declared const std::string file( … ), which extraction indexed as a symbol named file, so the gate's broad live-tree query --for="file" routed name-exact and kept 1 of 40. 3189ccb6 renames the local and uses = initialization. The probe routes subtoken+body:broad again (kept 40 of 40, no cliff), and adaptivecheck, this lane's gate, mcpflagshipcheck, mcpclidiffcheck, mcpcontractcheck, mcpframehonestycheck and gateexitcheck pass. No local full suite has run on 3189ccb6: per the coordinator's plan for a loaded shared machine, CI's legs are that run.

Pins moved

  • test/printf_parity.manifest: help_all only (UPDATE_GOLDEN_EXPECT=help_all matched). Still green on the merged tree with no re-record.
  • docs/COMMANDS.md: regenerated (the --quality-baseline and --quality-delta rows).
  • Gate count: generated, 621 after merging main (diagnoticecheck + this lane's gate).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Prevented quality snapshots and cached results from being reused across builds with different source content.
    • Improved handling of incompatible, unreadable, or foreign quality-baseline sidecars, with clearer fallback behavior and diagnostics.
    • Updated baseline metadata validation to avoid accepting artifacts from incompatible builds.
  • Documentation

    • Updated command help and documentation to explain build-specific baseline handling and fallback behavior.
    • Updated documented gate counts from 619 to 621.
  • Tests

    • Added coverage for cross-build snapshot and baseline compatibility scenarios.

joyful-ii-V-I and others added 9 commits September 16, 2026 15:08
…lves calls differently

The --quality-delta HEAD snapshot cache (the qsnap blob) holds a dead set, and a dead set is a function of
call resolution: quality.h's isDeadCandidate reads g.inEdges. The blob's filename key folds the qsnap
scheme, the extraction identity, the excludes, the file-size ceiling and the HEAD sha. A resolution-only
change moves none of them, so two builds that resolve calls differently, sharing one cache dir on one HEAD,
serve each other's dead set.

Verified with two real builds before writing the gate: main a55b118 as built, and the same tree with
graph.h's keepStdQualifiedCandidates (d39554d's std:: guard) switched off. Over a two-file fixture where
`std::launder( &v )` may bind an in-repo Pool::launder, with a shared XDG cache:
  guarded build, cold cache                          regressions="0", exit 0
  guarded build after the unguarded one warmed it    a gating dead-code row on untouched Pool::launder, exit 2
  unguarded build, working tree deleting the call    the real gating dead-code row, exit 2 (cold)
  same run after the guarded build warmed it         regressions="0", exit 0 (the regression hidden)
One qsnap blob in the shared dir both times.

test/qsnapproducercheck.sh forges a real warmed blob by structure (the header ends at the fnv1a64(HEAD sha)
field; a parse that does not land on the trailer FAILs). Arms F and G are matched pairs differing in one
thing: dead keys dropped (phantom) or added (hidden) with the producer bytes kept, a control that must
change the answer, and the same forgery with those bytes flipped, which must be refused and match a cold
cache. Arms A-E pin the key, the header, the derivation of the identity from source content, the blob's
producer slot and warm reuse.

RED against this commit's binary: 8 FAIL / 5 PASS. Both controls fire (F1 exit 0 -> 2, G1 exit 2 -> 0), F2
has no producer bytes to flip, G2 hides the regression (cold exit 2, forged exit 0), and A/B/C/D find no
producer identity. Registered in test/regression.sh, the shard weights (5.2 s standalone) and the generated
gate count (619). The fix is the next commit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… set and all

The qsnap and qbody cache keys, and the blob header they share, now carry the PRODUCER IDENTITY: the
SHA-256 over every file under src/ and queries/, derived on each build by cmake/source_identity.cmake and
compiled in as one generated definition (build/generated/source_identity.cpp, declared by
src/sourceidentity.h). serializeSnapshot writes fnv1a64 of it after the extraction identity;
deserializeSnapshot refuses a mismatch. qheadsnap keeps its key: the ingest blob holds extraction facts only.

Derived, not a kResolverVer to bump. This cache's record: B10.1a's isDeadCandidate exemption and 28c7d32's
kParserVer bump each changed what a blob means without the bump that would retire it, none of the twelve
scheme versions cites a resolution change, and d39554d weighed the caches in its own message and still
missed this. A derived identity renames the blob on any source change, whoever remembers what.

A .cpp, not a generated header, so a changed identity recompiles one tiny object instead of main.cpp and
ingest.cpp. The header lives in src/, not src/infra/: it names this build, and infraportcheck (C) forbids
that in the vendorable layer. Header shape changed: kQSnapCacheScheme 12 -> 13, kQBodyCacheScheme 3 -> 4
(the P0.2 precedent), qschemetrip re-pinned with a log entry.

MEASURED. The identity script: 49 ms per build, 191 files, equal to an independent Python derivation. Cost of
the rename, ripwire's own tree, private XDG cache, five runs each, stdout asserted identical: warm
--quality-delta median 3.04 s (2.78-3.21); qsnap recomputed over a warm ingest median 5.12 s (4.85-5.34).
The two-build experiment re-run on this tree (fixed build vs fixed build with the std:: guard off): one qsnap
blob per build in the shared dir, both orders byte-identical to a cold cache, no corrupt alert.

GATE test/qsnapproducercheck.sh: 16/16 PASS on the plain build (8 FAIL / 5 PASS on the previous commit).
Under asan/ripwire, through a wrapper teeing every child's stderr: ALL PASS, 0 sanitizer reports.
cachefuzzcheck ALL PASS including its qsnap ASan sweep. Targeted batch, plain build: infraportcheck,
qschemetripcheck, qextractionkeycheck, qsnapcachecheck, evictioncheck, qsnapprefetchcheck, editcheckcheck,
g1freshcheck, manifestcheck, gatecountcheck, limitstablecheck, gitstampcheck, selfcontainedcheck,
xmlwellformed, binoverridecheck, clonededupcheck, fixedbufsweep, portablebuildcheck, g1configcheck,
headsnapcachecheck, qualityexcludecheck, qualitycheck, gateexitcheck, pargatescheck, readmedriftcheck,
deckcheck, noaliascheck, crossdirincludecheck all rc=0. ripwirepubliccheck arm 7 was red only while
src/sourceidentity.h was untracked (it sweeps tracked files); re-run after this commit.

Found on the way, not this change's defect: an experiment restore of graph.h landed in the same second as
main.cpp.o's write, Apple's make 3.81 compares whole seconds, and a "successful" incremental rebuild kept a
guard-off main.cpp.o (--callers=launder count 1, not 0). Deleting the object fixed it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…dentity

# Conflicts:
#	CHANGELOG.md
#	test/regression.sh
…nored as this build's floor

The sidecar --quality-baseline writes holds `dead <key>` records, and a dead set is a function of call
resolution (isDeadCandidate reads g.inEdges). It was stamped with `head <sha>` alone, and selectBaseline
compared only that, so at one HEAD a floor pinned by one build was compared against another build's working
tree. The qsnap cache had the same defect; this is the file a user writes on purpose.

Measured with two real builds of d8c225a, as built and with graph.h's keepStdQualifiedCandidates forced to
`return true`, over this gate's two-file fixture (`std::launder( &v )` binds an in-repo Pool::launder only
without the guard):
  phantom: pinned unguarded, delta guarded -> gating dead-code row on untouched Pool::launder, exit 2
           (the guarded build against its own pin: exit 0)
  hidden:  pinned guarded, delta unguarded, only std::launder call deleted -> exit 0
           (the unguarded build against its own pin: exit 2, the real regression)

test/qbaselineproducercheck.sh pins it with matched pairs over a real pin, one thing different per pair and
each mutation asserted to have taken: dead records dropped (phantom) or useIt's real dead key added (hidden)
with the producer record kept (controls that must change the answer), and the same forgery with one hex
digit of the producer flipped (must give the no-sidecar answer, leave the file byte-identical, and name the
foreign pin). Beside them: an unstamped v6 pin, a v5 pin, a pin both stale and foreign, a root with no git,
the MCP quality_delta verb, the legend and --help=all.

Red first: against d8c225a's binary 12 rows fail (format, both flipped-producer arms, unstamped, v5, no-git,
MCP, legend, help) and the controls pass (honored pin, C1, D1, stale self-heal, the no-git honored pin).
Registered in test/regression.sh and .github/pargates-shard-weights.json; docs/gatecount_build.py wrote 620.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… floor, dead set and all

The sidecar is now format v6 and carries `producer <64 hex>`, the build's source identity
(producerIdentity, cmake/source_identity.cmake). selectBaseline honors a pin at the current HEAD only when
that record names this build. One that is missing or names another build is the new BaselineSource::Foreign:
--quality-delta falls back to the HEAD tree this build computes, reported as
baseline="git-HEAD (foreign sidecar ignored)" with one CLI stderr line and a full-legend sentence. Unlike a
stale pin it is never unlinked, on either arm, because the build that wrote it can still use it (a PATH
binary and ./build/ripwire in turn). A root with no git has no fallback and exits 1 naming the foreign pin.
A pin at another sha stays stale first and keeps its self-heal. Demoting dead-code rows instead was ruled
out: that cannot surface a regression whose row never appears, and another build can compute any kind
differently.

Every existing sidecar is v5, which only a pre-stamp build can have written, so baselineHeaderIsForeign now
refuses it. That refusal was reported as Absent: the CLI printed baseline="git-HEAD" (no sidecar existed)
and "no <file>" one line under the refusal naming it (measured on d8c225a with a v4 header).
BaselineReadStats::olderFormat now routes it to baseline="git-HEAD (sidecar unreadable)". In a root with no
git, both arms' fatal messages now name an unreadable sidecar rather than "no <file>". The MCP messages move
into mcpNoBaselineMessage (the CLI twin's shape) instead of a longer nested conditional.

Measured on the fixed tree with the same two builds (the fix plus the guard off in the second): all four
cross-build runs give the same-build answer. Phantom exits 0, hidden exits 2 with the real dead-code row,
both marked foreign, sidecar still on disk. Same-build pairs are unchanged.

Gates: test/qbaselineproducercheck.sh 26/26 PASS (12 FAIL on d8c225a). Under ASan it plus qrevtokencheck
(25) and qualitystalecheck (37) pass with zero sanitizer logs; a deliberate heap overflow built by the same
clang wrote its log to the same place, so the capture is live. Targeted set of 34 gates green. Working-tree
--quality-delta: gating="0"; minor rows readBaseline ccx 35->36, emitQualityDeltaLegend 30->31, kHelpHead
LOC 1091->1093. test/qrevtokencheck.sh's hand-written sidecars move to the v6 header so its hostile head
stamps still reach the head-stamp path.

Pins moved: test/printf_parity.manifest help_all only (UPDATE_GOLDEN_EXPECT matched); docs/COMMANDS.md
regenerated (the --quality-baseline and --quality-delta rows).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…241, #245, #246)

This lane is stacked on the qsnap producer-identity lane, whose one new commit since d8c225a is its own merge of
origin/main. Two conflicts, both expected:
  test/regression.sh — the gate loop is the UNION: 620 each side, qbaselineproducercheck (here) and
    diagnoticecheck (main), 621 byte-sorted; docs/gatecount_build.py re-run to WRITE 621 into README.md,
    docs/EVALS.md and present/deck5_ripwire_build.js, then --check.
  CHANGELOG.md — both sides kept: this lane's entry stays beside the qsnap entry it extends, main's two follow.
docs/limits_build.py --check matches the merged src/. The merge touched none of this lane's sources, the printf
parity manifest or docs/COMMANDS.md; both are re-checked against a --clean-first rebuild of the merged tree.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ol and rerouted adaptivecheck's broad query

c0c1125's mcpNoBaselineMessage spelled its sidecar name `const std::string file( rw::quality::kBaselineFile );`.
Paren initialization reads as a declaration, so extraction indexed a symbol named `file` (mcpverbs.h:3360), and
`--for="file"` over src/ routed name-exact to it: `adaptive: kept 1 of 40 - only 1 symbols matched this query`.
test/adaptivecheck.sh arm (c) uses "file" as its BROAD query on the live tree and expects "no relevance cliff",
so it went red: the one failure in the merged branch's full suite (gates=635 pass=632 skip=2 fail=1, standalone
red twice). The local is now `sidecarName`, `=`-initialized. The same probe routes subtoken+body:broad again, "kept 40
of 40 - no relevance cliff". adaptivecheck, qbaselineproducercheck, mcpflagshipcheck, mcpclidiffcheck,
mcpcontractcheck, mcpframehonestycheck and gateexitcheck all pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@joyful-ii-V-I joyful-ii-V-I added this to the 0.6.2 milestone Sep 17, 2026
@coderabbitai

coderabbitai Bot commented Sep 17, 2026 •

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 59226830-ec7d-42a5-91e3-24bfd34b36c3

📥 Commits

Reviewing files that changed from the base of the PR and between 105666c and 3189ccb.

⛔ Files ignored due to path filters (2)
  • test/printf_parity.manifest is excluded by !test/printf_parity.manifest
  • test/qschemetrip.hash is excluded by !test/*.hash
📒 Files selected for processing (18)
  • .github/pargates-shard-weights.json
  • CHANGELOG.md
  • CMakeLists.txt
  • README.md
  • cmake/source_identity.cmake
  • docs/COMMANDS.md
  • docs/EVALS.md
  • present/deck5_ripwire_build.js
  • src/cli.h
  • src/mcpverbs.h
  • src/quality.h
  • src/sourceidentity.h
  • src/verbs_quality.h
  • test/qbaselineproducercheck.sh
  • test/qrevtokencheck.sh
  • test/qschemetripcheck.sh
  • test/qsnapproducercheck.sh
  • test/regression.sh

Included review availability: Your plan provides up to 8 included reviews per hour; 3 remain after this review.


📝 Walkthrough

Walkthrough

The build now generates a source identity and embeds it in both executables. Quality caches and baseline sidecars store and validate this identity. Foreign or unreadable artifacts receive distinct handling. Two producer checks and related documentation are added.

Changes

Quality producer identity

Layer / File(s) Summary
Source identity generation and build wiring
cmake/source_identity.cmake, CMakeLists.txt, src/sourceidentity.h, src/quality.h
The build hashes files under src/ and queries/, generates source_identity.cpp, and compiles it into both executables.
Snapshot cache producer validation
src/quality.h, test/qsnapproducercheck.sh, test/qschemetripcheck.sh, CHANGELOG.md
Snapshot and body cache schemes include producer identity in keys and blob headers. Deserialization rejects mismatched producers.
Baseline sidecar selection and reporting
src/quality.h, src/verbs_quality.h, src/mcpverbs.h, src/cli.h, docs/COMMANDS.md, test/qbaselineproducercheck.sh, test/qrevtokencheck.sh, CHANGELOG.md
Baseline sidecars use format v6 with a producer record. Matching sidecars are honored; foreign, stale, and unreadable sidecars receive distinct handling.
Gate registration and reference updates
.github/pargates-shard-weights.json, test/regression.sh, README.md, docs/EVALS.md, present/deck5_ripwire_build.js
Two producer checks are registered, and gate-count references are updated from 619 to 621.

Priority: ➖ Normal

Estimated code review effort: 4 (Complex) | ~45 minutes

Change: Bug fix

Suggested reviewers: quaterniondrift, calvinchengx

Merge Risk: ⚪ Minimal · up to 3189c

The producer-identity changes have no supported merge-blocking risk in the reviewed scope.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 43.18% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 44 functions across 11 files. (7 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title identifies the main quality-baseline defect: a pin produced by another build was incorrectly used as the floor. Although the wording is grammatically awkward, it is specific and related to t…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 43.18% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 44 functions across 11 files. (7 skipped: 7 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
⚔️ Resolve merge conflicts 💡
  • Resolve merge conflict in branch lane/qbaseline-producer-identity
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch lane/qbaseline-producer-identity

Comment @coderabbitai help to get the list of available commands.

@joyful-ii-V-I

Copy link
Copy Markdown
Collaborator Author

This lands through integration train 1, #263, together with the other two PRs in the train. Merge resolutions and review follow-ups are on that branch, so nothing more is needed here. This PR will show as merged when #263 merges.

joyful-ii-V-I added a commit that referenced this pull request Sep 17, 2026
…ly workflow) into integration/train-1

Main moved after #263's CI went green; merged once with rerere disabled. One textual conflict:
- CHANGELOG.md: KEEP ALL. The train's #255 and #259 entries, then #252's "a deep or odd-shaped argument, source
  file or skills tree could crash or stall a verb"; #262's nightly entry auto-merged. 12 Unreleased entries.
Auto-merged and re-verified on the rebuilt binary: README.md, docs/LIMITS.md, docs/TUNING.md, and four src
files the train also changed (src/query.h, src/verbs_navigate.h, src/ingest_astquery.h via #253; src/mcpverbs.h
via #259). docs/gatecount_build.py --check (624) and docs/limits_build.py --check (217 caps) are clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@joyful-ii-V-I
joyful-ii-V-I merged commit 9e74894 into main Sep 17, 2026
1 check passed
joyful-ii-V-I added a commit that referenced this pull request Sep 17, 2026
integration: train 1 — root-spelling invariance (#253), qsnap producer identity (#255), qbaseline producer stamp (#259)
andriytyurnikov pushed a commit to andriytyurnikov/ripwire that referenced this pull request Sep 17, 2026
…nary

help_all conflicted between the train side (redhat-et#250's --help text, 56f3a50f) and redhat-et#259 (its own cli.h help text,
d5c6cea0). Neither describes the merged binary: printffmtparitycheck on the clean-built 9e74894 read
7a34d653ae468c98640688d86f2844b74dba7d5a7202a3b44bec2c65fd2413b8, and every other row passed.
Re-recorded with UPDATE_GOLDEN=1 UPDATE_GOLDEN_EXPECT=help_all: moved={help_all}, 41 unchanged, and the
expectation matched. printffmtparitycheck is then ALL PASS over 42 verbs. docs/COMMANDS.md needed no
regeneration: docscommandscheck is ALL PASS on the same binary.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
andriytyurnikov pushed a commit to andriytyurnikov/ripwire that referenced this pull request Sep 17, 2026
The doc-level items from the independent reviews of redhat-et#253 and redhat-et#259, folded into the train. No behaviour change.

redhat-et#253 (CHANGELOG, its "an answer depended on how the root was typed" entry):
(a) CodeRabbit's wording: the six spellings agree "byte-identical output, once the printed `root=` and
    `est_tokens=` are normalised". Raw outputs differ in `root=` on purpose, and `est_tokens=` prices those bytes.
(b) New disclosure: `ripwire tests/` no longer counts its own files as tests (no layer="test", no test seeds for
    --affected/--test-gate, no dead-code exemption); it answers as `cd tests && ripwire .` does.
(c) New disclosure: the `.` answer itself moves slightly on byte-capped output, because the byte models charge a
    path as printed. The default --lint page on this repo went 680 -> 689 rows.

redhat-et#259 (legend and guidance text only; wording asked for by its review and its session):
(d) kQdBaseHeadUnreadable now says, like the foreign reading, that an allow-dirty pin's absorbed findings are not in
    force. The v5 upgrade is exactly where an allow-dirty pin gets dropped and an exit flips 0 -> 2.
(e) "re-pin" becomes "re-pin on a clean tree (commit or stash first)" in the foreign legend, the unreadable legend,
    both CLI stderr lines for a foreign pin (git root and no-git root), the MCP no-git twin, and redhat-et#259's CHANGELOG
    entry. A plain --quality-baseline refuses on a dirty tree, which is where a user meets this message.
    Legend text carries no double hyphen (it lands in an XML comment).
(f) The S2 comment above relForHash (src/model.h, moved there by redhat-et#253) no longer calls the quality baseline
    committed and portable: it is gitignored, re-pinned before a change, and producer-stamped. The comment keeps
    the root-spelling reason both sidecars hash root-relative paths.
redhat-et#259 CHANGELOG, a published-claim accuracy fix from its session: "A v6 pin read by an older binary is refused the
same way ..." is replaced by the exact text supplied. An older binary refuses a v6 pin but reports it the old way,
as baseline="git-HEAD" with a "no .ripwire_quality_baseline" line, and the file is intact. A v5 pin at an older
commit is no longer self-healed: it is refused as unreadable, with two stderr lines per run, until re-pinned or
deleted.
Not changed, as asked: the MCP spelling of the unreadable marker (pre-existing, a follow-up lane).
redhat-et#255: nothing to fold (its two LOWs are accepted as is).

Verified on the rebuilt binary (main.cpp.o/ingest.cpp.o newer than the edits; the new legend string present in the
binary): printffmtparitycheck, compactlegendcheck, legendcostcheck, legendcoveragecheck, docscommandscheck,
qbaselineproducercheck, xmlwellformed, floormarkcheck, testgatelegendbudgetcheck, mcpattrparitycheck all rc=0,
0 FAIL rows. No pin moved.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant