Skip to content

feat(workspace): guide tonight's first labeled handoff on map and player - #937

Open
seonghobae wants to merge 22 commits into
developfrom
feat/workspace-first-handoff-cut
Open

feat(workspace): guide tonight's first labeled handoff on map and player#937
seonghobae wants to merge 22 commits into
developfrom
feat/workspace-first-handoff-cut

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator

Product outcome

After analysis, the room can see tonight's first labeled handoff and take one next action: open that pass on the map, or hear it from the player when playback is available.

Cites docs/design-system/component-contract.md (First Handoff Callout) and the existing handoff form label in @bandscope/shared-types.

This is the remaining form-label next-action after pickup (#916) and stop (#934). It is not import-handoff #740 and not Part Handoff Map #850.

Exact current identity

  • Protected base: develop@acdbea6344fe1231c39535b575f4de35e4c607c9.
  • Exact head: 3a68b9a2915e6b96076da828eca264c27e346cad.
  • Branch: feat/workspace-first-handoff-cut.
  • Open, non-Draft, mergeable, unmerged.

What changed

  • Resolve only an explicit labeled handoff section whose runtime time window conforms to the shared integer timing contract: non-negative, strictly positive duration, and no endpoint beyond MAX_SECTION_TIME_SECONDS. Do not invent a pass from stop, pickup, graph handoff_to/handoff_from edges on another form label, zero-duration ranges, fractional ranges, or overflowed timestamps.
  • Name the holding part only when an active ranked graph node corroborates it; otherwise keep the cut band-wide.
  • Workspace action opens the renderer-owned map section. Player Hear exists only when the owning surface supplies a callable seek callback.
  • Arm follow-up guidance only after the selected action actually executes; a missing renderer target does not produce success-shaped guidance.
  • Fail closed on malformed runtime song/section collections/elements, section ids/windows, role/graph collections/elements, duplicate identities, malformed role ids/names, unknown rehearsal priorities, and non-boolean activity. Analysis section.id is never DOM-ID authority.
  • Keep Player summary rendering safe for non-array, sparse, or malformed runtime section evidence and non-string runtime song titles.
  • Localize the handoff form label at the i18n boundary so Korean copy never exposes the raw handoff enum.

Test-first hardening

  • RED 151feab1bb0fbf09b56bc7bd4d2ed13a969c626c proves sparse section, role, and part-graph collections must not authorize a handoff or named holder. GREEN 658f5c0af55787882bc4899b331a586838ab52a6 adds the dense-array runtime boundary.
  • RED 048dbfb4a80711cd5e79549fd075b7c5bdacc900 reproduces Player crashes and misleading section counts for non-array, sparse, and malformed runtime section evidence. GREEN 9cfb68f490df2d3a529b6fa0eac89278718a5005 contains those values before rendering the section summary.
  • RED fa558f287302ba3fe578ee2c974e2b9f84ef436e requires Korean copy to render 핸드오프 rather than the raw English enum. cda8be65e1add308128f9a7c536daaf7be876ae8 adds the locale-owned form-label map and b093e9f94d5804a068f3998a0703b5d041923dd3 applies it.
  • Eight later commits remain descendants of that localization head and harden malformed song-root/section evidence plus Player copy. Current 3a68b9a2915e6b96076da828eca264c27e346cad specifically prevents an object-valued runtime song title from reaching React text authority.

Exact-current-head verification

Repository evidence for exact head 3a68b9a2915e6b96076da828eca264c27e346cad currently includes terminal-success release, sbom, Bandit, SAST Semgrep, and secret-scan-gate; ci and build-baseline remain queued and are therefore non-passing. security-audit and aggregate Security Scan are terminal-failure on the inherited protected-base JavaScript HIGH set owned by canonical #783 (nanoid, pdfjs-dist, undici); this feature branch does not suppress or duplicate that dependency authority.

Current inline review threads are empty. No queued, failed inherited-base, predecessor-head, dismissed, self/author, or model-only evidence is treated as approval or merge readiness.

Dependency routing

Merge gate

Merge only after one unchanged exact head has every applicable repository and central required gate terminal-success, zero valid unresolved findings, exact required owned coverage/docstring evidence, canonical dependency prerequisites integrated/revalidated, a qualifying independent non-author last-push APPROVE, and ordinary protected-branch acceptance. Queue/pending/skipped/cancelled/failed/rate-limited/predecessor/protected-base/dismissed/self/author evidence is not success. Never bypass branch protection.

Security Notes

  • Untrusted input: rehearsal song, section, time-range, role, graph, title and copy-bearing metadata.
  • Trust boundary: renderer-owned song-structure children and Player summary rendering; lexical handoff resolution never opens files or network paths.
  • Mitigations: shared integer timing bound reuse, positive-length time-range checks, dense-array and object guards, non-empty string identities and holder copy, duplicate-identity fail-closed behavior, ranked-priority allowlisting, strict is_active === true, one-pass copy interpolation, locale-owned form labels, renderer-position navigation, runtime title containment, and reduced-motion presentation handling.
  • Action state is written only after the owning callback returns or a real renderer-owned section exposes and accepts scrollIntoView.

Open in Devin Review

Name the earliest labeled handoff cut so the room can catch the pass.
Workspace opens that map section; the player Hear action exists only
when playback supplies a seek callback. Do not invent a pass from stop,
pickup, or graph edges on another form label.
@coderabbitai

coderabbitai Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

Next included review available in 13 minutes.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a335f704-0a02-4786-8f51-c646d4695ab2

📥 Commits

Reviewing files that changed from the base of the PR and between 749511c and 90f29ba.

📒 Files selected for processing (30)
  • AGENTS.md
  • ARCHITECTURE.md
  • CHANGELOG.md
  • CLAUDE.md
  • apps/desktop/src/features/player/index.test.tsx
  • apps/desktop/src/features/player/index.tsx
  • apps/desktop/src/features/workspace/FirstHandoffCallout.invalid-song-root.test.tsx
  • apps/desktop/src/features/workspace/FirstHandoffCallout.reduced-motion.test.tsx
  • apps/desktop/src/features/workspace/FirstHandoffCallout.test.tsx
  • apps/desktop/src/features/workspace/FirstHandoffCallout.tsx
  • apps/desktop/src/features/workspace/Workspace.test.tsx
  • apps/desktop/src/features/workspace/Workspace.tsx
  • apps/desktop/src/features/workspace/firstLabeledHandoff.activity-type.test.ts
  • apps/desktop/src/features/workspace/firstLabeledHandoff.duplicate-identities.test.ts
  • apps/desktop/src/features/workspace/firstLabeledHandoff.inactive-labeled.test.ts
  • apps/desktop/src/features/workspace/firstLabeledHandoff.invalid-holder-collections.test.ts
  • apps/desktop/src/features/workspace/firstLabeledHandoff.invalid-holder-elements.test.ts
  • apps/desktop/src/features/workspace/firstLabeledHandoff.invalid-role-id.test.ts
  • apps/desktop/src/features/workspace/firstLabeledHandoff.invalid-section-collection.test.ts
  • apps/desktop/src/features/workspace/firstLabeledHandoff.invalid-section-id.test.ts
  • apps/desktop/src/features/workspace/firstLabeledHandoff.invalid-time-range.test.ts
  • apps/desktop/src/features/workspace/firstLabeledHandoff.sparse-collections.test.ts
  • apps/desktop/src/features/workspace/firstLabeledHandoff.test.ts
  • apps/desktop/src/features/workspace/firstLabeledHandoff.ts
  • apps/desktop/src/i18n/index.test.ts
  • apps/desktop/src/i18n/index.ts
  • apps/desktop/src/locales/en/common.json
  • apps/desktop/src/locales/ko/common.json
  • docs/design-system/component-contract.md
  • docs/doctoring/reduced-motion-first-handoff-navigation.md
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/workspace-first-handoff-cut

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

OpenCode cannot approve yet because required coverage evidence did not pass.

Review outcome

1. HIGH .github/workflows/opencode-review.yml:1 - Coverage evidence did not prove required test/docstring evidence

  • Problem: The required coverage-evidence job result was failure, so OpenCode cannot establish approval sufficiency for this head.

  • Root cause: Automated approval is only valid when the same-head coverage-evidence job proves supported repository test suites passed and configured docstring gates passed or were advisory, or reports not applicable because no supported source files or package manifests exist. Missing, failed, skipped, unavailable, or unsupported-tooling test evidence is a blocker.

  • Fix: Install or configure the repository test/docstring evidence tooling when source files or package manifests exist, rerun the current-head coverage-evidence job, and approve only after it reports success with required evidence or explicit no-source not-applicable evidence.

  • Regression test: Keep the approval branch checking needs.coverage-evidence.result == success before posting APPROVE, and publish REQUEST_CHANGES when coverage-evidence blocker states such as cancelled, skipped, failed, unsupported-tooling, or below-100 evidence are present.

  • Result: REQUEST_CHANGES

  • Reason: coverage-evidence result was failure, so required test/docstring evidence was not proven for current head 3a68b9a2915e6b96076da828eca264c27e346cad.

  • Head SHA: 3a68b9a2915e6b96076da828eca264c27e346cad

  • Workflow run: 32212704993

  • Workflow attempt: 1

Coverage evidence

Coverage evidence job did not run or did not publish coverage evidence.

Changed-File Evidence Map

flowchart LR
  PR["PR changed files"] --> Evidence["OpenCode bounded evidence"]
  Evidence --> S1["Changed file (28 files)"]
  S1 --> I1["repository behavior"]
  I1 --> R1["Review risk: Changed file (28 files)"]
  R1 --> V1["required checks"]
  Evidence --> S2["Docs (2 files)"]
  S2 --> I2["operator or user guidance"]
  I2 --> R2["Review risk: Docs (2 files)"]
  R2 --> V2["docs review"]
Loading

@opencode-agent

opencode-agent Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

OpenCode Review Overview

  • Head SHA: 3a68b9a2915e6b96076da828eca264c27e346cad
  • Workflow run: 32216801298
  • Workflow attempt: 1
  • Gate result: REQUEST_CHANGES (approval step)

Pull request overview

OpenCode cannot approve yet because required coverage evidence did not pass.

Review outcome

1. HIGH .github/workflows/opencode-review.yml:1 - Coverage evidence did not prove required test/docstring evidence

  • Problem: The required coverage-evidence job result was failure, so OpenCode cannot establish approval sufficiency for this head.

  • Root cause: Automated approval is only valid when the same-head coverage-evidence job proves supported repository test suites passed and configured docstring gates passed or were advisory, or reports not applicable because no supported source files or package manifests exist. Missing, failed, skipped, unavailable, or unsupported-tooling test evidence is a blocker.

  • Fix: Install or configure the repository test/docstring evidence tooling when source files or package manifests exist, rerun the current-head coverage-evidence job, and approve only after it reports success with required evidence or explicit no-source not-applicable evidence.

  • Regression test: Keep the approval branch checking needs.coverage-evidence.result == success before posting APPROVE, and publish REQUEST_CHANGES when coverage-evidence blocker states such as cancelled, skipped, failed, unsupported-tooling, or below-100 evidence are present.

  • Result: REQUEST_CHANGES

  • Reason: coverage-evidence result was failure, so required test/docstring evidence was not proven for current head 3a68b9a2915e6b96076da828eca264c27e346cad.

  • Head SHA: 3a68b9a2915e6b96076da828eca264c27e346cad

  • Workflow run: 32216801298

  • Workflow attempt: 1

Coverage evidence

Coverage evidence job did not run or did not publish coverage evidence.

Changed-File Evidence Map

flowchart LR
  PR["PR changed files"] --> Evidence["OpenCode bounded evidence"]
  Evidence --> S1["Changed file (28 files)"]
  S1 --> I1["repository behavior"]
  I1 --> R1["Review risk: Changed file (28 files)"]
  R1 --> V1["required checks"]
  Evidence --> S2["Docs (2 files)"]
  S2 --> I2["operator or user guidance"]
  I2 --> R2["Review risk: Docs (2 files)"]
  R2 --> V2["docs review"]
Loading

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

OpenCode cannot approve yet because required coverage evidence did not pass.

Review outcome

1. HIGH .github/workflows/opencode-review.yml:1 - Coverage evidence did not prove required test/docstring evidence

  • Problem: The required coverage-evidence job result was failure, so OpenCode cannot establish approval sufficiency for this head.

  • Root cause: Automated approval is only valid when the same-head coverage-evidence job proves supported repository test suites passed and configured docstring gates passed or were advisory, or reports not applicable because no supported source files or package manifests exist. Missing, failed, skipped, unavailable, or unsupported-tooling test evidence is a blocker.

  • Fix: Install or configure the repository test/docstring evidence tooling when source files or package manifests exist, rerun the current-head coverage-evidence job, and approve only after it reports success with required evidence or explicit no-source not-applicable evidence.

  • Regression test: Keep the approval branch checking needs.coverage-evidence.result == success before posting APPROVE, and publish REQUEST_CHANGES when coverage-evidence blocker states such as cancelled, skipped, failed, unsupported-tooling, or below-100 evidence are present.

  • Result: REQUEST_CHANGES

  • Reason: coverage-evidence result was failure, so required test/docstring evidence was not proven for current head 3a68b9a2915e6b96076da828eca264c27e346cad.

  • Head SHA: 3a68b9a2915e6b96076da828eca264c27e346cad

  • Workflow run: 32216801298

  • Workflow attempt: 1

Coverage evidence

Coverage evidence job did not run or did not publish coverage evidence.

Changed-File Evidence Map

flowchart LR
  PR["PR changed files"] --> Evidence["OpenCode bounded evidence"]
  Evidence --> S1["Changed file (28 files)"]
  S1 --> I1["repository behavior"]
  I1 --> R1["Review risk: Changed file (28 files)"]
  R1 --> V1["required checks"]
  Evidence --> S2["Docs (2 files)"]
  S2 --> I2["operator or user guidance"]
  I2 --> R2["Review risk: Docs (2 files)"]
  R2 --> V2["docs review"]
Loading

devin-ai-integration[bot]

This comment was marked as resolved.

@opencode-agent opencode-agent Bot added area: ui-ux Frontend, interaction, design, or user experience priority: medium Normal-priority or P2 work status: needs-review Open pull request requiring current-head review or checks type: feature New or expanded product capability labels Aug 22, 2026
@coderabbitai

coderabbitai Bot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Caution

Review failed

An error occurred during the review process. Please try again later.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 new potential issues.

Open in Devin Review

if (!handoff) {
return (
<aside
id="workspace-surface-handoff"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Fixed callout id shared by two surfaces

The callout always renders id workspace-surface-handoff (FirstHandoffCallout.tsx and :131), and both the workspace and the player mount it. Only one screen renders at a time today, so no collision occurs, but any future layout that mounts both surfaces produces a duplicate DOM id.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

return (
<section style={{ padding: "24px" }}>
<h2>{title}</h2>
<FirstHandoffCallout song={song} actionMode="callback-only" onHearHandoff={onPlayFromSeconds} />

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Player Hear action has no production caller

PlayerFeature forwards onPlayFromSeconds to the callout, but grep shows no non-test caller renders PlayerFeature at all, let alone with that prop. The Hear action stays hidden in the running app until some parent wires a seek callback.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Collaborator Author

@opencode-agent Repair the verified current-head ownership defect on the existing feat/workspace-first-handoff-cut branch only. Exact head 2643c0c7e9a741ca71ac15f082d398b7f0ce938a, protected base develop@acdbea6344fe1231c39535b575f4de35e4c607c9. Devin thread PRRT_kwDORjvEXs6cMzoW / comment 3856219443 is valid: PlayerFeature has no non-test production caller, and apps/desktop/src/App.tsx does not mount/import it, so this PR's new Hear callback path is dead buyer-visible code. Active transport/player authority belongs to accepted issue #961; do not create a second transport authority or wire a placeholder player here. Narrow this PR back to its working map next-action boundary: remove only the #937-introduced PlayerFeature/Player tests and player-specific docs/copy claims that imply a shipped Hear action, preserving the labeled-handoff resolver, Workspace map callout/navigation, localization and existing runtime safety regressions. If a small shared interface is genuinely required by an already-shipped #961 contract, prove that exact caller first; otherwise delete the dead leaf. Run focused desktop tests, typecheck, coverage for the remaining owned production surface, and applicable quickcheck; commit on this same branch and report the resulting exact head/evidence. Do not touch #783 dependency security or #961 transport implementation.

@seonghobae

Copy link
Copy Markdown
Collaborator Author

Re-review request for exact head ae186684b69c2fa3e67309e8b04ce0360262434e: all required status contexts are green on this head (including coverage-evidence and security scans) and all review threads are resolved. @opencode-agent please review current head ae186684b69c2fa3e67309e8b04ce0360262434e and update the review decision.

# Conflicts:
#	AGENTS.md
#	CHANGELOG.md
#	CLAUDE.md
#	apps/desktop/src/features/workspace/Workspace.tsx
#	apps/desktop/src/locales/en/common.json
#	apps/desktop/src/locales/ko/common.json
@seonghobae

Copy link
Copy Markdown
Collaborator Author

Merged current develop into this branch: resolved shared-file conflicts by including both this feature's first-handoff-cut callout and the newly landed #957 playable-range work (Workspace.tsx keeps both imports; en/ko locales unioned with symmetric keys; AGENTS/CLAUDE/CHANGELOG unioned). No behavior dropped.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

Open in Devin Review

Comment on lines +109 to +147
/** Return ranked roles whose unique graph node is explicitly active. */
function rankedActiveRoles(section: RehearsalSection): RehearsalRole[] {
if (!isDenseRuntimeArray(section.roles) || !isDenseRuntimeArray(section.partGraph)) {
return [];
}

const safeRoleIds = section.roles
.filter(
(role) => isRuntimeObject(role) && typeof role.id === "string" && role.id.trim().length > 0
)
.map((role) => role.id);
const safeGraphRoleIds = section.partGraph
.filter(
(node) => isRuntimeObject(node) && typeof node.role_id === "string" && node.role_id.trim().length > 0
)
.map((node) => node.role_id);
const repeatedRoleIds = repeatedIds(safeRoleIds);
const repeatedGraphRoleIds = repeatedIds(safeGraphRoleIds);
const activeIds = new Set(
section.partGraph
.filter(
(node) =>
isRuntimeObject(node) &&
node.is_active === true &&
typeof node.role_id === "string" &&
node.role_id.trim().length > 0 &&
!repeatedGraphRoleIds.has(node.role_id)
)
.map((node) => node.role_id)
);

return section.roles.filter(
(role) =>
isRuntimeObject(role) &&
hasRankedPriority(role) &&
!repeatedRoleIds.has(role.id) &&
activeIds.has(role.id)
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Named holder is highest-priority active role, not the pass-giver

resolveFirstLabeledHandoff names the holder as the highest-priority active ranked role in the section and ignores handoff_to/handoff_from edges (firstLabeledHandoff.ts:127-146). When several parts are active, the label reflects ranking, not which part actually gives the pass. The PR states edges are intentionally unused, so this is a design choice worth confirming against product intent.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

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

Labels

area: ui-ux Frontend, interaction, design, or user experience priority: medium Normal-priority or P2 work status: needs-review Open pull request requiring current-head review or checks type: feature New or expanded product capability

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant