port: the contract I read is 3185 lines shorter than the contract
docs/port/HANDOFF.md on main is 926 lines, last touched9ca1eb5on 2026-08-29. The live one is 4111 lines at27938aa, +3930/-745 across 96 commits I have never read, several of them addressed to the port by name. The Decoder writes HANDOFF on origin/auto/no-disc-and-menu-captures; main is a hundred-odd commits behind it; I open main's copy every iteration as instructed. So the rule meant to prevent this cannot detect it. tools/port/blocked-provenance recovers each row's derivation from history rather than memory -- git log -S on the row's key phrase -- and all 27 open rows derive from9ca1eb5, because HANDOFF-on-main has not moved. A constant cannot separate a fresh row from a rotten one. Withdrawn in BLOCKED.md: 'HANDOFF has not moved in four milestones' was missing the qualifier that carried its meaning. The tool's first version silently missed its own known positive: P6 looping vs712cac8, whose 9.44 s answer this port already ships. 'looping' did not stem to 'loop', 'menu' was stoplisted, and a >=2-shared-words threshold dropped the rest. The threshold was the defect -- two common words outscored one rare one -- so ranking is now by log(N/df) with no cutoff at all, and the control passes at rank 1 of 7 without touching the stoplist. Every discard is counted: struck rows, sub-rank pairs, stoplisted words. Same rule applied to check-claims, which now reports the 40 occurrences it suppresses; the Decoder reached it the same day from the opposite failure, a silent suppression path making a clean run unfalsifiable. The reading list found two open rows already answered: the plate's pulse period (120, not 105) and the main menu having no idle self-return, which refutes the B row's own reasoning. Refutation attempted on '+0x08 is the loop length', the claim the port was about to build on. It survives: their falsifier re-run on my own read of the disc gives 0 violations in 1781 records, and on the eight records this port animates their table reproduces cell for cell. Adopted -- screen.rs exports loop_length_units and ScreenView._loop_period prefers it, announcing any disagreement rather than silently resolving it. The value does not change: authored/timing.json already had 120 from a wall-clock measurement, so a disc field and an emulator stopwatch agree while sharing no instrument. Two asks filed: the field is exposed in no public API on any ref, so the port reads four bytes it should not own; and eleven focus records declare the same 120-unit cycle while only the plate is authored to animate, which is behavioural and not mine to infer. Every asserting check passes; oracle RMSEs unchanged, as 120 == 120 predicts. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF
This commit is contained in:
106
crates/sylpheed-export/examples/record_loop_control.rs
Normal file
106
crates/sylpheed-export/examples/record_loop_control.rs
Normal file
@@ -0,0 +1,106 @@
|
||||
//! Run the Decoder's own falsifier for "a nested record's `+0x08` is its loop
|
||||
//! length" against the bundles THIS PORT SHIPS, before shipping 120 for 105.
|
||||
//!
|
||||
//! HANDOFF (`27938aa`, delivered at `07e93ce`) says the plate's glow cycles over
|
||||
//! **120** units while its keyframes end at 105, and instructs the port to stop
|
||||
//! shipping 105. The port's `ScreenView` derives a looping record's period from
|
||||
//! the element's largest keyframe time, so it does ship 105 — and the field that
|
||||
//! 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:
|
||||
//!
|
||||
//! * **the falsifier** — `+0x08 < max keyframe time` must never occur; an
|
||||
//! animation cannot restart before its own last pose;
|
||||
//! * **non-triviality** — if every record had `+0x08 == max t` the field would
|
||||
//! carry nothing and the name would be a relabelling of the keyframes.
|
||||
//!
|
||||
//! and adds the one they could not run: the same two, restricted to the records
|
||||
//! **this port actually animates**. A disc-wide 0.00 % violation rate says
|
||||
//! nothing about my six screens if all six sit in the exceptional tail.
|
||||
use sylpheed_formats::{pak, ratc, ui_layout};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
/// The records the port animates: the plate glow, the five menu focus records,
|
||||
/// and the title's two sweeps. Named rather than pattern-matched, because the
|
||||
/// point is to check the ones that are shipped, not the ones that match a glob.
|
||||
const SHIPPED: &[&str] = &[
|
||||
"ptbtn00f", "ptbtn01f", "ptbtn02f", "ptbtn03f", "ptbtn04f", "ptbtn05f",
|
||||
"ptloop01", "ptloop02",
|
||||
];
|
||||
|
||||
fn main() {
|
||||
let root = std::env::var("SYLPHEED_DISC").unwrap_or_else(|_| "/disc".into());
|
||||
let mut paks: Vec<_> = std::fs::read_dir(format!("{root}/dat")).expect("dat/")
|
||||
.flatten().map(|e| e.path())
|
||||
.filter(|p| p.extension().and_then(|s| s.to_str()) == Some("pak")).collect();
|
||||
paks.sort();
|
||||
|
||||
let (mut total, mut exact, mut holds, mut violations) = (0usize, 0usize, 0usize, 0usize);
|
||||
let mut slack_hist: BTreeMap<i64, usize> = BTreeMap::new();
|
||||
let mut shipped: BTreeMap<String, (i64, i64)> = BTreeMap::new();
|
||||
|
||||
for p in &paks {
|
||||
let Ok(ar) = pak::PakArchive::open(p) else { continue };
|
||||
for e in ar.entries() {
|
||||
let Ok(by) = ar.read(e) else { continue };
|
||||
if !ratc::is_ratc(&by) { continue }
|
||||
let Some(b) = ui_layout::parse_build(&by) else { continue };
|
||||
for (rn, &(o, s)) in &b.records {
|
||||
if o + 12 > by.len() || o + s > by.len() { continue }
|
||||
if &by[o..o + 4] != b"RATC" { continue }
|
||||
let len = u32::from_be_bytes(by[o + 8..o + 12].try_into().unwrap()) as i64;
|
||||
let Some(lb) = ui_layout::parse_build(&by[o..o + s]) else { continue };
|
||||
let maxt = lb.elements.iter()
|
||||
.flat_map(|el| el.keyframes.iter().filter_map(|k| k.time))
|
||||
.max().unwrap_or(0) as i64;
|
||||
if maxt == 0 { continue } // static: declares no cycle at all
|
||||
total += 1;
|
||||
let slack = len - maxt;
|
||||
*slack_hist.entry(slack).or_default() += 1;
|
||||
if slack == 0 { exact += 1 } else if slack > 0 { holds += 1 } else { violations += 1 }
|
||||
let stem = rn.trim_end_matches(".rat");
|
||||
if SHIPPED.contains(&stem) {
|
||||
shipped.entry(stem.to_string()).or_insert((len, maxt));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
println!("disc-wide, records with timed keyframes: {total}");
|
||||
println!(" +08 == max t (exact) : {exact:5} {:5.1} %", pc(exact, total));
|
||||
println!(" +08 > max t (a hold) : {holds:5} {:5.1} %", pc(holds, total));
|
||||
println!(" +08 < max t <- FALSIFIER : {violations:5} {:5.2} %", pc(violations, total));
|
||||
println!("\nslack distribution, most common first:");
|
||||
let mut h: Vec<_> = slack_hist.iter().collect();
|
||||
h.sort_by_key(|&(_, n)| std::cmp::Reverse(*n));
|
||||
for (k, n) in h.iter().take(8) { println!(" slack {k:>6} : {n}"); }
|
||||
|
||||
println!("\nthe records THIS PORT animates:");
|
||||
println!(" {:<12} {:>6} {:>7} {:>7}", "record", "+0x08", "max t", "slack");
|
||||
let (mut ship_exact, mut ship_hold, mut ship_bad) = (0, 0, 0);
|
||||
for (n, (len, maxt)) in &shipped {
|
||||
let slack = len - maxt;
|
||||
match slack { 0 => ship_exact += 1, s if s > 0 => ship_hold += 1, _ => ship_bad += 1 }
|
||||
println!(" {n:<12} {len:>6} {maxt:>7} {slack:>7}{}",
|
||||
if slack < 0 { " 🔴 FALSIFIED" } else { "" });
|
||||
}
|
||||
println!("\n shipped: {ship_exact} exact, {ship_hold} hold, {ship_bad} falsified");
|
||||
if shipped.len() < SHIPPED.len() {
|
||||
let missing: Vec<_> = SHIPPED.iter().filter(|s| !shipped.contains_key(**s)).collect();
|
||||
println!(" ⚠️ not found on the disc: {missing:?} -- a name the port ships and");
|
||||
println!(" this control never checked is worse than a violation it found.");
|
||||
}
|
||||
println!("\n verdict: {}", if ship_bad > 0 {
|
||||
"🔴 the reading fails on a record the port animates -- do NOT adopt"
|
||||
} else if ship_hold == 0 {
|
||||
"⚠️ every shipped record is exact, so this port cannot tell loop length\n from max keyframe time -- adopting 120 would change nothing here"
|
||||
} else {
|
||||
"✅ falsifier clean and the field is non-trivial ON THE SHIPPED SET"
|
||||
});
|
||||
}
|
||||
|
||||
fn pc(n: usize, d: usize) -> f64 { if d == 0 { 0.0 } else { 100.0 * n as f64 / d as f64 } }
|
||||
@@ -108,10 +108,44 @@ 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`.
|
||||
pub record: String,
|
||||
/// The record header's `+0x08`: **where the cycle restarts**, in keyframe
|
||||
/// units — which is not the same thing as the last keyframe's time.
|
||||
///
|
||||
/// `ptbtn00f`, the `PRESS Ⓐ` plate's glow, ramps 0→80→0 over **105** units
|
||||
/// inside a **120**-unit cycle and rests dark for the remaining 15. Deriving
|
||||
/// the period from the largest keyframe time — what the port did until now —
|
||||
/// runs it 14 % fast and deletes the dark rest entirely.
|
||||
///
|
||||
/// Decoded by the Decoder (`07e93ce`, `docs/re/structures/ui-record-loop-length.md`,
|
||||
/// delivered in HANDOFF `27938aa`) and **re-run here before adoption**, with
|
||||
/// their falsifier and their non-triviality control:
|
||||
/// `cargo run -p sylpheed-export --example record_loop_control`. Disc-wide
|
||||
/// 1 781 timed records, 92.3 % exact, 7.7 % hold, **0 declaring less than
|
||||
/// 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.**
|
||||
pub loop_length_units: Option<u32>,
|
||||
/// Back-to-front, in the leaf's own declaration order.
|
||||
pub elements: Vec<FocusElement>,
|
||||
}
|
||||
@@ -453,7 +487,11 @@ pub fn export_build(
|
||||
Ok(if fes.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(Focus { record: rec.to_string(), elements: fes })
|
||||
Some(Focus {
|
||||
record: rec.to_string(),
|
||||
loop_length_units: record_loop_units(bundle, off, size),
|
||||
elements: fes,
|
||||
})
|
||||
})
|
||||
};
|
||||
|
||||
@@ -510,7 +548,11 @@ pub fn export_build(
|
||||
});
|
||||
}
|
||||
if !fes.is_empty() {
|
||||
focus = Some(Focus { record: rec, elements: fes });
|
||||
focus = Some(Focus {
|
||||
record: rec,
|
||||
loop_length_units: record_loop_units(bundle, off, size),
|
||||
elements: fes,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user