cap-pro 1.0.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.
Files changed (275) hide show
  1. package/.claude-plugin/README.md +26 -0
  2. package/.claude-plugin/marketplace.json +24 -0
  3. package/.claude-plugin/plugin.json +24 -0
  4. package/LICENSE +21 -0
  5. package/README.ja-JP.md +834 -0
  6. package/README.ko-KR.md +823 -0
  7. package/README.md +806 -0
  8. package/README.pt-BR.md +452 -0
  9. package/README.zh-CN.md +800 -0
  10. package/agents/cap-architect.md +269 -0
  11. package/agents/cap-brainstormer.md +207 -0
  12. package/agents/cap-curator.md +276 -0
  13. package/agents/cap-debugger.md +365 -0
  14. package/agents/cap-designer.md +246 -0
  15. package/agents/cap-historian.md +464 -0
  16. package/agents/cap-migrator.md +291 -0
  17. package/agents/cap-prototyper.md +197 -0
  18. package/agents/cap-validator.md +308 -0
  19. package/bin/install.js +5433 -0
  20. package/cap/bin/cap-tools.cjs +853 -0
  21. package/cap/bin/lib/arc-scanner.cjs +344 -0
  22. package/cap/bin/lib/cap-affinity-engine.cjs +862 -0
  23. package/cap/bin/lib/cap-anchor.cjs +228 -0
  24. package/cap/bin/lib/cap-annotation-writer.cjs +340 -0
  25. package/cap/bin/lib/cap-checkpoint.cjs +434 -0
  26. package/cap/bin/lib/cap-cluster-detect.cjs +945 -0
  27. package/cap/bin/lib/cap-cluster-display.cjs +52 -0
  28. package/cap/bin/lib/cap-cluster-format.cjs +245 -0
  29. package/cap/bin/lib/cap-cluster-helpers.cjs +295 -0
  30. package/cap/bin/lib/cap-cluster-io.cjs +212 -0
  31. package/cap/bin/lib/cap-completeness.cjs +540 -0
  32. package/cap/bin/lib/cap-deps.cjs +583 -0
  33. package/cap/bin/lib/cap-design-families.cjs +332 -0
  34. package/cap/bin/lib/cap-design.cjs +966 -0
  35. package/cap/bin/lib/cap-divergence-detector.cjs +400 -0
  36. package/cap/bin/lib/cap-doctor.cjs +752 -0
  37. package/cap/bin/lib/cap-feature-map-internals.cjs +19 -0
  38. package/cap/bin/lib/cap-feature-map-migrate.cjs +335 -0
  39. package/cap/bin/lib/cap-feature-map-monorepo.cjs +885 -0
  40. package/cap/bin/lib/cap-feature-map-shard.cjs +315 -0
  41. package/cap/bin/lib/cap-feature-map.cjs +1943 -0
  42. package/cap/bin/lib/cap-fitness-score.cjs +1075 -0
  43. package/cap/bin/lib/cap-impact-analysis.cjs +652 -0
  44. package/cap/bin/lib/cap-learn-review.cjs +1072 -0
  45. package/cap/bin/lib/cap-learning-signals.cjs +627 -0
  46. package/cap/bin/lib/cap-loader.cjs +227 -0
  47. package/cap/bin/lib/cap-logger.cjs +57 -0
  48. package/cap/bin/lib/cap-memory-bridge.cjs +764 -0
  49. package/cap/bin/lib/cap-memory-confidence.cjs +452 -0
  50. package/cap/bin/lib/cap-memory-dir.cjs +987 -0
  51. package/cap/bin/lib/cap-memory-engine.cjs +698 -0
  52. package/cap/bin/lib/cap-memory-extends.cjs +398 -0
  53. package/cap/bin/lib/cap-memory-graph.cjs +790 -0
  54. package/cap/bin/lib/cap-memory-migrate.cjs +2015 -0
  55. package/cap/bin/lib/cap-memory-pin.cjs +183 -0
  56. package/cap/bin/lib/cap-memory-platform.cjs +490 -0
  57. package/cap/bin/lib/cap-memory-prune.cjs +707 -0
  58. package/cap/bin/lib/cap-memory-schema.cjs +812 -0
  59. package/cap/bin/lib/cap-migrate-tags.cjs +309 -0
  60. package/cap/bin/lib/cap-migrate.cjs +540 -0
  61. package/cap/bin/lib/cap-pattern-apply.cjs +1203 -0
  62. package/cap/bin/lib/cap-pattern-pipeline.cjs +1034 -0
  63. package/cap/bin/lib/cap-plugin-manifest.cjs +80 -0
  64. package/cap/bin/lib/cap-realtime-affinity.cjs +399 -0
  65. package/cap/bin/lib/cap-reconcile.cjs +570 -0
  66. package/cap/bin/lib/cap-research-gate.cjs +218 -0
  67. package/cap/bin/lib/cap-scope-filter.cjs +402 -0
  68. package/cap/bin/lib/cap-semantic-pipeline.cjs +1038 -0
  69. package/cap/bin/lib/cap-session-extract.cjs +987 -0
  70. package/cap/bin/lib/cap-session.cjs +445 -0
  71. package/cap/bin/lib/cap-snapshot-linkage.cjs +963 -0
  72. package/cap/bin/lib/cap-stack-docs.cjs +646 -0
  73. package/cap/bin/lib/cap-tag-observer.cjs +371 -0
  74. package/cap/bin/lib/cap-tag-scanner.cjs +1766 -0
  75. package/cap/bin/lib/cap-telemetry.cjs +466 -0
  76. package/cap/bin/lib/cap-test-audit.cjs +1438 -0
  77. package/cap/bin/lib/cap-thread-migrator.cjs +307 -0
  78. package/cap/bin/lib/cap-thread-synthesis.cjs +545 -0
  79. package/cap/bin/lib/cap-thread-tracker.cjs +519 -0
  80. package/cap/bin/lib/cap-trace.cjs +399 -0
  81. package/cap/bin/lib/cap-trust-mode.cjs +336 -0
  82. package/cap/bin/lib/cap-ui-design-editor.cjs +642 -0
  83. package/cap/bin/lib/cap-ui-mind-map.cjs +712 -0
  84. package/cap/bin/lib/cap-ui-thread-nav.cjs +693 -0
  85. package/cap/bin/lib/cap-ui.cjs +1245 -0
  86. package/cap/bin/lib/cap-upgrade.cjs +1028 -0
  87. package/cap/bin/lib/cli/arg-helpers.cjs +49 -0
  88. package/cap/bin/lib/cli/frontmatter-router.cjs +31 -0
  89. package/cap/bin/lib/cli/init-router.cjs +68 -0
  90. package/cap/bin/lib/cli/phase-router.cjs +102 -0
  91. package/cap/bin/lib/cli/state-router.cjs +61 -0
  92. package/cap/bin/lib/cli/template-router.cjs +37 -0
  93. package/cap/bin/lib/cli/uat-router.cjs +29 -0
  94. package/cap/bin/lib/cli/validation-router.cjs +26 -0
  95. package/cap/bin/lib/cli/verification-router.cjs +31 -0
  96. package/cap/bin/lib/cli/workstream-router.cjs +39 -0
  97. package/cap/bin/lib/commands.cjs +961 -0
  98. package/cap/bin/lib/config.cjs +467 -0
  99. package/cap/bin/lib/convention-reader.cjs +258 -0
  100. package/cap/bin/lib/core.cjs +1241 -0
  101. package/cap/bin/lib/feature-aggregator.cjs +423 -0
  102. package/cap/bin/lib/frontmatter.cjs +337 -0
  103. package/cap/bin/lib/init.cjs +1443 -0
  104. package/cap/bin/lib/manifest-generator.cjs +383 -0
  105. package/cap/bin/lib/milestone.cjs +253 -0
  106. package/cap/bin/lib/model-profiles.cjs +69 -0
  107. package/cap/bin/lib/monorepo-context.cjs +226 -0
  108. package/cap/bin/lib/monorepo-migrator.cjs +509 -0
  109. package/cap/bin/lib/phase.cjs +889 -0
  110. package/cap/bin/lib/profile-output.cjs +989 -0
  111. package/cap/bin/lib/profile-pipeline.cjs +540 -0
  112. package/cap/bin/lib/roadmap.cjs +330 -0
  113. package/cap/bin/lib/security.cjs +394 -0
  114. package/cap/bin/lib/session-manager.cjs +292 -0
  115. package/cap/bin/lib/skeleton-generator.cjs +179 -0
  116. package/cap/bin/lib/state.cjs +1032 -0
  117. package/cap/bin/lib/template.cjs +231 -0
  118. package/cap/bin/lib/test-detector.cjs +62 -0
  119. package/cap/bin/lib/uat.cjs +283 -0
  120. package/cap/bin/lib/verify.cjs +889 -0
  121. package/cap/bin/lib/workspace-detector.cjs +371 -0
  122. package/cap/bin/lib/workstream.cjs +492 -0
  123. package/cap/commands/gsd/workstreams.md +63 -0
  124. package/cap/references/arc-standard.md +315 -0
  125. package/cap/references/cap-agent-architecture.md +101 -0
  126. package/cap/references/cap-gitignore-template +9 -0
  127. package/cap/references/cap-zero-deps.md +158 -0
  128. package/cap/references/checkpoints.md +778 -0
  129. package/cap/references/continuation-format.md +249 -0
  130. package/cap/references/contract-test-templates.md +312 -0
  131. package/cap/references/feature-map-template.md +25 -0
  132. package/cap/references/git-integration.md +295 -0
  133. package/cap/references/git-planning-commit.md +38 -0
  134. package/cap/references/model-profiles.md +174 -0
  135. package/cap/references/phase-numbering.md +126 -0
  136. package/cap/references/planning-config.md +202 -0
  137. package/cap/references/property-test-templates.md +316 -0
  138. package/cap/references/security-test-templates.md +347 -0
  139. package/cap/references/session-template.json +8 -0
  140. package/cap/references/tdd.md +263 -0
  141. package/cap/references/user-profiling.md +681 -0
  142. package/cap/references/verification-patterns.md +612 -0
  143. package/cap/templates/UAT.md +265 -0
  144. package/cap/templates/claude-md.md +175 -0
  145. package/cap/templates/codebase/architecture.md +255 -0
  146. package/cap/templates/codebase/concerns.md +310 -0
  147. package/cap/templates/codebase/conventions.md +307 -0
  148. package/cap/templates/codebase/integrations.md +280 -0
  149. package/cap/templates/codebase/stack.md +186 -0
  150. package/cap/templates/codebase/structure.md +285 -0
  151. package/cap/templates/codebase/testing.md +480 -0
  152. package/cap/templates/config.json +44 -0
  153. package/cap/templates/context.md +352 -0
  154. package/cap/templates/continue-here.md +78 -0
  155. package/cap/templates/copilot-instructions.md +7 -0
  156. package/cap/templates/debug-subagent-prompt.md +91 -0
  157. package/cap/templates/discussion-log.md +63 -0
  158. package/cap/templates/milestone-archive.md +123 -0
  159. package/cap/templates/milestone.md +115 -0
  160. package/cap/templates/phase-prompt.md +610 -0
  161. package/cap/templates/planner-subagent-prompt.md +117 -0
  162. package/cap/templates/project.md +186 -0
  163. package/cap/templates/requirements.md +231 -0
  164. package/cap/templates/research-project/ARCHITECTURE.md +204 -0
  165. package/cap/templates/research-project/FEATURES.md +147 -0
  166. package/cap/templates/research-project/PITFALLS.md +200 -0
  167. package/cap/templates/research-project/STACK.md +120 -0
  168. package/cap/templates/research-project/SUMMARY.md +170 -0
  169. package/cap/templates/research.md +552 -0
  170. package/cap/templates/roadmap.md +202 -0
  171. package/cap/templates/state.md +176 -0
  172. package/cap/templates/summary.md +364 -0
  173. package/cap/templates/user-preferences.md +498 -0
  174. package/cap/templates/verification-report.md +322 -0
  175. package/cap/workflows/add-phase.md +112 -0
  176. package/cap/workflows/add-tests.md +351 -0
  177. package/cap/workflows/add-todo.md +158 -0
  178. package/cap/workflows/audit-milestone.md +340 -0
  179. package/cap/workflows/audit-uat.md +109 -0
  180. package/cap/workflows/autonomous.md +891 -0
  181. package/cap/workflows/check-todos.md +177 -0
  182. package/cap/workflows/cleanup.md +152 -0
  183. package/cap/workflows/complete-milestone.md +767 -0
  184. package/cap/workflows/diagnose-issues.md +231 -0
  185. package/cap/workflows/discovery-phase.md +289 -0
  186. package/cap/workflows/discuss-phase-assumptions.md +653 -0
  187. package/cap/workflows/discuss-phase.md +1049 -0
  188. package/cap/workflows/do.md +104 -0
  189. package/cap/workflows/execute-phase.md +846 -0
  190. package/cap/workflows/execute-plan.md +514 -0
  191. package/cap/workflows/fast.md +105 -0
  192. package/cap/workflows/forensics.md +265 -0
  193. package/cap/workflows/health.md +181 -0
  194. package/cap/workflows/help.md +660 -0
  195. package/cap/workflows/insert-phase.md +130 -0
  196. package/cap/workflows/list-phase-assumptions.md +178 -0
  197. package/cap/workflows/list-workspaces.md +56 -0
  198. package/cap/workflows/manager.md +362 -0
  199. package/cap/workflows/map-codebase.md +377 -0
  200. package/cap/workflows/milestone-summary.md +223 -0
  201. package/cap/workflows/new-milestone.md +486 -0
  202. package/cap/workflows/new-project.md +1250 -0
  203. package/cap/workflows/new-workspace.md +237 -0
  204. package/cap/workflows/next.md +97 -0
  205. package/cap/workflows/node-repair.md +92 -0
  206. package/cap/workflows/note.md +156 -0
  207. package/cap/workflows/pause-work.md +176 -0
  208. package/cap/workflows/plan-milestone-gaps.md +273 -0
  209. package/cap/workflows/plan-phase.md +857 -0
  210. package/cap/workflows/plant-seed.md +169 -0
  211. package/cap/workflows/pr-branch.md +129 -0
  212. package/cap/workflows/profile-user.md +449 -0
  213. package/cap/workflows/progress.md +507 -0
  214. package/cap/workflows/quick.md +757 -0
  215. package/cap/workflows/remove-phase.md +155 -0
  216. package/cap/workflows/remove-workspace.md +90 -0
  217. package/cap/workflows/research-phase.md +82 -0
  218. package/cap/workflows/resume-project.md +326 -0
  219. package/cap/workflows/review.md +228 -0
  220. package/cap/workflows/session-report.md +146 -0
  221. package/cap/workflows/settings.md +283 -0
  222. package/cap/workflows/ship.md +228 -0
  223. package/cap/workflows/stats.md +60 -0
  224. package/cap/workflows/transition.md +671 -0
  225. package/cap/workflows/ui-phase.md +298 -0
  226. package/cap/workflows/ui-review.md +161 -0
  227. package/cap/workflows/update.md +323 -0
  228. package/cap/workflows/validate-phase.md +170 -0
  229. package/cap/workflows/verify-phase.md +254 -0
  230. package/cap/workflows/verify-work.md +637 -0
  231. package/commands/cap/annotate.md +165 -0
  232. package/commands/cap/brainstorm.md +393 -0
  233. package/commands/cap/checkpoint.md +106 -0
  234. package/commands/cap/completeness.md +94 -0
  235. package/commands/cap/continue.md +72 -0
  236. package/commands/cap/debug.md +588 -0
  237. package/commands/cap/deps.md +169 -0
  238. package/commands/cap/design.md +479 -0
  239. package/commands/cap/init.md +354 -0
  240. package/commands/cap/iterate.md +249 -0
  241. package/commands/cap/learn.md +459 -0
  242. package/commands/cap/memory.md +275 -0
  243. package/commands/cap/migrate-feature-map.md +91 -0
  244. package/commands/cap/migrate-memory.md +108 -0
  245. package/commands/cap/migrate-tags.md +91 -0
  246. package/commands/cap/migrate.md +131 -0
  247. package/commands/cap/prototype.md +510 -0
  248. package/commands/cap/reconcile.md +121 -0
  249. package/commands/cap/review.md +360 -0
  250. package/commands/cap/save.md +72 -0
  251. package/commands/cap/scan.md +404 -0
  252. package/commands/cap/start.md +356 -0
  253. package/commands/cap/status.md +118 -0
  254. package/commands/cap/test-audit.md +262 -0
  255. package/commands/cap/test.md +394 -0
  256. package/commands/cap/trace.md +133 -0
  257. package/commands/cap/ui.md +167 -0
  258. package/hooks/dist/cap-check-update.js +115 -0
  259. package/hooks/dist/cap-context-monitor.js +185 -0
  260. package/hooks/dist/cap-learn-review-hook.js +114 -0
  261. package/hooks/dist/cap-learning-hook.js +192 -0
  262. package/hooks/dist/cap-memory.js +299 -0
  263. package/hooks/dist/cap-prompt-guard.js +97 -0
  264. package/hooks/dist/cap-statusline.js +157 -0
  265. package/hooks/dist/cap-tag-observer.js +115 -0
  266. package/hooks/dist/cap-version-check.js +112 -0
  267. package/hooks/dist/cap-workflow-guard.js +175 -0
  268. package/hooks/hooks.json +55 -0
  269. package/package.json +58 -0
  270. package/scripts/base64-scan.sh +262 -0
  271. package/scripts/build-hooks.js +93 -0
  272. package/scripts/cap-removal-checklist.md +202 -0
  273. package/scripts/prompt-injection-scan.sh +199 -0
  274. package/scripts/run-tests.cjs +181 -0
  275. package/scripts/secret-scan.sh +227 -0
@@ -0,0 +1,202 @@
1
+ <planning_config>
2
+
3
+ Configuration options for `.planning/` directory behavior.
4
+
5
+ <config_schema>
6
+ ```json
7
+ "planning": {
8
+ "commit_docs": true,
9
+ "search_gitignored": false
10
+ },
11
+ "git": {
12
+ "branching_strategy": "none",
13
+ "phase_branch_template": "gsd/phase-{phase}-{slug}",
14
+ "milestone_branch_template": "gsd/{milestone}-{slug}",
15
+ "quick_branch_template": null
16
+ }
17
+ ```
18
+
19
+ | Option | Default | Description |
20
+ |--------|---------|-------------|
21
+ | `commit_docs` | `true` | Whether to commit planning artifacts to git |
22
+ | `search_gitignored` | `false` | Add `--no-ignore` to broad rg searches |
23
+ | `git.branching_strategy` | `"none"` | Git branching approach: `"none"`, `"phase"`, or `"milestone"` |
24
+ | `git.phase_branch_template` | `"gsd/phase-{phase}-{slug}"` | Branch template for phase strategy |
25
+ | `git.milestone_branch_template` | `"gsd/{milestone}-{slug}"` | Branch template for milestone strategy |
26
+ | `git.quick_branch_template` | `null` | Optional branch template for quick-task runs |
27
+ </config_schema>
28
+
29
+ <commit_docs_behavior>
30
+
31
+ **When `commit_docs: true` (default):**
32
+ - Planning files committed normally
33
+ - SUMMARY.md, STATE.md, ROADMAP.md tracked in git
34
+ - Full history of planning decisions preserved
35
+
36
+ **When `commit_docs: false`:**
37
+ - Skip all `git add`/`git commit` for `.planning/` files
38
+ - User must add `.planning/` to `.gitignore`
39
+ - Useful for: OSS contributions, client projects, keeping planning private
40
+
41
+ **Using cap-tools.cjs (preferred):**
42
+
43
+ ```bash
44
+ # Commit with automatic commit_docs + gitignore checks:
45
+ node "$HOME/.claude/cap/bin/cap-tools.cjs" commit "docs: update state" --files .planning/STATE.md
46
+
47
+ # Load config via state load (returns JSON):
48
+ INIT=$(node "$HOME/.claude/cap/bin/cap-tools.cjs" state load)
49
+ if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
50
+ # commit_docs is available in the JSON output
51
+
52
+ # Or use init commands which include commit_docs:
53
+ INIT=$(node "$HOME/.claude/cap/bin/cap-tools.cjs" init execute-phase "1")
54
+ if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
55
+ # commit_docs is included in all init command outputs
56
+ ```
57
+
58
+ **Auto-detection:** If `.planning/` is gitignored, `commit_docs` is automatically `false` regardless of config.json. This prevents git errors when users have `.planning/` in `.gitignore`.
59
+
60
+ **Commit via CLI (handles checks automatically):**
61
+
62
+ ```bash
63
+ node "$HOME/.claude/cap/bin/cap-tools.cjs" commit "docs: update state" --files .planning/STATE.md
64
+ ```
65
+
66
+ The CLI checks `commit_docs` config and gitignore status internally — no manual conditionals needed.
67
+
68
+ </commit_docs_behavior>
69
+
70
+ <search_behavior>
71
+
72
+ **When `search_gitignored: false` (default):**
73
+ - Standard rg behavior (respects .gitignore)
74
+ - Direct path searches work: `rg "pattern" .planning/` finds files
75
+ - Broad searches skip gitignored: `rg "pattern"` skips `.planning/`
76
+
77
+ **When `search_gitignored: true`:**
78
+ - Add `--no-ignore` to broad rg searches that should include `.planning/`
79
+ - Only needed when searching entire repo and expecting `.planning/` matches
80
+
81
+ **Note:** Most GSD operations use direct file reads or explicit paths, which work regardless of gitignore status.
82
+
83
+ </search_behavior>
84
+
85
+ <setup_uncommitted_mode>
86
+
87
+ To use uncommitted mode:
88
+
89
+ 1. **Set config:**
90
+ ```json
91
+ "planning": {
92
+ "commit_docs": false,
93
+ "search_gitignored": true
94
+ }
95
+ ```
96
+
97
+ 2. **Add to .gitignore:**
98
+ ```
99
+ .planning/
100
+ ```
101
+
102
+ 3. **Existing tracked files:** If `.planning/` was previously tracked:
103
+ ```bash
104
+ git rm -r --cached .planning/
105
+ git commit -m "chore: stop tracking planning docs"
106
+ ```
107
+
108
+ 4. **Branch merges:** When using `branching_strategy: phase` or `milestone`, the `complete-milestone` workflow automatically strips `.planning/` files from staging before merge commits when `commit_docs: false`.
109
+
110
+ </setup_uncommitted_mode>
111
+
112
+ <branching_strategy_behavior>
113
+
114
+ **Branching Strategies:**
115
+
116
+ | Strategy | When branch created | Branch scope | Merge point |
117
+ |----------|---------------------|--------------|-------------|
118
+ | `none` | Never | N/A | N/A |
119
+ | `phase` | At `execute-phase` start | Single phase | User merges after phase |
120
+ | `milestone` | At first `execute-phase` of milestone | Entire milestone | At `complete-milestone` |
121
+
122
+ **When `git.branching_strategy: "none"` (default):**
123
+ - All work commits to current branch
124
+ - Standard GSD behavior
125
+
126
+ **When `git.branching_strategy: "phase"`:**
127
+ - `execute-phase` creates/switches to a branch before execution
128
+ - Branch name from `phase_branch_template` (e.g., `gsd/phase-03-authentication`)
129
+ - All plan commits go to that branch
130
+ - User merges branches manually after phase completion
131
+ - `complete-milestone` offers to merge all phase branches
132
+
133
+ **When `git.branching_strategy: "milestone"`:**
134
+ - First `execute-phase` of milestone creates the milestone branch
135
+ - Branch name from `milestone_branch_template` (e.g., `gsd/v1.0-mvp`)
136
+ - All phases in milestone commit to same branch
137
+ - `complete-milestone` offers to merge milestone branch to main
138
+
139
+ **Template variables:**
140
+
141
+ | Variable | Available in | Description |
142
+ |----------|--------------|-------------|
143
+ | `{phase}` | phase_branch_template | Zero-padded phase number (e.g., "03") |
144
+ | `{slug}` | Both | Lowercase, hyphenated name |
145
+ | `{milestone}` | milestone_branch_template | Milestone version (e.g., "v1.0") |
146
+
147
+ **Checking the config:**
148
+
149
+ Use `init execute-phase` which returns all config as JSON:
150
+ ```bash
151
+ INIT=$(node "$HOME/.claude/cap/bin/cap-tools.cjs" init execute-phase "1")
152
+ if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
153
+ # JSON output includes: branching_strategy, phase_branch_template, milestone_branch_template
154
+ ```
155
+
156
+ Or use `state load` for the config values:
157
+ ```bash
158
+ INIT=$(node "$HOME/.claude/cap/bin/cap-tools.cjs" state load)
159
+ if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
160
+ # Parse branching_strategy, phase_branch_template, milestone_branch_template from JSON
161
+ ```
162
+
163
+ **Branch creation:**
164
+
165
+ ```bash
166
+ # For phase strategy
167
+ if [ "$BRANCHING_STRATEGY" = "phase" ]; then
168
+ PHASE_SLUG=$(echo "$PHASE_NAME" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-//;s/-$//')
169
+ BRANCH_NAME=$(echo "$PHASE_BRANCH_TEMPLATE" | sed "s/{phase}/$PADDED_PHASE/g" | sed "s/{slug}/$PHASE_SLUG/g")
170
+ git checkout -b "$BRANCH_NAME" 2>/dev/null || git checkout "$BRANCH_NAME"
171
+ fi
172
+
173
+ # For milestone strategy
174
+ if [ "$BRANCHING_STRATEGY" = "milestone" ]; then
175
+ MILESTONE_SLUG=$(echo "$MILESTONE_NAME" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-//;s/-$//')
176
+ BRANCH_NAME=$(echo "$MILESTONE_BRANCH_TEMPLATE" | sed "s/{milestone}/$MILESTONE_VERSION/g" | sed "s/{slug}/$MILESTONE_SLUG/g")
177
+ git checkout -b "$BRANCH_NAME" 2>/dev/null || git checkout "$BRANCH_NAME"
178
+ fi
179
+ ```
180
+
181
+ **Merge options at complete-milestone:**
182
+
183
+ | Option | Git command | Result |
184
+ |--------|-------------|--------|
185
+ | Squash merge (recommended) | `git merge --squash` | Single clean commit per branch |
186
+ | Merge with history | `git merge --no-ff` | Preserves all individual commits |
187
+ | Delete without merging | `git branch -D` | Discard branch work |
188
+ | Keep branches | (none) | Manual handling later |
189
+
190
+ Squash merge is recommended — keeps main branch history clean while preserving the full development history in the branch (until deleted).
191
+
192
+ **Use cases:**
193
+
194
+ | Strategy | Best for |
195
+ |----------|----------|
196
+ | `none` | Solo development, simple projects |
197
+ | `phase` | Code review per phase, granular rollback, team collaboration |
198
+ | `milestone` | Release branches, staging environments, PR per version |
199
+
200
+ </branching_strategy_behavior>
201
+
202
+ </planning_config>
@@ -0,0 +1,316 @@
1
+ # Property-Based Test Templates
2
+
3
+ Reference document for the cap-validator agent (test mode) when generating property-based tests. Property-based testing verifies invariants that hold for ALL valid inputs, not just hand-picked examples. Recommend `fast-check` as the property testing library.
4
+
5
+ ---
6
+
7
+ ## 1. Booking Invariants
8
+
9
+ Properties that must hold for any valid booking system.
10
+
11
+ ### No overlapping bookings
12
+
13
+ ```typescript
14
+ import { describe, it, expect } from 'vitest';
15
+ import * as fc from 'fast-check';
16
+
17
+ describe('Property: booking invariants', () => {
18
+ // Arbitrary for generating valid booking time ranges
19
+ const bookingArb = fc.record({
20
+ start: fc.date({ min: new Date('2026-01-01'), max: new Date('2026-12-31') }),
21
+ durationMinutes: fc.integer({ min: 15, max: 480 }),
22
+ resourceId: fc.constantFrom('room-a', 'room-b', 'room-c'),
23
+ }).map(({ start, durationMinutes, resourceId }) => ({
24
+ start,
25
+ end: new Date(start.getTime() + durationMinutes * 60000),
26
+ resourceId,
27
+ }));
28
+
29
+ it('no two confirmed bookings overlap for the same resource', () => {
30
+ fc.assert(
31
+ fc.property(
32
+ fc.array(bookingArb, { minLength: 2, maxLength: 20 }),
33
+ (bookings) => {
34
+ const confirmed = createBookings(bookings); // system under test
35
+ const byResource = groupByResource(confirmed);
36
+
37
+ for (const [, resourceBookings] of Object.entries(byResource)) {
38
+ const sorted = resourceBookings.sort((a, b) => a.start - b.start);
39
+ for (let i = 1; i < sorted.length; i++) {
40
+ // No overlap: previous end <= current start
41
+ expect(sorted[i - 1].end.getTime()).toBeLessThanOrEqual(sorted[i].start.getTime());
42
+ }
43
+ }
44
+ }
45
+ )
46
+ );
47
+ });
48
+
49
+ it('booking duration is always positive', () => {
50
+ fc.assert(
51
+ fc.property(bookingArb, (booking) => {
52
+ expect(booking.end.getTime()).toBeGreaterThan(booking.start.getTime());
53
+ })
54
+ );
55
+ });
56
+
57
+ it('booking end is always after start', () => {
58
+ fc.assert(
59
+ fc.property(bookingArb, (booking) => {
60
+ const created = createBooking(booking); // system under test
61
+ expect(new Date(created.end_time).getTime()).toBeGreaterThan(
62
+ new Date(created.start_time).getTime()
63
+ );
64
+ })
65
+ );
66
+ });
67
+
68
+ it('cancelled booking frees the time slot', () => {
69
+ fc.assert(
70
+ fc.property(bookingArb, (booking) => {
71
+ const created = createBooking(booking);
72
+ cancelBooking(created.id);
73
+ // Same time slot should now be available
74
+ const rebooked = createBooking(booking);
75
+ expect(rebooked.status).toBe('confirmed');
76
+ })
77
+ );
78
+ });
79
+ });
80
+ ```
81
+
82
+ ---
83
+
84
+ ## 2. Auth Invariants
85
+
86
+ Properties that must hold for any authentication/token system.
87
+
88
+ ### Token encode/decode roundtrip
89
+
90
+ ```typescript
91
+ describe('Property: auth token invariants', () => {
92
+ const userPayloadArb = fc.record({
93
+ sub: fc.uuid(),
94
+ email: fc.emailAddress(),
95
+ role: fc.constantFrom('user', 'admin', 'moderator'),
96
+ name: fc.string({ minLength: 1, maxLength: 100 }),
97
+ });
98
+
99
+ it('decode(encode(payload)) === payload', () => {
100
+ fc.assert(
101
+ fc.property(userPayloadArb, (payload) => {
102
+ const token = encodeToken(payload);
103
+ const decoded = decodeToken(token);
104
+ expect(decoded.sub).toBe(payload.sub);
105
+ expect(decoded.email).toBe(payload.email);
106
+ expect(decoded.role).toBe(payload.role);
107
+ })
108
+ );
109
+ });
110
+
111
+ it('expired tokens are always rejected', () => {
112
+ fc.assert(
113
+ fc.property(
114
+ userPayloadArb,
115
+ fc.integer({ min: 1, max: 365 * 24 * 3600 }), // seconds in past
116
+ (payload, secondsAgo) => {
117
+ const expiredAt = Math.floor(Date.now() / 1000) - secondsAgo;
118
+ const token = encodeToken({ ...payload, exp: expiredAt });
119
+ expect(() => verifyToken(token)).toThrow();
120
+ }
121
+ )
122
+ );
123
+ });
124
+
125
+ it('valid tokens are always accepted within TTL', () => {
126
+ fc.assert(
127
+ fc.property(
128
+ userPayloadArb,
129
+ fc.integer({ min: 1, max: 3600 }), // seconds in future
130
+ (payload, secondsFromNow) => {
131
+ const expiresAt = Math.floor(Date.now() / 1000) + secondsFromNow;
132
+ const token = encodeToken({ ...payload, exp: expiresAt });
133
+ const result = verifyToken(token);
134
+ expect(result.sub).toBe(payload.sub);
135
+ }
136
+ )
137
+ );
138
+ });
139
+
140
+ it('different payloads produce different tokens', () => {
141
+ fc.assert(
142
+ fc.property(userPayloadArb, userPayloadArb, (a, b) => {
143
+ fc.pre(a.sub !== b.sub); // precondition: different users
144
+ const tokenA = encodeToken(a);
145
+ const tokenB = encodeToken(b);
146
+ expect(tokenA).not.toBe(tokenB);
147
+ })
148
+ );
149
+ });
150
+ });
151
+ ```
152
+
153
+ ---
154
+
155
+ ## 3. Data Invariants
156
+
157
+ Properties that must hold for any data persistence layer.
158
+
159
+ ### Write-then-read consistency
160
+
161
+ ```typescript
162
+ describe('Property: data persistence invariants', () => {
163
+ const resourceArb = fc.record({
164
+ name: fc.string({ minLength: 1, maxLength: 200 }),
165
+ description: fc.string({ maxLength: 1000 }),
166
+ tags: fc.array(fc.string({ minLength: 1, maxLength: 50 }), { maxLength: 10 }),
167
+ active: fc.boolean(),
168
+ price: fc.float({ min: 0, max: 10000, noNaN: true }),
169
+ });
170
+
171
+ it('write then read returns same data', () => {
172
+ fc.assert(
173
+ fc.property(resourceArb, async (resource) => {
174
+ const created = await createResource(resource);
175
+ const fetched = await getResource(created.id);
176
+ expect(fetched.name).toBe(resource.name);
177
+ expect(fetched.description).toBe(resource.description);
178
+ expect(fetched.tags).toEqual(resource.tags);
179
+ expect(fetched.active).toBe(resource.active);
180
+ })
181
+ );
182
+ });
183
+
184
+ it('delete then read returns nothing', () => {
185
+ fc.assert(
186
+ fc.property(resourceArb, async (resource) => {
187
+ const created = await createResource(resource);
188
+ await deleteResource(created.id);
189
+ const fetched = await getResource(created.id);
190
+ expect(fetched).toBeNull();
191
+ })
192
+ );
193
+ });
194
+
195
+ it('update preserves unmodified fields', () => {
196
+ fc.assert(
197
+ fc.property(
198
+ resourceArb,
199
+ fc.record({ name: fc.string({ minLength: 1, maxLength: 200 }) }),
200
+ async (resource, update) => {
201
+ const created = await createResource(resource);
202
+ await updateResource(created.id, update);
203
+ const fetched = await getResource(created.id);
204
+ // Updated field changed
205
+ expect(fetched.name).toBe(update.name);
206
+ // Non-updated fields preserved
207
+ expect(fetched.description).toBe(resource.description);
208
+ expect(fetched.active).toBe(resource.active);
209
+ }
210
+ )
211
+ );
212
+ });
213
+
214
+ it('listing includes all created resources', () => {
215
+ fc.assert(
216
+ fc.property(
217
+ fc.array(resourceArb, { minLength: 1, maxLength: 10 }),
218
+ async (resources) => {
219
+ const ids = [];
220
+ for (const r of resources) {
221
+ const created = await createResource(r);
222
+ ids.push(created.id);
223
+ }
224
+ const list = await listResources();
225
+ for (const id of ids) {
226
+ expect(list.some(item => item.id === id)).toBe(true);
227
+ }
228
+ }
229
+ )
230
+ );
231
+ });
232
+ });
233
+ ```
234
+
235
+ ### String handling invariants
236
+
237
+ ```typescript
238
+ describe('Property: string handling', () => {
239
+ it('stored strings are never truncated silently', () => {
240
+ fc.assert(
241
+ fc.property(
242
+ fc.string({ minLength: 1, maxLength: 500 }),
243
+ async (input) => {
244
+ const created = await createResource({ name: input });
245
+ const fetched = await getResource(created.id);
246
+ expect(fetched.name.length).toBe(input.length);
247
+ }
248
+ )
249
+ );
250
+ });
251
+
252
+ it('unicode strings roundtrip correctly', () => {
253
+ fc.assert(
254
+ fc.property(
255
+ fc.fullUnicode(), // generates full Unicode range
256
+ async (input) => {
257
+ fc.pre(input.length > 0 && input.length <= 200);
258
+ const created = await createResource({ name: input });
259
+ const fetched = await getResource(created.id);
260
+ expect(fetched.name).toBe(input);
261
+ }
262
+ )
263
+ );
264
+ });
265
+ });
266
+ ```
267
+
268
+ ---
269
+
270
+ ## 4. Pagination Invariants
271
+
272
+ ```typescript
273
+ describe('Property: pagination', () => {
274
+ it('paginating through all pages returns all items', () => {
275
+ fc.assert(
276
+ fc.property(
277
+ fc.integer({ min: 1, max: 100 }), // total items
278
+ fc.integer({ min: 1, max: 20 }), // page size
279
+ async (totalItems, pageSize) => {
280
+ // Seed database with totalItems
281
+ await seedResources(totalItems);
282
+ const allItems = [];
283
+ let page = 1;
284
+ let hasMore = true;
285
+ while (hasMore) {
286
+ const result = await listResources({ page, pageSize });
287
+ allItems.push(...result.data);
288
+ hasMore = result.data.length === pageSize;
289
+ page++;
290
+ }
291
+ expect(allItems.length).toBe(totalItems);
292
+ // No duplicates
293
+ const ids = allItems.map(i => i.id);
294
+ expect(new Set(ids).size).toBe(ids.length);
295
+ }
296
+ )
297
+ );
298
+ });
299
+ });
300
+ ```
301
+
302
+ ---
303
+
304
+ ## Usage Notes
305
+
306
+ When generating property-based tests:
307
+
308
+ 1. **Install fast-check**: `npm install -D fast-check`
309
+ 2. **Start with invariants** -- what must ALWAYS be true, regardless of input?
310
+ 3. **Use `fc.pre()` for preconditions** -- skip inputs that don't apply
311
+ 4. **Keep properties simple** -- one property per test, clearly named
312
+ 5. **Use shrinking** -- fast-check automatically finds minimal failing examples
313
+ 6. **Combine with example tests** -- property tests complement, not replace, example-based tests
314
+ 7. **For async properties**: use `fc.assert(fc.asyncProperty(...))`
315
+ 8. **Set reasonable bounds** -- limit array sizes and string lengths to keep tests fast
316
+ 9. **Suggest property tests for**: any code with invariants, encode/decode pairs, sorting, filtering, pagination, CRUD operations