Skip to content

The audit-marker negation guard survives a line break - #143

Merged
b7n0de merged 1 commit into
mainfrom
fix/audit-marker-line-wrap
Aug 16, 2026
Merged

The audit-marker negation guard survives a line break#143
b7n0de merged 1 commit into
mainfrom
fix/audit-marker-line-wrap

Conversation

@b7n0de

@b7n0de b7n0de commented Aug 16, 2026

Copy link
Copy Markdown
Owner

Found by triggering it

_positive_audit_marker decides 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:

…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 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.

rule on the live case on the committed audit_artifacts/370/ record
line-scoped (before) True ← the defect True
sentence-scoped True ← does not close it True
paragraph-scoped (this PR) False True

Sentence scope was measured first and rejected. It still returns True on the block above, because 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 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_stricter rather 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.md still 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_attestation holds the boundary from the other side, so paragraph scope cannot silently become file scope.

Revert probe

with the fix fix stashed out
tests/test_audit_marker_line_wrap.py 9 passed, 14 subtests 3 failed, 6 passed
tests/test_roadmap_frontload_foundations.py 19 passed 19 passed (unchanged)

The three that go red are exactly the ones that name the property: the live case, the paragraph split, and the meta-test. ruff clean.

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.

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>
@b7n0de
b7n0de merged commit 9d643e9 into main Aug 16, 2026
22 checks passed
@b7n0de
b7n0de deleted the fix/audit-marker-line-wrap branch August 16, 2026 16:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant