ADR-013: Bidirectional Federation with Per-Namespace Conflict Policy¶
Date: 2026-04-22 Status: Accepted Decision-makers: FCC core maintainers · VDS (Vocabulary Drift Sentinel) persona · DAR (Decision Archaeologist) persona Consulted: CONSTEL / PRISM — federation v2 node · Research Center — namespace registry authority (ADR-005) Informed: All federation consumers; all 11 ecosystem namespaces
Context and Problem Statement¶
ADR-005 established the IRI-based NamespaceRegistry for cross-project
entity resolution across the 11 ecosystems. v1.3.x shipped
ChangeTracker under src/fcc/federation/tracker.py to track
unidirectional change propagation: when FCC's vocabulary changes,
downstream projects are notified via the plugin system.
By v1.4.x, three problems had surfaced:
- Changes flow upstream too. Constellation verticals (Ophiuchus, Libra, etc.) maintain their own industry-standard ontologies (FHIR Genomics, FIBO, IEC 61970 CIM). When those standards update, the change needs to flow into FCC's vocabulary mappings, not just out of them.
- Conflict resolution is domain-dependent. A change to a medical vocabulary (Ophiuchus/Serpens) must respect the medical-standard authority's governance — FCC cannot arbitrarily win a merge. But a change to an FCC-internal persona namespace should follow LWW. One-size-fits-all conflict resolution doesn't work.
- No health visibility. When a federation sync gets stuck or a namespace drifts, there's no endpoint for observability tooling (AOME auditor, AURORA dashboards) to poll.
The problem was how to generalise ChangeTracker into a bidirectional
sync with per-namespace conflict policy, without breaking the
existing unidirectional consumers.
Decision Drivers¶
- Backward compatibility. v1.3.x
ChangeTrackerconsumers must continue to work; bidirectional is additive. - Domain-owner authority. Medical / financial / government standards have authoritative owners; FCC cannot override their merge decisions.
- Deterministic default. When no domain-owner authority is registered, a deterministic LWW must be the default so automated syncs don't stall.
- Manual-merge escape hatch. When automation cannot resolve a conflict safely, the policy must support queueing for human review rather than silent data loss.
- Observability. Federation health must be pollable from admin APIs.
Considered Options¶
- Stay unidirectional. Leave
ChangeTrackeras-is. Misses the upstream-change use case entirely. - Bidirectional with single global LWW policy. Simple but violates domain-owner authority for regulated verticals.
- Bidirectional with per-namespace conflict policy. Each
NamespaceConfigdeclares one ofLWW,MANUAL_MERGE, orDOMAIN_OWNER_AUTHORITY.BidirectionalSyncextendsChangeTrackerand resolves conflicts per the namespace's declared policy. Default isLWW. - Event-sourced federation log. Full CRDT-style log with replay. Overkill for the ecosystem coordination scenario; also duplicates Pillar D (ADR-012) at the wrong layer.
Decision¶
We adopt option 3: BidirectionalSync with per-namespace
conflict policy, shipped in v1.5.0 Pillar E.
Implementation: src/fcc/federation/bidirectional.py (~400 LoC).
BidirectionalSync extends ChangeTracker and exposes:
sync_push(namespace, changes)— propagate local changes upstream.sync_pull(namespace)— pull remote changes into the local registry.sync(namespace)— bidirectional sync with conflict resolution per the namespace's policy.get_health(namespace)— returnSyncHealth(timestamp of last successful sync, pending conflicts count, policy in effect, latency-p95).
Conflict policies on NamespaceConfig:
LWW(default). Last-writer-wins by logical timestamp; ties broken by namespace-IRI lexicographic ordering. Deterministic. Used for FCC-internal namespaces and for vocabularies without an external standards owner.MANUAL_MERGE. Conflicts are queued to a pending-merge list rather than auto-resolved. VDS (Vocabulary Drift Sentinel) raises a governance finding; DAR (Decision Archaeologist) reconstructs the rationale. Used when data-loss tolerance is zero and human review is required.DOMAIN_OWNER_AUTHORITY. The namespace declares adomain_owner(e.g.,hl7.orgfor FHIR,edmcouncil.orgfor FIBO). The domain-owner's version always wins the merge; FCC's local copy is overwritten. Used for regulated-vertical standards where FCC is a consumer of an external authoritative registry.
Federation health dashboard. New admin API endpoint
GET /api/v1/federation/health returns a JSON object with per-namespace
health rows: {namespace, last_sync_at, pending_conflicts,
policy, p95_latency_ms}. Polled by AOME admin UI + AURORA Sky
Parlour dashboards.
DAR persona integration. When MANUAL_MERGE queues a conflict,
DAR (Decision Archaeologist, v1.5.0) reconstructs the ADR rationale
from the affected ModelHistoryEvent stream — so the human reviewer
has full context on why each side's change was made.
Consequences¶
Positive¶
- Upstream-change propagation. Vertical verticals can push standards updates (FHIR minor, FIBO minor) into FCC's vocabulary mappings without cross-repo PR friction.
- Domain-owner authority preserved. Regulated-vertical standards (medical, financial, government) always win merges against their authoritative owner's version.
- Deterministic default. LWW keeps automated syncs flowing when no domain-owner authority is declared.
- Manual-merge escape hatch. Governance-critical namespaces can require human review without stalling the whole ecosystem.
- Observability.
GET /api/v1/federation/healthgives AOME + AURORA a single pollable endpoint for health visibility. - Back-compat. Existing
ChangeTrackerconsumers continue to work;BidirectionalSyncis additive.
Negative¶
- Three policies to document per namespace. Namespace configuration becomes slightly more complex.
- Manual-merge queue growth. If reviewers fall behind, conflicts accumulate. Mitigated by DAR rationale reconstruction to speed review + health dashboard for visibility.
- Domain-owner registry dependency. Declaring
DOMAIN_OWNER_AUTHORITYrequires a stable domain-owner identifier (we reuse IRI prefix from ADR-005). External authorities that rebrand or merge may require namespace-level reconfiguration.
Confirmation¶
tests/test_federation_bidirectional.py(+20 tests) — 100% line- branch coverage; exercises all three policies, sync push/pull,
health endpoint, and the
MANUAL_MERGEqueue lifecycle. - Admin API endpoint
GET /api/v1/federation/healthlive at the admin service (port 8900). - DAR persona (v1.5.0) shipped with cross-references to federation events.
References¶
src/fcc/federation/bidirectional.py— implementationsrc/fcc/federation/tracker.py—ChangeTrackerbase (v1.3.x)src/fcc/federation/namespace.py—NamespaceConfig+ registrytests/test_federation_bidirectional.py— 20 tests- ADR-005 — IRI-based namespace registry (foundation)
- ADR-010 — six-pillars context (Pillar E)
- CHANGELOG
[1.5.0]§Pillar E — Advanced federation - New personas: DAR (Decision Archaeologist) — ADR rationale
reconstruction for
MANUAL_MERGEqueue; VDS (Vocabulary Drift Sentinel, from v1.4.1) — raises governance findings when drift crosses thresholds