Onboard Existing App
Instructions
Run the onboarding pipeline for the current project — an existing Go web application with no
(or partial) Nexa artifacts. Optional $ARGUMENTS: quick or full to preselect scan depth
(see Step 0.4); otherwise ask.
This produces the artifacts every other Nexa skill assumes already exist (docs/requirements.md,
docs/entity_model.md, docs/use_cases.puml, docs/use_cases/UC-XXX.md) from the existing
code, marks already-shipped features as such rather than queuing them for re-implementation,
and reports infrastructure gaps without touching anything that already works.
Prerequisites
go.mod at the repository root, and an HTTP server built on net/http.
gh CLI authenticated, for Step 5 tracking.
- Nothing else is required. SQL migrations,
sqlc.yaml, and _bmad/ are optional inputs
this skill detects and uses if present (Steps 0.1, 0.5, and 2).
- A router other than the stdlib
http.ServeMux (chi, gin, echo, gorilla/mux) does not block
onboarding. The routes are still crawled. Step 4 reports the router as a mismatch, because the
plugin's conventions register every route on one ServeMux in internal/web/routes.go.
DO NOT
- Modify or refactor any existing application code/behavior — this is a documentation and
detection pass only
- Run
go generate, templ generate, sqlc generate, or any migration — detection only reads
- Overwrite existing Nexa docs without going through the Step 0.2 three-way prompt
- Auto-run any
setup-* skill in Step 4, ever — report the gap and recommend the command
- Assert a UC as
Status: Done when its behavior is ambiguous in the code — use Review
- Trust a BMAD story's
Status: done without independently verifying it against the live
route/code — BMAD's record can drift from what's actually deployed
- Let parallel
use-case-archaeologist agents write to any file except their own assigned
UC-XXX.md — the orchestrator (this skill, main context) does the one sequential append
into docs/requirements.md and docs/use_cases.puml
- Attempt entity-model reverse-engineering from ORM code alone (GORM struct tags, ent schema)
— without SQL DDL, flag and skip instead
- Generate wireframes or design specs for existing screens —
/generate-wireframe and
/design-screens are prospective planning tools; reverse-capturing a live app's rendered UI
as a design artifact is a distinct capability this skill deliberately doesn't attempt.
/evaluate's Design Conformance and /audit's Screen Fidelity lens will simply skip for
onboarded UCs until a design doc exists via the normal flow on future work
- Auto-generate
BUG-XXX/TT-XXX docs from BMAD's deferred-work.md — mention counts/severity
in the Step 6 report and point at /report-bug instead; that needs resolve-bug's own
reproduction rigor, not a doc-mapping pass
Gates
- No Nexa Rules Gate section here — Step 5 of this skill is what establishes that gate's
<!-- NEXA_RULES_CONFIGURED v2 --> marker via /setup-project-rules. Requiring it upfront
would be circular, the same reason setup-project-rules itself doesn't gate on it.
- No Worktree Gate — this skill runs on
main in the primary checkout, not in a work item
worktree (documentation and read-only detection, not application feature code). It's listed in
${CLAUDE_PLUGIN_ROOT}/shared/readiness/WORKTREE_GATE.md's Exceptions section.
PROJECT_READINESS.md is audited, not gated on — Step 4 walks its checklist and reports
gaps; it does not block this skill from completing (it blocks implementation work later,
which is exactly what Step 4's report exists to prepare the user for).
Pipeline
Step 0: Discovery + Mode Selection
0.1 BMAD seed check
If _bmad/ exists, resolve its config for artifact paths — don't hardcode _bmad-output/,
it's user-configurable:
cat _bmad/bmm/config.yaml 2>/dev/null || cat _bmad/core/config.yaml 2>/dev/null
Extract planning_artifacts: and implementation_artifacts: values. When found, treat as a
primary seed, not a nice-to-have:
<planning_artifacts>/prd.md — #### FR-N: <title> sections, already near 1:1 with Nexa's
FR-XXX.
<planning_artifacts>/epics.md — ### Epic N / ### Story N.M: <title> — BMAD has already
clustered work; each Story is roughly one candidate UC/TT, so Step 0.6's clustering only
needs to run for routes no story covers.
<implementation_artifacts>/<epic>-<story>-<slug>.md — per-story Status: done|review|in-progress|deferred, a ## Story block already in As a X, I want Y, so that Z form, and ## Acceptance Criteria in Given/When/Then referencing the same FR-N tags.
Status: done is direct evidence for Status: Done on the matching UC — more reliable than
inferring completeness from code alone — but still gets verified against the live route in
Step 3, never transcribed blind.
<implementation_artifacts>/deferred-work.md — severity-tagged, file:line-cited past review
findings. Note the count/severity in the Step 6 report; do not act on it here (see DO NOT).
Build a match list: {cluster candidate → BMAD story path | none}.
0.2 Existing-docs check (expect hybrid state)
A real project can have partial/full Nexa docs layered on top of an earlier BMAD phase, or
neither, or both — never assume docs/ is empty or fully-Nexa. If docs/requirements.md,
docs/entity_model.md, docs/use_cases.puml, or docs/onboarding/ already exist, stop and
present:
ONBOARDING: existing Nexa artifacts found
[list what exists]
How should I proceed?
1. Rescan — re-run discovery; skip clusters that already have a UC doc, only process newly
discovered ones (no diffing/merging of existing docs in this version).
2. Deep-dive one area — name a specific cluster or infra concern to redo.
3. Cancel — make no changes.
Wait for a reply before continuing.
0.3 Resumability
Check for docs/onboarding/scan-state.json:
{ "mode": "quick|full", "clusters": [{"id": "UC-001", "status": "pending|done", "source": "bmad|code"}], "completed_steps": ["0","1"] }
If present and incomplete, resume from the last unfinished cluster/step instead of
restarting. Write/update this file after every completed step and every completed cluster —
a killed or multi-session Full run should not have to start over.
0.4 Scan depth
If not given via $ARGUMENTS, ask:
ONBOARDING: choose scan depth
1. Quick — full skeleton (requirements catalog, entity model, use case diagram, infra gap
report, GitHub issues) but UC specs are cheap Draft stubs (name, primary actor, one-line
goal) for /engineer-requirements to flesh out later. Fast, good first look or a very large
app.
2. Full — Quick's output plus fully grounded UC specs (MSS, alt-flows, business rules cited
to file:line), BMAD-seeded where available. The complete pipeline.
0.5 Crawl the codebase (read-only)
Read go.mod for the module path and dependencies. Do not assume this plugin's layout
(internal/<feature>/, templ, sqlc, goose): an existing app can keep its component packages at
the module root, all handlers in one web package, and its own migration runner. Find each
item below by what the code does, not by where this plugin would put it. Then collect:
- Routes — every registration with its method and pattern: stdlib
mux.HandleFunc("GET /items/{id}", ...) / mux.Handle(...), or the third-party router
equivalent (r.Get("/items/{id}", ...) in chi, r.GET("/items/:id", ...) in gin/echo,
r.HandleFunc(...).Methods("GET") in gorilla/mux). Record which router is in use for Step 4.
Apps often register through a local helper that adds middleware, e.g.
handle := func(p string, h http.HandlerFunc) { mux.Handle(p, auth.RequireAuth(h)) } — find
every such helper first, then every call to it. For each route, record the full pattern
(add the prefix of an enclosing chi Route/Mount, gin/echo Group, or a sub-mux behind
http.StripPrefix) and the middleware its registration adds (a login or role check). That
middleware is evidence for a precondition in Step 3.
- Handlers — the functions those routes call, wherever they live (one file per feature in a
web package, or a handler.go per feature), including htmx fragment endpoints.
- Views —
.templ components, or html/template files (*.html, *.tmpl, *.gohtml,
including partials and the //go:embed directive that loads them) — record html/template
for Step 4.
- Services — the business logic the handlers call, in
internal/ or in top-level
component packages.
- Validation —
Validate() methods, go-playground/validator struct tags
(validate:"required,email"), explicit if guards in handlers and services, and sentinel
domain errors that services return for a broken rule (var ErrNameRequired = errors.New(...))
together with the handler branch that maps each one to a response.
- Schema — every ordered stream of SQL DDL files. Find a stream by its content, not by its
tool: a directory of
.sql files with a version prefix (001_create_items.sql,
20240101120000_add_users.sql, 000001_init.up.sql) that contains CREATE TABLE. goose,
golang-migrate, atlas, the schema path in sqlc.yaml, and a custom runner all produce
this shape. For a custom runner, read the runner and the //go:embed directive that feeds
it: they give the apply order and the PostgreSQL schema each stream targets (e.g.
migrations/app/ → public, migrations/reference/ → reference). Record every
stream with its target schema. Exclude a stream that only inserts data (demo data, seed
files) — it has no CREATE TABLE or ALTER TABLE. If no stream exists but the app clearly
uses a database (GORM, ent, sqlx with no DDL in the repo), flag this prominently and skip
Step 2 entirely — guessing at a schema from ORM code is out of scope.
- Queries — sqlc queries in
db/queries/*.sql (or the queries path in sqlc.yaml), or
the SQL strings the services pass to pgx or database/sql, or their ORM calls.
- Tests — existing
*_test.go files (handler tests that drive a flow through httptest
are evidence of intended behavior for Step 3) and the e2e/ directory, if present.
0.6 Cluster
For every route/handler not already matched to a BMAD story (0.1), group into candidate use
cases by user-facing goal, not by file — e.g. GET /checkout + POST /checkout +
POST /discount-codes/apply (htmx fragment) → one "Checkout with Discount" cluster. Inspired
by how engineer-requirements clusters existing UC docs; here the input is raw code.
Put routes that call the same handler in the same cluster: GET /items and
GET /api/items served by one handler are one use case with an HTML and a JSON response,
not two use cases. Do not cluster routes that no user goal needs — health checks, metrics,
static files. List them in the Step 6 report as not user-facing.
Step 1: Reverse Requirements
Compile one consolidated, code-derived summary across every cluster (each cluster contributes
one candidate FR: title, user story reconstructed as As a [actor], I want [goal] so that [benefit], grounded in its BMAD FR-N when matched, otherwise in the code). Author
docs/requirements.md directly, following its documented table format exactly:
| ID | Title | User Story | Priority | Status |
Status for every discovered FR is Verified — the correct terminal value for an
already-shipped requirement (docs/requirements.md's status vocabulary has no "Done"). Do not
pause per-cluster to ask clarifying questions the way a fresh requirements-gathering pass
would — ground ambiguity in a note for the Step 6 report instead; this pipeline may process
many clusters and per-item interactive dialogue doesn't scale.
Step 2: Entity Model
Skip entirely if no SQL DDL was found (Step 0.5). Otherwise build the final schema of each
stream by replaying its up migrations in the order its runner applies them — normally the
version prefix (CREATE TABLE, then every later ALTER TABLE, DROP, and RENAME). Ignore
down migrations and goose -- +goose Down sections. Then author docs/entity_model.md
directly, following its documented format exactly: a Mermaid erDiagram block (relationships
only, no attributes inside entity nodes) plus one ### ENTITY_NAME section per table with a
5-column attribute table (Attribute, Description, Data Type, Length/Precision, Validation
Rules). When the streams target more than one PostgreSQL schema, qualify every table outside
public with its schema (reference.countries), and keep foreign keys across schemas in the
diagram.
Map PostgreSQL types to the fixed vocabulary:
| PostgreSQL type |
Data Type |
Length/Precision |
Validation Rules add |
bigint, bigserial, int8 |
Long |
19 |
— |
integer, int, smallint, serial, smallserial |
Integer |
10 |
— |
text, varchar(n), char(n), citext |
String |
n where declared |
— |
uuid |
String |
36 |
Format: UUID |
numeric(p,s), decimal(p,s) |
Decimal |
p,s |
— |
real, double precision |
Decimal |
— |
— |
boolean |
Boolean |
1 |
— |
date |
Date |
— |
— |
timestamp, timestamptz |
DateTime |
— |
— |
jsonb, json |
String |
— |
Format: JSON |
bytea |
String |
— |
Format: Binary |
T[] (array) |
the Data Type of T |
as for T |
List |
enum (CREATE TYPE ... AS ENUM) |
String |
— |
Values: A, B, C |
For a type with no row, use the nearest Data Type, write the PostgreSQL type in Validation
Rules, and name it in the Step 6 report.
Map PRIMARY KEY, UNIQUE, NOT NULL, CHECK, and DEFAULT to Validation Rules. A
single-column CHECK (col IN ('a', 'b')) is an enum: write Values: a, b. Write any other
single-column CHECK as its condition. Write a CHECK that spans columns as a
Constraints: line after the table, per the entity model format. Map each FOREIGN KEY to
Mermaid cardinality: a plain foreign key is many-to-one; a foreign key that is also UNIQUE
is one-to-one. A join table with two foreign keys as its primary key is many-to-many.
Skip tables that are not part of the domain, and name them in the Step 6 report:
- migration tracking tables of every stream (
goose_db_version, schema_migrations,
or the tracking table of a seed stream) — a runner can keep one per schema
- a session store — a table whose columns are only a key, a session token or its hash, a user
reference or a session payload, and timestamps. Decide by the columns, not by the name: a
sessions table
of a training or booking domain is a domain entity.
Every entity must trace to a real CREATE TABLE — never invented.
Step 3: Use Case Diagram + Specs
Author docs/use_cases.puml directly for all clusters together, following its documented
PlantUML format (@startuml, actors, one rectangle containing usecase "UC-XXX\nDescription" as UCXXX per cluster).
Quick mode: write minimal Draft-status stub UC docs directly (no subagents) — name,
primary actor, one-line goal, Provenance: reverse-engineered, not yet elaborated — for
/engineer-requirements to flesh out later.
Full mode: spawn one typed use-case-archaeologist subagent per cluster, batched in
parallel (single message, N Agent tool calls — same pattern resolve-bug uses for its
Step 2 parallel pair). Invoke via the Agent tool with
subagent_type: "nexa-claude-go:use-case-archaeologist". Prompt per agent:
Write the use case spec for cluster "[name]" — primary actor [actor], implemented in
[routes with method, full pattern, and the middleware each registration adds; handler,
view, service, query, and handler test files]. [If
matched: BMAD story at [path], FR-N [n], claimed Status [status] — verify, don't transcribe.]
Follow your operating manual (onboard-existing-app/SKILL.md Step 3, loaded as your
identity) to the letter. Write only docs/use_cases/UC-XXX.md. Report the file path, the
Status you assigned and why, a draft FR-XXX table row, any BR-XXX beyond what's in the UC
file, and any BMAD/code discrepancy you found.
After all agents in a batch return, do one sequential pass: append each returned FR-XXX
row into docs/requirements.md (replacing the earlier per-cluster draft from Step 1 if it
was a placeholder) and cross-reference each UC in docs/use_cases.puml. This is the only
point that writes to those shared files, avoiding the concurrent-write race parallel agents
would otherwise hit.
Update docs/onboarding/scan-state.json after each batch (Step 0.3).
Step 4: Infra Gap Audit (stop-and-list only — never auto-run)
For each concern, check its own CLAUDE.md marker first (every setup-* skill already writes
and self-checks one, so this reuses that instead of inventing new detection):
NEXA_ENV_PROFILES_CONFIGURED, NEXA_I18N_CONFIGURED, NEXA_WEB_MIDDLEWARE_CONFIGURED,
NEXA_ARCH_UNIT_CONFIGURED, NEXA_PLAYWRIGHT_CI_CONFIGURED, NEXA_QUALITY_CI_CONFIGURED,
plus every item in ${CLAUDE_PLUGIN_ROOT}/shared/readiness/PROJECT_READINESS.md.
Marker present: document as configured.
Marker absent but functionally equivalent infra clearly exists: flag as a mismatch for
human review — never stamp the marker on unverified equivalence. Typical Go mismatches:
| Found in the app |
Plugin convention |
component packages at the module root, all handlers in one web package, routes registered outside internal/web/routes.go |
internal/<feature>/ packages, routes in internal/web/routes.go |
| chi, gin, echo, or gorilla/mux router |
one http.ServeMux in internal/web/routes.go |
gorilla/csrf or another CSRF token library |
http.CrossOriginProtection |
html/template views |
templ + htmx |
GORM, sqlx, raw database/sql, or hand-written SQL over pgx |
sqlc over pgx |
| golang-migrate, atlas, or a custom migration runner |
goose |
godotenv loading env files |
env files sourced by the shell, read in internal/config |
| zap, zerolog, or logrus |
log/slog JSON handler |
| an i18n library other than go-i18n |
go-i18n via /setup-i18n |
A mismatch is not a defect in the app. It means the plugin's skills (/implement,
/integration-test, /playwright-test) assume a structure the app does not have. List what
that costs for future work, so the user can decide to migrate or to adapt.
Many PROJECT_READINESS.md items check an exact path (cmd/dev/main.go,
internal/web/routes.go, db/migrations/, sqlc.yaml, .env). When the app has its own
layout, do not report each of these items as a separate gap. Report them as one Layout
mismatch row that names the app's equivalent for each item. Report an item as Missing only
when the app has no equivalent at all.
Genuinely missing: list as a recommended follow-up (e.g. "Run /setup-i18n — no i18n
detected"). Never auto-run any setup-* skill — each one makes opinionated decisions
(auth strategy, locale list, RBAC model) that could conflict with how this specific existing
app already does things. This is the one place in the pipeline where "onboard" stops short
of acting, by design.
Step 5: Nexa Rules + Tracking
Run /setup-project-rules (Skill tool) if <!-- NEXA_RULES_CONFIGURED v2 --> is missing from
CLAUDE.md — idempotent, no judgment calls, safe to always run.
Then, per ${CLAUDE_PLUGIN_ROOT}/shared/tracking/TRACKING.md conventions, for every UC:
gh issue create --title "UC-XXX: <name>" --body "<one-line summary>
**Spec:** [\`docs/use_cases/UC-XXX.md\`](<SPEC_URL>)"
- Status: Done →
gh issue close <number> immediately after creating — same as any other
already-shipped, DoD-satisfying work.
- Status: Review or Draft → leave open — not confirmed-complete, so it shouldn't read
as closed/done in the tracker.
Step 6: Onboarding Report
Write docs/onboarding/ONBOARDING_REPORT.md:
# Onboarding Report
## Summary
[scan depth used, cluster count, router detected, BMAD seed used: yes/no]
## Use Cases
| UC | Name | Status | Source | Notes |
|----|------|--------|--------|-------|
| UC-001 | ... | Done | BMAD story 1.2, verified | internal/order/handler.go, internal/order/views.templ |
| UC-002 | ... | Review | Code archaeology | [what couldn't be confirmed, e.g. error path in internal/invoice/service.go:88 unreachable] |
## Entity Model
[coverage: N tables mapped from M migrations in S streams (one line per stream: directory → schema), infrastructure tables skipped, types with no row in the map, or "skipped — no SQL DDL, schema lives in GORM models"]
## Routes Not Clustered
[routes that serve no user goal — health checks, metrics, static files]
## Infrastructure Gaps
| Concern | Status | Recommended Action |
|---------|--------|---------------------|
| i18n | Missing | Run /setup-i18n |
| Web Middleware | Mismatch — chi router + gorilla/csrf | Review: migrate to ServeMux + CrossOriginProtection, or adapt |
| Layout | Mismatch — routes outside internal/web/routes.go, a custom migration runner, html/template | Review: migrate to the plugin layout, or adapt the skills |
| Environment Profiles | Configured | — |
## Deferred Work (BMAD, informational only)
[N items found in deferred-work.md, by severity — consider /report-bug for High severity]
## Before You Run /implement or /deliver-use-case
[prioritized list: Review-status UCs to confirm, infra gaps and mismatches to close, entity model gaps]
Optionally draft an informal docs/vision.md retrospective summary — no template exists for
it anywhere in this repo, so keep it best-effort and clearly labeled as reconstructed, not
authoritative.
Verification
- Confirm the Nexa Rules Gate (
<!-- NEXA_RULES_CONFIGURED v2 -->) now passes.
- Confirm
git status shows changes only under docs/, CLAUDE.md, and nothing in Go,
templ, or SQL files.
- Confirm every route found in Step 0.5 is in exactly one cluster or in the report's Routes Not
Clustered list. Count the registrations in the code again, including the calls to every
registration helper — a route that is in neither place was missed by the crawl.
- Spot-check at least one generated
UC-XXX.md against the live handler/view it claims to
describe — MSS steps should match actual code behavior, not paraphrase intent.
- Spot-check at least one entity in
docs/entity_model.md against the final migration that
shapes its table — a column added or dropped by a later ALTER TABLE must be reflected.
- Confirm
docs/delivery/UC-XXX-iterations.md exists only for Status: Done UCs. This is
what makes the project-wide "delivered" detection recognize them correctly —
it keys off file existence, not the Status field. Write one with a single entry noting
Reverse-engineered from existing code, not delivered via the Nexa pipeline for each Done
UC, matching deliver-use-case/SKILL.md's iterations-log format.
- Confirm every closed GitHub issue corresponds to a
Status: Done UC and every open one to
Review/Draft.
scripts/sync-shared.sh --check if WORKTREE_GATE.md was touched this run (it
shouldn't be — that's a one-time repo change, not something this skill edits per-project).
1---2name: onboard-existing-app3description: Retrofits an existing Go web application into the Nexa Agentic Engineering methodology by reverse-engineering a requirements catalog, entity model, use case diagram, and use case specs from the live code (routes, handlers, views, services, SQL migrations, and queries), and from BMAD-method artifacts under `_bmad/`, when present. Then audits cross-cutting infrastructure and creates thin-pointer GitHub issues for already-shipped work. Use when the user asks to "onboard this codebase", "onboard this Go app", "onboard an existing app", "retrofit Nexa onto my project", "adopt the Nexa pipeline for an existing Go app", "reverse-engineer requirements from code", or mentions bringing a brownfield or already-built Go project under Nexa/`/deliver-use-case`/`/resolve-bug` for future work.4---56# Onboard Existing App78## Instructions910Run the onboarding pipeline for the current project — an existing Go web application with no11(or partial) Nexa artifacts. Optional `$ARGUMENTS`: `quick` or `full` to preselect scan depth12(see Step 0.4); otherwise ask.1314This produces the artifacts every other Nexa skill assumes already exist (`docs/requirements.md`,15`docs/entity_model.md`, `docs/use_cases.puml`, `docs/use_cases/UC-XXX.md`) *from the existing16code*, marks already-shipped features as such rather than queuing them for re-implementation,17and reports infrastructure gaps without touching anything that already works.1819## Prerequisites2021- `go.mod` at the repository root, and an HTTP server built on `net/http`.22- `gh` CLI authenticated, for Step 5 tracking.23- Nothing else is required. SQL migrations, `sqlc.yaml`, and `_bmad/` are optional inputs24 this skill detects and uses if present (Steps 0.1, 0.5, and 2).25- A router other than the stdlib `http.ServeMux` (chi, gin, echo, gorilla/mux) does not block26 onboarding. The routes are still crawled. Step 4 reports the router as a mismatch, because the27 plugin's conventions register every route on one `ServeMux` in `internal/web/routes.go`.2829## DO NOT3031- Modify or refactor any existing application code/behavior — this is a documentation and32 detection pass only33- Run `go generate`, `templ generate`, `sqlc generate`, or any migration — detection only reads34- Overwrite existing Nexa docs without going through the Step 0.2 three-way prompt35- Auto-run any `setup-*` skill in Step 4, ever — report the gap and recommend the command36- Assert a UC as `Status: Done` when its behavior is ambiguous in the code — use `Review`37- Trust a BMAD story's `Status: done` without independently verifying it against the live38 route/code — BMAD's record can drift from what's actually deployed39- Let parallel `use-case-archaeologist` agents write to any file except their own assigned40 `UC-XXX.md` — the orchestrator (this skill, main context) does the one sequential append41 into `docs/requirements.md` and `docs/use_cases.puml`42- Attempt entity-model reverse-engineering from ORM code alone (GORM struct tags, ent schema)43 — without SQL DDL, flag and skip instead44- Generate wireframes or design specs for existing screens — `/generate-wireframe` and45 `/design-screens` are prospective planning tools; reverse-capturing a live app's rendered UI46 as a design artifact is a distinct capability this skill deliberately doesn't attempt.47 `/evaluate`'s Design Conformance and `/audit`'s Screen Fidelity lens will simply skip for48 onboarded UCs until a design doc exists via the normal flow on future work49- Auto-generate `BUG-XXX`/`TT-XXX` docs from BMAD's `deferred-work.md` — mention counts/severity50 in the Step 6 report and point at `/report-bug` instead; that needs `resolve-bug`'s own51 reproduction rigor, not a doc-mapping pass5253## Gates5455- **No Nexa Rules Gate section here** — Step 5 of this skill is what establishes that gate's56 `<!-- NEXA_RULES_CONFIGURED v2 -->` marker via `/setup-project-rules`. Requiring it upfront57 would be circular, the same reason `setup-project-rules` itself doesn't gate on it.58- **No Worktree Gate** — this skill runs on `main` in the primary checkout, not in a work item59 worktree (documentation and read-only detection, not application feature code). It's listed in60 `${CLAUDE_PLUGIN_ROOT}/shared/readiness/WORKTREE_GATE.md`'s Exceptions section.61- **`PROJECT_READINESS.md` is audited, not gated on** — Step 4 walks its checklist and reports62 gaps; it does not block this skill from completing (it blocks *implementation* work later,63 which is exactly what Step 4's report exists to prepare the user for).6465## Pipeline6667---6869### Step 0: Discovery + Mode Selection7071#### 0.1 BMAD seed check7273If `_bmad/` exists, resolve its config for artifact paths — don't hardcode `_bmad-output/`,74it's user-configurable:7576```77cat _bmad/bmm/config.yaml 2>/dev/null || cat _bmad/core/config.yaml 2>/dev/null78```7980Extract `planning_artifacts:` and `implementation_artifacts:` values. When found, treat as a81**primary seed**, not a nice-to-have:8283- `<planning_artifacts>/prd.md` — `#### FR-N: <title>` sections, already near 1:1 with Nexa's84 `FR-XXX`.85- `<planning_artifacts>/epics.md` — `### Epic N` / `### Story N.M: <title>` — BMAD has already86 clustered work; each Story is roughly one candidate UC/TT, so Step 0.6's clustering only87 needs to run for routes no story covers.88- `<implementation_artifacts>/<epic>-<story>-<slug>.md` — per-story `Status:89 done|review|in-progress|deferred`, a `## Story` block already in `As a X, I want Y, so that90 Z` form, and `## Acceptance Criteria` in Given/When/Then referencing the same `FR-N` tags.91 `Status: done` is direct evidence for `Status: Done` on the matching UC — more reliable than92 inferring completeness from code alone — but still gets verified against the live route in93 Step 3, never transcribed blind.94- `<implementation_artifacts>/deferred-work.md` — severity-tagged, file:line-cited past review95 findings. Note the count/severity in the Step 6 report; do not act on it here (see DO NOT).9697Build a match list: `{cluster candidate → BMAD story path | none}`.9899#### 0.2 Existing-docs check (expect hybrid state)100101A real project can have partial/full Nexa docs layered on top of an earlier BMAD phase, or102neither, or both — never assume `docs/` is empty or fully-Nexa. If `docs/requirements.md`,103`docs/entity_model.md`, `docs/use_cases.puml`, or `docs/onboarding/` already exist, stop and104present:105106```107ONBOARDING: existing Nexa artifacts found108109[list what exists]110111How should I proceed?1121. Rescan — re-run discovery; skip clusters that already have a UC doc, only process newly113 discovered ones (no diffing/merging of existing docs in this version).1142. Deep-dive one area — name a specific cluster or infra concern to redo.1153. Cancel — make no changes.116```117118Wait for a reply before continuing.119120#### 0.3 Resumability121122Check for `docs/onboarding/scan-state.json`:123124```json125{ "mode": "quick|full", "clusters": [{"id": "UC-001", "status": "pending|done", "source": "bmad|code"}], "completed_steps": ["0","1"] }126```127128If present and incomplete, resume from the last unfinished cluster/step instead of129restarting. Write/update this file after every completed step and every completed cluster —130a killed or multi-session Full run should not have to start over.131132#### 0.4 Scan depth133134If not given via `$ARGUMENTS`, ask:135136```137ONBOARDING: choose scan depth1381391. Quick — full skeleton (requirements catalog, entity model, use case diagram, infra gap140 report, GitHub issues) but UC specs are cheap Draft stubs (name, primary actor, one-line141 goal) for /engineer-requirements to flesh out later. Fast, good first look or a very large142 app.1432. Full — Quick's output plus fully grounded UC specs (MSS, alt-flows, business rules cited144 to file:line), BMAD-seeded where available. The complete pipeline.145```146147#### 0.5 Crawl the codebase (read-only)148149Read `go.mod` for the module path and dependencies. Do not assume this plugin's layout150(`internal/<feature>/`, templ, sqlc, goose): an existing app can keep its component packages at151the module root, all handlers in one `web` package, and its own migration runner. Find each152item below by what the code does, not by where this plugin would put it. Then collect:153154- **Routes** — every registration with its method and pattern: stdlib155 `mux.HandleFunc("GET /items/{id}", ...)` / `mux.Handle(...)`, or the third-party router156 equivalent (`r.Get("/items/{id}", ...)` in chi, `r.GET("/items/:id", ...)` in gin/echo,157 `r.HandleFunc(...).Methods("GET")` in gorilla/mux). Record which router is in use for Step 4.158 Apps often register through a local helper that adds middleware, e.g.159 `handle := func(p string, h http.HandlerFunc) { mux.Handle(p, auth.RequireAuth(h)) }` — find160 every such helper first, then every call to it. For each route, record the full pattern161 (add the prefix of an enclosing chi `Route`/`Mount`, gin/echo `Group`, or a sub-mux behind162 `http.StripPrefix`) and the middleware its registration adds (a login or role check). That163 middleware is evidence for a precondition in Step 3.164- **Handlers** — the functions those routes call, wherever they live (one file per feature in a165 `web` package, or a `handler.go` per feature), including htmx fragment endpoints.166- **Views** — `.templ` components, or `html/template` files (`*.html`, `*.tmpl`, `*.gohtml`,167 including partials and the `//go:embed` directive that loads them) — record `html/template`168 for Step 4.169- **Services** — the business logic the handlers call, in `internal/` or in top-level170 component packages.171- **Validation** — `Validate()` methods, `go-playground/validator` struct tags172 (`validate:"required,email"`), explicit `if` guards in handlers and services, and sentinel173 domain errors that services return for a broken rule (`var ErrNameRequired = errors.New(...)`)174 together with the handler branch that maps each one to a response.175- **Schema** — every ordered stream of SQL DDL files. Find a stream by its content, not by its176 tool: a directory of `.sql` files with a version prefix (`001_create_items.sql`,177 `20240101120000_add_users.sql`, `000001_init.up.sql`) that contains `CREATE TABLE`. goose,178 golang-migrate, atlas, the `schema` path in `sqlc.yaml`, and a custom runner all produce179 this shape. For a custom runner, read the runner and the `//go:embed` directive that feeds180 it: they give the apply order and the PostgreSQL schema each stream targets (e.g.181 `migrations/app/` → `public`, `migrations/reference/` → `reference`). Record every182 stream with its target schema. Exclude a stream that only inserts data (demo data, seed183 files) — it has no `CREATE TABLE` or `ALTER TABLE`. If no stream exists but the app clearly184 uses a database (GORM, ent, sqlx with no DDL in the repo), flag this prominently and skip185 Step 2 entirely — guessing at a schema from ORM code is out of scope.186- **Queries** — sqlc queries in `db/queries/*.sql` (or the `queries` path in `sqlc.yaml`), or187 the SQL strings the services pass to pgx or `database/sql`, or their ORM calls.188- **Tests** — existing `*_test.go` files (handler tests that drive a flow through `httptest`189 are evidence of intended behavior for Step 3) and the `e2e/` directory, if present.190191#### 0.6 Cluster192193For every route/handler not already matched to a BMAD story (0.1), group into candidate use194cases by user-facing goal, not by file — e.g. `GET /checkout` + `POST /checkout` +195`POST /discount-codes/apply` (htmx fragment) → one "Checkout with Discount" cluster. Inspired196by how `engineer-requirements` clusters *existing* UC docs; here the input is raw code.197198Put routes that call the same handler in the same cluster: `GET /items` and199`GET /api/items` served by one handler are one use case with an HTML and a JSON response,200not two use cases. Do not cluster routes that no user goal needs — health checks, metrics,201static files. List them in the Step 6 report as not user-facing.202203---204205### Step 1: Reverse Requirements206207Compile one consolidated, code-derived summary across every cluster (each cluster contributes208one candidate FR: title, user story reconstructed as `As a [actor], I want [goal] so that209[benefit]`, grounded in its BMAD `FR-N` when matched, otherwise in the code). Author210`docs/requirements.md` directly, following its documented table format exactly:211212```213| ID | Title | User Story | Priority | Status |214```215216Status for every discovered FR is **Verified** — the correct terminal value for an217already-shipped requirement (`docs/requirements.md`'s status vocabulary has no "Done"). Do not218pause per-cluster to ask clarifying questions the way a fresh requirements-gathering pass219would — ground ambiguity in a note for the Step 6 report instead; this pipeline may process220many clusters and per-item interactive dialogue doesn't scale.221222---223224### Step 2: Entity Model225226Skip entirely if no SQL DDL was found (Step 0.5). Otherwise build the final schema of each227stream by replaying its up migrations in the order its runner applies them — normally the228version prefix (`CREATE TABLE`, then every later `ALTER TABLE`, `DROP`, and `RENAME`). Ignore229down migrations and goose `-- +goose Down` sections. Then author `docs/entity_model.md`230directly, following its documented format exactly: a Mermaid `erDiagram` block (relationships231only, no attributes inside entity nodes) plus one `### ENTITY_NAME` section per table with a2325-column attribute table (Attribute, Description, Data Type, Length/Precision, Validation233Rules). When the streams target more than one PostgreSQL schema, qualify every table outside234`public` with its schema (`reference.countries`), and keep foreign keys across schemas in the235diagram.236237Map PostgreSQL types to the fixed vocabulary:238239| PostgreSQL type | Data Type | Length/Precision | Validation Rules add |240|---|---|---|---|241| `bigint`, `bigserial`, `int8` | Long | 19 | — |242| `integer`, `int`, `smallint`, `serial`, `smallserial` | Integer | 10 | — |243| `text`, `varchar(n)`, `char(n)`, `citext` | String | `n` where declared | — |244| `uuid` | String | 36 | Format: UUID |245| `numeric(p,s)`, `decimal(p,s)` | Decimal | `p,s` | — |246| `real`, `double precision` | Decimal | — | — |247| `boolean` | Boolean | 1 | — |248| `date` | Date | — | — |249| `timestamp`, `timestamptz` | DateTime | — | — |250| `jsonb`, `json` | String | — | Format: JSON |251| `bytea` | String | — | Format: Binary |252| `T[]` (array) | the Data Type of `T` | as for `T` | List |253| enum (`CREATE TYPE ... AS ENUM`) | String | — | Values: A, B, C |254255For a type with no row, use the nearest Data Type, write the PostgreSQL type in Validation256Rules, and name it in the Step 6 report.257258Map `PRIMARY KEY`, `UNIQUE`, `NOT NULL`, `CHECK`, and `DEFAULT` to Validation Rules. A259single-column `CHECK (col IN ('a', 'b'))` is an enum: write `Values: a, b`. Write any other260single-column `CHECK` as its condition. Write a `CHECK` that spans columns as a261**Constraints:** line after the table, per the entity model format. Map each `FOREIGN KEY` to262Mermaid cardinality: a plain foreign key is many-to-one; a foreign key that is also `UNIQUE`263is one-to-one. A join table with two foreign keys as its primary key is many-to-many.264265Skip tables that are not part of the domain, and name them in the Step 6 report:266267- migration tracking tables of every stream (`goose_db_version`, `schema_migrations`,268 or the tracking table of a seed stream) — a runner can keep one per schema269- a session store — a table whose columns are only a key, a session token or its hash, a user270 reference or a session payload, and timestamps. Decide by the columns, not by the name: a `sessions` table271 of a training or booking domain is a domain entity.272273Every entity must trace to a real `CREATE TABLE` — never invented.274275---276277### Step 3: Use Case Diagram + Specs278279Author `docs/use_cases.puml` directly for all clusters together, following its documented280PlantUML format (`@startuml`, actors, one `rectangle` containing `usecase "UC-XXX\nDescription"281as UCXXX` per cluster).282283**Quick mode:** write minimal `Draft`-status stub UC docs directly (no subagents) — name,284primary actor, one-line goal, `Provenance: reverse-engineered, not yet elaborated` — for285`/engineer-requirements` to flesh out later.286287**Full mode:** spawn one **typed `use-case-archaeologist` subagent** per cluster, batched in288parallel (single message, N `Agent` tool calls — same pattern `resolve-bug` uses for its289Step 2 parallel pair). Invoke via the Agent tool with290`subagent_type: "nexa-claude-go:use-case-archaeologist"`. Prompt per agent:291292> Write the use case spec for cluster "[name]" — primary actor [actor], implemented in293> [routes with method, full pattern, and the middleware each registration adds; handler,294> view, service, query, and handler test files]. [If295> matched: BMAD story at [path], FR-N [n], claimed Status [status] — verify, don't transcribe.]296>297> Follow your operating manual (`onboard-existing-app/SKILL.md` Step 3, loaded as your298> identity) to the letter. Write only `docs/use_cases/UC-XXX.md`. Report the file path, the299> Status you assigned and why, a draft FR-XXX table row, any BR-XXX beyond what's in the UC300> file, and any BMAD/code discrepancy you found.301302After all agents in a batch return, do **one sequential pass**: append each returned FR-XXX303row into `docs/requirements.md` (replacing the earlier per-cluster draft from Step 1 if it304was a placeholder) and cross-reference each UC in `docs/use_cases.puml`. This is the only305point that writes to those shared files, avoiding the concurrent-write race parallel agents306would otherwise hit.307308Update `docs/onboarding/scan-state.json` after each batch (Step 0.3).309310---311312### Step 4: Infra Gap Audit (stop-and-list only — never auto-run)313314For each concern, check its own CLAUDE.md marker first (every `setup-*` skill already writes315and self-checks one, so this reuses that instead of inventing new detection):316`NEXA_ENV_PROFILES_CONFIGURED`, `NEXA_I18N_CONFIGURED`, `NEXA_WEB_MIDDLEWARE_CONFIGURED`,317`NEXA_ARCH_UNIT_CONFIGURED`, `NEXA_PLAYWRIGHT_CI_CONFIGURED`, `NEXA_QUALITY_CI_CONFIGURED`,318plus every item in `${CLAUDE_PLUGIN_ROOT}/shared/readiness/PROJECT_READINESS.md`.319320- **Marker present:** document as configured.321- **Marker absent but functionally equivalent infra clearly exists:** flag as a **mismatch** for322 human review — never stamp the marker on unverified equivalence. Typical Go mismatches:323324 | Found in the app | Plugin convention |325 |---|---|326 | component packages at the module root, all handlers in one `web` package, routes registered outside `internal/web/routes.go` | `internal/<feature>/` packages, routes in `internal/web/routes.go` |327 | chi, gin, echo, or gorilla/mux router | one `http.ServeMux` in `internal/web/routes.go` |328 | `gorilla/csrf` or another CSRF token library | `http.CrossOriginProtection` |329 | `html/template` views | templ + htmx |330 | GORM, sqlx, raw `database/sql`, or hand-written SQL over pgx | sqlc over pgx |331 | golang-migrate, atlas, or a custom migration runner | goose |332 | `godotenv` loading env files | env files sourced by the shell, read in `internal/config` |333 | zap, zerolog, or logrus | `log/slog` JSON handler |334 | an i18n library other than go-i18n | go-i18n via `/setup-i18n` |335336 A mismatch is not a defect in the app. It means the plugin's skills (`/implement`,337 `/integration-test`, `/playwright-test`) assume a structure the app does not have. List what338 that costs for future work, so the user can decide to migrate or to adapt.339340 Many `PROJECT_READINESS.md` items check an exact path (`cmd/dev/main.go`,341 `internal/web/routes.go`, `db/migrations/`, `sqlc.yaml`, `.env`). When the app has its own342 layout, do not report each of these items as a separate gap. Report them as one **Layout**343 mismatch row that names the app's equivalent for each item. Report an item as Missing only344 when the app has no equivalent at all.345- **Genuinely missing:** list as a recommended follow-up (e.g. "Run `/setup-i18n` — no i18n346 detected"). **Never auto-run any `setup-*` skill** — each one makes opinionated decisions347 (auth strategy, locale list, RBAC model) that could conflict with how this specific existing348 app already does things. This is the one place in the pipeline where "onboard" stops short349 of acting, by design.350351---352353### Step 5: Nexa Rules + Tracking354355Run `/setup-project-rules` (Skill tool) if `<!-- NEXA_RULES_CONFIGURED v2 -->` is missing from356`CLAUDE.md` — idempotent, no judgment calls, safe to always run.357358Then, per `${CLAUDE_PLUGIN_ROOT}/shared/tracking/TRACKING.md` conventions, for every UC:359360```361gh issue create --title "UC-XXX: <name>" --body "<one-line summary>362363**Spec:** [\`docs/use_cases/UC-XXX.md\`](<SPEC_URL>)"364```365366- **Status: Done** → `gh issue close <number>` immediately after creating — same as any other367 already-shipped, DoD-satisfying work.368- **Status: Review or Draft** → leave **open** — not confirmed-complete, so it shouldn't read369 as closed/done in the tracker.370371---372373### Step 6: Onboarding Report374375Write `docs/onboarding/ONBOARDING_REPORT.md`:376377```markdown378# Onboarding Report379380## Summary381[scan depth used, cluster count, router detected, BMAD seed used: yes/no]382383## Use Cases384| UC | Name | Status | Source | Notes |385|----|------|--------|--------|-------|386| UC-001 | ... | Done | BMAD story 1.2, verified | internal/order/handler.go, internal/order/views.templ |387| UC-002 | ... | Review | Code archaeology | [what couldn't be confirmed, e.g. error path in internal/invoice/service.go:88 unreachable] |388389## Entity Model390[coverage: N tables mapped from M migrations in S streams (one line per stream: directory → schema), infrastructure tables skipped, types with no row in the map, or "skipped — no SQL DDL, schema lives in GORM models"]391392## Routes Not Clustered393[routes that serve no user goal — health checks, metrics, static files]394395## Infrastructure Gaps396| Concern | Status | Recommended Action |397|---------|--------|---------------------|398| i18n | Missing | Run /setup-i18n |399| Web Middleware | Mismatch — chi router + gorilla/csrf | Review: migrate to ServeMux + CrossOriginProtection, or adapt |400| Layout | Mismatch — routes outside internal/web/routes.go, a custom migration runner, html/template | Review: migrate to the plugin layout, or adapt the skills |401| Environment Profiles | Configured | — |402403## Deferred Work (BMAD, informational only)404[N items found in deferred-work.md, by severity — consider /report-bug for High severity]405406## Before You Run /implement or /deliver-use-case407[prioritized list: Review-status UCs to confirm, infra gaps and mismatches to close, entity model gaps]408```409410Optionally draft an informal `docs/vision.md` retrospective summary — no template exists for411it anywhere in this repo, so keep it best-effort and clearly labeled as reconstructed, not412authoritative.413414---415416## Verification417418- Confirm the Nexa Rules Gate (`<!-- NEXA_RULES_CONFIGURED v2 -->`) now passes.419- Confirm `git status` shows changes only under `docs/`, `CLAUDE.md`, and nothing in Go,420 templ, or SQL files.421- Confirm every route found in Step 0.5 is in exactly one cluster or in the report's Routes Not422 Clustered list. Count the registrations in the code again, including the calls to every423 registration helper — a route that is in neither place was missed by the crawl.424- Spot-check at least one generated `UC-XXX.md` against the live handler/view it claims to425 describe — MSS steps should match actual code behavior, not paraphrase intent.426- Spot-check at least one entity in `docs/entity_model.md` against the final migration that427 shapes its table — a column added or dropped by a later `ALTER TABLE` must be reflected.428- Confirm `docs/delivery/UC-XXX-iterations.md` exists **only** for `Status: Done` UCs. This is429 what makes the project-wide "delivered" detection recognize them correctly —430 it keys off file existence, not the `Status` field. Write one with a single entry noting431 `Reverse-engineered from existing code, not delivered via the Nexa pipeline` for each Done432 UC, matching `deliver-use-case/SKILL.md`'s iterations-log format.433- Confirm every closed GitHub issue corresponds to a `Status: Done` UC and every open one to434 `Review`/`Draft`.435- `scripts/sync-shared.sh --check` if `WORKTREE_GATE.md` was touched this run (it436 shouldn't be — that's a one-time repo change, not something this skill edits per-project).