# SDK company intelligence: margin, labour, the weekly review and the board pack

This guide covers the part of the SDK that answers a business owner's questions: what makes money, what does not, who can do which job, and what to do about it this week. It is complete from 0.25.1. Everything here is deterministic. No model ranks, scores or writes any of it, and every figure can be traced to the persisted state it was computed from.

## The shape of it

Four layers, each built on the one below.

1. **Chains** hold the facts. An order, a stock position, a bill, a receivable, a work order and a hire are each a persisted state machine. A transition is admitted only with a receipt that satisfies the chain's rule, and each state is sealed with a digest. Chains never call a provider. A host performs a governed read, wraps the payload in provenance (the tool that produced it and a hash of the exact bytes), and hands it in.
2. **Documents** are pure calculations over those states. `build_product_margin`, `build_break_even` and the rest take a sealed plan and persisted states, and return a sealed document with a verdict and recommendations. A document reads nothing and persists nothing itself.
3. **The weekly intelligence review** gathers the documents, raises actions ranked by severity and money at stake, records the owner's decisions, and scores next week what actually cleared.
4. **The board pack** carries one line per document for the month, each with its digest in the evidence.

An unknown stays unknown. A margin with no landed cost, a cash cycle with no receivables, a repeat rate with no customers: each comes back as `None` with the reason in a note, never as zero.

## Plans live in the bundle

Each document has a small sealed plan compiled once for the company and its currency, for example `compile_working_capital_plan("maple", currency="CAD")`. Plans are stored in the company bundle under `supporting_plans[<engine>]`. A plan naming another company is refused with `SCOPE_MISMATCH`, and one in another currency with `ENGINE_CURRENCY_MISMATCH`. When a bundle carries no plan for a document, the console raises `LookupError`, and the review and board pack leave that document out and name it as a blind spot.

## Zero to running

Nothing here needs a hand-written bundle. `company_bootstrap propose` reads the ledger's organisation record, the connectors bound to the project and, when recorded, the books, and proposes the bundle: the archetype with its evidence, the ledger-first plans, the account roles suggested by name, and what each connector unlocks. `accept` keeps it in the project, after which every tool runs with an empty `bundle_json`.

`company_intelligence_readiness` says, for every document, whether it can be built now and what would unlock it. `company_ask` takes a question in plain words ("are we making money", "will we run out of cash", "who has the skills for this job") to the document that answers it, and builds it when it can. The match is a fixed word score over a published catalogue, not a model.

## Starting from the books alone

A company with only its accounting system connected starts here. `books` reads the ledger's profit and loss for a window and, optionally, its balance sheet, and seals the margin ladder, the variable and fixed split of cost, cash, receivables, payables, tax owed, and books-health findings.

```python
console.chain("books", operation="record", payload={"sources": [
    {"kind": "profit_and_loss", "provenance": pnl_read, "payload": pnl_report},
    {"kind": "balance_sheet", "provenance": sheet_read, "payload": sheet_report},
]})
```

The window defaults to the one the report states for itself. The snapshot refuses a report whose account rows do not foot to its own totals, and a balance sheet that does not balance. Roles such as receivable, payable and tax come only from the plan's `account_roles`; an unmapped role stays unknown and names its likely accounts. Recording persists the snapshot under its window, a re-read amends it by one version, and the weekly review raises its findings as actions.

Once a snapshot is recorded for a window, `break_even` and `working_capital` for that same window read it wherever the chains hold nothing, and label every figure that came from the books. Break-even takes contribution and fixed cost from it; working capital takes the receivable, payable and stock balances. Thin books leave the break-even unknown and say why. The month's board pack opens with the books line.

`cash_calendar` then turns the latest snapshot into a 13-week cash calendar. Receivables, payables and the tax owed are dated only by timings you state, or by the working capital plan's day targets; a balance with no timing is left out and named. The books' cost run-rate is used only when the books record enough cost to mean anything, and otherwise the calendar is marked an optimistic ceiling.

`tie_out` then puts every chain-built document's total beside the books' figure for the same window, with the difference, whether it sits inside tolerance, and the reasons such a difference usually has, named but never chosen. A line outside tolerance becomes a weekly-review action. The definitions of every figure, with a worked example an accountant can check, are in `sdk-intelligence-definitions.md`.

`what_if` points the same arithmetic forward: raise prices, hire, take on or lose a customer, change payment terms, add a cost. It returns the ladder before and after, the change in operating result, break-even and cash, a price lever at three elasticities, and every assumption. It is arithmetic on the books, not a forecast, and says so.

`customers` reads the ledger's sales invoices: per customer the revenue and share, what they owe and how old it is, and how long they take to pay. It flags one customer carrying too much of the revenue, balances past due, and slow payers, and the review raises each as an action.

`hygiene` answers "can I trust these numbers": months with sales and no costs, no wages recorded, money in suspense or uncategorised accounts, tax owed beyond the cash, and (given the bank transactions read) old unreconciled bank lines, each with its effect and the fix, and what could not be checked.

`digest` is the week for the owner in plain words: the last month against the one before, the figures that matter with their change, three actions ranked by the money at stake (each keyed to the review so it can be accepted, deferred or declined), what has moved the wrong way, and what cannot be said yet. It sends nothing; with a channel and an opaque recipient reference it returns the approval-gated send.

`backfill` recovers the past: `plan` lists one profit and loss read per past month and a balance sheet at each month end, and `record` builds and keeps each month from the reports that state it, skipping months before the ledger's first activity and naming any month the books refuse. Accepting a bootstrap proposal with a bound Xero ledger hands back the plan, so the history starts with the past year.

`history` lines the headline figures up across every window recorded so far (revenue, net margin, cash, cost share, overdue receivables, top-customer share, cash conversion days, margin of safety, hours short, tie-out differences, open actions): the latest value, the change on the previous window, the direction over the last three, and a warning when a figure has moved the wrong way three windows running. A figure a window could not build is a gap, never a zero. `calibrate` then proposes plan thresholds from that history by plain percentiles (the concentration ceiling, the minimum cost share, the receivable-days target), each with the observations it rests on and the current setting beside it; proposals only, written into the bundle by the owner or not at all.

## Product and money documents

| Verb | What it answers |
|---|---|
| `product_margin` | Revenue, landed cost, fees, returns and contribution per product over a window. Landed cost is the stock position's blended average unit cost. |
| `product_portfolio` | Each product placed as grow, fix, run down or discontinue, from margin and velocity across two windows. Return problems outrank overstock. |
| `price_probes`, `probe_outcomes` | A bounded price test per product, then the measured result: did the after-window unit contribution reach the target. |
| `contribution_bridge` | Why contribution moved between two windows, split into price, volume and mix. |
| `break_even` | Fixed cost from the cost registers, prorated to the window, against contribution. States whether fixed cost is known. |
| `working_capital` | Days sales outstanding, days payable outstanding, days inventory outstanding, the cash conversion cycle against target, and the cash each lever would free. Payables free cash by stretching, the others by coming down. |
| `open_to_buy` | What stock to buy, funded in contribution-per-unit order inside the cash envelope's discretionary capacity. |
| `stockouts` | Replays the on-hand timeline, finds the empty spells, and values the lost units at the product's contribution. Says when a reorder came late rather than small. |
| `returns_ledger` | Refund, handling and write-downs less restocked value per product. A restock counts only when bound to the returned order's state digest. |
| `customer_profitability`, `customer_cohorts` | Contribution per customer after cost to serve, and cohorts by first-order month with acquisition spend, repeat rate and payback. |
| `supplier_scorecard` | How each goods supplier actually performed, from the purchases, receipts and landed costs the inventory chain recorded. |

## Services and skilled labour documents

| Verb | What it answers |
|---|---|
| `skills` | Matches each job's required skills and levels to worker profiles, using evidence from completed work orders. Reports unstaffed jobs and why. |
| `assignment` | Every open job staffed at once from the match report, so a person's hours are given only once. A job that cannot be filled is short by a stated number of hours and names who would have covered it had an earlier job not taken them. |
| `skills_inventory` | Depth per skill, key-person risk, and licences about to lapse. |
| `sourcing` | For each gap: assign, upskill, contract, hire or escalate, priced against market rates. A licence is never trained into. |
| `skills_development` | For each shortfall, depth or licence need: train, hire, contract, renew or escalate, with standing cost over a payback horizon. |
| `worked_hours` | Timesheet hours per worker against the weekly hours each profile plans with; over, under or as planned, with a proposed figure. |
| `market_rates` | Realised hourly rates per skill from contractor engagements actually paid, beside the plan's typed rates, with proposals. |
| `hand_offs` | Accepted hire, requisition and contract routes drafted as the hiring chain's receipt or a contractor brief; nothing is opened. |
| `workforce_capacity` | Week by week, the hours each skill is asked for against the hours qualified people can give, and what the bench costs. |
| `rate_card`, `job_quote`, `quote_outcomes` | The bill rate each role needs once cost, overhead and unbillable hours are counted, a quote built from it, and later the verdict: held, overran, underquoted, open or unmatched. Labour is compared like for like. |
| `job_costing`, `services_margin` | What each job cost against its estimate and where the money went, and what each engagement, role and worker earned against what they cost. |

## Running a document

From Python, through the console:

```python
result = console.chain("break_even", operation="build",
                       payload={"window_start": "2026-10-01T00:00:00Z",
                                "window_end": "2026-11-01T00:00:00Z"})
print(result["verdict"])
```

From the command line, `lightbulb company <verb>` takes `--bundle`, `--project-id`, `--payload`, `--states`, `--now` and `--out`. From an MCP host, each verb is the tool `company_<verb>`. All three run the same function. Each verb is also registered as an executable primitive with a worked example, so a workflow can call it as a step.

Windows default to the last thirty days. `REPORT_WINDOW_MISMATCH` is raised when a supplied margin report covers a different window from the document that consumes it.

## Getting paid

`chase build` lists the reminder due for every overdue customer under the collections ladder, drafted, with the mandate's answer and the send to make; `chase outcomes` scores what came back after each reminder sent.

## Scoring an operator

`gym run` runs a synthetic company for simulated months through the same console, mandate and `next` step an agent uses, and returns a sealed score: sales growth, cash and its low point, overdue left at the end, actions by kind and actions outside the mandate. Compare an operator against `do_nothing` and the reference `mandate` operator on the same scenario.

## Growing the company

`goals set` turns a target into arithmetic on the books (baseline run rate, required run rate, the gap, and the drivers that must close it); `goals progress` shows each driver against its target. `bets place` preregisters a growth bet with a spend cap (checked against the mandate), one history metric, a target and a kill rule; `bets judge` reads every bet against the recorded history and says scale, kill, extend or running.

## Running the company

`mandate` is the owner's standing permission: per kind of action, act alone, ask, or never, with a cap per action and a monthly envelope; reads never need one; every unattended action is logged. `next` is the agent's one call: everything open, ranked and gated by the mandate, with the tool to call and the effect to verify; `journal` records what each session did and promised, and the next `next` opens with it. The loop is: `next`, do the head of the queue (checking it through `mandate check`, logging it with `mandate record`), `journal record`, repeat.

## The weekly review loop

```python
review = console.chain("intelligence_review", operation="record",
                       payload={"review_ref": "intel-2026-W42", **window})
```

`record` builds every document the bundle's plans allow, raises actions, and persists the sealed review. Each action has a stable key of source, kind and subject. The review also lists **blind spots** (document kinds it could not build) and **stale** inputs. A review that carries forward a previous one marks which actions are new and which persisted.

Decisions are recorded against those keys:

```python
console.chain("review_decisions", operation="record", payload={"decisions": [
    {"key": key, "decision": "accept", "owner_ref": "user:ops",
     "due_at": "2026-11-05T00:00:00Z"},
]})
```

An `accept` needs an owner and a due date. A `defer` needs a due date. A `decline` needs neither. The ledger refuses an unknown action, a second decision on the same action, and a due date before the decision. New decisions merge over recorded ones, and the ledger advances exactly one version per change.

Every accepted or deferred decision without an outcome then becomes a **work item** in the cadence, of kind `act_on_decision`, overdue first:

```python
items = console.work_items()["work_items"]
console.supply(items[0]["action_id"], {"outcome": "done", "note": "customer moved to prepaid"})
```

Supplying `done` or `dropped` closes the item by amending the ledger. `company_decision_work_items` lists them without the rest of the cadence.

The following week, `review_outcomes` compares the two reviews: which actions cleared, which carried, how much money was released, and of the cleared ones, how many had been accepted, how many were closed as done, and how many cleared with no decision at all. That last number is the honest measure of whether the review is driving anything.

## The board pack

`console.board_pack(month="2026-10")` builds the month's pack from the persisted periods and chains. When the bundle carries their plans, it also builds working capital, break-even, customer cohorts, stockouts and the returns ledger over the calendar month, adds the trend line from every recorded window and the open decision work items of the latest ledger, and renders one line each under **Cash, customers, and stock**, with `kind:digest` evidence references. Supplying `intelligence_documents` overrides the built set. A document from another company or of an unknown kind is refused.

## Bringing an existing company in

`adopt` plans and runs the first import from provider reads: the latest invoice of each Stripe customer opens a subscription case, every ledger bill with something outstanding opens a payable, and every open CRM deal with the facts the ideal-customer profile needs opens a prospect. Each rejected row is reported with its reason. Paid bills and closed deals are left with the provider as history. `sync` then advances the persisted cases from fresh reads, week after week, and reports what no transition can honestly absorb as a discrepancy for a person.

What the platform's reads carry today matters here. Under governed execution `xero.list_bills` returns Spring's month-close projection, which states no due date, so Xero bills adopt only when the payables plan sets `default_terms_days`; the receipt then records that the due date came from the plan's terms. `crm.search_deals` rows carry no industry, size, region or title, so open deals are reported as missing those facts rather than adopted. The governed `stripe.list_invoices` page carries no line items, so subscriptions cannot be adopted from it. Each of these is an exact, named refusal, never an empty result.

Both require the provider payload to match the digest in its provenance. They replay what was read and never write to a provider.

## What this does not establish

- These documents describe **persisted SDK state only**. They cannot show that every customer, bill or stock movement in an external system was imported.
- Local acceptance of the package does not certify a live provider journey. Reads of controlled financial records run through governed connector execution, which a deployment must have enabled, and connectors must be bound to the project.
- Output quality is input quality. A company with no recorded costs will show full margin and an unknown break-even, correctly.
