Blocked by #6820. Closes #6701.
What happens
CreateRepo returns as soon as the GitHub API accepts the creation request. Callers that immediately perform git operations (create tree, commit, branch) hit eventual-consistency errors because the auto_init commit hasn't propagated:
422 Tree SHA does not exist
422 base_tree is not a valid tree oid
409 Git Repository is empty
404 Not Found
The current workaround — awaitCreation in ensure.go — polls GetRepo, which only proves API visibility, not git-layer readiness. The poll succeeds but the repo isn't actually usable yet.
4 behaviour test failures in a 2-day window (Aug 31) hit these errors, all during test setup on freshly created repos. See also #5671 (closed), #5774 (closed), #6701 (open, superseded by this issue).
What should happen
CreateRepo should not return until the repo is ready for git operations. Polling GetRef("heads/main") (or equivalent) after the create call confirms the auto_init commit and default branch ref have propagated. Once that succeeds, the repo is usable — git objects exist and permissions are settled.
Every existing CreateRepo caller immediately performs git operations on the result, so this benefits all call sites with no behavior change.
Cleanup
Once #6820 lands (ephemeral single-org repos with --per-repo-wif-repos "*" on the preview mint), the pool and recycling machinery becomes dead code. This is a significant simplification.
Pool recycling (already identified)
- Delete
repopool_external_mint.go — unused factory, bot-generated, never called
- Delete
resetRepo, awaitCreation, awaitDeletion, ensureRepoExists and related constants (resetMaxAttempts, resetRetryDelay)
Channel-based pool in composedDriver
With --per-repo-wif-repos "*" on the preview mint, repo names no longer need to be coordinated between the factory and the driver. Each repo is unique, created once, and deleted after its test. The pool pattern is unnecessary:
- Remove
names chan string, capacity field, Capacity() method
AllocateRepo generates a fresh bt-{uuid} name on the spot instead of pulling from a channel
DeallocateRepo no longer pushes names back to the channel — the repo is deleted, the name is never reused
- Remove
DefaultPoolSize, envPoolSize(), BEHAVIOUR_POOL_SIZE env var support
- Remove
buildEphemeralRepoList and the prefix parameter threading from factory → composed driver
- If concurrency limiting is still needed, use a
sync.Semaphore instead of a channel
Ensurer simplification
Each repo is unique and never reused, so caching and deduplication are unnecessary:
- Remove
ensured map and singleflight.Group from repoEnsurer
- Remove
InvalidateCache method and the ensurer interface's InvalidateCache contract
- Collapse the
ensurer interface into a simple function: CreateRepo → installFullsend → awaitWorkflowReady
Finalize cleanup
- Simplify
Finalize's lease reclamation — outstanding repos should be deleted, not pushed back to a pool
- Mint teardown remains unchanged
Summary
The ensure flow simplifies from the current multi-step reset/create/validate/cache cycle to: CreateRepo (with git-readiness wait) → installFullsend → awaitWorkflowReady. The composed driver becomes a thin wrapper around name generation, outstanding-repo tracking, and mint lifecycle.
Blocked by #6820. Closes #6701.
What happens
CreateReporeturns as soon as the GitHub API accepts the creation request. Callers that immediately perform git operations (create tree, commit, branch) hit eventual-consistency errors because theauto_initcommit hasn't propagated:422 Tree SHA does not exist422 base_tree is not a valid tree oid409 Git Repository is empty404 Not FoundThe current workaround —
awaitCreationinensure.go— pollsGetRepo, which only proves API visibility, not git-layer readiness. The poll succeeds but the repo isn't actually usable yet.4 behaviour test failures in a 2-day window (Aug 31) hit these errors, all during test setup on freshly created repos. See also #5671 (closed), #5774 (closed), #6701 (open, superseded by this issue).
What should happen
CreateReposhould not return until the repo is ready for git operations. PollingGetRef("heads/main")(or equivalent) after the create call confirms theauto_initcommit and default branch ref have propagated. Once that succeeds, the repo is usable — git objects exist and permissions are settled.Every existing
CreateRepocaller immediately performs git operations on the result, so this benefits all call sites with no behavior change.Cleanup
Once #6820 lands (ephemeral single-org repos with
--per-repo-wif-repos "*"on the preview mint), the pool and recycling machinery becomes dead code. This is a significant simplification.Pool recycling (already identified)
repopool_external_mint.go— unused factory, bot-generated, never calledresetRepo,awaitCreation,awaitDeletion,ensureRepoExistsand related constants (resetMaxAttempts,resetRetryDelay)Channel-based pool in
composedDriverWith
--per-repo-wif-repos "*"on the preview mint, repo names no longer need to be coordinated between the factory and the driver. Each repo is unique, created once, and deleted after its test. The pool pattern is unnecessary:names chan string,capacityfield,Capacity()methodAllocateRepogenerates a freshbt-{uuid}name on the spot instead of pulling from a channelDeallocateRepono longer pushes names back to the channel — the repo is deleted, the name is never reusedDefaultPoolSize,envPoolSize(),BEHAVIOUR_POOL_SIZEenv var supportbuildEphemeralRepoListand theprefixparameter threading from factory → composed driversync.Semaphoreinstead of a channelEnsurer simplification
Each repo is unique and never reused, so caching and deduplication are unnecessary:
ensuredmap andsingleflight.GroupfromrepoEnsurerInvalidateCachemethod and theensurerinterface'sInvalidateCachecontractensurerinterface into a simple function:CreateRepo→installFullsend→awaitWorkflowReadyFinalize cleanup
Finalize's lease reclamation — outstanding repos should be deleted, not pushed back to a poolSummary
The ensure flow simplifies from the current multi-step reset/create/validate/cache cycle to:
CreateRepo(with git-readiness wait) →installFullsend→awaitWorkflowReady. The composed driver becomes a thin wrapper around name generation, outstanding-repo tracking, and mint lifecycle.