---
title: Audit & provenance
description: Every quote is re-derivable. Audit blob, provenance chain, conformal intervals, immutability.
---

# Audit and provenance

ARCNM quotes are **defensible**. Every cost field traces back to:

- **Which input** the feature came from (3D geometry, drawing, or both).
- **Which process** the time estimate was computed for (milling,
  turning, sheet-metal forming, etc.).
- **Which source** supplied the rate (manual override, parent
  environment, environment calibration, platform default).
- **Which calibration run** produced the active coefficients, on
  what evidence, at what timestamp.

When a procurement team asks "how did this part get to €12.84?", the
audit blob answers in 30 seconds, not a week.

---

## The audit blob

`GET /api/v1/parts/calculations/{id}` returns the result + a deep
`analytics` object. The audit blob lives at:

```
analytics.cost_pipeline
```

with one section per stage that ran (`extraction`, `drawing`,
`fusion`, `planning`, `physics`, `cost_decomposition`).

> The `analytics` blob is free-form and its internal field names are
> illustrative — treat it as informational and don't hardcode the exact
> shape. The **stable** top-level fields (`unit_cost`, `total_cost`,
> `setup_cost`, `currency`, `status`, timestamps) are documented in the
> [Calculations reference](../api/calculations.md#get-calculation).

### Top-level structure

```json
{
  "id": "9d3f…",
  "status": "succeeded",
  "unit_cost": 12.84,
  "currency": "EUR",
  "analytics": {
    "cost_pipeline": {
      "engine_version":  "2026-05-21",
      "schema_version":  "v2",
      "wallclock_s":     32.4,
      "stages": {
        "extraction":         { /* … */ },
        "drawing":            { /* … */ },
        "fusion":             { /* … */ },
        "planning":           { /* … */ },
        "physics":            { /* … */ },
        "cost_decomposition": { /* … */ }
      },
      "provenance": { /* per-parameter resolved-source map */ },
      "intervals":  { /* conformal intervals at each stage */ }
    }
  }
}
```

---

## Per-stage extracts

### Extraction (`stages.extraction`)

```json
{
  "envelope_mm":  { /* … */ },
  "features":     [ /* … */ ],
  "dfm_issues":   [ /* … */ ],
  "material_classification": { "iso_group": "P" },
  "wallclock_s":  4.1
}
```

### Drawing (`stages.drawing`)

Carries the typed `DrawingExtraction` + per-field confidence +
validation report.

```json
{
  "extraction_tier":          "arcnm",
  "extraction_tier_display":  "ARCNM",
  "wallclock_s":              21.0,
  "drawing_extraction":       { /* typed DrawingExtraction */ },
  "validation_report": [
    {
      "rule_id":          "R-ISO286-001",
      "severity":         "confidence_adjust",
      "field_path":       "pmi_supplements[3]",
      "message":          "Tolerance pair (+25.0/-0.0 µm) disagrees with ISO 286 lookup (+21/0 µm) for j6.",
      "value":            [25.0, 0.0],
      "expected":         [21, 0],
      "clause":           "ISO 286-1 §A",
      "confidence_factor": 0.5
    }
  ],
  "self_correction": [ /* per-field retry log */ ]
}
```

Validation `rule_id`s are stable: `R-ISO286-001/002`, `R-ISO2768-001/002`,
`R-Y14_5-001..010`, `R-TB-001/002/003`, `R-CHM-001`, `R-RAD-001`,
`R-Ra-001/002/003`, `R-THD-001/002`, `R-MAT-001`. Switch on them in
your downstream pipelines.

### Physics (`stages.physics`)

For each operation, the cost inputs the engine consumed (each resolved
through the [environment cascade](./calibration-environments.md)) and
the times produced:

```json
{
  "operations": [
    {
      "operation_id":  "op_a8…",
      "process":       "drilling",
      "feature_id":    "feat_92…",
      "inputs": {
        "tool_diameter_mm":          8.0,
        "feed_rate_mm_per_rev":      { "value": 0.18, "source": "platform_default" },
        "cutting_speed_m_per_min":   { "value": 35.0, "source": "environment_override" }
      },
      "outputs": {
        "setup_time_s":   42.0,
        "cycle_time_s":   18.5,
        "tool_life_units": 850
      }
    }
  ]
}
```

### Cost decomposition (`stages.cost_decomposition`)

```json
{
  "decomposition": { /* per-line-item costs — see Cost concepts */ },
  "lot_size_curve": { /* points across batch sizes */ }
}
```

The conservative tenant-specific adjustment that calibration applies is
recorded here too, with its supporting evidence, so every quote stays
auditable.

---

## Provenance map

`analytics.cost_pipeline.provenance` is a flat map from parameter path
to the source that resolved it:

```json
{
  "milling.feed_rate_mm_per_rev":      "environment_calibration",
  "milling.cutting_speed_m_per_min":   "environment_override",
  "labour_rate_eur_per_hour":          "platform_default",
  "machine_rate_eur_per_hour.haas_vf2":"environment_override",
  "material.steel.1.0577.eur_per_kg":  "environment_catalogue"
}
```

Run `jq` on it to assert at deploy time:

```bash
arc calc get $CALC | jq '.analytics.cost_pipeline.provenance | with_entries(select(.value == "platform_default"))'
# Empty list = every parameter is calibrated, no defaults leaking in.
```

---

## Immutability

Every successful calculation row is **append-only**:

- The audit blob is stored as-is in versioned, lifecycle-managed
  object storage.
- The row's `analytics` field is read-only after the worker writes it.
- Re-running the same `part_revision_id + costing_environment_id +
  lot_size + material` is **not idempotent** — it creates a new
  Calculation row with a new id, so calibration changes are visible.
- The previous row stays accessible by id forever.

For "the price as it was on 2026-05-28" queries, persist the
`calculation_id` in your ERP and re-fetch from us — we keep them.

---

## Conformal intervals

Three levels of interval:

| Level | Field | Width depends on |
|---|---|---|
| Per-parameter | `inputs.<key>.interval` | Calibration sample count + spread |
| Per-operation | `outputs.<key>.interval` | Propagated from the input intervals it depends on |
| Quote-level | `unit_cost_interval_eur` | Combined platform + environment evidence (conservative) |

Where platform and environment evidence both apply, ARCNM reports a
**conservative** interval rather than the tightest one it could — by
design (see
[Calibration → Conformal intervals](./calibration-environments.md#conformal-intervals)).

---

## Compliance & retention

- **HGB §257 / GoBD:** 10-year retention on audit blobs for German
  customers.
- **GDPR:** no personal data in the audit blob by construction; only
  CAD-derived geometry and pricing math.
- **SOC 2:** audit entries are part of the SOC 2 control set. Read them
  via the `audit` API with the `audit:read` scope.

---

## Exporting to your data warehouse

The `audit` API returns audit rows with **cursor pagination** — page
through them and drop into Snowflake / BigQuery / Postgres for cost
analytics. Requires the `audit:read` scope; narrow the stream with the
available action and resource-type filters.

---

## See also

- [Calculations](../api/calculations.md) — the `analytics` blob is
  returned by `GET /parts/calculations/{id}`.
- [Concepts → Calibration](./calibration-environments.md) — where the
  provenance sources come from.
- [API → Calculations](../api/calculations.md) — fetching the full blob.
