Feature #210: create-repo placeholder #218

Merged
crueber merged 3 commits from feat/issue-210 into main 2026-09-08 21:38:23 +00:00
Owner

Explicit create-repo placeholder with push docs (Forgejo #210, plan R1 + review normative).

What: same-manifest + meta/placeholder.json sidecar (Create-only/delete-on-transition, Delete-then-Create only — no §14.11 change); ?placeholder=true on PUT /{o}/{r} (flag AS the shape) + POST /api/v1/repos twin via server.ExtraRoutes + api.RegisterExposed (both lanes, discovery lists /api/v1/repos); org-membership gate (403 only on proven non-membership, probe errors → 503); eager access.json default (none-mode skips creator binding); hint-gated first-push adoption (post-CAS post-response, push budgets unchanged — push-budget test passes unmodified); create-twice idempotency (same principal 200 already:true + unborn re-check; different → 409 plain-text with winner URL); /new form + placeholder view (creator/created, delete, clone/push commands) + SDK submodule (repos.create, createPlaceholder, naming validation); docs (06 §3+PUT row+/new reserved, 07 §9.1.1+shapes+discovery+6 Decisions, 12 /new+view, 01 §5.2+gate decision, EVIDENCE E14 + E13 amend). TTL sweep off by default (expires_at null, no counter objects).

Proof: go test -race green on api/identity/server/cmd; cover ≥95% (api 95.4, identity 97.2, server 95.6); node --test 417/417; e2e package green; live two-transport proof (real git HTTP+SSH push → first-try land, empty→healthy, marker adopted at bucket level, delete→204→recreate→201); real-Chromium 24/24 (create → placeholder → push → real, both themes, console clean, shots in review notes). No new Go modules, no new npm deps.

Closes #210 (do NOT merge — review requested).

Explicit create-repo placeholder with push docs (Forgejo #210, plan R1 + review normative). **What:** same-manifest + `meta/placeholder.json` sidecar (Create-only/delete-on-transition, Delete-then-Create only — no §14.11 change); `?placeholder=true` on `PUT /{o}/{r}` (flag AS the shape) + `POST /api/v1/repos` twin via `server.ExtraRoutes` + `api.RegisterExposed` (both lanes, discovery lists `/api/v1/repos`); org-membership gate (403 only on proven non-membership, probe errors → 503); eager `access.json` default (none-mode skips creator binding); hint-gated first-push adoption (post-CAS post-response, push budgets unchanged — push-budget test passes unmodified); create-twice idempotency (same principal 200 `already:true` + unborn re-check; different → 409 plain-text with winner URL); `/new` form + placeholder view (creator/created, delete, clone/push commands) + SDK submodule (`repos.create`, `createPlaceholder`, naming validation); docs (06 §3+PUT row+/new reserved, 07 §9.1.1+shapes+discovery+6 Decisions, 12 /new+view, 01 §5.2+gate decision, EVIDENCE E14 + E13 amend). TTL sweep off by default (`expires_at` null, no counter objects). **Proof:** `go test -race` green on api/identity/server/cmd; cover ≥95% (api 95.4, identity 97.2, server 95.6); `node --test` 417/417; e2e package green; live two-transport proof (real git HTTP+SSH push → first-try land, empty→healthy, marker adopted at bucket level, delete→204→recreate→201); real-Chromium 24/24 (create → placeholder → push → real, both themes, console clean, shots in review notes). No new Go modules, no new npm deps. Closes #210 (do NOT merge — review requested).
Implements docs plan Ticket 2 per R1 + review (normative): same-manifest +
meta/placeholder.json sidecar (Create-only/delete-on-transition, no
14.11 change), org-membership gate (403 only on proven non-membership),
eager access.json default (none-mode skips creator binding), hint-gated
post-CAS post-response adoption (push budgets unchanged), create-twice
idempotency (same principal 200 already:true + unborn re-check, else 409
plain-text with winner URL), POST twin via ExtraRoutes + RegisterExposed,
/new form + placeholder view + SDK submodule, docs (06/07/12/01+E14).
POST /api/v1/repos accepted visibility:private but EnsureRepoAccess always
materialized public (07_api.md documented the toggle). AccessBootstrap now
takes the request visibility ("" from the PUT-flag path = public default);
identity honors "private", unknown falls back to public. Tests: threading
(api) + private doc (identity). Coverage gates hold.
Sign in to join this conversation.
No description provided.