@pilotspace/add 1.13.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.
- package/CHANGELOG.md +189 -1
- package/THIRD_PARTY_NOTICES.md +41 -0
- package/bin/cli.js +240 -26
- package/docs/02-the-flow.md +1 -1
- package/docs/03-step-1-specify.md +4 -0
- package/docs/04-step-2-scenarios.md +4 -0
- package/docs/06-step-4-tests.md +4 -0
- package/docs/17-components.md +23 -5
- package/docs/18-personas.md +80 -0
- package/docs/add-milestone-task-lifecycle.png +0 -0
- package/docs/add-task-growth-wheel.png +0 -0
- package/docs/appendix-c-glossary.md +8 -4
- package/docs/appendix-d-worked-example.md +79 -0
- package/package.json +3 -1
- package/personas-teacher/LICENSE +21 -0
- package/personas-teacher/README.md +1067 -0
- package/personas-teacher/VENDOR.md +13 -0
- package/personas-teacher/academic/academic-anthropologist.md +125 -0
- package/personas-teacher/academic/academic-geographer.md +127 -0
- package/personas-teacher/academic/academic-historian.md +123 -0
- package/personas-teacher/academic/academic-narratologist.md +118 -0
- package/personas-teacher/academic/academic-psychologist.md +118 -0
- package/personas-teacher/design/design-brand-guardian.md +322 -0
- package/personas-teacher/design/design-image-prompt-engineer.md +236 -0
- package/personas-teacher/design/design-inclusive-visuals-specialist.md +71 -0
- package/personas-teacher/design/design-persona-walkthrough.md +272 -0
- package/personas-teacher/design/design-ui-designer.md +383 -0
- package/personas-teacher/design/design-ux-architect.md +469 -0
- package/personas-teacher/design/design-ux-researcher.md +329 -0
- package/personas-teacher/design/design-visual-storyteller.md +149 -0
- package/personas-teacher/design/design-whimsy-injector.md +438 -0
- package/personas-teacher/divisions.json +21 -0
- package/personas-teacher/engineering/engineering-ai-data-remediation-engineer.md +211 -0
- package/personas-teacher/engineering/engineering-ai-engineer.md +146 -0
- package/personas-teacher/engineering/engineering-autonomous-optimization-architect.md +107 -0
- package/personas-teacher/engineering/engineering-backend-architect.md +236 -0
- package/personas-teacher/engineering/engineering-cms-developer.md +536 -0
- package/personas-teacher/engineering/engineering-code-reviewer.md +76 -0
- package/personas-teacher/engineering/engineering-codebase-onboarding-engineer.md +173 -0
- package/personas-teacher/engineering/engineering-data-engineer.md +306 -0
- package/personas-teacher/engineering/engineering-database-optimizer.md +176 -0
- package/personas-teacher/engineering/engineering-devops-automator.md +376 -0
- package/personas-teacher/engineering/engineering-drupal-shopping-cart.md +360 -0
- package/personas-teacher/engineering/engineering-email-intelligence-engineer.md +353 -0
- package/personas-teacher/engineering/engineering-embedded-firmware-engineer.md +173 -0
- package/personas-teacher/engineering/engineering-feishu-integration-developer.md +598 -0
- package/personas-teacher/engineering/engineering-filament-optimization-specialist.md +283 -0
- package/personas-teacher/engineering/engineering-frontend-developer.md +225 -0
- package/personas-teacher/engineering/engineering-git-workflow-master.md +84 -0
- package/personas-teacher/engineering/engineering-incident-response-commander.md +444 -0
- package/personas-teacher/engineering/engineering-it-service-manager.md +561 -0
- package/personas-teacher/engineering/engineering-minimal-change-engineer.md +207 -0
- package/personas-teacher/engineering/engineering-mobile-app-builder.md +493 -0
- package/personas-teacher/engineering/engineering-multi-agent-systems-architect.md +600 -0
- package/personas-teacher/engineering/engineering-orgscript-engineer.md +113 -0
- package/personas-teacher/engineering/engineering-prompt-engineer.md +202 -0
- package/personas-teacher/engineering/engineering-rapid-prototyper.md +462 -0
- package/personas-teacher/engineering/engineering-senior-developer.md +176 -0
- package/personas-teacher/engineering/engineering-software-architect.md +112 -0
- package/personas-teacher/engineering/engineering-solidity-smart-contract-engineer.md +522 -0
- package/personas-teacher/engineering/engineering-sre.md +90 -0
- package/personas-teacher/engineering/engineering-technical-writer.md +393 -0
- package/personas-teacher/engineering/engineering-voice-ai-integration-engineer.md +561 -0
- package/personas-teacher/engineering/engineering-wechat-mini-program-developer.md +350 -0
- package/personas-teacher/engineering/engineering-wordpress-shopping-cart.md +346 -0
- package/personas-teacher/examples/README.md +48 -0
- package/personas-teacher/examples/nexus-spatial-discovery.md +852 -0
- package/personas-teacher/examples/workflow-book-chapter.md +55 -0
- package/personas-teacher/examples/workflow-landing-page.md +119 -0
- package/personas-teacher/examples/workflow-startup-mvp.md +155 -0
- package/personas-teacher/examples/workflow-with-memory.md +238 -0
- package/personas-teacher/finance/finance-bookkeeper-controller.md +260 -0
- package/personas-teacher/finance/finance-financial-analyst.md +234 -0
- package/personas-teacher/finance/finance-fpa-analyst.md +263 -0
- package/personas-teacher/finance/finance-investment-researcher.md +272 -0
- package/personas-teacher/finance/finance-tax-strategist.md +239 -0
- package/personas-teacher/game-development/blender/blender-addon-engineer.md +234 -0
- package/personas-teacher/game-development/game-audio-engineer.md +264 -0
- package/personas-teacher/game-development/game-designer.md +167 -0
- package/personas-teacher/game-development/godot/godot-gameplay-scripter.md +334 -0
- package/personas-teacher/game-development/godot/godot-multiplayer-engineer.md +297 -0
- package/personas-teacher/game-development/godot/godot-shader-developer.md +266 -0
- package/personas-teacher/game-development/level-designer.md +208 -0
- package/personas-teacher/game-development/narrative-designer.md +243 -0
- package/personas-teacher/game-development/roblox-studio/roblox-avatar-creator.md +297 -0
- package/personas-teacher/game-development/roblox-studio/roblox-experience-designer.md +305 -0
- package/personas-teacher/game-development/roblox-studio/roblox-systems-scripter.md +325 -0
- package/personas-teacher/game-development/technical-artist.md +229 -0
- package/personas-teacher/game-development/unity/unity-architect.md +271 -0
- package/personas-teacher/game-development/unity/unity-editor-tool-developer.md +310 -0
- package/personas-teacher/game-development/unity/unity-multiplayer-engineer.md +321 -0
- package/personas-teacher/game-development/unity/unity-shader-graph-artist.md +269 -0
- package/personas-teacher/game-development/unreal-engine/unreal-multiplayer-architect.md +313 -0
- package/personas-teacher/game-development/unreal-engine/unreal-systems-engineer.md +310 -0
- package/personas-teacher/game-development/unreal-engine/unreal-technical-artist.md +256 -0
- package/personas-teacher/game-development/unreal-engine/unreal-world-builder.md +273 -0
- package/personas-teacher/gis/gis-3d-scene-developer.md +111 -0
- package/personas-teacher/gis/gis-analyst.md +91 -0
- package/personas-teacher/gis/gis-bim-specialist.md +108 -0
- package/personas-teacher/gis/gis-cartography-designer.md +150 -0
- package/personas-teacher/gis/gis-drone-reality-mapping.md +120 -0
- package/personas-teacher/gis/gis-geoai-ml-engineer.md +105 -0
- package/personas-teacher/gis/gis-geoprocessing-specialist.md +97 -0
- package/personas-teacher/gis/gis-qa-engineer.md +133 -0
- package/personas-teacher/gis/gis-solution-engineer.md +101 -0
- package/personas-teacher/gis/gis-spatial-data-engineer.md +97 -0
- package/personas-teacher/gis/gis-spatial-data-scientist.md +111 -0
- package/personas-teacher/gis/gis-technical-consultant.md +86 -0
- package/personas-teacher/gis/gis-web-gis-developer.md +108 -0
- package/personas-teacher/marketing/marketing-aeo-foundations.md +264 -0
- package/personas-teacher/marketing/marketing-agentic-search-optimizer.md +311 -0
- package/personas-teacher/marketing/marketing-ai-citation-strategist.md +170 -0
- package/personas-teacher/marketing/marketing-app-store-optimizer.md +321 -0
- package/personas-teacher/marketing/marketing-baidu-seo-specialist.md +226 -0
- package/personas-teacher/marketing/marketing-bilibili-content-strategist.md +199 -0
- package/personas-teacher/marketing/marketing-book-co-author.md +110 -0
- package/personas-teacher/marketing/marketing-carousel-growth-engine.md +199 -0
- package/personas-teacher/marketing/marketing-china-ecommerce-operator.md +283 -0
- package/personas-teacher/marketing/marketing-china-market-localization-strategist.md +283 -0
- package/personas-teacher/marketing/marketing-content-creator.md +54 -0
- package/personas-teacher/marketing/marketing-cross-border-ecommerce.md +259 -0
- package/personas-teacher/marketing/marketing-douyin-strategist.md +149 -0
- package/personas-teacher/marketing/marketing-email-strategist.md +249 -0
- package/personas-teacher/marketing/marketing-global-podcast-strategist.md +206 -0
- package/personas-teacher/marketing/marketing-growth-hacker.md +54 -0
- package/personas-teacher/marketing/marketing-instagram-curator.md +113 -0
- package/personas-teacher/marketing/marketing-kuaishou-strategist.md +223 -0
- package/personas-teacher/marketing/marketing-linkedin-content-creator.md +214 -0
- package/personas-teacher/marketing/marketing-livestream-commerce-coach.md +305 -0
- package/personas-teacher/marketing/marketing-multi-platform-publisher.md +217 -0
- package/personas-teacher/marketing/marketing-podcast-strategist.md +277 -0
- package/personas-teacher/marketing/marketing-pr-communications-manager.md +473 -0
- package/personas-teacher/marketing/marketing-private-domain-operator.md +308 -0
- package/personas-teacher/marketing/marketing-reddit-community-builder.md +123 -0
- package/personas-teacher/marketing/marketing-seo-specialist.md +321 -0
- package/personas-teacher/marketing/marketing-short-video-editing-coach.md +412 -0
- package/personas-teacher/marketing/marketing-social-media-strategist.md +125 -0
- package/personas-teacher/marketing/marketing-tiktok-strategist.md +125 -0
- package/personas-teacher/marketing/marketing-twitter-engager.md +126 -0
- package/personas-teacher/marketing/marketing-video-optimization-specialist.md +119 -0
- package/personas-teacher/marketing/marketing-wechat-official-account.md +145 -0
- package/personas-teacher/marketing/marketing-weibo-strategist.md +240 -0
- package/personas-teacher/marketing/marketing-x-twitter-intelligence-analyst.md +161 -0
- package/personas-teacher/marketing/marketing-xiaohongshu-specialist.md +138 -0
- package/personas-teacher/marketing/marketing-zhihu-strategist.md +162 -0
- package/personas-teacher/paid-media/paid-media-auditor.md +71 -0
- package/personas-teacher/paid-media/paid-media-creative-strategist.md +71 -0
- package/personas-teacher/paid-media/paid-media-paid-social-strategist.md +71 -0
- package/personas-teacher/paid-media/paid-media-ppc-strategist.md +71 -0
- package/personas-teacher/paid-media/paid-media-programmatic-buyer.md +71 -0
- package/personas-teacher/paid-media/paid-media-search-query-analyst.md +71 -0
- package/personas-teacher/paid-media/paid-media-tracking-specialist.md +71 -0
- package/personas-teacher/product/product-behavioral-nudge-engine.md +80 -0
- package/personas-teacher/product/product-feedback-synthesizer.md +119 -0
- package/personas-teacher/product/product-manager.md +469 -0
- package/personas-teacher/product/product-sprint-prioritizer.md +154 -0
- package/personas-teacher/product/product-trend-researcher.md +159 -0
- package/personas-teacher/project-management/project-management-experiment-tracker.md +198 -0
- package/personas-teacher/project-management/project-management-jira-workflow-steward.md +230 -0
- package/personas-teacher/project-management/project-management-meeting-notes-specialist.md +95 -0
- package/personas-teacher/project-management/project-management-project-shepherd.md +194 -0
- package/personas-teacher/project-management/project-management-studio-operations.md +200 -0
- package/personas-teacher/project-management/project-management-studio-producer.md +203 -0
- package/personas-teacher/project-management/project-manager-senior.md +135 -0
- package/personas-teacher/sales/sales-account-strategist.md +227 -0
- package/personas-teacher/sales/sales-coach.md +271 -0
- package/personas-teacher/sales/sales-deal-strategist.md +180 -0
- package/personas-teacher/sales/sales-discovery-coach.md +225 -0
- package/personas-teacher/sales/sales-engineer.md +182 -0
- package/personas-teacher/sales/sales-offer-lead-gen-strategist.md +257 -0
- package/personas-teacher/sales/sales-outbound-strategist.md +201 -0
- package/personas-teacher/sales/sales-pipeline-analyst.md +267 -0
- package/personas-teacher/sales/sales-proposal-strategist.md +217 -0
- package/personas-teacher/security/security-appsec-engineer.md +491 -0
- package/personas-teacher/security/security-architect.md +304 -0
- package/personas-teacher/security/security-blockchain-security-auditor.md +463 -0
- package/personas-teacher/security/security-cloud-security-architect.md +523 -0
- package/personas-teacher/security/security-compliance-auditor.md +158 -0
- package/personas-teacher/security/security-incident-responder.md +437 -0
- package/personas-teacher/security/security-penetration-tester.md +399 -0
- package/personas-teacher/security/security-senior-secops.md +750 -0
- package/personas-teacher/security/security-threat-detection-engineer.md +534 -0
- package/personas-teacher/security/security-threat-intelligence-analyst.md +644 -0
- package/personas-teacher/spatial-computing/macos-spatial-metal-engineer.md +337 -0
- package/personas-teacher/spatial-computing/terminal-integration-specialist.md +70 -0
- package/personas-teacher/spatial-computing/visionos-spatial-engineer.md +54 -0
- package/personas-teacher/spatial-computing/xr-cockpit-interaction-specialist.md +32 -0
- package/personas-teacher/spatial-computing/xr-immersive-developer.md +32 -0
- package/personas-teacher/spatial-computing/xr-interface-architect.md +32 -0
- package/personas-teacher/specialized/accounts-payable-agent.md +185 -0
- package/personas-teacher/specialized/agentic-identity-trust.md +387 -0
- package/personas-teacher/specialized/agents-orchestrator.md +367 -0
- package/personas-teacher/specialized/automation-governance-architect.md +216 -0
- package/personas-teacher/specialized/business-strategist.md +488 -0
- package/personas-teacher/specialized/change-management-consultant.md +497 -0
- package/personas-teacher/specialized/chief-financial-officer.md +388 -0
- package/personas-teacher/specialized/corporate-training-designer.md +192 -0
- package/personas-teacher/specialized/customer-service.md +398 -0
- package/personas-teacher/specialized/customer-success-manager.md +460 -0
- package/personas-teacher/specialized/data-consolidation-agent.md +60 -0
- package/personas-teacher/specialized/data-privacy-officer.md +412 -0
- package/personas-teacher/specialized/esg-sustainability-officer.md +396 -0
- package/personas-teacher/specialized/government-digital-presales-consultant.md +363 -0
- package/personas-teacher/specialized/grant-writer.md +511 -0
- package/personas-teacher/specialized/healthcare-customer-service.md +389 -0
- package/personas-teacher/specialized/healthcare-marketing-compliance.md +395 -0
- package/personas-teacher/specialized/hospitality-guest-services.md +603 -0
- package/personas-teacher/specialized/hr-onboarding.md +451 -0
- package/personas-teacher/specialized/identity-graph-operator.md +260 -0
- package/personas-teacher/specialized/language-translator.md +264 -0
- package/personas-teacher/specialized/legal-billing-time-tracking.md +569 -0
- package/personas-teacher/specialized/legal-client-intake.md +492 -0
- package/personas-teacher/specialized/legal-document-review.md +454 -0
- package/personas-teacher/specialized/loan-officer-assistant.md +555 -0
- package/personas-teacher/specialized/lsp-index-engineer.md +314 -0
- package/personas-teacher/specialized/ma-integration-manager.md +427 -0
- package/personas-teacher/specialized/medical-billing-coding-specialist.md +491 -0
- package/personas-teacher/specialized/operations-manager.md +399 -0
- package/personas-teacher/specialized/organizational-psychologist.md +391 -0
- package/personas-teacher/specialized/personal-growth-mentor.md +159 -0
- package/personas-teacher/specialized/real-estate-buyer-seller.md +596 -0
- package/personas-teacher/specialized/recruitment-specialist.md +509 -0
- package/personas-teacher/specialized/report-distribution-agent.md +65 -0
- package/personas-teacher/specialized/retail-customer-returns.md +566 -0
- package/personas-teacher/specialized/sales-data-extraction-agent.md +67 -0
- package/personas-teacher/specialized/sales-outreach.md +425 -0
- package/personas-teacher/specialized/specialized-chief-of-staff.md +279 -0
- package/personas-teacher/specialized/specialized-civil-engineer.md +356 -0
- package/personas-teacher/specialized/specialized-cultural-intelligence-strategist.md +88 -0
- package/personas-teacher/specialized/specialized-developer-advocate.md +317 -0
- package/personas-teacher/specialized/specialized-document-generator.md +55 -0
- package/personas-teacher/specialized/specialized-french-consulting-market.md +192 -0
- package/personas-teacher/specialized/specialized-korean-business-navigator.md +216 -0
- package/personas-teacher/specialized/specialized-mcp-builder.md +248 -0
- package/personas-teacher/specialized/specialized-model-qa.md +488 -0
- package/personas-teacher/specialized/specialized-pricing-analyst.md +243 -0
- package/personas-teacher/specialized/specialized-salesforce-architect.md +180 -0
- package/personas-teacher/specialized/specialized-strategy-duel-agent.md +130 -0
- package/personas-teacher/specialized/specialized-workflow-architect.md +597 -0
- package/personas-teacher/specialized/study-abroad-advisor.md +282 -0
- package/personas-teacher/specialized/supply-chain-strategist.md +582 -0
- package/personas-teacher/specialized/zk-steward.md +211 -0
- package/personas-teacher/strategy/EXECUTIVE-BRIEF.md +95 -0
- package/personas-teacher/strategy/QUICKSTART.md +194 -0
- package/personas-teacher/strategy/coordination/agent-activation-prompts.md +401 -0
- package/personas-teacher/strategy/coordination/handoff-templates.md +357 -0
- package/personas-teacher/strategy/nexus-strategy.md +1110 -0
- package/personas-teacher/strategy/playbooks/phase-0-discovery.md +178 -0
- package/personas-teacher/strategy/playbooks/phase-1-strategy.md +238 -0
- package/personas-teacher/strategy/playbooks/phase-2-foundation.md +278 -0
- package/personas-teacher/strategy/playbooks/phase-3-build.md +286 -0
- package/personas-teacher/strategy/playbooks/phase-4-hardening.md +332 -0
- package/personas-teacher/strategy/playbooks/phase-5-launch.md +277 -0
- package/personas-teacher/strategy/playbooks/phase-6-operate.md +318 -0
- package/personas-teacher/strategy/runbooks/scenario-enterprise-feature.md +157 -0
- package/personas-teacher/strategy/runbooks/scenario-incident-response.md +217 -0
- package/personas-teacher/strategy/runbooks/scenario-marketing-campaign.md +187 -0
- package/personas-teacher/strategy/runbooks/scenario-startup-mvp.md +154 -0
- package/personas-teacher/support/support-analytics-reporter.md +365 -0
- package/personas-teacher/support/support-executive-summary-generator.md +212 -0
- package/personas-teacher/support/support-finance-tracker.md +442 -0
- package/personas-teacher/support/support-infrastructure-maintainer.md +618 -0
- package/personas-teacher/support/support-legal-compliance-checker.md +588 -0
- package/personas-teacher/support/support-support-responder.md +585 -0
- package/personas-teacher/testing/testing-accessibility-auditor.md +316 -0
- package/personas-teacher/testing/testing-api-tester.md +306 -0
- package/personas-teacher/testing/testing-evidence-collector.md +210 -0
- package/personas-teacher/testing/testing-performance-benchmarker.md +268 -0
- package/personas-teacher/testing/testing-reality-checker.md +236 -0
- package/personas-teacher/testing/testing-test-results-analyzer.md +305 -0
- package/personas-teacher/testing/testing-tool-evaluator.md +394 -0
- package/personas-teacher/testing/testing-workflow-optimizer.md +450 -0
- package/personas-teacher/tools.json +19 -0
- package/skill/add/SKILL.md +17 -12
- package/skill/add/advisor.md +31 -34
- package/skill/add/components.md +21 -23
- package/skill/add/confidence.md +1 -1
- package/skill/add/deltas.md +12 -13
- package/skill/add/design.md +52 -49
- package/skill/add/fold.md +18 -16
- package/skill/add/intake.md +4 -3
- package/skill/add/loop.md +1 -2
- package/skill/add/phases/0-ground.md +32 -34
- package/skill/add/phases/0-setup.md +22 -21
- package/skill/add/phases/1-specify.md +8 -6
- package/skill/add/phases/3-contract.md +1 -1
- package/skill/add/phases/4-tests.md +9 -10
- package/skill/add/phases/5-build.md +17 -13
- package/skill/add/phases/6-verify.md +7 -4
- package/skill/add/phases/7-observe.md +2 -1
- package/skill/add/report-template.md +61 -18
- package/skill/add/run.md +4 -3
- package/skill/add/scope.md +2 -2
- package/skill/add/sensitivity.md +51 -0
- package/skill/add/streams.md +24 -23
- package/tooling/add.py +1076 -39
- package/tooling/add_engine/autonomy.py +36 -1
- package/tooling/add_engine/constants.py +56 -3
- package/tooling/add_engine/guidelines.py +22 -20
- package/tooling/add_engine/predicates.py +56 -3
- package/tooling/add_engine/search.py +165 -0
- package/tooling/templates/GLOSSARY.md.tmpl +16 -0
- package/tooling/templates/MILESTONE.md.tmpl +9 -0
- package/tooling/templates/PROMPT.persona.md.tmpl +44 -0
- package/tooling/templates/TASK.fast.md.tmpl +2 -1
- package/tooling/templates/TASK.md.tmpl +36 -6
- package/tooling/templates/gitignore.tmpl +12 -0
- 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)
|
|
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
|
|
4
|
-
CLAUDE.md. DYNAMIC-BY-REFERENCE: the block tells the
|
|
5
|
-
read PROJECT.md — it never embeds live state. A closed,
|
|
6
|
-
the cluster-private _INIT_EXCLUDE); transitive-closure AST
|
|
7
|
-
non-cluster add fns. Deps: constants + _atomic_write (io_state)
|
|
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.
|
|
38
|
-
"
|
|
39
|
-
"
|
|
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
|
|
46
|
-
"
|
|
47
|
-
"
|
|
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
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
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
|
|
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
|
-
|
|
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,7 +1,8 @@
|
|
|
1
1
|
# TASK: {{title}}
|
|
2
2
|
|
|
3
3
|
slug: {{slug}} · created: {{date}} · stage: {{stage}}
|
|
4
|
-
|
|
4
|
+
milestone: {{milestone}}
|
|
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. -->
|
|
7
8
|
|