Concepts
Adaptive Calibration & environments
Teach ARCNM your shop. Costing environments group your fleet, rates and historical observations, so one batch of actuals turns a benchmark into your own costs.
Out of the box, ARCNM prices parts against industry-standard benchmarks. With one batch of your own historical observations, Adaptive Calibration learns your shop — your machines, your operators, your overhead, your supplier rates — and stays aligned as your reality shifts.
Every quote you accept, every realised cost you push back, every correction your estimators make is fed into the next calibration cycle. The model gets sharper with use.
This is the defensible difference between a generic quoting engine and a tenant-specific cortex.
Costing environments
A costing environment is a tenant-scoped bundle of:
| Component | Holds |
|---|---|
| Fleet machines | Hourly rate, capacity, axis count, RPM ceiling, capability |
| Labour rates | EUR / hour, per discipline / shift |
| Overhead rates | Variable + fixed, VDI 3258-compatible schema |
| Material catalogue | EUR / kg, density, machinability, supplier SKU |
| Default materials | Per discipline |
| Subcontract rates | Bought-in operations (DE: Fremdbearbeitung / Fremdvergabe): coating per m² — except hot-dip galvanizing and browning, which the trade sells per kg — heat treatment per kg, marking per part, welding per m; each with an optional order minimum per lot |
| Calibration data | Your observed prices, fed back as targets |
Every rate above is denominated in the environment's own currency
(currency, ISO 4217) — not in EUR. The unit_rate_eur /
lot_minimum_eur column names on subcontract rows are a legacy misnomer;
both values are used unconverted, and every rate an environment states is
denominated in that environment's currency throughout.
An org can have any number of environments. Typical setup:
production— real machines, real rates, attached to every customer quote.staging— sandbox prices used by CI / pre-flight.subcontractor_a— alternate fleet for outsourced work.
Set the env per calculation:
{
"part_revision_id": "1f…",
"costing_environment_id":"env_2a…"
}
Or price the same revision in several environments at once — one
calculation per environment, grouped under a batch_id (each cell is
one calculation against your quota):
{
"part_revision_id": "1f…",
"costing_environment_ids": ["env_prod…", "env_sub_a…"]
}
GET /api/v1/calculations/batches/{batch_id}/comparison then returns
the pivoted matrix — the cheapest environment per part, deltas vs your
baseline, per-environment totals — server-computed, so the numbers you
compare are the same ones the UI and MCP report. Every figure is struck on
the offer price (offer_price per part, offer_total per lot): the price
each calculation headlines, with the surcharges its environment states.
Each cell also carries unit_cost, the cost before those surcharges. Don't
rank on it: an environment that states its overheads and margin as
cost-sheet rows moves them out of unit_cost, so two environments' unit
costs are not like for like. Deltas anchor on your
baseline environment when it is part of the grid, otherwise on the
grid's first environment. See
Quote a part → multiple environments.
Subcontract rates (Fremdbearbeitung)
Operations a shop buys in rather than runs on its own machines —
coating, heat treatment, marking, welding — are priced by
subcontract rate rows of the environment
(POST /api/v1/environments/{env_id}/rates with kind: "subcontract"). Each row names the process (the resolver slug, e.g.
coating:zinc_plate), the price per that process's canonical unit
(coating per m², heat treatment per kg, marking per part, welding per
m) — in the environment's currency — and an optional order minimum
that is amortised over the lot.
The unit belongs to the process, not only to its family, because the
trade does not price every coating the same way. Hot-dip galvanizing
(coating:galvanise, EN ISO 1461) and browning (coating:black_oxide,
DIN 50938) are sold per kilogram — hot-dip is billed on the weight
after galvanizing — while paint, powder coat and anodizing are sold per
area. The API validates a row against the unit its process is priced in
and rejects a mismatch, and a row stored in a unit that is no longer the
current one is reported pricing_unit_mismatch and not applied, rather
than being reinterpreted: 9.00 entered as €/m² is not 9.00 €/kg.
Until a tenant adds a row, platform default prices apply; a tenant row
takes precedence from the next calculation. In the app these live
under Setup → Subcontracting.
Comparing a calibrated environment with an uncalibrated one
Calibration corrects prices in the environment it was fitted for. So if one environment in a comparison is calibrated and another is not, part of the gap between them is the correction rather than the factories — and nothing in the costs themselves says so.
The comparison says so instead. Every cell carries
calibration_cost_offset (the multiplier that price was produced under;
1.0 = none, null = a run from before this was recorded) and
calibrated (whether any calibration moved that price, a per-driver one
included, which leaves the multiplier at 1.0; null for a run from
before this was recorded). calibrated is recorded when the calculation
runs. It is the same verdict as the calculation's own
confidence.calibrated, null included, and publishing or retiring a
calibration later does not change it. Re-run to price under the current
calibration. A cell whose calibrated differs from its row's baseline
cell (one calibrated, the other not) is flagged calibration_mismatch.
Two calibrated cells are not flagged, even though each environment is
fitted to its own costs and their multipliers differ. Each environment
reports calibrated_cells, and the matrix sets mixed_calibration when
any cell is flagged.
These are flags, not exclusions. The deltas are real money and stay in
the comparison — a mismatched cell is still ranked, still wins if it is
cheapest, and still counts toward totals. Excluding uncalibrated columns
would leave anyone who has calibrated exactly one environment with no
comparison at all. Treat mixed_calibration as a prompt to calibrate the
other environments before acting on a close result.
Parent environments
Environments support a parent chain — override only what differs from the parent (a night-shift labour rate, for example) and inherit everything else.
production
└── production-night-shift (overrides labour rate)
└── production-night-shift-2025-q4 (overrides electricity cost)
The cascade walks the chain until a tier resolves.
Parameter resolution
Every cost coefficient resolves through a strict precedence order.
First match wins. No silent defaults — a parameter that can't
resolve raises parameter_unresolved on the request rather than
emitting a suspiciously low price.
Resolved values are wrapped with full provenance: every quote is traceable back to which source supplied which number.
{
"feed_rate_mm_min": {
"value": 450.0,
"source": "environment_calibration",
"provenance": {
"env_id": "env_2a…",
"samples": 42,
"interval": [410.0, 490.0]
}
}
}
Sources, in precedence order:
- Manual override on the environment (operator-set).
- Parent override (inherited via the parent chain).
- Environment calibration (learned from your observations).
- Platform default (industry-standard benchmark).
If you've never calibrated, every parameter resolves at tier 4 — you still get a defensible quote from published-literature benchmarks.
Calibration
You give ARCNM a batch of "target prices" for parts you've already costed externally (your ERP, an external quote, an internal cost-engineer estimate), and the environment adapts so the next quote lands within tolerance. Drive it from the dashboard (Environments → Calibration) or over the API — see Teach an environment from your actuals.
Observations that fall outside a conservative safe band are logged and suppressed, not applied — a value far outside the expected range almost always signals a data-entry error or a misclassification, not a real cost shift. The dashboard surfaces what was applied vs suppressed.
When to recalibrate
- After hardware changes (new machine, new fixture, retired equipment).
- Quarterly, as a hygiene cycle.
- When the drift detector fires (see below).
20-40 observations per discipline is enough to tighten the environment's intervals materially.
Drift detection
ARCNM watches the live stream of quotes against any realised target prices you push back. When the model and reality diverge:
- The dashboard surfaces a
Driftbadge on the environment. - A re-calibration run is recommended.
Prediction intervals
Every resolved parameter carries a prediction interval. The interval widens when the evidence is thin and tightens as data accumulates:
{
"labour_rate_eur_per_hour": {
"value": 38.50,
"interval": [37.10, 39.90]
}
}
The cost itself comes with a quote-level interval (alongside the
unit_cost on the calculation result):
{ "unit_cost": 12.84, "currency": "EUR", "interval": [11.95, 13.85] }
Use the interval to:
- Show "± €0.95" on a quote PDF.
- Set a quote-buffer for a high-confidence margin.
- Trigger a re-quote when the interval is too wide for the customer.
Where platform + environment evidence both apply, ARCNM reports a conservative interval rather than the tightest one it could. You'd rather your quote say "± €1.20" than under-promise tightness that doesn't exist.
See also
- Concepts → Extraction — what feeds the cost model.
- Concepts → Cost and lot-size — where rates land.
- Concepts → Audit & provenance — every source visible.
- API → Calibration — every calibration endpoint.
- Recipe → Teach from your actuals — calibrate over the API.