Skip to content
quarry Artifact [e1ab343af3]
Login

Artifact [e1ab343af3]

Artifact e1ab343af39685ad2d69dfe111980b971982f3158ae5115263678208f0747ce8:


0003 — quarry implementation plan

Companion to 0001-quarry-forge.md (the spec) and 0002-forgejo-salvage.md (the UI salvage map). This document owns what the spec deliberately does not: concrete technology choices, build order, testing strategy, and observability wiring. Decisions here are revisable without touching the spec; decisions that would change the spec's architecture go back through 0001.

0. Guiding constraints (inherited, not restated)

1. Build order

The organizing rule: the spec's riskiest claims get proven by a tracer bullet before any breadth work, and the three enforcement mechanisms (disposability harness, ops registry, provenance ledger) are built first, because retrofitting a merge gate onto an existing codebase never works.

Phase 0 — rails before features

Exit: CI runs all three gates; a dummy op renders a dummy page and appears in a generated OpenAPI document.

Phase 1 — the tracer bullet

One store → watcher (validated-prefix fold via the zit subprocess, zit#17 stat-and-reopen discipline from the start) → embedded NATS event → per-node SQLite fold → one op (timeline.list) → one HTML page and its generated API endpoint, footer with versions and render timing.

Proves the four load-bearing claims at minimum cost: log-as-event-stream, embedded-index-as-derived-state, registry-generates-spec, disposability-as-gate.

Phase 2 — read plane breadth

0002 §4's extraction order: markup first (cleanroom sanitizer + conformance corpus before anything renders) → base layout → fleet → repo home/files → timeline/check-in → diff stack → graph → wiki render → search (FTS5) → activity/stats. Each page lands against the per-page definition of done (0002 §4 item 5) and grows the disposability snapshot set incrementally.

Phase 3 — liveness

Datastar SSE wiring, chat transport + forum archival, notify integration. First real test of Principle 3's refresh-fallback discipline.

Phase 4 — sync fronting + leases

/xfer fronting, then the 0001 §3 lease machinery. Deliberately last in v1: it is the only distributed-systems-hard part, and everything before it works single-node.

v2/v3 then follow 0001 §10's staging. Each phase ends with the harness green and a docs/ page generated from the spec — documentation is a phase exit criterion, not a v-final activity.

2. Technology choices

Each entry: choice, rationale, escape hatch. These are ADR-grade decisions recorded compactly; split any into a standalone ADR when it accretes real debate.

Area Choice Rationale Escape hatch
SQLite driver ncruces/go-sqlite3 Upstream SQLite compiled to Wasm (wazero): real SQLite behavior, fast tracking of releases, no CGO (cross-compilation stays trivial), FTS5 enabled, and a Go-implementable VFS surface — the scale-out hook if node indexes ever want S3-backed hydration. Preferred over modernc (transpiler artifacts, historic concurrency edges) and mattn (CGO). Known costs: Wasm boundary tax on write-heavy paths (fits our read-dominated profile), amd64/arm64-only compiler (interpreter elsewhere), ~tens-of-ms startup compile, narrow bus factor (mitigated: unmodified upstream SQLite makes a forced move driver-swap-shaped, not data-shaped). mattn (CGO) or modernc; schema is plain SQLite either way
HTTP router stdlib http.ServeMux (1.22+ patterns) The ops layer already does what chi would add; a router dependency is a shallow module here. chi, mechanically
Ops registry Hand-rolled on stdlib + generics (~1.5k lines total) The registry is quarry's spine; you don't build the central abstraction on someone else's framework. The flat, form-expressible struct constraint (0001 §9) makes the reflector genuinely small. Pieces: generic Register[In,Out] over a descriptor map (~150 lines, explicit RegisterAll() wiring); JSON codec (stdlib); form/URL-values codec (~150 lines, needed for HTML regardless of framework); path-param binding onto ServeMux wildcards; OpenAPI reflector (~300–400 lines); tag-driven validator shared by both codecs; instrumentation wrapper around Run. Huma (danielgtaylor/huma) — our Run signature is already its handler shape, so migration is mechanical
OpenAPI document types Reuse dagnats' OpenAPI 3.1 synthesis types (currently internal/openapi in dagnats — promotion to a public package is a prerequisite, see §5) One shared, tested document-assembly layer; quarry writes only the struct-reflection front half; both projects emit consistent 3.1 documents. Hand-roll the back half too (~200 more lines)
Spec correctness gate Off-the-shelf OpenAPI validator lints the generated spec in CI (dev-time tool, not a runtime dep); every registered op's fixtures round-trip through both codecs Closes "our generator emits a subtly invalid spec" without a runtime framework. —
Markup goldmark + bluemonday + chroma Per 0002 §2; sanitizer policy is cleanroom with the two-lineage conformance corpus + fuzz target. —
Embedded NATS nats-server as a library The dagnats/notify pattern, already proven in this stack. External NATS URL (already the scale-out flag)
litestream Supervised sidecar firestorm-proven shape; embedding buys nothing. —
Parquet writing Deferred with the interchange lake (0001: scale-out) No Parquet dependency in v1 at all. When the lake lands, prefer writing via a query engine's COPY TO over a standalone parquet library — decide then. —
Frontend Datastar (only default-allowed framework, 0001 Principle 2) + html/template Per 0001/0002. Explicit per-case exception process
zit access Subprocess boundary — the zit binary is the only store interface, reads and writes (0001 §2) zit is Zig, quarry is Go; the versioned CLI is the whole contract, so releases decouple and quarry stays CGO-free. Most page data serves from quarry's indexes; on-demand reads (file-at-checkin, diff, annotate) pay ~1–5ms spawn, off the hot path and behind caching. Quarry's CI tests against pinned zit versions and each release declares a minimum zit version. C-ABI read library exported from zit — revisit only with profiling evidence

3. dagnats integration

dagnats provides durable HTTP endpoints (HTTP trigger → event-sourced run → engine-resolved respond step) with OpenAPI 3.1 synthesis from trigger defs + declared schemas. Division of labor:

4. Testability — one strategy per risk class

5. Observability

Lean on dagnats: quarry imports dagnats' observe package for OTel exporter wiring, resource attributes, and conventions rather than building its own setup. Command ops propagate W3C trace context into the workflows they launch (dagnats' bridge already speaks tracecontext), so one stitched trace runs from HTTP request through the durable run.

Quarry adds only what dagnats cannot see:

6. Prerequisites and upstream work

7. Risks