From 3a048de5362a5b5e4f23a96845f4506dba312ac6 Mon Sep 17 00:00:00 2001 From: Sylpheed port agent Date: Mon, 31 Aug 2026 02:12:56 +0000 Subject: [PATCH] port: every one of my checkers passed on an empty input The Decoder generalised my empty-band case into the rule I now keep: a control that only compares two things cannot tell you the comparison is happening. An empty band list, a blank frame, an empty register -- each makes a checker agreeable rather than wrong, and agreeable is indistinguishable from correct in a log. Swept my tools against inputs containing nothing. audit-kinds exited 0 on a tree with no authored/*.json, having printed '0 kind label(s)' and reported clean. verify-transcode-fidelity would call every transcode faithful with no videos in the manifest, having compared none. check-claims exited 1 from a FileNotFoundError inside the withdrawal hook -- which in that script's own vocabulary means 'a refuted claim is still being asserted', so a wrong directory got diagnosed as a dirty corpus. A real failure with a fabricated reason, the third instance of that family after my control anchoring at the wrong document. All three now exit 2, check-claims via a preflight that names the roots it needs. Both self-tests gained the liveness case driven as subprocesses: audit-kinds --selftest runs itself in an empty directory and requires 2, and check-claims --control is now six cases -- clean 0, unmarked 1, marked 0, outside-root 0, empty register 2, nothing to scan 2. What makes this worth an iteration rather than tidying: none of these tools was ever wrong on real input. What none of them could do was tell 'I checked and it was fine' from 'I checked nothing', and every green line I have quoted was the first of those only because the directory happened to be right. Also recorded: their ring_row.py used 'main_menu_item(ring_row(f)) is not None' as a main-menu test, and a TITLE frame passes it -- the gutter carries a bright cluster at y=243 inside tolerance of row 0. No result they sent me is affected, for a structural reason rather than a lucky one: (B) from a submenu goes to the menu, never the title, so the weak test was never shown the frame that breaks it. I have not re-derived their focus results and am not treating this as a reason to; what I have is their statement of the exposure and the structural argument, recorded as that rather than as verification. Every asserting check passes, 14 of them. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF --- docs/port/BLOCKED.md | 6 +++ docs/port/DECISIONS.md | 59 +++++++++++++++++++++++++++- tools/port/audit-kinds | 26 +++++++++++- tools/port/check-claims | 40 ++++++++++++++++++- tools/port/verify-transcode-fidelity | 7 ++++ 5 files changed, 135 insertions(+), 3 deletions(-) diff --git a/docs/port/BLOCKED.md b/docs/port/BLOCKED.md index 191ea789..319137f8 100644 --- a/docs/port/BLOCKED.md +++ b/docs/port/BLOCKED.md @@ -147,6 +147,12 @@ HANDOFF. | ~~P1–P7 — the keyframe record layout~~ | ~~adopt the corrected pose/time pairing~~ | — | ✅ **ADOPTED 2026-08-29 by pinning `formats-pin-2026-08-29c`.** This row was wrong twice: it said the change *"cannot be taken yet"* and that it *"reaches the port only when that branch lands on `main`"*. **It arrives when the tag is pinned**, which is what MISSION §2's tagging rule exists for. ⚠️ And the knob I tested first, `SYLPHEED_KF_TIME_SHIFT`, is a **retired partial fix** that left pose 0 untimed — the real correction is the tagged crate's default, with the old reading behind `SYLPHEED_KF_TIME_LEGACY=1`. **The blast radius was far smaller than this row predicted**: under the correction *every pose is timed* (866 keyframes, 0 untimed), so `pose_at`'s synthetic-exit branch became dead code rather than wrong code and nothing needed re-deriving. Oracle: `publisher_logo` 1.00 %→**0.75 %**, `developer_logos` 0.39 %→**0.33 %**, `extras`' differing region collapsing from 736×525 to **398×295 at the sweep position**. 🔴 Open cost: `sylpheed-cli` builds from the workspace crate, so `verify-screen` compares two decoder eras until the tag reaches `main`. Revert to the path dependency then. | | ~~P7 / naming — the four unnamed builds~~ | ~~which locale and variant is each of entries 0, 1, 12, 15?~~ | — | ✅ **answered 2026-08-29** (`docs/re/ui-title-build-map.md`): all four are the loading screen, two variants — plain (7 elements) and dressed (10) — decoded from their own `pgloading_*` element names. ⚠️ **Not adopted as names yet, for two reasons the RE agent gave and one the port found.** Theirs: the executable names exactly two, and *which* bundle takes which name is 🟡 undecided, so `LOADING`/`LOADING2` must not go in an asset path; and locale is 🟡 — the English member of a pair is the one in the first half of `GP_TITLE.p00`, 8/8 structurally but only 3/3 where a capture can check, and the three pairs that matter are the three no capture can check. Mine: **the message gives the bundles as "0/1 and 10/11", which is the `is_build` ordinal, and `authored/screen_names.json` is keyed by PAK ENTRY** — in entry space 10 and 11 are `palogo_sqex` and `palogo_gamearts`, the splashes. See the refutation section in `DECISIONS.md`. | +## Closed on my own tooling, 2026-08-31 — derived from HANDOFF `abeea3b` + +| Milestone | Needs | HANDOFF | State | +|---|---|---|---| +| ~~all — checkers that pass on an empty input~~ | ~~liveness guards~~ | `abeea3b` | ✅ **SWEPT AND FIXED.** Every one of my tools reported clean when it examined **nothing**: `audit-kinds` exited **0** on a tree with no `authored/*.json`; `verify-transcode-fidelity` would call every transcode faithful with no videos in the manifest; `check-claims` exited **1** from a `FileNotFoundError` in the withdrawal hook — *"a refuted claim is still being asserted"* as the diagnosis for **a wrong directory**, a real failure with a fabricated reason and the third of that family. All three now exit **2**, and both self-tests assert the liveness case as subprocesses (`check-claims --control` is six cases). 📌 None of these tools was ever wrong on real input; what none could do was tell *"I checked and it was fine"* from *"I checked nothing"*. | + ## Half-answered, 2026-08-31 — derived from HANDOFF (today's `DIFFICULTY` delivery) | Milestone | Needs | HANDOFF | State | diff --git a/docs/port/DECISIONS.md b/docs/port/DECISIONS.md index 97197bcd..3f483f64 100644 --- a/docs/port/DECISIONS.md +++ b/docs/port/DECISIONS.md @@ -9,7 +9,7 @@ dies, which is what this file is for. -274 sections. Search this before re-deriving anything. +276 sections. Search this before re-deriving anything. * [P0 — the exporter, 2026-08-28](#p0--the-exporter-2026-08-28) * [P1 — Godot draws the screen, 2026-08-28](#p1--godot-draws-the-screen-2026-08-28) @@ -285,6 +285,8 @@ dies, which is what this file is for. * [Settled: a submenu resets to its OWN OPENING ITEM, not to its top item](#settled-a-submenu-resets-to-its-own-opening-item-not-to-its-top-item) * [Their refutation attempt on `extras/initial_focus` — checked against the bytes, twice](#their-refutation-attempt-on-extrasinitial_focus--checked-against-the-bytes-twice) * [Menu focus does not survive a reboot — and the reach matters more than the result](#menu-focus-does-not-survive-a-reboot--and-the-reach-matters-more-than-the-result) +* [Liveness: every one of my tools passed on an empty input](#liveness-every-one-of-my-tools-passed-on-an-empty-input) +* [Their `ring_row.py` defect, and why it did not reach me](#their-ring_rowpy-defect-and-why-it-did-not-reach-me) ## P0 — the exporter, 2026-08-28 @@ -13909,3 +13911,58 @@ an offset, essentially no scaling). **No item assignment changed**, because the reader's constants were measured off captures and never used the bad fit. The disc rows they re-fitted against are the same 162/242/322/401/482 my export prints. + +## Liveness: every one of my tools passed on an empty input + +The Decoder generalised my empty-band case into the rule I now keep: **a control +that only compares two things cannot tell you the comparison is happening.** An +empty band list, a blank frame, an empty register — each makes a checker +**agreeable rather than wrong**, and agreeable is indistinguishable from correct +in a log. + +Swept my own tools against inputs that contain nothing: + +| tool | before | now | +|---|---|---| +| `audit-kinds` | **exit 0** — printed *"0 kind label(s)"* and reported clean | **exit 2** | +| `check-claims` | **exit 1** from a `FileNotFoundError` inside the withdrawal hook | **exit 2**, via a preflight | +| `verify-transcode-fidelity` | manifest with no videos → loop never runs, *"every transcode faithful"* having compared none | **exit 2** | + +🔴 **The `check-claims` case is the one worth naming.** Run from the wrong +directory it died in the hook and exited **1** — which in that script's own +vocabulary means *"a refuted claim is still being asserted"*. **A real failure +with a fabricated diagnosis**, the same shape as my control anchoring at the +wrong document two iterations ago, and the third instance of that family. A +preflight now names the roots it needs and calls their absence a **harness** +fault. + +✅ Both self-tests gained the liveness case, driven as subprocesses so the real +exit code is read: `audit-kinds --selftest` runs itself in an empty directory and +requires 2; `check-claims --control` is now **six** cases — clean 0, unmarked 1, +marked 0, outside-root 0, empty register 2, **nothing to scan 2**. + +📌 What makes this worth an iteration rather than tidying: **none of these tools +was wrong.** Each produced correct output on real input, every time it ran. What +they could not do is tell the difference between *"I checked and it was fine"* and +*"I checked nothing"* — and every green line I have quoted in this file was the +first of those only because the directory happened to be right. + +## Their `ring_row.py` defect, and why it did not reach me + +Their liveness self-test found that `main_menu_item(ring_row(f)) is not None` was +being used as a main-menu test, and **a TITLE frame passes it** — the gutter +carries a bright cluster at y=243, inside tolerance of row 0, so the title reads +as `NEW GAME`. Glyph count separates them cleanly (714 against the menu's 327); +the ring row alone does not. + +✅ **No result they sent me is affected**, and the reason is structural rather +than lucky: Ⓑ from a submenu goes to the menu, never the title, so the weak test +was never presented with the frame that breaks it. **The test was weaker than +they were trusting it to be, not wrong in what it produced** — which is precisely +the state a self-test exists to expose *before* a screen sequence changes and it +starts mattering. + +⚠️ I have not re-derived their focus results, and I am not treating this as a +reason to. Several of my authored values rest on them; what I have instead is +their statement of the exposure and the structural argument for why it did not +fire. Recorded as that, not as verification. diff --git a/tools/port/audit-kinds b/tools/port/audit-kinds index fa4fbca8..2c1a1507 100755 --- a/tools/port/audit-kinds +++ b/tools/port/audit-kinds @@ -139,6 +139,15 @@ def selftest(): means anything. """ paths = known_paths() + # The liveness case belongs in the self-test too, driven as a subprocess so + # its real exit code is read rather than reasoned about. + empty = os.path.join(os.environ.get("TMPDIR", "/tmp"), "audit-kinds-liveness") + os.makedirs(empty, exist_ok=True) + got = subprocess.run([sys.executable, os.path.abspath(__file__)], cwd=empty, + capture_output=True).returncode + print(f" harness: an empty tree -> exit {got} (want 2) " + f"{'✅' if got == 2 else '🔴 examined nothing and reported clean'}") + live_ok = got == 2 cases = [ ("bare", "no citation of any kind here, just prose", "BARE"), ("ok", "see tools/port/audit-kinds for the method", "ok"), @@ -158,8 +167,11 @@ def selftest(): if got != want: bad += 1 print() + if not live_ok: + bad += 1 if bad: - print("🔴 the classifier cannot tell grounded labels from ungrounded ones.") + print("🔴 the classifier cannot tell grounded labels from ungrounded ones,") + print(" or it reports clean on an empty tree.") print(" Exit 2: nothing this tool has reported clean is trustworthy.") return 2 print("the classifier separates bare, dangling and grounded citations") @@ -172,6 +184,18 @@ def main(): rows = [] for f in sorted(glob.glob("authored/*.json")): walk(json.load(open(f)), f, "", rows) + # 🔴 LIVENESS. Run against a tree with no `authored/*.json` this printed + # "0 kind label(s)" and exited 0 -- examined nothing, reported clean. The + # Decoder's generalisation of my empty-band case, which is more general than + # either instance: **a control that only compares two things cannot tell you + # the comparison is happening.** An empty input makes a checker AGREEABLE + # rather than wrong, and agreeable is indistinguishable from correct in a + # log. + if not rows: + print("🔴 no `kind` labels found at all -- this audit examined NOTHING.") + print(" Exit 2: the harness is broken (wrong directory, renamed files),") + print(" not the corpus.") + return 2 paths = known_paths() bare = dangling = 0 kinds = {} diff --git a/tools/port/check-claims b/tools/port/check-claims index e3bb93b0..d2fa1260 100755 --- a/tools/port/check-claims +++ b/tools/port/check-claims @@ -23,7 +23,7 @@ set -euo pipefail cd "${PROJECT_DIR:-/work}" WINDOW=400 # characters either side of a hit in which the marker must appear -fail=0; total_marked=0 +fail=0; total_marked=0; scanned=0 # 🔴 THE MARKER IS AN EXPLICIT SENTINEL, NOT A KEYWORD. # @@ -130,6 +130,13 @@ if [ "${1:-}" = "--control" ]; then got=$(CLAIMS_REGISTER="" "$0" >/dev/null 2>&1; echo $?) if [ "$got" = "2" ]; then printf ' %-26s exit 2 ✅\n' "empty register" else printf ' %-26s exit %s, wanted 2 🔴\n' "empty register" "$got"; ok=1; fi + # Sixth case: a tree with nothing to scan. It used to die in the withdrawal + # hook and exit 1 -- "a refuted claim is still being asserted" -- for a wrong + # directory. Liveness and diagnosis are both asserted here. + _empty="${TMPDIR:-/tmp}/claims-liveness-root"; mkdir -p "$_empty" + got=$(cd "$_empty" && PROJECT_DIR="$_empty" "$OLDPWD/$0" >/dev/null 2>&1; echo $?) + if [ "$got" = "2" ]; then printf ' %-26s exit 2 ✅\n' "nothing to scan" + else printf ' %-26s exit %s, wanted 2 🔴\n' "nothing to scan" "$got"; ok=1; fi echo [ $ok -eq 0 ] && echo "the register check fails when it must, and says so distinctly" \ || echo "🔴 the control machinery itself is broken" @@ -172,6 +179,25 @@ fi # never a defect count. echo echo "withdrawal-time hook -- correction sections that registered nothing:" +# 🔴 PREFLIGHT. Run from the wrong directory this used to die inside the +# withdrawal hook with a FileNotFoundError and exit **1** -- which in this +# script's own vocabulary means "a refuted claim is still being asserted". A real +# failure with a fabricated diagnosis, the same shape as my control anchoring at +# the wrong document. The roots it needs are named here and their absence is a +# HARNESS fault with its own code. +for _root in docs docs/port authored tools/port; do + [ -d "$_root" ] || { + echo "🔴 \`$_root\` is not here -- this check cannot scan anything." >&2 + echo " Exit 2: wrong directory or a bad checkout, not a dirty corpus." >&2 + exit 2 + } +done +[ -f docs/port/DECISIONS.md ] || { + echo "🔴 docs/port/DECISIONS.md is missing -- the withdrawal hook has nothing" >&2 + echo " to read. Exit 2: the harness is broken, not the corpus." >&2 + exit 2 +} + REG="$REGISTER" python3 - <<'HOOK' import os, re reg = [r.strip() for r in os.environ["REG"].split("\n") if r.strip()] @@ -235,6 +261,7 @@ PY # clause. A register matching EXACT wording does not protect the documents that # rewrite most, and a capital letter is the cheapest rewrite there is. done < <(grep -ril -- "$claim" docs/ crates/ port/ tools/ authored/ 2>/dev/null || true) + scanned=$((scanned + hits)) if [ "$bad" -eq 0 ]; then printf ' %-42s %d file(s), %d occurrence(s) suppressed\n' "$claim" "$hits" "$marked" total_marked=$((total_marked + marked)) @@ -243,6 +270,17 @@ PY fi done <<< "$REGISTER" +# 🔴 LIVENESS. A register full of claims and a tree with nothing in it reports +# clean: the grep matches no files, every row scores 0, and the run passes having +# READ NOTHING. Wrong directory, renamed docs, a bad checkout -- all produce a +# green line. The Decoder's rule for the family: a control that only compares two +# things cannot tell you the comparison is happening. +if [ "$scanned" -eq 0 ]; then + echo "🔴 no file anywhere contains any registered claim -- this check READ" >&2 + echo " NOTHING. Exit 2: the harness is broken, not the corpus." >&2 + exit 2 +fi + echo printf ' %d occurrence(s) were SUPPRESSED by a neighbouring `%s`.\n' "$total_marked" "$MARKER" echo " That number is the size of what this check chose not to look at. A" diff --git a/tools/port/verify-transcode-fidelity b/tools/port/verify-transcode-fidelity index c4192dc1..6c5d75d0 100755 --- a/tools/port/verify-transcode-fidelity +++ b/tools/port/verify-transcode-fidelity @@ -338,6 +338,13 @@ def main(): return 2 man = json.load(open("export/manifest.json")) names = [v["name"] for v in man.get("videos", [])] + # 🔴 LIVENESS, the same shape as the empty band list one line up. With no + # videos in the manifest the loop never runs, `fail` stays 0 and this reports + # every transcode faithful -- having compared none. + if not names: + print("🔴 the manifest lists NO videos -- nothing was compared.") + print(" Exit 2: the harness is broken, not the transcodes.") + return 2 control = "--control" in sys.argv fail = 0 print(f" window {WINDOW_S:.0f} s from t=2 s, mono {RATE} Hz, pass at "