@ap/ Package Conventions
Scope: All repositories under the @ap/ npm scope.
Source: Build Plan - Phase 1.md, Tool Shed Master Plan.md
Decisions: reconciled with CGD-01 to CGD-04, CGD-18, CGD-19 on 2026-10-02. Agent-facing versions of these requirements live in Rules.
These are hard requirements — not guidelines. Every @ap/ package must satisfy all of them before being considered publish-ready.
The Non-Negotiables
| Rule | Requirement |
|---|---|
| Zero runtime dependencies | dependencies in package.json must be empty. devDependencies only. |
| 100% TypeScript | No any, no @ts-ignore. strict: true in tsconfig.json. |
| Immutable APIs | Never mutate caller inputs. Return new values. |
| Side-effect free | Every export must be independently tree-shakeable (no top-level side effects). |
| ESM-only | Ship ESM plus .d.ts in dist/, built by tsc. No CJS build, no tsup (CGD-03). |
| Node floor | engines.node is >=22.12; CI tests Node 22 and 24 (CGD-19). |
| npm | package-lock.json committed; install with npm ci --ignore-scripts (CGD-18). |
| Vitest tests | Test suite uses vitest.config.ts, not Jest. Agent tests in tests/unit/, owner acceptance tests in protected tests/acceptance/ (CGD-04). |
| AC coverage | Every Acceptance Criterion in the PRD maps to a named test case. |
Bundle Size Targets (gzipped)
| Package | Limit |
|---|---|
@ap/event-bus | < 1.5 KB |
@ap/schema | < 2 KB |
@ap/state-machine | < 2 KB |
@ap/diff | < 3 KB |
| All other @ap/ packages | < 5 KB unless PRD specifies otherwise |
Size is checked in CI via a bundle size action step. Exceeding the limit blocks the build.
Package Structure
{repo}/
├── .planning/PRD.md # Read before writing any code
├── .github/workflows/
│ ├── ci.yml # lint → typecheck → test → build → size check
│ └── release.yml # npm publish on tag
├── src/
│ ├── index.ts # Public API surface (named exports only)
│ └── {module}.ts # Internal modules
├── tests/
│ ├── unit/{module}.test.ts # Vitest tests, one file per module (agent may add)
│ └── acceptance/ # Owner-authored, protected
├── dist/ # Built ESM + .d.ts (gitignored)
├── package.json
├── package-lock.json
├── tsconfig.json # Typecheck: src + tests
├── tsconfig.build.json # Emit: src only
├── vitest.config.ts
├── eslint.config.js # Flat config (CGD-02)
├── .prettierrc.json
├── .gitignore
├── README.md
├── LICENSE # MIT
├── CONTRIBUTING.md
├── SECURITY.md
└── CLAUDE.md
package.json Template
{
"name": "@ap/{package-name}",
"version": "0.1.0",
"type": "module",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"files": ["dist"],
"sideEffects": false,
"engines": { "node": ">=22.12" },
"scripts": {
"build": "tsc -p tsconfig.build.json",
"test": "vitest run",
"test:watch": "vitest",
"lint": "eslint src tests",
"typecheck": "tsc --noEmit",
"size": "size-limit"
},
"devDependencies": {
"typescript": "<pinned>",
"vitest": "<pinned>",
"eslint": "<pinned>",
"typescript-eslint": "<pinned>",
"prettier": "<pinned>",
"@size-limit/preset-small-lib": "<pinned>",
"size-limit": "<pinned>"
}
}Versions are pinned exactly from the A7 archetype kernel lockfile, not chosen per repo. types must be the first condition in exports; TypeScript stops at the first matching condition.
Key rules:
namemust be scoped to@ap/type: "module"alwaysexportshas norequirecondition (ESM-only)files: ["dist"]— never shipsrc/ortests/- No
dependencieskey — or empty if present
tsconfig.json Requirements
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"rootDir": "src",
"outDir": "dist"
},
"include": ["src"]
}This is tsconfig.build.json. The root tsconfig.json extends it, sets "noEmit": true, drops rootDir, and includes tests for typechecking. NodeNext requires .js extensions on relative imports (CGD-01).
All strict flags enabled. noUncheckedIndexedAccess and exactOptionalPropertyTypes are required in addition to strict: true.
CI Pipeline (ci.yml)
name: CI
on: [push, pull_request]
jobs:
ci:
runs-on: ubuntu-latest
steps:
strategy:
matrix:
node: [22, 24]
steps:
# Pin every action to a full commit SHA in the real workflow.
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '${{ matrix.node }}', cache: 'npm' }
- run: npm ci --ignore-scripts
- run: npm run lint
- run: npm run typecheck
- run: npm test -- --coverage
- run: npm run build
- run: npm run sizeCI must pass green before any PR is merged. Size check step must be present.
Package Manager
Use npm (CGD-18). It matches the A7 governance checker, fixtures and CI commands, and ships with Node. Revisit only if the packages consolidate into a workspace monorepo.
# Install exactly what the lockfile records
npm ci --ignore-scripts
# Add a dev dependency (owner only; agents escalate with ESC-DEP)
npm install -D --save-exact vitest
# Publish (done by release.yml, not manually)
npm publish --access publicGovernance Scaffold
Every @ap/ package needs these files before it is considered public-ready:
| File | Content |
|---|---|
README.md | Install, API docs, examples, bundle size badge |
LICENSE | MIT |
CONTRIBUTING.md | Standard AP contribution guide |
SECURITY.md | Security contact and disclosure policy |
CLAUDE.md | AI context: package purpose, key design decisions, what NOT to change |
.github/workflows/ci.yml | See template above |
.github/workflows/release.yml | Triggered on v* tags; runs npm publish |
repo-score audits for all of these and grades A–F. Target: B or higher within 6 months of implementation.
Wave 1 @ap/ Packages (P0 — Build First)
| Package | Bundle Limit | Key Functionality |
|---|---|---|
@ap/schema | < 2 KB | Runtime type validation, Zod-like API |
@ap/diff | < 3 KB | Text + JSON structural diff, patch generation |
@ap/event-bus | < 1.5 KB | Typed pub/sub, wildcards, replay |
@ap/state-machine | < 2 KB | FSM with typed transitions, guards, history |
These four must be implemented before any other repo in the ecosystem — repo-score, ap-cli, ci-monitor, VaultKeep, and acg-assessment all depend on them.