mirror of
https://github.com/callstack/agent-device.git
synced 2026-09-14 20:06:34 +08:00
f3d5b3d92c
* 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.
288 lines
10 KiB
TypeScript
288 lines
10 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import { test } from 'node:test';
|
|
import { checkRuntimeCommandCutover } from './runtime-command-cutover-policy.ts';
|
|
import { MIGRATED_COMMAND_CUTOVERS } from './runtime-command-cutover-table.ts';
|
|
import { cutoverRowDefects, type MigratedCommandCutover } from './runtime-command-cutover-model.ts';
|
|
import { PLANTED_ROW, rowFor, sources, summariesFor } from './runtime-command-cutover-fixtures.ts';
|
|
|
|
// The mechanism itself (ADR 0019 §8): one planted row, one planted red. Per-row
|
|
// acceptance for the migrated commands lives in the table test.
|
|
|
|
const PLANTED_RULE = 'RPlanted planted-cutover';
|
|
|
|
test('the parametrized gate goes red on a planted row across every generalized column', () => {
|
|
assert.deepEqual(
|
|
summariesFor(
|
|
PLANTED_RULE,
|
|
[
|
|
['src/daemon/planted-legacy.ts', 'export const retired = true;'],
|
|
[
|
|
'src/daemon/planted-handler.ts',
|
|
[
|
|
"import { resolvePlantedBackend } from './planted-legacy.ts';",
|
|
"requireCommandSupported('planted', device);",
|
|
'const widened = runtime as PlantedRuntimeOperations;',
|
|
'const forged = { admitted: true, plan } as AdmittedRuntimePlan<PlantedPlan>;',
|
|
"function handlePlantedCommand() { widened.operations['plantedDump']({}); }",
|
|
].join('\n'),
|
|
],
|
|
[
|
|
'src/core/command-descriptor/registry.ts',
|
|
`const commands = [{ name: 'planted', capability: { apple: {} } }];`,
|
|
],
|
|
['src/core/capabilities.ts', `const WEB_QUERY_COMMANDS = ['find', 'planted'];`],
|
|
[
|
|
'src/platforms/apple/plugin.ts',
|
|
`const supports = { [PUBLIC_COMMANDS.planted]: supportsDevice };
|
|
const plugin = { planted: { resolveBackend() {} } } satisfies PlatformPlugin;`,
|
|
],
|
|
],
|
|
[PLANTED_ROW],
|
|
),
|
|
[
|
|
'src/daemon/planted-legacy.ts: retired planted module remains',
|
|
"src/daemon/planted-handler.ts: production source imports retired planted module './planted-legacy.ts'",
|
|
'src/daemon/planted-handler.ts: legacy planted route resolvePlantedBackend',
|
|
'src/daemon/planted-handler.ts: legacy planted capability admission requireCommandSupported',
|
|
'src/daemon/planted-handler.ts: widened planted runtime type assertion',
|
|
// The admission proof is shared across rows: a route that casts its way to an
|
|
// AdmittedRuntimePlan has manufactured the facts-first admission the binder requires.
|
|
'src/daemon/planted-handler.ts: widened planted runtime type assertion',
|
|
'src/daemon/planted-handler.ts: bracketed planted operation access',
|
|
'src/core/command-descriptor/registry.ts: planted descriptor retains legacy capability admission',
|
|
'src/core/capabilities.ts: static platform command set retains planted admission',
|
|
'src/platforms/apple/plugin.ts: legacy PlatformPlugin planted facet',
|
|
'src/platforms/apple/plugin.ts: Apple plugin retains legacy planted support or hint closure',
|
|
// The bracketed access is still a call, so the operation count is satisfied while the
|
|
// route is missing: the narrowing column and the singular-route column are independent.
|
|
'(planted runtime): expected one handlePlantedCommand route, found 0',
|
|
],
|
|
);
|
|
});
|
|
|
|
test('the planted row is green once the command has exactly one execution path', () => {
|
|
assert.deepEqual(
|
|
summariesFor(
|
|
PLANTED_RULE,
|
|
[
|
|
[
|
|
'src/daemon/planted-handler.ts',
|
|
`
|
|
// resolvePlantedBackend and requireCommandSupported('planted') were removed.
|
|
const note = 'planted-legacy.ts';
|
|
function handlePlantedCommand() {
|
|
runtime.operations.plantedDump(input);
|
|
}
|
|
handlePlantedCommand(request);
|
|
`,
|
|
],
|
|
],
|
|
[PLANTED_ROW],
|
|
),
|
|
[],
|
|
);
|
|
});
|
|
|
|
test('rows are independent: one command going red leaves the others alone', () => {
|
|
const violations = checkRuntimeCommandCutover(
|
|
sources([['src/daemon/network-log.ts', 'export const parser = () => [];']]),
|
|
MIGRATED_COMMAND_CUTOVERS.filter(
|
|
({ command }) => command === 'network' || command === 'record',
|
|
),
|
|
);
|
|
assert.deepEqual(
|
|
violations
|
|
.filter(({ rule }) => rule === 'R15 network-runtime-cutover')
|
|
.map(({ message }) => message),
|
|
[
|
|
'retired network module remains',
|
|
'expected one handleNetworkCommand route, found 0',
|
|
'expected one narrowed networkDump call, found 0',
|
|
],
|
|
);
|
|
assert.deepEqual(
|
|
violations
|
|
.filter(({ rule }) => rule === 'R16 record-runtime-cutover')
|
|
.map(({ message }) => message),
|
|
[
|
|
'expected one handleRecordTraceCommands route, found 0',
|
|
'expected one narrowed screenRecordingStart call, found 0',
|
|
'expected one narrowed screenRecordingReattach call, found 0',
|
|
'expected one narrowed screenRecordingCleanup call, found 0',
|
|
],
|
|
);
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// A row cannot opt out of enforcement by leaving its claims unstated.
|
|
// ---------------------------------------------------------------------------
|
|
|
|
test('planted red: an incomplete row fails instead of enforcing nothing', () => {
|
|
const incomplete = {
|
|
rule: PLANTED_RULE,
|
|
command: 'planted',
|
|
subject: 'planted',
|
|
} as unknown as MigratedCommandCutover;
|
|
|
|
assert.deepEqual(
|
|
summariesFor(PLANTED_RULE, [['src/daemon/anything.ts', 'export const x = 1;']], [incomplete]),
|
|
[
|
|
'(planted cutover row): cutover row declares no legacy retirement form',
|
|
'(planted cutover row): cutover row declares no evidence tier',
|
|
'(planted cutover row): cutover row declares no execution kind',
|
|
],
|
|
);
|
|
});
|
|
|
|
test('planted red: a row that retires nothing is rejected', () => {
|
|
assert.deepEqual(cutoverRowDefects({ ...PLANTED_ROW, legacyRetirement: {} }), [
|
|
'declares no legacy retirement form',
|
|
]);
|
|
});
|
|
|
|
test('planted red: an empty singular-execution claim is rejected', () => {
|
|
assert.deepEqual(
|
|
cutoverRowDefects({
|
|
...PLANTED_ROW,
|
|
singularExecution: { routes: [] as unknown as [string, ...string[]] },
|
|
}),
|
|
[
|
|
'declares no singular daemon route',
|
|
'names its operations but enforces none of them exactly once',
|
|
],
|
|
);
|
|
});
|
|
|
|
test('planted red: a named-operation row that enforces no operation is rejected', () => {
|
|
assert.deepEqual(
|
|
cutoverRowDefects({
|
|
...PLANTED_ROW,
|
|
singularExecution: {
|
|
routes: ['handlePlantedCommand'],
|
|
} as unknown as (typeof PLANTED_ROW)['singularExecution'],
|
|
}),
|
|
['names its operations but enforces none of them exactly once'],
|
|
);
|
|
});
|
|
|
|
test('planted red: a named-operation row that enforces only some operations is rejected', () => {
|
|
assert.deepEqual(
|
|
cutoverRowDefects({
|
|
...PLANTED_ROW,
|
|
operations: { names: ['plantedDump', 'plantedReattach'] },
|
|
singularExecution: {
|
|
routes: ['handlePlantedCommand'],
|
|
operations: ['plantedDump'],
|
|
operationOwners: {
|
|
plantedDump: ['handlePlantedCommand'],
|
|
plantedReattach: ['handlePlantedCommand'],
|
|
},
|
|
},
|
|
}),
|
|
['names operations it does not enforce exactly once: plantedReattach'],
|
|
);
|
|
});
|
|
|
|
test('planted red: a row that enforces an operation it does not name is rejected', () => {
|
|
assert.deepEqual(
|
|
cutoverRowDefects({
|
|
...PLANTED_ROW,
|
|
operations: { names: ['plantedDump'] },
|
|
singularExecution: {
|
|
routes: ['handlePlantedCommand'],
|
|
operations: ['plantedDump', 'somebodyElsesOperation'],
|
|
operationOwners: { plantedDump: ['handlePlantedCommand'] },
|
|
},
|
|
}),
|
|
['enforces operations it does not name: somebodyElsesOperation'],
|
|
);
|
|
});
|
|
|
|
test('planted red: duplicate operations on either side are rejected', () => {
|
|
assert.deepEqual(
|
|
cutoverRowDefects({
|
|
...PLANTED_ROW,
|
|
operations: { names: ['plantedDump', 'plantedDump'] },
|
|
singularExecution: {
|
|
routes: ['handlePlantedCommand'],
|
|
operations: ['plantedDump', 'plantedDump'],
|
|
operationOwners: { plantedDump: ['handlePlantedCommand'] },
|
|
},
|
|
}),
|
|
['names duplicate operations: plantedDump', 'enforces duplicate operations: plantedDump'],
|
|
);
|
|
});
|
|
|
|
test('planted red: every named operation declares its lexical owner', () => {
|
|
assert.deepEqual(
|
|
cutoverRowDefects({
|
|
...PLANTED_ROW,
|
|
singularExecution: {
|
|
routes: ['handlePlantedCommand'],
|
|
operations: ['plantedDump'],
|
|
operationOwners: {},
|
|
},
|
|
}),
|
|
['names operations without lexical owners: plantedDump'],
|
|
);
|
|
});
|
|
|
|
test('unrelated daemon calls neither satisfy nor duplicate an owner-scoped operation', () => {
|
|
const missing = summariesFor(
|
|
PLANTED_RULE,
|
|
[
|
|
[
|
|
'src/daemon/planted-handler.ts',
|
|
`
|
|
function handlePlantedCommand() {}
|
|
function handleUnrelatedCommand() { runtime.operations.plantedDump(input); }
|
|
handlePlantedCommand(request);
|
|
`,
|
|
],
|
|
],
|
|
[PLANTED_ROW],
|
|
);
|
|
assert.deepEqual(missing, ['(planted runtime): expected one narrowed plantedDump call, found 0']);
|
|
|
|
const present = summariesFor(
|
|
PLANTED_RULE,
|
|
[
|
|
[
|
|
'src/daemon/planted-handler.ts',
|
|
`
|
|
function handlePlantedCommand() { runtime.operations.plantedDump(input); }
|
|
function handleUnrelatedCommand() { runtime.operations.plantedDump(input); }
|
|
handlePlantedCommand(request);
|
|
`,
|
|
],
|
|
],
|
|
[PLANTED_ROW],
|
|
);
|
|
assert.deepEqual(present, []);
|
|
});
|
|
|
|
test('a pattern-only row may prove singularity through its route alone', () => {
|
|
assert.deepEqual(cutoverRowDefects(rowFor('logs')), []);
|
|
});
|
|
|
|
test('planted red: a durable row without a lifecycle proof is rejected', () => {
|
|
assert.deepEqual(cutoverRowDefects({ ...PLANTED_ROW, tier: 'durable-resource' }), [
|
|
'is durable-resource tier but declares no lifecycle proof',
|
|
]);
|
|
});
|
|
|
|
test('planted red: a request-scoped row claiming durable machinery is rejected', () => {
|
|
assert.deepEqual(cutoverRowDefects({ ...PLANTED_ROW, lifecycleProof: () => [] }), [
|
|
'is request-scoped tier but declares a durable lifecycle proof',
|
|
]);
|
|
});
|
|
|
|
test('every shipped row states its claims', () => {
|
|
assert.deepEqual(
|
|
MIGRATED_COMMAND_CUTOVERS.flatMap((row) =>
|
|
cutoverRowDefects(row).map((defect) => `${row.command}: ${defect}`),
|
|
),
|
|
[],
|
|
);
|
|
});
|