feat: durable state milestone 1 — version-2 state, lease, and state commands - #42
Merged
Merged
Conversation
…te commands Implements the durable-state foundation from docs/DURABLE-STATE-PLAN.md Milestone 1: - version-2 per-environment state at .labcoat/state/<environment>/state.json: lineage UUID, monotonic serial, chain identity, append-only instance history, and a schema-only operations journal (labcoat-core state.rs) - locked, atomic, fail-closed backend (state_backend.rs): exclusive OS lease on state.lock via fd-lock, compare-and-swap serial commits with temp+fsync+rename, parent-directory fsync, and a state.json.prev backup; corruption is never read as empty state - labcoat state migrate: imports the resolved network's labcoat.lock records as imported instances, backs the ledger up first with a timestamp, refuses existing state, and regenerates labcoat.lock byte-identically as the active-address book; state list and state show [--history] inspect resources and history - environment setting (labcoat.toml, LABCOAT_ENVIRONMENT, global --environment), defaulting to "default" - persistent Labcoat Network instance UUID in the qubitcoin data dir, regenerated by labcoat reset and shown by labcoat status - deploy-time recording: deploys and reserve adoptions append instances under the lease when state exists, and refuse a reset or foreign chain with STATE_CHAIN_MISMATCH before broadcasting; labcoat.lock remains canonical for name resolution Covered by unit and crash-simulation tests, a two-process lease-contention test, and a golden migration fixture (bless with LABCOAT_BLESS=1). The web CLI reference is regenerated and docs/STATE.md documents the shipped scope and its deviations from the design doc. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017KCX1ovEzPP5fsAYQBA2LW
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements the durable-state foundation from
docs/DURABLE-STATE-PLAN.mdMilestone 1. This is the local regtest foundation only — the planner, apply engine, and every later milestone remain proposed.What's here
Version-2 durable state (
labcoat-core/src/state.rs).labcoat/state/<environment>/state.json: lineage UUID, monotonic serial, explicit chain identity, append-only instance history per resource, and a schema-only operations journal for the future apply engine.instance-2and moves the active pointer instead of erasing history.state.lockandbackups/) instead of the flat.labcoat/state/<environment>.json, because that namespace already belongs to the apply call journal (apply.rs), which is untouched.Locked, atomic, fail-closed backend (
labcoat-core/src/state_backend.rs)state.lockviafd-lock(kernel-released on crash,STATE_LOCKEDon contention).backups/state.json.prev.STATE_INVALID, never an empty ledger; unknown schema versions areSTATE_UNSUPPORTED.StateBackendtrait is deferred to the first remote backend (noted in the doc); the lease + serial-CAS semantics it needs are whatStateLease::commitimplements.CLI:
labcoat state migrate/state list/state show <resource> [--history]migrateimports the resolved network'slabcoat.lockrecords asimportedinstances, takes a timestamped backup into the environment'sbackups/first, refuses to run onto existing state, and regenerateslabcoat.lockbyte-identically as the active-address book.labcoat.lockstays canonical for name resolution.environmentsetting (labcoat.tomlkey,LABCOAT_ENVIRONMENT, global--environment), defaultdefault, validated as a path component.Reset detection
labcoat reset(which deletes that directory) regenerates it for free;labcoat statusshows it.STATE_CHAIN_MISMATCHbefore broadcasting. Projects that never runstate migrateare unaffected — the recording is dormant without state.New error codes (
STATE_MISSING,STATE_UNSUPPORTED,STATE_LOCKED,STATE_CHAIN_MISMATCH,STATE_CONFLICT) registered in the docs error table;STATE_INVALIDwording broadened to cover both.labcoat/statestores.Acceptance criteria → tests
state.json.prevtests (state_backend.rs)lease_blocks_a_second_process)from_v1/to_v1verbatim round-trips (incl. theadoptedsentinel and revert records), golden byte-compare, byte-identical lockfile round-trip incl. an untouched foreign-network subtree (tests/state_migrate.rs, bless withLABCOAT_BLESS=1)validate_chainmatrix, instance-UUID regeneration test,deploy_guarddormant/mismatch/lease-release testsValidation
cargo fmt --check,cargo check/test/clippy --workspace --locked -- -D warnings: 193 tests passing.sync-reference.mjs --checkgreen;apps/webreference regenerated (never hand-edited);pnpm --filter @labcoat/web checkandbuildgreen; brand/release-notes/runtime-manifest guards pass.Cargo.lockdiff is exactly the two newlabcoat-coredependency edges (fd-lock4.0.4 andrand0.8.5, both already resolved transitively — no version changes, no new packages).state list→STATE_MISSING;migrate --json(offline: warns and defers chain identity);list/show --history; secondmigrate→STATE_CONFLICT;labcoat.lockbyte-identical to its backup.Manual follow-up (needs a live stack; not run in CI)
The against-live check documented in
docs/STATE.md: in a project with deploys,state migrate→ deploy again and seeinstance-2instate show --history→labcoat reset -y+labcoat up→ confirm the next deploy fails withSTATE_CHAIN_MISMATCHbefore broadcasting.🤖 Generated with Claude Code
https://claude.ai/code/session_017KCX1ovEzPP5fsAYQBA2LW
Generated by Claude Code