Skip to content

feat(workspace): name tonight's first drop plan on the map - #1040

Open
seonghobae wants to merge 12 commits into
developfrom
feat/workspace-first-drop-plan
Open

feat(workspace): name tonight's first drop plan on the map#1040
seonghobae wants to merge 12 commits into
developfrom
feat/workspace-first-drop-plan

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

Buyer-visible next action

This PR names tonight's first drop plan on the rehearsal map when an entering part lands a full-band arrival after a thin texture: previous graph 1–2 distinct sources, current graph ≥3 sources, previous sources stay, and a new named entrance arrives. Open scrolls that rendered map section so the landing part can come in together when the texture fills.

A drop is a corroborated density fill that arrives. Breakdown is a staying sparse hold. Dropout is a leaving part. Cutoff is a stop-time. Pickup is rest-then-enter. Heuristic-only topology stays unnamed.

Summary

  • Engine emits Hit this drop with {target}; come in together when the texture fills. (or the solo fill) only from real stem activity that proves the thin-to-full arrival. Shared other stems may corroborate density but never own the landing.
  • Fail-closed own-data / Proxy-safe resolver admits bounded dropPlan copy only from owned data properties and snapshots it once before ranking. Inherited, accessor-backed, or Proxy-substituted runtime metadata remains guidance-only.
  • i18n EN/KO next-action copy is particle-safe. Korean Open is {at} {role} 드롭 열기.
  • Navigation uses the current song-structure renderer (data-section-index) and fails closed on ambiguous or missing targets. Reduced motion uses behavior: "auto".
  • Demo and heuristic topology stay unnamed until real stem activity corroborates the fill.

Exact current identity

Verification

  • Desktop vitest for drop-plan resolver, callout, i18n, and workspace armed-state
  • shared-types dropPlan provenance tests
  • Python tests/test_drop_plan.py plus extractor/roles/activity
  • Rust drop_plan_contract round-trip
  • ESLint + tsc --noEmit for desktop and shared-types
  • ./scripts/harness/quickcheck.sh (CI)

Security Notes

Attack surface

Untrusted rehearsal-song JSON, Proxy/inherited role metadata, and rendered-map DOM selectors used for Open navigation.

Trust boundary

UI resolver reads only own data properties (Object.getOwnPropertyDescriptor) and fails closed. Engine drop copy is emitted only from corroborated stem activity. Native payload admits optional dropPlan / dropPlanSource with deny_unknown_fields.

Mitigations

Bounded copy (180 code points), reject multiline/blank plans, reject provenance without copy, snapshot guidance before ranking, fail closed on ambiguous song-structure-grid mounts, preserve user copy verbatim unless model provenance is explicit.

Test points

Hostile Proxy get, inherited metadata, malformed song root, Korean particle-safe copy, navigation failure, reduced motion, demo unnamed, 2→3 solo fill, two named partners, other-stem unnamed, density-drop-is-not-drop, live stem extract.

Dependency and Supply Chain

  • No new direct dependency was added
  • If a new dependency was added, this PR explains why it is needed
  • runtime / dev / build / test classification is recorded
  • alternatives were considered
  • maintainer trust and update health were checked
  • license fit was checked
  • known security issues were checked
  • transitive footprint impact was considered
  • SBOM or supplemental inventory impact was recorded (unchanged)

Canonical #783 is protected develop shipped truth. This branch inherits that JavaScript baseline and does not duplicate or suppress it. Inherited npm HIGH must not be suppressed elsewhere.

i18n impact

  • No user-visible string changed
  • Korean and English locale impact was updated

Reviewer checklist

  • Gitflow target branch is develop
  • protected-branch rules were not weakened
  • required checks are expected to stay green

Keep unmerged until the unchanged then-current head has every applicable repository and central CI/build/release/security/SAST/SBOM/supply-chain/coverage/review gate terminal-success, zero valid unresolved findings, and a qualifying independent non-author last-push approval under live branch protection.

Queued, pending, skipped, cancelled, failed, predecessor-head, protected-base, model-only, self/author, or administrative-bypass evidence is not success.


Devin Review

Summary by CodeRabbit

  • 새로운 기능

    • 리허설 워크스페이스에 “오늘 밤의 첫 드롭 계획” 안내를 추가했습니다.
    • 안내에서 관련 파트와 진입 시점을 확인하고, 버튼으로 곡 맵의 해당 구간으로 이동할 수 있습니다.
    • 실제 스템 활동이 충분히 확인된 경우에만 안내를 표시합니다.
    • 사용자 작성 안내를 그대로 보존하며, 영어와 한국어를 지원합니다.
    • 동작 감지 설정에 따라 이동 효과를 조정하고, 이동할 수 없는 경우 안내 메시지를 표시합니다.
  • 개선 사항

    • 드롭 계획 정보의 저장 및 불러오기를 지원합니다.
    • 관련 섹션 형식과 안내 문구의 현지화를 강화했습니다.

Name the earliest corroborated full-band arrival after a thin texture
so the entering part can land together when the map fills. Heuristic
demo topology stays unnamed until real stem activity proves the drop.
@coderabbitai

coderabbitai Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

Next included review available in 10 minutes.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fd442205-ac0b-4705-8eac-39d0307e7343

📥 Commits

Reviewing files that changed from the base of the PR and between fa27cdf and 0892572.

📒 Files selected for processing (10)
  • apps/desktop/core/tests/drop_plan_contract.rs
  • apps/desktop/src/features/workspace/FirstDropPlanCallout.custom-guidance.test.tsx
  • apps/desktop/src/features/workspace/FirstDropPlanCallout.particle.test.tsx
  • apps/desktop/src/features/workspace/FirstDropPlanCallout.provenance.test.tsx
  • apps/desktop/src/features/workspace/firstDropPlan.model-guidance.test.ts
  • apps/desktop/src/features/workspace/firstDropPlan.ts
  • apps/desktop/src/i18n/index.test.ts
  • apps/desktop/src/locales/ko/common.json
  • services/analysis-engine/src/bandscope_analysis/roles/extractor.py
  • services/analysis-engine/tests/test_drop_plan.py
📝 Walkthrough

Walkthrough

분석 엔진이 검증된 스템 활동에서 첫 드롭 계획을 생성합니다. 데스크톱 워크스페이스는 계획을 현지화해 표시하고, 해당 곡 맵 섹션으로 이동합니다.

Changes

첫 드롭 계획

Layer / File(s) Summary
드롭 계획 데이터 계약
packages/shared-types/src/index.ts, apps/desktop/core/src/lib.rs, apps/desktop/src/i18n/*, packages/shared-types/test/*, apps/desktop/core/tests/*
역할과 프로젝트 payload가 dropPlan 및 provenance 필드를 지원합니다. 섹션 라벨 번역과 검증 테스트가 추가되었습니다.
분석 엔진의 드롭 계획 생성
services/analysis-engine/src/bandscope_analysis/roles/*, services/analysis-engine/tests/test_drop_plan.py
이전 섹션의 source 활동을 사용해 확인된 density fill을 찾습니다. 조건을 충족한 새 역할에만 model 출처의 계획을 부여합니다.
첫 드롭 해석 및 선택
apps/desktop/src/features/workspace/firstDropPlan.ts, apps/desktop/src/features/workspace/firstDropPlan*.test.ts
시간, 역할, 그래프, source, 소유 데이터 속성을 검증합니다. 유효한 후보 중 가장 이른 계획을 안정적으로 선택합니다.
워크스페이스 표시와 섹션 탐색
apps/desktop/src/features/workspace/FirstDropPlanCallout*, apps/desktop/src/features/workspace/Workspace*, apps/desktop/src/locales/*
callout이 provenance별 문구를 표시합니다. 일반 및 reduced-motion 스크롤, 탐색 실패, 상태 초기화와 다중 마운트 식별자를 처리합니다.
기능 규칙 문서화
AGENTS.md, ARCHITECTURE.md, CHANGELOG.md
첫 드롭 계획의 생성 조건, 표시 동작 및 다른 first-* 계획과의 구분을 문서화합니다.

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

Merge Risk: 🔵 Low · up to fa27c

The PR adds first-drop guidance and map navigation across analysis and desktop UI. Current evidence identifies localized correctness and copy follow-ups—drums may be omitted from density corroboration, invalid model guidance may render incorrectly, and Korean endings are inconsistent—without a security, availability, or release-blocking impact; merge is reasonable with owner awareness and follow-up.

Sequence Diagram(s)

sequenceDiagram
  participant StemActivity
  participant AnalysisEngine
  participant Workspace
  participant SongMap
  StemActivity->>AnalysisEngine: 이전 및 현재 섹션 활동 제공
  AnalysisEngine->>AnalysisEngine: 확인된 density fill에서 첫 드롭 계획 선택
  AnalysisEngine-->>Workspace: dropPlan과 dropPlanSource 전달
  Workspace->>Workspace: 계획 문구 현지화
  Workspace->>SongMap: 일치하는 섹션으로 스크롤
  SongMap-->>Workspace: 열린 계획 상태 표시
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 워크스페이스 지도에 오늘 밤의 첫 드롭 계획을 명명하는 이 pull request의 주요 변경 사항을 정확하고 간결하게 설명합니다.
Docstring Coverage ✅ Passed Docstring coverage is 84.85% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 66 functions across 25 files. (6 skipped: 6…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 84.85% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 66 functions across 25 files. (6 skipped: 6 unsupported.)

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/workspace-first-drop-plan

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

github-code-quality[bot]

This comment was marked as resolved.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

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

Devin Review found 4 potential issues.

Devin Review

Comment on lines +194 to +197
#[serde(default, skip_serializing_if = "Option::is_none")]
drop_plan: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
drop_plan_source: Option<String>,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Rust payload accepts dropPlanSource without dropPlan; TS rejects it

The native RehearsalRolePayload adds drop_plan and drop_plan_source as independent optional fields with no cross-field rule, so a payload carrying dropPlanSource alone parses in Rust. The TS validateRehearsalRole rejects that same shape. Both guard the same persisted contract, so they disagree. Impact is small: the engine always emits both, and the TS side fails closed.

Devin Review

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

Comment thread apps/desktop/src/features/workspace/FirstDropPlanCallout.tsx
Comment on lines +385 to +386
if previous_active - current_active:
return None

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: "Previous sources stay" is enforced at role level, not source level

resolveSafeFirstDropPlan and the engine's _activity_drop_plan both reject a drop when any previous active role id is missing from the current section, comparing role ids not collapsed sources. A within-accompaniment swap blocks the drop even though the shared other source stays. Consistent across both layers and fail-closed.

Devin Review

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

Comment on lines +134 to +141
if (song !== previousSongRef.current) {
const isLocalWorkspaceUpdate = song === localSongUpdateRef.current;
if (!isLocalWorkspaceUpdate) {
workspaceInstanceRef.current = song;
}
localSongUpdateRef.current = null;
previousSongRef.current = song;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Opened-drop stability depends on parent re-passing the exact updated object

Workspace mutates refs during render to keep the opened drop stable across immutable practice-progress edits and reset it on real song swaps. The guard relies on the parent re-rendering with the same object handed to onSongUpdate, tracked via localSongUpdateRef. A parent that forwards a new but equal object for an internal edit would reset the opened drop.

Devin Review

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

coderabbitai[bot]

This comment was marked as resolved.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

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

Devin Review found 1 new potential issue.

Devin Review

Comment on lines +353 to +386
if (
typeof sectionId !== "string" ||
sectionId.trim().length === 0 ||
typeof sectionLabel !== "string" ||
!SECTION_FORM_LABEL_SET.has(sectionLabel) ||
timeRange === null ||
previousTimeRange === null ||
previousTimeRange.end !== timeRange.start
) {
return [];
}

const previousActiveIds = rankedGraphRoleIds(previousSection as RehearsalSection, true);
const currentActiveIds = rankedGraphRoleIds(section as RehearsalSection, true);
const previousSourceCount = activeSourceCount(previousActiveIds);
const currentSourceCount = activeSourceCount(currentActiveIds);
if (previousSourceCount < 1 || previousSourceCount > 2 || currentSourceCount < 3) {
return [];
}
for (const roleId of previousActiveIds) {
if (!currentActiveIds.has(roleId)) {
return [];
}
}
let entered = false;
for (const roleId of currentActiveIds) {
if (!previousActiveIds.has(roleId)) {
entered = true;
break;
}
}
if (!entered) {
return [];
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Engine and UI double-gate drop-plan emission

The Python engine emits the drop copy per active role, while resolveFirstDropPlan re-derives the same density gate and adds checks the engine lacks: sections must abut in time, the label must be a known form label, and ranges must be bounded integers. An engine-emitted drop in a non-abutting section shows nothing in the UI. Real boundaries abut, so this holds today, but the two gates can diverge on hand-edited or future data.

Devin Review

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

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

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

Devin Review found 2 new potential issues.

Devin Review

Comment thread apps/desktop/src/features/workspace/FirstDropPlanCallout.particle.test.tsx Outdated
Comment on lines +134 to +141
if (song !== previousSongRef.current) {
const isLocalWorkspaceUpdate = song === localSongUpdateRef.current;
if (!isLocalWorkspaceUpdate) {
workspaceInstanceRef.current = song;
}
localSongUpdateRef.current = null;
previousSongRef.current = song;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Workspace ref plumbing preserves armed state across self-edits

The added refs distinguish self-emitted immutable song updates from externally swapped songs, holding workspaceInstanceRef stable so the callout keeps its armed state on a practice-progress edit while still resetting on a real song swap. Consistent with the callout's songIdentity comparison.

Devin Review

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

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

Devin Review

Comment on lines +196 to +197
#[serde(default, skip_serializing_if = "Option::is_none")]
drop_plan_source: Option<String>,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Native drop_plan_source accepts arbitrary strings

drop_plan_source is an unconstrained Option<String>, while the shared-types validateRehearsalRole restricts dropPlanSource to model/user. The native loader accepts values the TypeScript contract rejects. App-written payloads only emit model, so impact is limited to hand-edited or corrupted project files.

Devin Review

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant