Overview
Surfaces are the atomic visualisation units in Condelo. Each surface represents a single visual element — a chart, a metric card, a data table, a gauge — carrying both the configuration for how to render it and the data to display. Surfaces are generated from inferences and agent runs, translating analytical findings into visual artefacts.
Experiences are composite pages that arrange multiple surfaces into coherent layouts. A dashboard experience might combine metric cards, trend charts, and a summary table into a two-column grid. A briefing experience might arrange surfaces into a narrative flow. Stories are a special experience type that follow the Situation-Complication-Question-Resolution (SCR) framework for structured analytical narratives.
Key Concepts
- Surfaces — Atomic visualisation units. Each has a
surfaceType(chart, card, table, gauge, etc.), aconfigdescribing how to render it, anddatacontaining what to render. - Experiences — Composite pages that compose multiple surfaces. Types include
dashboard,briefing,report, andstory. - Layout — Experiences have a
layoutconfiguration (column count, section structure) and asurfaceIdsarray that determines which surfaces appear and in what order. - Stories — A specialised experience type with narrative metadata following the SCR framework (situation, complication, question, resolution).
- Templates — Experiences can be marked as templates (
isTemplate: true) and reused viatemplateId. - Provenance — Every surface links back to its source: an
inferenceId,agentRunId, orfeedId.
Data Model
surfaces
| Column | Type | Notes |
|---|---|---|
id | uuid (PK) | |
spaceId | uuid (FK → spaces) | Owning space |
inferenceId | uuid (FK → inferences) | The inference this surface visualises |
agentRunId | uuid (FK → agent_runs) | The agent run that produced this surface |
feedId | uuid (FK → feeds) | The feed context |
surfaceType | text | One of: chart, card, table, gauge, metric, trend, comparison, distribution, timeline |
title | text | Display title |
description | text | What this surface shows |
config | jsonb | Rendering configuration (axes, colours, thresholds, etc.) |
data | jsonb | The data to display |
createdBy | text | Default "system". Who/what created this surface |
status | text | Default "active". Current status |
createdAt | timestamp | |
updatedAt | timestamp |
experiences
| Column | Type | Notes |
|---|---|---|
id | uuid (PK) | |
spaceId | uuid (FK → spaces) | Owning space |
name | text | Display name |
experienceType | text | One of: dashboard, briefing, report, story |
description | text | What this experience shows |
icon | text | Display icon |
layout | jsonb | Default {columns: 2}. Grid/section configuration |
surfaceIds | jsonb | Default []. Ordered list of surface IDs to display |
filters | jsonb | Active filters (time range, categories, etc.) |
feedIds | jsonb | Default []. Feeds this experience draws from |
timeRange | text | Default "7d". Time window for data |
sections | jsonb | Default []. Named sections for grouping surfaces |
isTemplate | boolean | Whether this experience is a reusable template |
templateId | uuid | The template this experience was created from |
createdBy | text | Default "user". Who/what created this experience |
createdAt | timestamp | |
updatedAt | timestamp |
story_metadata
| Column | Type | Notes |
|---|---|---|
id | uuid (PK) | |
experienceId | uuid (FK → experiences) | The story experience |
spaceId | uuid (FK → spaces) | |
situation | text | SCR: What is the current state? |
complication | text | SCR: What has changed or gone wrong? |
question | text | SCR: What needs to be decided? |
resolution | text | SCR: What should be done? |
pyramid | jsonb | Minto pyramid structure |
storyline | jsonb | Narrative flow definition |
strategy | jsonb | Presentation strategy |
sourceType | text | What generated this story |
sourceId | text | Reference to the source entity |
audience | text | Default "executive". Target audience |
format | text | Default "deck". Output format |
hint | text | User guidance for story generation |
qualityScore | numeric | Quality assessment score |
qualityIssues | jsonb | Identified quality problems |
status | text | Generation status |
version | integer | Version number |
initiatedBy | text | Who triggered generation |
createdAt | timestamp | |
updatedAt | timestamp |
How It Works
- Agent run produces inferences — Each inference includes
surfaceHintssuggesting how it should be visualised. - Surface generation — The surface agent reads inferences and their hints, then generates surface records with appropriate
surfaceType,config, anddata. - Experience assembly — The experience generation service composes related surfaces into an experience, choosing a layout and ordering based on the experience type.
- Story assembly — For story-type experiences, the story assembler agent applies the SCR framework: it identifies the situation, complication, question, and resolution, then structures surfaces into a narrative flow.
- Rendering — The frontend reads the experience's
layout,sections, andsurfaceIds, then renders each surface according to itssurfaceTypeandconfig.
Why It Works This Way
Config + Data Separation
Each surface carries both config (how to render) and data (what to render) as separate jsonb fields. This means the rendering layer is stateless — it does not need to fetch additional data or know about the analytical context. A chart surface contains its axis configuration, colour scheme, and the data points to plot. This makes surfaces portable and cacheable.
Surface Types as a Finite Vocabulary
The surfaceType enum (chart, card, table, gauge, metric, trend, comparison, distribution, timeline) gives agents a finite vocabulary for expressing visualisations. Agents do not need to generate arbitrary UI — they choose from a known set of types and populate the corresponding config schema. This constraint makes agent output predictable and renderable.
SCR Framework for Stories
The Situation-Complication-Question-Resolution framework (from Barbara Minto's Pyramid Principle) gives stories a reliable narrative structure. Rather than letting agents generate free-form narratives, the SCR framework ensures every story answers: what is happening, what has changed, what needs to be decided, and what should be done. The pyramid and storyline fields capture the logical structure underlying the narrative.
Templates for Reuse
Marking an experience as isTemplate: true allows it to serve as a blueprint. When a new experience is created from a template, it inherits the layout, sections, and surface configuration — but with fresh data. This is particularly useful for recurring reports where the structure is fixed but the content changes weekly or monthly.
Provenance Chain
Every surface links back to its source via inferenceId, agentRunId, or feedId. Every experience links to its surfaces via surfaceIds and to its feeds via feedIds. This full provenance chain means you can always trace a visualisation back to the analytical finding, the agent run, and ultimately the source documents that produced it.
Code Reference
| File | Description |
|---|---|
packages/db/src/schema/surfaces.ts | Surfaces table definition |
packages/db/src/schema/experiences.ts | Experiences table definition |
packages/db/src/schema/stories.ts | Story metadata table definition |
apps/data-plane/src/services/surface-agent.ts | Surface generation from inferences |
apps/data-plane/src/services/experience-agent.ts | Experience composition service |
apps/data-plane/src/services/story-assembler-agent.ts | SCR story assembly |
apps/data-plane/src/routes/surfaces.ts | Surfaces API routes |
Relationships
- Spaces — All surfaces, experiences, and stories are scoped to a space
- Inferences & Signals — Surfaces visualise inferences;
inferenceIdlinks back - Agents — Surfaces can be linked to agent runs via
agentRunId - Feeds — Experiences draw from feeds; surfaces can be scoped to a feed