From 552d06fdd999b262a41148daf1eae238480720e2 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 06:31:41 -0700 Subject: [PATCH 01/77] docs: add BandScope product and technical gap baseline --- docs/product-technical-gap-baseline.md | 261 +++++++++++++++++++++++++ 1 file changed, 261 insertions(+) create mode 100644 docs/product-technical-gap-baseline.md diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md new file mode 100644 index 000000000..cb292c7c7 --- /dev/null +++ b/docs/product-technical-gap-baseline.md @@ -0,0 +1,261 @@ +# BandScope Product and Technical Gap Baseline + +**Status:** Proposed baseline +**Snapshot date:** 2026-08-20 (Asia/Seoul) +**Protected base reviewed:** `develop@acdbea6344fe1231c39535b575f4de35e4c607c9` +**Open pull requests inventoried:** 83 +**Program issue:** [#958](https://github.com/ContextualWisdomLab/bandscope/issues/958) + +> This document is a point-in-time product-readiness baseline, not a claim that any listed pull request is merge-ready. Pull-request heads, checks, reviews, and branch-protection decisions can change after this snapshot. Before any merge, the exact live head must be refreshed and revalidated. + +## Executive conclusion + +BandScope already has a credible local-first product boundary, a typed Tauri/Python analysis architecture, extensive security hardening, and substantial rehearsal-oriented UI work. It is not yet a complete commercial desktop product. + +The protected snapshot does not yet demonstrate the complete buyer journey: + +```text +install a trusted build +→ import a real song +→ obtain measurably accurate analysis +→ actively rehearse with the analysis +→ save and recover the project +→ share a bounded handoff +→ diagnose failure without leaking the song +→ update or roll back safely +``` + +The highest-leverage work is therefore not another isolated cue card. It is to converge the current queue into measurable product verticals and finish the missing distribution, active rehearsal, durability, supportability, activation, accessibility, and scientific-evidence boundaries. + +## Product boundary + +BandScope is a **local-first rehearsal decision tool**. It should help musicians understand a recording, decide what to practise, and rehearse the difficult passage. It is not a notation editor, DAW, plugin host, cloud-storage product, or autonomous authority over musical truth. + +The product must preserve these claim boundaries: + +- analysis results are evidence-backed recommendations, not infallible transcription; +- local files remain local unless the user explicitly exports a bounded artifact; +- a visual state, mocked array, skipped GPU test, predecessor-head check, or unsigned package is not production evidence; +- unsupported stems, roles, formats, or model capabilities fail honestly rather than fabricating a result. + +## Evidence reviewed + +The investigation covered: + +- repository metadata and the protected `develop` tree; +- README, architecture, acceptance, release, deployment, package, Tauri, workflow, Rust, TypeScript, and Python surfaces; +- all 83 open pull requests returned by the connected GitHub account; +- existing canonical product/security issues; +- saved Figma file `BP30foevuRtufwRpTknZUw`; +- current official guidance for Tauri signing/updating, Apple notarization, Microsoft signing, WCAG 2.2, and music-information-retrieval evaluation. + +## Buyer-visible completion gaps + +| Priority | Gap | Current evidence | Buyer impact | Owning issue | +|---:|---|---|---|---| +| P0 | Trusted distribution and update | Version `0.1.0`; release workflow is principally validation/preflight; no complete signed/notarized updater and rollback evidence | Buyers cannot establish publisher trust or safely update/repair | [#960](https://github.com/ContextualWisdomLab/bandscope/issues/960) | +| P0 | Active rehearsal player | Many cue/action PRs exist, but there is no single timing/playback authority proving count-in, precise looping, rate, cue navigation, and role controls through the production desktop boundary | Analysis does not yet close the loop into repeated practice | [#961](https://github.com/ContextualWisdomLab/bandscope/issues/961) | +| P0 | Crash-safe durable projects | Durable project truth, autosave, atomic publish, migrations, backups, recovery, and rollback are not governed by one versioned format | A crash or upgrade can threaten user work and trust | [#962](https://github.com/ContextualWisdomLab/bandscope/issues/962) | +| P0 | Scientific acceptance | Real decoded-audio acceptance exists as work in progress, but the broader licensed multi-fixture MIR program, recognized metrics, uncertainty, CPU/GPU parity, and claim matrix are not closed | Musicians cannot judge where results are trustworthy | [#770](https://github.com/ContextualWisdomLab/bandscope/issues/770) | +| P0 | Resource/cancellation contract | Canonical local-audio admission is being developed, but all decode, separation, analysis, PDF, cancellation, and fallback paths must converge | Large or hostile files can degrade availability or create inconsistent behavior | [#781](https://github.com/ContextualWisdomLab/bandscope/issues/781) | +| P1 | Diagnostics and support evidence | Multiple redaction fixes exist, but no single typed local diagnostics contract or previewable support bundle exists | Failures remain expensive to diagnose and risk leaking private paths/content | [#963](https://github.com/ContextualWisdomLab/bandscope/issues/963) | +| P1 | First-run activation | Empty states and next-action PRs exist, but no licensed demo proves time-to-first-rehearsal through the production path | A new buyer cannot evaluate value quickly or reproducibly | [#964](https://github.com/ContextualWisdomLab/bandscope/issues/964) | +| P1 | Design and accessibility parity | Storybook work is open. The saved Figma file claims 28 pages but currently exposes two top-level pages; its footer cites `0.1.3`, while repository/runtime metadata cites `0.1.0` | Design status can be mistaken for shipped behavior; assistive-technology acceptance is incomplete | [#965](https://github.com/ContextualWisdomLab/bandscope/issues/965) | +| P0 | PR queue convergence | 83 open PRs include canonical bases, small feature slices, security repairs, dependency updates, and overlapping writers | Review drift and ancestry conflicts prevent coherent delivery | [#966](https://github.com/ContextualWisdomLab/bandscope/issues/966) | + +## Completion program + +| Issue | Product outcome | +|---|---| +| [#958](https://github.com/ContextualWisdomLab/bandscope/issues/958) | Parent BandScope 1.0 completion program and commercial definition of done | +| [#960](https://github.com/ContextualWisdomLab/bandscope/issues/960) | Signed/notarized, provenance-backed desktop release and verified updater | +| [#961](https://github.com/ContextualWisdomLab/bandscope/issues/961) | Active rehearsal transport, loop, count-in, cue navigation, and honest role controls | +| [#962](https://github.com/ContextualWisdomLab/bandscope/issues/962) | Versioned project schema, atomic save, autosave, migration, backup, and recovery | +| [#963](https://github.com/ContextualWisdomLab/bandscope/issues/963) | Typed redacted diagnostics, crash evidence, and offline support bundle | +| [#964](https://github.com/ContextualWisdomLab/bandscope/issues/964) | Licensed demo and measurable first-run rehearsal journey | +| [#965](https://github.com/ContextualWisdomLab/bandscope/issues/965) | Figma/Storybook/shipped-UI parity and WCAG 2.2 AA acceptance | +| [#966](https://github.com/ContextualWisdomLab/bandscope/issues/966) | Dependency-aware merge trains and explicit PR succession | + +Existing canonical work that must be preserved rather than duplicated includes [#610](https://github.com/ContextualWisdomLab/bandscope/issues/610), [#739](https://github.com/ContextualWisdomLab/bandscope/issues/739), [#770](https://github.com/ContextualWisdomLab/bandscope/issues/770), [#781](https://github.com/ContextualWisdomLab/bandscope/issues/781), [#526](https://github.com/ContextualWisdomLab/bandscope/issues/526), [#542](https://github.com/ContextualWisdomLab/bandscope/issues/542), [#779](https://github.com/ContextualWisdomLab/bandscope/issues/779), [#847](https://github.com/ContextualWisdomLab/bandscope/issues/847), [#852](https://github.com/ContextualWisdomLab/bandscope/issues/852), and [#864](https://github.com/ContextualWisdomLab/bandscope/issues/864). + +## Merge-train model + +| Train | Responsibility | Initial live PR count | Completion issue | +|---|---|---:|---| +| `T0` | Dependency, toolchain, workflow and quality base | 30 | [#966](https://github.com/ContextualWisdomLab/bandscope/issues/966) | +| `T1` | Local input, filesystem authority, resource admission and cancellation | 6 | [#781](https://github.com/ContextualWisdomLab/bandscope/issues/781), [#962](https://github.com/ContextualWisdomLab/bandscope/issues/962) | +| `T2` | Scientific accuracy, MIR evaluation and numerical parity | 8 | [#770](https://github.com/ContextualWisdomLab/bandscope/issues/770) | +| `T3` | Rehearsal actions and active-player vertical | 23 | [#961](https://github.com/ContextualWisdomLab/bandscope/issues/961) | +| `T4` | Project portability, handoff and interoperability | 2 | [#739](https://github.com/ContextualWisdomLab/bandscope/issues/739), [#962](https://github.com/ContextualWisdomLab/bandscope/issues/962) | +| `T5` | Activation, UI system, Storybook and accessibility | 8 | [#964](https://github.com/ContextualWisdomLab/bandscope/issues/964), [#965](https://github.com/ContextualWisdomLab/bandscope/issues/965) | +| `T6` | Diagnostics, redaction, security evidence and supportability | 6 | [#963](https://github.com/ContextualWisdomLab/bandscope/issues/963) | +| `T7` | Signed commercial release and updater | 0 current implementation PRs in this snapshot | [#960](https://github.com/ContextualWisdomLab/bandscope/issues/960) | + +The routing below is an **initial product-boundary classification**, not a merge-readiness verdict. Issue #966 must refresh exact head SHAs, dependency edges, current checks, current reviews, unresolved threads, and succession before any action. + +## Current open PR inventory + +| PR | Title | Initial train | Required next action | +|---:|---|---|---| +| [731](https://github.com/ContextualWisdomLab/bandscope/pull/731) | 🎨 Palette: 향상된 접근성을 위해 아이콘 버튼에 툴팁 및 aria-disabled 적용 | `T5` | Triage against the live exact head before action | +| [732](https://github.com/ContextualWisdomLab/bandscope/pull/732) | ⚡ Chords: vectorize HMM reference and correct relative-key prior | `T2` | Triage against the live exact head before action | +| [737](https://github.com/ContextualWisdomLab/bandscope/pull/737) | feat(integration): add versioned naruon rehearsal handoff | `T4` | Triage against the live exact head before action | +| [740](https://github.com/ContextualWisdomLab/bandscope/pull/740) | feat: import rehearsal handoffs for focused reanalysis | `T4` | Triage against the live exact head before action | +| [744](https://github.com/ContextualWisdomLab/bandscope/pull/744) | feat(i18n): localize Workspace controls and harden interpolation | `T5` | Triage against the live exact head before action | +| [746](https://github.com/ContextualWisdomLab/bandscope/pull/746) | perf(segmenter): vectorize checkerboard novelty reference | `T2` | Triage against the live exact head before action | +| [750](https://github.com/ContextualWisdomLab/bandscope/pull/750) | fix(score): validate PDF bridge byte arrays without coercion | `T1` | Triage against the live exact head before action | +| [754](https://github.com/ContextualWisdomLab/bandscope/pull/754) | build(deps): bump time from 0.3.53 to 0.3.55 in /apps/desktop/src-tauri | `T0` | Triage against the live exact head before action | +| [776](https://github.com/ContextualWisdomLab/bandscope/pull/776) | feat(chords): surface actionable rehearsal guidance | `T3` | Triage against the live exact head before action | +| [780](https://github.com/ContextualWisdomLab/bandscope/pull/780) | fix(security): keep every CodeQL Action phase on one revision | `T0` | Triage against the live exact head before action | +| [783](https://github.com/ContextualWisdomLab/bandscope/pull/783) | fix(security): establish canonical npm, PDF.js, Nanoid, and Undici baseline | `T0` | Triage against the live exact head before action | +| [811](https://github.com/ContextualWisdomLab/bandscope/pull/811) | 🔒 [보안] CLI 무제한 파일 읽기 취약점 수정 | `T1` | Triage against the live exact head before action | +| [824](https://github.com/ContextualWisdomLab/bandscope/pull/824) | 🧪 테스트: 줄기 분리 시간 초과 처리 테스트 추가 | `T1` | Triage against the live exact head before action | +| [826](https://github.com/ContextualWisdomLab/bandscope/pull/826) | 🧪 [테스트 개선] TemporalAnalyzer 분석 오류 테스트 추가 | `T6` | Triage against the live exact head before action | +| [827](https://github.com/ContextualWisdomLab/bandscope/pull/827) | fix(desktop): remove synthetic browser analysis success | `T2` | Triage against the live exact head before action | +| [828](https://github.com/ContextualWisdomLab/bandscope/pull/828) | test(analysis): govern real YouTube known-stem benchmark | `T2` | Triage against the live exact head before action | +| [833](https://github.com/ContextualWisdomLab/bandscope/pull/833) | 🎨 Palette: 코드 수정 버튼에 접근성 있는 툴팁 지원 추가 | `T5` | Triage against the live exact head before action | +| [834](https://github.com/ContextualWisdomLab/bandscope/pull/834) | ⚡ Bolt: O(1) 메모리로 순차적 코드 변경 횟수 계산 최적화 | `T2` | Triage against the live exact head before action | +| [845](https://github.com/ContextualWisdomLab/bandscope/pull/845) | feat(analysis): CSV 큐시트 내보내기 기능 및 수식 주입 보안 로직 추가 | `T3` | Triage against the live exact head before action | +| [849](https://github.com/ContextualWisdomLab/bandscope/pull/849) | ⚡ Bolt: 차트 내보내기(chart.py)의 중복 제거 로직 O(N^2)에서 O(N)으로 최적화 | `T5` | Triage against the live exact head before action | +| [850](https://github.com/ContextualWisdomLab/bandscope/pull/850) | feat: add Part Handoff Map UI component to workspace | `T3` | Triage against the live exact head before action | +| [858](https://github.com/ContextualWisdomLab/bandscope/pull/858) | fix(security): bind analysis paths to filesystem authority | `T1` | Triage against the live exact head before action | +| [859](https://github.com/ContextualWisdomLab/bandscope/pull/859) | ⚡ Bolt: GrooveMap의 maxTime 계산 성능 개선 | `T5` | Triage against the live exact head before action | +| [861](https://github.com/ContextualWisdomLab/bandscope/pull/861) | test(quality): enforce Python branch coverage | `T0` | Triage against the live exact head before action | +| [865](https://github.com/ContextualWisdomLab/bandscope/pull/865) | fix(score): bound native PDF reads before allocation | `T1` | Triage against the live exact head before action | +| [866](https://github.com/ContextualWisdomLab/bandscope/pull/866) | fix(audio): establish canonical local-audio resource policy | `T1` | Triage against the live exact head before action | +| [867](https://github.com/ContextualWisdomLab/bandscope/pull/867) | test(supply-chain): preserve simple dependency-path cycle semantics | `T0` | Triage against the live exact head before action | +| [873](https://github.com/ContextualWisdomLab/bandscope/pull/873) | feat(roles): wire measured register overlap into section warnings | `T2` | Triage against the live exact head before action | +| [874](https://github.com/ContextualWisdomLab/bandscope/pull/874) | feat(workspace): name the parts to lock in first | `T3` | Triage against the live exact head before action | +| [881](https://github.com/ContextualWisdomLab/bandscope/pull/881) | test(ci): lock local OpenCode small_model to NVIDIA NIM | `T0` | Triage against the live exact head before action | +| [884](https://github.com/ContextualWisdomLab/bandscope/pull/884) | feat(workspace): open Stem Lab as honest isolation lanes | `T3` | Triage against the live exact head before action | +| [891](https://github.com/ContextualWisdomLab/bandscope/pull/891) | test(analysis): lock verse/chorus chord recovery on a known take | `T2` | Triage against the live exact head before action | +| [892](https://github.com/ContextualWisdomLab/bandscope/pull/892) | fix(analysis): score C major acceptance from decoded WAV bytes | `T2` | Triage against the live exact head before action | +| [894](https://github.com/ContextualWisdomLab/bandscope/pull/894) | fix(security): drop persisted credentials before dependency lifecycle code | `T0` | Triage against the live exact head before action | +| [895](https://github.com/ContextualWisdomLab/bandscope/pull/895) | fix(workflows): audit orphaned Actions registry identities | `T0` | Triage against the live exact head before action | +| [896](https://github.com/ContextualWisdomLab/bandscope/pull/896) | build(node): coordinate Node 22.22.2 floor with jsdom 30 | `T0` | Triage against the live exact head before action | +| [897](https://github.com/ContextualWisdomLab/bandscope/pull/897) | feat(workspace): Storybook tokens for rehearsal components | `T5` | Triage against the live exact head before action | +| [898](https://github.com/ContextualWisdomLab/bandscope/pull/898) | feat(workspace): put the next rehearsal action on empty and error cards | `T5` | Triage against the live exact head before action | +| [899](https://github.com/ContextualWisdomLab/bandscope/pull/899) | feat(workspace): open existing rehearsal surfaces from the sidebar | `T5` | Triage against the live exact head before action | +| [900](https://github.com/ContextualWisdomLab/bandscope/pull/900) | feat(workspace): name tonight's export and priority actions | `T3` | Triage against the live exact head before action | +| [901](https://github.com/ContextualWisdomLab/bandscope/pull/901) | feat(workspace): start tonight's first part from the ready board | `T3` | Triage against the live exact head before action | +| [903](https://github.com/ContextualWisdomLab/bandscope/pull/903) | feat(workspace): loop tonight's first section on the map | `T3` | Triage against the live exact head before action | +| [904](https://github.com/ContextualWisdomLab/bandscope/pull/904) | feat(workspace): count in tonight's first section on the map | `T3` | Triage against the live exact head before action | +| [905](https://github.com/ContextualWisdomLab/bandscope/pull/905) | feat(workspace): open tonight's first notes on the groove map | `T3` | Triage against the live exact head before action | +| [907](https://github.com/ContextualWisdomLab/bandscope/pull/907) | chore(env): add Cloud Agent environment config | `T0` | Triage against the live exact head before action | +| [910](https://github.com/ContextualWisdomLab/bandscope/pull/910) | feat(workspace): set up tonight's part before the first entrance | `T3` | Triage against the live exact head before action | +| [912](https://github.com/ContextualWisdomLab/bandscope/pull/912) | feat(workspace): guide tonight's first entrance on map and player | `T3` | Triage against the live exact head before action | +| [913](https://github.com/ContextualWisdomLab/bandscope/pull/913) | feat(workspace): guide tonight's first lyric cue on map and player | `T3` | Triage against the live exact head before action | +| [914](https://github.com/ContextualWisdomLab/bandscope/pull/914) | feat(workspace): guide tonight's first dropout on map and player | `T3` | Triage against the live exact head before action | +| [916](https://github.com/ContextualWisdomLab/bandscope/pull/916) | feat(workspace): guide tonight's first pickup on map and player | `T3` | Triage against the live exact head before action | +| [918](https://github.com/ContextualWisdomLab/bandscope/pull/918) | build(deps): bump uuid from 1.23.4 to 1.24.1 in /apps/desktop/src-tauri | `T0` | Triage against the live exact head before action | +| [919](https://github.com/ContextualWisdomLab/bandscope/pull/919) | build(deps): update numba requirement from <0.67.0 to <0.68.0 in /services/analysis-engine | `T0` | Triage against the live exact head before action | +| [920](https://github.com/ContextualWisdomLab/bandscope/pull/920) | build(deps): bump react and @types/react | `T0` | Triage against the live exact head before action | +| [921](https://github.com/ContextualWisdomLab/bandscope/pull/921) | build(deps): bump @base-ui/react from 1.5.0 to 1.7.0 | `T0` | Triage against the live exact head before action | +| [922](https://github.com/ContextualWisdomLab/bandscope/pull/922) | build(deps-dev): bump storybook from 10.4.6 to 10.5.8 | `T0` | Triage against the live exact head before action | +| [923](https://github.com/ContextualWisdomLab/bandscope/pull/923) | build(deps-dev): bump @storybook/react-vite from 10.4.6 to 10.5.8 | `T0` | Triage against the live exact head before action | +| [924](https://github.com/ContextualWisdomLab/bandscope/pull/924) | build(deps): bump github/codeql-action/init from 4.37.0 to 4.37.7 | `T0` | Triage against the live exact head before action | +| [925](https://github.com/ContextualWisdomLab/bandscope/pull/925) | build(deps-dev): bump typescript-eslint from 8.63.0 to 8.67.0 | `T0` | Triage against the live exact head before action | +| [926](https://github.com/ContextualWisdomLab/bandscope/pull/926) | build(deps): bump lucide-react from 1.24.0 to 1.31.0 | `T0` | Triage against the live exact head before action | +| [927](https://github.com/ContextualWisdomLab/bandscope/pull/927) | build(deps): bump sonner from 2.0.7 to 2.0.8 | `T0` | Triage against the live exact head before action | +| [928](https://github.com/ContextualWisdomLab/bandscope/pull/928) | build(deps-dev): bump eslint-plugin-jsdoc from 63.0.13 to 64.2.0 | `T0` | Triage against the live exact head before action | +| [929](https://github.com/ContextualWisdomLab/bandscope/pull/929) | build(deps-dev): bump @testing-library/jest-dom from 6.9.1 to 7.0.1 | `T0` | Triage against the live exact head before action | +| [930](https://github.com/ContextualWisdomLab/bandscope/pull/930) | build(deps): bump github/codeql-action/autobuild from 4.37.0 to 4.37.7 | `T0` | Triage against the live exact head before action | +| [931](https://github.com/ContextualWisdomLab/bandscope/pull/931) | build(deps): bump astral-sh/setup-uv from 8.3.2 to 10.0.1 | `T0` | Triage against the live exact head before action | +| [932](https://github.com/ContextualWisdomLab/bandscope/pull/932) | build(deps): bump github/codeql-action/analyze from 4.37.0 to 4.37.7 | `T0` | Triage against the live exact head before action | +| [933](https://github.com/ContextualWisdomLab/bandscope/pull/933) | build(deps): bump github/codeql-action/upload-sarif from 4.37.0 to 4.37.7 | `T0` | Triage against the live exact head before action | +| [934](https://github.com/ContextualWisdomLab/bandscope/pull/934) | feat(workspace): guide tonight's first stop on map and player | `T3` | Triage against the live exact head before action | +| [936](https://github.com/ContextualWisdomLab/bandscope/pull/936) | build(deps): bump the uv group across 1 directory with 2 updates | `T0` | Triage against the live exact head before action | +| [937](https://github.com/ContextualWisdomLab/bandscope/pull/937) | feat(workspace): guide tonight's first labeled handoff on map and player | `T3` | Triage against the live exact head before action | +| [939](https://github.com/ContextualWisdomLab/bandscope/pull/939) | feat(workspace): guide tonight's first chorus on map and player | `T3` | Triage against the live exact head before action | +| [941](https://github.com/ContextualWisdomLab/bandscope/pull/941) | 🛡️ Sentinel: CSV 수식 주입 NUL·전각 연산자 우회 차단 | `T6` | Triage against the live exact head before action | +| [942](https://github.com/ContextualWisdomLab/bandscope/pull/942) | build(deps): bump react-dom and @types/react-dom | `T0` | Triage against the live exact head before action | +| [943](https://github.com/ContextualWisdomLab/bandscope/pull/943) | feat(workspace): guide tonight's first intro on map and player | `T3` | Triage against the live exact head before action | +| [944](https://github.com/ContextualWisdomLab/bandscope/pull/944) | build(rust): pin all product and release lanes to 1.97.1 | `T0` | Triage against the live exact head before action | +| [946](https://github.com/ContextualWisdomLab/bandscope/pull/946) | feat(workspace): guide tonight's first bridge on map and player | `T3` | Triage against the live exact head before action | +| [947](https://github.com/ContextualWisdomLab/bandscope/pull/947) | feat(workspace): guide tonight's first verse on map and player | `T3` | Triage against the live exact head before action | +| [948](https://github.com/ContextualWisdomLab/bandscope/pull/948) | fix(security): retire quick-xml RustSec exceptions | `T0` | Triage against the live exact head before action | +| [949](https://github.com/ContextualWisdomLab/bandscope/pull/949) | fix(security): redact key detector dependency failures | `T6` | Triage against the live exact head before action | +| [950](https://github.com/ContextualWisdomLab/bandscope/pull/950) | fix(security): redact temporal detector failure logs | `T6` | Triage against the live exact head before action | +| [951](https://github.com/ContextualWisdomLab/bandscope/pull/951) | fix(security): redact range-analysis failure logs | `T6` | Triage against the live exact head before action | +| [955](https://github.com/ContextualWisdomLab/bandscope/pull/955) | feat(workspace): guide tonight's first pre-chorus on map and player | `T3` | Triage against the live exact head before action | +| [956](https://github.com/ContextualWisdomLab/bandscope/pull/956) | fix(security): redact articulation failure logs | `T6` | Triage against the live exact head before action | +| [957](https://github.com/ContextualWisdomLab/bandscope/pull/957) | feat(workspace): name tonight's first playable range on the map | `T3` | Triage against the live exact head before action | + +## Required execution order + +1. Establish the canonical dependency, toolchain, workflow, and branch-coverage base. +2. Refresh the live PR graph and remove unrelated lock/toolchain drift from feature heads. +3. Complete local input/resource/cancellation and real-audio scientific acceptance. +4. Consolidate cue/action slices behind one active rehearsal-player contract. +5. Establish the versioned project/persistence boundary and portable handoff distinction. +6. Complete first-run activation, Storybook, Figma parity, localization, and end-to-end accessibility. +7. Consolidate diagnostics/redaction into one supportability contract. +8. Cut the signed/notarized updater-backed release only from the protected, evidenced product vertical. + +For each train: + +```text +refresh live queue +→ validate canonical predecessor +→ inspect current review threads +→ repair root causes +→ remove unrelated drift +→ run current-head checks +→ obtain qualifying independent approval +→ merge or enable auto-merge +→ restack the next PR +→ close superseded duplicates with succession evidence +→ refresh this baseline +``` + +Waiting for one train's external review or hosted runner is not a reason to stop work on an independent train. It is also not permission to create a competing writer or transfer stale evidence. + +## BandScope 1.0 commercial definition of done + +### Product outcome + +- A clean supported Windows or macOS installation reaches one useful rehearsal action without terminal setup. +- A licensed demo and a user-selected local file use the same production intake, decode, analysis, player, save, and recovery boundaries. +- Analysis quality is reported with recognized metrics, fixture rights, uncertainty, backend parity, and explicit limitations. +- The user can repeat a selected passage using a deterministic, accessible rehearsal player. +- Project work survives ordinary restart, crash, interrupted write, migration, and supported rollback. +- A bounded rehearsal handoff preserves provenance without granting filesystem or network authority. + +### Trust, privacy, and operations + +- Windows artifacts are signed; macOS artifacts are signed and notarized. +- The updater verifies signatures/digests, works offline when unavailable, and has a tested recovery path. +- No ordinary log or support artifact contains raw audio, project payloads, credentials, absolute paths, or unnecessary PII. +- Support evidence is deterministic, bounded, user-previewable, and useful without uploading the song. +- All security, dependency, model, benchmark, and release artifacts are bound to the exact protected source commit. + +### Accessibility and design + +- Keyboard-only and screen-reader users complete first-run, import, analyze, rehearse, save/recover, share, support, and update-decision journeys. +- Charts, timelines, waveforms, confidence displays, and exports preserve exact values, units, warnings, and uncertainty. +- Code tokens, Storybook stories, Figma components, localization keys, runtime version, and shipped UI have an explicit parity matrix. +- Korean and English provide equivalent choices, limitations, privacy statements, and next actions. + +### Engineering evidence + +- Every open PR belongs to exactly one train and has an explicit disposition. +- There is one active canonical writer per product boundary or a declared stack order. +- Duplicate/superseded work is closed only after unique tests and requirements are transferred. +- Every merged exact head has all required terminal-success checks, qualifying independent approval, zero unresolved actionable threads, 100% repository-owned production statement/branch coverage, and complete public API documentation. +- The final release includes checksums, SBOM, provenance/attestation, migration/recovery evidence, accessibility evidence, and MIR accuracy evidence. + +## Explicit non-goals + +- Do not merge all 83 PRs merely to reduce the count. +- Do not turn BandScope into a DAW, notation editor, or mandatory cloud service. +- Do not claim unsigned validation artifacts are releases. +- Do not use synthetic arrays, mocked browser success, skipped GPU execution, stale checks, or predecessor-head approval as product evidence. +- Do not treat Figma labels or unimplemented Storybook states as shipped features. +- Do not silently discard project fields, unsupported analysis, migration data, or user corrections. + +## Known limitations of this snapshot + +- Exact PR head SHAs were not frozen into this human baseline because active branches can advance while the inventory is reviewed. The machine-readable seed marks them for refresh before action. +- This document does not assert that any existing PR is approved, passing, or safe to merge. +- The Figma inspection is a point-in-time metadata/structure review; visual and interaction acceptance remains issue #965 work. +- The investigation created requirements and a convergence plan. It did not implement, merge, sign, notarize, benchmark, or release the product. From 9ac6d71f0b267ff78c60866785b546e7d2a60ae8 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 06:32:25 -0700 Subject: [PATCH 02/77] docs(adr): record BandScope 1.0 readiness decision --- .../0001-bandscope-1-0-product-readiness.md | 165 ++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 docs/architecture/decisions/0001-bandscope-1-0-product-readiness.md diff --git a/docs/architecture/decisions/0001-bandscope-1-0-product-readiness.md b/docs/architecture/decisions/0001-bandscope-1-0-product-readiness.md new file mode 100644 index 000000000..2172349f4 --- /dev/null +++ b/docs/architecture/decisions/0001-bandscope-1-0-product-readiness.md @@ -0,0 +1,165 @@ +# ADR-0001: BandScope 1.0 Product-Readiness Program and Merge Trains + +- **Status:** Proposed +- **Date:** 2026-08-20 +- **Decision owners:** BandScope maintainers +- **Program issue:** [#958](https://github.com/ContextualWisdomLab/bandscope/issues/958) +- **Figma file ID:** `BP30foevuRtufwRpTknZUw` + +## Context + +BandScope has grown through many narrow, independently reviewable pull requests. The 2026-08-20 snapshot contains 83 open PRs above protected `develop@acdbea6344fe1231c39535b575f4de35e4c607c9`. + +The repository already contains valuable local-first architecture, scientific-analysis, security, accessibility, and rehearsal-workspace work. The queue nevertheless lacks a single commercial completion sequence. Several boundaries have many small writers while other buyer-critical boundaries—trusted distribution, active playback, durable projects, support evidence, and licensed activation—have no complete protected vertical. + +A large number of individually plausible PRs can make delivery less reliable when: + +- several heads carry unrelated dependency/toolchain changes; +- adjacent UI slices create independent state or action stores; +- reviews and required checks are bound to predecessor heads; +- protected-base failures are copied into leaf PRs; +- a closed or squash-merged predecessor changes the successor's ancestry; +- design states are represented before runtime behavior exists; +- a validation package is described as a release without signing, notarization, updater, or rollback evidence. + +The saved Figma file is useful but also demonstrates the need for source parity. Its cover describes a 28-page design-system plan while the current file exposes two top-level pages. Its footer cites version `0.1.3`, whereas the reviewed repository and Tauri package metadata cite `0.1.0`. + +## Decision + +### 1. Preserve the product boundary + +BandScope remains a **local-first rehearsal decision tool**. + +It will: + +- analyze user-authorized local audio; +- explain evidence, confidence, and limitations; +- turn analysis into concrete rehearsal actions; +- provide an active, accessible passage-rehearsal player; +- preserve durable local projects and bounded handoffs; +- distribute trusted desktop artifacts and support evidence. + +It will not become a DAW, notation editor, plugin host, mandatory cloud account, or unbounded general-purpose media/file processor. + +### 2. Use one completion program + +Issue #958 is the parent product-completion program. The following child issues own independent buyer boundaries: + +- #960 — trusted release, updater, and rollback; +- #961 — active rehearsal player; +- #962 — project format, autosave, migration, and recovery; +- #963 — diagnostics and offline support bundle; +- #964 — licensed demo and first-run activation; +- #965 — Figma, Storybook, shipped UI, and WCAG parity; +- #966 — dependency-aware PR convergence. + +Existing canonical accuracy, resource, handoff, supply-chain, and platform issues remain authoritative where their responsibility already exists. + +### 3. Process work as dependency-aware merge trains + +Each open PR must belong to exactly one initial train: + +- `T0` dependency, toolchain, workflow, and quality base; +- `T1` local input, filesystem authority, resource admission, and cancellation; +- `T2` scientific accuracy, MIR evaluation, and numerical parity; +- `T3` rehearsal actions and active-player vertical; +- `T4` project portability, handoff, and interoperability; +- `T5` activation, UI system, Storybook, and accessibility; +- `T6` diagnostics, redaction, security evidence, and supportability; +- `T7` signed commercial release and updater. + +The initial routing is an investigation aid. Issue #966 must refresh the live exact head, checks, reviews, unresolved threads, dependencies, and succession before any action. + +### 4. Enforce one canonical writer per boundary + +A product/file boundary may have multiple active PRs only when their stack order is declared. Otherwise maintainers must select one canonical PR, transfer unique requirements/tests, and close duplicates or superseded heads with succession evidence. + +No feature PR may carry an unrelated dependency, lockfile, workflow, or toolchain baseline. Those changes belong to `T0` and are inherited only after protected integration. + +### 5. Treat exact-head evidence as non-transferable + +A PR may merge only when its unchanged exact current head has: + +- every required repository and central check in terminal success; +- qualifying independent non-author approval that covers the last push; +- zero unresolved actionable review threads; +- branch-protection acceptance without bypass; +- required repository-owned production statement and branch coverage; +- required public API documentation evidence. + +Queued, skipped-required, failed inherited-base, predecessor-head, protected-base, model-only, self/author, or administrative-bypass evidence is not success. + +### 6. Define design authority explicitly + +- Runtime behavior, semantics, localization keys, and version identity originate in the repository. +- Design tokens are versioned in code and synchronized to Figma variables. +- Storybook is the executable component/state inventory. +- Figma file `BP30foevuRtufwRpTknZUw` is the reviewed visual/interaction specification. +- The shipped Tauri application is the final acceptance target. +- A Figma or Storybook state that is not implemented must be marked proposed rather than complete. + +### 7. Release only after measurable product verticals + +A release candidate is not created merely because packaging succeeds. The protected release commit must carry: + +- real decoded-audio MIR acceptance and claim boundaries; +- bounded resource and cancellation evidence; +- active-player acceptance; +- project migration and recovery evidence; +- end-to-end accessibility evidence; +- support-bundle/privacy evidence; +- signed Windows artifacts; +- signed and notarized macOS artifacts; +- signature-verified updater and rollback evidence; +- checksums, SBOM, and build provenance. + +## Consequences + +### Positive + +- Buyers can evaluate a coherent rehearsal workflow rather than isolated features. +- Maintainers can make explicit succession decisions and reduce review drift. +- Scientific, accessibility, security, release, and design evidence are bound to the same product version. +- Dependency and workflow changes stop contaminating unrelated feature heads. +- Waiting for one external review lane does not stop independent productive work. + +### Costs + +- Some existing PRs will be restacked, reconstructed, superseded, or closed. +- Exact-head verification must be repeated after ancestry changes. +- The initial queue manifest requires continuing maintenance. +- Design work may be revised when it does not match implemented contracts. +- A 1.0 release is later than an unsigned or evidence-light package, but materially more trustworthy. + +## Rejected alternatives + +### Merge every open PR in numerical order + +Rejected because PR number is not dependency order, and merging all work can preserve duplicate stores, stale architecture, and conflicting dependency baselines. + +### Close most PRs solely to reduce the count + +Rejected because unique tests, security findings, and product requirements could be lost. Closure requires explicit succession evidence. + +### Treat the Figma file as the product source of truth + +Rejected because the current file contains planned/unimplemented pages and stale version metadata. Behavior and semantics must remain executable and testable in code. + +### Release unsigned artifacts and add trust later + +Rejected because signing, notarization, updater verification, project compatibility, and rollback are part of the commercial distribution contract. + +### Expand into a DAW + +Rejected because recording, plugin hosting, free-form mixing, and composition would dilute the buyer problem and multiply real-time/audio-platform risk. + +## Review triggers + +This ADR must be revisited when: + +- the product boundary changes beyond rehearsal decision/support; +- BandScope adds a mandatory cloud account or remote project storage; +- the Figma source-of-truth model changes; +- the PR queue no longer needs train-based governance; +- the application adopts a different desktop/runtime distribution architecture; +- a project format or updater change makes rollback guarantees materially different. From bd872d667a1dcc43ae0c59832ea8965b38a86f18 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 06:32:55 -0700 Subject: [PATCH 03/77] docs(doctoring): add product-readiness references --- .../doctoring/PRODUCT_READINESS_REFERENCES.md | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 docs/doctoring/PRODUCT_READINESS_REFERENCES.md diff --git a/docs/doctoring/PRODUCT_READINESS_REFERENCES.md b/docs/doctoring/PRODUCT_READINESS_REFERENCES.md new file mode 100644 index 000000000..1d0c3fa8d --- /dev/null +++ b/docs/doctoring/PRODUCT_READINESS_REFERENCES.md @@ -0,0 +1,48 @@ +# Product Readiness References + +This bibliography supports the BandScope 1.0 product-readiness baseline and related issues. Citations use APA 7th style where the source provides sufficient publication metadata. Product documentation pages are cited as organizational web resources and must be rechecked when their upstream version changes. + +## Desktop distribution and update + +Apple Inc. (n.d.). *Notarizing macOS software before distribution*. Apple Developer Documentation. https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution + +Microsoft. (n.d.). *Sign an app package using SignTool*. Microsoft Learn. https://learn.microsoft.com/windows/msix/package/sign-app-package-using-signtool + +Tauri Contributors. (n.d.). *Application signing*. Tauri. https://v2.tauri.app/distribute/sign/ + +Tauri Contributors. (n.d.). *Updater plugin*. Tauri. https://v2.tauri.app/plugin/updater/ + +## Accessibility and executable design evidence + +Storybook Contributors. (n.d.). *Accessibility testing*. Storybook. https://storybook.js.org/docs/writing-tests/accessibility-testing + +World Wide Web Consortium. (2023). *Web Content Accessibility Guidelines (WCAG) 2.2*. https://www.w3.org/TR/WCAG22/ + +World Wide Web Consortium. (2023). *Accessible Rich Internet Applications (WAI-ARIA) 1.2*. https://www.w3.org/TR/wai-aria-1.2/ + +## Music-information-retrieval evaluation + +Bittner, R. M., Salamon, J., Tierney, M., Mauch, M., Cannam, C., & Bello, J. P. (2014). MedleyDB: A multitrack dataset for annotation-intensive MIR research. In *Proceedings of the 15th International Society for Music Information Retrieval Conference* (pp. 155–160). https://medleydb.weebly.com/ + +MIREX. (n.d.). *Audio chord estimation*. Music Information Retrieval Evaluation eXchange. https://www.music-ir.org/mirex/wiki/2024:Audio_Chord_Estimation + +Raffel, C., McFee, B., Humphrey, E. J., Salamon, J., Nieto, O., Liang, D., & Ellis, D. P. W. (2014). mir_eval: A transparent implementation of common MIR metrics. In *Proceedings of the 15th International Society for Music Information Retrieval Conference* (pp. 367–372). https://craffel.github.io/mir_eval/ + +Rafii, Z., Liutkus, A., Stöter, F.-R., Mimilakis, S. I., & Bittner, R. (2019). MUSDB18-HQ—An uncompressed version of MUSDB18. *Zenodo*. https://doi.org/10.5281/zenodo.3338373 + +Stöter, F.-R., Liutkus, A., & Ito, N. (2018). The 2018 Signal Separation Evaluation Campaign. In E. Vincent, A. Yeredor, Z. Koldovský, & P. Tichavský (Eds.), *Latent Variable Analysis and Signal Separation* (pp. 293–305). Springer. https://doi.org/10.1007/978-3-319-93764-9_28 + +## Use in BandScope + +These sources do not by themselves prove BandScope conformance or accuracy. Each cited requirement must map to: + +```text +source requirement or evaluation method +→ BandScope product decision +→ owning issue and implementation +→ exact fixture and rights evidence +→ current-head test/build/release evidence +→ known limitation and supported claim +``` + +Official certification, notarization, code-signing, conformance, or benchmark claims may be made only after the corresponding external process and exact release artifact have been verified. From 999946498bb64ee56d1bc53ea42c44f378cd637d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 06:35:33 -0700 Subject: [PATCH 04/77] docs: add machine-readable open PR queue seed --- docs/product-readiness/open-pr-queue.json | 793 ++++++++++++++++++++++ 1 file changed, 793 insertions(+) create mode 100644 docs/product-readiness/open-pr-queue.json diff --git a/docs/product-readiness/open-pr-queue.json b/docs/product-readiness/open-pr-queue.json new file mode 100644 index 000000000..06806e24f --- /dev/null +++ b/docs/product-readiness/open-pr-queue.json @@ -0,0 +1,793 @@ +{ + "schema_version": "1.0.0", + "snapshot_date": "2026-08-20", + "timezone": "Asia/Seoul", + "repository": "ContextualWisdomLab/bandscope", + "base_branch": "develop", + "base_sha": "acdbea6344fe1231c39535b575f4de35e4c607c9", + "open_pr_count": 83, + "authority_note": "Initial read-only inventory. Refresh exact live head SHA, checks, reviews, unresolved threads, and dependencies before any merge or closure action.", + "trains": { + "T0": { + "description": "Dependency, toolchain, workflow and quality base", + "issue": 966 + }, + "T1": { + "description": "Local input, filesystem authority, resource admission and cancellation", + "issue": 781 + }, + "T2": { + "description": "Scientific accuracy, MIR evaluation and numerical parity", + "issue": 770 + }, + "T3": { + "description": "Rehearsal actions and active-player vertical", + "issue": 961 + }, + "T4": { + "description": "Project portability, handoff and interoperability", + "issue": 962 + }, + "T5": { + "description": "Activation, UI system, Storybook and accessibility", + "issue": 965 + }, + "T6": { + "description": "Diagnostics, redaction, security evidence and supportability", + "issue": 963 + }, + "T7": { + "description": "Signed commercial release and updater", + "issue": 960 + } + }, + "pull_requests": [ + { + "number": 731, + "title": "🎨 Palette: 향상된 접근성을 위해 아이콘 버튼에 툴팁 및 aria-disabled 적용", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/731", + "initial_train": "T5", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 732, + "title": "⚡ Chords: vectorize HMM reference and correct relative-key prior", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/732", + "initial_train": "T2", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 737, + "title": "feat(integration): add versioned naruon rehearsal handoff", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/737", + "initial_train": "T4", + "initial_disposition": "outbound_naruon_handoff_contract", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 740, + "title": "feat: import rehearsal handoffs for focused reanalysis", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/740", + "initial_train": "T4", + "initial_disposition": "inbound_handoff_reanalysis_slice", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 744, + "title": "feat(i18n): localize Workspace controls and harden interpolation", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/744", + "initial_train": "T5", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 746, + "title": "perf(segmenter): vectorize checkerboard novelty reference", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/746", + "initial_train": "T2", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 750, + "title": "fix(score): validate PDF bridge byte arrays without coercion", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/750", + "initial_train": "T1", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 754, + "title": "build(deps): bump time from 0.3.53 to 0.3.55 in /apps/desktop/src-tauri", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/754", + "initial_train": "T0", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 776, + "title": "feat(chords): surface actionable rehearsal guidance", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/776", + "initial_train": "T3", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 780, + "title": "fix(security): keep every CodeQL Action phase on one revision", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/780", + "initial_train": "T0", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 783, + "title": "fix(security): establish canonical npm, PDF.js, Nanoid, and Undici baseline", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/783", + "initial_train": "T0", + "initial_disposition": "canonical_dependency_security_base", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 811, + "title": "🔒 [보안] CLI 무제한 파일 읽기 취약점 수정", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/811", + "initial_train": "T1", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 824, + "title": "🧪 테스트: 줄기 분리 시간 초과 처리 테스트 추가", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/824", + "initial_train": "T1", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 826, + "title": "🧪 [테스트 개선] TemporalAnalyzer 분석 오류 테스트 추가", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/826", + "initial_train": "T6", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 827, + "title": "fix(desktop): remove synthetic browser analysis success", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/827", + "initial_train": "T2", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 828, + "title": "test(analysis): govern real YouTube known-stem benchmark", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/828", + "initial_train": "T2", + "initial_disposition": "real_audio_accuracy_slice_under_issue_770", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 833, + "title": "🎨 Palette: 코드 수정 버튼에 접근성 있는 툴팁 지원 추가", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/833", + "initial_train": "T5", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 834, + "title": "⚡ Bolt: O(1) 메모리로 순차적 코드 변경 횟수 계산 최적화", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/834", + "initial_train": "T2", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 845, + "title": "feat(analysis): CSV 큐시트 내보내기 기능 및 수식 주입 보안 로직 추가", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/845", + "initial_train": "T3", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 849, + "title": "⚡ Bolt: 차트 내보내기(chart.py)의 중복 제거 로직 O(N^2)에서 O(N)으로 최적화", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/849", + "initial_train": "T5", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 850, + "title": "feat: add Part Handoff Map UI component to workspace", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/850", + "initial_train": "T3", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 858, + "title": "fix(security): bind analysis paths to filesystem authority", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/858", + "initial_train": "T1", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 859, + "title": "⚡ Bolt: GrooveMap의 maxTime 계산 성능 개선", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/859", + "initial_train": "T5", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 861, + "title": "test(quality): enforce Python branch coverage", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/861", + "initial_train": "T0", + "initial_disposition": "canonical_python_branch_coverage_base", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 865, + "title": "fix(score): bound native PDF reads before allocation", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/865", + "initial_train": "T1", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 866, + "title": "fix(audio): establish canonical local-audio resource policy", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/866", + "initial_train": "T1", + "initial_disposition": "canonical_local_audio_resource_policy", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 867, + "title": "test(supply-chain): preserve simple dependency-path cycle semantics", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/867", + "initial_train": "T0", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 873, + "title": "feat(roles): wire measured register overlap into section warnings", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/873", + "initial_train": "T2", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 874, + "title": "feat(workspace): name the parts to lock in first", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/874", + "initial_train": "T3", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 881, + "title": "test(ci): lock local OpenCode small_model to NVIDIA NIM", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/881", + "initial_train": "T0", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 884, + "title": "feat(workspace): open Stem Lab as honest isolation lanes", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/884", + "initial_train": "T3", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 891, + "title": "test(analysis): lock verse/chorus chord recovery on a known take", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/891", + "initial_train": "T2", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 892, + "title": "fix(analysis): score C major acceptance from decoded WAV bytes", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/892", + "initial_train": "T2", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 894, + "title": "fix(security): drop persisted credentials before dependency lifecycle code", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/894", + "initial_train": "T0", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 895, + "title": "fix(workflows): audit orphaned Actions registry identities", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/895", + "initial_train": "T0", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 896, + "title": "build(node): coordinate Node 22.22.2 floor with jsdom 30", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/896", + "initial_train": "T0", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 897, + "title": "feat(workspace): Storybook tokens for rehearsal components", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/897", + "initial_train": "T5", + "initial_disposition": "storybook_design_inventory_slice", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 898, + "title": "feat(workspace): put the next rehearsal action on empty and error cards", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/898", + "initial_train": "T5", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 899, + "title": "feat(workspace): open existing rehearsal surfaces from the sidebar", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/899", + "initial_train": "T5", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 900, + "title": "feat(workspace): name tonight's export and priority actions", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/900", + "initial_train": "T3", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 901, + "title": "feat(workspace): start tonight's first part from the ready board", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/901", + "initial_train": "T3", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 903, + "title": "feat(workspace): loop tonight's first section on the map", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/903", + "initial_train": "T3", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 904, + "title": "feat(workspace): count in tonight's first section on the map", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/904", + "initial_train": "T3", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 905, + "title": "feat(workspace): open tonight's first notes on the groove map", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/905", + "initial_train": "T3", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 907, + "title": "chore(env): add Cloud Agent environment config", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/907", + "initial_train": "T0", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 910, + "title": "feat(workspace): set up tonight's part before the first entrance", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/910", + "initial_train": "T3", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 912, + "title": "feat(workspace): guide tonight's first entrance on map and player", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/912", + "initial_train": "T3", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 913, + "title": "feat(workspace): guide tonight's first lyric cue on map and player", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/913", + "initial_train": "T3", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 914, + "title": "feat(workspace): guide tonight's first dropout on map and player", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/914", + "initial_train": "T3", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 916, + "title": "feat(workspace): guide tonight's first pickup on map and player", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/916", + "initial_train": "T3", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 918, + "title": "build(deps): bump uuid from 1.23.4 to 1.24.1 in /apps/desktop/src-tauri", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/918", + "initial_train": "T0", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 919, + "title": "build(deps): update numba requirement from <0.67.0 to <0.68.0 in /services/analysis-engine", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/919", + "initial_train": "T0", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 920, + "title": "build(deps): bump react and @types/react", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/920", + "initial_train": "T0", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 921, + "title": "build(deps): bump @base-ui/react from 1.5.0 to 1.7.0", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/921", + "initial_train": "T0", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 922, + "title": "build(deps-dev): bump storybook from 10.4.6 to 10.5.8", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/922", + "initial_train": "T0", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 923, + "title": "build(deps-dev): bump @storybook/react-vite from 10.4.6 to 10.5.8", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/923", + "initial_train": "T0", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 924, + "title": "build(deps): bump github/codeql-action/init from 4.37.0 to 4.37.7", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/924", + "initial_train": "T0", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 925, + "title": "build(deps-dev): bump typescript-eslint from 8.63.0 to 8.67.0", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/925", + "initial_train": "T0", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 926, + "title": "build(deps): bump lucide-react from 1.24.0 to 1.31.0", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/926", + "initial_train": "T0", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 927, + "title": "build(deps): bump sonner from 2.0.7 to 2.0.8", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/927", + "initial_train": "T0", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 928, + "title": "build(deps-dev): bump eslint-plugin-jsdoc from 63.0.13 to 64.2.0", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/928", + "initial_train": "T0", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 929, + "title": "build(deps-dev): bump @testing-library/jest-dom from 6.9.1 to 7.0.1", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/929", + "initial_train": "T0", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 930, + "title": "build(deps): bump github/codeql-action/autobuild from 4.37.0 to 4.37.7", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/930", + "initial_train": "T0", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 931, + "title": "build(deps): bump astral-sh/setup-uv from 8.3.2 to 10.0.1", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/931", + "initial_train": "T0", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 932, + "title": "build(deps): bump github/codeql-action/analyze from 4.37.0 to 4.37.7", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/932", + "initial_train": "T0", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 933, + "title": "build(deps): bump github/codeql-action/upload-sarif from 4.37.0 to 4.37.7", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/933", + "initial_train": "T0", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 934, + "title": "feat(workspace): guide tonight's first stop on map and player", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/934", + "initial_train": "T3", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 936, + "title": "build(deps): bump the uv group across 1 directory with 2 updates", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/936", + "initial_train": "T0", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 937, + "title": "feat(workspace): guide tonight's first labeled handoff on map and player", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/937", + "initial_train": "T3", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 939, + "title": "feat(workspace): guide tonight's first chorus on map and player", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/939", + "initial_train": "T3", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 941, + "title": "🛡️ Sentinel: CSV 수식 주입 NUL·전각 연산자 우회 차단", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/941", + "initial_train": "T6", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 942, + "title": "build(deps): bump react-dom and @types/react-dom", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/942", + "initial_train": "T0", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 943, + "title": "feat(workspace): guide tonight's first intro on map and player", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/943", + "initial_train": "T3", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 944, + "title": "build(rust): pin all product and release lanes to 1.97.1", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/944", + "initial_train": "T0", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 946, + "title": "feat(workspace): guide tonight's first bridge on map and player", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/946", + "initial_train": "T3", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 947, + "title": "feat(workspace): guide tonight's first verse on map and player", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/947", + "initial_train": "T3", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 948, + "title": "fix(security): retire quick-xml RustSec exceptions", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/948", + "initial_train": "T0", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 949, + "title": "fix(security): redact key detector dependency failures", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/949", + "initial_train": "T6", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 950, + "title": "fix(security): redact temporal detector failure logs", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/950", + "initial_train": "T6", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 951, + "title": "fix(security): redact range-analysis failure logs", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/951", + "initial_train": "T6", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 955, + "title": "feat(workspace): guide tonight's first pre-chorus on map and player", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/955", + "initial_train": "T3", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 956, + "title": "fix(security): redact articulation failure logs", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/956", + "initial_train": "T6", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + }, + { + "number": 957, + "title": "feat(workspace): name tonight's first playable range on the map", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/957", + "initial_train": "T3", + "initial_disposition": "triage_required", + "head_sha": null, + "head_sha_status": "refresh_required_before_action" + } + ] +} From f3afdc0ceb44d7d73966ed4eeb38a06c947784e4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 11:09:03 -0700 Subject: [PATCH 05/77] test(readiness): reject invalid open PR queue manifests --- .../tests/test_open_pr_queue_manifest.py | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 services/analysis-engine/tests/test_open_pr_queue_manifest.py diff --git a/services/analysis-engine/tests/test_open_pr_queue_manifest.py b/services/analysis-engine/tests/test_open_pr_queue_manifest.py new file mode 100644 index 000000000..62778694e --- /dev/null +++ b/services/analysis-engine/tests/test_open_pr_queue_manifest.py @@ -0,0 +1,110 @@ +"""Regression tests for the BandScope open pull-request queue manifest contract.""" + +from __future__ import annotations + +import importlib.util +from pathlib import Path +from types import ModuleType + +import pytest + +REPO_ROOT = Path(__file__).resolve().parents[3] +VERIFIER_PATH = REPO_ROOT / "scripts" / "checks" / "verify_open_pr_queue.py" +QUICKCHECK_PATH = REPO_ROOT / "scripts" / "harness" / "quickcheck.sh" + + +def _load_verifier() -> ModuleType: + """Load the repository verifier without requiring scripts to be a Python package.""" + spec = importlib.util.spec_from_file_location("verify_open_pr_queue", VERIFIER_PATH) + assert spec is not None and spec.loader is not None + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +def _valid_manifest() -> dict[str, object]: + """Return the smallest representative queue manifest accepted by the contract.""" + return { + "schema_version": "1.0.0", + "snapshot_date": "2026-08-20", + "timezone": "Asia/Seoul", + "repository": "ContextualWisdomLab/bandscope", + "base_branch": "develop", + "base_sha": "a" * 40, + "open_pr_count": 2, + "authority_note": "Refresh exact live evidence before action.", + "trains": { + "T0": {"description": "Dependency base", "issue": 966}, + "T6": {"description": "Diagnostics", "issue": 963}, + }, + "pull_requests": [ + { + "number": 783, + "title": "dependency baseline", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/783", + "initial_train": "T0", + "initial_disposition": "canonical_dependency_security_base", + "head_sha": None, + "head_sha_status": "refresh_required_before_action", + }, + { + "number": 967, + "title": "support manifest", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/967", + "initial_train": "T6", + "initial_disposition": "triage_required", + "head_sha": "b" * 40, + "head_sha_status": "exact_current_head", + }, + ], + } + + +def test_open_pr_queue_manifest_accepts_a_well_formed_seed() -> None: + """A structurally consistent seed is accepted without network access.""" + verifier = _load_verifier() + verifier.validate_manifest(_valid_manifest()) + + +@pytest.mark.parametrize( + ("mutate", "expected"), + [ + (lambda manifest: manifest.update(open_pr_count=3), "open_pr_count"), + ( + lambda manifest: manifest["pull_requests"].append( + dict(manifest["pull_requests"][0]) + ), + "duplicate pull request number", + ), + ( + lambda manifest: manifest["pull_requests"][0].update(initial_train="T99"), + "unknown train", + ), + ( + lambda manifest: manifest["pull_requests"][0].update( + head_sha=None, head_sha_status="exact_current_head" + ), + "refresh_required_before_action", + ), + ( + lambda manifest: manifest["pull_requests"][1].update( + head_sha="not-a-commit", head_sha_status="exact_current_head" + ), + "40 hexadecimal", + ), + ], +) +def test_open_pr_queue_manifest_fails_closed_on_corrupt_evidence(mutate, expected: str) -> None: + """Count, identity, train, and exact-head evidence drift must fail closed.""" + verifier = _load_verifier() + manifest = _valid_manifest() + mutate(manifest) + + with pytest.raises(verifier.ManifestError, match=expected): + verifier.validate_manifest(manifest) + + +def test_quickcheck_executes_open_pr_queue_verifier() -> None: + """The repository harness must execute the queue contract on every normal quickcheck.""" + quickcheck = QUICKCHECK_PATH.read_text(encoding="utf-8") + assert "python3 scripts/checks/verify_open_pr_queue.py" in quickcheck From 5d86aa805b2fb3ec8e29dbf86ae681897a33b1d0 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 11:09:54 -0700 Subject: [PATCH 06/77] feat(readiness): validate open PR queue manifest --- scripts/checks/verify_open_pr_queue.py | 154 +++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 scripts/checks/verify_open_pr_queue.py diff --git a/scripts/checks/verify_open_pr_queue.py b/scripts/checks/verify_open_pr_queue.py new file mode 100644 index 000000000..b444c128e --- /dev/null +++ b/scripts/checks/verify_open_pr_queue.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python3 +"""Fail closed when the BandScope product-readiness pull-request queue is inconsistent.""" + +from __future__ import annotations + +import json +import re +import sys +from pathlib import Path +from typing import Any + +REPO_ROOT = Path(__file__).resolve().parents[2] +DEFAULT_MANIFEST_PATH = REPO_ROOT / "docs" / "product-readiness" / "open-pr-queue.json" +REPOSITORY = "ContextualWisdomLab/bandscope" +BASE_BRANCH = "develop" +SHA_PATTERN = re.compile(r"^[0-9a-f]{40}$", re.IGNORECASE) + + +class ManifestError(ValueError): + """Raised when queue evidence is structurally inconsistent or ambiguous.""" + + +def _fail(message: str) -> None: + """Raise the stable manifest-validation exception.""" + raise ManifestError(message) + + +def _require_record(value: object, field: str) -> dict[str, Any]: + """Return a JSON object or fail with a field-specific diagnostic.""" + if not isinstance(value, dict): + _fail(f"{field} must be an object") + return value + + +def _require_list(value: object, field: str) -> list[Any]: + """Return a JSON array or fail with a field-specific diagnostic.""" + if not isinstance(value, list): + _fail(f"{field} must be an array") + return value + + +def _require_non_empty_string(value: object, field: str) -> str: + """Return a non-empty string without coercing queue evidence.""" + if not isinstance(value, str) or not value.strip() or value != value.strip(): + _fail(f"{field} must be a non-empty trim-stable string") + return value + + +def _require_sha(value: object, field: str) -> str: + """Return an immutable 40-hex Git commit identity.""" + text = _require_non_empty_string(value, field) + if SHA_PATTERN.fullmatch(text) is None: + _fail(f"{field} must be 40 hexadecimal characters") + return text.lower() + + +def validate_manifest(manifest: object) -> None: + """Validate intrinsic queue invariants without treating the seed as live GitHub evidence.""" + root = _require_record(manifest, "manifest") + if root.get("schema_version") != "1.0.0": + _fail("schema_version must be 1.0.0") + if root.get("repository") != REPOSITORY: + _fail(f"repository must be {REPOSITORY}") + if root.get("base_branch") != BASE_BRANCH: + _fail(f"base_branch must be {BASE_BRANCH}") + _require_sha(root.get("base_sha"), "base_sha") + _require_non_empty_string(root.get("snapshot_date"), "snapshot_date") + _require_non_empty_string(root.get("timezone"), "timezone") + _require_non_empty_string(root.get("authority_note"), "authority_note") + + trains = _require_record(root.get("trains"), "trains") + if not trains: + _fail("trains must not be empty") + for train_name, raw_train in trains.items(): + _require_non_empty_string(train_name, "train name") + train = _require_record(raw_train, f"trains.{train_name}") + _require_non_empty_string(train.get("description"), f"trains.{train_name}.description") + issue = train.get("issue") + if isinstance(issue, bool) or not isinstance(issue, int) or issue <= 0: + _fail(f"trains.{train_name}.issue must be a positive integer") + + pull_requests = _require_list(root.get("pull_requests"), "pull_requests") + open_pr_count = root.get("open_pr_count") + if isinstance(open_pr_count, bool) or not isinstance(open_pr_count, int): + _fail("open_pr_count must be an integer") + if open_pr_count != len(pull_requests): + _fail( + f"open_pr_count must equal pull_requests length: {open_pr_count} != {len(pull_requests)}" + ) + + seen_numbers: set[int] = set() + for index, raw_pr in enumerate(pull_requests): + prefix = f"pull_requests[{index}]" + pr = _require_record(raw_pr, prefix) + number = pr.get("number") + if isinstance(number, bool) or not isinstance(number, int) or number <= 0: + _fail(f"{prefix}.number must be a positive integer") + if number in seen_numbers: + _fail(f"duplicate pull request number: {number}") + seen_numbers.add(number) + + _require_non_empty_string(pr.get("title"), f"{prefix}.title") + expected_url = f"https://github.com/{REPOSITORY}/pull/{number}" + if pr.get("url") != expected_url: + _fail(f"{prefix}.url must be {expected_url}") + + train_name = _require_non_empty_string(pr.get("initial_train"), f"{prefix}.initial_train") + if train_name not in trains: + _fail(f"{prefix}.initial_train references unknown train: {train_name}") + _require_non_empty_string(pr.get("initial_disposition"), f"{prefix}.initial_disposition") + + head_sha = pr.get("head_sha") + head_status = pr.get("head_sha_status") + if head_sha is None: + if head_status != "refresh_required_before_action": + _fail( + f"{prefix}.head_sha_status must be refresh_required_before_action when head_sha is null" + ) + else: + _require_sha(head_sha, f"{prefix}.head_sha") + if head_status != "exact_current_head": + _fail(f"{prefix}.head_sha_status must be exact_current_head when head_sha is present") + + +def load_manifest(path: Path = DEFAULT_MANIFEST_PATH) -> object: + """Load the queue JSON without accepting duplicate object keys silently.""" + + def reject_duplicate_keys(pairs: list[tuple[str, object]]) -> dict[str, object]: + result: dict[str, object] = {} + for key, value in pairs: + if key in result: + _fail(f"duplicate JSON object key: {key}") + result[key] = value + return result + + try: + return json.loads(path.read_text(encoding="utf-8"), object_pairs_hook=reject_duplicate_keys) + except (OSError, UnicodeError, json.JSONDecodeError) as exc: + raise ManifestError(f"unable to read open PR queue manifest: {type(exc).__name__}") from exc + + +def main() -> int: + """Validate the committed seed and return a shell-friendly status code.""" + try: + validate_manifest(load_manifest()) + except ManifestError as exc: + print(f"open PR queue verification failed: {exc}", file=sys.stderr) + return 1 + print("open PR queue verification passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 3e6b00b5f896e70a713769666778c92ad63d92cf Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 11:10:07 -0700 Subject: [PATCH 07/77] feat(readiness): enforce PR queue contract in quickcheck --- scripts/harness/quickcheck.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/harness/quickcheck.sh b/scripts/harness/quickcheck.sh index f2b87e4e8..acfbd77bc 100755 --- a/scripts/harness/quickcheck.sh +++ b/scripts/harness/quickcheck.sh @@ -5,6 +5,7 @@ REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" cd "$REPO_ROOT" python3 scripts/checks/verify_docs.py +python3 scripts/checks/verify_open_pr_queue.py python3 scripts/checks/verify_security_notes.py python3 scripts/checks/security_gates.py python3 scripts/checks/verify_supply_chain.py From d3604aba1945f929f01c08ad72b28a32bd0f7e78 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 11:12:04 -0700 Subject: [PATCH 08/77] test(readiness): isolate duplicate PR regression --- .../tests/test_open_pr_queue_manifest.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/services/analysis-engine/tests/test_open_pr_queue_manifest.py b/services/analysis-engine/tests/test_open_pr_queue_manifest.py index 62778694e..f6434129c 100644 --- a/services/analysis-engine/tests/test_open_pr_queue_manifest.py +++ b/services/analysis-engine/tests/test_open_pr_queue_manifest.py @@ -60,6 +60,14 @@ def _valid_manifest() -> dict[str, object]: } +def _append_duplicate_pr(manifest: dict[str, object]) -> None: + """Duplicate one PR while keeping the declared count internally consistent.""" + pull_requests = manifest["pull_requests"] + assert isinstance(pull_requests, list) + pull_requests.append(dict(pull_requests[0])) + manifest["open_pr_count"] = len(pull_requests) + + def test_open_pr_queue_manifest_accepts_a_well_formed_seed() -> None: """A structurally consistent seed is accepted without network access.""" verifier = _load_verifier() @@ -70,12 +78,7 @@ def test_open_pr_queue_manifest_accepts_a_well_formed_seed() -> None: ("mutate", "expected"), [ (lambda manifest: manifest.update(open_pr_count=3), "open_pr_count"), - ( - lambda manifest: manifest["pull_requests"].append( - dict(manifest["pull_requests"][0]) - ), - "duplicate pull request number", - ), + (_append_duplicate_pr, "duplicate pull request number"), ( lambda manifest: manifest["pull_requests"][0].update(initial_train="T99"), "unknown train", From d9e6dba69da09b02911c9b50c63999a553ad5c0d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 17:11:29 -0700 Subject: [PATCH 09/77] test(readiness): reject unsupported queue evidence --- .../tests/test_open_pr_queue_manifest.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/services/analysis-engine/tests/test_open_pr_queue_manifest.py b/services/analysis-engine/tests/test_open_pr_queue_manifest.py index f6434129c..72d147da0 100644 --- a/services/analysis-engine/tests/test_open_pr_queue_manifest.py +++ b/services/analysis-engine/tests/test_open_pr_queue_manifest.py @@ -107,6 +107,30 @@ def test_open_pr_queue_manifest_fails_closed_on_corrupt_evidence(mutate, expecte verifier.validate_manifest(manifest) +@pytest.mark.parametrize( + ("mutate", "expected"), + [ + (lambda manifest: manifest.update(merge_ready=True), "manifest has unsupported field"), + ( + lambda manifest: manifest["trains"]["T0"].update(owner="dependency-team"), + "trains.T0 has unsupported field", + ), + ( + lambda manifest: manifest["pull_requests"][0].update(merge_ready=True), + r"pull_requests\[0\] has unsupported field", + ), + ], +) +def test_open_pr_queue_manifest_rejects_unsupported_evidence_fields(mutate, expected: str) -> None: + """Unreviewed fields must not smuggle unsupported success or ownership evidence into the queue.""" + verifier = _load_verifier() + manifest = _valid_manifest() + mutate(manifest) + + with pytest.raises(verifier.ManifestError, match=expected): + verifier.validate_manifest(manifest) + + def test_quickcheck_executes_open_pr_queue_verifier() -> None: """The repository harness must execute the queue contract on every normal quickcheck.""" quickcheck = QUICKCHECK_PATH.read_text(encoding="utf-8") From 21d87ff98d9cb3d3a56e519c5de82bf8515bd22f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 17:12:32 -0700 Subject: [PATCH 10/77] fix(readiness): reject unsupported queue evidence --- scripts/checks/verify_open_pr_queue.py | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/scripts/checks/verify_open_pr_queue.py b/scripts/checks/verify_open_pr_queue.py index b444c128e..c7d7770aa 100644 --- a/scripts/checks/verify_open_pr_queue.py +++ b/scripts/checks/verify_open_pr_queue.py @@ -14,6 +14,32 @@ REPOSITORY = "ContextualWisdomLab/bandscope" BASE_BRANCH = "develop" SHA_PATTERN = re.compile(r"^[0-9a-f]{40}$", re.IGNORECASE) +ROOT_FIELDS = frozenset( + { + "schema_version", + "snapshot_date", + "timezone", + "repository", + "base_branch", + "base_sha", + "open_pr_count", + "authority_note", + "trains", + "pull_requests", + } +) +TRAIN_FIELDS = frozenset({"description", "issue"}) +PULL_REQUEST_FIELDS = frozenset( + { + "number", + "title", + "url", + "initial_train", + "initial_disposition", + "head_sha", + "head_sha_status", + } +) class ManifestError(ValueError): @@ -32,6 +58,13 @@ def _require_record(value: object, field: str) -> dict[str, Any]: return value +def _reject_unknown_fields(record: dict[str, Any], allowed: frozenset[str], field: str) -> None: + """Reject evidence fields that are not part of the reviewed manifest schema.""" + unsupported = sorted(set(record) - allowed) + if unsupported: + _fail(f"{field} has unsupported field: {unsupported[0]}") + + def _require_list(value: object, field: str) -> list[Any]: """Return a JSON array or fail with a field-specific diagnostic.""" if not isinstance(value, list): @@ -57,6 +90,7 @@ def _require_sha(value: object, field: str) -> str: def validate_manifest(manifest: object) -> None: """Validate intrinsic queue invariants without treating the seed as live GitHub evidence.""" root = _require_record(manifest, "manifest") + _reject_unknown_fields(root, ROOT_FIELDS, "manifest") if root.get("schema_version") != "1.0.0": _fail("schema_version must be 1.0.0") if root.get("repository") != REPOSITORY: @@ -74,6 +108,7 @@ def validate_manifest(manifest: object) -> None: for train_name, raw_train in trains.items(): _require_non_empty_string(train_name, "train name") train = _require_record(raw_train, f"trains.{train_name}") + _reject_unknown_fields(train, TRAIN_FIELDS, f"trains.{train_name}") _require_non_empty_string(train.get("description"), f"trains.{train_name}.description") issue = train.get("issue") if isinstance(issue, bool) or not isinstance(issue, int) or issue <= 0: @@ -92,6 +127,7 @@ def validate_manifest(manifest: object) -> None: for index, raw_pr in enumerate(pull_requests): prefix = f"pull_requests[{index}]" pr = _require_record(raw_pr, prefix) + _reject_unknown_fields(pr, PULL_REQUEST_FIELDS, prefix) number = pr.get("number") if isinstance(number, bool) or not isinstance(number, int) or number <= 0: _fail(f"{prefix}.number must be a positive integer") From 6456d325ac32d41f11670ee37fb216e28701e40c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 20:39:07 -0700 Subject: [PATCH 11/77] test(readiness): wrap queue evidence docstring --- services/analysis-engine/tests/test_open_pr_queue_manifest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/analysis-engine/tests/test_open_pr_queue_manifest.py b/services/analysis-engine/tests/test_open_pr_queue_manifest.py index 72d147da0..689a3b9e2 100644 --- a/services/analysis-engine/tests/test_open_pr_queue_manifest.py +++ b/services/analysis-engine/tests/test_open_pr_queue_manifest.py @@ -122,7 +122,7 @@ def test_open_pr_queue_manifest_fails_closed_on_corrupt_evidence(mutate, expecte ], ) def test_open_pr_queue_manifest_rejects_unsupported_evidence_fields(mutate, expected: str) -> None: - """Unreviewed fields must not smuggle unsupported success or ownership evidence into the queue.""" + """Reject fields that could smuggle unsupported success or ownership evidence.""" verifier = _load_verifier() manifest = _valid_manifest() mutate(manifest) From d5b8793435077dca20f25c6d3102048d703940bb Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 23 Aug 2026 10:05:45 -0700 Subject: [PATCH 12/77] test(readiness): require fail-closed live PR queue refresh --- .../tests/test_open_pr_queue_refresh.py | 166 ++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 services/analysis-engine/tests/test_open_pr_queue_refresh.py diff --git a/services/analysis-engine/tests/test_open_pr_queue_refresh.py b/services/analysis-engine/tests/test_open_pr_queue_refresh.py new file mode 100644 index 000000000..5c0eecf07 --- /dev/null +++ b/services/analysis-engine/tests/test_open_pr_queue_refresh.py @@ -0,0 +1,166 @@ +"""Regression tests for live BandScope open pull-request queue refresh.""" + +from __future__ import annotations + +import importlib.util +import sys +from pathlib import Path +from types import ModuleType + +import pytest + +REPO_ROOT = Path(__file__).resolve().parents[3] +REFRESHER_PATH = REPO_ROOT / "scripts" / "checks" / "refresh_open_pr_queue.py" + + +def _load_refresher() -> ModuleType: + """Load the operator refresher without requiring scripts to be a Python package.""" + sys.path.insert(0, str(REFRESHER_PATH.parent)) + try: + spec = importlib.util.spec_from_file_location("refresh_open_pr_queue", REFRESHER_PATH) + assert spec is not None and spec.loader is not None + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + finally: + sys.path.pop(0) + + +def _seed() -> dict[str, object]: + """Return the smallest representative queue seed used by live refresh tests.""" + return { + "schema_version": "1.0.0", + "snapshot_date": "2026-08-20", + "timezone": "Asia/Seoul", + "repository": "ContextualWisdomLab/bandscope", + "base_branch": "develop", + "base_sha": "a" * 40, + "open_pr_count": 1, + "authority_note": "Refresh exact live evidence before action.", + "trains": {"T0": {"description": "Dependency base", "issue": 966}}, + "pull_requests": [ + { + "number": 783, + "title": "old title", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/783", + "initial_train": "T0", + "initial_disposition": "canonical_dependency_security_base", + "head_sha": None, + "head_sha_status": "refresh_required_before_action", + } + ], + } + + +def _live_pr( + number: int, + head_sha: str, + *, + base_ref: str = "develop", + title: str | None = None, +) -> dict[str, object]: + """Return a minimal GitHub pulls-API record for one open PR.""" + return { + "number": number, + "title": title or f"PR {number}", + "html_url": f"https://github.com/ContextualWisdomLab/bandscope/pull/{number}", + "state": "open", + "base": {"ref": base_ref}, + "head": {"sha": head_sha}, + } + + +def test_collect_paginated_pulls_consumes_every_announced_page() -> None: + """The collector follows every announced next page before declaring completeness.""" + refresher = _load_refresher() + pages = { + 1: ([_live_pr(1, "1" * 40), _live_pr(2, "2" * 40)], True), + 2: ([_live_pr(3, "3" * 40)], False), + } + + result = refresher.collect_paginated_pulls( + lambda page, size: pages[page], page_size=2, max_pages=3 + ) + + assert [item["number"] for item in result["pull_requests"]] == [1, 2, 3] + assert result["incomplete_results"] is False + + +def test_collect_paginated_pulls_fails_closed_when_bound_would_truncate() -> None: + """A pagination bound must fail instead of publishing a success-shaped partial queue.""" + refresher = _load_refresher() + + with pytest.raises(refresher.RefreshError, match="pagination bound"): + refresher.collect_paginated_pulls( + lambda page, size: ([_live_pr(page, f"{page:x}".rjust(40, "0"))], True), + page_size=1, + max_pages=2, + ) + + +def test_refresh_manifest_updates_exact_heads_and_adds_untriaged_live_prs() -> None: + """Live refresh preserves reviewed routing while recording every current open PR head.""" + refresher = _load_refresher() + live = { + "incomplete_results": False, + "pull_requests": [ + _live_pr(783, "b" * 40, title="current dependency title"), + _live_pr(1002, "c" * 40), + ], + } + + refreshed = refresher.build_refreshed_manifest( + _seed(), live, base_sha="d" * 40, snapshot_date="2026-08-24" + ) + + assert refreshed["open_pr_count"] == 2 + assert refreshed["base_sha"] == "d" * 40 + assert refreshed["snapshot_date"] == "2026-08-24" + assert refreshed["pull_requests"][0]["number"] == 783 + assert refreshed["pull_requests"][0]["title"] == "current dependency title" + assert refreshed["pull_requests"][0]["head_sha"] == "b" * 40 + assert refreshed["pull_requests"][0]["head_sha_status"] == "exact_current_head" + assert ( + refreshed["pull_requests"][0]["initial_disposition"] + == "canonical_dependency_security_base" + ) + assert refreshed["pull_requests"][1]["number"] == 1002 + assert refreshed["pull_requests"][1]["initial_train"] == "T8" + assert refreshed["pull_requests"][1]["initial_disposition"] == "triage_required" + assert refreshed["trains"]["T8"]["issue"] == 966 + + +@pytest.mark.parametrize( + ("live", "expected"), + [ + ({"incomplete_results": True, "pull_requests": []}, "incomplete"), + ( + { + "incomplete_results": False, + "pull_requests": [_live_pr(783, "b" * 40), _live_pr(783, "c" * 40)], + }, + "duplicate pull request number", + ), + ( + {"incomplete_results": False, "pull_requests": [_live_pr(783, "not-a-sha")]}, + "head.sha", + ), + ( + { + "incomplete_results": False, + "pull_requests": [_live_pr(783, "b" * 40, base_ref="main")], + }, + "base.ref", + ), + ], +) +def test_refresh_manifest_rejects_untrustworthy_live_inventory( + live: dict[str, object], expected: str +) -> None: + """Incomplete, duplicate, malformed, or wrong-base live evidence fails closed.""" + refresher = _load_refresher() + + with pytest.raises(refresher.RefreshError, match=expected): + refresher.build_refreshed_manifest( + _seed(), live, base_sha="d" * 40, snapshot_date="2026-08-24" + ) From 6d559b6ae53de862774eca74d6e6b98c6fe2f393 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 23 Aug 2026 10:07:39 -0700 Subject: [PATCH 13/77] feat(readiness): add bounded live PR queue refresher --- scripts/checks/refresh_open_pr_queue.py | 325 ++++++++++++++++++++++++ 1 file changed, 325 insertions(+) create mode 100644 scripts/checks/refresh_open_pr_queue.py diff --git a/scripts/checks/refresh_open_pr_queue.py b/scripts/checks/refresh_open_pr_queue.py new file mode 100644 index 000000000..4bbfaa468 --- /dev/null +++ b/scripts/checks/refresh_open_pr_queue.py @@ -0,0 +1,325 @@ +#!/usr/bin/env python3 +"""Refresh the BandScope open-PR readiness manifest from bounded live GitHub evidence.""" + +from __future__ import annotations + +import json +import os +import re +import sys +import urllib.error +import urllib.parse +import urllib.request +from collections.abc import Callable +from copy import deepcopy +from datetime import datetime +from pathlib import Path +from typing import Any +from zoneinfo import ZoneInfo + +from verify_open_pr_queue import ManifestError, load_manifest, validate_manifest + +REPO_ROOT = Path(__file__).resolve().parents[2] +MANIFEST_PATH = REPO_ROOT / "docs" / "product-readiness" / "open-pr-queue.json" +REPOSITORY = "ContextualWisdomLab/bandscope" +BASE_BRANCH = "develop" +GITHUB_API_ORIGIN = "https://api.github.com" +PAGE_SIZE = 100 +MAX_PAGES = 10 +UNTRIAGED_TRAIN = "T8" +UNTRIAGED_DESCRIPTION = "Live additions awaiting explicit merge-train triage" +UNTRIAGED_ISSUE = 966 +SHA_PATTERN = re.compile(r"^[0-9a-f]{40}$", re.IGNORECASE) +PageFetcher = Callable[[int, int], tuple[list[dict[str, Any]], bool]] + + +class RefreshError(ValueError): + """Raised when live queue evidence is incomplete, malformed, or ambiguous.""" + + +def _fail(message: str) -> None: + """Raise the stable live-refresh exception.""" + raise RefreshError(message) + + +def _require_record(value: object, field: str) -> dict[str, Any]: + """Return a mapping or fail with a field-specific diagnostic.""" + if not isinstance(value, dict): + _fail(f"{field} must be an object") + return value + + +def _require_list(value: object, field: str) -> list[Any]: + """Return a list or fail with a field-specific diagnostic.""" + if not isinstance(value, list): + _fail(f"{field} must be an array") + return value + + +def _require_text(value: object, field: str) -> str: + """Return trim-stable non-empty text without coercion.""" + if not isinstance(value, str) or not value.strip() or value != value.strip(): + _fail(f"{field} must be a non-empty trim-stable string") + return value + + +def _require_sha(value: object, field: str) -> str: + """Return a normalized immutable Git commit SHA.""" + text = _require_text(value, field) + if SHA_PATTERN.fullmatch(text) is None: + _fail(f"{field} must be 40 hexadecimal characters") + return text.lower() + + +def _require_positive_int(value: object, field: str) -> int: + """Return a positive integer without accepting booleans.""" + if isinstance(value, bool) or not isinstance(value, int) or value <= 0: + _fail(f"{field} must be a positive integer") + return value + + +def collect_paginated_pulls( + fetch_page: PageFetcher, + *, + page_size: int = PAGE_SIZE, + max_pages: int = MAX_PAGES, +) -> dict[str, object]: + """Collect every announced page or fail instead of returning partial evidence.""" + _require_positive_int(page_size, "page_size") + _require_positive_int(max_pages, "max_pages") + pull_requests: list[dict[str, Any]] = [] + for page in range(1, max_pages + 1): + items, has_next = fetch_page(page, page_size) + if not isinstance(items, list) or any(not isinstance(item, dict) for item in items): + _fail(f"pull request page {page} must contain objects") + if not isinstance(has_next, bool): + _fail(f"pull request page {page} next-page marker must be boolean") + if has_next and not items: + _fail(f"pull request page {page} is empty but announces another page") + pull_requests.extend(items) + if not has_next: + return {"incomplete_results": False, "pull_requests": pull_requests} + _fail("live pull-request pagination bound would truncate the queue") + + +def _live_pr_entry( + raw_pr: object, + *, + index: int, + base_sha: str, + existing: dict[int, dict[str, Any]], +) -> dict[str, object]: + """Convert one trusted pulls-API record into the reviewed manifest schema.""" + pr = _require_record(raw_pr, f"pull_requests[{index}]") + number = _require_positive_int(pr.get("number"), f"pull_requests[{index}].number") + if pr.get("state") != "open": + _fail(f"pull_requests[{index}].state must be open") + title = _require_text(pr.get("title"), f"pull_requests[{index}].title") + expected_url = f"https://github.com/{REPOSITORY}/pull/{number}" + if pr.get("html_url") != expected_url: + _fail(f"pull_requests[{index}].html_url must be {expected_url}") + + base = _require_record(pr.get("base"), f"pull_requests[{index}].base") + if base.get("ref") != BASE_BRANCH: + _fail(f"pull_requests[{index}].base.ref must be {BASE_BRANCH}") + if ( + "sha" in base + and _require_sha(base.get("sha"), f"pull_requests[{index}].base.sha") != base_sha + ): + _fail(f"pull_requests[{index}].base.sha must match the live {BASE_BRANCH} tip") + + head = _require_record(pr.get("head"), f"pull_requests[{index}].head") + head_sha = _require_sha(head.get("sha"), f"pull_requests[{index}].head.sha") + + prior = existing.get(number) + if prior is None: + initial_train = UNTRIAGED_TRAIN + initial_disposition = "triage_required" + else: + initial_train = _require_text(prior.get("initial_train"), "existing.initial_train") + initial_disposition = _require_text( + prior.get("initial_disposition"), "existing.initial_disposition" + ) + + return { + "number": number, + "title": title, + "url": expected_url, + "initial_train": initial_train, + "initial_disposition": initial_disposition, + "head_sha": head_sha, + "head_sha_status": "exact_current_head", + } + + +def build_refreshed_manifest( + seed: object, + live_result: object, + *, + base_sha: str, + snapshot_date: str, +) -> dict[str, Any]: + """Build a deterministic complete queue while preserving reviewed routing metadata.""" + try: + validate_manifest(seed) + except ManifestError as exc: + raise RefreshError(f"seed manifest is invalid: {exc}") from exc + seed_record = _require_record(seed, "seed") + live = _require_record(live_result, "live result") + if live.get("incomplete_results") is not False: + _fail("live pull-request inventory is incomplete") + pulls = _require_list(live.get("pull_requests"), "live result.pull_requests") + normalized_base_sha = _require_sha(base_sha, "base_sha") + try: + datetime.strptime(snapshot_date, "%Y-%m-%d") + except (TypeError, ValueError) as exc: + raise RefreshError("snapshot_date must use YYYY-MM-DD") from exc + + existing_items = _require_list(seed_record.get("pull_requests"), "seed.pull_requests") + existing: dict[int, dict[str, Any]] = {} + for index, raw_pr in enumerate(existing_items): + pr = _require_record(raw_pr, f"seed.pull_requests[{index}]") + number = _require_positive_int(pr.get("number"), f"seed.pull_requests[{index}].number") + existing[number] = pr + + seen: set[int] = set() + refreshed_items: list[dict[str, object]] = [] + for index, raw_pr in enumerate(pulls): + entry = _live_pr_entry( + raw_pr, + index=index, + base_sha=normalized_base_sha, + existing=existing, + ) + number = int(entry["number"]) + if number in seen: + _fail(f"duplicate pull request number: {number}") + seen.add(number) + refreshed_items.append(entry) + refreshed_items.sort(key=lambda item: int(item["number"])) + + refreshed = deepcopy(seed_record) + trains = _require_record(refreshed.get("trains"), "seed.trains") + expected_untriaged = {"description": UNTRIAGED_DESCRIPTION, "issue": UNTRIAGED_ISSUE} + prior_untriaged = trains.get(UNTRIAGED_TRAIN) + if prior_untriaged is not None and prior_untriaged != expected_untriaged: + _fail(f"{UNTRIAGED_TRAIN} is already assigned to a different routing authority") + trains[UNTRIAGED_TRAIN] = expected_untriaged + refreshed["base_sha"] = normalized_base_sha + refreshed["snapshot_date"] = snapshot_date + refreshed["open_pr_count"] = len(refreshed_items) + refreshed["authority_note"] = ( + "Generated from a complete live GitHub open-PR inventory. Refresh checks, reviews, " + "threads, ancestry, and writer evidence immediately before action." + ) + refreshed["pull_requests"] = refreshed_items + try: + validate_manifest(refreshed) + except ManifestError as exc: + raise RefreshError(f"refreshed manifest is invalid: {exc}") from exc + return refreshed + + +def _request_json(url: str, token: str | None) -> tuple[object, str]: + """Read JSON from the fixed GitHub API origin without exposing token-bearing custom URLs.""" + if not url.startswith(f"{GITHUB_API_ORIGIN}/"): + _fail("GitHub API URL escaped the canonical origin") + headers = { + "Accept": "application/vnd.github+json", + "X-GitHub-Api-Version": "2022-11-28", + "User-Agent": "bandscope-open-pr-queue-refresh", + } + if token: + headers["Authorization"] = f"Bearer {token}" + request = urllib.request.Request(url, headers=headers) + try: + with urllib.request.urlopen(request, timeout=20) as response: # noqa: S310 + payload = json.loads(response.read().decode("utf-8")) + link = response.headers.get("Link", "") + except (OSError, UnicodeError, json.JSONDecodeError, urllib.error.URLError) as exc: + raise RefreshError(f"GitHub API request failed: {type(exc).__name__}") from exc + return payload, link + + +def fetch_live_base_sha(token: str | None) -> str: + """Resolve the current develop branch tip from the canonical GitHub API.""" + url = f"{GITHUB_API_ORIGIN}/repos/{REPOSITORY}/branches/{BASE_BRANCH}" + payload, _ = _request_json(url, token) + branch = _require_record(payload, "branch") + commit = _require_record(branch.get("commit"), "branch.commit") + return _require_sha(commit.get("sha"), "branch.commit.sha") + + +def fetch_live_pull_page( + page: int, + page_size: int, + token: str | None, +) -> tuple[list[dict[str, Any]], bool]: + """Fetch one bounded page of open develop-targeted PRs from GitHub.""" + _require_positive_int(page, "page") + _require_positive_int(page_size, "page_size") + query = urllib.parse.urlencode( + { + "state": "open", + "base": BASE_BRANCH, + "per_page": page_size, + "page": page, + "sort": "created", + "direction": "asc", + } + ) + url = f"{GITHUB_API_ORIGIN}/repos/{REPOSITORY}/pulls?{query}" + payload, link = _request_json(url, token) + items = _require_list(payload, f"pull request page {page}") + if any(not isinstance(item, dict) for item in items): + _fail(f"pull request page {page} must contain objects") + return items, 'rel="next"' in link + + +def _write_manifest_atomic(manifest: dict[str, Any]) -> None: + """Atomically replace the canonical manifest without following a symlink target.""" + if MANIFEST_PATH.is_symlink(): + _fail("open PR queue manifest path must not be a symbolic link") + temporary = MANIFEST_PATH.with_name(f".{MANIFEST_PATH.name}.tmp") + if temporary.exists() or temporary.is_symlink(): + _fail("temporary manifest path already exists") + encoded = json.dumps(manifest, indent=2, ensure_ascii=False) + "\n" + try: + with temporary.open("x", encoding="utf-8", newline="\n") as handle: + handle.write(encoded) + handle.flush() + os.fsync(handle.fileno()) + os.replace(temporary, MANIFEST_PATH) + finally: + if temporary.exists(): + temporary.unlink() + + +def main() -> int: + """Refresh the committed queue from GitHub and return a shell-friendly status code.""" + try: + seed = load_manifest(MANIFEST_PATH) + token = os.environ.get("GITHUB_TOKEN") + base_sha = fetch_live_base_sha(token) + live_result = collect_paginated_pulls( + lambda page, size: fetch_live_pull_page(page, size, token) + ) + snapshot_date = datetime.now(ZoneInfo("Asia/Seoul")).date().isoformat() + refreshed = build_refreshed_manifest( + seed, + live_result, + base_sha=base_sha, + snapshot_date=snapshot_date, + ) + _write_manifest_atomic(refreshed) + except (ManifestError, RefreshError) as exc: + print(f"open PR queue refresh failed: {exc}", file=sys.stderr) + return 1 + print( + f"open PR queue refreshed: {refreshed['open_pr_count']} PRs at {refreshed['base_sha']}" + ) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From d12d51bfc34e1d41e74a5491720fdf2958841ee8 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 23 Aug 2026 10:11:57 -0700 Subject: [PATCH 14/77] docs(readiness): correct protected version evidence --- docs/product-technical-gap-baseline.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index cb292c7c7..abf62808c 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -53,14 +53,14 @@ The investigation covered: | Priority | Gap | Current evidence | Buyer impact | Owning issue | |---:|---|---|---|---| -| P0 | Trusted distribution and update | Version `0.1.0`; release workflow is principally validation/preflight; no complete signed/notarized updater and rollback evidence | Buyers cannot establish publisher trust or safely update/repair | [#960](https://github.com/ContextualWisdomLab/bandscope/issues/960) | +| P0 | Trusted distribution and update | Version `0.1.3`; release workflow is principally validation/preflight; no complete signed/notarized updater and rollback evidence | Buyers cannot establish publisher trust or safely update/repair | [#960](https://github.com/ContextualWisdomLab/bandscope/issues/960) | | P0 | Active rehearsal player | Many cue/action PRs exist, but there is no single timing/playback authority proving count-in, precise looping, rate, cue navigation, and role controls through the production desktop boundary | Analysis does not yet close the loop into repeated practice | [#961](https://github.com/ContextualWisdomLab/bandscope/issues/961) | | P0 | Crash-safe durable projects | Durable project truth, autosave, atomic publish, migrations, backups, recovery, and rollback are not governed by one versioned format | A crash or upgrade can threaten user work and trust | [#962](https://github.com/ContextualWisdomLab/bandscope/issues/962) | | P0 | Scientific acceptance | Real decoded-audio acceptance exists as work in progress, but the broader licensed multi-fixture MIR program, recognized metrics, uncertainty, CPU/GPU parity, and claim matrix are not closed | Musicians cannot judge where results are trustworthy | [#770](https://github.com/ContextualWisdomLab/bandscope/issues/770) | | P0 | Resource/cancellation contract | Canonical local-audio admission is being developed, but all decode, separation, analysis, PDF, cancellation, and fallback paths must converge | Large or hostile files can degrade availability or create inconsistent behavior | [#781](https://github.com/ContextualWisdomLab/bandscope/issues/781) | | P1 | Diagnostics and support evidence | Multiple redaction fixes exist, but no single typed local diagnostics contract or previewable support bundle exists | Failures remain expensive to diagnose and risk leaking private paths/content | [#963](https://github.com/ContextualWisdomLab/bandscope/issues/963) | | P1 | First-run activation | Empty states and next-action PRs exist, but no licensed demo proves time-to-first-rehearsal through the production path | A new buyer cannot evaluate value quickly or reproducibly | [#964](https://github.com/ContextualWisdomLab/bandscope/issues/964) | -| P1 | Design and accessibility parity | Storybook work is open. The saved Figma file claims 28 pages but currently exposes two top-level pages; its footer cites `0.1.3`, while repository/runtime metadata cites `0.1.0` | Design status can be mistaken for shipped behavior; assistive-technology acceptance is incomplete | [#965](https://github.com/ContextualWisdomLab/bandscope/issues/965) | +| P1 | Design and accessibility parity | Storybook work is open. The saved Figma file claims 28 pages but currently exposes two top-level pages; its footer and protected repository/runtime metadata both cite `0.1.3`, so the remaining gap is implementation/design parity rather than version drift | Design status can be mistaken for shipped behavior; assistive-technology acceptance is incomplete | [#965](https://github.com/ContextualWisdomLab/bandscope/issues/965) | | P0 | PR queue convergence | 83 open PRs include canonical bases, small feature slices, security repairs, dependency updates, and overlapping writers | Review drift and ancestry conflicts prevent coherent delivery | [#966](https://github.com/ContextualWisdomLab/bandscope/issues/966) | ## Completion program From ee6783a198827d95119733908fd1b8780e9c3ea8 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 23 Aug 2026 10:13:05 -0700 Subject: [PATCH 15/77] test(readiness): require per-PR base identity --- .../tests/test_open_pr_queue_refresh.py | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/services/analysis-engine/tests/test_open_pr_queue_refresh.py b/services/analysis-engine/tests/test_open_pr_queue_refresh.py index 5c0eecf07..9165f4c71 100644 --- a/services/analysis-engine/tests/test_open_pr_queue_refresh.py +++ b/services/analysis-engine/tests/test_open_pr_queue_refresh.py @@ -57,15 +57,19 @@ def _live_pr( head_sha: str, *, base_ref: str = "develop", + base_sha: str | None = "d" * 40, title: str | None = None, ) -> dict[str, object]: """Return a minimal GitHub pulls-API record for one open PR.""" + base: dict[str, object] = {"ref": base_ref} + if base_sha is not None: + base["sha"] = base_sha return { "number": number, "title": title or f"PR {number}", "html_url": f"https://github.com/ContextualWisdomLab/bandscope/pull/{number}", "state": "open", - "base": {"ref": base_ref}, + "base": base, "head": {"sha": head_sha}, } @@ -152,6 +156,20 @@ def test_refresh_manifest_updates_exact_heads_and_adds_untriaged_live_prs() -> N }, "base.ref", ), + ( + { + "incomplete_results": False, + "pull_requests": [_live_pr(783, "b" * 40, base_sha=None)], + }, + "base.sha", + ), + ( + { + "incomplete_results": False, + "pull_requests": [_live_pr(783, "b" * 40, base_sha="e" * 40)], + }, + "base.sha", + ), ], ) def test_refresh_manifest_rejects_untrustworthy_live_inventory( From 71b5c321b07cc60fb1a558e5fb2fcbd7dafbf6a2 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 23 Aug 2026 10:13:47 -0700 Subject: [PATCH 16/77] fix(readiness): bind every PR to resolved base tip --- scripts/checks/refresh_open_pr_queue.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/scripts/checks/refresh_open_pr_queue.py b/scripts/checks/refresh_open_pr_queue.py index 4bbfaa468..fd22a9663 100644 --- a/scripts/checks/refresh_open_pr_queue.py +++ b/scripts/checks/refresh_open_pr_queue.py @@ -122,10 +122,8 @@ def _live_pr_entry( base = _require_record(pr.get("base"), f"pull_requests[{index}].base") if base.get("ref") != BASE_BRANCH: _fail(f"pull_requests[{index}].base.ref must be {BASE_BRANCH}") - if ( - "sha" in base - and _require_sha(base.get("sha"), f"pull_requests[{index}].base.sha") != base_sha - ): + pr_base_sha = _require_sha(base.get("sha"), f"pull_requests[{index}].base.sha") + if pr_base_sha != base_sha: _fail(f"pull_requests[{index}].base.sha must match the live {BASE_BRANCH} tip") head = _require_record(pr.get("head"), f"pull_requests[{index}].head") From f2373ba79fb6e984d4deaf206194a8b5d33bdc49 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 23 Aug 2026 10:16:51 -0700 Subject: [PATCH 17/77] test(readiness): require fixed-host GitHub transport --- .../tests/test_open_pr_queue_refresh.py | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/services/analysis-engine/tests/test_open_pr_queue_refresh.py b/services/analysis-engine/tests/test_open_pr_queue_refresh.py index 9165f4c71..a46e22bf1 100644 --- a/services/analysis-engine/tests/test_open_pr_queue_refresh.py +++ b/services/analysis-engine/tests/test_open_pr_queue_refresh.py @@ -182,3 +182,73 @@ def test_refresh_manifest_rejects_untrustworthy_live_inventory( refresher.build_refreshed_manifest( _seed(), live, base_sha="d" * 40, snapshot_date="2026-08-24" ) + + +def test_github_request_uses_fixed_https_host_and_relative_repo_path( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Token-bearing live reads connect only to the canonical GitHub API host.""" + refresher = _load_refresher() + observed: dict[str, object] = {} + + class FakeResponse: + status = 200 + reason = "OK" + + def read(self, amount: int) -> bytes: + observed["read_amount"] = amount + return b'{"ok":true}' + + def getheader(self, name: str, default: str = "") -> str: + assert name == "Link" + return default + + class FakeConnection: + def __init__(self, host: str, *, timeout: int) -> None: + observed["host"] = host + observed["timeout"] = timeout + + def request(self, method: str, path: str, *, headers: dict[str, str]) -> None: + observed["method"] = method + observed["path"] = path + observed["headers"] = headers + + def getresponse(self) -> FakeResponse: + return FakeResponse() + + def close(self) -> None: + observed["closed"] = True + + monkeypatch.setattr(refresher.http.client, "HTTPSConnection", FakeConnection) + + payload, link = refresher._request_github_json( + "/repos/ContextualWisdomLab/bandscope/branches/develop", "token-value" + ) + + assert payload == {"ok": True} + assert link == "" + assert observed["host"] == "api.github.com" + assert observed["method"] == "GET" + assert observed["path"] == "/repos/ContextualWisdomLab/bandscope/branches/develop" + assert observed["headers"]["Authorization"] == "Bearer token-value" + assert observed["closed"] is True + + +def test_github_request_rejects_absolute_or_foreign_paths_before_connecting( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Caller-controlled authorities cannot be smuggled into a token-bearing request target.""" + refresher = _load_refresher() + + def unexpected_connection(host: str, *, timeout: int) -> object: + raise AssertionError(f"unexpected connection to {host} with timeout {timeout}") + + monkeypatch.setattr(refresher.http.client, "HTTPSConnection", unexpected_connection) + + for target in ( + "https://api.github.com/repos/ContextualWisdomLab/bandscope/branches/develop", + "//evil.example/repos/ContextualWisdomLab/bandscope", + "/repos/other-owner/other-repo/pulls", + ): + with pytest.raises(refresher.RefreshError, match="repository path"): + refresher._request_github_json(target, "token-value") From b17c1a463380c997aab54ae028c5248c5096dd14 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 23 Aug 2026 10:17:48 -0700 Subject: [PATCH 18/77] fix(readiness): pin token transport to GitHub host --- scripts/checks/refresh_open_pr_queue.py | 45 ++++++++++++++++--------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/scripts/checks/refresh_open_pr_queue.py b/scripts/checks/refresh_open_pr_queue.py index fd22a9663..1b9b1965a 100644 --- a/scripts/checks/refresh_open_pr_queue.py +++ b/scripts/checks/refresh_open_pr_queue.py @@ -3,13 +3,12 @@ from __future__ import annotations +import http.client import json import os import re import sys -import urllib.error import urllib.parse -import urllib.request from collections.abc import Callable from copy import deepcopy from datetime import datetime @@ -23,9 +22,11 @@ MANIFEST_PATH = REPO_ROOT / "docs" / "product-readiness" / "open-pr-queue.json" REPOSITORY = "ContextualWisdomLab/bandscope" BASE_BRANCH = "develop" -GITHUB_API_ORIGIN = "https://api.github.com" +GITHUB_API_HOST = "api.github.com" +REPOSITORY_API_PREFIX = f"/repos/{REPOSITORY}/" PAGE_SIZE = 100 MAX_PAGES = 10 +MAX_RESPONSE_BYTES = 4 * 1024 * 1024 UNTRIAGED_TRAIN = "T8" UNTRIAGED_DESCRIPTION = "Live additions awaiting explicit merge-train triage" UNTRIAGED_ISSUE = 966 @@ -218,10 +219,11 @@ def build_refreshed_manifest( return refreshed -def _request_json(url: str, token: str | None) -> tuple[object, str]: - """Read JSON from the fixed GitHub API origin without exposing token-bearing custom URLs.""" - if not url.startswith(f"{GITHUB_API_ORIGIN}/"): - _fail("GitHub API URL escaped the canonical origin") +def _request_github_json(target: str, token: str | None) -> tuple[object, str]: + """Read bounded JSON through a fixed GitHub host and repository-relative request target.""" + target = _require_text(target, "GitHub repository path") + if not target.startswith(REPOSITORY_API_PREFIX) or "://" in target or "\n" in target or "\r" in target: + _fail("GitHub repository path escaped the canonical repository") headers = { "Accept": "application/vnd.github+json", "X-GitHub-Api-Version": "2022-11-28", @@ -229,20 +231,31 @@ def _request_json(url: str, token: str | None) -> tuple[object, str]: } if token: headers["Authorization"] = f"Bearer {token}" - request = urllib.request.Request(url, headers=headers) + + connection = http.client.HTTPSConnection(GITHUB_API_HOST, timeout=20) try: - with urllib.request.urlopen(request, timeout=20) as response: # noqa: S310 - payload = json.loads(response.read().decode("utf-8")) - link = response.headers.get("Link", "") - except (OSError, UnicodeError, json.JSONDecodeError, urllib.error.URLError) as exc: + connection.request("GET", target, headers=headers) + response = connection.getresponse() + if response.status != 200: + _fail(f"GitHub API request failed with HTTP status {response.status}") + encoded = response.read(MAX_RESPONSE_BYTES + 1) + if len(encoded) > MAX_RESPONSE_BYTES: + _fail("GitHub API response exceeded the bounded response size") + payload = json.loads(encoded.decode("utf-8")) + link = response.getheader("Link", "") or "" + if not isinstance(link, str): + _fail("GitHub API Link header must be text") + except (OSError, UnicodeError, json.JSONDecodeError, http.client.HTTPException) as exc: raise RefreshError(f"GitHub API request failed: {type(exc).__name__}") from exc + finally: + connection.close() return payload, link def fetch_live_base_sha(token: str | None) -> str: """Resolve the current develop branch tip from the canonical GitHub API.""" - url = f"{GITHUB_API_ORIGIN}/repos/{REPOSITORY}/branches/{BASE_BRANCH}" - payload, _ = _request_json(url, token) + target = f"{REPOSITORY_API_PREFIX}branches/{BASE_BRANCH}" + payload, _ = _request_github_json(target, token) branch = _require_record(payload, "branch") commit = _require_record(branch.get("commit"), "branch.commit") return _require_sha(commit.get("sha"), "branch.commit.sha") @@ -266,8 +279,8 @@ def fetch_live_pull_page( "direction": "asc", } ) - url = f"{GITHUB_API_ORIGIN}/repos/{REPOSITORY}/pulls?{query}" - payload, link = _request_json(url, token) + target = f"{REPOSITORY_API_PREFIX}pulls?{query}" + payload, link = _request_github_json(target, token) items = _require_list(payload, f"pull request page {page}") if any(not isinstance(item, dict) for item in items): _fail(f"pull request page {page} must contain objects") From 9853e2463bb063f53d8e7a98562c5691813414d8 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 23 Aug 2026 10:25:59 -0700 Subject: [PATCH 19/77] docs(readiness): align ADR version evidence --- .../decisions/0001-bandscope-1-0-product-readiness.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/architecture/decisions/0001-bandscope-1-0-product-readiness.md b/docs/architecture/decisions/0001-bandscope-1-0-product-readiness.md index 2172349f4..9694b4c0e 100644 --- a/docs/architecture/decisions/0001-bandscope-1-0-product-readiness.md +++ b/docs/architecture/decisions/0001-bandscope-1-0-product-readiness.md @@ -22,7 +22,7 @@ A large number of individually plausible PRs can make delivery less reliable whe - design states are represented before runtime behavior exists; - a validation package is described as a release without signing, notarization, updater, or rollback evidence. -The saved Figma file is useful but also demonstrates the need for source parity. Its cover describes a 28-page design-system plan while the current file exposes two top-level pages. Its footer cites version `0.1.3`, whereas the reviewed repository and Tauri package metadata cite `0.1.0`. +The saved Figma file is useful but also demonstrates the need for source parity. Its cover describes a 28-page design-system plan while the current file exposes two top-level pages. Its footer and the reviewed repository/Tauri package metadata all cite version `0.1.3`; the remaining parity risk is planned or unimplemented design state rather than a current version-label mismatch. ## Decision @@ -143,7 +143,7 @@ Rejected because unique tests, security findings, and product requirements could ### Treat the Figma file as the product source of truth -Rejected because the current file contains planned/unimplemented pages and stale version metadata. Behavior and semantics must remain executable and testable in code. +Rejected because the current file contains planned or unimplemented pages. Behavior, semantics, and version identity must remain executable and testable in code. ### Release unsigned artifacts and add trust later From db52897352ae945f80333de8fe60c9159e7f9f2c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 23 Aug 2026 11:59:32 -0700 Subject: [PATCH 20/77] fix(security): document verified HTTPSConnection contract --- scripts/checks/refresh_open_pr_queue.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/checks/refresh_open_pr_queue.py b/scripts/checks/refresh_open_pr_queue.py index 1b9b1965a..2b777d475 100644 --- a/scripts/checks/refresh_open_pr_queue.py +++ b/scripts/checks/refresh_open_pr_queue.py @@ -232,7 +232,13 @@ def _request_github_json(target: str, token: str | None) -> tuple[object, str]: if token: headers["Authorization"] = f"Bearer {token}" - connection = http.client.HTTPSConnection(GITHUB_API_HOST, timeout=20) + # Python >=3.12 verifies HTTPS certificates by default; the host and target + # are both fixed/validated above, so this generic legacy-version warning is + # a false positive for BandScope's declared runtime contract. + connection = http.client.HTTPSConnection( # nosemgrep: python.lang.security.audit.httpsconnection-detected.httpsconnection-detected + GITHUB_API_HOST, + timeout=20, + ) try: connection.request("GET", target, headers=headers) response = connection.getresponse() From 735f3f70353c1240edc648d6bbd7995504de6c54 Mon Sep 17 00:00:00 2001 From: seonghobae Date: Mon, 24 Aug 2026 15:32:45 +0900 Subject: [PATCH 21/77] docs(readiness): refresh gap baseline with 2026-08-24 exact-head evidence - Capture exact current head SHA for all 122 open PRs via the bounded live-refresh script and record the snapshot in the queue manifest. - Triage all 40 post-957 additions into merge trains (T0-T6) so no untriaged T8 remainder is left in the committed baseline. - Record systemic gate evidence: inherited npm HIGH CVE failures (pdfjs-dist, undici, nanoid) blocking feature heads until #783 lands, and the intermittent central Strix provider outage. - Fix ruff formatting in test_open_pr_queue_refresh.py (CI gate). --- docs/product-readiness/open-pr-queue.json | 707 +++++++++++++----- docs/product-technical-gap-baseline.md | 259 ++++--- .../tests/test_open_pr_queue_refresh.py | 3 +- 3 files changed, 693 insertions(+), 276 deletions(-) diff --git a/docs/product-readiness/open-pr-queue.json b/docs/product-readiness/open-pr-queue.json index 06806e24f..bc34a2d2d 100644 --- a/docs/product-readiness/open-pr-queue.json +++ b/docs/product-readiness/open-pr-queue.json @@ -1,12 +1,12 @@ { "schema_version": "1.0.0", - "snapshot_date": "2026-08-20", + "snapshot_date": "2026-08-24", "timezone": "Asia/Seoul", "repository": "ContextualWisdomLab/bandscope", "base_branch": "develop", "base_sha": "acdbea6344fe1231c39535b575f4de35e4c607c9", - "open_pr_count": 83, - "authority_note": "Initial read-only inventory. Refresh exact live head SHA, checks, reviews, unresolved threads, and dependencies before any merge or closure action.", + "open_pr_count": 122, + "authority_note": "Generated from a complete live GitHub open-PR inventory. Refresh checks, reviews, threads, ancestry, and writer evidence immediately before action.", "trains": { "T0": { "description": "Dependency, toolchain, workflow and quality base", @@ -39,6 +39,10 @@ "T7": { "description": "Signed commercial release and updater", "issue": 960 + }, + "T8": { + "description": "Live additions awaiting explicit merge-train triage", + "issue": 966 } }, "pull_requests": [ @@ -48,8 +52,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/731", "initial_train": "T5", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "5ea5663f26e33315ac6dda3c4f983f0f12a1cba7", + "head_sha_status": "exact_current_head" }, { "number": 732, @@ -57,8 +61,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/732", "initial_train": "T2", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "830dd4c982b12e1dcdf896e3e951363f1ec16a15", + "head_sha_status": "exact_current_head" }, { "number": 737, @@ -66,8 +70,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/737", "initial_train": "T4", "initial_disposition": "outbound_naruon_handoff_contract", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "82ae343e9911e30cbfe65f1264367b6ae8576cb6", + "head_sha_status": "exact_current_head" }, { "number": 740, @@ -75,8 +79,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/740", "initial_train": "T4", "initial_disposition": "inbound_handoff_reanalysis_slice", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "4761a882d1b345647fb6ca7bbb7c89c5420a097f", + "head_sha_status": "exact_current_head" }, { "number": 744, @@ -84,8 +88,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/744", "initial_train": "T5", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "0ca00b110d046f8101b01a13fcc3f10d57feebd2", + "head_sha_status": "exact_current_head" }, { "number": 746, @@ -93,8 +97,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/746", "initial_train": "T2", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "9d0def7d5038b62b1eb0a8ce2784e23d6618ad74", + "head_sha_status": "exact_current_head" }, { "number": 750, @@ -102,8 +106,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/750", "initial_train": "T1", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "d982adef81cd54adae37078f71a7976aa122e986", + "head_sha_status": "exact_current_head" }, { "number": 754, @@ -111,8 +115,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/754", "initial_train": "T0", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "966d5f1204ec90c303f8903e55f71f67bcba136d", + "head_sha_status": "exact_current_head" }, { "number": 776, @@ -120,8 +124,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/776", "initial_train": "T3", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "8b38e4885e21619cb502eb6d2be25420e6246fd3", + "head_sha_status": "exact_current_head" }, { "number": 780, @@ -129,8 +133,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/780", "initial_train": "T0", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "738495ca9d425fb34a12e76c0614eda356e68f3a", + "head_sha_status": "exact_current_head" }, { "number": 783, @@ -138,8 +142,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/783", "initial_train": "T0", "initial_disposition": "canonical_dependency_security_base", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "1168c8f4257de5de036ea54bf5ee73edb83e775e", + "head_sha_status": "exact_current_head" }, { "number": 811, @@ -147,8 +151,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/811", "initial_train": "T1", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "54c645c9dcace65dca4e5bb669a665480a26dcb7", + "head_sha_status": "exact_current_head" }, { "number": 824, @@ -156,8 +160,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/824", "initial_train": "T1", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "f4d01558eb3c0c2a0b6c81f9614eeb083b6bb39b", + "head_sha_status": "exact_current_head" }, { "number": 826, @@ -165,8 +169,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/826", "initial_train": "T6", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "9002ff8b92272c590a57eecdc80c95f71e4bec55", + "head_sha_status": "exact_current_head" }, { "number": 827, @@ -174,8 +178,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/827", "initial_train": "T2", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "a2e5d260e68a0fe36a4d0551af202b73f257dd70", + "head_sha_status": "exact_current_head" }, { "number": 828, @@ -183,8 +187,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/828", "initial_train": "T2", "initial_disposition": "real_audio_accuracy_slice_under_issue_770", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "9331b406e7fbe4d1407f9f7854db1fd9ba12b194", + "head_sha_status": "exact_current_head" }, { "number": 833, @@ -192,8 +196,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/833", "initial_train": "T5", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "3206c259afcc02a1cc625f3e9f785d48b4a799ed", + "head_sha_status": "exact_current_head" }, { "number": 834, @@ -201,8 +205,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/834", "initial_train": "T2", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "c686ae450677303d2622abec1869aef6cef98155", + "head_sha_status": "exact_current_head" }, { "number": 845, @@ -210,8 +214,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/845", "initial_train": "T3", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "d1dc4da583320205ba92dfabb3fd48092a51d9e9", + "head_sha_status": "exact_current_head" }, { "number": 849, @@ -219,8 +223,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/849", "initial_train": "T5", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "e3268c8bf632d49a524b84cce141f850dd3f252d", + "head_sha_status": "exact_current_head" }, { "number": 850, @@ -228,8 +232,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/850", "initial_train": "T3", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "adbe3da061722ae0dc98bff368ecff494363cdc9", + "head_sha_status": "exact_current_head" }, { "number": 858, @@ -237,8 +241,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/858", "initial_train": "T1", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "bedc2069494c0687f02873097226de04fa86f484", + "head_sha_status": "exact_current_head" }, { "number": 859, @@ -246,8 +250,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/859", "initial_train": "T5", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "38e1027d3a9613da5ea912020fa3f08ef0814e69", + "head_sha_status": "exact_current_head" }, { "number": 861, @@ -255,8 +259,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/861", "initial_train": "T0", "initial_disposition": "canonical_python_branch_coverage_base", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "6afbc9fff585a5a7c2bf0454b0673c3f71f1b0e1", + "head_sha_status": "exact_current_head" }, { "number": 865, @@ -264,8 +268,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/865", "initial_train": "T1", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "f86e266b2ab2dc5a95e6b4a484e777b29f0feeaf", + "head_sha_status": "exact_current_head" }, { "number": 866, @@ -273,8 +277,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/866", "initial_train": "T1", "initial_disposition": "canonical_local_audio_resource_policy", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "223dd78126deeb3f12a68dc140f6a83fbe422225", + "head_sha_status": "exact_current_head" }, { "number": 867, @@ -282,8 +286,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/867", "initial_train": "T0", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "f1ac4167b8b7bdb88c92a89c437e0dd5432ba6ae", + "head_sha_status": "exact_current_head" }, { "number": 873, @@ -291,8 +295,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/873", "initial_train": "T2", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "c30be7afa82beeade94c99d2a4bae818aebf121f", + "head_sha_status": "exact_current_head" }, { "number": 874, @@ -300,8 +304,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/874", "initial_train": "T3", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "05935e02f29366b2bd240d6fb4d1bae784cb992b", + "head_sha_status": "exact_current_head" }, { "number": 881, @@ -309,8 +313,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/881", "initial_train": "T0", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "4e07356d1ece6f22c040d6a0cd24c995800f88a9", + "head_sha_status": "exact_current_head" }, { "number": 884, @@ -318,8 +322,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/884", "initial_train": "T3", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "e33ca56a3922b78b5569e32e1a949f5a488c7e01", + "head_sha_status": "exact_current_head" }, { "number": 891, @@ -327,8 +331,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/891", "initial_train": "T2", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "92fe9155a1896e13ba8e06e1762f6848fc2efdfa", + "head_sha_status": "exact_current_head" }, { "number": 892, @@ -336,8 +340,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/892", "initial_train": "T2", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "40f138e00ece3d480b5433a5ff8e1d9026cd7ff2", + "head_sha_status": "exact_current_head" }, { "number": 894, @@ -345,8 +349,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/894", "initial_train": "T0", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "475adeba62109e1e34981baed22ce89728211108", + "head_sha_status": "exact_current_head" }, { "number": 895, @@ -354,8 +358,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/895", "initial_train": "T0", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "9de29814cb307e7e50817e820c2510058d67233f", + "head_sha_status": "exact_current_head" }, { "number": 896, @@ -363,8 +367,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/896", "initial_train": "T0", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "c07e51639dbb7b44f57ff0aabc26ad793e691add", + "head_sha_status": "exact_current_head" }, { "number": 897, @@ -372,8 +376,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/897", "initial_train": "T5", "initial_disposition": "storybook_design_inventory_slice", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "d31a6ce740f756902d59edaf4420eea713007c81", + "head_sha_status": "exact_current_head" }, { "number": 898, @@ -381,8 +385,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/898", "initial_train": "T5", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "48c6084e9e8e74026e5690173c01a5e1479b7026", + "head_sha_status": "exact_current_head" }, { "number": 899, @@ -390,8 +394,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/899", "initial_train": "T5", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "b263ac441df5522a1a943463744f9f732d806adc", + "head_sha_status": "exact_current_head" }, { "number": 900, @@ -399,8 +403,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/900", "initial_train": "T3", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "0c09689fc981d72c2a6d7505c0ab0c41a97d0ebe", + "head_sha_status": "exact_current_head" }, { "number": 901, @@ -408,8 +412,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/901", "initial_train": "T3", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "940ff24aaeac3261f922a8f33d47dd8abb1abb93", + "head_sha_status": "exact_current_head" }, { "number": 903, @@ -417,17 +421,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/903", "initial_train": "T3", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" - }, - { - "number": 904, - "title": "feat(workspace): count in tonight's first section on the map", - "url": "https://github.com/ContextualWisdomLab/bandscope/pull/904", - "initial_train": "T3", - "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "1f3e8c75c57232208d2b723aa37137e47ccf46de", + "head_sha_status": "exact_current_head" }, { "number": 905, @@ -435,8 +430,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/905", "initial_train": "T3", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "a1a38fc4e537f22d7811592e49baeb9d816faeff", + "head_sha_status": "exact_current_head" }, { "number": 907, @@ -444,8 +439,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/907", "initial_train": "T0", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "b0972c5ad01c506e24566433e8dbf9008c91c4a9", + "head_sha_status": "exact_current_head" }, { "number": 910, @@ -453,8 +448,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/910", "initial_train": "T3", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "61aa51f274c04d60289da7919a70d40155f2674d", + "head_sha_status": "exact_current_head" }, { "number": 912, @@ -462,8 +457,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/912", "initial_train": "T3", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "b8f588ad6b0d017a4539ed6956fdcf53057292bc", + "head_sha_status": "exact_current_head" }, { "number": 913, @@ -471,8 +466,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/913", "initial_train": "T3", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "c56a58933c418d9b0d86faca5a663de21f5b1d8c", + "head_sha_status": "exact_current_head" }, { "number": 914, @@ -480,8 +475,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/914", "initial_train": "T3", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "a0b5fe92436113fcf57017f4a065e8901a471f9d", + "head_sha_status": "exact_current_head" }, { "number": 916, @@ -489,8 +484,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/916", "initial_train": "T3", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "6657a47b706e40d704a6949de383c5d55aa288a5", + "head_sha_status": "exact_current_head" }, { "number": 918, @@ -498,8 +493,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/918", "initial_train": "T0", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "77a2c35a21eeaf6273ea6bf825d9ea0c1793f33b", + "head_sha_status": "exact_current_head" }, { "number": 919, @@ -507,8 +502,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/919", "initial_train": "T0", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "b113cec48235bc74660f74d5ed9bcdb5357d74b0", + "head_sha_status": "exact_current_head" }, { "number": 920, @@ -516,8 +511,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/920", "initial_train": "T0", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "477fa3e363e77cbc0b05b9147f58398f781d6e58", + "head_sha_status": "exact_current_head" }, { "number": 921, @@ -525,8 +520,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/921", "initial_train": "T0", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "2840d0d0831d705b357aca15d7ec91bcf6928ca4", + "head_sha_status": "exact_current_head" }, { "number": 922, @@ -534,8 +529,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/922", "initial_train": "T0", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "5f6f0a809eb05ec6b37417b37581a8927c76e2a8", + "head_sha_status": "exact_current_head" }, { "number": 923, @@ -543,8 +538,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/923", "initial_train": "T0", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "d3d65db3541850bbec781757341934cdf6a8aca7", + "head_sha_status": "exact_current_head" }, { "number": 924, @@ -552,8 +547,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/924", "initial_train": "T0", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "bd7cd7d49cf5ea05081b12dca13385ebf67a881e", + "head_sha_status": "exact_current_head" }, { "number": 925, @@ -561,8 +556,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/925", "initial_train": "T0", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "525bb99a455361d531ac26a856f70903e9ce14de", + "head_sha_status": "exact_current_head" }, { "number": 926, @@ -570,8 +565,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/926", "initial_train": "T0", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "5b27740c54bbdd81c05008224f6a32ddbaa265fd", + "head_sha_status": "exact_current_head" }, { "number": 927, @@ -579,8 +574,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/927", "initial_train": "T0", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "235019727df523ea459a6bd09b60208906eb22c8", + "head_sha_status": "exact_current_head" }, { "number": 928, @@ -588,8 +583,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/928", "initial_train": "T0", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "f87c0fbdca3b69af444a617412e3d620171b2ee4", + "head_sha_status": "exact_current_head" }, { "number": 929, @@ -597,8 +592,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/929", "initial_train": "T0", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "0ce4cb84cbafcc81cbd824b2a7879ab5549d419a", + "head_sha_status": "exact_current_head" }, { "number": 930, @@ -606,8 +601,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/930", "initial_train": "T0", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "927c473bbfefc63a08aaf352222372506d836abd", + "head_sha_status": "exact_current_head" }, { "number": 931, @@ -615,8 +610,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/931", "initial_train": "T0", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "8aed8adac44592b6dbfac30bf4cfe419e71eebe0", + "head_sha_status": "exact_current_head" }, { "number": 932, @@ -624,8 +619,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/932", "initial_train": "T0", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "57011511f876883347a5da5f5e2540f356cb0195", + "head_sha_status": "exact_current_head" }, { "number": 933, @@ -633,8 +628,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/933", "initial_train": "T0", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "dff69016587f35141a0f13c1bec84fa19fc18a5a", + "head_sha_status": "exact_current_head" }, { "number": 934, @@ -642,8 +637,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/934", "initial_train": "T3", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "3d796dfffd64b2331bf4146166449f7725b8b673", + "head_sha_status": "exact_current_head" }, { "number": 936, @@ -651,8 +646,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/936", "initial_train": "T0", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "cb048b1f455b3655aa0fcc086b15b0cc496d339a", + "head_sha_status": "exact_current_head" }, { "number": 937, @@ -660,8 +655,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/937", "initial_train": "T3", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "c3685178c6f393902748d25c30d087af8c6597b7", + "head_sha_status": "exact_current_head" }, { "number": 939, @@ -669,8 +664,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/939", "initial_train": "T3", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "d3c852894bc51425653d6e296c4ea9c69fe533cf", + "head_sha_status": "exact_current_head" }, { "number": 941, @@ -678,8 +673,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/941", "initial_train": "T6", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "f7a2634607c5a7792f05309ec9e284d9d6132ca6", + "head_sha_status": "exact_current_head" }, { "number": 942, @@ -687,8 +682,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/942", "initial_train": "T0", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "647b883996ed49141e177a5e97138aaca368ef81", + "head_sha_status": "exact_current_head" }, { "number": 943, @@ -696,8 +691,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/943", "initial_train": "T3", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "4855357e19dd444271f95142667cb226989e1142", + "head_sha_status": "exact_current_head" }, { "number": 944, @@ -705,8 +700,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/944", "initial_train": "T0", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "b0f8cf0de9f02ee1bed7a8ee964daed6d2063562", + "head_sha_status": "exact_current_head" }, { "number": 946, @@ -714,8 +709,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/946", "initial_train": "T3", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "4d13986d7d486caae8f87db7d468388624acd29f", + "head_sha_status": "exact_current_head" }, { "number": 947, @@ -723,8 +718,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/947", "initial_train": "T3", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "315e563ffe6d6ad83c48117e308477f8ca280cbc", + "head_sha_status": "exact_current_head" }, { "number": 948, @@ -732,8 +727,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/948", "initial_train": "T0", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "d415bc382167c90216fb0cae46821b5597270766", + "head_sha_status": "exact_current_head" }, { "number": 949, @@ -741,8 +736,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/949", "initial_train": "T6", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "6ee9ada2228a91d6e1a0de776347890e8ea82132", + "head_sha_status": "exact_current_head" }, { "number": 950, @@ -750,8 +745,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/950", "initial_train": "T6", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "396d42017bd32c9b7215ec8bf8f5bf8669139e85", + "head_sha_status": "exact_current_head" }, { "number": 951, @@ -759,8 +754,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/951", "initial_train": "T6", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "9c64ca3cd3bd2669d012f86b7f3611e7f2463583", + "head_sha_status": "exact_current_head" }, { "number": 955, @@ -768,8 +763,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/955", "initial_train": "T3", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "d1975e182f529cad3acba1c6c6d7d0a74ac4e47a", + "head_sha_status": "exact_current_head" }, { "number": 956, @@ -777,8 +772,8 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/956", "initial_train": "T6", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "2707ad39e0198857a9f5eedfddea8a36718257e8", + "head_sha_status": "exact_current_head" }, { "number": 957, @@ -786,8 +781,368 @@ "url": "https://github.com/ContextualWisdomLab/bandscope/pull/957", "initial_train": "T3", "initial_disposition": "triage_required", - "head_sha": null, - "head_sha_status": "refresh_required_before_action" + "head_sha": "4c4c045cfe1eabf585aadb01bf799ed68d654e4f", + "head_sha_status": "exact_current_head" + }, + { + "number": 967, + "title": "feat(operations): add privacy-safe support manifest boundary", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/967", + "initial_train": "T6", + "initial_disposition": "support_manifest_boundary", + "head_sha": "b9881489d297f1f97f985a880df72da886b861a2", + "head_sha_status": "exact_current_head" + }, + { + "number": 968, + "title": "feat(readiness): establish BandScope 1.0 product-readiness baseline", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/968", + "initial_train": "T0", + "initial_disposition": "product_readiness_baseline_program", + "head_sha": "db52897352ae945f80333de8fe60c9159e7f9f2c", + "head_sha_status": "exact_current_head" + }, + { + "number": 969, + "title": "fix(design): restore Figma contract-page inventory and drift check", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/969", + "initial_train": "T5", + "initial_disposition": "figma_contract_inventory_drift_check", + "head_sha": "934b76286632a7d7ade8d99e6ead85a7e751ce4d", + "head_sha_status": "exact_current_head" + }, + { + "number": 970, + "title": "fix(project): stage new saves without clobbering known-good files", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/970", + "initial_train": "T4", + "initial_disposition": "project_atomic_publication", + "head_sha": "2e48e59916c8fb4552e26c26c8a6c65946f6b7fb", + "head_sha_status": "exact_current_head" + }, + { + "number": 971, + "title": "feat(workspace): loop tonight's first section from the map", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/971", + "initial_train": "T3", + "initial_disposition": "player_first_section_loop", + "head_sha": "7c1b4973f9d7f4ae7320814cfa340d42d29e2706", + "head_sha_status": "exact_current_head" + }, + { + "number": 972, + "title": "feat(workspace): name the next rehearsal action from help", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/972", + "initial_train": "T3", + "initial_disposition": "help_next_action_copy", + "head_sha": "c0095ca8f4015133a7debb6fdac910d573a6ec79", + "head_sha_status": "exact_current_head" + }, + { + "number": 974, + "title": "feat(workspace): start analysis from the first-run card", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/974", + "initial_train": "T5", + "initial_disposition": "first_run_analyze_entry", + "head_sha": "fd33104ca88030b5218e1c56a0cb683e2b50d7db", + "head_sha_status": "exact_current_head" + }, + { + "number": 976, + "title": "feat(workspace): name the next action after analysis fails", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/976", + "initial_train": "T3", + "initial_disposition": "analysis_failure_next_action_copy", + "head_sha": "d07dc57347ce8187bc70b1d1f01b1ac5ec195887", + "head_sha_status": "exact_current_head" + }, + { + "number": 980, + "title": "feat(workspace): name using your own song as the first next action", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/980", + "initial_train": "T5", + "initial_disposition": "first_run_own_song_action", + "head_sha": "9bc2ead0d52f4f3da811305042755512d396a9f3", + "head_sha_status": "exact_current_head" + }, + { + "number": 981, + "title": "feat(workspace): name Choose another song after local intake fails", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/981", + "initial_train": "T3", + "initial_disposition": "local_intake_failure_next_action_copy", + "head_sha": "9059b4ceb5de2b223114611751b9bc26d9f220b2", + "head_sha_status": "exact_current_head" + }, + { + "number": 982, + "title": "feat(workspace): name Paste another YouTube link after import fails", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/982", + "initial_train": "T3", + "initial_disposition": "youtube_import_failure_next_action_copy", + "head_sha": "54f5a59edfea79e42b363bae334d99de13016235", + "head_sha_status": "exact_current_head" + }, + { + "number": 984, + "title": "feat(workspace): name the next action after project load or save fails", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/984", + "initial_train": "T4", + "initial_disposition": "project_save_load_failure_next_action_copy", + "head_sha": "8207813b5ef7637946e6510e09fa734529b1f654", + "head_sha_status": "exact_current_head" + }, + { + "number": 985, + "title": "feat(analysis): enforce one canonical audio resource policy (#781)", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/985", + "initial_train": "T1", + "initial_disposition": "canonical_audio_resource_policy", + "head_sha": "d2cf2047af790cddf02b3957856d246638a754b5", + "head_sha_status": "exact_current_head" + }, + { + "number": 986, + "title": "feat(workspace): guide tonight's first outro on the rehearsal map", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/986", + "initial_train": "T3", + "initial_disposition": "workspace_rehearsal_map_slice", + "head_sha": "4222d99c025723329e522f98b7088486a4c3b5ca", + "head_sha_status": "exact_current_head" + }, + { + "number": 987, + "title": "feat(workspace): name tonight's tempo, starting chord, and transpose setup", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/987", + "initial_train": "T3", + "initial_disposition": "workspace_rehearsal_map_slice", + "head_sha": "ec87428f4ef976d3a71570b5ef60c29a315f1c35", + "head_sha_status": "exact_current_head" + }, + { + "number": 989, + "title": "feat(workspace): guide tonight's first tag on the rehearsal map", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/989", + "initial_train": "T3", + "initial_disposition": "workspace_rehearsal_map_slice", + "head_sha": "6b83256ff8415d5e8afb0c034f97c6d40674ed79", + "head_sha_status": "exact_current_head" + }, + { + "number": 990, + "title": "feat(workspace): name tonight's first simpler take on the map", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/990", + "initial_train": "T3", + "initial_disposition": "workspace_rehearsal_map_slice", + "head_sha": "3736a1647ec06251214005b82552717fd8dd8b69", + "head_sha_status": "exact_current_head" + }, + { + "number": 991, + "title": "feat(workspace): name tonight's first groove on the map", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/991", + "initial_train": "T3", + "initial_disposition": "workspace_rehearsal_map_slice", + "head_sha": "ac360bd992feb7167fdf0ec92d50413c6df383f9", + "head_sha_status": "exact_current_head" + }, + { + "number": 992, + "title": "feat(workspace): name tonight's first overlap on the map", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/992", + "initial_train": "T3", + "initial_disposition": "workspace_rehearsal_map_slice", + "head_sha": "510347b801bf1d69a32b26b42519dbbb2827e4f7", + "head_sha_status": "exact_current_head" + }, + { + "number": 993, + "title": "feat(workspace): name tonight's first transition cue on the map", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/993", + "initial_train": "T3", + "initial_disposition": "workspace_rehearsal_map_slice", + "head_sha": "151d8ec7336889fecfaead0367b734ef1fbe38d5", + "head_sha_status": "exact_current_head" + }, + { + "number": 994, + "title": "feat(workspace): name tonight's first transition on the map", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/994", + "initial_train": "T3", + "initial_disposition": "workspace_rehearsal_map_slice", + "head_sha": "6e8d5412d8935bf767425c9e6c6adc19e7f19031", + "head_sha_status": "exact_current_head" + }, + { + "number": 995, + "title": "feat(workspace): name tonight's first count on the map", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/995", + "initial_train": "T3", + "initial_disposition": "workspace_rehearsal_map_slice", + "head_sha": "4d353ca99a389189b2d7c1eda04067a071e1ac79", + "head_sha_status": "exact_current_head" + }, + { + "number": 996, + "title": "feat(workspace): name tonight's first assignment on the map", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/996", + "initial_train": "T3", + "initial_disposition": "workspace_rehearsal_map_slice", + "head_sha": "54b75a265ef3b7265461abf0601b6daf52d67b5f", + "head_sha_status": "exact_current_head" + }, + { + "number": 997, + "title": "feat(workspace): name tonight's first open rehearsal comment on the map", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/997", + "initial_train": "T3", + "initial_disposition": "workspace_rehearsal_map_slice", + "head_sha": "fa4aadd429c220bc1e620d475892e2292c92f4b8", + "head_sha_status": "exact_current_head" + }, + { + "number": 998, + "title": "feat(workspace): name tonight's first pending approval on the map", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/998", + "initial_train": "T3", + "initial_disposition": "workspace_rehearsal_map_slice", + "head_sha": "b67afb688866f5436b0c6079ffe4291e918be7ea", + "head_sha_status": "exact_current_head" + }, + { + "number": 999, + "title": "⚡ Bolt: 관측 확률 계산 배열 연산으로 벡터화 (성능 개선)", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/999", + "initial_train": "T2", + "initial_disposition": "observation_probability_vectorization", + "head_sha": "c11f5ed592bd982d1148050f7cec6a1573ce5160", + "head_sha_status": "exact_current_head" + }, + { + "number": 1000, + "title": "feat(workspace): name tonight's first blocked assignment on the map", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/1000", + "initial_train": "T3", + "initial_disposition": "workspace_rehearsal_map_slice", + "head_sha": "6c6b3e75616e36f7b2997b92ea13bdca7628b523", + "head_sha_status": "exact_current_head" + }, + { + "number": 1001, + "title": "feat(workspace): name tonight's first ear check on the map", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/1001", + "initial_train": "T3", + "initial_disposition": "workspace_rehearsal_map_slice", + "head_sha": "38781c86dbcfdee00da1072062056deeada54afe", + "head_sha_status": "exact_current_head" + }, + { + "number": 1002, + "title": "feat(workspace): name tonight's first confirmed chord on the map", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/1002", + "initial_train": "T3", + "initial_disposition": "workspace_rehearsal_map_slice", + "head_sha": "1a1f231052d1cecadf1178b14cc9ff1e43a21975", + "head_sha_status": "exact_current_head" + }, + { + "number": 1003, + "title": "feat(workspace): name tonight's first harmonic explanation on the map", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/1003", + "initial_train": "T3", + "initial_disposition": "workspace_rehearsal_map_slice", + "head_sha": "8f88db8c23134afb4e2e726b10b1cada7bb969d0", + "head_sha_status": "exact_current_head" + }, + { + "number": 1004, + "title": "feat(workspace): name tonight's first setup note on the map", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/1004", + "initial_train": "T3", + "initial_disposition": "workspace_rehearsal_map_slice", + "head_sha": "9411885d68ed7a08d880043dca4f52bc6166abce", + "head_sha_status": "exact_current_head" + }, + { + "number": 1005, + "title": "feat(workspace): name tonight's first harmonic function on the map", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/1005", + "initial_train": "T3", + "initial_disposition": "workspace_rehearsal_map_slice", + "head_sha": "00cf87dca1bd334cb5c891b3bc3bd19510d10611", + "head_sha_status": "exact_current_head" + }, + { + "number": 1006, + "title": "feat(workspace): name tonight's first transposition plan on the map", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/1006", + "initial_train": "T3", + "initial_disposition": "workspace_rehearsal_map_slice", + "head_sha": "f1e86e9c66c09252fbedd6626f015cf370464c56", + "head_sha_status": "exact_current_head" + }, + { + "number": 1007, + "title": "feat(workspace): name tonight's first part handoff on the map", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/1007", + "initial_train": "T3", + "initial_disposition": "workspace_rehearsal_map_slice", + "head_sha": "e06ca27fad4792f8b191fd314a630e5932580007", + "head_sha_status": "exact_current_head" + }, + { + "number": 1008, + "title": "feat(workspace): name tonight's first capo plan on the map", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/1008", + "initial_train": "T3", + "initial_disposition": "workspace_rehearsal_map_slice", + "head_sha": "e1673b5968411cde72ce8945281c5db5779e145f", + "head_sha_status": "exact_current_head" + }, + { + "number": 1009, + "title": "feat(activation): license a demo song and name first-run next actions", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/1009", + "initial_train": "T5", + "initial_disposition": "licensed_demo_first_run_activation", + "head_sha": "86004787d77cdcfbcc3d411124e9b01ed2898a71", + "head_sha_status": "exact_current_head" + }, + { + "number": 1010, + "title": "feat(workspace): name tonight's first tuning plan on the map", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/1010", + "initial_train": "T3", + "initial_disposition": "workspace_rehearsal_map_slice", + "head_sha": "5b07db76282e89bf5c7aefb2cd680343c2d63f05", + "head_sha_status": "exact_current_head" + }, + { + "number": 1011, + "title": "feat(workspace): name tonight's first dynamics plan on the map", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/1011", + "initial_train": "T3", + "initial_disposition": "workspace_rehearsal_map_slice", + "head_sha": "1d077d091025ab90b2831069a689b4bddb782725", + "head_sha_status": "exact_current_head" + }, + { + "number": 1012, + "title": "feat(workspace): name tonight's first articulation plan on the map", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/1012", + "initial_train": "T3", + "initial_disposition": "workspace_rehearsal_map_slice", + "head_sha": "039b68cf556d997c2a9145cc0250b93aa254f0c7", + "head_sha_status": "exact_current_head" + }, + { + "number": 1013, + "title": "feat(workspace): name tonight's first voicing plan on the map", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/1013", + "initial_train": "T3", + "initial_disposition": "workspace_rehearsal_map_slice", + "head_sha": "812d60a93cf4ee5ac9cdceb4f96ce4b9f09cf7b5", + "head_sha_status": "exact_current_head" } ] } diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index abf62808c..ce2345994 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -44,11 +44,33 @@ The investigation covered: - repository metadata and the protected `develop` tree; - README, architecture, acceptance, release, deployment, package, Tauri, workflow, Rust, TypeScript, and Python surfaces; -- all 83 open pull requests returned by the connected GitHub account; +- all 122 open pull requests returned by the connected GitHub account, with exact-head capture on 2026-08-24; - existing canonical product/security issues; - saved Figma file `BP30foevuRtufwRpTknZUw`; - current official guidance for Tauri signing/updating, Apple notarization, Microsoft signing, WCAG 2.2, and music-information-retrieval evaluation. + +## Live refresh findings (2026-08-24) + +The 2026-08-24 exact-head refresh surfaced three systemic merge-gate conditions that sit above any +individual feature review: + +1. **Canonical npm security baseline is still unmerged (`#783`, train `T0`).** Nearly every open + feature head inherits `security-audit` and `trivy-fs` failures from `package-lock.json`: + `pdfjs-dist` (GHSA-hq66-cqwq-w95j / CVE-2026-16633, HIGH), four `undici` HIGH advisories + (GHSA-8xcm-r25x-g524, GHSA-4cwx-7wf7-3272, GHSA-m8rv-5g2x-5cg5, GHSA-jr45-8vmc-qm54, + GHSA-v3r7-h72x-cjcm), and a HIGH `nanoid` advisory. `#783` is green on both checks; merging it + first and updating dependent branches is the single highest-leverage unblock for the queue. +2. **Central Strix gate provider outage is intermittent, not per-PR.** Required-check runs show + `nvidia_nim` model failures followed by a direct-OpenAI fallback that cannot connect + ("404 page not found"), failing closed per org policy. The fix belongs in + `ContextualWisdomLab/.github` (provider-prefix migration already in flight there); BandScope + must not weaken or bypass the gate locally. +3. **The queue grew 83 → 122 without triage.** All 40 additions after PR #957 were classified into + trains during this refresh (see manifest); none changes the completion program below. + +These are environment/gate facts recorded as evidence. They do not change product priorities. + ## Buyer-visible completion gaps | Priority | Gap | Current evidence | Buyer impact | Owning issue | @@ -61,7 +83,7 @@ The investigation covered: | P1 | Diagnostics and support evidence | Multiple redaction fixes exist, but no single typed local diagnostics contract or previewable support bundle exists | Failures remain expensive to diagnose and risk leaking private paths/content | [#963](https://github.com/ContextualWisdomLab/bandscope/issues/963) | | P1 | First-run activation | Empty states and next-action PRs exist, but no licensed demo proves time-to-first-rehearsal through the production path | A new buyer cannot evaluate value quickly or reproducibly | [#964](https://github.com/ContextualWisdomLab/bandscope/issues/964) | | P1 | Design and accessibility parity | Storybook work is open. The saved Figma file claims 28 pages but currently exposes two top-level pages; its footer and protected repository/runtime metadata both cite `0.1.3`, so the remaining gap is implementation/design parity rather than version drift | Design status can be mistaken for shipped behavior; assistive-technology acceptance is incomplete | [#965](https://github.com/ContextualWisdomLab/bandscope/issues/965) | -| P0 | PR queue convergence | 83 open PRs include canonical bases, small feature slices, security repairs, dependency updates, and overlapping writers | Review drift and ancestry conflicts prevent coherent delivery | [#966](https://github.com/ContextualWisdomLab/bandscope/issues/966) | +| P0 | PR queue convergence | 122 open PRs (2026-08-24 exact-head capture) include canonical bases, small feature slices, security repairs, dependency updates, and overlapping writers; systemic `security-audit`/`trivy-fs` failures block nearly all feature heads until the `#783` npm baseline lands | Review drift, ancestry conflicts, and inherited-base failures prevent coherent delivery | [#966](https://github.com/ContextualWisdomLab/bandscope/issues/966) | ## Completion program @@ -82,104 +104,144 @@ Existing canonical work that must be preserved rather than duplicated includes [ | Train | Responsibility | Initial live PR count | Completion issue | |---|---|---:|---| -| `T0` | Dependency, toolchain, workflow and quality base | 30 | [#966](https://github.com/ContextualWisdomLab/bandscope/issues/966) | -| `T1` | Local input, filesystem authority, resource admission and cancellation | 6 | [#781](https://github.com/ContextualWisdomLab/bandscope/issues/781), [#962](https://github.com/ContextualWisdomLab/bandscope/issues/962) | -| `T2` | Scientific accuracy, MIR evaluation and numerical parity | 8 | [#770](https://github.com/ContextualWisdomLab/bandscope/issues/770) | -| `T3` | Rehearsal actions and active-player vertical | 23 | [#961](https://github.com/ContextualWisdomLab/bandscope/issues/961) | -| `T4` | Project portability, handoff and interoperability | 2 | [#739](https://github.com/ContextualWisdomLab/bandscope/issues/739), [#962](https://github.com/ContextualWisdomLab/bandscope/issues/962) | -| `T5` | Activation, UI system, Storybook and accessibility | 8 | [#964](https://github.com/ContextualWisdomLab/bandscope/issues/964), [#965](https://github.com/ContextualWisdomLab/bandscope/issues/965) | -| `T6` | Diagnostics, redaction, security evidence and supportability | 6 | [#963](https://github.com/ContextualWisdomLab/bandscope/issues/963) | -| `T7` | Signed commercial release and updater | 0 current implementation PRs in this snapshot | [#960](https://github.com/ContextualWisdomLab/bandscope/issues/960) | +| `T0` | Dependency, toolchain, workflow and quality base | 31 | [#966](https://github.com/ContextualWisdomLab/bandscope/issues/966) | +| `T1` | Local input, filesystem authority, resource admission and cancellation | 7 | [#781](https://github.com/ContextualWisdomLab/bandscope/issues/781), [#962](https://github.com/ContextualWisdomLab/bandscope/issues/962) | +| `T2` | Scientific accuracy, MIR evaluation and numerical parity | 9 | [#770](https://github.com/ContextualWisdomLab/bandscope/issues/770) | +| `T3` | Rehearsal actions and active-player vertical | 52 | [#961](https://github.com/ContextualWisdomLab/bandscope/issues/961) | +| `T4` | Project portability, handoff and interoperability | 4 | [#739](https://github.com/ContextualWisdomLab/bandscope/issues/739), [#962](https://github.com/ContextualWisdomLab/bandscope/issues/962) | +| `T5` | Activation, UI system, Storybook and accessibility | 12 | [#964](https://github.com/ContextualWisdomLab/bandscope/issues/964), [#965](https://github.com/ContextualWisdomLab/bandscope/issues/965) | +| `T6` | Diagnostics, redaction, security evidence and supportability | 7 | [#963](https://github.com/ContextualWisdomLab/bandscope/issues/963) | +| `T7` | Signed commercial release and updater | 0 | [#960](https://github.com/ContextualWisdomLab/bandscope/issues/960) | The routing below is an **initial product-boundary classification**, not a merge-readiness verdict. Issue #966 must refresh exact head SHAs, dependency edges, current checks, current reviews, unresolved threads, and succession before any action. ## Current open PR inventory -| PR | Title | Initial train | Required next action | -|---:|---|---|---| -| [731](https://github.com/ContextualWisdomLab/bandscope/pull/731) | 🎨 Palette: 향상된 접근성을 위해 아이콘 버튼에 툴팁 및 aria-disabled 적용 | `T5` | Triage against the live exact head before action | -| [732](https://github.com/ContextualWisdomLab/bandscope/pull/732) | ⚡ Chords: vectorize HMM reference and correct relative-key prior | `T2` | Triage against the live exact head before action | -| [737](https://github.com/ContextualWisdomLab/bandscope/pull/737) | feat(integration): add versioned naruon rehearsal handoff | `T4` | Triage against the live exact head before action | -| [740](https://github.com/ContextualWisdomLab/bandscope/pull/740) | feat: import rehearsal handoffs for focused reanalysis | `T4` | Triage against the live exact head before action | -| [744](https://github.com/ContextualWisdomLab/bandscope/pull/744) | feat(i18n): localize Workspace controls and harden interpolation | `T5` | Triage against the live exact head before action | -| [746](https://github.com/ContextualWisdomLab/bandscope/pull/746) | perf(segmenter): vectorize checkerboard novelty reference | `T2` | Triage against the live exact head before action | -| [750](https://github.com/ContextualWisdomLab/bandscope/pull/750) | fix(score): validate PDF bridge byte arrays without coercion | `T1` | Triage against the live exact head before action | -| [754](https://github.com/ContextualWisdomLab/bandscope/pull/754) | build(deps): bump time from 0.3.53 to 0.3.55 in /apps/desktop/src-tauri | `T0` | Triage against the live exact head before action | -| [776](https://github.com/ContextualWisdomLab/bandscope/pull/776) | feat(chords): surface actionable rehearsal guidance | `T3` | Triage against the live exact head before action | -| [780](https://github.com/ContextualWisdomLab/bandscope/pull/780) | fix(security): keep every CodeQL Action phase on one revision | `T0` | Triage against the live exact head before action | -| [783](https://github.com/ContextualWisdomLab/bandscope/pull/783) | fix(security): establish canonical npm, PDF.js, Nanoid, and Undici baseline | `T0` | Triage against the live exact head before action | -| [811](https://github.com/ContextualWisdomLab/bandscope/pull/811) | 🔒 [보안] CLI 무제한 파일 읽기 취약점 수정 | `T1` | Triage against the live exact head before action | -| [824](https://github.com/ContextualWisdomLab/bandscope/pull/824) | 🧪 테스트: 줄기 분리 시간 초과 처리 테스트 추가 | `T1` | Triage against the live exact head before action | -| [826](https://github.com/ContextualWisdomLab/bandscope/pull/826) | 🧪 [테스트 개선] TemporalAnalyzer 분석 오류 테스트 추가 | `T6` | Triage against the live exact head before action | -| [827](https://github.com/ContextualWisdomLab/bandscope/pull/827) | fix(desktop): remove synthetic browser analysis success | `T2` | Triage against the live exact head before action | -| [828](https://github.com/ContextualWisdomLab/bandscope/pull/828) | test(analysis): govern real YouTube known-stem benchmark | `T2` | Triage against the live exact head before action | -| [833](https://github.com/ContextualWisdomLab/bandscope/pull/833) | 🎨 Palette: 코드 수정 버튼에 접근성 있는 툴팁 지원 추가 | `T5` | Triage against the live exact head before action | -| [834](https://github.com/ContextualWisdomLab/bandscope/pull/834) | ⚡ Bolt: O(1) 메모리로 순차적 코드 변경 횟수 계산 최적화 | `T2` | Triage against the live exact head before action | -| [845](https://github.com/ContextualWisdomLab/bandscope/pull/845) | feat(analysis): CSV 큐시트 내보내기 기능 및 수식 주입 보안 로직 추가 | `T3` | Triage against the live exact head before action | -| [849](https://github.com/ContextualWisdomLab/bandscope/pull/849) | ⚡ Bolt: 차트 내보내기(chart.py)의 중복 제거 로직 O(N^2)에서 O(N)으로 최적화 | `T5` | Triage against the live exact head before action | -| [850](https://github.com/ContextualWisdomLab/bandscope/pull/850) | feat: add Part Handoff Map UI component to workspace | `T3` | Triage against the live exact head before action | -| [858](https://github.com/ContextualWisdomLab/bandscope/pull/858) | fix(security): bind analysis paths to filesystem authority | `T1` | Triage against the live exact head before action | -| [859](https://github.com/ContextualWisdomLab/bandscope/pull/859) | ⚡ Bolt: GrooveMap의 maxTime 계산 성능 개선 | `T5` | Triage against the live exact head before action | -| [861](https://github.com/ContextualWisdomLab/bandscope/pull/861) | test(quality): enforce Python branch coverage | `T0` | Triage against the live exact head before action | -| [865](https://github.com/ContextualWisdomLab/bandscope/pull/865) | fix(score): bound native PDF reads before allocation | `T1` | Triage against the live exact head before action | -| [866](https://github.com/ContextualWisdomLab/bandscope/pull/866) | fix(audio): establish canonical local-audio resource policy | `T1` | Triage against the live exact head before action | -| [867](https://github.com/ContextualWisdomLab/bandscope/pull/867) | test(supply-chain): preserve simple dependency-path cycle semantics | `T0` | Triage against the live exact head before action | -| [873](https://github.com/ContextualWisdomLab/bandscope/pull/873) | feat(roles): wire measured register overlap into section warnings | `T2` | Triage against the live exact head before action | -| [874](https://github.com/ContextualWisdomLab/bandscope/pull/874) | feat(workspace): name the parts to lock in first | `T3` | Triage against the live exact head before action | -| [881](https://github.com/ContextualWisdomLab/bandscope/pull/881) | test(ci): lock local OpenCode small_model to NVIDIA NIM | `T0` | Triage against the live exact head before action | -| [884](https://github.com/ContextualWisdomLab/bandscope/pull/884) | feat(workspace): open Stem Lab as honest isolation lanes | `T3` | Triage against the live exact head before action | -| [891](https://github.com/ContextualWisdomLab/bandscope/pull/891) | test(analysis): lock verse/chorus chord recovery on a known take | `T2` | Triage against the live exact head before action | -| [892](https://github.com/ContextualWisdomLab/bandscope/pull/892) | fix(analysis): score C major acceptance from decoded WAV bytes | `T2` | Triage against the live exact head before action | -| [894](https://github.com/ContextualWisdomLab/bandscope/pull/894) | fix(security): drop persisted credentials before dependency lifecycle code | `T0` | Triage against the live exact head before action | -| [895](https://github.com/ContextualWisdomLab/bandscope/pull/895) | fix(workflows): audit orphaned Actions registry identities | `T0` | Triage against the live exact head before action | -| [896](https://github.com/ContextualWisdomLab/bandscope/pull/896) | build(node): coordinate Node 22.22.2 floor with jsdom 30 | `T0` | Triage against the live exact head before action | -| [897](https://github.com/ContextualWisdomLab/bandscope/pull/897) | feat(workspace): Storybook tokens for rehearsal components | `T5` | Triage against the live exact head before action | -| [898](https://github.com/ContextualWisdomLab/bandscope/pull/898) | feat(workspace): put the next rehearsal action on empty and error cards | `T5` | Triage against the live exact head before action | -| [899](https://github.com/ContextualWisdomLab/bandscope/pull/899) | feat(workspace): open existing rehearsal surfaces from the sidebar | `T5` | Triage against the live exact head before action | -| [900](https://github.com/ContextualWisdomLab/bandscope/pull/900) | feat(workspace): name tonight's export and priority actions | `T3` | Triage against the live exact head before action | -| [901](https://github.com/ContextualWisdomLab/bandscope/pull/901) | feat(workspace): start tonight's first part from the ready board | `T3` | Triage against the live exact head before action | -| [903](https://github.com/ContextualWisdomLab/bandscope/pull/903) | feat(workspace): loop tonight's first section on the map | `T3` | Triage against the live exact head before action | -| [904](https://github.com/ContextualWisdomLab/bandscope/pull/904) | feat(workspace): count in tonight's first section on the map | `T3` | Triage against the live exact head before action | -| [905](https://github.com/ContextualWisdomLab/bandscope/pull/905) | feat(workspace): open tonight's first notes on the groove map | `T3` | Triage against the live exact head before action | -| [907](https://github.com/ContextualWisdomLab/bandscope/pull/907) | chore(env): add Cloud Agent environment config | `T0` | Triage against the live exact head before action | -| [910](https://github.com/ContextualWisdomLab/bandscope/pull/910) | feat(workspace): set up tonight's part before the first entrance | `T3` | Triage against the live exact head before action | -| [912](https://github.com/ContextualWisdomLab/bandscope/pull/912) | feat(workspace): guide tonight's first entrance on map and player | `T3` | Triage against the live exact head before action | -| [913](https://github.com/ContextualWisdomLab/bandscope/pull/913) | feat(workspace): guide tonight's first lyric cue on map and player | `T3` | Triage against the live exact head before action | -| [914](https://github.com/ContextualWisdomLab/bandscope/pull/914) | feat(workspace): guide tonight's first dropout on map and player | `T3` | Triage against the live exact head before action | -| [916](https://github.com/ContextualWisdomLab/bandscope/pull/916) | feat(workspace): guide tonight's first pickup on map and player | `T3` | Triage against the live exact head before action | -| [918](https://github.com/ContextualWisdomLab/bandscope/pull/918) | build(deps): bump uuid from 1.23.4 to 1.24.1 in /apps/desktop/src-tauri | `T0` | Triage against the live exact head before action | -| [919](https://github.com/ContextualWisdomLab/bandscope/pull/919) | build(deps): update numba requirement from <0.67.0 to <0.68.0 in /services/analysis-engine | `T0` | Triage against the live exact head before action | -| [920](https://github.com/ContextualWisdomLab/bandscope/pull/920) | build(deps): bump react and @types/react | `T0` | Triage against the live exact head before action | -| [921](https://github.com/ContextualWisdomLab/bandscope/pull/921) | build(deps): bump @base-ui/react from 1.5.0 to 1.7.0 | `T0` | Triage against the live exact head before action | -| [922](https://github.com/ContextualWisdomLab/bandscope/pull/922) | build(deps-dev): bump storybook from 10.4.6 to 10.5.8 | `T0` | Triage against the live exact head before action | -| [923](https://github.com/ContextualWisdomLab/bandscope/pull/923) | build(deps-dev): bump @storybook/react-vite from 10.4.6 to 10.5.8 | `T0` | Triage against the live exact head before action | -| [924](https://github.com/ContextualWisdomLab/bandscope/pull/924) | build(deps): bump github/codeql-action/init from 4.37.0 to 4.37.7 | `T0` | Triage against the live exact head before action | -| [925](https://github.com/ContextualWisdomLab/bandscope/pull/925) | build(deps-dev): bump typescript-eslint from 8.63.0 to 8.67.0 | `T0` | Triage against the live exact head before action | -| [926](https://github.com/ContextualWisdomLab/bandscope/pull/926) | build(deps): bump lucide-react from 1.24.0 to 1.31.0 | `T0` | Triage against the live exact head before action | -| [927](https://github.com/ContextualWisdomLab/bandscope/pull/927) | build(deps): bump sonner from 2.0.7 to 2.0.8 | `T0` | Triage against the live exact head before action | -| [928](https://github.com/ContextualWisdomLab/bandscope/pull/928) | build(deps-dev): bump eslint-plugin-jsdoc from 63.0.13 to 64.2.0 | `T0` | Triage against the live exact head before action | -| [929](https://github.com/ContextualWisdomLab/bandscope/pull/929) | build(deps-dev): bump @testing-library/jest-dom from 6.9.1 to 7.0.1 | `T0` | Triage against the live exact head before action | -| [930](https://github.com/ContextualWisdomLab/bandscope/pull/930) | build(deps): bump github/codeql-action/autobuild from 4.37.0 to 4.37.7 | `T0` | Triage against the live exact head before action | -| [931](https://github.com/ContextualWisdomLab/bandscope/pull/931) | build(deps): bump astral-sh/setup-uv from 8.3.2 to 10.0.1 | `T0` | Triage against the live exact head before action | -| [932](https://github.com/ContextualWisdomLab/bandscope/pull/932) | build(deps): bump github/codeql-action/analyze from 4.37.0 to 4.37.7 | `T0` | Triage against the live exact head before action | -| [933](https://github.com/ContextualWisdomLab/bandscope/pull/933) | build(deps): bump github/codeql-action/upload-sarif from 4.37.0 to 4.37.7 | `T0` | Triage against the live exact head before action | -| [934](https://github.com/ContextualWisdomLab/bandscope/pull/934) | feat(workspace): guide tonight's first stop on map and player | `T3` | Triage against the live exact head before action | -| [936](https://github.com/ContextualWisdomLab/bandscope/pull/936) | build(deps): bump the uv group across 1 directory with 2 updates | `T0` | Triage against the live exact head before action | -| [937](https://github.com/ContextualWisdomLab/bandscope/pull/937) | feat(workspace): guide tonight's first labeled handoff on map and player | `T3` | Triage against the live exact head before action | -| [939](https://github.com/ContextualWisdomLab/bandscope/pull/939) | feat(workspace): guide tonight's first chorus on map and player | `T3` | Triage against the live exact head before action | -| [941](https://github.com/ContextualWisdomLab/bandscope/pull/941) | 🛡️ Sentinel: CSV 수식 주입 NUL·전각 연산자 우회 차단 | `T6` | Triage against the live exact head before action | -| [942](https://github.com/ContextualWisdomLab/bandscope/pull/942) | build(deps): bump react-dom and @types/react-dom | `T0` | Triage against the live exact head before action | -| [943](https://github.com/ContextualWisdomLab/bandscope/pull/943) | feat(workspace): guide tonight's first intro on map and player | `T3` | Triage against the live exact head before action | -| [944](https://github.com/ContextualWisdomLab/bandscope/pull/944) | build(rust): pin all product and release lanes to 1.97.1 | `T0` | Triage against the live exact head before action | -| [946](https://github.com/ContextualWisdomLab/bandscope/pull/946) | feat(workspace): guide tonight's first bridge on map and player | `T3` | Triage against the live exact head before action | -| [947](https://github.com/ContextualWisdomLab/bandscope/pull/947) | feat(workspace): guide tonight's first verse on map and player | `T3` | Triage against the live exact head before action | -| [948](https://github.com/ContextualWisdomLab/bandscope/pull/948) | fix(security): retire quick-xml RustSec exceptions | `T0` | Triage against the live exact head before action | -| [949](https://github.com/ContextualWisdomLab/bandscope/pull/949) | fix(security): redact key detector dependency failures | `T6` | Triage against the live exact head before action | -| [950](https://github.com/ContextualWisdomLab/bandscope/pull/950) | fix(security): redact temporal detector failure logs | `T6` | Triage against the live exact head before action | -| [951](https://github.com/ContextualWisdomLab/bandscope/pull/951) | fix(security): redact range-analysis failure logs | `T6` | Triage against the live exact head before action | -| [955](https://github.com/ContextualWisdomLab/bandscope/pull/955) | feat(workspace): guide tonight's first pre-chorus on map and player | `T3` | Triage against the live exact head before action | -| [956](https://github.com/ContextualWisdomLab/bandscope/pull/956) | fix(security): redact articulation failure logs | `T6` | Triage against the live exact head before action | -| [957](https://github.com/ContextualWisdomLab/bandscope/pull/957) | feat(workspace): name tonight's first playable range on the map | `T3` | Triage against the live exact head before action | +| PR | Title | Initial train | Exact head (2026-08-24) | Required next action | +|---:|---|---|---|---| +| [731](https://github.com/ContextualWisdomLab/bandscope/pull/731) | 🎨 Palette: 향상된 접근성을 위해 아이콘 버튼에 툴팁 및 aria-disabled 적용 | `T5` | `5ea5663f26e3` | Triage against the live exact head before action | +| [732](https://github.com/ContextualWisdomLab/bandscope/pull/732) | ⚡ Chords: vectorize HMM reference and correct relative-key prior | `T2` | `830dd4c982b1` | Triage against the live exact head before action | +| [737](https://github.com/ContextualWisdomLab/bandscope/pull/737) | feat(integration): add versioned naruon rehearsal handoff | `T4` | `82ae343e9911` | Triage against the live exact head before action | +| [740](https://github.com/ContextualWisdomLab/bandscope/pull/740) | feat: import rehearsal handoffs for focused reanalysis | `T4` | `4761a882d1b3` | Triage against the live exact head before action | +| [744](https://github.com/ContextualWisdomLab/bandscope/pull/744) | feat(i18n): localize Workspace controls and harden interpolation | `T5` | `0ca00b110d04` | Triage against the live exact head before action | +| [746](https://github.com/ContextualWisdomLab/bandscope/pull/746) | perf(segmenter): vectorize checkerboard novelty reference | `T2` | `9d0def7d5038` | Triage against the live exact head before action | +| [750](https://github.com/ContextualWisdomLab/bandscope/pull/750) | fix(score): validate PDF bridge byte arrays without coercion | `T1` | `d982adef81cd` | Triage against the live exact head before action | +| [754](https://github.com/ContextualWisdomLab/bandscope/pull/754) | build(deps): bump time from 0.3.53 to 0.3.55 in /apps/desktop/src-tauri | `T0` | `966d5f1204ec` | Triage against the live exact head before action | +| [776](https://github.com/ContextualWisdomLab/bandscope/pull/776) | feat(chords): surface actionable rehearsal guidance | `T3` | `8b38e4885e21` | Triage against the live exact head before action | +| [780](https://github.com/ContextualWisdomLab/bandscope/pull/780) | fix(security): keep every CodeQL Action phase on one revision | `T0` | `738495ca9d42` | Triage against the live exact head before action | +| [783](https://github.com/ContextualWisdomLab/bandscope/pull/783) | fix(security): establish canonical npm, PDF.js, Nanoid, and Undici baseline | `T0` | `1168c8f4257d` | Triage against the live exact head before action | +| [811](https://github.com/ContextualWisdomLab/bandscope/pull/811) | 🔒 [보안] CLI 무제한 파일 읽기 취약점 수정 | `T1` | `54c645c9dcac` | Triage against the live exact head before action | +| [824](https://github.com/ContextualWisdomLab/bandscope/pull/824) | 🧪 테스트: 줄기 분리 시간 초과 처리 테스트 추가 | `T1` | `f4d01558eb3c` | Triage against the live exact head before action | +| [826](https://github.com/ContextualWisdomLab/bandscope/pull/826) | 🧪 [테스트 개선] TemporalAnalyzer 분석 오류 테스트 추가 | `T6` | `9002ff8b9227` | Triage against the live exact head before action | +| [827](https://github.com/ContextualWisdomLab/bandscope/pull/827) | fix(desktop): remove synthetic browser analysis success | `T2` | `a2e5d260e68a` | Triage against the live exact head before action | +| [828](https://github.com/ContextualWisdomLab/bandscope/pull/828) | test(analysis): govern real YouTube known-stem benchmark | `T2` | `9331b406e7fb` | Triage against the live exact head before action | +| [833](https://github.com/ContextualWisdomLab/bandscope/pull/833) | 🎨 Palette: 코드 수정 버튼에 접근성 있는 툴팁 지원 추가 | `T5` | `3206c259afcc` | Triage against the live exact head before action | +| [834](https://github.com/ContextualWisdomLab/bandscope/pull/834) | ⚡ Bolt: O(1) 메모리로 순차적 코드 변경 횟수 계산 최적화 | `T2` | `c686ae450677` | Triage against the live exact head before action | +| [845](https://github.com/ContextualWisdomLab/bandscope/pull/845) | feat(analysis): CSV 큐시트 내보내기 기능 및 수식 주입 보안 로직 추가 | `T3` | `d1dc4da58332` | Triage against the live exact head before action | +| [849](https://github.com/ContextualWisdomLab/bandscope/pull/849) | ⚡ Bolt: 차트 내보내기(chart.py)의 중복 제거 로직 O(N^2)에서 O(N)으로 최적화 | `T5` | `e3268c8bf632` | Triage against the live exact head before action | +| [850](https://github.com/ContextualWisdomLab/bandscope/pull/850) | feat: add Part Handoff Map UI component to workspace | `T3` | `adbe3da06172` | Triage against the live exact head before action | +| [858](https://github.com/ContextualWisdomLab/bandscope/pull/858) | fix(security): bind analysis paths to filesystem authority | `T1` | `bedc2069494c` | Triage against the live exact head before action | +| [859](https://github.com/ContextualWisdomLab/bandscope/pull/859) | ⚡ Bolt: GrooveMap의 maxTime 계산 성능 개선 | `T5` | `38e1027d3a96` | Triage against the live exact head before action | +| [861](https://github.com/ContextualWisdomLab/bandscope/pull/861) | test(quality): enforce Python branch coverage | `T0` | `6afbc9fff585` | Triage against the live exact head before action | +| [865](https://github.com/ContextualWisdomLab/bandscope/pull/865) | fix(score): bound native PDF reads before allocation | `T1` | `f86e266b2ab2` | Triage against the live exact head before action | +| [866](https://github.com/ContextualWisdomLab/bandscope/pull/866) | fix(audio): establish canonical local-audio resource policy | `T1` | `223dd78126de` | Triage against the live exact head before action | +| [867](https://github.com/ContextualWisdomLab/bandscope/pull/867) | test(supply-chain): preserve simple dependency-path cycle semantics | `T0` | `f1ac4167b8b7` | Triage against the live exact head before action | +| [873](https://github.com/ContextualWisdomLab/bandscope/pull/873) | feat(roles): wire measured register overlap into section warnings | `T2` | `c30be7afa82b` | Triage against the live exact head before action | +| [874](https://github.com/ContextualWisdomLab/bandscope/pull/874) | feat(workspace): name the parts to lock in first | `T3` | `05935e02f293` | Triage against the live exact head before action | +| [881](https://github.com/ContextualWisdomLab/bandscope/pull/881) | test(ci): lock local OpenCode small_model to NVIDIA NIM | `T0` | `4e07356d1ece` | Triage against the live exact head before action | +| [884](https://github.com/ContextualWisdomLab/bandscope/pull/884) | feat(workspace): open Stem Lab as honest isolation lanes | `T3` | `e33ca56a3922` | Triage against the live exact head before action | +| [891](https://github.com/ContextualWisdomLab/bandscope/pull/891) | test(analysis): lock verse/chorus chord recovery on a known take | `T2` | `92fe9155a189` | Triage against the live exact head before action | +| [892](https://github.com/ContextualWisdomLab/bandscope/pull/892) | fix(analysis): score C major acceptance from decoded WAV bytes | `T2` | `40f138e00ece` | Triage against the live exact head before action | +| [894](https://github.com/ContextualWisdomLab/bandscope/pull/894) | fix(security): drop persisted credentials before dependency lifecycle code | `T0` | `475adeba6210` | Triage against the live exact head before action | +| [895](https://github.com/ContextualWisdomLab/bandscope/pull/895) | fix(workflows): audit orphaned Actions registry identities | `T0` | `9de29814cb30` | Triage against the live exact head before action | +| [896](https://github.com/ContextualWisdomLab/bandscope/pull/896) | build(node): coordinate Node 22.22.2 floor with jsdom 30 | `T0` | `c07e51639dbb` | Triage against the live exact head before action | +| [897](https://github.com/ContextualWisdomLab/bandscope/pull/897) | feat(workspace): Storybook tokens for rehearsal components | `T5` | `d31a6ce740f7` | Triage against the live exact head before action | +| [898](https://github.com/ContextualWisdomLab/bandscope/pull/898) | feat(workspace): put the next rehearsal action on empty and error cards | `T5` | `48c6084e9e8e` | Triage against the live exact head before action | +| [899](https://github.com/ContextualWisdomLab/bandscope/pull/899) | feat(workspace): open existing rehearsal surfaces from the sidebar | `T5` | `b263ac441df5` | Triage against the live exact head before action | +| [900](https://github.com/ContextualWisdomLab/bandscope/pull/900) | feat(workspace): name tonight's export and priority actions | `T3` | `0c09689fc981` | Triage against the live exact head before action | +| [901](https://github.com/ContextualWisdomLab/bandscope/pull/901) | feat(workspace): start tonight's first part from the ready board | `T3` | `940ff24aaeac` | Triage against the live exact head before action | +| [903](https://github.com/ContextualWisdomLab/bandscope/pull/903) | feat(workspace): loop tonight's first section on the map | `T3` | `1f3e8c75c572` | Triage against the live exact head before action | +| [905](https://github.com/ContextualWisdomLab/bandscope/pull/905) | feat(workspace): open tonight's first notes on the groove map | `T3` | `a1a38fc4e537` | Triage against the live exact head before action | +| [907](https://github.com/ContextualWisdomLab/bandscope/pull/907) | chore(env): add Cloud Agent environment config | `T0` | `b0972c5ad01c` | Triage against the live exact head before action | +| [910](https://github.com/ContextualWisdomLab/bandscope/pull/910) | feat(workspace): set up tonight's part before the first entrance | `T3` | `61aa51f274c0` | Triage against the live exact head before action | +| [912](https://github.com/ContextualWisdomLab/bandscope/pull/912) | feat(workspace): guide tonight's first entrance on map and player | `T3` | `b8f588ad6b0d` | Triage against the live exact head before action | +| [913](https://github.com/ContextualWisdomLab/bandscope/pull/913) | feat(workspace): guide tonight's first lyric cue on map and player | `T3` | `c56a58933c41` | Triage against the live exact head before action | +| [914](https://github.com/ContextualWisdomLab/bandscope/pull/914) | feat(workspace): guide tonight's first dropout on map and player | `T3` | `a0b5fe924361` | Triage against the live exact head before action | +| [916](https://github.com/ContextualWisdomLab/bandscope/pull/916) | feat(workspace): guide tonight's first pickup on map and player | `T3` | `6657a47b706e` | Triage against the live exact head before action | +| [918](https://github.com/ContextualWisdomLab/bandscope/pull/918) | build(deps): bump uuid from 1.23.4 to 1.24.1 in /apps/desktop/src-tauri | `T0` | `77a2c35a21ee` | Triage against the live exact head before action | +| [919](https://github.com/ContextualWisdomLab/bandscope/pull/919) | build(deps): update numba requirement from <0.67.0 to <0.68.0 in /services/analysis-engine | `T0` | `b113cec48235` | Triage against the live exact head before action | +| [920](https://github.com/ContextualWisdomLab/bandscope/pull/920) | build(deps): bump react and @types/react | `T0` | `477fa3e363e7` | Triage against the live exact head before action | +| [921](https://github.com/ContextualWisdomLab/bandscope/pull/921) | build(deps): bump @base-ui/react from 1.5.0 to 1.7.0 | `T0` | `2840d0d0831d` | Triage against the live exact head before action | +| [922](https://github.com/ContextualWisdomLab/bandscope/pull/922) | build(deps-dev): bump storybook from 10.4.6 to 10.5.8 | `T0` | `5f6f0a809eb0` | Triage against the live exact head before action | +| [923](https://github.com/ContextualWisdomLab/bandscope/pull/923) | build(deps-dev): bump @storybook/react-vite from 10.4.6 to 10.5.8 | `T0` | `d3d65db35418` | Triage against the live exact head before action | +| [924](https://github.com/ContextualWisdomLab/bandscope/pull/924) | build(deps): bump github/codeql-action/init from 4.37.0 to 4.37.7 | `T0` | `bd7cd7d49cf5` | Triage against the live exact head before action | +| [925](https://github.com/ContextualWisdomLab/bandscope/pull/925) | build(deps-dev): bump typescript-eslint from 8.63.0 to 8.67.0 | `T0` | `525bb99a4553` | Triage against the live exact head before action | +| [926](https://github.com/ContextualWisdomLab/bandscope/pull/926) | build(deps): bump lucide-react from 1.24.0 to 1.31.0 | `T0` | `5b27740c54bb` | Triage against the live exact head before action | +| [927](https://github.com/ContextualWisdomLab/bandscope/pull/927) | build(deps): bump sonner from 2.0.7 to 2.0.8 | `T0` | `235019727df5` | Triage against the live exact head before action | +| [928](https://github.com/ContextualWisdomLab/bandscope/pull/928) | build(deps-dev): bump eslint-plugin-jsdoc from 63.0.13 to 64.2.0 | `T0` | `f87c0fbdca3b` | Triage against the live exact head before action | +| [929](https://github.com/ContextualWisdomLab/bandscope/pull/929) | build(deps-dev): bump @testing-library/jest-dom from 6.9.1 to 7.0.1 | `T0` | `0ce4cb84cbaf` | Triage against the live exact head before action | +| [930](https://github.com/ContextualWisdomLab/bandscope/pull/930) | build(deps): bump github/codeql-action/autobuild from 4.37.0 to 4.37.7 | `T0` | `927c473bbfef` | Triage against the live exact head before action | +| [931](https://github.com/ContextualWisdomLab/bandscope/pull/931) | build(deps): bump astral-sh/setup-uv from 8.3.2 to 10.0.1 | `T0` | `8aed8adac445` | Triage against the live exact head before action | +| [932](https://github.com/ContextualWisdomLab/bandscope/pull/932) | build(deps): bump github/codeql-action/analyze from 4.37.0 to 4.37.7 | `T0` | `57011511f876` | Triage against the live exact head before action | +| [933](https://github.com/ContextualWisdomLab/bandscope/pull/933) | build(deps): bump github/codeql-action/upload-sarif from 4.37.0 to 4.37.7 | `T0` | `dff69016587f` | Triage against the live exact head before action | +| [934](https://github.com/ContextualWisdomLab/bandscope/pull/934) | feat(workspace): guide tonight's first stop on map and player | `T3` | `3d796dfffd64` | Triage against the live exact head before action | +| [936](https://github.com/ContextualWisdomLab/bandscope/pull/936) | build(deps): bump the uv group across 1 directory with 2 updates | `T0` | `cb048b1f455b` | Triage against the live exact head before action | +| [937](https://github.com/ContextualWisdomLab/bandscope/pull/937) | feat(workspace): guide tonight's first labeled handoff on map and player | `T3` | `c3685178c6f3` | Triage against the live exact head before action | +| [939](https://github.com/ContextualWisdomLab/bandscope/pull/939) | feat(workspace): guide tonight's first chorus on map and player | `T3` | `d3c852894bc5` | Triage against the live exact head before action | +| [941](https://github.com/ContextualWisdomLab/bandscope/pull/941) | 🛡️ Sentinel: CSV 수식 주입 NUL·전각 연산자 우회 차단 | `T6` | `f7a2634607c5` | Triage against the live exact head before action | +| [942](https://github.com/ContextualWisdomLab/bandscope/pull/942) | build(deps): bump react-dom and @types/react-dom | `T0` | `647b883996ed` | Triage against the live exact head before action | +| [943](https://github.com/ContextualWisdomLab/bandscope/pull/943) | feat(workspace): guide tonight's first intro on map and player | `T3` | `4855357e19dd` | Triage against the live exact head before action | +| [944](https://github.com/ContextualWisdomLab/bandscope/pull/944) | build(rust): pin all product and release lanes to 1.97.1 | `T0` | `b0f8cf0de9f0` | Triage against the live exact head before action | +| [946](https://github.com/ContextualWisdomLab/bandscope/pull/946) | feat(workspace): guide tonight's first bridge on map and player | `T3` | `4d13986d7d48` | Triage against the live exact head before action | +| [947](https://github.com/ContextualWisdomLab/bandscope/pull/947) | feat(workspace): guide tonight's first verse on map and player | `T3` | `315e563ffe6d` | Triage against the live exact head before action | +| [948](https://github.com/ContextualWisdomLab/bandscope/pull/948) | fix(security): retire quick-xml RustSec exceptions | `T0` | `d415bc382167` | Triage against the live exact head before action | +| [949](https://github.com/ContextualWisdomLab/bandscope/pull/949) | fix(security): redact key detector dependency failures | `T6` | `6ee9ada2228a` | Triage against the live exact head before action | +| [950](https://github.com/ContextualWisdomLab/bandscope/pull/950) | fix(security): redact temporal detector failure logs | `T6` | `396d42017bd3` | Triage against the live exact head before action | +| [951](https://github.com/ContextualWisdomLab/bandscope/pull/951) | fix(security): redact range-analysis failure logs | `T6` | `9c64ca3cd3bd` | Triage against the live exact head before action | +| [955](https://github.com/ContextualWisdomLab/bandscope/pull/955) | feat(workspace): guide tonight's first pre-chorus on map and player | `T3` | `d1975e182f52` | Triage against the live exact head before action | +| [956](https://github.com/ContextualWisdomLab/bandscope/pull/956) | fix(security): redact articulation failure logs | `T6` | `2707ad39e019` | Triage against the live exact head before action | +| [957](https://github.com/ContextualWisdomLab/bandscope/pull/957) | feat(workspace): name tonight's first playable range on the map | `T3` | `4c4c045cfe1e` | Triage against the live exact head before action | +| [967](https://github.com/ContextualWisdomLab/bandscope/pull/967) | feat(operations): add privacy-safe support manifest boundary | `T6` | `b9881489d297` | Triage against the live exact head before action | +| [968](https://github.com/ContextualWisdomLab/bandscope/pull/968) | feat(readiness): establish BandScope 1.0 product-readiness baseline | `T0` | `db52897352ae` | Triage against the live exact head before action | +| [969](https://github.com/ContextualWisdomLab/bandscope/pull/969) | fix(design): restore Figma contract-page inventory and drift check | `T5` | `934b76286632` | Triage against the live exact head before action | +| [970](https://github.com/ContextualWisdomLab/bandscope/pull/970) | fix(project): stage new saves without clobbering known-good files | `T4` | `2e48e59916c8` | Triage against the live exact head before action | +| [971](https://github.com/ContextualWisdomLab/bandscope/pull/971) | feat(workspace): loop tonight's first section from the map | `T3` | `7c1b4973f9d7` | Triage against the live exact head before action | +| [972](https://github.com/ContextualWisdomLab/bandscope/pull/972) | feat(workspace): name the next rehearsal action from help | `T3` | `c0095ca8f401` | Triage against the live exact head before action | +| [974](https://github.com/ContextualWisdomLab/bandscope/pull/974) | feat(workspace): start analysis from the first-run card | `T5` | `fd33104ca880` | Triage against the live exact head before action | +| [976](https://github.com/ContextualWisdomLab/bandscope/pull/976) | feat(workspace): name the next action after analysis fails | `T3` | `d07dc57347ce` | Triage against the live exact head before action | +| [980](https://github.com/ContextualWisdomLab/bandscope/pull/980) | feat(workspace): name using your own song as the first next action | `T5` | `9bc2ead0d52f` | Triage against the live exact head before action | +| [981](https://github.com/ContextualWisdomLab/bandscope/pull/981) | feat(workspace): name Choose another song after local intake fails | `T3` | `9059b4ceb5de` | Triage against the live exact head before action | +| [982](https://github.com/ContextualWisdomLab/bandscope/pull/982) | feat(workspace): name Paste another YouTube link after import fails | `T3` | `54f5a59edfea` | Triage against the live exact head before action | +| [984](https://github.com/ContextualWisdomLab/bandscope/pull/984) | feat(workspace): name the next action after project load or save fails | `T4` | `8207813b5ef7` | Triage against the live exact head before action | +| [985](https://github.com/ContextualWisdomLab/bandscope/pull/985) | feat(analysis): enforce one canonical audio resource policy (#781) | `T1` | `d2cf2047af79` | Triage against the live exact head before action | +| [986](https://github.com/ContextualWisdomLab/bandscope/pull/986) | feat(workspace): guide tonight's first outro on the rehearsal map | `T3` | `4222d99c0257` | Triage against the live exact head before action | +| [987](https://github.com/ContextualWisdomLab/bandscope/pull/987) | feat(workspace): name tonight's tempo, starting chord, and transpose setup | `T3` | `ec87428f4ef9` | Triage against the live exact head before action | +| [989](https://github.com/ContextualWisdomLab/bandscope/pull/989) | feat(workspace): guide tonight's first tag on the rehearsal map | `T3` | `6b83256ff841` | Triage against the live exact head before action | +| [990](https://github.com/ContextualWisdomLab/bandscope/pull/990) | feat(workspace): name tonight's first simpler take on the map | `T3` | `3736a1647ec0` | Triage against the live exact head before action | +| [991](https://github.com/ContextualWisdomLab/bandscope/pull/991) | feat(workspace): name tonight's first groove on the map | `T3` | `ac360bd992fe` | Triage against the live exact head before action | +| [992](https://github.com/ContextualWisdomLab/bandscope/pull/992) | feat(workspace): name tonight's first overlap on the map | `T3` | `510347b801bf` | Triage against the live exact head before action | +| [993](https://github.com/ContextualWisdomLab/bandscope/pull/993) | feat(workspace): name tonight's first transition cue on the map | `T3` | `151d8ec73368` | Triage against the live exact head before action | +| [994](https://github.com/ContextualWisdomLab/bandscope/pull/994) | feat(workspace): name tonight's first transition on the map | `T3` | `6e8d5412d893` | Triage against the live exact head before action | +| [995](https://github.com/ContextualWisdomLab/bandscope/pull/995) | feat(workspace): name tonight's first count on the map | `T3` | `4d353ca99a38` | Triage against the live exact head before action | +| [996](https://github.com/ContextualWisdomLab/bandscope/pull/996) | feat(workspace): name tonight's first assignment on the map | `T3` | `54b75a265ef3` | Triage against the live exact head before action | +| [997](https://github.com/ContextualWisdomLab/bandscope/pull/997) | feat(workspace): name tonight's first open rehearsal comment on the map | `T3` | `fa4aadd429c2` | Triage against the live exact head before action | +| [998](https://github.com/ContextualWisdomLab/bandscope/pull/998) | feat(workspace): name tonight's first pending approval on the map | `T3` | `b67afb688866` | Triage against the live exact head before action | +| [999](https://github.com/ContextualWisdomLab/bandscope/pull/999) | ⚡ Bolt: 관측 확률 계산 배열 연산으로 벡터화 (성능 개선) | `T2` | `c11f5ed592bd` | Triage against the live exact head before action | +| [1000](https://github.com/ContextualWisdomLab/bandscope/pull/1000) | feat(workspace): name tonight's first blocked assignment on the map | `T3` | `6c6b3e75616e` | Triage against the live exact head before action | +| [1001](https://github.com/ContextualWisdomLab/bandscope/pull/1001) | feat(workspace): name tonight's first ear check on the map | `T3` | `38781c86dbcf` | Triage against the live exact head before action | +| [1002](https://github.com/ContextualWisdomLab/bandscope/pull/1002) | feat(workspace): name tonight's first confirmed chord on the map | `T3` | `1a1f231052d1` | Triage against the live exact head before action | +| [1003](https://github.com/ContextualWisdomLab/bandscope/pull/1003) | feat(workspace): name tonight's first harmonic explanation on the map | `T3` | `8f88db8c2313` | Triage against the live exact head before action | +| [1004](https://github.com/ContextualWisdomLab/bandscope/pull/1004) | feat(workspace): name tonight's first setup note on the map | `T3` | `9411885d68ed` | Triage against the live exact head before action | +| [1005](https://github.com/ContextualWisdomLab/bandscope/pull/1005) | feat(workspace): name tonight's first harmonic function on the map | `T3` | `00cf87dca1bd` | Triage against the live exact head before action | +| [1006](https://github.com/ContextualWisdomLab/bandscope/pull/1006) | feat(workspace): name tonight's first transposition plan on the map | `T3` | `f1e86e9c66c0` | Triage against the live exact head before action | +| [1007](https://github.com/ContextualWisdomLab/bandscope/pull/1007) | feat(workspace): name tonight's first part handoff on the map | `T3` | `e06ca27fad47` | Triage against the live exact head before action | +| [1008](https://github.com/ContextualWisdomLab/bandscope/pull/1008) | feat(workspace): name tonight's first capo plan on the map | `T3` | `e1673b596841` | Triage against the live exact head before action | +| [1009](https://github.com/ContextualWisdomLab/bandscope/pull/1009) | feat(activation): license a demo song and name first-run next actions | `T5` | `86004787d77c` | Triage against the live exact head before action | +| [1010](https://github.com/ContextualWisdomLab/bandscope/pull/1010) | feat(workspace): name tonight's first tuning plan on the map | `T3` | `5b07db76282e` | Triage against the live exact head before action | +| [1011](https://github.com/ContextualWisdomLab/bandscope/pull/1011) | feat(workspace): name tonight's first dynamics plan on the map | `T3` | `1d077d091025` | Triage against the live exact head before action | +| [1012](https://github.com/ContextualWisdomLab/bandscope/pull/1012) | feat(workspace): name tonight's first articulation plan on the map | `T3` | `039b68cf556d` | Triage against the live exact head before action | +| [1013](https://github.com/ContextualWisdomLab/bandscope/pull/1013) | feat(workspace): name tonight's first voicing plan on the map | `T3` | `812d60a93cf4` | Triage against the live exact head before action | + ## Required execution order @@ -246,7 +308,7 @@ Waiting for one train's external review or hosted runner is not a reason to stop ## Explicit non-goals -- Do not merge all 83 PRs merely to reduce the count. +- Do not merge all 122 open PRs merely to reduce the count. - Do not turn BandScope into a DAW, notation editor, or mandatory cloud service. - Do not claim unsigned validation artifacts are releases. - Do not use synthetic arrays, mocked browser success, skipped GPU execution, stale checks, or predecessor-head approval as product evidence. @@ -255,7 +317,8 @@ Waiting for one train's external review or hosted runner is not a reason to stop ## Known limitations of this snapshot -- Exact PR head SHAs were not frozen into this human baseline because active branches can advance while the inventory is reviewed. The machine-readable seed marks them for refresh before action. -- This document does not assert that any existing PR is approved, passing, or safe to merge. +- Exact PR head SHAs were frozen on 2026-08-24 into `docs/product-readiness/open-pr-queue.json`; any branch that advances after that capture invalidates its own row until the next refresh. +- The 2026-08-20 seed left head SHAs unfrozen; the 2026-08-24 refresh supersedes it with exact-head capture and explicit triage of the 40 additions (no `T8` remainder). +- This document does not assert that any existing PR is approved, passing, or safe to merge. In particular, the recorded `security-audit`, `trivy-fs`, and intermittent Strix failures are gate evidence, not per-change verdicts. - The Figma inspection is a point-in-time metadata/structure review; visual and interaction acceptance remains issue #965 work. - The investigation created requirements and a convergence plan. It did not implement, merge, sign, notarize, benchmark, or release the product. diff --git a/services/analysis-engine/tests/test_open_pr_queue_refresh.py b/services/analysis-engine/tests/test_open_pr_queue_refresh.py index a46e22bf1..58ecff0ef 100644 --- a/services/analysis-engine/tests/test_open_pr_queue_refresh.py +++ b/services/analysis-engine/tests/test_open_pr_queue_refresh.py @@ -125,8 +125,7 @@ def test_refresh_manifest_updates_exact_heads_and_adds_untriaged_live_prs() -> N assert refreshed["pull_requests"][0]["head_sha"] == "b" * 40 assert refreshed["pull_requests"][0]["head_sha_status"] == "exact_current_head" assert ( - refreshed["pull_requests"][0]["initial_disposition"] - == "canonical_dependency_security_base" + refreshed["pull_requests"][0]["initial_disposition"] == "canonical_dependency_security_base" ) assert refreshed["pull_requests"][1]["number"] == 1002 assert refreshed["pull_requests"][1]["initial_train"] == "T8" From f1bd911a606668decdc515bc3f4484d63e081f2c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 24 Aug 2026 05:22:12 -0700 Subject: [PATCH 22/77] test(readiness): reject ambiguous queue authority --- .../test_open_pr_queue_manifest_contract.py | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 services/analysis-engine/tests/test_open_pr_queue_manifest_contract.py diff --git a/services/analysis-engine/tests/test_open_pr_queue_manifest_contract.py b/services/analysis-engine/tests/test_open_pr_queue_manifest_contract.py new file mode 100644 index 000000000..3609d37e0 --- /dev/null +++ b/services/analysis-engine/tests/test_open_pr_queue_manifest_contract.py @@ -0,0 +1,67 @@ +"""Regression tests for fail-closed BandScope open-PR queue authority.""" + +from __future__ import annotations + +import importlib.util +from pathlib import Path +from types import ModuleType + +import pytest + +REPO_ROOT = Path(__file__).resolve().parents[3] +VERIFIER_PATH = REPO_ROOT / "scripts" / "checks" / "verify_open_pr_queue.py" + + +def _load_verifier() -> ModuleType: + """Load the queue verifier without requiring scripts to be a Python package.""" + spec = importlib.util.spec_from_file_location("verify_open_pr_queue_contract", VERIFIER_PATH) + assert spec is not None and spec.loader is not None + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +def _manifest() -> dict[str, object]: + """Return the smallest valid queue manifest used by authority regressions.""" + return { + "schema_version": "1.0.0", + "snapshot_date": "2026-08-24", + "timezone": "Asia/Seoul", + "repository": "ContextualWisdomLab/bandscope", + "base_branch": "develop", + "base_sha": "a" * 40, + "open_pr_count": 1, + "authority_note": "Refresh exact live evidence before action.", + "trains": {"T0": {"description": "Dependency base", "issue": 966}}, + "pull_requests": [ + { + "number": 783, + "title": "canonical dependency security", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/783", + "initial_train": "T0", + "initial_disposition": "triage_required", + "head_sha": None, + "head_sha_status": "refresh_required_before_action", + } + ], + } + + +def test_manifest_rejects_unsupported_initial_disposition() -> None: + """Routing evidence must not accept success-shaped or otherwise unreviewed dispositions.""" + verifier = _load_verifier() + manifest = _manifest() + manifest["pull_requests"][0]["initial_disposition"] = "merge_ready" + + with pytest.raises(verifier.ManifestError, match="initial_disposition"): + verifier.validate_manifest(manifest) + + +def test_manifest_requires_explicit_head_sha_key() -> None: + """A missing head identity must not masquerade as an explicit refresh-required null value.""" + verifier = _load_verifier() + manifest = _manifest() + del manifest["pull_requests"][0]["head_sha"] + + with pytest.raises(verifier.ManifestError, match="head_sha is required"): + verifier.validate_manifest(manifest) From 7bf4e001fb7610e516526236b4cc4bff4a9fb879 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 24 Aug 2026 05:22:56 -0700 Subject: [PATCH 23/77] fix(readiness): constrain queue routing evidence --- scripts/checks/verify_open_pr_queue.py | 40 ++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/scripts/checks/verify_open_pr_queue.py b/scripts/checks/verify_open_pr_queue.py index c7d7770aa..7b6b1c1bc 100644 --- a/scripts/checks/verify_open_pr_queue.py +++ b/scripts/checks/verify_open_pr_queue.py @@ -40,6 +40,34 @@ "head_sha_status", } ) +ALLOWED_INITIAL_DISPOSITIONS = frozenset( + { + "analysis_failure_next_action_copy", + "canonical_audio_resource_policy", + "canonical_dependency_security_base", + "canonical_local_audio_resource_policy", + "canonical_python_branch_coverage_base", + "figma_contract_inventory_drift_check", + "first_run_analyze_entry", + "first_run_own_song_action", + "help_next_action_copy", + "inbound_handoff_reanalysis_slice", + "licensed_demo_first_run_activation", + "local_intake_failure_next_action_copy", + "observation_probability_vectorization", + "outbound_naruon_handoff_contract", + "player_first_section_loop", + "product_readiness_baseline_program", + "project_atomic_publication", + "project_save_load_failure_next_action_copy", + "real_audio_accuracy_slice_under_issue_770", + "storybook_design_inventory_slice", + "support_manifest_boundary", + "triage_required", + "workspace_rehearsal_map_slice", + "youtube_import_failure_next_action_copy", + } +) class ManifestError(ValueError): @@ -143,9 +171,15 @@ def validate_manifest(manifest: object) -> None: train_name = _require_non_empty_string(pr.get("initial_train"), f"{prefix}.initial_train") if train_name not in trains: _fail(f"{prefix}.initial_train references unknown train: {train_name}") - _require_non_empty_string(pr.get("initial_disposition"), f"{prefix}.initial_disposition") + disposition = _require_non_empty_string( + pr.get("initial_disposition"), f"{prefix}.initial_disposition" + ) + if disposition not in ALLOWED_INITIAL_DISPOSITIONS: + _fail(f"{prefix}.initial_disposition is unsupported: {disposition}") - head_sha = pr.get("head_sha") + if "head_sha" not in pr: + _fail(f"{prefix}.head_sha is required") + head_sha = pr["head_sha"] head_status = pr.get("head_sha_status") if head_sha is None: if head_status != "refresh_required_before_action": @@ -187,4 +221,4 @@ def main() -> int: if __name__ == "__main__": - raise SystemExit(main()) + raise SystemExit(main()) \ No newline at end of file From 02e1fe89079f9ac27c311ca5aaa3d9fdb0b2b6cd Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 24 Aug 2026 05:23:32 -0700 Subject: [PATCH 24/77] docs(readiness): define triage-only T8 train --- .../decisions/0001-bandscope-1-0-product-readiness.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/architecture/decisions/0001-bandscope-1-0-product-readiness.md b/docs/architecture/decisions/0001-bandscope-1-0-product-readiness.md index 9694b4c0e..d5b4cc319 100644 --- a/docs/architecture/decisions/0001-bandscope-1-0-product-readiness.md +++ b/docs/architecture/decisions/0001-bandscope-1-0-product-readiness.md @@ -57,7 +57,7 @@ Existing canonical accuracy, resource, handoff, supply-chain, and platform issue ### 3. Process work as dependency-aware merge trains -Each open PR must belong to exactly one initial train: +Each reviewed open PR must belong to exactly one initial train: - `T0` dependency, toolchain, workflow, and quality base; - `T1` local input, filesystem authority, resource admission, and cancellation; @@ -68,6 +68,8 @@ Each open PR must belong to exactly one initial train: - `T6` diagnostics, redaction, security evidence, and supportability; - `T7` signed commercial release and updater. +A live refresh may place newly discovered PRs in `T8`, a **triage-only temporary train** owned by issue #966. `T8` is inventory evidence, not an actionable merge train: every `T8` PR must be reviewed and reclassified to exactly one of `T0`–`T7` before any source/ref mutation, merge-readiness decision, auto-merge, or merge action is taken for that PR. + The initial routing is an investigation aid. Issue #966 must refresh the live exact head, checks, reviews, unresolved threads, dependencies, and succession before any action. ### 4. Enforce one canonical writer per boundary @@ -162,4 +164,4 @@ This ADR must be revisited when: - the Figma source-of-truth model changes; - the PR queue no longer needs train-based governance; - the application adopts a different desktop/runtime distribution architecture; -- a project format or updater change makes rollback guarantees materially different. +- a project format or updater change makes rollback guarantees materially different. \ No newline at end of file From 0332756364443e4236829c3f5dc5947b815b24bf Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 24 Aug 2026 05:23:59 -0700 Subject: [PATCH 25/77] test(readiness): type queue authority fixtures --- .../tests/test_open_pr_queue_manifest_contract.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/analysis-engine/tests/test_open_pr_queue_manifest_contract.py b/services/analysis-engine/tests/test_open_pr_queue_manifest_contract.py index 3609d37e0..53dbf8af3 100644 --- a/services/analysis-engine/tests/test_open_pr_queue_manifest_contract.py +++ b/services/analysis-engine/tests/test_open_pr_queue_manifest_contract.py @@ -5,6 +5,7 @@ import importlib.util from pathlib import Path from types import ModuleType +from typing import Any import pytest @@ -21,7 +22,7 @@ def _load_verifier() -> ModuleType: return module -def _manifest() -> dict[str, object]: +def _manifest() -> dict[str, Any]: """Return the smallest valid queue manifest used by authority regressions.""" return { "schema_version": "1.0.0", From 7e4e25ef54a5c95d969dfaabff8a33d04a72cc63 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 17:04:09 +0900 Subject: [PATCH 26/77] chore(queue): preserve canonical gap baseline while stacking #968 --- docs/product-technical-gap-baseline.md | 621 +++++++++++++------------ 1 file changed, 323 insertions(+), 298 deletions(-) diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index ce2345994..063b1eb95 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -1,324 +1,349 @@ -# BandScope Product and Technical Gap Baseline +# BandScope Product-Technical Gap Baseline -**Status:** Proposed baseline -**Snapshot date:** 2026-08-20 (Asia/Seoul) -**Protected base reviewed:** `develop@acdbea6344fe1231c39535b575f4de35e4c607c9` -**Open pull requests inventoried:** 83 -**Program issue:** [#958](https://github.com/ContextualWisdomLab/bandscope/issues/958) +Last updated: 2026-09-01 +Evidence capture: 2026-09-01 14:37 KST unless a row says otherwise +Protected base: `develop@749511c3ad4000090048718f685c6bee6b3d2c25` -> This document is a point-in-time product-readiness baseline, not a claim that any listed pull request is merge-ready. Pull-request heads, checks, reviews, and branch-protection decisions can change after this snapshot. Before any merge, the exact live head must be refreshed and revalidated. +## 1. Purpose and buyer outcome -## Executive conclusion +This document is the current engineering evidence baseline for BandScope. Customer-facing behavior follows `docs/brand-story.md`: practical, rehearsal-first, non-authoritative, and explicit about uncertainty. This file connects buyer promises to implementation boundaries, tests, research, security controls, and live GitHub evidence; those internals must not leak into product copy. -BandScope already has a credible local-first product boundary, a typed Tauri/Python analysis architecture, extensive security hardening, and substantial rehearsal-oriented UI work. It is not yet a complete commercial desktop product. - -The protected snapshot does not yet demonstrate the complete buyer journey: +BandScope is a local-first rehearsal companion for working musicians and band hobbyists who need to understand a song quickly and spend rehearsal time playing rather than decoding an arrangement. ```text -install a trusted build -→ import a real song -→ obtain measurably accurate analysis -→ actively rehearse with the analysis -→ save and recover the project +trusted install +→ admit a real song safely +→ derive evidence-backed section/role guidance +→ expose uncertainty and allow correction +→ rehearse a precise passage +→ save/recover accepted work → share a bounded handoff -→ diagnose failure without leaking the song → update or roll back safely ``` -The highest-leverage work is therefore not another isolated cue card. It is to converge the current queue into measurable product verticals and finish the missing distribution, active rehearsal, durability, supportability, activation, accessibility, and scientific-evidence boundaries. - -## Product boundary - -BandScope is a **local-first rehearsal decision tool**. It should help musicians understand a recording, decide what to practise, and rehearse the difficult passage. It is not a notation editor, DAW, plugin host, cloud-storage product, or autonomous authority over musical truth. - -The product must preserve these claim boundaries: - -- analysis results are evidence-backed recommendations, not infallible transcription; -- local files remain local unless the user explicitly exports a bounded artifact; -- a visual state, mocked array, skipped GPU test, predecessor-head check, or unsigned package is not production evidence; -- unsupported stems, roles, formats, or model capabilities fail honestly rather than fabricating a result. - -## Evidence reviewed - -The investigation covered: - -- repository metadata and the protected `develop` tree; -- README, architecture, acceptance, release, deployment, package, Tauri, workflow, Rust, TypeScript, and Python surfaces; -- all 122 open pull requests returned by the connected GitHub account, with exact-head capture on 2026-08-24; -- existing canonical product/security issues; -- saved Figma file `BP30foevuRtufwRpTknZUw`; -- current official guidance for Tauri signing/updating, Apple notarization, Microsoft signing, WCAG 2.2, and music-information-retrieval evaluation. - - -## Live refresh findings (2026-08-24) - -The 2026-08-24 exact-head refresh surfaced three systemic merge-gate conditions that sit above any -individual feature review: - -1. **Canonical npm security baseline is still unmerged (`#783`, train `T0`).** Nearly every open - feature head inherits `security-audit` and `trivy-fs` failures from `package-lock.json`: - `pdfjs-dist` (GHSA-hq66-cqwq-w95j / CVE-2026-16633, HIGH), four `undici` HIGH advisories - (GHSA-8xcm-r25x-g524, GHSA-4cwx-7wf7-3272, GHSA-m8rv-5g2x-5cg5, GHSA-jr45-8vmc-qm54, - GHSA-v3r7-h72x-cjcm), and a HIGH `nanoid` advisory. `#783` is green on both checks; merging it - first and updating dependent branches is the single highest-leverage unblock for the queue. -2. **Central Strix gate provider outage is intermittent, not per-PR.** Required-check runs show - `nvidia_nim` model failures followed by a direct-OpenAI fallback that cannot connect - ("404 page not found"), failing closed per org policy. The fix belongs in - `ContextualWisdomLab/.github` (provider-prefix migration already in flight there); BandScope - must not weaken or bypass the gate locally. -3. **The queue grew 83 → 122 without triage.** All 40 additions after PR #957 were classified into - trains during this refresh (see manifest); none changes the completion program below. - -These are environment/gate facts recorded as evidence. They do not change product priorities. - -## Buyer-visible completion gaps - -| Priority | Gap | Current evidence | Buyer impact | Owning issue | -|---:|---|---|---|---| -| P0 | Trusted distribution and update | Version `0.1.3`; release workflow is principally validation/preflight; no complete signed/notarized updater and rollback evidence | Buyers cannot establish publisher trust or safely update/repair | [#960](https://github.com/ContextualWisdomLab/bandscope/issues/960) | -| P0 | Active rehearsal player | Many cue/action PRs exist, but there is no single timing/playback authority proving count-in, precise looping, rate, cue navigation, and role controls through the production desktop boundary | Analysis does not yet close the loop into repeated practice | [#961](https://github.com/ContextualWisdomLab/bandscope/issues/961) | -| P0 | Crash-safe durable projects | Durable project truth, autosave, atomic publish, migrations, backups, recovery, and rollback are not governed by one versioned format | A crash or upgrade can threaten user work and trust | [#962](https://github.com/ContextualWisdomLab/bandscope/issues/962) | -| P0 | Scientific acceptance | Real decoded-audio acceptance exists as work in progress, but the broader licensed multi-fixture MIR program, recognized metrics, uncertainty, CPU/GPU parity, and claim matrix are not closed | Musicians cannot judge where results are trustworthy | [#770](https://github.com/ContextualWisdomLab/bandscope/issues/770) | -| P0 | Resource/cancellation contract | Canonical local-audio admission is being developed, but all decode, separation, analysis, PDF, cancellation, and fallback paths must converge | Large or hostile files can degrade availability or create inconsistent behavior | [#781](https://github.com/ContextualWisdomLab/bandscope/issues/781) | -| P1 | Diagnostics and support evidence | Multiple redaction fixes exist, but no single typed local diagnostics contract or previewable support bundle exists | Failures remain expensive to diagnose and risk leaking private paths/content | [#963](https://github.com/ContextualWisdomLab/bandscope/issues/963) | -| P1 | First-run activation | Empty states and next-action PRs exist, but no licensed demo proves time-to-first-rehearsal through the production path | A new buyer cannot evaluate value quickly or reproducibly | [#964](https://github.com/ContextualWisdomLab/bandscope/issues/964) | -| P1 | Design and accessibility parity | Storybook work is open. The saved Figma file claims 28 pages but currently exposes two top-level pages; its footer and protected repository/runtime metadata both cite `0.1.3`, so the remaining gap is implementation/design parity rather than version drift | Design status can be mistaken for shipped behavior; assistive-technology acceptance is incomplete | [#965](https://github.com/ContextualWisdomLab/bandscope/issues/965) | -| P0 | PR queue convergence | 122 open PRs (2026-08-24 exact-head capture) include canonical bases, small feature slices, security repairs, dependency updates, and overlapping writers; systemic `security-audit`/`trivy-fs` failures block nearly all feature heads until the `#783` npm baseline lands | Review drift, ancestry conflicts, and inherited-base failures prevent coherent delivery | [#966](https://github.com/ContextualWisdomLab/bandscope/issues/966) | - -## Completion program - -| Issue | Product outcome | -|---|---| -| [#958](https://github.com/ContextualWisdomLab/bandscope/issues/958) | Parent BandScope 1.0 completion program and commercial definition of done | -| [#960](https://github.com/ContextualWisdomLab/bandscope/issues/960) | Signed/notarized, provenance-backed desktop release and verified updater | -| [#961](https://github.com/ContextualWisdomLab/bandscope/issues/961) | Active rehearsal transport, loop, count-in, cue navigation, and honest role controls | -| [#962](https://github.com/ContextualWisdomLab/bandscope/issues/962) | Versioned project schema, atomic save, autosave, migration, backup, and recovery | -| [#963](https://github.com/ContextualWisdomLab/bandscope/issues/963) | Typed redacted diagnostics, crash evidence, and offline support bundle | -| [#964](https://github.com/ContextualWisdomLab/bandscope/issues/964) | Licensed demo and measurable first-run rehearsal journey | -| [#965](https://github.com/ContextualWisdomLab/bandscope/issues/965) | Figma/Storybook/shipped-UI parity and WCAG 2.2 AA acceptance | -| [#966](https://github.com/ContextualWisdomLab/bandscope/issues/966) | Dependency-aware merge trains and explicit PR succession | - -Existing canonical work that must be preserved rather than duplicated includes [#610](https://github.com/ContextualWisdomLab/bandscope/issues/610), [#739](https://github.com/ContextualWisdomLab/bandscope/issues/739), [#770](https://github.com/ContextualWisdomLab/bandscope/issues/770), [#781](https://github.com/ContextualWisdomLab/bandscope/issues/781), [#526](https://github.com/ContextualWisdomLab/bandscope/issues/526), [#542](https://github.com/ContextualWisdomLab/bandscope/issues/542), [#779](https://github.com/ContextualWisdomLab/bandscope/issues/779), [#847](https://github.com/ContextualWisdomLab/bandscope/issues/847), [#852](https://github.com/ContextualWisdomLab/bandscope/issues/852), and [#864](https://github.com/ContextualWisdomLab/bandscope/issues/864). - -## Merge-train model - -| Train | Responsibility | Initial live PR count | Completion issue | -|---|---|---:|---| -| `T0` | Dependency, toolchain, workflow and quality base | 31 | [#966](https://github.com/ContextualWisdomLab/bandscope/issues/966) | -| `T1` | Local input, filesystem authority, resource admission and cancellation | 7 | [#781](https://github.com/ContextualWisdomLab/bandscope/issues/781), [#962](https://github.com/ContextualWisdomLab/bandscope/issues/962) | -| `T2` | Scientific accuracy, MIR evaluation and numerical parity | 9 | [#770](https://github.com/ContextualWisdomLab/bandscope/issues/770) | -| `T3` | Rehearsal actions and active-player vertical | 52 | [#961](https://github.com/ContextualWisdomLab/bandscope/issues/961) | -| `T4` | Project portability, handoff and interoperability | 4 | [#739](https://github.com/ContextualWisdomLab/bandscope/issues/739), [#962](https://github.com/ContextualWisdomLab/bandscope/issues/962) | -| `T5` | Activation, UI system, Storybook and accessibility | 12 | [#964](https://github.com/ContextualWisdomLab/bandscope/issues/964), [#965](https://github.com/ContextualWisdomLab/bandscope/issues/965) | -| `T6` | Diagnostics, redaction, security evidence and supportability | 7 | [#963](https://github.com/ContextualWisdomLab/bandscope/issues/963) | -| `T7` | Signed commercial release and updater | 0 | [#960](https://github.com/ContextualWisdomLab/bandscope/issues/960) | - -The routing below is an **initial product-boundary classification**, not a merge-readiness verdict. Issue #966 must refresh exact head SHAs, dependency edges, current checks, current reviews, unresolved threads, and succession before any action. - -## Current open PR inventory - -| PR | Title | Initial train | Exact head (2026-08-24) | Required next action | -|---:|---|---|---|---| -| [731](https://github.com/ContextualWisdomLab/bandscope/pull/731) | 🎨 Palette: 향상된 접근성을 위해 아이콘 버튼에 툴팁 및 aria-disabled 적용 | `T5` | `5ea5663f26e3` | Triage against the live exact head before action | -| [732](https://github.com/ContextualWisdomLab/bandscope/pull/732) | ⚡ Chords: vectorize HMM reference and correct relative-key prior | `T2` | `830dd4c982b1` | Triage against the live exact head before action | -| [737](https://github.com/ContextualWisdomLab/bandscope/pull/737) | feat(integration): add versioned naruon rehearsal handoff | `T4` | `82ae343e9911` | Triage against the live exact head before action | -| [740](https://github.com/ContextualWisdomLab/bandscope/pull/740) | feat: import rehearsal handoffs for focused reanalysis | `T4` | `4761a882d1b3` | Triage against the live exact head before action | -| [744](https://github.com/ContextualWisdomLab/bandscope/pull/744) | feat(i18n): localize Workspace controls and harden interpolation | `T5` | `0ca00b110d04` | Triage against the live exact head before action | -| [746](https://github.com/ContextualWisdomLab/bandscope/pull/746) | perf(segmenter): vectorize checkerboard novelty reference | `T2` | `9d0def7d5038` | Triage against the live exact head before action | -| [750](https://github.com/ContextualWisdomLab/bandscope/pull/750) | fix(score): validate PDF bridge byte arrays without coercion | `T1` | `d982adef81cd` | Triage against the live exact head before action | -| [754](https://github.com/ContextualWisdomLab/bandscope/pull/754) | build(deps): bump time from 0.3.53 to 0.3.55 in /apps/desktop/src-tauri | `T0` | `966d5f1204ec` | Triage against the live exact head before action | -| [776](https://github.com/ContextualWisdomLab/bandscope/pull/776) | feat(chords): surface actionable rehearsal guidance | `T3` | `8b38e4885e21` | Triage against the live exact head before action | -| [780](https://github.com/ContextualWisdomLab/bandscope/pull/780) | fix(security): keep every CodeQL Action phase on one revision | `T0` | `738495ca9d42` | Triage against the live exact head before action | -| [783](https://github.com/ContextualWisdomLab/bandscope/pull/783) | fix(security): establish canonical npm, PDF.js, Nanoid, and Undici baseline | `T0` | `1168c8f4257d` | Triage against the live exact head before action | -| [811](https://github.com/ContextualWisdomLab/bandscope/pull/811) | 🔒 [보안] CLI 무제한 파일 읽기 취약점 수정 | `T1` | `54c645c9dcac` | Triage against the live exact head before action | -| [824](https://github.com/ContextualWisdomLab/bandscope/pull/824) | 🧪 테스트: 줄기 분리 시간 초과 처리 테스트 추가 | `T1` | `f4d01558eb3c` | Triage against the live exact head before action | -| [826](https://github.com/ContextualWisdomLab/bandscope/pull/826) | 🧪 [테스트 개선] TemporalAnalyzer 분석 오류 테스트 추가 | `T6` | `9002ff8b9227` | Triage against the live exact head before action | -| [827](https://github.com/ContextualWisdomLab/bandscope/pull/827) | fix(desktop): remove synthetic browser analysis success | `T2` | `a2e5d260e68a` | Triage against the live exact head before action | -| [828](https://github.com/ContextualWisdomLab/bandscope/pull/828) | test(analysis): govern real YouTube known-stem benchmark | `T2` | `9331b406e7fb` | Triage against the live exact head before action | -| [833](https://github.com/ContextualWisdomLab/bandscope/pull/833) | 🎨 Palette: 코드 수정 버튼에 접근성 있는 툴팁 지원 추가 | `T5` | `3206c259afcc` | Triage against the live exact head before action | -| [834](https://github.com/ContextualWisdomLab/bandscope/pull/834) | ⚡ Bolt: O(1) 메모리로 순차적 코드 변경 횟수 계산 최적화 | `T2` | `c686ae450677` | Triage against the live exact head before action | -| [845](https://github.com/ContextualWisdomLab/bandscope/pull/845) | feat(analysis): CSV 큐시트 내보내기 기능 및 수식 주입 보안 로직 추가 | `T3` | `d1dc4da58332` | Triage against the live exact head before action | -| [849](https://github.com/ContextualWisdomLab/bandscope/pull/849) | ⚡ Bolt: 차트 내보내기(chart.py)의 중복 제거 로직 O(N^2)에서 O(N)으로 최적화 | `T5` | `e3268c8bf632` | Triage against the live exact head before action | -| [850](https://github.com/ContextualWisdomLab/bandscope/pull/850) | feat: add Part Handoff Map UI component to workspace | `T3` | `adbe3da06172` | Triage against the live exact head before action | -| [858](https://github.com/ContextualWisdomLab/bandscope/pull/858) | fix(security): bind analysis paths to filesystem authority | `T1` | `bedc2069494c` | Triage against the live exact head before action | -| [859](https://github.com/ContextualWisdomLab/bandscope/pull/859) | ⚡ Bolt: GrooveMap의 maxTime 계산 성능 개선 | `T5` | `38e1027d3a96` | Triage against the live exact head before action | -| [861](https://github.com/ContextualWisdomLab/bandscope/pull/861) | test(quality): enforce Python branch coverage | `T0` | `6afbc9fff585` | Triage against the live exact head before action | -| [865](https://github.com/ContextualWisdomLab/bandscope/pull/865) | fix(score): bound native PDF reads before allocation | `T1` | `f86e266b2ab2` | Triage against the live exact head before action | -| [866](https://github.com/ContextualWisdomLab/bandscope/pull/866) | fix(audio): establish canonical local-audio resource policy | `T1` | `223dd78126de` | Triage against the live exact head before action | -| [867](https://github.com/ContextualWisdomLab/bandscope/pull/867) | test(supply-chain): preserve simple dependency-path cycle semantics | `T0` | `f1ac4167b8b7` | Triage against the live exact head before action | -| [873](https://github.com/ContextualWisdomLab/bandscope/pull/873) | feat(roles): wire measured register overlap into section warnings | `T2` | `c30be7afa82b` | Triage against the live exact head before action | -| [874](https://github.com/ContextualWisdomLab/bandscope/pull/874) | feat(workspace): name the parts to lock in first | `T3` | `05935e02f293` | Triage against the live exact head before action | -| [881](https://github.com/ContextualWisdomLab/bandscope/pull/881) | test(ci): lock local OpenCode small_model to NVIDIA NIM | `T0` | `4e07356d1ece` | Triage against the live exact head before action | -| [884](https://github.com/ContextualWisdomLab/bandscope/pull/884) | feat(workspace): open Stem Lab as honest isolation lanes | `T3` | `e33ca56a3922` | Triage against the live exact head before action | -| [891](https://github.com/ContextualWisdomLab/bandscope/pull/891) | test(analysis): lock verse/chorus chord recovery on a known take | `T2` | `92fe9155a189` | Triage against the live exact head before action | -| [892](https://github.com/ContextualWisdomLab/bandscope/pull/892) | fix(analysis): score C major acceptance from decoded WAV bytes | `T2` | `40f138e00ece` | Triage against the live exact head before action | -| [894](https://github.com/ContextualWisdomLab/bandscope/pull/894) | fix(security): drop persisted credentials before dependency lifecycle code | `T0` | `475adeba6210` | Triage against the live exact head before action | -| [895](https://github.com/ContextualWisdomLab/bandscope/pull/895) | fix(workflows): audit orphaned Actions registry identities | `T0` | `9de29814cb30` | Triage against the live exact head before action | -| [896](https://github.com/ContextualWisdomLab/bandscope/pull/896) | build(node): coordinate Node 22.22.2 floor with jsdom 30 | `T0` | `c07e51639dbb` | Triage against the live exact head before action | -| [897](https://github.com/ContextualWisdomLab/bandscope/pull/897) | feat(workspace): Storybook tokens for rehearsal components | `T5` | `d31a6ce740f7` | Triage against the live exact head before action | -| [898](https://github.com/ContextualWisdomLab/bandscope/pull/898) | feat(workspace): put the next rehearsal action on empty and error cards | `T5` | `48c6084e9e8e` | Triage against the live exact head before action | -| [899](https://github.com/ContextualWisdomLab/bandscope/pull/899) | feat(workspace): open existing rehearsal surfaces from the sidebar | `T5` | `b263ac441df5` | Triage against the live exact head before action | -| [900](https://github.com/ContextualWisdomLab/bandscope/pull/900) | feat(workspace): name tonight's export and priority actions | `T3` | `0c09689fc981` | Triage against the live exact head before action | -| [901](https://github.com/ContextualWisdomLab/bandscope/pull/901) | feat(workspace): start tonight's first part from the ready board | `T3` | `940ff24aaeac` | Triage against the live exact head before action | -| [903](https://github.com/ContextualWisdomLab/bandscope/pull/903) | feat(workspace): loop tonight's first section on the map | `T3` | `1f3e8c75c572` | Triage against the live exact head before action | -| [905](https://github.com/ContextualWisdomLab/bandscope/pull/905) | feat(workspace): open tonight's first notes on the groove map | `T3` | `a1a38fc4e537` | Triage against the live exact head before action | -| [907](https://github.com/ContextualWisdomLab/bandscope/pull/907) | chore(env): add Cloud Agent environment config | `T0` | `b0972c5ad01c` | Triage against the live exact head before action | -| [910](https://github.com/ContextualWisdomLab/bandscope/pull/910) | feat(workspace): set up tonight's part before the first entrance | `T3` | `61aa51f274c0` | Triage against the live exact head before action | -| [912](https://github.com/ContextualWisdomLab/bandscope/pull/912) | feat(workspace): guide tonight's first entrance on map and player | `T3` | `b8f588ad6b0d` | Triage against the live exact head before action | -| [913](https://github.com/ContextualWisdomLab/bandscope/pull/913) | feat(workspace): guide tonight's first lyric cue on map and player | `T3` | `c56a58933c41` | Triage against the live exact head before action | -| [914](https://github.com/ContextualWisdomLab/bandscope/pull/914) | feat(workspace): guide tonight's first dropout on map and player | `T3` | `a0b5fe924361` | Triage against the live exact head before action | -| [916](https://github.com/ContextualWisdomLab/bandscope/pull/916) | feat(workspace): guide tonight's first pickup on map and player | `T3` | `6657a47b706e` | Triage against the live exact head before action | -| [918](https://github.com/ContextualWisdomLab/bandscope/pull/918) | build(deps): bump uuid from 1.23.4 to 1.24.1 in /apps/desktop/src-tauri | `T0` | `77a2c35a21ee` | Triage against the live exact head before action | -| [919](https://github.com/ContextualWisdomLab/bandscope/pull/919) | build(deps): update numba requirement from <0.67.0 to <0.68.0 in /services/analysis-engine | `T0` | `b113cec48235` | Triage against the live exact head before action | -| [920](https://github.com/ContextualWisdomLab/bandscope/pull/920) | build(deps): bump react and @types/react | `T0` | `477fa3e363e7` | Triage against the live exact head before action | -| [921](https://github.com/ContextualWisdomLab/bandscope/pull/921) | build(deps): bump @base-ui/react from 1.5.0 to 1.7.0 | `T0` | `2840d0d0831d` | Triage against the live exact head before action | -| [922](https://github.com/ContextualWisdomLab/bandscope/pull/922) | build(deps-dev): bump storybook from 10.4.6 to 10.5.8 | `T0` | `5f6f0a809eb0` | Triage against the live exact head before action | -| [923](https://github.com/ContextualWisdomLab/bandscope/pull/923) | build(deps-dev): bump @storybook/react-vite from 10.4.6 to 10.5.8 | `T0` | `d3d65db35418` | Triage against the live exact head before action | -| [924](https://github.com/ContextualWisdomLab/bandscope/pull/924) | build(deps): bump github/codeql-action/init from 4.37.0 to 4.37.7 | `T0` | `bd7cd7d49cf5` | Triage against the live exact head before action | -| [925](https://github.com/ContextualWisdomLab/bandscope/pull/925) | build(deps-dev): bump typescript-eslint from 8.63.0 to 8.67.0 | `T0` | `525bb99a4553` | Triage against the live exact head before action | -| [926](https://github.com/ContextualWisdomLab/bandscope/pull/926) | build(deps): bump lucide-react from 1.24.0 to 1.31.0 | `T0` | `5b27740c54bb` | Triage against the live exact head before action | -| [927](https://github.com/ContextualWisdomLab/bandscope/pull/927) | build(deps): bump sonner from 2.0.7 to 2.0.8 | `T0` | `235019727df5` | Triage against the live exact head before action | -| [928](https://github.com/ContextualWisdomLab/bandscope/pull/928) | build(deps-dev): bump eslint-plugin-jsdoc from 63.0.13 to 64.2.0 | `T0` | `f87c0fbdca3b` | Triage against the live exact head before action | -| [929](https://github.com/ContextualWisdomLab/bandscope/pull/929) | build(deps-dev): bump @testing-library/jest-dom from 6.9.1 to 7.0.1 | `T0` | `0ce4cb84cbaf` | Triage against the live exact head before action | -| [930](https://github.com/ContextualWisdomLab/bandscope/pull/930) | build(deps): bump github/codeql-action/autobuild from 4.37.0 to 4.37.7 | `T0` | `927c473bbfef` | Triage against the live exact head before action | -| [931](https://github.com/ContextualWisdomLab/bandscope/pull/931) | build(deps): bump astral-sh/setup-uv from 8.3.2 to 10.0.1 | `T0` | `8aed8adac445` | Triage against the live exact head before action | -| [932](https://github.com/ContextualWisdomLab/bandscope/pull/932) | build(deps): bump github/codeql-action/analyze from 4.37.0 to 4.37.7 | `T0` | `57011511f876` | Triage against the live exact head before action | -| [933](https://github.com/ContextualWisdomLab/bandscope/pull/933) | build(deps): bump github/codeql-action/upload-sarif from 4.37.0 to 4.37.7 | `T0` | `dff69016587f` | Triage against the live exact head before action | -| [934](https://github.com/ContextualWisdomLab/bandscope/pull/934) | feat(workspace): guide tonight's first stop on map and player | `T3` | `3d796dfffd64` | Triage against the live exact head before action | -| [936](https://github.com/ContextualWisdomLab/bandscope/pull/936) | build(deps): bump the uv group across 1 directory with 2 updates | `T0` | `cb048b1f455b` | Triage against the live exact head before action | -| [937](https://github.com/ContextualWisdomLab/bandscope/pull/937) | feat(workspace): guide tonight's first labeled handoff on map and player | `T3` | `c3685178c6f3` | Triage against the live exact head before action | -| [939](https://github.com/ContextualWisdomLab/bandscope/pull/939) | feat(workspace): guide tonight's first chorus on map and player | `T3` | `d3c852894bc5` | Triage against the live exact head before action | -| [941](https://github.com/ContextualWisdomLab/bandscope/pull/941) | 🛡️ Sentinel: CSV 수식 주입 NUL·전각 연산자 우회 차단 | `T6` | `f7a2634607c5` | Triage against the live exact head before action | -| [942](https://github.com/ContextualWisdomLab/bandscope/pull/942) | build(deps): bump react-dom and @types/react-dom | `T0` | `647b883996ed` | Triage against the live exact head before action | -| [943](https://github.com/ContextualWisdomLab/bandscope/pull/943) | feat(workspace): guide tonight's first intro on map and player | `T3` | `4855357e19dd` | Triage against the live exact head before action | -| [944](https://github.com/ContextualWisdomLab/bandscope/pull/944) | build(rust): pin all product and release lanes to 1.97.1 | `T0` | `b0f8cf0de9f0` | Triage against the live exact head before action | -| [946](https://github.com/ContextualWisdomLab/bandscope/pull/946) | feat(workspace): guide tonight's first bridge on map and player | `T3` | `4d13986d7d48` | Triage against the live exact head before action | -| [947](https://github.com/ContextualWisdomLab/bandscope/pull/947) | feat(workspace): guide tonight's first verse on map and player | `T3` | `315e563ffe6d` | Triage against the live exact head before action | -| [948](https://github.com/ContextualWisdomLab/bandscope/pull/948) | fix(security): retire quick-xml RustSec exceptions | `T0` | `d415bc382167` | Triage against the live exact head before action | -| [949](https://github.com/ContextualWisdomLab/bandscope/pull/949) | fix(security): redact key detector dependency failures | `T6` | `6ee9ada2228a` | Triage against the live exact head before action | -| [950](https://github.com/ContextualWisdomLab/bandscope/pull/950) | fix(security): redact temporal detector failure logs | `T6` | `396d42017bd3` | Triage against the live exact head before action | -| [951](https://github.com/ContextualWisdomLab/bandscope/pull/951) | fix(security): redact range-analysis failure logs | `T6` | `9c64ca3cd3bd` | Triage against the live exact head before action | -| [955](https://github.com/ContextualWisdomLab/bandscope/pull/955) | feat(workspace): guide tonight's first pre-chorus on map and player | `T3` | `d1975e182f52` | Triage against the live exact head before action | -| [956](https://github.com/ContextualWisdomLab/bandscope/pull/956) | fix(security): redact articulation failure logs | `T6` | `2707ad39e019` | Triage against the live exact head before action | -| [957](https://github.com/ContextualWisdomLab/bandscope/pull/957) | feat(workspace): name tonight's first playable range on the map | `T3` | `4c4c045cfe1e` | Triage against the live exact head before action | -| [967](https://github.com/ContextualWisdomLab/bandscope/pull/967) | feat(operations): add privacy-safe support manifest boundary | `T6` | `b9881489d297` | Triage against the live exact head before action | -| [968](https://github.com/ContextualWisdomLab/bandscope/pull/968) | feat(readiness): establish BandScope 1.0 product-readiness baseline | `T0` | `db52897352ae` | Triage against the live exact head before action | -| [969](https://github.com/ContextualWisdomLab/bandscope/pull/969) | fix(design): restore Figma contract-page inventory and drift check | `T5` | `934b76286632` | Triage against the live exact head before action | -| [970](https://github.com/ContextualWisdomLab/bandscope/pull/970) | fix(project): stage new saves without clobbering known-good files | `T4` | `2e48e59916c8` | Triage against the live exact head before action | -| [971](https://github.com/ContextualWisdomLab/bandscope/pull/971) | feat(workspace): loop tonight's first section from the map | `T3` | `7c1b4973f9d7` | Triage against the live exact head before action | -| [972](https://github.com/ContextualWisdomLab/bandscope/pull/972) | feat(workspace): name the next rehearsal action from help | `T3` | `c0095ca8f401` | Triage against the live exact head before action | -| [974](https://github.com/ContextualWisdomLab/bandscope/pull/974) | feat(workspace): start analysis from the first-run card | `T5` | `fd33104ca880` | Triage against the live exact head before action | -| [976](https://github.com/ContextualWisdomLab/bandscope/pull/976) | feat(workspace): name the next action after analysis fails | `T3` | `d07dc57347ce` | Triage against the live exact head before action | -| [980](https://github.com/ContextualWisdomLab/bandscope/pull/980) | feat(workspace): name using your own song as the first next action | `T5` | `9bc2ead0d52f` | Triage against the live exact head before action | -| [981](https://github.com/ContextualWisdomLab/bandscope/pull/981) | feat(workspace): name Choose another song after local intake fails | `T3` | `9059b4ceb5de` | Triage against the live exact head before action | -| [982](https://github.com/ContextualWisdomLab/bandscope/pull/982) | feat(workspace): name Paste another YouTube link after import fails | `T3` | `54f5a59edfea` | Triage against the live exact head before action | -| [984](https://github.com/ContextualWisdomLab/bandscope/pull/984) | feat(workspace): name the next action after project load or save fails | `T4` | `8207813b5ef7` | Triage against the live exact head before action | -| [985](https://github.com/ContextualWisdomLab/bandscope/pull/985) | feat(analysis): enforce one canonical audio resource policy (#781) | `T1` | `d2cf2047af79` | Triage against the live exact head before action | -| [986](https://github.com/ContextualWisdomLab/bandscope/pull/986) | feat(workspace): guide tonight's first outro on the rehearsal map | `T3` | `4222d99c0257` | Triage against the live exact head before action | -| [987](https://github.com/ContextualWisdomLab/bandscope/pull/987) | feat(workspace): name tonight's tempo, starting chord, and transpose setup | `T3` | `ec87428f4ef9` | Triage against the live exact head before action | -| [989](https://github.com/ContextualWisdomLab/bandscope/pull/989) | feat(workspace): guide tonight's first tag on the rehearsal map | `T3` | `6b83256ff841` | Triage against the live exact head before action | -| [990](https://github.com/ContextualWisdomLab/bandscope/pull/990) | feat(workspace): name tonight's first simpler take on the map | `T3` | `3736a1647ec0` | Triage against the live exact head before action | -| [991](https://github.com/ContextualWisdomLab/bandscope/pull/991) | feat(workspace): name tonight's first groove on the map | `T3` | `ac360bd992fe` | Triage against the live exact head before action | -| [992](https://github.com/ContextualWisdomLab/bandscope/pull/992) | feat(workspace): name tonight's first overlap on the map | `T3` | `510347b801bf` | Triage against the live exact head before action | -| [993](https://github.com/ContextualWisdomLab/bandscope/pull/993) | feat(workspace): name tonight's first transition cue on the map | `T3` | `151d8ec73368` | Triage against the live exact head before action | -| [994](https://github.com/ContextualWisdomLab/bandscope/pull/994) | feat(workspace): name tonight's first transition on the map | `T3` | `6e8d5412d893` | Triage against the live exact head before action | -| [995](https://github.com/ContextualWisdomLab/bandscope/pull/995) | feat(workspace): name tonight's first count on the map | `T3` | `4d353ca99a38` | Triage against the live exact head before action | -| [996](https://github.com/ContextualWisdomLab/bandscope/pull/996) | feat(workspace): name tonight's first assignment on the map | `T3` | `54b75a265ef3` | Triage against the live exact head before action | -| [997](https://github.com/ContextualWisdomLab/bandscope/pull/997) | feat(workspace): name tonight's first open rehearsal comment on the map | `T3` | `fa4aadd429c2` | Triage against the live exact head before action | -| [998](https://github.com/ContextualWisdomLab/bandscope/pull/998) | feat(workspace): name tonight's first pending approval on the map | `T3` | `b67afb688866` | Triage against the live exact head before action | -| [999](https://github.com/ContextualWisdomLab/bandscope/pull/999) | ⚡ Bolt: 관측 확률 계산 배열 연산으로 벡터화 (성능 개선) | `T2` | `c11f5ed592bd` | Triage against the live exact head before action | -| [1000](https://github.com/ContextualWisdomLab/bandscope/pull/1000) | feat(workspace): name tonight's first blocked assignment on the map | `T3` | `6c6b3e75616e` | Triage against the live exact head before action | -| [1001](https://github.com/ContextualWisdomLab/bandscope/pull/1001) | feat(workspace): name tonight's first ear check on the map | `T3` | `38781c86dbcf` | Triage against the live exact head before action | -| [1002](https://github.com/ContextualWisdomLab/bandscope/pull/1002) | feat(workspace): name tonight's first confirmed chord on the map | `T3` | `1a1f231052d1` | Triage against the live exact head before action | -| [1003](https://github.com/ContextualWisdomLab/bandscope/pull/1003) | feat(workspace): name tonight's first harmonic explanation on the map | `T3` | `8f88db8c2313` | Triage against the live exact head before action | -| [1004](https://github.com/ContextualWisdomLab/bandscope/pull/1004) | feat(workspace): name tonight's first setup note on the map | `T3` | `9411885d68ed` | Triage against the live exact head before action | -| [1005](https://github.com/ContextualWisdomLab/bandscope/pull/1005) | feat(workspace): name tonight's first harmonic function on the map | `T3` | `00cf87dca1bd` | Triage against the live exact head before action | -| [1006](https://github.com/ContextualWisdomLab/bandscope/pull/1006) | feat(workspace): name tonight's first transposition plan on the map | `T3` | `f1e86e9c66c0` | Triage against the live exact head before action | -| [1007](https://github.com/ContextualWisdomLab/bandscope/pull/1007) | feat(workspace): name tonight's first part handoff on the map | `T3` | `e06ca27fad47` | Triage against the live exact head before action | -| [1008](https://github.com/ContextualWisdomLab/bandscope/pull/1008) | feat(workspace): name tonight's first capo plan on the map | `T3` | `e1673b596841` | Triage against the live exact head before action | -| [1009](https://github.com/ContextualWisdomLab/bandscope/pull/1009) | feat(activation): license a demo song and name first-run next actions | `T5` | `86004787d77c` | Triage against the live exact head before action | -| [1010](https://github.com/ContextualWisdomLab/bandscope/pull/1010) | feat(workspace): name tonight's first tuning plan on the map | `T3` | `5b07db76282e` | Triage against the live exact head before action | -| [1011](https://github.com/ContextualWisdomLab/bandscope/pull/1011) | feat(workspace): name tonight's first dynamics plan on the map | `T3` | `1d077d091025` | Triage against the live exact head before action | -| [1012](https://github.com/ContextualWisdomLab/bandscope/pull/1012) | feat(workspace): name tonight's first articulation plan on the map | `T3` | `039b68cf556d` | Triage against the live exact head before action | -| [1013](https://github.com/ContextualWisdomLab/bandscope/pull/1013) | feat(workspace): name tonight's first voicing plan on the map | `T3` | `812d60a93cf4` | Triage against the live exact head before action | - - -## Required execution order - -1. Establish the canonical dependency, toolchain, workflow, and branch-coverage base. -2. Refresh the live PR graph and remove unrelated lock/toolchain drift from feature heads. -3. Complete local input/resource/cancellation and real-audio scientific acceptance. -4. Consolidate cue/action slices behind one active rehearsal-player contract. -5. Establish the versioned project/persistence boundary and portable handoff distinction. -6. Complete first-run activation, Storybook, Figma parity, localization, and end-to-end accessibility. -7. Consolidate diagnostics/redaction into one supportability contract. -8. Cut the signed/notarized updater-backed release only from the protected, evidenced product vertical. - -For each train: +BandScope is not a DAW, notation editor, mandatory cloud service, or an authority that claims one analysis is unquestionably correct. + +### 1.1 Buyer-facing PRD + +Core jobs: + +1. identify what each instrument/vocal role should prepare; +2. understand form, entries/dropouts, timing, harmony, range, overlap, handoffs, and setup cues by section; +3. rehearse the highest-value passage without rebuilding transport in another tool; +4. correct uncertain analysis while retaining model/user provenance; +5. return later without losing accepted work; +6. install/update a build whose identity and provenance can be verified. + +Representative user stories: + +- As a player, I can open a local song and see the first useful rehearsal action without learning a DAW. +- As a band member, I can see section×role guidance rather than a single flat song-wide chord track. +- As a user, I can distinguish machine evidence from user-confirmed correction. +- As a player, I can count in, loop, navigate cues, and use the same controls from keyboard and assistive technology. +- As a returning user, I can recover the last known-good project after a crash, interrupted write, migration, or failed update. + +## 2. Current architecture and responsibility boundaries + +Protected `develop` remains a local desktop architecture: + +- `apps/desktop`: React/Vite rehearsal workspace in a Tauri shell; +- `apps/desktop/src-tauri`: native command/orchestration boundary; +- `apps/desktop/core`: Rust authority/input validation helpers; +- `packages/shared-types`: versioned cross-layer contracts; +- `services/analysis-engine`: current Python orchestration plus still-mixed music-analysis code; +- `services/analysis-engine/rust`: `bandscope_numeric` Rust/PyO3 numerical kernels. + +Typed Tauri IPC and bounded stdin/stdout JSON are the local orchestration path; ordinary local analysis does not require a loopback HTTP server or cloud service. Files, URLs, project data, model artifacts, PDFs, subprocess output, exports, and diagnostics are untrusted at their owning boundaries. + +### 2.1 DDD context map + +```mermaid +flowchart LR + U[Musician / band member] + UI[Rehearsal Workspace\nUI Context] + RI[Rehearsal Intelligence\nCore Domain] + IN[Local Intake & Project\nSupporting Context] + PT[Playback & Transport\nSupporting Context] + RH[Release & Recovery\nSupporting Context] + CO[Collaboration / Handoff\nSupporting Context] + SK[Minimal Shared Contract Kernel] + ACL[External codecs / models / tools\nAnti-Corruption Layer] + + U --> UI + UI --> SK + SK --> RI + SK --> IN + SK --> PT + SK --> CO + IN --> ACL + RI --> ACL + RH --> UI +``` -```text -refresh live queue -→ validate canonical predecessor -→ inspect current review threads -→ repair root causes -→ remove unrelated drift -→ run current-head checks -→ obtain qualifying independent approval -→ merge or enable auto-merge -→ restack the next PR -→ close superseded duplicates with succession evidence -→ refresh this baseline +Core subdomain: **Rehearsal Intelligence**. Supporting subdomains: Local Intake & Project, Playback & Transport, Release & Recovery, and bounded Collaboration/Handoff. Generic concerns: logging, localization, accessibility primitives, release metadata, and supply-chain evidence. + +Shared Kernel stays intentionally small: stable identifiers plus section/role/cue/confidence/provenance and versioned interchange contracts. Codec, Demucs/librosa-era, PDF, platform, and accelerator types remain behind Anti-Corruption Layers. + +### 2.2 Ubiquitous language, aggregates, invariants, events + +| Term | Meaning | Invariant / transaction boundary | +|---|---|---| +| `RehearsalProject` | durable work for one admitted rehearsal source | one published project version; no partial publication | +| `SongSection` | time-bounded structural region | ordered, finite range inside admitted media duration | +| `RehearsalRole` | instrument, vocal function, or useful subdivision | guidance belongs to a section/project and retains provenance | +| `RehearsalCue` | actionable entry/stop/pickup/handoff/range/setup/timing instruction | referenced section/time/role remains resolvable | +| `AnalysisEvidence` | versioned machine estimate with confidence/provenance | never silently promoted to user-confirmed truth | +| `ManualOverride` | user-confirmed correction | preserves original evidence and authoring provenance | +| `RehearsalTransport` | count-in/loop/playback/navigation state | one authoritative state machine; no competing writers | + +Candidate domain events: `AnalysisCompleted`, `CueConfirmed`, `SectionBoundaryCorrected`, `LoopActivated`, `ProjectSnapshotPublished`, `ProjectRecovered`, and `UpdateRollbackCompleted`. + +## 3. Technical design contract (TRD) + +### 3.1 Rust owns repository core computation + +Protected `develop` is still mixed: Rust owns selected numerical kernels, while material DSP/feature/ranking work remains Python/NumPy. That is a product-technical gap, not a permanent target architecture. + +Target contract: + +- repository-owned mathematical, DSP, vector, matrix, exploratory/data-science, ranking/weighting, token-size, and other core analysis computation is Rust; +- Python may remain only as bounded orchestration/compatibility while migration is incomplete; +- CPU execution uses bounded multithreading with avoidable context switching removed; +- accelerator support is explicit and measured: CPU baseline, then validated CUDA/OpenCL/MLX adapters where meaningful; +- Rust↔Python parity proves migration correctness but does not justify a hidden permanent Python numerical fallback; +- no heuristic weight or rule-of-thumb threshold is accepted without a documented measurement model, calibration dataset, or research basis. + +Migration order follows buyer impact and dependency leverage: temporal/beat and harmony → range/pitch/role features → prioritization/weighting → source-separation integration → remaining vector/matrix utilities. + +### 3.2 Real-audio measurement contract + +Synthetic fixtures are acceptable for unit tests but are not product-accuracy evidence. GA evidence requires licensed or redistribution-safe real audio and human-verified ground truth. + +Task-specific metrics remain separate: + +- harmony/chords: benchmark-defined chord metric such as Weighted Chord Symbol Recall; +- beat/timing: listener-annotated event metrics compatible with the chosen MIREX task contract; +- source separation: SI-SDR plus task-appropriate robustness/perceptual evidence; +- range/pitch/transcription: reference-note/frame/event metrics declared with the corpus; +- section/cue boundaries: time-tolerant event metrics whose tolerance comes from annotation uncertainty and rehearsal error cost, not an unexplained constant. + +Acceptance criteria are preregistered before tuning. Candidate-vs-baseline inference reports uncertainty across tracks; CI thresholds are never invented merely to obtain green status. + +### 3.3 Persistence, playback, release, privacy + +- **Project source of truth — Issue #962:** atomic publication, known-good backup, deterministic/idempotent migration, bounded inputs, explicit single-writer/locking ownership, tested crash recovery. +- **Active rehearsal player — Issue #961:** precise loop/count-in/rate/cue/role interaction; timing-sensitive transport belongs in Rust; real-time callbacks do no unbounded allocation, blocking I/O, network access, or lock-heavy work. +- **Trusted distribution — Issue #960:** signed/notarized artifacts, verifiable updater metadata, SPDX SBOM/provenance, staged rollout and rollback evidence. +- **Private diagnostics — Issue #963:** ordinary logs/support bundles exclude raw private audio, secrets, full local paths, and dependency-controlled exception payloads. + +## 4. Capability and gap matrix + +| Capability | Current direction | Remaining buyer-visible gap | +|---|---|---| +| Local file intake | implemented authority boundary | complete resource budgets and cross-platform fault evidence | +| YouTube import | policy-constrained/partial | honest failure guidance; no DRM/login bypass | +| Section×role hierarchy | represented | real-audio accuracy + correction round trip | +| Harmony guidance | implemented/mixed compute | calibrated evidence, Rust ownership, uncertainty quality | +| Groove/beat/timing | implemented/mixed compute | real-audio benchmark, Rust ownership, full production integration | +| Range/overlap | implemented | reference-audio validation + Rust migration | +| Stems/source separation | partial | platform/accelerator coverage, artifact provenance, real-audio SI-SDR | +| Confidence/provenance | represented | calibration + user correction persistence | +| Rehearsal action map | many open slices | consolidate micro-PRs into coherent section/role UX | +| Active player | incomplete | #961 | +| Crash-safe project/autosave | incomplete | #962 | +| Signed/notarized update/rollback | partial | #960 | +| Private support bundle | incomplete | #963 | +| Licensed first-run demo | incomplete | #964 | +| WCAG/Figma/Storybook parity | incomplete | #965 | +| Sustainable merge train | incomplete | #966 | + +## 5. Organization-wide live backlog evidence + +A fresh organization-wide GitHub search at this capture includes **all 72 repositories currently accessible through the connected ContextualWisdomLab GitHub account** and reports **2,690 open pull requests** in total. Repository enumeration includes `ContextualWisdomLab/litellm-patched-proxy` and `ContextualWisdomLab/pingora-gateway`; the prior 71-repository and 2,697-PR snapshots are stale. Counts are volatile evidence, not product constants. + +The highest-backlog candidates were re-fetched individually after the organization-wide recount: + +| Rank | Repository | Open PRs | +|---:|---|---:| +| 1 | `ContextualWisdomLab/bandscope` | **188** | +| 2 | `ContextualWisdomLab/TEPP` | 142 | +| 3 | `ContextualWisdomLab/OriginWeave` | 140 | +| 4 | `ContextualWisdomLab/newsdom-api` | 130 | +| 5 | `ContextualWisdomLab/naruon` | 127 | +| 6 | `ContextualWisdomLab/html4tree` | 117 | +| 7 | `ContextualWisdomLab/pg-erd-cloud` | 114 | +| 8 | `ContextualWisdomLab/Orgmetra` | 113 | +| 9 | `ContextualWisdomLab/.github` | 110 | + +BandScope remains the selected delivery lane because it has the largest live backlog **and** the repository owns the end-user rehearsal product whose duplicated workspace slices are contributing directly to buyer-delivery fragmentation. Selection is therefore based on both count and product responsibility, not repository name. + +### 5.1 Current merge-loop evidence + +Protected `develop` currently requires these status contexts, among others: `ci / build-and-test`, `dependency-review`, `security-audit`, `sbom`, `release-preflight`, Windows/macOS build gates, `trivy-fs`, `coverage-evidence`, `opencode-review`, `strix`, `scan-pr-queue`, `osv-scan`, `scorecard`, and CodeQL JavaScript/TypeScript + Python analysis. Required contexts are read from live branch protection before merge; this list is evidence from this capture, not permission to infer future policy. + +Current examples: + +- **#1103 CSV NUL hardening is the canonical desktop export owner.** New duplicate #1121 touched the same three files and added one useful NUL-only assertion. That unique edge was transferred into #1103 in normal non-force history before #1121 was closed unmerged as superseded. No check/review evidence transfers between the PRs; #1103 needs fresh exact-head evidence after the consolidation commit. +- **#1119 Trivy PR-head evidence** correctly identifies a stale local policy-test conflict: CodeQL/Scorecard remain push-only local signals, while Trivy needs ordinary `pull_request` SARIF coverage. A failed temporary source-fix workflow was removed; the permanent policy-test repair belongs in normal source history, not in a dormant self-modifying workflow. +- **#1007/#1094 first-part-handoff** are not yet safe to collapse blindly. #1007 has absorbed the selected-role semantics at resolver/callout level, but mounted `Workspace` must pass its selected `activeRole` through and pin that integration before #1094 can be closed without losing unique production behavior. +- **#1116 is the canonical baseline owner.** Older #1025 was closed only after its unique PRD/TRD/UML/Rust/accuracy/security/accessibility/release requirements were preserved here. +- **Central required-workflow coverage is repaired at the owner, not in BandScope.** Current `ContextualWisdomLab/.github/main` inherited a 99% `scripts/ci` coverage regression from merged #1541. Competing #1547 was closed unmerged after exact patch comparison established #1551 as the more complete canonical owner. #1548 was then stacked on #1551 and merged normally into that branch as `c858ee4065dbda73b38150c95b635c0db7266a39`, preserving its unique CHANGELOG/G-13 traceability plus direct-RCA regression. The #1551 exact head changed as a result, so predecessor checks do not transfer; current security/review workflows are being regenerated before that central repair can land. + +Operational invariant: queued/pending/neutral/skipped/cancelled/failed, predecessor-head, protected-base, self/author, status-only, and model-only evidence is non-passing. Central-gate defects are repaired in the owning central repository; member branches do not weaken gates or use administrative bypass. + +## 6. Prioritized product-technical backlog + +Priority is buyer impact × dependency leverage × risk, not PR age. + +### P0 — trustworthy product completion + +1. **Sustainable exact-head merge throughput — #966.** Consolidate duplicate/superseded writers, require current-head terminal gates, zero actionable threads, and current qualifying independent non-author approval. +2. **Real-audio accuracy — #770.** Licensed corpora, human truth, task-specific metrics, preregistered statistical acceptance, reproducible artifacts. +3. **Rust core-computation migration.** Inventory every DSP/math/vector/matrix/data-science call path and move production ownership to Rust with CPU multithread + explicit accelerator boundaries. +4. **Resource/filesystem authority completion.** Bounded duration/size/allocation, cancellation, path containment, model/PDF bounds, and cross-platform production-path fault tests. + +### P1 — close the rehearsal loop + +5. **Active rehearsal player — #961.** +6. **Crash-safe project/autosave — #962.** +7. **Trusted distribution/update/rollback — #960.** +8. **Private diagnostics/supportability — #963.** +9. **Licensed first-run rehearsal — #964.** +10. **WCAG 2.2 AA + Figma/Storybook/shipped parity — #965.** + +### P2 — analytical depth after the core loop is reliable + +11. Replace unbounded “first-X” plan-field micro-PR growth with coherent engine-generated role guidance, conflict rules, priority, and edit provenance. +12. Replace untraceable weights/priors with documented literature/calibration evidence and sensitivity tests. +13. Preserve the `song → section → role → time` hierarchy; use multilevel/time-dependent evidence where it materially improves rehearsal decisions instead of atomistic aggregation. +14. Harden model artifact provenance and reproducibility across CPU/CUDA/OpenCL/MLX-supported paths. +15. Expand collaboration only behind a stable local-first project/handoff contract and a clear buyer outcome. + +## 7. Quality, UX, test, security, and operability baseline + +### 7.1 Coverage and documentation + +- Python production coverage/docstring policy is 100% in repository guidance. +- Protected JavaScript configs still contain 90% thresholds in parts of the repository; this is below the target contract. +- Target: **100% statement coverage, 100% branch/edge-case coverage, and 100% public/repository-owned API documentation coverage** for owned production surfaces. A lower configured threshold is a gap, not equivalent evidence. + +### 7.2 Realistic validation + +Minimum scenario inventory includes supported 44.1/48/96 kHz audio, mono/stereo, short/long recordings, pickup before bar one, odd meter, tempo change, silence near boundaries, unsupported codecs, moved/replaced files, device changes, cancellation, disk full, corrupted project state, migration interruption, unavailable source separation, and uncertainty correction round trips. + +### 7.3 UI/design acceptance + +Storybook is the executable component/interaction inventory; Figma is reviewed design/handoff evidence, not a second runtime authority. UI changes require screenshot-backed validation of relevant states and edge cases, keyboard/focus behavior, touch target sizing, responsive layout, typography/color contrast, animation/reduced-motion, forms/feedback, navigation, and data visualization alternatives. Repeated visual objects belong behind shared tokens/components, not per-feature drift. + +Customer copy names the next action and never exposes repository/module boundaries. English/Korean semantics stay aligned. + +### 7.4 Security and supply chain + +- ordinary analysis stays local and network-independent; +- files/URLs/metadata/models/PDFs/project state/subprocess output are untrusted; +- capabilities are narrow and allowlisted; no generic exec/read/write surface; +- Dependency Review, OSV, Trivy, CodeQL, secret scanning, SBOM, release provenance, and cross-platform build controls remain fail-closed; +- suppressions are not a substitute for root-cause remediation; +- signing/release credentials never enter repository files or ordinary artifacts. + +### 7.5 Release/operability + +GA requires protected-source identity, reproducible build evidence, checksums, SPDX SBOM/provenance, supported architecture matrix, signatures, macOS notarization, verified update metadata, offline startup, and tested repair/rollback. A development artifact alone is not GA evidence. + +## 8. UML / state supplements + +### 8.1 Import → analyze → rehearse + +```mermaid +sequenceDiagram + actor U as User + participant UI as React Workspace + participant T as Tauri Shell + participant V as Rust Authority Boundary + participant O as Analysis Orchestration + participant R as Rust Analysis Core + + U->>UI: Choose local audio + UI->>T: typed intake command + T->>V: validate path/project/resource authority + V-->>T: admitted source reference + T->>O: start bounded analysis job + O->>R: compute section/role/temporal evidence + R-->>O: versioned evidence + confidence + O-->>T: progress / completed result + T-->>UI: analysis-job-updated + UI-->>U: rehearsal action + uncertainty + correction path +``` + +### 8.2 Project state machine + +```mermaid +stateDiagram-v2 + [*] --> Clean + Clean --> Dirty: accepted mutation + Dirty --> Staging: autosave/manual save + Staging --> Published: validate + atomic replace + Staging --> Dirty: failure; retain known-good + Published --> Dirty: next mutation + Published --> RecoveryAvailable: unclean shutdown/newer recovery evidence + RecoveryAvailable --> Published: restore validated snapshot + RecoveryAvailable --> Clean: discard recovery evidence ``` -Waiting for one train's external review or hosted runner is not a reason to stop work on an independent train. It is also not permission to create a competing writer or transfer stale evidence. +## 9. Research and standards traceability -## BandScope 1.0 commercial definition of done +Standards are evaluation structures, not decoration: -### Product outcome +- ISO/IEC 25010:2023 supplies the product-quality model for specifying/evaluating software quality characteristics. +- NIST SP 800-218 SSDF v1.1 supplies outcome-oriented secure-development practices and traceable security requirements/design decisions. +- WCAG 2.2 is the current W3C Recommendation baseline for desktop-webview accessibility. +- MIREX task definitions provide domain-relevant precedent using real audio and human/listener annotation. +- MIR evidence remains task-specific: Foote for self-similarity/novelty, Viterbi for sequence decoding, Le Roux et al. for SI-SDR, and benchmark-specific corpora/metrics for harmony. These references do not justify unrelated hand-tuned product weights. -- A clean supported Windows or macOS installation reaches one useful rehearsal action without terminal setup. -- A licensed demo and a user-selected local file use the same production intake, decode, analysis, player, save, and recovery boundaries. -- Analysis quality is reported with recognized metrics, fixture rights, uncertainty, backend parity, and explicit limitations. -- The user can repeat a selected passage using a deterministic, accessible rehearsal player. -- Project work survives ordinary restart, crash, interrupted write, migration, and supported rollback. -- A bounded rehearsal handoff preserves provenance without granting filesystem or network authority. +### References (APA 7th) -### Trust, privacy, and operations +Foote, J. (1999). Visualizing music and audio using self-similarity. In *Proceedings of the Seventh ACM International Conference on Multimedia* (pp. 77–80). Association for Computing Machinery. -- Windows artifacts are signed; macOS artifacts are signed and notarized. -- The updater verifies signatures/digests, works offline when unavailable, and has a tested recovery path. -- No ordinary log or support artifact contains raw audio, project payloads, credentials, absolute paths, or unnecessary PII. -- Support evidence is deterministic, bounded, user-previewable, and useful without uploading the song. -- All security, dependency, model, benchmark, and release artifacts are bound to the exact protected source commit. +International Organization for Standardization, & International Electrotechnical Commission. (2023). *ISO/IEC 25010:2023 Systems and software engineering—Systems and software Quality Requirements and Evaluation (SQuaRE)—Product quality model* (2nd ed.). ISO. -### Accessibility and design +Le Roux, J., Wisdom, S., Erdogan, H., & Hershey, J. R. (2019). SDR—Half-baked or well done? In *2019 IEEE International Conference on Acoustics, Speech and Signal Processing (ICASSP)* (pp. 626–630). IEEE. -- Keyboard-only and screen-reader users complete first-run, import, analyze, rehearse, save/recover, share, support, and update-decision journeys. -- Charts, timelines, waveforms, confidence displays, and exports preserve exact values, units, warnings, and uncertainty. -- Code tokens, Storybook stories, Figma components, localization keys, runtime version, and shipped UI have an explicit parity matrix. -- Korean and English provide equivalent choices, limitations, privacy statements, and next actions. +Music Information Retrieval Evaluation eXchange. (2025). *Audio beat tracking*. MIREX Wiki. https://music-ir.org/mirex/wiki/2025:Audio_Beat_Tracking -### Engineering evidence +Souppaya, M., Scarfone, K., & Dodson, D. (2022). *Secure Software Development Framework (SSDF) Version 1.1: Recommendations for mitigating the risk of software vulnerabilities* (NIST Special Publication 800-218). National Institute of Standards and Technology. https://doi.org/10.6028/NIST.SP.800-218 -- Every open PR belongs to exactly one train and has an explicit disposition. -- There is one active canonical writer per product boundary or a declared stack order. -- Duplicate/superseded work is closed only after unique tests and requirements are transferred. -- Every merged exact head has all required terminal-success checks, qualifying independent approval, zero unresolved actionable threads, 100% repository-owned production statement/branch coverage, and complete public API documentation. -- The final release includes checksums, SBOM, provenance/attestation, migration/recovery evidence, accessibility evidence, and MIR accuracy evidence. +Viterbi, A. J. (1967). Error bounds for convolutional codes and an asymptotically optimum decoding algorithm. *IEEE Transactions on Information Theory, 13*(2), 260–269. -## Explicit non-goals +World Wide Web Consortium. (2023). *Web Content Accessibility Guidelines (WCAG) 2.2*. https://www.w3.org/TR/WCAG22/ -- Do not merge all 122 open PRs merely to reduce the count. -- Do not turn BandScope into a DAW, notation editor, or mandatory cloud service. -- Do not claim unsigned validation artifacts are releases. -- Do not use synthetic arrays, mocked browser success, skipped GPU execution, stale checks, or predecessor-head approval as product evidence. -- Do not treat Figma labels or unimplemented Storybook states as shipped features. -- Do not silently discard project fields, unsupported analysis, migration data, or user corrections. +## 10. Re-runnable verification -## Known limitations of this snapshot +```bash +# protected source identity +git rev-parse develop + +# organization-wide open PR count +gh api search/issues -f q='org:ContextualWisdomLab is:pr is:open' --jq '.total_count' + +# BandScope current queue +gh pr list --state open --limit 500 --json number --jq 'length' + +# exact-head merge evidence for a candidate +gh pr view --json number,state,isDraft,headRefOid,baseRefOid,reviews,statusCheckRollup + +# product/doctoring Mermaid inventory +git grep -n '```mermaid' -- docs ARCHITECTURE.md + +# JS threshold gap +git grep -n '90' -- apps/desktop/vite.config.ts packages/shared-types/vitest.config.ts + +# Rust numerical ownership and remaining Python production modules +git grep -n 'checkerboard_novelty\|viterbi_decode' -- services/analysis-engine/rust services/analysis-engine/src +find services/analysis-engine/src/bandscope_analysis -type f -name '*.py' -print + +# real-audio test fixture inventory +find . -type f \( -path '*/tests/*' -o -path '*/test/*' \) \ + \( -iname '*.wav' -o -iname '*.flac' -o -iname '*.mp3' \) -not -path './.git/*' -print +``` -- Exact PR head SHAs were frozen on 2026-08-24 into `docs/product-readiness/open-pr-queue.json`; any branch that advances after that capture invalidates its own row until the next refresh. -- The 2026-08-20 seed left head SHAs unfrozen; the 2026-08-24 refresh supersedes it with exact-head capture and explicit triage of the 40 additions (no `T8` remainder). -- This document does not assert that any existing PR is approved, passing, or safe to merge. In particular, the recorded `security-audit`, `trivy-fs`, and intermittent Strix failures are gate evidence, not per-change verdicts. -- The Figma inspection is a point-in-time metadata/structure review; visual and interaction acceptance remains issue #965 work. -- The investigation created requirements and a convergence plan. It did not implement, merge, sign, notarize, benchmark, or release the product. +Every GitHub state in this document is capture-time evidence. Immediately before a merge, re-fetch the unchanged exact head, current branch protection, all required checks, review decision, unresolved threads, dependency/ancestry order, and concurrent writer state. From b1dd8c6392f0bcf9824be40a1a65bd62e657c53b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 17:30:38 +0900 Subject: [PATCH 27/77] test(queue): reject invalid predecessor graphs --- .../tests/test_open_pr_queue_manifest.py | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/services/analysis-engine/tests/test_open_pr_queue_manifest.py b/services/analysis-engine/tests/test_open_pr_queue_manifest.py index 689a3b9e2..c1a4ac68e 100644 --- a/services/analysis-engine/tests/test_open_pr_queue_manifest.py +++ b/services/analysis-engine/tests/test_open_pr_queue_manifest.py @@ -46,6 +46,7 @@ def _valid_manifest() -> dict[str, object]: "initial_disposition": "canonical_dependency_security_base", "head_sha": None, "head_sha_status": "refresh_required_before_action", + "predecessor_prs": [], }, { "number": 967, @@ -55,6 +56,7 @@ def _valid_manifest() -> dict[str, object]: "initial_disposition": "triage_required", "head_sha": "b" * 40, "head_sha_status": "exact_current_head", + "predecessor_prs": [783], }, ], } @@ -131,6 +133,37 @@ def test_open_pr_queue_manifest_rejects_unsupported_evidence_fields(mutate, expe verifier.validate_manifest(manifest) +def _set_unknown_predecessor(manifest: dict[str, object]) -> None: + """Point one PR at an identity that is absent from the complete queue.""" + pull_requests = manifest["pull_requests"] + assert isinstance(pull_requests, list) + pull_requests[1]["predecessor_prs"] = [999] + + +def _set_dependency_cycle(manifest: dict[str, object]) -> None: + """Create a two-node predecessor cycle that has no dependency root.""" + pull_requests = manifest["pull_requests"] + assert isinstance(pull_requests, list) + pull_requests[0]["predecessor_prs"] = [967] + + +@pytest.mark.parametrize( + ("mutate", "expected"), + [ + (_set_unknown_predecessor, "unknown predecessor"), + (_set_dependency_cycle, "predecessor cycle"), + ], +) +def test_open_pr_queue_manifest_rejects_invalid_predecessor_graph(mutate, expected: str) -> None: + """Unknown dependency identities and cycles must fail before queue actions.""" + verifier = _load_verifier() + manifest = _valid_manifest() + mutate(manifest) + + with pytest.raises(verifier.ManifestError, match=expected): + verifier.validate_manifest(manifest) + + def test_quickcheck_executes_open_pr_queue_verifier() -> None: """The repository harness must execute the queue contract on every normal quickcheck.""" quickcheck = QUICKCHECK_PATH.read_text(encoding="utf-8") From e3e1c57fd53671ee56fcd1bdd4e69f72d75afc7c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 17:31:49 +0900 Subject: [PATCH 28/77] fix(queue): validate predecessor dependency graph --- scripts/checks/verify_open_pr_queue.py | 53 +++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/scripts/checks/verify_open_pr_queue.py b/scripts/checks/verify_open_pr_queue.py index 7b6b1c1bc..f98c34b4e 100644 --- a/scripts/checks/verify_open_pr_queue.py +++ b/scripts/checks/verify_open_pr_queue.py @@ -38,6 +38,7 @@ "initial_disposition", "head_sha", "head_sha_status", + "predecessor_prs", } ) ALLOWED_INITIAL_DISPOSITIONS = frozenset( @@ -115,6 +116,50 @@ def _require_sha(value: object, field: str) -> str: return text.lower() +def _require_predecessors(value: object, field: str) -> list[int]: + """Return a duplicate-free list of positive predecessor PR identities.""" + predecessors = _require_list(value, field) + normalized: list[int] = [] + seen: set[int] = set() + for index, predecessor in enumerate(predecessors): + if isinstance(predecessor, bool) or not isinstance(predecessor, int) or predecessor <= 0: + _fail(f"{field}[{index}] must be a positive integer") + if predecessor in seen: + _fail(f"{field} contains duplicate predecessor: {predecessor}") + seen.add(predecessor) + normalized.append(predecessor) + return normalized + + +def _validate_predecessor_graph( + predecessors_by_pr: dict[int, list[int]], known_prs: set[int] +) -> None: + """Reject unknown dependency identities and directed predecessor cycles.""" + for number, predecessors in predecessors_by_pr.items(): + for predecessor in predecessors: + if predecessor not in known_prs: + _fail(f"pull request {number} references unknown predecessor: {predecessor}") + if predecessor == number: + _fail(f"pull request {number} has a predecessor cycle") + + visiting: set[int] = set() + visited: set[int] = set() + + def visit(number: int) -> None: + if number in visited: + return + if number in visiting: + _fail(f"predecessor cycle detected at pull request {number}") + visiting.add(number) + for predecessor in predecessors_by_pr.get(number, []): + visit(predecessor) + visiting.remove(number) + visited.add(number) + + for number in known_prs: + visit(number) + + def validate_manifest(manifest: object) -> None: """Validate intrinsic queue invariants without treating the seed as live GitHub evidence.""" root = _require_record(manifest, "manifest") @@ -152,6 +197,7 @@ def validate_manifest(manifest: object) -> None: ) seen_numbers: set[int] = set() + predecessors_by_pr: dict[int, list[int]] = {} for index, raw_pr in enumerate(pull_requests): prefix = f"pull_requests[{index}]" pr = _require_record(raw_pr, prefix) @@ -162,6 +208,9 @@ def validate_manifest(manifest: object) -> None: if number in seen_numbers: _fail(f"duplicate pull request number: {number}") seen_numbers.add(number) + predecessors_by_pr[number] = _require_predecessors( + pr.get("predecessor_prs", []), f"{prefix}.predecessor_prs" + ) _require_non_empty_string(pr.get("title"), f"{prefix}.title") expected_url = f"https://github.com/{REPOSITORY}/pull/{number}" @@ -191,6 +240,8 @@ def validate_manifest(manifest: object) -> None: if head_status != "exact_current_head": _fail(f"{prefix}.head_sha_status must be exact_current_head when head_sha is present") + _validate_predecessor_graph(predecessors_by_pr, seen_numbers) + def load_manifest(path: Path = DEFAULT_MANIFEST_PATH) -> object: """Load the queue JSON without accepting duplicate object keys silently.""" @@ -221,4 +272,4 @@ def main() -> int: if __name__ == "__main__": - raise SystemExit(main()) \ No newline at end of file + raise SystemExit(main()) From 9f996b82e013dd77f61600eb19d61644878e13da Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 17:33:01 +0900 Subject: [PATCH 29/77] test(queue): preserve dependency routing on refresh --- .../tests/test_open_pr_queue_refresh.py | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/services/analysis-engine/tests/test_open_pr_queue_refresh.py b/services/analysis-engine/tests/test_open_pr_queue_refresh.py index 58ecff0ef..946eec491 100644 --- a/services/analysis-engine/tests/test_open_pr_queue_refresh.py +++ b/services/analysis-engine/tests/test_open_pr_queue_refresh.py @@ -52,6 +52,28 @@ def _seed() -> dict[str, object]: } +def _seed_with_predecessor() -> dict[str, object]: + """Return reviewed routing with one explicit dependency edge.""" + seed = _seed() + pull_requests = seed["pull_requests"] + assert isinstance(pull_requests, list) + pull_requests[0]["predecessor_prs"] = [700] + pull_requests.append( + { + "number": 700, + "title": "toolchain root", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/700", + "initial_train": "T0", + "initial_disposition": "triage_required", + "head_sha": "7" * 40, + "head_sha_status": "exact_current_head", + "predecessor_prs": [], + } + ) + seed["open_pr_count"] = 2 + return seed + + def _live_pr( number: int, head_sha: str, @@ -133,6 +155,31 @@ def test_refresh_manifest_updates_exact_heads_and_adds_untriaged_live_prs() -> N assert refreshed["trains"]["T8"]["issue"] == 966 +def test_refresh_preserves_reviewed_predecessors_and_defaults_new_prs_to_root() -> None: + """Live identity refresh must not erase reviewed dependency routing.""" + refresher = _load_refresher() + live = { + "incomplete_results": False, + "pull_requests": [ + _live_pr(700, "7" * 40), + _live_pr(783, "b" * 40), + _live_pr(1002, "c" * 40), + ], + } + + refreshed = refresher.build_refreshed_manifest( + _seed_with_predecessor(), + live, + base_sha="d" * 40, + snapshot_date="2026-09-01", + ) + + by_number = {item["number"]: item for item in refreshed["pull_requests"]} + assert by_number[783]["predecessor_prs"] == [700] + assert by_number[700]["predecessor_prs"] == [] + assert by_number[1002]["predecessor_prs"] == [] + + @pytest.mark.parametrize( ("live", "expected"), [ From 15db16db48909178e187bb5eb9021602f194b24f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 17:34:37 +0900 Subject: [PATCH 30/77] fix(queue): preserve reviewed predecessor routing --- scripts/checks/refresh_open_pr_queue.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/checks/refresh_open_pr_queue.py b/scripts/checks/refresh_open_pr_queue.py index 2b777d475..0b7f39eac 100644 --- a/scripts/checks/refresh_open_pr_queue.py +++ b/scripts/checks/refresh_open_pr_queue.py @@ -134,11 +134,15 @@ def _live_pr_entry( if prior is None: initial_train = UNTRIAGED_TRAIN initial_disposition = "triage_required" + predecessor_prs: list[Any] = [] else: initial_train = _require_text(prior.get("initial_train"), "existing.initial_train") initial_disposition = _require_text( prior.get("initial_disposition"), "existing.initial_disposition" ) + predecessor_prs = list( + _require_list(prior.get("predecessor_prs", []), "existing.predecessor_prs") + ) return { "number": number, @@ -148,6 +152,7 @@ def _live_pr_entry( "initial_disposition": initial_disposition, "head_sha": head_sha, "head_sha_status": "exact_current_head", + "predecessor_prs": predecessor_prs, } From f1cee0a8694cefc3ae7fc98abdf06c8e05ec396d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 19:29:29 +0900 Subject: [PATCH 31/77] test(queue): require explicit overlap succession routing --- .../test_open_pr_queue_overlap_contract.py | 168 ++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 services/analysis-engine/tests/test_open_pr_queue_overlap_contract.py diff --git a/services/analysis-engine/tests/test_open_pr_queue_overlap_contract.py b/services/analysis-engine/tests/test_open_pr_queue_overlap_contract.py new file mode 100644 index 000000000..c3883240c --- /dev/null +++ b/services/analysis-engine/tests/test_open_pr_queue_overlap_contract.py @@ -0,0 +1,168 @@ +"""Regression tests for overlap ownership and explicit PR succession routing.""" + +from __future__ import annotations + +import importlib.util +import sys +from pathlib import Path +from types import ModuleType +from typing import Any + +import pytest + +REPO_ROOT = Path(__file__).resolve().parents[3] +VERIFIER_PATH = REPO_ROOT / "scripts" / "checks" / "verify_open_pr_queue.py" +REFRESHER_PATH = REPO_ROOT / "scripts" / "checks" / "refresh_open_pr_queue.py" + + +def _load_module(path: Path, module_name: str) -> ModuleType: + """Load one queue script without requiring scripts to be a Python package.""" + sys.path.insert(0, str(path.parent)) + try: + spec = importlib.util.spec_from_file_location(module_name, path) + assert spec is not None and spec.loader is not None + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + finally: + sys.path.pop(0) + + +def _manifest() -> dict[str, Any]: + """Return a valid queue with one reviewed overlap and one explicit successor.""" + return { + "schema_version": "1.0.0", + "snapshot_date": "2026-09-01", + "timezone": "Asia/Seoul", + "repository": "ContextualWisdomLab/bandscope", + "base_branch": "develop", + "base_sha": "a" * 40, + "open_pr_count": 3, + "authority_note": "Refresh exact live evidence before action.", + "trains": {"T3": {"description": "Active rehearsal player", "issue": 961}}, + "pull_requests": [ + { + "number": 1007, + "title": "canonical handoff owner", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/1007", + "initial_train": "T3", + "initial_disposition": "triage_required", + "head_sha": "1" * 40, + "head_sha_status": "exact_current_head", + "predecessor_prs": [], + "overlap_prs": [1094], + "successor_pr": None, + }, + { + "number": 1094, + "title": "older overlapping handoff writer", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/1094", + "initial_train": "T3", + "initial_disposition": "triage_required", + "head_sha": "2" * 40, + "head_sha_status": "exact_current_head", + "predecessor_prs": [], + "overlap_prs": [1007], + "successor_pr": 1007, + }, + { + "number": 1120, + "title": "independent collaboration slice", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/1120", + "initial_train": "T3", + "initial_disposition": "triage_required", + "head_sha": "3" * 40, + "head_sha_status": "exact_current_head", + "predecessor_prs": [], + "overlap_prs": [], + "successor_pr": None, + }, + ], + } + + +def test_manifest_accepts_symmetric_overlap_with_explicit_successor() -> None: + """Reviewed overlap may name one canonical successor without implying merge readiness.""" + verifier = _load_module(VERIFIER_PATH, "verify_open_pr_queue_overlap") + verifier.validate_manifest(_manifest()) + + +@pytest.mark.parametrize( + ("mutate", "expected"), + [ + ( + lambda manifest: manifest["pull_requests"][1].update(overlap_prs=[9999]), + "unknown overlap", + ), + ( + lambda manifest: manifest["pull_requests"][1].update(overlap_prs=[1094]), + "overlap itself", + ), + ( + lambda manifest: manifest["pull_requests"][0].update(overlap_prs=[]), + "symmetric", + ), + ( + lambda manifest: manifest["pull_requests"][1].update(successor_pr=1120), + "must also be declared in overlap_prs", + ), + ], +) +def test_manifest_rejects_ambiguous_overlap_or_succession(mutate, expected: str) -> None: + """Unknown, self, unilateral, or unrelated succession evidence must fail closed.""" + verifier = _load_module(VERIFIER_PATH, "verify_open_pr_queue_overlap_invalid") + manifest = _manifest() + mutate(manifest) + + with pytest.raises(verifier.ManifestError, match=expected): + verifier.validate_manifest(manifest) + + +def test_manifest_rejects_successor_cycle() -> None: + """Two PRs cannot each claim the other as their canonical successor.""" + verifier = _load_module(VERIFIER_PATH, "verify_open_pr_queue_successor_cycle") + manifest = _manifest() + manifest["pull_requests"][0]["successor_pr"] = 1094 + + with pytest.raises(verifier.ManifestError, match="successor cycle"): + verifier.validate_manifest(manifest) + + +def test_refresh_preserves_reviewed_overlap_and_defaults_new_prs_to_unowned() -> None: + """A live identity refresh must preserve reviewed overlap routing without inventing ownership.""" + refresher = _load_module(REFRESHER_PATH, "refresh_open_pr_queue_overlap") + seed = _manifest() + seed["open_pr_count"] = 2 + seed["pull_requests"] = seed["pull_requests"][:2] + + def live_pr(number: int, head_sha: str) -> dict[str, object]: + return { + "number": number, + "title": f"PR {number}", + "html_url": f"https://github.com/ContextualWisdomLab/bandscope/pull/{number}", + "state": "open", + "base": {"ref": "develop", "sha": "d" * 40}, + "head": {"sha": head_sha}, + } + + refreshed = refresher.build_refreshed_manifest( + seed, + { + "incomplete_results": False, + "pull_requests": [ + live_pr(1007, "4" * 40), + live_pr(1094, "5" * 40), + live_pr(1120, "6" * 40), + ], + }, + base_sha="d" * 40, + snapshot_date="2026-09-01", + ) + + by_number = {item["number"]: item for item in refreshed["pull_requests"]} + assert by_number[1007]["overlap_prs"] == [1094] + assert by_number[1007]["successor_pr"] is None + assert by_number[1094]["overlap_prs"] == [1007] + assert by_number[1094]["successor_pr"] == 1007 + assert by_number[1120]["overlap_prs"] == [] + assert by_number[1120]["successor_pr"] is None From 6cefa46f9a6d9460efb4d1d3212f1b5f3f4ee2ec Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 19:30:27 +0900 Subject: [PATCH 32/77] fix(queue): validate overlap ownership and succession --- scripts/checks/verify_open_pr_queue.py | 84 +++++++++++++++++++++++--- 1 file changed, 74 insertions(+), 10 deletions(-) diff --git a/scripts/checks/verify_open_pr_queue.py b/scripts/checks/verify_open_pr_queue.py index f98c34b4e..5dd95ab5c 100644 --- a/scripts/checks/verify_open_pr_queue.py +++ b/scripts/checks/verify_open_pr_queue.py @@ -39,6 +39,8 @@ "head_sha", "head_sha_status", "predecessor_prs", + "overlap_prs", + "successor_pr", } ) ALLOWED_INITIAL_DISPOSITIONS = frozenset( @@ -116,21 +118,30 @@ def _require_sha(value: object, field: str) -> str: return text.lower() -def _require_predecessors(value: object, field: str) -> list[int]: - """Return a duplicate-free list of positive predecessor PR identities.""" - predecessors = _require_list(value, field) +def _require_pr_numbers(value: object, field: str) -> list[int]: + """Return a duplicate-free list of positive PR identities.""" + pr_numbers = _require_list(value, field) normalized: list[int] = [] seen: set[int] = set() - for index, predecessor in enumerate(predecessors): - if isinstance(predecessor, bool) or not isinstance(predecessor, int) or predecessor <= 0: + for index, pr_number in enumerate(pr_numbers): + if isinstance(pr_number, bool) or not isinstance(pr_number, int) or pr_number <= 0: _fail(f"{field}[{index}] must be a positive integer") - if predecessor in seen: - _fail(f"{field} contains duplicate predecessor: {predecessor}") - seen.add(predecessor) - normalized.append(predecessor) + if pr_number in seen: + _fail(f"{field} contains duplicate pull request: {pr_number}") + seen.add(pr_number) + normalized.append(pr_number) return normalized +def _require_optional_pr_number(value: object, field: str) -> int | None: + """Return a nullable positive PR identity without accepting booleans.""" + if value is None: + return None + if isinstance(value, bool) or not isinstance(value, int) or value <= 0: + _fail(f"{field} must be null or a positive integer") + return value + + def _validate_predecessor_graph( predecessors_by_pr: dict[int, list[int]], known_prs: set[int] ) -> None: @@ -160,6 +171,50 @@ def visit(number: int) -> None: visit(number) +def _validate_overlap_and_successor_graph( + overlaps_by_pr: dict[int, list[int]], + successor_by_pr: dict[int, int | None], + known_prs: set[int], +) -> None: + """Require symmetric overlap evidence and an acyclic explicit succession relation.""" + for number, overlaps in overlaps_by_pr.items(): + for overlap in overlaps: + if overlap not in known_prs: + _fail(f"pull request {number} references unknown overlap: {overlap}") + if overlap == number: + _fail(f"pull request {number} cannot overlap itself") + if number not in overlaps_by_pr.get(overlap, []): + _fail(f"pull request overlap must be symmetric: {number} <-> {overlap}") + + for number, successor in successor_by_pr.items(): + if successor is None: + continue + if successor not in known_prs: + _fail(f"pull request {number} references unknown successor: {successor}") + if successor == number: + _fail(f"pull request {number} cannot succeed itself") + if successor not in overlaps_by_pr.get(number, []): + _fail(f"pull request {number} successor_pr must also be declared in overlap_prs") + + visiting: set[int] = set() + visited: set[int] = set() + + def visit(number: int) -> None: + if number in visited: + return + if number in visiting: + _fail(f"successor cycle detected at pull request {number}") + visiting.add(number) + successor = successor_by_pr.get(number) + if successor is not None: + visit(successor) + visiting.remove(number) + visited.add(number) + + for number in known_prs: + visit(number) + + def validate_manifest(manifest: object) -> None: """Validate intrinsic queue invariants without treating the seed as live GitHub evidence.""" root = _require_record(manifest, "manifest") @@ -198,6 +253,8 @@ def validate_manifest(manifest: object) -> None: seen_numbers: set[int] = set() predecessors_by_pr: dict[int, list[int]] = {} + overlaps_by_pr: dict[int, list[int]] = {} + successor_by_pr: dict[int, int | None] = {} for index, raw_pr in enumerate(pull_requests): prefix = f"pull_requests[{index}]" pr = _require_record(raw_pr, prefix) @@ -208,9 +265,15 @@ def validate_manifest(manifest: object) -> None: if number in seen_numbers: _fail(f"duplicate pull request number: {number}") seen_numbers.add(number) - predecessors_by_pr[number] = _require_predecessors( + predecessors_by_pr[number] = _require_pr_numbers( pr.get("predecessor_prs", []), f"{prefix}.predecessor_prs" ) + overlaps_by_pr[number] = _require_pr_numbers( + pr.get("overlap_prs", []), f"{prefix}.overlap_prs" + ) + successor_by_pr[number] = _require_optional_pr_number( + pr.get("successor_pr"), f"{prefix}.successor_pr" + ) _require_non_empty_string(pr.get("title"), f"{prefix}.title") expected_url = f"https://github.com/{REPOSITORY}/pull/{number}" @@ -241,6 +304,7 @@ def validate_manifest(manifest: object) -> None: _fail(f"{prefix}.head_sha_status must be exact_current_head when head_sha is present") _validate_predecessor_graph(predecessors_by_pr, seen_numbers) + _validate_overlap_and_successor_graph(overlaps_by_pr, successor_by_pr, seen_numbers) def load_manifest(path: Path = DEFAULT_MANIFEST_PATH) -> object: From 049d8757bc6631458d7e49e36818b617a9c29330 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 19:31:10 +0900 Subject: [PATCH 33/77] fix(queue): preserve reviewed overlap succession routing --- scripts/checks/refresh_open_pr_queue.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/checks/refresh_open_pr_queue.py b/scripts/checks/refresh_open_pr_queue.py index 0b7f39eac..c8e914310 100644 --- a/scripts/checks/refresh_open_pr_queue.py +++ b/scripts/checks/refresh_open_pr_queue.py @@ -135,6 +135,8 @@ def _live_pr_entry( initial_train = UNTRIAGED_TRAIN initial_disposition = "triage_required" predecessor_prs: list[Any] = [] + overlap_prs: list[Any] = [] + successor_pr: int | None = None else: initial_train = _require_text(prior.get("initial_train"), "existing.initial_train") initial_disposition = _require_text( @@ -143,6 +145,8 @@ def _live_pr_entry( predecessor_prs = list( _require_list(prior.get("predecessor_prs", []), "existing.predecessor_prs") ) + overlap_prs = list(_require_list(prior.get("overlap_prs", []), "existing.overlap_prs")) + successor_pr = prior.get("successor_pr") return { "number": number, @@ -153,6 +157,8 @@ def _live_pr_entry( "head_sha": head_sha, "head_sha_status": "exact_current_head", "predecessor_prs": predecessor_prs, + "overlap_prs": overlap_prs, + "successor_pr": successor_pr, } From 807c83284e8552bb38561ebfb6e8ba00fa3ceb5c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 19:32:19 +0900 Subject: [PATCH 34/77] test(queue): make overlap failure oracles causal --- .../tests/test_open_pr_queue_overlap_contract.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/analysis-engine/tests/test_open_pr_queue_overlap_contract.py b/services/analysis-engine/tests/test_open_pr_queue_overlap_contract.py index c3883240c..a1e94ddcc 100644 --- a/services/analysis-engine/tests/test_open_pr_queue_overlap_contract.py +++ b/services/analysis-engine/tests/test_open_pr_queue_overlap_contract.py @@ -91,11 +91,11 @@ def test_manifest_accepts_symmetric_overlap_with_explicit_successor() -> None: ("mutate", "expected"), [ ( - lambda manifest: manifest["pull_requests"][1].update(overlap_prs=[9999]), + lambda manifest: manifest["pull_requests"][0].update(overlap_prs=[9999]), "unknown overlap", ), ( - lambda manifest: manifest["pull_requests"][1].update(overlap_prs=[1094]), + lambda manifest: manifest["pull_requests"][0].update(overlap_prs=[1007]), "overlap itself", ), ( From 50f9a0c41d08d0083f3ee27b72d67b97f6dab922 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 19:33:24 +0900 Subject: [PATCH 35/77] test(queue): include stacked PRs in complete inventory --- .../test_open_pr_queue_stacked_inventory.py | 157 ++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 services/analysis-engine/tests/test_open_pr_queue_stacked_inventory.py diff --git a/services/analysis-engine/tests/test_open_pr_queue_stacked_inventory.py b/services/analysis-engine/tests/test_open_pr_queue_stacked_inventory.py new file mode 100644 index 000000000..feb157f4c --- /dev/null +++ b/services/analysis-engine/tests/test_open_pr_queue_stacked_inventory.py @@ -0,0 +1,157 @@ +"""Regression tests for complete open-PR inventory across stacked base branches.""" + +from __future__ import annotations + +import importlib.util +import sys +from pathlib import Path +from types import ModuleType +from typing import Any + +import pytest + +REPO_ROOT = Path(__file__).resolve().parents[3] +VERIFIER_PATH = REPO_ROOT / "scripts" / "checks" / "verify_open_pr_queue.py" +REFRESHER_PATH = REPO_ROOT / "scripts" / "checks" / "refresh_open_pr_queue.py" + + +def _load_module(path: Path, module_name: str) -> ModuleType: + """Load one queue script without requiring scripts to be a Python package.""" + sys.path.insert(0, str(path.parent)) + try: + spec = importlib.util.spec_from_file_location(module_name, path) + assert spec is not None and spec.loader is not None + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + finally: + sys.path.pop(0) + + +def _seed() -> dict[str, Any]: + """Return a minimal valid queue seed before a complete live refresh.""" + return { + "schema_version": "1.0.0", + "snapshot_date": "2026-09-01", + "timezone": "Asia/Seoul", + "repository": "ContextualWisdomLab/bandscope", + "base_branch": "develop", + "base_sha": "d" * 40, + "open_pr_count": 1, + "authority_note": "Refresh exact live evidence before action.", + "trains": {"T8": {"description": "Live additions awaiting explicit merge-train triage", "issue": 966}}, + "pull_requests": [ + { + "number": 1116, + "title": "canonical baseline", + "url": "https://github.com/ContextualWisdomLab/bandscope/pull/1116", + "initial_train": "T8", + "initial_disposition": "triage_required", + "head_sha": "1" * 40, + "head_sha_status": "exact_current_head", + "predecessor_prs": [], + "overlap_prs": [], + "successor_pr": None, + } + ], + } + + +def _live_pr(number: int, head_sha: str, base_ref: str, base_sha: str) -> dict[str, object]: + """Return one same-repository open PR targeting the supplied live base branch.""" + return { + "number": number, + "title": f"PR {number}", + "html_url": f"https://github.com/ContextualWisdomLab/bandscope/pull/{number}", + "state": "open", + "base": {"ref": base_ref, "sha": base_sha}, + "head": {"sha": head_sha}, + } + + +def test_refresh_includes_direct_and_stacked_open_prs_with_exact_base_tips() -> None: + """A complete queue retains PRs targeting develop and another live branch.""" + refresher = _load_module(REFRESHER_PATH, "refresh_open_pr_queue_stacked") + live = { + "incomplete_results": False, + "pull_requests": [ + _live_pr(1116, "2" * 40, "develop", "d" * 40), + _live_pr(968, "3" * 40, "docs/gap-baseline-2026-08-31", "e" * 40), + ], + } + + refreshed = refresher.build_refreshed_manifest( + _seed(), + live, + base_sha="d" * 40, + snapshot_date="2026-09-01", + base_tips={"develop": "d" * 40, "docs/gap-baseline-2026-08-31": "e" * 40}, + ) + + by_number = {item["number"]: item for item in refreshed["pull_requests"]} + assert set(by_number) == {968, 1116} + assert by_number[1116]["base_ref"] == "develop" + assert by_number[1116]["base_sha"] == "d" * 40 + assert by_number[968]["base_ref"] == "docs/gap-baseline-2026-08-31" + assert by_number[968]["base_sha"] == "e" * 40 + + verifier = _load_module(VERIFIER_PATH, "verify_open_pr_queue_stacked") + verifier.validate_manifest(refreshed) + + +def test_refresh_rejects_pr_base_sha_that_is_not_the_independently_resolved_tip() -> None: + """A PR object's stale base SHA cannot substitute for an independently resolved branch tip.""" + refresher = _load_module(REFRESHER_PATH, "refresh_open_pr_queue_stale_stacked_base") + live = { + "incomplete_results": False, + "pull_requests": [ + _live_pr(1116, "2" * 40, "develop", "d" * 40), + _live_pr(968, "3" * 40, "docs/gap-baseline-2026-08-31", "f" * 40), + ], + } + + with pytest.raises(refresher.RefreshError, match="independently resolved base tip"): + refresher.build_refreshed_manifest( + _seed(), + live, + base_sha="d" * 40, + snapshot_date="2026-09-01", + base_tips={"develop": "d" * 40, "docs/gap-baseline-2026-08-31": "e" * 40}, + ) + + +def test_fetch_live_pull_page_does_not_filter_out_stacked_bases(monkeypatch: pytest.MonkeyPatch) -> None: + """The GitHub pulls request enumerates all open PRs rather than only develop-targeted PRs.""" + refresher = _load_module(REFRESHER_PATH, "refresh_open_pr_queue_all_bases") + observed: dict[str, str] = {} + + def fake_request(target: str, token: str | None) -> tuple[object, str]: + observed["target"] = target + return [], "" + + monkeypatch.setattr(refresher, "_request_github_json", fake_request) + items, has_next = refresher.fetch_live_pull_page(1, 100, None) + + assert items == [] + assert has_next is False + assert "state=open" in observed["target"] + assert "base=" not in observed["target"] + + +def test_fetch_live_branch_sha_encodes_branch_name_without_changing_authority( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """A slash-bearing base ref stays inside the fixed repository API authority.""" + refresher = _load_module(REFRESHER_PATH, "refresh_open_pr_queue_branch_tip") + observed: dict[str, str] = {} + + def fake_request(target: str, token: str | None) -> tuple[object, str]: + observed["target"] = target + return {"commit": {"sha": "e" * 40}}, "" + + monkeypatch.setattr(refresher, "_request_github_json", fake_request) + sha = refresher.fetch_live_branch_sha("docs/gap-baseline-2026-08-31", None) + + assert sha == "e" * 40 + assert observed["target"].startswith("/repos/ContextualWisdomLab/bandscope/branches/") + assert observed["target"].endswith("docs%2Fgap-baseline-2026-08-31") From d5adebef8a3eb6fedadb0d389e0ec9e6bdc5118d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 19:34:16 +0900 Subject: [PATCH 36/77] fix(queue): validate exact base identity for stacked PRs --- scripts/checks/verify_open_pr_queue.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/scripts/checks/verify_open_pr_queue.py b/scripts/checks/verify_open_pr_queue.py index 5dd95ab5c..db6a7c708 100644 --- a/scripts/checks/verify_open_pr_queue.py +++ b/scripts/checks/verify_open_pr_queue.py @@ -36,6 +36,8 @@ "url", "initial_train", "initial_disposition", + "base_ref", + "base_sha", "head_sha", "head_sha_status", "predecessor_prs", @@ -289,6 +291,14 @@ def validate_manifest(manifest: object) -> None: if disposition not in ALLOWED_INITIAL_DISPOSITIONS: _fail(f"{prefix}.initial_disposition is unsupported: {disposition}") + base_ref = pr.get("base_ref") + pr_base_sha = pr.get("base_sha") + if (base_ref is None) != (pr_base_sha is None): + _fail(f"{prefix}.base_ref and {prefix}.base_sha must be present together") + if base_ref is not None: + _require_non_empty_string(base_ref, f"{prefix}.base_ref") + _require_sha(pr_base_sha, f"{prefix}.base_sha") + if "head_sha" not in pr: _fail(f"{prefix}.head_sha is required") head_sha = pr["head_sha"] From 8eaeb88db9928a7407d7fad5b1c4cae4581b9e04 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 19:35:20 +0900 Subject: [PATCH 37/77] fix(queue): enumerate stacked PR bases without filtering --- scripts/checks/refresh_open_pr_queue.py | 66 ++++++++++++++++++++----- 1 file changed, 54 insertions(+), 12 deletions(-) diff --git a/scripts/checks/refresh_open_pr_queue.py b/scripts/checks/refresh_open_pr_queue.py index c8e914310..9838c5505 100644 --- a/scripts/checks/refresh_open_pr_queue.py +++ b/scripts/checks/refresh_open_pr_queue.py @@ -79,6 +79,20 @@ def _require_positive_int(value: object, field: str) -> int: return value +def _normalize_base_tips(base_tips: object | None, protected_base_sha: str) -> dict[str, str]: + """Normalize independently resolved target-branch tips used by live PR records.""" + if base_tips is None: + return {BASE_BRANCH: protected_base_sha} + base_tip_record = _require_record(base_tips, "base_tips") + normalized: dict[str, str] = {} + for raw_ref, raw_sha in base_tip_record.items(): + branch_ref = _require_text(raw_ref, "base_tips branch ref") + normalized[branch_ref] = _require_sha(raw_sha, f"base_tips.{branch_ref}") + if normalized.get(BASE_BRANCH) != protected_base_sha: + _fail(f"base_tips.{BASE_BRANCH} must match the protected base_sha") + return normalized + + def collect_paginated_pulls( fetch_page: PageFetcher, *, @@ -107,7 +121,7 @@ def _live_pr_entry( raw_pr: object, *, index: int, - base_sha: str, + base_tips: dict[str, str], existing: dict[int, dict[str, Any]], ) -> dict[str, object]: """Convert one trusted pulls-API record into the reviewed manifest schema.""" @@ -121,11 +135,13 @@ def _live_pr_entry( _fail(f"pull_requests[{index}].html_url must be {expected_url}") base = _require_record(pr.get("base"), f"pull_requests[{index}].base") - if base.get("ref") != BASE_BRANCH: - _fail(f"pull_requests[{index}].base.ref must be {BASE_BRANCH}") + base_ref = _require_text(base.get("ref"), f"pull_requests[{index}].base.ref") + resolved_base_sha = base_tips.get(base_ref) + if resolved_base_sha is None: + _fail(f"pull_requests[{index}].base.ref has no independently resolved base tip") pr_base_sha = _require_sha(base.get("sha"), f"pull_requests[{index}].base.sha") - if pr_base_sha != base_sha: - _fail(f"pull_requests[{index}].base.sha must match the live {BASE_BRANCH} tip") + if pr_base_sha != resolved_base_sha: + _fail(f"pull_requests[{index}].base.sha must match the independently resolved base tip") head = _require_record(pr.get("head"), f"pull_requests[{index}].head") head_sha = _require_sha(head.get("sha"), f"pull_requests[{index}].head.sha") @@ -154,6 +170,8 @@ def _live_pr_entry( "url": expected_url, "initial_train": initial_train, "initial_disposition": initial_disposition, + "base_ref": base_ref, + "base_sha": resolved_base_sha, "head_sha": head_sha, "head_sha_status": "exact_current_head", "predecessor_prs": predecessor_prs, @@ -168,6 +186,7 @@ def build_refreshed_manifest( *, base_sha: str, snapshot_date: str, + base_tips: object | None = None, ) -> dict[str, Any]: """Build a deterministic complete queue while preserving reviewed routing metadata.""" try: @@ -180,6 +199,7 @@ def build_refreshed_manifest( _fail("live pull-request inventory is incomplete") pulls = _require_list(live.get("pull_requests"), "live result.pull_requests") normalized_base_sha = _require_sha(base_sha, "base_sha") + normalized_base_tips = _normalize_base_tips(base_tips, normalized_base_sha) try: datetime.strptime(snapshot_date, "%Y-%m-%d") except (TypeError, ValueError) as exc: @@ -198,7 +218,7 @@ def build_refreshed_manifest( entry = _live_pr_entry( raw_pr, index=index, - base_sha=normalized_base_sha, + base_tips=normalized_base_tips, existing=existing, ) number = int(entry["number"]) @@ -269,27 +289,33 @@ def _request_github_json(target: str, token: str | None) -> tuple[object, str]: return payload, link -def fetch_live_base_sha(token: str | None) -> str: - """Resolve the current develop branch tip from the canonical GitHub API.""" - target = f"{REPOSITORY_API_PREFIX}branches/{BASE_BRANCH}" +def fetch_live_branch_sha(branch_ref: str, token: str | None) -> str: + """Resolve one current same-repository base branch tip through a fixed API authority.""" + normalized_ref = _require_text(branch_ref, "branch_ref") + encoded_ref = urllib.parse.quote(normalized_ref, safe="") + target = f"{REPOSITORY_API_PREFIX}branches/{encoded_ref}" payload, _ = _request_github_json(target, token) branch = _require_record(payload, "branch") commit = _require_record(branch.get("commit"), "branch.commit") return _require_sha(commit.get("sha"), "branch.commit.sha") +def fetch_live_base_sha(token: str | None) -> str: + """Resolve the current protected develop tip for backward-compatible callers.""" + return fetch_live_branch_sha(BASE_BRANCH, token) + + def fetch_live_pull_page( page: int, page_size: int, token: str | None, ) -> tuple[list[dict[str, Any]], bool]: - """Fetch one bounded page of open develop-targeted PRs from GitHub.""" + """Fetch one bounded page of all open PRs so stacked bases remain in the queue.""" _require_positive_int(page, "page") _require_positive_int(page_size, "page_size") query = urllib.parse.urlencode( { "state": "open", - "base": BASE_BRANCH, "per_page": page_size, "page": page, "sort": "created", @@ -304,6 +330,20 @@ def fetch_live_pull_page( return items, 'rel="next"' in link +def resolve_live_base_tips(live_result: object, token: str | None) -> dict[str, str]: + """Resolve every distinct current PR base branch after the complete live inventory read.""" + live = _require_record(live_result, "live result") + if live.get("incomplete_results") is not False: + _fail("live pull-request inventory is incomplete") + pulls = _require_list(live.get("pull_requests"), "live result.pull_requests") + base_refs: set[str] = {BASE_BRANCH} + for index, raw_pr in enumerate(pulls): + pr = _require_record(raw_pr, f"pull_requests[{index}]") + base = _require_record(pr.get("base"), f"pull_requests[{index}].base") + base_refs.add(_require_text(base.get("ref"), f"pull_requests[{index}].base.ref")) + return {branch_ref: fetch_live_branch_sha(branch_ref, token) for branch_ref in sorted(base_refs)} + + def _write_manifest_atomic(manifest: dict[str, Any]) -> None: """Atomically replace the canonical manifest without following a symlink target.""" if MANIFEST_PATH.is_symlink(): @@ -328,16 +368,18 @@ def main() -> int: try: seed = load_manifest(MANIFEST_PATH) token = os.environ.get("GITHUB_TOKEN") - base_sha = fetch_live_base_sha(token) live_result = collect_paginated_pulls( lambda page, size: fetch_live_pull_page(page, size, token) ) + base_tips = resolve_live_base_tips(live_result, token) + base_sha = base_tips[BASE_BRANCH] snapshot_date = datetime.now(ZoneInfo("Asia/Seoul")).date().isoformat() refreshed = build_refreshed_manifest( seed, live_result, base_sha=base_sha, snapshot_date=snapshot_date, + base_tips=base_tips, ) _write_manifest_atomic(refreshed) except (ManifestError, RefreshError) as exc: From 6b750e2e4d1260c8b0e417054d934b3510b08234 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 19:39:47 +0900 Subject: [PATCH 38/77] test(queue): make live branch tip authoritative over PR base snapshot --- .../test_open_pr_queue_stacked_inventory.py | 67 +++++++++++++++---- 1 file changed, 55 insertions(+), 12 deletions(-) diff --git a/services/analysis-engine/tests/test_open_pr_queue_stacked_inventory.py b/services/analysis-engine/tests/test_open_pr_queue_stacked_inventory.py index feb157f4c..84bfaf837 100644 --- a/services/analysis-engine/tests/test_open_pr_queue_stacked_inventory.py +++ b/services/analysis-engine/tests/test_open_pr_queue_stacked_inventory.py @@ -39,7 +39,12 @@ def _seed() -> dict[str, Any]: "base_sha": "d" * 40, "open_pr_count": 1, "authority_note": "Refresh exact live evidence before action.", - "trains": {"T8": {"description": "Live additions awaiting explicit merge-train triage", "issue": 966}}, + "trains": { + "T8": { + "description": "Live additions awaiting explicit merge-train triage", + "issue": 966, + } + }, "pull_requests": [ { "number": 1116, @@ -57,8 +62,13 @@ def _seed() -> dict[str, Any]: } -def _live_pr(number: int, head_sha: str, base_ref: str, base_sha: str) -> dict[str, object]: - """Return one same-repository open PR targeting the supplied live base branch.""" +def _live_pr( + number: int, + head_sha: str, + base_ref: str, + base_sha: str, +) -> dict[str, object]: + """Return one same-repository open PR targeting the supplied branch.""" return { "number": number, "title": f"PR {number}", @@ -85,7 +95,10 @@ def test_refresh_includes_direct_and_stacked_open_prs_with_exact_base_tips() -> live, base_sha="d" * 40, snapshot_date="2026-09-01", - base_tips={"develop": "d" * 40, "docs/gap-baseline-2026-08-31": "e" * 40}, + base_tips={ + "develop": "d" * 40, + "docs/gap-baseline-2026-08-31": "e" * 40, + }, ) by_number = {item["number"]: item for item in refreshed["pull_requests"]} @@ -99,14 +112,41 @@ def test_refresh_includes_direct_and_stacked_open_prs_with_exact_base_tips() -> verifier.validate_manifest(refreshed) -def test_refresh_rejects_pr_base_sha_that_is_not_the_independently_resolved_tip() -> None: - """A PR object's stale base SHA cannot substitute for an independently resolved branch tip.""" - refresher = _load_module(REFRESHER_PATH, "refresh_open_pr_queue_stale_stacked_base") +def test_refresh_uses_independently_resolved_tip_when_pr_base_snapshot_is_stale() -> None: + """The live branch lookup, not the PR object's base SHA, owns current base identity.""" + refresher = _load_module(REFRESHER_PATH, "refresh_open_pr_queue_stale_base_snapshot") + live = { + "incomplete_results": False, + "pull_requests": [ + _live_pr(1116, "2" * 40, "develop", "9" * 40), + _live_pr(968, "3" * 40, "docs/gap-baseline-2026-08-31", "8" * 40), + ], + } + + refreshed = refresher.build_refreshed_manifest( + _seed(), + live, + base_sha="d" * 40, + snapshot_date="2026-09-01", + base_tips={ + "develop": "d" * 40, + "docs/gap-baseline-2026-08-31": "e" * 40, + }, + ) + + by_number = {item["number"]: item for item in refreshed["pull_requests"]} + assert by_number[1116]["base_sha"] == "d" * 40 + assert by_number[968]["base_sha"] == "e" * 40 + + +def test_refresh_rejects_base_without_independently_resolved_tip() -> None: + """Every current PR target branch still needs a separately resolved live tip.""" + refresher = _load_module(REFRESHER_PATH, "refresh_open_pr_queue_missing_base_tip") live = { "incomplete_results": False, "pull_requests": [ _live_pr(1116, "2" * 40, "develop", "d" * 40), - _live_pr(968, "3" * 40, "docs/gap-baseline-2026-08-31", "f" * 40), + _live_pr(968, "3" * 40, "docs/gap-baseline-2026-08-31", "8" * 40), ], } @@ -116,12 +156,14 @@ def test_refresh_rejects_pr_base_sha_that_is_not_the_independently_resolved_tip( live, base_sha="d" * 40, snapshot_date="2026-09-01", - base_tips={"develop": "d" * 40, "docs/gap-baseline-2026-08-31": "e" * 40}, + base_tips={"develop": "d" * 40}, ) -def test_fetch_live_pull_page_does_not_filter_out_stacked_bases(monkeypatch: pytest.MonkeyPatch) -> None: - """The GitHub pulls request enumerates all open PRs rather than only develop-targeted PRs.""" +def test_fetch_live_pull_page_does_not_filter_out_stacked_bases( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """The GitHub pulls request enumerates all open PRs, including stacked bases.""" refresher = _load_module(REFRESHER_PATH, "refresh_open_pr_queue_all_bases") observed: dict[str, str] = {} @@ -153,5 +195,6 @@ def fake_request(target: str, token: str | None) -> tuple[object, str]: sha = refresher.fetch_live_branch_sha("docs/gap-baseline-2026-08-31", None) assert sha == "e" * 40 - assert observed["target"].startswith("/repos/ContextualWisdomLab/bandscope/branches/") + expected_prefix = "/repos/ContextualWisdomLab/bandscope/branches/" + assert observed["target"].startswith(expected_prefix) assert observed["target"].endswith("docs%2Fgap-baseline-2026-08-31") From ea0f180f2d4e3126a70d6600ed508f00056aacf1 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 19:40:57 +0900 Subject: [PATCH 39/77] fix(queue): trust independently resolved live base tips --- scripts/checks/refresh_open_pr_queue.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/checks/refresh_open_pr_queue.py b/scripts/checks/refresh_open_pr_queue.py index 9838c5505..38f7fe10a 100644 --- a/scripts/checks/refresh_open_pr_queue.py +++ b/scripts/checks/refresh_open_pr_queue.py @@ -139,9 +139,9 @@ def _live_pr_entry( resolved_base_sha = base_tips.get(base_ref) if resolved_base_sha is None: _fail(f"pull_requests[{index}].base.ref has no independently resolved base tip") - pr_base_sha = _require_sha(base.get("sha"), f"pull_requests[{index}].base.sha") - if pr_base_sha != resolved_base_sha: - _fail(f"pull_requests[{index}].base.sha must match the independently resolved base tip") + # The pull object can retain an older target SHA when its target branch advances. + # Validate that snapshot as untrusted input, but do not let it override the branch lookup. + _require_sha(base.get("sha"), f"pull_requests[{index}].base.sha") head = _require_record(pr.get("head"), f"pull_requests[{index}].head") head_sha = _require_sha(head.get("sha"), f"pull_requests[{index}].head.sha") @@ -392,4 +392,4 @@ def main() -> int: if __name__ == "__main__": - raise SystemExit(main()) + raise SystemExit(main()) \ No newline at end of file From 62afa1ab8fa6946dd23d665457e970db13c7d912 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 19:42:49 +0900 Subject: [PATCH 40/77] test(queue): stop treating stale PR base snapshots as live authority --- .../analysis-engine/tests/test_open_pr_queue_refresh.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/services/analysis-engine/tests/test_open_pr_queue_refresh.py b/services/analysis-engine/tests/test_open_pr_queue_refresh.py index 946eec491..dfa975519 100644 --- a/services/analysis-engine/tests/test_open_pr_queue_refresh.py +++ b/services/analysis-engine/tests/test_open_pr_queue_refresh.py @@ -147,7 +147,8 @@ def test_refresh_manifest_updates_exact_heads_and_adds_untriaged_live_prs() -> N assert refreshed["pull_requests"][0]["head_sha"] == "b" * 40 assert refreshed["pull_requests"][0]["head_sha_status"] == "exact_current_head" assert ( - refreshed["pull_requests"][0]["initial_disposition"] == "canonical_dependency_security_base" + refreshed["pull_requests"][0]["initial_disposition"] + == "canonical_dependency_security_base" ) assert refreshed["pull_requests"][1]["number"] == 1002 assert refreshed["pull_requests"][1]["initial_train"] == "T8" @@ -212,7 +213,7 @@ def test_refresh_preserves_reviewed_predecessors_and_defaults_new_prs_to_root() ( { "incomplete_results": False, - "pull_requests": [_live_pr(783, "b" * 40, base_sha="e" * 40)], + "pull_requests": [_live_pr(783, "b" * 40, base_sha="not-a-sha")], }, "base.sha", ), @@ -221,7 +222,7 @@ def test_refresh_preserves_reviewed_predecessors_and_defaults_new_prs_to_root() def test_refresh_manifest_rejects_untrustworthy_live_inventory( live: dict[str, object], expected: str ) -> None: - """Incomplete, duplicate, malformed, or wrong-base live evidence fails closed.""" + """Incomplete, duplicate, malformed, or unresolved-base live evidence fails closed.""" refresher = _load_refresher() with pytest.raises(refresher.RefreshError, match=expected): From 337d9522c2485422f2eeb31b6d2cda4c203f1719 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 19:46:21 +0900 Subject: [PATCH 41/77] test(queue): resolve target tips from one branch snapshot --- .../test_open_pr_queue_stacked_inventory.py | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/services/analysis-engine/tests/test_open_pr_queue_stacked_inventory.py b/services/analysis-engine/tests/test_open_pr_queue_stacked_inventory.py index 84bfaf837..8ac9dc138 100644 --- a/services/analysis-engine/tests/test_open_pr_queue_stacked_inventory.py +++ b/services/analysis-engine/tests/test_open_pr_queue_stacked_inventory.py @@ -198,3 +198,69 @@ def fake_request(target: str, token: str | None) -> tuple[object, str]: expected_prefix = "/repos/ContextualWisdomLab/bandscope/branches/" assert observed["target"].startswith(expected_prefix) assert observed["target"].endswith("docs%2Fgap-baseline-2026-08-31") + + +def test_resolve_live_base_tips_uses_one_matching_refs_snapshot( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """All required target tips come from one bounded branch-ref snapshot.""" + refresher = _load_module(REFRESHER_PATH, "refresh_open_pr_queue_branch_snapshot") + observed: list[str] = [] + live = { + "incomplete_results": False, + "pull_requests": [ + _live_pr(1116, "2" * 40, "develop", "9" * 40), + _live_pr(968, "3" * 40, "docs/gap-baseline-2026-08-31", "8" * 40), + ], + } + + def fake_request(target: str, token: str | None) -> tuple[object, str]: + observed.append(target) + return ( + [ + {"ref": "refs/heads/develop", "object": {"type": "commit", "sha": "d" * 40}}, + { + "ref": "refs/heads/docs/gap-baseline-2026-08-31", + "object": {"type": "commit", "sha": "e" * 40}, + }, + {"ref": "refs/heads/unrelated", "object": {"type": "commit", "sha": "f" * 40}}, + ], + "", + ) + + monkeypatch.setattr(refresher, "_request_github_json", fake_request) + tips = refresher.resolve_live_base_tips(live, None) + + assert observed == [ + "/repos/ContextualWisdomLab/bandscope/git/matching-refs/heads/" + ] + assert tips == { + "develop": "d" * 40, + "docs/gap-baseline-2026-08-31": "e" * 40, + } + + +def test_resolve_live_base_tips_rejects_target_missing_from_branch_snapshot( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """A live PR target absent from the independent branch snapshot fails closed.""" + refresher = _load_module(REFRESHER_PATH, "refresh_open_pr_queue_missing_snapshot_ref") + live = { + "incomplete_results": False, + "pull_requests": [ + _live_pr(968, "3" * 40, "docs/gap-baseline-2026-08-31", "8" * 40), + ], + } + + def fake_request(target: str, token: str | None) -> tuple[object, str]: + return ( + [ + {"ref": "refs/heads/develop", "object": {"type": "commit", "sha": "d" * 40}}, + ], + "", + ) + + monkeypatch.setattr(refresher, "_request_github_json", fake_request) + + with pytest.raises(refresher.RefreshError, match="absent from the live branch snapshot"): + refresher.resolve_live_base_tips(live, None) From dbc69bd30260a074875a0e1a0b53feecd2f10f58 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 19:47:20 +0900 Subject: [PATCH 42/77] fix(queue): resolve stacked bases from one bounded ref snapshot --- scripts/checks/refresh_open_pr_queue.py | 38 +++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/scripts/checks/refresh_open_pr_queue.py b/scripts/checks/refresh_open_pr_queue.py index 38f7fe10a..16455ec80 100644 --- a/scripts/checks/refresh_open_pr_queue.py +++ b/scripts/checks/refresh_open_pr_queue.py @@ -24,6 +24,7 @@ BASE_BRANCH = "develop" GITHUB_API_HOST = "api.github.com" REPOSITORY_API_PREFIX = f"/repos/{REPOSITORY}/" +BRANCH_REF_PREFIX = "refs/heads/" PAGE_SIZE = 100 MAX_PAGES = 10 MAX_RESPONSE_BYTES = 4 * 1024 * 1024 @@ -300,6 +301,34 @@ def fetch_live_branch_sha(branch_ref: str, token: str | None) -> str: return _require_sha(commit.get("sha"), "branch.commit.sha") +def fetch_live_branch_index(token: str | None) -> dict[str, str]: + """Resolve all branch tips from one bounded Git refs snapshot.""" + target = f"{REPOSITORY_API_PREFIX}git/matching-refs/heads/" + payload, _ = _request_github_json(target, token) + refs = _require_list(payload, "branch snapshot") + branch_index: dict[str, str] = {} + for index, raw_ref in enumerate(refs): + ref_record = _require_record(raw_ref, f"branch snapshot[{index}]") + full_ref = _require_text(ref_record.get("ref"), f"branch snapshot[{index}].ref") + if not full_ref.startswith(BRANCH_REF_PREFIX) or full_ref == BRANCH_REF_PREFIX: + _fail(f"branch snapshot[{index}].ref must be a refs/heads branch") + branch_ref = full_ref[len(BRANCH_REF_PREFIX) :] + target_object = _require_record( + ref_record.get("object"), f"branch snapshot[{index}].object" + ) + if target_object.get("type") != "commit": + _fail(f"branch snapshot[{index}].object.type must be commit") + branch_sha = _require_sha( + target_object.get("sha"), f"branch snapshot[{index}].object.sha" + ) + if branch_ref in branch_index: + _fail(f"branch snapshot contains duplicate branch ref: {branch_ref}") + branch_index[branch_ref] = branch_sha + if BASE_BRANCH not in branch_index: + _fail(f"live branch snapshot is missing protected base {BASE_BRANCH}") + return branch_index + + def fetch_live_base_sha(token: str | None) -> str: """Resolve the current protected develop tip for backward-compatible callers.""" return fetch_live_branch_sha(BASE_BRANCH, token) @@ -331,7 +360,7 @@ def fetch_live_pull_page( def resolve_live_base_tips(live_result: object, token: str | None) -> dict[str, str]: - """Resolve every distinct current PR base branch after the complete live inventory read.""" + """Select every current PR target from one independently resolved branch snapshot.""" live = _require_record(live_result, "live result") if live.get("incomplete_results") is not False: _fail("live pull-request inventory is incomplete") @@ -341,7 +370,12 @@ def resolve_live_base_tips(live_result: object, token: str | None) -> dict[str, pr = _require_record(raw_pr, f"pull_requests[{index}]") base = _require_record(pr.get("base"), f"pull_requests[{index}].base") base_refs.add(_require_text(base.get("ref"), f"pull_requests[{index}].base.ref")) - return {branch_ref: fetch_live_branch_sha(branch_ref, token) for branch_ref in sorted(base_refs)} + + branch_index = fetch_live_branch_index(token) + missing = sorted(base_refs - branch_index.keys()) + if missing: + _fail(f"target base ref is absent from the live branch snapshot: {missing[0]}") + return {branch_ref: branch_index[branch_ref] for branch_ref in sorted(base_refs)} def _write_manifest_atomic(manifest: dict[str, Any]) -> None: From fbc3bd38bcc2a31e80f6cdba7e315e4103755296 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 19:58:33 +0900 Subject: [PATCH 43/77] test(queue): reproduce truncated branch-tip inventory --- .../test_open_pr_queue_branch_pagination.py | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 services/analysis-engine/tests/test_open_pr_queue_branch_pagination.py diff --git a/services/analysis-engine/tests/test_open_pr_queue_branch_pagination.py b/services/analysis-engine/tests/test_open_pr_queue_branch_pagination.py new file mode 100644 index 000000000..9d3656bb4 --- /dev/null +++ b/services/analysis-engine/tests/test_open_pr_queue_branch_pagination.py @@ -0,0 +1,71 @@ +"""Regression tests for bounded pagination of live branch-tip inventory.""" + +from __future__ import annotations + +import importlib.util +import sys +from pathlib import Path +from types import ModuleType + +import pytest + +REPO_ROOT = Path(__file__).resolve().parents[3] +REFRESHER_PATH = REPO_ROOT / "scripts" / "checks" / "refresh_open_pr_queue.py" + + +def _load_refresher() -> ModuleType: + """Load the queue refresher without making scripts a Python package.""" + sys.path.insert(0, str(REFRESHER_PATH.parent)) + try: + spec = importlib.util.spec_from_file_location( + "refresh_open_pr_queue_branch_pagination", REFRESHER_PATH + ) + assert spec is not None and spec.loader is not None + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + finally: + sys.path.pop(0) + + +def _branch_ref(branch_ref: str, sha: str) -> dict[str, object]: + """Return one matching-refs API branch record.""" + return { + "ref": f"refs/heads/{branch_ref}", + "object": {"type": "commit", "sha": sha}, + } + + +def test_branch_index_follows_matching_refs_pagination( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """A target branch present only after page one must still resolve exactly.""" + refresher = _load_refresher() + observed: list[str] = [] + + def fake_request(target: str, token: str | None) -> tuple[object, str]: + observed.append(target) + if target.endswith("?per_page=100&page=1"): + return ( + [_branch_ref("develop", "d" * 40)], + '; rel="next"', + ) + if target.endswith("?per_page=100&page=2"): + return ( + [_branch_ref("docs/gap-baseline-2026-08-31", "e" * 40)], + "", + ) + raise AssertionError(f"unexpected GitHub target: {target}") + + monkeypatch.setattr(refresher, "_request_github_json", fake_request) + + branch_index = refresher.fetch_live_branch_index(None) + + assert branch_index == { + "develop": "d" * 40, + "docs/gap-baseline-2026-08-31": "e" * 40, + } + assert observed == [ + "/repos/ContextualWisdomLab/bandscope/git/matching-refs/heads/?per_page=100&page=1", + "/repos/ContextualWisdomLab/bandscope/git/matching-refs/heads/?per_page=100&page=2", + ] From 6c9a36c128eb53ee436f784e2217f65cb88651fe Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 19:59:58 +0900 Subject: [PATCH 44/77] fix(queue): paginate live branch-tip inventory --- scripts/checks/refresh_open_pr_queue.py | 53 ++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/scripts/checks/refresh_open_pr_queue.py b/scripts/checks/refresh_open_pr_queue.py index 16455ec80..05d1c3dce 100644 --- a/scripts/checks/refresh_open_pr_queue.py +++ b/scripts/checks/refresh_open_pr_queue.py @@ -118,6 +118,30 @@ def collect_paginated_pulls( _fail("live pull-request pagination bound would truncate the queue") +def collect_paginated_branch_refs( + fetch_page: PageFetcher, + *, + page_size: int = PAGE_SIZE, + max_pages: int = MAX_PAGES, +) -> list[dict[str, Any]]: + """Collect every announced branch-ref page or fail rather than truncate base authority.""" + _require_positive_int(page_size, "page_size") + _require_positive_int(max_pages, "max_pages") + branch_refs: list[dict[str, Any]] = [] + for page in range(1, max_pages + 1): + items, has_next = fetch_page(page, page_size) + if not isinstance(items, list) or any(not isinstance(item, dict) for item in items): + _fail(f"branch-ref page {page} must contain objects") + if not isinstance(has_next, bool): + _fail(f"branch-ref page {page} next-page marker must be boolean") + if has_next and not items: + _fail(f"branch-ref page {page} is empty but announces another page") + branch_refs.extend(items) + if not has_next: + return branch_refs + _fail("live branch-ref pagination bound would truncate the inventory") + + def _live_pr_entry( raw_pr: object, *, @@ -301,11 +325,28 @@ def fetch_live_branch_sha(branch_ref: str, token: str | None) -> str: return _require_sha(commit.get("sha"), "branch.commit.sha") +def fetch_live_branch_ref_page( + page: int, + page_size: int, + token: str | None, +) -> tuple[list[dict[str, Any]], bool]: + """Fetch one bounded page of repository branch refs for exact target-tip resolution.""" + _require_positive_int(page, "page") + _require_positive_int(page_size, "page_size") + query = urllib.parse.urlencode({"per_page": page_size, "page": page}) + target = f"{REPOSITORY_API_PREFIX}git/matching-refs/heads/?{query}" + payload, link = _request_github_json(target, token) + items = _require_list(payload, f"branch-ref page {page}") + if any(not isinstance(item, dict) for item in items): + _fail(f"branch-ref page {page} must contain objects") + return items, 'rel="next"' in link + + def fetch_live_branch_index(token: str | None) -> dict[str, str]: - """Resolve all branch tips from one bounded Git refs snapshot.""" - target = f"{REPOSITORY_API_PREFIX}git/matching-refs/heads/" - payload, _ = _request_github_json(target, token) - refs = _require_list(payload, "branch snapshot") + """Resolve all branch tips from a complete bounded Git refs inventory.""" + refs = collect_paginated_branch_refs( + lambda page, size: fetch_live_branch_ref_page(page, size, token) + ) branch_index: dict[str, str] = {} for index, raw_ref in enumerate(refs): ref_record = _require_record(raw_ref, f"branch snapshot[{index}]") @@ -360,7 +401,7 @@ def fetch_live_pull_page( def resolve_live_base_tips(live_result: object, token: str | None) -> dict[str, str]: - """Select every current PR target from one independently resolved branch snapshot.""" + """Select every current PR target from one independently resolved branch inventory.""" live = _require_record(live_result, "live result") if live.get("incomplete_results") is not False: _fail("live pull-request inventory is incomplete") @@ -426,4 +467,4 @@ def main() -> int: if __name__ == "__main__": - raise SystemExit(main()) \ No newline at end of file + raise SystemExit(main()) From 8200cc3811c800ce61ea7a4a732eb7aff620998d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 20:00:48 +0900 Subject: [PATCH 45/77] test(queue): align branch snapshot contract with pagination --- .../tests/test_open_pr_queue_stacked_inventory.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/services/analysis-engine/tests/test_open_pr_queue_stacked_inventory.py b/services/analysis-engine/tests/test_open_pr_queue_stacked_inventory.py index 8ac9dc138..e283cebd6 100644 --- a/services/analysis-engine/tests/test_open_pr_queue_stacked_inventory.py +++ b/services/analysis-engine/tests/test_open_pr_queue_stacked_inventory.py @@ -203,7 +203,7 @@ def fake_request(target: str, token: str | None) -> tuple[object, str]: def test_resolve_live_base_tips_uses_one_matching_refs_snapshot( monkeypatch: pytest.MonkeyPatch, ) -> None: - """All required target tips come from one bounded branch-ref snapshot.""" + """All required target tips come from one bounded branch-ref inventory.""" refresher = _load_module(REFRESHER_PATH, "refresh_open_pr_queue_branch_snapshot") observed: list[str] = [] live = { @@ -232,7 +232,7 @@ def fake_request(target: str, token: str | None) -> tuple[object, str]: tips = refresher.resolve_live_base_tips(live, None) assert observed == [ - "/repos/ContextualWisdomLab/bandscope/git/matching-refs/heads/" + "/repos/ContextualWisdomLab/bandscope/git/matching-refs/heads/?per_page=100&page=1" ] assert tips == { "develop": "d" * 40, @@ -243,7 +243,7 @@ def fake_request(target: str, token: str | None) -> tuple[object, str]: def test_resolve_live_base_tips_rejects_target_missing_from_branch_snapshot( monkeypatch: pytest.MonkeyPatch, ) -> None: - """A live PR target absent from the independent branch snapshot fails closed.""" + """A live PR target absent from the independent branch inventory fails closed.""" refresher = _load_module(REFRESHER_PATH, "refresh_open_pr_queue_missing_snapshot_ref") live = { "incomplete_results": False, From ec825fa3226075a2cdf5281e487ccb2992cb11be Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 20:02:18 +0900 Subject: [PATCH 46/77] test(queue): cover bounded branch-ref pagination failures --- .../test_open_pr_queue_branch_pagination.py | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/services/analysis-engine/tests/test_open_pr_queue_branch_pagination.py b/services/analysis-engine/tests/test_open_pr_queue_branch_pagination.py index 9d3656bb4..8b60f4c57 100644 --- a/services/analysis-engine/tests/test_open_pr_queue_branch_pagination.py +++ b/services/analysis-engine/tests/test_open_pr_queue_branch_pagination.py @@ -69,3 +69,50 @@ def fake_request(target: str, token: str | None) -> tuple[object, str]: "/repos/ContextualWisdomLab/bandscope/git/matching-refs/heads/?per_page=100&page=1", "/repos/ContextualWisdomLab/bandscope/git/matching-refs/heads/?per_page=100&page=2", ] + + +def test_branch_ref_collector_fails_closed_when_bound_would_truncate() -> None: + """An announced page beyond the bound cannot yield a partial branch authority set.""" + refresher = _load_refresher() + + with pytest.raises(refresher.RefreshError, match="branch-ref pagination bound"): + refresher.collect_paginated_branch_refs( + lambda page, size: ([_branch_ref(f"branch-{page}", f"{page:x}".rjust(40, "0"))], True), + page_size=1, + max_pages=2, + ) + + +@pytest.mark.parametrize( + ("page_result", "expected"), + [ + (("not-a-list", False), "must contain objects"), + (([object()], False), "must contain objects"), + (([_branch_ref("develop", "d" * 40)], "yes"), "next-page marker"), + (([], True), "empty but announces another page"), + ], +) +def test_branch_ref_collector_rejects_malformed_pagination( + page_result: tuple[object, object], expected: str +) -> None: + """Malformed page payloads and pagination metadata fail before publication.""" + refresher = _load_refresher() + + with pytest.raises(refresher.RefreshError, match=expected): + refresher.collect_paginated_branch_refs(lambda page, size: page_result) + + +def test_branch_ref_page_rejects_non_record_items( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """The network adapter rejects a JSON array containing non-record ref entries.""" + refresher = _load_refresher() + + monkeypatch.setattr( + refresher, + "_request_github_json", + lambda target, token: (["not-a-ref-record"], ""), + ) + + with pytest.raises(refresher.RefreshError, match="must contain objects"): + refresher.fetch_live_branch_ref_page(1, 100, None) From 7168424e8653f372a3659ea5855d0739641f7e1a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 21:05:59 +0900 Subject: [PATCH 47/77] test(queue): require generated human merge-train view --- .../tests/test_open_pr_queue_human_view.py | 141 ++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 services/analysis-engine/tests/test_open_pr_queue_human_view.py diff --git a/services/analysis-engine/tests/test_open_pr_queue_human_view.py b/services/analysis-engine/tests/test_open_pr_queue_human_view.py new file mode 100644 index 000000000..832c47954 --- /dev/null +++ b/services/analysis-engine/tests/test_open_pr_queue_human_view.py @@ -0,0 +1,141 @@ +"""Regression tests for the generated human view of the BandScope merge-train queue.""" + +from __future__ import annotations + +import importlib.util +import sys +from pathlib import Path +from types import ModuleType + +import pytest + +REPO_ROOT = Path(__file__).resolve().parents[3] +RENDERER_PATH = REPO_ROOT / "scripts" / "checks" / "render_open_pr_queue.py" + + +def _load_renderer() -> ModuleType: + """Load the queue renderer without requiring scripts to be a Python package.""" + sys.path.insert(0, str(RENDERER_PATH.parent)) + try: + spec = importlib.util.spec_from_file_location("render_open_pr_queue", RENDERER_PATH) + assert spec is not None and spec.loader is not None + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + finally: + sys.path.pop(0) + + +def _manifest() -> dict[str, object]: + """Return a minimal valid queue with two trains and one stack edge.""" + return { + "schema_version": "1.0.0", + "snapshot_date": "2026-09-01", + "timezone": "Asia/Seoul", + "repository": "ContextualWisdomLab/bandscope", + "base_branch": "develop", + "base_sha": "a" * 40, + "open_pr_count": 2, + "authority_note": ( + "Generated from a complete live GitHub open-PR inventory. Refresh checks, reviews, " + "threads, ancestry, and writer evidence immediately before action." + ), + "trains": { + "T0": {"description": "Dependency | base ", "issue": 966}, + "T3": {"description": "Active player", "issue": 961}, + }, + "pull_requests": [ + { + "number": 968, + "title": "queue | renderer