Skip to content

feat(cli): alchemy sync — reconcile state drift via read+reconcile - #766

Merged
sam-goodwin merged 3 commits into
mainfrom
claude/brave-tesla-0f53b4
Jul 8, 2026
Merged

feat(cli): alchemy sync — reconcile state drift via read+reconcile#766
sam-goodwin merged 3 commits into
mainfrom
claude/brave-tesla-0f53b4

Conversation

@sam-goodwin

Copy link
Copy Markdown
Contributor

Add alchemy sync: reconcile out-of-band cloud drift back to the last-deployed state recorded in the state store. Unlike deploy, it needs no re-plan of the stack program — per resource it runs observe → compare → converge:

  1. provider.read observes the live cloud state
  2. deep-compare observed vs. persisted attributes (equal ⇒ unchanged)
  3. on drift, provider.reconcile runs with persisted props as news and the observed attributes as output; a missing resource reconciles greenfield under the same instance id so deterministic physical names regenerate identically
  4. fresh attributes are persisted back to state
alchemy sync ./alchemy.run.ts --stage prod            # detect + repair
alchemy sync ./alchemy.run.ts --stage prod --dry-run  # detect only
const result = yield* Sync.sync({ name: stack.name, stage: stack.stage });
// result.resources: { [fqn]: { action: "unchanged" | "repaired" | "recreated" | "skipped" | ... } }
  • Resources sync concurrently and independently (persisted props are fully resolved, so there are no data edges to order by); failures are aggregated into one combined cause after every resource is attempted, mirroring apply.
  • Skipped (with a reason) instead of failed: providers without read, and rows in non-terminal statuses (creating, replacing, replaced, …) — those belong to deploy's recovery machinery. Action rows are ignored.
  • Unowned-branded reads are stripped and handled as ordinary tag drift; the brand never persists.
  • Tests introduce a DriftResource + TestCloud fixture (an inspectable in-memory cloud) so suites mutate/delete live state out-of-band and assert convergence — 21 cases covering repair, recreation, dry-run, skip reasons, partial-failure isolation, ownership, and binding-derived attribute repair.

🤖 Generated with Claude Code

…cile

Syncs every resource persisted under a stack/stage back to its
last-deployed desired state: observe live cloud state via provider.read,
deep-compare against persisted attributes, and on drift run
provider.reconcile with the observed attributes as output. Missing
resources reconcile greenfield under the same instance id so
deterministic physical names converge. --dry-run reports drifted/missing
without repairing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@alchemy-version-bot

alchemy-version-bot Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Install the packages built from this commit:

alchemy

bun add alchemy@https://pkg.ing/alchemy/a356a75

@alchemy.run/better-auth

bun add @alchemy.run/better-auth@https://pkg.ing/@alchemy.run/better-auth/a356a75

@alchemy.run/pr-package

bun add @alchemy.run/pr-package@https://pkg.ing/@alchemy.run/pr-package/a356a75

sam-goodwin and others added 2 commits July 7, 2026 17:04
…53b4

# Conflicts:
#	packages/alchemy/test/test.resources.ts
Sync.plan projects the detection pass onto the engine's Plan shape
(drifted → update, missing → create, unchanged/skipped → noop) so the
sync command reuses displayPlan / approvePlan / startApplySession —
ink TUI when interactive, plain logging fallback otherwise. Skipped
resources settle with a terminal 'skipped' status and a reason note.
Adds --yes; drops the bespoke printSync formatter.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@sam-goodwin
sam-goodwin merged commit dcafc26 into main Jul 8, 2026
5 checks passed
@sam-goodwin
sam-goodwin deleted the claude/brave-tesla-0f53b4 branch July 8, 2026 00:17
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