Skip to content

Commit 646b2a5

Browse files
committed
fix(apple): reach runner liveness through the memoized operations loader
Every Apple tool port loads the runner operations through the one memoized loader (#2314): a port that opens its own dynamic import can resolve the unmocked module while a test's mock factory is still loading and let a real local runner escape. The liveness port now uses the loader like its siblings; the facade members consumed only through the loader are declared to fallow, and the plan resolver reads one step per helper to stay under the complexity threshold.
1 parent 6cfc93e commit 646b2a5

3 files changed

Lines changed: 26 additions & 16 deletions

File tree

.fallowrc.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,15 @@
9191
"file": "packages/platform-apple/src/app-resolution-facade.ts",
9292
"exports": ["buildAppNotInstalledError"]
9393
},
94+
{
95+
"comment": "Runner operations reach the daemon only through the memoized loader in src/platform-runtime-apple-application-tools.ts (one dynamic import per specifier, #2314). Fallow cannot connect a member read off that loader's promise to these re-exports; keep the list to the facade members no static import consumes.",
96+
"file": "packages/platform-apple/src/runner-operations-facade.ts",
97+
"exports": [
98+
"detachIosSimulatorRunnerSessionsForShutdown",
99+
"hasLiveIosRunnerSession",
100+
"stopAllIosRunnerSessions"
101+
]
102+
},
94103
{
95104
"comment": "Apple install mechanics are reached through the named install-artifact façade. Fallow cannot connect workspace package exports to these source exports; keep this list limited to the actual facade re-exports.",
96105
"file": "packages/platform-apple/src/core/install-artifact.ts",

src/core/command-descriptor/planned-operations.ts

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,22 @@ export function resolvePlannedRuntimeOperations(
2929
): readonly PlannedRuntimeOperation[] | undefined {
3030
const operations = new Set<PlannedRuntimeOperation>();
3131
for (const step of steps) {
32-
const descriptor = descriptorsByName.get(step.command);
33-
if (!descriptor) return undefined;
34-
const execution = descriptor.platformExecution;
35-
if (execution.kind !== 'device-runtime') continue;
36-
const uses =
37-
'uses' in execution ? (execution.selectUses?.(step) ?? execution.uses) : [execution.use];
38-
for (const use of uses) {
39-
for (const operation of use.required) {
40-
// `defineUse` admits only operation keys, so an unknown name means the vocabulary list
41-
// and the operations union drifted; treat the plan as unproven rather than guess.
42-
if (!isRuntimeOperationName(operation)) return undefined;
43-
operations.add(operation);
44-
}
45-
}
32+
const required = requiredOperationsOf(step);
33+
if (required === undefined) return undefined;
34+
for (const operation of required) operations.add(operation);
4635
}
4736
return Object.freeze([...operations].sort());
4837
}
38+
39+
/** One step's required operations, or `undefined` when the step cannot be planned honestly. */
40+
function requiredOperationsOf(step: PlannedStep): readonly PlannedRuntimeOperation[] | undefined {
41+
const execution = descriptorsByName.get(step.command)?.platformExecution;
42+
if (execution === undefined) return undefined;
43+
if (execution.kind !== 'device-runtime') return [];
44+
const uses =
45+
'uses' in execution ? (execution.selectUses?.(step) ?? execution.uses) : [execution.use];
46+
const required = uses.flatMap((use) => use.required);
47+
// `defineUse` admits only operation keys, so an unknown name means the vocabulary list and the
48+
// operations union drifted; treat the plan as unproven rather than guess.
49+
return required.every(isRuntimeOperationName) ? required : undefined;
50+
}

src/platform-runtime-apple-application-tools.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,7 @@ export function createAppleApplicationTools(): AppleApplicationTools {
7777
await stopIosRunnerSession(deviceId);
7878
},
7979
hasLiveRunnerSession: async (device, execution) => {
80-
const { hasLiveIosRunnerSession } =
81-
await import('@agent-device/platform-apple/runner/operations');
80+
const { hasLiveIosRunnerSession } = await loadRunnerOperations();
8281
return hasLiveIosRunnerSession(device, { requestId: execution.requestId });
8382
},
8483
scheduleRunnerIdleStop: (deviceId) => {

0 commit comments

Comments
 (0)