Chapter 13
Drift attribution
When a score changes over time, drift attribution tells you which layer of your system the change came from. It answers the question every operator asks after a moved number: is this us, or is this the vendor, or is this our evaluation, or is this just noise?
What it is
A per-surface analysis over a window that returns a timeline of change points. Each change point carries three things: the timestamp of the shift, one of four source labels, and the window over which the analysis is confident about that label.
The four source labels are the same four sources of drift the four-layer snapshot separates at capture time:
- substrate — your own deployment
- light source — the vendor moved under you
- lens — your evaluation configuration changed
- noise — the model's irreducible non-determinism
Why it matters
Without attribution, a score move is a mystery — the operator has to guess at the cause, and every guess pulls the team in a different direction. With attribution, the same score move is a labeled event with a first suggestion for what to do next. The whole point is to route the response to the layer that can act on it.
How to use it
Point the analysis at a surface and a window:
POST /lens/attribute
Authorization: Bearer <token>
x-holonograph-run-mode: production
Content-Type: application/json
{
"surfaceId": "classify-intent",
"since": "2026-06-01T00:00:00Z",
"until": "2026-07-05T00:00:00Z",
"bucket": "day",
"changePointDeltaThreshold": 0.15
}
The response is the timeline plus a couple of companion arrays:
{
"surfaceId": "classify-intent",
"eventCount": 12483,
"changePoints": [
{
"timestamp": "2026-06-14T09:20:00Z",
"source": "light source",
"window": { "from": "2026-06-12T00:00:00Z", "to": "2026-06-15T00:00:00Z" }
}
],
"bracketedWindows": [ /* companion windows the analysis wants you to know about before acting */ ]
}
bucket controls the temporal resolution of the timeline. changePointDeltaThreshold is a sensitivity knob: higher = fewer points. Both are optional; the defaults are usually what you want.
Reading the output
Every change point comes with one of the four source labels. Do this with each:
| Label | What it means | What to do |
|---|---|---|
substrate |
The change tracks something you changed about your deployment. | Check your own deployment: recent prompt edits, tool-schema revisions, feature flags, retrieval-index updates. Your substrate columns (Chapter 04) are where you look first. |
light source |
The vendor moved under you — a model was silently re-routed, updated, or its behavior shifted. | Check light_source_identifier across the window. If it's stable and the behavior still moved, the vendor changed the model behind an unchanged name. |
lens |
Your evaluation configuration changed. | Check the lens version history. A new lens version around the change point means you're seeing the apparatus move, not the system. |
noise |
The model's irreducible non-determinism dominates this window. | Gather more data before acting. A noise-labeled change point is a signal to widen the window, not a signal to ship a fix. |
The bracketedWindows array holds companion windows the analysis wants you to see before acting on a source label — periods where the timeline is less confident than usual. Read those first when a change point lands adjacent to one.
Working through a report
The usual cadence: run the analysis over a window that ends at "yesterday," then walk the change points in order. Each label sends you to a different place — your deploy log, the vendor's changelog, your lens history — and the labeled window tells you where to look. Once every point has an action (or a decision not to act), the report is done.
Reference
Endpoint
| Method & path | Purpose |
|---|---|
POST /lens/attribute | Run drift attribution over a window. |
Request
| Field | Shape | Notes |
|---|---|---|
surfaceId | string (req) | The surface to analyze. |
since | ISO 8601 (opt) | Inclusive lower bound on events considered. |
until | ISO 8601 (opt) | Exclusive upper bound. |
bucket | enum (opt) | day, hour, minute. Temporal resolution of the timeline. |
changePointDeltaThreshold | number 0–1 (opt) | Sensitivity. Higher = fewer change points. |
Response
| Field | Shape | Notes |
|---|---|---|
surfaceId | string | Echo of the surface. |
eventCount | number | How many events were considered. |
changePoints[] | array | Timeline of change points; each has timestamp, source label, and window. |
bracketedWindows[] | array | Companion windows worth reading before acting on nearby change points. |