Skip to main content
Last Updated: 2026-05-28 Authoritative source: constitution.md §1 (Architecture) This page is the single-place answer to “can core X import from core Y?” — consolidated from constitution.md §1, .cursor/rules/integration-patterns.md, and ADR-010. If this page conflicts with the constitution, the constitution wins.

The two non-negotiable rules

  1. Domain cores depend only on Platform Foundation (PF). Never on other domain cores.
  2. Clinical (CL) is downstream only. No other core may import from or depend on CL types or services. CL consumes from PM, RH, and PF; it does not export to them.
If your branch attempts a forbidden import, npm run check-architecture fails locally and CI blocks the merge.

What goes where

The root path alias @/*./src/* is the root app only. Cross-package imports use package names (@encore-os/core-pm, @encore-os/platform).

Decision tree — “I need data from another core”


The three sanctioned cross-core patterns

1. Platform Integration Layer (PIL)

PF exposes thin adapters that wrap a domain core’s public surface. Other cores import from PF, never from the core. The PIL adapter is the only place that knows the shape of the foreign core. See PLATFORM_INTEGRATION_LAYERS.

2. Event bus

Cores fire and listen for events through PF’s event bus. Event names are typed in KnownEventName; contracts live in EVENT_CONTRACTS. For new events, follow encore-spec-research flow.

3. API contracts

A core publishes an explicit API surface other cores may call (via PIL). Contracts live in API_CONTRACTS. Versioning and breaking-change rules are in api-versioning-guide.

The encounter as the PM↔CL contract

The pm_encounters table is the canonical link between Practice Management (PM) and Clinical (CL). It is the only encounter entity — do not invent parallel cl_encounters, appt_encounters, etc. Both cores read the encounter via the PF clinical primitives layer. See CL-PM-ENCOUNTER-TO-BILLING for the full lifecycle.

Worked examples

A) FA needs the patient’s primary insurance to post a charge

  • ❌ Wrong: import { fetchInsurance } from '@encore-os/core-pm'
  • ✅ Right: import { getPrimaryInsurance } from '@encore-os/platform/billing' — PIL adapter that wraps PM’s published API.

B) HR needs to know when a credential expires so it can fire a workflow

  • ❌ Wrong: HR polls PM tables.
  • ✅ Right: PM publishes pm.credential.expired on the event bus; HR subscribes. Contract in EVENT_CONTRACTS.

C) CL needs to display the patient’s appointment slot

  • ❌ Wrong: CL imports Appointment from PM.
  • ✅ Right: PM exports an EncounterSummary clinical primitive into @/platform/clinical; CL imports from there.

D) A new core (say “Pharmacy”) wants to read clinical notes

  • ❌ Wrong: Pharmacy imports CL types.
  • ✅ Right: CL is downstream only. Pharmacy reads through @/platform/clinical (which CL has populated) or via an explicit CL→PIL adapter. CL must not depend on Pharmacy.

How this is enforced


When you think you need an exception

You don’t. Open a discussion in #engineering and bring the spec — the answer is almost always one of the three sanctioned patterns. Genuine architectural changes go through an ADR (see architecture/decisions/).