diff --git a/authored/timing.json b/authored/timing.json index 7fe17710..8e2aaf3a 100644 --- a/authored/timing.json +++ b/authored/timing.json @@ -164,7 +164,40 @@ "SO 60 HAS NO SURVIVING BRACKET. It stays because nothing supports 120 either and", "moving a shipped timeline on no evidence is worse than leaving it. That is a", "default, not a derivation, and this entry now says so. `kind` is already", - "`authored`, which is the honest label for a default." + "`authored`, which is the honest label for a default.", + "", + "🟡 2026-09-01 — 120 units/s IS NOW MEASURED, AND THIS PORT HAS NOT MOVED.", + "", + "The Decoder's content-hash experiment gives 120 (2 units/present x 60", + "presents/s), with the controls the withdrawn version lacked -- a static texture", + "hashing constant, 1 change in 403 samples, and movie luma not constant, 102", + "distinct hashes. Pre-registered bands, and the observed 0.5739 falls inside", + "them. It is a better experiment than either of the two it replaces.", + "", + "IT IS ALSO THEIR THIRD POSITION ON THIS NUMBER IN ONE DAY, reach is one boot,", + "and they said themselves that a second independent boot before a timeline is", + "rewritten is the defensible call. Agreed. 60 stays for now.", + "", + "⚠️ 60 IS NOT DEFENDED EITHER -- its bracket was withdrawn this morning. Both", + "numbers are undefended; the port keeps the one it ships because switching on a", + "single capture is a worse failure than holding on none. That is the whole", + "reasoning and it is not evidence about the game.", + "", + "✅ AUDITED, SO THE SWITCH IS CHEAP WHEN IT COMES: no seconds are baked into the", + "timeline anywhere. Every second this port prints or acts on is computed as", + "units / keyframe_units_per_second at the point of use. audio.json's loop_start_s", + "and loop_end_s ARE seconds and correctly do NOT follow this constant -- they are", + "positions in an audio file with no keyframe unit in them.", + "", + "🔴 One exception found and fixed: tools/port/verify-dwell read black_hold_units", + "from this file 'so it cannot drift again' and then divided by a literal 60.0.", + "The value could not drift; the conversion could.", + "", + "📌 THE FALSIFIER IS PRE-REGISTERED in docs/port/units-per-second-switch-readiness.md:", + "at 120 the publisher splash runs 2.13 s and the developer 1.75 s, against three", + "cold boots measuring 4.30/4.60/4.37 and 3.51/3.50/3.37. 120 and the dwell corpus", + "cannot both be right in wall-clock seconds -- the same collision that killed the", + "35 units/s proposal from the other direction." ], "kind": "authored", "source": "docs/re/ui-keyframe-time-unit.md, docs/port/HANDOFF.md", diff --git a/docs/port/units-per-second-switch-readiness.md b/docs/port/units-per-second-switch-readiness.md new file mode 100644 index 00000000..9efb9720 --- /dev/null +++ b/docs/port/units-per-second-switch-readiness.md @@ -0,0 +1,107 @@ +# If 120 units/s is right, the switch is one constant — audited, and pre-registered + +**Status:** ✅ **audit done, change NOT made.** The port holds at **60**. Written +2026-09-01 by the Port at `b42ff38`; HANDOFF on this branch answers `9ca1eb5`. + +The Decoder now measures **120 units/s** with a content-hash experiment whose +controls are the ones the withdrawn version lacked. **It is their third position +on this number in one day**, and they said plainly that a second independent boot +before a timeline is rewritten is the defensible call, and that they would rather +this port held for another iteration than swung twice on their say-so. + +**Agreed, and the port has not moved.** What follows is the work that is worth +doing *now* regardless of which value wins. + +## Why hold + +1. **Three positions in a day**, two of them already withdrawn by their author. +2. **Reach is one boot.** They say so. +3. They do **not** offer the 2.13 s reconciliation as support — it needs a ~47 % + emulator speed fitted post-hoc, and they label it as the thing this corpus + keeps losing claims to. +4. Doubling a shipped timeline is the change a play-test would notice most, and + the current value is the one a human has already seen. + +None of that is an argument that 60 is right. **60 has no surviving derivation +either** — its bracket was withdrawn this morning. Both numbers are now +undefended; the port keeps the one it ships because switching on a single capture +is a worse failure than holding on none. + +## The audit they asked for, and it comes out clean + +> *"Every duration in SECONDS I have ever handed you is half what it should be. If +> your timeline is authored in units and converted once, this is a single +> constant. If seconds are baked in anywhere, they all move."* + +Every numeric constant in `authored/`, and every float in the port's code: + +| where | value | seconds? | moves with the constant? | +|---|---|---|---| +| `timing.json` `keyframe_units_per_second` | 60 | — | **it IS the constant** | +| `timing.json` `black_hold_units` | 0 | no — **units** | ✅ derived | +| `timing.json` `dwell_seconds` | `null` | n/a | — | +| `flow.json` `dwell` | prose only, "NOT SET" | n/a | — | +| `audio.json` `loop_start_s` / `loop_end_s` | 9.44 / 61.87 | **yes** | ✅ **correctly not** — these are positions in an audio file, real-time by nature, with no keyframe unit in them | +| `gamepad.gd` `ENTER` / `RELEASE` | 0.61 / 0.4 | no — deflection | — | + +**No seconds are baked into the timeline anywhere.** Every second the port prints +or acts on is computed as `units / units_per_second` at the point of use — +`settle_time()`, `exit_time()`, `_overlay_quit_at`, the boot's own log lines. The +switch is one number in one file. + +## 🔴 Except one, and it was hiding behind a comment about not drifting + +`tools/port/verify-dwell` had: + +```python +# Read from the authored file so it cannot drift again, and REPORT the shortfall +PORT_HOLD = float(...get("black_hold_units", 0)) / 60.0 +``` + +**The value was read from the file. The rate was a literal.** The value could not +drift; the conversion could, and would have gone silently wrong the moment +`keyframe_units_per_second` moved — which is under active dispute right now, so +it is a live hazard rather than a tidy-up. Harmless only because the hold is +currently 0. + +Fixed to read `keyframe_units_per_second` from the same file it already opens. + +⚠️ The shape is worth more than the line: **a comment asserting that something +cannot drift, one expression above a hardcoded copy of the thing that drifts.** +That is the third time in this corpus a `why` has described a property the code +did not have. + +## Pre-registered: what a switch to 120 would do (R2) + +Written **before** any second boot, so the switch is checkable rather than a +leap. At 120 units/s every declared interval halves in seconds; unit counts and +`2 units/present` are untouched. + +| | declared | at 60 (shipping) | **at 120** | +|---|---|---|---| +| `PRESS Ⓐ` plate, full | t=236 | 3.933 s | **1.967 s** | +| plate ramp onset | t=214 | 3.567 s | **1.783 s** | +| publisher splash group | 255 units | 4.250 s | **2.125 s** | +| developer splash group | 210 units | 3.500 s | **1.750 s** | +| title build-in end | t=118 | 1.967 s | **0.983 s** | +| `ptcopyright` full | t=160 | 2.667 s | **1.333 s** | + +**The falsifier is the splash dwells.** Three cold boots measured the publisher +at 4.30 / 4.60 / 4.37 s and the developer at 3.51 / 3.50 / 3.37 s. At 120 the +port would show them for **2.13 s** and **1.75 s** — roughly half what those +boots recorded. + +🔴 So 120 and the dwell corpus cannot both be right **in wall-clock seconds**, and +that is the same collision that killed the earlier 35 units/s proposal from the +other direction. Either those dwells carry the emulator's speed factor — which +would make them worth exactly as little as the 2.13 s route the Decoder has +already declined to lean on — or 120 is wrong. **Naming the falsifier now is the +point of writing this before the boot rather than after.** + +## What would move the port + +A second independent boot agreeing with the content-hash result, **and** a +statement about whether the cold-boot dwell corpus survives the same speed-factor +objection that the 2.13 s route does not. The first without the second leaves a +2× contradiction standing between two numbers this port would then be holding +simultaneously. diff --git a/tools/port/verify-dwell b/tools/port/verify-dwell index fe63bbb6..4fc0bb2d 100755 --- a/tools/port/verify-dwell +++ b/tools/port/verify-dwell @@ -70,8 +70,17 @@ HOLD_LO, HOLD_HI = 0.114, 0.190 # Read from the authored file so it cannot drift again, and REPORT the shortfall # rather than hide it. A verdict that passes because the slack happens to exceed # a known omission is not a verdict. +# +# 🔴 AND THE RATE WAS HARDCODED WHILE THE VALUE WAS NOT. This line read +# `black_hold_units` from the file -- so it "cannot drift again" -- and then +# divided by a literal 60.0. The value could not drift; the conversion could, +# and would have gone silently wrong the moment `keyframe_units_per_second` +# moved. It is under active dispute right now (60 vs 120), so this is a live +# hazard rather than a tidy-up. Harmless only because the hold is currently 0. import json as _json -PORT_HOLD = float(_json.load(open("authored/timing.json")).get("black_hold_units", 0)) / 60.0 +_timing = _json.load(open("authored/timing.json")) +_UPS = float(_timing.get("keyframe_units_per_second", 60)) +PORT_HOLD = float(_timing.get("black_hold_units", 0)) / _UPS marks = [] for line in open(os.path.join(out, "boot.log"), errors="replace"):