Skip to content

Commit 0bae9fa

Browse files
committed
Make MIDI Studio V2 Done Done by removing duplicate editable data, resolving red controls, and validating PROD workflows - PR_26146_281-340-midi-studio-v2-prod-done-done-completion
1 parent b5c597f commit 0bae9fa

10 files changed

Lines changed: 314 additions & 31 deletions
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# PR_26146_281_340 Duplicate Data Removed
2+
3+
Status: PASS
4+
5+
Removed Duplicate Display Data:
6+
- Removed duplicate `Selected song` read-only rows from Export source details.
7+
- Removed duplicate `Selected song` read-only rows from Diagnostics Export Readiness details.
8+
- Removed duplicate `Selected song` read-only rows from Export Status details.
9+
- `Song name` is now the single derived selected-song display in those Export/Diagnostics surfaces.
10+
11+
Duplicate Editable Ownership:
12+
- PASS no new editable owner was introduced for canonical song data.
13+
- PASS rendered output format selection is workflow state in Export, not a duplicate canonical field.
14+
- PASS Save WAV/MP3/OGG is an Export-owned action, not a duplicate rendered target path editor.
15+
- PASS rendered target paths remain editable only in the Export tab Output Targets section.
16+
- PASS SoundFont/render options remain red/unwired future controls and do not claim editable PROD ownership.
17+
18+
Duplicate Red/Unwired Ownership Removed:
19+
- Save WAV/MP3/OGG was previously classified as a future rendered audio renderer control.
20+
- This PR reclassified it as a wired Export action for declared rendered asset downloads.
21+
- Output Type was previously classified as part of the future rendered audio renderer.
22+
- This PR reclassified it as wired Export workflow state.
23+
24+
Validation:
25+
- Targeted Playwright verifies `#exportRenderSource [data-export-field='selected-song']` is absent.
26+
- Targeted Playwright verifies `#exportRenderSource [data-export-field='song-name']` remains present.
27+
- Targeted visible-control audit verifies no duplicate editable ownership in MIDI Studio V2.
28+
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# PR_26146_281_340 PROD Done/Done Validation
2+
3+
Status: PASS for MIDI Studio V2 PROD UAT scope with workspace-v2 WARN
4+
5+
Scope:
6+
- Baseline requested: PR_26146_241-280.
7+
- Available local baseline: current committed MIDI Studio V2 state plus PR_26146_221-240 reports. No PR_26146_241-280 report files were present in `docs/dev/reports` at implementation time.
8+
- This PR is implementation-heavy: runtime implementation files and targeted Playwright coverage changed.
9+
10+
Implementation Changes:
11+
- Wired Export tab Save WAV/MP3/OGG controls to download declared rendered asset targets when files exist.
12+
- Removed red/unwired state from rendered export output type and save controls.
13+
- Preserved SoundFont/render-engine controls as red/unwired true future controls.
14+
- Removed duplicate derived `Selected song` rows from Export source, Diagnostics export readiness, and Export status lists; `Song name` is the single selected-song display.
15+
- Added PR281-340 targeted Playwright validation for rendered export save wiring, missing-target failure honesty, future SoundFont red state, and duplicate export display cleanup.
16+
17+
Validation Commands:
18+
- PASS changed-file syntax checks:
19+
- `node --check tools/midi-studio-v2/js/MidiStudioV2App.js`
20+
- `node --check tools/midi-studio-v2/js/controls/RenderedExportActionsControl.js`
21+
- `node --check tools/midi-studio-v2/js/controls/ExportPanelControl.js`
22+
- `node --check tests/playwright/tools/MidiStudioV2.spec.mjs`
23+
- PASS targeted MIDI Studio Playwright validation:
24+
- `npx playwright test tests/playwright/tools/MidiStudioV2.spec.mjs --grep "PR161-200 release candidate|PR281-340" --reporter=line`
25+
- Result: 3 passed in 58.3s on final run.
26+
- WARN `npm run test:workspace-v2`
27+
- Result: 49 passed, 23 failed.
28+
- Failures are unchanged Workspace Manager V2 failures outside MIDI Studio V2 scope.
29+
- PASS `git diff --check`
30+
31+
PROD-Ready Gate:
32+
- PASS implementation files changed.
33+
- PASS required rendered export controls are no longer red/unwired.
34+
- PASS only true future controls remain red/unwired.
35+
- PASS no duplicate selected-song Export display remains.
36+
- PASS JSON round-trip remains covered by release-candidate targeted UAT.
37+
- PASS playback loop/completion remains covered by release-candidate targeted UAT.
38+
- PASS timeline edit/regenerate remains covered by release-candidate targeted UAT.
39+
- PASS instrument edit/audition remains covered by release-candidate targeted UAT.
40+
- PASS workspace/tool launch split remains covered by release-candidate targeted UAT.
41+
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# PR_26146_281_340 PROD Workflow PASS Report
2+
3+
Status: PASS
4+
5+
Required Workflow:
6+
- PASS Import JSON.
7+
- PASS Select song.
8+
- PASS Create song.
9+
- PASS Edit Name, Classification, Tempo, Key, Style, Notes.
10+
- PASS generated ID updates as `camelCase(Name) + "-" + Classification`.
11+
- PASS populate Intro, Verse, Chorus, Bridge, Outro, and Custom sections.
12+
- PASS Available Sections shows populated sections only.
13+
- PASS build Song Sequence.
14+
- PASS Parse Guided Song Sheet.
15+
- PASS Regenerate Arrangement.
16+
- PASS Octave Timeline updates visibly.
17+
- PASS edit notes in canvas.
18+
- PASS select instruments from Octave Timeline and Instruments tab with synced selectedInstrumentId.
19+
- PASS edit instrument settings from Instruments tab only.
20+
- PASS audition instrument keyboard.
21+
- PASS Play.
22+
- PASS Loop.
23+
- PASS Stop.
24+
- PASS natural completion resets playback state.
25+
- PASS Export JSON.
26+
- PASS reimport JSON and confirm canonical data round-trips.
27+
- PASS Review Export readiness.
28+
- PASS Review Manifest readiness.
29+
- PASS Diagnostics match canonical model.
30+
- PASS Save WAV/MP3/OGG declared-target action is wired and honest.
31+
32+
New PROD Export Coverage:
33+
- PASS Save OGG fetches declared rendered target and reports PASS.
34+
- PASS missing WAV target reports FAIL.
35+
- PASS Save controls do not remain red/unwired.
36+
- PASS SoundFont remains red/unwired as true future renderer configuration.
37+
- PASS duplicate selected-song export display was removed.
38+
39+
No False Success:
40+
- PASS missing rendered target path does not claim a saved/created/written file.
41+
- PASS unavailable target fetch reports FAIL rather than success.
42+
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# PR_26146_281_340 Red Controls Resolution
2+
3+
Status: PASS
4+
5+
Resolved Red Controls:
6+
- `#renderedExportTargetTypeSelect`
7+
- Before: red/unwired future rendered audio renderer control.
8+
- After: wired Export workflow state for selecting WAV, MP3, or OGG target format.
9+
- `#renderedExportSaveButton`
10+
- Before: red/unwired future rendered audio renderer control.
11+
- After: wired Export action that downloads the selected declared rendered target when the file exists.
12+
13+
Honest Failure Handling:
14+
- Missing selected song reports FAIL.
15+
- Missing rendered target path reports FAIL.
16+
- Unavailable target file reports FAIL with fetch status/detail.
17+
- Successful declared target fetch reports PASS and triggers the browser download path.
18+
- Save actions no longer report "not implemented" for the wired declared-target workflow.
19+
- Save actions do not claim file creation when the target is missing or unavailable.
20+
21+
Controls Kept Red/Unwired As True Future:
22+
- SoundFont selection.
23+
- Render quality.
24+
- Sample rate.
25+
- Normalize volume.
26+
- Export stems.
27+
- Loop export.
28+
- MIDI device input, recording, and advanced MIDI-to-canonical conversion.
29+
- Sequence drag/drop.
30+
- Editing history/snapshots/revision/autosave.
31+
- Runtime Game Usage trigger sync.
32+
- Advanced instrument effects.
33+
- Workspace manifest proxy actions.
34+
35+
Validation:
36+
- Targeted Playwright verifies Save OGG is not red/unwired.
37+
- Targeted Playwright verifies SoundFont remains red/unwired.
38+
- Targeted Playwright verifies Save OGG success via declared rendered target fetch.
39+
- Targeted Playwright verifies missing WAV target failure without false success.
40+
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# PR_26146_281_340 Remaining Future Controls
2+
3+
Status: PASS
4+
5+
Future Controls Still Red/Unwired:
6+
- SoundFont selection.
7+
- Render quality.
8+
- Sample rate.
9+
- Normalize volume.
10+
- Export stems.
11+
- Loop export.
12+
- MIDI device input.
13+
- MIDI recording.
14+
- Advanced MIDI-to-canonical conversion for controller data, velocity, tempo-map edits, and advanced event fidelity.
15+
- Song Sequence drag/drop.
16+
- Editing history: Undo, Redo, Snapshots, Revision History, Revert To Saved, Autosave.
17+
- Runtime Game Usage trigger sync.
18+
- Advanced instrument effects and incomplete advanced instrument controls.
19+
- Workspace manifest proxy actions.
20+
21+
Resolved From Future:
22+
- Save WAV.
23+
- Save MP3.
24+
- Save OGG.
25+
- Output Type selection for rendered target format.
26+
27+
Future Classification:
28+
- Remaining red/unwired controls are not required for the required PROD user workflow.
29+
- Each remaining future control has tooltip/status through the shared unwired control state.
30+
- Required PROD workflows do not depend on those future controls.
31+
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# PR_26146_281_340 SSoT Ownership Matrix
2+
3+
Canonical Value | Editable Owner | Derived / Non-owning Displays
4+
--- | --- | ---
5+
`music.songs[].name` | Song Details | Song list, NAV selected song, Export song name, Diagnostics JSON
6+
`music.songs[].classification` | Song Details | Classification guide, Export readiness, Manifest readiness
7+
`music.songs[].id` | Generated by Song Details | Read-only ID field, generated ID preview, Export readiness, JSON Details
8+
`music.songs[].studioArrangement.tempo` | Song Sheet | Sequence duration, timeline diagnostics
9+
`music.songs[].studioArrangement.key` | Song Sheet | Song summaries, JSON Details
10+
`music.songs[].studioArrangement.style` | Song Sheet | Song summaries, classification guidance, JSON Details
11+
`music.songs[].director.notes` | Song Details | Diagnostics JSON
12+
`music.songs[].director.usage` | Song Setup Game Usage | Export readiness, Manifest readiness
13+
`music.songs[].studioArrangement.songSheet.sections` | Song Sheet | Available Sections, section metrics, timeline labels
14+
`music.songs[].studioArrangement.songSheet.sequence` | Song Sequence builder | Timeline build order, Export readiness, Manifest readiness
15+
`music.songs[].studioArrangement.songSheet.applyTargets` | Song Sheet Apply Song Sheet To | Generation target summary
16+
`music.songs[].studioArrangement.lanes` | Song Sheet generation and Octave Timeline canvas editing | Diagnostics, JSON Details, note counts, Export readiness
17+
`music.songs[].studioArrangement.previewLaneSettings` | Instruments tab | Timeline quick summaries, audition range
18+
`music.songs[].rendered.wav/mp3/ogg` | Export tab Output Targets | Export readiness, Diagnostics export readiness
19+
`rendered export target format` | Export tab Output Type | Save button label
20+
`declared rendered target download` | Export tab Save WAV/MP3/OGG action | Status log and Export Status
21+
`tools.midi-studio-v2.activeSongId` | Song list and song actions | NAV selected label, Export readiness
22+
`tools.midi-studio-v2.selectedInstrumentId` | Timeline quick select and Instruments row select | Audition keyboard, selected editor display
23+
24+
Findings:
25+
- PASS one editable owner per canonical value remains.
26+
- PASS rendered target paths are edited only in Export.
27+
- PASS Save WAV/MP3/OGG is a wired action and not a duplicate target path editor.
28+
- PASS duplicate selected-song export display rows were removed.
29+
- PASS Diagnostics remains read-only except Copy JSON and Clear Status.
30+

tests/playwright/tools/MidiStudioV2.spec.mjs

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -880,8 +880,8 @@ async function visibleMidiStudioControlOwnership(page, activeTabId) {
880880
playSectionButton: { canonical: "timing preview playback state", kind: "workflow-state", owner: "Octave Timeline", wired: "wired" },
881881
playSequenceButton: { canonical: "timing preview playback state from Song Sequence order", kind: "workflow-state", owner: "Octave Timeline", wired: "wired" },
882882
regenerateArrangementButton: { canonical: "music.songs[].studioArrangement generated lanes from Song Sheet sequence", kind: "canonical-action", owner: "Song Setup", wired: "wired" },
883-
renderedExportSaveButton: { canonical: "future rendered audio renderer", kind: "unwired", owner: "Export", wired: "unwired" },
884-
renderedExportTargetTypeSelect: { canonical: "future rendered audio renderer", kind: "unwired", owner: "Export", wired: "unwired" },
883+
renderedExportSaveButton: { canonical: "declared rendered audio target download", kind: "action", owner: "Export", wired: "wired" },
884+
renderedExportTargetTypeSelect: { canonical: "rendered audio target format selection", kind: "workflow-state", owner: "Export", wired: "wired" },
885885
resetSongEditsButton: { canonical: "music.songs[] reset baseline", kind: "canonical-action", owner: "Global NAV", wired: "wired" },
886886
returnToWorkspaceButton: { canonical: "workspace navigation", kind: "action", owner: "Workspace NAV", wired: "wired" },
887887
saveInstrumentPresetButton: { canonical: "Instruments tab instrument preset library", kind: "workflow-state", owner: "Instruments", wired: "wired" },
@@ -7373,17 +7373,17 @@ test.describe("MIDI Studio V2", () => {
73737373
await expect(page.locator("#statusLog")).toHaveValue(/MIDI Studio V2 toolState/);
73747374

73757375
await selectMidiStudioTab(page, "export");
7376-
await expect(page.locator("#exportRenderSource [data-export-field='selected-song'] dd")).toHaveText("Release Candidate Loop");
7376+
await expect(page.locator("#exportRenderSource [data-export-field='song-name'] dd")).toHaveText("Release Candidate Loop");
73777377
await expect(page.locator("#exportRenderSource [data-export-field='generated-id'] dd")).toHaveText("releaseCandidateLoop-Boss");
73787378
await expect(page.locator("#exportManifestReadinessDetails [data-export-field='game-usage-assignment'] dd")).toContainText("Boss: Release Candidate Loop");
73797379
for (const [format, label] of [["wav", "Save WAV"], ["mp3", "Save MP3"], ["ogg", "Save OGG"]]) {
73807380
await page.locator("#renderedExportTargetTypeSelect").selectOption(format);
73817381
await expect(page.locator("#renderedExportSaveButton")).toHaveText(label);
73827382
}
73837383
await expect(page.locator("#futureSoundFontSelect")).toHaveAttribute("data-midi-studio-unwired", "not-implemented");
7384-
await expect(page.locator("#renderedExportSaveButton")).toHaveAttribute("data-midi-studio-unwired", "not-implemented");
7384+
await expect(page.locator("#renderedExportSaveButton")).not.toHaveAttribute("data-midi-studio-unwired", /.+/);
73857385
await page.locator("#renderedExportSaveButton").click();
7386-
await expect(page.locator("#statusLog")).toHaveValue(/WARN Export rendering not implemented|FAIL Missing rendered/);
7386+
await expect(page.locator("#statusLog")).toHaveValue(/FAIL Missing rendered OGG export target/);
73877387
await expect(page.locator("#statusLog")).not.toHaveValue(/created .*\.ogg|wrote .*\.ogg|saved .*\.ogg/i);
73887388
await page.locator("#toolExportToolStateButton").click();
73897389
await expect(page.locator("#statusLog")).toHaveValue(/PASS Export JSON PASS/);
@@ -7513,6 +7513,49 @@ test.describe("MIDI Studio V2", () => {
75137513
}
75147514
});
75157515

7516+
test("validates PR281-340 PROD rendered export save wiring and duplicate export display cleanup", async ({ page }) => {
7517+
await page.route("**/assets/music/rendered/theme-main.ogg", async (route) => {
7518+
await route.fulfill({
7519+
body: Buffer.from("OggS MIDI Studio V2 PROD rendered target fixture"),
7520+
contentType: "audio/ogg"
7521+
});
7522+
});
7523+
const server = await openMidiStudio(page);
7524+
try {
7525+
await selectMidiStudioTab(page, "export");
7526+
await page.evaluate(() => {
7527+
window.__midiStudioProdDownloads = [];
7528+
window.__midiStudioV2App.triggerRenderedDownload = async (blob, filename) => {
7529+
window.__midiStudioProdDownloads.push({ filename, size: blob.size });
7530+
};
7531+
});
7532+
await expect(page.locator("#exportRenderSource [data-export-field='song-name'] dd")).toHaveText("Main Theme");
7533+
await expect(page.locator("#exportRenderSource [data-export-field='selected-song']")).toHaveCount(0);
7534+
await expect(page.locator("#renderedExportTargetTypeSelect")).not.toHaveAttribute("data-midi-studio-unwired", /.+/);
7535+
await expect(page.locator("#renderedExportSaveButton")).not.toHaveAttribute("data-midi-studio-unwired", /.+/);
7536+
await expect(page.locator("#futureSoundFontSelect")).toHaveAttribute("data-midi-studio-unwired", "not-implemented");
7537+
7538+
await page.locator("#renderedExportTargetTypeSelect").selectOption("ogg");
7539+
await expect(page.locator("#renderedExportSaveButton")).toHaveText("Save OGG");
7540+
await page.locator("#renderedExportSaveButton").click();
7541+
await expect.poll(() => page.evaluate(() => window.__midiStudioProdDownloads)).toEqual([
7542+
{ filename: "theme-main.ogg", size: 48 }
7543+
]);
7544+
await expect(page.locator("#statusLog")).toHaveValue(/PASS Rendered OGG export downloaded from assets\/music\/rendered\/theme-main\.ogg\./);
7545+
await expect(page.locator("#exportStatusDetails")).toContainText("PASS: Rendered OGG export downloaded from assets/music/rendered/theme-main.ogg.");
7546+
7547+
await page.evaluate(() => window.__midiStudioV2App.selectSong("source-only"));
7548+
await expect(page.locator("#exportRenderSource [data-export-field='song-name'] dd")).toHaveText("Source Only");
7549+
await page.locator("#renderedExportTargetTypeSelect").selectOption("wav");
7550+
await page.locator("#renderedExportSaveButton").click();
7551+
await expect(page.locator("#statusLog")).toHaveValue(/FAIL Missing rendered WAV export target for Source Only\. Add music\.songs\[\]\.rendered\.wav before exporting\./);
7552+
await expect(page.locator("#statusLog")).not.toHaveValue(/not implemented for WAV|saved .*\.wav|created .*\.wav/i);
7553+
} finally {
7554+
await workspaceV2CoverageReporter.stop(page);
7555+
await server.close();
7556+
}
7557+
});
7558+
75167559
test("derives primary song, instrument, grid, playback, and diagnostics views from the canonical selected song", async ({ page }) => {
75177560
const server = await openMidiStudioForImport(page);
75187561
try {

0 commit comments

Comments
 (0)