feat(sdk,studio): stage 7 step 3a — persistPath + SDK session reload-on-change (#1449)

* feat(sdk,studio): stage 7 step 3a — persistPath + SDK session reload-on-change

Stage 7 Step 3a — SDK plumbing for routing Studio commits through the SDK
session. No behavior change: the session stays idle (no op routed yet).

SDK:
- Add OpenCompositionOptions.persistPath; thread to createPersistQueue so the
  persist queue writes back to the composition's real path instead of the
  "composition.html" default (blocker A).

Studio (useSdkSession):
- Pass persistPath = activeCompPath so a future dispatch persists the right file.
- Re-open the session when the active composition file changes on disk (HMR
  hf:file-change / SSE file-change), scoped to activeCompPath, so the in-memory
  linkedom document never goes stale under code-editor/agent/server edits
  (blocker C). Re-opening is additive while the session is idle; 3c must add
  self-write suppression once dispatch writes.

Tests: SDK persistPath default + override; shouldReloadSdkSession path-match.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(sdk): document persistPath as immutable for session lifetime

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Co-authored-by: Miguel Ángel <miguel07alm@protonmail.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Miguel Ángel <miguel07alm@protonmail.com>
This commit is contained in:
Vance Ingalls
2026-06-15 14:03:08 -07:00
committed by GitHub
co-authored by Claude Opus 4.8 Miguel Ángel
parent 92e2c8ce6a
commit 5fe87cc39b
4 changed files with 95 additions and 5 deletions
+3
View File
@@ -39,6 +39,8 @@ import type { PersistQueueModule } from "./persist-queue.js";
export interface OpenCompositionOptions {
persist?: PersistAdapter;
/** Adapter path the persist queue writes to. Default: "composition.html". Immutable for the session lifetime. */
persistPath?: string;
preview?: PreviewAdapter;
/** T3 embedded mode: override-set applied on top of the base template. */
overrides?: OverrideSet;
@@ -502,6 +504,7 @@ export async function openComposition(
if (opts?.persist) {
const pq = createPersistQueue(session, opts.persist, {
path: opts.persistPath,
onError: (e) => session._fireError(e),
});
session.attachPersistQueue(pq);
+27
View File
@@ -240,6 +240,33 @@ describe("persist adapter", () => {
await new Promise((r) => setTimeout(r, 20));
expect(errors).toHaveLength(1);
});
it("defaults the write path to composition.html when persistPath is omitted", async () => {
const adapter = createMemoryAdapter();
const writeSpy = vi.spyOn(adapter, "write");
const comp = await openComposition(BASE_HTML, { persist: adapter });
comp.setStyle("hf-title", { color: "#f00" });
await comp.flush();
const [path] = writeSpy.mock.calls[0] as [string, string];
expect(path).toBe("composition.html");
});
it("writes to persistPath when supplied", async () => {
const adapter = createMemoryAdapter();
const writeSpy = vi.spyOn(adapter, "write");
const comp = await openComposition(BASE_HTML, {
persist: adapter,
persistPath: "scenes/intro.html",
});
comp.setStyle("hf-title", { color: "#f00" });
await comp.flush();
const [path] = writeSpy.mock.calls[0] as [string, string];
expect(path).toBe("scenes/intro.html");
});
});
// ─── T3 embedded mode (override-set) ─────────────────────────────────────────