ADR-0044: Repository Decomposition & Source Parity (P13)
Status: Accepted
Date: 2026-06-12
Author: @johalputt
Context
Through Prompt 12, the entire VayuPress Go application lived embedded inside a bash
heredoc in scripts/deploy-vayupress.sh. The repository contained zero .go
files. This delivered an excellent self-contained install model
(curl -sSL ... | bash, per Prompt 5) but created severe engineering debt:
- No native Go tooling (
go build,go vet,go test). - No IDE indexing, jump-to-definition, or refactoring support.
- Weak static analysis —
golangci-lintandgovulncheckcould not run. - Poor contributor ergonomics — a contributor could not clone and build.
- Difficult debugging and code review of a 2,400-line heredoc.
Prompt 13 governs the transition to a real, navigable Go source tree without breaking the self-contained install model.
Decision
-
Canonical source stays embedded. The deploy script's heredoc (
cat > main.go << 'GOEOF' ... GOEOF) remains the canonical source of truth socurl -sSL ... | bashkeeps working unchanged (Prompt 5: Operational Simplicity). -
Mirror to a real tree. The identical Go source is committed at
cmd/vayupress/main.go, with committedgo.modandgo.sum, enabling nativego build,go vet,go test,gofmt,golangci-lint, andgovulncheck. -
Enforced parity via
scripts/sync-source.sh. The script extracts the heredoc body and writescmd/vayupress/main.go. In--checkmode it runs in CI (P13 · Source Sync) and fails the build if the two ever drift. The deploy script and the real tree can never silently diverge (Prompt 10: Automated Governance). -
gofmt-clean canonical. The canonical heredoc source was normalized once with
gofmtso the mirror passes the formatting gate. Compactness was sacrificed for tool-compatibility; the deploy script grew accordingly. -
Pinned, govulncheck-clean dependencies + latest-stable toolchain. The deploy script pins exact dependency versions matching the committed
go.mod(e.g.go-chi/chi/v5@v5.1.0,golang.org/x/crypto@v0.39.0,golang.org/x/net@v0.41.0) instead of@latest, making deploys reproducible. Bringing up the CIgovulncheckgate surfaced two classes of reachable vulnerabilities:- Third-party:
golang.org/x/net/html(pulled in by bluemonday) — fixed byx/net >= 0.41.0/x/crypto >= 0.39.0. - Standard library:
crypto/x509.Verify,html/template.Execute,net/textproto.ReadMIMEHeader,net.Listen,net.Resolver.LookupIPAddr, all reachable from our HTTP/template/TLS paths — fixed only in newer Go releases.
Per the Constitution's priority order (Security > Simplicity > Performance), the build toolchain was moved to the latest stable Go (1.25.11) across the deploy script's
GO_VERSIONand the CIsetup-goversion (go-version: '1.25', which tracks the newest patch and therefore the newest stdlib security fixes). The committedgo.modkeeps ago 1.23.0minimum directive. Using@latestfor deps would also have pulled chi v5.3.0 unpredictably — pinning removes that risk. - Third-party:
-
Progressive split. This ADR covers the first stage: a single
mainpackage extracted with zero behavior change (verified:go build,go vet,go test,gofmt -lall clean). Splitting intointernal/packages (auth, db, security, health, queue) is deferred to follow-up P13.x increments, each independently verified, to keep risk low.
Rationale
Keeping the heredoc canonical preserves the project's signature install UX while the mirrored tree unlocks the entire Go toolchain. The CI sync-check makes drift impossible, so contributors get real tooling and operators get an unchanged install path — both guarantees enforced by automation rather than discipline.
A single-package extraction first (rather than a full internal/ split) means the
working binary is never at risk during the transition; the split can proceed
incrementally with each step independently green.
Consequences
- Positive:
git clone && go build ./...now works. IDEs index the code. - Positive: CI runs native
go vet,gofmt,go build -race,go test -race, andgovulncheck— directly addressing the "weak CI" finding. - Positive: Reproducible deploys via pinned dependency versions.
- Negative: The deploy script grew (~4,300 → ~5,500 lines) because gofmt expanded the previously compact one-line function bodies. Acceptable: tool-compatibility over byte-golfing.
- Negative: Two copies of the source exist. Mitigated entirely by the CI sync-check — divergence fails the build.
- Follow-up:
internal/package split (P13.x), on-disk migration files, and full restore-boot validation remain future work.