@pilotspace/add 1.14.0 → 1.15.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 (305) hide show
  1. package/CHANGELOG.md +149 -1
  2. package/THIRD_PARTY_NOTICES.md +41 -0
  3. package/bin/cli.js +37 -11
  4. package/docs/02-the-flow.md +1 -1
  5. package/docs/03-step-1-specify.md +4 -0
  6. package/docs/04-step-2-scenarios.md +4 -0
  7. package/docs/06-step-4-tests.md +4 -0
  8. package/docs/18-personas.md +80 -0
  9. package/docs/add-milestone-task-lifecycle.png +0 -0
  10. package/docs/add-task-growth-wheel.png +0 -0
  11. package/docs/appendix-c-glossary.md +5 -1
  12. package/package.json +3 -1
  13. package/personas-teacher/LICENSE +21 -0
  14. package/personas-teacher/README.md +1067 -0
  15. package/personas-teacher/VENDOR.md +13 -0
  16. package/personas-teacher/academic/academic-anthropologist.md +125 -0
  17. package/personas-teacher/academic/academic-geographer.md +127 -0
  18. package/personas-teacher/academic/academic-historian.md +123 -0
  19. package/personas-teacher/academic/academic-narratologist.md +118 -0
  20. package/personas-teacher/academic/academic-psychologist.md +118 -0
  21. package/personas-teacher/design/design-brand-guardian.md +322 -0
  22. package/personas-teacher/design/design-image-prompt-engineer.md +236 -0
  23. package/personas-teacher/design/design-inclusive-visuals-specialist.md +71 -0
  24. package/personas-teacher/design/design-persona-walkthrough.md +272 -0
  25. package/personas-teacher/design/design-ui-designer.md +383 -0
  26. package/personas-teacher/design/design-ux-architect.md +469 -0
  27. package/personas-teacher/design/design-ux-researcher.md +329 -0
  28. package/personas-teacher/design/design-visual-storyteller.md +149 -0
  29. package/personas-teacher/design/design-whimsy-injector.md +438 -0
  30. package/personas-teacher/divisions.json +21 -0
  31. package/personas-teacher/engineering/engineering-ai-data-remediation-engineer.md +211 -0
  32. package/personas-teacher/engineering/engineering-ai-engineer.md +146 -0
  33. package/personas-teacher/engineering/engineering-autonomous-optimization-architect.md +107 -0
  34. package/personas-teacher/engineering/engineering-backend-architect.md +236 -0
  35. package/personas-teacher/engineering/engineering-cms-developer.md +536 -0
  36. package/personas-teacher/engineering/engineering-code-reviewer.md +76 -0
  37. package/personas-teacher/engineering/engineering-codebase-onboarding-engineer.md +173 -0
  38. package/personas-teacher/engineering/engineering-data-engineer.md +306 -0
  39. package/personas-teacher/engineering/engineering-database-optimizer.md +176 -0
  40. package/personas-teacher/engineering/engineering-devops-automator.md +376 -0
  41. package/personas-teacher/engineering/engineering-drupal-shopping-cart.md +360 -0
  42. package/personas-teacher/engineering/engineering-email-intelligence-engineer.md +353 -0
  43. package/personas-teacher/engineering/engineering-embedded-firmware-engineer.md +173 -0
  44. package/personas-teacher/engineering/engineering-feishu-integration-developer.md +598 -0
  45. package/personas-teacher/engineering/engineering-filament-optimization-specialist.md +283 -0
  46. package/personas-teacher/engineering/engineering-frontend-developer.md +225 -0
  47. package/personas-teacher/engineering/engineering-git-workflow-master.md +84 -0
  48. package/personas-teacher/engineering/engineering-incident-response-commander.md +444 -0
  49. package/personas-teacher/engineering/engineering-it-service-manager.md +561 -0
  50. package/personas-teacher/engineering/engineering-minimal-change-engineer.md +207 -0
  51. package/personas-teacher/engineering/engineering-mobile-app-builder.md +493 -0
  52. package/personas-teacher/engineering/engineering-multi-agent-systems-architect.md +600 -0
  53. package/personas-teacher/engineering/engineering-orgscript-engineer.md +113 -0
  54. package/personas-teacher/engineering/engineering-prompt-engineer.md +202 -0
  55. package/personas-teacher/engineering/engineering-rapid-prototyper.md +462 -0
  56. package/personas-teacher/engineering/engineering-senior-developer.md +176 -0
  57. package/personas-teacher/engineering/engineering-software-architect.md +112 -0
  58. package/personas-teacher/engineering/engineering-solidity-smart-contract-engineer.md +522 -0
  59. package/personas-teacher/engineering/engineering-sre.md +90 -0
  60. package/personas-teacher/engineering/engineering-technical-writer.md +393 -0
  61. package/personas-teacher/engineering/engineering-voice-ai-integration-engineer.md +561 -0
  62. package/personas-teacher/engineering/engineering-wechat-mini-program-developer.md +350 -0
  63. package/personas-teacher/engineering/engineering-wordpress-shopping-cart.md +346 -0
  64. package/personas-teacher/examples/README.md +48 -0
  65. package/personas-teacher/examples/nexus-spatial-discovery.md +852 -0
  66. package/personas-teacher/examples/workflow-book-chapter.md +55 -0
  67. package/personas-teacher/examples/workflow-landing-page.md +119 -0
  68. package/personas-teacher/examples/workflow-startup-mvp.md +155 -0
  69. package/personas-teacher/examples/workflow-with-memory.md +238 -0
  70. package/personas-teacher/finance/finance-bookkeeper-controller.md +260 -0
  71. package/personas-teacher/finance/finance-financial-analyst.md +234 -0
  72. package/personas-teacher/finance/finance-fpa-analyst.md +263 -0
  73. package/personas-teacher/finance/finance-investment-researcher.md +272 -0
  74. package/personas-teacher/finance/finance-tax-strategist.md +239 -0
  75. package/personas-teacher/game-development/blender/blender-addon-engineer.md +234 -0
  76. package/personas-teacher/game-development/game-audio-engineer.md +264 -0
  77. package/personas-teacher/game-development/game-designer.md +167 -0
  78. package/personas-teacher/game-development/godot/godot-gameplay-scripter.md +334 -0
  79. package/personas-teacher/game-development/godot/godot-multiplayer-engineer.md +297 -0
  80. package/personas-teacher/game-development/godot/godot-shader-developer.md +266 -0
  81. package/personas-teacher/game-development/level-designer.md +208 -0
  82. package/personas-teacher/game-development/narrative-designer.md +243 -0
  83. package/personas-teacher/game-development/roblox-studio/roblox-avatar-creator.md +297 -0
  84. package/personas-teacher/game-development/roblox-studio/roblox-experience-designer.md +305 -0
  85. package/personas-teacher/game-development/roblox-studio/roblox-systems-scripter.md +325 -0
  86. package/personas-teacher/game-development/technical-artist.md +229 -0
  87. package/personas-teacher/game-development/unity/unity-architect.md +271 -0
  88. package/personas-teacher/game-development/unity/unity-editor-tool-developer.md +310 -0
  89. package/personas-teacher/game-development/unity/unity-multiplayer-engineer.md +321 -0
  90. package/personas-teacher/game-development/unity/unity-shader-graph-artist.md +269 -0
  91. package/personas-teacher/game-development/unreal-engine/unreal-multiplayer-architect.md +313 -0
  92. package/personas-teacher/game-development/unreal-engine/unreal-systems-engineer.md +310 -0
  93. package/personas-teacher/game-development/unreal-engine/unreal-technical-artist.md +256 -0
  94. package/personas-teacher/game-development/unreal-engine/unreal-world-builder.md +273 -0
  95. package/personas-teacher/gis/gis-3d-scene-developer.md +111 -0
  96. package/personas-teacher/gis/gis-analyst.md +91 -0
  97. package/personas-teacher/gis/gis-bim-specialist.md +108 -0
  98. package/personas-teacher/gis/gis-cartography-designer.md +150 -0
  99. package/personas-teacher/gis/gis-drone-reality-mapping.md +120 -0
  100. package/personas-teacher/gis/gis-geoai-ml-engineer.md +105 -0
  101. package/personas-teacher/gis/gis-geoprocessing-specialist.md +97 -0
  102. package/personas-teacher/gis/gis-qa-engineer.md +133 -0
  103. package/personas-teacher/gis/gis-solution-engineer.md +101 -0
  104. package/personas-teacher/gis/gis-spatial-data-engineer.md +97 -0
  105. package/personas-teacher/gis/gis-spatial-data-scientist.md +111 -0
  106. package/personas-teacher/gis/gis-technical-consultant.md +86 -0
  107. package/personas-teacher/gis/gis-web-gis-developer.md +108 -0
  108. package/personas-teacher/marketing/marketing-aeo-foundations.md +264 -0
  109. package/personas-teacher/marketing/marketing-agentic-search-optimizer.md +311 -0
  110. package/personas-teacher/marketing/marketing-ai-citation-strategist.md +170 -0
  111. package/personas-teacher/marketing/marketing-app-store-optimizer.md +321 -0
  112. package/personas-teacher/marketing/marketing-baidu-seo-specialist.md +226 -0
  113. package/personas-teacher/marketing/marketing-bilibili-content-strategist.md +199 -0
  114. package/personas-teacher/marketing/marketing-book-co-author.md +110 -0
  115. package/personas-teacher/marketing/marketing-carousel-growth-engine.md +199 -0
  116. package/personas-teacher/marketing/marketing-china-ecommerce-operator.md +283 -0
  117. package/personas-teacher/marketing/marketing-china-market-localization-strategist.md +283 -0
  118. package/personas-teacher/marketing/marketing-content-creator.md +54 -0
  119. package/personas-teacher/marketing/marketing-cross-border-ecommerce.md +259 -0
  120. package/personas-teacher/marketing/marketing-douyin-strategist.md +149 -0
  121. package/personas-teacher/marketing/marketing-email-strategist.md +249 -0
  122. package/personas-teacher/marketing/marketing-global-podcast-strategist.md +206 -0
  123. package/personas-teacher/marketing/marketing-growth-hacker.md +54 -0
  124. package/personas-teacher/marketing/marketing-instagram-curator.md +113 -0
  125. package/personas-teacher/marketing/marketing-kuaishou-strategist.md +223 -0
  126. package/personas-teacher/marketing/marketing-linkedin-content-creator.md +214 -0
  127. package/personas-teacher/marketing/marketing-livestream-commerce-coach.md +305 -0
  128. package/personas-teacher/marketing/marketing-multi-platform-publisher.md +217 -0
  129. package/personas-teacher/marketing/marketing-podcast-strategist.md +277 -0
  130. package/personas-teacher/marketing/marketing-pr-communications-manager.md +473 -0
  131. package/personas-teacher/marketing/marketing-private-domain-operator.md +308 -0
  132. package/personas-teacher/marketing/marketing-reddit-community-builder.md +123 -0
  133. package/personas-teacher/marketing/marketing-seo-specialist.md +321 -0
  134. package/personas-teacher/marketing/marketing-short-video-editing-coach.md +412 -0
  135. package/personas-teacher/marketing/marketing-social-media-strategist.md +125 -0
  136. package/personas-teacher/marketing/marketing-tiktok-strategist.md +125 -0
  137. package/personas-teacher/marketing/marketing-twitter-engager.md +126 -0
  138. package/personas-teacher/marketing/marketing-video-optimization-specialist.md +119 -0
  139. package/personas-teacher/marketing/marketing-wechat-official-account.md +145 -0
  140. package/personas-teacher/marketing/marketing-weibo-strategist.md +240 -0
  141. package/personas-teacher/marketing/marketing-x-twitter-intelligence-analyst.md +161 -0
  142. package/personas-teacher/marketing/marketing-xiaohongshu-specialist.md +138 -0
  143. package/personas-teacher/marketing/marketing-zhihu-strategist.md +162 -0
  144. package/personas-teacher/paid-media/paid-media-auditor.md +71 -0
  145. package/personas-teacher/paid-media/paid-media-creative-strategist.md +71 -0
  146. package/personas-teacher/paid-media/paid-media-paid-social-strategist.md +71 -0
  147. package/personas-teacher/paid-media/paid-media-ppc-strategist.md +71 -0
  148. package/personas-teacher/paid-media/paid-media-programmatic-buyer.md +71 -0
  149. package/personas-teacher/paid-media/paid-media-search-query-analyst.md +71 -0
  150. package/personas-teacher/paid-media/paid-media-tracking-specialist.md +71 -0
  151. package/personas-teacher/product/product-behavioral-nudge-engine.md +80 -0
  152. package/personas-teacher/product/product-feedback-synthesizer.md +119 -0
  153. package/personas-teacher/product/product-manager.md +469 -0
  154. package/personas-teacher/product/product-sprint-prioritizer.md +154 -0
  155. package/personas-teacher/product/product-trend-researcher.md +159 -0
  156. package/personas-teacher/project-management/project-management-experiment-tracker.md +198 -0
  157. package/personas-teacher/project-management/project-management-jira-workflow-steward.md +230 -0
  158. package/personas-teacher/project-management/project-management-meeting-notes-specialist.md +95 -0
  159. package/personas-teacher/project-management/project-management-project-shepherd.md +194 -0
  160. package/personas-teacher/project-management/project-management-studio-operations.md +200 -0
  161. package/personas-teacher/project-management/project-management-studio-producer.md +203 -0
  162. package/personas-teacher/project-management/project-manager-senior.md +135 -0
  163. package/personas-teacher/sales/sales-account-strategist.md +227 -0
  164. package/personas-teacher/sales/sales-coach.md +271 -0
  165. package/personas-teacher/sales/sales-deal-strategist.md +180 -0
  166. package/personas-teacher/sales/sales-discovery-coach.md +225 -0
  167. package/personas-teacher/sales/sales-engineer.md +182 -0
  168. package/personas-teacher/sales/sales-offer-lead-gen-strategist.md +257 -0
  169. package/personas-teacher/sales/sales-outbound-strategist.md +201 -0
  170. package/personas-teacher/sales/sales-pipeline-analyst.md +267 -0
  171. package/personas-teacher/sales/sales-proposal-strategist.md +217 -0
  172. package/personas-teacher/security/security-appsec-engineer.md +491 -0
  173. package/personas-teacher/security/security-architect.md +304 -0
  174. package/personas-teacher/security/security-blockchain-security-auditor.md +463 -0
  175. package/personas-teacher/security/security-cloud-security-architect.md +523 -0
  176. package/personas-teacher/security/security-compliance-auditor.md +158 -0
  177. package/personas-teacher/security/security-incident-responder.md +437 -0
  178. package/personas-teacher/security/security-penetration-tester.md +399 -0
  179. package/personas-teacher/security/security-senior-secops.md +750 -0
  180. package/personas-teacher/security/security-threat-detection-engineer.md +534 -0
  181. package/personas-teacher/security/security-threat-intelligence-analyst.md +644 -0
  182. package/personas-teacher/spatial-computing/macos-spatial-metal-engineer.md +337 -0
  183. package/personas-teacher/spatial-computing/terminal-integration-specialist.md +70 -0
  184. package/personas-teacher/spatial-computing/visionos-spatial-engineer.md +54 -0
  185. package/personas-teacher/spatial-computing/xr-cockpit-interaction-specialist.md +32 -0
  186. package/personas-teacher/spatial-computing/xr-immersive-developer.md +32 -0
  187. package/personas-teacher/spatial-computing/xr-interface-architect.md +32 -0
  188. package/personas-teacher/specialized/accounts-payable-agent.md +185 -0
  189. package/personas-teacher/specialized/agentic-identity-trust.md +387 -0
  190. package/personas-teacher/specialized/agents-orchestrator.md +367 -0
  191. package/personas-teacher/specialized/automation-governance-architect.md +216 -0
  192. package/personas-teacher/specialized/business-strategist.md +488 -0
  193. package/personas-teacher/specialized/change-management-consultant.md +497 -0
  194. package/personas-teacher/specialized/chief-financial-officer.md +388 -0
  195. package/personas-teacher/specialized/corporate-training-designer.md +192 -0
  196. package/personas-teacher/specialized/customer-service.md +398 -0
  197. package/personas-teacher/specialized/customer-success-manager.md +460 -0
  198. package/personas-teacher/specialized/data-consolidation-agent.md +60 -0
  199. package/personas-teacher/specialized/data-privacy-officer.md +412 -0
  200. package/personas-teacher/specialized/esg-sustainability-officer.md +396 -0
  201. package/personas-teacher/specialized/government-digital-presales-consultant.md +363 -0
  202. package/personas-teacher/specialized/grant-writer.md +511 -0
  203. package/personas-teacher/specialized/healthcare-customer-service.md +389 -0
  204. package/personas-teacher/specialized/healthcare-marketing-compliance.md +395 -0
  205. package/personas-teacher/specialized/hospitality-guest-services.md +603 -0
  206. package/personas-teacher/specialized/hr-onboarding.md +451 -0
  207. package/personas-teacher/specialized/identity-graph-operator.md +260 -0
  208. package/personas-teacher/specialized/language-translator.md +264 -0
  209. package/personas-teacher/specialized/legal-billing-time-tracking.md +569 -0
  210. package/personas-teacher/specialized/legal-client-intake.md +492 -0
  211. package/personas-teacher/specialized/legal-document-review.md +454 -0
  212. package/personas-teacher/specialized/loan-officer-assistant.md +555 -0
  213. package/personas-teacher/specialized/lsp-index-engineer.md +314 -0
  214. package/personas-teacher/specialized/ma-integration-manager.md +427 -0
  215. package/personas-teacher/specialized/medical-billing-coding-specialist.md +491 -0
  216. package/personas-teacher/specialized/operations-manager.md +399 -0
  217. package/personas-teacher/specialized/organizational-psychologist.md +391 -0
  218. package/personas-teacher/specialized/personal-growth-mentor.md +159 -0
  219. package/personas-teacher/specialized/real-estate-buyer-seller.md +596 -0
  220. package/personas-teacher/specialized/recruitment-specialist.md +509 -0
  221. package/personas-teacher/specialized/report-distribution-agent.md +65 -0
  222. package/personas-teacher/specialized/retail-customer-returns.md +566 -0
  223. package/personas-teacher/specialized/sales-data-extraction-agent.md +67 -0
  224. package/personas-teacher/specialized/sales-outreach.md +425 -0
  225. package/personas-teacher/specialized/specialized-chief-of-staff.md +279 -0
  226. package/personas-teacher/specialized/specialized-civil-engineer.md +356 -0
  227. package/personas-teacher/specialized/specialized-cultural-intelligence-strategist.md +88 -0
  228. package/personas-teacher/specialized/specialized-developer-advocate.md +317 -0
  229. package/personas-teacher/specialized/specialized-document-generator.md +55 -0
  230. package/personas-teacher/specialized/specialized-french-consulting-market.md +192 -0
  231. package/personas-teacher/specialized/specialized-korean-business-navigator.md +216 -0
  232. package/personas-teacher/specialized/specialized-mcp-builder.md +248 -0
  233. package/personas-teacher/specialized/specialized-model-qa.md +488 -0
  234. package/personas-teacher/specialized/specialized-pricing-analyst.md +243 -0
  235. package/personas-teacher/specialized/specialized-salesforce-architect.md +180 -0
  236. package/personas-teacher/specialized/specialized-strategy-duel-agent.md +130 -0
  237. package/personas-teacher/specialized/specialized-workflow-architect.md +597 -0
  238. package/personas-teacher/specialized/study-abroad-advisor.md +282 -0
  239. package/personas-teacher/specialized/supply-chain-strategist.md +582 -0
  240. package/personas-teacher/specialized/zk-steward.md +211 -0
  241. package/personas-teacher/strategy/EXECUTIVE-BRIEF.md +95 -0
  242. package/personas-teacher/strategy/QUICKSTART.md +194 -0
  243. package/personas-teacher/strategy/coordination/agent-activation-prompts.md +401 -0
  244. package/personas-teacher/strategy/coordination/handoff-templates.md +357 -0
  245. package/personas-teacher/strategy/nexus-strategy.md +1110 -0
  246. package/personas-teacher/strategy/playbooks/phase-0-discovery.md +178 -0
  247. package/personas-teacher/strategy/playbooks/phase-1-strategy.md +238 -0
  248. package/personas-teacher/strategy/playbooks/phase-2-foundation.md +278 -0
  249. package/personas-teacher/strategy/playbooks/phase-3-build.md +286 -0
  250. package/personas-teacher/strategy/playbooks/phase-4-hardening.md +332 -0
  251. package/personas-teacher/strategy/playbooks/phase-5-launch.md +277 -0
  252. package/personas-teacher/strategy/playbooks/phase-6-operate.md +318 -0
  253. package/personas-teacher/strategy/runbooks/scenario-enterprise-feature.md +157 -0
  254. package/personas-teacher/strategy/runbooks/scenario-incident-response.md +217 -0
  255. package/personas-teacher/strategy/runbooks/scenario-marketing-campaign.md +187 -0
  256. package/personas-teacher/strategy/runbooks/scenario-startup-mvp.md +154 -0
  257. package/personas-teacher/support/support-analytics-reporter.md +365 -0
  258. package/personas-teacher/support/support-executive-summary-generator.md +212 -0
  259. package/personas-teacher/support/support-finance-tracker.md +442 -0
  260. package/personas-teacher/support/support-infrastructure-maintainer.md +618 -0
  261. package/personas-teacher/support/support-legal-compliance-checker.md +588 -0
  262. package/personas-teacher/support/support-support-responder.md +585 -0
  263. package/personas-teacher/testing/testing-accessibility-auditor.md +316 -0
  264. package/personas-teacher/testing/testing-api-tester.md +306 -0
  265. package/personas-teacher/testing/testing-evidence-collector.md +210 -0
  266. package/personas-teacher/testing/testing-performance-benchmarker.md +268 -0
  267. package/personas-teacher/testing/testing-reality-checker.md +236 -0
  268. package/personas-teacher/testing/testing-test-results-analyzer.md +305 -0
  269. package/personas-teacher/testing/testing-tool-evaluator.md +394 -0
  270. package/personas-teacher/testing/testing-workflow-optimizer.md +450 -0
  271. package/personas-teacher/tools.json +19 -0
  272. package/skill/add/SKILL.md +17 -12
  273. package/skill/add/advisor.md +31 -34
  274. package/skill/add/confidence.md +1 -1
  275. package/skill/add/deltas.md +12 -13
  276. package/skill/add/design.md +52 -49
  277. package/skill/add/fold.md +18 -16
  278. package/skill/add/intake.md +4 -3
  279. package/skill/add/loop.md +1 -2
  280. package/skill/add/phases/0-ground.md +32 -34
  281. package/skill/add/phases/0-setup.md +22 -21
  282. package/skill/add/phases/1-specify.md +8 -6
  283. package/skill/add/phases/3-contract.md +1 -1
  284. package/skill/add/phases/4-tests.md +9 -10
  285. package/skill/add/phases/5-build.md +17 -13
  286. package/skill/add/phases/6-verify.md +7 -4
  287. package/skill/add/phases/7-observe.md +2 -1
  288. package/skill/add/report-template.md +61 -18
  289. package/skill/add/run.md +4 -3
  290. package/skill/add/scope.md +2 -2
  291. package/skill/add/sensitivity.md +51 -0
  292. package/skill/add/streams.md +24 -23
  293. package/tooling/add.py +822 -26
  294. package/tooling/add_engine/autonomy.py +36 -1
  295. package/tooling/add_engine/constants.py +56 -3
  296. package/tooling/add_engine/guidelines.py +22 -20
  297. package/tooling/add_engine/predicates.py +56 -3
  298. package/tooling/add_engine/search.py +165 -0
  299. package/tooling/templates/GLOSSARY.md.tmpl +16 -0
  300. package/tooling/templates/MILESTONE.md.tmpl +9 -0
  301. package/tooling/templates/PROMPT.persona.md.tmpl +44 -0
  302. package/tooling/templates/TASK.fast.md.tmpl +1 -0
  303. package/tooling/templates/TASK.md.tmpl +36 -6
  304. package/tooling/templates/gitignore.tmpl +12 -0
  305. package/tooling/templates/personas/_template.md.tmpl +60 -0
@@ -11,10 +11,11 @@ from __future__ import annotations
11
11
  import re
12
12
  from pathlib import Path
13
13
 
14
- from add_engine.constants import _AUTONOMY_LEVELS
14
+ from add_engine.constants import _AUTONOMY_LEVELS, _STREAMS_POSTURES
15
15
  from add_engine.taskdoc import _task_header
16
16
 
17
17
  _AUTONOMY_LINE_RE = re.compile(r"(?:^|·)[ \t]*autonomy:[ \t]*([^\s<#|]+)", re.MULTILINE)
18
+ _STREAMS_LINE_RE = re.compile(r"(?:^|·)[ \t]*streams:[ \t]*([^\s<#|]+)", re.MULTILINE)
18
19
 
19
20
 
20
21
  def _autonomy_level(hdr: str):
@@ -56,3 +57,37 @@ def _project_autonomy(root: Path) -> str:
56
57
  Read-only and PURE — mirrors _project_goal; the seed source for cmd_new_task."""
57
58
  tok = _project_autonomy_token(root)
58
59
  return "auto" if tok is None else ("conservative" if tok == "?" else tok)
60
+
61
+
62
+ # --- streams posture: the parallel-vs-sequential half of the run mode (persist-run-mode) ---
63
+ # A project-scoped declaration in PROJECT.md beside autonomy. Mirrors the autonomy resolvers:
64
+ # anchored read (a title/prose substring is never a declaration), fail-safe defaults.
65
+
66
+ def _streams_posture(text: str):
67
+ """The declared streams posture from a foundation region (HTML comments already stripped
68
+ by the caller). A member of _STREAMS_POSTURES, None when no `streams:` line is present, or
69
+ "?" when a REAL token outside the set was written (unknown). PURE."""
70
+ m = _STREAMS_LINE_RE.search(text)
71
+ if not m:
72
+ return None
73
+ tok = m.group(1).strip().lower()
74
+ return tok if tok in _STREAMS_POSTURES else "?"
75
+
76
+ def _project_streams_token(root: Path):
77
+ """The RAW streams declaration in PROJECT.md — a recognized posture, None when no
78
+ declaration line is present, or "?" for a real-but-unrecognized token. Uses the anchored
79
+ _streams_posture (a title/prose substring is never a declaration) with HTML comments
80
+ stripped. Unreadable foundation -> None. Read-only and PURE."""
81
+ try:
82
+ text = (root / "PROJECT.md").read_text(encoding="utf-8")
83
+ except OSError:
84
+ return None
85
+ return _streams_posture(re.sub(r"<!--.*?-->", "", text, flags=re.S))
86
+
87
+ def _project_streams(root: Path) -> str:
88
+ """The streams posture the project runs under. Fail-SAFE: no declaration -> "parallel"
89
+ (the documented project default: parallel+auto); an unrecognized token -> "sequential"
90
+ (the conservative fallback, NEVER silently parallel); an unreadable foundation -> "parallel".
91
+ Read-only and PURE — mirrors _project_autonomy."""
92
+ tok = _project_streams_token(root)
93
+ return "parallel" if tok is None else ("sequential" if tok == "?" else tok)
@@ -22,6 +22,8 @@ __all__ = [
22
22
  "PHASE_GUIDE",
23
23
  "PHASE_OWNER",
24
24
  "SETUP_FILES",
25
+ "PERSONA_FRONTMATTER_KEYS",
26
+ "PERSONA_REQUIRED_SECTIONS",
25
27
  "GUIDELINE_FILES",
26
28
  "RULES_FILE_REL",
27
29
  "WORKFLOW_HEADINGS",
@@ -88,7 +90,14 @@ PHASE_OWNER = {
88
90
  "specify": "human", "scenarios": "human", "contract": "seam",
89
91
  "tests": "ai", "build": "ai", "verify": "human", "observe": "ai", "done": "human",
90
92
  }
91
- SETUP_FILES = ("PROJECT.md", "CONVENTIONS.md", "GLOSSARY.md", "MODEL_REGISTRY.md", "dependencies.allowlist", "DESIGN.md", "SOUL.md")
93
+ SETUP_FILES = ("PROJECT.md", "CONVENTIONS.md", "GLOSSARY.md", "MODEL_REGISTRY.md", "dependencies.allowlist", "DESIGN.md", "SOUL.md", "personas/_template.md")
94
+
95
+ # persona-setup: a PERSONA living doc (`.add/personas/<slug>.md`) is a frozen-schema file
96
+ # distilled from the vendored teacher library to its critical-rules + default-requirement +
97
+ # measurable success-metrics. The schema is presence-based (these keys/sections must exist);
98
+ # content quality is the AI's authoring concern, not the engine gate. NO-EXEC: validation is pure.
99
+ PERSONA_FRONTMATTER_KEYS = ("name", "vibe")
100
+ PERSONA_REQUIRED_SECTIONS = ("## Identity", "## Critical Rules", "## Default Requirement", "## Success Metrics")
92
101
 
93
102
  # Scaffolded into .add/.gitignore at init so the engine's transient LOCAL artifacts
94
103
  # never reach git. Bare-filename patterns match at any depth under .add/ (tasks/,
@@ -109,12 +118,24 @@ scope-snapshot.json
109
118
  pre-archive-state.bak.json
110
119
  pre-update-state.bak.json
111
120
  .update-cache.json
121
+
122
+ # ADD-managed vendor trees: regenerable/vendored copies the installer drops in,
123
+ # never project-authored — mirrors the .add/docs/ rationale above, generalized
124
+ # to every consumer project (not just this repo). Patterns are BARE, not repo-
125
+ # root style: this file lives INSIDE .add/, so git resolves its patterns
126
+ # relative to .add/ itself — a ".add/"-prefixed pattern here would look for
127
+ # the non-existent .add/.add/tooling/ and never match anything. (one further
128
+ # managed tree is NOT listed here — the engine's own _GITIGNORE_BODY constant
129
+ # must stay hands-off of it by name; the installer twins seed that pattern
130
+ # themselves, also bare.)
131
+ tooling/
132
+ docs/
112
133
  """
113
134
 
114
135
  # Guideline-injection targets + version-stable markers. NEVER change these marker
115
136
  # strings: a re-run finds the old block by exact match, so changing them would
116
137
  # orphan every block written by a prior version (see TASK guideline-inject).
117
- GUIDELINE_FILES = ("AGENTS.md", "CLAUDE.md")
138
+ GUIDELINE_FILES = ("AGENTS.md", "CLAUDE.md", ".clinerules")
118
139
  _GUIDE_BEGIN = "<!-- ADD:BEGIN — managed by `add.py sync-guidelines`; do not edit inside -->"
119
140
  _GUIDE_END = "<!-- ADD:END -->"
120
141
 
@@ -208,14 +229,46 @@ _DEFAULT_WIDTH = 72 # fixed width for the persisted/canonical render (RETR
208
229
 
209
230
 
210
231
  # --- shared delta-parsing regexes (used by taskdoc readers AND the deltas-web lint) ---
232
+ # Groups stay (1) competency, (2) status, (3) text — every caller relies on that. A competency
233
+ # lesson MAY carry an OPTIONAL persona target + section hint between status and `]`
234
+ # (persona-self-improve): `[<comp> · <status> · persona:<slug> · <critical-rule|success-metric>] …`.
235
+ # That clause is NON-capturing here (group numbering unchanged); _PERSONA_TAG_RE below pulls the
236
+ # slug + hint out when a route needs them — permissively, so an unroutable hint still PARSES (and is
237
+ # rejected by code) rather than silently failing to match.
211
238
  _DELTA_RE = re.compile(
212
- r"\s*-\s*\[\s*(DDD|SDD|UDD|TDD|ADD)\s*·\s*(open|folded|rejected)\s*\]\s*(.+)$"
239
+ r"\s*-\s*\[\s*(DDD|SDD|UDD|TDD|ADD)\s*·\s*(open|folded|rejected)"
240
+ r"(?:\s*·\s*persona:[^\s·\]]+\s*·\s*[^·\]]+?)?\s*\]\s*(.+)$"
213
241
  )
242
+ # Pull the OPTIONAL persona target + section hint out of a delta tag line (persona-self-improve).
243
+ _PERSONA_TAG_RE = re.compile(r"persona:([^\s·\]]+)\s*·\s*([^·\]]+?)\s*\]")
214
244
  _EVIDENCE_RE = re.compile(r"^(.*?)\s*\(evidence:\s*(.*?)\)\s*$")
215
245
  _SPEC_DELTA_RE = re.compile(
216
246
  r"\s*-\s*\[\s*(SPEC)\s*·\s*(open|seeded|dropped|carried)\s*\]\s*(.+)$"
217
247
  )
218
248
 
249
+ # delta-task-backlink: reads the `[→ <slug>]` seed stamp `_resolve_spec_delta` appends, so the
250
+ # delta→task lineage can be walked back (check WARNs when a seeded pointer no longer resolves).
251
+ _SEED_POINTER_RE = re.compile(r"\[→\s*([A-Za-z0-9_-]+)\s*\]")
252
+
253
+ # rule-id-coverage: §1 Must-ID / Reject-code lines, plus the §2 scenario tag and §4 `covers:`
254
+ # back-reference a task uses to claim coverage of a rule. A Reject's ID is its literal error_code
255
+ # string (from `-> "<error_code>"`), never a positional R1/R2 sequence number.
256
+ _MUST_ID_RE = re.compile(r"^\s*-\s*(M\d+)\s*:", re.MULTILINE)
257
+ _REJECT_CODE_RE = re.compile(r'^\s*-\s.*->\s*"([^"]+)"\s*$', re.MULTILINE)
258
+ _SCENARIO_TAG_RE = re.compile(r"^\s*Scenario:.*#\s*(.+?)\s*$", re.MULTILINE)
259
+ _COVERS_LINE_RE = re.compile(r"covers:\s*(.+?)\s*$", re.MULTILINE)
260
+ _TAG_TOKEN_RE = re.compile(r"(M\d+|R:[A-Za-z0-9_]+)")
261
+
219
262
 
220
263
  # --- autonomy levels (shared: autonomy resolvers + _AUTONOMY_ORDER/cmd_autonomy) ---
221
264
  _AUTONOMY_LEVELS = ("manual", "conservative", "auto")
265
+
266
+ # --- streams posture (shared: streams resolvers + cmd_streams) — the parallel-vs-sequential
267
+ # half of the run mode (persist-run-mode); project-scoped, persisted in PROJECT.md beside autonomy ---
268
+ _STREAMS_POSTURES = ("parallel", "sequential")
269
+
270
+ # --- sensitivity taxonomy (shared: _task_sensitivity reader + cmd_freeze/status/audit) — the
271
+ # risk-CLASS the human declares in the TASK header at freeze (risk-sensitivity-taxonomy). The
272
+ # engine validates + surfaces a HUMAN-declared token; it NEVER classifies. A closed enum, sibling
273
+ # of _AUTONOMY_LEVELS/_STREAMS_POSTURES. Consumed downstream by advisor-gate-relax (mechanical). ---
274
+ _SENSITIVITY_VALUES = ("security", "data", "architecture", "mechanical")
@@ -1,10 +1,11 @@
1
1
  """add_engine.guidelines — the guidelines / CLAUDE.md-injection subsystem (engine-modularization 8/N).
2
2
 
3
- Inject one stable, marker-delimited ADD block into the project root's AGENTS.md and
4
- CLAUDE.md. DYNAMIC-BY-REFERENCE: the block tells the agent to run `add.py status` and
5
- read PROJECT.md — it never embeds live state. A closed, self-contained cluster (8 fns +
6
- the cluster-private _INIT_EXCLUDE); transitive-closure AST scan: ZERO outbound calls to
7
- non-cluster add fns. Deps: constants + _atomic_write (io_state) + stdlib. None patched.
3
+ Inject one stable, marker-delimited ADD block into every GUIDELINE_FILES target under the
4
+ project root (AGENTS.md, CLAUDE.md, .clinerules). DYNAMIC-BY-REFERENCE: the block tells the
5
+ agent to run `add.py status` and read PROJECT.md — it never embeds live state. A closed,
6
+ self-contained cluster (8 fns + the cluster-private _INIT_EXCLUDE); transitive-closure AST
7
+ scan: ZERO outbound calls to non-cluster add fns. Deps: constants + _atomic_write (io_state)
8
+ + stdlib. None patched.
8
9
  """
9
10
  from __future__ import annotations
10
11
 
@@ -34,24 +35,25 @@ def _guideline_block() -> str:
34
35
  "the human owns direction and verification. The loop below works for any agent —\n"
35
36
  "Claude, Cursor, Copilot, Codex — through the CLI alone. Before you change code:\n"
36
37
  "\n"
37
- "1. Run `python3 .add/tooling/add.py status` — where the project is and what's\n"
38
- " next (the resume point; read it first every session).\n"
39
- "2. Read `.add/PROJECT.md` — the foundation (domain · spec · UI/UX) every task\n"
40
- " builds on.\n"
41
- "3. Run `python3 .add/tooling/add.py guide` — it names the phase and the exact\n"
42
- " phase-guide file to read (the `guide :` line). Work ONLY that phase — each\n"
43
- " guide ends with its exit gate and the command to move on.\n"
38
+ "1. `python3 .add/tooling/add.py status` — the resume point; read it first each session.\n"
39
+ "2. `.add/PROJECT.md` — the foundation (domain · spec · UI/UX) every task builds on.\n"
40
+ "3. `python3 .add/tooling/add.py guide` — it names the phase + its guide file; work ONLY that phase.\n"
44
41
  "\n"
45
- "The flow: INTAKE sizes a request into a milestone; each task runs the\n"
46
- "**specification bundle** — Spec+Scenarios+Contract+Tests as one bundle,\n"
47
- "ONE human approval at the frozen contract — then a self-driving build→verify\n"
48
- "run. Non-negotiable for every agent:\n"
42
+ "The flow: INTAKE sizes a request into a milestone; each task runs the **specification bundle**\n"
43
+ "(Spec+Scenarios+Contract+Tests) ONE human approval at the frozen contract — then a\n"
44
+ "self-driving build→verify run. Non-negotiable for every agent:\n"
49
45
  "Never weaken a test or edit a frozen contract to make a build pass; a security\n"
50
46
  "finding is always HARD-STOP — never auto-passed.\n"
51
47
  "\n"
52
- "On Claude Code the `add` skill drives this loop automatically; other agents\n"
53
- "follow the three steps. The book is in `.add/docs/`. This block is generated\n"
54
- "by `add.py sync-guidelines`; edit outside the markers, not inside.\n"
48
+ "Roster (from `add-method/agents/*.md`; adopt per phase point at each phase guide, the floor above binds all):\n"
49
+ "- design · setup contract · top\n"
50
+ "- build · tests build · mid\n"
51
+ "- verify · verify → observe · top\n"
52
+ "- persona · any phase, cross-cutting service · mid\n"
53
+ "- advisor · any phase, cross-cutting service · top\n"
54
+ "\n"
55
+ "On Claude Code the `add` skill drives this loop; other agents follow the three steps. The\n"
56
+ "book is in `.add/docs/`. This block is generated by `add.py sync-guidelines` — edit outside the markers.\n"
55
57
  f"{_GUIDE_END}"
56
58
  )
57
59
 
@@ -235,7 +237,7 @@ def _inject_guidelines(project_root: Path, rule_file: bool = False) -> list[tupl
235
237
  # --- commands ----------------------------------------------------------------
236
238
 
237
239
  _INIT_EXCLUDE = {
238
- ".add", "AGENTS.md", "CLAUDE.md", ".git",
240
+ ".add", "AGENTS.md", "CLAUDE.md", ".clinerules", ".git",
239
241
  ".gitignore", ".gitattributes", ".github", ".editorconfig", # VCS/CI/editor scaffolding — no domain signal
240
242
  "LICENSE", "LICENSE.md", "LICENSE.txt", "COPYING", # legal boilerplate — no domain signal
241
243
  } # README/docs/source are NOT excluded: they carry domain content adopt.md maps -> brownfield
@@ -9,7 +9,10 @@ from __future__ import annotations
9
9
 
10
10
  import re
11
11
 
12
- from add_engine.constants import PHASE_OWNER
12
+ from add_engine.constants import (
13
+ PHASE_OWNER, PERSONA_FRONTMATTER_KEYS, PERSONA_REQUIRED_SECTIONS,
14
+ _MUST_ID_RE, _REJECT_CODE_RE, _SCENARIO_TAG_RE, _COVERS_LINE_RE, _TAG_TOKEN_RE,
15
+ )
13
16
  from add_engine.io_state import _die
14
17
 
15
18
 
@@ -45,7 +48,9 @@ def _section_unfilled(md_text: str, header: str) -> bool:
45
48
  """True iff the `header` section is PRESENT but UNFILLED — empty (no real bullet) or
46
49
  still a `<…>` template placeholder. ABSENT section -> False (grandfathered legacy);
47
50
  a filled section (>=1 real bullet, no `<…>`) -> False. Pure predicate — the shared
48
- placeholder test the fill gates use (contract-fill at confirm; build-expectations at build)."""
51
+ placeholder test the fill gates use (contract-fill at confirm; build-expectations at build).
52
+ Angle brackets INSIDE a backtick code span are literal technical notation (`<persona>`,
53
+ `.add/personas/<slug>.md`), not a fill placeholder — only a BARE <…> counts as unfilled."""
49
54
  body, in_sec, present = [], False, False
50
55
  for ln in md_text.splitlines():
51
56
  if ln.startswith(header):
@@ -62,7 +67,55 @@ def _section_unfilled(md_text: str, header: str) -> bool:
62
67
  text = "\n".join(body).strip()
63
68
  if not text:
64
69
  return True # present but empty
65
- return bool(re.search(r"<[^>\n]+>", text)) # a <…> placeholder remains
70
+ no_code = re.sub(r"`[^`\n]*`", "", text) # drop code spans — backtick <…> is content
71
+ return bool(re.search(r"<[^>\n]+>", no_code)) # a BARE <…> placeholder remains
72
+
73
+
74
+ def _persona_missing(md_text: str) -> list[str]:
75
+ """The required frontmatter keys + section headers ABSENT from a persona file
76
+ (`.add/personas/<slug>.md`). `[]` == schema-conformant. Presence-based: a section
77
+ counts as present iff its `## <Title>` header line appears; a frontmatter key counts
78
+ iff a `^<key>:` line appears inside the leading `---`-fenced block. Content QUALITY is
79
+ the AI's authoring concern, not this gate (measure-not-block). PURE; NO-EXEC — no file IO,
80
+ no network, no process launch. The single source of truth is constants.PERSONA_* so the
81
+ schema and its validator never drift."""
82
+ missing: list[str] = []
83
+ fm = re.match(r"\s*---\s*\n(.*?)\n---\s*\n", md_text, re.S)
84
+ fm_body = fm.group(1) if fm else ""
85
+ for key in PERSONA_FRONTMATTER_KEYS:
86
+ if not re.search(rf"(?m)^\s*{re.escape(key)}\s*:", fm_body):
87
+ missing.append(key)
88
+ for section in PERSONA_REQUIRED_SECTIONS:
89
+ if not re.search(rf"(?m)^{re.escape(section)}\s*$", md_text):
90
+ missing.append(section)
91
+ return missing
92
+
93
+
94
+ def _persona_slug_valid(slug: str) -> bool:
95
+ """A persona file slug is valid iff non-empty and alphanumeric with `-`/`_` only
96
+ (mirrors new-task's slug rule). PURE; NO-EXEC."""
97
+ return bool(slug) and slug.replace("-", "").replace("_", "").isalnum()
98
+
99
+
100
+ def _rule_coverage_gaps(sec1: str, sec2: str, sec4: str) -> list[tuple[str, str]]:
101
+ """§1 Must/Reject IDs with no §2 scenario tag and no §4 `covers:` reference — a coverage
102
+ gap (rule-id-coverage). A task that carries NO tag anywhere in §2/§4 is grandfathered
103
+ (never adopted the convention) -> []. A bare `<…>` template placeholder (e.g. the
104
+ unfilled `covers: <M#, R:code — optional>` scaffold) is stripped before scanning — it
105
+ is not authored content, mirrors `_section_unfilled`'s own placeholder convention.
106
+ De-dupes a repeated/typo'd ID. PURE; NO-EXEC."""
107
+ strip_placeholders = lambda text: re.sub(r"<[^>\n]+>", "", text or "")
108
+ tag_ids: set[str] = set()
109
+ for m in _SCENARIO_TAG_RE.finditer(strip_placeholders(sec2)):
110
+ tag_ids.update(_TAG_TOKEN_RE.findall(m.group(1)))
111
+ for m in _COVERS_LINE_RE.finditer(strip_placeholders(sec4)):
112
+ tag_ids.update(_TAG_TOKEN_RE.findall(m.group(1)))
113
+ if not tag_ids:
114
+ return [] # grandfathered — never opted in
115
+ musts = [(mid, "Must") for mid in _MUST_ID_RE.findall(sec1 or "")]
116
+ rejects = [(f"R:{code}", "Reject") for code in _REJECT_CODE_RE.findall(sec1 or "")]
117
+ gaps = [(rid, kind) for rid, kind in musts + rejects if rid not in tag_ids]
118
+ return list(dict.fromkeys(gaps)) # de-dup a repeated ID
66
119
 
67
120
 
68
121
  def _task_done(t: dict) -> bool:
@@ -0,0 +1,165 @@
1
+ """add_engine.search — keyword/substring search over the milestone/task corpus
2
+ (context-search, search-index task). A flat, keyword-only index over document
3
+ prose — title/goal/rationale (MILESTONE.md) or title/Feature (TASK.md) lines,
4
+ never full body, never graph/backlink traversal, never a persisted cache:
5
+ rebuilt fresh on every call (the milestone's own "rebuild-on-demand is enough"
6
+ scope decision). Deliberately distinct from a graph-query command over the
7
+ artifact backlink edges (rejected elsewhere) — this is plain substring
8
+ retrieval over document prose. PURE except the file reads `_search_corpus`
9
+ performs; NO-EXEC — no network, no subprocess, no persona/teacher read.
10
+ """
11
+ from __future__ import annotations
12
+
13
+ import re
14
+ from collections.abc import Iterator
15
+ from pathlib import Path
16
+
17
+ from add_engine.constants import MILESTONE_FILE
18
+
19
+ MAX_SNIPPET_LEN = 120
20
+
21
+ _PLACEHOLDER_RE = re.compile(r"^<[^>\n]+>$")
22
+
23
+
24
+ def _fold_continuation(raw: str) -> str:
25
+ """Join a field's first-line value with any indented wrap-continuation
26
+ lines that follow it into one flat, single-line string (mirrors
27
+ taskdoc._task_prose's continuation join). `raw` is the value's own first
28
+ line followed by the REST of the document — folding stops at the first
29
+ blank line or the first line that is NOT indented (a sibling `key:` line
30
+ never bleeds in). PURE."""
31
+ lines = raw.splitlines()
32
+ if not lines:
33
+ return ""
34
+ parts = [lines[0].strip()]
35
+ for ln in lines[1:]:
36
+ if not ln or not ln[:1].isspace():
37
+ break
38
+ stripped = ln.strip()
39
+ if not stripped:
40
+ break
41
+ parts.append(stripped)
42
+ return " ".join(p for p in parts if p).strip()
43
+
44
+
45
+ def _line_field(text: str, prefix: str) -> str:
46
+ """The first `prefix`-led line's value (continuation-folded via
47
+ `_fold_continuation`); '' if no line starts with `prefix`. Shared helper
48
+ behind `_milestone_fields`/`_task_fields` — not part of the frozen 6, but
49
+ a private implementation detail (mirrors milestones.py's own private
50
+ _VERIFY_CITE_RE convention)."""
51
+ lines = text.splitlines()
52
+ for i, ln in enumerate(lines):
53
+ if ln.startswith(prefix):
54
+ first = ln[len(prefix):].strip()
55
+ return _fold_continuation("\n".join([first, *lines[i + 1:]]))
56
+ return ""
57
+
58
+
59
+ def _milestone_fields(text: str) -> dict[str, str]:
60
+ """{'title', 'goal', 'rationale'} from raw MILESTONE.md text, continuation-
61
+ folded. Missing field -> ''. Extends milestones._milestone_doc's
62
+ (title, goal) shape with 'rationale'. Dict INSERTION ORDER (title, goal,
63
+ rationale) is the snippet priority `_keyword_hit` reads. PURE; NO-EXEC."""
64
+ return {
65
+ "title": _line_field(text, "# MILESTONE:"),
66
+ "goal": _line_field(text, "goal:"),
67
+ "rationale": _line_field(text, "rationale:"),
68
+ }
69
+
70
+
71
+ def _task_fields(text: str) -> dict[str, str]:
72
+ """{'title', 'feature'} from raw TASK.md text — H1 title + §1 `Feature:`
73
+ line, continuation-folded; an unfilled `<name>`-style placeholder value
74
+ -> '' (never treated as matchable content). Dict INSERTION ORDER (title,
75
+ feature) is the snippet priority `_keyword_hit` reads. PURE; NO-EXEC."""
76
+ feature = _line_field(text, "Feature:")
77
+ if _PLACEHOLDER_RE.match(feature.strip()):
78
+ feature = ""
79
+ return {
80
+ "title": _line_field(text, "# TASK:"),
81
+ "feature": feature,
82
+ }
83
+
84
+
85
+ def _keyword_hit(fields: dict[str, str], keywords: list[str]) -> tuple[int, str] | None:
86
+ """(match_count, snippet) for one artifact's indexed fields against
87
+ `keywords`: case-insensitive substring, OR-combined. count sums ALL
88
+ keyword occurrences across ALL fields; snippet is the first field (dict
89
+ order) containing ANY keyword match, truncated to MAX_SNIPPET_LEN with an
90
+ ellipsis. None -> no keyword matched anywhere. PURE; NO-EXEC."""
91
+ needles = [k.lower() for k in keywords]
92
+ count = 0
93
+ snippet: str | None = None
94
+ for value in fields.values():
95
+ if not value:
96
+ continue
97
+ hay = value.lower()
98
+ field_count = sum(hay.count(n) for n in needles if n)
99
+ count += field_count
100
+ if snippet is None and field_count > 0:
101
+ snippet = (value[:MAX_SNIPPET_LEN] + "...") if len(value) > MAX_SNIPPET_LEN else value
102
+ if count == 0:
103
+ return None
104
+ return count, snippet
105
+
106
+
107
+ def _iter_corpus(root: Path) -> Iterator[tuple[Path, str, bool]]:
108
+ """Yield (path, kind, archived) over the 4 frozen corpus roots, in fixed
109
+ order: active milestones, active tasks, archived milestones, archived
110
+ tasks (nested under the owning milestone's compact bundle — never a flat
111
+ `archive/*/TASK.md`). A missing root dir yields nothing — `Path.glob` on
112
+ an absent directory is empty, never an OSError."""
113
+ for pattern, kind, archived in (
114
+ (f"milestones/*/{MILESTONE_FILE}", "milestone", False),
115
+ ("tasks/*/TASK.md", "task", False),
116
+ (f"archive/*/{MILESTONE_FILE}", "milestone", True),
117
+ ("archive/*/tasks/*/TASK.md", "task", True),
118
+ ):
119
+ for p in sorted(root.glob(pattern)):
120
+ yield p, kind, archived
121
+
122
+
123
+ def _own_status(text: str, kind: str) -> str:
124
+ """The doc's own status — milestone: the `status:` token off the
125
+ `stage: ... · status: ... · created: ...` header line; task: the
126
+ `phase:` header line's value. '(unknown)' if absent. Never consulted for
127
+ an archive-root hit — `_search_corpus` overrides to "archived" first.
128
+ Private implementation detail behind `_search_corpus`. PURE."""
129
+ if kind == "milestone":
130
+ m = re.search(r"(?m)^stage:.*?\bstatus:\s*(\S+)", text)
131
+ else:
132
+ m = re.search(r"(?m)^phase:\s*(\S+)", text)
133
+ return m.group(1) if m else "(unknown)"
134
+
135
+
136
+ def _search_corpus(root: Path, keywords: list[str]) -> list[dict]:
137
+ """Assemble {slug, kind, status, snippet, count} per hit across the full
138
+ corpus (`_iter_corpus`): status is the literal "archived" for any
139
+ archive/-root hit (overrides a stale in-doc status:/phase: value), else
140
+ the doc's own status:/phase: line (`_own_status`), or "(unknown)" if
141
+ absent. Sorted by (-count, slug) — strictly descending count, ties broken
142
+ alphabetically by slug. An unreadable doc is skipped, never raises
143
+ (mirrors _collect_open_deltas' degrade-safe try/except OSError). PURE
144
+ except the file reads it performs."""
145
+ hits: list[dict] = []
146
+ for path, kind, archived in _iter_corpus(root):
147
+ try:
148
+ text = path.read_text(encoding="utf-8")
149
+ except OSError:
150
+ continue
151
+ fields = _milestone_fields(text) if kind == "milestone" else _task_fields(text)
152
+ hit = _keyword_hit(fields, keywords)
153
+ if hit is None:
154
+ continue
155
+ count, snippet = hit
156
+ status = "archived" if archived else _own_status(text, kind)
157
+ hits.append({
158
+ "slug": path.parent.name,
159
+ "kind": kind,
160
+ "status": status,
161
+ "snippet": snippet,
162
+ "count": count,
163
+ })
164
+ hits.sort(key=lambda h: (-h["count"], h["slug"]))
165
+ return hits
@@ -2,11 +2,27 @@
2
2
 
3
3
  <term>: <definition>
4
4
 
5
+ ## Sensitivity classes
6
+ The risk-CLASS a task declares (`sensitivity:` in the TASK header) — read live by freeze/status/check.
7
+ The base four below are method-UNIVERSAL and always apply; you EXTEND them with your project's own
8
+ DOMAIN classes (the AI keeps these current as the domain is learned — see the `sensitivity` skill guide).
9
+
10
+ Base (always apply): security · data · architecture · mechanical
11
+
12
+ <!-- Domain classes — uncomment and add yours, one `- <token>: <definition>` bullet per line:
13
+ - pii: personally identifiable information; any task touching it escalates to human review
14
+ - payments: money movement; reconciliation + an audit trail are required
15
+ -->
16
+
5
17
  # ADD method vocabulary (domain-standard names; bridges to legacy terms)
6
18
  GOAL: the one durable outcome a project (and each milestone) runs toward — the loop's orientation anchor, declared as the lowercase `goal:` line in PROJECT.md / MILESTONE.md and surfaced by status/guide every session; distinct from a task's §1 Must (a single required behavior, not the whole-project outcome).
7
19
  deep verify: the deepened Verify evidence (v20) required beyond passing tests — for a task that produced code, that every new symbol is referenced (wiring) and no new dead/unused code exists; for prose/non-code, a recorded no-skim semantic read; which path applies is resolver-judged and the engine never classifies (a rubric, not add.py).
8
20
  earned green: the build-integrity property — the green is EARNED when the implementation makes the UNCHANGED red suite pass by GENERAL behavior, not by overfitting src to the test fixtures, vacuous/tautological asserts, or stubbing real logic away; task 1's tamper tripwire protects it mechanically and the adversarial refute-read by judgment, and a confirmed earned-green failure is HARD-STOP-class (never auto-passed, never RISK-ACCEPTED).
9
21
  adversarial refute-read: the verify-gate, whole-suite specialization of run.md's adversarial verify — an independent reviewer (a subagent under autonomy:auto is recommended; the engine never spawns one) prompted to argue "the green was NOT earned", scoring the earned-green cheats (overfit · vacuous · stub) the mechanical tripwire cannot see.
22
+ advisor 3-lens verdict: the sequential security→concurrency→architecture non-functional sweep recorded in §6 (Verdict · Residue · Binding fields); Verdict is PASS or HARD-STOP only; a Security HARD-STOP short-circuits the remaining lenses.
23
+ advisor-gate-relax: a risk:high + sensitivity:mechanical task with a recorded Verdict:PASS and Residue:none in the §6 Advisor 3-lens verdict may auto-complete via gate PASS without requiring a lowered autonomy level; security and all non-mechanical sensitivity classes are never relaxed via this pathway.
24
+ advisory verdict: a §6 Advisor 3-lens verdict with Binding:advisory — surfaced for human awareness but not engine-enforced (the gate is not relaxed by it); applies to all sensitivity classes other than mechanical.
25
+ binding verdict: a §6 Advisor 3-lens verdict with Binding:yes — engine-enforced for risk:high + sensitivity:mechanical tasks (gates auto-completion via advisor-gate-relax); tasks carrying any other sensitivity class receive an advisory verdict instead.
10
26
  bounded self-heal: the verify-gate loop (heal-then-escalate) that gives a CONFIRMED cheat — mechanical tamper (the tripwire) or a reported earned-green failure (`add.py heal --reason`) — a chance to redo honestly before it stops: the engine returns the task to build, COUNTS the attempt, CAPS it at 3 (`HEAL_CAP`), and on the next confirmed cheat forces a HARD-STOP that escalates to the human. The counter is MONOTONIC — it never auto-resets (cmd_phase is unguarded, so a reset would be a zero-human cap bypass). The engine counts/caps/escalates; the AGENT does the honest re-build (never the engine). An honest build passes even at the cap (the cap bites a CONTINUED cheat, never a recovery); a gamed green is never auto-passed, never RISK-ACCEPTED-waived.
11
27
  onboarding: the install -> first-milestone path (formerly "on-ramp").
12
28
  primary flow: the solid forward path of the flow diagram — a phase starts only when its input exists (formerly "forward spine").
@@ -3,6 +3,7 @@
3
3
  goal: {{goal}}
4
4
  rationale: <why this scope — the confirmed intake classification (bucket + reason)>
5
5
  stage: {{stage}} · status: active · created: {{date}}
6
+ release: pending
6
7
 
7
8
  > SDD living doc for this milestone. Keep it THIN: breadth, shared decisions, and
8
9
  > exit criteria only — per-task detail lives in each `.add/tasks/<slug>/TASK.md`,
@@ -12,6 +13,14 @@ stage: {{stage}} · status: active · created: {{date}}
12
13
  In: <what this milestone delivers>
13
14
  Out: <explicitly deferred — the anti-scope-creep list>
14
15
 
16
+ > UI/UX in scope? Name it precisely, not "make it nice" — information architecture ·
17
+ > interaction pattern · visual hierarchy · design tokens · component states ·
18
+ > accessibility floor (WCAG AA) · responsive breakpoints · user journey
19
+ > (`.add/personas-teacher/design/`). Precise ≠ distinctive: skip generic AI-design
20
+ > defaults (cream+serif+terracotta · near-black+neon · broadsheet-hairline) and name ONE
21
+ > deliberate signature element instead (Claude Code's `frontend-design` skill). A UI
22
+ > feature also triggers DESIGN.md via the `add` skill's design.md.
23
+
15
24
  ## Shared decisions & glossary deltas (living — every task must honor these)
16
25
  - <cross-cutting rule, named from GLOSSARY.md>
17
26
 
@@ -0,0 +1,44 @@
1
+ <!-- PROMPT.persona.md.tmpl — the ONE canonical, portable, persona-aware worker prompt. Render it
2
+ per stream (substitute the {{...}} slots). NO runner-specific tokens live in the BODY — only
3
+ the thin per-runner spawn differs (see "## Adapter stubs" below). It mirrors the streams.md
4
+ worker contract; the engine renders it LOCALLY (no network, no process launch). -->
5
+
6
+ # Worker prompt — task {{TASK_SLUG}} ({{MILESTONE}})
7
+
8
+ Objective: execute the LOCKED dynamic run — drive the §4 TESTS red->green against the FROZEN
9
+ contract {{CONTRACT_VERSION}}, converge, and resolve verify per autonomy={{AUTONOMY}}. You own ONLY
10
+ the machine-led span; the two human decision points (bundle approval, escalated verify) are NOT yours.
11
+
12
+ Persona: load `.add/personas/{{PERSONA_SLUG}}.md` and BECOME it —
13
+ - its `## Identity` is your identity (the perspective you bring);
14
+ - its `## Critical Rules` are your non-negotiable constraints (apply them as your build limits);
15
+ - its `## Success Metrics` are your DONE-BAR — the measurable bar a result must clear before "done".
16
+ No persona matched / seeded? Use this generic persona instead: a {{DOMAIN}} engineer with 15 years
17
+ building {{DOMAIN_DETAIL}}; correctness over speed. The generic body always works — it never blocks.
18
+
19
+ Expertise: if your runner supports specialist injection (a skill, a system-prompt preamble, an agent
20
+ profile), load the one matching {{PERSONA_SLUG}} / {{DOMAIN}}. Otherwise the persona above is it.
21
+
22
+ Boundary:
23
+ - MAY: rewrite code in src/ · drive tests green WITHOUT weakening them · gather verify evidence.
24
+ - MUST NOT: edit the frozen contract or locked scope · weaken/delete/skip a test · touch a sibling stream.
25
+ - STOP-and-escalate (return findings; do not decide): any SECURITY finding (HARD-STOP, always) ·
26
+ a discovered scope/contract gap · a concurrency/architecture risk the tests cannot exercise.
27
+
28
+ ## Adapter stubs
29
+ ONE body above; only the spawn differs per runner. Honesty: only **Claude Code** is verified
30
+ in-repo; every other row is an *illustrative* shape — confirm exact syntax with the `find-docs`
31
+ skill before relying on it.
32
+
33
+ | Runner | Spawn the worker with the rendered prompt body |
34
+ |--------|------------------------------------------------|
35
+ | Claude Code (reference) | `Task(subagent_type="add:add-{{PHASE}}", prompt=<rendered PROMPT.persona.md>, isolation="worktree")` — the registered phase-specialist (roster: `agents/add-<phase>.md`; advisor.md). Plain `Task(description=<label>, prompt=…)` still works when no roster is installed. |
36
+ | Codex (illustrative) | `cd $WT && codex run --prompt-file PROMPT.persona.md` |
37
+ | opencode (illustrative) | `cd $WT && opencode run --prompt-file PROMPT.persona.md` |
38
+ | Cursor (illustrative) | `cursor agent --prompt-file PROMPT.persona.md` |
39
+ | Windsurf (illustrative) | `windsurf run --prompt-file PROMPT.persona.md` |
40
+ | Trae (illustrative) | `trae run --prompt-file PROMPT.persona.md` |
41
+ | Copilot (illustrative) | `gh copilot suggest < PROMPT.persona.md` |
42
+ | Cline (illustrative) | `cline task --file PROMPT.persona.md` |
43
+ | Aider (illustrative) | `aider --message-file PROMPT.persona.md` |
44
+ | Gemini CLI (illustrative) | `gemini generate --prompt-file PROMPT.persona.md` |
@@ -1,6 +1,7 @@
1
1
  # TASK: {{title}}
2
2
 
3
3
  slug: {{slug}} · created: {{date}} · stage: {{stage}}
4
+ milestone: {{milestone}}
4
5
  autonomy: {{autonomy}} <!-- Multi-component repo? add a `component: <name>` line (declared in .add/components.toml) to bind this fast task to a component — its root joins §5 Scope and its green-bar/verify surface at the gate. Omit for single-component projects. -->
5
6
  phase: ground <!-- fast lane: ground -> specify -> contract -> tests -> build -> verify -> observe -> done -->
6
7
  fast: true <!-- the fast lane: a small task, collapsed flow + minimal template. Omit --fast for full rigor. -->