Skip to content

Tasks 002 — Monorepo wiring ​

Execution skill: superpowers:subagent-driven-development — one implementer per task, then a two-stage review (spec compliance, then code quality). superpowers:test-driven-development applies inside every task: no production code before a failing test that demands it. Reach for superpowers:systematic-debugging on any surprise rather than guessing.

Derived from plan.md (approved). Each task is small, independently verifiable, and reviewed as its own diff. Split where a reviewer could reject one task while approving its neighbour — not where the work merely changes subject. A task is done only when it satisfies the definition of done in CLAUDE.md.

Global Constraints in plan.md apply to every task and are not repeated per task.


T1 — Adopt Biome as the sole linter and formatter ​

Satisfies: R5, R6 (lint half), P2 #3, SC4.

  • [ ] RED: create tests/monorepo/wiring.test.ts with a case SC4/R5: Biome is the only linter and formatter asserting: biome.json exists; root package.json has scripts lint = biome check . and format = biome check --write .; @biomejs/biome is in devDependencies; and neither eslint nor prettier appears anywhere in package.json or pnpm-lock.yaml. Run pnpm exec vitest run tests/monorepo/wiring.test.ts — watch it FAIL because biome.json and the scripts do not exist yet.
  • [ ] GREEN: add @biomejs/biome@2.5.5 to devDependencies and pnpm install; create biome.json (formatter: 2-space indent, single quotes, semicolons always; assist.source.organizeImports on; vcs: { enabled: true, clientKind: "git", useIgnoreFile: true } so it honours .gitignore and skips node_modules/dist/.vitepress/cache); add the lint and format scripts. Rerun — PASS.
  • [ ] REFACTOR: none expected.
  • [ ] Confirm the test has teeth — remove @biomejs/biome from devDependencies (or rename the lint script), watch the case fail, restore.
  • [ ] Commit (chore: adopt Biome as sole linter and formatter).
  • [ ] Formatting pass, as its own commit: run pnpm format, review the diff (existing files gain semicolons / sorted imports — expected on .vitepress/config.mts, scripts/new-spec.ts, tests/workflow/*), confirm pnpm lint exits 0, then commit (style: format repository with Biome).

Verified by: tests/monorepo/wiring.test.ts › SC4/R5: Biome is the only linter and formatter; pnpm lint exits 0.


T2 — Split tsconfig.base.json out of tsconfig.json ​

Satisfies: R2, R3.

  • [ ] RED: add to tests/monorepo/wiring.test.ts a case R2/R3: tsconfig base split, root extends and stays noEmit with packages included asserting: tsconfig.base.json exists and holds exactly the strictness keys (strict, noUncheckedIndexedAccess, noImplicitOverride, noFallthroughCasesInSwitch, exactOptionalPropertyTypes, verbatimModuleSyntax, skipLibCheck) and none of lib/module/types; root tsconfig.json has extends: "./tsconfig.base.json", noEmit: true, and an include containing packages/**/*.ts. Run the file — watch it FAIL.
  • [ ] GREEN: create tsconfig.base.json with the strictness keys only; edit tsconfig.json to extends it, keep the Node profile (target: ES2023, lib: ["ES2023"], module/moduleResolution: nodenext, types: ["node"]), keep allowImportingTsExtensions and noEmit, and widen include to add packages/**/*.ts. Rerun the case — PASS.
  • [ ] REFACTOR: none.
  • [ ] Confirm teeth — delete the extends line, watch the case fail, restore.
  • [ ] Run pnpm typecheck — exit 0 (scripts/ and tests/ still typecheck under the reconstituted config).
  • [ ] Run pnpm format, then commit (chore: split tsconfig.base from tsconfig).

Verified by: tests/monorepo/wiring.test.ts › R2/R3: tsconfig base split…; pnpm typecheck exits 0.


T3 — Seed the @gw2priory/domain package (source-only, self-tested) ​

Satisfies: R1, R4, R7, R9, P1 #2, P1 #3, SC1.

  • [ ] RED: create packages/domain/package.json (name: "@gw2priory/domain", private: true, type: "module", version: "0.0.0", exports: { ".": "./src/index.ts" }); create vitest.config.ts with include: ['packages/**/*.test.ts', 'tests/**/*.test.ts']; write the co-located test packages/domain/src/index.test.ts (P1 #3: brands an item id and P1 #3: netSellPrice takes the 15% TP tax off — itemId(19721) === 19721, netSellPrice(100) === 85) importing from ./index.ts. pnpm install, then run pnpm test — watch it FAIL because ./index.ts does not exist.
  • [ ] GREEN: write packages/domain/src/index.ts exporting ItemId (branded), itemId(), and netSellPrice(gross) => Math.floor(gross * 0.85) exactly as in plan.md › Data & contracts. Rerun pnpm test — the co-located test PASSES.
  • [ ] Add to tests/monorepo/wiring.test.ts a case SC1/R1/R7/R9: packages are private, source-only via exports, under @gw2priory, and emit no dist scanning every packages/*: private: true, exports["."] ends in .ts, name starts @gw2priory/, and no dist/ directory exists. It should pass immediately for domain.
  • [ ] REFACTOR: none.
  • [ ] Confirm teeth — change 0.85 to 0.75, watch the co-located test fail, restore.
  • [ ] Run pnpm typecheck — exit 0, now covering packages/domain (P1 #2: no emit produced).
  • [ ] Run pnpm format (keep the tree Biome-clean), then commit (feat: seed @gw2priory/domain package).

Verified by: packages/domain/src/index.test.ts (P1 #3 cases); tests/monorepo/wiring.test.ts › SC1/R1/R7/R9: packages are private, source-only…; pnpm typecheck exits 0.


T4 — Consume the package across the workspace (the F3 guard) ​

Satisfies: P1 #1, P1 #4, R8, SC3.

  • [ ] RED: create tests/monorepo/resolution.test.ts with a case P1 #1/#4, R8, SC3: @gw2priory/domain resolves by name across the workspace that imports { itemId, netSellPrice, type ItemId } from @gw2priory/domain by name and asserts on them (netSellPrice(1000) === 850). Run pnpm exec vitest run tests/monorepo/resolution.test.ts — watch it FAIL with Cannot find package '@gw2priory/domain'. Confirm that is the reason (the F3 finding: the workspace glob alone does not link the package), not a typo in the specifier.
  • [ ] GREEN: add "@gw2priory/domain": "workspace:*" to the root package.json devDependencies; pnpm install (creates node_modules/@gw2priory/domain -> ../packages/domain); rerun — PASS.
  • [ ] REFACTOR: none.
  • [ ] Confirm teeth — remove the workspace:* dep and pnpm install, watch the case fail with the same Cannot find package, then restore and pnpm install.
  • [ ] Run pnpm typecheck — exit 0 (tsc now resolves the by-name import too).
  • [ ] Run pnpm format, then commit (feat: consume @gw2priory/domain across the workspace).

Verified by: tests/monorepo/resolution.test.ts › P1 #1/#4, R8, SC3: …; pnpm typecheck exits 0.


T5 — Lock the remaining invariants and fill traceability ​

Satisfies: R6 (orchestration half), SC5, SC7; final verification of SC2, SC6, R10.

  • [ ] RED: add to tests/monorepo/wiring.test.ts two cases: SC5/R6: no build or cache orchestration tool is present (no turbo, nx, @nrwl/*, @moonrepo/*, moon, or rush in package.json or pnpm-lock.yaml) and SC7: the spec 002 traceability table is complete (parse specs/002-monorepo-wiring/spec.md’s traceability table — no empty Test cells, and every test name it lists is found as a string in some file under tests/** or packages/**). Run — the SC7 case FAILS because the table cells are still empty.
  • [ ] GREEN: fill the Test column of spec.md’s traceability table with the test names delivered in T1–T4 and this task (each row → its describe/it name). Rerun — PASS.
  • [ ] REFACTOR: none.
  • [ ] Confirm teeth — blank one table cell, watch the SC7 case fail, restore.
  • [ ] Full verification (the step-5 command runs behind SC2/SC6/R10): pnpm typecheck exits 0 (SC2), pnpm lint exits 0, pnpm test is fully green including tests/workflow/* (spec 001 suite intact — SC6/R10), and find docs/superpowers -type f | wc -l prints 0 (SC6).
  • [ ] Run pnpm format, then commit (specs: fill spec 002 traceability and lock wiring invariants).

Verified by: tests/monorepo/wiring.test.ts › SC5/R6: … and SC7: …; a clean pnpm typecheck, pnpm lint, and pnpm test; zero files under docs/superpowers/.


Notes ​

Staging area for decisions and surprises found during implementation — including anything that turned out differently from what plan.md assumed. Move each one into spec.md, research.md, or docs/ before closing the feature; this section is not a home.

  • Environment quirk (this sandbox): node/pnpm are not on the default PATH; they live at /opt/homebrew/bin. Prefix export PATH="/opt/homebrew/bin:$PATH" when a command reports command not found.
  • After T1, every task runs pnpm format before its commit so pnpm lint stays green throughout.
  • Graduation (for step 6): the source-package convention + the workspace:* precondition, and Biome as the chosen linter/formatter, are the two findings research.md flagged to move into docs/architecture/ — do not let the branch close without them.