Dashboard linking v1 (dashboard-root paths, render-time rewrite, docs)
Problem
Authors need one markdown link form that works in dft serve and Cloud without hand-building Suite URLs. Browsers resolve relative href against the dashboard page URL, not the repo slug tree—so we must rewrite board links at render time.
Decisions (explicit — do not drift)
| Topic | Decision |
|---|---|
| Author URLs | Root-relative from dashboard URL root: e.g. /zendesk/tickets/list?status=open, /zendesk/tickets/detail?id={{ ticket_id }}. Authors do not type faces/ in the common case. |
| List vs detail | Convention via path segments (…/list, …/detail, …)—not YAML metadata in v1. |
| Frontmatter overrides | No slug / url_path overrides in v1—file path is source of truth. |
| Markdown | Standard [text](url) only; optional suffix .md / .yml / .yaml stripped when resolving. |
| Canonical / entity registry | Out of scope — M5 follow-up only if proven necessary. |
Full spec: initiative spec. ADRs: decisions.
Context
- Research: landscape.
- Consumers: Quickstart dashboards; program setup depends on this task.
- Today: Cloud passes query params to variables; markdown
contentis Jinja +mdsvgwithout board-aware rewriting (dataface/core/render/faces.py).dft serve:/{path}→{path}.yml(dataface/core/serve/server.py).
Possible Solutions
- Render-time resolver in core +
link_contextfrom serve/Cloud — required; matches spec.
Plan
1. Contract + docs outline
- Freeze author ↔ storage slug mapping rules in spec open questions if needed.
- Docs: add
docs/docs/...page (or extend variables doc) covering: dashboard-root paths,../behavior, query = variables,branchmerge, list/detail naming convention, examples. Ship docs in the same PR / release as the resolver (or immediately after with a tracked follow-up—prefer same).
2. TDD — resolver (dataface/core/render/, e.g. board_links.py)
- Map
/zendesk/tickets/list→ storage slug used by Cloud/serve (e.g.faces/zendesk/tickets/list). ..//./from current board slug directory; suffix strip; query preserved;https:untouched; Cloud URL shape + branch merge.
3. Integrate pipeline
- Thread
link_contextthroughrender(..., **options)→ content/markdown path (faces.py, etc.).
4. Wire callers
dft serveanddashboard_rendersupply org/project/slug/branch as needed.
5. Templates + drill-down doc
- Quickstart link-matrix template —
/connector/.../listand…/detailexamples. - Drill-down example when
action: linkreuses resolver.
6. Agent skills (dashboard authoring)
Update product-facing skills so agents emit correct content links after ship (same rules as docs/docs/):
dataface/ai/skills/building-dataface-dashboards/SKILL.md— add a Cross-board links subsection: dashboard-root paths,../from current board, query params = target board variables,list/detailpath convention, pointer to initiative spec. Add a row to the Reference Guides table if a smallreferences/note is cleaner than bloating the main file..codex/skills/quickstart-product-dashboard-research/SKILL.md— align link-matrix / outbound-link guidance with the spec (already partially there via templates; refresh after resolver behavior is fixed).
If other agent surfaces duplicate these skills (e.g. sync’d copies), update them in the same change set per repo sync-skills practice.
7. Explicitly not in this task
canonical_for/ entity index → dashboard-linking-canonical-entity-metadata-m5.
Implementation Progress
- [x] Resolver —
dataface/core/render/board_links.py:LinkContextdataclass,resolve_href(),rewrite_board_links(). Root-relative + relative path resolution, suffix strip, serve/cloud URL shapes, branch merge. Usescontextvarsto avoid threading through deep layout call chains. - [x] Tests —
tests/core/test_board_links.py: 28 tests covering external passthrough, serve root-relative/relative, cloud root-relative/relative, suffix stripping, branch merge, markdown rewriting, context variable. - [x] Pipeline integration —
_render_content_svg()infaces.pycallsrewrite_board_links(resolved, get_link_context())after Jinja resolution, before mdsvg rendering. All face content (root + nested) is covered. - [x] Renderer wiring —
renderer.pysets/clearslink_contextfromrender(**options)viaset_link_context()+finallycleanup. - [x] Serve wiring —
server.py_render_face_file()buildsLinkContext(runtime="serve", ...)from file path and passes torender(). - [x] Cloud wiring —
dashboard_render()inviews.pybuildsLinkContext(runtime="cloud", org_slug, project_slug, branch)and passes throughcompile_and_render_dashboard(). - [x] Docs —
docs/docs/boards/linking.md: author-facing guide covering root-relative paths, relative paths, query/variable mapping, suffix strip, external passthrough, branch propagation, list/detail convention. - [x] Drill-down example — Updated
docs/docs/examples/drill-down.md"Link to URL" section to reference board linking with resolver syntax.
QA Exploration
N/A — Pure render-pipeline feature; link rewriting is fully covered by unit tests on the resolver and integration through _render_content_svg. No browser QA needed for the text-level markdown rewriting; Cloud and serve wiring will be exercised when dashboards with board links are authored.
- [x] QA exploration completed (or N/A for non-UI tasks)
Review Feedback
- [ ] Review cleared