Files
callstack__agent-device/scripts/layering/runtime-command-cutover-descriptor.ts
Michał Pierzchała d8a7d03faf refactor: route application lifecycle through runtime facts (#1759)
* refactor: route application lifecycle through runtime facts

Moves the canonical `open`, `prepare`, `close` and internal `runtime` descriptors
behind package-owned lifecycle bindings admitted from device runtime facts, while
daemon request/session policy and public response construction stay put.

Based on main, which already carries the boot unit, the parametrized cutover gate
and the apps unit. Readiness is package-owned there, so the Apple and Android
bindings call ensureAppleReady/ensureAndroidReady rather than a root readiness
bag; ensureAppleReady gained an onColdBootStart hook so open keeps warming the
runner cache in parallel with a cold boot, and a narrow markBooted port publishes
readiness' fresh observation so a flow still makes one simctl listing.

Cutover rows take R24-R27, clear of the accepted catalog and the sibling install
stack, and cutoverTableDefects rejects a duplicate rule id.

Two defects this unit introduced are fixed here rather than shipped:
`open <app> <url>` dropped the URL on a first open, and test-IME activation was
first fatal on an unobtainable helper and then over-caught. Helper unavailability
is a typed non-activation outcome now; fence, lock and post-record failures
propagate.

The duplication the unit had accumulated is gone: one runtime-admission module
instead of five per-command copies, one direct-lifecycle binding factory instead
of six hand-rolled packages, one transport-hint predicate, one session
finalization path, and no identity-wrapper module.

* fix: allocate lifecycle cutover rows after deployment

* chore: preserve lifecycle union reconstruction

* fix: reconcile lifecycle runtime stack

* refactor: tighten lifecycle runtime topology

* refactor: remove superseded runtime adapters

* fix: preserve stacked runtime cutovers

* test: preserve migrated runtime ownership

* test: move Android deployment retry ownership

* test: extract runtime hint fixtures

* fix: preserve lifecycle stack invariants

* fix: complete lifecycle runtime cutover

* fix: remove lifecycle cutover residue
2026-08-16 15:13:10 +02:00

67 lines
2.3 KiB
TypeScript

import { parseSync } from 'oxc-parser';
import { propertyName, visitAst } from './cutover-policy-ast.ts';
import { lineOf, type CutoverAstNode } from './runtime-command-cutover-ast.ts';
import type { UnruledViolation } from './runtime-command-cutover-model.ts';
const COMMAND_DESCRIPTOR_MODULE = 'src/core/command-descriptor/registry.ts';
/** A device-runtime cutover must remove the command from the legacy dispatch projection. */
export function retiredDispatchProjectionViolations(
sources: ReadonlyMap<string, string>,
command: string,
): UnruledViolation[] {
const source = sources.get(COMMAND_DESCRIPTOR_MODULE);
if (source === undefined) {
return [
{
file: COMMAND_DESCRIPTOR_MODULE,
line: 1,
message: `${command} command descriptor module is missing`,
},
];
}
const program = parseSync(COMMAND_DESCRIPTOR_MODULE, source).program as CutoverAstNode;
let descriptor: CutoverAstNode | undefined;
visitAst(program, (node) => {
if (node.type !== 'ObjectExpression') return;
const properties = objectProperties(node);
const name = properties.find((property) => propertyName(property.key) === 'name');
if (literalString(name?.value) !== command) return;
if (!properties.some((property) => propertyName(property.key) === 'platformExecution')) return;
descriptor = node;
});
if (descriptor === undefined) {
return [
{
file: COMMAND_DESCRIPTOR_MODULE,
line: 1,
message: `${command} command descriptor is missing`,
},
];
}
const dispatch = objectProperties(descriptor).find(
(property) => propertyName(property.key) === 'dispatch',
);
return dispatch === undefined
? []
: [
{
file: COMMAND_DESCRIPTOR_MODULE,
line: lineOf(source, dispatch),
message: `${command} command still projects into the retired legacy dispatcher`,
},
];
}
function objectProperties(node: CutoverAstNode): CutoverAstNode[] {
return Array.isArray(node.properties) ? (node.properties as CutoverAstNode[]) : [];
}
function literalString(node: unknown): string | undefined {
if (node === null || typeof node !== 'object') return undefined;
const candidate = node as CutoverAstNode;
return candidate.type === 'Literal' && typeof candidate.value === 'string'
? candidate.value
: undefined;
}