Skip to content

refactor(claude): triage the 37 always-loaded user rules into hooks, skills, and ambient (~10.4k est. tokens/session) #353

Description

@laurigates

Problem

exact_dot_claude/rules/ holds 47 rules. Ten carry globs: frontmatter and load lazily; the other 37 load into every session of every project:

37 always-loaded, 102,868c ≈ 25,700 est. tokens

That is ~13% of a 200k context window spent before the first prompt is read. Measured 2026-08-05 alongside a /doctor pass; total memory-file load across all scopes was ~58.6k est. tokens (~29%).

The sibling pass on the portfolio rules has already landed as
laurigates/repos-claude-config#21 — six procedural rules promoted to skills, 36,320c → 3,296c, ~8.2k est. tokens saved. This issue is the same triage applied here.

Constraint: these files are chezmoi-managed. All edits go to ~/.local/share/chezmoi/exact_dot_claude/rules/, never the live ~/.claude/rules/ targets.

The triage

A rule is push — resident, shaping behaviour unasked. A skill is pull — loads once recognised as relevant. Converting the wrong rule breaks it: if a hazard rule only loads after I suspect the hazard, I already knew enough not to need it. So sort by whether the trigger is recognisable in advance, into three destinations. This is offload-to-deterministic-substrate.md's own routing table applied to the rules directory itself.

Bucket 1 — mechanically enforceable → PreToolUse hook

Deterministic, fires every time, costs zero context. Highest value where it applies, but it applies to less than it first appears.

Rule Chars Hook shape
repo-deletion-safety 2,576 PreToolUse/Bash on rm -rf <path>: if <path> is a git repo, run git remote -v; block when empty (local-only checkout)
git-hazards (trap #2 only) PreToolUse/Bash on git switch -c/git checkout -b: warn when git log origin/main..main is non-empty

Honest scope note: the rest of git-hazards and most of pr-merge-hazards are diagnostic (how to recognise a symptom), not checkable preconditions. They belong in bucket 2 or 3, not here. pr-merge-hazards #1 already has just -g branch-audit as its deterministic substrate.

Bucket 2 — procedural, recognisable trigger → skill + pointer stub

Each fires at a moment nameable in advance. Same treatment as the portfolio six: body moves verbatim into SKILL.md, rule becomes a stub, description written as a trigger rather than a topic.

Rule Chars Trigger
prefer-diy-over-heavy-dependency 7,079 about to depend on a single-purpose tool / GUI app / bundled binary
pr-merge-hazards 7,607 merging a PR, deleting a branch, force-pushing — keep the gate inline (see below)
verify-machine-facts-before-publishing 4,654 writing org docs from scutil/route/ifconfig/local config readings
read-issue-thread-before-contributing 3,916 scoping a contribution from a GitHub issue
documentation-authoring 3,093 writing or restructuring docs
taskwarrior-tracking 2,978 end-of-session capture (already sibling to task-bulk-ops)
scaffold-fix-backport 2,960 fixing something a generator/template produced
verify-upstream-before-patching 2,710 patching vendored / tarball-installed / forked code
repo-deletion-safety 2,576 if not hooked per bucket 1
Subtotal 37,573 → ~3,600c of stubs

≈ 8,500 est. tokens/session.

pr-merge-hazards follows the worktree-stale-base-merge exception from repos-claude-config#21: the load-bearing checks stay inline in the stub (the gh pr list --head authority order for squash-merge detection; UNSTABLEBLOCKED), and the exposition — stacked-PR auto-close recovery, the push-by-SHA protocol, the negated-closing-keyword trap — moves to the skill.

Bucket 3 — genuinely ambient → stays resident

These have no recognisable trigger, or the trigger is the failure they prevent. Do not convert:

  • communication, decision-defaults, copy-paste-commands — shape every response
  • front-load-executive-decisions, plan-mode, agent-and-tool-selection, security — apply at every task boundary or dispatch
  • diagnose-at-the-failure-point, never-fabricate-test-identifiers — the trap is invisible; by the time you'd think to load the skill, you have already fabricated the identifier or believed the sentinel
  • offload-to-deterministic-substrate, code-quality — meta-principles that inform the others
  • skill-and-agent-catalog-checkload-bearing for this entire plan. Promoting rules to skills only pays off if the catalog is actually consulted at task boundaries. This one must stay resident, and is the reason the pattern works at all.

Bucket 4 — one rule worth splitting rather than moving

tool-use-patterns (10,778c) is the single largest always-loaded rule and is a grab-bag of two different kinds of content:

  • Ambient (keep): Read-before-Edit, re-Read after formatter, the rg -r fabrication trap, "a rejected flag looks like no results", parallel-batch cancellation, Bash denials are terminal
  • Procedural / lookup (promote): the WebFetch failure→fallback table, the agent fan-out burst-limit guidance, the usage-limit mid-run recovery protocol

Splitting should roughly halve it — ≈ 1,300 est. tokens — without making any always-on trap lazy.

Expected total

Bucket Saving
2 — promote to skills ~8,500 tok
4 — split tool-use-patterns ~1,300 tok
1 — hooks ~650 tok + deterministic enforcement
Total ~10,400 est. tokens/session

Roughly 40% off the user-rules load, in every project, on top of the ~8.2k already landed for the portfolio rules.

Suggested execution

  1. Bucket 2 first — highest saving, lowest risk, and the pattern is already proven by repos-claude-config#21 and by the five stubs that already exist here (huggingface-downloads, taskwarrior-bulk-operations, multi-model-delegation, tui-keybinding-terminal-interception, bulk-syntax-sweep-classify-matches).
  2. Bucket 4 split as a separate commit — it is an editorial judgement per section, not a mechanical move.
  3. Bucket 1 last — two hooks, each needing its own test.

Move bodies mechanically (script, not retyping) and verify byte-identical against the pre-change source, as repos-claude-config#21 did. That is what makes "the skill cannot have drifted from the rule it replaces" a checkable claim rather than a hope.

Where do the new skills live? Options: exact_dot_claude/skills/ (user scope, mirrors the current rules location), or promote into the relevant laurigates/claude-plugins plugin (matches how hf-downloads and task-bulk-ops were handled). The existing stubs all point at plugin skills, so the plugin route is the established precedent — worth deciding before starting bucket 2.

Metadata

Metadata

Assignees

Labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions