Commit Graph

16 Commits

Author SHA1 Message Date
Michał Pierzchała 46eff36f85 refactor: migrate focus to the request-bound device runtime (#1925)
* refactor: migrate focus to the request-bound device runtime

Wave 5's first unit (#1739, ADR 0019). `focus x y` and `find <q> focus` now
reach the device through one admitted, request-bound `focusPoint` operation
instead of the `handleFocusCommand` interactor leaf and its dispatch-table arm.

- New `FocusRuntimeOperations` contract with local and provider interactor
  binders, mirroring the screenshot/element-text seam rather than inventing a
  second way for one operation class to reach its mechanics.
- Exact-owner facts replace the capability bucket: apple simulator/device,
  android emulator/device/unknown, harmonyos emulator/device, linux device,
  web device, vega none, providers wherever their interactor is reachable.
  That is the retired bucket's cell table, restated as facts.
- `focus` leaves BASE_COMMAND_CAPABILITY_MATRIX and both hand-maintained
  overlays (HARMONYOS_SUPPORTED_COMMANDS, WEB_INTERACTION_COMMANDS).
- R40 is the new parametrized cutover row; `focusPoint` has exactly one owner.
- The `x y` positional parse moves to utils and is shared with the still-legacy
  touch siblings, so a migrated command cannot drift from them.

`find` stays legacy: this unit owns its focus leg only, its `type` leg still
dispatches, and R35 waits on the Wave 5 `type` unit.

* test(focus): cover the owning interactor binders, lower the find ratchet

Review follow-ups on #1925.

P1: focus-runtime.test.ts bound a fake focusPoint, so deleting the interactor
call inside bindLocalFocusInteractor left focus a successful no-op with every
test green. Adds packages/contracts/src/focus-runtime.test.ts, which executes
both binders and asserts resolver context, positional (x, y) forwarding, the
structured missing-provider failure, and that an already-cancelled request
never resolves an interactor at all.

Two planted mutants confirm it bites: removing
`await interactor.focus(input.point.x, input.point.y)` and transposing its two
arguments each fail exactly the two forwarding tests, while the daemon-level
focus and find suites stay green — which is the gap the reviewer named.

Coverage: find.test.ts shrank to 1204 lines when its focus assertion moved off
the dispatch mock; the ratchet pin follows it down.

* test(focus): add live Linux focus coverage to the desktop replay

The Linux `focus` claim rested on the provider scenario at command-contract
level. The desktop replay runs on real Linux hardware in the Smoke lane, so it
now runs a coordinate focus and re-asserts the session survived it.

Coordinate, not selector: the step exists to prove the migrated `focusPoint`
path executes on real hardware, so it must not be able to fail on match
ambiguity or CI layout drift.

Reclassifies focus contract -> live in the Linux coverage manifest and updates
the two pinned counts. The manifest gate is two-sided — a live claim must name
a command the replay actually invokes — so the claim cannot drift from the file.
2026-08-21 11:34:22 +02:00
Michał Pierzchała 17bdca76cc refactor: migrate wait to request-bound runtime (#1875)
* refactor: migrate wait to request-bound runtime

* fix: preserve native selector wait observation

* fix: classify wait observations as conditional

* refactor: compact conditional runtime declarations

* fix: isolate selector runtime intents
2026-08-20 15:59:38 +02:00
Michał Pierzchała 494eb52f66 refactor: migrate get to the request-bound device runtime (#1877)
* refactor: migrate get to the request-bound device runtime

`get` declares `elementReadRuntimeUse` (required `captureSnapshot`, preferred
`readTextAtPoint`), admits once from exact owner facts, refuses before binding,
and binds exactly once. Its capability bucket, the static HarmonyOS/Web command
sets that augmented it, and `requireCommandSupported` admission for `get` are
gone; `'get'` leaves the `createSelectorRuntime` capability union.

The neutral `readTextAtPoint` operation replaces the branch-per-family legacy
`read` dispatch on the `get` path. Every local family and both providers now
classify it exhaustively — Web, HarmonyOS, Vega and every provider row report it
unavailable, which is behaviour-preserving because the legacy dispatch had no arm
for them and threw on every call before falling back.

R36 is the new parametrized cutover row.

* fix(get): admit before the direct-iOS fast path; close the element-read outcome

Review blockers on #1877.

1. `dispatchGetViaRuntime` could complete the direct-iOS selector query before
   `resolveBoundGetRuntime`. Once `get` declares `device-runtime`, ADR 0019
   requires resolve -> admit -> bind before anything in the request path
   operates, so admission now runs first for every target shape and the fast
   path is a fast path *within* an admitted request. Regression: an eligible
   direct selector cannot operate when facts refuse admission.

2. `readTextAtPoint` returned `Promise<string>` and `readTextForNode` caught
   any throw and fell back, assigning a typed diagnostic after an untyped
   failure. It now returns a closed `ElementTextReadOutcome`; fallback happens
   only for the contract's classified reasons; unexpected errors propagate.
   The reason union is derived from its runtime list so the two cannot drift,
   and an unhandled reason is a compile error at the consumer.

This retires the generic catch the start record promised.

* feat(daemon): land the selector capture seam with get as its first consumer

Takes ownership of the request-bound selector capture seam from #1876, which
cannot ship standalone: with find's cutover deferred it had no consuming
command (ADR 0019 §10) and was not dead-code clean (check:production-exports
19 -> 20). `get` is its first consumer, so it lands here.

Adopts find's handoff as given. The one shape change, approved by the
coordinator: the selector family gets its own capture uses carrying a PREFERRED
`readTextAtPoint`, declared ALONGSIDE the snapshot uses so `snapshot`/`diff`
keep binding exactly what they bind today. The read is surfaced through the
existing arms of `bindSnapshotCaptureRuntime`, reusing the same
selectActiveAppSnapshot / selectSnapshotWithoutActiveApp selectors — no second
plan-to-operation dispatch.

`get` now runs through `createBoundSelectorRuntime`; `resolveBoundGetRuntime`
and its test are deleted as superseded, and `'get'` leaves the
`createSelectorRuntime` capability union.

The legacy read adapter survives for `find <q> get text` and is selected by
which command constructed the runtime — never by failure, family, environment,
or flag — so `get` cannot reach it. It retires in find's cutover, where the
last consumer moves.

* refactor: retire the read dispatch alias across both selector consumers

Read-only `find` now constructs a BOUND selector backend, so `get text` and
`find <q> get text` execute the same bound `readTextAtPoint` instead of one
binding it and the other dispatching the legacy `read`. This moves find's READ
LEG only: find's descriptor stays LEGACY_PLATFORM_EXECUTION and it claims no
cutover row.

With no consumer left, the whole chain goes: the `read` registry entry and its
`dispatch: {}` projection, `DISPATCH_HANDLERS.read`, `handleReadCommand`,
`interaction-read-legacy-dispatch.ts`, and the duplicate platform reader
branches it carried. `read` was the only `dispatch-alias` descriptor, so that
catalog group goes too.

Deleting the registry entry drops 'read' from DescriptorDispatchCommandName,
which makes a surviving DISPATCH_HANDLERS.read a compile error rather than
something R36 has to police. R36 now claims the retirement it can prove.

`find.test.ts` is over the size tripwire, so its handler invocation is
extracted to find-handler-fixture.ts and the pin lowered 1237 -> 1221.

* refactor(daemon): apply the seam addendum after #1876 was re-scoped

Two edits, per find's ADDENDUM.md:

1. `includeRects` returns to `buildRuntimeCaptureInput`. It was removed from
   #1876 as unconsumed; the selector capture path is genuinely its first
   consumer (a Web rect capture requests bounds explicitly), so it lands here
   under the same rule that moved the seam. `snapshot`/`diff` pass nothing.

2. The per-capture `signal` is dropped, not restored. `CaptureSnapshotInput`
   has no such field on this stack — it moved to `wait` (#1875) with the
   regression that proves per-poll abort and quiescence. `get` captures once
   per resolution and never polls, so nothing here needs it. The seam test and
   fixture coverage for it moves with the contract rather than being kept
   against a field that no longer exists.

* refactor(get): retire the direct-iOS selector shortcut

`get` declares device-runtime, so its request path must reach the platform only
through operations R36 declares. `dispatchDirectIosSelectorGet` reached
`runAppleRunnerCommand` through a path the row declares no operation for;
admitting before a bypass is not executing through the seam, so the bypass is
removed rather than ordered after admission. Every target shape — including the
simple iOS `id=` selector — now resolves through the bound capture.

`queryDirectIosSelector` itself stays: `offscreen-target-probe.ts` still
consumes it and it remains single-copy. `dispatchDirectIosSelectorIs` belongs to
`is` (#1883). Two get-only helpers (`readDirectIosGetSelector`,
`buildDirectIosGetResult`) became unreachable and are deleted with the caller.

Declaring `querySelector` as a fact-admitted preferred operation was rejected on
duplication, not correctness: the offscreen probe takes a plain session and
cannot consume a bound operation, so it would ship the query twice until Wave 5
moves the probe — the deferred-duplication shape this PR was already overruled
for on the `read` alias. It returns as a declared, §9-measured operation in a
later unit that also moves the probe.

Cost, stated plainly: `get text id=…` loses its tree-capture skip on iOS. No
fallback was added and the latency is not recovered elsewhere. R36's
singularExecution claim is now what the code does rather than aspirational.

* refactor: ride the Interactor seam for the element read; drop the bespoke host

Two operations of the same class were reaching their mechanics two different
ways: `findText` rides `Interactor` via `localInteractors.resolve`, while
`readTextAtPoint` had its own host port. That is duplication of MECHANISM, so
the read now rides the same seam.

`Interactor` gains `readTextAtPoint?`, implemented on the Apple, Android and
Linux interactors where those mechanics already live.
`src/platform-runtime-element-text-host.ts` and its `elementText` host wiring
are deleted; the contract binds through the resolver exactly as the snapshot
runtime does.

Size honesty: this removes an 89-line module but the four readers still have to
exist, so they moved into the interactors rather than vanishing. Net production
change is ~4 lines, not ~89. The duplication of mechanism is what is actually
fixed; Wave 5/6 retires the seam for both operations together.

Also from the size investigation:
- `ElementTextRuntimeExecution` was byte-identical to `SnapshotRuntimeExecution`;
  removed and reused, as `find-text-runtime.ts` does.
- Removed a stranded, stale comment in `selector-capture-binding.ts` that still
  claimed a duplication this branch had already retired.
- `FrozenUnavailablePlatformRuntimeFacts` is derived from its input type rather
  than restated, removing a 14-line clone group my new cell had pushed over the
  detector threshold.

* refactor: migrate is to the request-bound device runtime (#1883)

* refactor: migrate is to the request-bound device runtime

`is` declares the shared selector capture use, admits once from exact owner
facts, refuses before binding, and binds exactly once. Its capability bucket,
the static HarmonyOS/Web command sets that augmented it, and
`requireCommandSupported` admission for `is` are gone; `'is'` leaves the
`createSelectorRuntime` capability union.

Admission now runs BEFORE the direct-iOS selector fast path. ADR 0019 requires
resolve -> admit -> bind before anything in a `device-runtime` command's request
path reaches the device, so that query becomes a fast path *within* an admitted
request rather than a way around exact-owner facts. The rule is documented once,
on `createBoundSelectorRuntime`, replacing the two duplicated call-site comments
`get` and `is` were each carrying.

Declared behaviour change: `is` takes the active-app plan split, so the facts
decide per family. On iOS `appBundleId` is the XCUITest attach target — with no
tracked app the runner's own process comes to the foreground, displaces the app
under test, and the capture then answers confidently about the runner's own
blank screen. An iOS `is` on a session with no tracked app is now a typed
SESSION_NOT_FOUND refusal carrying the `open` hint. Refusing beats
displacing-and-lying. Android captures the real launcher in that state and is
unchanged, which is what the platform facts already encoded.

The two Apple watchOS cells move from capability-admitted-then-runner-failure to
a typed unavailable refusal, the same classification snapshot, diff, and get
already landed.

R37 is the new parametrized cutover row. `find` keeps `createSelectorRuntime`
and its `requireCommandSupported` call, so `captureData` stays optional and
`captureSnapshotWithInteractor` stays: this unit is not the last selector unit.

* fix(is): a failing iOS assertion fails instead of exiting zero

Reverses part of #557, on thymikee's explicit instruction.

`is` is an assertion: the docs state it "exits non-zero on failure". The
direct-iOS fast path broke that contract — it reported a failed predicate as a
completed command, so on device

    $ agent-device is text id=… "Wrong Expected Text"
    Passed: is text          (exit 0)

because `{ok: true, pass: false}` reaches `isCliOutput`, which renders
"Passed: is <predicate>" without reading `pass`. A failing assertion reported as
success lets a replay run on past a broken state. Now:

    Error (COMMAND_FAILED): is text failed for selector id=…:
      expected="Wrong Expected Text" actual="Apple Account, …"   (exit 1)

The renderer needed no patch: a negative can no longer produce a success
envelope, so it is correct by construction.

Direction chosen deliberately. Making the two paths agree could have gone either
way, and "an agent asked a question and got an answer" is a real argument for the
other one. This follows the DOCUMENTED contract rather than merely the incumbent
behaviour, and the alternative is a far larger change: a zero-exit `is` would
alter every platform and path, break scripts that rely on it failing the shell,
and needs its own PR, docs, and probably a major version. It is also already how
`is hidden` and `is exists` behave end to end.

PASSING assertion, and that arm still answers with zero captures (pinned). Only
the negative falls through — what #557's own summary asked for, "preserving
snapshot fallback for misses", refusing fallback only for hard failures like
ambiguity. The fall-through was #557's own design, never armed: the `| null`
return and the caller's `if (!payload) return null;` guard were unreachable.
This makes that dead guard live.

Measured on iPhone 17 (median of 9, warm daemon): predicate holds 0.14s / 0
snapshots, unchanged; predicate fails 0.25s / 1 snapshot. ~+0.11s on failing
assertions only.

Correctness gain beyond the envelope: the fast path evaluates a ONE-NODE tree, so
`visible` cannot see the ancestor geometry a list row inherits and its negative
can be wrong. Falling through re-asks the real tree and can turn a spurious
negative into a pass.

The #557 pin moved with its reasoning at the pin site.

* fix(layering): let a cutover row state a data-only admission retirement

Review blocker on #1883: R37 claimed `legacyRetirement.routeNames:
['WEB_QUERY_COMMANDS_WITH_IS', 'HARMONYOS_IS_SUPPORT']`. Neither identifier has
ever existed. They satisfied the non-empty shape check while proving nothing —
the vacuous registry claim AGENTS.md warns about, and a green gate that would
stay green if the deletion were reverted.

The cause was the model, not the row. Every `LegacyRetirementClaim` form names
something that must NOT exist, which a row can always satisfy by inventing a
name. `is` retired no module, route, or dispatch projection because it had none:
its legacy admission was a capability bucket plus membership in two static
platform command sets, so its real retirement is a DATA deletion the model could
not express. Rather than patch around that with sentinels or a per-command
policy file — both forbidden by the playbook — this generalizes the model.

`staticCommandSets` names the sets themselves and is proven from both sides:
each must still be DECLARED in production source, and must no longer list the
command. A fictional set fails the first half; a skipped deletion fails the
second. That is what an identifier-shaped claim cannot state.

R37 now claims HARMONYOS_SUPPORTED_COMMANDS and WEB_QUERY_COMMANDS, which is the
deletion it actually performed.

Planted red, both halves, against the real gate:

  [R37 is-runtime-cutover] 2 violation(s):
    (is cutover row):1 — claims retired static command set
      'WEB_QUERY_COMMANDS_WITH_IS', which no production source declares
    (is cutover row):1 — claims retired static command set
      'HARMONYOS_IS_SUPPORT', which no production source declares

  [R37 is-runtime-cutover] 2 violation(s):
    src/core/capabilities.ts:59 — static command set WEB_QUERY_COMMANDS still
      admits is

so the exact claim that shipped is now rejected by name, and so is restoring the
membership it claims to have removed. Mechanism cases live with the other
planted-row tests; layering goes 177 -> 181.

* test(is): pin the exit-code guarantee independently of what answers the predicate

Prep for the Blocker 1 retirement, which deletes `buildDirectIosIsResult` — the
function the #557 reversal fixed. The reversal's guarantee must not evaporate
with it, so it gets a case that does not know how the daemon decided.

`is` is documented to "exit non-zero on failure". The reversal proved that at
the JSON envelope; nothing pinned it at the CLI boundary, which is where the
defect was actually visible (`Passed: is text`, exit 0). This asserts the CLI
contract directly: a `predicate_failed` response exits 1 and never renders as
passed.

It survives the retirement untouched, because it asserts the outcome rather than
the path. Planted red with the exact pre-#1739 envelope the shortcut produced
(`{ok: true, data: {pass: false}}`): `exitSpy.calls` is `[]` — no exit call at
all — so the case fails, which is the regression it exists to catch.

Unpushed on purpose: the restack will carry it into the retirement cycle.

* refactor(is): retire the direct-iOS selector shortcut

thymikee's ruling (option b). `is` declares `device-runtime`, so its request path
must reach the device only through the operations R37 declares. It did not: a
simple iOS `id=`/`label=` target was answered by a direct XCUITest querySelector
without any capture, ordered after admission but not executing through the seam.

This is not retired because it was wrong. `wait` hypothesized that the degenerate
one-node evaluation mis-answers `is visible` for off-viewport nodes, traced it
through the code convincingly, then tested it on device and it did not reproduce
— XCUITest's own query is conservative about visibility, so the degenerate
evaluation never gets the chance. It is retired because it was an undeclared,
unmeasured bypass that made R37's singularExecution claim false: the same class
of untruth as the sentinel retirement names fixed in the previous commit.

Declaring querySelector as a real operation instead was rejected for a concrete
reason: offscreen-target-probe.ts consumes queryDirectIosSelector with a plain
session and cannot take a bound operation, so declaring it now would ship it
twice until Wave 5 moves the probe — the deferred-duplication shape that got
get's read deferral overruled. It returns as a declared, fact-admitted,
section 9-measured operation in the unit that also moves the probe.

Retired: dispatchDirectIosSelectorIs, its call site, buildDirectIosIsResult, and
resolveDirectIosSelectorQuery — each had exactly one caller, all on this path —
plus the ResolvedDirectIosSelectorQuery type they orphaned and two imports.
queryDirectIosSelector itself stays: the offscreen probe still consumes it and it
remains single-copy.

Latency cost, stated plainly and not softened: a held predicate on a simple iOS
selector goes from ~0.14s with no capture to ~0.25s with one, measured as the
median of 9 warm runs on iPhone 17. There is no fallback and no fast path.

R37's comment finally describes the code: "every predicate answers from the
resolved tree" was written while the shortcut existed. Its scope is now stated
too, so it is not read as absolute — the Android foreground-blocker diagnostic
still reaches adb on the failure path, where it cannot produce or change a
verdict; that edge is pre-existing, co-owned with wait, and recorded as Wave 6
denominator work with R22's appState as its declared replacement.

Seven tests lost their subject. Those whose only content was the shortcut's own
mechanics are deleted; the outcome-level ones are retargeted and keep asserting
what survives.

---------

Co-authored-by: agent <agent@local>

* fix(contracts): a falsely advertised element read fails as a contract bug

An owner whose facts advertised `readTextAtPoint` but whose interactor cannot
perform it was reported as `{ status: 'unreadable', reason: 'surface-not-readable' }`.
That put a contract violation inside the closed reason set that licenses falling
back to the captured tree, so `get text` answered from potentially stale snapshot
text precisely because the runtime lied about itself. ADR 0019 §2 requires the
mismatch to fail as `runtime-contract-invalid`; it now throws.

Removing the only producer of `surface-not-readable` made that reason dead: no
path can reach it, since an interactor that HAS the read maps a blank or absent
answer to `no-text-at-point` via `elementTextRead`. Dropped from the union, its
consumer switch arm, and both test lists. `classifiedFallbackReason`'s `never`
arm stays — it is what makes adding a reason a compile error rather than a
silent untyped fallback.

Deduplication found while auditing the change:

- `invalidRuntimeContract` was module-private in `platform-runtime.ts`. It now
  owns its own module so both runtime modules share one construction. It is
  deliberately not exported through the platform facade: that facade must stay
  exhaustive over its sources, which would make this a public symbol with no
  external consumer.
- The 8-field runner execution projection was written out three times
  (`snapshot-runtime-capture-input.ts`, `interaction-read.ts`,
  `screenshot-runtime.ts`). One `runtimeExecutionFromContext` now serves all
  three; `screenshotExecutionFromContext` keeps its name and delegates, since
  `ScreenshotRuntimeExecution` and `SnapshotRuntimeExecution` are the same type.
  Dropping a field here silently strips request id, log/trace paths, XCUITest
  overrides, or runner lease context — an operation that still answers but runs
  unconfigured, which is exactly the defect the wait unit hit as a P1.

Red before green: with the old guard restored the new regression fails with
"Missing expected rejection" — the call resolves instead of throwing, which is
the silent degradation it exists to forbid.

---------

Co-authored-by: agent <agent@local>
2026-08-20 15:59:38 +02:00
Michał Pierzchała d8e03aea9b refactor: migrate screenshot to request-bound runtime (#1878)
Retires the last dispatchCommand edges for screen capture: the generic-route
command, the sparse-snapshot fallback, and the Android snapshot-timeout evidence
capture all admit exact owner facts and bind once (ADR 0019, cutover rule R39).

--overlay-refs becomes part of the declared use, so a target that can capture
pixels but not a tree is refused before anything is written to disk.
2026-08-19 17:33:48 +02:00
Michał Pierzchała 79dddaf781 refactor: tighten viewport runtime facts (#1873) 2026-08-19 11:06:18 +02:00
Michał Pierzchała 37b1bc8cbd refactor: migrate viewport to request runtime (#1864)
* refactor: migrate viewport to request runtime

* fix: preserve viewport cutover evidence
2026-08-19 10:38:27 +02:00
Michał Pierzchała d76e0f94e9 refactor: migrate snapshot to device runtime (#1779)
* refactor: migrate snapshot to device runtime

* refactor: complete snapshot runtime policy cutover

* test: enforce snapshot owner-facts admission

* refactor: consolidate desktop snapshot capture

* fix: scroll to visible iOS smoke targets

* fix: close snapshot cutover alias bypasses

* fix: constrain snapshot admission identity flow

* fix: enforce snapshot admission through owner facts

* fix: adapt replay source tests to snapshot runtime
2026-08-18 15:49:24 +02:00
Michał Pierzchała d8a7d03faf refactor: route application lifecycle through runtime facts (#1759)
* refactor: route application lifecycle through runtime facts

Moves the canonical `open`, `prepare`, `close` and internal `runtime` descriptors
behind package-owned lifecycle bindings admitted from device runtime facts, while
daemon request/session policy and public response construction stay put.

Based on main, which already carries the boot unit, the parametrized cutover gate
and the apps unit. Readiness is package-owned there, so the Apple and Android
bindings call ensureAppleReady/ensureAndroidReady rather than a root readiness
bag; ensureAppleReady gained an onColdBootStart hook so open keeps warming the
runner cache in parallel with a cold boot, and a narrow markBooted port publishes
readiness' fresh observation so a flow still makes one simctl listing.

Cutover rows take R24-R27, clear of the accepted catalog and the sibling install
stack, and cutoverTableDefects rejects a duplicate rule id.

Two defects this unit introduced are fixed here rather than shipped:
`open <app> <url>` dropped the URL on a first open, and test-IME activation was
first fatal on an unobtainable helper and then over-caught. Helper unavailability
is a typed non-activation outcome now; fence, lock and post-record failures
propagate.

The duplication the unit had accumulated is gone: one runtime-admission module
instead of five per-command copies, one direct-lifecycle binding factory instead
of six hand-rolled packages, one transport-hint predicate, one session
finalization path, and no identity-wrapper module.

* fix: allocate lifecycle cutover rows after deployment

* chore: preserve lifecycle union reconstruction

* fix: reconcile lifecycle runtime stack

* refactor: tighten lifecycle runtime topology

* refactor: remove superseded runtime adapters

* fix: preserve stacked runtime cutovers

* test: preserve migrated runtime ownership

* test: move Android deployment retry ownership

* test: extract runtime hint fixtures

* fix: preserve lifecycle stack invariants

* fix: complete lifecycle runtime cutover

* fix: remove lifecycle cutover residue
2026-08-16 15:13:10 +02:00
Michał Pierzchała 66cca1a5b8 refactor: route install commands through platform runtime (#1758)
* refactor: route install commands through platform runtime

* fix: preserve stacked runtime facts

* fix: align deployment facts with shutdown runtime

* refactor: simplify capability facts projection

* style: format capability facts projection

* fix: preserve migrated capability ownership

* fix: propagate deployment artifact cancellation

* refactor: move Harmony deployment mechanics into package

* refactor: move Apple deployment tools into package

* refactor: move Android deployment tools into package

* refactor: inject deployment temporary storage

* refactor: remove superseded deployment helpers

* fix: preserve provider deployment transport
2026-08-16 15:13:09 +02:00
Michał Pierzchała 5855dfc2e0 refactor: route shutdown through device runtime (#1757)
* refactor: route shutdown through device runtime

* fix: cover shutdown cutover review gaps

* fix: propagate shutdown cancellation

* fix: move shutdown mechanics to platform owners

* fix: pass device to shutdown fact fixture

* test: cover shutdown facts in session state fixtures

* test: simplify Android shutdown assertions

* fix: preserve Apple shutdown cancellation
2026-08-16 15:13:08 +02:00
Michał Pierzchała 39cd4d346a refactor: route appstate through platform runtime (#1755)
* refactor: route appstate through platform runtime

* test: keep appstate capability fixture below complexity limit

* test: cover appstate required readiness fact

* fix: align appstate facts with boot readiness

* fix: keep appstate use declaration minimal

* fix: close appstate parity and ownership gaps

* docs: record final appstate size accounting

* fix: merge neutral runtime imports

* docs: align final appstate size totals

* fix: remove stale runtime dependency edges

* docs: correct appstate size accounting

* refactor: keep runtime-use factory internal

* docs: itemize runtime-use relocation

* fix: move appstate queries into runtime packages

* refactor: retire root foreground query paths

* refactor: share Android foreground parser ownership

* fix: preserve Android appstate parser precedence

* docs: keep appstate evidence in review artifacts

* fix: keep appstate runtime loading lazy

* fix: fail closed for stale limrun appstate

* fix: preserve limrun recovery and abort appstate

* fix: narrow limrun exact-owner recovery

* fix: allocate appstate cutover rule

* fix: reconcile appstate with merged main

* style: format harmony runtime test

* fix: allocate appstate rule id

* fix: allocate appstate layering rule

* fix: remove stale app command admissions

* fix: close appstate layering regressions

* fix: align Harmony capability parity with runtime facts

* test: cover limrun recovery-only readiness

* fix: keep Limrun recovery binding app-log only

* fix: parse Android app state in linear time
2026-08-12 16:55:50 +02:00
Michał Pierzchała eabc936a0f refactor: route apps through request runtime (#1756)
* refactor: route apps through request runtime

* test: remove stale apps adapter mock

* fix: clean apps runtime replay artifacts

* fix: remove stale runtime test exports

* refactor: simplify apps runtime admission

* test: exercise runtime use through facade

* fix: close apps runtime admission gaps

* fix: disambiguate doctor app inventory callback

* fix: keep capability fixture below complexity limit

* fix: keep HarmonyOS app inventory fail-closed

* test: type HarmonyOS app admission fixture

* fix: restore HarmonyOS app inventory parity

* test: align HarmonyOS readiness fixture

* fix: preserve HarmonyOS doctor app parity

* test: type HarmonyOS doctor fixture

* fix: isolate HarmonyOS doctor policy

* fix: align apps cutover with shared rule catalog
2026-08-12 11:55:09 +02:00
Michał Pierzchała b13c06338e refactor: route boot through readiness runtime (#1747)
* refactor: route boot through readiness runtime

* fix: separate boot admission from readiness

* fix: register boot cutover policy

* refactor(runtime): move readiness into platform owners

* fix(test): tolerate provider temp cleanup races
2026-08-12 11:55:09 +02:00
Michał Pierzchała b15c502318 refactor: extract platform network runtime (#1702)
* refactor: extract platform network runtime

* fix: preserve platform network recovery routes

* test: guard network parser placement
2026-08-10 17:58:42 +02:00
Michał Pierzchała b1ed5353d1 refactor: extract platform log runtime (#1701)
* refactor: extract platform log runtime

* fix: clear terminal app log recovery markers

* fix: preserve scoped app log tooling

* fix: preserve app log cancellation

* fix: handle large changed coverage diffs

* fix: harden Limrun runtime identity

* refactor: tighten platform log runtime

* fix: close app log trust gaps

* fix: accept canonical session path aliases

* refactor: extract durable capture kit

* fix: refresh retained log marker admission

* fix: rotate app logs after process relaunch
2026-08-10 17:58:42 +02:00
Michał Pierzchała c06bed9f77 refactor: extract platform device inventory runtime (#1699)
* refactor: extract platform inventory runtime

* fix: preserve scoped Apple inventory tooling

* fix: preserve Apple tool cancellation

* refactor: tighten platform inventory boundaries
2026-08-10 12:51:59 +02:00