Skip to content

fix: prepare reinstall artifacts before removing apps - #4533

Merged
thymikee merged 8 commits into
mainfrom
t3/apple-test-implementation
Nov 14, 2026
Merged

thymikee merged 8 commits into
mainfrom
t3/apple-test-implementation

Conversation

@thymikee

@thymikee thymikee commented Nov 13, 2026 •

Copy link
Copy Markdown
Member

Summary

Prepare Apple and Android replacement artifacts before uninstalling the existing app. An archive that cannot be unpacked now fails while the app and its data remain installed. Successful reinstall still creates fresh app state.

The 10-file change preserves cache invalidation, cancellation, hosted installers and cleanup. Invalid app bundles, corrupt APKs or later installation failures can still leave the app removed. No validator or rollback is added.

Closes #4516. Closes #4517. Refs #4368.

Validation

Tested 9d7993318de5d202ecb7c653ff099baca1f43e8f against ddbfc855661fa9d29b8c836057b7f5b099bcad44:

  • Exact pnpm check:affected --run --head <sha> --json passed: 11 checks passed, 8 named skips, 1 provider deduplication. Full unit: 19,771 passed/10 skipped; related: 390 passed.
  • 37 Apple deployment tests and the targeted typed no-app IPA provider case passed. The fixture detects the original destructive order; restored controls pass. The post-prepare cancellation case detects a skipped-cleanup mutant.
  • Separate Apple and Android original-production experiments detected destructive ordering. Owned Android 15/API 35 and iOS 27.0 native runs at production-identical 88bfc202 retained saved markers after bad-archive failures and cleared them after valid reinstall. Owned resources were cleaned up.
  • Independent combined source review found no defects. Later review corrections are test-only.

CI is green at this head (21 successful checks, 1 skipped); no review threads are open. Historical provider timeout causes remain unproven. Native cancellation and arbitrary invalid APK/app-bundle protection are not claimed.

Validation evidence.

thymikee and others added 6 commits November 13, 2026 22:17
A replacement that cannot be prepared no longer removes the installed app or its data.
A failure during install after the uninstall can still leave the app removed.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
A replacement that cannot be materialized no longer removes the installed app.
Cancellation after preparation also stops before the uninstall.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
@thymikee

Copy link
Copy Markdown
Member Author

Evidence for head 88bfc20263d77baedc88db4c4cdb9349bc7fe740 (base ddbfc855661fa9d29b8c836057b7f5b099bcad44). Historical failed receipts remain failed; this is the new passed receipt after the stale-order migrations.

Exact-head receipt facts and commands
{
  "status": "passed",
  "exitCode": 0,
  "refs": {
    "base": "origin/main",
    "head": "88bfc20263d77baedc88db4c4cdb9349bc7fe740",
    "resolvedBase": "ddbfc855661fa9d29b8c836057b7f5b099bcad44",
    "resolvedHead": "88bfc20263d77baedc88db4c4cdb9349bc7fe740",
    "mergeBase": "ddbfc855661fa9d29b8c836057b7f5b099bcad44"
  },
  "exactCommit": true,
  "checkoutChanged": false,
  "receiptSha256": "c4709de6a6f94863ad0a7373cad28c503a1c48ce9800bf7d279851a97693035c",
  "results": [
    {
      "id": "format",
      "status": "passed",
      "command": [
        "pnpm",
        "run",
        "format:check"
      ],
      "exitCode": 0,
      "durationMs": 1279
    },
    {
      "id": "lint",
      "status": "passed",
      "command": [
        "pnpm",
        "run",
        "lint"
      ],
      "exitCode": 0,
      "durationMs": 2433
    },
    {
      "id": "typecheck",
      "status": "passed",
      "command": [
        "pnpm",
        "run",
        "typecheck"
      ],
      "exitCode": 0,
      "durationMs": 1256
    },
    {
      "id": "layering",
      "status": "passed",
      "command": [
        "pnpm",
        "run",
        "check:layering"
      ],
      "exitCode": 0,
      "durationMs": 31513
    },
    {
      "id": "fallow",
      "status": "passed",
      "command": [
        "pnpm",
        "run",
        "check:fallow",
        "--base",
        "ddbfc855661fa9d29b8c836057b7f5b099bcad44"
      ],
      "exitCode": 0,
      "durationMs": 9417
    },
    {
      "id": "build",
      "status": "passed",
      "command": [
        "pnpm",
        "run",
        "build"
      ],
      "exitCode": 0,
      "durationMs": 5768
    },
    {
      "id": "vitest-related",
      "status": "passed",
      "command": [
        "pnpm",
        "exec",
        "vitest",
        "related",
        "--run",
        "--passWithNoTests",
        "--project=!unit-core",
        "--project=!apple-runner",
        "--project=!fuzz-worker",
        "packages/command-surface/src/management/install.ts",
        "packages/platform-android/src/deployment/runtime.test.ts",
        "packages/platform-android/src/deployment/runtime.ts",
        "packages/platform-apple/src/deployment/runtime.test.ts",
        "packages/platform-apple/src/deployment/runtime.ts",
        "src/core/interactors/limrun-ios-runner-parity.test.ts",
        "test/integration/provider-scenarios/android-reinstall.test.ts",
        "test/integration/provider-scenarios/ios-lifecycle.test.ts",
        "test/integration/provider-scenarios/limrun-deployment-cancellation.test.ts",
        "website/docs/docs/commands.md"
      ],
      "exitCode": 0,
      "durationMs": 44184
    },
    {
      "id": "unit",
      "status": "passed",
      "command": [
        "pnpm",
        "run",
        "check:unit"
      ],
      "exitCode": 0,
      "durationMs": 233840
    },
    {
      "id": "coverage",
      "status": "skipped",
      "reason": "GitHub-authoritative (jobs: Coverage)"
    },
    {
      "id": "provider-integration",
      "status": "deduplicated",
      "reason": "covered by related tests"
    },
    {
      "id": "web-smoke",
      "status": "skipped",
      "reason": "GitHub-authoritative (jobs: Apple / Smoke Tests (${{ matrix.platform }}), Integration Tests, Lint, Typecheck & Package, Perf Nightly / Android Command Perf, Perf Nightly / iOS Command Perf)"
    },
    {
      "id": "command-docs",
      "status": "passed",
      "command": [
        "pnpm",
        "run",
        "check:command-docs"
      ],
      "exitCode": 0,
      "durationMs": 1479
    },
    {
      "id": "docs-links",
      "status": "passed",
      "command": [
        "pnpm",
        "run",
        "check:doc-links"
      ],
      "exitCode": 0,
      "durationMs": 7886
    },
    {
      "id": "docs-prose",
      "status": "passed",
      "command": [
        "pnpm",
        "run",
        "check:doc-prose"
      ],
      "exitCode": 0,
      "durationMs": 1172
    },
    {
      "id": "replay-ios",
      "status": "skipped",
      "reason": "parked, workflow_dispatch only (Replay Manual / iOS Replay Suite)"
    },
    {
      "id": "replay-ios-device",
      "status": "skipped",
      "reason": "parked, workflow_dispatch only (Replay Manual / iOS Replay Suite)"
    },
    {
      "id": "replay-macos",
      "status": "skipped",
      "reason": "GitHub-authoritative (jobs: Apple / Smoke Tests (${{ matrix.platform }}))"
    },
    {
      "id": "replay-linux",
      "status": "skipped",
      "reason": "GitHub-authoritative (jobs: Linux / Smoke Tests)"
    },
    {
      "id": "linux-command-evidence",
      "status": "skipped",
      "reason": "GitHub-authoritative (jobs: Linux / Smoke Tests)"
    },
    {
      "id": "replay-android",
      "status": "skipped",
      "reason": "parked, workflow_dispatch only (Replay Manual / Android Full Emulator Suite)"
    }
  ]
}

The full-unit production fallback was retained. Skips are not passes. No separate provider run is claimed by this receipt.

Native marker retention and fresh-reinstall controls

Combined #4516 (Apple) + #4517 (Android) reinstall: native evidence

Candidate: the reviewed checkout, HEAD 88bfc20, base ddbfc85. git status empty and HEAD unchanged at start and end. No source edits, push, PR, comments, gate or test run.

Runtime binding

  • One private foreground daemon from this checkout dist/src/internal/daemon.js, AGENT_DEVICE_DAEMON_SERVER_MODE=dual, state dir a private, now-removed state directory (0700; removed at the end).
  • Record (token stripped, daemon-record.json): version 0.21.38-dev, codeOrigin checkout, codeSignature graph:124:41e09e190213e25c7216e20c5564f6ab268df53f, transport dual, pid 95573.
  • Private 0600 --config with daemonBaseUrl plus token. Every command used the T3-returned CLI path verbatim plus --config and my own --session (audit4517-a, audit4516-i).
  • Provenance that the candidate served the requests: the Android snapshot reports androidSnapshot.backend=android-helper, helperVersion=0.21.38-dev (equals package.json; T3 wrapper bundles 0.21.31). Per-request and session logs sit in the private state dir, copied to raw-*/. A token scan of all artifacts found nothing.
  • The Apple runner was started by this daemon (runner.log, xcodebuild test-without-building). I did not separately verify that the runner binary was built from the candidate. That binary does not run the changed reinstall path, which is daemon-side.
  • The T3 device_open call booted/attached both devices before the daemon sent any command.

Android: ReactNative_API_35B (emulator-5556, Android 15 / API 35), fixture com.callstack.audit4517fixture

Fixture: Java Activity with SharedPreferences. Source, manifest, build notes and hashes are in android-fixture/ (apk sha256 4caf5036...). Bad archive: zip with only README.

Step Result
install, open, snapshot -i OK. helper backend, version 0.21.38-dev.
is exists 'label="Marker: empty"' pass
press 'label="Save marker"', then is exists 'label="Marker: audit-4517-synthetic"' pass
reinstall <id> bad-archive.zip exit 1, INVALID_ARGS: "Expected Android installable (.apk or .aab) source, but got .../agent-device-archive-*/extracted"
open --relaunch, then is exists 'label="Marker: audit-4517-synthetic"' pass: marker survived
reinstall <id> audit4517fixture.apk (valid), open --relaunch, is exists 'label="Marker: empty"' pass: fresh empty marker, so a successful reinstall still clears data
Daemon log of the bad request (daemon-req-bad-reinstall.ndjson): request_start, resolve_target_device, request_failed, request_failed. No install or uninstall phase.
Good control: session events show request.started and action.recorded "Reinstalled". The marker cleared, which shows the uninstall ran. The daemon does not log a per-step uninstall dispatch, so there is no explicit log line for it.
Screenshots: raw-android/android-{1..4}.png and T3 device_screenshot. The title bar partly covers the Marker text, so the pass relies on the named accessibility expectations (the "Save marker" button is visible).

Apple: iPhone 17 Pro BC54AC11 (iOS 27.0), fixture com.callstack.audit4516fixture

Fixture: UIKit app with UserDefaults, source in ios-fixture/ (ipa sha256 486531ed...). bad.ipa is a text file, not a zip.

  • The first fixture build crashed at launch on iOS 27 (UIKit no-scene-lifecycle trap; raw-ios/invalid-first-attempt/). That whole first attempt is INVALID. It includes a bad reinstall run while the app was not running and no marker was saved. I rebuilt the fixture with scene lifecycle and repeated the entire sequence from the beginning; every figure below comes from the second run.
    | Step | Result |
    |---|---|
    | install, open, snapshot -i | OK |
    | is exists 'label="Marker: empty"' | pass |
    | press 'label="Save marker"', then is exists 'label="Marker: audit-4516-synthetic"' | pass |
    | reinstall <id> bad.ipa | exit 1, code UNKNOWN, message "End of central directory record signature not found. Either not a zip file, or file is truncated." (the raw yauzl error, as the independent review noted) |
    | open --relaunch, then is exists 'label="Marker: audit-4516-synthetic"' | pass: marker survived |
    | reinstall <id> audit4516fixture.ipa (valid), open --relaunch, is exists 'label="Marker: empty"' | pass: fresh empty marker |
    Daemon log of the bad request (request 82da0430476de88a): request_start, resolve_target_device, request_failed. No uninstall or install phase. Good control: action.recorded "Reinstalled", and the marker was cleared.
    Screenshots: raw-ios/ios-{1..4}.png and T3 device_screenshot (final state "Marker: empty", "Save marker" visible).

Proven vs missing

Proven on both platforms (API 35 emulator, iOS 27.0 simulator), with the candidate daemon: a corrupt or unmaterializable archive fails reinstall at preparation, before any uninstall or install, and the app data survives. A valid reinstall still clears data.
Missing or limited:

  • No explicit daemon log line for the uninstall dispatch on the good control. The marker clearing is the evidence.
  • Android's bad-archive error is typed (INVALID_ARGS). Apple's is UNKNOWN with the yauzl text. Behavior matches the unit/provider tests.
  • No rollback or protection against a corrupt APK/.app inside a valid archive, and none claimed.
  • No cancellation scenario on device.

Cleanup

  • Sessions audit4517-a and audit4516-i closed with the original CLI. Fixtures uninstalled with uninstall (exit 0). Both used close --shutdown (success).
  • T3 device_close(shutdown:true) failed on Android with "Device shutdown failed: The device hub could not complete the request." (same as the earlier 4426 run). The device was already off, so a plain device_close succeeded. The Apple device_close(shutdown:true) succeeded. Final device_list: open [], both assigned devices booted:false. bench-r1 and emulator-5554 were untouched.
  • Private daemon stopped by stopping its foreground exec task (TaskStop); pid 95573 confirmed exited. Private state dir, config and tokens removed. The throwaway signing keystore was deleted.

Last status: 4516+4517, exact SHA 88bfc20, native state proven (both platforms, per above), unpublished.

The initial iOS fixture crash attempt is excluded, as the native report states. Only the rebuilt fixture's complete empty → saved → failed-reinstall/saved → successful-reinstall/empty sequence is evidence. Apple runner binary provenance was not independently checked; the changed deployment ordering runs in the verified candidate daemon, and this PR changes no runner source. Native cancellation was not tested.

The native marker expectations were checked again against the saved raw JSON before publication. The absence of an uninstall log line is not treated as a dispatch trace: retained marker data and the successful-clearing control are the live behavioral evidence.

@github-actions

github-actions Bot commented Nov 13, 2026 •

Copy link
Copy Markdown
Contributor

Size Report

Metric Base Current Diff
Installed (including dependencies) 6.27 MB 6.27 MB +308 B
Package (unpacked) 6.27 MB 6.27 MB +308 B
Package (download) 1.90 MB 1.90 MB +123 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 22.7 ms 26.3 ms +3.6 ms
CLI --help 50.1 ms 52.2 ms +2.1 ms

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All reported issues were addressed across 10 files

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

@apex-sentinel-staging

apex-sentinel-staging Bot commented Nov 13, 2026 •

Copy link
Copy Markdown

Apex Sentinel · ✅ Approve

Since the last review: 0 new · 0 still open · 1 no longer raised

No open findings.

1 finding below minor hidden (user_settings.min_severity).

Reviewed with this repository's own Sentinel configuration (ddbfc855661f).

💬 Talking to Sentinel

In a finding's thread, reply with:

Command What it does
@apex-sentinel-staging note <context> Adds context; Sentinel re-checks the cited code
@apex-sentinel-staging pivot <why> Says the finding is wrong; Sentinel re-checks the cited code
@apex-sentinel-staging like Marks the finding as useful
@apex-sentinel-staging dislike <why> Marks it as not useful, so it is not raised again

On this conversation, comment @apex-sentinel-staging review to run a full review once again.

Earlier reviews

Review of 88bfc202 · 13.11.2026 22:56 UTC

Apex Sentinel · ✅ Approve

1 finding(s): 1 minor.

🟡 Minor (1)

Where Finding
test/integration/provider-scenarios/ios-lifecycle.test.ts:415 iOS corrupt-archive test asserts yauzl's error text, not a typed reason

1 finding below minor hidden (user_settings.min_severity).

Reviewed with this repository's own Sentinel configuration (ddbfc855661f).

@apex-sentinel-staging apex-sentinel-staging 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.

Apex Sentinel: 1 new finding on 88bfc20. The verdict and the full list are in the summary comment.

@thymikee

Copy link
Copy Markdown
Member Author

The code at 88bfc20 looks right, but two open review threads still need the test fixes described below before merge. The 23 checks are green, and they exercise the Apple and Android deployment routes this diff touches. There are no conflicts. I ran no tests and drove no device, so the native Android API 35 and iOS 27 retention evidence rests on your comment at this head.

Not blocking, and you can take or leave these: the harness in packages/platform-apple/src/deployment/runtime.test.ts#L190 casts the prepareArtifact callback and uses deployApp!, so typing it against the executor parameters and narrowing once would be clearer. In test/integration/provider-scenarios/ios-lifecycle.test.ts#L459 every call containing "uninstall" also contains "install", so one predicate on the tool verbs is enough. The reinstall help in packages/command-surface/src/management/install.ts#L41 repeats the long partial-failure text from website/docs/docs/commands.md#L688, so could help keep the short retention guarantee and let the docs page carry the detail?

These two open threads still apply and should be fixed before merge: #4533 (comment) (post-prepare cancellation test) and #4533 (comment) (typed no-app-bundle fixture). Please push your local fixes for both (24a20eb, not part of this review) and re-run validation at the new head.

@thymikee

Copy link
Copy Markdown
Member Author

Both requested test fixes are now pushed in 9d7993318de5d202ecb7c653ff099baca1f43e8f (24a20eb9 plus the typed no-app IPA fixture). I replied on and resolved both threads; the post-update thread query has no unresolved threads.

The exact-head local gate passed:11checks passed,8named owner skips,1provider deduplication,no failures or unrun checks. Full unit passed19,771 with10skipped;related tests passed390. The revised provider case detects the original uninstall-before-prepare order (control1pass,original runtime1fail at the no-uninstall assertion,restored control1pass). The cancellation case detects skipped cleanup,with identical source/test blobs permitting its recorded proof to carry over.

CI for this pushed head is still awaited. I am keeping the watch and will not merge.

Final-head local validation report

PR #4533 final feedback validation

PR #4533. Reviewed head 9d79933, base ddbfc85.
GitHub main (gh api) = ddbfc85. Actual origin/main (git ls-remote and local ref) = ddbfc85. merge-base = ddbfc85. Published head 88bfc20 (22 success, 1 skipped) is not validation of this head.
Pinned pnpm 11.17.0 (/Users/michal/.local/share/vite-plus/fallback-bin/pnpm; the default pnpm on PATH is 11.25.0 and was not used).
No source edits, push, devices, subagents or new checkouts. No stash.

1. Typed no-app provider fixture detects the original order (dirty patched evidence)

BASE tree t3-95bb651d-99fe5e13e5d1cdf6 was clean at ddbfc85 before edits. Patch = git diff --binary base..9d79933 (old-order/base-to-candidate.patch, SHA256 3188a313e00fb2914f701394ee02c711e18a37a8e0d3adab096d969477b6e420). git apply --check OK, then applied. Frozen install and build ran once (setup/base-*), exit 0.
Command (exact): pnpm exec vitest run --project provider-integration test/integration/provider-scenarios/ios-lifecycle.test.ts -t "Provider-backed integration iOS physical reinstall uses scripted devicectl provider"

Run Production runtime.ts Result
control1 patched (sha256 effa302d...ce30, equals 9d79933 blob) exit 0, 1 passed, 3 skipped
mutant exact ddbfc85 bytes (sha256 1f0b4708...f6a3), rebuilt exit 1, 1 failed, 3 skipped
control2 patched bytes restored, rebuilt exit 0, 1 passed, 3 skipped

Mutant diagnostic (old-order/mutant.stderr): AssertionError: Archive without an app must not reach uninstall or install: [["devicectl","device","info","details",...],["devicectl","device","uninstall","app","--device","device-1","com.example.demo"]] at ios-lifecycle.test.ts:420.
The earlier assertions (HTTP 400 and error code INVALID_ARGS, test lines 416-417) passed first. So the old order still returned the typed error, but dispatched uninstall before preparation. The failure is the intended invariant, not a type, setup or timeout failure. The no-uninstall/no-install assertion is what detects it.
Cleanup: patch reversed. BASE is at ddbfc85 with an empty git status --porcelain (old-order/base-final-status.txt). The managed tree and logs are left in place.

2. Reused cancellation proof (not rerun)

Source: 4533-cancellation-feedback/validation/logs, attributed to 24a20eb. control1 exit 0 (1 passed, 36 skipped). mutant (skips cleanup when signal.aborted) exit 1: expected ['prepare'] to deeply equal ['prepare','cleanup'], 1 failed, 36 skipped. control2 exit 0 (1 passed, 36 skipped). runtime.ts patched sha256 restore check OK.
Identity check: platform-apple runtime.ts blob d58e2d19e57ccce306670a787c8b7b1dc48fea57 and runtime.test.ts blob 540e4934b88225b0d80efe813084612a74ebf463 are identical at 24a20eb and 9d79933. The delta 24a20eb..9d79933 is only ios-lifecycle.test.ts (11 insertions, 11 deletions). The proof therefore carries over. It is patched isolated proof, not an exact-9d7993 gate. The old 24a20eb gate was cancelled during unit and I do not claim it passed.

3. Final-head gate (single run)

Reviewed tree clean at 9d79933 before install/build, after build, and after the gate. Frozen install and build exit 0 (setup/reviewed-*).
Command: pnpm --silent check:affected --run --head 9d7993318de5d202ecb7c653ff099baca1f43e8f --json, exit 0. Receipt: receipt.json (stdout), progress.log (stderr). Raw runner logs: gate/raw-run-PKIbs1/.
Receipt: status passed, exactCommit true, checkoutChanged false, failOpen false (reasons []). Initial and final head 9d79933, status empty, fingerprints equal (55ac32d2...). refs: base origin/main resolved ddbfc85, mergeBase ddbfc85. 10 changed files. The plan selected 20 checks.

Passed (exit 0): format, lint, typecheck, layering, fallow (--base ddbfc85), build, vitest-related, unit, command-docs, docs-links, docs-prose.
Deduplicated: provider-integration ("covered by related tests").
Skipped, not passed: coverage (GitHub-authoritative: Coverage); web-smoke (GitHub-authoritative: Apple/Linux smoke, Integration Tests, Lint/Typecheck & Package, Perf Nightly); replay-ios and replay-ios-device (parked, workflow_dispatch: Replay Manual / iOS); replay-macos (GitHub: Apple Smoke); replay-linux and linux-command-evidence (GitHub: Linux Smoke); replay-android (parked: Replay Manual / Android).
No check was "not run" other than the skips and the dedup above.
Counts:

  • vitest-related (non-unit projects, including provider-integration): 84 files passed, 390 tests passed.
  • unit (unit-core, apple-runner, fuzz-worker; the full-unit investigate(gates): model source-reading test dependencies before narrowing selection #4050 fallback, not narrowed): 2101 files passed, 19771 tests passed, 10 skipped (19781). Followed by tmpdir-leak and smoke checks inside check:unit, which exited 0.
  • command-docs: 12 passed.
  • Provider counts are those inside vitest-related. The named provider scenario was proven separately in section 1.

Limits

  • Native malformed-ZIP behavior stays UNKNOWN; no typed producer was invented.
  • Section 1 is dirty-patched evidence and the cancellation proof is reused. Only the gate at 9d79933 is exact-commit evidence, and it covers only the local runnable checks. GitHub-authoritative checks (coverage, smoke, native, device) must be read on the PR at the final head.
  • I did not read the ADR 4516/4517 Decisions or the 4368 Current state in this run; no ADR-dependent claim is made. AGENTS.md and the testing, test-authoring, test-experiments, gates and review docs were read; code-ownership.md was read only in part.
  • PR comments, reviews and published checks were not re-read, so the PR check state is not re-verified.

@thymikee

Copy link
Copy Markdown
Member Author

I found no problems in 9d79933. Both findings from the earlier review (#4533 (comment)) are now fixed, and nothing from it remains.

The post-prepare cancellation case now expects prepare then cleanup and rejects with the cancellation, and the iOS lifecycle integration test now asserts the typed INVALID_ARGS code with HTTP 400 instead of matching message text. The production code is identical to 88bfc20, so this delta is two test files only.

Both earlier inline threads (#4533 (comment) and #4533 (comment)) are fixed at this head, so the author can resolve them. No lower-priority threads still apply.

All 22 checks pass. No conflicts. Nothing else must happen before merge.

I read the code path for the two new tests but did not run them locally or run mutants against them. I also did not re-check the native device runs, which were at 88bfc20 and still cover the same production code. The integration fixtures use the host zip binary, as the Android scenario already does, and CI is green with it.

@thymikee thymikee added the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Nov 14, 2026
@thymikee
thymikee merged commit 2131de1 into main Nov 14, 2026
22 checks passed
@thymikee
thymikee deleted the t3/apple-test-implementation branch November 14, 2026 07:27
thymikee added a commit that referenced this pull request Nov 14, 2026
* origin/main:
  fix(android): carry an empty text on a node labelled by its content description (#4531)
  fix(ios): restore the back swipe's pre-swipe still span (#4515)
  fix(daemon-client): type the remote daemon refusal with its dispatch fact (#4532)
  fix: prepare reinstall artifacts before removing apps (#4533)
  fix(ios): classify the keyboard dismiss refusal by what it dispatched (#4536)
  fix(ios): press the navigation bar back button and speed up the back swipe (#4508)
  refactor(gestures): one owner for the densified gesture path; drop the unproven sendevent transport (#4239)
  fix(host-kit): keep Linux process identity stable across clock steps (#4438)
  fix(ios): bound the idle wait of keyboard dismiss and keyboard enter (#4454)
  fix(device-selection): fail an ambiguous device request with only the tied devices (#4506)
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.

Prepare Android reinstall materialization before uninstalling the app Prepare Apple reinstall artifacts before removing the installed app

1 participant