specrails-core 4.10.0 → 4.11.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
@@ -134,6 +134,12 @@ specrails is not a chat interface. It's a **development pipeline** that coordina
134
134
 
135
135
  Architect designs → developer builds → reviewer validates → PR created. Multiple features run in parallel with git worktrees.
136
136
 
137
+ #### Letting a host own version control (`SPECRAILS_GIT_AUTO`)
138
+
139
+ By default the pipeline ships automatically (`GIT_AUTO=true`): it creates a branch, commits, pushes, and opens a pull request. When specrails-core runs **inside a host that owns version control itself** — such as [specrails-desktop](https://github.com/fjpulidop/specrails-desktop), which runs each pipeline in an isolated git worktree and opens the pull request for you — that host sets the `SPECRAILS_GIT_AUTO` environment variable to `false`.
140
+
141
+ When `SPECRAILS_GIT_AUTO=false` (or `0`), the Ship phase is forced onto the **manual** path regardless of configuration: the pipeline stops at "code written and verified" and makes **no branch, commit, push, or PR** — the host does that. This prevents a second, uncoordinated pull request. Leave the variable unset for the normal standalone behaviour (automatic shipping, subject to your `GIT_AUTO` configuration). It composes with `--dry-run`, which independently skips all git/GitHub/backlog operations.
142
+
137
143
  #### Dry-run / preview mode
138
144
 
139
145
  Not ready to commit? Run the full pipeline without touching git or GitHub:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specrails-core",
3
- "version": "4.10.0",
3
+ "version": "4.11.0",
4
4
  "description": "AI agent workflow system for Claude Code — installs 12 specialized agents, orchestration commands, and persona-driven product discovery into any repository",
5
5
  "type": "module",
6
6
  "bin": {
@@ -6,9 +6,9 @@ Full OpenSpec lifecycle with specialized agents: architect designs, developer im
6
6
 
7
7
  **Input:** $ARGUMENTS — accepts three modes:
8
8
 
9
- 1. **Issue numbers** (recommended): `#85, #71, #63` — implement these specific GitHub Issues directly. Skips exploration and selection.
9
+ 1. **Ticket numbers** (recommended): `#85, #71, #63` — implement these specific tickets from the local backlog (`local-tickets.json`). Skips exploration and selection.
10
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 issues exist.
11
+ 3. **Area names** (fallback): `Analytics, UI, Testing` — explores areas and picks the best items. Only use if no backlog tickets exist.
12
12
 
13
13
  **IMPORTANT:** Before running, ensure Read/Write/Bash/Glob/Grep permissions are set to "allow" — background agents cannot request permissions interactively.
14
14
 
@@ -21,7 +21,7 @@ Your working directory may NOT be the user's source repository. **Repo-resident*
21
21
  Rules used throughout this pipeline:
22
22
  - **openspec reads/writes** → `${SPECRAILS_REPO_DIR:-.}/openspec/...`
23
23
  - **git commands** → `git -C "${SPECRAILS_REPO_DIR:-.}" ...`
24
- - **`gh` commands** (issue/PR — they need the repo's remote) → run them from the repo: `(cd "${SPECRAILS_REPO_DIR:-.}" && gh ...)`
24
+ - **`gh` commands** (PR — they need the repo's remote) → run them from the repo: `(cd "${SPECRAILS_REPO_DIR:-.}" && gh ...)`
25
25
  - **worktree merge-back** → the merge *target* side (the main working tree) is `${SPECRAILS_REPO_DIR:-.}/<file>`
26
26
 
27
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:-.}`.
@@ -40,22 +40,13 @@ Check the environment variable `CLAUDE_CODE_ENTRYPOINT`. If it contains `remote_
40
40
 
41
41
  #### 1. Backlog provider availability
42
42
 
43
- Read `.specrails/backlog-config.json` and extract `BACKLOG_PROVIDER`.
44
-
45
- **If `BACKLOG_PROVIDER=local`:**
46
43
  ```bash
47
44
  [[ -f ".specrails/local-tickets.json" ]] && echo "Local tickets storage: OK" || echo "WARNING: local-tickets.json not found"
48
45
  ```
49
46
  - Set `LOCAL_TICKETS_AVAILABLE=true/false` based on file existence.
50
- - Set `GH_AVAILABLE=false` (GitHub CLI not needed for local provider).
47
+ - Set `GH_AVAILABLE=false` (backlog is always local).
51
48
  - Set `BACKLOG_AVAILABLE=true` if local-tickets.json exists.
52
49
 
53
- **Otherwise:**
54
- ```bash
55
- gh auth status 2>&1
56
- ```
57
- - Set `GH_AVAILABLE=true/false` for later phases.
58
-
59
50
  #### 2. OpenSpec CLI
60
51
 
61
52
  ```bash
@@ -312,13 +303,10 @@ Initialize conflict-tracking variables:
312
303
  - Set `SINGLE_MODE = true`. No worktrees, no parallelism.
313
304
  - **Skip Phase 1 and Phase 2** — go directly to Phase 3a.
314
305
 
315
- **If the user passed issue/ticket references** (e.g. `#85, #71` for GitHub, `#1, #2` for local tickets, or `PROJ-85, PROJ-71` for JIRA):
316
- - Fetch each issue/ticket:
317
- ```bash
318
- {{BACKLOG_VIEW_CMD}}
319
- ```
320
- - Extract area, value, effort, and feature details from each issue body.
321
- - If only 1 issue: set `SINGLE_MODE = true`.
306
+ **If the user passed ticket references** (e.g. `#85, #71` or `#1, #2`):
307
+ - Fetch each ticket from `.specrails/local-tickets.json` at `tickets["{id}"]`.
308
+ - Extract area, value, effort, and feature details from each ticket body.
309
+ - If only 1 ticket: set `SINGLE_MODE = true`.
322
310
  - **Skip Phase 1 and Phase 2** — go directly to confirmation table.
323
311
 
324
312
  #### Phase 0 snapshot capture
@@ -362,48 +350,7 @@ If the write succeeds: set `SNAPSHOTS_CAPTURED=true`.
362
350
 
363
351
  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.
364
352
 
365
- ##### If `GH_AVAILABLE=true` and input mode was issue numbers (GitHub/JIRA):
366
-
367
- For each resolved issue number, run:
368
-
369
- ```bash
370
- (cd "${SPECRAILS_REPO_DIR:-.}" && gh issue view {number} --json number,title,state,assignees,labels,body,updatedAt)
371
- ```
372
-
373
- Build a snapshot object for each issue:
374
- - `number`: integer issue number
375
- - `title`: issue title string
376
- - `state`: `"open"` or `"closed"`
377
- - `assignees`: array of assignee login names, sorted alphabetically
378
- - `labels`: array of label names, sorted alphabetically
379
- - `body_sha`: SHA-256 of the raw body string — compute with:
380
- ```bash
381
- echo -n "{body}" | sha256sum | cut -d' ' -f1
382
- ```
383
- If `sha256sum` is not available, fall back to `openssl dgst -sha256 -r` or `shasum -a 256`.
384
- - `updated_at`: the `updatedAt` value from the GitHub API response
385
- - `captured_at`: current local time in ISO 8601 format
386
-
387
- Write the following JSON to `.claude/backlog-cache.json` (overwrite fully — this establishes a fresh baseline for this run):
388
-
389
- ```json
390
- {
391
- "schema_version": "1",
392
- "provider": "github",
393
- "last_updated": "<ISO 8601 timestamp>",
394
- "written_by": "implement",
395
- "issues": {
396
- "<number>": { <snapshot object> },
397
- ...
398
- }
399
- }
400
- ```
401
-
402
- If the write succeeds: set `SNAPSHOTS_CAPTURED=true`.
403
-
404
- If the write fails (e.g., `.claude/` directory does not exist): print `[backlog-cache] Warning: could not write cache. Conflict detection disabled for this run.` and set `SNAPSHOTS_CAPTURED=false`. Do NOT abort the pipeline.
405
-
406
- ##### Otherwise (no backlog available or non-issue input):
353
+ ##### Otherwise (no backlog available or non-ticket input):
407
354
 
408
355
  Set `SNAPSHOTS_CAPTURED=false`. Print: `[conflict-check] Snapshot skipped — backlog unavailable or non-issue input.`
409
356
 
@@ -509,19 +456,9 @@ Pick the single idea with the best impact/effort ratio from each exploration. Pr
509
456
 
510
457
  **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.
511
458
 
512
- Otherwise, re-fetch each issue in scope and diff against the Phase 0 snapshot:
459
+ Otherwise, re-fetch each ticket in scope and diff against the Phase 0 snapshot:
513
460
 
514
- **If `BACKLOG_PROVIDER=local`:** 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.
515
-
516
- **If `BACKLOG_PROVIDER=github`:** For each issue number in `ISSUE_REFS`:
517
-
518
- ```bash
519
- (cd "${SPECRAILS_REPO_DIR:-.}" && gh issue view {number} --json number,title,state,assignees,labels,body,updatedAt)
520
- ```
521
-
522
- If the `gh` command returns non-zero (issue deleted or inaccessible): treat as a CRITICAL conflict — field `"state"`, was `<cached state>`, now `"deleted"`.
523
-
524
- In both cases, reconstruct a current snapshot (same shape as Phase 0: sort `assignees` and `labels`, compute `body_sha`).
461
+ 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`).
525
462
 
526
463
  **Short-circuit:** If `current.updatedAt == cached.updated_at`, mark the issue as clean and skip field comparison.
527
464
 
@@ -1223,14 +1160,9 @@ When `DRY_RUN=true`, the reviewer still writes `confidence-score.json` (it is an
1223
1160
 
1224
1161
  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.
1225
1162
 
1226
- Re-fetch each issue in `ISSUE_REFS` and diff against `.claude/backlog-cache.json` using the same algorithm as Phase 3a.0:
1163
+ Re-fetch each ticket in `ISSUE_REFS` and diff against `.claude/backlog-cache.json` using the same algorithm as Phase 3a.0:
1227
1164
 
1228
- **If `BACKLOG_PROVIDER=local`:** Read `.specrails/local-tickets.json` and extract each ticket by ID.
1229
-
1230
- **If `BACKLOG_PROVIDER=github`:**
1231
- ```bash
1232
- (cd "${SPECRAILS_REPO_DIR:-.}" && gh issue view {number} --json number,title,state,assignees,labels,body,updatedAt)
1233
- ```
1165
+ Read `.specrails/local-tickets.json` and extract each ticket by ID.
1234
1166
 
1235
1167
  If the cache file is missing or malformed JSON at this point: log `[conflict-check] Warning: cache file missing or unreadable. Skipping diff for this run.` and proceed to Phase 4c (treat as clean).
1236
1168
 
@@ -1264,9 +1196,6 @@ Record skipped operations to `.cache-manifest.json` under `skipped_operations`:
1264
1196
  - If `BACKLOG_PROVIDER=local` and `BACKLOG_WRITE=true`:
1265
1197
  - `"local: ticket comment #{id}"` for each ticket in scope
1266
1198
  - `"local: ticket status update #{id}"` for each fully resolved ticket
1267
- - If `BACKLOG_PROVIDER=github` and `BACKLOG_WRITE=true`:
1268
- - `"github: issue comment #N"` for each issue in scope
1269
- - `"github: issue close #N (via PR merge)"` for each fully resolved issue
1270
1199
 
1271
1200
  Then skip the rest of Phase 4c and proceed directly to Phase 4e.
1272
1201
 
@@ -1284,6 +1213,8 @@ Then skip the rest of Phase 4c and proceed directly to Phase 4e.
1284
1213
 
1285
1214
  This phase respects the `GIT_AUTO` and `BACKLOG_WRITE` settings from configuration.
1286
1215
 
1216
+ **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.
1217
+
1287
1218
  #### If `GIT_AUTO=true` (automatic shipping)
1288
1219
 
1289
1220
  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.
@@ -1324,15 +1255,12 @@ All implementation is complete and CI checks pass.
1324
1255
  ```bash
1325
1256
  {{BACKLOG_COMMENT_CMD}}
1326
1257
  ```
1327
- - **Local:** 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}}`. Local tickets are closed directly there is no auto-close-on-merge mechanism.
1328
- - **GitHub:** `(cd "${SPECRAILS_REPO_DIR:-.}" && gh issue comment {number} --body "Implemented in PR #XX. All acceptance criteria met.")` — do NOT close the issue explicitly. Use `Closes #N` in the PR body so GitHub auto-closes on merge.
1329
- - **JIRA:** `jira issue comment {key} --message "Implemented in PR #XX. All acceptance criteria met."`
1330
- - For GitHub/JIRA: ensure the PR body includes `Closes #N` for each fully resolved issue (auto-closes on merge)
1258
+ - 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`.
1331
1259
  - For partially resolved issues/tickets: add a comment noting progress:
1332
1260
  ```bash
1333
1261
  {{BACKLOG_PARTIAL_COMMENT_CMD}}
1334
1262
  ```
1335
- - **Local:** Additionally update the ticket status to `"in_progress"` via `{{BACKLOG_UPDATE_CMD}}` if it is still `"todo"`.
1263
+ - Additionally update the ticket status to `"in_progress"` via `{{BACKLOG_UPDATE_CMD}}` if it is still `"todo"`.
1336
1264
 
1337
1265
  **If `BACKLOG_WRITE=false`:**
1338
1266
  - Do NOT create, modify, or comment on any issues/tickets.
@@ -1343,8 +1271,8 @@ All implementation is complete and CI checks pass.
1343
1271
  The following tickets should be updated:
1344
1272
  | Ticket | Status | Suggested Action |
1345
1273
  |--------|--------|-----------------|
1346
- | #85 / PROJ-85 | Fully implemented | Close / move to Done |
1347
- | #71 / PROJ-71 | Partial progress | Comment: "X completed, Y remaining" |
1274
+ | #85 | Fully implemented | Mark as Done in local-tickets.json |
1275
+ | #71 | Partial progress | Update to "in_progress": "X completed, Y remaining" |
1348
1276
  ```
1349
1277
 
1350
1278
  **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`.