lessonmatch

package
v1.0.0-beta.154 Latest Latest
Warning

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

Go to latest
Published: Jul 19, 2026 License: MIT Imports: 4 Imported by: 0

Documentation

Overview

Package lessonmatch is the deterministic, PURE lesson selector that powers bounded push-based lesson delivery (ADR-080, agent-memory-lesson-substrate).

It answers one question: given a set of lesson candidates already fetched from the graph and a loop's scope, which active lessons — in which order, within which bounds — should be injected into that loop's brief?

Two properties are load-bearing:

  • PURE. Match issues no graph queries, reads no clock, touches no I/O. The caller fetches candidates (agentic-loop's LessonReader today; the future `want:[lessons]` fusion facet tomorrow) and hands them in; this package only ranks and bounds. That separation is what lets both consumers reuse one selector without dragging in a transport.
  • DETERMINISTIC. Ordering is severity → immutable created-at → entity-ID, with no similarity, embedding, or relevance scoring (the fusion determinism contract). The same candidates + scope always yield the same Result, including across an ADR-073 from-zero reingest — because the ordering key is the lesson's IMMUTABLE birth timestamp, never a KV revision or a re-stamped update time.

Index

Constants

View Source
const (
	// DefaultK is the count ceiling applied when Opts.K is unset (<= 0).
	DefaultK = 10

	// MaxK is the hard upper bound on the count ceiling. A requested K above
	// this is clamped down — briefs stay small regardless of caller intent.
	MaxK = 25

	// DefaultByteBudget is the total injection-form byte budget applied when
	// Opts.ByteBudget is unset (<= 0). ~4 KB (≈1000 tokens) — large enough that
	// a default-K selection of byte-bounded (≤320B) injection forms passes, so
	// K is the biting bound in the common case and the byte budget is the
	// secondary safety net. Callers configure the real budget.
	DefaultByteBudget = 4096
)

Variables

This section is empty.

Functions

This section is empty.

Types

type Lesson

type Lesson struct {
	// EntityID is the lesson record's 6-part entity ID. Used verbatim in the
	// rendered block and as the final deterministic ordering tiebreak.
	EntityID string

	// Status is agent.lesson.status. Only "active" lessons are eligible; every
	// other value (proposed/retired/superseded/empty) is excluded here so the
	// exclusion lives in ONE place both consumers share.
	Status string

	// Severity is agent.lesson.severity (critical|warning|info). An unrecognised
	// or empty value ranks below "info" (lowest urgency), never panics.
	Severity string

	// CreatedAt is agent.lesson.created-at — the IMMUTABLE RFC3339 birth
	// timestamp. The replay-stable ordering key. Empty or unparseable sorts
	// last, deterministically (see less).
	CreatedAt string

	// AppliesTo is every agent.lesson.applies-to scope key on the lesson
	// (multi-valued). Grammar: "tag:<token>" | "id:<entity-id-prefix>".
	AppliesTo []string

	// InjectionForm is agent.lesson.injection-form — the bounded string rendered
	// verbatim into the brief. Its byte length is charged against ByteBudget.
	InjectionForm string
}

Lesson is one candidate's matcher-relevant projection. The caller populates it from an agent.lesson.record entity's triples. Fields the matcher does not read (summary, detail, polarity, evidence, ...) are deliberately absent.

type MatchedLesson

type MatchedLesson struct {
	EntityID      string
	InjectionForm string
}

MatchedLesson is one included lesson, in ranked order.

type Opts

type Opts struct {
	// K is the count ceiling. <= 0 uses DefaultK; values above MaxK clamp to MaxK.
	K int

	// ByteBudget is the total injection-form byte budget. <= 0 uses
	// DefaultByteBudget. Selection stops at the first ranked lesson that would
	// push the running injection-form byte sum over the budget.
	ByteBudget int
}

Opts bounds the selection.

type Result

type Result struct {
	Included      []MatchedLesson
	MatchedCount  int
	IncludedCount int
}

Result is the bounded, ordered selection plus the observability counts that make truncation visible: MatchedCount is every eligible lesson (post scope + status filter, pre-bounds); IncludedCount is len(Included) after the K and byte bounds. MatchedCount > IncludedCount means the brief was truncated.

func Match

func Match(candidates []Lesson, scope Scope, opts Opts) Result

Match selects, orders, and bounds the active lessons whose scope keys match the given scope. Pure and deterministic; see the package doc.

type Scope

type Scope struct {
	// EntityIDs are the loop's in-scope entity IDs, matched against `id:` keys
	// on entity-ID SEGMENT boundaries (see idPrefixMatchesEntity).
	EntityIDs []string

	// Tags are the loop's scope tags (e.g. the loop role), matched against
	// `tag:` keys by exact string equality.
	Tags []string
}

Scope is the loop's declared applicability scope. A lesson is eligible when ANY of its AppliesTo keys matches this scope. An empty Scope (no tags AND no entity IDs) matches nothing — never a firehose.

Jump to

Keyboard shortcuts

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