rules

package
v0.6.8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 29, 2026 License: MIT Imports: 18 Imported by: 0

Documentation

Overview

Package rules is abcd's transport-agnostic modular rules loader (itd-3). It owns the whole capability behind two front doors: the `abcd rules [domain]` CLI verb and the Claude Code prompt-router hook. Nothing here writes to stdout or knows about a harness event — the front doors under internal/surface and the hook entrypoint marshal these results for their transport.

The model is a small set of binary-bundled default domains (embedded below) merged with an optional per-repo <repoRoot>/.abcd/rules.json override. Each domain carries recall keywords + aliases and a list of rules; a prompt is recall-matched against the active domains and only the matching rules are rendered for injection. A leading *<DOMAIN> star-command activates a domain unconditionally (overriding a dormant state, but never the top-level kill switch).

Validation is hand-rolled Go (zero new dependencies): a rules.json that fails to parse or validate is a fail-closed error the caller surfaces loudly — it never silently degrades to zero injection.

Index

Constants

View Source
const (
	StateActive  = "active"
	StateDormant = "dormant"
)

Domain state values. An empty string is treated as active.

View Source
const DefaultRefreshBackstop = 15

DefaultRefreshBackstop is the fixed-N full-refresh backstop (D1): the primary refresh is event-driven (a SessionStart/PreCompact reset clears the ledger), and this large counter only catches always-relevant domains that never recall-match. It is deliberately larger than CARL's every-5.

View Source
const RepoRelPath = ".abcd/rules.json"

RepoRelPath is the per-repo override file, relative to the repo worktree.

View Source
const StateTTL = 7 * 24 * time.Hour

StateTTL bounds how long a per-session ledger lives before the reset hook sweeps it (sessions are one-shot; a week is generous headroom).

Variables

This section is empty.

Functions

func LoadBackstop

func LoadBackstop(repoRoot string) int

LoadBackstop reads rules.force_refresh_every_n from <repoRoot>/.abcd/config.json — the fixed-N full-refresh backstop (D1). Event-driven reset is the primary refresh, so this only bounds always-relevant domains; a missing file/key or a non-positive value falls back to DefaultRefreshBackstop.

func PruneState

func PruneState(maxAge time.Duration)

PruneState removes session-state files older than maxAge, bounding the growth of the temp state dir across many sessions. Best-effort housekeeping: a missing dir or a per-entry error is ignored.

func Render

func Render(domains []ResolvedDomain) string

Render is the single renderer both front doors use. It emits a header plus one section per domain. An empty domain list renders to zero bytes (D3: no model-facing tokens on a no-match).

func ResetState

func ResetState(session string) error

ResetState clears a session's ledger (the event-driven refresh: the next prompt re-injects every matching domain). A missing file is not an error.

func SaveState

func SaveState(session string, st SessionState) error

SaveState durably persists the ledger for a session through the canonical fsutil.WriteFileAtomic (temp-write, fsync, rename, parent-dir fsync), so the write is crash-safe and a reader sees either the old file or the complete new one. The state dir is pre-created 0700 (WriteFileAtomic's own MkdirAll is a no-op once it exists); the session file is 0600.

func Signature

func Signature(d ResolvedDomain) string

Signature is the per-domain dedup key: an FNV-1a hash of the rendered block, so identical rendered content (defaults or override) dedups and any content drift invalidates. FNV is sufficient — this is dedup, not security.

func Validate

func Validate(rs RuleSet) error

Validate checks structural invariants: schema_version == 1, every domain name matches [A-Z][A-Z0-9_]*, every state is active/dormant (or empty), and no rule body is empty or whitespace-only. An empty rule body otherwise passes here and renders as a bare contentless "- " bullet in the injected block (iss-2608261550497978) — a loud refusal at load beats a silent empty bullet.

Types

type Domain

type Domain struct {
	State   string   `json:"state,omitempty"`
	Recall  []string `json:"recall,omitempty"`
	Aliases []string `json:"aliases,omitempty"`
	Rules   []string `json:"rules,omitempty"`
}

Domain is one keyed rule domain. The map key in RuleSet.Domains is its name; the name is therefore not a serialized field on the value.

type InjectResult

type InjectResult struct {
	Text     string
	Injected []string
	State    SessionState
}

InjectResult is the outcome of one prompt-router evaluation. Text is empty when nothing new is injected (a healthy no-match renders zero model-facing tokens, per D3).

func Inject

func Inject(rs RuleSet, prompt string, prev SessionState, backstop int) InjectResult

Inject is the pure heart of the prompt-router hook: it recall-matches prompt, drops any matched domain already injected this session with an unchanged signature, renders the remainder, and returns the updated session state. It performs no I/O and never reflects prompt bytes into the output — the rendered text is abcd's own rule content only.

backstop is the fixed-N full-refresh interval; <= 0 uses DefaultRefreshBackstop. When the (incremented) prompt count is a multiple of the backstop, the ledger is cleared first so always-relevant domains re-inject.

type ResolvedDomain

type ResolvedDomain struct {
	Name string `json:"name"`
	Domain
}

ResolvedDomain pairs a domain with its name for ordered rendering and dedup.

type RuleSet

type RuleSet struct {
	SchemaVersion int               `json:"schema_version"`
	Disabled      bool              `json:"disabled"`
	Domains       map[string]Domain `json:"domains"`
}

RuleSet is the merged, validated rule model: the bundled defaults overlaid with the per-repo override.

func Defaults

func Defaults() RuleSet

Defaults returns a deep copy of the binary-bundled default rule set, safe for the caller to mutate.

func Load

func Load(repoRoot string) (RuleSet, error)

Load returns the defaults merged with <repoRoot>/.abcd/rules.json when that file exists. An absent file yields the defaults unchanged; a present file that cannot be parsed or fails validation is a fail-closed error.

func Merge

func Merge(base, over RuleSet) RuleSet

Merge overlays over onto base. Domain fields are per-field: a field set on the override wins; an absent field inherits the base (so {"state":"dormant"} on a default domain silences it while keeping its recall and rules). New domain keys are added. The kill switch is sticky (either side can enable it).

func (RuleSet) Active

func (rs RuleSet) Active() []ResolvedDomain

Active returns every injectable domain (state != dormant) in name-sorted order — the full set the diagnostic `abcd rules` render shows. The top-level kill switch yields nothing.

func (RuleSet) Lookup

func (rs RuleSet) Lookup(name string) (ResolvedDomain, bool)

Lookup returns one domain by name regardless of its state (a dormant domain is still inspectable); ok is false when the name is absent.

func (RuleSet) Match

func (rs RuleSet) Match(prompt string) []ResolvedDomain

Match returns the domains to inject for prompt, in deterministic (name-sorted) order. The top-level kill switch suppresses everything. Otherwise a domain is injected if a star-command names it (overriding dormant) or — when active — its recall keywords or aliases hit the prompt.

type SessionState

type SessionState struct {
	Count  int               `json:"count"`
	Ledger map[string]string `json:"ledger"` // domain name -> last-injected signature
}

SessionState is the per-session dedup ledger plus the prompt counter that drives the fixed-N refresh backstop.

func LoadState

func LoadState(session string) SessionState

LoadState reads the ledger for a session. A missing, oversized, or malformed file yields the zero state (a fresh session), never an error — dedup is a best-effort optimisation, not a correctness gate.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL