port: wire flow.json's dwell, assert the three authored values the port hardcodes
Applying the prior from six prior findings to authored/ itself: five keys had no reader. dwell, ramp, left_right, input_during_transition, stems. dwell is the one that mattered. Its own text says a measured hold goes there and a number placed there did nothing -- and two iterations ago I asked the Decoder for measurements destined for that slot. Wired now, and it stays EMPTY: the splash dwells are declared on the disc and measured to agree. I wired it to the wrong branch first and it did nothing, silently -- holding longer after settle is absorbed because the screen still leaves at exit_time + black_hold. A dwell must delay the departure. Caught only by testing the control: +120 units moves the transition 4.46 -> 6.43 s. ramp, left_right and input_during_transition describe hardcoded behaviour and are written like switches. Rather than invent the missing implementations, they are now asserted against the value the port was built for, naming the file -- which is the distinction left_right's own why claims to make and was not making. The validator is called, not merely defined. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF
This commit is contained in:
@@ -6220,3 +6220,67 @@ full-frame and **0.000 %** across the band. A general floor could not coexist wi
|
||||
either number. So the 0.301 % is specific to the attract band capture, and my
|
||||
0.01–0.2 % rows are not sitting on a hidden floor. ⚠️ What that does *not* settle
|
||||
is why those two frames differ — still theirs, and still worth an answer.
|
||||
|
||||
## Five authored values had no reader — including the one I asked for measurements into
|
||||
|
||||
Applying the prior from the last six findings — *a rule or capability nothing
|
||||
exercises turns out broken or inert when someone looks* — to `authored/` itself.
|
||||
Grepping every authored key for a reader in the exporter or the runtime:
|
||||
|
||||
| key | file | status |
|
||||
|---|---|---|
|
||||
| `dwell` | `flow.json` | 🔴 **no reader** — now wired |
|
||||
| `ramp` | `timing.json` | no reader — now asserted |
|
||||
| `left_right` | `flow.json` | no reader — now asserted |
|
||||
| `input_during_transition` | `flow.json` | no reader — now asserted |
|
||||
| `stems` | `audio.json` | no reader (`stems_why` is carried; the sum is hardcoded) |
|
||||
|
||||
Everything else — `se`, `bgm`, `voice`, `boot`, `screens`, `navigation.wrap`,
|
||||
`draw_leaf_for`, `loop_leaf_on_screens`, `keyframe_units_per_second`,
|
||||
`black_hold_units`, `archives`, `also_export`, `presentation`, `loop_mode`,
|
||||
`initial_focus`, `skippable`, `then_video`, `after_video` — is read.
|
||||
|
||||
### 🔴 `dwell` is the one that mattered
|
||||
|
||||
Its own text says *"when a capture times the real boot, the extra hold per screen
|
||||
goes here."* **A number placed there did nothing.** Two iterations ago I asked
|
||||
the Decoder for measurements destined for exactly that slot; had they arrived,
|
||||
they would have been filed into a value with no reader and the boot would have
|
||||
been unchanged, silently — and I would have reported the boot as matching.
|
||||
|
||||
It is wired now, and **stays empty**. Nothing is authored into it: the splash
|
||||
dwells are declared on the disc and measured to agree. Wiring the slot so that a
|
||||
future number has an effect is the opposite of adopting one.
|
||||
|
||||
⚠️ **I wired it to the wrong branch first, and it did nothing — silently.** Holding
|
||||
longer after settle changes nothing, because the screen still leaves when
|
||||
`exit_time() + black_hold` arrives and the extra hold is absorbed. A dwell has to
|
||||
delay the **departure**. I found it only because I tested the control:
|
||||
+120 units moved the transition 4.46 s → **6.43 s**, +1.97 s, with the video
|
||||
following by the same amount. Reproducing the exact defect I was removing, inside
|
||||
the fix for it, is the strongest argument I have for testing that a wire carries
|
||||
current rather than that it exists.
|
||||
|
||||
### The other three are asserted, not implemented
|
||||
|
||||
`ramp`, `left_right` and `input_during_transition` describe behaviour the port
|
||||
**hardcodes**. That is fine for a record and dangerous for a switch, and they are
|
||||
written like switches — setting `left_right` to `"move"` would change nothing and
|
||||
warn nobody.
|
||||
|
||||
Rather than invent the missing implementations, `_check_authored_invariants`
|
||||
**asserts the value the port was built against**, naming the file. Changing one
|
||||
now produces an error instead of silence.
|
||||
|
||||
That is precisely the distinction `left_right`'s own `why` claims to be making —
|
||||
*"written out rather than left unhandled so that 'the game ignores it' and 'we
|
||||
never wired it' are different lines of code"* — and which was not in fact being
|
||||
made, because nothing read the value that was supposed to make it.
|
||||
|
||||
✅ The validator is **called**, not merely defined. A validator nobody invokes is
|
||||
the same defect it exists to catch, and this file now documents six other
|
||||
instances of exactly that.
|
||||
|
||||
Verified: clean boot with no invariant errors and unchanged timings; setting
|
||||
`left_right: "move"` produces the error; all five MODDING rules still pass; the
|
||||
oracle rows are unmoved.
|
||||
|
||||
@@ -202,6 +202,9 @@ func _ready() -> void:
|
||||
# own logic on purpose -- see `looping_focus` there for the census that says
|
||||
# this cannot be a rule.
|
||||
_looping = timing.get("looping_focus_records", {})
|
||||
# Called, not merely defined. A validator nobody invokes is the same defect
|
||||
# it exists to catch.
|
||||
_check_authored_invariants(timing)
|
||||
# Which decoded rules apply where. See `authored/rendering.json`.
|
||||
var rendering: Variant = export_tree.authored("rendering.json")
|
||||
_draw_leaf_for = [] if rendering == null else rendering.get("draw_leaf_for", [])
|
||||
@@ -391,6 +394,25 @@ func _process(delta: float) -> void:
|
||||
#
|
||||
# `_advance` is CAUSED by the next screen arriving, never scheduled off a
|
||||
# timer, which is what the draw stream says the game does.
|
||||
# `authored/flow.json` `dwell` is applied at the EXIT below, not here.
|
||||
#
|
||||
# 🔴 I wired it here first and it did nothing, silently -- which is the
|
||||
# defect it exists to remove, reproduced while removing it. Holding longer
|
||||
# after settle changes nothing, because the screen still leaves when
|
||||
# `exit_time() + black_hold` arrives and the extra hold is absorbed. A dwell
|
||||
# has to delay the DEPARTURE.
|
||||
#
|
||||
# It was read NOWHERE for eight milestones. The
|
||||
# block's own text says "when a capture times the real boot, the extra hold
|
||||
# per screen goes here" -- and a number placed there did nothing at all. Two
|
||||
# iterations ago I asked the Decoder for measurements destined for that slot;
|
||||
# had they arrived, they would have been filed into a value with no reader
|
||||
# and the boot would have been unchanged, silently.
|
||||
#
|
||||
# It stays EMPTY. Nothing is authored into it, because the splash dwells are
|
||||
# declared on the disc and measured to agree. This wires the slot so the day
|
||||
# a number belongs there it has an effect, which is the opposite of adopting
|
||||
# one now.
|
||||
if view.holding and view.time_units >= view.settle_time():
|
||||
# The LAST screen in the sequence keeps holding. A screen plays itself
|
||||
# out because something is taking its place; nothing is taking the
|
||||
@@ -420,7 +442,8 @@ func _process(delta: float) -> void:
|
||||
_menu_enter(String(_sequence[_step].get("screen", "")), true)
|
||||
elif _film == "" and _overlay_spec.is_empty() and _overlay_quit_at < 0.0:
|
||||
get_tree().quit(0)
|
||||
elif not view.holding and view.time_units >= view.exit_time() + _black_hold:
|
||||
elif not view.holding and view.time_units >= view.exit_time() + _black_hold \
|
||||
+ _dwell_for(String(_sequence[_step].get("screen", ""))):
|
||||
# 🔴 THE BLACK HOLD, which this port had never implemented. A transition
|
||||
# is a fade THROUGH black (HANDOFF Q7), and the pure-black plateau
|
||||
# between one screen leaving and the next arriving was measured at
|
||||
@@ -1165,3 +1188,45 @@ func _looping_for(screen_name: String) -> Dictionary:
|
||||
if parts.size() == 2 and parts[0] == screen_name:
|
||||
out[parts[1]] = _looping[key]
|
||||
return out
|
||||
|
||||
|
||||
## Extra hold for one screen, in units, from `authored/flow.json` `dwell`.
|
||||
##
|
||||
## Zero unless a measurement is authored. A value here is an ADDITION to the
|
||||
## screen's own declared group, not a replacement for it.
|
||||
func _dwell_for(screen_name: String) -> float:
|
||||
if _flow == null or not (_flow is Dictionary):
|
||||
return 0.0
|
||||
var table: Variant = (_flow as Dictionary).get("dwell", {})
|
||||
if not (table is Dictionary):
|
||||
return 0.0
|
||||
var v: Variant = (table as Dictionary).get(screen_name, 0.0)
|
||||
return float(v) if (v is float or v is int) else 0.0
|
||||
|
||||
|
||||
## Check the authored values this port CANNOT act on, and fail loudly if one
|
||||
## changes.
|
||||
##
|
||||
## `left_right`, `input_during_transition` and `ramp` are authored with reasons
|
||||
## and read by nothing -- the behaviour they describe is hardcoded. That is
|
||||
## defensible for a record and dangerous for a switch, and they are written like
|
||||
## switches: someone setting `left_right` to "move" would change nothing and get
|
||||
## no warning.
|
||||
##
|
||||
## So rather than invent the missing implementations, the port ASSERTS the value
|
||||
## it was built against. Changing one now produces an error naming the file
|
||||
## instead of silence, which is the distinction the `why` for `left_right`
|
||||
## claims to be making -- "the game ignores it" and "we never wired it" are
|
||||
## different lines of code -- and which was not actually being made.
|
||||
func _check_authored_invariants(timing: Dictionary) -> void:
|
||||
var nav: Variant = (_flow as Dictionary).get("navigation", {}) if _flow is Dictionary else {}
|
||||
if nav is Dictionary:
|
||||
var lr := String((nav as Dictionary).get("left_right", "nothing"))
|
||||
if lr != "nothing":
|
||||
push_error("authored/flow.json navigation.left_right is \"%s\"; this port implements only \"nothing\"" % lr)
|
||||
var idt := String((nav as Dictionary).get("input_during_transition", "ignored"))
|
||||
if idt != "ignored":
|
||||
push_error("authored/flow.json navigation.input_during_transition is \"%s\"; this port implements only \"ignored\"" % idt)
|
||||
var ramp := String(timing.get("ramp", "linear"))
|
||||
if ramp != "linear":
|
||||
push_error("authored/timing.json ramp is \"%s\"; ScreenView interpolates linearly and has no other mode" % ramp)
|
||||
|
||||
Reference in New Issue
Block a user