Artifact d1e91867ef8f85667423505e0099836816c7d5417b66a80b0d19e623907ccea5:
- File
internal/arch/arch_test.go
— part of check-in
[bd91af3158]
at
2026-08-16 05:16:45
on branch trunk
— chat archival: fold chat into forum-thread artifacts (0001 §5 durability)
internal/chatarchive: batches a channel's messages (per N or T) and mints them as a 'Chat archive' forum thread through zit forum post/reply (zit#76) — one thread per channel, each batch a reply carrying the transcript, so history clones with the repo. Minting runs only on the lease holder; non-holders drop their buffers (the holder's archive folds back to every node). zitcli gains ForumPost/ForumReply/ TechnoteAdd. --chat-archive on by default. Verified live: chat batches materialize as forum threads and reply to the same thread. (user: dmestas, size: 3297) [annotate] [blame] [check-ins using]
// Package arch holds quarry's structural assertions. This test enforces the // ops-seam rule from docs/rfcs/0001-quarry-forge.md §9: only internal/ops // may import the data planes. It runs `go list` over the module, so it needs // no dependencies and cannot drift from the real build graph. package arch import ( "encoding/json" "os/exec" "strings" "testing" ) // dataPlanePrefixes are import-path prefixes only internal/ops may reach. // Grow this list as data planes land (SQLite driver, forge OLTP, the zit // subprocess wrapper). The zero state still verifies the mechanism. var dataPlanePrefixes = []string{ "github.com/ncruces/go-sqlite3", "github.com/nats-io/nats-server", "github.com/nats-io/nats.go", "craftdesign.group/quarry/internal/zitcli", "craftdesign.group/quarry/internal/index", "craftdesign.group/quarry/internal/events", "craftdesign.group/quarry/internal/oltp", "craftdesign.group/quarry/internal/xfer", "craftdesign.group/quarry/internal/lease", "craftdesign.group/quarry/internal/chat", "craftdesign.group/quarry/internal/notify", "craftdesign.group/quarry/internal/chatarchive", } // allowedImporters may import data-plane packages directly. internal/api // is here because RegisterAll is the wiring point where op Run closures // over the data planes are built — the seam invariant is that data // reaches HTML and API responses only through registered ops, and this // list is what makes web/, cmd/, and everything else unable to shortcut. var allowedImporters = []string{ "craftdesign.group/quarry/cmd/quarry", // composition root: builds the object graph "craftdesign.group/quarry/internal/harness", // test harness: stands the system up from nothing "craftdesign.group/quarry/internal/ops", "craftdesign.group/quarry/internal/api", "craftdesign.group/quarry/internal/ingest", "craftdesign.group/quarry/internal/zitcli", "craftdesign.group/quarry/internal/index", "craftdesign.group/quarry/internal/events", "craftdesign.group/quarry/internal/oltp", "craftdesign.group/quarry/internal/xfer", // sync fronting: execs zit itself (0001 §7) "craftdesign.group/quarry/internal/lease", // single-writer coordination (0001 §3) "craftdesign.group/quarry/internal/chat", // per-repo chat plane (0001 §5) "craftdesign.group/quarry/internal/notify", // notification trigger edge (0001 §5) "craftdesign.group/quarry/internal/chatarchive", // chat -> forum durability (0001 §5) } type pkg struct { ImportPath string Imports []string Deps []string } func TestOnlyOpsImportsDataPlanes(t *testing.T) { out, err := exec.Command("go", "list", "-json", "./...").Output() if err != nil { t.Fatalf("go list: %v", err) } dec := json.NewDecoder(strings.NewReader(string(out))) for dec.More() { var p pkg if err := dec.Decode(&p); err != nil { t.Fatal(err) } if isAllowed(p.ImportPath) { continue } for _, imp := range p.Imports { for _, plane := range dataPlanePrefixes { if strings.HasPrefix(imp, plane) { t.Errorf("%s imports data plane %s directly; route through internal/ops", p.ImportPath, imp) } } } } } func isAllowed(path string) bool { for _, a := range allowedImporters { if path == a || strings.HasPrefix(path, a+"/") { return true } } return false }