|
| 1 | +# Migrating one command onto the request-bound platform runtime (ADR 0019 unit) |
| 2 | + |
| 3 | +The checklist for one command unit. ADR 0019 §6–10 holds the rules and the why; issue #1739 holds |
| 4 | +the wave plan and per-unit budgets; this file is the order of operations, with the seam, fixture, |
| 5 | +and gate you touch at each step. Everything here was rediscovered during the `snapshot` unit |
| 6 | +(#1779, 134 files) — do not rediscover it again. |
| 7 | + |
| 8 | +## Before writing code |
| 9 | + |
| 10 | +1. **Post the unit on #1739 first**: exact denominator (every inventory source or supported |
| 11 | + device-runtime cell), parity source, facet owner, expected deletions, evidence tier |
| 12 | + (request-scoped vs durable-resource), and the size budget. A unit without a posted budget has |
| 13 | + no acceptance criterion; the late budget negotiation on #1779 is the incident. |
| 14 | +2. **One integration owner.** Parallel agents audit (platform parity, tests, size, architecture) |
| 15 | + and implement only clearly disjoint lanes — a platform package's operations, a fixture builder, |
| 16 | + the test migration. The shared spine — plan → facts → bind in the handler, the descriptor's |
| 17 | + `platformExecution`, the cutover row — moves serially under one owner. Two agents editing the |
| 18 | + spine produced the rebase churn on #1779. |
| 19 | +3. **Ask the owning-interface question before adding any gate**: "can this invariant be made |
| 20 | + impossible at the seam (a type only one function can produce, a required parameter, one |
| 21 | + construction path)?" If yes, do that; a cutover-row *extension* that recognizes a call shape is |
| 22 | + the fallback, not the default, and it is what AGENTS.md's "reconstructing a compiler" principle |
| 23 | + forbids once it needs a second omission patch. |
| 24 | + |
| 25 | +## The unit, in order |
| 26 | + |
| 27 | +Each step names its declaration site; read that, not prose. |
| 28 | + |
| 29 | +| Step | Where | |
| 30 | +| --- | --- | |
| 31 | +| Declare the use(s) with the one neutral `defineUse`; required-only by default, a preferred operation needs a recorded measurement in the unit review (§9) | `packages/contracts/src/platform-runtime-operations.ts` | |
| 32 | +| Plan resolution is pure and lives in contracts (`resolve<Command>RuntimePlan`), never in the handler | `packages/contracts/src/*-runtime-plan.ts` | |
| 33 | +| Handler: resolve plan → `inspectRequiredRuntimeUse(device, plan.use, inspectFacts)` → bind **once** with that use → operate. No admission-only binds, no `requireCommandSupported`, no capability bucket | `src/daemon/handlers/session-runtime-admission.ts`, the `snapshot` route in `src/daemon/snapshot-runtime.ts` as the model | |
| 34 | +| Descriptor flips to `platformExecution: { kind: 'device-runtime', use(s) }` in the same PR; the discriminator is exhaustive, so a forgotten descriptor fails typecheck | `src/core/command-descriptor/registry.ts` | |
| 35 | +| Each platform package reports exact-owner facts and implements the operations; provider ownership fails closed (missing behavior never falls through to a local owner) | `packages/platform-*/src/**`, `packages/provider-*/src/**` | |
| 36 | +| Add one row to the parametrized cutover table: `legacyRetirement` (what must be gone), `runtimeTypeNames`, `operations`, `singularExecution` with lexical `operationOwners`. The mechanism already carries the planted-red proof; a row that leaves a claim unstated is rejected by `cutoverRowDefects` | `scripts/layering/runtime-command-cutover-table.ts` (rule ids allocate upward; snapshot is R32) | |
| 37 | +| Delete: the legacy adapter, the descriptor's capability bucket + `requireCommandSupported` wiring, the retired route names — the row's `legacyRetirement` is the machine-checked list | wherever `legacyRetirement` points | |
| 38 | +| Tests: bind a fake runtime at the seam the handler consumes (`inspectFacts` / `bindDevice`), never a `dispatchCommand` mock. Move the command's existing tests off the dispatch mock in this PR | `src/daemon/__tests__/snapshot-runtime-fixture.ts` (fixture shape), `src/daemon/handlers/__tests__/session-command-harness.ts` (`mockInspectDeviceRuntimeFacts`, `mockBindDeviceRuntime`), `src/__tests__/test-utils/runtime-operation-facts.ts` (facts builders) | |
| 39 | +| Cross-cutting facets (freshness, system-chrome guard, presentation) land inside the first consuming unit and get one contract owner; later units consume, never fork | ADR 0019 §10 | |
| 40 | + |
| 41 | +## Evidence the unit review must contain |
| 42 | + |
| 43 | +- Request-scoped tier: the typed use declaration, fact coverage for every denominator cell, the |
| 44 | + enumerated legacy-parity cell table, and the cutover-gate row. Durable-resource tier adds ADR |
| 45 | + §4–5 lifecycle evidence. Importing durable machinery promotes the tier — say so. |
| 46 | +- Planted-red for anything new that is *not* a table row (a facet, a package boundary): revert, |
| 47 | + run, quote the failing line. A row needs no planted-red of its own. |
| 48 | +- Size: root-bytes-removed vs package-bytes-added and the four checkpoint metrics |
| 49 | + (`pnpm size --compare` against the base build), against the posted budget. Move-dominated is the |
| 50 | + rule; net growth is itemized, not explained away. |
| 51 | +- Live evidence for the changed path on at least one real target per family the denominator |
| 52 | + claims (`docs/agents/device-verification.md`); fixture-backed parity does not replace it. |
| 53 | +- Layering: `pnpm check:layering` green — R3 seam list narrows in the unit that removes an area's |
| 54 | + last platform import and never grows; R9/R10 must not grow (`docs/agents/testing.md`). |
| 55 | + |
| 56 | +## What "done" is not |
| 57 | + |
| 58 | +- A migrated command with a legacy fallback, a provider/local fallback, or a `dispatchCommand` |
| 59 | + branch left "just in case" — the row's retirement claim rejects it, and so does review. |
| 60 | +- Tests green because they mock the old seam. Grep the command's name across |
| 61 | + `vi.mock('.../core/dispatch.ts')` users before calling the tail closed. |
| 62 | +- A per-command policy file in `scripts/layering/`. If the row's generalized columns cannot |
| 63 | + express the invariant, first return to step 3; extensions are the exception and each one names |
| 64 | + what the seam could not make impossible. |
0 commit comments