Skip to content

Commit 7584bf2

Browse files
committed
chore(gates): classify the claim-recovery gateway composition edge
R76 inventory gains the daemon-runtime -> platform-runtime-claim-recovery edge and retires the superseded device-claim-owner-recovery -> platform-runtime entry; the root-module zone list and planted-violation fixtures follow.
1 parent d0c47f8 commit 7584bf2

3 files changed

Lines changed: 57 additions & 116 deletions

File tree

‎scripts/layering/daemon-platform-runtime-inventory.test.ts‎

Lines changed: 37 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ import {
88
isRootPlatformRuntimeTarget,
99
} from './daemon-platform-runtime-inventory.ts';
1010

11-
const DEVICE_READY_TARGET = 'src/platform-runtime-device-ready.ts';
12-
const DEVICE_READY_STUB =
13-
'export async function ensureLocalPlatformDeviceReady(device: unknown) { return false; }\n';
11+
// The classified pair these fixtures plant is the claim-recovery gateway factory: the composition
12+
// edge this port introduced, and the shortest one whose symbols the rule still classifies.
13+
const CLASSIFIED_TARGET = 'src/platform-runtime-claim-recovery.ts';
14+
const CLASSIFIED_STUB = 'export const createClaimRecoveryGateway = () => ({});\n';
15+
const CLASSIFIED_IMPORTER = 'src/daemon/server/daemon-runtime.ts';
1416

1517
function violations(sources: Record<string, string>): LayeringViolation[] {
1618
return checkDaemonPlatformRuntimeInventory(resolveImportEdges(new Map(Object.entries(sources))));
@@ -22,12 +24,12 @@ function edgeViolations(sources: Record<string, string>, file: string): Layering
2224

2325
test('R76 accepts a classified edge with the exact recorded symbols', () => {
2426
const sources = {
25-
[DEVICE_READY_TARGET]: DEVICE_READY_STUB,
26-
'src/daemon/device/device-ready.ts':
27-
"import { ensureLocalPlatformDeviceReady } from '../../platform-runtime-device-ready.ts';\n" +
28-
'void ensureLocalPlatformDeviceReady;\n',
27+
[CLASSIFIED_TARGET]: CLASSIFIED_STUB,
28+
[CLASSIFIED_IMPORTER]:
29+
"import { createClaimRecoveryGateway } from '../../platform-runtime-claim-recovery.ts';\n" +
30+
'void createClaimRecoveryGateway;\n',
2931
};
30-
assert.deepEqual(edgeViolations(sources, 'src/daemon/device/device-ready.ts'), []);
32+
assert.deepEqual(edgeViolations(sources, CLASSIFIED_IMPORTER), []);
3133
});
3234

3335
for (const [file, target, symbol] of [
@@ -50,20 +52,18 @@ for (const [file, target, symbol] of [
5052

5153
test('R76 reports every classified edge missing from the tree as stale, not the other way around', () => {
5254
const sources = {
53-
[DEVICE_READY_TARGET]: DEVICE_READY_STUB,
54-
'src/daemon/device/device-ready.ts':
55-
"import { ensureLocalPlatformDeviceReady } from '../../platform-runtime-device-ready.ts';\n" +
56-
'void ensureLocalPlatformDeviceReady;\n',
55+
[CLASSIFIED_TARGET]: CLASSIFIED_STUB,
56+
[CLASSIFIED_IMPORTER]:
57+
"import { createClaimRecoveryGateway } from '../../platform-runtime-claim-recovery.ts';\n" +
58+
'void createClaimRecoveryGateway;\n',
5759
};
5860
const stale = violations(sources).filter(
5961
(violation) => violation.file === 'scripts/layering/daemon-platform-runtime-inventory.ts',
6062
);
6163
assert.equal(stale.length, DAEMON_PLATFORM_RUNTIME_EDGES.length - 1);
6264
assert.ok(stale.every((violation) => violation.message.includes('stale classified edge')));
63-
const deviceReadyStale = stale.find((violation) =>
64-
violation.message.includes(DEVICE_READY_TARGET),
65-
);
66-
assert.equal(deviceReadyStale, undefined);
65+
const classifiedStale = stale.find((violation) => violation.message.includes(CLASSIFIED_TARGET));
66+
assert.equal(classifiedStale, undefined);
6767
});
6868

6969
test('R76 rejects an unclassified edge with the pair and its line', () => {
@@ -86,35 +86,32 @@ test('R76 rejects an unclassified edge with the pair and its line', () => {
8686

8787
test('R76 rejects new symbols on a classified edge', () => {
8888
const sources = {
89-
[DEVICE_READY_TARGET]: DEVICE_READY_STUB + 'export function extraReadiness() {}\n',
90-
'src/daemon/device/device-ready.ts':
91-
"import { ensureLocalPlatformDeviceReady, extraReadiness } from '../../platform-runtime-device-ready.ts';\n" +
92-
'void [ensureLocalPlatformDeviceReady, extraReadiness];\n',
89+
[CLASSIFIED_TARGET]: CLASSIFIED_STUB + 'export function extraGatewayKnob() {}\n',
90+
[CLASSIFIED_IMPORTER]:
91+
"import { createClaimRecoveryGateway, extraGatewayKnob } from '../../platform-runtime-claim-recovery.ts';\n" +
92+
'void [createClaimRecoveryGateway, extraGatewayKnob];\n',
9393
};
94-
const found = edgeViolations(sources, 'src/daemon/device/device-ready.ts');
94+
const found = edgeViolations(sources, CLASSIFIED_IMPORTER);
9595
assert.equal(found.length, 1);
9696
assert.equal(found[0]!.rule, DAEMON_PLATFORM_RUNTIME_RULE);
9797
assert.match(found[0]!.message, /classified symbols drifted/);
98-
assert.match(found[0]!.message, /ensureLocalPlatformDeviceReady, extraReadiness/);
98+
assert.match(found[0]!.message, /createClaimRecoveryGateway, extraGatewayKnob/);
9999
});
100100

101-
test('R76 rejects a reintroduced Android-mechanics import on the selector-dispatch edge', () => {
101+
test('R76 rejects a gateway assembled from an expanded recovery edge', () => {
102102
const sources = {
103-
'src/platform-runtime-open-target.ts':
104-
'export async function resolveSessionAppBundleIdForTarget() { return undefined; }\n' +
105-
'export async function resolveAndroidPackageForOpen() { return undefined; }\n',
106-
'src/daemon/handlers/session-selector-dispatch.ts':
107-
"import { resolveAndroidPackageForOpen, resolveSessionAppBundleIdForTarget } from '../../platform-runtime-open-target.ts';\n" +
108-
'void [resolveAndroidPackageForOpen, resolveSessionAppBundleIdForTarget];\n',
103+
'src/platform-runtime-claim-recovery.ts':
104+
'export const createClaimRecoveryGateway = () => ({});\n' +
105+
'export const createProviderRecoveryGateway = () => ({});\n',
106+
'src/daemon/server/daemon-runtime.ts':
107+
"import {\n createClaimRecoveryGateway,\n createProviderRecoveryGateway,\n} from '../../platform-runtime-claim-recovery.ts';\n" +
108+
'void [createClaimRecoveryGateway, createProviderRecoveryGateway];\n',
109109
};
110-
const found = edgeViolations(sources, 'src/daemon/handlers/session-selector-dispatch.ts');
110+
const found = edgeViolations(sources, 'src/daemon/server/daemon-runtime.ts');
111111
assert.equal(found.length, 1);
112112
assert.equal(found[0]!.rule, DAEMON_PLATFORM_RUNTIME_RULE);
113113
assert.match(found[0]!.message, /classified symbols drifted/);
114-
assert.match(
115-
found[0]!.message,
116-
/resolveAndroidPackageForOpen, resolveSessionAppBundleIdForTarget/,
117-
);
114+
assert.match(found[0]!.message, /createClaimRecoveryGateway, createProviderRecoveryGateway/);
118115
});
119116

120117
test('R76 matches a destructured dynamic import by target with the recorded bindings', () => {
@@ -211,13 +208,13 @@ test('R76 rejects a namespace import alongside the recorded named binding on the
211208

212209
test('R76 treats the import and re-export of one classified pair as one entry', () => {
213210
const sources = {
214-
[DEVICE_READY_TARGET]: DEVICE_READY_STUB,
215-
'src/daemon/device/device-ready.ts':
216-
"import { ensureLocalPlatformDeviceReady } from '../../platform-runtime-device-ready.ts';\n" +
217-
"export { ensureLocalPlatformDeviceReady } from '../../platform-runtime-device-ready.ts';\n" +
218-
'void ensureLocalPlatformDeviceReady;\n',
211+
[CLASSIFIED_TARGET]: CLASSIFIED_STUB,
212+
[CLASSIFIED_IMPORTER]:
213+
"import { createClaimRecoveryGateway } from '../../platform-runtime-claim-recovery.ts';\n" +
214+
"export { createClaimRecoveryGateway } from '../../platform-runtime-claim-recovery.ts';\n" +
215+
'void createClaimRecoveryGateway;\n',
219216
};
220-
assert.deepEqual(edgeViolations(sources, 'src/daemon/device/device-ready.ts'), []);
217+
assert.deepEqual(edgeViolations(sources, CLASSIFIED_IMPORTER), []);
221218
});
222219

223220
test('R76 ignores test-shaped and non-daemon importers', () => {

‎scripts/layering/daemon-platform-runtime-inventory.ts‎

Lines changed: 18 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -123,26 +123,6 @@ export const DAEMON_PLATFORM_RUNTIME_EDGES: readonly DaemonPlatformRuntimeEdge[]
123123
'sole place that names the Apple runner owner, the Android snapshot-helper and Web ' +
124124
'orphan cleanups, and legacy app-log marker recovery.',
125125
},
126-
{
127-
file: 'src/daemon/device/device-claim-owner-recovery.ts',
128-
target: 'src/platform-runtime.ts',
129-
symbols: ['createPlatformRuntimeGateway'],
130-
classification: 'composition-essential',
131-
rationale:
132-
"per-transaction neutral gateway assembly scoped to the dead owner's state dir " +
133-
'(#2168); the process root cannot carry a per-claim sessionsDir, so the scoped ' +
134-
"composition belongs to the recovery policy's own module.",
135-
},
136-
{
137-
file: 'src/daemon/device/device-ready.ts',
138-
target: 'src/platform-runtime-device-ready.ts',
139-
symbols: ['ensureLocalPlatformDeviceReady'],
140-
classification: 'composition-essential',
141-
rationale:
142-
'neutral local-device-readiness port assembled at the root composition layer (the ' +
143-
'platform dispatch is internal to the root module); the daemon keeps its TTL cache ' +
144-
'and provider-device policy locally.',
145-
},
146126
{
147127
file: 'src/daemon/direct-ios-selector.ts',
148128
target: 'src/platform-runtime-apple-resources.ts',
@@ -154,54 +134,6 @@ export const DAEMON_PLATFORM_RUNTIME_EDGES: readonly DaemonPlatformRuntimeEdge[]
154134
'audit deliberately adds no second selector producer.',
155135
deepenedBy: '#2273, #2274',
156136
},
157-
{
158-
file: 'src/daemon/ios-app-session-hint.ts',
159-
target: 'src/platform-runtime-apple-resources.ts',
160-
symbols: ['appleSessionObservation'],
161-
classification: 'daemon-policy-essential',
162-
rationale:
163-
'daemon-owned hint composition and length limits consume the neutral foreground-app ' +
164-
'observation; the Apple package owns ambiguity and probe mechanics.',
165-
},
166-
{
167-
file: 'src/daemon/request-recording-health.ts',
168-
target: 'src/platform-runtime-apple-resources.ts',
169-
symbols: ['appleSessionObservation'],
170-
classification: 'daemon-policy-essential',
171-
rationale:
172-
'daemon-owned recording invalidation consumes only liveness and session identity ' +
173-
'through the neutral observation contract; runner mechanics stay Apple-owned.',
174-
},
175-
{
176-
file: 'src/daemon/session-device-resolution.ts',
177-
target: 'src/platform-runtime-apple-resources.ts',
178-
symbols: ['appleSessionObservation'],
179-
classification: 'daemon-policy-essential',
180-
rationale:
181-
'daemon-owned device refresh uses the neutral runner-session observation as boot ' +
182-
'evidence; inventory selection and provider exclusions remain local policy.',
183-
},
184-
{
185-
file: 'src/daemon/session-lifecycle/internal/session-open-execution.ts',
186-
target: 'src/platform-runtime-device-boot.ts',
187-
symbols: ['deviceBootObservation'],
188-
classification: 'daemon-policy-essential',
189-
rationale:
190-
'daemon-owned claim reconciliation asks the device when it last booted to decide whether a ' +
191-
'foreign claim can still describe live ownership; the per-family probe mechanics stay in the ' +
192-
'Apple and Android packages behind the neutral observation contract (#2538).',
193-
},
194-
{
195-
file: 'src/daemon/handlers/session-selector-dispatch.ts',
196-
target: 'src/platform-runtime-open-target.ts',
197-
symbols: ['resolveSessionAppBundleIdForTarget'],
198-
classification: 'daemon-policy-essential',
199-
rationale:
200-
'selector dispatch reconstructs the session app-bundle identity after a trigger-app-event ' +
201-
'deep link through the one neutral open-plan resolver (#2334); Android package resolution ' +
202-
'moved behind the Android owning seam in packages/platform-android, so the resolver is the ' +
203-
'only symbol this edge names.',
204-
},
205137
{
206138
file: 'src/daemon/server/daemon-runtime.ts',
207139
target: 'src/core/interactors.ts',
@@ -236,15 +168,25 @@ export const DAEMON_PLATFORM_RUNTIME_EDGES: readonly DaemonPlatformRuntimeEdge[]
236168
'src/daemon/provider-device-admission.ts, so this edge is the composition and nothing else.',
237169
},
238170
{
239-
file: 'src/daemon/session-lifecycle/internal/session-open-prepare.ts',
240-
target: 'src/platform-runtime-open-target.ts',
241-
symbols: ['resolveRequestedOpenSurface', 'validateOpenRelaunchTarget'],
242-
classification: 'daemon-policy-essential',
171+
file: 'src/daemon/server/daemon-runtime.ts',
172+
target: 'src/platform-runtime-daemon-services.ts',
173+
symbols: ['createDaemonPlatformServices'],
174+
classification: 'composition-essential',
175+
rationale:
176+
'process-root assembly of the daemon platform-services port (ADR 0019): local readiness, ' +
177+
'boot and runner-session observation, and open-target classification reach the request ' +
178+
'path only as this frozen object, so no daemon module names a platform adapter. Device ' +
179+
'execution never enters the port; it stays behind the request-bound runtime binding.',
180+
},
181+
{
182+
file: 'src/daemon/server/daemon-runtime.ts',
183+
target: 'src/platform-runtime-claim-recovery.ts',
184+
symbols: ['createClaimRecoveryGateway'],
185+
classification: 'composition-essential',
243186
rationale:
244-
'open-prepare policy consumes only the neutral open plan/result surface (#2334): surface ' +
245-
'classification and relaunch-target validation. The platform mechanics that used to share ' +
246-
'the file (Android package resolution) moved behind the Android owning seam, leaving this ' +
247-
'edge daemon policy over two neutral, non-mechanics functions.',
187+
'process-root assembly of the per-transaction recovery gateway factory (#2168): the daemon ' +
188+
'owns which dead owner state dir a recovery is rebuilt from and when the gateway is ' +
189+
'disposed, and this composition module is the sole place that assembles the gateway itself.',
248190
},
249191
] as const;
250192

‎scripts/layering/root-module-zones.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,10 @@ export const ROOT_MODULE_ZONES: Readonly<Record<string, readonly string[]>> = {
5050
'src/platform-runtime-android-observation-host.ts',
5151
'src/platform-runtime-apple-resources.ts',
5252
'src/platform-runtime-apple-runner-owner.ts',
53+
'src/platform-runtime-claim-recovery.ts',
5354
'src/platform-runtime-daemon-lifecycle.ts',
5455
'src/platform-runtime-daemon-owner-cleanup.ts',
56+
'src/platform-runtime-daemon-services.ts',
5557
'src/platform-runtime-device-boot.ts',
5658
'src/platform-runtime-device-inventory.ts',
5759
'src/platform-runtime-device-ready.ts',

0 commit comments

Comments
 (0)