Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/studio/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@
"test:timeline-virtualization": "TIMELINE_ROW_VIRTUALIZATION=on TIMELINE_ELEMENT_COUNT=50000 node tests/e2e/timeline-virtualization.mjs",
"test:watch": "vitest",
"report:sdk-cutover": "bun src/utils/sdkCutoverPolicy.report.ts",
"test:timeline-default": "bun run test:timeline-virtualization"
"test:timeline-default": "bun run test:timeline-virtualization",
"test:studio-load": "STUDIO_LOAD_ARM=dev bun tests/e2e/studio-load.mjs",
"test:studio-load-embedded": "STUDIO_LOAD_ARM=embedded bun tests/e2e/studio-load.mjs"
},
"dependencies": {
"@codemirror/autocomplete": "^6.20.1",
Expand Down
3 changes: 3 additions & 0 deletions packages/studio/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,14 @@ import {
} from "./utils/studioUrlState";
import { trackStudioSessionStart } from "./telemetry/events";
import { hasFiredSessionStart, markSessionStartFired } from "./telemetry/config";
import { useStudioLoadMarks } from "./lib/useStudioLoadMarks";
// fallow-ignore-next-line complexity
export function StudioApp() {
const { projectId, resolving, waitingForServer } = useServerConnection();
const isPastSplashGate = !(resolving || waitingForServer || !projectId);
const initialUrlStateRef = useRef(readStudioUrlStateFromWindow());
const viewModeValue = useViewModeState();
useStudioLoadMarks(isPastSplashGate, projectId);
useEffect(() => {
if (resolving || waitingForServer) return;
if (hasFiredSessionStart()) return;
Expand Down
47 changes: 47 additions & 0 deletions packages/studio/src/lib/studioLoadBudgets.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { describe, expect, it } from "vitest";
import { STUDIO_LOAD_BUDGETS, resolveStudioLoadBudgets } from "./studioLoadBudgets";

const EXPECTED_STUDIO_LOAD_BUDGETS = {
shellReadyP95Ms: 250,
constrainedShellReadyP95Ms: 600,
projectOpen1kClipsP95Ms: 1_500,
constrainedProjectOpen1kClipsP95Ms: 3_000,
projectOpen3kClipsP95Ms: 2_500,
constrainedProjectOpen3kClipsP95Ms: 5_000,
devShellReadyP95Ms: 400,
constrainedDevShellReadyP95Ms: 1_000,
devProjectOpen3kClipsP95Ms: 5_500,
constrainedDevProjectOpen3kClipsP95Ms: 9_000,
eagerEntryChunkGzipBytes: 600 * 1024,
};

describe("studio load budgets", () => {
it("owns the provisional studio load ceilings", () => {
expect(STUDIO_LOAD_BUDGETS).toMatchObject(EXPECTED_STUDIO_LOAD_BUDGETS);
expect(Object.isFrozen(STUDIO_LOAD_BUDGETS)).toBe(true);
});

it("creates an immutable result without changing production defaults", () => {
const resolved = resolveStudioLoadBudgets({});

expect(resolved).toMatchObject(EXPECTED_STUDIO_LOAD_BUDGETS);
expect(Object.isFrozen(resolved)).toBe(true);
expect(STUDIO_LOAD_BUDGETS).toMatchObject(EXPECTED_STUDIO_LOAD_BUDGETS);
});

it("overrides one budget while preserving every other default", () => {
const resolved = resolveStudioLoadBudgets({ shellReadyP95Ms: 500 });

expect(resolved).toMatchObject({ ...EXPECTED_STUDIO_LOAD_BUDGETS, shellReadyP95Ms: 500 });
});

it.each([
[{ eagerEntryChunkGzipBytes: -1 }, "eagerEntryChunkGzipBytes"],
[{ projectOpen3kClipsP95Ms: Number.NaN }, "projectOpen3kClipsP95Ms"],
])("rejects an invalid override %#", (overrides, field) => {
expect(() => resolveStudioLoadBudgets(overrides)).toThrow(RangeError);
expect(() => resolveStudioLoadBudgets(overrides)).toThrow(
`Studio load budget ${field} must be a finite non-negative number`,
);
});
});
48 changes: 48 additions & 0 deletions packages/studio/src/lib/studioLoadBudgets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
export interface StudioLoadBudgets {
shellReadyP95Ms: number;
constrainedShellReadyP95Ms: number;
projectOpen1kClipsP95Ms: number;
constrainedProjectOpen1kClipsP95Ms: number;
projectOpen3kClipsP95Ms: number;
constrainedProjectOpen3kClipsP95Ms: number;
devShellReadyP95Ms: number;
constrainedDevShellReadyP95Ms: number;
devProjectOpen3kClipsP95Ms: number;
constrainedDevProjectOpen3kClipsP95Ms: number;
eagerEntryChunkGzipBytes: number;
}

// Shell and dev-open budgets were revised once against the 2026-07-31 baseline
// per KTD1; see tests/e2e/studio-load.baseline.json. The originals (1000ms shell,
// 8000ms dev open) sat so far above measured reality that they gated nothing.
// The embedded project-open budgets are deliberately NOT revised: they are red
// today and stay red until U4 removes the quadratic.
export const STUDIO_LOAD_BUDGETS: Readonly<StudioLoadBudgets> = Object.freeze({
shellReadyP95Ms: 250,
constrainedShellReadyP95Ms: 600,
projectOpen1kClipsP95Ms: 1_500,
constrainedProjectOpen1kClipsP95Ms: 3_000,
projectOpen3kClipsP95Ms: 2_500,
constrainedProjectOpen3kClipsP95Ms: 5_000,
devShellReadyP95Ms: 400,
constrainedDevShellReadyP95Ms: 1_000,
devProjectOpen3kClipsP95Ms: 5_500,
constrainedDevProjectOpen3kClipsP95Ms: 9_000,
eagerEntryChunkGzipBytes: 600 * 1024,
});

function assertValidBudget(name: string, value: unknown): void {
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
throw new RangeError(`Studio load budget ${name} must be a finite non-negative number`);
}
}

export function resolveStudioLoadBudgets(
overrides: Partial<StudioLoadBudgets> = {},
): Readonly<StudioLoadBudgets> {
const entries: Array<[string, unknown]> = Object.entries(overrides);
for (const [name, value] of entries) {
assertValidBudget(name, value);
}
return Object.freeze({ ...STUDIO_LOAD_BUDGETS, ...overrides });
}
120 changes: 120 additions & 0 deletions packages/studio/src/lib/studioLoadMarks.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { beforeEach, describe, expect, it } from "vitest";
import {
STUDIO_LOAD_MEASURES,
markBoot,
markClipsPainted,
markManifestDeriveEnd,
markManifestDeriveStart,
markPreviewLoaded,
markProjectOpenStart,
markShellReady,
} from "./studioLoadMarks";

function markCompleteStudioLoad(): void {
markBoot();
markShellReady();
markProjectOpenStart();
markPreviewLoaded();
markManifestDeriveStart();
markManifestDeriveEnd();
markClipsPainted();
}

describe("studio load marks", () => {
beforeEach(() => {
performance.clearMarks();
performance.clearMeasures();
});

it("constructs all named studio load measures", () => {
markCompleteStudioLoad();

for (const name of Object.values(STUDIO_LOAD_MEASURES)) {
const entries = performance.getEntriesByName(name, "measure");
expect(entries).toHaveLength(1);
expect(entries[0]?.name).toBe(name);
expect(entries[0]?.duration).toBeGreaterThanOrEqual(0);
}
});

it("contains preview and manifest derivation within the project-open interval", () => {
markCompleteStudioLoad();

const projectOpen = performance.getEntriesByName(
STUDIO_LOAD_MEASURES.projectOpen,
"measure",
)[0];
const subMeasures = [
performance.getEntriesByName(STUDIO_LOAD_MEASURES.preview, "measure")[0],
performance.getEntriesByName(STUDIO_LOAD_MEASURES.manifestDerive, "measure")[0],
];

expect(projectOpen).toBeDefined();
for (const subMeasure of subMeasures) {
expect(subMeasure).toBeDefined();
if (!projectOpen || !subMeasure) continue;
expect(subMeasure.startTime).toBeGreaterThanOrEqual(projectOpen.startTime);
expect(subMeasure.startTime + subMeasure.duration).toBeLessThanOrEqual(
projectOpen.startTime + projectOpen.duration,
);
}
});

it("does not close project open at its start mark", () => {
markProjectOpenStart();

expect(performance.getEntriesByName(STUDIO_LOAD_MEASURES.projectOpen, "measure")).toHaveLength(
0,
);
});

it("overwrites the project-open measure on a later project open", () => {
markProjectOpenStart();
markClipsPainted();
markProjectOpenStart();
markClipsPainted();

expect(
performance
.getEntriesByType("measure")
.filter((entry) => entry.name === STUDIO_LOAD_MEASURES.projectOpen),
).toHaveLength(1);
});

it("does not fabricate project open without its start mark", () => {
expect(markClipsPainted).not.toThrow();
expect(performance.getEntriesByName(STUDIO_LOAD_MEASURES.projectOpen, "measure")).toHaveLength(
0,
);
});

it("does not fabricate manifest derivation without its start mark", () => {
expect(markManifestDeriveEnd).not.toThrow();
expect(
performance.getEntriesByName(STUDIO_LOAD_MEASURES.manifestDerive, "measure"),
).toHaveLength(0);
});

it("degrades to no-ops when the Performance API is unavailable", () => {
const realPerformance = globalThis.performance;
try {
Object.defineProperty(globalThis, "performance", {
configurable: true,
value: { now: () => 0 },
});

expect(markBoot).not.toThrow();
expect(markShellReady).not.toThrow();
expect(markProjectOpenStart).not.toThrow();
expect(markPreviewLoaded).not.toThrow();
expect(markManifestDeriveStart).not.toThrow();
expect(markManifestDeriveEnd).not.toThrow();
expect(markClipsPainted).not.toThrow();
} finally {
Object.defineProperty(globalThis, "performance", {
configurable: true,
value: realPerformance,
});
}
});
});
99 changes: 99 additions & 0 deletions packages/studio/src/lib/studioLoadMarks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
const STUDIO_LOAD_MARKS = Object.freeze({
boot: "hf.studio.boot",
shellReady: "hf.studio.shell-ready",
projectOpenStart: "hf.studio.project-open-start",
previewLoaded: "hf.studio.preview-loaded",
clipsPainted: "hf.studio.clips-painted",
manifestDeriveStart: "hf.studio.manifest-derive-start",
manifestDeriveEnd: "hf.studio.manifest-derive-end",
});

export const STUDIO_LOAD_MEASURES = Object.freeze({
shell: "hf.studio.shell",
projectOpen: "hf.studio.project-open",
preview: "hf.studio.preview",
manifestDerive: "hf.studio.manifest-derive",
});

function getPerformance(): Performance | null {
const currentPerformance = globalThis.performance;
if (
typeof currentPerformance?.mark !== "function" ||
typeof currentPerformance?.measure !== "function"
) {
return null;
}
return currentPerformance;
}

function mark(name: string): void {
try {
const currentPerformance = getPerformance();
if (!currentPerformance) return;
if (typeof currentPerformance.clearMarks === "function") {
currentPerformance.clearMarks(name);
}
currentPerformance.mark(name);
} catch {}
}

function measure(name: string, startMark: string, endMark: string): void {
try {
const currentPerformance = getPerformance();
if (!currentPerformance || typeof currentPerformance.getEntriesByName !== "function") return;
if (
currentPerformance.getEntriesByName(startMark, "mark").length === 0 ||
currentPerformance.getEntriesByName(endMark, "mark").length === 0
) {
return;
}
if (typeof currentPerformance.clearMeasures === "function") {
currentPerformance.clearMeasures(name);
}
currentPerformance.measure(name, startMark, endMark);
} catch {}
}

export function markBoot(): void {
mark(STUDIO_LOAD_MARKS.boot);
}

export function markShellReady(): void {
mark(STUDIO_LOAD_MARKS.shellReady);
measure(STUDIO_LOAD_MEASURES.shell, STUDIO_LOAD_MARKS.boot, STUDIO_LOAD_MARKS.shellReady);
}

export function markProjectOpenStart(): void {
mark(STUDIO_LOAD_MARKS.projectOpenStart);
}

export function markPreviewLoaded(): void {
mark(STUDIO_LOAD_MARKS.previewLoaded);
measure(
STUDIO_LOAD_MEASURES.preview,
STUDIO_LOAD_MARKS.projectOpenStart,
STUDIO_LOAD_MARKS.previewLoaded,
);
}

export function markManifestDeriveStart(): void {
mark(STUDIO_LOAD_MARKS.manifestDeriveStart);
}

export function markManifestDeriveEnd(): void {
mark(STUDIO_LOAD_MARKS.manifestDeriveEnd);
measure(
STUDIO_LOAD_MEASURES.manifestDerive,
STUDIO_LOAD_MARKS.manifestDeriveStart,
STUDIO_LOAD_MARKS.manifestDeriveEnd,
);
}

export function markClipsPainted(): void {
mark(STUDIO_LOAD_MARKS.clipsPainted);
measure(
STUDIO_LOAD_MEASURES.projectOpen,
STUDIO_LOAD_MARKS.projectOpenStart,
STUDIO_LOAD_MARKS.clipsPainted,
);
}
44 changes: 44 additions & 0 deletions packages/studio/src/lib/useStudioLoadMarks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { useEffect, useRef } from "react";
import { usePlayerStore } from "../player/store/playerStore";
import { markClipsPainted, markProjectOpenStart, markShellReady } from "./studioLoadMarks";

/**
* Fires the three load seams the shell owns: shell-ready once the splash gate
* clears, project-open-start whenever a distinct project resolves, and
* clips-painted on the first frame after the timeline holds any clip.
*
* Clips-painted is observed from the store rather than from Timeline because
* both App and Timeline sit at the studio 600-line cap, and because the seam
* is "the element set became non-empty" — a store fact, not a component one.
* Deliberately not anchored on timelineReady: that flips in initializeAdapter
* before clip derivation runs, which would put the derivation cost outside the
* measured window.
*/
export function useStudioLoadMarks(isPastSplashGate: boolean, projectId: string | null): void {
const shellReadyFired = useRef(false);
useEffect(() => {
if (!isPastSplashGate || shellReadyFired.current) return;
shellReadyFired.current = true;
markShellReady();
}, [isPastSplashGate]);

const openStartFiredFor = useRef<string | null>(null);
useEffect(() => {
if (!projectId || openStartFiredFor.current === projectId) return;
openStartFiredFor.current = projectId;
markProjectOpenStart();
}, [projectId]);

const hasClips = usePlayerStore((state) => state.elements.length > 0);
const clipsPaintedFired = useRef(false);
useEffect(() => {
if (!hasClips) {
clipsPaintedFired.current = false;
return;
}
if (clipsPaintedFired.current) return;
clipsPaintedFired.current = true;
const frame = requestAnimationFrame(() => markClipsPainted());
return () => cancelAnimationFrame(frame);
}, [hasClips]);
}
Loading
Loading