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.
policydecides 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.
cataloguereadsshare/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 thereviewsfeature become advisors at run time from the same declaration. - Absence is data.
availabilityturns presence (an executable onPATH, 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.
recordis the typed vocabulary a session writes — assessment, plan, consultation, disagreement, resolution, conclusion, validation, attempt — andstorethe 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.statederives 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 ofshare/providers.yamlandshare/models.yaml. - check
reasoning.check: the executable half of the rulesproject.advisors-are-optionalandproject.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).Nonefor 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.