Archilyzer · Source

archilyzer

Archilyzer
git clone https://archilyzer.pages.dev/source/archilyzer.git
Log | Files | Refs | README | LICENSE

commit 6651ce562afc92cc0f460b8e4a51adfd435da752
parent 21d5f923c480d7c773283fe9fff22e6beeaf03bc
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Thu,  1 Oct 2026 16:16:12 -0400

umtool: a window save clamps a mute mark within 0.02 s of the moved edge onto it and stores it, so the build accepts every manifest updateClip saved

The writer's tolerance stays; the build's check stays strict. Clamping is
what a patched mark already got: a mark the new start passed still mutes
the whole clip, and one the new end passed moves at most 0.02 s.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Diffstat:
Mumtool/lib/report/manifest.mjs | 7++++++-
Mumtool/lib/report/manifest.test.mjs | 23+++++++++++++++++++++++
2 files changed, 29 insertions(+), 1 deletion(-)

diff --git a/umtool/lib/report/manifest.mjs b/umtool/lib/report/manifest.mjs @@ -295,8 +295,13 @@ export async function updateClip(dir, clipId, patch, { token = null } = {}) { if (v == null) delete entry.muteFrom; else entry.muteFrom = v; } else if ((patch.start !== undefined || patch.end !== undefined) && entry.muteFrom != null) { + // Clamped and STORED, as a patched mark is: a mark within the writer's + // 0.02 s of the moved edge lands on it (past the new start it still + // mutes the whole clip), and the build (validateMuteFrom, strict) + // accepts every manifest saved here. Left as it was, a start moved from 10.00 to 10.01 under a mark at + // 10.00 saved, and the next build refused the whole manifest. try { - parseMuteFrom(entry.muteFrom, entry.start, entry.end); + entry.muteFrom = parseMuteFrom(entry.muteFrom, entry.start, entry.end); } catch { throw new Error( `the mute mark ${entry.muteFrom} must lie inside the window ` + diff --git a/umtool/lib/report/manifest.test.mjs b/umtool/lib/report/manifest.test.mjs @@ -22,6 +22,7 @@ import { updateOnscreen, updatePosts, } from "./manifest.mjs"; +import { validateCutEdits } from "umtool-report-to-video/deck"; const base = () => ({ slug: "t", @@ -217,6 +218,28 @@ test("updateClip: muteFrom is a number inside the clip, rounded; empty or null d } }); +test("updateClip: a window edge moved within 0.02 s past the mute mark clamps the mark onto it, so the build accepts what was saved", async () => { + const dir = await project(); + try { + // c01 is 10–20. A mark on the start, then the start moved a hundredth on. + await updateClip(dir, "c01", { muteFrom: 10 }); + await updateClip(dir, "c01", { start: 10.01 }); + let m = await read(dir); + assert.equal(entry(m, "c01").muteFrom, 10.01, "the mark moves onto the new start"); + assert.deepEqual(validateCutEdits(m), [], "and the build's strict check takes it"); + // The same at the end. + await updateClip(dir, "c01", { muteFrom: 20 }); + await updateClip(dir, "c01", { end: 19.99 }); + m = await read(dir); + assert.equal(entry(m, "c01").muteFrom, 19.99); + assert.deepEqual(validateCutEdits(m), []); + // Further than the tolerance is still refused. + await assert.rejects(updateClip(dir, "c01", { end: 19.9 }), /mute mark 19.99 must lie inside the window/); + } finally { + await rm(dir, { recursive: true, force: true }); + } +}); + const DECK = { engine: "hyperframes", layout: "deck", deck: { height: 180, title: { size: 60 } } }; test("updateChrome: round trip stores the block as given; null removes it", async () => {