Skip to content

refactor(apple): put the Apple app states at one source and reuse the contract failure - #2950

Merged
thymikee merged 2 commits into
mainfrom
refactor/apple-app-state-single-source
Sep 25, 2026
Merged

thymikee merged 2 commits into
mainfrom
refactor/apple-app-state-single-source

Conversation

@thymikee

@thymikee thymikee commented Sep 25, 2026 •

Copy link
Copy Markdown
Member

Summary

Follow-up to #2929 that resolves the duplication that PR surfaced, without touching the runner wire.

  • State lists point the right way. APPLE_APPLICATION_STATES is now the single declaration of the XCApplication.State names, and IOS_TARGET_ACTIVATION_PRIOR_STATES derives from it by dropping runningForeground. Before, the full list spread from the prior-state subset, so the narrower concept owned the names the appState command answers with. Behavior and order are unchanged; the decoder's raw table and the native SDK pins still verify the mapping.
  • The Apple binder stops restating contracts. AppStateInteractorResolver is now an alias of LocalInteractorOperationResolver, and the advertised-without-an-implementation failure is built by invalidRuntimeContract instead of a second copy of the code, reason, and hint. That failure branch had no test; it now has one pinning the typed reason, which is what keeps it outside every closed reason set that licenses answering from the session record (ADR 0019 §2).
  • AppleApplicationState stops re-exporting through contracts/app-state-runtime; the kernel is its declaration site and both consumers read it there.

Evaluated and skipped from the same review, with reasons:

  • Send targetActivation.priorState as a name instead of an integer. Not safe on this repo's seams: runner-artifact.ts accepts an operator-supplied .xctestrun (iosXctestrunFile) and AGENT_DEVICE_IOS_RUNNER_DERIVED_PATH overrides the derived path, and neither spends the runner-source fingerprint that guards cache reuse and lease adoption. A daemon can therefore reach a runner it did not build, and the numeric table plus the native raw-value pins (#2726) are what catch SDK drift on that path.
  • Require appBundleId in the Apple input to drop a session check. The two checks are not redundant: one refuses explicit --platform requests before device resolution, the other decides after resolveCommandDevice for selector-targeted requests.
  • Share one Swift helper across the app-state switches. The switches answer different questions (wire naming, alert-observation eligibility, foreground-host policy); merging them couples unrelated policies.

9 files, +58/−39. No CLI, MCP, docs, or wire change.

Validation

Tested at d76aeb2.

  • pnpm check:affected --run: all runnable checks passed (format, lint, typecheck, layering, di-seams, fallow, mcp-metadata, build, package, integration-node, macos-coverage, vitest-related, integration-progress, replay-compat, daemon-wire-compat, affected-selector, and the model/tooling suites).
  • pnpm test:unit: 1403 files / 11374 tests passed.
  • pnpm check:fallow --base origin/main: no issues in the 9 changed files.
  • pnpm check:production-exports: 68 findings, identical to the origin/main baseline (no new unused export).
  • pnpm check:daemon-wire-compat: protocol unchanged vs v0.21.14.
  • New test is a real regression proof: replacing the contract-bug throw with a silent {} fails only that test.

No device-facing behavior changed, so no device runs apply.

Review in cubic

… contract failure

#2929 added `appState` beside the #2682 activation disclosure, and the two state
lists ended up pointing the wrong way: the full `XCUIApplication.State` list was
spread from the smaller prior-state subset, so the narrower concept owned the
names the broader one answers with. The full list is now the single declaration
and the prior states derive from it by dropping the one state the runner skips
`activate()` in.

The Apple binder also restated two things contracts already owns: a resolver
type that is the local interactor resolver under another name, and the
advertised-without-an-implementation failure the `invalidRuntimeContract` leaf
already builds. Both are reused now, and the failure branch finally has a test
pinning its typed reason, which is what keeps it outside every closed reason set
that licenses answering from the session record.

`AppleApplicationState` no longer re-exports through `contracts/app-state-runtime`:
the kernel is its declaration site and both consumers read it there.
`invalidRuntimeContract` had no export target: it was a leaf kept out of the
`platform-runtime` façade so that façade stays exhaustive over its sources and
inside its eager-closure budget. Now that a platform package consumes it, the
subpath is its declaration site in the manifest, and the independently committed
contracts export snapshot carries it with the one-line regeneration.
@cubic-dev-ai

cubic-dev-ai Bot commented Sep 25, 2026

Copy link
Copy Markdown

We've triggered an ultrareview automatically — This cross-cutting refactor changes the Apple app-state source of truth, shifts contract subpath exports, and alters the exported type shape of the prior-state list, so a subtle API or mapping regression could break downstream consumers or the appState command.. I'll post findings when complete.

An ultrareview is cubic's deepest review, catching hard-to-find bugs in the most critical PRs. It runs a longer, multi-pass analysis using cubic's most capable review models, and typically takes around 30 minutes. It consumes your team's reviewed-lines allowance at 3× the standard rate.

Automated ultrareviews are disabled by default. We triggered this run as part of your trial. Want cubic to do this for every high-risk PR? Enable auto-ultrareview in your settings.

@github-actions

Copy link
Copy Markdown

Size Report

Metric Base Current Diff
Installed (including dependencies) 4.82 MB 4.82 MB +172 B
Package (unpacked) 4.82 MB 4.82 MB +172 B
Package (download) 1.44 MB 1.44 MB +112 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 29.4 ms 28.1 ms -1.3 ms
CLI --help 86.7 ms 79.9 ms -6.8 ms

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ultrareview completed in 1m 26s

No issues found across 9 files

Re-trigger cubic

@thymikee

Copy link
Copy Markdown
Member Author

Reviewed d76aeb2. I found no defect: the Apple app states now come from one source, the invalid-state path reuses the shared contract failure, and the runner tests confirm the raw-value mapping without a change to the runner wire. All 19 checks pass, and there are no conflicts.

Not blocking: the new test checks code, message and details.reason but not details.hint, so it would also pass against the old inline error. Comparing against invalidRuntimeContract('...').details would pin the shared construction.

@thymikee thymikee added the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Sep 25, 2026
@thymikee
thymikee merged commit c31cc49 into main Sep 25, 2026
19 checks passed
@thymikee
thymikee deleted the refactor/apple-app-state-single-source branch September 25, 2026 07:59
@github-actions

Copy link
Copy Markdown
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-09-25 07:59 UTC

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

Labels

ready-for-human Valid work that needs human implementation, judgment, or maintainer merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant