Skip to content

Decision-engine scope: governed adapters for selected problems ​

Current capability: company_decision_solve supports two decision problems: inventory/service-level policy and rolling-horizon re-optimization. It records caller inputs as assumptions, evaluates and exposes candidate policies, and returns reviewable proofs. The rolling loop returns forecast calibration measures. Proposed actions are not executed; they carry the approval type required for a governed action. The engine does not read company data itself.

Ambition: Connect governed enterprise context and measured outcomes to a broader set of decision workflows, so state, uncertainty, alternatives, constraints, actions and results can inform later decisions. A complete enterprise semantic layer, broad cross-domain scorecards, and proven general improvement to later decisions are not established capabilities.

Company OS is the substrate a decision engine runs inside, not the engine. This document records what the company_decision_solve seam provides, which decision problems have a real engine behind them, and how the remaining catalog problems attach without a second governance model.

Catalog entry (use this wording) ​

Decision-intelligence engines — hostable as governed adapters through company_decision_solve: caller inputs recorded as assumptions, deterministic uncertainty sampling, every candidate evaluated, proposed actions tagged with the approval action type they need (spend), replayable input_digest. Shipped engines: inventory / service-level policy (#1) and rolling-horizon re-optimization with forecast calibration (#18). The other sixteen catalog problems have no engine yet; they attach by adding a ProblemKind.

What the seam guarantees ​

PropertyMechanism
Assumptions are visibleEvery caller field is echoed in proof.assumptions with source: caller_assumption; the rolling loop marks fields it updated as caller_assumption_updated_by_observations. The engine never reads company data itself.
Replayableproof.input_digest is SHA-256 over canonical JSON of the problem; identical inputs produce an identical proof (sampling is SplitMix64 seeded from the digest, or stratified inverse-CDF).
Reviewableproof.candidates lists every policy evaluated, not only the winner; proof.sensitivity re-scores the chosen policy at ±10 % demand; proof.decision.flags names capacity_bound / target_service_level_unmet.
Proposes, never executesproof.proposed_actions[*].executed is always false; each carries approval_action_type (spend for purchase orders) so the run raises the matching approval request instead of acting.
Read-only toolcompany_decision_solve is classified read_only in src/tools/mod.rs; it needs no mutation scope and writes nothing. The run's tool-call receipt is the record.

Engines ​

inventory_policy — demand, inventory and service-level (#1) ​

Input: per-period demand model (normal, log_normal, or empirical samples), lead time, review period, unit cost, holding and shortage cost per unit, optional target_service_level, on-hand / on-order, optional capacity_max_units.

Method: order-up-to (base-stock) level over the protection interval (lead time + review period). Demand over the interval is sampled deterministically; candidates on a service-level grid plus the critical fractile p/(p+h) are scored by sample average for expected holding units, expected shortage units, cycle service level, fill rate, and cost. The lowest-cost candidate wins, or the cheapest meeting the target when one is set.

Output decision: order_up_to, order_quantity_now, service level, fill rate, expected cost, working_capital_at_policy, and one proposed purchase_order when the inventory position is below the policy.

Verified: crates/company-os-decision-engine tests pin the normal quantile, check the chosen level against the analytic newsvendor solution, and cover target override, capacity cap, determinism, log-normal and empirical models.

rolling_horizon — autonomous re-optimization (#18) ​

start makes the first decision and places the order in a pipeline. Each observe(realized_demand) scores the forecast the last decision rested on (mean, p10, p90), advances arrivals and consumption, records shortage, updates the demand model (exponential smoothing for parametric models; window append for empirical), re-solves, and returns the next proof with calibration: observation count, mean absolute percentage error, bias, and 80 % interval coverage. The state is a JSON value (company_os.decision_rolling_state.v1) the caller carries between calls; it round-trips and replays identically.

Calibration is the evidence a reviewer uses to decide whether the loop should keep being trusted; a persistently positive bias or low coverage is the signal to stop auto-proposing and escalate.

Inventory decision pilot (capability under evaluation) ​

The pilot proves the Decide → Act → Measure → Learn → Re-solve loop on one item against one baseline before any other engine is added. It does not show that the loop improves decisions; it produces the evidence a reviewer needs to judge that.

StepMechanism
Decidepilot::start / pilot::observe solve and open a DecisionRecord (company_os.inventory_pilot_decision_record.v1)
Actpilot::record_action stores what was executed. Any order needs approval_ref and action_ref; a quantity that differs from the recommendation needs override_reason and counts as an override. The rolling pipeline carries the executed quantity, not the recommended one.
Measurepilot::observe(realized_demand) closes the record with both arms: the engine as operated, and the predeclared baseline replayed on the same demand
Learnthe rolling demand model updates from the observation, and calibration is reported
Re-solvethe next record opens from the updated model and position

Each record carries:

  • the source snapshot: source ref, content digest, as-of, and the demand and period definitions;
  • the definitions version (company_os.inventory_pilot_definitions.v1);
  • the full proof: assumptions, uncertainty summary, every candidate, and constraint flags;
  • the baseline order;
  • a decision_digest over everything known at decision time;
  • the action evidence;
  • the realized outcome.

The baseline (fixed_order_up_to), the holding-cost rate, the source snapshot, and the minimum period count are fixed at start and bound by setup_digest. A state whose setup was edited afterwards is refused.

pilot::scorecard compares the arms on the predeclared measures:

  • fill rate;
  • cycle service level;
  • shortage cost;
  • holding cost;
  • total cost;
  • average working capital (on-hand stock at unit cost);
  • the override rate, which is reported for the engine arm only.

Before min_periods_for_comparison it compares nothing. The scorecard always reports earned_claim_level: none and status: capability_under_evaluation: it covers one item and one run, has no uncertainty interval, and no producing code may raise its own claim level (claim contract §6).

company_os_core::inventory_pilot_episode maps each record onto a Decision Episode without writing anything:

  • the candidates, the baseline, and an operator-set option become the sealed alternatives;
  • decision_digest becomes context_digest;
  • every pilot measure, plus its baseline_ counterfactual, is a predeclared expected outcome;
  • a followed recommendation resolves as automated, with the approval as its authority;
  • an override becomes an override intervention plus a human resolution;
  • the period's outcome is observed against inventory_pilot_baseline:<setup_digest>. Engine-arm measures are derived and baseline measures estimated.

Callers submit the OpenDecision and commands through the authenticated episode HTTP adapter.

The Python pilot preflight (scripts/company_os_decision_learning_pilot.py) was not changed here. Its fixed metric set does not yet include the inventory measures.

Agents drive the pilot through company_decision_solve with problem: inventory_pilot and a pilot_action:

  • start takes inventory and pilot_setup.
  • record_action takes state and action.
  • observe takes state and realized_demand.
  • scorecard takes state.

Each response (company_os.inventory_pilot_response.v1) returns the next state, the open_record, and the scorecard. observe also returns the closed_record it measured. The tool stays read_only: record_action stores what the caller reports was executed elsewhere under its approval.

Not wired yet:

  • Nothing posts pilot episodes automatically.
  • No pilot has run on real company data.

Verified: crates/company-os-decision-engine/tests/pilot.rs (including the JSON entry point pilot::handle) and crates/company-os-core/tests/inventory_pilot_episode.rs. The tool file src/tools/decision_engine_tools.rs and its tests were compiled and run against the real engine in a standalone harness, with Tool/ToolOutput copied from src/tools/mod.rs. A full root-crate build has not been run.

Not shipped ​

Production planning and scheduling, procurement and supplier mix, multi-echelon network flows, maintenance policy, workforce allocation, portfolio and capex prioritization, experiment selection, order allocation, working-capital coordination, pricing and promotion, clinical and energy dispatch, and investment sizing have no engine. Each needs its own model and usually a solver; they attach by adding a module and ProblemKind to crates/company-os-decision-engine so the proof shape, digest, and proposed-action contract stay the same.

Adding a problem ​

  1. Add a module with a Problem type (deny_unknown_fields, a validate), a deterministic sampler seeded from input_digest, and a solve that fills DecisionProof — every candidate, flags, sensitivity, proposed actions with an approval action type.
  2. Add the ProblemKind variant and a branch in src/tools/decision_engine_tools.rs.
  3. Prove at least one analytic case and a determinism case in the crate.

Company OS documentation — built with VitePress