Imported from cogni-work/insight-wave (
cogni-consult/skills/consult-dashboard/SKILL.md). Install upstream withnpx skills add cogni-work/insight-wave --skill consult-dashboard. Copyright stays with the author.
Consult Dashboard
Generate a self-contained HTML dashboard that visualizes a consulting engagement's
status — the action-field work breakdown, every deliverable's state and
design-thinking stage, acting-persona review progress, knowledge-base linkage, and
the recommended next action. The dashboard opens in the browser and is the visual
sibling of the text WBS table that consult-resume and consult-action-fields
render inline.
Core Concept
Engagement status lives in consult-project.json plus one field.json per action
field, where each deliverable carries its own state, dt_stage, and
persona_review. The text WBS table is good for a quick check; this dashboard is for
visual exploration — scanning field-by-field progress, spotting which deliverables are
stuck mid-loop, and seeing persona-review coverage at a glance.
Deliverable state is the single source of truth (it lives only in field.json);
field and engagement completion are derived at read time, never stored. The
generator is read-only — it never modifies any engagement file.
Workflow
0. Resolve the Interaction Language
Before any user-facing output, resolve the interaction language — the
workspace default, overridden by the user's message language — per
$CLAUDE_PLUGIN_ROOT/references/interaction-language.md, which owns the
resolution ladder. Conduct the entire conversation in the resolved language.
It is independent of the engagement's language field, which is the
deliverable axis. This contract holds on the default path: it does not depend
on an output style being active.
The description of a Bash tool call is rendered to the consultant, so it is
user copy — write it in the interaction language, outcome-shaped, at most 6
words, with no script, file, or skill names, and never derived from the
script's filename or header comment. Worked pair:
Discover cogni-consult engagements → Laufende Engagements holen.
Section (f) of the canonical ecosystem register owns these five constraints —
edit them there first, then mirror into
$CLAUDE_PLUGIN_ROOT/references/user-facing-output.md and here.
The dashboard document's own <html lang> attribute and language badge follow
the engagement's deliverable language, not the interaction language.
The register that output follows has two tiers. The overlay
$CLAUDE_PLUGIN_ROOT/references/user-facing-output.md carries this plugin's own
state lexicon, coinages and German step vocabulary, and opens with the command
that reads the canonical ecosystem register behind it — which owns scope, the
table contract, step announcements and brevity budgets, and the executive
register. Read the overlay and follow that command; table cells and headers are
user copy too, not an exemption.
1. Find the Active Engagement
Discover engagements with scripts/discover-projects.sh (the registry wrapper), or scan
for consult-project.json files under cogni-consult/ paths. If multiple engagements
exist, ask the user which one to open. Store the resolved engagement directory path.
2. Pick Theme
First, check whether <engagement-dir>/output/design-variables.json already exists from
a previous dashboard run. If it does, ask the user: "A dashboard theme is already
configured. Reuse it, or pick a new one?" Default to reuse — most re-runs just want fresh
data with the same look.
- If reusing: skip directly to step 4 (Generate the Dashboard).
- If picking new (or no design-variables exist): use the
cogni-workspace:manage-themes(Operation 11, Select Theme) skill to let the user select a theme. The skill returnstheme_path,theme_name, andtheme_slug.
Additional skip conditions (auto-select without prompting): the caller already
provided a theme_path, or only one theme exists in the workspace.
3. Generate Design Variables
Read the selected theme.md and produce a design-variables JSON file at
<engagement-dir>/output/design-variables.json, following the schema at
$CLAUDE_PLUGIN_ROOT/skills/consult-dashboard/schemas/design-variables.schema.json. See
the example at
$CLAUDE_PLUGIN_ROOT/skills/consult-dashboard/examples/design-variables-cogni-work.json.
This is the same design-variables contract the rest of the ecosystem uses, so the consult
dashboard inherits the same look as the portfolio dashboard. What the LLM adds beyond a
raw token extraction: derive surface2 (~4% darker than surface), accent_muted /
accent_dark variants from accent, a Google Fonts @import URL, dark-theme shadow
opacity, and WCAG-AA contrast between text and surfaces.
Required fields: theme_name, colors (13 keys), status (4 keys), fonts (3 keys).
Optional with defaults: google_fonts_import (empty), radius ("12px"), shadows.
4. Generate the Dashboard
python3 $CLAUDE_PLUGIN_ROOT/skills/consult-dashboard/scripts/generate-dashboard.py "<engagement-dir>" --design-variables "<engagement-dir>/output/design-variables.json"
The script reads consult-project.json and every action-fields/<slug>/field.json (the
same read model as engagement-status.sh), counts research syntheses under scope/research/
and action-fields/<slug>/research/, loads the design-variables JSON, writes a self-contained
HTML file at <engagement-dir>/output/dashboard.html, and prints
{"success": true, "data": {"path": ..., "theme": ..., "completion_pct": ...}, "error": ""}.
After the dashboard is written, resolve any {{asm:}} assumption placeholders it
carries so the status view shows registered values instead of raw markers. Run a
read-only dry-run resolve (omit --in-place) and write the returned resolved
text back to dashboard.html yourself — this rewrites only the generated output,
never the assumption registry:
# 1. Dry-run resolve → JSON on stdout (omit --in-place; assumptions.json stays untouched).
# 2. Extract data.resolved_text and overwrite dashboard.html with it — but only when
# placeholders were actually found, so a marker-free dashboard is a clean no-op.
python3 $CLAUDE_PLUGIN_ROOT/scripts/resolve-assumptions.py "<engagement-dir>" resolve "<engagement-dir>/output/dashboard.html" \
| python3 -c 'import json,sys; e=json.load(sys.stdin); d=e.get("data") or {}; (open("<engagement-dir>/output/dashboard.html","w",encoding="utf-8").write(d["resolved_text"]) if e.get("success") and d.get("placeholders_found",0)>0 else (None if e.get("success") else sys.stderr.write((e.get("error") or "resolve failed")+chr(10))))'
resolve-assumptions.py — the plugin-level scripts/ resolver, distinct from the
skill-local generate-dashboard.py above — reads assumptions.json and returns
each {{asm:<suffix>}} marker replaced by the registered value (and, where
present, its status and provenance) in data.resolved_text. Take that text and
overwrite dashboard.html with it; when data.placeholders_found is 0 the
dashboard has no markers and no write is needed (a clean no-op). Deliberately
omit --in-place: the in-place mode records a used_by[] reference edge back
into assumptions.json, which would both mutate engagement state (breaking the
read-only contract below) and pollute the register's backlink graph with an
ephemeral, overwrite-on-rerun render artifact. The dry-run keeps assumptions.json
untouched. The resolver is fail-loud on an unknown or malformed placeholder (exit
1, success:false with a data.failed_check discriminator) — surface that as a
warning rather than aborting the dashboard.
Legacy fallback: the script also accepts --theme <path-to-theme.md> (best-effort
markdown parse) for CI/automated runs. Precedence: --design-variables > --theme >
built-in default.
5. Open in Browser
open "<engagement-dir>/output/dashboard.html"
Tell the user the dashboard is open. To refresh after working on deliverables, just rerun the
script (re-running overwrites the previous output/dashboard.html), or let the
consult-dashboard-refresher agent regenerate it at a milestone — see Milestone Dashboard below.
Milestone Dashboard
The dashboard is also a checkpoint tool, not just a capstone. Once an engagement has a theme
configured (output/design-variables.json), the engagement skills offer a fresh dashboard at
natural milestones — consult-design-thinking after a deliverable reaches complete (or its
persona review closes), consult-action-fields after the WBS structure changes, and
consult-resume at re-entry. At those checkpoints the skill delegates to the
consult-dashboard-refresher agent (engagement_dir, plugin_root: $CLAUDE_PLUGIN_ROOT), which
runs the read-only generator and opens the HTML without a theme prompt. When no theme is
configured yet, the skills point the consultant back here to set one up first.
Dashboard Sections
The generated HTML is a single self-contained page with these sections:
- Header — engagement name, SMART key question, engagement-state badge, scope-state badge, language, last updated.
- Progress — derived overall completion % (deliverables complete / total), with stat cards for action fields, deliverables, persona reviews done, and research syntheses, plus a progress bar and a complete/in-progress/pending breakdown.
- Action fields — work breakdown — one card per action field showing the field title,
framing, derived state, and a
done/totalcount; each deliverable row shows its title, a state badge, a five-step design-thinking indicator (empathize→define→ideate→prototype→test with the current stage highlighted), and its persona-review status. A deliverable that an upstream change has invalidated (lineage_status.status: "stale") carries a stale badge next to its state, and a deliverable with declared dependencies shows a⤴ depends onhint listing the upstream deliverables to refresh first. A deliverable that has been published viaconsult-publishshows a📤publish sub-row — one chip per published format naming the format, the brief path, and the publish date, with a→ render in Claude Designpointer (hand the brief to Claude Design to render). An unpublished deliverable shows no sub-row. A deliverable that carries any of the optional scheduling fields (start_date,due_date,duration,owner,milestone) in itsfield.jsonshows a🗓schedule sub-row — an owner chip, a start→due range chip, and a duration chip, preceded by a◆ milestonemarker when the deliverable is a milestone. These fields are authored for the project-plan / schedule read model (consult-project-plan,deliverable-graph.py schedule); a deliverable with none of them set shows no schedule sub-row — the surface degrades silently, the same graceful pattern as the publish sub-row. Read-only throughout: the dashboard never writes these fields. - Knowledge base — the bound knowledge-base slug and the count of research synthesis files across scope and action fields.
- Next action — a single recommended next step derived from scope state and deliverable
states. Stale deliverables take precedence: when any exist, the recommendation is to refresh
them upstream-first (the layer-0 deliverable in the topological refresh order) before any
pending or in-progress work; otherwise it falls through to finish scoping / continue an
in-progress deliverable / start the next one / — once everything is complete — publish a
complete-but-unpublished deliverable with
consult-publish, or (when all are published) hand the briefs to Claude Design to render. - Refresh order — appears when deliverables are stale: the stale set grouped into topological layers (layer 0 first — safe to refresh now, since nothing else stale depends on it; deeper layers become reliable once the layer above is refreshed). When nothing is stale it shows a "current" note; when the dependency-graph engine is unavailable the section is omitted entirely and the rest of the dashboard still renders (graceful degradation).
A Warnings card appears only when a field.json is unreadable (surfaced, never conflated
with "pending").
Important Notes
- The dashboard is read-only over engagement state — it visualizes
consult-project.json,field.json, andassumptions.jsonwithout ever modifying them. The only file it writes is its own generatedoutput/dashboard.html(rendered bygenerate-dashboard.py, then rewritten in place with resolved assumption values via the dry-run resolve above — the resolver readsassumptions.jsonread-only and records noused_by[]edge). - The HTML file is fully self-contained (inline CSS, no external dependencies beyond an optional Google Fonts import).
- Re-running the script overwrites the previous dashboard at
output/dashboard.html.