@gobing-ai/spur 0.4.0 → 0.4.1

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.
Files changed (154) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/config/decisions/gates.yaml +36 -0
  3. package/config/decisions/history-anatomy.yaml +35 -0
  4. package/config/decisions/idea-pipeline.yaml +38 -0
  5. package/config/plugin-scripts.json +15 -0
  6. package/config/rules/docs/sourced-api-claims.yaml +60 -0
  7. package/config/rules/recommended-pre-check.yaml +8 -3
  8. package/config/rules/structure/scratch-evidence-pointer.yaml +35 -0
  9. package/config/script-placement-baseline.json +2 -2
  10. package/config/workflows/feature-lifecycle.yaml +9 -2
  11. package/config/workflows/history-anatomy.yaml +22 -0
  12. package/config/workflows/idea-pipeline.yaml +75 -0
  13. package/config/workflows/task-pipeline.yaml +216 -57
  14. package/config/workflows/wrapup-pipeline.yaml +106 -13
  15. package/package.json +9 -9
  16. package/plugins/sp/README.md +11 -1
  17. package/plugins/sp/agents/super-planner.md +11 -0
  18. package/plugins/sp/agents/super-reviewer.md +14 -0
  19. package/plugins/sp/bunfig.toml +8 -0
  20. package/plugins/sp/commands/dev-brainstorm.md +29 -16
  21. package/plugins/sp/commands/dev-find-way.md +80 -0
  22. package/plugins/sp/commands/dev-idea.md +6 -0
  23. package/plugins/sp/commands/dev-parallel.md +8 -2
  24. package/plugins/sp/commands/dev-plan.md +6 -0
  25. package/plugins/sp/commands/dev-review-session.md +3 -1
  26. package/plugins/sp/commands/dev-run.md +12 -2
  27. package/plugins/sp/commands/dev-runall.md +18 -10
  28. package/plugins/sp/hooks/context-session-start.ts +1 -1
  29. package/plugins/sp/lib/idea-handoff.generated.mjs +41 -5
  30. package/plugins/sp/lib/inline-run-app.ts +28 -0
  31. package/plugins/sp/lib/inline-run.generated.d.mts +5 -0
  32. package/plugins/sp/lib/inline-run.generated.mjs +333 -45
  33. package/plugins/sp/lib/plan-projection.generated.d.mts +28 -0
  34. package/plugins/sp/lib/plan-projection.generated.mjs +255 -0
  35. package/plugins/sp/lib/quality-gate.generated.d.mts +47 -1
  36. package/plugins/sp/lib/quality-gate.generated.mjs +30489 -82
  37. package/plugins/sp/lib/residual-scan.generated.d.mts +19 -0
  38. package/plugins/sp/lib/residual-scan.generated.mjs +66 -0
  39. package/plugins/sp/lib/run-summary-core.ts +561 -0
  40. package/plugins/sp/lib/transcript.ts +121 -6
  41. package/plugins/sp/plugin.json +1 -1
  42. package/plugins/sp/references/roles.md +1 -1
  43. package/plugins/sp/scripts/batch-plan.mjs +335 -0
  44. package/plugins/sp/scripts/batch-plan.ts +100 -0
  45. package/plugins/sp/scripts/batch-preflight.mjs +51 -3
  46. package/plugins/sp/scripts/batch-preflight.ts +72 -1
  47. package/plugins/sp/scripts/commit-guard.mjs +216 -0
  48. package/plugins/sp/scripts/commit-guard.ts +249 -0
  49. package/plugins/sp/scripts/inline-run-dispatch.mjs +130 -0
  50. package/plugins/sp/scripts/inline-run-dispatch.ts +135 -0
  51. package/plugins/sp/scripts/inline-run-setup.mjs +551 -80
  52. package/plugins/sp/scripts/inline-run-setup.ts +52 -78
  53. package/plugins/sp/scripts/persist-out-check.mjs +90 -41
  54. package/plugins/sp/scripts/persist-out-check.ts +112 -57
  55. package/plugins/sp/scripts/quality-gate.mjs +30407 -73
  56. package/plugins/sp/scripts/quality-gate.ts +21 -5
  57. package/plugins/sp/scripts/residual-scan.mjs +86 -31
  58. package/plugins/sp/scripts/residual-scan.ts +28 -30
  59. package/plugins/sp/scripts/run-summary.mjs +421 -14
  60. package/plugins/sp/scripts/run-summary.ts +41 -177
  61. package/plugins/sp/scripts/session-timeline.mjs +109 -12
  62. package/plugins/sp/scripts/session-timeline.ts +71 -19
  63. package/plugins/sp/scripts/wrapup-steps.mjs +150 -2
  64. package/plugins/sp/scripts/wrapup-steps.ts +203 -1
  65. package/plugins/sp/skills/brainstorm/SKILL.md +9 -47
  66. package/plugins/sp/skills/branch-workflow/SKILL.md +1 -0
  67. package/plugins/sp/skills/branch-workflow/references/worktree-patterns.md +1 -0
  68. package/plugins/sp/skills/code-implementation/SKILL.md +48 -1
  69. package/plugins/sp/skills/code-verification/SKILL.md +3 -4
  70. package/plugins/sp/skills/code-verification/references/secu-review.md +3 -0
  71. package/plugins/sp/skills/code-verification/references/verdict-schema.md +68 -0
  72. package/plugins/sp/skills/fleet-join/SKILL.md +1 -1
  73. package/plugins/sp/skills/session-review/SKILL.md +38 -7
  74. package/plugins/sp/skills/source-driven-development/SKILL.md +5 -0
  75. package/plugins/sp/skills/spec-decomposition/references/decomposition.md +5 -3
  76. package/plugins/sp/skills/spur-cli/SKILL.md +5 -2
  77. package/plugins/sp/skills/spur-cli/references/agent.md +20 -15
  78. package/plugins/sp/skills/spur-cli/references/decision.md +7 -6
  79. package/plugins/sp/skills/spur-cli/references/message.md +7 -7
  80. package/plugins/sp/skills/spur-cli/references/tasks/verbs.md +19 -3
  81. package/plugins/sp/skills/spur-cli/references/tasks.md +10 -7
  82. package/plugins/sp/skills/spur-cli/references/workflows/authoring-workflows.md +7 -0
  83. package/plugins/sp/skills/spur-cli/references/workflows.md +7 -2
  84. package/plugins/sp/skills/spur-composer/SKILL.md +1 -1
  85. package/plugins/sp/skills/spur-dev/SKILL.md +18 -14
  86. package/plugins/sp/skills/spur-dev/references/ac-style-guide.md +6 -3
  87. package/plugins/sp/skills/spur-dev/references/cross-cutting.md +49 -14
  88. package/plugins/sp/skills/spur-dev/references/dev-operations.md +37 -16
  89. package/plugins/sp/skills/spur-dev/references/execution-batch-continuation.md +68 -0
  90. package/plugins/sp/skills/spur-dev/references/execution-batch-report.md +194 -0
  91. package/plugins/sp/skills/spur-dev/references/execution-batch.md +113 -1020
  92. package/plugins/sp/skills/spur-dev/references/execution-parallel-isolation.md +130 -0
  93. package/plugins/sp/skills/spur-dev/references/execution-workflow.md +6 -0
  94. package/plugins/sp/skills/spur-dev/references/execution-worktree-landing.md +471 -0
  95. package/plugins/sp/skills/spur-dev/references/execution-worktree-setup.md +379 -0
  96. package/plugins/sp/skills/spur-dev/references/flag-glossary.md +35 -5
  97. package/plugins/sp/skills/spur-dev/references/glossary.md +2 -2
  98. package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +343 -163
  99. package/plugins/sp/skills/spur-dev/references/structured-trace-emission.md +178 -0
  100. package/plugins/sp/skills/wayfinder/SKILL.md +10 -14
  101. package/schemas/state-machine-workflow.schema.json +23 -0
  102. package/spur.js +20728 -16630
  103. package/web/_astro/{BoardApp.VeoxxLBy.js → BoardApp.D66k_s_T.js} +1 -1
  104. package/web/_astro/{BoardApp.Bzr4OXrW.js → BoardApp.D6sjAJr_.js} +173 -173
  105. package/web/_astro/{TaskDetail.LAyR_Pvn.js → TaskDetail.BynLnMNc.js} +1 -1
  106. package/web/_astro/{arc.f4e0OYLg.js → arc._BUadrdH.js} +1 -1
  107. package/web/_astro/{architectureDiagram-3BPJPVTR.SSIvxi-Z.js → architectureDiagram-3BPJPVTR.Dh7xS1Pp.js} +1 -1
  108. package/web/_astro/{blockDiagram-GPEHLZMM.DSXIHOFf.js → blockDiagram-GPEHLZMM.DFDVpfXm.js} +1 -1
  109. package/web/_astro/{c4Diagram-AAUBKEIU.DG9Ubu85.js → c4Diagram-AAUBKEIU.V7_3_glh.js} +1 -1
  110. package/web/_astro/channel.DZVfto-r.js +1 -0
  111. package/web/_astro/{chunk-2J33WTMH.C7QU88Qh.js → chunk-2J33WTMH.De1yx7Jz.js} +1 -1
  112. package/web/_astro/{chunk-4BX2VUAB.26Z94xBj.js → chunk-4BX2VUAB.BDhHFuxx.js} +1 -1
  113. package/web/_astro/{chunk-55IACEB6.DYRiBkqw.js → chunk-55IACEB6.DDLkWnmC.js} +1 -1
  114. package/web/_astro/{chunk-727SXJPM.D5Vfv5pe.js → chunk-727SXJPM.CwfiLnA7.js} +1 -1
  115. package/web/_astro/{chunk-AQP2D5EJ.DXHV47bN.js → chunk-AQP2D5EJ.CtGDmUnM.js} +1 -1
  116. package/web/_astro/{chunk-FMBD7UC4.D1suAfOs.js → chunk-FMBD7UC4.BOLjpw73.js} +1 -1
  117. package/web/_astro/{chunk-ND2GUHAM.D6BmdHEY.js → chunk-ND2GUHAM.BDhQls6O.js} +1 -1
  118. package/web/_astro/{chunk-QZHKN3VN.rnPU-Ak-.js → chunk-QZHKN3VN.76qBynDF.js} +1 -1
  119. package/web/_astro/{classDiagram-4FO5ZUOK.CvEfXIo1.js → classDiagram-4FO5ZUOK.N0mpPHT0.js} +1 -1
  120. package/web/_astro/{classDiagram-v2-Q7XG4LA2.CvEfXIo1.js → classDiagram-v2-Q7XG4LA2.N0mpPHT0.js} +1 -1
  121. package/web/_astro/{cose-bilkent-S5V4N54A.DkMisLSm.js → cose-bilkent-S5V4N54A.CKknFfjB.js} +1 -1
  122. package/web/_astro/{cynefin-OW5HDTMX.C77Q-pF6.js → cynefin-OW5HDTMX.Cw3ZWBAh.js} +1 -1
  123. package/web/_astro/{dagre-BM42HDAG.YaV8ce27.js → dagre-BM42HDAG.D3yIKCRD.js} +1 -1
  124. package/web/_astro/{diagram-2AECGRRQ.CUyGEKge.js → diagram-2AECGRRQ.D51dO83Z.js} +1 -1
  125. package/web/_astro/{diagram-5GNKFQAL.CRgd8dgj.js → diagram-5GNKFQAL.Bb-FViDj.js} +1 -1
  126. package/web/_astro/{diagram-KO2AKTUF.Bmzys9hq.js → diagram-KO2AKTUF.D3mN8S6x.js} +1 -1
  127. package/web/_astro/{diagram-LMA3HP47.BvmJY4Lw.js → diagram-LMA3HP47.dMMyPcbJ.js} +1 -1
  128. package/web/_astro/{diagram-OG6HWLK6.0hEhD30d.js → diagram-OG6HWLK6.AzbvzY4C.js} +1 -1
  129. package/web/_astro/{erDiagram-TEJ5UH35.Bjt8lrnm.js → erDiagram-TEJ5UH35.BR5I4OZb.js} +1 -1
  130. package/web/_astro/{flowDiagram-I6XJVG4X.CJZTjRDV.js → flowDiagram-I6XJVG4X.DFqvhGkn.js} +1 -1
  131. package/web/_astro/{ganttDiagram-6RSMTGT7._VqoVafc.js → ganttDiagram-6RSMTGT7.ByJbhH6J.js} +1 -1
  132. package/web/_astro/{gitGraphDiagram-PVQCEYII.CEDb7tVo.js → gitGraphDiagram-PVQCEYII.CInHjQR3.js} +1 -1
  133. package/web/_astro/index.COh_kBxN.css +1 -0
  134. package/web/_astro/{infoDiagram-5YYISTIA.CgwifPpK.js → infoDiagram-5YYISTIA.Cm0dGVjJ.js} +1 -1
  135. package/web/_astro/{ishikawaDiagram-YF4QCWOH.B99wx2J1.js → ishikawaDiagram-YF4QCWOH.C3nOP6Sj.js} +1 -1
  136. package/web/_astro/{journeyDiagram-JHISSGLW.2hRwJbEo.js → journeyDiagram-JHISSGLW.4IvLa2MW.js} +1 -1
  137. package/web/_astro/{kanban-definition-UN3LZRKU.BahJkKNC.js → kanban-definition-UN3LZRKU.DS87Cl-k.js} +1 -1
  138. package/web/_astro/{linear.DguUUe6V.js → linear.MzmRrNER.js} +1 -1
  139. package/web/_astro/{mermaid.core.CvgfmqVr.js → mermaid.core.fK-WNswS.js} +4 -4
  140. package/web/_astro/{mindmap-definition-RKZ34NQL.B3qEA3x9.js → mindmap-definition-RKZ34NQL.BqipOHhY.js} +1 -1
  141. package/web/_astro/{pieDiagram-4H26LBE5.FlWA-_ru.js → pieDiagram-4H26LBE5.0nBiPqFG.js} +1 -1
  142. package/web/_astro/{quadrantDiagram-W4KKPZXB.9bVn35X7.js → quadrantDiagram-W4KKPZXB.Szj7Zpx5.js} +1 -1
  143. package/web/_astro/{requirementDiagram-4Y6WPE33.ChykIhct.js → requirementDiagram-4Y6WPE33.D4UR6TWo.js} +1 -1
  144. package/web/_astro/{sankeyDiagram-5OEKKPKP.Z2Y3hkYb.js → sankeyDiagram-5OEKKPKP.DAE7BdOs.js} +1 -1
  145. package/web/_astro/{sequenceDiagram-3UESZ5HK.J7JsHWDt.js → sequenceDiagram-3UESZ5HK.BP9ZVq25.js} +1 -1
  146. package/web/_astro/{stateDiagram-AJRCARHV.DAPbiCSS.js → stateDiagram-AJRCARHV.BQW7mIxk.js} +1 -1
  147. package/web/_astro/{stateDiagram-v2-BHNVJYJU.D2jvSi0q.js → stateDiagram-v2-BHNVJYJU.PbK4nDih.js} +1 -1
  148. package/web/_astro/{timeline-definition-PNZ67QCA.psW2ci29.js → timeline-definition-PNZ67QCA.Ci_GvUpo.js} +1 -1
  149. package/web/_astro/{vennDiagram-CIIHVFJN.DlILYVmg.js → vennDiagram-CIIHVFJN.DNw1Y5qx.js} +1 -1
  150. package/web/_astro/{wardleyDiagram-YWT4CUSO.xNWwLJnn.js → wardleyDiagram-YWT4CUSO.CwlkaaNe.js} +1 -1
  151. package/web/_astro/{xychartDiagram-2RQKCTM6.EU_BuH7V.js → xychartDiagram-2RQKCTM6.MI6ZuiH_.js} +1 -1
  152. package/web/index.html +2 -2
  153. package/web/_astro/channel.CUFdtWhm.js +0 -1
  154. package/web/_astro/index.Dg_3bOaJ.css +0 -1
@@ -0,0 +1,178 @@
1
+ ---
2
+ name: structured-trace-emission
3
+ description: "Structured trace emission (task 1128 split, ADR-117 / task 0868): the action vocabulary, the per-step emit shape, the close contract and the parse rules the host driver follows. Read it when the first trace event is emitted."
4
+ see_also:
5
+ - inline-pipeline-driver
6
+ ---
7
+
8
+ ## Structured trace emission (ADR-117, task 0868)
9
+
10
+ `.spur/memory/runs/<run-id>.md` is the human half of the two-file run record — evidence, **not the record
11
+ of truth**. A run's
12
+ observability is a property of the run, so the inline driver owes the same structured trace the
13
+ engine subprocess writes — and it owes it through the **same writer**, never a parallel
14
+ implementation. The shared writer is `WorkflowActionTraceWriter`
15
+ (`packages/app/src/workflow/action-trace.ts`): the same decorator the engine composition installs
16
+ around `DbWorkflowPersistenceAdapter`, so the two surfaces call one emission path and one run-row
17
+ closure path and cannot drift.
18
+
19
+ The driver reaches it through the existing run delegate (`$SETUP_SCRIPT`,
20
+ `plugins/sp/scripts/inline-run-setup.ts`) — no new entry point, no second resolution chain:
21
+
22
+ - **Every executed action** — after the action settles, whether it ran host-inline or via a native
23
+ subagent — append its provenance line as before, then record the boundary:
24
+
25
+ ```bash
26
+ bun "$SETUP_SCRIPT" --action --run-id "$RUN_ID" --node <state-id> --kind <action-kind> \
27
+ --status <done|failed> --ok <true|false> [--duration-ms <measured-ms>] [--estimated] \
28
+ --project-root "$OWNING_TREE"
29
+ ```
30
+
31
+ `<state-id>` is the current YAML state id (the `node`), `<action-kind>` the YAML action kind
32
+ (`agent.run`, `shell`, `note`, `doctor.probe`, …). `--status` is `done` when the action settled
33
+ under its declared error policy and `failed` otherwise. This writes the `action_runs` row (node,
34
+ kind, status, `ok`, `duration_ms`, `run_id`) the engine would have written, so the run's rows are
35
+ queryable by run id (`spur workflow progress <run-id>`, `ActionRunDao`) without reading the text
36
+ log. The writer
37
+ back-dates the row's `started_at` from its own `completed_at` minus the measured duration
38
+ (0887 R8), so `completed_at − started_at == duration_ms` exactly; a back-date failure is
39
+ recorded (`action.backdate`) and never affects the run.
40
+
41
+ **`--project-root` names the tree that owns the run row (task 1136 R1/R2).** Per-tree DBs are
42
+ deliberate, so when the driver runs inside a `--worktree` (or any other execution tree) the run row
43
+ lives in the **invoking** tree's `.spur/spur.db` while the action commands run in the execution
44
+ tree. `--project-root` is the invoking tree recorded by the WT-3 marker; without it the row is read
45
+ from the cwd tree. A run-row miss is a **correctness failure**, not a best-effort emission failure:
46
+ `--action`, `--actions-file` and `--node-enter` exit `1` with
47
+ `{"ok":false,"code":"RUN_NOT_FOUND"}`, exactly like `--close`. **Never redirect these calls'
48
+ stdout/stderr to `/dev/null`** — the suppressed-RUN_NOT_FOUND run of task 1132 lost 93 action rows
49
+ to exactly that redirect. Capture the output or let it stream; a failure must be visible.
50
+
51
+ **Durations: measured beats reported (task 1136 R4).** Stamp the enter time before the action runs:
52
+
53
+ ```bash
54
+ bun "$SETUP_SCRIPT" --node-enter --run-id "$RUN_ID" --node <state-id> --project-root "$OWNING_TREE"
55
+ ```
56
+
57
+ Then call `--action` **without** `--duration-ms` — the delegate computes
58
+ `duration = now − enter` and stamps `provenance: measured` into `action_runs.result_json`. A
59
+ caller-supplied `--duration-ms` stays valid but is recorded `provenance: host-reported` (with
60
+ `--estimated` marking a value reconstructed after the fact); every other inline row is
61
+ `host-reported` too, and the projection exposes `provenance` per attempt so the Board can label a
62
+ reported duration instead of presenting it as measured. A row whose computed start would precede
63
+ `runs.started_at` is **rejected** (exit `1`) — a timestamp before the run began is a broken clock,
64
+ not evidence. Calling `--action` without `--duration-ms` and without a matching `--node-enter`
65
+ exits `1` with `code:"ACTION_DURATION_UNAVAILABLE"`; it never silently drops the row.
66
+
67
+ - **A state with several actions (1007 R5)** — emit the whole state's boundaries in one call
68
+ instead of one `--action` invocation per action. Write a JSON array
69
+ (`[{node,kind,status,ok,durationMs,estimated?}, …]` — the `--action` fields, `estimated`
70
+ optional and `false` when absent) to a temp file and pass it with `--actions-file`:
71
+
72
+ ```bash
73
+ bun "$SETUP_SCRIPT" --actions-file <actions.json> --run-id "$RUN_ID" --project-root "$OWNING_TREE"
74
+ ```
75
+
76
+ Every row is recorded through the same writer as `--action` (one `action_runs` row per entry);
77
+ the batch is validated in full before the first write, so an invalid row or unreadable file
78
+ exits `1` with `{"ok":false}` and leaves **no** partial rows — fix the batch and re-emit. On
79
+ success it prints `{"ok":true,"runId":…,"recorded":<n>}` and exits `0`. Row emission stays
80
+ best-effort exactly like `--action`: if a row's write fails mid-batch, the failure is recorded
81
+ to the run record and the call reports `{"ok":false,…,"error":…}` but still exits `0` — the run
82
+ continues; never retry the batch or backfill by hand. `--actions-file` is exclusive with the
83
+ other mode flags (`--action`, `--decide`, `--close`, …): mixing them is a usage error (exit 2),
84
+ and a mixed call must be corrected, not silently split.
85
+
86
+ - **A `decide` action (0941)** — the driver never executes the DecisionMaker itself; it delegates
87
+ to the same app runner the engine registers, which writes the resultFile row (schemaVersion 1)
88
+ and returns the decision, then the delegate records the `action_runs` row (`kind=decide`)
89
+ through the same writer as every other action:
90
+
91
+ ```bash
92
+ bun "$SETUP_SCRIPT" --decide --run-id "$RUN_ID" --node <state-id> --options-json <options-file>
93
+ ```
94
+
95
+ The options JSON mirrors the YAML `decide` options (`id`, `method: choice|noul`, `question`,
96
+ `choices`/`default`, optional `evidence`, optional `minConfidence`, `resultFile`); paths resolve
97
+ against the project workdir. The decision never pauses and never fails the run for model
98
+ problems: a degraded outcome (feature switch off, no backend, error, timeout, low confidence)
99
+ prints `ok:true` with `degraded:true`, the declared `reason`, and `value = default`, and exits
100
+ `0` — route on the resultFile's `.value` with the declared file guards. Every row carries
101
+ `source: model|default` (0976 R2), and the delegate appends
102
+ `decide node=<id> value=<v> source=<s> reason=<r>` to the run log, so a declared-default
103
+ fallback is never read as a model decision. Only an invalid options
104
+ schema exits `1` (fail closed), and usage errors exit `2`. The `action_runs` trace row is
105
+ best-effort exactly like `--action`.
106
+
107
+ - **An operator pause is its own row (task 1136 R5)** — while the driver waits on a `hitl.confirm`
108
+ or `hitl.input`, emit the wait as its own boundary instead of charging it to the neighbouring
109
+ node:
110
+
111
+ ```bash
112
+ bun "$SETUP_SCRIPT" --action --run-id "$RUN_ID" --node <state-id> --kind operator-wait \
113
+ --status done --ok true --duration-ms <waited-ms> --project-root "$OWNING_TREE"
114
+ ```
115
+
116
+ `operator-wait` is a row kind, not a new state — the decision itself still belongs to the
117
+ declared node, and the wait's wall clock stays visible instead of inflating the node's duration.
118
+
119
+ - **At the run's declared terminal state** — before the driver reports the run complete, close the
120
+ row so a successful inline run is never left non-terminal for `spur workflow clean` to reap as
121
+ stale:
122
+
123
+ ```bash
124
+ bun "$SETUP_SCRIPT" --close --run-id "$RUN_ID" --status <done|failed|paused> --project-root "$OWNING_TREE"
125
+ ```
126
+
127
+ The close also produces the execution summary (1146 R1): its stdout carries
128
+ **`summaryFile`** (`.spur/run/<runId>-summary.md`) unless `--no-summary` was passed. Print that
129
+ file's contents verbatim under an `### Execution summary` heading — the driver generates nothing
130
+ itself, and the window is the run row's `started_at` → close time, so no `SINCE` is recorded. A
131
+ summary failure never changes the close verdict: the file then holds exactly
132
+ `Execution summary: n/a (<reason>)`.
133
+
134
+ `--status` is the declared terminal state's verdict, not a guess: a run that reached a terminal
135
+ state is `done`; a run halted by a failing action under its error policy is `failed`. On success
136
+ the close reports the recorded evidence: `{"ok":true,"runId":…,"actionRows":<n>}` plus
137
+ **`missingNodes`** — the declared states the driver recorded as entered that never emitted an
138
+ `action_runs` row (`{"missingNodes":["precheck","verify"]}`, empty array when the trace is
139
+ whole). `missingNodes` is a **trace defect reported, never a close failure**: the close still
140
+ exits `0`, and it exists so a gap is visible — the repair is to emit the missing rows in the next
141
+ run, never to backfill them (ADR-117).
142
+ After the close, the invoking command prints the measured
143
+ [execution summary](dev-operations.md#execution-summary) for `$RUN_ID` unless `--no-summary`.
144
+
145
+ **Zero-row done closes are a named failure (task 0975 R2).** A run closed `done` with **zero**
146
+ `action_runs` rows (`actionRows:0`) finalizes the row but exits `1` with
147
+ `{"ok":false,"code":"NO_ACTION_ROWS","actionRows":0}` — a run that claims success without a
148
+ single recorded action is exactly the untraced-runs gap ADR-117 closes, so the driver must
149
+ surface the code in its final report instead of reporting a clean close. The failure must NOT
150
+ be repaired by backfilling rows: the run row is already terminal, and hand-written rows are
151
+ forbidden (ADR-117) — record the finding and let the task's evidence show the gap. A `failed`
152
+ close with zero rows stays a clean `0` (a halt before the first boundary legitimately records
153
+ nothing), and any `done` close with `actionRows ≥ 1` exits `0`.
154
+
155
+ **Best-effort at the action boundary only (ADR-117, task 1136 R1/R2).** An `--action` persistence
156
+ failure is recorded — the delegate appends a `trace-emission-failed` line to
157
+ `.spur/memory/runs/<run-id>.md` and prints `{"ok":false}` on stdout — and the run continues to its
158
+ declared terminal state; the delegate exits `0` for that outcome and the driver must never treat an
159
+ emission failure as a run failure, retry it in a loop, or substitute a hand-written row. The
160
+ exception is the **run row itself**: a missing run row, a duration that cannot be measured, or a
161
+ start time before `runs.started_at` are correctness failures and exit `1`
162
+ (`RUN_NOT_FOUND`, `ACTION_DURATION_UNAVAILABLE`). The run-row closure (`--close`) is bookkeeping, not
163
+ trace emission, and is **not** best-effort either: a missing run row or a persistence failure exits
164
+ `1` with `{"ok":false}` and a named error (a missing row also carries `code:"RUN_NOT_FOUND"`),
165
+ because a silently `running` row is exactly the stale state `spur workflow clean` reaps as `failed`.
166
+ Exit `2` means the invocation itself was malformed (missing `--node`/`--kind`/`--status`/`--ok`, a
167
+ miscased `--ok`, or a malformed `--duration-ms`) and must be corrected, not ignored.
168
+
169
+ **Never suppress this output.** The driver reference forbids redirecting `inline-run-setup`,
170
+ `quality-gate` and `persist-out*` stdout/stderr to `/dev/null`; a static pin fails any reference
171
+ snippet that does. Suppressing a `RUN_NOT_FOUND` is how task 1132's 93 action rows disappeared.
172
+
173
+ Emission is not optional and not deferred: an inline run that skips it reintroduces the
174
+ 1,011-untraced-runs gap ADR-117 exists to close.
175
+
176
+ Transition guards are not advisory. Execute the declared guard exactly, in order, with the same
177
+ resolved variables and artifacts. `--no-lifecycle` remains bookkeeping only; the YAML's task checks,
178
+ verdict gate, record step, and done guard all remain authoritative.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: wayfinder
3
- description: "Chart a multi-session investigation map when the destination is too foggy to spec in one session. Creates a spur feature as the map, decomposes specifiable questions into spur tasks, and resolves them one at a time until the route becomes visible. Triggers: wayfind, chart a course, multi-session investigation, --wayfind."
3
+ description: "Chart a multi-session investigation map when the destination is too foggy to spec in one session. Creates a spur feature as the map, decomposes specifiable questions into spur tasks, and resolves them one at a time until the route becomes visible. Triggers: wayfind, chart a course, multi-session investigation, /sp:dev-find-way."
4
4
  license: Apache-2.0
5
5
  version: 1.0.0
6
6
  created_at: 2026-07-06
@@ -57,7 +57,7 @@ Activate sp:wayfinder when:
57
57
  | "multi-session investigation" | User expects multiple sessions to reach clarity |
58
58
  | "find the way to X" | Destination is named but the route is unknown |
59
59
  | "explore the solution space" | Open-ended exploration before committing |
60
- | `--wayfind` flag on `/sp:dev-brainstorm` | Power-user skip straight to charting |
60
+ | `/sp:dev-find-way` | The command entry — charting or one-ticket resolution in an isolated worktree |
61
61
 
62
62
  **NOT for:**
63
63
  - A clear spec that just needs decomposition (use `sp:spec-decomposition` instead)
@@ -110,7 +110,7 @@ Two modes. Either way, **never resolve more than one ticket per session.**
110
110
 
111
111
  Invoked when the operator has a loose idea and the destination itself is foggy. Charting IS one session's work — do not also resolve tickets.
112
112
 
113
- 0. **Branch first.** `git checkout -b wayfind/<destination-slug>` before touching the map. A session is what spans the fog edit and the tickets it graduates into — routinely separate commits — so the branch point is the only boundary that contains both. The `corpus.ungraduated-fog` gate measures `merge-base(origin/main, HEAD)..(working tree)`, which means it **cannot fire at all** on work done directly on the default branch. That is exactly how the 2026-08-07 incident shipped.
113
+ 0. **Worktree first.** Run in an isolated git worktree on its own branch — `/sp:dev-find-way` does this (`sp/wayfind-<slug>-<short-id>`, FF-merged on success). Never `git checkout -b` in a shared tree: it switches the checkout under every other agent using it. The `corpus.ungraduated-fog` gate measures `merge-base(origin/main, HEAD)..(working tree)`, so it **cannot fire at all** on the default branch — how the 2026-08-07 incident shipped.
114
114
  1. **Name the destination.** Run a discovery interview (one question at a time, always with a recommendation) to pin down what this map is finding its way to — the spec, decision, or change. The destination fixes the scope, so it's settled first.
115
115
  2. **Map the frontier breadth-first.** Fan out across the whole space rather than deep on any one thread, surfacing open decisions and the first steps takeable now. Use the decision-brief format for each HITL choice (see `../spur-dev/references/decision-brief.md`).
116
116
  3. **Create the map as a spur feature.** `spur feature create "<destination>"`. The feature description carries:
@@ -130,7 +130,7 @@ Invoked when the operator has a loose idea and the destination itself is foggy.
130
130
 
131
131
  Invoked when a map already exists (operator provides the feature ID). A ticket is **optional** — without one, pick the next frontier ticket, not the operator's preference. Pipeline runs: `references/pipeline-resolution.md`.
132
132
 
133
- 0. **Branch first.** `git checkout -b wayfind/<wbs>-<slug>`, for the same reason as charting: steps 6 and 7 below remove fog from the map, and the gate that checks the removal was paid for measures the branch, not the commit. On the default branch the check silently passes no matter what you delete.
133
+ 0. **Worktree first.** Same rule as charting (`sp/wayfind-<wbs>-<short-id>`): steps 6 and 7 remove fog from the map, and the gate that checks the removal measures the branch, not the commit. The `wip` claim lands only in the worktree until the merge, so `/sp:dev-find-way` excludes tickets held by other active `dev-find-way` worktree markers when picking the frontier.
134
134
  1. **Load the map** — the feature description (the low-res view), not every task body. Read the destination and Decisions-so-far to orient.
135
135
  2. **Pick the first frontier ticket.** Query: `spur task list --feature <id> --status todo` — the first open, unblocked, unclaimed task. If the operator named one, use it instead.
136
136
  3. **Claim it.** `spur task update <wbs> wip` — before any work.
@@ -195,13 +195,9 @@ Ruling something out of scope is a scoping act, not a step on the route. When a
195
195
 
196
196
  ## Invocation
197
197
 
198
- ### From `/sp:dev-brainstorm` (semi-automatic escalation)
198
+ ### From `/sp:dev-find-way` (primary)
199
199
 
200
- At the end of the discovery interview (Phase 1), the brainstorm command runs a **scope check**: "Can this be spec'd in one session, or is the destination itself still foggy?" If foggy, it offers wayfinding as the escalation path:
201
-
202
- > *"This is a multi-session investigation. Want me to chart a wayfinder map so we can work through it one decision at a time?"*
203
-
204
- The operator confirms before wayfinding begins — never silently escalate. The `--wayfind` flag on `/sp:dev-brainstorm` skips the prompt and enters wayfinding directly.
200
+ `/sp:dev-find-way "<idea>"` charts; `/sp:dev-find-way <feature-id> [<wbs>]` resolves one ticket. The command owns the worktree lifecycle and dispatches here. `/sp:dev-brainstorm` never charts: when its scope check finds a foggy destination it stops and recommends `/sp:dev-find-way` — the operator decides.
205
201
 
206
202
  ### Direct invocation
207
203
 
@@ -225,9 +221,9 @@ The operator invokes this skill directly: `Skill(skill="sp:wayfinder", args="<lo
225
221
  ## Red Flags
226
222
 
227
223
  - Resolving more than one ticket in a single session.
228
- - Charting or resolving directly on the default branch — the `corpus.ungraduated-fog` gate is branch-scoped, so on `main` fog can be deleted with no ticket and nothing complains.
224
+ - Charting or resolving outside an isolated worktree — on the default branch the branch-scoped `corpus.ungraduated-fog` gate never fires; a `git checkout -b` in a shared tree moves every other agent's checkout.
229
225
  - Creating a map without a destination statement — the destination fixes scope; without it, every ticket is unbounded.
230
- - Auto-escalating to wayfinding without operator confirmation (except under `--wayfind`).
226
+ - Starting wayfinding without operator confirmation.
231
227
  - An empty or missing **### Not yet specified** section when the destination was described as foggy — fog that isn't written down is fog the next session can't see.
232
228
  - Skipping the claim step (`spur task update <wbs> wip`) before work — concurrent sessions may collide.
233
229
  - Pre-slicing fog into ticket stubs before the questions are sharp.
@@ -243,7 +239,7 @@ The operator invokes this skill directly: `Skill(skill="sp:wayfinder", args="<lo
243
239
 
244
240
  ### Charting verification
245
241
 
246
- - [ ] The session ran on its own branch, not the default branch (`git branch --show-current`).
242
+ - [ ] The session ran in its own worktree + branch (`git rev-parse --git-dir` ≠ `--git-common-dir`; branch is not the default).
247
243
  - [ ] Destination is a single, concrete sentence (not a paragraph, not a vague noun phrase).
248
244
  - [ ] Map feature exists (`spur feature show <id>` returns clean).
249
245
  - [ ] Feature description has all six sections: Destination, Notes, Open questions, Decisions so far (empty), Not yet specified, Out of scope.
@@ -256,7 +252,7 @@ The operator invokes this skill directly: `Skill(skill="sp:wayfinder", args="<lo
256
252
 
257
253
  ### Resolution verification
258
254
 
259
- - [ ] The session ran on its own branch, not the default branch (`git branch --show-current`).
255
+ - [ ] The session ran in its own worktree + branch (`git rev-parse --git-dir` ≠ `--git-common-dir`; branch is not the default).
260
256
  - [ ] Exactly one ticket was resolved this session.
261
257
  - [ ] The ticket was claimed (`wip`) before work began.
262
258
  - [ ] The resolution is recorded in the task body (not just a status transition — the answer is written down).
@@ -105,6 +105,29 @@
105
105
  "type": "boolean",
106
106
  "description": "When true, the engine pauses the run at this state for HITL (E3); resume with `spur workflow continue`."
107
107
  },
108
+ "display": {
109
+ "type": "object",
110
+ "additionalProperties": false,
111
+ "description": "Presentation-only metadata for plan/UI generators; the engine never reads it at run time.",
112
+ "required": ["phase"],
113
+ "properties": {
114
+ "phase": {
115
+ "type": "string",
116
+ "minLength": 1
117
+ },
118
+ "phaseTitle": {
119
+ "type": "string",
120
+ "minLength": 1
121
+ },
122
+ "title": {
123
+ "type": "string",
124
+ "minLength": 1
125
+ },
126
+ "show": {
127
+ "enum": ["plan", "on-entry"]
128
+ }
129
+ }
130
+ },
108
131
  "onEnter": {
109
132
  "type": "array",
110
133
  "items": {