diff --git a/src/lib/onboard/lifecycle-contracts.md b/src/lib/onboard/lifecycle-contracts.md index 0c2fb9f922b..3909f6a94ff 100644 --- a/src/lib/onboard/lifecycle-contracts.md +++ b/src/lib/onboard/lifecycle-contracts.md @@ -3,7 +3,10 @@ # Onboarding lifecycle contract map (#6225, epic #6224) -This is a behavior-preserving inventory of onboarding and runtime-mutation journeys. It records current ownership, effect boundaries, checkpoints, secret handling, recovery, and known gaps; it is not a target design. Target refactors belong to #6226, #6227, and #6228. References use stable symbols and modules rather than line numbers. +This is a behavior-preserving inventory of onboarding and runtime-mutation journeys. +It records current ownership, effect boundaries, checkpoints, secret handling, recovery, and known gaps; it is not a target design. +The completed #6226, #6227, and #6228 work established the current create-intent, recovery, and checkpoint contracts. +References use stable symbols and modules rather than line numbers. Related guides: [`README.md`](README.md) describes package placement, [`machine/README.md`](machine/README.md) describes the FSM, and [`../messaging/AGENTS.md`](../messaging/AGENTS.md) describes manifest-first messaging. @@ -49,7 +52,14 @@ Every nonterminal state has one production owner or an explicit internal designa | `finalizing`, `post_verify` | `machine/handlers/finalization.ts` | | `complete`, `failed` | Terminal; no handler | -FSM transitions remain step-granular. For OpenClaw onboarding, the sandbox handler additionally checkpoints each completed secret-free prompt group: sandbox name, web search selection, messaging selection and non-secret configuration, and resource profile. After the sandbox name and web search are checkpointed, a validated web search credential may be registered in OpenShell before messaging. After messaging is checkpointed, validated messaging credentials may be registered before the resource prompt. Each successful registration saves a secret-free provider-name receipt. The machine still cannot resume inside gateway startup, an individual credential upsert, sandbox creation, policy application, or another handler-owned effect group. +FSM transitions remain step-granular. +For OpenClaw onboarding, the sandbox handler additionally checkpoints each completed secret-free prompt group: sandbox name, web search selection, messaging selection and non-secret configuration, and resource profile. +After the sandbox name and web search are checkpointed, a validated web search credential may be registered in OpenShell before messaging. +After messaging is checkpointed, validated messaging credentials may be registered before the resource prompt. +Each successful registration saves a secret-free provider-name receipt. +The versioned checkpoint also records durable sandbox identity and completed web-search provider, messaging provider, sandbox-create, and sandbox-register effect groups. +Resume skips an effect only after its live postcondition is revalidated. +The machine still cannot resume inside gateway startup, an individual credential upsert, sandbox creation, policy application, or another handler-owned effect group. ## Effect-order flows @@ -58,12 +68,13 @@ FSM transitions remain step-granular. For OpenClaw onboarding, the sandbox handl ```text fresh OpenClaw onboard resolve entry options -> save session/machine* -> host preflight - -> gateway/inference-provider effects -> checkpoint name/web prompts* + -> gateway/inference-provider effects -> checkpoint name/identity and web prompts* -> create or update exact web-search provider + save name receipt* -> checkpoint messaging prompts* -> create or update exact messaging providers + save name receipts* -> checkpoint resource prompt* -> resolve complete sandbox-create intent - -> materialize create plan -> create -> ready -> live validation -> register* + -> materialize create plan -> create -> ready -> live validation -> register + -> record create/register receipts* -> finalize session* --fresh entry reset @@ -75,7 +86,7 @@ ordinary live recreate -> remove registry -> materialize plan -> create -> ready -> restore/validate -> register* resume drift - load session* -> reject hint conflicts -> validate replacement credential + load versioned checkpoint* -> reject hint conflicts -> revalidate current bindings -> resolve complete create intent -> optional registry removal -> ordinary path conditional backup* -> ! delete -> remove registry -> materialize plan -> create -> ready -> restore/validate -> register* @@ -101,7 +112,7 @@ runtime mutation |---|---|---|---|---| | **New interactive or non-interactive onboard** — `onboard()` and `resolveOnboardEntryOptions` | Current flags, environment, and prompts. `MessagingWorkflowPlanner.buildPlan`, `prepareSandboxMessagingPreflight`, resource-profile selection, `resolveSandboxCreateIntent`, and `materializeSandboxCreatePlan` assemble policy, provider, package, resource, host-forward, and runtime-setup contributions. Non-interactive mode replaces prompts with defaults or hard aborts. | Consent/session/lock setup and preflight can persist local state, install OpenShell, or clean stale gateway artifacts before the gateway handler. Gateway reuse/recovery/start is the first provider-routing effect; inference-provider upserts follow. For OpenClaw, a validated web-search provider can be created or updated after the name/web checkpoint and before messaging; validated messaging providers can be created or updated after the messaging checkpoint and before resource selection. A name with no live sandbox has no sandbox-destructive boundary; an existing target enters the recreate contract below. | Whole-step session plus machine snapshot. OpenClaw adds narrow checkpoints after each completed secret-free sandbox prompt group; sandbox registry registration is deferred until readiness and live validation. The session stores credential environment names, redacted endpoint metadata, legacy-value digests, and non-secret names of web-search and messaging providers registered for resume; real values remain process- or gateway-bound. | A non-Docker-GPU readiness failure attempts to delete the failed sandbox; the Docker-GPU patch path preserves it and emits patch-specific recovery diagnostics. Temp build-context cleanup is attempted inline with an exit-handler fallback; cancel rollback applies only to a brand-new sandbox. Coverage: `transition-traces.test.ts`, `sandbox-create-intent-boundary.test.ts`, and `sandbox-create-plan.test.ts`. Gap: gateway upserts can outlive a failed/interrupted create. | | **`--fresh` onboard** — `resolveOnboardEntryOptions`, `prepareFreshSession`, `createBaseImageResolutionContext` | Current flags/environment/prompts replace resumable intent. `--fresh` disables auto-resume and forces base-image resolution; it does not prove that the selected sandbox name is unused. | The first destructive effect is local: the prior onboard session is cleared before a new session is saved. A matching live sandbox can later reuse or recreate through the normal sandbox decision; `--fresh` does not itself delete it. | The new session and machine snapshot replace the old resume checkpoint. Credential and effect boundaries then match new onboard or live recreate. | The discarded resume checkpoint is not restored on later failure. Covered by `entry-options.test.ts`, `session-bootstrap.test.ts`, and base-image resolution tests. | -| **Resume, re-onboard, or recreate** — `onboard()`, `prepareOnboardSession`, `decideSandboxResume`, live-sandbox handling in `createSandbox` | For `--resume`, the recorded session is authoritative and conflicting current name/provider/model/image/tool-disclosure hints are rejected. A new re-onboard run takes current flags, environment, and prompts as intent while registry/gateway state provides drift evidence. The machine resolves a complete secret-free create intent, including policy, messaging/provider, GPU, resource, disabled-channel, and agent inputs, before repair/removal or live recreation. | Ordinary live recreation conditionally backs up before provider cleanup, **delete**, image removal, and registry removal. A selected pre-upgrade backup suppresses a new one; an explicit override permits recreation without backup. Resume registry removal and `repair-and-recreate` now follow complete intent validation. Temporary policy/build artifacts remain materialization effects after the delete boundary. | Resume continues the recorded session/machine snapshot; non-resume re-onboard writes a new session first. OpenClaw records completed sandbox name, web search, messaging, and resource choices with explicit progress markers, including explicit `null` choices, while the complete create intent stays process-local and is not persisted or emitted. Raw credential values remain outside the session. A missing process value can be rebound only when the same OpenClaw session recorded successfully registering that provider and its live name, type, and credential-key binding still match; otherwise interactive resume requests it again and non-interactive resume exits with environment-variable guidance. Credentials are checked before mutation and again immediately before materialization. | Restore failures warn and can still publish the replacement; managed-DCode live-selection failure leaves a running, unregistered sandbox with manual-delete guidance. Cancel rollback is not armed and there is no rebuild-style receipt rollback. Coverage: transition traces, sandbox handler tests, create-intent characterization, and the real create boundary. Gaps: #5961/#5783, #6040, early backup asymmetry, and delete-to-register window (#6228). | +| **Resume, re-onboard, or recreate** — `onboard()`, `prepareOnboardSession`, `decideSandboxResume`, live-sandbox handling in `createSandbox` | For `--resume`, the recorded session is authoritative and conflicting current name/provider/model/image/tool-disclosure hints are rejected. A new re-onboard run takes current flags, environment, and prompts as intent while registry/gateway state provides drift evidence. The machine resolves a complete secret-free create intent, including policy, messaging/provider, GPU, resource, disabled-channel, and agent inputs, before repair/removal or live recreation. | Ordinary live recreation conditionally backs up before provider cleanup, **delete**, image removal, and registry removal. A selected pre-upgrade backup suppresses a new one; an explicit override permits recreation without backup. Resume registry removal and `repair-and-recreate` now follow complete intent validation. Temporary policy/build artifacts remain materialization effects after the delete boundary. | Resume continues the recorded session/machine snapshot; non-resume re-onboard writes a new session first. OpenClaw records completed sandbox name, web search, messaging, and resource choices with explicit progress markers, including explicit `null` choices, while the complete create intent stays process-local and is not persisted or emitted. Raw credential values remain outside the session. A missing process value can be rebound only when the same OpenClaw session recorded successfully registering that provider and its live name, type, and credential-key binding still match; otherwise interactive resume requests it again and non-interactive resume exits with environment-variable guidance. Credentials are checked before mutation and again immediately before materialization. | Restore failures warn and can still publish the replacement; managed-DCode live-selection failure leaves a running, unregistered sandbox with manual-delete guidance. Checkpoint replay reuses an exact live sandbox after an interrupted create and backfills missing create/register receipts. Cancel rollback is not armed and there is no rebuild-style receipt rollback. Coverage: transition traces, create-intent characterization, checkpoint replay and resume guards, and sandbox-handler crash recovery. Gaps: early backup asymmetry and no rebuild-style cross-effect rollback. | | **Rebuild or installer-driven upgrade** — `rebuildSandbox` in `rebuild-pipeline.ts`; `upgradeSandboxes` | Registry state is authoritative. A matching session may fill guarded legacy gaps only when its selection agrees; an unrelated/global session is never used. Ambient provider/model selection is quarantined by `isolateAmbientRecreateEnv`, apart from narrowly scoped legacy recovery. Preflight assembles target config, messaging/policy/runtime inputs, recovery inputs, and a retained replacement context. Generic agents use `preflightRebuildImage`; DCode uses its specialized managed-context preflight instead and proves the live route only for normal live rebuild. Resource profile is not part of preflight. | Consent persistence, target-gateway selection/recovery, and target-preflight registry updates can precede disposable image build/probes. Backup is the first durable recovery checkpoint when available. Shields unlock, MCP detach/scrub, and NIM stop are destructive in-place effects before the **sandbox delete** boundary. Prepared context and mutation-edge conditions are rechecked before delete, proving buildability/input identity but not replacement health or atomic swap. | Durable checkpoints are the backup/recovery manifest when one exists and the rewritten recreate session; stale recovery can reach deletion without a manifest, making that session its first new durable checkpoint. Rollback receipts/snapshots are process-local. Credential metadata comes from the target or guarded fallback; raw credentials/providers are checked against current process/gateway state, while prepared installer recovery may reconstruct a missing gateway provider from a validated host credential. | In-process rollback best-effort restores registry/MCP retry metadata, but process death after non-MCP delete can still lose it. The inner onboard selects resource profile after deletion from non-quarantined ambient input. Covered by rebuild, image-preflight, DCode, and messaging tests. Gaps: post-delete resource intent plus health-before-delete/atomic swap (#5801). | | **Channel add/remove/start/stop** — `addSandboxChannel`, `removeSandboxChannel`, `sandboxChannelsSetEnabled` in `policy-channel.ts` | Add compiles and merges a manifest-derived channel delta with `MessagingWorkflowPlanner`. Start, stop, and remove transform the registry plan and rehydrate executable render/build/runtime/forward details from current manifests. | Token-backed add can mutate gateway credentials before policy and plan persistence; QR/in-sandbox-auth add skips that credential upsert. Start persists the enabled plan before policy; stop persists the disabled plan before the rebuild prompt. Remove clears QR-backed durable state when applicable, detaches gateway/bridge state, removes policy, then persists the plan. A queued rebuild has a separate delete boundary. | The compact registry messaging plan is authoritative; render/build/runtime/state/health entries and nested host-forward details are rehydrated rather than persisted. Session policy-preset sync is best-effort, and channel mutations do not rewrite `Session.messagingPlan`. Raw tokens stay in process/gateway bindings. | `rollbackChannelAdd`, re-disable after failed start, and fail-closed QR-state cleanup provide partial compensation. Covered by `policy-channel*.test.ts`, `workflow-planner.test.ts`, and channel integration tests. Gaps: channel add has a separate `--force` conflict policy; add/remove effects can precede plan persistence, and persistence failures are not fully rolled back. | | **Provider, model, or credential-binding change** — `runInferenceSet` | CLI intent plus registry/session metadata. Target resolution and OpenShell preparation occur before locking. The target is re-resolved in the mutating phase under the sandbox lifecycle and timer-bound shields locks; that phase validates provider/model syntax, selected agent, shields state, and local reachability before the first write. | First mutation is the gateway route, then a minimal registry write, API-family/config resolution, registry refresh, best-effort config/hash sync, matching-session update, and audit. An OpenClaw API-family change can then restart the managed gateway after the shields lock is released but while the outer sandbox lock remains held. No sandbox deletion. | Registry and matching session store logical provider/model/credential-environment metadata. Audit records the action, sandbox, and reason rather than credentials; raw values remain gateway-bound. | Forward-only; no rollback. `rebuild` is the repair path for degraded state. Covered by `inference-set*.test.ts`. Gap: several stores can diverge after a mid-sequence failure. | @@ -136,11 +147,11 @@ The registry is separately owned by `src/lib/state/registry.ts`; backup/recovery ## Duplicated decision points -1. **Messaging intent source:** onboard environment plan, rebuild registry plan, resume fallback chain, and channel delta plans. Recommended owner: registry-persisted plan (#6226/#6228). -2. **Ambient environment policy:** onboard treats provider/model variables as intent; rebuild quarantines ambient selection except for narrowly target-scoped legacy recovery. Recommended owner: one target-resolution module (#6226). +1. **Messaging intent source:** onboard environment plan, rebuild registry plan, resume fallback chain, and channel delta plans. Recommended owner: registry-persisted plan. +2. **Ambient environment policy:** onboard treats provider/model variables as intent; rebuild quarantines ambient selection except for narrowly target-scoped legacy recovery. Recommended owner: one target-resolution module. 3. **Conflict policy:** onboard and rebuild share `enforceMessagingChannelConflicts` with different prompt/abort policies; channel add uses the separate hand-built `checkChannelAddConflict` with `--force`. Recommended owner: one declarative conflict policy. -4. **Backup/restore policy:** rebuild and ordinary live recreate back up, while not-ready resume repair deletes before the generic backup; installer restore and channel mutation checkpoint different state again. Recommended owner: one backup/restore policy module (#6228). -5. **Registry lifecycle:** create registers post-ready; rebuild records removals and restores retry metadata through `rebuild-registry-rollback.ts`; some resume decisions remove the row before backup/create preparation and have no receipt-based compensation. Recommended owner: `sandbox-registration.ts` plus durable pre-create identity (#6228). +4. **Backup/restore policy:** rebuild and ordinary live recreate back up, while not-ready resume repair deletes before the generic backup; installer restore and channel mutation checkpoint different state again. Recommended owner: one backup/restore policy module. +5. **Registry lifecycle:** create registers post-ready; rebuild records removals and restores retry metadata through `rebuild-registry-rollback.ts`; some resume decisions remove the row before backup/create preparation and have no receipt-based compensation. Recommended owner: `sandbox-registration.ts` plus the existing durable pre-create identity. 6. **Replacement validation:** generic rebuild retains and fingerprints a successfully built context; normal live DCode rebuild adds route and managed-context proofs; re-onboard still stages the replacement after delete. Health-before-delete and atomic swap remain #5801. 7. **Policy reconciliation:** registration records create-time presets, `handlePoliciesState` later persists the reconciled live set, and channel mutations synchronize their own plan/session preset state. Recommended owner: policy preset persistence/sync modules. @@ -148,28 +159,33 @@ The registry is separately owned by `src/lib/state/registry.ts`; backup/recovery | Issue | Gap | Current status | |---|---|---| -| #5961 | Interrupted onboard lacks durable sandbox identity/effect-group metadata | Durable identity and effect-group receipts now captured in the `checkpoint`; the live sandbox handler consumes `planSandboxCreateReplay`, reuses a surviving exact-identity sandbox, and never recreates under a new name. Remaining apply-boundary interruption coverage belongs to #6228 | -| #6040 | Only selected malformed terminal snapshots are repaired | Open; #6227 | -| #6179 | Stale handler results can reach an invalid transition | Open; #6227 | +| #5961 | Interrupted onboard lacks durable sandbox identity/effect-group metadata | Fixed by #7022. The checkpoint records durable identity and effect-group receipts; replay reuses a surviving exact-identity sandbox or recreates under that same identity. | +| #6040 | Resume aborts after restoring the preflight cache | Fixed by #7022 and verified on macOS and DGX Spark with v0.0.88. | +| #6179 | Stale handler results can reach an invalid transition | Fixed by #6253. Transition validation prevents a terminal `failed` state from re-entering an agent or flow state. | | #5954 | Rebuild conflict was discovered after delete | Fixed by #5955 | -| #6099 | Late dashboard-forward failure could roll back a healthy sandbox | Open; #6116 | +| #6099 | Late dashboard-forward failure could roll back a healthy sandbox | Fixed by #6116 and #6833. | | #6195 | DCode rebuild deleted before replacement validation | Fixed by #6214 | | #6743 | Resume repeated completed sandbox prompts | Fixed by secret-free sandbox prompt checkpoints | -PR #6218 separated secret-free create intent from effectful materialization. PR #6214 added DCode-specific pre-delete replacement validation; generic rebuild now also retains and revalidates a prebuilt context. PR #5955 moved the rebuild messaging conflict check before destruction. The remaining ownership and recovery changes belong to the open child issues and should update this map and its focused traces when behavior intentionally changes. +PR #6218 separated secret-free create intent from effectful materialization. +PR #6742 moved complete create-intent validation before destructive onboarding effects. +PR #6253 made terminal recovery and transition validation explicit. +PR #7022 added versioned checkpoint migration, durable identity, effect-group replay, and fail-closed binding revalidation. +PR #6214 added DCode-specific pre-delete replacement validation; generic rebuild now also retains and revalidates a prebuilt context. +PR #5955 moved the rebuild messaging conflict check before destruction. ## Characterization coverage | Contract | Executable evidence | Uncovered boundary | |---|---|---| -| Fresh, resumed, recreate, successful, and failed machine event order | `machine/transition-traces.test.ts` | Effect-level crash recovery and end-to-end delete-to-register effects | +| Fresh, resumed, recreate, successful, and failed machine event order | `machine/transition-traces.test.ts` | None at this boundary. Sandbox-handler replay tests cover effect-group crash recovery. | | Detailed recreate decisions and repair branches | `machine/handlers/sandbox-resume.test.ts`, `machine/handlers/sandbox.test.ts` | Cross-handler effect transaction | | Legal edges, result kinds, runtime event shapes, runner sequencing | `machine/transitions.test.ts`, `machine/runtime.test.ts`, `machine/runner*.test.ts` | None at the unit boundary | | Create intent/provider ordering and fail-closed credential drift | `sandbox-create-plan.test.ts` | Cross-module pre-delete ordering has no behavioral seam | | Messaging conflict validation before recreate | `sandbox-messaging-preflight.test.ts`, rebuild preflight tests | One shared declarative policy across all callers | -| Resume identity | `test/onboard.test.ts`, `handlers/sandbox-resume.test.ts` | Interrupted live-flow identity (#5961) | -| Session sanitation, sandbox prompt checkpoints, and no-secret persistence | `src/lib/state/onboard-session-sandbox-prompts.test.ts`, `machine/handlers/sandbox-create-intent-boundary.test.ts` | Unset/declined/cleared modeling outside the checkpointed sandbox choices (#6228) | +| Resume identity | `checkpoint-replay.test.ts`, `checkpoint-resume-guard.test.ts`, `machine/handlers/sandbox-checkpoint-crash-recovery.test.ts` | Live process-termination E2E with a real OpenShell sandbox | +| Session sanitation, sandbox prompt checkpoints, and no-secret persistence | `src/lib/state/onboard-session-sandbox-prompts.test.ts`, `src/lib/state/onboard-checkpoint.test.ts`, `machine/handlers/sandbox-create-intent-boundary.test.ts` | Tri-state decisions remain scoped to checkpointed sandbox choices. | | Versioned checkpoint schema, tri-state decisions, migration, and unknown-future fail-safe | `src/lib/state/onboard-checkpoint.test.ts`, `src/lib/state/onboard-checkpoint-migrate.test.ts` | Live decision reads still use legacy fields | -| Resumable create replay, durable identity, and stale-binding fail-closed | `src/lib/onboard/checkpoint-replay.test.ts`, `src/lib/onboard/checkpoint-resume-guard.test.ts` | End-to-end effect recording at every apply boundary in the live handler | +| Resumable create replay, durable identity, and stale-binding fail-closed | `src/lib/onboard/checkpoint-replay.test.ts`, `src/lib/onboard/checkpoint-resume-guard.test.ts`, `machine/handlers/sandbox-checkpoint-crash-recovery.test.ts` | None at the sandbox-handler boundary. | -When a child issue changes one of these contracts, update the map and the narrow owning test in that same PR. Do not add source-text scans or production scaffolding solely to preserve current orchestration order. +When lifecycle behavior changes one of these contracts, update the map and the narrow owning test in that same PR. Do not add source-text scans or production scaffolding solely to preserve current orchestration order.