Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 39 additions & 5 deletions scripts/pre_tag_audit_gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,45 @@


def _positive_audit_marker(text: str) -> bool:
"""True iff some line ASSERTS an adversarial/N-lens audit was run — a discipline marker on a line that
is NOT negated. Line-scoped so a real positive note survives an unrelated negation elsewhere in the file,
while a marker whose own sentence concedes the audit did not run does not grant a false PASS."""
for line in text.splitlines():
if _AUDIT_MARKERS.search(line) and not _AUDIT_NEGATION.search(line):
"""True iff some PARAGRAPH asserts an adversarial/N-lens audit was run — a discipline marker in a
paragraph that carries no negation.

RT10-PRETAG-03 (2026-08-16, found by triggering it): the scope used to be the physical LINE, and a
wrapped sentence walked straight through the RT10-PRETAG-02 negation guard. Measured live on a real
release candidate, where an honest retraction being written INTO the CHANGELOG flipped the gate to
``ok: true, changelog_records_audit: true`` for roughly 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

The second line opens with ``adversarial`` and carries no negation token of its own — the words that
take it back (``never``, and the fact that the audit was still running) sit on the line above. Nothing
was crafted to defeat the guard; ordinary prose wrapping at 110 columns did it, and it will do it again
to anyone who writes a careful sentence about an audit that has not finished. The guard was therefore
most easily defeated by exactly the kind of honest text this project requires of itself.

Paragraph scope is strictly STRICTER than line scope — a paragraph contains its lines, so it can only
ever bring MORE negation tokens into view, never fewer. No text that the old rule rejected can be
accepted by the new one.

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.

The counter-direction is measured too, because a stricter gate that rejects genuine records would just
be a different defect: the real ``audit_artifacts/370/pre_tag_adversarial_audit_370.md`` still returns
True. Its opening paragraph pairs the claim with a "NOT a substitute for the external audit"
disclaimer, so that paragraph is correctly not counted — and the later "Six diverse falsification-first
lenses …" paragraph carries the marker with no negation, which is the attestation. House style survives.

HONEST LIMIT, unchanged by this fix: this 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 is what the version-consistency-gate branch
moves to. This narrows a live hole in the mechanism that guards releases today; it does not claim to
have made prose inference sound.
"""
for absatz in re.split(r"\n\s*\n", text):
if _AUDIT_MARKERS.search(absatz) and not _AUDIT_NEGATION.search(absatz):
return True
return False

Expand Down
164 changes: 164 additions & 0 deletions tests/test_audit_marker_line_wrap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
"""RT10-PRETAG-03: the audit-marker negation guard must not be defeated by ordinary line wrapping.

`_positive_audit_marker` decides whether a release has recorded its pre-tag adversarial audit. It looks
for a discipline marker (`N-lens`, `adversarial`, `master-prompt`, `Linsen`) that is not negated. The
scope of "not negated" used to be the physical LINE.

HOW IT WAS FOUND, which matters more than the fix: by triggering it. 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.
Nothing was crafted. Prose wrapped at 110 columns put the word `adversarial` at the start of a line, and
the words that take it back stayed on the line above.

That is the sharp edge of the class: the guard was most easily defeated by exactly the kind of careful,
self-critical text the project requires of itself. A release could have been tagged on the strength of a
sentence admitting it was not ready.
"""
from __future__ import annotations

import importlib.util
import re
import unittest
from pathlib import Path

REPO = Path(__file__).resolve().parents[1]


def _load(mod_name: str, rel: str):
"""Same loader shape the rest of the suite uses for `scripts/` (see test_roadmap_frontload_foundations)."""
spec = importlib.util.spec_from_file_location(mod_name, REPO / rel)
module = importlib.util.module_from_spec(spec)
assert spec and spec.loader
spec.loader.exec_module(module)
return module


gate = _load("marker_wrap_pretag_gate", "scripts/pre_tag_audit_gate.py")


def _line_scoped(text: str) -> bool:
"""The rule as it stood before this fix, kept so both directions can be measured, not asserted."""
for line in text.splitlines():
if gate._AUDIT_MARKERS.search(line) and not gate._AUDIT_NEGATION.search(line):
return True
return False


# The exact two lines that flipped the live gate. Reproduced verbatim rather than described, so this
# test fails for the reason it names if the wrapping ever stops mattering.
DER_LIVE_FALL = (
"…never shipped a user-facing CLI flag in a patch release. That claim was falsified during the pre-tag\n"
"adversarial audit and is retracted here rather than quietly deleted: four patch releases have shipped"
)


class TestWrappedNegationNoLongerGrantsPass(unittest.TestCase):

def test_the_live_case_is_now_rejected(self):
self.assertFalse(gate._positive_audit_marker(DER_LIVE_FALL),
"a wrapped retraction still certifies the audit it retracts")

def test_counter_check_the_old_rule_really_did_accept_it(self):
"""Without this, the test above could be green because the corpus is harmless.

A fix test that never demonstrates the defect is a test that measures nothing. This one shows the
previous rule saying yes to the same bytes.
"""
self.assertTrue(_line_scoped(DER_LIVE_FALL),
"the pre-fix rule does not reproduce the defect — the fixture no longer discriminates")

def test_a_genuine_record_is_still_accepted(self):
"""A stricter gate that rejects real records would just be a different defect.

The committed 3.7.0 record is the house style: its opening paragraph pairs the claim with a
"NOT a substitute for the external audit" disclaimer, and a later paragraph carries the marker
cleanly. Paragraph scope must count the second and not the first.
"""
echt = REPO / "audit_artifacts" / "370" / "pre_tag_adversarial_audit_370.md"
if not echt.is_file():
self.skipTest(f"reference record absent: {echt} — measuring nothing is not a pass")
text = echt.read_text(encoding="utf-8")
self.assertTrue(gate._positive_audit_marker(text),
"the fix rejects a real, committed pre-tag audit record")

def test_the_original_single_line_negation_still_holds(self):
"""RT10-PRETAG-02 must not regress: a negated marker on one line stays a non-attestation."""
for text in ("the adversarial audit did NOT run",
"adversarial review pending",
"the 6-lens jury has not completed",
"master-prompt audit skipped for this release",
"Linsen-Lauf noch nicht durchgefuehrt"):
with self.subTest(text=text):
self.assertFalse(gate._positive_audit_marker(text), f"negated marker granted a pass: {text}")

def test_the_new_scope_is_strictly_stricter(self):
"""The property that makes this a safe change: accepted(new) is a SUBSET of accepted(old).

A paragraph contains its own lines, so widening the scope can only bring more negation tokens into
view. Anything the old rule rejected must stay rejected. Asserted over a corpus rather than argued,
because "obviously monotone" is how a loosening slips in.
"""
korpus = [
DER_LIVE_FALL,
"six-lens adversarial audit run before the tag",
"adversarial audit\nnot completed", # wrap, negation AFTER the marker
"not completed\nadversarial audit", # wrap, negation BEFORE the marker
"clean prose with no marker at all",
"adversarial\n\nnot completed", # separate paragraphs: marker survives
"| F7 | target | holds | a 6-lens pass |\n| F8 | other | fell | not run |",
"",
"adversarial audit run.\n\nA later paragraph says the release is not ready.",
]
for text in korpus:
with self.subTest(text=text[:48]):
if gate._positive_audit_marker(text):
self.assertTrue(_line_scoped(text),
"the new rule accepts text the old rule rejected — this is a LOOSENING, "
"not a hardening")

def test_a_separate_paragraph_still_carries_its_own_attestation(self):
"""Paragraph scope must not become file scope, or one disclaimer anywhere would kill every record.

This is the boundary the fix has to hit exactly: strict enough that a wrapped retraction cannot
certify itself, loose enough that an unrelated caveat elsewhere in the document does not suppress a
genuine attestation. It is the same intent RT10-PRETAG-02 stated for lines, moved up one level.
"""
text = ("A six-lens adversarial audit ran on this candidate.\n"
"\n"
"This is not a substitute for the external human audit.\n")
self.assertTrue(gate._positive_audit_marker(text),
"an unrelated caveat in a DIFFERENT paragraph suppressed a genuine attestation")

def test_meta_reverting_the_fix_makes_this_file_fail(self):
"""PLANT-AND-MUST-CATCH: with the line-scoped rule restored, the live case must go red again.

Without this, a future refactor could quietly restore line scope and every test above would still
pass on its own terms — the file would keep asserting a property it no longer measures.
"""
gemessen = _line_scoped(DER_LIVE_FALL)
self.assertTrue(gemessen,
"the restored pre-fix rule no longer accepts the live case, so this file can no "
"longer tell the two rules apart")
self.assertNotEqual(gemessen, gate._positive_audit_marker(DER_LIVE_FALL),
"the two rules agree on the defect case — the fix has no effect")


class TestParagraphSplitIsTheDocumentedOne(unittest.TestCase):
"""The split must be blank-line separated paragraphs, measured rather than assumed from the docstring."""

def test_a_blank_line_separates_scopes(self):
verbunden = "adversarial audit\nnot completed"
getrennt = "adversarial audit\n\nnot completed"
self.assertFalse(gate._positive_audit_marker(verbunden), "same paragraph: negation must apply")
self.assertTrue(gate._positive_audit_marker(getrennt), "different paragraph: negation must not apply")

def test_whitespace_only_lines_also_separate(self):
"""A line of spaces looks blank in an editor and must behave that way, or the scope depends on
invisible characters."""
self.assertTrue(gate._positive_audit_marker("adversarial audit\n \nnot completed"),
"a whitespace-only line did not separate paragraphs")
self.assertTrue(re.split(r"\n\s*\n", "a\n \nb").__len__() == 2)


if __name__ == "__main__": # pragma: no cover
unittest.main()