Takes the port branch up to77320d5e-- the state the human play-tested on 2026-09-02 -- for SOURCE paths only. Not a branch merge: `auto/port-p6-audio` is 366 commits and 938 files, and most of that must not land. WHAT COMES IN (76 files, all human-confirmed working): * the logo splash animation.08ed3dd1found it: `pose_at` ASSIGNED the settle instant instead of clamping to it, so the splash never animated at all -- and the same bug manufactured a passing harness result, because the harness photographed t past the settle. Confirmed by play-test: "cannot notice any obvious difference from the actual game." * gamepad input -- (A)/(B) bound additively (`ui_accept` ships with NO joypad binding), stick latched with hysteresis at the game's own 61% digitise threshold. This is what made (A), video-skip and Extras work at all. * menu navigation and flow, menu audio, the exporter, the authored declarations, and 23 verification tools under tools/port/. WHAT IS DELIBERATELY LEFT ON THE BRANCH: * everything afterc0ae460a-- the F5/F6 title-timing investigation, whose own tip commit calls itself a "hand-off for one-minute human checks". Unchecked by definition; it goes through the new review gate like anything else. * the OPTIONS menu work of 2026-09-03. Real, probably good, NOT play-tested. * the F1 repeat mechanism, which its own commit calls "deliberately inert". WHAT MUST NOT LAND, AND WHY THE .gitignore CHANGED: 545 MB of extracted game content was committed on that branch -- 850 sprite, audio and transcoded video files under `export-probe/` and `export-probe2/`, plus 246 MB of loose .wav and .tsv at the repo root. This repository's own rule, in this file, is "never game content". The rule was not missing. It was written, and it was tightened on that very branch, with a careful comment explaining why BOTH `export/` and `data/base/` had to be listed -- while the exporter was writing to a third name that nobody had thought to list. Enumerating names is the thing that failed. So the ignore rules now describe the SHAPE: any top-level `export*/`, game media by extension, and loose capture output at the root. Verified both ways -- it catches all four offenders and ignores nothing currently tracked. Verified: `cargo check --workspace` clean; all nine GDScript files parse in project context, with a positive control (an injected syntax error is detected, 3 lines) so the clean result means something. `tools/port/check-all` was NOT run -- it needs the container, the export tree and a display.
130 lines
8.6 KiB
Markdown
130 lines
8.6 KiB
Markdown
# Agreed retro — Port and Decoder, 2026-08-31
|
|
|
|
Two self-reviews, one round of mutual attack, and the result both agents accept.
|
|
|
|
* Decoder's own review: `docs/agents/RETRO-2026-08-31.md` on `auto/frame-blend-draw-path`.
|
|
* Port's failures and the original eight proposals: this file's §1, and `docs/port/DECISIONS.md`.
|
|
|
|
⚠️ **Nothing here is applied to `PROTOCOL.md`.** The parts that change the shared
|
|
refuted-claim register are presented for the human, not enacted by two agents
|
|
agreeing with each other. Both agents remain paused.
|
|
|
|
## 1. The single most expensive thing we did
|
|
|
|
**We let claims that rest on our own renderer sit in the register as settled
|
|
refutations.**
|
|
|
|
`REFUTED.md` killed *"`T8aD +0x04` bit `0x02` selects an additive blend"* with the
|
|
reason *"blending those sprites additively worsens every measure against the
|
|
capture"* — a statement about our renderer, made while that renderer had a stale
|
|
keyframe association, no leaf geometry and no rotation. The field is real. It sat
|
|
dead for weeks, and the cost was: a published *"the blend is not on the disc"*, an
|
|
authored table built on it, **three rounds of per-element transcription**, and one
|
|
agent steering a search deliberately around the entry.
|
|
|
|
The Port paid the mirror of it: a phase sweep that *"refuted"* menu-looping was
|
|
measuring the Port's own sweeps, not the game's, and was re-run and reported as
|
|
*strengthened* one iteration before the oracle contradicted it.
|
|
|
|
📌 **Neither of us was careless. The rule was in `PROTOCOL.md` the whole time** —
|
|
*"anything derived from our own renderer rather than a capture"* is named as a
|
|
prime refutation target. What was missing is that nothing **re-opens** a claim when
|
|
the instrument that killed it improves.
|
|
|
|
## 2. The gap underneath our controls
|
|
|
|
`PROTOCOL.md` already requires running an instrument through a control. **We both
|
|
did, and it did not help**, because:
|
|
|
|
> **Our controls verified capability, not configuration.**
|
|
|
|
* The Port's additive material passed every control — they tested whether the
|
|
*method* detects a blend difference, not whether *this run* had `blend_mode` set.
|
|
It was left at Godot's default, `MIX`. The change predicted a large move and
|
|
delivered **0.03**, and would have been publishable as a careful negative.
|
|
* The Decoder's vertex dump passed every control — they 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, and four elements therefore
|
|
appeared *in no draw on any screen*.
|
|
|
|
## 3. The gap neither of us had noticed
|
|
|
|
> **We have never given a NEGATIVE a positive control.**
|
|
|
|
Every *"undecodable, with reach"* page lists **where we looked**. Not one shows
|
|
that the search method **can find a property that is there**. *"Absent"* and
|
|
*"my search does not work"* are indistinguishable in all of them — and *"the blend
|
|
is not on the disc"* is exactly that failure, published.
|
|
|
|
## 4. The rules we agree to work by
|
|
|
|
| | rule | replaces / from |
|
|
|---|---|---|
|
|
| **R1** | **A refutation whose instrument is one of our renderers is not a refutation.** It is *"our renderer disagrees"* — 🟡, not ❌. Each register entry names its `instrument:`, and a `--stale <instrument>` mode lists everything that instrument killed, for re-opening when it changes. | Port P2, strengthened by Decoder |
|
|
| **R2** | **State the expected number before you read the actual one** — the effect size for a change, the *count* for a parse. *"This draw declares 24 indices, so I expect 6 quads."* | Port P3+P4, merged by Decoder |
|
|
| **R3** | **Instruments print their own completeness**: *n* resolved of *n* declared, and refuse to be trusted otherwise. | Port P4 |
|
|
| **R4** | **A negative carries a positive control.** Before publishing *"no field encodes X"*, show the same search finding a field known to exist. | Decoder D1 — **neither agent had this** |
|
|
| **R5** | **Label provenance is part of the artefact.** A field hunt states where its ground truth came from, and **renderer-derived labels are disqualified for disc-side questions.** | replaces Port P1, which had no teeth — the question *was* asked and answered wrongly |
|
|
| **R6** | **Suppression localises disagreement; only the oracle labels it.** It is two renders of ours: it found the frames, it could not have said *additive*. | Decoder's correction of Port P5 |
|
|
| **R7** | **Coverage is computed against a declared denominator** — *"35 of the 41 elements entry 6 declares"*, never *"everything is covered"*. | Port P6 + Decoder |
|
|
| **R8** | **Hold the role line even when the answer looks obvious.** The asymmetry is the argument: refusing to infer `ptframe4` cost one message; inferring *"frame-shaped and mostly transparent ⇒ additive"* would have cost a wrong renderer until the title was captured — **and the title capture killed that exact rule.** | Port P7, agreed |
|
|
| **R9** | **The message carries the delta and names the file and section; it does not summarise it.** Short messages are safe only when the pointer is precise. | Port P8 + Decoder's caveat |
|
|
| **R10** | **A disagreement is evidence about the CHAIN — disc → decode → render → capture — not about a link.** A chain-level residual gets a named owner and a next experiment, or is recorded as unowned. | Decoder F |
|
|
| **R11** | **A cross-agent pointer must fail loudly when it goes stale.** Every staleness incident here was silent. | Port, new |
|
|
| **R12** | **Each iteration names the gate it moved, or says plainly that it moved none.** | Port, new — see §5 |
|
|
|
|
## 5. The efficiency finding neither review led with
|
|
|
|
**The record has grown faster than the artifact.** `DECISIONS.md` is past 13 000
|
|
lines. This session produced twelve Port commits of genuine measurement — and the
|
|
milestone gate did not move, because **P5's gate has needed a human, not code, the
|
|
whole time.** Writing more is not free, and a capability that lives only in the
|
|
record is, to the person who needs it, absent.
|
|
|
|
R12 exists so that a run of iterations that moves no gate **says so**, rather than
|
|
reading as progress because each entry is individually rigorous.
|
|
|
|
## 6. What each agent changes, without a human
|
|
|
|
* **Decoder:** a standing pointer at the top of `HANDOFF.md` — which their brief
|
|
already forces them to read every iteration, and which is theirs to write — to
|
|
`git show origin/auto/port-p6-audio:docs/port/BLOCKED.md`. **One line in a file
|
|
they own**, routing the Port's standing asks into a file they must already open.
|
|
This closes a gap `BLOCKED.md` records as having cost three sessions.
|
|
* **Port:** `instrument:` provenance and `--stale` in `check-claims`; completeness
|
|
lines (R3) and predicted counts (R2) in the port's tools; a loud staleness
|
|
failure for peer pointers (R11).
|
|
|
|
## 7. What needs the human
|
|
|
|
1. ✅ **The register re-classification (R1) — DONE 2026-09-01, by the human**, on
|
|
`docs/re/REFUTED.md` at the Decoder's tip. All **222** entries now carry an
|
|
`⟨instrument⟩`; the file opens with a reading guide naming which instruments
|
|
are ours; R1 is now standing text in `PROTOCOL.md`; and
|
|
`tools/stale-instrument` is the `--stale` query — run it whenever you improve
|
|
a renderer, a reader or the harness, and it lists what that instrument killed.
|
|
|
|
**Ten entries moved ❌ → 🟡**, each naming what would settle it: eight
|
|
`render-vs-capture`, one `our-reader`, one `harness`.
|
|
|
|
Three things the pass turned up that neither self-review had:
|
|
|
|
* **The `rest()` question is open, and had been reading as settled in both
|
|
directions.** *"rest = last keyframe"* was refuted by the sibling argument;
|
|
that refutation was then refuted by correlating our render against
|
|
captures. Both legs run through our renderer, so under R1 neither survives
|
|
— and which one you believed depended on which entry you found first.
|
|
🔴 **This one is load-bearing for the port**: `rest()` 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 degrades and then
|
|
repeats a stale frame. The 1 674-sample negative three lines above it —
|
|
same probe, same instrument, comparable duration — was left standing as a
|
|
*reinstated measurement*. §1's lesson, inside the register itself.
|
|
* **83 of 222 entries — 37 % — record no instrument at all.** Not disputed,
|
|
not safe: **unauditable**. `stale-instrument unrecorded` is the backfill
|
|
queue, and it is larger than every other group combined.
|
|
|
|
2. **P5's gate** — a person clicking through the port. Unchanged, and it is the
|
|
only thing standing between the milestone and done.
|