Report
Definition
A Report is a user-owned, pointer-only saved collection of Block references. It stores how to re-fetch each block (the query/tool and its params), not the block data itself — so opening a report re-runs the underlying queries against live sources rather than showing a stale snapshot. This is the deliberate contrast with an Edition: a Report is a lightweight, shareable "saved view" with no sealing, hashing, or attestation; an Edition is the immutable decision record. Reports are for "let me keep and share this set of things I was looking at," not "this is the record of a decision."
Command logic: cortex/cortex/report.py and cortex/cortex/tools/report.py. Persistence: a UDS DAO over the shared
Elasticsearch intelligence index under the report alias, id report-{uuid.hex}. Schema (aspirational):
cortex/server/api/schema/intelligence.yaml:560.
Note a schema-vs-runtime divergence: the OpenAPI Report schema lists insight_ids[], status, shared_with[],
visibility, but the document report_create actually writes (tools/report.py:43) is id, title, description,
blocks[] (the block references), username, visibility — i.e. block references, not insight ids, and no status.
Lifecycle
Minimal — there is no freeze/seal/render state machine. The schema's status enum (draft | published | archived) is
defined but never set or transitioned in code. Real state is just visibility: a Report is created PRIVATE, and
report_share flips it to SHARED (adding users to shared_with[]) or PUBLIC (report.py:83). Public is the
broadest and overrides the share list.
Journey through the code
MCP tools report_create, report_list, report_get, report_share (cortex/cortex/tools/report.py, registered via
cortex.tools.registry). Each calls its command function, which writes
Report().update(with_visibility(strip_none(doc)), uid=report_id) to the report alias; REST exposes /report
(intelligence.yaml:1208). On render, the stored block references are re-fetched from their original sources
(tools/report.py:30). A separate generic export_data (report.py:112) renders arbitrary result rows to
CSV/JSON/HTML/Markdown via cortex/cortex/export/export_utils.py — it takes raw rows, not a report id, so it is not
currently wired to report rendering.
Data shape
Runtime required: id, title, username, visibility. Optional: description, blocks[] (each a reference:
query_hash / tool / params), shared_with[], updated_at. Schema-only / not populated by code: insight_ids[],
status. Storage: intelligence index, report alias; UDS metadata (uds.visibility, create_ts, …) via the base.
Invariants
- Pointer-only — a Report stores block references and re-fetches on render; it never holds frozen block data.
- Ownership-scoped sharing — only the owner may
report_share(tools/report.py:194);PUBLICoverrides the share list.
Related products
product.block— a Report references Blocks (by query, notblock_id) and re-fetches them live.product.edition— distinct from a Report: the Edition is the sealed, attestable decision artifact; the Report is the lightweight shareable pointer collection.product.insight— the schema claims aninsight_ids[]link, but the code does not populate it.
Open questions
- Schema vs runtime golden shape — is the contract
insight_ids[]+status(schema) orblocks[]+ no status (code)? They disagree. - Render path — is there a real report-render/PDF path? None found;
export_datais generic row-rendering, not wired to a report id. - Dead
statuslifecycle and an ownership check inreport.pythat is a documented no-op (enforced only in thetools/report.pyvariant).
Realized by: component.cortex.intelligence