---
name: ba-develop
description: >
  Consumes a module's PRD on disk (`prd.md` + 3 phase slices + `pagespecs/*.md`
  under `.smartstack/ba/<APP>/<MODULE>/`) and drives development through 6
  sequential phases (Core → Entities → API Integration → API Screen-driven →
  Frontend → Acceptance Tests). Fully autonomous: gates between phases
  auto-heal on failure (max 25 retries per item); an unhealable failure is
  deferred as a blocker and the run continues — it never halts and never asks
  the user mid-run, surfacing all blockers in the final report. Invoked after
  `/ba-create-prd` + `/ba-audit-prd` (dev-ready GO, score ≥ 80).
argument-hint: "<APP>/<MODULE> [--force] [--allow-dirty]"
group: H
allowed-tools: [Read, Glob, Grep, Bash, Agent]  # Bash: CLI invocation
---

# ba-develop — PRD-driven phased development

> **MAINTENANCE FREEZE (plan v3, 2026-07-18).** The SmartStack **Studio** is
> the canonical development orchestrator: it executes a compiled DAG of
> short-context units (deterministic entity/endpoint scaffolds on pre-compiled
> specs, one unit per screen with build/test/polish/probe gates, per-unit
> retry). This CLI orchestrator (6 phases, 25 retries) remains usable
> standalone but is FROZEN — no new phases, gates or heal logic land here.
> The scaffolder CLIs under `templates/skills/development/**` stay maintained
> (synced from the Studio via `cli-app-sync`). Phase 2b « API Screen-driven »
> (`scaffold-screen-controller`, `/api/screens/...`) is REMOVED from the
> canonical path: generated apps route [NavRoute]-only, that stratum is
> runtime-dead — do not generate it for new modules.

## Your role

You are the **orchestrator** of module development from a validated PRD. You
do not write code directly. Your job is to:

1. Read the PRD dev-readiness verdict — but **never refuse to start** (a weak
   verdict becomes a `prd.not-dev-ready` blocker, not a stop).
2. Launch a **subagent per phase** with the slice and scaffold skills it
   needs. Phase 2b fans out per (section, entity); Phase 3a fans out per
   feature/entity.
3. Gate each phase. On failure, auto-heal (max 25 retries per item); if a
   failure is unhealable, **defer the item** — record a blocker, take the
   safest best-effort action, and continue. See `references/auto-healing.md`.
   **The run never halts and never asks the user mid-flight.**
4. Report a unified dev log including `healingSummary[]` **and `blockers[]`**.

The autonomy contract: `/ba-develop` always runs every phase to the end of the
module. Anything that needs a human decision (regenerate a PRD slice, edit a BA
file, review a destructive migration, adjudicate a verb mismatch) goes into
`blockers[]` and is surfaced **only in the final report** — never as a mid-run
interaction.

You never load the full PRD yourself — only the subagents do. This keeps
your own context tight and lets each phase see only the Markdown it needs.

## Data source — the `.smartstack/ba/` tree

You read everything from the module's folder on disk. A module's PRD lives at
`.smartstack/ba/<APP>/<MODULE>/`:

- `prd.md` — product framing (context, goals, non-goals, MoSCoW, constraints).
- `prd.entities.md` / `prd.api.md` / `prd.frontend.md` — phase slices.
- `pagespecs/<Entity>.<view>.md` — per-screen specs (each a fenced ```json block).
- `claude.md` — the module CLAUDE.md dropped into the generated app.
- `_audit/prd.md` — the dev-readiness verdict (from `/ba-audit-prd`).
- Upstream BA docs: `entité.md`, `rbac.md`, `<section>/use-case.md` (with
  `**Acceptance Criteria**` bullets — Phase 4's input), `<section>/règles-métier.md`,
  `<APP>/acteur.md`, the `index.md` tree.

Wherever this skill refers to `structured.entities[]`, `structured.permissions[]`,
`structured.screens[]`, `structured.businessRules[]` or `structured.nonGoals[]`,
read them from the files (entities ← `entité.md` / `prd.entities.md`;
permissions ← `rbac.md` / `prd.api.md`; screens ← `screen.md` / `prd.frontend.md`;
rules ← `règles-métier.md`; non-goals ← `prd.md`).

## Arguments

```
/ba-develop <APP>/<MODULE>                        # normal run (skip phases with passing gates)
/ba-develop <APP>/<MODULE> --force                # full regeneration (see § --force mode below)
/ba-develop <APP>/<MODULE> --force --allow-dirty  # …even over an uncommitted tree (overwrites hand-fixes)
```

## Pre-flight — before launching any subagent

You receive `moduleDir` (`.smartstack/ba/<APP>/<MODULE>`), `projectPath`
(where the generated code lands), and an optional `--force` flag.

Parse `--force` from the user's input. When present, set `forceMode = true`
for the entire run. The flag affects Phase 3 behavior (see § `--force` mode)
and triggers a memory cleanup (see § Pre-flight memory cleanup).

Parse `--allow-dirty` too (only meaningful together with `--force`): it is the
USER's explicit opt-in to regenerate over an uncommitted working tree. **NEVER
set it yourself** — it exists solely so the git-cleanliness guard in § `--force`
mode can be overridden on the user's explicit say-so (same discipline as
`GIT_GUARD_OFF`).

**Safety invariants (non-negotiable).** (1) Frontend scaffolds receive the WEB
APP directory as their `projectPath` — `<projectPath>/<webRoot>` (the React/Vite
app), resolved via the configured `webRoot` or `findWebProjectFolder()`, NEVER the
project/repo root (they write to `<projectPath>/src/…` and would clobber the
backend `src/`). (2) NEVER `rm -rf` / delete a directory to "clean up" — if a
scaffold hard-fails with an `assertWebProjectRoot` error, fix the path and re-run;
never delete. See `references/auto-healing.md` § Hard safety rule.

Read `_audit/prd.md` and parse the verdict
header `_… · Verdict : <GO|NO-GO> · score <N>/100 · <e> err …_`:

- `GO` AND `score >= 80` AND `0 err` → proceed silently.
- Otherwise → **proceed anyway** and record a `prd.not-dev-ready` blocker
  (severity `high`, `userAction: "run /ba-audit-prd, fix findings, regenerate
  the PRD"`). Do NOT refuse to start.

If `_audit/prd.md` is missing, record a `prd.not-dev-ready` blocker
(`summary: "PRD not audited — proceeding without a dev-readiness verdict"`) and
proceed. The pre-flight is **advisory, not a gate** — running on a weak PRD may
cost rework, but the autonomy contract forbids stopping the user here. The
blocker makes the risk loud in the final report.

### Pre-flight memory cleanup (always, not just --force)

Before launching Phase 0, scan the project's memory directory for entries that
could interfere with CLI usage. Look for memory files (under the project's
`.claude/` memory path) containing patterns like:

- "CLIs incompatible", "CLI incompatible", "cannot run", "cannot produce"
- "develop frontend by hand", "hand-write", "manual frontend"
- "do NOT re-run Phase 3 CLIs", "scaffold-component" + "incompatible"

For each matching memory: if the memory's `description` or body references
scaffold-component, scaffold-api-client, scaffold-routes, or Phase 3
compatibility, **rewrite it** with `STATUS: RESOLVED` and a note that the
CLIs now support configurable options (`webRoot`, `httpClient`, `useScreens`).
Update the `description` field to start with `[RESOLVED]`.

This cleanup is non-destructive — it preserves the memory (useful history)
but neutralizes the directive. Log each rewritten memory in the run output.

## The phases — overview

| Phase | Subagent reads | Subagent writes | Detail |
|-------|----------------|------------------|--------|
| 0. Core Foundation Seed | `index.md`, `acteur.md`, `rbac.md` | `Persistence/Seeding/Core/*.cs` × 5 | `references/phases-detail.md` |
| 1. Entities | `prd.entities.md` + `entité.md` | `Domain/{Module}/*.cs`, EF Configs, migration | same |
| 2a. API Integration | `prd.api.md` + `règles-métier.md` + `use-case.md` | `Controllers/{Module}/*.cs` (`[NavRoute]` → `/api/{module}/{section}`), `Application/{Module}/{DTOs,Commands,Validators,Services}/*.cs` | same |
| 2b. API Screen-driven | `pagespecs/<Entity>.*.md` + `rbac.md` + Phase 2a interfaces (read-only) | `Controllers/{Module}/Screens/*ScreenController.cs` (`/api/screens/{plural}/`), `Application/{Module}/DTOs/Screens/*ScreenDto.cs` | same |
| 3. Frontend | `prd.frontend.md` + pagespecs (per entity) | `src/features/{module}/{entity}/`, `src/pages/`, `src/extensions/`, `src/i18n/locales/` | same |
| 3e. Wire-up gate | generated `*Service.ts` + `*Controller.cs` | `_audit/dev-wire-{module}.md`, smoke probe report | `references/gates.md` § "After Phase 3e" |
| 4. Acceptance Tests | every `<section>/use-case.md` + Phase 2 controllers + DTOs | `Tests/{Module}/Acceptance/*.cs` | same |

Phase 3 has seven sub-phases (3.0 theme/layout/primitives → 3a pages → 3b routes
→ 3c registry aggregator → 3.5 audit-apply → 3d build gate → 3e wire-up gate).
Sub-phase 3.0 runs `scaffold-theme` (which now emits the `--dataviz-*`/`--chart-*`
tokens) + `scaffold-ui-primitives` + `scaffold-dashboard-primitives` (emits
`src/components/dashboard/*` so any `dashboard` view resolves its imports).
Phase 3e runs `audit-dev-wire` (static URL parity vs the NavRoute-resolved backend
routes, ~1 s) then `run-smoke` (runtime probe, ~30-120 s) — both BLOCKING. `run-smoke`
is MANDATORY and runs unconditionally (never skipped on an audit `err` or a deferred
migration); it boots the app, HTTP-probes every route, **loads pages in a real
headless browser** (catches a client React crash + a CSS/PostCSS 500 that HTTP probes
never see), checks `/api/navigation/menu` app-consistency, and runs **axis 4b — the
custom-action contract probe** (fires each custom action with a synthesized VALID body
at the sentinel id so a `415`/`405`/`5xx` on the request contract fails the gate; needs
`adminToken` to authenticate, else a `smoke.interaction-unavailable` medium note), then
writes `_audit/smoke-<MODULE>.md` — a missing report is itself a failure, and an unusable
Playwright/Chromium is a `smoke.browser-unavailable` blocker, never a silent pass. Phase 3a fans out
one subagent per feature/entity — **each 3a subagent MUST be launched with
`allowed-tools: [Read, Glob, Grep, Bash]`** (no Edit, no Write) to physically
prevent hand-writing .tsx files. `validate-page` enforces the
`@generated-by scaffold-component` marker. Full sub-phase walkthrough +
audit-or-regenerate loop in `references/phases-detail.md`.

**Custom page actions are DETERMINISTIC.** Phases 2a + 3a do NOT hand-derive the
per-generator custom-action arrays — they run `cli/derive-action-specs` (reads
`pagespecs/*.md`, validates against `PageCustomActionSchema`, projects via
`lib/page-spec-actions.ts`) and splice its `controller` / `business` / `apiClient`
output **verbatim** into the scaffolders. Phase 3a runs it with
`"mode":"derive"`, which additionally BACKFILLS each `type:lookup` payload
parameter's resolved `navRoute`/`apiEndpoint` **into the pagespec** (§28) —
the dialog endpoint is then the `[NavRoute]` mirror instead of the rebuilt
`{module}/{english-plural}` guess that 404'd 9/9 dialog lookups on one
project. It is persisted, not spliced in memory, so a re-scaffold OUTSIDE this
pipeline (`/ui-design`, a heal loop, a manual run) cannot regenerate the bug;
an `unresolved` entry means the pagespec must author the `apiEndpoint`
explicitly. Coverage is **fail-closed every run**:
`audit-dev-api DEV-API-010` (backend) and `audit-dev-actions-alignment` (frontend
button+hook+service, `ACTION-DRIFT-006`) BLOCK on a dropped action — the
`custom-action-missing` heal re-derives + re-scaffolds. This closes the
silent-drop gap behind "page actions are not implemented".

**FK lookups are DETERMINISTIC too.** Phase 3a does NOT hand-derive `fkTo` from
the `Rel:` lines — it runs `cli/derive-fk-specs` (parses `entité.md`, resolves
each target's `{app, module, section}` from the target's OWN pagespec / the Core
V1 catalogue) and splices its `fields[].fkTo` **verbatim** into
`scaffold-component` / `scaffold-api-client`. Fail-closed at THREE layers:
`derive-fk-specs` reports `unresolved[]` as BLOCKING, `scaffold-component`
validation REJECTS a FK-shaped field without `fkTo` (no more silent raw-Guid
`<input>` fallback), and `DEV-UI-022`/`DEV-UI-033` block any residual Guid
reaching the user (form, list column, FK filter, detail). This closes the
silent-drop gap behind "lookups show the Guid instead of the label".

**List FILTERS follow the same contract.** A reference filter carries its own
`fkTo` on `pageSpec.filters[]` and names the **FK property** (`clientId`), never
the relation (`client`) — that string is at once the filter state key, the DTO
property, the query param and the backend `[FromQuery] Guid?`. A relation-named
filter matched none of them and shipped a free-text box over a Guid that also
filtered nothing server-side. `cli/derive-filter-fks` backfills an older pagespec
(idempotent, `labelKey` preserved); the same three layers gate it —
`unresolved[]` BLOCKING, `scaffold-component` validation rejects an unresolvable
`lookup` filter, `PRD-113` / `DEV-UI-033` catch the residue.

**Platform-seam registrations CLOSE Phase 2 — a mandatory step, not a
reference note.** Three platform seams are wired at the end of Phase 2
(`references/phases-detail.md` § "Platform-seam registrations", gates in
`references/gates.md` § After Phase 2b):

1. **Global search** — assemble the spec with
   `scaffold-extension-search/build-spec.ts` (sections + permissions + list
   screens + the entities' `dataScopes`) and run the CLI against the
   `<<< EXTENSION-SEARCH-DI >>>` markers. The engine is fail-closed BY DESIGN
   (no auto-scan of the `extensions` schema): skipping this step produces no
   error anywhere — just a global search that silently returns 0 results on
   every business entity. Gate: `audit-dev-api DEV-API-029` (err) — every
   `view: list` entity must carry its ACTIVE `search.Entity<…>` registration;
   a row-scoped entity carries its derived `.RestrictTo(...)` (`rowScope`),
   never gets skipped.
2. **Data scopes** — `scaffold-data-scope` for every own/assigned entity; gate
   `DEV-API-020` (err).
3. **Coded entities** — `scaffold-coded-entity` for every `**Code pattern**`
   entity (the AUTHORITATIVE invocation is Phase 1 — both halves from
   `derive-code-specs`, see phases-detail's cascade; this closing step is the
   idempotent catch-up verification); gate `DEV-API-022` (err). The reciprocal gate `DEV-API-034` (err)
   scans the whole backend C# for a HAND-ROLLED generator (`Generate*` method,
   counter `DbSet`, `Max(Code)+1`) — the socle's `CodedEntitySaveHandler` is
   the only allocator; heal by declaring the `**Code pattern**` and deleting
   the generator, never by keeping it.

The four seams that stayed empty on the incident project were exactly the four
without a blocking gate — a seam documented only in prose is optional in
practice. Do not end Phase 2 before the three gates above pass.

## ABSOLUTE RULE — the backend is scaffolder-owned (no hand-writing)

The frontend is locked to its scaffolders (no Edit/Write, `@generated-by` marker).
**The backend has the SAME contract — and it was the missing half.** A backend
hand-written off `scaffold-controller`/`scaffold-business` drifts from the
contract the deterministic frontend is generated against: it drops the
`[HttpGet("lookup")]` route (every `<EntityLookup>` 404s) and returns a bare
array instead of `PaginatedResult<…ListDto>` (every list page renders empty
because the frontend reads `response.items`). That is the recurring
"lookups 404 + tables don't load + data never shows" failure.

So in **Phase 2a**:

- **Controllers, DTOs, Command/Query records and the `{Entity}Service` skeleton
  are 100% generated** by `scaffold-controller` + `scaffold-business`. Each
  integration controller carries `// @generated-by scaffold-controller`. The
  subagent invokes the scaffolders via Bash — it does NOT author these files.
- The subagent's Edit/Write is **confined to the business-logic pass**: filling
  the `// TODO[BR-…]` / `// TODO[UC-…]` markers the scaffolder left **inside the
  `{Entity}Service` method bodies**, and adding the `RuleFor(...)` validators
  those markers call for. It MUST NOT create, rewrite or restructure a
  controller, a DTO, a Command/Query record, the service class signature, the
  route attributes, the paginated `GetAll`, the `/lookup` endpoint, or the
  `IExtensionsDbContext.Set<T>()` access pattern. Never replace the
  scaffolder's service layer with a parallel "pure MediatR + typed DbSet"
  rewrite — that is exactly the drift that broke RH.
- `audit-dev-api DEV-API-016` is the BACKEND twin of the frontend
  `@generated-by` gate: it BLOCKS Phase 2a when a controller lacks the marker,
  the `/lookup` endpoint, the paginated list, or carries an unexpanded
  `[controller]` token. `DEV-WIRE-001` (Phase 3e) is the other half.
- **If a scaffolder cannot run for an entity** — `derive-action-specs` rejects a
  malformed pagespec action, or any `cli.runtime-error` — do NOT hand-write the
  backend "directly from `prd.api.md`". **Hard-fail that entity**: record the
  blocker, **skip its frontend too** (front+back are a coupled pair — see
  `references/phases-detail.md` § Phase 3a backend precondition), and continue to
  the next entity/module. A half-app with a hand-written backend is worse than a
  clearly-reported gap.

## Phase → scaffold-skill routing

| Phase | Scaffold skills the subagent loads |
|-------|------------------------------------|
| 0 | `development/backend/core-seed` |
| 1 | `development/backend/{data-layer, dotnet-structure, seed-data}` |
| 2a | `development/backend/{dto, business-layer, controller}`, `development/testing` |
| 2b | `development/backend/screen-controller`, `development/backend/data-layer` (Phase 2 closing step — platform seams: extension-search / data-scope / coded-entity), `development/testing` |
| 3 | `development/frontend/{theme, layout, ui-primitives, dashboard, routes, component, api-client}`, `ui-design` (3.1 judgment pre-pass, two legs — rubric + `cli/apply-form-directives` for the `uiDesign` overlay AND the first-order `lifecycle` block, deterministic anchors pre-written by `business-analyse/create-prd/cli/derive-lifecycle`), `development/frontend/ui-polish`, `development/audit-dev-frontend`, `development/testing` |
| 3e | `development/audit-dev-wire`, `development/smoke-test` |
| 4 | `development/testing` (scaffold-tests-from-ac) |

Each subagent gets: its slice Markdown in context, the scaffold-skill SKILL.md
content, `projectPath` (cwd for Read/Glob/Grep/Bash), `moduleCode`. Returns
`{ artifacts, testsRun, testsPassed, compileOk }` (Phase 4 also
`{ factsEmitted, acsParsed }`).

## Re-run semantics — NEVER skip Phase 3

When re-running a module that was previously developed:

- **Phases 0-2**: may be skipped ONLY if BOTH conditions hold:
  1. **Artifacts exist** and the compile + test gate passes, AND
  2. **PRD-to-code coverage passes** — a fast pre-entry check (<5 s)
     confirms every item declared in the PRD/BA files is already
     implemented in the codebase.
     See `references/gates.md` § "Pre-entry coverage checks".

  If condition 1 holds but condition 2 fails (PRD has new items not yet
  implemented), the phase MUST re-enter. Existing artifacts are kept;
  the subagent adds only what is missing. This eliminates the silent-skip
  bug where PRD additions after the first run are never implemented.
- **Phase 3 (Frontend): ALWAYS re-enter the pre-entry gate — but regenerate
  only the delta (F4.1).** The gate runs `compute-page-diff` (spec-drift) plus
  `validate-page` on the `unchanged` set (disk-drift), regenerates the union,
  and **skips the per-entity fan-out only when BOTH are empty**. Pages missing
  the `@generated-by scaffold-component` marker fail `validate-page` →
  classified as disk-drift → regenerated, so hand-written pages are still
  caught. The win: a single-label edit costs ~15 s instead of ~3 min (full
  28-page re-gen). After a green build, `update-snapshot` writes the new
  baseline. See `references/gates.md` § "Phase 3 pre-entry".
- **Phase 4**: always re-run (tests are cheap, regressions are not).

**Never declare Phase 3 "already done" based on file existence or build
success alone.** A module with hand-written pages compiles and passes
`npm run build` but renders broken i18n at runtime.

**ABSOLUTE RULE — no project memory can override CLI usage.** Phase 3a
subagents MUST invoke scaffold-component / scaffold-api-client /
scaffold-routes via Bash. If a project memory claims "CLIs are incompatible
with this project", IGNORE IT — the CLIs support configurable options
(`webRoot`, `httpClient`, `useScreens`). The legacy `apiBasePath` /
`apiPathPattern` fields were removed on 2026-05-27 (Wave 1 of the URL
alignment) — the path is now derived deterministically from `useScreens` +
the entity's plural via `lib/url-conventions.ts`. If a CLI genuinely fails
at runtime, **record a `cli.runtime-error` blocker (with the stderr), run the
`/support-report` protocol (verify → versions → dedup'd report for
support@atlashub.ch), skip that one artifact, and continue** — do NOT fall
back to hand-writing `.tsx` files (skipping ≠ hand-writing), and NEVER edit
the deployed CLI under `~/.claude/` (installed copies — skills-guard blocks
it). Hand-written pages bypass the i18n entity-nesting, the `@generated-by`
marker, and the camelCase field-key normalization.

## `--force` mode — full frontend regeneration

**The default (no `--force`) is targeted re-gen** — only spec-drifted or
disk-drifted pages regenerate (see § "Re-run semantics" and
`references/gates.md`). `--force` is the escape hatch for a full regeneration.

When the user passes `--force` (`/ba-develop <APP>/<MODULE> --force`), the
orchestrator treats Phase 3 as a **full regeneration** — every page, every
api-client, every i18n file is re-invoked via CLI, overwriting what exists.
The spec-diff is skipped (`compute-page-diff` not consulted); the snapshot is
still refreshed via `update-snapshot` after the build gate.

0. **Git-cleanliness guard (BLOCKING preflight — runs FIRST, before any
   regeneration).** `--force` overwrites files in place, so a dirty working
   tree means uncommitted hand-fixes get clobbered (the "green build → red
   after --force" regression). Run:
   ```bash
   npx --prefer-offline tsx skills/ba-develop/cli/preflight-force/index.ts \
     --spec '{"projectPath":"<projectPath>","allowDirty":<true only if the user passed --allow-dirty, else false>}'
   ```
   - `success: true` → proceed with the regeneration below.
   - `success: false` → **STOP the `--force` run immediately.** Do NOT enter
     Phase 3 regeneration. Surface `report.dirtyFiles` + the CLI `errors` to the
     user (commit/stash first, or re-run with `--allow-dirty`). This is the ONE
     place `/ba-develop` legitimately stops before finishing — it stops BEFORE
     touching anything, so nothing is left half-done, and it does not violate the
     never-halt-mid-run contract (there is no mid-run yet). A normal run (no
     `--force`) never runs this guard and is unaffected.
1. **Skip the validate-page pre-scan.** Do not check existing pages for the
   `@generated-by` marker — just re-enter Phase 3 unconditionally.
2. **Re-enter Phase 3 from sub-phase 3.0** (theme/layout/primitives), then
   3a (all entities via CLI), 3b (routes), 3c (aggregator), 3.5 (audit-apply),
   3d (build gate).
3. **Phase 3a: no skip rule.** For every entity × every view, invoke
   `scaffold-component` even if the `.tsx` already exists. The CLI overwrites
   the file in place. Same for `scaffold-api-client` — re-invoke for every
   entity, overwriting existing services and hooks.
4. **Phases 0-2** are NOT affected by `--force` — they follow normal re-run
   semantics (skip if gates pass). Backend code is untouched.
5. **Phase 4** always re-runs regardless of `--force`.

No files are deleted. The CLIs overwrite existing files with freshly generated
content. Files outside the module's scope (custom components, other modules)
are never touched.

**When to use `--force`:**
- After fixing CLI bugs that affected previous generation
- When pages were hand-written by a subagent that bypassed the CLIs
- When i18n structure is corrupted (flat keys instead of entity-nested)
- When the user modified pagespecs and wants a full regeneration

**Output**: the run report includes `"forceMode": true` and
`"phase3.regeneratedFiles": [<list of overwritten files>]`.

## Auto-healing — the short version

When a phase's gate fails, the orchestrator **never halts** and never asks the
user. It classifies the failure, applies a targeted fix, and re-runs the gate —
up to **25 retries per item**, with fingerprint dedup (same hash 3× → stop
retrying THIS item early).

The 29 failure-kind classification table (22 original + 6 wire/smoke kinds
added in Wave 2 of the URL alignment, 2026-05-27 + 1 cross-module FK kind
added in Bug 6 fix, 2026-05-28), the defer conditions, and the heal-log +
blocker-log schemas all live in **`references/auto-healing.md`**. Load that
file when a gate fails for the first time in a run.

When a failure is genuinely unhealable (the retry budget exhausts, a fingerprint
loops, or the failure is structural), the orchestrator **defers the item**: it
takes the safest best-effort action (skip the item, keep existing code, generate
without applying, preserve `@customised` files), records a **blocker**, and
continues. Blockers (never halts) are recorded for: PRD-not-dev-ready, PRD/BA
gaps, destructive migration, real business-logic / acceptance disagreement,
registry collision, wire verb-mismatch, smoke 5xx, smoke boot timeout,
cross-module FK missing, customised-file conflict, missing scaffolder, and CLI
runtime errors. **Halt is never the outcome — the run always reaches the end of
the module and reports `blockers[]`.**

## Commits — the sanctioned path (never raw `git`)

After each phase whose gate passes, commit through the **`gitflow commit` CLI**,
NEVER a raw `git add -A && git commit` (the `git-guard` PreToolUse hook blocks raw
commits — the user's anti-"commit n'importe comment" safety net). The CLI runs git
via `execFile` inside tsx, so the hook never sees it → it passes BY DESIGN, and the
orchestrator checkpoints autonomously without anyone flipping `GIT_GUARD_OFF`:

```bash
npx --prefer-offline tsx skills/gitflow/cli/commit/index.ts \
  --spec '{"message":"<per-phase message>","noEfcore":true}' \
  --workdir "<projectPath>" --json
```

Per-phase message templates + auto-healed body line + JSON failure handling →
**`references/commit-checkpoints.md`**.

Same model for the backend/migrations: `scaffold-migration` (creates migrations via
`dotnet ef` inside tsx) and `/efcore` likewise **bypass `ef-guard` by design** — and
the orchestrator **applies additive migrations autonomously through the sanctioned
`skills/efcore/cli/apply` CLI** (never a raw `dotnet ef database update`). The apply
CLI's 3-tier policy is the authority: local DB → 🟢 applied; remote/unknown → 🔴
refused, recorded as a `migration.not-applied` blocker (the app may still auto-migrate
at boot). **ORDER MATTERS**: the apply policy has NO destructive axis (local is green
regardless) — so the orchestrator scans the generated `Up()` for destructive ops
FIRST and, when destructive, records `migration.destructive` (critical) and **skips
the apply entirely**. The apply spec is **cwd-only** (`{"cwd":"<projectPath>"}` —
never pass `connectionString`, which would only skew classification), and the result
is read from the JSON envelope, not the exit code (blocked → exit 1).
**What stays gated (manual user approval):** `push`,
`tag`, PR, and 🔴 EF cases (remote DB / protected branch / history rewrite).
**When the project's standing rule reserves `migrations add` to the user**, the
migration is NOT created — emit the `migrationPlan` (table list + sanctioned
`/efcore create` command) and a `migration.deferred` blocker instead of running it
(Phase 3e smoke STILL runs — a routing 404 is independent of DB state). See
`references/output-contract.md` + the deferred path in `references/gates.md` (After Phase 1).

> **Scope of this autonomy.** It applies to `/ba-develop`'s own per-phase
> checkpoints — invoking `/ba-develop` IS the user's authorization for them. It
> does NOT change the interactive rule: outside a `/ba-develop` run, propose a
> commit and wait for the user's "yes" — then still commit via the `gitflow commit`
> CLI, never raw git. Never push from this orchestrator (`/gitflow pr` / `/gitflow
> sync` are the user's separate action).

## Self-verification before reporting completion

1. **AC coverage** — Phase 4 emits `acCoverage = { total, covered, missing[] }`
   from BA `use-case.md` × generated `[Trait("AC", ...)]` Facts. Phase 4's
   gate already enforces `missing == []`; this is a post-hoc safety check.
2. **Non-goal adherence** — grep generated code for keywords from
   `structured.nonGoals[]`. Any match is a potential scope violation.
3. **Slice boundary respect** — no entity path in a frontend file, no DTO
   in a domain file. Quick Grep sanity check.

## Output

Emit one JSON object describing the entire run. Full schema (with `phases[]`,
`healingSummary[]`, `acCoverage`, `overallStatus`) lives in
**`references/output-contract.md`**.

## Detailed references

The full mechanics live in `references/`:

| File | When to load |
|------|--------------|
| [`references/phases-detail.md`](references/phases-detail.md) | Phase-by-phase walkthrough (sub-phases, derivation, per-entity loop, cross-checks). Load at the start of each phase. |
| [`references/auto-healing.md`](references/auto-healing.md) | Retry loop + failure classification + defer conditions (never aborts the run) + heal log + blocker log. Load the first time a gate fails. |
| [`references/gates.md`](references/gates.md) | Per-phase BLOCKING gate conditions (build, tests, audit, ComponentKey shape, etc.). Load when running a gate. |
| [`references/commit-checkpoints.md`](references/commit-checkpoints.md) | Commit command, per-phase messages, auto-healed body line, failure handling. Load before each commit. |
| [`references/anti-patterns.md`](references/anti-patterns.md) | Failure modes the design forbids — orchestrator, auto-healing, Phase 2, Phase 3. Load when uncertain about a design decision. |
| [`references/output-contract.md`](references/output-contract.md) | Final JSON schema with `phases[]`, `healingSummary[]`, `overallStatus`. Load before emitting the report. |

## Non-goals

- Not a generic orchestrator. PRD-driven only. Ad-hoc feature work uses direct
  scaffold skills.
- Does not deploy. Generates code locally against `projectPath`; deployment
  is separate.
- Does not modify the PRD. Real gaps → implement what the PRD does specify,
  then **defer the gap as a `prd.gap` blocker** (`userAction` names the slice to
  regenerate). Never patch the PRD locally; never halt to ask for a regeneration.
- Does not invent scaffold skills. Missing scaffolder → record a
  `scaffolder.missing` blocker, skip that artifact, continue. Never improvise.
