@akinet/akidevrule 3.3.1 → 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/README.md +34 -29
  3. package/claude/CLAUDE.md +5 -6
  4. package/claude/agents/aki-challenger.md +1 -1
  5. package/claude/agents/aki-conduct.md +2 -2
  6. package/claude/agents/aki-hands.md +4 -4
  7. package/claude/agents/aki-judge.md +2 -2
  8. package/claude/agents/aki-maker.md +2 -2
  9. package/install.mjs +115 -292
  10. package/lib/permissions.mjs +244 -0
  11. package/package.json +5 -2
  12. package/payload/GEMINI.md +2 -0
  13. package/payload/METHOD-audit-frozen-reference.md +33 -0
  14. package/payload/METHOD-audit-zero-trust.md +1 -1
  15. package/payload/METHOD-deep-think.md +1 -1
  16. package/payload/RULE-agent-behavior.md +4 -2
  17. package/payload/RULE-coding.md +2 -1
  18. package/payload/RULE-content-write.md +3 -3
  19. package/payload/RULE-docs.md +27 -6
  20. package/payload/RULE-pattern-core.md +1 -1
  21. package/payload/RULE-release.md +37 -17
  22. package/payload/RULE-seo.md +15 -13
  23. package/payload/RULE-stack-akiNuxtCf.md +1 -0
  24. package/payload/RULE-ui-pattern.md +1 -1
  25. package/payload/index.md +14 -10
  26. package/skills/aki-article-writer/SKILL.md +7 -7
  27. package/skills/aki-article-writer/references/article-workflow.md +11 -15
  28. package/skills/akidevsync-notes/SKILL.md +1 -1
  29. package/skills/akiflow/references/harness-facts.md +8 -6
  30. package/skills/akiflow/scripts/release_lint.py +157 -0
  31. package/skills/akihelp/SKILL.md +7 -7
  32. package/skills/akihtmlreport/SKILL.md +1 -1
  33. package/skills/akilint/SKILL.md +1 -1
  34. package/skills/akiopen/SKILL.md +38 -0
  35. package/skills/akirule/SKILL.md +46 -130
  36. package/skills/akiship/SKILL.md +6 -3
  37. package/skills/akithink/SKILL.md +8 -7
  38. package/skills/akiflow/scripts/council-cost.sh +0 -4
  39. package/skills/akiflow/scripts/council-open.sh +0 -4
  40. package/skills/akiflow/scripts/council-read.sh +0 -4
  41. package/skills/akiflow/scripts/council-verify.sh +0 -4
  42. package/skills/akiflow/scripts/scythe.sh +0 -4
@@ -0,0 +1,244 @@
1
+ // Script pre-allow for every agent harness akidevrule deploys skills to.
2
+ // Matcher facts per harness: docs/ref/cli-permission-allowlist-standard.md.
3
+ import { existsSync, readdirSync, readFileSync, mkdirSync } from "node:fs";
4
+ import { join, dirname, relative, isAbsolute, sep } from "node:path";
5
+
6
+ // ---------------------------------------------------------------------------
7
+ // Inventory — the scripts and the command lines that invoke them
8
+ // ---------------------------------------------------------------------------
9
+
10
+ export function listSkillScripts(skillsSrc) {
11
+ const scripts = [];
12
+ for (const skill of readdirSync(skillsSrc).sort()) {
13
+ const dir = join(skillsSrc, skill, "scripts");
14
+ if (!existsSync(dir)) continue;
15
+ for (const name of readdirSync(dir).sort()) {
16
+ if (name.endsWith(".py")) scripts.push([skill, "scripts", name]);
17
+ }
18
+ }
19
+ return scripts;
20
+ }
21
+
22
+ function launchers(isWin) {
23
+ return isWin ? ["py -3", "python", "python3"] : ["python3"];
24
+ }
25
+
26
+ // Every rendering a model may type: absolute and `~/`-literal, since no surveyed matcher expands `~` on both sides.
27
+ export function scriptInvocations({ roots, scripts, home, isWin }) {
28
+ const out = new Set();
29
+ for (const root of new Set(roots)) {
30
+ for (const parts of scripts) {
31
+ const abs = join(root, ...parts);
32
+ const renderings = [abs];
33
+ const rel = relative(home, abs);
34
+ if (rel && !rel.startsWith("..") && !isAbsolute(rel)) renderings.push("~/" + rel.split(sep).join("/"));
35
+ for (const launcher of launchers(isWin)) {
36
+ for (const path of renderings) out.add(`${launcher} ${path}`);
37
+ }
38
+ }
39
+ }
40
+ return [...out];
41
+ }
42
+
43
+ // ---------------------------------------------------------------------------
44
+ // Ownership — which existing entries this installer wrote and may replace
45
+ // ---------------------------------------------------------------------------
46
+
47
+ const escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
48
+
49
+ // Owned = any Python launcher pointing into `<akiSkill>/scripts/`, plus the directory-glob rules of earlier releases.
50
+ export function ownershipTest(skillNames) {
51
+ const names = skillNames.map(escapeRe).join("|");
52
+ const current = new RegExp(`(?:py -3|python3?)[\\s:]+\\S*[\\\\/](?:${names})[\\\\/]scripts[\\\\/]`);
53
+ const legacy = /^(?:(?:Bash|command)\()?python3 \S*(?:skills|agskills)[\\/]\*{1,2}\)?$/;
54
+ return (entry) => typeof entry === "string" && (current.test(entry) || legacy.test(entry));
55
+ }
56
+
57
+ export function replaceOwned(list, isOwned, desired) {
58
+ return [...list.filter((e) => !isOwned(e)), ...desired];
59
+ }
60
+
61
+ // Recovers a --claude-dir root already recorded in a shared target's allowlist, so a run that omits the flag does not prune it as stale.
62
+ export function recoverSkillRoots(allow, isOwned, home) {
63
+ const roots = new Set();
64
+ for (const entry of allow) {
65
+ if (!isOwned(entry)) continue;
66
+ const m = /^command\((?:py -3|python3?)[ :]+(.+)\)$/.exec(entry);
67
+ if (!m) continue;
68
+ let p = m[1];
69
+ if (p.startsWith("~/")) p = join(home, ...p.slice(2).split("/"));
70
+ if (!isAbsolute(p)) continue;
71
+ const root = dirname(dirname(dirname(p)));
72
+ if (isAbsolute(root)) roots.add(root);
73
+ }
74
+ return [...roots];
75
+ }
76
+
77
+ // ---------------------------------------------------------------------------
78
+ // File writers
79
+ // ---------------------------------------------------------------------------
80
+
81
+ const isObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
82
+
83
+ function writeIfChanged(path, before, after, io) {
84
+ if (before === after) return false;
85
+ mkdirSync(dirname(path), { recursive: true });
86
+ if (existsSync(path)) io.backup(path);
87
+ io.writeText(path, after);
88
+ return true;
89
+ }
90
+
91
+ function updateJson(path, mutate, io) {
92
+ const before = existsSync(path) ? readFileSync(path, "utf-8") : null;
93
+ const data = before === null ? {} : JSON.parse(before);
94
+ if (!isObject(data)) throw new Error(`must be a JSON object: ${path}`);
95
+ mutate(data);
96
+ return writeIfChanged(path, before, JSON.stringify(data, null, 2) + "\n", io);
97
+ }
98
+
99
+ function allowList(data, key = "allow") {
100
+ if (!isObject(data.permissions)) data.permissions = {};
101
+ if (!Array.isArray(data.permissions[key])) data.permissions[key] = [];
102
+ return data.permissions;
103
+ }
104
+
105
+ const BLOCK_START = "# >>> akidevrule managed — regenerated on every install";
106
+ const BLOCK_END = "# <<< akidevrule managed";
107
+
108
+ // Kiro 3.x: one `rules:` list per file; the managed items live between two marker comments at its end.
109
+ function writeKiroYaml(path, matches, isOwned, io) {
110
+ const before = existsSync(path) ? readFileSync(path, "utf-8") : null;
111
+ let body = (before ?? "").replace(new RegExp(`\\n?${escapeRe(BLOCK_START)}[\\s\\S]*?${escapeRe(BLOCK_END)}\\n?`), "\n");
112
+ // Pre-marker releases appended bare allow items; drop those whose every match line is owned.
113
+ body = body.replace(/\n - capability: shell\n match:\n((?: - ".*"\n)+) effect: allow\n?/g, (item, lines) =>
114
+ lines.trimEnd().split("\n").every((l) => isOwned(JSON.parse(l.trim().slice(2)))) ? "\n" : item
115
+ );
116
+ body = body.replace(/\s+$/, "");
117
+ if (!/^rules:/m.test(body)) body = (body ? body + "\n" : "") + "rules:";
118
+ const block = [BLOCK_START, " - capability: shell", " match:", ...matches.map((m) => ` - ${JSON.stringify(m)}`), " effect: allow", BLOCK_END];
119
+ return writeIfChanged(path, before, body + "\n" + block.join("\n") + "\n", io);
120
+ }
121
+
122
+ function splitLauncher(invocation) {
123
+ const i = invocation.startsWith("py -3 ") ? 5 : invocation.indexOf(" ");
124
+ return [invocation.slice(0, i), invocation.slice(i + 1)];
125
+ }
126
+
127
+ // ---------------------------------------------------------------------------
128
+ // Harness adapters — one per rule dialect
129
+ // ---------------------------------------------------------------------------
130
+
131
+ // Claude Code: `*` spans `/` and spaces; used inside the settings.json merge the installer already owns.
132
+ export function claudeAllowRules(invocations) {
133
+ return invocations.map((inv) => `Bash(${inv}*)`);
134
+ }
135
+
136
+ // Each adapter: which config it owns, which skill roots its sessions see, and how to write the rules.
137
+ export function harnessAdapters(ctx) {
138
+ const { home, claudeSkillRoots, dirs } = ctx;
139
+ const primaryClaude = claudeSkillRoots[0];
140
+ return [
141
+ {
142
+ id: "antigravity",
143
+ present: existsSync(dirs.gemini),
144
+ roots: [dirs.geminiSkills, ...claudeSkillRoots],
145
+ files: [join(dirs.gemini, "antigravity-cli", "settings.json"), join(dirs.gemini, "settings.json")],
146
+ // Literal string-prefix matcher, no glob: one rule per exact invocation.
147
+ write(path, invs, isOwned, io) {
148
+ return updateJson(path, (data) => {
149
+ const perms = allowList(data);
150
+ const lanes = [
151
+ `write_file(${join(home, ".aki", "agent-council")}/)`,
152
+ `read_file(${join(home, ".aki", "akidevrule")}/)`,
153
+ "write_file(~/.aki/agent-council/)",
154
+ "read_file(~/.aki/akidevrule/)",
155
+ ];
156
+ perms.allow = replaceOwned(perms.allow.filter((e) => !lanes.includes(e)), isOwned, [
157
+ ...invs.map((inv) => `command(${inv})`),
158
+ ...lanes,
159
+ ]);
160
+ perms.allowNonWorkspaceAccess = true;
161
+ perms.agentMode = true;
162
+ if (!Array.isArray(perms.trustedWorkspaces)) perms.trustedWorkspaces = [];
163
+ if (!perms.trustedWorkspaces.includes(home)) perms.trustedWorkspaces.push(home);
164
+ }, io);
165
+ },
166
+ },
167
+ {
168
+ id: "kiro",
169
+ present: existsSync(dirs.kiro),
170
+ roots: [dirs.kiroSkills, primaryClaude],
171
+ files: [join(dirs.kiro, "settings", "permissions.yaml")],
172
+ write: (path, invs, isOwned, io) => writeKiroYaml(path, invs.map((inv) => `${inv}*`), isOwned, io),
173
+ },
174
+ {
175
+ id: "codex",
176
+ present: existsSync(dirs.codex),
177
+ roots: [dirs.agentsSkills, primaryClaude],
178
+ // A whole file akidevrule owns — regenerated, never merged.
179
+ files: [join(dirs.codex, "rules", "akidevrule.rules")],
180
+ write(path, invs, _isOwned, io) {
181
+ const before = existsSync(path) ? readFileSync(path, "utf-8") : null;
182
+ const rules = invs.map((inv) => {
183
+ const [launcher, script] = splitLauncher(inv);
184
+ const pattern = [...launcher.split(" "), script].map((t) => JSON.stringify(t)).join(", ");
185
+ return `prefix_rule(pattern = [${pattern}], decision = "allow")`;
186
+ });
187
+ return writeIfChanged(path, before, ["# Generated by akidevrule installer — do not edit.", ...rules, ""].join("\n"), io);
188
+ },
189
+ },
190
+ {
191
+ id: "cursor",
192
+ present: existsSync(dirs.cursor),
193
+ roots: [dirs.agentsSkills, primaryClaude],
194
+ files: [join(dirs.cursor, "cli-config.json")],
195
+ // `Shell(<first token>:<args glob>)`; a bare `Shell(python3)` would allow every Python command.
196
+ write(path, invs, isOwned, io) {
197
+ return updateJson(path, (data) => {
198
+ const perms = allowList(data);
199
+ perms.allow = replaceOwned(perms.allow, isOwned, invs.map((inv) => {
200
+ const [launcher, args] = splitLauncher(inv);
201
+ const [base, ...flags] = launcher.split(" ");
202
+ return `Shell(${base}:${[...flags, args].join(" ")}*)`;
203
+ }));
204
+ }, io);
205
+ },
206
+ },
207
+ {
208
+ id: "opencode",
209
+ present: existsSync(dirs.opencode),
210
+ roots: [primaryClaude, dirs.agentsSkills],
211
+ files: [join(dirs.opencode, "opencode.json")],
212
+ // `permission.bash` maps glob → action, last match wins, so owned keys go last.
213
+ write(path, invs, isOwned, io) {
214
+ return updateJson(path, (data) => {
215
+ if (!isObject(data.permission)) data.permission = {};
216
+ const bash = data.permission.bash;
217
+ const kept = isObject(bash) ? bash : typeof bash === "string" ? { "*": bash } : {};
218
+ const next = Object.fromEntries(Object.entries(kept).filter(([k]) => !isOwned(k)));
219
+ for (const inv of invs) next[`${inv}*`] = "allow";
220
+ data.permission.bash = next;
221
+ }, io);
222
+ },
223
+ },
224
+ ];
225
+ }
226
+
227
+ // Returns one report row per file touched or skipped, for the install summary.
228
+ export function applyHarnessPreAllow(ctx, io) {
229
+ const isOwned = ownershipTest(ctx.akiSkillNames);
230
+ const report = [];
231
+ for (const adapter of harnessAdapters(ctx)) {
232
+ if (!adapter.present) continue;
233
+ const invs = scriptInvocations({ roots: adapter.roots, scripts: ctx.scripts, home: ctx.home, isWin: ctx.isWin });
234
+ for (const file of adapter.files) {
235
+ try {
236
+ const changed = adapter.write(file, invs, isOwned, io);
237
+ report.push({ id: adapter.id, file, rules: invs.length, changed });
238
+ } catch (err) {
239
+ report.push({ id: adapter.id, file, error: err.message });
240
+ }
241
+ }
242
+ }
243
+ return report;
244
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@akinet/akidevrule",
3
- "version": "3.3.1",
4
- "description": "Aki's shared rule corpus + Agent Skills for Claude Code, Gemini/Antigravity, Codex, Kiro and Grok — install and update with one command.",
3
+ "version": "3.5.0",
4
+ "description": "Aki's shared rule corpus + Agent Skills for Claude Code, Gemini/Antigravity, Codex, Kiro, Grok, Cursor and OpenCode — install and update with one command.",
5
5
  "keywords": [
6
6
  "claude-code",
7
7
  "agent-skills",
@@ -10,6 +10,8 @@
10
10
  "codex",
11
11
  "kiro",
12
12
  "grok",
13
+ "cursor",
14
+ "opencode",
13
15
  "ai-rules",
14
16
  "coding-standards",
15
17
  "installer",
@@ -40,6 +42,7 @@
40
42
  "install.mjs",
41
43
  "install.sh",
42
44
  "install.ps1",
45
+ "lib/",
43
46
  "payload/",
44
47
  "skills/",
45
48
  "claude/",
package/payload/GEMINI.md CHANGED
@@ -33,6 +33,8 @@ These directives patch Antigravity's known weak spots. They are hard-loaded (no
33
33
 
34
34
  ## 3. Always comply with the akirule corpus — and with rule 0
35
35
  - The shared rule corpus installed at `~/.aki/akidevrule/` ("akirule") applies to you, not only to other agents. When a task touches an area it covers, follow it.
36
+ - **Session start, before the first task action:** `view_file` `~/.aki/akidevrule/RULE-coding.md` and `~/.aki/akidevrule/RULE-pattern-core.md` in full. They are core rules; the rules budget cannot inline them, so this read is how they enter context. Read every `~/.aki/akidevrule/` file from that directory — the copies under `~/.gemini/config/rules/` are not readable by `view_file`.
37
+ - **First line of every response is the receipt** `[RULES] agent (always_on) + coding,pattern,<topics you viewed this session> (viewed)` — topic addresses from `~/.aki/akidevrule/index.md`. A rule absent from the line was not read; the line is self-reported and is a diagnostic, never proof of compliance.
36
38
  - **Re-assertion of rule 0, by design:** whatever else you are doing, you comply with the prime directive. Never act outside the requested scope.
37
39
 
38
40
  ## 4. Rule 0 again — no unrequested action, at any cost
@@ -0,0 +1,33 @@
1
+ # Frozen-Reference Compliance Audit
2
+
3
+ <!-- Address map: frozen-ref.A1-3 · frozen-ref.B1-5 · frozen-ref.C1-5 -->
4
+
5
+ **Activation**: a rule or project doc names one or more concrete implementations as the canonical/frozen shape another project must match structurally (a pinned dependency version, a byte-identical component, a literal config key, a frozen page layout) — and the user asks whether the target actually conforms, at any strictness down to a single character.
6
+
7
+ ## Why this method exists
8
+
9
+ Ordinary rule-following audits judge against *principles* (DRY, SRP, naming) — a project can satisfy those by reading well. A frozen-reference clause instead claims literal parity with a specific artifact that exists somewhere else. Reading the rule's prose and judging "this looks compliant" against memory or impression is the single highest-frequency failure mode for this class of clause: the rule text describes the shape, but only the reference file *is* the shape. A rule audited this way will keep re-surfacing the same violations across sessions, because "looks compliant" is not falsifiable and each session re-derives its own impression instead of re-running the same diff.
10
+
11
+ This method forces the audit to be a diff against the actual reference bytes, never a recollection of them. It inherits `zero-trust`'s CERTAIN/SUGGESTED evidence split and read-only discipline (`agent.B5`) — this file adds what is specific to *comparing against a named external artifact* rather than auditing a codebase against itself.
12
+
13
+ ## A. Locate every reference before judging anything
14
+
15
+ 1. **Resolve the exact file(s)** the clause points to — not "the project that implements X" but the literal path(s):line(s). A frozen-reference clause with no resolvable path is not yet auditable; resolve it first or say plainly it cannot be resolved.
16
+ 2. **A single reference can itself have drifted from the written rule.** When more than one implementation of the standard exists, read at least two independently before ruling — where they disagree with each other, that is a defect in the *rule corpus*, not in the target project being audited. Report it as a **standard-vs-practice gap**, separate from target defects, and never charge it against the target.
17
+ 3. **Note what changed since the clause was written.** A frozen reference frozen a year ago against a codebase that has since evolved elsewhere is now two artifacts pulling apart; say so rather than silently picking a side.
18
+
19
+ ## B. Build the comparison mechanically, never from memory
20
+
21
+ 1. **One row per literal unit**, not per file: a specific exported function name, a specific class/prop, a specific config key, a specific sequence of steps in a flow. "The auth composable looks similar" is not a row; "`loginWithGoogle` returns `Result<T>`, the reference throws" is.
22
+ 2. **Verdict per row is one of exactly:** `MATCH` / `RENAMED` / `MISSING` / `EXTRA` / `STRUCTURAL-DIFF`. No row gets softened into "acceptable variance," "close enough," or "minor style difference" inside the audit — that judgment belongs to whoever reads the report, not to the auditor produced it. State the literal fact; let severity be decided downstream.
23
+ 3. **Distinguish clauses that explicitly allow local naming** (a rule that says "class names follow each project's own tokens") from clauses that claim structural/sequence/value identity. The former makes a naming difference not-a-violation by the rule's own text; the latter makes even a one-character difference a row. Cite which kind of clause governs each row — do not apply free-naming leniency to a clause that never granted it, and do not manufacture a violation out of a clause that did.
24
+ 4. **Every row carries `path:line` on both sides** — the target and whichever reference the row was checked against. A row without both sides is not yet evidence.
25
+ 5. **When the target diverges from the frozen shape, check whether a separate, higher-precedence rule justifies the divergence** (a project-level constraint that pre-dates and conflicts with the frozen clause). Report the conflict explicitly rather than either silently fixing it or silently ignoring it — that decision is the project owner's, not the auditor's.
26
+
27
+ ## C. Report
28
+
29
+ 1. **Verdict first**: does the target meet the standard, plainly stated, before any table.
30
+ 2. **Findings ranked by severity** (hard-rule / functional break → structural drift → cosmetic), each with the comparison row(s) that produced it.
31
+ 3. **A separate, clearly labeled section for standard-vs-practice gaps** (A2) — these are follow-ups for the rule corpus, not the audited project.
32
+ 4. **A coverage line**: which references were read, which clauses could not be resolved to a path, what was explicitly out of scope for this run.
33
+ 5. **Read-only** (`agent.B5`): this produces a report, never a fix. Fixing what the report finds is a separate, explicitly authorized run.
@@ -1,6 +1,6 @@
1
1
  # Zero-Trust Audit Method
2
2
 
3
- **Activation**: the user asks for a strict, uncompromising sweep ("audit khắt khe", "ép rule", "force audit", "quét tuyệt đối", "zero-trust audit", "rà soát toàn bộ").
3
+ **Activation**: the user asks for a strict, uncompromising sweep that must be proven by detectors rather than impression — of the whole project, or of a change plus everything that reads it.
4
4
 
5
5
  Zero trust means nothing counts as clean because it looks clean: a finding exists only when a mechanism produced it, and it weighs exactly what that mechanism weighs — an exact match is a verdict, a pattern match is a candidate. This is **read-only** like every audit (`agent.B5`): it reports, it does not fix, it never mutates git state. Fixing is a separate run, sized after the report is read.
6
6
 
@@ -22,7 +22,7 @@ Not every decision deserves the same depth. Before applying any module, size the
22
22
 
23
23
  This METHOD is consumed three ways:
24
24
 
25
- - **Passive (this file, via akirule):** akirule auto-loads it when a normal task hits a matching signal. Apply the lenses inline, briefly, inside the current answer. Ask at most ONE clarifying question. Never turn a routine task into an interrogation session.
25
+ - **Passive (this file, via akirule):** the router loads it whenever a task evaluates, decides or critiques rather than only executes. Apply the lenses inline, briefly, inside the current answer. Ask at most ONE clarifying question. Never turn a routine task into an interrogation session.
26
26
  - **Triggered self-run:** fired by `agent.A3`'s deep-think triggers, or by owner-authorized self-run (`skills/akithink/SKILL.md` § Self-run mode). Non-interactive — run Modules 1–3 and 5 (add 4 when there is business context), depth scaled to difficulty. Ends in decide-and-report or escalate per `agent.A3`'s outcomes, never in a question left hanging.
27
27
  - **Active (`/akithink` skill):** the user explicitly opens a full structured thinking session. That skill runs a 5-phase interactive protocol and uses this METHOD as its toolbox at maximum depth.
28
28
 
@@ -58,11 +58,11 @@ A worker is a subagent, or the same or another CLI called headlessly (`claude -p
58
58
 
59
59
  **Route by context need, not by the word “exploration.”** Conversation-dependent synthesis, ambiguous classification, and per-item judgment stay with the caller; delegate only the retrieval that feeds them. Prefer a fresh worker for a precise, context-free digest. Use a fork only when a bounded bulk task genuinely needs substantial accumulated context that would be expensive to restate. A worker never delegates again unless the caller explicitly grants a fan-out with bounded depth and width.
60
60
 
61
- **When delegation is warranted, use the current default wide-context tier available, one shot** — on Antigravity that is `agy --model gemini-3.7-flash-high --mode plan -p "<prompt>"` (prompt last; `-p` swallows the next token; owner-set default, 2026-08-15). It holds a very large context; its failure mode is skimming, so the counter is prompt precision rather than a bigger model: name the exact paths, the exact question, and the exact output shape, and leave it nothing to improvise. Keep it to a single call — multi-turn on that CLI degrades badly.
61
+ **When delegation is warranted, use the cheap wide-context tier the host resolves, one shot** — the literal command, model id and read-only mechanism per host live in one table (`skills/akiflow/references/harness-facts.md` § Model tiers › Host resolution), never in this rule. That tier holds a very large context; its failure mode is skimming, so the counter is prompt precision rather than a bigger model: name the exact paths, the exact question, and the exact output shape, and leave it nothing to improvise. Keep it to a single call — multi-turn on those CLIs degrades badly.
62
62
 
63
63
  **Know which kind of cheap you are buying.** A stateless cheap call is cheap *per call* and must re-receive its context every time. A persistent worker (`claude -p --session-id <uuid>`, later `--resume <uuid>`) is cheap *per turn after the first*, because its prefix is cached — roughly an eighth of the opening turn, then flat — and it keeps everything **it** was told, though nothing the caller knows. Use the first for one wide question, the second for a worker you will come back to. The session id is scoped to the directory it was created in.
64
64
  - **A worker inherits nothing** — not your context, not your rules, not your router. Name the exact rule files it must read and the exact paths or targets it must look at. "Follow the project rules" loads nothing and reads as compliance.
65
- - **Require the return leg — the worker reports what it actually received.** Naming the files is only half the loop: a brief that was ignored, a path that no longer resolves, and a rule read in full all produce output that looks the same. The worker's first line is a receipt — `[RULES] agent,coding (brief) | missing: none` — naming the topic address of every rule file it read and listing anything it was told to read and could not. A worker gets one round, so this is not conditional: with no receipt, a later violation cannot be traced to either the brief or the behavior, and those two have opposite fixes. Format and the session-side duty: `skills/akirule/SKILL.md` § Load confirmation.
65
+ - **Require the return leg — the worker reports what it actually received.** Naming the files is only half the loop: a brief that was ignored, a path that no longer resolves, and a rule read in full all produce output that looks the same. The worker's first line is a receipt — `[RULES] agent,coding (brief)` — naming the topic address of every rule file it read; a file the brief named that is absent from the line was not read. A worker gets one round, so this is not conditional: with no receipt, a later violation cannot be traced to either the brief or the behavior, and those two have opposite fixes. Format and the session-side duty: `skills/akirule/SKILL.md` § Load confirmation.
66
66
  - **Set both dials, every time: model tier and thinking effort.** An omitted parameter does not fall back to something cheap; it silently inherits the caller's own expensive settings. Silence is an expensive choice made by accident.
67
67
  - **Enforce read-only by mechanism, not by wording**, wherever "fixing while I'm here" would be unrecoverable — restrict the worker's tool set, or use the CLI's read-only/plan mode. A prompt-worded ban is one the model can talk itself out of.
68
68
  - **If a program will parse the output, use the structured-output flag** rather than asking for JSON in prose.
@@ -89,7 +89,9 @@ A worker is a subagent, or the same or another CLI called headlessly (`claude -p
89
89
  ### B3. Decision boundaries
90
90
  Ask before:
91
91
  - destructive or hard-to-reverse actions — hard-to-reverse means no backup/restore or fix-forward path exists; an action that has one (e.g. an additive migration with a backup, `stack.C8`) climbs `coding.B5`'s ladder instead of asking
92
+ - discarding or hiding tracked/uncommitted work: `git stash`, `checkout -- <path>`/`checkout .`, `restore .`, `reset --hard`, `clean -f[d]`, `push --force`, `branch -D` — run only on the user's explicit ask, never as a shortcut past a failing check or an obstacle (`coding.B3`'s stash-for-attribution ban is the narrow instance of this)
92
93
  - changing deployment, infrastructure, auth, billing, or shared config assumptions
94
+ - any test, benchmark, or trial run that spends paid API credits or session quota
93
95
  - modifying shared rule files, templates, or project-wide conventions
94
96
  - large rewrites or broad renames
95
97
  - actions visible to other people or external services
@@ -49,7 +49,7 @@ A principle with the procedure that guarantees it — apply to any edit of code
49
49
  ### B4. Self-documenting code — comments are a last resort
50
50
  Domain application of the density root (`agent.A4` — every line must carry information the reader does not already have); the naming root is `pattern.A7`. Penalty card: `[YAP]` (`agent` §0).
51
51
  - Naming and shape come first: a comment that explains *what* a block does is a failed name or a failed extraction — fix the name/structure (`pattern.A7`, `pattern.A3`), then delete the comment. Clean flow plus role-named functions and variables need no narration.
52
- - A comment may state only what the code cannot say: a non-obvious constraint, an external contract, a genuine why. Never narrate the next line, restate the signature, or record change history.
52
+ - A comment may state only what the code cannot say: a non-obvious constraint, an external contract, a genuine why. Never narrate the next line, restate the signature, or record change history. What a pinned project doc already states, the code need not say either (`C1`).
53
53
  - Deletion test, per comment: if removing it loses nothing a reader needs beyond what the code already says, remove it. Default is silence — comment density is a smell, not a virtue.
54
54
  - Comments rot: no compiler checks a comment, so it drifts silently as the code under it changes, and a stale comment misleads worse than none — one more reason deletion is the default, and why a rationale that must stay current lives in a doc the code references ([[RULE-docs]] B3), never duplicated inline.
55
55
  - One line when a comment is genuinely needed; a rationale bigger than that lives in docs, with the comment holding only the reference (see [[RULE-docs]] B3).
@@ -79,6 +79,7 @@ None of this weakens `B3`'s honesty floor: what genuinely stays unverified is st
79
79
  ### C1. Error handling
80
80
  - Validate at system boundaries: user input, external APIs, filesystem, network, persistence
81
81
  - Do not add defensive guards for impossible internal states — and size the ones that do guard a reachable state against who can actually reach it (`METHOD-proportionality.md`), instead of adding protection by reflex
82
+ - **Impossible and obvious are judged against the project's pinned facts** — `CLAUDE.md`, `docs/biz`, `docs/feat|arch`, recorded research decisions — never against imagination: a state those documents rule out gets no guard, a dependency they declare present gets no fallback, a fact they state gets no comment (`B4`), and a path they define gets one natural flow, not a patch beside it (`pattern.A8`)
82
83
  - Fail loudly in development when it helps reveal broken assumptions
83
84
  - Keep production failures safe and user-appropriate
84
85
  - **Never fabricate mock/fixture data as a runtime fallback for a missing dependency** (DB, API, service binding). Throw/return a real error instead. If a local dev environment genuinely lacks that dependency, fix the environment itself (real local instance, proper binding/proxy) — don't paper over it with fake data. Verify the dependency is actually unavailable by reading how the runtime/framework wires it in dev before assuming a fallback is needed at all.
@@ -28,7 +28,7 @@ These rules apply to all product content: interface text, meta titles/descriptio
28
28
  ### B2. Writing style — density is enforced, not preferred
29
29
  - Prefer clear, concrete wording
30
30
  - Deletion test per sentence (domain application of `agent.A4`): a sentence ships only if cutting it loses information the reader needs. Cut preamble, filler connectives, restatement, and reassurance — length follows content, never the reverse.
31
- - First sentence carries the point (the benefit, the instruction, or the answer); detail follows. This generalizes B3's FAQ rule to all content.
31
+ - First sentence carries the point (the benefit, the instruction, or the answer); detail follows. An FAQ answer is the sharpest case: answer in the first sentence, never a "Đây là...", "According to..." preamble.
32
32
  - Avoid filler and vague marketing language unless the project explicitly wants it
33
33
  - Keep headings short and literal
34
34
  - Punctuation: Strictly limit the use of em dash (—) and en dash (–)
@@ -38,7 +38,6 @@ These rules apply to all product content: interface text, meta titles/descriptio
38
38
  - Use stable labels for repeated concepts
39
39
  - Avoid unnecessary abbreviations in user-facing text
40
40
  - Make important entity definitions obvious near the start of a page or section
41
- - FAQ answers: answer directly in the first sentence — no "Đây là...", "According to..." preamble
42
41
 
43
42
  ## C. Separation
44
43
 
@@ -47,8 +46,9 @@ These rules apply to all product content: interface text, meta titles/descriptio
47
46
  - Do not let temporary task context leak into permanent copy
48
47
 
49
48
  ### C2. Content audit
50
- Read-only (`agent.B5`). Three sweeps, each anchored to the rule it checks:
49
+ Read-only (`agent.B5`). Four sweeps, each anchored to the rule it checks:
51
50
  1. **Canonical-term drift** (A3) — grep UI strings and i18n keys for synonyms of one concept; one concept with two live labels is a finding.
52
51
  2. **Density** (B2) — deletion test per shipped sentence; preamble, restatement, and reassurance in product copy are findings.
53
52
  3. **i18n coverage** (A2) — hardcoded user-facing strings that should be keys (excluding the EN=VI exception).
53
+ 4. **Fact-check** (`agent.B2`) — every claim about a product, feature, version or number must trace to the project's `ref/fact-*` (`docs.A6`) first, then to that product's own repo or live page; a claim that cannot be traced is a finding, not a style note.
54
54
  Classify severity per `docs.C4` (wrong / stale / incomplete / cosmetic); findings spanning domains route into the `docs.C2` research+plan pair.
@@ -1,6 +1,6 @@
1
1
  # Core Docs Rules
2
2
 
3
- <!-- Address map: docs.A1-4 · docs.B1-3 · docs.C1-4 -->
3
+ <!-- Address map: docs.A1-6 · docs.B1-3 · docs.C1-4 -->
4
4
 
5
5
  ## Goals
6
6
  Docs should be readable for both humans and LLMs.
@@ -19,7 +19,7 @@ Use these short, stable topic folders:
19
19
  - `docs/feat/` — features, systems, behaviors
20
20
  - `docs/arch/` — architecture, structure, technical design
21
21
  - `docs/plan/` — plans and execution notes
22
- - `docs/ref/` — stable references, setup notes, lookup docs
22
+ - `docs/ref/` — stable lookups: commands, paths and setup steps, verified by running them; `ref/fact-*.md` holds claims about the outside world, verified by evidence (A6)
23
23
  - `docs/research/` — exploratory, comparative, or time-bound findings
24
24
 
25
25
  Do not create new top-level doc topics unless the existing set clearly fails.
@@ -32,10 +32,11 @@ Filenames across all `docs/*` never lead with a date — the content-identifying
32
32
  - For any project with a business dimension, `docs/biz/` is REQUIRED and is the spine.
33
33
  - All `arch/`, `feat/`, and `plan/` docs that touch product direction or money must reference it.
34
34
  - When code intent and a `biz/` doc disagree, the `biz/` doc wins — reconcile or escalate.
35
+ - `biz/` holds decisions, not facts: it wins because the owner chose it, never because a source proved it. A market fact the decision rests on (a competitor's price, a platform limit) belongs in `ref/fact-*` with its evidence (A6), and the `biz/` doc cites it.
35
36
 
36
- ### A4. Anchor stamp — `updated <time> <version>` on every `arch|biz|feat` doc
37
+ ### A4. Anchor stamp — `updated <time> <version>` on every `arch|biz|feat` doc and every `ref/fact-*`
37
38
 
38
- `arch/`, `biz/` and `feat/` hold current state and are the SSoT other docs and code are written against, so a reader cannot tell a still-true doc from a silently rotted one without knowing when it was last confirmed. These three folders carry a stamp; `plan/`, `research/` and `ref/` do not — the first two are event records whose own schema already dates them (B1, B2), and `ref/` is verified by running its commands, not by a date.
39
+ `arch/`, `biz/` and `feat/` hold current state and are the SSoT other docs and code are written against, so a reader cannot tell a still-true doc from a silently rotted one without knowing when it was last confirmed. These three folders carry a stamp, and so does every `ref/fact-*` doc (A6): a fact is confirmed by reading a source on a date, and the outside world moves without touching this repo. `plan/`, `research/` and the rest of `ref/` do not — the first two are event records whose own schema already dates them (B1, B2), and a command lookup is verified by running it, not by a date.
39
40
 
40
41
  **Placement** — first line of the file's own header block: immediately under the H1 for a plain Markdown doc, or as a `updated:` key in the frontmatter/description field where the file already has one. One stamp per file, never per section.
41
42
 
@@ -51,6 +52,26 @@ Filenames across all `docs/*` never lead with a date — the content-identifying
51
52
 
52
53
  The stamp is what makes drift mechanically visible: a `docs/arch/` file stamped three releases back is a drift-audit lead (C3) before anyone reads a line of it.
53
54
 
55
+ ### A5. The auto-loaded instruction file — `CLAUDE.md` / `AGENTS.md` / `GEMINI.md`
56
+
57
+ The harness prepends this file to every request, so every line in it is paid on every turn and read under every task, related or not. It is the most expensive doc in the project and carries the highest bar: `agent.A4`'s deletion test with a reach condition on top. A line stays only if it passes all five, and the whole file is re-run through them on every edit:
58
+ 1. **Harm** — absent from every request, name the concrete mistake the agent makes. No nameable mistake, no line.
59
+ 2. **Reach** — it governs the majority of requests in this project. A line that matters to one domain belongs in the doc that domain's route loads (`feat/`, `arch/`, `biz/`, a project rule file), not here.
60
+ 3. **Not derivable** — it cannot be read from the code, the manifest (`package.json`, `Cargo.toml`), or a doc the router already loads for that task.
61
+ 4. **Not a restatement** — a shared-corpus rule is pointed at by address (`coding.B3`), never copied; a copy drifts and doubles the cost.
62
+ 5. **Facts and limits, not behavior** — the file binds the project's facts (stack, reference implementation, test and compile command, ship platform, hard limits) and stricter constraints; behavior rules live in the corpus (`index.md` § Precedence). Those bindings are the router's standing signal for every task in the project.
63
+
64
+ One file is the source: a per-project `GEMINI.md` or `AGENTS.md` is a bootstrap that points at `CLAUDE.md`, never a second copy.
65
+
66
+ ### A6. Fact docs — `docs/ref/fact-*.md`
67
+
68
+ Three claim classes, three authorities, never interchangeable: a **biz** claim is decided by the owner and wins by decision (A3); a **decision** is reached by research and holds by its recorded reasoning (B2); a **fact** is a claim about the outside world — a vendor's behavior, a limit, a price, what a standard specifies, what an artifact of a given version contains — and holds only by evidence. A fact nobody can trace is an unverified claim: it lives in `research/` marked as such, never in `ref/fact-*`.
69
+
70
+ - **Every fact carries its own trail, on the claim, not only on the file**: the source — an official page with the date it was read, or a named artifact pinned by version with the path inside it — and the research doc and section that verified it (`research/<doc>.md § R3`). A fact with a source but no research origin was asserted, not verified.
71
+ - **Current state only; history lives in research.** The fact doc is the distilled answer (B2 Action). Every change lands through a research event — an `## Amendments` entry when the finding stands, a successor doc when it changes — and the fact doc is updated in the same edit with its stamp rewritten (A4). A fact doc edited with no research event behind it is a **Wrong** finding (C3).
72
+ - **A fact earns its row when a second consumer needs to cite it** — a rule, another doc, an audit (`content.C2`'s fact-check reads here before the product's repo or live page). A one-off finding stays in the research doc that produced it.
73
+ - **The filename declares the class**: `fact-<subject>.md`. The rest of `ref/` stays commands and setup, verified by running.
74
+
54
75
  ## B. Lifecycle & Sync
55
76
 
56
77
  ### B1. Plan lifecycle & Filename Rules
@@ -79,7 +100,7 @@ Required fields, in order:
79
100
  - **Verification** — the evidence/method that hardens the result (data, test, cross-check against another case). If not verified, say so explicitly — silence reads as certainty when it isn't.
80
101
  - **Corroborating links** — links to the evidence/cases the result rests on or conflicts with (not just a verified/unverified flag)
81
102
  6. **Decision** — the resolution reached, one of:
82
- - **Action** — link to the artifact(s) where it materialized (`arch/`, `plan/`, `feat/`, `biz/`, `ref/`, or code/commit); 0 or many. Landing in `ref/` always means a **new** clean lookup doc, never the research doc itself relocated or rewritten into ref format — `ref/` is a distilled answer, research is the narrative trail behind it.
103
+ - **Action** — link to the artifact(s) where it materialized (`arch/`, `plan/`, `feat/`, `biz/`, `ref/`, or code/commit); 0 or many. Landing in `ref/` always means a **new** clean lookup doc, never the research doc itself relocated or rewritten into ref format — `ref/` is a distilled answer, research is the narrative trail behind it. A fact lands in `ref/fact-*` (A6) with this doc's section named on the claim, so the trail runs both ways.
83
104
  - **No action** — state why explicitly, so it reads as a deliberate stop, not an abandoned doc
84
105
  - **Follow-up research** — link to the new research doc opened by this result
85
106
  - **Rejected/closed** — an option eliminated with no replacement; no link needed
@@ -128,7 +149,7 @@ Walk the topology, checking each doc against what is actually true now:
128
149
  - `docs/feat/` — the described behavior still matches what the code does
129
150
  - `docs/biz/` — where code intent contradicts it, A3 decides: the `biz/` doc wins until it is explicitly changed
130
151
  - `docs/research/` — a conclusion whose recorded context no longer holds needs a successor doc plus a `Status: superseded by` line; a claim corrected while the Decision stands needs an `## Amendments` entry plus a `Status: amended` notice; a body rewritten in place with neither is a **Wrong** finding (B2)
131
- - `docs/ref/` — commands, paths, and setup steps still run
152
+ - `docs/ref/` — commands, paths, and setup steps still run; in `ref/fact-*`, every source still resolves and still says what the claim says, every pinned artifact version is still the one in use, and every claim's research origin exists — a claim changed with no research event behind it is **Wrong** (A6)
132
153
  - Doc references inside code comments (B3) still point at a heading that exists
133
154
  - **The inverse walk — code → docs:** a complex feature or subsystem shipped with no corresponding `feat/`/`arch/` doc is an **Incomplete** finding (C4). The audit checks both directions, never only whether existing docs still hold
134
155
 
@@ -37,7 +37,7 @@ These are constraints on **structure and reuse**, not style. Reach for this file
37
37
  **A7 — Name by role, never by concrete value.** Name things for what they *mean*, not what they *currently are*: `retryLimit` not `three`, `PrimaryAction` not `BlueButton`, `AuthBoundary` not `FirebaseWrapper`. Value-names rot the instant the value changes and force codebase-wide find-and-replace.
38
38
  - *Root rule for naming.* Every other naming item in this corpus (`agent.C1` file names, `ui.A` tokens, `stack.C1` component names, `release.A3` version/tag format, `content` semantic stability) is a **domain application** of A7, not a competing rule — do not restate A7 in them, and do not move them out of their domain. Address map: `index.md` § Cross-cutting lens.
39
39
 
40
- **A8 — One flow, made natural — not guarded.** When the same guard / check / fallback keeps reappearing around a path, the path's shape is wrong. Reshape the flow so the correct behavior is automatic; do not stack more enforcement on a weak path. Full method: `METHOD-audit-flow.md`.
40
+ **A8 — One flow, made natural — not guarded.** When the same guard / check / fallback keeps reappearing around a path, the path's shape is wrong. Reshape the flow so the correct behavior is automatic; do not stack more enforcement on a weak path. "Correct" is measured against the project's pinned facts (`coding.C1`), so a guard for a state those facts rule out is a patch, not a flow. Full method: `METHOD-audit-flow.md`.
41
41
 
42
42
  ---
43
43