Skip to content

feat(workspace): name tonight's first ear check on the map - #1001

Open
seonghobae wants to merge 30 commits into
developfrom
feat/workspace-first-ear-check
Open

feat(workspace): name tonight's first ear check on the map#1001
seonghobae wants to merge 30 commits into
developfrom
feat/workspace-first-ear-check

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 23, 2026

Copy link
Copy Markdown
Collaborator

Product outcome

Uncertain analysis is no longer a badge-only fact. After a song is analyzed, the mounted rehearsal workspace names tonight's first ear check — the earliest canonical labeled section whose owned confidence is low or medium — and the Open action moves to that uniquely rendered map section so the room can confirm it by ear before rehearsal starts.

Protected target: develop@acdbea6344fe1231c39535b575f4de35e4c607c9.
Exact current head: c8804b7662b9a9d6bcabc920fcab73330bb89f4f.
Branch: feat/workspace-first-ear-check.

Buyer-visible next action

  • Ready copy names the corroborated part, canonical section label, and time: Bass Guitar still needs an ear check in the verse at 0:10.
  • Open scrolls the renderer-owned song-structure section and arms: Confirm Bass Guitar by ear at 0:10 before the room starts.
  • When a holding part is named, displayed confidence.notes come only from that same part; another uncertain part's note cannot appear under the named holder.
  • When no active ranked part owns the ear check, the callout stays section-wide and may show only section-owned confidence notes; notes owned solely by inactive/unranked roles never become band-wide guidance.
  • Owned confidence.notes stay visible as a text node and are never rescanned as template syntax.
  • Armed guidance survives immutable updates of an ordinary bounded song when the song owns a stable plain-string id plus stable bounded content; hostile or oversized identity inputs fall back to object identity.
  • When every owned confidence is high, the callout stays guidance-only: stay on tonight's map until a part is marked uncertain.
  • Korean copy keeps vowel-ending role names particle-safe (피아노 파트를).

Current-head repairs

  • aa610108d76e1604ffe1ffffcdad99ed4e1323b7 adds a regression requiring concurrent ear-check callouts to avoid duplicate DOM ids. No hosted RED run completed before the successor push, so this is regression-first source evidence rather than terminal hosted RED evidence.
  • da9c1a70041073421b798ca795f0239540e5681e uses React useId() so concurrently mounted callouts receive unique landmark ids while preserving the workspace-surface-ear-check- prefix.
  • c5d3a746e3f5dd73f98045e5ca55a5c83553dc8 adds a regression requiring a runtime verse-custom section label to fail closed rather than surface untranslated buyer copy. No hosted RED run completed before the successor push.
  • Current head c8804b7662b9a9d6bcabc920fcab73330bb89f4f accepts section labels only from shared SECTION_FORM_LABELS before they can become ear-check/navigation/copy authority.

Trust boundary

  • Required song/section/role/graph/confidence fields are accepted only as own data properties.
  • Runtime section labels must belong to shared SECTION_FORM_LABELS; unknown nonempty strings are not buyer-visible localization authority.
  • Sparse arrays, inherited prototypes, accessors, and arrays masquerading as section records fail closed.
  • Stable callout identity accepts song.id only as an owned plain-string data property and pairs it with a bounded content fingerprint; accessor/descriptor-trap identity or oversized/hostile songs fall back to object identity.
  • Navigation uses renderer-owned data-section-index; analysis section.id is never DOM-ID authority.
  • Landmark ids are mount-local React ids, so co-mounted callouts do not publish duplicate fixed ids.
  • Groove, cue, setup, simplification, overlap warnings, range copy, and high confidence cannot invent an ear check.
  • #783 remains the canonical protected JavaScript dependency-security owner. Inherited dependency findings are not copied or suppressed here.
  • Do not mix with 🔒 [보안] CLI 무제한 파일 읽기 취약점 수정 #811, test(analysis): govern real YouTube known-stem benchmark #828, or feat(workspace): Storybook tokens for rehearsal components #897.

Security and accessibility notes

  • Untrusted input: song, song identity metadata, section label/time-range, role, confidence marker, and section-local graph metadata.
  • Trust boundary: ear-check resolution accepts required fields only when the inspected record owns them and the section label is canonical; renderer-owned song-structure children remain the only navigation targets.
  • Mitigations: dense-collection guards, own-property checks, canonical label allowlist, fail-closed try/catch around hostile identity/metadata access, active-holder-bound role notes, section-owned fallback notes, once-only copy interpolation, 180-code-point hint bound, reduced-motion auto scroll, and unique callout landmark ids.
  • Test points: demo-song first ear check, inactive-role note isolation, same-song immutable state preservation, distinct accessor-id song reset, mismatched-role-note rejection, inherited/accessor/sparse/array rejection, unknown section-label rejection, duplicate identity band-wide fallback, concurrent landmark uniqueness, reduced-motion scroll, Korean particle-safe copy, and missing renderer target stays unarmed.

Exact-head verification state

Fresh repository workflows were dispatched for exact head c8804b7662b9a9d6bcabc920fcab73330bb89f4f:

  • CI 32761528172;
  • release 32761528089;
  • security-audit 32761528225;
  • aggregate Security Scan 32761528299;
  • SBOM 32761528371;
  • build-baseline 32761528361;
  • SAST Semgrep 32761528220;
  • Bandit 32761528448;
  • secret-scan-gate 32761528344.

At the latest refetch these runs were queued, so none is counted as success and no predecessor-head evidence transfers. Addressed review threads remain subject to exact-head verification before merge readiness.

Merge gate

Keep unmerged until one unchanged exact head has every applicable repository and central CI/security/SAST/SBOM/coverage/review gate terminal-success, exact required coverage/docstring evidence, zero valid unresolved current-head findings, a qualifying independent non-author last-push approval, and ordinary branch-protection acceptance.

Queued, pending, skipped-required, cancelled, failed, stale/predecessor/protected-base, status-only/model-only, self/author, or administrative-bypass evidence is not success. Never bypass branch protection, suppress inherited dependency findings, or transfer predecessor evidence.


Open in Devin Review

Surface the earliest low or medium confidence section as a rehearsal next
action so uncertain analysis is confirmed by ear before the room starts.
@coderabbitai

coderabbitai Bot commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

Next included review available in 33 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: 26e27a05-fc77-492e-b22a-a0444552da11

📥 Commits

Reviewing files that changed from the base of the PR and between cb86ea2 and d3bbd82.

📒 Files selected for processing (16)
  • .Jules/palette.md
  • .jules/palette.md
  • AGENTS.md
  • ARCHITECTURE.md
  • CHANGELOG.md
  • CLAUDE.md
  • apps/desktop/src/features/workspace/FirstEarCheckCallout.concurrent-id.test.tsx
  • apps/desktop/src/features/workspace/FirstEarCheckCallout.sections-accessor.test.tsx
  • apps/desktop/src/features/workspace/FirstEarCheckCallout.test.tsx
  • apps/desktop/src/features/workspace/FirstEarCheckCallout.tsx
  • apps/desktop/src/features/workspace/Workspace.test.tsx
  • apps/desktop/src/features/workspace/Workspace.tsx
  • apps/desktop/src/features/workspace/firstEarCheck.section-label.test.ts
  • apps/desktop/src/features/workspace/firstEarCheck.ts
  • apps/desktop/src/locales/en/common.json
  • apps/desktop/src/locales/ko/common.json
📝 Walkthrough

Walkthrough

Changes

첫 ear check 기능

Layer / File(s) Summary
ear check 해석 및 검증
apps/desktop/src/features/workspace/firstEarCheck.ts, apps/desktop/src/features/workspace/firstEarCheck*.test.ts
곡의 소유 메타데이터와 시간 범위를 검증합니다. 활성 역할과 섹션의 신뢰도를 기준으로 첫 ear check를 선택합니다.
지역화 계약 및 문구
apps/desktop/src/i18n/index.ts, apps/desktop/src/i18n/index.test.ts, apps/desktop/src/locales/*/common.json
섹션 라벨 조회 함수와 영어·한국어 ear check 문구를 추가합니다. 역할별 및 전체 안내를 지원합니다.
FirstEarCheckCallout 안내 및 탐색
apps/desktop/src/features/workspace/FirstEarCheckCallout.tsx, apps/desktop/src/features/workspace/FirstEarCheckCallout*.test.tsx
첫 ear check 안내를 렌더링합니다. renderer 소유 섹션으로 이동하고, reduced-motion 설정과 곡별 안내 상태를 처리합니다.
Workspace 연결 및 운영 문서
apps/desktop/src/features/workspace/Workspace.tsx, apps/desktop/src/features/workspace/Workspace.test.tsx, AGENTS.md, ARCHITECTURE.md, CHANGELOG.md, CLAUDE.md, docs/design-system/component-contract.md, docs/doctoring/reduced-motion-first-ear-check-navigation.md, apps/desktop/vite.config.ts
Workspace에 callout과 섹션 인덱스를 연결합니다. ear check 생성 기준, 탐색 계약, 커버리지 대상을 문서화하고 설정합니다.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk: 🟡 Moderate · up to cb86e

A malformed or hostile song record can still crash the workspace while rendering the ear-check guidance, and the Korean callout may communicate the wrong state. The PR should not merge until the guarded access and localized wording are corrected or explicitly accepted.

Sequence Diagram(s)

sequenceDiagram
  participant Workspace
  participant FirstEarCheckCallout
  participant resolveFirstEarCheck
  participant SongStructureRenderer
  Workspace->>FirstEarCheckCallout: 현재 song 전달
  FirstEarCheckCallout->>resolveFirstEarCheck: 첫 ear check 해석
  resolveFirstEarCheck-->>FirstEarCheckCallout: ear check 정보 반환
  FirstEarCheckCallout->>SongStructureRenderer: 대상 섹션 탐색
  SongStructureRenderer-->>FirstEarCheckCallout: reduced-motion 옵션으로 스크롤
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring check was indeterminate for this PR — some files could not be analyzed in time. Not blocking.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 워크스페이스에서 첫 ear check를 지도에 표시하고 이름을 지정하는 주요 변경 사항을 간결하고 정확하게 설명합니다.
✨ 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-ear-check

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[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

…te semantics

- key armed guidance on owned song id plus content fingerprint so distinct
  songs sharing an id stop sharing armed state while immutable copies keep it
- resolve map navigation through accessibility regions owning rendered
  section cells first, keeping the data-testid hook only as a nested
  identification of the same map; collapse nested surfaces to the outermost
  one and fail closed on disjoint local or global ambiguity
- memoize locale detection, translator, and per-song ear-check resolution
  once per mount/song to match the surrounding workspace pattern
- reserve band-wide ear-check copy for section-owned uncertainty: sections
  whose only uncertainty sits on inactive or unprovable parts no longer
  claim tonight's ear check
devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

seonghobae commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator Author

@opencode-agent

Repair only canonical BandScope PR #1001 branch feat/workspace-first-ear-check at exact current head 9e7bbea6b91abad79f854daa16dcb510e0068aab if it is still current; if it moved, refetch the intervening delta first and adapt rather than overwriting another writer. Do not create another PR.

Use systematic debugging + TDD. Two current-head BandScope-owned defects are now proven:

  1. Exact CI RED: stale Korean rendered-copy expectations. CI run 32742264295, job 97479273442, actual merge checkout e0c9b45222d2d832ca6bbaa168d5027fe59b1ffc (9e7bbea... into protected develop@acdbea6344fe1231c39535b575f4de35e4c607c9) passes docs/security/supply-chain/bootstrap, ESLint, Ruff, Bandit, typecheck, then desktop Vitest finishes 258 passed / 2 failed. The only failures are FirstEarCheckCallout.test.tsx lines ~467 and ~500 because they still expect 0:10 벌스에서 베이스 기타 파트를 귀로 확인하세요. while the authoritative locale/current DOM correctly renders 0:10 벌스에서 베이스 기타 파트는 아직 귀 확인이 필요합니다.. Production ko/common.json must stay declarative; update only those stale rendered expectations. This directly addresses review thread PRRT_kwDORjvEXs6btfaV.

  2. Root song.sections trust-boundary reread. Current FirstEarCheckCallout.tsx still calls resolveFirstEarCheck(song) and then separately reads runtimeSong?.sections / runtimeSong.sections.indexOf(...). The new always-throw accessor regression at head 9e7bbea... passes because resolveFirstEarCheck fails closed and the later read short-circuits, but it does not cover a hostile Proxy/accessor that returns valid sections during resolver validation and throws or changes on the later component reread. This is the still-valid CodeRabbit finding in thread PRRT_kwDORjvEXs6bvPwH.

For defect 2, add the smallest realistic regression first that lets resolver validation succeed but makes a later root sections reread throw/change, proving the current component can still crash or lose navigation authority. Fix at the owning boundary rather than wrapping only the symptom: snapshot the owned root sections once inside resolveFirstEarCheck, preserve the original renderer-owned sectionIndex in the resolved result, and have FirstEarCheckCallout consume that validated index instead of rereading song.sections. Preserve current ordering/tie-breaking, dense-array/own-data fail-closed semantics, bounded identity behavior, reduced-motion behavior, and all current buyer copy.

Run focused ear-check tests, full desktop tests/coverage, typecheck/lint, then repository quickcheck/current-head CI. Resolve only PRRT_kwDORjvEXs6btfaV and PRRT_kwDORjvEXs6bvPwH after their exact fixes are present. Do not touch #783-owned dependency/security state, gates, coverage thresholds, lockfiles, or foreign repos; do not merge, force-push, self-approve, or weaken protection. Reply with the exact successor head and verification evidence.

coderabbitai[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

@opencode-agent

Copy link
Copy Markdown
Contributor

Queued @opencode-agent for PR #1001 at head c8804b7662b9a9d6bcabc920fcab73330bb89f4f. Central exact-name Actions artifacts are the durable dispatch ledger; existing review workflows remain authoritative for the final verdict and failure evidence.

@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 c8804b7662b9a9d6bcabc920fcab73330bb89f4f.

  • Head SHA: c8804b7662b9a9d6bcabc920fcab73330bb89f4f

  • Workflow run: 32766926114

  • 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 (25 files)"]
  S1 --> I1["repository behavior"]
  I1 --> R1["Review risk: Changed file (25 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

Copy link
Copy Markdown
Contributor

OpenCode Review Overview

  • Head SHA: c8804b7662b9a9d6bcabc920fcab73330bb89f4f
  • Workflow run: 32766926114
  • 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 c8804b7662b9a9d6bcabc920fcab73330bb89f4f.

  • Head SHA: c8804b7662b9a9d6bcabc920fcab73330bb89f4f

  • Workflow run: 32766926114

  • 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 (25 files)"]
  S1 --> I1["repository behavior"]
  I1 --> R1["Review risk: Changed file (25 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

@seonghobae

Copy link
Copy Markdown
Collaborator Author

Re-review request for exact head 6762e266dd1edaebffb4927142da84c299ea7a4e: 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 6762e266dd1edaebffb4927142da84c299ea7a4e 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

Resolved merge conflicts against origin/develop (749511c): union-merged AGENTS.md/CHANGELOG.md bullets, sentence-merged CLAUDE.md, kept both import sets in Workspace.tsx (FirstEarCheckCallout + firstRangeSqueeze), unioned en/ko locale tails (JSON valid, en/ko symmetric). Merged legacy .Jules/palette.md into canonical .jules/palette.md, dropped alias. No conflict markers remain. Merge head: d3bbd82.

@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

Comment on lines +75 to +82
function stableEarCheckSongIdentity(song: RehearsalSong): unknown {
const songId = stableEarCheckSongId(song);
if (songId === null) {
return song;
}
const fingerprint = earCheckSongFingerprint(song);
return fingerprint === null ? song : `${songId}\u0000${fingerprint}`;
}

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: Armed state keyed on id plus bounded fingerprint

stableEarCheckSongIdentity (FirstEarCheckCallout.tsx) keys armed guidance on song.id plus a bounded fingerprint of title and per-section id/start/end. Two distinct songs sharing all of those would share armed state, but the opened guard at FirstEarCheckCallout.tsx additionally compares section id, index, holding role, and time, so a different ear check still resets. Behavior holds.

Open in Devin Review

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

Comment on lines +200 to +208
useEffect(() => {
setOpenedEarCheck(null);
}, [
songIdentity,
earCheckSectionIndex,
earCheck?.section.id,
earCheck?.holdingRole?.id,
earCheck?.atSeconds
]);

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: Redundant reset effect

The effect at FirstEarCheckCallout.tsx clears openedEarCheck when identity, section, role, or time changes. The opened computation at lines 223-229 already returns false whenever any of those differ, so the effect only triggers an extra render and never alters what the user sees.

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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant