Skip to content

CI: enforce typecheck and run the full test suite#196

Merged
willwashburn merged 2 commits into
mainfrom
audit/ci-quality-gates
Jun 10, 2026
Merged

CI: enforce typecheck and run the full test suite#196
willwashburn merged 2 commits into
mainfrom
audit/ci-quality-gates

Conversation

@willwashburn

Copy link
Copy Markdown
Member

What this does

The repo had TypeScript strict mode configured but no typecheck ran anywhere (electron-vite builds with esbuild; npm test strips types), and CI ran only a single vitest file (src/main/broker.test.ts) out of the full suite. This PR wires real quality gates.

New npm scripts

  • typecheck (typecheck:node + typecheck:web) — tsc --noEmit over tsconfig.node.json (main+preload+shared) and tsconfig.web.json (renderer).
  • test:all — the existing node:test suite plus the full npx vitest run.

What now gates PRs (checks job)

Gate Status
npm run typecheck non-blocking (continue-on-error) — ~95 pre-existing node-side errors listed below; renderer is clean
npm test (node:test) blocking (unchanged)
npx vitest run (full suite, 19 files / 263 tests) blocking — was previously just broker.test.ts
npm run build blocking (unchanged)

New playwright job

  • playwright.fidelity.config.tsnon-blocking (pre-existing failure, see below).
  • playwright.redraw.config.tsblocking (passes today).
  • Builds build:web once up front; installs chromium via playwright install --with-deps chromium; uploads traces on failure.

New nightly-stress.yml

  • cron: '0 7 * * *' (daily 07:00 UTC) + workflow_dispatch, running npm run test:stress on ubuntu-latest with the same setup.

Config fixes (needed to make the gates meaningful)

tsconfig.node.json: added target: ES2022 + lib: ["ES2022"]. It previously set no target, defaulting to ES3, which produced ~60 false-positive errors (--downlevelIteration, "regex flag only available when targeting es6+", Array.prototype.at) that do not reflect the actual Node 22 / Electron runtime. The renderer config already targets ES2022. This is typecheck-only — the build is unaffected because electron-vite transpiles with esbuild, not tsc (verified: npm run build passes).

vitest.config.mjs: testTimeout/hookTimeout → 20s. A few timing-sensitive tests (cloud-agent.test.ts git-overlay cases, slack writeback) exceeded the 5s default only when the suite runs as a full gate or back-to-back with node:test under load (e.g. 5008ms). They pass deterministically in isolation. Raising the timeout stabilizes the suite as a gate without skipping any test (preferred over it.skip, which would drop coverage). After the bump, npm run test:all passes green repeatedly (node:test 115/115, vitest 263/263).


Pre-existing failures and how each is handled

1. Typecheck — tsconfig.node.json has ~95 genuine pre-existing type errors → step is continue-on-error

tsconfig.web.json (renderer) is clean (0 errors). The node side has real errors (not config noise — captured after the ES2022 target fix). Flip the CI step to blocking once these are fixed. By file:

 22  src/main/integration-event-bridge.ts
 15  src/main/cloud-agent.ts
 12  src/main/integrations.test.ts
 11  src/main/proactive-agent.ts
 10  src/main/store.ts
  7  src/main/integration-mounts.test.ts
  6  src/main/broker.ts
  3  src/main/relayfile-mount-launcher.test.ts
  3  src/main/cloud-agent.test.ts
  2  src/main/integrations.ts
  2  src/main/broker.test.ts
  1  src/main/index.ts
  1  src/main/auth.test.ts
Full list (95 errors)
src/main/auth.test.ts(605,5): error TS2349: This expression is not callable.
src/main/broker.test.ts(227,12): error TS2790: The operand of a 'delete' operator must be optional.
src/main/broker.test.ts(815,73): error TS2493: Tuple type '[]' of length '0' has no element at index '0'.
src/main/broker.ts(650,31): error TS2339: Property 'reason' does not exist on type 'BrokerEvent'.
src/main/broker.ts(650,59): error TS2339: Property 'reason' does not exist on type 'BrokerEvent'.
src/main/broker.ts(651,34): error TS2339: Property 'lastError' does not exist on type 'BrokerEvent'.
src/main/broker.ts(651,65): error TS2339: Property 'lastError' does not exist on type 'BrokerEvent'.
src/main/broker.ts(842,53): error TS2322: Type '{}' is not assignable to type 'string'.
src/main/broker.ts(843,91): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'.
src/main/cloud-agent.test.ts(338,69): error TS2345: Argument of type '"rk_live_project"' is not assignable to parameter of type 'undefined'.
src/main/cloud-agent.test.ts(373,69): error TS2345: Argument of type '"rk_live_project"' is not assignable to parameter of type 'undefined'.
src/main/cloud-agent.test.ts(629,69): error TS2345: Argument of type '"rk_live_project"' is not assignable to parameter of type 'undefined'.
src/main/cloud-agent.ts(1441,19): error TS2339: Property 'cloudAgent' does not exist on type '{ id: string; name: string; relayWorkspaceId: string; rootPath: string; roots: { id: string; name: string; path: string; }[]; channels: string[]; channelPeople: Record<string, string[]>; integrations: { ...; }[]; }'.
src/main/cloud-agent.ts(1441,41): error TS2339: Property 'cloudAgent' does not exist on type '{ id: string; name: string; relayWorkspaceId: string; rootPath: string; roots: { id: string; name: string; path: string; }[]; channels: string[]; channelPeople: Record<string, string[]>; integrations: { ...; }[]; }'.
src/main/cloud-agent.ts(1443,55): error TS2339: Property 'cloudAgent' does not exist on type '{ id: string; name: string; relayWorkspaceId: string; rootPath: string; roots: { id: string; name: string; path: string; }[]; channels: string[]; channelPeople: Record<string, string[]>; integrations: { ...; }[]; }'.
src/main/cloud-agent.ts(1498,19): error TS2339: Property 'cloudAgent' does not exist on type '{ id: string; name: string; relayWorkspaceId: string; rootPath: string; roots: { id: string; name: string; path: string; }[]; channels: string[]; channelPeople: Record<string, string[]>; integrations: { ...; }[]; }'.
src/main/cloud-agent.ts(1502,29): error TS2339: Property 'cloudAgent' does not exist on type '{ id: string; name: string; relayWorkspaceId: string; rootPath: string; roots: { id: string; name: string; path: string; }[]; channels: string[]; channelPeople: Record<string, string[]>; integrations: { ...; }[]; }'.
src/main/cloud-agent.ts(1503,26): error TS2339: Property 'cloudAgent' does not exist on type '{ id: string; name: string; relayWorkspaceId: string; rootPath: string; roots: { id: string; name: string; path: string; }[]; channels: string[]; channelPeople: Record<string, string[]>; integrations: { ...; }[]; }'.
src/main/cloud-agent.ts(1504,35): error TS2339: Property 'cloudAgent' does not exist on type '{ id: string; name: string; relayWorkspaceId: string; rootPath: string; roots: { id: string; name: string; path: string; }[]; channels: string[]; channelPeople: Record<string, string[]>; integrations: { ...; }[]; }'.
src/main/cloud-agent.ts(1505,27): error TS2339: Property 'cloudAgent' does not exist on type '{ id: string; name: string; relayWorkspaceId: string; rootPath: string; roots: { id: string; name: string; path: string; }[]; channels: string[]; channelPeople: Record<string, string[]>; integrations: { ...; }[]; }'.
src/main/cloud-agent.ts(1515,15): error TS2339: Property 'cloudAgent' does not exist on type '{ id: string; name: string; relayWorkspaceId: string; rootPath: string; roots: { id: string; name: string; path: string; }[]; channels: string[]; channelPeople: Record<string, string[]>; integrations: { ...; }[]; }'.
src/main/cloud-agent.ts(1517,22): error TS2339: Property 'cloudAgent' does not exist on type '{ id: string; name: string; relayWorkspaceId: string; rootPath: string; roots: { id: string; name: string; path: string; }[]; channels: string[]; channelPeople: Record<string, string[]>; integrations: { ...; }[]; }'.
src/main/cloud-agent.ts(533,72): error TS2339: Property 'cloudAgent' does not exist on type '{ id: string; name: string; relayWorkspaceId: string; rootPath: string; roots: { id: string; name: string; path: string; }[]; channels: string[]; channelPeople: Record<string, string[]>; integrations: { ...; }[]; }'.
src/main/cloud-agent.ts(705,65): error TS2339: Property 'cloudAgent' does not exist on type '{ id: string; name: string; relayWorkspaceId: string; rootPath: string; roots: { id: string; name: string; path: string; }[]; channels: string[]; channelPeople: Record<string, string[]>; integrations: { ...; }[]; }'.
src/main/cloud-agent.ts(783,19): error TS2339: Property 'cloudAgent' does not exist on type '{ id: string; name: string; relayWorkspaceId: string; rootPath: string; roots: { id: string; name: string; path: string; }[]; channels: string[]; channelPeople: Record<string, string[]>; integrations: { ...; }[]; }'.
src/main/cloud-agent.ts(786,45): error TS2339: Property 'cloudAgent' does not exist on type '{ id: string; name: string; relayWorkspaceId: string; rootPath: string; roots: { id: string; name: string; path: string; }[]; channels: string[]; channelPeople: Record<string, string[]>; integrations: { ...; }[]; }'.
src/main/cloud-agent.ts(905,33): error TS2339: Property 'cloudAgent' does not exist on type '{ id: string; name: string; relayWorkspaceId: string; rootPath: string; roots: { id: string; name: string; path: string; }[]; channels: string[]; channelPeople: Record<string, string[]>; integrations: { ...; }[]; }'.
src/main/index.ts(317,7): error TS18048: 'app.dock' is possibly 'undefined'.
src/main/integration-event-bridge.ts(1403,11): error TS2352: Conversion of type 'ChangeEvent' to type 'Record<string, unknown>' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
src/main/integration-event-bridge.ts(1403,53): error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'.
src/main/integration-event-bridge.ts(1623,10): error TS2352: Conversion of type '{ id: string; workspace: string; type: "relayfile.changed.summary"; occurredAt: string; resource: { path: string; provider: string; kind: string; id: string; }; summary: { title: string; compactedIntegrationEvents: number; latestEventId: string; latestEventPath: string; }; digest: string; expand: () => Promise<...>; }' to type 'ChangeEvent' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
src/main/integration-event-bridge.ts(1742,42): error TS2339: Property 'provider' does not exist on type '{}'.
src/main/integration-event-bridge.ts(1743,46): error TS2339: Property 'path' does not exist on type '{}'.
src/main/integration-event-bridge.ts(1744,44): error TS2339: Property 'id' does not exist on type '{}'.
src/main/integration-event-bridge.ts(2040,7): error TS2367: This comparison appears to be unintentional because the types '"relayfile.changed"' and '"writeback.succeeded"' have no overlap.
src/main/integration-event-bridge.ts(2084,47): error TS2339: Property 'provider' does not exist on type '{}'.
src/main/integration-event-bridge.ts(2085,52): error TS2339: Property 'path' does not exist on type '{}'.
src/main/integration-event-bridge.ts(2124,47): error TS2339: Property 'provider' does not exist on type '{}'.
src/main/integration-event-bridge.ts(2125,52): error TS2339: Property 'path' does not exist on type '{}'.
src/main/integration-event-bridge.ts(2128,51): error TS2339: Property 'kind' does not exist on type '{}'.
src/main/integration-event-bridge.ts(2129,49): error TS2339: Property 'id' does not exist on type '{}'.
src/main/integration-event-bridge.ts(2682,42): error TS2367: This comparison appears to be unintentional because the types '"relayfile.changed"' and '"relayfile.changed.summary"' have no overlap.
src/main/integration-event-bridge.ts(2682,9): error TS2367: This comparison appears to be unintentional because the types '"relayfile.changed"' and '"file.deleted"' have no overlap.
src/main/integration-event-bridge.ts(2719,31): error TS2339: Property 'path' does not exist on type '{}'.
src/main/integration-event-bridge.ts(2719,66): error TS2339: Property 'path' does not exist on type '{}'.
src/main/integration-event-bridge.ts(2720,24): error TS2339: Property 'data' does not exist on type '{}'.
src/main/integration-event-bridge.ts(3393,123): error TS5097: An import path can only end with a '.ts' extension when 'allowImportingTsExtensions' is enabled.
src/main/integration-event-bridge.ts(3416,22): error TS18047: 'auth' is possibly 'null'.
src/main/integration-event-bridge.ts(3417,9): error TS2322: Type '() => Promise<string | undefined>' is not assignable to type 'AccessTokenProvider | undefined'.
src/main/integration-event-bridge.ts(689,20): error TS2322: Type '"summary"' is not assignable to type 'L'.
src/main/integration-mounts.test.ts(508,38): error TS2345: Argument of type '(path: string) => Promise<string>' is not assignable to parameter of type '() => Promise<string>'.
src/main/integration-mounts.test.ts(548,38): error TS2345: Argument of type '(path: string) => Promise<string>' is not assignable to parameter of type '() => Promise<string>'.
src/main/integration-mounts.test.ts(588,38): error TS2345: Argument of type '(path: string) => Promise<string>' is not assignable to parameter of type '() => Promise<string>'.
src/main/integration-mounts.test.ts(629,38): error TS2345: Argument of type '(path: string) => Promise<string>' is not assignable to parameter of type '() => Promise<string>'.
src/main/integration-mounts.test.ts(670,38): error TS2345: Argument of type '(path: string) => Promise<string>' is not assignable to parameter of type '() => Promise<string>'.
src/main/integration-mounts.test.ts(701,38): error TS2345: Argument of type '(path: string) => Promise<string>' is not assignable to parameter of type '() => Promise<string>'.
src/main/integration-mounts.test.ts(732,38): error TS2345: Argument of type '(path: string) => Promise<string>' is not assignable to parameter of type '() => Promise<string>'.
src/main/integrations.test.ts(711,54): error TS2345: Argument of type '() => Promise<{ name: string; projectId: string; }[]>' is not assignable to parameter of type '() => Promise<never[]>'.
src/main/integrations.test.ts(743,54): error TS2322: Type '{ name: string; projectId: string; }' is not assignable to type 'never'.
src/main/integrations.test.ts(769,54): error TS2322: Type '{ name: string; projectId: string; }' is not assignable to type 'never'.
src/main/integrations.test.ts(773,13): error TS2493: Tuple type '[]' of length '0' has no element at index '1'.
src/main/integrations.test.ts(773,25): error TS2352: Conversion of type 'undefined' to type '{ data?: { kind?: string | undefined; } | undefined; }' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
src/main/integrations.test.ts(797,54): error TS2322: Type '{ name: string; projectId: string; }' is not assignable to type 'never'.
src/main/integrations.test.ts(803,13): error TS2493: Tuple type '[]' of length '0' has no element at index '1'.
src/main/integrations.test.ts(803,25): error TS2352: Conversion of type 'undefined' to type '{ data?: { kind?: string | undefined; } | undefined; }' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
src/main/integrations.test.ts(827,54): error TS2322: Type '{ name: string; projectId: string; }' is not assignable to type 'never'.
src/main/integrations.test.ts(844,54): error TS2322: Type '{ name: string; projectId: string; }' is not assignable to type 'never'.
src/main/integrations.test.ts(878,69): error TS2345: Argument of type '"workspace-id"' is not assignable to parameter of type 'null'.
src/main/integrations.test.ts(880,7): error TS2322: Type 'string' is not assignable to type 'never'.
src/main/integrations.ts(815,33): error TS2339: Property 'pendingWriteback' does not exist on type '{ type: "auth-stall"; remotePath: string; status: string | null; pendingWriteback: number; message: string; } | { type: "reconcile-stalled"; remotePath: string; status: string | null; lastSuccessfulReconcileAt: string | null; }'.
src/main/integrations.ts(816,24): error TS2339: Property 'message' does not exist on type '{ type: "auth-stall"; remotePath: string; status: string | null; pendingWriteback: number; message: string; } | { type: "reconcile-stalled"; remotePath: string; status: string | null; lastSuccessfulReconcileAt: string | null; }'.
src/main/proactive-agent.ts(186,29): error TS2339: Property 'proactiveAgents' does not exist on type '{ id: string; name: string; relayWorkspaceId: string; rootPath: string; roots: { id: string; name: string; path: string; }[]; channels: string[]; channelPeople: Record<string, string[]>; integrations: { ...; }[]; }'.
src/main/proactive-agent.ts(203,21): error TS2339: Property 'proactiveAgents' does not exist on type '{ id: string; name: string; relayWorkspaceId: string; rootPath: string; roots: { id: string; name: string; path: string; }[]; channels: string[]; channelPeople: Record<string, string[]>; integrations: { ...; }[]; }'.
src/main/proactive-agent.ts(317,19): error TS2339: Property 'proactiveAgents' does not exist on type '{ id: string; name: string; relayWorkspaceId: string; rootPath: string; roots: { id: string; name: string; path: string; }[]; channels: string[]; channelPeople: Record<string, string[]>; integrations: { ...; }[]; }'.
src/main/proactive-agent.ts(317,52): error TS2339: Property 'proactiveAgents' does not exist on type '{ id: string; name: string; relayWorkspaceId: string; rootPath: string; roots: { id: string; name: string; path: string; }[]; channels: string[]; channelPeople: Record<string, string[]>; integrations: { ...; }[]; }'.
src/main/proactive-agent.ts(317,83): error TS7006: Parameter 'entry' implicitly has an 'any' type.
src/main/proactive-agent.ts(505,30): error TS2339: Property 'proactiveAgents' does not exist on type '{ id: string; name: string; relayWorkspaceId: string; rootPath: string; roots: { id: string; name: string; path: string; }[]; channels: string[]; channelPeople: Record<string, string[]>; integrations: { ...; }[]; }'.
src/main/proactive-agent.ts(506,39): error TS7006: Parameter 'candidate' implicitly has an 'any' type.
src/main/proactive-agent.ts(512,13): error TS2339: Property 'proactiveAgents' does not exist on type '{ id: string; name: string; relayWorkspaceId: string; rootPath: string; roots: { id: string; name: string; path: string; }[]; channels: string[]; channelPeople: Record<string, string[]>; integrations: { ...; }[]; }'.
src/main/proactive-agent.ts(520,13): error TS2339: Property 'proactiveAgents' does not exist on type '{ id: string; name: string; relayWorkspaceId: string; rootPath: string; roots: { id: string; name: string; path: string; }[]; channels: string[]; channelPeople: Record<string, string[]>; integrations: { ...; }[]; }'.
src/main/proactive-agent.ts(526,30): error TS2339: Property 'proactiveAgents' does not exist on type '{ id: string; name: string; relayWorkspaceId: string; rootPath: string; roots: { id: string; name: string; path: string; }[]; channels: string[]; channelPeople: Record<string, string[]>; integrations: { ...; }[]; }'.
src/main/proactive-agent.ts(526,59): error TS7006: Parameter 'candidate' implicitly has an 'any' type.
src/main/relayfile-mount-launcher.test.ts(53,26): error TS2345: Argument of type '{ env: { RELAYFILE_TOKEN: string; RELAYFILE_LOCAL_DIR: string; }; }' is not assignable to parameter of type 'MountLauncherStart'.
src/main/relayfile-mount-launcher.test.ts(78,26): error TS2345: Argument of type '{ env: { RELAYFILE_LOCAL_DIR: string; }; }' is not assignable to parameter of type 'MountLauncherStart'.
src/main/relayfile-mount-launcher.test.ts(83,26): error TS2345: Argument of type '{ env: { RELAYFILE_TOKEN: string; }; }' is not assignable to parameter of type 'MountLauncherStart'.
src/main/store.ts(105,15): error TS2339: Property 'relayWorkspace' does not exist on type '{ projects: { id: string; name: string; relayWorkspaceId: string; rootPath: string; roots: { id: string; name: string; path: string; }[]; channels: string[]; channelPeople: Record<string, string[]>; integrations: { ...; }[]; }[]; activeProjectId: string | null; }'.
src/main/store.ts(110,22): error TS2339: Property 'relayWorkspace' does not exist on type '{ projects: { id: string; name: string; relayWorkspaceId: string; rootPath: string; roots: { id: string; name: string; path: string; }[]; channels: string[]; channelPeople: Record<string, string[]>; integrations: { ...; }[]; }[]; activeProjectId: string | null; }'.
src/main/store.ts(115,26): error TS2304: Cannot find name 'normalizeRelayWorkspace'.
src/main/store.ts(117,10): error TS2339: Property 'relayWorkspace' does not exist on type '{ projects: { id: string; name: string; relayWorkspaceId: string; rootPath: string; roots: { id: string; name: string; path: string; }[]; channels: string[]; channelPeople: Record<string, string[]>; integrations: { ...; }[]; }[]; activeProjectId: string | null; }'.
src/main/store.ts(119,17): error TS2339: Property 'relayWorkspace' does not exist on type '{ projects: { id: string; name: string; relayWorkspaceId: string; rootPath: string; roots: { id: string; name: string; path: string; }[]; channels: string[]; channelPeople: Record<string, string[]>; integrations: { ...; }[]; }[]; activeProjectId: string | null; }'.
src/main/store.ts(126,9): error TS2741: Property 'relayWorkspaceId' is missing in type '{ id: `${string}-${string}-${string}-${string}-${string}`; name: string; rootPath: string; roots: { id: `${string}-${string}-${string}-${string}-${string}`; name: string; path: string; }[]; channels: string[]; channelPeople: {}; integrations: never[]; }' but required in type '{ id: string; name: string; relayWorkspaceId: string; rootPath: string; roots: { id: string; name: string; path: string; }[]; channels: string[]; channelPeople: Record<string, string[]>; integrations: { ...; }[]; }'.
src/main/store.ts(88,38): error TS2339: Property 'relayWorkspace' does not exist on type '{ projects: { id: string; name: string; relayWorkspaceId: string; rootPath: string; roots: { id: string; name: string; path: string; }[]; channels: string[]; channelPeople: Record<string, string[]>; integrations: { ...; }[]; }[]; activeProjectId: string | null; }'.
src/main/store.ts(94,26): error TS2304: Cannot find name 'normalizeRelayWorkspace'.
src/main/store.ts(96,10): error TS2339: Property 'relayWorkspace' does not exist on type '{ projects: { id: string; name: string; relayWorkspaceId: string; rootPath: string; roots: { id: string; name: string; path: string; }[]; channels: string[]; channelPeople: Record<string, string[]>; integrations: { ...; }[]; }[]; activeProjectId: string | null; }'.
src/main/store.ts(98,17): error TS2339: Property 'relayWorkspace' does not exist on type '{ projects: { id: string; name: string; relayWorkspaceId: string; rootPath: string; roots: { id: string; name: string; path: string; }[]; channels: string[]; channelPeople: Record<string, string[]>; integrations: { ...; }[]; }[]; activeProjectId: string | null; }'.

2. Vitest full suite — passes (263/263) after the timeout bump

No it.skip needed. The only failures observed were the timing flakes described above, resolved by testTimeout.

3. Playwright fidelity suite — pre-existing failure → step is continue-on-error

All 6 fidelity specs time out waiting for [data-testid="terminal-instance"]. Root cause: the web-mock build now lands on the Attention Inbox tab by default — src/renderer/src/stores/ui-store.ts sets initialTab to 'issues' when VITE_PEAR_MOCK_IPC === 'true'. bootWithAgents spawns agents but never opens the Agents/terminal view, so terminal-instance never mounts. Fix: add a step to open the Agents view before spawning (the redraw spec already navigates via the general channel and passes). Remove the step's continue-on-error once the spec is updated.

4. Playwright redraw suite — passes, gates as a hard requirement.


Verified locally

  • npm run typecheck → renderer clean; node surfaces the 95 errors above (documented, non-blocking).
  • npm run test:all → node:test 115/115, vitest 263/263 (green, repeated).
  • npm run test:redraw → 1 passed.
  • npm run test:fidelity → 6 failed (root cause documented; non-blocking).
  • npm run build → passes (tsconfig target change does not affect the esbuild build).
  • Both workflow YAMLs parse cleanly.

Note: the 95-error count is a snapshot of origin/main at branch time; parallel branches (e.g. integrations / CSP / PTY-typing work) may resolve some before this merges.

🤖 Generated with Claude Code

Add real quality gates discovered missing in the repo audit: the repo had
TypeScript strict mode but no typecheck ran anywhere, and CI ran only a single
vitest file (broker.test.ts) out of the full suite.

Scripts (package.json):
- typecheck / typecheck:node / typecheck:web — tsc --noEmit over tsconfig.node.json
  (main+preload+shared) and tsconfig.web.json (renderer).
- test:all — node:test suite + full `npx vitest run`.

Config fixes:
- tsconfig.node.json: add target/lib ES2022 (it previously defaulted to ES3,
  producing ~60 false-positive downlevelIteration / regex-flag / .at() errors
  that don't reflect the Node 22 / Electron runtime). Build is unaffected
  (electron-vite transpiles with esbuild, not tsc).
- vitest.config.mjs: testTimeout/hookTimeout 20s. A few timing-sensitive tests
  (cloud-agent git-overlay, slack writeback) exceeded the 5s default only under
  full-suite / back-to-back load; this stabilizes the suite as a gate without
  skipping any test.

CI (.github/workflows/ci.yml):
- checks: add `npm run typecheck` (continue-on-error — see PR body for the ~95
  pre-existing node-side errors; renderer is clean) and replace the single-file
  vitest step with full `npx vitest run` (19 files / 263 tests).
- new playwright job: fidelity (continue-on-error — pre-existing failure, web-mock
  now lands on the Attention Inbox tab so terminal-instance never mounts) + redraw
  (passing, hard gate).

Nightly (.github/workflows/nightly-stress.yml):
- cron daily + workflow_dispatch running `npm run test:stress` on ubuntu-latest.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@gemini-code-assist

Copy link
Copy Markdown

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@codeant-ai

codeant-ai Bot commented Jun 10, 2026

Copy link
Copy Markdown

Your free trial PR review limit of 300 PRs has been reached. Please upgrade your plan to continue using CodeAnt AI.

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@willwashburn, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 27 minutes and 46 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6a56976e-fad6-4560-9c99-65688c49d420

📥 Commits

Reviewing files that changed from the base of the PR and between 599b033 and 649a95e.

📒 Files selected for processing (5)
  • .github/workflows/ci.yml
  • .github/workflows/nightly-stress.yml
  • package.json
  • tsconfig.node.json
  • vitest.config.mjs
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch audit/ci-quality-gates

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1cd8aa9d69

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread package.json Outdated
"release:mac": "npm run icons:macos && npm run build && electron-builder --mac --publish always",
"typecheck:node": "tsc --noEmit -p tsconfig.node.json",
"typecheck:web": "tsc --noEmit -p tsconfig.web.json",
"typecheck": "npm run typecheck:node && npm run typecheck:web",

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 Badge Run both typecheck targets before returning

In the CI workflow I checked, .github/workflows/ci.yml runs npm run typecheck with continue-on-error while the node config is documented as currently failing; because this script uses &&, typecheck:web is short-circuited whenever typecheck:node returns non-zero. That means renderer type regressions are not exercised by the new gate until all node errors are fixed, despite the workflow comment saying both configs are checked. Split the CI steps or make the aggregate script collect both exits before failing.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good catch — fixed in 649a95e, and went one further: instead of collecting both exits under one non-blocking step, CI now runs them as separate steps with typecheck:web blocking (it's clean today, so renderer regressions can't land) and typecheck:node non-blocking until the 95 listed errors are fixed. The local typecheck script was reordered web-first so the short-circuit can't hide renderer errors either. Verified typecheck:web passes.

Codex review on #196: 'npm run typecheck' short-circuits on the failing
node config, so the clean renderer config was never exercised under
continue-on-error. typecheck:web now gates PRs as a hard requirement;
typecheck:node stays non-blocking until its 95 pre-existing errors are
fixed. Local 'typecheck' script reordered web-first for the same reason.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@codeant-ai

codeant-ai Bot commented Jun 10, 2026

Copy link
Copy Markdown

Your free trial PR review limit of 300 PRs has been reached. Please upgrade your plan to continue using CodeAnt AI.

@willwashburn willwashburn merged commit 831a56b into main Jun 10, 2026
5 checks passed
@willwashburn willwashburn deleted the audit/ci-quality-gates branch June 10, 2026 11:03
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