---
name: platform-make-modules
description: "Create a NET-NEW @platform-modules/* module from scratch — for a capability that does NOT already exist in any prior app. ALWAYS starts with a scan-gate: survey the prior-app source repos first; if the capability exists in even one prior app, STOP and use /platform-extract-modules (porting beats reinventing). Only a genuine zero-reference capability is authored fresh here. Use on /platform-make-modules, or when asked to build a brand-new module/package in platform. Holds no code rules of its own — points to docs/standards/coding-standard.md and the module-plan-template gates, and scaffolds via `turbo gen`."
---

# Make Modules — Net-New Module Creation (scan-gated)

**Audience: AI coding agents first.** Optimize for activation, not prose — keep ladders as ladders.

Build module **from scratch** — only for capability that genuinely does not exist anywhere in the prior-app source repos. Platform code is *mostly adapted from prior apps* (that is `/platform-extract-modules`); fresh authoring is the rare zero-reference case. This skill's value = **scan-gate** that stops you reinventing existing code, then thin from-scratch path reusing platform canonical rules + scaffolder.

> Holds **no code rules of its own.** Code rules → `docs/standards/coding-standard.md`. Gates → `docs/plans/module-plan-template.md`. Structure → `turbo gen` generator. Architecture/layers/governance → `docs/specs/2026-06-13-monorepo-architecture-synthesis.md` + `CLAUDE.md`. This file *process*, not doctrine.

## Step 0 — SCAN-GATE (BLOCKING — before anything else)

Before design or write one line, prove capability **not already implemented in any prior app**. Reinventing what prior apps already battle-tested = most expensive mistake here, violates platform thesis (modules *ported*).

1. Name capability in one concrete phrase ("rate limiting", "image pipeline", "webhook signature verification").
2. **Survey prior-app source repos** — run read-only **`cursor`** survey across the prior-app source repos; cheap one-file *locates* may ride built-in `Explore` (haiku) — locate, not survey. (Dispatch policy: `platform-orchestrator` skill. Old haiku `source-scout` retired — survey accuracy load-bearing.) Grep names **and READ candidate impls** — filename not proof (see learned rules in `/platform-extract-modules`: read impl, never infer from name).
3. **Decide:**
   - **Found in ≥1 prior app → STOP.** Not from-scratch job. Switch to **`/platform-extract-modules`** — port it; even one prior app = starting impl, starts disposition clock. Don't build fresh.
   - **Found in 0 prior apps →** genuine net-new. Proceed below.

"Found it — routing to extract" outcome = gate **succeeding**, not failing.

## Step 1 — Is it a module at all?

No rubric restated — point to canonical filter:

- **Gate 0 (candidate rubric)** + **Gate 1 (delete-test keystone)** — `docs/plans/module-plan-template.md`. Copy template, fill gates honestly. Zero-reference capability has *even higher* bar: no proven reuse, so delete-test + "promote at 2nd independent consumer" rule (synthesis governance) usually say **WATCH** or **keep in first host** until real second consumer appears. That common, correct answer — building shared package for one speculative consumer = exactly what §2 ladder rung 1 refuses.

## Step 2 — Boundary design (classify before scaffolding)

- **Three axes (adapter / capability / domain)** — `module-plan-template` "Module shape" decision rule: different external service doing same job → **adapter** (separate package); optional feature within service → **capability** (opt-in subpath); your product specific composition → **domain** (stays in host, never in module).
- **Governance Patterns A/B/C** — `CLAUDE.md §3` (provider-in-charge → config not module; shared primitive lives *above* consumers; pricing-model = host-level boundary driver).
- **Layer placement** — L0–L3 DAG (`CLAUDE.md §2` / synthesis doc). Never build above missing dep.

## Step 3 — Scaffold (do NOT hand-create files)

Structure owned by generator — emit uniform skeleton, never hand-type `package.json`/`tsup.config.ts`/`tsconfig.json`:

```bash
pnpm turbo gen mod-package        # core @platform-modules/<name>: seam + capability subpaths
pnpm turbo gen mod-react-sibling  # ONLY if it needs a React adapter (flat sibling, never a /react subpath)
```

Generator (`turbo/generators/config.ts`) produce i18n-uniform shape: ESM-only, `sideEffects:false`, dynamic `exports`/`entry` per capability, `it.todo` test stubs (honest pending, not fake-pass).

## Step 4 — Author fresh against the seam

Write impl — `docs/standards/coding-standard.md` is BLOCKING:

- **§2 ladder** — climb to first rung that holds; refuse speculative layers it lists (no repository/service/controller around one call). Hard floor (trust-boundary validation, data-loss, security, a11y) never cut.
- **§4 boundary checklist** — every public export passes all boxes before "done".
- **R2 / R3 (§3)** — even with no prior app: no identity ceremony (R2 — factory that returns its input is a type), and memoize expensive natives (R3). **R1** (strip source-app defaults) has no source to strip here, but positive form still binds: never bake home-market/tenant/locale default into fresh seam either — make it required or config-resolved.

## Step 5 — Harness + publish

- **Gate 3 — consumer-harness fixture** in `apps/consumer` (`docs/specs/2026-06-13-consumer-harness.md`): real fixture that visibly turns red when export breaks, exercising at least one failure/edge path. Not "done" until it does.
- **Changesets** for per-package semver; publish per `CLAUDE.md §6` conventions.

## Build execution

Net-new module often one-file seam + types → may build **in-loop** (platform IRON LAW reserves cursor-agent for *substantive multi-file builds*). Multi-file from-scratch build → cursor-agent in worktree. Discriminator = churn risk (`CLAUDE.md §4 L3`).