@event4u/agent-config 1.20.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 (238) hide show
  1. package/.agent-src/commands/agents.md +1 -1
  2. package/.agent-src/commands/bug-fix.md +1 -1
  3. package/.agent-src/commands/bug-investigate.md +2 -2
  4. package/.agent-src/commands/chat-history/import.md +60 -64
  5. package/.agent-src/commands/compress.md +12 -0
  6. package/.agent-src/commands/context/create.md +2 -2
  7. package/.agent-src/commands/context.md +1 -1
  8. package/.agent-src/commands/copilot-agents.md +1 -1
  9. package/.agent-src/commands/council/default.md +17 -5
  10. package/.agent-src/commands/council.md +1 -1
  11. package/.agent-src/commands/e2e-heal.md +1 -1
  12. package/.agent-src/commands/e2e-plan.md +1 -1
  13. package/.agent-src/commands/feature/dev.md +3 -3
  14. package/.agent-src/commands/feature.md +1 -1
  15. package/.agent-src/commands/fix/seeder.md +2 -2
  16. package/.agent-src/commands/fix.md +1 -1
  17. package/.agent-src/commands/jira-ticket.md +1 -1
  18. package/.agent-src/commands/judge.md +2 -2
  19. package/.agent-src/commands/memory.md +1 -1
  20. package/.agent-src/commands/mode.md +5 -5
  21. package/.agent-src/commands/module.md +1 -1
  22. package/.agent-src/commands/onboard.md +4 -4
  23. package/.agent-src/commands/optimize/augmentignore.md +1 -1
  24. package/.agent-src/commands/optimize-prompt.md +61 -0
  25. package/.agent-src/commands/optimize.md +1 -1
  26. package/.agent-src/commands/override.md +1 -1
  27. package/.agent-src/commands/review-changes.md +1 -1
  28. package/.agent-src/commands/review-routing.md +1 -1
  29. package/.agent-src/commands/roadmap.md +1 -1
  30. package/.agent-src/commands/set-cost-profile.md +3 -3
  31. package/.agent-src/commands/sync-agent-settings.md +2 -2
  32. package/.agent-src/commands/tests/create.md +2 -2
  33. package/.agent-src/commands/tests.md +1 -1
  34. package/.agent-src/commands/threat-model.md +4 -4
  35. package/.agent-src/contexts/authority/commit-mechanics.md +14 -1
  36. package/.agent-src/contexts/authority/destructive-mechanics.md +14 -1
  37. package/.agent-src/contexts/authority/scope-mechanics.md +5 -0
  38. package/.agent-src/contexts/communication/rules-auto/guidelines-mechanics.md +76 -0
  39. package/.agent-src/contexts/communication/rules-auto/slash-command-routing-policy-mechanics.md +1 -1
  40. package/.agent-src/contexts/communication/rules-auto/think-before-action-mechanics.md +98 -0
  41. package/.agent-src/contexts/communication/rules-auto/token-efficiency-mechanics.md +93 -0
  42. package/.agent-src/contexts/communication/rules-auto/user-interaction-mechanics.md +128 -5
  43. package/.agent-src/contexts/execution/autonomy-mechanics.md +44 -0
  44. package/.agent-src/contexts/model-recommendations.md +2 -2
  45. package/.agent-src/contexts/override-system.md +1 -1
  46. package/.agent-src/personas/product-owner.md +2 -2
  47. package/.agent-src/personas/qa.md +1 -1
  48. package/.agent-src/rules/agent-authority.md +5 -6
  49. package/.agent-src/rules/agent-docs.md +11 -53
  50. package/.agent-src/rules/analysis-skill-routing.md +10 -40
  51. package/.agent-src/rules/architecture.md +6 -1
  52. package/.agent-src/rules/artifact-drafting-protocol.md +5 -0
  53. package/.agent-src/rules/artifact-engagement-recording.md +23 -59
  54. package/.agent-src/rules/ask-when-uncertain.md +24 -47
  55. package/.agent-src/rules/augment-portability.md +14 -62
  56. package/.agent-src/rules/augment-source-of-truth.md +10 -1
  57. package/.agent-src/rules/autonomous-execution.md +17 -98
  58. package/.agent-src/rules/capture-learnings.md +9 -80
  59. package/.agent-src/rules/cli-output-handling.md +12 -42
  60. package/.agent-src/rules/command-suggestion-policy.md +25 -73
  61. package/.agent-src/rules/commit-conventions.md +9 -58
  62. package/.agent-src/rules/commit-policy.md +16 -47
  63. package/.agent-src/rules/context-hygiene.md +5 -0
  64. package/.agent-src/rules/direct-answers.md +21 -50
  65. package/.agent-src/rules/docker-commands.md +11 -45
  66. package/.agent-src/rules/docs-sync.md +10 -56
  67. package/.agent-src/rules/downstream-changes.md +5 -0
  68. package/.agent-src/rules/e2e-testing.md +9 -44
  69. package/.agent-src/rules/guidelines.md +13 -75
  70. package/.agent-src/rules/improve-before-implement.md +10 -2
  71. package/.agent-src/rules/language-and-tone.md +41 -106
  72. package/.agent-src/rules/laravel-translations.md +11 -40
  73. package/.agent-src/rules/markdown-safe-codeblocks.md +4 -0
  74. package/.agent-src/rules/minimal-safe-diff.md +4 -0
  75. package/.agent-src/rules/missing-tool-handling.md +4 -0
  76. package/.agent-src/rules/model-recommendation.md +9 -61
  77. package/.agent-src/rules/no-attribution-footers.md +5 -0
  78. package/.agent-src/rules/no-cheap-questions.md +11 -27
  79. package/.agent-src/rules/no-council-references.md +76 -0
  80. package/.agent-src/rules/no-roadmap-references.md +7 -0
  81. package/.agent-src/rules/non-destructive-by-default.md +13 -43
  82. package/.agent-src/rules/onboarding-gate.md +9 -117
  83. package/.agent-src/rules/package-ci-checks.md +10 -37
  84. package/.agent-src/rules/php-coding.md +10 -55
  85. package/.agent-src/rules/preservation-guard.md +9 -0
  86. package/.agent-src/rules/review-routing-awareness.md +9 -97
  87. package/.agent-src/rules/reviewer-awareness.md +8 -83
  88. package/.agent-src/rules/roadmap-progress-sync.md +7 -170
  89. package/.agent-src/rules/role-mode-adherence.md +6 -2
  90. package/.agent-src/rules/rule-type-governance.md +8 -66
  91. package/.agent-src/rules/runtime-safety.md +5 -0
  92. package/.agent-src/rules/scope-control.md +17 -62
  93. package/.agent-src/rules/security-sensitive-stop.md +7 -1
  94. package/.agent-src/rules/size-enforcement.md +6 -1
  95. package/.agent-src/rules/skill-improvement-trigger.md +9 -49
  96. package/.agent-src/rules/skill-quality.md +7 -113
  97. package/.agent-src/rules/slash-command-routing-policy.md +11 -63
  98. package/.agent-src/rules/think-before-action.md +22 -87
  99. package/.agent-src/rules/token-efficiency.md +10 -74
  100. package/.agent-src/rules/token-optimizer-maintenance.md +68 -0
  101. package/.agent-src/rules/tool-safety.md +4 -0
  102. package/.agent-src/rules/ui-audit-gate.md +25 -61
  103. package/.agent-src/rules/upstream-proposal.md +9 -67
  104. package/.agent-src/rules/user-interaction.md +22 -108
  105. package/.agent-src/rules/verify-before-complete.md +1 -1
  106. package/.agent-src/skills/agent-docs-writing/SKILL.md +1 -1
  107. package/.agent-src/skills/ai-council/SKILL.md +65 -0
  108. package/.agent-src/skills/analysis-autonomous-mode/SKILL.md +1 -1
  109. package/.agent-src/skills/analysis-skill-router/SKILL.md +3 -3
  110. package/.agent-src/skills/artisan-commands/SKILL.md +2 -2
  111. package/.agent-src/skills/authz-review/SKILL.md +1 -1
  112. package/.agent-src/skills/aws-infrastructure/SKILL.md +5 -5
  113. package/.agent-src/skills/blast-radius-analyzer/SKILL.md +8 -8
  114. package/.agent-src/skills/bug-analyzer/SKILL.md +5 -5
  115. package/.agent-src/skills/code-refactoring/SKILL.md +4 -4
  116. package/.agent-src/skills/code-review/SKILL.md +2 -2
  117. package/.agent-src/skills/command-writing/SKILL.md +11 -0
  118. package/.agent-src/skills/composer-packages/SKILL.md +2 -2
  119. package/.agent-src/skills/context-authoring/SKILL.md +11 -0
  120. package/.agent-src/skills/context-document/SKILL.md +1 -1
  121. package/.agent-src/skills/copilot-agents-optimization/SKILL.md +23 -0
  122. package/.agent-src/skills/copilot-config/SKILL.md +1 -1
  123. package/.agent-src/skills/dependency-upgrade/SKILL.md +2 -2
  124. package/.agent-src/skills/devcontainer/SKILL.md +2 -2
  125. package/.agent-src/skills/developer-like-execution/SKILL.md +1 -1
  126. package/.agent-src/skills/docker/SKILL.md +1 -1
  127. package/.agent-src/skills/dto-creator/SKILL.md +1 -1
  128. package/.agent-src/skills/estimate-ticket/SKILL.md +2 -2
  129. package/.agent-src/skills/fe-design/SKILL.md +4 -4
  130. package/.agent-src/skills/feature-planning/SKILL.md +5 -5
  131. package/.agent-src/skills/funnel-analysis/SKILL.md +1 -1
  132. package/.agent-src/skills/laravel/SKILL.md +1 -1
  133. package/.agent-src/skills/laravel-notifications/SKILL.md +5 -5
  134. package/.agent-src/skills/laravel-pennant/SKILL.md +1 -1
  135. package/.agent-src/skills/laravel-pulse/SKILL.md +4 -4
  136. package/.agent-src/skills/laravel-reverb/SKILL.md +2 -2
  137. package/.agent-src/skills/laravel-scheduling/SKILL.md +1 -1
  138. package/.agent-src/skills/migration-creator/SKILL.md +7 -7
  139. package/.agent-src/skills/multi-tenancy/SKILL.md +8 -8
  140. package/.agent-src/skills/performance-analysis/SKILL.md +3 -3
  141. package/.agent-src/skills/pest-testing/SKILL.md +6 -6
  142. package/.agent-src/skills/php-service/SKILL.md +2 -2
  143. package/.agent-src/skills/project-analysis-hypothesis-driven/SKILL.md +3 -3
  144. package/.agent-src/skills/project-analysis-react/SKILL.md +1 -1
  145. package/.agent-src/skills/project-analysis-symfony/SKILL.md +1 -1
  146. package/.agent-src/skills/project-analysis-zend-laminas/SKILL.md +2 -2
  147. package/.agent-src/skills/project-analyzer/SKILL.md +4 -4
  148. package/.agent-src/skills/prompt-optimizer/SKILL.md +108 -0
  149. package/.agent-src/skills/readme-reviewer/SKILL.md +1 -1
  150. package/.agent-src/skills/rule-writing/SKILL.md +33 -0
  151. package/.agent-src/skills/sentry-integration/SKILL.md +1 -1
  152. package/.agent-src/skills/skill-writing/SKILL.md +14 -0
  153. package/.agent-src/skills/terraform/SKILL.md +2 -2
  154. package/.agent-src/skills/terragrunt/SKILL.md +8 -8
  155. package/.agent-src/skills/test-performance/SKILL.md +5 -5
  156. package/.agent-src/skills/threat-modeling/SKILL.md +2 -2
  157. package/.agent-src/skills/token-optimizer/SKILL.md +110 -0
  158. package/.agent-src/skills/universal-project-analysis/SKILL.md +1 -1
  159. package/.agent-src/templates/AGENTS.md +1 -1
  160. package/.agent-src/templates/agent-settings.md +21 -16
  161. package/.agent-src/templates/contexts/tenant-boundaries.md +2 -2
  162. package/.agent-src/templates/contexts.md +1 -1
  163. package/.agent-src/templates/copilot-instructions.md +21 -0
  164. package/.agent-src/templates/copilot-review-instructions.md +76 -0
  165. package/.agent-src/templates/features.md +1 -1
  166. package/.agent-src/templates/rule.md +127 -0
  167. package/.claude-plugin/marketplace.json +4 -1
  168. package/AGENTS.md +32 -5
  169. package/CHANGELOG.md +69 -3
  170. package/README.md +22 -21
  171. package/config/agent-settings.template.yml +44 -10
  172. package/config/gitignore-block.txt +7 -0
  173. package/docs/architecture.md +86 -5
  174. package/docs/catalog.md +16 -6
  175. package/docs/contracts/agent-memory-contract.md +1 -1
  176. package/docs/contracts/context-paths.md +2 -1
  177. package/docs/contracts/file-ownership-matrix.json +354 -500
  178. package/docs/contracts/iron-law-overrides.txt +25 -0
  179. package/docs/contracts/kernel-membership.md +273 -0
  180. package/docs/contracts/load-context-schema.md +26 -11
  181. package/docs/contracts/pilot/agent-authority.md +24 -0
  182. package/docs/contracts/pilot/direct-answers.md +70 -0
  183. package/docs/contracts/pilot/language-and-tone.md +63 -0
  184. package/docs/contracts/rule-classification.md +170 -0
  185. package/docs/contracts/rule-router.md +153 -0
  186. package/docs/customization.md +17 -6
  187. package/docs/decisions/ADR-001-kernel-swap-deferred.md +109 -0
  188. package/docs/decisions/ADR-002-kernel-bucket-overrides.md +124 -0
  189. package/docs/decisions/ADR-rule-kernel-and-router.md +122 -0
  190. package/docs/getting-started.md +2 -2
  191. package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +176 -0
  192. package/docs/guidelines/agent-infra/rule-type-governance.md +73 -0
  193. package/docs/guidelines/agent-infra/size-and-scope.md +13 -2
  194. package/docs/guidelines/agent-infra/skill-quality-checklist.md +119 -0
  195. package/docs/guidelines/augment-portability-patterns.md +68 -0
  196. package/docs/guidelines/php/php-coding-patterns.md +62 -0
  197. package/package.json +1 -1
  198. package/scripts/_p43_bodies.py +235 -0
  199. package/scripts/_p43_compress.py +118 -0
  200. package/scripts/_p4_migrate.py +199 -0
  201. package/scripts/_pilot_council_question.py +57 -0
  202. package/scripts/_pilot_measure.py +53 -0
  203. package/scripts/ai_council/session.py +107 -5
  204. package/scripts/build_linear_digest.py +3 -5
  205. package/scripts/check_always_budget.py +39 -6
  206. package/scripts/check_compressed_paths.py +213 -0
  207. package/scripts/check_compression.py +15 -0
  208. package/scripts/check_context_paths.py +1 -0
  209. package/scripts/check_council_layout.py +105 -0
  210. package/scripts/check_council_references.py +145 -0
  211. package/scripts/check_portability.py +2 -0
  212. package/scripts/check_references.py +2 -0
  213. package/scripts/check_token_optimizer_freshness.py +131 -0
  214. package/scripts/compile_router.py +148 -0
  215. package/scripts/compress.py +219 -11
  216. package/scripts/council_cli.py +9 -5
  217. package/scripts/council_prune.py +81 -0
  218. package/scripts/count_token_optimizer_usage.sh +54 -0
  219. package/scripts/install.sh +44 -2
  220. package/scripts/iron_law_sha.py +98 -0
  221. package/scripts/lint_load_context.py +35 -5
  222. package/scripts/measure_rule_budget.py +314 -0
  223. package/scripts/prototype_lint_contradictions.py +150 -0
  224. package/scripts/schemas/rule.schema.json +55 -6
  225. package/scripts/skill_linter.py +196 -6
  226. package/scripts/smoke_path_resolution.py +93 -0
  227. package/scripts/validate_frontmatter.py +41 -1
  228. package/.agent-src/contexts/communication/rules-auto/artifact-engagement-recording-mechanics.md +0 -72
  229. package/.agent-src/contexts/communication/rules-auto/augment-portability-mechanics.md +0 -79
  230. package/.agent-src/contexts/communication/rules-auto/cli-output-handling-mechanics.md +0 -87
  231. package/.agent-src/contexts/communication/rules-auto/command-suggestion-policy-mechanics.md +0 -62
  232. package/.agent-src/contexts/communication/rules-auto/docs-sync-mechanics.md +0 -78
  233. package/.agent-src/contexts/communication/rules-auto/package-ci-checks-mechanics.md +0 -85
  234. package/.agent-src/contexts/communication/rules-auto/review-routing-awareness-mechanics.md +0 -65
  235. package/.agent-src/contexts/communication/rules-auto/roadmap-progress-sync-mechanics.md +0 -78
  236. package/.agent-src/contexts/communication/rules-auto/ui-audit-gate-mechanics.md +0 -53
  237. /package/{docs → .agent-src/contexts}/contracts/artifact-engagement-flow.md +0 -0
  238. /package/{docs → .agent-src/contexts}/contracts/command-suggestion-flow.md +0 -0
@@ -117,6 +117,17 @@ VALID_RULE_TYPES = {"always", "auto"}
117
117
  VALID_RULE_SOURCES = {"package", "project"}
118
118
  VALID_STATUSES = {"active", "deprecated", "superseded"}
119
119
 
120
+ # --- Router schema (docs/contracts/rule-router.md) ---
121
+ ROUTER_ALLOWED_TRIGGER_KEYS = {"keyword", "phrase", "intent", "file_pattern",
122
+ "path_prefix", "command"}
123
+ ROUTER_ALLOWED_PROFILES = {"minimal", "balanced", "full"}
124
+ KERNEL_RULE_IDS: set[str] = {
125
+ "agent-authority", "ask-when-uncertain", "commit-policy",
126
+ "direct-answers", "language-and-tone", "no-cheap-questions",
127
+ "non-destructive-by-default", "scope-control",
128
+ "verify-before-complete",
129
+ }
130
+
120
131
  # --- Runtime execution metadata constants ---
121
132
  VALID_EXECUTION_TYPES = {"manual", "assisted", "automated"}
122
133
  VALID_EXECUTION_HANDLERS = {"none", "shell", "php", "node", "internal"}
@@ -220,6 +231,42 @@ def extract_sections(text: str) -> set[str]:
220
231
  return {match.group(1).strip() for match in SECTION_PATTERN.finditer(text)}
221
232
 
222
233
 
234
+ def _count_code_blocks(text: str) -> int:
235
+ """Return the number of fenced code blocks (``` … ```) in *text*."""
236
+ fence_count = 0
237
+ for line in text.splitlines():
238
+ stripped = line.lstrip()
239
+ if stripped.startswith("```"):
240
+ fence_count += 1
241
+ return fence_count // 2
242
+
243
+
244
+ def _fenced_content_ratio(text: str) -> float:
245
+ """Return the fraction of non-empty lines that sit inside fenced blocks.
246
+
247
+ Used as a structural signal: rules / files dominated by verbatim Iron-Law
248
+ blocks or worked examples score high and are exempted from raw line-count
249
+ warnings (council review 2026-05-06).
250
+ """
251
+ inside = False
252
+ fenced_lines = 0
253
+ non_empty = 0
254
+ for line in text.splitlines():
255
+ stripped = line.strip()
256
+ if stripped.startswith("```"):
257
+ inside = not inside
258
+ if stripped:
259
+ non_empty += 1
260
+ continue
261
+ if stripped:
262
+ non_empty += 1
263
+ if inside:
264
+ fenced_lines += 1
265
+ if non_empty == 0:
266
+ return 0.0
267
+ return fenced_lines / non_empty
268
+
269
+
223
270
  def extract_description(text: str) -> Optional[str]:
224
271
  frontmatter = FRONTMATTER_PATTERN.search(text)
225
272
  if not frontmatter:
@@ -495,8 +542,12 @@ def lint_skill(path: Path, text: str) -> LintResult:
495
542
  suggestions.append("Add a requirement-checking or validation step before implementation")
496
543
 
497
544
  # --- Size check (see guidelines/agent-infra/size-and-scope.md) ---
545
+ # Threshold raised from 300 → 400 (council review 2026-05-06): reference-rich
546
+ # skills (quality-tools 411, ai-council 399, project-analyzer 341) legitimately
547
+ # exceed 300 lines without being split-candidates. Structural follow-up tracked
548
+ # in agents/roadmaps/road-to-structural-linter-reform.md.
498
549
  total_lines = len(text.splitlines())
499
- if total_lines > 300:
550
+ if total_lines > 400:
500
551
  issues.append(Issue("warning", "skill_too_large", f"Skill has {total_lines} lines; review for split (see size-and-scope guideline)"))
501
552
 
502
553
  # --- Pointer-only / guideline-dependent skill detection ---
@@ -553,6 +604,131 @@ def extract_frontmatter(text: str) -> Optional[str]:
553
604
  return match.group(1) if match else None
554
605
 
555
606
 
607
+ def _parse_yaml_list(frontmatter: str, key: str) -> Optional[list]:
608
+ """Parse a simple top-level YAML list `key:` from frontmatter.
609
+
610
+ Supports the two shapes we emit in rule frontmatter:
611
+ triggers:
612
+ - keyword: "foo"
613
+ - phrase: "bar baz"
614
+ routes_to:
615
+ - skill:php-coder
616
+ - guideline:agent-infra/asking-and-brevity-examples
617
+
618
+ Returns ``None`` if the key is absent (so the caller can distinguish
619
+ "missing" from "empty"); returns ``[]`` for an explicitly empty list.
620
+ """
621
+ lines = frontmatter.splitlines()
622
+ out: list = []
623
+ in_block = False
624
+ for line in lines:
625
+ if not in_block:
626
+ if line.startswith(f"{key}:"):
627
+ rhs = line[len(key) + 1:].strip()
628
+ if rhs in ("", "[]"):
629
+ if rhs == "[]":
630
+ return []
631
+ in_block = True
632
+ else:
633
+ return None # unexpected scalar shape
634
+ continue
635
+ if line.startswith(" - "):
636
+ item = line[4:].strip()
637
+ if ":" in item and not item.startswith(("'", '"')):
638
+ k, _, v = item.partition(":")
639
+ out.append({k.strip(): v.strip().strip('"').strip("'")})
640
+ else:
641
+ out.append(item.strip('"').strip("'"))
642
+ elif line.strip() == "" or line.startswith(" "):
643
+ continue
644
+ else:
645
+ break
646
+ return out if in_block else None
647
+
648
+
649
+ def lint_router_frontmatter(rule_id: str, frontmatter: str,
650
+ rule_type: Optional[str]) -> List[Issue]:
651
+ """Validate `triggers:` / `routes_to:` per docs/contracts/rule-router.md.
652
+
653
+ Strict checks (always errors): kernel rules MUST NOT carry router fields;
654
+ `triggers:` items must use one allowed key; `routes_to:` items must
655
+ follow `kind:id` with kind ∈ {skill, guideline} and the target file
656
+ must exist on disk.
657
+
658
+ Lenient checks (info-level until Phase 4 migrations land): non-kernel
659
+ rules without `triggers:` / `routes_to:` get an informational note,
660
+ not an error — the existing description-matching path still works.
661
+ """
662
+ issues: List[Issue] = []
663
+ triggers = _parse_yaml_list(frontmatter, "triggers")
664
+ routes_to = _parse_yaml_list(frontmatter, "routes_to")
665
+
666
+ is_kernel = rule_id in KERNEL_RULE_IDS or rule_type == "always"
667
+
668
+ if is_kernel:
669
+ if triggers is not None:
670
+ issues.append(Issue("error", "kernel_has_triggers",
671
+ "Kernel rules MUST NOT declare triggers: (kernel is unconditional)"))
672
+ if routes_to is not None:
673
+ issues.append(Issue("error", "kernel_has_routes_to",
674
+ "Kernel rules MUST NOT declare routes_to: (kernel body stays inline)"))
675
+ return issues
676
+
677
+ # Non-kernel rule path
678
+ if triggers is None:
679
+ issues.append(Issue("info", "router_triggers_missing",
680
+ "Non-kernel rule has no triggers: — falls back to description matching "
681
+ "until Phase 4 migration lands"))
682
+ else:
683
+ for idx, item in enumerate(triggers):
684
+ if not isinstance(item, dict) or len(item) != 1:
685
+ issues.append(Issue("error", "trigger_shape_invalid",
686
+ f"triggers[{idx}] must be a single-key mapping"))
687
+ continue
688
+ (k,) = item.keys()
689
+ if k not in ROUTER_ALLOWED_TRIGGER_KEYS:
690
+ allowed = ", ".join(sorted(ROUTER_ALLOWED_TRIGGER_KEYS))
691
+ issues.append(Issue("error", "trigger_key_unknown",
692
+ f"triggers[{idx}] key '{k}' not in allowed set ({allowed})"))
693
+
694
+ if routes_to is None:
695
+ issues.append(Issue("info", "router_routes_to_missing",
696
+ "Non-kernel rule has no routes_to: — body should migrate to skill / "
697
+ "guideline in Phase 4"))
698
+ else:
699
+ repo_root = Path(__file__).resolve().parent.parent
700
+ for idx, item in enumerate(routes_to):
701
+ if not isinstance(item, str) or ":" not in item:
702
+ issues.append(Issue("error", "route_shape_invalid",
703
+ f"routes_to[{idx}] must be 'kind:id'"))
704
+ continue
705
+ kind, _, target_id = item.partition(":")
706
+ if kind == "skill":
707
+ target = repo_root / ".agent-src.uncompressed" / "skills" / target_id / "SKILL.md"
708
+ elif kind == "guideline":
709
+ target = repo_root / "docs" / "guidelines" / f"{target_id}.md"
710
+ elif kind == "command":
711
+ target = repo_root / ".agent-src.uncompressed" / "commands" / f"{target_id}.md"
712
+ elif kind == "contract":
713
+ # Contracts live in two places: stable host docs in
714
+ # docs/contracts/ and load-bearing flows in
715
+ # .agent-src.uncompressed/contexts/contracts/ (road-to-path-fixes
716
+ # P4 / Council R2). Try both before failing.
717
+ target = repo_root / "docs" / "contracts" / f"{target_id}.md"
718
+ if not target.exists():
719
+ alt = repo_root / ".agent-src.uncompressed" / "contexts" / "contracts" / f"{target_id}.md"
720
+ if alt.exists():
721
+ target = alt
722
+ else:
723
+ issues.append(Issue("error", "route_kind_unknown",
724
+ f"routes_to[{idx}] kind '{kind}' must be 'skill', 'guideline', 'command', or 'contract'"))
725
+ continue
726
+ if not target.exists():
727
+ issues.append(Issue("error", "route_target_missing",
728
+ f"routes_to[{idx}] target '{item}' not found at {target}"))
729
+ return issues
730
+
731
+
556
732
  def extract_frontmatter_field(frontmatter: str, pattern: re.Pattern[str]) -> Optional[str]:
557
733
  match = pattern.search(frontmatter)
558
734
  return match.group(1).strip() if match else None
@@ -801,6 +977,9 @@ def lint_rule(path: Path, text: str) -> LintResult:
801
977
  f"Always-rule with topic-specific description ({', '.join(topic_keywords)}) — "
802
978
  f"consider auto type per rule-type-governance"))
803
979
 
980
+ # Router schema validation (docs/contracts/rule-router.md, Phase 3.3).
981
+ issues.extend(lint_router_frontmatter(path.stem, frontmatter, rule_type))
982
+
804
983
  # --- Structure checks ---
805
984
  # H1 heading
806
985
  if not H1_PATTERN.search(text):
@@ -817,14 +996,18 @@ def lint_rule(path: Path, text: str) -> LintResult:
817
996
  issues.append(Issue("warning", "double_blank_lines", "File contains double or triple blank lines"))
818
997
 
819
998
  # --- Content checks (see guidelines/agent-infra/size-and-scope.md) ---
999
+ # Length thresholds gated by fenced-content density (council review 2026-05-06):
1000
+ # rules dominated by verbatim Iron-Law blocks / worked examples are protected
1001
+ # from the > 40 / > 60 warnings. Hard error at 200 stays unconditional.
820
1002
  line_count = len([line for line in text.splitlines() if line.strip()])
821
1003
  total_lines = len(text.splitlines())
1004
+ fenced_ratio = _fenced_content_ratio(text)
822
1005
  if total_lines > 200:
823
1006
  issues.append(Issue("error", "rule_too_large", f"Rule has {total_lines} lines (hard limit: 200); must split or move to guideline"))
824
- elif line_count > 60:
825
- issues.append(Issue("warning", "long_rule", f"Rule has {line_count} non-empty lines; prefer < 60 (see size-and-scope guideline)"))
826
- elif line_count > 40:
827
- issues.append(Issue("warning", "long_rule", f"Rule has {line_count} non-empty lines; rules should be concise"))
1007
+ elif line_count > 60 and fenced_ratio < 0.30:
1008
+ issues.append(Issue("warning", "long_rule", f"Rule has {line_count} non-empty lines (fenced-content {fenced_ratio:.0%}); prefer < 60 (see size-and-scope guideline)"))
1009
+ elif line_count > 40 and fenced_ratio < 0.30:
1010
+ issues.append(Issue("warning", "long_rule", f"Rule has {line_count} non-empty lines (fenced-content {fenced_ratio:.0%}); rules should be concise"))
828
1011
 
829
1012
  for bad_sign in RULE_BAD_SIGNS:
830
1013
  if bad_sign in text:
@@ -969,9 +1152,16 @@ def lint_command(path: Path, text: str) -> LintResult:
969
1152
  issues.append(Issue("warning", "no_steps", "Command has no Steps section or numbered sub-headings"))
970
1153
 
971
1154
  # --- Size check (see guidelines/agent-infra/size-and-scope.md) ---
1155
+ # Word threshold (1000) gated by structural delegation signal (council review
1156
+ # 2026-05-06): well-factored orchestrators with ≥ 5 sub-sections AND ≥ 3 code
1157
+ # blocks are exempt — the size reflects dispatch breadth, not bloat.
972
1158
  word_count = len(text.split())
973
1159
  if word_count > 1000:
974
- issues.append(Issue("warning", "large_command", f"Command has {word_count} words (target: 200-600, max ~1000)"))
1160
+ section_count = len(sections)
1161
+ code_block_count = _count_code_blocks(text)
1162
+ delegation_signal = section_count >= 5 and code_block_count >= 3
1163
+ if not delegation_signal:
1164
+ issues.append(Issue("warning", "large_command", f"Command has {word_count} words (target: 200-600, max ~1000); {section_count} sub-sections, {code_block_count} code blocks — lacks delegation structure"))
975
1165
 
976
1166
  # File must end with exactly one newline
977
1167
  if not text.endswith("\n"):
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env python3
2
+ """Smoke-test path resolution against the package's own `.augment/` projection.
3
+
4
+ Per `agents/roadmaps/road-to-path-fixes.md` Phase 7 (Council Decision 3,
5
+ 2026-05-06): the package's `.augment/` tree has the same shape as the
6
+ `.augment/` tree a consumer would receive after `scripts/install.sh`.
7
+ If `load_context:` entries resolve cleanly here, they resolve cleanly
8
+ in any consumer.
9
+
10
+ What it does:
11
+ - Walks `.augment/rules/*.md`.
12
+ - Parses each rule's YAML frontmatter.
13
+ - Resolves every `load_context:` and `load_context_eager:` entry
14
+ against the rule file's directory.
15
+ - Reports any miss with a file:entry line.
16
+
17
+ Exit codes: 0 = all entries resolve, 1 = one or more misses, 3 = no
18
+ `.augment/rules/` directory found (run `task sync` first).
19
+ """
20
+ from __future__ import annotations
21
+
22
+ import sys
23
+ from pathlib import Path
24
+
25
+ import yaml
26
+
27
+ ROOT = Path(__file__).resolve().parent.parent
28
+ AUGMENT_RULES = ROOT / ".augment" / "rules"
29
+
30
+
31
+ def _split_frontmatter(text: str):
32
+ if not text.startswith("---\n"):
33
+ return None
34
+ end = text.find("\n---\n", 4)
35
+ if end == -1:
36
+ return None
37
+ try:
38
+ fm = yaml.safe_load(text[4:end])
39
+ except yaml.YAMLError:
40
+ return None
41
+ return fm if isinstance(fm, dict) else {}
42
+
43
+
44
+ def _check_rule(rule_file: Path, misses: list[tuple[str, str]]) -> int:
45
+ fm = _split_frontmatter(rule_file.read_text(encoding="utf-8"))
46
+ if not fm:
47
+ return 0
48
+ checked = 0
49
+ rule_dir = rule_file.parent
50
+ for key in ("load_context", "load_context_eager"):
51
+ entries = fm.get(key) or []
52
+ if not isinstance(entries, list):
53
+ continue
54
+ for entry in entries:
55
+ if not isinstance(entry, str):
56
+ continue
57
+ checked += 1
58
+ target = (rule_dir / entry).resolve()
59
+ if not target.is_file():
60
+ misses.append((str(rule_file.relative_to(ROOT)), entry))
61
+ return checked
62
+
63
+
64
+ def main() -> int:
65
+ if not AUGMENT_RULES.is_dir():
66
+ print(
67
+ f"❌ {AUGMENT_RULES.relative_to(ROOT)} not found — run `task sync` first",
68
+ file=sys.stderr,
69
+ )
70
+ return 3
71
+
72
+ misses: list[tuple[str, str]] = []
73
+ rule_count = 0
74
+ entry_count = 0
75
+ for rule_file in sorted(AUGMENT_RULES.glob("*.md")):
76
+ rule_count += 1
77
+ entry_count += _check_rule(rule_file, misses)
78
+
79
+ if misses:
80
+ print(f"❌ {len(misses)} unresolved load_context entr(y/ies):")
81
+ for rule, entry in misses:
82
+ print(f" {rule} → {entry!r}")
83
+ return 1
84
+
85
+ print(
86
+ f"✅ smoke-path-resolution clean "
87
+ f"({rule_count} rules, {entry_count} load_context entr(y/ies) resolved)"
88
+ )
89
+ return 0
90
+
91
+
92
+ if __name__ == "__main__":
93
+ sys.exit(main())
@@ -157,15 +157,55 @@ def _parse_yaml_block(body: str) -> dict[str, Any]:
157
157
  def _consume_block_list(lines: list[str], start: int) -> tuple[list[Any], int]:
158
158
  items: list[Any] = []
159
159
  i = start
160
+ item_indent: int | None = None
160
161
  while i < len(lines):
161
162
  line = lines[i]
162
163
  if not line.strip():
163
164
  i += 1
164
165
  continue
165
166
  stripped = line.lstrip()
167
+ leading = len(line) - len(stripped)
166
168
  if not stripped.startswith("- "):
169
+ # Out of the list — caller resumes.
167
170
  break
168
- items.append(_coerce(stripped[2:]))
171
+ if item_indent is None:
172
+ item_indent = leading
173
+ elif leading != item_indent:
174
+ break
175
+ item_body = stripped[2:].strip()
176
+ # Inline mapping inside the list: `- key: value`
177
+ m = re.match(r"^([\w-]+):\s*(.*)$", item_body)
178
+ if m:
179
+ mapping: dict[str, Any] = {}
180
+ key = m.group(1)
181
+ raw = m.group(2).strip()
182
+ mapping[key] = _coerce(raw) if raw != "" else ""
183
+ # Consume continuation lines that are indented past the dash —
184
+ # they belong to the same mapping item (`pattern: …`, `reason: …`).
185
+ cont_indent = item_indent + 2 # `- ` is two chars
186
+ i += 1
187
+ while i < len(lines):
188
+ cont = lines[i]
189
+ if not cont.strip():
190
+ i += 1
191
+ continue
192
+ cont_stripped = cont.lstrip()
193
+ cont_leading = len(cont) - len(cont_stripped)
194
+ if cont_stripped.startswith("- "):
195
+ break
196
+ if cont_leading <= item_indent:
197
+ break
198
+ cm = re.match(r"^([\w-]+):\s*(.*)$", cont_stripped)
199
+ if cm and cont_leading >= cont_indent:
200
+ ckey = cm.group(1)
201
+ cval = cm.group(2).strip()
202
+ mapping[ckey] = _coerce(cval) if cval != "" else ""
203
+ i += 1
204
+ else:
205
+ break
206
+ items.append(mapping)
207
+ continue
208
+ items.append(_coerce(item_body))
169
209
  i += 1
170
210
  return items, i - start
171
211
 
@@ -1,72 +0,0 @@
1
- # Artifact Engagement Recording — mechanics
2
-
3
- CLI invocation, privacy contract, failure-mode handling, and "what
4
- this rule does NOT do" catalog for the
5
- [`artifact-engagement-recording`](../../../rules/artifact-engagement-recording.md)
6
- rule. The activation gate, cadence table, and consulted-vs-applied
7
- definitions live in the rule; this file is the lookup material for
8
- the actual `telemetry:record` call and the privacy floor.
9
-
10
- ## What to record — id-only, no payload
11
-
12
- ```bash
13
- ./agent-config telemetry:record \
14
- --task-id "$TASK_ID" \
15
- --boundary task \
16
- --consulted skills:php-coder \
17
- --consulted skills:eloquent \
18
- --consulted rules:scope-control \
19
- --applied skills:php-coder \
20
- --applied rules:scope-control
21
- ```
22
-
23
- - `--task-id` — the ticket key (`PROJ-123`) for `/implement-ticket`, or a
24
- short opaque slug derived from the prompt for `/work`. **Never** a
25
- branch name, a file path, or a free-text title.
26
- - `--boundary` — `task` or `phase-step`, matching the cadence in the rule.
27
- - `--consulted <kind>:<id>` — repeat per artefact. `<kind>` is one of
28
- `skills`, `rules`, `commands`, `guidelines`, `personas`.
29
- - `--applied <kind>:<id>` — repeat per artefact actually applied.
30
- - Exit `0` always when disabled (silent). Exit `1` on schema validation
31
- failure (rule must NOT swallow this — surface to the user). Exit `2`
32
- on IO failure.
33
-
34
- ## Privacy contract — what NEVER goes into a record
35
-
36
- The CLI rejects most violations on the input boundary, but the agent must
37
- not even attempt these:
38
-
39
- - ❌ File paths (`src/Foo.php`, `tests/...`) — id fields are
40
- artefact identifiers only.
41
- - ❌ Source code, prompt text, ticket body, AC text.
42
- - ❌ Branch names, commit shas, PR numbers, URLs.
43
- - ❌ Secrets, env vars, credentials, customer data.
44
- - ❌ Free-text strings longer than 200 chars (CLI enforces; agent must
45
- not generate).
46
-
47
- When in doubt → **don't record**. A missing event is cheap; a leaked
48
- prompt is not.
49
-
50
- ## Failure modes — DO NOT block the user's task
51
-
52
- - Schema rejection (CLI exit `1`) → log the message internally, continue
53
- the user's task. Do **not** halt the dispatch loop.
54
- - IO failure (CLI exit `2`) → same. The telemetry is **observation**, not
55
- a delivery requirement.
56
- - Settings malformed → already handled by the CLI: it falls back to
57
- disabled and exits `0`. Agent treats it as "disabled this task".
58
-
59
- The only error the agent surfaces is when the user explicitly asked for
60
- recording (`telemetry:status` confirms enabled) but no event reached the
61
- log — that is a real bug, not a swallowed error.
62
-
63
- ## What this rule does NOT do
64
-
65
- - Run when `enabled: false` (cost floor is zero — see
66
- [`tests/telemetry/test_cost_floor.py`](../../../../tests/telemetry/test_cost_floor.py)).
67
- - Track the agent's tool calls, file reads, or token spend — that is
68
- out of scope, see the roadmap's "out-of-scope" section.
69
- - Decide retirement. Phase 4's aggregator + report renderer are the only
70
- consumers that may interpret the JSONL.
71
- - Run on cloud surfaces (Claude.ai Web, Skills API). The
72
- `cloud_safe: noop` marker keeps it inert there.
@@ -1,79 +0,0 @@
1
- # Augment portability — mechanics
2
-
3
- `task`-to-script translation table, the `./agent-config` consumer-CLI
4
- table, and the rationale behind both for the
5
- [`augment-portability`](../../../rules/augment-portability.md) rule.
6
- The portability obligation, the agnostic-content rules, and the
7
- enforcement script reference live in the rule; this file is the
8
- lookup material when an agent is about to write or edit an artefact
9
- that mentions a runtime invocation.
10
-
11
- ## Why no `task` commands inside artefacts
12
-
13
- Skills, rules, commands, guidelines, personas, and context docs
14
- shipped by this package run in **consumer projects**. Consumer
15
- projects may not have [Task](https://taskfile.dev) installed —
16
- they might use npm scripts, Composer scripts, Make, or nothing at
17
- all. A skill that instructs an agent to run `task <something>`
18
- silently breaks in every project without a `Taskfile.yml`.
19
-
20
- Task remains a convenience shortcut for maintainers working on the
21
- package repo itself — `task ci` is the recommended local gate before
22
- a PR and lives in `Taskfile.yml`, `AGENTS.md`, and the package README.
23
- Those maintainer-facing surfaces are outside the scope of this rule.
24
- Artefact files must assume Task is absent.
25
-
26
- The detection pattern *"if the consumer has a `Makefile` / build
27
- script, prefer its targets over raw commands"* is still allowed when
28
- the skill adapts to the **consumer's own** tooling (e.g.
29
- `tests-execute` detecting `php artisan test` vs `vendor/bin/pest`).
30
- It is not allowed to reference `task <name>` as the detected target —
31
- every direct invocation must resolve to a real script path.
32
-
33
- ## Translation table — `task` → script
34
-
35
- | ❌ Forbidden | ✅ Portable |
36
- |---|---|
37
- | `task sync` | `bash scripts/compress.sh --sync` |
38
- | `task sync-check` | `bash scripts/compress.sh --check` |
39
- | `task sync-check-hashes` | `bash scripts/compress.sh --check-hashes` |
40
- | `task sync-changed` | `bash scripts/compress.sh --changed` |
41
- | `task sync-mark-done -- X` | `bash scripts/compress.sh --mark-done X` |
42
- | `task generate-tools` | `python3 scripts/compress.py --generate-tools` |
43
- | `task lint-skills` | `python3 scripts/skill_linter.py --all` |
44
- | `task check-refs` | `python3 scripts/check_references.py` |
45
- | `task check-portability` | `python3 scripts/check_portability.py` |
46
- | `task check-compression` | `python3 scripts/check_compression.py` |
47
- | `task validate-schema` | `python3 scripts/validate_frontmatter.py` |
48
- | `task counts-check` | `python3 scripts/update_counts.py --check` |
49
- | `task roadmap-progress` | `./agent-config roadmap:progress` |
50
- | `task ci` | run each underlying script directly (no single portable equivalent) |
51
-
52
- ## Consumer CLI — `./agent-config`
53
-
54
- A subset of package scripts is exposed through a project-local CLI
55
- wrapper `./agent-config` (written into the project root by the
56
- installer, gitignored). Artefacts MUST prefer the CLI over raw
57
- `python3 scripts/…` paths for every command the CLI already covers,
58
- because the raw paths only resolve inside the package repo — in a
59
- consumer project the scripts live under `node_modules/` or `vendor/`.
60
-
61
- | ❌ Forbidden in artefacts | ✅ Portable |
62
- |---|---|
63
- | `python3 scripts/mcp_render.py` | `./agent-config mcp:render` |
64
- | `python3 scripts/mcp_render.py --check` | `./agent-config mcp:check` |
65
- | `python3 .augment/scripts/update_roadmap_progress.py` | `./agent-config roadmap:progress` |
66
- | `python3 .augment/scripts/update_roadmap_progress.py --check` | `./agent-config roadmap:progress-check` |
67
- | `bash scripts/first-run.sh` | `./agent-config first-run` |
68
- | `PYTHONPATH=… python3 -m implement_ticket` | `./agent-config implement-ticket` |
69
- | `python3 scripts/memory_lookup.py` | `./agent-config memory:lookup` |
70
- | `python3 scripts/memory_signal.py` | `./agent-config memory:signal` |
71
- | `python3 scripts/memory_hash.py` | `./agent-config memory:hash` |
72
- | `python3 scripts/check_memory.py` | `./agent-config memory:check` |
73
- | `python3 scripts/check_memory_proposal.py` | `./agent-config memory:check-proposal` |
74
- | `python3 scripts/check_proposal.py` | `./agent-config proposal:check` |
75
- | `python3 scripts/refine_ticket_detect.py` | `./agent-config refine-ticket:detect` |
76
-
77
- Commands not covered by the CLI stay as direct script invocations
78
- (e.g. `bash scripts/compress.sh --sync`) — those are maintainer-only
79
- and not reachable from a consumer project anyway.
@@ -1,87 +0,0 @@
1
- # CLI output handling — mechanics
2
-
3
- Codebase navigation tips, the redirect-summarize-target fallback
4
- pattern, the general rules around exit codes / summary lines, and the
5
- CLI-over-MCP catalog for the
6
- [`cli-output-handling`](../../../rules/cli-output-handling.md) rule.
7
- The Iron Law, the detection rule (`rtk_installed`), the verbose-command
8
- table, and the exceptions list live in the rule; this file is the
9
- lookup material when the agent has to fall back from `rtk`.
10
-
11
- ## Codebase Navigation
12
-
13
- ### Use what you already have
14
-
15
- - Edited a file → the edit tool already returned the result. Skip re-reading.
16
- - Ran a command → you have the output. Skip re-running to "verify".
17
- - File in context from recent messages → skip reloading.
18
- - Found a symbol → use it. Skip searching again differently.
19
-
20
- ### Search before reading
21
-
22
- - **Search first** — `codebase-retrieval`, `search_query_regex`, or `grep`.
23
- - **Load only what you need** — use `view_range` or `search_query_regex`, not full files.
24
- - **Small files** (< 50 lines) — OK to read fully.
25
-
26
- ### Ignored files (`.augmentignore`)
27
-
28
- - `vendor/`, `node_modules/`, lock files, and generated files are excluded from `codebase-retrieval`.
29
- - When you need to understand a vendor package (base class, interface, API), **read the specific file** with `view`. This bypasses the ignore.
30
- - Load only the file you need — never browse entire vendor directories.
31
-
32
- ### Minimize tool calls
33
-
34
- - **Parallel reads** — read multiple files in one batch, not sequentially.
35
- - **`search_query_regex`** over full file reads.
36
- - **`view_range`** when you know the exact lines.
37
- - **One `codebase-retrieval` call** with all symbols — batch, not 5 separate calls.
38
-
39
- ## Fallback Pattern: Redirect, Summarize, Target
40
-
41
- When `rtk` has no matching subcommand for the tool at hand, fall back to
42
- this pattern. Every command that MAY produce more than ~30 lines of output:
43
-
44
- ### Step 1: Redirect to file
45
- ```bash
46
- docker compose exec -T <service> <command> 2>&1 > /tmp/<tool>-output.txt
47
- echo "EXIT=$?"
48
- ```
49
-
50
- ### Step 2: Read ONLY the summary
51
- ```bash
52
- tail -5 /tmp/<tool>-output.txt
53
- ```
54
-
55
- ### Step 3: If errors exist, read ONLY what you need to fix
56
- ```bash
57
- grep "ERROR\|error\|✏️" /tmp/<tool>-output.txt | head -20
58
- grep "app/Services/MyService.php" /tmp/<tool>-output.txt
59
- ```
60
-
61
- **NEVER** do:
62
- - `cat /tmp/<tool>-output.txt` (loads everything)
63
- - Read the full output of a passing command (waste)
64
- - Read diffs you don't plan to act on
65
-
66
- ## General Rules
67
-
68
- For tool-specific commands → see the `quality-tools` skill.
69
-
70
- 1. **Exit code first**: Check `$?` before reading ANY output. If 0, you're done — skip reading.
71
- 2. **Summary line**: Most tools print a summary as the last few lines. That's all you need.
72
- 3. **Targeted grep**: When you need details, `grep` for the specific file or error type.
73
- 4. **Read once, act, move on**: Once you've read output and acted on it, skip re-reading.
74
- 5. **Iterative fixing**: Fix one error at a time, re-run, check exit code.
75
- Output becomes stale after each fix — always re-run before reading again.
76
-
77
- ## CLI Over MCP
78
-
79
- MCP servers are **significantly more token-expensive** than CLI equivalents.
80
- When both options exist, prefer the CLI tool.
81
-
82
- - **Git**: `git` CLI, not Git MCP
83
- - **Files**: shell commands, not filesystem MCP
84
- - **APIs**: `curl`/`httpie`, not HTTP MCP
85
- - **Database**: `mysql`/`psql` CLI, not DB MCP
86
-
87
- Exception: MCPs with **unique capabilities** (Sentry, Playwright, Jira).