plans/correct-state-machines.md
Generated by a swarm planning session on 2026-07-28
Status: Proposed
Evidence window: First-parent merges after
1014afff63622100bbf2e8a76fb536afc8cb9c88 through 9d4b4d2 on main
Source plans: plans/claude-correct-state-machines.md and
plans/codex-correct-state-machines.md
The Codex plan is the better plan overall. It has the accurate evidence set, the stronger assurance boundary, the better conformance model, safer compatibility strategy, clearer renderer semantics, and more credible persistence sequencing.
The Claude plan is the better compact mechanics checklist. Its two-phase fence, non-creating producer handle, rendered-revision discipline, canonical fingerprinting, envelope-budget test, typed refusal handling, and concrete failure taxonomy sharpen the implementation.
Neither should be adopted unchanged:
MachineSpec, universal lifetime
substrate, full ActorHost representation rewrite, generated renderer
bindings, and eighteen-PR rollout would create a second framework before its
first primitives are proven.afterCommands, automatic rendered-revision inference, field-name
identity heuristics, and a universal remote manager.The unified direction is therefore:
Keep Codex's evidence, assurance model, compatibility posture, conformance, and pilot discipline. Replace its broad framework with layered contracts and five narrow mechanisms. Import Claude's strongest concrete mechanics. Commit to a bounded MVP with two vertical pilots and a stop/go decision before routing, durability, or chat work.
| Dimension | Claude plan | Codex plan | Unified choice |
|---|---|---|---|
| Evidence | Rich examples, but says “~150” findings and mixes pre-baseline PRs into the stated wave | Exact 21-merge, 169-commit, 256-thread inventory with methodology and links | Codex evidence |
| Diagnosis | Excellent E1–E11 failure taxonomy | Equally strong mapping from failures to missing guarantees | Combine and condense |
| Assurance | Sometimes says an API makes a failure unrepresentable when ordering or inventory is still required | Separates type, runtime, finite-model, and domain-owned guarantees | Codex boundary |
| Authoring | Twelve sibling primitives remain visible to authors | One recipe/facade path, but too much in one MachineSpec | Thin index over layered contracts |
| Host lifecycle | Adds a concrete fence without replacing the host | Proposes a risky lifetime-map rewrite | Add a host gate; preserve internals |
| Operations | Narrow waiters, but not the full safe request boundary | Oversized OperationScope owns many resource kinds | Two-stage request handle plus narrow admitted OperationRegistry |
| Renderer | Better receipts but callers still assemble bootstrap, admission, completion, and presentation | Generated handle/hook has the right UX but is premature | Shared handwritten typed helper; generate later if pilots converge |
| Remote lifecycle | One broad createRemoteManager | Generated subscription/action facade | Extend the client with a subscription lease; keep domain managers |
| Testing | Useful named conformance cases and envelope budgets | Tiered conformance, historical mutants, leak accounting, explicit bounds | Codex tiers plus Claude's concrete helpers |
| Persistence | Generalizes claim/CAS/checkpoints too early | Fixes the known bug now and pilots executable durability later | Codex sequencing |
| Rollout | Eight nominal PRs, several containing multiple risky domain migrations | Eighteen narrow PRs, but too long-lived a partial migration | Eight logical MVP stages, split into narrow PRs when needed |
| Success | “Materially fewer iterations” is not measurable | Better metrics, but pilot refactors are not comparable to authority migrations | Pilot safety/glue gates plus normalized future churn |
assertEnvelopeBudget using the actual codec and structured-clone
measurement.afterCommands: rejected. The current scheduler promise means handoff,
not completion of async commands, emitted terminal events, or compensation.
Route cleanup must follow authoritative operation settlement.createRemoteManager: deferred. Common subscription mechanics belong in
RemoteMachineClient; domain managers keep their product-specific surfaces.appId/chatId heuristics
are brittle. Use entity-relative intents or an explicit key/intent
relationship contract.MachineSpec becomes a thin index over layered production contracts and a
separate test-only conformance sidecar.LifetimeLease becomes three concrete MVP mechanisms:
KeyedAdmissionGate, RemoteSubscriptionLease, and
PendingReceiptLedger.OperationScope becomes a two-stage request protocol: a pre-admission request
handle plus a narrow admitted OperationRegistry that composes existing
TaskScope, TimerLeaseScope, and later route/subscription leases.ActorHost lifetime-slot rewrite is deferred. The MVP integrates one
final admission gate around the reviewed host implementation.The pure transition layer held up. The churn occurred in implicit protocols
around it: admission across await, construction/disposal, request settlement,
subscription ownership, deletion fences, retry/deduplication, late producer
output, presentation routing, and persistence.
The MVP will make framework-owned admission, lifetime, receipt, and in-process settlement invariants unavailable through the normal API for two migrated pilot surfaces. It will use deterministic adversarial tests for properties TypeScript cannot prove, while preserving current IPC envelopes, renderer façades, and product behavior.
The MVP does not claim crash-safe arbitrary side effects or prove domain policy. It ends with a stop/go decision. Presentation backfill, generic durability, and chat/queue work proceed only if the pilots reduce glue, kill the historical failure shapes, preserve behavior, and remain understandable to authors.
The authoritative sample is the 21 first-parent merges after the requested baseline:
git log --first-parent \
1014afff63622100bbf2e8a76fb536afc8cb9c88..9d4b4d2
GitHub review-thread counts are a 2026-07-28 snapshot. Top-level review threads are counted once; replies are analyzed separately. Review can continue after merge, as demonstrated by the newest version-preview recovery finding.
| Evidence | Count |
|---|---|
| First-parent merges | 21 |
| Commits | 169 |
| Top-level review findings | 256 |
| HIGH/P1 findings | 70 |
| MEDIUM/P2 findings | 185 |
| State-machine-program findings after excluding unrelated #4124 | 252 |
| Findings in the five main migrations | 192 |
| Commits in the five main migrations | 95 |
| Foundation findings in #4097–#4106 | 46 |
New rules/state-machines.md bullets since baseline | 47 |
The five main migrations—#4108 app run, #4116 GitHub operations, #4121 image generation, #4119 chat/plan, and #4123 version preview—account for 75% of all findings.
| PR | Commits | Findings | Dominant churn |
|---|---|---|---|
| #4100 | 14 | 14 | construction, reentry, disposal, retention |
| #4105 | 9 | 10 | authorization, dedupe, subscriptions |
| #4108 | 14 | 31 | request/runtime identity, waiters, deletion |
| #4116 | 5 | 11 | claims, routing, reset |
| #4121 | 10 | 19 | effects, UI admission, deletion, retention |
| #4119 | 50 | 96 | queue ownership, settlement, finalization |
| #4123 | 16 | 35 | persistence, Git recovery, window interest |
The Claude plan's broader chronology is useful background, but PRs such as #4086, #4090, #4092, #4093, and #4095 are not in the requested post-baseline first-parent set. Its “~150” figure should not be used as the program baseline.
The foundation's 46 findings break down as:
| Primary cause | Findings |
|---|---|
| Actor/subscription lifetime, admission, and disposal barriers | 17 |
| Identity, correlation, authorization, and deduplication | 9 |
| Retention, backpressure, and resource ownership | 8 |
| Cross-window projection and delivery compatibility | 5 |
| Error isolation and classification | 4 |
| Abort and terminal-settlement semantics | 3 |
Only six of thirteen inventoried machine directories currently use shared
matrix/reachability drivers. Only three of six distributed definitions have
reachability coverage, and no domain machine uses runCosim.
The conclusion is not that review was noisy. At least 123 of 139 replies to findings explicitly said “Fixed” or “Addressed,” while only five were clearly rebutted or intentional by conservative phrase matching. Review was discovering real protocol defects after implementation.
Correctness-critical state is still encoded in maps, promises, callbacks, authorizers, renderer hooks, and destructive handlers surrounding otherwise explicit reducers.
The same implicit protocols are repeatedly reimplemented:
The documented invariant is not enough if an author can bypass it through a normal API. Review then becomes the mechanism that discovers missing ordering, identity, and cleanup edges.
The goal is:
Make framework-owned protocol invariants structural for migrated surfaces, require domain policy to be explicit, and turn review-discovered interleavings into reusable deterministic conformance.
“Correct by construction” has four distinct levels.
For migrated surfaces:
RemoteIntent is structurally distinct from trusted internal
Event.For migrated surfaces:
await.These guarantees end at the declared process, host, actor, or window-session lifetime. An in-memory ledger does not provide exactly-once behavior across process crashes.
The framework does not generically prove:
Domains must declare and test these policies. The framework must not infer them.
This framework work follows the repository product principles:
PendingReceiptLedger, adopted by remote transport and MCP OAuth.RemoteIntentContract, trusted-event conversion, typed authorization,
transport-private PreparedSubscribe/PreparedDispatch, and
RemoteSubscriptionLease.KeyedAdmissionGate with two-phase fence semantics and
non-creating producer sinks.PreparedRequest, narrow
admitted OperationRegistry, safe actor.request() facade, and an optional
post-commit transition outcome channel.defineEffectHandlers.useMachineMutation behind existing renderer façades.In order:
OperationRouteRegistry and version-preview window-interest
ownership.ActorHost lifetime-map rewrite.Do not ship one deeply generic object that owns transitions, wire protocol, lifecycle, effects, outcomes, persistence, test fixtures, and renderer code.
Use composable layers:
RemoteIntentContract<Key, Intent, Event, Snapshot>EffectContract<Command, Event>;OutcomeContract<RequestId, Outcome>; andMachineConformance.A thin defineMachine(...)/manifest may index these layers for inventory and
review output. It does not own runtime behavior and does not put fixtures into
production bundles.
Conceptual shape:
const remote = defineRemoteIntentContract({
keyCodec,
intentCodec,
snapshotCodec,
intents: {
START: {
completion: "tracked",
revision: "required",
retry: "none",
acceptance: "admission",
inputDisposition: "preserve-until-accepted",
},
},
authorizeSubscribe,
authorizeDispatch,
toInternalEvent,
budgets,
});
export const definition = defineDistributedMachine({
...existingDefinition,
remote,
lifecycle,
outcomes,
effectHandlers,
});
export const conformance = defineMachineConformance({
definition,
states,
intents,
invariants,
faultScenarios,
exclusions,
});
The MVP contract must declare only facts that generate enforcement or tests. Add new declarations after pilot evidence, not in anticipation of every future machine.
Split the renderer's wire type from the actor's internal event:
interface RemoteIntentContract<Key, Intent, Event, Snapshot> {
keyCodec: z.ZodType<Key>;
intentCodec: z.ZodType<Intent>;
snapshotCodec: z.ZodType<Snapshot>;
authorizeSubscribe(
context: SubscribeAuthorizationContext<Key>,
): AuthorizationDecision | Promise<AuthorizationDecision>;
authorizeDispatch(
context: DispatchAuthorizationContext<Key, Intent>,
): AuthorizationDecision | Promise<AuthorizationDecision>;
toInternalEvent(context: AdmittedIntentContext<Key, Intent>): Event;
policy(intent: Intent): IntentPolicy;
budgets: EnvelopeBudgets;
}
Requirements:
toInternalEvent creates a new immutable trusted event. It never mutates the
decoded renderer object.{ kind: "deny", error: DyadError }.messageId remain compatible.RemoteMachineClient cannot infer what React rendered. A renderer hook returns
an opaque token beside the selected view:
const { view, observedRevision } = useDistributedMachine(...);
await mutate(intent, {
expected: observedRevision,
});
The intent contract declares whether actor revision, a domain revision such as queue revision, or no revision is required. There is:
allowStaleWrite; andAn intent may explicitly implement resync/recheck/retry with one stable prepared identity when its domain policy says that is safe.
Add:
canonicalDispatchFingerprint(contract, address, intent), computed in main
over the protocol version, machine identity, canonical encoded wire address,
codec-parsed immutable intent, expected revision token, RequestId, and
declared idempotency identity;assertEnvelopeBudget(contract, worstCaseFactory) using the actual codec and
structured-clone-compatible measurement.The dispatch messageId is the ledger key and the window session is the ledger
scope; neither is duplicated inside the fingerprint. Mutable facts such as the
current actor instance are admission bindings, not retry payload identity.
Volatile main-injected provenance is also excluded.
Apply the untrusted envelope byte bound before codec traversal or hashing. Hash large payloads only once per prepared delivery and store only the digest in ledgers.
PendingReceiptLedgerExtract the data structure independently implemented in remote transport and MCP OAuth:
ledger.claim({
scope: windowSessionId,
messageId,
fingerprint,
start: () => admit(),
});
Outcomes:
Guarantees:
This ledger owns delivery idempotency within its declared session/host lifetime. It does not own domain request completion and is not durable across restart.
Subscription creation and event dispatch have different prerequisites and must not share one universal preparation capability.
Both PreparedSubscribe and PreparedDispatch are framework-private,
single-use values.
A new subscription has no actor instance/revision yet:
PreparedSubscribe at final synchronous subscription admission.Rejected untrusted IDs never enter a process-lifetime interner.
Remote dispatch addresses an existing actor established by an admitted subscription:
PreparedDispatch at final synchronous ActorHost admission.If any captured fact changed, admission returns a typed refusal. It never silently subscribes or dispatches against a successor lifetime.
RemoteSubscriptionLeaseExtend RemoteMachineClient with:
interface RemoteSubscriptionLease {
readonly ready: Promise<void>;
refresh(): Promise<void>;
release(): void;
}
Guarantees:
refresh() never acquires ownership;Keep app-run, chat, and plan managers. They compose the lease while preserving their domain APIs.
KeyedAdmissionGateIntegrate one gate at the final ActorHost ingress without replacing its
reviewed lifetime maps.
interface KeyedAdmissionGate<Key, Event> {
beginFence(options: {
key: Key;
allowDuringDrain(event: Event): boolean;
}): FenceHandle<Key>;
assertCreateAllowed(key: Key): void;
assertDispatchAllowed(key: Key, event: Event): void;
track<Result>(key: Key, start: () => Promise<Result>): Promise<Result>;
}
Fence lifecycle:
await; admits only
declared cleanup/cancellation events.The fence remains held through the domain's database/filesystem destructive commit. The host cannot infer whether that operation succeeded.
The gate applies to:
Command runners and external producers receive a captured non-creating sink
bound to the expected actor instance. They cannot call a creating ensure().
The fence drain includes the full admitted command continuation and terminal
events it synchronously creates.
A remote request spans a pre-admission lifetime in the client/transport and an authoritative admitted lifetime in main. No actor-owned registry can exist before authorization without either touching or creating unauthorized actor state.
PreparedRequestactor.request(intent) synchronously creates a client request handle before it
starts IPC:
interface PreparedRequest<Admission, Outcome> {
readonly requestId: RequestId;
readonly admission: Promise<Admission>;
readonly settled: Promise<Outcome | NotAdmitted>;
readonly retry:
| { kind: "disabled" }
| { kind: "enabled"; dispatch(): Promise<Admission> };
}
It owns:
RequestId, transport message ID, and declared idempotency identity;It is not authoritative domain state. Refusal creates no actor operation. Renderer loss cannot prevent main-owned work that was already admitted from settling.
OperationRegistryAt final synchronous actor admission, main atomically creates or reattaches the authoritative operation entry before enqueuing the trusted event:
class OperationRegistry<RequestId, Outcome> {
admit(id: RequestId, actor: ActorInstanceId): OperationTicket<Outcome>;
settle(id: RequestId, outcome: Outcome): boolean;
dispose(outcome: Outcome): void;
}
It owns only:
RequestId;It composes existing TaskScope and TimerLeaseScope; it does not absorb
tasks, timers, routes, subscriptions, persistence, or presentation.
The ordinary facade remains:
const operation = actor.request(intent);
const admission = await operation.admission;
const outcome = await operation.settled;
For local actors, client preparation and admitted registration can occur in one synchronous call. For remote actors, transport admission bridges the two stages. A duplicate stable request reattaches to or replays the same admitted operation; a refused request never appears in the main registry.
actor.enqueue() remains explicitly admission-only and is unavailable to new
domain completion-aware code.
Request identity is distinct from reusable runtime InvocationRef. A domain
may deliberately relate them through a named mapping, not a cast or shared
generic string.
Prototype a backward-compatible optional outcome field on changed transition results:
change(nextState, commands, {
outcomes: [complete(requestId, outcome)],
});
ActorHost publishes outcomes after snapshot commit. The default generic is
never, so existing machines remain source-compatible.
Do not settle operations from arbitrary observers, scheduler handoff, or a new
afterCommands phase. Effects emit correlated terminal events; the pure
transition decides whether those events settle a request.
Add a small exhaustive map for one-shot commands:
defineEffectHandlers<Command, Event>()({
StartRuntime: correlatedEffect({
run,
succeeded,
failed,
cancelled,
}),
PublishStatus: isolatedEffect({
run,
onError,
}),
});
MVP guarantees:
Concurrency, supersession, scheduling, and point-of-no-return remain domain policy. Streaming and checkpointed effect recipes are follow-ups.
Ship one shared handwritten typed helper behind current domain hooks:
useMachineMutation({
prepare,
request,
observedRevision,
acceptance,
inputDisposition,
concurrency,
retry,
});
Expose orthogonal facts:
connection:
| "connecting"
| "ready"
| "disconnected"
| "incompatible";
snapshot:
| { kind: "unavailable" }
| { kind: "available"; freshness: "live" | "cached" };
admission:
| { kind: "idle" }
| { kind: "dispatching" }
| { kind: "accepted"; disposition: "fresh" | "replayed" }
| { kind: "refused"; reason: AdmissionRefusal };
execution:
| { kind: "untracked" }
| { kind: "pending" | "running" }
| {
kind: "settled";
outcome:
| "succeeded"
| "failed"
| "cancelled"
| "superseded"
| "disposed";
};
A derived convenience status is allowed, but the framework does not collapse
these facts into one linear lifecycle. The connection facet preserves the
existing RemoteConnectionStatus; actor snapshot/capability availability is a
separate fact.
The helper:
disposed is presentation-neutral by default. A domain may opt into error copy
only when disposal is exceptional for that operation.
Current runApp, stopApp, image-generation, and other renderer façades remain
the public product APIs during the pilots. Generate bindings only if both pilots
converge on this shape.
Keep three channels separate:
| Channel | Examples | Semantics |
|---|---|---|
| Transport/admission result | applied, ignored, replayed, refused, conflict | Typed data; not domain completion |
| Infrastructure failure | dependency failure, codec bug, programmer throw | Promise rejection and telemetry-visible |
| Domain outcome | succeeded, failed, cancelled, superseded, disposed | Typed terminal result correlated to RequestId |
Expected denial remains a DyadError with its original DyadErrorKind
internally. Do not convert broad exceptions to Auth. Expected disposal is a
domain outcome; legacy IPC adapters map it to an existing appropriate error
classification.
No renderer best-effort delivery may be the authoritative settlement path.
Do not implement 256 bespoke regression tests. Map duplicate findings to one reusable failure shape. Every one of the 46 foundation findings must map to:
Minimum MVP scenarios:
await;| Tier | Applies to | MVP checks |
|---|---|---|
| T0 pure | Finite machine definitions | variant inventories, totality, reachability, reference stability, invariants, explicit exclusions |
| T1 hosted/effectful | Hosted actors/controllers | construction/disposal/reentry, outcome settlement, effect terminals, non-creating late output, zero resources |
| T2 distributed | Remote definitions | intent/event codecs, key relationship, authorization awaits, duplicate/retry, subscriptions, two windows, budgets |
| T3 persistent | Later persistent contracts | checkpoint failure, crash boundaries, hydration barrier, reconciliation |
| T4 composition | Declared multi-machine protocols | bounded cosim and ownership transfer |
Use existing fake clocks, controlled promises, FakeDuplexRemoteTransport,
host/controller conformance, reachability exploration, two-window harness, and
runCosim.
A bound hit is not a pass. CI must report exhaustive versus boundReached,
and required scenarios must split their alphabets until exhaustive.
Do not block the primitives on a general async trace-minimization platform. Reuse the shortest schedule available from existing exploration and emit:
The internal record should be JSON-safe, but a general JSON shrinker and GUI are follow-ups. Definition-owned redaction must exclude prompts, credentials, provider payloads, filesystem contents, and other sensitive data.
Tracing/reporting failure is isolated from domain settlement.
The framework is incomplete unless failure states have defined product behavior:
connection: connecting and
snapshot: unavailable, never fabricated idle. Disconnected and incompatible
remain distinct transport states.disposed; it does not automatically
synthesize a generic failure toast.The MVP is eight logical stages after the immediate safety prerequisite. Split a stage into smaller PRs if its review surface is large. Never compensate by combining a new primitive with multiple domain migrations.
Acceptance:
RemoteIntentContract and test-only MachineConformance.assertEnvelopeBudget.Acceptance:
PendingReceiptLedgerAcceptance:
RemoteIntent from trusted Event behind a compatibility adapter.PreparedSubscribe and PreparedDispatch.RemoteSubscriptionLease to RemoteMachineClient.Acceptance:
KeyedAdmissionGateActorHost.Acceptance:
PreparedRequest.OperationRegistry.actor.request() synchronously create the client handle and bridge
it to atomic final main registration.useMachineMutation with orthogonal facets.Acceptance:
OperationRegistry.runApp/stopApp promise façades.Acceptance:
Acceptance:
ensure(), and
independent subscription ref counting on migrated surfaces.Acceptance:
Proceed beyond the MVP only if all are true:
Review findings discovered and fixed during the pilot are evidence, not automatic failure. “Zero initial HIGH/P1 comments” is an aspiration, not the release gate.
npm run ts performance regression; record before/after medians.actor.request() and
useMachineMutation, not raw lifecycle pieces.Behavior-preserving pilot refactors cannot prove reduced future review churn. Measure the next two comparable new-machine or lifecycle/protocol migration PRs:
Target at least a 50% reduction against comparable baseline work. If two qualifying PRs do not occur, mark churn reduction unproven rather than claiming success.
OperationRouteRegistry only after the pilot operation
lifetime is stable.RemoteSubscriptionLease adoption.Do not add afterCommands; route cleanup follows terminal operation publication
and settlement.
The guarantee is checkpoint ordering plus explicit recovery—not exactly-once external effects or generic compensation.
Scheduling and replacement remain domain policy.
messageId.DyadErrorKind; do not hide dependency/programming failures as
denial.npm run ts and contract-suite runtime before and after each generic
type/test addition.| Risk | Mitigation |
|---|---|
| This creates a second framework | Use layered contracts and five narrow mechanisms; stop after two pilots |
| Host changes reopen #4100-class bugs | Preserve host representation; land adversarial conformance before the gate |
| The operation abstraction becomes a mega-scope | Registry owns outcomes only and composes existing scopes |
| Authors still assemble temporal pieces manually | Ordinary path is actor.request() plus domain façade; internal pieces stay framework-private |
Types encourage casts or slow tsgo | Use flat unions, few initial brands, and measured type-check baselines |
| Retries duplicate destructive work | Default no retry; require stable prepared identity and receiver dedupe |
| In-memory exactly-once is mistaken for durability | Put the host/session boundary in APIs and reports |
| Renderer semantics regress | Keep façades, require observed revision and explicit acceptance/input policy, run golden UI tests |
| State-space exploration gives false confidence | Report bounds explicitly and split required alphabets until exhaustive |
| Diagnostics expose sensitive data | Definition-owned redaction and bounded capture |
| Review metrics are gamed or incomparable | Normalize by production LOC and measure two future comparable PRs |
| Conditional work becomes assumed scope | Stop/go decision explicitly funds each follow-up |
src/state_machines/types.ts — optional post-commit outcome channelsrc/state_machines/testing.ts — tier registration and shared scenariossrc/state_machines/trace.ts — causal/resource diagnosticssrc/state_machines/operation_registry.tssrc/state_machines/pending_receipt_ledger.tssrc/distributed_machines/definition.ts — layered remote intent contractsrc/distributed_machines/actor_host.ts — final keyed admission gatesrc/distributed_machines/remote_transport.ts — ledger and prepared
subscribe/dispatchsrc/distributed_machines/remote_client.ts — subscription leasesrc/distributed_machines/remote_protocol.ts — intent/event-compatible wire
boundarysrc/distributed_machines/react.ts — observed revision and mutation helpersrc/distributed_machines/boundaries.test.ts — progressive unsafe-use
inventorysrc/distributed_machines/testing/ — tiered host/transport conformancesrc/app_run/definition.tssrc/app_run/remote_manager.tssrc/ipc/services/image_generation_definition.tsThe exact file list should be revalidated at each implementation PR because upstream migration work may continue.
| Decision | Rationale |
|---|---|
| Codex is the base plan | Its evidence, assurance model, testing, compatibility, and sequencing are stronger |
| Claude's concrete fence/hash/budget/revision details are retained | They directly encode repeated accepted findings |
| No XState or statechart replacement | Pure transitions were not the churn source |
Layered contracts, not monolithic MachineSpec | Lower type and abstraction risk; production and test concerns stay separate |
PendingReceiptLedger is core MVP | Transport and OAuth independently proved the same reusable invariant |
| Prepared subscribe/dispatch values stay private | Each authorization/final-admission flow must be one framework transaction |
Keep current ActorHost internals | A representation rewrite would reopen heavily reviewed lifecycle behavior |
Two-stage request plus narrow OperationRegistry | Pre-admission and admitted lifetimes differ; tasks/timers/routes remain separate |
No afterCommands phase | Scheduler handoff is not effect or operation completion |
| Explicit observed-revision token | The client cannot infer what React rendered |
| Keep domain managers | Only subscription lifetime has demonstrated commonality |
| Handwritten shared mutation helper first | Two pilots must validate the renderer contract before generation |
| App run is the first pilot | It directly tests request/runtime identity, waiters, producer output, and disposal |
| Image generation is the second pilot | It tests collections, effects, form preservation, retention, and parallelism |
| #4123 is fixed immediately; durability is later | Known product safety should not wait for a speculative framework |
| Chat/queue is last | It has the largest domain-specific state space |
| Pilot safety does not prove reduced future churn | Normalize the next two comparable migrations before claiming success |
At MVP completion, the project may claim:
For app run and image generation, framework-owned remote admission, keyed lifetime, subscription, receipt, and in-process request-settlement invariants are enforced through the normal API and covered by deterministic adversarial tests without changing wire, IPC, or renderer behavior.
The project may claim that this program reduced review churn only after two comparable future migrations demonstrate the normalized reduction target.
Generated by dyad:swarm-to-plan.