Skip to content

feat: itd-3 modular rules loader (loader core, hook, verb, OPINIONS) - #18

Merged
REPPL merged 9 commits into
mainfrom
auto/itd-3-rules-loader-design
Jul 11, 2026
Merged

feat: itd-3 modular rules loader (loader core, hook, verb, OPINIONS)#18
REPPL merged 9 commits into
mainfrom
auto/itd-3-rules-loader-design

Conversation

@REPPL

@REPPL REPPL commented Jul 11, 2026

Copy link
Copy Markdown
Owner

Implements itd-3, the modular rules loader, per the SOTA-verified design of record (.abcd/development/plans/2026-07-11-itd-3-rules-loader.md). Maintainer signed off the four intent deltas (D1 event-driven refresh, D2 shipped schema shape, D3 zero no-match tokens, D4 no .abcdignore).

Shape

Rule-loading is a transport-agnostic core (internal/core/rules) with two front doors — the vendor-neutral abcd rules [domain] verb and a Claude Code hook (abcd hook prompt-router / -reset) — not an adapter seam. Default domains are bundled in the binary; a per-repo .abcd/rules.json overrides them per-field.

What's here

  • Core: embedded default domains, per-field merge, word-bounded recall with a conservative suffix stemmer, *<DOMAIN> star-commands, per-domain FNV dedup, fail-closed Load.
  • Hook: UserPromptSubmit injection with per-session signature dedup; event-driven refresh (ledger cleared on SessionStart/PreCompact), fixed-N backstop from rules.force_refresh_every_n (default 15). Fail-closed and non-blocking; zero model-facing tokens on a no-match, diagnostics out-of-band.
  • Verb: bare renders the active set; positional DOMAIN scopes; --json parity.
  • ahoy wiring: hooks/hooks.json (invokes the plugin's own binary — Go-only), verifier reconcile, present-tense marker block.
  • OPINIONS domain: rules point at .abcd/development/principles/, never copies.

Reviews

  • security-reviewer: PASS-WITH-NOTES, no blocker (prompt-injection, path traversal, resource caps, fail-closed all handled).
  • ruthless-reviewer: SHIP, no FIX-FIRST.

Verification

make preflight (build + vet + test + race) and make record-lint green. New behaviour is TDD (test watched fail then pass).

The hook only fires in an installed plugin where $CLAUDE_PLUGIN_ROOT/abcd exists (release packaging); in-repo it is exercised via the CLI and tests.

Assisted-by: Claude:claude-opus-4-8

REPPL added 9 commits July 11, 2026 14:46
…sign-off)

Records the design of record for the itd-3 modular rules loader, reached via
the prefer-sota process: an adversary challenged generic just-in-time
rule-injection SOTA (CARL, AGENTS.md, Cursor rules, Skills) for repo fit, then
a sota-researcher verdict was taken on the fit-surviving hypothesis.

Surviving shape: a transport-agnostic core capability (internal/core/rules)
with two front doors — the vendor-neutral `abcd rules [domain]` verb and a
Claude Code hook (`abcd hook prompt-router`) — not an adapter seam. The verdict
confirms this is current SOTA (JIT progressive disclosure) and refines CARL in
four ways: event-driven refresh on SessionStart(compact) over fixed-N; per-
domain rendered-content dedup with a ledger cleared on reset; keyword+alias
recall (zero-dep, no ML); zero model-facing tokens on no-match with out-of-band
observability.

No build begins until the maintainer signs off the four intent deltas (D1 N-
refresh, D2 schema shape, D3 no-match budget, D4 .abcdignore) enumerated in the
STOP section.

Assisted-by: Claude:claude-opus-4-8
The transport-agnostic core of the modular rules loader: binary-embedded default
domains (COMMITTING, DOCUMENTATION, ROADMAP, ISSUES, INTENTS, LIFEBOAT, PII),
per-repo .abcd/rules.json merge (per-field domain override, sticky kill switch),
hand-rolled zero-dep validation, word-bounded recall matching with multi-word
aliases, RE2-safe *<DOMAIN> star-command parsing, one shared renderer, and
per-domain FNV-1a dedup signatures.

Fail-closed: a malformed or invalid rules.json is an error the caller surfaces,
never a silent fall-back. No model-facing tokens on a no-match (D3): Render(nil)
is zero bytes.

Not yet wired to a front door — the `abcd rules` verb (phase 3) and the
prompt-router hook (phase 2) land next; this core does nothing on the live path
until they do.

Tests watched fail then pass; make preflight green (build/vet/test/race).

Assisted-by: Claude:claude-opus-4-8
Wires the rules loader core to its vendor-neutral front door. Bare `abcd rules`
renders the active rule set; a positional DOMAIN (case-insensitive) scopes to
one domain; an unknown domain exits 2; --json emits the structured envelope. A
malformed .abcd/rules.json fails closed. Adds core Active()/Lookup() helpers.

The capability is now reachable and demonstrable from the production CLI entry
point without any harness. The prompt-router hook (phase 2) injects the same
core just-in-time next.

Tests watched fail then pass; make preflight + record-lint green.

Assisted-by: Claude:claude-opus-4-8
The Claude Code injection front door onto internal/core/rules. Two operator-
internal entrypoints — `abcd hook prompt-router` (UserPromptSubmit) and
`abcd hook prompt-router-reset` (SessionStart/PreCompact) — read the hook stdin
payload, recall-match, dedup per session, and inject only fresh matched rules.

Core additions (pure, transport-free): Inject() with per-domain signature dedup
and a fixed-N refresh backstop (default 15; event-driven reset is primary per
D1), and session-state persistence (LoadState/SaveState/ResetState) keyed on a
sha256 of the session id so a hostile id cannot traverse the state dir.

Trust boundary hardening (security-reviewer: PASS-WITH-NOTES, no blocker): Load
now refuses a symlinked/non-regular/oversized rules.json; stdin (1 MiB), prompt
(64 KiB), and state file (256 KiB) are capped; the injected text is abcd's own
rule content only and never reflects prompt bytes; the reset diagnostic quotes
the untrusted event name. Every path is fail-closed and non-blocking (exit 0 +
out-of-band stderr diagnostic); a no-match renders zero model-facing tokens (D3).

Tests watched fail then pass; make preflight + record-lint green.

Assisted-by: Claude:claude-opus-4-8
…cile)

Makes the loader a live install, not built scaffolding.

- Add hooks/hooks.json: UserPromptSubmit -> `abcd hook prompt-router`,
  SessionStart/PreCompact -> `abcd hook prompt-router-reset`, invoking the
  plugin's own binary ($CLAUDE_PLUGIN_ROOT/abcd) — Go-only, no shell shim.
- Reconcile store.go requiredHookCommand to the Go-subcommand substrings
  (`hook prompt-router` / `hook prompt-router-reset`), superseding the stale
  prompt_router_hook/_reset script names; update the two manifest test fixtures.
- Add TestShippedHookManifestVerifies so the shipped manifest and the verifier
  cannot drift.
- Rewrite the CLAUDE.md marker block present-tense-accurate: real domains (not
  placeholders), the override-skeleton shape, star-command + kill-switch +
  dormant semantics, event-driven reset; drop the retired scripts/abcd schema
  path and the fn- reference.
- Clarify stepRules writes the empty-domains override skeleton (defaults live
  once in the binary; per-repo overrides per-field — one-canonical-primitive),
  not a copy of the bundled defaults.

Behaviour-preserving manifest reconciliation kept in this wiring commit; make
preflight + record-lint green.

Assisted-by: Claude:claude-opus-4-8
Adds the OPINIONS/CONVENTIONS default domain to the binary-bundled defaults, so
every abcd-managed repo inherits it via ahoy. Its rules are short directives that
POINT AT the canonical conventions under .abcd/development/principles/
(prefer-sota, one-canonical-primitive, evaluator-outside-the-loop,
verifier-selects-gates-decide, loud-staging, script-first-mvp) plus an index
rule naming the directory — never a copy of the principle text
(one-canonical-primitive). Recall triggers on opinion/convention/principle/
approach/SOTA prompts.

No path-resolution surface: the rule text names the file for the agent to read
on demand; the hook never resolves or reads it, so there is no traversal vector.

Marker block domain list + CHANGELOG updated. Tests watched fail then pass; make
preflight + record-lint green.

Assisted-by: Claude:claude-opus-4-8
Ruthless-reviewer verdict was SHIP (no FIX-FIRST); this closes the two
integrity findings and the cheap nits.

- Wire the fixed-N backstop to config: rules.LoadBackstop reads
  rules.force_refresh_every_n from .abcd/config.json (default 15 when unset),
  and the hook passes it instead of a hardcoded 0 — the documented config field
  now does something. Corrects the comment that claimed it was already wired,
  and updates the config brief to the D1 backstop semantics (default 15,
  event-driven reset is primary).
- Rewrite the vacuous reset test into TestInjectReinjectsOnClearedLedger, which
  threads session state and asserts dedup-then-reinject; add TestLoadBackstop.
- SaveState uses os.CreateTemp for a unique temp name (no orphaned fixed .tmp,
  no same-session writer race; cleans up on rename failure).
- LoadState hoists the double session-id hash; comments clarify the id-less
  session collapse and that scoped `abcd rules DOMAIN` inspects content
  independent of the kill switch.

make preflight + record-lint green.

Assisted-by: Claude:claude-opus-4-8
End-of-run record of the design forks, non-obvious decisions, and process
learnings from the itd-3 build, so a future session need not re-derive them.

Assisted-by: Claude:claude-opus-4-8
Implements the three follow-ups rather than deferring them, per maintainer
direction.

- Recall stemming: inflected forms recall their keyword (commits->commit,
  pushes->push, issues->issue) via a conservative suffix stemmer. Short tokens
  are left unstemmed and `-es` only strips after a sibilant root, so the
  test->attestation over-match the SOTA verdict warned about is closed by a
  regression test rather than accepted.
- PruneState sweeps session-state files older than StateTTL (one week) on
  SessionStart, bounding temp-dir growth.
- Load refuses a symlinked .abcd directory component, not just the rules.json
  leaf (the residual the security review flagged as out-of-remit).

Learnings note updated to present-tense; tests watched fail then pass; make
preflight + record-lint green.

Assisted-by: Claude:claude-opus-4-8
@REPPL
REPPL merged commit 0525957 into main Jul 11, 2026
12 checks passed
@REPPL
REPPL deleted the auto/itd-3-rules-loader-design branch July 11, 2026 15:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant