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.
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.
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.
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.
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.
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.
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.
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).
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.
- 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).
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.
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.
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).
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.
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).
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).
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.
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.
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.
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.
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.
Every generator embedded `generated_at: new Date().toISOString()` in its
output, causing constant git noise on every build/dev run even when
actual content was unchanged. Remove the field from all 4 generator
scripts, all consumer interfaces (Registry, BundledContent,
BundledStarters, DocsStatusBundle), inline type casts, and test
assertions.
Also: add shell-dashboard as a generate-registry output directory (it
was cross-importing from shell); move probe-docs output to
shell-dashboard/src/data/ (sole consumer); update test beforeAll to
generate files instead of restoring from git HEAD (prep for gitignore).
Declare open-gen-ui and open-gen-ui-advanced in langgraph-python
manifest (code existed, was never registered). Add both to
constrained-explicit allowlist, fill shell_docs_path for 5 demos,
add hitl-in-app override, drop stale chat-customization-css fallback.
Regenerate registry.json, demo-content.json, constraints.json,
and docs-status.json across shell / shell-dojo / shell-docs.
Bump feature/demo count assertion 30→32 in generate-registry test.
Extend check-binaries.sh whitelist for sister-shell demo-content.
showcase/scripts/package.json bumped vitest to ^4.1.5 and added
@vitest/coverage-v8 ^4.1.5, but package-lock.json still carried vitest
4.1.4 and was missing the coverage package entirely. Docker builder
stage runs `npm ci`, which refuses to reconcile drift and fails with:
Missing: @vitest/coverage-v8@4.1.5 from lock file
Invalid: lock file's vitest@4.1.4 does not satisfy vitest@4.1.5
Regenerated showcase/scripts/package-lock.json with
`npm install --package-lock-only` to bring it back in sync.
Also removed --silent from the builder-stage `npm ci` calls so the
next same-class lockfile drift surfaces the real error in CI instead
of a bare 'exit code: 1'. Kept --silent on the prod-deps stage (that
one wasn't masking anything here).
Verified: `docker build -f showcase/shell-dashboard/Dockerfile .`
completes end-to-end locally.
Replace the static status.json feed with a live PocketBase subscription
(useLiveStatus / useLastTransition hooks + live-status lib + pb client),
add vitest + playwright visual test setup, wire feature-grid + cell
pieces + badges to the live data model. Adds Dockerfile + .dockerignore
for Railway deployment of the dashboard.
The shell-dashboard app baked http://localhost:3000 into every demo and code link because NEXT_PUBLIC_SHELL_URL was never provided at build time and the source defaulted to localhost. Next.js inlines NEXT_PUBLIC_* at next build, so setting the value on Railway at runtime does nothing.
Fix: remove the silent localhost fallback, pass NEXT_PUBLIC_SHELL_URL as a Docker build arg from showcase_deploy.yml, and fail loudly if it's unset at build so this can't regress silently.
- Renamed showcase/shell-internal/ → showcase/shell-dashboard/ (git mv for history).
- Updated package name to @copilotkit/showcase-shell-dashboard (+ lockfile).
- Updated external references: showcase/scripts/{probe-docs,generate-status}.ts comments and showcase/README.md.
- Added dispatch + filter + ALL_SERVICES entry for shell-dashboard in .github/workflows/showcase_deploy.yml.
- Provisioned Railway service showcase-shell-dashboard (id 4d5dfd74-be61-40b2-8564-b53b7dd4c15b):
- image source ghcr.io/copilotkit/showcase-shell-dashboard:latest
- env vars OPENAI_API_KEY, ANTHROPIC_API_KEY, PORT copied from showcase-shell
- custom domain dashboard.showcase.copilotkit.ai attached (CNAME → 6u9icvje.up.railway.app)
- image will be built and pushed via showcase_deploy.yml on merge