gate: check 7 (register citations) + content WARNings on the memory index
gates / gates (push) Successful in 8s

Check 7 catches "cites a register item and calls it open when it is not" -- the R-168 class, four
files, one self-contradicting. Trigger is an openness CLAIM, not any citation: policing every
mention would fire on ~30 legitimate provenance citations and the gate would be switched off.
Deliberate deviation from the task's literal wording, to keep it alive.

Two bugs found by the check's own red-proofs, both of which would have shipped:
- the state marker is not self-closing (**SHIPPED - text**), so the first parser read R-168 itself
  as OPEN -- a gate that cannot convict its founding case is decoration;
- the CLOSED exemption was line-wide, so "shipped" in a title pardoned "OPEN R-25b".

Check 6 gains WARN-only content classes on MEMORY.md. Link targets are stripped first: the earlier
scan reported three expired statements, all three false (dates in filenames), while missing the one
real expired claim, whose deadline was written ~08-02 with no ISO date.

39 -> 60 assertions. All four runners green.
This commit is contained in:
2026-08-06 11:39:34 +02:00
parent 92a076c239
commit 15fa5273ba
3 changed files with 434 additions and 1 deletions
+159
View File
@@ -295,6 +295,165 @@ def test_memory_store_without_index_fails():
check("message says unreachable knowledge", "unreachable knowledge" in out)
# --- check 7: register citations ----------------------------------------------------------------
#
# The founding case is "CI is still owed (R-168)" shipped four days after R-168 closed, in four
# files, one of which contradicted itself. The trigger is an OPENNESS CLAIM, not any citation:
# requiring every mention of a non-open item to carry "CLOSED" would fire on ~30 legitimate
# provenance citations like "(R-161)" or "R-117 spike §6.3", and a gate that noisy gets switched
# off — which is the fate R-29 documented.
def make_register(tmp, rows):
"""rows: list of (item, what, state-cell). Mirrors the real 6-column table shape."""
d = os.path.join(tmp, "felhom.eu", "documentation", "backlog")
os.makedirs(d, exist_ok=True)
body = ["| ID | What | State | Blocked on | Next action | Owner |", "|---|---|---|---|---|---|"]
for item, what, st in rows:
body.append("| **%s** | %s | %s | — | — | operator |" % (item, what, st))
with open(os.path.join(d, "OPEN-ITEMS.md"), "w", encoding="utf-8") as fh:
fh.write("\n".join(body) + "\n")
with open(os.path.join(d, "ROADMAP.md"), "w", encoding="utf-8") as fh:
fh.write("| R-900 | a historical item, closed long ago and pruned from OPEN-ITEMS |\n")
REG_ROWS = [
("R-168", "~~CI: no runner exists~~", "**SHIPPED — and the alarm is DEMONSTRATED** (2026-08-02)"),
("R-161", "volume-persistence gate", "**REDUCED SCOPE — open** (operator ruling)"),
]
def test_citation_claiming_open_about_a_closed_item_fails():
"""The founding case, verbatim."""
with tempfile.TemporaryDirectory() as tmp:
make_register(tmp, REG_ROWS)
root = make_repo(tmp, "# Repo\n\nBoth facts are why CI is still owed (`OPEN-ITEMS.md` R-168).\n")
rc, out = run_gate(root)
check("stale-open citation exits non-zero", rc != 0, "rc=%d" % rc)
check("message names the item", "R-168" in out)
check("message says the register disagrees", "register says it is closed" in out)
def test_citation_that_says_closed_passes():
"""Explicitly required by the spec: a correct citation must survive."""
with tempfile.TemporaryDirectory() as tmp:
make_register(tmp, REG_ROWS)
root = make_repo(
tmp, "# Repo\n\na bypass is noticed even though it is not blocked (R-168, CLOSED 2026-08-02).\n"
)
rc, out = run_gate(root)
check("citation saying CLOSED exits 0", rc == 0, "rc=%d" % rc)
def test_bare_provenance_citation_of_a_closed_item_passes():
"""Load-bearing negative: citing a closed item as the SOURCE OF A FACT is not a claim it is open."""
with tempfile.TemporaryDirectory() as tmp:
make_register(tmp, REG_ROWS)
root = make_repo(tmp, "# Repo\n\nThe canonical runner shape (R-168) is what survives.\n")
rc, out = run_gate(root)
check("provenance citation of a closed item exits 0", rc == 0, "rc=%d" % rc)
def test_openness_claim_about_an_actually_open_item_passes():
with tempfile.TemporaryDirectory() as tmp:
make_register(tmp, REG_ROWS)
root = make_repo(tmp, "# Repo\n\n**R-161 stays open at reduced scope** — enforcement is by convention.\n")
rc, out = run_gate(root)
check("openness claim about an OPEN item exits 0", rc == 0, "rc=%d" % rc)
def test_closed_word_elsewhere_on_the_line_does_not_pardon_a_stale_claim():
"""Regression. The exemption is scoped to the citation's CLAUSE, not the whole line.
`- [Customer RESET shipped](customer-reset-shipped-2026-07-17.md) — …; OPEN R-25b` contains
"shipped" twice — once in a title, once in a filename — and a line-wide exemption pardoned the
stale claim sitting at the end of it. Found by this check's own red-proof failing to go red,
which is the only reason it was found at all."""
with tempfile.TemporaryDirectory() as tmp:
make_register(tmp, REG_ROWS)
root = make_repo(
tmp,
"# Repo\n\n- [Customer RESET shipped](customer-reset-shipped-2026-07-17.md) — "
"teardown first; OPEN R-168\n",
)
rc, out = run_gate(root)
check("a distant 'shipped' does not pardon the claim", rc != 0, "rc=%d" % rc)
check("the stale item is still named", "R-168" in out)
def test_citation_of_an_item_in_no_register_fails():
with tempfile.TemporaryDirectory() as tmp:
make_register(tmp, REG_ROWS)
root = make_repo(tmp, "# Repo\n\nSee R-4242 for the rationale.\n")
rc, out = run_gate(root)
check("citation of a nonexistent item exits non-zero", rc != 0, "rc=%d" % rc)
check("message says it is in neither register", "NEITHER" in out)
def test_citation_in_an_html_comment_is_not_policed():
"""Comments are stripped before injection, so a historical citation parked there costs nothing."""
with tempfile.TemporaryDirectory() as tmp:
make_register(tmp, REG_ROWS)
root = make_repo(tmp, "# Repo\n\n<!-- history: CI was still owed (R-168) at the time -->\n")
rc, out = run_gate(root)
check("citation inside a comment is not policed", rc == 0, "rc=%d" % rc)
def test_citation_check_covers_rule_files_too():
with tempfile.TemporaryDirectory() as tmp:
make_register(tmp, REG_ROWS)
root = make_repo(
tmp, "# Repo\n",
rules={"g.md": '---\npaths: ["**/*.go"]\n---\n\nCI is still owed (R-168).\n'},
)
rc, out = run_gate(root)
check("a rule file's stale citation also fails", rc != 0, "rc=%d" % rc)
check("message names the rule file", "g.md" in out)
# --- check 6: content WARNings on the memory index ------------------------------------------------
def test_memory_expired_statement_warns_but_does_not_fail():
with tempfile.TemporaryDirectory() as tmp:
make_memory(tmp, "# Index\n- [a](a.md) — demo boxes REMOTE till ~08-02\n", topics={"a.md": "# A\n"})
root = make_repo(tmp, "# Repo\n")
rc, out = run_gate(root)
check("expired statement in the index does NOT fail", rc == 0, "rc=%d" % rc)
check("expired statement WARNs", "EXPIRED statement" in out)
def test_memory_date_inside_a_link_target_is_not_an_expired_statement():
"""THE load-bearing negative. On 2026-08-06 a scan matched the ISO date in the link TARGET and
reported three expired statements, every one of them false, while missing the one real expired
claim in the same file — whose deadline was written `~08-02` with no ISO date at all."""
with tempfile.TemporaryDirectory() as tmp:
make_memory(tmp, "# Index\n- [Customer RESET](customer-reset-shipped-2026-07-17.md) — teardown first\n",
topics={"customer-reset-shipped-2026-07-17.md": "# X\n"})
root = make_repo(tmp, "# Repo\n")
rc, out = run_gate(root)
check("a dated FILENAME is not an expired statement", rc == 0 and "EXPIRED" not in out, "rc=%d" % rc)
def test_memory_future_deadline_does_not_warn():
with tempfile.TemporaryDirectory() as tmp:
make_memory(tmp, "# Index\n- [a](a.md) — rig stays up until 2026-12-31\n", topics={"a.md": "# A\n"})
root = make_repo(tmp, "# Repo\n")
rc, out = run_gate(root)
check("a future deadline does not warn", "EXPIRED" not in out)
def test_memory_version_literal_and_address_warn_but_do_not_fail():
with tempfile.TemporaryDirectory() as tmp:
make_memory(tmp, "# Index\n- [a](a.md) — ctrl 0.162.0 on 192.168.0.180\n", topics={"a.md": "# A\n"})
root = make_repo(tmp, "# Repo\n")
rc, out = run_gate(root)
check("version literal in the index does NOT fail", rc == 0, "rc=%d" % rc)
check("version literal WARNs", "version literal" in out)
check("host address WARNs", "host address" in out)
check("loopback is not reported", "127.0.0.1" not in out)
def main():
print("test_instructions_gate")
for fn in sorted(