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
- Domain cores depend only on Platform Foundation (PF). Never on other domain cores.
- 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.
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 inKnownEventName; 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 inapi-versioning-guide.
The encounter as the PM↔CL contract
Thepm_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.expiredon the event bus; HR subscribes. Contract in EVENT_CONTRACTS.
C) CL needs to display the patient’s appointment slot
- ❌ Wrong: CL imports
Appointmentfrom PM. - ✅ Right: PM exports an
EncounterSummaryclinical 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 (seearchitecture/decisions/).