Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 161 additions & 0 deletions docs/components/backend/analytics/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -911,6 +911,48 @@
],
"type": "object"
},
"MetricDrilldownExportFormat": {
"enum": [
"csv",
"xlsx"
],
"type": "string"
},
"MetricDrilldownExportRequest": {
"properties": {
"display_dimensions": {
"items": {
"type": "string"
},
"type": "array"
},
"entity": {
"$ref": "#/components/schemas/MetricDrilldownEntity"
},
"filters": {
"items": {
"$ref": "#/components/schemas/MetricDrilldownFilter"
},
"type": "array"
},
"format": {
"$ref": "#/components/schemas/MetricDrilldownExportFormat"
},
"metric_key": {
"type": "string"
},
"period": {
"$ref": "#/components/schemas/MetricDrilldownPeriod"
}
},
"required": [
"metric_key",
"entity",
"period",
"format"
],
"type": "object"
},
"MetricDrilldownFilter": {
"properties": {
"dimension": {
Expand Down Expand Up @@ -3380,6 +3422,125 @@
"summary": "List metric evidence"
}
},
"/v1/metric-drilldown/export": {
"post": {
"operationId": "analytics_api.metric_drilldown.export",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/MetricDrilldownExportRequest"
}
}
},
"description": "Metric evidence export selection",
"required": true
},
"responses": {
"200": {
"content": {
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": {
"schema": {
"format": "binary",
"type": "string"
}
},
"text/csv": {
"schema": {
"format": "binary",
"type": "string"
}
}
},
"description": "Complete metric evidence export",
"headers": {
"Content-Disposition": {
"description": "Attachment filename",
"schema": {
"type": "string"
}
}
}
},
"400": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Bad Request"
},
"401": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Unauthorized"
},
"403": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Forbidden"
},
"404": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Not Found"
},
"409": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Conflict"
},
"429": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Too Many Requests"
},
"500": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Internal Server Error"
}
},
"security": [
{
"bearerAuth": []
}
],
"summary": "Export metric evidence"
}
},
"/v1/metric-results": {
"post": {
"operationId": "analytics_api.metric_results.create",
Expand Down
27 changes: 21 additions & 6 deletions docs/domain/metrics/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ anywhere: at request time the runtime applies a definition to the matching
observations — "alice, January: 312 ÷ 405 = 77%" — and returns it labeled and
ready to render.

Metric **evidence** is the source-level population behind that answer. Each
managed source exposes a normalized serving table.
Definitions inherit drilldown support when all compatible inputs are backed by
the same validated evidence relation, so adding a metric over an existing
measure requires no drilldown-specific configuration.
The backend applies the same entity, period, dimension, input-role, and
computation semantics used by metric results.

The split exists because one fact serves many meanings and one meaning serves
many questions. The same `accepted_edit_actions` observation is the whole
value of `ai.accepted_edit_actions` and the numerator of
Expand All @@ -43,12 +51,12 @@ in that shape, definitions reference facts only by measure key, and the
runtime can therefore connect any definition to any matching facts without
either side knowing the other exists.

| | Observation | Definition | Metric result |
|---|---|---|---|
| What | a fact | the meaning of facts | the computed answer |
| Lives | ClickHouse views over silver (computed on read, nothing stored) | registry (MariaDB, seeded from Rust) | nowhere — made per request |
| Knows | what happened | what it is called, how to compute, how to show | both, combined |
| Authored by | connector + gold model | one struct per metric | nobody — the runtime derives it |
| | Observation | Evidence | Definition | Metric result |
|---|---|---|---|---|
| What | an aggregate-ready fact | the participating source population | the meaning of facts | the computed answer |
| Lives | ClickHouse views or tables over silver | ClickHouse serving tables | registry | nowhere |
| Knows | what happened numerically | which records participated | how to compute and display | all three, combined |
| Authored by | connector + gold model | connector + gold model | one struct per metric | nobody |

## Documents

Expand All @@ -64,12 +72,19 @@ either side knowing the other exists.
| Definition loading, reconciler, schema validator | [`src/backend/services/analytics/src/domain/metric_definitions/`](../../../src/backend/services/analytics/src/domain/metric_definitions/) |
| Result runtime (validation, query compiler, response builder) | [`src/backend/services/analytics/src/domain/metric_results/`](../../../src/backend/services/analytics/src/domain/metric_results/) |
| Result endpoint | [`src/backend/services/analytics/src/api/metric_results.rs`](../../../src/backend/services/analytics/src/api/metric_results.rs) |
| Drilldown runtime | [`src/backend/services/analytics/src/domain/metric_drilldown/`](../../../src/backend/services/analytics/src/domain/metric_drilldown/) |
| Drilldown endpoints | [`src/backend/services/analytics/src/api/metric_drilldown.rs`](../../../src/backend/services/analytics/src/api/metric_drilldown.rs) |
| Registry schema migration | [`src/backend/services/analytics/src/migration/m20260625_000001_metric_definitions.rs`](../../../src/backend/services/analytics/src/migration/m20260625_000001_metric_definitions.rs) |
| Managed observation sources (dbt gold models) | [`src/ingestion/gold/`](../../../src/ingestion/gold/) |
| Class-contract data-quality tests | [`src/ingestion/dbt/tests/ai/`](../../../src/ingestion/dbt/tests/ai/) |

## Boundaries

- Current deployments isolate one tenant per instance. Drilldown entity IDs
remain source-derived identifiers, commonly normalized email addresses.
Multi-tenant warehouse predicates, canonical person IDs, cross-source alias
resolution, and subordinate authorization belong to the identity-resolution
epic and are required before a multi-tenant instance enables drilldown.
- The AI class contracts feeding the observation models are documented in
[`src/ingestion/silver/ai/schema.yml`](../../../src/ingestion/silver/ai/schema.yml)
(activity invariant, label and conversation-count semantics).
Expand Down
Loading
Loading