Files
suaveplan/openspec/project.md
2026-05-27 23:29:53 +01:00

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:

  1. 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.
  2. Layer 1 — true leaves with no workspace deps (error, datetime, candidate opentelemetry adapter).
  3. Layer 2+ — packages that depend on Layer 1 (schema-registry, utils, etc.).

Non-negotiable rules

  1. Never run tsc directly. Use Vite/tsup via bunx turbo build. Bun runs .ts natively.
  2. No mocks. Real services only. vi.mock is unreliable in Bun ESM — use dependency injection. No vi.mock / jest.mock in production tests.
  3. Bun overrides with exact versions. Catalogs for external deps (catalog:<key>), workspace:^ for internal. Never use bare version strings (e.g. "^1.2.3") in package.json — always reference a catalog entry. If a catalog key does not exist for a dep, add it to the root package.json catalog block first. Note: some Suaveplan skills say workspace:* — genesis uses workspace:^ exclusively.
  4. Never re-export external deps for convenience — import directly from source.
  5. Never auto-commit or push without user approval. Never --no-verify.
  6. Always use absolute paths in tool calls. Read files before editing.
  7. No stubs, no TODOs, no placeholder implementations in shipped code. Enterprise-grade only.
  8. Professional honesty — no marketing language, no fabricated metrics.
  9. 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.
  10. 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.
  11. Subpath exports per feature module. Every feature directory under src/ must have its own entry in the exports map in package.json (e.g. "./retry/retry", "./circuit-breaker/circuit-breaker"). A single "." barrel export is not sufficient — tree-shaking requires granular subpaths.
  12. @vitest/coverage-istanbul must be in devDependencies. Use catalog:testing or equivalent. The provider: 'istanbul' setting silently falls back to v8 if the package is missing.
  13. 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 test run is required — a single-file vitest run is 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.md exists at the package root (not inside src/) — one-paragraph description, install, quick-start, Modules section linking to every co-located .md in src/
  • Every .ts / .tsx implementation file has a sibling .md (≥200 words, simple + advanced examples, API reference, implementation notes) — excludes .test.ts, .spec.ts, .types.ts inside feature dirs, .stories.tsx, barrel index.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
  • AbortSignal checks 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, .only in src/
  • sideEffects: false holds; entry point imports verified to have no side effects; tree-shake test included
  • Subpath exports: one exports entry per feature module in package.json
  • @vitest/coverage-istanbul in devDependencies (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:

  1. Reading the matching enterprise/<package>.md on the main branch (/mnt/s/web/suaveplan/enterprise/<pkg>.md).
  2. Reading the current implementation on the main branch for shape-preservation (export inventory).
  3. Creating openspec/changes/enterprise-<pkg>/ with proposal.md, design.md, specs/<capability>/spec.md, and tasks.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/config once @suaveplan/testing lands.
  • Use forks pool. vmThreads and threads both fail in Bun on WSL2.
  • fileParallelism: false required 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 version 0.1.0.
  • Edit: no version bump during genesis; version stays 0.1.0 until 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>