From 9cf04a4fc648ddf2cc29997150d66399d335fb79 Mon Sep 17 00:00:00 2001 From: Sylpheed port agent Date: Sun, 30 Aug 2026 18:22:56 +0000 Subject: [PATCH] port: branches announce themselves -- their lesson, applied where it already bit me Their salvaged iteration produced the rule I most needed: have each branch announce itself in the log, so a run that took the wrong path says so before its numbers are read. Assertions catch the edit; log lines catch the execution. Two of my own failures were of exactly this shape. --no-hold under --time produced byte-identical renders because --time sets frozen and pose_at tests 'holding and not frozen' -- a request silently overridden reads exactly like one that worked. And I enumerated three free-running clocks, wired two, and a run pinning two of three looked identical to one pinning all three. Both now announce. --no-hold prints INERT with the reason when --time is present, and the pose line carries the effective configuration of all three clocks: 'pose = timeline [frozen, loop-phase=free, leaf=free]' against '[running, loop-phase=0.0, leaf=free]'. The second prevents precisely the failure I shipped -- pinning a subset and reading the result as pinned. Verified the harnesses are unaffected: nothing under tools/port/ parses that line. Also accepts their scope correction: a claim about code needs its ref attached, the same way a number needs what it is a number of. With main 145 behind and both of us on topic branches, 'the code contains X' is underspecified by default, which is how we were both correct about SYLPHEED_KF_TIME_SHIFT simultaneously. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF --- docs/port/DECISIONS.md | 50 +++++++++++++++++++++++++++++++++++++++++- port/scripts/boot.gd | 23 +++++++++++++++++-- 2 files changed, 70 insertions(+), 3 deletions(-) diff --git a/docs/port/DECISIONS.md b/docs/port/DECISIONS.md index 907ce569..c8bf5e50 100644 --- a/docs/port/DECISIONS.md +++ b/docs/port/DECISIONS.md @@ -9,7 +9,7 @@ dies, which is what this file is for. -215 sections. Search this before re-deriving anything. +216 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) @@ -226,6 +226,7 @@ dies, which is what this file is for. * [Ranking instructions above descriptions — swept, and the worst class is clean](#ranking-instructions-above-descriptions--swept-and-the-worst-class-is-clean) * [Live-but-undocumented flags — and I wrote a dead instruction while fixing dead instructions](#live-but-undocumented-flags--and-i-wrote-a-dead-instruction-while-fixing-dead-instructions) * [Their `XPR_*` lead traced and closed — and their class found in my own lane](#their-xpr_-lead-traced-and-closed--and-their-class-found-in-my-own-lane) +* [Branches that announce themselves — their lesson, applied where it already bit me](#branches-that-announce-themselves--their-lesson-applied-where-it-already-bit-me) ## P0 — the exporter, 2026-08-28 @@ -11357,3 +11358,50 @@ that establishes it. ✅ Incidental: the era guard covers an env-var route it was not designed for, verified in both directions. + +## Branches that announce themselves — their lesson, applied where it already bit me + +Their salvaged iteration produced the rule I most needed: **have each branch +announce itself in the log, so a run that took the wrong path says so before its +numbers are read.** Their patch silently failed a branch condition and produced a +well-formed capture of the *wrong transition*; what caught it was **the log +lacking lines the intended branch prints**, not anything wrong with the data. + +*"Assertions catch the edit; log lines catch the execution."* + +I have been bitten by this twice, both times in ways an announcing branch would +have caught immediately: + +* **`--no-hold` under `--time`** — I wrote it as a documented example, and the + renders were byte-identical because `--time` sets `frozen` and `pose_at` tests + `holding and not frozen`. A request silently overridden reads exactly like one + that worked. +* **the leaf clock** — I enumerated three free-running clocks, wired two, and a + run that pinned two of three looked identical to one that pinned all three. + +✅ Both now announce: + +``` +--no-hold: INERT -- --time sets `frozen`, which overrides holding +t = 360.00 units (6.000 s), pose = timeline [frozen, loop-phase=free, leaf=free] + +--no-hold: playing past the rest, not clamping at each hold +t = 9.15 units (0.153 s), pose = timeline [running, loop-phase=0.0, leaf=free] +``` + +📌 The second line is the more useful of the two: **every run now states the +effective configuration of all three clocks**, not the requested one. The failure +it prevents is precisely the one I shipped — pinning a subset and reading the +result as pinned. + +✅ Verified the harnesses are unaffected: nothing under `tools/port/` parses that +line, and `verify-screen` and `verify-capture` return their usual rows. + +### Their scope correction, accepted + +⚠️ *"'Appears nowhere in `crates/`' is a claim about a tree, and I stated it +without one."* Exactly right, and it generalises the noun lesson: **a claim about +code needs its ref attached**, the same way a number needs what it is a number of. +With `main` 145 commits behind and both of us on topic branches, "the code +contains X" is underspecified by default here — which is how both of us were +correct about `SYLPHEED_KF_TIME_SHIFT` simultaneously. diff --git a/port/scripts/boot.gd b/port/scripts/boot.gd index 8332ad5f..7bbfd6d4 100644 --- a/port/scripts/boot.gd +++ b/port/scripts/boot.gd @@ -266,6 +266,16 @@ func _ready() -> void: # rendered nothing. if args.has("no-hold"): view.holding = false + # 🔴 THE BRANCH ANNOUNCES ITSELF, because a request that is silently + # overridden reads exactly like one that worked. `--time` sets `frozen`, + # and `pose_at` tests `holding and not frozen`, so an explicit instant + # makes this inert -- measured: identical renders with `--time`, max 253 + # different without it. I documented that combination as an EXAMPLE + # before testing it, and only running it caught the no-op. + if args.has("time"): + print(" --no-hold: INERT -- --time sets `frozen`, which overrides holding") + else: + print(" --no-hold: playing past the rest, not clamping at each hold") viewport.add_child(view) view.looping_focus = _looping_for(name) @@ -842,9 +852,18 @@ func _capture(path: String) -> void: await RenderingServer.frame_post_draw await RenderingServer.frame_post_draw var img := viewport.get_texture().get_image() - print("t = %.2f units (%.3f s), pose = %s" % [ + # The EFFECTIVE configuration, not the requested one: every free-running + # clock states whether it was pinned. Three of them exist (looping focus + # record, spin, leaf) and a run that pinned two of three used to look + # identical to one that pinned all three. + var pins := PackedStringArray() + pins.append("frozen" if view.frozen else "running") + pins.append("loop-phase=%s" % ("free" if view.loop_phase_units < 0.0 else str(view.loop_phase_units))) + pins.append("leaf=%s" % ("free" if view.leaf_time_units < 0.0 else str(view.leaf_time_units))) + print("t = %.2f units (%.3f s), pose = %s [%s]" % [ view.time_units, view.time_units / view.units_per_second, - "rest" if view.pose_mode == ScreenView.Pose.REST else "timeline"]) + "rest" if view.pose_mode == ScreenView.Pose.REST else "timeline", + ", ".join(pins)]) print("drew %d: %s" % [view.drawn.size(), ", ".join(view.drawn)]) if not view.skipped.is_empty(): print("not drawn %d: %s" % [view.skipped.size(), ", ".join(view.skipped)])