Major project-dashboard release. Five new widget types (markdown_file, log_tail, cron_status, image, status_grid), inline sparkline on stat, typed status enum shared by list + status_grid, structured WidgetErrorCard, and a project-wide .scarf/ directory watch that picks up files cron jobs write next to dashboard.json. - ProjectDashboard: extend DashboardWidget with path/lines/jobId/cells/gridColumns/sparkline; add StatusGridCell + ListItemStatus (lenient parse with synonyms) - HermesFileWatcher: watch each project's .scarf/ dir alongside dashboard.json (local FSEvents + remote SSH mtime poll); updateProjectWatches signature now takes dashboardPaths + scarfDirs - New widget views: CronStatus, Image, LogTail, MarkdownFile, StatusGrid, plus WidgetErrorCard for structured failure messaging; legacy "Unknown" placeholder replaced everywhere - WidgetPathResolver: project-root-anchored path resolution that rejects absolute paths + ".." escapes pre and post canonicalization - Stat widget gains optional inline sparkline (pure SwiftUI Path, no Charts dep); list widget rows route through typed status with semantic icons + ScarfColor tints - iOS list widget + unsupported card adopt typed status + warning-toned error card (parity with Mac error styling); new widget types remain Mac-only - Site mirror: widgets.js renders all five new types (file-reading widgets show annotated catalog placeholders), sparkline SVG, status-grid grid; styles.css adds typed-status palette + error-card + sparkline + grid styles - Catalog validator: tools/widget-schema.json is the single source of truth; build-catalog.py loads it and enforces per-type required fields. 8 new test cases in test_build_catalog.py covering schema load, v2.7 additions, and missing-required rejection - Template-author skill (SKILL.md) gains v2.7 Widget Catalog section + canonical status guidance; CONTRIBUTING.md points authors at widget-schema.json; template-author bundle rebuilt - Localizable.xcstrings picks up auto-extracted strings for the previously-shipped OAuth keepalive feature - Release notes drafted at releases/v2.7.0/RELEASE_NOTES.md Backwards compatible — existing dashboard.json renders byte-identically, status synonyms (ok/up/down/active/etc.) keep working. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6.8 KiB
What's in 2.7.0
A focused release on project dashboards — the most "live" surface for users running cron-driven workflows. This release does three things at once:
- Auto-refresh now covers the entire project, not just
dashboard.json. A widget that points at<project>/.scarf/reports/uptime.mdrefreshes the moment the cron job rewrites it. - Five new widget types make cron-driven monitoring dashboards much more expressive — render markdown reports from disk, tail log files, surface Hermes cron-job state, embed images, and pack many services into a compact status grid.
statwidgets gain inline sparklines.listitems get a typed status enum with semantic colors. Unknown widget types render as a structured error card (not a generic "Unknown" placeholder).
Backwards compatible — no schema bump. Every existing dashboard.json renders byte-identically on v2.7. The catalog manifest format is unchanged. v1, v2, v3 bundles install identically as before. Templates that adopt new widget types still validate against the existing manifest schema — only the catalog validator's vocabulary list was extended.
Project-wide auto-refresh
HermesFileWatcher used to watch each project's dashboard.json file specifically. v2.7 promotes that to a watch on the entire <project>/.scarf/ directory:
- Local — adds a
DispatchSourceFileSystemObjectper project's.scarf/dir alongside the existing per-file watch ondashboard.json. - Remote (SSH) — folds project
.scarf/directories into the existing 3-second mtime poll. Closes the explicit "Phase 4 polish item" deferral that landed in v2.3.
Effect: a markdown_file or log_tail widget pointing at <project>/.scarf/reports/foo.md refreshes automatically when a cron job rewrites the file. By convention, place files the dashboard reads inside .scarf/ so the watch picks them up. Files outside .scarf/ work too but only refresh when dashboard.json itself changes.
Limitation: in-place appends to an existing file (>> file.log) don't tick the watcher — the cron job should write atomically (write-temp + rename), or touch dashboard.json after each run to force a refresh. Per-widget data-source watching (the granular alternative) is deferred to a future release; this project-wide pattern covers the common cron-driven workflow without the extra plumbing.
Five new widget types
All five additive — pre-v2.7 Scarf renders unknown widget types as a clearly-labeled error card now (not a crash). They share two conventions: file paths are resolved relative to the project root with a hard ..-escape rejection at WidgetPathResolver, and reads happen in Task.detached so dashboards never block the main actor.
markdown_file— renders a markdown file from disk through the sameMarkdownContentViewpipeline used by inlinetextwidgets. Pair with cron jobs that write longer-form reports.log_tail— lastlinesof a file (default 20, max 200), monospaced, ANSI codes stripped. Killer for "what did my cron job print last run?".cron_status— last run / next run / state for one Hermes cron job byjobId, plus a small inline log tail. Read-only — Run / Pause / Resume controls stay on the Cron tab so the dashboard isn't a place where you accidentally fire a job.image— local file (pathrelative to project root, viatransport.readFile) or remoteurl(viaAsyncImage). Optionalheightcap. Useful for matplotlib/Plotly PNGs the cron job generates.status_grid— compact NxM grid of colored cells, one per service / item, with hover labels. Reuses the typed status enum so colors stay consistent withlistwidgets.
stat widget gains inline sparklines
stat widgets now accept an optional sparkline: [Number] field — a tiny inline trend line under the big number. SVG-only render, no Chart.js dependency, dozens per dashboard cost nothing. Old stat widgets without the field render exactly as before.
Typed status badges (lenient decode)
list items and status_grid cells share a typed status enum: success, warning, danger, info, pending, done, neutral. Common synonyms map to the canonical case (ok / up → success, down / error / failed → danger, active → info, complete → done). Unknown strings render as plain text rather than crashing — the dev's machine alone has dashboards using ad-hoc statuses like "ok", "up", "info", and they all keep working byte-identically. For new templates, prefer the canonical names so colors stay predictable across releases.
Structured widget error card
The legacy "Unknown: <type>" placeholder is replaced with a structured error card surfacing the widget's title, the specific reason (unknown type, missing file, parse error, path escapes project root), and a hint. Used by the dispatcher's default branch and by every v2.7 file-reading widget when its underlying data can't be loaded.
Schema mirror — single source of truth
The widget vocabulary is now defined once at tools/widget-schema.json instead of being maintained in three places by hand. The catalog validator (tools/build-catalog.py) reads from it and now enforces per-type required fields (e.g. cron_status requires jobId, log_tail requires path). Adding a future widget type means editing one JSON file plus implementing a Swift view + a JS renderer; the validator picks up the addition automatically.
What's deferred
Two items from the design plan stayed deferred:
- Per-widget data sources + per-widget refresh granularity. The general "widget points at a typed data source (file / cron / json-path / …)" abstraction is the next-largest win in this area but materially expands the model + JS mirror + validator + authoring skill surface. The project-wide watch covers the common cron-driven workflow without it; revisit when a real-world template wants the granular control.
- Cross-project health digest sidebar rollup. Counting attention-needed projects across the registry was scoped for this release but ended up not pulling its weight against the rest of the work; the underlying status enum (B.1) makes a future digest cheap to add.
Compatibility
- macOS 14+ (unchanged).
- Hermes target: still v2026.4.30 (v0.12.0). No new Hermes capability gates added.
- Existing
dashboard.jsonfiles render unchanged. - Existing
.scarftemplatebundles install unchanged. Catalog manifest schemaVersion stays at 1/2/3 — no bump. - The
awizemann/template-authorbundle was rebuilt to ship the updatedSKILL.md(Widget Catalog v2.7+ section) so Hermes can scaffold dashboards using the new widget types out of the box.