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 ¶
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 ¶
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.
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.