Skip to content

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

Open
seonghobae wants to merge 13 commits into
developfrom
feat/workspace-first-score-check
Open

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

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 30, 2026

Copy link
Copy Markdown
Collaborator

Summary

The ready rehearsal map names tonight's first attached score and the next Score action only while the current session still has a validated live project workspace. With a trusted attachment plus a named span, the player is told to open that score in Score and check the selected role's range against the page. If there is no live project context, no trusted score attachment, or no usable attachment metadata, the map fails closed to the existing add-a-score / ear-check guidance.

This slice connects the rehearsal map to Score using existing scoreAttachments, sourceBootstrap, and firstRangeSqueeze contracts. It does not open, read, or parse PDF bytes, rebuild a filesystem path from display metadata, or invent page-to-note mapping/MIR.

Figma/component contract: docs/design-system/component-contract.md ready Workspace on page 34 Workspace State Matrix.

Exact identity

  • Protected target: develop@749511c3ad4000090048718f685c6bee6b3d2c25.
  • Branch: feat/workspace-first-score-check.
  • Exact current head: 8099e3b2525723474aca09db4d669167035263b3.
  • All predecessor-head checks and reviews are historical only and do not transfer to this head.

Buyer-visible behavior

  • Live project + trusted attachment + named span: Open opener.pdf in Score and check Bass Guitar's C#2–E3 in verse against the page before the verse.
  • Live project + trusted attachment + no span: Open opener.pdf in Score and mark tonight's first range on the page.
  • Reopened metadata-only .bscope project with no live project bootstrap: Add a score in Score, or check tonight's first range by ear before the first section.
  • No trusted attachment: the same add-a-score / ear-check fallback.
  • Korean copy names the equivalent next action.
  • Selecting a role limits the paired range to that part.

Review-driven repairs on the current branch

Preserve native-valid display names

The original UI helper imposed an unrelated secondary basename policy and could hide scores the native attach boundary had already accepted. Current trustedScoreFileName therefore preserves native-valid literal display metadata including long names, surrounding spaces, repeated dots, and platform-reserved stems. It only rejects values that cannot be represented safely as PDF display metadata in this copy surface: empty/non-string values, control characters, path separators, and non-PDF names. The value is never used to reconstruct a filesystem path.

Do not advertise impossible score opens after reopen

Persisted scoreAttachments metadata is not proof that the native Score workspace still exists. loadProject() intentionally clears the live analysis/project bootstrap, so Workspace derives scoreWorkspaceAvailable from a validated sourceBootstrap and passes that authority to firstScoreCheck. Reopened metadata-only projects therefore fall back instead of promising an action that ScoreView cannot execute.

Isolate generic persisted score keys at an anti-corruption boundary

The existing project-format score attachment shape uses the established wire keys id and fileName. Those persisted keys remain unchanged in this PR to avoid a silent project-format break. trustedScoreAttachment is now the explicit compatibility boundary: it validates the old wire shape and immediately translates it to the workspace-owned semantic projection { scoreId, scoreFileName }. Workspace logic no longer carries the generic persisted names beyond that boundary.

TDD sequence for this naming repair:

  • RED contract commit 35dc521f03711d749771751ecf39b904f193057d changed the focused regression to require { scoreId, scoreFileName } while production still returned { id, fileName }.
  • GREEN production commit 8cd6756ef242d99fc323181b21b58f96fe24c731 introduced TrustedScoreAttachment, translated the persisted wire keys at trustedScoreAttachment, and renamed touched organization-owned locals to bounded score/range vocabulary.
  • No database object, migration, index, foreign key, ORM mapping, UPSERT path, locking topology, or persisted project wire key changed in this slice.

Keep product guidance truthful

Current review found that ARCHITECTURE.md, AGENTS.md, CHANGELOG.md, and CLAUDE.md could be read as promising that any persisted attachment was immediately openable. Commits 5af64f5c3ddc85b237a4426678de0233ee4f5fdf, 5a2abb1aa404eb0df133cbaeade44439621e56d6, 893b87a53faaa08f3f972a4dc264c47ff9c83511, and 8099e3b2525723474aca09db4d669167035263b3 now state the same runtime invariant as production: score-open guidance requires both validated attachment metadata and a live Score workspace; reopened metadata-only projects or untrusted metadata use the add-score / ear-check fallback.

Ownership boundary

Exact-head verification

Fresh verification must be evaluated on exact head 8099e3b2525723474aca09db4d669167035263b3. Every earlier success receipt, including those for dd17febbbb3b35d9e322ac50a200c0fae51a0cbc and the intermediate naming head 8cd6756ef242d99fc323181b21b58f96fe24c731, is predecessor evidence only. Queued, pending, absent, or stale evidence is non-passing.

Security notes

Attack surface

  • Untrusted persisted scoreAttachments metadata with compatibility wire keys id and fileName rendered as customer-facing copy only after validation and semantic translation.
  • Untrusted/possibly stale project bootstrap used only to decide whether an open action is currently truthful.
  • No new filesystem, URL, subprocess, network, model, WebView, or PDF-parse capability.

Trust boundary and mitigations

  • Score attach/open/remove remain on the existing project-scoped desktop bridge.
  • Attachment ids must match the lowercase hyphenated UUID allowlist; extra keys and inherited members fail closed.
  • The generic persisted keys exist only at the compatibility parser boundary; downstream workspace logic uses scoreId and scoreFileName.
  • Display names are never used as path authority.
  • Live project bootstrap is validated with the existing shared parser before it can authorize open guidance.
  • fillRangeCopy performs one-shot literal replacement so attached names are not rescanned as template syntax.
  • No dependency, lockfile, vulnerability suppression, or inherited fix(security): establish canonical npm, PDF.js, Nanoid, and Undici baseline #783 security-baseline mutation is introduced.

Merge gate

Keep unmerged until this unchanged exact head has every applicable repository and central CI/build/release/security/SAST/SBOM/supply-chain/coverage/review gate terminal-success, zero valid unresolved findings, a qualifying independent non-author last-push approval, and ordinary branch-protection acceptance. Never self-approve or bypass protection. Queued, pending, skipped-required, cancelled, failed, predecessor-head, protected-base, author, model-only, or administrative-bypass evidence is non-passing.


Devin Review

Tell the player to open a trusted attached score in Score and check
tonight's first range on the page, or add a score and check the span
by ear. Display names stay fail-closed; PDF bytes stay on the Score
bridge.
@coderabbitai

coderabbitai Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

ready Workspace가 신뢰할 수 있는 첫 번째 PDF 악보와 선택된 역할의 음역을 계산하고, Score에서 확인할 안내를 영어와 한국어로 표시합니다. 관련 테스트와 프로젝트 문서도 갱신합니다.

Changes

첫 번째 악보 점검

Layer / File(s) Summary
악보 첨부 검증과 점검 데이터 생성
apps/desktop/src/features/workspace/firstScoreCheck.ts, apps/desktop/src/features/workspace/firstScoreCheck.test.ts
firstScoreCheck가 유효한 PDF 파일명, UUID, 첨부 객체를 검증합니다. 첫 번째 신뢰 가능한 악보에 선택된 역할의 음역을 연결합니다.
Workspace 점검 안내 표시
apps/desktop/src/features/workspace/Workspace.tsx, apps/desktop/src/features/workspace/Workspace.test.tsx, apps/desktop/src/locales/*/common.json, apps/desktop/src/i18n/index.test.ts
Workspace가 악보 점검 결과, 음역 없음 상태, 악보 없음 상태를 새 번역 키로 표시합니다. 렌더링, 역할 선택, 한국어 번역을 테스트합니다.
Workspace 상태 계약 문서화
AGENTS.md, ARCHITECTURE.md, CHANGELOG.md, CLAUDE.md, docs/design-system/component-contract.md
ready Workspace가 첫 연주 가능 범위와 Score에서 확인할 첫 악보를 표시한다는 설명을 갱신합니다.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: 🔵 Low · up to dd17f

The workspace guidance is safely limited to validated score metadata and falls back when no usable score is available. The PR is mergeable with owner follow-up to align the architecture, contributor, changelog, and agent documentation with that behavior.

Sequence Diagram(s)

sequenceDiagram
  participant Workspace
  participant safeProjectBootstrapSummary
  participant firstScoreCheck
  participant RehearsalSong
  Workspace->>safeProjectBootstrapSummary: sourceBootstrap 검증
  safeProjectBootstrapSummary-->>Workspace: scoreWorkspaceAvailable 반환
  Workspace->>firstScoreCheck: song, activeRole, workspace 상태 전달
  firstScoreCheck->>RehearsalSong: scoreAttachments와 역할 음역 조회
  RehearsalSong-->>firstScoreCheck: 첫 신뢰 가능한 악보와 범위 반환
  firstScoreCheck-->>Workspace: 점검 데이터 또는 null 반환
  Workspace-->>Workspace: 번역된 first-score-check 섹션 렌더링
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 9 functions across 5 files. (7 skipped: 7… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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 제목은 ready workspace 지도에 오늘 밤의 첫 스코어 확인을 표시하는 주요 변경 사항을 정확하게 설명합니다.
Full details: Docstring Coverage

Explanation

Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 9 functions across 5 files. (7 skipped: 7 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/workspace-first-score-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.

@seonghobae seonghobae added area: ui-ux Frontend, interaction, design, or user experience priority: medium Normal-priority or P2 work scope: product-gap Customer-visible product gap type: feature New or expanded product capability labels Aug 30, 2026 — with Grok (by xAI)

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

Note

This report is out of date. Scroll down for Devin Review's latest report on this PR.

Devin Review found 5 potential issues.

Devin Review

Comment thread apps/desktop/src/features/workspace/firstScoreCheck.ts Outdated
Comment thread apps/desktop/src/features/workspace/firstScoreCheck.ts Outdated
Comment thread apps/desktop/src/features/workspace/firstScoreCheck.ts Outdated
Comment on lines +168 to +182
const firstScoreCopy = firstScore
? fillRangeCopy(
t(
firstScore.sectionLabel
? "workspaceFirstScoreCheck"
: "workspaceFirstScoreCheckNoRange"
),
{
fileName: firstScore.fileName,
roleName: firstScore.roleName ?? "",
lowestNote: firstScore.lowestNote ?? "",
highestNote: firstScore.highestNote ?? "",
sectionLabel: firstScore.sectionLabel ?? ""
}
)

@devin-ai-integration devin-ai-integration Bot Aug 30, 2026

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: Filename interpolation remains literal

fillRangeCopy performs one callback-based pass. Braces and replacement patterns inside filenames remain visible text rather than becoming template syntax.

Devin Review

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

Comment thread apps/desktop/src/features/workspace/firstScoreCheck.ts Outdated

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

Note

This report is out of date. Scroll down for Devin Review's latest report on this PR.

Devin Review found 2 new potential issues.

Devin Review

Comment thread apps/desktop/src/features/workspace/firstScoreCheck.ts Outdated
Comment on lines +34 to +38
* Native attachment owns filesystem validation and can legitimately persist
* long names, surrounding spaces, repeated dots, and platform-reserved stems.
* This UI helper therefore avoids imposing a second basename policy. It only
* rejects values that cannot represent a PDF display name safely in this
* copy surface; the value is never used to rebuild a filesystem path.

@devin-ai-integration devin-ai-integration Bot Aug 30, 2026

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: Relaxed names remain display-only

Long names, repeated dots, reserved stems, and leading spaces cannot alter storage paths. Native attachment stores each PDF under a generated UUID.

Devin Review

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

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

Note

This report is out of date. Scroll down for Devin Review's latest report on this PR.

Devin Review found 1 new potential issue.

Devin Review

Comment on lines +167 to +173
const scoreWorkspaceAvailable = useMemo(
() => safeProjectBootstrapSummary(sourceBootstrap) !== null,
[sourceBootstrap]
);
const firstScore = useMemo(
() => firstScoreCheck(song, activeRole, scoreWorkspaceAvailable),
[activeRole, scoreWorkspaceAvailable, song]

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: Availability matches Score actions

scoreWorkspaceAvailable follows the bootstrap state that enables Score actions. Live results retain it; reopened project files clear it and receive fallback guidance.

Devin Review

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

coderabbitai[bot]

This comment was marked as resolved.

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

Devin Review

if (scoreFileName === null) {
return null;
}
return { scoreId: attachmentCandidate.id, scoreFileName };

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: Compatibility translation stays local

Only firstScoreCheck consumes trustedScoreAttachment, and it uses the semantic fields. Persisted projects retain their established id and fileName keys.

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 scope: product-gap Customer-visible product gap type: feature New or expanded product capability

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant