@gobing-ai/spur 0.3.41 → 0.3.43
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 +15 -0
- package/README.md +15 -1
- package/config/templates/AGENTS.md +5 -3
- package/config/workflows/task-pipeline.yaml +113 -12
- package/package.json +15 -12
- package/plugins/README.md +655 -0
- package/plugins/sp/README.md +621 -0
- package/plugins/sp/agents/expert-spur.md +109 -0
- package/plugins/sp/agents/super-coder.md +126 -0
- package/plugins/sp/agents/super-planner.md +283 -0
- package/plugins/sp/agents/super-reviewer.md +154 -0
- package/plugins/sp/commands/dev-arch.md +30 -0
- package/plugins/sp/commands/dev-brainstorm.md +40 -0
- package/plugins/sp/commands/dev-changelog.md +28 -0
- package/plugins/sp/commands/dev-daily.md +29 -0
- package/plugins/sp/commands/dev-debug.md +29 -0
- package/plugins/sp/commands/dev-dogfood.md +33 -0
- package/plugins/sp/commands/dev-featurechange.md +130 -0
- package/plugins/sp/commands/dev-find-conflict.md +51 -0
- package/plugins/sp/commands/dev-find-issue.md +76 -0
- package/plugins/sp/commands/dev-find-next.md +60 -0
- package/plugins/sp/commands/dev-fixall.md +33 -0
- package/plugins/sp/commands/dev-gitmsg.md +28 -0
- package/plugins/sp/commands/dev-gtd.md +97 -0
- package/plugins/sp/commands/dev-handover.md +26 -0
- package/plugins/sp/commands/dev-idea.md +50 -0
- package/plugins/sp/commands/dev-next.md +31 -0
- package/plugins/sp/commands/dev-parallel.md +30 -0
- package/plugins/sp/commands/dev-plan.md +46 -0
- package/plugins/sp/commands/dev-refine.md +48 -0
- package/plugins/sp/commands/dev-refineall.md +65 -0
- package/plugins/sp/commands/dev-refresh.md +32 -0
- package/plugins/sp/commands/dev-reverse.md +32 -0
- package/plugins/sp/commands/dev-review.md +31 -0
- package/plugins/sp/commands/dev-run.md +63 -0
- package/plugins/sp/commands/dev-runall.md +83 -0
- package/plugins/sp/commands/dev-simplify.md +39 -0
- package/plugins/sp/commands/dev-unit.md +29 -0
- package/plugins/sp/commands/dev-verify.md +48 -0
- package/plugins/sp/commands/dev-verifyall.md +72 -0
- package/plugins/sp/commands/dev-wrap.md +48 -0
- package/plugins/sp/commands/dev-wrapall.md +51 -0
- package/plugins/sp/commands/rule-add.md +18 -0
- package/plugins/sp/commands/rule-refine.md +18 -0
- package/plugins/sp/commands/rule-scan.md +18 -0
- package/plugins/sp/commands/spur-init.md +22 -0
- package/plugins/sp/commands/workflow-add.md +18 -0
- package/plugins/sp/commands/workflow-refine.md +18 -0
- package/plugins/sp/hooks/agent-hint.ts +60 -0
- package/plugins/sp/hooks/careful-guard.ts +147 -0
- package/plugins/sp/hooks/context-post-tool.ts +325 -0
- package/plugins/sp/hooks/context-session-start.ts +182 -0
- package/plugins/sp/hooks/context-session-stop.ts +95 -0
- package/plugins/sp/hooks/hooks.json +51 -0
- package/plugins/sp/hooks/pi/guard-extension.ts +341 -0
- package/plugins/sp/hooks/task-write-guard.ts +84 -0
- package/plugins/sp/plugin.json +8 -0
- package/plugins/sp/scripts/batch-preflight.ts +204 -0
- package/plugins/sp/scripts/daily-summary/daily-summary.ts +606 -0
- package/plugins/sp/scripts/daily-summary/logger.ts +28 -0
- package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.ts +363 -0
- package/plugins/sp/scripts/dogfood-testing/validate-report.ts +168 -0
- package/plugins/sp/scripts/feature-sync-bounded.ts +451 -0
- package/plugins/sp/scripts/stage-registry-adapter.ts +1341 -0
- package/plugins/sp/scripts/task-size-precheck.ts +202 -0
- package/plugins/sp/scripts/validate-commands.ts +651 -0
- package/plugins/sp/scripts/validate-flag-contracts.ts +875 -0
- package/plugins/sp/skills/brainstorm/SKILL.md +361 -0
- package/plugins/sp/skills/brainstorm/agents/openai.yaml +14 -0
- package/plugins/sp/skills/brainstorm/examples/ideation-example.md +209 -0
- package/plugins/sp/skills/brainstorm/metadata.openclaw +7 -0
- package/plugins/sp/skills/brainstorm/references/grilling-interview.md +68 -0
- package/plugins/sp/skills/brainstorm/references/workflows.md +255 -0
- package/plugins/sp/skills/branch-workflow/SKILL.md +109 -0
- package/plugins/sp/skills/branch-workflow/references/branch-lifecycle.md +69 -0
- package/plugins/sp/skills/branch-workflow/references/worktree-patterns.md +100 -0
- package/plugins/sp/skills/code-implementation/SKILL.md +203 -0
- package/plugins/sp/skills/code-implementation/references/debugging.md +64 -0
- package/plugins/sp/skills/code-implementation/references/implementation-patterns.md +113 -0
- package/plugins/sp/skills/code-improvement/SKILL.md +231 -0
- package/plugins/sp/skills/code-improvement/references/deepening-signals.md +205 -0
- package/plugins/sp/skills/code-review/SKILL.md +112 -0
- package/plugins/sp/skills/code-review/references/receiving-code-review.md +70 -0
- package/plugins/sp/skills/code-review/references/review-lenses.md +117 -0
- package/plugins/sp/skills/code-review/references/self-review-checklist.md +53 -0
- package/plugins/sp/skills/code-review/references/verification-before-completion.md +68 -0
- package/plugins/sp/skills/code-simplification/SKILL.md +225 -0
- package/plugins/sp/skills/code-testing/SKILL.md +108 -0
- package/plugins/sp/skills/code-testing/references/stacks/bun-ts.md +165 -0
- package/plugins/sp/skills/code-testing/references/stacks/go.md +88 -0
- package/plugins/sp/skills/code-testing/references/stacks/python.md +117 -0
- package/plugins/sp/skills/code-testing/references/test-loop-breaker.md +37 -0
- package/plugins/sp/skills/code-testing/references/test-output-discipline.md +52 -0
- package/plugins/sp/skills/code-testing/references/unit-testing.md +212 -0
- package/plugins/sp/skills/code-verification/SKILL.md +535 -0
- package/plugins/sp/skills/code-verification/references/code-improvement.md +39 -0
- package/plugins/sp/skills/code-verification/references/secu-review.md +94 -0
- package/plugins/sp/skills/code-verification/references/verdict-schema.md +141 -0
- package/plugins/sp/skills/conflict-finding/SKILL.md +254 -0
- package/plugins/sp/skills/conflict-finding/references/authority-resolution.md +258 -0
- package/plugins/sp/skills/conflict-finding/references/comparison-protocol.md +277 -0
- package/plugins/sp/skills/conflict-finding/references/finding-contract.md +346 -0
- package/plugins/sp/skills/conflict-finding/references/remediation-routing.md +276 -0
- package/plugins/sp/skills/daily-summary/SKILL.md +171 -0
- package/plugins/sp/skills/daily-summary/agents/openai.yaml +13 -0
- package/plugins/sp/skills/doc-evolve/SKILL.md +175 -0
- package/plugins/sp/skills/doc-evolve/references/operations.md +93 -0
- package/plugins/sp/skills/dogfood-testing/SKILL.md +608 -0
- package/plugins/sp/skills/dogfood-testing/references/monitor-ledger.md +211 -0
- package/plugins/sp/skills/dogfood-testing/references/report-template.md +351 -0
- package/plugins/sp/skills/doubt-driven-development/SKILL.md +136 -0
- package/plugins/sp/skills/functional-review/SKILL.md +364 -0
- package/plugins/sp/skills/functional-review/references/verdict-schema.md +133 -0
- package/plugins/sp/skills/indexed-context/SKILL.md +164 -0
- package/plugins/sp/skills/issue-finding/SKILL.md +442 -0
- package/plugins/sp/skills/issue-finding/agents/openai.yaml +15 -0
- package/plugins/sp/skills/issue-finding/examples/expected-findings.json +27 -0
- package/plugins/sp/skills/issue-finding/examples/session-test-loop.jsonl +23 -0
- package/plugins/sp/skills/issue-finding/references/session-formats.md +151 -0
- package/plugins/sp/skills/next-feature/SKILL.md +125 -0
- package/plugins/sp/skills/next-feature/references/handoff-routing.md +92 -0
- package/plugins/sp/skills/next-feature/references/proposal-contract.md +49 -0
- package/plugins/sp/skills/next-feature/references/ranking-rubric.md +52 -0
- package/plugins/sp/skills/next-feature/references/signal-derivation.md +67 -0
- package/plugins/sp/skills/next-router/SKILL.md +181 -0
- package/plugins/sp/skills/next-router/references/messages.md +90 -0
- package/plugins/sp/skills/next-router/references/routing-table.md +216 -0
- package/plugins/sp/skills/parallel-execution/SKILL.md +148 -0
- package/plugins/sp/skills/parallel-execution/references/dispatch-surface.md +109 -0
- package/plugins/sp/skills/parallel-execution/references/fan-out-patterns.md +101 -0
- package/plugins/sp/skills/parallel-execution/references/result-synthesis.md +94 -0
- package/plugins/sp/skills/reverse-engineering/SKILL.md +375 -0
- package/plugins/sp/skills/reverse-engineering/agents/openai.yaml +18 -0
- package/plugins/sp/skills/source-driven-development/SKILL.md +124 -0
- package/plugins/sp/skills/spec-decomposition/SKILL.md +117 -0
- package/plugins/sp/skills/spec-decomposition/references/decomposition.md +539 -0
- package/plugins/sp/skills/spur-cli/SKILL.md +133 -0
- package/plugins/sp/skills/spur-cli/references/agent.md +179 -0
- package/plugins/sp/skills/spur-cli/references/features/acceptance-criteria.md +82 -0
- package/plugins/sp/skills/spur-cli/references/features/hierarchy-mece.md +177 -0
- package/plugins/sp/skills/spur-cli/references/features/roadmap-priority.md +84 -0
- package/plugins/sp/skills/spur-cli/references/features/verbs.md +122 -0
- package/plugins/sp/skills/spur-cli/references/features.md +234 -0
- package/plugins/sp/skills/spur-cli/references/init.md +119 -0
- package/plugins/sp/skills/spur-cli/references/message.md +98 -0
- package/plugins/sp/skills/spur-cli/references/rules/authoring-rules.md +212 -0
- package/plugins/sp/skills/spur-cli/references/rules/fine-tuning.md +148 -0
- package/plugins/sp/skills/spur-cli/references/rules/operations.md +228 -0
- package/plugins/sp/skills/spur-cli/references/rules/validation-and-extension.md +129 -0
- package/plugins/sp/skills/spur-cli/references/rules.md +209 -0
- package/plugins/sp/skills/spur-cli/references/serve.md +58 -0
- package/plugins/sp/skills/spur-cli/references/tasks/l3-guard-cheatsheet.md +98 -0
- package/plugins/sp/skills/spur-cli/references/tasks/section-editing.md +95 -0
- package/plugins/sp/skills/spur-cli/references/tasks/verbs.md +361 -0
- package/plugins/sp/skills/spur-cli/references/tasks.md +280 -0
- package/plugins/sp/skills/spur-cli/references/team.md +133 -0
- package/plugins/sp/skills/spur-cli/references/workflows/authoring-workflows.md +226 -0
- package/plugins/sp/skills/spur-cli/references/workflows/operations.md +211 -0
- package/plugins/sp/skills/spur-cli/references/workflows/validation-and-extension.md +129 -0
- package/plugins/sp/skills/spur-cli/references/workflows.md +355 -0
- package/plugins/sp/skills/spur-dev/SKILL.md +224 -0
- package/plugins/sp/skills/spur-dev/references/ac-style-guide.md +196 -0
- package/plugins/sp/skills/spur-dev/references/cross-cutting.md +680 -0
- package/plugins/sp/skills/spur-dev/references/decision-brief.md +80 -0
- package/plugins/sp/skills/spur-dev/references/dev-operations.md +546 -0
- package/plugins/sp/skills/spur-dev/references/done-housekeeping.md +164 -0
- package/plugins/sp/skills/spur-dev/references/execution-batch.md +790 -0
- package/plugins/sp/skills/spur-dev/references/execution-workflow.md +352 -0
- package/plugins/sp/skills/spur-dev/references/feature-link-helper.md +191 -0
- package/plugins/sp/skills/spur-dev/references/flag-glossary.md +417 -0
- package/plugins/sp/skills/spur-dev/references/gate-checklists.md +171 -0
- package/plugins/sp/skills/spur-dev/references/glossary.md +95 -0
- package/plugins/sp/skills/spur-dev/references/idea-evaluation.md +80 -0
- package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +120 -0
- package/plugins/sp/skills/spur-dev/references/planning-workflow.md +302 -0
- package/plugins/sp/skills/spur-dev/references/product-planning.md +206 -0
- package/plugins/sp/skills/spur-dev/references/section-batching.md +24 -0
- package/plugins/sp/skills/sys-architecture/SKILL.md +155 -0
- package/plugins/sp/skills/sys-architecture/references/decision-method.md +137 -0
- package/plugins/sp/skills/sys-architecture/references/upkeep-survey.md +75 -0
- package/plugins/sp/skills/sys-debugging/SKILL.md +228 -0
- package/plugins/sp/skills/sys-debugging/references/debugging-protocol.md +62 -0
- package/plugins/sp/skills/test-driven-development/SKILL.md +248 -0
- package/plugins/sp/skills/wayfinder/SKILL.md +292 -0
- package/spur.js +6035 -4963
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: stacks/python
|
|
3
|
+
description: "Stack adapter for Python — pytest test/coverage command, term-missing parsing, pytest idioms (fixtures, parametrize, mock-at-boundary), and the dashed-filename module-registration gotcha. Loaded by unit-testing.md for pyproject.toml/pytest projects."
|
|
4
|
+
see_also:
|
|
5
|
+
- unit-testing
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Stack adapter: Python
|
|
9
|
+
|
|
10
|
+
Mechanics for the [unit-testing.md](../unit-testing.md) spine when the project is Python (pytest).
|
|
11
|
+
|
|
12
|
+
## Test + coverage command
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pytest -v --tb=short # run + interpret
|
|
16
|
+
pytest --cov=src --cov-report=term-missing # measure coverage
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
`term-missing` output:
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
Name Stmts Miss Cover Missing
|
|
23
|
+
-------------------------------------------------
|
|
24
|
+
src/auth.py 89 5 94% 23-27
|
|
25
|
+
-------------------------------------------------
|
|
26
|
+
TOTAL 245 17 93%
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The `Missing` column lists uncovered line ranges directly — feed those into the spine's gap analysis.
|
|
30
|
+
|
|
31
|
+
## Test-file convention
|
|
32
|
+
|
|
33
|
+
Tests in `tests/`, named `test_*.py`; shared fixtures in `tests/conftest.py`. Minimal
|
|
34
|
+
`pyproject.toml`:
|
|
35
|
+
|
|
36
|
+
```toml
|
|
37
|
+
[tool.pytest.ini_options]
|
|
38
|
+
testpaths = ["tests"]
|
|
39
|
+
addopts = ["--cov=src", "--cov-report=term-missing"]
|
|
40
|
+
|
|
41
|
+
[tool.coverage.run]
|
|
42
|
+
source = ["src"]
|
|
43
|
+
omit = ["*/tests/*"]
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Idioms
|
|
47
|
+
|
|
48
|
+
**Fixtures + mock at the boundary:**
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
import pytest
|
|
52
|
+
from unittest.mock import Mock
|
|
53
|
+
|
|
54
|
+
@pytest.fixture
|
|
55
|
+
def auth_service():
|
|
56
|
+
return AuthService(Mock(spec=UserRepository))
|
|
57
|
+
|
|
58
|
+
def test_login_rejects_bad_credentials(auth_service):
|
|
59
|
+
with pytest.raises(InvalidCredentials):
|
|
60
|
+
auth_service.login("user", "wrong")
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Parameterized branch / edge tests** — one case per branch:
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
@pytest.mark.parametrize("page,page_size", [(0, 10), (-1, 10), (1, 0)])
|
|
67
|
+
def test_paginate_rejects_invalid_args(page, page_size):
|
|
68
|
+
with pytest.raises(ValueError):
|
|
69
|
+
paginate([], page, page_size)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Mock external deps at the boundary** (`patch` the I/O call, assert behavior):
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
@patch("requests.get")
|
|
76
|
+
def test_fetch_user_raises_on_404(mock_get):
|
|
77
|
+
mock_get.return_value = Mock(status_code=404)
|
|
78
|
+
with pytest.raises(APIError):
|
|
79
|
+
APIClient().fetch_user(999)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**Inline coverage-exclusion rationale:**
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
# pragma: no cover — unreachable in production; hardware-specific error path
|
|
86
|
+
if hardware_state == IMPOSSIBLE_STATE:
|
|
87
|
+
log_and_reboot()
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Configure recognized exclusions in `[tool.coverage.report] exclude_lines` (e.g. `pragma: no cover`,
|
|
91
|
+
`raise NotImplementedError`, `if __name__ == .__main__.:`).
|
|
92
|
+
|
|
93
|
+
## Gotcha: dashed-filename module registration
|
|
94
|
+
|
|
95
|
+
pytest imports modules by Python identifier, so a source file with a **dash** in its name
|
|
96
|
+
(`context-validator.py`) cannot be imported as `import context_validator` — the underscore name
|
|
97
|
+
doesn't exist on disk, and the dashed name is not a valid identifier. Tests silently fail to find it,
|
|
98
|
+
and coverage shows the file as entirely uncovered.
|
|
99
|
+
|
|
100
|
+
**Fix** — register the dashed file under an importable alias in `tests/conftest.py`:
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
# tests/conftest.py
|
|
104
|
+
import importlib.util, sys
|
|
105
|
+
from pathlib import Path
|
|
106
|
+
|
|
107
|
+
def _register(dashed_path: str, alias: str) -> None:
|
|
108
|
+
spec = importlib.util.spec_from_file_location(alias, Path(dashed_path).resolve())
|
|
109
|
+
module = importlib.util.module_from_spec(spec)
|
|
110
|
+
sys.modules[alias] = module
|
|
111
|
+
spec.loader.exec_module(module)
|
|
112
|
+
|
|
113
|
+
_register("src/context-validator.py", "context_validator")
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Tests then `import context_validator` normally and coverage tracks the file. Apply this **before**
|
|
117
|
+
concluding a dashed-named module is untested.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: test-loop-breaker
|
|
3
|
+
description: Stop repeated identical failing-test runs and require source inspection, a falsifiable hypothesis, and an edit before retrying.
|
|
4
|
+
see_also:
|
|
5
|
+
- code-testing
|
|
6
|
+
- sys-debugging
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Test-loop breaker
|
|
10
|
+
|
|
11
|
+
Treat repeated execution without new information as spinning.
|
|
12
|
+
|
|
13
|
+
## Protocol
|
|
14
|
+
|
|
15
|
+
Track the normalized test command, failure signature, and whether relevant source changed.
|
|
16
|
+
|
|
17
|
+
1. Run the narrowest red-capable test once.
|
|
18
|
+
2. On the first failure, read the failing assertion and the component/module under test.
|
|
19
|
+
3. State a falsifiable hypothesis: “The test expects X because Y, but the code does Z.”
|
|
20
|
+
4. Make one source or test edit that directly tests the hypothesis.
|
|
21
|
+
5. Re-run the narrow test once.
|
|
22
|
+
6. If the signature is unchanged, discard or refine the hypothesis before another edit.
|
|
23
|
+
7. After two identical failure signatures, do not re-run without a relevant source/test change.
|
|
24
|
+
8. Hard cap: three executions of the same command without a relevant source/test change. Escalate
|
|
25
|
+
with the command, signature, inspected files, and rejected hypotheses.
|
|
26
|
+
|
|
27
|
+
A single debugging thread must not exceed five test executions without a source/test change.
|
|
28
|
+
Changing flags, output filters, or temporary filenames does not reset the count.
|
|
29
|
+
|
|
30
|
+
## Required evidence before retry
|
|
31
|
+
|
|
32
|
+
- Failing assertion inspected with a `file:line` anchor.
|
|
33
|
+
- Component/module under test inspected with a `file:line` anchor.
|
|
34
|
+
- One written hypothesis and its predicted observation.
|
|
35
|
+
- One relevant edit, or a different diagnostic command that can falsify the hypothesis.
|
|
36
|
+
|
|
37
|
+
Anti-pattern: test → grep → unchanged test → same failure. Grep is context gathering, not progress.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: test-output-discipline
|
|
3
|
+
description: Preserve test exit status while keeping routine output small enough for an agent context.
|
|
4
|
+
see_also:
|
|
5
|
+
- code-testing
|
|
6
|
+
- test-loop-breaker
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Test-output discipline
|
|
10
|
+
|
|
11
|
+
Prefer the runner's concise reporter and a narrow test selector. Filter only when the runner cannot
|
|
12
|
+
produce bounded output, and always preserve the test process exit status.
|
|
13
|
+
|
|
14
|
+
## Bun / TypeScript examples
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# Full suite: concise progress plus the final summary.
|
|
18
|
+
bun test --reporter=dots
|
|
19
|
+
|
|
20
|
+
# One file, then one named test after the first failure.
|
|
21
|
+
bun test tests/foo.test.ts --reporter=dots
|
|
22
|
+
bun test tests/foo.test.ts --test-name-pattern "specific behavior" --reporter=dots
|
|
23
|
+
|
|
24
|
+
# Last resort when only pass/fail matters: filtered diagnostics with pipeline failure preserved.
|
|
25
|
+
set -o pipefail
|
|
26
|
+
bun test tests/foo.test.ts 2>&1 | rg -i -A 5 'fail|error' | tail -30
|
|
27
|
+
|
|
28
|
+
# Recommended when the exact Bun exit status must be returned.
|
|
29
|
+
test_log=$(mktemp)
|
|
30
|
+
trap 'rm -f "$test_log"' EXIT
|
|
31
|
+
test_status=0
|
|
32
|
+
bun test tests/foo.test.ts >"$test_log" 2>&1 || test_status=$?
|
|
33
|
+
rg -i -A 5 'fail|error' "$test_log" | tail -30 || true
|
|
34
|
+
exit "$test_status"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Never infer PASS from filtered text. `pipefail` preserves failure semantics, but it does not
|
|
38
|
+
necessarily return the test process's exact status: a later `rg` with no matches can make a green
|
|
39
|
+
test look red. Prefer the captured-log pattern above when the caller needs the real Bun status. If
|
|
40
|
+
a direct pipeline is unavoidable, capture the first stage immediately with `${PIPESTATUS[0]}` in
|
|
41
|
+
Bash or `$pipestatus[1]` in zsh, then return that value explicitly.
|
|
42
|
+
|
|
43
|
+
## Context budget
|
|
44
|
+
|
|
45
|
+
- Routine green run: summary only, target under 500 tokens.
|
|
46
|
+
- First red run: retain the failing assertion and short stack.
|
|
47
|
+
- Later red run: retain only the changed failure signature and affected `file:line` anchors.
|
|
48
|
+
- Large assertion diffs belong in an artifact or local file; do not paste them repeatedly.
|
|
49
|
+
|
|
50
|
+
Do not run a full suite again while debugging one assertion. Isolate with
|
|
51
|
+
`--test-name-pattern`, apply the test-loop breaker, then return to the full suite once the narrow
|
|
52
|
+
case is green.
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: unit-testing
|
|
3
|
+
description: "SSOT for the `unit` operation procedure (language-agnostic spine) — file-focused and task-scoped test workflows, gap categorization, coverage-vs-quality rules, escalation. Backs `/sp:dev-unit` and dev-operations.md §1. Per-stack commands, coverage parsing, and gotchas live in stacks/<stack>.md adapters."
|
|
4
|
+
see_also:
|
|
5
|
+
- code-testing
|
|
6
|
+
- test-driven-development
|
|
7
|
+
- dev-operations
|
|
8
|
+
parameters:
|
|
9
|
+
coverage:
|
|
10
|
+
flag: "--coverage <pct>"
|
|
11
|
+
default: 90
|
|
12
|
+
applies_to: "line and function coverage targets"
|
|
13
|
+
invariant: "Never relaxes the 100% passing-test requirement."
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# Unit Testing
|
|
17
|
+
|
|
18
|
+
The procedure behind the `unit` operation (`/sp:dev-unit`, `dev-operations.md §1`). It extends or
|
|
19
|
+
generates tests until the coverage target is met with a fully passing suite. It does **one** thing —
|
|
20
|
+
write tests — and yields; it does not implement, review, or verify.
|
|
21
|
+
|
|
22
|
+
This file is the **coverage half** of the testing surface: how to *fill gaps* on code that already
|
|
23
|
+
exists. For the **design half** — how to write a good test test-first (red-green-refactor, AAA,
|
|
24
|
+
naming, mock-at-boundary) — use the **`sp:test-driven-development`** skill. They compose: TDD designs the tests,
|
|
25
|
+
the unit op proves the coverage.
|
|
26
|
+
|
|
27
|
+
**This file is the language-agnostic spine.** It owns the *thinking* — workflows, gap analysis,
|
|
28
|
+
quality rules, escalation. Each stack's *mechanics* — the test command, how to parse its coverage
|
|
29
|
+
report, framework idioms, and stack-specific gotchas — live in a thin adapter under
|
|
30
|
+
[`stacks/`](stacks/). The operation runs: **detect stack → load the one matching adapter → run this
|
|
31
|
+
spine.**
|
|
32
|
+
|
|
33
|
+
> **Scope.** The `unit` operation runs against whatever project the agent is working in — which may
|
|
34
|
+
> be any stack, not Bun/TS. (Spur's *own* self-build gate is Bun/TS per AGENTS.md, but that is a
|
|
35
|
+
> different concern from testing user code.) The spine is universal; the adapter supplies the stack.
|
|
36
|
+
|
|
37
|
+
## Stack detection → adapter
|
|
38
|
+
|
|
39
|
+
Detect the project stack from its manifest, then load the matching adapter for all command/parsing
|
|
40
|
+
specifics:
|
|
41
|
+
|
|
42
|
+
| Manifest signal | Stack | Adapter |
|
|
43
|
+
| ----------------- | ------- | --------- |
|
|
44
|
+
| `bun.lock` / `bunfig.toml` | Bun + TypeScript | [stacks/bun-ts.md](stacks/bun-ts.md) |
|
|
45
|
+
| `package.json` with `vitest` / `jest`, no bun lock | Node + TypeScript/JS | [stacks/bun-ts.md](stacks/bun-ts.md) (Node section) |
|
|
46
|
+
| `pyproject.toml` / `pytest` | Python | [stacks/python.md](stacks/python.md) |
|
|
47
|
+
| `go.mod` | Go | [stacks/go.md](stacks/go.md) |
|
|
48
|
+
| `Cargo.toml` | Rust | *adapter not yet authored — see [stacks/](stacks/)* |
|
|
49
|
+
| other | — | author a new adapter following the existing pattern |
|
|
50
|
+
|
|
51
|
+
If no adapter exists for the detected stack, author one (≈40–60 lines: test command, coverage
|
|
52
|
+
command + report parsing, framework idioms, known gotchas) rather than inlining stack specifics here.
|
|
53
|
+
|
|
54
|
+
## Two workflows
|
|
55
|
+
|
|
56
|
+
The `target` argument selects the workflow:
|
|
57
|
+
|
|
58
|
+
| Input | Detection | Workflow |
|
|
59
|
+
|-------|-----------|----------|
|
|
60
|
+
| A source file path or a glob | Source path / file glob | **A — file-focused** |
|
|
61
|
+
| A WBS number, a `.md` task file, or any other string | WBS / task ref | **B — task-scoped** |
|
|
62
|
+
|
|
63
|
+
### Workflow A: file-focused
|
|
64
|
+
|
|
65
|
+
Use when `target` is a source file path or glob.
|
|
66
|
+
|
|
67
|
+
1. Resolve the source file(s) and **detect the stack** (load its adapter).
|
|
68
|
+
2. Derive or locate the test file(s) — see the adapter for the stack's test-file convention. For a
|
|
69
|
+
loose source file with no matching test, create one with the stack's standard test structure.
|
|
70
|
+
3. Run tests with coverage (the adapter's measurement command).
|
|
71
|
+
4. Identify gaps ([§ Gap analysis](#gap-analysis)).
|
|
72
|
+
5. Add targeted tests for the highest-priority gaps first.
|
|
73
|
+
6. Re-run; compare before/after coverage.
|
|
74
|
+
7. Repeat until the target is met or the loop escalates (max 3 passes — see [§ Escalation](#escalation)).
|
|
75
|
+
|
|
76
|
+
The repository coverage signal is not a strict per-source-file proof. For file-focused usage treat
|
|
77
|
+
coverage as **target-focused evidence**: combine the coverage output with a direct assertion that
|
|
78
|
+
the target file's behavior/path set is exercised by the added tests. Do **not** claim mathematically
|
|
79
|
+
exact per-file coverage for the source target unless the toolchain actually reports it.
|
|
80
|
+
|
|
81
|
+
### Workflow B: task-scoped
|
|
82
|
+
|
|
83
|
+
Use when `target` is a WBS number or task file path. This workflow moves task status and is gated by
|
|
84
|
+
the CLI.
|
|
85
|
+
|
|
86
|
+
1. Resolve the task reference and **detect the stack** (load its adapter).
|
|
87
|
+
2. **Pre-testing guard** (see below) — ensure required sections exist before moving status.
|
|
88
|
+
3. `spur task update <wbs> testing`.
|
|
89
|
+
4. Run a task-scoped test/coverage pass (the adapter's measurement command).
|
|
90
|
+
5. If testing reveals implementation gaps, move the task back: `spur task update <wbs> wip`.
|
|
91
|
+
6. Repeat until tests are green and the coverage target is satisfied.
|
|
92
|
+
|
|
93
|
+
`dev-unit` is a **testing** command, not a completion command — it never marks a task `done`. Final
|
|
94
|
+
closure belongs to the pipeline's verify gate.
|
|
95
|
+
|
|
96
|
+
| Workflow moment | Required status action |
|
|
97
|
+
| ----------------- | ------------------------ |
|
|
98
|
+
| Start task-scoped testing | Ensure `Solution` + `Plan` exist, backfill `Design` if needed, then `spur task update <wbs> testing` |
|
|
99
|
+
| Testing reveals implementation work remains | `spur task update <wbs> wip` |
|
|
100
|
+
| Testing pass succeeds | Keep current status — do **not** mark `done` |
|
|
101
|
+
|
|
102
|
+
#### Pre-testing guard
|
|
103
|
+
|
|
104
|
+
`spur task check` gates `testing` status on required sections. Task-scoped `dev-unit` must not move a
|
|
105
|
+
task to `testing` blindly. Before the transition:
|
|
106
|
+
|
|
107
|
+
1. Ensure `Solution` describes the implemented behavior being tested.
|
|
108
|
+
2. Ensure `Plan` records the concrete execution/testing steps taken.
|
|
109
|
+
3. Add minimal real `Design` content if needed to avoid warning-driven `--force` usage.
|
|
110
|
+
4. Only then call `spur task update <wbs> testing`.
|
|
111
|
+
|
|
112
|
+
Never substitute `--force` for honest backfill. The section-editing rules live in the spine's
|
|
113
|
+
`cross-cutting.md` (`sp:spur-dev`). (This guard governs the Spur task corpus and is stack-agnostic.)
|
|
114
|
+
|
|
115
|
+
## Coverage target
|
|
116
|
+
|
|
117
|
+
The default target is **per-file line ≥ 90% and function ≥ 90%** with a fully passing suite. The
|
|
118
|
+
wrapper flag is owned here: `--coverage <pct>` may raise or lower the line/function target but never
|
|
119
|
+
relaxes the "100% passing" requirement. How each stack reports those numbers is in its adapter.
|
|
120
|
+
|
|
121
|
+
> **Realistic targets by surface.** 90% is the Spur default; some surfaces justify lower (see
|
|
122
|
+
> [§ When to accept lower coverage](#when-to-accept-lower-coverage)). Domain/business logic should run
|
|
123
|
+
> high (90–100%); generated code, simple wrappers, and config can run lower with documented rationale.
|
|
124
|
+
|
|
125
|
+
## Gap analysis
|
|
126
|
+
|
|
127
|
+
Read the coverage report (via the adapter) and categorize each gap before writing tests — priority
|
|
128
|
+
order top to bottom:
|
|
129
|
+
|
|
130
|
+
| Category | Example | Strategy | Priority |
|
|
131
|
+
| ---------- | --------- | ---------- | ---------- |
|
|
132
|
+
| Error paths | a thrown/raised error in a guard or catch block | Add error-condition tests | High |
|
|
133
|
+
| Complex logic | nested conditionals, branch matrices | Add parameterized/table-driven tests covering each branch | High |
|
|
134
|
+
| Edge cases | boundary conditions (0, -1, empty, max) | Add boundary-value tests | Medium |
|
|
135
|
+
| External deps | network/file/process calls | Add mock-at-boundary tests | Medium |
|
|
136
|
+
| Unreachable | defensive checks, impossible states | Document rationale, skip | Skip |
|
|
137
|
+
|
|
138
|
+
**Branch coverage rule (language-agnostic):** every conditional branch needs at least one test that
|
|
139
|
+
takes it. A function with `if vip { if large {A} else {B} } else {C}` needs three tests (large-vip,
|
|
140
|
+
small-vip, non-vip) — counting line coverage alone hides the missing branch. The stack adapter shows
|
|
141
|
+
the idiomatic way to express parameterized/table-driven branch tests.
|
|
142
|
+
|
|
143
|
+
## Coverage vs quality
|
|
144
|
+
|
|
145
|
+
Coverage measures execution, not correctness. A 100%-covered test with no assertion is worthless —
|
|
146
|
+
the test must assert observable behavior, not merely call the code. Per AGENTS.md, tests encode
|
|
147
|
+
**why** the behavior matters: names describe behavior under a condition; assertions tie to the
|
|
148
|
+
requirement, not the implementation.
|
|
149
|
+
|
|
150
|
+
**Coverage anti-patterns** (do not produce these — they are language-agnostic):
|
|
151
|
+
|
|
152
|
+
1. **Coverage padding** — tests that touch code without asserting.
|
|
153
|
+
2. **Happy-path only** — only success cases, no error/edge branches.
|
|
154
|
+
3. **Implementation-detail testing** — asserting internal state instead of observable behavior.
|
|
155
|
+
4. **Over-mocking** — mocks that hide the real behavior gap. Mock only at boundaries (network, file,
|
|
156
|
+
process, clock), never internal collaborators.
|
|
157
|
+
|
|
158
|
+
### When to accept lower coverage
|
|
159
|
+
|
|
160
|
+
Valid reasons (document the rationale inline, in the stack's idiom):
|
|
161
|
+
|
|
162
|
+
1. **Generated code** — schema/ORM/OpenAPI output, tested at the contract.
|
|
163
|
+
2. **Simple wrappers** — pure delegation, no new logic.
|
|
164
|
+
3. **Impossible states** — defensive checks unreachable in production.
|
|
165
|
+
|
|
166
|
+
The adapter shows each stack's inline-exclusion/rationale syntax.
|
|
167
|
+
|
|
168
|
+
## Completion criteria
|
|
169
|
+
|
|
170
|
+
The operation succeeds only when **all** are true:
|
|
171
|
+
|
|
172
|
+
1. The relevant tests pass with `0` failures.
|
|
173
|
+
2. The coverage target is met (`--coverage` when provided, else per-file line/function ≥ 90%).
|
|
174
|
+
3. No unresolved blocker remains from the last test pass.
|
|
175
|
+
|
|
176
|
+
If coverage is still below target after focused extension, **escalate** — do not pretend success.
|
|
177
|
+
|
|
178
|
+
## Escalation
|
|
179
|
+
|
|
180
|
+
Escalate when:
|
|
181
|
+
|
|
182
|
+
- Coverage plateaus after repeated passes (**max 3 gap-filling passes**).
|
|
183
|
+
- A failure requires debugging rather than more test authoring.
|
|
184
|
+
- Environment or dependency issues block meaningful testing.
|
|
185
|
+
|
|
186
|
+
Before assuming a real gap, **rule out instrumentation artifacts** — most stacks have coverage
|
|
187
|
+
gotchas where the metric understates reality (synthetic constructors, unloaded modules, worker
|
|
188
|
+
leaks). The adapter's gotcha table lists them per stack; check it before writing tests that cannot
|
|
189
|
+
move the number.
|
|
190
|
+
|
|
191
|
+
On escalation:
|
|
192
|
+
|
|
193
|
+
1. **Document the untestable code** — some gaps are unreachable by design.
|
|
194
|
+
2. **Report status honestly** — which gaps remain, which are documented-skipped, final coverage achieved.
|
|
195
|
+
3. For task-scoped runs, leave the task in `wip` if implementation changes are still required; do not
|
|
196
|
+
force it forward.
|
|
197
|
+
|
|
198
|
+
## Advanced Techniques
|
|
199
|
+
|
|
200
|
+
Advanced testing remains a technique layer, not a new command surface. Use these when coverage is
|
|
201
|
+
green but confidence is still weak:
|
|
202
|
+
|
|
203
|
+
| Technique | Use When | Typical Tooling |
|
|
204
|
+
| ----------- | ---------- | ----------------- |
|
|
205
|
+
| Mutation testing | Critical business/security logic needs proof that tests catch real defects. | Stryker, mutmut, PIT, infection |
|
|
206
|
+
| Property-based testing | Algorithms, parsers, serializers, or data transforms have invariants across many inputs. | fast-check, Hypothesis, proptest |
|
|
207
|
+
| Accessibility testing | UI behavior must meet WCAG expectations beyond visual checks. | axe, Pa11y, Playwright accessibility assertions |
|
|
208
|
+
| Implementation comparison | Competing algorithms or designs need objective selection. | benchmark harness + common test corpus |
|
|
209
|
+
|
|
210
|
+
Do not add these by default to every task. They are escalation tools for high-risk code or for a
|
|
211
|
+
specific confidence gap the normal unit workflow cannot close. If the technique introduces new
|
|
212
|
+
tooling, document why the existing stack gate is insufficient before adding it.
|