Skip to main content
← Back to list
01Issue
FeatureIn ProgressSwamp CLIPublic
Assigneesstack72

Relationships

#4097 Datastore rework Phase 5 (team mode): config edits name the version they replace, so a stale edit is refused instead of overwriting another member's

Opened by stack72 · 10/28/2026

Background

Datastore rework Phase 5 (team mode). With managedConfig, a repository's configuration lives in the datastore: behavior and workflow definitions, vault configs and the extension lockfile. On v3 each definition is already a config partition of its own (config/models/<type>/<name>.yaml, and so on).

Two people editing one definition still means last writer wins:

  • put ops can carry expect: "absent" | {sha256} (src/domain/datastore_v3/op.ts), and tombstones expect: "present", but no edit flow sends them;
  • swamp-uat#490 (closed) recorded today's outcome as a baseline: A and B load workflow deploy at the same revision, A pushes an edit, then B pushes an edit to a different field. B's write replaces A's, and nobody is told. The design calls that "the baseline for the future compare-and-swap rejection".

Design: design/enablers/datastore-commit-log.md, the Definition row ("optimistic concurrency, expect: sha") and the edit rule: "put with expect: {sha256} of the version the user started from. A stale edit fails with who changed it and when."

Content-level authorisation of forwarded config writes shipped in #3922.

What to build

  1. Every config edit names what it replaces. A create puts with expect: "absent". An edit puts with expect: {sha256} of the stored version the edit started from, recorded when the definition was loaded, not when it is saved. A delete is a tombstone with expect: "present". This covers every config writer:
    • behavior create, edit and delete (and their model spellings), and workflow create, edit and delete;
    • vault create, edit and delete, where they write config;
    • the lockfile writers (extension pull, update, rm);
    • the --server handlers that do the same edits on serve.
  2. A stale edit is refused, not merged. The commit log refuses a put whose expect doesn't match. The user gets a stable code (for example config_edit_conflict) that names the definition, says who changed it and when (the commit's principal and time, which serve stamps), and says how to proceed: reload, re-apply the change, and save again. Nothing is written.
  3. Through serve and solo alike. The same rule holds when a serve-mode checkout forwards the edit (#3985), when serve's own handlers edit, and in a solo checkout. In team mode serve decides the expectation inside its commit transaction, so two members racing get exactly one success.
  4. Lockfile edits compare on the lockfile's sha256. A conflict there means another checkout changed extensions meanwhile, and the message says to pull and retry.

Decisions for the plan (recommendations included)

  • Where the expected sha comes from. Recommend: the content hash the read model already records for the record when it was loaded, carried through the edit flow (the editor session, or --from-file with the hash of what was read). Don't re-read at save time, which would hide the race.
  • Team mode without managedConfig. #3985 documents that behavior definitions are then repository files: serve judges a forwarded run against its own checkout's files, and a behavior defined only on a laptop is refused. Recommend: keep that as the documented limit for now, and say in the team-mode docs that teams run with managedConfig. Don't widen this issue to move non-managed definitions through serve.

Out of scope

  • swamp-club#2612 (pulled extension archives stored in the config tier, with every checkout converging to the shared lockfile) is its own issue, linked here. It may follow this one.
  • Merging non-conflicting field edits. A stale edit is refused whole; a merge is a later decision.

Tests

Red-check each against main:

  • The uat#490 scenario in-process: two checkouts load deploy, A saves, B's save is refused with config_edit_conflict naming A and the time, and A's edit stands.
  • Create against an existing name, and delete of a changed or already-deleted definition, are each refused.
  • Through serve: two members race one edit and exactly one succeeds; a serve handler's edit and a forwarded edit race the same way.
  • A lockfile conflict between two extension pulls is refused with the pull-and-retry message.
  • Solo v3 and v2 managedConfig keep their behaviour where this issue doesn't change it. Say in the PR what v2 does.

Where

src/domain/datastore_v3/op.ts (the expectations), the definition, workflow, vault and lockfile repositories and their edit flows in src/libswamp/ and src/cli/commands/, serve's handlers, and the design docs.

After this ships, swamp-uat#490's concurrent-edit assertion is updated deliberately to the rejection. That's a separate swamp-uat issue.

02Bog Flow
✓OPEN✓TRIAGED◉IN PROGRESS○SHIPPED+ 1 MOREASSIGNED+ 5 MOREREVIEW+ 1 MOREIMPLEMENTATION

In Progress

10/28/2026, 11:55:56 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
stack72 assigned stack7210/28/2026, 11:16:24 PM

Sign in to post a ripple.