port: take formats-pin-2026-08-30b and stop owning the +0x08 read

The tag was cut within the iteration, so screen.rs now calls
ui_layout::loop_length_units and its local RATC guard and byte read are deleted.
One line, as predicted -- and the doc comment promising that deletion is the only
reason a temporary reading did not quietly become permanent.

A pin bump moves the whole crate, not one function, and this pin is recorded
load-bearing, so both commits between the tags were read before taking it:
b5df02a adds the public function, d020845 is comment-only -- two 'fixed code
under an unfixed description' corrections. No behavioural change in either, and
the oracle RMSEs confirm it: main_menu 13.21, extras 13.38, title 14.16, all
identical across the bump.

d020845 is worth noting for what it is. The rest override's comment still claimed
it tested the shifted time reading that the record-layout fix had refuted, and a
continue branch was documented with the pre-fix rule. Both are the same failure
this port hit in spin_period_units: a doc comment describing the rule the body no
longer implements. Three instances now, across two agents and two languages.

examples/record_loop_control.rs deliberately did NOT follow the API. The moment a
control calls the thing it exists to check it stops being a control and becomes
the API tested against itself; the falsifier means something only because the
reading is independent. Re-run at the new pin, unchanged. So the port holds one
copy of this reading instead of two, and it is the copy whose job is to disagree.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF
This commit is contained in:
Sylpheed port agent
2026-08-30 21:27:54 +00:00
parent abaa9de4e3
commit 0e41f5381d
5 changed files with 57 additions and 27 deletions

4
Cargo.lock generated
View File

@@ -4627,7 +4627,7 @@ dependencies = [
"image",
"serde",
"serde_json",
"sylpheed-formats 0.1.0 (git+https://git.mc02.dev/fabi/Sylpheed.git?tag=formats-pin-2026-08-30)",
"sylpheed-formats 0.1.0 (git+https://git.mc02.dev/fabi/Sylpheed.git?tag=formats-pin-2026-08-30b)",
]
[[package]]
@@ -4651,7 +4651,7 @@ dependencies = [
[[package]]
name = "sylpheed-formats"
version = "0.1.0"
source = "git+https://git.mc02.dev/fabi/Sylpheed.git?tag=formats-pin-2026-08-30#c8d3a6a15d5feb0416b2b589cb8e0c442f7422d3"
source = "git+https://git.mc02.dev/fabi/Sylpheed.git?tag=formats-pin-2026-08-30b#b5df02a4f74c80b93b93cd1a26296ee08040d826"
dependencies = [
"anyhow",
"binrw",

View File

@@ -81,7 +81,7 @@ license.workspace = true
# geometry -- does NOT reach this port from here: `sylpheed-cli` builds from the
# WORKSPACE crate, so the reference renderer stays unrotated until the tag lands
# on `main`. This bump is for the parser, not for the renderer.
sylpheed-formats = { git = "https://git.mc02.dev/fabi/Sylpheed.git", tag = "formats-pin-2026-08-30" }
sylpheed-formats = { git = "https://git.mc02.dev/fabi/Sylpheed.git", tag = "formats-pin-2026-08-30b" }
serde = { version = "1", features = ["derive"] }
serde_json = "1"

View File

@@ -8,10 +8,15 @@
//! would fix it is decoded in an *example* and a *test* on the Decoder's branch
//! and **exposed in `sylpheed_formats`' public API on no ref at all**.
//!
//! It is still reachable: `parse_build` publishes each record's `(offset, size)`,
//! so reading a big-endian `u32` at `+0x08` of a span whose magic is `RATC` is
//! consuming a delivered finding, not decoding a format. What must not be
//! consumed on trust is the READING. So this re-runs both of their controls:
//! ✅ **Since then the crate exposes it** — `ui_layout::loop_length_units`, taken
//! at `formats-pin-2026-08-30b` — and `screen.rs` has deleted its local copy.
//!
//! 🔴 **This file deliberately did NOT follow it.** The read below is still the
//! raw four bytes, because the moment a control calls the API it is meant to
//! check, it stops being a control and becomes the API tested against itself. It
//! is the independent reading that makes the falsifier mean anything.
//!
//! So this re-runs both of their controls:
//!
//! * **the falsifier** — `+0x08 < max keyframe time` must never occur; an
//! animation cannot restart before its own last pose;

View File

@@ -108,18 +108,6 @@ pub struct FocusElement {
pub keyframes: Vec<Keyframe>,
}
/// A nested record's declared cycle length, or `None` if the span is not one.
///
/// Guarded rather than trusted: the magic is checked and the header must fit,
/// because an offset that has drifted returns a plausible number otherwise.
fn record_loop_units(bundle: &[u8], off: usize, size: usize) -> Option<u32> {
let span = bundle.get(off..off.checked_add(size)?)?;
if span.len() < 12 || &span[..4] != b"RATC" {
return None;
}
Some(u32::from_be_bytes(span[8..12].try_into().ok()?))
}
#[derive(Serialize)]
pub struct Focus {
/// The `.rat` leaf this came from, e.g. `ptbtn01f.rat`.
@@ -140,11 +128,12 @@ pub struct Focus {
/// their own last pose**; on the eight records this port animates, seven
/// exact and `ptbtn00f` the one hold.
///
/// ⚠️ Read here rather than through `sylpheed_formats` because the field is
/// exposed in **no public API on any ref** — it lives in an example and a
/// test. `parse_build` publishes each record's `(offset, size)`, so this is
/// four big-endian bytes at a documented offset inside a span whose magic is
/// checked, not a second decoder. **Delete it the day the crate exposes it.**
/// ✅ **The port no longer owns this reading.** For one iteration `screen.rs`
/// held its own guard and byte read, because the field was decoded in an
/// example and a test and exposed in no public API on any ref. It is now
/// `ui_layout::loop_length_units`, taken at `formats-pin-2026-08-30b`, and
/// the local copy is deleted — the doc comment that promised that deletion
/// is the only reason it did not quietly become permanent.
pub loop_length_units: Option<u32>,
/// Back-to-front, in the leaf's own declaration order.
pub elements: Vec<FocusElement>,
@@ -489,7 +478,7 @@ pub fn export_build(
} else {
Some(Focus {
record: rec.to_string(),
loop_length_units: record_loop_units(bundle, off, size),
loop_length_units: ui_layout::loop_length_units(&bundle[off..off + size]),
elements: fes,
})
})
@@ -550,7 +539,7 @@ pub fn export_build(
if !fes.is_empty() {
focus = Some(Focus {
record: rec,
loop_length_units: record_loop_units(bundle, off, size),
loop_length_units: ui_layout::loop_length_units(&bundle[off..off + size]),
elements: fes,
});
}

View File

@@ -9,7 +9,7 @@ dies, which is what this file is for.
<!-- INDEX: generated by tools/port/index-decisions -- do not hand-edit -->
239 sections. Search this before re-deriving anything.
240 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)
@@ -250,6 +250,7 @@ dies, which is what this file is for.
* [A refutation attempt on the fade numbers — it survives, from a third reader](#a-refutation-attempt-on-the-fade-numbers--it-survives-from-a-third-reader)
* [The walk is checked too, and "only the ring moves" tested against my own renderer](#the-walk-is-checked-too-and-only-the-ring-moves-tested-against-my-own-renderer)
* [The `+0x08` ask came back answered — and is not consumable yet](#the-0x08-ask-came-back-answered--and-is-not-consumable-yet)
* [The pin moves to `formats-pin-2026-08-30b`, and the port stops owning `+0x08`](#the-pin-moves-to-formats-pin-2026-08-30b-and-the-port-stops-owning-0x08)
<!-- /INDEX -->
## P0 — the exporter, 2026-08-28
@@ -12483,3 +12484,38 @@ big-endian read. So the deletion my doc comment promises is a one-line switch.
branch would replace a deliberate pin with an incidental one — and `BLOCKED.md`
already records this pin as load-bearing. **Asked for a tag; keeping the local
read, which is guarded and controlled, until one exists.**
## The pin moves to `formats-pin-2026-08-30b`, and the port stops owning `+0x08`
The tag was cut within the iteration, so the deletion the doc comment promised is
done: `screen.rs` calls `ui_layout::loop_length_units` and its local `RATC` guard
and byte read are **gone**. One line, exactly as predicted — the promise in the
comment is the only reason a temporary reading did not quietly become permanent.
**What the pin actually brings, checked before taking it.** A pin bump moves the
whole crate, not one function, and this one is recorded load-bearing, so the two
commits between the tags were read rather than assumed:
| | |
|---|---|
| `b5df02a` | adds the public `loop_length_units` |
| `d020845` | **comment-only** — two "fixed code under an unfixed description" corrections |
No behavioural change in either. `d020845` is worth noting for what it is: the
`rest` override's stated purpose was retired by the record-layout fix and the
comment still claimed it tested the shifted reading, and a `continue` branch was
documented with the pre-fix rule. **Both are the same failure this port hit in
`spin_period_units` — a doc comment describing the rule the body no longer
implements.** Three instances now, across two agents and two languages.
### 🔴 The control did NOT follow the API, on purpose
`examples/record_loop_control.rs` still reads the raw four bytes. **The moment a
control calls the API it exists to check, it stops being a control and becomes
the API tested against itself.** The falsifier — 0 of 1 781 records declaring
less than their own last pose — means something only because the reading is
independent of the crate's. Re-run at the new pin: unchanged, 7 exact and
`ptbtn00f` the one hold.
📌 So the port now holds **one** copy of this reading instead of two, and it is
the copy whose job is to disagree.