Help us shape the future of Go! Take the 2026 Go Developer Survey

rules

package
v2.0.0 Latest Latest
Warning

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

Go to latest
Published: Oct 9, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package rules holds celeste's stream rules (2.0 W3, #175): a regex condition on the model's streamed text or a tool call's arguments, and a reminder that joins the conversation only when the rule fires. Rules come from the built-ins, ~/.celeste/rules/*.md and the grimoire's "## Stream Rules" section. A rule costs no context until it fires.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Destructive

func Destructive(command string) bool

Destructive reports a bash command the destructive-bash rule fires on (a force push, a recursive forced rm outside build output, or any rm the bash tool refuses), read as a shell would read it.

func FieldValues

func FieldValues(input map[string]any, field string) []string

FieldValues returns the values of a tool_args field. A dotted field walks into objects, and an array along the way fans out to each element, so "edits.new_string" is every patch_file edit's new_string (2.0 W4). A string met before the path ends is decoded as JSON.

func IsCheck

func IsCheck(tool string) bool

IsCheck reports a tool whose successful run counts as checking the work (bash: a build, a test run).

func IsEdit

func IsEdit(tool string) bool

IsEdit reports a tool that changes a file. An edit with no successful check after it leaves the work unverified (task-complete-before-verify, and the watchdog's unverified heuristic).

func Record

func Record(name string, acted bool)

Record counts a fire: acted, or only logged (stream_rules: shadow).

func ResetStats

func ResetStats()

ResetStats clears the counters (tests).

func Snapshot

func Snapshot() map[string]any

Snapshot is the celeste_status "rules" object (without "mode", which the server adds).

func StripUnbackedAudioClaim

func StripUnbackedAudioClaim(content string, ttsRan bool) string

StripUnbackedAudioClaim replaces a fabricated "Audio saved:" success string in a final reply when no TTS tool ran (ttsRan false). It is the unbacked-audio-claim rule's backstop: MCP chat applies it to every reply, whether stream rules are off, in shadow, or acting and out of retries. Moved here from llm/toolargs.go (2.0 W3).

func StripUnbackedSpawnClaim

func StripUnbackedSpawnClaim(content string, spawnRan bool) string

StripUnbackedSpawnClaim removes a fabricated "subagent spawned (id: …)" success string the model may emit when it can't actually drive the spawn_agent tool (a non-reasoning/weak model flails then claims success — observed with a fake id "task-47"). If no spawn_agent tool ran this run (spawnRan false) and the content claims a spawned subagent with an id, the claim is replaced with an honest error. Returned unchanged when a spawn ran or no claim is present. Moved here from llm/toolargs.go, next to its audio twin.

func VoiceLeak

func VoiceLeak(path, content string) bool

VoiceLeak reports persona voice in file content as the persona-voice-in-files rule judges it (fenced code, quotation lines, docs and persona paths exempt). The watchdog's heuristic uses it.

Types

type Action

type Action string

Action is what a fired rule does.

const (
	// Interrupt cuts the request short and re-runs the turn with the rule's
	// reminder; for tool_args, the turn's calls never run.
	Interrupt Action = "interrupt"
	// Append adds the reminder before the next request, after the turn's
	// tool results. After a final reply it waits for the next run.
	Append Action = "append"
	// Queue holds the reminder for the next run (the next user turn).
	Queue Action = "queue"
)

type Call

type Call struct {
	ID    string
	Name  string
	Input map[string]any
}

Call is one tool call as the matcher sees it.

type Facts

type Facts struct {
	TTSRan bool // generate_speech returned without an error this session

	// RuntimeVerifies is set for an agent run with verification commands:
	// the runtime checks the work after TASK_COMPLETE, so the
	// task-complete-before-verify rule stands down.
	RuntimeVerifies bool
	// contains filtered or unexported fields
}

Facts are what the built-in guards know about the session, kept by the Matcher from tool results.

func (*Facts) Unverified

func (f *Facts) Unverified() bool

Unverified reports a file changed after the last command that ran.

type Hit

type Hit struct {
	Rule  *Rule
	Scope Scope
	Text  string // what matched
	Call  *Call  // tool_args scope: the call
	Value string // tool_args scope: the whole argument value matched
}

Hit is one rule firing.

type Matcher

type Matcher struct {
	// contains filtered or unexported fields
}

Matcher runs a Set over one session. Not safe for concurrent use: the caller (steer.Session) serializes it.

func NewMatcher

func NewMatcher(set *Set) *Matcher

NewMatcher returns a matcher over set (nil: no rules).

func (*Matcher) Calls

func (m *Matcher) Calls(calls []Call) []Hit

Calls checks a turn's complete tool calls and returns the tool_args rules they fire.

func (*Matcher) Facts

func (m *Matcher) Facts() *Facts

Facts returns the session facts, for RuntimeVerifies and the backstops.

func (*Matcher) Flush

func (m *Matcher) Flush() []Hit

Flush scans text the batching held back. Call it once the stream ends.

func (*Matcher) StartRequest

func (m *Matcher) StartRequest()

StartRequest begins a model request: the streamed text starts empty and each rule fires at most once per request.

func (*Matcher) Text

func (m *Matcher) Text(delta string) []Hit

Text feeds one streamed text delta and returns the text rules it fires. Scans are batched (scanEvery, or a newline); call Flush when the stream ends so the last batch is scanned too.

func (*Matcher) ToolResult

func (m *Matcher) ToolResult(name string, isError bool)

ToolResult records a call's outcome for the facts.

type Rule

type Rule struct {
	Name      string
	Condition *regexp.Regexp
	Scopes    []Scope
	Action    Action
	Once      bool // repeat: once (per session)
	Gap       int  // repeat: after-gap:N, in requests
	Message   string
	Source    string // file path, grimoire path, or "builtin"
	Disabled  bool   // enabled: false (removes a rule of the same name)
	// contains filtered or unexported fields
}

Rule is one parsed rule.

func Builtins

func Builtins() []*Rule

Builtins returns the shipped rules (spec §5 W3), parsed fresh each call. A user or grimoire rule of the same name replaces one; enabled: false turns it off.

func Parse

func Parse(name, source string, data []byte) (*Rule, error)

Parse reads one rule: YAML-style "key: value" frontmatter between "---" lines, then the reminder text. name is the default name (the file's base name). Keys: name, condition (RE2 regex, required unless enabled: false), scope (comma-separated; default text), action (default append), repeat (once | after-gap:N; default once), enabled (default true).

type Scope

type Scope struct {
	Kind  ScopeKind
	Tool  string
	Field string
}

Scope is one place a condition is matched: text, thinking, or tool_args:<tool>.<field>.

func (Scope) String

func (s Scope) String() string

type ScopeKind

type ScopeKind string

ScopeKind is what a rule's condition is matched against.

const (
	ScopeText     ScopeKind = "text"      // the model's streamed reply
	ScopeThinking ScopeKind = "thinking"  // not wired to the reasoning stream yet: parsed, never fires
	ScopeToolArgs ScopeKind = "tool_args" // one argument of one tool's calls
)

type Section

type Section struct {
	Source string // the grimoire file
	Body   string // "### <name>" subsections, each a rule in the file format
}

Section is one grimoire file's "## Stream Rules" body.

func Trusted

func Trusted(home string, secs []Section, approve hooks.ApproveFunc, warn func(string)) []Section

Trusted returns the grimoire sections whose stream rules may load (2.0 W3, ruling 18). A repo grimoire's "## Stream Rules" can stop replies, force paid re-runs and inject reminders, so it is trusted like a repo hook: by content hash in the hooks trust store (~/.celeste/trusted.json, `celeste hooks trust`). The global grimoire (~/.celeste/grimoire.md) is the user's own file and needs no trust; neither do the built-ins and ~/.celeste/rules, which never pass through here.

approve is the interactive chat's prompt (the same one repo hooks use); a yes is recorded in the store. Non-interactive runs pass nil: only sections already trusted load, and the rest are skipped with one warning.

type Set

type Set struct{ Rules []*Rule }

Set is the rules in effect, sorted by name.

func Load

func Load(home string, grimoire []Section, warn func(string)) *Set

Load returns the built-ins, overridden by ~/.celeste/rules/*.md, then by the grimoire's "## Stream Rules" sections in order. A later rule replaces an earlier one of the same name, and enabled: false removes it. A file that does not parse is skipped with a warning; so is a rule whose only scope is thinking (thinking-scope rules are not wired to the model's reasoning yet).

func (*Set) Len

func (s *Set) Len() int

Len is the number of rules (0 for a nil Set).

Jump to

Keyboard shortcuts

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