fix(v1-gate): rows 1-2 stop at FORM OK — what files cannot show, a person has to say
The PM checkpoint of 18.09 paid M-6's price three times and row 2 still read 3 of 3 GREEN:
a whole artefact family written by hand and made internally consistent (verdict_id minted
with the product's own rule, about sixty lines of script), four REAL runs' artefacts under
a handwritten feedback file, and `<n>/outbox` symlinked out of the round to a real run
elsewhere. `RUN_ATTESTATION` stood in the output the whole time, and the row was green
anyway — which is the finding: a row that PRINTS its limit and reports GREEN has not
stated a limit, it has decorated one.
One of the three is a hole and is closed; two are not holes at all.
1. The outbox must lie in the round ITSELF (`outbox_escape`). Derived is only derived if
the filesystem cannot redirect it: `<n>/outbox` as a symlink, or any symlinked artefact
inside it, is refused by name.
2. Rows 1 and 2 can no longer be green from files. What the computation gives is
`FORM_OK` ("FORM OK, IKKE BEVIST"): every check the gate knows how to make passed, the
round counts 0 against the criterion, and the exit code still fails — it is un-green in
every way RED is. What separates the two is what the reader must do next, not what the
gate concludes.
3. Green is `<n>/attestering.txt`, per round: the operator's own statement, naming the
round, the run and the date. Plain text with three keyed lines, deliberately not JSON —
it is the one artefact in this contract a PERSON types, and a missing brace must never
be able to read as "not confirmed". Nothing in the product writes one, and a test in
`src/` says so: a product that can produce a witness to its own run has produced
exactly the thing these rows cannot.
A MISSING attestation is FORM OK: nobody has confirmed anything yet, and the gate is
not entitled to call that a lie. A PRESENT one that names another round or run, carries
no date, an unparsable date, or a date before the run, is RED: it is a statement about
something this is not. Row 2 needs round 0 attested as well, since round 1 is measured
against it — an unattested baseline is an unattested comparison.
The attestation is read raw off `outcome.json` (`_declared_run`), not through
`read_outcome`, so row 1 — which reads no run — does not inherit row 2's artefact checks
through the back door.
Nothing moved on the criterion: 0/3 · 0/3 · 3/8 · no report · 3/8 · IKKE MÅLT · 1/20,
exit 1. What changed is that rows 1-2 now name what they are waiting for, and it is not
another file.
Suite after `git add` on a clean tree: `uv run pytest -q` = 2014 passed, 5 skipped,
5 xfailed (508 s). The attack of b769537/ab00016 is the red half of this commit.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
ab00016ed5
commit
1b48124a01
2 changed files with 204 additions and 21 deletions
|
|
@ -521,12 +521,13 @@ def _forged_rounds(root: Path) -> Path:
|
|||
|
||||
|
||||
def test_m6_a_handwritten_outbox_is_not_a_run(tmp_path: Path) -> None:
|
||||
"""Row 2 must not be satisfiable by files a forger wrote. Row 1 stays GREEN on the same tree —
|
||||
the feedback there IS well formed, and that is what makes this an attack on row 2 rather than
|
||||
a broken fixture. The control that the row can still go green is ``_green_rounds``, which
|
||||
carries a whole run family (``test_row2_a_traced_change_counts``)."""
|
||||
"""Row 2 must not be satisfiable by files a forger wrote. Row 1 reads FORM OK on the same
|
||||
tree — the feedback there IS well formed, and that is what makes this an attack on row 2
|
||||
rather than a broken fixture; since M-7 that form is as far as row 1 can get without the
|
||||
operator's attestation. The control that the row can still go green is ``_green_rounds``,
|
||||
which carries a whole run family AND an attestation (``test_row2_a_traced_change_counts``)."""
|
||||
root = _forged_rounds(tmp_path)
|
||||
assert gate.score_rounds(root, 3, _AI).status == gate.GREEN
|
||||
assert gate.score_rounds(root, 3, _AI).status == gate.FORM_OK
|
||||
row = gate.score_changes(root, 3, _AI)
|
||||
assert (row.k, row.status) == (0, gate.RED), row.exceptions
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue