Files
callstack__agent-device/scripts/layering/substrate-domain-shape.ts
Michał Pierzchała ddb415a2c7 refactor: sink package-closed src modules into existing packages (#2106)
* refactor: sink package-closed src modules into existing packages

Move closed modules into contracts, kernel, capture-kit, and ad-script,
and declare DaemonCommandDescriptor in core so R6/R9 can pin the remaining
provider-webdriver type cycle.

Co-authored-by: Cursor <cursoragent@cursor.com>

* refactor: keep contracts and capture-kit off generic sinks

Move interaction-outcome, snapshot warning rendering, and inventory ALS
behind focused owners, and plant R18/R70 domain-shape gates so they
cannot return as package export-map growth.

Co-authored-by: Cursor <cursoragent@cursor.com>

* refactor: drop moved implementation comments from owner modules

Names, types, and tests already carry those invariants; the relocated
files should not keep review-history or control-flow narration.

Co-authored-by: Cursor <cursoragent@cursor.com>

* refactor: drop the empty snapshot-quality layering zone

W1 moved the verdict into capture-kit and this PR moved warning rendering
into snapshot-presentation, so the ranked zone no longer has production files.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 07:56:18 +02:00

111 lines
3.9 KiB
TypeScript

import { parseSync } from 'oxc-parser';
import type { LayeringViolation } from './model.ts';
export const SUBSTRATE_DOMAIN_SHAPE_RULE = 'R70 substrate-domain-shape';
export type SubstrateDomainSource = Readonly<{ path: string; source: string }>;
const ASYNC_HOOKS = /^(?:node:)?async_hooks(?:\/|$)/;
/** Capture-kit is durable capture; request ALS and retired src/contracts/ stay out of substrate packages. */
export function substrateDomainShapeViolations(
sources: readonly SubstrateDomainSource[],
): LayeringViolation[] {
const violations: LayeringViolation[] = [];
for (const file of sources) {
if (isRetiredContractsRoot(file.path)) {
violations.push(
violation(
file.path,
1,
'src/contracts/ is retired; vocabulary lives in packages/contracts, and executable policy belongs to its owning domain',
),
);
continue;
}
if (!isCaptureKitProduction(file.path)) continue;
const parsed = parseSync(file.path, file.source);
for (const site of moduleSpecifiers(parsed.module, file.source)) {
if (!ASYNC_HOOKS.test(site.spec)) continue;
violations.push(
violation(
file.path,
site.line,
`capture-kit imports '${site.spec}'; request-scoped AsyncLocalStorage dispatch belongs in src/request`,
),
);
}
visit(parsed.program, (node) => {
if (!isAsyncLocalStorageConstruction(node)) return;
violations.push(
violation(
file.path,
lineAt(file.source, Number(node.start ?? 0)),
'capture-kit constructs AsyncLocalStorage; request-scoped dispatch belongs in src/request',
),
);
});
}
return violations;
}
function isRetiredContractsRoot(file: string): boolean {
return file.startsWith('src/contracts/') && file.endsWith('.ts');
}
function isCaptureKitProduction(file: string): boolean {
return (
file.startsWith('packages/capture-kit/src/') &&
!file.endsWith('.test.ts') &&
!file.includes('/__tests__/')
);
}
function isAsyncLocalStorageConstruction(node: Record<string, unknown>): boolean {
if (node.type !== 'NewExpression') return false;
const callee = node.callee;
if (callee === null || typeof callee !== 'object') return false;
const expression = callee as Record<string, unknown>;
return expression.type === 'Identifier' && expression.name === 'AsyncLocalStorage';
}
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 violation(file: string, line: number, message: string): LayeringViolation {
return { rule: SUBSTRATE_DOMAIN_SHAPE_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);
}