Skip to content

fix(repo): a status write that conflicts after a successful push no longer wedges the Repo (#26) - #27

Merged
braghettos merged 1 commit into
mainfrom
fix/repo-create-status-conflict
Sep 28, 2026
Merged

braghettos merged 1 commit into
mainfrom
fix/repo-create-status-conflict

Conversation

@braghettos

Copy link
Copy Markdown

Fixes #26.

The bug

  • What happened: a Repo whose push had landed got stuck permanently on krateo.io/external-create-pending.
  • Why: the status write after the push lost an optimistic-concurrency race, and Create returned that 409. provider-runtime requeues a conflict from Create without recording an outcome (reconciler.go:997-1008), so the resource stays stuck.
  • Where it was seen: krateo-057 on 0.15.4, on the first publish of a blueprint, 14 ms after a successful push.

The fix

  • Retried status writes: both status writes in SyncRepos (the committed path and the already-up-to-date path) now go through updateStatusWithRetry. It retries on conflict, re-reads the object, and keeps both the status the sync produced and the external name it set. This is the LocalResource fix from LocalResource wedges on external-create-pending under fan-out: concurrent pushes to one branch + a lost status-write race #16, which Repo never got.
  • External name kept: a status-subresource write returns the server's object, whose annotations replace the local copy's. So the helper sets the external name again after the write. The runtime persists it as the create's critical annotation.
  • Observe fallback: Observe reads the target commit from the external name when status.targetCommitId is empty, so a Repo whose status write was lost converges.

Tests

  • Real-conflict test: a concurrent write makes the reconciler's resourceVersion stale, then the test checks that:
    • the status lands;
    • the concurrent write survives;
    • the external name survives.
    • Mutation check: with the re-read removed, it fails.
  • Source guard: no bare Status().Update may exist outside the helper, as fix: a concurrent push must not wedge a LocalResource forever (#16) #17 does for LocalResource.
  • Checks: go test -race ./... and go vet pass.
  • Not unit-tested: the Observe fallback, which needs a git remote; it's covered by the live check after rollout.

Not in scope

Repos that got stuck before this release (#22). Resolving an existing pending annotation needs the runtime-side confirm-the-negative.

🤖 Generated with Claude Code

https://claude.ai/code/session_016V8EVEvyty3XvXMHUWJvPg

…onger wedges the Repo (#26)

A Repo whose push had LANDED wedged permanently on krateo.io/external-create-pending. The status
write after the push lost an optimistic-concurrency race, Create returned that 409, and
provider-runtime answers a conflict from Create by requeueing without recording an outcome
(reconciler.go:997-1008) — pending stays with neither -succeeded nor -failed, and every later
reconcile refuses with "cannot determine creation result". Seen on krateo-057 (0.15.4) on the first
publish of a blueprint, 14ms after a successful push.

- Both status writes in SyncRepos (the committed path and the already-up-to-date path) go through
  updateStatusWithRetry: RetryOnConflict that re-reads the object and keeps the status the sync
  produced and the external name it set — the LocalResource fix from #16, which Repo never got.
- A status-subresource write returns the server's object, whose annotations replace the copy's, so
  the helper sets the external name again after the write: the runtime persists it as the create's
  critical annotation, the one record of the push that does not depend on the status write.
- Observe reads the target commit from the external name when status.targetCommitId is empty,
  instead of checking the history for "" — so a Repo whose status write was lost converges.

Tests: a REAL conflict (a concurrent write makes the reconciler's resourceVersion stale) — the
status lands, the concurrent write survives, the external name survives; with the refresh removed
the test fails. A source guard pins that no bare Status().Update exists outside the helper (the
LocalResource guard, #17). go test -race ./... passes.

Not fixed here: Repos wedged before this release (#22) — resolving an existing pending annotation
needs the runtime-side confirm-the-negative.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016V8EVEvyty3XvXMHUWJvPg
@braghettos
braghettos merged commit 51a361b into main Sep 28, 2026
3 checks passed
@braghettos
braghettos deleted the fix/repo-create-status-conflict branch September 28, 2026 08:01
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.

Repo create wedges on external-create-pending when the status write after a successful push conflicts (0.15.4)

1 participant