fix(release): make durable runs recovery safe
This commit is contained in:
@@ -63,6 +63,16 @@ these digests detect accidental or manual corruption, not an attacker who can
|
||||
replace the private record and recompute its checksums. Changing a target,
|
||||
channel, or gate input requires a new run.
|
||||
|
||||
Creation requires a caller-generated `request_id`. Its SHA-256 fingerprint is
|
||||
the private, workspace-scoped durable mapping to exactly one run; the raw
|
||||
identifier is never persisted. Repeating the same identifier with the same
|
||||
immutable inputs returns that run without rebuilding the plan, even if the
|
||||
live dashboard has since drifted. Reusing it with different inputs fails
|
||||
closed. The browser keeps an uncertain create identifier in session storage,
|
||||
replays it after reload, and selects the known run returned by the server. A
|
||||
successful create remains shown as saved if only the subsequent list refresh
|
||||
fails.
|
||||
|
||||
Run records survive console restarts, but remain local operator state rather
|
||||
than a signed release artifact or the system audit log. The default location is
|
||||
`$XDG_STATE_HOME/govoplan/release-console/workspace-<sha256>/release-runs/`, or
|
||||
@@ -79,8 +89,11 @@ rewritten nor automatically quarantined.
|
||||
The full resolved-workspace SHA-256 is part of both the private storage
|
||||
namespace and immutable input snapshot. Every list, read, and state transition
|
||||
checks it. An alternate workspace therefore cannot list or resume another
|
||||
workspace's runs even when an embedding/test `run_state_root` override is
|
||||
shared accidentally.
|
||||
workspace's runs. This remains true for an embedding/test `run_state_root`
|
||||
override: the server always appends
|
||||
`workspace-<full-sha256>/release-runs/` rather than treating the override as a
|
||||
shared record directory. A corrupt record from one workspace therefore cannot
|
||||
leak even its identifier or an integrity error into another workspace.
|
||||
|
||||
Each frozen plan step has an explicit `pending`, `running`, `succeeded`,
|
||||
`failed`, or `interrupted` state. Plan order remains a prerequisite: a later
|
||||
@@ -94,6 +107,15 @@ enum event type, timestamp, step identifier, and bounded result code—never
|
||||
commands, process output, confirmation text, credentials, bearer tokens, or
|
||||
signing material.
|
||||
|
||||
Before a step can enter `running`, the store reserves the two command-ledger
|
||||
slots needed for worst-case recovery. An explicit resume consumes one slot and
|
||||
is accepted only while a persisted attempt is actually running. A mutating
|
||||
attempt always retains its final `effect_absent` or `effect_succeeded` slot;
|
||||
`unresolved` may be recorded at most once for that attempt and only when an
|
||||
additional slot is available. Read-only interruption and known failure retain
|
||||
the slot needed to prepare a retry. Capacity exhaustion is therefore detected
|
||||
before starting an effect rather than stranding an ambiguous attempt.
|
||||
|
||||
An explicit resume after a process restart converts every persisted `running`
|
||||
step to `interrupted`; it never guesses whether an external effect happened.
|
||||
An interrupted read-only step can be prepared for retry. An interrupted
|
||||
@@ -105,10 +127,15 @@ effect, and `unresolved` keeps the run blocked. Each outcome emits a bounded,
|
||||
code-only state event. A known failed attempt can likewise be prepared for
|
||||
retry. The UI keeps unavailable controls visible and disabled.
|
||||
|
||||
The browser likewise retains the request identifier for an uncertain
|
||||
resume/retry/reconciliation response and reuses it on the operator's retry.
|
||||
Identifiers are cleared only after the server returns the resulting run state.
|
||||
|
||||
The run API is covered by the same local console token middleware as every
|
||||
other `/api/` route:
|
||||
|
||||
- `POST /api/release-runs` rebuilds and freezes a selective plan.
|
||||
- `POST /api/release-runs` requires `request_id`, then idempotently rebuilds and
|
||||
freezes a selective plan only when that creation is not already known.
|
||||
- `GET /api/release-runs` lists bounded summaries; unreadable entries are
|
||||
reported as unavailable instead of being silently omitted.
|
||||
- `GET /api/release-runs/{run_id}` reads and verifies one exact record.
|
||||
|
||||
Reference in New Issue
Block a user