Skip to content

docs(adr): 0005-lineage-acyclicity — enforce DAG at write time#93

Merged
hyperpolymath merged 1 commit into
mainfrom
docs/adr-0005-lineage-acyclicity
May 14, 2026
Merged

docs(adr): 0005-lineage-acyclicity — enforce DAG at write time#93
hyperpolymath merged 1 commit into
mainfrom
docs/adr-0005-lineage-acyclicity

Conversation

@hyperpolymath
Copy link
Copy Markdown
Owner

Summary

Per V-L1-G1: README claims "DAG" but multi-hop cycles still slip through after #42 (which only blocks self-loops). Pick enforce-at-write-time:

Alternatives rejected: rename to "Lineage Graph" (every consumer would have to defend against cycles forever), SQL triggers only (varies across backends).

Implementation lands in V-L1-G2 (to be filed). This ADR is binding for that follow-up.

Closes

Test plan

Doc-only; no code or DDL changes. The README's existing "DAG" claim is now backed by a binding decision, not aspirational language.

Closes #65.

The README and `LineageEdge` doc both claim "DAG". Until #42 closed
single-hop self-loops at the storage layer, nothing enforced that
claim. After #42, multi-hop cycles (A → B → A, …) still slip through.

Lineage is write-only today, so the cost of a cycle is invisible —
but every documented traversal (V-L1-D2 lineage queries,
impact-analysis, transitive provenance) needs acyclicity for
soundness.

ADR-0005 picks **enforce-at-write-time**:

* Refuse inserts where `(source, target)` would close a cycle.
* Implementation uses recursive CTEs (works on SQLite 3.8.3+ and
  Postgres) emitted at codegen time, so the cost stays in SQL.
* Concurrent inserts serialise on the cycle check (same V-L2-L1
  per-entity pattern).
* Storage-layer single-hop CHECK (#42) remains as the last line of
  defence.

Alternatives considered & rejected:

  - Rename to "Lineage Graph" and ship cycles. Rejected: every
    documented use of lineage needs acyclicity for soundness;
    write cost is small, read cost across every future consumer
    is not.
  - SQL triggers only. Rejected: triggers vary across backends;
    application-layer enforcement returns a structured error type
    carrying the cycle path.

The implementation lands in V-L1-G2 (to be filed). This ADR is
binding for that follow-up.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@hyperpolymath hyperpolymath merged commit 598998a into main May 14, 2026
16 of 18 checks passed
@hyperpolymath hyperpolymath deleted the docs/adr-0005-lineage-acyclicity branch May 14, 2026 20:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

V-L1-G1: lineage acyclicity — enforce or rename

1 participant