Status: Accepted Date: 2026-04-22 Participants: Platform Architecture (cloud agent draft, Jeremy Bloom approval) Related:Documentation Index
Fetch the complete documentation index at: https://docs.encoreos.io/llms.txt
Use this file to discover all available pages before exploring further.
ADR-008, ADR-010, constitution §1
Context
The repo is a single Vite SPA with 12 cores, ~2,100 platform files, ~8,300 source files insrc/ (per the dev-velocity baseline captured in PR #138). The recommendations doc (docs/recommendations/DEV_VELOCITY_AND_ARCHITECTURE_PLAN_2026-04-22.md, PR #137, § 5.4) calls for converting cores into real workspace packages so:
- A PR touching one core invalidates Turborepo cache only for that core.
- Architecture boundaries (
cores depend only on PF,CL is downstream) become enforced by the workspace dependency graph instead of a custom audit script. - Future Phase 3 microfrontends have a clean per-zone entry point.
src/cores/{core}/** file to packages/core-{core}/src/** and rewriting every @/cores/... import. That delivers the strongest TS-project-references win but at very high cost (~8,300 files moved, ~10,000+ imports rewritten, 12+ PRs).
Options Considered
Option A: Full physical migration
- Move every
src/cores/{core}/**topackages/core-{core}/src/**. - Rewrite all
@/cores/{core}/...imports. - Configure each package with
tsconfig.json composite: truefor per-package incremental typecheck. - Land 12+ separate PRs, one per core.
git blame baseline shifts). High risk of subtle regressions during file moves. Requires a long stop-the-world freeze on prod. Reversal would be even more painful than the migration itself.
Option B (chosen): Workspace overlay, no source moves
- Add
packages/{platform,shared,core-*}/package.jsonfiles declaring tier, source directory, and dependency surface. - Source files stay in
src/unchanged. - Existing path aliases (
@/cores/hr/*→src/cores/hr/*) keep working. - A new
audit-package-boundaries.tsenforces the dependency graph. - The existing
check-architecture.jscontinues to enforce import-statement-level boundaries.
package.json + the audit script + docs), zero source files moved, zero imports rewritten. Reversible by deleting packages/. Captures all the workspace-graph wins (Turborepo --filter, npm ls --workspaces, structural boundary audit, foundation for future codegen). No risk to live features.
Cons: Does not enable per-package tsc --build (we still have one TS project; warm typecheck remains ~8 s rather than ~1 s per untouched core). Two sources of truth for “where does HR live” (the src/cores/hr/ directory and the packages/core-hr/ package metadata) — but the linkage is one-line and explicit.
Option C: Hybrid — cores stay in src/, but each has its own tsconfig.json with TS project references
Considered briefly. Rejected because TS project references with composite: true require declaration-file emission, which conflicts with the app’s noEmit: true. Workable, but adds non-trivial config that needs careful per-build coordination. Not worth the complexity until Option B’s ceiling is hit.
Decision
Adopt Option B. Phase 2 ships the overlay packages with zero source moves. A future Phase 2.B (per-core PRs) can perform the physical migration if/when measurements show Option B’s ceiling has been reached.Consequences
Positive
- Workspace dependency graph enforced by npm + the new
audit-package-boundariesscript — closes the loophole where a developer could writeimport {} from '@/cores/cl/...'insidesrc/cores/hr/without triggering a metadata violation (the import-scan still catches it; now there are two layers). - Turborepo
--filterworks against the workspace graph, enabling per-PR cache scoping. - CL’s “downstream-only” status is now machine-readable (
encoreos.downstreamOnly: trueinpackages/core-cl/package.json) and enforced by the audit, not just documented in prose. - Each core’s
package.jsonbecomes the right place to declare its locally-needed npm dependencies in the future, slowing the unbounded growth of the rootpackage.json. - Reversal is trivial:
rm -rf packages/{platform,shared,core-*}+ revert rootworkspacesfield.
Negative
- Two-step lookup for “what is HR’s source directory” —
packages/core-hr/package.json→encoreos.sourceDirectory: "../../src/cores/hr". Mitigated by theWORKSPACE_PACKAGES.mddoc and the consistentencoreos.*field naming. - Per-package incremental TS typecheck is not unlocked — that requires the full physical migration (Phase 2.B).
- The
packages/docs/directory (Docusaurus site) is intentionally excluded from the workspaces glob, which means a future contributor adding a new package underpackages/must remember to update the workspaces glob if they want it included. Mitigated by clear comment in the rootpackage.jsonand the doc.
Mitigations
- Audit script (
audit:package-boundaries) is fast enough to run as a CI gate; should be added to.github/workflows/build.ymlin a follow-up PR alongside the existingcheck-architecture:cistep. - The script’s negative-test path is documented and verified in the PR description (a deliberately-bad core dep correctly produces exit 1; restoration returns to exit 0).
Related Documents
docs/development/WORKSPACE_PACKAGES.md— operational guidedocs/recommendations/DEV_VELOCITY_AND_ARCHITECTURE_PLAN_2026-04-22.md§ 5.4 — original Phase-2 plan (lands via PR #137)scripts/audit/audit-package-boundaries.ts- Constitution §1 (architecture and module boundaries)
ADR-008— preserved (Vite SPA stands)ADR-010— this ADR provides a structural enforcement layer for ADR-010’s policy