Skip to content

Connection health checks (liveness) with OpenAPI backing#1108

Open
RhysSullivan wants to merge 1 commit into
mainfrom
claude/health-checks
Open

Connection health checks (liveness) with OpenAPI backing#1108
RhysSullivan wants to merge 1 commit into
mainfrom
claude/health-checks

Conversation

@RhysSullivan

@RhysSullivan RhysSullivan commented Jun 23, 2026

Copy link
Copy Markdown
Owner

Base of a 4-PR stack. A connection declares ONE authenticated operation that answers "is this credential still alive?". The probe maps HTTP status to a health state (2xx healthy, 401/403 expired, other degraded, unset unknown) — built for short-lived OAuth tokens (Google's 7-day dev-token revocation).

  • Core vocabulary (HealthStatus, HealthCheckSpec, HealthCheckResult, HealthCheckCandidate, classifyHttpStatus, ranking) + dispatch (integrations.healthCheck.{get,candidates,set}, connections.{checkHealth,validate}).
  • OpenAPI backing: ranked candidates, persist the chosen spec (merging over raw config so provider supersets keep their keys), run the probe.
  • React: per-connection status dot + "Check now"; a self-hiding operation editor; and "Check the key works" in the Add Connection modal — probe the pasted key before saving, and when no check is configured yet, pick a read-only operation inline (a healthy probe saves it as the integration's check).

The operation picker is a base-ui combobox whose popup portals out of the surrounding Radix modal, so this PR also carries the fixes that make it usable in a modal (folded in from the former combobox-sheet PR, since both the editor sheet and the Add Connection dialog need them): pointer-events on the popup, an outside-interaction guard on the dialog AND the sheet so clicking an option does not dismiss the modal, and a non-modal editor sheet so the popup list can scroll.

Built on the self-contained add-connection modal (#1126): the key-check state lives inside the modal view, so closing unmounts and resets it (no hand-reset).

e2e: saved connection healthy -> expired; unconfigured -> unknown; the operation picker filters a hundreds-long list on the edit sheet and add screen; Add Connection checks the key against an inline-picked operation BY MOUSE and persists it; the editor-sheet combobox is clickable and scrollable.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 23, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
executor-marketing d1aa3ea Commit Preview URL

Branch Preview URL
Jun 26 2026, 02:28 AM

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 23, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
executor-cloud d1aa3ea Jun 26 2026, 02:30 AM

@github-actions

github-actions Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Cloudflare preview

Console https://executor-preview-pr-1108.executor-e2e.workers.dev
MCP https://executor-preview-pr-1108.executor-e2e.workers.dev/mcp
Deployed commit d1aa3ea

Sign-in is Cloudflare Access (one-time PIN to an allowed email). The preview has its own database and encryption key; it is destroyed when this PR closes.

@pkg-pr-new

pkg-pr-new Bot commented Jun 23, 2026

Copy link
Copy Markdown

Open in StackBlitz

@executor-js/cli

npm i https://pkg.pr.new/@executor-js/cli@1108

@executor-js/config

npm i https://pkg.pr.new/@executor-js/config@1108

@executor-js/execution

npm i https://pkg.pr.new/@executor-js/execution@1108

@executor-js/sdk

npm i https://pkg.pr.new/@executor-js/sdk@1108

@executor-js/codemode-core

npm i https://pkg.pr.new/@executor-js/codemode-core@1108

@executor-js/runtime-quickjs

npm i https://pkg.pr.new/@executor-js/runtime-quickjs@1108

@executor-js/plugin-file-secrets

npm i https://pkg.pr.new/@executor-js/plugin-file-secrets@1108

@executor-js/plugin-graphql

npm i https://pkg.pr.new/@executor-js/plugin-graphql@1108

@executor-js/plugin-keychain

npm i https://pkg.pr.new/@executor-js/plugin-keychain@1108

@executor-js/plugin-mcp

npm i https://pkg.pr.new/@executor-js/plugin-mcp@1108

@executor-js/plugin-onepassword

npm i https://pkg.pr.new/@executor-js/plugin-onepassword@1108

@executor-js/plugin-openapi

npm i https://pkg.pr.new/@executor-js/plugin-openapi@1108

executor

npm i https://pkg.pr.new/executor@1108

commit: d1aa3ea

@greptile-apps

greptile-apps Bot commented Jun 23, 2026

Copy link
Copy Markdown

Greptile Summary

This PR introduces connection health checks (liveness probes) backed by OpenAPI operations. A connection declares one authenticated operation; its HTTP response is mapped to healthy / expired / degraded / unknown, with expired (401/403) being the primary target for Google's 7-day OAuth dev-token revocation case.

  • Core vocabulary and dispatch: HealthStatus, HealthCheckSpec, HealthCheckResult, HealthCheckCandidate, classifyHttpStatus, and compareHealthCheckCandidates live in the browser-safe SDK; OpenAPI-backed implementations (checkHealthOpenApi, listHealthCheckCandidatesOpenApi, setHealthCheckOpenApi) live in the plugin; two new API endpoints (checkHealth on saved connections, validate on in-flight credentials) complete the server surface.
  • React surfaces: a self-hiding HealthCheckEditor sheet (non-modal to avoid scroll-locking the portaled combobox), per-connection status dots with a "Check now" affordance in AccountRow, and an inline key-check section in the Add Connection modal that lets the user pick a read-only operation to probe the pasted key before saving — a healthy inline probe auto-saves the spec as the integration's health check.
  • e2e coverage: API-level scenarios validate ranking, healthy-to-expired lifecycle, and unknown state; six browser scenarios exercise the operation picker combobox, click-without-dismiss behavior, and scroll-lock freedom.

Confidence Score: 5/5

Safe to merge. The core vocabulary, ranking, HTTP classification, and probe dispatch are all straightforward and well-tested by the new e2e scenarios.

The health-check spec save in the add-connection modal is a secondary UX path (the primary key validation still works correctly), and a silent failure there is non-destructive. No data integrity, security, or correctness issues were found in the probe dispatch, HTTP status classification, config read-modify-write, or the non-modal sheet design.

packages/react/src/components/add-account-modal.tsx — the inline spec save after a healthy probe has no error feedback path.

Important Files Changed

Filename Overview
packages/core/sdk/src/health-check.ts Introduces the core health-check vocabulary: HealthStatus, HealthCheckSpec, HealthCheckResult, HealthCheckCandidate, classifyHttpStatus, and compareHealthCheckCandidates. All shapes are pure Effect/Schema with no server imports, making them safe for React consumption. Ranking logic (non-destructive first, fewest args, GET first, alphabetical) is well-specified.
packages/plugins/openapi/src/sdk/backing.ts Adds checkHealthOpenApi, listHealthCheckCandidatesOpenApi, setHealthCheckOpenApi, and describeHealthCheckOpenApi. The setHealthCheckOpenApi correctly merges over the raw config object so provider supersets survive a health-check write, and uses healthCheck: undefined to drop the key on JSON serialization. The listHealthCheckCandidatesOpenApi re-parses the full spec to recover summaries (best-effort, with an error catch), which is a known trade-off for large specs.
packages/react/src/components/add-account-modal.tsx Adds inline key-check flow: pasted credential probed before saving, with an inline operation picker when no health check is configured. The handleValidate function silently swallows a doSetHealthCheck save failure — the user sees "Healthy" but the spec may not persist, breaking the stated guarantee. Otherwise the flow is correct: state lives entirely in the view, unmounting on close resets everything cleanly.
packages/react/src/components/health-check-editor.tsx Self-hiding editor sheet that re-seeds draft state from the persisted spec on each open. Uses modal={false} to avoid react-remove-scroll locking the portaled combobox popup. Shares HealthCheckConfigFields with the add-connection modal for identical form behavior in both surfaces.
e2e/scenarios/health-checks-ui.test.ts Six browser scenarios covering the operation picker, inline key-check, and combobox scroll. All pass {} instead of { needs: ["browser"] } (flagged in a previous review thread). Also duplicates serveIdentityApi and identitySpec from the API scenario file.
e2e/scenarios/health-checks.test.ts Three API-only scenarios: ranking, healthy-to-expired lifecycle, and unknown with no configured check. Uses a real node:http server as the upstream. Cleanup is correct (Effect.ensuring with Effect.ignore). serveIdentityApi/identitySpec are duplicated from the UI scenario file.
packages/core/api/src/connections/api.ts Adds checkHealth (POST on saved connection) and validate (POST without saving) endpoints. ValidateConnectionPayload includes a schema filter ensuring exactly one credential origin is provided.
packages/react/src/lib/health-display.ts New shared display constants for health status labels, indicator dot/ring colors, and badge variants. Clean single-responsibility module following the existing policy-display.ts convention.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant UI as React UI
    participant API as Connections API
    participant Plugin as OpenAPI Plugin
    participant Upstream as Upstream API

    Note over UI,Upstream: Add Connection - inline key check
    UI->>API: POST /connections/validate (credential + spec)
    API->>Plugin: connections.validate → checkHealth(spec override)
    Plugin->>Upstream: HTTP probe (e.g. GET /me with Bearer token)
    Upstream-->>Plugin: 200 / 401 / other
    Plugin-->>API: "HealthCheckResult {status, httpStatus}"
    API-->>UI: HealthCheckResult
    alt "status = healthy AND inlineSpec set"
        UI->>API: PUT /integrations/:slug/health-check (spec)
        API-->>UI: saved
    end

    Note over UI,Upstream: Check saved connection health
    UI->>API: POST /connections/:owner/:integration/:name/health
    API->>Plugin: connections.checkHealth
    Plugin->>Upstream: HTTP probe (from stored HealthCheckSpec)
    Upstream-->>Plugin: HTTP status
    Plugin-->>API: HealthCheckResult
    API-->>UI: "{status: healthy|expired|degraded|unknown}"
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant UI as React UI
    participant API as Connections API
    participant Plugin as OpenAPI Plugin
    participant Upstream as Upstream API

    Note over UI,Upstream: Add Connection - inline key check
    UI->>API: POST /connections/validate (credential + spec)
    API->>Plugin: connections.validate → checkHealth(spec override)
    Plugin->>Upstream: HTTP probe (e.g. GET /me with Bearer token)
    Upstream-->>Plugin: 200 / 401 / other
    Plugin-->>API: "HealthCheckResult {status, httpStatus}"
    API-->>UI: HealthCheckResult
    alt "status = healthy AND inlineSpec set"
        UI->>API: PUT /integrations/:slug/health-check (spec)
        API-->>UI: saved
    end

    Note over UI,Upstream: Check saved connection health
    UI->>API: POST /connections/:owner/:integration/:name/health
    API->>Plugin: connections.checkHealth
    Plugin->>Upstream: HTTP probe (from stored HealthCheckSpec)
    Upstream-->>Plugin: HTTP status
    Plugin-->>API: HealthCheckResult
    API-->>UI: "{status: healthy|expired|degraded|unknown}"
Loading

Reviews (5): Last reviewed commit: "feat: connection health checks (liveness..." | Re-trigger Greptile

Comment thread packages/core/sdk/src/health-check.ts
Comment on lines +1 to +20
// Cross-target (browser): the UI side of connection health checks, the feature
// that answers "has this credential expired?" (the Google 7-day dev-token case)
// and, optionally, "whose account is this?". These scenarios pin the redesign:
//
// 1. Edit sheet, WITH identity: the operation and identity-field pickers are
// comboboxes (the identity options are the operation's typed response
// fields). A live preview probes a pasted key and shows the actual response
// (path -> value rows) plus "Resolves to: <identity>". The saved check then
// drives "Check now" on a live connection: healthy, then expired once the
// upstream revokes the key.
// 2. Edit sheet, NO identity: picking "None - health check only" leaves a pure
// alive/expired probe. The preview still shows the response sample but no
// "Resolves to" line; the persisted spec carries no identity field.
// 3. Add screen: the same check is configurable while adding the integration,
// and it persists.
// 4. Connect modal: key-first. The credential field comes before the display
// name; a valid key auto-fills the name from the resolved identity (and for
// a no-identity integration leaves the name for the user).
//
// The upstream API is a real node:http server on 127.0.0.1 that gates `GET /me`

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Missing needs: ["browser"] capability declaration

All 6 scenarios in this file depend on the Browser service but are defined with {} options. Per e2e/AGENTS.md, every scenario that uses browser capabilities must declare needs: ["browser"] (e.g., test("...", { needs: ["browser"] }, ({ browser }) => ...)). Without the declaration the test runner may not provision the browser context, causing the tests to fail or be silently skipped in environments that gate capability provisioning.

Context Used: e2e/AGENTS.md (source)

@RhysSullivan RhysSullivan force-pushed the claude/health-checks branch from 522f93b to 59fd53f Compare June 23, 2026 22:25
@RhysSullivan RhysSullivan changed the title Connection health checks and identity Connection health checks (liveness) with OpenAPI backing Jun 23, 2026
@RhysSullivan RhysSullivan force-pushed the claude/health-checks branch 2 times, most recently from dc99124 to d6b3eac Compare June 25, 2026 20:45
A connection can declare ONE authenticated operation that answers "is this
credential still alive?". The probe runs the operation, maps the HTTP status to
a health state (2xx healthy, 401/403 expired, other degraded, unset unknown),
and reports it. Built for short-lived OAuth tokens (Google's 7-day dev-token
revocation): a credential that authenticated yesterday and now 401s reads as
expired.

Core owns the shared vocabulary (HealthStatus, HealthCheckSpec, HealthCheckResult,
HealthCheckCandidate, classifyHttpStatus, candidate ranking); plugins own which
operation runs, stored in their opaque integration config and picked the same way
auth methods are. Dispatch adds integrations.healthCheck.{get,candidates,set} and
connections.{checkHealth,validate}.

OpenAPI backing: list ranked candidates (non-destructive GET first), persist the
chosen spec (merging over the raw config so provider supersets keep their keys),
and run the probe against a resolved credential.

React surfaces:
- a per-connection status dot + "Check now",
- a self-hiding operation editor (a searchable freeform combobox over the whole
  spec),
- "Check the key works" in the Add Connection modal: probe the pasted key before
  saving; when no check is configured yet, pick a read-only operation inline and
  a healthy probe saves it as the integration's health check.

The operation picker is a base-ui combobox whose popup portals out of the
surrounding Radix modal, so this also carries the fixes that make it usable in a
modal: pointer-events on the popup, an outside-interaction guard on both the
dialog and the sheet (so clicking an option doesn't dismiss the modal), and a
non-modal editor sheet (so the popup list can scroll).

Covered by e2e: saved connection healthy -> expired; unconfigured -> unknown; the
operation picker filters a hundreds-long list on the edit sheet and add screen;
Add Connection checks the key against an inline-picked operation (by mouse) and
persists it; the editor-sheet combobox is clickable and scrollable.
@RhysSullivan RhysSullivan force-pushed the claude/health-checks branch from d6b3eac to d1aa3ea Compare June 26, 2026 02:26
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