@akinet/akidevrule 3.5.0 → 3.6.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 CHANGED
@@ -1,5 +1,27 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.6.0] - 2026-09-30
4
+
5
+ ### Added
6
+ - **`claude/hooks/aki-route-guard.mjs` — a `PreToolUse` route gate that enforces the router's second hop by mechanism.** Evidence: `docs/research/rule-delivery-second-hop-sep29.md`, measured on the owner's own Claude Code transcripts — after the router was force-imported (3.5.0) the `[RULES]` receipt rose from 36% to 81% of edit sessions, but the routed file was actually read in 57% versus 56% before, and 26 of those 36 reads came only after the owner typed `akirule`; organic second hop ≈ 18%, at a cost of 126 reminder messages in four days. Root cause: the import fixed what text can fix (the routing is in context) and left the `Read` to a model decision, and the three miss classes — no receipt, receipt naming the wrong domain, receipt naming a file never opened — are three different model decisions no wording reaches; text had already been changed for this twice (2026-08-06, 2026-09-25). Mechanism: on `Edit|MultiEdit|Write|NotebookEdit` the hook maps the edited path to rule files (code extension → `coding` + `pattern`, `.md` → `docs`, `CHANGELOG.md`/`releases.json` → `release`, `.vue`/`.css`/`.scss`/`.tsx` → `ui`, `.vue`/`.ts` under `nuxt.config.*` → `stack`, `.rs`/`src-tauri/` → `tauri`, `.sql`/`migrations/` → `db`, `locales/` → `content`), drops any file the global `CLAUDE.md` already `@`-imports, scans the actor's transcript (the subagent's own file when `agent_id` is present) for a `Read` or a Bash `cat` of each remaining file, and denies the edit with a reason naming the unread ones. Zero model hops in the check; at most one denial per artifact type per session; a subagent is gated on its own transcript, which closes the `agent.A5` "worker inherits nothing" gap by mechanism; fail-open on any error, an unreadable transcript, or three denials for the same file, so a detection bug can never lock a session; `AKI_ROUTE_GUARD=0` disables it; never gated: the corpus under `~/.aki/`, the harness config dir, `/tmp/` and any `scratchpad/` path, and any path outside the session's `cwd` — a Q&A session that writes a throwaway script or a memory note must not pay for rules no project file is governed by. Registered by `install.mjs` with the same idempotent filter-then-push as the update-check hook, Claude Code only (Antigravity attaches rules natively; the skill-only harnesses have no hook surface). Rejected: `SessionStart` injection of rule text (each hook string is capped at 10,000 characters, below every rule file but `pattern`); a per-turn reminder hook (declined 2026-08-03 as one more model hop, and this gate is not one — the edit cannot proceed until the read happened); a state file (the transcript already records every tool call and every hook decision). Residue: routes with no artifact signature (`think`, `proportion`, `biz`, `ux`, the audits) and code discussed without an edit stay on the router's meaning clause.
7
+
8
+ - **`pattern.A9` — Draft, then commit once: exploration lives in a draft owned by the unit showing it; the record changes once, at an explicit commit event; cancel discards the draft and has nothing to undo.** Evidence: an agent implementing drag-and-drop column reordering in a Vue app wrote to the shared store and `localStorage` on every `dragenter`, with `pattern-core` in context, so one drag re-rendered and re-persisted the whole table many times and `Esc` could not cancel because the record had already changed. The owner named the model — dragging is a preview, dropping is the commit — and then asked for the root, not the symptom: the same shape already governs `release.A5` (`[Unreleased]` is the draft, the release event mints the version) and `agent.A3` (a question is answered, never acted on), and no law stated it, so A1 and A8 were not enough for the model to derive it. Mechanism: one law in `pattern.A`, a `ui` map bullet carrying the checkable grep (no store setter, `localStorage`, IPC or fetch inside a `dragenter`/`dragover`/`pointermove`/`input` handler), a lens row in `docs/arch/corpus-map.md` pointing at the three domain applications, and `pattern`/`ui` route signals for the gesture wording. Rejected after a subtraction pass: a `ui.B6` section of its own (shipped first, then cut — a domain section restating a law is the leaf, the law is the root), and placing the law only in `RULE-ui-pattern.md` (drag-and-drop is one manifestation; a form written to the store on every keystroke and an agent editing a file to answer a question are the same miss).
9
+
10
+ ### Changed
11
+ - **`claude/CLAUDE.md` template slimmed to two imports and two paragraphs; the installer block carries the edit-source rule once.** Evidence: plan Item 2 — "edit source, not the deployed copy" appeared three times on the owner's machine (template, installer block, `CLAUDE.local.md`), the router-import history duplicated the research doc, the `ref-ECC` guard referred to a directory the installer excludes and the repo does not ship, and the opening line "keep global context small" sat above 60 KB of imports. Mechanism: the template keeps the two imports, one paragraph on the gate and the lookup rule, one line pointing named corpora at `CLAUDE.local.md`; the installer block gains the one step only the template had (read the source repo's `CLAUDE.md` first) and names all three source directories. Template 3.5 KB → 0.9 KB.
12
+ - `.gitignore` ignores `.akidevsync/project.json*` (the desktop sync app's local state and its timestamped backups, never source).
13
+ - **`RULE-agent-behavior.md`: `B6` Precedence (moved from the manifest); `A2` states the cost unit as round trips rather than "every call re-sends the conversation" (true for tokens, misleading for money under caching); `C3`'s line-merging tells are one sentence each instead of a worked YAML paragraph. `RULE-coding.md` `B3`'s stash ban is a pointer to its root `agent.B3`** (plan Items 3c, 3d). **`coding.B3` and `B5` are one section** — what counts as verification and the six-rung ladder that decides who performs it were one topic written twice (7.7 KB → 4.6 KB); the address `coding.B5` is retired and every citation repointed to `B3` (`agent.B3`, `release.B9`, `akiopen`, `corpus-map`). **`agent.A5` trimmed 4.7 KB → 2.6 KB**: the mechanics stay (a worker inherits nothing, the receipt, both dials, read-only by mechanism, judgment stays with the caller); host tiers and stateless-versus-persistent cost economics live only in `skills/akiflow/references/harness-facts.md`, which the rule now points at. Rejected: delivering `A5` through a `PreToolUse` hook on `Agent` — `additionalContext` fires after the spawn is composed, so it is the per-turn reminder declined 2026-08-03; a deny gate cannot tell a rule-bearing brief from a bare lookup. `agent.A3`'s self-sufficiency kill-test cut to the length of its siblings. Resident context per Claude Code session 81,985 B (3.5.0) → 38,406 B.
14
+ - **`RULE-coding.md` and `RULE-pattern-core.md` leave the `@` imports: two resident files (`RULE-agent-behavior.md`, the router) instead of five; both rules are now router rows (`coding`, `pattern`) gated by the route guard on every code edit.** Evidence: the owner's proposal (2026-09-29) that the two files are not behavior-floor rules and should load at very high sensitivity instead of unconditionally, plus the second-hop measurement above showing that a router row alone would have left roughly four code sessions in five without them — which is why the 2026-08-06 promotion happened and why demotion was first rejected in the same research doc. Mechanism: with the gate, a code-file edit is denied until both files were read in that transcript, so the guarantee the imports provided is restored where it applies (a code turn) and dropped where it never applied (a session that touches no code — the majority for many downstream users of this corpus and akimcp, whose sessions are questions rather than edits). The `[RULES]` receipt's `(core)` set is therefore `agent` alone and `coding,pattern` appear under `(router)`. The router's rule 2 now states the tiering's purpose as a route: a lookup turn — one that only reads, counts, locates or explains what exists — routes nothing, and the `coding`/`pattern` clauses no longer say "any talk about code, when in doubt load" (evidence: an Antigravity session read both files, plus the project's `CLAUDE.md`, to answer "how big is this project's source", with the old clause in its rule description); the same clause now feeds Antigravity, since the installer derives the `akirule-coding`/`akirule-pattern-core` descriptions from the router instead of a hardcoded sentence that predated their route rows. Narrowing is safe on Claude Code because the gate catches an under-route at the first edit; on Antigravity, which has no gate, the clause keeps "ON for every code edit" unambiguous. `payload/GEMINI.md` no longer orders an unconditional session-start `view_file` of both files (it called them core rules, which they no longer are, and it was the mechanism actually loading them for every Antigravity session — a clean-context challenge caught it after the first sweep missed it); its receipt example drops `coding,pattern` from the always-present set. Rule 2 precedes rules 3 and 5, so a project binding (`stack`, `tauri` ON for the whole project) applies to task turns and not to a lookup. `index.md` drops the paragraph recounting the 2026-08-06 → 2026-09-30 import history — it is in this entry and the research doc, and a resident file pays for every line on every request (`docs.A5`). Tradeoff accepted knowingly: a design discussion with no edit, and any meaning-only route, now depends on the router's clause, which is the owner's "extreme sensitivity" reading of the two new route rows; the 2026-08-06 reopen trigger is unchanged in spirit — a `coding`/`pattern` violation traced to LOAD-fail by `aki-conduct` re-promotes the file by reverting one import line. Verified on the dev box 2026-09-30: a source-size question in a code project loaded no routed file on either Claude Code or Antigravity; `scripts/second_hop_audit.py` now measures `coding`/`pattern` too (era split 2026-09-30) for later re-checks. Updated together: `claude/CLAUDE.md`, `skills/akirule/SKILL.md` (two route rows, rule 2, receipt semantics, gate line), `payload/index.md` (manifest tiers, loading paragraph — then removed in this same batch, see Removed), `payload/RULE-pattern-core.md` tier line, `docs/arch/rule-delivery-architecture.md` (route-gate section), `README.md`, `skills/akihelp/SKILL.md`, `skills/akiship/SKILL.md` (receipt line), `install.mjs` (`AG_RULE_MAP` descriptions, `MultiEdit` in the matcher), `payload/GEMINI.md`, `.github/workflows/install-smoke.yml`; the repo's own `CLAUDE.md` gains the change-sweep rule this batch needed twice.
15
+ - **`agent.C3` fences every copy-verbatim artifact (prompt, template, file body, multi-line command block) with four backticks, never three.** Evidence: the owner asked for it after paste artifacts kept breaking — such artifacts routinely contain a fence of their own (a CLAUDE.md fragment, a skill body, a prompt with a code example), so a three-backtick wrapper closes at the inner fence and the rest renders as prose, which the reader copies incomplete without noticing. Mechanism: one line beside the existing prompt-never-hard-wrap bullet, since both exist for the same reason (the text is pasted verbatim, so the rendering must not alter it); the wider fence is CommonMark-standard and renders in every harness and terminal. Scoped to multi-line artifacts, inline code stays for a single token. Not given a `§0` penalty card and not detected by `scythe.py`: the frequency does not yet clear the `pattern.A2` bar, and a fence choice is visible at a glance.
16
+
17
+ - **`docs/ref/*.md` renamed to `fact-*.md` per `docs.A6`, each stamped (`docs.A4`) and given a trail line; the deployed TCC lookup moves to `~/.aki/akidevrule/docs/ref/fact-macos-codesign-tcc.md`.** Evidence: all four are claims about the outside world (two open standards, per-CLI permission dialects, macOS TCC), which `A6` says must carry the `fact-` name and a per-claim trail; deferred from the rule's own batch because one file is deployed by the installer. Mechanism: plain rename with every live reference updated (`CLAUDE.md`, `README.md`, `install.mjs`, `lib/permissions.mjs`, `docs/index.md`, the arch doc, `RULE-stack-tauri.md`, the cross-links); the installer already removes the deployed `docs/` tree before copying, so the old path is pruned on the next install with no extra code. The Agent Skills doc's format-identity claims had rested on secondary sources with no read date; verified 2026-09-30 against `code.claude.com/docs/en/skills` and `antigravity.google/docs/skills`, which corrected two details (Antigravity's workspace path is `.agents/skills/`, and the optional subfolder names differ per vendor and are not validated) and recorded the CLI's documented global path beside the measured `skills.json` mechanism this repo's installer actually relies on.
18
+
19
+ ### Removed
20
+ - **`payload/index.md` — the corpus no longer ships a manifest; the router is the only resident map.** Evidence: the router's Routes table and the manifest both answered "which file, when" (`docs/plan/done/resident-context-slim-sep28.md` Item 1); the manifest had regrown to 22.5 KB, a quarter of every session's resident context, and its Purpose column restated the router clause and the file itself. Root cause: the file was promoted to an import on 2026-08-06 to carry the receipt vocabulary and Precedence, then accumulated prose because it was resident and convenient. Mechanism: each route row now carries its topic (`RULE-coding.md` · `coding`), so the receipt vocabulary lives where the routing does; § Precedence is `agent.B6` (behavior belongs in the behavior floor, `docs.A5` item 5 repointed); the Groups table and the cross-cutting lens moved to `docs/arch/corpus-map.md`, a maintainer's address map that is not installed — `pattern.A7` no longer points at it, since every domain application is already listed inline. Runtime consumers retargeted in the same change: the installer's "Rules deployed" printout (derived from `AG_RULE_MAP` and the router clauses), `aki_version_check.mjs` (intact test on `RULE-agent-behavior.md`), the CI smoke assertion, `akihelp` step 1, `payload/GEMINI.md` receipt wording. `@akinet/akimcp` tests `index.md` only as an existence fallback beside `CHANGELOG.md`, so it keeps working. Rejected: keeping a trimmed `index.md` in `payload/` (the owner's ruling: a map the model does not need belongs to the repo, not the payload); keeping the lens resident inside the router (5.8 KB for a maintainer's dedupe record; reopen if a model restates a root rule in a domain edit because the lens was absent).
21
+
22
+ ### Fixed
23
+ - **`release_lint.py --latest` false-positive `[PARITY]` on every project with an open `[Unreleased]` section.** Evidence: running the B7-gate command on aiobox flagged `releases.json version 0.4.6 has no CHANGELOG entry` even though 0.4.6 has a full CHANGELOG entry — reproduced identically on akitao.com (real ecosystem repo, also mid-accumulation with `[Unreleased]` open). Root cause: `--latest` truncates `_changelog_blocks()` to the single newest block for the reverse `releases.json → CHANGELOG` parity check; the moment that newest block is `[Unreleased]` (the normal `release.A5` working state between releases), the truncated version list cannot contain the last *shipped* version, so the check misreports every prior release as missing. `lint_changelog` now returns both the `--latest`-scoped version list (still used for the forward "did I add this new entry to releases.json" check, which is exactly what `--latest` is meant to scope) and the full, untruncated version list; the reverse check now tests membership against the full list — the truncation still limits which `releases.json` entries get inspected (`items[:1]`), only the comparison target was wrong. Verified: `--all` output is byte-identical before/after on aiobox and 7 other ecosystem repos (zero regression); a synthetic case with a `releases.json` version absent from CHANGELOG entirely still fires `[PARITY]` in both modes (true positive preserved); a synthetic `[Unreleased]`-open-plus-matching-older-version case now exits 0 (the bug scenario, fixed). Rejected: skipping the reverse check whenever `--latest`'s top block is `Unreleased` — that would also blind the check to a `releases.json` entry added without any matching CHANGELOG entry anywhere, which is the exact drift `[PARITY]` exists to catch.
24
+
3
25
  ## [3.5.0] - 2026-09-27
4
26
 
5
27
  ### Added
package/README.md CHANGED
@@ -51,7 +51,7 @@ The installer is `install.mjs` — one cross-platform Node.js program (Node stdl
51
51
 
52
52
  Tooling — have these installed first:
53
53
 
54
- - **Node.js 18+** — the one hard requirement to install or update. The installer and the SessionStart update-check hook are Node; the 18 floor is the built-in `fetch` the update check uses. `install.sh` / `install.ps1` locate `node` and stop with one clear message if it is missing.
54
+ - **Node.js 18+** — the one hard requirement to install or update. The installer and the two Claude Code hooks are Node; the 18 floor is the built-in `fetch` the update check uses. `install.sh` / `install.ps1` locate `node` and stop with one clear message if it is missing.
55
55
  - **Python 3.7+** — only to *run* the akiflow skill helper scripts (`skills/akiflow/scripts/*.py`); never needed for install or update.
56
56
  - `git` — for the `git clone` remote install.
57
57
 
@@ -67,7 +67,7 @@ Interpreter convention (documented once): the installer and hooks run on `node`
67
67
  | `akiflow` | `/akiflow` | Lead-coordinated **agent council** for work needing more than one kind of judgment. The lead's job is two laws: **ANCHOR** — the owner's verbatim message is pinned as the run's immutable first block (`council_open.py` refuses to open a room without it) and every numbered requirement must quote a fragment of it; and **JUSTIFICATION** — every seat, check and script is OFF by default and turns on only when this run produces a reason, so there are no standing seats and no roster derived from a tier. It decomposes the request into owned work items, checks a three-condition activation gate, and convenes seats from the five definitions in `~/.claude/agents/` — one batch, each seat traced to a requirement, never picked from a menu. **Two shapes, discriminated by whether anything is actually being arbitrated.** A *council* is items with adversaries, for work where two competent seats could reach different defensible answers. A *dispatch* is lanes with exclusive file ownership, for a fan-out whose answer is already knowable and whose only real hazard is two workers writing the same file — `--convene` refuses an overlapping `writes:` before a token is spent. Dispatch drops the challenger, the debate and the three-condition gate; it keeps the anchor, the quoted requirements, the `[RULES]` receipts, the durable record and the whole closure gate. It exists because 19 of 70 live rooms posted no debate turn at all and 11 of those still did substantive fan-out work, paying council overhead for machinery they never used. Orthogonally, three modes discriminated by one question, *what changes outside the room*: `discuss`, `audit` (read-only by construction), `execute` (only `aki-maker` may write). The lead does no menial work and settles what doctrine answers, escalating only a one-way door, a contradiction with documented design, or scope expansion — then writes the owner's answer back into doctrine the same turn, so a question never escalates twice. `council_verify.py` refuses closure on a missing anchor, a requirement quoting nothing the owner wrote, a declared seat that left no trace anywhere in the session, a seat with no `[RULES]` receipt, or an unanswered reminder — reading each seat's own `<seat>.md` as well as its turns, and printing `SKIP` rather than `PASS` where there was nothing to check — and deliberately requires no named seat, since an earlier version that did forced a seat to exist in a run with nothing to enforce and was gamed rather than questioned. **A read is a subscription, not a purchase** — the cost model that shapes how the room is used: every turn re-sends the whole history, so a read of size `S` at turn `t` of a `T`-turn run is charged about `S × (T − t)`, and pulling a 50k-token room at turn 50 of 200 costs ~7.5M cache-read tokens from one call. Measured on a real run with three `aki-maker` seats doing every file edit, the lead still held 70% of cache-read and 66% of output: delegation moves the work but not the money, because what a run pays for is the lead's own accumulated context. Hence `council_read.py --grep` to locate for a few hundred bytes and `--turn` to pull only what the grep pointed at. Close-out reconciles declared model tiers against actual spend, cross-CLI calls added by hand since they never appear in the transcript. Design record: [`docs/arch/akiflow.md`](docs/arch/akiflow.md). |
68
68
  | `akithink` | `/akithink`, or self-run | Structured deep thinking: restate → goal excavation → first principles → mandatory critique → convergence into a `docs/` decision record. Self-run (model-invoked, non-interactive, decide-and-report) whenever the task is a decision rather than execution; interactive only when the owner asks. Recommends a top-tier model (Opus/Fable). |
69
69
  | `akihtmlreport` | `/akihtmlreport` | Distills a dense analysis already in the conversation into one self-contained, ultra-wide `REPORT.html` at the project root — no new analysis, no dropped detail — then opens it locally. Exactly one per project; asks before overwriting. |
70
- | `akihelp` | `/akihelp` | Live introduction to the whole installed Aki system, rendered by reading `index.md` and skill frontmatters at runtime — it can never go stale. Includes a **painpoint → what to say** table (sprawling CSS, docs that no longer match code, a half-finished tree, a pre-ship check, a hard-to-reverse decision, padded or hard-wrapped output, over-guarded flows, UX friction, pricing calls) built from that live state, with any row whose target is not installed dropped rather than shown. Closes on the caveat that governs everything else: the router is always present but the `Read` of a routed file is still model-dependent, so name the rule file in the prompt whenever the load must be deterministic. |
70
+ | `akihelp` | `/akihelp` | Live introduction to the whole installed Aki system, rendered by reading the router's Routes table and skill frontmatters at runtime — it can never go stale. Includes a **painpoint → what to say** table (sprawling CSS, docs that no longer match code, a half-finished tree, a pre-ship check, a hard-to-reverse decision, padded or hard-wrapped output, over-guarded flows, UX friction, pricing calls) built from that live state, with any row whose target is not installed dropped rather than shown. Closes on the caveat that governs everything else: the router is always present but the `Read` of a routed file is still model-dependent, so name the rule file in the prompt whenever the load must be deterministic. |
71
71
  | `akigitcommit` | `/akigitcommit` | Turns a messy working tree into a few clean, logically grouped Conventional Commits. Triages a half-finished tree first — finished vs mid-edit vs abandoned vs accidental, asking rather than guessing — then stages by explicit path, never `git add -A`, never pushes unasked. |
72
72
  | `aki-article-writer` | `/aki-article-writer` or natural language | Per-project pipeline for publishable site prose (articles, news/blog posts, announcements, knowledge entries, including a release turned into a post): research & fact-verification, SEO metadata, JSON-LD schema, UX-psychology-aware content, a rendered-output pass on the built HTML before delivery, and a dedicated Image Scout subagent (Gemini Flash / Haiku) for search → download → visual inspection → ffmpeg processing → slug-named WebP output. One subagent per article; image work is always isolated to a separate lightweight subagent. |
73
73
  | `akidevsync-notes` | natural language | Reads/edits a project's `.akidevsync/notes.json` — the per-project task list the Aki-Dev-Sync app itself writes (list/add/pin/mark-done/edit/delete tasks) via a bundled script that preserves the app's own JSON formatting, plus a workflow for cross-checking pinned notes against a shipped release (CHANGELOG + code) before marking them done. |
@@ -97,22 +97,21 @@ A catalog is not a roster. Five files on disk make it easy to pick seats from a
97
97
 
98
98
  - `RULE-*.md` — constraints: what the agent must or must not do (behavior, coding, design/patterns, docs, content, stacks — Nuxt/Cloudflare + Tauri, UI, SEO, release, DB design, business/market).
99
99
  - `METHOD-*.md` — analytical frameworks: how to reason through a specific class of problem. Heavy, loaded only when the task is genuinely analytical.
100
- - `index.md` — file manifest, precedence order, cross-cutting lens.
101
100
 
102
101
  Loading happens on two different mechanisms.
103
102
 
104
- **Core and router — harness-embedded.** `index.md`, `RULE-agent-behavior.md`, `RULE-coding.md`, `RULE-pattern-core.md` and the router `skills/akirule/SKILL.md` are `@`-imported by `~/.claude/CLAUDE.md`, which Claude Code reads mechanically at session start. No model decision is involved, so the four rule files genuinely apply to every task and the routing is always present. The router joined them for the same reason: as a skill it went uninvoked until the owner asked for it by name. The two rule files were promoted out of the router after "default ON" proved to be a statement of intent rather than a mechanism: routing them through a skill meant they loaded only when the model first chose to invoke that skill, and the rules needing the most owner correction were absent from the context rather than present and disobeyed. They cost context in every session, including sessions with no code in them — that is the price of the guarantee. `@` imports have this effect **only** inside `CLAUDE.md` — the same syntax written into a skill body looks like an import but loads nothing, because a skill body is read only after the model has already chosen to invoke the skill.
103
+ **Resident — harness-embedded.** `RULE-agent-behavior.md` and the router `skills/akirule/SKILL.md` are `@`-imported by `~/.claude/CLAUDE.md`, which Claude Code reads mechanically at session start. No model decision is involved, so the behavior floor applies to every task and the routing is always present. The router joined the imports because as a skill it went uninvoked until the owner asked for it by name. `@` imports have this effect **only** inside `CLAUDE.md` — the same syntax written into a skill body looks like an import but loads nothing, because a skill body is read only after the model has already chosen to invoke the skill.
105
104
 
106
- **Everything else — routed by meaning.** Each task turn is classified by the domains it touches and the act (create, decide, audit, ship), in any language; each route carries concept signals in English and Vietnamese as evidence, never as the test, so a paraphrase routes as well as the listed word. The one model-dependent hop left is the `Read` of a routed file. Sensitivity is deliberately high (err toward loading — a false positive costs a few tokens, a false negative causes wrong behavior).
105
+ **Everything else — routed by meaning, and gated where the artifact gives it away.** Each task turn is classified by the domains it touches and the act (create, decide, audit, ship), in any language; each route carries concept signals in English and Vietnamese as evidence, never as the test, so a paraphrase routes as well as the listed word. The one model-dependent hop left is the `Read` of a routed file, and on Claude Code the `aki-route-guard` PreToolUse hook (`claude/hooks/aki-route-guard.mjs`) enforces it for every route with an artifact signature: the first Edit/Write of each artifact type in a session — a code file (`coding` + `pattern`), `.md` (`docs`), `CHANGELOG.md` (`release`), `.vue`/`.css` (`ui`, plus `stack` in a Nuxt project), `.rs` (`tauri`), `.sql` (`db`), `locales/` (`content`) — is denied until those files have been read in that transcript; a subagent is gated on its own transcript. Zero model hops in the check, at most one denial per artifact type per session, fail-open on any error, `AKI_ROUTE_GUARD=0` disables it. `RULE-coding.md` and `RULE-pattern-core.md` were resident imports from 2026-08-06 to 2026-09-30 because the router as a skill went uninvoked; importing the router restored the routing but not the read (measured: the model read a routed file unprompted in about one session in five — `docs/research/rule-delivery-second-hop-sep29.md`), so the gate now restores the guarantee by mechanism and the two files load only on code turns. Meaning-only routes (`think`, `proportion`, `biz`, `ux`, the audits) stay on the router alone, with sensitivity deliberately high (err toward loading — a false positive costs a few tokens, a false negative causes wrong behavior). The one deliberate exception is a lookup: a turn that only reads, counts, locates or explains what exists routes nothing, because no rule's act is performed — those turns are the majority for many users of this corpus, and the tiering exists so they pay for nothing beyond the two resident files.
107
106
 
108
- - **Contextual and analytical — read on route match:** `RULE-docs.md` (structure and lifecycle, evidence-bound `ref/fact-*` fact docs, plus the docs-vs-code drift audit), `RULE-content-write.md` (UI copy and writing style, plus the content audit — canonical-term drift, density deletion test, i18n coverage, fact-check), `RULE-stack-akiNuxtCf.md`, `RULE-stack-tauri.md` (Tauri v2 + Rust: never-block-the-UI, version SSOT, target context, the macOS TCC/Gatekeeper boundary for spawned sidecars), `RULE-ui-pattern.md` (design-system layer: the subtraction pass that runs before the tier ladder, class taxonomy, tokens, variant API, and the audit playbook), `RULE-seo.md` (metadata, schema, sitemap, and URL form: relative at rest, absolute only where a consumer requires it), `RULE-release.md`, `RULE-db-design.md`, `RULE-biz.md` (market-facing decisions: positioning, pricing, audience) — plus the analytical methods (tagged `Analytical` in `index.md`, loaded on route match like the rest): `METHOD-audit-flow.md` (refactors, multi-file bugs, fragile flows), `METHOD-audit-zero-trust.md` (strict mechanical-first audit: detectors before opinion, exact matches separated from pattern-level candidates), `METHOD-deep-think.md` (scope/architecture/value decisions, first-principles and critique-style thinking), `METHOD-ux-psych.md` (UX/user-behavior evaluation, onboarding and conversion flows), `METHOD-proportionality.md` (sizing a guard, limit or accepted risk against reach, capability, motive and blast radius — the lens that stops both over-engineering and client-side-limits-as-enforcement), `METHOD-audit-subtraction.md` (repo-wide "does this need to exist" sweep, terminating on two dry rounds), and `METHOD-audit-frozen-reference.md` (compliance audit for a clause naming a concrete external artifact as the canonical shape to match — resolve to an exact path, diff literally against it, never judge from memory of the rule's prose).
107
+ - **Contextual and analytical — read on route match:** `RULE-coding.md` (code-quality floor: source of truth, changing existing code, verification and the hand-off ladder, comment budget, runtime safety), `RULE-pattern-core.md` (the 9 structural laws, decomposition, the critique gate before any abstraction), `RULE-docs.md` (structure and lifecycle, evidence-bound `ref/fact-*` fact docs, plus the docs-vs-code drift audit), `RULE-content-write.md` (UI copy and writing style, plus the content audit — canonical-term drift, density deletion test, i18n coverage, fact-check), `RULE-stack-akiNuxtCf.md`, `RULE-stack-tauri.md` (Tauri v2 + Rust: never-block-the-UI, version SSOT, target context, the macOS TCC/Gatekeeper boundary for spawned sidecars), `RULE-ui-pattern.md` (design-system layer: the subtraction pass that runs before the tier ladder, class taxonomy, tokens, variant API, and the audit playbook), `RULE-seo.md` (metadata, schema, sitemap, and URL form: relative at rest, absolute only where a consumer requires it), `RULE-release.md`, `RULE-db-design.md`, `RULE-biz.md` (market-facing decisions: positioning, pricing, audience) — plus the analytical methods (`METHOD-*`, loaded on route match like the rest): `METHOD-audit-flow.md` (refactors, multi-file bugs, fragile flows), `METHOD-audit-zero-trust.md` (strict mechanical-first audit: detectors before opinion, exact matches separated from pattern-level candidates), `METHOD-deep-think.md` (scope/architecture/value decisions, first-principles and critique-style thinking), `METHOD-ux-psych.md` (UX/user-behavior evaluation, onboarding and conversion flows), `METHOD-proportionality.md` (sizing a guard, limit or accepted risk against reach, capability, motive and blast radius — the lens that stops both over-engineering and client-side-limits-as-enforcement), `METHOD-audit-subtraction.md` (repo-wide "does this need to exist" sweep, terminating on two dry rounds), and `METHOD-audit-frozen-reference.md` (compliance audit for a clause naming a concrete external artifact as the canonical shape to match — resolve to an exact path, diff literally against it, never judge from memory of the rule's prose).
109
108
  - **Full load on explicit request:** asking, in any wording, to load the whole corpus reads every `RULE-*`/`METHOD-*` file at once.
110
109
 
111
110
  No harness magic beyond the `CLAUDE.md` import: routes are instructions telling Claude to Read the file from `~/.aki/akidevrule/` when the task's domain matches; the full-load request is the escape hatch.
112
111
 
113
112
  ### Addressing — `topic.A1`, and the `⟨Aki⟩` flag
114
113
 
115
- Every rule/method file is internally organized into groups `A`/`B`/`C` and numbered items `1`/`2`/`3…`, so any single rule can be named precisely — `coding.B2` (changing existing code), `stack.C1` (canonical component names) — without touching routing or renaming any file (`topic` is the filename minus its `RULE-`/`METHOD-` prefix). The full group map lives in `payload/index.md`.
114
+ Every rule/method file is internally organized into groups `A`/`B`/`C` and numbered items `1`/`2`/`3…`, so any single rule can be named precisely — `coding.B2` (changing existing code), `stack.C1` (canonical component names) — without touching routing or renaming any file (`topic` is the filename minus its `RULE-`/`METHOD-` prefix). The full group map and the cross-cutting lens (which file holds the root of a subject several files touch) live in `docs/arch/corpus-map.md`, a maintainer's doc that is not installed.
116
115
 
117
116
  Three files (`RULE-seo.md`, `RULE-release.md`, `RULE-stack-akiNuxtCf.md`) mix universal rules with content specific to Aki's own AkiNuxtCf ecosystem (usePageSeo API, releases.json schema, canonical component names, …). That ecosystem-specific content is isolated into each file's **last group**, logically flagged `⟨Aki⟩`. It stays in this public repo and auto-loads like everything else — Aki is this repo's heaviest user, so auto-load stays more valuable than a clean public/private split — but the flag marks exactly what a stripped public export would drop. Every other file, and every group outside `⟨Aki⟩`, is 100% universal.
118
117
 
@@ -138,7 +137,7 @@ A `SessionStart` hook and the installer's `--check` flag both classify install s
138
137
 
139
138
  | State | Condition | Hook (SessionStart) | `install.mjs --check` |
140
139
  |---|---|---|---|
141
- | **missing** | no installed `CHANGELOG.md` (or `index.md`) | announces every session — no 24h throttle | prints "not installed" + the install command |
140
+ | **missing** | no installed `CHANGELOG.md` (or `RULE-agent-behavior.md`) | announces every session — no 24h throttle | prints "not installed" + the install command |
142
141
  | **current** | installed released semver == remote | silent | prints "up to date" (`run_install()` skips its y/n overwrite prompt only when `.version`'s `commit=` equals the checkout HEAD with a clean tree — the overwrite source is the checkout, not remote) |
143
142
  | **update** | remote newer than installed | announces `x.y.z → a.b.c` + the update command (24h throttle) | prints `x.y.z → a.b.c` |
144
143
  | **ahead** | installed newer than remote, or installed CHANGELOG is Unreleased-only | silent — never nag a dev machine | prints "ahead of remote" |
@@ -166,7 +165,7 @@ Install once; from then on the system has two kinds of surface. **Rules load the
166
165
 
167
166
  Three habits that make the system pay off:
168
167
 
169
- - **Cite rules by address, not by pasting them.** Every rule item has a stable address — `coding.B4`, `pattern.A2`, `agent.A3` — mapped in `payload/index.md`. One address in a prompt, review comment, or commit message names an exact obligation without duplicating its text.
168
+ - **Cite rules by address, not by pasting them.** Every rule item has a stable address — `coding.B4`, `pattern.A2`, `agent.A3` — mapped in `docs/arch/corpus-map.md`. One address in a prompt, review comment, or commit message names an exact obligation without duplicating its text.
170
169
  - **Bind each project with a short root `CLAUDE.md`** — project facts and stricter constraints only, referencing the shared corpus instead of copying it (see [Project binding & change policy](#project-binding--change-policy)).
171
170
  - **Edit rules in this repo, never in the installed copies.** Everything under `~/.aki/akidevrule`, `~/.claude/skills`, and the managed parts of `~/.claude/settings.json` is overwritten on every install; the change flow is always source repo → `node install.mjs` (or `npx @akinet/akidevrule@latest`).
172
171
 
@@ -174,7 +173,6 @@ Three habits that make the system pay off:
174
173
 
175
174
  ```text
176
175
  payload/ → installed to ~/.aki/akidevrule/
177
- index.md
178
176
  RULE-agent-behavior.md
179
177
  RULE-coding.md
180
178
  RULE-pattern-core.md
@@ -219,6 +217,7 @@ skills/ → shared Agent Skills corpus (SKILL.md open
219
217
 
220
218
  scripts/ → repo-only tooling, never installed
221
219
  test-agy-bias.sh (6-trap agy/Gemini bias regression suite, runnable by anyone with agy — docs/plan/done/agy-helpful-bias-containment.md §3)
220
+ second_hop_audit.py (measures, per route and era, whether Claude Code sessions read the routed rule before the first edit — docs/research/rule-delivery-second-hop-sep29.md)
222
221
 
223
222
  claude/ → Claude Code-only runtime assets, installed to ~/.claude/
224
223
  CLAUDE.md
@@ -229,14 +228,15 @@ claude/ → Claude Code-only runtime assets, installed
229
228
  agents/aki-maker.md
230
229
  hooks/aki-update-check.mjs
231
230
  hooks/aki_version_check.mjs (shared version-status parser, imported by both the hook and install.mjs --check)
231
+ hooks/aki-route-guard.mjs (PreToolUse route gate: denies the first edit of an artifact type until its routed rule was Read)
232
232
  fragments/settings.akidoc.fragment.json (illustrative reference only — never apply manually)
233
233
 
234
234
  docs/ → repo-internal records; one TCC lookup is installed
235
235
  index.md (master doc index)
236
- arch/ (current-state design records: rule delivery, akiflow)
236
+ arch/ (current-state design records: rule delivery, akiflow, corpus-map — the topic.A1 address map and the cross-cutting lens)
237
237
  plan/ · plan/done/ (execution plans; completed plans move to done/)
238
238
  research/ (event records: frozen body, dated amendments, a successor doc when the decision changes)
239
- ref/ (stable lookups; macos-codesign-tcc.md → ~/.aki/akidevrule/docs/ref/)
239
+ ref/ (stable lookups; fact-macos-codesign-tcc.md → ~/.aki/akidevrule/docs/ref/)
240
240
 
241
241
  CLAUDE.md (operating rules for agents working IN this repo — not installed anywhere)
242
242
  GEMINI.md (the per-project Antigravity bootstrap, serving this repo itself; copied into other projects by hand)
@@ -252,7 +252,7 @@ install.ps1 (thin launcher → node install.mjs)
252
252
  flowchart TD
253
253
  subgraph SRC["📦 Source: akidevrule Repo"]
254
254
  PAYLOAD["payload/ (18 raw rule files)"]
255
- TCCREF["docs/ref/macos-codesign-tcc.md"]
255
+ TCCREF["docs/ref/fact-macos-codesign-tcc.md"]
256
256
  PGEMINI["payload/GEMINI.md (template)"]
257
257
  CSKILLS["skills/ (11 skills, shared open standard)"]
258
258
  CCLAUDE["claude/CLAUDE.md (template)"]
@@ -266,7 +266,7 @@ flowchart TD
266
266
  %% TARGET 1: ~/.aki/akidevrule/
267
267
  subgraph T1["📂 1. Shared SSOT Rule Corpus (~/.aki/akidevrule/)"]
268
268
  R_CORPUS["*.md (Raw payload rules)"]
269
- R_TCC["docs/ref/macos-codesign-tcc.md"]
269
+ R_TCC["docs/ref/fact-macos-codesign-tcc.md"]
270
270
  R_AGSKILLS["agskills/ (Shared skill tree for AG)"]
271
271
  R_META[".source-repo & .version"]
272
272
  end
@@ -311,18 +311,18 @@ flowchart TD
311
311
 
312
312
  Targets 4-6 only get the shared skill corpus (no rule corpus / no `CLAUDE.md`/`GEMINI.md`-style overrides — those CLIs have no equivalent hard-load hook this baseline plugs into yet). Each sync is scoped per skill folder name via a Node `fs` copy plus a managed-names-only prune, same never-touch-the-rest guarantee as targets 2 and 3, and runs unconditionally — harmless if that CLI isn't installed on the machine, picked up the moment it is.
313
313
 
314
- 1. Syncs `payload/*` into `~/.aki/akidevrule/` (Node `fs` copy, excludes `ref-ECC/`), removes stale files left by renames, syncs `agskills/` for Antigravity skill inheritance, and deploys the full TCC lookup to `~/.aki/akidevrule/docs/ref/macos-codesign-tcc.md`.
314
+ 1. Syncs `payload/*` into `~/.aki/akidevrule/` (Node `fs` copy, excludes `ref-ECC/`), removes stale files left by renames, syncs `agskills/` for Antigravity skill inheritance, and deploys the full TCC lookup to `~/.aki/akidevrule/docs/ref/fact-macos-codesign-tcc.md`.
315
315
  2. Deploys to **all detected Claude config directories** — `~/.claude` (default primary), all existing `~/.claude*` profile variants (e.g. `~/.claude-9rt`, `~/.claude-prx`), plus `$CLAUDE_CONFIG_DIR` or `--claude-dir <path>` if provided:
316
- - Syncs every skill folder under `skills/*/` (whole directory, including any `references/` or `scripts/`) into `<target>/skills/`, one named folder at a time (copy + managed-names-only prune), removing only Aki's own old/renamed skill directories (`akidoc-*`, `akiadvise`) — any other skill you already have is never touched. `skills/` is a top-level, agent-neutral folder (siblings with `payload/`, not nested under `claude/`) because SKILL.md is a shared open standard both Claude Code and Antigravity/AGY consume identically — see [docs/ref/agent-skills-standard.md](docs/ref/agent-skills-standard.md).
316
+ - Syncs every skill folder under `skills/*/` (whole directory, including any `references/` or `scripts/`) into `<target>/skills/`, one named folder at a time (copy + managed-names-only prune), removing only Aki's own old/renamed skill directories (`akidoc-*`, `akiadvise`) — any other skill you already have is never touched. `skills/` is a top-level, agent-neutral folder (siblings with `payload/`, not nested under `claude/`) because SKILL.md is a shared open standard both Claude Code and Antigravity/AGY consume identically — see [docs/ref/fact-agent-skills-standard.md](docs/ref/fact-agent-skills-standard.md).
317
317
  - Copies `claude/agents/*.md` into `<target>/agents/` **file by file, never a directory mirror with `--delete`** — that folder is a shared namespace where your own agent definitions sit beside Aki's, exactly like `<target>/skills/`, so nothing you did not install is ever removed.
318
318
  - Replaces `<target>/CLAUDE.md` with the packaged guidance (timestamped backup first), appending this machine's source-repo path and an `@<target>/CLAUDE.local.md` import.
319
319
  - Creates `<target>/CLAUDE.local.md` **only if missing** — never overwritten afterward. On profile variants (`~/.claude-*`), the template imports `@~/.claude/CLAUDE.local.md` by default so machine-wide facts are inherited. Put per-machine/per-profile rules there; they survive every reinstall.
320
320
  - Before the confirmation prompt or any mutation, preflights every existing JSON file it may update: each detected profile's `settings.json`, `~/.gemini/config/skills.json`, `~/.gemini/antigravity-cli/settings.json`, and `~/.gemini/settings.json`. A malformed file or non-object root aborts the install with originals untouched. After preflight, updates `<target>/settings.json` with a timestamped backup: read permission for `~/.aki/akidevrule/**`, one `Bash(<launcher> <script>*)` rule per Aki skill script per rendering (absolute and `~/`-literal — Claude Code does not expand `~` before matching), `skillOverrides.akirule = "on"`, idempotent registration of the `SessionStart` update-check hook.
321
- - Installs `<target>/hooks/aki-update-check.mjs` plus its shared parser `<target>/hooks/aki_version_check.mjs`.
321
+ - Installs `<target>/hooks/aki-update-check.mjs` plus its shared parser `<target>/hooks/aki_version_check.mjs`, and `<target>/hooks/aki-route-guard.mjs` registered as a `PreToolUse` hook on `Edit|MultiEdit|Write|NotebookEdit` (idempotent, same filter-then-push as the update check).
322
322
  3. Writes `~/.aki/akidevrule/.version` with `installed=`/`version=`/`commit=`/`branch=` and records the source-repo path in `~/.aki/akidevrule/.source-repo` — `version=` is the just-installed CHANGELOG's latest released semver, the same value `install.mjs --check` and the hook compare against remote.
323
- 4. Installs `payload/GEMINI.md` to `~/.gemini/GEMINI.md` — Antigravity global behavior overrides, stamped with a version marker (`[AKIRULE-AG-OVERRIDES-…]`) on line 1. Generates one native rule file per `RULE-*`/`METHOD-*` under `~/.gemini/config/rules/` with YAML `trigger` frontmatter — `agent` `always_on`, the stacks `glob`, the rest `model_decision` — each description generated from its `akirule` route, so both harnesses route from one table. Deploys 11 skills directly to `~/.gemini/config/skills/` for native auto-discovery (synced per skill folder, same never-touch-the-rest guarantee as step 2), configures `~/.gemini/config/skills.json` with absolute paths as secondary, and merges skill execution permissions into `~/.gemini/antigravity-cli/settings.json` and `~/.gemini/settings.json` — a `command()` prefix rule for every `skills/*/scripts/*.py` per skill root, in both the expanded and the tilde-literal rendering (agy's matcher compares command strings literally — no glob expansion, and no tilde expansion in either direction — so a directory wildcard never matches and a rule only matches a command written the same way; see [docs/ref/cli-permission-allowlist-standard.md](docs/ref/cli-permission-allowlist-standard.md) §1.2) plus scoped `write_file`/`read_file` rules for the council workspace and rule corpus.
323
+ 4. Installs `payload/GEMINI.md` to `~/.gemini/GEMINI.md` — Antigravity global behavior overrides, stamped with a version marker (`[AKIRULE-AG-OVERRIDES-…]`) on line 1. Generates one native rule file per `RULE-*`/`METHOD-*` under `~/.gemini/config/rules/` with YAML `trigger` frontmatter — `agent` `always_on`, the stacks `glob`, the rest `model_decision` — each description generated from its `akirule` route, so both harnesses route from one table. Deploys 11 skills directly to `~/.gemini/config/skills/` for native auto-discovery (synced per skill folder, same never-touch-the-rest guarantee as step 2), configures `~/.gemini/config/skills.json` with absolute paths as secondary, and merges skill execution permissions into `~/.gemini/antigravity-cli/settings.json` and `~/.gemini/settings.json` — a `command()` prefix rule for every `skills/*/scripts/*.py` per skill root, in both the expanded and the tilde-literal rendering (agy's matcher compares command strings literally — no glob expansion, and no tilde expansion in either direction — so a directory wildcard never matches and a rule only matches a command written the same way; see [docs/ref/fact-cli-permission-allowlist-standard.md](docs/ref/fact-cli-permission-allowlist-standard.md) §1.2) plus scoped `write_file`/`read_file` rules for the council workspace and rule corpus.
324
324
  5. Syncs the same skill folders to `~/.agents/skills/` (Codex CLI, Cursor), `~/.kiro/skills/` (Kiro CLI), and `~/.grok/skills/` (Grok CLI) — each a plain global skills root these CLIs read natively, synced per skill folder name exactly like step 2. Skills-only: no rule corpus is generated for these targets.
325
- 6. Pre-allows every Aki skill script in each harness present on the machine, one adapter per rule dialect (`lib/permissions.mjs`): `~/.kiro/settings/permissions.yaml` (a marker-delimited managed block), `~/.codex/rules/akidevrule.rules` (a file akidevrule owns), `~/.cursor/cli-config.json` (`Shell(python3:<script>*)`, never a bare `Shell(python3)`), `~/.config/opencode/opencode.json` (`permission.bash`). Every rule names one exact script, in both path renderings; entries a previous install wrote are replaced, the user's own are kept. Grok CLI and Ollama have no file-based allowlist, so nothing is written for them — [docs/ref/cli-permission-allowlist-standard.md](docs/ref/cli-permission-allowlist-standard.md).
325
+ 6. Pre-allows every Aki skill script in each harness present on the machine, one adapter per rule dialect (`lib/permissions.mjs`): `~/.kiro/settings/permissions.yaml` (a marker-delimited managed block), `~/.codex/rules/akidevrule.rules` (a file akidevrule owns), `~/.cursor/cli-config.json` (`Shell(python3:<script>*)`, never a bare `Shell(python3)`), `~/.config/opencode/opencode.json` (`permission.bash`). Every rule names one exact script, in both path renderings; entries a previous install wrote are replaced, the user's own are kept. Grok CLI and Ollama have no file-based allowlist, so nothing is written for them — [docs/ref/fact-cli-permission-allowlist-standard.md](docs/ref/fact-cli-permission-allowlist-standard.md).
326
326
 
327
327
  Re-running the installer updates the same managed files cleanly.
328
328
 
@@ -351,13 +351,15 @@ rm -rf ~/.agents/skills/{akirule,akiflow,akithink,akihtmlreport,akihelp,akigitco
351
351
  rm -rf ~/.kiro/skills/{akirule,akiflow,akithink,akihtmlreport,akihelp,akigitcommit,akilint,akiopen,akiship,aki-article-writer,akidevsync-notes} # Kiro CLI
352
352
  rm -rf ~/.grok/skills/{akirule,akiflow,akithink,akihtmlreport,akihelp,akigitcommit,akilint,akiopen,akiship,aki-article-writer,akidevsync-notes} # Grok CLI (other, non-Aki skills already in this folder are untouched)
353
353
  rm -f ~/.claude/agents/aki-{hands,judge,conduct,challenger,maker}.md # your own agents in that folder are untouched
354
- rm -f ~/.claude/hooks/aki-update-check.mjs ~/.claude/hooks/aki_version_check.mjs ~/.claude/hooks/aki-update-check.py ~/.claude/hooks/aki_version_check.py
354
+ rm -f ~/.claude/hooks/aki-update-check.mjs ~/.claude/hooks/aki_version_check.mjs ~/.claude/hooks/aki-route-guard.mjs
355
+ # then delete the two entries whose command names those hooks from hooks.SessionStart and hooks.PreToolUse in ~/.claude/settings.json
356
+ rm -f ~/.claude/hooks/aki-update-check.mjs ~/.claude/hooks/aki_version_check.mjs ~/.claude/hooks/aki-route-guard.mjs ~/.claude/hooks/aki-update-check.py ~/.claude/hooks/aki_version_check.py
355
357
  rm -f ~/.gemini/GEMINI.md # restore from a *.akidevrule-backup-* if needed; GEMINI.local.md is left untouched
356
358
  ```
357
359
 
358
360
  On **Windows** the same targets live under `%USERPROFILE%` (e.g. `%USERPROFILE%\.aki\akidevrule`, `%USERPROFILE%\.claude\skills\...`); remove them with `Remove-Item -Recurse -Force`.
359
361
 
360
- Then remove the akidevrule block from `~/.claude/CLAUDE.md` and its entries (permission, skillOverrides, SessionStart hook) from `~/.claude/settings.json` if desired.
362
+ Then remove the akidevrule block from `~/.claude/CLAUDE.md` and its entries (permission, skillOverrides, SessionStart and PreToolUse hooks) from `~/.claude/settings.json` if desired.
361
363
 
362
364
  ## Content for dev.akitao.com
363
365
 
package/claude/CLAUDE.md CHANGED
@@ -1,39 +1,8 @@
1
1
  # Aki global Claude Code guidance
2
2
 
3
- Keep global context small. Prefer current project files and runtime output over stale docs or memory.
4
-
5
- ## Core rules and the router — mechanically loaded, every session
6
-
7
- @~/.aki/akidevrule/index.md
8
3
  @~/.aki/akidevrule/RULE-agent-behavior.md
9
- @~/.aki/akidevrule/RULE-coding.md
10
- @~/.aki/akidevrule/RULE-pattern-core.md
11
4
  @~/.claude/skills/akirule/SKILL.md
12
5
 
13
- The harness embeds these five when it reads this file at session start — no model decision is involved. `index.md` is the corpus map; `RULE-agent-behavior.md` the behavior floor; `RULE-coding.md` the code-quality floor; `RULE-pattern-core.md` the structural floor; `akirule/SKILL.md` the router that decides which contextual rule files to read on each task turn.
14
-
15
- The router is imported rather than left as a skill because a skill runs only when the model decides to invoke it, and it went uninvoked until the owner asked by name. The same failure earlier promoted `RULE-coding.md` and `RULE-pattern-core.md` to core. All five are paid for in every session; that cost is the price of the guarantee.
16
-
17
- What stays best-effort is the second hop: a routed file enters context only when the model `Read`s it on a route match.
18
-
19
- ## Shared Aki rule source
20
-
21
- Aki's shared rule corpus lives at `~/.aki/akidevrule`.
22
-
23
- **IMPORTANT — editing shared rules:** The installed `~/.aki/akidevrule` directory is a **deployed copy**, not the source of truth. To change a shared rule:
24
- 1. Find the source repo: its absolute path on this machine is recorded in `~/.aki/akidevrule/.source-repo`, written by the installer on every install. Read that file — do not guess a location, and do not ask the user for something already recorded. Ask only if the recorded path no longer exists.
25
- 2. Edit under `<source-repo>/payload/` (shared rule corpus), `<source-repo>/skills/` (Agent Skills, shared with Antigravity), or `<source-repo>/claude/` (Claude Code-only runtime assets: global guidance, hooks, settings fragment).
26
- 3. **Read `<source-repo>/CLAUDE.md` before editing.** It carries that repo's own operating rules — which files must be updated together (`payload/index.md`, `skills/akirule/SKILL.md`, `README.md`, `CHANGELOG.md`), file-naming conventions, and non-goals. This step matters most when the request arrives from *another* project's working directory, where that file is not auto-loaded.
27
- 4. Run the installer to propagate changes to the installed copy — `node install.mjs` (any platform), or `./install.sh` / `.\install.ps1`.
28
-
29
- Never edit the installed `~/.aki/akidevrule` files directly — changes will be silently overwritten on the next install.
30
-
31
- ## Named local corpora
32
-
33
- Doc corpora that live outside any single project are often referred to by short name in conversation (e.g. "UNIDOC", "the standards doc"). Their names, paths, and usage notes are **machine-specific**, so they are recorded in `~/.claude/CLAUDE.local.md` — not in this shared file. When the user names a corpus you cannot resolve, read that file before searching the filesystem or asking.
34
-
35
- ## ref-ECC guard
36
-
37
- `~/.aki/akidevrule/ref-ECC` is intentionally very large. Do not scan, summarize, or bulk-load it by default.
6
+ The harness embeds both at session start: the behavior floor and the router. Every other rule enters context when the model `Read`s it on a route match. For routes with an artifact signature the `aki-route-guard` PreToolUse hook denies the first Edit/Write of that artifact type in a session until the routed files were read — the deny reason names them; read them in full, then retry. Meaning-only routes stay model-dependent: route on meaning, load when the domain is in doubt. A turn that only reads, counts or explains what exists routes nothing.
38
7
 
39
- Only use `ref-ECC` when the user explicitly asks for it or when a task has a specific, narrow need for that reference corpus. Prefer targeted file/path lookup over broad search to avoid context bloat.
8
+ Doc corpora named by short name in conversation ("UNIDOC", "the standards doc") are machine-specific: resolve them in `~/.claude/CLAUDE.local.md` before searching or asking.
@@ -36,12 +36,12 @@ Name every rule file you actually read — nothing else. You get one round, so t
36
36
 
37
37
  The mandate, rule manifest, receipt, and output contract above are the same on every lane. Only the substrate changes. This file *is* the definition when the lane is an in-harness Claude subagent; on every other lane the caller pastes the same four sections into the headless prompt, because **no other CLI reads this file**.
38
38
 
39
- **Position in this table is not precedence.** The size of the surface picks the lane, and for a wide read that is agy backgrounded — the discovery default (`agent.A5`).
39
+ **Position in this table is not precedence.** The size of the surface picks the lane, and for a wide read that is agy backgrounded — the discovery default (`skills/akiflow/references/harness-facts.md` § Cross-CLI worker).
40
40
 
41
41
  | Lane | Invocation | Pick it when | Context / rules the caller must pass |
42
42
  |---|---|---|---|
43
43
  | **Claude subagent · `haiku`** | in-harness spawn of this agent; the frontmatter carries `model: haiku` | the paths are known and few, the result must land in this session, or several hands must run **concurrently** — the Agent tool fans out natively where every headless lane needs its own backgrounding | nothing — the frontmatter carries the tools, and this body carries the manifest. Name the paths and the question in the prompt |
44
- | **agy headless · `gemini-3.8-flash-high`, backgrounded** | `agy --model gemini-3.8-flash-high --mode plan --output-format json -p "<prompt>"` — **prompt last** — launched in the background, because a real sweep costs seconds to a minute and there is no reason for the caller to block on it | the discovery default (`agent.A5`): a wide sweep over a large surface, or the Claude quota is the constraint. ~1M context on a separate quota; `--mode plan` makes read-only mechanical rather than worded. Its weakness is skimming, so pay for it in prompt precision, not a bigger model | `~/.gemini/GEMINI.md` auto-loads the behavior baseline, so the behavior floor is free — but **name the domain rule files and the exact absolute paths anyway**: `cwd` is not a scope boundary for agy |
44
+ | **agy headless · `gemini-3.8-flash-high`, backgrounded** | `agy --model gemini-3.8-flash-high --mode plan --output-format json -p "<prompt>"` — **prompt last** — launched in the background, because a real sweep costs seconds to a minute and there is no reason for the caller to block on it | the discovery default (`skills/akiflow/references/harness-facts.md` § Cross-CLI worker): a wide sweep over a large surface, or the Claude quota is the constraint. ~1M context on a separate quota; `--mode plan` makes read-only mechanical rather than worded. Its weakness is skimming, so pay for it in prompt precision, not a bigger model | `~/.gemini/GEMINI.md` auto-loads the behavior baseline, so the behavior floor is free — but **name the domain rule files and the exact absolute paths anyway**: `cwd` is not a scope boundary for agy |
45
45
  | **kiro-cli headless · `claude-sonnet-4.5`** | `kiro-cli chat --no-interactive --trust-tools=fs_read --model claude-sonnet-4.5 "<prompt>"` | a sweep that is wide **and** needs real reading comprehension — flash skims, and this lane buys a strong model on a **third** quota (owner's pick for hands, 2026-08-16). Not the cheap tier: sonnet-4.5 meters 1.3× against `qwen3-coder-next`'s 0.05×, so pick it for the comprehension, not the price | everything: no rule file loads by itself. Paste the four sections above plus exact paths. `--trust-tools=fs_read` is the read-only mechanism |
46
46
  | **cl-9rt (Claude via proxy gateway)** | `CLAUDE_CONFIG_DIR=~/.claude-9rt claude -p --tools "Read,Grep" --model <alias> --effort low "<prompt>"` | a parallel explore lane is wanted **concurrently** with this session, on separate metering | everything, as with kiro. `cl-9rt`/`cl-9rt-min` is a **shell alias** the owner defined interactively — it does not exist in a spawned/non-interactive shell. Always run the expanded literal command shown here, never the alias name. Note the gateway may route an alias to a non-Anthropic core — treat as bandwidth, never as judgment |
47
47
 
@@ -0,0 +1,145 @@
1
+ #!/usr/bin/env node
2
+ // PreToolUse route gate, fail-open. Design: docs/research/rule-delivery-second-hop-sep29.md
3
+ import { readFileSync, existsSync } from "node:fs";
4
+ import { homedir } from "node:os";
5
+ import { join, dirname, basename, extname } from "node:path";
6
+
7
+ const HOME = homedir();
8
+ const RULE_DIR = join(HOME, ".aki", "akidevrule");
9
+ const CONFIG_DIR = process.env.CLAUDE_CONFIG_DIR || join(HOME, ".claude");
10
+ const MARK = "aki-route-guard";
11
+ const MAX_DENIALS_PER_RULE = 3; // a detection bug must never lock a session; after this many denials the rule is treated as read
12
+
13
+ const CODE_EXT = new Set(["ts", "tsx", "js", "jsx", "mjs", "cjs", "vue", "svelte", "rs", "py", "go", "rb", "php", "java", "kt", "swift", "c", "cc", "cpp", "h", "hpp", "cs", "sh", "bash", "zsh", "ps1", "sql", "css", "scss", "lua", "dart"]);
14
+ const FRONTEND_EXT = new Set(["vue", "svelte", "css", "scss", "tsx", "jsx"]);
15
+
16
+ function allow() {
17
+ process.exit(0);
18
+ }
19
+
20
+ /** Rule files a path routes to; only routes with an artifact signature (docs/research/rule-delivery-second-hop-sep29.md §2). */
21
+ function routesFor(filePath, cwd) {
22
+ const p = filePath.replace(/\\/g, "/");
23
+ const name = basename(p);
24
+ const ext = extname(name).slice(1).toLowerCase();
25
+ const rules = new Set();
26
+ if (CODE_EXT.has(ext)) {
27
+ rules.add("RULE-coding.md");
28
+ rules.add("RULE-pattern-core.md");
29
+ }
30
+ if (ext === "md") rules.add("RULE-docs.md");
31
+ if (name === "CHANGELOG.md" || name === "releases.json") rules.add("RULE-release.md");
32
+ if (FRONTEND_EXT.has(ext)) rules.add("RULE-ui-pattern.md");
33
+ if (ext === "sql" || /\/migrations\//.test(p)) rules.add("RULE-db-design.md");
34
+ if (/\/(locales|i18n|lang)\//.test(p)) rules.add("RULE-content-write.md");
35
+ if (ext === "rs" || /\/src-tauri\//.test(p) || name === "tauri.conf.json") rules.add("RULE-stack-tauri.md");
36
+ if ((ext === "vue" || ext === "ts") && isNuxtProject(cwd)) rules.add("RULE-stack-akiNuxtCf.md");
37
+ return rules;
38
+ }
39
+
40
+ /** Scratchpad, harness state and the corpus itself: files no project rule governs, so a Q&A session that writes a throwaway script or a memory note stays ungated. */
41
+ function isUngatedPath(filePath, cwd) {
42
+ const p = filePath.replace(/\\/g, "/");
43
+ if (p.includes("/.aki/")) return true;
44
+ if (p.startsWith(CONFIG_DIR.replace(/\\/g, "/") + "/")) return true;
45
+ if (/^\/tmp\//.test(p) || /\/scratchpad\//.test(p)) return true;
46
+ if (cwd && !p.startsWith(cwd.replace(/\\/g, "/") + "/")) return true;
47
+ return false;
48
+ }
49
+
50
+ function isNuxtProject(cwd) {
51
+ if (!cwd) return false;
52
+ return ["nuxt.config.ts", "nuxt.config.js", "nuxt.config.mjs"].some((f) => existsSync(join(cwd, f)));
53
+ }
54
+
55
+ /** Rule files the harness already embeds via `@` imports in the global CLAUDE.md; never gated. */
56
+ function residentRules() {
57
+ const out = new Set();
58
+ try {
59
+ for (const line of readFileSync(join(CONFIG_DIR, "CLAUDE.md"), "utf8").split("\n")) {
60
+ const m = line.match(/^@.*\/((?:RULE|METHOD)-[\w-]+\.md)\s*$/);
61
+ if (m) out.add(m[1]);
62
+ }
63
+ } catch {
64
+ /* no global file: nothing is resident */
65
+ }
66
+ return out;
67
+ }
68
+
69
+ /** The transcript that records this actor's own tool calls: the subagent file when the hook fires inside a subagent, else the session transcript. */
70
+ function transcriptFor(input) {
71
+ const main = input.transcript_path;
72
+ if (input.agent_id) {
73
+ const sub = join(dirname(main), input.session_id, "subagents", `agent-${input.agent_id}.jsonl`);
74
+ if (existsSync(sub)) return sub;
75
+ }
76
+ return main;
77
+ }
78
+
79
+ /** Scan a transcript once: which rule files were Read (Read tool, or a Bash cat/sed/head/bat of the file), and how many times this hook already denied for each. */
80
+ function scanTranscript(path, wanted) {
81
+ const read = new Set();
82
+ const denials = new Map();
83
+ let text;
84
+ try {
85
+ text = readFileSync(path, "utf8");
86
+ } catch {
87
+ return { read, denials, unreadable: true };
88
+ }
89
+ for (const line of text.split("\n")) {
90
+ if (!line.includes("akidevrule") && !line.includes(MARK)) continue;
91
+ let d;
92
+ try {
93
+ d = JSON.parse(line);
94
+ } catch {
95
+ continue;
96
+ }
97
+ if (d.type === "assistant") {
98
+ const content = d.message && Array.isArray(d.message.content) ? d.message.content : [];
99
+ for (const c of content) {
100
+ if (!c || c.type !== "tool_use" || !c.input) continue;
101
+ if (c.name === "Read") {
102
+ const target = String(c.input.file_path || "");
103
+ for (const r of wanted) if (target.endsWith(r)) read.add(r);
104
+ } else if (c.name === "Bash") {
105
+ const cmd = String(c.input.command || "");
106
+ for (const r of wanted) if (new RegExp(`\\b(cat|sed|head|bat|less)\\b[^|;&\\n]*${r.replace(".", "\\.")}`).test(cmd)) read.add(r);
107
+ }
108
+ }
109
+ } else if (d.attachment && typeof d.attachment.stdout === "string" && d.attachment.stdout.includes(MARK)) {
110
+ for (const r of wanted) if (d.attachment.stdout.includes(r)) denials.set(r, (denials.get(r) || 0) + 1);
111
+ }
112
+ }
113
+ return { read, denials, unreadable: false };
114
+ }
115
+
116
+ function deny(missing, filePath) {
117
+ const files = missing.map((r) => `~/.aki/akidevrule/${r}`).join(" and ");
118
+ const reason = `[${MARK}] Editing ${basename(filePath)} is gated on rule files not yet read in this session: ${missing.join(", ")}. Read ${files} in full with the Read tool, add them to the [RULES] receipt, then retry this edit.`;
119
+ process.stdout.write(JSON.stringify({ hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "deny", permissionDecisionReason: reason } }) + "\n");
120
+ process.exit(0);
121
+ }
122
+
123
+ function main() {
124
+ if (process.env.AKI_ROUTE_GUARD === "0") allow();
125
+ const input = JSON.parse(readFileSync(0, "utf8"));
126
+ const ti = input.tool_input || {};
127
+ const filePath = ti.file_path || ti.notebook_path;
128
+ if (!filePath || !input.transcript_path) allow();
129
+ if (isUngatedPath(filePath, input.cwd)) allow();
130
+ const wanted = [...routesFor(filePath, input.cwd)].filter((r) => existsSync(join(RULE_DIR, r)));
131
+ const resident = residentRules();
132
+ const gated = wanted.filter((r) => !resident.has(r));
133
+ if (gated.length === 0) allow();
134
+ const { read, denials, unreadable } = scanTranscript(transcriptFor(input), gated);
135
+ if (unreadable) allow();
136
+ const missing = gated.filter((r) => !read.has(r) && (denials.get(r) || 0) < MAX_DENIALS_PER_RULE);
137
+ if (missing.length === 0) allow();
138
+ deny(missing, filePath);
139
+ }
140
+
141
+ try {
142
+ main();
143
+ } catch {
144
+ allow();
145
+ }
@@ -66,9 +66,9 @@ function isFile(p) {
66
66
  }
67
67
  }
68
68
 
69
- /** True when installRoot looks intact enough to compare (CHANGELOG.md and index.md both present). */
69
+ /** True when installRoot looks intact enough to compare (CHANGELOG.md and the core rule both present). */
70
70
  export function localInstallPresent(installRoot) {
71
- return isFile(join(installRoot, "CHANGELOG.md")) && isFile(join(installRoot, "index.md"));
71
+ return isFile(join(installRoot, "CHANGELOG.md")) && isFile(join(installRoot, "RULE-agent-behavior.md"));
72
72
  }
73
73
 
74
74
  /** One of the STATE_* constants — see README.md "Update notifications" for the full 5-state table. */
@@ -1,6 +1,8 @@
1
1
  # Local macOS codesign and TCC
2
2
 
3
- The one AkiDevRule lookup for this chain. `tauri.B7` is the concise rule (do not delete it); the installer (`install.mjs`) deploys this file to `~/.aki/akidevrule/docs/ref/macos-codesign-tcc.md`. Evidence trail: `docs/research/macos-tcc-tauri-boundary-aug21.md`.
3
+ `updated 2026-09-30 · v3.5.0`
4
+
5
+ The one AkiDevRule lookup for this chain. `tauri.B7` is the concise rule (do not delete it); the installer (`install.mjs`) deploys this file to `~/.aki/akidevrule/docs/ref/fact-macos-codesign-tcc.md`. Evidence trail: `docs/research/macos-tcc-tauri-boundary-aug21.md`.
4
6
 
5
7
  **Stable self-signed identity keeps TCC grants across rebuilds. Apple ad-hoc (`codesign --sign -`) does not.** Artifact names, install paths, and identity names live in that project’s own docs — not here.
6
8