ADR-001 and ADR-010 both leave unanswered which repository files the hub reads. Verified 2026-08-25: neither the forge nor a working copy — the projection derives from whichever checkout most recently ran the sync, which is written down nowhere. Proposes the forge as projection source, unpushed local work as a labelled preliminary overlay rather than a second store, and commit provenance on every record. Implements ADR-010 decision 6's unbuilt notion of "preliminary" and sharpens decision 1 rather than reversing it. Status: proposed — needs review before anything is built on it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
8.3 KiB
| id | type | title | status | owner | revision | last_reviewed | review_interval | decided_by | date | tags | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| CUST-ADR-012 | architecture-decision-record | What the Hub Projects: Forge as Projection Source, Working Copies as Preliminary Overlay | proposed | the-custodian | draft-1 | 2026-08-25 | 6m | Bernd Worsch | 2026-08-25 |
|
ADR-012: What the Hub Projects — Forge as Projection Source, Working Copies as Preliminary Overlay
Status
Proposed. Supersedes ADR-010 decision 1's phrase "authoritative as a reading of
the repositories" by making the reading concrete, and implements decision 6's
unbuilt notion of "preliminary".
Context
ADR-001 says work originates as repository files and the hub is a read model.
ADR-010 says central is authoritative as a reading of the repositories. Both
leave one question unanswered, and it turns out to be the load-bearing one:
Which repository files? The forge, or a working copy?
Verified on 2026-08-25, the answer today is neither.
Central runs one FastAPI pod on railiance01 with a CNPG database. Inspected from
inside, it holds /app — its own source — and nothing else. No clone tree, no
repository mount; the sweep hostPath that would give it one is disabled. The
hub never reads a repository. It cannot see the forge, and it cannot see a
workstation.
What actually happens is the inverse of the ADR language: fix-consistency and
registrar-reconcile run on a workstation, read files in /home/<user>/<repo>,
and POST the result over a tunnel. The host_paths field records this plainly —
117 repositories carry a path on bnt-lap001, a laptop.
So the projection derives from whichever checkout most recently ran the sync. That is a third thing, distinct from both the forge and any particular working copy, and it is written down nowhere.
Why this is not academic
Every failure investigated under CUST-WP-0067 and CUST-WP-0068 is downstream
of it:
- A local hub and central both served port 8000, and every default reached the local one for seven weeks. Neither could be distinguished by what it projected.
- 50 repository records pointed at a retired forge, so the sync could not match the checkout and silently registered nothing.
- Duplicate registrations accumulated: the same file registered twice under two identifiers, because two environments each believed they were projecting it.
git_fingerprint— the field that should say which commit a projection reflects — still holds the initial commit forthe-custodian, whilelast_state_synced_atis minutes old. There is no reliable way to ask central what state it is a projection of.
The forcing function
With one contributor, "last writer wins" is invisible, because there is only one
writer. With several, each pushes a projection of a different repository state
into one shared read model, and each will look locally consistent while
disagreeing with the others. That is the peer-database problem ADR-010
diagnosed, relocated one layer up and made harder to see.
The window to fix this cheaply is before the second contributor, not after.
Decision
1. The forge is the projection source. Central derives its projection from Forgejo — the pushed state of the default branch — not from any working copy. "Authoritative as a reading of the repositories" now names a specific reading: what the forge holds.
Forgejo runs in the same cluster as central, so this is an in-cluster fetch. No
tunnel, no cross-network credentials, and central already carries a git binary.
2. Every projected record carries the commit it derived from. A projection
without provenance cannot be audited, and the existing git_fingerprint proves
the point by being wrong and silent about it. Records must reference the commit
that produced them, and a projection whose commit is older than the forge's head
must be visibly stale rather than quietly current.
3. Unpushed local work is a preliminary overlay, never the baseline. A working copy may contribute records the forge does not yet hold. They are marked preliminary, attributed to their environment, and never silently merged into the derived baseline. When the commit that carries them reaches the forge, the baseline absorbs them and the overlay entry retires.
This implements ADR-010 decision 6, which named "preliminary" but left it
unbuilt.
4. The overlay is a label, not a second store. Preliminary records live in the same projection, flagged. They are not a parallel database and not a local hub. Every local projection this ecosystem has had was eventually mistaken for authority; the defence is that there is nothing separate to mistake.
5. A working copy is a workspace, not a source of authority. Deleting a checkout after pushing must lose nothing and invalidate nothing. Validation of a projection must not require a local clone of the repository being validated.
6. Push-based sync is retired as the primary path. Workstation tooling stops being how records reach central. It may continue to propose preliminary records under decision 3, but the baseline is derived, not received. This closes the gap where "central derives" was policy while "the laptop pushes" was practice.
7. Formal git review stays optional. Deriving from the default branch gives a shared baseline without requiring pull requests. Review can be adopted per repository where it earns its keep; this ADR neither mandates nor forbids it.
Consequences
Positive. Truth becomes checkable by anyone, from anywhere, without a clone. Multiple contributors share one baseline instead of overwriting each other's views. Provenance becomes auditable — every record can name its commit. The "push then delete the working copy" case simply works. The distinction between committed and uncommitted work becomes visible in the model rather than a matter of who ran which command last.
Negative. Git becomes load-bearing for the hub: Forgejo availability now affects projection freshness. A derive loop needs a cadence, and freshness becomes a property to monitor rather than assume. Unpushed work becomes explicitly second-class — which is its honest status, but it will feel like a restriction to a solo developer used to local-first behaviour.
Migration. The sweep hostPath — central reading a node-local clone tree —
is a half-measure toward this decision and was disabled pending "governed remote
reconciliation" while railiance01 checkouts still targeted Gitea. As of
2026-08-25 all 79 node checkouts track Forgejo, so that stated blocker has
cleared. Sweep should be evaluated as a stepping stone or retired in favour of a
direct forge fetch, not left dormant with an obsolete justification.
Unresolved. This ADR does not settle the derive cadence, whether central clones or uses the Forgejo API, how preliminary records are surfaced in the dashboard and MCP, or what happens to a preliminary record whose commit never arrives. Those belong to implementation.
Relationship to prior decisions
ADR-001— unchanged. Work still originates as repository files; this ADR says which copy of them the hub reads.ADR-010— decisions 1 and 5 are sharpened, not reversed: central still derives and still does not accept pushes of derived state. Decision 6's "preliminary" gains a mechanism. The local-cache-versus-database framing stands.ADR-007— derived identifiers become more valuable here: a forge-derived projection and a preliminary overlay compute the same identifier for the same record, so absorbing an overlay entry needs no reconciliation.
References
ADR-001— workplans originate as repo files; hub is a read modelADR-010— hub authority, local cache, and the two kinds of hub dataADR-007— identifier uniqueness and derived identifiersCUST-WP-0067— hub target resolution; retired the impersonating local instanceCUST-WP-0068— cache-only work-record recovery; surfaced the stalegit_fingerprintand the duplicate registrations- Verification, 2026-08-25: central pod holds no repository files;
sweepdisabled; 117 repositories record a laptop path;the-custodiangit_fingerprintis the initial commit whilelast_state_synced_atis current