@event4u/agent-config 1.23.0 → 1.25.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 (79) hide show
  1. package/.agent-src/commands/analyze-reference-repo.md +3 -0
  2. package/.agent-src/commands/review-routing.md +7 -10
  3. package/.agent-src/commands/roadmap/process-full.md +41 -1
  4. package/.agent-src/contexts/authority/kernel-rule-edits.md +48 -0
  5. package/.agent-src/contexts/authority/scope-mechanics.md +15 -0
  6. package/.agent-src/contexts/contracts/consumer-agents-md-guide.md +127 -0
  7. package/.agent-src/contexts/contracts/emergency-triage-block.md +53 -0
  8. package/.agent-src/contexts/execution/roadmap-process-loop.md +29 -6
  9. package/.agent-src/rules/analysis-skill-routing.md +1 -1
  10. package/.agent-src/rules/artifact-drafting-protocol.md +1 -1
  11. package/.agent-src/rules/artifact-engagement-recording.md +1 -1
  12. package/.agent-src/rules/augment-source-of-truth.md +1 -1
  13. package/.agent-src/rules/autonomous-execution.md +1 -1
  14. package/.agent-src/rules/caveman-speak.md +1 -1
  15. package/.agent-src/rules/cli-output-handling.md +1 -1
  16. package/.agent-src/rules/command-suggestion-policy.md +1 -1
  17. package/.agent-src/rules/docs-sync.md +1 -1
  18. package/.agent-src/rules/guidelines.md +1 -1
  19. package/.agent-src/rules/improve-before-implement.md +1 -1
  20. package/.agent-src/rules/invite-challenge.md +1 -1
  21. package/.agent-src/rules/minimal-safe-diff.md +1 -1
  22. package/.agent-src/rules/model-recommendation.md +1 -1
  23. package/.agent-src/rules/no-attribution-footers.md +1 -1
  24. package/.agent-src/rules/no-roadmap-references.md +56 -20
  25. package/.agent-src/rules/onboarding-gate.md +1 -1
  26. package/.agent-src/rules/package-ci-checks.md +1 -1
  27. package/.agent-src/rules/reviewer-awareness.md +9 -2
  28. package/.agent-src/rules/roadmap-progress-sync.md +37 -3
  29. package/.agent-src/rules/scope-control.md +6 -0
  30. package/.agent-src/rules/security-sensitive-stop.md +1 -1
  31. package/.agent-src/rules/size-enforcement.md +1 -1
  32. package/.agent-src/rules/token-optimizer-maintenance.md +1 -1
  33. package/.agent-src/rules/ui-audit-gate.md +1 -1
  34. package/.agent-src/skills/adr-create/SKILL.md +2 -1
  35. package/.agent-src/skills/agents-md-thin-root/SKILL.md +125 -0
  36. package/.agent-src/skills/ai-council/SKILL.md +9 -7
  37. package/.agent-src/skills/learning-to-rule-or-skill/SKILL.md +9 -0
  38. package/.agent-src/skills/markitdown/SKILL.md +239 -0
  39. package/.agent-src/skills/review-routing/SKILL.md +3 -4
  40. package/.agent-src/skills/universal-project-analysis/SKILL.md +8 -0
  41. package/.agent-src/templates/AGENTS.md +18 -148
  42. package/.agent-src/templates/copilot-instructions.md +41 -17
  43. package/.agent-src/templates/github-workflows/pr-risk-review.yml +1 -1
  44. package/.agent-src/templates/scripts/pr_review_routing.py +1 -1
  45. package/.claude-plugin/marketplace.json +7 -5
  46. package/AGENTS.md +18 -205
  47. package/CHANGELOG.md +70 -0
  48. package/README.md +2 -2
  49. package/docs/architecture.md +13 -7
  50. package/docs/catalog.md +45 -29
  51. package/docs/contracts/agents-md-tech-stack.md +74 -0
  52. package/docs/contracts/linear-ai-rules-inclusion.md +1 -1
  53. package/docs/contracts/package-self-orientation.md +135 -0
  54. package/docs/contracts/rule-classification.md +4 -4
  55. package/docs/decisions/ADR-004-rule-governance-pruning.md +240 -0
  56. package/docs/getting-started.md +1 -1
  57. package/docs/guidelines/agent-infra/review-routing-data-format.md +1 -2
  58. package/package.json +1 -1
  59. package/scripts/_p4_migrate.py +5 -5
  60. package/scripts/audit_auto_rules.py +159 -0
  61. package/scripts/audit_likelihood.py +148 -0
  62. package/scripts/audit_overlap.py +145 -0
  63. package/scripts/build_rule_trigger_matrix.py +3 -5
  64. package/scripts/check_augment_description_cap.py +79 -0
  65. package/scripts/check_council_references.py +3 -3
  66. package/scripts/check_kernel_rule_bundle.py +151 -0
  67. package/scripts/check_references.py +21 -1
  68. package/scripts/compile_router.py +3 -0
  69. package/scripts/install.sh +0 -1
  70. package/scripts/lint_agents_md.py +168 -0
  71. package/scripts/measure_augment_budget.py +208 -0
  72. package/scripts/measure_markitdown_lift.py +127 -0
  73. package/scripts/schemas/rule.schema.json +2 -1
  74. package/scripts/skill_linter.py +10 -4
  75. package/scripts/spotcheck_thin_root.py +134 -0
  76. package/scripts/update_counts.py +6 -10
  77. package/.agent-src/rules/no-council-references.md +0 -76
  78. package/.agent-src/rules/review-routing-awareness.md +0 -19
  79. package/.agent-src/templates/copilot-review-instructions.md +0 -76
@@ -0,0 +1,240 @@
1
+ ---
2
+ adr: 004
3
+ status: accepted
4
+ date: 2026-05-08
5
+ decision: rule-governance-pruning
6
+ supersedes: —
7
+ superseded_by: —
8
+ phase: road-to-augment-limit-fit · P5.5
9
+ ---
10
+
11
+ # ADR-004 — Rule-Governance Pruning (Phase 5)
12
+
13
+ ## Status
14
+
15
+ **Accepted** · 2026-05-08.
16
+
17
+ ## Context
18
+
19
+ `road-to-augment-limit-fit` Phase 5 ran a Rule-Governance Audit on
20
+ the 49 `type: auto` rules that consume the workspace-guidelines
21
+ budget via description-stub injection (~250 chars each). Three
22
+ analytic passes ran:
23
+
24
+ - **5.1** `scripts/audit_auto_rules.py` — measured stub vs. body
25
+ cost. Auto-rule stubs total **11,513 chars · 23.3 %** of the
26
+ 49,512 ceiling.
27
+ - **5.2** `scripts/audit_overlap.py` — pairwise Jaccard on
28
+ `path_prefix` triggers + symmetric keyword overlap on
29
+ description/keyword/intent token sets. 4 pairs flagged
30
+ (path-Jaccard ≥ 0.5 OR keyword-overlap ≥ 0.4).
31
+ - **5.3** `scripts/audit_likelihood.py` — corpus-keyword scoring
32
+ against indexed skills + commands + contexts + guidelines. 0
33
+ rules below the strict `< 2` hits floor; bottom-10 list surfaced
34
+ for council walk.
35
+
36
+ **5.4** AI Council R3 (claude-sonnet-4-5 + gpt-4o, 2 rounds, prompt
37
+ mode) walked the candidate list. Council convergence and host
38
+ verdicts are recorded in `agents/reports/auto-rules-audit.md`
39
+ § Phase 5.4. The dominant council insight:
40
+
41
+ > *"Rarity ≠ redundancy. Low corpus hits often indicate a
42
+ > preventative rule that fires precisely when needed, not a useless
43
+ > one. The audit cannot distinguish dead weight from option-value
44
+ > insurance."* — claude-sonnet-4-5
45
+
46
+ This narrowed the action surface from "remove anything in the
47
+ bottom 10" to four targeted decisions where redundancy or
48
+ mechanical-already status was structurally provable.
49
+
50
+ ## Decision
51
+
52
+ Four approved actions, applied in Phase 5.6:
53
+
54
+ ### Implementation pattern: demote via frontmatter
55
+
56
+ All four actions use the same mechanical pattern: change rule
57
+ frontmatter `type: "auto"` → `type: "manual"`. This:
58
+
59
+ - **Removes the stub from the workspace budget.**
60
+ `scripts/measure_augment_budget.py` only counts `type: "auto"`
61
+ rules (line 99). Anything else has zero stub cost.
62
+ - **Preserves the file and its cross-references.**
63
+ Skills, contexts, templates, and contracts that link to the rule
64
+ (`size-enforcement` is referenced from `rule-writing/SKILL.md`,
65
+ `command-writing/SKILL.md`, `artifact-drafting-protocol.md`,
66
+ `proposal.example.md`, `rule-classification.md`,
67
+ `self-improvement-pipeline.md`, etc.) keep working.
68
+ - **Keeps `compile_router.py` deterministic.**
69
+ Non-auto rules are skipped by `_resolve_tier`; the rule no
70
+ longer routes through `router.json` but remains a reference
71
+ document.
72
+
73
+ This pattern was chosen over hard deletion after the
74
+ cross-reference audit (Phase 5.6 prep) showed each candidate has
75
+ ≥ 5 inbound references. Deletion would force a wide-radius
76
+ documentation rewrite for marginal additional savings.
77
+
78
+ ### 1. `guidelines` — demote
79
+
80
+ - **Verdict:** demote (`type: "auto"` → `type: "manual"`).
81
+ - **Rationale:** Generic name, no `routes_to:` target, 552 corpus
82
+ hits, no `path_prefix` trigger. Description ("Writing or
83
+ reviewing code — check relevant guideline before writing or
84
+ reviewing code") is ambient guidance already covered by the 9
85
+ always-rules and the Iron-Law floor. The auto-stub adds ~185
86
+ chars of overhead for a rule that fires on every code touch but
87
+ has no specific routing target — i.e., it functions as a noop
88
+ reminder. The body remains as a reference doc citing the
89
+ guidelines-mechanics context.
90
+ - **Preserved triggers:** the body retains its trigger discussion
91
+ for human readers; auto-discovery is dropped.
92
+ - **Migration:** none. Inbound link in
93
+ `contexts/communication/rules-auto/guidelines-mechanics.md`
94
+ remains valid.
95
+
96
+ ### 2. `size-enforcement` — demote (logical merge into `rule-type-governance`)
97
+
98
+ - **Verdict:** demote (`type: "auto"` → `type: "manual"`); record
99
+ the logical merge in this ADR rather than physically folding the
100
+ body. `rule-type-governance` already routes to its own
101
+ guideline; both rules now share the same auto-trigger surface
102
+ conceptually but only `rule-type-governance` injects a stub.
103
+ - **Rationale:** Both rules fire during rule/skill/command
104
+ authoring. `size-enforcement` enforces character budgets;
105
+ `rule-type-governance` enforces always-vs-auto classification.
106
+ Council R3 convergence on the merge. Physical body-fold rejected
107
+ to keep blast radius small (see implementation-pattern note).
108
+ - **Preserved triggers:** "size", "budget", "limit", "char count",
109
+ artefact creation/editing scope — all retained in the rule body
110
+ for ad-hoc consultation; the auto-discovery surface is dropped.
111
+ - **Migration:** none required. `rule-writing/SKILL.md`,
112
+ `command-writing/SKILL.md`, `artifact-drafting-protocol.md`,
113
+ `proposal.example.md`, and the contracts continue to cite the
114
+ rule by file path; the file still exists.
115
+
116
+ ### 3. `package-ci-checks` — demote
117
+
118
+ - **Verdict:** demote (`type: "auto"` → `type: "manual"`).
119
+ - **Rationale:** Rule is `mechanical-already` — `task ci` already
120
+ enforces the same checks before a PR can merge. The rule is also
121
+ package-self-referential (it only fires when contributing to
122
+ `event4u/agent-config` itself); consumer projects never benefit
123
+ from the stub. Council R3: gpt-4o (demote), Sonnet (verify skill
124
+ links contract first — confirmed: `routes_to: skill:lint-skills`).
125
+ - **Preserved triggers:** "task ci", "before push", "before pr".
126
+ Body retains trigger phrases for human reference.
127
+ - **Migration:** AGENTS.md "Working on this repo" section already
128
+ documents `task ci`; no consumer-project regression because
129
+ consumers never received this rule's stub anyway (`source: package`).
130
+
131
+ ### 4. `analysis-skill-routing` — demote
132
+
133
+ - **Verdict:** demote (`type: "auto"` → `type: "manual"`).
134
+ - **Rationale:** The rule's only function is to point host agents
135
+ at the `analysis-skill-router` skill when an analysis request
136
+ fires. The skill's own description carries the same trigger
137
+ surface ("picking which analysis or project-analysis-* skill
138
+ fits a request") and is already auto-discoverable as a Skill.
139
+ The rule is a redundant pointer-to-pointer. Council R3:
140
+ gpt-4o (merge into slash-command-routing-policy — host rejected:
141
+ analysis ≠ slash-commands; merge would collapse a meaningful
142
+ category distinction). Sonnet (keep + add `routes_to:` — host
143
+ rejected: routing already exists via the skill itself).
144
+ - **Preserved triggers:** "analyze", "analysis", "dig into the
145
+ codebase". Already present in the skill's description.
146
+ - **Migration:** Verify `analysis-skill-router` skill description
147
+ is pushy enough; no other action required.
148
+
149
+ ## Consequences
150
+
151
+ ### Accepted
152
+
153
+ - **Stub-cost saving:** ~849 chars freed (~1.7 % of the 49,512 cap).
154
+ Phase 5 alone is insufficient to hit the 20 % headroom goal.
155
+ - **Phase 6 (Thin-Root AGENTS.md) was mandatory**, not optional.
156
+ AGENTS.md was the largest single asset (12,042 chars) and the
157
+ only remaining lever once Phase 5 was locked in.
158
+ - **Four rules (`guidelines`, `size-enforcement`,
159
+ `package-ci-checks`, `analysis-skill-routing`) demoted from
160
+ `type: "auto"` to `type: "manual"`.** Total auto-rules: 49 → 45.
161
+ Files preserved on disk; cross-references intact.
162
+ - **Final budget after Phases 5–7** (`scripts/measure_augment_budget.py`,
163
+ 2026-05-08): AGENTS.md 2,773 + always-rules (9) 26,322 + auto-rule
164
+ stubs (45) 10,664 = **39,759 chars · 80.3 % utilisation · 19.7 %
165
+ headroom** (149 chars / 0.3 % short of the ≥ 20 % goal — within
166
+ rounding; effectively at target). Phase 6 (Thin-Root AGENTS.md
167
+ refactor: 12,042 → 2,773 chars) carried the bulk of the saving
168
+ that Phase 5 alone could not deliver. Phase 7 (`scripts/lint_agents_md.py`,
169
+ CI-blocking) locks the contract in.
170
+
171
+ ### Trade-offs
172
+
173
+ - **Sonnet's "rarity ≠ redundancy" critique honored.** The audit
174
+ identified 14 candidates; only 4 pass the host's redundancy /
175
+ mechanical-already test. Aggressive ceiling (~2,750 chars, per
176
+ gpt-4o) was rejected as it would force domain-specific rules
177
+ (`docker-commands`, `laravel-translations`) into manual
178
+ guideline-load workflows for the 20 % of projects that need them.
179
+ - **`upstream-proposal` and `slash-command-routing-policy` both
180
+ retained without `routes_to:` fix.** Flagged as follow-up work
181
+ outside this ADR's scope; their preservation is justified by
182
+ rare-but-critical activation pattern.
183
+ - **No `augment-portability` / `docs-sync` merge** despite 1.00
184
+ path-Jaccard. Council R3 convergence: workspace-layout
185
+ coincidence, not logical duplication. Different intents
186
+ (host-portability vs. sync-workflow hygiene).
187
+
188
+ ## Re-evaluation trigger
189
+
190
+ - Augment changes its accounting model (e.g. starts injecting
191
+ auto-rule bodies into the workspace prompt) → re-open this
192
+ governance pass; the stub-cost / body-cost ratio changes
193
+ drastically.
194
+ - Auto-rule count grows by ≥ 5 (history check via
195
+ `agents/.augment-budget-history.jsonl`) → repeat the audit
196
+ with the same three-pass methodology.
197
+ - A retained rule (`upstream-proposal`, `slash-command-routing-policy`,
198
+ `analysis-skill-routing`'s sibling skill) shows a 30-day window
199
+ with zero documented activation in `agents/council-sessions/` →
200
+ open a follow-up ADR demoting it.
201
+
202
+ ## Alternatives considered
203
+
204
+ - **Aggressive prune (gpt-4o R1 line):** demote
205
+ `agent-docs`, `docker-commands`, `laravel-translations`. Rejected.
206
+ Sonnet's rarity-≠-redundancy critique applies; these are
207
+ domain-specific rules with substantial corpus presence (240+
208
+ hits each) and their absence forces consumer projects into
209
+ manual guideline-load. The ~750-char additional saving is not
210
+ worth the behavioural regression.
211
+ - **`augment-portability` / `docs-sync` merge (gpt-4o R2):**
212
+ Rejected. 1.00 path-Jaccard reflects shared filesystem
213
+ triggers (`docs/guides/agent-setup/augment.md`), not shared
214
+ intent. `augment-portability` enforces project-agnostic content
215
+ in `.agent-src/`; `docs-sync` enforces cross-reference integrity
216
+ on add/rename/delete. Different verbs, different blast radii.
217
+ - **Defer all rule changes; focus on Phase 6 only:** Rejected.
218
+ The four approved actions are independently defensible; bundling
219
+ them with Phase 6 would obscure the rationale and conflate two
220
+ different optimisation strategies (rule-set hygiene vs.
221
+ AGENTS.md restructure). Each phase ships its own savings on its
222
+ own audit trail.
223
+
224
+ ## References
225
+
226
+ - `agents/roadmaps/archive/road-to-augment-limit-fit.md` § Phase 5
227
+ - `agents/reports/auto-rules-audit.md` (full audit findings,
228
+ council walk, host verdicts)
229
+ - `agents/reports/auto-rules-overlap.json` (Phase 5.2 data)
230
+ - `agents/reports/auto-rules-likelihood.json` (Phase 5.3 data)
231
+ - `agents/council-questions/augment-limit-fit-rule-governance.md`
232
+ (Phase 5.4 prompt)
233
+ - `agents/council-responses/augment-limit-fit-rule-governance.json`
234
+ (Phase 5.4 R3 raw debate) <!-- council-ref-allowed: ADR decision trace -->
235
+ - `docs/decisions/ADR-rule-kernel-and-router.md` (kernel-membership
236
+ contract — Phase 5 changes leave kernel untouched per Lever C lock)
237
+ - `.agent-src.uncompressed/rules/guidelines.md` (deprecated subject)
238
+ - `.agent-src.uncompressed/rules/size-enforcement.md` (merged subject)
239
+ - `.agent-src.uncompressed/rules/package-ci-checks.md` (demoted subject)
240
+ - `.agent-src.uncompressed/rules/analysis-skill-routing.md` (demoted subject)
@@ -115,7 +115,7 @@ Your agent is now:
115
115
  - **Respecting your codebase** — no conflicting patterns
116
116
  - **Following standards** — consistent code quality
117
117
 
118
- This is enforced automatically by 60 rules. No configuration needed.
118
+ This is enforced automatically by 58 rules. No configuration needed.
119
119
 
120
120
  ---
121
121
 
@@ -1,7 +1,7 @@
1
1
  # Review Routing Data Format
2
2
 
3
3
  Schema and conventions for the two project-local YAML files that feed the
4
- [`review-routing-awareness`](../../rules/review-routing-awareness.md) rule
4
+ [`reviewer-awareness`](../../rules/reviewer-awareness.md) rule
5
5
  and the [`review-routing`](../../skills/review-routing/SKILL.md) skill.
6
6
 
7
7
  Both files are **optional** and live in the consumer repository — never
@@ -139,6 +139,5 @@ Field semantics:
139
139
 
140
140
  ## See also
141
141
 
142
- - [`review-routing-awareness`](../../rules/review-routing-awareness.md)
143
142
  - [`reviewer-awareness`](../../rules/reviewer-awareness.md)
144
143
  - [`review-routing`](../../skills/review-routing/SKILL.md)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@event4u/agent-config",
3
- "version": "1.23.0",
3
+ "version": "1.25.0",
4
4
  "description": "Shared agent configuration \u2014 skills, rules, commands, guidelines, and templates for AI coding tools",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -52,12 +52,12 @@ SKILL_MIGRATIONS = [
52
52
  ("package-ci-checks", "skill:lint-skills",
53
53
  [("phrase", "task ci"), ("phrase", "before push"), ("phrase", "before pr")],
54
54
  "Run `task ci` locally and confirm green before pushing or opening a PR in this package."),
55
- ("review-routing-awareness", "skill:review-routing",
56
- [("keyword", "reviewer"), ("phrase", "risk hotspot"), ("phrase", "ownership map")],
57
- "Consult ownership-map and historical-bug-patterns before suggesting reviewers or claiming a change is safe."),
55
+ # review-routing-awareness was merged into reviewer-awareness on 2026-05-08
56
+ # (see agents/contexts/adr-auto-rule-consolidation.md) as part of the
57
+ # Augment literal-budget relief work Lever D consolidation.
58
58
  ("reviewer-awareness", "skill:review-routing",
59
- [("keyword", "reviewer"), ("phrase", "suggest reviewers")],
60
- "Anchor reviewer choice in paths and risk, never seniority; medium / high risk requires primary + secondary role."),
59
+ [("keyword", "reviewer"), ("phrase", "suggest reviewers"), ("phrase", "risk hotspot"), ("phrase", "ownership map")],
60
+ "Anchor reviewer choice in paths and risk, never seniority; consult ownership-map + historical-bug-patterns; medium / high risk requires primary + secondary role."),
61
61
  ("skill-improvement-trigger", "skill:skill-improvement-pipeline",
62
62
  [("phrase", "after completing"), ("keyword", "improvement"), ("keyword", "pipeline")],
63
63
  "After a meaningful task, trigger the post-task learning capture if `pipelines.skill_improvement` is enabled."),
@@ -0,0 +1,159 @@
1
+ #!/usr/bin/env python3
2
+ """Audit auto-rules for the Rule-Governance pass (Phase 5.1 of
3
+ road-to-augment-limit-fit).
4
+
5
+ Walk `.agent-src.uncompressed/rules/*.md`, collect per-rule frontmatter
6
+ (`description`, `triggers`, `routes_to`, `tier`), measure body and
7
+ registry-stub costs, and emit:
8
+
9
+ - `agents/reports/auto-rules-audit.json` — deterministic, machine-readable.
10
+ - `agents/reports/auto-rules-audit.md` — ranked summary for review.
11
+
12
+ The registry-stub cost mirrors `scripts/measure_augment_budget.py`'s
13
+ accounting model: only the stub line is injected into the Augment
14
+ workspace-guidelines budget for `type: auto` rules. The body cost is
15
+ informational — it ships in the projected `.augment/rules/` tree but
16
+ does NOT count against the 49,512-char ceiling.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import json
22
+ import sys
23
+ from pathlib import Path
24
+
25
+ import yaml
26
+
27
+ REPO_ROOT = Path(__file__).resolve().parent.parent
28
+ SRC_RULES = REPO_ROOT / ".agent-src.uncompressed" / "rules"
29
+ PROJECTED_RULES = REPO_ROOT / ".augment" / "rules"
30
+ REPORT_DIR = REPO_ROOT / "agents" / "reports"
31
+ JSON_OUT = REPORT_DIR / "auto-rules-audit.json"
32
+ MD_OUT = REPORT_DIR / "auto-rules-audit.md"
33
+
34
+ # Stub Augment injects per auto-rule. Mirrors measure_augment_budget.STUB_TEMPLATE.
35
+ STUB_TEMPLATE = (
36
+ 'If the user prompt matches the description "{desc}", '
37
+ "read the file located in {path}"
38
+ )
39
+
40
+
41
+ def _split_frontmatter(text: str) -> tuple[dict, str]:
42
+ if not text.startswith("---\n"):
43
+ return {}, text
44
+ end = text.find("\n---", 4)
45
+ if end < 0:
46
+ return {}, text
47
+ fm = yaml.safe_load(text[4:end]) or {}
48
+ body = text[end + 4 :].lstrip("\n")
49
+ return fm, body
50
+
51
+
52
+ def _trigger_summary(triggers: list) -> dict:
53
+ paths: list[str] = []
54
+ keywords: list[str] = []
55
+ intents: list[str] = []
56
+ for entry in triggers or []:
57
+ if not isinstance(entry, dict):
58
+ continue
59
+ if "path_prefix" in entry:
60
+ paths.append(str(entry["path_prefix"]))
61
+ if "keyword" in entry:
62
+ keywords.append(str(entry["keyword"]))
63
+ if "intent" in entry:
64
+ intents.append(str(entry["intent"]))
65
+ return {"path_prefixes": paths, "keywords": keywords, "intents": intents}
66
+
67
+
68
+ def collect() -> list[dict]:
69
+ rules: list[dict] = []
70
+ for path in sorted(SRC_RULES.glob("*.md")):
71
+ text = path.read_text(encoding="utf-8")
72
+ fm, body = _split_frontmatter(text)
73
+ if fm.get("type") != "auto":
74
+ continue
75
+ desc = (fm.get("description") or "").strip()
76
+ rel_projected = f".augment/rules/{path.name}"
77
+ stub = STUB_TEMPLATE.format(desc=desc, path=rel_projected)
78
+ triggers = _trigger_summary(fm.get("triggers") or [])
79
+ routes_to = list(fm.get("routes_to") or [])
80
+ rules.append(
81
+ {
82
+ "name": path.stem,
83
+ "src_path": str(path.relative_to(REPO_ROOT)),
84
+ "tier": fm.get("tier"),
85
+ "description": desc,
86
+ "description_chars": len(desc),
87
+ "triggers": triggers,
88
+ "trigger_count": (
89
+ len(triggers["path_prefixes"])
90
+ + len(triggers["keywords"])
91
+ + len(triggers["intents"])
92
+ ),
93
+ "routes_to": routes_to,
94
+ "body_chars": len(body),
95
+ "file_chars": len(text),
96
+ "stub_chars": len(stub),
97
+ }
98
+ )
99
+ return rules
100
+
101
+
102
+ def render_markdown(rules: list[dict]) -> str:
103
+ total_stub = sum(r["stub_chars"] for r in rules)
104
+ total_body = sum(r["body_chars"] for r in rules)
105
+ total_desc = sum(r["description_chars"] for r in rules)
106
+ lines = [
107
+ "# Auto-Rule Audit",
108
+ "",
109
+ "Generated by `scripts/audit_auto_rules.py` for Phase 5 of",
110
+ "`agents/roadmaps/road-to-augment-limit-fit.md`. Re-run after",
111
+ "any rule add/merge/deprecate to refresh the baseline.",
112
+ "",
113
+ "## Totals",
114
+ "",
115
+ f"- auto-rules: **{len(rules)}**",
116
+ f"- registry-stub cost (counts against 49,512 cap): **{total_stub:,}** chars",
117
+ f"- description chars (subset of stub cost): **{total_desc:,}** chars",
118
+ f"- body chars (informational, NOT in budget): **{total_body:,}** chars",
119
+ "",
120
+ "## Ranked by registry-stub cost",
121
+ "",
122
+ "| # | Rule | Tier | Desc | Stub | Body | Triggers | Routes |",
123
+ "|---|------|------|------|------|------|----------|--------|",
124
+ ]
125
+ for i, r in enumerate(sorted(rules, key=lambda x: -x["stub_chars"]), 1):
126
+ triggers = (
127
+ f"{len(r['triggers']['path_prefixes'])}p / "
128
+ f"{len(r['triggers']['keywords'])}k / "
129
+ f"{len(r['triggers']['intents'])}i"
130
+ )
131
+ routes = ", ".join(r["routes_to"]) or "—"
132
+ lines.append(
133
+ f"| {i} | `{r['name']}` | {r['tier'] or '—'} | "
134
+ f"{r['description_chars']} | {r['stub_chars']} | "
135
+ f"{r['body_chars']} | {triggers} | {routes} |"
136
+ )
137
+ lines.append("")
138
+ lines.append("Trigger key: `Np` = path-prefix, `Nk` = keyword, `Ni` = intent.")
139
+ lines.append("")
140
+ return "\n".join(lines)
141
+
142
+
143
+ def main() -> int:
144
+ if not SRC_RULES.is_dir():
145
+ print(f"❌ Missing source dir: {SRC_RULES}", file=sys.stderr)
146
+ return 1
147
+ rules = collect()
148
+ REPORT_DIR.mkdir(parents=True, exist_ok=True)
149
+ payload = {"rule_count": len(rules), "rules": rules}
150
+ JSON_OUT.write_text(json.dumps(payload, indent=2, sort_keys=True), encoding="utf-8")
151
+ MD_OUT.write_text(render_markdown(rules), encoding="utf-8")
152
+ print(f"✅ Audited {len(rules)} auto-rules.")
153
+ print(f" JSON: {JSON_OUT.relative_to(REPO_ROOT)}")
154
+ print(f" MD: {MD_OUT.relative_to(REPO_ROOT)}")
155
+ return 0
156
+
157
+
158
+ if __name__ == "__main__":
159
+ sys.exit(main())
@@ -0,0 +1,148 @@
1
+ #!/usr/bin/env python3
2
+ """Activation-likelihood heuristic for the Rule-Governance pass
3
+ (Phase 5.3 of road-to-augment-limit-fit).
4
+
5
+ For every auto-rule from `agents/reports/auto-rules-audit.json`:
6
+
7
+ 1. Build a token set from `description`, `triggers[].keyword`,
8
+ `triggers[].intent`, and the rule name itself.
9
+ 2. Index a corpus of skills (`SKILL.md`), contexts
10
+ (`agents/contexts/**/*.md`), guidelines, and command files.
11
+ 3. Score `corpus_hits = sum(1 for token in tokens if token in corpus)`.
12
+ 4. Flag rules with `< 2` corpus hits as "low-likelihood" (their trigger
13
+ surface is so generic that the host LLM is unlikely to find a
14
+ project-local file the rule was written to bridge to).
15
+
16
+ Result is a JSON dump + Markdown section appended to
17
+ `agents/reports/auto-rules-audit.md`.
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ import json
23
+ import re
24
+ import sys
25
+ from collections import Counter
26
+ from pathlib import Path
27
+
28
+ REPO_ROOT = Path(__file__).resolve().parent.parent
29
+ REPORT_DIR = REPO_ROOT / "agents" / "reports"
30
+ AUDIT_JSON = REPORT_DIR / "auto-rules-audit.json"
31
+ AUDIT_MD = REPORT_DIR / "auto-rules-audit.md"
32
+ LIKELIHOOD_JSON = REPORT_DIR / "auto-rules-likelihood.json"
33
+
34
+ CORPUS_GLOBS = [
35
+ ".agent-src.uncompressed/skills/**/SKILL.md",
36
+ ".agent-src.uncompressed/commands/**/*.md",
37
+ "agents/contexts/**/*.md",
38
+ "docs/guidelines/**/*.md",
39
+ ]
40
+
41
+ LOW_LIKELIHOOD_HITS = 2
42
+
43
+ STOPWORDS = {
44
+ "the", "and", "for", "with", "when", "use", "or", "of", "to", "a",
45
+ "an", "is", "in", "on", "by", "be", "at", "as", "it", "if", "are",
46
+ "this", "that", "from", "but", "not", "can", "any", "all", "no",
47
+ "after", "before", "during", "user", "agent", "code", "project",
48
+ "via", "into", "onto", "even", "without", "naming", "rule", "rules",
49
+ "skill", "skills", "command", "commands", "files", "file", "doc",
50
+ "docs", "md", "txt",
51
+ }
52
+
53
+
54
+ def tokens(text: str) -> set[str]:
55
+ raw = re.findall(r"[A-Za-z][A-Za-z0-9_-]{2,}", text.lower())
56
+ return {t for t in raw if t not in STOPWORDS and len(t) > 3}
57
+
58
+
59
+ def build_corpus() -> Counter:
60
+ counter: Counter = Counter()
61
+ for glob in CORPUS_GLOBS:
62
+ for path in REPO_ROOT.glob(glob):
63
+ if not path.is_file():
64
+ continue
65
+ try:
66
+ text = path.read_text(encoding="utf-8")
67
+ except UnicodeDecodeError:
68
+ continue
69
+ for tok in tokens(text):
70
+ counter[tok] += 1
71
+ return counter
72
+
73
+
74
+ def score(rule: dict, corpus: Counter) -> dict:
75
+ rule_tokens = (
76
+ tokens(rule["description"])
77
+ | tokens(rule["name"].replace("-", " "))
78
+ | tokens(" ".join(rule["triggers"]["keywords"]))
79
+ | tokens(" ".join(rule["triggers"]["intents"]))
80
+ )
81
+ hits = {t: corpus[t] for t in rule_tokens if corpus[t] > 0}
82
+ return {
83
+ "name": rule["name"],
84
+ "tokens": sorted(rule_tokens),
85
+ "hits": dict(sorted(hits.items(), key=lambda x: -x[1])[:8]),
86
+ "hit_count": len(hits),
87
+ "total_hit_volume": sum(hits.values()),
88
+ "low_likelihood": len(hits) < LOW_LIKELIHOOD_HITS,
89
+ }
90
+
91
+
92
+ def render_md(scores: list[dict]) -> str:
93
+ flagged = [s for s in scores if s["low_likelihood"]]
94
+ lines = [
95
+ "",
96
+ "## Phase 5.3 — Activation likelihood (corpus-keyword)",
97
+ "",
98
+ f"Corpus: skills + commands + contexts + guidelines.",
99
+ f"Low-likelihood threshold: `< {LOW_LIKELIHOOD_HITS}` distinct corpus hits.",
100
+ "",
101
+ f"Rules flagged: **{len(flagged)} / {len(scores)}**.",
102
+ "",
103
+ "### Low-likelihood rules",
104
+ "",
105
+ ]
106
+ if not flagged:
107
+ lines += ["_None._", ""]
108
+ else:
109
+ lines += ["| Rule | Hits | Tokens (top) |", "|------|------|--------------|"]
110
+ for s in sorted(flagged, key=lambda x: x["hit_count"]):
111
+ toks = ", ".join(f"`{t}`" for t in s["tokens"][:6]) or "—"
112
+ lines.append(f"| `{s['name']}` | {s['hit_count']} | {toks} |")
113
+ lines.append("")
114
+ lines += [
115
+ "### Full ranking (lowest hit-count first, top 20)",
116
+ "",
117
+ "| Rule | Distinct hits | Total hit volume |",
118
+ "|------|---------------|------------------|",
119
+ ]
120
+ for s in sorted(scores, key=lambda x: (x["hit_count"], x["total_hit_volume"]))[:20]:
121
+ lines.append(f"| `{s['name']}` | {s['hit_count']} | {s['total_hit_volume']} |")
122
+ lines.append("")
123
+ return "\n".join(lines)
124
+
125
+
126
+ def main() -> int:
127
+ if not AUDIT_JSON.exists():
128
+ print(f"❌ Run audit_auto_rules.py first: missing {AUDIT_JSON}", file=sys.stderr)
129
+ return 1
130
+ rules = json.loads(AUDIT_JSON.read_text(encoding="utf-8"))["rules"]
131
+ corpus = build_corpus()
132
+ scores = [score(r, corpus) for r in rules]
133
+ LIKELIHOOD_JSON.write_text(
134
+ json.dumps({"corpus_size": len(corpus), "scores": scores}, indent=2),
135
+ encoding="utf-8",
136
+ )
137
+ md = AUDIT_MD.read_text(encoding="utf-8") if AUDIT_MD.exists() else ""
138
+ if "## Phase 5.3 — Activation likelihood" in md:
139
+ md = md.split("## Phase 5.3 — Activation likelihood")[0].rstrip() + "\n"
140
+ AUDIT_MD.write_text(md + render_md(scores), encoding="utf-8")
141
+ flagged = [s for s in scores if s["low_likelihood"]]
142
+ print(f"✅ Likelihood scored: {len(scores)} rules, {len(flagged)} low-likelihood.")
143
+ print(f" JSON: {LIKELIHOOD_JSON.relative_to(REPO_ROOT)}")
144
+ return 0
145
+
146
+
147
+ if __name__ == "__main__":
148
+ sys.exit(main())