Surfaced during review of #196 (pre-existing ordering; not in that PR's diff).
fork_repo ordering (api/repos.rs:1555 -> 1567 -> 1585 -> 1605): spend proof -> git clone --mirror to disk -> release_after_write (Tigris upload) -> db.create_repo. If create_repo fails, the on-disk mirror and the already-uploaded Tigris object are left with no DB row, and the single-use iCaptcha proof is already consumed.
create_repo (api/repos.rs:208 -> 210-219 -> 236): proof.consume then repo_store.init (blocking git init) run before db.create_repo; a DB failure orphans the freshly-initialized directory. No cleanup-on-failure exists on either path.
Fix direction: make disk materialization + proof spend adjacent to a guaranteed-successful DB write; on db.create_repo failure best-effort remove_dir_all(disk_path), and prefer deferring the Tigris upload until after the row commits. Verified by tracing the ordering during the #196 review.
Surfaced during review of #196 (pre-existing ordering; not in that PR's diff).
fork_repo ordering (api/repos.rs:1555 -> 1567 -> 1585 -> 1605): spend proof ->
git clone --mirrorto disk ->release_after_write(Tigris upload) ->db.create_repo. Ifcreate_repofails, the on-disk mirror and the already-uploaded Tigris object are left with no DB row, and the single-use iCaptcha proof is already consumed.create_repo (api/repos.rs:208 -> 210-219 -> 236):
proof.consumethenrepo_store.init(blockinggit init) run beforedb.create_repo; a DB failure orphans the freshly-initialized directory. No cleanup-on-failure exists on either path.Fix direction: make disk materialization + proof spend adjacent to a guaranteed-successful DB write; on
db.create_repofailure best-effortremove_dir_all(disk_path), and prefer deferring the Tigris upload until after the row commits. Verified by tracing the ordering during the #196 review.