Skip to main content

Module reasoning

Module reasoning 

Source
Expand description

Reasoning: how a session turns material engineering uncertainty into a recorded, evidence-backed conclusion — with independent advisors when some are present, and without them when none are. ADR 0098 is the decision; docs/REASONING.md the manual.

Four separations hold the design together, and each is a module boundary here:

  • Reasoning is not a provider. policy decides whether independent review is worth having and which advisory capabilities it needs; it never names a vendor, a client tool or a model. A test holds that no source file of this module does.
  • Advisors are declared once. catalogue reads share/advisors.yaml: advisory roles that reference the provider table (share/providers.yaml) and the model catalogue (share/models.yaml) rather than restating them. Mesh peers carrying the reviews feature become advisors at run time from the same declaration.
  • Absence is data. availability turns presence (an executable on PATH, a credential variable set — never read), the reasoning mode and the recorded outcomes of earlier consultations into one typed status per advisor. No advisor at all is an ordinary answer, not an error, and nothing here touches the network (ADR 0032).
  • Records, not transcripts. record is the typed vocabulary a session writes — assessment, plan, consultation, disagreement, resolution, conclusion, validation, attempt — and store the one writer, which validates every reference before a record becomes durable. A consultation may name only an advisor its recorded plan selected; a conclusion’s review count is computed, never authored; a disagreement is closed by evidence, never by a count. state derives the session’s reasoning state, its timeline and its report from the records alone, deterministically.

Talking to an advisor is the one thing this module does not do. The transport is the Node layer under scripts/lib/advisors/, the side of ADR 0032’s boundary that already holds the repository’s network code: it asks this crate for a plan, consults what the plan selected, and records each normalised outcome back through the same writer.

§Example

A checkout whose share declares mesh peers as advisors, with one reviewing peer linked: the situation resolves it as available, and a plan for a material uncertainty selects it.

use majordomus_cli::reasoning::availability::LinkedPeer;
use majordomus_cli::reasoning::{plan, Materiality, PlanOutcome, PlanRequest, Situation};
use majordomus_cli::share::ProviderDeclarations;

// the mode is named outright, so the example does not depend on the runner
std::env::set_var("MAJORDOMUS_REASONING_MODE", "standard");
std::env::remove_var("MAJORDOMUS_ADVISORS_DISABLE");
let root = tempfile::tempdir().unwrap();
let share = tempfile::tempdir().unwrap();
std::fs::write(
    share.path().join("advisors.yaml"),
    "version: 1\ncapabilities:\n  - id: independent_reasoning\n    description: x\npeers:\n  feature: reviews\n  adapter: mesh-review\n  capabilities: [independent_reasoning]\n",
).unwrap();
let peer = LinkedPeer { runtime: "n1-r1".into(), name: "one".into(), features: vec!["reviews".into()] };

let situation = Situation::resolve(
    root.path(), Some(share.path()), &ProviderDeclarations::default(), &[peer],
).unwrap();
assert_eq!(situation.available(), 1);
assert!(situation.loaded.records.is_empty(), "no record has been written yet");

let request = PlanRequest { materiality: Materiality::Material, ..Default::default() };
let p = plan(&request, &situation.mode, &situation.states);
assert_eq!(p.outcome, PlanOutcome::Consult);
assert_eq!(p.selected[0].advisor, "peer:n1-r1");

Re-exports§

pub use availability::derive_availability;
pub use availability::AdvisorCircuit;
pub use availability::AdvisorState;
pub use availability::AdvisorStatus;
pub use availability::ModeResolution;
pub use availability::Presence;
pub use availability::ReasoningMode;
pub use availability::SystemPresence;
pub use catalogue::AdvisorCatalogue;
pub use catalogue::AdvisorDeclaration;
pub use catalogue::AdvisorTransport;
pub use catalogue::ADVISORS_FILE;
pub use policy::plan;
pub use policy::Materiality;
pub use policy::PlanOutcome;
pub use policy::PlanRequest;
pub use policy::ReasoningConfidence;
pub use policy::ReviewPlan;

Modules§

availability
Which advisors can be asked right now, and why each one that cannot, cannot.
catalogue
The advisor catalogue: share/advisors.yaml, one declaration of every advisory role a session may consult, in the grain of share/providers.yaml and share/models.yaml.
check
reasoning.check: the executable half of the rules project.advisors-are-optional and project.review-is-recorded-not-claimed. Each check reads files and says what it found; none of them asks an advisor anything.
policy
The consultation policy: given what is uncertain and how much it matters, whether independent review is worth having, how much, and from which available advisors.
record
The records a session writes while it reasons: typed engineering artefacts, never a transcript and never hidden reasoning. What is kept is what a later session needs to avoid rediscovering a decision — facts, evidence references, assumptions, questions, positions, conclusions, validation — and nothing an advisor said verbatim beyond the summary its adapter normalised.
state
The reasoning state of a session, derived from its records and nothing else: what is uncertain, what was asked and what came back, where advice disagreed and what settled it, what was concluded and whether it was proven — plus the timeline, the degradation that happened along the way, and the report a handover or a final summary carries.
store
The one writer of reasoning records, and the rules a record must satisfy before it becomes durable.

Structs§

Situation
Everything reasoning reads about this checkout, resolved once: the catalogue and the references it resolves against, the mode, every advisor’s standing and the records. Each surface — the capabilities, the environment snapshot — starts here, so none of them derives availability a second way.

Functions§

epoch_seconds
Seconds since the epoch of an RFC 3339 UTC stamp as this crate writes them (2026-10-01T09:30:00Z, optionally with fractional seconds). None for anything else: a stamp that cannot be read is not guessed at.
now_seconds
The current time, seconds since the epoch: the clock availability measures cooldowns against. Zero on a clock set before the epoch rather than a panic.
now_stamp
Now as an RFC 3339 stamp with milliseconds (2026-10-01T09:30:00.123Z): the order of records written within one second is the order they were written in, and the fixed width keeps byte order and time order the same.
stamp_of
The RFC 3339 stamp of seconds since the epoch — the inverse of epoch_seconds.