Phase 2 (Development), step 2 of product-playbook — the FIRST thing you build. Choose the project's SHAPE (domain modules by default, or layers) and explain what each folder is for and why, plus the root scaffolding every project needs — agent instructions, ignore rules, .env.example, secret-scan, commit hooks, a task runner — using the TOOLS #Architecture chose, and a prompts/ YAML folder for AI. Use when starting to code, or run /structure "set up the project", "folder structure", "where do...
Installs into .claude/skills of the current project.
Are you the author of Structure?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/kish21-structure)
---
name: structure
description: >
Phase 2 (Development), step 2 of product-playbook — the FIRST thing you build. Choose the project's
SHAPE (domain modules by default, or layers) and explain what each folder is for and why, plus the
root scaffolding every project needs — agent instructions, ignore rules, .env.example, secret-scan,
commit hooks, a task runner — using the TOOLS #Architecture chose, and a prompts/ YAML folder for AI.
Use when starting to code, or run /structure "set up the project", "folder structure", "where does
this go". Writes STRUCTURE.md + the Structure section of PRODUCT.md. Run /foundation next.
---
# `/structure` — Phase 2 · Development ② · run as a **senior engineer**
> Part of **product-playbook**. Reads + updates the project spine (`PRODUCT.md`, or the project's existing docs — resolve per MECHANISMS.md §Spine resolution); writes `STRUCTURE.md`.
> **Rule files — open by path, never search:** `${CLAUDE_PLUGIN_ROOT}/PRINCIPLES.md` · MECHANISMS.md = `${CLAUDE_PLUGIN_ROOT}/references/mechanisms.md` · MECHANISMS-ON-DEMAND.md = `${CLAUDE_PLUGIN_ROOT}/references/mechanisms-on-demand.md` · STATE-MODEL.md = `${CLAUDE_PLUGIN_ROOT}/docs/state-model.md` (a path still starting with `$`: the same names in `.claude/product-playbook/`, else `~/.claude/product-playbook/`).
> **This skill's OWN reference files are a DIFFERENT folder** — they live in `references/` **beside this SKILL.md** (`commands/structure/references/`), not in the plugin-root `references/` named above. Both install routes put them there. Below, `choosing-the-shape.md` · `layered-shapes.md` · `root-scaffolding.md` each mean that folder: open them by that path, and never look for them next to MECHANISMS.md.
> **Open `PRINCIPLES.md` and `MECHANISMS.md` before the first step** (paths above; a situational companion only when a rule points into it), then apply them — load-bearing:
> **modular/single-responsibility**, **layered sub-packages**, **intention-revealing naming**,
> **prompts externalized to `prompts/` YAML**, **no-hardcoding (secrets→`.env`, knobs→config)**,
> **no secret in any code file**.
> This skill exists because bad/ad-hoc folder structure is the #1 thing newcomers get wrong (god-files,
> "where does this go?"). The goal is to **teach what each folder is for**, not just create it, so the
> layout stays clean as it grows. Adapt names to the chosen stack, but always lay the same solid base.
## Contract
- **Purpose:** a chosen, explained layout + the root scaffolding files + (AI) `prompts/`.
- **Reads:** `PRODUCT.md#Architecture` — the **stack** *and* the **Dev tooling** line (which decides the
tool in every scaffolding slot); `#Scope` (the concerns that become modules); `#Vision` (AI product?).
- **Writes:** `STRUCTURE.md` (folder→purpose map) + `PRODUCT.md#Structure` (summary); re-run after `/tickets`: moved paths in `docs/issues/`, `TICKETS.md`, open issues.
- **Gate type:** `derivation` — computable from `#Architecture` + the stack. Batchable - a `derivation` run may chain with its neighbours and end in ONE review. (`STATE-MODEL.md` §2d)
- **State model** (`STATE-MODEL.md` §2c): writes `#Structure` · `declined` ✓ · `override` ✓ · `superseded` ✓
- **Exit criteria:**
- [ ] **The project's agent instructions exist** (`CLAUDE.md` / `AGENTS.md` — one may point at the
other), from `templates/AGENTS.md`: a **pointer** to the spine plus the rules an agent breaks first,
**never a copy of `PRODUCT.md`**. Five skills read this file and `/drift-check` polices it; nothing
created it. **Write outside any tool-owned `<!-- BEGIN: -->` block** — those are rewritten every run.
- [ ] **The SHAPE was chosen, stated and recorded** — domain modules (the default: one folder per thing
the product does) or layers — with a one-line why in `STRUCTURE.md`. A shape nobody chose is a
decision nobody can revisit. Dependencies point inward either way; no god-files.
- [ ] **A module is a complete lane** (`references/choosing-the-shape.md` §A module is a complete lane):
routes, handlers, store and tests live inside the module folder; the app reaches it through one
registry line; `platform/` holds only what no module owns; the frontend follows the same rule. **`STRUCTURE.md`
names the hub files** under `## Hub files`, each verified to exist by `check_structure.py`.
- [ ] `STRUCTURE.md` explains **what each folder is for and why**, in plain language.
- [ ] **`STRUCTURE.md`'s map and the real tree agree BOTH ways** — proven by `scripts/check_structure.py`
(Step 3b), not by eye. A drawn-but-uncreated folder is silent doc↔code drift; an unmapped folder on
disk is a layout decision nobody recorded.
- [ ] Root scaffolding present — each a **capability**, filled by the tool `#Architecture` recorded (the
playbook never dictates the tool): `README.md` · ignore rules · `.env.example` · **secret-scan config**
· **commit-hook runner** · **task runner** · **dependency manifest** (dev/prod split) · `SECURITY.md` ·
`CHANGELOG.md` (Keep a Changelog, seeded with `[Unreleased]`).
- [ ] **Every scaffolded tool matches `#Architecture`'s Dev tooling line.** `.pre-commit-config.yaml` in
a Node repo whose ADR chose lefthook makes the recorded trail describe a tool the repo does not use,
and forces a Node contributor to install Python tooling to commit. No tool recorded for a slot →
**recommend one that fits the detected stack, say why, and record it back**.
- [ ] **The config-layering files are actually scaffolded**, not an empty `config/`: a **typed loader**
in the project's own language + `platform.yaml` (engine knobs) + `product.yaml` (product knobs),
reading `.env` — the no-hardcoding engine.
- [ ] Ignore rules cover `.env` **and its variants/backups** (`.env.bak`, `*.env.local`); only `.env.example` is committed. **No secret in any code file.**
- [ ] **Dependency manifest: this phase owns its EXISTENCE AND SHAPE** — the file, the dev/prod split,
project metadata (`MECHANISMS.md` §Seam); versions, the install and tool configs belong to
`/foundation`. **Leave no script that cannot run** — it runs now, or it is marked as arriving with
`/foundation`. Green while `make check` fails is a lie.
- [ ] **`.env.example` declares the isolated test datastore** (`TEST_DATABASE_URL` or equivalent) as its
own variable, never the development one — `/test` runs against real boundaries, so the two values must
differ. `/foundation` provisions it and guards it.
- [ ] **`.env.example` holds only unmistakable placeholders** — `CHANGE_ME__<VAR>__CHANGE_ME`, never a
realistic-looking string long enough to pass validation (`replace-me-with-32-plus-random-characters`
is 48 chars and **passes** `min(32)`, so a copied `.env` boots the app on a signing key that is public
in git). Each carries a one-line comment on generating the real value; `/foundation` **rejects them by
name** at boot (`PRINCIPLES.md` §Production safeguards).
- [ ] For AI products: a **`prompts/` YAML folder inside the backend package** (root `prompts/` only
when there is no backend package) — prompts never inline in code.
## Step 0 — Context + prior-gate check
- Read `#Architecture` and `#Vision` (AI?). Take **two** things from `#Architecture`: the **stack**, and
the **Dev tooling** line (hook runner · secret scanner · task runner · linter · dependency manifest),
**which decides every scaffolding file you are about to write**. Reading only the stack is how a
recorded tool choice gets silently overridden one phase later.
- **Read the recorded runtime target before writing any infra file** — `#Architecture`'s custody + runtime
line decides what is appropriate at all: **no `docker-compose.yml` for a target that does not use one**
(managed-serverless data, a PaaS, an embedded datastore). A compose file written by habit is an artifact
the project must later delete, and implies a custody decision nobody made.
- If `#Architecture` is empty, warn and offer `/architect` first (allow override). Running standalone, detect the stack from the repo and pick tooling that fits it — then record the choice so the next phase inherits it.
- **An override is RECORDED, never a verbal "yes"** (`MECHANISMS.md` §Declined runs): name the gate, ask for the **reason in the user's own words**, and write `Override <date>: <reason> — bypassed <gate>` at the top of `#Structure` before continuing. Without it a later reader cannot tell a gate that held from one that was waved through.
- Brownfield: read the existing tree; propose a clean target layout + a migration note — don't blindly move files.
- **Re-running — `MECHANISMS.md` §Re-run semantics, in full.** The section is already filled → show what would change and ask first; a reversed decision keeps its dated `superseded` line.
- **A shape-changing re-run after `/tickets` rewrites every path — named AND planned files — in `docs/issues/*.md` and `TICKETS.md`, closes the finding, then commits it as a `path rewrite` and syncs the open issues (`/tickets`' `publishing.md` §A path rewrite reaches GitHub); lanes, order and owners stay.** Stale paths send the next builder to recreate the emptied folder; the close counts the issues synced. (case file: The move that came a phase late)
- **A file moved from a shared folder into a lane carries its imports with it — check the import graph, not just the tree.** Hand cross-lane needs across in the composition root and enforce the arrows with a check beside `check_structure.py`. (case file: The move that came a phase late)
- **Stopping at an unmet gate — `MECHANISMS.md` §Declined runs, in full.** One dated `_Not run_` line at the top of `#Structure`, nothing else touched; the section stays unfilled so `/playbook` still routes here.
## Step 1 — Apply principles (this phase)
- **One folder = one concern; dependencies point inward** (api → service/domain → data). No god-files.
- **No-hardcoding:** secrets go to `.env`; tunable knobs to layered config; **never a key/secret in a code file**.
- **Prompts are config (AI):** `app/prompts/*.yaml` (backend sub-package), versioned, never inline.
- Keep sub-package conventions consistent across the tree; don't reinvent per folder.
## Step 2 — Lay the structure (pick the shape, then adapt names to the stack)
### 2a — The shape, in two decisions. **Open `references/choosing-the-shape.md` and follow it.**
1. **The split is classified from the PRODUCT, not asked and not read off the stack.** UI *and*
server-side logic/data/externals → **full-stack → two folders** (a backend package and `frontend/`).
The stack names what is *inside* each; a UI-tooling constraint binds the UI only. **Collapsing them
into one app is the exception, recorded with a reason** — and if `#Architecture`'s stack implies a
shape that disagrees with this classification, **surface it rather than following the stack**.
2. **Then how folders are organised: by concern (the default) or by layer.** Domain modules — one folder
per thing the product *does*, owning its own types, persistence, logic, **routes and tests**, reached
through one registry line (§A module is a complete lane) — unless this is a genuinely single-concern
service. Dependencies point inward either way. **Say which and why in one line, in `STRUCTURE.md`,
before drawing anything**: a shape nobody chose is a decision nobody can revisit.
### 2b — The layered shapes (when 2a chose layers)
Three trees — backend, frontend, and full-stack (backend `app/` and `frontend/` side by side) — plus the
**layered `config/` pattern** that keeps secrets and tunables out of code. **Open
`references/layered-shapes.md` and follow it**; it also carries the AI/agentic sub-packages
(`agents/` · `pipeline/` · `prompts/` · `retrieval/`), which attach to whichever shape you chose.
**Root scaffolding (every shape):** README · **agent instructions** (`CLAUDE.md`/`AGENTS.md`) ·
ignore rules · `.env.example` · `CONTRIBUTING.md` ·
secret-scan config · commit-hook runner · `SECURITY.md` · `CHANGELOG.md` · task runner ·
dependency manifest with a dev/prod split · `tests/` · `docs/` · `tools/`. Each is a **capability**,
filled by the tool `#Architecture` named — **open `references/root-scaffolding.md`** for the exact
list, the per-tool choices, the conditional files (containers, CI, migrations, evals) and the task
runner's generic targets.
## Step 3 — Write back
Write **`STRUCTURE.md`** (one line per folder — *what goes here and why*, plain language) **with a
`## Hub files` section** listing every file all lanes may touch by one line (the registries, the config
loader, the dependency manifest) **as a table or list, one backticked path per row** — `check_structure.py`
verifies the first backticked token of each row and ignores prose. Fill `PRODUCT.md#Structure` with the
summary + the prompts location (AI).
## Step 3b — Self-verify (completeness gate)
**Copy `templates/check_structure.py` into the project** (`scripts/`, or wherever `STRUCTURE.md` puts
tooling), **commit it, and run it** — it compares the map against the tree in **both** directions and
exits non-zero on either mismatch — faster and stricter than reading 25 folders by eye. It is committed,
not scratch, because the transition guard re-runs `evidence:` lines in later sessions; the line names the
script, its result, `STRUCTURE.md` and today's date.
Check the boxes. **STOP and fix if:** a folder is unexplained; **the map and the tree disagree either
way** (the script says which); ignore rules don't cover `.env*`;
the secret-scan config or the commit-hook runner is missing; **`.env.example` holds any value that would
survive a plausible validity check** (it must be an unmistakable placeholder — see the criterion above);
a secret sits in a code file; or an AI product has no `prompts/` folder. An unexplained layout decays
into god-files; a leaked `.env` is a real incident.
**Close the loop (`MECHANISMS.md` §Step 3b):** update the `Stage:`/`Last updated:` header, reconcile any number this phase introduced against `#Vision` (surface a contradiction, never write over it), and **offer to commit the change** (`MECHANISMS.md` §Commit the work — check the repo exists, name the branch, offer the message, push only if a remote exists and the user says so). Then **run the transition guard** (`MECHANISMS.md` §Step 3b, item 4): re-run this phase's own `evidence:` lines and report a verdict for every exit criterion — `UNVERIFIED` is a normal outcome, silence is not — and check the transition is legal. **Close in plain language** (`MECHANISMS.md` §Plain-language close): two or three sentences of *what just happened* with no playbook dialect, then a numbered *what YOU do next* — the user's own actions, dated where they are time-bound, or "Nothing — you're done".
## Step 3c — Contradiction check (before the gate closes)
Per `MECHANISMS.md` §Step 3c, check what this phase produced against decisions **already recorded** — here `#Architecture`, **especially its Dev tooling line**: every scaffolding file must be the tool recorded there (a run once scaffolded `.pre-commit-config.yaml` into a Node repo whose ADR chose lefthook, and nothing noticed). On a conflict **name both sides, ask which wins, update the loser**. Carry any decision still OPEN forward (§Step 3c item 4). Adding detail to an earlier decision is not a contradiction.
## Step 4 — Handoff
"Clean structure + root scaffolding in place and explained in `STRUCTURE.md`.
- **User-facing UI?** Run **`/design-system`** next — principles, a confirmed sample page and a concrete
`DESIGN.md` *before* any screen is built, so the UI doesn't end up generic. Then `/foundation`.
- **Backend/API/CLI only:** straight to **`/foundation`** for a walking skeleton that actually runs —
alone, or as a **batch** `/foundation` + `/contracts` + `/tickets` with one review at the end
(`MECHANISMS-ON-DEMAND.md` §Batch mode); the batch still stops wherever a phase asks you to confirm."