> ## 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-016: Workspace packages overlay (no source moves)

> Status: Accepted Date: 2026-04-22 Participants: Platform Architecture (cloud agent draft, Jeremy Bloom approval) Related: ADR-008, ADR-010, constitution §1

**Status:** Accepted
**Date:** 2026-04-22
**Participants:** Platform Architecture (cloud agent draft, Jeremy Bloom approval)
**Related:** [`ADR-008`](ADR-008-vite-spa-vs-ssr.md), [`ADR-010`](ADR-010-core-pf-dependency-boundary.md), constitution §1

***

## Context

The repo is a single Vite SPA with 12 cores, \~2,100 platform files, \~8,300 source files in `src/` (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.

The original recommendation also proposed **moving** every `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}/**` to `packages/core-{core}/src/**`.
* Rewrite all `@/cores/{core}/...` imports.
* Configure each package with `tsconfig.json composite: true` for per-package incremental typecheck.
* Land 12+ separate PRs, one per core.

**Pros:** Strongest possible incremental-typecheck wins (each core compiles in isolation; warm typecheck on an untouched core is \~1 s). Clearest physical separation.

**Cons:** Massive churn (every IDE, code-search shortcut, and `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.json` files 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.ts` enforces the dependency graph.
* The existing `check-architecture.js` continues to enforce import-statement-level boundaries.

**Pros:** \~14 files added (13 `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-boundaries` script — closes the loophole where a developer could write `import {} from '@/cores/cl/...'` inside `src/cores/hr/` without triggering a metadata violation (the import-scan still catches it; now there are two layers).
* Turborepo `--filter` works against the workspace graph, enabling per-PR cache scoping.
* CL's "downstream-only" status is now machine-readable (`encoreos.downstreamOnly: true` in `packages/core-cl/package.json`) and enforced by the audit, not just documented in prose.
* Each core's `package.json` becomes the right place to declare its locally-needed npm dependencies in the future, slowing the unbounded growth of the root `package.json`.
* Reversal is trivial: `rm -rf packages/{platform,shared,core-*}` + revert root `workspaces` field.

### Negative

* Two-step lookup for "what is HR's source directory" — `packages/core-hr/package.json` → `encoreos.sourceDirectory: "../../src/cores/hr"`. Mitigated by the `WORKSPACE_PACKAGES.md` doc and the consistent `encoreos.*` 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 under `packages/` must remember to update the workspaces glob if they want it included. Mitigated by clear comment in the root `package.json` and the doc.

### Mitigations

* Audit script (`audit:package-boundaries`) is fast enough to run as a CI gate; should be added to `.github/workflows/build.yml` in a follow-up PR alongside the existing `check-architecture:ci` step.
* 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`](../../development/WORKSPACE_PACKAGES.md) — operational guide
* `docs/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`](../../../scripts/audit/audit-package-boundaries.ts)
* Constitution §1 (architecture and module boundaries)
* [`ADR-008`](ADR-008-vite-spa-vs-ssr.md) — preserved (Vite SPA stands)
* [`ADR-010`](ADR-010-core-pf-dependency-boundary.md) — this ADR provides a structural enforcement layer for ADR-010's policy
