You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Turn the daemon into a serialized application host rather than the home of replay, Maestro, and
replay-test semantics. Contributors should be able to change one module by reading one façade and
its directory, with authority crossing seams only through narrow capabilities.
This issue is the implementation handoff for #1451. PR #1451 contains the architecture evidence,
ADRs, measurements, and design probes. The decisions below are the result of grilling those sketches
against current call sites and supersede the exact TypeScript sketches in the proposal where they
differ.
Start condition: merge #1451, then branch each implementation PR from the latest main.
Sequencing preconditions: P3 starts only after #1448 merges or its Vitest-runner changes are
otherwise accounted for. P0 remeasures every baseline on that latest main; the proposal R9
baseline is 102, and #1480 does not change the production dependency graph.
Preserve the shipped custom reporter object/factory contract, hook values, timing/order, output
context, error handling, and exit-code behavior exactly.
Do not publish reporter v2, deprecate the current shape, add a compatibility adapter, or write
migration guidance in this initiative. A new public reporter interface requires a demonstrated
external need and a separate compatibility decision.
Preserve ADRs 0001–0017, especially request admission/locking, provider-first translation, replay
resume/repair, ref-frame lifetime, direct Maestro semantics, active publication, and parameterized
recorded inputs.
The one intentional hardening is unsupported raw saveScript flags outside the released open, close, and replay flag owners. Pin fail-closed rejection at the daemon seam before deleting the
surface-dormant record-only writer.
Change internal interfaces atomically. Do not carry compatibility shims for private imports.
State crosses seams as immutable values, authority as capabilities, and both only narrow.
A seam exists when its port has two real adapters.
Calls go down through module façades and back through ports. Do not add an inter-module event bus.
Each logical module has one façade, private implementation under internal/, and tests mirroring
the source topology.
No engine receives DaemonRequest, DaemonInvokeFn, DaemonError, SessionStore, mutable SessionState, provider handles, or concrete platform implementations.
No imports into another module's internal/ tree.
Local filesystems use temporary directories in tests; do not invent a filesystem port only for
mocking.
Locked module decisions
Replay-test
Use a function façade, not a framework class: runReplayTestSuite(request, host): Promise<ReplayTestSuiteResult>.
Replay-test is format-agnostic. It imports neither engine and sees only a minimal manifest plus
neutral attempt values/outcomes.
Replay-test owns discovery policy, filtering, sharding, retries, fail-fast, timeout policy,
attempt identity, result aggregation, reporter events, and the ordering/guarantees of attempt
finalization and cleanup.
The daemon adapter owns how an attempt is executed, finalized, and cleaned up. It maps an
engine-neutral ReplayTestAttemptId to daemon request/session identifiers, binds cancellation,
manages video/artifacts, and routes to the correct engine. The scheduler creates the attempt ID;
the host must not issue identity back to it.
Finalization happens before cleanup in replay-test-owned finally orchestration. Moving
orchestration does not give replay-test access to SessionStore or daemon lifecycle authority.
Keep the host small. Prefer one object created by a daemon factory over a class hierarchy.
Do not add source format, app ID, environment, Maestro tags/steps, digest, includes, config, or
source path unless a demonstrated scheduler or reporter call site requires it.
Discovery and format routing
Use one internal path expansion/traversal/ordering implementation for both .ad and Maestro test
inputs, matching Maestro behavior for explicit files, directories, and globs. Do not maintain a
second .ad ordering implementation.
Discovery is implementation-private. No public discovery interface is better than an unearned
interface.
Route format once, above both engines, with a pure internal resolver: resolveReplayFormat(sourcePath, backend): 'ad' | 'maestro'.
.ad always means native replay, including inside a suite run with --maestro.
.yaml/.yml means Maestro only when the Maestro backend was explicitly requested.
Do not parser-probe, register pluggable formats, fall back from one engine to another, or let
native replay import Maestro. Delete runTypedReplayIfNeeded after cutover.
Reporter preservation
The shipped reporter interface already has the desired seam: reporters receive
suite/test/progress/result values and output facilities only. Those values carry no authority to
inspect or control daemon, session, engine, attempt-finalization, or cleanup state.
Preserve the current module export spellings, object/factory loading, hook names, hook
timing/order, value fields, synchronous live-hook rule, awaited suite completion, error handling,
and exit-code recommendation semantics.
Reuse the existing typed semantic reporter event shape as private implementation. Move generic RequestProgressEvent translation to the CLI/daemon adapter as needed so replay-test has no
request-global import.
Do not add a public v2, deprecate the shipped shape, export new package types solely for this
extraction, or create migration docs. A better public interface can follow when an actual custom
reporter needs capability the current value-only contract cannot provide.
Do not expose engine IR, daemon errors, attempt sessions, runtime callbacks, or cleanup authority
merely because a reporter might find them convenient.
There is no module-public PreparedAdReplay. The module reads the source, expands includes,
computes provenance/digest, validates resume, plans, and executes behind its façade.
Cancellation is run control and belongs to the run request. The bound runtime inherits the same
request cancellation; do not make cancellation a device capability.
The runtime exposes only demonstrated execution/capture capabilities. Progress is a separate,
narrow output port, never a generic event stream.
Expected pass/fail/timeout/cancel/infrastructure states resolve as tagged outcomes. Throw only for
invalid programmatic usage or violated internal invariants.
Failure tags carry explicit neutral fields. Do not pass DaemonError, legacy, metadata, or an
opaque daemon details bag through the engine.
Parsing, include expansion, variables, plan digest, target verification, resume, divergence, and
repair suggestions become private implementation. The canonical .ad codec remains a
repository-private leaf shared only with session script publication.
Maestro
Keep Maestro as a source-preserving typed engine over MaestroRuntimePort.
Do not lower Maestro into .ad, create a shared replay VM, or define a common operation union.
Selectors, control flow, generations, stabilization, divergence, and repair semantics remain
Maestro-owned.
Add one Maestro façade and route non-test consumers through it. Keep engine IR, parser, plan,
ranking, expression state, and compatibility policy private.
Move the five daemon-runtime-* files from src/compat/maestro/ to src/daemon/adapters/maestro/; move only genuinely neutral snapshot helpers to a neutral module.
Internal Maestro observations use daemon-private capture. They must not call the public snapshot operation and must never publish a client ref frame.
Native and Maestro may share daemon-private request-bound command/capture mechanics below their
separate adapters. That shared implementation is not a module-public runtime vocabulary.
Session ownership and ref publication
The daemon remains the composition root and owns request admission, locks, leases, provider
binding, session lifetime, diagnostics, artifacts, response projection, and finalization.
Engines return outcomes. A daemon-private locked coordinator applies replay repair/publication
transitions and maps outcomes to the public wire.
Collapse co-resident replay/record/publication flags into one tagged private aggregate. The
aggregate and both ReplaySessionTransaction and SessionScriptPublication projections land
atomically in P4a; do not keep parallel old/new repair state. Neither projection crosses into an
engine.
The repair variant persists output target plus per-target force authorization and explicit armed, complete, close-succeeded, committed, and aborted status. close-succeeded carries a RepairPlatformCloseReceipt keyed to the effective platform-close
operation.
SessionScriptPublication owns close sequencing through a narrow operation identity plus bound
performer. Do not pass a caller-computed platformCloseSucceeded boolean.
Reserve mutable session records for capability implementations. Ordinary readers use immutable SessionView projections. Update R7 with every moved field cluster.
Keep the three consistency disciplines distinct:
immediate pessimistic transitions for ref/observation lineage;
staged arm/complete/close-succeeded/commit-or-abort protocols for repair/publication, with
operation-keyed receipts retained across retries;
append-only facts for recorded actions and diagnostics.
Do not introduce Redux, end-of-request rollback, a generic session transaction, or a session
event bus.
Internal observation capture updates operational observation state only; it does not issue refs.
Direct replay publishes refs only after the daemon has projected the exact outward response.
The engine may carry opaque capture evidence in a divergence outcome but has no publication or
commit capability. Daemon-private finalization validates that the capture still belongs to the
same locked session/snapshot/generation/runtime revision, applies redaction and response-level
caps, then synchronously activates exactly the exposed refs before returning the response.
A later capture, command, cancellation, session replacement/close, generation change,
finalization, or request end invalidates stale capture evidence.
Refs exposed inline or in a successfully exposed overflow artifact count as published. Failed
artifact creation with no inline refs publishes nothing. An empty set never supersedes the prior
frame.
Replay-test attempts clean up their session before the suite result is returned, so reporters get
values/artifact references but no promise of lasting ref authority.
Platform coupling and workspaces
Preserve provider-first translation and the current Interactor semantics.
Move concrete platform resources out of SessionState behind opaque daemon-owned handles. The
Apple perf edge is the measured load-bearing R9 edge; the Android-only move is ownership hygiene
and must not be credited with SCC reduction.
Introduce request-bound platform binding and focused facets only where current local/provider
implementations demonstrate a real seam. Do not create a universal command dispatcher.
Packaging enforces an already-earned module interface; it does not itself earn a port or justify
widening one. Leaf vocabulary packages may expose focused domain subpaths, while the deep
Maestro/replay-test/ad-replay packages export only their façade at ..
Preserve one published npm artifact, a packed manifest with no private @agent-device/*
runtime dependencies, and lazy platform loading.
Execution graph
One worker owns one PR. Rebase each worker from the dependency's merged commit. The default schedule
is serial through P5: avoiding merge repair and duplicate abstractions is more valuable than
speculative parallel throughput.
P0-P5 plus #1490's W-foundation are the committed product-facing extraction arc. W1b/W1c/W1d are
sibling provider-package work and do not block P2 once W0 and W1a have merged. Do not run P2 and P4a
concurrently by default; both alter routing/session neighborhoods and the merge-risk concentration is
not worth the saved wall time. P6 proceeds only after the explicit checkpoint below.
Worker briefs
P0 — Characterize contracts and install migration ratchets
Goal: make accidental behavior drift fail before files move.
Pin current CLI/Node/MCP/RPC shapes, shipped reporter loading/hooks/timing/error/exit behavior,
artifact layout, exit codes, cancellation/timeout/finalize/cleanup order, provider-scope reuse,
and nested same-session execution.
Add format-routing tests for .ad under Maestro mode and YAML without explicit Maestro mode.
Pin shared discovery ordering for explicit files, directories, globs, duplicates, and mixed .ad/YAML inputs against current Maestro-compatible behavior.
Record R7/R9/module import baselines. The proposal measured R9 at 102; remeasure rather than
copying that number after prerequisites merge. Do not scaffold unused façades or speculative
ports.
Done: all characterization tests and gates pass; production behavior is unchanged.
P1 — Correct observation and ref publication
Goal: correct product behavior so internal captures never alter client ref authority and direct
replay publishes only what it actually exposes. Review this as an ADR 0014 behavior change, not as a
file-move refactor.
Add the daemon-private capture path and paired synchronous finalizer.
Switch native verification/divergence and Maestro internal observations away from public snapshot
issuance.
Validate capture lineage and exact wire/artifact ref projection.
Keep transport failure after publication fail-closed; do not roll back to older authority.
Add ref-publication counterfactuals for internal observation, direct divergence at
default/digest/full levels, overflow success/failure, stale capture, cancellation, and empty
publication in the same PR as the correction.
Run a purpose-built live divergence/ref-publication scenario on both iOS and Android following docs/agents/device-verification.md. Provider fixtures do not satisfy this evidence requirement;
attach the commands, target identity, and observed ref-authority result to the PR.
Done: ADR 0014 tests cover stale/superseded capture, later command, cancellation, session close,
generation change, response-level caps, overflow, artifact failure, and empty sets; the live iOS and
Android scenarios both pass. If either live target is unavailable, report residual risk and do not
mark the PR ready.
STOP if: correctness appears to require engine-callable publication, asynchronous work between
ref activation and response return, or rollback of pre-side-effect expiry.
P2 — Correct Maestro ownership and centralize routing/discovery
Goal: Maestro is a deep independent module; daemon-specific code lives in the daemon.
Add the single exported façade at packages/maestro and move the five daemon adapter files plus
mirrored tests to src/daemon/adapters/maestro/.
Move only helpers with two real neutral consumers.
Add the pure central format resolver and delete native-to-Maestro routing.
Replace duplicate .ad discovery ordering with the one Maestro-compatible implementation.
Return typed Maestro failure identity from outcomes instead of reconstructing it from an observer.
Gate Maestro-to-daemon imports at zero.
Done:packages/maestro exports only . and imports no root src/,
daemon/platform/provider/native replay; .ad never enters Maestro; pnpm maestro:conformance
remains green.
STOP if: the move requires a shared replay VM, parser probing, engine fallback, or promoting DaemonRequest into contracts.
P3 — Extract replay-test while preserving reporters
Goal: scheduling/reporting becomes a format-neutral deep module with one daemon adapter and no
new public reporter surface.
Before moving production code, characterize the exact reporter start/step/result values (especially session) and the late-timeout finalization/double-cleanup behavior.
Extend R10 so replay-test also rejects imports from src/request/** and engine internals.
Move the session-test* scheduler/runtime behavior and mirrored tests into packages/replay-test/internal/, with one package-root façade export.
Add the function façade, minimal manifest, neutral outcomes, scheduler-owned attempt IDs, and
format-neutral host.
Keep attempt finalization-before-cleanup orchestration inside replay-test; keep daemon effects in
the adapter.
Preserve the shipped reporter object/factory contract and every timing/error/exit semantic pinned
by P0. Reuse the existing value-only semantic event shape privately and move generic request
progress translation to the adapter.
Do not add reporter v2, a v1 adapter, deprecation, migration docs, or new public type exports.
Remove daemon, platform, request-global, and engine-internal imports from replay-test.
Consolidate the live iOS/Android test-suite harness at the value-contract boundary: one shared
helper owns public test invocation, JUnit validation, per-script status, and replay-count
assertions; platform callers retain scripts, retry policy, command evidence, and behavioral
claims. Do not create a platform-configured mega-runner or template platform-specific .ad
journeys.
Done:packages/replay-test exports only . and has zero root src/,
daemon/platform/provider/engine-internal/request-global imports; its façade is the only permitted
consumer import; all shipped custom reporters and public test behavior still pass without a
compatibility adapter.
STOP if: a scheduler decision requires mutable daemon state, engine-specific manifest fields,
reporter control over attempts/sessions, or a reporter-contract break. A demonstrated need for a new
public reporter interface becomes a separate proposal.
P4-pre — Reject unsupported raw saveScript flags
Goal: remove the surface-dormant record-only writer and close every equivalent raw-wire arming path
in a small behavior-hardening PR before the highest-blast-radius state migration.
At daemon ingress, reject flags.saveScript on every command outside the released open, close,
and replay flag owners, including record, trace, and other recordable commands.
Prove unsupported requests cannot reach admission/device work, arm script publication, or write an
artifact through either HTTP or socket transport.
Delete the record-only immediate writer invocation only after those seam tests are green.
Do not introduce the tagged aggregate or migrate supported publication writers in this PR.
Done: supported CLI/Node/MCP behavior is unchanged; the raw request is pinned closed; the dormant
writer invocation is deleted.
STOP if: closing this path changes a released surface or requires touching the P4a aggregate
migration.
P4a — Encapsulate session script publication
Goal: four supported publication paths consume one capability and one tagged state machine.
Introduce the tagged aggregate and both daemon-private capability projections atomically. Migrate
every old field writer in the same PR; do not leave a shadow repair/publication state.
Persist output target and its per-target force authorization with repair status. Changing the
target without live force clears the previous target's authorization.
Put active publish, ordinary close, repair close, and lifecycle teardown behind SessionScriptPublication.
Give the capability the effective platform-close operation identity and a bound performer. It
derives retargeting without mutation, runs an unmatched close, then persists target plus close-succeeded receipt before atomic publication.
Pin the failure/retry transitions: platform-close failure leaves state unchanged; publication
failure retains target/force plus receipt; the same operation retry skips close dispatch; a
different operation identity dispatches; committed and aborted states are explicit.
Consolidate the corresponding R7 rows.
Done: one aggregate and two projections own the migrated state; one publication capability owns
all supported writes; no engine can call either projection; no old field/writer assembly path
remains.
STOP if: the migration needs parallel repair state, a caller-computed close-success boolean, or
clears a close receipt before the retained session has reached terminal lifecycle cleanup.
P4b — Centralize replay transaction use in the locked coordinator
Goal: all use of the P4a replay projection is serialized behind one daemon-owned coordinator
before engine code moves.
Add a daemon-private coordinator around the current native replay path.
Route arm, corrective watermark, divergence, complete, abort, and tombstone calls through the ReplaySessionTransaction projection created in P4a; remove scattered direct transition calls.
Add immutable SessionView projections for readers touched by this slice.
Preserve admission/lease/lock/provider ordering and map neutral failures to the existing wire.
Keep request-scoped execute/capture mechanics private to daemon adapters.
Done: current replay behavior uses the P4a aggregate only through one locked coordinator;
engines cannot name/relock a session or reacquire a provider; R7 shrinks.
P5 — Extract native .ad replay
Goal: move native program semantics behind inspectAdReplay/runAdReplay.
Move parsing, includes, variables, planning, digest/resume, verification, divergence, and typed
outcomes into packages/ad-replay/src/internal/, with one package-root façade export.
Add the narrow runtime plus deterministic in-memory adapter and run one contract suite against
both it and the daemon adapter.
Keep the canonical codec private and shared only with publication.
Cut all production callers over in the same PR, then delete superseded helpers and handler-mock
tests. Do not retain a fallback engine.
Keep all engine files outside the largest R9 SCC.
After building the extracted engine and refreshing daemon/helper state, run pnpm test-app:replay:ios and pnpm test-app:replay:android against live targets following docs/agents/device-verification.md; preserve the artifacts and target details as PR evidence.
Done:packages/ad-replay exports only . and has zero root src/,
daemon/platform/provider/Maestro imports; no deep importer bypasses the façade; released replay
behavior and provenance remain byte/semantics compatible; both live test-app replay suites pass
against the extracted engine. Missing live evidence is residual risk, not a ready-to-merge state.
P6 decision checkpoint
Goal: decide whether platform modularity is the next best investment rather than inheriting it
from P0-P5 momentum.
After P5, remeasure R9, concrete platform state in SessionState, daemon-to-platform imports,
platform-condition sites, adapter sizes, and contribution-locality pain.
Weigh the measured residual coupling against current product work such as replay composition, the
Maestro importer, and replay-corpus expansion.
Record an explicit go or defer decision in this issue. Deferring P6 is a valid completion of the
checkpoint and proceeds directly to P7.
Done: the decision cites current measurements and product priorities; no P6 worker starts from
the original proposal's momentum alone.
P6a/P6b — Narrow session resources and platform binding (conditional)
Goal: if the checkpoint says go, remove concrete platform state and routine platform branching
from daemon/session code.
P6a: introduce the ordered resource ledger, replace concrete perf/log/recording/helper state with
opaque handles, unify close/expiry/reap/shutdown cleanup, and lower the R9 baseline in the same PR
when the Apple perf edge is removed.
P6b: inject an immutable platform registry, bind one request-scoped device, and move focused
lifecycle/log/recording/perf mechanics behind facets one facet per PR where necessary.
Keep selectors/ref policy, settle/verify, response building, and guarantee classification in
daemon/core.
Measure platform-condition branches and concrete imports after every facet; do not claim success
from moved LOC alone.
Done: no concrete platform resource types remain in SessionState; composition/adapters own
concrete platform imports; R9 reaches the evidence-backed bound without recreating the Apple edge.
STOP if: a proposed facet has only one implementation, hides guarantee-path evidence, bypasses
provider-first integration, or becomes a universal command gateway.
Workspace foundation dependency
#1490 supersedes the optional-workspace decision. After P1 merges, complete W-pre, W0, and W1a
before P2. This is the minimum foundation the replay chain needs: workspace/toolchain enforcement, @agent-device/kernel, and value-only shared contracts. Provider leaf extraction (W1b/W1c/W1d)
may proceed independently after W1a and does not block P2.
P2/P3/P5 land directly as packages/maestro, packages/replay-test, and packages/ad-replay; do not create temporary logical modules under src/ and move them again.
Daemon adapters remain in root composition. Package extraction must not widen a façade, create a
port without two real adapters, or expose implementation files merely to make imports compile.
P7 — Clean internal architecture documentation
Goal: leave durable decisions, not migration history.
Audit internal docs against code, ADRs, registries, help, and gates.
Update ADRs/CONTEXT.md only with durable “why” and accepted invariants.
Delete superseded proposals, completed migration/status tables, duplicate architecture prose, and
guidance now enforced by code or registries.
Remove the daemon-boundary prototypes, their README, and prototype:* package scripts after the
relevant modules and contract suites have landed.
Keep module-interface-principles.md only if it still carries durable guidance not better owned by CONTEXT.md, AGENTS.md, an ADR, or a gate.
Check all remaining internal links. Do not move historical prose into a new archive folder; git is
the archive.
ADR 0018 is a separate optional follow-up, not Phase 7 and not a prerequisite.
Done: current docs describe the current system; deleted history remains recoverable through git;
no prototype or stale migration entry point remains.
Validation
Every worker runs:
pnpm check:affected --base origin/main --run
Also run the focused gates selected by the touched contract:
replay grammar/provenance: pnpm exec vitest run --project unit-core test/replay-compat and pnpm check:replay-compat;
Maestro: pnpm maestro:conformance;
daemon/session concurrency: the focused unit suites plus test/integration/nightly/concurrency-torture.test.ts;
platform/provider response work: pnpm test:integration:provider and pnpm test:coverage;
P1 behavior correction: purpose-built live iOS and Android divergence/ref-publication evidence;
P5 engine cutover: pnpm test-app:replay:ios and pnpm test-app:replay:android on live targets;
final integration PR: pnpm check.
Use pnpm format, never a path-local formatter. CI remains authoritative.
Success metrics
daemon-server production LOC below 42,000 after extraction, with adapters below 300 LOC each;
package-to-root-src/ imports: 0; deep engine packages export only .;
the packed agent-device manifest contains 0 private @agent-device/* runtime dependencies;
compat/engine to daemon imports: 0;
replay-test to daemon/platform/provider/engine-internal imports: 0;
external production imports of daemon/types.ts: 0 by Maestro/replay-test cutover;
engine files in the largest SCC: 0;
R9 does not grow during engine work; if P6 proceeds, it reaches at most 91 after the
load-bearing Apple resource edge is removed;
if P6 proceeds, concrete platform state types in SessionState: 0;
if P6 proceeds, daemon platform-condition sites and concrete imports decrease per extracted
facet;
one script-publication capability owns four supported paths; unsupported raw writer removed;
no public compatibility regression and no duplicate/fallback engine.
Initiative STOP conditions
Stop the current worker and report rather than improvising if:
a released public contract must break or rpcProtocolVersion appears to need a bump;
an engine appears to require daemon/session/provider/concrete-platform authority;
a move introduces a second production path, parser probing, fallback engine, shared replay VM, or
generic event bus;
a test only passes by widening a module façade or adding test-only dependency injection;
the source has drifted enough that the named ownership/call-site evidence no longer holds;
the PR crosses more than one worker brief without an explicit rescope.
Handoff protocol
One worker/branch/PR per brief; conventional commit titles.
Link the PR here and check off only machine-verified criteria.
Report files moved/created/deleted, focused and aggregate gates run, R7/R9/import/LOC deltas, and
any released compatibility behavior intentionally retained.
Delete superseded code in the same PR that completes a cutover.
Do not merge workspace packaging or documentation cleanup before their decision gates.
Outcome
Turn the daemon into a serialized application host rather than the home of replay, Maestro, and
replay-test semantics. Contributors should be able to change one module by reading one façade and
its directory, with authority crossing seams only through narrow capabilities.
This issue is the implementation handoff for #1451. PR #1451 contains the architecture evidence,
ADRs, measurements, and design probes. The decisions below are the result of grilling those sketches
against current call sites and supersede the exact TypeScript sketches in the proposal where they
differ.
Start condition: merge #1451, then branch each implementation PR from the latest
main.Sequencing preconditions: P3 starts only after #1448 merges or its Vitest-runner changes are
otherwise accounted for. P0 remeasures every baseline on that latest
main; the proposal R9baseline is 102, and #1480 does not change the production dependency graph.
Planned against:
ef1a71afe(proposal branch), 2026-07-29.Tracking
packages/maestro— completed in refactor: isolate maestro engine package #1506packages/replay-testwhile preserving the shipped reporter contract — P3a seam neutralization completed in refactor(replay-test): neutralize the values crossing the scheduler seam (#1478 P3, part 1) #1509; P3b package extraction pendingsaveScriptflags and remove the record-only writer.adreplay topackages/ad-replayNon-negotiable compatibility
This is an internal refactor for every supported surface.
artifact layout, exit codes, timeout/cancellation behavior, and
rpcProtocolVersion.context, error handling, and exit-code behavior exactly.
migration guidance in this initiative. A new public reporter interface requires a demonstrated
external need and a separate compatibility decision.
code,message,hint,diagnosticId,logPath, and typedretry/support/divergence data.
resume/repair, ref-frame lifetime, direct Maestro semantics, active publication, and parameterized
recorded inputs.
saveScriptflags outside the releasedopen,close, andreplayflag owners. Pin fail-closed rejection at the daemon seam before deleting thesurface-dormant record-only writer.
Target shape
Rules:
internal/, and tests mirroringthe source topology.
DaemonRequest,DaemonInvokeFn,DaemonError,SessionStore, mutableSessionState, provider handles, or concrete platform implementations.internal/tree.mocking.
Locked module decisions
Replay-test
Use a function façade, not a framework class:
runReplayTestSuite(request, host): Promise<ReplayTestSuiteResult>.Replay-test is format-agnostic. It imports neither engine and sees only a minimal manifest plus
neutral attempt values/outcomes.
Replay-test owns discovery policy, filtering, sharding, retries, fail-fast, timeout policy,
attempt identity, result aggregation, reporter events, and the ordering/guarantees of attempt
finalization and cleanup.
The daemon adapter owns how an attempt is executed, finalized, and cleaned up. It maps an
engine-neutral
ReplayTestAttemptIdto daemon request/session identifiers, binds cancellation,manages video/artifacts, and routes to the correct engine. The scheduler creates the attempt ID;
the host must not issue identity back to it.
Finalization happens before cleanup in replay-test-owned
finallyorchestration. Movingorchestration does not give replay-test access to
SessionStoreor daemon lifecycle authority.Keep the host small. Prefer one object created by a daemon factory over a class hierarchy.
The neutral manifest is intentionally small:
Do not add source format, app ID, environment, Maestro tags/steps, digest, includes, config, or
source path unless a demonstrated scheduler or reporter call site requires it.
Discovery and format routing
.adand Maestro testinputs, matching Maestro behavior for explicit files, directories, and globs. Do not maintain a
second
.adordering implementation.interface.
resolveReplayFormat(sourcePath, backend): 'ad' | 'maestro'..adalways means native replay, including inside a suite run with--maestro..yaml/.ymlmeans Maestro only when the Maestro backend was explicitly requested.native replay import Maestro. Delete
runTypedReplayIfNeededafter cutover.Reporter preservation
suite/test/progress/result values and output facilities only. Those values carry no authority to
inspect or control daemon, session, engine, attempt-finalization, or cleanup state.
timing/order, value fields, synchronous live-hook rule, awaited suite completion, error handling,
and exit-code recommendation semantics.
RequestProgressEventtranslation to the CLI/daemon adapter as needed so replay-test has norequest-global import.
extraction, or create migration docs. A better public interface can follow when an actual custom
reporter needs capability the current value-only contract cannot provide.
merely because a reporter might find them convenient.
Native
.adreplayinspectAdReplay(sourcePath): AdReplayManifestrunAdReplay(request, runtime): Promise<AdReplayOutcome>PreparedAdReplay. The module reads the source, expands includes,computes provenance/digest, validates resume, plans, and executes behind its façade.
request cancellation; do not make cancellation a device capability.
narrow output port, never a generic event stream.
invalid programmatic usage or violated internal invariants.
DaemonError,legacy,metadata, or anopaque daemon
detailsbag through the engine.repair suggestions become private implementation. The canonical
.adcodec remains arepository-private leaf shared only with session script publication.
Maestro
MaestroRuntimePort..ad, create a shared replay VM, or define a common operation union.Selectors, control flow, generations, stabilization, divergence, and repair semantics remain
Maestro-owned.
ranking, expression state, and compatibility policy private.
daemon-runtime-*files fromsrc/compat/maestro/tosrc/daemon/adapters/maestro/; move only genuinely neutral snapshot helpers to a neutral module.snapshotoperation and must never publish a client ref frame.separate adapters. That shared implementation is not a module-public runtime vocabulary.
Session ownership and ref publication
binding, session lifetime, diagnostics, artifacts, response projection, and finalization.
transitions and maps outcomes to the public wire.
aggregate and both
ReplaySessionTransactionandSessionScriptPublicationprojections landatomically in P4a; do not keep parallel old/new repair state. Neither projection crosses into an
engine.
armed,complete,close-succeeded,committed, andabortedstatus.close-succeededcarries aRepairPlatformCloseReceiptkeyed to the effective platform-closeoperation.
SessionScriptPublicationowns close sequencing through a narrow operation identity plus boundperformer. Do not pass a caller-computed
platformCloseSucceededboolean.SessionViewprojections. Update R7 with every moved field cluster.operation-keyed receipts retained across retries;
event bus.
Direct replay publishes refs only after the daemon has projected the exact outward response.
commit capability. Daemon-private finalization validates that the capture still belongs to the
same locked session/snapshot/generation/runtime revision, applies redaction and response-level
caps, then synchronously activates exactly the exposed refs before returning the response.
finalization, or request end invalidates stale capture evidence.
artifact creation with no inline refs publishes nothing. An empty set never supersedes the prior
frame.
values/artifact references but no promise of lasting ref authority.
Platform coupling and workspaces
Interactorsemantics.SessionStatebehind opaque daemon-owned handles. TheApple perf edge is the measured load-bearing R9 edge; the Android-only move is ownership hygiene
and must not be credited with SCC reduction.
implementations demonstrate a real seam. Do not create a universal command dispatcher.
foundation lands after P1 and before P2; P2/P3/P5 then land directly as workspace packages rather
than moving through temporary
src/<module>/destinations.widening one. Leaf vocabulary packages may expose focused domain subpaths, while the deep
Maestro/replay-test/ad-replay packages export only their façade at
..@agent-device/*runtime dependencies, and lazy platform loading.
Execution graph
One worker owns one PR. Rebase each worker from the dependency's merged commit. The default schedule
is serial through P5: avoiding merge repair and duplicate abstractions is more valuable than
speculative parallel throughput.
flowchart TD P0["P0 — Characterize contracts + ratchets"] --> P1["P1 — Correct observation/ref publication"] P1 --> WF["W-foundation — #1490 W-pre/W0/W1a"] WF --> P2["P2 — Package Maestro + central routing/discovery"] P2 --> P3["P3 — Package replay-test; preserve reporters"] P3 --> P4P["P4-pre — Close unsupported raw saveScript flags"] P4P --> P4A["P4a — Session script publication capability"] P4A --> P4B["P4b — Locked replay coordinator"] P4B --> P5["P5 — Package native .ad module"] P5 --> P6G{"P6 platform-modularity work now?"} P6G -->|"go"| P6A["P6a — Session resource ledger + platform handles"] P6A --> P6B["P6b — Request-bound platform facets"] P6B --> P7["P7 — Internal docs + prototype cleanup"] P6G -->|"defer"| P7P0-P5 plus #1490's W-foundation are the committed product-facing extraction arc. W1b/W1c/W1d are
sibling provider-package work and do not block P2 once W0 and W1a have merged. Do not run P2 and P4a
concurrently by default; both alter routing/session neighborhoods and the merge-risk concentration is
not worth the saved wall time. P6 proceeds only after the explicit checkpoint below.
Worker briefs
P0 — Characterize contracts and install migration ratchets
Goal: make accidental behavior drift fail before files move.
artifact layout, exit codes, cancellation/timeout/finalize/cleanup order, provider-scope reuse,
and nested same-session execution.
.adunder Maestro mode and YAML without explicit Maestro mode..ad/YAML inputs against current Maestro-compatible behavior.copying that number after prerequisites merge. Do not scaffold unused façades or speculative
ports.
Done: all characterization tests and gates pass; production behavior is unchanged.
P1 — Correct observation and ref publication
Goal: correct product behavior so internal captures never alter client ref authority and direct
replay publishes only what it actually exposes. Review this as an ADR 0014 behavior change, not as a
file-move refactor.
issuance.
default/digest/full levels, overflow success/failure, stale capture, cancellation, and empty
publication in the same PR as the correction.
docs/agents/device-verification.md. Provider fixtures do not satisfy this evidence requirement;attach the commands, target identity, and observed ref-authority result to the PR.
Done: ADR 0014 tests cover stale/superseded capture, later command, cancellation, session close,
generation change, response-level caps, overflow, artifact failure, and empty sets; the live iOS and
Android scenarios both pass. If either live target is unavailable, report residual risk and do not
mark the PR ready.
STOP if: correctness appears to require engine-callable publication, asynchronous work between
ref activation and response return, or rollback of pre-side-effect expiry.
P2 — Correct Maestro ownership and centralize routing/discovery
Goal: Maestro is a deep independent module; daemon-specific code lives in the daemon.
packages/maestroand move the five daemon adapter files plusmirrored tests to
src/daemon/adapters/maestro/..addiscovery ordering with the one Maestro-compatible implementation.Done:
packages/maestroexports only.and imports no rootsrc/,daemon/platform/provider/native replay;
.adnever enters Maestro;pnpm maestro:conformanceremains green.
STOP if: the move requires a shared replay VM, parser probing, engine fallback, or promoting
DaemonRequestinto contracts.P3 — Extract replay-test while preserving reporters
Goal: scheduling/reporting becomes a format-neutral deep module with one daemon adapter and no
new public reporter surface.
session) and the late-timeout finalization/double-cleanup behavior.src/request/**and engine internals.session-test*scheduler/runtime behavior and mirrored tests intopackages/replay-test/internal/, with one package-root façade export.format-neutral host.
the adapter.
by P0. Reuse the existing value-only semantic event shape privately and move generic request
progress translation to the adapter.
test-suite harness at the value-contract boundary: one sharedhelper owns public
testinvocation, JUnit validation, per-script status, and replay-countassertions; platform callers retain scripts, retry policy, command evidence, and behavioral
claims. Do not create a platform-configured mega-runner or template platform-specific
.adjourneys.
Done:
packages/replay-testexports only.and has zero rootsrc/,daemon/platform/provider/engine-internal/request-global imports; its façade is the only permitted
consumer import; all shipped custom reporters and public test behavior still pass without a
compatibility adapter.
STOP if: a scheduler decision requires mutable daemon state, engine-specific manifest fields,
reporter control over attempts/sessions, or a reporter-contract break. A demonstrated need for a new
public reporter interface becomes a separate proposal.
P4-pre — Reject unsupported raw
saveScriptflagsGoal: remove the surface-dormant record-only writer and close every equivalent raw-wire arming path
in a small behavior-hardening PR before the highest-blast-radius state migration.
flags.saveScripton every command outside the releasedopen,close,and
replayflag owners, includingrecord,trace, and other recordable commands.artifact through either HTTP or socket transport.
Done: supported CLI/Node/MCP behavior is unchanged; the raw request is pinned closed; the dormant
writer invocation is deleted.
STOP if: closing this path changes a released surface or requires touching the P4a aggregate
migration.
P4a — Encapsulate session script publication
Goal: four supported publication paths consume one capability and one tagged state machine.
every old field writer in the same PR; do not leave a shadow repair/publication state.
target without live force clears the previous target's authorization.
SessionScriptPublication.derives retargeting without mutation, runs an unmatched close, then persists target plus
close-succeededreceipt before atomic publication.disjointness, retry behavior, and failure tombstones.
failure retains target/force plus receipt; the same operation retry skips close dispatch; a
different operation identity dispatches; committed and aborted states are explicit.
Done: one aggregate and two projections own the migrated state; one publication capability owns
all supported writes; no engine can call either projection; no old field/writer assembly path
remains.
STOP if: the migration needs parallel repair state, a caller-computed close-success boolean, or
clears a close receipt before the retained session has reached terminal lifecycle cleanup.
P4b — Centralize replay transaction use in the locked coordinator
Goal: all use of the P4a replay projection is serialized behind one daemon-owned coordinator
before engine code moves.
ReplaySessionTransactionprojection created in P4a; remove scattered direct transition calls.SessionViewprojections for readers touched by this slice.Done: current replay behavior uses the P4a aggregate only through one locked coordinator;
engines cannot name/relock a session or reacquire a provider; R7 shrinks.
P5 — Extract native
.adreplayGoal: move native program semantics behind
inspectAdReplay/runAdReplay.outcomes into
packages/ad-replay/src/internal/, with one package-root façade export.both it and the daemon adapter.
tests. Do not retain a fallback engine.
pnpm test-app:replay:iosandpnpm test-app:replay:androidagainst live targets followingdocs/agents/device-verification.md; preserve the artifacts and target details as PR evidence.Done:
packages/ad-replayexports only.and has zero rootsrc/,daemon/platform/provider/Maestro imports; no deep importer bypasses the façade; released replay
behavior and provenance remain byte/semantics compatible; both live test-app replay suites pass
against the extracted engine. Missing live evidence is residual risk, not a ready-to-merge state.
P6 decision checkpoint
Goal: decide whether platform modularity is the next best investment rather than inheriting it
from P0-P5 momentum.
SessionState, daemon-to-platform imports,platform-condition sites, adapter sizes, and contribution-locality pain.
Maestro importer, and replay-corpus expansion.
checkpoint and proceeds directly to P7.
Done: the decision cites current measurements and product priorities; no P6 worker starts from
the original proposal's momentum alone.
P6a/P6b — Narrow session resources and platform binding (conditional)
Goal: if the checkpoint says go, remove concrete platform state and routine platform branching
from daemon/session code.
opaque handles, unify close/expiry/reap/shutdown cleanup, and lower the R9 baseline in the same PR
when the Apple perf edge is removed.
lifecycle/log/recording/perf mechanics behind facets one facet per PR where necessary.
daemon/core.
from moved LOC alone.
Done: no concrete platform resource types remain in
SessionState; composition/adapters ownconcrete platform imports; R9 reaches the evidence-backed bound without recreating the Apple edge.
STOP if: a proposed facet has only one implementation, hides guarantee-path evidence, bypasses
provider-first integration, or becomes a universal command gateway.
Workspace foundation dependency
#1490 supersedes the optional-workspace decision. After P1 merges, complete W-pre, W0, and W1a
before P2. This is the minimum foundation the replay chain needs: workspace/toolchain enforcement,
@agent-device/kernel, and value-only shared contracts. Provider leaf extraction (W1b/W1c/W1d)may proceed independently after W1a and does not block P2.
P2/P3/P5 land directly as
packages/maestro,packages/replay-test, andpackages/ad-replay; do not create temporary logical modules undersrc/and move them again.Daemon adapters remain in root composition. Package extraction must not widen a façade, create a
port without two real adapters, or expose implementation files merely to make imports compile.
P7 — Clean internal architecture documentation
Goal: leave durable decisions, not migration history.
CONTEXT.mdonly with durable “why” and accepted invariants.guidance now enforced by code or registries.
prototype:*package scripts after therelevant modules and contract suites have landed.
module-interface-principles.mdonly if it still carries durable guidance not better owned byCONTEXT.md,AGENTS.md, an ADR, or a gate.the archive.
Done: current docs describe the current system; deleted history remains recoverable through git;
no prototype or stale migration entry point remains.
Validation
Every worker runs:
Also run the focused gates selected by the touched contract:
pnpm exec vitest run --project unit-core test/replay-compatandpnpm check:replay-compat;pnpm maestro:conformance;test/integration/nightly/concurrency-torture.test.ts;pnpm test:integration:providerandpnpm test:coverage;pnpm test-app:replay:iosandpnpm test-app:replay:androidon live targets;pnpm check.Use
pnpm format, never a path-local formatter. CI remains authoritative.Success metrics
src/imports: 0; deep engine packages export only.;agent-devicemanifest contains 0 private@agent-device/*runtime dependencies;internal/: 0;SessionStatewriters outside capabilities: 0; R7 rows shrink per migrated cluster;daemon/types.ts: 0 by Maestro/replay-test cutover;load-bearing Apple resource edge is removed;
SessionState: 0;facet;
Initiative STOP conditions
Stop the current worker and report rather than improvising if:
rpcProtocolVersionappears to need a bump;generic event bus;
Handoff protocol
any released compatibility behavior intentionally retained.
References
docs/module-interface-principles.mddocs/adr/README.md, especially ADRs 0001, 0003, 0006–0008, and 0010–0017docs/agents/testing.mddocs/dependency-graph-findings.md