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
28 changes: 28 additions & 0 deletions .agents/completed/issue-index.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
ID: ISSUE-LOCAL-01M3NR65JDEV0NH28W5ZPQDPYC
Title: the frozen archive dropped 27 rows its own evidence records still cite
Row: GATE-ISSUE-ARCHIVE-RESTORE
State: OPEN
Kind: bug
GitHub: -
Mirror: PENDING
Availability: FULL
Created: 2026-09-29
Updated: 2026-09-29
Closed: -

## Problem

Commit 2e84a073b deleted .agents/completed/issue-index.md wholesale (913 lines) and e3539d994 restored a hand-picked 886-line copy that dropped 27 archived rows. Those 27 rows are exactly what 43 frozen-evidence records cite: 24 cite a line past the restored file's end, 19 cite a line that now holds a different row. No gate sees any of that today: the frozen-evidence comparison runs only in the _intake branch, whose 9 records all cite surviving lines, so all 43 stale quotes are row-owned records that validate untouched. But every one of those 43 quotes is byte-true to the pre-deletion archive, so no edit to any record can repair them against a truncated archive; the defect is in the archive, not in the quoting. Measured: re-inserting the deleted lines at the positions difflib reports leaves the file byte-identical to the pre-deletion archive except line 406, which keeps e3539d994's own deliberate link re-point; the 831-quote census moves from 350 byte-equal to 373, and check-agent-record stays green with unchanged row counts. With the #3350 relative-link comparison the restored archive resolves all 831 quotes, 0 failing. Fix: splice the 27 rows back at their original positions, re-pointing any link that does not resolve from .agents/completed/ (measured: none needed). Prerequisite for the stacked checker change that enforces the comparison outside _intake.

## Resolution

-
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
ID: ISSUE-LOCAL-01M3NSTDJSHREP8HCX83K5KXV0
Title: the frozen-evidence contract is not enforced outside _intake
Row: GATE-ISSUE-ARCHIVE-RESTORE
State: OPEN
Kind: bug
GitHub: -
Mirror: PENDING
Availability: FULL
Created: 2026-09-29
Updated: 2026-09-29
Closed: -

## Problem

validate_issue_record runs the frozen-evidence comparison (the quoted archive line must be byte-equal to the declared line, and must name the record's own GitHub number) only in the _intake branch. Row-owned and _owed records that carry a Frozen archive evidence block are never compared: any of the 822 non-intake blocks could drift from the archive, swap a URL, or quote another issue's row, and every gate stays green. Measured on the restored archive (#3351 data half): 831 records carry a block, 831 resolve under the #3350 comparison with the record directory as base, 831 quote a line carrying their own issue number, 0 violations, so enforcing the same rule outside _intake adds no new red today while closing the drift door. Absence of the block stays legal outside _intake (456 row-owned records legitimately have none); presence is not. Fix: hoist the comparison into a shared helper and apply it in the _owed and row-owned branches, and strip a trailing CR from the archived line so a CRLF Windows working copy compares equal to the LF committed blob.

## Resolution

-
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
ID: ISSUE-LOCAL-01M3NH23E78PEXCJF4HW1XHQQ3
Title: frozen-evidence byte equality and record link resolution cannot both hold
Row: POLICY-ISSUE-INTAKE
State: OPEN
Kind: bug
GitHub: -
Mirror: PENDING
Availability: FULL
Created: 2026-09-28
Updated: 2026-09-28
Closed: -

## Problem

An _intake record's Problem must quote the frozen archive line byte for byte, but check-agent-record's check_links requires every link in a record to resolve from the record's own directory. The archive lives at .agents/completed/issue-index.md, one level under .agents, so a link to a spec is spelled ../specs/x.md there; the record that QUOTES the row lives at .agents/issues/<owner>/, two levels down, so the same link must be spelled ../../specs/x.md from there. One string cannot satisfy both. Commit e3539d994 re-pointed the ISSUE-GH-1033 quote to the record-relative spelling to fix the dangling link, which broke the byte comparison: first divergence at column 2191 of archive line 350, archive 2237 chars against evidence 2240. Measured over all 831 records that carry a Frozen archive evidence block, 350 are byte-equal, 438 differ from the cited line ONLY by a relative link rebase, and 43 cite a line the archive no longer has (24 past its 886 lines after the delete and re-add, 19 naming a row that moved). Restoring the archive spelling makes check-links red instead. Fix: compare the quote against the cited line with each side's relative link targets resolved to the file they denote, so the check asks whether the quote IS the archived row rather than which directory holds the quote.

## Resolution

-
83 changes: 83 additions & 0 deletions .agents/specs/gate-issue-archive-restore.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Spec — the frozen archive dropped 27 rows its own evidence records still cite

Row: `GATE-ISSUE-ARCHIVE-RESTORE` (unplaced record/gate defect; the completed
archive is a record surface, not a matrix row)
State: `ACTIVE`

## Scope

`.agents/completed/issue-index.md` is the frozen archive every
`### Frozen archive evidence` block quotes. It is not frozen in the way the
name claims:

1. Commit `2e84a073b` deleted the file wholesale — 913 lines — alongside its
(legitimate) spec addition.
2. Commit `e3539d994` restored a hand-picked copy: "The last pre-retirement
revision is restored at `.agents/completed/issue-index.md`, with its
internal links re-pointed." The restore measured 886 content lines: 27
archived rows came back missing, and one surviving line (406) was
deliberately re-pointed.

The 27 dropped rows are not idle history. Exactly 43 records under
`.agents/issues/` cite them: 24 cite a line past the restored file's end, 19
cite a line that now holds a different row. No gate sees any of that today:
the frozen-evidence comparison runs only in the `_intake` branch of
`validate_issue_record`, whose 9 records all cite surviving lines, so all 43
stale quotes are row-owned records that validate untouched. But every one of
those 43 quotes is byte-true to the pre-deletion archive, so no edit to any
record can repair them against a truncated archive. The defect is in the
archive, not in the quoting.

Measured over all 831 records that carry a frozen-evidence block, against
the committed LF blob:

- before the restore: 350 byte-equal, 458 failing under this base's
byte-only comparison (481 under the #3350 link-rebase comparison);
- after re-inserting the 27 lines at the positions `difflib` reports
between `2e84a073b~1` and the restored file: the file is byte-identical
to the pre-deletion archive except line 406, which keeps `e3539d994`'s own
deliberate re-point; 373 byte-equal, and under #3350's comparison all 831
resolve, 0 failing.

`scripts/check-agent-record.py` stays green with unchanged row counts
(ENGINE=179 MODEL=384 QUANT=87 KERNEL=60 BACKEND=90); none of the re-pointed
links in the restored lines dangle from `.agents/completed/`, so nothing
needed re-pointing beyond what `e3539d994` already did.

In scope:

1. Splice the 27 dropped rows back at their original positions.
2. Re-point any restored link that does not resolve from
`.agents/completed/` (measured: none).
3. The red-before / green-after census in the commit message.

Out of scope, each for its own reason:

1. **Editing any record.** The 43 quotes are correct; "repairing" them would
re-anchor records onto a truncated archive and make the falsification
load-bearing.
2. **Changing any checker.** Widening or narrowing a gate to make a red go
green is what AGENTS.md forbids; the archive, not the gate, is wrong.
3. **Enforcing the frozen-evidence comparison outside `_intake`.** That is
the checker half of this pair and lands as its own stacked PR (#3350 is
the comparison it needs; this restore is the data it needs).
4. **The 4 row-cell anomalies in the ROW bucket** — 3 records whose archived
cell is the em-dash placeholder (`ISSUE-GH-83`, `ISSUE-GH-606`,
`ISSUE-GH-408`) and 1 genuine cross-row citation (`ISSUE-GH-298` cites a
`PERF-27B-LMHEAD-DSR` line while living under `PERF-27B-LMHEAD-FP4`).
They are pre-existing record-content questions, orthogonal to line
existence, and every one of them still resolves after the restore.

## Enforcement (the stacked checker PR)

With the rows restored and #3350's relative-link comparison landed, the
frozen-evidence contract is enforced wherever the block appears, in every
owner directory: a record that QUOTES an archived row must quote the line it
declares, modulo exactly the relative-link rebase the record's directory
forces, and the quoted line must carry the record's own GitHub number.
Absence of the block stays legal everywhere except `_intake`; presence is
not. Measured over the corpus on the restored archive: 831 records carry a
block, 831 resolve, 831 identify their own issue, 0 violations -- the
ratchet adds no new red. Working-copy EOL no longer changes the answer: the
comparison strips a trailing CR from the archived line, because the
committed blob is LF and a Windows checkout is not.
154 changes: 147 additions & 7 deletions scripts/issue_records.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from dataclasses import dataclass
from datetime import date
from pathlib import Path
import posixpath
import re
from typing import TypeAlias

Expand All @@ -38,11 +39,21 @@
_LOCAL_ID = re.compile(r"ISSUE-LOCAL-([0-7][0-9A-HJKMNP-TV-Z]{25})\Z")
_ROW_ID = re.compile(r"[A-Z0-9][A-Za-z0-9_.-]*\Z")
_GITHUB_NUMBER = re.compile(r"[1-9][0-9]*\Z")
# The one frozen archive an intake record's Problem must quote, in
# repository-relative form. The record that HOLDS a quote lives deeper than
# the file the quote was cut from, which is why the comparison below
# resolves relative link targets instead of comparing their spelling: the
# same row is `../specs/x.md` in the archive and `../../specs/x.md` in the
# record, and both gates (this one and check-links) must be satisfiable at
# once.
FROZEN_ARCHIVE_RELPATH = ".agents/completed/issue-index.md"
_INTAKE_PROBLEM = re.compile(
r"Archive: `\.agents/completed/issue-index\.md:([1-9][0-9]*)`\n\n"
r"Archive: `" + re.escape(FROZEN_ARCHIVE_RELPATH) + r":([1-9][0-9]*)`\n\n"
r"### Frozen archive evidence\n\n"
r"> ([^\n]+)\Z"
)
_LINK_TARGET = re.compile(r"(\[[^\]]*\]\()([^)]+)(\))")
_REMOTE_TARGET = re.compile(r"[A-Za-z][A-Za-z0-9+.-]*:")


class IssueRecordError(ValueError):
Expand Down Expand Up @@ -303,19 +314,86 @@ def intake_archive_evidence(record: IssueRecord) -> tuple[int, str] | None:
return int(match.group(1)), match.group(2)


def _resolve_relative_links(line: str, base: str) -> str:
"""Rewrite every relative Markdown link target to the file it denotes.

A relative link resolves against the file that QUOTES it, so one frozen
row cannot keep one spelling in the archive (`.agents/completed/`) and in
the record that quotes it (`.agents/issues/<owner>/`). Resolving both
sides to the repository-relative path they point at is what lets the
evidence comparison ask the question it means to ask -- is this the
archived row? -- instead of which directory holds the quote.

Remote (`https:`, `mailto:`, ...) and root-absolute (`/...`) targets are
left exactly as written: only a rebase of a relative target is
comparable, so a swapped remote URL, a truncated link, or any other
difference still fails the comparison byte for byte.
"""

def rewrite(match: re.Match[str]) -> str:
raw = match.group(2)
lead = raw[: len(raw) - len(raw.lstrip())]
trail = raw[len(raw.rstrip()) :]
target = raw.strip()
angled = target.startswith("<") and target.endswith(">")
if angled:
target = target[1:-1]
path, separator, fragment = target.partition("#")
if (
not path
or path.startswith(("/", "?"))
or _REMOTE_TARGET.match(path) is not None
):
return match.group(0)
resolved = posixpath.normpath(posixpath.join(base, path))
rendered = f"{resolved}{separator}{fragment}"
if angled:
rendered = f"<{rendered}>"
return f"{match.group(1)}{lead}{rendered}{trail}{match.group(3)}"

return _LINK_TARGET.sub(rewrite, line)


def _archive_evidence_matches_source(
evidence: tuple[int, str],
frozen_archive: bytes | None,
record_base: str = "",
) -> bool:
"""Require exact UTF-8 evidence bytes at the declared one-based source line."""
"""Require the declared line to be the quote, modulo relative link rebase.

Byte equality is tried first and answers almost every record: an archive
that has not moved and a record that copied the line verbatim need
nothing resolved. Only when the bytes differ is the quote compared with
each side's relative link targets resolved, which admits exactly the
spelling a MOVE forces (`../specs/x.md` vs `../../specs/x.md`) and
nothing else. `record_base` is the record's repository-relative
directory; with no base to resolve against, only byte equality passes.

A trailing CR on the archived line is stripped before comparing: the
committed blob is LF, but a Windows working copy checks the archive out
CRLF, and byte equality must answer the CONTENT of the line, not which
checkout read it. The quote side is parsed from record text, so it has
no CR to strip.
"""

if frozen_archive is None:
return False
line_number, archived_line = evidence
source_lines = frozen_archive.split(b"\n")
if line_number > len(source_lines):
return False
return source_lines[line_number - 1] == archived_line.encode("utf-8")
source_line = source_lines[line_number - 1].removesuffix(b"\r")
if source_line == archived_line.encode("utf-8"):
return True
if not record_base:
return False
try:
decoded = source_line.decode("utf-8")
except UnicodeDecodeError:
return False
return _resolve_relative_links(
decoded, posixpath.dirname(FROZEN_ARCHIVE_RELPATH)
) == _resolve_relative_links(archived_line, record_base)


def _archive_row_owner(line: str, github: int | None) -> str | None:
Expand All @@ -342,19 +420,57 @@ def _archive_row_owner(line: str, github: int | None) -> str | None:
def valid_intake_archive_evidence(
record: IssueRecord,
frozen_archive: bytes | None = None,
record_base: str = "",
) -> tuple[int, str] | None:
"""Return evidence only when exact source bytes identify ownerless self."""
"""Return evidence only when the source line identifies ownerless self.

`record_base` is the record's repository-relative directory, so a quote
whose links were re-pointed to resolve from there still matches the line
it was cut from; see `_resolve_relative_links`.
"""

evidence = intake_archive_evidence(record)
if evidence is None or not _archive_evidence_matches_source(
evidence,
frozen_archive,
record_base,
):
return None
_, line = evidence
return evidence if _archive_row_owner(line, record.github) in {"", "-", "—"} else None


def _quoted_evidence_errors(
evidence: tuple[int, str] | None,
frozen_archive: bytes | None,
record_base: str,
github: int | None,
) -> list[str]:
"""Contract errors for a record that QUOTES an archived row.

Wherever a record carries a Frozen archive evidence block -- _intake,
_owed, or row-owned -- the quote must be the line it declares, modulo
the relative-link rebase the record's directory forces, and the line
must be about this record's own GitHub number. Absence of the block is
legal everywhere except _intake; presence is not, in any owner
directory. Measured over the corpus at d15b1cc09 with the 27 dropped
archive rows restored (GATE-ISSUE-ARCHIVE-RESTORE): all 831 existing
blocks satisfy both halves, so the ratchet adds no new red.
"""

if evidence is None:
return []
if not _archive_evidence_matches_source(evidence, frozen_archive, record_base):
return [
"Frozen archive evidence must equal the declared line in the frozen "
"archive source (a relative link may differ only by spelling, and "
"must resolve to the same file)"
]
if _archive_row_owner(evidence[1], github) is None:
return ["Frozen archive evidence must identify this issue"]
return []


def validate_issue_record(
record: IssueRecord,
path: str | Path,
Expand Down Expand Up @@ -455,7 +571,7 @@ def validate_issue_record(
errors.append(f"{name} must be UNKNOWN for Availability METADATA_ONLY")
if normalize_body(record.resolution).strip() != "-":
errors.append("Resolution must be - for Availability METADATA_ONLY")
archive = ".agents/completed/issue-index.md"
archive = FROZEN_ARCHIVE_RELPATH
exact_number = (
record.github is not None
and re.search(rf"(?<![0-9])#{record.github}(?![0-9])", record.problem)
Expand All @@ -470,6 +586,11 @@ def validate_issue_record(
)

owner = path.parent.name
# The record's own directory, repository-relative, so the frozen-evidence
# comparison can resolve a re-pointed relative link against where the
# quote lives instead of where the quote was cut from. Two levels under
# .agents/, against the archive's one.
record_base = f".agents/issues/{owner}"
owed_count = _owed_count(owed, record.id)
if owner == "_intake":
intake_shape = (
Expand Down Expand Up @@ -498,10 +619,13 @@ def validate_issue_record(
archived_owner = _archive_row_owner(archived_line, record.github)
if archived_owner not in {"", "-", "—"}:
errors.append("_intake frozen evidence must contain archived Row -")
if not _archive_evidence_matches_source(evidence, frozen_archive):
if not _archive_evidence_matches_source(
evidence, frozen_archive, record_base
):
errors.append(
"_intake Frozen archive evidence must equal the declared line "
"in the frozen archive source"
"in the frozen archive source (a relative link may differ only "
"by spelling, and must resolve to the same file)"
)
if owed_count:
errors.append("_intake must not have an owning spec reference")
Expand All @@ -510,13 +634,29 @@ def validate_issue_record(
errors.append("Row must be - for a file under _owed")
if owed_count != 1:
errors.append("an _owed issue must have exactly one owning spec reference")
errors.extend(
_quoted_evidence_errors(
intake_archive_evidence(record),
frozen_archive,
record_base,
record.github,
)
)
else:
if record.row != owner:
errors.append(f"path row {owner!r} must equal Row field {record.row or '-'}")
elif not _claimable(rows, owner):
errors.append(f"row {owner!r} is not canonical and claimable")
if owed_count:
errors.append("a row-owned issue must not retain an owed reference")
errors.extend(
_quoted_evidence_errors(
intake_archive_evidence(record),
frozen_archive,
record_base,
record.github,
)
)

if errors:
raise IssueRecordError("; ".join(errors))
Expand Down
Loading
Loading