12 KiB
Suaveplan Genesis — Project Conventions
Authoritative project-wide context for all OpenSpec changes on the genesis branch. Every proposal inherits these rules.
Mission
Suaveplan is a monorepo of composable @suaveplan/* packages. The genesis branch is a bottom-up rebuild: every package lands fresh at enterprise quality, starting from the leaves. Designs are informed by the matching enterprise/*.md specification in the main worktree (/mnt/s/web/suaveplan/enterprise/). Packages start at version 0.1.0; the Verdaccio private registry is wiped and repopulated from genesis output.
No package on genesis is "enterprise-ready" until its acceptance criteria (coverage, docs, structured errors, Zod schemas, observability, property tests, benchmarks, security review, full export surface) are all green.
Stack
| Area | Technology |
|---|---|
| Runtime | Bun ≥ 1.3.0 (TypeScript native, ESM-first) |
| Monorepo | Turborepo + Bun workspaces (<category>/<domain>/<pkg>), catalogs, overrides |
| Registry | Verdaccio at https://npmjs.fq.io/ (auth via NPM_TOKEN env var) |
| Lint/Format | Biome (strict base; no per-package overrides unless justified) |
| Unit/Integration Tests | Vitest (istanbul coverage) |
| E2E Tests | Playwright |
| Bundling | Vite, tsup, Bun.build — never tsc directly |
| Property tests | fast-check |
| Benchmarks | mitata |
| Runtime validation | Zod (external catalog dep, not via @suaveplan/schema-registry) |
| Observability | @opentelemetry/api (external) until @suaveplan/opentelemetry ships |
Package taxonomy
Genesis starts empty. Packages land under packages/<category>/<plugin>/<name>, where <category> is one of 18 super-categories (foundation, persistence, networking, communication, api-stack, identity, security-stack, observability, media, ui, threejs, localization, geospatial, money, commerce, testkit, devtools, gaming) and <plugin> is a domain folder defined in FOLDERS.md. Tier (universal / browser / react / r3f / server / python) is inferred per-package from package.json — it is never encoded in the directory path.
Packages live at packages/<category>/<plugin>/<pkg> (e.g. packages/security-stack/crypto/crypto, packages/foundation/core/error). Workspace glob: packages/*/*/*. Category → plugin → package map lives in scripts/folder-reorg/assignment.yml and is summarised in MANIFEST.md at the repo root. Run bun scripts/folder-reorg/generate-manifest.ts to refresh MANIFEST.md after any package add, remove, or category move.
Dependency-layer order
Build packages in dependency-order waves. A package cannot land until every @suaveplan/* workspace dep it needs is already enterprise on genesis:
- Layer 0 — testing-infrastructure packages (
@suaveplan/testing,testing-dom,testing-e2e). These are forbidden from depending on ANY@suaveplan/*package — see "Non-negotiable rules" below. - Layer 1 — true leaves with no workspace deps (
error,datetime, candidateopentelemetryadapter). - Layer 2+ — packages that depend on Layer 1 (
schema-registry,utils, etc.).
Non-negotiable rules
- Never run
tscdirectly. Use Vite/tsup viabunx turbo build. Bun runs.tsnatively. - No mocks. Real services only.
vi.mockis unreliable in Bun ESM — use dependency injection. Novi.mock/jest.mockin production tests. - Bun
overrideswith exact versions. Catalogs for external deps (catalog:<key>),workspace:^for internal. Never use bare version strings (e.g."^1.2.3") inpackage.json— always reference a catalog entry. If a catalog key does not exist for a dep, add it to the rootpackage.jsoncatalogblock first. Note: some Suaveplan skills sayworkspace:*— genesis usesworkspace:^exclusively. - Never re-export external deps for convenience — import directly from source.
- Never auto-commit or push without user approval. Never
--no-verify. - Always use absolute paths in tool calls. Read files before editing.
- No stubs, no TODOs, no placeholder implementations in shipped code. Enterprise-grade only.
- Professional honesty — no marketing language, no fabricated metrics.
- Testing-infrastructure packages (
@suaveplan/testing,testing-dom,testing-e2e) MUST NOT depend on any@suaveplan/*workspace package. External catalog deps only. Circular-use-of-testing otherwise. - Preserve the full public surface during rewrites. Trimming is allowed only when code is duplicated elsewhere in-repo or can be replaced by an existing
@suaveplan/*library. Never trim on "probably unused" grounds. - Subpath exports per feature module. Every feature directory under
src/must have its own entry in theexportsmap inpackage.json(e.g."./retry/retry","./circuit-breaker/circuit-breaker"). A single"."barrel export is not sufficient — tree-shaking requires granular subpaths. @vitest/coverage-istanbulmust be indevDependencies. Usecatalog:testingor equivalent. Theprovider: 'istanbul'setting silently falls back to v8 if the package is missing.- Clean workspace on task completion. Remove any scratch files, temporary outputs, or debugging artifacts before declaring a task done.
Pre-publish gate (non-negotiable)
Before any bun scripts/publish.ts --package @suaveplan/<pkg>:
bunx turbo lint typecheck test --filter=@suaveplan/<pkg>MUST be green.bunx turbo test:coverage --filter=@suaveplan/<pkg>MUST meet the coverage threshold for the package layer (see Quality gates below).- A full-package
turbo testrun is required — a single-filevitest runis not a substitute.
Never publish on red.
Quality gates (per package)
| Gate | Threshold |
|---|---|
| Test coverage — every package, no exceptions | 100% statement + branch + function + line (istanbul). No "foundation only" carve-out, no degraded-situation floor. Pre-publish gate hard-fails below 100%. |
| Docs coverage | ≥ 90% (every public export has a sibling .md) |
| Code file size | ~250 lines soft / 300 lines hard |
| Test file size | 500 lines max |
| Test extension | .test.ts (Vitest) / .spec.ts (Playwright E2E) |
it() / test() timeout |
{ timeout: 5000 } sync, 30000 async |
| Vitest coverage provider | istanbul — requires @vitest/coverage-istanbul in devDependencies |
| React component | Every component has .stories.tsx (default + playground + variants) |
| Public types | Modeled by Zod schemas exported from *.schemas.ts |
| Error shape | Extends a structured error class with { code, cause, context } |
| Subpath exports | One exports entry per feature module for tree-shaking |
sideEffects |
false (or explicit array); entry points must not import with side effects |
Package enterprise checklist
Every package is "enterprise-done" only when:
README.mdexists at the package root (not insidesrc/) — one-paragraph description, install, quick-start, Modules section linking to every co-located.mdinsrc/- Every
.ts/.tsximplementation file has a sibling.md(≥200 words, simple + advanced examples, API reference, implementation notes) — excludes.test.ts,.spec.ts,.types.tsinside feature dirs,.stories.tsx, barrelindex.ts,constants/index.ts - Every public function has Zod input/output schemas in a sibling
*.schemas.ts - Every thrown error extends the package's structured error class with a stable
UTILS.<DOMAIN>.<REASON>-style code (or equivalent namespace) and{ cause, context }fields - Every function reading time / randomness / iterating accepts an optional
{ clock?, rng?, signal? }options parameter; zero-config defaults preserved AbortSignalchecks on every iterative helper at bounded intervals (≤ 256 items or ≤ 1 KB)- OTEL instrumentation via
@opentelemetry/api— counters, histograms, spans on public entrypoints (no-op when no exporter registered) - fast-check property tests for pure functions covering round-trip, idempotency, associativity, boundary invariants
- mitata benchmarks with baseline + CI regression gate (>10% fails)
- Full public surface preserved relative to the design spec
- Zero
any,ts-ignore,ts-expect-error,TODO,FIXME,@deprecated,.skip,.onlyinsrc/ sideEffects: falseholds; entry point imports verified to have no side effects; tree-shake test included- Subpath exports: one
exportsentry per feature module inpackage.json @vitest/coverage-istanbulindevDependencies(catalog ref, not bare version)- React version conflict (if browser/react package): verify single React version with
find . -path "*/node_modules/react/package.json"— use exact overrides if multiple found - Workspace clean: no scratch files, temp outputs, or debug artifacts remain
- OpenSpec change validates and is archived to
openspec/archive/<yyyy-mm-dd>-<change-id>/
Repo ↔ enterprise spec mapping
Every enterprise-level package rewrite starts by:
- Reading the matching
enterprise/<package>.mdon the main branch (/mnt/s/web/suaveplan/enterprise/<pkg>.md). - Reading the current implementation on the main branch for shape-preservation (export inventory).
- Creating
openspec/changes/enterprise-<pkg>/withproposal.md,design.md,specs/<capability>/spec.md, andtasks.md— adapting each enterprise-doc clause to the constraints above (especially rule #9: no@suaveplan/*workspace deps in testing-infrastructure packages).
Vitest / WSL2 quirks
- Use
createTestConfig()from@suaveplan/testing/configonce@suaveplan/testinglands. - Use
forkspool.vmThreadsandthreadsboth fail in Bun on WSL2. fileParallelism: falserequired for large browser test suites.- SurrealDB connection errors use
'ConnectionRefused'(not'ECONNREFUSED').
Package lifecycle
- New package: land via an
enterprise-<pkg>OpenSpec change. Package starts at version0.1.0. - Edit: no version bump during genesis; version stays
0.1.0until graduation. - Publish:
bun scripts/publish.ts --package @suaveplan/<pkg>after pre-publish gate passes. - Wipe + republish: Verdaccio is wiped once genesis is sufficiently complete; republish happens as part of the cutover.
Commonly-used commands
# Single package
bunx turbo test --filter=@suaveplan/<pkg>
bunx turbo test:coverage --filter=@suaveplan/<pkg>
bunx turbo lint typecheck test --filter=@suaveplan/<pkg> # pre-publish gate
# Workspace-wide
bunx turbo lint typecheck
bun run gate # all packages pre-publish gate
# Turborepo advanced filtering
bunx turbo test --filter=@suaveplan/<pkg>... # pkg + everything it depends on
bunx turbo test --filter=...@suaveplan/<pkg> # pkg + everything that depends on it
bunx turbo test --filter=...[main] # packages changed since main branch
bunx turbo test --filter=...[HEAD^1] # packages changed in last commit
bunx turbo test --filter=@suaveplan/{pkg-a,pkg-b} # multiple packages (OR)
bunx turbo test --filter=!@suaveplan/<pkg> # exclude one package
# Publish
bun scripts/publish.ts --package @suaveplan/<pkg> # publish one package
# Dependency management — nuclear clean install (use after overrides changes)
rm -rf node_modules bun.lockb && find . -type d -name "node_modules" -exec rm -rf {} + && bun install --force
# Profiling (Bun ≥ 1.3.7 — outputs Markdown for LLM analysis)
bun --cpu-prof-md run <script>
bun --heap-prof-md run <script>