Skip to content

member_spans: where each episode member is heard on its mix - #97

Merged
thorwhalen merged 4 commits into
mainfrom
member-spans
Oct 13, 2026
Merged

thorwhalen merged 4 commits into
mainfrom
member-spans

Conversation

@thorwhalen

@thorwhalen thorwhalen commented Oct 13, 2026 •

Copy link
Copy Markdown
Collaborator

What

braidio.transforms.member_spans(project, episode): each member of an episode-render/v1, in play order, with where it is heard on the mix (MemberSpan(member_id, position, start_s, end_s, concurrent)).

  • Pairing: ordered_member_ids[i] ↔ the timeline span whose index is i, the rule episode_script + captions.cues_for and the picture track already rely on.
  • The sequential lane comes back exclusive: the weave tucks each part ~0.12 s (crossfade) or 0.5 s (clip edge) under its predecessor, so each placement starts where the previous one finished (the cues_for clamp).
  • A member laid under what follows, or shorter than its tuck, keeps its true span and is marked concurrent.
  • Bounded by the mix's duration_s. Unknown placement (None) when there is no span, the timeline is malformed or cannot be rebuilt, or its indices are not member positions; never rebuilt from durations.
  • BeatSpan.placement (additive): build_timeline records layout_placed's sequential/under, to_dict writes it only when recorded (older breakdowns round-trip to the same bytes), from_dict accepts only those two values. Older breakdowns are read off the layout's own signature, property-tested against layout_placed (exact agreement on 3000 random realistic layouts).
  • Importer: refuses a manifest timeline whose indices are not distinct member positions (a shifted index would hand every member its neighbour's interval).

First customer: reeleehq/reelee#546 (a spend-free read of commentary takes with text and place on the mix, for reelee-web#356). Follow-up filed: #98.

Checked

  • tests/test_importing.py: negative control for the clamp, under/tucked/tie cases, bounds, unpairable and malformed timelines, the importer refusal, the property test. 93 pass; timeline, video-transforms and doctests pass.
  • Read-only copies of two real imported productions: 14 and 57 takes, all placed, ordered, inside the mix.
  • Local full suite: 640 passed; only py2mcp-dependent files fail locally (module absent from the local venv).

Adversarial review

Three rounds by a fresh Opus reviewer.

  • Round 1 (fix first): the clamp squeezed under-laid members to zero; spans could pass the mix end; a non-positional timeline paired members with neighbours' spans; malformed timelines raised. All fixed.
  • Round 2 (fix first): inferring under from start order missed ties (cold-open under clip, zero crossfade) and flagged short members. Fixed by recording placement and reading older timelines off layout_placed's signature.
  • Round 3 (fix first): a 1 ms rounding tolerance missed ~3.5% of real-valued layouts; a null duration raised; arbitrary placement values were trusted. Fixed (2.5 ms tolerance, property test, validated values, tucked members advance the lane). Confirmed placement in to_dict touches no plan identity, cache key or fingerprint. Remaining by design: older timelines made of parts shorter than the clip edge are ambiguous without a recorded placement (documented).

🤖 Generated with Claude Code

Pairs ordered_member_ids[i] with the timeline span whose index is i (the
captions' and picture track's rule) and returns exclusive, ordered
placements: the weave's crossfade puts a member's start a fraction of a
second before the previous end, so each placement starts where the
previous one finished, as cues_for clamps its cues. A member with no span
has no placement (None), never one rebuilt from durations.

First customer: reelee's commentary takes read (reeleehq/reelee#546).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
thorwhalen and others added 3 commits October 13, 2026 04:24
…efuse unpairable timelines

Adversarial review of #97: the clamp squeezed a member laid under what
follows (layout_placed's 'under') to zero length; spans could pass the mix
end; a timeline whose indices are not positions paired members with their
neighbours' intervals; a malformed timeline raised. Now: an under member
keeps its true span and is marked concurrent, only the sequential lane is
made exclusive, intervals are bounded by duration_s, an unpairable or
unreadable timeline gives null placements, and the importer refuses a
timeline whose indices are not distinct member positions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Round-2 review of #97: inferring 'under' from 'a later member starts
earlier' missed ties (an under clip at position 0, a zero crossfade) and
flagged short sequential members. BeatSpan now carries layout_placed's
placement (additive: written only when recorded, so older breakdowns
round-trip to the same bytes), build_timeline records it, and member_spans
reads it. An older breakdown is read off the layout's own signature: an
under member starts exactly at the end of the sequential lane and the next
member starts at or before it. Only KeyError/ValueError mean an unknown
placement now; the importer reads timeline indices as from_dict does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Round-3 review of #97: the lane end and the next start are each rounded,
so the older-timeline inference needs a 2.5 ms tolerance (property-tested
against layout_placed); a sequential member shorter than its tuck keeps
its true span (concurrent) and still advances the lane; a null duration
gives an unknown placement again; from_dict accepts only 'sequential' or
'under' as a recorded placement.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@thorwhalen
thorwhalen merged commit 161636d into main Oct 13, 2026
12 checks passed
@thorwhalen
thorwhalen deleted the member-spans branch October 13, 2026 04:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant