Repository navigation
Add a converge action: re-apply the merged manifest when only its shape changed - #2
Merged
Merged
Conversation
A manifest edit that changes env, volumes, or the template without moving
the image digest had no path to the host: Deploy refuses the pinned digest
(ErrAlreadyAtDigest), so the rendered unit kept the old environment until
the next digest change — observed live 2026-08-27 during the snowsys
Phase 12 budget red test.
Converge is a third engine action that applies the manifest as main holds
it at the front of the service's queue: sync, re-render, write the unit,
restart, probe. It opens no pull request (the change it applies already
merged through review) and never enters the merge queue. It journals a
run like any other, joins duplicate converge clicks, and deliberately
never joins a pending deploy at the same digest, whose frozen manifest
may predate the edit. There is no automatic rollback: the previously
rendered unit is recorded nowhere, so a failed probe finishes failed and
says exactly that.
Exposed as POST /api/v1/services/{name}/converge (no body) and
'snowdeploy converge <service>', which follows the event stream like
deploy and rollback.
… truth Review caught two holes in the first cut. Converge read the mirror without syncing at the front of the queue, so it applied whatever the daemon last happened to fetch — an edit merged while it waited went unapplied yet the run finished healthy, and after a failed deploy's revert the mirror still pinned the digest that had just failed its probe. Converge now syncs before its re-read, wording failures through syncFailure so the credential-bearing transport error stays out of the journal. And when the pin moved while the converge waited, its receipt kept the click-time digests: a healthy row whose new_digest this run never verified, feeding PreviousHealthyDigest the wrong rollback target. Journal.SetDigests rewrites the still-unfinished entry to the pin actually applied; it refuses finished receipts, so receipts stay immutable. The single-view fake repo could not see either bug — merge landed straight in the manifests every read consulted. It now keeps main and the fetched mirror apart, the way the real gitops repo does, with an onSync hook for moving main between two syncs.
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.
A manifest edit that changes env, volumes, or the template without moving the image digest had no path to the host:
Deployrefuses the pinned digest (ErrAlreadyAtDigest), so the rendered unit kept the old environment until the next digest change — observed live 2026-08-27 during the snowsys Phase 12 budget red test (a mergedBUDGET_MONTHLY_USDnever reached the unit).What this adds
converge, that applies the manifest as main holds it at the front of the service's queue — sync, re-render, write the unit, restart, probe. It opens no pull request (the change it applies already merged through review) and never enters the merge queue. It journals a run like any other, joins duplicate converge clicks, and never joins a pending deploy at the same digest, whose frozen manifest may predate the edit.failedwith explicit guidance instead of pretending it can restore anything.SetDigestsrewrites a still-unfinished entry when the pin moved while the converge waited, so a healthy converge receipt names the pin it actually verified (PreviousHealthyDigeststays honest). Finished receipts are refused.POST /api/v1/services/{name}/converge(authed, no body) → 202{journalId, joined}.snowdeploy converge <service>, following the event stream to a terminal state like deploy/rollback.Design doc with the considered alternatives:
docs/superpowers/specs/2026-08-27-converge-action-design.md.Review
An adversarial review pass caught two real bugs in the first cut, both fixed test-first in 5f3a1c2: converge applied the mirror without syncing at its turn (stale or post-revert mirrors would be applied and reported healthy), and a moved pin left click-time digests on the receipt. The test fake could not see either, so
fakeReponow models main and the fetched mirror separately with anonSynchook.go vetclean, fullgo test -race ./...green. Shipping to the SnowSys host is a separate, operator-scheduled release cycle — this PR only lands the code.