port: a mistyped mod override was silent -- the liveness defect, in the product

Every checker fix this week was about a tool that could not tell 'I checked and it
was fine' from 'I checked nothing'. The port had the same defect facing the person
the asset tree exists for.

ExportTree.resolve announces every shadow as it happens, and its comment already
records why a startup summary was wrong. Nothing reported the opposite. Measured
with two planted overrides, one correct and one in a mistyped directory: the
correct one is announced and the typo produces NO OUTPUT AT ALL. The modder sees
the port load, run, and say nothing about the file that did nothing -- MODDING
rule 4's own failure mode, since base-and-overrides is only usable if an override
that misses says so.

ExportTree.unused_mods() and a report at run end now list them. Controlled both
directions: one inert file with the typo present, silent with it removed.

Getting the category right took three tries and that is the point. v1 'never used'
flagged data/mods/README.md on every run, and a report with a standing false
positive is one nobody reads -- precisely the failure it exists to fix. v2 'no such
path in the export' was correct and still flagged the README. v3 excludes by
extension with the rule checked rather than assumed: the export tree contains only
png, json, ogg, ogv and cmd, verified zero .md anywhere, so a .md in data/mods
could never be an override by construction.

The report also separates what v1 conflated: a file whose path exists in the export
but was not read this run is NOT listed. Every line printed is an override that
can never apply, whatever the run does.

boot.gd already had an _exit_tree and adding a second was a parse error -- the run
failed loudly instead of one hook silently replacing the other, the cheapest
possible failure mode and only because GDScript rejects it.

Their P3 delivery is taken at the strength given: Q6's count-match has disc
support for its structure -- every button record across all 16 GP_TITLE entries is
ptbtn00, ptbtn01-05, ptbtn11-13 -- but it does not show that event 3 is a
particular row, and they said not to author from it. flow.json already binds
buttons by measured screen rather than event index, so nothing changes. Their own
negative is narrower than 'not found': the DIFFICULTY search assumed four items
pair with f variants, so what is established is 'not an 8-record btn-named build
anywhere'.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF
This commit is contained in:
Sylpheed port agent
2026-08-31 02:20:38 +00:00
parent 3a048de536
commit 7a7bc53893
3 changed files with 161 additions and 1 deletions

View File

@@ -9,7 +9,7 @@ dies, which is what this file is for.
<!-- INDEX: generated by tools/port/index-decisions -- do not hand-edit -->
276 sections. Search this before re-deriving anything.
278 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)
@@ -287,6 +287,8 @@ dies, which is what this file is for.
* [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)
* [The liveness lesson, applied to the product: a mistyped override was silent](#the-liveness-lesson-applied-to-the-product-a-mistyped-override-was-silent)
* [Their P3 delivery, taken at the strength they gave it](#their-p3-delivery-taken-at-the-strength-they-gave-it)
<!-- /INDEX -->
## P0 — the exporter, 2026-08-28
@@ -13966,3 +13968,65 @@ starts mattering.
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.
## The liveness lesson, applied to the product: a mistyped override was silent
Every checker fix this week has been about a tool that could not tell *"I checked
and it was fine"* from *"I checked nothing"*. **The port itself had the same
defect, facing the person the asset tree exists for.**
`ExportTree.resolve` announces every shadow as it happens, and its comment already
records why a startup summary was wrong. **Nothing reported the opposite.**
Measured with two planted overrides — one correct, one in a mistyped directory:
```
mod: sprites/title/main_menu/ptbase.png <- data/mods/... ← announced
sprites/title/TYPO_menu/pteff05.png ← NO OUTPUT AT ALL
```
The modder sees the port load, run, and say nothing about the file that did
nothing. **That is MODDING rule 4's own failure mode**: base-and-overrides is only
usable if an override that misses says so.
`ExportTree.unused_mods()` + a report at run end now lists them. Controlled both
directions: **one inert file with the typo present, silent with it removed.**
### 🔴 Getting the report's *category* right took three tries, and that is the point
* **v1 — "never used".** Flagged `data/mods/README.md` on every run. **A report
with a standing false positive is one nobody reads**, which is precisely the
failure it exists to fix.
* **v2 — "no such path in the export".** Correct, and still flagged the README:
it genuinely cannot shadow anything.
* **v3 — excluded by extension, with the rule checked rather than assumed.** The
export tree contains only `png`, `json`, `ogg`, `ogv`, `cmd` — **verified, zero
`.md` anywhere** — so a `.md` in `data/mods` could never be an override *by
construction*. Flagging a class that can never be one is noise.
📌 And the report distinguishes two things v1 conflated: a file whose path exists
in the export but **was not read this run** (a `--menu` run touches one screen) is
**not listed**. Every line printed is an override that can never apply, whatever
the run does.
⚠️ One incident worth keeping: `boot.gd` **already had an `_exit_tree`**, and
adding a second was a **parse error** — the run failed loudly instead of one hook
silently replacing the other. The cheapest possible failure mode, and only
because GDScript happens to reject it.
## Their P3 delivery, taken at the strength they gave it
Q6's count-match now has **disc support for its structure**: every button record
across all 16 `GP_TITLE` entries is `ptbtn00`, `ptbtn0105`, `ptbtn1113` — three
button screens and no fourth, with the other four destinations in their own paks.
⚠️ **Not authored from, and they said not to.** *"It shows the shape the
count-match asserts is real on the disc; it does not show that event 3 is a
particular row."* My `flow.json` already binds buttons to destinations by
measured screen rather than by event index, so nothing here changes — and if a
button-to-event map were ever needed, **there is not one**.
📌 Their negative carries its own reach, which is the part I would have got wrong:
they searched every pak for an 8-button-record build and found none, but the
search assumed DIFFICULTY's four items pair with `f` variants as `GP_TITLE`'s
screens do. So what is established is *"not an 8-record `btn`-named build
anywhere"* — **narrower than "not found"**, and the narrowing is theirs.