Skip to content

motion-doctrine: seam-gate speed-match is a ±0.1 s window average, so seam-stamp's default durations score 1.70 (x/y) and 3.7 (z); eases don't fix it, ledger dur options do #7021

Open

Description

@jojinkoo

Summary

motion-doctrine's seam-gate.mjs measures entry/exit velocity as an average over roughly ±0.1 s around the cut. With the durations seam-stamp.mjs writes by default (x/y exit 0.34 s → entry 0.42 s; z exit 0.21 s → entry 0.5 s) the speed-match row reports ratio 1.70 on every x/y seam and 3.6–3.7 (WARN) on z seams, on a film that is otherwise stamped "by construction". Swapping the stamped eases for mirrored or steeper ones makes the ratio worse; only the per-seam exit.dur / entry.dur ledger options bring it to ~1.

Repo-native skill (.agents/skills/motion-doctrine, metadata.internal: true), so feel free to triage as low priority. Measured 2026-11-02 against main b23b51f, HyperFrames CLI 0.8.216, GSAP 3.14.2, macOS arm64, chrome-headless-shell 156 from the puppeteer cache.

Reproduction

A 12.7 s, 7-scene 1080×1080 film with six seams (↓ ← ↑ → z+ z−), ledger.json written first, seams stamped with seam-stamp.mjs --write, verified with:

node seam-gate.mjs verify --ledger ledger.json --project . --server-cmd "hyperframes preview --foreground --no-open --port {port}" --json

All variants pass every other row (ledger, exit/entry vector, zero-overlap, z-sign-scan) — only speed-match moves.

Changing the eases (durations as stamped)

variant exit / entry ease travel · duration x/y ratio (4 seams, identical) z ratio (2 seams)
stamp default power3.in / power4.out (z: expo.out) 12 %·0.34 s / 10 %·0.42 s 1.70 3.64 · 3.71 WARN
expo.in / expo.out same 2.88 7.34 · 7.54 WARN
mirrored power4.in / power4.out (cut-the-curve §3 shape) 12 %·0.34 s both sides 2.60 3.64 · 3.71
expo.in / expo.out, matched travel+duration 12 %·0.34 s both sides 3.84 WARN 7.34 · 7.54 WARN

Mirrored eases match the instantaneous velocity at the cut frame, but the gate averages over a window, so an ease that concentrates its velocity at the cut is under-read on exit and over-read on entry.

Changing durations only (stamped eases untouched, via ledger options)

x/y entry.dur z exit.dur / entry.dur x/y ratio z ratio
0.65 0.21 / 0.9 1.23 2.43 · 2.48
0.85 0.21 / 1.2 0.99 1.93 · 1.97
1.10 0.21 / 1.5 0.79 1.61 · 1.64
0.85 0.12 / 0.9 0.99 12.2 · 12.5 (a 0.12 s exit barely moves inside the window)
0.85 0.45 / 0.9 0.99 1.72 · 1.77
0.85 0.45 / 1.4 0.99 1.21 · 1.24
0.85 0.45 / 1.7 0.99 1.03 · 1.05 → PASSED 0 fail 0 warn

hyperframes check stayed at 0 errors throughout.

Suggestions (any one would do)

  1. Make the stamp defaults closer to what the gate wants (e.g. x/y entry.dur 0.85; z exit.dur 0.45, entry.dur 1.7), or
  2. document in references/seam-gate.md that speed-match is a ±0.1 s window average and list the duration ranges that satisfy it, or
  3. have seam-stamp derive entry.dur from exit.dur so the window averages match by construction.

Also a small note for cut-the-curve §3: "mirrored eases → velocity matches exactly at the cut" is true pointwise but is not what seam-gate measures; a one-line caveat would save a round.

Happy to share the test film (fictional brand, no third-party assets) if useful.

Activity

  1. added
    bugSomething isn't working
    triage/readyAccepted scope, current evidence, acceptance criteria, and validation path are recorded.
    difficulty/easyLocalized change with a known approach, limited risk, and straightforward verification.
    on Nov 2, 2026
  2. jrusso1020 commented on Nov 2, 2026

    @jrusso1020
    Collaborator

    Thanks, this is a great writeup. We confirmed it: seam-gate samples each side at cut ±0.1 s and compares those averages, while seam-stamp's defaults (x/y 0.34/0.42 s, z 0.21/0.5 s) were tuned for the instantaneous match at the cut frame. We'll bring the stamp defaults in line with what the gate measures and add a note on the window to seam-gate.md and cut-the-curve §3. Low priority since it's a repo-native skill, but it's on our list.

  3. PaprikaChen commented on Nov 5, 2026

    @PaprikaChen
    Contributor

    I will try to work on this for my 2nd PR for CS146S

  4. PaprikaChen commented on Nov 8, 2026

    @PaprikaChen
    Contributor

    Thanks for the detailed report, and thanks for confirming it! While reproducing it I found a different root cause, so I'd like to suggest a different fix before changing the stamp defaults.

    What I measured (same 7-scene / 6-seam layout as the report, default stamp output, current main):

    cut position x/y ratio z ratio
    on the 30 fps frame grid (e.g. 1.8, 3.6) 0.82 1.09 / 1.12
    +0.01 s off the grid 1.02 1.54
    +0.025 s off the grid 1.42 2.66
    +0.0325 s off the grid (just under one frame) 1.67 3.51 / 3.59

    The last row matches the report's 1.70 / 3.64–3.71. With cuts half a frame off the grid, the duration table follows the same trend as the report too, including the "0.12 s exit barely moves" row.

    Why: the runtime floors every seek to the frame grid (quantizeTimeToFrame in packages/core/src/inline-scripts/parityContract.ts). When cut falls between two frames, both exit samples (cut - 0.1, cut - dt) land almost a frame early and miss the fastest part of the exit. The first entry sample (cut + dt) lands right at the cut, at the fastest part of the entry. So exit is under-read and entry is over-read.

    The window average alone doesn't explain the numbers. On a grid-aligned film, mirrored eases with matched travel and duration give exactly 1.00 (the report got 2.60 / 3.84 for the same setup), because the two windows are symmetric. Lengthening the entry only flattens the curve and hides the asymmetry. On a grid-aligned film, the proposed defaults (x/y entry 0.85, z 0.45 / 1.7) move the ratio from 0.82 to 0.57 / 0.68, i.e. further from 1.

    Proposed fix:

    1. In seam-gate.mjs, snap the cut to the first frame at or after cut, then sample the same number of frames on each side, so the windows stay symmetric wherever the cut falls. The zero-overlap and carrier checks share these sample times, so they would compare the last frame before the cut with the first frame after it, which is what the render shows.
    2. Keep the stamp defaults as they are.
    3. In references/seam-gate.md, note that speed-match is a short window average and that cuts should sit on the frame grid. In cut-the-curve §3, add the one-line caveat from the report (pointwise vs. window-measured velocity).

    My PR for this is due tomorrow evening, so I'll go ahead with this approach for now. If you'd still prefer to change the stamp defaults as you described, just let me know and I'll update the PR.

  5. jrusso1020 commented on Nov 8, 2026

    @jrusso1020
    Collaborator

    Thanks @PaprikaChen, this is a better diagnosis than mine. You're right: seeks floor to the frame grid, so a cut that falls between frames makes the gate under-read the exit and over-read the entry, and tuning the stamp defaults would only hide that. Please go ahead with your approach in #7396: snap the cut to the frame grid and sample the same number of frames on each side, keep the stamp defaults as they are, and add the notes to seam-gate.md and cut-the-curve §3.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/toolbugSomething isn't workingdifficulty/easyLocalized change with a known approach, limited risk, and straightforward verification.triage/readyAccepted scope, current evidence, acceptance criteria, and validation path are recorded.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions