Skip to content

refactor: route install commands through platform runtime - #1758

Draft
thymikee wants to merge 20 commits into
mainfrom
refactor/adr19-install-deploy-unit
Draft

refactor: route install commands through platform runtime#1758
thymikee wants to merge 20 commits into
mainfrom
refactor/adr19-install-deploy-unit

Conversation

@thymikee

@thymikee thymikee commented Aug 11, 2026

Copy link
Copy Markdown
Member

Summary

Draft ADR 0019 Wave 3 install/deploy facet for #1739. This is the tracker-assigned cohesive unit for install, reinstall, push, and internal install_source; public install-from-source normalizes to install_source.

Exact head: 21ac50e7c683928c832501a7978fc50462fbc4cc, replayed only onto main at 07d528086b5922a6b74a645cc12124ec1489dbea. The range contains 20 install/deploy-owned commits only; no prerequisite commits were replayed into this PR.

  • Facts are the sole deployment support authority. The command capability bucket, requireCommandSupported wiring, legacy adapters/branches/tags/maps, and superseded helpers/tests are removed.
  • Each handler admits facts once and binds once: install/reinstall → deployApp; install_source → ensureReady, materializeAppSource, deployMaterializedApp; push → ensureReady, sendPushNotification. Readiness is provider-owned only: no daemon retry or fallback.
  • Platform-family packages own native sequencing. The wide root deployment host is gone; root supplies focused lazy Apple/Android/Harmony executor ports. No durable-tier machinery, fifth policy file, or global capabilities cutover was added.
  • The shared parametrized allocation is R20 boot, R21 apps, and R22–R25 install/reinstall/install_source/push; every named operation has a lexical owner in the existing table.

Review parity cells

Command surface Canonical route Bound use / operation(s) Retired path
install handleAppDeploymentCommand deployAppUse / deployApp session-deploy, APP_INSTALL_CAPABILITY, default install ops, provider adapter
reinstall handleAppDeploymentCommand deployAppUse / deployApp session-deploy, default reinstall ops, provider adapter
install-from-source handleInstallFromSourceDeploymentCommand via install_source readyMaterializeAndDeployAppUse / ensureReady, materializeAppSource, deployMaterializedApp source handler and provider installable adapter
push handlePushNotificationCommand readySendPushNotificationUse / ensureReady, sendPushNotification handlePushCommand and Apple capability closure

push and install_source expire an active ref frame immediately before their sole bound dispatch. Facts, readiness, and materialization failures preserve it; every dispatch attempt expires it, including a rejecting operation. No legacy device-ready import/call remains.

Facts denominator and coverage

D = install/reinstall deployment; S = install_source (including readiness); P = push.

Complete leaf-kind-provider cell D S P
Apple iOS (including unstamped iOS), iPadOS, tvOS, visionOS simulators available available available
Same Apple leaves, CoreDevice physical device available available unavailable: simulator-only
Same Apple leaves, XCTest physical device unavailable: unsupported-device-backend unavailable: unsupported-device-backend unavailable: simulator-only
macOS host/simulator sentinel; watchOS simulator/device unavailable: unsupported-platform-leaf unavailable: unsupported-platform-leaf unavailable: unsupported-platform-leaf
Invalid Apple emulator kind unavailable: unsupported-device-kind unavailable: unsupported-device-kind unavailable: unsupported-device-kind
Android emulator and physical device available available available
Android simulator sentinel unavailable: unsupported-device-kind unavailable: unsupported-device-kind unavailable: unsupported-device-kind
HarmonyOS emulator and physical device available unavailable: unsupported-platform-leaf unavailable: unsupported-platform-leaf
HarmonyOS simulator sentinel unavailable: unsupported-device-kind unavailable: unsupported-platform-leaf unavailable: unsupported-platform-leaf
Web (local or transport-composed), Linux, Vega unavailable: unsupported-platform-leaf unavailable: unsupported-platform-leaf unavailable: unsupported-platform-leaf
Direct WebDriver, active owned Android/iOS session with install capability available available; provider-owned readiness no-op unavailable: unsupported-provider-mode
Direct WebDriver with stale session or missing install capability unavailable: owner-capability-missing, before bind unavailable: owner-capability-missing, before bind unavailable: unsupported-provider-mode
Direct Limrun, active owned iOS simulator or Android emulator with deployment operations available available; provider-owned readiness no-op unavailable: unsupported-provider-mode
Direct Limrun without a live owner/deployment session unavailable: owner-capability-missing, before bind unavailable: owner-capability-missing, before bind unavailable: unsupported-provider-mode

Apple, Android, HarmonyOS, unavailable-local, WebDriver, and Limrun fact tests cover every cell. Production-composition stale-provider tests prove one inspection, zero gateway binds, and zero local execution for stale WebDriver and Limrun IDs. capabilities remains its Wave 6 legacy handler: one facts inspection projects only these four assigned surfaces, with descriptor/use drift coverage.

Planted-red / route evidence

  • Removing source-deploy or push ref-frame expiry leaves the frame active; tests require admission failure to preserve it and a rejecting dispatch to expire it.
  • Stale WebDriver ownership and Limrun liveness used to reach bind/local execution; production-composition tests prove one inspect, zero bind, and zero local operation.
  • Removing the awaitLimrunOperation rejection continuation produces the planted late-sendAsset unhandled rejection; request-local draining retains opaque provider operations through settlement before binding disposal.
  • Removing signal forwarding from BrowserStack/WebDriver upload, Limrun deployment, or Harmony unzip makes the in-flight cancellation tests fail.
  • Removing the Android reinstall cache transaction leaves stale fuzzy/alias resolution observable; Apple coverage proves whole-operation invalidation on prepare failure and concurrent repopulation.
  • The generalized cutover suite rejects missing lexical operation ownership; source-install and push call runtime.operations.ensureReady({}) directly in their owning handlers, so the structural proof is green.

Exact-head size accounting

Fresh same-host detached worktrees ran pnpm install --frozen-lockfile && pnpm build && node scripts/size-report.mjs at the release baseline, current main, and the exact head:

Metric 44c298d7f immediate base 07d528086 this head vs 44c298d7f vs base
JS raw 2,036,067 B 2,190,805 B 2,203,363 B +167,296 B +12,558 B
JS gzip 659,646 B 716,284 B 721,518 B +61,872 B +5,234 B
npm tarball 797,027 B 842,055 B 845,532 B +48,505 B +3,477 B
npm unpacked 2,781,186 B 2,934,531 B 2,947,123 B +165,937 B +12,592 B

The exact head has 187 JS chunks / 279 packed files; the immediate base has 179 / 271. The eight added clean-pack entries are focused deployment/supporting chunks (app-deployment, app-device-io, app-install, apps-simctl, apps2, byte-limit-stream, sdk, and appearance); no new dependency or durable resource is introduced. This remains exceptional cohesive-facet growth and requires independent size-budget acceptance.

UTF-8 changed-source accounting against 07d528086, excluding tests and fixtures:

Scope Added Removed Net
Root production src/** 46,779 B 45,039 B +1,740 B
Family/contract packages/** 65,301 B 20,430 B +44,871 B
Existing parametrized cutover table 3,908 B 61 B +3,847 B
Total non-test source 115,988 B 65,530 B +50,458 B

The ownership move specifically removes 45,039 root bytes while adding 65,301 package bytes: +20,262 B at that boundary. The remaining net growth is itemized in the table above; the cutover growth stays in the existing parametrized mechanism and no handwritten policy was added.

Draft blockers / residual risk

Keep this PR draft. Exact-head code/ADR rereview is clean, and supported-path iOS simulator deployment evidence now exists for every assigned surface. The iOS Smoke rerun is fully green, including the previously failing targeted XCTest, public CLI preflight, fixture-backed simulator E2E smoke, and host-focus canary. CI remains blocked only by the shared Stryker alias-ordering defect fixed in #1753; this unit does not duplicate that tooling patch. Explicit acceptance of the exceptional size budget remains required.

Residual live risk is limited to denominators unavailable locally: no Android emulator was booted, no HarmonyOS target or active WebDriver/Limrun provider lease was available, and personal iOS hardware was not used for a destructive app replacement. Those cells retain production-composition, cancellation, cache, and fail-closed fact coverage rather than a claimed live success.

Validation

  • pnpm check:affected --run && git push --force-with-lease passed from a clean committed tree at 21ac50e7c; it fail-open selected all 21 runnable checks.
  • Affected Vitest: 556 files / 4,468 tests passed. Changed-line coverage: 629/690 (91.16%); changed branches: 425/522 (81.42%).
  • The shared layering suite passed, including R22–R25 uniqueness and the parametrized cutover table’s owner-scoped planted-red cases. Daemon wire compatibility: 151 declarations, 0 changed/removed/added.
  • Live iOS simulator (iPhone 17 Pro, isolated state dir): local install and reinstall of the trusted fingerprinted AgentDeviceTester.app succeeded; public Node apps.installFromSource() materialized the canonical binary.tar.gz path and returned bundle identity com.callstack.agentdevicelab; push delivered a benign APS payload; open --foreground exposed the Agent Device Tester surface and exact-label verification succeeded. Session adr19-install-deploy-live was closed and the temporary localhost server was stopped.
  • Docs and skills were not changed: this is an internal runtime cutover with unchanged CLI surface and existing ADR/tracker evidence.

@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown

Size Report

Metric Base Current Diff
JS raw 2.19 MB 2.20 MB +12.6 kB
JS gzip 716.3 kB 721.5 kB +5.2 kB
npm tarball 842.1 kB 845.5 kB +3.5 kB
npm unpacked 2.93 MB 2.95 MB +12.6 kB

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 20.5 ms 21.0 ms +0.5 ms
CLI --help 52.3 ms 50.8 ms -1.5 ms

Top changed chunks:

Chunk Raw diff Gzip diff
dist/src/dispatch.js -2.8 kB -774 B
dist/src/src3.js +2.4 kB +660 B
dist/src/runtime3.js +2.2 kB +477 B
dist/src/runtime2.js +1.8 kB +462 B
dist/src/session.js +433 B +392 B

@thymikee

Copy link
Copy Markdown
Member Author

Adversarial review of exact head ab42b0428 found these blockers:

  1. P1 — push no longer expires the ref frame. push remains may-invalidate in the descriptor, but session-app-deployment.ts performs readiness and sendPushNotification without expireRefFrame. Restore expiry immediately before the bound push operation. Add red-before coverage proving admission failure preserves the frame while any dispatch attempt expires it.
  2. P1 — stale WebDriver devices can fall through to local execution. Provider ownership is currently tied to an active session. Once stale, ordinary selection can choose the local runtime, making the intended provider stale-session fact unreachable and violating provider-authoritative fail-closed behavior. Persist ownership independently of live capability and add a production-composition stale-device test proving one inspection, zero binds, and zero local operations.
  3. P1 — Limrun deployment is admitted without a live session. Parseable IDs claim ownership and production always supplies deployment callbacks, so admission reports available before discovering the absent session only after bind. Make session liveness part of the admission fact and cover the actual composed runtime with one inspection and zero binds for stale IDs.
  4. Integration — R19–R22 collide with refactor: route selector-resolution structural stages into typed policy #1744’s reserved R19. Rebase onto the rule-catalog allocation and assign unique IDs before merge.

All completed CI checks pass; iOS Smoke remains pending. Keep draft, rerun pnpm check:affected --run before pushing, and retain the stated live-device blocker.

@thymikee

Copy link
Copy Markdown
Member Author

Reviewed exact head ab42b04. Changes requested.

[P1] ADR 0019 defines the abandonment-safe migration unit as one command descriptor across its full denominator, with post-checkpoint units authorized and budgeted individually. This PR instead batches install, reinstall, install_source, and push behind one family route across 92 files. Please split these into independently reviewable descriptor units and rebase them onto the agreed current stack. The new R19-R22 allocation also precedes integration of the agreed selector R19/current stack, so it is not safe to land as-is.

[P1] This device-facing cutover still has no live supported-path evidence for install, reinstall, source install, or push. Green CI does not exercise those production command routes. Each resulting unit needs practical evidence on its supported device/provider denominator, with cleanup recorded.

[P2] The stack-base accounting removes 25,681 root source bytes but adds 42,549 package source bytes: +16,868 net source growth (+6,498 raw shipped, +1,742 gzip). That is exceptional rather than move-dominated and cannot be reviewed against ADR 0019 while four units share one after-the-fact budget. Record and obtain approval for each descriptor budget independently.

The facts-first admission, single-bind operation routes, fail-closed behavior, and preservation tests otherwise look coherent, and exact-head CI is green. No ready label until the split, rebase, per-unit budget, and live evidence are complete.

@thymikee

Copy link
Copy Markdown
Member Author

Coordinator allocation decision: preserve this as the single cohesive install/deploy family unit.

The original tracker assignment and explicit coordination brief allocate install, reinstall, push, and internal install_source together because they land one deploy facet and move the shared session-deploy.ts dispatch table atomically. Splitting those descriptors now would change the approved ownership and stack topology and would fragment shared mechanics rather than improve abandonment safety. The earlier split request is therefore superseded; do not create four branches.

The remaining valid readiness requirements still apply: exact-head adversarial rereview, unique rule IDs on the integrated catalog, honest per-route parity evidence, exceptional size justification for the cohesive facet, and practical supported-path live-device evidence or an explicitly accepted residual gap. Keep the PR draft.

@thymikee

Copy link
Copy Markdown
Member Author

Addressed the exact-head findings at 13c4451c9698061cd78c9266cb1b961776b8c6e5.

  1. push now calls expireRefFrame(session) immediately before the single bound sendPushNotification dispatch. The new regression pair proves facts/readiness admission failure preserves the frame, while a bound-operation rejection still expires it. The old implementation went red with the frame left active.
  2. Cloud WebDriver now retains provider ownership after lease release, while its facts expose a stale session as owner-capability-missing. The production-composition scenario takes the public facade through allocate/release and proves one inspection, zero gateway binds, and zero local loads. The old code went red because released ownership became false and local selection was reachable.
  3. Limrun facts now include actual runtime session liveness for deployment/readiness. The composed stale-ID scenario proves one inspection and zero binds; before the fix it reached the deployment bind and threw only there. Durable fenced app-log recovery remains intentionally bindable after deployment admission closes.
  4. The shared parametrized rows are now R20–R23; R19 remains reserved for refactor: route selector-resolution structural stages into typed policy #1744. No handwritten policy was added.

pnpm check:affected --run && git push passed at this head (4,786 tests; changed-line coverage 481/535, 89.91%). The PR body now carries the refreshed clean-worktree size table and exceptional cohesive-facet justification.

The later allocation decision supersedes the split request: this remains the one tracker-assigned install/deploy family facet. The PR stays draft while current CI/exact-head rereview, final rule-catalog integration, and supported-path live-device evidence or accepted residual remain outstanding.

@thymikee

Copy link
Copy Markdown
Member Author

Exact-head rereview of 13c4451c confirms the prior push-expiry, stale-WebDriver, Limrun-liveness, and R20–R23 fixes. Two P1 blockers remain:

  1. ADR 0014 ref-frame correctness — install_source never expires before dispatch. session-app-source-deployment.ts completes admission/materialization/retention and calls deployMaterializedApp without expiring the ref frame. Public install-from-source reaches this specialized route directly. Expire immediately before the bound deploy operation; add planted-red coverage that admission/materialization failure preserves the frame while any deploy dispatch attempt expires it.
  2. Android reinstall parity — whole-operation resolution-cache invalidation was lost. Legacy reinstallAndroidApp wrapped boot → resolve/uninstall → prepare/install in androidAppResolutionCache.invalidateWhile. The new runtime calls the host uninstall, which resolves through the cache, while install invalidates only afterward. Preserve pre/post whole-operation invalidation at the Android deployment owner interface and add a cached fuzzy/alias red-before regression.

Also refresh the PR body’s exact-head size/test/rule evidence after the next correction. Cohesive family allocation remains valid; no split is required. Keep draft, run pnpm check:affected --run before pushing, and retain the explicit live-device gap.

@thymikee

Copy link
Copy Markdown
Member Author

Addressed both remaining P1s at 81a00c83a150e295ba4aa3ea0556375243e2a402.

  1. install_source now calls expireRefFrame(session) immediately before its sole bound deployMaterializedApp dispatch. The production-route regression proves unavailable admission is one inspect / zero binds and preserves the frame; materialization failure preserves it; and a rejecting deploy callback observes the frame already expired, so every deploy attempt expires it.
  2. Android reinstall now wraps the whole boot → resolve/uninstall → prepare → install lifecycle in the Android deployment owner’s withInvalidatedAppResolutionCache. The cache remains platform-owned and lazy-loaded through the runtime host; no daemon cache patch was added. The regression starts with cached fuzzy Maps → com.example.stale, proves the fresh com.example.current resolution is used, and verifies the cache is cleared again after a partial failure.

Planted red before these corrections produced expected "expired", received "active" for the source-deploy attempt and stale fuzzy target: com.example.stale for Android reinstall. The focused suite passed (25 tests), then pnpm check:affected --run && git push passed at this exact head (4,791 tests; changed-line coverage 488/543, 89.87%). The PR body now records the refreshed parity/facts evidence and clean-worktree size accounting: +8,061 B raw / +2,107 B gzip / +1,909 B tarball / +8,061 B unpacked over boot; this repair itself is +369 / +97 / +91 / +369 B from 13c4451c9.

The PR remains draft. Exact-head rereview, current CI, final prerequisite/rule-catalog integration, exceptional cohesive-facet size approval, and supported-path live-device evidence or an accepted residual are still blockers; no real-app success is claimed.

@thymikee

Copy link
Copy Markdown
Member Author

Re-reviewed exact head 81a00c8. The functional repair is sound: push and install_source expire ref frames immediately before dispatch while admission/materialization failures preserve them; stale WebDriver and Limrun sessions fail facts admission before bind or local fallback; Android reinstall restores whole-operation cache invalidation; and R20-R23 preserve the allocated selector R19. The coordinator decision also confirms this remains one cohesive install/deploy family unit, so no split is required. Exact-head CI is fully green and touched-file topology remains within limits.

This is still not ready:

No ready-for-human label yet.

@thymikee

Copy link
Copy Markdown
Member Author

Exact-head rereview of 81a00c83: all prior install-family code findings are fixed, but one P1 scope blocker remains.

session-inventory.ts silently migrates the still-legacy capabilities command to a global one-inspectFacts projection for every runtime-backed command. Its descriptor remains legacy, it has no cutover row, and #1739 schedules capabilities as its own last-wave unit. ADR 0019 §9 forbids moving another handler to facts inspection as a side effect.

Restore the prior capabilities behavior and retain only the narrow transitional projection required for this install/deploy family, or obtain an explicit capabilities-unit allocation. Do not claim or ship the global capabilities migration from #1758.

Everything else now passes adversarial review: both expiry fixes, Android cache invalidation, provider fail-closed ownership, route shape, IDs, tier, and CI. Exceptional size still needs reviewer acceptance and live supported-path evidence remains absent. Keep draft and run pnpm check:affected --run before pushing.

@thymikee

Copy link
Copy Markdown
Member Author

Addressed the remaining scope P1 at be45ac17d82a89600dec0126fa80cf2d1ac4909d.

capabilities is again a legacy handler: its original appLogAdmissionUse, networkAdmissionUse, and screenRecordingAdmissionUse binds are restored, and all non-install-family entries use the prior isCommandSupportedOnDevice behavior. The removed global requiredRuntimeOperationsForCommand projection is not replaced.

The sole transition retained here is intentionally narrow: one side-effect-free facts inspection projects only install, reinstall, public install-from-source, and push from this unit’s existing use declarations. A facts failure hides only those four entries fail-closed; it cannot change the availability result for another legacy command. No capabilities descriptor, general facts cutover, table row, fifth policy, package dependency, or durable state was introduced.

Planted-red evidence: before this repair, a test made bootTarget unavailable while deployment facts were available and observed expected [...] to include 'boot'; the global projection hid boot. The repaired focused pair passes (7 tests), including the facts-inspection-failure case. A clean pnpm check:affected --run && git push passed at this head: 4,794 tests and 490/545 changed lines (89.91%).

The draft body now records the refreshed detached-worktree size comparison (+8,329 B raw / +2,191 B gzip / +2,008 B tarball / +8,329 B unpacked over 3ed473400) and keeps the exceptional cohesive-facet size approval plus live supported-path evidence as explicit blockers. PR remains draft.

@thymikee

Copy link
Copy Markdown
Member Author

CI triage for exact head be45ac17d:

The only failed lane is iOS Smoke’s unchanged XCTest RunnerTests.testBareTypeUsesTappedInputWhenSoftwareKeyboardIsHidden in apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift:396. It expected hardware-keyboard but observed an empty field after the synthesized-first-responder route. The current scope correction does not touch apple/runner, that test, or text-entry mechanics; it only restores the daemon capabilities legacy bindings and narrows install-family availability projection.

I am not patching this iOS runner failure from the install/deploy unit. It needs the Apple runner owner or a rerun if classified as the existing simulator text-entry flake. All other reported checks on this head pass; the PR remains draft for this lane as well as the documented size and live-device blockers.

@thymikee

Copy link
Copy Markdown
Member Author

Exact-head full audit of be45ac17: the capabilities scope correction and all prior functional fixes are clean. Three architecture/quality blockers remain:

  1. Ownership/deletion: AppDeploymentRuntimeHost is still a wide cross-family semantic service bag; root platform-runtime-app-deployment-host.ts owns prepare/install/uninstall/push/open while family packages call back into it. Move concrete mechanics/tests into family owners or inject honest focused low-level executor capabilities, then delete superseded install wrappers retained only by tests.
  2. Cancellation: host install/uninstall/push/open signatures omit AbortSignal; native simctl/adb/hdc side effects do not receive the request binding signal. Thread it through and add planted in-flight abort coverage.
  3. Maintainability: packages/provider-webdriver/src/runtime.ts grew 475→567 LOC. Extract deployment ownership/session logic before adding more behavior.

Independently rerun/classify the unrelated red iOS Smoke test. Live supported-path evidence and exceptional size approval remain absent. Keep draft and run pnpm check:affected --run before pushing.

@thymikee

Copy link
Copy Markdown
Member Author

Reviewed exact head be45ac1. The global capabilities scope regression is fixed: legacy commands retain their prior paths and only the install family consumes the new facts projection. The planted bootTarget regression is valid.

Still not ready:

  • [P2] session-install-capability-projection.ts manually restates the descriptor-owned command-to-use mapping. The values currently match, but this is a second capability model that can drift from registry.ts. Keep the transition limited to these four surfaces while deriving each use from the descriptor source of truth.
  • [P2] AppDeploymentRuntimeHost remains a root cross-family semantic service bag. The root host dynamically imports concrete Apple/Android/Harmony install, uninstall, push, and open mechanics while family packages call back into it. Complete ADR 0019 ownership by moving those mechanics to family adapters and injecting only focused low-level capabilities.
  • Native Apple install/uninstall/push, Android install/uninstall/push/app-name, and Harmony install/open calls still do not receive the request binding AbortSignal; only artifact preparation, boot, and sleep do. Propagate cancellation through in-flight native calls and add planted abort coverage.
  • The exceptional +16,868-byte source growth remains unaccepted; this delta adds another 268 raw shipped bytes. packages/provider-webdriver/src/runtime.ts also remains above the 500-line tripwire at 567 lines.
  • No live supported-path install, reinstall, source-install, or push success is recorded. Stack/replay prerequisites remain unresolved, and iOS Smoke is still pending after the unrelated text-entry failure.

Keep draft and do not apply the ready label.

@thymikee

Copy link
Copy Markdown
Member Author

Addressed the full-audit findings at e5a717f4208d5421d0a679c4b3fe9369dc78b7b9.

  1. Deleted the root AppDeploymentRuntimeHost and its wide aggregate host file. The operation host now exposes only focused lazy Apple/Android/Harmony executor ports; family runtimes own deployment sequencing and the aggregate test wrappers are gone.
  2. Threaded the bound AbortSignal to simctl/devicectl, ADB/bundletool, and HDC deployment side effects. Planted-red tests each failed when one native call lost its signal: simulator install received no options, Android push received undefined, and Harmony install received only the timeout.
  3. Split WebDriver’s former 567-line runtime.ts into a 198-line facade, 297-line session module, and 147-line deployment module while retaining lazy metadata and fail-closed stale ownership.
  4. Independently reran the prior iOS Smoke failure: run 31546162362 passed. It remains classified as the unrelated non-reproducing Apple runner/simulator text-entry flake; new exact-head smoke lanes are pending.

pnpm check:affected --run && git push passed from a clean committed tree (4,903 tests; changed-line coverage 610/665, 91.73%). The PR body now has exact clean-worktree size comparisons against 44c298d7f and boot, the complete facts denominator/parity table, planted-red evidence, and an explicit current-head source-growth ledger. It remains a draft pending exact-head CI/rereview, prerequisite replay, size-exception acceptance, and supported-path live-device evidence or an accepted residual.

@thymikee

Copy link
Copy Markdown
Member Author

Reviewed exact head e5a717f. The wide cross-family host is removed, local deployment sequencing now belongs to the platform packages, binding cancellation reaches simctl/devicectl, ADB/bundletool, and HDC, and the WebDriver runtime split is clean.

Still not ready:

  • [P2] session-install-capability-projection.ts is unchanged and still manually maps the four commands to imported uses. The PR body now calls this descriptor-derived, but it is a second model that can drift from registry.ts. Derive the narrow projection from the actual descriptor declarations and add a drift regression.
  • [P1] Cancellation remains incomplete for provider cells. WebDriver upload and session.client.installApp(...) receive no request signal; Limrun receives a binding signal but ignores it around deployApp and deployMaterializedApp. These in-flight provider installs can outlive cancellation. Thread the signal through the provider operation contracts and add planted abort coverage.
  • [P2] This delta adds deployment behavior to the already 925-line src/platforms/android/app-lifecycle.ts, despite the extract-before-add tripwire. Move the executor mechanics into a focused Android module.
  • Exceptional full-head growth is now +46,255 non-test source bytes and +10,812 raw shipped bytes over boot, without independent acceptance. No live supported-path install/reinstall/source-install/push success is recorded; stack and refactor(layering): give each colliding rule id its own number #1750 replay blockers remain; exact-head iOS Smoke is still pending.

Keep draft and do not apply the ready label.

@thymikee

Copy link
Copy Markdown
Member Author

Exact-head rereview of e5a717f4 confirms the ownership move, native AbortSignal plumbing, WebDriver split, and all earlier functional fixes. Three items remain:

  1. P1 — Apple reinstall lost whole-operation cache invalidation. Package-owned uninstall → prepare → install lacks the outer invalidateIosAppResolutionCache transaction retained by legacy. Nested clears leave a gap where concurrent display-name resolution can repopulate stale identity, especially when preparation fails. Add a focused Apple cache-scope executor port, wrap the complete reinstall, and prove planted-red partial-failure/concurrent-repopulation behavior.
  2. P2 — capability projection still duplicates command→use mapping. Keep it narrow, but derive install/reinstall/install-from-source/push requirements through descriptor-owned metadata and alias normalization; add a drift guard so descriptor-use changes cannot leave capabilities stale.
  3. P2 — delete superseded wrappers/tests/exports now duplicated by packages: installIosApp/reinstallIosApp, installAndroidApp, and installHarmonyApp.

The unrelated iOS longpress smoke rerun is still classifying. Live supported-path evidence and exceptional size approval remain absent. Keep draft and run pnpm check:affected --run before push.

@thymikee

Copy link
Copy Markdown
Member Author

Exact-head evidence update — this supersedes the prior current-head/size lines in the PR description.

Head: 18b0b96e856a31cec20eda74809e50f53a72cfed (draft; base refactor/adr19-boot-unit at 3ed473400ebb8f29405912856e0bcfdff2b836fc).

Validation from one isolated, clean committed tree:

  • pnpm check:affected --run && git push passed: 4,903 tests; changed-line coverage 614/668 (91.92%); changed-branch coverage 413/498 (82.93%); all runnable checks passed.
  • The Apple whole-reinstall transaction now spans uninstall, artifact preparation, install, and cleanup through the focused platform executor port. Its planted-red test proves a concurrently repopulated fuzzy/alias entry persists after a prepare failure without that outer transaction, then is cleared with it.
  • The narrow transitional capability projection derives only install, reinstall, install-from-sourceinstall_source, and push from descriptor metadata plus alias normalization. Its drift guard verifies the exact declared use sequence; capabilities otherwise remains legacy Wave 6 behavior.
  • Superseded installIosApp, reinstallIosApp, installAndroidApp, and installHarmonyApp wrappers, exports, and wrapper-only tests are gone. R20–R23 remain the unique rows in the existing parametrized cutover table; no fifth policy file or capabilities cutover row was added.

Fresh same-host detached-worktree size reports:

Metric 44c298d7f boot 3ed473400 this head vs 44c vs boot
JS raw 2,036,067 B 2,183,020 B 2,194,324 B +158,257 B +11,304 B
JS gzip 659,646 B 714,183 B 719,015 B +59,369 B +4,832 B
npm tarball 797,027 B 840,013 B 843,188 B +46,161 B +3,175 B
npm unpacked 2,781,186 B 2,926,675 B 2,938,013 B +156,827 B +11,338 B

The current build emits 187 JS files / 279 packed files, versus boot's 179 / 271. This is still the exceptional cohesive-facet growth already called out in the PR, not a new dependency or durable resource; independent size-budget acceptance remains required.

This PR remains draft. No merge/readiness claim: exact-head GitHub CI and rereview are pending; final replay/rule-catalog prerequisites #1740, #1745, #1747, #1744 (and transitive DIRTY #1750) remain blockers; and no supported real-app install/reinstall/source-install/push live-device success has been obtained. The prior simulator had no safe repository app fixture; physical devices were lock/trust/Developer-Mode blocked; Android was unavailable. The live-device residual and size exception must be accepted independently.

@thymikee

Copy link
Copy Markdown
Member Author

Final rereview of 18b0b96e8: Apple cache scope, descriptor-derived projection, wrapper deletion, and all earlier route/ownership fixes are clean. Three blockers remain:

  1. P1 — provider deployment cancellation is incomplete. WebDriver and Limrun binding signals stop after materialization; upload/install operations receive no signal and can outlive request cancellation. Thread AbortSignal through provider deployment callbacks, upload, and install, with planted in-flight provider abort coverage.
  2. P2 — Android deployment mechanics remain in the 889-line src/platforms/android/app-lifecycle.ts. Wrapper deletion reduced LOC but did not satisfy extract-before-add. Extract focused deployment executor/cache/install-uninstall/bundletool mechanics and mirror tests.
  3. P2 — Harmony archive bundle-name resolution runs unzip without the binding signal. Thread signal through resolver/runCmd and add the missing-signal regression.

Exact-head CI is fully green, but body size/evidence is stale: CI is about +11.3k raw/+4.8k gzip/+3.2k tar/+11.3k unpacked; update after correction. No supported-path live evidence exists and exceptional growth remains unaccepted. Keep draft and run pnpm check:affected --run before push.

@thymikee

Copy link
Copy Markdown
Member Author

Reviewed exact head 18b0b96. The descriptor-drift finding is fixed: capability projection now derives the use from commandDescriptors, with the four-command set only bounding this migration. Obsolete high-level install wrappers are removed and Apple reinstall cache invalidation is restored; all exact-head CI, including iOS Smoke, is green.

Two code findings remain.

[P1] Provider cancellation is still incomplete: WebDriver upload/install has no binding AbortSignal, and Limrun applies the signal only to materialization while deployApp/deployMaterializedApp can continue after request cancellation. Thread cancellation through both provider operations and add in-flight abort regressions.

[P2] Android deployment mechanics remain in the 889-line src/platforms/android/app-lifecycle.ts; extract the signal-aware install/uninstall/bundletool/package-inventory work into a focused deployment executor module rather than adding behavior to a >500 legacy file.

Readiness also remains blocked by absent live supported-path success, unaccepted exceptional size, stale exact-head size/body accounting, and unresolved stack dependencies. No ready-for-human label.

@thymikee

Copy link
Copy Markdown
Member Author

Addressed the final exact-head findings at 85bb4b4f25099b63344d4a239a3f4aae35a3c738.

  1. WebDriver now propagates the bound AbortSignal through BrowserStack upload, WebDriverClient.installApp, retry delay, and timeout composition. Limrun’s deployApp and deployMaterializedApp callbacks receive the same signal; its opaque WebSocket operations race cancellation safely because that API exposes no native abort hook. In-flight planted-red tests previously observed the missing or un-aborted signal and now pass.
  2. Android deployment/cache/install-uninstall/bundletool mechanics moved from the former 889-line lifecycle file into focused app-deployment.ts and app-deployment-resolution.ts, with the deployment test mirrored and parser tests separated. app-lifecycle.ts is now 611 LOC and no longer owns that deployment work.
  3. Harmony’s archive bundle-name resolver now forwards the binding signal to unzip; the planted-red resolver test previously observed no signal.

pnpm check:affected --run && git push passed from a clean committed tree: 4,925 tests, changed-line coverage 722/790 (91.39%), changed-branch coverage 474/574 (82.58%). I refreshed the PR body with exact clean detached-worktree size reports: +12,005 B raw / +5,171 B gzip / +3,444 B tarball / +12,039 B unpacked over boot, plus the full parity denominator, source-growth ledger, and residual-risk record.

The PR remains draft. New CI and rereview, final prerequisite replay, independent size acceptance, and supported real-app live-device evidence (or an accepted residual) remain unresolved.

@thymikee

Copy link
Copy Markdown
Member Author

Final exact-head review of 85bb4b4f: implementation, structure, and all CI are clean, but two required evidence cells remain:

  1. Limrun abort proof is too shallow. Current test stubs deployment callbacks and proves only binding→callback signal. Removing signal forwarding in runtime or the actual Android/iOS upload/install race handling leaves it green. Add planted in-flight tests through real Limrun runtime/provider seams: Android getOrUpload/sendAsset and iOS getOrUpload/installApp/listApps as applicable, proving prompt caller rejection and handled opaque continuation.
  2. WebDriver unsupported-install cell is untested. Add an active provider-session fact/bind test for AWS Device Farm’s unsupported install capability, asserting deploy/materialize facts unavailable and operations absent/no bind execution.

Everything else audits clean, including extraction, cancellation chains, facts/routes, deletion, cutover, and R7/R9/R10. After tests, run pnpm check:affected --run and push. Keep draft for size acceptance, live evidence, and prerequisites.

@thymikee

Copy link
Copy Markdown
Member Author

Reviewed exact head 85bb4b4. Android deployment mechanics are genuinely extracted into focused 191/109-LOC modules; WebDriver cancellation now reaches BrowserStack/WebDriver fetch and retry delay end-to-end; body/size accounting is current; all exact-head checks are green.

Two findings remain.

[P1] Limrun cancellation is still caller-only: awaitLimrunOperation races the opaque sendAsset/installApp/listApps promises and rejects promptly, but the remote mutation continues after request cancellation and binding disposal. The new runtime tests use fake callbacks that reject on the signal, so they do not prove the shipped Limrun transport stops or safely owns the mutation. Add an abortable transport/operation, or fence the started mutation as owned work whose completion/cleanup is awaited; add a production-path regression.

[P2] webdriver-client.ts grows from 498 to 536 LOC while adding request/retry/timeout behavior, crossing the >500 extract-before-add tripwire. Extract that transport policy into a focused module.

Readiness also remains blocked by no live supported-path deployment success, unaccepted exceptional growth, and unresolved stack dependencies. No ready-for-human label.

@thymikee

Copy link
Copy Markdown
Member Author

Evidence update for exact head ac2bf9e4a41c74626457290a571e0ed347176671 (supersedes the description’s prior-head validation line):

  • Added real Limrun runtime/provider in-flight cancellation coverage through Android getOrUpload and opaque sendAsset, plus iOS getOrUpload, opaque installApp, and post-install listApps. Each binds the actual platform runtime with the request signal and proves the real client seam receives or observes it.
  • Added active AWS Device Farm/WebDriver unsupported-install admission coverage. Actual provider facts mark deployment/source operations unavailable before bind; the composed route records one inspection, zero binds, zero local loads, and no provider operation execution.
  • Planted-red evidence: removing the signal from Limrun Android sendAsset causes the bounded in-flight abort test to time out; changing AWS install facts to supported makes the test reach an unexpected successful route. Both temporary mutations were restored before the green run.
  • This commit changes tests/fixtures only, so the previously reported production/package size metrics are unchanged.

pnpm check:affected --run && git push passed from a clean committed tree: 603 test files / 4,931 tests, 724/790 changed lines (91.65%), 475/574 changed branches (82.75%), Fallow/layering/package/wire compatibility green; daemon wire surface unchanged (151 declarations).

Keep draft: fresh CI is pending, as are prerequisites, independent exceptional-size acceptance, and supported-path live-device evidence (no new live claim).

@thymikee

Copy link
Copy Markdown
Member Author

Evidence rereview of ac2bf9e4: production remains identical to the previously clean head and CI is fully green, but two mutation-resistant proofs are still missing:

  1. Limrun real-seam tests abort promptly but fulfill every opaque provider promise afterward. Add one sendAsset or installApp case that aborts the caller, then rejects the provider promise, proving the late continuation is observed/handled without unhandled rejection. Removing awaitLimrunOperation’s rejection continuation must make it fail.
  2. Active AWS test proves unavailable facts and production-route zero bind, but never asserts the owner binding exposes no deployment operations. Add a direct active-AWS owner binding assertion that deployApp, materializeAppSource, and deployMaterializedApp are undefined.

Afterward run pnpm check:affected --run and push. Keep draft for unaccepted size, absent live success, and prerequisites.

@thymikee

Copy link
Copy Markdown
Member Author

Addressed the two mutation-resistant evidence findings at e6c3938.

  • The real bound Limrun Android sendAsset seam now rejects after the caller aborts. The regression verifies prompt caller abort, then rejects the opaque provider promise and observes a complete event-loop turn with zero unhandled rejections. Planted red: temporarily removing awaitLimrunOperation’s rejection continuation produces the late sendAsset unhandled rejection, then the restored continuation passes.
  • The active AWS Device Farm owner test now calls the actual provider runtime binding directly. For deployApp, materializeAppSource, and deployMaterializedApp, facts are unavailable with owner-capability-missing and the corresponding bound operation is undefined. The existing public production-composition test still proves one facts inspection, zero binds, zero local loads, and zero provider execution. Planted red: removing the deployment-fact guard exposes deployApp and fails the new assertion.

This is test-only evidence, so the PR’s production/package size accounting is unchanged. The body now names this exact head and the fresh clean-tree gate: 603 test files / 4,933 tests; changed-line coverage 726/790 (91.90%); changed-branch coverage 475/574 (82.75%); all runnable local checks passed.

The PR remains draft: fresh CI/rereview, final prerequisite replay, exceptional size acceptance, and supported-path live-device evidence or an accepted residual are still blockers.

@thymikee

Copy link
Copy Markdown
Member Author

Reviewed exact head e6c3938f. This test-only delta does not resolve the remaining production findings.

[P1] Limrun request cancellation still rejects only the caller while the opaque provider mutation continues beyond request-binding disposal. The new integration tests demonstrate that gap: after the caller rejects on abort, they must manually resolve the still-running sendAsset/installApp/listApps promise in finally; the late-rejection case proves only that the detached promise is observed, not that the remote mutation stops or remains owned through completion. Provide an abortable transport operation, or fence/adopt the started mutation and await its completion/cleanup before disposal, with a production-path regression.

[P2] packages/provider-webdriver/src/webdriver-client.ts remains 536 LOC after adding request/retry/timeout policy and still needs extraction into a focused transport-policy module.

Exact-head CI is still pending. Readiness also remains blocked by absent live supported-path deployment success, unaccepted exceptional size growth, and unresolved stack dependencies. No ready-for-human label.

@thymikee

Copy link
Copy Markdown
Member Author

Addressed the exact-head P1/P2 at a6e46d61ffd19e261391a08991e7f6d157dc49e3.

  • Limrun operation ownership: its opaque WebSocket deployment transport exposes no per-call abort API, so I did not claim remote cancellation. loadLimrunPlatformRuntime now carries a request-local operation drain through the actual bound deployment runtime into Android getOrUpload/sendAsset and iOS getOrUpload/installApp/listApps. Caller abort still rejects promptly, but binding disposal awaits every accepted opaque source promise to settlement before the provider session is released. It introduces no durable resource or daemon policy.
  • Production-path proof: real bound Android sendAsset abort coverage now proves that disposal remains pending after caller abort and completes only once the provider operation settles. The late-rejection case then proves zero unhandled rejections. Planted red before the drain was bindingDisposed === true while sendAsset was pending; temporarily removing awaitLimrunOperation's reject continuation produced the captured late sendAsset unhandled rejection. Both mutations were restored.
  • WebDriver extraction: request/retry/timeout/error policy moved from the 536-LOC client into focused webdriver-transport.ts (182 LOC); webdriver-client.ts is now 389 LOC and retains session/domain semantics. Its mirrored transport test proves abort interrupts an in-flight retry delay after one request.

pnpm check:affected --run && git push passed from a clean committed tree. Exact-head changed-line coverage is 769/841 (91.44%); changed-branch coverage is 511/615 (83.09%). Clean detached-worktree size is +12,603 B raw / +5,360 B gzip / +3,594 B tarball / +12,637 B unpacked over boot; the exceptional cohesive-facet budget remains explicitly unaccepted. Fresh CI has no failure yet but remains in progress.

The PR remains draft: live supported-path deployment evidence, final prerequisite replay (#1740/#1745/#1747/#1744 plus transitive #1750), size acceptance, CI completion, and exact-head rereview remain blockers.

@thymikee

Copy link
Copy Markdown
Member Author

Reviewed exact head a6e46d61. The prior code findings are resolved. Limrun now retains each accepted opaque deployment operation in a request-local drain through settlement, and binding disposal awaits that drain before response construction and lock release. WebDriver request/retry/timeout policy is cleanly extracted into the focused 182-LOC webdriver-transport.ts, reducing webdriver-client.ts from 536 to 389 LOC. The production-path regressions are non-vacuous, and all exact-head CI is green. I found no new code issue; ready-for-human from code review.

Final readiness remains blocked only on non-code evidence/coordination: supported-path live install/reinstall/source-install/push success or an accepted residual, explicit acceptance of the exceptional +50,755 B non-test source budget, and replay/resolution of the draft stack dependencies. Please also update the stale PR-body sentence saying selectors-1 is pending; that check has passed.

@thymikee thymikee added the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Aug 12, 2026
@thymikee
thymikee force-pushed the refactor/adr19-boot-unit branch from 3ed4734 to a200645 Compare August 12, 2026 07:26
@thymikee
thymikee force-pushed the refactor/adr19-boot-unit branch from fcb2f35 to 9d2337f Compare August 12, 2026 09:42
@thymikee

thymikee commented Aug 12, 2026

Copy link
Copy Markdown
Member Author

Additional exact-head CI classification: iOS Smoke failed only RunnerTests.testHungCustomActionReadIsContainedAndRecovers() in the Apple XCTest runner custom-action recovery suite. The #1758-owned range above c7b5a241 does modify Apple deployment executor files, but it does not modify the apple/runner/AgentDeviceRunner/** runner implementation or the custom-action recovery test involved. This is not an install/deploy route or platform executor failure. I am leaving it to the Apple runner owner for rerun/classification rather than widening this unit.

The draft remains blocked on this lane, the already-documented merged-#1744 Stryker sandbox import failure, live supported-path evidence, and size acceptance.

An error occurred while trying to automatically change base from refactor/adr19-boot-unit to refactor/adr19-parametrized-cutover-gate August 12, 2026 09:55
@thymikee thymikee removed the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Aug 12, 2026
@thymikee
thymikee force-pushed the refactor/adr19-install-deploy-unit branch 2 times, most recently from c6552f1 to fbacabf Compare August 12, 2026 10:52
@thymikee
thymikee changed the base branch from refactor/adr19-boot-unit to main August 12, 2026 10:55
@thymikee
thymikee force-pushed the refactor/adr19-install-deploy-unit branch from fbacabf to 21ac50e Compare August 12, 2026 11:01
@thymikee

Copy link
Copy Markdown
Member Author

Reviewed exact head 21ac50e7. The rebase only incorporates base Apple/macOS fixes; the install/deploy production route is unchanged and code review remains clean.

Exact CI is not ready. Nine mutation shards deterministically fail during the initial Stryker dry run, before mutation, because the sandbox cannot resolve @agent-device/selectors/engine imported by src/core/selector-pipeline.ts. This is a base selector/tooling integration issue, not an install-delta finding or hosted-infrastructure flake; rerunning alone will not fix it. Correct the base alias/tooling, rebase, and rerun.

The iOS Smoke failure is an unrelated simulator typing flake in testBareTypeUsesTappedInputWhenSoftwareKeyboardIsHidden; exact base passed the same targeted case, but this head still needs an authoritative green rerun. Do not apply ready-for-human while CI is red.

Remaining readiness blockers are the absence of supported-path live deployment success (or an explicitly accepted residual) and explicit acceptance of the exceptional size budget.

@thymikee

Copy link
Copy Markdown
Member Author

Exact-head follow-up at 21ac50e7:

  • Live supported-path verification now covers all four assigned surfaces on iPhone 17 Pro simulator using the trusted fingerprinted Agent Device Tester artifact: local install, local reinstall, public Node apps.installFromSource() via path materialization/internal install_source, and push all succeeded. open --foreground exposed the exact Agent Device Tester surface; session adr19-install-deploy-live was closed and the temporary localhost server was stopped.
  • iOS Smoke attempt 2 is fully green, including the previously failing targeted XCTest, public CLI preflight, fixture-backed E2E smoke, and host-focus canary.
  • The remaining red mutation shards are the shared Stryker alias-ordering defect. PR test(ci): prove every registered gate is owned and reachable (#1429) #1753 now contains the exact planted correction (@agent-device/selectors/engine subpath aliases sorted before the bare package alias), and its mutation shards are executing successfully. I am not duplicating that tooling patch in this install/deploy unit.

The PR body carries the refreshed evidence. This stays draft until #1753 lands/replays and the exceptional cohesive-facet size budget receives explicit acceptance.

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