Home / Projects / Orchestrata Ultimate / Decisions / ADR index / ADR-0004
ADR-0004: Facts are keyed by (worktree, commit); merge is fact promotion
| field | value |
|---|---|
| Status | Accepted |
| Date | 2026-07-23 |
| Register | factory (docs/adr, 4-digit ids) |
| Source | docs/adr/0004-facts-keyed-by-worktree-commit.md — permalink at bdafa201a (unchanged on master since that commit) |
| Wiki register entry | ADR-0004 (wiki status: Accepted) |
The record
- Status: Accepted
- Date: 2026-07-23
- Spec: SPEC-033 §4
Context
The build runs parallel worktrees by DAG level. AST and traceability facts are branch-dependent state: worktree A and worktree B hold different ASTs of overlapping modules at the same instant.
With a single unversioned fact space, concurrent branches poison each other's AST ≡ design checks,
and the integrated-trunk re-gate cannot distinguish trunk facts from branch facts. Every downstream
gate then reads a fact set that corresponds to no actual tree.
Decision
All AST/trace facts are keyed by (worktree, commit).
- Merge = fact promotion into the trunk namespace, performed after the integrated-trunk re-gate passes — never before, and never by the agent's own git (see ADR-0018).
- Facts from abandoned worktrees are garbage-collected, with a retention rule, so
(worktree, commit)keys cannot collide aftergit worktree removeand reuse. - Deployment contract: one factory per Neo4j database (ruled by Mohamed, 2026-07-24 — binding,
do not re-open). GC judges every non-trunk namespace in the graph against the caller's live
git worktree list; that is sound only when exactly one factory (one repo) owns the database. Two factories sharing a database WILL delete each other's in-flight namespaces (demonstrated 2026-07-24 under a mis-isolated parallel test run). Isolation is by database, not by key: namespaces carry no repo identity, and gc is deliberately not repo-scoped.
Alternatives rejected
- Single mutable fact space — the poisoning failure above; makes parallelism unsound.
- A separate graph per worktree — no trunk view, no cheap cross-branch impact query, and merge becomes a graph-union problem rather than a promotion.
- Serialise all worktrees — abandons the parallelism the DAG schedule exists to exploit.
Consequences
- Parallel level-based building becomes sound; conflicts surface exactly at the integration gate.
- Fact volume grows per commit — retention/GC is a first-class requirement, not an afterthought.
- The promotion step is a privileged operation and therefore belongs on the trusted side.
- Note the boundary this does not fix:
(worktree, commit)eliminates graph-vs-Prolog drift, but graph-vs-worktree-files drift is a separate problem, addressed by refresh-on-write plus the full-tree security diff (ADR-0001).
Provenance
Round-2 Fable: "AST facts are branch-dependent state… parallel branches poison each other's checks", and named merge-as-promotion as the correct construction. Round-3 Sonnet flagged the missing dependency on the integration re-gate and the abandoned-worktree GC question.
Related ADRs
Rulings cited
None.