feat(workflow): StateSync recipe completes at release — mark-ready is terminal#485
feat(workflow): StateSync recipe completes at release — mark-ready is terminal#485bdchatham wants to merge 1 commit into
Conversation
… terminal The StateSync recipe ends at mark-ready: Complete means every mutation was performed and the node was released to re-bootstrap. Catch-up is verified node-side (sdk WaitCaughtUp, RPC, alerts) by whoever triggered the workflow. Why: catching_up is a proxy for health the workflow cannot own -- the old final await-condition(catchingUp) held the node's single adoption slot for the full catch-up (hours at real chain height) with no deadline anywhere (the sidecar task polls unbounded, the plan executor has no per-task clock), and the only exit from a wedged wait was the force-delete ceremony, which the docs framed as Failed-workflow recovery only. Completing at release frees the slot in minutes, deletes the unbounded-wait failure mode outright, and separates execution (the workflow's job) from verification (the node's health surfaces). Two properties this buys, both now test-pinned: - Failed <=> held: mark-ready is the terminal step and it is fire-and-forget (success == submitted), so a Failed workflow always means the node is still held. The old shape could fail AFTER release (await failing on a slow-but-healthy sync), which made "Failed holds the node" conditional. - The adoption slot frees at release, so drift work and queued workflows resume promptly. Accepted costs, on the record (peer-reviewed with kubernetes-specialist + systems-engineer, chain-side behavior verified by sei-network-specialist): - WorkflowInProgress clears at release, so height-lag alerts can fire during legitimate catch-up. Traced against the platform alert rules: at most 1-2 auto-resolving warning tickets on protocol-low, no pages. - Drift suppression lifts at release, so drift work (an image roll, a config reconcile) can interrupt catch-up at any point in the window, not only at the tail. Verified against sei-chain: the giga startup safety checks key on SS version markers written only at the END of a snapshot restore, so an interrupted restore re-enters state-sync and converges. One sub-second wedge window exists between the sequential cosmos/evm marker writes at the restore tail; recovery is re-running the recipe (runbook note, negligible probability). - The adoption slot frees at release, so a queued workflow can adopt and re-wipe a node mid-catch-up. Wasteful, never corrupting (the recipe quiesces before it wipes); operators sequence workflows per node. - Config drift cannot undo the migration flags: managed nodes have no config-drift detector, and the only running-node config surface (the update progression's p2p patch) never touches app.toml state-store keys. Test changes: planner progression expectations end at mark-ready with an explicit no-await pin; envtest lifecycle stall point moves to configure-state-sync (mark-ready is fire-and-forget, so GetTask-based stalls cannot hold it); AdoptAndComplete pins the terminal-step shape; workflow_test task counts 6 -> 5. sdk/sei's StateSyncWorkflow doc now states the release semantics (callers verify catch-up via WaitCaughtUp). No CRD schema change (make manifests generate produced no config/manifests diff). The seictl UX/docs companion (success handoff print, 15m default timeout, README) follows in sei-protocol/seictl. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
PR SummaryMedium Risk Overview Docs in the CRD kind comment and Planner, controller, envtest, and integration tests were updated: plans are one step shorter, lifecycle tests assert the last task is Reviewed by Cursor Bugbot for commit 6e88d31. Bugbot is set up for automated code reviews on this repo. Configure here. |
Summary
The StateSync recipe ends at mark-ready. Complete means every mutation was performed and the node was released to re-bootstrap; catch-up is verified node-side (sdk
WaitCaughtUp,seictl node watch --until=caught-up, alerts) by whoever triggered the workflow.Why:
catching_upis a proxy for health the workflow cannot own. The old finalawait-condition(catchingUp)held the node's single adoption slot for the full catch-up (hours at real chain height) with no deadline anywhere, and the only exit from a wedged wait was the force-delete ceremony. Completing at release frees the slot in minutes, deletes the unbounded-wait failure mode outright, and separates execution (the workflow's job) from verification (the node's health surfaces).Two properties this buys, both test-pinned:
Accepted costs, on the record
WorkflowInProgressclears at release, so height-lag alerts can fire during legitimate catch-up. Traced against the platform alert rules: at most 1-2 auto-resolving warning tickets on protocol-low, no pages.A lost mark-ready self-heals from two directions: the sidecar persists the Running row before returning and rehydrates on restart, and the controller repair loop (
sidecarNeedsReapproval, wired in every mode planner) re-issues mark-ready when the post-release health probe sees a 503.Changes
internal/planner/workflow.go: BuildPlan ends at mark-ready; no await-condition step.api/v1alpha1kind doc +sdk/seiStateSyncWorkflow doc state the release semantics.make manifests generateproduced no config/ diff).Review
xreview ledger:
bdchatham-designs/designs/seinode-task/xreview/workflow-complete-at-release.md— unanimous over two rounds (idiomatic-reviewer, systems-engineer as assigned dissenter, kubernetes-specialist), all findings resolved and verified.Companion seictl PR (handoff print, 15m default timeout, README) follows.
🤖 Generated with Claude Code