@gobing-ai/spur 0.3.48 → 0.3.49
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.example.yaml +52 -4
- package/config/workflows/pr-review.yaml +338 -0
- package/package.json +8 -8
- package/plugins/sp/README.md +9 -6
- package/plugins/sp/commands/{dev-featurechange.md → dev-feature-change.md} +7 -10
- package/plugins/sp/commands/dev-find-issue.md +24 -19
- package/plugins/sp/commands/dev-find-next.md +3 -3
- package/plugins/sp/commands/dev-gtd.md +11 -12
- package/plugins/sp/commands/dev-history-load.md +63 -0
- package/plugins/sp/commands/dev-pr-review.md +39 -0
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/references/roles.md +25 -12
- package/plugins/sp/scripts/history-load.ts +400 -0
- package/plugins/sp/scripts/pr-reviewing.ts +867 -0
- package/plugins/sp/scripts/validate-commands.ts +33 -2
- package/plugins/sp/skills/code-implementation/SKILL.md +9 -1
- package/plugins/sp/skills/code-verification/SKILL.md +27 -28
- package/plugins/sp/skills/issue-finding/SKILL.md +6 -5
- package/plugins/sp/skills/issue-finding/references/session-formats.md +4 -2
- package/plugins/sp/skills/next-feature/SKILL.md +6 -6
- package/plugins/sp/skills/next-feature/references/handoff-routing.md +5 -5
- package/plugins/sp/skills/next-feature/references/signal-derivation.md +7 -2
- package/plugins/sp/skills/pr-reviewing/SKILL.md +285 -0
- package/plugins/sp/skills/spur-cli/references/features/hierarchy-mece.md +5 -5
- package/plugins/sp/skills/spur-cli/references/features.md +1 -1
- package/plugins/sp/skills/spur-dev/references/flag-glossary.md +14 -4
- package/schemas/spur-config.schema.json +20 -0
- package/spur.js +682 -222
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
*
|
|
9
9
|
* Gates:
|
|
10
10
|
* (a) heading whitelist — H1 title + the per-contract ordered section headings
|
|
11
|
-
* (b) frontmatter schema — description, argument-hint, allowed-tools; dev-only extras
|
|
11
|
+
* (b) frontmatter schema — description, argument-hint, allowed-tools; dev-only extras;
|
|
12
|
+
* real-YAML re-parse so malformed blocks cannot ship an empty description
|
|
12
13
|
* (c) target resolution — sp:<skill> refs, workflow files, procedure anchors
|
|
13
14
|
* (d) allowed-tools coherence — Skill present iff body contains Skill() call
|
|
14
15
|
* (e) dev-command argument contract — syntax-only hint, Argument Flags table columns,
|
|
@@ -19,6 +20,7 @@
|
|
|
19
20
|
|
|
20
21
|
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
21
22
|
import { join, resolve } from 'node:path';
|
|
23
|
+
import { parse as parseYaml } from 'yaml';
|
|
22
24
|
|
|
23
25
|
// ─── Types ──────────────────────────────────────────────────────────────────
|
|
24
26
|
|
|
@@ -53,6 +55,8 @@ interface ParsedCommand {
|
|
|
53
55
|
readonly description: string | undefined;
|
|
54
56
|
readonly argumentHint: string | undefined;
|
|
55
57
|
readonly allowedTools: string[] | undefined;
|
|
58
|
+
/** Set when the frontmatter fails a real YAML parse or yields an empty description. */
|
|
59
|
+
readonly frontmatterYamlProblem: string | undefined;
|
|
56
60
|
readonly body: string;
|
|
57
61
|
}
|
|
58
62
|
|
|
@@ -68,6 +72,30 @@ function parseCommand(filePath: string, name: string): ParsedCommand {
|
|
|
68
72
|
const argumentHint = extractYamlField(fm, 'argument-hint');
|
|
69
73
|
const allowedTools = extractYamlList(fm, 'allowed-tools');
|
|
70
74
|
|
|
75
|
+
// The regex extractions above feed the per-field gates, but they cannot see
|
|
76
|
+
// malformed YAML: `description: >-` followed by unindented keys reads as a
|
|
77
|
+
// present description to the regex while a real YAML parser folds the next
|
|
78
|
+
// keys into the block scalar — the bug that shipped an empty description to
|
|
79
|
+
// superskill install for dev-feature-change. Gate (b) re-checks with a real
|
|
80
|
+
// parse so that class cannot regress.
|
|
81
|
+
let frontmatterYamlProblem: string | undefined;
|
|
82
|
+
if (fm.trim() !== '') {
|
|
83
|
+
try {
|
|
84
|
+
const parsed: unknown = parseYaml(fm);
|
|
85
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
86
|
+
frontmatterYamlProblem = 'frontmatter is not a YAML mapping';
|
|
87
|
+
} else {
|
|
88
|
+
const parsedDescription = (parsed as Record<string, unknown>).description;
|
|
89
|
+
if (typeof parsedDescription !== 'string' || parsedDescription.trim() === '') {
|
|
90
|
+
frontmatterYamlProblem = 'frontmatter description is empty after YAML parsing';
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
} catch (error) {
|
|
94
|
+
const detail = error instanceof Error ? error.message.split('\n')[0] : String(error);
|
|
95
|
+
frontmatterYamlProblem = `frontmatter is not valid YAML: ${detail}`;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
71
99
|
const lines = body.split('\n');
|
|
72
100
|
const title = lines[0]?.startsWith('# ') ? lines[0].slice(2).trim() : '';
|
|
73
101
|
|
|
@@ -90,7 +118,7 @@ function parseCommand(filePath: string, name: string): ParsedCommand {
|
|
|
90
118
|
}
|
|
91
119
|
}
|
|
92
120
|
|
|
93
|
-
return { name, title, headings, description, argumentHint, allowedTools, body };
|
|
121
|
+
return { name, title, headings, description, argumentHint, allowedTools, frontmatterYamlProblem, body };
|
|
94
122
|
}
|
|
95
123
|
|
|
96
124
|
/** Extract a plain YAML string field (handles quoted and unquoted). */
|
|
@@ -212,6 +240,9 @@ function checkHeadingWhitelist(cmd: ParsedCommand): readonly Violation[] {
|
|
|
212
240
|
|
|
213
241
|
function checkFrontmatterSchema(cmd: ParsedCommand): readonly Violation[] {
|
|
214
242
|
const violations: Violation[] = [];
|
|
243
|
+
if (cmd.frontmatterYamlProblem) {
|
|
244
|
+
violations.push({ command: cmd.name, gate: 'b', message: cmd.frontmatterYamlProblem });
|
|
245
|
+
}
|
|
215
246
|
if (!cmd.description) {
|
|
216
247
|
violations.push({ command: cmd.name, gate: 'b', message: 'missing frontmatter description' });
|
|
217
248
|
}
|
|
@@ -89,6 +89,14 @@ only burns wall clock and context budget.
|
|
|
89
89
|
`bun test <file> --test-name-pattern "<test>"`, or `bunx tsc --noEmit` on a single package.
|
|
90
90
|
- **NEVER run** `bun run test`, `bun run spur-check`, `bun run check`, or any other full-suite /
|
|
91
91
|
project-gate command from inside implement. These belong to the pipeline's `test` hop.
|
|
92
|
+
- **Full-suite budget: at most 2 per task** (task 0436 R2) — counted across the whole task run
|
|
93
|
+
(implement probes + the pipeline `test` hop + verify/recheck), not per step. When a check fails,
|
|
94
|
+
run the narrow target (`bun test <file> --test-name-pattern <test>`) to green before any full
|
|
95
|
+
suite; reach for the second full run only when the narrow target cannot reproduce the failure.
|
|
96
|
+
- **Consolidate dogfood runs**: one combined real-data execution that exercises all scenarios,
|
|
97
|
+
not N near-identical `--dry-run`/real invocations of the same script. If you find yourself
|
|
98
|
+
rerunning the same dogfood command with one flag changed, stop and fold the variants into a
|
|
99
|
+
single run — repeated identical commands are loop-detector findings and pure cost.
|
|
92
100
|
- If a targeted probe reveals a failure you cannot fix within implement scope, note it in
|
|
93
101
|
`## Solution` and let the `test` hop's fixall handle it — do not pre-empt the gate.
|
|
94
102
|
|
|
@@ -146,7 +154,7 @@ reproduce → isolate → minimal fix → regression guard.
|
|
|
146
154
|
## Common Rationalizations
|
|
147
155
|
|
|
148
156
|
| Rationalization | Reality |
|
|
149
|
-
|
|
157
|
+
| --- | --- |
|
|
150
158
|
| "The spec is clear — I don't need to read the callers." | Code that looks orthogonal is how regressions ship (R5). Read the exports you touch and their immediate callers before writing. |
|
|
151
159
|
| "I'll add the tests in a follow-up." | Untested production code is unverified code. The task's test step is not optional; behavior ships with its test. |
|
|
152
160
|
| "This abstraction will be useful later." | Speculative abstraction is complexity without a caller (R2). Build for the requirement in front of you; add the seam when the second use arrives. |
|
|
@@ -239,6 +239,11 @@ printf '...' > /tmp/<wbs>-testing.md
|
|
|
239
239
|
spur task update <wbs> --section Testing --from-file /tmp/<wbs>-testing.md
|
|
240
240
|
```
|
|
241
241
|
|
|
242
|
+
> **Corrections: the answer file is the source of truth.** `spur task record` re-transcribes
|
|
243
|
+
> `## Testing` from the verdict artifact, overwriting `--section Testing` writes — direct section
|
|
244
|
+
> fixes are futile. Fix `.spur/run/<wbs>-verify-answer.txt` → `spur task verdict <wbs>
|
|
245
|
+
> --from-answer <file>` → re-record. `--section` is initial authorship only.
|
|
246
|
+
|
|
242
247
|
> **Do not write `## Review` directly in verify mode.** The `## Review` section is owned by the
|
|
243
248
|
> `review` step (`/sp:dev-review`), which dispatches `functional-review` + `code-verification`
|
|
244
249
|
> review mode + `code-improvement`. The `record` step backfills `## Review` from the verdict
|
|
@@ -283,9 +288,7 @@ Verdict: PASS
|
|
|
283
288
|
| P4 | — | — | No P1–P3 findings; verify verdict PASS |
|
|
284
289
|
```
|
|
285
290
|
|
|
286
|
-
The per-requirement traceability table MUST use `| Req | Status | Evidence |` (exactly this header, no `R#`/`R`/`Requirement` variant
|
|
287
|
-
|
|
288
|
-
**MUST NOT:** use `| R# | ... |` as the sole id header without `Status` in column 2.
|
|
291
|
+
The per-requirement traceability table MUST use `| Req | Status | Evidence |` (exactly this header, no `R#`/`R`/`Requirement` variant — `Status` in column 2 — and no extra columns between Req and Status). The Acceptance Criteria table MUST use `| AC | Status | Evidence Type | Evidence |`.
|
|
289
292
|
**MUST NOT:** place a `Severity` column between `Req` and `Status` in the authoring contract.
|
|
290
293
|
The parser is tolerant of these variants (defense-in-depth), but the authoring contract is
|
|
291
294
|
canonical.
|
|
@@ -348,8 +351,7 @@ mutation is discoverable from the tracked task file alone, without diffing untra
|
|
|
348
351
|
### Step 13 — Shippable readiness gate (feature-level)
|
|
349
352
|
|
|
350
353
|
Per-task PASS is **not** the same as “this feature is ready to ship.” After Steps 11–12, when the
|
|
351
|
-
gate is **active**, evaluate feature AC satisfaction via the existing CLI
|
|
352
|
-
framework).
|
|
354
|
+
gate is **active**, evaluate feature AC satisfaction via the existing CLI.
|
|
353
355
|
|
|
354
356
|
**When active**
|
|
355
357
|
|
|
@@ -371,16 +373,17 @@ framework).
|
|
|
371
373
|
`docs/.spur/run` or other nested `.spur` trees). Ephemeral scratch may use `/tmp` or
|
|
372
374
|
`/private/tmp`. Requirement / AC row `id`s in the verdict MUST match feature scenario titles
|
|
373
375
|
(or `AC-N` aliases) so satisfaction can mark MET.
|
|
374
|
-
2. Run:
|
|
376
|
+
2. Run (0568 R6 monorepo-safe: SPUR_BIN env > local CLI > PATH):
|
|
375
377
|
|
|
376
378
|
```bash
|
|
377
|
-
|
|
378
|
-
|
|
379
|
+
SPUR_BIN="${SPUR_BIN:-$([ -f apps/cli/src/index.ts ] && echo 'bun apps/cli/src/index.ts' || echo spur)}"
|
|
380
|
+
$SPUR_BIN feature check <featureId> --json
|
|
381
|
+
$SPUR_BIN task list --feature <featureId> --json
|
|
379
382
|
```
|
|
380
383
|
|
|
381
384
|
3. Classify **Shippable: PASS** only if **all** of:
|
|
382
385
|
- No finding whose code/message indicates **linked but unverified** scenarios
|
|
383
|
-
(`L4_SCENARIO_UNVERIFIED`
|
|
386
|
+
(`L4_SCENARIO_UNVERIFIED`).
|
|
384
387
|
- No **orphan / uncovered** feature scenarios (`L4_ORPHAN_SCENARIOS`,
|
|
385
388
|
`L4_UNCOVERED_FEATURE_SCENARIO` / no covering task).
|
|
386
389
|
- No **incomplete** linked tasks: every task with this `feature_id` is `done` or `cancelled`
|
|
@@ -424,16 +427,14 @@ Full flag matrix and ops notes: [spur-dev/references/dev-operations.md](../spur-
|
|
|
424
427
|
|
|
425
428
|
### Step 14 — Report
|
|
426
429
|
|
|
427
|
-
Show the per-task verdict,
|
|
428
|
-
**Shippable:** line from Step 13 when applicable. Under the pipeline the
|
|
429
|
-
|
|
430
|
+
Show the per-task verdict, per-requirement table, gate outcome (cleared/blocked), and the
|
|
431
|
+
**Shippable:** line from Step 13 when applicable. Under the pipeline the done-gate consumes the
|
|
432
|
+
verdict; for a direct `/sp:dev-verify` invocation the full report is the operator's summary.
|
|
430
433
|
|
|
431
|
-
**`--next` on
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
machine signal; the report line is the operator-visible summary - both must agree so a terminal-task
|
|
436
|
-
re-audit cannot be misread as a successful `testing -> done` transition.
|
|
434
|
+
**`--next` on a terminal task (no-op surfacing).** The transition cannot fire; the verify report
|
|
435
|
+
line MUST state the no-op itself (e.g. `--next: no-op - task already terminal (<status>)`). The CLI
|
|
436
|
+
print is the machine signal, the report line the operator summary — both must agree so a terminal
|
|
437
|
+
re-audit is never misread as a successful `testing -> done` (dev-verify.md `--next` chain).
|
|
437
438
|
|
|
438
439
|
---
|
|
439
440
|
|
|
@@ -513,14 +514,12 @@ Do **not** use this skill for:
|
|
|
513
514
|
- **`sp:spur-dev`** — the execution-half umbrella that drives the pipeline this skill gates.
|
|
514
515
|
- [references/code-improvement.md](references/code-improvement.md) — architecture-improvement lens
|
|
515
516
|
for module depth, seam placement, locality, coupling, and testability.
|
|
516
|
-
- **`sp:functional-review`** —
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
blocker/major/minor/advisory). When review findings expose structural friction rather than a
|
|
523
|
-
localized defect, dispatch this skill; see [../code-improvement/SKILL.md](../code-improvement/SKILL.md).
|
|
517
|
+
- **`sp:functional-review`** — requirements-traceability peer review (R{n} → file:line,
|
|
518
|
+
per-requirement MET/PARTIAL/UNMET). Dispatch when the `review` dimension needs functional
|
|
519
|
+
traceability, not just SECUA: [../functional-review/SKILL.md](../functional-review/SKILL.md).
|
|
520
|
+
- **`sp:code-improvement`** — architectural-deepening peer review (5 signals, severity
|
|
521
|
+
blocker/major/minor/advisory). Dispatch for structural friction rather than localized
|
|
522
|
+
defects: [../code-improvement/SKILL.md](../code-improvement/SKILL.md).
|
|
524
523
|
|
|
525
524
|
---
|
|
526
525
|
|
|
@@ -533,5 +532,5 @@ directly: `Skill(skill="sp:code-verification", args="verify <wbs> --fix all")`.
|
|
|
533
532
|
|
|
534
533
|
### Codex / OpenClaw / OpenCode / Antigravity
|
|
535
534
|
|
|
536
|
-
Run `spur` CLI via Bash; parse `--json`. Invoke
|
|
537
|
-
|
|
535
|
+
Run `spur` CLI via Bash; parse `--json`. Invoke the skill directly — the skill is the SSOT; the
|
|
536
|
+
commands are thin wrappers.
|
|
@@ -139,11 +139,12 @@ sessions (typed ETL via `spur history` — or raw JSONL under the three fallback
|
|
|
139
139
|
**Primary path (typed sources):** `spur history report --mode forensics` (task 0555).
|
|
140
140
|
|
|
141
141
|
```bash
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
142
|
+
# 0568 R4: SPUR_BIN env > local CLI > PATH — stale PATH spur fails history import.
|
|
143
|
+
SPUR_BIN="${SPUR_BIN:-$([ -f apps/cli/src/index.ts ] && echo 'bun apps/cli/src/index.ts' || echo spur)}"
|
|
144
|
+
|
|
145
|
+
$SPUR_BIN history import --source <source> --json # checkpoint resume
|
|
146
|
+
$SPUR_BIN history analyze --json # writes versioned artifact (0554)
|
|
147
|
+
$SPUR_BIN history report --mode forensics # pure renderer; latest artifact pointer
|
|
147
148
|
```
|
|
148
149
|
|
|
149
150
|
The forensics renderer emits **8 CLI-derivable sections**: Session Data Summary, Tool Breakdown,
|
|
@@ -84,8 +84,10 @@ each file, the session key is the JSONL filename stem (importer `sessionIdFromCo
|
|
|
84
84
|
analyze per key:
|
|
85
85
|
|
|
86
86
|
```bash
|
|
87
|
-
spur
|
|
88
|
-
|
|
87
|
+
# Monorepo-safe spur resolution (0568 R6): SPUR_BIN env > monorepo-local CLI > PATH.
|
|
88
|
+
SPUR_BIN="${SPUR_BIN:-$([ -f apps/cli/src/index.ts ] && echo 'bun apps/cli/src/index.ts' || echo spur)}"
|
|
89
|
+
$SPUR_BIN history import --source omp --file <absolute-file> --mode force-file --json
|
|
90
|
+
$SPUR_BIN history analyze --session <filename-stem> --json
|
|
89
91
|
```
|
|
90
92
|
|
|
91
93
|
ETL owns token/cost/message/tool/loop/assistant-duration aggregates; raw JSONL stays authoritative
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: next-feature
|
|
3
|
-
description: "Prompt-first feature frontier prioritizer — answers 'which feature should we work on now?' by deriving importance/urgency from corpus, git, and authority-doc evidence, and emits rank-distorting tree defects as proposals /sp:dev-
|
|
3
|
+
description: "Prompt-first feature frontier prioritizer — answers 'which feature should we work on now?' by deriving importance/urgency from corpus, git, and authority-doc evidence, and emits rank-distorting tree defects as proposals /sp:dev-feature-change consumes. Triggers: find next, which feature, feature ranking, frontier priority, what should I work on."
|
|
4
4
|
license: Apache-2.0
|
|
5
5
|
version: 1.0.0
|
|
6
6
|
metadata:
|
|
@@ -30,7 +30,7 @@ A prompt-first prioritizer that answers **"which feature should we work on now?"
|
|
|
30
30
|
`sp:next-router` deliberately does not answer (`routing-table.md` §0 step 1c: target omitted → not
|
|
31
31
|
v1). It derives importance and urgency from evidence already in the corpus, ranks the actionable
|
|
32
32
|
frontier in **tiers with per-candidate evidence**, and emits rank-distorting tree defects as
|
|
33
|
-
**proposals** `/sp:dev-
|
|
33
|
+
**proposals** `/sp:dev-feature-change` consumes.
|
|
34
34
|
|
|
35
35
|
**Honesty contract:** prompt-first. The model applies the rubric; existing deterministic tools
|
|
36
36
|
(`spur feature|task … --json`, `git`, `rg`) gather facts. No TypeScript analyzer, no numeric scores,
|
|
@@ -39,7 +39,7 @@ in this corpus it is 76% one value (0493 measurement).
|
|
|
39
39
|
|
|
40
40
|
**Propose, never apply.** This skill performs no `spur feature move` and writes nothing under
|
|
41
41
|
`docs/features/**`. The only path from a structure proposal to a changed tree is
|
|
42
|
-
`/sp:dev-
|
|
42
|
+
`/sp:dev-feature-change` (dry-run → confirm → apply). Ranking runs are read-only; the sole exception is
|
|
43
43
|
`--task`, which after an **operator confirm** (interactive, or auto-accepted under `--auto`)
|
|
44
44
|
dispatches `/sp:dev-plan` and `/sp:dev-refineall` — commands that write `docs/tasks*/` through their
|
|
45
45
|
own gates. This skill still creates no tasks itself.
|
|
@@ -52,7 +52,7 @@ own gates. This skill still creates no tasks itself.
|
|
|
52
52
|
**Do NOT use for:**
|
|
53
53
|
|
|
54
54
|
- Advancing an already-chosen task or feature — that is `/sp:dev-next` (`sp:next-router`).
|
|
55
|
-
- Applying tree changes — that is `/sp:dev-
|
|
55
|
+
- Applying tree changes — that is `/sp:dev-feature-change` (feature F31).
|
|
56
56
|
- Task-level ordering inside a feature — next-router's TABLE A owns that.
|
|
57
57
|
|
|
58
58
|
## Protocol
|
|
@@ -106,7 +106,7 @@ Run the steps in order. Each step's depth lives in its reference; this file is t
|
|
|
106
106
|
- Ranking a feature whose actionability gate fails. Gate first, rank second.
|
|
107
107
|
- Emitting a numeric score (WSJF/RICE arithmetic) from absent value/effort estimates.
|
|
108
108
|
- Copying the B3 predicate into this skill. Cite it; read it at runtime.
|
|
109
|
-
- Any `spur feature move`, or writing proposals anywhere `docs/features/**` —
|
|
109
|
+
- Any `spur feature move`, or writing proposals anywhere `docs/features/**` — feature-change owns apply.
|
|
110
110
|
- Decomposing a feature here, or calling `spur task create` / `spur task batch-create` under `--task`.
|
|
111
111
|
Dispatch `/sp:dev-plan`; it owns decomposition and the batch-create schema gate. Equally:
|
|
112
112
|
dispatching under `--auto` without `--task` (there is no confirm to skip), auto-accepting a target
|
|
@@ -123,7 +123,7 @@ Run the steps in order. Each step's depth lives in its reference; this file is t
|
|
|
123
123
|
| [references/signal-derivation.md](references/signal-derivation.md) | Sync precondition, B3 runtime citation, per-signal derivation commands, degenerate-spread rejection |
|
|
124
124
|
| [references/ranking-rubric.md](references/ranking-rubric.md) | Tier definitions, tie-breaks, evidence-per-candidate output contract |
|
|
125
125
|
| [references/proposal-contract.md](references/proposal-contract.md) | D1–D4 defect set, evidence bar, mapping-schema conformance, silence |
|
|
126
|
-
| [references/handoff-routing.md](references/handoff-routing.md) |
|
|
126
|
+
| [references/handoff-routing.md](references/handoff-routing.md) | feature-change handoff, next-router seam, the `--task` tier→hop routing table and its confirm contract |
|
|
127
127
|
|
|
128
128
|
Grounding: tickets 0493 (measured signals), 0494 (reuse ledger), 0495 (defect contract) under
|
|
129
129
|
feature H12.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Handoff routing —
|
|
1
|
+
# Handoff routing — feature-change handoff, next-router seam, conditional dispatch
|
|
2
2
|
|
|
3
3
|
## The seam with `/sp:dev-next` (next-router)
|
|
4
4
|
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
- When the operator picks a winner from the ranked report, the printed handoff line is:
|
|
9
9
|
`/sp:dev-next <feature-id>`.
|
|
10
10
|
|
|
11
|
-
## The handoff to `/sp:dev-
|
|
11
|
+
## The handoff to `/sp:dev-feature-change` (feature F31)
|
|
12
12
|
|
|
13
13
|
Defect proposals follow the 0495 Artifact C boundary, traced end to end:
|
|
14
14
|
|
|
@@ -16,9 +16,9 @@ Defect proposals follow the 0495 Artifact C boundary, traced end to end:
|
|
|
16
16
|
| --- | --- | --- |
|
|
17
17
|
| 1. Detect + emit proposal rows | this skill | No |
|
|
18
18
|
| 2. Handoff | printed inline in the report (report reading of OQ1) | No |
|
|
19
|
-
| 3. `--dry-run` | `/sp:dev-
|
|
19
|
+
| 3. `--dry-run` | `/sp:dev-feature-change` | No |
|
|
20
20
|
| 4. Confirm | operator | — |
|
|
21
|
-
| 5. `--apply` (`spur feature move`) | `/sp:dev-
|
|
21
|
+
| 5. `--apply` (`spur feature move`) | `/sp:dev-feature-change` only | **Yes — sole writer** |
|
|
22
22
|
|
|
23
23
|
There is no path from this skill to a mutated tree that bypasses step 4. Proposal rows are printed
|
|
24
24
|
inline in the default report; writing them into `docs/plans/feature-tree-restructure-map.md` as new
|
|
@@ -83,7 +83,7 @@ inside dispatched children remain governed by their own contracts (`--approve-ta
|
|
|
83
83
|
### What `--task` does not change
|
|
84
84
|
|
|
85
85
|
The defect half is untouched: still no `spur feature move`, still nothing written under
|
|
86
|
-
`docs/features/**`, still `/sp:dev-
|
|
86
|
+
`docs/features/**`, still `/sp:dev-feature-change` as the sole applier of structure proposals. `--task`
|
|
87
87
|
adds one gated path to `docs/tasks*/`, through commands that own their own gates.
|
|
88
88
|
|
|
89
89
|
## Where outputs go
|
|
@@ -7,7 +7,10 @@ cannot be derived is reported as **unavailable**, never fabricated.
|
|
|
7
7
|
## §0 — Sync-first precondition (step zero)
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
spur
|
|
10
|
+
# Monorepo-safe spur resolution (0568 R6): SPUR_BIN env > monorepo-local CLI > PATH.
|
|
11
|
+
# Defined once here; §1+ reuse $SPUR_BIN — §0 runs first by construction.
|
|
12
|
+
SPUR_BIN="${SPUR_BIN:-$([ -f apps/cli/src/index.ts ] && echo 'bun apps/cli/src/index.ts' || echo spur)}"
|
|
13
|
+
$SPUR_BIN feature sync --all --dry-run --json
|
|
11
14
|
```
|
|
12
15
|
|
|
13
16
|
Feature `status` is manual bookkeeping and drifts (0493: 24 of 25 rankable features would change
|
|
@@ -34,7 +37,7 @@ renamed, the fallback key is its content: "frontier = open ∧ unblocked".)
|
|
|
34
37
|
Inputs per candidate feature:
|
|
35
38
|
|
|
36
39
|
```bash
|
|
37
|
-
|
|
40
|
+
$SPUR_BIN task list --feature <id> --json
|
|
38
41
|
```
|
|
39
42
|
|
|
40
43
|
`task list --feature` is **active-folder-only**: it enumerates tasks in the active task folder
|
|
@@ -52,9 +55,11 @@ is not authoritative — a frontier task may be archived outside it. Run the fal
|
|
|
52
55
|
the feature's row as an **anomaly hint** only: it may flag the feature without naming a WBS. The
|
|
53
56
|
sync reason is never treated as a WBS source — no WBS is ever inferred from sync prose.
|
|
54
57
|
2. Scan the whole corpus for linked tasks:
|
|
58
|
+
|
|
55
59
|
```bash
|
|
56
60
|
rg -l '^feature_id: "?<id>"?$' docs/tasks*/
|
|
57
61
|
```
|
|
62
|
+
|
|
58
63
|
Corpus ids are `[A-Z][0-9]+`-shaped, so `<id>` is regex-safe as-is; escape metacharacters if a
|
|
59
64
|
non-conforming id ever appears.
|
|
60
65
|
3. Parse the leading WBS from each matched basename; resolve every corpus-only WBS (not present in
|