@event4u/agent-config 1.19.0 → 1.21.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 (297) hide show
  1. package/.agent-src/commands/agent-handoff.md +14 -10
  2. package/.agent-src/commands/agents.md +1 -1
  3. package/.agent-src/commands/bug-fix.md +1 -1
  4. package/.agent-src/commands/bug-investigate.md +2 -2
  5. package/.agent-src/commands/chat-history/import.md +166 -0
  6. package/.agent-src/commands/chat-history/learn.md +178 -0
  7. package/.agent-src/commands/chat-history/show.md +17 -18
  8. package/.agent-src/commands/chat-history.md +26 -25
  9. package/.agent-src/commands/compress.md +12 -0
  10. package/.agent-src/commands/context/create.md +2 -2
  11. package/.agent-src/commands/context.md +1 -1
  12. package/.agent-src/commands/copilot-agents.md +1 -1
  13. package/.agent-src/commands/council/default.md +21 -12
  14. package/.agent-src/commands/council.md +1 -1
  15. package/.agent-src/commands/create-pr.md +28 -8
  16. package/.agent-src/commands/e2e-heal.md +1 -1
  17. package/.agent-src/commands/e2e-plan.md +1 -1
  18. package/.agent-src/commands/feature/dev.md +3 -3
  19. package/.agent-src/commands/feature.md +1 -1
  20. package/.agent-src/commands/fix/seeder.md +2 -2
  21. package/.agent-src/commands/fix.md +1 -1
  22. package/.agent-src/commands/jira-ticket.md +1 -1
  23. package/.agent-src/commands/judge.md +2 -2
  24. package/.agent-src/commands/memory.md +1 -1
  25. package/.agent-src/commands/mode.md +5 -5
  26. package/.agent-src/commands/module.md +1 -1
  27. package/.agent-src/commands/onboard.md +4 -4
  28. package/.agent-src/commands/optimize/augmentignore.md +1 -1
  29. package/.agent-src/commands/optimize-prompt.md +61 -0
  30. package/.agent-src/commands/optimize.md +1 -1
  31. package/.agent-src/commands/override.md +1 -1
  32. package/.agent-src/commands/review-changes.md +1 -1
  33. package/.agent-src/commands/review-routing.md +1 -1
  34. package/.agent-src/commands/roadmap.md +1 -1
  35. package/.agent-src/commands/set-cost-profile.md +3 -3
  36. package/.agent-src/commands/sync-agent-settings.md +2 -2
  37. package/.agent-src/commands/sync-gitignore.md +1 -1
  38. package/.agent-src/commands/tests/create.md +2 -2
  39. package/.agent-src/commands/tests.md +1 -1
  40. package/.agent-src/commands/threat-model.md +4 -4
  41. package/.agent-src/contexts/authority/commit-mechanics.md +14 -1
  42. package/.agent-src/contexts/authority/destructive-mechanics.md +14 -1
  43. package/.agent-src/contexts/authority/scope-mechanics.md +5 -0
  44. package/.agent-src/contexts/communication/rules-auto/guidelines-mechanics.md +76 -0
  45. package/.agent-src/contexts/communication/rules-auto/skill-quality-mechanics.md +76 -0
  46. package/.agent-src/contexts/communication/rules-auto/slash-command-routing-policy-mechanics.md +4 -4
  47. package/.agent-src/contexts/communication/rules-auto/think-before-action-mechanics.md +98 -0
  48. package/.agent-src/contexts/communication/rules-auto/token-efficiency-mechanics.md +93 -0
  49. package/.agent-src/contexts/communication/rules-auto/user-interaction-mechanics.md +125 -9
  50. package/.agent-src/contexts/execution/autonomy-mechanics.md +44 -0
  51. package/.agent-src/contexts/model-recommendations.md +2 -2
  52. package/.agent-src/contexts/override-system.md +1 -1
  53. package/.agent-src/personas/product-owner.md +2 -2
  54. package/.agent-src/personas/qa.md +1 -1
  55. package/.agent-src/rules/agent-authority.md +5 -6
  56. package/.agent-src/rules/agent-docs.md +11 -53
  57. package/.agent-src/rules/analysis-skill-routing.md +10 -40
  58. package/.agent-src/rules/architecture.md +6 -1
  59. package/.agent-src/rules/artifact-drafting-protocol.md +5 -0
  60. package/.agent-src/rules/artifact-engagement-recording.md +23 -59
  61. package/.agent-src/rules/ask-when-uncertain.md +24 -47
  62. package/.agent-src/rules/augment-portability.md +14 -62
  63. package/.agent-src/rules/augment-source-of-truth.md +10 -1
  64. package/.agent-src/rules/autonomous-execution.md +17 -98
  65. package/.agent-src/rules/capture-learnings.md +9 -80
  66. package/.agent-src/rules/cli-output-handling.md +12 -42
  67. package/.agent-src/rules/command-suggestion-policy.md +25 -73
  68. package/.agent-src/rules/commit-conventions.md +9 -58
  69. package/.agent-src/rules/commit-policy.md +16 -47
  70. package/.agent-src/rules/context-hygiene.md +5 -0
  71. package/.agent-src/rules/direct-answers.md +21 -42
  72. package/.agent-src/rules/docker-commands.md +11 -45
  73. package/.agent-src/rules/docs-sync.md +10 -56
  74. package/.agent-src/rules/downstream-changes.md +5 -0
  75. package/.agent-src/rules/e2e-testing.md +9 -44
  76. package/.agent-src/rules/guidelines.md +13 -75
  77. package/.agent-src/rules/improve-before-implement.md +10 -2
  78. package/.agent-src/rules/language-and-tone.md +35 -69
  79. package/.agent-src/rules/laravel-translations.md +11 -40
  80. package/.agent-src/rules/markdown-safe-codeblocks.md +4 -0
  81. package/.agent-src/rules/minimal-safe-diff.md +4 -0
  82. package/.agent-src/rules/missing-tool-handling.md +4 -0
  83. package/.agent-src/rules/model-recommendation.md +9 -61
  84. package/.agent-src/rules/no-attribution-footers.md +53 -0
  85. package/.agent-src/rules/no-cheap-questions.md +11 -27
  86. package/.agent-src/rules/no-council-references.md +76 -0
  87. package/.agent-src/rules/no-roadmap-references.md +8 -1
  88. package/.agent-src/rules/non-destructive-by-default.md +13 -43
  89. package/.agent-src/rules/onboarding-gate.md +9 -117
  90. package/.agent-src/rules/package-ci-checks.md +10 -37
  91. package/.agent-src/rules/php-coding.md +10 -55
  92. package/.agent-src/rules/preservation-guard.md +9 -0
  93. package/.agent-src/rules/review-routing-awareness.md +9 -97
  94. package/.agent-src/rules/reviewer-awareness.md +8 -83
  95. package/.agent-src/rules/roadmap-progress-sync.md +7 -170
  96. package/.agent-src/rules/role-mode-adherence.md +6 -2
  97. package/.agent-src/rules/rule-type-governance.md +8 -66
  98. package/.agent-src/rules/runtime-safety.md +5 -0
  99. package/.agent-src/rules/scope-control.md +17 -62
  100. package/.agent-src/rules/security-sensitive-stop.md +7 -1
  101. package/.agent-src/rules/size-enforcement.md +6 -1
  102. package/.agent-src/rules/skill-improvement-trigger.md +9 -49
  103. package/.agent-src/rules/skill-quality.md +7 -64
  104. package/.agent-src/rules/slash-command-routing-policy.md +11 -63
  105. package/.agent-src/rules/think-before-action.md +22 -87
  106. package/.agent-src/rules/token-efficiency.md +10 -74
  107. package/.agent-src/rules/token-optimizer-maintenance.md +68 -0
  108. package/.agent-src/rules/tool-safety.md +4 -0
  109. package/.agent-src/rules/ui-audit-gate.md +25 -61
  110. package/.agent-src/rules/upstream-proposal.md +9 -67
  111. package/.agent-src/rules/user-interaction.md +25 -95
  112. package/.agent-src/rules/verify-before-complete.md +1 -1
  113. package/.agent-src/skills/agent-docs-writing/SKILL.md +1 -1
  114. package/.agent-src/skills/ai-council/SKILL.md +69 -5
  115. package/.agent-src/skills/analysis-autonomous-mode/SKILL.md +1 -1
  116. package/.agent-src/skills/analysis-skill-router/SKILL.md +3 -3
  117. package/.agent-src/skills/artisan-commands/SKILL.md +2 -2
  118. package/.agent-src/skills/authz-review/SKILL.md +1 -1
  119. package/.agent-src/skills/aws-infrastructure/SKILL.md +5 -5
  120. package/.agent-src/skills/blast-radius-analyzer/SKILL.md +8 -8
  121. package/.agent-src/skills/bug-analyzer/SKILL.md +5 -5
  122. package/.agent-src/skills/code-refactoring/SKILL.md +4 -4
  123. package/.agent-src/skills/code-review/SKILL.md +2 -2
  124. package/.agent-src/skills/command-writing/SKILL.md +11 -0
  125. package/.agent-src/skills/composer-packages/SKILL.md +2 -2
  126. package/.agent-src/skills/context-authoring/SKILL.md +11 -0
  127. package/.agent-src/skills/context-document/SKILL.md +1 -1
  128. package/.agent-src/skills/copilot-agents-optimization/SKILL.md +23 -0
  129. package/.agent-src/skills/copilot-config/SKILL.md +1 -1
  130. package/.agent-src/skills/dcf-modeling/SKILL.md +89 -0
  131. package/.agent-src/skills/dependency-upgrade/SKILL.md +2 -2
  132. package/.agent-src/skills/devcontainer/SKILL.md +2 -2
  133. package/.agent-src/skills/developer-like-execution/SKILL.md +1 -1
  134. package/.agent-src/skills/docker/SKILL.md +1 -1
  135. package/.agent-src/skills/dto-creator/SKILL.md +1 -1
  136. package/.agent-src/skills/estimate-ticket/SKILL.md +2 -2
  137. package/.agent-src/skills/fe-design/SKILL.md +4 -4
  138. package/.agent-src/skills/feature-planning/SKILL.md +5 -5
  139. package/.agent-src/skills/funnel-analysis/SKILL.md +100 -0
  140. package/.agent-src/skills/laravel/SKILL.md +1 -1
  141. package/.agent-src/skills/laravel-notifications/SKILL.md +5 -5
  142. package/.agent-src/skills/laravel-pennant/SKILL.md +1 -1
  143. package/.agent-src/skills/laravel-pulse/SKILL.md +4 -4
  144. package/.agent-src/skills/laravel-reverb/SKILL.md +2 -2
  145. package/.agent-src/skills/laravel-scheduling/SKILL.md +1 -1
  146. package/.agent-src/skills/md-language-check/SKILL.md +1 -1
  147. package/.agent-src/skills/migration-creator/SKILL.md +7 -7
  148. package/.agent-src/skills/multi-tenancy/SKILL.md +8 -8
  149. package/.agent-src/skills/okr-tree-modeling/SKILL.md +93 -0
  150. package/.agent-src/skills/performance-analysis/SKILL.md +3 -3
  151. package/.agent-src/skills/pest-testing/SKILL.md +6 -6
  152. package/.agent-src/skills/php-service/SKILL.md +2 -2
  153. package/.agent-src/skills/project-analysis-hypothesis-driven/SKILL.md +3 -3
  154. package/.agent-src/skills/project-analysis-react/SKILL.md +1 -1
  155. package/.agent-src/skills/project-analysis-symfony/SKILL.md +1 -1
  156. package/.agent-src/skills/project-analysis-zend-laminas/SKILL.md +2 -2
  157. package/.agent-src/skills/project-analyzer/SKILL.md +4 -4
  158. package/.agent-src/skills/prompt-optimizer/SKILL.md +108 -0
  159. package/.agent-src/skills/readme-reviewer/SKILL.md +1 -1
  160. package/.agent-src/skills/rice-prioritization/SKILL.md +100 -0
  161. package/.agent-src/skills/rule-writing/SKILL.md +33 -0
  162. package/.agent-src/skills/sentry-integration/SKILL.md +1 -1
  163. package/.agent-src/skills/skill-writing/SKILL.md +14 -0
  164. package/.agent-src/skills/subagent-orchestration/SKILL.md +34 -2
  165. package/.agent-src/skills/terraform/SKILL.md +2 -2
  166. package/.agent-src/skills/terragrunt/SKILL.md +8 -8
  167. package/.agent-src/skills/test-performance/SKILL.md +5 -5
  168. package/.agent-src/skills/threat-modeling/SKILL.md +2 -2
  169. package/.agent-src/skills/token-optimizer/SKILL.md +110 -0
  170. package/.agent-src/skills/unit-economics-modeling/SKILL.md +104 -0
  171. package/.agent-src/skills/universal-project-analysis/SKILL.md +1 -1
  172. package/.agent-src/skills/using-git-worktrees/SKILL.md +1 -0
  173. package/.agent-src/templates/AGENTS.md +1 -1
  174. package/.agent-src/templates/agent-settings.md +25 -41
  175. package/.agent-src/templates/contexts/tenant-boundaries.md +2 -2
  176. package/.agent-src/templates/contexts.md +1 -1
  177. package/.agent-src/templates/copilot-instructions.md +21 -0
  178. package/.agent-src/templates/copilot-review-instructions.md +76 -0
  179. package/.agent-src/templates/features.md +1 -1
  180. package/.agent-src/templates/rule.md +127 -0
  181. package/.agent-src/templates/scripts/work_engine/hook_bootstrap.py +7 -5
  182. package/.agent-src/templates/scripts/work_engine/hooks/__init__.py +0 -4
  183. package/.agent-src/templates/scripts/work_engine/hooks/builtin/__init__.py +0 -4
  184. package/.agent-src/templates/scripts/work_engine/hooks/builtin/_chat_history_base.py +7 -51
  185. package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_append.py +1 -2
  186. package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_halt_append.py +1 -2
  187. package/.agent-src/templates/scripts/work_engine/hooks/builtin/memory_visibility.py +2 -3
  188. package/.agent-src/templates/skill.md +30 -1
  189. package/.claude-plugin/marketplace.json +11 -4
  190. package/AGENTS.md +71 -3
  191. package/CHANGELOG.md +180 -3
  192. package/README.md +24 -23
  193. package/config/agent-settings.template.yml +63 -23
  194. package/config/gitignore-block.txt +11 -4
  195. package/docs/architecture.md +84 -3
  196. package/docs/catalog.md +23 -11
  197. package/docs/contracts/adr-chat-history-split.md +10 -1
  198. package/docs/contracts/agent-memory-contract.md +1 -1
  199. package/docs/contracts/command-clusters.md +1 -1
  200. package/docs/contracts/context-paths.md +2 -1
  201. package/docs/contracts/cross-wing-handoff.md +133 -0
  202. package/docs/contracts/file-ownership-matrix.json +678 -609
  203. package/docs/contracts/hook-architecture-v1.md +8 -1
  204. package/docs/contracts/iron-law-overrides.txt +25 -0
  205. package/docs/contracts/kernel-membership.md +273 -0
  206. package/docs/contracts/load-context-schema.md +26 -11
  207. package/docs/contracts/memory-visibility-v1.md +8 -24
  208. package/docs/contracts/pilot/agent-authority.md +24 -0
  209. package/docs/contracts/pilot/direct-answers.md +70 -0
  210. package/docs/contracts/pilot/language-and-tone.md +63 -0
  211. package/docs/contracts/rule-classification.md +170 -0
  212. package/docs/contracts/rule-router.md +153 -0
  213. package/docs/customization.md +18 -7
  214. package/docs/decisions/ADR-001-kernel-swap-deferred.md +109 -0
  215. package/docs/decisions/ADR-002-kernel-bucket-overrides.md +124 -0
  216. package/docs/decisions/ADR-rule-kernel-and-router.md +122 -0
  217. package/docs/getting-started.md +19 -27
  218. package/docs/guidelines/agent-infra/ask-when-uncertain-demos.md +1 -1
  219. package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +176 -0
  220. package/docs/guidelines/agent-infra/rule-type-governance.md +73 -0
  221. package/docs/guidelines/agent-infra/size-and-scope.md +13 -2
  222. package/docs/guidelines/agent-infra/skill-quality-checklist.md +119 -0
  223. package/docs/guidelines/augment-portability-patterns.md +68 -0
  224. package/docs/guidelines/php/php-coding-patterns.md +62 -0
  225. package/docs/hook-payload-capture.md +221 -0
  226. package/docs/migrations/commands-1.15.0.md +17 -12
  227. package/docs/skills-catalog.md +5 -4
  228. package/llms.txt +4 -3
  229. package/package.json +1 -1
  230. package/scripts/_p43_bodies.py +235 -0
  231. package/scripts/_p43_compress.py +118 -0
  232. package/scripts/_p4_migrate.py +199 -0
  233. package/scripts/_pilot_council_question.py +57 -0
  234. package/scripts/_pilot_measure.py +53 -0
  235. package/scripts/agent-config +1 -1
  236. package/scripts/ai_council/_default_prices.py +4 -4
  237. package/scripts/ai_council/clients.py +1 -1
  238. package/scripts/ai_council/modes.py +3 -4
  239. package/scripts/ai_council/pricing.py +10 -9
  240. package/scripts/ai_council/session.py +107 -5
  241. package/scripts/build_linear_digest.py +3 -5
  242. package/scripts/build_rule_trigger_matrix.py +1 -9
  243. package/scripts/chat_history.py +952 -596
  244. package/scripts/check_always_budget.py +39 -6
  245. package/scripts/check_compressed_paths.py +213 -0
  246. package/scripts/check_compression.py +15 -0
  247. package/scripts/check_context_paths.py +1 -0
  248. package/scripts/check_council_layout.py +105 -0
  249. package/scripts/check_council_references.py +145 -0
  250. package/scripts/check_portability.py +2 -0
  251. package/scripts/check_references.py +14 -2
  252. package/scripts/check_token_optimizer_freshness.py +131 -0
  253. package/scripts/compile_router.py +148 -0
  254. package/scripts/compress.py +219 -11
  255. package/scripts/council_cli.py +63 -9
  256. package/scripts/council_prune.py +81 -0
  257. package/scripts/count_token_optimizer_usage.sh +54 -0
  258. package/scripts/hook_manifest.yaml +33 -0
  259. package/scripts/hooks/augment-chat-history.sh +10 -0
  260. package/scripts/hooks/cowork-dispatcher.sh +98 -0
  261. package/scripts/hooks/dispatch_hook.py +35 -0
  262. package/scripts/hooks_status.py +12 -1
  263. package/scripts/install-hooks.sh +2 -2
  264. package/scripts/install.sh +81 -2
  265. package/scripts/iron_law_sha.py +98 -0
  266. package/scripts/lint_handoffs.py +214 -0
  267. package/scripts/lint_hook_manifest.py +2 -1
  268. package/scripts/lint_load_context.py +35 -5
  269. package/scripts/measure_rule_budget.py +314 -0
  270. package/scripts/prototype_lint_contradictions.py +150 -0
  271. package/scripts/redact_hook_capture.py +148 -0
  272. package/scripts/schemas/rule.schema.json +55 -6
  273. package/scripts/schemas/skill.schema.json +5 -0
  274. package/scripts/skill_linter.py +359 -7
  275. package/scripts/smoke_path_resolution.py +93 -0
  276. package/scripts/update_prices.py +3 -3
  277. package/scripts/validate_frontmatter.py +41 -1
  278. package/.agent-src/commands/chat-history/checkpoint.md +0 -126
  279. package/.agent-src/commands/chat-history/clear.md +0 -103
  280. package/.agent-src/commands/chat-history/resume.md +0 -183
  281. package/.agent-src/contexts/communication/rules-auto/artifact-engagement-recording-mechanics.md +0 -72
  282. package/.agent-src/contexts/communication/rules-auto/augment-portability-mechanics.md +0 -79
  283. package/.agent-src/contexts/communication/rules-auto/cli-output-handling-mechanics.md +0 -87
  284. package/.agent-src/contexts/communication/rules-auto/command-suggestion-policy-mechanics.md +0 -62
  285. package/.agent-src/contexts/communication/rules-auto/docs-sync-mechanics.md +0 -78
  286. package/.agent-src/contexts/communication/rules-auto/package-ci-checks-mechanics.md +0 -85
  287. package/.agent-src/contexts/communication/rules-auto/review-routing-awareness-mechanics.md +0 -65
  288. package/.agent-src/contexts/communication/rules-auto/roadmap-progress-sync-mechanics.md +0 -78
  289. package/.agent-src/contexts/communication/rules-auto/ui-audit-gate-mechanics.md +0 -53
  290. package/.agent-src/rules/chat-history-cadence.md +0 -143
  291. package/.agent-src/rules/chat-history-ownership.md +0 -124
  292. package/.agent-src/rules/chat-history-visibility.md +0 -97
  293. package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_heartbeat.py +0 -50
  294. package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_turn_check.py +0 -49
  295. package/scripts/check_phase_coupling.py +0 -148
  296. /package/{docs → .agent-src/contexts}/contracts/artifact-engagement-flow.md +0 -0
  297. /package/{docs → .agent-src/contexts}/contracts/command-suggestion-flow.md +0 -0
@@ -47,6 +47,26 @@ FAIL_THRESHOLD = 0.90
47
47
  CONCENTRATION_SINGLE_PCT = 0.12
48
48
  CONCENTRATION_TOP3_PCT = 0.30
49
49
 
50
+ # Transitional concentration allowlist — non-safety-floor rules whose
51
+ # extended share exceeds CONCENTRATION_SINGLE_PCT after the kernel-trim
52
+ # refactor (commit 4e771da `refactor(kernel): compress 8 kernel rules
53
+ # per P2.2 playbook + lock kernel`). Trimming safety-floor rules shrank
54
+ # the denominator, mechanically lifting non-floor rules' percentage
55
+ # share even though their absolute size did not grow. Each entry pins
56
+ # the measured extended-size ceiling at the day road-to-path-fixes was
57
+ # closed; growth above the ceiling regresses CI. Future kernel-aware
58
+ # trimming work retires entries here.
59
+ KNOWN_CONCENTRATION_BREACHES: dict[str, int] = {
60
+ "language-and-tone.md": 3_985,
61
+ "no-cheap-questions.md": 3_530,
62
+ }
63
+ # Top-3 non-floor concentration ceiling — same rationale as the
64
+ # per-rule allowlist above. The current top-3 sum (language-and-tone +
65
+ # scope-control-allowlisted + non-destructive-allowlisted) clears the
66
+ # 30 % cap; the entry below pins the measured ceiling. Future trim
67
+ # work drops this back to None (default 30 %).
68
+ KNOWN_TOP3_CONCENTRATION_CEILING: int | None = 10_900
69
+
50
70
  # Q3=A locked safety-floor rules — out of scope for slimming and for the
51
71
  # concentration check. Their size is intentional (Iron Laws + obligation
52
72
  # surface), not drift. See road-to-structural-optimization Phase 5.
@@ -221,6 +241,12 @@ def _concentration_check(
221
241
  Returns (single-rule breaches, top-3 breach or None). Q3=A locked
222
242
  safety-floor rules are excluded from both numerator and the top-3
223
243
  selection — their size is intentional, not drift.
244
+
245
+ Allowlisted rules in `KNOWN_CONCENTRATION_BREACHES` are exempted
246
+ from the per-rule cap as long as their extended size does not
247
+ exceed the recorded ceiling (regression guard). The top-3 cap is
248
+ relaxed to `KNOWN_TOP3_CONCENTRATION_CEILING` while that ceiling
249
+ is non-None.
224
250
  """
225
251
  non_floor = [
226
252
  (name, raw, ext) for name, raw, ext in sizes
@@ -229,15 +255,22 @@ def _concentration_check(
229
255
  single_cap = total_ext * CONCENTRATION_SINGLE_PCT
230
256
  top3_cap = total_ext * CONCENTRATION_TOP3_PCT
231
257
 
232
- single_breaches = [
233
- (name, ext, ext / total_ext)
234
- for name, _, ext in non_floor
235
- if ext > single_cap
236
- ]
258
+ single_breaches: list[tuple[str, int, float]] = []
259
+ for name, _, ext in non_floor:
260
+ if ext <= single_cap:
261
+ continue
262
+ ceiling = KNOWN_CONCENTRATION_BREACHES.get(name)
263
+ if ceiling is not None and ext <= ceiling:
264
+ continue
265
+ single_breaches.append((name, ext, ext / total_ext))
266
+
237
267
  top3_sum = sum(ext for _, _, ext in non_floor[:3])
268
+ effective_top3_cap = top3_cap
269
+ if KNOWN_TOP3_CONCENTRATION_CEILING is not None:
270
+ effective_top3_cap = max(top3_cap, KNOWN_TOP3_CONCENTRATION_CEILING)
238
271
  top3_breach = (
239
272
  (top3_sum, top3_sum / total_ext)
240
- if top3_sum > top3_cap else None
273
+ if top3_sum > effective_top3_cap else None
241
274
  )
242
275
  return single_breaches, top3_breach
243
276
 
@@ -0,0 +1,213 @@
1
+ #!/usr/bin/env python3
2
+ """Validate compressed-output paths in `.agent-src/rules/*.md`.
3
+
4
+ Runs after `scripts/compress.py` projects sources to `.agent-src/`. The
5
+ rewriter in `compress.py` is the load-bearing primitive (road-to-path-fixes
6
+ P1.2); this script is the post-condition gate (P5.1) — every `load_context:`
7
+ entry in `.agent-src/rules/*.md` must resolve relative to the rule file's
8
+ directory to an existing file, and forbidden substrings must not survive
9
+ the rewrite (unless declared in `validator_ignore`).
10
+
11
+ Forbidden substrings (load_context + body):
12
+ - `.agent-src.uncompressed/` unless declared in validator_ignore
13
+ - `../../docs/` body-link two-up form (rewriter
14
+ collapses to single-up)
15
+ - `../../agents/` same shape, different root
16
+
17
+ Body-link checks (Council Decision 2, 2026-05-06):
18
+ - `load_context:` entries MUST resolve to an existing file under
19
+ `.agent-src/`.
20
+ - Body markdown links to `../contexts/...md` MUST resolve.
21
+ - Body markdown links to `../docs/guidelines/...md` are NOT checked
22
+ (P3.1 was cancelled; resolution is intentionally out of scope, the
23
+ Copilot suppression floor in P6 is the silencer).
24
+
25
+ `validator_ignore:` frontmatter primitive:
26
+ - Per-rule allowlist for rules that *describe* a forbidden substring as
27
+ their subject matter (e.g. `augment-source-of-truth` documents the
28
+ `.agent-src.uncompressed/` boundary). Each entry: `{type, pattern,
29
+ reason}`. The validator emits an audit line per matched ignore so
30
+ drift cannot hide.
31
+
32
+ Exit codes: 0 = clean, 1 = violations found, 3 = internal error.
33
+ """
34
+ from __future__ import annotations
35
+
36
+ import re
37
+ import sys
38
+ from dataclasses import dataclass
39
+ from pathlib import Path
40
+
41
+ import yaml
42
+
43
+ ROOT = Path(__file__).resolve().parent.parent
44
+ RULES_DIR = ROOT / ".agent-src" / "rules"
45
+
46
+ FORBIDDEN_SUBSTRINGS = (
47
+ ".agent-src.uncompressed/",
48
+ "../../docs/",
49
+ "../../agents/",
50
+ )
51
+
52
+ # Markdown links: `[text](path)` — capture path. Skip URLs and anchors.
53
+ _LINK_RE = re.compile(r'\[[^\]]*\]\(([^)#\s]+)(?:#[^)]*)?\)')
54
+
55
+
56
+ # Body-link prefixes whose resolution is intentionally out of scope.
57
+ # Council Decision 2 (2026-05-06): P3.1 was cancelled, so guideline links
58
+ # under `.agent-src/rules/` cannot resolve in the projected tree. Copilot
59
+ # suppression (P6) is the silencer for the noise.
60
+ UNCHECKED_LINK_PREFIXES = (
61
+ "../docs/guidelines/",
62
+ "../../docs/guidelines/",
63
+ )
64
+
65
+
66
+ @dataclass
67
+ class Violation:
68
+ file: str
69
+ line: int
70
+ kind: str
71
+ detail: str
72
+
73
+
74
+ @dataclass
75
+ class IgnoreEntry:
76
+ """Frontmatter `validator_ignore:` entry."""
77
+ kind: str # "substring" | "link"
78
+ pattern: str # exact substring or link prefix to ignore
79
+ reason: str # human-readable rationale (audited)
80
+
81
+
82
+ def _split_frontmatter(text: str):
83
+ if not text.startswith("---\n"):
84
+ return None, text
85
+ end = text.find("\n---\n", 4)
86
+ if end == -1:
87
+ return None, text
88
+ fm_text = text[4:end]
89
+ body = text[end + len("\n---\n"):]
90
+ try:
91
+ fm = yaml.safe_load(fm_text)
92
+ except yaml.YAMLError:
93
+ return None, text
94
+ return fm if isinstance(fm, dict) else {}, body
95
+
96
+
97
+ def _parse_ignores(fm: dict) -> list[IgnoreEntry]:
98
+ entries = fm.get("validator_ignore") or []
99
+ if not isinstance(entries, list):
100
+ return []
101
+ out: list[IgnoreEntry] = []
102
+ for raw in entries:
103
+ if not isinstance(raw, dict):
104
+ continue
105
+ kind = str(raw.get("type") or "").strip()
106
+ pattern = str(raw.get("pattern") or "").strip()
107
+ reason = str(raw.get("reason") or "").strip()
108
+ if kind in ("substring", "link") and pattern and reason:
109
+ out.append(IgnoreEntry(kind=kind, pattern=pattern, reason=reason))
110
+ return out
111
+
112
+
113
+ def _ignored(needle: str, ignores: list[IgnoreEntry], kind: str) -> IgnoreEntry | None:
114
+ for ig in ignores:
115
+ if ig.kind == kind and ig.pattern == needle:
116
+ return ig
117
+ return None
118
+
119
+
120
+ def _check_load_context(rule_file: Path, fm: dict, viols: list[Violation],
121
+ ignores: list[IgnoreEntry], audited: list[tuple[str, IgnoreEntry]]) -> None:
122
+ rule_dir = rule_file.parent
123
+ for key in ("load_context", "load_context_eager"):
124
+ entries = fm.get(key) or []
125
+ if not isinstance(entries, list):
126
+ continue
127
+ for entry in entries:
128
+ if not isinstance(entry, str):
129
+ continue
130
+ blocked = False
131
+ for needle in FORBIDDEN_SUBSTRINGS:
132
+ if needle in entry:
133
+ ig = _ignored(needle, ignores, "substring")
134
+ if ig:
135
+ audited.append((str(rule_file.relative_to(ROOT)), ig))
136
+ continue
137
+ viols.append(Violation(
138
+ str(rule_file.relative_to(ROOT)), 0, f"{key}-forbidden",
139
+ f"forbidden substring {needle!r} in entry {entry!r}",
140
+ ))
141
+ blocked = True
142
+ break
143
+ if blocked:
144
+ continue
145
+ target = (rule_dir / entry).resolve()
146
+ if not target.is_file():
147
+ viols.append(Violation(
148
+ str(rule_file.relative_to(ROOT)), 0, f"{key}-missing",
149
+ f"{entry!r} does not resolve to an existing file",
150
+ ))
151
+
152
+
153
+ def _check_body(rule_file: Path, body: str, viols: list[Violation],
154
+ ignores: list[IgnoreEntry], audited: list[tuple[str, IgnoreEntry]]) -> None:
155
+ rule_dir = rule_file.parent
156
+ for line_num, line in enumerate(body.splitlines(), start=1):
157
+ for needle in FORBIDDEN_SUBSTRINGS:
158
+ if needle in line:
159
+ ig = _ignored(needle, ignores, "substring")
160
+ if ig:
161
+ audited.append((f"{rule_file.relative_to(ROOT)}:{line_num}", ig))
162
+ continue
163
+ viols.append(Violation(
164
+ str(rule_file.relative_to(ROOT)), line_num, "body-forbidden",
165
+ f"forbidden substring {needle!r}",
166
+ ))
167
+ for m in _LINK_RE.finditer(line):
168
+ link = m.group(1)
169
+ if link.startswith(("http://", "https://", "mailto:", "#")):
170
+ continue
171
+ if not link.endswith(".md"):
172
+ continue
173
+ if any(link.startswith(p) for p in UNCHECKED_LINK_PREFIXES):
174
+ continue
175
+ target = (rule_dir / link).resolve()
176
+ if not target.is_file():
177
+ viols.append(Violation(
178
+ str(rule_file.relative_to(ROOT)), line_num, "body-link-missing",
179
+ f"link target {link!r} does not resolve",
180
+ ))
181
+
182
+
183
+ def main() -> int:
184
+ if not RULES_DIR.is_dir():
185
+ print(f"❌ {RULES_DIR} not found — run compression first", file=sys.stderr)
186
+ return 3
187
+ viols: list[Violation] = []
188
+ audited: list[tuple[str, IgnoreEntry]] = []
189
+ for rule_file in sorted(RULES_DIR.glob("*.md")):
190
+ text = rule_file.read_text(encoding="utf-8")
191
+ fm, body = _split_frontmatter(text)
192
+ ignores: list[IgnoreEntry] = _parse_ignores(fm) if fm is not None else []
193
+ if fm is not None:
194
+ _check_load_context(rule_file, fm, viols, ignores, audited)
195
+ _check_body(rule_file, body, viols, ignores, audited)
196
+ if audited:
197
+ print("ℹ️ validator_ignore audit:")
198
+ for loc, ig in audited:
199
+ print(f" {loc} — [{ig.kind}] {ig.pattern!r} → {ig.reason}")
200
+ print()
201
+ if viols:
202
+ for v in viols:
203
+ loc = f"{v.file}:{v.line}" if v.line else v.file
204
+ print(f"❌ [{v.kind}] {loc} — {v.detail}")
205
+ print(f"\n{len(viols)} violation(s) in .agent-src/rules/")
206
+ return 1
207
+ rule_count = len(list(RULES_DIR.glob('*.md')))
208
+ print(f"✅ compressed-path check clean ({rule_count} rules, {len(audited)} ignore(s) audited)")
209
+ return 0
210
+
211
+
212
+ if __name__ == "__main__":
213
+ sys.exit(main())
@@ -24,6 +24,15 @@ from dataclasses import dataclass, asdict
24
24
  from pathlib import Path
25
25
  from typing import List, Literal
26
26
 
27
+ # Import the rewriter so frontmatter comparison can normalise the source
28
+ # side through the same path transformations the compressor applies. Without
29
+ # this, every `load_context:` logical name (e.g. `contexts/foo.md`) and every
30
+ # `../../docs/...` body link looks like a frontmatter / body mismatch even
31
+ # though the rewriter is doing exactly what road-to-path-fixes.md P2/P3
32
+ # specified.
33
+ sys.path.insert(0, str(Path(__file__).resolve().parent))
34
+ from compress import _rewrite_paths # noqa: E402
35
+
27
36
  Severity = Literal["error", "warning", "info"]
28
37
 
29
38
  SOURCE_DIR = Path(".agent-src.uncompressed")
@@ -275,6 +284,12 @@ def scan_all(root: Path) -> List[Issue]:
275
284
 
276
285
  source_text = source_file.read_text(encoding="utf-8")
277
286
  target_text = target_file.read_text(encoding="utf-8")
287
+ # Normalise source through the path rewriter (idempotent) so logical
288
+ # `load_context:` names and `../../docs/...` body links match the
289
+ # depth-aware form the compressor produced. Compression word-count
290
+ # checks downstream are unaffected because rewriting only edits
291
+ # frontmatter list values and link targets, not prose tokens.
292
+ source_text = _rewrite_paths(source_text, rel_str)
278
293
  issues.extend(check_pair(rel_str, source_text, target_text))
279
294
 
280
295
  return issues
@@ -35,6 +35,7 @@ LOCKED_SUBTREES = (
35
35
  "chat-history",
36
36
  "execution",
37
37
  "authority",
38
+ "contracts",
38
39
  )
39
40
 
40
41
  # Files allowed to remain at the contexts root. Anything else at the root
@@ -0,0 +1,105 @@
1
+ #!/usr/bin/env python3
2
+ """CI guard for the `ai-council` skill's output-path convention.
3
+
4
+ Council artefacts (questions, responses, sessions) belong in three
5
+ canonical directories under `agents/`:
6
+
7
+ - agents/council-questions/<topic-slug>.md (paired with roadmap/ADR)
8
+ - agents/council-responses/<topic-slug>.json (paired with question)
9
+ - agents/council-sessions/<UTC-timestamp>.json (ad-hoc sessions)
10
+
11
+ The three canonical dirs are gitignored — the linter therefore only
12
+ catches **misplacement**, not naming-conventions inside the dirs:
13
+
14
+ - Files at agents/ root with a council-* or .council-* prefix
15
+ (e.g. agents/council-question-foo.md, agents/.council-foo.md).
16
+ - council-* files under any other subdirectory of agents/.
17
+
18
+ Failure modes are enforced by `.agent-src.uncompressed/skills/ai-council/SKILL.md`
19
+ § "Output path convention".
20
+
21
+ Exit codes:
22
+ 0 — layout is clean.
23
+ 1 — at least one violation found; details printed to stdout.
24
+
25
+ Invocation (from project root):
26
+ python3 scripts/check_council_layout.py
27
+ """
28
+
29
+ from __future__ import annotations
30
+
31
+ import re
32
+ import sys
33
+ from pathlib import Path
34
+
35
+ AGENTS_ROOT = Path("agents")
36
+ CANONICAL_DIRS = {
37
+ "council-questions": ".md",
38
+ "council-responses": ".json",
39
+ "council-sessions": ".json",
40
+ }
41
+ # A council artefact is a file whose name starts with `council-` or
42
+ # `.council-`. This intentionally excludes roadmaps like
43
+ # `road-to-ai-council.md` whose stem only contains the word "council".
44
+ COUNCIL_PREFIX_RE = re.compile(r"^\.?council-")
45
+
46
+
47
+ def is_council_artefact(path: Path) -> bool:
48
+ return bool(COUNCIL_PREFIX_RE.match(path.name))
49
+
50
+
51
+ def find_violations(root: Path) -> list[str]:
52
+ findings: list[str] = []
53
+ if not root.is_dir():
54
+ return findings
55
+
56
+ # 1. Stray council artefacts at agents/ root
57
+ for path in sorted(root.iterdir()):
58
+ if not path.is_file():
59
+ continue
60
+ if is_council_artefact(path):
61
+ findings.append(
62
+ f"{path}: council artefact at agents/ root — move to "
63
+ f"agents/council-questions/, agents/council-responses/, "
64
+ f"or agents/council-sessions/ per ai-council § Output path "
65
+ f"convention."
66
+ )
67
+
68
+ # 2. Council artefacts in non-canonical subdirectories
69
+ for path in sorted(root.rglob("*")):
70
+ if not path.is_file() or not is_council_artefact(path):
71
+ continue
72
+ try:
73
+ rel = path.relative_to(root)
74
+ except ValueError:
75
+ continue
76
+ if len(rel.parts) == 1:
77
+ continue # already handled above
78
+ if rel.parts[0] in CANONICAL_DIRS:
79
+ continue
80
+ findings.append(
81
+ f"{path}: council artefact in non-canonical directory "
82
+ f"agents/{rel.parts[0]}/ — only council-questions/, "
83
+ f"council-responses/, council-sessions/ are allowed."
84
+ )
85
+
86
+ return findings
87
+
88
+
89
+ def main() -> int:
90
+ findings = find_violations(AGENTS_ROOT)
91
+ if findings:
92
+ print("❌ Council layout violations:\n")
93
+ for f in findings:
94
+ print(f" - {f}")
95
+ print(
96
+ "\nRule: .agent-src.uncompressed/skills/ai-council/SKILL.md "
97
+ '§ "Output path convention"'
98
+ )
99
+ return 1
100
+ print("✅ Council layout clean.")
101
+ return 0
102
+
103
+
104
+ if __name__ == "__main__":
105
+ sys.exit(main())
@@ -0,0 +1,145 @@
1
+ #!/usr/bin/env python3
2
+ """CI guard for the `no-council-references` rule.
3
+
4
+ Council artefacts under `agents/council-{questions,responses,sessions}/`
5
+ are gitignored, local-only, and auto-pruned. A link to a specific
6
+ council file rots three ways: gitignored (not in cloned repo),
7
+ pruned after the retention window (gone even locally), and the
8
+ installed `.augment/` projection cannot follow a path that does not
9
+ exist in the consumer.
10
+
11
+ This linter scans durable artefacts for forbidden links to specific
12
+ council files. Directory mentions and placeholder paths
13
+ (`<timestamp>`, `<topic-slug>`) are allowed — they document the
14
+ output-path convention, not a live reference.
15
+
16
+ Forbidden hits in this codebase exist today (kernel-membership ADRs
17
+ cite real session JSONs as decision traces). Suppress them with an
18
+ inline pragma at the end of the line:
19
+
20
+ `agents/council-sessions/...json` <!-- council-ref-allowed: <reason> -->
21
+
22
+ Exit codes:
23
+ 0 — no forbidden references.
24
+ 1 — at least one forbidden reference found.
25
+
26
+ Invocation (from project root):
27
+ python3 scripts/check_council_references.py
28
+ """
29
+
30
+ from __future__ import annotations
31
+
32
+ import re
33
+ import sys
34
+ from pathlib import Path
35
+ from typing import Iterable
36
+
37
+ ROOT = Path(".")
38
+
39
+ # A specific file inside a council dir: must end with .md or .json,
40
+ # must NOT contain `<` or `>` (placeholders), must NOT contain backticks
41
+ # or quotes (those are line delimiters, not path content).
42
+ PATTERN = re.compile(
43
+ r"agents/council-(?:questions|responses|sessions)/"
44
+ r"([^\s\"'<>)\]`]+\.(?:md|json))"
45
+ )
46
+
47
+ # Only these durable surfaces are scanned. Archive, analysis, and the
48
+ # council dirs themselves are excluded by design.
49
+ SCAN_ROOTS = (
50
+ ".agent-src.uncompressed",
51
+ "agents/roadmaps",
52
+ "agents/contexts",
53
+ "agents/docs",
54
+ "docs/contracts",
55
+ "docs/decisions",
56
+ "docs/guidelines",
57
+ )
58
+ SCAN_EXTS = (".md", ".yml", ".yaml", ".json", ".py")
59
+
60
+ # Files (or directory prefixes) that legitimately document the output
61
+ # convention or are scratch / archived. Paths are POSIX-style, repo-relative.
62
+ ALLOWLIST_PREFIXES: tuple[str, ...] = (
63
+ # Archived roadmaps — historical evidence trail.
64
+ "agents/roadmaps/archive/",
65
+ # Working comparison docs — forward-refs to planned artefacts (mirrors
66
+ # the SKIP_DIRS contract in scripts/check_references.py).
67
+ "agents/analysis/",
68
+ # The rule itself documents forbidden vs. allowed forms.
69
+ ".agent-src.uncompressed/rules/no-council-references.md",
70
+ # ai-council skill documents the output-path schema.
71
+ ".agent-src.uncompressed/skills/ai-council/",
72
+ # Council commands document the output-path schema.
73
+ ".agent-src.uncompressed/commands/council/",
74
+ ".agent-src.uncompressed/commands/council.md",
75
+ )
76
+ # Top-level files that are also exempt (e.g. CHANGELOG with historical entries).
77
+ ALLOWLIST_FILES: frozenset[str] = frozenset({
78
+ "CHANGELOG.md",
79
+ })
80
+
81
+ INLINE_PRAGMA = re.compile(r"<!--\s*council-ref-allowed:[^>]*-->")
82
+
83
+
84
+ def _is_allowlisted(rel: str) -> bool:
85
+ if rel in ALLOWLIST_FILES:
86
+ return True
87
+ return any(rel.startswith(prefix) for prefix in ALLOWLIST_PREFIXES)
88
+
89
+
90
+ def _scan_file(path: Path) -> list[tuple[int, str]]:
91
+ findings: list[tuple[int, str]] = []
92
+ try:
93
+ text = path.read_text(encoding="utf-8")
94
+ except (OSError, UnicodeDecodeError):
95
+ return findings
96
+ for ln, line in enumerate(text.splitlines(), 1):
97
+ if INLINE_PRAGMA.search(line):
98
+ continue
99
+ for m in PATTERN.finditer(line):
100
+ findings.append((ln, m.group(0)))
101
+ return findings
102
+
103
+
104
+ def _iter_files(roots: Iterable[str]) -> Iterable[Path]:
105
+ for root in roots:
106
+ base = ROOT / root
107
+ if not base.exists():
108
+ continue
109
+ if base.is_file():
110
+ yield base
111
+ continue
112
+ for path in sorted(base.rglob("*")):
113
+ if path.is_file() and path.suffix in SCAN_EXTS:
114
+ yield path
115
+
116
+
117
+ def main() -> int:
118
+ violations: list[tuple[Path, int, str]] = []
119
+ for path in _iter_files(SCAN_ROOTS):
120
+ rel = path.as_posix()
121
+ if _is_allowlisted(rel):
122
+ continue
123
+ for ln, ref in _scan_file(path):
124
+ violations.append((path, ln, ref))
125
+
126
+ if not violations:
127
+ print("✅ No forbidden council references in durable artefacts.")
128
+ return 0
129
+
130
+ print(f"❌ {len(violations)} forbidden council reference(s):\n")
131
+ for path, ln, ref in violations:
132
+ print(f" - {path.as_posix()}:{ln}: {ref}")
133
+ print(
134
+ "\nRule: .agent-src/rules/no-council-references.md\n"
135
+ "Fix: inline the convergence summary (members + date) instead of\n"
136
+ "linking the file. Append "
137
+ "<!-- council-ref-allowed: <reason> --> on the same line to\n"
138
+ "suppress when the reference is genuinely required (ADR / contract\n"
139
+ "decision trace)."
140
+ )
141
+ return 1
142
+
143
+
144
+ if __name__ == "__main__":
145
+ sys.exit(main())
@@ -316,6 +316,8 @@ _TASK_FENCE_RE = re.compile(r"^\s*task\s+([a-z][a-z0-9:_-]*)\b")
316
316
  _TASK_DETECTOR_SKIP = (
317
317
  "rules/augment-portability.md",
318
318
  "contexts/communication/rules-auto/augment-portability-mechanics.md",
319
+ "rules/package-ci-checks.md",
320
+ "contexts/communication/rules-auto/package-ci-checks-mechanics.md",
319
321
  )
320
322
 
321
323
 
@@ -33,8 +33,10 @@ class BrokenRef:
33
33
 
34
34
  SCAN_DIRS = [".agent-src", "agents"]
35
35
  SKIP_DIRS = [
36
- "agents/roadmaps/archive", # archived roadmaps have historical refs
37
- "agents/council-sessions", # per-user audit trail (gitignored), captured provider output
36
+ "agents/roadmaps/archive", # archived roadmaps have historical refs
37
+ "agents/council-sessions", # per-user audit trail (gitignored), captured provider output
38
+ "agents/council-questions", # design Q&A trail — forward-refs to planned artifacts
39
+ "agents/analysis", # plate-comparison working docs — forward-refs to planned artifacts
38
40
  ]
39
41
  ROOT = Path(".")
40
42
 
@@ -101,6 +103,8 @@ EXAMPLE_PATH_PATTERNS = [
101
103
  re.compile(r"skills/[\w-]+/SKILL\.md"), # example skill paths in commands
102
104
  re.compile(r"\{"), # template placeholders like {module}
103
105
  re.compile(r"\.compression-hashes\.json"), # JSON file, not .md
106
+ re.compile(r"-foo\.(md|json|yml|yaml)$"), # `-foo.<ext>` placeholder examples
107
+ re.compile(r"-bar\.(md|json|yml|yaml)$"), # `-bar.<ext>` placeholder examples
104
108
  # Forward references inside in-flight planning docs (road-to-
105
109
  # structural-optimization.md and its companion spike protocols).
106
110
  # Each pattern below is removed once the matching phase lands.
@@ -280,6 +284,14 @@ def check_file(filepath: Path, artifacts: dict[str, set[str]], root: Path) -> Li
280
284
  # checkable file paths.
281
285
  if not resolved and raw_ref.startswith("agents/state/"):
282
286
  resolved = True
287
+ # `agents/.agent-prices.md` is a runtime-bootstrapped pricing
288
+ # cache — gitignored (.gitignore:/agents/.agent-prices.md),
289
+ # auto-generated by scripts/ai_council/pricing.py from
290
+ # _default_prices.py if missing. Same class as agents/state/*
291
+ # but a single named file, not a directory pattern, so the
292
+ # carve-out stays narrow.
293
+ if not resolved and raw_ref == "agents/.agent-prices.md":
294
+ resolved = True
283
295
  if not resolved:
284
296
  broken.append(BrokenRef(
285
297
  file=str(filepath), line=i, ref=m.group(1),