@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.
- package/.agent-src/commands/analyze-reference-repo.md +3 -0
- package/.agent-src/commands/review-routing.md +7 -10
- package/.agent-src/commands/roadmap/process-full.md +41 -1
- package/.agent-src/contexts/authority/kernel-rule-edits.md +48 -0
- package/.agent-src/contexts/authority/scope-mechanics.md +15 -0
- package/.agent-src/contexts/contracts/consumer-agents-md-guide.md +127 -0
- package/.agent-src/contexts/contracts/emergency-triage-block.md +53 -0
- package/.agent-src/contexts/execution/roadmap-process-loop.md +29 -6
- package/.agent-src/rules/analysis-skill-routing.md +1 -1
- package/.agent-src/rules/artifact-drafting-protocol.md +1 -1
- package/.agent-src/rules/artifact-engagement-recording.md +1 -1
- package/.agent-src/rules/augment-source-of-truth.md +1 -1
- package/.agent-src/rules/autonomous-execution.md +1 -1
- package/.agent-src/rules/caveman-speak.md +1 -1
- package/.agent-src/rules/cli-output-handling.md +1 -1
- package/.agent-src/rules/command-suggestion-policy.md +1 -1
- package/.agent-src/rules/docs-sync.md +1 -1
- package/.agent-src/rules/guidelines.md +1 -1
- package/.agent-src/rules/improve-before-implement.md +1 -1
- package/.agent-src/rules/invite-challenge.md +1 -1
- package/.agent-src/rules/minimal-safe-diff.md +1 -1
- package/.agent-src/rules/model-recommendation.md +1 -1
- package/.agent-src/rules/no-attribution-footers.md +1 -1
- package/.agent-src/rules/no-roadmap-references.md +56 -20
- package/.agent-src/rules/onboarding-gate.md +1 -1
- package/.agent-src/rules/package-ci-checks.md +1 -1
- package/.agent-src/rules/reviewer-awareness.md +9 -2
- package/.agent-src/rules/roadmap-progress-sync.md +37 -3
- package/.agent-src/rules/scope-control.md +6 -0
- package/.agent-src/rules/security-sensitive-stop.md +1 -1
- package/.agent-src/rules/size-enforcement.md +1 -1
- package/.agent-src/rules/token-optimizer-maintenance.md +1 -1
- package/.agent-src/rules/ui-audit-gate.md +1 -1
- package/.agent-src/skills/adr-create/SKILL.md +2 -1
- package/.agent-src/skills/agents-md-thin-root/SKILL.md +125 -0
- package/.agent-src/skills/ai-council/SKILL.md +9 -7
- package/.agent-src/skills/learning-to-rule-or-skill/SKILL.md +9 -0
- package/.agent-src/skills/markitdown/SKILL.md +239 -0
- package/.agent-src/skills/review-routing/SKILL.md +3 -4
- package/.agent-src/skills/universal-project-analysis/SKILL.md +8 -0
- package/.agent-src/templates/AGENTS.md +18 -148
- package/.agent-src/templates/copilot-instructions.md +41 -17
- package/.agent-src/templates/github-workflows/pr-risk-review.yml +1 -1
- package/.agent-src/templates/scripts/pr_review_routing.py +1 -1
- package/.claude-plugin/marketplace.json +7 -5
- package/AGENTS.md +18 -205
- package/CHANGELOG.md +70 -0
- package/README.md +2 -2
- package/docs/architecture.md +13 -7
- package/docs/catalog.md +45 -29
- package/docs/contracts/agents-md-tech-stack.md +74 -0
- package/docs/contracts/linear-ai-rules-inclusion.md +1 -1
- package/docs/contracts/package-self-orientation.md +135 -0
- package/docs/contracts/rule-classification.md +4 -4
- package/docs/decisions/ADR-004-rule-governance-pruning.md +240 -0
- package/docs/getting-started.md +1 -1
- package/docs/guidelines/agent-infra/review-routing-data-format.md +1 -2
- package/package.json +1 -1
- package/scripts/_p4_migrate.py +5 -5
- package/scripts/audit_auto_rules.py +159 -0
- package/scripts/audit_likelihood.py +148 -0
- package/scripts/audit_overlap.py +145 -0
- package/scripts/build_rule_trigger_matrix.py +3 -5
- package/scripts/check_augment_description_cap.py +79 -0
- package/scripts/check_council_references.py +3 -3
- package/scripts/check_kernel_rule_bundle.py +151 -0
- package/scripts/check_references.py +21 -1
- package/scripts/compile_router.py +3 -0
- package/scripts/install.sh +0 -1
- package/scripts/lint_agents_md.py +168 -0
- package/scripts/measure_augment_budget.py +208 -0
- package/scripts/measure_markitdown_lift.py +127 -0
- package/scripts/schemas/rule.schema.json +2 -1
- package/scripts/skill_linter.py +10 -4
- package/scripts/spotcheck_thin_root.py +134 -0
- package/scripts/update_counts.py +6 -10
- package/.agent-src/rules/no-council-references.md +0 -76
- package/.agent-src/rules/review-routing-awareness.md +0 -19
- 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)
|
package/docs/getting-started.md
CHANGED
|
@@ -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
|
|
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
|
-
[`
|
|
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
package/scripts/_p4_migrate.py
CHANGED
|
@@ -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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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())
|