Skip to content

Commit 9abcd7f

Browse files
authored
refactor: move Apple platform family into package (#2118)
* refactor: move Apple platform family into package * fix: preserve Apple facade sync contracts * fix: complete Apple W4 rebase review fixes
1 parent c7f42cc commit 9abcd7f

231 files changed

Lines changed: 2218 additions & 1216 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.fallowrc.json

Lines changed: 219 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,227 @@
7171
},
7272
{
7373
"comment": "Apple perf sampling metadata is selected through the same lazy platform host; Fallow cannot follow the dynamic property read in src/platform-runtime-perf-host.ts.",
74-
"file": "src/platforms/apple/core/perf.ts",
74+
"file": "packages/platform-apple/src/core/perf.ts",
7575
"exports": ["buildAppleFrameSamplingMetadata", "buildAppleMemorySamplingMetadata"]
7676
},
77+
{
78+
"comment": "These host-process helpers are consumed only by integration leak-oracle support and host-process unit tests; Fallow's production graph intentionally excludes both consumers.",
79+
"file": "packages/host-kit/src/process.ts",
80+
"exports": ["uniquePositivePids", "readOwnedProcessRecordFile"]
81+
},
82+
{
83+
"comment": "Apple app-resolution helpers are reached through the named lazy façade in packages/platform-apple/src/app-resolution-facade.ts. Fallow cannot connect dynamic import member reads to these source exports; keep this list limited to the facade's re-exports.",
84+
"file": "packages/platform-apple/src/core/app-resolution.ts",
85+
"exports": ["buildAppNotInstalledError", "resolveIosAppAlias"]
86+
},
87+
{
88+
"comment": "The app-resolution package facade is consumed through workspace package exports. Fallow does not follow that package boundary, so keep only its externally consumed helper that has no visible production edge here.",
89+
"file": "packages/platform-apple/src/app-resolution-facade.ts",
90+
"exports": ["buildAppNotInstalledError"]
91+
},
92+
{
93+
"comment": "The legacy push helper remains a package-root operation reached through a dynamic import; Fallow cannot connect that root wrapper to this source export.",
94+
"file": "packages/platform-apple/src/core/app-device-io.ts",
95+
"exports": ["pushIosNotification"]
96+
},
97+
{
98+
"comment": "The lifecycle package facade is consumed through workspace package exports. Fallow does not follow that package boundary, so keep only the two root lifecycle helpers without visible production edges here.",
99+
"file": "packages/platform-apple/src/app-lifecycle-facade.ts",
100+
"exports": ["openIosApp", "openIosDevice"]
101+
},
102+
{
103+
"comment": "Apple install mechanics are reached through the root lazy façade, while bundle-info consumers use the named install-artifact façade. Fallow cannot connect dynamic import member reads to these source exports; keep this list limited to the actual facade re-exports.",
104+
"file": "packages/platform-apple/src/core/install-artifact.ts",
105+
"exports": ["prepareIosInstallArtifact", "readIosBundleInfo"]
106+
},
107+
{
108+
"comment": "Apple perf mechanics are reached through the named lazy perf façade. Fallow cannot connect dynamic import member reads to these source exports; keep this list limited to the facade's re-exports.",
109+
"file": "packages/platform-apple/src/perf-facade.ts",
110+
"exports": [
111+
"buildAppleFrameSamplingMetadata",
112+
"buildAppleMemorySamplingMetadata",
113+
"buildAppleMemorySnapshotSupport",
114+
"isRetryableIosDeviceTraceRecordFailure",
115+
"resolveIosDevicePerfHint",
116+
"captureAppleMemorySnapshot",
117+
"readAppleProcessSamples",
118+
"resolveAppleExecutable",
119+
"resolveIosDevicePerfTarget",
120+
"sampleAppleFramePerf",
121+
"sampleAppleMemoryPerf",
122+
"cleanupAppleXctracePerfCapture",
123+
"startAppleXctracePerfCapture",
124+
"stopAppleXctracePerfCapture",
125+
"writeAppleXctracePerfReport",
126+
"AppleXctraceCpuProfileReport",
127+
"AppleXctracePerfMode",
128+
"AppleXctracePerfResult"
129+
]
130+
},
131+
{
132+
"comment": "Apple tool-provider mechanics are reached through the named lazy tool-provider façade. Fallow cannot connect dynamic import member reads to these source exports; keep this list limited to the facade's re-exports.",
133+
"file": "packages/platform-apple/src/tool-provider-facade.ts",
134+
"exports": [
135+
"hasScopedAppleToolProvider",
136+
"resolveAppleToolProvider",
137+
"createLocalAppleToolProvider",
138+
"readApplePlistJson",
139+
"runAppleToolCommand",
140+
"runXcrun"
141+
]
142+
},
143+
{
144+
"comment": "The interactions package facade is consumed through workspace package exports. Fallow does not follow that package boundary, so keep the synchronous helper explicitly accounted for.",
145+
"file": "packages/platform-apple/src/interactions-facade.ts",
146+
"exports": ["resolveAppleBackRunnerCommand"]
147+
},
148+
{
149+
"comment": "The runner-operations package facade is consumed through workspace package exports. Fallow does not follow that package boundary, so keep the two operations without visible production edges here explicitly accounted for.",
150+
"file": "packages/platform-apple/src/runner-operations-facade.ts",
151+
"exports": ["hasCachedAppleRunnerArtifact", "runApplePressSeries"]
152+
},
153+
{
154+
"comment": "The device-aware simctl argument builder is reached through the named lazy simctl façade. Fallow cannot connect the dynamic member read to this source export; keep the suppression limited to that re-export.",
155+
"file": "packages/platform-apple/src/core/simctl.ts",
156+
"exports": ["buildSimctlArgsForDevice"]
157+
},
158+
{
159+
"comment": "The device-aware simctl argument builder is a named package entrypoint reached through dynamic platform-runtime imports. Fallow cannot connect those member reads to the re-export; keep the suppression limited to this one facade symbol.",
160+
"file": "packages/platform-apple/src/simctl-facade.ts",
161+
"exports": ["buildSimctlArgs", "buildSimctlArgsForDevice"]
162+
},
163+
{
164+
"comment": "The simulator package facade is consumed through workspace package exports. Fallow does not follow that package boundary, so keep the legacy shutdown operation explicitly accounted for.",
165+
"file": "packages/platform-apple/src/simulator-facade.ts",
166+
"exports": ["shutdownSimulator"]
167+
},
168+
{
169+
"comment": "Apple mechanics are reached through the named lazy façade in packages/platform-apple/src/index.ts. Fallow cannot connect dynamic import member reads to this source export; keep this list limited to wrappers present in that façade.",
170+
"file": "packages/platform-apple/src/core/perf-xctrace.ts",
171+
"exports": ["cleanupAppleXctracePerfCapture"]
172+
},
173+
{
174+
"comment": "Apple runner mechanics are reached through the named lazy façade in packages/platform-apple/src/index.ts. Fallow cannot connect dynamic import member reads to these source exports; keep this list limited to wrappers present in that façade or its cleanup composition.",
175+
"file": "packages/platform-apple/src/core/runner-client.ts",
176+
"exports": [
177+
"notifyIosRunnerAppRelaunched",
178+
"prewarmIosRunnerSession",
179+
"prepareIosRunner",
180+
"resolveRunnerAppBundleId",
181+
"detachIosSimulatorRunnerSessionsForShutdown",
182+
"getRunnerSessionSnapshot",
183+
"scheduleIosRunnerIdleStop",
184+
"stopIosRunnerSession",
185+
"stopAllIosRunnerSessions",
186+
"readStaleRunnerLease",
187+
"verifyLeaseRunnerPidIdentity",
188+
"applyXctestRunnerAppIconFromDerivedPath"
189+
]
190+
},
191+
{
192+
"comment": "The Apple root façade owns the lazy runner-owner mutator; Fallow cannot connect its dynamic import member read to the moved source export.",
193+
"file": "packages/platform-apple/src/core/runner-owner-state.ts",
194+
"exports": ["setRunnerLeaseOwnerStateDir"]
195+
},
196+
{
197+
"comment": "Apple diagnostics are reached through the named lazy façade in packages/platform-apple/src/index.ts. Fallow cannot connect dynamic import member reads to these source exports; keep this list limited to wrappers present in that façade.",
198+
"file": "packages/platform-apple/src/doctor.ts",
199+
"exports": ["appleToolchainCheck", "appleRunnerWarmupCheck"]
200+
},
201+
{
202+
"comment": "macOS helper mechanics are reached through the named lazy façade in packages/platform-apple/src/index.ts. Fallow cannot connect dynamic import member reads to these source exports; keep this list limited to wrappers present in that façade.",
203+
"file": "packages/platform-apple/src/os/macos/helper.ts",
204+
"exports": ["startMacOsAudioProbeProcess", "resolveFrontmostMacOsApp"]
205+
},
206+
{
207+
"comment": "The Apple package root is a cold-start-preserving named façade: its production consumers load the value wrappers through dynamic imports. Fallow cannot connect those member reads, so this exact list records only the façade exports currently reported as unreachable; static consumers remain visible to the audit.",
208+
"file": "packages/platform-apple/src/index.ts",
209+
"exports": [
210+
"appleToolchainCheck",
211+
"appleRunnerWarmupCheck",
212+
"detectSoleRunningIosSimulatorApp",
213+
"findIosSimulatorInstalledApp",
214+
"invalidateIosAppResolutionCache",
215+
"listIosApps",
216+
"resolveIosApp",
217+
"resolveIosSimulatorDeepLinkBundleId",
218+
"closeIosApp",
219+
"openIosApp",
220+
"openIosDevice",
221+
"screenshotIos",
222+
"pushIosNotification",
223+
"readIosClipboardText",
224+
"writeIosClipboardText",
225+
"runAppleToolCommand",
226+
"runXcrun",
227+
"createLocalAppleToolProvider",
228+
"readApplePlistJson",
229+
"setIosSetting",
230+
"readAppleAlert",
231+
"awaitAppleAlert",
232+
"actOnAppleAlert",
233+
"createAppleInteractor",
234+
"ensureBootedSimulator",
235+
"shutdownSimulator",
236+
"withAppleToolProvider",
237+
"prepareIosInstallArtifact",
238+
"symbolicateCrashArtifact",
239+
"queryAppleRunnerSelector",
240+
"resolveFrontmostMacOsApp",
241+
"runMacOsAlertAction",
242+
"startMacOsAudioProbeProcess",
243+
"captureMacOsSurfaceSnapshot",
244+
"cleanupAppleXctracePerfCapture",
245+
"sampleAppleMemoryPerf",
246+
"captureAppleMemorySnapshot",
247+
"sampleAppleFramePerf",
248+
"prepareAppleTraceRecordRetry",
249+
"resolveAppleExecutable",
250+
"resolveIosDevicePerfTarget",
251+
"readAppleProcessSamples",
252+
"startAppleXctracePerfCapture",
253+
"stopAppleXctracePerfCapture",
254+
"writeAppleXctracePerfReport",
255+
"runApplePressSeries",
256+
"runAppleRunnerCommand",
257+
"notifyIosRunnerAppRelaunched",
258+
"prewarmAppleRunnerCache",
259+
"prewarmIosRunnerSession",
260+
"prepareIosRunner",
261+
"resolveRunnerAppBundleId",
262+
"detachIosSimulatorRunnerSessionsForShutdown",
263+
"getRunnerSessionSnapshot",
264+
"scheduleIosRunnerIdleStop",
265+
"stopIosRunnerSession",
266+
"stopAllIosRunnerSessions",
267+
"cleanupRunnerLeasesForOwner",
268+
"readStaleRunnerLease",
269+
"verifyLeaseRunnerPidIdentity",
270+
"applyXctestRunnerAppIconFromDerivedPath",
271+
"AppleMacOsHelperProvider",
272+
"AppleToolAvailabilityChecker",
273+
"AppleToolCommandExecutor",
274+
"AppleXcrunToolProvider",
275+
"IosPhysicalDeviceBackend",
276+
"IosPhysicalDeviceControl",
277+
"IosPhysicalDeviceTunnel",
278+
"PreparedIosInstallArtifact",
279+
"AppleMemoryPerfSample",
280+
"AppleMemorySnapshotResult",
281+
"AppleProcessSample",
282+
"AppleXctraceCpuProfileReport",
283+
"AppleXctracePerfCapture",
284+
"AppleXctracePerfMode",
285+
"AppleXctracePerfResult",
286+
"MacOsPermissionTarget",
287+
"MacOsSnapshotNode"
288+
]
289+
},
290+
{
291+
"comment": "The legacy Maestro fallback wording remains contracts vocabulary for wire/fixture compatibility, but has no in-repo production consumer. The Apple migration changes this file's documentation path, so keep the existing declaration explicit without treating it as a new W4 export leak.",
292+
"file": "packages/contracts/src/interactor-types.ts",
293+
"exports": ["MAESTRO_NON_HITTABLE_FALLBACK_MESSAGE"]
294+
},
77295
{
78296
"comment": "Daemon route handlers are reached only through the dynamic `import()` table in request-handler-chain.ts, which --production analysis cannot follow to a consumer.",
79297
"file": "src/daemon/handlers/{lease,session,snapshot,react-native,record-trace,find,interaction}.ts",

docs/adr/0009-apple-platform-consolidation.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ is therefore costly out of proportion to the actual work, and iPadOS/visionOS/wa
2727

2828
Model Apple OSes with an **`AppleOS` discriminant** (`ios | ipados | tvos | watchos | visionos | macos`)
2929
under a single `apple` Platform — **not** six `Platform` literals. The OS-agnostic Apple engine consolidates
30-
under `src/platforms/apple/core/`, with genuinely per-OS code in `src/platforms/apple/os/<os>/` leaves;
30+
under `packages/platform-apple/src/core/`, with genuinely per-OS code in
31+
`packages/platform-apple/src/os/<os>/` leaves;
3132
the Apple plugin is the first instance of the platform-plugin registry (the platform axis of the
3233
completed perfect-shape refactor; the registry and enforcement paths below are authoritative).
3334
Per-OS capability differences become data keyed by `AppleOS`. The additive,
@@ -62,11 +63,12 @@ This composes with ADR 0008 (the descriptor's capability facet) and ADR 0003.
6263
Implementation status as of 2026-08:
6364

6465
- Shipped: the internal `Platform` collapse to `apple`; additive `appleOs` groundwork; the shared
65-
Apple engine under `src/platforms/apple/core`; macOS leaf files under
66-
`src/platforms/apple/os/macos`; a dedicated tvOS leaf under `src/platforms/apple/os/tvos`;
67-
direct internal imports to the Apple modules; the per-`AppleOS` capability table
68-
(`APPLE_OS_CAPABILITIES`, `src/platforms/apple/capabilities.ts`, parity-pinned against the pre-table
69-
predicates); the watchOS **unsupported sentinel** (reserved in the `AppleOS` type and interactor-rejected —
66+
Apple engine under `packages/platform-apple/src/core`; macOS leaf files under
67+
`packages/platform-apple/src/os/macos`; a dedicated tvOS leaf under
68+
`packages/platform-apple/src/os/tvos`;
69+
direct internal imports to the Apple modules; the per-`AppleOS` runtime facts (including
70+
`packages/platform-apple/src/gesture-facts.ts`; the former capability projection was retired after
71+
its predicates moved into request-bound facts); the watchOS **unsupported sentinel** (reserved in the `AppleOS` type and interactor-rejected —
7072
XCUITest cannot drive watchOS UI — `isSupportedAppleDeploymentLeaf`, the Apple interactor, and
7173
gesture admission reject it — never produced by discovery); and visionOS profile/build/discovery
7274
plus simulator-deployment evidence.

fallow-baselines/health.json

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -351,38 +351,38 @@
351351
"count": 2
352352
}
353353
},
354-
"src/platforms/apple/core/app-resolution.ts": {
354+
"packages/platform-apple/src/core/app-resolution.ts": {
355355
"crap_moderate": {
356356
"count": 1
357357
}
358358
},
359-
"src/platforms/apple/core/app-settings.ts": {
359+
"packages/platform-apple/src/core/app-settings.ts": {
360360
"crap_moderate": {
361361
"count": 1
362362
}
363363
},
364-
"src/platforms/apple/core/perf.ts": {
364+
"packages/platform-apple/src/core/perf.ts": {
365365
"complexity_high": {
366366
"count": 1
367367
},
368368
"crap_high": {
369369
"count": 1
370370
}
371371
},
372-
"src/platforms/apple/core/screenshot-status-bar.ts": {
372+
"packages/platform-apple/src/core/screenshot-status-bar.ts": {
373373
"complexity_moderate": {
374374
"count": 1
375375
},
376376
"crap_moderate": {
377377
"count": 2
378378
}
379379
},
380-
"src/platforms/apple/core/screenshot.ts": {
380+
"packages/platform-apple/src/core/screenshot.ts": {
381381
"complexity_moderate": {
382382
"count": 1
383383
}
384384
},
385-
"src/platforms/apple/os/macos/helper.ts": {
385+
"packages/platform-apple/src/os/macos/helper.ts": {
386386
"crap_high": {
387387
"count": 1
388388
}
@@ -578,20 +578,20 @@
578578
"src/snapshot/snapshot-lines.ts:high impact",
579579
"src/utils/result-serialization.ts:high impact",
580580
"src/utils/rect-center.ts:high impact",
581-
"src/platforms/apple/core/app-launch.ts:complexity",
581+
"packages/platform-apple/src/core/app-launch.ts:complexity",
582582
"src/utils/parsing.ts:high impact",
583583
"src/daemon/daemon-command-registry.ts:high impact",
584584
"src/replay/script.ts:complexity",
585585
"src/daemon/handlers/session-doctor-output.ts:high impact",
586-
"src/platforms/apple/core/perf-xml.ts:high impact",
586+
"packages/platform-apple/src/core/perf-xml.ts:high impact",
587587
"src/daemon/daemon-process.ts:high impact",
588588
"src/utils/screenshot-result.ts:high impact",
589589
"packages/platform-web/src/json-utils.ts:high impact",
590590
"packages/platform-android/src/settings.ts:complexity",
591591
"src/utils/text-surface.ts:high impact",
592592
"src/daemon/handlers/session-test-sharding.ts:high impact",
593593
"src/daemon/handlers/session-replay-runtime.ts:complexity",
594-
"src/platforms/apple/core/debug-symbols/utils.ts:high impact",
594+
"packages/platform-apple/src/core/debug-symbols/utils.ts:high impact",
595595
"packages/platform-linux/src/snapshot.ts:high impact",
596596
"src/core/interaction-targeting.ts:high impact",
597597
"packages/maestro/src/internal/runtime-targets.ts:high impact",

packages/contracts/src/interactor-types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,9 @@ export const MAESTRO_NON_HITTABLE_FALLBACK_MESSAGE = 'tapped via non-hittable co
8787
* `TextEntryResult.textEntryRoute` (RunnerTests+TextTyping.swift,
8888
* RunnerTests+SynthesizedTextEntry.swift). Closed set, because the runner is
8989
* its only producer and the boundary that narrows it
90-
* (readTypeTextBackendResult, src/platforms/apple/interactions.ts) drops a
90+
* (readTypeTextBackendResult, packages/platform-apple/src/interactions.ts) drops a
9191
* route it cannot name — so a Swift-side addition would silently vanish. The
92-
* route-parity test in src/platforms/apple/core/__tests__/interactions.test.ts
92+
* route-parity test in packages/platform-apple/src/core/__tests__/interactions.test.ts
9393
* reads the Swift sources and fails instead.
9494
*/
9595
export const TEXT_ENTRY_ROUTES = [

packages/host-kit/src/host-file.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,30 @@
11
export {
22
createHostDirectoryLinkSync,
3+
accessHostFile,
4+
chmodHostFile,
5+
copyHostFile,
6+
copyHostPath,
7+
ensureHostDirectory,
38
ensureHostDirectorySync,
49
hostFileExistsSync,
510
hostFileLstatSync,
11+
hostFileStat,
612
hostFileStatSync,
713
hostHomeDirectory,
814
hostTemporaryDirectory,
915
makeHostTemporaryDirectory,
1016
readHostBinaryFile,
17+
readHostDirectory,
1118
readHostDirectorySync,
1219
readHostSymbolicLinkSync,
1320
readHostTextFile,
1421
readHostTextFileSync,
1522
removeHostDirectory,
23+
removeHostPath,
1624
removeHostFileSync,
25+
renameHostPath,
26+
unlinkHostFile,
27+
writeHostBinaryFile,
28+
writeHostTextFile,
1729
writeHostTextFileSync,
1830
} from './internal/host-file.ts';

0 commit comments

Comments
 (0)