rules

package
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Oct 8, 2026 License: AGPL-3.0 Imports: 14 Imported by: 0

Documentation

Overview

Package rules holds the rule types, the condition matcher, validation, the evaluation order for one email and YAML import/export. It never calls a model and never touches a mailbox.

Index

Constants

View Source
const (
	OpEq          = "eq"
	OpNe          = "ne"
	OpIn          = "in"
	OpContains    = "contains"
	OpContainsAny = "contains_any"
	OpNotContains = "not_contains"
	OpMatches     = "matches"
	OpGt          = "gt"
	OpLt          = "lt"
	OpExists      = "exists"
)

Condition operators.

View Source
const (
	MatchAddress = "address"
	MatchDomain  = "domain"

	VerdictRoute = "route"
	VerdictKeep  = "keep"
	VerdictBlock = "block"
)

Sender rule match types and verdicts.

View Source
const (
	ActMove    = "move"
	ActArchive = "archive"
	ActTrash   = "trash"
	ActJunk    = "junk"
	ActFlag    = "flag"
	ActUnflag  = "unflag"
	ActRead    = "read"
	ActUnread  = "unread"
	ActKeep    = "keep"
)

Action types, in the order the plan lists them.

View Source
const MinTrashConfidence = 0.85

MinTrashConfidence is the lowest threshold an intent-based trash rule may have.

Variables

This section is empty.

Functions

func ActionTypes

func ActionTypes() []string

ActionTypes lists the action types a rule may name.

func FolderProblem

func FolderProblem(name string) string

FolderProblem says, in a sentence, what is wrong with a folder name a rule would move mail to, or "" when it is fine. It is the check a move action's folder gets.

func MarshalYAML

func MarshalYAML(f File) ([]byte, error)

MarshalYAML writes a rules file in priority order, using the shorthands.

Types

type Action

type Action struct {
	Type   string `json:"type"`
	Folder string `json:"folder,omitempty"`
}

Action is one step a rule applies to an email. Folder is set for move only.

func (Action) String

func (a Action) String() string

String is the YAML shorthand: "move:Food", "trash".

type Cond

type Cond struct {
	All   []Cond `json:"all,omitempty"`
	Any   []Cond `json:"any,omitempty"`
	Field string `json:"field,omitempty"`
	Op    string `json:"op,omitempty"`
	Value any    `json:"value,omitempty"`
	// contains filtered or unexported fields
}

Cond is one node of a condition tree: a group ("all" or "any" of its children) or a leaf (field, op, value). The zero value is the empty tree, which always matches.

func (Cond) IsEmpty

func (c Cond) IsEmpty() bool

IsEmpty reports whether the tree has no conditions at all.

func (Cond) Match

func (c Cond) Match(e *message.Summary, now time.Time) bool

Match reports whether the email satisfies the tree. now is only used by age_days. A leaf that would not pass validation never matches.

func (Cond) Text

func (c Cond) Text() string

Text renders the tree as one line of plain English, for showing a rule's "unless" part to a model: `from_domain is one of a.com, b.com and (subject contains refund or is_bulk is true)`. The empty tree renders as "".

func (*Cond) UnmarshalJSON

func (c *Cond) UnmarshalJSON(b []byte) error

UnmarshalJSON decodes a node strictly (a misspelt key must not turn a leaf into an empty, always-true tree) and compiles its regex once.

type Defaults

type Defaults struct {
	DecisionModel string   `yaml:"decision_model,omitempty"`
	FallbackModel string   `yaml:"fallback_model,omitempty"`
	MinConfidence *float64 `yaml:"min_confidence,omitempty"`
}

Defaults is the optional "defaults" block. The models are daemon settings, so the importer decides what to do with them; MinConfidence is copied onto every rule that sets none.

type Evaluation

type Evaluation struct {
	Final      *Result
	Candidates []Rule // passing intent rules above the cut-off, in priority order
	CutOff     *Rule  // the default when the decider picks none; may be nil
	// contains filtered or unexported fields
}

Evaluation is what Evaluate found. When Final is set the email is settled without a model. Otherwise the caller asks the decider to pick among Candidates and passes its answer to Resolve.

func Evaluate

func Evaluate(e message.Summary, rs []Rule, senders []SenderRule, opt Options) Evaluation

Evaluate runs the evaluation order for one email: sender rules, then the priority walk down to the cut-off rule. It makes no model call.

func (Evaluation) Resolve

func (ev Evaluation) Resolve(ruleID int64, confidence float64) Result

Resolve turns the decider's answer into the final result (step 5). A pick outside the candidates counts as "none of these", for which the cut-off rule applies; when none does, the result keeps the decider's confidence that no rule applies. A pick below its threshold goes to Needs review with no action at all, even when there is a cut-off rule.

type Field

type Field struct {
	Name string
	Type string   // string | boolean | number | pass, fail or none | account id
	Ops  []string // the operators it accepts
}

Field describes one condition field for whoever writes conditions: the rule composer's prompt is built from this list.

func Fields

func Fields() []Field

Fields lists every condition field with its type and operators, header:<Name> last.

type File

type File struct {
	Defaults Defaults
	Rules    []Rule
}

File is a rules YAML document in the PRD shape. The database stays the source of truth; this is import/export only.

func ParseYAML

func ParseYAML(data []byte) (File, error)

ParseYAML reads a rules file and validates every rule. Priority follows the order in the file. All problems are returned together (errors.Join), each a *RuleError.

type Options

type Options struct {
	MinConfidence float64   // threshold for rules that set none
	Now           time.Time // for age_days
}

Options are the per-run settings Evaluate needs.

type Result

type Result struct {
	Stage       Stage
	RuleID      int64 // 0 = no rule; with Review, the rule the decider suggested
	RuleVersion int
	Confidence  float64
	Actions     []Action // the primary rule's actions, then each stacking rule's
	Stacked     []int64  // stacking rules whose actions were appended
	Review      bool     // below threshold: take no action, show in Needs review
	// SenderRuleID is the sender rule that settled the email (Stage is StageSender); 0 otherwise.
	SenderRuleID int64
}

Result is the final outcome for one email.

type Rule

type Rule struct {
	ID            int64
	UserID        int64
	AccountID     int64 // 0 = all accounts
	Name          string
	Said          string // the user's original wording; "" when they gave none
	Template      string // the name of the gallery template it was added from; "" = none
	Intent        string // plain-English intent for the decider; "" = condition-only
	Conditions    Cond
	Exceptions    Cond // "unless"
	Actions       []Action
	Priority      int // lower runs first
	Stack         bool
	Model         string   // per-rule decider override
	MinConfidence *float64 // nil = the configured default
	Enabled       bool
	Version       int
	CreatedAt     int64
	UpdatedAt     int64
}

Rule mirrors one row of the rules table.

func (Rule) Validate

func (r Rule) Validate() error

Validate checks a rule before it is saved or imported and returns the first problem as a *ValidationError.

type RuleError

type RuleError struct {
	N   int    // the rule's place in the file, from 1
	ID  string // its id (name) as written
	Err *ValidationError
}

RuleError is a problem with one rule of a rules file: which rule, and what is wrong.

func (*RuleError) Error

func (e *RuleError) Error() string

Error is the line the command line prints: it keeps the path.

func (*RuleError) Sentence

func (e *RuleError) Sentence() string

Sentence is the same for a person: which rule and what is wrong, without the field path.

func (*RuleError) Unwrap

func (e *RuleError) Unwrap() error

type SenderRule

type SenderRule struct {
	ID        int64
	UserID    int64
	MatchType string // address | domain
	Value     string // lower-case
	RuleID    int64  // for route: the rule whose actions apply; 0 otherwise
	Verdict   string // route | keep | block
	Source    string // user | learned
	Hits      int
	CreatedAt int64
}

SenderRule mirrors one row of the sender_rules table.

type Stage

type Stage string

Stage says which step produced a result; it is stored in decisions.stage. The pipeline rewrites StageDecider to "fallback" when the fallback model answered, which only it knows.

const (
	StageSender    Stage = "sender"
	StageCondition Stage = "condition"
	StageDecider   Stage = "decider"
	StageNone      Stage = "none"
)

type ValidationError

type ValidationError struct {
	Path    string
	Message string
}

ValidationError is a rule problem. Path is the offending key as the API reports it, e.g. "conditions.all[0].op". Message is a sentence for a person, complete without the path: capitalised, ending with a full stop, and naming no field path. Error joins the two for a log or the command line.

func (*ValidationError) Error

func (e *ValidationError) Error() string

Jump to

Keyboard shortcuts

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