# Rule Graduation Ledger

CLAUDE.md prose rules are graduated into machine checks (lint rules, ratchets,
boundary tests, fitness tests) so the doc converges back to architecture
description. Once a rule graduates, it is **deleted from CLAUDE.md** — the
check is the new source of truth, and this ledger preserves the mapping so the
history is not lost.

Graduation criteria (per #1528): a prose rule graduates only when a machine
check makes the mistake impossible-or-loud. Rules that encode judgment
("don't destroy old state before new state is confirmed") stay as prose.

| Rule # | Original rule text | Check location | Graduated in |
|--------|--------------------|----------------|--------------|
| 31 | **Core package files must never have host-specific prefixes** — generic modules in `@remnic/core` must use generic names (e.g. `recall-audit.ts`, not `openclaw-recall-audit.ts`); host adapters wrap core, not the other way around. | `scripts/check-review-patterns.sh` check #15 — `find packages/remnic-core/src -maxdepth 1 -type f \( -name "openclaw-*" -o -name "hermes-*" -o -name "codex-*" \)` is **BLOCKING** (`fail`, exits 1). Scoped to top-level generic modules; legitimate host adapters in `memory-extension/`, `connectors/`, `adapters/` are excluded. | #1638 |
| 26 | **Import via package name, not relative cross-package paths** — `import { X } from "@remnic/core"` not `import { X } from "../../../remnic-core/src/foo.js"`. Directory renames silently break relative imports with no package-dependency signal. | `scripts/check-review-patterns.sh` check #36 — `grep -rnE 'from .*\.\..*remnic-(core|cli)/src'` is **BLOCKING** (`fail`, exits 1). Zero current violations. Scoped to `.ts` files in `packages/`, excluding tests and node_modules. | #TBD |
| 27 | **Guard `slice(-n)` against `n === 0`** — `entries.slice(-0)` equals `slice(0)` and returns ALL entries. Always check `if (n <= 0)` before negating for slice. The `-0 === 0` footgun is a JavaScript-specific trap. | `scripts/check-review-patterns.sh` check #35 — `grep -rnE '\.slice\(-[a-zA-Z_]'` is **WARN-level** (19 existing call sites use computed offsets, many guarded by `Math.max(1,...)` above). New code should add a guard or justify safety. Scoped to `.ts` files in `packages/remnic-core/src/` and `packages/remnic-cli/src/`, excluding tests and comments. | #TBD |
| 36 | **String `"false"` is truthy in JavaScript** — `--config installExtension=false` produces `"false"` (string), which `!== false` evaluates as `true`. Coerce boolean-like strings (`"false"`, `"0"`, `"no"`, `"off"`) at config-read boundaries using a shared helper. | `scripts/check-review-patterns.sh` check #19 — `grep -rn '!== false\|!= false'` on config/option/flag values is **WARN-level** (147 existing sites; most are typed booleans already coerced by the config layer via `coerceBool()`/`coerceBooleanLike()`). New code should use the shared coercion helpers. Scoped to `.ts` files in `packages/`, excluding tests and comments. | #TBD |
| 46 | **Escape literal template parts before building regex** — when constructing regex from user-provided templates, always `escapeRegex()` on the prefix/suffix. Empty prefix+suffix produces a match-everything regex. Special `$` in replacement strings corrupts output — use a replacement function or escape `$` → `$$`. | `scripts/check-review-patterns.sh` check #26 — `grep -rn 'new RegExp(' \| grep -v 'escapeRegex\|escape(' \| grep -i 'template\|prefix\|suffix\|pattern\|format\|config'` is **WARN-level** (13 existing sites flagged). New code should escape user-derived parts with `escapeRegex()`. Scoped to `.ts` files in `packages/ src/`, excluding tests. | #TBD |
| 53 | **Status filters must enumerate ALL non-active states** — filtering only `superseded` and `archived` but not `quarantined`, `rejected`, or `pending_review` causes stale data in user-facing outputs. Define an explicit `ACTIVE_STATUSES` set rather than an ad-hoc exclusion list. When adding a new status, grep ALL filters. | `scripts/check-review-patterns.sh` check #37 — `grep -rnE '(status\|frontmatter\.status)\s*(===\|!==)\s*"(superseded\|archived\|...)"' \| awk '$1 >= 2'` (files with 2+ such comparisons) is **WARN-level**. The shared `ACTIVE_STATUSES` set is exported from `contradiction-scan.ts`. New code should use the set instead of ad-hoc lists. Scoped to `.ts` files in `packages/remnic-core/src/` and `packages/remnic-cli/src/`, excluding tests. | #TBD |
