diff --git a/docs/port/DECISIONS.md b/docs/port/DECISIONS.md index 9267eaee..a2a9febd 100644 --- a/docs/port/DECISIONS.md +++ b/docs/port/DECISIONS.md @@ -9,7 +9,7 @@ dies, which is what this file is for. -170 sections. Search this before re-deriving anything. +171 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) @@ -181,6 +181,7 @@ dies, which is what this file is for. * [🔴 CORRECTION: my "the eras render identically" measurement was void](#correction-my-the-eras-render-identically-measurement-was-void) * [🔴 CORRECTION: my branch *is* the stale era, and the reference binary was never the workspace build](#correction-my-branch-is-the-stale-era-and-the-reference-binary-was-never-the-workspace-build) * [`exit_ramp_units`: the refuted constant was living in a default](#exit_ramp_units-the-refuted-constant-was-living-in-a-default) +* [Auditing the whole tree for "a deleted value that something still supplies"](#auditing-the-whole-tree-for-a-deleted-value-that-something-still-supplies) ## P0 — the exporter, 2026-08-28 @@ -9759,3 +9760,48 @@ Last iteration I said the era guard "closes that for `verify-screen` only, not f the other tools that call the CLI". ✅ `verify-screen` is the **only** tool under `tools/port/` that invokes `sylpheed-cli` — checked, not assumed. The guard covers every caller there is. + +## Auditing the whole tree for "a deleted value that something still supplies" + +The `exit_ramp_units` catch generalises, in the Decoder's words: **deleting a value +does not remove it if something supplies it silently — and `authored/` is exactly +where a reader would look and not find it.** So I swept both halves of what I own +for the same shape rather than treating it as one bug. + +### GDScript: every keyed lookup with a fallback + +| key | default | in `authored/`? | +|---|---|---| +| `period_units`, `record_element` | `0.0`, `""` | ✅ | +| `black_hold_units` | `0.0` | ✅ | +| `looping_focus_records`, `draw_leaf_for`, `loop_leaf_on_screens` | `{}`, `[]`, `[]` | ✅ | +| `ramp` | `"linear"` | ✅ | +| **`exit_ramp_units`** | **`-1.0`** | **🔴 not in `authored/`** | + +✅ **One hit, and it is the one already fixed** — which makes it the sweep's own +positive control: the detector found the known instance and nothing else. Its +default is now `-1.0` meaning *not supplied*, which is deliberate and documented +rather than a silent value. + +The other numeric fallbacks in `screen_view.gd` are identity or sentinel — +`rotation_deg → 0` is *no rotation*, `period_units → 0.0` is *no loop* and is +guarded by `> 0.0`, `index → -1` is a sentinel. None of them invents a quantity. + +### The exporter: `serde(default)` does the same thing in Rust + +14 sites. All but one attach to `Option`, a `Vec` or a map — absent key becomes +`None`/empty, which asserts nothing. + +⚠️ **My classifier produced a false positive and I nearly wrote it up.** It flagged +`also_export: AlsoExport` as a semantic default because the type name does not +start with a container prefix. `AlsoExport` is a **type alias for a `BTreeMap`**; +its default is an empty map. Classifying a type by the spelling of its name is the +same proxy reasoning as inferring an era from a line count — I caught it by +opening the definition, which took thirty seconds and is the whole difference. + +### Result + +✅ **Nothing new.** One instance across the port and the exporter, already fixed. +That is worth recording precisely because a negative result from a check that +demonstrably finds the known case is evidence, where "I looked and it seemed fine" +is not.