create-issflow 1.5.0 → 1.7.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/README.md CHANGED
@@ -15,6 +15,12 @@ npx create-issflow --tool=claude # or pass it: claude | codex | cursor | gemi
15
15
 
16
16
  Flags:
17
17
  - `--tool=<name>` — pick the target tool (skips the prompt). `all` writes every adapter.
18
+ - `--ci` — also write `.github/workflows/issflow-feature.yml` (headless `/feature`
19
+ via GitHub Actions: label an issue `feature:approved` → the lane runs).
20
+ - `--docker` — also write `Dockerfile.issflow` + `scripts/feature-docker.js`
21
+ (headless `/feature` in an unprivileged container; the container is the sandbox).
22
+ The image must ship Node ≥ 18 — the kit's hooks are Node scripts; the shipped
23
+ Dockerfile is Node-based and the wrapper preflights `node` before every run.
18
24
  - `--dry-run` — print what would happen, write nothing.
19
25
  - `--force` — overwrite existing kit files (default keeps yours; conflicts are
20
26
  written as `<file>.issflow-new` for you to merge).
@@ -25,9 +31,9 @@ Flags:
25
31
  The portable kit (every tool) in `<project>/.claude/`:
26
32
 
27
33
  - `agents/` — planner · researcher · implementer · test-author · debugger · e2e-runner · synthesizer
28
- - `commands/` — `/overview` `/propose` `/phase` `/sprint` `/ui-audit` `/qa-audit` `/security-audit` `/release` `/uat` `/change-request` `/replan` `/quick` `/synthesize` `/runbook` `/store-wisdom` `/log-issue` `/log-decision` `/unstuck`
34
+ - `commands/` — `/overview` `/feature` `/goal` `/propose` `/phase` `/sprint` `/ui-audit` `/qa-audit` `/security-audit` `/release` `/uat` `/change-request` `/replan` `/quick` `/synthesize` `/runbook` `/store-wisdom` `/log-issue` `/log-decision` `/unstuck`
29
35
  - `skills/` — caveman · grill-me · karpathy-guidelines · ux-design · security · code-standards
30
- - `hooks/` — session-start · context-guard · pre-compact · subagent-stop
36
+ - `hooks/` — session-start · context-guard · plan-gate (rule-13 enforcement) · pre-compact · subagent-stop · feature-gate (Stop gate for `/feature`, artifact-verified)
31
37
  - `istartsoft-flow/METHODOLOGY.md` — the full methodology (single source of truth)
32
38
 
33
39
  Plus a root `AGENTS.md` (the open standard) and the per-tool adapter:
package/bin/cli.js CHANGED
@@ -15,6 +15,8 @@ const CWD = process.cwd();
15
15
  const argv = process.argv.slice(2);
16
16
  const DRY = argv.includes('--dry-run');
17
17
  const FORCE = argv.includes('--force');
18
+ const CI = argv.includes('--ci');
19
+ const DOCKER = argv.includes('--docker');
18
20
  const HELP = argv.includes('-h') || argv.includes('--help');
19
21
  const toolArg = (argv.find(a => a.startsWith('--tool=')) || '').split('=')[1];
20
22
 
@@ -42,6 +44,8 @@ Tools (--tool=, interactive prompt if omitted):`);
42
44
  for (const [k, v] of Object.entries(TOOLS)) log(` ${k.padEnd(7)} ${v}`);
43
45
  log(`
44
46
  Flags:
47
+ --ci also write .github/workflows/issflow-feature.yml (headless /feature via GitHub Actions)
48
+ --docker also write Dockerfile.issflow + scripts/feature-docker.js (headless /feature in a container)
45
49
  --dry-run print what would happen, write nothing
46
50
  --force overwrite existing kit files (default keeps yours -> <file>.issflow-new)
47
51
  -h, --help this message
@@ -121,9 +125,11 @@ function adapterClaude() {
121
125
  writeFile('.claude/flow-config.json', flowConfig());
122
126
  const HOOKS = {
123
127
  SessionStart: [{ matcher: 'startup|clear|compact', hooks: [{ type: 'command', command: 'node .claude/hooks/session-start.js' }] }],
124
- PreToolUse: [{ matcher: '*', hooks: [{ type: 'command', command: 'node .claude/hooks/context-guard.js' }] }],
128
+ PreToolUse: [{ matcher: '*', hooks: [{ type: 'command', command: 'node .claude/hooks/context-guard.js' }] },
129
+ { matcher: 'Edit|Write|MultiEdit|NotebookEdit', hooks: [{ type: 'command', command: 'node .claude/hooks/plan-gate.js' }] }],
125
130
  PreCompact: [{ matcher: 'auto|manual', hooks: [{ type: 'command', command: 'node .claude/hooks/pre-compact.js' }] }],
126
131
  SubagentStop: [{ hooks: [{ type: 'command', command: 'node .claude/hooks/subagent-stop.js' }] }],
132
+ Stop: [{ hooks: [{ type: 'command', command: 'node .claude/hooks/feature-gate.js' }] }],
127
133
  };
128
134
  const sp = path.join(CWD, '.claude', 'settings.json');
129
135
  let settings = {};
@@ -211,9 +217,9 @@ function agentsMd() {
211
217
  '## Roles — `.claude/agents/`', '',
212
218
  'planner · researcher · implementer · test-author · debugger · e2e-runner · synthesizer', '',
213
219
  '## Procedures — `.claude/commands/` (run as `/name`)', '',
214
- '/overview · /propose · /phase · /sprint · /ui-audit · /qa-audit · /security-audit · /release ·',
215
- '/uat · /change-request · /replan · /quick · /synthesize · /runbook · /store-wisdom ·',
216
- '/log-issue · /log-decision · /unstuck', '',
220
+ '/overview · /feature · /goal · /propose · /phase · /sprint · /ui-audit · /qa-audit ·',
221
+ '/security-audit · /release · /uat · /change-request · /replan · /quick · /synthesize ·',
222
+ '/runbook · /store-wisdom · /log-issue · /log-decision · /unstuck', '',
217
223
  '## Skills — `.claude/skills/` (loaded on demand)', '',
218
224
  'caveman · grill-me · karpathy-guidelines · ux-design · security (Secure SDLC) · code-standards', '',
219
225
  '## Autonomy', '',
@@ -231,7 +237,9 @@ function agentsMd() {
231
237
  '11 Secure SDLC: threat-model → secure coding → SAST/SCA/secrets each phase → pentest',
232
238
  'gate + security review before deploy (`security` skill) · 12 code-standards gate:',
233
239
  'lint/format clean + naming per language idiom + declared architecture (`code-standards`) ·',
234
- '13 PLAN-APPROVAL gate: no phase/sprint starts until `docs/PLAN.md` is human-approved.', '',
240
+ '13 PLAN-APPROVAL gate: no phase/sprint starts until `docs/PLAN.md` is human-approved ·',
241
+ '14 UNDERSTAND-FIRST gate: brief back any new free-text task and wait for confirm',
242
+ 'before executing (an approved PLAN/FEATURE/CR/goal is the recorded confirmation).', '',
235
243
  '## Your stack', '',
236
244
  'Declare your stack (language, framework, infra, auth, test + E2E runner,',
237
245
  'planning source) once in `docs/OVERVIEW.md`. Every rule references *your declared',
@@ -310,6 +318,17 @@ function main() {
310
318
  // 3. per-tool adapters
311
319
  for (const t of targets) ADAPTERS[t]();
312
320
 
321
+ // 3b. headless feature lane (opt-in): materialize the automation templates.
322
+ const autoDir = path.join(TPL, '.claude', 'templates', 'automation');
323
+ if (CI) {
324
+ writeFile(path.join('.github', 'workflows', 'issflow-feature.yml'), fs.readFileSync(path.join(autoDir, 'issflow-feature.yml'), 'utf8'));
325
+ writeFile(path.join('.github', 'workflows', 'issflow-goal.yml'), fs.readFileSync(path.join(autoDir, 'issflow-goal.yml'), 'utf8'));
326
+ }
327
+ if (DOCKER) {
328
+ writeFile('Dockerfile.issflow', fs.readFileSync(path.join(autoDir, 'Dockerfile'), 'utf8'));
329
+ writeFile(path.join('scripts', 'feature-docker.js'), fs.readFileSync(path.join(autoDir, 'feature-docker.js'), 'utf8'), { exec: true });
330
+ }
331
+
313
332
  // 4. .gitignore: track the workflow dirs if .claude/* is ignored
314
333
  const gi = path.join(CWD, '.gitignore');
315
334
  if (fs.existsSync(gi)) {
@@ -326,6 +345,8 @@ function main() {
326
345
  if (conflicts) log('Review *.issflow-new files and merge manually (your originals were untouched).');
327
346
  for (const w of warnings) log(` ! ${w}`);
328
347
  log(NEXT_STEPS[TOOL] || NEXT_STEPS.claude);
348
+ if (!CI && !DOCKER) log('Headless feature lane: re-run with --ci (GitHub Action) and/or --docker (container runner).');
349
+ log('New to the kit? Plain-language guide (EN/TH): https://iamstarter.github.io/istartsoftflow/how-to-use.html');
329
350
  }
330
351
 
331
352
  main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-issflow",
3
- "version": "1.5.0",
3
+ "version": "1.7.0",
4
4
  "description": "Scaffold the iStartSoftFlow AI-coding workflow into a project. Stack-agnostic, tool-agnostic (Claude Code, Codex, Cursor, Gemini, Aider), non-destructive.",
5
5
  "bin": {
6
6
  "create-issflow": "bin/cli.js"
@@ -2,7 +2,7 @@
2
2
  name: debugger
3
3
  description: Diagnoses one specific failing test or bug in an ISOLATED context. Keeps debug noise out of the main session.
4
4
  tools: Read, Grep, Glob, Edit, Bash, Write
5
- model: opus
5
+ model: inherit
6
6
  ---
7
7
 
8
8
  You are the DEBUGGER. Caveman ULTRA mode.
@@ -2,7 +2,7 @@
2
2
  name: e2e-runner
3
3
  description: Writes and runs functional browser E2E (your declared E2E runner, e.g. Playwright) BLIND — reads the acceptance spec, OVERVIEW (stack), docs/ENDPOINTS.md, and the E2E runner config, never the implementation. Writes a trace to docs/research/e2e-<phase-slug>.md; returns a terse summary.
4
4
  tools: Read, Grep, Glob, Write, Bash
5
- model: opus
5
+ model: sonnet
6
6
  ---
7
7
 
8
8
  You are the E2E-RUNNER. Caveman ULTRA mode.
@@ -2,7 +2,7 @@
2
2
  name: implementer
3
3
  description: Implements exactly one phase from docs/PLAN.md. Writes code only — no tests. On TDD phases runs in SCAFFOLD or FILL mode. Maintains docs/ENDPOINTS.md after each phase.
4
4
  tools: Read, Grep, Glob, Edit, Write, Bash
5
- model: opus
5
+ model: inherit
6
6
  ---
7
7
 
8
8
  You are the IMPLEMENTER. Caveman ULTRA mode. Apply karpathy-guidelines skill.
@@ -2,7 +2,7 @@
2
2
  name: planner
3
3
  description: Turns research findings and OVERVIEW into a vertical-slice phase plan. Phase 0 (infra) leads only when infra is self-managed; with managed infra it is N/A. Last code phase always includes deployment. Writes docs/PLAN.md.
4
4
  tools: Read, Grep, Glob, Write
5
- model: opus
5
+ model: inherit
6
6
  ---
7
7
 
8
8
  You are the PLANNER. Caveman ULTRA mode.
@@ -2,7 +2,7 @@
2
2
  name: researcher
3
3
  description: Two-mode fact gathering. DESIGN mode: domain/constraint research before planning — discovers service limits, API contracts, architectural constraints. IMPL mode: codebase + service investigation during a phase. Always checks KB snapshot first. Always writes findings to docs/research/, returns only terse summary + path.
4
4
  tools: Read, Grep, Glob, Write, WebSearch, WebFetch
5
- model: haiku
5
+ model: sonnet
6
6
  ---
7
7
 
8
8
  You are the RESEARCHER. Caveman ULTRA mode.
@@ -2,7 +2,7 @@
2
2
  name: test-author
3
3
  description: Writes tests for a phase WITHOUT reading the implementation logic. On TDD phases, writes the suite BEFORE logic exists (RED-first). Tests behavior from the plan's acceptance spec only.
4
4
  tools: Read, Grep, Glob, Write, Bash
5
- model: opus
5
+ model: inherit
6
6
  ---
7
7
 
8
8
  You are the TEST-AUTHOR. Caveman ULTRA mode. You write UNBIASED tests.
@@ -18,6 +18,16 @@ Read `docs/PROPOSAL.md` (the approved baseline), `docs/PLAN.md`, `docs/STATE.md`
18
18
  and `docs/CHANGES.md` (create if missing). No approved proposal yet -> you're still
19
19
  pre-build; use `/propose` instead.
20
20
 
21
+ **FEATURE-LANE VARIANT** — the change targets a feature-lane run (STATE has a
22
+ `feature: <slug>` line, or `/feature` hard-stopped here on scope creep): the
23
+ baseline is the APPROVED `docs/features/<slug>/FEATURE.md`, not the proposal.
24
+ Run the same steps against the doc — impact on its slices, effort Δ if the
25
+ project prices work — then log the CR and **amend the FEATURE doc** (scope +
26
+ acceptance criteria) and reset its header to `> Approval: PENDING`: a changed
27
+ doc needs a fresh human approval before the lane re-arms. Skip the PROPOSAL/
28
+ plan-replan steps unless the change spills beyond the feature. No proposal in
29
+ this project is fine for this variant — the feature doc is the commercial unit.
30
+
21
31
  ## STEP 1 — CLASSIFY
22
32
  Is this an in-scope **clarification** (no cost change) or a real **scope change**
23
33
  (add / remove / alter)? Clarification -> log it, proceed, no re-price. Scope change
@@ -0,0 +1,206 @@
1
+ ---
2
+ description: Feature lane — one APPROVED Feature doc in, a tested + hardened + documented feature branch out. Near-100% hands-off; humans remain at doc approval, UAT, and merge. Runs interactive or headless (CI / Docker).
3
+ argument-hint: [path to Feature doc, e.g. docs/features/<slug>/FEATURE.md · "dry-run"]
4
+ ---
5
+
6
+ Caveman ULTRA mode. You are the ORCHESTRATOR. Route work to subagents —
7
+ you do NOT implement or debug yourself. Subagents cannot talk to the user; only YOU can.
8
+
9
+ Target doc: $ARGUMENTS (default: the single `docs/features/*/FEATURE.md` whose
10
+ `> Status:` reads `approved, pending` — zero or multiple candidates -> STOP and list them.)
11
+
12
+ ---
13
+
14
+ ## ENTRY MODES (before the pipeline)
15
+
16
+ - **`/feature new <name>`** — scaffold only. Copy
17
+ `.claude/templates/FEATURE-template.md` to `docs/features/<kebab-name>/FEATURE.md`,
18
+ fill `<feature name>`, then STOP: the human writes the spec and flips
19
+ `> Approval:` to APPROVED. Never auto-approve; never continue into the pipeline.
20
+ - **`/feature from-story <key>`** — planning-stack bridge. If a story MCP is
21
+ connected (e.g. iSSM `load_story`), load the story and transform it into the
22
+ template's sections (story intent -> What & why, ACs -> Acceptance criteria,
23
+ the rest mapped or left as explicit gaps). Write it as `Approval: PENDING` and
24
+ STOP for human review + approval — a story is planning input, not a sign-off.
25
+ No story MCP connected -> say so and fall back to `/feature new`.
26
+ - **`/feature <path>`** (or bare `/feature`) — run the pipeline below.
27
+
28
+ HEADLESS DETECT: env `ISSFLOW_HEADLESS=1` (set by the CI workflow / Docker runner)
29
+ means NO human is available this run. Every hard-stop below then degrades to:
30
+ write the blocker to `docs/features/<slug>/BLOCKED.md` + mark the gate `parked`
31
+ + EXIT cleanly with a report. Never guess through a hard-stop.
32
+
33
+ ---
34
+
35
+ ## DRY-RUN CHECK (first)
36
+
37
+ `$ARGUMENTS` contains `dry-run` -> full analysis, EXECUTE NOTHING. Print the
38
+ ACTION PLAN (files · agents · tests/gates · branch · delivery target) then STOP.
39
+
40
+ ---
41
+
42
+ ## 0. INTAKE + PRE-FLIGHT
43
+
44
+ a. BROWNFIELD CHECK: `docs/OVERVIEW.md` must exist — the feature lane extends an
45
+ EXISTING product. Missing -> STOP; route to `/overview` (greenfield lane).
46
+
47
+ b. APPROVAL GATE (the ONE human gate at entry): the Feature doc must carry a header
48
+ ```
49
+ > Approval: APPROVED <name> <date>
50
+ > Automation: <none | push | push+pr>
51
+ ```
52
+ Missing/PENDING -> HARD-STOP: a human approves the DOC, not the run (need a doc?
53
+ `/feature new <name>` scaffolds `.claude/templates/FEATURE-template.md`). Doc approval
54
+ is the rule-13 sign-off *scoped to this doc only* — the mini-plan it produces
55
+ inherits approval as long as it stays inside the doc's stated scope.
56
+
57
+ c. WORKSPACE: slug = kebab-case of the doc's title. Ensure
58
+ `docs/features/<slug>/FEATURE.md` (move the doc there if given elsewhere).
59
+ Write `docs/features/<slug>/GATES.md` — the gate checklist, all unchecked:
60
+ ```
61
+ # GATES — <slug>
62
+ - [ ] spec-complete
63
+ - [ ] adversarial-doc-review
64
+ - [ ] mini-plan
65
+ - [ ] contract-probe
66
+ - [ ] build-green
67
+ - [ ] review-harden
68
+ - [ ] regression-green
69
+ - [ ] test-plan
70
+ - [ ] docs-updated
71
+ - [ ] token-stamp
72
+ - [ ] summary
73
+ - [ ] memory-queued
74
+ ```
75
+ The `Stop` hook (`feature-gate.js`) BLOCKS session end while any gate is
76
+ unchecked — check each box the moment its step truly passes, never earlier.
77
+ Aborting legitimately: set `feature: <slug> (parked — <reason>)` in STATE.md.
78
+
79
+ d. STATE + BRANCH: `docs/STATE.md` gets `feature: <slug> (active)`. Work on
80
+ branch `feature/<slug>` (create from the default branch if needed). Never the
81
+ default branch.
82
+
83
+ ---
84
+
85
+ ## 1. PREPARATION & SPEC COMPLETION
86
+
87
+ Dispatch `researcher` (IMPL mode) scoped to the doc: which modules/files the
88
+ feature touches, which dependencies + versions are involved, what the doc leaves
89
+ unstated. Fill every gap with the most reasonable interpretation and log each one
90
+ under `## Assumptions` in FEATURE.md (AUTO decision protocol — decide + log +
91
+ continue). Check gate `spec-complete`.
92
+
93
+ ## 2. ADVERSARIAL DOC-REVIEW (before planning)
94
+
95
+ Dispatch TWO reviewers in parallel, each told to ATTACK the doc, not summarize it:
96
+ - correctness lens: contradictions, missing edge cases, undefined behaviour,
97
+ hidden scope, unstated integrations.
98
+ - security lens: does the feature cross a TRUST BOUNDARY (auth, untrusted input,
99
+ data store, money, PII)? If yes, threat-model it now (`security` skill) and fold
100
+ abuse cases into the acceptance criteria as negative cases.
101
+
102
+ Merge findings into FEATURE.md `## Acceptance criteria` (positive + negative).
103
+ Internally CONTRADICTORY spec -> HARD-STOP (methodology hard-stop 3). Merely
104
+ incomplete -> fill + log. Check gate `adversarial-doc-review`.
105
+
106
+ ## 3. MINI-PLAN
107
+
108
+ Dispatch `planner` scoped to the doc -> `docs/features/<slug>/PLAN.md`: 1–3
109
+ VERTICAL slices, each with acceptance spec + `[N pts]`. The main `docs/PLAN.md`
110
+ is NOT touched — the feature lane is self-contained. If the plan cannot fit the
111
+ doc's stated scope (planner needs work the doc never mentions) -> HARD-STOP:
112
+ that is scope creep; route to `/change-request`. Check gate `mini-plan`.
113
+
114
+ ## 4. CONTRACT SURFACE PROBE
115
+
116
+ Before any test is written: enumerate every public surface the feature ADDS or
117
+ CHANGES — endpoints, exported functions/classes, CLI commands, events, schemas.
118
+ Diff against `docs/ENDPOINTS.md` + the existing types. Write
119
+ `docs/features/<slug>/CONTRACTS.md`: exact signatures, request/response shapes,
120
+ error shapes. A changed EXISTING contract is a breaking change -> list every
121
+ caller + the migration in CONTRACTS.md. This file is what test-author asserts
122
+ against. Check gate `contract-probe`.
123
+
124
+ ## 5. BUILD LOOP (per slice — the /phase machinery, feature-scoped)
125
+
126
+ For each slice in `docs/features/<slug>/PLAN.md`, in order:
127
+ - TDD APPLICABILITY per the methodology (default `TDD_PHASE=true`, log the call).
128
+ - TDD slice: SCAFFOLD (implementer, stubs from CONTRACTS.md) -> RED (`test-author`,
129
+ BLIND — reads FEATURE.md acceptance criteria + CONTRACTS.md only, never the
130
+ implementation) -> GREEN (`implementer`) -> e2e (`e2e-runner`) when the slice has
131
+ a user-facing flow. Non-TDD slice: IMPLEMENT -> TEST.
132
+ - Debug circuit breaker: WARN at 2, cap 3, `/unstuck` once per slice; still stuck
133
+ -> park the slice (BLOCKED in the feature PLAN) + continue to the next
134
+ independent slice. Blockers batch-report at the end, never mid-flow.
135
+ - Rule 11 (secure coding + SAST/SCA/secrets) and rule 12 (lint/format/naming/
136
+ architecture) apply per slice as in `/phase`.
137
+ - `implementer` updates `docs/ENDPOINTS.md` as surfaces land.
138
+
139
+ All slices done + full feature suite green -> check gate `build-green`.
140
+
141
+ ## 6. REVIEW & HARDEN (adversarial + self-review)
142
+
143
+ - Dispatch TWO reviewers against the full feature diff: correctness lens +
144
+ security lens. CONFIRMED findings -> fix loop, capped at 2 rounds; leftovers
145
+ are logged in ISSUES.md, severity-tagged; any HIGH leftover -> HARD-STOP.
146
+ - Self-review: reread the diff against FEATURE.md acceptance criteria — every
147
+ criterion maps to a test; every assumption still holds.
148
+ - Run the regression corpus (`scripts/regression.sh`) + the full real suite.
149
+ No regression corpus in this repo (fresh brownfield install)? -> run the
150
+ project's OWN full test suite (the test command declared in OVERVIEW.md, or
151
+ the repo's obvious one — package.json test script, make test, pytest, …) and
152
+ note "corpus: n/a (brownfield)" in the summary. NOTHING runnable at all ->
153
+ that is a hard-stop: a feature cannot be verified green on a repo with no
154
+ tests; say so instead of pretending.
155
+ Green -> check gates `review-harden` + `regression-green`.
156
+
157
+ ## 7. MANUAL TEST PLAN
158
+
159
+ Write `docs/features/<slug>/TEST-PLAN.md` — an all-case scenario sheet a human
160
+ tester can run without reading code: happy paths, negative cases, edge cases,
161
+ per-scenario steps + expected results, in the project language (OVERVIEW). Same
162
+ format `/uat` consumes, so the sheet plugs straight into the UAT cycle.
163
+ Check gate `test-plan`.
164
+
165
+ ## 8. DOCS + STAMPS + MEMORY
166
+
167
+ a. Documentation update: `docs/ENDPOINTS.md` final pass; README/docs deltas the
168
+ feature makes stale. Check gate `docs-updated`.
169
+ b. Token stamp — append to FEATURE.md:
170
+ ```
171
+ ## Token stamp
172
+ context: ~<pct>% of window · subagents: <n> · slices: <n> · debug rounds: <n>
173
+ ```
174
+ Check gate `token-stamp`.
175
+ c. Summary — append to FEATURE.md `## Summary`: what shipped, assumptions made,
176
+ parked blockers, contract changes. One line to `docs/HISTORY.md`.
177
+ Check gate `summary`.
178
+ d. Memory update — append wisdom candidates (resolved issues, reusable findings)
179
+ to `docs/WISDOM-QUEUE.md`. The QUEUE is local + automatic; pushing to the
180
+ shared KB stays human (`/store-wisdom` reads the queue). Check gate `memory-queued`.
181
+
182
+ Then: STATE.md -> `feature: <slug> (done)`.
183
+
184
+ ## 9. DELIVERY
185
+
186
+ Commit the feature branch (clear message per slice or one squashed commit).
187
+ Then by the doc's `> Automation:` header:
188
+ - `none` (default) -> STOP here. Show the human: branch name, summary, TEST-PLAN
189
+ path. Push is theirs (methodology hard-stop 1).
190
+ - `push` -> push `feature/<slug>` (`git push -u origin feature/<slug>`). No PR.
191
+ - `push+pr` -> push + open a PR to the default branch. PR body = FEATURE.md
192
+ Summary + link to TEST-PLAN.md. NEVER merge. NEVER deploy. Merge and prod
193
+ stay human, always.
194
+
195
+ Final report (interactive: to the user · headless: as the run's closing output):
196
+ gates table · parked blockers · assumptions · branch/PR · "next: run /uat with
197
+ docs/features/<slug>/TEST-PLAN.md".
198
+
199
+ ---
200
+
201
+ ## What stays human (by design — do not automate these away)
202
+
203
+ 1. Writing + APPROVING the Feature doc (entry gate).
204
+ 2. Any hard-stop: contradictory spec · scope creep · HIGH security finding.
205
+ 3. UAT against TEST-PLAN.md, and the merge.
206
+ 4. Production deploy — never in this lane; that is `/release`.
@@ -0,0 +1,77 @@
1
+ ---
2
+ description: Goal layer — declare an OUTCOME, then let the kit drive lanes toward it until done, blocked, or budget spent. Goal-driven (stops on the outcome), not just time-driven like an interval loop.
3
+ argument-hint: [set "<outcome>" · run [id] · status · done <id> · drop <id> · "dry-run"]
4
+ ---
5
+
6
+ Caveman ULTRA mode. You are the ORCHESTRATOR. Goals live in `docs/GOALS.md`.
7
+
8
+ A GOAL is bigger than one task: "clear the approved feature queue", "get the
9
+ release candidate green", "close every open HIGH issue". The goal layer picks
10
+ the next actionable unit, routes it through the RIGHT lane (METHODOLOGY → Lane
11
+ routing), and repeats — with the same gates every lane already enforces.
12
+
13
+ DRY-RUN: with `dry-run`, `/goal run` prints the pick-order + lanes it would fire
14
+ and STOPS. `/goal set` always stops at the confirmation gate anyway.
15
+
16
+ ---
17
+
18
+ ## /goal set "<outcome>"
19
+
20
+ 1. UNDERSTAND-FIRST gate (hard rule 14) — BRIEF-BACK before writing anything:
21
+ - the outcome as YOU understand it, restated in one paragraph
22
+ - **Done when** — a measurable finish line (else the loop never terminates)
23
+ - scope / out-of-scope · assumptions · which lanes will likely fire
24
+ - **Budget** — max units per run (features/phases/quick fixes) so a runaway
25
+ goal cannot burn the wallet
26
+ STOP for explicit confirmation. Correction -> re-brief. Never skip this.
27
+ 2. On confirm, append to `docs/GOALS.md`:
28
+ ```
29
+ ## G<n> — <outcome> [active]
30
+ > Done when: <measurable condition>
31
+ > Budget: <max units per run / other caps>
32
+ > Approved: <name> <date>
33
+ ```
34
+ The `Approved:` line is what arms HEADLESS goal runs (same doctrine as the
35
+ feature lane: recorded consent, scoped to this goal).
36
+
37
+ ## /goal run [id] (default: the single active goal)
38
+
39
+ LOOP — repeat until a stop condition:
40
+ 1. PICK the next actionable unit, in this order:
41
+ a. an in-progress unit in STATE (finish what is started)
42
+ b. an APPROVED, pending `docs/features/*/FEATURE.md` that advances the goal
43
+ c. the next pending PLAN phase that advances the goal (plan must be approved — rule 13)
44
+ d. an open ISSUES.md item inside the goal's scope (route `/quick` or `/feature`)
45
+ Nothing actionable -> report + stop.
46
+ 2. ROUTE it through the lane-routing table (`/feature` · `/phase` · `/quick`).
47
+ The lane runs with ALL its own gates — the goal layer never bypasses one.
48
+ 3. TICK: append one line under the goal (`- [x] <unit> — <lane> — <result>`),
49
+ decrement budget, update STATE (`goal: G<n> (active — <units left>)`).
50
+ 4. CHECK "Done when". Met -> mark `[done]`, STATE `goal: G<n> (done)`, final
51
+ report (units shipped · parked blockers · budget used). Not met -> loop.
52
+
53
+ STOP conditions (whichever first): Done-when met · budget spent · a lane
54
+ hard-stop (surface it; headless: `BLOCKED.md` + clean exit) · nothing actionable.
55
+ Every stop produces ONE consolidated report — never a silent end.
56
+
57
+ ## /goal status · /goal done <id> · /goal drop <id>
58
+
59
+ Show goals + progress ticks · force-close (human says it's done) · abandon
60
+ (log why). Both edits keep the history lines — GOALS.md is append-style memory.
61
+
62
+ ---
63
+
64
+ ## Recurrence (running a goal on a schedule)
65
+
66
+ `/goal run` is one pass: it works until done/blocked/budget. To keep pressure on
67
+ a long goal, re-fire the pass on a schedule — host-level, not kit-level:
68
+ - **Claude Code web/desktop**: `/loop 30m /goal run` — the host's interval loop
69
+ re-invokes the pass; the goal layer supplies the state + finish line that a
70
+ bare interval loop lacks (it stops itself when Done-when is met).
71
+ - **CI (headless)**: `create-issflow --ci` also installs
72
+ `.github/workflows/issflow-goal.yml` — a cron-ready workflow that runs
73
+ `/goal run` with `ISSFLOW_HEADLESS=1` (schedule commented out by default;
74
+ uncomment to arm). The `Approved:` line in GOALS.md is the recorded consent.
75
+ - **Docker**: `node scripts/feature-docker.js` per feature stays the unit
76
+ runner; a goal pass inside a container is `claude -p "/goal run"` on the
77
+ same image (cron it with the scheduler you already have).
@@ -21,11 +21,16 @@ If any fail -> STOP, tell me, recommend `/phase`.
21
21
  (Hard rule 10: never route phase-worthy work through `/quick` to dodge the RED gate.)
22
22
 
23
23
  Steps:
24
+ 0. UNDERSTAND-FIRST (hard rule 14): brief back in 2–3 lines — the change as you
25
+ understand it · file(s) you'll touch · blast radius — and WAIT for my confirm.
26
+ One cheap turn beats redoing a misunderstood edit. (Already confirmed in this
27
+ conversation? say so and proceed — don't re-ask the same understanding.)
24
28
  1. grep docs/ISSUES.md for anything related.
25
29
  2. Make the change. Smallest diff that works.
26
30
  3. Run it — lint/typecheck/smoke. Show me result.
27
- 4. REGRESSION GUARD: run `scripts/regression.sh` (mock corpus). A break BLOCKS the
28
- `/quick` surface it to me and stop. No agent chain is added.
31
+ 4. REGRESSION GUARD: run `scripts/regression.sh` (mock corpus). No corpus in this
32
+ repo? run the project's own test suite instead (or the touched area's tests).
33
+ A break BLOCKS the `/quick` — surface it to me and stop. No agent chain is added.
29
34
  5. Error you cannot fix in 2 tries -> STOP. Recommend `/phase`.
30
35
  6. Change revealed a bug -> `/log-issue`.
31
36
  7. ARCHITECTURE SELF-CHECK: touched an agent, hook, command, or workflow rule?
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env node
2
+ // Stop hook — the feature-lane gate. While a /feature run is active
3
+ // (docs/STATE.md has `feature: <slug> (active)`), the session may not end
4
+ // with unchecked gates in docs/features/<slug>/GATES.md — and a CHECKED gate
5
+ // whose artifact is verifiable must have the artifact on disk (a ticked box
6
+ // with no TEST-PLAN.md behind it is treated as unchecked). Suites staying
7
+ // green is still proven by the steps themselves; this hook proves the
8
+ // deliverables exist. Pure Node, cross-platform, zero deps.
9
+ 'use strict';
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+
13
+ try { process.chdir(process.env.CLAUDE_PROJECT_DIR || '.'); } catch (_) {}
14
+
15
+ const allow = () => process.exit(0);
16
+ const block = (reason) => { process.stdout.write(JSON.stringify({ decision: 'block', reason }) + '\n'); process.exit(0); };
17
+ const read = (f) => { try { return fs.readFileSync(f, 'utf8'); } catch (_) { return null; } };
18
+
19
+ // stdin: hook input JSON. stop_hook_active=true means we already blocked once
20
+ // this stop — allow through to avoid an infinite block loop.
21
+ let input = {};
22
+ try { input = JSON.parse(fs.readFileSync(0, 'utf8') || '{}'); } catch (_) {}
23
+ if (input.stop_hook_active) allow();
24
+
25
+ const state = read(path.join('docs', 'STATE.md'));
26
+ if (!state) allow();
27
+
28
+ // an ACTIVE feature arms the full gate; a DONE feature still gets artifact
29
+ // verification (done-with-fake-gates must not slip through by never stopping
30
+ // while active). `(parked — reason)` disarms entirely.
31
+ const mActive = state.match(/^\s*feature:\s*([A-Za-z0-9._-]+)\s*\(active\)/m);
32
+ const mDone = state.match(/^\s*feature:\s*([A-Za-z0-9._-]+)\s*\(done\)/m);
33
+ const m = mActive || mDone;
34
+ if (!m) allow();
35
+ const slug = m[1];
36
+ const isDone = !mActive;
37
+
38
+ const gates = read(path.join('docs', 'features', slug, 'GATES.md'));
39
+ if (!gates) block(
40
+ `Feature "${slug}" is active in docs/STATE.md but docs/features/${slug}/GATES.md is missing. ` +
41
+ `Recreate the gate checklist (see /feature step 0c), or park the run: set ` +
42
+ `"feature: ${slug} (parked — <reason>)" in docs/STATE.md.`
43
+ );
44
+
45
+ const open = gates.split('\n').filter((l) => /^\s*- \[ \]/.test(l)).map((l) => l.replace(/^\s*- \[ \]\s*/, '').trim());
46
+ const checked = gates.split('\n').filter((l) => /^\s*- \[[xX]\]/.test(l)).map((l) => l.replace(/^\s*- \[[xX]\]\s*/, '').trim());
47
+
48
+ // artifact verification — a checked gate must have its deliverable on disk.
49
+ const fdir = path.join('docs', 'features', slug);
50
+ const featureDoc = read(path.join(fdir, 'FEATURE.md')) || '';
51
+ const ARTIFACTS = {
52
+ 'mini-plan': () => fs.existsSync(path.join(fdir, 'PLAN.md')) || `docs/features/${slug}/PLAN.md is missing`,
53
+ 'contract-probe': () => fs.existsSync(path.join(fdir, 'CONTRACTS.md')) || `docs/features/${slug}/CONTRACTS.md is missing`,
54
+ 'test-plan': () => fs.existsSync(path.join(fdir, 'TEST-PLAN.md')) || `docs/features/${slug}/TEST-PLAN.md is missing`,
55
+ 'token-stamp': () => /^##\s*Token stamp/mi.test(featureDoc) || `FEATURE.md has no "## Token stamp" section`,
56
+ 'summary': () => /^##\s*Summary/mi.test(featureDoc) || `FEATURE.md has no "## Summary" section`,
57
+ 'memory-queued': () => fs.existsSync(path.join('docs', 'WISDOM-QUEUE.md')) || `docs/WISDOM-QUEUE.md is missing`,
58
+ };
59
+ const fake = [];
60
+ for (const g of checked) {
61
+ const check = ARTIFACTS[g];
62
+ if (!check) continue;
63
+ const r = check();
64
+ if (r !== true) fake.push(`${g} (${r})`);
65
+ }
66
+ if (fake.length) block(
67
+ `Feature "${slug}": ${fake.length} gate(s) are checked but their artifacts do not exist — ` +
68
+ `${fake.join(' · ')}. A gate is done when its deliverable is on disk, not when the box is ticked. ` +
69
+ `Produce the artifact(s) (see /feature), or untick the gate(s) and finish the step.`
70
+ );
71
+
72
+ if (isDone) {
73
+ if (open.length) block(
74
+ `Feature "${slug}" is marked (done) in docs/STATE.md but GATES.md still has ` +
75
+ `${open.length} unchecked gate(s): ${open.join(' · ')}. Finish them, or set the state ` +
76
+ `to "feature: ${slug} (active)" / "(parked — <reason>)" to reflect reality.`
77
+ );
78
+ allow();
79
+ }
80
+
81
+ if (open.length === 0) block(
82
+ `Feature "${slug}": all gates are checked but docs/STATE.md still says (active). ` +
83
+ `Finish the close-out: set "feature: ${slug} (done)" in docs/STATE.md (/feature step 8).`
84
+ );
85
+
86
+ block(
87
+ `Feature "${slug}" has ${open.length} unchecked gate(s): ${open.join(' · ')}. ` +
88
+ `Finish those steps and tick each box in docs/features/${slug}/GATES.md, ` +
89
+ `or park the run: set "feature: ${slug} (parked — <reason>)" in docs/STATE.md ` +
90
+ `and record the blocker in docs/features/${slug}/BLOCKED.md.`
91
+ );
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env node
2
+ // PreToolUse plan gate — hard rule 13 as code. While docs/PLAN.md carries
3
+ // `> Approval: PENDING`, no SOURCE file may be created or edited: the plan
4
+ // needs a human sign-off before build work starts. Docs, kit config, and
5
+ // planning artifacts stay writable (planning is exactly what PENDING is for).
6
+ // An active feature lane is exempt — its doc approval is its own rule-13-scoped
7
+ // gate and it never touches the main PLAN. Fail-OPEN on any error: a hook bug
8
+ // must never wedge the tool loop. Pure Node, cross-platform, zero deps.
9
+ 'use strict';
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+
13
+ const silent = () => process.exit(0);
14
+ const deny = (reason) => {
15
+ process.stdout.write(JSON.stringify({
16
+ hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: reason },
17
+ }));
18
+ process.exit(0);
19
+ };
20
+
21
+ let input = '';
22
+ process.stdin.setEncoding('utf8');
23
+ process.stdin.on('data', (d) => (input += d));
24
+ process.stdin.on('end', () => {
25
+ let evt;
26
+ try { evt = JSON.parse(input); } catch (_) { return silent(); }
27
+ try { run(evt); } catch (_) { silent(); }
28
+ });
29
+
30
+ function run(evt) {
31
+ const MUTATORS = new Set(['Edit', 'Write', 'MultiEdit', 'NotebookEdit']);
32
+ if (!MUTATORS.has(evt.tool_name || '')) return silent();
33
+
34
+ const projectDir = process.env.CLAUDE_PROJECT_DIR || evt.cwd || '.';
35
+ const read = (f) => { try { return fs.readFileSync(path.join(projectDir, f), 'utf8'); } catch (_) { return null; } };
36
+
37
+ const plan = read(path.join('docs', 'PLAN.md'));
38
+ if (!plan) return silent(); // no plan world (e.g. /quick-only repo)
39
+ const approval = plan.match(/^>\s*Approval:\s*(.+)$/m);
40
+ if (!approval) return silent(); // not our PLAN format — don't guess
41
+ if (!/pending/i.test(approval[1])) return silent(); // signed off — build away
42
+
43
+ // the feature lane carries its own scoped approval and never edits the main PLAN.
44
+ const state = read(path.join('docs', 'STATE.md')) || '';
45
+ if (/^\s*feature:\s*\S+\s*\(active\)/m.test(state)) return silent();
46
+
47
+ const file = String((evt.tool_input || {}).file_path || (evt.tool_input || {}).notebook_path || '');
48
+ if (!file) return silent();
49
+ const rel = path.relative(projectDir, path.resolve(projectDir, file)).split(path.sep).join('/');
50
+
51
+ // writable while PENDING: planning + docs + kit + repo meta — never product source.
52
+ const ALLOW = [
53
+ /^docs\//, /^\.claude\//, /^\.cursor\//, /^\.github\//, /^\.aider/, /^\.git/,
54
+ /^(README|AGENTS|CLAUDE|GEMINI|CHANGELOG|LICENSE)[^/]*$/i, /^[^/]+\.md$/,
55
+ ];
56
+ if (ALLOW.some((re) => re.test(rel))) return silent();
57
+
58
+ deny(
59
+ `PLAN-APPROVAL gate (hard rule 13): docs/PLAN.md still reads "> Approval: PENDING", ` +
60
+ `so source changes are blocked (attempted: ${rel}). Get the plan signed off first — ` +
61
+ `run /overview's PLAN-APPROVAL step (or /replan, then re-approve) and stamp the header ` +
62
+ `"> Approval: approved <date> v<n>". Planning artifacts under docs/ stay writable.`
63
+ );
64
+ }
@@ -77,13 +77,15 @@ if (issues !== null) {
77
77
  emit('');
78
78
  }
79
79
 
80
- // 3b. research index
80
+ // 3b. research index — token economy: inject only the 5 newest rows, each
81
+ // truncated; the full table stays on disk for grep, sessions don't re-pay it.
81
82
  const idx = read('docs/research/INDEX.md');
82
83
  if (idx !== null) {
83
84
  const rows = idx.split('\n').filter((l) => /^\|\s*\d{4}-\d{2}-\d{2}/.test(l));
84
85
  emit(`## research/INDEX.md (${rows.length} prior investigations)`);
85
86
  emit('grep this before any new research or debugging.');
86
- for (const l of rows.slice(-15)) emit(' ' + l);
87
+ for (const l of rows.slice(-5)) emit(' ' + (l.length > 220 ? l.slice(0, 217) + '…' : l));
88
+ if (rows.length > 5) emit(` … (+${rows.length - 5} older — grep docs/research/INDEX.md)`);
87
89
  emit('');
88
90
  }
89
91
 
@@ -137,6 +139,8 @@ emit(' Hard-stops only: security / irreversible-or-outbound actions / contradic
137
139
  emit('- caveman ULTRA mode is active.');
138
140
  emit('- PLAN-APPROVAL gate (rule 13): no /phase or /sprint while STATE `plan:` reads');
139
141
  emit(' PENDING — the plan needs a human sign-off via /overview first.');
142
+ emit('- UNDERSTAND-FIRST (rule 14): new free-text task -> brief back your understanding');
143
+ emit(' (goal · scope · assumptions · blast radius) and WAIT for confirm before executing.');
140
144
  emit('- before debugging ANY error: grep ISSUES.md AND research/INDEX.md first.');
141
145
  emit('- debug attempts: WARN at 2; cap 3. AUTO: log + park the slice + continue (batched');
142
146
  emit(' report at the phase boundary). GUIDED: stop and ask you.');
@@ -137,6 +137,63 @@ AUTO governs the *dev loop between* those gates, never the plan or the money dec
137
137
 
138
138
  -----
139
139
 
140
+ ## Feature lane (hands-off delivery — `/feature`)
141
+
142
+ The lifecycle above is the GREENFIELD lane (a whole project). The **feature lane**
143
+ is the BROWNFIELD lane: one APPROVED Feature doc in → a tested + hardened +
144
+ documented feature branch out, near-100% hands-off. Humans remain at exactly three
145
+ points: **approve the doc** (entry) · **UAT** (`/uat` against the generated
146
+ TEST-PLAN) · **merge**. Production deploy is never in this lane (`/release` owns it).
147
+
148
+ Pipeline (canonical body in `.claude/commands/feature.md`):
149
+ spec completion → adversarial doc-review → mini-plan → contract surface probe →
150
+ build loop (the `/phase` machinery, feature-scoped) → review & harden → manual
151
+ test plan → docs + token stamp + summary → memory queue → delivery.
152
+
153
+ Three layers make "hands-off" real — each uses the mechanism suited to its job:
154
+
155
+ 1. **Judgment → the orchestrator + subagents.** `/feature` routes the pipeline
156
+ through the standard roles under AUTO; the circuit breaker, rules 11/12, and
157
+ blind TDD apply unchanged.
158
+ 2. **Must-happen-every-time → a lifecycle hook.** `docs/features/<slug>/GATES.md`
159
+ is the run's gate checklist; the `Stop` hook (`.claude/hooks/feature-gate.js`)
160
+ BLOCKS session end while the feature is `(active)` in STATE with unchecked
161
+ gates — and it verifies ARTIFACTS, not ticks: a checked `test-plan` /
162
+ `mini-plan` / `contract-probe` / `token-stamp` / `summary` / `memory-queued`
163
+ gate whose deliverable is missing on disk blocks too (also re-checked in the
164
+ `(done)` state). A prompt can be ignored; the hook cannot. Rule 13 gets the
165
+ same treatment: the `PreToolUse` plan gate (`.claude/hooks/plan-gate.js`)
166
+ denies source Edit/Write while `docs/PLAN.md` reads `> Approval: PENDING`
167
+ (docs/kit paths stay writable; an active feature lane is exempt — its doc
168
+ approval is its own scoped gate). Hosts without lifecycle hooks run these as
169
+ model-run rituals.
170
+ 3. **No-human-at-the-keyboard → a headless runner.** `ISSFLOW_HEADLESS=1` tells the
171
+ lane no human is present: every hard-stop degrades to a `BLOCKED.md` report +
172
+ clean exit — never a guess. Two shipped runners (`create-issflow --ci|--docker`,
173
+ sources in `.claude/templates/automation/`):
174
+ - **GitHub Actions** — label an issue `feature:approved` (or dispatch with a doc
175
+ path); the workflow runs the lane on an ephemeral runner.
176
+ - **Docker** — `node scripts/feature-docker.js <FEATURE.md>` builds
177
+ `Dockerfile.issflow` and runs the lane in an unprivileged container with the
178
+ repo mounted; the container is the sandbox that makes a skip-permissions run
179
+ acceptable. Works for any host that ships a headless CLI. The runner image
180
+ MUST contain Node ≥ 18 — the lifecycle hooks are `node .claude/hooks/*.js`,
181
+ and without node they die silently (no session context, no feature gate).
182
+ The shipped image is Node-based and the wrapper preflights `node` before
183
+ every run, including bring-your-own images (`ISSFLOW_IMAGE=<name>`).
184
+ `--worktree` isolates a run in its own `git worktree` + branch, so 2–3
185
+ features build in parallel without touching your checkout or each other
186
+ (path-identical mounts: Linux/macOS).
187
+
188
+ **Approval semantics (rule-13 scoped).** The doc header
189
+ `> Approval: APPROVED <name> <date>` is the human sign-off, scoped to that doc
190
+ only; the mini-plan inherits it while it stays inside the doc's stated scope —
191
+ scope creep hard-stops to `/change-request`. The `> Automation: none|push|push+pr`
192
+ header pre-authorizes outbound git actions for THAT run (hard-stop 1 satisfied by
193
+ recorded consent). Merge and prod deploy can never be pre-authorized.
194
+
195
+ -----
196
+
140
197
  ## BMAD integration (planning front-end)
141
198
 
142
199
  iStartSoftFlow is the EXECUTION loop; **BMAD-METHOD** is an optional PLANNING
@@ -201,14 +258,56 @@ can. Escalation is at most two hops.
201
258
 
202
259
  The orchestrator ROUTES. It does not implement or debug.
203
260
 
261
+ **Model routing (per-role tiers).** Each role's `.claude/agents/<role>.md` pins a
262
+ `model:` tier suited to its work, so the RIGHT model runs each task by default:
263
+
264
+ | Role | `model:` | Why |
265
+ |------|----------|-----|
266
+ | planner · debugger · implementer · test-author | `inherit` | hardest reasoning — follows the session model the OWNER picked (`/model` / `--model`), so one choice cascades |
267
+ | researcher · e2e-runner | `sonnet` | judgment-heavy but mid-tier is the sweet spot |
268
+ | synthesizer | `haiku` | mechanical compression — cheapest tier |
269
+
270
+ Owner wants a SPECIFIC model? Edit the role's `model:` line — values `haiku` ·
271
+ `sonnet` · `opus` · `inherit` · or a full model id. The installer is
272
+ non-destructive, so your pins survive kit updates. Hosts without per-agent model
273
+ support run everything on the session model (graceful degrade).
274
+
204
275
  -----
205
276
 
206
277
  ## Procedures (the slash-command set)
207
278
 
208
279
  Named procedures, each with a canonical body in `.claude/commands/<name>.md`.
209
280
 
281
+ **Lane routing — which entry point for which job:**
282
+
283
+ | The job | Lane |
284
+ |---------|------|
285
+ | Brand-new project (no OVERVIEW yet) | `/overview` → `/phase` (optionally `/sprint`) |
286
+ | New FEATURE on an existing product | `/feature` (scaffold the doc with `/feature new`) |
287
+ | Small, obvious, non-phase change (a fix, a rename, a copy tweak) | `/quick` |
288
+ | Scope change to already-approved work | `/change-request` |
289
+ | An OUTCOME spanning several units ("clear the feature queue") | `/goal` (drives the lanes above) |
290
+ | Whole-product quality sweep / pre-release | `/ui-audit` · `/qa-audit` · `/security-audit` · `/release` |
291
+
292
+ On ambiguity between `/quick` and `/feature`: does it add or change a public
293
+ surface or need its own acceptance criteria? -> `/feature`. Otherwise `/quick`.
294
+
210
295
  - **overview** — bootstrap a project: design-research → grill r1 → design-research
211
296
  → re-grill r2 → `OVERVIEW.md` → planner → `PLAN.md`.
297
+ - **feature [new <name> | from-story <key> | doc]** — the brownfield feature lane:
298
+ one APPROVED Feature doc → spec completion → adversarial doc-review → mini-plan
299
+ → contract probe → build loop → review & harden → manual test plan →
300
+ docs/stamps/memory → delivery. `new` scaffolds the doc from
301
+ `.claude/templates/FEATURE-template.md`; `from-story` transforms a BMAD/iSSM
302
+ story into a PENDING doc (approval stays human). Gate checklist in
303
+ `docs/features/<slug>/GATES.md`, enforced by the `Stop` hook with artifact
304
+ verification. Headless-capable (CI / Docker, `ISSFLOW_HEADLESS=1`). See "Feature lane".
305
+ - **goal [set|run|status|done|drop]** — the goal layer: declare an OUTCOME with a
306
+ measurable Done-when + budget (`set`, behind the rule-14 brief-back), then
307
+ `run` loops pick-next-unit → route lane → tick until done / budget / hard-stop.
308
+ Goal-driven, not time-driven: it stops itself on the finish line. Recurrence is
309
+ host-level (interval loop or the cron-ready `issflow-goal.yml`). `docs/GOALS.md`
310
+ holds state; its `Approved:` line arms headless passes.
212
311
  - **propose** — turn approved requirements + stack into `PROPOSAL.md` (scope, phase
213
312
  breakdown, effort + cost estimate, assumptions) with a client sign-off gate.
214
313
  - **change-request** — a mid-project scope change: impact analysis + re-estimate +
@@ -363,7 +462,7 @@ Mirrors the installer's `--dry-run`. (In a dry-run, even AUTO never acts — it
363
462
 
364
463
  -----
365
464
 
366
- ## Hard rules (1–13)
465
+ ## Hard rules (1–14)
367
466
 
368
467
  1. Before debugging ANY error: grep `docs/ISSUES.md` AND `docs/research/INDEX.md`.
369
468
  The SESSION-OPEN ritual surfaces ISSUES.md — there is no excuse to miss it.
@@ -427,6 +526,16 @@ Mirrors the installer's `--dry-run`. (In a dry-run, even AUTO never acts — it
427
526
  the planning twin of the commercial sign-off gate (`/propose`). A `/replan` that
428
527
  adds or reshapes UNBUILT scope reverts the affected plan to `PENDING` and
429
528
  re-surfaces it for confirmation before those phases run.
529
+ 14. **UNDERSTAND-FIRST gate (brief-back).** No new task starts executing on an
530
+ unconfirmed understanding. Any command that takes free-text work (`/quick`,
531
+ `/change-request`, `/goal set`, the `/overview` grill) BRIEFS BACK first:
532
+ restate the task — goal · scope · out-of-scope · assumptions · plan sketch ·
533
+ blast radius — then WAIT for explicit confirmation before touching anything.
534
+ A recorded approval artifact IS the confirmation for its lane (approved
535
+ PLAN → phases · APPROVED FEATURE doc → the feature lane · approved CR → the
536
+ change · `Approved:` goal → goal runs) — that is exactly what arms headless.
537
+ Rationale: a wrong understanding burns tokens and context at 100× the cost
538
+ of one confirm turn. AUTO governs execution AFTER intake, never instead of it.
430
539
 
431
540
  -----
432
541
 
@@ -476,6 +585,22 @@ the KB. The kit works normally without a KB.
476
585
  summary, known limitations, approver — the gate to promote to production.
477
586
  - `docs/ui-audit-<date>.md` · `docs/qa-audit-<date>.md` · `docs/security-audit-<date>.md`
478
587
  — scored whole-product audit reports (from the `*-audit` commands).
588
+ - `docs/features/<slug>/` — one dir per feature-lane run: `FEATURE.md` (the doc:
589
+ approval header, acceptance criteria, assumptions, token stamp, summary),
590
+ `PLAN.md` (mini-plan), `CONTRACTS.md` (probed public surfaces), `TEST-PLAN.md`
591
+ (the UAT scenario sheet), `GATES.md` (the Stop-hook-enforced checklist),
592
+ `BLOCKED.md` (headless blocker reports).
593
+ - `docs/WISDOM-QUEUE.md` — auto-appended wisdom candidates from feature runs;
594
+ `/store-wisdom` reads it before pushing to the shared KB (push stays human).
595
+ - `.claude/templates/automation/` — headless-runner sources (GitHub Actions ·
596
+ Dockerfile · docker wrapper), materialized by `create-issflow --ci` /
597
+ `--docker` as `.github/workflows/issflow-feature.yml` +
598
+ `.github/workflows/issflow-goal.yml` (cron-ready, disarmed by default) ·
599
+ `Dockerfile.issflow` · `scripts/feature-docker.js`.
600
+ - `docs/GOALS.md` — the goal layer's state: one `## G<n>` block per goal
601
+ (Done-when · Budget · `Approved:` line · progress ticks). Maintained by `/goal`.
602
+ - `.claude/templates/FEATURE-template.md` — the Feature-doc form `/feature new`
603
+ scaffolds (Approval/Automation headers + spec sections).
479
604
  - `docs/STATE.md` — current position. Small. Rewritten, not appended.
480
605
  - `docs/ISSUES.md` — error log. Deduped by synthesizer.
481
606
  - `docs/PLAN.md` — the phase plan (the product backlog). Carries an `> Approval:`
@@ -527,7 +652,7 @@ the same everywhere — only the *wiring* differs.
527
652
 
528
653
  | Host | Entry file | Commands | Subagents | Lifecycle hooks | Shared KB |
529
654
  |------|-----------|----------|-----------|-----------------|-----------|
530
- | **Claude Code** (reference) | `CLAUDE.md` (`@AGENTS.md`) + `.claude/` | `.claude/commands/` | native | SessionStart · PreToolUse (context-budget watchdog) · PreCompact · SubagentStop | yes |
655
+ | **Claude Code** (reference) | `CLAUDE.md` (`@AGENTS.md`) + `.claude/` | `.claude/commands/` | native | SessionStart · PreToolUse (context watchdog + rule-13 plan gate) · PreCompact · SubagentStop · Stop (feature gate) | yes |
531
656
  | **Codex CLI** | `AGENTS.md` (native) | `.claude/commands/` (read as prompts) | read as reference | model-run | yes |
532
657
  | **Cursor** | `.cursor/rules/` + `AGENTS.md` | `.cursor/commands/` | reads `.claude/agents/` | `.cursor/hooks.json` (sessionStart · subagentStop) | yes |
533
658
  | **Gemini CLI** | `GEMINI.md` + `AGENTS.md` | `.claude/commands/` (read as prompts) | read as reference | model-run | yes |
@@ -0,0 +1,43 @@
1
+ # FEATURE: <feature name>
2
+
3
+ > Status: draft
4
+ > Approval: PENDING <!-- APPROVED <name> <YYYY-MM-DD> arms the lane -->
5
+ > Automation: none <!-- none | push | push+pr — what the run may do outbound -->
6
+
7
+ <!--
8
+ The ONE human input of the feature lane. Fill it in, flip Approval to
9
+ APPROVED, then run: /feature docs/features/<slug>/FEATURE.md
10
+ (or headless: node scripts/feature-docker.js docs/features/<slug>/FEATURE.md)
11
+ Everything below Approval is the spec the agents build and test against —
12
+ write WHAT and WHY; the lane decides HOW and logs it.
13
+ -->
14
+
15
+ ## What & why
16
+
17
+ <2–5 sentences: the capability, who uses it, why now. Plain language.>
18
+
19
+ ## Scope
20
+
21
+ - <bullet the concrete behaviours in scope — each becomes acceptance criteria>
22
+ - <keep it to ONE feature; a second feature = a second doc>
23
+
24
+ ## Out of scope
25
+
26
+ - <name what this doc deliberately does NOT cover — the scope-creep fence.
27
+ The lane HARD-STOPS to /change-request if the plan needs anything here>
28
+
29
+ ## Constraints & contracts
30
+
31
+ - <APIs / schemas / integrations it must respect or extend, if known>
32
+ - <performance, compliance, or platform constraints, if any>
33
+
34
+ ## Acceptance criteria
35
+
36
+ <!-- The lane's adversarial doc-review will ATTACK this list and fold in the
37
+ negative/abuse cases it finds. Start with the positives you care about. -->
38
+ - <given/when/then or checklist form — every line must be testable>
39
+
40
+ ## Done when
41
+
42
+ - Every acceptance criterion has a passing test (blind TDD).
43
+ - Every scenario in the generated TEST-PLAN.md passes UAT.
@@ -0,0 +1,44 @@
1
+ # iStartSoftFlow — headless runner image (Docker lane).
2
+ # Installed by `npx create-issflow --docker` as Dockerfile.issflow.
3
+ # Built + run by scripts/feature-docker.js — you rarely use this file directly.
4
+ #
5
+ # The container IS the sandbox: an ephemeral, unprivileged workspace where
6
+ # `claude --dangerously-skip-permissions` is acceptable because the blast radius
7
+ # is the mounted repo only. Never mount the Docker socket or your $HOME into it.
8
+ #
9
+ # The image is tool-only; YOUR project's runtime deps are not baked in. If the
10
+ # feature lane must run your test suite and it needs more than Node (Python, Go,
11
+ # a DB client, …), extend this image: `FROM issflow-runner` + your toolchain.
12
+
13
+ FROM node:20-bookworm-slim
14
+
15
+ # git — branches/commits are the lane's deliverable. ca-certificates — API TLS.
16
+ RUN apt-get update \
17
+ && apt-get install -y --no-install-recommends git ca-certificates \
18
+ && rm -rf /var/lib/apt/lists/* \
19
+ && npm install -g @anthropic-ai/claude-code
20
+
21
+ # sanity: the kit's lifecycle hooks are `node .claude/hooks/*.js` — a runner
22
+ # image WITHOUT node runs claude fine but every hook silently dies (no
23
+ # session-start context, no feature gate). Fail the build, not the run.
24
+ RUN node --version && claude --version
25
+
26
+ # unprivileged runner; /work is the mounted repo.
27
+ RUN useradd --create-home --shell /bin/sh runner
28
+ USER runner
29
+ WORKDIR /work
30
+
31
+ # the mounted repo is owned by the HOST uid, not `runner` — without this every
32
+ # git command fails with "detected dubious ownership in repository".
33
+ RUN git config --global --add safe.directory '*'
34
+
35
+ # headless marker — /feature degrades every hard-stop to a BLOCKED report + clean exit.
36
+ ENV ISSFLOW_HEADLESS=1
37
+
38
+ # identity for the commits the lane makes (override via docker run -e).
39
+ ENV GIT_AUTHOR_NAME="issflow-runner" \
40
+ GIT_AUTHOR_EMAIL="issflow-runner@local" \
41
+ GIT_COMMITTER_NAME="issflow-runner" \
42
+ GIT_COMMITTER_EMAIL="issflow-runner@local"
43
+
44
+ ENTRYPOINT ["claude"]
@@ -0,0 +1,107 @@
1
+ #!/usr/bin/env node
2
+ // iStartSoftFlow — run the feature lane headless in Docker.
3
+ // Installed by `npx create-issflow --docker` as scripts/feature-docker.js.
4
+ // Pure Node, cross-platform (macOS / Windows / Linux). Needs: docker, ANTHROPIC_API_KEY.
5
+ //
6
+ // node scripts/feature-docker.js docs/features/<slug>/FEATURE.md [--rebuild] [--worktree]
7
+ //
8
+ // What it does: build the runner image (Dockerfile.issflow) if missing, then run
9
+ // `claude -p "/feature <doc>"` inside an ephemeral container with THIS repo
10
+ // mounted at /work. The container is the sandbox — the run edits, tests, and
11
+ // commits on feature/<slug> in your working tree. Push/PR happen only if the
12
+ // doc's `> Automation:` header authorizes them AND credentials are available
13
+ // in the container; otherwise the branch stays local and pushing is yours.
14
+ //
15
+ // --worktree: parallel lane. Mounts a FRESH `git worktree` (../<repo>-feature-<slug>,
16
+ // on branch feature/<slug>) instead of your working tree, so 2–3 features can run
17
+ // concurrently without touching your checkout or each other. Each run needs its
18
+ // own doc + slug. Results come back as commits on feature/<slug> (visible from the
19
+ // main repo); remove the dir with `git worktree remove` after merge.
20
+ 'use strict';
21
+ const fs = require('fs');
22
+ const path = require('path');
23
+ const { spawnSync } = require('child_process');
24
+
25
+ const IMAGE = process.env.ISSFLOW_IMAGE || 'issflow-runner';
26
+ const argv = process.argv.slice(2);
27
+ const rebuild = argv.includes('--rebuild');
28
+ const useWorktree = argv.includes('--worktree');
29
+ const doc = argv.find((a) => !a.startsWith('--'));
30
+
31
+ const die = (msg) => { console.error('feature-docker: ' + msg); process.exit(1); };
32
+ const run = (cmd, args, opts = {}) => spawnSync(cmd, args, { stdio: 'inherit', ...opts });
33
+
34
+ if (!doc) die('usage: node scripts/feature-docker.js <path/to/FEATURE.md> [--rebuild] [--worktree]');
35
+ const repo = process.cwd();
36
+ if (!fs.existsSync(path.join(repo, doc))) die(`Feature doc not found: ${doc} (run from the repo root)`);
37
+ if (!fs.existsSync(path.join(repo, '.claude', 'commands', 'feature.md'))) die('no .claude/commands/feature.md here — run from a repo with the kit installed');
38
+ if (!process.env.ANTHROPIC_API_KEY) die('ANTHROPIC_API_KEY is not set');
39
+
40
+ const dockerOk = spawnSync('docker', ['version'], { stdio: 'ignore' });
41
+ if (dockerOk.error || dockerOk.status !== 0) die('docker is not available');
42
+
43
+ const haveImage = spawnSync('docker', ['image', 'inspect', IMAGE], { stdio: 'ignore' }).status === 0;
44
+ if (!haveImage || rebuild) {
45
+ const df = fs.existsSync(path.join(repo, 'Dockerfile.issflow'))
46
+ ? 'Dockerfile.issflow'
47
+ : path.join('.claude', 'templates', 'automation', 'Dockerfile');
48
+ console.log(`feature-docker: building ${IMAGE} from ${df} …`);
49
+ if (run('docker', ['build', '-t', IMAGE, '-f', df, '.']).status !== 0) die('image build failed');
50
+ }
51
+
52
+ // preflight: the kit's hooks (session-start, the feature gate) are Node
53
+ // scripts. An image without node — e.g. claude installed as a native binary,
54
+ // or a bring-your-own ISSFLOW_IMAGE built on a non-Node base — runs claude
55
+ // but every hook dies silently, so the gate enforcement is gone. Refuse early.
56
+ const nodeCheck = spawnSync('docker', ['run', '--rm', '--entrypoint', 'node', IMAGE, '--version'], { stdio: 'ignore' });
57
+ if (nodeCheck.status !== 0) die(
58
+ `image "${IMAGE}" has no usable node — the kit's lifecycle hooks are Node scripts and cannot run. ` +
59
+ `Use the shipped image (delete it + rerun with --rebuild), or base your custom image on Node >= 18 ` +
60
+ `(e.g. FROM issflow-runner).`
61
+ );
62
+
63
+ // --worktree: isolate the run in its own checkout so features run in parallel.
64
+ let mountDir = repo;
65
+ if (useWorktree) {
66
+ const slug = path.basename(path.dirname(path.resolve(repo, doc))) || 'feature';
67
+ const wt = path.join(path.dirname(repo), `${path.basename(repo)}-feature-${slug}`);
68
+ const branch = `feature/${slug}`;
69
+ if (!fs.existsSync(wt)) {
70
+ console.log(`feature-docker: creating worktree ${wt} on ${branch}`);
71
+ const branchExists = spawnSync('git', ['rev-parse', '--verify', branch], { stdio: 'ignore' }).status === 0;
72
+ const wtArgs = branchExists ? ['worktree', 'add', wt, branch] : ['worktree', 'add', '-b', branch, wt];
73
+ if (run('git', wtArgs).status !== 0) die('git worktree add failed');
74
+ }
75
+ // the doc may only exist in the main tree so far — carry it into the worktree.
76
+ const wtDoc = path.join(wt, doc);
77
+ if (!fs.existsSync(wtDoc)) {
78
+ fs.mkdirSync(path.dirname(wtDoc), { recursive: true });
79
+ fs.copyFileSync(path.resolve(repo, doc), wtDoc);
80
+ }
81
+ mountDir = wt;
82
+ console.log(`feature-docker: parallel lane — this run is isolated in ${wt}; your checkout stays untouched.`);
83
+ }
84
+
85
+ console.log(`feature-docker: running /feature ${doc} in ${IMAGE}`);
86
+ const args = ['run', '--rm', '--entrypoint', 'claude'];
87
+ if (useWorktree) {
88
+ // a worktree's git metadata references BOTH trees by absolute HOST path
89
+ // (.git file -> main .git/worktrees/<n>; gitdir file -> back to the worktree).
90
+ // Mount both at their host paths so every pointer resolves in-container.
91
+ // (Path-identical mounts: Linux/macOS; on Windows use the default mode.)
92
+ args.push('-v', `${repo}:${repo}`, '-v', `${mountDir}:${mountDir}`, '-w', mountDir);
93
+ } else {
94
+ args.push('-v', `${mountDir}:/work`, '-w', '/work');
95
+ }
96
+ args.push(
97
+ '-e', 'ANTHROPIC_API_KEY',
98
+ '-e', 'ISSFLOW_HEADLESS=1'
99
+ );
100
+ // pass git identity through when the host has one configured.
101
+ for (const k of ['GIT_AUTHOR_NAME', 'GIT_AUTHOR_EMAIL', 'GIT_COMMITTER_NAME', 'GIT_COMMITTER_EMAIL'])
102
+ if (process.env[k]) args.push('-e', k);
103
+ args.push(IMAGE, '-p', `/feature ${doc}`, '--dangerously-skip-permissions');
104
+
105
+ const res = run('docker', args);
106
+ if (res.status !== 0) die(`run exited with status ${res.status} — see output above; blockers land in docs/features/<slug>/BLOCKED.md`);
107
+ console.log('feature-docker: done. Review the feature branch, then run /uat with the TEST-PLAN.');
@@ -0,0 +1,82 @@
1
+ # iStartSoftFlow — headless feature lane (GitHub Actions).
2
+ # Installed by `npx create-issflow --ci` as .github/workflows/issflow-feature.yml.
3
+ #
4
+ # Two triggers:
5
+ # 1. Label an issue `feature:approved` — the issue TITLE becomes the slug and
6
+ # the issue BODY becomes docs/features/<slug>/FEATURE.md. The body must
7
+ # already carry the `> Approval:` / `> Automation:` headers (/feature step 0b);
8
+ # the label is the human act that fires the run.
9
+ # 2. Manual dispatch with the path of a Feature doc already committed.
10
+ #
11
+ # The run pushes feature/<slug> and opens a PR when the doc says
12
+ # `> Automation: push+pr`. It NEVER merges and NEVER deploys — UAT + merge stay human.
13
+ #
14
+ # Setup: repo secret ANTHROPIC_API_KEY. Grant Actions "Read and write permissions"
15
+ # (Settings → Actions → General → Workflow permissions) so the run can push + open PRs.
16
+
17
+ name: issflow-feature
18
+
19
+ on:
20
+ issues:
21
+ types: [labeled]
22
+ workflow_dispatch:
23
+ inputs:
24
+ feature_doc:
25
+ description: Path to the approved Feature doc (docs/features/<slug>/FEATURE.md)
26
+ required: true
27
+ type: string
28
+
29
+ permissions:
30
+ contents: write
31
+ pull-requests: write
32
+ issues: write
33
+
34
+ jobs:
35
+ feature:
36
+ if: github.event_name == 'workflow_dispatch' || github.event.label.name == 'feature:approved'
37
+ runs-on: ubuntu-latest
38
+ timeout-minutes: 90
39
+ steps:
40
+ - uses: actions/checkout@v4
41
+ with:
42
+ fetch-depth: 0
43
+
44
+ # Issue trigger: materialize the issue body as the Feature doc.
45
+ - name: Write Feature doc from issue
46
+ if: github.event_name == 'issues'
47
+ env:
48
+ ISSUE_TITLE: ${{ github.event.issue.title }}
49
+ ISSUE_BODY: ${{ github.event.issue.body }}
50
+ run: |
51
+ slug=$(printf '%s' "$ISSUE_TITLE" | tr '[:upper:]' '[:lower:]' | sed -E 's/[^a-z0-9]+/-/g; s/^-+|-+$//g')
52
+ test -n "$slug"
53
+ mkdir -p "docs/features/$slug"
54
+ printf '%s\n' "$ISSUE_BODY" > "docs/features/$slug/FEATURE.md"
55
+ echo "FEATURE_DOC=docs/features/$slug/FEATURE.md" >> "$GITHUB_ENV"
56
+
57
+ - name: Resolve Feature doc path
58
+ if: github.event_name == 'workflow_dispatch'
59
+ run: echo "FEATURE_DOC=${{ inputs.feature_doc }}" >> "$GITHUB_ENV"
60
+
61
+ - name: Run the feature lane
62
+ uses: anthropics/claude-code-action@v1
63
+ env:
64
+ ISSFLOW_HEADLESS: "1"
65
+ with:
66
+ anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
67
+ github_token: ${{ secrets.GITHUB_TOKEN }}
68
+ prompt: "/feature ${{ env.FEATURE_DOC }}"
69
+ # Broad allowlist via the documented --allowedTools flag (the ephemeral
70
+ # runner is the sandbox). The Stop hook (feature-gate.js) still enforces
71
+ # the gate checklist inside the run.
72
+ claude_args: "--allowedTools Bash,Edit,Write,Read,Glob,Grep,Task,WebFetch,WebSearch,TodoWrite"
73
+
74
+ # The lane's own delivery step (git push / PR) runs inside Claude per the
75
+ # doc's `> Automation:` header. This job only reports.
76
+ - name: Report back to the issue
77
+ if: github.event_name == 'issues' && always()
78
+ env:
79
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
80
+ run: |
81
+ gh issue comment ${{ github.event.issue.number }} \
82
+ --body "issflow-feature run finished: ${{ job.status }}. See the run log + the feature/<slug> branch. Next: /uat with docs/features/<slug>/TEST-PLAN.md."
@@ -0,0 +1,48 @@
1
+ # iStartSoftFlow — scheduled goal pass (GitHub Actions).
2
+ # Installed by `npx create-issflow --ci` as .github/workflows/issflow-goal.yml.
3
+ #
4
+ # Runs ONE `/goal run` pass headless: pick next actionable unit -> route lane ->
5
+ # tick -> repeat until Done-when / budget / hard-stop. The goal's `> Approved:`
6
+ # line in docs/GOALS.md is the recorded consent that arms the run; a lane
7
+ # hard-stop writes BLOCKED.md and exits cleanly. It never merges, never deploys.
8
+ #
9
+ # DISARMED BY DEFAULT: uncomment `schedule:` to run on cron. Manual dispatch
10
+ # always works. Setup: secret ANTHROPIC_API_KEY + Actions write permissions
11
+ # (same as issflow-feature.yml).
12
+
13
+ name: issflow-goal
14
+
15
+ on:
16
+ workflow_dispatch:
17
+ inputs:
18
+ goal_id:
19
+ description: Goal id to run (blank = the single active goal)
20
+ required: false
21
+ type: string
22
+ # schedule:
23
+ # - cron: '0 1 * * 1-5' # 01:00 UTC weekdays — one pass per night
24
+
25
+ permissions:
26
+ contents: write
27
+ pull-requests: write
28
+ issues: write
29
+
30
+ jobs:
31
+ goal:
32
+ runs-on: ubuntu-latest
33
+ timeout-minutes: 120
34
+ steps:
35
+ - uses: actions/checkout@v4
36
+ with:
37
+ fetch-depth: 0
38
+
39
+ - name: Run one goal pass
40
+ uses: anthropics/claude-code-action@v1
41
+ env:
42
+ ISSFLOW_HEADLESS: "1"
43
+ with:
44
+ anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
45
+ github_token: ${{ secrets.GITHUB_TOKEN }}
46
+ prompt: "/goal run ${{ inputs.goal_id }}"
47
+ # Documented --allowedTools flag; the ephemeral runner is the sandbox.
48
+ claude_args: "--allowedTools Bash,Edit,Write,Read,Glob,Grep,Task,WebFetch,WebSearch,TodoWrite"