Skip to main content

LearningStore

Struct LearningStore 

Source
pub struct LearningStore { /* private fields */ }

Implementations§

Source§

impl LearningStore

Source

pub fn default_root() -> Result<PathBuf>

Source

pub fn open(root: impl Into<PathBuf>) -> Result<Self>

Open the store, creating the layout (and, best-effort, the git repo) if it is not there yet. Git being absent degrades to plain files — the audit trail is lost, the data is not.

Source

pub fn open_existing_default() -> Option<Self>

Open at the default location only if it already exists — for read paths (prompt assembly) that must not create state as a side effect.

Source

pub fn root(&self) -> &Path

Source

pub fn append_reflexion(&self, r: &Reflexion) -> Result<()>

Source

pub fn reflexions(&self) -> Result<Vec<Reflexion>>

Source

pub fn mined_sessions(&self) -> Result<HashSet<String>>

Sessions already mined, so mecha reflect never re-reads one.

Source

pub fn mark_mined(&self, session_id: &str) -> Result<()>

Source

pub fn mined_outbox(&self) -> Result<HashSet<String>>

Outbox items already mined for writing reflections — the outbox counterpart of Self::mined_sessions, so the nightly pass never re-argues the same edit.

Source

pub fn mark_outbox_mined(&self, item_id: &str) -> Result<()>

Source

pub fn mined_corrections(&self) -> Result<HashSet<String>>

Triage corrections already mined for triage reflections — a third ledger beside sessions and outbox items, for the same reason they are separate from each other: an id in one must never satisfy another, and a shared ledger makes that an accident waiting to happen.

Keyed per correction, not per thread. A thread corrected once and then corrected again is two lessons — the second is often the more interesting one, since it says the first correction was not enough.

Source

pub fn mark_correction_mined(&self, key: &str) -> Result<()>

Source

pub fn distilled_sessions(&self) -> Result<HashSet<String>>

Sessions already distilled to the knowledge graph — mecha distill’s ledger. Kept in this store, not beside the sessions, for the same reasons the mining ledgers are: the writer lock covers the read-then-mark race between two detached session_end hooks, and the git history says when each push happened.

Source

pub fn mark_distilled(&self, session_id: &str) -> Result<()>

Source

pub fn user_rules(&self, domain: &str) -> Result<Vec<Rule>>

The user’s own rules. This file is never written by any pass: the consolidation prompt is told these rules are immutable, and this is that constraint made structural rather than left to the model.

Source

pub fn learned_rules(&self, domain: &str) -> Result<Vec<Rule>>

Source

pub fn write_learned_rules(&self, domain: &str, rules: &[Rule]) -> Result<()>

Replace a domain’s learned rules. Only consolidation calls this. Written via a temp sibling and rename: the run-start injection path reads this file with no lock (a read must never wait on a learn pass), so the file on disk has to be complete at every instant — a torn TOML here would fail an unrelated run at startup.

Source

pub fn domains(&self) -> Vec<String>

Domains that have any rules file on disk.

Source

pub fn rules_prompt_block(&self) -> Result<Option<String>>

Every domain’s rules, rendered. This is the whole store’s view — mecha rules, a proposal diff, a validation arm — and deliberately not what a run’s system prompt gets. Use Self::rules_prompt_block_for for that.

Source

pub fn rules_prompt_block_for(&self, domains: &[&str]) -> Result<Option<String>>

The block injected into one run’s system prompt: the user’s rules first, then enabled learned rules, for the named domains only. None when there is nothing to say — an empty section would spend cache-prefix tokens on a heading.

Selection exists because a domain is not universally relevant and the block rides in every turn’s cached prefix. writing rules describe how this user’s prose should read; they earn their tokens when the model is drafting a message and cost them on every run that never drafts anything. A future triage domain — rules for the mail classifier — is worse still: that pass is a tool-less, history-less call with one job, and general conduct rules are noise to it exactly as its rules would be noise everywhere else.

Named rather than filtered, so a new domain is opt-in. A domain that appears on disk joins no prompt until something asks for it, which is the direction that fails safely: the cost of forgetting to add one is rules that do not fire, and Self::unrouted_domains reports that at startup. The cost of the other default is every future domain silently joining every prefix — and with MAX_ACTIVE_RULES_PER_DOMAIN at 25, three domains would be 75 rules in front of every request.

Source

pub fn unrouted_domains(&self, routed: &[&str]) -> Result<Vec<String>>

Domains that hold active rules but ride in no run’s prompt — the silent half of opt-in selection. Startup warns on these, the routed-name-matches-no-tool precedent: a user rule nobody reads is indistinguishable from a user rule being obeyed, and a typo in a filename is the likely cause.

Source

pub fn over_budget_domains(&self) -> Result<Vec<(String, usize)>>

Domains whose active learned rules exceed MAX_ACTIVE_RULES_PER_DOMAIN — the always-loaded block drifting past the adherence cliff. Startup warns on these (the routed-name precedent); the learn gate refuses to grow them further.

Source

pub fn lock(&self) -> Result<StoreLock>

Take the store’s writer lock, blocking until it is free.

Every pass that writes (reflect, learn) takes this before reading the state it will act on — the read is where the race lives: two reflects that both read mined_sessions before either marks would mine the same session twice, which stopped being hypothetical the moment reflect started running detached at every session close.

Advisory flock, so it serializes mecha’s own writers without doing anything to the user’s $EDITOR — the store’s files staying humanly editable is a requirement, not an accident. The kernel drops the lock when the fd closes, crash included, so a dead pass can never wedge the store. Read paths (prompt assembly, validate) do not take it: a run start must never block on a learn pass, which is why every rewrite in this module goes through a temp sibling and rename.

Source

pub fn try_lock(&self) -> Result<Option<StoreLock>>

Non-blocking variant: None when another pass holds it.

Source

pub fn commit(&self, message: &str)

Best-effort commit of the store’s current state. Losing git loses the audit trail, never the data, so failures are logged and swallowed.

Source§

impl LearningStore

Source

pub fn write_proposal(&self, p: &Proposal) -> Result<()>

Write (or rewrite) one proposal, atomically — mecha proposals list must never read a half-written file from a nightly pass.

Source

pub fn proposals(&self) -> Result<Vec<Proposal>>

Every proposal, oldest first.

Source

pub fn proposal(&self, id: &str) -> Result<Proposal>

Find one proposal by id or unique prefix. Ambiguity is an error rather than a guess, same as session lookup.

Source

pub fn append_run(&self, run: &LeapRun) -> Result<()>

Source

pub fn mark_reflexions_processed( &self, ids: &[String], run_id: &str, ) -> Result<usize>

Mark reflections consumed by a pass. Rewrites the file via a temp sibling and rename, so a crash mid-write loses the marking, never the reflections.

Source§

impl LearningStore

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more