Skip to content

fix(wave_init): atomic kahuna bootstrap + plan persist (resequence)#401

Merged
bakeb7j0 merged 1 commit intokahuna/390-bug-drainfrom
fix/378-wave-init-atomic
May 5, 2026
Merged

fix(wave_init): atomic kahuna bootstrap + plan persist (resequence)#401
bakeb7j0 merged 1 commit intokahuna/390-bug-drainfrom
fix/378-wave-init-atomic

Conversation

@bakeb7j0
Copy link
Copy Markdown
Contributor

@bakeb7j0 bakeb7j0 commented May 5, 2026

Summary

Resequences wave_init to make kahuna bootstrap + plan persist atomic. The reversible step (kahuna branch creation) now happens BEFORE the irreversible step (plan persist to disk), so kahuna failures cannot leave a half-state requiring --force to recover.

Background

Per #378: wave_init was non-atomic across its two intended steps. When kahuna bootstrap failed for any reason after plan persist succeeded, the wave plan ended up on disk but the kahuna didn't exist. Subsequent wave_init --extend calls failed with wave-ID-collision errors because the persisted plan blocked retry.

Changes

  • handlers/wave_init.ts — resequenced to: validate → kahuna remote bootstrap → wave-status init → set-kahuna-branch. Atomicity comment block added (lines 4-26).
  • lib/wave_init_plan.ts — split bootstrapKahunaBranch into bootstrapKahunaBranchRemote (no state writes) + recordKahunaBranchInState (state-only). Added previously_recorded field. Orphan-with-matching-name now claimed as idempotent reuse rather than refused.
  • tests/wave_init.test.ts — 6 new atomicity tests + 1 existing test updated.

Tests

  • ✅ 36/36 wave_init tests pass
  • ✅ 2237 unit tests + 18 integration tests pass via validate.sh
  • ✅ All ACs verified by tests

Behavior Changes

Orphan-with-matching-name (state empty + remote has exact kahuna/<plan_id>-<slug>) is now claimed as idempotent reuse instead of refused. This is necessary for retry semantics to converge after a wave-status init mid-flow failure. Safe because (plan_id, slug) is a deterministic mapping per plan.

Closes #378

Resequence so the kahuna branch is created on remote BEFORE
`wave-status init` persists the plan. Kahuna failure now leaves no
half-state: nothing on disk, nothing on remote.

Two-phase bootstrap in `lib/wave_init_plan.ts`:
  1. `bootstrapKahunaBranchRemote` — pre-check + create branch (no
     state.json writes; safe to run before `wave-status init`).
  2. `recordKahunaBranchInState` — `wave-status set-kahuna-branch`
     write, runs after `wave-status init` creates state.json.

Behavior change: when state has no kahuna_branch but the remote has
the EXACT desired `kahuna/<plan_id>-<slug>` branch, claim it as
idempotent reuse rather than refusing as orphan. This makes retry
converge after a `wave-status init` failure that left the branch on
remote but never persisted state.

Tests cover: ordering (create-branch → init → set-kahuna), kahuna
failure paths blocking init, retry-after-init-failure orphan claim,
extend-mode wave-ID-collision absence after kahuna fail.

Pre-#378 single-phase `bootstrapKahunaBranch` retained as deprecated
back-compat for any external caller; updated to use `previously_recorded`.

Closes #378
@bakeb7j0 bakeb7j0 merged commit cabbf61 into kahuna/390-bug-drain May 5, 2026
1 check failed
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