fix(retrieval-gate): row 5 reads git for the half a registration cannot assert
PM's J1. Three of row 5's ten checks now read the repository's history instead of the registration's own fields: - the registration is COMMITTED and unmodified against HEAD -- a file written in a working tree is not a pre-registration; - the commit that ADDED it is not itself a change to the ranking -- a threshold and the change it is supposed to bind, in one commit, is neither; - a change to the ranking landed AFTER it. This is the one that cannot be self-attested: it is satisfied only in the order a pre-registration actually happens, and it is red today because neither half has happened. The three come from `git_provenance`, a `Provenance` the row receives the way it already receives a path, so both directions are driven from the test and the default is the real git. Outside a git tree, or with no git on PATH, the fields come back in their REFUSING form with the reason printed -- unknown is not unknown-and-therefore-fine. WHAT GIT CANNOT PROVE IS STATED IN THE ROW, not implied: history shows ORDER, never that nobody read the number before writing the threshold -- a number can be read from an uncommitted tree. Order is what these three checks are. Row 5 is unchanged where it matters today: `0 of 1 RED`, no registration. The two existing direction tests move 7 -> 10 checks and a third arm is added that drives each git check red on its own. 54 passed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
e207935ba6
commit
30cbb69ac0
2 changed files with 169 additions and 6 deletions
|
|
@ -45,10 +45,11 @@ import argparse
|
|||
import contextlib
|
||||
import hashlib
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from collections.abc import Callable, Iterator, Mapping, Sequence
|
||||
from dataclasses import dataclass, field
|
||||
from dataclasses import dataclass, field, replace
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
|
@ -1155,6 +1156,74 @@ def row_four(cases: Sequence[Case]) -> Row:
|
|||
)
|
||||
|
||||
|
||||
#: The path a capability session changes. Row 5 reads git for the ONE thing a
|
||||
#: registration cannot assert about itself: that it was already committed when
|
||||
#: that path moved.
|
||||
RANKING_PATH = "src/llm_ingestion_okf/consume.py"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Provenance:
|
||||
"""What GIT says about a file. Every field here is a fact about the
|
||||
repository's history, which is the one thing the file cannot also write.
|
||||
|
||||
Unknown is NOT unknown-and-therefore-fine: outside a git tree, or with no
|
||||
git on PATH, the fields come back in their refusing form and the note says
|
||||
why.
|
||||
"""
|
||||
|
||||
tracked: bool
|
||||
unmodified: bool
|
||||
commit: str
|
||||
commit_touches_ranking: bool
|
||||
ranking_commits_after: int
|
||||
note: str = ""
|
||||
|
||||
|
||||
def _git(repo: Path, *arguments: str) -> tuple[int, str]:
|
||||
try:
|
||||
finished = subprocess.run(
|
||||
["git", "-C", str(repo), *arguments],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
except OSError as error:
|
||||
return 127, str(error)
|
||||
return finished.returncode, finished.stdout
|
||||
|
||||
|
||||
def git_provenance(path: Path, *, repo: Path = REPO, ranking: str = RANKING_PATH) -> Provenance:
|
||||
"""`path`'s history, read from git and never from `path`."""
|
||||
refusing = Provenance(False, False, "", True, 0)
|
||||
code, _ = _git(repo, "rev-parse", "--git-dir")
|
||||
if code != 0:
|
||||
return replace(refusing, note="not a git tree, or git is not on PATH")
|
||||
tracked = _git(repo, "ls-files", "--error-unmatch", "--", str(path))[0] == 0
|
||||
if not tracked:
|
||||
return replace(refusing, note=f"{_display(path)} is not tracked in this repository")
|
||||
unmodified = _git(repo, "diff", "--quiet", "HEAD", "--", str(path))[0] == 0
|
||||
_, log = _git(repo, "log", "--diff-filter=A", "--format=%H", "--", str(path))
|
||||
commits = [line.strip() for line in log.splitlines() if line.strip()]
|
||||
if not commits:
|
||||
return replace(
|
||||
refusing,
|
||||
tracked=True,
|
||||
unmodified=unmodified,
|
||||
note="no commit adds this file; it is staged and not committed",
|
||||
)
|
||||
commit = commits[-1]
|
||||
_, touched = _git(repo, "show", "--pretty=", "--name-only", commit)
|
||||
_, after = _git(repo, "log", "--format=%H", f"{commit}..HEAD", "--", ranking)
|
||||
return Provenance(
|
||||
tracked=True,
|
||||
unmodified=unmodified,
|
||||
commit=commit,
|
||||
commit_touches_ranking=ranking in touched.split(),
|
||||
ranking_commits_after=len([line for line in after.splitlines() if line.strip()]),
|
||||
)
|
||||
|
||||
|
||||
def _display(path: Path) -> str:
|
||||
"""Repo-relative where it is inside the repo, absolute otherwise: a test
|
||||
drives this row from `tmp_path`, and `relative_to` raises there."""
|
||||
|
|
@ -1164,12 +1233,31 @@ def _display(path: Path) -> str:
|
|||
return str(path)
|
||||
|
||||
|
||||
def row_five(registration: Path = HOLDOUT_REGISTRATION) -> Row:
|
||||
def row_five(
|
||||
registration: Path = HOLDOUT_REGISTRATION,
|
||||
*,
|
||||
provenance: Callable[[Path], Provenance] = git_provenance,
|
||||
) -> Row:
|
||||
"""The hold-out set: written blind, frozen before the first capability
|
||||
line, its threshold written before anyone saw the number.
|
||||
|
||||
Report-only without a written threshold is not a protection, so the
|
||||
absence of a threshold is red rather than absent.
|
||||
|
||||
AND NEITHER IS A PROTECTION THE FILE WRITES ABOUT ITSELF. Until
|
||||
2026-09-19 every check here read a field the registration owned, and two
|
||||
files PM wrote in the moment came back `7 of 7 GREEN`. Three checks now
|
||||
read GIT instead: the registration is committed and unmodified, the commit
|
||||
that ADDED it is not itself a change to the ranking, and a change to the
|
||||
ranking landed AFTER it. The third is the one that cannot be self-attested
|
||||
-- it is green only in the order a pre-registration actually happens, the
|
||||
registration first and the ranking change second, and it is red today
|
||||
because neither has happened.
|
||||
|
||||
WHAT GIT CANNOT PROVE, stated rather than implied: that nobody read the
|
||||
number before writing the threshold. A number can be read from an
|
||||
uncommitted working tree, and no history shows that. What history does
|
||||
show is ORDER, and order is what these three checks are.
|
||||
"""
|
||||
if not registration.is_file():
|
||||
return Row(
|
||||
|
|
@ -1183,6 +1271,9 @@ def row_five(registration: Path = HOLDOUT_REGISTRATION) -> Row:
|
|||
" a set written by a session other than the one that changes the "
|
||||
"ranking, frozen with sha256 before the first capability line",
|
||||
" its threshold written, with a date, before its number is read",
|
||||
" and COMMITTED before the ranking moves: git must show the "
|
||||
f"registration in a commit of its own, with a later commit to "
|
||||
f"{RANKING_PATH}. That is the half a session cannot write about itself",
|
||||
],
|
||||
)
|
||||
try:
|
||||
|
|
@ -1225,6 +1316,30 @@ def row_five(registration: Path = HOLDOUT_REGISTRATION) -> Row:
|
|||
f"{len(early)} reading(s) before {written_at}" if early else "0 early readings",
|
||||
)
|
||||
)
|
||||
history = provenance(registration)
|
||||
checks.append(
|
||||
(
|
||||
"git: the registration is committed, unmodified",
|
||||
history.tracked and history.unmodified and bool(history.commit),
|
||||
history.note or (f"{history.commit[:12]} clean" if history.unmodified else "modified"),
|
||||
)
|
||||
)
|
||||
checks.append(
|
||||
(
|
||||
"git: its commit is not itself a ranking change",
|
||||
bool(history.commit) and not history.commit_touches_ranking,
|
||||
f"{RANKING_PATH} in the same commit"
|
||||
if history.commit_touches_ranking
|
||||
else "separate commit",
|
||||
)
|
||||
)
|
||||
checks.append(
|
||||
(
|
||||
"git: a ranking change landed after it",
|
||||
history.ranking_commits_after > 0,
|
||||
f"{history.ranking_commits_after} commit(s) touching {RANKING_PATH} since",
|
||||
)
|
||||
)
|
||||
passed = sum(1 for _, ok, _ in checks if ok)
|
||||
return _row(
|
||||
5,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue