@iseoane/agent-depot 0.2.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/CHANGELOG.md +30 -0
- package/LICENSE +21 -0
- package/README.md +465 -0
- package/package.json +69 -0
- package/skills/doctor-md-agents/README.md +51 -0
- package/skills/doctor-md-agents/SKILL.md +275 -0
- package/skills/doctor-md-agents/references/rule-model.md +56 -0
- package/skills/doctor-md-agents/references/structure.md +89 -0
- package/skills/doctor-md-agents/scorers/adherence.md +73 -0
- package/skills/doctor-md-agents/scorers/rule-value.md +49 -0
- package/skills/doctor-md-agents/scripts/collect_sessions.py +1135 -0
- package/skills/doctor-md-agents/scripts/extract_rules.py +704 -0
- package/skills/doctor-md-agents/scripts/render_report.py +644 -0
- package/skills/doctor-md-agents/scripts/scan_corrections.py +781 -0
- package/skills/doctor-md-agents/scripts/test_collect_sessions.py +780 -0
- package/skills/doctor-md-agents/scripts/test_extract_rules.py +530 -0
- package/skills/doctor-md-agents/scripts/test_render_report.py +383 -0
- package/skills/doctor-md-agents/scripts/test_scan_corrections.py +343 -0
- package/skills/doctor-md-agents/scripts/test_verify_citations.py +236 -0
- package/skills/doctor-md-agents/scripts/testdata/old_extract_rules_pre_t1.py.txt +507 -0
- package/skills/doctor-md-agents/scripts/verify_citations.py +217 -0
- package/skills/doctor-md-skill/README.md +27 -0
- package/skills/doctor-md-skill/SKILL.md +212 -0
- package/skills/doctor-md-skill/assets/pierre-diffs.js +1531 -0
- package/skills/doctor-md-skill/references/skill-improvements.md +32 -0
- package/skills/doctor-md-skill/references/supported-harnesses.md +44 -0
- package/skills/doctor-md-skill/scorers/code-quality.md +41 -0
- package/skills/doctor-md-skill/scorers/efficiency.md +38 -0
- package/skills/doctor-md-skill/scripts/collect_sessions.py +1112 -0
- package/skills/doctor-md-skill/scripts/detect_machine_sessions.py +315 -0
- package/skills/doctor-md-skill/scripts/render_report.py +545 -0
- package/skills/doctor-md-skill/scripts/test_collect_sessions.py +593 -0
- package/skills/doctor-md-skill/scripts/test_detect_machine_sessions.py +181 -0
- package/skills/doctor-md-skill/scripts/test_render_report.py +241 -0
- package/skills/doctor-md-skill/scripts/test_verify_citations.py +196 -0
- package/skills/doctor-md-skill/scripts/verify_citations.py +221 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "doctor-md-agents"
|
|
3
|
+
description: "Runs only when the user asks for it by name. Grades the agent instruction files (AGENTS.md, CLAUDE.md and friends) against real local conversation history: which rules were ever applicable, which were obeyed, which the user had to correct by hand, what they cost in tokens, and which rules are missing. Never invoke it on inferred intent, on a related-sounding question about instructions or token budget, or as a step inside another workflow — only when the user names this skill."
|
|
4
|
+
metadata:
|
|
5
|
+
author: "iseoane"
|
|
6
|
+
version: "1.0"
|
|
7
|
+
argument-hint: "[--mode grade|restructure|both] [--scope repo|all|<path>...] [--files global|repo|both] [--days N]"
|
|
8
|
+
disable-model-invocation: true
|
|
9
|
+
---
|
|
10
|
+
# doctor-md-agents
|
|
11
|
+
|
|
12
|
+
Grade the instruction files that are injected into every prompt — `AGENTS.md`, `CLAUDE.md`, and any extra file the user names — by measuring them against the user's own local conversation history. Produce one report page with, per rule: how often it was applicable, whether it was obeyed, how often the user had to correct the agent on that point, what it costs in tokens, and a verdict of **keep / improve / drop**. Then propose the rules that are *missing*, mined from repeated user corrections that no current rule covers.
|
|
13
|
+
|
|
14
|
+
## Invocation: only when the user asks for it by name
|
|
15
|
+
|
|
16
|
+
This skill runs **only when the user names it**. Do not start it because a question resembles its subject — someone asking why a rule keeps being ignored, what their instruction files cost, or whether a rule is worth keeping is asking a question, not requesting an audit. Answer the question instead.
|
|
17
|
+
|
|
18
|
+
It is also never a step inside another workflow: no other skill, review, planning or implementation flow may invoke it on its own. A full run reads hundreds of sessions and spends several judging passes; that cost is the user's to authorize, every time.
|
|
19
|
+
|
|
20
|
+
Invocation arguments only preset answers to Step 0's questions — see "Arguments" under Step 0. They never make the skill run on inferred intent: the skill still runs only when the user names it explicitly, exactly as above.
|
|
21
|
+
|
|
22
|
+
Everything runs locally. Never upload transcripts, session files, instruction files, or any excerpt of them anywhere. The only shareable artifact is the report the user chooses to share.
|
|
23
|
+
|
|
24
|
+
Let `SKILL_ROOT` be the directory containing this SKILL.md.
|
|
25
|
+
|
|
26
|
+
## What this skill does and does not claim
|
|
27
|
+
|
|
28
|
+
A skill either fires or does not, and the transcript shows it. A rule in an instruction file is *always* in context, so "was it used?" is not observable. Only two things are observable, and they have **different denominators** — keep them apart everywhere, in the report and in what you tell the user:
|
|
29
|
+
|
|
30
|
+
- **Corrections (hard signal).** The user explicitly correcting the agent on a point. Mined by string patterns over the **entire** session corpus in the window (hundreds of sessions). Reliable evidence.
|
|
31
|
+
- **Adherence (soft signal).** A model judging, per sampled transcript, whether an applicable rule was followed. Sampled (a couple of dozen transcripts). Noisy — good for ranking candidates, never for deleting a rule on its own.
|
|
32
|
+
|
|
33
|
+
Never divide one by the other and never merge them into a single score. A rule with 9 corrections across 372 sessions and "followed" adherence across 20 judged transcripts is not a contradiction; those are two measurements of different things.
|
|
34
|
+
|
|
35
|
+
**Never edit the user's instruction files in this run.** Proposals go to `$REPORT_DIR/proposed/`. Applying them is a separate, explicitly confirmed step.
|
|
36
|
+
|
|
37
|
+
## Step 0: Start the run
|
|
38
|
+
|
|
39
|
+
### Nothing to resolve
|
|
40
|
+
|
|
41
|
+
Every script this skill runs lives under `$SKILL_ROOT/scripts/`, including the session collector, which reads Claude Code, Codex, Pi, Grok Build and ZCode history. The skill has no runtime dependency on any other skill or harness.
|
|
42
|
+
|
|
43
|
+
### Arguments
|
|
44
|
+
|
|
45
|
+
Invocation arguments: `$ARGUMENTS`. If the value shown in backticks above is empty, or is the unexpanded placeholder itself (a dollar sign followed by the word ARGUMENTS, meaning the harness did not substitute it), treat it as no arguments — every question below is asked, exactly as before this section existed.
|
|
46
|
+
|
|
47
|
+
Otherwise, parse it as flags, each preset-answering one question from "Ask the scope" below:
|
|
48
|
+
|
|
49
|
+
| Flag | Accepted values | Presets |
|
|
50
|
+
|---|---|---|
|
|
51
|
+
| `--mode` | `grade`, `restructure`, `both` | Question 3 — `grade` → *Grade only*, `restructure` → *Restructure only (no session evidence)*, `both` → *Grade and restructure* |
|
|
52
|
+
| `--scope` | `repo`, `all`, or one or more paths | Question 1 — `repo` → *Conversations in this repository*, `all` → *All conversations*, one or more paths → *Choose projects* with those paths. Paths are consumed until the next `--flag`. Ignored, with a one-line note, when the resolved mode is *Restructure only* for every selected file — see below. |
|
|
53
|
+
| `--files` | `global`, `repo`, `both` | Question 2 — `global` → *Global only*, `repo` → *This repository's only*, `both` → *Global + this repository's* |
|
|
54
|
+
| `--days N` | a positive integer | sets the **corrections** window (Step 2) to `N` days instead of the default 90; the adherence sample window stays 45. Under *Restructure only*, Step 2 does not run, so `--days` has no effect there. |
|
|
55
|
+
|
|
56
|
+
Validate before creating `REPORT_DIR` — a malformed invocation must leave no scratch directory behind. Stop and print the accepted syntax — `[--mode grade|restructure|both] [--scope repo|all|<path>...] [--files global|repo|both] [--days N]` — plus the specific reason, never guessing a value or falling back to a default, for any of:
|
|
57
|
+
|
|
58
|
+
- an unknown flag, or a flag repeated more than once
|
|
59
|
+
- a value for `--mode`/`--files` outside its accepted list, or a `--scope` value that is neither `repo`, `all`, nor a path
|
|
60
|
+
- a `--days` value that is not a positive integer
|
|
61
|
+
- a `--scope` path that does not validate as a git repository, the same check a manually-chosen project path gets
|
|
62
|
+
- `--scope repo` or `--files repo` when the current directory is not inside a git repository
|
|
63
|
+
|
|
64
|
+
`--mode` only matters when Question 3 would otherwise apply (at least one selected file has no `<important if>` blocks yet — see Question 3 below, which depends on the `--files`/Question 2 answer being resolved first). If `--mode` is given but every selected file already has blocks, say so in one line ("`--mode` ignored: all selected files already restructured") and do not ask Question 3.
|
|
65
|
+
|
|
66
|
+
Question 1 depends on the mode in turn: when the resolved mode — from `--mode` or from Question 3's answer — is *Restructure only* and it covers **every** selected file (none of them already has `<important if>` blocks, so none needs Steps 2–4 to be graded), Question 1 is not asked at all, since Steps 2–4 never run for this file set. If `--scope` was given in that case, say so in one line ("`--scope` ignored: restructure-only run, no conversations to mine") instead of asking about it or using it. If even one selected file already has blocks, that file still needs mining to be graded, so Question 1 is asked regardless of what the mode turns out to be. See "Ask the scope" below for the ordering this implies and for what happens when Question 2 later changes the selected-file set.
|
|
67
|
+
|
|
68
|
+
Ask, in as few interactions as the ordering in "Ask the scope" below requires, only the questions that arguments did not already answer and that apply. If arguments answered every question that applies, ask nothing — but still do the rest of "Ask the scope" below (create `REPORT_DIR`, run the block check that decides whether Questions 1 and 3 apply), then print the resolved choices, including the auto-discovered file list, before moving to Step 1.
|
|
69
|
+
|
|
70
|
+
### Ask the scope
|
|
71
|
+
|
|
72
|
+
Create one fresh scratch directory per run and use it as `REPORT_DIR` for every artifact. Never write into the user's repo or into the skill directory:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
REPORT_DIR="$(mktemp -d "${TMPDIR:-/tmp}/doctor-md-agents-XXXXXXXX")"
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Check whether the current directory is inside a git repository (`git rev-parse --show-toplevel`). Before asking anything, check each candidate file (auto-discovered, narrowed by any `--files` preset) for an own-line `<important if="` opening tag (`references/rule-model.md` — same recognition rule the extractor uses: the tag alone on its line, up to 3 leading spaces). This single check decides both whether Question 3 applies and, together with the mode, whether Question 1 applies.
|
|
79
|
+
|
|
80
|
+
1. **"Which conversations should I mine?"** — *Conversations in this repository* (recommended when there is one) / *All conversations* (recommended when there is not) / *Choose projects*. For chosen projects, ask for paths and validate each as a git repository.
|
|
81
|
+
2. **"Which instruction files should I grade?"** — *Global + this repository's* (recommended) / *This repository's only* / *Global only*. Mention the files auto-discovery found, so the user can add one you missed.
|
|
82
|
+
3. **"What should this run do?"** (only when at least one selected file has no `<important if>` blocks yet, and only about those files) — *Grade and restructure* (recommended) / *Grade only* / *Restructure only (no session evidence)*. A file that already has blocks is read as already restructured, is graded only (Step 5b is skipped for it regardless of the answer), and is never asked about again.
|
|
83
|
+
|
|
84
|
+
*Restructure only* skips Steps 2–4 entirely: no corrections mining, no transcript sampling, no adherence judging. It also skips Step 5's verdicts — with no adherence or correction evidence to apply `rule-value.md` to, every rule keeps verdict `unknown` and goes straight into Step 5b. Every condition Step 5b writes is marked `estimated`, and the report says so. Use it for a file with no session history yet, or to reorder quickly without spending a judging pass.
|
|
85
|
+
|
|
86
|
+
The mode must be known — from `--mode` or from Question 3's answer — before deciding whether Question 1 is asked, since Question 1 is skipped only when the resolved mode is *Restructure only* for every selected file. Using the harness's user-question tool when available, resolve in this order:
|
|
87
|
+
|
|
88
|
+
- The mode is already known (`--mode` given, or Question 3 does not apply because every selected file already has blocks, which always needs Question 1 to be graded): ask Question 1 together with whichever of Questions 2 and 3 arguments left unanswered, all in one interaction — except that when the now-known mode is *Restructure only* covering every selected file, Question 1 is not asked at all, only the rest.
|
|
89
|
+
- The mode is not yet known, but at least one selected file already has blocks: that file needs Question 1 regardless of what Question 3's answer turns out to be, so asking together is possible — ask Questions 1, 2 and 3 together in one interaction, as before.
|
|
90
|
+
- The mode is not yet known and every selected file lacks blocks: Question 1's necessity depends entirely on the still-unknown mode, so asking it together with Question 3 is not possible. Ask Question 3 first, alone (together with Question 2 if that is also unanswered). Once the answer comes back, ask Question 1 in a second, separate interaction — unless the answer is *Restructure only*, in which case skip Question 1 entirely.
|
|
91
|
+
|
|
92
|
+
All three bullets above decide Question 1's applicability from the candidate files known before asking. Question 2's "add one you missed" can change the selected set afterward, so re-run the block check on any file the user adds through Question 2, then re-decide Question 1 against that final set, not the original candidates:
|
|
93
|
+
|
|
94
|
+
- A newly added file has blocks, and Question 1 was skipped or is about to be: ask Question 1 now (in the same interaction if it has not been sent yet, otherwise as one more follow-up), since that file needs mining to be graded.
|
|
95
|
+
- A newly added file has no blocks and none of the added files change the outcome, and Question 1 was already asked and answered under a mode that turns out to be *Restructure only* for the now-final set: the collected answer is unnecessary — drop it and say so in one line, the same way an ignored `--scope` is noted.
|
|
96
|
+
|
|
97
|
+
Default window: 90 days for the corrections pass, 45 for the adherence sample. `--days N` (see Arguments above) overrides only the corrections window. Accept a different window if the user asks, even without `--days`.
|
|
98
|
+
|
|
99
|
+
## Step 1: Extract the rules
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
python3 "$SKILL_ROOT/scripts/extract_rules.py" --out "$REPORT_DIR" \
|
|
103
|
+
<--repo PATH per selected repo | --scope global> [--file PATH[:LABEL]]...
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
This writes `$REPORT_DIR/rules.json`: every instruction file it graded, every rule inside it with a stable `id`, its heading path, line range, character count and estimated tokens, plus a per-section rollup. Read `references/rule-model.md` for what counts as one rule and why the rollup exists.
|
|
107
|
+
|
|
108
|
+
Read `rules.json`. Sanity-check the granularity before going on: a large prose contract file should land in the low hundreds of rules, a compact project file in the dozens. If a single rule is thousands of characters, note it in the findings as a rule that is too big to judge or to obey — that is itself a finding.
|
|
109
|
+
|
|
110
|
+
If no instruction file is found, stop and say so.
|
|
111
|
+
|
|
112
|
+
## Step 2: Mine the corrections (hard signal, full corpus)
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
python3 "$SKILL_ROOT/scripts/scan_corrections.py" --out "$REPORT_DIR" \
|
|
116
|
+
--days <90, or N from --days> <--repo PATH... | --all-conversations>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
This reads the raw session logs directly — every session in the window, not just the sampled ones — and writes `$REPORT_DIR/corrections.json`: each user turn that looks like a correction, with session id, timestamp, cwd, branch, the matched pattern, the user's words, and a short excerpt of what the agent had just done. It also groups them into rough keyword clusters.
|
|
120
|
+
|
|
121
|
+
Not every `user` turn was typed by a human. Sub-agents, audit harnesses, judges and other skills all write turns that the log records as `user`, and they are long, templated and repeated verbatim across hundreds of sessions — left unfiltered they drown the real corrections. The script drops them by shape, not by name: turns above `--max-turn-chars`, turns whose normalized text repeats across `--template-threshold` or more distinct sessions, and turns carrying the self-session sentinel. Read `stats.turns_dropped_*` in the output: if the dropped counts dwarf the kept ones, say so — it means this corpus is mostly machine-driven and the corrections signal is thin.
|
|
122
|
+
|
|
123
|
+
The clusters are a starting point, not an answer. Read them and:
|
|
124
|
+
|
|
125
|
+
- Merge, split and rename clusters by meaning. Drop anything that is not really a correction (the patterns over-match on purpose — a missed correction cannot be recovered later, a false positive costs one read).
|
|
126
|
+
- For each real cluster, decide whether an existing rule in `rules.json` already covers it. If one does, attach the cluster to that rule id — those corrections are evidence that the rule exists and is *not working*. If none does, it becomes a **missing-rule candidate**.
|
|
127
|
+
|
|
128
|
+
Cite session ids for every cluster. A cluster with one occurrence is an anecdote; say so rather than proposing a rule from it.
|
|
129
|
+
|
|
130
|
+
## Step 3: Sample transcripts for the adherence pass
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
python3 "$SKILL_ROOT/scripts/collect_sessions.py" --out "$REPORT_DIR" \
|
|
134
|
+
<conversation-scope arguments> \
|
|
135
|
+
--days 45 --max-sessions 20 --sample newest
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Scope arguments are `--repo PATH` (repeatable) or `--all-conversations`. `--sample newest` takes the newest N sessions with no skill-related stratification, which is what this report needs.
|
|
139
|
+
|
|
140
|
+
Read `$REPORT_DIR/inventory.json` and take the session list from `inventory.sessions`, using each entry's `transcript_path`. Ignore every skill-related field in that file — this run is not about skills. Deduplicate by session id. If `sessions_sampled` is 0, continue with the corrections pass alone and say clearly in the report that no adherence judgment was possible.
|
|
141
|
+
|
|
142
|
+
## Step 4: Judge adherence
|
|
143
|
+
|
|
144
|
+
Score transcripts in the current local agent process, or delegate only to local child agents that keep transcript contents on this machine. Process up to 50 transcripts in one batch; beyond that, use parallel batches of about 20.
|
|
145
|
+
|
|
146
|
+
Build a **rule index** to pass as context: for every rule, its `id`, file label, heading path and `gist` — not the full text. Pass `$SKILL_ROOT/scorers/adherence.md` as the rubric.
|
|
147
|
+
|
|
148
|
+
Every prompt you send to a judge — including a delegated child agent's — must start with the self-session sentinel line, on its own line:
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
<!-- doctor-md-agents:self-session -->
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
This is how a future run recognizes its own prompts in the logs and refuses to mine them as user corrections. A run that omits it poisons the next one's evidence.
|
|
155
|
+
|
|
156
|
+
For each transcript, record only the rules the transcript gives evidence about, in the shape `scorers/adherence.md` defines: the five required fields (`rule_id`, `label`, `class`, `quote`, `reason`) plus the optional sixth, `trigger` — what the session was doing when the rule applied. `trigger` feeds Step 5b's conditions directly, so record it whenever a clear one-clause answer exists. A rule the transcript never had occasion to touch is simply absent from that transcript's result. Absence is the normal case; do not invent `not_applicable` entries for 200 rules.
|
|
157
|
+
|
|
158
|
+
Write the collected findings to `$REPORT_DIR/findings.json` as `{"findings": [ ... ]}`, each carrying its `transcript_path`, then verify them:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
python3 "$SKILL_ROOT/scripts/verify_citations.py" "$REPORT_DIR/findings.json" --out "$REPORT_DIR"
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
This whitespace-folds each `quote` and the transcript the finding came from and requires the quote to be a literal substring. It writes `$REPORT_DIR/findings.verified.json`, marking each finding `verified: true|false`. **Aggregate only the verified ones.** Carry the unverified count into the report: a judge that cites text which is not there is confabulating, and a rule whose entire case was unverified has no case.
|
|
165
|
+
|
|
166
|
+
Then aggregate per rule, counting **distinct sessions** rather than findings: `applicable_sessions` (sessions that judged it anything other than `not_applicable`), the adherence label from the rubric's aggregation table, the judging denominator, and the counts per evidence class. Never take a count from a judge's own claim — compute it. A rule that no sampled transcript could judge gets `insufficient_evidence`, which is *not* the same as "never applicable", and the report must not present it as such.
|
|
167
|
+
|
|
168
|
+
## Step 5: Verdicts
|
|
169
|
+
|
|
170
|
+
Skip this step entirely under *Restructure only* (Step 0): with no corrections or adherence evidence gathered, every rule keeps verdict `unknown` and goes straight into Step 5b.
|
|
171
|
+
|
|
172
|
+
Apply `$SKILL_ROOT/scorers/rule-value.md` to every rule, using its token cost, its applicability, its adherence label, and its correction count. Each verdict is `keep` / `improve` / `drop` / `unknown` with a one-sentence rationale naming the evidence that produced it.
|
|
173
|
+
|
|
174
|
+
Two constraints that the rubric enforces and you must not relax:
|
|
175
|
+
|
|
176
|
+
- A `drop` verdict needs a cost argument plus absence of evidence for the rule, never a low adherence score. Low adherence means the rule is not working, which argues for `improve` — a rewrite, a move to a more visible position, a concrete command instead of prose.
|
|
177
|
+
- Nothing is dropped because a model found it mediocre. The payload of this report is the budget: rank by `est_tokens` × absence of evidence, and lead with the aggregate — "these N rules are X% of the file and no sampled session was ever able to judge them."
|
|
178
|
+
|
|
179
|
+
For each `improve` rule, write the proposed replacement text to `$REPORT_DIR/proposed/<file-label>/<rule-id>.md` and produce a unified diff against the current rule text. For each missing-rule candidate with 2+ occurrences, write the proposed new rule the same way, with the evidence sessions listed.
|
|
180
|
+
|
|
181
|
+
## Step 5b: Restructure
|
|
182
|
+
|
|
183
|
+
Skip this step if the user chose *Grade only*. Read `$SKILL_ROOT/references/structure.md` first.
|
|
184
|
+
|
|
185
|
+
Work per instruction file, on the rules that survive Step 5 (every verdict except `drop`; a `drop` stays in place and is left to its own confirmation — under *Restructure only*, every rule survives, since Step 5 never ran). For each rule:
|
|
186
|
+
|
|
187
|
+
1. Decide bare or wrapped using the thresholds and exceptions in `structure.md`. Record the ratio that decided it, or `invariant` / `estimated`. Under *Restructure only*, every condition is `estimated` — there is no adherence sample to compute a ratio from.
|
|
188
|
+
2. For wrapped rules, write the condition from the verified findings' `trigger` fields and the attached correction clusters. Cite the session ids behind it. When a rule has no evidence (or Steps 2–4 never ran), derive the condition from the rule's own text instead, and mark it `estimated`.
|
|
189
|
+
3. Group rules only when their conditions are the same situation.
|
|
190
|
+
|
|
191
|
+
Write the whole restructured file to `$REPORT_DIR/proposed/<file-label>/RESTRUCTURED.md` and its unified diff against the current file to `RESTRUCTURED.diff`. The rule text inside each block is the Step 5 replacement when the rule got `improve`, and the original text otherwise; the restructure never rewrites a rule on its own.
|
|
192
|
+
|
|
193
|
+
Skip a file entirely if Step 0 found it already had `<important if>` blocks — it was graded only, and its existing structure is left alone.
|
|
194
|
+
|
|
195
|
+
Check before writing: re-extract `RESTRUCTURED.md` on its own —
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
python3 "$SKILL_ROOT/scripts/extract_rules.py" --out "$REPORT_DIR/proposed/<file-label>/check" \
|
|
199
|
+
--file "$REPORT_DIR/proposed/<file-label>/RESTRUCTURED.md:restructured" --only
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
— using `--only` so auto-discovery never pulls in whatever CLAUDE.md/AGENTS.md the repo happens to have; only the restructured file itself is parsed. `ids` are internal bookkeeping and are never written into the file, so match by text, not id: every rule from `rules.json` that is not `drop` must have its text (the Step 5 replacement when it got `improve`, the original text otherwise) appear exactly once in the re-extracted output, and every command and fenced block from the original must be present. If either check fails, fix it; never ship a restructure that lost a rule.
|
|
203
|
+
|
|
204
|
+
When assembling `RESTRUCTURED.md` programmatically from extracted `text` fields, copy each rule's whitespace exactly as extracted: an extra or missing blank line inside a rule changes its text and breaks the "appears exactly once" check above.
|
|
205
|
+
|
|
206
|
+
Record, per file, how many blocks were written, how many rules stayed bare vs got wrapped, and how many conditions are `estimated` — this feeds `report.json`'s `structure` section (Step 6). Under *Restructure only*, there is no evidence session to cite for any block, so every block's `sessions` is `[]` and its `basis` is `estimated`, not just some of the wrapped ones.
|
|
207
|
+
|
|
208
|
+
## Step 6: Report
|
|
209
|
+
|
|
210
|
+
Write `$REPORT_DIR/report.json`:
|
|
211
|
+
|
|
212
|
+
```json
|
|
213
|
+
{
|
|
214
|
+
"title": "Agent Instructions Report",
|
|
215
|
+
"generated_at": "<ISO timestamp>",
|
|
216
|
+
"harness": "<from inventory.json, or 'n/a'>",
|
|
217
|
+
"scope": "<repo name, project list, or 'all conversations'>",
|
|
218
|
+
"windows": {"corrections_days": 90, "adherence_days": 45},
|
|
219
|
+
"stats": {
|
|
220
|
+
"files": 0, "rules": 0, "est_tokens": 0,
|
|
221
|
+
"sessions_scanned": 0, "sessions_judged": 0,
|
|
222
|
+
"corrections_found": 0, "corrections_dropped_as_machine": 0,
|
|
223
|
+
"rules_without_evidence": 0,
|
|
224
|
+
"findings_total": 0, "findings_unverified": 0
|
|
225
|
+
},
|
|
226
|
+
"files": [
|
|
227
|
+
{"label": "", "path": "", "scope": "global|project", "rules": 0, "est_tokens": 0,
|
|
228
|
+
"verdicts": {"keep": 0, "improve": 0, "drop": 0, "unknown": 0}}
|
|
229
|
+
],
|
|
230
|
+
"top_findings": ["", "", ""],
|
|
231
|
+
"rules": [
|
|
232
|
+
{"id": "", "file_label": "", "section": "", "title": "", "line": 0, "est_tokens": 0,
|
|
233
|
+
"applicable_sessions": 0, "judged_sessions": 0,
|
|
234
|
+
"adherence": "followed|mixed|violated|insufficient_evidence",
|
|
235
|
+
"classes": {"harm": 0, "non-compliance": 0, "irrelevant": 0},
|
|
236
|
+
"corrections": 0, "verdict": "keep|improve|drop|unknown",
|
|
237
|
+
"rationale": "",
|
|
238
|
+
"evidence": [{"session_id": "", "class": "", "quote": "", "reason": ""}],
|
|
239
|
+
"proposed_path": null, "diff": null}
|
|
240
|
+
],
|
|
241
|
+
"missing_rules": [
|
|
242
|
+
{"title": "", "occurrences": 0, "evidence": ["<session id>"],
|
|
243
|
+
"proposed_path": "", "proposed_text": "", "nearest_rule": null}
|
|
244
|
+
],
|
|
245
|
+
"structure": [
|
|
246
|
+
{"file_label": "", "proposed_path": "", "diff_path": "",
|
|
247
|
+
"bare": 0, "wrapped": 0, "estimated_conditions": 0,
|
|
248
|
+
"blocks": [{"condition": "", "rule_ids": [""], "basis": "measured|estimated",
|
|
249
|
+
"sessions": ["<session id>"]}]}
|
|
250
|
+
],
|
|
251
|
+
"notes": [""]
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
`structure` is Step 5b's output and is entirely optional: omit it when Step 5b did not run (the user chose *Grade only*), and `render_report.py` renders the rest of the page exactly the same either way. When present, put the count of `estimated` conditions (summed across every file's `structure` entry) in `notes` alongside the rest.
|
|
256
|
+
|
|
257
|
+
Under *Restructure only*, every block's `basis` is `"estimated"` and its `sessions` is `[]` — there is no adherence sample or correction cluster to cite a session from, for any block, not only the wrapped ones. When Question 1 was not asked (Step 0 skipped it — see "Ask the scope"), set the top-level `scope` field to `"n/a (restructure only, no conversations mined)"` and leave `sessions_scanned`/`sessions_judged`/`corrections_found` at `0`.
|
|
258
|
+
|
|
259
|
+
Put these in `notes` every time: the denominator caveat (corrections come from `sessions_scanned` sessions, adherence from `sessions_judged`), how many machine-written turns were dropped from the corrections pass, how many findings failed quote verification, and — when `structure` is present — the estimated-conditions count above. Then render:
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
python3 "$SKILL_ROOT/scripts/render_report.py" "$REPORT_DIR/report.json" --open
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
That writes one self-contained `$REPORT_DIR/report.html` and tries to open it.
|
|
266
|
+
|
|
267
|
+
## Step 7: Output
|
|
268
|
+
|
|
269
|
+
Tell the user, in text: the total token cost of their instruction files, the three top findings, the single largest block of rules with no evidence, and the count of missing-rule candidates. Then:
|
|
270
|
+
|
|
271
|
+
- Your instruction file report: `file://$REPORT_DIR/report.html`
|
|
272
|
+
- Proposed edits (nothing applied): `$REPORT_DIR/proposed/`
|
|
273
|
+
- Proposed restructure (nothing applied): `$REPORT_DIR/proposed/<file-label>/RESTRUCTURED.md`
|
|
274
|
+
|
|
275
|
+
Ask whether to apply any of the proposed edits. Apply only the ones the user names, one file at a time, and show each diff before writing. Never apply a `drop` without the user confirming that specific rule.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# What counts as one rule
|
|
2
|
+
|
|
3
|
+
## The problem this solves
|
|
4
|
+
|
|
5
|
+
Instruction files have wildly different granularity. A global file can be prose contracts under nested headings, hundreds of lines deep; a project file is usually numbered lists and tables. A naive heading splitter emits one 400-line "rule" for a review lifecycle and fifteen 2-line rules for a lessons-learned list — and the token-cost ranking, which is the report's whole payload, ends up measuring the splitter instead of the file.
|
|
6
|
+
|
|
7
|
+
## The split
|
|
8
|
+
|
|
9
|
+
1. **Sections.** Cut on every ATX heading (`#`…`######`). A section is the heading plus the body up to the next heading of any level. Sections whose body is only whitespace are dropped; their heading survives as part of the child sections' heading path.
|
|
10
|
+
2. **List items.** Inside a section, if there are **2 or more** top-level list items (`-`, `*`, `+`, or `1.`, indented no more than 3 spaces), each item — with its indented continuation lines and nested sub-items — becomes its own rule. Prose before the first item becomes one more rule. The threshold exists to stop the splitter from atomising a single paragraph into fragments: a lone bullet under a heading is just a heading with one paragraph, not a list a reader would scan item by item, so it stays part of its section as one rule. Two clearly labelled bullets are not that case — each is already a distinct, independently gradable rule (a project file's "Otras reglas" section with exactly two unrelated bullets, one about learning from corrections and one about the package manager, is two rules, not one merged verdict covering both).
|
|
11
|
+
3. **Otherwise** the whole section body is one rule.
|
|
12
|
+
|
|
13
|
+
A fenced code block never starts a rule: list markers and `#` lines inside ``` fences are body text. Table rows are body text too; a Markdown table is part of whatever rule contains it.
|
|
14
|
+
|
|
15
|
+
## `<important if="condition">` containers
|
|
16
|
+
|
|
17
|
+
A block written as `<important if="…">` on its own line, closed by a `</important>` also on its own line, is a container -- not a rule of its own. The rules inside it split exactly the same way as any other section body (list-item splitting still applies), and each one carries `condition`: the block's condition text. A rule outside any block has `condition: null`.
|
|
18
|
+
|
|
19
|
+
The tags themselves are excluded from a rule's `text`, `chars` and `est_tokens`: they cost nothing to obey and must not inflate the rule's cost. They do count toward the file's own `chars`/`est_tokens`, since they really are injected into the prompt.
|
|
20
|
+
|
|
21
|
+
A tag is only recognized when it sits alone on its own line (up to 3 leading spaces, like a heading or list marker). An inline mention of the tag in prose -- this file's own examples, for instance -- is never treated as a real block boundary.
|
|
22
|
+
|
|
23
|
+
Nesting is rejected, not flattened: a second `<important if="…">` opened while already inside a block is a defect in the instruction file, not something the extractor silently resolves. Its content stays exactly where it is, as literal text under the *outer* condition, and a warning names the line. The same holds for a stray `</important>` with no matching open. Nothing is ever dropped: a rejected nested block still keeps every line of its text, just without its own (discarded) condition.
|
|
24
|
+
|
|
25
|
+
A block that is never closed reverts entirely: the opening tag itself becomes literal text, and every line from it through end of file loses its condition (`condition: null`), instead of silently wrapping the rest of the file -- potentially crossing headings and swallowing bare invariants -- under a condition nobody closed.
|
|
26
|
+
|
|
27
|
+
A file with no `<important if>` blocks at all extracts exactly as it did before this container existed, aside from the added `condition: null` key on every rule.
|
|
28
|
+
|
|
29
|
+
## Identity and cost
|
|
30
|
+
|
|
31
|
+
- `id` is `<file-label>#<start line>` — stable while the file does not move, and traceable back to the source by line.
|
|
32
|
+
- `section` is the full heading path (`Otras reglas › Lecciones Aprendidas`), so the rollup can group rules under the heading a human would recognize.
|
|
33
|
+
- `est_tokens` is `chars // 4`. A rough estimate is enough: the report ranks rules against each other, and every rule is measured the same way. Say it is an estimate in the report.
|
|
34
|
+
|
|
35
|
+
## Rollups
|
|
36
|
+
|
|
37
|
+
Report cost at two levels: per rule and per top-level section. A verdict belongs to a rule; a budget argument usually belongs to a section. "These 23 rules under *Review Execution Contract* are 31% of the global file and no sampled session could judge any of them" is an actionable finding. "Rule global#231 is 1.6k characters" is not.
|
|
38
|
+
|
|
39
|
+
## Granularity sanity check
|
|
40
|
+
|
|
41
|
+
After extraction, look at the counts before trusting anything downstream:
|
|
42
|
+
|
|
43
|
+
- A dense contract file should land in the **low hundreds** of rules. Under ten means the splitter collapsed it and the cost ranking is meaningless.
|
|
44
|
+
- A compact project file should land in the **dozens**.
|
|
45
|
+
- A single rule over ~2000 characters is a finding in its own right: too big to judge and too big to obey. Surface it rather than quietly scoring it.
|
|
46
|
+
|
|
47
|
+
## Which files get graded
|
|
48
|
+
|
|
49
|
+
Auto-discovery looks for the usual instruction files and lets the user add more:
|
|
50
|
+
|
|
51
|
+
- Global: `~/.claude/CLAUDE.md`, `~/.codex/AGENTS.md`, `~/.agents/AGENTS.md`, `~/AGENTS.md`
|
|
52
|
+
- Project: `<repo>/CLAUDE.md`, `<repo>/AGENTS.md`, `<repo>/.claude/CLAUDE.md`, `<repo>/.github/copilot-instructions.md`
|
|
53
|
+
|
|
54
|
+
Files a project file merely *indexes* (a `rules/` directory, for instance) are not injected into every prompt, so they are not graded by default and must not be counted in the budget. Grade one only if the user names it with `--file`, and label it as a referenced file so its cost is reported separately from the always-injected total.
|
|
55
|
+
|
|
56
|
+
`--only` disables auto-discovery entirely, so the run extracts exactly the `--file` paths given and nothing else; it errors if given without `--file`, since without auto-discovery there would be nothing to grade. This is not a user-facing scope choice (Step 0 still asks that) — it exists so Step 5b can re-extract one proposed `RESTRUCTURED.md` on its own, without the repo's real instruction files leaking into the check.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Structure: bare vs conditional
|
|
2
|
+
|
|
3
|
+
Claude Code wraps every instruction file in a reminder saying it "may or may not be
|
|
4
|
+
relevant". The more of a file that does not apply to the task at hand, the more of
|
|
5
|
+
all of it the model discounts, including the rules that do apply. The remedy is to
|
|
6
|
+
say, per rule, when it applies: an `<important if="condition">` block puts a
|
|
7
|
+
relevance signal next to the rule, in the same XML shape the harness itself uses.
|
|
8
|
+
|
|
9
|
+
## What stays bare
|
|
10
|
+
|
|
11
|
+
Bare means always attended. Keep bare only what governs nearly every session:
|
|
12
|
+
|
|
13
|
+
- Project identity: one sentence, what it is and what it is built with.
|
|
14
|
+
- The project map or rules index.
|
|
15
|
+
- Domain invariants: rules that must hold whatever the task, where a wrong guess
|
|
16
|
+
about applicability costs more than the tokens (determinism, tenant isolation,
|
|
17
|
+
data lifecycle, security boundaries). A missed invariant is a defect, so never
|
|
18
|
+
gate one behind a condition just because the sample rarely saw it. The
|
|
19
|
+
product's role/permission model -- who may do what, RBAC matrices, tenant
|
|
20
|
+
boundaries -- is one of these security boundaries and always stays bare, like
|
|
21
|
+
any other invariant. Never wrap it behind a condition such as "you are doing
|
|
22
|
+
authorization work": the model needs the permission boundary attended on
|
|
23
|
+
every task, not only the ones that look like they touch it.
|
|
24
|
+
- Cross-cutting behaviour: language, tone, attribution, commit policy.
|
|
25
|
+
|
|
26
|
+
Measured rule of thumb: `applicable_sessions / sessions_judged` of about 0.6 or
|
|
27
|
+
more → bare. Invariants stay bare whatever the ratio.
|
|
28
|
+
|
|
29
|
+
## What gets wrapped
|
|
30
|
+
|
|
31
|
+
Everything tied to a kind of work: a subsystem, a tool, a file type, a deployment
|
|
32
|
+
step, a gotcha. Lessons-learned lists are the typical case: each lesson matters in
|
|
33
|
+
one situation and is noise in all the others.
|
|
34
|
+
|
|
35
|
+
## Writing the condition
|
|
36
|
+
|
|
37
|
+
- Name an observable action, not a topic: "you are changing Python files in the
|
|
38
|
+
backend or the IRF structure", not "backend".
|
|
39
|
+
- One condition per rule or per tight group. Never group unrelated rules under a
|
|
40
|
+
broad condition ("you are writing code"). A broad condition is a bare rule with
|
|
41
|
+
extra tokens.
|
|
42
|
+
- Derive it from evidence. Use the `trigger` of the rule's verified findings (what
|
|
43
|
+
the session was doing when the rule applied) and the subject of the correction
|
|
44
|
+
clusters attached to it. Only when a rule has no evidence, write the condition
|
|
45
|
+
from the rule's own text and mark it `estimated`.
|
|
46
|
+
- The condition must be true in every session where the rule applies. If the
|
|
47
|
+
evidence shows the rule applying outside the condition you were about to write,
|
|
48
|
+
widen it or leave the rule bare.
|
|
49
|
+
|
|
50
|
+
## Never cut
|
|
51
|
+
|
|
52
|
+
The restructure moves rules. It does not delete them; deletion is Step 5's `drop`
|
|
53
|
+
verdict, with its own evidence bar. In particular never cut, whatever a generic
|
|
54
|
+
"less is more" guide says:
|
|
55
|
+
|
|
56
|
+
- Commands, even long ones or ones used rarely.
|
|
57
|
+
- Operational snippets that are not derivable from the code: rebuild commands,
|
|
58
|
+
hash recipes, Dockerfile fixes, escaping rules.
|
|
59
|
+
- Gotchas and naming traps. The current code does not reveal them, which is why
|
|
60
|
+
they were written down.
|
|
61
|
+
|
|
62
|
+
## Order
|
|
63
|
+
|
|
64
|
+
Bare block first (identity, map, invariants, cross-cutting), then the wrapped
|
|
65
|
+
blocks grouped by subsystem in the order a reader would meet them: build and run,
|
|
66
|
+
then data and domain, then infra and delivery -- except a heading with mixed
|
|
67
|
+
bare/wrapped rules, which stays in place; see below. Keep each rule's original
|
|
68
|
+
number or anchor in the block, so references from other files still resolve.
|
|
69
|
+
|
|
70
|
+
Subsystem grouping applies **between headings, never inside one**. A numbered or
|
|
71
|
+
ordered list under a single heading -- a lessons-learned list is the typical case
|
|
72
|
+
-- stays together, in its original order and numbering, under its original
|
|
73
|
+
heading: never split across subsystem groups because item 3 is about the backend
|
|
74
|
+
and item 7 is about infra. Wrap each item individually where it already sits;
|
|
75
|
+
a bare item in the same list stays bare, in place, in its original position --
|
|
76
|
+
never hoisted out to the shared bare block at the top. Only the item's own text
|
|
77
|
+
gets an `<important if>` block; the list, its order and its numbers stay intact.
|
|
78
|
+
|
|
79
|
+
The heading a wrapped rule lives under is kept as structural context: headings
|
|
80
|
+
are never dropped, even when every rule under them ends up wrapped. A reader (and
|
|
81
|
+
the model) still needs to know what subsystem a wrapped block belongs to.
|
|
82
|
+
|
|
83
|
+
This also decides where a heading itself sits. A heading whose rules are mixed
|
|
84
|
+
-- some bare, some wrapped -- stays in its original position relative to its
|
|
85
|
+
neighbours; its bare items stay bare, in place, under it. Only a heading whose
|
|
86
|
+
rules are *all* wrapped moves into the subsystem-ordered wrapped area below the
|
|
87
|
+
bare block. Do not relocate a mixed heading just because part of it got
|
|
88
|
+
wrapped, and do not hoist its bare items out to the shared bare block at the
|
|
89
|
+
top -- that would scatter one heading's rules across two places in the file.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Scorer: rule adherence
|
|
2
|
+
|
|
3
|
+
You are judging one transcript against a rule index. For each rule the transcript gives evidence about, decide whether the agent followed it. This is the **soft** signal of the report: it ranks candidates for a human to look at. It never, on its own, justifies deleting a rule.
|
|
4
|
+
|
|
5
|
+
## What to emit
|
|
6
|
+
|
|
7
|
+
Only rules the transcript actually speaks to. A rule the session never had occasion to touch is simply absent from your output — do not emit `not_applicable` rows for an entire file. Emit `not_applicable` only when the transcript *looks* like the rule's domain but on inspection it does not apply (the rule is about Celery workers, the session touched Celery config but never worker code).
|
|
8
|
+
|
|
9
|
+
Per rule, emit five required fields, plus one optional sixth:
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{"rule_id": "global#95",
|
|
13
|
+
"label": "followed|mixed|violated|not_applicable",
|
|
14
|
+
"class": "non-compliance|harm|irrelevant|none",
|
|
15
|
+
"quote": "<verbatim substring of the transcript>",
|
|
16
|
+
"reason": "<one sentence naming what happened>",
|
|
17
|
+
"trigger": "<what the session was doing that made the rule apply, one short clause>"}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`trigger` is optional and, unlike `quote`, is never checked mechanically — but it must describe the moment the quote is drawn from, not a paraphrase of the rule itself. Example: `"trigger": "editing backend/analysis/parser_4g.py"`. It is the raw material Step 5b's `references/structure.md` uses to write a wrapped rule's condition: the condition should be true of every `trigger` a rule collected, or it is too narrow. A finding classed `irrelevant` on a rule that is already wrapped is itself a signal that its condition is too broad — it argues for `improve`, without changing the verdict on its own.
|
|
21
|
+
|
|
22
|
+
**`quote` must be copied character-for-character from the transcript you were shown.** It is checked mechanically: the pipeline whitespace-folds your quote and the transcript and requires yours to be a literal substring. A quote that does not appear is discarded and reported as unverified — it does not count for or against the rule, and it makes your whole finding worthless. Do not paraphrase, do not stitch two passages together, do not tidy up punctuation or translate. Copy the shortest passage that actually proves your point; if no passage proves it, emit nothing for that rule.
|
|
23
|
+
|
|
24
|
+
`reason` is your own sentence and is not checked, but it must be about the quoted moment. No generic praise, no speculation about intent.
|
|
25
|
+
|
|
26
|
+
## The class field, and why it decides more than the label
|
|
27
|
+
|
|
28
|
+
The label says whether the rule was obeyed. The class says **what the failure was worth**, and it is the field that governs whether a rule can be dropped:
|
|
29
|
+
|
|
30
|
+
| Class | Meaning | What it argues for |
|
|
31
|
+
|---|---|---|
|
|
32
|
+
| `non-compliance` | The rule was right and was not followed. | Rewriting it — clearer, shorter, earlier, a literal command. Never removal. |
|
|
33
|
+
| `harm` | Following the rule made the outcome worse: it sent the work down a wrong path, contradicted the repo, or cost effort for nothing. | The rule itself being wrong. This is the only class that can support removal. |
|
|
34
|
+
| `irrelevant` | The rule was applied where it did not belong and produced noise. | Narrowing its scope, or removal if it is also expensive. |
|
|
35
|
+
| `none` | Use with `followed` and with `not_applicable`. | Nothing. |
|
|
36
|
+
|
|
37
|
+
Classify conservatively. `harm` needs the transcript to show the worse outcome, not your opinion that the rule is bad; if you cannot quote the damage, it is `non-compliance`. An unclassified or wrongly confident `harm` is how a working rule gets deleted.
|
|
38
|
+
|
|
39
|
+
## How to decide applicability
|
|
40
|
+
|
|
41
|
+
Applicability is the hard part and the place where this scorer goes wrong. Before labelling anything, ask what observable fact would have to be true for the rule to govern this session. If you cannot name one, the rule is not applicable here — say nothing about it.
|
|
42
|
+
|
|
43
|
+
- A rule about commit messages is applicable only if the session made a commit.
|
|
44
|
+
- A rule about which package manager to use is applicable only if the session ran that ecosystem's tooling.
|
|
45
|
+
- A rule about tone or language is applicable to every session that produced user-facing text.
|
|
46
|
+
- A rule that describes a workflow the session never entered is not applicable, no matter how prominent the rule is.
|
|
47
|
+
|
|
48
|
+
Never infer compliance from absence. "The session did not violate it" is not `followed`; if the rule never governed anything, emit nothing.
|
|
49
|
+
|
|
50
|
+
## Evidence quality
|
|
51
|
+
|
|
52
|
+
- Prefer what the tools show over what the agent claimed. An agent saying "I ran the tests" is weaker evidence than the test command and its output.
|
|
53
|
+
- A user correction on the rule's subject is strong evidence of `violated` or `mixed`, and the correction pass will count it separately — still cite it here.
|
|
54
|
+
- When the transcript is truncated or condensed past the point of judgment, emit nothing for that rule rather than guessing.
|
|
55
|
+
|
|
56
|
+
## Aggregating across transcripts
|
|
57
|
+
|
|
58
|
+
The caller aggregates, and the caller counts. Never report a count yourself — how many sessions corroborate something is computed from the verified findings, never taken from a judge's claim about how often it saw something.
|
|
59
|
+
|
|
60
|
+
Discard every finding whose `quote` failed verification before aggregating. Report how many were discarded: a scorer run that produces many unverified quotes is a broken run, and a rule whose only evidence was discarded has no evidence at all.
|
|
61
|
+
|
|
62
|
+
Given the surviving labels a rule collected across the sampled transcripts:
|
|
63
|
+
|
|
64
|
+
| Aggregate label | Condition |
|
|
65
|
+
|---|---|
|
|
66
|
+
| `insufficient_evidence` | No transcript judged it (`applicable_sessions` is 0). |
|
|
67
|
+
| `violated` | Mean score below 0.4. |
|
|
68
|
+
| `mixed` | Mean score from 0.4 to below 0.8, or any `violated` alongside `followed`. |
|
|
69
|
+
| `followed` | Mean score 0.8 or above. |
|
|
70
|
+
|
|
71
|
+
Corroboration is counted in **distinct sessions**, not findings: three findings from one transcript are one session's worth of evidence.
|
|
72
|
+
|
|
73
|
+
`insufficient_evidence` means the sample could not see the rule. It does **not** mean the rule is never applicable — the sample is a couple of dozen sessions out of hundreds. Report it as "no evidence in the sample", never as "unused".
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Scorer: rule value — keep, improve or drop
|
|
2
|
+
|
|
3
|
+
Every rule is paid for on every single prompt, whether or not it ever applies. The verdict answers one question: **is this rule worth its place in the budget?**
|
|
4
|
+
|
|
5
|
+
Inputs per rule: `est_tokens`, `applicable_sessions` (adherence pass), adherence label, `corrections` (hard pass), and the rule's own text.
|
|
6
|
+
|
|
7
|
+
## Verdicts
|
|
8
|
+
|
|
9
|
+
| Verdict | When |
|
|
10
|
+
|---|---|
|
|
11
|
+
| `keep` | Evidence that it applies and is obeyed, or it is cheap and encodes something the agent cannot derive from the repo (a credential location, a user preference, a hard prohibition). |
|
|
12
|
+
| `improve` | It applies but is not working — adherence `mixed` or `violated`, or corrections on its subject despite the rule existing. Also when it is correct but oversized, vague, or buried where nothing reads it. |
|
|
13
|
+
| `drop` | Either (a) expensive *and* evidence-free *and* derivable from the repo or already enforced by tooling — all three, not any one; or (b) corroborated `harm` or `irrelevant` evidence in **2 or more distinct sessions**. |
|
|
14
|
+
| `unknown` | Not enough signal either way. The honest default — use it freely. |
|
|
15
|
+
|
|
16
|
+
## Rules for the verdicts themselves
|
|
17
|
+
|
|
18
|
+
**Low adherence is never a reason to drop.** A rule the agent keeps breaking is a rule that is not working, which argues for rewriting it, shortening it, moving it earlier, or replacing prose with a literal command. Dropping it removes the intent along with the failure.
|
|
19
|
+
|
|
20
|
+
**The evidence class is the gate, and it is mechanical.** Only `harm` and `irrelevant` findings can support a `drop`; `non-compliance` never can, however many there are. A hundred violations of a rule the transcripts show to be right is the strongest possible case for `improve`. Check the class before writing the verdict — a `drop` resting on `non-compliance` evidence is a bug in the report, not a judgement call.
|
|
21
|
+
|
|
22
|
+
**Unverified evidence does not exist.** A finding whose quote failed verification was discarded before scoring; it cannot appear in a rationale and cannot be counted toward the two-session bar. If a rule's entire case was discarded, its verdict is `unknown`.
|
|
23
|
+
|
|
24
|
+
**`insufficient_evidence` is not "unused".** The adherence sample is a couple of dozen sessions. Evidence-free means the sample could not see it; combined with high cost, that makes it a *candidate* for review, not a delete.
|
|
25
|
+
|
|
26
|
+
**`drop` needs a cost argument.** State the tokens. A 40-token rule that never fires is noise; a 1,500-token section that never fires is the finding.
|
|
27
|
+
|
|
28
|
+
**Prefer the rollup.** Rank by `est_tokens` × absence of evidence and lead with the aggregate: "these N rules are X% of the file and no sampled session could judge any of them." That is a decision the user can act on. A per-rule quality opinion is not.
|
|
29
|
+
|
|
30
|
+
**Redundancy counts against a rule.** A rule that restates what the repo already makes obvious — a version table duplicating a lockfile, a structure description duplicating the directory tree — is paying rent for information the agent can read on demand. Say what makes it redundant. An index that tells the agent *where* to look is not redundant; it is the cheap version of the thing it points at.
|
|
31
|
+
|
|
32
|
+
**Size is a defect of its own.** A rule too long to hold in mind while working is one the agent will follow selectively. Flag rules over ~2000 characters as `improve` with "split or compress" even when adherence looks fine.
|
|
33
|
+
|
|
34
|
+
## Proposed edits
|
|
35
|
+
|
|
36
|
+
For every `improve`, write the full replacement text, changing only what the evidence justifies. Fix the part that actually failed:
|
|
37
|
+
|
|
38
|
+
- Corrections on its subject → make it concrete. Replace description with the exact command, path or phrasing the user kept asking for.
|
|
39
|
+
- Buried or late in the file → say where it should move and why.
|
|
40
|
+
- Oversized → split into the part that is always needed and the part that can live in a referenced file.
|
|
41
|
+
- Vague ("be careful with X") → name the observable behaviour.
|
|
42
|
+
|
|
43
|
+
Do not rewrite a rule's intent, do not add rules the evidence did not ask for, and do not smooth the user's voice into generic guidance. Their file, their register.
|
|
44
|
+
|
|
45
|
+
## Missing rules
|
|
46
|
+
|
|
47
|
+
A missing-rule candidate needs a cluster of **2 or more** corrections, across different sessions, that no existing rule covers. One occurrence is an anecdote — list it as such if it is interesting, but do not propose a rule from it.
|
|
48
|
+
|
|
49
|
+
Write the proposal in the file's existing style and register, place it in the section it belongs to, and cite the sessions. Prefer the smallest rule that would have prevented every correction in the cluster: the cluster shows exactly what the user had to say, so the rule should say it once, up front, in their words.
|