@event4u/agent-config 1.14.0 → 1.16.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 (293) hide show
  1. package/.agent-src/commands/agent-handoff.md +1 -1
  2. package/.agent-src/commands/bug-fix.md +3 -3
  3. package/.agent-src/commands/bug-investigate.md +2 -2
  4. package/.agent-src/commands/chat-history-checkpoint.md +3 -3
  5. package/.agent-src/commands/chat-history-clear.md +2 -2
  6. package/.agent-src/commands/chat-history-resume.md +2 -2
  7. package/.agent-src/commands/chat-history.md +3 -3
  8. package/.agent-src/commands/check-current-md.md +44 -33
  9. package/.agent-src/commands/commit-in-chunks.md +43 -23
  10. package/.agent-src/commands/compress.md +34 -2
  11. package/.agent-src/commands/council-design.md +96 -0
  12. package/.agent-src/commands/council-optimize.md +115 -0
  13. package/.agent-src/commands/council-pr.md +123 -0
  14. package/.agent-src/commands/council.md +219 -0
  15. package/.agent-src/commands/create-pr.md +23 -0
  16. package/.agent-src/commands/do-and-judge.md +3 -3
  17. package/.agent-src/commands/do-in-steps.md +4 -4
  18. package/.agent-src/commands/e2e-heal.md +1 -1
  19. package/.agent-src/commands/e2e-plan.md +1 -1
  20. package/.agent-src/commands/feature-dev.md +8 -0
  21. package/.agent-src/commands/feature-explore.md +6 -1
  22. package/.agent-src/commands/feature-plan.md +33 -2
  23. package/.agent-src/commands/feature-refactor.md +5 -0
  24. package/.agent-src/commands/feature-roadmap.md +8 -3
  25. package/.agent-src/commands/feature.md +58 -0
  26. package/.agent-src/commands/fix-ci.md +5 -0
  27. package/.agent-src/commands/fix-portability.md +7 -2
  28. package/.agent-src/commands/fix-pr-bot-comments.md +5 -0
  29. package/.agent-src/commands/fix-pr-comments.md +5 -0
  30. package/.agent-src/commands/fix-pr-developer-comments.md +5 -0
  31. package/.agent-src/commands/fix-references.md +5 -0
  32. package/.agent-src/commands/fix-seeder.md +5 -0
  33. package/.agent-src/commands/fix.md +60 -0
  34. package/.agent-src/commands/jira-ticket.md +1 -1
  35. package/.agent-src/commands/judge.md +1 -1
  36. package/.agent-src/commands/memory-add.md +3 -3
  37. package/.agent-src/commands/memory-full.md +2 -2
  38. package/.agent-src/commands/memory-promote.md +2 -2
  39. package/.agent-src/commands/mode.md +5 -5
  40. package/.agent-src/commands/onboard.md +17 -8
  41. package/.agent-src/commands/optimize-agents.md +6 -1
  42. package/.agent-src/commands/optimize-augmentignore.md +14 -0
  43. package/.agent-src/commands/optimize-rtk-filters.md +5 -0
  44. package/.agent-src/commands/optimize-skills.md +6 -1
  45. package/.agent-src/commands/optimize.md +54 -0
  46. package/.agent-src/commands/propose-memory.md +2 -2
  47. package/.agent-src/commands/refine-ticket.md +9 -7
  48. package/.agent-src/commands/review-changes.md +61 -9
  49. package/.agent-src/commands/review-routing.md +1 -1
  50. package/.agent-src/commands/roadmap-create.md +42 -4
  51. package/.agent-src/commands/roadmap-execute.md +9 -7
  52. package/.agent-src/commands/set-cost-profile.md +11 -3
  53. package/.agent-src/commands/sync-agent-settings.md +11 -2
  54. package/.agent-src/commands/tests-create.md +1 -1
  55. package/.agent-src/commands/tests-execute.md +2 -3
  56. package/.agent-src/commands/upstream-contribute.md +1 -1
  57. package/.agent-src/contexts/authority/commit-mechanics.md +57 -0
  58. package/.agent-src/contexts/authority/destructive-mechanics.md +66 -0
  59. package/.agent-src/contexts/authority/scope-mechanics.md +87 -0
  60. package/.agent-src/contexts/execution/autonomy-detection.md +54 -0
  61. package/.agent-src/contexts/execution/autonomy-examples.md +90 -0
  62. package/.agent-src/contexts/execution/autonomy-mechanics.md +29 -0
  63. package/.agent-src/contexts/execution/verification-mechanics.md +80 -0
  64. package/.agent-src/personas/README.md +1 -1
  65. package/.agent-src/rules/agent-authority.md +24 -0
  66. package/.agent-src/rules/architecture.md +1 -1
  67. package/.agent-src/rules/artifact-drafting-protocol.md +1 -1
  68. package/.agent-src/rules/artifact-engagement-recording.md +2 -2
  69. package/.agent-src/rules/ask-when-uncertain.md +1 -1
  70. package/.agent-src/rules/augment-portability.md +56 -37
  71. package/.agent-src/rules/autonomous-execution.md +78 -114
  72. package/.agent-src/rules/capture-learnings.md +1 -1
  73. package/.agent-src/rules/chat-history-cadence.md +109 -0
  74. package/.agent-src/rules/chat-history-ownership.md +123 -0
  75. package/.agent-src/rules/chat-history-visibility.md +96 -0
  76. package/.agent-src/rules/cli-output-handling.md +1 -1
  77. package/.agent-src/rules/{command-suggestion.md → command-suggestion-policy.md} +10 -9
  78. package/.agent-src/rules/commit-conventions.md +1 -1
  79. package/.agent-src/rules/commit-policy.md +43 -61
  80. package/.agent-src/rules/context-hygiene.md +3 -3
  81. package/.agent-src/rules/direct-answers.md +2 -2
  82. package/.agent-src/rules/docs-sync.md +1 -1
  83. package/.agent-src/rules/e2e-testing.md +1 -1
  84. package/.agent-src/rules/guidelines.md +4 -4
  85. package/.agent-src/rules/improve-before-implement.md +2 -2
  86. package/.agent-src/rules/language-and-tone.md +41 -96
  87. package/.agent-src/rules/minimal-safe-diff.md +3 -3
  88. package/.agent-src/rules/model-recommendation.md +4 -4
  89. package/.agent-src/rules/no-cheap-questions.md +89 -0
  90. package/.agent-src/rules/non-destructive-by-default.md +25 -59
  91. package/.agent-src/rules/onboarding-gate.md +5 -5
  92. package/.agent-src/rules/review-routing-awareness.md +9 -9
  93. package/.agent-src/rules/roadmap-progress-sync.md +132 -80
  94. package/.agent-src/rules/role-mode-adherence.md +3 -3
  95. package/.agent-src/rules/scope-control.md +65 -46
  96. package/.agent-src/rules/security-sensitive-stop.md +2 -2
  97. package/.agent-src/rules/size-enforcement.md +3 -2
  98. package/.agent-src/rules/think-before-action.md +5 -5
  99. package/.agent-src/rules/token-efficiency.md +4 -4
  100. package/.agent-src/rules/{ui-audit-before-build.md → ui-audit-gate.md} +3 -3
  101. package/.agent-src/rules/user-interaction.md +31 -7
  102. package/.agent-src/rules/verify-before-complete.md +12 -67
  103. package/.agent-src/scripts/update_roadmap_progress.py +65 -8
  104. package/.agent-src/skills/ai-council/SKILL.md +333 -0
  105. package/.agent-src/skills/api-endpoint/SKILL.md +2 -2
  106. package/.agent-src/skills/blade-ui/SKILL.md +30 -11
  107. package/.agent-src/skills/blast-radius-analyzer/SKILL.md +1 -1
  108. package/.agent-src/skills/bug-analyzer/SKILL.md +1 -1
  109. package/.agent-src/skills/command-routing/SKILL.md +1 -1
  110. package/.agent-src/skills/command-writing/SKILL.md +16 -5
  111. package/.agent-src/skills/conventional-commits-writing/SKILL.md +1 -1
  112. package/.agent-src/skills/copilot-agents-optimization/SKILL.md +2 -2
  113. package/.agent-src/skills/developer-like-execution/SKILL.md +2 -2
  114. package/.agent-src/skills/existing-ui-audit/SKILL.md +24 -9
  115. package/.agent-src/skills/fe-design/SKILL.md +20 -15
  116. package/.agent-src/skills/file-editor/SKILL.md +9 -0
  117. package/.agent-src/skills/flux/SKILL.md +1 -1
  118. package/.agent-src/skills/git-workflow/SKILL.md +1 -1
  119. package/.agent-src/skills/guideline-writing/SKILL.md +11 -11
  120. package/.agent-src/skills/learning-to-rule-or-skill/SKILL.md +4 -4
  121. package/.agent-src/skills/livewire/SKILL.md +27 -8
  122. package/.agent-src/skills/override-management/SKILL.md +2 -2
  123. package/.agent-src/skills/php-coder/SKILL.md +1 -1
  124. package/.agent-src/skills/playwright-testing/SKILL.md +2 -2
  125. package/.agent-src/skills/readme-reviewer/SKILL.md +1 -1
  126. package/.agent-src/skills/readme-writing/SKILL.md +1 -1
  127. package/.agent-src/skills/readme-writing-package/SKILL.md +1 -1
  128. package/.agent-src/skills/receiving-code-review/SKILL.md +1 -1
  129. package/.agent-src/skills/refine-ticket/SKILL.md +30 -24
  130. package/.agent-src/skills/review-routing/SKILL.md +2 -2
  131. package/.agent-src/skills/roadmap-management/SKILL.md +22 -16
  132. package/.agent-src/skills/rule-writing/SKILL.md +1 -1
  133. package/.agent-src/skills/skill-reviewer/SKILL.md +1 -1
  134. package/.agent-src/skills/skill-writing/SKILL.md +6 -6
  135. package/.agent-src/skills/subagent-orchestration/SKILL.md +1 -0
  136. package/.agent-src/skills/systematic-debugging/SKILL.md +1 -1
  137. package/.agent-src/skills/upstream-contribute/SKILL.md +3 -3
  138. package/.agent-src/skills/validate-feature-fit/SKILL.md +2 -2
  139. package/.agent-src/skills/{verify-before-complete → verify-completion-evidence}/SKILL.md +2 -2
  140. package/.agent-src/templates/agent-settings.md +9 -9
  141. package/.agent-src/templates/contexts/auth-model.md +1 -1
  142. package/.agent-src/templates/roadmaps.md +9 -8
  143. package/.agent-src/templates/scripts/README.md +2 -2
  144. package/.agent-src/templates/scripts/memory_lookup.py +1 -1
  145. package/.agent-src/templates/scripts/telemetry/aggregator.py +16 -1
  146. package/.agent-src/templates/scripts/telemetry/engagement.py +59 -0
  147. package/.agent-src/templates/scripts/telemetry/report_renderer.py +28 -1
  148. package/.agent-src/templates/scripts/telemetry_record.py +14 -1
  149. package/.agent-src/templates/scripts/work_engine/__init__.py +2 -2
  150. package/.agent-src/templates/scripts/work_engine/cli.py +64 -461
  151. package/.agent-src/templates/scripts/work_engine/cli_args.py +116 -0
  152. package/.agent-src/templates/scripts/work_engine/delivery_state.py +3 -3
  153. package/.agent-src/templates/scripts/work_engine/directives/backend/__init__.py +1 -1
  154. package/.agent-src/templates/scripts/work_engine/directives/backend/implement.py +1 -1
  155. package/.agent-src/templates/scripts/work_engine/directives/backend/memory.py +1 -1
  156. package/.agent-src/templates/scripts/work_engine/directives/backend/plan.py +1 -1
  157. package/.agent-src/templates/scripts/work_engine/directives/backend/report.py +1 -1
  158. package/.agent-src/templates/scripts/work_engine/dispatcher.py +1 -1
  159. package/.agent-src/templates/scripts/work_engine/emitters.py +43 -0
  160. package/.agent-src/templates/scripts/work_engine/errors.py +19 -0
  161. package/.agent-src/templates/scripts/work_engine/hook_bootstrap.py +76 -0
  162. package/.agent-src/templates/scripts/work_engine/input_builders.py +163 -0
  163. package/.agent-src/templates/scripts/work_engine/migration/v0_to_v1.py +34 -2
  164. package/.agent-src/templates/scripts/work_engine/persona_policy.py +1 -1
  165. package/.agent-src/templates/scripts/work_engine/resolvers/prompt.py +1 -1
  166. package/.agent-src/templates/scripts/work_engine/state_io.py +202 -0
  167. package/.claude-plugin/marketplace.json +10 -2
  168. package/AGENTS.md +16 -12
  169. package/CHANGELOG.md +206 -9
  170. package/README.md +51 -52
  171. package/config/agent-settings.template.yml +58 -1
  172. package/config/gitignore-block.txt +3 -0
  173. package/docs/MIGRATION.md +122 -0
  174. package/docs/architecture.md +83 -34
  175. package/docs/catalog.md +331 -0
  176. package/docs/contracts/STABILITY.md +134 -0
  177. package/docs/contracts/adr-chat-history-split.md +132 -0
  178. package/docs/contracts/adr-command-suggestion.md +146 -0
  179. package/docs/contracts/adr-implement-ticket-runtime.md +122 -0
  180. package/docs/contracts/adr-product-ui-track.md +384 -0
  181. package/docs/contracts/adr-prompt-driven-execution.md +187 -0
  182. package/docs/contracts/agent-memory-contract.md +149 -0
  183. package/docs/contracts/artifact-engagement-flow.md +262 -0
  184. package/docs/contracts/command-clusters.md +126 -0
  185. package/docs/contracts/command-suggestion-flow.md +148 -0
  186. package/docs/contracts/implement-ticket-flow.md +628 -0
  187. package/docs/contracts/linear-ai-rules-inclusion.md +143 -0
  188. package/docs/contracts/linear-ai-three-layers.md +131 -0
  189. package/docs/contracts/load-context-schema.md +186 -0
  190. package/docs/contracts/rule-interactions.md +107 -0
  191. package/docs/contracts/rule-interactions.yml +238 -0
  192. package/docs/contracts/rule-priority-hierarchy.md +87 -0
  193. package/docs/contracts/ui-stack-extension.md +236 -0
  194. package/docs/contracts/ui-track-flow.md +338 -0
  195. package/docs/customization.md +14 -0
  196. package/docs/end-to-end-walkthroughs.md +165 -0
  197. package/docs/getting-started.md +27 -9
  198. package/docs/github-topics.md +12 -3
  199. package/docs/guidelines/agent-infra/language-and-tone-examples.md +79 -0
  200. package/{.agent-src → docs}/guidelines/docs/readme-size-and-splitting.md +26 -25
  201. package/docs/guidelines/php/git.md +164 -0
  202. package/docs/installation.md +42 -6
  203. package/docs/migrations/commands-1.15.0.md +112 -0
  204. package/docs/showcase.md +9 -4
  205. package/docs/skills-catalog.md +14 -8
  206. package/docs/ui-track-mental-model.md +121 -0
  207. package/llms.txt +13 -7
  208. package/package.json +1 -1
  209. package/scripts/agent-config +23 -0
  210. package/scripts/ai_council/__init__.py +39 -0
  211. package/scripts/ai_council/_default_prices.py +41 -0
  212. package/scripts/ai_council/_one_off_rebalancing_audit.py +149 -0
  213. package/scripts/ai_council/_one_off_roundtrip.py +106 -0
  214. package/scripts/ai_council/budget_guard.py +172 -0
  215. package/scripts/ai_council/bundler.py +261 -0
  216. package/scripts/ai_council/clients.py +381 -0
  217. package/scripts/ai_council/modes.py +127 -0
  218. package/scripts/ai_council/orchestrator.py +350 -0
  219. package/scripts/ai_council/pricing.py +213 -0
  220. package/scripts/ai_council/project_context.py +159 -0
  221. package/scripts/ai_council/prompts.py +232 -0
  222. package/scripts/ai_council/session.py +144 -0
  223. package/scripts/build_linear_digest.py +4 -4
  224. package/scripts/check_always_budget.py +126 -0
  225. package/scripts/check_augmentignore.py +69 -0
  226. package/scripts/check_command_count_messaging.py +120 -0
  227. package/scripts/check_portability.py +57 -0
  228. package/scripts/check_public_catalog_links.py +122 -0
  229. package/scripts/check_public_links.py +185 -0
  230. package/scripts/check_references.py +5 -1
  231. package/scripts/check_roadmap_trackable.py +111 -0
  232. package/scripts/command_suggester/cooldown.py +1 -1
  233. package/scripts/generate_index.py +266 -0
  234. package/scripts/install_anthropic_key.sh +5 -0
  235. package/scripts/install_openai_key.sh +106 -0
  236. package/scripts/lint_load_context.py +163 -0
  237. package/scripts/lint_no_new_atomic_commands.py +179 -0
  238. package/scripts/lint_rule_interactions.py +149 -0
  239. package/scripts/memory_lookup.py +1 -1
  240. package/scripts/release.py +297 -64
  241. package/scripts/schemas/command.schema.json +20 -0
  242. package/scripts/schemas/rule.schema.json +10 -0
  243. package/scripts/skill_linter.py +26 -4
  244. package/scripts/sync_agent_settings.py +1 -1
  245. package/scripts/update_counts.py +19 -4
  246. package/scripts/update_prices.py +124 -0
  247. package/.agent-src/guidelines/php/git.md +0 -96
  248. package/.agent-src/rules/chat-history.md +0 -200
  249. /package/.agent-src/rules/{slash-commands.md → slash-command-routing-policy.md} +0 -0
  250. /package/{.agent-src → docs}/guidelines/agent-infra/agent-interaction-and-decision-quality.md +0 -0
  251. /package/{.agent-src → docs}/guidelines/agent-infra/break-glass-usage.md +0 -0
  252. /package/{.agent-src → docs}/guidelines/agent-infra/developer-judgment.md +0 -0
  253. /package/{.agent-src → docs}/guidelines/agent-infra/engineering-memory-data-format.md +0 -0
  254. /package/{.agent-src → docs}/guidelines/agent-infra/layered-settings.md +0 -0
  255. /package/{.agent-src → docs}/guidelines/agent-infra/memory-access.md +0 -0
  256. /package/{.agent-src → docs}/guidelines/agent-infra/naming.md +0 -0
  257. /package/{.agent-src → docs}/guidelines/agent-infra/output-patterns.md +0 -0
  258. /package/{.agent-src → docs}/guidelines/agent-infra/review-routing-data-format.md +0 -0
  259. /package/{.agent-src → docs}/guidelines/agent-infra/role-contracts.md +0 -0
  260. /package/{.agent-src → docs}/guidelines/agent-infra/role-mode-router.md +0 -0
  261. /package/{.agent-src → docs}/guidelines/agent-infra/runtime-layer.md +0 -0
  262. /package/{.agent-src → docs}/guidelines/agent-infra/self-improvement-pipeline.md +0 -0
  263. /package/{.agent-src → docs}/guidelines/agent-infra/size-and-scope.md +0 -0
  264. /package/{.agent-src → docs}/guidelines/agent-infra/tool-integration.md +0 -0
  265. /package/{.agent-src → docs}/guidelines/e2e/playwright.md +0 -0
  266. /package/{.agent-src → docs}/guidelines/php/api-design.md +0 -0
  267. /package/{.agent-src → docs}/guidelines/php/artisan-commands.md +0 -0
  268. /package/{.agent-src → docs}/guidelines/php/blade-ui.md +0 -0
  269. /package/{.agent-src → docs}/guidelines/php/controllers.md +0 -0
  270. /package/{.agent-src → docs}/guidelines/php/database.md +0 -0
  271. /package/{.agent-src → docs}/guidelines/php/eloquent.md +0 -0
  272. /package/{.agent-src → docs}/guidelines/php/flux.md +0 -0
  273. /package/{.agent-src → docs}/guidelines/php/general.md +0 -0
  274. /package/{.agent-src → docs}/guidelines/php/jobs.md +0 -0
  275. /package/{.agent-src → docs}/guidelines/php/livewire.md +0 -0
  276. /package/{.agent-src → docs}/guidelines/php/logging.md +0 -0
  277. /package/{.agent-src → docs}/guidelines/php/naming.md +0 -0
  278. /package/{.agent-src → docs}/guidelines/php/patterns/dependency-injection.md +0 -0
  279. /package/{.agent-src → docs}/guidelines/php/patterns/dtos.md +0 -0
  280. /package/{.agent-src → docs}/guidelines/php/patterns/events.md +0 -0
  281. /package/{.agent-src → docs}/guidelines/php/patterns/factory.md +0 -0
  282. /package/{.agent-src → docs}/guidelines/php/patterns/pipelines.md +0 -0
  283. /package/{.agent-src → docs}/guidelines/php/patterns/policies.md +0 -0
  284. /package/{.agent-src → docs}/guidelines/php/patterns/repositories.md +0 -0
  285. /package/{.agent-src → docs}/guidelines/php/patterns/service-layer.md +0 -0
  286. /package/{.agent-src → docs}/guidelines/php/patterns/strategy.md +0 -0
  287. /package/{.agent-src → docs}/guidelines/php/patterns.md +0 -0
  288. /package/{.agent-src → docs}/guidelines/php/performance.md +0 -0
  289. /package/{.agent-src → docs}/guidelines/php/resources.md +0 -0
  290. /package/{.agent-src → docs}/guidelines/php/security.md +0 -0
  291. /package/{.agent-src → docs}/guidelines/php/sql.md +0 -0
  292. /package/{.agent-src → docs}/guidelines/php/validations.md +0 -0
  293. /package/{.agent-src → docs}/guidelines/php/websocket.md +0 -0
@@ -0,0 +1,179 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Atomic-command linter for the command-collapse policy.
4
+
5
+ Reads the locked verb clusters from `docs/contracts/command-clusters.md`,
6
+ finds every command file under `.agent-src.uncompressed/commands/` that
7
+ was **added** since `--baseline` (default: `main`), and requires each
8
+ new file to declare either:
9
+
10
+ - `cluster: <locked-name>` (file is a cluster entry or sub-command), or
11
+ - `superseded_by: <slug>` (file is a deprecation shim).
12
+
13
+ Modifications to pre-existing files are NOT flagged — only additions.
14
+ This stops the atomic surface from growing without forcing every existing
15
+ command into a Phase 1 cluster (most aren't in Phase 1).
16
+
17
+ Exit codes: 0 = clean, 1 = violations found, 3 = internal error.
18
+
19
+ Usage:
20
+ python3 scripts/lint_no_new_atomic_commands.py
21
+ python3 scripts/lint_no_new_atomic_commands.py --baseline origin/main
22
+ python3 scripts/lint_no_new_atomic_commands.py --all # ignore baseline
23
+ """
24
+
25
+ from __future__ import annotations
26
+
27
+ import argparse
28
+ import re
29
+ import subprocess
30
+ import sys
31
+ from dataclasses import dataclass
32
+ from pathlib import Path
33
+
34
+ ROOT = Path(__file__).resolve().parent.parent
35
+ COMMANDS_DIR = Path(".agent-src.uncompressed/commands")
36
+ CLUSTER_CONTRACT = Path("docs/contracts/command-clusters.md")
37
+
38
+
39
+ @dataclass
40
+ class Violation:
41
+ file: str
42
+ reason: str
43
+
44
+
45
+ def load_locked_clusters() -> set[str]:
46
+ """Parse the Phase 1 cluster table from the locked contract."""
47
+ text = (ROOT / CLUSTER_CONTRACT).read_text(encoding="utf-8")
48
+ # Locate the Phase 1 table; cluster names sit in backticks in column 1.
49
+ in_phase_1 = False
50
+ clusters: set[str] = set()
51
+ for line in text.splitlines():
52
+ if line.startswith("## Phase 1 clusters"):
53
+ in_phase_1 = True
54
+ continue
55
+ if in_phase_1 and line.startswith("## "):
56
+ break
57
+ if in_phase_1:
58
+ m = re.match(r"\|\s*`([a-z][a-z0-9-]*)`\s*\|", line)
59
+ if m:
60
+ clusters.add(m.group(1))
61
+ if not clusters:
62
+ print(
63
+ f"❌ Could not parse Phase 1 cluster table from {CLUSTER_CONTRACT}",
64
+ file=sys.stderr,
65
+ )
66
+ sys.exit(3)
67
+ return clusters
68
+
69
+
70
+ def added_command_files(baseline: str) -> list[Path]:
71
+ """Files under commands/ added (status A) since baseline."""
72
+ try:
73
+ result = subprocess.run(
74
+ ["git", "diff", "--name-only", "--diff-filter=A",
75
+ f"{baseline}...HEAD", "--", str(COMMANDS_DIR)],
76
+ capture_output=True, text=True, cwd=ROOT, timeout=15,
77
+ )
78
+ except (FileNotFoundError, subprocess.TimeoutExpired) as exc:
79
+ print(f"❌ git diff failed: {exc}", file=sys.stderr)
80
+ sys.exit(3)
81
+ if result.returncode != 0:
82
+ print(f"❌ git diff exit {result.returncode}: {result.stderr}",
83
+ file=sys.stderr)
84
+ sys.exit(3)
85
+ files = [Path(p) for p in result.stdout.splitlines()
86
+ if p.endswith(".md") and p != ""]
87
+ # Also include untracked (newly added, uncommitted) files.
88
+ try:
89
+ wt = subprocess.run(
90
+ ["git", "status", "--porcelain", "--", str(COMMANDS_DIR)],
91
+ capture_output=True, text=True, cwd=ROOT, timeout=10,
92
+ )
93
+ for line in wt.stdout.splitlines():
94
+ if len(line) < 4:
95
+ continue
96
+ status = line[:2]
97
+ if status.strip() not in ("A", "??", "AM"):
98
+ continue
99
+ path = line[3:].strip().split(" -> ")[-1]
100
+ if path.endswith(".md"):
101
+ p = Path(path)
102
+ if p not in files:
103
+ files.append(p)
104
+ except (FileNotFoundError, subprocess.TimeoutExpired):
105
+ pass
106
+ return files
107
+
108
+
109
+ def all_command_files() -> list[Path]:
110
+ return sorted((ROOT / COMMANDS_DIR).glob("*.md"))
111
+
112
+
113
+ def parse_frontmatter(path: Path) -> dict[str, str]:
114
+ text = path.read_text(encoding="utf-8")
115
+ if not text.startswith("---"):
116
+ return {}
117
+ end = text.find("\n---", 3)
118
+ if end == -1:
119
+ return {}
120
+ fm: dict[str, str] = {}
121
+ for line in text[3:end].splitlines():
122
+ if ":" in line:
123
+ k, _, v = line.partition(":")
124
+ fm[k.strip()] = v.strip()
125
+ return fm
126
+
127
+
128
+ def check_file(path: Path, clusters: set[str]) -> Violation | None:
129
+ abs_path = path if path.is_absolute() else ROOT / path
130
+ if not abs_path.exists():
131
+ return None # deleted file, nothing to check
132
+ fm = parse_frontmatter(abs_path)
133
+ if "superseded_by" in fm:
134
+ return None # shim — exempt
135
+ cluster = fm.get("cluster")
136
+ if not cluster:
137
+ return Violation(str(path),
138
+ "missing `cluster:` frontmatter "
139
+ f"(allowed: {sorted(clusters)})")
140
+ if cluster not in clusters:
141
+ return Violation(str(path),
142
+ f"`cluster: {cluster}` is not a locked cluster "
143
+ f"(allowed: {sorted(clusters)})")
144
+ return None
145
+
146
+
147
+ def main() -> int:
148
+ ap = argparse.ArgumentParser(description=__doc__)
149
+ ap.add_argument("--baseline", default="main",
150
+ help="git ref to diff against (default: main)")
151
+ ap.add_argument("--all", action="store_true",
152
+ help="check every command file, not just changed ones")
153
+ args = ap.parse_args()
154
+
155
+ clusters = load_locked_clusters()
156
+ targets = (all_command_files() if args.all
157
+ else added_command_files(args.baseline))
158
+ if not targets:
159
+ print(f"✅ No new commands added under {COMMANDS_DIR} "
160
+ f"(baseline: {args.baseline}).")
161
+ return 0
162
+
163
+ violations = [v for v in (check_file(p, clusters) for p in targets)
164
+ if v is not None]
165
+ if violations:
166
+ print(f"❌ {len(violations)} newly-added atomic command(s) violate "
167
+ f"the command-cluster policy:")
168
+ for v in violations:
169
+ print(f" • {v.file} — {v.reason}")
170
+ print(f"\nSee docs/contracts/command-clusters.md for the locked "
171
+ f"cluster names and frontmatter contract.")
172
+ return 1
173
+ print(f"✅ {len(targets)} newly-added command(s) all declare a valid "
174
+ f"`cluster:` (or `superseded_by:`).")
175
+ return 0
176
+
177
+
178
+ if __name__ == "__main__":
179
+ sys.exit(main())
@@ -0,0 +1,149 @@
1
+ #!/usr/bin/env python3
2
+ """Lint docs/contracts/rule-interactions.yml.
3
+
4
+ Validates:
5
+ - Schema (required fields per pair)
6
+ - All rule slugs in `rules:` exist as `.agent-src.uncompressed/rules/<slug>.md`
7
+ - Every pair references rules listed in the top-level `rules:` block
8
+ - `relation` is one of the allowed values
9
+ - All `evidence:` entries point at real files (anchors are advisory, not checked)
10
+ - Pair `id`s are unique
11
+ - The anchor pair from `road-to-post-pr29-optimize.md` Phase 2 is present:
12
+ `non-destructive-by-default` × {autonomous-execution, scope-control,
13
+ commit-policy, ask-when-uncertain, verify-before-complete}.
14
+
15
+ Exits non-zero on any failure. Used in CI via Taskfile target
16
+ `lint-rule-interactions`.
17
+ """
18
+ from __future__ import annotations
19
+
20
+ import sys
21
+ from pathlib import Path
22
+
23
+ import yaml
24
+
25
+ ROOT = Path(__file__).resolve().parent.parent
26
+ MATRIX = ROOT / "docs" / "contracts" / "rule-interactions.yml"
27
+ RULES_DIR = ROOT / ".agent-src.uncompressed" / "rules"
28
+
29
+ ALLOWED_RELATIONS = {
30
+ "overrides",
31
+ "narrows",
32
+ "defers_to",
33
+ "restates",
34
+ "gates",
35
+ "complements",
36
+ }
37
+
38
+ REQUIRED_PAIR_FIELDS = {"id", "rules", "relation", "conflict", "resolution", "evidence"}
39
+
40
+ ANCHOR_PARTNERS = {
41
+ "autonomous-execution",
42
+ "scope-control",
43
+ "commit-policy",
44
+ "ask-when-uncertain",
45
+ "verify-before-complete",
46
+ }
47
+ ANCHOR_RULE = "non-destructive-by-default"
48
+
49
+
50
+ def fail(errors: list[str]) -> None:
51
+ print(f"❌ rule-interactions.yml — {len(errors)} issue(s):")
52
+ for e in errors:
53
+ print(f" • {e}")
54
+ sys.exit(1)
55
+
56
+
57
+ def main() -> int:
58
+ if not MATRIX.exists():
59
+ fail([f"{MATRIX.relative_to(ROOT)} is missing"])
60
+
61
+ data = yaml.safe_load(MATRIX.read_text())
62
+ errors: list[str] = []
63
+
64
+ if not isinstance(data, dict):
65
+ fail(["top-level YAML must be a mapping"])
66
+
67
+ if data.get("version") != 1:
68
+ errors.append("version must be 1")
69
+
70
+ declared_rules = data.get("rules") or []
71
+ if not isinstance(declared_rules, list) or not declared_rules:
72
+ errors.append("`rules:` must be a non-empty list of slugs")
73
+
74
+ for slug in declared_rules:
75
+ if not isinstance(slug, str):
76
+ errors.append(f"rule slug not a string: {slug!r}")
77
+ continue
78
+ rule_path = RULES_DIR / f"{slug}.md"
79
+ if not rule_path.exists():
80
+ errors.append(f"rule slug `{slug}` has no file at {rule_path.relative_to(ROOT)}")
81
+
82
+ pairs = data.get("pairs") or []
83
+ if not isinstance(pairs, list) or not pairs:
84
+ errors.append("`pairs:` must be a non-empty list")
85
+
86
+ seen_ids: set[str] = set()
87
+ declared_set = set(declared_rules) if isinstance(declared_rules, list) else set()
88
+ anchor_partners_seen: set[str] = set()
89
+
90
+ for idx, pair in enumerate(pairs):
91
+ if not isinstance(pair, dict):
92
+ errors.append(f"pair[{idx}] is not a mapping")
93
+ continue
94
+ missing = REQUIRED_PAIR_FIELDS - set(pair)
95
+ if missing:
96
+ errors.append(f"pair[{idx}] missing fields: {sorted(missing)}")
97
+ continue
98
+
99
+ pid = pair["id"]
100
+ if pid in seen_ids:
101
+ errors.append(f"duplicate pair id: {pid}")
102
+ seen_ids.add(pid)
103
+
104
+ rules_pair = pair["rules"]
105
+ if not (isinstance(rules_pair, list) and len(rules_pair) == 2):
106
+ errors.append(f"pair `{pid}` rules must be a 2-element list")
107
+ continue
108
+ for r in rules_pair:
109
+ if r not in declared_set:
110
+ errors.append(f"pair `{pid}` references undeclared rule `{r}`")
111
+
112
+ if pair["relation"] not in ALLOWED_RELATIONS:
113
+ errors.append(
114
+ f"pair `{pid}` relation `{pair['relation']}` not in {sorted(ALLOWED_RELATIONS)}"
115
+ )
116
+
117
+ evidence = pair.get("evidence") or []
118
+ if not isinstance(evidence, list) or not evidence:
119
+ errors.append(f"pair `{pid}` evidence must be a non-empty list")
120
+ for citation in evidence:
121
+ if not isinstance(citation, str):
122
+ errors.append(f"pair `{pid}` evidence item not a string: {citation!r}")
123
+ continue
124
+ file_part = citation.split("#", 1)[0]
125
+ if not (ROOT / file_part).exists():
126
+ errors.append(f"pair `{pid}` evidence path does not exist: {file_part}")
127
+
128
+ # Anchor coverage check
129
+ if ANCHOR_RULE in rules_pair:
130
+ partner = next((r for r in rules_pair if r != ANCHOR_RULE), None)
131
+ if partner in ANCHOR_PARTNERS:
132
+ anchor_partners_seen.add(partner)
133
+
134
+ missing_anchors = ANCHOR_PARTNERS - anchor_partners_seen
135
+ if missing_anchors:
136
+ errors.append(
137
+ f"anchor pairs missing for `{ANCHOR_RULE}` × {sorted(missing_anchors)} "
138
+ "(required by road-to-post-pr29-optimize.md P2.2)"
139
+ )
140
+
141
+ if errors:
142
+ fail(errors)
143
+
144
+ print(f"✅ rule-interactions.yml clean — {len(declared_rules)} rules, {len(pairs)} pairs.")
145
+ return 0
146
+
147
+
148
+ if __name__ == "__main__":
149
+ sys.exit(main())
@@ -246,7 +246,7 @@ def _apply_conflict_rule(
246
246
  # says retrieval should route through `@event4u/agent-memory`. The package
247
247
  # CLI is purely **semantic** (`memory retrieve <query> --type T …`); the
248
248
  # shared `retrieve(types, keys, …)` API is **key-based**. The hybrid
249
- # resolution agreed in `agents/contexts/agent-memory-contract.md` synthesises
249
+ # resolution agreed in `docs/contracts/agent-memory-contract.md` synthesises
250
250
  # `keys` into a single natural-language query for the package call, while
251
251
  # the file fallback continues to do glob/substring matching on the same
252
252
  # keys. Both legs land in the same `Hit` shape so the conflict rule can