refactor: move the reconciler's two deletes down to the layers that own them The reconciler reached past its layer twice, because the primitives it needed did not exist: a raw DELETE FROM proposal — the only SQL written outside db/ — and a go-git RemoveReference under gitx's write lock. Both move down, with no change in behaviour. db.Store.DeleteOpenProposal keeps the guard in the statement, as resolveProposal does, and distinguishes the two ways it can bite: a row that is gone is ErrNotFound, a row that has been resolved is the new ErrProposalNotOpen, which tells the reconciler "nothing to repair" apart from "the repair no longer applies". gitx.Repo.DeleteProposalBranch refuses anything outside proposals/* — the only thing between a caller bug and a deleted approved branch — takes the per-space write lock like every other write, and treats an already-absent branch as success: the repair is a postcondition, and the ref may legitimately vanish between the listing that found it and the delete.
feat: service — wiring, space lifecycle, read paths, push validation, reconciler Phase 1 of the implementation plan: the orchestration layer's read, validate and repair halves. The write plane (propose/merge) is Phase 3 and is absent. - Config/New assemble a Service from the shared config.ini, reporting every missing key in one message; TokenStore adapts db/ to authn.TokenStore, mapping db.ErrNotFound onto authn.ErrUnknownToken so an unknown credential is a 401 rather than a 503. - CreateSpace writes the repository first and the row second, removing the repository again if the insert fails; a crash between the two leaves content that is merely unlisted rather than a phantom space. - ReadDocument/ListDocuments/Policy/ResolveRev resolve the approved head or a pinned revision through one code path. - ValidatePush checks the refs rule first and unconditionally, then frontmatter and document-id uniqueness, which --push-option=skip-validation waives. The rejection is a structured, terminal-shaped message naming the document. - PlanRepairs is the repair table as a pure, table-tested function; Reconcile gathers the facts and applies them, listing stale-index spaces for Phase 2. Two departures from the design's repair table, both to stop the reconciler destroying live state, documented at their definitions: an open row with no branch is left alone inside a grace window (every propose passes through that state), and a branch still sitting on its recorded base is never treated as merged (its tip is trivially an ancestor of the approved head).