Skip to content

feat: new-user onboarding overhaul + realtime-signal & mobile fixes#362

Merged
enyineer merged 5 commits into
mainfrom
feat/onboarding-overhaul
Jun 26, 2026
Merged

feat: new-user onboarding overhaul + realtime-signal & mobile fixes#362
enyineer merged 5 commits into
mainfrom
feat/onboarding-overhaul

Conversation

@enyineer

Copy link
Copy Markdown
Owner

Why

A new user tried to create their first system + health check via the AI assistant and hit several walls: the assistant authored a script check instead of HTTP and couldn't finish the job (a created check was left unassigned, so it never ran), the docs didn't explain assignments or environments, and there were no real architecture diagrams. This PR drastically lowers that first-run hurdle, plus the follow-up fixes surfaced while reviewing it.

Onboarding

  • Atomic healthcheck.createAndAssign RPC (transaction): creates a config + assignment and starts it immediately, so the common 1-1 case can never leave a dormant check.
  • AI healthcheck.propose now prefers the HTTP strategy for a URL (never a script unless asked) and, given assignToSystemId, creates + assigns + starts in one approval. A gated onboarding system-prompt playbook steers HTTP, ask-before-guessing, and one-system-many-environments.
  • Catalog environment AI tools: catalog.createEnvironment, catalog.setSystemEnvironments, catalog.listEnvironments projection.
  • FirstCheckWizard (new @checkstack/ui Stepper): name/pick a system → paste a URL → review → it creates the system, an HTTP check (with a statusCode == 200 assertion), and the assignment in one guided flow. Reachable from the Health Checks empty state and an always-on "Quick start" header button; supports new or existing systems; inline environment nudges (linked to docs).
  • Docs: enable Mermaid (astro-mermaid, client-side); add architecture/onboarding diagrams; add a "what is an assignment / why a check needs one" callout + an Assignments concept section; stop endorsing one-system-per-environment.

AI chat

  • askOperator tool: the assistant asks discrete-choice questions as clickable chips (plus a free-text box) instead of a plaintext list; clicking sends the answer. Built on the existing confirm-card data-card pattern.

Fixes

  • Realtime signals — catalog was the only domain plugin that never broadcast a signal, and healthcheck only emitted run/status signals. Both now broadcast on every config/assignment mutation, so out-of-band writes (AI, GitOps, other pods/users) refresh open clients via the existing SignalAutoInvalidator. This fixes a stale-cache 404 on the system page after an AI-created system. (incident/maintenance/automation/slo/dependency already did this.)
  • associateSystem no longer silently drops the per-assignment notificationPolicy (regression test added).
  • Mobile: the nav drawer (Sheet) was bound to the layout viewport so its bottom hid behind the browser chrome — now h-[100dvh], so it scrolls to the last item; the "Checkstack" wordmark is hidden below sm to de-clutter the navbar.

Tests & verification

  • bun run typecheck and bun run lint clean.
  • New/extended unit + integration tests for: createAndAssign + notificationPolicy, the AI propose HTTP/assign behavior, catalog env tools, the onboarding prompt, the askOperator tool + SSE parser + reducer, the wizard payload logic, and the catalog/healthcheck signals.
  • Docs site builds (astro build).
  • 9 changesets (all minor — platform is in BETA).

Note

The wizard UI and the two mobile changes compile, lint, and unit-test, but the live UI click-through (and the askOperator model round-trip) need a running app + a configured model to eyeball.

🤖 Generated with Claude Code

https://claude.ai/code/session_011v4Nr8YnMGMRziVhgqTc1z

Reduce the initial hurdle new users face around the catalog and health checks,
plus follow-up fixes surfaced during review.

Onboarding:
- Atomic healthcheck.createAndAssign RPC; AI propose tool prefers HTTP over a
  script and creates + assigns in one step; gated onboarding system-prompt.
- catalog.createEnvironment / setSystemEnvironments AI tools + listEnvironments.
- FirstCheckWizard (new @checkstack/ui Stepper): system + HTTP check + assignment
  in one guided flow, from the Health Checks empty state and a "Quick start"
  header button; new-or-existing system; environment nudges.
- Docs: enable Mermaid, add architecture/onboarding diagrams, clarify
  assignments and environments.

AI chat:
- askOperator tool: clickable answer chips instead of plaintext questions.

Fixes:
- catalog + healthcheck now broadcast realtime signals on mutations, so
  out-of-band writes (AI, GitOps, other pods/users) refresh open clients - fixes
  a stale-cache 404 on the system page after AI-created systems.
- Mobile: nav drawer bound to the dynamic viewport (scrolls to the last item);
  navbar wordmark hidden on small screens.
- associateSystem no longer drops the per-assignment notificationPolicy.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011v4Nr8YnMGMRziVhgqTc1z
@changeset-bot

changeset-bot Bot commented Jun 26, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 122ee9a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 142 packages
Name Type
@checkstack/ai-backend Minor
@checkstack/ai-frontend Minor
@checkstack/catalog-backend Minor
@checkstack/catalog-common Minor
@checkstack/common Minor
@checkstack/healthcheck-frontend Minor
@checkstack/catalog-frontend Minor
@checkstack/healthcheck-common Minor
@checkstack/healthcheck-backend Minor
@checkstack/ui Minor
@checkstack/frontend Minor
@checkstack/anomaly-backend Patch
@checkstack/automation-backend Patch
@checkstack/dependency-backend Patch
@checkstack/incident-backend Patch
@checkstack/maintenance-backend Patch
@checkstack/slo-backend Patch
@checkstack/anomaly-common Patch
@checkstack/anomaly-frontend Patch
@checkstack/auth-frontend Patch
@checkstack/automation-frontend Patch
@checkstack/dashboard-frontend Patch
@checkstack/dependency-common Patch
@checkstack/dependency-frontend Patch
@checkstack/incident-common Patch
@checkstack/incident-frontend Patch
@checkstack/maintenance-common Patch
@checkstack/maintenance-frontend Patch
@checkstack/sdk Patch
@checkstack/slo-common Patch
@checkstack/slo-frontend Patch
@checkstack/status-page-frontend Patch
@checkstack/about-common Patch
@checkstack/about-frontend Patch
@checkstack/ai-common Patch
@checkstack/announcement-backend Patch
@checkstack/announcement-common Patch
@checkstack/announcement-frontend Patch
@checkstack/api-docs-common Patch
@checkstack/api-docs-frontend Patch
@checkstack/auth-backend Patch
@checkstack/auth-common Patch
@checkstack/automation-common Patch
@checkstack/backend-api Patch
@checkstack/backend Patch
@checkstack/cache-api Patch
@checkstack/cache-backend Patch
@checkstack/cache-common Patch
@checkstack/cache-frontend Patch
@checkstack/command-backend Patch
@checkstack/command-common Patch
@checkstack/command-frontend Patch
@checkstack/dev-server Patch
@checkstack/frontend-api Patch
@checkstack/gitops-backend Patch
@checkstack/gitops-common Patch
@checkstack/gitops-frontend Patch
@checkstack/infrastructure-common Patch
@checkstack/infrastructure-frontend Patch
@checkstack/integration-backend Patch
@checkstack/integration-common Patch
@checkstack/integration-frontend Patch
@checkstack/notification-backend Patch
@checkstack/notification-common Patch
@checkstack/notification-frontend Patch
@checkstack/pluginmanager-common Patch
@checkstack/pluginmanager-frontend Patch
@checkstack/queue-api Patch
@checkstack/queue-backend Patch
@checkstack/queue-common Patch
@checkstack/queue-frontend Patch
@checkstack/satellite-backend Patch
@checkstack/satellite-common Patch
@checkstack/satellite-frontend Patch
@checkstack/satellite Patch
@checkstack/script-packages-backend Patch
@checkstack/script-packages-common Patch
@checkstack/script-packages-frontend Patch
@checkstack/script-packages-store-postgres Patch
@checkstack/script-packages-store-s3 Patch
@checkstack/scripts Patch
@checkstack/secrets-backend-local Patch
@checkstack/secrets-backend-vault Patch
@checkstack/secrets-backend Patch
@checkstack/secrets-common Patch
@checkstack/secrets-frontend Patch
@checkstack/signal-backend Patch
@checkstack/signal-common Patch
@checkstack/status-page-backend Patch
@checkstack/status-page-common Patch
@checkstack/template-engine Patch
@checkstack/test-utils-backend Patch
@checkstack/theme-backend Patch
@checkstack/theme-common Patch
@checkstack/theme-frontend Patch
@checkstack/tips-backend Patch
@checkstack/tips-common Patch
@checkstack/tips-frontend Patch
@checkstack/auth-credential-backend Patch
@checkstack/auth-github-backend Patch
@checkstack/auth-ldap-backend Patch
@checkstack/auth-saml-backend Patch
@checkstack/cache-memory-backend Patch
@checkstack/cache-memory-common Patch
@checkstack/collector-hardware-backend Patch
@checkstack/healthcheck-dns-backend Patch
@checkstack/healthcheck-grpc-backend Patch
@checkstack/healthcheck-http-backend Patch
@checkstack/healthcheck-jenkins-backend Patch
@checkstack/healthcheck-mysql-backend Patch
@checkstack/healthcheck-ping-backend Patch
@checkstack/healthcheck-postgres-backend Patch
@checkstack/healthcheck-rcon-backend Patch
@checkstack/healthcheck-rcon-common Patch
@checkstack/healthcheck-redis-backend Patch
@checkstack/healthcheck-script-backend Patch
@checkstack/healthcheck-ssh-backend Patch
@checkstack/healthcheck-ssh-common Patch
@checkstack/healthcheck-tcp-backend Patch
@checkstack/healthcheck-tls-backend Patch
@checkstack/integration-jira-backend Patch
@checkstack/integration-jira-common Patch
@checkstack/integration-script-backend Patch
@checkstack/integration-teams-backend Patch
@checkstack/integration-webex-backend Patch
@checkstack/integration-webhook-backend Patch
@checkstack/notification-backstage-backend Patch
@checkstack/notification-discord-backend Patch
@checkstack/notification-gotify-backend Patch
@checkstack/notification-pushover-backend Patch
@checkstack/notification-slack-backend Patch
@checkstack/notification-smtp-backend Patch
@checkstack/notification-teams-backend Patch
@checkstack/notification-telegram-backend Patch
@checkstack/notification-webex-backend Patch
@checkstack/queue-bullmq-backend Patch
@checkstack/queue-bullmq-common Patch
@checkstack/queue-memory-backend Patch
@checkstack/queue-memory-common Patch
@checkstack/cache-utils Patch
create-checkstack-plugin Patch
@checkstack/signal-frontend Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Covers the APP_DOC_SLUGS.environments addition flagged by the changeset
coverage check.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011v4Nr8YnMGMRziVhgqTc1z
@github-actions

Copy link
Copy Markdown
Contributor

❌ PR Checks Failed

Check Status
Typecheck ❌ Failed
Lint ✅ Passed
Deps ✅ Passed
Test ❌ Failed
Integration ✅ Passed
Security ❌ Failed
E2E ❌ Failed
❌ Typecheck Errors
Output not available.

How to fix: Run bun run typecheck locally to reproduce these errors. Inspect the reported files and line numbers, then fix all type errors. Do not use any as an explicit type or @ts-ignore to suppress errors. Ensure all function signatures, return types, and variable types are correct.

❌ Test Failures
... (truncated 10304 lines)

::error file=scripts/generate-docs-index.test.ts,line=267,col=31,title=error: expect(received).toBe(expected)::Expected: 0%0AReceived: 1%0A%0A      at <anonymous> (/home/runner/work/checkstack/checkstack/scripts/generate-docs-index.test.ts:267:31)
(fail) generate-docs-index --check drift guard > --check passes on a clean tree (committed index matches a fresh run) [58.59ms]
284 |       expect(result.stderr).toContain("out of sync");
285 |     } finally {
286 |       writeFileSync(target, before, "utf8");
287 |     }
288 |     // Tree restored → clean again.
289 |     expect(runCheck().status).toBe(0);
                                    ^
error: expect(received).toBe(expected)

Expected: 0
Received: 1

      at <anonymous> (/home/runner/work/checkstack/checkstack/scripts/generate-docs-index.test.ts:289:31)

::error file=scripts/generate-docs-index.test.ts,line=289,col=31,title=error: expect(received).toBe(expected)::Expected: 0%0AReceived: 1%0A%0A      at <anonymous> (/home/runner/work/checkstack/checkstack/scripts/generate-docs-index.test.ts:289:31)
(fail) generate-docs-index --check drift guard > --check exits nonzero when the committed index is mutated [120.32ms]

::endgroup::

::group::scripts/eslint-rules/no-extraneous-runtime-deps.test.ts:
(pass) no-extraneous-runtime-deps > valid > import { CodeEditor } from "@checkstack/ui/code-editor"; [33.85ms]
(pass) no-extraneous-runtime-deps > valid > import { Button } from "@checkstack/ui"; [3.17ms]
(pass) no-extraneous-runtime-deps > invalid > import x from "totally-not-installed-pkg"; [4.30ms]

::endgroup::

::group::scripts/eslint-rules/no-unguarded-animation.test.ts:
(pass) no-unguarded-animation > valid > const x = <div className="flex gap-2" />; [5.88ms]
(pass) no-unguarded-animation > valid > const x = "animate-spin fade-in"; [2.15ms]
(pass) no-unguarded-animation > valid > const x = <div className={cn("base", !isLowPower && "animate-spin")} />; [4.16ms]
(pass) no-unguarded-animation > valid > const x = <div className={isLowPower ? "" : "animate-pulse"} />; [3.59ms]
(pass) no-unguarded-animation > valid > 
        const { isLowPower } = usePerformance();
        const cls = isLowPower ? "" : "x";
        const x = <div className={`animate-in fade-in-0 ${cls}`} />;
       [4.75ms]
(pass) no-unguarded-animation > valid > const x = <div className="fade-in-0" />; [3.00ms]
(pass) no-unguarded-animation > valid > const x = <div className={cn(!reduceMotion && "animate-spin")} />; [2.64ms]
(pass) no-unguarded-animation > valid > const x = <div className="bg-card border" />; [2.08ms]
(pass) no-unguarded-animation > valid > const x = <div className={cn(!isLowPower && "animate-spin", "animate-pulse")} />; [2.13ms]
(pass) no-unguarded-animation > invalid > const x = <div className="animate-spin" />; [1.94ms]
(pass) no-unguarded-animation > invalid > const x = <div className={`animate-in fade-in-0 duration-200`} />; [2.17ms]
(pass) no-unguarded-animation > invalid > const x = <div className="backdrop-blur-sm bg-card/80" />; [1.74ms]
(pass) no-unguarded-animation > invalid > const x = <div className={cn("base", "animate-pulse")} />; [2.50ms]
(pass) no-unguarded-animation > invalid > const x = <div className="slide-in-from-top zoom-in-95" />; [2.06ms]

::endgroup::

::group::scripts/eslint-rules/no-pod-local-entity-state.test.ts:
(pass) no-pod-local-entity-state > valid > entityPoint.defineEntity({ kind: "incident", state: s, read: createIncidentEntityRead(service) }); [5.24ms]
(pass) no-pod-local-entity-state > valid > defineEntity({ kind: "system", state: s, read: (ids) => createCatalogSystemEntityRead(svc())(ids) }); [3.29ms]
(pass) no-pod-local-entity-state > valid > defineEntity({ kind: "health", state: s, read: (ids) => { const db = x; return createHealthEntityRead({ db, service })(ids); } }); [3.00ms]
(pass) no-pod-local-entity-state > valid > defineEntity({ kind: "k", state: s, read: (ids) => db.select().from(t).where(inArray(t.id, ids)) }); [2.27ms]
(pass) no-pod-local-entity-state > valid > defineEntity({ kind: "k", state: s, read: (ids) => service.getManyEntityStates(ids) }); [2.38ms]
(pass) no-pod-local-entity-state > valid > const readState = (ids) => db.select().from(t); defineEntity({ kind: "k", state: s, read: readState }); [3.16ms]
(pass) no-pod-local-entity-state > valid > defineEntity({ kind: "ws-registry", state: s, read: (ids) => connections.get(ids[0]) }); [2.83ms]
(pass) no-pod-local-entity-state > valid > defineEntity({ kind: "k", state: s, read: opaqueRead }); [1.98ms]
(pass) no-pod-local-entity-state > valid > defineEntity({ kind: "k", state: s, read: (ids) => cache.get(ids[0]) }); [2.44ms]
(pass) no-pod-local-entity-state > valid > someOther({ read: (ids) => cache.get(ids[0]) }); [2.10ms]
(pass) no-pod-local-entity-state > invalid > const m = new Map(); defineEntity({ kind: "k", state: s, read: (ids) => ids.map((id) => m.get(id)) }); [3.05ms]
(pass) no-pod-local-entity-state > invalid > defineEntity({ kind: "k", state: s, read: (ids) => connections.get(ids[0]) }); [2.23ms]
(pass) no-pod-local-entity-state > invalid > defineEntity({ kind: "k", state: s, read: (ids) => { const store = new Map(); return store; } }); [2.35ms]
(pass) no-pod-local-entity-state > invalid > defineEntity({ kind: "k", state: s, read: importedOpaqueRead }); [1.80ms]
(pass) no-pod-local-entity-state > invalid > defineEntity({ kind: "k", state: s, read: (ids) => mysteryFetch(ids) }); [1.88ms]
(pass) no-pod-local-entity-state > invalid > defineEntity({ kind: "k", state: s, read: (ids) => { db.select(); return cache.get(ids[0]); } }); [2.43ms]

::endgroup::

::group::scripts/eslint-rules/no-unmanaged-entity-state.test.ts:
(pass) no-unmanaged-entity-state > valid > const h = createHook("notification.delivered"); [5.17ms]
(pass) no-unmanaged-entity-state > valid > const h = createHook("incident.created"); [1.68ms]
(pass) no-unmanaged-entity-state > valid > const h = createHook("auth.user.deleted"); [1.84ms]
(pass) no-unmanaged-entity-state > valid > const h = createHook(buildId()); [1.34ms]
(pass) no-unmanaged-entity-state > valid > db.update(incidents).set({ status: "open" }); [1.32ms]
(pass) no-unmanaged-entity-state > valid > db.update(incidents).set({ status: "open" }); [1.53ms]
(pass) no-unmanaged-entity-state > valid > db.update(incidents).set({ title: "x" }); [1.39ms]
(pass) no-unmanaged-entity-state > valid > cache.update(incidents).set({ status: "open" }); [1.29ms]
(pass) no-unmanaged-entity-state > invalid > const h = createHook("incident.created"); [1.29ms]
(pass) no-unmanaged-entity-state > invalid > const h = createHook("incident.updated"); [1.07ms]
(pass) no-unmanaged-entity-state > invalid > const h = createHook("catalog.system.deleted"); [1.27ms]
(pass) no-unmanaged-entity-state > invalid > const h = createHook("healthcheck.system.health_changed"); [1.28ms]
(pass) no-unmanaged-entity-state > invalid > const h = createHook("incident.resolved"); [1.19ms]
(pass) no-unmanaged-entity-state > invalid > const h = api.createHook("maintenance.updated"); [1.39ms]
(pass) no-unmanaged-entity-state > invalid > db.update(incidents).set({ status: "resolved" }); [1.72ms]
(pass) no-unmanaged-entity-state > invalid > tx.insert(incidents).values({ status: "open", title: "x" }); [1.65ms]

::endgroup::

2 tests failed:
(fail) generate-docs-index --check drift guard > --check passes on a clean tree (committed index matches a fresh run) [58.59ms]
(fail) generate-docs-index --check drift guard > --check exits nonzero when the committed index is mutated [120.32ms]

 188 pass
 2 fail
 339 expect() calls
Ran 190 tests across 11 files. [933.00ms]

How to fix: Run bun test locally to reproduce these failures. Read the failing test assertions carefully and fix the implementation code so the tests pass. Do not modify test expectations unless the test itself is clearly wrong. If a test is testing new functionality you introduced, make sure your implementation satisfies the test's contract.

❌ Security Audit Failures
🔎 Security Audit (image + filesystem)

❌ Fixable vulnerabilities (6) — these fail the build:
  [MEDIUM] astro 6.4.2 -> fixed in 6.4.6  (CVE-2026-54298)
  [MEDIUM] vite 7.3.2 -> fixed in 8.0.16, 7.3.5, 6.4.3  (CVE-2026-53632)
  [MEDIUM] js-yaml 4.1.1 -> fixed in 4.2.0  (CVE-2026-53550)
  [HIGH] astro 6.4.2 -> fixed in 6.4.6  (CVE-2026-54299)
  [HIGH] vite 7.3.2 -> fixed in 8.0.16, 7.3.5, 6.4.3  (CVE-2026-53571)
  [HIGH] devalue 5.8.0 -> fixed in 5.8.1  (CVE-2026-42570)

⚠️  Known unfixed vulnerabilities (0) — surfaced, not gated:
  (none)

How to fix: Only fixable findings (a patched version exists) fail the build; unfixed findings are surfaced as warnings, not gated. Run bun run audit:image (production image) and bun run audit:security (filesystem / full dependency graph incl. devDependencies) locally to reproduce. Bump the affected direct dependency, or for a transitive package add a documented entry to security/managed-overrides.json plus matching overrides/resolutions in package.json (then bun install). Re-run bun run audit:overrides:check to confirm the override is consistent.

❌ E2E Failures
... (truncated 245 lines)
    Error: strict mode violation: getByText('No health checks yet') resolved to 2 elements:
        1) <p class="text-foreground font-medium">No health checks yet</p> aka getByText('No health checks yet', { exact: true })
        2) <div class="text-sm text-muted-foreground mt-1 max-w-prose">No health checks yet. The quickest way to start i…</div> aka getByText('No health checks yet. The')

    Call log:
    �[2m  - Expect "toBeVisible" with timeout 5000ms�[22m
    �[2m  - waiting for getByText('No health checks yet')�[22m


      205 |     // ListEmptyState renders "No {resource} yet" (a styled <p>, not a heading)
      206 |     // plus the descriptive copy.
    > 207 |     await expect(page.getByText("No health checks yet")).toBeVisible();
          |                                                          ^
      208 |     await expect(
      209 |       page.getByText(
      210 |         "No health checks have been configured yet. Create one to start monitoring a system.",
        at /home/runner/work/checkstack/checkstack/core/e2e/tests/onboarding.empty.spec.ts:207:58

    attachment #1: screenshot (image/png) ──────────────────────────────────────────────────────────
    test-results/onboarding.empty-Health-ch-3f87b-ate-before-any-checks-exist-empty-state/test-failed-1.png
    ────────────────────────────────────────────────────────────────────────────────────────────────

    Error Context: test-results/onboarding.empty-Health-ch-3f87b-ate-before-any-checks-exist-empty-state/error-context.md

    Retry #1 ───────────────────────────────────────────────────────────────────────────────────────

    Error: �[2mexpect(�[22m�[31mlocator�[39m�[2m).�[22mtoBeVisible�[2m(�[22m�[2m)�[22m failed

    Locator: getByText('No health checks yet')
    Expected: visible
    Error: strict mode violation: getByText('No health checks yet') resolved to 2 elements:
        1) <p class="text-foreground font-medium">No health checks yet</p> aka getByText('No health checks yet', { exact: true })
        2) <div class="text-sm text-muted-foreground mt-1 max-w-prose">No health checks yet. The quickest way to start i…</div> aka getByText('No health checks yet. The')

    Call log:
    �[2m  - Expect "toBeVisible" with timeout 5000ms�[22m
    �[2m  - waiting for getByText('No health checks yet')�[22m


      205 |     // ListEmptyState renders "No {resource} yet" (a styled <p>, not a heading)
      206 |     // plus the descriptive copy.
    > 207 |     await expect(page.getByText("No health checks yet")).toBeVisible();
          |                                                          ^
      208 |     await expect(
      209 |       page.getByText(
      210 |         "No health checks have been configured yet. Create one to start monitoring a system.",
        at /home/runner/work/checkstack/checkstack/core/e2e/tests/onboarding.empty.spec.ts:207:58

    attachment #1: screenshot (image/png) ──────────────────────────────────────────────────────────
    test-results/onboarding.empty-Health-ch-3f87b-ate-before-any-checks-exist-empty-state-retry1/test-failed-1.png
    ────────────────────────────────────────────────────────────────────────────────────────────────

    Error Context: test-results/onboarding.empty-Health-ch-3f87b-ate-before-any-checks-exist-empty-state-retry1/error-context.md

    attachment #3: trace (application/zip) ─────────────────────────────────────────────────────────
    test-results/onboarding.empty-Health-ch-3f87b-ate-before-any-checks-exist-empty-state-retry1/trace.zip
    Usage:

        npx playwright show-trace test-results/onboarding.empty-Health-ch-3f87b-ate-before-any-checks-exist-empty-state-retry1/trace.zip

    ────────────────────────────────────────────────────────────────────────────────────────────────

    Retry #2 ───────────────────────────────────────────────────────────────────────────────────────

    Error: �[2mexpect(�[22m�[31mlocator�[39m�[2m).�[22mtoBeVisible�[2m(�[22m�[2m)�[22m failed

    Locator: getByText('No health checks yet')
    Expected: visible
    Error: strict mode violation: getByText('No health checks yet') resolved to 2 elements:
        1) <p class="text-foreground font-medium">No health checks yet</p> aka getByText('No health checks yet', { exact: true })
        2) <div class="text-sm text-muted-foreground mt-1 max-w-prose">No health checks yet. The quickest way to start i…</div> aka getByText('No health checks yet. The')

    Call log:
    �[2m  - Expect "toBeVisible" with timeout 5000ms�[22m
    �[2m  - waiting for getByText('No health checks yet')�[22m


      205 |     // ListEmptyState renders "No {resource} yet" (a styled <p>, not a heading)
      206 |     // plus the descriptive copy.
    > 207 |     await expect(page.getByText("No health checks yet")).toBeVisible();
          |                                                          ^
      208 |     await expect(
      209 |       page.getByText(
      210 |         "No health checks have been configured yet. Create one to start monitoring a system.",
        at /home/runner/work/checkstack/checkstack/core/e2e/tests/onboarding.empty.spec.ts:207:58

    attachment #1: screenshot (image/png) ──────────────────────────────────────────────────────────
    test-results/onboarding.empty-Health-ch-3f87b-ate-before-any-checks-exist-empty-state-retry2/test-failed-1.png
    ────────────────────────────────────────────────────────────────────────────────────────────────

    Error Context: test-results/onboarding.empty-Health-ch-3f87b-ate-before-any-checks-exist-empty-state-retry2/error-context.md

  1 failed
    [empty-state] › tests/onboarding.empty.spec.ts:189:3 › Health checks (empty state) › config list shows the empty state before any checks exist 
  33 did not run
  48 passed (53.0s)
::notice title=🎭 Playwright Run Summary::  1 failed%0A    [empty-state] › tests/onboarding.empty.spec.ts:189:3 › Health checks (empty state) › config list shows the empty state before any checks exist %0A  33 did not run%0A  48 passed (53.0s)
[e2e] stopping ephemeral Postgres...
[e2e] teardown complete.

How to fix: These are the Playwright end-to-end tests. Reproduce locally with bun run --filter @checkstack/e2e test:e2e (it provisions an ephemeral Postgres via Testcontainers, so Docker must be running). Read the failing assertions and uploaded traces, then fix the implementation or the selectors so the flows pass. Do not weaken or skip the tests.

@enyineer The above code quality issues were found in this PR. Please fix them before merging.

CI caught two issues from the docs and onboarding changes:
- The bundled AI docs index (core/ai-backend/src/generated/docs-index.ts) was
  stale after the docs markdown changes; regenerate it (fixes the Typecheck
  docs-index check and the drift-guard test).
- The Health Checks empty-state description started with "No health checks yet",
  colliding with ListEmptyState's title of the same text (strict-mode violation
  in onboarding.empty.spec.ts). Reword the description and update the e2e spec to
  the new copy + the new guided-setup buttons.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011v4Nr8YnMGMRziVhgqTc1z
@github-actions

Copy link
Copy Markdown
Contributor

❌ PR Checks Failed

Check Status
Typecheck ✅ Passed
Lint ✅ Passed
Deps ✅ Passed
Test ✅ Passed
Integration ✅ Passed
Security ❌ Failed
E2E ✅ Passed
❌ Security Audit Failures
🔎 Security Audit (image + filesystem)

❌ Fixable vulnerabilities (6) — these fail the build:
  [MEDIUM] astro 6.4.2 -> fixed in 6.4.6  (CVE-2026-54298)
  [MEDIUM] vite 7.3.2 -> fixed in 8.0.16, 7.3.5, 6.4.3  (CVE-2026-53632)
  [MEDIUM] js-yaml 4.1.1 -> fixed in 4.2.0  (CVE-2026-53550)
  [HIGH] astro 6.4.2 -> fixed in 6.4.6  (CVE-2026-54299)
  [HIGH] vite 7.3.2 -> fixed in 8.0.16, 7.3.5, 6.4.3  (CVE-2026-53571)
  [HIGH] devalue 5.8.0 -> fixed in 5.8.1  (CVE-2026-42570)

⚠️  Known unfixed vulnerabilities (0) — surfaced, not gated:
  (none)

How to fix: Only fixable findings (a patched version exists) fail the build; unfixed findings are surfaced as warnings, not gated. Run bun run audit:image (production image) and bun run audit:security (filesystem / full dependency graph incl. devDependencies) locally to reproduce. Bump the affected direct dependency, or for a transitive package add a documented entry to security/managed-overrides.json plus matching overrides/resolutions in package.json (then bun install). Re-run bun run audit:overrides:check to confirm the override is consistent.

@enyineer The above code quality issues were found in this PR. Please fix them before merging.

Trivy flagged 6 fixable CVEs in pre-existing build-tooling deps (astro, vite,
js-yaml, devalue). bun only honors flat overrides, so the conflicting majors
(frontend vite@8 vs astro's vite@7; changesets read-yaml-file's js-yaml@3 vs the
v4 consumers) can't be pinned surgically. A fresh resolve against the existing
semver ranges bumps each consumer to its latest in-range version:

- astro 6.4.2 -> 6.4.8        (CVE-2026-54298, CVE-2026-54299)
- vite 7.3.2 -> 7.3.6         (CVE-2026-53632, CVE-2026-53571; frontend vite@8 kept)
- js-yaml 4.1.1 -> 4.2.0      (CVE-2026-53550; read-yaml-file's js-yaml@3 kept)
- devalue 5.8.0 -> 5.8.1      (CVE-2026-42570)

Lockfile-only change; package.json ranges unchanged. typecheck, lint, and the
docs build pass locally.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011v4Nr8YnMGMRziVhgqTc1z
@github-actions

Copy link
Copy Markdown
Contributor

❌ PR Checks Failed

Check Status
Typecheck ✅ Passed
Lint ✅ Passed
Deps ✅ Passed
Test ✅ Passed
Integration ✅ Passed
Security ✅ Passed
E2E ✅ Passed

@enyineer The above code quality issues were found in this PR. Please fix them before merging.

The full lockfile re-resolve cleared the 6 build-dep CVEs but bumped the
frontend's vite/rolldown (and at least one further transitive dep) and broke the
frontend build (E2E Build). Pinning vite to 8.0.16 and rolldown to 1.0.3 did not
restore it, and the bundler swallows the underlying error messages, so a clean
fix isn't tractable inside this PR. Revert bun.lock to the working state; the
pre-existing CVEs are better handled in a dedicated, carefully-tested dependency
PR.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011v4Nr8YnMGMRziVhgqTc1z
@github-actions

Copy link
Copy Markdown
Contributor

❌ PR Checks Failed

⚠️ Escalation: Automated fixes have not resolved the issues after 3 attempts. Manual intervention is required.

Check Status
Typecheck ✅ Passed
Lint ✅ Passed
Deps ✅ Passed
Test ✅ Passed
Integration ✅ Passed
Security ❌ Failed
E2E ✅ Passed
❌ Security Audit Failures
🔎 Security Audit (image + filesystem)

❌ Fixable vulnerabilities (6) — these fail the build:
  [MEDIUM] astro 6.4.2 -> fixed in 6.4.6  (CVE-2026-54298)
  [MEDIUM] vite 7.3.2 -> fixed in 8.0.16, 7.3.5, 6.4.3  (CVE-2026-53632)
  [MEDIUM] js-yaml 4.1.1 -> fixed in 4.2.0  (CVE-2026-53550)
  [HIGH] astro 6.4.2 -> fixed in 6.4.6  (CVE-2026-54299)
  [HIGH] vite 7.3.2 -> fixed in 8.0.16, 7.3.5, 6.4.3  (CVE-2026-53571)
  [HIGH] devalue 5.8.0 -> fixed in 5.8.1  (CVE-2026-42570)

⚠️  Known unfixed vulnerabilities (0) — surfaced, not gated:
  (none)

How to fix: Only fixable findings (a patched version exists) fail the build; unfixed findings are surfaced as warnings, not gated. Run bun run audit:image (production image) and bun run audit:security (filesystem / full dependency graph incl. devDependencies) locally to reproduce. Bump the affected direct dependency, or for a transitive package add a documented entry to security/managed-overrides.json plus matching overrides/resolutions in package.json (then bun install). Re-run bun run audit:overrides:check to confirm the override is consistent.

@enyineer The above code quality issues were found in this PR. Automated fixes have not resolved them after 3 attempts. Manual intervention is required.

@enyineer enyineer merged commit defb97b into main Jun 26, 2026
15 of 17 checks passed
@enyineer enyineer deleted the feat/onboarding-overhaul branch June 26, 2026 16:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant