Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 97 additions & 9 deletions docs/adr/0019-request-bound-platform-runtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@

## Status

Accepted for staged adoption (2026-08-09). Checkpoint outcome: **pending**. Only the
platform-module substrate and complete `devices`, `logs`, and `network` command cutovers are
authorized before the checkpoint. Daemon-owned generic session teardown may invoke the neutral
app-log disposal contract without changing `close`'s legacy platform-execution owner. If another
command's platform adapter must change, this Status must name its complete unit before substrate work
begins. Broader migration requires this Status to record **continue**.
Accepted for staged adoption (2026-08-09). Checkpoint outcome: **continue** (2026-08-10), under the
revised cumulative package budget accepted in issue 1704. The clean checkpoint is measured from the
original baseline `44c298d7f3a0ef84bc47f34c54d88b6c9eeb0df2`, through merged `devices`
`c06bed9f773a27ae0a02cb012570def2f2d0b90e`, to `logs`
`188795386466cfdba5d5748db5c9d3477e70eb4e` and `network`
`457fafe6399a95a4ddbfac57f02b3a7fe4157a54`. The earlier checkpoints at `99f5af1b7` and `d73bdb4ae`
are superseded and were not behavior-passing: later review found correctness failures and the first
budget decision still used the unrevised +3% limit. The required cleanup package, explicit budget
decision, and clean rerun are now complete. The next authorized command unit is recordings onto the
durable-capture substrate; this decision does not authorize an unbounded platform migration.

During the `devices` unit, doctor discovery, replay-test sharding, Apple simulator hints, and Android
emulator lifecycle keep their existing command execution owners while consuming the same injected,
Expand Down Expand Up @@ -300,6 +304,14 @@ contract handle beside its descriptor and metadata; the field has one R7 transit
descriptor and neutral metadata enter the authoritative persisted recovery record. Concrete platform
classes, provider clients, child handles, timers, transports, and wait promises enter neither store.

A daemon-owned, process-lifetime admission ledger may retain bounded cleanup uncertainty that has no
honest durable representation, but it is not a second live-resource store: it contains no handle or
descriptor, never supersedes the persisted manifest, and is keyed by canonical device identity when
that identity is known. Evidence that can be checked again, such as a retained legacy marker path, is
revalidated at admission so manual recovery can unblock the matching device without a daemon restart.
Unknown-identity evidence remains globally fail-closed, while undurable in-memory blocks expire only
under an explicit bounded policy with diagnostics.

Persisted JSON re-enters as `unknown`. Contracts first validate a neutral envelope containing resource
kind/envelope version, session/device identity, exact owner reference, fence, and lifecycle state.
Only after exact-owner selection does that facet's total codec decode its descriptor body. Invalid or
Expand Down Expand Up @@ -473,6 +485,82 @@ neutral envelope or facet-owned descriptor codec, or contains raw live mechanics
laziness cannot be preserved; R7/R10/type-cycle pressure grows; or the landed slices add more daemon
platform ownership than they remove.

#### Checkpoint result: continue under the revised cumulative budget (2026-08-10)

The final checkpoint compares `44c298d7f3a0ef84bc47f34c54d88b6c9eeb0df2` with stack head
`457fafe6399a95a4ddbfac57f02b3a7fe4157a54` on the same host and toolchain. Merged `devices`
`c06bed9f773a27ae0a02cb012570def2f2d0b90e` is part of that range; rebasing the remaining PRs onto it
does not reset the denominator. The earlier `99f5af1b7` checkpoint missed a stale owned-marker wedge,
Apple scoped-provider bypass, Android optional-recovery loss, and absolute network line-number
regression. The later cleanup review found a cross-device retained-marker wedge, a manual-recovery
hint that remained blocked until daemon restart, and an Android app-log stream that stayed pinned to
the pre-relaunch app PID. Each correction was observed red before its focused test passed; the final
admission test proves removal of the matching retained marker permits a second start in the same
daemon process. On exact final logs head `188795386`, a controlled Android relaunch moved the app from
PID 10952 to 11455, rotated the durable logcat marker to PID 11455, and retained both before/after
canaries plus the new process output in one `app.log`. Guarded daemon/dead-child recovery separately
terminalized the manifest and allowed a clean second start.

The required revision package is complete:

- `@agent-device/capture-kit` is the private durable-capture implementation package. Contracts keeps
pure runtime types and plan models; capture-kit owns process/recovery/live-handle mechanics,
envelope/descriptor codecs, and network parsers. The dependency order is
`kernel < contracts < capture-kit < platform/provider/daemon`. A planted-red gate rejects process,
filesystem, or timer mechanics drifting into contracts. The package is wired into affected-check
selection, the layering test enumeration, and the workspace typecheck project list.
- Canonical kernel device identity replaces the duplicate encoders while facts-shape validation stays
separate. The bounded Android/Harmony PID owner factory lives in capture-kit; Apple and Limrun stay
custom, there is no network factory, and all loaders retain their lazy boundary. The managed-command
allowlist, unused façade exports, public Limrun runtime-module declaration leak, quadratic JSON walk,
and duplicate lifecycle/reconnect mechanics are gone. The temporary Android implementation files
introduced by the devices PR are also absent from contracts at this checkpoint.
- Plan-declared app-session requirements replace repeated handler guards. Persisted durable lifecycle
is `open | completed`, with transient phase retained as metadata. A daemon-owned admission ledger
scopes decodable evidence by device identity, rechecks manually removed legacy markers, bounds
undurable blocks, and remains subordinate to the durable manifest. Discriminated teardown settles
app-log state, re-reads the replaced session record, then runs generic teardown, avoiding the stale
reference and double-cleanup path.

The final gates passed:

- `pnpm check:affected --run` at `457fafe63` passed 799 test files / 6,424 tests with 2,097/2,401
changed executable lines covered (87.34%), plus format, lint, typecheck, build/declarations,
published-package clean install, fallow, provider integration, replay compatibility, and
integration-progress checks.
- `pnpm check:layering` passed 131 structural/model tests and scanned 1,157 production source files.
R11 owns 17 workspace packages behind 39 exported subpaths with no root back-imports; R13 keeps six
private implementation-lazy platform packages above capture-kit behind one composition root; R14
and R15 retain one typed route for `logs` and `network` with no legacy route.
- Six local inventory/runtime owners, all enumerated Apple leaf/kind cells, and the production
BrowserStack, AWS Device Farm, and Limrun provider modes remain covered. Provider ownership and
inventory are fail-closed; exact-owner recovery and provider-authoritative tests prove there is no
provider-to-local fallback. Durable tests cover every start/persist/adopt gap, exact-owner recovery,
cleanup-only and descriptor-only cleanup, fencing, idempotence, and primary-error precedence.
- R7/R10 remain at 23 writer-owned `SessionState` fields and 29 owner claims, with all 34 fields
classified. The largest type cycle is 46 against the 47-file ceiling, and the two external
production `daemon/types.ts` importers are unchanged. Checkpoint-owned `logs`/`network` platform
decision lines remain zero.

Issue 1704 revised the budget for this checkpoint for three explicit reasons: capture-pipeline
reliability through fenced ownership and exact recovery, durable cloud log streaming that did not
exist in the baseline, and a reusable durable-capture substrate. This is not a new observability
layer: the CLI-observable `logs` behavior remains parity work. The budget stays cumulative from
`44c298d7f`; the exact clean post-revision measurement is the reviewed upper bound for this checkpoint,
not a reusable allowance for future units:

| Metric | Original baseline | Revised cumulative bound / checkpoint | Change |
| --- | ---: | ---: | ---: |
| Raw JavaScript | 2,036,067 B | 2,131,689 B | +95,622 B (+4.696%) |
| Gzipped JavaScript | 659,646 B | 695,134 B | +35,488 B (+5.380%) |
| npm tarball | 797,027 B | 826,761 B | +29,734 B (+3.731%) |
| npm unpacked | 2,781,186 B | 2,878,492 B | +97,306 B (+3.499%) |

The controlled 15-run startup medians showed no regression (`--version` 94.5 ms to 47.4 ms;
`--help` 91.2 ms to 72.0 ms). Module-level source-map inspection found no duplicate implementation
emission; the distribution cost is the accepted reliability/cloud/substrate decision above. Future
units must define and review their own cumulative budget rather than inheriting this headroom.

The tracking issue owns command order, PR/file lists, test-only compatibility fixtures, exact
benchmark commands and thresholds, raw evidence, and reviewers. Temporary fixtures never authorize
a production bridge, duplicate route, or recorded package back-import. After the checkpoint, this
Expand Down Expand Up @@ -537,9 +625,9 @@ boundaries, and the early adoption checkpoint keep that coexistence shippable an
explicitly preserves the same no-state-from-events rule.
- **A separate process-local resource ledger beside `SessionState` for live handles:** rejected. The
session store is in-memory, so live `SessionState` already is the process-local home; a parallel
ledger keyed by the same session/resource identity would duplicate the ownership its R7 transition
owner already governs. The live/persisted boundary, not a second live store, is the protection:
only the descriptor and neutral metadata enter the persisted recovery record.
handle/descriptor store would duplicate the ownership its R7 transition owner governs. The accepted
admission ledger is narrower: it holds only bounded cleanup-block evidence, contains no live handle
or descriptor, and never replaces the authoritative manifest.
- **Rely only on performance thresholds for lazy loading:** rejected. Thresholds catch regressions
late and can pass while unrelated implementation graphs load; the import/evaluation shape is also
contract-tested.
Loading