Commit Graph

360 Commits

Author SHA1 Message Date
Jordan Ritter 5b915dd844 chore(shell-dashboard): humanizeCron HH:MM and rename completed→finished
Bucket-(b) bonus polish:
- humanizeCron now renders "M H * * *" with a non-zero minute as
  "Daily at HH:MM" instead of falling through to the raw expression.
- humanizeCron rejects */0 step values for both the every-Nh and every-Nm
  shapes — that's not a valid cron step.
- StatusRunningPanel: rename `completed` to `finished`. The variable
  counts services in either completed OR failed states, so "completed"
  read as misleadingly success-only. The data-completed attribute name
  stays for backwards compatibility with existing tests.
2026-04-25 19:31:39 -07:00
Jordan Ritter 5fe2058c79 fix(shell-dashboard): badge partial-pass runs amber instead of green
A run with failed=0 but passed<total represents some services that were
skipped/unknown — rendering this as green "completed" misleads operators
into thinking all targets passed (R2-D.4). Now badged amber with a
"partial (N skipped)" label so the partial-coverage case is visible.
2026-04-25 19:31:32 -07:00
Jordan Ritter 29a67b78e0 fix(shell-dashboard): surface trigger errors and reconcile selection
Three related fixes to StatusTriggerButton:
- R2-D.2: render onTrigger errors inline via data-testid="status-trigger-error"
  instead of discarding the value through a write-only setter.
- R2-D.3: when serviceSlugs prop changes, intersect selected with the new
  list so stale slugs cannot be POSTed; also clear selection on
  outside-click and Escape close so reopening doesn't show ghost checks.
- R2-D.5: drop unreachable serviceSlugs.length === 0 ternary inside the
  picker — the outer guard already requires length > 0.
2026-04-25 19:31:25 -07:00
Jordan Ritter 46aa14c35f fix(shell-dashboard): wire page.tsx to useProbes + useTriggerProbe
Replace the no-op handleTrigger stub and empty-list placeholder with real
hooks. useProbes feeds the schedule grid via 10s polling; useTriggerProbe
POSTs /trigger with the operator token. If NEXT_PUBLIC_OPS_TRIGGER_TOKEN
is unset, the trigger throws a clear "token required" error so operators
see fail-loud feedback instead of a silent no-op (R2-D.1).
2026-04-25 19:31:18 -07:00
Jordan Ritter 5cd22270a3 fix(shell-dashboard): keep useTriggerProbe POST alive on unmount, swallow supersession AbortError
POST to /trigger is non-idempotent — the server may have already received
the request and queued the run by the time the component unmounts.
Aborting the controller client-side hides the result, but does NOT
unfire the server-side action. Stop aborting on unmount; rely on
aliveRef to gate any setState after teardown.

Back-to-back trigger() calls intentionally abort the prior controller.
The first call's promise was rejecting with AbortError, surfacing as a
user-facing error on the Status tab. Detect AbortError (and the
cancelled-controller case) in the catch block and resolve silently
instead of setError + rethrow. The owning-controller guard on the
finally block prevents a superseded call from flipping pending off
while a newer call is still in flight.

Bonus consistency for useProbes: setLoading(true) on dep change so
consumers can render a "refreshing" indicator across baseUrl swaps.

Tests:
- unmount during in-flight trigger does NOT abort the signal
- post-unmount resolution does not call setState (no act warnings)
- back-to-back triggers: first promise resolves silently, error stays null
2026-04-25 19:29:20 -07:00
Jordan Ritter 0243aa39bd fix(shell-dashboard): preserve AbortError name through ops-api error wrapping
ensureOk and parseJson previously caught body-read failures and rewrapped
them in a generic Error, losing the AbortError name. Hooks filter abort
noise via err.name === "AbortError", so the wrap defeated cancellation
filtering and surfaced "AbortError" as a user-facing failure on the
Status tab.

Re-throw AbortError as-is from both helpers; non-Abort body errors keep
the descriptive wrap path. Also tighten ProbeKind to use (string & {})
so known-kind autocomplete is preserved alongside forward-compat strings.

Add covering tests:
- response.text() rejects with AbortError → ensureOk re-throws as-is
- response.json() rejects with AbortError → parseJson re-throws as-is
- non-Abort body errors still wrap with descriptive message
- triggerProbe with already-aborted signal surfaces AbortError unwrapped
2026-04-25 19:29:08 -07:00
Jordan Ritter 15af4c4779 test(showcase-dashboard): tighten matrix tests, cover starter cells and regressions filter
Backstop the matrix fixes and address weak/no-op assertions:

  - Tighten depth-chip count assertions from '>= N' to exact 'N'
    where the fixture pins the count.
  - Replace the no-op filter=wired test with a fixture containing a
    feature whose every cell is unshipped, then assert that row IS
    visible under filter=all and IS HIDDEN under filter=wired.
  - Extend the collapse test with a re-expand click that asserts
    chip count returns to baseline (regression guard for one-way
    toggle state).
  - Cover filter=regressions empty-state.
  - Cover starter cells (feature === null) in both matrices: the
    cell index must skip them without polluting the grid or leaking
    a 'null' string into rendered output.
  - Pin parity-matrix integration-header ordering across all five
    parity tiers (reference, at_parity, partial, minimal, not_wired).
  - Cover D2 cap for starter cells in deriveDepth (chat/tools cannot
    lift past D2 because D3 is not evaluable without a feature id).
  - Replace the locally-declared CatalogCell shape in depth-utils.test
    with the source type so tests cannot drift from production.
  - Add afterEach(vi.unstubAllGlobals) to both test files so the
    localStorage stub does not leak across files in the same worker.
2026-04-25 19:25:09 -07:00
Jordan Ritter f6742c9871 fix(showcase-dashboard): drop depth typecasts and harden matrix cell index
Three related cleanups in cell-matrix.tsx and parity-matrix.tsx:

  1. Drop the 'as 0 | 1 | 2 | 3 | 4' typecasts on depth.achieved. The
     newly tightened DepthResult.achieved literal-union type makes
     them redundant.

  2. Skip starter cells (feature === null) when building the
     cell-by-key index. The previous code interpolated null into the
     key string, producing '<slug>/null' entries that orphaned
     starter rows in the index.

  3. Render an explicit empty-state in CellMatrix when filter is
     'regressions'. The placeholder logic returned false for every
     row, silently hiding the entire matrix. Until regression
     detection lands (DepthResult.isRegression always false), the
     matrix now surfaces a 'Regression detection not yet implemented'
     message via data-empty-reason='regressions-not-implemented'.
2026-04-25 19:25:09 -07:00
Jordan Ritter 56e8f41861 fix(showcase-dashboard): tighten DepthResult.achieved to 0|1|2|3|4 literal union
deriveDepth's return type carried the loose 'number' on achieved, forcing
every consumer to cast to '0 | 1 | 2 | 3 | 4' to satisfy DepthChip's
prop type. Expose AchievedDepth alias and narrow the source so the
casts at use sites (cell-matrix.tsx, parity-matrix.tsx) become
unnecessary.
2026-04-25 19:25:08 -07:00
Jordan Ritter efba71b224 fix(showcase-dashboard): CellStatus drops smoke + hides D5/D6 for testing features
Per-cell status row corrections + a focused unit-test file covering CP1,
CP2, CP5, CP7, CP8 behaviors:

- CP3: stop consuming `cell.smoke` in CellStatus. resolveCell still
  computes the field for backwards-compat with other call-sites, but
  per Phase 3 Decision #7 the smoke L1 signal is integration-scoped and
  lives in the per-integration strip — there's no per-feature smoke
  badge in the cell row. (The CellState type narrowing for the `smoke`
  field is owned by the live-status.ts fix in a sibling worktree; this
  patch only removes the consumer.)
- CP8: hide the D5/D6 LiveBadges when feature.kind === "testing".
  The e2e-deep / e2e-parity producers only emit rows for primary
  features per spec; testing features would otherwise render a
  perpetual gray "?" that adds noise without information. Mirrors the
  docs-row visibility rule (already gated on isTesting).
- CP9: document the missing per-cell drilldown URL convention for D5/D6
  with a TODO comment. No drilldown route exists today; once one does,
  wire it through keyFor here.

Tests cover red-green for CP1 (mouseleave handler wired), CP2 (first /
error / green_to_red transition formatting), CP5 (opt-out vs absent
tooltip copy), and CP8 (D5/D6 hidden for testing features). The
existing docs-link.test.tsx still passes.
2026-04-25 19:25:08 -07:00
Jordan Ritter 2900998889 fix(showcase-dashboard): LiveBadge tooltip-open lifecycle and transition copy
- CP1: wrap Badge in a span that resets `tooltipOpen` on mouseleave/blur
  so the lazy-fetch flag can flip back to false. Without this, the flag
  stayed true forever after the first hover, defeating the §5.6
  optimization and (once the hook upgrades to a subscription) leaking it.
- CP2: discriminate `first` and `error` transitions in the tooltip copy
  so operators can tell "first observation (initial: <state>)" apart
  from "producer error (error → <state>)" instead of seeing the same
  fallback string. Logic moved into a named `formatTransitionLine`
  helper for testability.
2026-04-25 19:25:08 -07:00
Jordan Ritter 72275e8b2f fix(showcase-dashboard): tighten DocsRow/DocsLink — exhaustiveness, honest tooltips, error linkability
Four fixes to the per-cell docs row:

- CP4: extract the `unselected` shell-routing fallback into
  SHELL_UNSELECTED_PATH so the coupling is documented and easy to find.
- CP5: split the `missing` tooltip into "framework opt-out" vs "no docs
  URL declared" so operators can distinguish a framework that declined
  docs from a registry entry that never declared one.
- CP6: add `default: never` exhaustiveness guard to DocsLink's switch so
  any new DocState variant is a compile error until handled.
- CP7: render `<a>` (not `<span>`) for `state === "error"` when an href
  is present so operators can manually verify a flaky probe — `notfound`
  stays a span because the URL is positively known broken.
2026-04-25 19:25:08 -07:00
Jordan Ritter 3a826168c8 fix(showcase-dashboard): preserve array identity on no-op upsert and warn on merge collisions
LS6: `mergeRowsToMap` now logs a `console.warn` when the same key is
seen twice across the merged groups. Each dimension is supposed to own
a disjoint slice of the keyspace, so a collision is an upstream bug.
Return semantics are unchanged (last-wins remains correct under
eventual consistency), but the warn surfaces the regression in dev mode.

LS7: `upsertByKey` returns the SAME array reference when the incoming
row is observably identical (key + state + observed_at +
transitioned_at). Producers re-emit unchanged rows on every poll tick;
allocating a fresh array for each one defeats React's reference-equality
short-circuit and re-renders every memoised consumer downstream.

Tests cover no-op identity, mutation paths (state change,
observed_at change), the disjoint-merge happy path, and the collision
warn.
2026-04-25 19:25:08 -07:00
Jordan Ritter ae0bf40724 fix(showcase-dashboard): close stale-green hole and align smoke key shape
LS1: `allGreen` now requires BOTH `healthRow` and `e2eRow` to be present
and green. Previously a missing e2e row was treated as green-eligible,
which let a brand-new cell read green before any e2e probe had ticked —
the same flavour of stale-green lie as the connection=error case.

LS11: smoke lookup now uses the integration-scoped key (`smoke:<slug>`)
to match the producer. Pre-fix, resolveCell looked up
`smoke:<slug>/<featureId>` which always missed, leaving every smoke badge
gray. Tests update fixtures accordingly and add a regression guard so a
stray feature-keyed smoke row cannot bleed into the resolver.
2026-04-25 19:25:08 -07:00
Jordan Ritter ec94f64886 fix(showcase-dashboard): make formatTooltip honest and signal-aware
- Drop the hardcoded '>6h' duration from the degraded tooltip; the
  threshold lives in the producer config and lying about it in copy
  misleads operators when the cadence changes.
- Replace the empty-string default branch with a loud unknown-state
  fallback gated by a `never` exhaustiveness check, mirroring rowTone /
  formatLabel.
- For red and degraded states, append a truncated (80-char) summary of
  `row.signal` so the tooltip carries triage context without ballooning.
- When the SSE connection is in error AND the cached row is red or
  degraded, append the row's last-known state so operators don't lose
  triage context to the offline banner.
2026-04-25 19:25:08 -07:00
Jordan Ritter 6f057665d6 fix(showcase-dashboard): add keyFor input guards and exhaustive state checks
- keyFor now throws when slug or featureId contain ':' or '/', preventing
  silent collisions in the live-status lookup map (e.g. `smoke:a/b` vs
  `smoke:a` + `/b`).
- rowTone() and formatLabel() switch over the State union with explicit
  `never` exhaustiveness checks so adding a new variant fails the build
  instead of silently mapping to a misleading green/gray default.
- Unknown row states now surface a distinct 'error' tone (rowTone) and a
  '?' glyph (formatLabel) rather than a silent green check.
- Tests cover the three throw paths and remain green for the existing
  per-feature key shapes.
2026-04-25 19:25:07 -07:00
github-actions[bot] 938d734c34 style: auto-fix formatting 2026-04-26 02:15:30 +00:00
Jordan Ritter c743bebb6a merge: pull origin auto-format commit
# Conflicts:
#	showcase/shell-dashboard/src/components/status-duration-sparkline.tsx
2026-04-25 19:14:14 -07:00
Jordan Ritter 6e5427922a fix(shell-dashboard): null-guard lastRun.summary in status-table
After B1 tightened ProbeLastRun.summary to ProbeRunSummary | null, three
sites in status-table.tsx accessed .summary fields without null-guards
(TS2533 errors at lines 121 and 165).

- lastRunTone: when summary is null but state === "completed", return
  "gray" (matches runs-list "unknown" semantics — finished but no count
  info means we can't claim coverage). state === "failed" still returns
  "red" regardless of summary.
- Result cell: when summary is null, render "—" (matches existing
  fallbacks for unknown values); otherwise render passed/total as before.

Adds two covering tests for the null-summary completed (gray + "—") and
null-summary failed (red + "—") cases.
2026-04-25 19:12:41 -07:00
Jordan Ritter 8b3f53aefb fix(shell-dashboard): replace double cast with single CatalogData cast
CR-C-page.1: `catalog as unknown as CatalogData` violated the project
policy against double casts. catalog.json is generated by pretest/
predev/prebuild from generate-registry.ts and conforms to CatalogData
by construction, so a single `as CatalogData` cast is safe and clearer.

Typecheck: no new errors (pre-existing __tests__ + status-table.tsx
errors are unrelated and unchanged). All 220 vitest tests pass.
2026-04-25 19:07:22 -07:00
Jordan Ritter d24a9d9a5f merge: cr-fix-B2 CR fixes 2026-04-25 19:05:05 -07:00
Jordan Ritter d3ab9d0387 fix(showcase-dashboard): collapse sub-second formatRelative to 'now' sentinel
Sub-second deltas previously rendered as "in 0s" / "0s ago", which
flickered between sides of the boundary on every tick. Collapse to a
single "now" sentinel for |delta| < 1s and add a Number.isFinite guard
that falls back to "—" so non-finite inputs don't cascade into
unreadable output.
2026-04-25 18:56:15 -07:00
Jordan Ritter f727665124 fix(showcase-dashboard): guard sparkline against tiny widths and drop dead branch
Sparkline previously divided unconditionally by (durations.length - 1)
inside a length===1 branch that the early return at length<2 made
unreachable. Removed the dead conditional. Added a small-viewport guard
that falls back to the flat-dash placeholder when innerW or innerH is
non-positive, preventing negative or NaN coords when callers pass
width <= PADDING * 2.
2026-04-25 18:56:10 -07:00
Jordan Ritter 8a8e4d81d5 fix(showcase-dashboard): stabilize handleTrigger with useCallback for tabs deps
The tabs useMemo closed over handleTrigger but did not list it in its
dependency array. Today the handler is a no-op so the latent bug is
invisible, but once B4b wires this to the real ops API trigger
endpoint the memo would retain a stale closure. Wrap handleTrigger in
useCallback and add it to the tabs deps array so the memo refreshes
correctly.
2026-04-25 18:56:04 -07:00
Jordan Ritter fa612ac4a7 fix(showcase-dashboard): hide Run specific... when no service slugs are known
The trigger menu's "Run specific..." entry was unusable for idle probes
because slugs are sourced from inflight.services, which is null in the
typical manual-trigger case. Hide the entry entirely when serviceSlugs
is empty (Option C); operators can still trigger all targets via "Run
all". A richer ProbeScheduleEntry contract that exposes discovered
targets for idle probes is tracked separately.

Also wraps onTrigger awaits in try/catch so async rejections surface
through a local error state instead of bubbling up as unhandled promise
rejections, and resets the selected slug set on completion so the next
trigger session starts clean.
2026-04-25 18:55:57 -07:00
Jordan Ritter d719e8380e fix(showcase-dashboard): align run state label with tone for missing summary
runTone returned "gray" (unknown) when finishedAt was set but summary
was null, while runStateLabel returned "completed" for the same case —
producing a gray badge labeled "completed", exactly the misleading
combination the tone comment said it was avoiding. runStateLabel now
returns "unknown" so label and tone agree.
2026-04-25 18:55:49 -07:00
Jordan Ritter 7cec1033c2 fix(showcase-dashboard): filter past nextRunAt from idle next-run banner
When all probes are idle, findNextScheduled previously returned the
entry with the smallest nextRunAt regardless of whether that timestamp
was already in the past, producing nonsensical "Next: smoke 5m ago"
banners. Filter out entries whose nextRunAt is < now and fall back to
the existing "No upcoming runs scheduled." sentinel.

Also typed STATE_ICON as Record<ServiceState, string> so missing service
states fail to compile rather than silently rendering undefined icons.
2026-04-25 18:55:43 -07:00
Jordan Ritter 7d8228075d fix(shell-dashboard): drop duplicate ProbeScheduleEntry, re-export canonical
The local interface in status-tab.tsx was marked TODO and had drifted
from the canonical lib/ops-api definition: it lacked error? and
finishedAt? on service progress entries, and used a bare 'string' for
'kind' instead of the ProbeKind union.

Delete the local interface and re-export the canonical type from
lib/ops-api so existing 'import type { ProbeScheduleEntry } from
"./status-tab"' call sites continue to compile against the single
source of truth (CR-B1.1).
2026-04-25 18:55:30 -07:00
Jordan Ritter 02a54712b9 fix(shell-dashboard): replace aliveRef with per-effect cancelled flag
The previous useProbes/useProbeDetail hooks reset aliveRef.current to
true at the top of every effect run. A fetch from the prior cycle that
resolved between cleanup and re-init could pass the new effect's alive
check and clobber state with stale data.

- useProbes: per-effect 'cancelledRef' (mirrored so the imperative
  refetch callback can honor it). Cleanup flips cancelledRef true.
- useProbeDetail: per-effect 'let cancelled = false' closure; cleanup
  flips it. Each effect run gets its own closure, so a stale fetch from
  the prior cycle cannot land setData (CR-B1.3).
- useProbeDetail also clears data immediately on id change so the
  panel header (driven by prop) is never out of sync with the body
  while the new fetch is still in flight (CR-B1.4).
- useTriggerProbe gains a per-call AbortController whose signal is
  forwarded to triggerProbe; unmount aborts any in-flight POST so we
  don't fire duplicate runs after the user navigates away (CR-B1.5).

Tests cover: rapid-deps stale-data path, rapid-id stale-data path,
data-clear on id change, and unmount-during-trigger abort.
2026-04-25 18:55:18 -07:00
Jordan Ritter c3382e5ea1 fix(shell-dashboard): harden ops-api types and error reporting
- ProbeLastRun.summary is now nullable to match ProbeRun.summary; the
  server can complete a run with no summary (showcase-ops CR-A1.5).
- ensureOk surfaces body-read failures with a 'body read failed: ...'
  marker instead of swallowing them silently (CR-B1.6 / CR-C15).
- New parseJson helper attributes JSON parse failures to the requesting
  URL so call sites can tell which endpoint produced bad bytes.
- triggerProbe gains a TriggerProbeOptions interface with an optional
  AbortSignal; passed straight through to fetch() so callers can cancel
  in-flight POSTs (CR-B1.5).
2026-04-25 18:55:07 -07:00
github-actions[bot] d258bcf7d8 style: auto-fix formatting 2026-04-26 01:39:40 +00:00
Jordan Ritter dfdd8d4e88 chore(showcase-dashboard): add missing CatalogCell fields to test fixtures
The CatalogCell type gained three required fields (integration_name,
feature_name, category_name) before this branch landed; the existing
test fixtures predate that change and surface as 32 typecheck errors
in CI. Add plausible Title-Case names to each fixture so the type
checker is happy. Test logic and assertions are unchanged.
2026-04-25 18:34:27 -07:00
Jordan Ritter 77c510c0b5 feat(showcase-dashboard): wire row click selection to open the probe-detail panel
StatusTable gains an optional onSelect(probeId) prop. When provided, rows
become clickable (cursor-pointer) and the action cell stops propagation so
that clicking the trigger button does not also open the drilldown.

StatusTab owns the selection state (useState) and renders StatusDetailPanel
beneath the running panel. The panel returns null when no probe is
selected, so this is a zero-cost addition to the default render.
2026-04-25 18:30:24 -07:00
Jordan Ritter 11b1f39a9f feat(showcase-dashboard): add probe-detail panel with runs list and duration sparkline
Adds three new components for the Status tab drilldown:

- StatusRunsList: recent-runs table with relative-time, duration, state badge
  (running/completed/failed tones), manual chip when triggered=true, and
  pass/total summary with em-dash fallback.
- StatusDurationSparkline: pure-SVG inline sparkline (no charting deps),
  oldest-to-newest convention, dash placeholder for <2 points, configurable
  width/height.
- StatusDetailPanel: renders nothing when probeId is null, shows loading,
  error, and data states from useProbeDetail; composes the runs list and the
  sparkline; close button calls onClose.

All three components ship with red-green vitest + RTL coverage (21 new tests).
2026-04-25 18:30:19 -07:00
Jordan Ritter d7e7b08339 feat(showcase-dashboard): wire D5/D6 PB keys into live-status pipeline
Extends `resolveCell` to read the `d5:<slug>/<featureType>` and
`d6:<slug>/<featureType>` PB row keys produced by the e2e-deep (D5)
and e2e-parity (D6) drivers. The new badges are informational and do
NOT contribute to the rollup — same model as the post-Phase-3 smoke
slot — because the alert engine routes those dimensions independently.

`useLiveStatus` already pulls every dimension via the page-level
unfiltered subscription, so no subscription-shape change is required.
The `d5` / `d6` literals already live in the closed `DIMENSIONS` enum
in showcase/ops/src/types/index.ts.

Adds D5 / D6 chips to `CellStatus` next to the existing E2E badge —
two ~4-line additions that reuse the existing LiveBadge wrapper, no
structural changes to feature-grid / cells-view / parity-view.
2026-04-25 18:29:41 -07:00
Jordan Ritter a53d472428 merge: integrate B3+B4a+B4b deps for B8 scope 2026-04-25 18:25:14 -07:00
Jordan Ritter ffafa2f70a feat(shell-dashboard): add showcase-ops API client and probe hooks for Status tab
Provides the data layer for the dashboard's Status tab against the
showcase-ops HTTP contract being built in parallel (B3).

- lib/ops-api.ts: types + fetchProbes / fetchProbeDetail / triggerProbe.
  baseUrl resolves explicit param → NEXT_PUBLIC_OPS_BASE_URL → /api/ops.
  Trigger sends Bearer token from caller; URL-encodes ids; uniform
  status-line errors on non-2xx.
- hooks/use-probes.ts: useProbes / useProbeDetail / useTriggerProbe.
  10s default polling, AbortController cancels in-flight on unmount or
  interval rollover, AbortError suppressed from user-visible state.

33 new tests (red→green), full shell-dashboard suite green (163/164,
1 pre-existing skip).
2026-04-25 18:06:04 -07:00
Jordan Ritter 8168e37514 feat(showcase-dashboard): add Status tab with probe schedule and run controls
Adds a 5th "Status" tab to shell-dashboard that visualizes the probe
schedule (humanized cron, relative next/last run, duration, pass/fail
result), a "currently running" panel with progress bar and per-service
state grid, and a per-row Trigger control with "Run all" / "Run
specific..." menu.

Components live in src/components: status-tab, status-table,
status-running-panel, status-trigger-button. Local ProbeScheduleEntry
interface is a placeholder; B4b's lib/ops-api types replace it at
merge time. page.tsx wires the tab with an empty placeholder list and
no-op trigger; B4b's useProbes() hook lands in a follow-up.

19 new vitest cases cover humanized cron, relative-time tick, last-run
color tone (green/red/gray), trigger menu actions, and idle vs inflight
panel modes. Full dashboard suite green (150 tests, 1 skipped).
2026-04-25 18:02:24 -07:00
github-actions[bot] 9a6ef908ea style: auto-fix formatting 2026-04-25 05:21:44 +00:00
Jordan Ritter 3f88ad2c90 fix(showcase-dashboard): align e2e key lookup with probe dimension
The e2e-demos probe writes per-demo rows keyed as e2e:<slug>/<featureId>
but resolveCell looked up e2e_smoke:<slug>/<featureId>. The mismatch
caused every per-demo depth chip to show gray (no data), leaving cells
stuck at D2. Fix: read the e2e dimension the probe actually writes.
2026-04-24 22:20:14 -07:00
Jordan Ritter abd5759324 fix(showcase): use single PB subscription to prevent SSE connection race
Seven concurrent useLiveStatus hooks each opened their own PocketBase
SSE connection during hydration. The PB SDK multiplexes subscriptions
over a single EventSource, but the concurrent subscribe() calls raced
the initial SSE handshake — some POST /api/realtime calls fired before
the clientId was available, returning 400. After MAX_RECONNECT_ATTEMPTS
(3), affected hooks set connection="error", triggering the "dashboard
unavailable" banner on all tabs.

Fix: replace 7 per-dimension hooks with a single useLiveStatus() call
(no dimension filter). All rows are fetched in one subscription and
the existing client-side keyFor() lookups filter by dimension
implicitly. Eliminates the 7-way race, reduces PB connection count
from 7 to 1, and removes the aggregateConnection helper entirely.

Verified live at dashboard.showcase.copilotkit.ai — zero banners, all
four tabs show real PB data with D2/D4 depth chips.
2026-04-24 01:04:49 -07:00
github-actions[bot] ba4d47e45f style: auto-fix formatting 2026-04-24 07:49:44 +00:00
Jordan Ritter 692d47bb7c fix(showcase): wire build-time env vars + flatten table alignment
Three fixes batched to minimize PR churn:

1. Dockerfile: add ARG NEXT_PUBLIC_POCKETBASE_URL so the PB URL gets
   baked into the Next.js bundle at build time. Without this, pb.ts
   resolves to the sentinel URL and the dashboard shows "unavailable"
   on every tab. Pre-existing bug exposed by fresh deploys.

2. showcase_deploy.yml: pass NEXT_PUBLIC_POCKETBASE_URL and
   NEXT_PUBLIC_SHELL_URL as build args for the shell-dashboard service
   in the CI matrix. Neither was ever passed before.

3. cell-matrix.tsx + parity-matrix.tsx: flatten nested table pattern
   that caused column misalignment. Category rows used colSpan with
   an inner <table> whose columns floated independently of the header.
   Replaced with useCollapsible hook + flat sibling <tr> rows.

Also regenerates package-lock.json for the plugin-react downgrade
from PR #4241 (npm ci was failing in Docker).

Local Docker build verified with --build-arg for both NEXT_PUBLIC vars.
2026-04-24 00:48:12 -07:00
github-actions[bot] 9acb3e3f24 style: auto-fix formatting 2026-04-24 06:38:58 +00:00
Jordan Ritter a2976ef6f0 fix(showcase): downgrade @vitejs/plugin-react to ^5.2.0
6.0.1 requires vite ^8.0.0 but pnpm resolves vite 7.3.1, causing
ERR_PACKAGE_PATH_NOT_EXPORTED when vitest loads. 5.2.0 supports
vite ^4-8 and resolves the issue. Pre-existing on main.
2026-04-23 23:37:02 -07:00
Jordan Ritter 887e96b36f test(showcase): add unit tests for D0-D4 dashboard components
68 new tests across 7 files: depth derivation, DepthChip, ParityBadge,
CollapsibleCategory, CellMatrix, ParityMatrix, PackagesSection depth.
2026-04-23 23:37:02 -07:00
Jordan Ritter b31a4fa4cf feat(showcase): wire D0-D4 tabs into dashboard page
Lift 7 PB subscriptions to page level, shared across 4 tabs. Add
Cells, Parity, Packages tabs via TabShell. Packages section gains
depth column. live-status dim union widened for D3 e2e data.
2026-04-23 23:37:02 -07:00
Jordan Ritter cbdc4259aa feat(showcase): add Cells and Parity tab components
CellsView + CellMatrix: 663-cell grid with collapse/expand by category,
filter chips, parity-tier column sorting. ParityView + ParityMatrix:
38 features x 17 integrations with LGP reference depth column.
2026-04-23 23:37:02 -07:00
Jordan Ritter 6c53c6e328 feat(showcase): add shared primitives for D0-D4 dashboard tabs
DepthChip, ParityBadge, CollapsibleCategory, IntegrationHeader,
StatsBar, CoverageBar, FilterChips, TabShell, depth-utils, and
CatalogData types.
2026-04-23 23:37:01 -07:00
Jordan Ritter f861bd39b4 fix(showcase): make shell-dashboard import from its own data directory
Shell apps should be independent — no shell cross-imports another
shell's data directory. Replace ../../../shell/src/data/ imports in
shell-dashboard with @/data/ (resolves to src/data/ via tsconfig path
alias). Update Dockerfile to copy shell-docs content (for probe-docs
MDX checks) instead of the entire shell package. Remove unused
bundle-demo-content from dashboard's build pipeline.
2026-04-23 21:12:40 -07:00