retro: the agreed result, plus the cue-suppression matrix it came out of
Agreed retro with the Decoder at docs/agents/RETRO-2026-08-31-agreed.md; their own review is at docs/agents/RETRO-2026-08-31.md on auto/frame-blend-draw-path. Twelve rules, of which the ones neither of us had: a refutation whose instrument is one of our renderers is not a refutation but "our renderer disagrees" (this cost weeks, both ways); a negative must carry a positive control, which no "undecodable with reach" page either of us has written ever did; and a disagreement is evidence about the chain disc -> decode -> render -> capture, not about the link being looked at. The gap underneath our controls, stated: they verified capability, not configuration. My additive material passed every control because they tested whether the method detects a blend difference, not whether this run had blend_mode set -- it was Godot's default MIX. Their vertex dump passed every control because they tested the NDC conversion, not whether it captured all six quads. Nothing applied to PROTOCOL.md. The register re-classification changes the file both agents read to decide what not to try, and two agents agreeing is not the right authority for that. Also committing the work that was pending when the stop came: verify-menu-audio now settles cue presence by suppression -- silence one cue's .ogg, re-record the same walk, and require its own correlation to collapse. All three cues sound (+0.158 / +0.637 / +0.526). The bed-only margin no longer renders a verdict: it called `move` NOT FOUND on three runs at 0.109-0.131 while the cue was demonstrably sounding, and across runs the same cue reads 0.131 and 0.158, so the threshold was never a property of the cue. The off-diagonal is reported as masking rather than asserted: silencing confirm raises back by 0.468 because the game plays them 90 ms apart under a 0.15 s template. An assertion there would fail forever on correct audio.
This commit is contained in:
103
docs/agents/RETRO-2026-08-31-agreed.md
Normal file
103
docs/agents/RETRO-2026-08-31-agreed.md
Normal file
@@ -0,0 +1,103 @@
|
||||
# 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).** It changes the file both agents read to
|
||||
decide what *not* to try. Two agents agreeing is not the right authority.
|
||||
2. **P5's gate** — a person clicking through the port. Unchanged, and it is the
|
||||
only thing standing between the milestone and done.
|
||||
@@ -175,7 +175,13 @@ per-draw alpha is as a function of sweep position.
|
||||
**3. Does `kind & 0x2` belong in the exporter?** Their focusable-flag result (0
|
||||
violations in 15 493 entries, 24 paks) implies `ptbtn00` on the PRESS Ⓐ plate —
|
||||
`0x73002` in my export — is focusable, and my exporter classifies it `unknown`
|
||||
rather than `button`. That is a **decode**, so consuming it is legitimate; I am
|
||||
rather than `button`.
|
||||
|
||||
📌 **The consequence is concrete, not theoretical.** `export/screens/title/press_start.json`
|
||||
carries **`"buttons": []`** — an empty button list — on a screen whose one element
|
||||
**has a focus record**. The port is describing a screen with a focusable element as
|
||||
having no buttons. And it is the same element the Decoder says the PRESS Ⓐ pulse is
|
||||
made of: `ptbtn00f` additive over a non-additive `ptbtn00`. That is a **decode**, so consuming it is legitimate; I am
|
||||
asking rather than taking because it changes an exporter classification that other
|
||||
things read, and `0x3000` (817 elements disc-wide, button-shaped and *not*
|
||||
focusable) is exactly the trap a looser rule would fall into.
|
||||
|
||||
@@ -49,6 +49,29 @@ run walk down,down,accept,cancel,up
|
||||
run ctrl wait,wait,wait,wait,wait
|
||||
run noop left,left,left,left,left
|
||||
|
||||
# 🔴 AND A PER-CUE KNOWN NEGATIVE, because the bed-only control could not settle
|
||||
# what it was being asked. `move` reported NOT FOUND on three consecutive runs at
|
||||
# margins 0.109/0.120/0.131 against a 0.15 line that a documented earlier run had
|
||||
# cleared at 0.185. Two readings fit that -- the cue stopped playing, or the
|
||||
# threshold sits above the quietest cue's true signal -- and A MARGIN CANNOT
|
||||
# SEPARATE THEM, because both produce a small number.
|
||||
#
|
||||
# So each cue now gets its own negative: the SAME walk, with only that cue's .ogg
|
||||
# replaced by silence through the mod tree. Silencing a cue that is playing must
|
||||
# collapse its correlation and leave the other two alone, which is a 3x3 matrix
|
||||
# with six off-diagonal controls rather than one number to compare against a
|
||||
# threshold.
|
||||
for c in move confirm back; do
|
||||
d="$OUT/sup_$c"; mkdir -p "$d/audio/se"
|
||||
ffmpeg -v error -f lavfi -i anullsrc=r=44100:cl=stereo \
|
||||
-t "$(ffprobe -v error -show_entries format=duration -of csv=p=0 export/audio/se/$c.ogg)" \
|
||||
-c:a libvorbis "$d/audio/se/$c.ogg" -y
|
||||
SYLPHEED_MODS="$d" run "sup_$c" down,down,accept,cancel,up
|
||||
grep -q "^mod: audio/se/$c.ogg" "$OUT/sup_$c.log" || {
|
||||
echo "the $c override was never read -- the matrix below would be meaningless" >&2
|
||||
exit 2; }
|
||||
done
|
||||
|
||||
python3 - "$OUT" <<'PYEOF'
|
||||
import array, math, subprocess, sys
|
||||
O = sys.argv[1]; SR = 44100
|
||||
@@ -123,19 +146,54 @@ def slide(tpl, hay, step=16):
|
||||
return best
|
||||
|
||||
found = []
|
||||
tpls = {}
|
||||
for cue in ("move", "confirm", "back"):
|
||||
tpl = dec("export/audio/se/%s.ogg" % cue, "%s/%s.raw" % (O, cue))[:int(0.15*SR)]
|
||||
tpls[cue] = tpl
|
||||
rw, tw = slide(tpl, walk)
|
||||
rc, _ = slide(tpl, ctrl)
|
||||
# 🔴 NO VERDICT ON THIS LINE ANY MORE. It used to print PRESENT/NOT FOUND on
|
||||
# `margin > 0.15`, and it called `move` NOT FOUND on three consecutive runs at
|
||||
# 0.109/0.120/0.131 while the cue was DEMONSTRABLY SOUNDING -- silencing its
|
||||
# .ogg collapses it to the bed floor. The bed-only control is a DIFFERENT RUN,
|
||||
# so its margin carries every difference between two runs; the threshold that
|
||||
# once cleared 0.185 was never a property of the cue. The number is still worth
|
||||
# printing. The verdict now comes from the suppression matrix below.
|
||||
hit = rw - rc > 0.15
|
||||
found.append((cue, tw, hit))
|
||||
print("%-8s walk r=%.3f at %5.2fs | bed-only r=%.3f | margin %+.3f %s"
|
||||
% (cue, rw, tw, rc, rw-rc, "PRESENT" if hit else "NOT FOUND"))
|
||||
print("%-8s walk r=%.3f at %5.2fs | bed-only r=%.3f | margin %+.3f"
|
||||
% (cue, rw, tw, rc, rw-rc))
|
||||
|
||||
# 3. The ORDER is the strongest evidence here and it is free: the correlator is
|
||||
# never told where to look, so three templates landing in script order --
|
||||
# move (step 1) before confirm (step 3) before back (step 4) -- is three
|
||||
# independent searches agreeing with the log.
|
||||
# 3b. THE SUPPRESSION MATRIX. Row = the cue silenced, column = the template
|
||||
# searched for. The diagonal is the only cell that should move.
|
||||
sup = {c: dec("%s/sup_%s.wav" % (O, c), "%s/sup_%s.raw" % (O, c))
|
||||
for c in ("move", "confirm", "back")}
|
||||
base = {c: slide(tpls[c], walk)[0] for c in tpls}
|
||||
print("\nsuppression matrix -- drop in r when one cue's .ogg is silenced")
|
||||
print(" " + "".join("%9s" % c for c in ("move", "confirm", "back")))
|
||||
ok = True
|
||||
for row in ("move", "confirm", "back"):
|
||||
drops = {col: base[col] - slide(tpls[col], sup[row])[0] for col in ("move", "confirm", "back")}
|
||||
print(" silence %-6s" % row + "".join("%+9.3f" % drops[c] for c in ("move", "confirm", "back")))
|
||||
if drops[row] <= 0.05:
|
||||
ok = False
|
||||
print(" 🔴 silencing %s did not remove %s -- that cue is NOT SOUNDING" % (row, row))
|
||||
print(" => %s" % ("all three cues SOUND: silencing each one collapses its own signal"
|
||||
if ok else "at least one cue is not sounding"))
|
||||
# 🔴 THE VERDICT IS THE DIAGONAL ONLY, and the first version of this asserted the
|
||||
# off-diagonal too -- "silencing a cue must not move the others". That failed, and
|
||||
# the material is why: `confirm` lands at 1.12 s and `back` at 1.21 s, 0.09 s apart
|
||||
# under a 0.15 s template, so the two windows OVERLAP. Silencing `confirm` raises
|
||||
# `back` by 0.468 because confirm was masking it. That is a fact about two cues the
|
||||
# game plays 90 ms apart, not a fault, and an assertion that calls it one would
|
||||
# fail forever on correct audio.
|
||||
print(" (off-diagonal is MASKING between overlapping cues, not an error --")
|
||||
print(" confirm at 1.12 s and back at 1.21 s share a 0.15 s window)")
|
||||
|
||||
times = [t for _, t, hit in found if hit]
|
||||
print("cue order vs script order : %s"
|
||||
% ("CONSISTENT" if times == sorted(times) and len(times) == 3
|
||||
|
||||
Reference in New Issue
Block a user