Files
callstack__agent-device/scripts/layering/platform-composition-policy.ts
Michał Pierzchała 39cd4d346a refactor: route appstate through platform runtime (#1755)
* refactor: route appstate through platform runtime

* test: keep appstate capability fixture below complexity limit

* test: cover appstate required readiness fact

* fix: align appstate facts with boot readiness

* fix: keep appstate use declaration minimal

* fix: close appstate parity and ownership gaps

* docs: record final appstate size accounting

* fix: merge neutral runtime imports

* docs: align final appstate size totals

* fix: remove stale runtime dependency edges

* docs: correct appstate size accounting

* refactor: keep runtime-use factory internal

* docs: itemize runtime-use relocation

* fix: move appstate queries into runtime packages

* refactor: retire root foreground query paths

* refactor: share Android foreground parser ownership

* fix: preserve Android appstate parser precedence

* docs: keep appstate evidence in review artifacts

* fix: keep appstate runtime loading lazy

* fix: fail closed for stale limrun appstate

* fix: preserve limrun recovery and abort appstate

* fix: narrow limrun exact-owner recovery

* fix: allocate appstate cutover rule

* fix: reconcile appstate with merged main

* style: format harmony runtime test

* fix: allocate appstate rule id

* fix: allocate appstate layering rule

* fix: remove stale app command admissions

* fix: close appstate layering regressions

* fix: align Harmony capability parity with runtime facts

* test: cover limrun recovery-only readiness

* fix: keep Limrun recovery binding app-log only

* fix: parse Android app state in linear time
2026-08-12 16:55:50 +02:00

101 lines
3.6 KiB
TypeScript

import { parseSync } from 'oxc-parser';
import { parseImports, type LayeringViolation } from './model.ts';
const COMPOSITION_FILE = 'src/platform-runtime.ts';
const RULE = 'R13 platform-package-substrate';
function violation(line: number, message: string): LayeringViolation {
return { rule: RULE, file: COMPOSITION_FILE, line, message };
}
function lineOf(source: string, offset: number): number {
return source.slice(0, offset).split('\n').length;
}
function memberPropertyName(node: unknown): string | undefined {
if (node === null || typeof node !== 'object') return undefined;
const record = node as Record<string, unknown>;
if (record['type'] === 'ChainExpression') return memberPropertyName(record['expression']);
if (record['type'] !== 'MemberExpression') return undefined;
const property = record['property'] as Record<string, unknown> | undefined;
if (property?.['type'] === 'Identifier') return property['name'] as string | undefined;
const value = property?.['value'];
return typeof value === 'string' ? value : undefined;
}
function isFunction(node: Record<string, unknown>): boolean {
return (
node['type'] === 'FunctionDeclaration' ||
node['type'] === 'FunctionExpression' ||
node['type'] === 'ArrowFunctionExpression'
);
}
function eagerImplementationLoaderSites(source: string): number[] {
const sites: number[] = [];
const visit = (node: unknown): void => {
if (node === null || typeof node !== 'object') return;
if (Array.isArray(node)) {
for (const child of node) visit(child);
return;
}
const record = node as Record<string, unknown>;
if (isFunction(record)) return;
if (
record['type'] === 'CallExpression' &&
['loadInventory', 'loadRuntime'].includes(memberPropertyName(record['callee']) ?? '')
) {
sites.push(lineOf(source, (record['start'] as number | undefined) ?? 0));
return;
}
for (const value of Object.values(record)) visit(value);
};
visit(parseSync(COMPOSITION_FILE, source).program);
return sites;
}
function isAllowedCompositionImport(specifier: string): boolean {
return (
/^@agent-device\/contracts(?:\/|$)/.test(specifier) ||
/^@agent-device\/platform-[^/]+$/.test(specifier) ||
specifier === './platform-runtime-gateway.ts' ||
specifier === './platform-runtime-operation-host.ts' ||
specifier === './platform-runtime-app-state-host.ts' ||
specifier === './platform-runtime-device-inventory.ts' ||
specifier === './platform-runtime-host.ts' ||
specifier.startsWith('./platform-runtime-host/')
);
}
export function checkPlatformComposition(source: string | undefined): LayeringViolation[] {
if (source === undefined) {
return [violation(1, 'the exact platform composition root is missing')];
}
const violations: LayeringViolation[] = [];
for (const site of parseImports(source)) {
if (!isAllowedCompositionImport(site.spec)) {
violations.push(
violation(
site.line,
`composition imports only runtime contracts, host-capability adapters, and concrete platform facades; found '${site.spec}'`,
),
);
}
if (/^@agent-device\/platform-/.test(site.spec) && (site.dynamic || site.typeOnly)) {
violations.push(
violation(site.line, 'platform inventory modules must be statically composed'),
);
}
}
for (const line of eagerImplementationLoaderSites(source)) {
violations.push(
violation(
line,
'composition may not invoke a platform implementation loader before selected use',
),
);
}
return violations;
}