Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
96 commits
Select commit Hold shift + click to select a range
e8e42eb
feat(activation): license a demo song and name first-run next actions
seonghobae Aug 23, 2026
eded10b
test(activation): bind demo bundle destination to runtime lookup
seonghobae Aug 23, 2026
2d1de84
fix(activation): align bundled demo destination with runtime lookup
seonghobae Aug 23, 2026
9ced398
fix(activation): satisfy licensed-demo public documentation gate
seonghobae Aug 23, 2026
22ba7ae
test(analysis): require browser analysis to fail closed
seonghobae Aug 23, 2026
cfb2cff
fix(analysis): fail browser analysis closed
seonghobae Aug 23, 2026
127db19
docs(analysis): state browser analysis fail-closed contract
seonghobae Aug 23, 2026
1ece98e
fix(licensing): remove demo CC0 usage ambiguity
seonghobae Aug 24, 2026
5bddc15
docs(activation): correct demo provenance refresh order
seonghobae Aug 24, 2026
e11c528
test(security): reject demo provenance dot segments
seonghobae Aug 24, 2026
cffdd22
fix(security): reject demo provenance dot segments
seonghobae Aug 24, 2026
90c13ae
fix(demo): refresh license provenance metadata
seonghobae Aug 24, 2026
7ffd39b
test(analysis): align failed status contract
seonghobae Aug 24, 2026
c8015b8
test(workspace): isolate state transitions
seonghobae Aug 24, 2026
0200dca
test(workspace): align licensed demo state assertions
seonghobae Aug 24, 2026
b2082e5
fix: close licensed demo review findings
seonghobae Aug 24, 2026
7c5256f
test: remove unused demo selection mock
seonghobae Aug 24, 2026
8600478
fix: guard all audio source selection paths
seonghobae Aug 24, 2026
8ae7232
Merge branch 'develop' into feat/licensed-demo-first-run
seonghobae Aug 25, 2026
5c5a42a
Merge remote-tracking branch 'origin/develop' into HEAD
seonghobae Aug 26, 2026
2adaf85
fix(app): disable source-bar intake buttons while a selection is pending
seonghobae Aug 26, 2026
336995a
fix(app): clear stale selection state when a project loads
seonghobae Aug 26, 2026
6c4fe95
style(core): apply rustfmt to demo validation
seonghobae Aug 28, 2026
45f7e2e
test(activation): preserve local source-selection concurrency
seonghobae Sep 1, 2026
bff1702
ci: repair source-selection race at canonical owner
seonghobae Sep 1, 2026
8a7f237
chore(ci): retire source selection repair workflow
seonghobae Sep 1, 2026
a8e86c6
chore(ci): run bounded source selection repair
seonghobae Sep 1, 2026
4bc5bba
chore(ci): trigger bounded source selection repair
seonghobae Sep 1, 2026
dbeb181
fix(activation): serialize source selection authority
seonghobae Sep 1, 2026
c9a3c8e
chore(ci): remove completed source selection repair workflow
seonghobae Sep 1, 2026
74cf412
chore(ci): remove source selection repair trigger
seonghobae Sep 1, 2026
8e72fcd
test(supply-chain): require demo package inventory
seonghobae Sep 1, 2026
6e776c6
test(supply-chain): name demo asset checksum explicitly
seonghobae Sep 1, 2026
cbe02fe
fix(supply-chain): inventory bundled demo assets
seonghobae Sep 1, 2026
c15c87b
docs(changelog): record first-run authority and inventory repairs
seonghobae Sep 1, 2026
e335d90
docs: align disabled tooltip accessibility guidance
seonghobae Sep 1, 2026
5eb44d1
docs: document empty-state source kinds
seonghobae Sep 1, 2026
220a62a
docs: complete demo asset refresh order
seonghobae Sep 1, 2026
9bbbb87
test: bind demo inventory to packaged files
seonghobae Sep 1, 2026
9dae5ec
chore(ci): add bounded PR1009 repair runner
seonghobae Sep 1, 2026
012bc9b
chore(ci): trigger bounded PR1009 repair
seonghobae Sep 1, 2026
88ed260
fix(ci): make bounded PR1009 repair runner executable
seonghobae Sep 1, 2026
ec57d34
ci: add bounded PR1009 repair script
seonghobae Sep 1, 2026
929d04d
ci: execute bounded PR1009 source repair
seonghobae Sep 1, 2026
c3fbc02
chore(ci): remove completed PR1009 repair trigger
seonghobae Sep 1, 2026
f27486c
chore(ci): remove completed PR1009 repair script
seonghobae Sep 1, 2026
9857597
chore(ci): remove completed PR1009 repair workflow
seonghobae Sep 1, 2026
653e970
chore(ci): stage direct PR1009 source repair
seonghobae Sep 1, 2026
02a88c2
chore(ci): trigger direct PR1009 source repair
seonghobae Sep 1, 2026
feb9bf7
fix(ci): honor BandScope npm devEngine in PR1009 repair
seonghobae Sep 1, 2026
37d532a
chore(ci): retry direct PR1009 repair after npm toolchain fix
seonghobae Sep 1, 2026
2c5e20c
fix(ci): scope demo path rewrite to validator
seonghobae Sep 1, 2026
a90746b
chore(ci): retry direct PR1009 repair after scoped Rust fix
seonghobae Sep 1, 2026
9c0b018
fix(workspace): serialize intake and validate demo WAV contract
github-actions[bot] Sep 1, 2026
571342c
test(demo): reject symlinked package assets
seonghobae Sep 1, 2026
f74772a
refactor(demo): centralize canonical song title
seonghobae Sep 1, 2026
317cb6e
fix(demo): preserve canonical title in analysis request
seonghobae Sep 1, 2026
f90fe24
fix(demo): keep canonical title through job updates
seonghobae Sep 1, 2026
68b065c
test(demo): preserve canonical title across job updates
seonghobae Sep 1, 2026
17ce4e4
chore(demo): run one-shot structural contract repair
seonghobae Sep 1, 2026
3d4e4e8
fix(ci): harden one-shot demo repair script
seonghobae Sep 1, 2026
8154b47
test(demo): require structurally analyzable licensed audio
seonghobae Sep 1, 2026
c0eb3bc
fix(ci): remove self-modifying demo repair workflow
seonghobae Sep 1, 2026
c4d2dc2
fix(demo): extend licensed audio structural window
seonghobae Sep 1, 2026
5081ee1
fix(demo): align annotations with structural windows
seonghobae Sep 1, 2026
a0d1044
fix(demo): validate the extended licensed wav size
seonghobae Sep 1, 2026
d806a5e
chore(demo): materialize deterministic binary asset once
seonghobae Sep 1, 2026
317aa9f
test(demo): prove duration follows decoded frames
seonghobae Sep 1, 2026
11161b7
fix(demo): derive duration from decoded frames
seonghobae Sep 1, 2026
d58878c
fix(ci): constrain demo materialization authority
seonghobae Sep 1, 2026
d54dd2e
chore(demo): serialize artifact materialization
seonghobae Sep 1, 2026
5fbcb09
test(demo): reject oversized UTF-8 provenance
seonghobae Sep 1, 2026
8a9c72e
fix(demo): enforce provenance UTF-8 byte ceiling
seonghobae Sep 1, 2026
5dd45a8
test(demo): bind runtime size to packaged audio
seonghobae Sep 1, 2026
7ad8403
chore(demo): run one final artifact materialization
seonghobae Sep 1, 2026
f5f0f87
test(demo): require semantic internal provenance names
seonghobae Sep 1, 2026
a6e092a
fix(demo): translate provenance wire names at boundary
seonghobae Sep 1, 2026
e27afbd
test(demo): keep wire compatibility at adapter boundary
seonghobae Sep 1, 2026
b15515e
docs(demo): record provenance naming boundary
seonghobae Sep 1, 2026
8e90a88
docs(changelog): record provenance naming adapter
seonghobae Sep 1, 2026
2c8b584
docs(architecture): define demo provenance adapter boundary
seonghobae Sep 1, 2026
80ca113
ci(repair): unblock licensed demo materializer queue
seonghobae Sep 1, 2026
c5b44d4
fix(demo): materialize structurally analyzable licensed asset
github-actions[bot] Sep 1, 2026
681b95a
test(demo): reproduce title loss after renderer reload
seonghobae Sep 1, 2026
6fc41a3
fix(demo): preserve title marker across renderer reload
seonghobae Sep 1, 2026
afd0d0e
test(demo): cover unavailable session storage
seonghobae Sep 1, 2026
1a89944
fix(demo): retain title without session storage
seonghobae Sep 1, 2026
b6078b2
docs(product): establish licensed-demo technical gap baseline
seonghobae Sep 1, 2026
5fd89d1
docs(activation): defer canonical gap baseline to #1116
seonghobae Sep 1, 2026
813c230
test(demo): format supply-chain regression
seonghobae Sep 2, 2026
c5bc6c8
test(desktop): require legacy runner to fail closed
seonghobae Sep 2, 2026
11d4540
fix(desktop): fail legacy browser analysis closed
seonghobae Sep 2, 2026
f1013db
docs(changelog): record fail-closed legacy analysis bridge
seonghobae Sep 2, 2026
d9d2614
test(activation): preserve demo progress label
seonghobae Sep 2, 2026
61dd36a
fix(activation): preserve licensed demo ready label
seonghobae Sep 2, 2026
d25512e
docs(changelog): record demo ready-label repair
seonghobae Sep 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 0 additions & 39 deletions .Jules/palette.md

This file was deleted.

40 changes: 40 additions & 0 deletions .jules/palette.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,43 @@
## 2024-05-19 - Replace HTML disabled with aria-disabled="true" for Accessible Tooltips
**Learning:** Native HTML `disabled` attributes completely hide elements from screen readers and block all pointer/hover events, preventing tooltips from functioning for disabled elements.
**Action:** Replace `disabled` with `aria-disabled="true"`, enforce block click handlers via `e.preventDefault()`, and add a title tooltip directly to the element to maintain full tooltip accessibility and keyboard focus support for visually impaired and mouse users.

## 2024-05-18 - Added focus visible styles for keyboard navigation
**Learning:** Interactive inline buttons (like the chord editor) and scrollable regions with `tabIndex={0}` do not automatically get focus visible styles, meaning keyboard users tabbing through won't know they are focused on them. Unlike central `<Button />` components which bake focus states in, these custom inline interactive elements need explicit focus styling.
**Action:** Always add explicit focus visible styles (e.g., `focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-cyan-300`) to custom interactive elements and scrollable regions with `tabIndex={0}` for proper keyboard accessibility.

## 2024-05-24 - Visual tooltips for disabled icon-only buttons
**Learning:** Icon-only buttons with `aria-label` are accessible to screen readers, but sighted users relying on mouse hover don't get context if the `title` attribute is missing, especially when the button is disabled and its purpose is unclear (e.g. "coming soon").
**Action:** Always add a `title` attribute mirroring the `aria-label` (or providing a specific disabled reason) to icon-only buttons so sighted users also receive explanatory tooltips on hover.

## 2026-06-13 - Added screen reader text for tooltip divs
**Learning:** When using `title` attributes on non-interactive elements like icon-only `div`s for tooltips, screen readers might not announce them properly because they aren't focusable. The visual tooltip is not enough for accessibility.
**Action:** Always add a visually hidden `<span className="sr-only">[Tooltip Text]</span>` inside non-interactive elements that rely on a `title` attribute so that screen readers have text content to announce.

## 2026-06-18 - Added keyboard accessibility to scrollable regions
**Learning:** Horizontally scrollable regions (like the `SectionRoadmap` component) are not accessible to keyboard-only users unless they can receive focus. Keyboard users must be able to focus the container to scroll its content using arrow keys.
**Action:** For proper keyboard accessibility in custom scrollable regions, always include `tabIndex={0}`, an appropriate `aria-label`, `role="region"`, and explicit focus visible styling (e.g., `focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-cyan-300`).

## 2026-06-19 - Internationalization
**Learning:** The desktop app uses i18n via json files located in `apps/desktop/src/locales/`
**Action:** When adding new text strings, make sure to add it to all locale files.

## 2026-06-25 - Native tooltips on disabled elements
**Learning:** Standard HTML `title` attributes used as tooltips do not render on elements that use Tailwind's `pointer-events-none` class, which is often applied to `disabled:` variants in Base UI and styled components.
**Action:** Do not rely on native `title` attributes for explaining disabled states on buttons with `pointer-events-none`. Instead, either use a custom tooltip component or ensure focus/interactive styles are preserved if an explanation is strictly required.

## 2024-06-29 - 비활성화된 네이티브 버튼의 툴팁 차단
**Learning:** 네이티브 `<button>` 요소에 `disabled` 속성을 사용하면 마우스 호버 이벤트를 포함한 포인터 이벤트가 완전히 차단되어 표준 HTML `title` 속성이 툴팁으로 표시되지 않으며, 키보드 탭 순서(tab order)에서도 제외됩니다.
**Action:** "출시 예정" 등 설명 툴팁이 필요한 비활성화된 액션 버튼의 경우, `title`을 버튼에 직접 붙이는 대신 포커스 가능한 표준 `span` (`<span tabIndex={0} title={...}>`)으로 버튼을 감싸세요. 래퍼에는 `role="button"`이나 `aria-disabled="true"` 같은 인터랙티브 버튼 역할을 부여하지 말고, 실제 비활성 상태와 버튼 의미는 내부의 native `<button disabled>`가 소유하게 하여 중첩 인터랙티브 접근성 트리를 만들지 않습니다.

## 2024-07-01 - Testing components with focusable disabled button wrappers
**Learning:** When native disabled buttons are wrapped in a focusable `span` to provide accessible tooltips, tests that previously found and clicked the `button` (by temporarily removing the `disabled` attribute) may fail or become overly complex. It is cleaner and more accurate to query the wrapper element (e.g. via its `title`) and fire events on it, reflecting the actual accessible DOM structure.
**Action:** When testing UI components that wrap disabled buttons in a focusable span for accessibility (e.g., using a tooltip/title), use `screen.getByTitle(...)` to query the wrapper element for interactions like `fireEvent.click` rather than `screen.getByRole('button')`.

## 2024-05-24 - Avoid nesting native buttons with ARIA role button on wrappers
**Learning:** Adding `role="button"` to a `span` or `div` wrapper that contains a native `<button>` element inside violates ARIA specifications. Interactive roles (like `button`) must not contain other interactive elements (even if the inner element is disabled or has `aria-hidden`), as this causes invalid/redundant accessibility trees and screen reader confusion.
**Action:** Always verify wrappers used to implement tooltips for disabled buttons are standard elements (e.g., `<span tabIndex={0} title="...">`) but *do not* assign `role="button"` to the wrapper itself.

## 2026-07-02 - Inline clear buttons preserve focus
**Learning:** Inline clear buttons often unmount immediately after clearing state, which can drop keyboard focus to the document body.
**Action:** Move focus back to the owning input before clearing state, and cover the behavior with a DOM focus test.
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,4 @@ This section applies to any agent (Claude, Codex, Cursor, opencode, ...) working
- Do not justify risky debug defaults as temporary shortcuts.
- Do not defer dependency review, SBOM generation, or supply-chain checks to a later phase.
- Do not defer Windows or macOS build enforcement to a later phase.
- The licensed demo is original CC0 audio that must pass the same local-audio intake as a user-selected file. Never present mocked browser success as production analysis.
4 changes: 3 additions & 1 deletion ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# ARCHITECTURE.md

Last updated: 2026-03-11
Last updated: 2026-09-02

## Brand source

Expand Down Expand Up @@ -113,6 +113,8 @@ Last updated: 2026-03-11
- The current shared-types baseline includes a rehearsal-domain fixture that exercises section, role, cue, confidence, provenance, and export-summary fields in the desktop shell before the full analysis pipeline lands.
- Local analysis orchestration uses typed Tauri IPC commands and a Python subprocess over stdin/stdout rather than a loopback HTTP listener.
- Local audio intake bootstraps a project by validating a user-selected file in Rust, creating app-owned temp/cache/project roots, and referencing the original source file rather than copying it in this phase.
- First-run empty workspace names **Try the demo** and **Use my own song**. The licensed demo is the CC0 `Late Night Set` package under `apps/desktop/src-tauri/resources/demo/` and enters through `select_demo_audio_source`, which reuses the same bootstrap as a user-selected local file.
- The licensed-demo provenance JSON keeps its established public wire keys for compatibility; `parseDemoProvenanceManifest` is the anti-corruption boundary that translates them into semantically specific organization-owned song and asset names before runtime consumers use the manifest.
- Those bootstrap roots should resolve from app-owned Tauri data/cache paths instead of the shared system temp namespace.
- Product and UX decisions should prefer rehearsal-first simplicity while still maintaining high analytical accuracy.
- Security decisions should prefer allowlisted narrow capabilities over generic convenience APIs.
Expand Down
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,22 @@

### Added

- First-run empty workspace offers **Try the demo** and **Use my own song**, with a CC0 licensed `Late Night Set` package that uses the same local-audio intake as a user-selected file (#964).
- Name tonight's first playable range on the ready rehearsal map and tell the player to check that span on their instrument before the section.
- Display the analyzed song tempo (BPM) as a badge in the rehearsal workspace.
- 각 합주 역할(Role)별 개인 연습 진행도를 0~100% 범위로 기록 및 시각화할 수 있는 연습 진척도(`practiceProgress`) 트래커 기능 추가. UI 컨트롤(슬라이더 및 +/- 버튼)과 한/영 다국어 지원 포함.

### Changed

- Translate the licensed-demo provenance wire manifest at one anti-corruption boundary so established public JSON keys remain compatible while organization-owned runtime types use semantically specific song and asset names.
- Pinned npm `10.9.9` as the approved lockfile generator, activated it through Node-bundled Corepack before dependency consumption, and fail closed unless its bundled `tar` is at least `7.5.19`; primary CI still consumes the committed lock only through frozen `npm ci` validation, rejects mutable npm resolution in the lock gate, requires integrity evidence for public-registry lock entries, and preserves generator-sensitive root `@esbuild/*` peer metadata.

### Fixed

- Preserve the licensed demo's buyer-facing `Late Night Set` title in ready-state analysis progress after native local-source validation.
- Fail the legacy `job_runner` analysis mutation bridge closed outside Tauri instead of synthesizing browser-only success, while preserving its read-only placeholder workspace and native IPC wire contract.
- Serialized local, demo, and YouTube source selection behind one synchronous authority so replacement intake cannot analyze a stale prior song or race another source; failed replacement preserves the prior valid selection.
- Added checksum-bound supplemental inventory coverage for every packaged licensed-demo asset, including the WAV, license, annotations, and provenance manifest.
- Upgraded the local score PDF parser to `pdfjs-dist` 6.2.108, pinned Undici 7.29.0 across the workspace, and constrained PDF loading to copied in-memory bytes with a same-origin bundled worker and npm-generated lock provenance.

## [0.1.3] - 2026-04-29
Expand Down Expand Up @@ -74,4 +80,4 @@

- `ChordsFeature` (코드 분석) 화면에서 각 파트(Role)의 `transpositionPlan`(이조/조옮김 계획)을 표시하는 기능을 추가했습니다.
- `RangesFeature` (음역대 분석) 화면에서 겹침 경고(Overlap warning) 외에 해당 파트의 채보(Transcription) 가능 노드 수를 요약하여 보여주는 기능을 추가했습니다.
- 신규 UI 요소에 대한 단위 테스트를 추가했습니다 (`apps/desktop/src/features/chords/index.test.tsx`, `apps/desktop/src/features/ranges/index.test.tsx`).
- 신규 UI 요소에 대한 단위 테스트를 추가했습니다 (`apps/desktop/src/features/chords/index.test.tsx`, `apps/desktop/src/features/ranges/index.test.tsx`).
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ BandScope is a local-first desktop app for rehearsal prep: it turns a song into

Three layers, decoupled through shared contracts:

- `apps/desktop` — Tauri 2 + Vite + React 19 shell (Tailwind 4, Base UI, Storybook). Feature screens live in `src/features/` (home, workspace, chords, ranges, player, settings). The ready workspace names tonight's first playable range and the next instrument check. `src/lib/analysis.ts` and `src/lib/job_runner.ts` call typed Tauri IPC commands, with a browser fallback that serves demo data when not running inside Tauri.
- `apps/desktop/src-tauri/src/main.rs` — the Rust orchestration boundary. Tauri commands (`start_analysis_job`, `get_analysis_job_status`, `select_local_audio_source`, `import_youtube_url`) validate untrusted input (project IDs, file paths, URLs) and spawn the Python engine as a subprocess. There is no loopback HTTP listener and no network path for local analysis.
- `apps/desktop` — Tauri 2 + Vite + React 19 shell (Tailwind 4, Base UI, Storybook). Feature screens live in `src/features/` (home, workspace, chords, ranges, player, settings). The ready workspace names tonight's first playable range and the next instrument check. `src/lib/analysis.ts` and `src/lib/job_runner.ts` call typed Tauri IPC commands. Without the desktop IPC bridge, analysis fails closed instead of synthesizing a production success; bundled licensed-demo intake is desktop-only and also fails closed in the browser.
- `apps/desktop/src-tauri/src/main.rs` — the Rust orchestration boundary. Tauri commands (`start_analysis_job`, `get_analysis_job_status`, `select_local_audio_source`, `select_demo_audio_source`, `import_youtube_url`) validate untrusted input (project IDs, file paths, URLs) and spawn the Python engine as a subprocess. There is no loopback HTTP listener and no network path for local analysis.
- `services/analysis-engine` — Python package `bandscope_analysis` (librosa/numpy). Entry point `cli.py` reads a JSON job request on stdin and prints a structured job-status JSON envelope on stdout (`--progress-jsonl` streams progress lines). `api.py` orchestrates the pipeline across the `separation`, `sections`, `roles`, `chords`, `ranges`, `temporal`, `transcription`, and `youtube` modules.

Data flow: React UI → Tauri IPC command → Rust validation + Python subprocess over stdin/stdout → job status and progress events emitted back to the UI.
Expand Down
Loading
Loading