register: reclassify under R1 -- name every instrument, re-open the ten our own killed
Both agents asked for this and neither could do it: the register is the file they both read to decide what NOT to try, so two agents agreeing is not the authority for changing it (RETRO-2026-08-31-agreed §7.1). R1: a refutation whose instrument is one of our renderers is not a refutation, it is "our renderer disagrees" -- 🟡, not ❌. The motivating case was not careless work. "Blending those sprites additively worsens every measure against the capture" killed a real disc field for weeks, and read exactly like a publishable negative; the renderer behind it had a stale keyframe association, no leaf geometry and no rotation. Nothing in the entry could have told you. All 222 entries now end with ⟨instrument⟩, read off each entry's OWN stated evidence -- never inferred. An entry that states none gets `unrecorded`, which is 83 of them. Ten moved ❌ -> 🟡, each naming what would settle it: 8 render-vs-capture, 1 our-reader, 1 harness. Three things the pass turned up: * The rest() question is OPEN and had been reading as settled in both directions -- "rest = last keyframe" was refuted by a sibling argument, and that refutation refuted by correlating our render against captures. Both legs are our renderer. Which one you believed depended on which entry you found first, and it decides the pose every plateau-less element is drawn at. * A withdrawal never reached its sibling: "2 391 frames, max glyph 0" was withdrawn because a long-lived x11grab stream freezes and repeats a stale frame; the 1 674-sample negative three lines above it, same probe, was left standing as a reinstated measurement. * 83 of 222 -- 37 % -- record no instrument at all. Not disputed, not safe: unauditable, and larger than every other group combined. tools/stale-instrument is the --stale query the rule needs, because a colour alone re-opens nothing: the failure was that nothing re-opened a claim when the instrument that killed it improved. Its own --check found a real bug on the first run -- sys.exit("text") exits 1, so three faults documented as exit 2 were reporting the same code as an ordinary miss. R1 is now standing text in PROTOCOL.md, with R5's exception: our tool is the right instrument for a question about our tool.
This commit is contained in:
@@ -133,6 +133,42 @@ Refutation is cheapest where the other agent is most confident. Prefer:
|
||||
centroid estimator that is 19.8° out on a known rotation cannot measure an
|
||||
unknown one. A filter that fails its own known-positive is dead, not tuneable.
|
||||
|
||||
⚠️ **And a control verifies CAPABILITY, not CONFIGURATION.** Both agents ran
|
||||
controls and both were still wrong: one tested whether the method *can* detect a
|
||||
blend difference, not whether *that run* had `blend_mode` set — it was left at
|
||||
the engine default. The other tested whether NDC→pixel conversion is right, not
|
||||
whether the dump captured all six quads; it captured two, with a well-formed line
|
||||
and no ellipsis. Assert the run's configuration, not just the method's power.
|
||||
|
||||
### R1 — a refutation is only as good as its instrument
|
||||
|
||||
**A refutation whose instrument is one of our renderers is not a refutation.** It
|
||||
is *"our renderer disagrees"* — 🟡, not ❌.
|
||||
|
||||
Agreed by both agents 2026-08-31, applied to
|
||||
[`../re/REFUTED.md`](../re/REFUTED.md) by the human on 2026-09-01. The register
|
||||
is not yours to reclassify: it is the file you both read to decide what *not* to
|
||||
try, and two agents agreeing is not the authority for changing it. Propose;
|
||||
do not enact.
|
||||
|
||||
What this asks of you, in practice:
|
||||
|
||||
* **Every claim you retire names its instrument** — `⟨capture⟩`, `⟨disc⟩`,
|
||||
`⟨image⟩`, `⟨render-vs-capture⟩`, `⟨harness⟩`… The register's reading guide
|
||||
holds the vocabulary and says which tags are ours.
|
||||
* **When you improve a renderer, a reader or the harness, run
|
||||
`tools/stale-instrument <that instrument>`.** It lists what that instrument
|
||||
killed. Those claims re-open. This is the mechanism the rule exists for: the
|
||||
motivating failure was not that anyone was careless, it is that **nothing
|
||||
re-opened a claim when the instrument that killed it improved**, and a real
|
||||
disc field sat dead for weeks as a result.
|
||||
* **A 🟡 carries what would settle it.** A re-opened claim with no next
|
||||
experiment is an unanswered question wearing a colour.
|
||||
|
||||
The exception, from R5: our tool is the right instrument for a question **about
|
||||
our tool**. *"Can `screen render` draw the developer splash?"* is ours to answer.
|
||||
*"What does the game draw?"* is not.
|
||||
|
||||
**Disagreements escalate to the human with both positions.** They are not
|
||||
resolved by seniority, by who wrote it down first, or by whoever is more certain.
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
255
tools/stale-instrument
Executable file
255
tools/stale-instrument
Executable file
@@ -0,0 +1,255 @@
|
||||
#!/usr/bin/env python3
|
||||
"""What did this instrument kill? -- the re-open query for `docs/re/REFUTED.md`.
|
||||
|
||||
tools/stale-instrument everything, grouped
|
||||
tools/stale-instrument render-vs-capture what one instrument killed
|
||||
tools/stale-instrument --ours only instruments that are ours
|
||||
tools/stale-instrument --check self-test; exits non-zero if broken
|
||||
|
||||
Why this exists
|
||||
---------------
|
||||
Rule R1. A refutation whose instrument is one of our renderers is not a
|
||||
refutation -- it is *"our renderer disagrees"*. The register carries that as a
|
||||
🟡, but a colour alone does not re-open anything: the failure it is there to
|
||||
prevent was a real disc field sitting dead for weeks because **nothing re-opens a
|
||||
claim when the instrument that killed it improves.**
|
||||
|
||||
So this is the query you run *after* you fix a renderer, a reader or the harness.
|
||||
It answers "what did I just invalidate?" in one command, instead of relying on
|
||||
someone remembering which of 222 entries rested on the thing they changed.
|
||||
|
||||
The register is the only source. There is no second list to drift out of sync
|
||||
with it -- the tags live at the end of each entry, in the file people actually
|
||||
read, which is this corpus's own repeatedly-learned lesson about where a fact has
|
||||
to live to exist.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from collections import OrderedDict
|
||||
|
||||
REGISTER = os.environ.get("SYLPH_REGISTER", "docs/re/REFUTED.md")
|
||||
|
||||
# Ours, in the sense R1 means: an instrument whose defects we cannot rule out,
|
||||
# because we built it. `our-tool` is ours too, but it is not disqualifying --
|
||||
# see the note below and in the register's reading guide.
|
||||
OURS = {"render-vs-capture", "screen-render", "our-reader", "harness", "our-tool"}
|
||||
|
||||
# Not a verdict. It means nobody recorded how the claim died.
|
||||
UNRECORDED = "unrecorded"
|
||||
|
||||
TAG = re.compile(r"⟨([a-z][a-z-]*)⟩\s*$")
|
||||
|
||||
|
||||
def entries(text: str):
|
||||
"""Every bullet, with its instrument tag and whether it is already 🟡.
|
||||
|
||||
An entry is a `* ` bullet plus its continuation lines, ending at a blank
|
||||
line or a heading -- the same span rule the register's own enumeration uses.
|
||||
"""
|
||||
out, cur, start = [], None, 0
|
||||
for i, line in enumerate(text.split("\n"), 1):
|
||||
if line.startswith("* "):
|
||||
if cur is not None:
|
||||
out.append((start, cur))
|
||||
cur, start = [line], i
|
||||
elif line.startswith("#"):
|
||||
if cur is not None:
|
||||
out.append((start, cur))
|
||||
cur = None
|
||||
elif cur is not None:
|
||||
if not line.strip():
|
||||
out.append((start, cur))
|
||||
cur = None
|
||||
else:
|
||||
cur.append(line)
|
||||
if cur is not None:
|
||||
out.append((start, cur))
|
||||
|
||||
for ln, body in out:
|
||||
joined = " ".join(x.strip() for x in body)
|
||||
m = TAG.search(body[-1].rstrip())
|
||||
# 🔴 THE VERDICT IS THE LEADING GLYPH, NOT ANY 🟡 IN THE ENTRY. Matching
|
||||
# anywhere counted four entries as re-opened that merely carry an inline
|
||||
# 🟡 caveat mid-sentence ("🟡 a third recorded run implies NEW GAME").
|
||||
# An entry whose verdict is ❌ and whose *reach* is qualified is not an
|
||||
# open claim, and reporting it as one inflates exactly the number this
|
||||
# tool exists to make trustworthy.
|
||||
yield {
|
||||
"line": ln,
|
||||
"instrument": m.group(1) if m else None,
|
||||
"open": body[0].startswith("* 🟡"),
|
||||
"settled": body[0].startswith("* ✅"),
|
||||
"text": re.sub(r"\s+", " ", joined),
|
||||
}
|
||||
|
||||
|
||||
def summary(text: str) -> str:
|
||||
"""The claim, short enough to scan a list of them."""
|
||||
t = re.sub(r"^\*\s*(🟡|❌|✅)?\s*", "", text)
|
||||
t = re.sub(r"[*`~]", "", t)
|
||||
return (t[:118] + "…") if len(t) > 118 else t
|
||||
|
||||
|
||||
def die(msg: str):
|
||||
"""A HARNESS fault, exit 2 -- distinct from 'no such instrument', exit 1.
|
||||
|
||||
🔴 `sys.exit("text")` exits **1**, not 2. Every one of these sites used it,
|
||||
so three faults that this file's own docstring calls exit-2 were reporting
|
||||
the same code as an ordinary miss -- a real failure wearing a wrong
|
||||
diagnosis, the exact shape the register catalogues elsewhere. Its own
|
||||
`--check` caught it on the first run, which is the argument for having one.
|
||||
"""
|
||||
print(msg, file=sys.stderr)
|
||||
raise SystemExit(2)
|
||||
|
||||
|
||||
def load():
|
||||
if not os.path.exists(REGISTER):
|
||||
die(
|
||||
f"stale-instrument: {REGISTER} is not here.\n"
|
||||
" Run from the repository root, or set SYLPH_REGISTER.\n"
|
||||
" Exit 2: the harness is broken, not the corpus."
|
||||
)
|
||||
rows = list(entries(open(REGISTER, encoding="utf-8").read()))
|
||||
# 🔴 LIVENESS. A reader that parses nothing reports "no claims rest on that
|
||||
# instrument" -- which reads exactly like the good news you were hoping for.
|
||||
# A renamed file, a changed bullet shape or a lost tag all produce it. This
|
||||
# corpus has already produced one false zero from a reader invented minutes
|
||||
# earlier, so the empty case is a harness fault with its own exit code.
|
||||
if not rows:
|
||||
die("stale-instrument: parsed 0 entries from "
|
||||
f"{REGISTER} -- exit 2, the reader is broken.")
|
||||
if not any(r["instrument"] for r in rows):
|
||||
die(f"stale-instrument: {len(rows)} entries, none tagged ⟨…⟩ -- "
|
||||
"exit 2, the reader is broken or the register is untagged.")
|
||||
return rows
|
||||
|
||||
|
||||
def main(argv: list[str]) -> int:
|
||||
if "--check" in argv:
|
||||
return selftest()
|
||||
|
||||
rows = load()
|
||||
only_ours = "--ours" in argv
|
||||
wanted = [a for a in argv if not a.startswith("-")]
|
||||
|
||||
by = OrderedDict()
|
||||
for r in rows:
|
||||
by.setdefault(r["instrument"] or UNRECORDED, []).append(r)
|
||||
|
||||
if wanted:
|
||||
name = wanted[0]
|
||||
if name not in by:
|
||||
print(f"no entry names ⟨{name}⟩. Known instruments:")
|
||||
for k in sorted(by):
|
||||
print(f" {k}")
|
||||
return 1
|
||||
group = by[name]
|
||||
mine = " -- OURS, so these are re-openable" if name in OURS else ""
|
||||
print(f"⟨{name}⟩ killed {len(group)} claim(s){mine}\n")
|
||||
for r in group:
|
||||
mark = "🟡" if r["open"] else ("✅" if r["settled"] else "❌")
|
||||
print(f" {mark} L{r['line']:<4} {summary(r['text'])}")
|
||||
if name in OURS:
|
||||
print(f"\n If you have improved {name}, every ❌ above is a claim that"
|
||||
"\n died to an instrument that no longer exists in that form.")
|
||||
return 0
|
||||
|
||||
print(f"{len(rows)} entries in {REGISTER}\n")
|
||||
order = sorted(by, key=lambda k: (k not in OURS, k == UNRECORDED, -len(by[k])))
|
||||
for k in order:
|
||||
g = by[k]
|
||||
tag = " ← OURS" if k in OURS else (" ← nobody wrote it down"
|
||||
if k == UNRECORDED else "")
|
||||
opened = sum(1 for r in g if r["open"])
|
||||
note = f", {opened} already 🟡" if opened else ""
|
||||
print(f" {k:20s} {len(g):3d} claim(s){note}{tag}")
|
||||
|
||||
ours = sum(len(by[k]) for k in by if k in OURS)
|
||||
unrec = len(by.get(UNRECORDED, []))
|
||||
print(f"\n {ours} claim(s) died to an instrument WE BUILT.")
|
||||
print(f" {unrec} claim(s) record no instrument at all -- "
|
||||
"neither safe nor suspect,\n just unauditable. That is the backfill queue.")
|
||||
print("\n tools/stale-instrument <instrument> to list one")
|
||||
return 0
|
||||
|
||||
|
||||
def selftest() -> int:
|
||||
"""Cases that must hold, executed -- not reasoned about.
|
||||
|
||||
A control that does not run is not a control; this file's neighbours in
|
||||
`tools/` say so about other people's tools, so it is asserted here.
|
||||
"""
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
me = os.path.abspath(__file__)
|
||||
ok = 0
|
||||
|
||||
def run(reg, args=()):
|
||||
env = dict(os.environ, SYLPH_REGISTER=reg)
|
||||
p = subprocess.run([sys.executable, me, *args], capture_output=True,
|
||||
text=True, env=env)
|
||||
return p.returncode, p.stdout + p.stderr
|
||||
|
||||
with tempfile.TemporaryDirectory() as d:
|
||||
good = os.path.join(d, "good.md")
|
||||
open(good, "w", encoding="utf-8").write(
|
||||
'* "a" → refuted. ⟨disc⟩\n\n* 🟡 "b" — our renderer disagrees. ⟨render-vs-capture⟩\n'
|
||||
)
|
||||
cases = [
|
||||
("a tagged register parses", (good, ()), 0, "2 entries"),
|
||||
("one instrument lists", (good, ("disc",)), 0, '"a"'),
|
||||
("an OURS instrument says so", (good, ("render-vs-capture",)),
|
||||
0, "re-openable"),
|
||||
("an unknown instrument fails", (good, ("nope",)), 1, "no entry names"),
|
||||
]
|
||||
for name, (reg, args), want_rc, want_txt in cases:
|
||||
rc, out = run(reg, args)
|
||||
good_rc = rc == want_rc
|
||||
good_tx = want_txt in out
|
||||
if good_rc and good_tx:
|
||||
print(f" {name:34s} exit {rc} ✅")
|
||||
else:
|
||||
print(f" {name:34s} exit {rc} (wanted {want_rc}), "
|
||||
f"text {'found' if good_tx else 'MISSING'} 🔴")
|
||||
ok = 1
|
||||
|
||||
# The two harness faults, which must be distinguishable from a clean run.
|
||||
empty = os.path.join(d, "empty.md")
|
||||
open(empty, "w").write("# nothing here\n")
|
||||
rc, out = run(empty)
|
||||
if rc == 2 and "reader is broken" in out:
|
||||
print(f" {'an empty register is a fault':34s} exit 2 ✅")
|
||||
else:
|
||||
print(f" {'an empty register is a fault':34s} exit {rc}, wanted 2 🔴")
|
||||
ok = 1
|
||||
|
||||
untagged = os.path.join(d, "untagged.md")
|
||||
open(untagged, "w", encoding="utf-8").write('* "a" → refuted.\n')
|
||||
rc, out = run(untagged)
|
||||
if rc == 2 and "none tagged" in out:
|
||||
print(f" {'an untagged register is a fault':34s} exit 2 ✅")
|
||||
else:
|
||||
print(f" {'an untagged register is a fault':34s} exit {rc}, wanted 2 🔴")
|
||||
ok = 1
|
||||
|
||||
rc, _ = run(os.path.join(d, "does-not-exist.md"))
|
||||
if rc == 2:
|
||||
print(f" {'a missing register is a fault':34s} exit 2 ✅")
|
||||
else:
|
||||
print(f" {'a missing register is a fault':34s} exit {rc}, wanted 2 🔴")
|
||||
ok = 1
|
||||
|
||||
print()
|
||||
print("the query fails when it must, and says so distinctly" if not ok
|
||||
else "🔴 the query's own control is broken")
|
||||
return ok
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
Reference in New Issue
Block a user