Skip to content

Split managed state into draft and published slots - #437

Open
david-siqi-liu wants to merge 1 commit into
mainfrom
david/managed-state-slots
Open

Split managed state into draft and published slots#437
david-siqi-liu wants to merge 1 commit into
mainfrom
david/managed-state-slots

Conversation

@david-siqi-liu

@david-siqi-liu david-siqi-liu commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

🥞 Stack (AIGTWY-4342 ucode configure)


What did you change, and why?

~/.ucode/managed-state.json kept one config per workspace, so an admin authoring a config and a
developer's launch-time snapshot wrote to the same key and overwrote each other. This gives every
workspace two named slots: draft (what an admin is authoring, never applied to any agent) and
published (the last snapshot fetched from the workspace, the one that gets applied on launch).

  • New on-disk shape is a versioned map: version: 2 plus a workspaces object, each workspace
    holding a draft and a published slot.
  • v1 files migrate on first read, and the pre-migration bytes are preserved next to the file as
    managed-state.json.pre-v2.bak.
  • save_managed_state and load_managed_state remain as thin aliases over the published slot so
    the existing call sites keep working unchanged. Replace ucode setup with a ucode configure-centered workflow #433 deletes them once every caller has moved.
    Keeping them here holds this PR to one file of real logic instead of a mechanical rename across
    six more.
  • The map is written through a temp file and renamed into place. Before v2 a torn write only cost a
    published snapshot that the next launch refetches; now the same file holds a draft that nothing can
    rebuild.
  • Reviewer focus: _migrated_workspaces and _save_slot in src/ucode/managed_config.py. Everything
    else follows from those two.

Behavior preserved: an empty published config still records "this workspace has no managed config",
which is what stops a server-side removal from being reapplied from stale disk state after a
transient fetch failure.

How do you know it works?

tests/test_managed_config.py covers the v2 round trip, the v1 migration and its backup file, an
unreadable or malformed file, slot isolation (writing one slot leaves the other slot and every other
workspace untouched), and a write that fails part way leaving the previous state intact. Full unit suite green locally at this layer (2179 passed, 37 skipped), and test plus e2e are green on CI.

`~/.ucode/managed-state.json` held one config per workspace, written both by
`refresh_managed_config` (the copy fetched from the workspace on every launch)
and by the authoring wizard (the admin's local, unpublished draft). The two
clobbered each other: a launch wiped an in-progress draft, and a launch could
apply an unpublished draft as if it were published policy.

Store both under a versioned per-workspace map with separate `draft` and
`published` slots. A v1 file migrates on read, with its original bytes kept at
`managed-state.json.pre-v2.bak` on the next write. The map is written through a
sibling temp file and renamed into place, because it now holds the admin's draft:
nothing can refetch that, so a torn write would lose it outright.

`load_managed_state` and `save_managed_state` stay as thin wrappers over the
published slot so existing callers keep working; a follow-up moves them over.

Co-authored-by: Isaac <no-reply@databricks.com>
@david-siqi-liu
david-siqi-liu force-pushed the david/managed-state-slots branch from 84859b6 to e2ca8e2 Compare September 2, 2026 00:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant