Skip to content

chore: sync with upstream 2026-07-28 - #86

Merged
NicolasWalter merged 8 commits into
mainfrom
sync/upstream-2026-07-28
Jul 28, 2026
Merged

chore: sync with upstream 2026-07-28#86
NicolasWalter merged 8 commits into
mainfrom
sync/upstream-2026-07-28

Conversation

@NicolasWalter

@NicolasWalter NicolasWalter commented Jul 28, 2026

Copy link
Copy Markdown

Automated upstream sync

Clean merge from ColeMurray/background-agents@main.

This PR was opened automatically by .github/workflows/sync-upstream.yml. Review the commit list and merge when CI is green.

Summary by CodeRabbit

  • New Features

    • Browser sign-in now supports control-plane sessions with GitHub and optional Google authentication.
    • Bot-authored pull requests can be reviewed when explicitly allowed, using comment-only reviews.
    • Media viewers now include a prominent close button and improved sizing and scrolling.
  • Bug Fixes

    • Invalid repository, prompt, callback, and integration responses are handled more safely.
    • Unsubscribed WebSocket actions are rejected consistently.
    • Users may need to sign in again after upgrading.
  • Accessibility

    • Improved label associations across integration and sandbox settings.

open-inspect Bot and others added 8 commits July 26, 2026 23:26
This is an automated nightly unsafe-cast remediation sweep. It replaces
a high-risk request-body assertion at the authenticated session prompt
boundary with Zod-backed parsing, following the TypeScript Coding
Standards guidance for unsafe casts and parse-don't-assert validation.
The shared schemas follow the Zod boundary-validation pattern
established in PR ColeMurray#807.

| Finding | Risk | Cast Removed | Fix |
| --- | --- | --- | --- |
| `packages/control-plane/src/routes/session-prompt.ts:43` | HIGH |
`(await request.json()) as { content: string; source?: string; model?:
string; reasoningEffort?: string; attachments?: unknown;
callbackContext?: CallbackContext }` | Added shared
`sendPromptRequestSchema` and `callbackContextSchema`, then used
`safeParse` at the request boundary before forwarding prompt data. |

Verification:

| Command | Result |
| --- | --- |
| `npm run build -w @open-inspect/shared` | Passed |
| `npm run build -w @open-inspect/control-plane` | Passed |
| `npm run typecheck` | Passed |
| `npm run lint` | Passed in a clean repo view after temporarily moving
untracked local `.opencode` helper files that are not part of this
branch or repository checkout. |
| `npm run format` | Passed |
| `npm test -w @open-inspect/shared -- --run
src/types/boundary-schemas.test.ts` | Passed |
| `npm test -w @open-inspect/control-plane -- --run
src/router.session-prompt.test.ts` | Passed |
| `npm test -w @open-inspect/shared` | Passed |
| `npm test -w @open-inspect/control-plane` | Passed |

---
*Created with
[Open-Inspect](https://open-inspect-prod.vercel.app/session/a3ffb0d8f57202df1cc68ec9338de239)*

Co-authored-by: waclaude <colemurray.cs+ghwaclaude@gmail.com>
Co-authored-by: Cole Murray <colemurray.cs@gmail.com>
…1122)

## Summary

Fixes **10 root-cause task units** identified by the nightly React
Doctor scan while preserving existing behavior. The changes improve
screen-reader form associations, avoid redundant render and parsing
work, and simplify one frequently rebuilt helper.

## Tasks Fixed

1. **`react-doctor/async-parallel`** -
`packages/web/src/app/api/image-builds/route.ts`: three independent
response bodies were parsed sequentially. Parsing them together reduces
avoidable endpoint latency. **Human severity: medium.**
2. **`react-doctor/label-has-associated-control`** -
`packages/web/src/components/settings/integrations/github-integration-settings.tsx`
(code-review instructions): the visible label was not programmatically
tied to its textarea, making the field harder to identify with a screen
reader. Added matching `htmlFor`/`id`. **Human severity: medium.**
3. **`react-doctor/label-has-associated-control`** -
`packages/web/src/components/settings/integrations/github-integration-settings.tsx`
(comment-action instructions): the visible label was not
programmatically tied to its textarea. Added matching `htmlFor`/`id`.
**Human severity: medium.**
4. **`react-doctor/label-has-associated-control`** -
`packages/web/src/components/settings/integrations/linear-integration-settings.tsx`:
the issue-session instructions label was not tied to its textarea. Added
matching `htmlFor`/`id`. **Human severity: medium.**
5. **`react-doctor/prefer-module-scope-pure-function`** -
`packages/web/src/components/settings/sandbox-settings.tsx`:
`normalizePorts` was recreated on every editor render despite capturing
no render state. Hoisted it to module scope. **Human severity: low.**
6. **`react-doctor/js-combine-iterations`** -
`packages/web/src/components/settings/sandbox-settings.tsx`: port
normalization made repeated filter/map passes and intermediate arrays.
Replaced them with one behavior-preserving pass. **Human severity:
low.**
7. **`react-doctor/label-has-associated-control`** -
`packages/web/src/components/settings/sandbox-settings.tsx`: the visible
Web Terminal label was not associated with its switch. Added matching
`htmlFor`/`id`. **Human severity: medium.**
8. **`react-doctor/control-has-associated-label`** -
`packages/web/src/components/settings/sandbox-settings.tsx`: the Web
Terminal switch lacked an accessible name. The same visible
label/control association now supplies it. **Human severity: medium.**
9. **`react-doctor/jsx-no-constructed-context-values`** -
`packages/web/src/components/sidebar-layout.tsx`: the app-shell actions
context received a fresh object each render, needlessly redrawing
consumers. Memoized the value over its actual dependencies. **Human
severity: low.**
10. **`react-doctor/jsx-no-constructed-context-values`** -
`packages/web/src/components/ui/toggle-group.tsx`: the toggle-group
context received a fresh object each render. Memoized it over `variant`
and `size`. **Human severity: low.**

## Task Counting

A diagnostic with a non-null `fixGroupId` counts with every diagnostic
in that group as one task unit; an ungrouped diagnostic counts
individually. All 10 selected diagnostics were ungrouped. No
`fixGroupId` was split or partially fixed. The Web Terminal label and
control diagnostics are therefore counted as two task units even though
one association resolves both.

## React Doctor Results

- Before: **131 total** (1 error, 130 warnings)
- After: **121 total** (1 error, 120 warnings)
- Raw diagnostics cleared: **10**
- New stable diagnostic signatures: **0**
- Relevant rule counts:
  - `async-parallel`: 1 -> 0
  - `label-has-associated-control`: 21 -> 17
  - `control-has-associated-label`: 2 -> 1
  - `prefer-module-scope-pure-function`: 1 -> 0
  - `js-combine-iterations`: 8 -> 7
  - `jsx-no-constructed-context-values`: 2 -> 0

## Validation

- `npm run build -w @open-inspect/shared` - passed
- `npm run typecheck -w @open-inspect/web` - passed
- `npm exec prettier -- --check packages/web` - passed
- `npm run lint -w @open-inspect/web` - passed
- `npm test -w @open-inspect/web` - passed, 109 files and 933 tests
- `env -u NODE_ENV npm run build -w @open-inspect/web` - passed
- Full React Doctor after-scan - passed, selected tasks absent and no
new stable finding signatures
- `npx -y react-doctor@latest . --verbose --scope changed --base
origin/main --yes --blocking none` - completed; only three pre-existing
`prefer-useReducer` warnings in touched files remain
- `git diff --check` - passed

The initial baseline production build inherited a non-standard
`NODE_ENV` and failed while prerendering `/automations/new`; rerunning
with `NODE_ENV` unset passed. No pre-existing validation failures remain
under the project command's expected environment.

## Deferred

The remaining 121 diagnostics are explicitly left for later batches or
human judgment. This includes migration-scale component splitting and
state consolidation, auth-related Zod migration work, local-storage key
migration, iframe sandbox policy, locale/time-zone UX decisions, image
optimization requiring source/runtime validation, and effect/state
diagnostics whose safe fixes require broader ownership decisions.

Canonical validation also identified these current false positives,
which were not suppressed or edited:

- `effect-needs-cleanup` in `use-session-transport.ts`: teardown already
closes the socket and clears timers.
- `no-loading-flag-reset-outside-finally` in `use-sidebar-sessions.ts`:
the loading flag is already reset inside `finally`.
- `no-json-parse-stringify-clone` in `session-target.test.ts`: JSON
serialization is intentional to verify that `undefined` is omitted;
`structuredClone` would change the test's semantics.

## Visual Verification

No browser artifact was required because these changes do not alter
rendered layout, styling, or content. Form IDs/associations and
context/value allocation are non-visual.

Co-authored-by: waclaude <colemurray.cs+ghwaclaude@gmail.com>
## Summary
- require a subscribed client identity before any non-transport
WebSocket message can run
- disable Better Auth direct Google ID-token sign-in while preserving
authorization-code + PKCE
- add regression coverage for unauthenticated stop/typing and hostile
browser origins

## Security impact
- unauthenticated sockets can no longer stop active work or trigger
sandbox warming
- callers cannot exchange a directly supplied Google ID token for an
Open Inspect session
- existing signed web-channel and Better Auth origin checks remain
unchanged

## Validation
- `npm run test -w @open-inspect/control-plane` (2,111 tests)
- `npm run test:integration -w @open-inspect/control-plane` (654 tests
before the final characterization-only additions)
- focused auth/WebSocket integration suite (39 tests)
- `npm run typecheck -w @open-inspect/control-plane`
- `npm run lint -w @open-inspect/control-plane`
- `npm run build -w @open-inspect/control-plane`

## TDD evidence
- the pre-subscription stop test failed by mutating the processing
message and timing out before the guard was added
- the Google provider configuration test failed with
`disableIdTokenSignIn` unset before the option was added

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Security**
* Google sign-in now uses the approved flow: when Google is configured,
ID-token sign-in is disabled.
* Social sign-in requests from untrusted browser origins (with invalid
session cookies) are rejected and do not set cookies.
* **Bug Fixes**
* WebSocket clients must subscribe before sending actions like typing or
stopping execution; pre-subscription typing closes with code `4002`, and
stop does not mark the in-flight message as failed.
* **Tests**
* Added/extended integration coverage for these authentication and
WebSocket security boundaries.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary

- remove the unconditional skip for pull requests authored by the
configured GitHub App bot
- route bot-authored pull requests through the existing
`allowedTriggerUsers` gate
- make approved self-reviews comment-only because GitHub does not allow
PR authors to approve their own pull requests
- document the updated behavior and cover both allowlisted and rejected
bot senders

## Why

Automation sessions create pull requests with the GitHub App
installation identity. The GitHub bot previously skipped those pull
requests before evaluating the configured trigger-user allowlist, so
adding `open-inspect[bot]` as an allowed trigger user had no effect.

## Impact

Administrators can now opt into reviews of automation-created pull
requests by adding the App bot login, including the `[bot]` suffix, to
**Allowed Trigger Users**. Bot-authored pull requests remain rejected
when the bot is not on that list.

Self-reviews submit `COMMENT` reviews so findings are published without
attempting an invalid self-approval.

## Validation

- `npm test -w @open-inspect/github-bot` — 130 tests passed
- `npm run typecheck -w @open-inspect/github-bot`
- `npm run lint -w @open-inspect/github-bot`
- `npm run build -w @open-inspect/github-bot`
- `git diff --check`


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added configurable support for reviewing bot-created pull requests
when the bot is included in the allowed trigger list.
* Bot-authored pull requests now use comment-only reviews, complying
with GitHub’s restriction against self-approval.

* **Documentation**
* Updated guidance for configuring allowed trigger users and handling
bot-created pull requests.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary

- add a July 26 changelog entry for the Better Auth
browser-authentication cutover
- document control-plane-owned sessions, GitHub and optional Google
sign-in, and the signed web proxy
- call out the required sign-in after legacy browser tokens are retired

## Why

The Better Auth runtime and full browser-authentication cutover merged
in ColeMurray#1125 and ColeMurray#1126, but the user-facing changelog did not yet describe
the change or its upgrade impact.

## Validation

- `prettier --check CHANGELOG.md`
- `git diff --check`
- confirmed the change adds no Markdown links requiring target
validation

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Improved browser authentication with more secure, control-plane
managed sessions.
  * Added signed authentication routing for browser sign-in requests.

* **Breaking Changes**
  * Legacy browser tokens are no longer supported.
  * Existing users must sign in again after upgrading.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com>
## Summary
- constrain the media lightbox to the dynamic mobile viewport
- keep tall screenshots and videos scrollable within the available modal
space
- add an always-visible accessible close control
- cover the close interaction with a regression test

## Root cause
The media region could consume `80vh` before accounting for the title,
source URL, gaps, and padding. On mobile Safari, browser chrome further
reduces the visible dynamic viewport, allowing the centered fixed dialog
to extend beyond the screen.

## Verification
- `npm run lint -w @open-inspect/web`
- `npm run typecheck -w @open-inspect/web`
- `npm test -w @open-inspect/web --
src/components/screenshot-media.test.tsx`
- full web test run: 796/797 passed; the single timeout passed when
rerun independently
- visual verification at 390x700
  - before artifact: `2fd60db03d6015e885b921a8013e6014`
  - after artifact: `a47f9ac4737c9857397b5484a6499c55`

---
*Created with
[Open-Inspect](https://open-inspect-prod.vercel.app/session/de0a4097c37e1465acbb74c39939127a)*

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
  * Added a clearly visible close button to the media viewer.
* Improved media viewer layout, sizing, and scrolling across screen
sizes.
* Long titles and descriptions are now truncated for a cleaner
presentation.

* **Bug Fixes**
  * Improved video preview sizing within the media viewer.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com>
This is an automated nightly unsafe-cast remediation sweep. It replaces
selected unsafe TypeScript assertions at boundary and opaque-cache reads
with parse-don't-assert validation while preserving the existing
fail-open/null/false contracts. The changes follow the TypeScript Coding
Standards guidance for unsafe casts and parse-don't-assert validation,
and the Zod boundary-validation pattern established in PR ColeMurray#807.

| Finding | Risk | Cast removed | Fix |
| --- | --- | --- | --- |
| `packages/slack-bot/src/classifier/repos.ts:109` | HIGH | `(await
response.json()) as ControlPlaneReposResponse` for the control-plane
repo catalog used by Slack repository selection and cached for
routing/classification | Added shared Zod repository catalog schemas and
`controlPlaneReposResponseSchema.safeParse`; malformed responses log
`invalid_response` and use the existing cache/fallback path |
| `packages/slack-bot/src/classifier/repos.ts:165` | MEDIUM | `cached as
RepoConfig[]` for the opaque KV `repos:cache` value | Added shared
`repoConfigSchema` and validate the cached array before returning it;
malformed cache values fall through to the existing fallback path |
| `packages/linear-bot/src/utils/linear-client.ts:403` | HIGH | `(await
response.json()) as { data?: { commentCreate?: { success: boolean } } }`
for the external Linear comment mutation response | Added package-local
Zod `linearCommentCreateResponseSchema.safeParse`; malformed responses
preserve the existing `{ success: false }` behavior |

Verification:

| Command | Result |
| --- | --- |
| `npm run build -w @open-inspect/shared` | Passed |
| `npm run build -w @open-inspect/slack-bot` | Passed |
| `npm run build -w @open-inspect/linear-bot` | Passed |
| `npm run typecheck` | Passed |
| `npm run lint` | Passed after temporarily moving unrelated untracked
local `.opencode/` helper files out of the repo and restoring them, so
ESLint checked the tracked project tree |
| `npm run format` | Passed |
| `npm test -w @open-inspect/shared` | Passed, 37 files / 493 tests |
| `npm test -w @open-inspect/slack-bot` | Passed, 28 files / 340 tests |
| `npm test -w @open-inspect/linear-bot` | Passed, 13 files / 188 tests
|

No dependencies were added.

---
*Created with
[Open-Inspect](https://open-inspect-prod.vercel.app/session/3ec707511fea1b23341ace676c889963)*

---------

Co-authored-by: waclaude <colemurray.cs+ghwaclaude@gmail.com>
Co-authored-by: Cole Murray <colemurray.cs@gmail.com>
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds runtime validation for authentication, session, repository, and external API payloads; refactors WebSocket client-context handling; enables configured GitHub bot self-reviews; and updates web UI behavior, accessibility, and memoization.

Changes

Authentication and request validation

Layer / File(s) Summary
Authentication and prompt validation contracts
packages/control-plane/src/auth/user/better-auth.ts, packages/control-plane/src/routes/session-prompt.ts, packages/control-plane/vitest.integration.config.ts, CHANGELOG.md
Google direct ID-token sign-in is disabled, session prompt payloads use shared schemas, and Better Auth invalid-token errors are filtered in integration tests.
Browser authentication rejection coverage
packages/control-plane/test/integration/browser-auth.test.ts
Tests cover untrusted origins and direct Google ID-token rejection without creating sessions.

WebSocket client context and presence

Layer / File(s) Summary
Presence service client contract
packages/control-plane/src/session/presence-service.ts, packages/control-plane/src/session/presence-service.test.ts
Presence updates now accept authenticated clients directly and broadcast updated presence state.
Authenticated WebSocket dispatch
packages/control-plane/src/session/durable-object.ts
Prompt, history, and presence handlers receive one resolved client context per message.
Pre-subscription command tests
packages/control-plane/test/integration/stop-execution.test.ts, packages/control-plane/test/integration/websocket-client.test.ts
Unsubscribed stop and typing commands are verified to close with code 4002.

Runtime payload validation

Layer / File(s) Summary
Shared repository and session schemas
packages/shared/src/types/repository-catalog.ts, packages/shared/src/types/session-api.ts, packages/shared/src/types/index.ts
Repository and callback-context interfaces are represented by exported Zod schemas and inferred types.
Schema validation coverage
packages/shared/src/types/*test.ts
Tests cover valid and malformed repository, prompt, callback, and cached configuration payloads.
Slack repository response and cache validation
packages/slack-bot/src/classifier/*, packages/slack-bot/src/index.test.ts
Control-plane and KV repository data are parsed before use, with normalized response fixtures.
Linear comment response validation
packages/linear-bot/src/utils/linear-client.*
Linear comment mutation responses are validated and malformed JSON or shapes return failure.

GitHub bot self-review handling

Layer / File(s) Summary
Self-review prompt behavior
packages/github-bot/src/prompts.ts, packages/github-bot/src/handlers.ts, packages/github-bot/test/prompts.test.ts
Bot-authored reviews use comment-only events and self-review guidance.
Configured self-review gate
packages/github-bot/test/handlers.test.ts
Bot-authored pull requests are processed only when the bot is an allowed trigger user.
Auto-review flow documentation
packages/github-bot/README.md
The documented flow reflects configured trigger-user gating and comment-only self-reviews.

Web UI and rendering updates

Layer / File(s) Summary
Media lightbox and API response handling
packages/web/src/components/media-lightbox.tsx, packages/web/src/components/screenshot-media.test.tsx, packages/web/src/app/api/image-builds/route.ts
The media dialog gains an explicit close control and constrained scrolling, and image-build responses are parsed concurrently.
Settings controls and port normalization
packages/web/src/components/settings/*
Settings labels are associated with controls, and port normalization is extracted into a reusable helper.
Memoized web context values
packages/web/src/components/sidebar-layout.tsx, packages/web/src/components/ui/toggle-group.tsx
Context provider values are memoized before being passed to consumers.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Suggested reviewers: colemurray

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects that this is an upstream sync PR and includes the sync date, which matches the overall change set.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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

@github-actions

Copy link
Copy Markdown

Terraform Validation Results

Step Status
Format
Init
Validate

Pushed by: @NicolasWalter, Action: pull_request

@github-actions

Copy link
Copy Markdown

Terraform Plan Results

Status: ✅ Success

Show Plan
terraform_data.cloudflare_custom_domain_gate: Refreshing state... [id=fa456fac-6c14-16e4-a484-3338d1a3718d]
terraform_data.access_control_gate: Refreshing state... [id=841ab6bc-98a7-018c-1031-ebad4f8b62bc]
random_password.service_auth_secret_github_bot: Refreshing state... [id=none]
null_resource.slack_bot_build[0]: Refreshing state... [id=7727411747602718703]
local_file.web_app_wrangler_production[0]: Refreshing state... [id=daff6090dd93678e6fd6719293e71437dc6f5d6b]
null_resource.control_plane_build: Refreshing state... [id=2042406076740975108]
null_resource.web_app_cloudflare_build[0]: Refreshing state... [id=1984921645726027654]
null_resource.github_bot_build[0]: Refreshing state... [id=7536682210187789111]
random_password.service_auth_secret_web: Refreshing state... [id=none]
random_password.service_auth_secret_slack_bot: Refreshing state... [id=none]
random_password.service_auth_secret_modal: Refreshing state... [id=none]
null_resource.linear_bot_build[0]: Refreshing state... [id=937584459833419549]
random_password.image_callback_token_pepper: Refreshing state... [id=none]
module.slack_kv[0].cloudflare_workers_kv_namespace.this: Refreshing state... [id=729b357dbb5e4c9d99ec9212cc45766e]
cloudflare_queue.slack_completion_delivery_dlq[0]: Refreshing state... [id=06ce03d2663f4aea937b0c0c1c379c17]
module.github_kv[0].cloudflare_workers_kv_namespace.this: Refreshing state... [id=87dbfaa1d9ce4a37a42e04c57c434a72]
cloudflare_r2_bucket.media: Refreshing state... [id=open-inspect-media-primo]
module.session_index_kv.cloudflare_workers_kv_namespace.this: Refreshing state... [id=7f18644fbed34121bbe3a196f373ea93]
cloudflare_d1_database.main: Refreshing state... [id=dba95b03-ace9-47a8-81e9-6e39d8d694c5]
module.linear_kv[0].cloudflare_workers_kv_namespace.this: Refreshing state... [id=d003f1ad81384910a1f48a0a33f18c09]
data.external.modal_source_hash[0]: Reading...
cloudflare_queue.slack_completion_delivery[0]: Refreshing state... [id=56ef0f3e13bd46a3a39f30c79ec547fa]
random_password.service_auth_secret_linear_bot: Refreshing state... [id=none]
module.modal_app[0].null_resource.modal_secrets[0]: Refreshing state... [id=2786381799123975256]
data.external.modal_source_hash[0]: Read complete after 0s [id=-]
module.modal_app[0].null_resource.modal_deploy: Refreshing state... [id=7095165949869290970]

Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  + create
-/+ destroy and then create replacement

Terraform planned the following actions, but then encountered a problem:

  # local_file.web_app_wrangler_production[0] will be created
  + resource "local_file" "web_app_wrangler_production" {
      + content              = (sensitive value)
      + content_base64sha256 = (known after apply)
      + content_base64sha512 = (known after apply)
      + content_md5          = (known after apply)
      + content_sha1         = (known after apply)
      + content_sha256       = (known after apply)
      + content_sha512       = (known after apply)
      + directory_permission = "0777"
      + file_permission      = "0777"
      + filename             = "../../..//packages/web/wrangler.production.toml"
      + id                   = (known after apply)
    }

  # null_resource.control_plane_build must be replaced
-/+ resource "null_resource" "control_plane_build" {
      ~ id       = "2042406076740975108" -> (known after apply)
      ~ triggers = { # forces replacement
          ~ "always_run" = "2026-07-27T06:28:07Z" -> (known after apply)
        }
    }

  # null_resource.github_bot_build[0] must be replaced
-/+ resource "null_resource" "github_bot_build" {
      ~ id       = "7536682210187789111" -> (known after apply)
      ~ triggers = { # forces replacement
          ~ "always_run" = "2026-07-27T06:28:07Z" -> (known after apply)
        }
    }

  # null_resource.linear_bot_build[0] must be replaced
-/+ resource "null_resource" "linear_bot_build" {
      ~ id       = "937584459833419549" -> (known after apply)
      ~ triggers = { # forces replacement
          ~ "always_run" = "2026-07-27T06:28:07Z" -> (known after apply)
        }
    }

  # null_resource.slack_bot_build[0] must be replaced
-/+ resource "null_resource" "slack_bot_build" {
      ~ id       = "7727411747602718703" -> (known after apply)
      ~ triggers = { # forces replacement
          ~ "always_run" = "2026-07-27T06:28:07Z" -> (known after apply)
        }
    }

  # null_resource.web_app_cloudflare_build[0] must be replaced
-/+ resource "null_resource" "web_app_cloudflare_build" {
      ~ id       = "1984921645726027654" -> (known after apply)
      ~ triggers = { # forces replacement
          ~ "always_run" = "2026-07-27T06:28:07Z" -> (known after apply)
        }
    }

Plan: 6 to add, 0 to change, 5 to destroy.

Error: failed to make http request

  with cloudflare_d1_database.main,
  on d1.tf line 5, in resource "cloudflare_d1_database" "main":
   5: resource "cloudflare_d1_database" "main" {

GET
"https://api.cloudflare.com/client/v4/accounts/bf66240843ed90d19b82e4b90916d29a/d1/database/dba95b03-ace9-47a8-81e9-6e39d8d694c5":
401 Unauthorized
{"result":null,"success":false,"errors":[{"code":10000,"message":"Authentication
error"}],"messages":[]}

Error: failed to make http request

  with cloudflare_r2_bucket.media,
  on r2.tf line 5, in resource "cloudflare_r2_bucket" "media":
   5: resource "cloudflare_r2_bucket" "media" {

GET
"https://api.cloudflare.com/client/v4/accounts/bf66240843ed90d19b82e4b90916d29a/r2/buckets/open-inspect-media-primo":
403 Forbidden
{"success":false,"errors":[{"code":10000,"message":"Authentication
error"}],"messages":[],"result":null}


Error: failed to make http request

  with cloudflare_queue.slack_completion_delivery[0],
  on workers-slack.tf line 5, in resource "cloudflare_queue" "slack_completion_delivery":
   5: resource "cloudflare_queue" "slack_completion_delivery" {

GET
"https://api.cloudflare.com/client/v4/accounts/bf66240843ed90d19b82e4b90916d29a/queues/56ef0f3e13bd46a3a39f30c79ec547fa":
403 Forbidden
{"success":false,"errors":[{"code":10000,"message":"Authentication
error"}],"messages":[],"result":null}


Error: failed to make http request

  with cloudflare_queue.slack_completion_delivery_dlq[0],
  on workers-slack.tf line 12, in resource "cloudflare_queue" "slack_completion_delivery_dlq":
  12: resource "cloudflare_queue" "slack_completion_delivery_dlq" {

GET
"https://api.cloudflare.com/client/v4/accounts/bf66240843ed90d19b82e4b90916d29a/queues/06ce03d2663f4aea937b0c0c1c379c17":
403 Forbidden
{"success":false,"errors":[{"code":10000,"message":"Authentication
error"}],"messages":[],"result":null}


Error: failed to make http request

  with module.linear_kv[0].cloudflare_workers_kv_namespace.this,
  on ../../modules/cloudflare-kv/main.tf line 4, in resource "cloudflare_workers_kv_namespace" "this":
   4: resource "cloudflare_workers_kv_namespace" "this" {

GET
"https://api.cloudflare.com/client/v4/accounts/bf66240843ed90d19b82e4b90916d29a/storage/kv/namespaces/d003f1ad81384910a1f48a0a33f18c09":
401 Unauthorized
{"result":null,"success":false,"errors":[{"code":10000,"message":"Authentication
error"}],"messages":[]}

Error: failed to make http request

  with module.github_kv[0].cloudflare_workers_kv_namespace.this,
  on ../../modules/cloudflare-kv/main.tf line 4, in resource "cloudflare_workers_kv_namespace" "this":
   4: resource "cloudflare_workers_kv_namespace" "this" {

GET
"https://api.cloudflare.com/client/v4/accounts/bf66240843ed90d19b82e4b90916d29a/storage/kv/namespaces/87dbfaa1d9ce4a37a42e04c57c434a72":
401 Unauthorized
{"result":null,"success":false,"errors":[{"code":10000,"message":"Authentication
error"}],"messages":[]}

Error: failed to make http request

  with module.slack_kv[0].cloudflare_workers_kv_namespace.this,
  on ../../modules/cloudflare-kv/main.tf line 4, in resource "cloudflare_workers_kv_namespace" "this":
   4: resource "cloudflare_workers_kv_namespace" "this" {

GET
"https://api.cloudflare.com/client/v4/accounts/bf66240843ed90d19b82e4b90916d29a/storage/kv/namespaces/729b357dbb5e4c9d99ec9212cc45766e":
401 Unauthorized
{"result":null,"success":false,"errors":[{"code":10000,"message":"Authentication
error"}],"messages":[]}

Error: failed to make http request

  with module.session_index_kv.cloudflare_workers_kv_namespace.this,
  on ../../modules/cloudflare-kv/main.tf line 4, in resource "cloudflare_workers_kv_namespace" "this":
   4: resource "cloudflare_workers_kv_namespace" "this" {

GET
"https://api.cloudflare.com/client/v4/accounts/bf66240843ed90d19b82e4b90916d29a/storage/kv/namespaces/7f18644fbed34121bbe3a196f373ea93":
401 Unauthorized
{"result":null,"success":false,"errors":[{"code":10000,"message":"Authentication
error"}],"messages":[]}
::error::Terraform exited with code 1.

Pushed by: @NicolasWalter

@NicolasWalter
NicolasWalter merged commit c86f780 into main Jul 28, 2026
17 of 18 checks passed

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🧹 Nitpick comments (2)
packages/github-bot/src/prompts.ts (1)

48-62: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Move the parameter default to a named constant.

Use a named default constant for isSelfReview rather than an inline parameter default. As per coding guidelines, “Define each default value exactly once in a named constant and import or reuse that constant everywhere.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/github-bot/src/prompts.ts` around lines 48 - 62, Move the inline
default for isSelfReview in the surrounding prompt-building function to a named
constant, then reuse that constant when destructuring params. Ensure the default
value is defined only once and remains false.

Source: Coding guidelines

packages/shared/src/types/session-api.ts (1)

18-26: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Slack/automation callback fields allow empty strings; Linear's don't.

slackCallbackContextSchema (channel, threadTs, repoFullName, model) and automationCallbackContextSchema (automationId, runId, automationName) use plain z.string(), while linearCallbackContextSchema uses nonEmptyStringSchema for its identifiers. Since callbackContextSchema is used to validate the externally-supplied callbackContext on prompt requests, an empty channel/repoFullName/automationId would pass validation here and only fail later against the Slack/automation API.

🔧 Suggested fix for tightening identifier fields
 export const slackCallbackContextSchema = z.object({
   source: z.literal("slack"),
-  channel: z.string(),
-  threadTs: z.string(),
-  repoFullName: z.string(),
-  model: z.string(),
+  channel: nonEmptyStringSchema,
+  threadTs: nonEmptyStringSchema,
+  repoFullName: nonEmptyStringSchema,
+  model: nonEmptyStringSchema,
   reasoningEffort: z.string().optional(),
   reactionMessageTs: z.string().optional(),
 });

Also applies to: 69-74

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/shared/src/types/session-api.ts` around lines 18 - 26, Update
slackCallbackContextSchema and automationCallbackContextSchema to use the
existing nonEmptyStringSchema for all required identifier fields, including
channel, threadTs, repoFullName, model, automationId, runId, and automationName,
so externally supplied callback contexts reject empty strings consistently with
linearCallbackContextSchema.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/github-bot/src/prompts.ts`:
- Around line 63-66: Update the reviewEvent value used by the command example so
it always contains one valid event rather than the literal pipe-separated list.
Preserve the self-review behavior while selecting a single valid placeholder or
concrete event for the non-self-review path, and keep the related
reviewEventGuidance in sync.

---

Nitpick comments:
In `@packages/github-bot/src/prompts.ts`:
- Around line 48-62: Move the inline default for isSelfReview in the surrounding
prompt-building function to a named constant, then reuse that constant when
destructuring params. Ensure the default value is defined only once and remains
false.

In `@packages/shared/src/types/session-api.ts`:
- Around line 18-26: Update slackCallbackContextSchema and
automationCallbackContextSchema to use the existing nonEmptyStringSchema for all
required identifier fields, including channel, threadTs, repoFullName, model,
automationId, runId, and automationName, so externally supplied callback
contexts reject empty strings consistently with linearCallbackContextSchema.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e7dcb3eb-bfde-42a3-97cb-c842e922e3f5

📥 Commits

Reviewing files that changed from the base of the PR and between 18a0b55 and 24993fc.

📒 Files selected for processing (33)
  • CHANGELOG.md
  • packages/control-plane/src/auth/user/better-auth.ts
  • packages/control-plane/src/routes/session-prompt.ts
  • packages/control-plane/src/session/durable-object.ts
  • packages/control-plane/src/session/presence-service.test.ts
  • packages/control-plane/src/session/presence-service.ts
  • packages/control-plane/test/integration/browser-auth.test.ts
  • packages/control-plane/test/integration/stop-execution.test.ts
  • packages/control-plane/test/integration/websocket-client.test.ts
  • packages/control-plane/vitest.integration.config.ts
  • packages/github-bot/README.md
  • packages/github-bot/src/handlers.ts
  • packages/github-bot/src/prompts.ts
  • packages/github-bot/test/handlers.test.ts
  • packages/github-bot/test/prompts.test.ts
  • packages/linear-bot/src/utils/linear-client.test.ts
  • packages/linear-bot/src/utils/linear-client.ts
  • packages/shared/src/types/boundary-schemas.test.ts
  • packages/shared/src/types/index.ts
  • packages/shared/src/types/repository-catalog.test.ts
  • packages/shared/src/types/repository-catalog.ts
  • packages/shared/src/types/session-api.ts
  • packages/slack-bot/src/classifier/repos.test.ts
  • packages/slack-bot/src/classifier/repos.ts
  • packages/slack-bot/src/index.test.ts
  • packages/web/src/app/api/image-builds/route.ts
  • packages/web/src/components/media-lightbox.tsx
  • packages/web/src/components/screenshot-media.test.tsx
  • packages/web/src/components/settings/integrations/github-integration-settings.tsx
  • packages/web/src/components/settings/integrations/linear-integration-settings.tsx
  • packages/web/src/components/settings/sandbox-settings.tsx
  • packages/web/src/components/sidebar-layout.tsx
  • packages/web/src/components/ui/toggle-group.tsx

Comment on lines +63 to +66
const reviewEvent = isSelfReview ? "COMMENT" : "COMMENT|APPROVE|REQUEST_CHANGES";
const reviewEventGuidance = isSelfReview
? "Use COMMENT because GitHub does not allow pull request authors to approve their own PRs."
: "Use APPROVE if the code looks good, REQUEST_CHANGES if changes are needed,\n or COMMENT for general feedback.";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
curl --fail --silent --show-error \
  https://docs.github.com/en/rest/pulls/reviews \
  | rg -n 'APPROVE|REQUEST_CHANGES|COMMENT'

Repository: primo-devs/primo-bg-coding-agent

Length of output: 50389


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the relevant file around the changed lines and nearby prompt assembly.
FILE="packages/github-bot/src/prompts.ts"
wc -l "$FILE"
sed -n '1,220p' "$FILE"

# Find other uses of the reviewEvent / reviewEventGuidance fields.
rg -n "reviewEvent|reviewEventGuidance|COMMENT\|APPROVE\|REQUEST_CHANGES|isSelfReview" packages/github-bot/src -S

Repository: primo-devs/primo-bg-coding-agent

Length of output: 7256


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the prompt consumer and surrounding review-submission flow.
sed -n '320,390p' packages/github-bot/src/handlers.ts
sed -n '1,140p' packages/github-bot/src/prompts.ts

Repository: primo-devs/primo-bg-coding-agent

Length of output: 6414


Use a single review event in the command example. event only accepts APPROVE, REQUEST_CHANGES, or COMMENT; COMMENT|APPROVE|REQUEST_CHANGES is invalid if copied literally. Replace it with a single placeholder or one concrete event before showing the command.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/github-bot/src/prompts.ts` around lines 63 - 66, Update the
reviewEvent value used by the command example so it always contains one valid
event rather than the literal pipe-separated list. Preserve the self-review
behavior while selecting a single valid placeholder or concrete event for the
non-self-review path, and keep the related reviewEventGuidance in sync.

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.

2 participants