nitm-ai-dev-toolkit 4.0.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -1
- package/README.md +60 -4
- package/bin/cli.js +152 -25
- package/bin/factory-archive.js +71 -0
- package/bin/factory-prune.js +79 -0
- package/bin/factory-watch.js +509 -0
- package/package.json +1 -1
- package/src/monorepo/.agents/skills/factory/SKILL.md +593 -0
- package/src/monorepo/.claude/agents/admin-portal.agent.md +1 -1
- package/src/monorepo/.claude/agents/api-specialist.agent.md +1 -1
- package/src/monorepo/.claude/agents/backend-architect.agent.md +1 -1
- package/src/monorepo/.claude/agents/documenter.agent.md +1 -1
- package/src/monorepo/.claude/agents/factory-approver.agent.md +12 -0
- package/src/monorepo/.claude/agents/factory-builder.agent.md +12 -0
- package/src/monorepo/.claude/agents/factory-reviewer-code.agent.md +12 -0
- package/src/monorepo/.claude/agents/factory-reviewer-security.agent.md +12 -0
- package/src/monorepo/.claude/agents/factory-reviewer-ui.agent.md +12 -0
- package/src/monorepo/.claude/agents/factory-reviewer-ux.agent.md +12 -0
- package/src/monorepo/.claude/agents/factory-spec-writer.agent.md +12 -0
- package/src/monorepo/.claude/agents/feature-builder.agent.md +1 -1
- package/src/monorepo/.claude/agents/frontend-developer.agent.md +1 -1
- package/src/monorepo/.claude/agents/green.agent.md +1 -1
- package/src/monorepo/.claude/agents/implementer.agent.md +1 -1
- package/src/monorepo/.claude/agents/planner.agent.md +1 -1
- package/src/monorepo/.claude/agents/red.agent.md +1 -1
- package/src/monorepo/.claude/agents/refactor.agent.md +1 -1
- package/src/monorepo/.claude/agents/researcher.agent.md +1 -1
- package/src/monorepo/.claude/agents/reviewer.agent.md +1 -1
- package/src/monorepo/.claude/agents/tdd.agent.md +1 -1
- package/src/monorepo/.claude/agents-snippets/factory-approver.md +72 -0
- package/src/monorepo/.claude/agents-snippets/factory-builder.md +101 -0
- package/src/monorepo/.claude/agents-snippets/factory-reviewer-code.md +57 -0
- package/src/monorepo/.claude/agents-snippets/factory-reviewer-security.md +48 -0
- package/src/monorepo/.claude/agents-snippets/factory-reviewer-ui.md +64 -0
- package/src/monorepo/.claude/agents-snippets/factory-reviewer-ux.md +64 -0
- package/src/monorepo/.claude/agents-snippets/factory-spec-writer.md +119 -0
- package/src/monorepo/.claude/agents-snippets/feature-builder.md +1 -1
- package/src/monorepo/.claude/commands/factory.md +24 -0
- package/src/monorepo/.claude/prompt-snippets/assign-tasks.md +6 -2
- package/src/monorepo/.claude/prompt-snippets/bootstrap.md +22 -13
- package/src/monorepo/.claude/prompt-snippets/factory.md +20 -0
- package/src/monorepo/.claude/prompt-snippets/implement-feature.md +2 -2
- package/src/monorepo/.claude/prompt-snippets/tasks.md +49 -87
- package/src/monorepo/.claude/rules-snippets/documentation.md +8 -6
- package/src/monorepo/.cursor/agents/factory-approver.agent.md +11 -0
- package/src/monorepo/.cursor/agents/factory-builder.agent.md +11 -0
- package/src/monorepo/.cursor/agents/factory-reviewer-code.agent.md +11 -0
- package/src/monorepo/.cursor/agents/factory-reviewer-security.agent.md +11 -0
- package/src/monorepo/.cursor/agents/factory-reviewer-ui.agent.md +11 -0
- package/src/monorepo/.cursor/agents/factory-reviewer-ux.agent.md +11 -0
- package/src/monorepo/.cursor/agents/factory-spec-writer.agent.md +11 -0
- package/src/monorepo/.cursor/commands/factory.md +24 -0
- package/src/monorepo/.github/agents/factory-approver.agent.md +11 -0
- package/src/monorepo/.github/agents/factory-builder.agent.md +11 -0
- package/src/monorepo/.github/agents/factory-reviewer-code.agent.md +11 -0
- package/src/monorepo/.github/agents/factory-reviewer-security.agent.md +11 -0
- package/src/monorepo/.github/agents/factory-reviewer-ui.agent.md +11 -0
- package/src/monorepo/.github/agents/factory-reviewer-ux.agent.md +11 -0
- package/src/monorepo/.github/agents/factory-spec-writer.agent.md +11 -0
- package/src/monorepo/.github/prompts/factory.prompt.md +25 -0
- package/src/monorepo/.opencode/agents/factory-approver.md +20 -0
- package/src/monorepo/.opencode/agents/factory-builder.md +20 -0
- package/src/monorepo/.opencode/agents/factory-reviewer-code.md +20 -0
- package/src/monorepo/.opencode/agents/factory-reviewer-security.md +20 -0
- package/src/monorepo/.opencode/agents/factory-reviewer-ui.md +20 -0
- package/src/monorepo/.opencode/agents/factory-reviewer-ux.md +20 -0
- package/src/monorepo/.opencode/agents/factory-spec-writer.md +20 -0
- package/src/monorepo/.opencode/commands/factory.md +29 -0
- package/src/monorepo/AGENTS.md +22 -1
- package/src/monorepo/README.md +6 -0
- package/src/monorepo/factory/README.md +194 -0
- package/src/monorepo/factory/_gitignore +5 -0
- package/src/repo/.agents/skills/factory/SKILL.md +593 -0
- package/src/repo/.claude/agents/admin-portal.agent.md +1 -1
- package/src/repo/.claude/agents/api-specialist.agent.md +1 -1
- package/src/repo/.claude/agents/backend-architect.agent.md +1 -1
- package/src/repo/.claude/agents/documenter.agent.md +1 -1
- package/src/repo/.claude/agents/factory-approver.agent.md +12 -0
- package/src/repo/.claude/agents/factory-builder.agent.md +12 -0
- package/src/repo/.claude/agents/factory-reviewer-code.agent.md +12 -0
- package/src/repo/.claude/agents/factory-reviewer-security.agent.md +12 -0
- package/src/repo/.claude/agents/factory-reviewer-ui.agent.md +12 -0
- package/src/repo/.claude/agents/factory-reviewer-ux.agent.md +12 -0
- package/src/repo/.claude/agents/factory-spec-writer.agent.md +12 -0
- package/src/repo/.claude/agents/feature-builder.agent.md +1 -1
- package/src/repo/.claude/agents/frontend-developer.agent.md +1 -1
- package/src/repo/.claude/agents/green.agent.md +1 -1
- package/src/repo/.claude/agents/implementer.agent.md +1 -1
- package/src/repo/.claude/agents/planner.agent.md +1 -1
- package/src/repo/.claude/agents/red.agent.md +1 -1
- package/src/repo/.claude/agents/refactor.agent.md +1 -1
- package/src/repo/.claude/agents/researcher.agent.md +1 -1
- package/src/repo/.claude/agents/reviewer.agent.md +1 -1
- package/src/repo/.claude/agents/tdd.agent.md +1 -1
- package/src/repo/.claude/agents-snippets/factory-approver.md +72 -0
- package/src/repo/.claude/agents-snippets/factory-builder.md +100 -0
- package/src/repo/.claude/agents-snippets/factory-reviewer-code.md +57 -0
- package/src/repo/.claude/agents-snippets/factory-reviewer-security.md +48 -0
- package/src/repo/.claude/agents-snippets/factory-reviewer-ui.md +64 -0
- package/src/repo/.claude/agents-snippets/factory-reviewer-ux.md +64 -0
- package/src/repo/.claude/agents-snippets/factory-spec-writer.md +117 -0
- package/src/repo/.claude/agents-snippets/feature-builder.md +1 -1
- package/src/repo/.claude/commands/factory.md +24 -0
- package/src/repo/.claude/prompt-snippets/assign-tasks.md +6 -2
- package/src/repo/.claude/prompt-snippets/bootstrap.md +30 -20
- package/src/repo/.claude/prompt-snippets/factory.md +20 -0
- package/src/repo/.claude/prompt-snippets/implement-feature.md +2 -2
- package/src/repo/.claude/prompt-snippets/tasks.md +49 -112
- package/src/repo/.cursor/agents/factory-approver.agent.md +11 -0
- package/src/repo/.cursor/agents/factory-builder.agent.md +11 -0
- package/src/repo/.cursor/agents/factory-reviewer-code.agent.md +11 -0
- package/src/repo/.cursor/agents/factory-reviewer-security.agent.md +11 -0
- package/src/repo/.cursor/agents/factory-reviewer-ui.agent.md +11 -0
- package/src/repo/.cursor/agents/factory-reviewer-ux.agent.md +11 -0
- package/src/repo/.cursor/agents/factory-spec-writer.agent.md +11 -0
- package/src/repo/.cursor/commands/factory.md +24 -0
- package/src/repo/.github/agents/factory-approver.agent.md +11 -0
- package/src/repo/.github/agents/factory-builder.agent.md +11 -0
- package/src/repo/.github/agents/factory-reviewer-code.agent.md +11 -0
- package/src/repo/.github/agents/factory-reviewer-security.agent.md +11 -0
- package/src/repo/.github/agents/factory-reviewer-ui.agent.md +11 -0
- package/src/repo/.github/agents/factory-reviewer-ux.agent.md +11 -0
- package/src/repo/.github/agents/factory-spec-writer.agent.md +11 -0
- package/src/repo/.github/prompts/factory.prompt.md +25 -0
- package/src/repo/.opencode/agents/factory-approver.md +20 -0
- package/src/repo/.opencode/agents/factory-builder.md +20 -0
- package/src/repo/.opencode/agents/factory-reviewer-code.md +20 -0
- package/src/repo/.opencode/agents/factory-reviewer-security.md +20 -0
- package/src/repo/.opencode/agents/factory-reviewer-ui.md +20 -0
- package/src/repo/.opencode/agents/factory-reviewer-ux.md +20 -0
- package/src/repo/.opencode/agents/factory-spec-writer.md +20 -0
- package/src/repo/.opencode/commands/factory.md +29 -0
- package/src/repo/AGENTS.md +19 -1
- package/src/repo/README.md +6 -0
- package/src/repo/factory/README.md +194 -0
- package/src/repo/factory/_gitignore +5 -0
|
@@ -0,0 +1,593 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: factory
|
|
3
|
+
description: Opt-in software factory for feature-sized work. Runs one job through intake questions, a spec gate, a plan gate, build in an isolated git worktree (or in place on request), parallel reviews by lens, approval, and a local merge. Feature docs live in docs/features/, job state in factory/jobs/, and tickets (when the project tracks them) follow the job. Invoked as /factory with a request (intake), start, approve, rework, abandon, status, watch, archive, or prune. Quick fixes stay direct.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Factory (orchestrator)
|
|
7
|
+
|
|
8
|
+
One orchestrator session runs a feature through intake → spec → plan → build → review → approve → merge. Agents get their context from files and each agent writes only its own file. You are the orchestrator: you ask the intake questions, dispatch agents, read their files, rewrite `job.json`, run the gates with the operator, handle tickets, and run the merge. You never write code, specs, plans, reviews, or `qa.md` yourself. Exceptions: you write `intake.md` from the operator's answers, `spec-<k>.md` and `plan-<k>.md` from the operator's change requests, `tasks.md` ticket keys, the feature folder rename and copy, the feature docs commit on the job branch, worktree and branch removal, the moves and branch deletions in Archive, `decision.md` with `ESCALATE` when retries run out, `merge-conflict.md` on a merge conflict, and `rework-<k>.md` from the user's note.
|
|
9
|
+
|
|
10
|
+
The factory is opt-in and sits alongside Feature Builder and TDD. Nothing runs unless the user invokes `/factory`.
|
|
11
|
+
|
|
12
|
+
## Placeholders
|
|
13
|
+
|
|
14
|
+
Bootstrap replaces these when it adopts the template. The names below have no braces, so bootstrap and doctor skip them. Wherever this skill uses a name from the table wrapped in double curly braces, the project's value replaces it. Any other double-curly-brace token in this skill is an error.
|
|
15
|
+
|
|
16
|
+
| Placeholder | Meaning | Default |
|
|
17
|
+
| --- | --- | --- |
|
|
18
|
+
| DEFAULT_BRANCH | Branch jobs start from and merge into | `main` |
|
|
19
|
+
| FACTORY_PORT_BASE | Base dev-server port for job ports | `5100` |
|
|
20
|
+
| APPROVAL_MODE | `human` stops at `awaiting-human`; `auto` merges on `APPROVE` | `human` |
|
|
21
|
+
| FACTORY_REVIEW_LENSES | Comma list drawn from `code, security, ux, ui` | `code, security` |
|
|
22
|
+
|
|
23
|
+
## When to use
|
|
24
|
+
|
|
25
|
+
- **Use** for feature-sized work: several files, needs a spec and review.
|
|
26
|
+
- **Stay direct** for quick fixes, typos, single-file edits, and mechanical refactors. Ponytail applies to everything: the spec asks for the minimum, and the builder ships the shortest working diff.
|
|
27
|
+
- **Prerequisite:** the target project's `AGENTS.md` documents setup, dev, test, lint, and build commands, plus how the dev server takes a port flag or env var. Agents read those commands there. If any are missing, stop and ask the user to add them. The `ux` and `ui` lenses also need a browser tool available to the harness (for example a browser MCP server). Without one they report `Not verified`.
|
|
28
|
+
|
|
29
|
+
## Invocations
|
|
30
|
+
|
|
31
|
+
| Invocation | Action |
|
|
32
|
+
| --- | --- |
|
|
33
|
+
| `/factory [--in-place] <request>` | Intake (see Intake): read the project, ask only glaring questions, then continue as `start` |
|
|
34
|
+
| `/factory start [--in-place] <request \| KEY \| slug \| job-id>` | Resolve the feature folder, or resume its open job (see Start). `--in-place` applies only to a new job |
|
|
35
|
+
| `/factory approve <job-id>` | Merge a job in `awaiting-human` or `needs-human`; on a `merged` job, move its tickets to Done |
|
|
36
|
+
| `/factory rework <job-id> <note>` | Write a rework note, start a fresh builder, reset the retry budget |
|
|
37
|
+
| `/factory abandon <job-id>` | Close a job that will not continue. Nothing is merged. The worktree and branch are removed only when the branch has no unmerged commits (see Abandon) |
|
|
38
|
+
| `/factory status [--all]` | Summarize open jobs from their `job.json` (read-only). Closed jobs are hidden unless `--all` (see Status) |
|
|
39
|
+
| `/factory watch [--all]` | Show the status table, then the command for the live view to run in a terminal (read-only). `--all` as for status |
|
|
40
|
+
| `/factory archive [--all] [--dry-run]` | Move closed jobs to `factory/archive/` (see Archive). Open jobs never move |
|
|
41
|
+
| `/factory prune [--all] [--abandoned] [--dry-run]` | Permanently delete closed jobs from `factory/jobs/` and `factory/archive/` (see Prune). Open jobs and `docs/features/` are never touched |
|
|
42
|
+
|
|
43
|
+
A first argument of `start`, `approve`, `rework`, `abandon`, `status`, `watch`, `archive`, or `prune` selects its row. Anything else is a request for intake. A bare job id that matches an existing `factory/jobs/<id>/` or `factory/archive/<id>/` is not a request: it goes to Start. An archived job is closed. `rework` and `abandon` refuse it. `approve` on an archived `merged` job only moves its tickets to Done, and merges nothing.
|
|
44
|
+
|
|
45
|
+
## Layout
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
docs/features/<KEY>-<slug>/ tracked by the project; feature docs
|
|
49
|
+
spec.md spec-writer, spec mode (from the feature spec template)
|
|
50
|
+
plan.md spec-writer, plan mode: approach, steps, and the **Size:** line
|
|
51
|
+
tasks.md spec-writer, plan mode, large work only
|
|
52
|
+
qa.md builder, on the branch: suggested manual checks (not a gate)
|
|
53
|
+
factory/
|
|
54
|
+
README.md setup and reference
|
|
55
|
+
.gitignore ignores jobs/ and archive/
|
|
56
|
+
jobs/ runtime state, ignored by git
|
|
57
|
+
<NNN-slug>/
|
|
58
|
+
job.json orchestrator only; rewrite the whole file
|
|
59
|
+
intake.md you, from the intake questions and answers (only when asked)
|
|
60
|
+
spec-<k>.md you, from the operator's change request at the spec gate
|
|
61
|
+
plan-<k>.md you, from the operator's change request at the plan gate
|
|
62
|
+
build.md builder; cumulative round log
|
|
63
|
+
round-<N>/review-<lens>.md one per configured lens
|
|
64
|
+
decision.md approver (or you, on escalation)
|
|
65
|
+
merge-conflict.md you, on a merge conflict (blocks approve)
|
|
66
|
+
rework-<k>.md you, from the user's note
|
|
67
|
+
main-copies/ feature docs moved aside at merge (see Merge procedure)
|
|
68
|
+
archive/ closed jobs moved here by /factory archive, ignored by git
|
|
69
|
+
<NNN-slug>/ same layout as jobs/; never resumed
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`<KEY>` is an issue key in the project's issue key format (`^[A-Z][A-Z0-9]+-\d+$`, as in `PROJ-1234`, the format `AGENTS.md` documents). Without a ticket, the folder is `docs/features/<slug>/` and gets its key later if tracking starts. Feature folders always live in the repository root's `docs/features/`. In a monorepo they are not per app: `apps/<app>/docs/features/` is never used for factory jobs.
|
|
73
|
+
|
|
74
|
+
### job.json
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"id": "001-onboarding-bootstrap",
|
|
79
|
+
"feature": "Onboarding bootstrap for new workspaces",
|
|
80
|
+
"feature_dir": "docs/features/PROJ-1234-onboarding-bootstrap",
|
|
81
|
+
"issue_key": "PROJ-1234",
|
|
82
|
+
"size": "large",
|
|
83
|
+
"tracking": "tracked",
|
|
84
|
+
"tracker_states": { "in_progress": "In Progress", "in_review": "In Review", "done": "Done" },
|
|
85
|
+
"tracker_errors": [],
|
|
86
|
+
"branch": "factory/001-onboarding-bootstrap",
|
|
87
|
+
"worktree": true,
|
|
88
|
+
"worktree_path": "/abs/path/to/repo-factory/001-onboarding-bootstrap",
|
|
89
|
+
"failed_stage": null,
|
|
90
|
+
"stage": "awaiting-plan",
|
|
91
|
+
"round": 1,
|
|
92
|
+
"lenses": ["code", "security"],
|
|
93
|
+
"reviews": { "code": "pending", "security": "pending" },
|
|
94
|
+
"approvalMode": "human",
|
|
95
|
+
"createdAt": "2026-10-20T09:00:00Z",
|
|
96
|
+
"updatedAt": "2026-10-20T09:05:00Z"
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
- `feature_dir`: repo-relative path of the feature folder. It changes only on the key rename (see Tracking).
|
|
101
|
+
- `issue_key`: the job's ticket key, or `null` when the job has none. A job started from a key has it from the start. A parent ticket created at the plan gate sets it.
|
|
102
|
+
- `size`: `small`, `large`, or `unclear`. Set from `plan.md` (see Plan).
|
|
103
|
+
- `tracking`: `none` or `tracked`.
|
|
104
|
+
- `tracker_states`: the project's status names, asked once when tracking first needs them. `null` until then.
|
|
105
|
+
- `tracker_errors`: failed ticket transitions or creations, as short strings. Reported, never blocking.
|
|
106
|
+
- `worktree`: `true` (default) builds in a git worktree. `false` builds in place in the main checkout (see In-place mode). Set when the job is created; changed only at the plan gate.
|
|
107
|
+
- `worktree_path`: absolute path of the job's worktree, or `null` for an in-place job and before the build starts.
|
|
108
|
+
- `failed_stage`: `spec` or `plan` when the job is `needs-human` because its spec or plan agent failed, else `null`. `/factory start` reruns that stage (see Start).
|
|
109
|
+
- `reviews` values are `pending`, `PASS`, or `CHANGES`.
|
|
110
|
+
|
|
111
|
+
Stages, in order: `spec` (spec-writer running), `awaiting-spec` (spec gate), `plan` (plan writer running), `awaiting-plan` (plan gate), `build`, `review`, `approve`, `awaiting-human`, `merged`, `needs-human`. `abandoned` is set by `/factory abandon`, from any open stage. Set `updatedAt` on every write. Use `date -u +%Y-%m-%dT%H:%M:%SZ`.
|
|
112
|
+
|
|
113
|
+
A job is **closed** when its stage is `merged` or `abandoned`. Every other job is **open**, and every open-job check in this skill uses that definition. Closed jobs that `/factory archive` moved to `factory/archive/` are archived (see Archive). A job **holds its build location** when it is open and has reached the build: its stage is `build`, `review`, `approve`, or `awaiting-human`, or `needs-human` with `failed_stage` `null`. A worktree job holds a worktree, and an in-place job holds the main checkout, for as long as it holds its build location.
|
|
114
|
+
|
|
115
|
+
## Intake
|
|
116
|
+
|
|
117
|
+
`/factory <request>` starts here. Intake writes nothing until its questions are answered, so the operator can answer before any folder, job, or spec exists.
|
|
118
|
+
|
|
119
|
+
1. **Analyze, read-only.** Read the project's `AGENTS.md`. List `docs/features/` and read the title and Summary of each `spec.md` whose name or text matches the request's keywords, looking for related or overlapping features. Skim the code the request touches with glob and grep. Work out the feature folder the way Start steps 1 and 2 would, without creating anything.
|
|
120
|
+
2. **Ask only glaring questions.** Ask at most five, in chat, as a numbered list, then wait. Ask only when one of these holds:
|
|
121
|
+
- The scope is ambiguous: two readings lead to different specs.
|
|
122
|
+
- The request conflicts with or duplicates an existing feature in `docs/features/`.
|
|
123
|
+
- The target user, page, or system is missing.
|
|
124
|
+
- No acceptance is implied: there is no way to tell when it is done.
|
|
125
|
+
|
|
126
|
+
Do not ask about details the spec writer can decide and flag as `[PRODUCT DECISION]`, and do not ask for taste. If nothing is glaring, say so in one line ("No glaring questions; starting the job.") and continue to step 4. If the request resolves to an existing feature folder, still ask, then continue that feature.
|
|
127
|
+
3. **Intake gate.** Only when questions were asked. Once the operator has answered, show the questions and answers and ask in chat as a numbered choice:
|
|
128
|
+
1. Continue to spec and plan.
|
|
129
|
+
2. Make changes: the operator types them.
|
|
130
|
+
|
|
131
|
+
Changes → revise the Q&A with the operator's text, show it, and ask again. Nothing is created until the operator picks 1.
|
|
132
|
+
4. **Continue as Start.** Treat the answers as clarifications of the request. Run Start from step 1 with the request text. Once the job folder exists (Start step 4), write `factory/jobs/<id>/intake.md` with the questions as asked and the answers, verbatim, one question and answer each. Skip the file when nothing was asked. The spec writer reads it (paths block). If Start resumes an open job instead of creating one, say the answers were not applied. Changes after the spec go through the spec gate, the plan gate, or `/factory rework`.
|
|
133
|
+
|
|
134
|
+
`--in-place` on the command, or the operator's answer saying so, sets `worktree` to `false` on the new job (see In-place mode).
|
|
135
|
+
|
|
136
|
+
## Start
|
|
137
|
+
|
|
138
|
+
Start runs for `/factory start <request | KEY | slug | job-id>`, and intake runs it after its gate.
|
|
139
|
+
|
|
140
|
+
### 1. Classify the argument
|
|
141
|
+
|
|
142
|
+
Strip a leading `--in-place` first. It is a flag, not part of the argument. Then check in this order:
|
|
143
|
+
|
|
144
|
+
1. No argument: ask for a request, key, slug, or job id.
|
|
145
|
+
2. A job id (`^\d{3,}-[a-z0-9-]+$`) with a folder at `factory/jobs/<id>/`: resume that job, or report that it is closed (see the resume table). A job id with a folder at `factory/archive/<id>/` is archived: say so and stop. Archived jobs are not resumable. To work on the feature again, `/factory start <slug or KEY>` creates a new job.
|
|
146
|
+
3. An issue key (`^[A-Z][A-Z0-9]+-\d+$`): match `docs/features/<KEY>-*/` in the main checkout.
|
|
147
|
+
4. A kebab slug (`^[a-z0-9]+(-[a-z0-9]+)*$`): match `docs/features/<slug>/` or `docs/features/<KEY>-<slug>/`, where `<KEY>` has the key format in step 3.
|
|
148
|
+
5. Anything else is a free-text request. Derive a 2–4 word kebab slug from it, then match as in step 4.
|
|
149
|
+
|
|
150
|
+
### 2. Resolve the feature folder
|
|
151
|
+
|
|
152
|
+
Whichever way the folder was found, the job's key is the `<KEY>` in its name (`PROJ-12` for `PROJ-12-onboarding-bootstrap`), or `null` for an unkeyed folder. A keyed folder never gets a `null` key, so its parent ticket is not created a second time.
|
|
153
|
+
|
|
154
|
+
- **One match:** continue that feature. Its existing `spec.md`, `plan.md`, and `tasks.md` are inputs to the spec writer.
|
|
155
|
+
- **Several matches:** list them and ask which one.
|
|
156
|
+
- **None:**
|
|
157
|
+
- From a key: read the ticket title through the [issue-tracker](../issue-tracker/SKILL.md) skill (MCP first, CLI fallback). Derive a kebab slug from the title (lowercase, non-alphanumerics to `-`, at most 4 words). Create `docs/features/<KEY>-<slug>/`. If the ticket cannot be read, stop and ask for a slug. Do not guess.
|
|
158
|
+
- From a slug or free text: create `docs/features/<slug>/`.
|
|
159
|
+
|
|
160
|
+
### 3. Resume or create the job
|
|
161
|
+
|
|
162
|
+
Look for open jobs (see Stages) whose `feature_dir` is this folder.
|
|
163
|
+
|
|
164
|
+
- **One open job:** resume it. Do not create a second. If the operator's text is a new request, say the open job was resumed and the new text was not applied. Use `/factory rework` for changes after the build. A `--in-place` flag on a resumed job is not applied: the recorded `worktree` stands, and say so.
|
|
165
|
+
- **Several open jobs:** list them and ask which one.
|
|
166
|
+
- **None:** create a job.
|
|
167
|
+
|
|
168
|
+
Resume by stage:
|
|
169
|
+
|
|
170
|
+
| Stage | Resume action |
|
|
171
|
+
| --- | --- |
|
|
172
|
+
| `spec` | Rerun `factory-spec-writer` in spec mode, then show the spec gate |
|
|
173
|
+
| `awaiting-spec` | Show the spec gate |
|
|
174
|
+
| `plan` | Rerun `factory-spec-writer` in plan mode, then show the plan gate |
|
|
175
|
+
| `awaiting-plan` | Show the plan gate |
|
|
176
|
+
| `build` | Rerun `factory-builder` in the mode the files call for: rework if a `rework-<k>.md` is newer than `build.md`, repair if the last review round has `CHANGES`, otherwise build |
|
|
177
|
+
| `review` | Rerun the review round `round` |
|
|
178
|
+
| `approve` | Rerun `factory-approver` |
|
|
179
|
+
| `awaiting-human` | Print the approver's rationale, the suggested checks from `qa.md` (see QA guidance), and the `/factory approve` command |
|
|
180
|
+
| `needs-human`, `failed_stage` `spec` | Rerun the spec stage (see Spec), then show the spec gate |
|
|
181
|
+
| `needs-human`, `failed_stage` `plan` | Rerun the plan stage (see Plan), then show the plan gate |
|
|
182
|
+
| `needs-human`, otherwise | Print the reason from `decision.md`, `merge-conflict.md`, or `build.md`, and the options: rework, approve, or abandon |
|
|
183
|
+
| `abandoned` | Closed. Say so and stop. To work on the feature again, run `/factory start <slug or KEY>`, which creates a new job |
|
|
184
|
+
| Archived (in `factory/archive/`, any stage) | Closed. Say so and stop (see Start step 1) |
|
|
185
|
+
|
|
186
|
+
A rerun of a failed stage sets `stage` to that stage and clears `failed_stage`.
|
|
187
|
+
|
|
188
|
+
A job with no work started (stage before `build`, so no build has begun and no `tasks.md` line is ticked) shows its current gate, which asks whether anything needs changing before it continues. A job whose work has started (stage `build` or later, or any ticked task) resumes without that prompt.
|
|
189
|
+
|
|
190
|
+
### 4. Create a new job
|
|
191
|
+
|
|
192
|
+
1. `mkdir -p factory/jobs` in the main checkout.
|
|
193
|
+
2. Find the highest `NNN` among `factory/jobs/NNN-*` and `factory/archive/NNN-*` directories (0 if none). Archived ids count, so an id is never reused. A pruned id is gone and may be reused. Candidate = highest + 1, zero-padded to at least 3 digits.
|
|
194
|
+
3. Slug = the feature folder name without its key prefix (`onboarding-bootstrap` for `PROJ-1234-onboarding-bootstrap`). Stable across the key rename.
|
|
195
|
+
4. `mkdir factory/jobs/<NNN-slug>` (no `-p`). If it already exists, bump `NNN` and retry. `mkdir` without `-p` is the claim.
|
|
196
|
+
5. Write `job.json` with stage `spec`, `feature_dir`, `issue_key` (the key from Resolve, or `null`), `size` `unclear`, `tracking` `none`, `worktree` (`false` for `--in-place` or when the operator asked for it at intake, `true` otherwise), and `worktree_path` `null`.
|
|
197
|
+
6. If intake asked questions, write `intake.md` (see Intake).
|
|
198
|
+
|
|
199
|
+
## Spec
|
|
200
|
+
|
|
201
|
+
Set stage `spec`. Dispatch `factory-spec-writer` in spec mode with the paths block, the feature request, and the feature folder. It writes `spec.md`. If `intake.md` is in the paths block, the spec writer reads it, and the operator's answers win over the request text where they conflict. Wait for `spec.md` (see Harness notes). Set stage `awaiting-spec` and show the spec gate.
|
|
202
|
+
|
|
203
|
+
## Spec gate (mandatory)
|
|
204
|
+
|
|
205
|
+
Runs in every `APPROVAL_MODE`, before any plan is written. Show the operator in chat:
|
|
206
|
+
|
|
207
|
+
- The spec's Summary, the number of acceptance criteria, each open `[PRODUCT DECISION]` item, and Open Questions.
|
|
208
|
+
|
|
209
|
+
Then ask in chat, as a numbered choice:
|
|
210
|
+
|
|
211
|
+
1. Confirm the spec. Offered only when no `[PRODUCT DECISION]` item is open.
|
|
212
|
+
2. Request changes: the operator types the change. Each open `[PRODUCT DECISION]` item needs an answer in the change text.
|
|
213
|
+
|
|
214
|
+
Handle the answer:
|
|
215
|
+
|
|
216
|
+
- **Confirm** → continue to Plan. If decision items are still open, do not continue: list them and ask again.
|
|
217
|
+
- **Request changes:** write the operator's text verbatim to `spec-<k>.md` in the job folder (`k` = next free index). Set stage `spec`, rerun `factory-spec-writer` in spec mode with the paths block and `spec-<k>.md`. It updates `spec.md`. Set stage `awaiting-spec` and show the gate again. The spec writer records each answered decision as `[DECIDED] <answer>`.
|
|
218
|
+
|
|
219
|
+
If the session ends at this gate, `/factory start <job-id|slug|KEY>` shows it again (see Start).
|
|
220
|
+
|
|
221
|
+
## Plan
|
|
222
|
+
|
|
223
|
+
Set stage `plan`. Dispatch `factory-spec-writer` in plan mode with the paths block, and `plan-<k>.md` when a plan change request is pending. It writes `plan.md` in the feature folder, and `tasks.md` for large work (or removes a stale `tasks.md` when the plan is small). It does not change `spec.md`.
|
|
224
|
+
|
|
225
|
+
Read the `**Size:**` line of `plan.md`:
|
|
226
|
+
|
|
227
|
+
- `**Size:** small`, `large`, or `unclear` → copy into `job.json`.
|
|
228
|
+
- No valid line → re-dispatch once. If it is still missing, treat the size as `unclear`.
|
|
229
|
+
|
|
230
|
+
Proposed tracking: `tracked` only when size is `large` and the project's `AGENTS.md` Project Management section names a real tracker. Unfilled placeholders mean no tracker. Otherwise propose `none`.
|
|
231
|
+
|
|
232
|
+
Set stage `awaiting-plan` and show the plan gate.
|
|
233
|
+
|
|
234
|
+
## Plan gate (mandatory)
|
|
235
|
+
|
|
236
|
+
Every job stops at `awaiting-plan` before anything is ticketed or built, in every `APPROVAL_MODE`. Show the operator in chat:
|
|
237
|
+
|
|
238
|
+
- The plan's approach and steps from `plan.md`.
|
|
239
|
+
- The size and the reason from `plan.md`.
|
|
240
|
+
- For large work, the lines of `tasks.md`.
|
|
241
|
+
- The tracking proposal: create tickets in the tracker under the parent (the job's key, or a new parent), or no tickets (`tasks.md` only), or no tickets at all.
|
|
242
|
+
- The build mode: `worktree` (default) or `in-place`, as set in `job.json`.
|
|
243
|
+
|
|
244
|
+
Then ask in chat, as a numbered choice:
|
|
245
|
+
|
|
246
|
+
1. Confirm the plan.
|
|
247
|
+
2. Request changes: the operator types the change.
|
|
248
|
+
3. If size is `unclear`: answer `small` or `large`.
|
|
249
|
+
4. Change tracking: `tracked` or `none`.
|
|
250
|
+
5. Change build mode: `worktree` or `in-place`.
|
|
251
|
+
|
|
252
|
+
Handle the answer:
|
|
253
|
+
|
|
254
|
+
- **Confirm** with size known → continue to Tracking. If size is still `unclear`, ask again.
|
|
255
|
+
- **Request changes:** write the operator's text verbatim to `plan-<k>.md` (`k` = next free index). Set stage `plan`, rerun `factory-spec-writer` in plan mode with the paths block and `plan-<k>.md`. Show the gate again. If the change touches the spec itself, send it through the spec gate instead: write it to `spec-<k>.md`, rerun spec mode, show the spec gate, then plan again.
|
|
256
|
+
- **Change size only:** the operator's answer to choice 3 sets `size` in `job.json`. The plan does not change.
|
|
257
|
+
- **Change tracking only:** update `tracking` in `job.json`. Do not rerun the spec writer.
|
|
258
|
+
- **Change build mode only:** update `worktree` in `job.json`. Do not rerun the spec writer. In-place is checked when the build starts (see In-place mode).
|
|
259
|
+
|
|
260
|
+
If the session ends at the gate, nothing is lost. `/factory start <job-id|slug|KEY>` returns to this gate (see Start).
|
|
261
|
+
|
|
262
|
+
## Tracking
|
|
263
|
+
|
|
264
|
+
Runs once, after the operator confirms the plan and before the build location is created. Skip it when `tracking` is `none`. Keys written in earlier attempts make each step idempotent, so a rerun picks up where it stopped. A re-plan keeps the keys already written: a line with a key is never ticketed again, and tickets for lines a re-plan removes are left as they are and reported.
|
|
265
|
+
|
|
266
|
+
1. **Tracker states.** If `tracker_states` is `null`, ask once for the project's status names for In Progress, In Review, and Done. The names come from the project's `AGENTS.md` or the issue-tracker skill. Record them in `job.json`.
|
|
267
|
+
2. **Parent.** If `issue_key` is set, that ticket is the parent. Otherwise create the parent ticket. Set `issue_key` to its key.
|
|
268
|
+
3. **Sub-tasks.** Run the `/assign-tasks` flow on the feature's `tasks.md`, with the issue-tracker skill's epic discovery and the flow's consolidation rules. Create one ticket per `tasks.md` line that has no key yet, under the parent.
|
|
269
|
+
4. **Write keys.** Append ` — <KEY>` to each line as its ticket is created. Lines that already have a key are skipped.
|
|
270
|
+
5. **Rename.** If the folder had no key, rename `docs/features/<slug>/` to `docs/features/<KEY>-<slug>/`. Use `git mv` when git tracks the folder, `mv` otherwise. Stop if the target exists. Then update `feature_dir` in `job.json`. The rename is committed on the job branch with the feature docs (see Feature docs commit).
|
|
271
|
+
|
|
272
|
+
If a ticket cannot be created, stop at `awaiting-plan`. Report the lines that have keys and the error, and record it in `tracker_errors`. The operator can fix the cause and run `/factory start <job-id>`, or switch tracking to `none`.
|
|
273
|
+
|
|
274
|
+
## Worktree
|
|
275
|
+
|
|
276
|
+
The default build location. Use the [using-git-worktrees](../using-git-worktrees/SKILL.md) skill for its safety checks only. Its directory selection does not apply: the path below overrides it. Never edit `.gitignore` for the worktree.
|
|
277
|
+
|
|
278
|
+
- Branch `factory/<id>` from `{{DEFAULT_BRANCH}}`.
|
|
279
|
+
- Path `../<repo-name>-factory/<id>/` relative to the main checkout, where `<repo-name>` is the basename of the main checkout. Record it in `worktree_path`.
|
|
280
|
+
- Create it at the start of the build stage, after the plan gate and tracking. Reuse it if it exists (rework and repair).
|
|
281
|
+
- Copy the job's `spec.md`, `plan.md`, and `tasks.md` (if any) from the main checkout's feature folder into the same relative path in the worktree. The builder ticks `tasks.md` there. Then make the feature docs commit (next section).
|
|
282
|
+
- Job state stays in the main checkout's `factory/jobs/<id>/`, never in the worktree.
|
|
283
|
+
|
|
284
|
+
## Feature docs commit
|
|
285
|
+
|
|
286
|
+
Runs once the job's branch exists, in its build location (the worktree, or the main checkout for an in-place job), before the builder is dispatched. It puts the job's feature docs on the branch, so the main checkout can stay clean and the merge carries them:
|
|
287
|
+
|
|
288
|
+
1. Remove the old unkeyed folder when the feature folder was renamed to its keyed name: `git rm -r -q --ignore-unmatch -- docs/features/<slug>`. Skip it when `feature_dir` is `docs/features/<slug>`.
|
|
289
|
+
2. `git add -A -- <feature_dir>`.
|
|
290
|
+
3. `git diff --cached --quiet || git commit -q -m "docs: feature docs for <id>"`. Nothing is committed when nothing is staged.
|
|
291
|
+
|
|
292
|
+
It stages only the job's own paths. Other features' doc edits in the main checkout are left alone. Merge procedure step 3 reconciles the main checkout's copies.
|
|
293
|
+
|
|
294
|
+
## In-place mode
|
|
295
|
+
|
|
296
|
+
Used only when `worktree` is `false`: the operator asked for it with `--in-place`, at intake, or at the plan gate. The build then runs in the main checkout. Nothing is copied: the feature docs commit puts them on the branch.
|
|
297
|
+
|
|
298
|
+
Create the branch at build start, after the plan gate and tracking, only when all four checks pass. If one fails, stay at `awaiting-plan` and tell the operator which one:
|
|
299
|
+
|
|
300
|
+
- The main checkout is on `{{DEFAULT_BRANCH}}`.
|
|
301
|
+
- `git status --porcelain --untracked-files=no -- . ':(exclude)docs/features'` is empty. The feature docs are excluded because the spec writer and tracking write there, and the feature docs commit carries them onto the branch.
|
|
302
|
+
- No other job with `worktree` `false` is open (not `merged` or `abandoned`).
|
|
303
|
+
- No job with `worktree` `true` holds its build location.
|
|
304
|
+
|
|
305
|
+
Then run `git switch -c factory/<id> {{DEFAULT_BRANCH}}` in the main checkout, or `git switch factory/<id>` if the branch exists. Then make the feature docs commit. Keep `worktree_path` `null`. The Worktree line of the paths block is the main checkout.
|
|
306
|
+
|
|
307
|
+
While the job is open, the main checkout stays on `factory/<id>`. Say so at `awaiting-human` and `needs-human`. Before rework, repair, or a resume, switch the main checkout to `factory/<id>` if it is not already there, after the dirty-tree check above.
|
|
308
|
+
|
|
309
|
+
## Ports
|
|
310
|
+
|
|
311
|
+
For job number `n` (the integer of `NNN`):
|
|
312
|
+
|
|
313
|
+
- builder dev server: `{{FACTORY_PORT_BASE}} + 10n`
|
|
314
|
+
- ux reviewer: builder port + 1
|
|
315
|
+
- ui reviewer: builder port + 2
|
|
316
|
+
|
|
317
|
+
## Paths block
|
|
318
|
+
|
|
319
|
+
Every subagent prompt starts with this block, filled in:
|
|
320
|
+
|
|
321
|
+
```
|
|
322
|
+
PATHS
|
|
323
|
+
- Main checkout (absolute): <abs>
|
|
324
|
+
- Job folder (absolute): <abs>/factory/jobs/<id>
|
|
325
|
+
- Feature folder (absolute, main checkout): <abs>/<feature_dir>
|
|
326
|
+
- Worktree (absolute): <abs>/../<repo-name>-factory/<id> (in-place: the main checkout)
|
|
327
|
+
- Feature folder in worktree (absolute): <worktree>/<feature_dir> (in-place: the main checkout's)
|
|
328
|
+
- qa.md (absolute, build location): <worktree>/<feature_dir>/qa.md
|
|
329
|
+
- Branch: factory/<id>
|
|
330
|
+
- Default branch: {{DEFAULT_BRANCH}}
|
|
331
|
+
- Intake answers (absolute, only when intake.md exists): <job folder>/intake.md
|
|
332
|
+
- Round: <N>
|
|
333
|
+
- Ports: builder <p>, ux <p+1>, ui <p+2>
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
Agents read their inputs and write their one output file by these absolute paths. The spec-writer works in the main-checkout feature folder. Reviewers and the approver read the spec and plan there. The builder reads them there and ticks `tasks.md` in the build location's copy. Reviewers read `qa.md` at the paths block's `qa.md` line, for context only.
|
|
337
|
+
|
|
338
|
+
## Loop
|
|
339
|
+
|
|
340
|
+
Let `N` be the current round, starting at `1` for a new job.
|
|
341
|
+
|
|
342
|
+
1. **Spec.** Dispatch `factory-spec-writer` in spec mode (see Spec).
|
|
343
|
+
2. **Spec gate.** Show the gate and wait for the operator's confirmation (see Spec gate). Do not continue until the operator confirms.
|
|
344
|
+
3. **Plan.** Dispatch `factory-spec-writer` in plan mode (see Plan). Set stage `awaiting-plan`.
|
|
345
|
+
4. **Plan gate.** Show the gate and wait for the operator's confirmation (see Plan gate). Do not continue until the operator confirms.
|
|
346
|
+
5. **Tracking and build location.** Run Tracking if `tracking` is `tracked`. Create the build location: the worktree and its copy of the feature docs (see Worktree), or the in-place branch (see In-place mode). Set `worktree_path` if a worktree was made. Set stage `build`. Move any tracked tickets to In Progress (see Ticket lifecycle).
|
|
347
|
+
6. **Build.** Dispatch `factory-builder` in build mode. Wait (see Harness notes), then read the first line of `build.md`:
|
|
348
|
+
- `STATUS: READY` → continue.
|
|
349
|
+
- `STATUS: BLOCKED` → stage `needs-human`. Tell the user the reason from `build.md`. Stop.
|
|
350
|
+
- Anything else → treat as BLOCKED with reason "builder wrote no valid status".
|
|
351
|
+
7. **Review round N.** Set stage `review`, `round` to `N`, and every configured lens to `pending` in `reviews`. Move tracked tickets to In Review. Dispatch the lenses from `{{FACTORY_REVIEW_LENSES}}` in two stages, each with the paths block:
|
|
352
|
+
- **Stage A:** `code` → `factory-reviewer-code` and `security` → `factory-reviewer-security`. Dispatch both in one parallel batch (one message with multiple agent calls) when the harness can.
|
|
353
|
+
- **Stage B:** `ux` → `factory-reviewer-ux` and `ui` → `factory-reviewer-ui`, the browser lenses. Start them only after every stage A review is written. The code reviewer's build runs in the shared build location and would clobber the browser lenses' dev servers. The two browser lenses use separate ports and may run in parallel with each other.
|
|
354
|
+
|
|
355
|
+
If the harness cannot dispatch in parallel, run the lenses one at a time, in the order above.
|
|
356
|
+
|
|
357
|
+
Wait for every `round-N/review-<lens>.md` (see Harness notes). Each must have a first line `VERDICT: PASS` or `VERDICT: CHANGES`. A reviewer that returns without a valid file is re-dispatched alone, once. If it still fails, stage `needs-human`. Never resume the builder during a review round. Update `reviews` from the verdicts.
|
|
358
|
+
8. **Verdicts.** Check in this order:
|
|
359
|
+
- Any blocking item titled `Not verified:`, from any lens: no repair. Stage `needs-human`. Tell the user the `Not verified` items. Stop.
|
|
360
|
+
- Any `CHANGES`, and the cycle has used fewer than 3 review rounds: run a builder repair (below) with `Round` set to `N`, the round just reviewed. Then set `N` to `N + 1` and go to step 7.
|
|
361
|
+
- Any `CHANGES` on the third review round of the cycle: write `decision.md` with first line `ESCALATE` and the reason ("retries exhausted after round N; open items: …", listing the blocking items). Stage `needs-human`. Tell the user. Stop.
|
|
362
|
+
- All `PASS`: go to step 9.
|
|
363
|
+
|
|
364
|
+
A review cycle is the rounds since the job started, or since the latest `rework-<k>.md` (see Rework). "Third review round of the cycle" means two repairs have already been made in this cycle. The rework note records where its cycle starts.
|
|
365
|
+
9. **Approve.** Set stage `approve`. Dispatch `factory-approver` with the paths block. Wait for `decision.md` (see Harness notes). Its first line must be `APPROVE` or `ESCALATE`. If the line is missing or invalid, re-dispatch once, then stage `needs-human`.
|
|
366
|
+
- `ESCALATE` → stage `needs-human`. Tell the user the rationale. Stop.
|
|
367
|
+
- `APPROVE` and `{{APPROVAL_MODE}}` is exactly `auto` → run the merge procedure.
|
|
368
|
+
- `APPROVE` otherwise: stage `awaiting-human`. Print the suggested checks (see QA guidance). Tell the user to run `/factory approve <id>` after reviewing the branch. Stop. Any value other than exactly `auto` is treated as `human`.
|
|
369
|
+
|
|
370
|
+
### Repair
|
|
371
|
+
|
|
372
|
+
Dispatch `factory-builder` in repair mode with the paths block, where `Round` is the round just reviewed (the `N` from step 7, before it increments). It reads `spec.md`, `plan.md`, `build.md`, and `round-<Round>/review-*.md`. It must address every `CHANGES` item or justify it in `build.md`. It also rewrites `qa.md`. Wait for `build.md` (see Harness notes) and check its first line again (step 6 rules).
|
|
373
|
+
|
|
374
|
+
### Builder continuity
|
|
375
|
+
|
|
376
|
+
The builder keeps its context across repairs by whichever mechanism the harness offers:
|
|
377
|
+
|
|
378
|
+
- Resume the same builder session for repairs only where the harness supports it (Claude). Otherwise use the fresh-builder fallback below.
|
|
379
|
+
- Launch a fresh builder and point it at `spec.md`, `plan.md`, `build.md`, and the latest round's reviews. The files are the memory. The session is only a convenience.
|
|
380
|
+
|
|
381
|
+
Either way the builder runs only between reviews, never during one.
|
|
382
|
+
|
|
383
|
+
### Failures
|
|
384
|
+
|
|
385
|
+
- Missing or unreadable `AGENTS.md` commands → stop and ask the user to add them. Do not guess commands.
|
|
386
|
+
- An agent that finishes without its expected file → re-dispatch that agent once, then `needs-human`. For a spec or plan agent, set `failed_stage` to `spec` or `plan` with it.
|
|
387
|
+
- An agent that times out is not re-dispatched (see Harness notes).
|
|
388
|
+
|
|
389
|
+
## Ticket lifecycle
|
|
390
|
+
|
|
391
|
+
Tickets follow the job. The parent and every sub-task listed in `tasks.md` move together. Tickets exist only when `tracking` is `tracked`.
|
|
392
|
+
|
|
393
|
+
| Event | Tickets move to |
|
|
394
|
+
| --- | --- |
|
|
395
|
+
| Build starts, including rework | In Progress |
|
|
396
|
+
| Review round starts | In Review |
|
|
397
|
+
| Human approves and the merge runs (`/factory approve`) | Done |
|
|
398
|
+
| `/factory approve` on a `merged` job (for example after `auto` merged it) | Done |
|
|
399
|
+
| `/factory abandon` | Left as they are. The report lists their keys |
|
|
400
|
+
|
|
401
|
+
Rules:
|
|
402
|
+
|
|
403
|
+
- Use the names in `tracker_states`. Ask once if unknown (see Tracking).
|
|
404
|
+
- A failed transition is appended to `tracker_errors`, reported to the user, and never blocks the pipeline. Repeat transitions to the same status are harmless.
|
|
405
|
+
- `needs-human`, `awaiting-human`, and `abandoned` leave the tickets where they are. Only the operator's approval moves them to Done.
|
|
406
|
+
- The approver never transitions tickets.
|
|
407
|
+
|
|
408
|
+
## Rework
|
|
409
|
+
|
|
410
|
+
`/factory rework <job-id> <note>`:
|
|
411
|
+
|
|
412
|
+
1. Refuse unless stage is `awaiting-human` or `needs-human`, and the build location still exists (the worktree, or the branch for an in-place job). If a timed-out agent may still be running (see Harness notes), first confirm with the operator that it has stopped.
|
|
413
|
+
2. `k` = next free index of `rework-<k>.md` in the job folder, starting at 1.
|
|
414
|
+
3. Let `N` = `round + 1`. Write `rework-<k>.md`:
|
|
415
|
+
|
|
416
|
+
```
|
|
417
|
+
Starts at round N.
|
|
418
|
+
|
|
419
|
+
<the user's note, verbatim>
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
4. Set `round` to `N`, stage `build`. Move tracked tickets to In Progress. For an in-place job, switch the main checkout to `factory/<id>` if needed (see In-place mode). Dispatch a fresh `factory-builder` in rework mode. It reads the latest `rework-<k>.md` and rewrites `qa.md`. Wait for it. Then continue from step 6's status check on `build.md` (do not dispatch the builder again), and run review round `N` from step 7.
|
|
423
|
+
|
|
424
|
+
The new cycle starts with a fresh repair budget. Old rounds and reviews are kept, not overwritten.
|
|
425
|
+
|
|
426
|
+
## QA guidance (non-gating)
|
|
427
|
+
|
|
428
|
+
The builder writes `qa.md` in the feature folder of its build location (the worktree's copy, or the main checkout's for an in-place job). It is committed with the work. It is the operator's checklist for a human look before approval:
|
|
429
|
+
|
|
430
|
+
- How to run the feature: setup, dev-server command and port, seed data, flags.
|
|
431
|
+
- Numbered manual checks, each tied to an `AC-n` in `spec.md`, with the expected result.
|
|
432
|
+
- "Not covered by automated tests" items.
|
|
433
|
+
|
|
434
|
+
The builder rewrites it on repair and rework rounds. A small job can have a few lines.
|
|
435
|
+
|
|
436
|
+
Rules:
|
|
437
|
+
|
|
438
|
+
- `qa.md` never affects a verdict. Reviewers may read it for context. The approver ignores it.
|
|
439
|
+
- A missing `qa.md` is noted, never blocking.
|
|
440
|
+
- When the job reaches `awaiting-human` or `needs-human`, and after an auto merge, print `qa.md` (from the build location before the merge, from the main checkout's feature folder after it). Then print the `## Not verified` items from the latest round's reviews. Put this line before them: `Suggested checks before /factory approve — not a gate`. If `qa.md` is missing, print `No qa.md: no suggested checks.` in its place.
|
|
441
|
+
|
|
442
|
+
## Approve
|
|
443
|
+
|
|
444
|
+
`/factory approve <job-id>`:
|
|
445
|
+
|
|
446
|
+
1. If stage is `merged`: move the job's tickets to Done (if tracked), report, and stop. Do not merge again.
|
|
447
|
+
2. Otherwise refuse unless stage is `awaiting-human` or `needs-human`. The user's explicit command is the approval. Do not re-run the approver.
|
|
448
|
+
3. Refuse unless the first line of `build.md` is exactly `STATUS: READY`. A job that failed before its build has no `build.md`: say so, and point to `/factory start <job-id>` (for a failed spec or plan step) or `/factory abandon <job-id>`.
|
|
449
|
+
4. Refuse while `merge-conflict.md` exists in the job folder. The user resolves the conflict on the branch, then removes that file.
|
|
450
|
+
5. Print the reason the job stopped before merging, so the user sees what they approve: the `ESCALATE` rationale in `decision.md`, the `Not verified` items, or the failed agent. For `awaiting-human`, print the approver's rationale.
|
|
451
|
+
6. Run the merge procedure.
|
|
452
|
+
|
|
453
|
+
## Abandon
|
|
454
|
+
|
|
455
|
+
`/factory abandon <job-id>` closes a job that will not continue, such as one stopped before its build or one the operator no longer wants. It merges nothing and pushes nothing.
|
|
456
|
+
|
|
457
|
+
1. Refuse if the stage is already `merged` or `abandoned`. If an agent for the job may still be running (stage `spec`, `plan`, `build`, `review`, or `approve`), confirm with the operator in chat first, because the agent may still write its file.
|
|
458
|
+
2. Set stage `abandoned`, `failed_stage` `null`, and `updatedAt`.
|
|
459
|
+
3. If the job holds a build location (see Stages), let `U` be `git rev-list --count {{DEFAULT_BRANCH}}..factory/<id>`, the branch's commits that the default branch lacks. `U` is zero when the branch does not exist.
|
|
460
|
+
- `U` is zero: a worktree job runs `git worktree remove <worktree_path>`, then `git branch -d factory/<id>`. An in-place job, when the main checkout is on `factory/<id>`, runs `git switch {{DEFAULT_BRANCH}}`, then `git branch -d factory/<id>`.
|
|
461
|
+
- `U` is above zero: keep the worktree and the branch, so the commits are not lost. An in-place job still switches the main checkout back to `{{DEFAULT_BRANCH}}` when it is clean, so the main checkout is free.
|
|
462
|
+
- Use `-d` and never `--force`. A step that fails (uncommitted changes, a dirty checkout) is left in place and reported.
|
|
463
|
+
Every job that reached its build has the feature docs commit, so its `U` is above zero and its branch and worktree stay. That is expected.
|
|
464
|
+
4. Leave the tickets where they are and do not transition them. List their keys in the report.
|
|
465
|
+
5. Report what was removed, what was kept and why, and the ticket keys.
|
|
466
|
+
|
|
467
|
+
## Merge procedure
|
|
468
|
+
|
|
469
|
+
Local only. **Never push.** Never force-delete a branch or worktree.
|
|
470
|
+
|
|
471
|
+
1. **Preconditions.** Worktree jobs: the main checkout is on `{{DEFAULT_BRANCH}}`. In-place jobs: the main checkout is on `factory/<id>`. For both, `git status --porcelain --untracked-files=no -- . ':(exclude)docs/features'` is empty. Feature docs are excluded here because step 3 reconciles them. Untracked files, including ignored `factory/jobs/**`, do not block. Worktree jobs also stop while another in-place job holds its build location (see Stages). Say which job it is: "In-place job `<id>` holds the main checkout; approve or abandon it first." If a precondition fails, stop and tell the user. Do not change the stage. For an in-place job, once the checks pass, run `git switch {{DEFAULT_BRANCH}}`. The branch stays in place.
|
|
472
|
+
2. Record `PRE=$(git rev-parse HEAD)`.
|
|
473
|
+
3. **Feature docs.** The branch already carries the job's feature docs (see Feature docs commit), so the main checkout's copies are set aside and its feature paths return to the `{{DEFAULT_BRANCH}}` state. Let `P` be `<feature_dir>`, plus `docs/features/<slug>` when that differs:
|
|
474
|
+
- Copy each path that `git status --porcelain --untracked-files=all -- <P>` lists into `factory/jobs/<id>/main-copies/`, keeping its path. For a rename, copy the new path. Skip paths that no longer exist.
|
|
475
|
+
- `git reset -q -- <P>`, then `git clean -fdq -- <P>`. This removes only the untracked copies made above.
|
|
476
|
+
- If `git ls-tree -r --name-only {{DEFAULT_BRANCH}} -- <P>` lists files, `git checkout -- <P>` restores them.
|
|
477
|
+
|
|
478
|
+
This is a move, not a delete. Git refuses a merge that would overwrite local or untracked files, so this step must come first.
|
|
479
|
+
4. `git merge --no-ff factory/<id> -m "Merge factory/<id>: <feature>"`. On conflict run `git merge --abort`, copy the `main-copies/` files back to their paths, write `merge-conflict.md` in the job folder listing the conflicting files, set stage `needs-human`, and report them.
|
|
480
|
+
5. Run the test command from `AGENTS.md` in the main checkout. On failure run `git reset --keep $PRE` (this undoes the merge), copy the `main-copies/` files back to their paths, set stage `needs-human`, and report the failing output. For an in-place job, the main checkout stays on `{{DEFAULT_BRANCH}}` and the branch stays for inspection.
|
|
481
|
+
6. Worktree jobs: `git worktree remove <worktree>`, then `git branch -d factory/<id>`. In-place jobs: skip the worktree removal and run only `git branch -d factory/<id>`. `-d` refuses unless the branch is merged, which is the safety check. If either fails after the merge succeeded, do not undo the merge. Continue to step 7 and report the leftover worktree or branch path for the user to remove.
|
|
482
|
+
7. Set stage `merged`. If the merge came from `/factory approve`, move the job's tickets to Done (if tracked). After an `auto` merge they stay In Review; tell the user to run `/factory approve <id>` to move them to Done. Report that the merge is local and was not pushed. Print the suggested checks (see QA guidance).
|
|
483
|
+
|
|
484
|
+
On any `needs-human` outcome, leave the branch and worktree in place so the user can inspect them.
|
|
485
|
+
|
|
486
|
+
## Status
|
|
487
|
+
|
|
488
|
+
Read each `factory/jobs/*/job.json`. Print one row per open job: id, stage, round, reviews, issue key, size, tracking, updatedAt, and the next action for that stage. Write nothing.
|
|
489
|
+
|
|
490
|
+
Closed jobs (`merged` or `abandoned`) are hidden by default. Print one line in their place: `N closed jobs hidden (--all to show)`. With `--all`, show the closed jobs too, and the archived jobs from `factory/archive/`. The table below lists every stage, closed ones included.
|
|
491
|
+
|
|
492
|
+
| Stage | Next action |
|
|
493
|
+
| --- | --- |
|
|
494
|
+
| `spec` | Wait, or `/factory start <id>` to resume if the session ended |
|
|
495
|
+
| `awaiting-spec` | `/factory start <id>` to review the spec and confirm it |
|
|
496
|
+
| `plan` | Wait, or `/factory start <id>` to resume if the session ended |
|
|
497
|
+
| `awaiting-plan` | `/factory start <id>` to review the plan and confirm it |
|
|
498
|
+
| `build`, `review`, `approve` | Wait, or `/factory start <id>` to resume if the session ended |
|
|
499
|
+
| `awaiting-human` | Review the branch and its `qa.md` (suggested checks, not a gate), then `/factory approve <id>` |
|
|
500
|
+
| `needs-human` | Read the reason. A failed spec or plan step: `/factory start <id>` reruns it. After a build: `/factory rework <id> <note>` or `/factory approve <id>`. Either way, `/factory abandon <id>` closes the job |
|
|
501
|
+
| `abandoned` | Nothing: the job is closed. To work on the feature again, `/factory start <slug or KEY>` creates a new job |
|
|
502
|
+
| `merged` | Nothing, unless tickets are not Done yet: `/factory approve <id>` |
|
|
503
|
+
|
|
504
|
+
## Watch
|
|
505
|
+
|
|
506
|
+
`/factory watch` is read-only. It changes no stage and dispatches nothing.
|
|
507
|
+
|
|
508
|
+
1. Run `npx nitm-ai-dev-toolkit factory status` from the main checkout and show its table in chat. Pass `--all` when the operator asked for it.
|
|
509
|
+
2. Tell the operator to copy this exact command into a real terminal for the live view. Like status, it hides closed jobs unless `--all` is added:
|
|
510
|
+
|
|
511
|
+
`npx nitm-ai-dev-toolkit factory watch`
|
|
512
|
+
|
|
513
|
+
The live view needs an interactive terminal, which agent shells do not have, so do not run `watch` yourself. If the CLI cannot run, read each `job.json` and print the same table, as `/factory status` does.
|
|
514
|
+
|
|
515
|
+
## Archive
|
|
516
|
+
|
|
517
|
+
`/factory archive [--all] [--dry-run]` archives closed jobs. It moves them from `factory/jobs/<id>/` to `factory/archive/<id>/` and never touches an open job. Archived jobs are not resumable.
|
|
518
|
+
|
|
519
|
+
1. **Move.** Run `npx nitm-ai-dev-toolkit factory archive [--all] [--dry-run]` from the main checkout and show its output. It moves folders and touches no git state. If the CLI cannot run, do the same moves directly:
|
|
520
|
+
- A job is closed when its stage is `merged` or `abandoned`. Any other stage is open: skip it and count it.
|
|
521
|
+
- Default: move closed jobs whose `updatedAt` is more than 7 days before now (`date -u`). A closed job with no valid `updatedAt` is not moved. `--all`: move every closed job.
|
|
522
|
+
- Move with `mkdir -p factory/archive`, then `mv factory/jobs/<id> factory/archive/<id>`. If `factory/archive/<id>` already exists, skip the job and report it. Never overwrite.
|
|
523
|
+
- `--dry-run`: list the jobs that would move, move nothing, and skip step 2.
|
|
524
|
+
2. **Abandoned branches.** For each archived `abandoned` job whose branch `factory/<id>` still exists, ask the operator once per branch, in chat, as a numbered choice:
|
|
525
|
+
1. Delete the branch and its worktree.
|
|
526
|
+
2. Keep both.
|
|
527
|
+
|
|
528
|
+
Before asking, show the branch, the worktree path from `job.json` (`worktree_path`) when `git worktree list` still shows it, and `U`, the count of unmerged commits (see Abandon).
|
|
529
|
+
|
|
530
|
+
On yes: if the worktree still exists, run `git worktree remove <worktree_path>` without `--force`. Then run `git branch -D factory/<id>`. This is the one place the skill uses `-D`, because the operator has just seen the `U` commits that will be lost. If the worktree removal fails, keep the branch and report why. On no, leave both in place.
|
|
531
|
+
3. **Merged branches.** Never delete a branch of a merged job here. Merge removes it when it can. List each `factory/<id>` branch that still belongs to an archived `merged` job, and say that `git branch -d` removes it once the operator has checked it.
|
|
532
|
+
4. **Report.** Give the moved count, the open-job count, the jobs skipped (archive folder already present, or no valid `updatedAt`), the branches deleted, the branches kept and why, and the merged leftovers. Never push.
|
|
533
|
+
|
|
534
|
+
## Prune
|
|
535
|
+
|
|
536
|
+
`/factory prune [--all] [--abandoned] [--dry-run]` permanently deletes closed jobs from both `factory/jobs/` and `factory/archive/`. By default only `merged` jobs are deleted. `--abandoned` also deletes `abandoned` jobs. A pruned job cannot be recovered. It never deletes an open job, and it never touches `docs/features/`.
|
|
537
|
+
|
|
538
|
+
The age rule is the same as Archive: by default only jobs whose `updatedAt` is more than 7 days ago, and `--all` drops the age limit. A job with no valid `updatedAt` is not selected. `--abandoned` adds `abandoned` jobs under the same rule. Without it, abandoned jobs are never selected, and the list counts the ones it kept.
|
|
539
|
+
|
|
540
|
+
1. **List.** Run `npx nitm-ai-dev-toolkit factory prune [--all] [--abandoned]` from the main checkout, without `--yes`, using the flags the operator asked for. It lists the candidates and deletes nothing. Show the list in chat. If the list has abandoned candidates, read each one's `job.json` and keep its id, its `worktree_path`, and whether branch `factory/<id>` still exists. This must happen before any delete, because the folder and its `job.json` are gone afterwards. If the list kept abandoned jobs because `--abandoned` was not passed, say how many and offer once, in chat, to rerun the list with `--abandoned`. Do not add the flag without a yes.
|
|
541
|
+
With `--dry-run`, stop after this step. Nothing is deleted, and no question or branch question is asked.
|
|
542
|
+
2. **Confirm.** Ask once in chat, as a numbered choice:
|
|
543
|
+
1. Permanently delete the listed jobs.
|
|
544
|
+
2. Cancel.
|
|
545
|
+
|
|
546
|
+
Nothing is deleted without a yes. On cancel, stop and report that nothing changed.
|
|
547
|
+
3. **Delete.** On yes, run `npx nitm-ai-dev-toolkit factory prune [--all] [--abandoned] --yes` with the same flags as step 1. It deletes the listed jobs. If the CLI cannot run, delete directly: first recompute the selection with the same rules. If it differs from the list shown, stop and show the new list again. Otherwise `rm -rf` each listed folder, one exact path at a time: `factory/jobs/<id>` or `factory/archive/<id>`. Never use a glob, and never delete a path outside those two folders.
|
|
548
|
+
4. **Branches.** Only when `--abandoned` was used and abandoned jobs were pruned. For each pruned abandoned job whose branch existed in step 1, run the branch step of Archive (step 2) with the `worktree_path` recorded in step 1. The question, the `U` count, the `-D` rule, and the worktree rule are the same. Merged branches are listed, never deleted (Archive step 3). If no abandoned job was pruned, skip this step.
|
|
549
|
+
5. **Report.** Give the count pruned from `factory/jobs/` and from `factory/archive/`, the open jobs skipped, the abandoned jobs kept (when `--abandoned` was not passed), the branches deleted and kept (with why, when this step ran), and the merged leftovers. Never push.
|
|
550
|
+
|
|
551
|
+
## Subagent dispatch
|
|
552
|
+
|
|
553
|
+
Dispatch with the paths block first, then the task line, then the files to read. The task line names the mode when the agent has one (spec or plan for the spec writer; build, repair, or rework for the builder). The dispatch primitive is the Claude Agent tool, the Copilot `agent` tool, or the harness's own subagent mechanism elsewhere.
|
|
554
|
+
|
|
555
|
+
Each harness has its own wrappers. Every wrapper points to its snippet in `.claude/agents-snippets/`:
|
|
556
|
+
|
|
557
|
+
| Harness | Agent wrappers | Entry point |
|
|
558
|
+
| --- | --- | --- |
|
|
559
|
+
| Claude Code | `.claude/agents/factory-*.agent.md` | `.claude/commands/factory.md` |
|
|
560
|
+
| GitHub Copilot | `.github/agents/factory-*.agent.md` | `.github/prompts/factory.prompt.md` |
|
|
561
|
+
| Cursor | `.cursor/agents/factory-*.agent.md` | `.cursor/commands/factory.md` |
|
|
562
|
+
| OpenCode | `.opencode/agents/factory-*.md` | `.opencode/commands/factory.md` |
|
|
563
|
+
| Codex | none | this skill |
|
|
564
|
+
|
|
565
|
+
Codex has no wrappers. Run each `.claude/agents-snippets/factory-*.md` as a delegated or fresh session, passing the paths block first. If the harness gives you one session only, use single-session mode (see Harness notes).
|
|
566
|
+
|
|
567
|
+
Dispatch each agent by its kebab-case name, for example `factory-builder`. Copilot may show a display name in its agent list; pick the agent with that name.
|
|
568
|
+
|
|
569
|
+
## Harness notes
|
|
570
|
+
|
|
571
|
+
The loop is the same in every harness. Use a parallel lens batch where the harness offers one, otherwise run the lenses in order. If dispatch is background-only, poll each expected file for a valid first line until the timeout, then apply Failures. Builder resume is under Builder continuity; wrappers are under Subagent dispatch.
|
|
572
|
+
|
|
573
|
+
- **Timeout.** The default is 15 minutes per agent. The operator can set another value for one run by saying so in the request, for example `timeout 30 min`.
|
|
574
|
+
- **A timed-out agent is never re-dispatched.** It may still be running, and a second run could write the same file. Stage `needs-human` with the reason "`<agent>` timed out after `<N>` min; it may still be running". For a spec or plan agent, also set `failed_stage`. Tell the operator to check the job folder and the build location, and to confirm the agent has stopped, before `/factory start` or `/factory rework`.
|
|
575
|
+
|
|
576
|
+
**Single-session mode (Codex).** Codex has no wrappers, so one session plays every role. The orchestrator switches roles one at a time. For each agent it reads only that role's snippet and input files, writes only that role's output, and keeps the same write boundaries. The rule "never write code" applies per role: as orchestrator it writes only orchestrator files, and it writes code only as the builder, and only in the build location. Lenses run in order (stage A, then stage B). The security reviewer judges the diff, not the builder's account. A timed-out role is handled as above; a role that ends without its file is re-run once, then `needs-human`.
|
|
577
|
+
|
|
578
|
+
## Limits
|
|
579
|
+
|
|
580
|
+
File-write boundaries are prompt conventions, not enforced isolation. Nothing physically stops an agent from writing outside its file. The real gates are the spec and plan gates, the reviewer verdicts, the approver's rules and dry-run merge check, and the merge procedure's preconditions and test run. Ticket updates are best-effort and never gate the pipeline. `qa.md` is advisory and gates nothing.
|
|
581
|
+
|
|
582
|
+
## Related
|
|
583
|
+
|
|
584
|
+
- [Feature spec template](../../../templates/feature-spec.template.md)
|
|
585
|
+
- [Feature docs convention](../../../.claude/rules-snippets/documentation.md)
|
|
586
|
+
- [Issue tracker](../issue-tracker/SKILL.md)
|
|
587
|
+
- [Test-driven development](../test-driven-development/SKILL.md)
|
|
588
|
+
- [Git worktrees](../using-git-worktrees/SKILL.md)
|
|
589
|
+
- [Security review](../security-reviewer/SKILL.md)
|
|
590
|
+
- [Requesting code review](../requesting-code-review/SKILL.md)
|
|
591
|
+
- [Verification before completion](../verification-before-completion/SKILL.md)
|
|
592
|
+
- [Ponytail](../ponytail/SKILL.md)
|
|
593
|
+
- [Factory README](../../../factory/README.md)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: "
|
|
2
|
+
name: "admin-portal"
|
|
3
3
|
description: "Build administrator portals with RBAC, system dashboards, reporting, analytics, and operational tooling. Specializes in admin frameworks and monitoring ecosystem tools."
|
|
4
4
|
tools: ["Read", "Grep", "Write", "Edit", "Bash(*)"]
|
|
5
5
|
user-invocable: true
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: "
|
|
2
|
+
name: "api-specialist"
|
|
3
3
|
description: "Design and implement API architecture, documentation, and developer experience. Use for REST design, GraphQL, OpenAPI specs, SDK generation, API versioning, and integration patterns."
|
|
4
4
|
tools: ["Read", "Grep", "Write", "Edit", "Bash(*)"]
|
|
5
5
|
user-invocable: true
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: "
|
|
2
|
+
name: "backend-architect"
|
|
3
3
|
description: "Design APIs, databases, and server-side architecture. Use for scalable backend systems, data modeling, caching strategies, system design, and infrastructure decisions."
|
|
4
4
|
tools: ["Read", "Grep", "Write", "Edit", "Bash(*)"]
|
|
5
5
|
user-invocable: true
|