Skip to content

Task: three business-simulation bundles (vending machine, e-commerce, grocery) with automated core/REST e2e tests — no UI, binary-only, documented results #2114

Description

@Al629176

Summary

agentic_math_lab is the only company bundle that produces a verifiable pass/fail — a number is either right or it is not. Every other bundle produces prose an operator must judge. This task adds three more bundles whose outputs can be verified automatically without a human reader, drives them against the compiled binary via the REST surface (no browser, no Playwright), and documents the results in a format that slots into the existing tests/ suite alongside offline_e2e.rs.

The three scenarios are deliberately chosen to cover different orchestration shapes the existing suite does not exercise:

Bundle Core loop Verifiable claim
Vending Machine One orchestrator, one peer; buy/restock/audit cycle Ledger inventory matches purchase log; change calculation is correct
E-Commerce Store Order intake → fulfilment → invoice; multi-step with approval gate Invoice total equals order total; every dispatched card reaches Done
Grocery Supply Chain Forecast → replenishment → shelf-check; scheduled trigger Reorder quantity is within forecast bounds; no shelf goes below safety stock

Problem

The 24 existing bundles are untested end-to-end. The suite has offline_e2e.rs (one card, one scripted turn, done) and orchestration-simulation.spec.ts (scripted orchestrator via the browser), but no test boots a real company bundle and runs its agents through a full business cycle. So:

  • A change to src/company/ that breaks how manifests load their agents fails silently — the company passes cargo check but the agents never pick up the right tools or prompts.
  • A change to the ledger fold (e.g. fix(ledger): refuse a row missing a field the ledger declares required #2048's required-field gate) has no simulation that would have caught the regression before it reached production.
  • The agentic_math_lab pattern (tests/ Rust file + scripted endpoint + verifiable integer) has not been replicated for any business scenario. The math lab proves the stack can finish a card with a checkable answer; it leaves the business logic of ledger writes, budget checks, and multi-agent delegation untested.

The test harness exists but is not reused. offline_e2e.rs already shows the exact pattern:

  1. spawn_script(turns) — a scripted loopback POST /chat/completions endpoint.
  2. RuntimeBuilder — boots the host with a real company manifest and an injectable endpoint URL.
  3. HTTP calls via reqwest / axum::test — drives the REST surface (/api/v1/company/…) exactly as the console would.
  4. Assertions on the final state: task column, ledger entries, spend recorded.

What is missing is the company manifests (companies/agentic_vending_machine/, etc.) and the test files that drive them.

Mechanism (main @ current HEAD).

The scripted endpoint is defined in tests/offline_e2e.rs:

// tests/offline_e2e.rs – lines 68-120 (spawn_script / Script)
async fn spawn_script(turns: Vec<Turn>) -> (String, Arc<Script>) {}

A test boots the host with that endpoint as its inference URL and a company bundle as its manifest:

let manifest = CompanyManifest::load(companies/agentic_math_lab).unwrap();
let state = AppState::new(AppConfig { inference_url: endpoint,}, manifest).await;

Then it drives the REST surface and asserts on the settled state:

// POST /api/v1/company/tasks → dispatches a card
// GET  /api/v1/company/tasks/{id} → checks column == "done"
// GET  /api/v1/company/ledgers/inventory → checks entry fields

All three simulation tests will follow this pattern. None of them opens a browser.


Solution

1 — Bundle: companies/agentic_vending_machine/

A single-location vending machine company. The operator states a purchase (item + quantity); the orchestrator dispatches to a Sales Agent that records the transaction and a Stock Agent that decrements inventory and flags a reorder if stock falls below the par level.

Roster:

orchestrator.toml          — delegates purchase and restock tasks
agents/sales_agent.toml    — records sale to  ledger
agents/stock_agent.toml    — manages  ledger; can issue restock request

Ledgers:

# ledgers/inventory.toml
[[fields]]
name = "item"
required = true

[[fields]]
name = "qty_on_hand"
required = true
type = "integer"

[[fields]]
name = "par_level"
required = true
type = "integer"
# ledgers/transactions.toml
[[fields]]
name = "item"      ; required = true
name = "qty"       ; required = true ; type = "integer"
name = "unit_price" ; required = true ; type = "decimal"
name = "total"     ; required = true ; type = "decimal"

Test file: tests/vending_machine_e2e.rs

Scripted turns (two cards, two agents):

  1. Orchestrator receives "sell 3 × cola @ $1.50" → calls spawn_task for Sales and spawn_task for Stock.
  2. Sales Agent: calls record_entry { ledger: "transactions", item: "cola", qty: 3, unit_price: 1.50, total: 4.50 }update_card { column: "done" }.
  3. Stock Agent: reads inventory, decrements cola qty, calls record_entry { ledger: "inventory" … }update_card { column: "done" }.

Assertions:

// Change calculation is correct
let txn = get_ledger_entry("transactions", "cola").await;
assert_eq!(txn["total"], json!(4.50));

// Inventory decremented
let inv = get_ledger_entry("inventory", "cola").await;
assert!(inv["qty_on_hand"].as_i64().unwrap() < initial_qty);

// Both cards done
for id in &card_ids {
    assert_eq!(stage_of(id).await, "done");
}

2 — Bundle: companies/agentic_ecommerce_store/

A single-SKU storefront. The operator places an order; the orchestrator routes intake to a Fulfilment Agent that reserves stock, then to a Finance Agent that generates an invoice. The invoice total must exactly match the order total — the assertion the test makes, and the one a human would otherwise have to read.

Roster:

orchestrator.toml
agents/fulfilment_agent.toml   — reserves stock in  ledger
agents/finance_agent.toml      — writes invoice to  ledger

Approval gate exercised: Finance Agent uses request_approval before posting the invoice (autonomy tier supervised). The test answers the blocker via POST /api/v1/company/approvals/{id}/approve — the same path blocker-dm-flow.spec.ts drives from the browser. This is the first core-only proof that the approve → resume path works without a browser.

Test file: tests/ecommerce_e2e.rs

Scripted turns:

  1. Orchestrator: spawn_task for Fulfilment + Finance.
  2. Fulfilment: reserves 2 × widget, writes reservation; done.
  3. Finance: computes total, calls request_approval, parks.
  4. Test: POST /approvals/{id}/approve.
  5. Finance resumes: writes invoice { order_id, qty: 2, unit_price: 29.99, total: 59.98 }; done.

Assertions:

let inv = get_ledger_entry("invoices", order_id).await;
assert_eq!(inv["total"], json!(59.98));
assert_eq!(inv["qty"],   json!(2));
// Prove the blocker-resume path without a browser
let approval = get_approval(approval_id).await;
assert_eq!(approval["verdict"], "approved");

3 — Bundle: companies/agentic_grocery_supply/

A three-location grocery supply chain. A Forecast Agent reads weekly sales history and outputs a replenishment quantity; a Buyer Agent submits the purchase order; a Shelf Agent records the received stock and checks the shelf against the safety-stock floor. The test verifies that the reorder quantity stays within the forecast bounds (lower: safety stock, upper: 2× forecast) and that no shelf goes below zero.

Roster:

orchestrator.toml
agents/forecast_agent.toml   — reads  ledger; writes to 
agents/buyer_agent.toml      — writes purchase order to  ledger
agents/shelf_agent.toml      — updates ; flags breach to  ledger

Scheduled trigger exercised: The workflow is triggered by a cron (every Monday 06:00 UTC). The test fires it manually via POST /api/v1/company/workflows/{id}/run and asserts the run reaches done — the first core-only proof of manual workflow trigger without a browser.

Test file: tests/grocery_supply_e2e.rs

Scripted turns:

  1. Forecast Agent: reads seeded sales_history (avg 40 units/week); writes { item: "milk", forecast: 40, safety_stock: 10 }.
  2. Buyer Agent: submits { item: "milk", qty: 40 } to purchase_orders; done.
  3. Shelf Agent: adds 40 to current stock (seeded at 5); new stock 45; writes inventory; no risk flag needed (45 > 10).

Assertions:

let po = get_ledger_entry("purchase_orders", "milk").await;
let qty = po["qty"].as_i64().unwrap();
assert!(qty >= 10 && qty <= 80, "reorder qty {qty} outside [safety, 2×forecast]");

let inv = get_ledger_entry("inventory", "milk").await;
assert!(inv["qty_on_hand"].as_i64().unwrap() >= 0, "shelf went negative");

let risks = list_ledger("risks").await;
assert!(risks.is_empty(), "unexpected risk flag: {risks:?}");

Documentation

Each bundle gets a README.md at companies/agentic_<name>/README.md following the agentic_math_lab pattern:

  • One paragraph: what the company is, what it does, what role the operator plays.
  • One table: agents, their tools, their ledger access.
  • One section: "How the test works" — the scripted turns, the assertions, and what a passing run proves.

Results from the first automated run (CI Simulation E2E lane) are committed to docs/simulations/:

docs/simulations/
  vending-machine-run.md     ← timings, turn counts, assertion outcomes
  ecommerce-run.md
  grocery-supply-run.md

Acceptance criteria

  • Three bundles land in companies/agentic_vending_machine, agentic_ecommerce_store, agentic_grocery_supply — each with a company.toml, agents/, ledgers/, workspace/, and a README.md explaining what the company is and what the test proves.
  • Three test files in tests/vending_machine_e2e.rs, ecommerce_e2e.rs, grocery_supply_e2e.rs — each gated on #[cfg(feature = "openhuman")] and following the offline_e2e.rs scripted-endpoint pattern. No browser, no Playwright.
  • Verifiable assertions — each test has at least one assertion that would fail if the business logic went wrong: correct totals (vending), invoice matches order (e-commerce), reorder within bounds (grocery). Not "the card reached Done"; that is a harness proof, not a business proof.
  • Approval gate covered — the e-commerce test drives POST /api/v1/company/approvals/{id}/approve and confirms the run resumes and the invoice is written. This is the first core-only proof of the blocker-resume path (complements the Playwright proof in blocker-dm-flow.spec.ts).
  • Scheduled workflow trigger covered — the grocery test fires a workflow via POST /api/v1/company/workflows/{id}/run and confirms it reaches done. This is the first core-only proof of a manual workflow run.
  • A Simulation E2E CI lane — added to .github/workflows/ci.yml, running cargo test --features openhuman --test vending_machine_e2e --test ecommerce_e2e --test grocery_supply_e2e. The lane must assert a non-zero test count (script: scripts/ci/assert-integration-targets-run.sh).
  • Feature-lane entry — all three test files are listed in scripts/ci/feature-lanes.txt as tested under the openhuman feature (issue ci: every merge cancels the previous merge's verification, so main is never actually verified #770 pattern).
  • Documented resultsdocs/simulations/ contains one Markdown file per simulation with the run output (turn counts, latencies, assertion outcomes) from the first successful CI run.
  • Diff coverage ≥ 80% — any new Rust helpers in tests/ and any new src/ paths touched by the manifests meet the changed-lines gate.
  • README updatedcompanies/README.md lists the three new bundles and links their simulation docs, in the same format as the agentic_math_lab entry.

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or requestpriority: p3Whenever. Cosmetic, a nicety, or a cleanup with no user visible effect.

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions