Skip to content

feat(promote): record per-component finalize state via scoped writes - #517

Merged
joshua-temple merged 1 commit into
mainfrom
feat/promote-component-state
Jul 8, 2026
Merged

joshua-temple merged 1 commit into
mainfrom
feat/promote-component-state

Conversation

@joshua-temple

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

Copy link
Copy Markdown
Collaborator

Problem

Per-component promotion must record each component's state at state.components.<name>.<env> without clobbering a sibling. The finalizer today rebuilds the whole flat state node from a typed map, so under the CommitWithRetry 409 re-apply a concurrent finalizer would delete state.components.<sibling>.

Fix

  • Finalizer gains a component field (set via WithComponent, threaded from the promote --component flag). When set, writeStateViaAPI and WriteConfig route through WriteScopedState with one per-component StateWrite per promoted env (plus latest on publish), instead of the whole-node rebuild.
  • An empty component takes the exact original path, so single-component finalize is byte-identical.

Scope is the runtime finalize behavior only. Generator promote-<name>.yaml fan-out, the harness per-component execution extension, and the promotion end-to-end scenarios are separate changes.

Verification

go build ./..., go test ./... (2578 pass), go test -race ./... (2578 pass), golangci-lint run ./... clean; single-component byte-identical baseline gate green. Keystone test TestFinalizer_ConcurrentFinalize_SiblingSurvivesThrough409Reapply drives the real writeStateViaAPI -> CommitWithRetry -> stateMutation path: a fake 409s the first PUT and injects a concurrent sibling finalizer's subtree; the re-applied write lands this component's node and preserves the sibling verbatim, including an unmodeled key.

Refs #290.

Comment thread internal/promote/promote.go Fixed
@joshua-temple
joshua-temple force-pushed the feat/promote-component-state branch from 03ef25f to 58d56ef Compare July 8, 2026 05:23
When a component is selected, the promotion finalizer records state at state.components.<name>.<env> through the scoped serializer instead of rebuilding the whole flat state node. This keeps a sibling component's recorded state intact when a concurrent finalizer re-applies on a 409: the finalizer routes writeStateViaAPI and WriteConfig through WriteScopedState with per-component StateWrite entries. An empty component takes the original path, so single-component finalize is byte-identical. Threads --component from the promote command into the finalizer.

Refs #290.

Signed-off-by: Joshua Temple <joshua.temple@stablekernel.com>
@joshua-temple
joshua-temple force-pushed the feat/promote-component-state branch from 58d56ef to 4874d30 Compare July 8, 2026 05:39
@joshua-temple
joshua-temple merged commit 43000ea into main Jul 8, 2026
20 checks passed
@joshua-temple
joshua-temple deleted the feat/promote-component-state branch July 8, 2026 05:53
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.

2 participants