Skip to content

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:

  1. 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.
  2. 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.
  3. 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 ChangeTracker consumers 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

  1. Stay unidirectional. Leave ChangeTracker as-is. Misses the upstream-change use case entirely.
  2. Bidirectional with single global LWW policy. Simple but violates domain-owner authority for regulated verticals.
  3. Bidirectional with per-namespace conflict policy. Each NamespaceConfig declares one of LWW, MANUAL_MERGE, or DOMAIN_OWNER_AUTHORITY. BidirectionalSync extends ChangeTracker and resolves conflicts per the namespace's declared policy. Default is LWW.
  4. 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) — return SyncHealth (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 a domain_owner (e.g., hl7.org for FHIR, edmcouncil.org for 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/health gives AOME + AURORA a single pollable endpoint for health visibility.
  • Back-compat. Existing ChangeTracker consumers continue to work; BidirectionalSync is 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_AUTHORITY requires 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_MERGE queue lifecycle.
  • Admin API endpoint GET /api/v1/federation/health live at the admin service (port 8900).
  • DAR persona (v1.5.0) shipped with cross-references to federation events.

References

  • src/fcc/federation/bidirectional.py — implementation
  • src/fcc/federation/tracker.pyChangeTracker base (v1.3.x)
  • src/fcc/federation/namespace.pyNamespaceConfig + registry
  • tests/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_MERGE queue; VDS (Vocabulary Drift Sentinel, from v1.4.1) — raises governance findings when drift crosses thresholds