Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
513d39f
media: add bounded transport channel
SunkenInTime Jul 25, 2026
dc1c189
fix: make Windows media duplex nonblocking
SunkenInTime Jul 25, 2026
97bc9e1
docs: keep transport implementation terms private
SunkenInTime Jul 25, 2026
559f18e
test: expect macOS transport duplex endpoint
SunkenInTime Jul 25, 2026
3adba02
fix: harden media transport boundaries
SunkenInTime Jul 26, 2026
3dcce08
test: keep macOS socket peer alive through authentication
SunkenInTime Jul 26, 2026
d158710
fix: keep time-only macOS widgets off provider drain
SunkenInTime Jul 26, 2026
13d7687
fix: wake provider loop only for transport acks
SunkenInTime Jul 26, 2026
2bb9d9e
fix: recover fatal media channels safely
SunkenInTime Jul 26, 2026
933b82e
test: keep stalled macOS host connected
SunkenInTime Jul 26, 2026
4cb011b
test: bound macOS stalled-host prefill
SunkenInTime Jul 26, 2026
178846b
test: isolate stalled macOS send seam
SunkenInTime Jul 26, 2026
9fde8a6
test: make stalled macOS send deterministic
SunkenInTime Jul 26, 2026
be2eedf
fix: batch macOS system provider samples
SunkenInTime Jul 26, 2026
f7e5e21
fix: expose first provider batch deterministically
SunkenInTime Jul 26, 2026
c2b2017
fix: publish framed provider batches atomically
SunkenInTime Jul 26, 2026
8e44c0c
test: assert macOS provider fan-out semantically
SunkenInTime Jul 26, 2026
ddfff61
test: gate macOS provider fan-out on delivery
SunkenInTime Jul 26, 2026
8a57765
spike: record blocked macOS media adapter
SunkenInTime Jul 25, 2026
d033f31
media: add macOS adapter provider
SunkenInTime Jul 25, 2026
d00cf80
fix: keep media commands off host loop
SunkenInTime Jul 25, 2026
ded4e98
fix: harden macOS media adapter
SunkenInTime Jul 26, 2026
ff822b1
fix: declare seek readback helper
SunkenInTime Jul 26, 2026
097e266
test: preserve authenticated peer in macOS adapter stack
SunkenInTime Jul 26, 2026
2a674dd
fix: detach macOS media workers during slot teardown
SunkenInTime Jul 26, 2026
52292c3
fix: supervise macOS media startup and timeline
SunkenInTime Jul 26, 2026
9c31229
fix: enforce media first-frame deadline externally
SunkenInTime Jul 26, 2026
9c70e3c
examples: complete live noro media gate
SunkenInTime Jul 25, 2026
01d7510
docs: verify adversarial media acceptance
SunkenInTime Jul 26, 2026
6b4eb8f
docs: record bounded macOS worker teardown
SunkenInTime Jul 26, 2026
ea272fb
fix: normalize noro seek interaction
SunkenInTime Jul 26, 2026
9e44c98
docs: record green media round-two stack
SunkenInTime Jul 26, 2026
8019484
docs: verify atomic media session artwork
SunkenInTime Jul 26, 2026
c729fb2
docs: record atomic artwork review repair
SunkenInTime Jul 26, 2026
39d9b33
docs: record artwork retry remediation
SunkenInTime Jul 26, 2026
12d5040
docs: record bounded artwork failure outcome
SunkenInTime Jul 26, 2026
f7d7284
docs: record green final media stack
SunkenInTime Jul 26, 2026
b6cc02a
examples: make noro REC dot follow playing status
SunkenInTime Jul 26, 2026
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
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,9 @@ jobs:
- name: Public media API decision probe
run: spikes/macos-media-observation/build.sh

- name: Vendored MediaRemote adapter static audit
run: spikes/macos-mediaremote-adapter/static-audit.sh

- name: Native SDK stock profile
working-directory: runtime/native-sdk
run: zig build test
Expand Down
134 changes: 132 additions & 2 deletions cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ interface WidgetConfigData {
clickThrough?: boolean;
subscribe?: ("time" | "cpu" | "memory" | "audio" | "media")[];
origins?: string[];
capabilities?: never[];
capabilities?: ("media-transport")[];
}

interface RuntimeManifest {
Expand All @@ -77,6 +77,7 @@ interface RuntimeManifest {
transparent: true;
origins: string[];
subscribe: ("time" | "cpu" | "memory" | "audio" | "media")[];
capabilities: ("media-transport")[];
renderBackend: "gpu" | "software";
fonts: RuntimeFont[];
}
Expand Down Expand Up @@ -216,6 +217,7 @@ async function bundleWidget(directory: string): Promise<BundleResult> {
transparent: true,
origins: project.config.origins ?? [],
subscribe: project.config.subscribe ?? [],
capabilities: project.config.capabilities ?? [],
renderBackend: sourceUsesCanvas(project.sourceFile) ? "gpu" : "software",
fonts: project.fonts,
};
Expand Down Expand Up @@ -972,7 +974,9 @@ function validateConfigShape(value: unknown, sourceFile: ts.SourceFile, node: ts
if (value.layer !== undefined && !["desktop", "normal", "topmost"].includes(String(value.layer))) errors.push(locationMessage(sourceFile, node, "config.layer must be desktop, normal, or topmost"));
if (value.clickThrough !== undefined && typeof value.clickThrough !== "boolean") errors.push(locationMessage(sourceFile, node, "config.clickThrough must be boolean"));
if (value.subscribe !== undefined && (!Array.isArray(value.subscribe) || value.subscribe.some((item) => !["time", "cpu", "memory", "audio", "media"].includes(String(item))))) errors.push(locationMessage(sourceFile, node, 'config.subscribe supports only "time", "cpu", "memory", "audio", and "media"'));
if (value.capabilities !== undefined && (!Array.isArray(value.capabilities) || value.capabilities.length > 0)) errors.push(locationMessage(sourceFile, node, "Widget capabilities are not exposed in M2a; capabilities must be empty"));
if (value.capabilities !== undefined && (!Array.isArray(value.capabilities) || value.capabilities.some((item) => item !== "media-transport"))) {
errors.push(locationMessage(sourceFile, node, 'config.capabilities supports only "media-transport"'));
}
if (value.origins !== undefined) {
if (!Array.isArray(value.origins) || value.origins.some((origin) => !validOriginHost(origin))) errors.push(locationMessage(sourceFile, node, 'config.origins entries must be exact hosts such as "api.example.com"'));
}
Expand Down Expand Up @@ -1168,6 +1172,131 @@ function validateLoweredTreeBudgets(project: SourceProject, errors: string[]): v
}
}

function validateMediaTransportCapability(project: SourceProject, errors: string[]): void {
const configPath = join(project.directory, "tsconfig.json");
const configRead = ts.readConfigFile(configPath, (path) => readFileSync(path, "utf8"));
if (configRead.error) return; // The ordinary TypeScript invocation reports it.
const parsed = ts.parseJsonConfigFileContent(configRead.config, ts.sys, project.directory, undefined, configPath);
const sdkDirectory = join(repoRoot, "sdk");
// Bundling owns these two specifiers regardless of widget-authored paths.
// Check must compile the same graph or a local compatible declaration can
// hide capability use that the bundle later binds to Weaver's real SDK.
const options: ts.CompilerOptions = {
...parsed.options,
paths: {
...parsed.options.paths,
"@weaver/sdk": [join(sdkDirectory, "index.d.ts")],
"@weaver/sdk/jsx-runtime": [join(sdkDirectory, "jsx-runtime.d.ts")],
},
};
const program = ts.createProgram({ rootNames: parsed.fileNames, options });
const checker = program.getTypeChecker();

const sdkHookDeclaration = (declaration: ts.Declaration | undefined): boolean => {
if (!declaration) return false;
const path = resolve(declaration.getSourceFile().fileName);
if (!pathsEqual(path, sdkDirectory) && !pathInside(sdkDirectory, path)) return false;
const named = declaration as ts.Declaration & { name?: ts.DeclarationName };
if (!named.name) return false;
let symbol = checker.getSymbolAtLocation(named.name);
if (symbol && (symbol.flags & ts.SymbolFlags.Alias) !== 0) symbol = checker.getAliasedSymbol(symbol);
return symbol?.getName() === "useMediaTransport";
};

const assignments = new Map<ts.Symbol, ts.Expression[]>();
const canonicalSymbol = (node: ts.Node): ts.Symbol | undefined => {
let symbol = checker.getSymbolAtLocation(node);
const seen = new Set<ts.Symbol>();
while (symbol && (symbol.flags & ts.SymbolFlags.Alias) !== 0 && !seen.has(symbol)) {
seen.add(symbol);
symbol = checker.getAliasedSymbol(symbol);
}
return symbol;
};
const rememberAssignment = (target: ts.Expression, value: ts.Expression): void => {
const symbol = canonicalSymbol(target);
if (!symbol) return;
const values = assignments.get(symbol) ?? [];
values.push(value);
assignments.set(symbol, values);
};
for (const sourceFile of program.getSourceFiles()) {
const indexAssignments = (node: ts.Node): void => {
if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
rememberAssignment(node.left, node.right);
}
ts.forEachChild(node, indexAssignments);
};
indexAssignments(sourceFile);
}

const tracesSdkHookSymbol = (symbol: ts.Symbol | undefined, seen: Set<ts.Symbol>): boolean => {
while (symbol && (symbol.flags & ts.SymbolFlags.Alias) !== 0 && !seen.has(symbol)) {
seen.add(symbol);
symbol = checker.getAliasedSymbol(symbol);
}
if (!symbol || seen.has(symbol)) return false;
seen.add(symbol);
if (symbol.getName() === "useMediaTransport" && (symbol.declarations ?? []).some(sdkHookDeclaration)) return true;
for (const value of assignments.get(symbol) ?? []) {
if (tracesSdkHookNode(value, seen)) return true;
}
for (const declaration of symbol.declarations ?? []) {
if (ts.isVariableDeclaration(declaration) && declaration.initializer && tracesSdkHookNode(declaration.initializer, seen)) return true;
if (ts.isPropertyAssignment(declaration) && tracesSdkHookNode(declaration.initializer, seen)) return true;
if (ts.isShorthandPropertyAssignment(declaration)) {
const valueSymbol = checker.getShorthandAssignmentValueSymbol(declaration);
if (tracesSdkHookSymbol(valueSymbol, seen)) return true;
}
if (ts.isBindingElement(declaration)) {
let parent: ts.Node = declaration.parent;
while (ts.isObjectBindingPattern(parent) || ts.isArrayBindingPattern(parent) || ts.isBindingElement(parent)) parent = parent.parent;
if (ts.isVariableDeclaration(parent) && parent.initializer) {
const propertyNode = declaration.propertyName ?? declaration.name;
const propertyName = ts.isIdentifier(propertyNode) || ts.isStringLiteral(propertyNode) || ts.isNumericLiteral(propertyNode)
? propertyNode.text
: undefined;
if (propertyName) {
const sourceType = checker.getTypeAtLocation(parent.initializer);
if (tracesSdkHookSymbol(checker.getPropertyOfType(sourceType, propertyName), seen)) return true;
}
}
}
}
return false;
};
function tracesSdkHookNode(node: ts.Node, seen = new Set<ts.Symbol>()): boolean {
return tracesSdkHookSymbol(canonicalSymbol(node), seen);
}
const signatureIsSdkHook = (node: ts.CallExpression): boolean => {
const declaration = checker.getResolvedSignature(node)?.getDeclaration();
return sdkHookDeclaration(declaration);
};
const isSdkHook = (node: ts.CallExpression): boolean => {
// The signature declaration is the authoritative backstop: TypeScript
// preserves the SDK call signature through destructuring, assignments,
// object properties, and re-export chains even when the local symbol is a
// BindingElement or an inferred variable.
return signatureIsSdkHook(node) || tracesSdkHookNode(node.expression);
};

for (const sourceFile of program.getSourceFiles()) {
const path = resolve(sourceFile.fileName);
if (sourceFile.isDeclarationFile || (!pathsEqual(path, project.directory) && !pathInside(project.directory, path))) continue;
const visit = (node: ts.Node): void => {
if (ts.isCallExpression(node) && isSdkHook(node) && !project.config.capabilities?.includes("media-transport")) {
errors.push(locationMessage(
sourceFile,
node,
'useMediaTransport() requires capabilities: ["media-transport"]. Fix: add capabilities: ["media-transport"] to the widget config.',
));
}
ts.forEachChild(node, visit);
};
visit(sourceFile);
}
}

function validateSource(project: SourceProject): string[] {
const errors: string[] = [];
const usedProviders = new Set<"time" | "cpu" | "memory" | "audio" | "media">();
Expand Down Expand Up @@ -1250,6 +1379,7 @@ function validateSource(project: SourceProject): string[] {
for (const provider of usedProviders) {
if (!project.config.subscribe?.includes(provider)) errors.push(`useProvider("${provider}") requires subscribe: ["${provider}"] in the widget config`);
}
validateMediaTransportCapability(project, errors);
validateLoweredTreeBudgets(project, errors);
return errors;
}
Expand Down
117 changes: 117 additions & 0 deletions cli/test/cli.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,123 @@ export default widget({ name: "System Card", size: [200, 100], subscribe: ["cpu"
}
});

test("media transport capability gate follows SDK signatures through every binding form", () => {
const root = mkdtempSync(join(tmpdir(), "weaver-media-capability-"));
const widget = join(root, "transport");
const cli = fileURLToPath(new URL("../dist/index.js", import.meta.url));
try {
mkdirSync(widget, { recursive: true });
writeFileSync(join(widget, "tsconfig.json"), JSON.stringify({
compilerOptions: {
target: "ES2020", module: "ESNext", moduleResolution: "Bundler", strict: true, noEmit: true,
jsx: "react-jsx", jsxImportSource: "@weaver/sdk", baseUrl: ".",
paths: { "@weaver/sdk": [join(process.cwd(), "sdk/index.d.ts")], "@weaver/sdk/jsx-runtime": [join(process.cwd(), "sdk/jsx-runtime.d.ts")] },
},
include: ["**/*.ts", "**/*.tsx"],
}));
const rejected = [
`import { useMediaTransport as t, widget } from "@weaver/sdk";
export default widget({ name: "Alias", size: [160, 80] }, () => { const media = t(); return <button onPress={() => void media.play()}><text>Play</text></button>; });
`,
`import { widget } from "@weaver/sdk";
import * as Weaver from "@weaver/sdk";
export default widget({ name: "Namespace", size: [160, 80] }, () => { const media = Weaver.useMediaTransport(); return <button onPress={() => void media.pause()}><text>Pause</text></button>; });
`,
`import { widget } from "@weaver/sdk";
import * as Weaver from "@weaver/sdk";
const { useMediaTransport: hook } = Weaver;
export default widget({ name: "Destructure", size: [160, 80] }, () => { const media = hook(); return <button onPress={() => void media.play()}><text>Play</text></button>; });
`,
`import { widget } from "@weaver/sdk";
import * as Weaver from "@weaver/sdk";
let hook: typeof Weaver.useMediaTransport;
hook = Weaver.useMediaTransport;
export default widget({ name: "Assigned", size: [160, 80] }, () => { const media = hook(); return <button onPress={() => void media.pause()}><text>Pause</text></button>; });
`,
`import { widget } from "@weaver/sdk";
import * as Weaver from "@weaver/sdk";
const controls = { transport: Weaver.useMediaTransport };
export default widget({ name: "Property", size: [160, 80] }, () => { const media = controls.transport(); return <button onPress={() => void media.next()}><text>Next</text></button>; });
`,
];
for (const source of rejected) {
writeFileSync(join(widget, "widget.tsx"), source);
rmSync(join(widget, "controls.ts"), { force: true });
const result = spawnSync(process.execPath, [cli, "check", widget], { encoding: "utf8" });
assert.equal(result.status, 1, `${result.stderr}\n${source}`);
assert.match(result.stderr, /useMediaTransport\(\) requires capabilities: \["media-transport"\]/);
}

writeFileSync(join(widget, "widget.tsx"), `import { useMediaTransport, widget } from "@weaver/sdk";
const hook = useMediaTransport;
export default widget({ name: "Const alias", size: [160, 80] }, () => { const media = hook(); return <button onPress={() => void media.play()}><text>Play</text></button>; });
`);
const constAlias = spawnSync(process.execPath, [cli, "check", widget], { encoding: "utf8" });
assert.equal(constAlias.status, 1);
assert.match(constAlias.stderr, /useMediaTransport\(\) requires capabilities: \["media-transport"\]/);

writeFileSync(join(widget, "fake-sdk.d.ts"), `export declare function useMediaTransport(): { play(): Promise<boolean> };
export declare function widget(config: unknown, render: () => unknown): unknown;
`);
writeFileSync(join(widget, "tsconfig.json"), JSON.stringify({
compilerOptions: {
target: "ES2020", module: "ESNext", moduleResolution: "Bundler", strict: true, noEmit: true,
jsx: "react-jsx", jsxImportSource: "@weaver/sdk", baseUrl: ".",
paths: { "@weaver/sdk": ["fake-sdk.d.ts"], "@weaver/sdk/jsx-runtime": [join(process.cwd(), "sdk/jsx-runtime.d.ts")] },
},
include: ["**/*.ts", "**/*.tsx"],
}));
writeFileSync(join(widget, "widget.tsx"), `import { useMediaTransport, widget } from "@weaver/sdk";
export default widget({ name: "Mapped fake", size: [160, 80] }, () => { const media = useMediaTransport(); return <button onPress={() => void media.play()}><text>Play</text></button>; });
`);
const customMapping = spawnSync(process.execPath, [cli, "check", widget], { encoding: "utf8" });
assert.equal(customMapping.status, 1);
assert.match(customMapping.stderr, /useMediaTransport\(\) requires capabilities: \["media-transport"\]/);

writeFileSync(join(widget, "tsconfig.json"), JSON.stringify({
compilerOptions: {
target: "ES2020", module: "ESNext", moduleResolution: "Bundler", strict: true, noEmit: true,
jsx: "react-jsx", jsxImportSource: "@weaver/sdk", baseUrl: ".",
paths: { "@weaver/sdk": [join(process.cwd(), "sdk/index.d.ts")], "@weaver/sdk/jsx-runtime": [join(process.cwd(), "sdk/jsx-runtime.d.ts")] },
},
include: ["**/*.ts", "**/*.tsx"],
}));
writeFileSync(join(widget, "controls.ts"), `import { useMediaTransport } from "@weaver/sdk";
export function controls() { return useMediaTransport(); }
`);
writeFileSync(join(widget, "widget.tsx"), `import { widget } from "@weaver/sdk";
import { controls } from "./controls";
export default widget({ name: "Helper", size: [160, 80] }, () => { const media = controls(); return <button onPress={() => void media.next()}><text>Next</text></button>; });
`);
const helper = spawnSync(process.execPath, [cli, "check", widget], { encoding: "utf8" });
assert.equal(helper.status, 1);
assert.match(helper.stderr, /controls\.ts:\d+:\d+: useMediaTransport\(\) requires capabilities/);

writeFileSync(join(widget, "transport-origin.ts"), `export { useMediaTransport as transportHook } from "@weaver/sdk";
`);
writeFileSync(join(widget, "transport-export.ts"), `export { transportHook as hook } from "./transport-origin";
`);
writeFileSync(join(widget, "widget.tsx"), `import { widget } from "@weaver/sdk";
import { hook } from "./transport-export";
export default widget({ name: "Re-export", size: [160, 80] }, () => { const media = hook(); return <button onPress={() => void media.previous()}><text>Previous</text></button>; });
`);
const reexport = spawnSync(process.execPath, [cli, "check", widget], { encoding: "utf8" });
assert.equal(reexport.status, 1);
assert.match(reexport.stderr, /useMediaTransport\(\) requires capabilities: \["media-transport"\]/);

writeFileSync(join(widget, "widget.tsx"), `import { widget } from "@weaver/sdk";
import { controls } from "./controls";
export default widget({ name: "Declared", size: [160, 80], capabilities: ["media-transport"] }, () => { const media = controls(); return <button onPress={() => void media.previous()}><text>Previous</text></button>; });
`);
const bundle = spawnSync(process.execPath, [cli, "bundle", widget], { encoding: "utf8" });
assert.equal(bundle.status, 0, bundle.stderr);
const manifest = JSON.parse(readFileSync(join(widget, "dist", "widget.json"), "utf8"));
assert.deepEqual(manifest.capabilities, ["media-transport"]);
} finally {
rmSync(root, { recursive: true, force: true });
}
});

test("styling 07 discovers, validates, bundles, and names TrueType faces", () => {
const root = mkdtempSync(join(tmpdir(), "weaver-font-bundle-"));
try {
Expand Down
1 change: 1 addition & 0 deletions cli/test/example-surface-smoke.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const fixtures = [
"pomodoro",
"system",
"now-playing",
"noro-shell",
"visualizer",
"dpi-diagnostic",
"m4b-parity",
Expand Down
2 changes: 1 addition & 1 deletion cli/test/macos-host-control-smoke.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ try {
trackedPids.add(first.hostPid);
assert.deepEqual(first.widgets, []);
assert.equal(first.providers.audioCaptureActive, false);
assert.equal(first.providers.mediaAvailability, "unavailable");
assert.equal(first.providers.mediaAvailability, "idle");
assert.equal(first.providers.mediaSubscribers, 0);
assert.equal(first.providers.mediaPipeFrames, 0);
assert.match(run(["up"]).stdout, /already running/);
Expand Down
Loading
Loading