diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index 8da0c58d2..ca6dc84c8 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -1,6 +1,6 @@ # BandScope Product-Technical Gap Baseline -Last updated: 2026-08-29 +Last updated: 2026-08-30 Base revision: `develop@749511c3ad4000090048718f685c6bee6b3d2c25` (feat(workspace): name tonight's first playable range on the map, #957) ## 1. 목적과 범위 (Purpose & Scope) @@ -78,18 +78,18 @@ flowchart LR | 파트 겹침 경고 | `roles/overlap.py`, `RehearsalRole.overlapWarnings` | 구현됨 | | 단순화 가이드 | `roles/priority.py` 연계, `RehearsalRole.simplification` 필드 | 구현됨 (문자열 필드 중심) | | 전조/카포/튜닝 큐 | `chords/transposition.py`, `chords/capo.py`, `roles/tuning.py` | 구현됨 (계산), 워크스페이스 노출은 PR 진행 중 | -| 그루브/타이밍/히트 큐 | `temporal/groove.py`, `temporal/hits.py`, `temporal/stability.py` | 구현됨 | +| 그루브/타이밍/히트/tempo-stability 큐 | `temporal/groove.py`, `temporal/hits.py`, `temporal/stability.py`, PR #1059 | 구현됨 (tempo-stability 노출은 #1059 open stacked PR) | | 진입/이탈/카운트/가사 큐 앵커 | `sections/anchors.py`, `CueAnchorKind = lyric\|count\|transition` | 부분구현 | | 신뢰도 표시(section/role 수준) | `ConfidenceMarker(low/medium/high)` + `features/workspace/ConfidenceBadge.tsx` | 구현됨 | | 리허설 우선순위 | `roles/priority.py`, `RehearsalPriority`, `PracticeProgress.tsx` | 구현됨 (규칙 기반 휴리스틱) | | 수동 수정 + provenance | `ManualOverride[]`, `ProvenanceSource = model\|user` | 구현됨 | | 내보내기(cue-sheet CSV, chart JSON) | `exports/chart.py`, `src/lib/export.ts` (filename sanitize, CSV escape) | 구현됨 | | 악보(score) 보기 | `features/score/ScoreView.tsx`, `ScoreViewer.tsx`, `pdfjs.ts` | 부분구현 (PDF 뷰잉; PDF 바이트 검증은 PR 진행 중, 자동 채보 없음) | -| 루프 재생/역할별 재생 제어 | `features/player/index.tsx` | 미구현 (loop 미탐지, PR #903/#971 진행 중) | +| 루프 재생/역할별 재생 제어 | `features/workspace/RehearsalPlayer.tsx` | 부분구현 (PR #1062에서 선택 섹션의 실제 오디오 loop 재생, PR #1063에서 bounded playback-rate, PR #1066에서 선택 역할별 section picker, PR #1068에서 선택 cue의 수치 start/end 수동 보정, PR #1069에서 bounded seek, PR #1071에서 안전한 transport 단축키를 연결했으나 role-specific stem playback과 protected merge는 미완료) | | 협업(assignment/comment/approval) UI | `packages/shared-types` 타입만 존재 | 미구현 (UI 참조 0건; PR 시리즈가 첫 화면 진행 중) | | pad/solo/riff/hook/fill/voicing/articulation/dynamics/tuning/capo/vamp 등 plan 필드 | 없음 (shared-types에 미존재) | 미구현 (PR 시리즈가 추가 예정) | -| 라이선싱/데모곡 first-run | 없음 | 미구현 (PR #1009, Issue #964; diagnostics/privacy boundary는 Issue #963) | -| 자동 저장/crash-safe 프로젝트 포맷 | 없음 | 미구현 (Issue #962) | +| 라이선싱/데모곡 first-run | `apps/desktop/src-tauri/resources/demo/*`, PR #1009 | 부분구현 (번들 provenance/license와 first-run 경로는 PR #1009에 있으나 아직 protected `develop`에 착지하지 않음; 오프라인 활성화와 P0 실오디오 정확도는 미완료) | +| 자동 저장/crash-safe 프로젝트 포맷 | atomic publication과 bounded load의 일부가 PR #970에 구현됨 | 부분구현 (자동 저장, versioned migration, global recovery scan은 Issue #962) | | 서명/공증 배포+롤백 증적 | `.github/workflows/release.yml` 존재 | 부분구현 (Issue #960) | ## 4. 현재 열린 PR 기반 Gap 분석 (Open-PR Gap Analysis) @@ -118,11 +118,51 @@ flowchart LR | #858 | `f1f5e877f07aa7486cd343ddfe8aa45a9d628bbd` | analysis/cache/temp/project path authority와 redacted logging test 계약 보강; required `opencode-review` failure, `DRAFT/MERGEABLE/BLOCKED`, unresolved thread 0, qualifying independent approval 없음 | | #985 | `33d14e44bf7f41dda51b0ee246f7caae088101b9` | canonical audio resource ceiling을 desktop local/YouTube handoff와 Python defense-in-depth에 연결; required `opencode-review`·`strix` failure, `DRAFT/MERGEABLE/BLOCKED`, `REVIEW_REQUIRED`, qualifying independent approval 없음 | | #892 | `fa2abea4f0063db55490b6f392d28315a975cf59` | decoded WAV C-major acceptance와 silent aliased fixture fail-closed 보강; required `opencode-review`·`strix` failure, `DRAFT/MERGEABLE/BLOCKED`, `REVIEW_REQUIRED`, qualifying independent approval 없음 | -| #891 | `9220af2921438cb51a290ab4e4314e2fd321671d` | known-take verse/chorus chord recovery acceptance; required checks pass, `DRAFT/MERGEABLE/BLOCKED`, `REVIEW_REQUIRED`, qualifying independent approval 없음 | +| #891 | `9220af2921438cb51a290ab4e4314e2fd321671d` | dry synthetic C→G/G→C two-section chord-recovery accuracy lock; real-master evidence가 아니며 required `opencode-review` failure, `DRAFT/MERGEABLE/BLOCKED`, `REVIEW_REQUIRED`, qualifying independent approval 없음 | | #866 | `505a595d481f8ba03abd8d13e7c17202918c833f` | canonical local-audio resource policy; required `opencode-review`·`strix` failure, `DRAFT/MERGEABLE/BLOCKED`, `CHANGES_REQUESTED`, qualifying independent approval 없음 | +### 4.2 2026-08-30 tempo-stability current-head snapshot + +PR #1059 (`feat(tempo): surface tempo movement rehearsal cues`)의 현재 증거는 head `3d2aaa27804bd6113e8e9aee8aff611976698b40`, base `fix/temporal-log-privacy@b11b1e0c1756921c64524d691fb4fac440abc65b`이다. 이 PR은 #1055에 stacked되어 있으며 `OPEN`, `MERGEABLE`, `CLEAN`이지만 formal `reviewDecision`이 없고 qualifying independent approval도 없다. 동일 head의 Devin Review와 CodeRabbit status는 성공으로 보였고, CodeRabbit은 non-default base라 review를 건너뛴 상태이며, review thread unresolved count는 0이다. 따라서 이는 구현/리뷰 증거이지 보호 브랜치 병합 증거가 아니다. 다음 push 뒤에는 반드시 head, 동일 SHA Checks, reviews, threads를 다시 조회한다. + +구현 범위는 shared contract의 `TempoStability`/`TempoChange`, API의 validated tempo mapping과 local-only orchestration, Workspace의 en/ko rehearsal copy, chart JSON export, 그리고 final analysis cache v2와 reusable stem-feature cache v1의 독립 namespace다. PR branch에서 생성한 임시 click-track WAV를 실제 `TemporalAnalyzer`와 `analyze_tempo_stability`에 통과시킨 별도 probe는 `120.2 BPM`, `15 beats`, `steady`, `0 tempo changes`를 반환했다. 이 파일은 source tree나 CI fixture가 아니므로 P0 실오디오 정확도 acceptance를 대체하지 않는다. + +검증 증적은 pinned npm shim을 사용한 `./scripts/harness/quickcheck.sh` 성공이며, Python `684 passed, 24 skipped`, Python coverage `100.00%`, desktop `218` tests와 configured coverage `100%`, shared-types `21` tests, typecheck/mypy/Ruff/Bandit/security/supply-chain/GitHub bootstrap checks 및 Vite build 성공을 포함한다. 이 결과는 위 exact head에서 실행한 local evidence로만 취급한다. + +#### Security Notes + +- tempo 분석은 기존 validated local-audio 경계 안에 있으며 새 network, URL, subprocess, WebView, IPC 경계를 추가하지 않는다. +- cache/export payload에는 raw `audio_path`와 `beat_times`를 넣지 않고, shared/API 계약은 finite·positive tempo만 허용한다. +- final result cache는 schema v2로 pre-feature 결과를 재사용하지 않으며, 호환 가능한 stem features는 독립된 v1 namespace를 유지한다. + `#1025`의 과거 SHA(`3c459fd033ccd94ad6cc8df6092d9e1ce4a86e6b` 등)는 이 표의 current head가 아니므로, 해당 SHA의 Checks/review를 현재 증적으로 재사용하지 않는다. +### 4.3 2026-08-30 canonical audio-resource current-head snapshot + +PR #985 (`feat(analysis): enforce one canonical audio resource policy (#781)`)의 current head는 `b8a6db4fda36b5c07f5f30b4bd9195d5f4e2c431`이며, base는 `develop@749511c3ad4000090048718f685c6bee6b3d2c25`이다. canonical resource policy를 local/YouTube handoff와 temporal, separation, transcription, chord 경계에 연결하고, byte/header/decoded duration budget과 payload-free policy error를 공용 경계에 적용한다. 이 head에서 문서가 약속한 MP3/M4A 중 M4A path-backed intake가 libsndfile file-like probe와 `librosa.load(fileobj)` 조합 때문에 막히는 실제 결함을 확인했고, 후속 PR #1065로 보완했다. + +동일 head의 hosted 상태는 `OPEN`, `DRAFT`, `MERGEABLE`, `BLOCKED`, `REVIEW_REQUIRED`이며 review thread unresolved count는 0이다. CodeRabbit은 draft라 review를 건너뛴 성공 상태이고, 일반 CI/build/security/supply-chain gate는 통과했다. `noema-review`는 sidecar `413 request_too_large`, `opencode-review`는 current-head verdict 부재, `strix`는 잘못된 `LLM_API_BASE` 설정으로 실패했다. 이는 source finding이 아닌 외부 review/provider 증거 실패이므로 우회하지 않는다. formal qualifying independent approval은 없고, 동일 Gap의 이전 draft #866에는 #985를 canonical successor로 남겼다. + +후속 #1065는 이 head 위에 stacked 되었으며, #985 소유 브랜치는 덮어쓰지 않았다. + +#### Security Notes + +- 숫자 메타데이터는 caller/decoder가 공급하는 untrusted input이며, finite·정수성 검증 전에 실패할 수 있는 Python numeric conversion도 payload-free policy error로 정규화한다. +- 수정은 파일 열기, 디코드, URL, subprocess, IPC 경계를 추가하지 않으며, 기존 encoded/decoded resource budget과 local-only 분석 경계를 유지한다. +- `10**1000` overflow 회귀 테스트는 예외 타입과 stable reason code가 외부에 노출되는 경계를 확인한다. + +### 4.4 2026-08-30 real-audio loop current-head snapshot + +PR #1062 (`feat(player): play real audio section loops`)의 current head는 `949154375304ebef41e03d2e8bfde3ae86a40714`이며, base는 stacked branch `feat/rehearsal-player-first-section-loop@7a59047b004faa7a0b584f3d6d68c94889a452d5`이다. Tauri asset protocol은 startup scope를 비워 두고 native file-dialog 또는 validated YouTube-cache 경로의 canonical exact file만 runtime allowlist에 추가한다. `RehearsalPlayer`는 실제 `