Skip to main content

Module store

Module store 

Source
Expand description

The one writer of reasoning records, and the rules a record must satisfy before it becomes durable.

Records live under .ai/local/state/reasoning/<task>/, one JSON file per record, written to a temporary name and renamed into place so that a reader never meets half a record and two writers never share a file. They are checkout state (ADR 0005): never tracked, never a source, never published.

Admission is where the design’s promises become refusals:

  • a material assessment carries evidence — evidence before opinion;
  • a plan is computed here, from the availability at this moment, never supplied;
  • a consultation names an advisor its recorded plan selected — no review nobody planned, and no claim of review from an advisor that was not available (the plan’s own snapshot is the proof); a completed one carries a conclusion, a failed one none;
  • a disagreement is settled by a resolution that cites evidence, and a conclusion is refused while a disagreement on its assessment is unsettled — there is no path from “two advisors said A” to a decision that skips the evidence;
  • a conclusion on material uncertainty carries evidence and a validation plan, weighs every completed consultation of its assessment, and has its review count computed.

The lifecycle of a write: Store::load reads what is durable, admit checks the input against it, redact removes secret shapes, mint_id names the record, and Store::write makes it durable where the next Store::load finds it.

use majordomus_cli::reasoning::availability::{ModeResolution, ReasoningMode};
use majordomus_cli::reasoning::record::*;
use majordomus_cli::reasoning::store::{admit, mint_id, redact, Admission, Store};

let dir = tempfile::tempdir().unwrap();
let store = Store::at(dir.path());
let mode = ModeResolution { mode: ReasoningMode::Standard, source: "default".into() };
let admission = Admission { mode: &mode, availability: &[] };

let input = ReasoningInput::Assessment(AssessmentInput {
    subject: "whether the cache survives a restart".into(),
    ..Default::default()
});
let (body, redacted) = redact(admit(input, &store.load(), &admission).unwrap());
let at = "2026-10-01T00:00:00Z";
let record = ReasoningRecord {
    schema: RECORD_SCHEMA.into(),
    version: RECORD_VERSION,
    id: mint_id(body.kind(), at),
    task: store.current_task(),
    recorded_at: at.into(),
    head: None,
    redacted,
    body,
};
store.write(&record).unwrap();

let loaded = store.load();
assert_eq!(loaded.records.len(), 1);
assert_eq!(loaded.records[0].task, "untasked");
assert!(loaded.get(&record.id).is_some());

Structs§

Admission
What admission needs besides the records.
Loaded
The records of a store, read.
Refusal
Why a record was refused: one sentence a caller can act on.
Store
The store of one checkout: the one place reasoning records are read from and written to.

Constants§

STATE_DIR
The store’s directory, relative to the repository root.
UNTASKED
The task name of a record written while no task was open.

Functions§

admit
Admit an input against the records already durable: the body to store, or why not.
assessment_of
The assessment a record belongs to, when it belongs to one.
mint_id
A fresh record id: the kind, the stamp, and six hex digits of the moment and process.
prior_on_subject
A standing conclusion on the same subject as an assessment, from any task: what a later session reuses instead of asking again.
redact
Remove every secret shape from every string of a body; the shapes that fired.
resolution_of
The resolution of a disagreement, when there is one.
standing_conclusion
The conclusion that stands for an assessment: its latest not superseded by another.
strip_controls
A text without control characters: ANSI escape sequences dropped whole, every other control character but the newline dropped.