Skip to content

test: classify the runner XCTests — pure decisions to a macOS host lane, simulator semantics gated os(iOS) (#1781 A7) - #1861

Open
thymikee wants to merge 1 commit into
mainfrom
test/1781-a7-classify-xctests
Open

test: classify the runner XCTests — pure decisions to a macOS host lane, simulator semantics gated os(iOS) (#1781 A7)#1861
thymikee wants to merge 1 commit into
mainfrom
test/1781-a7-classify-xctests

Conversation

@thymikee

@thymikee thymikee commented Aug 18, 2026

Copy link
Copy Markdown
Member

Summary

The classify half of A7 (#1781): every one of the 157 AgentDeviceRunnerUITests methods on main (156 after this PR's one deletion) is sorted into a bucket, the sort is written into the code as #if guards, and the pure-decision majority now runs on every PR with no simulator.

Buckets (table below has every method, one reason each):

  • HOST — 132 pure Swift decision tests (rule tables, geometry, parsers, policies, journal/dispatch bookkeeping; nothing needing a launched app). These now run on the macOS host in ci.yml's existing job that already compiled exactly this bundle on every PR and threw it away (renamed "Swift Runner Unit Compile" → Swift Runner Host XCTests). Test execution measured at ~8 s, ~20 s wall for the test-without-building step. No new job, no simulator boot.
  • SIM — 22 runner/XCTest-semantics tests (launch the host app, route through SpringBoard, swizzle XCUIApplication, or pin a guard whose macOS arm is a bare return false). They stay on the iOS Simulator lanes: 11 on ios.yml's PR list, all 22 on the nightly.
  • ENTRY — 1 (testCommand, the runner's 24-hour server entry point; skipped everywhere, now checked everywhere).
  • DELETE — 2: testSnapshotAccessibilityUnavailableCarriesSparseVerdict duplicated testSnapshotAccessibilityUnavailableMarksSparseSnapshotRunnerFatal (same production call); its three verdict assertions are folded into the surviving test, which also checks reason — a field the deleted one never asserted. And testBlockingSystemAlertSnapshotIsNilOnTvOS, deleted rather than widened: blockingSystemAlertSnapshot is #if os(macOS) return nil, so on the only lane that could run it the assertion pins a compile-time literal. Nothing else deletes: the CLI/vitest suites observe the daemon through the runner's wire surface, not these in-process decision seams, so no XCTest here is "already observed by the CLI smoke" — and ios runner: navigation fallback helpers accept CGRect.infinite (tap point ≈ −9e307) #1812 is the standing counterexample for calling any of the dark set disposable.

The classification is enforced, not documented. A test's #if guard is its bucket (convention comment in RunnerTests.swift): AGENT_DEVICE_RUNNER_UNIT_TESTS alone ⇒ pure, host + simulator; … && os(iOS) ⇒ simulator only. check:xctest-selection now evaluates the guards per platform (small #if evaluator that throws on vocabulary it doesn't know rather than guessing) and derives what each of the three lanes — host (ci.yml, whole macOS bundle), PR list (ios.yml -only-testing:), nightly (whole iOS bundle) — reaches, failing on:

  • a flagged identifier no source declares (as before),
  • a flagged identifier that lane's platform never compiles (new),
  • a declared test no lane reaches (new — this immediately caught the two #if os(tvOS) tests, dark since birth; renamed …WithoutSpringBoardHost and widened to os(tvOS) || os(macOS) so the host lane runs their no-SpringBoard contract),
  • testCommand reachable by any lane (new — the nightly's skip was checked before; ci.yml's skip now is too).

The host lane and the nightly both assert executed == derived reach (scripts/xctest-run-summary.ts), closing both the known "-D flag missing ⇒ silent 0-test green" and its quieter sibling, a guard compiling a whole file out.

Swift changes: 12 sim-semantic tests gained os(iOS) guards — 10 in +CommandExecution.swift, 2 in +Snapshot.swift (they launch apps / probe SpringBoard, or — the case review caught — pin a guard whose macOS arm is a bare return false; 7 of the 12 demonstrably fail on the macOS host, and the other 5 pass there only because the branch they exist to assert is compiled out) — one tvOS test renamed + widened to os(tvOS) || os(macOS), 2 tests deleted. No test logic changed otherwise.

The table

All 157 methods · test · file · bucket · reason (132 HOST · 22 SIM · 1 ENTRY · 2 DELETE)
# test file bucket reason
1 testTapPointPolicyMatchesGoldenParityTable RunnerTapPointPolicy.swift HOST golden-table parity with contracts/fixtures/tap-point-policy.json (TS twin tap-point-policy-parity.test.ts); pure geometry, no XCUIElement — the Swift half of the parity gate must stay
2 testActionNamesAreCappedPerElementAndReported RunnerTests+AXSnapshotFallback.swift HOST (also PR list) per-element action caps + truncation flag; pure
3 testCustomActionCoverageParsesOnlyCompletePairs RunnerTests+AXSnapshotFallback.swift HOST (also PR list) bridge-dictionary → coverage parsing; pure
4 testCustomActionsRequestPinsPrivateAXBackend RunnerTests+AXSnapshotFallback.swift HOST (also PR list) decodes wire Command, checks presentationOptions backend pin; pure
5 testDeepExtensionCountsMissedFrontiers RunnerTests+AXSnapshotFallback.swift HOST (also PR list) drives RunnerAXSnapshotBridge.extend with fake frontiers/AX client (FrontierSnapshot…ForTesting); no live tree
6 testHungCustomActionReadIsContainedAndRecovers RunnerTests+AXSnapshotFallback.swift HOST (also PR list) single-flight containment of a hung AX read with a fake client and a real serial queue; timing-based (1 s deadline) but no app — flagged as the host lane's most timing-sensitive test
7 testPartialCustomActionPassIsDisclosedAndCompleteOneIsNot RunnerTests+AXSnapshotFallback.swift HOST (also PR list) legacyQualityMessage wording for partial coverage; pure
8 testPrivateAXAcceptedDepthMemoryMatchesBundleProcessAndExpires RunnerTests+AXSnapshotFallback.swift HOST PID-bound depth memory (remember/lookup/expiry) on runner state; no app
9 testPrivateAXAcceptedDepthMemoryRequiresProcessIdentifierToRecord RunnerTests+AXSnapshotFallback.swift HOST same memory, nil-PID rule; pure
10 testPrivateAXAttemptDepthsAppliesRememberedDepth RunnerTests+AXSnapshotFallback.swift HOST depth-ladder arithmetic (privateAXAttemptDepths); pure
11 testPrivateAXDepthLimitedRequiresEveryFrontierResolved RunnerTests+AXSnapshotFallback.swift HOST (also PR list) privateAXDepthLimited verdict matrix; pure
12 testPrivateAXInteractiveFiltersLoginLikeHiddenDrawer RunnerTests+AXSnapshotFallback.swift HOST (also PR list) privateAXPresentation interactive filter over a dictionary tree; pure
13 testPrivateAXNodesCarryAnnotatedCustomActions RunnerTests+AXSnapshotFallback.swift HOST (also PR list) privateAXPresentation over a dictionary tree; pure (uses XCUIElement.ElementType raw values only)
14 testPrivateAXScopeSelectsSubtreeNotMatchingLabels RunnerTests+AXSnapshotFallback.swift HOST (also PR list) privateAXPresentation scope selection over a dictionary tree; pure
15 testRequestPinnedBackendReportsItsOwnReason RunnerTests+AXSnapshotFallback.swift HOST (also PR list) xcTestChannelStateFirstFailure reason codes; pure
16 testViewportReadSkippedWhileXCTestChannelPenalized RunnerTests+AXSnapshotFallback.swift HOST shouldReadPrivateAXViewportViaXCTest over penalty + abandoned-capture state; no app
17 testAlertAcceptTreatsOpenAsAffirmative RunnerTests+Alert.swift HOST (also PR list) isAcceptButton label table; pure string rule
18 testRemoteHostProbeRunsOnlyWhenSpringboardModalHasNoActions RunnerTests+BlockingSystemModalResolution.swift HOST RemoteHostedSystemModalPolicy decision on an action count; pure
19 testRemoteHostStateGateFailsClosedToForeground RunnerTests+BlockingSystemModalResolution.swift HOST RemoteHostedSystemModalPolicy state gate; pure enum rule
20 testResolveBlockingSystemModalIsAbsentWithoutSpringBoardHost RunnerTests+BlockingSystemModalResolution.swift HOST was …OnTvOS under #if os(tvOS) — reachable by no lane; the contract (no SpringBoard host → .absent) is macOS's too, so widened to os(tvOS) || os(macOS) and now runs on the host lane
21 testCanonicalPlannedGestureResponseOmitsDragFrameAndPreservesDiagnostics RunnerTests+CommandExecution.swift HOST canonicalPlannedGestureResponse projection; pure
22 testExecuteDispatchedReturnsBusyBeforeMainThreadFastPath RunnerTests+CommandExecution.swift HOST executeDispatched short-circuits to RUNNER_BUSY on abandoned main-thread work before touching any app; no app
23 testExecuteDispatchedReturnsWedgedBeforeMainThreadFastPath RunnerTests+CommandExecution.swift HOST same short-circuit, RUNNER_WEDGED past the wedge threshold; no app
24 testGestureResponseIncludesMaestroNonHittableFallbackUsage RunnerTests+CommandExecution.swift HOST gestureResponse payload shape; pure
25 testGestureResponseIncludesSynthesizedTapFallbackDiagnostics RunnerTests+CommandExecution.swift HOST gestureResponse payload shape; pure
26 testInjectedTapRecordedFailureGateIsTapOnlyAndCountGated RunnerTests+CommandExecution.swift HOST the #1605 injection gate predicate; pure (test-only static, compiled out of production)
27 testPostSnapshotDelayMarkDoesNotQueueBehindAbandonedTreeCapture RunnerTests+CommandExecution.swift HOST setNeedsPostSnapshotInteractionDelay must not enqueue on main behind abandoned work; real GCD, no app
28 testRunMainThreadWorkExecutesOffMainCallerOnMainThread RunnerTests+CommandExecution.swift HOST runMainThreadWork hops an off-main caller to main; real GCD, no app
29 testRunMainThreadWorkTimeoutMarksAbandonedUntilDrained RunnerTests+CommandExecution.swift HOST runMainThreadWork timeout → abandoned/drained bookkeeping; real GCD + semaphores, no app
30 testXCTestRecordedFailureResponseDoesNotWrapReadOnlyOrRunnerFatalResponses RunnerTests+CommandExecution.swift HOST xctestRecordedFailureResponse for reads / runnerFatal; pure
31 testXCTestRecordedFailureResponseFailsMutatingSuccesses RunnerTests+CommandExecution.swift HOST xctestRecordedFailureResponse for mutations; pure
32 testCommandJournalKeepsErrorMetadataWhenResponseJsonIsDropped RunnerTests+CommandJournal.swift HOST journal error metadata retention; pure
33 testCommandJournalRetainsCompletedSequenceResults RunnerTests+CommandJournal.swift HOST journal keeps sequence results; pure
34 testCommandJournalRetainsFailedSequenceResults RunnerTests+CommandJournal.swift HOST journal keeps failed sequence results; pure
35 testCommandJournalRetentionPolicy RunnerTests+CommandJournal.swift HOST journal retention matrix (scalar / object / tree / artifact) ; pure
36 testJournalStoredResponseStaysUnstamped RunnerTests+CommandJournal.swift HOST RunnerCommandJournal stores unstamped JSON; pure
37 testStampingCurrentUptimeCreatesPayloadWhenNil RunnerTests+CommandJournal.swift HOST same stamping rule, nil payload; pure
38 testStampingCurrentUptimePreservesPayload RunnerTests+CommandJournal.swift HOST Response.stampingCurrentUptimeMs; pure
39 testStampingCurrentUptimeSkipsErrorResponses RunnerTests+CommandJournal.swift HOST same stamping rule, error response; pure
40 testUptimeBypassesCommandJournal RunnerTests+CommandJournal.swift HOST execute(uptime) must not be journaled; runs the command dispatcher without a target app
41 testFlatSnapshotFilterDecisionCarriesSubtreeScopeState RunnerTests+FlatSnapshotFiltering.swift HOST same decision, scope state; pure
42 testFlatSnapshotFilterDecisionMatrixCoversOptions RunnerTests+FlatSnapshotFiltering.swift HOST flatSnapshotFilterDecision option matrix; pure
43 testFlatSnapshotProjectionMatchesElementReverseScrollCapture RunnerTests+FlatSnapshotFiltering.swift HOST (also PR list) flat-projection cursor over a fixture tree (reverse-scroll capture shape); pure
44 testPrivateAXInteractiveCandidatesPreserveBackendInputs RunnerTests+FlatSnapshotFiltering.swift HOST privateAXInteractiveCandidate type rule; pure
45 testDesktopScrollWheelDeltaEventsHonorDurationAndPreservePixels RunnerTests+Interaction.swift HOST scroll-wheel event splitting arithmetic; pure
46 testDesktopScrollWheelDeltaEventsKeepInstantScrollSingleEvent RunnerTests+Interaction.swift HOST scroll-wheel event splitting, zero duration; pure
47 testDesktopScrollWheelDeltasMapDirections RunnerTests+Interaction.swift HOST macOS scroll-wheel delta mapping; pure (and now runs on macOS itself)
48 testNativeSynthesizedPointRotatesByInterfaceOrientation RunnerTests+Interaction.swift HOST orientation rotation math for synthesized points; pure
49 testNativeSynthesizedVectorRotatesByInterfaceOrientation RunnerTests+Interaction.swift HOST orientation rotation math for vectors; pure
50 testOrientedSynthesizedScreenshotReferenceFrameUsesLandscapeLogicalDimensions RunnerTests+Interaction.swift HOST same frame rule across orientations; pure
51 testPlannedMultiTouchGestureAcceptsMatchingInBoundsTrajectories RunnerTests+Interaction.swift HOST RunnerGesturePlan decode + validation; pure
52 testPlannedMultiTouchGestureRejectsMismatchedOffsets RunnerTests+Interaction.swift HOST gesture-plan validation; pure
53 testSinglePointerEndpointHoldUsesFastSwipeExecution RunnerTests+Interaction.swift HOST execution profile mapping; pure
54 testSinglePointerFlingUsesFastSwipeExecution RunnerTests+Interaction.swift HOST plannedGestureExecution profile mapping; pure
55 testSinglePointerGestureRejectsMissingExecutionProfile RunnerTests+Interaction.swift HOST gesture-plan validation error text; pure
56 testSinglePointerTimedPanUsesSampledExecution RunnerTests+Interaction.swift HOST execution profile mapping; pure
57 testSynthesizedScreenshotReferenceFrameRejectsInvalidSize RunnerTests+Interaction.swift HOST same frame rule, invalid size; pure
58 testSynthesizedScreenshotReferenceFrameUsesScreenshotSize RunnerTests+Interaction.swift HOST screenshot reference frame from a CGSize; pure
59 testRunnerScreenshotStabilitySettledFalseOnFailedCapture RunnerTests+Keyboard.swift HOST (also PR list) stability window rule with nil samples; pure
60 testRunnerScreenshotStabilitySettledFalseOnMidWindowMismatch RunnerTests+Keyboard.swift HOST (also PR list) stability window rule; pure
61 testRunnerScreenshotStabilitySettledNeedsEnoughSamples RunnerTests+Keyboard.swift HOST (also PR list) runnerScreenshotStabilitySettled window rule; pure over Data samples
62 testRunnerScreenshotStabilitySettledOnlyLooksAtTheTrailingWindow RunnerTests+Keyboard.swift HOST (also PR list) stability window rule; pure
63 testRunnerScreenshotStabilitySettledRejectsDegenerateRequirement RunnerTests+Keyboard.swift HOST (also PR list) stability window rule, degenerate requirement; pure
64 testRunnerScreenshotStabilitySettledTrueWhenWindowMatches RunnerTests+Keyboard.swift HOST (also PR list) stability window rule; pure
65 testCachedTargetInvalidationClearsProcessBoundState RunnerTests+LifecycleCacheTests.swift HOST (also PR list) invalidateCachedTarget clears process-bound fields; assigns the unlaunched XCUIApplication proxy as a token only
66 testCachedTargetRefreshRequiresChangedPositiveProcessIdentity RunnerTests+LifecycleCacheTests.swift HOST shouldRefreshCachedTarget PID rule; pure
67 testSnapshotPenaltyCanBeClearedAcrossTargetProcessReplacement RunnerTests+LifecycleCacheTests.swift HOST penalty set/clear on runner state; no app
68 testSnapshotPenaltyWarmupExemptionIsConsumedOnce RunnerTests+LifecycleCacheTests.swift HOST warm-up exemption flag on runner state; no app
69 testTargetResetInvalidatesProcessBoundStateWithoutRestartingRunner RunnerTests+LifecycleCacheTests.swift HOST resetTargetAfterExternalRelaunch bookkeeping on runner state; unlaunched proxy as token, no app
70 testTextEntryTapWitnessIsBoundToTargetIdentity RunnerTests+LifecycleCacheTests.swift HOST (also PR list) TextEntryTapWitness.matches identity rule; pure
71 testNavigationBackControlRankPrefersBackThenCloseThenCancel RunnerTests+Navigation.swift HOST back-control ranking table; pure
72 testNavigationBackPredicateUsesTheSharedKeywordTable RunnerTests+Navigation.swift HOST NSPredicate from the keyword table; pure
73 testNavigationFallbackRequiresObservedVisualChange RunnerTests+Navigation.swift HOST before/after Data comparison; pure
74 testTopLeadingNavigationFallbackPointRejectsInvalidFrame RunnerTests+Navigation.swift HOST the #1812 test — .infinite/.zero guard; pure geometry, fixed on main and green here
75 testTopLeadingNavigationFallbackPointTargetsHeaderControlBand RunnerTests+Navigation.swift HOST navigation fallback geometry; pure (#1812 family)
76 testTopNavigationControlFrameAcceptsOnlyHeaderBand RunnerTests+Navigation.swift HOST the other #1812 test — header-band geometry incl. .infinite; pure
77 testPrivateAXGeometrylessSemanticsAreNeverActionableOrScrollContexts RunnerTests+PrivateAXPresentation.swift HOST (also PR list) presentation rule for zero frames; pure
78 testPrivateAXPresentationKeepsOffscreenSubtreeExcludedWhenChildFramesAreClamped RunnerTests+PrivateAXPresentation.swift HOST (also PR list) presentation clamping rule; pure
79 testPrivateAXRegularPresentationProjectsToViewportAndKeepsScrollHint RunnerTests+PrivateAXPresentation.swift HOST (also PR list) privateAXPresentation viewport projection over a dictionary tree; pure
80 testRecordStopIsIdempotentAfterNativeRecorderAlreadyStopped RunnerTests+RecordingTests.swift HOST execute(recordStop) with no active recording; dispatcher path, no app or recorder
81 testRunnerScrollGesturePlanMatchesParityTable RunnerTests+ScrollGesture.swift HOST golden-table parity with contracts/fixtures/scroll-gesture.json (TS twin scroll-gesture.test.ts); pure
82 testRunnerScrollGesturePlanRejectsInvalidAmountAndPixels RunnerTests+ScrollGesture.swift HOST planner input validation (INVALID_ARGS mirror); pure
83 testRunnerScrollGesturePlanRejectsUnknownDirection RunnerTests+ScrollGesture.swift HOST planner input validation; pure
84 testRunnerScrollGesturePlanUsesParityTableConstants RunnerTests+ScrollGesture.swift HOST pins the planner constants through the same table; pure
85 testDirectSelectorAcceptsOneRawHittableMatch RunnerTests+SelectorMatchPolicyTests.swift HOST selector classification; pure
86 testDirectSelectorRejectsTwoRawMatchesBeforeHittabilityPreference RunnerTests+SelectorMatchPolicyTests.swift HOST classifyDirectSelectorCandidates mutation row; pure
87 testMaestroSelectorKeepsExpectedPointAndNonHittableFallbackSemantics RunnerTests+SelectorMatchPolicyTests.swift HOST selector classification, Maestro row; pure
88 testReadSelectorDoesNotAdoptTheNonHittableCoordinateFallback RunnerTests+SelectorMatchPolicyTests.swift HOST selector classification; pure
89 testReadSelectorPrefersTheHittableMatchOverANonHittableDuplicate RunnerTests+SelectorMatchPolicyTests.swift HOST selector classification, read row; pure
90 testReadSelectorStillRejectsTwoHittableMatches RunnerTests+SelectorMatchPolicyTests.swift HOST selector classification; pure
91 testAssembleSequencePreservesOrderOnSuccess RunnerTests+SequenceExecution.swift HOST assembleSequence result ordering with stubbed step results; pure
92 testAssembleSequenceStopsAtFirstFailure RunnerTests+SequenceExecution.swift HOST assembleSequence fail-fast; pure
93 testSequenceAcceptsDoubleTapKind RunnerTests+SequenceExecution.swift HOST executeSequence validation before any executor call; unlaunched proxy as activeApp
94 testSequenceDecodesStepsFromWire RunnerTests+SequenceExecution.swift HOST Command decode of sequence steps; pure
95 testSequenceHasSynthesizedCoordinateStep RunnerTests+SequenceExecution.swift HOST step-shape predicate; pure
96 testSequenceRejectsEmpty RunnerTests+SequenceExecution.swift HOST sequence validation; pure
97 testSequenceRejectsTooManySteps RunnerTests+SequenceExecution.swift HOST sequence validation (cap 20); pure
98 testSequenceRejectsUnknownKind RunnerTests+SequenceExecution.swift HOST sequence validation (INVALID_ARGS); the daemon validates too, but this is the runner's own wire contract for non-daemon clients
99 testSequenceWorstCaseResponseStaysUnderJournalCap RunnerTests+SequenceExecution.swift HOST worst-case response size vs journal cap; pure
100 testDispatchRecoverySkipsBookkeepingWhileXCTestChannelOccupied RunnerTests+Snapshot.swift HOST (also PR list) executeDispatchedWithRecovery with a stub perform closure over abandoned-work state; real GCD, no app
101 testRawSnapshotTooLargeFailureIsStructured RunnerTests+Snapshot.swift HOST failure struct formatting; pure
102 testRecoveredSnapshotMessagePreservesHint RunnerTests+Snapshot.swift HOST message formatting; pure
103 testSnapshotAccessibilityUnavailableMarksSparseSnapshotRunnerFatal RunnerTests+Snapshot.swift HOST snapshotAccessibilityUnavailable payload shape + target invalidation + (merged in) the sparse verdict; unlaunched proxy as token
104 testSystemModalProbeSliceSharesAndClampsToPlanDeadline RunnerTests+Snapshot.swift HOST (also PR list) systemModalProbeSlice clamp arithmetic; pure
105 testAbandonedTreeCaptureSkipsOnlyXCTestBackedSnapshotTiers RunnerTests+SnapshotCapturePlan.swift HOST shouldSkipSnapshotBackendForAbandonedTreeCapture; pure
106 testCollapsedLeafIndexesFlagsMergedContainersOnly RunnerTests+SnapshotCapturePlan.swift HOST collapsed-leaf index rule; pure
107 testDecodedPreferredBackendReachesOptionsAndApplicablePlan RunnerTests+SnapshotCapturePlan.swift HOST (also PR list) wire decode → options → capture plan; pure
108 testEffectiveSnapshotCapturePlanDefersXCTestBackedTiersOnlyWhenPenalizedRegularPlan RunnerTests+SnapshotCapturePlan.swift HOST capture-plan rule; pure
109 testEffectiveSnapshotCapturePlanUsesBoundedXCTestProbeWhenNoIndependentBackendRuns RunnerTests+SnapshotCapturePlan.swift HOST capture-plan rule; pure
110 testLegacyQualityMessageStatesFallbackMeaning RunnerTests+SnapshotCapturePlan.swift HOST legacyQualityMessage wording; pure
111 testPreferredPrivateAXBackendPlansAsPenalized RunnerTests+SnapshotCapturePlan.swift HOST (also PR list) capture-plan rule; pure
112 testSnapshotXCTestChannelPenaltyMatchesBundleAndExpires RunnerTests+SnapshotCapturePlan.swift HOST penalty set/lookup/expiry on runner state; no app
113 testSparsePayloadReasonMatrix RunnerTests+SnapshotCapturePlan.swift HOST (also PR list) sparse-verdict reason matrix; pure
114 testSuppressedAxSnapshotIssueClassifier RunnerTests+SnapshotCapturePlan.swift HOST isSuppressedAxSnapshotIssueDescription classifier; pure (record(_:) itself deliberately not invoked)
115 testTerminalFailsClosedOnInteractiveAxFailureRegardlessOfSparseBest RunnerTests+SnapshotCapturePlan.swift HOST terminal-tier decision; pure
116 testXCTestChannelStateFirstFailureStampsDeferredCodeOnlyForDeferral RunnerTests+SnapshotCapturePlan.swift HOST xcTestChannelStateFirstFailure codes; pure
117 testSnapshotPresentationOwnsBackendNeutralEligibility RunnerTests+SnapshotPresentationTests.swift HOST (also PR list) SnapshotPresentation backend-neutral eligibility (#1850, landed mid-classification); pure
118 testSnapshotPresentationPreservesCurrentWireShape RunnerTests+SnapshotPresentationTests.swift HOST (also PR list) SnapshotPresentation → JSON wire shape; pure
119 testSnapshotTraversalIdentityPreservesSameOriginNodesWithDifferentBounds RunnerTests+SnapshotTraversalIdentityTests.swift HOST (also PR list) snapshotTraversalIdentity — platform-dependent by design (#if os(iOS) NotEqual, else Equal), so both lanes assert their own branch; pure
120 testCoordinateTapTextInputProbeSkipsPenalizedXCTestChannel RunnerTests+SynthesizedGesturePolicy.swift HOST (also PR list) shouldProbeCoordinateTapTextInput; pure
121 testSynthesizedDragCoordinateFallbackAllowsUnknownButNotUnavailableAccessibility RunnerTests+SynthesizedGesturePolicy.swift HOST SynthesizedFallbackPolicy table; pure
122 testSynthesizedFallbackPolicyRequiresPrivateSynthesisForScrollWhenAxUnavailableOrUnknown RunnerTests+SynthesizedGesturePolicy.swift HOST SynthesizedFallbackPolicy table; pure
123 testSynthesizedGesturePoliciesMatchCommandContracts RunnerTests+SynthesizedGesturePolicy.swift HOST synthesizedGesturePolicy(kind) contract table; pure
124 testSynthesizedKeyboardPolicyKeepsUnknownDragProbeButNotUnknownScrollProbe RunnerTests+SynthesizedGesturePolicy.swift HOST SynthesizedKeyboardPolicy table; pure
125 testResolvedCoordinateTextEntryFallsBackWhenSynthesizedFocusIsUnavailable RunnerTests+TextEntryPolicyTests.swift HOST shouldFallbackFromSynthesizedTextEntryFocus; pure
126 testResolvedCoordinateTextEntryRouteRequiresReplacementCoordinatesAndPenalizedXCTest RunnerTests+TextEntryPolicyTests.swift HOST shouldUseResolvedCoordinateTextEntryRoute matrix; pure
127 testSynthesizedFirstResponderTypeRequiresHiddenKeyboardTapWitness RunnerTests+TextEntryPolicyTests.swift HOST shouldUseSynthesizedFirstResponderType matrix; pure
128 testSynthesizedReplacementPacesCharactersAfterSelectingOnce RunnerTests+TextEntryPolicyTests.swift HOST synthesizedReplacementSteps pacing; pure
129 testSynthesizedReplacementRequiresPenalizedXCTestAndCoordinates RunnerTests+TextEntryPolicyTests.swift HOST shouldUseSynthesizedFirstResponderReplacement matrix; pure
130 testSynthesizedTextCommitProgressWalksExpectedPrefixOnly RunnerTests+TextEntryPolicyTests.swift HOST (also PR list) commit-progress prefix walk; pure
131 testCoordinateTextInputCandidateMustBeEnabledAndContainTheTouchPoint RunnerTests+TextInputCandidatePolicy.swift HOST (also PR list) isCoordinateTextInputCandidate geometry rule; pure
132 testDuplicateCommandIdCoalescesOntoInFlightExecution RunnerTests+Transport.swift HOST in-flight command coalescing (attachToInFlightCommandIfNeeded/deliverCommandResult) on runner state; no transport, no app
133 testAlertResolutionCannotBypassRequestedDeadline RunnerTests+CommandExecution.swift SIM (PR list) currentApp = springboard + alert resolution deadline through the real dispatch path (failed on macOS: no SpringBoard) — newly gated
134 testBareDelayedTypeFailsWhenTappedInputDisappearsMidCommand RunnerTests+CommandExecution.swift SIM (PR list) launched app, input removed mid-command (32 s); already os(iOS)
135 testBareTypeUsesTappedInputWhenSoftwareKeyboardIsHidden RunnerTests+CommandExecution.swift SIM (PR list) launched app + hardware-keyboard fixture (17 s); already os(iOS)
136 testExecuteDispatchedReturnsBusyBeforeBlockingSystemModalProbeDrains RunnerTests+CommandExecution.swift SIM (nightly only) app.launch() + a real bounded SpringBoard probe timeout (13 s; failed on macOS) — newly gated
137 testMissingBundleCommandInvalidatesCompleteCachedTargetState RunnerTests+CommandExecution.swift SIM (PR list) app.launch() + prepareActiveCommandContext against a live foreground app (newly gated os(iOS); passed on macOS by launching the macOS host app, but the contract is about a real target)
138 testPrepareActiveCommandContextRoutesBlockingSystemModalToSpringboard RunnerTests+CommandExecution.swift SIM (nightly only) routes to springboard (XCUIApplication(bundleIdentifier: com.apple.springboard)), iOS-only routing (failed on macOS) — newly gated
139 testSelectorTapFallsBackToXCTestCoordinateWhenPrivateSynthesisFails RunnerTests+CommandExecution.swift SIM (nightly only) swizzled synthesis + selector tap on a launched app; already os(iOS)
140 testSinglePointerFlingFallsBackToXCTestCoordinateDragWhenPrivateSynthesisFails RunnerTests+CommandExecution.swift SIM (PR list) swizzles the private synthesis bridge and drags a launched app; already os(iOS)
141 testSkipAppActivationPreflightIncludesAlertCommands RunnerTests+CommandExecution.swift SIM (nightly only) asserts the iOS-only alert → SpringBoard routing branch (#if os(iOS) in shouldRouteToSpringboardBlockingSystemModal; failed on macOS) — newly gated
142 testSkipAppActivationPreflightIncludesForegroundCachedCoordinateOnlyTaps RunnerTests+CommandExecution.swift SIM (nightly only) app.launch(); foreground-state-dependent preflight (failed on macOS: no foreground state) — newly gated
143 testSkipAppActivationPreflightKeepsDragScrollAndSequenceOnForegroundGuard RunnerTests+CommandExecution.swift SIM (nightly only) app.launch(); foreground preflight rows — newly gated
144 testSkipAppActivationPreflightRejectsMissingChangedAndBackgroundTargets RunnerTests+CommandExecution.swift SIM (nightly only) app.launch()/terminate() to produce changed and background targets — newly gated
145 testSkipAppActivationPreflightRejectsSelectorAndMixedSequenceGestures RunnerTests+CommandExecution.swift SIM (nightly only) app.launch(); foreground preflight rows — newly gated
146 testSkipAppActivationPreflightRequiresCachedForegroundTarget RunnerTests+CommandExecution.swift SIM (nightly only) shouldSkipAppActivationPreflight is #if os(iOS) …guards… #else return false #endif, so on macOS this asserted a compile-time literal — moved to SIM in review; the guard it pins (currentApp.state == .runningForeground) only exists on iOS
147 testTypeWithoutResolvedInputReturnsTypedFailureBeforeDispatchingText RunnerTests+CommandExecution.swift SIM (PR list) XCUIApplication type path with no resolved input (12 s); already os(iOS)
148 testPenalizedCoordinateTapOnNonTextControlDoesNotAuthorizeBareType RunnerTests+CoordinateTextEntryTests.swift SIM (PR list) launched app with --agent-device-text-entry-regression fixture (17 s); already os(iOS)
149 testActivateTargetSkipsForegroundAndActivatesNonForegroundApplication RunnerTests+LifecycleCacheTests.swift SIM (PR list) swizzles XCUIApplication.state/activate; already os(iOS)
150 testQuerySelectorPrefersHittableMatchOverNonHittableDuplicate RunnerTests+SelectorMatchPolicyTests.swift SIM (PR list) launched app with --agent-device-selector-read-regression fixture (7 s); already os(iOS)
151 testBoundedSystemModalProbeTimeoutRecoversThenReleasesOnDrain RunnerTests+Snapshot.swift SIM (PR list) real runMainThreadWork timeout of the bounded SpringBoard probe through snapshotFast (5 s); the probe body returns nil on macOS (failed there) — newly gated
152 testBoundedSystemModalProbeTimeoutRecoversThenReleasesOnDrainForSnapshotRaw RunnerTests+Snapshot.swift SIM (PR list) same through snapshotRaw — deliberately parametrised over both entry points, kept — newly gated
153 testSynthesizedTextEntryFallsBackOnlyWhenPrivateSynthesisIsUnavailable RunnerTests+TextEntryPolicyTests.swift SIM (nightly only) pure decision, but PrivateXCTestTextEntrySynthesizer is an iOS-only type; already os(iOS), stays SIM by compile constraint
154 testTypeTextReliablyPacesSynthesizedReplacementThroughProductionCaller RunnerTests+TextEntryPolicyTests.swift SIM (nightly only) typeTextReliably with a fake synthesizer conforming to the iOS-only TextEntrySynthesizing; already os(iOS)
155 testCommand RunnerTests.swift ENTRY not a test — the runner's 24-hour server entry point; -skip-testing: on both whole-bundle lanes, absent from the PR list; the check fails if any lane reaches it
156 testSnapshotAccessibilityUnavailableCarriesSparseVerdict RunnerTests+SnapshotCapturePlan.swift DELETE duplicate of testSnapshotAccessibilityUnavailableMarksSparseSnapshotRunnerFatal (same production call, only the snapshotQuality assertions differed); all three folded into the survivor, which also asserts reason — a field the deleted one never checked
157 testBlockingSystemAlertSnapshotIsNilOnTvOS RunnerTests+BlockingSystemModalResolution.swift DELETE deleted in review, not widened: blockingSystemAlertSnapshot is #if os(macOS) return nil, so on the only lane that could run it the assertion pins a compile-time literal. Its twin testResolveBlockingSystemModalIsAbsentWithoutSpringBoardHost covers the same #1351 contract at the layer that decides it at runtime (hasSpringBoardSystemModalHost) and is widened

Lane arithmetic (so the numbers reconcile against the xcresult, not the exit code)

xcodebuild exits 0 on a selection that matches nothing, so every count below is read off the
result bundle's executed total, never off an exit code.

main this PR
declared methods 157 155 (−1 duplicate, −1 vacuous-on-macOS)
compiled for iOS 155 (157 − 2 os(tvOS)-only) 154 (−1 deleted duplicate; the tvOS-guarded pair never compiled here)
compiled for macOS 145 133 (−12 newly os(iOS), −1 deleted duplicate, +1 widened)
compiled for tvOS 147 133 (−12 newly os(iOS), −1 deleted duplicate, −1 deleted vacuous)
nightly executes (iOS − testCommand) 154 153 ✓ measured, unchanged by the review fixes
host lane executes (macOS − testCommand) — (job compiled only) 132 ✓ CI-measured on this head
PR list (ios.yml -only-testing:) 46 46 (untouched)

The nightly's 155-executed night predates two tests that have since landed and the ones this PR
moves; against current main the same command would execute 154, and against this head it
executes 153 — the delta is exactly the deleted duplicate. macOS and tvOS both land on 135
because the 11 newly os(iOS)-gated tests used to compile (and, on macOS, fail) on every
platform; they now compile only where they can pass. No lane runs a tvOS destination at all
which is why the two …OnTvOS tests were dark since birth, and why the fix routes their contract
through the macOS host lane instead of inventing a tvOS lane. Their tvOS branch is still never
executed; that is stated as residual risk rather than papered over.

Validation

  • Host lane in CI on this head (run 32221771402, Swift Runner Host XCTests): Executed 134 tests, with 0 failures (0 unexpected) in 3.152 seconds, reporter Executed 134 test(s); the source reaches 134 on this lane.82 s for the whole job (06:03:28 → 06:04:50Z) including checkout, toolchain, and the cached runner build the job already did before this PR.
  • Host lane, local run (macOS 26.5.2 / Xcode 26.2, no simulator): Executed 134 tests, with 0 failures in 8.1s via the same command; reporter 134 of 134.
  • Simulator, full suite on this head (iPhone 16 Pro, iOS 26.2): Executed 153 tests, with 0 failures in 119s; reporter passes with 153 of 153 for the nightly lane. (Nightly ran 155 before: −2 tvOS-gated tests that never executed there anyway, −1 deleted duplicate, +1 new test from feat(ios): unify snapshot eligibility #1850 which landed mid-classification and is bucketed HOST in the table.)
  • Planted red, gate (all three fire, then restored):
    1. typo in ci.yml's -skip-testing: → fails as unknown identifier and entry point reachable by lane(s): host (the 24-hour hang the check exists for);
    2. a pure test re-gated #if os(tvOS) → fails as reachable by no lane;
    3. a test file re-gated macOS-only → its six PR-list entries fail as platform never compiles.
  • Planted red, lane: flipped XCTAssertNilNotNil in testTopLeadingNavigationFallbackPointRejectsInvalidFrame (the ios runner: navigation fallback helpers accept CGRect.infinite (tap point ≈ −9e307) #1812 test), rebuilt: host lane runs Executed 133 tests, with 1 failure, exit 65 — the moved tests genuinely assert on macOS.
  • Planted red, reporter: the host xcresult fed to the reporter as XCTEST_LANE=nightly fails with executed 134 … reaches 153.
  • The measurement that produced the SIM bucket: before gating, an ungated whole-bundle run on the macOS host executed 143 with 11 failure records across 7 distinct failing tests — all 7 in the set now gated os(iOS) (SpringBoard probes and app launches; several of the records are the same test's cascading issues). The remaining 4 of the 11 gated tests passed on macOS only because their iOS-only branch compiles out there, so passing was not evidence of coverage — that is why the bucket is drawn by what the test asserts, not by what happens to go green. After gating: 134 executed / 0 failures, repeated across runs.
  • check:xctest-selection green on this head; its vitest suite grew planted-guard/uncompiled/dark/entry-point cases (planted-red for each new failure mode, asserted red in-suite); gate-manifest, scripts/gate + check-affected suites, typecheck, lint, format green; pnpm check:affected --run green before push.
  • Local-macOS note: a machine whose system policy refuses unsigned test bundles needs CODE_SIGN_IDENTITY on the build (now in docs/agents/testing.md); GitHub's macOS runners run the unsigned build as-is — this PR's own CI is the proof.

What independent review changed

An adversarial review re-derived the whole count table with its own parser (exact match) and found two false greens — the precise class this PR's bucket rule exists to prevent — plus three smaller items. All fixed here:

  1. testSkipAppActivationPreflightRequiresCachedForegroundTarget was HOST but vacuous there. shouldSkipAppActivationPreflight is #if os(iOS) …guards… #else return false #endif, so on macOS the XCTAssertFalse pinned a compile-time literal and no edit to the iOS body could turn it red. Its five siblings in the same matrix were already gated. Moved into the os(iOS) region → HOST 133 → 132, SIM 21 → 22.
  2. testBlockingSystemAlertSnapshotIsNilOnTvOS was widened into the same trap. blockingSystemAlertSnapshot is #if os(macOS) return nil, so its macOS run asserted the compiler. Deleted rather than widened; its twin testResolveBlockingSystemModalIsAbsentWithoutSpringBoardHost covers the same tvOS: every snapshot/alert resolution fails because the runner probes com.apple.springboard (no SpringBoard on tvOS) #1351 contract where the decision is actually taken at runtime (hasSpringBoardSystemModalHost) and keeps the widening. The honest statement is therefore that one of the two contracts now runs on macOS — corrected in the Swift comment, which had claimed both.
  3. check-xctest-selection.ts had grown 269 → 591 LOC, crossing both AGENTS.md tripwires. The #if evaluator is extracted to scripts/swift-conditional-compilation.ts (152 LOC — it answers "does this line compile for platform X", a different question from "which lane reaches what"), with its tests split to match. Main module now 458 LOC.
  4. Follow-up sizing was 2× off (see above): 11, not ~21.
  5. docs/agents/testing.md signing recipe did not work on a fresh Mac. Rewritten as machine-dependent after testing both spellings on this one: the generic CODE_SIGN_IDENTITY="Apple Development" fails here with No signing certificate "Mac Development" found (with and without CODE_SIGN_STYLE=Manual), while the certificate SHA-1 works; the reviewer's Mac is the inverse. The doc now says try both and notes the XCUITest automation permission a local host run needs.

Also fixed while in there: TEST_FLAG matched only the first flag per line (.exec, not matchAll) — latent today but silently permissive in the skip direction, where an unseen -skip-testing: is a lane that stops skipping the 24-hour entry point. Now global, with a regression test.

Review also confirmed, and worth keeping in the record: no executed coverage is lost (the 12 newly gated tests were compiled-but-never-executed on macOS before this PR, since no lane ran the bundle there); the sweep found exactly three production symbols with degenerate macOS arms and the third (testSnapshotTraversalIdentityPreservesSameOriginNodesWithDifferentBounds) asserts both branches via its own in-body #if and is correctly bucketed; and the evaluator resisted attempts to break it on precedence, !, nesting, #elseif, os(visionOS), and trailing comments while throwing on swift(>=), compiler(>=), and /* */.

One more thing the table makes visible: the golden-table parity tests (testTapPointPolicyMatchesGoldenParityTable, testRunnerScrollGesturePlanMatchesParityTable) and both #1812 navigation tests are on no -only-testing: list — their Swift half ran nightly-only until now and gates every PR from here.

The four lines

  • Catches: regressions in the 134 pure runner-decision tests on every PR in ~20 s of an existing job, independent of ios.yml (15 min, ~19% cancel rate, and its list names only 45 of them); plus three new gate classes — dark tests, wrong-platform selections, silent partial runs.
  • Evidence: ios runner: navigation fallback helpers accept CGRect.infinite (tap point ≈ −9e307) #1812 came from exactly this test class (a geometry guard) on its first-ever execution; the two tvOS tests were dark from birth and the new reachability rule found them mechanically before any human read the table.
  • Cost: ~20 s wall on a PR job that already built the bundle (8 s test execution measured); no new macOS job, no simulator; harness delta ≈ +240 net LOC across the two scripts, mostly the guard evaluator and its planted-red tests.
  • Kill-criterion: a host-lane test that proves flaky where its simulator run is not gets an os(iOS) guard and returns to the simulator lanes (review already exercised this direction — two rows moved out of HOST); more than a handful means the host lane is the wrong tool and goes back to compile-only. The nightly's own kill criterion is updated in its header with the measured figure below.

Ratchet-class — re-run against main immediately before merge

This PR tightens check:xctest-selection: it adds three failure modes (uncompiled selection,
dark test, entry point reachable) and both whole-bundle lanes now assert an exact executed
count. Any PR that lands a runner XCTest between this PR's last CI run and its merge can turn
main red the moment this merges — a new test whose guard names a platform no lane runs, or one
added to ios.yml's list under the wrong guard, fails the gate rather than the author's PR.
So: merge this last among the runner-touching PRs in flight, and re-run its CI against
current main immediately before merging.
It has already been rebased twice mid-review for
exactly this reason (absorbing #1850's new test, then #1860).

Scope

15 files: 6 Swift (guards, 1 rename+widening, 2 deletions, convention comment), 2 workflows (ci.yml job, xctest-nightly.yml env + honest comments), 3 scripts + 3 script tests (the #if evaluator and its tests are their own module after review), AGENTS.md gate bullet, docs/agents/testing.md row. ios.yml is deliberately untouched — its -only-testing: list is the follow-up's business, so this PR does not tighten the PR lane.

Residual risk

  • No lane runs tvOS. The two widened tests now assert their no-SpringBoard contract on macOS; their tvOS compilation is still never executed. The gate can only see "reachable by some lane", not "reachable on every platform it compiles for" — a tvOS-specific regression stays invisible until a tvOS lane exists.
  • Host-lane timing. testHungCustomActionReadIsContainedAndRecovers is the one moved test with real timing in it (a 1 s read deadline plus a drain poll). It passed on every local host run, but it is the first candidate to re-gate os(iOS) if the host lane ever flakes — named in the kill criterion for that reason.
  • The #if evaluator is a small parser, not the Swift compiler. It refuses vocabulary it does not know (test asserts this), so a new guard shape fails the gate loudly rather than being silently mis-bucketed — but a supported guard used in a way I did not anticipate could still mis-attribute a test. The executed-count assertion on both whole-bundle lanes is the backstop: source-derived reach and real executed count have to agree.
  • A #if inside a /* */ block comment or a """ string literal reads as a real directive. The evaluator is line-based, so commented-out or quoted directive text would shift its frame stack. On the host and nightly lanes the executed-count assertion backstops this (source-derived reach and real executed count must agree); the PR lane has no count assertion, so there it is unbackstopped until the follow-up adds one. No such construct exists in the tree today.
  • 5 of the 12 newly gated tests were green on macOS. They are gated on what they assert (iOS-only branches), not on observed failure — two of them only because review noticed the macOS arm was a literal. A reviewer who disagrees is disagreeing with 5 specific rows in the table, not with the mechanism.

Follow-up (separate PR, merges last per the ratchet rule): drop ios.yml's -only-testing: list in favor of the whole iOS bundle + executed-count assertion, then retire xctest-nightly.yml.

Sizing that follow-up honestly: what only the nightly reaches is nightly ∖ (host ∪ pr) = 11 tests / ~42 s of simulator time (measured off the first night's xcresult; an earlier draft of this body said "~21 / ~2.5 min", which double-counted the tests ios.yml's list already runs — the kill-criterion paragraph was the correct half and the two contradicted each other). The trade is not free: those 11 are exactly the app-launching, SpringBoard-probing class — the flakiest tests in the suite, with the longest ones at 13 s, 7 s, 6 s, 6 s — and moving them onto ios.yml puts them on a blocking 15-minute PR gate that is already cancelled ~19% of the time. Measure their flake rate on the nightly before moving them.

@github-actions

github-actions Bot commented Aug 18, 2026

Copy link
Copy Markdown

Size Report

Metric Base Current Diff
JS raw 2.31 MB 2.31 MB 0 B
JS gzip 759.3 kB 759.3 kB 0 B
npm tarball 882.6 kB 883.1 kB +451 B
npm unpacked 3.08 MB 3.08 MB +980 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 28.3 ms 26.8 ms -1.5 ms
CLI --help 71.8 ms 70.0 ms -1.8 ms

Top changed chunks: no changes in the largest emitted chunks.

@thymikee

Copy link
Copy Markdown
Member Author

Re-reviewed exact 27ede966: the XCTest classification, workflow production route, and planted non-vacuity evidence are sound.

P2 architecture blocker: scripts/check-xctest-selection.ts is now 591 LOC and its matching test is 539 LOC after substantial additions. That violates the repository’s >500 LOC extract-before-add rule and mirrored test-topology requirement. Please split conditional-compilation/platform-reach evaluation from workflow/lane manifest validation, give each focused mirrored tests, and keep the entry script orchestration-only.

Readiness: still draft; Coverage is red from the inherited main 2654/2652 file-size ratchet tracked by #1860 (owner-action, not introduced here), and Linux is still pending. Please also correct the understated body growth estimate.

@thymikee
thymikee force-pushed the test/1781-a7-classify-xctests branch from 27ede96 to ba30f37 Compare August 19, 2026 06:02
@thymikee
thymikee marked this pull request as ready for review August 19, 2026 06:19
@thymikee

Copy link
Copy Markdown
Member Author

CI green on the actual head ba30f3772 (rebased onto current main, which absorbed #1850's new test and #1860's ratchet fix): 28/28 checks pass, run 32221771402.

The lane this PR adds reports its own evidence: Swift Runner Host XCTests executed 134 tests, 0 failures, 3.2 s and its reporter asserted 134 of 134 against the count check:xctest-selection derives from the #if guards — the whole job took 82 s, on a macOS slot that already existed to compile this bundle.

Undrafting. Note this is ratchet-class (see the section in the body): it must be re-run against current main immediately before merge, and it should merge last among runner-touching PRs in flight.

@thymikee

Copy link
Copy Markdown
Member Author

Re-reviewed exact ba30f377: classification, production route, non-vacuity evidence, and exact-head CI are sound, but the architecture blocker remains. scripts/check-xctest-selection.ts is still 591 LOC and its matching test is 539 LOC, exceeding the repository’s >500 extract-before-add and mirrored-topology rules.

Split conditional-compilation/platform-reach evaluation from workflow/lane manifest validation, give each focused mirrored tests, and keep the entry script orchestration-only. Please also correct the understated growth accounting.

…ne, simulator semantics gated os(iOS) (#1781 A7)

Every declared AgentDeviceRunnerUITests method now belongs to a lane, and
the #if guard is the classification: AGENT_DEVICE_RUNNER_UNIT_TESTS alone
means a pure runner decision (runs on the macOS host on every PR — ci.yml's
existing compile job now executes the bundle it builds), '&& os(iOS)' means
runner/XCTest semantics (simulator lanes only). check:xctest-selection
evaluates the guards per platform, derives each lane's reach, and fails on
a flagged identifier that is undeclared or uncompiled on that lane, on a
declared test no lane reaches (found the two tvOS-only tests, dark since
birth — widened to os(tvOS) || os(macOS)), and on testCommand reaching any
lane. The host and nightly lanes assert executed == derived reach, so a
missing -D flag or a guard that compiles a file out reads red, not as a
smaller green. One duplicate test deleted (sparse-verdict assertions folded
into its twin).
@thymikee
thymikee force-pushed the test/1781-a7-classify-xctests branch from ba30f37 to 5c8f81d Compare August 19, 2026 07:04
@thymikee

Copy link
Copy Markdown
Member Author

Review fixes pushed — head 5c8f81df7, 28/28 green (CI run 32226140720; the Linux lane's first attempt cancelled with zero failed steps and was re-run whole, now green).

Both false greens are fixed, and the counts moved with them:

before review now
HOST 134 132
SIM 21 22
DELETE 1 2
host lane executed (CI) 134 of 134 132 of 132
nightly executed (local, iPhone 16 Pro) 153 of 153 153 of 153 ✓ unchanged
  • testSkipAppActivationPreflightRequiresCachedForegroundTarget → SIM: shouldSkipAppActivationPreflight is #if os(iOS) … #else return false #endif, so on macOS it asserted a literal.
  • testBlockingSystemAlertSnapshotIsNilOnTvOSdeleted, not widened: blockingSystemAlertSnapshot is #if os(macOS) return nil. Its twin keeps the widening because resolveBlockingSystemModal decides at runtime off hasSpringBoardSystemModalHost. So one of the two contracts now runs on macOS — the Swift comment claiming both is corrected.
  • Evaluator extracted to scripts/swift-conditional-compilation.ts (152 LOC); check-xctest-selection.ts 591 → 452, under the 500 tripwire.
  • Follow-up sizing corrected to nightly ∖ (host ∪ pr) = 11 tests / ~42 s, with the trade named (that set is the flakiest class, landing on a blocking 15-min gate cancelled ~19% of the time).
  • Signing recipe rewritten as machine-dependent — I tested both spellings here and the generic name is the one that fails on this Mac (No signing certificate "Mac Development" found), the inverse of the reviewer's box, so the doc now says try both.
  • Bonus from the nits: TEST_FLAG now uses matchAll, so a second flag on one line can no longer hide (silently permissive in the skip direction), with a regression test.

The unbackstopped-/* */ nit is recorded in Residual risk rather than fixed: the host and nightly lanes catch it via the executed-count assertion, the PR lane has no count assertion until the follow-up adds one, and no such construct exists in the tree today.

Still ratchet-class — re-run against current main immediately before merge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant