ADR 0028 — Package-First Repository Structure
ADR 0028 — Package-First Repository Structure
Status: Accepted Date: 2026-09-08
Context
Holos is a pnpm + Nx monorepo. Reusable code already lives under packages/ as
workspace packages, but non-code assets that are also reusable, versioned, and
distributable have been landing as bare top-level directories instead — most
recently the CALM architecture models under architecture/ (ADR 0027 §1).
A bare directory at the repo root has none of the properties that make a package useful:
- No version. There is no way to say “the architecture as of 1.3.0” or to tag a breaking change to the model.
- Not distributable. Another repo, a docs site, or a CI job cannot depend on it without copy-pasting files or pinning a git SHA.
- No dependency contract. It cannot declare what it needs (
@finos/calm-cli, a schema version) or what depends on it. - Invisible to the toolchain. Nx does not see it as a project, so it has no
cached targets, no
affectedwiring, noqa:*targets, no place for a colocated validation script. - Inconsistent. Contributors have to learn, per directory, whether tasks live
in a
project.json, a root script, or nowhere.
The monorepo already has the machinery to give any coherent unit of work all of
those properties for free: a package.json + project.json pair.
Decision
1. If something can be a package, it is a package
Any self-contained, independently-versionable, potentially-reusable unit of the
repository is a workspace package under packages/ (or apps/ / cli/ when it
is a runnable application or command-line tool). This includes non-TypeScript
units: architecture models, schemas, agent skills, static datasets, doc bundles.
A unit qualifies when all of these hold:
- It has an identity someone might refer to by name and version.
- It has a boundary — you can say what is in it and what is not.
- It could plausibly be consumed by more than one other thing (another package, a CI job, an external repo, a published site), now or later.
- It has, or will have, its own rules/scripts/checks that belong next to it rather than in a shared root pile.
If a unit fails those tests — it is genuinely one-repo, one-consumer, and
throwaway — it stays a plain directory. Prose-only documentation under docs/
and communication/ is explicitly out of scope; those are human-read trees, not
distributable artefacts.
2. What “being a package” buys, and is therefore required
Every package created under this rule MUST have:
| Requirement | Why |
|---|---|
package.json with a scoped name (@alistigo/<name>) and a semver version | Identity + versioning + workspace:* referenceability |
project.json as the single source of truth for Nx targets (per CLAUDE.md) | Cached tasks, affected, dependsOn, colocated qa:* |
A README.md describing what it is and how to consume it | Discoverability |
LICENSE (copied from repo root) | Distribution hygiene |
An explicit files array + publishConfig when it is meant to be published | Controls the published surface |
| Its own validation / lint / build scripts colocated in the package | Rules travel with the thing they govern |
A package that is not meant for npm sets "private": true; the default for
genuinely reusable units is publishable.
3. Consequence for the CALM architecture model
architecture/ at the repo root moves to packages/architecture/, published as
@alistigo/architecture. It gains:
- a
version(starts at0.1.0) project.jsonwith aqa:arch-calmtarget (wasscripts/validate-architecture.sh- a root
package.jsonscript)
- a root
- a colocated
scripts/validate.sh - its README, patterns, and systems unchanged in content, moved wholesale
This amends ADR 0027 §1 (“stored under architecture/ at the repo root”).
The path is now packages/architecture/; everything else in ADR 0027 stands.
New structure:
packages/architecture/├── package.json # @alistigo/architecture├── project.json # Nx targets: qa:arch-calm, qa:lint, clean├── README.md├── scripts/│ └── validate.sh # was scripts/validate-architecture.sh├── patterns/│ ├── ddd-hexagonal.pattern.json│ └── event-sourcing-cqrs.pattern.json└── systems/ ├── alistigo-platform.arch.json ├── list-artifact-ddd.arch.json ├── monorepo-toolchain.arch.json ├── monorepo-packages.arch.json └── alistigo-artifact-concept.arch.json4. Migration procedure (applies to this and future promotions)
git mv <dir> packages/<name>— move tracked files, preserving history.- Add
package.json(scoped name,version,files,publishConfigor"private": true),project.json(targets), copyLICENSE. - Move any root-level scripts that only served this dir into the package; make their paths package-relative.
- Repoint root
package.jsonscripts /nx run-manyaggregates at the new Nx target (nx run <name>:<target>), and delete the superseded root script. - Grep the repo for the old path (docs, ADRs, agent commands/skills, CI) and update every reference.
pnpm installto relink the workspace; run the package’s own checks.
Consequences
Positive:
- One consistent answer to “where do tasks live” — always
project.json. - The architecture model is versionable and
workspace:*-referenceable; a futurecli/calm-to-archifyor docs build can depend on@alistigo/architecture. - Nx
affectednow covers architecture changes;pnpm qarunsarchitecture:qa:arch-calmautomatically. - Sets the precedent for promoting agent skills, schemas, and datasets the same way.
Negative / trade-offs:
- More
package.json/project.jsonboilerplate for small units. - A publishable
@alistigo/architectureenters thenx releaseset — a new version can be published on release even though consumers are internal today. - Every promotion is a path change with a repo-wide reference sweep (step 5).
Neutral:
docs/andcommunication/stay as plain trees — this rule is about distributable artefacts, not all files.- No change to the CALM decision itself (ADR 0027); only where the files sit.
References
- ADR 0027 — Architecture as Code: CALM (amended by this ADR, §1 path)
- ADR 0026 — document format as a package standard (same package-first instinct)
- ADR 0015 — agent skills standard (candidate for future promotion)
CLAUDE.md→ “Nx Task Convention” —project.jsonis the single source of truth for tasksscripts/new-package.sh— package scaffold