> ## Documentation Index
> Fetch the complete documentation index at: https://docs.genesiscomputing.com/llms.txt
> Use this file to discover all available pages before exploring further.

# GenesisApps Tools

> Build, deploy, and manage full-stack web applications — Dash dashboards, Flask CRUD apps, and narrated presentations — directly from agent conversations.

## **Overview**

### **Tool Name**

```json theme={null}
apps_tools
```

### Purpose

The **apps\_tools** (also called **GenesisApps**) let agents create, deploy, and manage lightweight web applications that run inside the Genesis platform. These apps are accessible to users via the **Apps panel** in the Genesis UI — no external hosting, Docker knowledge, or DevOps pipeline required.

GenesisApps are ideal for turning one-off analyses into living, shareable deliverables: interactive dashboards, operational CRUD tools, and narrated slide decks.

### Key Features

<Steps>
  <Step title="Scaffold from Templates" icon="clone">
    Instantly generate a working application skeleton using built-in templates for Dash dashboards, Flask apps, or presentation decks.
  </Step>

  <Step title="Full Lifecycle Management" icon="rotate">
    Create → Scaffold → Write Files → Start → Verify → Capture Screenshot → Review → Stop — all from chat.
  </Step>

  <Step title="Reverse-Proxy Serving" icon="globe">
    Each app is served through Genesis's reverse proxy at `/api/apps/<app_id>/proxy/`, with automatic port management and base-path injection.
  </Step>

  <Step title="Dataset Integration" icon="database">
    Dash dashboards connect to data via the **Datasets fast layer** — cached SQLite snapshots that avoid hitting your warehouse on every page load.
  </Step>

  <Step title="Access Control" icon="lock">
    Grant view, edit, or manage privileges to roles and individual users. Publish or keep apps in draft mode.
  </Step>

  <Step title="Branding" icon="palette">
    Apps inherit organization branding (logos, colors, design guidance) automatically from `genesis_branding.json`.
  </Step>

  <Step title="Chat Embedding" icon="message">
    Embed any running app directly in a chat message with ` ```app my-app-id``` `.
  </Step>
</Steps>

***

## Architecture

```mermaid theme={null}
graph LR
    A[Agent in Chat] -->|apps_action| B[Apps runtime]
    B -->|CREATE| C[App Workspace]
    B -->|SCAFFOLD| C
    B -->|START| D[Python Process]
    D -->|binds to| E["MICROAPP_PORT (auto)"]
    F[Reverse Proxy] -->|/api/apps/ID/proxy/| E
    G[User Browser] -->|Apps Panel / Chat Embed| F
    D -->|API calls| H[Genesis API /api/query]
    D -->|fast layer| I[Datasets API /api/datasets]
```

Each GenesisApp is a standard Python web application (Dash or Flask) running in its own subprocess. Genesis manages:

| Concern | How It Works |
| - | - |
| **Port allocation** | Automatic — injected via `MICROAPP_PORT` env var |
| **URL routing** | Reverse proxy at `/api/apps/<app_id>/proxy/` |
| **Data access** | Apps call Genesis APIs (`/api/query` or `/api/datasets`) using `GENESIS_APP_TOKEN`; dashboards should prefer datasets by default |
| **Dependencies** | `requirements.txt` is pip-installed into a per-app venv on START |
| **State** | Persistent workspace on disk; survives STOP / RESTART |

On Linux, unconfined app processes run under per-run cleanup. **STOP** and
**RESTART** stop processes the app started, including detached descendants. If
the Genesis server exits unexpectedly, an app's reaper detects the parent exit
and starts cleanup. Dashboard render workers and dependency-installation
commands also stop processes they started when the operation completes or times
out.

When a versioned app's runtime moves to separate storage, Genesis removes its
obsolete copy under `bot_storage` during cleanup passes, unless that storage is
stage-backed. These passes run at startup and hourly by default, with no setting
to enable; active or otherwise referenced runtime paths are left alone.

<Warning>
  GenesisApps **do not** have direct database credentials. All data flows through the Genesis API layer. This ensures security, auditing, and connection reuse.
</Warning>

Genesis also runs an auxiliary LLM security review on selected `WRITE_FILE` /
`WRITE_FILES` payloads before they are saved — for example Python that may
probe the host filesystem, launch subprocesses, or reach outside documented
Genesis API usage. When that review cannot run because the active chat provider
is misconfigured, unreachable, or returns an empty response, Genesis **blocks**
the write with `success: false`, `error: "GenesisApp security review is
unavailable; blocking file."`, and a `security_findings` entry with
`rule_id: "llm_app_code_guard"` and `severity: "block"`. The finding message
includes the failure kind (`not_configured`, `provider_exception`, or
`empty_response`). Fix the provider in **Config → AI Providers** and retry the
write rather than treating the failure as a policy denial on the code itself.

***

## Functions Available

### `apps_action`

The primary function for all GenesisApp operations. Dispatches based on the `action` parameter.

#### Actions Reference

| Action | Description | Key Parameters |
| - | - | - |
| **LIST** | List registered apps with per-app status (capped at 50 most recent). When one app's status lookup fails, LIST still returns the rest with a degraded entry (`status: "unknown"`, `error`) for that app instead of aborting. | `limit` |
| **TEMPLATES** | List available scaffold templates | — |
| **CREATE** | Register a new app (creates workspace, does not start it). Agent tool calls require an active GenesisApps build skill before CREATE/SCAFFOLD/WRITE. Pass `display_name` when you want Genesis to preserve a human-friendly title instead of deriving one from `app_id`. `replace_existing=true` on a cross-thread or versioned app also requires explicit confirmation. | `app_id` (required), `runtime_type`, `display_name`, `repo_id`, `emoji`, `embed_mode`, `confirm_write` |
| **DELETE** | Unregister an app from the Apps Library. Defaults to preserving workspace files for recovery/audit; `delete_workspace=true` also purges saved versions and change history. See the [API delete behavior](/docs/Genesis-API/API-Reference#genesis-app-registry-writes). Alias: `DELETE_APP`. | `app_id`, `delete_workspace` |
| **SET\_EMOJI** | Set or clear the emoji icon in the Apps UI | `app_id`, `emoji` |
| **SCAFFOLD** | Write a starter app skeleton into the workspace. When you already have a dataset, `dataset_id` can bind it immediately by writing `genesis_datasets.json` during scaffold. | `app_id`, `template`, `overwrite`, `dataset_id` |
| **WRITE\_FILE** | Write a single file into the workspace. Use base64 content with `is_base64=true` for binary `.png` and `.pdf` files; a `.pdf` must be a real PDF (bytes start with `%PDF-`) and is limited to 2 MiB. Hosted/stateless callers can send raw HTML only for standard app resources under `templates/`, `static/`, or `assets/`. `app.py` and HTML writes return non-blocking `lint_warnings[]` for common static and proxy-path issues. | `app_id`, `path`, `content`, `is_base64`, `restart_after_write`, `substitutions` |
| **WRITE\_FILES** | Write multiple files into the workspace. Binary `.png` and `.pdf` files use base64 content and `is_base64=true`; a `.pdf` must be a real PDF (bytes start with `%PDF-`) and is limited to 2 MiB. When every destination is a standard app resource under `templates/`, `static/`, or `assets/`, hosted/stateless callers can send raw HTML/CSS/JS/SVG content. `app.py` and HTML entries return aggregated non-blocking `lint_warnings[]`. | `app_id`, `files`, `restart_after_write`, `substitutions` |
| **COPY\_FILE** | Copy a file from a bot\_git repo into the workspace; text sources can apply optional server-side substitutions before write | `app_id`, `path`, `source_repo_id`, `source_path` or `source_file`, `substitutions` |
| **SYNC\_FROM\_REPO\_ROOT** | Copy `app.py` / `requirements.txt` from a git repo | `app_id`, `source_repo_id` |
| **LIST\_CHANGE\_HISTORY** | List recent app checkpoints for rollback and audit. Each returned change also includes a short `summary`. | `app_id`, `limit` |
| **UNDO\_LAST\_CHANGE** | Restore the app to the previous checkpoint and record the undo as a new change | `app_id` |
| **RESTORE\_TO\_CHANGE** | Restore the app to a specific prior checkpoint | `app_id`, `change_id` |
| **START** | Install dependencies and launch the app process. For `presentation_deck` apps, the first `START` / `RESTART` must pass pre-start validation plus auto-capture once before the deck first-start gate clears. Use `auto_capture_after_start=true` or `auto_capture_after_restart=true` for immediate render diagnostics. | `app_id`, `genesis_api_base_url`, `restart_if_running`, `validate_before_restart`, `auto_capture_after_restart`, `auto_capture_after_start` |
| **STOP** | Stop the running app process | `app_id` |
| **RESTART** | Stop then start (reloads code and dependencies). `presentation_deck` apps use the same first-start gate until one validated auto-capture passes. Versioned apps honor `run_source` just like START and return `restarted_from`. | `app_id`, `validate_before_restart`, `auto_capture_after_restart`, `run_source` |
| **STATUS** | Get current status, workspace path, port, runtime info, and saved preflight health when available. For a recent refused app-token call, also reports `health: "degraded"` and `last_api_error` while the process remains `running`. | `app_id` |
| **EXPLAIN\_BLOCKING** | Explain whether an app is blocked, why, and the safest next `apps_action` call | `app_id` |
| **LOGS** | Tail app stdout/stderr. Apps with Genesis runtime support also log outbound Genesis API results, including refused calls, with the `[GENESIS_APP_API]` prefix. | `app_id`, `tail_lines`, `since_last_start` |
| **URL** | Get the proxy URL and user-facing links | `app_id` |
| **VERIFY** | Health-check the app via the reverse proxy | `app_id`, `verify_data` |
| **VALIDATE** | Run structured workspace validation; presentation decks add asset, visual-coverage, slide/stage target, slide-range, stage-sweep, and narration-audio checks | `app_id`, `validate_check_images`, `validate_check_audio`, `target_slide`, `target_stage`, `slide_range`, `stage_sweep` |
| **CAPTURE** | Take a server-side screenshot of the running app. Successful captures surface failed CSS/JS/image/font subresources in diagnostics and render signals; when semantic waits are used, the response also echoes `wait_signals`. Databricks durability behavior and when `durability_warning` is returned are described under [durable chat uploads](/docs/deployments/Databricks/Genesis-On-Databricks#durable-chat-uploads). | `app_id`, `timeout_seconds`, `wait_for_render_signals`, `wait_for_selector` |
| **SWEEP\_CAPTURE** | Capture a presentation-deck slide or slide range, optionally sweep staged slides, and return per-slide render diagnostics plus recommended next steps. | `app_id`, `slide` or `slide_range`, `stage_sweep`, `run_source`, `timeout_seconds` |
| **REVIEW** | Run VERIFY + CAPTURE + LOGS in one call with diagnostic hints, and clear the one-time first-start gate for new dashboard workspaces when the review passes cleanly. For truly static/no-data Dash apps, pass `no_data_declared=true`; if Genesis confirms there is no dataset, live-query, or `/api/datasets` path, REVIEW can bypass checklist attestation and rely on health plus rendered-content evidence. Responses include `next_step`, `review_runtime_lifecycle`, and `no_data_attestation_bypass` when that bypass applies. | `app_id`, `wait_for_render_signals`, `include_visual_description`, `no_data_declared`, `compact` |
| **ATTEST\_CHECKLIST** | Record scaffold attestation for the current workspace state; new `dashboard_readiness_v2` dashboards require typed proof here before the first REVIEW / START path can clear. Invalid v2 responses default to compact, machine-correctable feedback with `failing_item_ids`, and can include `compact_item_errors`, `corrected_minimal_examples` / `corrected_example_items`, `attempted_attestation_context`, and `schema_followup`; use `compact=false` when you need the full checklist schema and detailed validation payloads. | `app_id`, `checklist_attestation`, `checklist_answers`, `attestation_mode`, `sample_dashboard`, `dry_run`, `compact` |
| **DEV\_QUERY\_PREFLIGHT** | Run dev-time query preflight for live-query dashboards, including optional structural profiling. Use `compact=true` to slim large query/profile payloads. | `app_id`, `preflight_connection_id` or `preflight_queries`, `profile_mode`, `structural_profile_config`, `compact` |
| **RUNTIME\_INFO** | Show venv path, Python version, installed packages | `app_id` |
| **LIST\_DEPLOY\_TARGETS** | List saved remote deploy target environments and security profiles | — |
| **DEPLOY** | Deploy the app to a remote backend such as EKS, Railway, Docker, or Databricks Apps | `app_id`, `runtime_type` or `dataset_definition.target_env`, `dataset_definition`, `confirm_identity_delegation`, `version_id` |
| **REMOTE\_STATUS** | Get live remote deployment status, target, URL, and health | `app_id` |
| **UNDEPLOY** | Tear down a remote deployment and return the app to local-only mode | `app_id` |
| **ACCESS\_LIST** | List role/user access grants | `app_id` |
| **ACCESS\_GRANT** | Grant view/edit/manage privileges to roles or users | `app_id`, `role_names`, `privileges` |
| **ACCESS\_REVOKE** | Revoke privileges from roles or users | `app_id`, `role_names`, `privileges` |
| **SET\_PUBLISHED** | Toggle published (visible to all) vs. draft (owner only) | `app_id`, `published` |
| **UPDATE\_RUNTIME\_TTL** | Change the app's idle auto-stop time; for example, set `ttl_seconds=3600` for one hour | `app_id`, `ttl_seconds` |
| **TRANSFER\_OWNERSHIP** | Transfer app ownership to another user | `app_id`, `new_owner_identity` |
| **ENABLE\_VERSIONING** | Enable versioned publishing (creates v1 snapshot; `assign_production` defaults to `true` so v1 becomes production when the app was already published). Offer only when the user wants a safe edit/preview workflow — do not enable on your own initiative. | `app_id`, `note`, `assign_production` |
| **LIST\_VERSIONS** | List all saved versions, production assignment, and working draft state | `app_id` |
| **CREATE\_VERSION** | Snapshot the working draft as a new saved version. **User-gated:** call only when the user explicitly asks to save or snapshot a version, or as the first step of a user-requested publish flow — not automatically after edits. | `app_id`, `note` |
| **PROMOTE\_VERSION** | Set a saved version as the new production version. **User-gated:** call only when the user explicitly asks to publish or promote (e.g. "publish it", "make it live", "promote v3"); do not call as the closing step of an edit cycle. Chain immediately after `CREATE_VERSION` only when the user requested save-and-publish in the same turn. | `app_id`, `version_id`, `restart_after_promote`, `note` |
| **RESTORE\_VERSION** | Overwrite the working draft with a saved version's files | `app_id`, `version_id`, `force` |
| **DISCARD\_WORKING\_COPY** | Reset the working draft to its baseline version | `app_id`, `force` |
| **UPDATE\_VERSION\_NOTE** | Update the note on an existing saved version | `app_id`, `version_id`, `note` |
| **DATASETS\_SETUP** | Create + bind + refresh a dataset, or bind existing datasets, then restart the app | `app_id`, `dataset_definition`, `dataset_actions` |
| **IMPORT\_PRESENTATION** | Import a repo `.pptx` or native Google Slides source into `presentation_deck.json` plus provenance artifacts | `app_id`, `source_repo_id`, `source_path`, `google_drive_file_id`, `visual_slide_analysis` |
| **EXPORT\_PRESENTATION** | Snapshot a Genesis presentation app into `.pptx` and optionally publish it back to Google Slides | `app_id`, `publish_google_slides`, `target_folder_id`, `deck_path` |
| **FORK\_TO\_STANDALONE** | Copy a **git-backed** app into a new **genesis-versioned** standalone app (not available on already-standalone apps) | `app_id`, `new_name` |

<Note>
  `START` returns `start_outcome` so callers can distinguish a transition this
  call performed from a live runtime it found and left in place. A new start
  returns `{"started": true, "reason": "started"}`. When the runtime was
  already running or starting, the response returns `started: false`, the
  `reason`, `running_since` when available, `code_reloaded: false`, and
  `restart_with`. `restart_with` names the literal call the caller that received
  it can make — an `apps_action(...)` invocation for the tool, an HTTP endpoint
  for the REST route. `START` does not reload `app.py` or `requirements.txt` on
  that reuse path; use `RESTART`, or the call named by `restart_with`, when the
  code must be reloaded.
</Note>

<Note>
  `apps_action(action="LIST")` returns `success: true` with an `apps` array. Each
  entry is the same status-shaped payload as `STATUS` when lookup succeeds. When
  status lookup fails for one app (for example a legacy non-canonical registry
  key or a corrupted workspace record), that entry is still included with
  `status: "unknown"` and an `error` string so the rest of the list remains
  usable. Inspect `error` and call `STATUS` or `EXPLAIN_BLOCKING` on the
  affected `app_id` before mutating it.

  Genesis stores `app_id` in canonical form: lowercase letters, numbers, and
  hyphens, 1–64 characters, starting and ending with a letter or digit. On
  registry load, legacy keys that differ only by casing or punctuation are
  renormalized to the canonical id; duplicates after renormalization are skipped.
</Note>

<Note>
  Registry changes from `CREATE`, `DELETE`, `replace_existing`, and app metadata
  updates take effect only after the app registry is saved. If that save is
  unavailable during replacement or `DELETE`, the tool returns `success: false`,
  `error_code: "app.registry_unavailable"`, and `retryable: true`; retry the
  operation later. A refused replacement leaves the existing app registered.
  For metadata changes, a refused save reports failure and leaves the previous
  metadata in place. After `CREATE`, saving the requested or derived display
  name is best-effort: if that separate save is refused, the app remains created
  with its previous name.
</Note>

### `presentation_deck_generate_tts`

Generate MP3 narration audio for a running presentation deck app using OpenAI TTS.

Use `use_genesis=true` (default) to rely on the platform OpenAI credential. For fallback auth, set `use_genesis=false` and pass `api_key` as either a raw OpenAI key or a stored `secret_*` reference.

| Parameter | Description |
| - | - |
| `app_id` | The presentation deck GenesisApp ID (required) |
| `voice` | TTS voice: `nova`, `onyx`, `shimmer`, etc. (default: `nova`) |
| `model` | TTS model: `tts-1` or `tts-1-hd` (default: `tts-1`) |
| `overwrite` | Overwrite existing MP3 files (default: `false`) |
| `api_key` | Optional fallback credential. Accepts a raw OpenAI key or a stored `secret_*` reference. |

See the full parameter reference below for `format`, `slides`, `voice_match`, `use_genesis`, and `timeout_seconds`.

<Note>
  Narration is read only from the canonical `n` field in `presentation_deck.json`.
  If a slide uses alias keys such as `narration`, `speaker_notes`, `notes`,
  `voiceover`, or `script` without `n`, TTS skips that slide and validation
  reports a warning. If no selected slides contain `n`,
  `presentation_deck_generate_tts` returns `success=false` with diagnostic hints
  instead of generating empty output.
</Note>

***

## Scaffold Templates

Call `apps_action(action="TEMPLATES")` to see the latest list. Built-in templates:

<AccordionGroup>
  <Accordion title="dash_dashboard_server_callbacks" icon="chart-bar">
    **Best for:** Interactive data dashboards with Plotly charts and AG Grid tables.

    Generates: `app.py`, `requirements.txt`, `assets/style.css`

    Includes server-side Dash callbacks, null-safe filter patterns, Genesis API integration, and CSS styling. KPI cards are data-driven from the bound dataset (row count plus column-derived totals or distinct counts) rather than hardcoded demo labels. This is the **recommended default** for dashboard-style apps.

    **Aliases:** `dash_dashboard`
  </Accordion>

  <Accordion title="dash_benchmark_query_vs_datasets" icon="gauge-high">
    **Best for:** Comparing performance of direct `/api/query` calls vs. the Datasets fast layer.

    Generates: `app.py`, `requirements.txt`, `assets/style.css`

    Measures and displays latency benchmarks so you can validate the Datasets caching benefit.

    **Aliases:** `dash_dataset_benchmark`
  </Accordion>

  <Accordion title="flask_scaffold" icon="flask">
    **Best for:** Operational CRUD applications — forms, tables, import/export workflows.

    Generates: `app.py`, `requirements.txt`, `static/style.css`, `templates/*.html`

    Local-first design with SQLite under `MICROAPP_STATE_DIR` for persistent state. Includes a connectivity check endpoint but no data sync by default.

    **Aliases:** `flask_ops_crud`, `flask_enhancement_inbox`
  </Accordion>

  <Accordion title="flask_basic" icon="flask-vial">
    **Best for:** Minimal starting point for custom Flask apps.

    Generates: `app.py`, `requirements.txt`, `static/style.css`

    Prefix-safe with `/healthz` endpoint. Includes `genesis_llm_call(...)` and
    `genesis_llm_direct(...)` helpers for server-side app LLM calls. Add your own
    routes as needed.
  </Accordion>

  <Accordion title="presentation_deck" icon="presentation-screen">
    **Best for:** Narrated slide presentations with splash screens, transitions, and optional AI voice-over.

    Generates: `app.py`, `requirements.txt`, `presentation_deck.json`,
    `presentation.js`, `presentation_styles.css`, slide generators under
    `presentation_slides/`, and example images under `presentation_images/`.

    A stdlib-only HTTP server (no Dash/Flask) that renders slides from a JSON
    definition. `app.py` loads the scaffolded `presentation.js` and
    `presentation_styles.css` runtime assets; edit those files with `WRITE_FILE`
    when you need custom deck chrome or client behavior. Supports OpenAI TTS
    narration via `presentation_deck_generate_tts`.

    **Aliases:** `presentation`, `deck`
  </Accordion>
</AccordionGroup>

***

## Typical Workflow

### Building a Dashboard

<Steps>
  <Step title="Create the app">
    ```
    apps_action(action="CREATE", app_id="sales-dashboard",
                display_name="Sales Dashboard", emoji="📊")
    ```
  </Step>

  <Step title="Scaffold the template">
    ```
    apps_action(action="SCAFFOLD", app_id="sales-dashboard",
                template="dash_dashboard")
    ```

    If you already have a dataset, `SCAFFOLD` can bind it immediately with
    `dataset_id="sales_dashboard_data"`. That writes `genesis_datasets.json`
    using default `read` + `ssrm` actions unless you override them with
    `dataset_actions`. Use `DATASETS_SETUP` instead when Genesis still needs to
    create or refresh the dataset first.
  </Step>

  <Step title="Set up datasets (recommended)">
    ```
    apps_action(action="DATASETS_SETUP", app_id="sales-dashboard",
                dataset_definition={
                    "kind": "materialized_dataset",
                    "dataset_id": "sales_dashboard_data",
                    "sources": [
                        {"source_id": "monthly_sales",
                         "connection_id": "Snowflake",
                         "query": "SELECT * FROM ANALYTICS.SALES.MONTHLY_SUMMARY"}
                    ]
                })
    ```

    This creates the dataset, refreshes it, writes `genesis_datasets.json`, and restarts the app.

    To bind existing datasets without creating new ones, call:

    ```
    apps_action(action="DATASETS_SETUP", app_id="sales-dashboard",
                dataset_definition={"dataset_ids": ["sales_dashboard_data"]},
                dataset_actions=["read", "ssrm"])
    ```

    The binding shortcut writes `genesis_datasets.json`, preserves the existing
    datasets, and restarts the app. You can also put `actions` inside the
    shortcut payload; when omitted, Genesis defaults to both `read` and `ssrm`.

    Use `kind="materialized_dataset"` for larger single-source dashboard
    snapshots. `query_cache_dataset` is capped at `max_rows <= 10000`; if you
    stay on that lightweight cache path, reduce the cached result with filters
    or aggregation first.
  </Step>

  <Step title="Customize app.py">
    Edit the SQL queries, chart definitions, and layout. Use `WRITE_FILE` for a single full `app.py` replacement, and `WRITE_FILES` when updating `app.py` plus requirements or assets in one batch.
  </Step>

  <Step title="Record dashboard attestation">
    ```
    apps_action(action="ATTEST_CHECKLIST", app_id="sales-dashboard",
                checklist_attestation={"contract_id": "dashboard_readiness_v2",
                                       "items": [...]})
    ```

    New dashboard scaffolds can require typed proof before the first review and start sequence can complete.
  </Step>

  <Step title="Clear the first-start review gate">
    ```
    apps_action(action="REVIEW", app_id="sales-dashboard")
    ```

    REVIEW runs health checks, captures a screenshot, tails logs, reports diagnostic hints, and can pass even before the first runtime is started so the initial dashboard gate can clear.
  </Step>

  <Step title="Start or restart the runtime">
    ```
    apps_action(action="START", app_id="sales-dashboard")
    ```

    Use `RESTART` after later code or dependency changes. Add `auto_capture_after_start=true` when you want START to return screenshot/render diagnostics immediately.
  </Step>

  <Step title="Self-audit before sharing">
    ```python theme={null}
    apps_action(
        action="REVIEW",
        app_id="sales-dashboard",
        include_visual_description=true,
        verify_data=true
    )
    ```

    Compare the rendered dashboard against the original request or `REQUIREMENTS.md`.
    Confirm that required KPIs, filters, grid columns, chart labels, units, and
    time ranges are present and plausible. If gaps remain, update the app,
    `RESTART`, and re-run the audit. Limit this loop to 3 audit cycles; if gaps
    still remain, call them out explicitly in the handoff instead of treating the
    dashboard as complete.
  </Step>

  <Step title="Publish and share">
    ```
    apps_action(action="SET_PUBLISHED", app_id="sales-dashboard", published=true)
    apps_action(action="ACCESS_GRANT", app_id="sales-dashboard",
                role_names=["user"], privileges=["view"])
    ```
  </Step>
</Steps>

<Note>
  The final dashboard handoff should include the self-audit gap list, the deployed
  URL, the runtime env vars used (at minimum `GENESIS_API_BASE_URL` and
  `GENESIS_REFRESH_SECONDS`), verification evidence snippets, the final screenshot
  artifact path, and any known remaining gaps.
</Note>

### Versioned Publishing (Safe Edit → Promote)

<Warning>
  Versioning actions apply only to **genesis-versioned** apps. Git-backed apps
  (managed remote / Git source) must use `FORK_TO_STANDALONE` first; otherwise
  `ENABLE_VERSIONING`, `CREATE_VERSION`, `PROMOTE_VERSION`, and related calls
  return an `ARTIFACT_IS_GIT_BACKED` error.
</Warning>

<Warning>
  **`CREATE_VERSION` and `PROMOTE_VERSION` are user-gated.** Edit and preview the
  working draft, then stop and report the preview URL. Do not snapshot or promote on
  your own initiative after a successful preview. Phrases such as "looks good",
  "thanks", or "that worked" do not authorize publication — wait for explicit requests
  like "publish it", "promote it", "make it live", "push to production", or
  "save and publish".
</Warning>

The agent owns edit and preview. Save and promote run only when the user explicitly
asks (in chat or via the Apps UI **Promote** control).

<Steps>
  <Step title="Enable versioning (when the user wants safe iteration)">
    ```
    apps_action(action="ENABLE_VERSIONING", app_id="sales-dashboard")
    ```

    Creates Version 1 from the current files. When `assign_production` is `true`
    (default) on an already-published app, v1 is pinned as production so live users
    keep seeing the pre-change state. Do not enable versioning on your own initiative.
  </Step>

  <Step title="Edit the working draft">
    ```
    apps_action(action="WRITE_FILE", app_id="sales-dashboard",
                path="app.py", content="...")
    ```

    Changes go to the working draft — production is unaffected.
  </Step>

  <Step title="Preview the draft">
    ```
    apps_action(action="START", app_id="sales-dashboard",
                run_source="working_copy",
                auto_capture_after_restart=true)
    ```

    The draft runs at `/api/apps/<id>/working-copy/proxy/`. Production stays on the
    promoted version until the user publishes.
  </Step>

  <Step title="Stop and report">
    Tell the user the working-draft preview URL and which production version is still
    live (for example, *"Preview is at `/api/apps/sales-dashboard/working-copy/proxy/`;
    production is still on v3. Say if you want me to save and publish, or promote from
    App History."*). Also include the `--working-copy` chat embed from the tool's
    `reminders` response (see [Embedding Apps in Chat](#embedding-apps-in-chat)) so the
    user can review unpublished draft changes inline. Do not call `CREATE_VERSION` or
    `PROMOTE_VERSION` yet.
  </Step>

  <Step title="Save a version (only when the user asks)">
    ```
    apps_action(action="CREATE_VERSION", app_id="sales-dashboard",
                note="v2: new chart layout")
    ```
  </Step>

  <Step title="Promote to production (only when the user asks)">
    ```
    apps_action(action="PROMOTE_VERSION", app_id="sales-dashboard",
                version_id="<v2_id>", restart_after_promote=true)
    ```
  </Step>

  <Step title="Roll back (only when the user asks)">
    ```
    apps_action(action="LIST_VERSIONS", app_id="sales-dashboard")
    apps_action(action="PROMOTE_VERSION", app_id="sales-dashboard",
                version_id="<v1_id>", restart_after_promote=true)
    ```
  </Step>
</Steps>

Genesis also records file-level checkpoints. In the dashboard UI these appear inside **App History**, and for versioned apps the checkpoints are grouped beneath their parent saved version as dot releases such as `v2.1`. `LIST_CHANGE_HISTORY` also returns a compact `summary` for each checkpoint so agents can scan rollback candidates quickly.

For app-tool behavior after a Databricks restart, see [Databricks working-copy recovery](/docs/deployments/Databricks/Genesis-On-Databricks#durable-sqlite-stores).

### Building a Presentation

<Steps>
  <Step title="Create and scaffold">
    ```
    apps_action(action="CREATE", app_id="quarterly-review", emoji="🎤")
    apps_action(action="SCAFFOLD", app_id="quarterly-review",
                template="presentation_deck")
    ```
  </Step>

  <Step title="Edit presentation_deck.json">
    Define slides, titles, narration text, and images in the JSON file. Use `t` for
    visible slide title, `s` for visible subtitle/body HTML, and `n` for narration.
    `t` accepts either a plain string, `{"text":"Plain title"}` for escaped text,
    or `{"html":"<span>Styled</span>"}` for explicit safe title markup. When you
    use the object form, include exactly one key: `html` or `text`. Keep title
    styling in `s` when the title does not need inline markup.
    Export/publish validation rejects slides that store visible text only in alias
    fields such as `title`, `subtitle`, `body`, `body_text`, `bullets`,
    `left_content`, `right_content`, or `two_column`, and it also rejects slides
    with no visible content. Non-text slides should still provide visible `img`,
    `plotly`, `py`, or staged `label`/`sub` content. When you update
    `presentation_deck.json` through `WRITE_FILE` or `WRITE_FILES`, Genesis also
    returns `warnings` and `hints` when it detects long-form alias keys without
    the canonical short fields.
  </Step>

  <Step title="Validate the deck workspace">
    ```
    apps_action(action="VALIDATE", app_id="quarterly-review")
    ```

    Run this before export or before a gated first `START` / `RESTART` to catch
    missing `presentation_images/*` paths, slide/stage URL targets,
    `slide_range` selection errors, stage-sweep gaps, visual-coverage gaps, and
    audio/captions issues early. `validate_check_images` and
    `validate_check_audio` both default to `true`. Use `slide_range="1-6"` to
    validate only part of a deck, and add `stage_sweep=true` when those
    targeted slides must already expose staged content. When narration intent is
    explicit via deck metadata or `presentation_deck.json` `audio.format`,
    missing fallback narration clips become blocking errors instead of warnings.
    For `kind="python"` slides or stages, `py` / `python` must point to a file
    under `presentation_slides/*.py` that defines callable `render(ctx)`.
    Those renderer files are loaded as standalone workspace scripts, so prefer
    absolute imports such as `from presentation_slides.utils import helper`
    instead of relative imports.
  </Step>

  <Step title="First start: clear the deck gate and launch">
    ```python theme={null}
    apps_action(
        action="START",
        app_id="quarterly-review",
        validate_before_restart=true,
        auto_capture_after_restart=true
    )
    ```

    The first `START` / `RESTART` is blocked until both flags succeed once.
    Genesis runs structured deck validation before launch, then auto-captures
    slide `1` (the first authored content slide) rather than the splash gate
    shell. The gate clears only when that capture shows real slide content
    instead of an empty, loading, error, or broken-asset state. Font-only
    subresource failures are tolerated; other broken deck assets block the gate.
    After the gate is cleared, later `START` / `RESTART` calls remain
    warning-only.
  </Step>

  <Step title="Generate narration (optional)">
    ```
    presentation_deck_generate_tts(app_id="quarterly-review", overwrite=true)
    ```

    The app must already be running. If the selected slides do not contain
    narration in `n`, the tool returns `success=false` with hints telling you to
    move narration text into `n`.
    If the Genesis-configured TTS path returns `invalid_api_key`, retry with
    `use_genesis=false` and a valid direct `api_key` value or stored `secret_*`
    reference.
  </Step>

  <Step title="Import an existing deck (optional)">
    ```
    apps_action(action="IMPORT_PRESENTATION", app_id="quarterly-review",
                google_drive_file_id="<google_slides_file_id_or_shared_url>")
    ```

    Native Google Slides import now reads the deck in place through the Slides API, so large decks no longer depend on a Drive `.pptx` export succeeding.
  </Step>

  <Step title="Publish a snapshot export (optional)">
    ```
    apps_action(action="EXPORT_PRESENTATION", app_id="quarterly-review",
                publish_google_slides=true, target_folder_id="<drive_folder_id>")
    ```

    Export is snapshot-first: it freezes the rendered Genesis app state into a PowerPoint-ready artifact and can publish that `.pptx` back into Google Slides.
  </Step>
</Steps>

### Remote Deployment

GenesisApps can also run outside the Genesis host on supported remote backends.

Use `LIST_DEPLOY_TARGETS` first to discover saved `target_env` and `security_profile` names. Then call `DEPLOY` with `dataset_definition={"target_env":"<name>","security_profile":"<name>"}` to reuse stored backend defaults such as cluster, region, namespace, and runtime identity settings. If `target_env` already declares the backend, `runtime_type` is optional; otherwise set it explicitly to `eks`, `railway`, `docker`, or `databricks_apps`. For versioned apps, add `version_id="<saved_version_id>"` to deploy a specific saved version remotely; omit `version_id` to deploy Production.

```python theme={null}
apps_action(action="LIST_DEPLOY_TARGETS")

apps_action(
    action="DEPLOY",
    app_id="sales-dashboard",
    dataset_definition={
        "target_env": "shared-eks",
        "security_profile": "analyst-runtime",
    },
    confirm_identity_delegation="true",
)

apps_action(action="REMOTE_STATUS", app_id="sales-dashboard")
apps_action(action="UNDEPLOY", app_id="sales-dashboard")
```

For EKS, `DEPLOY` now defaults `service_type` to `LoadBalancer` so `REMOTE_STATUS`
usually reports a browser-reachable URL. Set `service_type="ClusterIP"` only when
you intentionally want a cluster-internal URL instead.

User-facing app links such as `apps_action(action="URL")` prefer these explicit
public-base overrides before falling back to inferred hostnames:
`GENESIS_MCP_PUBLIC_URL`, `GENESIS_PUBLIC_URL`,
`GENESIS_PUBLIC_BASE_URL`, `GENESIS_EXTERNAL_URL`,
`SERVICE_BASE_URL`, `NGROK_BASE_URL`, and `FRONTEND_BASE_URL`.
Set one of them in hosted deployments when Genesis cannot infer the browser-facing
hostname cleanly.

<Warning>
  Remote deployment requires `confirm_identity_delegation="true"`. For container backends, the caller must acknowledge that the app uses their delegated Genesis identity and permissions. A Databricks App instead runs as its own service principal and receives no Genesis app token.

  #### Databricks Apps

  `runtime_type="databricks_apps"` runs the app as a Databricks App on the workspace of the
  Databricks connection named in `dataset_definition.databricks_connection_id`. Genesis
  uploads the app's source files to `/Workspace/Users/<you>/genesis_apps/<app_name>`. It
  leaves out local runtime state such as the app's virtual environment, its logs and
  `genesis_datasets.json`. It then creates or reuses the app, starts its compute, and deploys
  a snapshot. Optional settings are `warehouse_id` (the SQL warehouse the app queries;
  defaults to the connection's), `grant_schema` (`catalog.schema` on which the app's service
  principal is granted `USE CATALOG`, `USE SCHEMA` and `SELECT`), and `app_name` (at most 30
  lowercase letters, digits and hyphens).

  Genesis ships a runner, `genesis_databricks_main.py`, beside the app and makes it the
  app's command. The runner serves the app's `app.py`: a Flask `app`, or a Dash `app` through
  its `app.server`, at the root path `/`. Most GenesisApps then run unchanged, because the
  runner answers the app's Genesis data calls itself. It starts a local stand-in for the
  Genesis API, and points the app's `GENESIS_API_*` settings and app token at it:

  * `POST /api/query` (the templates' `genesis_query_rows()`) runs the statement on the SQL
    warehouse, in the catalog and schema of the Genesis connection the app names. Only one
    read statement runs: anything after a `;` is refused, and so is a `WITH` that writes.
  * `/api/datasets/<id>`, `.../tables` and `.../tables|views/<name>/rows` (the dataset fast
    layer) are served from a local SQLite snapshot. The snapshot is rebuilt from the
    dataset's own source queries on the warehouse when it is first read, and again after an
    hour, or after the dataset's TTL if it has one. It is built with Genesis's own dataset
    store and query code, so dataset views run as they do in Genesis.
  * `GET /api/health` and `/api/healthcheck` return a local health response, and
    `POST /api/apps/<id>/app-token/rotate` returns the stand-in's current per-process token.
  * A module-level `query_rows(sql, max_rows=...)`, such as the one in the
    `foundry_workshop_app` template, is replaced by a direct statement on the warehouse.

  The runner is never placed in the GenesisApp workspace, because it reads Databricks
  credentials, which a GenesisApp may not.

  Before anything is created on Databricks, a preflight reads the app's source and refuses
  the deploy, listing every reason, when the app:

  * calls the Genesis LLM (`genesis_llm_call`, `genesis_llm_direct`, `/llm/call`,
    `/llm/direct`). An LLM feature whose function first checks that the LLM URL is set,
    as the dashboard scaffold's AI insight does, is not refused. It reports itself
    unavailable, and the deploy result says so in `notes`.
  * calls another Genesis endpoint beyond the query, dataset, health-check, and token
    rotation routes above, such as `/api/tts/speech`, `/api/apps/<id>/files/...` or the
    dataset SSRM endpoint `/api/datasets/<id>/datasource`.
  * names a connection that is not a Databricks connection on the deploy workspace, or that
    Genesis cannot find. An app whose `app.py` defines a module-level `query_rows` (the
    `foundry_workshop_app` contract) is not refused for this: the runner replaces that
    function with a direct statement on the warehouse, and the deploy result notes the
    connection instead.
  * is bound to a dataset with a source other than SQL or inline data, a source whose SQL
    lives in a repository file (`sql_path`), or a source on such a connection.
  * has no module-level `app` in `app.py`.

  The scaffolds `dash_dashboard`, `flask_basic`, `flask_enhancement_inbox` and
  `foundry_workshop_app` pass as shipped. `presentation_deck` is refused. The deploy result
  lists the packaged connections and datasets in `config.packaged_connections` and
  `config.packaged_datasets`.

  Unlike the other backends, a Databricks App does not call back into Genesis. No Genesis
  app token is minted for it, and it queries Unity Catalog as its own service principal. The
  returned URL is the app's own: it is opened with a Databricks login, which the Genesis
  remote proxy cannot pass. A first deployment takes several minutes, while the app's
  compute starts and the deployment builds.
</Warning>

***

## Environment Variables

These are injected automatically into each app process at START / RESTART:

| Variable | Description |
| - | - |
| `MICROAPP_HOST` | Bind address supplied by Genesis (currently `127.0.0.1`) |
| `MICROAPP_PORT` | Auto-assigned port — **never hardcode a port** |
| `MICROAPP_BASE_PATH` | Proxy prefix. Local apps use `/api/apps/<app_id>/proxy/`; remote deployments use `/api/apps/<app_id>/remote/proxy/`. |
| `MICROAPP_STATE_DIR` | Persistent state directory (survives restarts) |
| `MICROAPP_SOURCE_DIR` | Source workspace directory |
| `GENESIS_API_BASE_URL` | Internal Genesis API base URL |
| `GENESIS_API_APP_LLM_URL` | App-scoped orchestrated chat endpoint: `POST /api/apps/<app_id>/llm/call` |
| `GENESIS_API_APP_LLM_DIRECT_URL` | App-scoped direct completion endpoint: `POST /api/apps/<app_id>/llm/direct` |
| `GENESIS_APP_TOKEN` | Server-side auth token for Genesis API calls, minted automatically by Genesis for app startup and background work |
| `GENESIS_LLM_BOT_ID` | Default bot for app-scoped chat turns (the app's `created_by_agent`, or `Eve`) |
| `GENESIS_LLM_MODEL_TIER` | Default app LLM tier inherited from the authoring thread, falling back to `main` |
| `GENESIS_LLM_PROVIDER` | Optional provider override inherited from the authoring thread when present |
| `GENESIS_DATASET_IDS_JSON` | JSON string of bound dataset IDs |

<Warning>
  **Never hardcode ports.** Always bind to `MICROAPP_HOST` / `MICROAPP_PORT`.

  **Be proxy-prefix safe.** Dash apps must set `requests_pathname_prefix` and `routes_pathname_prefix` to `MICROAPP_BASE_PATH`. Flask apps should mount routes under `url_prefix=MICROAPP_BASE_PATH`.
</Warning>

### App-scoped LLM calls

When app code needs model output, call the app-scoped Genesis routes with
`X-Genesis-App-Token` rather than embedding third-party LLM API keys in the
workspace:

* `POST /api/apps/{app_id}/llm/call` runs one chat turn through a Genesis
  agent. Body fields: `bot_id`, `prompt`, optional `thread_id`, `model_tier`,
  `provider`, `execution_lane`, and `timeout_seconds`. Omit `thread_id` to
  create a new thread; when `model_tier` and `provider` are also omitted,
  Genesis seeds that new thread from the app authoring thread when available.
* `POST /api/apps/{app_id}/llm/direct` runs a direct base completion without
  agent orchestration. Body fields: `prompt`, optional `model_tier`,
  `provider`, and `timeout_seconds`. Omit `model_tier` and `provider` to
  inherit the app authoring thread defaults when available; otherwise Genesis
  falls back to `main` tier plus the deployment's configured main provider.
* Dash and **Flask Basic** scaffolds expose `genesis_llm_call(...)` and
  `genesis_llm_direct(...)`, and Genesis injects `GENESIS_API_APP_LLM_URL`,
  `GENESIS_API_APP_LLM_DIRECT_URL`, `GENESIS_LLM_BOT_ID`,
  `GENESIS_LLM_MODEL_TIER`, and optional `GENESIS_LLM_PROVIDER` for that
  contract.

```python theme={null}
reply = genesis_llm_call(prompt="Summarize the visible KPI deltas.")
```

***

## Data Access Patterns

### Dashboard Default Path

For dashboard-style GenesisApps, use datasets first:

1. `CREATE`
2. `SCAFFOLD`
3. `DATASETS_SETUP`
4. `ATTEST_CHECKLIST`
5. `REVIEW`
6. `START`

Use direct `/api/query` only as an explicit fallback for prototypes, tiny result sets, debugging, or true realtime needs, and run `DEV_QUERY_PREFLIGHT` before relying on it for an interactive dashboard.
Preflight now inspects both scaffold helper calls (`genesis_query_rows()` /
`genesis_query_df()`) and direct `requests.post(..., json={...})` `/api/query`
payloads. If the workspace defines `DEFAULT_CONNECTION_ID`, preflight reuses
that value for inferred query paths that do not set `connection_id` inline.
If preflight reports `query_health: "unknown"`, it means the tool still could
not infer a concrete `connection_id` from the current app code, not that the
query itself failed. This still commonly happens when the app reads the
connection from `GENESIS_CONNECTION_ID` or another runtime-only source.
Provide `preflight_connection_id` (or make the connection literal in code)
before treating it as a live-query error.

Prefer `profile_mode="both"` on `DEV_QUERY_PREFLIGHT` for dashboards so the same call returns latency plus structural findings such as grain mismatches, missing latest periods, and raw-vs-dedup deltas. When structural profiling runs, responses can also include `structural_profiles`, `structural_findings`, `structural_query_profiles`, and `structural_summary`.

When a workspace has saved preflight state, `apps_action(action="STATUS")` also
returns a compact `preflight` block. Genesis reuses cached health inside
`APPS_STATUS_PREFLIGHT_MAX_AGE_SECONDS` (default `60`) and otherwise runs a
lightweight `SELECT 1` smoke check against the saved connection before
returning status.

### Template-backed writes

Use `COPY_FILE` or source-backed `WRITE_FILE` / `WRITE_FILES` with
`substitutions` when you want large templates to stay in a repo instead of
expanding them in chat context.

```python theme={null}
apps_action(
    action="COPY_FILE",
    app_id="sales-dashboard",
    path="app.py",
    source_repo_id="template_repo",
    source_path="dashboards/base_app.py",
    substitutions={
        "<APP_TITLE>": "Sales Dashboard",
        "<DEFAULT_CONNECTION_ID>": "Snowflake",
    },
)
```

<Note>
  When `apps_action` is invoked through hosted MCP or `POST /api/tool_call`,
  Genesis allows raw HTML/CSS/JS/SVG content only for workspace-relative
  destinations under `templates/`, `static/`, or `assets/` ending in `.html`,
  `.htm`, `.css`, `.js`, `.mjs`, or `.svg`. Other destinations keep normal
  string sanitization.
</Note>

### Dashboard readiness v2

New dashboard scaffolds can require the `dashboard_readiness_v2` contract before the first meaningful start:

* Call `ATTEST_CHECKLIST` with `checklist_attestation`, not `checklist_answers`, on v2 workspaces.
* Each item in `checklist_attestation.items` must include `id`, `answer`, `decision`, and `evidence`.
* `data_plausibility_checklist.questions[*].override_supported` marks checklist items that accept `checklist_attestation.structural_overrides`.
* `checklist_attestation.structural_overrides` is optional and uses `{item_id, justification, verified_by}` entries for eligible items `grain`, `time_range`, `magnitude`, and `consistency`. `justification` must be at least 20 characters, and `verified_by` must be one of `DEV_QUERY_PREFLIGHT`, `dataset_query_tool`, or `external_check`.
* `query_result` evidence must include inline `result` rows or data, and `time_range` evidence accepts ISO dates plus year (`2015`), year-month (`2015-04`), and year-quarter (`2015-Q2`) `last_period_end` values.
* `tool_result_ref` resolves the current workspace state by `label`, or by structural identifiers such as `finding_type`, `finding_id`, or `check_id`.
* `data_plausibility_checklist` also exposes `structural_overrides_help` and `dry_run_supported=true`; pass top-level `dry_run=true` to `ATTEST_CHECKLIST` to validate evidence shape without persisting the attestation.
* For demos and prototypes, pass `attestation_mode="sample"` or `sample_dashboard=true` to use the explicit sample contract. Sample mode still requires `source_choice`, `grain`, and `dataset_strategy`, while `magnitude`, `consistency`, and `time_range` are reported as non-blocking validation details.
* Sample-mode failures can return `attempted_attestation_context` with the attempted `attestation_mode`, whether `sample_dashboard` was requested, and the required question ids checked for that attempt. `attestation_status` continues to describe the last persisted workspace attestation until a non-`dry_run` submission succeeds.
* In sample mode, `grain` can also be satisfied by a current `auto_profile_ref` structural profile, including heuristic sample-dashboard profiles, when that saved profile still matches the current workspace fingerprints.
* For additive KPI dashboards, exact grain proof can come from a fresh structural `grain_check` / `raw_vs_dedup` result with exact confidence, or from inline `query_result` evidence whose summary rows include `total_rows` and `distinct_entities` aligned with the `grain` decision.
* Known bundled demo connections such as `formula_1_sqlite`, `baseball_sqlite`, `credit_card_sqlite`, `asset_mgmt_sqlite`, or `workspace_sqlite` may trigger an advisory suggesting sample mode; Genesis never switches modes automatically.
* Invalid v2 submissions default to compact responses. Genesis may still repeat the checklist schema when the thread has not seen it yet unless you explicitly force `compact=true`. In compact failures, inspect `failing_item_ids`, `compact_item_errors`, `corrected_minimal_examples` / `corrected_example_items`, `attempted_attestation_context`, and `schema_followup`. Use `compact=false` when you need the full checklist schema again, including `item_validation_details`, `submission_rules`, `structural_overrides_help`, and the example command. After the checklist schema has already been emitted in a thread, repeated compact failures can suppress `data_plausibility_checklist.questions[*]` and keep only the machine-correctable fields.
* When `time_range.decision.covers_expected_period` contradicts evidence, `item_validation_details.time_range.resolution_hints[]` explains whether to refresh the dataset/source or record the observed stale `last_period_end`.
* `attestation_status.time_range_summary` surfaces `last_period_end`, `expected_end`, `covers_expected_period`, and `days_behind` when available so app chrome can display freshness state without parsing raw evidence. When the latest period still lags the expected period, app advisories can also include `freshness_banner` with a ready-to-display warning message.
* If `genesis_datasets.json` is already bound, `dataset_strategy` can auto-satisfy.
* For `dashboard_readiness_v2`, freshness is scoped to the data proof fingerprint rather than ordinary `app.py` edits. `attestation_status.code_changed_since_attestation` still flags code drift for visibility.
* Genesis computes a workspace **source fingerprint** from primary config files (`app.py`, `requirements.txt`, `genesis_datasets.json`, `genesis-microapp.json`, `genesis_branding.json`, `presentation_deck.json`, and for presentation decks `presentation.js` and `presentation_styles.css` when present), asset directories (`templates`, `static`, `assets`), and every authored `.py` module in the workspace (excluding build/cache directories such as `.venv`, `node_modules`, and `__pycache__`). Edits to helper modules or deck runtime assets therefore invalidate cached first-start `REVIEW` results tied to an older fingerprint. `START` / `RESTART` also treat changes to `presentation.js` or `presentation_styles.css` as restart-relevant, just like `app.py` and `requirements.txt`.
* The first `START` or `RESTART` is blocked until attestation is fresh and `REVIEW` passes once. `START` and `RESTART` normally attempt that `REVIEW` automatically and surface any blocking findings so you can fix them and retry. A standalone passing `REVIEW` may start and stop a temporary preview runtime; call `START` next to launch the persistent runtime.

### Datasets (Recommended for Dashboards)

The **Datasets fast layer** pre-materializes query results into SQLite snapshots. This avoids hitting your data warehouse on every dashboard page load.

1. Define a dataset with `DATASETS_SETUP` or the `dataset_create_tool`.
2. Bind it to the app via `genesis_datasets.json`:
   ```json theme={null}
   {
     "dataset_ids": ["my_dataset_id"],
     "actions": ["read", "ssrm"]
   }
   ```
3. Restart the app so dataset bindings and dataset env vars are refreshed.
4. `GET /api/datasets/<id>` also returns top-level freshness helpers for app code:
   `active_snapshot_id`, `last_refresh_at`, `snapshot_built_at`, `row_counts`,
   and `table_name_mapping`.
5. In your app code, fetch data from `/api/datasets/<id>/tables/<table>/rows` using `GENESIS_APP_TOKEN`:
   ```python theme={null}
   headers = {"X-Genesis-App-Token": os.environ["GENESIS_APP_TOKEN"]}
   resp = requests.post(
       f"{os.environ['GENESIS_API_BASE_URL']}/api/datasets/{dataset_id}/tables/{table}/rows",
       json={"limit": 1000},
       headers=headers
   )
   data = resp.json()
   ```
   `POST /api/datasets/<id>/datasource` also accepts `table` or `view` in the
   SSRM request body (or query string) when the dashboard should target a
   specific dataset table or view instead of the dataset default target.

`GENESIS_APP_TOKEN` is the app's server-side Genesis API credential.
`genesis_datasets.json` adds dataset scopes/bindings and populates dataset env
vars; it is not the sole reason the token exists.

<Note>
  `GENESIS_APP_TOKEN` authenticates API requests as the app's delegated Genesis
  user identity. Genesis rejects app tokens whose delegated identity is empty, so
  keep this token server-side and send it only from backend app code via
  `X-Genesis-App-Token`.
</Note>

<Note>
  When `genesis_datasets.json` is present, proxied browser requests can also
  include an incoming `X-Genesis-App-Token` header that Genesis minted for the
  current viewer. Request handlers that need the active viewer's dataset or
  `/api/query` scope should prefer that incoming header when present; background
  jobs and startup code should keep using `GENESIS_APP_TOKEN`.
</Note>

### Direct Query (Fallback)

For small, ad-hoc queries you can call `/api/query` directly:

```python theme={null}
resp = requests.post(
    f"{os.environ['GENESIS_API_BASE_URL']}/api/query",
    json={"connection_id": "Snowflake", "query": "SELECT ...", "max_rows": 100},
    headers={"X-Genesis-App-Token": os.environ["GENESIS_APP_TOKEN"]}
)
```

The Dash scaffold already includes `genesis_query_rows()` / `genesis_query_df()` helpers around
this pattern. Reuse those helpers when possible instead of hand-parsing the response.

Read the response as `body["data"]["rows"]`, not `body["data"]` as a list.

`DEV_QUERY_PREFLIGHT` can inspect both the scaffold helpers and direct
`json={...}` payloads for `/api/query`. Keep `query` and `connection_id`
literal when possible so preflight can auto-profile the live-query path. If
multiple inferred queries share one connection, set `DEFAULT_CONNECTION_ID` in
the workspace so preflight can reuse it automatically.

`DEV_QUERY_PREFLIGHT` does not execute `PRAGMA ...` statements. For schema
discovery, use `SELECT * FROM <table> LIMIT 1` and inspect the returned
`columns`.

If Genesis detects more live-query call sites than the latest preflight covered,
the response can include `coverage_gap` and may add a blocking finding for
dashboard apps with a large gap. Re-run preflight with `preflight_queries`
covering the actual query paths before relying on the result.

<Warning>
  Do not pass a dataset-prefixed `connection_id` such as `dataset.my_dataset` to `/api/query`. That route expects a real database connection. For dataset-backed reads, use the scaffold helpers `genesis_dataset_df()`, `genesis_dataset_frame_with_meta()`, or `dataset_read_or_query_fallback()`, which call `/api/datasets/{id}/{tables|views}/{name}/rows`.
</Warning>

<Warning>
  There is no SQL-style `POST /api/datasets/{id}/query` endpoint. If an app calls it, Genesis returns a structured `400` with suggested dataset rows and datasource endpoints. Use `/api/datasets/{id}/tables/{table}/rows`, `/api/datasets/{id}/views/{view}/rows`, or `/api/datasets/{id}/datasource` instead.
</Warning>

<Tip>
  Use Datasets for production dashboards (performance + caching). Use `/api/query` only for preflight-approved prototypes or truly real-time needs.
</Tip>

***

## Access Control

GenesisApps support four privilege levels:

| Privilege | Allows |
| - | - |
| **view** | See and interact with the app (production version for versioned apps) |
| **refresh** | Everything in view, plus restart and refresh data |
| **edit** | Everything in refresh, plus modify working draft, save/restore versions, access App History |
| **manage** | Everything in edit, plus promote versions to production, delete, change access, transfer ownership |

**Common patterns:**

```python theme={null}
# Make app visible to all standard users
apps_action(action="ACCESS_GRANT", app_id="my-app",
            role_names=["user"], privileges=["view"])

# Grant a specific user edit + manage
apps_action(action="ACCESS_GRANT", app_id="my-app",
            principal_ids=["alice@company.com"],
            privileges=["view", "edit", "manage"])

# List current access
apps_action(action="ACCESS_LIST", app_id="my-app")
```

### Published vs. Draft

* **Draft** (default): Only the owner can see the app in the Apps panel.
* **Published**: The app appears in the Apps panel for anyone with `view` access.

```python theme={null}
apps_action(action="SET_PUBLISHED", app_id="my-app", published=True)
```

***

## Embedding Apps in Chat

Embed a running GenesisApp directly in a chat message using a fenced code block:

````
```app sales-dashboard
```
````

For **genesis-versioned** apps, use the `--working-copy` flag when you want the user to
preview unpublished draft changes instead of production:

````
```app sales-dashboard --working-copy
sales-dashboard
```
````

After `WRITE_FILE` or `WRITE_FILES` on a versioned app, `apps_action` returns this embed
block in the `reminders` array — include it verbatim in your next reply so the user can
review the draft inline.

This renders the app inline in the conversation — great for presenting results to stakeholders without them needing to navigate to the Apps panel.

***

## Diagnostics and Troubleshooting

### REVIEW (One-Stop Diagnostics)

The `REVIEW` action combines verification, screenshot, and log analysis:

```python theme={null}
apps_action(action="REVIEW", app_id="my-app")
```

For new dashboard workspaces, `REVIEW` also clears the one-time first-start gate and can block when `ATTEST_CHECKLIST` proof is missing, contradictions remain between decision and evidence, or structural findings are still unresolved.

When a review runtime is already running but its `last_started_fingerprint` no longer matches `current_source_fingerprint`, Genesis stops the stale runtime and provisions a fresh one so `REVIEW` evaluates the current working copy. If the app was already serving that runtime, the replacement runtime is kept running after `REVIEW` completes instead of being torn down like a throwaway preview.

For truly static/no-data Dash apps, pass `no_data_declared=true` on `REVIEW`
only when the workspace has no Genesis dataset binding, live-query path, or
`/api/datasets` call. If the source scan agrees, the response sets
`no_data_attestation_bypass=true` and clears the attestation gate from health
plus rendered-content evidence instead of checklist proof.

When START or RESTART reports a blocking gate and you want the safest next move, call `apps_action(action="EXPLAIN_BLOCKING", app_id="my-app")`. It summarizes the active blocker and recommends the next `apps_action` call instead of making you infer it from raw state.

For apps that render charts or other content asynchronously, pass `wait_for_render_signals` such as `{"chart_data_detected": true}` or `{"plotly_chart_count": {"gte": 2}}` so capture waits for semantic readiness instead of a fixed delay. Prefer semantic signals over exact counts when you can. Capture-style responses echo both `render_signals` and `wait_signals` so you can see which semantic wait condition completed. Setting `include_visual_description=true` adds extra latency because Genesis runs a follow-up vision pass after the screenshot is taken.

Genesis Desktop's first-use browser-tools download during app capture or runtime
inspection is described under [Browser Tools](/docs/Genbot-Toolkit/Browser-Tools).

The [durable chat uploads guide](/docs/deployments/Databricks/Genesis-On-Databricks#durable-chat-uploads)
also covers screenshot persistence for `CAPTURE` and automatic capture after
`START` or `RESTART`.

For Dash dashboards, Genesis does not switch into presentation-deck review logic just because capture payloads include empty `genesis_export_*` fields. Presentation handling applies only when the app runtime is actually `presentation_deck` or capture data contains a real presentation value such as `genesis_export_slide_kind` or `genesis_export_blockers`, so dashboard-first review can still clear from positive render signals like `chart_data_detected`, `ag_grid_row_count`, and non-zero KPI values.

`[GENESIS_QUERY_OK]` and `[GENESIS_DATASET_OK]` app log markers are useful diagnostics, but `REVIEW` does not require them as the only proof. For dataset-backed dashboards, Genesis also inspects bound dataset metadata and readable snapshot rows. If neither markers, dataset readiness, nor populated capture evidence are available, `REVIEW` returns hints for the agent: bind or refresh a materialized dataset, keep/add a live-query marker, make populated runtime data visible to capture, rerun after async callbacks finish, and inspect dataset/query/callback errors.

For narrated decks, use `SWEEP_CAPTURE` when you want a whole-deck audit instead
of a single screenshot. If both `slide` and `slide_range` are omitted, Genesis
captures the full `presentation_deck.json` range automatically, `stage_sweep`
defaults to `true`, and the response returns `summary`, per-slide `slides[]`,
`failed_slides`, `slides_with_error_banners`, and `recommended_next_steps`.

### Interactive browser flows

For multi-step validation on a running GenesisApp, use [Browser Tools](/docs/Genbot-Toolkit/Browser-Tools) alongside `apps_action` instead of relying only on `CAPTURE` or `REVIEW`. This is the preferred path when you need to click buttons, fill forms, move between pages, or confirm post-click UI state.

Open the app proxy URL with `browser_open`, for example `browser_open(url="/api/apps/<app_id>/proxy/")`, then use `browser_snapshot`, `browser_click`, and `browser_type` within the same session. Because that proxy path resolves to the local Genesis backend, `browser_open` automatically adds the `X-Genesis-Internal-Token` header for that local origin. Finish with `browser_close` when you are done.

It returns:

* **verify**: Health check results (proxy reachability, healthz status, Dash layout validation)
* **capture**: Screenshot of the app
* **logs**: Recent log output
* **findings**: Auto-detected issues (callback 500s, query errors, shape mismatches)
* **hints**: Actionable fix suggestions

### Common Issues

For `WRITE_FILE` and `WRITE_FILES`, history placeholders are refused rather
than written as file content. See the [file-write placeholder guidance](/docs/Genbot-Toolkit/Git-Action-Tools#file-write-placeholder-guidance).

| Symptom | Likely Cause | Fix |
| - | - | - |
| App shows blank page | First-load query still running | Wait a few seconds, then CAPTURE again |
| First `START` / `RESTART` of a presentation deck is blocked | The one-time deck first-start gate is still active, or the required pre-start validation / auto-capture flags were omitted | Retry with `validate_before_restart=true` and `auto_capture_after_restart=true`, then fix any validation or auto-capture findings until the gate clears |
| Presentation `REVIEW` reports splash/loading or an empty slide | First-start auto-capture inspected slide `1` but the deck still showed splash/loading, had no slide visual, or visible text was stored outside canonical `t`/`s` fields | Move visible text into `t`/`s`, keep narration in `n`, or add `img`, `plotly`, `py`, or staged `label`/`sub` content on slide `1`, then rerun `REVIEW` or retry gated `START` / `RESTART` |
| First-start capture reports broken deck subresources | Slide `1` loaded but critical assets such as images, scripts, or styles failed (font-only failures are ignored) | Fix missing `presentation_images/*` paths, restore `presentation.js` / `presentation_styles.css`, or repair broken slide asset URLs, then retry gated `START` / `RESTART` |
| `DEV_QUERY_PREFLIGHT` returns `pragma_unsupported` | Preflight does not execute `PRAGMA` statements | Replace the probe with `SELECT * FROM <table> LIMIT 1` and inspect the returned `columns` for schema discovery |
| `presentation_deck_generate_tts` returns `No slides contained narration text` | Narration was stored in alias fields such as `narration` or `speaker_notes` instead of canonical `n` | Move narration into `n`, then rerun TTS generation. `WRITE_FILE`, `WRITE_FILES`, and presentation validation warn about these aliases. |
| `WRITE_FILE` / `WRITE_FILES` returns `GenesisApp security review is unavailable; blocking file.` with `security_findings[].rule_id="llm_app_code_guard"` | The auxiliary LLM security judge could not complete — for example the active provider is not configured, the provider call failed, or the model returned an empty response | Open **Config → AI Providers**, verify the active chat provider and **Test Connection**, then retry the write. This is an upstream LLM outage, not a code-pattern denial |
| `WRITE_FILE` / `WRITE_FILES` returns `GenesisApp security policy blocked this file.` with `security_findings[].rule_id="llm_app_code_guard"` | The LLM security review flagged host probing, subprocess launch, secret dumping, or other behavior outside the GenesisApps baseline | Rewrite the file to stay inside the workspace and call Genesis through `GENESIS_API_BASE_URL` plus `GENESIS_APP_TOKEN` (see the shipped scaffold helpers), then retry |
| `file(...)` mutation returns `blocked_by="microapp_apps_repo_mutation_guard"` | The call used `repo_id="apps"` with a registered app workspace path such as `chat_123/my-app/app.py` or `my-app/app.py`, which can diverge from the live microapp working copy | Retry with `apps_action(action="WRITE_FILE", app_id="my-app", ...)` or `apps_action(action="WRITE_FILES", app_id="my-app", ...)`, or use `file(..., repo_id="my-app")` so Genesis routes the write through the microapps service |
| `MICROAPP_BASE_PATH` 404 | Routes not mounted under prefix | Set Dash `requests_pathname_prefix` or Flask `url_prefix` to `MICROAPP_BASE_PATH` |
| `Datasets are required` error on START | Dashboard scaffold needs dataset bindings | Run `DATASETS_SETUP` first, or pass `allow_no_datasets=True` with `override_reason` |
| Start blocked because the dataset underneath this app is missing | `genesis_datasets.json` points to a deleted dataset, an unreadable binding payload, or a dataset the startup identity can no longer read | Repair or recreate the dataset binding, then retry `START` / `RESTART`. `EXPLAIN_BLOCKING` can summarize the current blocker and next step. |
| Dashboard REVIEW lacks data evidence | No app marker, bound dataset readiness, or populated capture evidence was found | Bind/refresh `genesis_datasets.json`, use `[GENESIS_QUERY_OK]` for intentional live-query apps, or rerun REVIEW/CAPTURE after async callbacks render charts, AG Grid rows, or KPI values |
| `RESTART` or `REVIEW` blocked after edits to helper `.py` files or deck runtime assets | A prior `REVIEW` passed for an older source fingerprint, or a stale review runtime was still running | Rerun `REVIEW`. Genesis fingerprints all workspace `.py` modules plus `presentation.js` and `presentation_styles.css` when present, and replaces a running review runtime when `last_started_fingerprint` differs from `current_source_fingerprint`. |
| `All arrays must be of the same length` | Pandas DataFrame construction mismatch | Align column arrays before creating DataFrame |
| `GENESIS_QUERY_ERR` in logs | SQL error in query | Validate SQL with a small `SELECT ... LIMIT 5` first |
| TTS generation warns about persistence failures | Audio was created in the runtime layer but not saved back to source storage | Fix `MICROAPP_SOURCE_DIR` write access, then rerun `presentation_deck_generate_tts` with `overwrite=true` |
| `apps_action(LIST)` shows one app with `status: "unknown"` | Status lookup failed for that registry entry (legacy non-canonical key, corrupted workspace, or similar) | Read the per-app `error` field, then call `STATUS` or `EXPLAIN_BLOCKING` on that `app_id`. Other apps in the same LIST response are unaffected. |

***

## Best Practices

<CardGroup cols={2}>
  <Card title="Use Datasets for Dashboards" icon="bolt">
    Avoid hammering your warehouse. The Datasets fast layer caches data in SQLite snapshots — dashboards load in milliseconds.
  </Card>

  <Card title="Restart After Code Changes" icon="arrows-rotate">
    Code and dependency changes only take effect after RESTART. Use `restart_after_write=true` to combine write + restart.
  </Card>

  <Card title="Read the Skill First" icon="book-open">
    The `apps-build-basics` skill contains the canonical GenesisApps workflow. Build and runtime actions require an active app skill in thread context; `CREATE` may register the app first, then `CHECK_PREREQUISITES` shows the activation step before scaffold/write/start work. For hosted MCP or other stateless callers, use the gateway `read_skill` or `list_skill_files` actions instead of relying on `manage_context(action="ADD_CONTEXT", ...)`, then retry `apps_action`.
  </Card>

  <Card title="Verify Before Sharing" icon="magnifying-glass">
    Always run REVIEW before publishing. It catches proxy issues, callback errors, and data-loading failures automatically.
  </Card>
</CardGroup>

***

## Parameters Reference

### `apps_action` Parameters

| Parameter | Type | Used By | Description |
| - | - | - | - |
| `action` | string | All | **Required.** The operation to perform. |
| `app_id` | string | All except LIST, TEMPLATES, LIST\_DEPLOY\_TARGETS | **Required.** Application identifier (1–64 characters; lowercase letters, numbers, and hyphens; must start and end with a letter or digit). Genesis normalizes `app_id` on input. |
| `runtime_type` | string | CREATE, DEPLOY | For CREATE: `python_template` (default), `dash_dashboard`, or `presentation_deck`. For DEPLOY: `eks`, `railway`, `docker`, or `databricks_apps` (also accepted as `databricks` or `databricks-apps`). Optional when `dataset_definition.target_env` already specifies the backend. |
| `template` | string | SCAFFOLD | Template name (call TEMPLATES to list) |
| `emoji` | string | CREATE, SET\_EMOJI | Emoji icon for Apps UI (e.g. `📊`) |
| `embed_mode` | string | CREATE | Chat embed: `auto`, `responsive`, or `scaled` |
| `fullscreen_mode` | string | CREATE | Fullscreen modal: `auto`, `responsive`, `contain`, or `cover` |
| `display_name` | string | CREATE | Optional human-readable app title. When provided on CREATE, Genesis preserves it instead of synthesizing a title from `app_id`. |
| `repo_id` | string | CREATE | Store app source in a bot\_git repo |
| `repo_relative_path` | string | CREATE | Subdirectory within repo\_id |
| `replace_existing` | boolean | CREATE | Stop + unregister existing app first |
| `delete_workspace` | boolean | DELETE, DELETE\_APP | Also remove workspace files after unregistering. Default `false` preserves source files for recovery/audit. |
| `genesis_api_base_url` | string | START, RESTART | Override Genesis API base URL |
| `restart_if_running` | boolean | START | Stop first if already running |
| `validate_before_restart` | boolean | START, RESTART | Run `VALIDATE` before restart and block when errors exist. Required together with `auto_capture_after_restart=true` for the first `presentation_deck` start gate. |
| `validate_check_images` | boolean | VALIDATE, START, RESTART | Enable image/path validation. Defaults to `true`. |
| `validate_check_audio` | boolean | VALIDATE, START, RESTART | Enable audio/captions validation. Defaults to `true`. |
| `tail_lines` | integer | LOGS | Lines to return (default 200) |
| `since_last_start` | boolean | LOGS | Only logs since last START/RESTART |
| `timeout_seconds` | integer | CAPTURE, SWEEP\_CAPTURE | Screenshot wait time (3–120 seconds). When `include_visual_description=true`, Genesis allows extra capture time for the follow-up vision pass. |
| `url_suffix` | string | CAPTURE, SWEEP\_CAPTURE | Appended to proxy URL before capture |
| `click_selector` | string | CAPTURE, SWEEP\_CAPTURE | CSS selector to click before screenshot |
| `wait_for_selector` | string | CAPTURE, SWEEP\_CAPTURE | CSS selector to wait for |
| `wait_for_text` | string | CAPTURE, SWEEP\_CAPTURE | Text to wait for in the DOM |
| `wait_for_render_signals` | object | CAPTURE, SWEEP\_CAPTURE, REVIEW, START, RESTART | Optional render-signal conditions such as `{"chart_data_detected": true}` or `{"plotly_chart_count": {"gte": 2}}`. Prefer semantic readiness signals over exact counts. |
| `include_visual_description` | boolean | CAPTURE, SWEEP\_CAPTURE, REVIEW, START, RESTART | Include a vision-generated description with the screenshot. This adds extra latency because capture runs a follow-up vision pass. |
| `slide` | integer | CAPTURE, SWEEP\_CAPTURE | Optional 1-based presentation-deck slide number to capture. When omitted and `slide_range` is also omitted on `SWEEP_CAPTURE`, Genesis captures the full deck. |
| `stage` | integer | CAPTURE, SWEEP\_CAPTURE | Optional 1-based stage number to capture for a staged slide. Use with `slide` when you want one specific stage. |
| `slide_range` | string | VALIDATE, CAPTURE, SWEEP\_CAPTURE, REVIEW, START, RESTART | Optional inclusive 1-based presentation slide range such as `"1-6"`. For START/RESTART, Genesis forwards it into pre-start validation when `validate_before_restart=true` and into auto-capture when `auto_capture_after_restart=true`. |
| `stage_sweep` | boolean | VALIDATE, CAPTURE, SWEEP\_CAPTURE, REVIEW, START, RESTART | Optional presentation-deck stage sweep. For VALIDATE, it requires `target_slide` or `slide_range` and checks that at least one targeted slide is staged with non-empty `stages`. For capture flows, it captures every stage for the targeted slide(s). |
| `target_slide` | integer | VALIDATE, START, RESTART | Optional 1-based presentation-deck slide target used by `VALIDATE` and by START/RESTART when `validate_before_restart=true`. |
| `target_stage` | integer | VALIDATE, START, RESTART | Optional 1-based presentation-deck stage target used by `VALIDATE` and by START/RESTART when `validate_before_restart=true`. Requires `target_slide`. |
| `checklist_answers` | array | ATTEST\_CHECKLIST | Legacy free-text attestation payload for `dashboard_readiness_v1` workspaces only. New dashboard scaffolds use `checklist_attestation`. |
| `checklist_attestation` | object | ATTEST\_CHECKLIST | Typed `dashboard_readiness_v2` attestation envelope with `contract_id`, `items[]`, and optional `structural_overrides[]`. Each item includes `id`, `answer`, `decision`, and `evidence`. Inspect `data_plausibility_checklist.questions[*]` for exact `decision_fields`, `evidence_requirements`, `example_item`, and `override_supported`, and use `data_plausibility_checklist.structural_overrides_help` for the override envelope rules. |
| `dry_run` | boolean | ATTEST\_CHECKLIST | Validate typed attestation evidence without persisting an attestation record. The response echoes `dry_run=true`, `validation_run_only=true`, and the same item validation feedback a real call would use. |
| `compact` | boolean | REVIEW, ATTEST\_CHECKLIST, DEV\_QUERY\_PREFLIGHT | Prefer slimmer response payloads. For `ATTEST_CHECKLIST`, compact failures always return `failing_item_ids`; when you explicitly set `compact=true`, Genesis keeps the failure minimal with fields such as `compact_item_errors`, `corrected_minimal_examples` / `corrected_example_items`, `attempted_attestation_context`, and `schema_followup`. Pass `compact=false` when you need the full checklist schema, detailed validation objects, and example payloads. |
| `profile_mode` | string | DEV\_QUERY\_PREFLIGHT, DATASETS\_SETUP | Structural profiling mode: `both`, `structural`, `latency`, or `off`. Use `both` on `DEV_QUERY_PREFLIGHT` for latency plus structural checks together. |
| `structural_profile_config` | object | DEV\_QUERY\_PREFLIGHT, DATASETS\_SETUP | Optional vendor-neutral structural profiling config. It can include `entity_key`, `base_relation`, `time_column`, `period_start_column`, `period_end_column`, `expected_end_mode`, `expected_end`, `raw_query_label`, `dedup_query_label`, `query_roles`, and `query_specs`. Each `query_spec` can include `role`, optional `label`, `query` or `sql`, optional `connection_id`, and per-spec overrides `entity_key`, `time_column`, `period_start_column`, `period_end_column`, `expected_end`, and `expected_end_mode`. `query_roles` accepts single-label or multi-label arrays; all labels for a role are checked and collisions surface non-blocking warnings. When both `expected_end` and `expected_end_mode` are omitted, Genesis can infer `expected_end_mode` from `refresh_policy` on the profile config or on a bound dataset when available. |
| `overwrite` | boolean | SCAFFOLD | Overwrite existing workspace files |
| `dataset_id` | string | SCAFFOLD | Optional existing dataset ID to bind immediately while scaffolding. Genesis writes `genesis_datasets.json` with default `read` + `ssrm` actions unless you override them with `dataset_actions`. |
| `path` | string | WRITE\_FILE, COPY\_FILE | Workspace-relative file path |
| `content` | string | WRITE\_FILE | File content. For binary `.png` or `.pdf` files, send base64 text and set `is_base64=true`. A `.pdf` must decode to bytes starting with `%PDF-` and be no larger than 2 MiB. Omit when writing from `source_repo_id` + `source_path` / `source_file`. |
| `is_base64` | boolean | WRITE\_FILE | Decode `content` from base64 before writing; use for binary `.png` and `.pdf` files. For `WRITE_FILES`, set this on the individual file entry. |
| `files` | array | WRITE\_FILES | Array of file specs. Each item can provide inline `content`, repo-backed `source_path` / `source_file`, and optional per-file `substitutions`. |
| `restart_after_write` | boolean | WRITE\_FILE(S) | Restart after writing |
| `source_repo_id` | string | SYNC\_FROM\_REPO\_ROOT, COPY\_FILE, source-backed WRITE\_FILE(S), IMPORT\_PRESENTATION | Git repo to copy from |
| `source_path` | string | COPY\_FILE, source-backed WRITE\_FILE(S), IMPORT\_PRESENTATION | Repo-relative source path to import |
| `source_file` | string | COPY\_FILE, source-backed WRITE\_FILE(S), IMPORT\_PRESENTATION | Alias of `source_path` |
| `substitutions` | object | COPY\_FILE, WRITE\_FILE, WRITE\_FILES | Optional string-to-string placeholder map applied server-side before text files are persisted. On `WRITE_FILES`, the top-level map acts as a fallback and each file entry can override it with its own `substitutions`, including an explicit empty `{}` to opt out. |
| `dataset_definition` | object | DATASETS\_SETUP, DEPLOY | For DATASETS\_SETUP: either a dataset create definition or a binding shortcut such as `{"dataset_ids":["sales_dashboard_data"]}` for existing datasets. Create definitions create and refresh the dataset before binding; binding shortcuts write `genesis_datasets.json` and restart without creating a dataset. Shortcut payloads can also include `actions` and optional `connection_ids`. For DEPLOY: backend options. Preferred: `{"target_env":"<name>","security_profile":"<name>"}` to reuse saved profiles. Raw keys include `credential_name`, `cluster` / `cluster_name`, `region`, `namespace`, `service_type`, `host_port`, `railway_token`, `project_id`, and `environment_id`. Databricks Apps also accept `databricks_connection_id`, `warehouse_id`, `grant_schema`, and `app_name`; see [Databricks Apps](#databricks-apps). For EKS, DEPLOY defaults `service_type` to `LoadBalancer`; set `ClusterIP` for internal-only access. |
| `confirm_identity_delegation` | string | DEPLOY | Must be `"true"` after deployment is confirmed. For container backends, acknowledge that the app uses the caller's delegated Genesis identity and permissions; Databricks Apps use their own service principal instead. |
| `dataset_actions` | array | SCAFFOLD, DATASETS\_SETUP | Allowed actions: `read`, `ssrm`. When omitted for scaffold-time binding or the DATASETS\_SETUP binding shortcut, Genesis defaults to both. |
| `allow_no_datasets` | boolean | START, RESTART | Allow Dash dashboard without datasets |
| `no_data_declared` | boolean | REVIEW | For static/no-data dashboards, explicitly declare that the app has no Genesis data API path. REVIEW bypasses checklist attestation only when the source scan also finds no dataset bindings, live queries, or `/api/datasets` calls. |
| `override_reason` | string | START, RESTART | Required when `allow_no_datasets=true` |
| `verify_data` | boolean | VERIFY | Include data-aware checks from logs |
| `published` | boolean | SET\_PUBLISHED | `true` = published, `false` = draft |
| `role_names` | array | ACCESS\_GRANT/REVOKE | Role names to update |
| `principal_ids` | array | ACCESS\_GRANT/REVOKE | User IDs for direct grants |
| `privileges` | array | ACCESS\_GRANT/REVOKE | `view`, `edit`, `manage` |
| `new_owner_identity` | string | TRANSFER\_OWNERSHIP | New owner user ID/email |
| `ttl_seconds` | integer | CREATE, UPDATE\_RUNTIME\_TTL | For CREATE, idle TTL in seconds (default `28800`, or 8 hours). For UPDATE\_RUNTIME\_TTL, use `300`–`86400`; pass `-1` for never auto-stop. |
| `limit` | integer | LIST, LIST\_CHANGE\_HISTORY | For `LIST`, maximum apps to return (default `50`, hard cap `50`). For `LIST_CHANGE_HISTORY`, maximum change records to return (default `20`). |
| `change_id` | string | RESTORE\_TO\_CHANGE | Target checkpoint ID from `LIST_CHANGE_HISTORY` |
| `run_source` | string | STATUS, LOGS, URL, VERIFY, CAPTURE, SWEEP\_CAPTURE, INSPECT\_RUNTIME\_STATE, REVIEW, START, RESTART | `"production"` (default) or `"working_copy"` for versioned apps |
| `auto_capture_after_restart` | boolean | START, RESTART | Capture a screenshot after starting. Required together with `validate_before_restart=true` for the first `presentation_deck` start gate. |
| `auto_capture_after_start` | boolean | START | Capture a screenshot immediately after `START` and return render diagnostics; use this as the canonical START flag, with `auto_capture_after_restart` retained as a compatibility alias. |
| `version_id` | string | DEPLOY, PROMOTE\_VERSION, RESTORE\_VERSION, UPDATE\_VERSION\_NOTE | For DEPLOY: optional saved version to deploy remotely; when omitted, DEPLOY targets Production. For the versioning actions, target saved version ID. |
| `restart_after_promote` | boolean | PROMOTE\_VERSION | Restart production runtime after promoting |
| `note` | string | ENABLE\_VERSIONING, CREATE\_VERSION, PROMOTE\_VERSION, UPDATE\_VERSION\_NOTE | Human-readable description |
| `assign_production` | boolean | ENABLE\_VERSIONING | Auto-promote v1 to production (default `true`) |
| `force` | boolean | RESTORE\_VERSION, DISCARD\_WORKING\_COPY | Skip confirmation when draft has unsaved changes |

### `presentation_deck_generate_tts` Parameters

| Parameter | Type | Description |
| - | - | - |
| `app_id` | string | **Required.** Presentation deck app ID. |
| `voice` | string | OpenAI TTS voice (default: `nova`) |
| `model` | string | TTS model (default: `tts-1`) |
| `format` | string | Audio format (default: `mp3`) |
| `overwrite` | boolean | Overwrite existing MP3 files (default: `false`) |
| `slides` | integer\[] | Optional 1-based slide numbers to regenerate |
| `voice_match` | boolean | Reuse slide/stage voice and model metadata when regenerating selected slides (default: `true`) |
| `use_genesis` | boolean | Use Genesis-configured OpenAI key (default: `true`) |
| `api_key` | string | Optional fallback OpenAI key or stored `secret_*` reference. Use only when `use_genesis=false`. |
| `timeout_seconds` | integer | Generation timeout (default: 600) |
