Surfaces & Experiences

Visualisation primitives (charts, cards, tables, gauges) and composite pages (dashboards, briefings, reports, stories). Data contracts per surface type.

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.), a config describing how to render it, and data containing what to render.
  • Experiences — Composite pages that compose multiple surfaces. Types include dashboard, briefing, report, and story.
  • Layout — Experiences have a layout configuration (column count, section structure) and a surfaceIds array 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 via templateId.
  • Provenance — Every surface links back to its source: an inferenceId, agentRunId, or feedId.

Data Model

surfaces

ColumnTypeNotes
iduuid (PK)
spaceIduuid (FK → spaces)Owning space
inferenceIduuid (FK → inferences)The inference this surface visualises
agentRunIduuid (FK → agent_runs)The agent run that produced this surface
feedIduuid (FK → feeds)The feed context
surfaceTypetextOne of: chart, card, table, gauge, metric, trend, comparison, distribution, timeline
titletextDisplay title
descriptiontextWhat this surface shows
configjsonbRendering configuration (axes, colours, thresholds, etc.)
datajsonbThe data to display
createdBytextDefault "system". Who/what created this surface
statustextDefault "active". Current status
createdAttimestamp
updatedAttimestamp

experiences

ColumnTypeNotes
iduuid (PK)
spaceIduuid (FK → spaces)Owning space
nametextDisplay name
experienceTypetextOne of: dashboard, briefing, report, story
descriptiontextWhat this experience shows
icontextDisplay icon
layoutjsonbDefault {columns: 2}. Grid/section configuration
surfaceIdsjsonbDefault []. Ordered list of surface IDs to display
filtersjsonbActive filters (time range, categories, etc.)
feedIdsjsonbDefault []. Feeds this experience draws from
timeRangetextDefault "7d". Time window for data
sectionsjsonbDefault []. Named sections for grouping surfaces
isTemplatebooleanWhether this experience is a reusable template
templateIduuidThe template this experience was created from
createdBytextDefault "user". Who/what created this experience
createdAttimestamp
updatedAttimestamp

story_metadata

ColumnTypeNotes
iduuid (PK)
experienceIduuid (FK → experiences)The story experience
spaceIduuid (FK → spaces)
situationtextSCR: What is the current state?
complicationtextSCR: What has changed or gone wrong?
questiontextSCR: What needs to be decided?
resolutiontextSCR: What should be done?
pyramidjsonbMinto pyramid structure
storylinejsonbNarrative flow definition
strategyjsonbPresentation strategy
sourceTypetextWhat generated this story
sourceIdtextReference to the source entity
audiencetextDefault "executive". Target audience
formattextDefault "deck". Output format
hinttextUser guidance for story generation
qualityScorenumericQuality assessment score
qualityIssuesjsonbIdentified quality problems
statustextGeneration status
versionintegerVersion number
initiatedBytextWho triggered generation
createdAttimestamp
updatedAttimestamp

How It Works

  1. Agent run produces inferences — Each inference includes surfaceHints suggesting how it should be visualised.
  2. Surface generation — The surface agent reads inferences and their hints, then generates surface records with appropriate surfaceType, config, and data.
  3. Experience assembly — The experience generation service composes related surfaces into an experience, choosing a layout and ordering based on the experience type.
  4. 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.
  5. Rendering — The frontend reads the experience's layout, sections, and surfaceIds, then renders each surface according to its surfaceType and config.

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

FileDescription
packages/db/src/schema/surfaces.tsSurfaces table definition
packages/db/src/schema/experiences.tsExperiences table definition
packages/db/src/schema/stories.tsStory metadata table definition
apps/data-plane/src/services/surface-agent.tsSurface generation from inferences
apps/data-plane/src/services/experience-agent.tsExperience composition service
apps/data-plane/src/services/story-assembler-agent.tsSCR story assembly
apps/data-plane/src/routes/surfaces.tsSurfaces API routes

Relationships

  • Spaces — All surfaces, experiences, and stories are scoped to a space
  • Inferences & Signals — Surfaces visualise inferences; inferenceId links back
  • Agents — Surfaces can be linked to agent runs via agentRunId
  • Feeds — Experiences draw from feeds; surfaces can be scoped to a feed

Making the unknown, known.

© 2026 Condelo. All rights reserved.