Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 98 additions & 2 deletions scripts/layering/daemon-platform-runtime-inventory.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ for (const [file, target, symbol] of [
const found = edgeViolations(sources, importer);
assert.equal(found.length, 1);
assert.equal(found[0]!.rule, DAEMON_PLATFORM_RUNTIME_RULE);
assert.match(found[0]!.message, /unclassified daemon-to-root|classified symbols drifted/);
assert.match(found[0]!.message, /unclassified daemon coupling|classified symbols drifted/);
});
}

Expand Down Expand Up @@ -77,7 +77,7 @@ test('R76 rejects an unclassified edge with the pair and its line', () => {
assert.equal(found.length, 1);
assert.equal(found[0]!.rule, DAEMON_PLATFORM_RUNTIME_RULE);
assert.equal(found[0]!.line, 1);
assert.match(found[0]!.message, /unclassified daemon-to-root platform-runtime coupling/);
assert.match(found[0]!.message, /unclassified daemon coupling to platform mechanics/);
assert.match(
found[0]!.message,
/src\/daemon\/fixture\.ts -> src\/platform-runtime-android-tool-host\.ts/,
Expand Down Expand Up @@ -255,3 +255,99 @@ test('the root composition family is src/platform-runtime.ts plus src/platform-r
assert.equal(isRootPlatformRuntimeTarget('src/platforms/runtime.ts'), false);
assert.equal(isRootPlatformRuntimeTarget('src/daemon/platform-runtime.ts'), false);
});

const ROOT_PLATFORM_STUB = 'export const gateway = 1;\nvoid gateway;\n';
const PROVIDER_HUB_STUB =
"import { gateway } from './platform-runtime.ts';\n" +
'void gateway;\n' +
'export function createProviderDeviceRuntimeRequestProviders() {}\n' +
'export function isActiveProviderDevice() { return false; }\n';

test('R76 catches a daemon import of a root hub that reaches the platform family', () => {
const sources = {
'src/platform-runtime.ts': ROOT_PLATFORM_STUB,
'src/hub-runtime.ts':
"import { gateway } from './platform-runtime.ts';\nvoid gateway;\n" +
'export function hubFact() {}\n',
'src/daemon/fixture.ts': "import { hubFact } from '../hub-runtime.ts';\nvoid hubFact;\n",
};
const found = edgeViolations(sources, 'src/daemon/fixture.ts');
assert.equal(found.length, 1);
assert.equal(found[0]!.rule, DAEMON_PLATFORM_RUNTIME_RULE);
assert.match(found[0]!.message, /src\/daemon\/fixture\.ts -> src\/hub-runtime\.ts/);
});

test('R76 accepts the classified provider-runtime hub edge at the composition site', () => {
const sources = {
'src/platform-runtime.ts': ROOT_PLATFORM_STUB,
'src/provider-device-runtime.ts': PROVIDER_HUB_STUB,
'src/daemon/server/daemon-runtime.ts':
'import {\n' +
' createProviderDeviceRuntimeRequestProviders,\n' +
' isActiveProviderDevice,\n' +
"} from '../../provider-device-runtime.ts';\n" +
'void [createProviderDeviceRuntimeRequestProviders, isActiveProviderDevice];\n',
};
assert.deepEqual(edgeViolations(sources, 'src/daemon/server/daemon-runtime.ts'), []);
});

test('R76 catches a symbol added to the classified provider-runtime hub edge', () => {
const sources = {
'src/platform-runtime.ts': ROOT_PLATFORM_STUB,
'src/provider-device-runtime.ts': `${PROVIDER_HUB_STUB}export function setProviderDevice() {}\n`,
'src/daemon/server/daemon-runtime.ts':
'import {\n' +
' createProviderDeviceRuntimeRequestProviders,\n' +
' isActiveProviderDevice,\n' +
' setProviderDevice,\n' +
"} from '../../provider-device-runtime.ts';\n" +
'void [createProviderDeviceRuntimeRequestProviders, isActiveProviderDevice, setProviderDevice];\n',
};
const found = edgeViolations(sources, 'src/daemon/server/daemon-runtime.ts');
assert.equal(found.length, 1);
assert.match(found[0]!.message, /classified symbols drifted/);
assert.match(found[0]!.message, /setProviderDevice/);
});

test('R76 classifies the dynamic interactor lookup instead of skipping it', () => {
const sources = {
'src/platform-runtime.ts': ROOT_PLATFORM_STUB,
'src/provider-device-runtime.ts': PROVIDER_HUB_STUB,
'src/core/interactors.ts':
"import { getProviderDeviceInteractor, isActiveProviderDevice } from '../provider-device-runtime.ts';\n" +
'void [getProviderDeviceInteractor, isActiveProviderDevice];\n' +
'export function getInteractor(device: unknown) {}\n',
'src/daemon/snapshot-interactor-capture.ts':
"const { getInteractor } = await import('../core/interactors.ts');\nvoid getInteractor;\n",
};
assert.deepEqual(edgeViolations(sources, 'src/daemon/snapshot-interactor-capture.ts'), []);
});

test('R76 catches a binding added to the dynamic interactor lookup', () => {
const sources = {
'src/platform-runtime.ts': ROOT_PLATFORM_STUB,
'src/provider-device-runtime.ts': PROVIDER_HUB_STUB,
'src/core/interactors.ts':
"import { isActiveProviderDevice } from '../provider-device-runtime.ts';\n" +
'void isActiveProviderDevice;\n' +
'export function getInteractor(device: unknown) {}\n' +
'export function getRetryInteractor(device: unknown) {}\n',
'src/daemon/snapshot-interactor-capture.ts':
"const { getInteractor, getRetryInteractor } = await import('../core/interactors.ts');\n" +
'void [getInteractor, getRetryInteractor];\n',
};
const found = edgeViolations(sources, 'src/daemon/snapshot-interactor-capture.ts');
assert.equal(found.length, 1);
assert.equal(found[0]!.rule, DAEMON_PLATFORM_RUNTIME_RULE);
assert.match(found[0]!.message, /classified symbols drifted/);
});

test('R76 does not turn an intra-daemon hop into a classified hub edge', () => {
const sources = {
'src/platform-runtime.ts': ROOT_PLATFORM_STUB,
'src/daemon/dispatch.ts':
"import { gateway } from '../platform-runtime.ts';\nvoid gateway;\nexport function dispatch() {}\n",
'src/daemon/handler.ts': "import { dispatch } from './dispatch.ts';\nvoid dispatch;\n",
};
assert.deepEqual(edgeViolations(sources, 'src/daemon/handler.ts'), []);
});
96 changes: 86 additions & 10 deletions scripts/layering/daemon-platform-runtime-inventory.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { isProductionSourceFile } from './tracked-sources.ts';
import type { LayeringViolation, ResolvedImportEdge } from './model.ts';

// The classified inventory of every production daemon import of a root platform-runtime
// composition module (#2278, ADR 0022). R65 already bans daemon imports of concrete platform
// packages and the retired src/platforms zone; the root src/platform-runtime*.ts family is the
// one composition layer the daemon may still touch, and this table is the classification of
// every edge that does. An edge is unclassifiable until it is recorded here with a rationale,
// and a recorded edge that no longer exists is stale — both fail, so the inventory and the
// The classified inventory of every production daemon import that reaches the root platform-runtime
// composition modules (#2278, ADR 0022, #2542). R65 already bans daemon imports of concrete platform
// packages and the retired src/platforms zone; the composition layer is the one place the daemon may
// still touch, and this table is the classification of every edge that reaches it — either directly
// into the src/platform-runtime*.ts family, or into any module outside the daemon zone that imports
// that family itself, statically or dynamically. A root hub such as src/provider-device-runtime.ts is
// named by neither file pattern nor zone, so a daemon import of it used to be invisible here while
// carrying the same platform mechanics. An edge is unclassifiable until it is recorded here with a
// rationale, and a recorded edge that no longer exists is stale — both fail, so the inventory and the
// tree cannot drift apart in either direction.

export const DAEMON_PLATFORM_RUNTIME_RULE = 'R76 daemon-platform-runtime-inventory';
Expand Down Expand Up @@ -38,6 +41,41 @@ export function isRootPlatformRuntimeTarget(target: string): boolean {
return /^src\/platform-runtime(?:\.ts|-[a-z0-9-]+\.ts)$/.test(target);
}

/**
* Every module outside the daemon zone through which a daemon import can reach the root
* platform-runtime family: the family itself, plus anything that imports it, transitively, over
* static and dynamic edges alike. Computed from the tree rather than patterned so a root hub that
* carries platform mechanics without naming them gains no immunity from its filename, and so the
* one dynamic edge out of the daemon zone is classified like the static ones (#2542).
*/
export function computePlatformMechanicsHubs(
edges: readonly ResolvedImportEdge[],
): ReadonlySet<string> {
const importersByTarget = new Map<string, string[]>();
const roots: string[] = [];
for (const edge of edges) {
const importers = importersByTarget.get(edge.target);
if (importers === undefined) importersByTarget.set(edge.target, [edge.file]);
else importers.push(edge.file);
if (isRootPlatformRuntimeTarget(edge.target) && !roots.includes(edge.target)) {
roots.push(edge.target);
}
}

const hubs = new Set<string>(roots);
const seen = new Set<string>(roots);
const frontier = [...roots];
while (frontier.length > 0) {
for (const importer of importersByTarget.get(frontier.pop()!) ?? []) {
if (seen.has(importer)) continue;
seen.add(importer);
frontier.push(importer);
if (!importer.startsWith('src/daemon/')) hubs.add(importer);
}
}
return hubs;
}

/**
* The current classified edges from the #2278 audit, ratcheted as owning interfaces land.
*/
Expand Down Expand Up @@ -159,6 +197,39 @@ export const DAEMON_PLATFORM_RUNTIME_EDGES: readonly DaemonPlatformRuntimeEdge[]
'moved behind the Android owning seam in packages/platform-android, so the resolver is the ' +
'only symbol this edge names.',
},
{
file: 'src/daemon/snapshot-interactor-capture.ts',
target: 'src/core/interactors.ts',
symbols: ['getInteractor'],
classification: 'leaked-platform-mechanics',
rationale:
'the snapshot capture looks an interactor up through a dynamic import of the shared ' +
'interactor lookup, the one production edge that leaves the daemon zone dynamically and so ' +
'the one the ranked spine never sees; interactor resolution behind a capability bound at ' +
'root composition is the accepted deepening (#2555).',
deepenedBy: '#2555',
},
{
file: 'src/daemon/server/daemon-runtime.ts',
target: 'src/provider-device-runtimes.ts',
symbols: ['createDefaultProviderRuntimeComposition', 'DEFAULT_PROVIDER_RUNTIME_REQUIRED_IDS'],
classification: 'composition-essential',
rationale:
'process-root assembly of the default provider runtime composition and the ids whose runtime ' +
'must be present for a request, which is what makes a cloud or remote lease holder visible to ' +
'the daemon at all; the daemon holds no provider mechanics at this site.',
},
{
file: 'src/daemon/server/daemon-runtime.ts',
target: 'src/provider-device-runtime.ts',
symbols: ['createProviderDeviceRuntimeRequestProviders', 'isActiveProviderDevice'],
classification: 'composition-essential',
rationale:
'process-root assembly of the request-scoped provider runtime providers, and the one site that ' +
'hands the resulting device-ownership fact to the daemon through its own typed admission seam ' +
'(#2541); the ten leaf call sites that used to name this hub now read ' +
'src/daemon/provider-device-admission.ts, so this edge is the composition and nothing else.',
},
{
file: 'src/daemon/session-lifecycle/internal/session-open-prepare.ts',
target: 'src/platform-runtime-open-target.ts',
Expand All @@ -181,13 +252,17 @@ function sorted(symbols: readonly string[]): string[] {
}

/**
* Catches: unclassified daemon-to-root platform-runtime coupling regrowing — a new edge (or a
* Catches: unclassified daemon-to-platform-mechanics coupling regrowing — a new edge (or a
* new symbol on an existing edge) that the #2278 audit never classified, the mirror failure, a
* classified edge that no longer exists and would silently admit its return, and dynamic
* imports whose binding set the inventory cannot name: a rest or computed destructure binding,
* or a namespace/side-effect import() call exposed alongside (or instead of) the named ones.
* Reaching the platform through a root hub is caught the same way as reaching it directly, and a
* dynamic edge is classified rather than skipped, so the direction the ranked spine cannot see is
* at least named here (#2542).
* Evidence: #2278 measured 14 production edges in 9 daemon files at origin/main 6e22e266d7;
* this table is that measurement, classified per ADR 0022.
* this table is that measurement, classified per ADR 0022, plus the 3 edges the hub widening and
* dynamic-edge pass made visible at #2541 (2 provider-runtime hubs, 1 dynamic interactor lookup).
* Cost: attributed to the R76 rule registration in check.ts; not a standalone CI job.
* Kill criterion: the daemon reaches the platform only through the gateway and declared
* contract capabilities (the inventory empty), or a maintainer decision retires the
Expand All @@ -196,14 +271,15 @@ function sorted(symbols: readonly string[]): string[] {
export function checkDaemonPlatformRuntimeInventory(
edges: readonly ResolvedImportEdge[],
): LayeringViolation[] {
const hubs = computePlatformMechanicsHubs(edges);
const actual = new Map<
string,
{ line: number; symbols: Set<string>; residue: boolean; openEnded: boolean }
>();
for (const edge of edges) {
if (!edge.file.startsWith('src/daemon/')) continue;
if (!isProductionSourceFile(edge.file)) continue;
if (!isRootPlatformRuntimeTarget(edge.target)) continue;
if (!hubs.has(edge.target)) continue;
const key = keyOf(edge.file, edge.target);
const entry = actual.get(key) ?? {
line: edge.line,
Expand Down Expand Up @@ -234,7 +310,7 @@ export function checkDaemonPlatformRuntimeInventory(
file: key.split(' -> ')[0]!,
line: entry.line,
message:
`unclassified daemon-to-root platform-runtime coupling: ${key}. Classify it in ` +
`unclassified daemon coupling to platform mechanics: ${key}. Classify it in ` +
`DAEMON_PLATFORM_RUNTIME_EDGES (${DAEMON_PLATFORM_RUNTIME_RULE}) with its rationale, ` +
`or remove the coupling.`,
});
Expand Down
Loading