Expand description
The self-learning store: reflections, learned rules, and the miner.
Reflexion-style (Shinn et al. 2023) with LEAP consolidation (Zhang et al. 2024) to come. Three stages: reflection (one contextual note per user intervention — this module), abstraction (reflections → candidate rules, batched), and consolidation (a fixed token budget per domain, so learning never grows the system prompt without bound).
Storage is files, not a database, on purpose: everything in mecha is
inspectable text (JSONL transcripts, TOML config), and the user’s explicit
requirement for this system is that it can be inspected and edited. The
layout under ~/.mecha/learning/:
reflections.jsonl append-only evidence, one line per reflection
mined.jsonl session ids already mined, one per line
distilled.jsonl session ids already distilled to the graph
rules/<domain>.user.toml the user's own rules — never written by code
rules/<domain>.learned.toml rewritten at consolidationThe directory is a git repository (created best-effort on first open), and
passes commit their changes: git log is the audit trail, git diff the
review UI, git revert the undo for a bad consolidation. If the workload
ever outgrows files — the CIPHER retrieval tier is the likely reason — the
swap to a database happens behind this module’s API. Noted as a real
possibility, not a failure of this design.
Split of responsibilities: extraction from transcripts is pure and
unit-tested here; the Reflector holds the one model call, mirroring
crate::eval::Judge. What counts as an intervention:
- Steering — user text riding in the same message as tool results. Unambiguous: the user reached in mid-run to redirect.
- Denial — a tool result reading “Denied by the user: …”. A recorded rejected intent.
- Follow-up turns — a later user turn may be a correction of the
assistant’s behaviour or just the next task. Extraction flags the
candidate; the
Reflectordecides, and is told to skip freely.
Structs§
- Intervention
- One moment in a transcript where the user stepped in.
- LeapRun
- Audit record for one abstraction/consolidation pass. Appended to
runs.jsonl; together with the store’s git history this is the full lineage from any rule back to the reflections that argued for it. - Learner
- Runs one abstraction/consolidation pass for a domain: current learned rules + unprocessed reflections in, a rewritten learned rule set out.
- Learning
Store - Proposal
- A rule change waiting for the user, with the evidence that argues for it.
- Reflector
- Turns interventions into reflections with one model call each.
Mirrors
crate::eval::Judge: bare provider, no tools, no history. - Reflexion
- One learned note, tied to the intervention that produced it.
- Rule
- One rule in a domain’s TOML file.
- Rule
Tally - What the ledger says about one rule, folded from its rows.
- Store
Lock - Holds the store’s writer lock for as long as it lives. See
LearningStore::lock. - Validation
Record - One probe’s measurement, written down instead of printed and discarded.
Enums§
- Evidence
- What the reflector was shown when a reflection was mined.
- Origin
- Where a reflection’s evidence came from, provenance-wise.
- Trigger
Constants§
- LEARN_
MIN_ REFLECTIONS - How many unprocessed reflections a domain needs before
mecha learnconsolidates — the default behindlearn --min, and the floor doctor’s starved-learner check measures against. One constant, two readers, on theMAX_ACTIVE_RULES_PER_DOMAINlesson: a check that names one number while the gate applies another fails silently, and looks like a healthy loop. - MAX_
ACTIVE_ RULES_ PER_ DOMAIN - Hard cap on active learned rules per domain — the count half of the
budget, where
RULES_CHAR_BUDGETis the size half. This is the check that does not depend on the model listening; [learner_frames] states the same number to the learner, interpolated from here so the two cannot disagree. - PASS_
DOMAINS - Domains loaded by a named pass rather than by a general run.
- RULES_
BLOCK_ HEADING - The heading
rules_prompt_blockemits, shared so a validator can strip an old block before injecting a candidate one — a session recorded with rules must not get them twice, or keep stale ones in its baseline arm. - RULES_
CHAR_ BUDGET - Roughly how large a domain’s rendered rules block should be allowed to get, in characters (~4 chars per token). Consolidation exists so learning never grows the system prompt without bound; this is the bound.
- RUN_
DOMAINS - The domains whose rules ride in an ordinary agent run’s system prompt.
- TRIAGE_
DOMAIN - The mail classifier’s own learning domain.
Functions§
- budget_
refuses - The budget gate’s arithmetic: a candidate set that ends over the cap may land only by shrinking an already-over set toward it. Growth past the cap — however the learner argued for it — is refused, and the refusal is what forces the next pass to merge or retire before it may add.
- classify_
origin - Classify a reflection’s origin from the taint covering its intervention.
- domain_
rules_ section - One domain’s section of the rules block, from explicit rule sets rather than the store — which is what lets a proposal gate render a candidate set exactly as a run would see it, before anything is written anywhere.
- evidence_
for - Decide what the reflector may see for one intervention, and how the resulting reflection classifies.
- extract_
interventions - Extract every intervention from a recorded conversation.
- finalize_
rules - locate_
followup - Find the user turn carrying
intervention_textand return the index of that message — the conversation prefix for a counterfactual probe is everything before it. - routed_
domains - Every domain something actually loads. What “unrouted” must be measured against — a domain is routed if a run carries it or a pass reads it.
- rule_
tallies - Fold ledger rows into per-rule tallies.
- rules_
hash - Stable content hash of a rendered rules block. FNV-1a written out here because the std hasher is deliberately unstable across Rust releases, and a ledger key that drifts with the toolchain would silently split every tally.
- run_
domains_ including - The domains a run exercising
domainwould carry:RUN_DOMAINS, plusdomainitself when it is not one of them. - strip_
rules_ block - Remove a previously injected rules block from a recorded system prompt.
- wrap_
rules_ block - Wrap rendered sections in the heading a run’s system prompt carries.