[go][js] update /api/capabilities#430
Conversation
Co-authored-by: ddol <445312+ddol@users.noreply.github.com> Agent-Logs-Url: https://github.com/banshee-data/velocity.report/sessions/1391681a-ee41-49b9-a6cf-3d8d3604c8e5
Replace the flat capabilities response shape with per-sensor-class maps keyed by a stable sensor name (e.g. 'default'). This allows multiple sensors of the same class (radar, lidar) to coexist in future without breaking the API contract. Wire changes: - SensorStatus / LidarSensorStatus replace LidarCapability - Capabilities.Radar is now map[string]SensorStatus - Capabilities.Lidar is now map[string]LidarSensorStatus - 'State' field renamed to 'Status'; top-level LidarSweep removed - Default fallback in showCapabilities updated - capabilitiesProvider.Capabilities() builds named-object maps - All tests updated for the new shape
Align the Svelte frontend with the new /api/capabilities JSON shape
where radar and lidar are Record<string, SensorStatus> maps keyed by
sensor name, replacing the flat boolean/object fields.
- api.ts: replace LidarCapability with SensorStatus and
LidarSensorStatus; Capabilities uses Record maps
- capabilities.ts: default to { radar: { default: ... }, lidar: {} };
derive lidarEnabled via Object.values().some()
- +layout.svelte: gate LiDAR nav items on the new map shape
- api.test.ts, capabilities.test.ts: update all test data to the new
shape
Co-authored-by: ddol <445312+ddol@users.noreply.github.com> Agent-Logs-Url: https://github.com/banshee-data/velocity.report/sessions/1391681a-ee41-49b9-a6cf-3d8d3604c8e5
🤖 Version Bump AdvisoryWarnings❌ Web Frontend code changed but version unchanged - update 📖 See CHANGELOG.md for detailed guidelines. This is an automated advisory. Review the detected changes and update versions accordingly. |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Pull request overview
Redesigns the /api/capabilities contract to support multiple named sensors per sensor class (radar/LiDAR) by switching from a flat single-sensor shape to map[string]T / Record<string, T> keyed by sensor name, and updates the frontend gating + tests accordingly.
Changes:
- Backend: replace flat
Radar bool/LidarCapabilitywithRadar map[string]SensorStatusandLidar map[string]LidarSensorStatus, plus updated defaults/providers/tests. - Frontend: update
Capabilitiestypings and derived stores to operate on named-object maps; update nav gating. - Docs/tests: add a plan doc describing the new format; update Go/TS test fixtures for the new shape.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
internal/api/server.go |
Introduces SensorStatus/LidarSensorStatus and changes Capabilities fields to named maps. |
internal/api/server_admin.go |
Updates the default /api/capabilities fallback response to the new map-based shape. |
cmd/radar/capabilities.go |
Updates the runtime capabilities provider to emit radar.default and optional lidar.default. |
internal/api/capabilities_test.go |
Updates API handler tests for the new JSON shape and map access. |
cmd/radar/capabilities_test.go |
Updates provider tests for map-based capabilities. |
web/src/lib/api.ts |
Updates TypeScript API types to map-based Capabilities with per-sensor status interfaces. |
web/src/lib/api.test.ts |
Updates API client tests to the new capabilities response shape. |
web/src/lib/stores/capabilities.ts |
Updates default capabilities + derived stores (lidarEnabled, lidarState) for map semantics. |
web/src/lib/stores/capabilities.test.ts |
Updates store tests for map-based capabilities and derived logic. |
web/src/routes/+layout.svelte |
Updates LiDAR nav gating to check Object.values($capabilities.lidar). |
docs/plans/api-multi-sensor-capabilities-plan.md |
Adds a plan/spec doc for the multi-sensor capabilities redesign. |
| caps := Capabilities{ | ||
| Radar: true, | ||
| Lidar: LidarCapability{ | ||
| Enabled: false, | ||
| State: "disabled", | ||
| Radar: map[string]SensorStatus{ | ||
| "default": {Enabled: true, Status: "receiving"}, | ||
| }, | ||
| LidarSweep: false, | ||
| Lidar: map[string]LidarSensorStatus{}, | ||
| } |
There was a problem hiding this comment.
This default response now represents LiDAR absence as an empty map (lidar: {}), which is semantically "no LiDAR configured" rather than an explicit disabled sensor. The doc comment above showCapabilities still says "LiDAR as disabled"; consider updating it to reflect the empty-map contract to avoid confusion for API consumers.
| "radar": { | ||
| "default": { | ||
| "enabled": true, | ||
| "status": "receiving", | ||
| "last_reading_at": "2026-03-24T06:45:12Z" | ||
| } | ||
| }, |
There was a problem hiding this comment.
The plan/examples and type definitions include a last_reading_at field, but the implemented Go/TypeScript SensorStatus types in this PR do not include it. Either implement last_reading_at end-to-end or remove it from the plan/spec so the documentation matches the shipped API.
| | `enabled` | `bool` | Sensor channel was activated at startup | | ||
| | `status` | `string` | Runtime state: `disabled`, `starting`, `receiving`, `stale`, `error` | | ||
| | `last_reading_at` | `string \| null` | ISO 8601 timestamp of last data received; `null` = never | | ||
| | `sweep` | `bool` | (lidar only) Sweep/auto-tuner operational | | ||
|
|
||
| ### State machine | ||
|
|
||
| ``` | ||
| disabled → starting → receiving ⇄ stale | ||
| ↘ error | ||
| ``` |
There was a problem hiding this comment.
The plan defines the status state machine/literals around "receiving"/"stale" and does not mention "ready", but the current implementation/provider emits "ready" for LiDAR. Please reconcile the documented status vocabulary/state machine with what the API actually returns (update the plan or adjust the implementation/tests).
Co-authored-by: ddol <445312+ddol@users.noreply.github.com> Agent-Logs-Url: https://github.com/banshee-data/velocity.report/sessions/abefb8ec-c4fd-4cea-a61a-12ddfc230d28
…r lidarState test Co-authored-by: ddol <445312+ddol@users.noreply.github.com> Agent-Logs-Url: https://github.com/banshee-data/velocity.report/sessions/abefb8ec-c4fd-4cea-a61a-12ddfc230d28
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: David Dolphin <445312+ddol@users.noreply.github.com>
Both items were deferred from PR 480 because they are out of scope for the embed-stub plumbing this branch fixes: - #483 (v0.5.2, S): Node baseline decision for `docs_html` build. - #482 (v0.5.3, S): surface offline docs URL from backend so the Web UI link tracks `--docs-listen`. Sits next to existing #430 capabilities redesign work. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Both items were deferred from PR 480 because they are out of scope for the embed-stub plumbing this branch fixes: - #483 (v0.5.2, S): Node baseline decision for `docs_html` build. - #482 (v0.5.3, S): surface offline docs URL from backend so the Web UI link tracks `--docs-listen`. Sits next to existing #430 capabilities redesign work. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* [docs] Add offline documentation site structure and styles
- Created build.js to manage versioning and build metadata.
- Added sidebar.njk for navigation structure in the documentation.
- Implemented base layout in base.njk for consistent page structure.
- Introduced site.css for styling the documentation site.
- Created index.njk as the main entry point for the offline documentation.
- Added stub-index.html to inform users when documentation is not built.
* [js] Implement offlineDocsUrl function and corresponding tests
* [go] Implement embedded offline documentation server and validation
* [js] Add Docs navigation item and update offlineDocsUrl handling
* [make][sh] Add offline documentation build and management scripts
* Potential fix for pull request finding 'CodeQL / Incomplete multi-character sanitization'
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Signed-off-by: David Dolphin <445312+ddol@users.noreply.github.com>
* [ai][ci] add offline docs embed stub step to Go CI jobs
The new `assets.go` embed pattern `all:docs_html/_site` requires the
directory to exist at compile time. CI was only running
`scripts/ensure-web-stub.sh`, so the build job failed with
`pattern all:docs_html/_site: no matching files found`.
Add `scripts/ensure-docs-stub.sh` to build, test-core, test-lidar, and
test-integration so all jobs that compile Go code see a populated stub.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* [ai][make][js] make offline docs lint non-mutating and arg-tolerant
Three related changes addressing PR review:
- `make test-go` and `make test-go-cov` now invoke
`scripts/ensure-web-stub.sh` and `scripts/ensure-docs-stub.sh` first,
so the documented `make test` quality gate works on a clean checkout.
- `lint-docs-offline` no longer depends on `build-docs-offline`. It now
delegates to `check-docs-offline-links`, keeping `make lint` purely
non-mutating. Use `make build-docs-offline` (which still calls
`check-docs-offline-links` under the hood) to regenerate the rendered
site before linting if desired.
- `scripts/check-docs-offline-links.js` parses argv flag-tolerantly: a
flag like `--strict-anchors` no longer gets misread as the positional
`siteRoot`. The cheerio require is deferred so a stub-only site (or a
fresh checkout without `make install-docs-offline`) skips gracefully
rather than crashing.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* [ai][docs] render offline docs index as Markdown
The homepage was authored as Markdown (`# Offline Documentation`,
Markdown links) but lived in `index.njk`. Eleventy is configured with
`htmlTemplateEngine: "njk"` and `markdownTemplateEngine: false`, so
`.njk` files were rendered as Nunjucks/HTML and the Markdown surfaced
verbatim in the generated page.
Rename to `index.md` so Eleventy runs the file through markdown-it. The
existing global layout (`base.njk`) still applies via
`addGlobalData("layout", "base.njk")`, so visual output is unchanged
apart from the headings and links now rendering correctly.
`.gitignore` previously ignored every `src/*.md` (the symlinked tree
generated by `scripts/docs-offline-symlinks.sh`). Add a negation for
`src/index.md` so the committed homepage is tracked.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* [ai][docs] track follow-ups from PR 480 review in backlog
Both items were deferred from PR 480 because they are out of scope for
the embed-stub plumbing this branch fixes:
- #483 (v0.5.2, S): Node baseline decision for `docs_html` build.
- #482 (v0.5.3, S): surface offline docs URL from backend so the Web UI
link tracks `--docs-listen`. Sits next to existing #430 capabilities
redesign work.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* [ai][js] copy mermaid ESM chunks into offline docs site
The mermaid ESM bundle is split into ~80 chunks under
`node_modules/mermaid/dist/chunks/mermaid.esm.min/`. The eleventy
passthrough copy only forwarded the entry file `mermaid.esm.min.mjs`,
so the browser loaded the entry, then 404'd on every relative
`./chunks/mermaid.esm.min/chunk-*.mjs` import: no diagrams rendered.
Add an `eleventy.after` hook that copies each `.mjs` chunk into
`_site/assets/chunks/mermaid.esm.min/`. Skip the matching `.map`
sourcemap files (~11 MB) so they don't inflate the embedded binary.
Validated locally with the dev server: 81 chunks resolve over HTTP and
the chunk import graph is closed (0 unresolved imports).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* [ai][docs] add dark mode toggle and sticky sidebar header
- Define a dark token set under `[data-theme="dark"]` and convert the
hardcoded mermaid block background to use the panel token. Default
is light; explicit choice persists in localStorage as `docs-theme`,
otherwise falls back to `prefers-color-scheme`.
- Add a no-flash inline script in `<head>` that sets `data-theme` on
`<html>` before the body paints, so dark-mode users never see a
light-mode flash on navigation.
- Wrap brand + theme toggle in `.sidebar-header` with
`position: sticky; top: 0` inside the scrollable sidebar, so the
toggle always sits at the top of the left column even as the nav
scrolls. Negative horizontal margin extends the header background
to the sidebar's borders.
- Sun/moon SVG icons (no emoji per repo style); button has
aria-label/title for screen readers and exposes focus-visible
styling.
- Mermaid is initialised with `startOnLoad: false` and re-rendered via
`mermaid.run()` on theme change, switching between the `neutral`
and `dark` mermaid themes so diagram colours track the page theme.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* [ai][js][docs] render LaTeX math in offline docs via KaTeX
Equations like $v_{\text{max}}$ and $\mathbf{x}$ in `data/maths/`
previously surfaced as raw `$...$` and `$$...$$` in the rendered
offline docs. Add KaTeX server-side rendering so the maths displays as
proper notation without requiring internet at runtime.
- Add `markdown-it-texmath` (KaTeX engine, `dollars` delimiters) to
the markdown-it pipeline in `.eleventy.js`. `throwOnError: false`
keeps a single malformed equation from breaking the whole build —
the offending block falls back to the source text.
- Pass through `katex.min.css` and the full `dist/fonts/` directory
(woff2 + woff + ttf, ~1.1 MB) so the rendered glyphs work offline
for any browser the operator might be using.
- Link `katex.min.css` from `base.njk` before `site.css` so site
styling can override KaTeX defaults if needed later.
Validated locally: the page at `/data/maths/classification-maths/`
emits 56 inline `katex` spans + 8 `katex-display` blocks and 0
`katex-error` spans; `\mathbf` and `\text` render to the expected
`mord mathbf` and `mord text mtight` classes; the offline link checker
still passes.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* [ai][docs] update offline docs plan with PR #480 implementation status
Bump the status header from "Draft" to track current state and add a
new §9 ("Implementation status, 2026-04-28") covering:
- What landed in PR #480 against each phase, including divergences
from the plan's recommendations (notably client-side Mermaid render
instead of build-time SVG pre-render — trade-off explained inline).
- Resolved §7 open questions (Q2/Q3/Q4/Q5/Q6/Q8).
- Open follow-ups deliberately not in PR #480, with links to the
tracking issues #482 (capabilities-sourced docs URL) and #483 (Node
baseline decision), plus the Phase 4/5 items still outstanding.
The plan stays as the authored design record; §9 captures actual
state without rewriting the original sections.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* [ai][js][docs] tree-view sidebar with collapse/expand and breadcrumb topbar
Three related navigation changes that travel together — the tree
relies on the new filter, the breadcrumb relies on the new filter,
and the styling/JS only make sense once both filters exist.
Sidebar tree view (replaces the flat grouped list):
- New `buildDocsTree` walks the docsPages collection by URL segments,
producing a hierarchical tree where each node carries its own URL
(when a page exists at that path, e.g. a folder's README.md) plus
its children. Nodes with no corresponding page render as
expand-only labels.
- `sidebar.njk` renders the tree with a recursive Nunjucks macro,
using native `<details>`/`<summary>` for expand/collapse so it
works without JS. A small chevron pseudo-element rotates 90°
when the folder is open.
- The branch leading to the current page is server-side flagged
via `hasCurrent` so those `<details>` start `open` on first load.
- Manual expand/collapse state persists in `localStorage`
(`docs-tree-open`, keyed by tree path) so navigating between pages
doesn't lose the operator's reading context.
Topbar breadcrumbs (replaces "Offline documentation" + README link):
- New `buildBreadcrumbs` filter splits the current page URL into
segments, links each parent folder iff a page exists at that path
(so `/docs/` links if the docs README is rendered, otherwise the
segment shows as plain text), and leaves the last segment as the
unlinked current crumb.
- The topbar emits the trail with `/` separators and a leading
"Offline docs" link to /. Styling is added in `site.css`; the
topbar's `justify-content` switches from `space-between` to
`flex-start` since the right-hand README link has been removed
in favour of the breadcrumb.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* [fs] add symlink for data/explore/README.md
* [ai][docs] suppress stale relative-link reports for runtime-resolved hrefs
Two unrelated places trip `scripts/check-relative-links.py` even
though the links are correct in their respective contexts:
- `docs_html/src/index.md` lists Markdown links into the symlinked
content tree (README.md, ARCHITECTURE.md, etc.). Eleventy follows
the symlinks at build time and the .md → / rewriter rewrites them
to served URLs, but the source-tree link checker doesn't see the
symlinks. Mark each line with `<!-- link-ignore -->`.
- `docs/plans/embedded-offline-docs-site.md:376` quotes the strings
`[x](../foo)` and `[x](dir/)` as illustrative examples of what the
rewriter handles. The checker treats them as real links. Mark the
line with `<!-- link-ignore -->`.
`python3 scripts/check-relative-links.py` now passes locally.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* [ai][js] compute Docs nav URL synchronously from page.url
`docsUrl` was initialised to `'/'` and only updated inside `onMount`,
so on first paint the Docs nav item briefly pointed at the app root.
Clicking quickly (or with a delayed JS load) navigated to the
dashboard instead of the offline docs.
Switch to a `$derived` value sourced from `page.url.href`, which is
populated both server-side and at component init on the client. The
URL is now correct on the very first render with no flicker, and
SvelteKit's reactive `page` store keeps it in sync across navigation.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* [ai][docs] dynamic aria-label on theme toggle reflects intended action
The theme toggle's aria-label/title was hard-coded to "Toggle dark
mode" but the button switches in either direction. Misleading for
assistive tech and tooltips.
Switch to a dynamic label managed by the existing toggle script:
"Switch to dark mode" when the current theme is light, and "Switch
to light mode" when the current theme is dark. The static fallback
in the markup is "Toggle theme" so users without JS see a neutral
label rather than a wrong one.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* [ai][go] split docsite.Start into Run(listener) to fix test port race
The original `TestStartShutdown` did `net.Listen(":0") → addr →
listener.Close() → Start(addr)` to discover an ephemeral port. There
is a TOCTOU race in that sequence: another process can claim the
freed port before `Start` re-binds, making the test flaky.
Refactor:
- `docsite.Start(ctx, listen, source, diskDir)` is now a thin wrapper
that calls `net.Listen` and forwards to `Run`. Existing call site
in `cmd/radar/radar.go` is unchanged.
- `docsite.Run(ctx, listener, source, diskDir)` is the new
test-friendly entry point: it takes ownership of a pre-bound
listener and serves on it. No close-then-rebind race.
- `TestStartShutdown` is renamed to `TestRunShutdown` and uses `Run`
with the same listener it created — no Close call between bind
and serve.
`go test ./internal/docsite/...` passes; lint clean; binary builds.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* [ai][docs] address PR 480 review follow-ups
* [docs] run 8083
* [docs] add embed stub for offline docs site and update related scripts
* [docs] enhance offline documentation integration and routing
* [docs] move from :8030 to /docs
* [ai] update CLAUDE.md with new build and development commands for offline docs
Co-authored-by: Copilot <copilot@github.com>
* [ver] bump version to 0.5.1-pre12 across all relevant files
* [docs] update CLAUDE.md system diagram
* [docs] clarify embedded offline docs section in .dockerignore
Co-authored-by: Copilot <copilot@github.com>
* [docs] update .dockerignore and .eleventy.js to exclude non-essential files and raw PDFs from embedded offline docs
Co-authored-by: Copilot <copilot@github.com>
* [docs] update embedded offline docs site status and clarify Milestone 2 plans
Co-authored-by: Copilot <copilot@github.com>
---------
Signed-off-by: David Dolphin <445312+ddol@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Copilot <copilot@github.com>
docs/plans/api-multi-sensor-capabilities-plan.mdwith the agreed format (radar/lidarnamed objects, no_sensorssuffix)💬 Send tasks to Copilot coding agent from Slack and Teams to turn conversations into code. Copilot posts an update in your thread when it's finished.