Dataface Tasks

Eliminate all custom HTML - dataface YAML everywhere

IDINSPECT_PROFILER-ELIMINATE_CUSTOM_HTML_STATUS_PAGES_USE_DATAFACE_YAML_FOR_ALL_INSPECTOR_UI
Statuscompleted
Priorityp2
Milestonem1-ft-analytics-analyst-pilot
Ownersr-engineer-architect
Completed bydave
Completed2026-03-17

Problem

Custom hand-crafted HTML exists in multiple places across the extension and server. Every rendered page should be a dataface page — this simplifies the codebase, ensures consistent look/feel, and establishes a clean invariant: dft serve never returns non-dataface HTML.

Custom HTML audit (complete inventory)

Location What Lines Action
inspector-pages.tsrenderCacheMissPage "No profile" page with button ~40 Replace with conditional layout in model.yml
inspector-pages.tsrenderErrorPage Error with retry/dbt/profile buttons ~35 Replace with error template
inspector-pages.tsrenderLoadingPage Spinner while inspecting ~20 Replace with static SVG (media/icons/loading-spinner.svg)
inspector-pages.tsrenderIdlePage "Open a SQL file" placeholder ~5 Replace with static SVG or minimal static file
renderer.pyrender_cache_miss_page() Server-side "no profile" HTML ~50 Delete — template handles both states
preview-manager.tsrenderError() Preview error (CLI not found, render failure) ~120 Replace with error dataface template
preview-manager.tswrapInWebview() Webview shell (toolbar, CSP) ~80 Keep — VS Code webview container, not a dataface page
server.py_render_directory_listing() File browser at / ~25 Replace with dataface template using markdown content:
html.pyto_html() Core render pipeline HTML wrapper ~30 Keep — this IS the dataface render output

Goal: after this task, only wrapInWebview() and to_html() produce HTML — everything else is dataface YAML.

Bugs this fixes

  • "Profile this table" button goes blank — the extension replaces server HTML with its own TS-rendered version, triggers CLI profiling, and the state machine races with editor change listeners
  • Duplicate cache-miss page — exists in both inspector-pages.ts and renderer.py

Context

Philosophy docs consulted

  • .cursor/rules/anti-slop.mdc — delete duplicate render paths instead of wrapping them
  • .cursor/rules/core.mdc — write regression tests first and keep error states explicit
  • docs/docs/contributing/architecture/index.md — app surfaces call into core render/serve paths
  • docs/docs/contributing/architecture/platform-overview.md — keep engine logic in dataface/core, app-specific webview plumbing in apps/ide
  • apps/cloud/DESIGN_PHILOSOPHY.md — server-rendered/YAML-first UI is the intended direction even for transient UI states

Existing conditional layout feature

Dataface already supports include: on any board/chart (see docs/variables/advanced.md):

rows:
  - include: "{{ overview_path }}"
    rows: [inspector_overview, ...]
  - include: "{{ not overview_path }}"
    content: |
      **No profile for {{ model }}**
      Run `dft inspect table {{ model }}` to generate one.

This is the natural way to handle "no data" states in dataface — no new features needed.

Server directory listing as dataface

The _render_directory_listing() in server.py is ~25 lines of custom HTML for a file browser. This becomes a dataface template with a table chart — the server passes discovered files/dirs as inline data and renders a table with clickable links. This means dft serve has a clean invariant: every route returns a dataface-rendered page.

Key files

  • apps/ide/vscode-extension/src/inspector/inspector-pages.ts
  • apps/ide/vscode-extension/src/inspector/inspector-panel.ts
  • apps/ide/vscode-extension/src/utils/path-validation.ts
  • apps/ide/vscode-extension/src/preview/preview-manager.ts (renderError)
  • dataface/core/inspect/renderer.py (render_cache_miss_page)
  • dataface/core/inspect/templates/model.yml
  • dataface/core/serve/server.py (inspect routes + _render_directory_listing)
  • tests/core/test_inspect_cache_first.py
  • tests/core/test_inspect_server.py
  • apps/ide/vscode-extension/src/inspector/inspector-panel.test.ts
  • apps/ide/vscode-extension/src/preview/preview-manager.test.ts

Possible Solutions

Extend model.yml to handle the "no profile" state using the existing include: conditional layout. The Jinja pre-processing already knows whether the model has profile data (via overview_path being empty or not). The cache-miss state becomes a content: block in the same template.

The extension always goes through the server for inspect states. For preview errors, render a tiny temporary YAML face through the normal CLI/Python render path instead of hand-writing HTML. Keep only the VS Code webview shell around the rendered page. Loading/idle inspector states stay as static bundled assets because they are not data pages and never go through the server.

Pros: Zero new features, uses existing patterns, eliminates all custom HTML Cons: Preview needs a small temp-file render helper; if both dft and Python are unavailable, there is no runtime to render the error face

Option B: First-class fallback: / empty: on queries/boards

Add an empty: property to queries or boards that renders alternative content when a query returns no data or fails. More powerful long-term (works for any dashboard), but requires new compile/render work and is a separate feature.

Pros: Reusable across all dashboards, handles any "no data" scenario Cons: New feature to design/build/test, scope creep for this task

Option C: redirect: with condition

Add a redirect: field to YAML spec for routing. Over-engineered — introduces server-side routing into a declarative viz layer.

Pros: Clean separation of concerns Cons: Wrong abstraction level, adds complexity

Plan

Use Option A — conditional layout in templates, no new features. Three areas of work:

Part 1: Inspector status pages

  1. Modify model.yml — wrap dashboard content in the conditional include: pattern shown above (overview vs cache-miss branch), showing the "no profile" message and dft inspect table command
  2. Update renderer.py — delete render_cache_miss_page(), have the server always render the template (which now handles both states)
  3. Update server.py — remove the data-cache-miss detection path, always return rendered template HTML
  4. Simplify inspector-panel.ts — remove data-cache-miss string matching, remove TS-rendered showCacheMissPrompt, always use server response. Loading state uses the static SVG spinner at media/icons/loading-spinner.svg
  5. Delete inspector-pages.ts entirely — all four render functions replaced: cache-miss/error by YAML template, loading/idle by static SVG
  6. Fix the "Profile" button — the button in the YAML-rendered cache-miss page triggers profiling via POST /inspect/profile/, then refresh. Extension intercepts the form POST via webview message

Part 2: Preview error page

  1. Replace preview-manager.ts:renderError() — render error states through a dataface YAML error face (a simple content: block with markdown showing the error title, summary, and setup instructions). Use the same runtime fallback order as the inspector (dft, then python -m dataface.cli.main). The wrapInWebview() shell stays (it's the VS Code container, not a page)

Part 3: Server directory listing

  1. Replace server.py:_render_directory_listing() — create a built-in _directory.yml template with a table chart. The server passes discovered entries as inline query data (name, type, link) and the table renders them with clickable links. Every dft serve route now returns dataface-rendered output

Tests & invariant

  1. Tests — update test_inspect_cache_first.py and test_inspect_server.py for template-based states. Add a test for directory listing rendering
  2. Enforce invariant — after this task, only two places produce raw HTML: to_html() (core render pipeline) and wrapInWebview() (VS Code container). Everything else is dataface YAML. Document this in architecture docs

Implementation Progress

  • 2026-03-17: Read the required philosophy/architecture docs, audited the current inspector/server/preview render paths, and confirmed the three remaining non-pipeline HTML producers targeted by this task: inspector-pages.ts, render_cache_miss_page(), and _render_directory_listing(). Also identified that preview errors can use the existing Python-path fallback rather than treating a missing dft binary as a hard stop.
  • 2026-03-17: Moved the inspect cache-miss state into dataface/core/inspect/templates/model.yml using include: layout branches plus a POST form to /inspect/profile/. Deleted render_cache_miss_page() and removed the server-side cache-miss HTML special case so /inspect/model/ always goes through the normal inspect template render path.
  • 2026-03-17: Replaced _render_directory_listing() with a built-in dataface page at dataface/core/serve/templates/directory.yml, rendered via the standard compile/render pipeline with generated markdown links for directories, parent navigation, and .yml / .yaml faces.
  • 2026-03-17: Deleted apps/ide/vscode-extension/src/inspector/inspector-pages.ts. Inspector errors now render through a shared temporary markdown-face helper, while loading/idle states are reduced to static extension-owned shell pages. The webview now intercepts dataface-rendered inspect links, command buttons, and profile form submissions.
  • 2026-03-17: Preview errors now render through the same shared markdown-face helper, and preview dashboard rendering now uses the same runtime fallback order as the inspector (dft, then python -m dataface.cli.main).
  • 2026-03-17: Validation run:
  • uv run pytest tests/core/test_inspect_cache_first.py tests/core/test_inspect_server.py -q
  • npm run test:unit -- src/inspector/inspector-panel.test.ts src/preview/preview-manager.test.ts
  • npm run compile

QA Exploration

  • N/A: this task touches FastAPI HTML responses and VS Code webviews, not a standalone browser app that can be exercised through the repo's Playwright QA flow.
  • [x] QA exploration completed (or N/A for non-UI tasks)

Review Feedback

  • 2026-03-17: cbox review was attempted repeatedly but failed due review infrastructure, not branch-specific findings. One live review session completed exploration and then hit repeated Claude API 500 Internal server error failures during final synthesis. A clean --model sonnet retry then stalled in review-container bootstrap before the reviewer launched.
  • 2026-03-17: PR #615 is green and mergeable after scripts/pr-validate pre, scripts/pr-validate post 615, and GitHub checks completed successfully.
  • [x] Review cleared