# Feature: Scaffold contract and the between-stories check

<!-- toc -->
- [The command](#the-command)
- [What a scaffold skill must do](#what-a-scaffold-skill-must-do)
- [The skeleton check](#the-skeleton-check)
- [Between stories: build, smoke, demo](#between-stories-build-smoke-demo)
- [Remote repository](#remote-repository)
- [Ledger](#ledger)
- [Limits](#limits)
- [Reference](#reference)
<!-- /toc -->

**Pattern**: a new project is only a good starting point if it is already green
by the same commands every later change is judged by. The pipeline does not
write project code here. It calls the enabled toolkit's `<toolkit>:scaffold`
skill, which owns the stack's layout and conventions, and then verifies the
result with the stack adapter's build, test and lint commands and the evidence
tooling the rest of the pipeline uses. After that, every story ends with the
app still building, passing, linting and running its demo; a story that
leaves it otherwise stops the next one from starting.

## The command

`/multi-agent:scaffold <ios|android|web|backend> <name> [--dir <path>]`
(`commands/multi-agent/scaffold/SKILL.md`, frontmatter contract fields per
`_command-contract.mjs`: one required enum, one required text, one path flag;
`gui: form`, not destructive).

| Step | What happens | Who |
|---|---|---|
| Resolve | `ai-<stack>-toolkit:scaffold` (`web` → `ai-frontend-toolkit`). Not available: stop, the plugin is not enabled (`/multi-agent:stack`). | pipeline |
| Dispatch | the Skill tool with the name and the target directory | pipeline |
| Create | the skeleton, `.scaffold.json`, a `.gitignore` covering `.pipeline/`, `git init` | toolkit skill |
| Verify | `scaffold-gate.mjs --dir <dir> --phase skeleton` | pipeline |
| Commit | `chore: scaffold <name>`, only after the gate exits 0 | pipeline |
| Remote | a separate question after the commit, run only on an explicit yes | person |

Dispatch follows the plugin-skill contract in `component-dispatch.md`: the
skill is resolved by name through the Skill tool, a missing skill is a halt
with the plugin to enable, never a fallback to hand-written files, and the
skill neither reads nor writes `agent-state.json`. The pipeline's side is
classification, dispatch and verification.

## What a scaffold skill must do

Each toolkit's `scaffold` skill (the `multi-agent-plugins` marketplace, authored
in the plugin repo: `build-stack-plugins.mjs` regenerates only the `knowledge/`
layer) states in its own SKILL.md what it creates, the build, test and lint
commands that must pass, and that it never creates a remote. The pipeline holds
it to this:

- Writes into an empty or new directory only.
- Includes at least one real test, so the test count is not zero.
- Writes `.scaffold.json` (`schemas/scaffold-manifest.schema.json`):

  ```json
  { "version": "1.0.0", "toolkit": "ai-ios-toolkit", "name": "SampleApp",
    "stack": "ios", "vars": { "scheme": "SampleApp-Package", "simulator": "iPhone 17 Pro" },
    "demo": { "command": "swift run SampleAppDemo", "expect": "SampleApp ready, modules: [0-9]+" } }
  ```

  `stack` is an adapter id in `schemas/stack-adapters.json`; `vars` fills that
  adapter's `{slot}`s. The gate sets `worktreePath` to the skeleton and a new
  `resultBundle` per run itself, because xcodebuild will not overwrite one; a
  `vars` entry of either name is ignored. `demo` is a command that terminates on its own and a
  pattern its output must match. A `remote` key is refused.
- Runs `git init`, makes no commit, adds no remote, pushes nothing.

## The skeleton check

`scaffold-gate.mjs --phase skeleton` passes when all of these hold:

| Check | Pass |
|---|---|
| `manifest` | `.scaffold.json` is valid and names an applicable adapter |
| `no-remote` | `git remote` lists nothing |
| `first-commit` | the repository has no commit yet: the proof comes before it |
| `build` | the adapter's build command exits 0 and `evidence-gate.mjs --claim build --stack` accepts the log |
| `test` | the adapter's test command exits 0 and `evidence-gate.mjs --claim test --stack` accepts the log, counts included (`test-summary.mjs`): zero tests executed fails. Gradle prints no counts on success, so for an adapter that reads JUnit XML the `test-results` directories are summed as well |
| `lint` | the adapter's lint command exits 0 |
| `demo` | when the manifest declares one: exit 0 and the output matches `expect` |

Commands come from the adapter (`_stack-adapter.mjs commandFor`), never from
the skill: `ios` builds and tests through `xcodebuild` on the package scheme
and lints with `swift format lint`, `android` through Gradle (`assembleDebug`,
`testDebugUnitTest`, `lintDebug`), `web` and `backend-node` through the
package's `build`, `test` and `lint` scripts with the repo's own package
manager. Every log is kept under `<dir>/.pipeline/scaffold/`. Exit 0 pass, 1 a
check failed, 2 usage, 3 manifest missing or invalid.

A failed check goes back to the scaffold skill once with the failing checks and
their logs; a second failure leaves the skeleton uncommitted and reports it.
The pipeline does not edit the skeleton to make a check pass.

## Between stories: build, smoke, demo

A story is not done because its code exists; the app has to still work and be
shown working. For a repository with `.scaffold.json` at its root, Phase 2 runs
the story check at entry and at exit:

```bash
node "$HOME/.claude/scripts/scaffold-gate.mjs" --dir "$WORKTREE" --phase story --story "$ITEM_ID"
```

The same build, test and lint checks as the skeleton, plus `demo`, which is
required here (a manifest without one is exit 3), and without the
`first-commit` check.

| Exit | At entry | At exit |
|---|---|---|
| 0 | the story starts | the story is done |
| 1 | the previous story left the app broken or not demoable: this one does not start, the run halts with the failing checks | a Phase 2 gate failure, like a red build |
| 2 | a call error: fix the arguments; never read as a pass | the same |
| 3 | `.scaffold.json` missing, unreadable, invalid or without `demo`: the run halts, nothing is built on a manifest the gate cannot read | the same |

The idea of checking that the app builds, passes a smoke and can be
demonstrated after every story, before the next begins, comes from GPT-Pilot.
Only the idea is taken; this is a separate implementation built on the
pipeline's own adapter and evidence tooling.

## Remote repository

Creating the hosted repository is an outward write and is never part of the
scaffold. `/multi-agent:scaffold` asks once, after the first commit, and runs
`gh repo create` only on an explicit yes to the exact command shown. With
`MULTI_AGENT_UNATTENDED=1` the question is never asked, and agent-guard's
unattended policy refuses the write anyway: `gh repo create`, a mutating
`gh api` call (for example `-X POST user/repos`) and `git push` are all
blocked (`scripts/unattended_policy.py`, `GH_WRITES`; asserted in
`test/scaffold-gate.test.mjs`).

## Ledger

With the quality gates active (`lib/unattended.mjs`, `gatesActive`) the verdict
is recorded as `scaffold/skeleton` or `scaffold/story` through
`gate-ledger.mjs`, next to the `evidence-gate/build` and `evidence-gate/test`
entries evidence-gate.mjs writes for the same logs. Neither scaffold entry is
mandatory at commit: a repository that was not scaffolded has nothing for them
to check. A skeleton has no HEAD before its first commit, so its entries carry
no sha, and an unattended first commit is refused by the commit hook's ledger
check. Scaffolding is a flow with a person at it, or a terminal autopilot run.

## Limits

- The demo is a command and a pattern. It shows the app starts and reaches a
  known state; it does not judge whether the story's behaviour is right, which
  is what the tests and the review are for.
- The iOS check needs Xcode and the simulator named in `vars`; a machine
  without them fails the build or test check rather than skipping it.
- A lint command the stack does not declare (`go`, `rust`, `python`, `jvm`
  today) fails the `lint` check; those stacks have no scaffold skill.

## Reference

Scripts: `scaffold-gate.mjs`, `_stack-adapter.mjs` (`lint` command kind),
`evidence-gate.mjs`, `test-summary.mjs`, `gate-ledger.mjs`. Schema:
`schemas/scaffold-manifest.schema.json`, `schemas/stack-adapters.json`.
Command: `commands/multi-agent/scaffold/SKILL.md`. Tests:
`test/scaffold-gate.test.mjs`, `test/stack-adapter.test.mjs`.
