The audit-marker negation guard survives a line break - #143
Merged
Conversation
RT10-PRETAG-02 stops a marker from counting when its own line takes it back --
"the adversarial audit did NOT run" must not certify anything. The scope was the
physical LINE, and that is where it breaks: wrapped prose puts the marker and its
negation on different lines.
FOUND BY TRIGGERING IT, not by reading the code. While writing an honest
retraction into a release candidate's CHANGELOG -- a paragraph whose entire point
is that the audit had not finished -- pre_tag_audit_gate flipped to
{"ok": true, "changelog_records_audit": true} and stayed there for about two
minutes:
...never shipped a user-facing CLI flag in a patch release. That claim was falsified during the pre-tag
adversarial audit and is retracted here rather than quietly deleted: four patch releases have shipped
Line two opens with `adversarial` and carries no negation token; the words that
take it back (`never`, and the fact that the run was still going) sit on line one.
Nothing was crafted to defeat the guard. Ordinary wrapping at 110 columns did it.
That is the sharp edge of this class and the reason it is worth a commit of its
own: the guard is most easily defeated by exactly the kind of careful,
self-critical writing the project demands of itself. A tag could have been cut on
the strength of a sentence admitting the release was not ready.
FIX: paragraph scope (blank-line separated) instead of line scope. A paragraph
contains its lines, so the change can only bring MORE negation tokens into view,
never fewer -- no text the old rule rejected can be accepted by the new one. That
monotonicity is asserted over a corpus in test_the_new_scope_is_strictly_stricter
rather than argued, because "obviously monotone" is how a loosening gets in.
SENTENCE SCOPE WAS MEASURED FIRST AND REJECTED. It still returns True on the block
above: "falsified" is not a negation token, and the sentence carrying `adversarial`
genuinely contains none. A finer scope does not help when the negation lives in the
surrounding argument rather than in a word. Recorded because the obvious fix looks
right and is not.
line-scoped (before) -> True the defect
sentence-scoped -> True does NOT close it
paragraph-scoped (now) -> False closes it
COUNTER-DIRECTION, because a stricter gate that rejects real records is just a
different defect: the committed audit_artifacts/370/pre_tag_adversarial_audit_370.md
still returns True under all three rules. Its opening paragraph pairs the claim with
a "NOT a substitute for the external audit" disclaimer and is correctly not counted;
the later "Six diverse falsification-first lenses ..." paragraph carries the marker
cleanly and is the attestation. House style survives, and a test pins that.
Paragraph scope also must not become FILE scope, or one caveat anywhere would kill
every record -- test_a_separate_paragraph_still_carries_its_own_attestation holds
that boundary from the other side.
REVERT PROBE: with the fix stashed out, this file goes red on exactly the three
tests that name the property (live case, paragraph split, meta) -- 3 failed,
6 passed. With it, 9 passed / 14 subtests. tests/test_roadmap_frontload_foundations.py
unchanged at 19 passed.
HONEST LIMIT, stated in the code as well: this still infers a fact from free prose,
and prose inference stays defeatable in principle. The durable answer is an explicit
attestation token that means one thing --
`pre-tag-adversarial-audit: RUN | version=X.Y.Z` -- which the version-consistency-gate
branch already moves to. This narrows a live hole in the mechanism that guards
releases TODAY; it does not claim to have made prose inference sound.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
b7n0de
pushed a commit
that referenced
this pull request
Aug 16, 2026
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>
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.
Found by triggering it
_positive_audit_markerdecides whether a release has recorded its pre-tag adversarial audit. RT10-PRETAG-02 already stops a negated marker from counting — "the audit did NOT run" must not certify anything. The scope of "not negated" was the physical line, and that is where it breaks.While writing an honest retraction into a release candidate's CHANGELOG — a paragraph whose whole point is that the audit had not finished — the gate flipped to
{"ok": true, "changelog_records_audit": true}and stayed there for about two minutes. The two lines that did it:Line two opens with
adversarialand carries no negation token. The words that take it back —never, and the fact that the run was still going — sit on line one. Nothing was crafted to defeat the guard; ordinary prose wrapping at 110 columns did it.That is the sharp edge of this class: the guard is most easily defeated by exactly the kind of careful, self-critical writing this project requires of itself. A tag could have been cut on the strength of a sentence admitting the release was not ready.
The fix, and the one that looked right and isn't
Paragraph scope (blank-line separated) instead of line scope.
audit_artifacts/370/recordTrue← the defectTrueTrue← does not close itTrueFalseTrueSentence scope was measured first and rejected. It still returns
Trueon the block above, becausefalsifiedis not a negation token and the sentence carryingadversarialgenuinely contains none. A finer scope does not help when the negation lives in the surrounding argument rather than in a word. Recorded here because the obvious fix looks right and is not.Why this is safe
A paragraph contains its lines, so widening the scope can only bring more negation tokens into view, never fewer. No text the old rule rejected can be accepted by the new one. That monotonicity is asserted over a corpus in
test_the_new_scope_is_strictly_stricterrather than argued — "obviously monotone" is how a loosening gets in.The counter-direction matters just as much, because a stricter gate that rejects genuine records is simply a different defect. The committed
audit_artifacts/370/pre_tag_adversarial_audit_370.mdstill passes: its opening paragraph pairs the claim with a "NOT a substitute for the external audit" disclaimer and is correctly not counted, while the later "Six diverse falsification-first lenses …" paragraph carries the marker cleanly and is the attestation. House style survives.test_a_separate_paragraph_still_carries_its_own_attestationholds the boundary from the other side, so paragraph scope cannot silently become file scope.Revert probe
tests/test_audit_marker_line_wrap.pytests/test_roadmap_frontload_foundations.pyThe three that go red are exactly the ones that name the property: the live case, the paragraph split, and the meta-test.
ruffclean.Honest limit
This still infers a fact from free prose, and prose inference stays defeatable in principle. The durable answer is an explicit attestation token that means one thing —
pre-tag-adversarial-audit: RUN | version=X.Y.Z— which #139 already moves to. This PR narrows a live hole in the mechanism that guards releases today; it does not claim to have made prose inference sound. If #139 lands first, this becomes redundant rather than wrong, and can be closed.