Dashboard authoring and promotion
Use this workflow when a dashboard change must move from a safe project draft to a production deployment. It defines the ownership, environment, source-control, and lifecycle boundaries for file, UI, and agent authors. The browser builder, headless authoring API, and dashboard-authoring agent tools all use the same draft, revision, authorization, and promotion rules.
An unpublished draft is visible to its author and users with authorized editing access. Draft status keeps it out of published dashboard delivery; it does not restrict access to the author alone.
Before you begin
Choose a permanent development boundary and a production boundary. Use separate LeapView instances for development and production; each instance is permanently bound to one environment and one active project generation. This workflow does not create temporary environments.
| Boundary choice | Isolation | Cost and operations | Decision |
|---|---|---|---|
| Separate development and production instances | Strongest: distinct instance identities, targets, credentials, storage, and failure domains | Higher infrastructure and operator cost; promotion crosses an explicit target boundary | Preferred for production isolation |
Create separate credentials for development and production. A recommended permission model is:
| Principal | Development | Production |
|---|---|---|
| Author | Edit private drafts, preview, and publish on the development instance | No edit, publish, or deploy grant on the production instance |
| QA/verifier | Read and test the development publication with representative grants | Read-only verification when assigned |
| Production deploy service account | No access unless a separate, explicitly scoped account is needed | Deploy the approved full project; no interactive authoring |
| Viewer | Read only | Read only |
| Operator | Administer the instance and recover it | Administer the instance and recover it; use a separately audited identity |
Use distinct development and production service principals. Target identity and credentials provide the first boundary; project-resource grants remain the second.
Understand ownership and status
Dashboards are project assets whether their visibility is private, restricted, or organization-wide. The interactive builder must select a governed semantic model from that project graph. Semantic-model usage, visibility counts, and publication state remain observable and governable in catalog, audit, and access surfaces.
File-authored, UI-authored, and agent-authored are origins. Origin is provenance, not ownership: it must not decide who can edit, publish, or deploy. Access and RBAC grant those actions to principals over a project and its assets. Anonymous publication is a separate, explicit publication capability; it does not transfer dashboard ownership or authoring rights.
Treat common UI badges as projections of these separate facts:
| Badge or concept | Meaning | Does not mean |
|---|---|---|
| Private / Restricted / Organization | The dashboard's project visibility | A different owner or a different semantic model |
| Draft | A mutable project draft that can be edited by an authorized principal | A production deployment or an immutable version |
| Published | A selected immutable dashboard revision is available through the project publication path | That the project is deployed to production |
| Archived | The dashboard lifecycle no longer accepts normal publication | That its historical revision or deployment evidence was deleted |
| File / UI / Agent | How the current authored content entered LeapView | Who owns the asset or who may deploy it |
The lifecycle layers are intentionally different:
- A draft is the mutable project-owned editing record. Saving or applying edits may point it at a new revision.
- A dashboard revision is an immutable, complete copy of one dashboard document. Publishing selects a revision; it does not mutate one that has already been published.
- Compiled serving-state generations are immutable full-project deployments for an environment. Each includes the compiled project graph, semantic, dashboard, access, policy, connection, and managed-data decisions needed to serve requests.
- A DuckLake data snapshot is an analytical consistency and retention boundary used by runtime queries. It is not a dashboard revision, a draft fork, or a customer-facing dashboard version.
Do not use a data snapshot, virtual schema, or automatic fork of an underlying model as a dashboard authoring mechanism.
Follow the normal change flow
- Fork or copy the published dashboard into a private draft. Keep the published dashboard stable while the change is being developed.
- Iterate in the draft. UI, file, and agent edits are equivalent origins. Select and reuse the existing governed semantic model for dashboard-only work; do not fork the model automatically.
- Validate and preview. Validate the project and inspect the draft with the author's effective grants. Check query results, filters, empty and failure states, layout, and accessibility. Use the existing dashboard creation guide for resource details and develop, review, and publish for CLI gates.
- Publish to development. Publish the draft on the development instance. Have QA exercise the published result with representative viewer and row-policy identities.
- Export canonical YAML. The export is the reviewable project representation. If Git is enabled, commit it to the user's repository and review the change through the user's Git provider. Merge only after the project and dashboard checks pass.
- Deploy the full project to production. A dedicated production, project-scoped deploy service account or CLI deploys the approved merged project. The production deployment is atomic across the resources in that project.
For a dashboard-only change, reuse the already approved semantic model and deploy the dashboard change with that model reference. If model, semantic-model, access-policy, or dashboard changes must move together, validate and QA the complete project in development, then deploy that same full project to production. Do not suggest snapshots, virtual schemas, or automatic model forks to avoid coordinating those changes.
Use the browser dashboard builder
Open the authenticated edit route:
GET /dashboards/{dashboard}/edit
The route requires dashboard edit access. It resolves the repository's current draft pointer and exact retained revision on the server, then streams a typed builder projection. The page shows the governed semantic-model fields, pages, visuals, source origin, visibility, lifecycle, revision number, save state, diagnostics, and source evidence. It never accepts a client-supplied document as authority.
An authenticated owner with current dashboard.update authority may permanently discard their own private draft that has never been published. Ownership alone is insufficient. This owner policy does not grant dashboard.delete, change built-in roles, or allow archiving published dashboards. API credentials still require explicit dashboard.delete authority within their permission ceiling. Delete is checked again against current authorization and durable lifecycle state when the command executes or replays.
The browser builder is a closed command surface. Each row below is a browser command emitted by the current builder and translated by the server into a typed authoring payload. The last column is part of the contract: values not listed as editable remain unchanged when that command is applied, or are intentionally code-only.
| Browser surface | Command | Browser-editable properties | Preserved or code-only properties |
|---|---|---|---|
| Dashboard appearance | update_appearance |
Dashboard icon and color | Dashboard title, description, slug, semantic model, visibility, pages, visuals, and advanced appearance metadata remain unchanged or code-only |
| Dashboard visibility | set_visibility |
private or organization visibility |
Dashboard document, lifecycle revision history, and restricted/access policy details remain server-owned or code-only |
| Dashboard metadata | update_dashboard_metadata |
Dashboard title and description | Slug, semantic model, visibility, icon/color, pages, visuals, and advanced metadata remain unchanged or code-only |
| Publish | publish |
The exact current draft revision selected by the browser | Draft bytes, revision history, publication evidence, and serving deployment remain server-owned; publish does not deploy a full project |
| Archive | archive |
Dashboard lifecycle state | Retained revisions, authored source, and evidence remain preserved; archive does not delete the document |
| Delete | delete |
The current private draft dashboard | Dashboard content and retained revisions are permanently removed; only audit and idempotency evidence remain; published or restricted dashboards cannot be deleted |
| Add page | add_page |
Page title and optional page ID | The server allocates missing IDs; description, grid, components, and advanced page metadata use canonical defaults or remain code-only |
| Rename page | rename_page |
Page title | Page ID, description, grid, components, and page bindings remain unchanged; description is code-only here |
| Page metadata | update_page_metadata |
Page description; the current title is carried to preserve it | Page ID, grid, components, bindings, and advanced page metadata remain unchanged or code-only |
| Duplicate page | duplicate_page |
Source page and optional duplicate title or ID | The server clones authored content and allocates collision-free visual/component IDs; the source page remains unchanged |
| Reorder page | move_page |
Page order index | Page identity, title, description, grid, components, and visual definitions remain unchanged |
| Page grid | update_page_layout |
Columns, row height, gap, and padding | Page title, description, components, canvas geometry, and visual/filter properties remain unchanged or code-only |
| Remove page | remove_page |
The selected page | Other pages, dashboard metadata, visual definitions not removed by canonical cleanup, and revision history remain preserved |
| Add visual | add_visual |
Target page, visual type, title, and optional governed field plus role | The server allocates IDs and canonical query/presentation defaults; arbitrary query expressions, advanced presentation, and interactions are code-only |
| Change visual type | set_visual_type |
Target visual and visual type | The server resolves compatible governed bindings; unsupported query-family options and advanced presentation settings remain code-only or are reset by the target family |
| Duplicate visual | duplicate_visual |
Source visual, optional title, and optional IDs | The server clones the authored definition and allocates collision-free IDs; source visual fields, formatting, filters, and interactions remain unchanged |
| Remove visual | remove_visual |
Selected visual placement/definition | Other visuals, page layout, filters, interactions, and dashboard metadata remain unchanged |
| Move or resize components | set_placements |
Selected page component positions and spans | Visual queries, formatting, filter definitions, interactions, and all untouched component placements remain unchanged |
| Add governed field | assign_field |
Target visual, semantic field ID, and metric, dimension, or detail role |
The active semantic model validates the field; arbitrary physical fields, expressions, query options, and presentation remain code-only |
| Remove governed field | remove_field |
Target visual, field ID, and role | Remaining field selections, query family, formatting, filters, and interactions remain unchanged; required scalar bindings cannot be removed |
| Reorder governed field | move_field |
Field order within a compatible role | Cross-role conversion, semantic meaning, query expressions, and physical bindings remain server-governed or code-only |
| Visual query options | set_visual_query_options |
Selected field alias, temporal grain, bounded sort, and result limit | Semantic field identity, query family, calculations, filters, interactions, and unexposed query options remain unchanged or code-only |
| Visual formatting | update_visual_format |
Visual title, title visibility, and catalog-provided renderer-neutral format options | Query/semantic selections, interactions, and presentation options not exposed by the catalog remain unchanged or code-only |
| Add report filter | add_filter |
Governed dimension, label, option dataset, and control type | Filter defaults, operators, targets, advanced option settings, and physical expressions use canonical defaults or remain code-only |
| Add filter slicer | add_slicer |
Governed dimension, label, option dataset, control type, and target page | The server atomically creates the filter and page component with generated IDs; defaults, operators, targets, and advanced filter settings remain code-only |
| Update filter | update_filter |
Label, description, option dataset, control type, required, reader-editable, and URL parameter | Authored defaults, operators, targets, and advanced option settings are deliberately preserved |
| Set filter scope | set_filter_scope |
Report/page scope and optional visual targets | Filter defaults, operators, reader-editable policy, and advanced options remain unchanged |
| Remove report filter | remove_filter |
Selected filter definition | Other filters, visuals, pages, and dashboard metadata remain unchanged; canonical cleanup handles its placements |
| Place existing filter | add_filter_component |
Target page, filter ID, and optional component ID | Filter definition and its defaults/options remain unchanged; the server allocates missing component IDs |
| Remove filter slicer | remove_filter_component |
Selected page component | The underlying filter definition, defaults, targets, and other placements remain unchanged |
| Set visual interaction target | set_interaction_target |
Source visual, target visual, and filter, highlight, or none effect |
Existing governed mappings and unsupported/spatial interaction forms remain server-owned or code-only |
| Header metadata | update_header_metadata |
Selected header title and description | Header ID, placement, kind, page identity, and unsupported header properties remain unchanged or code-only |
| Undo or redo exact revision | restore_revision |
Exact retained target revision selected by browser history | Restore appends a new revision; the target and intervening revisions remain retained, and no implicit “latest” lookup is allowed |
Field assignment is validated against the active governed semantic model before the edit is appended. The browser also has local-only controls (theme, canvas zoom, pane collapse, field search, and selection); they do not create authoring revisions. Preview, export, and Make a copy are links or read operations, not builder commands. The server accepts a few additional typed actions for headless/API callers, including set_filter_targets and rename_visual; they are not browser actions today. Arbitrary YAML patches, model edits, and properties marked code-only above require the project authoring guides or the headless/agent surfaces.
Every builder mutation carries the dashboard ID, draft ID, and a complete expected revision token (revisionId, number, and contentHash). The browser supplies a request ID (X-Request-ID, with Idempotency-Key as a fallback); the authenticated principal is the actor. A successful intent appends one immutable revision, increments the revision number, updates the draft pointer, and leaves an already published revision unchanged. Replaying the same command identity and fingerprint returns the recorded result without appending a second revision. A token that is no longer current returns a stale-revision conflict; reload the current draft and submit the intent again.
Preview, publish, and export exactly what was reviewed
The Preview link identifies one exact draft revision. It includes the draft ID, page ID, revision ID, revision number, and content hash; there is no implicit “latest” revision. The browser route is:
GET /dashboards/{dashboard}/preview?draft={draft}&page={page}&revisionId={id}&revisionNumber={number}&revisionContentHash={hash}
Preview compiles that retained document against the active governed runtime and returns the definition, page patch, semantic-model/runtime identity, exact serving identity (projectId, environment, and generationId), and DuckLake snapshot evidence. Preview does not change the draft, publish the dashboard, deploy a serving generation, or mutate data.
Publish is a typed command against the same complete expected revision. LeapView compiles the draft with its governed semantic model, stores the compiled revision and publication evidence, and moves the dashboard lifecycle to published. Publishing does not deploy a full project to production; use the development publication and full-project promotion gates described above.
Export YAML downloads the canonical authored source without changing lifecycle state:
GET /dashboards/{dashboard}/export.yaml
The export is reviewable project YAML. A retained project source resolves the current lifecycle draft by its dashboard ID; an active project source is the serving artifact with serving-state/path evidence. Neither export turns a compiled artifact into a fabricated authoring revision.
Use the headless dashboard-authoring API
The generated API is rooted at /api/v1/projects/{project}/authoring. Authenticate with a scoped principal and use IDs returned by the catalog; do not infer identity from titles. The generated OpenAPI contract is authoritative for field types and status codes.
| Method and path | Purpose and access |
|---|---|
GET /api/v1/projects/{project}/authoring/catalog |
List governed dashboard identities (RESOURCE_READ). |
GET /api/v1/projects/{project}/authoring/dashboards/{dashboard} |
Read one dashboard summary (RESOURCE_READ). |
GET /api/v1/projects/{project}/authoring/dashboards/{dashboard}/draft |
Read the current private draft, lifecycle pointer, document, and exact revision (RESOURCE_EDIT). |
GET /api/v1/projects/{project}/authoring/dashboards/{dashboard}/drafts/{draft}/revisions/{revision} |
Read that exact current draft revision (RESOURCE_EDIT); the draft and revision path values must match. |
GET /api/v1/projects/{project}/authoring/dashboards/{dashboard}/revisions/{revision} |
Read that exact published revision (RESOURCE_READ); this path never means “latest draft.” |
POST /api/v1/projects/{project}/authoring/drafts |
Create one named private draft. |
POST /api/v1/projects/{project}/authoring/commands |
Apply one closed command: one builder intent, publish, or archive. |
POST /api/v1/projects/{project}/authoring/forks |
Fork a retained project source into a new private draft. |
POST /api/v1/projects/{project}/authoring/dashboards/{dashboard}/drafts/{draft}/preview |
Preview one exact revision and page. |
GET /api/v1/projects/{project}/authoring/sources/{kind}/{dashboard}/export |
Export canonical YAML for the project source kind. |
POST /drafts, POST /commands, and POST /forks require an Idempotency-Key header (1–200 characters). The key is an operation-idempotency identity, separate from actor and tool-call provenance, and is audited with the authenticated actor; it is not a repository or Git authority. For create and fork, the same key with the same normalized payload durably replays the original draft/result; the same key with a changed payload is a conflict; a different key creates a new draft. Command retries likewise replay only when the same key is reused with the same request fingerprint. Preview, reads, and export do not require an idempotency key.
Command requests must name the exact dashboardId, draftId, and expectedRevision token:
{
"kind": "addVisual",
"dashboardId": "revenue",
"draftId": "draft-7",
"expectedRevision": {
"revisionId": "revision-12",
"number": 12,
"contentHash": "sha256:..."
},
"addVisual": {
"pageId": "overview",
"type": "bar",
"title": "Revenue by month"
}
}
The discriminated command union accepts exactly one payload for its kind: metadata, setVisibility, addPage, addVisual, assignField, upsertPage, removePage, upsertVisual, removeVisual, setLayout, setFilters, setInteraction, publish, or archive. Every successful edit returns the new immutable revision token and the repository-authoritative lifecycle pointer. A stale token is a 409 conflict; read the current draft and reconcile rather than dropping the token or defaulting to a newer revision. Preview requests repeat the complete token in their body, and responses include the same revision plus runtime and snapshot evidence.
Use dashboard-authoring agent tools
The dashboard-authoring subset of built-in chat and MCP contains these twelve tools:
list_dashboards
get_dashboard
get_dashboard_draft
create_dashboard_draft
execute_dashboard_command
fork_dashboard
preview_dashboard_draft
export_dashboard_yaml
set_dashboard_visibility
add_dashboard_page
add_dashboard_visual
assign_dashboard_field
The first three read the governed catalog or exact private draft. create_dashboard_draft and fork_dashboard create private drafts. The four intent tools apply the same visibility, add-page, add-visual, and assign-field union used by the browser. execute_dashboard_command is reserved for exact-revision publish and archive; preview_dashboard_draft requires an exact revision and page; and export_dashboard_yaml returns canonical YAML. Catalog, query, and documentation tools remain separate integration surfaces.
Agent inputs deliberately omit actor and provenance fields. The server binds origin: agent, the authenticated principal as actorId, the active conversation as conversationId, and the tool invocation ID as toolCallId; a model cannot spoof any of them. Agent calls use the invocation ID for both their operation idempotency key and actual tool-call identity. Agent tools enforce the project scope, object privilege, governed semantic-model check, and exact revision token before applying a mutation. Create and fork replay a durable result for the same invocation ID and normalized payload, conflict on a changed payload, and create a new draft for a different invocation ID; intent and lifecycle command retries follow command-ID/fingerprint replay rules. MCP writes are advertised as non-idempotent because MCP assigns a fresh server tool-call ID for each request; durable replay requires an agent caller to reuse the original invocation identity.
Choose a create, fork, or promotion flow
| Situation | Recommended flow |
|---|---|
| New dashboard with an existing governed semantic model | Create a private draft in the active project, use the four bounded intents, preview the exact latest revision, publish on the development instance, then export and promote the full project. |
| Change to an existing published project dashboard | Fork the exact published project revision into a new private draft; keep the source unchanged, iterate, preview, publish in development, and promote the reviewed project. |
| Only an active project artifact is available | Fork the project source into a private draft. Record its serving-state/path evidence, understand that it has no authoring revision token, and continue editing the retained project source when one is available. |
| Dashboard and model, policy, or semantic changes must move together | Validate and QA one complete project on the development instance, export the canonical YAML, and deploy that same full project atomically to the production instance. |
The create/fork operation never deploys, publishes a serving generation, mutates the semantic model, creates a data snapshot, or makes a temporary schema. Git is an optional review and evidence integration after export; LeapView has no native Git integration and no repository authority. The server remains authoritative for lifecycle, authorization, revisions, and deployment targets.
Update existing copies of Visual Showcase
The shared dashboards/dashboards/visual-showcase.yaml defines Purchase period as a dateRange control on purchase_timestamp. The browser displays Start and End dates as MM/DD/YYYY, and the timestamp range includes the full selected end date. The filter retains its purchase_time ID, existing targets, and unfiltered default. Its canvas component also retains its existing ID so references remain stable. Relative-period controls remain available for other filters and dashboards.
No database schema or automatic saved-dashboard migration is required. The rollout depends on how the dashboard is stored:
- Shared project dashboard: deploy the updated project source through the normal candidate/publication workflow. Viewers and new drafts forked from that generation receive the date-range definition. Updating the application binary alone does not deploy project YAML.
- Existing saved drafts or separately published copies: their documents are retained revisions, so the shared-source update does not rewrite them. To adopt this change, edit that copy's
purchase_timefilter to use labelPurchase periodand controldateRange, update its description, and clear any old relative-period default. Keep its field, ID, targets, and other customizations. Validate and preview the resulting revision, then publish it explicitly if the copy should be shared. Starting a new draft from the updated shared dashboard is another option. - Existing viewer sessions: filter state belongs to a serving generation. A new generation uses its own session state; an old rolling-period selection is not converted into an absolute date range. Choose the desired Start and End dates after opening the updated dashboard.
This is an explicit content update for existing saved copies, not a bulk rewrite: an automatic conversion could replace a deliberately chosen rolling-period filter or overwrite user edits.
Use Git when it helps review
Git is highly recommended, but opt-in. LeapView has no native Git integration, pull-request workflow, or merge automation. A mature integration exports canonical YAML, lets the user's Git provider handle branches and review, and invokes LeapView's CLI or API from a dedicated project-scoped deploy service account after merge.
Repository, ref, and commit metadata may be recorded as evidence and provenance. They are not a server-enforced “managed by Git” authority: the server still validates the project, authorization, candidate, and deployment target. Without Git, use the same draft, preview, development publication, and full-project production gates with an explicitly authorized publisher or service account.
Validate the promotion
Before production deployment, confirm that:
- the draft and published revision belong to the intended project and use the intended governed semantic model;
- the development and production instances are separate, permanent environment boundaries;
- dashboard visibility (
private,restricted, ororganization), access grants, row and column policies, and anonymous publication settings are intentional; - QA verified the development publication with representative viewer identities;
- the exported YAML, source revision (when Git is used), plan, and deploy service-account identity are retained as evidence; and
- coordinated model, semantic, policy, and dashboard changes are present in one full-project candidate.
Verify the result
After production activation, verify the serving-state generation and exercise one representative dashboard page, filter path, and table or query window. Check that the expected dashboard revision is published, visibility counts remain visible to authorized operators, and access policies still hold for viewers. A failed validation or activation leaves the previous valid generation serving; use the governed rollback operation rather than editing active files or deleting data snapshots.
Troubleshooting
| Symptom | Corrective action |
|---|---|
| An author can see or edit production assets | Remove the author's production-instance role or credential; do not rely on naming alone. |
| A draft edit changes the published dashboard | Confirm the author copied the published asset into a private draft and published a new revision only after review. |
| Git metadata is present but the server accepts an unintended source | Review the canonical YAML, candidate digest, target identity, and permissions. Repository/ref metadata is evidence, not authority. |
| A model and dashboard disagree after promotion | Treat them as one coordinated full-project change, revalidate in development, and redeploy the complete candidate. |
| A data snapshot is mistaken for a dashboard version | Inspect the dashboard revision and serving-state generation separately; snapshots only describe analytical runtime consistency. |
Next steps
Read Projects and environments for resource boundaries, API conventions for headless retry and error handling, Use the agent tool catalog for agent transport details, Targets and environments for target safeguards, and Dashboard authoring patterns for maintainable dashboard structure.