specrails-core 5.0.0 → 5.1.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 +103 -310
- package/bin/specrails-core.mjs +3 -1
- package/dist/installer/cli.js +4 -0
- package/dist/installer/cli.js.map +1 -1
- package/dist/installer/commands/framework.js +64 -49
- package/dist/installer/commands/framework.js.map +1 -1
- package/dist/installer/commands/init.js +102 -66
- package/dist/installer/commands/init.js.map +1 -1
- package/dist/installer/commands/update.js +80 -74
- package/dist/installer/commands/update.js.map +1 -1
- package/dist/installer/commands/v5-migration.js +14 -0
- package/dist/installer/commands/v5-migration.js.map +1 -1
- package/dist/installer/phases/framework-lifecycle.js +2 -0
- package/dist/installer/phases/framework-lifecycle.js.map +1 -1
- package/dist/installer/phases/scaffold.js +191 -258
- package/dist/installer/phases/scaffold.js.map +1 -1
- package/dist/installer/runtime/pipeline-state.js +801 -0
- package/dist/installer/runtime/pipeline-state.js.map +1 -0
- package/dist/installer/util/exec.js +6 -1
- package/dist/installer/util/exec.js.map +1 -1
- package/dist/installer/util/fs.js +11 -2
- package/dist/installer/util/fs.js.map +1 -1
- package/dist/installer/util/install-transaction.js +246 -0
- package/dist/installer/util/install-transaction.js.map +1 -0
- package/dist/installer/util/registry.js +20 -0
- package/dist/installer/util/registry.js.map +1 -1
- package/docs/ci-cd.md +57 -0
- package/docs/user-docs/codex-vs-claude-code.md +23 -151
- package/docs/user-docs/core-updates.md +70 -0
- package/docs/user-docs/provider-pipelines.md +53 -0
- package/integration-contract.json +179 -66
- package/package.json +5 -2
- package/templates/agents/sr-developer.md +9 -11
- package/templates/agents/sr-reviewer.md +26 -33
- package/templates/codex-skills/batch-implement/SKILL.md +58 -244
- package/templates/codex-skills/implement/SKILL.md +136 -338
- package/templates/codex-skills/rails/sr-architect/SKILL.md +7 -0
- package/templates/codex-skills/rails/sr-developer/SKILL.md +13 -0
- package/templates/codex-skills/rails/sr-reviewer/SKILL.md +39 -5
- package/templates/codex-skills/retry/SKILL.md +37 -117
- package/templates/commands/specrails/batch-implement.md +16 -288
- package/templates/commands/specrails/implement.md +62 -1057
- package/templates/commands/specrails/retry.md +22 -314
- package/templates/gemini-commands/batch-implement.toml +28 -40
- package/templates/gemini-commands/implement.toml +55 -114
- package/templates/gemini-commands/retry.toml +21 -0
- package/templates/kimi/specrails/run-skill.mjs +51 -2
- package/templates/runtime/provider-pipeline.md +55 -0
|
@@ -1,68 +1,42 @@
|
|
|
1
1
|
# Implementation Pipeline
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Implement one feature or a batch as one frozen candidate: architect designs, developer implements, reviewer validates, then the coordinator authorizes archive and delivery. Reuse completed phases only when the executable journal confirms current evidence.
|
|
4
4
|
|
|
5
|
-
**
|
|
5
|
+
**Input:** $ARGUMENTS: ticket references (`#85, #71`), a feature description, area names to explore before selecting scope, `--dry-run`/`--preview`, or `--apply <change>`.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## Execution scope and ownership (read first)
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
2. **Text description** (single feature): `"add price history chart"` — implement a single feature from a description. Skips exploration and selection.
|
|
11
|
-
3. **Area names** (fallback): `Analytics, UI, Testing` — explores areas and picks the best items. Only use if no backlog tickets exist.
|
|
9
|
+
Use the installed runtime `node "${SPECRAILS_PIPELINE_RUNTIME:-.specrails/runtime/pipeline.mjs}"`, never an assumed global/downloaded substitute. `SPECRAILS_EXECUTION_CONTEXT` identifies the authoritative schemaVersion 1 context: runId, frozen specs[] with acceptance criteria, backlogRoot/backlogPath, artifactRoot/artifactRepositoryId, selected repositories[] and ownership. Do not refetch tickets to replace this scope.
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
The working directory may be a framework workspace. Set `SPECRAILS_REPO_DIR` to artifactRoot for `${SPECRAILS_REPO_DIR:-.}/openspec/changes/<change>/`. Source edits and tests use the explicit selected repository ID/path; secondary tasks never default to the artifact root. Backlog belongs to backlogPath, independently of source roots.
|
|
14
12
|
|
|
15
|
-
|
|
13
|
+
Inspect ownership **before any mutation**:
|
|
14
|
+
- Host-owned worktrees: use supplied roots; no nested allocation, merge, deletion or reconstruction.
|
|
15
|
+
- Host-owned git: no staging, commits, pushes, PRs or shipping CI; return the candidate for host delivery.
|
|
16
|
+
- Host-owned backlog: no closing tickets/status writes; host acceptance owns completion.
|
|
17
|
+
- Core ownership permits only operations authorized by the user/settings. Preview or false GIT_AUTO disables shipping regardless. Never change permissions or forge context to bypass ownership.
|
|
16
18
|
|
|
17
|
-
|
|
19
|
+
Give every worker the absolute runtime/context paths, exact change, selected roots and frozen task group. Include env in each invocation; exports do not persist across tools. Workers stay foreground and must return terminal results. A background task start, summary or empty final message is not completion evidence.
|
|
18
20
|
|
|
19
|
-
|
|
21
|
+
### Standalone admission
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
- **openspec reads/writes** → `${SPECRAILS_REPO_DIR:-.}/openspec/...`
|
|
23
|
-
- **git commands** → `git -C "${SPECRAILS_REPO_DIR:-.}" ...`
|
|
24
|
-
- **`gh` commands** (PR — they need the repo's remote) → run them from the repo: `(cd "${SPECRAILS_REPO_DIR:-.}" && gh ...)`
|
|
25
|
-
- **worktree merge-back** → the merge *target* side (the main working tree) is `${SPECRAILS_REPO_DIR:-.}/<file>`
|
|
26
|
-
|
|
27
|
-
**Run-state stays with the working directory** (NOT the repo): `.claude/pipeline-state/`, `.claude/agent-memory/`, `.claude/backlog-cache.json`, and the dry-run cache `.claude/.dry-run/` are all written relative to the current directory. **Profile/agent files** (`.claude/agents/sr-*.md`) are likewise resolved relative to the current directory — do NOT prefix them with `${SPECRAILS_REPO_DIR:-.}`.
|
|
28
|
-
|
|
29
|
-
---
|
|
30
|
-
|
|
31
|
-
## Phase -1: Environment Setup (cloud pre-flight)
|
|
32
|
-
|
|
33
|
-
**This phase runs BEFORE anything else.** Detect if we're in a cloud/remote environment and ensure all required tools are available.
|
|
34
|
-
|
|
35
|
-
### Detection
|
|
36
|
-
|
|
37
|
-
Check the environment variable `CLAUDE_CODE_ENTRYPOINT`. If it contains `remote_mobile` or `remote_web`, OR if `CLAUDE_CODE_REMOTE` is `true`, we're in a **cloud environment**.
|
|
38
|
-
|
|
39
|
-
### Checks to run (sequential, fail-fast)
|
|
40
|
-
|
|
41
|
-
#### 1. Backlog provider availability
|
|
23
|
+
Without host context, choose one stable aggregate change and freeze requested tickets once:
|
|
42
24
|
|
|
43
25
|
```bash
|
|
44
|
-
|
|
26
|
+
node "${SPECRAILS_PIPELINE_RUNTIME:-.specrails/runtime/pipeline.mjs}" init --change <change> --tickets "85,71"
|
|
45
27
|
```
|
|
46
|
-
- Set `LOCAL_TICKETS_AVAILABLE=true/false` based on file existence.
|
|
47
|
-
- Set `GH_AVAILABLE=false` (backlog is always local).
|
|
48
|
-
- Set `BACKLOG_AVAILABLE=true` if local-tickets.json exists.
|
|
49
|
-
|
|
50
|
-
#### 2. OpenSpec CLI
|
|
51
28
|
|
|
52
|
-
|
|
53
|
-
which openspec && openspec --version
|
|
54
|
-
```
|
|
29
|
+
Optional `--backlog-path <absolute-file>` selects a file under the workspace. Freeform/selected proposals use `--scope-request <absolute-file>` containing `{"specs":[{"id":"feature","title":"...","description":"...","acceptanceCriteria":["..."]}],"ownership":{"git":"host","backlog":"host","worktrees":"host"}}`. Populate real user requirements; never invent a ticket or discard scope. Complete multi-repository standalone scope uses `--context <absolute-file>`.
|
|
55
30
|
|
|
56
|
-
-
|
|
57
|
-
- If install fails: **STOP** — openspec is required.
|
|
31
|
+
Default fallback ownership is review-only. An explicitly authorized Core ownership request may accompany --tickets without a specs field. After init, run status and pass the returned absolute stateDir/context.json to every child/retry as SPECRAILS_EXECUTION_CONTEXT. Same-change init preserves progress and original scope; another change creates a new run. Never select the most recently modified artifact directory or use old ad-hoc state as authority.
|
|
58
32
|
|
|
59
|
-
|
|
33
|
+
## Phase 0: Preflight
|
|
60
34
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
35
|
+
1. Read supplied context or perform standalone admission. Resolve --apply to its existing journal; never silently initialize a missing preview.
|
|
36
|
+
2. Confirm Node, Git, managed runtime, official OpenSpec commands/skills and project dependencies. Missing required tooling stops with a concrete repair command. Do not emulate missing skills or silently install an unpinned global.
|
|
37
|
+
3. Inspect dependencies and CI commands once per selected repo; relevant package/config changes invalidate assumptions.
|
|
38
|
+
4. Resolve the profile below and honor host model/effort/custom roles.
|
|
39
|
+
5. Read `status --json`: a completed valid phase requires no new model call; blocked/failed phases are resumable and dependents remain pending.
|
|
66
40
|
|
|
67
41
|
#### 5. Agent discovery
|
|
68
42
|
|
|
@@ -185,1060 +159,91 @@ ROUTING="$(jq '.routing' <<<"$PROFILE")"
|
|
|
185
159
|
PROFILE_NAME="$(jq -r '.name' <<<"$PROFILE")"
|
|
186
160
|
```
|
|
187
161
|
|
|
188
|
-
##### Apply per-agent model overrides (only when a profile declares them)
|
|
189
|
-
|
|
190
|
-
Claude Code's Agent tool determines a subagent's model from the `model:` line in the agent's `.md` frontmatter at invocation time — there is no per-call model parameter. When a profile declares model overrides, rewrite each agent's frontmatter `model:` value in-place to match `AGENT_MODEL[$id]`.
|
|
191
|
-
|
|
192
|
-
This rewrite is safe because:
|
|
193
|
-
- Multi-feature runs execute in **isolated git worktrees** (`isolation: worktree`), so each rail mutates its own copy of `.claude/agents/` without cross-rail contention.
|
|
194
|
-
- Single-feature runs are sequential within a single checkout.
|
|
195
|
-
- The desktop app writes a job-scoped snapshot of the profile and spawns `claude` with `$SPECRAILS_PROFILE_PATH` pointing at it; the frontmatter rewrite follows the snapshot, never the catalog.
|
|
196
|
-
|
|
197
|
-
```bash
|
|
198
|
-
for id in "${!AGENT_MODEL[@]}"; do
|
|
199
|
-
model="${AGENT_MODEL[$id]}"
|
|
200
|
-
file=".claude/agents/$id.md"
|
|
201
|
-
[[ -f "$file" ]] || continue
|
|
202
|
-
# Rewrite the first `model:` line within the frontmatter block (lines between the
|
|
203
|
-
# first two `---` separators). Use awk with portable syntax (macOS + Linux).
|
|
204
|
-
awk -v new="$model" '
|
|
205
|
-
BEGIN { in_fm=0; done=0 }
|
|
206
|
-
/^---$/ { in_fm = !in_fm; print; next }
|
|
207
|
-
in_fm && !done && /^model:[[:space:]]/ { print "model: " new; done=1; next }
|
|
208
|
-
{ print }
|
|
209
|
-
' "$file" > "$file.tmp" && mv "$file.tmp" "$file"
|
|
210
|
-
done
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
If a profile does not declare `model` for a given agent (the field is optional), that agent's frontmatter is left untouched.
|
|
214
|
-
|
|
215
|
-
##### Agent roles
|
|
216
|
-
|
|
217
|
-
The pipeline ships exactly three first-party agents. Any additional agent comes from an active profile that declares a user-owned `custom-*` agent (with routing) — the installer never ships or manages non-core agents.
|
|
218
|
-
|
|
219
|
-
| Agent | Role | Required? | Phase(s) affected |
|
|
220
|
-
|-------|------|-----------|-------------------|
|
|
221
|
-
| sr-architect | Architecture & design | **Core** (always present) | 3a |
|
|
222
|
-
| sr-developer | Full-stack implementation | **Core** (always present) | 3b |
|
|
223
|
-
| sr-reviewer | Quality gate (correctness, tests, security, performance) | **Core** (always present) | 4b |
|
|
224
|
-
| custom-* | Profile-declared specialist | Optional — only when an active profile lists it | per profile routing |
|
|
225
|
-
|
|
226
|
-
**Gate rules** (applied throughout the pipeline):
|
|
227
|
-
- The three baseline agents are the source of truth for the non-profile default; a profile's `agents[]` (baseline + any `custom-*`) is the source of truth when a profile is active. Agents not in `AVAILABLE_AGENTS` are unavailable regardless of what is on disk.
|
|
228
|
-
- If a `custom-*` agent routed by the profile is not in `AVAILABLE_AGENTS` (e.g. skipped above), that routing target is dropped — the task falls through to the profile's `default: true` rule.
|
|
229
|
-
- If a core agent is missing, **STOP** and print: `[error] Core agent <name> not found. Run npx specrails-core update to reinstall.`
|
|
230
|
-
|
|
231
|
-
### Summary
|
|
232
|
-
|
|
233
|
-
Print a setup report:
|
|
234
|
-
|
|
235
|
-
```
|
|
236
|
-
## Environment Setup
|
|
237
|
-
| Tool | Status | Notes |
|
|
238
|
-
|------|--------|-------|
|
|
239
|
-
| Backlog provider | ok/missing | {{BACKLOG_PROVIDER_NAME}} |
|
|
240
|
-
| OpenSpec | ok | ... |
|
|
241
|
-
| Dependencies | ok | ... |
|
|
242
|
-
| Test runner | ok | ... |
|
|
243
|
-
| Agents | N available | baseline: 3/3, custom (profile): M |
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
**Pass `TEST_CMD`, `BACKLOG_AVAILABLE`, and `AVAILABLE_AGENTS` forward** — all later phases must use these.
|
|
247
|
-
|
|
248
|
-
---
|
|
249
|
-
|
|
250
|
-
## Phase 0: Parse input and determine mode
|
|
251
|
-
|
|
252
|
-
### Flag Detection
|
|
253
|
-
|
|
254
|
-
Before parsing input, scan `$ARGUMENTS` for control flags:
|
|
255
|
-
|
|
256
|
-
- If `--dry-run` or `--preview` is present in `$ARGUMENTS`:
|
|
257
|
-
- Set `DRY_RUN=true`
|
|
258
|
-
- Strip the flag from the arguments before further parsing
|
|
259
|
-
- Print: `[dry-run] Preview mode active — no git, PR, or backlog operations will run.`
|
|
260
|
-
- Set `CACHE_DIR=.claude/.dry-run/<kebab-case-feature-name>` (derive after parsing the remaining input)
|
|
261
|
-
- Note: if a cache already exists at `CACHE_DIR`, print `[dry-run] Overwriting existing cache at CACHE_DIR` before overwriting.
|
|
262
|
-
|
|
263
|
-
- If `--apply <feature-name>` is present in `$ARGUMENTS`:
|
|
264
|
-
- Set `APPLY_MODE=true`
|
|
265
|
-
- Set `APPLY_TARGET=<feature-name>` (the argument immediately following `--apply`)
|
|
266
|
-
- Set `CACHE_DIR=.claude/.dry-run/<feature-name>`
|
|
267
|
-
- Verify `CACHE_DIR` exists. If it does not: print `[apply] Error: no cached dry-run found at CACHE_DIR` and stop.
|
|
268
|
-
- Skip Phases 1–4b. Go directly to Phase 4c (the apply path handles the rest).
|
|
269
|
-
- Strip `--apply` and the feature name before further parsing.
|
|
270
|
-
|
|
271
|
-
- If `--confidence-override "<reason>"` is present in `$ARGUMENTS`:
|
|
272
|
-
- Set `CONFIDENCE_OVERRIDE_REASON=<reason>` (the quoted string immediately following `--confidence-override`)
|
|
273
|
-
- Strip `--confidence-override` and the reason before further parsing.
|
|
274
|
-
- The one flag bypasses BOTH confidence gates: the architect's design-confidence gate (Phase 3a.3) and the reviewer's score gate (Phase 4b-conf).
|
|
275
|
-
|
|
276
|
-
If none of these flags is present: `DRY_RUN=false`, `APPLY_MODE=false`, `CONFIDENCE_OVERRIDE_REASON=""`. Pipeline runs as normal.
|
|
277
|
-
|
|
278
|
-
Note: `CACHE_DIR` for `--dry-run` is finalized after the feature name is derived from the remaining input. All subsequent phases that reference `CACHE_DIR` have access to it.
|
|
279
|
-
|
|
280
|
-
Initialize conflict-tracking variables:
|
|
281
|
-
- `SNAPSHOTS_CAPTURED=false` — set to true in Phase 0 if issue snapshots are successfully written.
|
|
282
|
-
- `CONFLICT_OVERRIDES=[]` — list of conflict records where the user chose to continue; appended by Phase 3a.0 and Phase 4c.0.
|
|
283
|
-
|
|
284
|
-
---
|
|
285
162
|
|
|
286
|
-
|
|
287
|
-
- **Single-feature mode**. Derive a kebab-case change name.
|
|
288
|
-
- Set `SINGLE_MODE = true`. No worktrees, no parallelism.
|
|
289
|
-
- Go directly to Phase 3a.
|
|
163
|
+
##### Invocation configuration
|
|
290
164
|
|
|
291
|
-
|
|
292
|
-
- Fetch each ticket from `.specrails/local-tickets.json` at `tickets["{id}"]`.
|
|
293
|
-
- Extract area, value, effort, and feature details from each ticket body.
|
|
294
|
-
- If only 1 ticket: set `SINGLE_MODE = true`.
|
|
295
|
-
- Go directly to the confirmation table.
|
|
165
|
+
Forward resolved models through supported per-invocation configuration; never rewrite shared agent frontmatter or profiles. If an override cannot be honored, report that limitation, not a false model claim. The caller selects the orchestrator model before launch.
|
|
296
166
|
|
|
297
|
-
|
|
167
|
+
Route task groups by the first matching rule and final default. Missing optional targets fall through to default. Custom roles assist a canonical phase; they never bypass its acceptance gate. The baseline trio is required.
|
|
298
168
|
|
|
299
|
-
|
|
169
|
+
## Durable phases and verification
|
|
300
170
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
For each resolved ticket ID, read `.specrails/local-tickets.json` and extract the ticket object at `tickets["{id}"]`.
|
|
304
|
-
|
|
305
|
-
Build a snapshot object for each ticket:
|
|
306
|
-
- `number`: ticket `id` (integer)
|
|
307
|
-
- `title`: ticket `title` string
|
|
308
|
-
- `state`: map ticket `status` — `"done"` or `"cancelled"` → `"closed"`, otherwise → `"open"`
|
|
309
|
-
- `assignees`: `[ticket.assignee]` if non-null, else `[]`
|
|
310
|
-
- `labels`: ticket `labels` array, sorted alphabetically
|
|
311
|
-
- `body_sha`: SHA-256 of the ticket `description` string — compute with:
|
|
312
|
-
```bash
|
|
313
|
-
echo -n "{description}" | sha256sum | cut -d' ' -f1
|
|
314
|
-
```
|
|
315
|
-
If `sha256sum` is not available, fall back to `openssl dgst -sha256 -r` or `shasum -a 256`.
|
|
316
|
-
- `updated_at`: ticket `updated_at` value
|
|
317
|
-
- `captured_at`: current local time in ISO 8601 format
|
|
318
|
-
|
|
319
|
-
Write the following JSON to `.claude/backlog-cache.json` (overwrite fully — this establishes a fresh baseline for this run):
|
|
320
|
-
|
|
321
|
-
```json
|
|
322
|
-
{
|
|
323
|
-
"schema_version": "1",
|
|
324
|
-
"provider": "local",
|
|
325
|
-
"last_updated": "<ISO 8601 timestamp>",
|
|
326
|
-
"written_by": "implement",
|
|
327
|
-
"issues": {
|
|
328
|
-
"<id>": { <snapshot object> },
|
|
329
|
-
...
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
If the write succeeds: set `SNAPSHOTS_CAPTURED=true`.
|
|
335
|
-
|
|
336
|
-
If the write fails: print `[backlog-cache] Warning: could not write cache. Conflict detection disabled for this run.` and set `SNAPSHOTS_CAPTURED=false`. Do NOT abort the pipeline.
|
|
337
|
-
|
|
338
|
-
##### Otherwise (no backlog available or non-ticket input):
|
|
339
|
-
|
|
340
|
-
Set `SNAPSHOTS_CAPTURED=false`. Print: `[conflict-check] Snapshot skipped — backlog unavailable or non-issue input.`
|
|
341
|
-
|
|
342
|
-
#### Gitignore advisory
|
|
343
|
-
|
|
344
|
-
If `SNAPSHOTS_CAPTURED=true`, check whether `.gitignore` already covers the cache file:
|
|
345
|
-
|
|
346
|
-
```bash
|
|
347
|
-
grep -q "backlog-cache" .gitignore 2>/dev/null || \
|
|
348
|
-
grep -q "\.claude/" .gitignore 2>/dev/null
|
|
349
|
-
```
|
|
350
|
-
|
|
351
|
-
If neither pattern is found, print:
|
|
352
|
-
|
|
353
|
-
```
|
|
354
|
-
[backlog-cache] Suggestion: add '.claude/backlog-cache.json' to .gitignore to avoid committing ephemeral cache state.
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
This advisory is non-blocking and suppressed when `.gitignore` already covers the file.
|
|
358
|
-
|
|
359
|
-
#### Pipeline state initialization
|
|
360
|
-
|
|
361
|
-
Set `PIPELINE_STATE_PATH=.claude/pipeline-state/<feature-name>.json` (use the same kebab-case feature name derived above).
|
|
362
|
-
|
|
363
|
-
Create the directory if it does not exist:
|
|
171
|
+
Use `architect → developer → reviewer → archive → ship → ci`. Start a phase with running; only actual completion and its runtime gate permit done. Failed/blocked needs a concrete reason. Reopening resets dependent completion. Never skip an implementation phase because its predecessor failed.
|
|
364
172
|
|
|
365
173
|
```bash
|
|
366
|
-
|
|
174
|
+
node "${SPECRAILS_PIPELINE_RUNTIME:-.specrails/runtime/pipeline.mjs}" phase --phase architect --status running
|
|
175
|
+
node "${SPECRAILS_PIPELINE_RUNTIME:-.specrails/runtime/pipeline.mjs}" phase --phase architect --status done
|
|
176
|
+
node "${SPECRAILS_PIPELINE_RUNTIME:-.specrails/runtime/pipeline.mjs}" phase --phase reviewer --status blocked --reason "Unresolved acceptance condition"
|
|
367
177
|
```
|
|
368
178
|
|
|
369
|
-
|
|
179
|
+
Store check requests/notes under stateDir, not as candidate files. Verification is executable argv:
|
|
370
180
|
|
|
371
181
|
```json
|
|
372
|
-
{
|
|
373
|
-
"schema_version": "1",
|
|
374
|
-
"feature": "<feature-name>",
|
|
375
|
-
"started_at": "<current ISO 8601 timestamp>",
|
|
376
|
-
"updated_at": "<current ISO 8601 timestamp>",
|
|
377
|
-
"phases": {
|
|
378
|
-
"architect": "pending",
|
|
379
|
-
"developer": "pending",
|
|
380
|
-
"reviewer": "pending",
|
|
381
|
-
"ship": "pending",
|
|
382
|
-
"ci": "pending"
|
|
383
|
-
},
|
|
384
|
-
"last_successful_phase": null,
|
|
385
|
-
"failed_phase": null,
|
|
386
|
-
"error_context": null,
|
|
387
|
-
"openspec_artifacts": "openspec/changes/<feature-name>/",
|
|
388
|
-
"implemented_files": [],
|
|
389
|
-
"input": {
|
|
390
|
-
"issues": [<issue numbers, or null for text-description mode>],
|
|
391
|
-
"flags": {
|
|
392
|
-
"dry_run": <DRY_RUN>,
|
|
393
|
-
"apply_mode": <APPLY_MODE>,
|
|
394
|
-
"single_mode": <SINGLE_MODE>
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
```
|
|
399
|
-
|
|
400
|
-
If the write fails: print `[pipeline-state] Warning: could not write state file. Smart retry (/specrails:retry) will not be available for this run.` Set `PIPELINE_STATE_AVAILABLE=false`. Do NOT abort the pipeline.
|
|
401
|
-
|
|
402
|
-
If the write succeeds: set `PIPELINE_STATE_AVAILABLE=true`.
|
|
403
|
-
|
|
404
|
-
**State update helper** — used by all subsequent phases to record progress:
|
|
405
|
-
|
|
406
|
-
When a phase completes or fails, update `PIPELINE_STATE_PATH`:
|
|
407
|
-
1. Read the current file.
|
|
408
|
-
2. Set `phases.<phase-key>` to `"done"`, `"failed"`, or `"skipped"`.
|
|
409
|
-
3. If `"done"`: set `last_successful_phase` to the phase key.
|
|
410
|
-
4. If `"failed"`: set `failed_phase` to the phase key; set `error_context` to a one-line description of the failure (agent name, error type, exit code if known).
|
|
411
|
-
5. Set `updated_at` to the current ISO 8601 timestamp.
|
|
412
|
-
6. Overwrite the file.
|
|
413
|
-
|
|
414
|
-
If `PIPELINE_STATE_AVAILABLE=false`: skip all state updates silently.
|
|
415
|
-
|
|
416
|
-
**If the user passed area names** (no concrete issue/spec):
|
|
417
|
-
- Check for open backlog issues. If found, filter and pick top 3.
|
|
418
|
-
- If none, STOP with: `[input] No backlog issue or spec resolved from the given area(s). Product exploration was removed in v5 — pass a backlog issue number or a feature description/spec.`
|
|
419
|
-
|
|
420
|
-
---
|
|
421
|
-
|
|
422
|
-
## Phase 1 & 2: (removed in v5)
|
|
423
|
-
|
|
424
|
-
Product exploration and idea selection were driven by `sr-product-manager`, which is not shipped in v5. The pipeline works from concrete inputs — backlog issues or a feature description/spec passed to the command — and proceeds directly from Phase 0 to Phase 3a. If you want product discovery, run it as a separate step and feed the resulting spec/issue into `/specrails:implement`.
|
|
425
|
-
|
|
426
|
-
## Phase 3a.0: Pre-architect conflict check
|
|
427
|
-
|
|
428
|
-
**Guard:** If `SNAPSHOTS_CAPTURED=false` OR `DRY_RUN=true`, print `[conflict-check] Skipped — SNAPSHOTS_CAPTURED=false (or dry-run mode).` and proceed directly to Phase 3a.
|
|
429
|
-
|
|
430
|
-
Otherwise, re-fetch each ticket in scope and diff against the Phase 0 snapshot:
|
|
431
|
-
|
|
432
|
-
For each ticket ID in `ISSUE_REFS`, read `.specrails/local-tickets.json` and extract the ticket at `tickets["{id}"]`. If the ticket does not exist (deleted): treat as a CRITICAL conflict — field `"state"`, was `<cached state>`, now `"deleted"`. Otherwise, reconstruct a current snapshot using the same mapping as the Phase 0 local snapshot (sort `assignees` and `labels`, compute `body_sha`).
|
|
433
|
-
|
|
434
|
-
**Short-circuit:** If `current.updatedAt == cached.updated_at`, mark the issue as clean and skip field comparison.
|
|
435
|
-
|
|
436
|
-
**Field comparison** (only when `updatedAt` differs):
|
|
437
|
-
|
|
438
|
-
| Field | Conflict if... | Severity |
|
|
439
|
-
|-------|----------------|----------|
|
|
440
|
-
| `state` | value differs (`open` → `closed`) | CRITICAL |
|
|
441
|
-
| `state` | value differs (`closed` → `open`) | WARNING |
|
|
442
|
-
| `title` | string differs | WARNING |
|
|
443
|
-
| `assignees` | sorted array differs | WARNING |
|
|
444
|
-
| `labels` | sorted array differs | INFO |
|
|
445
|
-
| `body_sha` | SHA differs | WARNING |
|
|
446
|
-
|
|
447
|
-
Collect all conflicts across all issues. If none: print `[conflict-check] All issues clean (Phase 3a.0). Proceeding.` and continue to Phase 3a.
|
|
448
|
-
|
|
449
|
-
**If conflicts exist**, print the following report and await user input:
|
|
450
|
-
|
|
451
|
-
```
|
|
452
|
-
## Backlog Conflict Detected
|
|
453
|
-
|
|
454
|
-
The following issues changed since Phase 0 snapshot (captured at <captured_at>):
|
|
455
|
-
|
|
456
|
-
| Issue | Field | Severity | Was | Now |
|
|
457
|
-
|-------|-------|----------|-----|-----|
|
|
458
|
-
| #N | state | CRITICAL | open | closed |
|
|
459
|
-
| #N | body | WARNING | <sha-prefix> | <sha-prefix> |
|
|
460
|
-
|
|
461
|
-
How would you like to proceed?
|
|
462
|
-
[A] Abort — stop the pipeline and exit cleanly
|
|
463
|
-
[C] Continue — proceed despite the conflicts (logged)
|
|
464
|
-
|
|
465
|
-
Enter A or C:
|
|
466
|
-
```
|
|
467
|
-
|
|
468
|
-
For `body_sha` rows in the table, display only the first 8 characters of each SHA as the "Was" and "Now" values.
|
|
469
|
-
|
|
470
|
-
**Input handling:**
|
|
471
|
-
- Accept `A`, `a` (abort) or `C`, `c` (continue).
|
|
472
|
-
- Re-prompt on any other input, up to 3 times total.
|
|
473
|
-
- After 3 invalid inputs: print `[conflict-abort] Defaulting to abort after 3 invalid inputs.` and abort.
|
|
474
|
-
|
|
475
|
-
**On abort:** Print `[conflict-abort] Pipeline aborted. Re-run /specrails:implement after resolving the issues.` and exit. No git state is left behind.
|
|
476
|
-
|
|
477
|
-
**On continue:** Print `[conflict-override] Continuing. N conflict(s) logged.` Append each conflict to `CONFLICT_OVERRIDES` as `{phase: "3a.0", issue: "#N", field: "<field>", severity: "<severity>", was: "<was>", now: "<now>"}`. Proceed to Phase 3a.
|
|
478
|
-
|
|
479
|
-
## Phase 3a: Architect (parallel, in main repo)
|
|
480
|
-
|
|
481
|
-
For each chosen idea, launch an **sr-architect** agent (`subagent_type: sr-architect`, `run_in_background: true`).
|
|
482
|
-
|
|
483
|
-
Each architect creates OpenSpec artifacts in `${SPECRAILS_REPO_DIR:-.}/openspec/changes/<name>/`.
|
|
484
|
-
|
|
485
|
-
Each agent's prompt should include:
|
|
486
|
-
- Description of the feature
|
|
487
|
-
- Context from exploration (if applicable)
|
|
488
|
-
- Instructions to create: proposal.md, design.md, delta-spec, tasks.md, context-bundle.md
|
|
489
|
-
- Tags for each task: {{LAYER_TAGS}}
|
|
490
|
-
|
|
491
|
-
### 3a.1 Identify shared file conflicts
|
|
492
|
-
|
|
493
|
-
**Only runs in multi-feature mode** (more than one feature). Skip entirely if `SINGLE_MODE=true`.
|
|
494
|
-
|
|
495
|
-
After all architect agents complete, before launching any developer agent:
|
|
496
|
-
|
|
497
|
-
#### Step 1: Extract file references
|
|
498
|
-
|
|
499
|
-
For each `${SPECRAILS_REPO_DIR:-.}/openspec/changes/<name>/tasks.md`, extract all paths listed under `**Files:**` entries (both `Create:` and `Modify:` lines). Normalize paths: strip leading `./`.
|
|
500
|
-
|
|
501
|
-
#### Step 2: Build the shared-file registry
|
|
502
|
-
|
|
503
|
-
Group file paths across all features. Any path appearing in two or more features' task lists is a **shared file**. Store as `SHARED_FILES` map: `{path: {features: [...], risk: ""}}`.
|
|
504
|
-
|
|
505
|
-
#### Step 3: Classify risk
|
|
506
|
-
|
|
507
|
-
For each shared file, classify risk based on file type and which regions each feature modifies (consult each feature's context-bundle.md "Exact Changes" section):
|
|
508
|
-
|
|
509
|
-
| Risk | Condition |
|
|
510
|
-
|------|-----------|
|
|
511
|
-
| `low` | Both features only append new named sections not present in the other feature's changes |
|
|
512
|
-
| `medium` | Both features modify structurally distinct regions (different `##` sections or different top-level YAML keys) |
|
|
513
|
-
| `high` | Both features modify the same region (same `##` section, same YAML key subtree, or any region in shell scripts) |
|
|
514
|
-
|
|
515
|
-
Shell scripts (`.sh`, `.bash`): always `high`.
|
|
516
|
-
Non-existent files that two features both create: always `high`.
|
|
517
|
-
|
|
518
|
-
#### Step 4: Derive MERGE_ORDER
|
|
519
|
-
|
|
520
|
-
Sort features so that for any pair sharing a `high`-risk file, one appears before the other. Use topological sort; break ties alphabetically. Set `MERGE_ORDER` = sorted feature list.
|
|
521
|
-
|
|
522
|
-
#### Step 5: Print pre-flight report
|
|
523
|
-
|
|
524
|
-
```
|
|
525
|
-
## Shared File Analysis
|
|
526
|
-
|
|
527
|
-
| File | Features | Risk |
|
|
528
|
-
|------|----------|------|
|
|
529
|
-
| <path> | <feature-a>, <feature-b> | <risk> |
|
|
530
|
-
|
|
531
|
-
Merge order: <feature-a> → <feature-b> → <feature-c>
|
|
532
|
-
|
|
533
|
-
High-risk files detected. These files will be merged sequentially.
|
|
534
|
-
Developers will still run in parallel — merge order applies at Phase 4a only.
|
|
535
|
-
```
|
|
536
|
-
|
|
537
|
-
If no shared files: print `No shared files detected. All features modify independent files.`
|
|
538
|
-
|
|
539
|
-
### 3a.2 Pre-validate architect output
|
|
540
|
-
|
|
541
|
-
Quick-check each architect's artifacts:
|
|
542
|
-
1. tasks.md exists and has tasks
|
|
543
|
-
2. context-bundle.md exists
|
|
544
|
-
3. File references are real (>70% must exist)
|
|
545
|
-
4. Layer tags present on tasks
|
|
546
|
-
|
|
547
|
-
**Pipeline state:** update `architect` → `done`. If any architect agent failed (skipped area): update `architect` → `failed` with error context `"sr-architect failed for: <area-names>"`.
|
|
548
|
-
|
|
549
|
-
### 3a.3 Design confidence gate
|
|
550
|
-
|
|
551
|
-
Implementation is the expensive phase — it only runs on designs the architect actually trusts. For each feature, read:
|
|
552
|
-
|
|
553
|
-
```
|
|
554
|
-
${SPECRAILS_REPO_DIR:-.}/openspec/changes/<name>/design-confidence.json
|
|
555
|
-
```
|
|
556
|
-
|
|
557
|
-
- **File missing** (pre-gate architect output): print `[design-confidence] Warning: design-confidence.json not found for <name>. Proceeding without gate.` and continue — backward compatible.
|
|
558
|
-
- **`confidence: "high"` or `"medium"`**: print `[design-confidence] <name>: <level>. Proceeding.` and continue to Phase 3b.
|
|
559
|
-
- **`confidence: "low"`**:
|
|
560
|
-
- If `CONFIDENCE_OVERRIDE_REASON` is non-empty: print `[design-confidence] Override accepted for <name>. Reason: <CONFIDENCE_OVERRIDE_REASON>. Proceeding.` and continue.
|
|
561
|
-
- Otherwise **HALT this feature before any implementation cost is paid.** Print:
|
|
562
|
-
|
|
563
|
-
```
|
|
564
|
-
## Design Confidence Gate: BLOCKED — <name>
|
|
565
|
-
|
|
566
|
-
The architect could not form a confident design.
|
|
567
|
-
|
|
568
|
-
**Reason:** <reason>
|
|
569
|
-
|
|
570
|
-
**Blocking question:** <blocking_question>
|
|
571
|
-
|
|
572
|
-
### Next Steps
|
|
573
|
-
1. Answer the question above (edit the ticket description or refine the spec), then re-run `/specrails:implement`.
|
|
574
|
-
2. Or, to accept the risk and implement the architect's best interpretation:
|
|
575
|
-
`/specrails:implement #N --confidence-override "reason"`
|
|
576
|
-
|
|
577
|
-
No implementation, git, or backlog operations were performed for this feature.
|
|
578
|
-
```
|
|
579
|
-
|
|
580
|
-
- **Pipeline state:** `developer` → `skipped`, `failed_phase` = `architect`, `error_context` = `"design confidence low: <blocking_question>"`.
|
|
581
|
-
- In multi-feature mode, a `low` on one feature halts only that feature — the others continue normally. Halted features appear in the Phase 4e report with Status `BLOCKED (confidence)`.
|
|
582
|
-
|
|
583
|
-
The OpenSpec artifacts the architect produced are intentionally left in place — the answered question turns them into a resumable starting point, not waste.
|
|
584
|
-
|
|
585
|
-
## Phase 3b: Implement
|
|
586
|
-
|
|
587
|
-
### Pre-flight: Verify Bash permission
|
|
588
|
-
|
|
589
|
-
Before launching any developer agent, run a trivial Bash command to confirm Bash is allowed.
|
|
590
|
-
|
|
591
|
-
### Launch developers
|
|
592
|
-
|
|
593
|
-
**Read reviewer learnings:** Check `.claude/agent-memory/sr-reviewer/common-fixes.md` and include in developer prompts.
|
|
594
|
-
|
|
595
|
-
**Test-execution economy:** every developer prompt MUST include this reminder: *"Per-task test runs are SCOPED to the test file(s) the task touches. The full suite runs exactly once, at your validation gate. The reviewer owns the pipeline's authoritative full CI run."*
|
|
596
|
-
|
|
597
|
-
#### Dry-Run: Redirect developer writes
|
|
598
|
-
|
|
599
|
-
**If `DRY_RUN=true`**, include the following in every developer agent prompt:
|
|
600
|
-
|
|
601
|
-
> IMPORTANT: This is a dry-run. Write all new or modified files under:
|
|
602
|
-
> .claude/.dry-run/\<feature-name\>/
|
|
603
|
-
>
|
|
604
|
-
> Mirror the real destination path within this directory. For example:
|
|
605
|
-
> Real path: src/utils/parser.ts
|
|
606
|
-
> Write to: .claude/.dry-run/\<feature-name\>/src/utils/parser.ts
|
|
607
|
-
>
|
|
608
|
-
> Do NOT write to real file paths. After writing each file, append an entry
|
|
609
|
-
> to .claude/.dry-run/\<feature-name\>/.cache-manifest.json using this JSON format:
|
|
610
|
-
> {"cached_path": "...", "real_path": "...", "operation": "create|modify"}
|
|
611
|
-
|
|
612
|
-
**If `DRY_RUN=false`**: developer agent instructions are unchanged.
|
|
613
|
-
|
|
614
|
-
#### Choosing the right developer agent
|
|
615
|
-
|
|
616
|
-
For each feature, read `${SPECRAILS_REPO_DIR:-.}/openspec/changes/<name>/tasks.md` and classify every task by its layer tags and file references.
|
|
617
|
-
|
|
618
|
-
**Step 1 — Classify tasks into layers:**
|
|
619
|
-
|
|
620
|
-
For each task, determine its layer from:
|
|
621
|
-
1. **Explicit layer tags** in tasks.md (e.g., `[frontend]`, `[backend]`, `[core]`, `[infra]`, `[docs]`, etc.)
|
|
622
|
-
2. **File references** under `**Files:**` entries — apply the same extension/path rules used in Phase 4b:
|
|
623
|
-
- Frontend: `.jsx`, `.tsx`, `.vue`, `.svelte`, `.css`, `.scss`, `.html`, or paths under `components/`, `pages/`, `views/`, `ui/`, `client/`, `frontend/`, `app/`, `public/`, `static/`, `assets/`
|
|
624
|
-
- Backend: `.py`, `.go`, `.java`, `.rb`, `.php`, `.rs`, `.cs`, `.sql`, or paths under `server/`, `api/`, `routes/`, `controllers/`, `services/`, `models/`, `db/`, `backend/`, `migrations/`
|
|
625
|
-
- Mixed/other: everything else (shell scripts, config files, markdown, YAML, etc.)
|
|
626
|
-
|
|
627
|
-
Produce three sets: `FRONTEND_TASKS`, `BACKEND_TASKS`, `OTHER_TASKS`.
|
|
628
|
-
|
|
629
|
-
**Step 2 — Route tasks to developer agents:**
|
|
630
|
-
|
|
631
|
-
Routing has a single path. When **no profile** is active, every task goes to `sr-developer` — `DEVELOPER_ROUTING = { sr-developer: <all tasks> }`. When a **profile** is active, apply its `ROUTING` rules (below), which may direct some tasks to profile-declared `custom-*` agents.
|
|
632
|
-
|
|
633
|
-
##### Profile routing
|
|
634
|
-
|
|
635
|
-
Apply `ROUTING` rules in their array order. For each task, collect its tag set (the layer tags from Step 1 plus any explicit `[tag]` markers in tasks.md). The first rule whose `tags` array intersects the task's tag set wins. The terminal `default: true` rule catches tasks matched by no earlier rule.
|
|
636
|
-
|
|
637
|
-
Example (pseudocode):
|
|
638
|
-
|
|
639
|
-
```bash
|
|
640
|
-
assigned_agent_for_task() {
|
|
641
|
-
local -a task_tags=("$@")
|
|
642
|
-
local rule_count
|
|
643
|
-
rule_count=$(jq 'length' <<<"$ROUTING")
|
|
644
|
-
local i=0
|
|
645
|
-
while [[ $i -lt $rule_count ]]; do
|
|
646
|
-
local is_default rule_tags agent
|
|
647
|
-
is_default=$(jq -r ".[$i].default // false" <<<"$ROUTING")
|
|
648
|
-
agent=$(jq -r ".[$i].agent" <<<"$ROUTING")
|
|
649
|
-
if [[ "$is_default" == "true" ]]; then
|
|
650
|
-
echo "$agent"
|
|
651
|
-
return
|
|
652
|
-
fi
|
|
653
|
-
rule_tags=$(jq -r ".[$i].tags[]" <<<"$ROUTING")
|
|
654
|
-
for rtag in $rule_tags; do
|
|
655
|
-
for ttag in "${task_tags[@]}"; do
|
|
656
|
-
if [[ "$rtag" == "$ttag" ]]; then
|
|
657
|
-
echo "$agent"
|
|
658
|
-
return
|
|
659
|
-
fi
|
|
660
|
-
done
|
|
661
|
-
done
|
|
662
|
-
i=$((i + 1))
|
|
663
|
-
done
|
|
664
|
-
}
|
|
665
|
-
```
|
|
666
|
-
|
|
667
|
-
Produce `DEVELOPER_ROUTING` from the per-task decisions, grouping by assigned agent. If a rule routes a task to an agent that is **not** in `AVAILABLE_AGENTS` (e.g. a `custom-*` agent that was warned-and-skipped in Phase -1 because its file is missing), that routing target is dropped and the task falls through to the terminal `default: true` rule (which resolves to a baseline agent). Do not STOP for a missing non-baseline target — graceful degradation is intentional.
|
|
668
|
-
|
|
669
|
-
##### Routing trace
|
|
670
|
-
|
|
671
|
-
After computing `DEVELOPER_ROUTING`, optionally emit a trace line to aid debugging:
|
|
672
|
-
|
|
673
|
-
```
|
|
674
|
-
[phase-3b] routing decision: profile=${PROFILE_NAME:-none} agents=[list]
|
|
675
|
-
```
|
|
676
|
-
|
|
677
|
-
**Step 3 — Print routing decision:**
|
|
678
|
-
|
|
182
|
+
{"kind":"full","commands":[{"repositoryId":"primary","command":"npm","args":["test"],"cwd":"/absolute/selected/repo","env":{"CI":"1"},"timeoutMs":900000}]}
|
|
679
183
|
```
|
|
680
|
-
## Developer Routing
|
|
681
|
-
|
|
682
|
-
| Agent | Tasks | Reason |
|
|
683
|
-
|-------|-------|--------|
|
|
684
|
-
| sr-developer | Task 1, Task 2 | Default (no profile / default rule) |
|
|
685
|
-
| custom-api-dev | Task 3 | Profile routed [backend] tasks here |
|
|
686
|
-
```
|
|
687
|
-
|
|
688
|
-
Also store `DEVELOPER_AGENTS_USED` (the set of developer agent IDs actually launched) — reported in the Phase 4e summary.
|
|
689
|
-
|
|
690
|
-
#### Launch modes
|
|
691
|
-
|
|
692
|
-
For each entry in `DEVELOPER_ROUTING`, launch the assigned developer agent using its `subagent_type` (`sr-developer` or a profile-declared `custom-*` developer) with its task subset.
|
|
693
|
-
|
|
694
|
-
**If `SINGLE_MODE` and only one agent in routing**: Launch in the main repo, foreground.
|
|
695
|
-
**If `SINGLE_MODE` but multiple agents in routing**: Launch agents sequentially in the main repo (one at a time, foreground), passing only their assigned tasks.
|
|
696
|
-
**If multiple features**: Launch in isolated worktrees (`isolation: worktree`, `run_in_background: true`).
|
|
697
|
-
|
|
698
|
-
Wait for all developers to complete.
|
|
699
|
-
|
|
700
|
-
**Summary timing (multi-feature mode):** When running multiple background developer agents, individual `task_notification` completions MUST NOT trigger a final Phase 3b summary. As each agent completes, emit only a brief one-line acknowledgment:
|
|
701
|
-
```
|
|
702
|
-
[phase-3b] Developer for <feature> ✓ (<N> tool uses, <duration>)
|
|
703
|
-
```
|
|
704
|
-
Only after the LAST background agent sends its completion notification, emit the consolidated summary:
|
|
705
|
-
```
|
|
706
|
-
## Phase 3b Complete
|
|
707
|
-
|
|
708
|
-
| Feature | Agent | Tool uses | Duration |
|
|
709
|
-
|---------|-------|-----------|----------|
|
|
710
|
-
| <feature-a> | sr-developer | 64 | 8m 02s |
|
|
711
|
-
| <feature-b> | sr-developer | 50 | 7m 35s |
|
|
712
|
-
|
|
713
|
-
All N developers complete. Proceeding to Phase 4.
|
|
714
|
-
```
|
|
715
|
-
|
|
716
|
-
This prevents stale "still waiting" text from appearing as the terminal result when the job completes.
|
|
717
|
-
|
|
718
|
-
**Pipeline state:** update `developer` → `done`. Also update `implemented_files` in the state file with the complete list of files created or modified by the developer agent(s). If developer failed: update `developer` → `failed` with error context `"<agent-id> failed: <exit code or error description>"`.
|
|
719
|
-
|
|
720
|
-
> **Note (v5):** dedicated test-writing (`sr-test-writer`) and doc-sync (`sr-doc-sync`) phases were removed. Tests and documentation are part of each OpenSpec task and are produced by `sr-developer`; the reviewer's TDD and spec-completeness checklist enforces them. A profile may reintroduce equivalent stages via `custom-*` agents with routing.
|
|
721
|
-
|
|
722
|
-
## Phase 4: Merge & Review
|
|
723
|
-
|
|
724
|
-
**This phase is fully autonomous.**
|
|
725
|
-
|
|
726
|
-
### 4a. Merge worktree changes to main repo
|
|
727
|
-
|
|
728
|
-
- If `SINGLE_MODE`: skip (no worktrees were used). Proceed to Phase 4b.
|
|
729
|
-
- If `DRY_RUN=true`: apply the merge algorithm below, writing all outputs to `CACHE_DIR/<file-path>` instead of the main repo working tree. Do NOT clean up worktrees in dry-run mode.
|
|
730
|
-
- Otherwise: apply the merge algorithm below, writing outputs to the main repo working tree. Clean up worktrees at the end.
|
|
731
|
-
|
|
732
|
-
#### Merge Algorithm
|
|
733
|
-
|
|
734
|
-
The merge **target** (the main repo working tree where merged files land) is `<target>` = **`${SPECRAILS_REPO_DIR:-.}`**. Every `<target>/<file>` below therefore resolves to `${SPECRAILS_REPO_DIR:-.}/<file>` so merged code lands in the real repo, not the working directory. (`<worktree-path>` is an absolute git-worktree path supplied by the runtime; `git -C <worktree-path>` already targets it directly.)
|
|
735
|
-
|
|
736
|
-
Process features in `MERGE_ORDER` sequence. For each feature:
|
|
737
|
-
|
|
738
|
-
**Step 1: Identify changed files**
|
|
739
184
|
|
|
740
185
|
```bash
|
|
741
|
-
|
|
186
|
+
node "${SPECRAILS_PIPELINE_RUNTIME:-.specrails/runtime/pipeline.mjs}" verify --request <stateDir/full-checks.json>
|
|
742
187
|
```
|
|
743
188
|
|
|
744
|
-
|
|
189
|
+
Derive actual CI-equivalent checks from each selected repository's scripts/config/workflows. The example is not universal. Full requests cover every selected root and required cross-repository integration; scoped requests target repair cycles. The runtime executes commands and binds exits/output, command/argv/cwd/environment identity to frozen scope and actual candidate, including additions and deletions.
|
|
745
190
|
|
|
746
|
-
**
|
|
191
|
+
Reuse a full receipt only while status.verification.valid is true **and recorded commands cover the required checks**. Baseline-only/stale evidence cannot certify changes. After candidate edits, use scoped repairs and one fresh final full pass. Command success still needs semantic acceptance review; do not replace it with another full-suite run.
|
|
747
192
|
|
|
748
|
-
|
|
749
|
-
```bash
|
|
750
|
-
cp <worktree-path>/<file> "${SPECRAILS_REPO_DIR:-.}"/<file>
|
|
751
|
-
```
|
|
752
|
-
Log: `Copied (exclusive): <file>`
|
|
753
|
-
|
|
754
|
-
**Step 3: Merge shared files**
|
|
755
|
-
|
|
756
|
-
For each shared file, choose strategy by file type:
|
|
757
|
-
|
|
758
|
-
**Strategy A — Markdown section-aware merge** (`.md` files):
|
|
759
|
-
1. Read base: current content of `${SPECRAILS_REPO_DIR:-.}/<file>` (the merge target).
|
|
760
|
-
2. Read incoming: `<worktree-path>/<file>`.
|
|
761
|
-
3. Parse both into sections using `##` heading boundaries (heading line + all content until next `##` or EOF).
|
|
762
|
-
4. Build section maps: `{heading_text: content}` for base and incoming.
|
|
763
|
-
5. Merge:
|
|
764
|
-
- Section in base only: keep.
|
|
765
|
-
- Section in incoming only: append to merged output.
|
|
766
|
-
- Section in both, content identical: keep base.
|
|
767
|
-
- Section in both, content differs: insert conflict markers:
|
|
768
|
-
```
|
|
769
|
-
<<<<<<< <feature-name>
|
|
770
|
-
<incoming section content>
|
|
771
|
-
=======
|
|
772
|
-
<base section content>
|
|
773
|
-
>>>>>>> base
|
|
774
|
-
```
|
|
775
|
-
Log: `CONFLICT: <file> — section "<heading>" requires manual resolution.`
|
|
776
|
-
6. Write merged result to `${SPECRAILS_REPO_DIR:-.}/<file>` (the merge target).
|
|
777
|
-
|
|
778
|
-
**Strategy B — Unified diff sequential apply** (all other file types):
|
|
779
|
-
1. Generate incoming diff against original `main`:
|
|
780
|
-
```bash
|
|
781
|
-
git -C <worktree-path> diff main -- <file>
|
|
782
|
-
```
|
|
783
|
-
2. Apply to current target:
|
|
784
|
-
```bash
|
|
785
|
-
patch --forward --fuzz=3 "${SPECRAILS_REPO_DIR:-.}"/<file> < <diff>
|
|
786
|
-
```
|
|
787
|
-
3. If `patch` succeeds: log `Merged (diff-apply): <file>`.
|
|
788
|
-
4. If `patch` fails: insert conflict markers around rejected hunks. Log: `CONFLICT: <file> — N hunks rejected.`
|
|
789
|
-
|
|
790
|
-
If `patch` is not available (detected in Phase -1): use Strategy A for all file types and print: `[warn] patch not available — using section-aware fallback for all shared files.`
|
|
791
|
-
|
|
792
|
-
**Step 4: Record outcomes**
|
|
793
|
-
|
|
794
|
-
Maintain `MERGE_REPORT`:
|
|
795
|
-
- `cleanly_merged`: exclusive files + shared files with no conflicts
|
|
796
|
-
- `auto_resolved`: shared files merged without conflict markers
|
|
797
|
-
- `requires_resolution`: `{file, feature, regions}` for files with conflict markers
|
|
798
|
-
|
|
799
|
-
**Step 5: Emit initial merge report**
|
|
800
|
-
|
|
801
|
-
After all features are processed, print the preliminary report:
|
|
802
|
-
|
|
803
|
-
```
|
|
804
|
-
## Phase 4a Merge Report (preliminary)
|
|
805
|
-
|
|
806
|
-
### Cleanly Merged
|
|
807
|
-
- <file> (exclusive to <feature>)
|
|
193
|
+
## Phase 1: Architect
|
|
808
194
|
|
|
809
|
-
|
|
810
|
-
- <file> (features: <a>, <b> — distinct sections)
|
|
195
|
+
Start architect if required by resumePhase, then invoke sr-architect once with specName and aggregate frozen scope/roots. Use the actual `Skill("opsx:ff", "<change>")` workflow (or provider-native equivalent) for `${SPECRAILS_REPO_DIR:-.}/openspec/changes/<change>/`.
|
|
811
196
|
|
|
812
|
-
|
|
813
|
-
- <file> (features: <a>, <b> — conflicting section: "<heading>")
|
|
814
|
-
```
|
|
197
|
+
One design covers shared contracts, explicit repository task ownership, integration order and acceptance. Batch tickets are dependency-ordered task groups within this change, not separate full pipelines. Validate existing Modify paths and genuinely new Create paths against selected roots; do not reject a plan by a percentage-of-paths-exist heuristic.
|
|
815
198
|
|
|
816
|
-
|
|
199
|
+
Require proposal/design/specs/tasks and medium/high design-confidence.json. Missing, malformed or low confidence blocks development with the unresolved issue. The runtime checks this before architect done. Reuse valid design after interruption; task checkbox progress does not invalidate its contract.
|
|
817
200
|
|
|
818
|
-
|
|
201
|
+
## Phase 2: Developer
|
|
819
202
|
|
|
820
|
-
-
|
|
821
|
-
- **Halt the affected features** — do not proceed to Phase 4c (git/PR) for any feature whose files still carry conflict markers. Independent features with no unresolved conflicts continue normally.
|
|
822
|
-
- Print: `[merge] N file(s) have unresolved conflict markers — halting the affected feature(s). Resolve manually, then re-run.`
|
|
203
|
+
Invoke sr-developer or profile-routed roles with immutable task groups and official `Skill("opsx:apply", "<change>")`. Serialize dependent/shared-file groups in supplied candidate roots. Read-only investigations may run concurrently, but collect every result; no recursive full pipeline or colliding writers.
|
|
823
204
|
|
|
824
|
-
|
|
205
|
+
Keep real checked task progress. Use meaningful scoped regressions during implementation and bounded repairs (at most two unsuccessful cycles per issue, then a concrete blocker). After all aggregate tasks finish, run one full CI-equivalent request. Runtime developer done requires completed tasks and current actual evidence.
|
|
825
206
|
|
|
826
|
-
|
|
207
|
+
Supplied roots already contain the candidate: inspect additions, renames and deletions there. Do not copy changed-file lists over another checkout, patch against an assumed main branch or force-remove worktrees.
|
|
827
208
|
|
|
828
|
-
|
|
829
|
-
## Phase 4a Merge Report
|
|
209
|
+
## Phase 3: Reviewer and confidence
|
|
830
210
|
|
|
831
|
-
|
|
832
|
-
- <file> (exclusive to <feature>)
|
|
211
|
+
Invoke sr-reviewer once with the context, exact specName, candidate summary and receipt. Map every frozen requirement to code and behavioral evidence. Existing green tests with missing implementation mean incomplete, not PASS.
|
|
833
212
|
|
|
834
|
-
|
|
835
|
-
- <file> (features: <a>, <b> — distinct sections)
|
|
836
|
-
- <file> (smart-resolver: additive-concat, confidence 92)
|
|
213
|
+
Reuse unchanged full evidence; run cheap static/scoped checks as needed. Any review edit requires one fresh full request after all fixes. Security, acceptance gaps, missing required regressions, untested critical side effects or unchecked tasks block acceptance.
|
|
837
214
|
|
|
838
|
-
|
|
839
|
-
- <file> (features: <a>, <b> — low-confidence: see merge-resolution-report.md)
|
|
840
|
-
Search for `<<<<<<< <feature-name>` to locate conflict markers.
|
|
215
|
+
Before archive, write `${SPECRAILS_REPO_DIR:-.}/openspec/changes/<change>/confidence-score.json` with canonical change, overall and five numeric aspects. Require overall ≥70, security ≥75, other aspects ≥60; stricter configured thresholds still apply. Missing/malformed score fails closed; a numeric average never overrules an explicit blocker.
|
|
841
216
|
|
|
842
|
-
|
|
843
|
-
Resolution report: openspec/changes/<feature>/merge-resolution-report.md
|
|
844
|
-
```
|
|
217
|
+
Normal reviewer returns acceptance/security verdict and confidence **without archiving**. Validate SECURITY_STATUS, task completion and acceptance, then runtime reviewer done. A human-facing PASS alone is insufficient.
|
|
845
218
|
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
**Step 6: Clean up worktrees** (skip if `DRY_RUN=true`)
|
|
219
|
+
## Phase 4: Archive after approval
|
|
849
220
|
|
|
850
221
|
```bash
|
|
851
|
-
|
|
222
|
+
node "${SPECRAILS_PIPELINE_RUNTIME:-.specrails/runtime/pipeline.mjs}" archive-check
|
|
852
223
|
```
|
|
853
224
|
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
### 4b. Review
|
|
225
|
+
Approval binds candidate, requirements and exact confidence bytes. Invoke reviewer with ARCHIVE_ONLY=true and ARCHIVE_AUTHORIZED=true, or execute the equivalent official workflow in the coordinator. No rescoring/code edits after approval. Run the Skill from `${SPECRAILS_REPO_DIR:-.}`:
|
|
857
226
|
|
|
858
|
-
There is a single reviewer, `sr-reviewer`. It owns every review dimension — correctness, TDD/spec completeness, code quality, **security**, and **performance** — scaled to what the change actually touches (its checklist covers all of them). There are no separate layer-reviewer passes.
|
|
859
|
-
|
|
860
|
-
Construct the reviewer's invocation prompt with:
|
|
861
|
-
- `MODIFIED_FILES_LIST`: the complete list of all files created or modified during this run
|
|
862
|
-
- `PIPELINE_CONTEXT`: a brief description of what was implemented
|
|
863
|
-
- `MERGE_REPORT`: any files still in `requires_resolution` (multi-feature runs)
|
|
864
|
-
- The security-exemptions config path: `.claude/security-exemptions.yaml` (if present)
|
|
865
|
-
- Full CI commands (reference material for its scoped-first verification policy — the reviewer runs diff-scoped tests plus fast static checks, escalating to one full pass only when its own fixes warrant it; the developer's Phase 4 full pass is the baseline)
|
|
866
|
-
- Cross-feature merge issue checks
|
|
867
|
-
- Instruction to record learnings to `common-fixes.md`
|
|
868
|
-
- Instruction to archive completed changes via OpenSpec
|
|
869
|
-
|
|
870
|
-
The reviewer emits `SECURITY_STATUS: BLOCKED | WARNINGS | CLEAN`. Set `SECURITY_BLOCKED=true` if `BLOCKED`, otherwise `false`.
|
|
871
|
-
|
|
872
|
-
**The security gate (blocking ship on `SECURITY_STATUS: BLOCKED`) is enforced in Phase 4c.** Do not apply it here.
|
|
873
|
-
|
|
874
|
-
Launch the **sr-reviewer** agent (`subagent_type: sr-reviewer`, foreground, `run_in_background: false`). Wait for it to complete.
|
|
875
|
-
|
|
876
|
-
**Pipeline state:** update `reviewer` → `done` (or `failed` with error context `"sr-reviewer timed out or did not complete"` if the agent errored out).
|
|
877
|
-
|
|
878
|
-
**If `DRY_RUN=true`**, add the following to the reviewer agent prompt:
|
|
879
|
-
|
|
880
|
-
> Note: This is a dry-run review. Developer files are under .claude/.dry-run/\<feature-name\>/.
|
|
881
|
-
> Read modified files from there. Write any reviewer fixes back to CACHE_DIR (not real paths).
|
|
882
|
-
> CI commands may be run — they read the real repo, but be aware developer changes are not
|
|
883
|
-
> yet applied to real paths.
|
|
884
|
-
|
|
885
|
-
### 4b-conf. Confidence Gate
|
|
886
|
-
|
|
887
|
-
After the generalist reviewer agent completes, evaluate the confidence score before proceeding to Phase 4c.
|
|
888
|
-
|
|
889
|
-
**In multi-feature mode (worktrees):** run this gate once per feature immediately after that feature's reviewer completes. Each feature is evaluated independently — a block on one feature does not prevent another feature's gate from running.
|
|
890
|
-
|
|
891
|
-
#### Step 1 — Read score file
|
|
892
|
-
|
|
893
|
-
Path: `${SPECRAILS_REPO_DIR:-.}/openspec/changes/<name>/confidence-score.json`
|
|
894
|
-
|
|
895
|
-
- If the file does not exist:
|
|
896
|
-
- Set `CONFIDENCE_STATUS=MISSING`
|
|
897
|
-
- Print: `[confidence] Warning: confidence-score.json not found. Proceeding without gate.`
|
|
898
|
-
- Continue to Phase 4c.
|
|
899
|
-
|
|
900
|
-
#### Step 2 — Read config
|
|
901
|
-
|
|
902
|
-
Path: `.claude/confidence-config.json`
|
|
903
|
-
|
|
904
|
-
- If the file does not exist:
|
|
905
|
-
- Use built-in defaults (overall: 70; type_correctness: 60; pattern_adherence: 60; test_coverage: 60; security: 75; architectural_alignment: 60).
|
|
906
|
-
- Print:
|
|
907
|
-
```
|
|
908
|
-
[confidence] No confidence-config.json found. Using built-in defaults.
|
|
909
|
-
[confidence] To customize thresholds, create .claude/confidence-config.json.
|
|
910
|
-
```
|
|
911
|
-
- If `enabled: false` in the config:
|
|
912
|
-
- Print: `[confidence] Gate disabled. Skipping.`
|
|
913
|
-
- Set `CONFIDENCE_STATUS=DISABLED`
|
|
914
|
-
- Continue to Phase 4c.
|
|
915
|
-
|
|
916
|
-
#### Step 3 — Compare scores
|
|
917
|
-
|
|
918
|
-
- Check `overall` against `thresholds.overall`.
|
|
919
|
-
- Check each of the five aspects against `thresholds.aspects.<aspect>`.
|
|
920
|
-
- Collect all breaches as a list: `{aspect, actual_score, threshold, delta}`.
|
|
921
|
-
|
|
922
|
-
#### Step 4 — Apply on_breach
|
|
923
|
-
|
|
924
|
-
**If no breaches:**
|
|
925
|
-
- Print: `[confidence] All scores meet thresholds. Proceeding.`
|
|
926
|
-
- Set `CONFIDENCE_STATUS=PASS`
|
|
927
|
-
- Continue to Phase 4c.
|
|
928
|
-
|
|
929
|
-
**If breaches exist and `on_breach: "block"`:**
|
|
930
|
-
|
|
931
|
-
1. Check for `--confidence-override`:
|
|
932
|
-
- If `CONFIDENCE_OVERRIDE_REASON` is non-empty and `override_allowed: true` in the config:
|
|
933
|
-
- Print: `[confidence] Override accepted. Reason: <CONFIDENCE_OVERRIDE_REASON>. Proceeding with gate bypassed.`
|
|
934
|
-
- Set `CONFIDENCE_STATUS=OVERRIDE`
|
|
935
|
-
- Continue to Phase 4c.
|
|
936
|
-
- If `CONFIDENCE_OVERRIDE_REASON` is non-empty but `override_allowed: false` in the config:
|
|
937
|
-
- Print: `[confidence] Override is disabled in confidence-config.json.`
|
|
938
|
-
- (Fall through to block below.)
|
|
939
|
-
- If `CONFIDENCE_OVERRIDE_REASON` is empty or override was rejected:
|
|
940
|
-
- Print the Breach Report (see format below).
|
|
941
|
-
- Set `CONFIDENCE_BLOCKED=true`
|
|
942
|
-
- Set `CONFIDENCE_STATUS=BLOCKED`
|
|
943
|
-
- **Halt: do not proceed to Phase 4c.**
|
|
944
|
-
|
|
945
|
-
**If breaches exist and `on_breach: "warn"`:**
|
|
946
|
-
- Print the Breach Report.
|
|
947
|
-
- Set `CONFIDENCE_STATUS=WARN`
|
|
948
|
-
- Continue to Phase 4c.
|
|
949
|
-
|
|
950
|
-
#### Breach Report Format
|
|
951
|
-
|
|
952
|
-
```
|
|
953
|
-
## Confidence Gate: BLOCKED
|
|
954
|
-
|
|
955
|
-
The reviewer's confidence scores do not meet configured thresholds.
|
|
956
|
-
|
|
957
|
-
| Aspect | Score | Threshold | Delta |
|
|
958
|
-
|--------|-------|-----------|-------|
|
|
959
|
-
| <aspect> | <actual> | <threshold> | <delta (negative)> |
|
|
960
|
-
|
|
961
|
-
### Reviewer Notes on Low-Scoring Aspects
|
|
962
|
-
|
|
963
|
-
**<aspect> (<score>):** <note from confidence-score.json>
|
|
964
|
-
|
|
965
|
-
### Flags
|
|
966
|
-
|
|
967
|
-
- <flag-1>
|
|
968
|
-
- <flag-2>
|
|
969
|
-
(omit this section if flags array is empty)
|
|
970
|
-
|
|
971
|
-
### Next Steps
|
|
972
|
-
|
|
973
|
-
1. Address the concerns above and re-run `/specrails:implement`.
|
|
974
|
-
2. Or, if you have reviewed the concerns and accept the risk, re-run with an override:
|
|
975
|
-
`/specrails:implement #N --confidence-override "reason"`
|
|
976
|
-
|
|
977
|
-
Pipeline halted. No git operations have been performed.
|
|
978
227
|
```
|
|
979
|
-
|
|
980
|
-
#### Dry-Run Behavior
|
|
981
|
-
|
|
982
|
-
When `DRY_RUN=true`, the reviewer still writes `confidence-score.json` (it is an OpenSpec artifact, not a git artifact). Phase 4b-conf still evaluates the score. If `CONFIDENCE_BLOCKED=true`, add to `.cache-manifest.json` under `skipped_operations`:
|
|
983
|
-
```
|
|
984
|
-
"confidence-gate: blocked — Phase 4c skipped"
|
|
228
|
+
Skill("opsx:archive", "<change>")
|
|
985
229
|
```
|
|
986
230
|
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
**Guard:** If `SNAPSHOTS_CAPTURED=false` OR `DRY_RUN=true`, print `[conflict-check] Skipped — SNAPSHOTS_CAPTURED=false (or dry-run mode).` and proceed directly to Phase 4c.
|
|
990
|
-
|
|
991
|
-
This check is independent of Phase 3a.0. Even if the user chose to continue through a conflict at Phase 3a.0, this gate re-checks all in-scope issues against the Phase 0 snapshot. It is the final gate before any code reaches git.
|
|
231
|
+
Confirm active artifacts moved and canonical specs synced, then record archive done. Do not emulate with file moves or automatically accept incomplete-task prompts. Failure is a resumable archive blocker, not permission to redevelop or reship.
|
|
992
232
|
|
|
993
|
-
|
|
233
|
+
## Phase 5: Delivery and backlog
|
|
994
234
|
|
|
995
|
-
|
|
235
|
+
Host-owned git: only ship/ci may be skipped; return ready-for-delivery evidence. Host-owned backlog stays untouched until host acceptance.
|
|
996
236
|
|
|
997
|
-
|
|
237
|
+
For explicitly Core-owned git and GIT_AUTO=true, use the project's shipping workflow in each correct selected repo, staging only reviewed candidate changes. Preserve unrelated preexisting work. Record real commits/PRs and CI results per repository; partial delivery is not whole-batch success. CI-only retry checks existing delivery and does not ship again.
|
|
998
238
|
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
If all issues are clean: print `[conflict-check] All issues clean (Phase 4c.0). Proceeding.` and continue.
|
|
1002
|
-
|
|
1003
|
-
If conflicts exist: print the same conflict report format as Phase 3a.0 (with `Phase 4c.0` context) and await `A`/`C` input (same re-prompt and default-abort logic).
|
|
1004
|
-
|
|
1005
|
-
**On abort:** Print `[conflict-abort] Pipeline aborted. Re-run /specrails:implement after resolving the issues.` and exit. No git operations have been performed at this point.
|
|
1006
|
-
|
|
1007
|
-
**On continue:** Print `[conflict-override] Continuing. N conflict(s) logged.` Append each conflict to `CONFLICT_OVERRIDES` as `{phase: "4c.0", issue: "#N", field: "<field>", severity: "<severity>", was: "<was>", now: "<now>"}`. Proceed to Phase 4c.
|
|
1008
|
-
|
|
1009
|
-
### 4c. Ship — Git & backlog updates
|
|
1010
|
-
|
|
1011
|
-
**Security gate:** If `SECURITY_BLOCKED=true`:
|
|
1012
|
-
1. Print all Critical findings from the security-reviewer output
|
|
1013
|
-
2. Do NOT create a branch, commit, push, or PR
|
|
1014
|
-
3. Print: "Pipeline blocked by security findings. Fix the Critical issues listed above and re-run /specrails:implement."
|
|
1015
|
-
4. Skip to Phase 4e.
|
|
1016
|
-
|
|
1017
|
-
### Dry-Run Gate
|
|
1018
|
-
|
|
1019
|
-
**If `DRY_RUN=true`:**
|
|
1020
|
-
Print: `[dry-run] Skipping all git and backlog operations.`
|
|
1021
|
-
Record skipped operations to `.cache-manifest.json` under `skipped_operations`:
|
|
1022
|
-
- `"git: branch creation (feat/<name>)"`
|
|
1023
|
-
- `"git: commit"`
|
|
1024
|
-
- `"git: push"`
|
|
1025
|
-
- `"github: pr creation"` (if `GH_AVAILABLE=true`)
|
|
1026
|
-
- If `BACKLOG_PROVIDER=local` and `BACKLOG_WRITE=true`:
|
|
1027
|
-
- `"local: ticket comment #{id}"` for each ticket in scope
|
|
1028
|
-
- `"local: ticket status update #{id}"` for each fully resolved ticket
|
|
1029
|
-
|
|
1030
|
-
Then skip the rest of Phase 4c and proceed directly to Phase 4e.
|
|
1031
|
-
|
|
1032
|
-
**If `APPLY_MODE=true`:**
|
|
1033
|
-
1. Read `.cache-manifest.json` from `CACHE_DIR`.
|
|
1034
|
-
2. For each entry in `files`: copy `cached_path` to `real_path`, creating directories as needed.
|
|
1035
|
-
3. Print: `[apply] Copied N files from .claude/.dry-run/<feature-name>/ to real locations.`
|
|
1036
|
-
4. Then proceed with Phase 4c normally (GIT_AUTO logic, backlog updates) using the real files.
|
|
1037
|
-
5. On successful completion of Phase 4c: delete `CACHE_DIR` and print `[apply] Cache cleaned up.`
|
|
1038
|
-
If Phase 4c fails: preserve `CACHE_DIR` for re-run.
|
|
1039
|
-
|
|
1040
|
-
**Otherwise:** proceed as normal.
|
|
1041
|
-
|
|
1042
|
-
---
|
|
1043
|
-
|
|
1044
|
-
This phase respects the `GIT_AUTO` and `BACKLOG_WRITE` settings from configuration.
|
|
1045
|
-
|
|
1046
|
-
**Environment override (host owns version control).** Before applying the `GIT_AUTO` logic below, check the `SPECRAILS_GIT_AUTO` environment variable. If it is set to `false` or `0`, treat `GIT_AUTO` as `false` for the rest of this phase (and Phase 4d) **regardless of configuration** — do not create a branch, commit, push, or open a PR; follow the `GIT_AUTO=false` (manual shipping) path instead. A host such as [specrails-desktop](https://github.com/fjpulidop/specrails-desktop) sets this when it owns version control (it runs the pipeline in an isolated worktree and opens the pull request itself), so honouring it prevents a second, uncoordinated PR. When `SPECRAILS_GIT_AUTO` is unset or any other value, resolve `GIT_AUTO` from configuration as normal.
|
|
1047
|
-
|
|
1048
|
-
#### If `GIT_AUTO=true` (automatic shipping)
|
|
1049
|
-
|
|
1050
|
-
All git operations run against the repo via `git -C "${SPECRAILS_REPO_DIR:-.}"`, and `gh` runs from inside the repo so it can detect the remote.
|
|
1051
|
-
|
|
1052
|
-
1. Create branch from `main`: `git -C "${SPECRAILS_REPO_DIR:-.}" checkout -b feat/<descriptive-name>`
|
|
1053
|
-
2. One commit per feature with descriptive messages (`git -C "${SPECRAILS_REPO_DIR:-.}" add … && git -C "${SPECRAILS_REPO_DIR:-.}" commit -m …`)
|
|
1054
|
-
3. If the reviewer modified files, create an additional commit: `git -C "${SPECRAILS_REPO_DIR:-.}" commit -m "fix: resolve CI issues (reviewer)"`
|
|
1055
|
-
4. Push with `-u` flag: `git -C "${SPECRAILS_REPO_DIR:-.}" push -u origin <branch-name>`
|
|
1056
|
-
5. Create PR (if GitHub CLI is available), running it from the repo:
|
|
1057
|
-
```bash
|
|
1058
|
-
(cd "${SPECRAILS_REPO_DIR:-.}" && {{PR_CREATE_CMD}})
|
|
1059
|
-
```
|
|
1060
|
-
If `gh` is not authenticated, print a compare URL for manual PR creation.
|
|
1061
|
-
|
|
1062
|
-
#### If `GIT_AUTO=false` (manual shipping)
|
|
1063
|
-
|
|
1064
|
-
Do NOT create branches, commits, or push. Instead display a summary:
|
|
1065
|
-
|
|
1066
|
-
```
|
|
1067
|
-
## Changes Ready for Review
|
|
1068
|
-
|
|
1069
|
-
All implementation is complete and CI checks pass.
|
|
1070
|
-
|
|
1071
|
-
### Files Changed
|
|
1072
|
-
- [list all modified/created files per feature]
|
|
1073
|
-
|
|
1074
|
-
### Suggested Next Steps
|
|
1075
|
-
1. Review the changes: `git -C "${SPECRAILS_REPO_DIR:-.}" diff`
|
|
1076
|
-
2. Create a branch: `git -C "${SPECRAILS_REPO_DIR:-.}" checkout -b feat/<name>`
|
|
1077
|
-
3. Stage and commit: `git -C "${SPECRAILS_REPO_DIR:-.}" add <files> && git -C "${SPECRAILS_REPO_DIR:-.}" commit -m "feat: ..."`
|
|
1078
|
-
4. Push and create PR manually
|
|
1079
|
-
```
|
|
1080
|
-
|
|
1081
|
-
#### Backlog updates (both modes)
|
|
1082
|
-
|
|
1083
|
-
**If `BACKLOG_WRITE=true`:**
|
|
1084
|
-
- For fully resolved issues/tickets: add a comment noting completion and reference the PR:
|
|
1085
|
-
```bash
|
|
1086
|
-
{{BACKLOG_COMMENT_CMD}}
|
|
1087
|
-
```
|
|
1088
|
-
- Update the ticket status to `"done"` using `{{BACKLOG_UPDATE_CMD}}` and add a comment: `"Implemented in PR #XX. All acceptance criteria met."` via `{{BACKLOG_COMMENT_CMD}}`. Tickets are closed directly in `local-tickets.json`.
|
|
1089
|
-
- For partially resolved issues/tickets: add a comment noting progress:
|
|
1090
|
-
```bash
|
|
1091
|
-
{{BACKLOG_PARTIAL_COMMENT_CMD}}
|
|
1092
|
-
```
|
|
1093
|
-
- Additionally update the ticket status to `"in_progress"` via `{{BACKLOG_UPDATE_CMD}}` if it is still `"todo"`.
|
|
1094
|
-
|
|
1095
|
-
**If `BACKLOG_WRITE=false`:**
|
|
1096
|
-
- Do NOT create, modify, or comment on any issues/tickets.
|
|
1097
|
-
- Instead, display what the user should update manually:
|
|
1098
|
-
```
|
|
1099
|
-
## Backlog Updates (manual)
|
|
1100
|
-
|
|
1101
|
-
The following tickets should be updated:
|
|
1102
|
-
| Ticket | Status | Suggested Action |
|
|
1103
|
-
|--------|--------|-----------------|
|
|
1104
|
-
| #85 | Fully implemented | Mark as Done in local-tickets.json |
|
|
1105
|
-
| #71 | Partial progress | Update to "in_progress": "X completed, Y remaining" |
|
|
1106
|
-
```
|
|
1107
|
-
|
|
1108
|
-
**Pipeline state:** update `ship` → `done` if git operations and PR creation succeeded, or `failed` with error context describing which step failed (e.g. `"git push failed: <exit code>"`, `"gh pr create failed"`, `"security gate blocked ship"`). If `DRY_RUN=true`: update `ship` → `skipped`.
|
|
1109
|
-
|
|
1110
|
-
### 4d. Monitor CI
|
|
1111
|
-
|
|
1112
|
-
**Only if `GIT_AUTO=true` and code was pushed.**
|
|
1113
|
-
|
|
1114
|
-
Check CI status after pushing. Fix failures (up to 2 retries).
|
|
1115
|
-
|
|
1116
|
-
**Pipeline state:** update `ci` → `done` if CI passed, or `failed` with error context `"CI failed after 2 retries: <summary>"`. If CI was not run (`GIT_AUTO=false` or dry-run): update `ci` → `skipped`.
|
|
1117
|
-
|
|
1118
|
-
If `GIT_AUTO=false`: skip — the user will push and monitor CI themselves.
|
|
1119
|
-
|
|
1120
|
-
### 4e. Report
|
|
1121
|
-
|
|
1122
|
-
**If `DRY_RUN=true`**, show this report instead of the standard pipeline table:
|
|
1123
|
-
|
|
1124
|
-
---
|
|
1125
|
-
|
|
1126
|
-
## Dry-Run Preview Report
|
|
1127
|
-
|
|
1128
|
-
### Artifacts Generated
|
|
1129
|
-
|
|
1130
|
-
| Type | Location |
|
|
1131
|
-
|------|----------|
|
|
1132
|
-
| OpenSpec proposal | openspec/changes/\<name\>/proposal.md |
|
|
1133
|
-
| OpenSpec design | openspec/changes/\<name\>/design.md |
|
|
1134
|
-
| OpenSpec tasks | openspec/changes/\<name\>/tasks.md |
|
|
1135
|
-
| OpenSpec context-bundle | openspec/changes/\<name\>/context-bundle.md |
|
|
1136
|
-
| Developer files | .claude/.dry-run/\<name\>/ (N files) |
|
|
1137
|
-
|
|
1138
|
-
### What Would Change
|
|
1139
|
-
|
|
1140
|
-
[For each file in `.cache-manifest.json` `files` array:]
|
|
1141
|
-
- `<real_path>` — [new file / modified] ([approximate line delta if available])
|
|
1142
|
-
|
|
1143
|
-
### Confidence
|
|
1144
|
-
|
|
1145
|
-
| | |
|
|
1146
|
-
|-|--|
|
|
1147
|
-
| Score file | `openspec/changes/<name>/confidence-score.json` |
|
|
1148
|
-
| Gate result | `<CONFIDENCE_STATUS>` (PASS / WARN / BLOCKED / OVERRIDE / MISSING / DISABLED) |
|
|
1149
|
-
| Overall score | `<overall score from confidence-score.json, or N/A if MISSING/DISABLED>` |
|
|
1150
|
-
|
|
1151
|
-
### Operations Skipped
|
|
1152
|
-
|
|
1153
|
-
[List items from `.cache-manifest.json` `skipped_operations` array]
|
|
1154
|
-
|
|
1155
|
-
### Next Steps
|
|
1156
|
-
|
|
1157
|
-
To apply these changes and ship:
|
|
1158
|
-
```
|
|
1159
|
-
/specrails:implement --apply <feature-name>
|
|
1160
|
-
```
|
|
1161
|
-
|
|
1162
|
-
To discard this dry run:
|
|
1163
|
-
```
|
|
1164
|
-
rm -rf .claude/.dry-run/<feature-name>/
|
|
1165
|
-
```
|
|
1166
|
-
|
|
1167
|
-
---
|
|
1168
|
-
|
|
1169
|
-
**Otherwise**, show the standard pipeline table:
|
|
1170
|
-
|
|
1171
|
-
```
|
|
1172
|
-
| Area | Feature | Change Name | Architect | Developer | Tests | Docs | Reviewer | Frontend | Backend | Confidence | Security | CI | Conflicts | Status |
|
|
1173
|
-
|------|---------|-------------|-----------|-----------|-------|------|----------|----------|---------|------------|----------|----|-----------|--------|
|
|
1174
|
-
```
|
|
1175
|
-
|
|
1176
|
-
Confidence column values:
|
|
1177
|
-
|
|
1178
|
-
| Value | Meaning |
|
|
1179
|
-
|-------|---------|
|
|
1180
|
-
| `PASS (82)` | All scores met thresholds; overall score shown in parens |
|
|
1181
|
-
| `WARN (62)` | Scores below threshold but `on_breach=warn`; overall score in parens |
|
|
1182
|
-
| `BLOCKED (62)` | Gate blocked the pipeline; overall score in parens |
|
|
1183
|
-
| `OVERRIDE (62)` | Gate bypassed by `--confidence-override`; overall score in parens |
|
|
1184
|
-
| `MISSING` | `confidence-score.json` not found after reviewer completed |
|
|
1185
|
-
| `DISABLED` | Gate disabled via `enabled: false` in config |
|
|
1186
|
-
|
|
1187
|
-
If `CONFIDENCE_OVERRIDE_REASON` is non-empty, append a `### Confidence Override` section below the table:
|
|
1188
|
-
|
|
1189
|
-
```
|
|
1190
|
-
### Confidence Override
|
|
1191
|
-
|
|
1192
|
-
**Reason:** <CONFIDENCE_OVERRIDE_REASON>
|
|
1193
|
-
```
|
|
1194
|
-
|
|
1195
|
-
Column values:
|
|
1196
|
-
- **Frontend**: `CLEAN`, `ISSUES`, or `SKIPPED` (no frontend files in changeset)
|
|
1197
|
-
- **Backend**: `CLEAN`, `ISSUES`, or `SKIPPED` (no backend files in changeset)
|
|
1198
|
-
- **Security**: `CLEAN`, `WARNINGS`, `BLOCKED`, or `SKIPPED`
|
|
1199
|
-
|
|
1200
|
-
The `Conflicts` column values:
|
|
1201
|
-
- `skipped` — `SNAPSHOTS_CAPTURED=false` (non-issue input or GH unavailable)
|
|
1202
|
-
- `clean` — both conflict checks ran and found no changes
|
|
1203
|
-
- `overridden (N)` — user chose Continue at one or both gates; N is the total number of conflict records in `CONFLICT_OVERRIDES`
|
|
1204
|
-
|
|
1205
|
-
If `MERGE_REPORT.requires_resolution` is non-empty, print an additional section:
|
|
1206
|
-
|
|
1207
|
-
```
|
|
1208
|
-
### Merge Conflicts Requiring Resolution
|
|
1209
|
-
|
|
1210
|
-
| File | Features | Conflicting Region | Resolver Status |
|
|
1211
|
-
|------|----------|--------------------|-----------------|
|
|
1212
|
-
| <file> | <feature-a>, <feature-b> | <section heading or hunk description> | LOW_CONFIDENCE / SKIPPED |
|
|
1213
|
-
|
|
1214
|
-
Fix these conflicts (search for `<<<<<<<` in each file), then commit the resolved files.
|
|
1215
|
-
To retry smart resolution after addressing context: `/specrails:merge-resolve --files <file>`
|
|
1216
|
-
```
|
|
1217
|
-
|
|
1218
|
-
If `CONFLICT_OVERRIDES` is non-empty, print:
|
|
1219
|
-
|
|
1220
|
-
```
|
|
1221
|
-
## Conflict Overrides
|
|
1222
|
-
|
|
1223
|
-
The following backlog conflicts were detected but overridden by the user:
|
|
1224
|
-
|
|
1225
|
-
| Phase | Issue | Field | Severity | Was | Now |
|
|
1226
|
-
|-------|-------|-------|----------|-----|-----|
|
|
1227
|
-
| 3a.0 | #42 | state | CRITICAL | open | closed |
|
|
1228
|
-
```
|
|
239
|
+
Only Core-owned backlog can close after all required delivery succeeds. First compare live ticket requirements against frozen scope; changed requirements remain open with the conflict reported. Failures preserve open tickets and resumable phase details.
|
|
1229
240
|
|
|
1230
|
-
|
|
241
|
+
## Preview and apply
|
|
1231
242
|
|
|
1232
|
-
|
|
1233
|
-
- If automatic: show PR URL, CI status, backlog updates made
|
|
1234
|
-
- If manual: show summary of changes, suggested git commands, backlog updates pending
|
|
243
|
+
--dry-run/--preview prepares bytes under stateDir without source edits/shipping/backlog writes. Manifest entries are `{repositoryId,path,operation:"write"|"delete",sourcePath?}`; targets are repository-relative. Record with `preview --request <stateDir/preview-request.json>`. Report **UNVERIFIED PREVIEW**; tests on untouched code describe only baseline.
|
|
1235
244
|
|
|
1236
|
-
|
|
245
|
+
--apply resumes the exact journal and calls `apply-preview --request <stateDir/full-checks.json>`. Runtime rejects stale base/cache, applies exact additions/edits/deletions and executes checks on the actual candidate. Failed checks retain reviewable applied work without success evidence. Continue developer task completion, reviewer/confidence and archive gates; never skip directly to shipping.
|
|
1237
246
|
|
|
1238
|
-
##
|
|
247
|
+
## Completion
|
|
1239
248
|
|
|
1240
|
-
|
|
1241
|
-
- If a sr-developer fails: report which phase it failed at
|
|
1242
|
-
- If the sr-reviewer finds unfixable issues: report them, push what works
|
|
1243
|
-
- If Phase 4c (ship) fails: report the failure
|
|
1244
|
-
- Never block the entire pipeline on a single agent failure. Always produce a final report.
|
|
249
|
+
Report run/change, frozen tickets/roots, phase statuses, actual receipt commands, acceptance, confidence, archive and per-repository delivery. Distinguish reused evidence, newly executed checks and gaps. Keep failure excerpts bounded and refer to durable receipts. Missing workers, failed gates or incomplete required repositories stay blocked/failed, never falsely complete.
|