Skip to content

feat(hotfix): record per-component finalize state and scope hotfix tags - #523

Merged
joshua-temple merged 1 commit into
mainfrom
feat/hotfix-finalize-scoped
Jul 8, 2026
Merged

joshua-temple merged 1 commit into
mainfrom
feat/hotfix-finalize-scoped

Conversation

@joshua-temple

@joshua-temple joshua-temple commented Jul 8, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

The hotfix finalizer records state via a whole-flat-node rewrite, so on a multi-component manifest a concurrent hotfix on one component would drop a sibling's state.components.* under the CommitWithRetry 409 re-apply. Hotfix tags are also not component-namespaced.

Fix

  • The hotfix finalizer gains a component (threaded from --component). When set, it overlays the component's persisted rows, re-applies the hotfix, and node-patches only state.components.<name>.<env> via WriteScopedState, preserving sibling subtrees. Empty component keeps the flat WriteManifestState path, byte-identical.
  • Hotfix tags resolve through the component's tag grammar (strict prefix), so a hotfix tag is created and looked up in the component's namespace and never cross-matches a sibling.
  • The finalizer records env/<component>/<env> for a component, env/<env> otherwise.

Scope is finalize state, tag scoping, and the env-branch name inside finalize. The promote rejoin cleanup and the generator emitting --component on the hotfix step are separate changes; until the generator wiring lands, the component path is inert.

Verification

go build ./..., go test ./... (2624 pass), go test -race on hotfix+config (677 pass), golangci-lint run ./... clean; single-component byte-identical baseline gate green. The load-bearing concurrency test TestHotfixFinalize_ConcurrentFinalize_SiblingSurvivesThrough409Reapply drives the real hotfixMutation -> CommitWithRetry path: a 409-once fake injects a concurrent sibling's subtree; this component's node lands while the sibling survives verbatim including an unmodeled key. A byte-identical single-component test proves the empty-component mutation equals the direct WriteManifestState reference.

Refs #293.

A hotfix on a selected component now records its state at state.components.<name>.<env> through the scoped serializer instead of rewriting the whole flat state node, so a concurrent hotfix on another component keeps its recorded state intact across the CommitWithRetry re-apply on a 409. The finalizer overlays the component's persisted rows, re-applies the hotfix, and node-patches only its own subtree; an empty component keeps the flat path, byte-identical. Hotfix tags resolve through the component's tag grammar (strict prefix), so a hotfix tag is created and looked up in the component's namespace and never cross-matches a sibling. The finalizer records env/<component>/<env> for a component and env/<env> otherwise.

Refs #293.

Signed-off-by: Joshua Temple <joshua.temple@stablekernel.com>
@joshua-temple
joshua-temple merged commit ae794a9 into main Jul 8, 2026
26 of 28 checks passed
@joshua-temple
joshua-temple deleted the feat/hotfix-finalize-scoped branch July 8, 2026 09:30
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