Commit Graph

315 Commits

Author SHA1 Message Date
Michał Pierzchała 7672454304 refactor: move interaction handlers behind daemon facade 2026-09-01 21:48:10 +02:00
Michał Pierzchała b15121ffc8 test(ios): establish snapshot convergence baselines and permanent evidence (#2204)
* test(ios): add snapshot convergence evidence harness

* fix(ios): satisfy benchmark CI guards

* fix(ios): constrain benchmark proxy routes

* fix(ios-benchmark): enforce cell admission evidence

* fix(ios-benchmark): protect benchmark state ownership

* fix(ios-benchmark): use proxy port flag

* fix(ios-benchmark): let proxy choose an ephemeral port

* test(ios-benchmark): keep CLI process seam local

* fix(ios-benchmark): parse proxy startup envelope

* fix(ios-benchmark): bind proxy lease to simulator

* fix(ios-benchmark): keep fresh proxy CLI sessions isolated

* fix(ios-benchmark): preserve async timeout evidence

* docs(ios-benchmark): retain exact-head evidence

* test(ios): reveal offscreen alert fixture controls

* test(ios): reset alert between relaunch samples

* test(ios): admit native alert snapshots

* docs(ios): publish snapshot convergence corpus

* chore(ios): format benchmark evidence

* fix(ios-benchmark): admit proxy fixture anchors

* docs(ios): republish exact-head benchmark corpus

* fix(size): make publish asset evidence hermetic

* style(size): format package evidence test

* test(size): update publish preparation contracts

* fix: retire stale utils layering zone

* test: pin shared publish asset owner

* test: verify preserved size reporter closure

* fix: move mutation ownership to snapshot module

* test(ios): add snapshot convergence evidence harness

* fix(ios): satisfy benchmark CI guards

* fix(ios): constrain benchmark proxy routes

* fix(ios-benchmark): enforce cell admission evidence

* fix(ios-benchmark): protect benchmark state ownership

* fix(ios-benchmark): use proxy port flag

* fix(ios-benchmark): let proxy choose an ephemeral port

* test(ios-benchmark): keep CLI process seam local

* fix(ios-benchmark): parse proxy startup envelope

* fix(ios-benchmark): bind proxy lease to simulator

* fix(ios-benchmark): keep fresh proxy CLI sessions isolated

* fix(ios-benchmark): preserve async timeout evidence

* docs(ios-benchmark): retain exact-head evidence

* test(ios): reveal offscreen alert fixture controls

* test(ios): reset alert between relaunch samples

* test(ios): admit native alert snapshots

* docs(ios): publish snapshot convergence corpus

* chore(ios): format benchmark evidence

* fix(ios-benchmark): admit proxy fixture anchors

* docs(ios): republish exact-head benchmark corpus

* fix(size): make publish asset evidence hermetic

* test(size): update publish preparation contracts

* fix: keep git-state gates out of mutation sandboxes
2026-09-01 21:39:05 +02:00
Michał Pierzchała 110c08c947 refactor(transport): move shared host mechanics (#2221) 2026-09-01 19:45:28 +02:00
Michał Pierzchała 1826b2e68b refactor(ios): integrate runner with snapshot engine (#2214)
* refactor(ios): integrate runner with snapshot engine

* fix(ios): preserve macOS runner snapshots

* refactor(ios): keep runner presentation device-aware

* fix(ios): validate runner scroll presentation

* fix(ios): close presenter package boundaries

* fix(ios): preserve snapshot source lineage

* test(ios): colocate snapshot engine coverage

* fix(ios): settle post-merge audit checks

* test(ios): fix manifest parity lint

* refactor(ios): simplify runner source walk

* test(ios): cover shared package source fixture

* fix(ios): close post-merge audit gaps

* perf(ios): avoid bundling acquired snapshot path
2026-09-01 18:36:09 +02:00
Michał Pierzchała 911945a8a6 refactor(provision-kit): move install source config (#2217) 2026-09-01 18:35:39 +02:00
Michał Pierzchała 544a804965 refactor(daemon): move session observability behind facade (#2216) 2026-09-01 16:39:01 +02:00
Michał Pierzchała a8ee397168 test(ios): add snapshot engine conformance gates (#2213)
* test(ios): add snapshot engine conformance gates

* test(ios): align differential acquisition inputs

* fix(ios): gate Swift differential on macOS

* test(ios): keep differential coverage host-aware

* test(ios): own snapshot differential on macOS
2026-09-01 15:59:31 +02:00
Michał Pierzchała 02116ccdd8 refactor(ios): implement snapshot engine (#2211)
* refactor(ios): implement snapshot engine

* fix(ios): finish snapshot engine ownership move
2026-09-01 15:59:30 +02:00
Michał Pierzchała 868f8f90ee refactor(ios): extract snapshot presenter (#2206)
* refactor(ios): extract snapshot presenter

* refactor(ios): consume shared snapshot presenter

* fix: unblock snapshot presenter CI
2026-09-01 15:59:30 +02:00
Michał Pierzchała 7646a73b1b perf: avoid redundant physical iOS runner health check (#2215)
* perf: avoid redundant physical iOS runner health check

* test: isolate iOS runner prewarm coverage
2026-09-01 15:30:00 +02:00
Michał Pierzchała fd4ab84166 refactor(ios): define snapshot acquisition and presentation contracts (#2203)
* refactor(ios): define snapshot acquisition and presentation contracts

* refactor(ios): isolate snapshot planning exports
2026-09-01 11:33:18 +02:00
Michał Pierzchała 81a9cb2b3c feat(daemon): establish interaction application facade (#2205)
* feat: establish interaction application facade (#2177)

* fix(daemon): narrow interaction runtime request seam
2026-09-01 10:46:54 +02:00
Michał Pierzchała 1522126f1f refactor: move close lifecycle behind session facade (#2212) 2026-09-01 10:25:59 +02:00
Michał Pierzchała b042045522 refactor(output): split presentation owners (#2202)
* refactor(output): split presentation owners

* fix(output): keep candidate rendering in surface owners
2026-09-01 07:50:25 +02:00
Michał Pierzchała 010f09bf0d refactor(daemon): move open lifecycle behind session facade (#2201) 2026-08-31 21:18:58 +02:00
Michał Pierzchała 8591f47dd3 refactor: extract daemon session lifecycle inventory facade (#2183)
* refactor: extract session lifecycle inventory facade

* test: cover session inventory failure response
2026-08-31 19:01:00 +02:00
Michał Pierzchała d330a679e2 fix(apple): switch to manual code signing when a provisioning profile is set (#2172)
* fix(apple): switch to manual code signing when a provisioning profile is set

CODE_SIGN_STYLE was hardcoded to Automatic even when
AGENT_DEVICE_IOS_PROVISIONING_PROFILE was configured, so xcodebuild rejected
the resulting PROVISIONING_PROFILE_SPECIFIER + CODE_SIGN_STYLE=Automatic
combination with "conflicting provisioning settings" on physical-device runs.

Fixes #2153

* fix: satisfy formatting and the test-file size ratchet

- oxfmt: wrap the long array literal in the new manual-signing test.
- runner-client.test.ts was already pinned at the 1000-line tripwire
  (1577 lines); adding a test grew it past the pin, which the ratchet
  test rejects by design ("extract instead of adding to a file over
  the tripwire"). Extract the pure runner-cache-metadata.ts build-
  settings tests (signing, bundle, performance, sandbox args) into a
  new runner-cache-metadata.test.ts, shrinking runner-client.test.ts
  to 1441 lines and lowering its pin to match.
2026-08-31 16:28:27 +02:00
Michał Pierzchała f513b1d4ae refactor: extract daemon replay behind one application facade (#2166)
* refactor: extract daemon replay behind application facade

* fix: address replay facade review findings

* test: close replay ownership import scan gap

* fix: tighten replay capability boundaries
2026-08-31 15:43:33 +02:00
Michał Pierzchała 34e8cbb7a2 docs+ux: make device ownership discoverable end to end (#2165)
* docs+ux: make device ownership discoverable end to end

Complete the #1320 agent experience so 'busy? -> inspect -> choose or
release' is discoverable from every surface an agent actually reads:

- devices now projects the blocking claim owner per row (claimedBy with
  session and workspace, observe-policy projection; provably dead owners are
  excluded because the next open replaces them automatically), so an agent
  told a device is busy can pick a free one from the same listing.
- help debugging gains a 'Device busy and ownership' section separating the
  two DEVICE_IN_USE flavors and their exact recoveries.
- AGENTS.md documents both flavors; docs/agents/device-verification.md
  retires the last ps/kill recovery guidance in favor of device status,
  daemon stop --state-dir, and device release --stale (Stage 5 of #1320).
- ADR-0010 no longer calls DEVICE_IN_USE 'the only retriable code' without
  naming the claim path's non-retriable override.
- The rendered cross-worktree claim error gains a help-conformance quiz case
  binding (sample-output-device-claim-inspects-owner).
- README points at device status / device release --stale.

Part of #1320.

* fix: key ownership projection by canonical device identity end to end

Review findings on #2165:

- blockingClaimOwnersByDevice keyed claims and inventory rows by bare
  device.id, so a live Android claim could project claimedBy onto an
  unrelated same-id Apple/Harmony/Vega row, with scan order picking the
  displayed owner. Both sides now use the canonical local device key
  (claim.deviceKey against canonicalLocalDeviceKey of the row's claim
  identity). The cross-family same-id regression was observed red against
  the bare-id keying.
- The projection is now asserted across every hop the PR promises: client
  normalization preserves well-formed claimedBy and drops malformed ones,
  and the devices CLI formatter carries it through JSON data and renders
  the text line (MCP shares the same serialization).
2026-08-31 14:32:29 +02:00
Michał Pierzchała a79f0d2e81 feat: add symbol-aware depgraph authority overlay (#2157)
* feat: add depgraph authority overlay

* fix: centralize depgraph authority declarations

* refactor: make authority overlay declaration-driven
2026-08-31 11:54:16 +02:00
Michał Pierzchała caa3dc23f9 refactor: dissolve caller-side src/replay into command and CLI owners (#2151)
* refactor: dissolve caller-side replay ownership

* fix: remove replay test-only export

* fix: restore replay loader promise boundary
2026-08-31 10:01:16 +02:00
Michał Pierzchała 4244691e1e refactor(layering): centralize architecture ownership (#2150) 2026-08-31 08:09:39 +02:00
Michał Pierzchała ed26b31c94 refactor: contract Apple platform surface (#2125)
* refactor: contract Apple platform surface

* refactor: use Apple plugin seam in tests

* test: ratchet snapshot handler size
2026-08-29 13:10:48 +02:00
Michał Pierzchała a6232e51cf refactor: prune platform split residue (#2123) 2026-08-29 13:10:47 +02:00
Michał Pierzchała ab20d5c2af refactor: retire platforms source seam (#2119) 2026-08-29 13:10:47 +02:00
Szymon Dziedzic d9677301f4 feat: add human takeover controls (#2078)
* feat: add human takeover controls

* fix: harden human takeover controls

* fix: align host XCTest selection count

* fix: address takeover readiness feedback

* fix: handle macos runner permission prompt in smoke tests

* fix: detect background macos permission dialog

* fix: dismiss inaccessible macos privacy sheet visually

* fix: map macos privacy prompt without accessibility

* refactor: own human-control holds in lease registry

* fix: cancel pending human takeover on disconnect
2026-08-29 13:09:54 +02:00
Michał Pierzchała 9abcd7fe03 refactor: move Apple platform family into package (#2118)
* refactor: move Apple platform family into package

* fix: preserve Apple facade sync contracts

* fix: complete Apple W4 rebase review fixes
2026-08-28 15:25:44 +02:00
Michał Pierzchała c7f42ccedc refactor: move Android family behind package exports (#2117)
* refactor: move Android family behind package exports

* fix: address Android W5 review feedback

* fix: update relocated routing fixture assertion
2026-08-28 13:02:28 +02:00
Michał Pierzchała 838ed223b5 refactor: move W6 platform families behind package facades (#2116)
* refactor: move W6 platform families behind package facades

* fix: address W6 loading and composition review
2026-08-28 12:46:39 +02:00
Michał Pierzchała af6f12e391 chore: adopt shared oxlint config (#2115)
* chore: adopt shared oxlint config

* fix: preserve project lint boundaries

* fix: remove redundant oxlint config
2026-08-28 11:42:58 +02:00
Michał Pierzchała 437465f37b ci(1874): declare the diagnose lane and read its iterations honestly (#2059)
The loop that #1874 is investigated with could not tell the truth about itself.
It classified every non-`passed` iteration as a stall, which after #2035 gave the
looped test an XCTSkipIf meant an environment flip would report a 100% stall
rate; it captured cadence only for failures, though an absorbed episode now
passes; and it read its logs with shell pipelines whose exit status means "did
this match", so an iteration that legitimately matched nothing killed the job
before it could be summarized.

scripts/diagnose-1874-iteration.ts reads one iteration: xcodebuild's own verdict,
the `type-all` duration, and the cadence worth keeping. A nonzero exit outranks a
green measured test — in `pair` mode the neighbour or the runner can fail while
the measured test passes — and a run that produced no verdict is named as ours
rather than counted as a stall. The workflow gains the #1781 lane declaration it
never had. Its kill criterion names #2080, which the loop can now serve rather
than merely claim to: the looped test is a dispatch input, so the fill route that
#2080 traces loops the same way. One test pins the contract the script cannot
check about itself — that the workflow hands it the status xcodebuild returned
rather than a literal.

Closes #1874.

Both filed symptoms are resolved. `smoke:form-input` was root-caused and fixed in
#2035: the fixture's placeholder was identical to the value every suite filled,
so `fill` could never be verified on the penalized route — deterministic, not a
flake, and only visible under load because that route is gated on a penalized
XCTest channel. The targeted XCTest is mitigated by the progress-aware commit
budget, with 200 consecutive green loop iterations across two dispatches.

The issue's remaining question — why the input pipeline throttles — is answered
by the second dispatch, and the premise was wrong: it does not. Posting 17
characters took 484 ms and the commit was observed on the first poll, inside an
iteration whose `type-all` measured 14334 ms. The ~12.6 s went to accessibility
round-trips before any character was posted, which is #1105's path, not the
input pipeline's.
2026-08-28 08:33:46 +02:00
Michał Pierzchała ddb415a2c7 refactor: sink package-closed src modules into existing packages (#2106)
* refactor: sink package-closed src modules into existing packages

Move closed modules into contracts, kernel, capture-kit, and ad-script,
and declare DaemonCommandDescriptor in core so R6/R9 can pin the remaining
provider-webdriver type cycle.

Co-authored-by: Cursor <cursoragent@cursor.com>

* refactor: keep contracts and capture-kit off generic sinks

Move interaction-outcome, snapshot warning rendering, and inventory ALS
behind focused owners, and plant R18/R70 domain-shape gates so they
cannot return as package export-map growth.

Co-authored-by: Cursor <cursoragent@cursor.com>

* refactor: drop moved implementation comments from owner modules

Names, types, and tests already carry those invariants; the relocated
files should not keep review-history or control-flow narration.

Co-authored-by: Cursor <cursoragent@cursor.com>

* refactor: drop the empty snapshot-quality layering zone

W1 moved the verdict into capture-kit and this PR moved warning rendering
into snapshot-presentation, so the ranked zone no longer has production files.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 07:56:18 +02:00
Michał Pierzchała 0ecfef1c17 refactor(platforms): sink the shared src/platforms root files into their substrate homes (#2100)
* refactor(platforms): sink the shared src/platforms root files into their substrate homes (#2082 W3)

The shared files left directly under src/platforms move onto a declared
owner: provisioning mechanics (install-source family, toolchain probing,
boot-failure classification, app-resolution caching) form
@agent-device/provision-kit above capture-kit; host mechanics resolve to
host-kit's seams; kernel takes the pure numeric helpers; contracts keeps
vocabulary only.

Settings parsing, command-attempt rendering, and the unsupported-interactor
factory stay with their families rather than pooling in a substrate
package: android and apple settings each own their parsing, and the
unsupported-interactor factory lives in root core with a vega-local copy.

A platforms-root-shape rule rejects any new shared file or directory
appearing directly under src/platforms, and the provision-kit direction
gates (no platform imports in, no capture-kit importer) are planted red.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018VngeKZH6zBuJzNBk5YzUH

* test: cover the family-owned parsers and the unsupported-interactor factories

The settings parsers and the unsupported-interactor factory arrived without
owning tests, so their branches rode on callers. Each now has one: the
appearance/state parsers over every accepted spelling and their rejections,
the attempt summarizer over its arg join and stderr budget, and both
interactor factories over the whole operation surface and the per-instance
label.

Also drops the duplicate ./snapshot-desktop-projection export key that a
rebase left in capture-kit's manifest, where JSON silently keeps the last,
and the root-shape docblock the violation message already states.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018VngeKZH6zBuJzNBk5YzUH

* docs: keep tool directives only in the touched files

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018VngeKZH6zBuJzNBk5YzUH

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-08-28 07:46:50 +02:00
Michał Pierzchała 7a6284bb36 refactor(platforms): break the four upward edges out of src/platforms (#2090)
* refactor(platforms): break the four upward edges out of src/platforms (#2082 W2)

src/platforms carried four imports that point up into root src, each of
which would become an R11 violation the moment its family moves into a
platform package:

- android/app-helpers.ts reached the composition root for the foreground
  parser. The parser is pure dumpsys vocabulary, so it moves to
  @agent-device/contracts/android-observation; the platform-android
  app-state module and app-helpers both consume it from there, and the
  composition wrapper plus the package facade's lazy re-export retire
  (R13 allows only the composition root to import platform packages, so
  vocabulary relocation is the inversion that stays legal).
- web/provider.ts and web/agent-browser-network.ts type-imported the
  backend diagnostics/network-dump vocabulary from src/backend.ts. Those
  six types move to @agent-device/contracts/backend-diagnostics;
  backend.ts re-exports them for its SDK consumers.
- snapshot/snapshot-desktop-surface.ts split three ways: the pure
  projection (scope/interactive/depth) moves to
  @agent-device/contracts/snapshot-desktop-projection, the per-family
  captures move to platforms/linux/surface-snapshot.ts and
  platforms/apple/os/macos/surface-snapshot.ts beside the code they
  dispatch to, and the root file keeps only the device-dispatching
  runtime host behind R3-tolerated dynamic imports. apple/interactor
  now reaches macOS surface capture family-internally instead of
  through root.

src/platforms -> root src is now zero edges. Contracts grows two entries
(pinned, budgeted); the android foreground-parser tests move beside the
parser. Left for a later pass: app-parsers.ts shares the marker-walk loop
shape with the contracts parser but parameterizes it for blocking-dialog
parsing - generalizing that is a design change, not a move.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018VngeKZH6zBuJzNBk5YzUH

* refactor(platforms): keep platform parsing with its family; give snapshot shaping its capture owner

ADR-0019's amendment forbids satisfying R13 by moving implementation
into contracts, so this wave's two relocations invert instead of sink:

- The Android dumpsys foreground parser returns to
  @agent-device/platform-android with its owning test, and
  contracts/android-observation goes back to observation vocabulary
  only. src/platforms/android/app-helpers exposes
  createAndroidAppStateReader(parseForegroundApp) and never imports
  upward; the composition seam in src/sdk/android-adb.ts injects the
  root-composed parser, keeping the published
  getAndroidAppStateWithAdb(adb) signature intact.
- The desktop snapshot projection moves to
  @agent-device/capture-kit/snapshot-desktop-projection beside the rest
  of the capture-side snapshot behavior; contracts exports the
  snapshot-scope vocabulary it consumes.

The #1832 history narration in the projection test is gone; the test
name and golden fixture carry the invariant.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018VngeKZH6zBuJzNBk5YzUH

* refactor(platforms): the adb app-state read lives whole behind the platform-android seam

The injected-parser loop retires: platform-android's app-state module
owns the complete adb-executor read/parse (readAndroidAppStateWithExecutor,
beside its host-based twin), the façade exposes it lazily, the
composition root wraps it, and src/sdk/android-adb.ts reaches it through
that root in one hop. app-helpers keeps only the app-list helpers, and
the SDK-route tests live in SDK topology as src/sdk/android-adb.test.ts.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018VngeKZH6zBuJzNBk5YzUH

* docs: drop the seam wrapper explainer comments

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018VngeKZH6zBuJzNBk5YzUH

* docs: drop the projection docblocks the test names already carry

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018VngeKZH6zBuJzNBk5YzUH

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-08-28 07:46:49 +02:00
Michał Pierzchała e832325e87 refactor(substrate): split host mechanics into @agent-device/host-kit capability ports (#2088)
* refactor: split generic host mechanics into @agent-device/host-kit (#2082 W1)

The shared src/utils closure that blocked the platform-family moves lands
on declared owners: generic host mechanics form a new private
@agent-device/host-kit package between kernel and capture-kit, and
capture-kit keeps capture, snapshot, and recording behavior, depending on
host-kit for the mechanics it needs. tar-stream and yauzl move with the
archive code.

Every seam's exported subpaths are pinned in package-boundaries.test.ts,
the layering model ranks the new zone, R13's allow-list names it, and each
seam carries an exact eager-closure row. ADR-0019's substrate amendment
describes the layout.

Tests that mocked two of the moved modules separately became duplicate
same-seam vi.mock factories, where the second silently replaced the first;
those are merged, and the mocks that production code reaches past are
pinned at their injection points instead.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018VngeKZH6zBuJzNBk5YzUH

* refactor(host-kit): one narrow capability port per export

The four technical barrels (exec/fs/values/request) grouped by category
rather than by capability, so a consumer needing one mechanic evaluated
unrelated ones. Each export is now a single capability over the host
machine: command, process, diagnostics, retry, archive, file, request,
version. A port re-exports only what a consumer of that capability uses,
and every port carries its own eager-closure row.

Most of the old values barrel was never host mechanics. Pure record
readers, config-source values, result text, memoization, async scoping,
coordinate validation, and device-scope parsing touch no process, file, or
environment, so they join kernel's other primitives instead.

Closures fall accordingly: capture-kit's png-worker-client from 20 to 10,
png-resize from 28 to 18, session-teardown from 79 to 68, and the CLI from
386 to 380.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018VngeKZH6zBuJzNBk5YzUH

* chore: drop the migration inventories and trim the touched comments

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018VngeKZH6zBuJzNBk5YzUH

* docs: trim the touched host-kit and mutation-lane comments

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018VngeKZH6zBuJzNBk5YzUH

* docs: keep tool directives only in the touched files

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018VngeKZH6zBuJzNBk5YzUH

* docs: keep tool directives only across the touched tree

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018VngeKZH6zBuJzNBk5YzUH

* fix: point the Swift parity comment at the real TS twin and test

The W1 move rewrote this citation to packages/contracts/src/mobile-snapshot-semantics.ts,
which does not exist: the module went to capture-kit while isTapPointInsideViewport itself
went to packages/contracts/src/snapshot-visibility.ts. The TS test line was left pointing at
the pre-move path. Both now resolve.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018VngeKZH6zBuJzNBk5YzUH

* fix: repoint comment citations at the homes this refactor moved them to

The W1 move left ~20 comment citations pointing at src/utils/*.ts and
src/request/*.ts paths that no longer exist. Each now names the capability
port that owns the symbol, which survives further file moves:

  exec -> host-kit/command          host-process, owner-identity -> host-kit/process
  diagnostics -> host-kit/diagnostics   atomic-file, process-lock -> host-kit/file
  retry -> host-kit/retry           request progress/cancel -> host-kit/request
  version -> host-kit/version       ttl-memo, source-value, parsing, device-isolation,
                                    keyed-lock, success-text -> kernel subpaths

Comment-only; no closure, budget, or behavior change. ADR citations are left
as written, being dated records of the decision rather than live references.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018VngeKZH6zBuJzNBk5YzUH

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-08-28 07:46:48 +02:00
Michał Pierzchała 4454aef139 refactor(layering): remove retired migration scaffolding (#2092)
* refactor(layering): remove retired migration scaffolding

* test: remove retired focus test wording
2026-08-27 19:24:46 +02:00
Michał Pierzchała ea1d6b8c55 refactor: remove retired capability matrix (#2089) 2026-08-27 16:12:44 +02:00
Michał Pierzchała 7b48531d3b refactor: retire ADR-0019 cutover scaffolding (#2081) 2026-08-27 15:11:14 +02:00
Michał Pierzchała 057b2da233 ci: run coverage in one job again (#2079)
The Coverage lane was split into two matrix shards plus a Coverage Report
job that downloaded both blob reports and merged them. That claimed three
runner slots per PR and put a barrier in front of the merge: the report
job could not start until the slower shard finished, and the blobs it
waited on are tens of MB to upload and download.

One job asks for one slot and reports its own thresholds where it runs, so
the lane finishes when the suite finishes. Everything the split needed goes
with it: the shard/merge switches in vitest.config.ts, the blob reporter
swap, the zeroed per-shard thresholds, and the env blanking that
`test:fuzz-worker` carried only to keep the second leg from inheriting them.
2026-08-27 13:28:54 +02:00
Michał Pierzchała c794c11d7e refactor: close the daemon platform boundary (#2072)
* refactor: move runtime resource mechanics out of daemon

* refactor: move Apple resource access out of daemon

* chore: enforce the terminal daemon platform boundary
2026-08-27 13:11:01 +02:00
Michał Pierzchała 3378d9c05a refactor: move Android system observation out of daemon (#2071) 2026-08-27 13:11:01 +02:00
Michał Pierzchała 03f0f408c2 refactor: move platform provider composition out of daemon (#2070) 2026-08-27 13:11:00 +02:00
Nicolas Bataille ed8d5efb00 fix(daemon): address cwd-scoped sessions by their store key, not their public name (#2068)
* fix(daemon): address cwd-scoped sessions by their store key, not their public name

An implicitly cwd-scoped session is NAMED `default` and STORED under
`cwd:<hash>:default`. Three surfaces built caller-facing text from the name, so
each pointed at something that does not exist:

- `DEVICE_IN_USE` reported `session "default"` and its recovery hint said
  `agent-device close --session default`. `--session` marks the session
  explicit, which disables cwd scoping, so following the hint addressed a
  different, absent session: `SESSION_NOT_FOUND`, and the device stayed held —
  the residual half of #2031 that #2057's superseded-daemon reconciliation does
  not cover.
- `session list` reported `sessionStateDir` and `runnerLogPath` under
  `<state>/sessions/default`, a directory that is never created; the session's
  real artifacts sit in `<state>/sessions/cwd_<hash>_default`, which is what
  `open` already answers with.

`SessionStore.entries()` exposes the key alongside the record, and
`buildSessionRecoveryHint` takes the session's address explicitly. Call sites
that only hold the session the current request named pass its name — the address
there — so their text is unchanged, as are explicitly named sessions everywhere.

Live before/after on an iOS simulator: `open` (cwd-scoped default), then
`open --session qa` on the same device. Before, the hint's own `close --session
default` answered `SESSION_NOT_FOUND` and the retry failed again; after, the
hint names `cwd:8bea844ab16aa9b3:default`, that close releases the device, and
the retry opens.

Refs #2031, #1394

* fix(daemon): thread the resolved session address through every recovery producer

The store key a request resolves — `cwd:<hash>:default` for an implicit
session — was known upstream but dropped before the selector-conflict and
lock-conflict producers, which then named `SessionState.name`. Both emitted
`close --session default`, the unreachable recovery this PR fixes for
DEVICE_IN_USE: `--session` marks the session explicit, so that command
addresses a different session.

`SessionRef` ({ address, session }) now carries the pair, so a recovery
producer cannot be handed a record whose address was never resolved.
`buildSessionRecoveryHint`, `assertSessionSelectorMatches` and
`applyRequestLockPolicy` take it; `prepareLockedRequestBinding` and the
Maestro replay route build it from the store key they already hold.

Replaces the broad `SessionStore.entries()` with the store's own narrow
lookups — `lookup`, `findByDevice`, `listRefs` — so callers receive the
session with its address instead of enumerating raw key/record tuples.

`session list` reports `address` alongside `name`, through the client
contract and serializer, so its discovery output names a value `--session`
accepts.

Regressions run the production routes, not the helpers: router-level
device-in-use, selector-conflict, lock-policy-conflict and `session list`
cases open an implicit session and read the address back off the store, plus
a typed-Maestro selector-conflict case. Each was observed red against the
pre-fix behavior it pins.

Refs #2031, #1394.

* fix(daemon): doctor names same-device sessions by address; green the fallow gate

- sessionChecks enumerated same-device sessions by SessionState.name:
  the printed close --session default was the exact unreachable
  recovery this PR removes elsewhere, the name-based dedupe hid two
  cwd-scoped default sessions from each other, and the evidence
  sessionStateDir pointed at <state>/sessions/default, which never
  exists. Candidates now come from listRefs() and report addresses.
- findByDevice joins values/delete in .fallowrc.json usedClassMembers
  (same resolution false positive on this class; the call site is
  session-open-execution.ts).
- the scoped-paths test narrows its response once instead of nine
  optional-chaining hops, which tripped the CRAP gate.

* style: format .fallowrc.json

---------

Co-authored-by: Michał Pierzchała <thymikee@gmail.com>
2026-08-27 11:06:32 +02:00
Michał Pierzchała 71214e11da refactor(runtime): close residue execution units (#2054)
* refactor(runtime): close residue execution units

* refactor(runtime): address residue ownership review

* fix(runtime): preserve viewport diagnostic log path
2026-08-27 07:46:25 +02:00
Michał Pierzchała 9f16fc885c refactor: migrate perf to device runtime (#2061) 2026-08-26 20:41:19 +02:00
Michał Pierzchała a904ef0d5d fix(fuzz): run parser cases in a worker process, not the runner's thread (#2053) (#2055)
The unit-lane corpus replay executed adversarial parser cases on worker
threads of the Vitest worker running the test file. A fault in a worker
thread ends its whole process, so a case that faulted killed the test
runner: `[vitest-pool]: Worker forks emitted error / Worker exited
unexpectedly`, with no test, file, or case named. Six of six Coverage
deaths before #1994's split were this one file out of ~1100, and the
uninstrumented second leg it created then lost the same file six more
times in three days.

Cases now run in a worker *process*. The two faults a case cannot report
about itself are both classified from outside it: a case that never
returns is a `hang` (unchanged), and one that ends the process it runs in
is a new `crash` failure carrying the exit code or signal and the tail of
the worker's stderr — the death certificate the lane used to lose. A
sixth self-check target seeds that kind, so a regression in reporting it
fails the harness self-check like every other kind.
2026-08-26 20:40:57 +02:00
Michał Pierzchała 33a816966c perf(apple): uninstall stale runner bundles concurrently (#2058)
* perf(apple): start stale simulator runner bundle uninstalls concurrently

cleanupStaleSimulatorRunnerBundles awaited each simctl uninstall
sequentially while discarding the results (best-effort cleanup). Run the
per-bundle uninstalls under Promise.allSettled like the sibling disposal
paths, and pin the concurrent start with a deferred-promise test.

* test(apple): split stale-bundle cleanup coverage out of the pinned runner-session suite

runner-session.test.ts is over the test-size tripwire and its pin may only
shrink. Move the three stale-bundle cleanup tests (boot availability,
best-effort stall, concurrent start) into a sibling file named for the
domain question, carrying the same seam scaffolding.

* test(apple): lower runner-session suite pin to bank the stale-bundle extraction

* test(apple): format stale bundle coverage
2026-08-26 17:43:16 +02:00
Michał Pierzchała 8af2660332 refactor(android): extract the adb executor and IME cluster into packages/platform-android (#2041) (#2044)
* refactor(android): extract adb executor and IME cluster into packages/platform-android (#2041)

Implementation moves behind an injected adb host port (R13-clean); shared
vocabulary moves to contracts (android-touch-plan, android-helper-artifacts)
and kernel (keyed-lock); root keeps thin re-export shims plus the composition
wiring that binds the port. session.ts/session-observability.ts no longer
import platform transport types (opaque unknown, R62 pattern).

* refactor(android): trim shims to consumed surface, break helper-install cycle, add gate coverage

Fallow-clean: shim re-exports carry only names root still consumes;
helper-package-install imports the package subpath directly (no cycle) and
reuses the contracts decision types; transitional R13 table gets planted-red
tests; ADR-0019 records the transitional exception; eager-closure rows added
for the new entry surfaces.

* review: restore maxBuffer on spawn options, scope the transitional test allowance

Adversarial review round 1: AndroidAdbSpawnOptions keeps maxBuffer for spawn-
signature width parity with the pre-move ExecBackgroundOptions; the R13 test
allowance narrows from any test file to the cluster's own src/platforms/android
__tests__ directory, with planted-red coverage for a foreign test file; ADR
text records the scoped allowance.

* review: keep an unbound adb host port loud in listAndroidAdbSerialsQuick

* style: format policy test

* refactor(android): split the extracted adb and IME modules into focused owners

Review: the moved adb-executor.ts (652 lines) and ime-lifecycle.ts (487 lines)
carried three concerns each across the package boundary. The entry subpaths are
now thin surfaces over focused modules — adb: transport vocabulary, failure
classification+enrichment, provider normalization, provider scope/routing,
port-reverse ownership, pull/install transfers; IME: ownership state, on-device
settings record, activation transaction, restore+orphan recovery — every module
under 250 lines, each with colocated tests over an in-memory host-port stub and
a shared fake IME device. R31's fence invariant repoints to ime-activation.ts;
eager-closure pins move to the split shape.

* rebase: reconcile the transitional android table with the #2050 mechanics-facet model

The R13 declaration check now composes both enumerated subpath sets on the
exact-list model #2050 introduced; the policy test fixture declares android's
transitional subpaths the same way it declares apple's runner facet.
2026-08-26 16:40:32 +02:00
Michał Pierzchała 72cae2bc72 refactor(apple): colocate the XCUITest runner client into packages/platform-apple (#2040) (#2050)
* refactor(apple): colocate the XCUITest runner client into packages/platform-apple (#2040)

Moves src/platforms/apple/core/runner/ (34 modules + apple-runner-platform.ts and
the 30 runner test suites) into packages/platform-apple/src/runner/ — Apple
mechanics live in the Apple package. Host capabilities (exec, diagnostics,
retry, process probes, locks, Apple tooling, physical-device control) enter
through the package-owned AppleRunnerHost port; the root composition module
src/platforms/apple/core/runner-client.ts constructs the client exactly once
and re-exposes the bound operations under their historical names.

R13 admits the transitional state deliberately: the family exports its root
façade plus exactly the enumerated ./runner, ./runner/client, and
./runner/test-host subpaths; the ./runner façade subpath is the recorded #1983
seam for unmigrated root consumers; ./runner/client has one composition root
and ./runner/test-host one vitest installer; the runner subtree may own its
cache files and sockets while raw process primitives stay banned. When #1983
completes, the subpaths and every subtree exemption are deleted and the family
returns to a single implementation-lazy façade export.

* docs(adr): model the runner subtree as a durable platform-owned facet

Review correction on #2050: the sunset story attributed the runner-consumer
migration to #1983, which owns snapshot/presentation vocabulary — not the
runner's daemon/root consumers — so that event cannot delete the ./runner
subpaths or the subtree exemptions. Reword ADR-0019, R13, and the gate
comments: the facet is the intended ownership model, its seam is enumerated
and pinned (exact export list, one client composition root, one test-host
installer, raw-process ban, eager-closure pins), and the seam narrows only
if a real runner-consumer migration retires the direct consumers. The
declaration mechanism stays apple-specific until another family needs a
mechanics facet. No behavior change; identifiers and comments only.
2026-08-26 15:53:01 +02:00
Michał Pierzchała 74a70f1764 refactor: remove next-major compatibility surfaces (#2046) 2026-08-26 14:14:15 +02:00