Files
callstack__agent-device/scripts/layering/contracts-implementation-policy.ts
Michał Pierzchała fd4ab84166 refactor(ios): define snapshot acquisition and presentation contracts (#2203)
* refactor(ios): define snapshot acquisition and presentation contracts

* refactor(ios): isolate snapshot planning exports
2026-09-01 11:33:18 +02:00

207 lines
7.8 KiB
TypeScript

import { parseSync } from 'oxc-parser';
import type { LayeringViolation } from './model.ts';
export type ContractsProductionSource = Readonly<{ path: string; source: string }>;
const RULE = 'R18 contracts-implementation-authority';
const FORBIDDEN_HOST_MODULES = /^(?:node:)?(?:child_process|fs|timers)(?:\/|$)/;
const FORBIDDEN_TIMER_CALLS = new Set([
'clearImmediate',
'clearInterval',
'clearTimeout',
'setImmediate',
'setInterval',
'setTimeout',
]);
const IOS_SNAPSHOT_CONTRACT = 'packages/contracts/src/ios-snapshot.ts';
/** Contracts owns vocabulary. Host/process/timer mechanics belong in capture-kit or an adapter. */
export function contractsImplementationAuthorityViolations(
sources: readonly ContractsProductionSource[],
): LayeringViolation[] {
const violations: LayeringViolation[] = [];
for (const file of sources) {
if (!isContractsProduction(file.path)) continue;
const parsed = parseSync(file.path, file.source);
const networkTrafficViolation = networkTrafficImplementationViolation(
file.path,
file.source,
parsed.program.body,
);
if (file.path === 'packages/contracts/src/interaction-outcome.ts') {
violations.push(
violation(
file.path,
1,
'contracts may not own mutable interaction-outcome lifecycle; that WeakMap identity map belongs in src/core',
),
);
}
if (file.path === 'packages/contracts/src/snapshot-quality-warnings.ts') {
violations.push(
violation(
file.path,
1,
'contracts may not own snapshot quality warning rendering; that presentation policy belongs in src/snapshot/snapshot-presentation',
),
);
}
if (networkTrafficViolation) violations.push(networkTrafficViolation);
const iosSnapshotViolation = iosSnapshotContractViolation(file.path, file.source, parsed);
if (iosSnapshotViolation) violations.push(iosSnapshotViolation);
for (const site of moduleSpecifiers(parsed.module, file.source)) {
if (!FORBIDDEN_HOST_MODULES.test(site.spec)) continue;
violations.push(
violation(
file.path,
site.line,
`contracts imports host implementation authority '${site.spec}'; move mechanics to @agent-device/capture-kit`,
),
);
}
visit(parsed.program, (node) => {
if (node.type !== 'CallExpression') return;
const timerName = timerCallName(node.callee);
if (!timerName) return;
violations.push(
violation(
file.path,
lineAt(file.source, Number(node.start ?? 0)),
`contracts calls timer primitive '${timerName}'; move lifecycle mechanics to @agent-device/capture-kit`,
),
);
});
}
return violations;
}
function iosSnapshotContractViolation(
file: string,
source: string,
parsed: ReturnType<typeof parseSync>,
): LayeringViolation | undefined {
if (file !== IOS_SNAPSHOT_CONTRACT) return undefined;
const implementation = parsed.program.body.find((statement) => !isTypeOnlyStatement(statement));
if (implementation && typeof implementation === 'object') {
return violation(
file,
lineAt(source, Number((implementation as Record<string, unknown>).start ?? 0)),
'iOS snapshot contracts own typed vocabulary only; planning algorithms and provider/lifecycle implementations cannot enter contracts',
);
}
const disallowedImport = moduleSpecifiers(parsed.module, source).find(
({ spec }) => !spec.startsWith('@agent-device/kernel/'),
);
if (!disallowedImport) return undefined;
return violation(
file,
disallowedImport.line,
`iOS snapshot contracts may import only kernel vocabulary; '${disallowedImport.spec}' would bring planning algorithms or provider/lifecycle implementation into contracts`,
);
}
function networkTrafficImplementationViolation(
file: string,
source: string,
body: readonly unknown[],
): LayeringViolation | undefined {
if (!file.startsWith('packages/contracts/src/network-traffic')) return undefined;
if (file !== 'packages/contracts/src/network-traffic.ts') {
return violation(
file,
1,
'contracts may own only the neutral network-traffic vocabulary; parser modules belong in @agent-device/capture-kit',
);
}
const implementation = body.find((statement) => !isTypeOnlyStatement(statement));
if (!implementation || typeof implementation !== 'object') return undefined;
return violation(
file,
lineAt(source, Number((implementation as Record<string, unknown>).start ?? 0)),
'contracts network-traffic vocabulary contains runtime implementation; move parser mechanics to @agent-device/capture-kit',
);
}
function isTypeOnlyStatement(value: unknown): boolean {
if (value === null || typeof value !== 'object') return false;
const statement = value as Record<string, unknown>;
if (statement.type === 'ImportDeclaration') return statement.importKind === 'type';
if (statement.type === 'TSTypeAliasDeclaration' || statement.type === 'TSInterfaceDeclaration') {
return true;
}
if (statement.type !== 'ExportNamedDeclaration') return false;
if (statement.exportKind === 'type') return true;
const declaration = statement.declaration as Record<string, unknown> | undefined;
return (
declaration?.type === 'TSTypeAliasDeclaration' || declaration?.type === 'TSInterfaceDeclaration'
);
}
function moduleSpecifiers(
module: ReturnType<typeof parseSync>['module'],
source: string,
): ReadonlyArray<{ spec: string; line: number }> {
const sites: Array<{ spec: string; line: number }> = [];
const add = (request: { value?: string; start?: number } | undefined): void => {
if (request?.value)
sites.push({ spec: request.value, line: lineAt(source, request.start ?? 0) });
};
for (const entry of module.staticImports) add(entry.moduleRequest);
for (const entry of module.staticExports) {
for (const exported of entry.entries) add(exported.moduleRequest);
}
for (const entry of module.dynamicImports) {
const raw = source.slice(entry.moduleRequest.start, entry.moduleRequest.end);
const literal = /^(['"])([^'"]*)\1$/.exec(raw);
if (literal) sites.push({ spec: literal[2]!, line: lineAt(source, entry.moduleRequest.start) });
}
return sites;
}
function timerCallName(value: unknown): string | undefined {
if (value === null || typeof value !== 'object') return undefined;
const callee = value as Record<string, unknown>;
if (callee.type === 'Identifier' && FORBIDDEN_TIMER_CALLS.has(String(callee.name))) {
return String(callee.name);
}
if (callee.type !== 'MemberExpression' || callee.computed === true) return undefined;
const object = callee.object as Record<string, unknown> | undefined;
const property = callee.property as Record<string, unknown> | undefined;
if (
object?.type !== 'Identifier' ||
!['global', 'globalThis', 'window'].includes(String(object.name)) ||
property?.type !== 'Identifier' ||
!FORBIDDEN_TIMER_CALLS.has(String(property.name))
) {
return undefined;
}
return String(property.name);
}
function isContractsProduction(file: string): boolean {
return (
file.startsWith('packages/contracts/src/') &&
!file.endsWith('.test.ts') &&
!file.includes('/__tests__/')
);
}
function violation(file: string, line: number, message: string): LayeringViolation {
return { rule: RULE, file, line, message };
}
function lineAt(source: string, offset: number): number {
return source.slice(0, offset).split('\n').length;
}
function visit(node: unknown, callback: (node: Record<string, unknown>) => void): void {
if (node === null || typeof node !== 'object') return;
if (Array.isArray(node)) {
for (const child of node) visit(child, callback);
return;
}
const record = node as Record<string, unknown>;
callback(record);
for (const value of Object.values(record)) visit(value, callback);
}