Files
callstack__agent-device/scripts/layering/runtime-command-cutover-table.test.ts
Michał Pierzchała f3d5b3d92c refactor(daemon): admit-before-bind as an admitted-plan token; retire the R32 syntax policy (#1841)
* refactor(daemon): admit-before-bind as an identity-keyed admitted-plan token; retire the R32 syntax policy

admitRuntimePlan (was inspectRequiredRuntimeUse) takes the plan and, on
success, mints an AdmittedRuntimePlan: a nominal class instance with nothing
readable on it. Its payload — a frozen copy of the device the facts were read
for, and the plan — lives in a module-private WeakMap keyed by the token's
exact identity, and the only way to read it is unwrapAdmittedRuntimePlan,
which refuses anything not minted here. The snapshot owning interface
(resolveBoundSnapshotCaptureRuntime, #1847) admits through it and its private
binder takes only the token: no bare plan, no separate device, and no
look-alike — a spread lacks the #private member (not assignable), a Proxy
around a real token types as the token but is a different identity (refused
at unwrap), Object.assign/defineProperty throw on the frozen instance, and the
class value is not exported so its constructor is not nameable.

That retires scripts/layering/runtime-command-cutover-snapshot.ts — R32's
per-command AST policy (call-shape recognition of the admission and a text
sniff for a local admission) — and the source-regex test beside the descriptor
tests. The generic row keeps retirement, narrowing, and singular execution;
the manufactured-proof column now also rejects casts to AdmittedRuntimePlan.

Planted reds: token degraded to a plain public shape → 2 unused
@ts-expect-error directives; unwrap reading the token surface via getters →
the Proxy regression fails; getter-based branded literal → the runtime
retarget test fails.

* docs(agents): the ADR 0019 unit checklist teaches the shipped admission API

#1836 documented inspectRequiredRuntimeUse with a forward note pointing here;
this PR makes admitRuntimePlan real, so the row now teaches it plus the
identity-keyed unwrap the binder uses, and points at the shared snapshot/diff
owning interface as the model.
2026-08-19 10:46:25 +02:00

650 lines
24 KiB
TypeScript

import assert from 'node:assert/strict';
import { test } from 'node:test';
import { checkRuntimeCommandCutover } from './runtime-command-cutover-policy.ts';
import { rowFor, sources, summariesFor } from './runtime-command-cutover-fixtures.ts';
import { cutoverTableDefects } from './runtime-command-cutover-model.ts';
import { MIGRATED_COMMAND_CUTOVERS } from './runtime-command-cutover-table.ts';
import { commandDescriptors } from '../../src/core/command-descriptor/registry.ts';
test('R20 boot rejects the superseded root readiness adapter', () => {
assert.deepEqual(
summariesFor('R20 boot-runtime-cutover', [
['src/platform-runtime-device-readiness-host.ts', 'export const adapter = {};'],
[
'src/daemon/handlers/session-state.ts',
`
function handleSessionStateCommands() {
runtime.operations.bootTarget(input);
runtime.operations.bootTargetHeadless(input);
}
handleSessionStateCommands(request);
`,
],
]),
['src/platform-runtime-device-readiness-host.ts: retired device readiness module remains'],
);
});
test('R20 boot operation singularity is scoped to the session-state handler', () => {
assert.deepEqual(
summariesFor('R20 boot-runtime-cutover', [
[
'src/daemon/handlers/session-state.ts',
`
function handleSessionStateCommands() {
runtime.operations.bootTarget(input);
runtime.operations.bootTargetHeadless(input);
}
function unrelatedShutdownHelper() { runtime.operations.bootTarget(input); }
handleSessionStateCommands(request);
`,
],
]),
[],
);
});
test('R32 snapshot rejects legacy admission and dispatcher projection', () => {
assert.deepEqual(
summariesFor('R32 snapshot-runtime-cutover', [
[
'src/core/command-descriptor/registry.ts',
`
const descriptors = [{
name: 'snapshot',
capability: { apple: {} },
dispatch: {},
platformExecution: { kind: 'device-runtime', use: snapshotCaptureUse },
}];
`,
],
[
'src/daemon/snapshot-runtime.ts',
`
function dispatchSnapshotViaRuntime(params) {
requireCommandSupported('snapshot', device);
return dispatchSnapshotRuntimeCommand({ ...params, command: 'snapshot' });
}
function handleSnapshotCommands() {
dispatchSnapshotViaRuntime(request);
}
handleSnapshotCommands(request);
`,
],
[
'src/daemon/snapshot-command-runtime.ts',
`
function dispatchSnapshotRuntimeCommand(params) {
return resolveBoundSnapshotCaptureRuntime(params, params.command);
}
`,
],
[
'src/daemon/snapshot-runtime-binding.ts',
`
function selectActiveAppSnapshot() { runtime.operations.captureSnapshot(input); }
function selectCustomActionsSnapshot() {
runtime.operations.captureSnapshotWithCustomActions(input);
}
function selectSnapshotWithoutActiveApp() {
runtime.operations.captureSnapshotWithoutActiveApp(input);
}
function resolveBoundSnapshotCaptureRuntime(params) {
const plan = resolveSnapshotRuntimePlan(normalizedIntent);
admitRuntimePlan({ device, plan, inspectFacts: params.inspectFacts });
return bindSnapshotCaptureRuntime(params.bindDevice, device, plan);
}
`,
],
['src/core/dispatch.ts', 'function handleSnapshotCommand() { return legacy.snapshot(); }'],
[
'src/core/capabilities.ts',
`
const HARMONYOS_SUPPORTED_COMMANDS = new Set(['snapshot']);
const WEB_SUPPORTED_COMMANDS = new Set(['snapshot']);
`,
],
]),
[
'src/core/command-descriptor/registry.ts: snapshot descriptor retains legacy capability admission',
'src/daemon/snapshot-runtime.ts: legacy snapshot capability admission requireCommandSupported',
'src/core/dispatch.ts: legacy snapshot capture route handleSnapshotCommand',
'src/core/capabilities.ts: static platform command set retains snapshot admission',
'src/core/capabilities.ts: static platform command set retains snapshot admission',
'src/core/command-descriptor/registry.ts: snapshot command still projects into the retired legacy dispatcher',
],
);
});
// Per-row acceptance for migrated commands. Every older-row case here was carried over
// from the per-command policy tests these rows replaced; the mechanism itself is proven
// in runtime-command-cutover-policy.test.ts.
const DEVICES_HANDLER = [
"import { listDeviceInventory as discoverInventory } from '../../core/dispatch-resolve.ts';",
'export async function handleDevices(request: Request) { return await discoverInventory(request); }',
].join('\n');
const DEVICES_HANDLER_FILE = 'src/daemon/handlers/session-inventory.ts';
// ---------------------------------------------------------------------------
// devices (R13)
// ---------------------------------------------------------------------------
test('devices rejects an exact legacy inventory module path', () => {
assert.deepEqual(
summariesFor('R17 device-inventory-cutover', [
[DEVICES_HANDLER_FILE, DEVICES_HANDLER],
['src/platforms/apple/devices.ts', 'export const staleInventoryRoute = true;'],
]),
['src/platforms/apple/devices.ts: retired device inventory module remains'],
);
});
test('devices rejects executable legacy imports and identifiers', () => {
const legacy = [
"import { listAppleDevices } from './platforms/apple/devices.ts';",
'export async function discover() { return await listAppleDevices(); }',
].join('\n');
const found = summariesFor('R17 device-inventory-cutover', [
[DEVICES_HANDLER_FILE, DEVICES_HANDLER],
['src/legacy-inventory.ts', legacy],
]).join('\n');
assert.match(found, /imports retired device inventory module/);
assert.match(found, /legacy device inventory route listAppleDevices/);
});
test('devices rejects dynamic imports and re-exports of legacy inventory modules', () => {
for (const route of [
"export const legacy = import('./platforms/apple/devices.ts');",
"export { staleInventory } from './core/platform-inventory.ts';",
]) {
assert.match(
summariesFor('R17 device-inventory-cutover', [
[DEVICES_HANDLER_FILE, DEVICES_HANDLER],
['src/legacy-inventory.ts', route],
]).join('\n'),
/imports retired device inventory module/,
);
}
});
test('devices ignores comments, strings, and similarly named non-route modules', () => {
const prose = [
"const note = 'listAppleDevices from core/platform-inventory';",
'// discoverDevices used to live in platforms/apple/devices.ts',
'export const cacheKey = note;',
].join('\n');
assert.deepEqual(
summariesFor('R17 device-inventory-cutover', [
[DEVICES_HANDLER_FILE, DEVICES_HANDLER],
['src/platforms/apple/devices-cache.ts', prose],
]),
[],
);
});
// ---------------------------------------------------------------------------
// logs (R14)
// ---------------------------------------------------------------------------
test('legacy logs scan catches a planted provider route and plugin facet', () => {
assert.deepEqual(
summariesFor('R14 logs-runtime-cutover', [
[
'src/daemon/planted.ts',
'await withAppLogProvider(provider, task); await startAppLog(request); handleLogsCommand(params);',
],
[
'src/platforms/planted/plugin.ts',
'export const plugin = { appLog: { resolveBackend() {} } } satisfies PlatformPlugin;',
],
]),
[
'src/daemon/planted.ts: legacy logs route withAppLogProvider',
'src/daemon/planted.ts: legacy logs route startAppLog',
'src/platforms/planted/plugin.ts: legacy PlatformPlugin appLog facet',
],
);
});
test('legacy logs scan ignores retired names in comments and string data', () => {
assert.deepEqual(
summariesFor('R14 logs-runtime-cutover', [
[
'src/daemon/planted.ts',
`
// AppLogProvider and startAppLog were removed.
const migrationNote = 'withAppLogProvider and requireCommandSupported("logs")';
const metadata = { note: 'appLogProvider' };
handleLogsCommand(params);
`,
],
['src/platforms/apple/plugin.ts', `const note = 'PUBLIC_COMMANDS.logs';`],
]),
[],
);
});
test('legacy logs scan retains type-import and computed executable route coverage', () => {
assert.deepEqual(
summariesFor('R14 logs-runtime-cutover', [
[
'src/daemon/planted.ts',
`
import type { AppLogProvider as LegacyProvider } from './old-app-log.ts';
const route = handlers['startAppLog'];
handleLogsCommand(params);
`,
],
]),
[
'src/daemon/planted.ts: legacy logs route AppLogProvider',
'src/daemon/planted.ts: legacy logs route startAppLog',
],
);
});
test('legacy logs scan catches planted capability and dual-admission routes', () => {
assert.deepEqual(
summariesFor('R14 logs-runtime-cutover', [
[
'src/core/command-descriptor/registry.ts',
`
const registry = [
{ name: 'logs', capability: { apple: {} }, platformExecution: runtime },
{ name: 'events', capability: { apple: {} } },
];
`,
],
[
'src/daemon/handlers/planted.ts',
`
const literal = requireCommandSupported('logs', device);
const symbolic = requireCommandSupported(PUBLIC_COMMANDS.logs, device);
handleLogsCommand(params);
`,
],
[
'src/platforms/apple/plugin.ts',
`const supports = { [PUBLIC_COMMANDS.logs]: supportsCoreDevice };`,
],
[
'src/core/capabilities.ts',
`const HARMONYOS_SUPPORTED_COMMANDS = new Set(['open', 'logs']);`,
],
]),
[
'src/core/command-descriptor/registry.ts: logs descriptor retains legacy capability admission',
'src/daemon/handlers/planted.ts: legacy logs capability admission requireCommandSupported',
'src/daemon/handlers/planted.ts: legacy logs capability admission requireCommandSupported',
'src/platforms/apple/plugin.ts: Apple plugin retains legacy logs support or hint closure',
'src/core/capabilities.ts: static platform command set retains logs admission',
],
);
});
test('legacy logs scan follows declaration roles after files move', () => {
assert.deepEqual(
summariesFor('R14 logs-runtime-cutover', [
[
'src/moved/command-declarations.ts',
`const descriptor = { name: 'logs', capability: { apple: {} } };`,
],
[
'src/moved/platform-contract.ts',
`export type PlatformPlugin = { readonly appLog?: { inspect(): void } };`,
],
[
'src/moved/apple-family.ts',
`const plugin = { appLog: { inspect() {} } } as const satisfies PlatformPlugin;`,
],
['src/moved/typed-plugin.ts', `const plugin: PlatformPlugin = { appLog: { inspect() {} } };`],
['src/moved/capability-data.ts', `const HARMONYOS_SUPPORTED_COMMANDS = new Set(['logs']);`],
['src/daemon/logs-route.ts', 'handleLogsCommand(params);'],
]),
[
'src/moved/command-declarations.ts: logs descriptor retains legacy capability admission',
'src/moved/platform-contract.ts: legacy PlatformPlugin appLog facet',
'src/moved/apple-family.ts: legacy PlatformPlugin appLog facet',
'src/moved/typed-plugin.ts: legacy PlatformPlugin appLog facet',
'src/moved/capability-data.ts: static platform command set retains logs admission',
],
);
});
test('R14 violations retain their exact source line', () => {
const [violation] = checkRuntimeCommandCutover(
sources([
[
'src/daemon/moved-handler.ts',
`
const note = 'reason: retained';
const widened = runtime as BoundDeviceRuntime<typeof use>;
`,
],
]),
[rowFor('logs')],
);
assert.deepEqual(violation, {
rule: 'R14 logs-runtime-cutover',
file: 'src/daemon/moved-handler.ts',
line: 3,
message: 'widened logs runtime type assertion',
});
});
test('logs narrowing scan catches planted assertions, non-null repair, and bracket access', () => {
const found = summariesFor('R14 logs-runtime-cutover', [
[
'src/daemon/handlers/planted.ts',
`
const widened = runtime as BoundDeviceRuntime<typeof use>;
widened.operations.appLogStart!({});
widened.operations['appLogDoctor']({});
handleLogsCommand(params);
`,
],
]);
assert.equal(found.length, 3);
assert.match(found.join('\n'), /widened logs runtime type assertion/);
assert.match(found.join('\n'), /non-null repair of a narrowed logs operation/);
assert.match(found.join('\n'), /bracketed logs operation access/);
});
test('logs keeps the broader non-null scan over every daemon runtime operation', () => {
assert.deepEqual(
summariesFor('R14 logs-runtime-cutover', [
[
'src/daemon/handlers/planted.ts',
'runtime.operations.somethingElse!({}); handleLogsCommand(params);',
],
]),
['src/daemon/handlers/planted.ts: non-null repair of a narrowed logs operation'],
);
});
// ---------------------------------------------------------------------------
// network (R15)
// ---------------------------------------------------------------------------
test('network cutover scan catches planted legacy execution and dual admission', () => {
assert.deepEqual(
summariesFor('R15 network-runtime-cutover', [
[
'src/daemon/handlers/planted.ts',
`
await readSessionNetworkCapture(input);
await provider.dumpNetwork({ include: 'all' });
requireCommandSupported('network', device);
function handleNetworkCommand() { runtime.operations.networkDump(input); }
handleNetworkCommand(request);
`,
],
[
'src/core/command-descriptor/registry.ts',
`const commands = [{ name: 'network', capability: { apple: {} } }];`,
],
['src/core/capabilities.ts', `const WEB_QUERY_COMMANDS = ['find', 'network'];`],
[
'src/platforms/apple/plugin.ts',
`const supports = { [PUBLIC_COMMANDS.network]: supportsDevice };`,
],
]),
[
'src/daemon/handlers/planted.ts: legacy network route readSessionNetworkCapture',
'src/daemon/handlers/planted.ts: daemon invokes legacy provider method dumpNetwork',
'src/daemon/handlers/planted.ts: legacy network capability admission requireCommandSupported',
'src/core/command-descriptor/registry.ts: network descriptor retains legacy capability admission',
'src/core/capabilities.ts: static platform command set retains network admission',
'src/platforms/apple/plugin.ts: Apple plugin retains legacy network support or hint closure',
],
);
});
test('network cutover scan rejects a retired implementation by committed path', () => {
assert.deepEqual(
summariesFor('R15 network-runtime-cutover', [
['src/daemon/network-log.ts', `export const parser = () => [];`],
[
'src/daemon/handlers/ok.ts',
'function handleNetworkCommand() { runtime.operations.networkDump(i); } handleNetworkCommand(r);',
],
]),
['src/daemon/network-log.ts: retired network module remains'],
);
});
test('network cutover scan ignores retired names in comments and string data', () => {
assert.deepEqual(
summariesFor('R15 network-runtime-cutover', [
[
'src/daemon/planted.ts',
`
// readSessionNetworkCapture and requireCommandSupported('network') were removed.
const note = 'provider.dumpNetwork and PUBLIC_COMMANDS.network';
function handleNetworkCommand() { runtime.operations.networkDump(input); }
handleNetworkCommand(request);
`,
],
]),
[],
);
});
test('network narrowing scan catches planted casts, non-null repair, and bracket access', () => {
const found = summariesFor('R15 network-runtime-cutover', [
[
'src/daemon/handlers/planted.ts',
`
const widened = runtime as BoundDeviceRuntime<typeof use>;
widened.operations.networkDump!({});
widened.operations['networkDump']({});
`,
],
]);
const messages = found.join('\n');
assert.match(messages, /widened network runtime type assertion/);
assert.match(messages, /non-null repair of a narrowed network operation/);
assert.match(messages, /bracketed network operation access/);
});
test('network route scan rejects planted neither and duplicate routes', () => {
assert.deepEqual(summariesFor('R15 network-runtime-cutover', []), [
'(network runtime): expected one handleNetworkCommand route, found 0',
'(network runtime): expected one narrowed networkDump call, found 0',
]);
assert.deepEqual(
summariesFor('R15 network-runtime-cutover', [
[
'src/daemon/planted.ts',
`
function handleNetworkCommand() {
runtime.operations.networkDump(input);
fallback.operations.networkDump(input);
}
handleNetworkCommand(first);
handleNetworkCommand(second);
`,
],
]),
[
'(network runtime): expected one handleNetworkCommand route, found 2',
'(network runtime): expected one narrowed networkDump call, found 2',
],
);
});
// ---------------------------------------------------------------------------
// record (R16)
// ---------------------------------------------------------------------------
test('R16 catches a planted legacy recording backend, admission, and plugin facet', () => {
assert.deepEqual(
summariesFor('R16 record-runtime-cutover', [
[
'src/daemon/handlers/planted.ts',
`
await resolveRecordingBackendForDevice(device).start(input);
requireCommandSupported('record', device);
requireCommandSupported(PUBLIC_COMMANDS.record, device);
const backend = RECORDING_BACKENDS_BY_TAG.android;
type LegacyTag = RecordingBackendTag;
function startRecording() { runtime.operations.screenRecordingStart(input); }
function createScreenRecordingRecoveryControl() {
runtime.operations.screenRecordingReattach(input);
runtime.operations.screenRecordingCleanup(input);
}
handleRecordTraceCommands(input);
`,
],
[
'src/core/command-descriptor/registry.ts',
`const commands = [{ name: 'record', capability: { apple: {} } }];`,
],
[
'src/platforms/apple/plugin.ts',
`const plugin = { recording: { resolveBackendTag() {} } } satisfies PlatformPlugin;`,
],
]),
[
'src/daemon/handlers/planted.ts: legacy recording route resolveRecordingBackendForDevice',
'src/daemon/handlers/planted.ts: legacy recording route RECORDING_BACKENDS_BY_TAG',
'src/daemon/handlers/planted.ts: legacy recording route RecordingBackendTag',
'src/daemon/handlers/planted.ts: legacy record capability admission requireCommandSupported',
'src/daemon/handlers/planted.ts: legacy record capability admission requireCommandSupported',
'src/core/command-descriptor/registry.ts: record descriptor retains legacy capability admission',
'src/platforms/apple/plugin.ts: legacy PlatformPlugin recording facet',
],
);
});
test('R16 excludes trace-only mechanics from the record cutover scan', () => {
assert.deepEqual(
summariesFor('R16 record-runtime-cutover', [
[
'src/daemon/handlers/record-trace.ts',
`
const traceRecording = { startTrace() {}, stopTrace() {} };
traceRecording.startTrace();
function startRecording() { runtime.operations.screenRecordingStart(input); }
function createScreenRecordingRecoveryControl() {
runtime.operations.screenRecordingReattach(input);
runtime.operations.screenRecordingCleanup(input);
}
handleRecordTraceCommands(input);
`,
],
]),
[],
);
});
test('R16 rejects the retired recording-provider scope while allowing the focused transport', () => {
const found = summariesFor('R16 record-runtime-cutover', [
[
'src/daemon/request-platform-providers.ts',
`
import type { RecordingProvider } from './recording-provider.ts';
const recordingProvider = resolveRecordingProvider();
await withRecordingProvider(undefined, task);
`,
],
[
'src/platform-runtime-screen-recording-apple-transport.ts',
`
// recordingProvider and withRecordingProvider are retired prose.
const prose = 'src/daemon/recording-provider.ts';
withAppleSimulatorScreenRecordingTransport(transport, task);
`,
],
['src/daemon/recording-provider.ts', `export const retired = true;`],
[
'src/daemon/dynamic-provider.ts',
`
const providers = { ['recordingProvider']: resolver };
await import('./recording-provider.ts');
`,
],
]);
const messages = found.join('\n');
assert.match(messages, /retired recording module remains/);
assert.match(messages, /imports retired recording module/);
assert.match(messages, /legacy recording route RecordingProvider/);
assert.match(messages, /legacy recording route recordingProvider/);
assert.match(messages, /legacy recording route resolveRecordingProvider/);
assert.match(messages, /legacy recording route withRecordingProvider/);
assert.doesNotMatch(
messages,
/src\/platform-runtime-screen-recording-apple-transport\.ts/,
'prose in the focused transport must stay allowed',
);
});
test('R16 rejects proof repair for a narrowed screen-recording operation', () => {
const found = summariesFor('R16 record-runtime-cutover', [
[
'src/daemon/handlers/planted.ts',
`
const widened = runtime as BoundDeviceRuntime<typeof use>;
widened.operations.screenRecordingStart!({});
widened.operations['screenRecordingReattach']({});
`,
],
]);
const messages = found.join('\n');
assert.match(messages, /widened recording runtime type assertion/);
assert.match(messages, /non-null repair of a narrowed recording operation/);
assert.match(messages, /bracketed recording operation access/);
});
// The four lifecycle descriptors are independent migration units: each carries its own durable
// row rather than inheriting a sibling's proof.
test('the four application-lifecycle descriptors each carry an independent durable cutover row', () => {
const rows = ['open', 'prepare', 'close', 'runtime'].map(rowFor);
assert.deepEqual(
rows.map(({ tier, execution }) => ({ tier, execution })),
[
{ tier: 'durable-resource', execution: 'device-runtime' },
{ tier: 'durable-resource', execution: 'device-runtime' },
{ tier: 'durable-resource', execution: 'device-runtime' },
{ tier: 'durable-resource', execution: 'device-runtime' },
],
);
assert.deepEqual(
rows.flatMap((row) => (row.lifecycleProof === undefined ? [row.command] : [])),
[],
);
});
// A row id names a heading in the layering report, so a sibling stack claiming the same id would
// silently merge two commands' violations.
test('the cutover table rejects a duplicate rule id', () => {
const [first] = MIGRATED_COMMAND_CUTOVERS;
assert.ok(first);
assert.deepEqual(cutoverTableDefects(MIGRATED_COMMAND_CUTOVERS), []);
assert.match(
cutoverTableDefects([first, { ...first, command: 'planted' }]).join('\n'),
/rule id .* is claimed by/,
);
});
test('every daemon-routed migrated descriptor has exactly one cutover row', () => {
const migratedDescriptors = commandDescriptors
.filter(
(descriptor) =>
descriptor.daemon !== undefined &&
(descriptor.platformExecution.kind === 'inventory' ||
descriptor.platformExecution.kind === 'device-runtime'),
)
.map(({ name }) => name)
.sort();
const tableCommands = MIGRATED_COMMAND_CUTOVERS.map(({ command }) => command).sort();
assert.deepEqual(tableCommands, migratedDescriptors);
});