# relations-on-sx: Cross-domain relationship graph on Datalog rose-ash's internal `relations` service tracks parent/child and peer relationships *across* domains — a blog post's comments, a thread's replies, a product's variants, an order's line items, a resource's containment tree, a federated content's origin. The questions are graph questions: who are X's children? its ancestors? is A reachable from B? what's the chain that connects them? is there a cycle? That is recursive Datalog in one rule — the same bottom-up reachability `acl-on-sx` uses for group/resource inheritance. Decisions come with a **trace**: not just "yes, related," but the path that proves it. relations is an **internal-only** service (no public URL); other domains call it to resolve hierarchy and linkage. End-state: a Datalog-on-SX layer for typed relationship facts, with reachability, path explanation, cycle detection, and a federation extension for cross-instance links. Reuses `lib/datalog/` — does not reimplement the engine. ## Status (rolling) `bash lib/relations/conformance.sh` → **0/0** (not yet started) ## Ground rules - **Scope:** only `lib/relations/**` and `plans/relations-on-sx.md`. Do **not** edit `spec/`, `hosts/`, `shared/`, `lib/datalog/**`, or other `lib//`. You may **import** from `lib/datalog/` (public API in `lib/datalog/datalog.sx`); do not copy or modify Datalog. - **Shared-file issues** → "Blockers" with a minimal repro; do not fix here. - **SX files:** `sx-tree` MCP tools only; `sx_validate` after every edit. - **Architecture:** relationships are `rel(Src, Dst, Kind)` Datalog facts; reachability/ancestry are recursive rules; the proof tree is the connecting path; the lifecycle (assert/retract) is an SX layer over the db. Keep relations *content-agnostic* — a node is an opaque id string; domains own what ids mean. - **Shared with acl-sx:** both run on Datalog and both lean on the same recursive- reachability shape (`reach(X,Y) :- edge(X,Y).` / `reach(X,Y) :- edge(X,Z), reach(Z,Y).`). Watch for it; flag convergence in the Progress log, but **do not extract** — `plans/mod-on-sx.md` records why cross-subsystem extraction waits for the architecture integrator with all consumers in view. - **Commits:** one feature per commit. Keep Progress log updated and tick boxes. ## Architecture sketch ``` relate(src, dst, kind) query │ │ ▼ ▼ lib/relations/schema.sx lib/relations/engine.sx — rel(Src,Dst,Kind) facts — children/parents/ancestors/descendants — kind vocabulary — reachable?(A,B), cycle?(X) │ ▲ ▼ │ lib/relations/api.sx lib/relations/explain.sx — relate / unrelate — path(A,B): the connecting chain — registry over a live db (from the Datalog derivation) │ ▼ lib/relations/federation.sx — cross-instance links via fed-sx (replicated rel facts, peer-trust gated) ``` ## Phase 1 — Schema + direct relations - [ ] `lib/relations/schema.sx` — `rel(Src, Dst, Kind)` fact projection; a small kind vocabulary (`parent`, `member`, `reply`, `variant`, `origin`, …) kept open - [ ] `lib/relations/api.sx` — `(relations/relate src dst kind)` / `(unrelate …)` over a live Datalog db (assert/retract); `(children-of db node kind)`, `(parents-of db node kind)`, `(related db node kind)` - [ ] `lib/relations/tests/direct.sx` — assert/retract, direct children/parents, kind filtering, unknown node → empty - [ ] `lib/relations/conformance.sh` + scoreboard ## Phase 2 — Reachability + cycles - [ ] recursive reachability rules: `ancestors`, `descendants`, `reachable?(A,B)` (transitive closure over a kind, the acl inheritance shape) - [ ] `roots` / `leaves` (no parents / no children) for a kind - [ ] cycle detection: `cycle?(X)` ⇔ `reachable(X, X)`; `acyclic?(db, kind)` - [ ] `lib/relations/tests/reach.sx` — deep chains, diamonds, disconnected nodes, self-loops, multi-kind isolation ## Phase 3 — Typed relations + path explanation - [ ] multiple kinds coexisting; mixed-kind vs single-kind reachability - [ ] `lib/relations/explain.sx` — `(path db a b kind)` returns the connecting chain (the relationship equivalent of acl's proof tree), nil if unreachable - [ ] `(distance db a b kind)` (hops) + shortest-path selection - [ ] `lib/relations/tests/path.sx` — path correctness, shortest among many, no-path ## Phase 4 — Federation - [ ] cross-instance relationships — a peer asserts `rel(local, remote, kind)`; replicate rel facts via fed-sx (mock the transport in tests) - [ ] trust gating — a peer's link binds locally only under a local trust fact (mirror acl's non-transitive `trust`/gate-in-engine model; do NOT copy acl code, re-derive the shape) - [ ] revocation — retract a replicated link; reachability re-saturates - [ ] `lib/relations/tests/fed.sx` — federated reachability chains, trust gating, revocation ## Progress log (loop fills this in) ## Blockers (loop fills this in)