sequant 2.10.0 → 2.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/.claude-plugin/plugin.json +1 -1
- package/README.md +6 -2
- package/dist/bin/cli.js +47 -2
- package/dist/src/commands/locks.d.ts +20 -1
- package/dist/src/commands/locks.js +206 -4
- package/dist/src/commands/ready.d.ts +6 -0
- package/dist/src/commands/ready.js +15 -1
- package/dist/src/commands/run-display.js +1 -0
- package/dist/src/commands/worktree.d.ts +31 -0
- package/dist/src/commands/worktree.js +95 -0
- package/dist/src/lib/cli-flags.d.ts +23 -0
- package/dist/src/lib/cli-flags.js +43 -0
- package/dist/src/lib/cli-ui/run-renderer-types.d.ts +2 -0
- package/dist/src/lib/cli-ui/run-renderer.js +7 -1
- package/dist/src/lib/locks/checkout-lock.d.ts +193 -0
- package/dist/src/lib/locks/checkout-lock.js +389 -0
- package/dist/src/lib/locks/index.d.ts +6 -3
- package/dist/src/lib/locks/index.js +4 -2
- package/dist/src/lib/locks/lock-manager.d.ts +81 -1
- package/dist/src/lib/locks/lock-manager.js +230 -5
- package/dist/src/lib/locks/types.d.ts +72 -0
- package/dist/src/lib/locks/types.js +28 -0
- package/dist/src/lib/settings.d.ts +73 -0
- package/dist/src/lib/settings.js +45 -0
- package/dist/src/lib/test-tautology-detector.d.ts +4 -3
- package/dist/src/lib/test-tautology-detector.js +101 -41
- package/dist/src/lib/workflow/batch-executor.js +78 -19
- package/dist/src/lib/workflow/config-resolver.d.ts +25 -0
- package/dist/src/lib/workflow/config-resolver.js +89 -0
- package/dist/src/lib/workflow/drivers/agent-driver.d.ts +15 -0
- package/dist/src/lib/workflow/drivers/claude-code.js +5 -0
- package/dist/src/lib/workflow/effort-escalation.d.ts +73 -0
- package/dist/src/lib/workflow/effort-escalation.js +82 -0
- package/dist/src/lib/workflow/error-classifier.d.ts +4 -1
- package/dist/src/lib/workflow/error-classifier.js +4 -0
- package/dist/src/lib/workflow/log-writer.d.ts +10 -1
- package/dist/src/lib/workflow/log-writer.js +20 -0
- package/dist/src/lib/workflow/metrics-schema.d.ts +49 -6
- package/dist/src/lib/workflow/metrics-schema.js +33 -0
- package/dist/src/lib/workflow/metrics-writer.d.ts +11 -0
- package/dist/src/lib/workflow/phase-detection.d.ts +12 -0
- package/dist/src/lib/workflow/phase-detection.js +5 -1
- package/dist/src/lib/workflow/phase-executor.js +10 -0
- package/dist/src/lib/workflow/ready-gate.d.ts +28 -0
- package/dist/src/lib/workflow/ready-gate.js +24 -3
- package/dist/src/lib/workflow/run-log-schema.d.ts +55 -0
- package/dist/src/lib/workflow/run-log-schema.js +31 -1
- package/dist/src/lib/workflow/run-orchestrator.js +27 -0
- package/dist/src/lib/workflow/spec-recommendation.d.ts +71 -0
- package/dist/src/lib/workflow/spec-recommendation.js +142 -0
- package/dist/src/lib/workflow/types.d.ts +64 -0
- package/dist/src/lib/workflow/worktree-manager.d.ts +8 -1
- package/dist/src/lib/workflow/worktree-manager.js +9 -1
- package/dist/src/lib/workflow/worktree-resolver.d.ts +73 -0
- package/dist/src/lib/workflow/worktree-resolver.js +126 -0
- package/package.json +3 -2
- package/templates/hooks/pre-tool.sh +228 -0
- package/templates/scripts/cleanup-worktree.sh +36 -15
- package/templates/scripts/new-feature.sh +25 -19
- package/templates/skills/_shared/references/subagent-types.md +7 -18
- package/templates/skills/assess/SKILL.md +5 -1
- package/templates/skills/exec/SKILL.md +61 -7
- package/templates/skills/fullsolve/SKILL.md +127 -21
- package/templates/skills/loop/SKILL.md +56 -11
- package/templates/skills/merger/SKILL.md +98 -10
- package/templates/skills/qa/SKILL.md +59 -6
- package/templates/skills/release/SKILL.md +79 -0
- package/templates/skills/spec/SKILL.md +31 -15
- package/templates/skills/spec/references/recommended-workflow.md +14 -1
- package/templates/skills/testgen/SKILL.md +23 -6
- package/templates/agents/sequant-explorer.md +0 -24
|
@@ -77,12 +77,17 @@ RIGHT: "Spec complete. Proceeding to exec..." [invokes /exec immediately]
|
|
|
77
77
|
├─────────────────────────────────────────────────────────────┤
|
|
78
78
|
│ │
|
|
79
79
|
│ ┌─────────┐ │
|
|
80
|
-
│ │ SPEC │ Plan implementation, extract AC
|
|
80
|
+
│ │ SPEC │ Plan implementation, extract AC (main repo) │
|
|
81
81
|
│ └────┬────┘ │
|
|
82
82
|
│ │ │
|
|
83
83
|
│ ▼ │
|
|
84
84
|
│ ┌─────────┐ │
|
|
85
|
-
│ │
|
|
85
|
+
│ │WORKTREE │ Create it here; export the resolved path │
|
|
86
|
+
│ └────┬────┘ │
|
|
87
|
+
│ │ │
|
|
88
|
+
│ ▼ │
|
|
89
|
+
│ ┌─────────┐ │
|
|
90
|
+
│ │ EXEC │ Verify the path, then implement inside it │
|
|
86
91
|
│ └────┬────┘ │
|
|
87
92
|
│ │ │
|
|
88
93
|
│ ▼ │
|
|
@@ -166,7 +171,7 @@ This skill acts as an **orchestrator** and sets environment variables for child
|
|
|
166
171
|
|
|
167
172
|
**Child skills detect orchestration via `SEQUANT_ORCHESTRATOR` and adjust behavior:**
|
|
168
173
|
- `/spec`: Runs normally (first phase, no prior context)
|
|
169
|
-
- `/exec`: Skips worktree creation,
|
|
174
|
+
- `/exec`: Skips worktree creation, but still verifies the provided path before using it
|
|
170
175
|
- `/test`: Skips issue fetch, trusts orchestrator context
|
|
171
176
|
- `/qa`: Skips pre-flight sync, defers GitHub updates
|
|
172
177
|
- `/loop`: Uses provided worktree, defers GitHub updates
|
|
@@ -250,9 +255,30 @@ Before creating any files, check if they already exist:
|
|
|
250
255
|
|
|
251
256
|
**If work already exists:** Skip to the appropriate phase (e.g., if implementation is done, go to Phase 3 or 4).
|
|
252
257
|
|
|
253
|
-
### 0.3 Acquire Concurrency
|
|
258
|
+
### 0.3 Acquire Concurrency Locks (#625, #901)
|
|
259
|
+
|
|
260
|
+
**Before invoking `/spec`**, claim the per-issue concurrency lock.
|
|
261
|
+
|
|
262
|
+
**Phase 0 always runs — including on a resumed run.** Smart Resumption (above) chooses only *which phase comes next after Phase 0*; it never skips Phase 0 itself. A resumed session that skipped the acquire below would hold neither lock while doing exactly the work the locks exist to protect, and would then run the release contract against a lock it never took.
|
|
254
263
|
|
|
255
|
-
**
|
|
264
|
+
**Declare the issue for the guard.** `pre-tool.sh` decides whether *you* are the checkout's holder by `sessionId` when both sides have one, then by `SEQUANT_ISSUE`, and finally by the session→issue binding it records for itself.
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
export SEQUANT_ISSUE=<issue-number>
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
**You do not need that export to be recognized as the holder (#906).** `PreToolUse` runs *outside and before* your command's shell, so nothing a skill bash block exports is visible to it — not even an export prepended to the same block as the guarded command. Instead the hook watches for `locks checkout acquire --issue=<N>` and records the binding itself, keyed on the session id, which is the one identity that survives the shell boundary killing the acquiring PID. The `export` above still helps a parent process that launched you with it; it is not what unblocks you.
|
|
271
|
+
|
|
272
|
+
Consequences worth knowing:
|
|
273
|
+
|
|
274
|
+
- After the acquire in this phase, branch-mutating git **in the main checkout is allowed for you** and still refused for every other session.
|
|
275
|
+
- `git -C "$SEQUANT_WORKTREE" …` is exempt regardless, and is what every phase after 1.5 should be using anyway.
|
|
276
|
+
- Path-restore (`git checkout -- <path>`) is exempt whether or not the path is quoted.
|
|
277
|
+
- If the hook never observed your acquire (it was disabled, or the lock was taken by another tool), you are treated as a non-holder — release and re-acquire so the binding is recorded.
|
|
278
|
+
|
|
279
|
+
**What this lock covers — and what it does not (#901).** The per-issue lock is keyed on the *issue number*. It prevents a second session from working on **the same issue** — another `/fullsolve <same-issue>`, an `npx sequant run <same-issue>`, or the same issue in a different window — and producing zero-diff exec failures.
|
|
280
|
+
|
|
281
|
+
It is **not** a general concurrency guarantee. Two sessions working *different* issues take different lock files and never contend, even when they share one working tree. `git checkout`, `switch`, `reset`, `rebase`, `merge` and `cherry-pick` are global to a checkout, so the per-issue lock says nothing about them. That contention is covered by the separate **checkout lock** below.
|
|
256
282
|
|
|
257
283
|
```bash
|
|
258
284
|
# Acquire lock for this issue. --skip-pid-check is required: the shell that
|
|
@@ -267,11 +293,26 @@ if ! npx sequant locks acquire <issue-number> \
|
|
|
267
293
|
fi
|
|
268
294
|
```
|
|
269
295
|
|
|
270
|
-
**
|
|
296
|
+
**Checkout lock (#901).** Claim the working tree as well, so a session on a *different* issue cannot run branch-mutating git in the same checkout. `pre-tool.sh` enforces this: a foreign session's `git checkout`/`switch`/`reset`/`rebase`/`merge`/`cherry-pick` in the main checkout is refused with a message naming the holder and its issue.
|
|
297
|
+
|
|
298
|
+
```bash
|
|
299
|
+
# Claim the shared working tree. Skip when you will work entirely inside a
|
|
300
|
+
# feature worktree (`git -C <worktree> ...` is never blocked).
|
|
301
|
+
npx sequant locks checkout acquire \
|
|
302
|
+
--issue=<issue-number> \
|
|
303
|
+
--command="/fullsolve <issue-number>" \
|
|
304
|
+
--skip-pid-check || true
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
Release it alongside the per-issue lock: `npx sequant locks checkout release --issue=<issue-number> || true`. **`--issue` is mandatory** (#906) — it is what proves you are the holder. `--skip-pid-check` means the acquiring shell's PID is already dead, so PID identity is unavailable and a release without `--issue` is refused, not merely ineffective. Stale recovery is therefore age-based only for this lock: the 6h `SEQUANT_SKILL_LOCK_TTL_MS` and the 24h `SEQUANT_MAX_LOCK_AGE_MS` ceiling, *not* same-host dead-PID recovery, which `--skip-pid-check` disables by definition. An abandoned holder still cannot wedge the checkout permanently.
|
|
308
|
+
|
|
309
|
+
**Release contract:** Phase 5.5 releases both locks on the happy path. On ANY branch that **exits the workflow without reaching Phase 5** — spec failure, exec iterations exhausted, unrecoverable error — you MUST run `npx sequant locks release <issue-number> || true` and `npx sequant locks checkout release --issue=<issue-number> || true` **before** printing the halt message. The explicit release calls below cover the known branches; if you add a new early-exit path, add a release call there too.
|
|
310
|
+
|
|
311
|
+
**Do NOT release at a branch that continues to Phase 5.** QA-loop exhaustion and the stagnation halt both fall through to PR creation, which still runs git in this tree — releasing there would leave Phase 5 unprotected.
|
|
271
312
|
|
|
272
313
|
**Backstop:** If a release is somehow missed, stale recovery clears the lock after 6h on the same host (`SEQUANT_SKILL_LOCK_TTL_MS` overrides). The user can also force-clear via `npx sequant locks clear <issue-number>`.
|
|
273
314
|
|
|
274
|
-
**Orchestrator/MCP mode:** When `SEQUANT_ORCHESTRATOR` is set, `locks acquire` and `locks
|
|
315
|
+
**Orchestrator/MCP mode:** When `SEQUANT_ORCHESTRATOR` is set, `locks acquire`, `locks release` and every `locks checkout` action are no-ops (exit 0, no file touched), and the `pre-tool.sh` checkout guard stands down. Safe to call unconditionally.
|
|
275
316
|
|
|
276
317
|
## Phase 1: Planning (SPEC)
|
|
277
318
|
|
|
@@ -290,20 +331,20 @@ The `/spec` skill will:
|
|
|
290
331
|
- Extract acceptance criteria (AC-1, AC-2, etc.)
|
|
291
332
|
- Create implementation plan (3-7 steps)
|
|
292
333
|
- Post plan comment to the issue
|
|
293
|
-
|
|
334
|
+
|
|
335
|
+
`/spec` plans in the main repository and does **not** create a worktree — that
|
|
336
|
+
is this skill's job, in Phase 1.5 below.
|
|
294
337
|
|
|
295
338
|
### 1.2 Capture Spec Output
|
|
296
339
|
|
|
297
340
|
After `/spec` completes, extract and store:
|
|
298
341
|
- **AC Checklist:** List of acceptance criteria for tracking
|
|
299
|
-
- **Worktree Path:** Location for subsequent phases
|
|
300
342
|
- **Recommended Phases:** Whether `/test` is needed (UI features)
|
|
301
343
|
|
|
302
344
|
```markdown
|
|
303
345
|
## Spec Output Captured
|
|
304
346
|
|
|
305
347
|
**Issue:** #<N>
|
|
306
|
-
**Worktree:** ../worktrees/feature/<issue-number>-*/
|
|
307
348
|
**AC Count:** <N> items
|
|
308
349
|
**Needs Testing:** Yes/No (based on labels)
|
|
309
350
|
```
|
|
@@ -319,6 +360,7 @@ If `/spec` fails:
|
|
|
319
360
|
```bash
|
|
320
361
|
# Release before halting — see Phase 0.3 release contract.
|
|
321
362
|
npx sequant locks release <issue-number> || true
|
|
363
|
+
npx sequant locks checkout release --issue=<issue-number> || true
|
|
322
364
|
```
|
|
323
365
|
|
|
324
366
|
```markdown
|
|
@@ -333,7 +375,55 @@ Workflow halted. Fix the issue and re-run `/fullsolve <issue-number>`.
|
|
|
333
375
|
**State after Phase 1:**
|
|
334
376
|
- AC checklist defined
|
|
335
377
|
- Implementation plan created (and posted to GitHub)
|
|
336
|
-
-
|
|
378
|
+
- Still in the main repository — no worktree exists yet
|
|
379
|
+
|
|
380
|
+
**→ IMMEDIATELY proceed to Phase 1.5 (do not wait for user input)**
|
|
381
|
+
|
|
382
|
+
## Phase 1.5: Create the Feature Worktree
|
|
383
|
+
|
|
384
|
+
<!-- BEGIN: worktree-creation (#899) -->
|
|
385
|
+
|
|
386
|
+
**This skill creates the worktree. Nothing upstream does it.** Every phase from
|
|
387
|
+
here on runs inside it, and `SEQUANT_WORKTREE` is what tells the child skills
|
|
388
|
+
where "here" is — so it must hold a **resolved absolute path**, never a glob.
|
|
389
|
+
|
|
390
|
+
```bash
|
|
391
|
+
# 1. Create it. Idempotent: exits 0 with "Worktree already exists" on re-entry
|
|
392
|
+
# (resumed session, retried phase), so this is safe to run unconditionally.
|
|
393
|
+
./scripts/new-feature.sh <issue-number>
|
|
394
|
+
|
|
395
|
+
# 2. Resolve the real path. `sequant worktree resolve` reads
|
|
396
|
+
# `git worktree list` in THIS repository and selects on the branch, so it
|
|
397
|
+
# can never return a sibling project's worktree — `../worktrees/` is one
|
|
398
|
+
# flat namespace shared by every repo under the same parent.
|
|
399
|
+
SEQUANT_WORKTREE="$(npx sequant worktree resolve <issue-number>)" || {
|
|
400
|
+
echo "❌ Could not resolve a worktree for #<issue-number> after creating one."
|
|
401
|
+
npx sequant locks release <issue-number> || true
|
|
402
|
+
npx sequant locks checkout release --issue=<issue-number> || true
|
|
403
|
+
exit 1
|
|
404
|
+
}
|
|
405
|
+
export SEQUANT_WORKTREE
|
|
406
|
+
echo "Worktree: $SEQUANT_WORKTREE"
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
**Never substitute a glob for this step.** `../worktrees/feature/<issue>-*/`
|
|
410
|
+
is not a path: unquoted it may not expand at all, and where it does expand it
|
|
411
|
+
matches on the directory slug, which is shared across repositories and can
|
|
412
|
+
drift from its own branch after a rename.
|
|
413
|
+
|
|
414
|
+
**If `new-feature.sh` fails** (dirty tree, branch conflict), release the lock
|
|
415
|
+
and halt — do not proceed into Phase 2 in the main checkout:
|
|
416
|
+
|
|
417
|
+
```bash
|
|
418
|
+
npx sequant locks release <issue-number> || true
|
|
419
|
+
npx sequant locks checkout release --issue=<issue-number> || true
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
<!-- END: worktree-creation (#899) -->
|
|
423
|
+
|
|
424
|
+
**State after Phase 1.5:**
|
|
425
|
+
- Feature worktree created and verified to belong to this repository
|
|
426
|
+
- `SEQUANT_WORKTREE` exported as a resolved absolute path
|
|
337
427
|
|
|
338
428
|
**→ IMMEDIATELY proceed to Phase 2 (do not wait for user input)**
|
|
339
429
|
|
|
@@ -363,12 +453,13 @@ Set environment variables before invoking `/exec` so it can optimize its behavio
|
|
|
363
453
|
export SEQUANT_ORCHESTRATOR=fullsolve
|
|
364
454
|
export SEQUANT_PHASE=exec
|
|
365
455
|
export SEQUANT_ISSUE=<issue-number>
|
|
366
|
-
|
|
456
|
+
# Absolute path resolved in Phase 1.5 — never a glob.
|
|
457
|
+
export SEQUANT_WORKTREE="$SEQUANT_WORKTREE"
|
|
367
458
|
```
|
|
368
459
|
|
|
369
460
|
When `/exec` detects `SEQUANT_ORCHESTRATOR`, it:
|
|
370
|
-
- Skips worktree creation (already done
|
|
371
|
-
-
|
|
461
|
+
- Skips worktree creation (already done in Phase 1.5)
|
|
462
|
+
- Verifies the provided path with `npx sequant worktree verify` before using it
|
|
372
463
|
- Defers GitHub comment updates to orchestrator
|
|
373
464
|
|
|
374
465
|
### 2.3 Handle Exec Failures
|
|
@@ -393,6 +484,7 @@ while exec_iteration < MAX_EXEC_ITERATIONS:
|
|
|
393
484
|
```bash
|
|
394
485
|
# Release before halting — see Phase 0.3 release contract.
|
|
395
486
|
npx sequant locks release <issue-number> || true
|
|
487
|
+
npx sequant locks checkout release --issue=<issue-number> || true
|
|
396
488
|
```
|
|
397
489
|
|
|
398
490
|
```markdown
|
|
@@ -465,7 +557,8 @@ The `/test` skill will:
|
|
|
465
557
|
export SEQUANT_ORCHESTRATOR=fullsolve
|
|
466
558
|
export SEQUANT_PHASE=test
|
|
467
559
|
export SEQUANT_ISSUE=<issue-number>
|
|
468
|
-
|
|
560
|
+
# Absolute path resolved in Phase 1.5 — never a glob.
|
|
561
|
+
export SEQUANT_WORKTREE="$SEQUANT_WORKTREE"
|
|
469
562
|
```
|
|
470
563
|
|
|
471
564
|
When `/test` detects `SEQUANT_ORCHESTRATOR`, it:
|
|
@@ -534,7 +627,8 @@ The `/qa` skill will:
|
|
|
534
627
|
export SEQUANT_ORCHESTRATOR=fullsolve
|
|
535
628
|
export SEQUANT_PHASE=qa
|
|
536
629
|
export SEQUANT_ISSUE=<issue-number>
|
|
537
|
-
|
|
630
|
+
# Absolute path resolved in Phase 1.5 — never a glob.
|
|
631
|
+
export SEQUANT_WORKTREE="$SEQUANT_WORKTREE"
|
|
538
632
|
```
|
|
539
633
|
|
|
540
634
|
When `/qa` detects `SEQUANT_ORCHESTRATOR`, it:
|
|
@@ -632,6 +726,11 @@ echo "Current branch: $CURRENT_BRANCH"
|
|
|
632
726
|
|
|
633
727
|
# HARD GATE: Must be on a feature branch, not main/master
|
|
634
728
|
if [[ "$CURRENT_BRANCH" == "main" || "$CURRENT_BRANCH" == "master" ]]; then
|
|
729
|
+
# Release before halting — see Phase 0.3 release contract. This gate fires
|
|
730
|
+
# later than any other exit path, so a leak here wedges the tree for the
|
|
731
|
+
# longest (#906).
|
|
732
|
+
npx sequant locks release <issue-number> || true
|
|
733
|
+
npx sequant locks checkout release --issue=<issue-number> || true
|
|
635
734
|
echo "❌ ERROR: On $CURRENT_BRANCH — commits must NOT land on main."
|
|
636
735
|
echo " Fix: git checkout feature/<issue-number>-* or create a new branch."
|
|
637
736
|
exit 1
|
|
@@ -699,16 +798,19 @@ npx sequant doctor
|
|
|
699
798
|
|
|
700
799
|
If any command fails, fix immediately on main before continuing. This catches issues like ESM compatibility bugs that unit tests may miss.
|
|
701
800
|
|
|
702
|
-
### 5.5 Release Concurrency
|
|
801
|
+
### 5.5 Release Concurrency Locks (#625, #901)
|
|
703
802
|
|
|
704
|
-
After the PR is created (or earlier if the workflow exits gracefully), release
|
|
803
|
+
After the PR is created (or earlier if the workflow exits gracefully), release both locks so other sessions can claim them:
|
|
705
804
|
|
|
706
805
|
```bash
|
|
707
806
|
npx sequant locks release <issue-number> || true
|
|
807
|
+
npx sequant locks checkout release --issue=<issue-number> || true
|
|
708
808
|
```
|
|
709
809
|
|
|
710
810
|
`|| true` is intentional — release is idempotent; the lock may already have been cleared (orchestrator mode, age-based recovery, or manual `locks clear`). The exit code is informational only.
|
|
711
811
|
|
|
812
|
+
**But read it if you are debugging (#906).** A non-zero exit from `locks checkout release` no longer means only "nothing was held". It now also means **"held, but not by you"** — a refusal, printed with the holder's issue and the `clear --force` recovery command. If you see that after your own run, the usual cause is a missing or wrong `--issue`, not a stale lock.
|
|
813
|
+
|
|
712
814
|
## Iteration Tracking
|
|
713
815
|
|
|
714
816
|
Track iterations to prevent infinite loops:
|
|
@@ -807,13 +909,16 @@ Ready for human review and merge.
|
|
|
807
909
|
|
|
808
910
|
## Error Recovery
|
|
809
911
|
|
|
810
|
-
**Concurrency lock cleanup (#625, applies to every abort path below):**
|
|
912
|
+
**Concurrency lock cleanup (#625, #901, applies to every abort path below):**
|
|
811
913
|
|
|
812
914
|
```bash
|
|
813
915
|
npx sequant locks release <issue-number> || true
|
|
916
|
+
npx sequant locks checkout release --issue=<issue-number> || true
|
|
814
917
|
```
|
|
815
918
|
|
|
816
|
-
Run this BEFORE printing the halt/exit message in any
|
|
919
|
+
Run this BEFORE printing the halt/exit message in any branch below that **exits the workflow**. The release call is idempotent (no-op when nothing is held, no-op in orchestrator mode), so calling it unconditionally on a genuine exit path is safe.
|
|
920
|
+
|
|
921
|
+
Do **not** run it on the two branches below that continue to Phase 5 — "test loop exhausted" and "QA loop exhausted" both go on to create a PR, and releasing there hands the tree away while this session is still using it.
|
|
817
922
|
|
|
818
923
|
**If spec fails:**
|
|
819
924
|
- Check issue exists and is readable
|
|
@@ -930,7 +1035,8 @@ As an orchestrator, `/fullsolve` must:
|
|
|
930
1035
|
export SEQUANT_ORCHESTRATOR=fullsolve
|
|
931
1036
|
export SEQUANT_PHASE=<current-phase>
|
|
932
1037
|
export SEQUANT_ISSUE=<issue-number>
|
|
933
|
-
|
|
1038
|
+
# Resolved in Phase 1.5 via `sequant worktree resolve` — absolute, never a glob.
|
|
1039
|
+
export SEQUANT_WORKTREE="$SEQUANT_WORKTREE"
|
|
934
1040
|
```
|
|
935
1041
|
|
|
936
1042
|
2. **State tracking** is handled automatically by the orchestrator runtime when `SEQUANT_ORCHESTRATOR` is set. Child skills defer state management to the orchestrator to avoid duplicate updates.
|
|
@@ -19,6 +19,7 @@ allowed-tools:
|
|
|
19
19
|
- mcp__context7__* # Library documentation - falls back to web search if unavailable
|
|
20
20
|
- Bash(gh issue view:*)
|
|
21
21
|
- Bash(gh issue comment:*)
|
|
22
|
+
- Bash(npx sequant worktree:*)
|
|
22
23
|
- Bash(npm test:*)
|
|
23
24
|
- Bash(npm run build:*)
|
|
24
25
|
- Bash(git diff:*)
|
|
@@ -217,22 +218,64 @@ When the marker is absent this is a byte-identical pass-through, so unmarked QA
|
|
|
217
218
|
### Step 4: Locate Feature Worktree
|
|
218
219
|
|
|
219
220
|
**If orchestrated (SEQUANT_WORKTREE is set):**
|
|
220
|
-
- Use the provided worktree path directly: `cd $SEQUANT_WORKTREE`
|
|
221
|
-
- Skip the lookup steps below
|
|
222
221
|
|
|
223
|
-
|
|
222
|
+
<!-- BEGIN: worktree-existence-guard (#899) -->
|
|
223
|
+
|
|
224
|
+
**Verify the path before you use it. Never `cd` into it unchecked.** The value
|
|
225
|
+
can name a worktree that was never created, or one belonging to a *different
|
|
226
|
+
repository* — `../worktrees/` is one flat namespace shared by every repo under
|
|
227
|
+
the same parent, and issue numbers are per-repo. A bare `cd` fails silently,
|
|
228
|
+
and this skill **writes**: unguarded, it applies fixes and commits them in the
|
|
229
|
+
main checkout, on whatever branch happens to be there.
|
|
224
230
|
|
|
225
|
-
Find the worktree for this issue:
|
|
226
231
|
```bash
|
|
227
|
-
|
|
232
|
+
npx sequant worktree verify "$SEQUANT_WORKTREE" --issue <issue-number> || {
|
|
233
|
+
echo "❌ HALT: SEQUANT_WORKTREE is not a usable worktree of this repository."
|
|
234
|
+
exit 1
|
|
235
|
+
}
|
|
236
|
+
cd "$SEQUANT_WORKTREE"
|
|
228
237
|
```
|
|
229
238
|
|
|
230
|
-
|
|
239
|
+
`verify` exits non-zero with one of these named errors. **Every one of them is
|
|
240
|
+
a halt** — report it and stop; never edit or commit from the current directory
|
|
241
|
+
as a fallback:
|
|
242
|
+
|
|
243
|
+
| Error | Meaning |
|
|
244
|
+
|-------|---------|
|
|
245
|
+
| `SEQUANT_WORKTREE_NOT_FOUND` | Path is empty, an unexpanded glob, or not an existing directory |
|
|
246
|
+
| `SEQUANT_WORKTREE_FOREIGN` | Real directory, but not a worktree of *this* repository (another project's, or stale) |
|
|
247
|
+
| `SEQUANT_WORKTREE_ISSUE_MISMATCH` | A worktree of this repo, but its branch belongs to a different issue |
|
|
248
|
+
|
|
249
|
+
Once verify passes, skip the lookup steps below.
|
|
250
|
+
|
|
251
|
+
<!-- END: worktree-existence-guard (#899) -->
|
|
252
|
+
|
|
253
|
+
**If standalone:**
|
|
254
|
+
|
|
255
|
+
<!-- BEGIN: worktree-standalone-lookup (#899) -->
|
|
256
|
+
|
|
257
|
+
Resolve the worktree through git, not the filesystem:
|
|
258
|
+
|
|
231
259
|
```bash
|
|
232
|
-
|
|
260
|
+
WORKTREE="$(npx sequant worktree resolve <issue-number>)" || {
|
|
261
|
+
echo "❌ HALT: no worktree for #<issue-number> in this repository."
|
|
262
|
+
exit 1
|
|
263
|
+
}
|
|
264
|
+
cd "$WORKTREE"
|
|
233
265
|
```
|
|
234
266
|
|
|
235
|
-
|
|
267
|
+
`sequant worktree resolve` reads `git worktree list` in the current repository
|
|
268
|
+
— which reports only *this* repo's worktrees — and selects on the **branch**
|
|
269
|
+
git reports, not the directory name.
|
|
270
|
+
|
|
271
|
+
**Do not glob `../worktrees/feature/<issue-number>-*`, and do not grep
|
|
272
|
+
`git worktree list` for the issue number.** The first matches across sibling
|
|
273
|
+
repositories, which share that directory; the second matches the printed path,
|
|
274
|
+
so it keys on the directory slug — and a slug can drift from its own branch
|
|
275
|
+
after a rename. Because this skill commits, landing in the wrong tree is
|
|
276
|
+
destructive rather than merely wrong.
|
|
277
|
+
|
|
278
|
+
<!-- END: worktree-standalone-lookup (#899) -->
|
|
236
279
|
|
|
237
280
|
### Step 5: Fix Identified Issues
|
|
238
281
|
|
|
@@ -511,12 +554,14 @@ Warning: No specific issues found in log.
|
|
|
511
554
|
Recommend running /qa <N> for fresh assessment.
|
|
512
555
|
```
|
|
513
556
|
|
|
514
|
-
**If worktree not found
|
|
557
|
+
**If worktree not found** (`sequant worktree resolve` exited non-zero):
|
|
515
558
|
```
|
|
516
|
-
Error:
|
|
517
|
-
Expected: ../worktrees/feature/<N>-*/
|
|
559
|
+
Error: no worktree in this repository has a branch for issue #<N>
|
|
518
560
|
Please run /exec <N> first to create the worktree.
|
|
519
561
|
```
|
|
562
|
+
Report the resolver's own message (`WORKTREE_NOT_FOUND` or
|
|
563
|
+
`WORKTREE_AMBIGUOUS`) rather than naming a filesystem path — the worktree is
|
|
564
|
+
identified by its branch, not by a directory under `../worktrees/`.
|
|
520
565
|
|
|
521
566
|
## Configuration
|
|
522
567
|
|
|
@@ -6,6 +6,7 @@ metadata:
|
|
|
6
6
|
author: sequant
|
|
7
7
|
version: "1.0"
|
|
8
8
|
allowed-tools:
|
|
9
|
+
- Bash(npx sequant worktree:*)
|
|
9
10
|
- Bash(git:*)
|
|
10
11
|
- Bash(gh pr:*)
|
|
11
12
|
- Bash(gh issue:*)
|
|
@@ -99,6 +100,54 @@ When processing multiple issues, determine the execution mode for validation che
|
|
|
99
100
|
|
|
100
101
|
## Workflow
|
|
101
102
|
|
|
103
|
+
### Acquire the Checkout Lock (REQUIRED)
|
|
104
|
+
|
|
105
|
+
`/merger` runs branch-mutating git **in the main checkout**: `git checkout main`
|
|
106
|
+
for the baseline in Step 0, then `git checkout -b integrate/…` and `git merge` on
|
|
107
|
+
the integration path (Step 5), plus the mid-workflow `git checkout main` in
|
|
108
|
+
Steps 6–7. Those verbs are global to the working tree, so before the first one
|
|
109
|
+
you must claim the checkout lock (#901), exactly as `/fullsolve` does in its
|
|
110
|
+
Phase 0.3. Without it a session on another issue can interleave a `git checkout`
|
|
111
|
+
and land a merge on the wrong HEAD; and because the guard refuses a *non-holder*,
|
|
112
|
+
an unparticipating `/merger` is itself refused when someone else holds the tree.
|
|
113
|
+
|
|
114
|
+
`/merger` operates on several issues, so identify the holder by the **first issue
|
|
115
|
+
number** passed to it and use that same `<first-issue>` on the acquire and on
|
|
116
|
+
*every* release below — acquire and release must name the same holder or the
|
|
117
|
+
release is refused against your own lock (#906). Substitute the literal number
|
|
118
|
+
(e.g. `--issue=10` for `/merger 10 12`); a shell variable is invisible to the
|
|
119
|
+
`pre-tool.sh` guard, which reads the command text before the shell expands it.
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
# Claim the shared working tree before the first branch-mutating verb.
|
|
123
|
+
npx sequant locks checkout acquire \
|
|
124
|
+
--issue=<first-issue> \
|
|
125
|
+
--command="/merger <issue-numbers>" \
|
|
126
|
+
--skip-pid-check || true
|
|
127
|
+
# Belt-and-suspenders for a parent that launched us with it; the guard's real
|
|
128
|
+
# binding comes from watching the acquire command above (#906).
|
|
129
|
+
export SEQUANT_ISSUE=<first-issue>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**`--issue` is mandatory** (#906): it is what proves you are the holder on
|
|
133
|
+
release, since the acquiring shell's PID is gone by the next block
|
|
134
|
+
(`--skip-pid-check`). Stale recovery is therefore age-based only (the 6h
|
|
135
|
+
`SEQUANT_SKILL_LOCK_TTL_MS` and the 24h `SEQUANT_MAX_LOCK_AGE_MS` ceiling), not
|
|
136
|
+
same-host dead-PID recovery.
|
|
137
|
+
|
|
138
|
+
**Release contract:** release the checkout lock —
|
|
139
|
+
`npx sequant locks checkout release --issue=<first-issue> || true` — on **every**
|
|
140
|
+
path that exits `/merger`: happy-path completion (Step 8), the regression-gate
|
|
141
|
+
halt (Step 7), and any error/abort in "Error Handling". Do **NOT** release on a
|
|
142
|
+
branch that *continues* to a later branch-mutating verb (the #906 distinction):
|
|
143
|
+
the integration `git merge` in Step 5 and the `git checkout main` in Steps 6–7
|
|
144
|
+
run under the lock and must stay protected until the run ends.
|
|
145
|
+
|
|
146
|
+
**Orchestrator/MCP mode:** when `SEQUANT_ORCHESTRATOR` is set, every `locks
|
|
147
|
+
checkout` action is a no-op (exit 0, no file touched) and the `pre-tool.sh`
|
|
148
|
+
checkout guard stands down, so the acquire/release calls are safe to run
|
|
149
|
+
unconditionally.
|
|
150
|
+
|
|
102
151
|
### Step 0: Baseline Capture (REQUIRED)
|
|
103
152
|
|
|
104
153
|
**Purpose:** Capture build error count and test pass/fail counts on main **before** any merge, so post-merge results can be compared to detect regressions.
|
|
@@ -157,8 +206,9 @@ fi
|
|
|
157
206
|
For each issue specified:
|
|
158
207
|
|
|
159
208
|
```bash
|
|
160
|
-
# Find the worktree for the issue
|
|
161
|
-
|
|
209
|
+
# Find the worktree for the issue. Resolve by branch (#899/#904) — grepping
|
|
210
|
+
# for "feature/$ISSUE" is a prefix match, so ISSUE=89 also hits feature/899-*.
|
|
211
|
+
worktree_path=$(npx sequant worktree resolve "$ISSUE") || echo "No worktree for #$ISSUE"
|
|
162
212
|
|
|
163
213
|
# Check PR status
|
|
164
214
|
gh pr list --head "feature/$ISSUE-*" --json number,state,title
|
|
@@ -222,10 +272,16 @@ gh pr merge <PR_NUMBER> --squash
|
|
|
222
272
|
|
|
223
273
|
# Only clean up worktree AFTER merge succeeds
|
|
224
274
|
# If merge fails, the worktree is preserved so work isn't lost
|
|
225
|
-
|
|
226
|
-
|
|
275
|
+
#
|
|
276
|
+
# Resolve by BRANCH, never by grepping the printed path (#899/#904): the path
|
|
277
|
+
# line contains the directory slug, so `grep "feature/$ISSUE"` with ISSUE=89
|
|
278
|
+
# also matches `feature/899-...` — and the next line force-removes whatever it
|
|
279
|
+
# matched. `worktree resolve` matches the branch's issue number exactly, and
|
|
280
|
+
# exits non-zero rather than guessing when nothing (or more than one) matches.
|
|
281
|
+
if worktree_path=$(npx sequant worktree resolve "$ISSUE"); then
|
|
282
|
+
branch=$(git -C "$worktree_path" rev-parse --abbrev-ref HEAD)
|
|
227
283
|
git worktree remove "$worktree_path" --force
|
|
228
|
-
git branch -D "
|
|
284
|
+
git branch -D "$branch" 2>/dev/null || true
|
|
229
285
|
fi
|
|
230
286
|
|
|
231
287
|
# Delete remote branch (previously handled by --delete-branch)
|
|
@@ -285,17 +341,23 @@ Read(file_path=".sequant/state.json")
|
|
|
285
341
|
**After each successful merge, ensure the worktree is removed:**
|
|
286
342
|
|
|
287
343
|
```bash
|
|
288
|
-
# Find and remove worktree for the issue
|
|
289
|
-
|
|
290
|
-
|
|
344
|
+
# Find and remove worktree for the issue.
|
|
345
|
+
# Resolve by branch, not by grepping the printed path — see the note in the
|
|
346
|
+
# clean-merge block above. This one force-removes, so a slug collision here
|
|
347
|
+
# destroys an unrelated issue's worktree.
|
|
348
|
+
if worktree_path=$(npx sequant worktree resolve "$ISSUE"); then
|
|
291
349
|
echo "Removing worktree: $worktree_path"
|
|
292
350
|
git worktree remove "$worktree_path" --force
|
|
293
351
|
else
|
|
294
352
|
echo "No worktree found for #$ISSUE (already cleaned up)"
|
|
295
353
|
fi
|
|
296
354
|
|
|
297
|
-
# Verify worktree removal
|
|
298
|
-
|
|
355
|
+
# Verify worktree removal — resolve must now fail for this issue.
|
|
356
|
+
if npx sequant worktree resolve "$ISSUE" >/dev/null 2>&1; then
|
|
357
|
+
echo "WARNING: Worktree still exists"
|
|
358
|
+
else
|
|
359
|
+
echo "✅ Worktree removed"
|
|
360
|
+
fi
|
|
299
361
|
```
|
|
300
362
|
|
|
301
363
|
**Why this matters:** Leftover worktrees waste disk space and can cause confusion when re-running `sequant run` on the same issues. The state guard (#305) prevents re-execution, but the worktree should still be cleaned up.
|
|
@@ -387,6 +449,9 @@ if [[ "$REGRESSION_DETECTED" == "true" ]]; then
|
|
|
387
449
|
echo "⚠️ REGRESSION DETECTED but --force flag set. Proceeding with merge."
|
|
388
450
|
echo "⚠️ Acknowledgment: Merging despite $BUILD_DELTA new build error(s) and $TEST_FAIL_DELTA new test failure(s)."
|
|
389
451
|
else
|
|
452
|
+
# Terminal halt — release the checkout lock before stopping (#901/#906).
|
|
453
|
+
# This branch does NOT continue to Step 8, so the release lives here.
|
|
454
|
+
npx sequant locks checkout release --issue=<first-issue> || true
|
|
390
455
|
echo "❌ REGRESSION DETECTED — merge is blocked."
|
|
391
456
|
echo ""
|
|
392
457
|
echo "New build errors: $BUILD_DELTA"
|
|
@@ -460,6 +525,21 @@ git diff HEAD~1 --stat
|
|
|
460
525
|
|
|
461
526
|
**Important:** Regression detection does NOT trigger automatic rollback. It reports for human decision-making.
|
|
462
527
|
|
|
528
|
+
### Step 8: Release the Checkout Lock (REQUIRED)
|
|
529
|
+
|
|
530
|
+
The merge is complete. Release the working-tree lock so other sessions can run
|
|
531
|
+
branch-mutating git again (#901). Run this on the happy path — a successful merge
|
|
532
|
+
and smoketest, or the `--force` proceed past a regression. It is the counterpart
|
|
533
|
+
to the acquire in "Acquire the Checkout Lock" above.
|
|
534
|
+
|
|
535
|
+
```bash
|
|
536
|
+
npx sequant locks checkout release --issue=<first-issue> || true
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
On a *failure* exit instead, the release is done at the halt site — the
|
|
540
|
+
regression gate (Step 7) or "Error Handling" — never both, so exactly one release
|
|
541
|
+
runs per invocation.
|
|
542
|
+
|
|
463
543
|
## Dependency Detection
|
|
464
544
|
|
|
465
545
|
Parse dependencies from issue body or comments:
|
|
@@ -579,6 +659,14 @@ gh pr view <PR_NUMBER> --json baseRefName,body | \
|
|
|
579
659
|
|
|
580
660
|
## Error Handling
|
|
581
661
|
|
|
662
|
+
**Before halting on any failure below, release the checkout lock** (#901/#906) —
|
|
663
|
+
the acquire in "Acquire the Checkout Lock" claimed the tree, and every failure
|
|
664
|
+
here is a terminal exit that must hand it back before stopping:
|
|
665
|
+
|
|
666
|
+
```bash
|
|
667
|
+
npx sequant locks checkout release --issue=<first-issue> || true
|
|
668
|
+
```
|
|
669
|
+
|
|
582
670
|
**If validation fails:**
|
|
583
671
|
- Report which issues failed validation
|
|
584
672
|
- Suggest corrective actions
|
|
@@ -6,6 +6,7 @@ metadata:
|
|
|
6
6
|
author: sequant
|
|
7
7
|
version: "1.0"
|
|
8
8
|
allowed-tools:
|
|
9
|
+
- Bash(npx sequant worktree:*)
|
|
9
10
|
- Bash(npm test:*)
|
|
10
11
|
- Bash(npm run build:*)
|
|
11
12
|
- Bash(git diff:*)
|
|
@@ -362,15 +363,64 @@ Include in QA output when branch is stale:
|
|
|
362
363
|
**QA Phase:** Review code in the feature worktree.
|
|
363
364
|
|
|
364
365
|
**If orchestrated (SEQUANT_WORKTREE is set):**
|
|
365
|
-
|
|
366
|
-
|
|
366
|
+
|
|
367
|
+
<!-- BEGIN: worktree-existence-guard (#899) -->
|
|
368
|
+
|
|
369
|
+
**Verify the path before you use it. Never `cd` into it unchecked.** The value
|
|
370
|
+
can name a worktree that was never created, or one belonging to a *different
|
|
371
|
+
repository* — `../worktrees/` is one flat namespace shared by every repo under
|
|
372
|
+
the same parent, and issue numbers are per-repo. A bare `cd` fails silently and
|
|
373
|
+
leaves you reviewing the main checkout while reporting on the feature branch.
|
|
374
|
+
|
|
375
|
+
```bash
|
|
376
|
+
npx sequant worktree verify "$SEQUANT_WORKTREE" --issue <issue-number> || {
|
|
377
|
+
echo "❌ HALT: SEQUANT_WORKTREE is not a usable worktree of this repository."
|
|
378
|
+
exit 1
|
|
379
|
+
}
|
|
380
|
+
cd "$SEQUANT_WORKTREE"
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
`verify` exits non-zero with one of these named errors. **Every one of them is
|
|
384
|
+
a halt** — report it and stop; do not fall back to reviewing the current
|
|
385
|
+
directory, and do not emit a verdict for a tree you did not actually read:
|
|
386
|
+
|
|
387
|
+
| Error | Meaning |
|
|
388
|
+
|-------|---------|
|
|
389
|
+
| `SEQUANT_WORKTREE_NOT_FOUND` | Path is empty, an unexpanded glob, or not an existing directory |
|
|
390
|
+
| `SEQUANT_WORKTREE_FOREIGN` | Real directory, but not a worktree of *this* repository (another project's, or stale) |
|
|
391
|
+
| `SEQUANT_WORKTREE_ISSUE_MISMATCH` | A worktree of this repo, but its branch belongs to a different issue |
|
|
392
|
+
|
|
393
|
+
Once verify passes, skip step 1 below (worktree location provided by orchestrator).
|
|
394
|
+
|
|
395
|
+
<!-- END: worktree-existence-guard (#899) -->
|
|
367
396
|
|
|
368
397
|
**If standalone:**
|
|
369
398
|
|
|
399
|
+
<!-- BEGIN: worktree-standalone-lookup (#899) -->
|
|
400
|
+
|
|
370
401
|
1. **Locate the worktree:**
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
402
|
+
|
|
403
|
+
The worktree should already exist from the execution phase (`/exec`).
|
|
404
|
+
Resolve it through git, not the filesystem:
|
|
405
|
+
|
|
406
|
+
```bash
|
|
407
|
+
WORKTREE="$(npx sequant worktree resolve <issue-number>)" || {
|
|
408
|
+
echo "No worktree for #<issue-number> in this repository."
|
|
409
|
+
# Fall through to the "No Worktree Found" section below.
|
|
410
|
+
}
|
|
411
|
+
cd "$WORKTREE"
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
`sequant worktree resolve` reads `git worktree list` in the current
|
|
415
|
+
repository — which reports only *this* repo's worktrees — and selects on the
|
|
416
|
+
**branch** git reports, not the directory name.
|
|
417
|
+
|
|
418
|
+
**Do not glob `../worktrees/feature/<issue-number>-*` to find it.** That
|
|
419
|
+
directory is shared by every sibling repository, so a name match may belong
|
|
420
|
+
to another project; and a directory slug can drift from its own branch after
|
|
421
|
+
a rename, so it proves nothing about which branch you would review.
|
|
422
|
+
|
|
423
|
+
<!-- END: worktree-standalone-lookup (#899) -->
|
|
374
424
|
|
|
375
425
|
2. **Check implementation status:**
|
|
376
426
|
- Navigate to worktree: `cd <worktree-path>`
|
|
@@ -425,7 +475,10 @@ If no feature worktree exists (work was done directly on main):
|
|
|
425
475
|
|
|
426
476
|
```bash
|
|
427
477
|
# 1. Check for worktree (indicates work may have started)
|
|
428
|
-
|
|
478
|
+
# Resolve by branch, not by grepping the printed path (#899/#904): the path
|
|
479
|
+
# line carries the directory slug, so a bare number match is both cross-issue
|
|
480
|
+
# (89 matches 899-...) and slug-based rather than branch-based.
|
|
481
|
+
worktree_path=$(npx sequant worktree resolve "<issue-number>" 2>/dev/null || true)
|
|
429
482
|
|
|
430
483
|
# 2. Check for commits on feature branch (vs main) — include ALL file types
|
|
431
484
|
commits_exist=$(git log --oneline origin/main..HEAD 2>/dev/null | head -1)
|