@jabworks/condux 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -18
- package/agents/explorer.md +1 -0
- package/agents/planner.md +1 -0
- package/agents/researcher.md +1 -0
- package/index.js +34 -3
- package/package.json +6 -4
- package/skills/code-review/SKILL.md +136 -0
- package/skills/code-review/evals/trigger_eval.json +22 -0
- package/skills/discovery/SKILL.md +242 -0
- package/skills/discovery/evals/trigger_eval.json +24 -0
- package/skills/discovery/references/choice-server.js +216 -0
- package/skills/discovery/references/mockup-picker.md +90 -0
- package/skills/discovery/references/package.json +3 -0
- package/skills/draft-plan/SKILL.md +229 -0
- package/skills/draft-plan/evals/trigger_eval.json +22 -0
- package/skills/draft-plan/references/plan-template.md +96 -0
- package/skills/finalize/SKILL.md +123 -0
- package/skills/finalize/evals/trigger_eval.json +22 -0
- package/skills/finalize/references/rationalization-table.md +15 -0
- package/skills/plan-review/SKILL.md +215 -0
- package/skills/plan-review/evals/trigger_eval.json +22 -0
- package/skills/plan-review/references/annotate-server.js +465 -0
- package/skills/plan-review/references/install-codex-hook.sh +112 -0
- package/skills/plan-review/references/package.json +3 -0
- package/skills/plan-review/references/plan-review-template.html +730 -0
- package/skills/preflight/SKILL.md +128 -0
- package/skills/preflight/evals/trigger_eval.json +26 -0
- package/skills/root-cause-analysis/SKILL.md +82 -0
- package/skills/root-cause-analysis/evals/trigger_eval.json +22 -0
- package/skills/subagent-deployment/SKILL.md +105 -0
- package/skills/subagent-deployment/evals/trigger_eval.json +22 -0
- package/skills/subagent-deployment/references/safety-checklist.md +54 -0
- package/skills/subagent-execution/SKILL.md +182 -0
- package/skills/subagent-execution/agents/coder.md +55 -0
- package/skills/subagent-execution/agents/explorer.md +77 -0
- package/skills/subagent-execution/agents/planner.md +101 -0
- package/skills/subagent-execution/agents/researcher.md +78 -0
- package/skills/subagent-execution/evals/trigger_eval.json +22 -0
- package/skills/subagent-execution/references/install-codex-agents.mjs +98 -0
- package/skills/subagent-execution/references/review-package.sh +29 -0
- package/skills/subagent-execution/references/spawn-rules.md +88 -0
- package/skills/subagent-execution/references/task-brief.sh +39 -0
- package/skills/technical-spec/SKILL.md +190 -0
- package/skills/technical-spec/evals/trigger_eval.json +23 -0
- package/skills/technical-spec/references/scaffold.sh +98 -0
- package/skills/technical-spec/references/templates.md +159 -0
- package/skills/test-first-development/SKILL.md +121 -0
- package/skills/test-first-development/evals/trigger_eval.json +22 -0
- package/skills/test-first-development/references/rationalization-table.md +16 -0
- package/skills/workflow/SKILL.md +241 -0
- package/skills/workflow/evals/trigger_eval.json +30 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: technical-spec
|
|
3
|
+
description: Use when a feature plan, design session, or implementation milestone is complete and the decisions, API contracts, implementation details, and quirks should be persisted for future reference. Also use when the user says "save spec", "document this", "write up what we decided", or "spec this out".
|
|
4
|
+
disable-model-invocation: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Save Tech Spec
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
Persists feature decisions, API contracts, implementation details, and quirks into a structured, queryable spec tree. One folder per feature, one file per concern — future sessions load only what they need. Specs are living contracts: `/preflight`'s Drift Check compares the implementation against them at end-of-task.
|
|
12
|
+
|
|
13
|
+
## Spec Folder Layout
|
|
14
|
+
|
|
15
|
+
All specs live under one `specs/` tree at the **git root**, mirroring the
|
|
16
|
+
repo structure. The scaffold script detects the nearest package root (walking
|
|
17
|
+
up from CWD to the git root, finding the first `package.json`, `Cargo.toml`,
|
|
18
|
+
`go.mod`, or `pyproject.toml`) and uses its path *relative to the git root*
|
|
19
|
+
to place the spec.
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
<git-root>/specs/
|
|
23
|
+
{feature-slug}/ # cross-cutting, or scaffolded from the repo root
|
|
24
|
+
apps/web/
|
|
25
|
+
{feature-slug}/ # scaffolded while working in apps/web
|
|
26
|
+
index.md # TOC, last updated, commit hash, changelog ← scaffold creates this
|
|
27
|
+
decisions.md # Design decisions with context + rationale
|
|
28
|
+
api.md # Endpoints, types, external APIs consumed
|
|
29
|
+
fields.md # Field mappings: BE/3rd-party → UI, or forwarding chains
|
|
30
|
+
implementation.md # Key files, patterns, how it hangs together
|
|
31
|
+
quirks.md # Edge cases, gotchas, known issues
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Slugs are kebab-case and acronym-aware: `WanConfig` → `wan-config`,
|
|
35
|
+
`UIFormControls` → `ui-form-controls`.
|
|
36
|
+
|
|
37
|
+
In a single-package repo the package root is the git root, so specs land at
|
|
38
|
+
`specs/<slug>/`. In a monorepo working on `apps/web`, they land at
|
|
39
|
+
`specs/apps/web/<slug>/` — one browsable tree, with a natural home for
|
|
40
|
+
cross-cutting features at `specs/<slug>/`.
|
|
41
|
+
|
|
42
|
+
Existing specs co-located inside packages (`apps/web/specs/…`) are not
|
|
43
|
+
auto-migrated — `git mv` them into the root tree if you want them unified.
|
|
44
|
+
|
|
45
|
+
Only create files that have actual content. Don't create empty files.
|
|
46
|
+
|
|
47
|
+
## Workflow
|
|
48
|
+
|
|
49
|
+
```dot
|
|
50
|
+
digraph save_spec {
|
|
51
|
+
rankdir=TB;
|
|
52
|
+
"Infer feature name from context" [shape=box];
|
|
53
|
+
"Feature name clear?" [shape=diamond];
|
|
54
|
+
"Ask user for feature name" [shape=box];
|
|
55
|
+
"Notify user — confirm before writing" [shape=box];
|
|
56
|
+
"User approves?" [shape=diamond];
|
|
57
|
+
"Stop" [shape=box];
|
|
58
|
+
"Run scaffold.sh <feature>" [shape=box];
|
|
59
|
+
"Output: exists or created?" [shape=diamond];
|
|
60
|
+
"Write content files, update index.md Contents" [shape=box];
|
|
61
|
+
"Read index.md, update changed files only" [shape=box];
|
|
62
|
+
"Ask: open live HTML preview?" [shape=diamond];
|
|
63
|
+
"Locate + run plan-review annotate-server.js on the spec dir" [shape=box];
|
|
64
|
+
"Tell user: Ctrl+C when done reviewing" [shape=box];
|
|
65
|
+
"Done" [shape=doublecircle];
|
|
66
|
+
|
|
67
|
+
"Infer feature name from context" -> "Feature name clear?";
|
|
68
|
+
"Feature name clear?" -> "Notify user — confirm before writing" [label="yes"];
|
|
69
|
+
"Feature name clear?" -> "Ask user for feature name" [label="no"];
|
|
70
|
+
"Ask user for feature name" -> "Notify user — confirm before writing";
|
|
71
|
+
"Notify user — confirm before writing" -> "User approves?";
|
|
72
|
+
"User approves?" -> "Stop" [label="no"];
|
|
73
|
+
"User approves?" -> "Run scaffold.sh <feature>" [label="yes"];
|
|
74
|
+
"Run scaffold.sh <feature>" -> "Output: exists or created?";
|
|
75
|
+
"Output: exists or created?" -> "Write content files, update index.md Contents" [label="created"];
|
|
76
|
+
"Output: exists or created?" -> "Read index.md, update changed files only" [label="exists"];
|
|
77
|
+
"Write content files, update index.md Contents" -> "Ask: open live HTML preview?";
|
|
78
|
+
"Read index.md, update changed files only" -> "Ask: open live HTML preview?";
|
|
79
|
+
"Ask: open live HTML preview?" -> "Locate + run plan-review annotate-server.js on the spec dir" [label="yes"];
|
|
80
|
+
"Ask: open live HTML preview?" -> "Done" [label="no"];
|
|
81
|
+
"Locate + run plan-review annotate-server.js on the spec dir" -> "Tell user: Ctrl+C when done reviewing";
|
|
82
|
+
"Tell user: Ctrl+C when done reviewing" -> "Done";
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Required Notifications
|
|
87
|
+
|
|
88
|
+
Always announce and confirm before writing or editing. Never silently modify files.
|
|
89
|
+
|
|
90
|
+
New spec:
|
|
91
|
+
```
|
|
92
|
+
About to save tech spec for `wan-config` → specs/wan-config/
|
|
93
|
+
Proceed? [y/n]
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Updating existing spec:
|
|
97
|
+
```
|
|
98
|
+
Updating spec for `wan-config` (last saved: 2026-06-20, commit abc1234)
|
|
99
|
+
Files to update: decisions.md, quirks.md
|
|
100
|
+
Proceed? [y/n]
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Running the Scaffold Script
|
|
104
|
+
|
|
105
|
+
The script lives next to this skill file. Find it relative to where this SKILL.md was loaded from:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
bash path/to/skill/references/scaffold.sh "WanConfig"
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The script outputs one line:
|
|
112
|
+
- `created:/abs/path/to/specs/wan-config commit:abc1234 date:2026-06-24` — new spec, `index.md` written
|
|
113
|
+
- `exists:/abs/path/to/specs/wan-config commit:abc1234 date:2026-06-24` — spec already exists
|
|
114
|
+
|
|
115
|
+
Parse `commit`, `date`, and the **absolute spec path** from the output — use the absolute path when writing content files and launching the preview server.
|
|
116
|
+
|
|
117
|
+
## After Scaffold: Writing Content Files
|
|
118
|
+
|
|
119
|
+
Read [references/templates.md](references/templates.md) for the structure of each file.
|
|
120
|
+
|
|
121
|
+
- Write only files that have real content
|
|
122
|
+
- After writing, update the `## Contents` section in `index.md` with links to the files you created
|
|
123
|
+
|
|
124
|
+
## Updating an Existing Spec
|
|
125
|
+
|
|
126
|
+
When scaffold output is `exists:`:
|
|
127
|
+
1. Read `index.md` to see current state and which files exist
|
|
128
|
+
2. Update only files with new or changed content
|
|
129
|
+
3. Bump `Last updated` and `Commit` in `index.md` using values from scaffold output
|
|
130
|
+
4. Append to the `## Changelog` in `index.md`
|
|
131
|
+
|
|
132
|
+
## Live HTML Preview & Review (Optional)
|
|
133
|
+
|
|
134
|
+
Spec preview is served by the **plan-review** skill's annotate server in
|
|
135
|
+
directory mode — the same annotation UI used for plan review, so the user can
|
|
136
|
+
not only read the spec live but select text, attach notes per file, and submit
|
|
137
|
+
a decision.
|
|
138
|
+
|
|
139
|
+
After writing spec files, ask:
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
Spec saved. Want a live HTML preview? It renders all spec files, updates as you edit, and lets you annotate. [y/n]
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
If yes, locate the plan-review server script:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
find ~/.claude ~/.codex ~/.agents -name "annotate-server.js" -path "*plan-review*" 2>/dev/null | head -1
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Run it in the background, pointing at the spec folder:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
node /path/to/plan-review/references/annotate-server.js specs/wan-config
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
It opens the browser automatically, lists every top-level `.md` in the folder
|
|
158
|
+
(Contents tab), watches for file changes, and re-renders live. If the user
|
|
159
|
+
submits a decision, it lands in `specs/<slug>/review.feedback.md` with notes
|
|
160
|
+
grouped by file — read it and action it:
|
|
161
|
+
|
|
162
|
+
- **Approve** — the spec is accurate; nothing to do.
|
|
163
|
+
- **Request Revisions** — fix the spec files per the notes.
|
|
164
|
+
|
|
165
|
+
Spec review is accept-or-fix, so the bar shows only these two verdicts (the
|
|
166
|
+
plan-only "Reject / don't build it" verdict is hidden in directory mode). If
|
|
167
|
+
the spec's whole premise is wrong, the user leaves a note saying so and stops
|
|
168
|
+
the server — treat that as a cue to discuss before rewriting, not a routine
|
|
169
|
+
revision.
|
|
170
|
+
|
|
171
|
+
Tell the user:
|
|
172
|
+
|
|
173
|
+
```
|
|
174
|
+
Preview running. Edit your spec files and the browser updates live.
|
|
175
|
+
Annotate and submit a decision, or just press Ctrl+C in the terminal when done.
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
The server is in-memory only — no HTML files are written to disk (only the
|
|
179
|
+
feedback file, if a decision is submitted).
|
|
180
|
+
|
|
181
|
+
## Common Mistakes
|
|
182
|
+
|
|
183
|
+
| Mistake | Fix |
|
|
184
|
+
|---------|-----|
|
|
185
|
+
| Skipping the notification/confirmation | Always announce before writing, wait for approval |
|
|
186
|
+
| Not using scaffold output for date/commit | Parse them from the script — don't run git separately |
|
|
187
|
+
| Rewriting all files on update | Read `index.md` first, only touch files with new content |
|
|
188
|
+
| Vague decisions | Each decision needs context + rationale + consequences |
|
|
189
|
+
| Creating empty placeholder files | Only create a file when it has real content |
|
|
190
|
+
| Forgetting to update index.md Contents | Always link to files you create |
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
[
|
|
2
|
+
{ "query": "save spec", "should_trigger": true, "expected_skill": "technical-spec" },
|
|
3
|
+
{ "query": "document what we decided", "should_trigger": true, "expected_skill": "technical-spec" },
|
|
4
|
+
{ "query": "spec this out", "should_trigger": true, "expected_skill": "technical-spec" },
|
|
5
|
+
{ "query": "persist the api contract and the quirks we found", "should_trigger": true, "expected_skill": "technical-spec" },
|
|
6
|
+
{ "query": "write up the field mappings from backend to UI", "should_trigger": true, "expected_skill": "technical-spec" },
|
|
7
|
+
{ "query": "update the spec for wan-config", "should_trigger": true, "expected_skill": "technical-spec" },
|
|
8
|
+
{ "query": "scaffold a spec folder for this feature", "should_trigger": true, "expected_skill": "technical-spec" },
|
|
9
|
+
{ "query": "whats the format for quirks.md", "should_trigger": true, "expected_skill": "technical-spec", "kind": "in-context" },
|
|
10
|
+
{ "query": "record the decision rationale for future sessions", "should_trigger": true, "expected_skill": "technical-spec" },
|
|
11
|
+
{ "query": "bump the index.md changelog for the spec", "should_trigger": true, "expected_skill": "technical-spec" },
|
|
12
|
+
{ "query": "save the design as a tech spec with live preview", "should_trigger": true, "expected_skill": "technical-spec" },
|
|
13
|
+
{ "query": "sepc this out pls", "should_trigger": true, "expected_skill": "technical-spec" },
|
|
14
|
+
{ "query": "write up what we decided about the milestone", "should_trigger": true, "expected_skill": "technical-spec" },
|
|
15
|
+
{ "query": "update the spec with what we built", "should_trigger": true, "expected_skill": "technical-spec" },
|
|
16
|
+
{ "query": "load the spec for checkout before we start the task", "should_trigger": true, "expected_skill": "workflow" },
|
|
17
|
+
{ "query": "browse the specs tree", "should_trigger": true, "expected_skill": "spec-browser" },
|
|
18
|
+
{ "query": "where is the spec for the auth feature", "should_trigger": true, "expected_skill": "spec-browser" },
|
|
19
|
+
{ "query": "design the feature first before documenting", "should_trigger": true, "expected_skill": "discovery" },
|
|
20
|
+
{ "query": "generate openapi yaml from the routes", "should_trigger": false, "expected_skill": null },
|
|
21
|
+
{ "query": "write jsdoc comments for this module", "should_trigger": false, "expected_skill": null },
|
|
22
|
+
{ "query": "host our swagger docs somewhere", "should_trigger": false, "expected_skill": null }
|
|
23
|
+
]
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Scaffolds a new tech spec directory for a feature.
|
|
3
|
+
# Usage: scaffold.sh <FeatureName or feature-name>
|
|
4
|
+
# Output (one line):
|
|
5
|
+
# created:<absolute-spec-path> commit:<hash> date:<YYYY-MM-DD>
|
|
6
|
+
# exists:<absolute-spec-path> commit:<hash> date:<YYYY-MM-DD>
|
|
7
|
+
#
|
|
8
|
+
# Spec location: <git-root>/specs/<pkg-relpath>/<slug>/
|
|
9
|
+
# pkg-relpath = path of the nearest package root (first directory above CWD
|
|
10
|
+
# with package.json, Cargo.toml, go.mod, or pyproject.toml) relative to the
|
|
11
|
+
# git root — empty when they coincide, giving <git-root>/specs/<slug>/.
|
|
12
|
+
# All specs live under the root specs/ tree, mirroring the repo structure.
|
|
13
|
+
|
|
14
|
+
set -euo pipefail
|
|
15
|
+
|
|
16
|
+
if [[ $# -lt 1 ]]; then
|
|
17
|
+
echo "Usage: scaffold.sh <feature-name>" >&2
|
|
18
|
+
exit 1
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
INPUT="$*"
|
|
22
|
+
|
|
23
|
+
# PascalCase or spaces → kebab-case, acronym-aware: dashes go only at
|
|
24
|
+
# lower/digit→Upper and UPPER→Upper+lower boundaries, so AOGrcIntegration →
|
|
25
|
+
# ao-grc-integration and UIFormControls → ui-form-controls. Kebab input
|
|
26
|
+
# passes through unchanged.
|
|
27
|
+
SLUG=$(echo "$INPUT" \
|
|
28
|
+
| sed 's/\([a-z0-9]\)\([A-Z]\)/\1-\2/g' \
|
|
29
|
+
| sed 's/\([A-Z]\)\([A-Z][a-z]\)/\1-\2/g' \
|
|
30
|
+
| tr '[:upper:]' '[:lower:]' \
|
|
31
|
+
| tr ' _' '-' \
|
|
32
|
+
| tr -s '-' \
|
|
33
|
+
| sed 's/^-//; s/-$//')
|
|
34
|
+
|
|
35
|
+
if [[ -z "$SLUG" || ! "$SLUG" =~ ^[a-z0-9]+(-[a-z0-9]+)*$ ]]; then
|
|
36
|
+
echo "ERROR: feature name must produce a kebab-case slug (letters, numbers, and hyphens only): $INPUT" >&2
|
|
37
|
+
exit 1
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
DATE=$(date +%Y-%m-%d)
|
|
41
|
+
COMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo "no-git")
|
|
42
|
+
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
|
|
43
|
+
[[ -z "$REPO_ROOT" ]] && REPO_ROOT="${PWD}"
|
|
44
|
+
|
|
45
|
+
# ---------------------------------------------------------------------------
|
|
46
|
+
# Detect package root: nearest dir with a package manifest, up to git root.
|
|
47
|
+
# Used only to compute the path relative to the git root — specs themselves
|
|
48
|
+
# always live under <git-root>/specs/, mirroring the repo structure.
|
|
49
|
+
# ---------------------------------------------------------------------------
|
|
50
|
+
detect_spec_base() {
|
|
51
|
+
local dir="${PWD}"
|
|
52
|
+
while [[ "$dir" != "/" ]]; do
|
|
53
|
+
if [[ -f "$dir/package.json" || -f "$dir/Cargo.toml" || \
|
|
54
|
+
-f "$dir/go.mod" || -f "$dir/pyproject.toml" ]]; then
|
|
55
|
+
echo "$dir"
|
|
56
|
+
return
|
|
57
|
+
fi
|
|
58
|
+
[[ "$dir" == "$REPO_ROOT" ]] && break
|
|
59
|
+
dir=$(dirname "$dir")
|
|
60
|
+
done
|
|
61
|
+
echo "$REPO_ROOT"
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
SPEC_BASE=$(detect_spec_base)
|
|
65
|
+
PKG_REL="${SPEC_BASE#"$REPO_ROOT"}"
|
|
66
|
+
PKG_REL="${PKG_REL#/}"
|
|
67
|
+
if [[ -n "$PKG_REL" ]]; then
|
|
68
|
+
SPEC_DIR="$REPO_ROOT/specs/$PKG_REL/$SLUG"
|
|
69
|
+
else
|
|
70
|
+
SPEC_DIR="$REPO_ROOT/specs/$SLUG"
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
case "$SPEC_DIR" in
|
|
74
|
+
"$REPO_ROOT/specs/"*) ;;
|
|
75
|
+
*) echo "ERROR: resolved spec path escapes $REPO_ROOT/specs: $SPEC_DIR" >&2; exit 1 ;;
|
|
76
|
+
esac
|
|
77
|
+
|
|
78
|
+
if [[ -d "$SPEC_DIR" ]]; then
|
|
79
|
+
echo "exists:$SPEC_DIR commit:$COMMIT date:$DATE"
|
|
80
|
+
exit 0
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
mkdir -p "$SPEC_DIR"
|
|
84
|
+
|
|
85
|
+
cat > "$SPEC_DIR/index.md" <<EOF
|
|
86
|
+
# $INPUT — Tech Spec
|
|
87
|
+
|
|
88
|
+
**Last updated:** $DATE
|
|
89
|
+
**Commit:** $COMMIT
|
|
90
|
+
**Status:** draft
|
|
91
|
+
|
|
92
|
+
## Contents
|
|
93
|
+
|
|
94
|
+
## Changelog
|
|
95
|
+
- $DATE ($COMMIT): Initial spec
|
|
96
|
+
EOF
|
|
97
|
+
|
|
98
|
+
echo "created:$SPEC_DIR commit:$COMMIT date:$DATE"
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# Spec File Templates
|
|
2
|
+
|
|
3
|
+
Copy-paste templates for each spec file. Omit sections that don't apply — don't leave empty headings.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## index.md
|
|
8
|
+
|
|
9
|
+
```markdown
|
|
10
|
+
# {Feature Name} — Tech Spec
|
|
11
|
+
|
|
12
|
+
**Last updated:** {YYYY-MM-DD}
|
|
13
|
+
**Commit:** {short-hash}
|
|
14
|
+
**Status:** draft | review | stable
|
|
15
|
+
|
|
16
|
+
## Contents
|
|
17
|
+
- [Decisions](decisions.md) — why we built it this way
|
|
18
|
+
- [API](api.md) — contracts, endpoints, types
|
|
19
|
+
- [Fields](fields.md) — field mappings: BE/3rd-party → UI
|
|
20
|
+
- [Implementation](implementation.md) — how it works, key files
|
|
21
|
+
- [Quirks](quirks.md) — edge cases and gotchas
|
|
22
|
+
|
|
23
|
+
## Changelog
|
|
24
|
+
- {YYYY-MM-DD} ({short-hash}): Initial spec
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## decisions.md
|
|
30
|
+
|
|
31
|
+
```markdown
|
|
32
|
+
# Decisions — {Feature Name}
|
|
33
|
+
|
|
34
|
+
## {Decision Title}
|
|
35
|
+
|
|
36
|
+
**Date:** {YYYY-MM-DD}
|
|
37
|
+
**Status:** accepted | superseded | deprecated
|
|
38
|
+
|
|
39
|
+
### Context
|
|
40
|
+
Why this decision needed to be made. What constraints or requirements drove it.
|
|
41
|
+
|
|
42
|
+
### Decision
|
|
43
|
+
What was decided, stated clearly.
|
|
44
|
+
|
|
45
|
+
### Rationale
|
|
46
|
+
Why this over the alternatives. Include alternatives considered and why they were rejected.
|
|
47
|
+
|
|
48
|
+
### Consequences
|
|
49
|
+
What changes as a result — positive and negative trade-offs.
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## api.md
|
|
55
|
+
|
|
56
|
+
```markdown
|
|
57
|
+
# API — {Feature Name}
|
|
58
|
+
|
|
59
|
+
## Endpoints
|
|
60
|
+
|
|
61
|
+
| Method | Path | Description | Auth |
|
|
62
|
+
|--------|------|-------------|------|
|
|
63
|
+
| GET | /api/... | ... | Bearer |
|
|
64
|
+
|
|
65
|
+
## Key Types / Schemas
|
|
66
|
+
|
|
67
|
+
\`\`\`typescript
|
|
68
|
+
interface ExampleType {
|
|
69
|
+
id: string;
|
|
70
|
+
// ...
|
|
71
|
+
}
|
|
72
|
+
\`\`\`
|
|
73
|
+
|
|
74
|
+
## External APIs Consumed
|
|
75
|
+
|
|
76
|
+
| Service | Purpose | Notes |
|
|
77
|
+
|---------|---------|-------|
|
|
78
|
+
| {Service} | {What we use it for} | {Rate limits, quirks} |
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## fields.md
|
|
84
|
+
|
|
85
|
+
```markdown
|
|
86
|
+
# Fields — {Feature Name}
|
|
87
|
+
|
|
88
|
+
Document which fields flow from source (BE or 3rd-party API) to destination (UI or your BE),
|
|
89
|
+
with descriptions so you can glance at the spec instead of traversing the codebase.
|
|
90
|
+
|
|
91
|
+
## Response Fields — {Endpoint or Source Name}
|
|
92
|
+
|
|
93
|
+
> Source: `GET /api/...` or `{ThirdParty} → BE → FE`
|
|
94
|
+
|
|
95
|
+
| Source Field | UI Label / FE Key | Type | Nullable | Description |
|
|
96
|
+
|---|---|---|---|---|
|
|
97
|
+
| `user_full_name` | Full Name | `string` | no | Display name shown in profile card |
|
|
98
|
+
| `created_at` | Created | `string (ISO 8601)` | no | Formatted to `MMM D, YYYY` in UI |
|
|
99
|
+
| `metadata.region` | Region | `string` | yes | Omitted from UI when null |
|
|
100
|
+
|
|
101
|
+
### Forwarding Chain (3rd-party → BE → FE)
|
|
102
|
+
|
|
103
|
+
Use this sub-section when your BE proxies a 3rd-party API and renames fields.
|
|
104
|
+
|
|
105
|
+
| 3rd-party Field | BE Field | FE Key | Notes |
|
|
106
|
+
|---|---|---|---|
|
|
107
|
+
| `usr_nm` | `username` | `username` | Renamed at BE layer |
|
|
108
|
+
| `acct_bal` | `accountBalance` | `balance` | Converted cents → dollars at BE |
|
|
109
|
+
|
|
110
|
+
## Request Fields — {Endpoint or Action Name}
|
|
111
|
+
|
|
112
|
+
> Sent from FE to `POST /api/...`
|
|
113
|
+
|
|
114
|
+
| FE Key | BE Field | Type | Required | Description |
|
|
115
|
+
|---|---|---|---|---|
|
|
116
|
+
| `email` | `email` | `string` | yes | Validated client-side before send |
|
|
117
|
+
| `preferredName` | `preferred_name` | `string` | no | Optional; omitted if empty |
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## implementation.md
|
|
123
|
+
|
|
124
|
+
```markdown
|
|
125
|
+
# Implementation — {Feature Name}
|
|
126
|
+
|
|
127
|
+
## Overview
|
|
128
|
+
One paragraph on how the feature works end-to-end.
|
|
129
|
+
|
|
130
|
+
## Key Files
|
|
131
|
+
|
|
132
|
+
| File | Role |
|
|
133
|
+
|------|------|
|
|
134
|
+
| `src/...` | ... |
|
|
135
|
+
|
|
136
|
+
## Data Flow
|
|
137
|
+
Step-by-step description of how data moves through the system.
|
|
138
|
+
|
|
139
|
+
## Patterns Used
|
|
140
|
+
Non-obvious patterns, abstractions, or conventions introduced.
|
|
141
|
+
|
|
142
|
+
## Dependencies
|
|
143
|
+
Internal modules or external packages this feature leans on heavily.
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## quirks.md
|
|
149
|
+
|
|
150
|
+
```markdown
|
|
151
|
+
# Quirks — {Feature Name}
|
|
152
|
+
|
|
153
|
+
## {Quirk Title}
|
|
154
|
+
|
|
155
|
+
**Severity:** low | medium | high
|
|
156
|
+
**Discovered:** {date or commit}
|
|
157
|
+
|
|
158
|
+
Description of the edge case, gotcha, or known issue. Include what triggers it and how it's handled or worked around.
|
|
159
|
+
```
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: test-first-development
|
|
3
|
+
description: "Tests-first development — write tests before implementation. Opt-in only. Get one upfront consent (write tests first + auto-run), then run the cycle without re-asking. Still always ask before updating existing test specs. Never silently rewrite test specs to match new behavior. Trigger when the user explicitly asks for tests-first, picks it at a workflow checkpoint, or when implementing logic with clear input/output behavior. Also triggers whenever an existing test spec is about to be edited to make it pass — that decision runs through this skill."
|
|
4
|
+
argument-hint: "<what to test>"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# /test-first-development
|
|
8
|
+
|
|
9
|
+
Tests first, implementation second. But only when you've asked for it.
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
/test-first-development $ARGUMENTS
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## The Ask Rules
|
|
18
|
+
|
|
19
|
+
**One upfront consent, then work.** At skill start, ask once:
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
"Tests-first for this task: I'll write failing tests, auto-run them
|
|
23
|
+
through each red-green-refactor cycle, and implement against them.
|
|
24
|
+
Ok to proceed?"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Wait for yes. After that, do **not** re-ask before writing new tests or
|
|
28
|
+
running them — the cycle needs fast red-green feedback, and re-asking at
|
|
29
|
+
every step is noise. If invoked from a `/workflow` checkpoint where the
|
|
30
|
+
user already picked "Write tests first", that pick *is* the consent —
|
|
31
|
+
start immediately.
|
|
32
|
+
|
|
33
|
+
**The one standing exception — updating existing test specs.** Always ask
|
|
34
|
+
before modifying any existing test file:
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
"This change affects existing test specs — want me to update them,
|
|
38
|
+
or review the diffs first?"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Never silently update an existing spec. That is a decision point every time.
|
|
42
|
+
|
|
43
|
+
## The RED-GREEN-REFACTOR Cycle
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
┌──────────────────────────────────────────────────────────────────┐
|
|
47
|
+
│ TDD CYCLE │
|
|
48
|
+
├──────────────────────────────────────────────────────────────────┤
|
|
49
|
+
│ RED │
|
|
50
|
+
│ → Write the failing test │
|
|
51
|
+
│ → Run it — it must fail before moving on │
|
|
52
|
+
│ (a test that passes immediately proves nothing) │
|
|
53
|
+
│ │
|
|
54
|
+
│ GREEN │
|
|
55
|
+
│ → Write the minimal implementation to make it pass │
|
|
56
|
+
│ → Run it — it must pass before moving on │
|
|
57
|
+
│ │
|
|
58
|
+
│ REFACTOR │
|
|
59
|
+
│ → Clean up without changing behavior │
|
|
60
|
+
│ → Run the tests again — they must still pass │
|
|
61
|
+
└──────────────────────────────────────────────────────────────────┘
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Run only the relevant test file(s) during the cycle — the full suite
|
|
65
|
+
belongs to `/finalize`.
|
|
66
|
+
|
|
67
|
+
## What Makes a Good Test
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
✓ Tests behavior, not implementation details
|
|
71
|
+
✓ One clear assertion per test
|
|
72
|
+
✓ Descriptive name: "should return error when token is expired"
|
|
73
|
+
✓ Tests edge cases discovered during thinking, not after
|
|
74
|
+
✓ Independent — doesn't rely on other tests running first
|
|
75
|
+
|
|
76
|
+
✗ Tests internal function calls or private methods
|
|
77
|
+
✗ Tests that only pass because of the implementation you just wrote
|
|
78
|
+
✗ Vague names: "test1", "should work"
|
|
79
|
+
✗ Tests that need other tests to run first
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Updating Existing Test Specs
|
|
83
|
+
|
|
84
|
+
This is the most dangerous operation in TDD. Silently rewriting specs to make a bug fix pass is how bugs get buried.
|
|
85
|
+
|
|
86
|
+
Before touching any existing test file:
|
|
87
|
+
|
|
88
|
+
1. Show the current spec and what would change
|
|
89
|
+
2. Explain why the change is necessary (behavior change vs. test was wrong)
|
|
90
|
+
3. Wait for explicit approval
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
Legitimate reasons to update a spec:
|
|
94
|
+
✓ The feature behavior intentionally changed (product decision)
|
|
95
|
+
✓ The test was testing the wrong thing (implementation detail)
|
|
96
|
+
✓ The test was brittle/flaky and needs a better assertion
|
|
97
|
+
|
|
98
|
+
NOT legitimate:
|
|
99
|
+
✗ "The test was failing so I updated it to match the new output"
|
|
100
|
+
✗ "The behavior changed as a side effect of the fix"
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## When TDD Applies (and When It Doesn't)
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
Good candidates for TDD:
|
|
107
|
+
✓ Pure functions with clear inputs/outputs
|
|
108
|
+
✓ Utility logic (date formatting, currency, validation)
|
|
109
|
+
✓ API handlers with predictable request/response shapes
|
|
110
|
+
✓ Bug fixes where a failing test proves the bug exists
|
|
111
|
+
|
|
112
|
+
Poor candidates (don't force TDD here):
|
|
113
|
+
✗ UI layout and visual components
|
|
114
|
+
✗ Framework glue code (wiring up routes, registering providers)
|
|
115
|
+
✗ One-off scripts or migrations
|
|
116
|
+
✗ Prototyping — test after shape is clear
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## See Also
|
|
120
|
+
|
|
121
|
+
- `references/rationalization-table.md` — common rationalizations for skipping tests-first and why they're wrong
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
[
|
|
2
|
+
{ "query": "write tests first for this", "should_trigger": true, "expected_skill": "test-first-development" },
|
|
3
|
+
{ "query": "tdd this feature", "should_trigger": true, "expected_skill": "test-first-development" },
|
|
4
|
+
{ "query": "test-first for the parser module", "should_trigger": true, "expected_skill": "test-first-development" },
|
|
5
|
+
{ "query": "write failing tests before implementing", "should_trigger": true, "expected_skill": "test-first-development" },
|
|
6
|
+
{ "query": "red green refactor cycle please", "should_trigger": true, "expected_skill": "test-first-development" },
|
|
7
|
+
{ "query": "can you do this test driven", "should_trigger": true, "expected_skill": "test-first-development" },
|
|
8
|
+
{ "query": "write unit tests for the date helpers before you code them", "should_trigger": true, "expected_skill": "test-first-development" },
|
|
9
|
+
{ "query": "update the failing test to match the new behavior", "should_trigger": true, "expected_skill": "test-first-development" },
|
|
10
|
+
{ "query": "the test is failing so just fix the test", "should_trigger": true, "expected_skill": "test-first-development" },
|
|
11
|
+
{ "query": "should I tdd ui components", "should_trigger": true, "expected_skill": "test-first-development" },
|
|
12
|
+
{ "query": "you already asked once, dont re-ask each cycle right?", "should_trigger": true, "expected_skill": "test-first-development", "kind": "in-context" },
|
|
13
|
+
{ "query": "bug: prove it with a failing test then fix it", "should_trigger": true, "expected_skill": "test-first-development" },
|
|
14
|
+
{ "query": "tets first plz", "should_trigger": true, "expected_skill": "test-first-development" },
|
|
15
|
+
{ "query": "add tests for the existing legacy code", "should_trigger": true, "expected_skill": "workflow" },
|
|
16
|
+
{ "query": "implement the currency formatter utility", "should_trigger": true, "expected_skill": "workflow" },
|
|
17
|
+
{ "query": "write e2e tests with playwright for the flow", "should_trigger": true, "expected_skill": "workflow" },
|
|
18
|
+
{ "query": "run the tests", "should_trigger": true, "expected_skill": "finalize" },
|
|
19
|
+
{ "query": "why is this test flaky", "should_trigger": true, "expected_skill": "root-cause-analysis" },
|
|
20
|
+
{ "query": "jest config setup for the monorepo", "should_trigger": true, "expected_skill": "workflow" },
|
|
21
|
+
{ "query": "wheres the coverage report hosted", "should_trigger": false, "expected_skill": null }
|
|
22
|
+
]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Tests-First Rationalization Table
|
|
2
|
+
|
|
3
|
+
Common rationalizations agents (and developers) use to skip writing tests — and why they're wrong.
|
|
4
|
+
|
|
5
|
+
| Rationalization | Reality | Counter |
|
|
6
|
+
| ------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
|
|
7
|
+
| "This change is too small to need a test" | Small changes break things too. The test takes 2 minutes. | Write the test. It documents the behavior even if it never catches a regression. |
|
|
8
|
+
| "I'll write tests after the implementation is working" | Tests written after are biased by the implementation you built. You test what you know works, not what could break. | Tests first force you to think about edge cases before you're anchored to an implementation. |
|
|
9
|
+
| "The existing tests cover this" | They cover the old behavior. The new behavior needs its own test. | Add a test for the new behavior. Don't assume coverage. |
|
|
10
|
+
| "This is just UI, tests don't apply" | Logic in UI components (conditional rendering, state transitions, handlers) can and should be tested. Layout doesn't need tests. Logic does. | Separate the logic from the layout. Test the logic. |
|
|
11
|
+
| "We're moving fast, we'll add tests later" | "Later" doesn't come. The feature ships, the next feature starts, the tests never get written. | Tests now are cheaper than debugging later. The test is part of the task, not optional cleanup. |
|
|
12
|
+
| "The type system already catches this" | Types catch shape errors. Tests catch behavioral errors. These are different things. | Types and tests are complementary, not substitutes. |
|
|
13
|
+
| "Running tests takes too long" | Running the specific test for the specific change takes seconds. | Run only the relevant test file, not the full suite. |
|
|
14
|
+
| "I need to update the spec to make the test pass" | If you're rewriting the spec to match new output, you're not fixing a bug — you're hiding one. | Stop. Explain what changed and why. Get explicit approval before touching existing specs. |
|
|
15
|
+
| "This is a prototype, no tests needed" | Prototypes become production code. Untested prototypes become untested production code. | Decide: is this throwaway? If yes, delete it. If it might ship, test the core logic. |
|
|
16
|
+
| "The test is too hard to write" | If the test is hard to write, the code is too coupled or too complex. | Hard-to-test code is a design smell. Simplify the code, then write the test. |
|