From 822b320d1a6345f5a7d3ffcc7f52997c71043007 Mon Sep 17 00:00:00 2001 From: Mawen Salignat-Moandal Date: Fri, 9 Oct 2026 21:24:17 +0200 Subject: [PATCH] Report whether each inspect source has decoded audio loaded. Agents using session.inspect / inspect_session can tell missing media from metadata alone, complementing open_session missing lists and audio_health. Also credit Mawen in people.toml. --- contributors/people.toml | 4 ++++ crates/engine/src/inspect.rs | 27 ++++++++++++++++++++++++++- docs/control-protocol.md | 2 +- docs/mcp.md | 2 +- 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/contributors/people.toml b/contributors/people.toml index 89c6db2..fad3a62 100644 --- a/contributors/people.toml +++ b/contributors/people.toml @@ -21,3 +21,7 @@ real_name = "Brandon Thomas" display_name = "Brandon Thomas" sync_github_name = false + +[people.salignatmoandal] +real_name = "Mawen Salignat-Moandal" +sync_github_name = true diff --git a/crates/engine/src/inspect.rs b/crates/engine/src/inspect.rs index 2ca264b..b5de447 100644 --- a/crates/engine/src/inspect.rs +++ b/crates/engine/src/inspect.rs @@ -80,7 +80,10 @@ pub fn session(e: &Engine, full: bool) -> Value { "busses": s.busses.iter().map(|b| json!({"id": b.id, "name": b.name, "format": b.format.label()})).collect::>(), "markers": s.markers.iter().map(|m| json!({"id": m.id, "number": m.number, "name": m.name, "kind": m.kind, "start": m.start, "end": m.end})).collect::>(), "groups": s.groups.iter().map(|g| json!({"id": g.id, "name": g.name, "letter": g.letter, "members": g.members, "active": g.active})).collect::>(), - "sources": s.sources.iter().map(|x| json!({"id": x.id, "name": x.name, "channels": x.channels, "frames": x.frames, "path": x.path})).collect::>(), + "sources": s.sources.iter().map(|x| json!({ + "id": x.id, "name": x.name, "channels": x.channels, "frames": x.frames, "path": x.path, + "loaded": s.pool.contains(x.id), + })).collect::>(), "selection": {"start": s.edit.selection.start, "end": s.edit.selection.end, "tracks": s.edit.selected_tracks, "clips": s.edit.selected_clips}, "edit_mode": s.edit.edit_mode, "tool": s.edit.tool, "grid": s.edit.grid.label(), "nudge": s.edit.nudge.label(), "transport": e.transport, @@ -146,3 +149,25 @@ pub fn session_text(e: &Engine) -> String { } out } + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + #[test] + fn sources_report_whether_audio_is_loaded() { + let mut e = crate::demo::demo_engine(); + let before = session(&e, false); + let sources = before["sources"].as_array().expect("sources"); + assert!(!sources.is_empty()); + assert!(sources.iter().all(|s| s["loaded"] == true), "{sources:?}"); + + let id = e.session().sources[0].id; + assert!(e.session_mut().pool.remove(id).is_some()); + let after = session(&e, false); + let entry = after["sources"].as_array().unwrap().iter().find(|s| s["id"] == json!(id)).expect("source still listed"); + assert_eq!(entry["loaded"], false, "{entry}"); + assert_eq!(entry["path"], before["sources"][0]["path"], "metadata stays even when unloaded"); + } +} diff --git a/docs/control-protocol.md b/docs/control-protocol.md index 869278d..ba7b079 100644 --- a/docs/control-protocol.md +++ b/docs/control-protocol.md @@ -30,7 +30,7 @@ Errors: `{"id": 1, "ok": false, "error": "bad parameters for `mix.volume`: no tr | `engine.execute` (alias `command`) | `{command, params}` | Run a command programmatically. **Never opens a dialog**; empty params use defaults or the current selection. | | `engine.commands` | `{filter?}` | Every command: id, label, menu path, shortcut, params doc, enabled + reason. Includes UI-layer commands (`window.*`). | | `engine.parity` | `{}` | Parity report against the incumbent's menu catalog. | -| `session.inspect` | `{detail?: "summary"|"full"}` | The document: tracks, clips (with times), inserts (+params when full), sends, routing, automation, markers, groups, busses, sources, selection, transport, undo label. | +| `session.inspect` | `{detail?: "summary"|"full"}` | The document: tracks, clips (with times), inserts (+params when full), sends, routing, automation, markers, groups, busses, sources (`loaded` true when decoded audio is in the pool), selection, transport, undo label. | | `ui.inspect` | `{}` | UI state (`window`, panels, dialogs), window size, playing/position, audio device, and `edit_layout` (timeline rect and every track row's rect, for clicking). | | `ui.set` | any `UiState` fields | e.g. `{"window": "Mix", "narrow_mix": true}`. | | `ui.menu.list` | `{}` | Every catalog menu path and the command that implements it (or null). | diff --git a/docs/mcp.md b/docs/mcp.md index ed5c8e6..ad197bb 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -23,7 +23,7 @@ claude mcp add soundcraft -- soundcraft-cli mcp --demo | `list_commands` | Discover every command (id, label, menu path, shortcut, params doc, enabled). | | `execute` | Run one command with JSON params. | | `batch` | Run several commands in order (`keep_going` optional). | -| `inspect_session` | Verify work: tracks, clips, mixer, plugins, sends, automation, markers, selection. | +| `inspect_session` | Verify work: tracks, clips, mixer, plugins, sends, automation, markers, selection; each `sources[]` entry includes `loaded` (decoded audio in the pool). | | `new_session`, `open_session`, `save_session` | Session files (`.scraft`, audio in `Audio Files/`). | | `import_audio` | WAV, AIFF, FLAC, MP3, OGG, AAC/M4A, ALAC, CAF onto a new or existing track. | | `bounce_mix` | Render to WAV/AIFF/FLAC; returns peak dBFS and integrated LUFS. |