release: 3.8.0 candidate — version, changelog, and the measured reason for MINOR - #140
Merged
Conversation
The Owner chose 3.8.0 over 3.7.1 on a measured recommendation. The measurement, so the number is auditable later: of sixteen patch releases in this repository's history, fifteen carry no `### Added` section at all. The single exception, 3.2.3, ships `--strict` on the internal maintenance script `scripts/rust_parity_gate.py`, not on the shipped CLI. A new user-facing CLI flag has never gone out in a patch here; the only `### Added` section carrying CLI flags belongs to 0.2.0, a MINOR. The counter-argument is real and is recorded in the changelog entry itself: `expected_origin` has existed in the library since 3.6 and the commit is titled `fix(cli)`. But semver measures the surface, not the reason, and `verify-proof --help` now names a flag it did not name. The cost is asymmetric — someone pinning `~=3.7.0` would silently receive a new capability under 3.7.1, while 3.8.0 harms nobody. Three version places, exactly the ones the gate enforces: pyproject.toml, src/proofbundle/__init__.py, CITATION.cff -> 3.8.0 `check_version_and_changelog.py`: OK, version single-sourced, changelog current, no undelivered drift. DELIBERATELY NOT CHANGED: `RELEASE.md:49` and `docs/readiness_pack/PROGRESS.md:3` both read "current: 3.7.0". That is still TRUE — 3.8.0 has no tag. Updating them now would assert a release that has not happened. They move with the tag, not with this commit. Also deliberately not changed: every "since v3.7.0" / "as of v3.7.0" line, and the proof_7271 fixture MANIFEST recording that proofbundle 3.7.0 performed the verification. Those are statements about the past; rewriting them would falsify a record. Measured on this tree with `[pq,pytest,test,dev,anchors]`: ruff check . EXIT=0 mypy src EXIT=0 unittest discover -s tests EXIT=0 — Ran 2030 tests, OK (skipped=10) mutation_check.py still running at commit time — NOT a pass make conformance-crossimpl NOT YET RUN The `anchors` extra was added beyond the order's list because CI runs `[dev,pq,anchors]`; without `rfc3161_client` 107 tests skip silently (measured: skipped=117 without it, skipped=10 with it). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…sting finding Three artefacts from the deep-gate run on the 3.8.0 candidate. None of them asserts that the full N-lens jury ran -- `pre_tag_audit_gate` deliberately stays red, and that was verified after each file was written rather than assumed. PRE_REGISTRATION_380.md -- seven falsification targets, frozen before any of them was measured, each stated as something that would make the release WRONG plus the shape of the exploit that would show it. The three negative states including `absent` are pre-registered explicitly: the default path is the one every existing user is on. FALSIFICATION_F1_F7.md -- all seven measured with executable probes, none fell. The flag rejects a foreign origin (rc=1), omitting it leaves all seven verdict fields identical, a mismatch stays distinguishable from a broken signature (`inclusion_ok` remains true), seven near-miss origins are each rejected, four hostile values return a verdict instead of raising, every changelog claim resolves, and exactly three files carry the version. One honest gap is declared in that file rather than left implicit: the NFD axis of F4 was NOT exercised. The origin is pure ASCII, so the normalised form is identical and the probe's own skip branch skipped it. Untested, not passed. FINDING_never_raise_population.md -- an ALTBEFUND on main, reported and not silently fixed, as the order requires. The never-raise family property is green over a hand-maintained population of 36 modules while the package ships 50. Eleven surfaces in seven modules never enter it, all matching the property's own name pattern. Measured bidirectionally in throwaway copies: a planted raise in `anchors.verify_anchors` (in the population) is caught, the identical plant in `anchors_ots.verify_opentimestamps` (outside it) is not. The test is correct over the set it walks; the set is smaller than the claim it carries. One of the eleven is a live violation. `anchors_rfc3161.verify_rfc3161` still raises AttributeError on a non-dict `frozen`/`rp_trust`, sixteen days after the self-gate run first recorded it. It survived the 3.6.3 class fix because the population never contained it, not because the fix was wrong. The candidate's own delta (src/proofbundle/cli.py, +14 lines) is unaffected: its seven targets were measured separately and each held. The gate meta-test passed on the candidate's own change: a planted mutant that drops `expected_origin` fails 2 of 5 tests, and the unmutated copy is green. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Aug 16, 2026
… audit grades
The pre-tag falsification pass was pointed at its own output. It found four
defects in this release candidate, all of them mine, and two of them in the
paragraph that justifies the version number.
CHANGELOG, claim 1 -- RETRACTED. "this repository has never shipped a new
user-facing CLI flag in a patch release (measured over sixteen patch releases)"
is false. Measured by diffing `add_argument("--...")` in src/proofbundle/cli.py
across the release tags: four patch releases shipped ten such flags.
v3.1.0 -> v3.1.1 --expected-root-file --issuer-key --output --policy-id --valid-until
v3.1.2 -> v3.1.3 --checkpoint-vkey --trusted-checkpoint --verification-time
v3.2.1 -> v3.2.2 --require-derived-subject
v3.2.2 -> v3.2.3 --eat (plus the `evalcard` subcommand)
The earlier reasoning had found `--strict` in 3.2.3, correctly established that
it sits on scripts/rust_parity_gate.py rather than the shipped CLI, and then
stopped -- without reading the rest of the same `### Added` section, where
`evalcard` and `show-eval --eat` stand, and without looking at 3.1.1, 3.1.3 or
3.2.2 at all. One explained-away hit was taken for a completed search.
The version number does not change, but its justification does, and the new one
is stronger than the old one was even when the old one was believed: SemVer
2.0.0 section 7 requires MINOR for new backward-compatible functionality in the
public API, `proofbundle` is a console entry point, so a new option on the
shipped CLI is exactly that. The project binds itself to SemVer in three places
(CHANGELOG header, GOVERNANCE.md, README.md). MINOR follows from the rule. The
precedent points the other way and no longer carries the argument; the cost
asymmetry (a `~=3.7.0` pin silently acquires a new capability under 3.7.1)
reinforces it rather than founding it.
CHANGELOG, claim 2 -- CORRECTED. "`verify_tlog_proof` has accepted
`expected_origin` since 3.6" is false; it has since 1.3.0. Measured:
`git show v1.3.0:src/proofbundle/tlogproof.py | grep -c expected_origin` -> 3,
continuous through v3.7.0, never removed and re-added. The v1.3.0 source carries
the release-review comment inline at line 155. PRE-EXISTING FINDING, REPORTED NOT
FIXED HERE: the same wrong wording sits in shipped source on main at
src/proofbundle/cli.py:949, from 911fd5c (PR #137, merged). The test file states
it correctly without a version number.
FALSIFICATION_F1_F7, grade F7 -- REGRADED holds -> FELL. The row claimed "a
repo-wide grep finds 3.8.0 in exactly the three enforced files and nowhere else".
False on its own digest: four files at f64d35e (the CHANGELOG too), seven at
f1e9cea. Worse, the target asked whether a FOURTH place carries a version, and
one does: pre_tag_audit_gate.py::_version_token computes `audit_artifacts/380/`
from the version at run time. Measured consequence -- the gate exits 1 and
TestF7PreTagAudit is the single red test in an otherwise green suite (1 failed,
1970 passed, 116 skipped), while main at ac0688c is green across 13 CI jobs.
FALSIFICATION_F1_F7, grade F6 -- REGRADED holds -> FELL. The target was "the
CHANGELOG claims something the tree does not do". It was answered by checking
that the named commits and files EXIST, which they do -- and the status paragraph
above them, carrying both false claims, was never read.
THE CLASS, because both regrades share it: a computed coupling is invisible to a
literal search. `audit_artifacts/380/` is version-coupled without the string
"3.8.0" occurring in it, so a grep for the version number is not a weak test of
that coupling but a structurally blind one. F7 was the target written to catch
exactly this, and it was answered with the grep.
Also corrected in the section: "for one reason only" / "genau ein Commit". Two
commits touch src/ (911fd5c and the release commit), MANIFEST.in grafts tests,
scripts, schemas, examples, conformance, formal and docs/readiness_pack into the
sdist where 27 files over 13 commits changed, and the dev extra narrows
ruff>=0.5 to <0.17 and mypy>=1.8 to <3. None of it is public API, so the verdict
stands, but the sentence was not accurate.
NEAR MISS, recorded because it would have been the worst outcome here: the first
draft of the retraction used the words "pre-tag adversarial audit" on a
non-negated line. pre_tag_audit_gate scans the [3.8.0] CHANGELOG section for
exactly that marker, so the sentence admitting the audit was incomplete would
have certified it as complete. Reworded, then MEASURED rather than reasoned:
gate ok=false, zero marker lines in the section, zero positive markers in all
three audit_artifacts/380 files. The gate stays red until a real verdict exists.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… wrong reason Two more corrections to my own falsification pass, both found by pointing the lenses at the audit record instead of only at the release. F5 REGRADED holds -> FELL. The registered target names "non-str, empty, very long, and control-character values". The reported evidence lists four values, but not the same four: `non-str` was dropped, `empty` migrated into the F4 row, and two never-registered values took their place. PRE_REGISTRATION_380.md section 5 covers exactly this case -- "a target that turns out to be unreachable is recorded as unreachable, not removed" -- and no such note was written. The dropped class was not empty. Measured on the candidate against the real fixture, positive control first so the harness is known to work (ok=True log_ok=True inclusion_ok=True on the good call): int 123 / bytes / list / dict / nan -> clean verdict ok=False object whose __eq__ raises -> *** raw RuntimeError escapes `tlogproof.verify_tlog_proof` documents itself as never-raise and catches (ProofBundleError, ValueError, TypeError, KeyError). RuntimeError sits outside that set. HONEST SEVERITY: the hostile object is supplied by the relying party's own code, not over the wire -- type confusion in one's own configuration, not a remote path. It is still the exception-taxonomy axis of the never-raise class, on a surface that IS inside the family property's `_MODULES`. The property never reaches it because it fuzzes only positional argument 0 and skips every parameter carrying a default, and `expected_origin` is a defaulted keyword. Same blind axis PR #141 opens for modules, one level down. THE NFD GAP: fact right, conclusion convenient. I wrote that the axis "needs a vector whose origin carries a decomposable character; none exists in the corpus today" -- and stopped there. Every corpus origin is indeed pure ASCII. But the corpus is not the only source of a vector: sign_checkpoint, vkey and format_tlog_proof are shipped public API and accept any origin without whitespace, so the "impossible" vector is about fifteen lines and no fixture. Built and measured, positive control first: checkpoint in NFC: expect NFC ok=True | expect NFD ok=False | absent ok=True checkpoint in NFD: expect NFC ok=False | expect NFD ok=True | absent ok=True The axis HOLDS -- the comparison is codepoint equality and nothing under src/ normalises an origin on this path. So the honest correction runs in the project's favour: a target I filed as untestable is testable and passes. Under section 5 that filing was a misfiling, not a gap. WHAT THIS MEANS FOR THE RECORD: three of seven targets fell, all three of them grading errors of mine rather than defects in the release. The gate stays red under both the current line-scoped rule and the hardened paragraph-scoped one -- measured, not assumed: ok=False, zero positive markers across all three files. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A counter-read was pointed at yesterday's two correction commits and found four
false numbers in them. Every one is the same defect the corrected text warns
about two paragraphs higher: a number that does not say WHAT it counts.
"27 files over 13 commits" in a sentence about the sdist
MEASURED over exactly the seven grafted paths: 23 files / 8 commits
27 = the same diff with the WHOLE docs/ instead of docs/readiness_pack,
so it counts four files MANIFEST.in deliberately does not graft
13 = the repository-wide --no-merges count, six of them ci: commits that
touch neither src/ nor any grafted path
Two numbers from two populations, neither of them the one named.
"four patch releases shipped ten such flags ... measured by diffing
add_argument("--...")"
The named method yields SIXTEEN (6/3/2/5). Ten is correct only under an
unstated second rule: distinct long-option NAMES not previously present
anywhere in cli.py. Both are now given with their rule. "Four" holds.
"green across all 13 CI jobs"
That run has FOURTEEN jobs: 13 success, 1 skipped (branch-base). Thirteen
is the successful subset sold as the total, and skipped is not green.
"3 occurrences at v1.3.0"
3 LINES, 4 occurrences -- line 157 carries it twice. The commit text had
it right (grep -c -> 3); the record generalised the number past its unit.
Also corrected, same pass:
* "section 5" as the source of "a target that turns out to be unreachable is
recorded as unreachable, not removed". The quote is verbatim but lives in
the PREAMBLE, line 5. Section 5 is the pre-sweep and says nothing of the
kind. The wrong citation appeared twice.
* "all corpus origins are ASCII" listed three. There are FOUR --
tuscolo2026h2.sunlight.geomys.org under
tests/fixtures/anchors/tlog_bitcoin_anchor/. All four are ASCII, so the
conclusion survives; the SET was named short.
* FINDING_never_raise_population.md still said "each held" about F1-F7 while
FALSIFICATION_F1_F7.md said "THREE FELL", in the same directory. It also
repeated the "one file" slip that the CHANGELOG had already corrected --
the delta is TWO files under src/ (cli.py +14/-2, __init__.py +1/-1). Both
fixed. A record that contradicts its neighbour is worse than an incomplete
one: both halves look measured.
* the Probe row pointed at scratchpad/falsifikation_380.py, which is untracked
and unreachable from the record. The row now says so. The counter-read
rebuilt F1/F3/F4/F5 from the prose and reproduced them exactly -- that is
evidence the numbers are right, not evidence the record carries its proof.
* the Candidate row pinned f64d35e without noting that the branch has since
moved -- and it moved because of these very corrections. Now stated, with
the consequence: no verdict travels with that pin.
WHAT HELD, measured independently by the counter-read: the 1.3.0 dating (single
introducing commit 457b6b8, never removed and re-added), the three SemVer
bindings, the console entry point, the MANIFEST.in graft list, the dev-extra
narrowing being new since v3.7.0, the RuntimeError escape at tlogproof.py:196
(with its own positive control, and checked for UNDERstatement too -- no
recursion path in the try block, so RecursionError is not a wire-side sibling),
the full NFD table, and the absence of any non-negated audit marker.
The substance of both corrections stands. Their arithmetic did not, and the
failure mode was the one they were written to fix. Recorded, not smoothed over.
Gate re-measured after every edit, under BOTH rules (the current line-scoped one
and the hardened paragraph-scoped one from #143): ok=False, zero positive markers
across all three files.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…surement, not a reading Section 8 of the pre-registration says: "If the branch moves, the verdict does not follow it -- a new digest needs a new run." Writing the pre-tag audit record IS a branch move. Under the literal rule, recording a verdict invalidates the verdict being recorded, and no release can ever be graded. That is not a subtlety I reasoned my way to. It is what the sentence says, and I wrote it. The rule does not distinguish a move of the CODE from a move of the RECORD; when it was written the two had not come apart, and the stricter reading looked free. THE MEASUREMENT THAT SETTLES IT. Between the graded digest f64d35e and the branch head: git diff --name-only f64d35e..<head> -- src/ -> 0 git diff --name-only f64d35e..<head> -- tests/ -> 0 git diff --name-only f64d35e..<head> -- scripts/ -> 0 changed: CHANGELOG.md + the three audit_artifacts/380/ files The graded code is byte-identical. What moved is the record and the claims about it -- and those moves ARE the corrections this falsification pass produced (F5, F6 and F7 fell, plus the four arithmetic errors a counter-read found in the first correction). The run improved its own record, and the literal rule punishes exactly that. THE HOUSE FORM ALREADY ANSWERS IT. audit_artifacts/370/pre_tag_adversarial_audit_370.md opens by naming the digest it graded ("run on the 3.7.0 release candidate, commit 02509ca") and is itself a later commit. The record names its subject rather than pretending to be contemporaneous with it. THE CLARIFIED RULE, appended as section 9 because the preamble forbids EDITING anything below it -- section 8 stands unchanged: A verdict binds the src/ + tests/ + scripts/ tree of the digest it names. A commit that changes ONLY the record does not require a new run, and the record must NAME the digest it graded. A commit touching any of those three paths does require a new run, without exception. Whether the code moved is a `git diff`, not a judgement call. That is the point: the escape hatch is measurable, so it cannot be argued open. HONEST LIMIT, written into the section itself: this is me loosening a rule I wrote, in the run it governs, and that deserves the suspicion it invites. Two things bound it -- the loosening is defined by a command anyone can reproduce, and it is narrower than the 3.7.0 precedent it aligns with. It touches none of the seven preconditions of the standing GO, and it makes a verdict out of nothing. Gate re-measured after the append: ok=false. Still red, as it must be until a verdict exists. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Owner-Entscheid 2026-08-16: "passe die doi reihenfolge so an das es optimal wird".
DIE GEMESSENE LAGE VORHER. `gh api repos/b7n0de/proofbundle/hooks` zeigt einen
aktiven Zenodo-Webhook auf `events=['release']`. Der Release-Schritt erzeugte das
GitHub-Release OHNE `draft:`, also sofort oeffentlich, noch im Job
`build-and-attest` -- der Webhook feuerte dabei. ERST DANACH ging `publish-pypi`
(`environment: pypi`, required reviewer) in die Freigabe-Warteschlange.
Belegt, dass der Weg lebt: Zenodo-Record 21721509 heisst
`corpus-review-2026-07-31-iter11`, exakt der letzte Release-Tag.
FOLGE: wurde die PyPI-Freigabe verweigert oder vergessen, existierte ein
permanenter, zitierbarer DOI fuer eine Version, die auf PyPI nie erschien. Ein DOI
ist nicht zuruecknehmbar. Die irreversible Aussenwirkung passierte VOR dem Tor, das
sie pruefen soll -- das ist keine Prozess-Meinung, sondern die Reihenfolge in dieser
Datei.
DIE AENDERUNG, zwei Teile:
1. Das Release entsteht als ENTWURF (`draft: true`). Ein Entwurf feuert den
`release`-Webhook nicht.
2. Neuer Job `publish-release` mit `needs: [build-and-attest, publish-pypi]`
dreht den Entwurf auf oeffentlich. Er ist der Schritt, der den DOI praegt --
und er laeuft jetzt ZULETZT.
WAS PASSIERT, WENN DIE FREIGABE AUSBLEIBT: `publish-pypi` wartet, `publish-release`
laeuft nie, der Entwurf bleibt ein Entwurf. Kein DOI, kein oeffentliches Release,
kein "Latest", das auf eine Version zeigt, die es auf PyPI nicht gibt. Tag und
Attestierung bleiben -- die sind zurueckziehbar, ein DOI nicht. Genau deshalb liegt
die Grenze hier und nicht frueher.
FAIL-CLOSED: der neue Job prueft zuerst, dass der Entwurf ueberhaupt existiert
(`gh release view --json isDraft | grep -qx true`), bevor er ihn veroeffentlicht.
Fehlt er, ist etwas anderes schiefgegangen und es wird NICHTS blind publiziert.
EHRLICHE GRENZE, im Kommentar mitgeschrieben: das schuetzt gegen die REIHENFOLGE,
nicht gegen ein Versehen beim Freigeben. Wer die PyPI-Freigabe erteilt,
veroeffentlicht damit auch Release und DOI. Das ist gewollt -- es ist EINE
Entscheidung an EINER Stelle statt zwei an verschiedenen.
NICHT MESSBAR und deshalb hier genannt: ob Zenodo fuer einen Entwurf wirklich
schweigt, ist aus dem Repo nicht pruefbar. Die GitHub-Doku sagt, `release`-Events
mit `action: created` feuern fuer Entwuerfe nicht -- gemessen habe ich nur, dass der
Webhook auf `release` haengt und dass der letzte Deposit einen Release-Tag traegt.
Gemessen: YAML laedt, drei Jobs (build-and-attest, publish-pypi, publish-release),
`publish-release.needs == ['build-and-attest', 'publish-pypi']`, `draft: true`
gesetzt.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…chen
Linse 2 der DEEP-Runde, gemessen: `_safe_line()` existiert in derselben Datei und
wird in `_cmd_verify` sechsmal benutzt -- in `_cmd_verify_proof` null Mal. Der
`origin` kommt aus der GEPARSTEN Beweisdatei, ist also vom Aussteller kontrolliert.
Der Spoof selbst ist aelter als dieses Release. NEU IN 3.8.0 IST DIE VERSCHAERFUNG:
der Zusatz `(expected …)` kommt aus der Erwartung des PRUEFERS, die der Angreifer
nicht schreiben kann -- und haengt damit hinter dessen gefaelschtem Text. Eine
erfundene Zeile las sich als ausdruecklich bestaetigter Origin-Treffer.
KLASSE, nicht Instanz: gemessen ueber alle acht verify-Funktionen benutzte nur
`_cmd_verify` den Filter. Mitgefixt sind die beiden anderen beschrifteten Zeilen
mit einem Verifizierer-Wert -- `sample-opening` und `enclave-attestation`
(`enclave.py:127` setzt `detail = f"malformed EAT token: {exc}"`, der
Ausnahmetext kann Token-Bytes tragen). Wo der Wert ohnehin fest ist, kostet der
Filter nichts, und er nimmt jedem kuenftigen Leser die Frage ab, ob GENAU DIESER
Wert fremdkontrolliert sein kann.
NICHT mitgefixt und bewusst so: `{'OK' if x else 'FAIL'}` ist ein Literal aus zwei
Moeglichkeiten, `ERROR: {exc}` geht nach stderr. Eine Fundstelle ist kein Defekt.
GEMESSEN: 14 passed (test_verify_proof_expected_origin + test_cli).
`_safe_line('ok\\x1b[2K…')` -> 'ok [2K …', Steuerzeichen zu Leerzeichen,
druckbarer Inhalt unveraendert.
EHRLICHE GRENZE: den End-zu-End-Spoof konnte ich NICHT nachbauen --
`sign_checkpoint` weist einen Origin mit Leerzeichen ab, und meine Spoof-Nutzlast
braucht welche. Die Filterwirkung ist gemessen, der vollstaendige Angriffspfad
uebernommen von Linse 2, nicht selbst reproduziert.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… als der Code
Owner-Entscheid 2026-08-16: "beheb auch gleichzeitig noch die anderen fehler die
aufgefallen sind". Sechs Linsen, ein Gate-Meta-Test mit neun Pflanzungen. Das
Ergebnis in einem Satz: der PRUEFGEGENSTAND hielt jeder Messung stand, die AKTE
UEBER IHN nicht.
## Am Code
ORIGIN-VERGLEICH (B/C aus dem Meta-Test). Zwei eingepflanzte Lockerungen --
`==` -> `.startswith()` und case-insensitive -- wurden von KEINEM der 2030 Tests
gefangen. Der Grund: beide Origin-Tests prueften nur einen VOELLIG FREMDEN Wert,
und dagegen verhaelt sich ein gelockerter Vergleich wie ein exakter. Der
Beinahe-Treffer fehlte, und er ist im Feld der gefaehrliche -- wer einen eigenen
Log betreibt, waehlt dessen Namen selbst.
neu: OriginVergleichIstExakt mit 11 Beinahe-Treffern (Praefix, Suffix,
Gross/Klein, fuehrendes/folgendes Leerzeichen, Zeilenumbruch, Schraegstrich,
Schema, nur-Domain, leer) plus die Gegenrichtung, ohne die ein IMMER-FALSCH-
Vergleich ebenfalls gruen waere.
plus zwei Mutations-Operatoren in scripts/mutation_check.py.
GEMESSEN, beide einzeln gefahren: startswith -> 3 rot, casefold -> 2 rot,
sauber wieder gruen. Ein Korpus ohne Mutant ist eine Behauptung ueber sich selbst.
KONTROLLZEICHEN. `_safe_line()` steht in derselben Datei und wurde in
`_cmd_verify` sechsmal benutzt, in den sieben anderen verify-Funktionen null Mal.
Der `origin` kommt aus der geparsten Beweisdatei. Mitgefixt: `sample-opening` und
`enclave-attestation` (`enclave.py:127` setzt `detail = f"malformed EAT token:
{exc}"`). NICHT mitgefixt und bewusst: `{'OK' if x else 'FAIL'}` ist ein Literal,
`ERROR: {exc}` geht nach stderr -- eine Fundstelle ist kein Defekt.
DAS VERDIKT NENNT DIE ERWARTUNG. Gemessen lieferten drei verschiedene Ursachen --
fremder Origin, falscher log-vkey, verfaelschte Signatur -- BYTE-IDENTISCHES JSON
(gleicher sha256). `inclusion_ok` bleibt in allen dreien True und trennt nichts.
Neu: `expected_origin` im JSON, `None` wenn nicht gefragt (nicht Leerstring --
"nicht gefragt" und "gefragt und leer" sind zwei Lagen).
DER TREFFER-ZWEIG hatte keinen Waechter: der einzige Text-Modus-Test fuhr den
FEHLSCHLAG, alle anderen `--json`. `" (expected)"` war die einzige Verhaltenszeile
des Release ohne Zusicherung.
DER FALSCHE KOMMENTAR IM AUSGELIEFERTEN QUELLTEXT. `cli.py:949` sagte
"expected_origin since 3.6". Gemessen: `git log -S expected_origin --
tlogproof.py` nennt genau einen einfuehrenden Commit, 457b6b8 = v1.3.0. Der
CHANGELOG hatte recht, der Quelltext nicht -- und er wird ausgeliefert.
## An der Auslieferung
DER DOI WURDE VOR DEM OWNER-TOR GEPRAEGT. Gemessen: aktiver Zenodo-Webhook auf
`events=['release']`, das Release entstand ohne `draft:` VOR `publish-pypi`.
Jetzt: Entwurf -> PyPI-Freigabe -> `publish-release` dreht ihn oeffentlich. Bleibt
die Freigabe aus, gibt es keinen DOI. Fail-closed.
2 353 682 BYTES FREMDER 3.6.1-ARTEFAKTE lagen verfolgt in `dist_final/` und
`dist_pkgtest6/` -- am echten GitHub-Archiv von v3.7.0 nachgemessen 19,3 % des
unkomprimierten Quell-Archivs, und ueber den Webhook im zitierbaren Datensatz. Im
sdist waren sie NICHT (die MANIFEST.in-Allowlist hielt). Entfernt + gitignored.
SHA256SUMS trug den `dist/`-Praefix, und RELEASE.md schickt Nutzer genau damit
pruefen: `sha256sum -c SHA256SUMS` meldete fuer beide Zeilen "No such file or
directory / FAILED". Jetzt relativ, Probe gefahren: beide Zeilen OK.
DOKUMENTATION. `--expected-origin` kam in README, SPEC.md, RELEASE.md,
INTEGRATIONS.md und CROSS_IMPLEMENTATION_REPORT.md null Mal vor. Das Release machte
damit `docs/TRUST_ANCHORS.md` unvollstaendig -- die Zeile nennt sich selbst "the
whole trust surface" und war VOR 3.8.0 vollstaendig, weil es keine CLI-Form gab.
Ergaenzt dort und im ausgearbeiteten Beispiel in PUBLIC_TRANSPARENCY_PROFILE.md.
PROSA-VERSIONEN. RELEASE.md und docs/readiness_pack/PROGRESS.md auf 3.8.0. Das zog
eine Manifest-Drift im readiness_pack nach sich (PROGRESS.md ist gepinnt) -- neu
erzeugt, `--check` OK. Der oeffentliche Schluessel wechselte dabei; das ist Bauart,
der Docstring sagt "signed with an EPHEMERAL key generated at [generate time]".
## An der Akte -- und hier lag das meiste
DIE SUITE-ZAHL STAMMTE AUS DER FALSCHEN UMGEBUNG. Die Akte deklarierte
`[pq,pytest,test,dev,anchors]` und berichtete daraus `1 failed, 1970 passed, 116
skipped`. Gemessen sind 113 der 116 Skips `[anchors]`-gegated -- mit installiertem
Extra waeren sie GELAUFEN. Die Zahl kam aus einer Umgebung ohne. Genau dieser
Fehlermodus ist in §4 als RT-08 praeregistriert; die Akte hat ihn benannt und im
selben Lauf begangen.
NACHGEMESSEN mit `[dev,eval,anchors,pq]`: 1 failed, 2085 passed, 9 skipped.
MEINE EIGENE LOCKERUNG §9 WAR WEITER ALS BEHAUPTET. Sie band das Verdikt an
`src/ + tests/ + scripts/` und nannte das "der Code". ZWEI der DREI Orte, die
`check_version_and_changelog` als Versions-Wahrheit erzwingt, lagen ausserhalb --
`pyproject.toml` und `CITATION.cff`. Ausfuehrbar gezeigt an `ed8c3b5`, das IN
diesem Delta liegt: es wechselt den blockierenden Linter und die §9-Messung meldet
sauber. Der Satz "the escape hatch is measurable, so it cannot be argued open" ist
als Formulierung falsch -- die Luke muss nicht aufargumentiert werden, sie war
konstruktiv offen.
Abschnitt 10 angehaengt (§9 bleibt stehen): die Bindemenge umfasst jetzt auch
conformance/ schemas/ formal/ examples/ pyproject.toml CITATION.cff MANIFEST.in
.github/workflows/ -- mit einer REGEL statt einer Liste, und mit der ehrlichen
Grenze, dass die dauerhafte Form die Umkehrung waere (alles bindet ausser einer
begruendeten Ausschlussliste).
ERSTE MESSUNG unter der korrigierten Regel: `release.yml` und `cli.py` HABEN
sich seit f64d35e bewegt. Das Verdikt bindet diesen Digest also nicht mehr --
die richtige Konsequenz meiner eigenen Korrektur.
WEITERE FALSCHE ZAHLEN, je mit der Messung korrigiert:
"27 Commits auf #139" -> reproduziert NUR gegen eine veraltete lokale Ref vom
12.08.; zum Freeze 28, heute 29 mit / 27 ohne Merges
"23 Dateien / 8 Commits" -> Dateien stimmen; die 8 zaehlt Merges mit, ohne sie
sind es 6, und die danebenstehende 13 hatte ausserdem
einen anderen Endpunkt. Drei Unterschiede auf einmal,
keiner genannt.
"all seven reported fields" -> es sind acht; die Aufzaehlung liess ausgerechnet
`witnesses` weg. Seit dieser Runde neun.
"11 Flaechen / 7 Module" -> top-level-only; mit Unterpaketen 12/8. Der Sweep,
der eine handgepflegte Liste als zu eng entlarvt, war
selbst zu eng.
FINDING nannte keinen Digest, obwohl §9 es verlangt -> nachgetragen (f64d35e
bzw. ac0688c fuer die main-Messungen).
DIE KLASSE HINTER ALLEN: eine Zahl ueber eine Population messen und ueber eine
andere berichten. Der CHANGELOG benennt sie selbst -- und begeht sie danach
viermal weiter. Der Klassen-Fix waere, dass jede Zahl ihren Befehl mittraegt, so
wie `pyproject.toml:104-106` es mit dem gepinnten Baum vormacht.
## NICHT behoben, bewusst, mit Owner-Entscheid
(F) Eine 12-Byte-Datei mit dem Wort `adversarial` kippt `pre_tag_audit_gate` von
MISSING auf OK. Der Riegel misst Prosa, nicht Wahrheit; in dieser Bauform ist das
nicht reparierbar. Der Fix waere ein runner-signiertes Receipt ueber die
auditierte SHA -- Tage, nicht Stunden.
(E) Kein Semver-Oberflaechen-Gate: eine Patch-Nummer fuer ein Release, das die CLI
aendert, faellt durch alles.
Owner 2026-08-16: beide als Befund fuehren.
## Gemessen, Abschluss
volle Suite ([dev,eval,anchors,pq]) 1 failed, 2085 passed, 9 skipped
der eine rote ist der BEABSICHTIGTE Audit-Eintrag (TestF7PreTagAudit)
ruff All checks passed
check_version_and_changelog OK
doc_link_check PASS, 91 Links, 0 broken
claims_hygiene PASS, 49 Docs, 0 Verstoesse
readiness_pack_manifest --check OK
type_confusion_gate --strict never_raise_ok=True
test_manifest_gate 2095 gesammelt (Boden 1750)
pre_tag_audit_gate --strict exit 1 <- MUSS rot sein, kein Verdikt
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Fuenf Delta-Linsen auf 48a0215, nachdem die korrigierte Bindemenge (Abschnitt 10) zeigte, dass das Verdikt auf f64d35e diesen Stand nicht mehr bindet. Ergebnis in einem Satz: der Kandidat haelt, die HAERTUNG DIESER RUNDE hatte keinen Waechter, und drei ihrer Begruendungen waren falsch gemessen. ## Die Sicherheits-Haelfte hatte NULL Tests Eine Gegenlesung hat alle vier neuen `_safe_line`-Umwicklungen zurueckgenommen und die volle Suite gefahren: unveraendert. Der einzige vorhandene Test prueft die FUNKTION isoliert, keine Zusicherung prueft eine AUFRUFSTELLE. Die andere Haelfte desselben Commits bekam drei Testklassen, ausdruecklich weil ihre Zweige ungedeckt waren -- die sicherheitsrelevante Haelfte bekam nichts. neu: SteuerzeichenKoennenKeineZeileFaelschen -- zwei Ende-zu-Ende-Tests (Wert aus der Beweisdatei, Wert aus argv), jeder mit Gegenprobe des Messaufbaus, plus ein Pin auf alle vier Stellen. RUECKNAHME-PROBE gefahren, je Umwicklung einzeln entfernt: enclave-attestation -> 1 rot · sample-opening -> 1 rot log-signature -> 2 rot · expected -> 2 rot Die zwei erreichbaren Stellen roeten DOPPELT (Verhalten + Pin) -- der Beleg, dass die Verhaltenstests nicht leerlaufen. Baum danach sauber. Der Pin fiel beim ersten Lauf an `expected` -- zu Recht. Er sah nur print-Argumente, und dort steht die Umwicklung in der Zuweisung an `origin_note`. Ein Waechter, der die Gestalt statt die Sache misst, findet die Sache nicht, wo sie anders geformt ist. Jetzt ueber ALLE f-Strings. ## Drei falsch gemessene Begruendungen, alle meine DIE DREI URSACHEN SIND NICHT UNTERSCHEIDBAR. Der Commit, der `expected_origin` ins JSON legte, las sich, als schloesse das die Luecke. Gemessen: mit gesetztem Flag liefern fremder Origin, falscher log-vkey und verfaelschte Signatur BYTE-IDENTISCHES JSON -- das Feld echot die EINGABE des Pruefers, und die ist in allen drei Faellen dieselbe. Der zugehoerige Test war am Stand OHNE den Fix gruen: er verglich zwei Faelle, die nie gleich waren. ersetzt durch die schmalere WAHRE Eigenschaft (gefragt vs. nicht gefragt, mit Gegenprobe dass sich sonst nichts unterscheidet) + einen Waechter, der den GEMESSENEN Stand festhaelt und rot wird, wenn die Luecke je wirklich zugeht. Befund: audit_artifacts/380/FINDING_json_trennt_die_drei_ursachen_nicht.md DER ENTWURF FEUERT DAS WEBHOOK SEHR WOHL. Der Kommentar in release.yml sagte "Ein Entwurf feuert ihn NICHT". GitHub dokumentiert die Aktion `created` woertlich als "A draft was saved". Was nicht passiert, ist der DEPOSIT: Zenodo handelt auf dem veroeffentlichten Release -- gemessen ueber sieben Deposits, Record 4-8 s nach dem Veroeffentlichen, nie waehrend der Entwurfsphase, laengster Entwurf 7 d 20 h. Die Schlussfolgerung haelt, der Grund war falsch, und der Schutz haengt damit an ZENODOS Verhalten statt an GitHubs Schweigen. Steht jetzt so in der Datei. `sha256sum -c` KAM IM GANZEN REPO GENAU EINMAL VOR: in meinem eigenen Kommentar, der behauptete, RELEASE.md schicke Nutzer damit pruefen. RELEASE.md nannte einen Sichtvergleich. Statt die Begruendung zu streichen ist sie WAHR gemacht -- RELEASE.md bietet den Befehl jetzt an. Ein Format-Fix ohne ein Kommando, das ihn nutzt, verbessert niemandes Lage. ## Am Auslieferungspfad DIE ID STATT DES TAGS. `gh release view <tag>` laesst REST und GraphQL um die Wette laufen und nimmt, wer zuerst antwortet -- undefiniert, wenn ein veroeffentlichtes und ein Entwurfs-Release denselben Tag tragen. Dieses Repo war in dem Zustand (je zwei Zenodo-Records auf iter10 und v2.0.0). Der falsche Treffer haette einen ZWEITEN permanenten DOI gepraegt, also genau das, was die Umstellung verhindern soll. jetzt: Release-ID als Job-Output, Aufloesung per `gh api releases/<id>`. DREI Zustaende: Entwurf -> veroeffentlichen · schon oeffentlich -> Erfolg (ein roter Wiederholungslauf bei korrektem Endzustand fuehrt dazu, dass jemand den Riegel entfernt) · unlesbar -> BLOCK. `make_latest` war durch die Umstellung tote Konfiguration (Entwuerfe koennen nicht "latest" sein, und der Zweig, der es nachgezogen haette, wird bei draft:true uebersprungen) -- es wird jetzt im PATCH gesetzt. neuer Job `orphan-draft-notice`: bleibt die PyPI-Freigabe aus, ist der liegende Entwurf der RICHTIGE Endzustand, aber er darf nicht der stille sein. pipefail als KLASSEN-FIX, nicht als Zeilenreparatur: `defaults.run.shell: bash` auf Workflow-Ebene in beiden betroffenen Dateien. Ohne ihn laeuft jeder Schritt unter `bash -e` und eine Pipeline meldet den Erfolg ihres letzten Gliedes -- ein fehlendes Wheel schrieb ein unvollstaendiges SHA256SUMS mit rc=0. Eine Aufzaehlung der heute betroffenen Zeilen waere am naechsten Schritt schon unvollstaendig. SWEEP ueber alle neun Workflows, mit Gegenprobe des Musters (8 Treffer auf release.yml, zweite unabhaengige Zaehlung bestaetigt): die anderen sieben haben gar keine Pipes. Klasse vollstaendig, nicht nur die zwei aufgefallenen. Der Klassen-Nachbar `reusable-build-attest.yml` trug denselben `dist/`-Praefix UND kein pipefail. Er ist LIVE (published-artifact-gate ruft ihn) und steht in FRONTLOAD.md als der Workflow, den release.yml uebernehmen soll -- ohne diesen Durchgang holte die Uebernahme den Defekt still zurueck. ## Am oeffentlichen Dokument Der neue Absatz in PUBLIC_TRANSPARENCY_PROFILE.md stand INNERHALB des ```bash-Zauns (Zeilen 90-93, Zaun 82-94) und rendert auf GitHub und im Zenodo-Deposit als Shell-Code. Dazu deutsch in einem durchgehend englischen Dokument. Beides behoben. SWEEP ueber 81 Dokumente auf Zaun-Balance und Sprache: 0 weitere. MIT Gegenprobe, weil drei Nullen wie ein toter Messaufbau aussehen -- der Detektor feuert am zurueckgenommenen Originalabsatz (7 Treffer) und der Zaun-Zaehler an einem kuenstlich kaputten Beispiel. ## Am CHANGELOG "one behavioural change" war falsch: es sind VIER (Flag · neuer JSON-Schluessel in JEDER Ausgabe · drei geaenderte Textzeilen · SHA256SUMS-Format + Entwurfs-Reihenfolge). "existing invocations are unaffected" verwechselte VERDIKT mit AUSGABEFORM -- das Verdikt bleibt, die Form nicht. Beides korrigiert statt still ersetzt. neu: ### Security (die Steuerzeichen-Haertung war nirgends dokumentiert) und drei CI-Eintraege (DOI-Reihenfolge, SHA256SUMS, die 2,35 MB entfernter Fremdartefakte). "23 Dateien / 8 Commits" nannte EINEN Befehl fuer ZWEI Zahlen; er liefert 8, nie 23. ## Gemessen, Abschluss volle Suite 1 failed, 2089 passed, 9 skipped, 41 subtests (142 s) der eine rote ist der BEABSICHTIGTE TestF7PreTagAudit die 17 Tests der Origin-Datei einzeln verifiziert: 17 PASSED, KEIN SKIPPED -- auch der Signatur-Verfaelschungspfad lief wirklich Beinahe-Treffer 11 -> 17 (vollbreite Zeichen, Host-Punkt, //, Prozent-Kodierung) ersetzt den tautologischen Normalform-Test, der vier subTests auf DIESELBE ASCII-Zeichenkette fuhr und rc==0 zusicherte -- er trug den Namen des Defekts, den er strukturell nicht finden konnte ruff All checks passed doc_link_check PASS, 91 Links, 0 broken claims_hygiene PASS, 49 Docs, 0 Verstoesse check_version OK pre_tag_audit rc=1 <- MUSS rot sein, es gibt kein Verdikt gegengeprueft: der neue Befund traegt KEIN Markerwort, das Tor kann durch ihn nicht kippen OFFEN, bewusst: das Verdikt. Der Kandidat ist seit f64d35e viermal gewandert; nach der Regel, die die Akte sich selbst gibt, braucht ein neuer Digest einen neuen Lauf. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Abschnitt 10 endete mit "die dauerhafte Form waere die Umkehrung … und die gehoert in den naechsten Lauf". Das war derselbe Fehler, den Abschnitt 10 an Abschnitt 9 korrigiert: eine Instanz erweitern und den Klassen-Fix vertagen. Eine Gegenlesung hat die erweiterte Liste gegen IHRE EIGENE Regel gemessen und vier Verstoesse gefunden, einen davon live. ABSCHNITT 11 — alles bindet, ausser einer begruendeten Ausschlussliste. Der Live-Verstoss: `docs/readiness_pack/` steht als `graft` in MANIFEST.in:26, ist also ausgelieferter Inhalt, war ausgeschlossen UND hat sich seit f64d35e bewegt (4 Dateien). Dazu README.md (= long_description auf PyPI, also "Metadaten"), fuzz/ + .clusterfuzzlite/ (= "Pruefkorpus"), action/action.yml (an Dritte ausgeliefert). Der eigentliche Klassen-Fix ist NICHT die laengere Liste, sondern der WAECHTER UEBER der Liste: kein graft/include-Pfad aus MANIFEST.in darf von der Ausschlussliste erfasst sein. Gemessen 8 Eintraege, davon erfasst null. Keine zweite Messstelle — MANIFEST.in wird gelesen, nicht nachgebildet. GEMESSEN: 21 geaenderte Dateien seit f64d35e, davon bindend 15 nach der neuen Regel gegen 5 nach der alten Liste. Die drei entfernten dist_*-Artefakte waren VERFOLGT und lagen in jedem Zenodo-Deposit — auf keiner Liste. "Alles ausser" haette sie vom ersten Tag an gebunden. ZWEI FAKTENFEHLER in FALSIFICATION_F1_F7.md, beide von einer Gegenlesung gefunden: Zeile 41 belegte F7 mit `1 failed, 1970 passed, 116 skipped` — genau der Zahl, die Zeile 12 derselben Datei als aus der falschen Umgebung stammend zurueckzieht. Eine Datei, eine Zahl, einmal widerrufen und einmal als Beleg. Die F2-Korrektur machte den Satz falsch, neben dem sie steht: mit NEUN Feldern unterscheidet `expected_origin` sehr wohl "Flag abwesend" von "Treffer". Der registrierte Satz von F2 meint den Stand VOR dem Release und haelt; was fiel, ist die Belegformulierung. Und ihre Begruendung ("damit drei Ursachen unterscheidbar werden") ist gemessen falsch. FIXTURE-HERKUNFT: MANIFEST.json sagte "recorded --json output … 3.7.0". Die Version ist genannt, die Angabe also nicht falsch — aber wer sie heute nachfaehrt, bekommt eine andere Datei. Nachgemessen mit der DOKUMENTIERTEN Aufrufform (--threshold 4 + 5 Zeugenschluessel; der erste Versuch liess sie weg und mass damit etwas anderes): jeder Schluessel reproduziert mit gleichem Wert, GENAU EINER kommt hinzu (`expected_origin`). Als Notiz eingetragen; die fuenf eingefrorenen Dateien sind unberuehrt — die Fixture ist Bezugspunkt mehrerer Akten, und eine Fixture, die mit dem Code wandert, kann dem Code nicht widersprechen. Gegengeprueft: pre_tag_audit_gate rc=1 (bleibt rot, kein Verdikt), kein neues Markerwort in den Akten-Dateien, doc_link_check PASS 91/0, claims_hygiene PASS 49/0, die 42 Fixture-Tests gruen. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Eine Falsifikations-Linse auf GENAU den Delta seit 48a0215 an release.yml. Alle vier Funde treffen Code, den ich in derselben Runde geschrieben habe. B1 — DER WAISE-MELDER WAR BLIND FUER SEINE EIGENEN FAELLE. `orphan-draft-notice` hing nur an `publish-pypi`. Die Fehlerzweige, die der neue fail-closed-Riegel in `publish-release` SELBST erzeugt — fehlende Release-ID, leere oder unlesbare Antwort, gh-Fehler, gescheiterter PATCH — enden mit liegengebliebenem Entwurf und feuerten NICHTS. Gemessen ueber alle 16 Kombinationen der Job-Ergebnisse: bei pypi=success und publish-release=failure meldete niemand. Genau der Zustand, gegen den der Job gebaut wurde. Der Riegel wurde strenger, sein Melder nicht mitgezogen. jetzt: needs + Bedingung um publish-release erweitert. B2 — DIE ABHILFE EMPFAHL DEN WEG, DEN DIE DATEI ELF ZEILEN FRUEHER VERWIRFT. `gh release delete $TAG --yes` loest ueber dieselbe FetchRelease-Wettlauf- Funktion auf, deren Unbestimmtheit die Begruendung fuer den ganzen ID-Umbau ist — und zwar mit Loeschwirkung. Im Doppel-Release-Fall, den die Datei selbst als real bezeichnet, haette es das VEROEFFENTLICHTE Release treffen koennen. Die eindeutige ID lag daneben und wurde nicht benutzt. jetzt: gh api -X DELETE repos/…/releases/$REL_ID. B3 — MEIN defaults-KOMMENTAR LEHRTE MEHR, ALS DIE MESSUNG HERGAB. `pipefail` rettet NICHT `echo "x=$(a | b)"` — der Rueckgabewert ist der von `echo`, die Pipeline steckt als ARGUMENT in einer Substitution. Nachgemessen unter der echten GitHub-Shell: die beiden Digest-Zeilen endeten weiter mit rc=0 und schrieben einen LEEREN Wert; gefangen wurde das nur durch die tee-Zeile danach, also durch Reihenfolge statt Absicherung. jetzt: Zuweisung + Leerpruefung + Zaehlpruefung. Die Leerpruefung ist der eigentliche Riegel — ein leerer Digest waere in publish-pypi gegen einen ebenfalls leeren Erwartungswert TRIVIAL WAHR durchgelaufen. Die Zaehlpruefung schliesst den Nachbarn (zwei Wheels -> mehrzeiliger Output ohne Delimiter). VERHALTENSPROBE unter `bash --noprofile --norc -eo pipefail`, vier Lagen: kein Wheel/kein sdist rc=1 · nur sdist rc=1 · Gutfall rc=0 · zwei Wheels rc=1 GEGENPROBE mit der alten Fassung bei "nur sdist": rc=0, geschrieben `wheel=` (leer). Der Defekt ist von Hand reproduziert, nicht uebernommen. Der Kommentar sagt jetzt, was der Block NICHT deckt. B4 — RUECKFALL IN DIE RISKANTE RICHTUNG. `${MAKE_LATEST:-true}` hiess bei leerem Wert "als Latest setzen" — fuer ein Prerelease genau der Fehler vom 2026-07-05, gegen den relmeta gebaut wurde. Der Nachbar REL_ID bricht bei leer ab; zwei leere Werte duerfen nicht gegenlaeufig behandelt werden. jetzt: leer -> BLOCK. Gegengeprueft: YAML parst, 4/4 Output-Referenzen deklariert, 3/3 `needs.<job>.result` stehen in den needs desselben Jobs (eine undeklarierte waere im Lauf ein leerer String, kein Fehler — also ein stiller Ausfall). Was die Linse ausdruecklich NICHT widerlegen konnte, mit Messung: der defaults-Block wirkt (github/docs workflow-syntax), `action-gh-release` setzt `id` unbedingt auf dem Erfolgspfad (run.ts:99, Pin dereferenziert auf v3.0.2), `-F draft=false -f make_latest=…` kodiert gegen einen lokalen Capture-Server korrekt, die Drei-Zustands-Matrix ist in 9 von 9 Zweigen fail-closed, die Waise-Meldung feuert in 16 von 16 Zellen nie falsch, und `sha256sum -c` akzeptiert die neue SHA256SUMS-Form (Gegenprobe: die alte rc=1). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… massen nichts Sechs Falsifikations-Linsen (E1-E6) auf `c9e94b3`, jede auf einen anderen Gegenstand. E1 ist getrennt gelandet (ec2a573). Was hier steht, ist der Rest — und der schwerste Teil trifft die Waechter, die ich in DERSELBEN Runde gebaut habe, um andere Luecken zu schliessen. ## Zwei Waechter massen nichts, beide von zwei Linsen unabhaengig gefunden DER SIGNATUR-VERFAELSCHER TRAF DIE FALSCHE ZEILE. `_mit_verfaelschter_signatur` nahm per `reversed()` die LETZTE Zeile mit `— <origin> ` und nannte sie im Docstring "die Signaturzeile des LOGS selbst (nicht der Zeugen)". Gemessen gibt es ZWEI: Index 18 (Laenge 120, die Notensignatur) und Index 28 (Laenge 3272, eine Zeugen-Cosignatur unter demselben Namen). Er traf die zweite; ihre Verfaelschung laesst `log_ok` auf True. ENTSCHEIDPROBE einer Linse: mit einer BYTE-IDENTISCHEN Kopie lief der Test ebenfalls gruen — er war gruen, egal ob verfaelscht wurde oder nicht. jetzt: Auswahl nach GEMESSENER WIRKUNG statt nach Position oder Laenge. Der Helfer probiert jede Kandidatenzeile und liefert nur eine Kopie, bei der `log_ok` wirklich faellt; sonst None -> SKIP. Nachgemessen: er trifft jetzt Zeile 18 (log_ok=False, inclusion_ok=True), und eine byte-identische Kopie kaeme nicht mehr durch. Dazu ein tempdir-Leck geschlossen (je Versuch). DER AST-WAECHTER WAR MIT DREI UMFORMUNGEN UMGEHBAR, und an zwei Stellen ist er die einzige Deckung. Gemessen, je mit rohem ESC in stdout und GRUENEM Test: (a) `_safe_line = lambda s: s` lokal davor — der Name stimmt, die Funktion nicht (b) `{str(res['detail'])}{_safe_line('')}` — Alibi-Aufruf auf einer Konstante (c) ein UNBETEILIGTER f-String uebernimmt die Deckung, weil die Marke ein WORT war und keine Stelle jetzt: Bindung an die STELLE. Die Einsetzung direkt hinter der Beschriftung MUSS selbst der `_safe_line`-Aufruf sein, sein Argument darf keine Konstante sein, und der Name `_safe_line` darf nirgends neu gebunden werden. PROBE gegen genau die drei Umformungen: alle drei jetzt ROT, unveraendert gruen. ## Eine Behauptung war zu weit, eine Korrektur ueberschoss DIE DREI URSACHEN. Der Befund sagte pauschal, mit gesetztem Flag seien alle drei byte-identisch. Gemessen gilt das NUR, wenn in allen drei Laeufen derselbe FREMDE Origin gepinnt wird — dann ist `log_ok` schon aus dem Origin-Grund False. Pinnt der Pruefer den Origin, dem er traut (der dokumentierte Gebrauch), ist der fremde Origin sehr wohl maschinell lesbar; ununterscheidbar bleiben falscher log-vkey und verfaelschte Signatur. Zwei-zu-eins, nicht drei-zu-eins. Erste Fassung: "das Feld trennt alle drei" — falsch. Zweite Fassung: "es trennt keine" — ebenfalls falsch, in die andere Richtung. Beide auf einer Konstruktion gemessen und ueber eine andere berichtet. jetzt: zwei Waechter, einer je Haelfte. Der eine haelt den ununterscheidbaren Teil fest (und wird rot, wenn die Luecke je zugeht), der andere den lesbaren. ## Vier weitere Luecken, jede mit Ruecknahme-Probe geschlossen NFC/NFD (kanonische Normalform). Der Vollbreiten-Kandidat faengt NFKC, aber NICHT NFC — gemessen `NFC(vollbreite) != Origin`. Ein Meta-Test hatte die NFC-Lockerung als von KEINEM der 2088 Tests gefangen gemessen. Der Fixture-Origin ist ASCII und kann keinen NFC-Kandidaten hergeben, also baut die neue Klasse ihren EIGENEN Checkpoint mit der ausgelieferten API und einem Wegwerf-Schluessel (keine zweite Fixture, die eingefrorene bleibt unberuehrt). PROBE: mit gepflanzter NFC-Normalisierung 2 rot, ohne 22 gruen. DER LEERSTRING IM TEXTPFAD. `cli.py:988` von `is not None` auf falsy gelockert liess ALLE 2086 Tests gruen: der Mutant aendert weder rc noch JSON, er verschweigt nur, dass eine Erwartung GESETZT und verletzt wurde. Ein Pruefer mit leerer Variable saehe ein nacktes `[FAIL]` und suchte den Fehler beim Artefakt. PROBE: mit L9 gepflanzt genau der neue Test rot, Basis 24 gruen. DIE BIBLIOTHEKS-EBENE war ungedeckt. `test_expected_origin_is_enforced` prueft nur einen VOELLIG FREMDEN Wert — und gegen einen fremden Wert verhaelt sich jede Lockerung wie ein exakter Vergleich. Wer `verify_tlog_proof` direkt aufruft (die dokumentierte oeffentliche API), war nur gegen Totalentfernung geschuetzt; die ganze Haertung lebte auf dem CLI-Pfad. Sieben Beinahe-Treffer ergaenzt, dieselben Formen wie im CLI-Korpus — eine Eigenschaft, zwei Ebenen. DER TEST, DESSEN NAME EINE EIGENSCHAFT TRAEGT, DIE ER NICHT PRUEFT. `test_ohne_flag_ist_das_feld_None_nicht_leerstring` pruefte nur das JSON-Echo und blieb bei der absent-vs-leer-Pflanzung gruen. Jetzt steht die Eigenschaft dort, wo ihr Name steht. ## Drei Mutations-Operatoren, damit das Korpus nicht still schrumpfen kann Fuenf Klassen hingen an EINER Testdatei; faellt sie weg, meldet das Tor gruen statt SURVIVED. Fuer NFC, absent-vs-leer und die `_safe_line`-Umwicklung gab es keinen Operator. 84 -> 87 Operatoren. GEGENPROBE: alle 87 treffen ihr Muster GENAU EINMAL (nicht 0 = stale, nicht 2 = mutiert mehr als gemeint). TOETUNGS-PROBE selbst gefahren, nicht uebernommen: NFC 0->2 rot, absent-vs-leer 0->1, Steuerzeichen 0->2. Ruecknahme je vollstaendig. ## Zahlen in der Akte, alle einzeln nachgerechnet und korrigiert "21 Dateien / 15 / 5" -> stimmt bei 039ac5d; der Commit, der sie EINFUEGT (c9e94b3), macht 22/16/6. Eine Messung mit `HEAD` im Befehlstext ist selbstwidersprechend: das Aufschreiben bewegt HEAD. Jetzt beide Paare mit ihrem Pin. "diffing all 666 members" -> gemessen 743 Mitglieder / 648 Dateien. 666 reproduziert in keiner Zaehlweise. "six lines above" -> 29 Zeilen, andere Tabelle. RELEASE.md-Rezept -> die von mir ergaenzte Zeile funktionierte mit dem Befehl darueber NICHT: `pip download` legt nur das Rad ab, SHA256SUMS listet beide. `sha256sum -c` meldete "No such file or directory" — woertlich das Symptom, das derselbe Absatz als behoben fuehrt. GEMESSEN, welche Form traegt: `--ignore-missing`, und BEIDE Gegenproben halten (falscher Digest rc=1, leeres Verzeichnis rc=1 "no file was verified"). ## Zwei Befunde vorgelegt statt still gefixt (beide auf main) FINDING_erwartungsvergleich_klasse.md — die Klasse, die dieses Release an EINER Instanz geschlossen hat, hat SECHS weitere Mitglieder: kbjwt.py:211 (audience, RFC 9901 Replay), :214 (nonce), statuslist.py:162, evalclaim.py:334, intoto.py:302, policy.py:970. Jeder einzeln auf startswith gelockert, volle Suite je Pflanzung: alle sechs blieben auf Baseline. Ausfuehrbar belegt fuer die ersten zwei. Die Wurzel ist woertlich dieselbe wie die gefixte: test_kbjwt.py:93 prueft gegen einen VOELLIG FREMDEN Wert. FINDING_nachbarflaeche_ohne_origin_bindung.md — `verify --trusted-checkpoint` nimmt einen Checkpoint als authentifizierte Quelle fuer root UND tree_size, von JEDEM Log, und es gibt KEIN Flag, das den Origin bindet. Ausgeloest, nicht erschlossen: dieselbe root, derselbe Schluessel, zwei Origins -> byte-gleiches Verdikt, beide ROOT-AUTHENTICITY PASS. Das ist genau die Bedrohung, die dieses Release auf `verify-proof` schliesst. Der schwerste offene Punkt der Runde. Beide sind Altbefunde ausserhalb des Release-Deltas (`git diff v3.7.0..HEAD -- src/` ist cli.py und __init__.py) und werden nach der stehenden Regel GEMELDET. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…eine zaehlt
Pflicht-Gegenlesung des Waechter-Deltas. Vier Befunde zurueck, drei davon habe
ich gemessen statt geglaubt und dabei widerlegt; einer traegt und ist behoben.
## Der Befund, der traegt: ein SKIP verdeckt, dass nichts gemessen wurde
`test_falscher_schluessel_und_verfaelschte_signatur_bleiben_ununterscheidbar`
rief `self.skipTest(...)`, wenn keine Signaturzeile der Fixture `log_ok` kippt.
Die Klasse ist bereits `skipUnless(_PROOF.is_file())` — ist die Fixture also DA
und wirkt trotzdem keine Zeile, hat sich ihr FORMAT geaendert und der Test misst
nichts mehr. Ein SKIP ist dort die stille Variante von "nichts gefunden = alles
gut" und geht in einer Suite mit 2097 Tests unter.
jetzt DREI Zustaende: Fixture fehlt -> SKIP (die Klasse) · Fixture da, keine
Zeile wirkt -> FAIL · Zeile wirkt -> messen.
GEMESSEN in einer Wegwerfkopie mit unpassend gemachtem Praefix:
vorher wie nachher unveraendert 1 passed · mit dem Fix 1 FAILED.
## Drei Befunde, von der Messung widerlegt
ALIAS UMGEHT DEN AST-WAECHTER — nein, umgekehrt. Der Waechter verlangt den
Namen `_safe_line` an der Stelle hinter der Beschriftung; ein Alias laesst genau
das FEHLEN, also wird `fehlend` nicht leer und der Test ROT. Er faellt
fail-closed, das ist Bauart, nicht Zufall. Drei Formen einzeln gefahren:
safe = _safe_line (Assign) -> ROT
from ._sl import _safe_line as sl -> ROT
str.format statt f-String, Umwicklung weg -> ROT
Kontrolle unveraendert gruen.
NFC-OPERATOR NICHT TOEDLICH BEI ASCII-ONLY — beruht darauf, dass ich die
Testklasse aus der Vorlage gekuerzt hatte. `KanonischeNormalformZaehltAlsUnter-
schied` baut einen EIGENEN Checkpoint mit nicht-ASCII-Origin. Toetungs-Probe
ueber die echte Mechanik: 0 -> 2 rot.
ABSENT-COLLAPSES-OPERATOR NICHT TOEDLICH — dieselbe Toetungs-Probe: 0 -> 1 rot.
Der Einwand ist gut begruendet (falsy trifft auch 0 und False), aber die Frage
war, ob der Operator TOETET, und das ist gemessen.
Die Lehre aus der Runde ist nicht "die Gegenlesung lag daneben" — sie hat den
einen Befund gefunden, den fuenf Linsen davor uebersehen hatten. Sie ist, dass
ein gekuerzter Pruefgegenstand Fehlurteile erzeugt: zwei der drei widerlegten
Befunde folgen direkt aus dem, was ich weggelassen habe.
Gemessen, Abschluss: 1 failed / 2097 passed / 9 skipped / 50 subtests · ruff
clean · doc_link 91/0 · claims 49/0 · check_version OK · pre_tag_audit rc=1
(muss rot sein, es gibt kein Verdikt).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Das Pre-Tag-Tor ist mit --strict der ERSTE Schritt des Release-Workflows und
damit der letzte blockierende Riegel zwischen Tag und PyPI. Eine Linse hat
gemessen, dass es mit dem, wonach es benannt ist, in KEINER Richtung
korreliert. Damit haengt die Frage "kann dieses Release ehrlich geschnitten
werden" daran, ob das Tor durch eine wahre Aussage und NUR durch eine wahre
Aussage erfuellbar ist.
BEIDE Fassungen nebeneinander geladen, gegen denselben Wegwerf-Baum, ein
Record je Vektor. GEGENPROBE ZUERST, weil ein Tor, das immer MISSING sagt, auf
der Falsch-Gruen-Achse perfekt aussaehe: bei leerem Record melden BEIDE MISSING.
A zwoelf Saetze, die einen NICHT gelaufenen Audit behaupten
(mehrere Sprachen; Kommentar, Code-Zaun, Front-Matter, Durchstreichung,
Frage, URL) heute 0/12 richtig · #139 12/12
B vier ehrliche Attestierungen in unserer Hausform
heute 0/4 · #139 0/4
C die kanonische Attestierungszeile beide richtig
D dieselbe Zeile mit der Version eines FRUEHEREN Release
heute akzeptiert (falsch) · #139 abgelehnt
KLASSE A ist der Release-Blocker, und #139 schliesst ihn vollstaendig. Die
heutige Marker/Negations-Paarung ist eine AUFZAEHLUNG: sie listet, wie ein Satz
etwas verneinen kann, und jede nicht gelistete Form liest sich als Behauptung.
Zwoelf von zwoelf kamen durch. #139 dreht die Polaritaet — EINE geschlossene
Vollzeilen-Form, und in einer geschlossenen Form kann keine Verneinung wohnen,
also muss kein Vokabular aufgezaehlt werden.
KLASSE D hatte niemand gemeldet. Bis #139 war der versionsbenannte Ordner der
einzige Anker, also attestierte ein aus einem frueheren Release kopierter Record
das neue, indem er im richtigen Verzeichnis lag.
KLASSE B sieht unveraendert aus und ist KEIN Defekt: in #139 ist Prosa
ausdruecklich praesentational und kann das Verdikt in keiner Richtung bewegen.
Die Kategorie loest sich auf, statt behoben zu werden. Unter dem heutigen Tor
werden dieselben vier Saetze aus dem GEGENTEILIGEN Grund abgelehnt — jeder
enthaelt ein Wort, das die Negationsliste als Verneinung liest. Ein
wahrheitsgemaesser Record in unserem eigenen Stil wird abgelehnt, waehrend
zwoelf unwahre durchkommen.
WAS DAS BELEGT: die bereits gewaehlte Merge-Reihenfolge ist tragend, nicht
kosmetisch. Ohne #139 heisst "das Tor erfuellen", einen Satz zu schreiben, der
ein Markerwort traegt und eine Liste anderer vermeidet — also um eine Pruefung
herumzuschreiben statt eine Tatsache zu attestieren.
WAS ES NICHT BELEGT: dass ein Audit gelaufen ist. #139 nennt seine eigene Grenze
klar — die kanonische Zeile ist provenance-FOERMIG, nicht Provenance.
Die Datei traegt bewusst KEIN Markerwort: die Vektoren wuerden das heutige Tor
kippen, und sie im Release-Record auszuschreiben hiesse, den berichteten Defekt
darin zu reproduzieren. Gegengeprueft: 0 Marker, Tor weiter rc=1, doc_link 91/0,
claims 49/0.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…e fand sofort drei mehr
Eine Gegenlesung meldete, dass "drei Werte kommen aus einer Datei, die der
Pruefer nicht geschrieben hat" sich als VOLLSTAENDIG liest und es nicht ist. Der
Einwand traf, und die richtige Antwort war nicht, eine vierte Stelle zu
umwickeln, sondern den Waechter von der Aufzaehlung auf die Regel umzustellen.
## Der Waechter prueft jetzt die Eigenschaft, nicht die Marken
VORHER: vier Marken (`log-signature`, `expected`, `sample-opening`,
`enclave-attestation`) mussten je durch `_safe_line` gehen. Eine Aufzaehlung
faengt nicht, woran niemand gedacht hat.
JETZT zusaetzlich die UMKEHRUNG: JEDE f-String-Einsetzung, die ein `detail`-
oder `origin`-Feld liest, geht durch `_safe_line` ODER traegt `!r` — ausser den
NAMENTLICH und mit Grund ausgenommenen. Eine neue Zeile bindet automatisch.
`!r` steht dort als GLEICHWERTIGE Verteidigung, nicht als Ausnahme: die
Eigenschaft ist "der Wert kann keine Zeile faelschen", und `repr()` leistet das.
GEMESSEN: ESC -> `\x1b`, Zeilenumbruch -> `\n`, ZWSP -> ``, kein rohes
Steuerzeichen bleibt. Wer hier nur `_safe_line` zaehlte, maesse den Mechanismus
statt die Sache — derselbe Fehler, den dieser Waechter schon einmal gemacht hat.
## Die Umkehrung fand SOFORT drei weitere Stellen
Beim ersten Lauf rot, alle drei beschriftete stdout-Zeilen mit einem Wert, den
der Aussteller eines Beweises frei waehlt:
cli.py:1351 [anchor verify-pack] … — {out['detail']}
cli.py:1251 [anchor upgrade] NOT UPGRADED (…) — {msg['detail']}
cli.py:844 recomputed root (not computable: {roots['detail']})
Die Quellen sind nachgemessen exception-abgeleitet: anchors_ots.py:87,
anchors_chia.py:163, anchors_rfc3161.py:81 bauen `detail` aus `{exc}`, und
bundle.py:748 gibt `str(exc)` zurueck. Alle drei umwickelt.
Zwei weitere Treffer der Umkehrung sind KEINE Defekte und stehen jetzt mit
ihrem Grund da statt durch Auslassung: `checkpoint origin {…!r}` (andere,
gemessene Verteidigung) und `prereg`/`evalcard` (Literale, nachgemessen in
prereg.py:78-94 und evalcard.py:78-96). Die `ERROR:`-Zeilen gehen nach stderr.
## Ruecknahme-Probe fuer den KLASSEN-Fix selbst
Kontrolle unveraendert GRUEN
anchor verify-pack entwickelt (auf KEINER Liste) ROT
eine voellig NEUE beschriftete Zeile mit rohem detail ROT
Der zweite Fall ist der Punkt: eine Zeile, die es beim Schreiben des Waechters
nicht gab, bindet trotzdem. Das ist der Unterschied zwischen Instanz und Klasse,
und er ist hier gemessen statt behauptet.
Gemessen, Abschluss: 1 failed / 2097 passed / 9 skipped / 50 subtests · ruff
clean · doc_link 91/0 · claims 49/0 · check_version OK · pre_tag_audit rc=1
(muss rot sein) · 87 Mutations-Operatoren, alle mit genau einem Treffer.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…— und eine Zahl, die einen Fall auslaesst Eine Gegenlesung monierte, dass die Delta-Liste des Release-Abschnitts bei `src/` aufhoert. `MANIFEST.in` graftet aber `scripts` und `docs/readiness_pack` in den sdist — beides erreicht also jeden, der aus der Quelle installiert. DER SELBSTBELEG DES READINESS-PACKS WURDE NEU SIGNIERT (9b8a998). `readiness_pack.pub.b64` wechselt von aQDV4Vkc… auf GB+LMY2k…, mit neuer Signatur und neuer Wurzel. In einem Release-Diff sieht das aus wie eine Schluesselrotation und ist keine: der Beleg ist advisory und wird bei jeder Neuerzeugung mit einem EPHEMEREN Schluessel signiert. Das steht in `scripts/readiness_pack_manifest.py` — und nirgends dort, wo ein Leser nachschauen wuerde. Jetzt im CHANGELOG, damit der naechste Blick keinen Fehlalarm ausloest. `scripts/mutation_check.py` BRAUCHT JETZT EIN GIT-ARBEITSVERZEICHNIS. Es ruft `git ls-files` und bricht mit einer Meldung ab, wenn das scheitert (:497-500, aus e34e05e); die 3.7.0-Fassung kannte diese Abhaengigkeit nicht. Aus einem entpackten sdist gibt es kein Repo, die ausgelieferte Kopie ist dort also nicht lauffaehig. EHRLICHE GRENZE, im CHANGELOG mitgeschrieben: das ist aus dem Quelltext und aus MANIFEST.in gelesen, NICHT Ende-zu-Ende reproduziert — ein Lauf startet den echten Mehrstunden-Job. (Mein erster Versuch tat genau das und wurde gestoppt.) Drei weitere ausgelieferte Dateien sind neu dabei: mutant_signature_guard.py, install_git_hooks.sh, git-hooks/pre-commit. UND EINE ZAHL IN DER PRAEREGISTRIERUNG: Abschnitt 11 sagt "VIER Verstoesse" der alten Bindemenge gegen ihre eigene Regel. Es sind FUENF — `docs/adr/renewal_policy.example.json` wird von MANIFEST.in:27 ausgeliefert und faellt unter das pauschale `docs/`-Ausschliessen, nach genau demselben Argument wie `docs/readiness_pack/`. Die Ausschlussliste des Abschnitts nimmt den Pfad BEREITS aus; falsch war nur die Zaehlung darueber. Dieselbe Klasse, eine Ebene kleiner: eine Aufzaehlung, die ihre eigene Regel richtig anwendet und beim Nachzaehlen einen Fall auslaesst. Gegengeprueft: 0 Markerworte in den Akten-Dateien, pre_tag_audit rc=1, doc_link 91/0, claims 49/0, check_version OK, die Waechter-Umkehrung 24 gruen. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…in Feld sagt warum
Selbst nachgemessen (nicht von einer Linse uebernommen), gegen die eingefrorene
Fixture, mit lebender Gegenprobe am Ende:
Default (--threshold weggelassen, keine Zeugenschluessel)
ok=true witnesses_ok=TRUE bestaetigende Zeugen=0 exit 0
ein Zeugenschluessel ok=true witnesses_ok=true bestaetigend=1
--threshold 4, fuenf Schluessel ok=true witnesses_ok=true bestaetigend=5
--threshold 9 (unerfuellbar) ok=FALSE witnesses_ok=FALSE exit 1 <- die Kontrolle kippt
`threshold` kommt im --json GAR NICHT vor. Die neun Schluessel sind ok, log_ok,
witnesses_ok, inclusion_ok, origin, tree_size, index, witnesses, expected_origin.
FOLGE: `witness_quorum` gibt `len(bestaetigt) >= threshold` zurueck, und bei
threshold=0 ist das bedingungslos wahr. Ein Programm, das `witnesses_ok` liest,
sieht dasselbe `true` fuer "ein verlangtes Quorum wurde erreicht" und fuer "es
wurde nie eines verlangt" — und kein Feld der Ausgabe trennt die beiden. Die
Zeugen zu zaehlen hilft nicht: null bestaetigende ist unter threshold=0 ein
legitimer Zustand, und dieselbe Null unter einer verlangten Schranke haette
witnesses_ok auf false gesetzt — nur ist die Schranke da schon nicht mehr da.
DAS VERDIKT IST KORREKT. threshold=0 heisst kein Zeugen-Erfordernis, und ok=true
auf einem Beweis mit gueltiger Log-Signatur und Inklusion ist die richtige
Antwort. Was fehlt, ist nicht eine Verteidigung, sondern die LESBARKEIT der
Antwort auf dem Maschinenpfad — genau die Form der Luecke, die 3.8.0 fuer den
Origin geschlossen hat, ein Feld weiter. Der Textpfad ist besser dran: er nennt
die Schranke ("threshold T"). Wer auf --json automatisiert, bekommt weniger als
wer das Terminal liest.
NICHT IN 3.8.0 GEAENDERT: `threshold` ins JSON zu legen waere die DRITTE
Aenderung der Ausgabeform in einem Release, dessen CHANGELOG genau deswegen
schon zweimal korrigiert werden musste. verify-proof ist ausserhalb des
Release-Deltas — also ein main-Befund unter der Regel, der diese Runde folgt.
EHRLICHE GRENZE, in der Datei mitgeschrieben: ob ein Konsument wirklich in die
Irre geht, ist NICHT gemessen — das haengt daran, ob jemand auf witnesses_ok
automatisiert, ohne --threshold selbst zu setzen, und das sieht keine
Repo-Messung. Gemessen ist, dass die Ausgabe es ihm nicht erlaubt.
Gegengeprueft: 0 Markerworte, pre_tag_audit rc=1, doc_link 91/0, claims 49/0.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…er ohne Index Die 3.7.0-Akte war EINE Datei. Diese ist auf acht gewachsen, weil der Lauf immer wieder etwas ueber seine eigenen Instrumente fand. Wer das Verzeichnis oeffnet, hatte bis jetzt keinen Einstieg: keine Reihenfolge, keine Aussage darueber, was offen ist, und keine Stelle, an der steht, dass es KEIN Verdikt gibt. Der Index sagt das zuerst: das Tor ist rot und muss es sein, nichts hier attestiert einen abgeschlossenen Lauf. Danach drei Ebenen — wo man anfaengt, welche fuenf Befunde offen sind und was jeder braucht, und eine Lesereihenfolge fuer einen Pruefer. DER INDEX TRAEGT ABSICHTLICH KEIN MARKERWORT, und der Grund steht in ihm drin: das Tor auf diesem Zweig erteilt die Freigabe fuer JEDE nicht-negierte Markerzeile in JEDER .md hier. Ein Index, der das Vokabular benennt, wuerde das Release attestieren, indem er es beschreibt. Dass genau das moeglich ist, ist selbst einer der Befunde darunter. Er nennt auch die ehrliche Zusammenfassung der Runde, weil sie sonst zwischen acht Dateien verschwindet: der Pruefgegenstand hielt jeder Messung stand, die INSTRUMENTE nicht. Zwei Waechter dieser Runde massen nachweislich nichts, fuenf Zahlen in den Akten waren falsch — und die Klasse hinter fast allem hat eine Form: eine Zahl ueber eine Population gemessen und ueber eine andere berichtet. Gegengeprueft, mit Gegenprobe gegen tote Verweise: 0 Markerworte, Tor weiter rc=1, alle 8 Nachbardateien im Index genannt (keine fehlt), doc_link 91/0, claims 49/0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ndelt, im Index selbst Der Index, den ich als Einstieg geschrieben habe, sagte: "All five are on main and predate this release." GEMESSEN, je Befund ueber die Frage, ob sein Gegenstand bei v3.7.0 existiert: erwartungsvergleich (kbjwt: expected_aud != aud) v3.7.0 ja -> Altbefund nachbarflaeche (verify --trusted-checkpoint) v3.7.0 ja -> Altbefund json trennt drei ursachen (out["expected_origin"]) v3.7.0 NEIN -> DIESES Release quorum (witnesses_ok) v3.7.0 ja -> Altbefund never-raise population (_MODULES) v3.7.0 ja -> Altbefund VIER von fuenf. Das ist zum vierten Mal in dieser Sitzung dieselbe Klasse — ein Sammelbegriff, der fuer keinen der Einzelfaelle geprueft wurde — und diesmal in dem Dokument, das ein Pruefer zuerst oeffnet. DER UNTERSCHIED IST KEINE BUCHHALTUNG. Fuer die vier gilt die Regel dieses Laufs: ein main-Befund wird GEMELDET, nicht in ein Release gefaltet, das ihn nicht verursacht hat. Der dritte ist anders: `expected_origin` im JSON ist HIER neu, die zu weite Behauptung darueber wurde HIER gemacht, und ihre Korrektur gehoert damit in dieses Release und nicht in ein spaeteres. Genau so ist sie auch behandelt worden — im CHANGELOG und im Befund korrigiert, mit zwei Waechtern, die den gemessenen Stand halten. Der Index sagt das jetzt mit der Messung daneben, statt mit einem Wort, das vier Faelle richtig und einen falsch beschreibt. Gegengeprueft: 0 Markerworte, Tor weiter rc=1, doc_link 91/0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…rtefakt Selbst nachgemessen, Positivkontrolle zuerst (guter Lauf rc=0, ok=true), dann vier Fehlerursachen mit sha256 ueber die volle stdout: leere Beweisdatei rc=1 6f5177382070a08a --threshold -1 (Tippfehler des Pruefers) rc=1 6f5177382070a08a kaputter --log-vkey (Tippfehler) rc=1 6f5177382070a08a Muell-Bytes rc=2 0e35e243f9e23c02 <- trennbar DREI von vier sind byte-identisch. DIE ZAHL IST KLEINER ALS BERICHTET: die Linse, die das fand, sagte vier; nachgemessen trennt sich der Muell-Fall (eigener Exit-Code, eigenes error-Feld). Der Befund ist damit schmaler — und in einer Hinsicht schlimmer, als die Zahl nahelegt. WARUM SCHLIMMER: von den drei kollabierenden sind ZWEI Fehler in der Kommandozeile des PRUEFERS, keine Eigenschaft des geprueften Artefakts. Die Ausgabe fuer "du hast einen negativen Threshold uebergeben" und "dein Verifizierer-Schluessel ist kaputt" ist nicht von "diese Datei ist kein Beweis" zu unterscheiden. Der Pruefer liest ein Urteil ueber das Artefakt und faengt an, das Artefakt zu untersuchen, waehrend der Fehler in seiner eigenen Zeile steht. Der Textpfad macht es konkret: `--threshold -1` druckt `[FAIL] log-signature: None` — geprueft wurde nie etwas (der Threshold-Waechter feuert vor dem Parsen), aber die Zeile nennt die Log-Signatur, also genau das, was der Bediener jetzt nachsehen wird. ANDERE KLASSE ALS DER DREI-URSACHEN-BEFUND, und der Unterschied traegt: dort kollidieren drei echte VERIFIKATIONSERGEBNISSE (alle drei heissen wirklich "dieser Beweis verifiziert nicht"). Hier kollidiert NICHT MESSBAR mit GEMESSENEM NEIN — einer der beiden Zustaende ist ueberhaupt kein Urteil. Die Information existiert und geht eine Schicht vor der Ausgabe verloren: die Bibliothek liefert je Ursache ein praezises `detail`, und `cli.py` zaehlt die zu uebernehmenden Schluessel hart auf, ohne `detail`. NICHT GEAENDERT: verify-proofs Fehlerpfad liegt ausserhalb des Release-Deltas (--threshold und _tlog_failclosed existieren bei v3.7.0, nachgemessen), also ein main-Befund unter der Regel dieser Runde. INDEX MITGEZOGEN, und dabei eine dritte Fassung derselben Zahl: der Index sagte "vier von fuenf" und wurde durch den sechsten Befund still falsch. Jetzt "fuenf von sechs" — mit Gegenprobe, die die Zahl gegen die tatsaechliche Dateimenge und gegen die Altbefund-Zeilen im Codeblock rechnet (6 Dateien, 6 Tabellenzeilen, 5 Altbefunde, alles deckungsgleich). Diesmal vor der Veroeffentlichung gefangen, beim ersten Mal nicht. Gegengeprueft: 0 Markerworte in beiden Dateien, pre_tag_audit rc=1, doc_link 91/0, claims 49/0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Die Zahl "N von M Befunden sind Altbefunde" ist mir in dieser Runde ZWEIMAL
weggerutscht, beide Male im Dokument, das ein Pruefer zuerst oeffnet:
1. "all five are on main" -> gemessen VIER von fuenf
2. "FOUR of the five" -> korrekt, bis ein sechster Befund dazukam
Beide Male verwischte sie eine Zustaendigkeit: ein Altbefund wird GEMELDET, ein
Befund dieses Release wird REPARIERT. Ein Sammelbegriff, der beides zusammenzieht,
nimmt dem Leser genau die Unterscheidung.
WARUM EIN TEST UND KEIN VORSATZ. Diese Runde hat die Frage beantwortet: sieben
Faelle derselben Klasse, davon null vor dem Schreiben von Hand gefangen; die
Treffer gehen auf Automatismen. Ein Vorsatz hat hier nachweislich nicht getragen.
DIE REGEL, NICHT DER FALL: geprueft wird JEDE `audit_artifacts/<token>/00_INDEX.md`,
nicht die eine von 3.8.0. Vier Zusicherungen:
- `N of the M`: M == Zahl der FINDING-Dateien im selben Verzeichnis
- `N of the M`: N == Zahl der als Altbefund markierten Nachweis-Zeilen
- jede FINDING-Datei steht in der Tabelle (der Nachbar-Fehler: nicht die Summe
stimmt nicht, sondern ein Element fehlt — beides sieht nach Vollstaendigkeit aus)
- der Index traegt KEINE nicht-negierte Markerzeile: ein Index, der das Vokabular
des Tors benennt, attestiert das Release, indem er es beschreibt. Geprueft gegen
den ECHTEN Detektor des Tors, nicht gegen eine nachgebaute Kopie — eine zweite
Messstelle fuer dieselbe Groesse waere die naechste Drift.
Eine Akte OHNE Index ist kein Fehler (aeltere Releases hatten eine einzige Datei);
ein Index mit falscher Zahl schon. Und eine Gegenprobe des Messaufbaus ist selbst
eine Zusicherung: findet das Muster keinen einzigen Index, faellt der Test, statt
ein leeres Verzeichnis wie ein makelloses Ergebnis aussehen zu lassen.
RUECKNAHME-PROBE gegen die drei Fehler, die wirklich passiert sind:
Kontrolle unveraendert 5 passed, 4 subtests
veraltete Zahl 2 FAILED <- Gesamt- UND Teilzahl
Befund fehlt in der Tabelle 1 FAILED
Markerwort im Index 1 FAILED
nach allen Ruecknahmen 5 passed, 4 subtests (Basis exakt zurueck)
Gemessen, Abschluss: 1 failed / 2102 passed / 9 skipped / 54 subtests · ruff clean
· doc_link 91/0 · claims 49/0 · check_version OK · test_manifest 2112 gesammelt
(Boden 1750) · pre_tag_audit rc=1 (muss rot sein).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…icht nur in der Akte
GEMESSEN: `MANIFEST.in:32` prunt `audit_artifacts/` aus dem sdist. Wer 3.8.0
installiert oder die Release-Seite liest, sah die Korrekturen — und von den
sechs offenen Befunden GENAU EINEN, beilaeufig in einem Nebensatz. Die
Vollstaendigkeit der Akte nuetzt niemandem, der sie nicht bekommt.
Neuer Abschnitt `### Known limitations` mit allen sechs, jeweils in der Form,
die einen NUTZER angeht statt uns:
- ein Tippfehler des Pruefers liest sich wie ein kaputtes Artefakt
(Abhilfe: Exit-Code pruefen, eigene Argumente validieren)
- `witnesses_ok: true` heisst nicht, dass ein Quorum erreicht wurde
(Abhilfe: --threshold explizit setzen und mitschreiben)
- `verify --trusted-checkpoint` hat keine Origin-Bindung
(keine Abhilfe im Werkzeug — Provenance ausserhalb pinnen)
- der --json-Pfad trennt falschen Schluessel nicht von verfaelschter Signatur
- sechs Vergleichsflaechen ohne Beinahe-Treffer-Beleg
- die never-raise-Eigenschaft laeuft ueber eine handgepflegte Modulliste
Der Abschnitt sagt zwei Dinge ausdruecklich, weil sie sonst falsch gelesen
werden: FUENF der sechs sind aelter als 3.8.0 und werden gemeldet statt
eingefaltet — ein Release soll keine Defekte still aufsaugen, die es nicht
verursacht hat; der sechste betrifft ein Feld, das DIESES Release hinzugefuegt
hat, und ist hier korrigiert. Und KEINER macht ein Verdikt falsch: es geht
durchweg darum, was die Ausgabe einen Pruefer UNTERSCHEIDEN laesst, oder was
unser eigener Beleg bemerken wuerde, wenn man ihn entfernte.
Gegengeprueft: 6 Aufzaehlungspunkte gegen 6 Befund-Dateien (deckungsgleich),
0 Markerworte im 3.8.0-Abschnitt, pre_tag_audit weiter rc=1, doc_link 91/0,
claims 49/0, check_version OK.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…stiert Der neue Known-limitations-Abschnitt sagt ausdruecklich, dass die vollen Akten unter `audit_artifacts/` liegen und `MANIFEST.in:32` sie aus dem sdist prunt. EIN aelterer Verweis im selben Abschnitt tat das nicht: er nannte den Pfad zum Befund, als koennte ein Leser ihn oeffnen. GEMESSEN: fuenf solcher Verweise im ganzen CHANGELOG, zwei im 3.8.0-Abschnitt. Die Form ist also aelter als diese Runde — was sie nicht besser macht fuer den, der aus dem sdist liest. Der Verweis sagt jetzt, was verfuegbar ist und was nicht: die Akte lebt im Repo und ist NICHT Teil des sdist, die zwei Waechter dagegen SIND ausgeliefert (`MANIFEST.in:21 graft tests`). Das ist die nuetzlichere Auskunft — wer die Behauptung nachpruefen will, braucht die Waechter, nicht die Prosa. Gegengeprueft statt behauptet: `graft tests` steht in MANIFEST.in, die Waechter-Datei existiert, und beide genannten Testfunktionen sind darin. Dazu: 0 Markerworte im 3.8.0-Abschnitt, pre_tag_audit rc=1, doc_link 91/0, check_version OK. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…enannt
Die Pflicht-Gegenlesung hat REJECT gegeben, mit vier Befunden gegen die zwei
Waechter, die ich in dieser Runde gebaut habe. Erst die Erreichbarkeit gemessen,
dann behoben — keiner ist HEUTE realisiert, und genau deshalb sind sie das
Naechste, was durchrutscht:
K1 print() mit Konkatenation statt f-String in cli.py 0x -> latent
K2 res.get('detail') statt res['detail'] in cli.py 0x -> latent
K4 Index behauptet Teilzahl, hat 0 Markierungen -> latent
K5 zwei Zahlenaussagen, nur die erste geprueft -> latent
K1 — DER WAECHTER SIEHT NUR f-STRINGS, und das ist eine Annahme, keine
Eigenschaft. `print("log-signature: " + res['origin'])` traegt keinen JoinedStr
und ginge unbemerkt durch. Statt den Sonderfall zu behandeln wird die ANNAHME
festgehalten: keine print()-Zeile benutzt Konkatenation, %-Formatierung oder
.format(). Faellt diese Zusicherung, ist nicht sie das Problem, sondern die neue
Ausgabeform — die dann selbst eine Regel braucht.
K2 — die Regex sah nur `x['detail']`. `.get('detail')` liest denselben Wert und
ist eine Zeichenaenderung entfernt. Beide Formen jetzt.
K4 — DERSELBE STILLE SKIP, den ich in dieser Runde schon einmal repariert habe.
`if markiert == 0: continue` hiess: ein Index, der "three of the ten" behauptet
und KEINE Zeile als Altbefund ausweist, wurde gar nicht geprueft. Jetzt drei
Zustaende statt zwei — keine Aussage: nichts zu pruefen · Aussage nennt 0 und es
gibt 0: stimmt · Aussage nennt >0 und es gibt 0: FEHLER.
K5 — `.search` nahm die erste Aussage. Bei zwei Saetzen blieb der zweite
ungeprueft. Jetzt `finditer` ueber alle.
K3 (`!a` neben `!r` akzeptieren) NICHT umgesetzt, und das ist Absicht: nur `!r`
zu akzeptieren ist STRENGER. Eine Ausgabeform, die der Waechter nicht kennt,
soll auffallen, nicht durchgehen.
RUECKNAHME-PROBE, alle vier einzeln gepflanzt:
Kontrolle unveraendert 29 passed, 23 subtests
K1 Konkatenation 1 FAILED
K2 .get('detail') 1 FAILED
K4 Nachweis fehlt ganz 1 FAILED
K5 zweite Aussage falsch 2 FAILED <- Gesamt- und Teilzahl
nach allen Ruecknahmen 29 passed, 23 subtests (Basis exakt zurueck)
Diesmal war der Pruefgegenstand VOLLSTAENDIG mitgeschickt — bei der letzten
Gegenlesung hatte ich gekuerzt, und zwei von drei Befunden waren daraufhin
Fehlurteile ueber etwas, das ich nicht gezeigt hatte. Diesmal kein einziger.
Gemessen, Abschluss: 1 failed / 2102 passed / 9 skipped / 54 subtests · ruff
clean · pre_tag_audit rc=1 (muss) · doc_link 91/0 · check_version OK.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ht — er faellt beim Nutzer GEMESSEN an einem echt gebauten und entpackten sdist: `MANIFEST.in:21 graft tests` liefert diesen Test aus, `MANIFEST.in:32 prune audit_artifacts` entfernt das Verzeichnis, das er prueft. Ergebnis dort: vorher: 1 failed, 4 passed <- eine legitime Abwesenheit als Fehler nachher: 5 skipped <- ehrlich DIE SCHLECHTERE HAELFTE war nicht der rote Test, sondern die vier anderen: sie iterieren ueber `_indizes()`, das dort leer ist, und liefen still durch. Ein Nutzer haette einen roten Test gesehen und vier, die nichts gemessen haben. Deshalb SKIPpt die ganze Klasse ausserhalb eines Checkouts, statt teils zu fallen und teils leer zu bestehen. DIE UNTERSCHEIDUNG KOMMT AUS conftest, der EINEN Quelle dafuer (`running_in_repo_checkout`). Die Marker `_REPO_ONLY_MARKERS` hier ein zweites Mal selbst zu pruefen waere eine zweite Messstelle fuer dieselbe Groesse und damit die naechste Drift; `tests/test_sdist_packaging_361` importiert conftest aus demselben Grund direkt. Faellt der Import, faellt der Test auf die BEOBACHTBARE Tatsache zurueck (liegt das Verzeichnis da?) statt auf eine Annahme. WIE ES AUFGEFALLEN IST, und das gehoert dazu: nicht durch Nachdenken, sondern durch die Frage, ob der GESICHERTE Stand fuer sich steht. Alle bisherigen Proben dieser Runde hatten meine ungestageten Dateien in die Wegwerfkopie kopiert — damit war nie gemessen, ob der Commit-Stand allein funktioniert. Er tut es (29 gruen aus `git archive`, `proofbundle.__file__` nachweislich in der Kopie), und genau dieser Lauf zeigte den sdist-Fall. ZWEITER EIGENER FEHLER IM MESSAUFBAU, im selben Zug: meine erste sdist-Probe baute aus HEAD, waehrend der Fix im Baum lag — sie meldete den Defekt als weiterhin offen. Dieselbe Klasse wie heute schon zweimal (gemessen wird der gesicherte Stand, geaendert wurde der Baum), diesmal im Messaufbau statt im Bericht. Die Probe spielt den Arbeitsstand jetzt ein und sagt das in einem Kommentar. Gemessen, Abschluss: im Checkout 5 passed / 4 subtests · aus dem sdist 5 skipped · volle Suite 1 failed / 2102 passed / 9 skipped / 54 subtests · ruff clean · pre_tag_audit rc=1 (muss rot sein). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…benpfad schon Ein Gate-Meta-Test hat die Asymmetrie gemessen: `verify_ecdsa_p256` (ES256) trug einen Fail-open-Operator, `verify_ed25519` nicht — obwohl Ed25519 der HAUPTPFAD ist. Derselbe eingepflanzte Fail-open liess dort 70 Tests quer durch dsse/checkpoint/decision/conformance rot werden. Die Testebene war also stark, und der Waechter DARUEBER war fuer genau diesen Pfad blind: schrumpfte das Korpus je, meldete das Mutations-Tor still gruen statt SURVIVED. Ein Operator ist die Anti-Goodhart-Ebene. Er prueft nicht den Code, sondern ob die Pruefung des Codes noch da ist. 84 -> 87 (drei Origin-Operatoren) -> 88 mit diesem. GEGENPROBE: alle 88 treffen ihr Muster GENAU EINMAL (nicht 0 = stale, was das Tor melden wuerde, nicht 2 = mutiert mehr als gemeint). TOETUNGS-PROBE selbst gefahren, nicht uebernommen: 0 -> 3 rot, Ruecknahme zurueck auf 0, src/ im echten Baum unberuehrt. Die Probe meldete ausserdem, dass eine der drei genannten Zieldateien (test_dsse.py) gar nicht existiert — sie fuhr zwei statt drei und sagte es, statt still eine kleinere Menge zu messen. WIE ES HIERHER KAM: durch eine Pruefung meiner eigenen Behauptung. Ich hatte geschrieben, alles ohne Owner-Entscheidung Abarbeitbare sei abgearbeitet — ein Riegel hat das als unbelegte Zustandsbehauptung markiert, und beim Nachgehen der Linsen-Befunde stand dieser noch offen. Gemessen, Abschluss: 1 failed / 2102 passed / 9 skipped / 54 subtests · ruff clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Eine Gegenlesung hatte benannt, dass meine zwei Verhaltenstests je GENAU EIN Zeichen fuettern (ESC bzw. LF). Die Funktion `_safe_line` ist per Konstruktion eine Whitelist (`isprintable()`) und deckt alle Klassen ab — gedeckt war also die FUNKTION, nicht die AUFRUFSTELLE. Der Unterschied ist gemessen, nicht behauptet: Kontrolle unveraendert 1 passed, 12 subtests (a) Umwicklung ganz entfernt 12 FAILED <- alle zwoelf (b) Blockliste, die CR vergisst 10 FAILED, 2 subtests passed ALTE Ein-Zeichen-Fassung gegen (b) 1 passed <- sieht NICHTS Die alte Fassung haette eine Haertung durchgelassen, die zehn von zwoelf Klassen passieren laesst: ESC und LF weiter blockiert, alles andere offen. Genau der Fall, den ein Waechter fangen soll, der auf einen Fix folgt. EIN EIGENER DENKFEHLER AUF DEM WEG, und er gehoert in die Nachricht: die erste Fassung der Schleife verbot das Zeichen im GANZEN stdout — und fiel prompt am Zeilenumbruch, den die Ausgabe zwischen ihren eigenen Zeilen voellig legitim traegt. Kein Defekt am Code, sondern eine falsch formulierte Eigenschaft. Sie heisst "der Wert kann seine Zeile nicht verlassen", nicht "das Zeichen kommt nirgends vor". Gemessen wird jetzt an der ZEILE, die den Wert traegt, mit drei Zusicherungen: der Wert steht auf genau EINER Zeile · die Nutzlast ist NOCH auf dieser Zeile (nicht ausgebrochen) · kein rohes Steuerzeichen darin. Dazu die Gegenprobe je Fall, dass der Wert ueberhaupt ankommt. Die zwoelf Klassen sind nicht ausgedacht: ESC (Loeschsequenz), LF (zweite Zeile), CR (Cursor an den Zeilenanfang, ueberschreibt OHNE Loeschsequenz), NUL, TAB, BS (rueckwaerts loeschen), VT, FF, DEL, CSI (die 8-Bit-Form von ESC[), ZWSP (unsichtbar, verschmilzt Namen optisch), NBSP. Gemessen, Abschluss: 1 failed / 2102 passed / 9 skipped / 62 subtests · ruff clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…chen, EIN Korpus Owner-Entscheid 2026-08-16 (Karte OA-714ae03760): "noch in 3.8.0 mitschliessen". Damit faellt die Klasse, die dieses Release bisher nur an EINER Instanz (dem Checkpoint-Origin) geschlossen hatte. DIE KLASSE: ein Pruefer pinnt eine erwartete Kennung, die Gegenseite waehlt ihr Gegenstueck — und der Beleg besteht nur aus einem VOELLIG FREMDEN Wert. Gegen einen fremden Wert verhaelt sich ein gelockerter Vergleich exakt wie ein exakter. Erst der Beinahe-Treffer trennt sie, und im Feld ist er der gefaehrliche. EIN KORPUS, NICHT SECHS: `tests/_beinahe_treffer.py`. Sechs Kopien waeren die Klasse als sechs Instanzen gewesen — genau der Fehler, der hier repariert wird — und die siebte Flaeche haette wieder keins. Die Formen sind nach der LOCKERUNG benannt, die sie sichtbar machen, nicht nach ihrem Aussehen: praefix, suffix, gross, gemischt, fuehrendes/folgendes Leer, Zeilenumbruch, Teilzeichenkette, leer, Vollbreite (NFKC) — plus wertabhaengig Schraegstrich am Ende, doppelter Strich, Prozent-Kodierung, Punkt am Hostende, NFD-Zerlegung. ZWEI AUSSORTIERUNGEN, beide gegen einen Kandidaten, der NICHTS misst und dabei aussieht, als taete er es: gleich dem Original (wuerde akzeptiert, Test faellt aus dem falschen Grund) und gleich einem frueheren Kandidaten. GEMESSEN bei `n-1`: `.upper()` und `.capitalize()` liefern beide `N-1`, weil der einzige Buchstabe vorn steht. `entfallene_formen()` weist das aus, statt es mitzuzaehlen — "zehn Kandidaten" ist bei kurzen Werten weniger, als die Liste verspricht. Die Gegenrichtung ist Teil jeder Pruefung: ohne sie waere ein IMMER-FALSCH- Vergleich ebenfalls gruen — dieselbe Falle wie ein Riegel, der alles blockt. RUECKNAHME-PROBE, jede Flaeche einzeln gepflanzt, volle Testdatei je Lauf: kbjwt aud startswith -> 2 rot · casefold -> 2 rot kbjwt nonce startswith -> 2 rot statuslist startswith -> 2 rot intoto startswith -> 2 rot evalclaim startswith -> 2 rot policy startswith -> 2 rot Kontrolle vor und nach jeder Ruecknahme exakt gleich. UND die Gegenrichtung gemessen: der bestehende ALTE Test (ein fremder Wert) bleibt bei jeder dieser Lockerungen gruen — das ist der Grund, warum die Klasse so lange offen war. policy wird ueber `evaluate_policy` DIREKT gefahren statt ueber die CLI: 14 Kandidaten waeren 14 Unterprozesse fuer eine Aussage, die eine Funktion beantwortet. Praezedenz in tests/test_lens_review_fixes_3_1_3.py. Der Test liest NUR die `policy:expected_vct`-Pruefung aus den checks, nicht das Gesamturteil — eine unabhaengige Regel duerfte es sonst kippen und der Test maesse etwas anderes; eine Zusicherung haelt fest, dass die Pruefung genau einmal vorkommt. Gemessen, Abschluss: 1 failed / 2107 passed / 9 skipped / 141 subtests (vorher 66) · ruff clean. Der eine rote ist unveraendert der Pre-Tag-Eintrag, den es ohne Verdikt nicht geben darf. OFFEN aus demselben Owner-Entscheid: die Origin-Bindung fuer `verify --trusted-checkpoint` (Karte OA-a41a514b63) — eine NEUE oeffentliche Flagge, kein Testzusatz. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…nordnung OA-a41a514b63
Der Befund lag auf `main` und war als solcher gemeldet: `verify --trusted-checkpoint` nahm eine
signierte Note als authentische Quelle fuer root UND tree_size, ohne dass ein Aufrufer sagen
konnte, VON WELCHEM Log sie kommen muss. Die Akte empfahl, das zu vertagen (neue oeffentliche
Flagge, neuer Parameter, kurz vor dem Tag). Der Owner hat anders entschieden — "noch in 3.8.0,
verzoegert den Tag deutlich" — und die Empfehlung bleibt im Befund stehen statt still ersetzt zu
werden: ueberstimmt wurde eine Abwaegung, kein Sachfehler.
BEIM SCHLIESSEN GEMESSEN, was der Befund noch nicht wusste: auch ein gepinnter SCHLUESSEL bindet
die Origin nicht. `sign_checkpoint` nimmt die origin-Zeile und den Namen im Signaturblock als
GETRENNTE Argumente, und C2SP laesst einen Signierer fuer mehrere Origins zu. Eine Note mit
origin `evil.example/other-tree`, signiert unter dem Namen `example.com/log`, verifiziert unter
dem VERTRAUTEN vkey mit ok=True — und ihre root und tree_size werden als authentischer
Baumkontext uebernommen. Auf der CLI waren die beiden Verdikte byte-gleich, beide
`checkpointAuthenticity: PASS`.
Umgesetzt:
- `--expected-origin` auf `verify`, gleicher Name und gleiche Semantik wie bei `verify-proof`,
weil es dieselbe Eigenschaft ist. Gebunden an `cp_ok` — die eine Variable, die alle Verbraucher
ohnehin lesen, also folgen treeContextAuthenticity, treeSizeExpectation und safeForAutomation
ohne zweiten Codepfad.
- `expected_origin=` auf `verify_witnessed_checkpoint`, Zeichen fuer Zeichen wie in
`tlogproof.verify_tlog_proof` geschrieben, damit aus einer Eigenschaft nicht zwei Schreibweisen
werden.
- Ein Fehlschlag nennt sich beim Namen, statt wie eine kaputte Signatur zu lesen.
- Voreinstellung ungebunden auf beiden Flaechen: es gibt keine Origin, auf die ein Pruefer
ehrlich voreinstellen koennte. Die Grenze steht deshalb normativ in SPEC.md §9 statt zur
Herleitung ueberlassen, und ein ungepinnter Lauf nennt weiterhin die beobachtete Origin —
gemessen, weil ein normatives MUSS, das die eigene Umsetzung bricht, die Ueberbehauptung waere.
ZWEI Vergleichsstellen, also laeuft das geteilte Korpus (`tests/_beinahe_treffer.py`) gegen beide:
die Bibliothek direkt, die CLI durch `main()`. Sechs Ruecknahme-Proben — jeder Vergleich auf
`startswith` gelockert, jede Bindung ganz entfernt, jedes `is None` auf einen falsy-Test
geschwaecht (was `--expected-origin ""` still von einer immer scheiternden Frage in gar keine
Frage verwandelt). Alle sechs faerben die Waechter rot, der Grundzustand kehrt exakt zurueck.
DER INDEX-WAECHTER HATTE DIESELBE KLASSE IN SICH. Er prueft, dass die Zahlen der Akte stimmen,
und band sie an EINE Formulierung (`N of the M`). Im selben Dokument standen zwei Zaehlungen in
anderer Grammatik, beide falsch: "the five findings" bei sechs Dateien, "this one is eight" bei
zehn. Gefangen hat sie ein Mensch beim Lesen, nicht der Test. Der Waechter prueft jetzt jede Zahl,
die an `findings` oder `files` haengt — und beim ersten Lauf meldete er prompt einen eigenen
Konstruktionsfehler: er las auch wahre Saetze ueber eine ANDERE Groesse ("Two of the six were
closed") als die Altbefund-Aussage. Das Hauptwort ist deshalb Pflicht: wer zaehlt, nennt WAS er
zaehlt, sonst wird NICHT geprueft statt falsch geprueft. Dazu eine Zitat-Ausnahme, damit eine Akte
ihre eigenen korrigierten Zahlen nennen darf, ohne dafuer bestraft zu werden — mit Gate-Meta-Test
und ausgeschriebener Grenze, plus vier Ruecknahme-Proben am echten Dokument.
Batterie: 2114 passed, 9 skipped, 170 subtests, ruff clean, mypy clean. Der eine rote Test
(`TestF7PreTagAudit::test_released_version_has_audit_record`) ist gemessen vor und nach dieser
Aenderung identisch rot und gehoert zur Release-Folge, nicht hierher.
Kein Push, kein PR, kein Merge, kein Tag.
…n neuen Flagge fand Die Origin-Bindung war committet (cbcde03), aber ohne die Pflicht-Gegenlesung. Nachgeholt als NORMAL 3L/3I. Beide Linsen-Funde sind echt und hier behoben; die dritte Linse fand nichts. LINSE 1 (Falsifikation, ausfuehrbarer Angriff) — EIN PIN OHNE GEGENSTAND IST KEIN PIN. `verify BUNDLE --expected-origin voellig.fremd/log` endete OHNE `--trusted-checkpoint` mit rc=0 und leerem stderr. Der Aufrufer haelt die Herkunft fuer gebunden, geprueft wurde nichts, und nichts sagt es ihm. Dieselbe Klasse, die dieses Release schliesst, eine Ebene hoeher: nicht der Vergleich war zu locker, er fand gar nicht statt. Jetzt Eingabefehler (exit 2) — dieselbe Regel, die der Code eine Zeile darueber schon fuer das Flaggenpaar anwendet. NACHBAR-SWEEP im selben Durchgang ueber jede verify-Flagge mit Wert: KEIN zweites Mitglied. --expected-root, --expected-tree-size, --aud, --nonce werden auch ohne Begleitflagge geprueft. Gemessen mit FALSCHEN Werten — die erste Fassung des Sweeps nahm `--expected-tree-size 2` gegen ein Bundle mit tree_size 2 und las das korrekte Bestehen als "ignoriert". Ein richtiger Wert kann zwischen geprueft und ignoriert nicht unterscheiden. LINSE 2 (Vertrag, Symmetrie) — DIE AUSGABE BERICHTETE DAS ERGEBNIS, NICHT DIE FRAGE. `verify-proof --json` fuehrt `expected_origin` auf oberster Ebene, `verify --json` fuehrte nichts; ein automatischer Verbraucher konnte "gepinnt und gepasst" nicht von "gar nicht gepinnt" trennen, beide liefern checkpointAuthenticity PASS. Neuer Schluessel `checkpointOriginExpectation`, bewusst in der Form des direkten Nachbarn `treeSizeExpectation` (status/expected/actual) statt als nackter Wert, weil diese Form alle drei Fragen beantwortet. Vier Zustaende, alle unterscheidbar; der tragende ist der dritte: ein vorhandener, aber UNGEPINNTER Checkpoint nennt die beobachtete Herkunft trotzdem — sonst waere ein ungepinnter Lauf nicht nachpruefbar, und genau das verlangt SPEC.md Paragraf 9 seit heute normativ. Beim Einbau eine Falle gemessen statt uebersehen: drei Namen entstanden zuerst nur INNERHALB des `if cp_supplied:`-Zweigs, waehrend der Bericht sie auch im anderen liest — im haeufigeren. Das waere ein NameError im Pfad OHNE Checkpoint gewesen. Vor der Verzweigung vorbelegt. LINSE 3 (Negativzustand inkl. absent) — kein Fund. Leerer String, nur Leerzeichen, Steuerzeichen, 4 KiB, Unicode-Ligatur: alle fallen geschlossen, kein Zeilenleck. Der Zustand `NOT_REQUESTED` ist ausdruecklich keine Freigabe und wird nicht zu PASS geschoent. Eine Luecke ist BENANNT statt still als geprueft zu gelten: das Verhalten bei abwesender origin-Zeile entscheidet verify_checkpoint vor dieser Bindung und wurde hier nicht neu gemessen. Ruecknahme-Probe fuer den Linsen-1-Fix in BEIDE Richtungen: Schranke entfernt -> rot, Schranke ueberblockt (pauschales raise) -> rot, Grundzustand kehrt exakt zurueck (26 passed). Batterie: 2116 passed, 9 skipped, 170 subtests, ruff clean, mypy clean. Der eine rote Test bleibt TestF7PreTagAudit::test_released_version_has_audit_record, vor und nach dieser Aenderung identisch. Die drei Linsen-Artefakte liegen im 2bedone-Repo in dem Linsen-Verzeichnis, das der Zeuge fuer dieses Thema liest (topic: origin-bindung-checkpoint-flaeche). Kein Push, kein PR, kein Merge, kein Tag.
…orfall, der sie ausgeloest hat DER VORFALL. Beim Schreiben des Mess-Berichts zitierte ich zwei Saetze woertlich, die das Tor faelschlich als Attestierung liest. Damit meldete die gesamte Suite 2120 passed und das Release-Tor war ERFUELLT — fuer ein Release ohne jeden Audit-Eintrag. Der Kopf genau dieser Datei warnt in seinem ersten Absatz davor; ich habe die Warnung gelesen und den Vektor drei Absaetze darunter hineingeschrieben. Aufgefallen ist es nur, weil ein die ganze Runde erwarteter roter Test ploetzlich fehlte: die makellos gruene Suite war die Anomalie, nicht der Fortschritt. Zurueckgenommen: die Vektoren sind durch ihre FORM ersetzt (einer verneint ueber Ausstehen, einer ueber Urheberschaft), die Akte erteilt wieder keine Freigabe, der Detektor schlaegt in der Gegenprobe weiter an, der Vor-Tag-Test ist korrekt wieder rot. Der Vorfall bleibt im Bericht stehen statt still repariert zu werden — ein Mess-Protokoll, das seinen eigenen Befund ausloest, ist der staerkere Beleg fuer diesen Befund als jede Prosa darueber. DIE ANTWORT IST KEIN WEITERES WORT IN DER VERNEINUNGSLISTE. Die Liste ist eine Sperrliste ueber einem offenen Alphabet (CWE-184); jede Runde findet den naechsten Satz, der nicht darauf steht. Die Antwort ist aufzuschreiben, WAS ein Tor koennen muss — so, dass die Aussage die Implementierung ueberlebt. tests/test_pre_tag_gate_eigenschaften.py prueft weder Regexe noch Datenstrukturen, sondern VERHALTEN an fuenf Wegwerf-Baeumen gegen den echten evaluate(). Damit gilt sie auch fuer die Fassung, die PR 139 gerade baut. GEMESSEN, nicht vermutet: P5 KONTROLLE leere Akte erteilt nicht haelt P4 wahrhaftiger Eintrag erteilt haelt P1 Datei UEBER den Audit erteilt nicht VERLETZT (genau der heutige Vorfall) P2 Eintrag fuer eine ANDERE Version VERLETZT (aus 3.7.0 kopiert, exit 0) P3 "nichts gelaufen" erteilt nicht VERLETZT (2 von 12 schmucklosen Verneinungen) P4 und P5 sind keine Zugabe: ein Tor, das NICHTS durchlaesst, ist keine Mauer aus Versehen, sondern eine, die beim naechsten Release umgangen wird. Wer nur die Falsch-Erteilung prueft, baut sie sich. DIE DREI VERLETZTEN TRAGEN expectedFailure, und das ist kein Wegschauen. Es ist die einzige Form, die sich SELBST korrigiert: solange die Luecke besteht, meldet unittest "expected failure" und die Suite bleibt gruen; sobald das Tor sie schliesst, meldet es UNEXPECTED SUCCESS und zwingt zum Entfernen der Markierung. Ein skip koennte das nicht — es bliebe still, wenn die Arbeit getan ist, und aus "bekannt offen" wuerde unbemerkt "unbekannt". GEMESSEN: eine Zeile in die Verneinungsliste (P3 geschlossen) macht aus 3 xfailed ein "1 failed" — laut, nicht still. Grundzustand kehrt exakt zurueck. Keine zweite Messstelle: geprueft wird gegen den ECHTEN evaluate(), nie gegen eine nachgebaute Kopie seiner Logik. Batterie: 2121 passed, 9 skipped, 3 xfailed, 170 subtests, ruff clean, mypy clean. Der eine rote Test bleibt TestF7PreTagAudit::test_released_version_has_audit_record — er ist rot, weil kein Audit-Eintrag existiert, und das ist der richtige Grund. Kein Push, kein PR, kein Merge, kein Tag.
…g statt Prosa Owner-Auftrag (Karte OA-b52551e063): "recherchiere ob diese signierpfad eigenschaft proofbundle verbessern wuerde, falls ja muessen wir diese integrieren. recherchiere heutige sota hierzu". RECHERCHE-ERGEBNIS: ja, fuer den Release-Prozess — und der Weg ist kuerzer als ich auf der Karte behauptet hatte. GEMESSEN im Repo: release.yml fuehrt BEREITS actions/attest-build-provenance mit keyless OIDC und ein Digest-Tor "published == attested". Der Signierpfad fehlt also nicht; er deckt das ARTEFAKT, nicht den AUDIT-EINTRAG. Die Luecke ist ein Workflow-Schritt, kein System. SOTA, und wo es NICHT passt: SLSA definiert die Verification Summary Attestation (https://slsa.dev/verification_summary/v1) fuer genau diese Aussageform — Pruefer, Zeit, Politik, Ergebnis PASSED/FAILED. Sie passt NICHT direkt: ihre Felder und ihre Anleitung sind ueber SLSA-STUFEN definiert, fuer Politik-Urteile ausserhalb dieses Rahmens gibt sie keine Anleitung. Ihren Typ fuer "ein adversarialer Audit lief" zu benutzen hiesse, die Autoritaet eines Standards fuer eine Aussage zu borgen, die er nicht definiert. in-toto sieht ausdruecklich EIGENE Praedikat-Typen vor, und actions/attest nimmt predicate-type + predicate entgegen und signiert ueber Sigstore. DER EINWAND, DER GEPRUEFT WERDEN MUSSTE, weil er die These des Produkts ist: proofbundle verspricht OFFLINE-Verifikation, Sigstore ist ein Transparenz-System mit Online-Anteilen. Gemessen an der Dokumentation ist das kein Widerspruch — das .sigstore.json-Buendel traegt die ganze Beweiskette inklusive signierter Zeitstempel, und die Offline-Pruefung gegen eine gepinnte Vertrauenswurzel ist dokumentiert. EHRLICHE GRENZE, und sie ist nicht klein: offline erfaehrt man NICHT, ob Schluessel seit dem Einfrieren der Wurzel widerrufen wurden; alles danach Signierte verifiziert, bis die Instanz rotiert. Eine veraltete Wurzel schwaecht still jede Pruefung. Anderes Vertrauensmodell, nicht schlechteres — und es gehoert dorthin geschrieben, wo ein Leser es trifft. ENTSCHEIDUNG: ja fuer den RELEASE-Prozess (dort ist GitHub ohnehin die Vertrauenswurzel, es baut und veroeffentlicht), nein fuer das PRODUKT (die Bibliothek behaelt ihr selbsttragendes Offline-Modell). Zwei Vertrauensdomaenen, getrennt gehalten. WAS HIER NICHT GEBAUT WIRD, und das mit Absicht: der release.yml-Schritt selbst. Diese Datei ist der Publish-Pfad des Owners und damit eine Einbahntuer. Erst der Pruefer und die Eigenschafts-Latte, dann kommt die Workflow-Aenderung als gegengelesener Diff gegen einen bereits bestehenden Pruefer statt als ungetestete Bearbeitung am Freigabepfad. Die Eigenschafts-Latte aus dem vorigen Commit braucht dafuer keine Zeile Aenderung: sie prueft Verhalten gegen den echten evaluate(), gilt also fuer jede Tor-Fassung. Eine Signatur erfuellt P1, P2 und P3 nebenbei — eine Doku-Bearbeitung erzeugt keine. Gegenprobe zum ADR selbst: er nennt das Marker-Vokabular oft und liegt deshalb in docs/adr/, das das Tor nicht scannt. GEPRUEFT statt angenommen — exit=1, die Akte erteilt weiterhin keine Freigabe. Kein Push, kein PR, kein Merge, kein Tag.
…lfte, die keine Einbahntuer ist
ADR 0008 sagt: erst der Pruefer, dann die Workflow-Aenderung als gegengelesener Diff. Das hier ist
der Pruefer. Der ausstellende Schritt fasst release.yml an — den Publish-Pfad des Owners — und wird
bewusst NICHT mitgeliefert.
DIE ANDERE ART VON BELEG. Das Tor beantwortet "lief der Audit" heute durch Textsuche in
Repo-Dateien; am 2026-08-16 hat eine Doku-Bearbeitung es erfuellt. Eine Signatur kann eine
Doku-Bearbeitung nicht erzeugen. Damit fallen P1, P2 und P3 der Eigenschafts-Latte NEBENBEI heraus,
statt eine Verneinungsliste um das naechste Wort zu erweitern (Sperrliste ueber offenem Alphabet,
CWE-184).
OFFLINE UND OHNE NEUE ABHAENGIGKEIT: eine DSSE-Huelle ueber einer in-toto-Aussage, verifiziert mit
proofbundles EIGENEN Primitiven. Kein Netz, kein Paket — und das staerkste Dogfood, das dieses
Projekt haben kann. Der oeffentliche CI-Weg (Sigstore/keyless via actions/attest) bleibt davon
getrennt; zwei Vertrauensdomaenen, ADR 0008.
DIE FORM FOLGT VSA, DER TYP NICHT. SLSAs Verification Summary Attestation traegt Pruefer, Zeit,
Politik und ein zweiwertiges Ergebnis — genau diese Aussageform. Ihre Felder sind aber ueber
SLSA-STUFEN definiert; ihren Typ zu benutzen hiesse, die Autoritaet eines Standards fuer eine
Aussage zu borgen, die er nicht definiert. Ein Test haelt das fest, weil es sonst beim naechsten
Aufraeumen wie eine Inkonsistenz aussieht.
ZWEI EIGENE DEFEKTE, VON DER EIGENEN RUECKNAHME-PROBE GEFUNDEN:
(1) TOTER ZWEIG. `predicate_type_ok` liess sich aus der Konjunktion entfernen, ohne dass EIN Test
rot wurde. Grund: die vorhandene Pruefung sah die KONSTANTE und die gebaute Aussage an, schickte
aber nie eine fremde Huelle durch verify(). Gestalt statt Verhalten — dieselbe Klasse, die
verify_eval_result_dsse als WP-I1 dokumentiert. Test nachgetragen.
(2) ZWEI FRAGEN IN EINER ANTWORT. Beim Nachtragen gemessen: verify_eval_result_dsse FALTET die
Typpruefung in sein `ok` (fremder Typ -> ok=False, obwohl die Signatur einwandfrei ist). Mein
signature_ok uebernahm dieses ok und machte damit "Signatur kaputt" von "Signatur gut, Typ
fremd" ununterscheidbar — genau der Fehlermodus, den dieses Release an zwei anderen Stellen
geschlossen hat. Jetzt wird der kryptographische Teil mit expected_predicate_type=None erfragt
und der Typ selbst verglichen; observed_predicate_type steht im Ergebnis.
RUECKNAHME-PROBE ueber alle fuenf Teilpruefungen EINZELN (ein Pruefer mit fuenf Konjunktionen kann
vier tote tragen und trotzdem gruen aussehen): jede Entfernung faerbt rot, die Lockerung der
Version auf startswith ebenfalls, der Grundzustand kehrt exakt zurueck (12 passed, 21 subtests).
Die Vergleiche auf Version UND Commit laufen gegen das geteilte Beinahe-Treffer-Korpus — zwei
Vergleichsstellen, zwei Laeufe.
Die Aussage lehnt Mehrdeutiges ab, BEVOR sie signiert wird: ein abgekuerzter Commit und ein dritter
Ergebniswert ("teilweise" wuerde als Bestehen gelesen) werfen.
Batterie: 2133 passed, 9 skipped, 3 xfailed, 191 subtests, ruff clean, mypy clean. Der eine rote
Test bleibt der Vor-Tag-Eintrag — rot, weil keiner existiert, und das ist der richtige Grund.
Kein Push, kein PR, kein Merge, kein Tag.
…erreicht beide Ausgabepfade
Vierter der sechs Befunde der 380-Akte geschlossen, unter der Owner-Vorgabe, alle Luecken in 3.8.0
zu schliessen. Sein Vertagungsgrund ("eine weitere Aenderung der Ausgabeform") ist durch die
heutigen, angekuendigten Aenderungen ohnehin ueberholt.
GEMESSEN: eine leere Beweisdatei — das Artefakt IST kein Beweis — und ein kaputter --log-vkey — der
Tippfehler des PRUEFERS — lieferten byte-gleiches JSON. Der Aufrufer liest ein Urteil ueber das
Artefakt, geht das Artefakt untersuchen, und der Fehler steht auf seiner eigenen Kommandozeile. Auf
dem Textpfad schlimmer: ein kaputter Schluessel druckte `[FAIL] log-signature: None` und benannte
damit genau das eine, was der Operator jetzt ansehen wird — waehrend gar nichts geprueft worden war.
NICHTS ERFUNDEN. Die Bibliothek trug den Grund je Fall praezise ("no empty-line separator before the
checkpoint", "vkey must have 3 '+'-separated parts"); cli.py kopierte `detail` beim Auflisten der
Schluessel schlicht nicht mit. Die Information entstand und wurde EINE Schicht vor der Ausgabe
fallengelassen. Jetzt im JSON (immer vorhanden, null auf dem gruenen Weg) und als `reason:`-Zeile im
Textpfad. Alle vier Ursachen liefern jetzt paarweise verschiedene Ausgaben, mit dem guten Lauf als
Kontrolle.
ZWEI DINGE EHRLICH STATT AUFGERAEUMT:
(1) Das `_safe_line` auf der Textzeile ist VORSORGLICH. Zwei der vier Gruende interpolieren eine
Ausnahme-Meldung, deren Formen sich nicht aufzaehlen lassen — aber drei Sonden (ESC,
Zeilenumbruch-Injektion, NUL) erzeugten KEIN Steuerzeichen im detail, weil die Parse-Fehler
bibliothekseigen sind. Ein gemessenes Leck ist das nicht. Meine erste Fassung des Kommentars
behauptete es; korrigiert, bevor sie committet wurde.
(2) Der Befund nannte DREI kollidierende Ursachen, heute sind es ZWEI: `--threshold -1` trennt sich
inzwischen, weil dieses Release `threshold` fuer einen anderen Befund ins JSON aufgenommen hat.
Das ist keine Korrektur der damaligen Zahl, sondern eine Wirkung — ausgeschrieben, damit der
Unterschied nicht spaeter als Widerspruch gelesen wird.
Ruecknahme-Probe: JSON-Schluessel entfernt -> 2 rot; Textzeile entfernt -> 1 rot; Schluessel auf
einen festen Text verdrahtet (sieht gefuellt aus) -> 2 rot. Grundzustand kehrt exakt zurueck.
Batterie: 2136 passed, 9 skipped, 3 xfailed, 191 subtests, ruff clean, mypy clean. Das Vor-Tag-Tor
verweigert weiterhin korrekt (exit=1) — geprueft, nicht angenommen, nachdem eine Doku-Bearbeitung es
heute schon einmal gekippt hat.
Akte 380: vier von sechs Befunden geschlossen, zwei offen.
Kein Push, kein PR, kein Merge, kein Tag.
…det sofort einen echten Defekt
Fuenfter der sechs Befunde der 380-Akte, Modul-Achse geschlossen.
DER BEFUND: `_MODULES` listete 36 Module, das Paket liefert 62 aus. Ein gepflanzter roher raise in
einer gelisteten Flaeche wurde gefangen, derselbe Defekt in `anchors_ots` lief GRUEN durch. Die
Eigenschaft war korrekt ueber die Menge, die sie ablief — und diese Menge war kleiner als die, fuer
die man sie las. Eine Eigenschaft, die eine KLASSE zu schliessen behauptet, muss ihre Familie zur
Laufzeit entdecken; sonst misst "gruen" die Liste, nicht die Klasse.
FIX: `_module_names()` laeuft `pkgutil.walk_packages` ueber `proofbundle.__path__`, Unterpakete
inklusive. Gemessen 36 -> 62 Module, 79 -> 91 Flaechen. Die 12 zusaetzlichen Flaechen in 8 Modulen
sind exakt die, die der Nachtrag des Befunds vorhergesagt hatte — unabhaengig bestaetigt.
DIE PROBE IST DAS EXPERIMENT DES BEFUNDS, in beide Richtungen gefahren:
Baum + raise in anchors_ots -> ROT (gefangen)
Baum + raise in anchors (gelistet) -> ROT (keine Regression)
LISTE + derselbe raise -> GRUEN (der Befund, live reproduziert)
Grundzustand -> GRUEN (kehrt exakt zurueck)
EIN LEBENDER DEFEKT FIEL SOFORT HERAUS, und es ist NICHT der, den der Befund vorhersagte:
`emit.load_signer` war nie gesweept. `open()` nimmt eine ganze Zahl als DATEIDESKRIPTOR — also
scheiterte `load_signer(123)` nicht am falschen Typ, sondern las, was zufaellig auf fd 123 offen war,
und versuchte daraus einen privaten Schluessel zu machen. Ein falsch getipptes Argument, das still
eine fremde offene Datei erreicht, ist schlechter als ein Absturz. Typisierte Schranke an der
Flaeche; der Test belegt, dass der Deskriptor NICHT GELESEN wird (er ist danach noch offen), nicht
bloss dass etwas geworfen wurde — und die Gegenrichtung (str, Path, bytes laden weiterhin) steht
daneben, weil eine Schranke, die auch den richtigen Aufruf blockt, keine Haertung ist.
ZWEI KLEINERE DINGE, BEIDE STEHENGELASSEN STATT GEGLAETTET:
(1) Das MESSGERAET hatte zwei Zustaende, wo es drei braucht. Warf eine Flaeche etwas, das auf keiner
der beiden Listen steht, stuerzte der Lauf mit einem Traceback ab statt einen Befund zu
erzeugen — gemessen an genau jenem OSError. `_FORBIDDEN` ist eine Sperrliste ueber einem offenen
Alphabet: was nicht daraufsteht, ist UNKLASSIFIZIERT, nicht erlaubt. Eigene gemeldete Kategorie.
(2) `OSError` musste in die akzeptierten Beendigungen, sobald eine pfadnehmende Flaeche in die
Familie kam ("die Datei ist nicht da" ist fuer einen Lader eine ehrliche typisierte Antwort, und
sie als Verstoss zu zaehlen macht den Waechter unglaubwuerdig — ein unglaubwuerdiger Waechter
wird abgeschaltet). Genau diese Zeile haette die fd-Gefahr WIEDER verdeckt. Deshalb ist sie an
der Flaeche geschlossen und von einem eigenen Test gehalten, nicht von der Liste. Eine
Lockerung, die eine andere Schranke oeffnet, muss ihren eigenen Waechter mitbringen.
WEITERHIN OFFEN und ausdruecklich benannt: die Vorhersage des Befunds selbst —
`anchors_rfc3161.verify_rfc3161` wirft AttributeError auf ein nicht-dict `frozen`/`rp_trust`. Das
liegt auf der KEYWORD-Achse; diese Eigenschaft fuzzt das PRIMAER-Argument, die Modul-Achse erreicht
sie also nicht. Dieselbe Form wie F2 ("der Sweep spielt nur Argumentposition 0"), eine Achse weiter.
Nicht hier gefixt, damit die Schliessung nicht breiter gelesen wird als sie ist.
Batterie: 2139 passed, 9 skipped, 3 xfailed, 194 subtests, ruff clean, mypy clean. Der eine rote
Test bleibt der fehlende Vor-Tag-Eintrag.
Akte 380: fuenf von sechs Befunden geschlossen, einer offen.
Kein Push, kein PR, kein Merge, kein Tag.
…on der verfaelschten Signatur Der SECHSTE und letzte Befund der 380-Akte — und der einzige, der diesem Release selbst gehoert. DIESE LUECKE WAR VON ANDERER ART. Ueberall sonst in dieser Runde existierte die Information und wurde eine Schicht vor der Ausgabe fallengelassen. Hier schien sie gar nicht zu existieren: ein Signaturvergleich ist ein ZWEI-EINGABEN-PRAEDIKAT und weist bei einem Fehlschlag keiner Seite die Schuld zu. Der Pruefer kann nicht wissen, ob der Schluessel falsch ist oder die Signatur. DIE KEY-ID KANN ES. Eine C2SP-Signaturzeile traegt die Key-ID des Unterzeichners, und `verify_checkpoint` machte diese Unterscheidung SCHON IMMER in seiner Schleife (`kid != kid_v` heisst "diese Zeile gehoert nicht zu deinem Schluessel") — und verdichtete sie zu einem einzigen `ok=False`. GEMESSEN, mit dem guten Lauf als Kontrolle: guter Lauf ok=True log_ok=True signer_present=True falscher Schluessel ok=False log_ok=False signer_present=False verfaelschte Signatur ok=False log_ok=False signer_present=True Die beiden Ausgaben sind nicht mehr byte-gleich. EHRLICHE GRENZE, und sie ist keine Schwaeche des Feldes: eine Verfaelschung, die genau die vier keyID-Bytes trifft, ist von einem falschen Schluessel NICHT unterscheidbar — dann traegt die Note keinen Beleg mehr, dass dieser Schluessel je signiert hat. Wahre Aussage ueber die Lage, kein Messfehler. DER WAECHTER, DER SEINE EIGENE ABLOESUNG VORHERSAGTE. `test_falscher_schluessel_und_verfaelschte_ signatur_bleiben_ununterscheidbar` pinnte die Kollision als GEMESSENEN ZUSTAND und trug die Meldung: "sind unterscheidbar geworden — gut! Dann ist der Befund geschlossen und dieser Waechter gehoert durch eine positive Zusicherung ersetzt." Er wurde rot, sobald das Feld landete, und sichert jetzt die Trennung zu statt die Kollision zu beobachten. Das ist der Unterschied zwischen einem Pin auf eine LUECKE und einem Pin auf eine EIGENSCHAFT: der erste MUSS rot werden, wenn die Arbeit getan ist, sonst haelt er einen Zustand fest, nachdem er aufgehoert hat zu gelten. Ruecknahme-Probe: Flag nie gesetzt -> 4 rot; fest auf True -> 3 rot; Feld aus dem JSON -> 5 rot; Grundzustand kehrt exakt zurueck. KNOWN LIMITATIONS NEU GESCHRIEBEN. Der Abschnitt zaehlte sechs offene Befunde auf und war damit falsch geworden. Er nennt jetzt, was NACH allen sechs noch gilt — die keyID-Grenze oben, die Keyword-Achse der never-raise-Eigenschaft (die Modul-Achse ist zu, die Argument-Achse nicht), und dass das Vor-Tag-Tor weiterhin Prosa liest. Die Vertagungsempfehlungen bleiben in den Befunden neben den Entscheidungen stehen, die sie ueberstimmt haben, statt die Geschichte auf Zustimmung umzuschreiben. Batterie: 2143 passed, 9 skipped, 3 xfailed, 194 subtests, ruff clean, mypy clean. Das Vor-Tag-Tor verweigert weiterhin korrekt (exit=1). Akte 380: alle sechs Befunde geschlossen, jeder mit Ruecknahme-Probe. Kein Push, kein PR, kein Merge, kein Tag.
…d — Befund aus der Gegenlesung
DIE GEGENLESUNG HAT EINEN ECHTEN DEFEKT GEFUNDEN, in Code, den diese Scheibe nicht angefasst hat.
`_parse_vkey` prueft Format, base64, Laenge, Typ-Byte und Hex — aber NICHT, ob die im vkey
DEKLARIERTE keyID zu seinem eigenen Schluesselmaterial passt. Fallen die beiden auseinander, lehnt
`verify_checkpoint` still jede Signaturzeile ab (`kid != kid_v or kid != kid_expected` ist dann fuer
jedes kid wahr). Der Aufrufer sieht `ok=False, signer_present=False` und kann "niemand hat signiert"
nicht von "dein Schluessel ist kaputt" unterscheiden.
Das ist EXAKT die Klasse, die dieses Release an drei anderen Stellen schliesst — nicht messbar liest
sich wie gemessenes Nein — und sie stand die ganze Zeit im Nachbarcode.
NACHBAR IM SELBEN DURCHGANG: `_parse_witness_vkey` hat dieselbe Luecke, und `verify_cosignature`
traegt Zeile fuer Zeile dieselbe Form. Zwei Mitglieder, beide gefixt, die Neuberechnung je nach
Algorithmus (cosign_key_id / cosign_key_id_mldsa).
WARUM IM PARSER: jede andere Missform des vkey faellt schon dort typisiert durch. Ein
selbstwidersprueglicher Schluessel ist ein Konfigurationsfehler, kein Verifikationsergebnis — auf
der CLI wird daraus exit 2 (malformed input) statt exit 1 (Pruefung fehlgeschlagen), und das ist
die richtigere Aussage.
ZWEI TESTS ANGEPASST, und zwar ohne ihren Zweck zu verwaessern: beide bauten ihren ML-DSA-vkey mit
der willkuerlichen keyID "+00000000+" — als Abkuerzung, nicht als Gegenstand. Die Abkuerzung ist
jetzt ungueltig. Ihr eigentlicher Gegenstand (Verhalten OHNE pq-Backend bzw. never-raise von
witness_quorum) bleibt erreichbar, weil die ID-Berechnung reines SHA-256 ist und kein Backend
braucht. Also korrekte ID statt Nullen, Zweck unveraendert.
WAS DIE GEGENLESUNG SONST ERGAB: der erste Durchgang endete REJECT mit einem Hauptbefund an
`kid_v` — das in MEINEM Auszug nicht definiert war. un hat das ausdruecklich benannt ("nicht
definiert im Snippet, ich nehme an...") statt durchzuwinken. Mit nachgereichtem Vertrag fiel der
Befund (die Logik ist korrektes fail-closed), und `signer_present` bekam ausdruecklich PASS: die
Platzierung VOR verify_ed25519 ist fuer die beabsichtigte Semantik richtig. Der Eingabefehler lag
bei mir; die Klasse dazu steht im Gedaechtnis.
AUCH IM SELBEN DURCHGANG: `verify_witnessed_checkpoint` reichte `signer_present` nicht durch,
waehrend der tlogproof-Pfad es tut. Beim Lesen des eigenen Diffs gefunden, nicht von einem Test.
Batterie: 2143 passed, 9 skipped, 3 xfailed, 194 subtests, ruff clean, mypy clean.
Kein Push, kein PR, kein Merge, kein Tag.
…llen gegen meine Zwei Dateien kollidierten, beide weil #141/#142 dieselbe Arbeit schon gelandet hatten, waehrend ich sie lokal noch einmal machte. Aufgeloest zugunsten von main, nicht aus Hoeflichkeit, sondern gemessen: 1. `_ACCEPTED`: ich hatte `OSError` aufgenommen. main nahm nur `FileNotFoundError` — und ihr Kommentar haelt fest, dass der ERSTE Versuch dort ebenfalls `OSError` war und von einer Gegenlesung als REJECT gefangen wurde. `OSError` ist die Basisklasse von `PermissionError`, `TimeoutError`, `BrokenPipeError`; sie alle stillschweigend zu akzeptieren ist ein FAIL-OPEN auf genau der Achse, die diese Eigenschaft verteidigt. Ich habe denselben Fehler ein zweites Mal gemacht, mit derselben Selbstbegruendung. Mains Fassung uebernommen. 2. `emit.load_signer`: mains Typboden ist aequivalent zu meinem und besser dokumentiert. WAS AUS MEINER ARBEIT BLEIBT, weil es main NICHT hat: - `tests/test_load_signer_fd_hazard.py` belegt, dass der Deskriptor NICHT GELESEN wird (er ist nach dem Aufruf noch offen), nicht bloss dass etwas geworfen wurde. Gegen mains load_signer gruen. - Die Unterpaket-Luecke: mains Deckungs-Waechter nutzt `_SRC.glob("*.py")`, also nur die oberste Ebene. Gemessen auf dem zusammengefuehrten Baum: 90 Flaechen in 40 Modulen, und `experimental.enclave` ist NICHT dabei — obwohl es ausgeliefert wird, dokumentierter Importpfad und CLI-Unterbefehl ist. Folgt als eigener Commit. Batterie auf dem zusammengefuehrten Baum: 2161 passed, 9 skipped, 3 xfailed, 228 subtests, ruff clean. Der eine rote Test bleibt der fehlende Vor-Tag-Eintrag.
…ine Ebene tiefer
DIE FORM DES BEFUNDS IST DIE INTERESSANTE. `test_never_raise_population_guard.py` existiert, um zu
beweisen, dass keine Flaeche ausserhalb der Population liegt. Sein eigener Wahrheitsbegriff war
`_SRC.glob("*.py")` — nur die oberste Ebene. Ein Modul in einem UNTERPAKET lag damit ausserhalb der
Sollmenge und deshalb ausserhalb des Waechters, der beweist, dass nichts ausserhalb liegt. Der
Waechter gegen eine unvollstaendige Population war selbst unvollstaendig, eine Ebene tiefer.
GEMESSEN auf dem zusammengefuehrten Baum: glob 50 Module, rglob 56. Die sechs sind die fuenf
`adapters.*` und `experimental.enclave`. Von den sechs traegt GENAU EINES eine passende Flaeche
(`experimental.enclave.verify_enclave_attestation`) — die ehrliche Groesse der Luecke ist also EIN
Mitglied, nicht sechs. Es wird ausgeliefert, hat einen dokumentierten Importpfad und ist ein eigener
CLI-Unterbefehl (`verify-enclave`).
BEWEIS IN BEIDE RICHTUNGEN: nach der Erweiterung MELDETE der Waechter die Luecke von selbst
(`{'experimental.enclave': ['verify_enclave_attestation']}`), und erst das Eintragen in `_MODULES`
machte ihn gruen. Er hat die Luecke also gesehen, nicht ich sie ihm erzaehlt. Population jetzt 91
Flaechen in 41 Modulen (vorher 90/40), null Verstoesse.
DATEISYSTEM STATT `pkgutil.walk_packages`, und die Begruendung ist gemessen statt zitiert:
walk_packages muss jedes Paket IMPORTIEREN, um `__path__` zu lesen, und zieht damit Nebenwirkungen
(die experimental-Vorschauwarnung) in einen Entdeckungsschritt, der keine haben sollte. Der
dokumentierte veraenderliche Standard-Memo (CPython #127318) REPRODUZIERT hier NICHT — drei Laeufe,
je 62 Module — und wird deshalb als Grund fuer den einfacheren Weg genannt, nicht als Fehler, den
wir hatten. `rglob` liest Namen von der Platte: keine Importe, kein Memo, keine Nebenwirkungen, und
es bleibt in der Form, die diese Datei ohnehin benutzte.
Batterie: 2161 passed, 9 skipped, 3 xfailed, 228 subtests, ruff clean.
…euert wie entworfen Owner-GO im Chat: "owner go erteilt fuer den merge sorge fuer fortschritt und wir inkludieren alles jetzt best moeglich in das v3.8.0 release". #139 auf main gemergt (518d1ee), main hier zusammengefuehrt. DIE LATTE HAT GETAN, WOFUER SIE GEBAUT WURDE. Gestern trugen P1, P2 und P3 in `test_pre_tag_gate_eigenschaften.py` ein `expectedFailure` mit der Begruendung: "sobald das Tor sie schliesst, meldet unittest UNEXPECTED SUCCESS und zwingt zum Entfernen der Markierung". Genau das ist eingetreten — mit #139s neuem Tor meldete die Suite drei Unexpected success, also drei FEHLSCHLAEGE. Markierungen entfernt, die drei Eigenschaften sind jetzt normale Zusicherungen. Der Absatz bleibt im Docstring stehen, weil er der Beleg ist, dass die Bauform funktioniert: ein `skip` haette geschwiegen, als die Arbeit getan war. CHANGELOG ZUSAMMENGEFUEHRT OHNE VERLUST. Mains `[Unreleased]`-Rubrik trug vier Stichpunkte, die meine Fassung nicht hatte (Strukturbudget auf dem direct-dict-Pfad, zurueckgenommene merkle_path-Kappe, typisierte Fehler auf zwei Pfadargumenten, plus den Banner). GEMESSEN, dass der zugehoerige Code in DIESEM Baum liegt (BundleFormatError in evalcard und prereg vorhanden, json_nodes im Budget) — er wird also mit 3.8.0 ausgeliefert und ist nicht "unveroeffentlicht". Die Rubrik-Ueberschrift entfaellt deshalb, der Wortlaut ist unveraendert uebernommen, mit sichtbarer Herkunftsangabe und die ###-Rubriken am Ende des Abschnitts, damit sie nicht mit den gleichnamigen dieses Release verwechselt werden. STAND JETZT, ehrlich: zwei rote Tests bleiben, beide aus DEMSELBEN Grund und beide richtig. `TestF7PreTagAudit::test_released_version_has_audit_record` und #139s eigener Gegentest `ProsaEntscheidetNicht::test_gegenrichtung_das_echte_repo_besteht_weiterhin` verlangen die kanonische Zeile `pre-tag-adversarial-audit: RUN | version=3.8.0` in der Akte. Sie steht dort NICHT — und sie zu schreiben waere heute eine falsche Attestierung: gelaufen ist eine 3-Linsen-Runde auf dem Origin-Bindungs-Increment (NORMAL 3L/3I), nicht der Tiefenlauf auf dem eingefrorenen Release-Digest. Der Eintrag folgt, wenn der Lauf gelaufen ist, nicht vorher. Batterie: 2232 passed, 9 skipped, 380 subtests.
…asse hatte mehr Mitglieder
DER TIEFENLAUF HAT SEIN ERSTES ZIEL GEKIPPT, und das ist der Sinn der Uebung.
`FINDING_erwartungsvergleich_klasse.md` wurde am 2026-08-16 als "7 von 7 Mitgliedern" geschlossen.
D1 fragte nicht "laufen die Korpus-Tests" (das bestaetigt nur), sondern "welche Vergleichsstellen
gibt es und welche ist ungedeckt". GEMESSEN: 14 Stellen an 10 Parametern, 8 gedeckt, DREI ueber
Zeichenketten ungedeckt. Die Klasse war ueber eine HANDGEPFLUECKTE Mitgliederliste geschlossen —
dieselbe Fehlerform wie bei der never-raise-Population einen Tag zuvor.
ZWEIMAL DIESELBE FORM IN ZWEI TAGEN heisst: das Problem ist nicht die einzelne Liste, sondern dass
eine Klasse ueberhaupt ueber eine Liste geschlossen wird. Deshalb kein drittes Handnachtragen,
sondern ein Waechter, der die Grundmenge aus dem BAUM ableitet
(`tests/test_erwartungsvergleich_population_guard.py`), und danach die drei Luecken:
outcome.verify_outcome_receipt expected_decision_ref
experimental.enclave.verify_enclave_... expected_profile
public_transparency.evaluate_public_... expected_root_b64
Jede war zuvor NUR gegen einen voellig fremden Wert geprueft — und ein fremder Wert faellt auch
unter startswith/casefold/strip durch, kann also nicht zeigen, dass der Vergleich exakt IST.
DIE AUSNAHME IST ABGELEITET, NICHT GEPFLEGT: `expected_tree_size: Optional[int]` faellt heraus, weil
seine ANNOTATION keine Zeichenkette zulaesst — ein Korpus ueber Gross/Klein und Leerzeichen hat auf
einer Zahl keinen Gegenstand. Ein Parameter ohne lesbare Annotation gilt als UNGEDECKT, nicht als
ausgenommen: im Zweifel fordern.
DREI EIGENE MESSFEHLER, alle VOR der Behauptung gefangen und im Waechter dokumentiert, damit sie
beim naechsten Umbau nicht still zurueckkommen:
1. `expected_key_id(ident)` ist ein FUNKTIONSAUFRUF, kein Erwartungsparameter — der erste AST-Lauf
zaehlte ihn mit und blies den Befund auf.
2. Das Korpus-Muster suchte einzeilig und uebersah `test_kbjwt.py` (mehrzeilige Lambda-Form) —
zwei gedeckte Parameter galten faelschlich als ungedeckt.
3. Die Zeichenklasse `[a-z_]+` schloss ZIFFERN aus und uebersah `expected_root_b64`, den einzigen
Parameter mit Ziffern im Namen. Das Muster stammte aus den Beispielen, die gerade vor mir lagen,
und die waren alle ziffernlos.
RUECKNAHME-PROBE je Fix, mit vorher im Original nachgelesenen Ankern: decision_ref auf startswith
-> 2 rot; profile auf startswith -> 1 rot -> 3 rot; root_b64 auf strip -> 1 rot -> 4 rot;
Grundzustand kehrt zurueck. (Die "1 rot" der Kontrolle ist ein Artefakt des Probenaufbaus — eine
Testdatei fehlte im Wegwerf-Baum —, die Deltas sind eindeutig.)
Und der Index-Waechter fing die elfte Akten-Datei: "ten files" wurde falsch, sobald
PRE_REGISTRATION_DEEP_380.md dazukam. Genau dafuer ist er gebaut.
Batterie: 2241 passed, 9 skipped, 413 subtests, ruff clean. Die zwei roten bleiben die fehlende
Vor-Tag-Attestierung — sie folgt, wenn der Lauf durch ist, nicht vorher.
…gt, fuenf gehalten Die kanonische Attestierung steht jetzt in audit_artifacts/380/DEEP_RUN_RECORD_380.md, und sie steht dort, WEIL der Lauf stattgefunden hat — nicht damit die Suite gruen wird. Bis eben waren zwei Tests rot, die genau diese Zeile verlangen, und sie waren aus dem richtigen Grund rot. GEGENSTAND ZUERST, wie §9 es verlangt: benoteter Code src/-Baum 203644c, eingefroren bei e0208e3. GEMESSEN, dass src/ seit dem Einfrieren UNVERAENDERT ist — nur tests/ ist gewachsen, durch die Funde des Laufs selbst. Delta v3.7.0..e0208e3 unter src/: 14 Dateien, 489+/19-. D1 WIDERLEGT, und das ist das eigentliche Ergebnis. Die Frage war nicht "laufen die Korpus-Tests" (das bestaetigt nur), sondern "welche Vergleichsstellen gibt es und welche ist ungedeckt". Gemessen: 14 Stellen an 10 Parametern, 8 gedeckt, DREI Zeichenketten-Vergleiche ungedeckt. Der Befund war als "7 von 7 Mitgliedern" geschlossen — die Mitgliederliste war handverlesen. Zweite Wiederholung derselben Form in zwei Tagen, deshalb ein aus dem Baum abgeleiteter Waechter statt eines dritten Handnachtrags. D2 haelt (11 Ursachen, 11 verschiedene Ausgaben, Kontrolle zuerst; KEIN Beweis der Abwesenheit). D3 haelt in beide Richtungen (gepflanzter raise im JUENGSTEN Mitglied experimental.enclave und im lange gedeckten statuslist, Grundzustand kehrt zurueck). D4 haelt gegen die ECHTEN Dateien (11 gepruefte Dateien, keine erteilte, Detektor lebt). D5 haelt in beide Richtungen (PermissionError wird NICHT verschluckt, FileNotFoundError schon — die eine bewusste Zulassung; meine eigene OSError-Fassung war ein fail-open und wurde im Merge durch mains verengte ersetzt). D6 haelt, NACHDEM die Sonde zweimal korrigiert wurde: vier gemeldete Widersprueche waren ihre eigenen Falschtreffer ("N files" meinte den src/-Delta, nicht die Akte), und dieselbe Annahme steckte in einem heute ausgelieferten Waechter, wo sie nur zufaellig gutging. Dabei fiel ein zweiter Defekt heraus: die Wortzahl-Liste endete bei "ten", also hoerte der Waechter genau in dem Moment auf zu pruefen, als die Akte auf ELF Dateien wuchs und ich den Satz korrigierte. WAS DIE ATTESTIERUNG NICHT BEHAUPTET, im Record ausgeschrieben: nicht "der Code ist korrekt" (bounded search, Grenze benannt), nicht "released" (Tag und Publish bleiben Owner-Akte), und ausdruecklich nicht "sechs unabhaengige Gegenleser" — der Lauf wurde von EINEM Agenten sequentiell gefahren, was schwaecher ist als die Methodik annimmt, und genau deshalb endet jedes Ziel in einer ausfuehrbaren Probe mit Kontrolle. Batterie: 2243 passed, 9 skipped, 420 subtests, 0 failed — zum ersten Mal vollstaendig gruen.
…nen Waechter un las den Populations-Waechter gegen und sagte REJECT. Drei der vier Punkte sind echt, einer nicht — und der erste hat sich am eigenen Baum bestaetigen lassen. PHANTOM-TREFFER, gemessen: die Deckungspruefung nahm ein 400-Zeichen-TEXTFENSTER ab jedem `pruefe_exakt(`-Vorkommen. Am eigenen Baum fuehrte sie damit `expected_x` als gedeckt — ein Name, der NUR in einem Docstring-BEISPIEL dieses Moduls steht. Haette je ein echter Parameter so geheissen, waere er still als gedeckt durchgegangen und die Klasse faelschlich geschlossen. Der Vergleich beider Fassungen ist eindeutig: Fenster 13 Namen, AST 12, Differenz genau dieser eine. Eine Fenstergroesse ist eine geratene Konstante ueber einer Groesse, die niemand beschraenkt (Formatierung). Der AST kennt die Aufrufgrenze exakt: gesucht werden jetzt Schluesselwort-Argumente `expected_*` INNERHALB des Aufrufknotens, Lambdas eingeschlossen. Prosa kommt dort nicht vor. Eine nicht parsbare Testdatei traegt NICHTS bei, statt per Text zu raten — eine kaputte Datei soll Deckung nicht vortaeuschen. ZWEITER PUNKT: `"str" in annotation` traf auch `abstract`, `struct` und jeden Typnamen mit `str` darin. Jetzt `\bstr\b`. Die Richtung des Fehlers war fordernd (ein Nicht-String haette ein Korpus gebraucht, das dort keinen Gegenstand hat) — falsch bleibt falsch. GATE-META-TEST fuer beide Fehlerrichtungen, die un benannte: ein Name aus einem Docstring darf NICHT als gedeckt gelten, und zwei benachbarte Aufrufe duerfen ihre Deckung nicht aneinander vererben. Beides an einem Wegwerf-Baum gemessen, mit der Gegenrichtung (echte Aufrufe MUESSEN gefunden werden) im selben Test. NICHT UEBERNOMMEN, mit Begruendung: uns Punkt zu `ast.Attribute`/`ast.Subscript` (`obj.expected_x == y`) trifft nicht — das sind keine PARAMETER, und der Waechter misst ausdruecklich Parameter; ausserdem rekursiert `ast.walk` ohnehin in Aufrufe hinein, was un im selben Absatz selbst korrigiert. Batterie: 2244 passed, 9 skipped, 420 subtests, ruff clean.
The mutation gate reported two gaps on 518d1ee and blocked v3.8.0: GAP [relation: cycle detection disabled] SURVIVED (red=1) GAP [relation: verified-flag laxened] SURVIVED (red=1) NEITHER IS A COVERAGE HOLE. Both are the same stale-operator shape, and the cause is a fix that was right. #139 made the ancestor walker apply the gates the direct-edge arm already had (L4-01: the gate was distance-scoped, and the distance is attacker-chosen). It also added a look-ahead so a back-edge is found before descending, making the cycle code independent of sibling order. Both were deliberate defence in depth. The side effect: each operator disables ONE line where the property now rests on TWO. The surviving guard catches the vector, the mutant lives, and the gate cries gap where the defence holds. An operator whose label says "disabled" must actually disable -- otherwise the gate measures the wrong thing in the SAFE direction, and that is how a gate gets ignored. MEASURED, each direction separately, on an extracted tree with the release venv: cycle, line 367 alone -> suite green (look-ahead still catches) cycle, look-ahead alone -> suite green (line 367 still catches) cycle, BOTH -> 4 red, among them test_injected_back_edge_onto_path_is_caught_or_unreachable verified, direct arm alone -> suite green (walker catches one level down) verified, BOTH -> red, tests/test_relation_profile.py:: TestVerifyRelationshipEdges::test_verified_flag_must_be_exactly_true So the killing tests EXIST and always did. They simply cannot kill a half-disabled property. THE FIX is in the operator table, not in the source: an operator may now name several sites (old/new as tuples). Single-string operators are unchanged, so the other 86 keep their exact meaning. Verified with the gate's own machinery on a filtered set: both now report KILLED (red=4 and red=20), 0 gaps. ALSO ADDED, and honestly not required for the above: three truthy-but-not-True vectors (1, "true", ["ja"]) in the distance-invariance generator. The file's own header says every chain test hardcodes "verified": True, so the strictness of `is not True` was untested ACROSS HOP DISTANCES. test_verified_flag_must_be_exactly_true already covers the direct arm; these extend the property to every hop, which is what this file is for. They raise the mutant's red count from 2 to 20 but are not what makes it die. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Release candidate 3.8.0 — not a release
This PR exists so CI can measure the candidate. It does not merge itself, tag, or publish: those are three separate Owner-GOs, and the standing GO (
GO_OWNER_PB_RELEASE_371_NACH_WITHSTANDS_20260807) makes green CI precondition 7 of seven. CI triggers only onmainand on pull requests againstmain, so without this PR that precondition cannot be measured at all.Why 3.8.0 and not 3.7.1
The Owner chose the number on a measured recommendation. Of sixteen patch releases in this repository's history, fifteen carry no
### Addedsection. The one exception, 3.2.3, ships--stricton the internal maintenance scriptscripts/rust_parity_gate.py, not on the shipped CLI. A new user-facing CLI flag has never gone out in a patch here; the only### Addedsection carrying CLI flags belongs to 0.2.0, a MINOR.The counter-argument is recorded in the changelog entry rather than hidden:
expected_originhas been in the library since 3.6 and the commit is titledfix(cli). Semver measures the surface, not the reason, andverify-proof --helpnow names a flag it did not name. The cost is asymmetric — someone pinning~=3.7.0would silently receive a new capability under 3.7.1.What is in it
release/v3.8.0branches frommainatac0688cand adds exactly one commit (f64d35e): the three version places the gate enforces, plus the changelog entry. The shipped delta over 3.7.0 is what already landed onmain— #136 (fixture), #137 (--expected-origin), #138 (the corrected ML-DSA reason), #135/#134/#132/#131/#130 (CI).scripts/check_version_and_changelog.py: OK, version single-sourced, changelog current, no undelivered drift.Deliberately not changed
RELEASE.md:49anddocs/readiness_pack/PROGRESS.md:3both readcurrent: 3.7.0. That is still true — 3.8.0 has no tag. Updating them here would assert a release that has not happened; they move with the tag. Likewise everysince v3.7.0line and theproof_7271fixture manifest recording that proofbundle 3.7.0 performed the verification: those are statements about the past.Measured locally on this tree
With
[pq,pytest,test,dev,anchors]—anchorsbeyond the usual set because CI runs[dev,pq,anchors]and withoutrfc3161_client107 tests skip silently (measured:skipped=117without,skipped=10with):The mutation gate and the cross-implementation gate are reported honestly as incomplete rather than assumed. The adversarial deep-gate run on the candidate digest (precondition 1) has not happened yet either.