@gobing-ai/spur 0.3.98 → 0.4.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/marketplace.json +1 -1
- package/config/config.global.yaml +17 -0
- package/config/decisions/task-pipeline.yaml +55 -0
- package/config/plugin-scripts.json +15 -0
- package/config/rules/boundary/sp-runtime-path.yaml +2 -0
- package/config/rules/quality/coverage-gate.yaml +5 -3
- package/config/rules/strict/runtime-boundaries.yaml +11 -0
- package/config/rules/structure/protected-files.yaml +3 -0
- package/config/templates/task/brainstorm.md +1 -1
- package/config/templates/task/feature-impl.md +1 -1
- package/config/templates/task/issue.md +1 -1
- package/config/templates/task/meta.md +1 -1
- package/config/templates/task/review.md +1 -1
- package/config/templates/task/standard.md +1 -1
- package/config/workflows/task-pipeline.yaml +178 -18
- package/package.json +9 -9
- package/plugins/sp/README.md +14 -1
- package/plugins/sp/agents/super-reviewer.md +25 -11
- package/plugins/sp/commands/dev-changelog.md +5 -4
- package/plugins/sp/commands/dev-review-session.md +12 -7
- package/plugins/sp/commands/dev-run.md +5 -2
- package/plugins/sp/commands/dev-runall.md +5 -2
- package/plugins/sp/hooks/agent-lifecycle.ts +155 -0
- package/plugins/sp/hooks/fleet-guest-stop.ts +180 -0
- package/plugins/sp/hooks/hooks.json +45 -1
- package/plugins/sp/hooks/pi/guard-extension.ts +21 -0
- package/plugins/sp/lib/history-anatomy.generated.mjs +5 -0
- package/plugins/sp/lib/idea-handoff.generated.mjs +68 -10
- package/plugins/sp/lib/inline-run.generated.d.mts +1 -0
- package/plugins/sp/lib/inline-run.generated.mjs +83 -20
- package/plugins/sp/lib/residual-scan.generated.d.mts +5 -0
- package/plugins/sp/lib/residual-scan.generated.mjs +45 -5
- package/plugins/sp/lib/transcript.ts +160 -0
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/scripts/history-anatomy-cache.mjs +5 -0
- package/plugins/sp/scripts/inline-run-setup.mjs +21 -20
- package/plugins/sp/scripts/inline-run-setup.ts +25 -31
- package/plugins/sp/scripts/persist-out-check.mjs +164 -0
- package/plugins/sp/scripts/persist-out-check.ts +185 -0
- package/plugins/sp/scripts/residual-scan.mjs +52 -15
- package/plugins/sp/scripts/residual-scan.ts +11 -11
- package/plugins/sp/scripts/run-summary.mjs +286 -0
- package/plugins/sp/scripts/run-summary.ts +241 -0
- package/plugins/sp/scripts/session-timeline.mjs +234 -0
- package/plugins/sp/scripts/session-timeline.ts +152 -0
- package/plugins/sp/scripts/task-diffstat.mjs +17 -5
- package/plugins/sp/scripts/task-diffstat.ts +25 -4
- package/plugins/sp/skills/code-verification/SKILL.md +8 -0
- package/plugins/sp/skills/code-verification/references/verdict-schema.md +17 -1
- package/plugins/sp/skills/fleet-join/SKILL.md +63 -0
- package/plugins/sp/skills/session-review/SKILL.md +46 -24
- package/plugins/sp/skills/spur-cli/SKILL.md +1 -0
- package/plugins/sp/skills/spur-cli/references/agent.md +108 -6
- package/plugins/sp/skills/spur-cli/references/decision.md +97 -0
- package/plugins/sp/skills/spur-cli/references/tasks/verbs.md +22 -0
- package/plugins/sp/skills/spur-composer/SKILL.md +27 -1
- package/plugins/sp/skills/spur-dev/references/ac-style-guide.md +40 -7
- package/plugins/sp/skills/spur-dev/references/dev-operations.md +28 -16
- package/plugins/sp/skills/spur-dev/references/execution-batch.md +278 -42
- package/plugins/sp/skills/spur-dev/references/execution-workflow.md +17 -1
- package/plugins/sp/skills/spur-dev/references/flag-glossary.md +16 -4
- package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +139 -10
- package/plugins/sp/skills/spur-dev/references/planning-workflow.md +6 -0
- package/schemas/decision-catalog.schema.json +226 -0
- package/schemas/spur-config.schema.json +25 -0
- package/spur.js +8856 -2564
- package/web/_astro/BoardApp.Bzr4OXrW.js +324 -0
- package/web/_astro/{BoardApp.Dflq5a8x.js → BoardApp.VeoxxLBy.js} +1 -1
- package/web/_astro/{TaskDetail.CK7oXddP.js → TaskDetail.LAyR_Pvn.js} +1 -1
- package/web/_astro/{arc.BIqkDATc.js → arc.f4e0OYLg.js} +1 -1
- package/web/_astro/{architectureDiagram-3BPJPVTR.D0B3SvWv.js → architectureDiagram-3BPJPVTR.SSIvxi-Z.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.C2YyKQmL.js → blockDiagram-GPEHLZMM.DSXIHOFf.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.DmtTYrt_.js → c4Diagram-AAUBKEIU.DG9Ubu85.js} +1 -1
- package/web/_astro/channel.CUFdtWhm.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.HsRXbh7l.js → chunk-2J33WTMH.C7QU88Qh.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.obE_P-R3.js → chunk-4BX2VUAB.26Z94xBj.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.CjWTF5mz.js → chunk-55IACEB6.DYRiBkqw.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.DMo799Ax.js → chunk-727SXJPM.D5Vfv5pe.js} +1 -1
- package/web/_astro/{chunk-AQP2D5EJ.-cE6JOKZ.js → chunk-AQP2D5EJ.DXHV47bN.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.D6Rw6HIk.js → chunk-FMBD7UC4.D1suAfOs.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM.d5rBD73Z.js → chunk-ND2GUHAM.D6BmdHEY.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.r3ox40Q3.js → chunk-QZHKN3VN.rnPU-Ak-.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.CGGbvjjJ.js → classDiagram-4FO5ZUOK.CvEfXIo1.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.CGGbvjjJ.js → classDiagram-v2-Q7XG4LA2.CvEfXIo1.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.CmKtPd4l.js → cose-bilkent-S5V4N54A.DkMisLSm.js} +1 -1
- package/web/_astro/{cynefin-OW5HDTMX.QNg5GXs8.js → cynefin-OW5HDTMX.C77Q-pF6.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.B1SabsjC.js → dagre-BM42HDAG.YaV8ce27.js} +1 -1
- package/web/_astro/{diagram-2AECGRRQ.RtDbNj89.js → diagram-2AECGRRQ.CUyGEKge.js} +1 -1
- package/web/_astro/{diagram-5GNKFQAL.IzZpuM-Y.js → diagram-5GNKFQAL.CRgd8dgj.js} +1 -1
- package/web/_astro/{diagram-KO2AKTUF.DPTF_fgv.js → diagram-KO2AKTUF.Bmzys9hq.js} +1 -1
- package/web/_astro/{diagram-LMA3HP47.CCyza39P.js → diagram-LMA3HP47.BvmJY4Lw.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.BXAi8zcZ.js → diagram-OG6HWLK6.0hEhD30d.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.DUDX7_MR.js → erDiagram-TEJ5UH35.Bjt8lrnm.js} +1 -1
- package/web/_astro/{flowDiagram-I6XJVG4X.C63QTh3N.js → flowDiagram-I6XJVG4X.CJZTjRDV.js} +1 -1
- package/web/_astro/{ganttDiagram-6RSMTGT7.CnlZJSwG.js → ganttDiagram-6RSMTGT7._VqoVafc.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.CTqIrOPC.js → gitGraphDiagram-PVQCEYII.CEDb7tVo.js} +1 -1
- package/web/_astro/index.Dg_3bOaJ.css +1 -0
- package/web/_astro/{infoDiagram-5YYISTIA.BYMiDFh9.js → infoDiagram-5YYISTIA.CgwifPpK.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.CRiQmI-z.js → ishikawaDiagram-YF4QCWOH.B99wx2J1.js} +1 -1
- package/web/_astro/{journeyDiagram-JHISSGLW.YQgWBsEU.js → journeyDiagram-JHISSGLW.2hRwJbEo.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.1ECnrrK1.js → kanban-definition-UN3LZRKU.BahJkKNC.js} +1 -1
- package/web/_astro/{linear.DzVA71yT.js → linear.DguUUe6V.js} +1 -1
- package/web/_astro/{mermaid.core.BK7P2Sr_.js → mermaid.core.CvgfmqVr.js} +4 -4
- package/web/_astro/{mindmap-definition-RKZ34NQL.BvXyWZK7.js → mindmap-definition-RKZ34NQL.B3qEA3x9.js} +1 -1
- package/web/_astro/{pieDiagram-4H26LBE5.BiXk1TW3.js → pieDiagram-4H26LBE5.FlWA-_ru.js} +1 -1
- package/web/_astro/{quadrantDiagram-W4KKPZXB.krvBH8lh.js → quadrantDiagram-W4KKPZXB.9bVn35X7.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.ih1gL25c.js → requirementDiagram-4Y6WPE33.ChykIhct.js} +1 -1
- package/web/_astro/{sankeyDiagram-5OEKKPKP.CpUxkv4u.js → sankeyDiagram-5OEKKPKP.Z2Y3hkYb.js} +1 -1
- package/web/_astro/{sequenceDiagram-3UESZ5HK.clAfeN7y.js → sequenceDiagram-3UESZ5HK.J7JsHWDt.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.DpXWb8-m.js → stateDiagram-AJRCARHV.DAPbiCSS.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.ClZb9GIH.js → stateDiagram-v2-BHNVJYJU.D2jvSi0q.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.BLcX5dh7.js → timeline-definition-PNZ67QCA.psW2ci29.js} +1 -1
- package/web/_astro/{vennDiagram-CIIHVFJN.oGgpKMpx.js → vennDiagram-CIIHVFJN.DlILYVmg.js} +1 -1
- package/web/_astro/{wardleyDiagram-YWT4CUSO.BPHtMtt-.js → wardleyDiagram-YWT4CUSO.xNWwLJnn.js} +1 -1
- package/web/_astro/{xychartDiagram-2RQKCTM6.BNZOPYSk.js → xychartDiagram-2RQKCTM6.EU_BuH7V.js} +1 -1
- package/web/board-runtime.json +1 -1
- package/web/index.html +2 -2
- package/web/_astro/BoardApp.CYbe-OTb.js +0 -322
- package/web/_astro/channel.JuKVyuhZ.js +0 -1
- package/web/_astro/index.CbNz17Sx.css +0 -1
|
@@ -424,7 +424,8 @@ one or more tasks sit on an operator question (escalate, 0933) or an approval ga
|
|
|
424
424
|
(cycle / unknown selector) — emit a structured report. The report is the orchestrator's sole
|
|
425
425
|
output; it does not mutate the corpus (the pipeline's `record` step already wrote per-task
|
|
426
426
|
results). A `paused` task is non-terminal — the report marks it `paused` (never `done`) and names
|
|
427
|
-
the resume command.
|
|
427
|
+
the resume command. Unless `--no-summary`, follow it with the measured
|
|
428
|
+
[execution summary](dev-operations.md#execution-summary), one `--progress <wbs>=<file>` per attempted run.
|
|
428
429
|
|
|
429
430
|
```
|
|
430
431
|
## Batch Report — <selector>
|
|
@@ -626,6 +627,20 @@ resolution runs, the loop executes with the worktree as process cwd, and a termi
|
|
|
626
627
|
retains after the batch report is emitted. Steps 1–5 themselves are not modified — only their cwd
|
|
627
628
|
differs.
|
|
628
629
|
|
|
630
|
+
**Per-call tree pinning (task 1058).** A host shell call starts in an arbitrary directory and
|
|
631
|
+
inherits no cwd from the previous call, so "the loop runs with the worktree as process cwd" is a
|
|
632
|
+
bootstrap convenience, never a carried guarantee. Every host command boundary in this lifecycle —
|
|
633
|
+
corpus reads/writes, the WT-3b commit, WT-4's merge and evidence persistence, the WT-4a
|
|
634
|
+
persist-out — re-selects its intended tree within the same call via the canonical per-call pin
|
|
635
|
+
protocol ([inline-pipeline-driver.md](inline-pipeline-driver.md) § "Per-call execution-tree
|
|
636
|
+
pin"): `cd --` to the quoted absolute tree, verify `pwd -P` against the recorded physical tree,
|
|
637
|
+
verify Git top-level and expected branch, then execute the already-resolved Spur invocation. The
|
|
638
|
+
identity is consumed, not re-derived: it is the WT-3 marker's confirmed `path` + `branch` (or the
|
|
639
|
+
resolved worktree's physical path + checked-out branch in reuse mode). The one deliberate tree
|
|
640
|
+
change is WT-4: the terminal merge actions select the **invoking** tree, not the execution tree —
|
|
641
|
+
and pin it explicitly the same way. FF ancestry, marker and cleanup sequencing remain owned by
|
|
642
|
+
1059.
|
|
643
|
+
|
|
629
644
|
**Portability (R10).** Use portable `git worktree` commands only. Do **not** depend on the Claude
|
|
630
645
|
Code `EnterWorktree`/`ExitWorktree` tools — the `sp` plugin ships to Codex, Gemini CLI, pi, omp, and
|
|
631
646
|
OpenCode. The underlying git mechanics (create / list / remove / prune, sibling-directory naming,
|
|
@@ -730,6 +745,34 @@ existing batch loop (Steps 1–5) with the worktree as process cwd. `spur workfl
|
|
|
730
745
|
from the process (`apps/cli/src/commands/workflow.ts:124`), so no CLI change is needed — `cd` into
|
|
731
746
|
the worktree directory before launching the loop.
|
|
732
747
|
|
|
748
|
+
#### Evidence staging before the first task (create and reuse mode)
|
|
749
|
+
|
|
750
|
+
`.spur/memory/evidence/` and `.spur/run/` are **untracked per-tree** planes, so a fresh worktree
|
|
751
|
+
starts with neither while the invoking tree holds every earlier task's verdict. Both
|
|
752
|
+
`preflightFeature` (`plugins/sp/scripts/wrapup-steps.ts` → `spur feature check --strict --as done`
|
|
753
|
+
with cwd = the worktree) and `runMetrics` resolve verdict artifacts relative to the tree they run
|
|
754
|
+
in, so without this step an already-`done` linked task reads as missing evidence
|
|
755
|
+
(`L4.evidence-not-recoverable`, `L4.scenario-unverified`) and its metrics row degrades to
|
|
756
|
+
`UNKNOWN`.
|
|
757
|
+
|
|
758
|
+
Stage the invoking tree's verdict artifacts into the worktree **before the first task runs** —
|
|
759
|
+
from the invoking tree (cwd), with `$WT` the resolved absolute worktree path. `cp -n` is
|
|
760
|
+
load-bearing: reuse mode may be re-entering a tree that already owns divergence-checked evidence,
|
|
761
|
+
and this step must never clobber it. Only `*-verdict.json` travels in each direction; receipts and
|
|
762
|
+
other scratch artifacts are not staged, so WT-4a's identity classification is unaffected.
|
|
763
|
+
|
|
764
|
+
```bash
|
|
765
|
+
# Run from the INVOKING tree; $WT is the worktree (create- or reuse-mode) absolute path.
|
|
766
|
+
for d in .spur/memory/evidence .spur/run; do
|
|
767
|
+
mkdir -p "$WT/$d"
|
|
768
|
+
for f in "$d"/*-verdict.json; do [ -f "$f" ] && cp -n "$f" "$WT/$d/"; done
|
|
769
|
+
done
|
|
770
|
+
```
|
|
771
|
+
|
|
772
|
+
The position relative to the dependency install is immaterial (this is a plain copy into an
|
|
773
|
+
untracked plane); the position relative to the **first task** is not — a preflight or metrics read
|
|
774
|
+
that runs before this step sees an empty evidence plane.
|
|
775
|
+
|
|
733
776
|
#### Name resolution (`--worktree <name>`)
|
|
734
777
|
|
|
735
778
|
<a id="name-resolution---worktree-name"></a>
|
|
@@ -765,6 +808,10 @@ the tree's own source:
|
|
|
765
808
|
|
|
766
809
|
cd "<worktree>" && bun apps/cli/src/index.ts task check <wbs> --json
|
|
767
810
|
|
|
811
|
+
Run that as a per-call pin subshell (task 1058) — `cd --` plus the `pwd -P` / git-top-level /
|
|
812
|
+
branch identity checks of the canonical protocol (inline-pipeline-driver.md § "Per-call
|
|
813
|
+
execution-tree pin") — rather than relying on a persistent `cd` between host tool calls.
|
|
814
|
+
|
|
768
815
|
Confirm isolation by making a distinctive change in the worktree and checking that
|
|
769
816
|
the command reflects it.
|
|
770
817
|
|
|
@@ -832,13 +879,19 @@ Reuse mode resolves the marker by the resolved worktree's `path` (not by `comman
|
|
|
832
879
|
### WT-3b — Commit the batch's writes on `$BRANCH` (task 0701 R1)
|
|
833
880
|
|
|
834
881
|
Before any terminal action, commit the batch's corpus writes **on `$BRANCH`, inside the
|
|
835
|
-
worktree** — including the generated task files under `docs/tasks*/` and the kanban index
|
|
882
|
+
worktree** — including the generated task files under `docs/tasks*/` and the kanban index. The
|
|
883
|
+
commit runs as a per-call pinned subshell (task 1058): it selects the execution tree inside the
|
|
884
|
+
same call, verifies the WT-3 identity, and never relies on a persistent `cd` or a trailing
|
|
885
|
+
`cd -` to restore the caller's directory:
|
|
836
886
|
|
|
837
887
|
```bash
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
git
|
|
841
|
-
|
|
888
|
+
(
|
|
889
|
+
cd -- "../<worktree-dir>" || { echo "tree missing: expected ../<worktree-dir>" >&2; exit 1; }
|
|
890
|
+
[ "$(git branch --show-current)" = "$BRANCH" ] \
|
|
891
|
+
|| { echo "branch mismatch: expected $BRANCH, got $(git branch --show-current)" >&2; exit 1; }
|
|
892
|
+
git add <files-the-batch-wrote>
|
|
893
|
+
git commit -m "<type>(<scope>): <command> <selector> batch writes"
|
|
894
|
+
)
|
|
842
895
|
```
|
|
843
896
|
|
|
844
897
|
The FF-only git merge carries only commits — uncommitted writes in the worktree would be left
|
|
@@ -855,18 +908,66 @@ only what it created*):
|
|
|
855
908
|
|
|
856
909
|
```bash
|
|
857
910
|
# Run these from the main tree (not inside the worktree) - you merge the worktree branch
|
|
858
|
-
# back onto the base ref there:
|
|
859
|
-
|
|
911
|
+
# back onto the base ref there. Per-call tree pinning (task 1058, § above): every command
|
|
912
|
+
# below selects the INVOKING tree (where the WT-3 marker lives), re-pinned with the canonical
|
|
913
|
+
# per-call pin protocol in its own subshell — never the execution tree. The whole sequence is
|
|
914
|
+
# fail-stop: checkout, guard, ancestry check, merge, landed verify, persistence, and cleanup
|
|
915
|
+
# each stop explicitly. Every tip/ancestry read uses local refs — no fetch runs anywhere:
|
|
916
|
+
MARKER=".spur/run/worktree-<marker-id>.json"
|
|
917
|
+
git checkout "$BASE_REF" \
|
|
918
|
+
|| { echo "WT-4 halt: checkout $BASE_REF failed - nothing merged, marker stays \"active\"" >&2; exit 1; } # -> WT-5
|
|
860
919
|
# Guard (task 0701 R1): a zero-commit branch makes the FF-only git merge exit 0
|
|
861
|
-
# ("Already up to date") while merging nothing — the
|
|
920
|
+
# ("Already up to date") while merging nothing — the lines below would then delete
|
|
862
921
|
# the worktree holding the only copy of the batch's writes. Refuse instead:
|
|
863
|
-
[ "$(git rev-list --count "$
|
|
864
|
-
|| { echo "halt: branch carries no commits - nothing to merge" >&2;
|
|
922
|
+
[ "$(git rev-list --count "$BASE_REF..$BRANCH")" -gt 0 ] \
|
|
923
|
+
|| { echo "WT-4 halt: branch carries no commits - nothing to merge" >&2; exit 1; } # -> WT-5, marker stays "active"
|
|
924
|
+
# Capture the batch tip BEFORE any cleanup or branch work: every later step (landed verify,
|
|
925
|
+
# mergeCommit, retained report) reads this captured value — a removed branch is never queried.
|
|
926
|
+
BATCH_TIP="$(git rev-parse "$BRANCH")" \
|
|
927
|
+
|| { echo "WT-4 halt: cannot read $BRANCH" >&2; exit 1; }
|
|
928
|
+
# Fresh LOCAL base tip, re-read now; the marker's baseSha may be stale and never governs this
|
|
929
|
+
# decision (an advanced-but-ancestor base still FF-merges, a divergent base halts):
|
|
930
|
+
BASE_TIP="$(git rev-parse "$BASE_REF")" \
|
|
931
|
+
|| { echo "WT-4 halt: cannot read $BASE_REF" >&2; exit 1; }
|
|
932
|
+
# Ancestry check immediately before the sole mutation (the FF merge below). Command/read
|
|
933
|
+
# errors are distinguished from ordinary non-ancestry; both fail closed:
|
|
934
|
+
ANCESTRY_RC=0
|
|
935
|
+
git merge-base --is-ancestor "$BASE_TIP" "$BATCH_TIP" || ANCESTRY_RC=$?
|
|
936
|
+
[ "$ANCESTRY_RC" -gt 1 ] \
|
|
937
|
+
&& { echo "WT-4 halt: ancestry check could not run (git exit $ANCESTRY_RC) - failing closed" >&2; exit 1; }
|
|
938
|
+
[ "$ANCESTRY_RC" -eq 1 ] && {
|
|
939
|
+
echo "WT-4 halt: divergent - batch tip $BATCH_TIP vs base tip $BASE_TIP" >&2
|
|
940
|
+
git log --oneline "$BASE_TIP..$BATCH_TIP" >&2 # batch-side divergent commits
|
|
941
|
+
git log --oneline "$BATCH_TIP..$BASE_TIP" >&2 # base-side divergent commits
|
|
942
|
+
exit 1; } # -> WT-5: tree + branch retained, marker stays "active"
|
|
865
943
|
git merge --ff-only "$BRANCH" # FF-only: never rebase, merge-commit, or resolve conflicts
|
|
866
|
-
|
|
944
|
+
[ $? -eq 0 ] \
|
|
945
|
+
|| { echo "WT-4 halt: FF-merge failed - the concurrent-writer race survives every precheck; marker stays \"active\"" >&2; exit 1; } # -> WT-5
|
|
946
|
+
# Landed verification before anything else runs: the captured BATCH_TIP must be an ancestor of
|
|
947
|
+
# the base ref as it now stands (base tips advancing again later stay valid — this binds the
|
|
948
|
+
# merge result, not the end state):
|
|
949
|
+
LANDED_BASE_TIP="$(git rev-parse "$BASE_REF")" \
|
|
950
|
+
|| { echo "WT-4 halt: cannot read landed base tip" >&2; exit 1; }
|
|
951
|
+
git merge-base --is-ancestor "$BATCH_TIP" "$LANDED_BASE_TIP" \
|
|
952
|
+
|| { echo "WT-4 halt: landed verify failed - $BATCH_TIP is not an ancestor of $LANDED_BASE_TIP" >&2; exit 1; }
|
|
953
|
+
# --- Landed from here on. Any halt below is landed-but-incomplete: it records status
|
|
954
|
+
# --- "retained" + mergeCommit "$BATCH_TIP" via write_marker and stops — never a branch query.
|
|
955
|
+
write_marker () { # $1 = merged | retained; read-modify-write: preserve WT-3 fields (id, command, selector, createdAt, adopted, adoptedAt); jq already assumed by WT-3/task-resolution flows
|
|
956
|
+
mkdir -p "$(dirname "$MARKER")"
|
|
957
|
+
if [ -f "$MARKER" ]; then
|
|
958
|
+
TMP_MARKER="$(mktemp)"
|
|
959
|
+
jq --arg s "$1" --arg m "$BATCH_TIP" '.status = $s | .mergeCommit = $m' "$MARKER" > "$TMP_MARKER" \
|
|
960
|
+
|| { echo "WT-4 halt: cannot rewrite marker $MARKER" >&2; rm -f "$TMP_MARKER"; exit 1; }
|
|
961
|
+
mv "$TMP_MARKER" "$MARKER"
|
|
962
|
+
else
|
|
963
|
+
printf '{ "baseRef": "%s", "baseSha": "%s", "branch": "%s", "path": "%s", "status": "%s", "mergeCommit": "%s" }\n' \
|
|
964
|
+
"$BASE_REF" "$BASE_SHA" "$BRANCH" "$WT_PATH" "$1" "$BATCH_TIP" > "$MARKER"
|
|
965
|
+
fi
|
|
966
|
+
}
|
|
967
|
+
# WT-4a evidence persistence runs FIRST after the landed verify (Step 5, task 0720 R3):
|
|
867
968
|
# persist the batch report + verdict artifacts AND the per-run provenance (task
|
|
868
969
|
# 0975 R1) into the invoking tree before anything below touches the worktree.
|
|
869
|
-
# Any persistence failure routes to WT-5 —
|
|
970
|
+
# Any persistence failure routes to WT-5 — landed-but-incomplete: retained marker + mergeCommit.
|
|
870
971
|
WT_PATH="$(cd "../<worktree-dir>" && pwd)" # hoisted: needed by WT-4a AND WT-4b below
|
|
871
972
|
# WT-4a provenance persist-out (task 0975 R1): copy the worktree DB's run rows plus
|
|
872
973
|
# the .spur/memory/runs/<runId>.md + .state.json records into THIS tree. Run from the main
|
|
@@ -880,7 +981,7 @@ WT_PATH="$(cd "../<worktree-dir>" && pwd)" # hoisted: needed by WT-4a AND WT-4
|
|
|
880
981
|
# NOT proceed to WT-4b removal:
|
|
881
982
|
SETUP_SCRIPT=plugins/sp/scripts/inline-run-setup.ts; [ -f config/plugin-scripts.json -a -f "$SETUP_SCRIPT" ] || SETUP_SCRIPT="$(superskill script path sp inline-run-setup.mjs 2>/dev/null)"
|
|
882
983
|
bun "$SETUP_SCRIPT" --persist-out --from "$WT_PATH" "${TASK_FILE_ARGS[@]}" \
|
|
883
|
-
|| { echo "halt: worktree run-record persist-out failed - worktree retained (WT-5)" >&2; exit 1; }
|
|
984
|
+
|| { echo "halt: worktree run-record persist-out failed - worktree retained (WT-5)" >&2; write_marker retained; exit 1; }
|
|
884
985
|
#
|
|
885
986
|
# WT-4b — bounded CWD-holder cleanup (task 0720 R1). $WT_PATH above is the EXACT
|
|
886
987
|
# absolute worktree path; a relative path or a stale entry matches the wrong processes.
|
|
@@ -911,39 +1012,114 @@ if [ -n "$FINAL" ]; then
|
|
|
911
1012
|
PORTS="$(lsof -nP -a -p "$(echo "$FINAL" | paste -sd, -)" -iTCP -sTCP:LISTEN 2>/dev/null \
|
|
912
1013
|
| awk 'NR>1 {print $9}' | sort -u | paste -sd' ' -)"
|
|
913
1014
|
echo "halt: worktree still held by PID(s): $FINAL ${PORTS:+listening: $PORTS}" >&2
|
|
1015
|
+
write_marker retained # landed-but-incomplete: retained + mergeCommit
|
|
914
1016
|
exit 1 # -> WT-5: retain worktree + branch,
|
|
915
1017
|
fi # NO prune/remove/branch delete
|
|
916
|
-
|
|
917
|
-
|
|
1018
|
+
# Pre-removal evidence assertion (task 1067 R1, E71 enforcement): refuse to remove the
|
|
1019
|
+
# worktree while it still owns evidence absent from (or divergent from) the invoking tree —
|
|
1020
|
+
# the D63 landing skipped persist-out and silently lost verdicts + receipts. WBSes ride the
|
|
1021
|
+
# same TASK_FILE_ARGS resolved for WT-4a; run ids are a separate `--run-id <id>` per run and
|
|
1022
|
+
# belong to THIS check only — `inline-run-setup.ts --persist-out` accepts `--from` and
|
|
1023
|
+
# `--task-file` alone and exits 2 on `--run-id` (1089 driver note).
|
|
1024
|
+
CHECK_SCRIPT=plugins/sp/scripts/persist-out-check.ts; [ -f config/plugin-scripts.json -a -f "$CHECK_SCRIPT" ] || CHECK_SCRIPT="$(superskill script path sp persist-out-check.mjs 2>/dev/null)"
|
|
1025
|
+
bun "$CHECK_SCRIPT" --from "$WT_PATH" "${TASK_FILE_ARGS[@]}" \
|
|
1026
|
+
|| { echo "WT-4 halt: worktree evidence would be abandoned - persist-out missing or stale (WT-5)" >&2; write_marker retained; exit 1; }
|
|
1027
|
+
git worktree remove "../<worktree-dir>" \
|
|
1028
|
+
|| { echo "WT-4 halt: worktree remove failed after landed merge $BATCH_TIP" >&2; write_marker retained; exit 1; }
|
|
1029
|
+
# Branch deletion runs only after the merge landed, persisted, and the worktree is gone —
|
|
1030
|
+
# and it is existence-guarded, so a removed branch is never queried. -D over -d: the landed
|
|
1031
|
+
# verify already proved the merge landed, so an unmerged-refusal would only block proven-merged cleanup.
|
|
1032
|
+
if git rev-parse --verify --quiet "$BRANCH" >/dev/null; then
|
|
1033
|
+
git branch -D "$BRANCH" || { echo "WT-4 halt: cannot delete branch $BRANCH after landed merge $BATCH_TIP" >&2; write_marker retained; exit 1; }
|
|
1034
|
+
fi
|
|
918
1035
|
# WT-4c — clean up registry entry in ~/.config/spur/projects.json (task 0924)
|
|
919
1036
|
spur projects remove "$WT_PATH" 2>/dev/null || spur projects clean --json 2>/dev/null || true
|
|
920
|
-
#
|
|
1037
|
+
# Success marker, written once and only here: status "merged" + mergeCommit "$BATCH_TIP" —
|
|
1038
|
+
# after landed verification, required persistence, and cleanup:
|
|
1039
|
+
write_marker merged
|
|
921
1040
|
```
|
|
922
1041
|
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
1042
|
+
Every halt above stops explicitly and falls through to **WT-5** — zero-commit branch (task 0701
|
|
1043
|
+
R1), failed checkout, divergent base, failed FF, persist-out failure (task 0975), or surviving CWD
|
|
1044
|
+
holders (task 0720 R1). Pre-merge failures leave the marker at `status: active` and the worktree +
|
|
1045
|
+
branch untouched. Landed-but-incomplete failures record `status: retained` + `mergeCommit`
|
|
1046
|
+
BATCH_TIP: the merge landed, and the retained report reads the captured tip only. While any holder
|
|
1047
|
+
remains, do **not** run `git worktree prune`, `git worktree remove`, or branch deletion — only an
|
|
1048
|
+
EMPTY holder set may proceed to those steps. The holder
|
|
926
1049
|
halt report names every surviving PID; the listening port is best-effort — a CWD holder may own no
|
|
927
1050
|
socket, and `lsof` port discovery failing must not hide the PIDs.
|
|
928
1051
|
|
|
929
1052
|
#### Reuse mode — merge, retain
|
|
930
1053
|
|
|
931
|
-
The
|
|
932
|
-
|
|
1054
|
+
The fail-stop merge sequence runs identically — same guard, captured `BATCH_TIP`/`BASE_TIP`,
|
|
1055
|
+
ancestry check immediately before FF, `git merge --ff-only`, landed verification (per-call pinned
|
|
1056
|
+
to the invoking tree, task 1058) — but no WT-4a/4b/4c and no removal: the worktree and branch are
|
|
1057
|
+
**retained**. Pre-merge halts leave the marker `active`; a persistence failure after the landed
|
|
1058
|
+
merge records `retained` + `mergeCommit`:
|
|
933
1059
|
|
|
934
1060
|
```bash
|
|
935
|
-
|
|
936
|
-
git merge --ff-only
|
|
937
|
-
|
|
1061
|
+
# Same fail-stop sequence as create mode (zero-commit guard -> BATCH_TIP/BASE_TIP -> ancestry
|
|
1062
|
+
# rc check -> git merge --ff-only -> landed verify); local refs only — no fetch.
|
|
1063
|
+
MARKER=".spur/run/worktree-<marker-id>.json"
|
|
1064
|
+
git checkout "$BASE_REF" \
|
|
1065
|
+
|| { echo "WT-4 halt: checkout $BASE_REF failed - nothing merged, marker stays \"active\"" >&2; exit 1; } # -> WT-5
|
|
1066
|
+
[ "$(git rev-list --count "$BASE_REF..$BRANCH")" -gt 0 ] \
|
|
1067
|
+
|| { echo "WT-4 halt: branch carries no commits - nothing to merge" >&2; exit 1; }
|
|
1068
|
+
BATCH_TIP="$(git rev-parse "$BRANCH")" || { echo "WT-4 halt: cannot read $BRANCH" >&2; exit 1; }
|
|
1069
|
+
BASE_TIP="$(git rev-parse "$BASE_REF")" || { echo "WT-4 halt: cannot read $BASE_REF" >&2; exit 1; }
|
|
1070
|
+
ANCESTRY_RC=0
|
|
1071
|
+
git merge-base --is-ancestor "$BASE_TIP" "$BATCH_TIP" || ANCESTRY_RC=$?
|
|
1072
|
+
[ "$ANCESTRY_RC" -gt 1 ] \
|
|
1073
|
+
&& { echo "WT-4 halt: ancestry check could not run (git exit $ANCESTRY_RC) - failing closed" >&2; exit 1; }
|
|
1074
|
+
[ "$ANCESTRY_RC" -eq 1 ] && {
|
|
1075
|
+
echo "WT-4 halt: divergent - batch tip $BATCH_TIP vs base tip $BASE_TIP" >&2
|
|
1076
|
+
git log --oneline "$BASE_TIP..$BATCH_TIP" >&2
|
|
1077
|
+
git log --oneline "$BATCH_TIP..$BASE_TIP" >&2
|
|
1078
|
+
exit 1; } # -> WT-5: tree + branch retained, marker stays "active"
|
|
1079
|
+
git merge --ff-only "$BRANCH" # FF-only: never rebase, merge-commit, or resolve conflicts
|
|
1080
|
+
[ $? -eq 0 ] \
|
|
1081
|
+
|| { echo "WT-4 halt: FF-merge failed - the concurrent-writer race survives every precheck; marker stays \"active\"" >&2; exit 1; }
|
|
1082
|
+
LANDED_BASE_TIP="$(git rev-parse "$BASE_REF")" \
|
|
1083
|
+
|| { echo "WT-4 halt: cannot read landed base tip" >&2; exit 1; }
|
|
1084
|
+
git merge-base --is-ancestor "$BATCH_TIP" "$LANDED_BASE_TIP" \
|
|
1085
|
+
|| { echo "WT-4 halt: landed verify failed - $BATCH_TIP is not an ancestor of $LANDED_BASE_TIP" >&2; exit 1; }
|
|
1086
|
+
WT_PATH="<resolved-worktree-path>" # reuse mode: the adopted tree's physical path
|
|
1087
|
+
write_marker () { # $1 = merged | retained; read-modify-write: preserve WT-3 fields (id, command, selector, createdAt, adopted, adoptedAt); jq already assumed by WT-3/task-resolution flows
|
|
1088
|
+
mkdir -p "$(dirname "$MARKER")"
|
|
1089
|
+
if [ -f "$MARKER" ]; then
|
|
1090
|
+
TMP_MARKER="$(mktemp)"
|
|
1091
|
+
jq --arg s "$1" --arg m "$BATCH_TIP" '.status = $s | .mergeCommit = $m' "$MARKER" > "$TMP_MARKER" \
|
|
1092
|
+
|| { echo "WT-4 halt: cannot rewrite marker $MARKER" >&2; rm -f "$TMP_MARKER"; exit 1; }
|
|
1093
|
+
mv "$TMP_MARKER" "$MARKER"
|
|
1094
|
+
else
|
|
1095
|
+
printf '{ "baseRef": "%s", "baseSha": "%s", "branch": "%s", "path": "%s", "status": "%s", "mergeCommit": "%s" }\n' \
|
|
1096
|
+
"$BASE_REF" "$BASE_SHA" "$BRANCH" "$WT_PATH" "$1" "$BATCH_TIP" > "$MARKER"
|
|
1097
|
+
fi
|
|
1098
|
+
}
|
|
1099
|
+
# Step 5 evidence persistence (task 0720 R3) — required BEFORE the success marker:
|
|
1100
|
+
if printf 'batch report %s\n' "$BATCH_TIP" > ".spur/run/worktree-<marker-id>-batch-report.md"; then :; else
|
|
1101
|
+
echo "WT-5 halt: persistence failed after landed merge $BATCH_TIP; recovery reads captured tips only" >&2
|
|
1102
|
+
write_marker retained
|
|
1103
|
+
exit 1
|
|
1104
|
+
fi
|
|
1105
|
+
# Success marker: status "merged" + mergeCommit "$BATCH_TIP" — written once, only after landed verification and required persistence.
|
|
1106
|
+
# The worktree and branch are intentionally NOT removed.
|
|
1107
|
+
write_marker merged
|
|
938
1108
|
```
|
|
939
1109
|
|
|
940
1110
|
The operator supplied the tree, so the operator owns its lifetime. After a green reuse batch
|
|
941
1111
|
`baseRef == $BRANCH`, so the same worktree keeps fast-forwarding on the next invocation instead of
|
|
942
1112
|
having to be rebuilt — the continue-the-work loop is stable.
|
|
943
1113
|
|
|
944
|
-
**Fast-forward only.**
|
|
945
|
-
|
|
946
|
-
|
|
1114
|
+
**Fast-forward only.** `git merge --ff-only` is the sole mutation of the terminal sequence; every
|
|
1115
|
+
other step is a read, a check, a persistence write, or a cleanup, and every tip/ancestry read uses
|
|
1116
|
+
local refs — no fetch runs anywhere in this lifecycle. If the fresh `BASE_TIP` is not an ancestor of
|
|
1117
|
+
`BATCH_TIP`, the ancestry check halts before the merge: the report names both tips and both
|
|
1118
|
+
divergent commit ranges (`$BASE_TIP..$BATCH_TIP` and `$BATCH_TIP..$BASE_TIP`) and falls to the
|
|
1119
|
+
retention path (WT-5) with the tree and branch retained. A concurrent writer that lands between the
|
|
1120
|
+
ancestry check and the merge still trips `--ff-only` — the race survives every precheck, and a
|
|
1121
|
+
failed FF leaves the marker at `status: active`. Do **not** rebase, merge-commit, or resolve
|
|
1122
|
+
conflicts. The corpus files (`docs/tasks*/`, kanban/index) are
|
|
947
1123
|
auto-generated and conflict-prone; automated conflict resolution over generated files is exactly the
|
|
948
1124
|
wrong thing to attempt unattended. FF-only means the merge either is trivially correct or does not
|
|
949
1125
|
happen.
|
|
@@ -952,8 +1128,10 @@ happen.
|
|
|
952
1128
|
`--worktree --auto` batch — it does **not** pause even though it performs a merge and a branch
|
|
953
1129
|
deletion. That is the explicit single exception to Auto-Decision Principle #6 (`cross-cutting.md`),
|
|
954
1130
|
which otherwise pauses any `--merge` / branch-deletion action regardless of `--auto`. The exception
|
|
955
|
-
is safe because `git merge --ff-only`
|
|
956
|
-
|
|
1131
|
+
is safe because `git merge --ff-only` fails closed (it refuses rather than risk losing work), the
|
|
1132
|
+
branch deletion runs only after the merge has landed, persisted, and cleaned up — and is
|
|
1133
|
+
existence-guarded (`git rev-parse --verify --quiet`), so a removed branch is never queried;
|
|
1134
|
+
WT-5 retains the worktree and branch whenever FF is impossible or any task fails.
|
|
957
1135
|
Reuse mode is **narrower** than the carve-out (it merges but does not delete the branch), so the
|
|
958
1136
|
carve-out text needs no widening.
|
|
959
1137
|
|
|
@@ -982,22 +1160,44 @@ record and the persisted run artifacts are the evidence record.
|
|
|
982
1160
|
|
|
983
1161
|
### WT-5 — Failure path: retain and report (R5)
|
|
984
1162
|
|
|
985
|
-
On any per-task failure, batch halt, HITL pause that ends the run, or
|
|
986
|
-
worktree directory and branch are left **intact**. No destructive automation on this path under
|
|
987
|
-
flag combination (`--auto`, `--force`, `--keep-going` — all leave the worktree in place).
|
|
988
|
-
marker
|
|
989
|
-
|
|
1163
|
+
On any per-task failure, batch halt, HITL pause that ends the run, or a WT-4 halt, the
|
|
1164
|
+
worktree directory and branch are left **intact**. No destructive automation on this path under
|
|
1165
|
+
any flag combination (`--auto`, `--force`, `--keep-going` — all leave the worktree in place). The
|
|
1166
|
+
marker records which class of halt occurred (frozen vocabulary):
|
|
1167
|
+
|
|
1168
|
+
- **Pre-merge failure** (failed checkout, zero-commit guard, divergent base, failed FF): the merge
|
|
1169
|
+
never landed, so the marker **stays at `status: active`** — the WT-3 marker stands and WT-5 is
|
|
1170
|
+
its disposition. Nothing was merged onto the base ref.
|
|
1171
|
+
- **Landed-but-incomplete failure** (persist-out, holder cleanup, or worktree removal failed after
|
|
1172
|
+
a landed merge): the marker records `status: retained` + `mergeCommit` BATCH_TIP — a truthful
|
|
1173
|
+
report that the merge landed. The retained path reads the captured `BATCH_TIP` only; a removed
|
|
1174
|
+
branch is never queried.
|
|
1175
|
+
|
|
1176
|
+
The worktree's own `.spur` lifecycle DB is retained with the tree,
|
|
1177
|
+
so nothing is lost on either path (see the WT-4 lifecycle-DB disposition for the merged case —
|
|
990
1178
|
task 0701 R2d). Emit a retention report in the existing halt-report shape:
|
|
991
1179
|
|
|
1180
|
+
**Persist-out skip recovery (task 1067 R2, E71).** When evidence was lost to a landing that
|
|
1181
|
+
skipped persist-out (or the pre-removal assertion fired too late), verdicts remain derivable
|
|
1182
|
+
from the task records — `spur task show <wbs> --json` carries the recorded verdict and its
|
|
1183
|
+
proof block, so a lost `<wbs>-verdict.json` is regenerated by re-recording or copying from
|
|
1184
|
+
the task store. Feature run/latest receipts are re-runnable, not recovered: rerun the
|
|
1185
|
+
feature-verification workflow in the invoking tree —
|
|
1186
|
+
`spur workflow run feature-verification.yaml --vars '{"featureId":"<id>"}'` — which rewrites
|
|
1187
|
+
the receipt files it owns. Divergent files are never auto-overwritten; reconcile by hand.
|
|
1188
|
+
|
|
992
1189
|
```
|
|
993
1190
|
## Worktree retained — <command> <selector>
|
|
994
1191
|
|
|
995
|
-
**Halt cause:** <one-line cause — batch halted at task <wbs> / non-FF base ref / HITL pause>
|
|
1192
|
+
**Halt cause:** <one-line cause — batch halted at task <wbs> / non-FF base ref / HITL pause / landed-but-incomplete cleanup failure>
|
|
996
1193
|
**Worktree path:** ../<worktree-dir>
|
|
997
1194
|
**Branch:** sp/<command>-<selector-slug>-<short-id>
|
|
998
1195
|
**Base ref:** <base-ref> (<base-sha>)
|
|
1196
|
+
**Merge state:** pre-merge failure — nothing landed | landed-but-incomplete — mergeCommit <batch-tip-sha>
|
|
999
1197
|
|
|
1000
|
-
The worktree and its branch are intact.
|
|
1198
|
+
The worktree and its branch are intact.
|
|
1199
|
+
pre-merge failure: nothing was merged onto the base ref.
|
|
1200
|
+
landed-but-incomplete: the batch merge LANDED as <batch-tip-sha> on <base-ref>; retention covers post-merge persistence/cleanup only.
|
|
1001
1201
|
Resume, merge, or discard:
|
|
1002
1202
|
|
|
1003
1203
|
resume: cd <worktree-path> && <command> --continue --worktree <worktree-path>
|
|
@@ -1010,16 +1210,42 @@ ordered divergence recipe, run by the operator — the driver never merges, reba
|
|
|
1010
1210
|
conflicts itself. Other halt causes (task failure, HITL pause) keep the hint as printed:
|
|
1011
1211
|
|
|
1012
1212
|
```
|
|
1013
|
-
# 1.
|
|
1014
|
-
git
|
|
1213
|
+
# 1. decide whether conflicts need resolving FIRST, because that decides the merge form:
|
|
1214
|
+
# git merge-tree $(git merge-base <base-ref> <branch>) <base-ref> <branch> | grep -q '^<<<<<<<' \
|
|
1215
|
+
# && echo "conflicts — use the two-phase form below" || echo "clean — use the atomic form"
|
|
1216
|
+
#
|
|
1217
|
+
# 1a. CLEAN (the common case): integrate as ONE atomic merge commit. Never a rebase — task
|
|
1218
|
+
# evidence cites the branch's commit SHAs. This is the file's sanctioned merge commit.
|
|
1219
|
+
# Do NOT split this into a two-phase merge + commit when nothing needs resolving: that spans
|
|
1220
|
+
# the whole gate run, and anything in that window that touches the repository's git state
|
|
1221
|
+
# (a test, a hook, a tool invocation) discards MERGE_HEAD silently. The failure is invisible
|
|
1222
|
+
# until the history is read — it produced a single-parent commit in run ada5a36c (task 1090),
|
|
1223
|
+
# which then needed an extra empty ancestry merge to repair. A cheap probe could not
|
|
1224
|
+
# reproduce it (MERGE_HEAD survived `bun run lint`, the rule preset and a test-file subset),
|
|
1225
|
+
# so the cross-command dependency is removed rather than diagnosed.
|
|
1226
|
+
git checkout <base-ref> && git merge --no-ff -m "<prepared message>" <branch>
|
|
1227
|
+
|
|
1228
|
+
# 1b. CONFLICTING: the two-phase form is correct here, because the resolution work must land
|
|
1229
|
+
# between the merge and the commit. Everything in steps 2–4 belongs to this branch only.
|
|
1230
|
+
# A divergent branch cannot fast-forward, so the merge flag from 1a is not needed here.
|
|
1231
|
+
# git checkout <base-ref> && git merge --no-commit <branch>
|
|
1015
1232
|
# 2. resolve source conflicts by hand; generated files are then regenerated with the project's
|
|
1016
1233
|
# generator, never hand-merged
|
|
1017
1234
|
# (this repo: bun run build:plugin-lib && bun run --filter @gobing-ai/spur build:bundle)
|
|
1018
1235
|
# 3. stage every resolved path and regenerated bundle (git add …) — an unmerged or
|
|
1019
1236
|
# unstaged path makes step 5 abort
|
|
1020
1237
|
# 4. run qualityGateCmd once, after ALL conflicts are resolved
|
|
1021
|
-
# 5. commit the merge with the prepared message file
|
|
1022
|
-
git commit -F <message-file>
|
|
1238
|
+
# 5. commit the merge with the prepared message file — CHECK THE PARENTS FIRST (step 5a)
|
|
1239
|
+
# git commit -F <message-file>
|
|
1240
|
+
#
|
|
1241
|
+
# 5a. ALWAYS verify the merge really is a merge before treating it as landed:
|
|
1242
|
+
# git log -1 --format='%h parents=%p' # must print TWO parents
|
|
1243
|
+
# One parent means MERGE_HEAD was lost somewhere between steps 1 and 5 — the content is on
|
|
1244
|
+
# the base ref but the branch is not an ancestor, so `git branch -d` would refuse and the
|
|
1245
|
+
# retained worktree's provenance would not be readable from the base ref. Recover by
|
|
1246
|
+
# re-running step 1a against the already-landed tree (a content-free merge commit restores
|
|
1247
|
+
# ancestry).
|
|
1248
|
+
|
|
1023
1249
|
# 6. persist evidence out (WT-4a), then WT-4b/4c cleanup, and set the marker to merged
|
|
1024
1250
|
inline-run-setup --persist-out --from <worktree> --task-file …
|
|
1025
1251
|
```
|
|
@@ -1030,6 +1256,16 @@ are long and already resumable via `--continue`; auto-deleting is data loss, aut
|
|
|
1030
1256
|
partial result presented as a whole. The answer to "what happens if it fails" is "nothing happens,
|
|
1031
1257
|
and we tell you where the work is."
|
|
1032
1258
|
|
|
1259
|
+
**Partial worktree-removal recovery is inspection-only.** If a create-mode run halted partway
|
|
1260
|
+
through removal (leftover directory, stale registration), the recovery pass gathers facts and
|
|
1261
|
+
nothing else: `git worktree list` (is the tree still registered?), `ls` of the path (what remains
|
|
1262
|
+
on disk?), and the marker / `~/.config/spur/projects.json` registration state. Before removing
|
|
1263
|
+
anything, verify the leftover path is the batch's **owned** path — match it against the marker's
|
|
1264
|
+
`path`/`branch` (or the resolved `--worktree <name>` identity), never a guessed sibling directory.
|
|
1265
|
+
Recursive deletion of leftover directories that survived `git worktree remove` is **never
|
|
1266
|
+
automatic** — it requires explicit operator authorization. No automatic rebase, recovery merge,
|
|
1267
|
+
`git worktree prune`, or holder kill runs on this path.
|
|
1268
|
+
|
|
1033
1269
|
### WT-6 — `--continue` re-entry (R7)
|
|
1034
1270
|
|
|
1035
1271
|
A `--continue` resume of a batch started with `--worktree` must re-enter the existing worktree via
|
|
@@ -1172,7 +1408,7 @@ integrate(t):
|
|
|
1172
1408
|
├─ rebase fails → git -C "$WT" rebase --abort → conflict path (R4) below
|
|
1173
1409
|
└─ rebase clean → git checkout "$BASE_REF" → zero-commit guard → git merge --ff-only "$BRANCH"
|
|
1174
1410
|
→ WT-4a verdict persist → WT-4b holders → WT-4c registry
|
|
1175
|
-
→ git worktree remove → git branch -
|
|
1411
|
+
→ git worktree remove → existence-guarded git branch -D → marker merged
|
|
1176
1412
|
```
|
|
1177
1413
|
|
|
1178
1414
|
The merge is always `--ff-only`: a base that will not fast-forward after a clean rebase is a
|
|
@@ -163,6 +163,20 @@ The pipeline (`kind: state-machine`) runs the work loop:
|
|
|
163
163
|
precheck → implement → test [→ test-fix → test-recheck] → review → approve(HITL) → verify → record → done
|
|
164
164
|
```
|
|
165
165
|
|
|
166
|
+
Two branches reach the bounded repair hop, not one (session finding after 1088):
|
|
167
|
+
|
|
168
|
+
- `test`/`test-recheck` FAIL → `test-fail-triage` (0943's failure-class router) → `test-fix`;
|
|
169
|
+
- `review` without a `Verdict: PASS` in its answer → `review-fail-triage` (the review-side twin) →
|
|
170
|
+
`test-fix`, bounded by the SAME `qualityGateMaxFixAttempts` counter. A FAIL review therefore has
|
|
171
|
+
an on-graph, bounded path to remediation instead of dead-ending the run, and the fix re-enters
|
|
172
|
+
quality → review → verify on a freshly captured proof digest.
|
|
173
|
+
|
|
174
|
+
The completion gate also reads the verifier's confidence (session finding after 1088): the verify
|
|
175
|
+
answer's `Confidence: HIGH|MEDIUM|LOW` line reaches the verdict artifact, and both `verify → record`
|
|
176
|
+
and `record → done` refuse a `LOW` (or absent) level unless the run was launched with
|
|
177
|
+
`--vars '{"ackLowConfidence":"true"}'`. A PASS the verifier would not stand behind is an operator
|
|
178
|
+
risk decision, so it takes an operator acknowledgement — not a silent certification.
|
|
179
|
+
|
|
166
180
|
Agentic steps use the pure slash inputs declared by each YAML `agent.run` action (ADR-043). The
|
|
167
181
|
workflow engine dispatches them; the interactive driver invokes their backing skills in-session.
|
|
168
182
|
The `test` hop is primarily
|
|
@@ -203,7 +217,9 @@ When `--next` is set and implementation succeeds:
|
|
|
203
217
|
2. **Record provenance** — `spur task run-link <wbs> --source next-auto --json`. Writes a
|
|
204
218
|
`kind: pipeline` entry into `task_run_links` so the `testing → done` provenance guard
|
|
205
219
|
(lifecycle-adapter.ts L106-131) accepts the in-session implementation path. Idempotent:
|
|
206
|
-
safe to call even when a pipeline link already exists.
|
|
220
|
+
safe to call even when a pipeline link already exists. Run it from the same cwd as the
|
|
221
|
+
`task update` calls in step 1 — per-tree DB isolation makes a link from another tree
|
|
222
|
+
invisible to this tree's guard.
|
|
207
223
|
3. **On a clean transition:** invoke `/sp:dev-verify <wbs> --auto --next` (`--auto` propagates
|
|
208
224
|
down the whole chain). The verify step's `--next` transition to `done` now passes the
|
|
209
225
|
provenance guard because step 2 recorded the link.
|
|
@@ -301,10 +301,13 @@ tasks by `updated_at >= date`).
|
|
|
301
301
|
|
|
302
302
|
**Anchor:** `#flag-fix`.
|
|
303
303
|
|
|
304
|
-
Remediation policy on verify-family commands (`dev-verify`, `dev-verifyall`)
|
|
305
|
-
coordinator (`dev-refactor`):
|
|
304
|
+
Remediation policy on verify-family commands (`dev-verify`, `dev-verifyall`), the refactor
|
|
305
|
+
coordinator (`dev-refactor`) and the session review (`dev-review-session`):
|
|
306
306
|
`none|blockers-first|all`. `none` reports findings without fixing; `blockers-first` fixes only P1/P2;
|
|
307
|
-
`all` fixes everything found.
|
|
307
|
+
`all` fixes everything found. On `dev-review-session` the vocabulary is `none|auto|all`: `none`
|
|
308
|
+
report-only, `auto` the `--triage` behavior (inline direct fixes, the rest filed as tasks), `all`
|
|
309
|
+
fixes every actionable finding inline and files no task. Deprecated on `dev-review` (routes to
|
|
310
|
+
`dev-verify --fix`).
|
|
308
311
|
|
|
309
312
|
### `--until <ref>` — upper bound on a range
|
|
310
313
|
|
|
@@ -336,6 +339,14 @@ specific status slice).
|
|
|
336
339
|
Skip the shippable guard on verify-family commands (`dev-verify`, `dev-verifyall`). Used when
|
|
337
340
|
verifying a task whose artifact is intentionally not yet shippable (e.g. a doc-only task).
|
|
338
341
|
|
|
342
|
+
### `--no-summary` — skip the execution summary
|
|
343
|
+
|
|
344
|
+
**Anchor:** `#flag-no-summary`.
|
|
345
|
+
|
|
346
|
+
Skip the measured execution summary that run commands (`dev-run`, `dev-runall`) print by default
|
|
347
|
+
once the run settles: per-stage (or per-run) time, operator wait, tool calls and total / non-cached
|
|
348
|
+
tokens from the host transcript. Procedure: [dev-operations.md § Execution summary](dev-operations.md#execution-summary).
|
|
349
|
+
|
|
339
350
|
### `--skip-design` — omit the design package
|
|
340
351
|
|
|
341
352
|
**Anchor:** `#flag-skip-design`.
|
|
@@ -445,7 +456,8 @@ Review commands (`dev-review`, `dev-review-session`): after the report, bucket e
|
|
|
445
456
|
remainder through `spur task` — never fix straight from the raw findings list. Off by default
|
|
446
457
|
(report-only). Both file one or more implement-ready tasks sized per cohesive unit, under existing
|
|
447
458
|
features ([dev-operations.md § 2. review](dev-operations.md#2-review)); `dev-review-session` keeps
|
|
448
|
-
the stricter direct-fix bar (pure docs / one-to-two-line fixes)
|
|
459
|
+
the stricter direct-fix bar (pure docs / one-to-two-line fixes), and there `--triage` is an alias
|
|
460
|
+
of `--fix auto` ([`--fix`](#flag-fix)).
|
|
449
461
|
|
|
450
462
|
### `--worktree [<name>]` — run the batch in an isolated git worktree (create or reuse)
|
|
451
463
|
|