Skip to content

chore: sync with upstream 2026-08-27 - #118

Open
NicolasWalter wants to merge 16 commits into
mainfrom
sync/upstream-2026-08-27
Open

chore: sync with upstream 2026-08-27#118
NicolasWalter wants to merge 16 commits into
mainfrom
sync/upstream-2026-08-27

Conversation

@NicolasWalter

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.

ColeMurray and others added 16 commits August 25, 2026 22:56
…gest closure bags (ColeMurray#1608)

## What

First PR of the deps-style normalization campaign (follow-through on the
ColeMurray#1594ColeMurray#1604 decomposition): replace the composition root's three biggest
closure-bag literals with composition classes, per the house deps
standard from the ColeMurray#1045-series (pass collaborators directly with full
types; give a closure group that shares collaborators a named class).

Behavior-preserving — no port changes, no call-flow changes.

## Changes

- **`DurableObjectSandboxStorage`** (new
`session/sandbox-lifecycle-adapters.ts`) implements the lifecycle
manager's `SandboxStorage` port over its four real collaborators:
`SandboxRepository`, `SessionCoreRepository`, `UserEnvResolver`, and the
secrets encryption key. Replaces the 28-property literal in the root.
The encrypt-before-store rule (code-server/VNC/ttyd secrets), previously
copy-pasted three times inline, is one private `encryptIfConfigured`
method.
- **`LifecycleSocketAdapter`** (same file) implements the manager's
`WebSocketManager` port over `SessionWebSocketManager` — the name
translation and the no-socket send branch get a typed home instead of a
literal.
- **`SessionClientCommandFacade`** (new
`session/client-command-facade.ts`) implements the message router's
`SessionClientCommands<WebSocket, ClientInfo>` port with the four
services as constructor deps. The port itself stays generic — that
genericity is what lets the server stack unit-test over string
connections, so the facade is the production binding, not a port
rewrite. The router's client-message type aliases are now exported (they
are referenced by the exported port, so naming them outside the module
was already implied).

Net: 39 function-valued props removed from `components.ts`; the root now
constructs objects in these three spots instead of authoring behavior
inline.

## Tests

New `sandbox-lifecycle-adapters.test.ts` covers the pieces with real
logic, which previously lived untested inside the root literal: the
encrypt-when-configured branch (round-trips via `decryptToken`), the
plaintext-passthrough branch, the repository-shape defaults
(`baseBranch` → `"main"`, missing row → `baseSha: null`), the
`setLastSpawnError` → `updateSandboxSpawnError` rename, and both
`sendToSandbox` branches. Pure forwards stay covered through the manager
and server suites.

## Queue context

Next in the campaign (separate PRs): handler deps-bags → classes
(normalizing the 7-factory/5-class split), vestigial thunk removal
(`getLogger: () => log` first), and the `test/integration` typecheck
spike.


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

- **Refactor**
- Improved session command handling for prompts, execution controls,
typing indicators, presence, subscriptions, and history.
- Improved sandbox lifecycle and WebSocket handling for more consistent
session connectivity.
- **Security**
- Sandbox access credentials can now be encrypted when configured, while
retaining compatibility with existing setups.
- **Tests**
- Added coverage for credential storage, sandbox startup errors,
repository behavior, and WebSocket communication.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
…lve the storage middle-man (ColeMurray#1609)

## What

Campaign item 2, combining two agreed decisions: **the secrets
encryption key is required** (it always was operationally — Terraform
declares it with no default — but the code treated it as optional and
silently fell back to storing plaintext), and **the storage middle-man
from ColeMurray#1608 is dissolved** (its ~25 one-line pass-throughs were the smell
that prompted the design discussion).

## Encryption key is required

- New `requireRepoSecretsEncryptionKey(env)`: the session graph throws
at construction when the key is absent (the ColeMurray#1602 eager posture — a
misconfigured deployment fails every request at initialization instead
of running degraded), and the five MCP-server routes validate the same
way.
- Every plaintext-**write** fallback is deleted: the sandbox
access-secret stores, `McpServerStore`'s keyless branch, and
`UserEnvResolver`'s "skip secret loading" branch.
`isManagedSecretsConfigured` reduces to `Boolean(db)`.
- Plaintext-**read** fallbacks stay: pre-encryption legacy rows still
decrypt-or-degrade exactly as before (`McpServerStore`'s catch fallback,
access values resolving to null on decrypt failure).
- The integration environment already provides a test key in its
miniflare bindings, so no test-infra changes were needed.

## Encryption is owned by persistence; the middle-man is gone

- `SandboxRepository` takes the key at construction and encrypts
code-server/VNC/ttyd secrets inside its write methods — the same pattern
the D1 stores already use. No caller can persist an access secret in the
clear, structurally.
- The manager's conflated port is **split into two roles** — the root
cause behind both the ColeMurray#1608 forwarding layer and an interim inheritance
design. `SandboxStorage` shrinks to the sandbox-row contract, which
`SandboxRepository` now satisfies **structurally** (no adapter, no
subclass, and no manager-port import in the repository — the structural
check happens at the composition boundary). The three session-context
reads become their own `SessionContextReader` port, implemented by a
small `LifecycleSessionContext` facade over `SessionCoreRepository` +
`UserEnvResolver` — an honest adapter: it spans two collaborators and
owns the repository-shape defaults. `DurableObjectSandboxStorage` is
deleted.
- The shared test mock already implements both ports, so the manager's
test harness changes are mechanical: the same fake is passed for both
parameters at every constructor site.
- `updateSandboxSpawnError` is renamed `setLastSpawnError` to match the
port vocabulary, removing the last name translation.

## Tests

Encryption round-trips (via `decryptToken`) now live in
`sandbox-repository.test.ts` with the logic; the adapter tests pin the
context mapping and the inheritance wiring ("sandbox writes hit SQL with
no forwarding layer"). Deleted-behavior tests are deleted with their
behavior: the keyless verbatim-read test, the resolver's
skip-secret-loading test, and ColeMurray#1608's synchronous-keyless-persist test
(that branch no longer exists — with the key required, every secret
write takes the same WebCrypto await it always took on real
deployments). `McpServerStore` tests construct keyed; their
plaintext-seeded rows now exercise the legacy-read fallback, which is
exactly what such rows are.

## Behavior change (intended)

A deployment without `REPO_SECRETS_ENCRYPTION_KEY` now fails loudly at
session initialization and on MCP routes, instead of silently persisting
secrets unencrypted. Valid deployments are unaffected.


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

* **Security**
* Repository secrets encryption is now required for control-plane
operations.
* Sandbox passwords, tokens, credentials, and stored environment secrets
are encrypted before persistence.
* Encryption keys are strictly validated for required format and length.

* **Bug Fixes**
  * Improved handling of unavailable or empty stored secrets.
  * Reduced unnecessary decryption errors for empty credentials.
  * Improved sandbox error reporting.

* **Refactor**
* Streamlined sandbox lifecycle and session-context handling for more
consistent behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary

- move the six Python CI jobs into a dedicated `CI (Python)` workflow
- keep the seven Node.js/TypeScript jobs in `CI (TypeScript)`
- trigger each workflow only for its package and root-tooling dependency
surface
- preserve the Markdown-only exclusions added in ColeMurray#1590

## Motivation

The main CI workflow currently runs both ecosystems for every code
change. This split prevents Python-only changes from allocating
TypeScript runners and TypeScript-only changes from allocating Python
runners, while preserving all existing job commands and dependencies.

This is the ecosystem-level step before introducing narrower
package-aware filtering in follow-up PRs.

## Validation

- `npx prettier --check .github/workflows/ci.yml
.github/workflows/ci-python.yml`
- parsed both workflows and verified all 13 original job definitions
remain present
- `git diff --check`

`actionlint` and Go were unavailable in the local environment. The
repository-wide `npm run format:check` also reports a pre-existing
formatting issue in `.opencode/package.json`; both changed workflow
files pass their targeted formatting check.

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

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

* **Chores**
* Added dedicated continuous integration checks for Python linting,
formatting, type checking, and tests.
  * Updated TypeScript validation to run through a dedicated workflow.
* Refined workflow triggers to focus on relevant code and configuration
changes, excluding documentation-only updates.
* Expanded validation coverage for runtime, deployment, and
infrastructure changes.
* Added concurrency controls to cancel outdated runs and strengthened
workflow security settings.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com>
while working on ColeMurray#1037 i noticed that the e2b sandboxes started by the
current template were failing to run bun despite being installed by the
dockerfile.

The Dockerfile previously ran the installer like this:

`BUN_INSTALL=/usr/local curl ... | bash`

That environment variable applied to `curl`, not the `bash` process
running the installer. Bun therefore used its default install location,
which was outside the runtime user's PATH.

This change passes `BUN_INSTALL=/usr/local` to `bash` and also adds
`command -v bun` to the template readiness check.


### Before

<img width="1228" height="755" alt="e2b-bun-issue-before"
src="https://github.com/user-attachments/assets/781533c4-5983-4262-bcf8-acb0cdddcf26"
/>

### After

<img width="1231" height="782" alt="e2b-bun-issue-after"
src="https://github.com/user-attachments/assets/7258ce2b-8f53-42f3-9a98-2a8603181fa5"
/>


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

## Summary by CodeRabbit

* **Bug Fixes**
* Template readiness checks now verify that Bun is available before
finalization.
* **Chores**
  * Improved the Bun installation setup during environment creation.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
…asses (ColeMurray#1612)

## Summary

Item 3 of the deps-style normalization campaign (follow-up to
ColeMurray#1608/ColeMurray#1609): the seven session HTTP handlers still built as
`createXHandler(deps)` factories over deps-bags become classes with
direct constructor collaborators, matching the `SessionDiffsHandler`
(ColeMurray#1047) and `AttachmentsHandler` precedents. One prerequisite commit
makes `TOKEN_ENCRYPTION_KEY` required, mirroring ColeMurray#1609's treatment of
the repo-secrets key.

The deps-bags were where most of the composition root's pure same-name
forwards lived — closures like `getSession: () =>
sessionCoreRepository.getSession()` that exist only because a bag can't
hold the repository itself. Net effect in `components.ts`: 43
function-valued closure lines removed, 8 added back as named per-request
adapters (−35), and all seven `XHandlerDeps` interfaces deleted.

## `TOKEN_ENCRYPTION_KEY` is now required (first commit)

Terraform already requires the key (no default, `sensitive`) and the
`Env` type declares it non-optional — the three falsy-guards were
silent-degradation branches:

- `identity.ts` silently dropped stored SCM tokens from GitHub
enrichment,
- the session graph silently skipped constructing the user token store,
- session init silently discarded a plaintext SCM token instead of
encrypting it.

`requireTokenEncryptionKey(env)` shares the AES-256 material validator
with `requireRepoSecretsEncryptionKey` (strict base64, exactly 32
decoded bytes) and is thrown at session-graph construction, so a
misconfigured deployment fails every request at init rather than
degrading. Plaintext-read paths are untouched.

## Conversion rules (uniform across all seven)

- **Collaborators become constructor params with their real types** —
repositories, services, messenger. `deps.getSession()` →
`this.sessionCoreRepository.getSession()`.
- **Constant thunks become data** — `getDurableObjectId: () =>
durableObjectId` → `durableObjectId: string`;
`isManagedSecretsConfigured: () => Boolean(db)` →
`managedSecretsConfigured: boolean` (fixed at composition).
- **Module functions re-wrapped only to bind composition-time values are
called directly** — `resolvePublicSessionId(session,
this.durableObjectId)`, `parseArtifactMetadata(artifact, this.log)`,
`validateReasoningEffort(model, effort, this.log)`; same instances, same
arguments as the deleted closures.
- **Genuine adapters stay function-typed params** (8 total): the three
per-request token/credential service factories on `SandboxHandler`, the
request-log-scoped `createPullRequest` factory + `getSessionUrl` +
background `triggerPullRequestRefresh` on `PullRequestHandler`, and
`scheduleWarmSandbox` + `cancelSession` on `SessionLifecycleHandler`.
- **Seams stay functions without eta-expansion** — the root passes
`generateId`/`hashToken`/`encryptToken`/`isValidSandboxToken` as bare
module references; `now` defaults to `Date.now` per the
`AttachmentsHandler` precedent.
- **The class replaces the same-named interface**, so the internal route
table (`components.ts` tier 9) is untouched — those wrappers adapt the
uniform route signature to method arities and are not forwards.
- `SessionLifecycleHandler`'s cancel path reuses the lifecycle
`WebSocketManager` port via a `LifecycleSocketAdapter` instance (ColeMurray#1608)
instead of two raw socket forwards; the adapter's `sendToSandbox`
performs the identical resolve-then-send.
- `PullRequestHandler`'s local result-union aliases were byte-identical
to `ParticipantService`'s declared return types and are deleted.

## Behavior notes

- Behavior-preserving except the deliberate key-requirement change
above.
- Tests now exercise the real `resolvePublicSessionId` (via
`session_name` fixtures) and the real `validateReasoningEffort` (whose
catalog answers match what the old stubs returned) instead of stubs.
- One commit per handler group; every commit is independently green.

## Testing

- `tsc --noEmit` (prod + test configs), ESLint, Prettier
- Unit: 205 files / 3186 tests green
- Integration (workerd + real D1): green


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

* **New Features**
* Added validation for the token encryption key used to protect OAuth
tokens.
* Token-based identity enrichment now requires valid encryption-key
configuration.

* **Bug Fixes**
* Improved configuration errors for missing, malformed, or incorrectly
sized encryption keys.

* **Refactor**
* Updated session and HTTP request handling for more consistent
dependency management without changing endpoint behavior.

* **Tests**
* Expanded coverage for encryption-key validation and token-related
session flows.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary

Behavior-preserving follow-up to ColeMurray#1608/ColeMurray#1609/ColeMurray#1612 (deps-style
normalization, per the ColeMurray#1045ColeMurray#1049 standard): drop the vestigial logger
thunks. Five sites took the session logger as a zero-arg function
(`getLogger: () => Logger` / `getLog: () => Logger`) and called it on
every use; all five are fed a value that is constant after composition,
so they now take `log: Logger` directly.

The thunks existed for the DO-era log swap: `SessionDO` used to reassign
its logger once the public session id resolved, so anything that
captured a logger by value at construction time kept logging the stale
id. That mechanism is gone — the composition root builds one
session-scoped logger whose `session_id` is injected **per emit**
through the latched resolver (`components.ts`: "for every component in
the graph, however early it captured the logger"). The comment in
`sandbox-events.ts` justifying its getter ("The DO swaps its logger for
a request-scoped child during fetch()") described behavior that no
longer exists.

## Changes

| Site | Before | After |
| --- | --- | --- |
| `SessionHttpDispatcher` deps | `getLogger: () => Logger` | `log:
Logger` |
| `SessionMessageRouter` deps | `getLogger: () => Logger` | `log:
Logger` |
| `SessionDisconnectHandler` deps | `getLogger: () => Logger` | `log:
Logger` |
| `SessionSandboxEventProcessor` ctor | `getLog: () => Logger` +
`private get log()` accessor | `private readonly log: Logger` (accessor
deleted; internal `this.log` uses unchanged) |
| `createCloudflareBackgroundTasks` | `getLogger: () => Logger = () =>
log` | `logger: Logger = log` (worker/scheduler callers use the default,
unchanged) |

Composition root: the three `getLogger: () => log` props and two `() =>
log` arguments become `log`.

## What deliberately stays a function

Everything that is genuinely dynamic, per the campaign's classification:

- **Latched resolvers** — `getSessionId` (DO id until the session row
exists, public id after).
- **Live queries** — `getStatus`, `getAuthenticatedClients`,
`getSandboxSocket`, `getProcessingMessageAuthor`, `isSpawning`.
- **Post-init freshness reads** — `getExecutionTimeoutMs`.
- **The SCM provider cell** — `() => scmProvider` reads a mutable `let`
that live-DO integration tests substitute after graph construction.
- **Clock/id seams and adapters** — `now`, `generateId`, action-shaped
deps.

## Testing

- `npm run typecheck -w @open-inspect/control-plane` (both tsconfigs)
clean
- `npm run lint -w @open-inspect/control-plane` clean
- Unit: 3187 passed; integration: 1002 passed


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

## Summary by CodeRabbit

* **Refactor**
* Updated session and background task components to receive logging
instances directly.
* Streamlined error, request, message, disconnect, and sandbox-event
logging.
* Preserved existing session handling, cleanup, reconnection, and close
behavior.

* **Tests**
* Updated automated tests and test setup to match the simplified logging
configuration.

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

`test/integration/**` (91 files) was never typechecked — eslint covers
`src/` only, and the tsconfigs excluded the directory. Store-signature
drift there has repeatedly survived until runtime (`D1_TYPE_ERROR`
mid-suite; most recently a stale `SandboxRepository` construction found
during ColeMurray#1609). This PR adds `tsconfig.integration.json`, fixes
everything it surfaced (1,033 errors initially, most from one root
cause), and wires it into `npm run typecheck` so CI enforces it from now
on.

## The config

- Extends the production tsconfig with `types:
["@cloudflare/workers-types", "@cloudflare/vitest-pool-workers/types"]`
— the integration files execute inside workerd, so they compile against
workers types **without Node globals** (same boundary rationale as the
prod config; Node-context files like `vitest.integration.config.ts` run
in the Vite host and are not part of this program).
- The pool's `cloudflare:test` declarations live at the package's
`./types` subpath export (v0.16 layout). The old root-package reference
silently loads nothing — which is why the existing `env.d.ts` was
augmenting a `ProvidedEnv` interface that no longer exists.
- `env.d.ts` rewritten to the v0.16 contract: merge the worker's real
`Env` (plus `TEST_MIGRATIONS`) into the `Cloudflare.Env` placeholder
that `env` from `cloudflare:test` is typed as. This one fix collapsed
~900 of the initial errors.
- An experiment narrowing `SESSION` to
`DurableObjectNamespace<SessionDO>` inside the augmentation was
reverted: it makes `Cloudflare.Env` unassignable to the production `Env`
at every `handleRequest(env)` call site. The production `Env` cannot be
narrowed either — importing the DO class from `types.ts` is exactly what
the only-`index.ts`-imports-the-adapter lint exists to prevent. Instead,
stub typing happens at one seam:

## New test seams (all in existing helper files)

| Helper | Why |
| --- | --- |
| `runInSessionDO(stub, cb)` | `runInDurableObject` with the stub typed
as the session DO — the single cast asserting what the SESSION namespace
hosts (43 call sites converted) |
| `ctxOf(instance)` | the DO's `ctx` is `protected` on the
`DurableObject` base class; storage seeding/assertions go through this
one cast |
| `sqlDatabase(env.DB)` | plain assignment (no cast) viewing D1 through
the engine-neutral `SqlDatabase` interface, so tests can `batch()`
store-bound statements (21 sites) |
| `getSetCookies(headers)` | workerd implements `Headers.getSetCookie()`
but this workers-types version doesn't declare it — same cast
`src/routes/browser-auth.ts` carries |

## Latent drift the checker caught (the point of the exercise)

All fixed behavior-preservingly:

- **`AutomationRow` fixtures still carried
`repo_owner`/`repo_name`/`base_branch`/`repo_id`** (6 files) — dead
since repos moved to the `automation_repositories` junction table;
linkage in the affected tests already flows through
`replaceRepositories(...)`.
- **Run fixtures set `concurrency_key`** — it lives on invocations now,
so the seeded value never reached any table. Note for a follow-up: the
scheduler-events "does not block a different concurrency key" test seeds
its active run without any key either way, so it doesn't currently
distinguish per-key scoping from no-key blocking (left as-is; runtime
unchanged).
- **Browser-auth router tests passed a raw `ExecutionContext` where the
router now takes `BackgroundTasks`** (3 files) — worked only because the
failure path never ran. Now wrapped with
`createCloudflareBackgroundTasks`, mirroring `index.ts`.
- **`stubSourceControlProvider` was missing
`resolveCommit`/`listTree`/`readBlob`** — the provider read-surface
added for skills import; stubbed with the suite's existing `notUsedHere`
idiom.
- **A session fixture wrote status `"initializing"`** — removed from the
status vocabulary (ColeMurray#1554); now `"active"`.
- **`generateId({ model: "user" })`** — Better Auth's canonical
generator takes no arguments; the argument was silently ignored.
- **`ensureInitialized` still passed in a `SessionPlatform` stub** —
unthreaded by ColeMurray#1604.
- **Repository skill assignments missing the now-required
`baseBranch`**, and **image-build correlation contexts missing the
required `trace_id`**.

Plus mechanical strictness fixes (WebCrypto union narrowing in the
Google id-token helper, `json<T>()` typing, non-null assertions where
`subscribe: true` guarantees replay messages).

`session-do-access.ts`'s old comment — "test/integration/** is never
typechecked (eslint + grep are the only static gates here)" — is
retired.

## Testing

- `npm run typecheck` (now three programs) clean
- Unit: 3187 passed; integration: 1002 passed — no behavioral change
- Prettier over the touched files


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

* **Tests**
* Improved integration-test coverage and type-checking across
authentication, sessions, automations, scheduling, webhooks, and Durable
Object workflows.
* Updated test infrastructure for more reliable cookie handling,
database batching, background tasks, and session state access.
* Refined fixtures and assertions to reflect current repository,
concurrency, and session behavior.
* **Chores**
* Updated test TypeScript configurations and runtime type definitions
for improved validation and editor support.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary

- count bridge heartbeats as sandbox activity while a message is
processing
- keep idle heartbeats liveness-only so abandoned sandboxes still reach
inactivity cleanup
- add unit and Durable Object integration coverage for both states

## Motivation

A long-running tool call can emit no agent events for longer than the
sandbox inactivity timeout even though the bridge remains healthy.
Previously, bridge heartbeats refreshed only heartbeat liveness, so the
lifecycle alarm could classify the sandbox as idle and stop it
mid-execution.

The sandbox event processor already owns which incoming events count as
activity. While a message is processing, a live bridge heartbeat now
renews the existing activity timestamp. After processing finishes,
heartbeats no longer renew activity and ordinary idle cleanup remains
unchanged.

This is a deliberately narrow alternative to ColeMurray#1601. It does not change
execution-timeout recovery, provider stop behavior, queue recovery,
schema, or cleanup semantics.

## Validation

- npm test -w @open-inspect/control-plane — 205 files, 3,188 tests
passed
- npm run test:integration -w @open-inspect/control-plane — 81 files,
1,002 tests passed
- npm run typecheck -w @open-inspect/control-plane
- npm run lint --workspace=@open-inspect/control-plane -- --no-fix
- Prettier check for all changed files
- git diff --check origin/main...HEAD

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

## Summary by CodeRabbit

* **Bug Fixes**
* Improved heartbeat tracking so idle heartbeats maintain liveness
without incorrectly extending activity timers.
* Heartbeats received while processing a message now correctly refresh
activity status.
  * Heartbeat events continue to be excluded from stored event history.

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

Closes out the deps-style normalization campaign
(ColeMurray#1608/ColeMurray#1609/ColeMurray#1612/ColeMurray#1615/ColeMurray#1616): the last-resort `"main"` base-branch
fallback was written as a literal at seven independent sites. Per the
repo convention ("define each default value exactly once — extract to a
named constant and import everywhere"), it is now `DEFAULT_BASE_BRANCH`
in `src/repos/default-branch.ts`, imported at all seven.

Deferred from the ColeMurray#1608 review round.

## The seven sites

All express the same concept — the branch assumed only when neither the
caller nor the SCM provider's repository metadata supplies one;
configured per-repo defaults (ColeMurray#757) always win:

- `repos/resolve.ts` — `input.baseBranch?.trim() || access.defaultBranch
|| …`
- `automation/repository.ts` — same shape for automation repo selections
- `routes/session-child-spawn.ts` — spawn-context fallback
- `session/initialize.ts` and
`session/http/handlers/session-lifecycle.handler.ts` — init-payload
fallback
- `session/snapshot-reader.ts` and
`session/sandbox-lifecycle-adapters.ts` — legacy repository rows
persisted before `base_branch` was stored

Test fixtures keep their literals (they are inputs, not the default's
definition). No behavior change: the constant's value is `"main"`.

## Testing

- `npm run typecheck` (all three programs) clean; ESLint clean
- Unit + integration batteries green
- `rg '\?\? "main"|\|\| "main"' src` (non-test) → no matches


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

* **Bug Fixes**
* Standardized repository branch fallback behavior across session
initialization, automation, repository resolution, and child sessions.
* Repositories without a configured or provider-supplied base branch now
consistently use the default `main` branch.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
- keep directly automated and GitHub bot sessions hidden from the Mine
inbox
- allow user-attributed agent children with automation lineage to appear
as re-rooted Mine entries
- add integration coverage for an automation root with a user-attributed
child

## Root cause
The Mine inbox rejected every session with a non-null `automation_id`.
Child sessions inherit that ID from an automation parent, so even
children created after a user follow-up were filtered out.

## Verification
- `npm run test:integration -w @open-inspect/control-plane --
session-inbox.test.ts`
- `npm test -w @open-inspect/control-plane --
src/routes/session-index.test.ts src/db/session-index.test.ts`
- `npm run typecheck -w @open-inspect/control-plane`
- `npm run lint -w @open-inspect/control-plane`
- focused Prettier check
- `git diff --check`

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

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

* **New Features**
* Updated the “Mine” inbox view to include agent sessions spawned from
automated sessions.
  * Clarified the option used to exclude automated sessions.

* **Bug Fixes**
* Improved inbox filtering so directly automated and GitHub Bot sessions
are excluded while eligible child sessions remain visible.

* **Tests**
* Expanded integration coverage for automated sessions, their child
sessions, and user-owned sessions in the “Mine” view.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com>
## Summary

- queues eligible GitHub PR comments and submitted reviews after signed
webhook validation
- re-reads authoritative GitHub state, correlates the owning session,
and applies repository policy
- records durable decisions and atomically admits one idempotent message
into the existing SessionDO queue
- enforces the rolling per-PR attempt cap and recovers ambiguous or
duplicate deliveries
- keeps Autofix default-off and preserves explicit mention behavior
- uses D1 migration 0058 without colliding with current main

## Stack

1. This PR: human and explicitly allowlisted review feedback foundation
2. ColeMurray#1183: producer-agnostic Open Inspect App reviews
3. ColeMurray#1184: configuration, timeline, queue health, and dogfood operations

## Validation

- all required GitHub checks pass
- full control-plane, web, bot, shared, Python, build, typecheck, lint,
format, integration, and Terraform validation jobs pass
- targeted D1 Autofix integration passes

## Rollout

Autofix remains disabled by default. This PR does not enable any
production repository.

Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com>
## Summary

- accepts actionable submitted reviews authored by the exact configured
Open Inspect App login and Bot actor type
- keeps the dedicated Open Inspect review setting independent from
third-party bot allowlists
- rejects App-authored PR comments, approved reviews, empty reviews, and
matching human logins without normal write permission
- requires no producer-session metadata, publication receipt, special
sandbox tool, or reviewer prompt change

## Why

Autofix consumes authoritative GitHub reviews. Built-in review sessions
and custom automations can continue publishing reviews through their
existing GitHub mechanisms. Eligibility depends on the provider-read App
identity and repository setting, not on which Open Inspect workflow
produced the review.

## Stack

- Depends on ColeMurray#1182
- Base branch: pr-feedback-autofix-human
- Next: ColeMurray#1184 configuration, timeline, queue health, and dogfood
operations

## Validation

- repository typecheck, lint, and format check
- full affected shared, control-plane, GitHub bot, and web suites
- focused own-App eligibility and ingress tests
- targeted D1 Autofix integration
- Terraform format check

## Rollout

Open Inspect review Autofix remains disabled by default. Existing review
producers require no change.

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

## Summary by CodeRabbit

* **New Features**
* Improved pull request feedback processing to recognize authoritative
reviews from the configured Open Inspect app.
* Actionable reviews can now be queued without an additional permission
check.
  * Inline-only review comments are supported.

* **Bug Fixes**
* Improved filtering for unauthorized bots, bot comments, disabled
review handling, non-actionable reviews, and reviewers without write
permission.
  * Removed an incorrect attribution-based rejection case.

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

---------

Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com>
## Summary

- adds global and repository-override Autofix settings with default-off
behavior
- explains that exact Open Inspect App reviews are eligible regardless
of producer workflow
- warns operators before trusting third-party bot input or raising
attempt limits
- labels admitted feedback with the existing generic review origin in
the session timeline
- adds primary Queue and DLQ health inspection without delaying
scheduled work
- documents producer-neutral dogfood, triage, and kill-switch procedures
- makes warranted originating-PR outcome responses explicit

## Stack

- Depends on ColeMurray#1183
- Base branch: pr-feedback-autofix-open-inspect-review
- Final PR in the stack

## Validation

- all required GitHub checks pass
- full control-plane, web, bot, shared, Python, build, typecheck, lint,
format, integration, and Terraform validation jobs pass
- independent thermo review and closure re-review pass
- independent revised-plan adherence review passes with no deviations

## Dogfood gates

This PR does not enable a repository. Before dogfood:

- configure external alert routing for Queue and DLQ health events
- exercise both the built-in reviewer and an existing custom review
automation
- verify duplicate delivery, timeline provenance, and attempt-cap
behavior
- explicitly accept the absence of an authoritative spend budget or add
that platform capability first

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

## Summary by CodeRabbit

* **New Features**
* Added GitHub PR feedback Autofix settings, including review/comment
triggers, approved bot accounts, and attempt limits.
  * Added per-repository Autofix overrides.
* Session timelines now show whether work resumed from a human or bot
comment/review, with a link to the feedback.
  * GitHub avatars now use stable profile images.
* **Bug Fixes**
  * Improved Autofix queue monitoring and operational alerts.
* **Documentation**
  * Added a rollout and troubleshooting runbook for PR Feedback Autofix.

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

---------

Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com>
## Summary
- replace the generic `create-pull-request` argument/output disclosure
with the selected pull request preview treatment
- render agent-authored PR bodies as sanitized Markdown without assuming
Summary or Verification sections
- parse current created, updated, draft, manual, pending, and failure
output variants while preserving unknown output verbatim
- validate external PR links and keep long descriptions progressively
disclosed
- add focused coverage for rendering, lifecycle states, unsafe URLs,
arbitrary body formats, and case-insensitive tool dispatch

## Verification
- `npm test -w @open-inspect/web --
src/components/create-pull-request-event.test.tsx
src/components/tool-call-item.test.tsx`
- `npm run lint -w @open-inspect/web`
- `npm run typecheck -w @open-inspect/web`
- `git diff --check`

## Testing note
- the full web suite completed all 1,226 assertions successfully, but
Vitest exited nonzero because the pre-existing
`sandbox-settings.test.tsx` timeout callback fired after jsdom teardown
(`window is not defined`)

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

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

* **New Features**
* Added rich pull-request timeline events for creation, updates, drafts,
pending states, failures, and manual creation.
* Added expandable descriptions with Markdown support, branch details,
links, and status indicators.
* Added safe handling for external links and unrecognized pull-request
output.

* **Bug Fixes**
* Pull-request tool calls now consistently use the specialized display,
including mixed-case names.

* **Tests**
* Added comprehensive coverage for pull-request states, expansion
behavior, link safety, and fallback rendering.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com>
## Summary

- replace the Autofix session HTTP handler factory with a class
- inject `SessionAutofixService` directly through the constructor
- update session composition and handler tests to use the class API
- preserve the existing route adapter, validation, logging, and response
behavior

## Context

This aligns the Autofix endpoint with the class-based session HTTP
handler pattern established in ColeMurray#1612.

## TDD

- changed the handler test to instantiate `AutofixHandler`, confirming
the red state with `AutofixHandler is not a constructor`
- implemented the class and reran the focused test to green

## Validation

- `npm run build -w @open-inspect/shared`
- focused Autofix handler tests: 2 passed
- `npm test -w @open-inspect/control-plane`: 3,253 passed
- `npm run test:integration -w @open-inspect/control-plane`: 1,006
passed
- `npm run typecheck -w @open-inspect/control-plane`
- `npm run lint -w @open-inspect/control-plane`
- targeted Prettier check
- `npm run build -w @open-inspect/control-plane`
- `git diff --check`

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

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

## Summary by CodeRabbit

* **Bug Fixes**
* Maintained autofix request handling, validation, error responses, and
service dispatch behavior.
* Updated internal handler wiring without changing the user-visible
autofix experience.

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

Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Aug 27, 2026

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 171 files, which is 21 over the limit of 150.

To get a review, reduce the PR to 150 files or fewer by splitting it into smaller PRs or changing its base branch.

Upgrade to Pro+ to raise the limit.

This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8b24e475-993b-4008-a785-d574abd338ed

📥 Commits

Reviewing files that changed from the base of the PR and between c5d8412 and c9edfdb.

📒 Files selected for processing (171)
  • .github/workflows/ci-python.yml
  • .github/workflows/ci.yml
  • docs/GETTING_STARTED.md
  • packages/control-plane/package.json
  • packages/control-plane/src/autofix/handler.ts
  • packages/control-plane/src/autofix/queue-consumer.test.ts
  • packages/control-plane/src/autofix/queue-consumer.ts
  • packages/control-plane/src/autofix/queue-health.test.ts
  • packages/control-plane/src/autofix/queue-health.ts
  • packages/control-plane/src/autofix/service.test.ts
  • packages/control-plane/src/autofix/service.ts
  • packages/control-plane/src/automation/repository.ts
  • packages/control-plane/src/cloudflare/background-tasks.test.ts
  • packages/control-plane/src/cloudflare/background-tasks.ts
  • packages/control-plane/src/db/integration-settings.test.ts
  • packages/control-plane/src/db/integration-settings.ts
  • packages/control-plane/src/db/mcp-servers.test.ts
  • packages/control-plane/src/db/mcp-servers.ts
  • packages/control-plane/src/db/pr-autofix-feedback-store.ts
  • packages/control-plane/src/db/session-inbox-store.ts
  • packages/control-plane/src/env-validation.test.ts
  • packages/control-plane/src/env-validation.ts
  • packages/control-plane/src/index.ts
  • packages/control-plane/src/queue-routing.test.ts
  • packages/control-plane/src/queue-routing.ts
  • packages/control-plane/src/repos/default-branch.ts
  • packages/control-plane/src/repos/resolve.ts
  • packages/control-plane/src/router.autofix.test.ts
  • packages/control-plane/src/router.create-session.test.ts
  • packages/control-plane/src/router.ts
  • packages/control-plane/src/routes/autofix.ts
  • packages/control-plane/src/routes/mcp-servers.ts
  • packages/control-plane/src/routes/session-child-spawn.ts
  • packages/control-plane/src/routes/session-index.ts
  • packages/control-plane/src/sandbox/lifecycle/manager.test.ts
  • packages/control-plane/src/sandbox/lifecycle/manager.ts
  • packages/control-plane/src/session/client-command-facade.ts
  • packages/control-plane/src/session/components.ts
  • packages/control-plane/src/session/contracts.ts
  • packages/control-plane/src/session/disconnect-handler.ts
  • packages/control-plane/src/session/http/dispatcher.ts
  • packages/control-plane/src/session/http/handlers/autofix.handler.test.ts
  • packages/control-plane/src/session/http/handlers/autofix.handler.ts
  • packages/control-plane/src/session/http/handlers/child-sessions.handler.test.ts
  • packages/control-plane/src/session/http/handlers/child-sessions.handler.ts
  • packages/control-plane/src/session/http/handlers/messages.handler.test.ts
  • packages/control-plane/src/session/http/handlers/messages.handler.ts
  • packages/control-plane/src/session/http/handlers/participants.handler.test.ts
  • packages/control-plane/src/session/http/handlers/participants.handler.ts
  • packages/control-plane/src/session/http/handlers/pull-request.handler.test.ts
  • packages/control-plane/src/session/http/handlers/pull-request.handler.ts
  • packages/control-plane/src/session/http/handlers/sandbox.handler.test.ts
  • packages/control-plane/src/session/http/handlers/sandbox.handler.ts
  • packages/control-plane/src/session/http/handlers/session-lifecycle.handler.test.ts
  • packages/control-plane/src/session/http/handlers/session-lifecycle.handler.ts
  • packages/control-plane/src/session/http/handlers/ws-token.handler.test.ts
  • packages/control-plane/src/session/http/handlers/ws-token.handler.ts
  • packages/control-plane/src/session/http/routes.test.ts
  • packages/control-plane/src/session/http/routes.ts
  • packages/control-plane/src/session/identity.test.ts
  • packages/control-plane/src/session/identity.ts
  • packages/control-plane/src/session/initialize.ts
  • packages/control-plane/src/session/message-queue.test.ts
  • packages/control-plane/src/session/message-queue.ts
  • packages/control-plane/src/session/message-repository.test.ts
  • packages/control-plane/src/session/message-repository.ts
  • packages/control-plane/src/session/message-router.ts
  • packages/control-plane/src/session/participant-service.test.ts
  • packages/control-plane/src/session/participant-service.ts
  • packages/control-plane/src/session/repository-target.ts
  • packages/control-plane/src/session/sandbox-access-reader.ts
  • packages/control-plane/src/session/sandbox-access.test.ts
  • packages/control-plane/src/session/sandbox-access.ts
  • packages/control-plane/src/session/sandbox-events.test.ts
  • packages/control-plane/src/session/sandbox-events.ts
  • packages/control-plane/src/session/sandbox-lifecycle-adapters.test.ts
  • packages/control-plane/src/session/sandbox-lifecycle-adapters.ts
  • packages/control-plane/src/session/sandbox-repository.test.ts
  • packages/control-plane/src/session/sandbox-repository.ts
  • packages/control-plane/src/session/schema.test.ts
  • packages/control-plane/src/session/schema.ts
  • packages/control-plane/src/session/server.test.ts
  • packages/control-plane/src/session/services/autofix.service.test.ts
  • packages/control-plane/src/session/services/autofix.service.ts
  • packages/control-plane/src/session/services/message.service.test.ts
  • packages/control-plane/src/session/session-core-repository.ts
  • packages/control-plane/src/session/snapshot-reader.ts
  • packages/control-plane/src/session/types.ts
  • packages/control-plane/src/session/user-env-resolver.test.ts
  • packages/control-plane/src/session/user-env-resolver.ts
  • packages/control-plane/src/source-control/providers/github-provider.test.ts
  • packages/control-plane/src/source-control/providers/github-provider.ts
  • packages/control-plane/src/types.ts
  • packages/control-plane/test/integration/auth-sign-in-claim.test.ts
  • packages/control-plane/test/integration/automation-store.test.ts
  • packages/control-plane/test/integration/automations-slack-route.test.ts
  • packages/control-plane/test/integration/browser-auth-callback.test.ts
  • packages/control-plane/test/integration/browser-auth-router.test.ts
  • packages/control-plane/test/integration/browser-auth.test.ts
  • packages/control-plane/test/integration/child-session-ops.test.ts
  • packages/control-plane/test/integration/cleanup.ts
  • packages/control-plane/test/integration/create-pr.test.ts
  • packages/control-plane/test/integration/durable-object-eviction.test.ts
  • packages/control-plane/test/integration/durable-object.test.ts
  • packages/control-plane/test/integration/env.d.ts
  • packages/control-plane/test/integration/google-id-token.ts
  • packages/control-plane/test/integration/helpers.ts
  • packages/control-plane/test/integration/image-build-finalization-store.test.ts
  • packages/control-plane/test/integration/managed-skills.test.ts
  • packages/control-plane/test/integration/pr-autofix-feedback-store.test.ts
  • packages/control-plane/test/integration/provider-account-device-authorizations.test.ts
  • packages/control-plane/test/integration/provider-account-foundation.test.ts
  • packages/control-plane/test/integration/run-helpers.ts
  • packages/control-plane/test/integration/sandbox-events.test.ts
  • packages/control-plane/test/integration/scheduler-events.test.ts
  • packages/control-plane/test/integration/scheduler-slack-events.test.ts
  • packages/control-plane/test/integration/scheduler.test.ts
  • packages/control-plane/test/integration/session-components.test.ts
  • packages/control-plane/test/integration/session-do-access.ts
  • packages/control-plane/test/integration/session-do-collaborator-wiring.test.ts
  • packages/control-plane/test/integration/session-inbox.test.ts
  • packages/control-plane/test/integration/session-lifecycle-alarm-recovery.test.ts
  • packages/control-plane/test/integration/session-lifecycle.test.ts
  • packages/control-plane/test/integration/session-pull-requests.test.ts
  • packages/control-plane/test/integration/session-read-state.test.ts
  • packages/control-plane/test/integration/session-snapshot.test.ts
  • packages/control-plane/test/integration/slack-channel-store.test.ts
  • packages/control-plane/test/integration/spawn-children.test.ts
  • packages/control-plane/test/integration/tsconfig.json
  • packages/control-plane/test/integration/webhooks-slack.test.ts
  • packages/control-plane/test/integration/webhooks.test.ts
  • packages/control-plane/test/integration/websocket-sandbox.test.ts
  • packages/control-plane/tsconfig.test.json
  • packages/control-plane/vitest.integration.config.ts
  • packages/e2b-infra/build-template.py
  • packages/e2b-infra/e2b.Dockerfile
  • packages/github-bot/README.md
  • packages/github-bot/src/autofix-ingress.ts
  • packages/github-bot/src/github-mention.ts
  • packages/github-bot/src/handlers.ts
  • packages/github-bot/src/index.ts
  • packages/github-bot/src/types.ts
  • packages/github-bot/test/autofix-ingress.test.ts
  • packages/github-bot/test/handlers.test.ts
  • packages/github-bot/test/webhook.test.ts
  • packages/sandbox-runtime/src/sandbox_runtime/plugins/inspect-plugin.js
  • packages/sandbox-runtime/tests/test_repository_target.py
  • packages/shared/package.json
  • packages/shared/src/index.ts
  • packages/shared/src/public-api.test.ts
  • packages/shared/src/pull-request-tool.test.ts
  • packages/shared/src/pull-request-tool.ts
  • packages/shared/src/types/github-autofix.ts
  • packages/shared/src/types/index.ts
  • packages/shared/src/types/integrations.ts
  • packages/shared/src/types/sandbox-events.ts
  • packages/web/src/components/create-pull-request-event.test.tsx
  • packages/web/src/components/create-pull-request-event.tsx
  • packages/web/src/components/session-timeline.test.tsx
  • packages/web/src/components/session-timeline.tsx
  • packages/web/src/components/settings/integrations/github-autofix-settings-fields.tsx
  • packages/web/src/components/settings/integrations/github-global-settings-section.tsx
  • packages/web/src/components/settings/integrations/github-integration-settings.test.tsx
  • packages/web/src/components/settings/integrations/github-integration-settings.tsx
  • packages/web/src/components/settings/integrations/github-repo-overrides-section.tsx
  • packages/web/src/components/settings/integrations/integration-settings-section.tsx
  • packages/web/src/components/tool-call-item.test.tsx
  • packages/web/src/components/tool-call-item.tsx
  • terraform/d1/migrations/0070_pr_autofix_feedback.sql
  • terraform/environments/production/workers-control-plane.tf
  • terraform/environments/production/workers-github.tf

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


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

@github-actions

Copy link
Copy Markdown

Terraform Validation Results

Step Status
Format
Init
Validate
Tests

Pushed by: @NicolasWalter, Action: pull_request

@github-actions

Copy link
Copy Markdown

Terraform Plan Results

Status: ✅ Success

Show Plan
terraform_data.access_control_gate: Refreshing state... [id=841ab6bc-98a7-018c-1031-ebad4f8b62bc]
terraform_data.sign_in_provider_gate: Refreshing state... [id=7f4a67d1-6978-0b23-899d-c2a9004643bd]
terraform_data.cloudflare_custom_domain_gate: Refreshing state... [id=fa456fac-6c14-16e4-a484-3338d1a3718d]
local_file.web_app_wrangler_production[0]: Refreshing state... [id=71b0d3758fc7d34e75fd9a3abe59e2c1b6dadeea]
data.external.modal_source_hash[0]: Reading...
random_password.service_auth_secret_linear_bot: Refreshing state... [id=none]
random_password.service_auth_secret_github_bot: Refreshing state... [id=none]
random_password.service_auth_secret_slack_bot: Refreshing state... [id=none]
random_password.service_auth_secret_web: Refreshing state... [id=none]
random_bytes.provider_accounts_encryption_key: Refreshing state...
module.linear_kv[0].cloudflare_workers_kv_namespace.this: Refreshing state... [id=d003f1ad81384910a1f48a0a33f18c09]
random_password.image_callback_token_pepper: Refreshing state... [id=none]
module.github_kv[0].cloudflare_workers_kv_namespace.this: Refreshing state... [id=87dbfaa1d9ce4a37a42e04c57c434a72]
null_resource.linear_bot_build[0]: Refreshing state... [id=956366907826137814]
null_resource.web_app_cloudflare_build[0]: Refreshing state... [id=5024517857365059396]
cloudflare_queue.image_build_finalization: Refreshing state... [id=1ca823a150c54578a9ad1814325147a3]
cloudflare_r2_bucket.media: Refreshing state... [id=open-inspect-media-primo]
cloudflare_queue.image_build_finalization_dlq: Refreshing state... [id=cbbc2d8794c04396a550996e7f0cc129]
null_resource.control_plane_build: Refreshing state... [id=9105849033611783886]
module.modal_app[0].null_resource.modal_secrets[0]: Refreshing state... [id=8757687985279342629]
cloudflare_queue.slack_completion_delivery[0]: Refreshing state... [id=56ef0f3e13bd46a3a39f30c79ec547fa]
null_resource.github_bot_build[0]: Refreshing state... [id=8271231830927734747]
cloudflare_queue.slack_completion_delivery_dlq[0]: Refreshing state... [id=06ce03d2663f4aea937b0c0c1c379c17]
module.slack_kv[0].cloudflare_workers_kv_namespace.this: Refreshing state... [id=729b357dbb5e4c9d99ec9212cc45766e]
module.session_index_kv.cloudflare_workers_kv_namespace.this: Refreshing state... [id=7f18644fbed34121bbe3a196f373ea93]
data.external.modal_source_hash[0]: Read complete after 0s [id=-]
cloudflare_d1_database.main: Refreshing state... [id=dba95b03-ace9-47a8-81e9-6e39d8d694c5]
null_resource.slack_bot_build[0]: Refreshing state... [id=5379137651876318417]
module.modal_app[0].null_resource.modal_deploy: Refreshing state... [id=5768192879312552892]
module.linear_bot_worker[0].cloudflare_worker.this: Refreshing state... [id=33782d80e8ff4af9b30b92870084b674]
module.slack_bot_worker[0].cloudflare_worker.this: Refreshing state... [id=5200e96d69804ea296e1f3a6b39e4243]
module.slack_bot_worker[0].cloudflare_worker_version.this: Refreshing state... [id=e87b85ce-d5a2-4b3a-8fbe-23e6200e98fe]
null_resource.d1_migrations: Refreshing state... [id=5980374278680462129]
module.linear_bot_worker[0].cloudflare_worker_version.this: Refreshing state... [id=a55bcc0f-a65c-41a5-8441-05d7b4af3966]
module.slack_bot_worker[0].cloudflare_workers_deployment.this: Refreshing state... [id=ba95e23c-c5c2-40d9-a17e-823adba5df2a]
module.linear_bot_worker[0].cloudflare_workers_deployment.this: Refreshing state... [id=6986a9e1-4d49-41fa-a780-f4ad5e481b34]
cloudflare_queue_consumer.slack_completion_delivery[0]: Refreshing state...
module.control_plane_worker.cloudflare_worker.this: Refreshing state... [id=c208a60c393e45e38eb502346bb7ce1e]
module.control_plane_worker.cloudflare_worker_version.this: Refreshing state... [id=e480d777-f058-4246-86ef-dc36a6fa28fe]
module.control_plane_worker.cloudflare_workers_deployment.this: Refreshing state... [id=6edc103c-bb0f-471e-8224-9f17597d658a]
module.control_plane_worker.cloudflare_workers_cron_trigger.this[0]: Refreshing state... [id=open-inspect-control-plane-primo]
cloudflare_queue_consumer.image_build_finalization: Refreshing state...
module.github_bot_worker[0].cloudflare_worker.this: Refreshing state... [id=4b5e2696491a41eaaa124f4e2a9855f2]
null_resource.web_app_cloudflare_deploy[0]: Refreshing state... [id=1320732786606345683]
null_resource.web_app_cloudflare_secrets[0]: Refreshing state... [id=8867783181576424643]
module.github_bot_worker[0].cloudflare_worker_version.this: Refreshing state... [id=21ef4d5d-ec56-47a1-8646-dbfcd4af510b]
module.github_bot_worker[0].cloudflare_workers_deployment.this: Refreshing state... [id=0a0e290a-7d4b-42f1-a5e0-49385187b3c5]

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

Terraform will perform the following actions:

  # cloudflare_queue.github_autofix[0] will be created
  + resource "cloudflare_queue" "github_autofix" {
      + account_id            = "bf66240843ed90d19b82e4b90916d29a"
      + consumers             = (known after apply)
      + consumers_total_count = (known after apply)
      + created_on            = (known after apply)
      + id                    = (known after apply)
      + modified_on           = (known after apply)
      + producers             = (known after apply)
      + producers_total_count = (known after apply)
      + queue_id              = (known after apply)
      + queue_name            = "open-inspect-github-autofix-primo"
      + settings              = (known after apply)
    }

  # cloudflare_queue.github_autofix_dlq[0] will be created
  + resource "cloudflare_queue" "github_autofix_dlq" {
      + account_id            = "bf66240843ed90d19b82e4b90916d29a"
      + consumers             = (known after apply)
      + consumers_total_count = (known after apply)
      + created_on            = (known after apply)
      + id                    = (known after apply)
      + modified_on           = (known after apply)
      + producers             = (known after apply)
      + producers_total_count = (known after apply)
      + queue_id              = (known after apply)
      + queue_name            = "open-inspect-github-autofix-dlq-primo"
      + settings              = (known after apply)
    }

  # cloudflare_queue_consumer.github_autofix[0] will be created
  + resource "cloudflare_queue_consumer" "github_autofix" {
      + account_id        = "bf66240843ed90d19b82e4b90916d29a"
      + consumer_id       = (known after apply)
      + created_on        = (known after apply)
      + dead_letter_queue = "open-inspect-github-autofix-dlq-primo"
      + queue_id          = (known after apply)
      + queue_name        = (known after apply)
      + script_name       = "open-inspect-control-plane-primo"
      + settings          = {
          + batch_size            = 1
          + max_concurrency       = 5
          + max_retries           = 4
          + max_wait_time_ms      = 1000
          + retry_delay           = 30
          + visibility_timeout_ms = (known after apply)
        }
      + type              = "worker"
    }

  # local_file.web_app_wrangler_production[0] will be created
  + resource "local_file" "web_app_wrangler_production" {
      + content              = <<-EOT
            name = "open-inspect-web-primo"
            main = ".open-next/worker.js"
            compatibility_date = "2025-08-15"
            compatibility_flags = ["nodejs_compat", "global_fetch_strictly_public"]
            
            # A custom-domain deployment has one canonical browser origin.
            workers_dev = true
            
            [vars]
            CONTROL_PLANE_URL = "https://open-inspect-control-plane-primo.primo-bf6.workers.dev"
            NEXT_PUBLIC_WS_URL = "wss://open-inspect-control-plane-primo.primo-bf6.workers.dev"
            NEXT_PUBLIC_SANDBOX_PROVIDER = "modal"
            NEXT_PUBLIC_APP_NAME = "Primo"
            NEXT_PUBLIC_APP_ICON_URL = ""
            
            [assets]
            directory = ".open-next/assets"
            binding = "ASSETS"
            
            [[services]]
            binding = "CONTROL_PLANE_WORKER"
            service = "open-inspect-control-plane-primo"
        EOT
      + 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       = "9105849033611783886" -> (known after apply)
      ~ triggers = { # forces replacement
          ~ "always_run" = "2026-08-26T12:04:31Z" -> (known after apply)
        }
    }

  # null_resource.d1_migrations must be replaced
-/+ resource "null_resource" "d1_migrations" {
      ~ id       = "5980374278680462129" -> (known after apply)
      ~ triggers = { # forces replacement
          ~ "migrations_sha" = "177eee0e2901d7ed13c268ff3734192a648a7c7ed0f08db7d5568f5277847087" -> "2634877bac226aff76346d1db31648d3a4d14db4827981a6e00663b4502b07af"
            # (1 unchanged element hidden)
        }
    }

  # null_resource.github_bot_build[0] must be replaced
-/+ resource "null_resource" "github_bot_build" {
      ~ id       = "8271231830927734747" -> (known after apply)
      ~ triggers = { # forces replacement
          ~ "always_run" = "2026-08-26T12:04:31Z" -> (known after apply)
        }
    }

  # null_resource.linear_bot_build[0] must be replaced
-/+ resource "null_resource" "linear_bot_build" {
      ~ id       = "956366907826137814" -> (known after apply)
      ~ triggers = { # forces replacement
          ~ "always_run" = "2026-08-26T12:04:31Z" -> (known after apply)
        }
    }

  # null_resource.slack_bot_build[0] must be replaced
-/+ resource "null_resource" "slack_bot_build" {
      ~ id       = "5379137651876318417" -> (known after apply)
      ~ triggers = { # forces replacement
          ~ "always_run" = "2026-08-26T12:04:31Z" -> (known after apply)
        }
    }

  # null_resource.web_app_cloudflare_build[0] must be replaced
-/+ resource "null_resource" "web_app_cloudflare_build" {
      ~ id       = "5024517857365059396" -> (known after apply)
      ~ triggers = { # forces replacement
          ~ "always_run" = "2026-08-26T12:04:31Z" -> (known after apply)
        }
    }

  # null_resource.web_app_cloudflare_deploy[0] must be replaced
-/+ resource "null_resource" "web_app_cloudflare_deploy" {
      ~ id       = "1320732786606345683" -> (known after apply)
      ~ triggers = { # forces replacement
          ~ "always_run" = "2026-08-26T12:05:09Z" -> (known after apply)
        }
    }

  # module.control_plane_worker.cloudflare_worker.this will be updated in-place
  ~ resource "cloudflare_worker" "this" {
        id             = "c208a60c393e45e38eb502346bb7ce1e"
        name           = "open-inspect-control-plane-primo"
      ~ observability  = {
          ~ logs               = {
              + destinations       = (known after apply)
                # (4 unchanged attributes hidden)
            }
          ~ traces             = {
              + destinations       = (known after apply)
                # (3 unchanged attributes hidden)
            }
            # (2 unchanged attributes hidden)
        }
      ~ references     = {
          ~ dispatch_namespace_outbounds = [] -> (known after apply)
          ~ domains                      = [] -> (known after apply)
          ~ durable_objects              = [
              - {
                  - namespace_id   = "4c77239db3614a6aac69a90e1fbd8955" -> null
                  - namespace_name = "open-inspect-control-plane-primo_SessionDO" -> null
                  - worker_id      = "c208a60c393e45e38eb502346bb7ce1e" -> null
                  - worker_name    = "open-inspect-control-plane-primo" -> null
                },
            ] -> (known after apply)
          ~ queues                       = [
              - {
                  - queue_consumer_id = "648d35d2a8064e8ea79899e946a65334" -> null
                  - queue_id          = "1ca823a150c54578a9ad1814325147a3" -> null
                  - queue_name        = "open-inspect-image-build-finalization-primo" -> null
                },
            ] -> (known after apply)
          ~ workers                      = [
              - {
                  - id   = "ac07332f8b0f4cdfa4ca04f966a9fa61" -> null
                  - name = "open-inspect-web-primo" -> null
                },
              - {
                  - id   = "4b5e2696491a41eaaa124f4e2a9855f2" -> null
                  - name = "open-inspect-github-bot-primo" -> null
                },
              - {
                  - id   = "33782d80e8ff4af9b30b92870084b674" -> null
                  - name = "open-inspect-linear-bot-primo" -> null
                },
              - {
                  - id   = "5200e96d69804ea296e1f3a6b39e4243" -> null
                  - name = "open-inspect-slack-bot-primo" -> null
                },
            ] -> (known after apply)
        } -> (known after apply)
        tags           = []
      ~ updated_on     = "2026-08-26T12:04:33Z" -> (known after apply)
        # (6 unchanged attributes hidden)
    }

  # module.control_plane_worker.cloudflare_worker_version.this must be replaced
-/+ resource "cloudflare_worker_version" "this" {
      ~ annotations         = {
          + workers_message      = (known after apply)
          + workers_tag          = (known after apply)
          ~ workers_triggered_by = "create_version_api" -> (known after apply)
        } -> (known after apply)
      ~ bindings            = (sensitive value) # forces replacement
      ~ created_on          = "2026-08-26T12:04:38Z" -> (known after apply)
      ~ id                  = "e480d777-f058-4246-86ef-dc36a6fa28fe" -> (known after apply)
      + limits              = (known after apply)
      + main_script_base64  = (known after apply)
      ~ migration_tag       = "v1" -> (known after apply)
      ~ modules             = [
          - { # forces replacement
              - content_file   = "../../..//packages/control-plane/dist/index.js" -> null
              - content_sha256 = "4ac20254b2f6c06558a76dfc57261274427cf88b501a0f3645025100bee072e6" -> null
              - content_type   = "application/javascript+module" -> null
              - name           = "index.js" -> null
            },
          + { # forces replacement
              + content_file   = "../../..//packages/control-plane/dist/index.js"
              + content_sha256 = "325774ffe71e2979508b9e6a9bb468842fb7d936b1909ec287a1dda27739726b"
              + content_type   = "application/javascript+module"
              + name           = "index.js"
            },
        ]
      ~ number              = 65 -> (known after apply)
      ~ source              = "terraform" -> (known after apply)
      ~ startup_time_ms     = 124 -> (known after apply)
      ~ urls                = [] -> (known after apply)
        # (6 unchanged attributes hidden)
    }

  # module.control_plane_worker.cloudflare_workers_deployment.this must be replaced
-/+ resource "cloudflare_workers_deployment" "this" {
      ~ annotations  = {
          + workers_message      = (known after apply)
          ~ workers_triggered_by = "deployment" -> (known after apply)
        } -> (known after apply)
      ~ author_email = "nicolas@primo.la" -> (known after apply)
      ~ created_on   = "2026-08-26T12:04:40Z" -> (known after apply)
      ~ id           = "6edc103c-bb0f-471e-8224-9f17597d658a" -> (known after apply)
      ~ source       = "terraform" -> (known after apply)
      ~ versions     = [ # forces replacement
          ~ {
              ~ version_id = "e480d777-f058-4246-86ef-dc36a6fa28fe" -> (known after apply)
                # (1 unchanged attribute hidden)
            },
        ]
        # (3 unchanged attributes hidden)
    }

  # module.github_bot_worker[0].cloudflare_worker.this will be updated in-place
  ~ resource "cloudflare_worker" "this" {
        id             = "4b5e2696491a41eaaa124f4e2a9855f2"
        name           = "open-inspect-github-bot-primo"
      ~ observability  = {
          ~ logs               = {
              + destinations       = (known after apply)
                # (4 unchanged attributes hidden)
            }
          ~ traces             = {
              + destinations       = (known after apply)
                # (3 unchanged attributes hidden)
            }
            # (2 unchanged attributes hidden)
        }
      ~ references     = {
          ~ dispatch_namespace_outbounds = [] -> (known after apply)
          ~ domains                      = [] -> (known after apply)
          ~ durable_objects              = [] -> (known after apply)
          ~ queues                       = [] -> (known after apply)
          ~ workers                      = [] -> (known after apply)
        } -> (known after apply)
        tags           = []
      ~ updated_on     = "2026-08-26T12:04:40Z" -> (known after apply)
        # (6 unchanged attributes hidden)
    }

  # module.github_bot_worker[0].cloudflare_worker_version.this must be replaced
-/+ resource "cloudflare_worker_version" "this" {
      ~ annotations         = {
          + workers_message      = (known after apply)
          + workers_tag          = (known after apply)
          ~ workers_triggered_by = "create_version_api" -> (known after apply)
        } -> (known after apply)
      ~ bindings            = (sensitive value) # forces replacement
      ~ created_on          = "2026-08-26T12:04:41Z" -> (known after apply)
      ~ id                  = "21ef4d5d-ec56-47a1-8646-dbfcd4af510b" -> (known after apply)
      + limits              = (known after apply)
      + main_script_base64  = (known after apply)
      + migration_tag       = (known after apply)
      ~ modules             = [
          - { # forces replacement
              - content_file   = "../../..//packages/github-bot/dist/index.js" -> null
              - content_sha256 = "54510ead747ee6d78a3d7db31ac2cd9ffeeafb439c037df965ee7c51ccdeda05" -> null
              - content_type   = "application/javascript+module" -> null
              - name           = "index.js" -> null
            },
          + { # forces replacement
              + content_file   = "../../..//packages/github-bot/dist/index.js"
              + content_sha256 = "fe516287d7787ba968ebf59ebf849ad36a1f36a7651f26e6602050f7c9f88ce0"
              + content_type   = "application/javascript+module"
              + name           = "index.js"
            },
        ]
      ~ number              = 49 -> (known after apply)
      ~ source              = "terraform" -> (known after apply)
      ~ startup_time_ms     = 35 -> (known after apply)
      ~ urls                = [
          - "https://21ef4d5d-open-inspect-github-bot-primo.primo-bf6.workers.dev",
        ] -> (known after apply)
        # (6 unchanged attributes hidden)
    }

  # module.github_bot_worker[0].cloudflare_workers_deployment.this must be replaced
-/+ resource "cloudflare_workers_deployment" "this" {
      ~ annotations  = {
          + workers_message      = (known after apply)
          ~ workers_triggered_by = "deployment" -> (known after apply)
        } -> (known after apply)
      ~ author_email = "nicolas@primo.la" -> (known after apply)
      ~ created_on   = "2026-08-26T12:04:41Z" -> (known after apply)
      ~ id           = "0a0e290a-7d4b-42f1-a5e0-49385187b3c5" -> (known after apply)
      ~ source       = "terraform" -> (known after apply)
      ~ versions     = [ # forces replacement
          ~ {
              ~ version_id = "21ef4d5d-ec56-47a1-8646-dbfcd4af510b" -> (known after apply)
                # (1 unchanged attribute hidden)
            },
        ]
        # (3 unchanged attributes hidden)
    }

  # module.linear_bot_worker[0].cloudflare_worker.this will be updated in-place
  ~ resource "cloudflare_worker" "this" {
        id             = "33782d80e8ff4af9b30b92870084b674"
        name           = "open-inspect-linear-bot-primo"
      ~ observability  = {
          ~ logs               = {
              + destinations       = (known after apply)
                # (4 unchanged attributes hidden)
            }
          ~ traces             = {
              + destinations       = (known after apply)
                # (3 unchanged attributes hidden)
            }
            # (2 unchanged attributes hidden)
        }
      ~ references     = {
          ~ dispatch_namespace_outbounds = [] -> (known after apply)
          ~ domains                      = [] -> (known after apply)
          ~ durable_objects              = [] -> (known after apply)
          ~ queues                       = [] -> (known after apply)
          ~ workers                      = [
              - {
                  - id   = "c208a60c393e45e38eb502346bb7ce1e" -> null
                  - name = "open-inspect-control-plane-primo" -> null
                },
            ] -> (known after apply)
        } -> (known after apply)
        tags           = []
      ~ updated_on     = "2026-08-26T12:04:32Z" -> (known after apply)
        # (6 unchanged attributes hidden)
    }

  # module.linear_bot_worker[0].cloudflare_worker_version.this must be replaced
-/+ resource "cloudflare_worker_version" "this" {
      ~ annotations         = {
          + workers_message      = (known after apply)
          + workers_tag          = (known after apply)
          ~ workers_triggered_by = "create_version_api" -> (known after apply)
        } -> (known after apply)
      ~ bindings            = (sensitive value) # forces replacement
      ~ created_on          = "2026-08-26T12:04:32Z" -> (known after apply)
      ~ id                  = "a55bcc0f-a65c-41a5-8441-05d7b4af3966" -> (known after apply)
      + limits              = (known after apply)
      + main_script_base64  = (known after apply)
      + migration_tag       = (known after apply)
      ~ modules             = [
          - { # forces replacement
              - content_file   = "../../..//packages/linear-bot/dist/index.js" -> null
              - content_sha256 = "cdd5ab4993778450482ea7956889b7ffd9de4c45960b12976b384f16b5b539bf" -> null
              - content_type   = "application/javascript+module" -> null
              - name           = "index.js" -> null
            },
          + { # forces replacement
              + content_file   = "../../..//packages/linear-bot/dist/index.js"
              + content_sha256 = "df0e0f4d432e9a36e6cff1a54c70f897d25fe49f93166e076c6fbdb5265c8838"
              + content_type   = "application/javascript+module"
              + name           = "index.js"
            },
        ]
      ~ number              = 68 -> (known after apply)
      ~ source              = "terraform" -> (known after apply)
      ~ startup_time_ms     = 42 -> (known after apply)
      ~ urls                = [
          - "https://a55bcc0f-open-inspect-linear-bot-primo.primo-bf6.workers.dev",
        ] -> (known after apply)
        # (6 unchanged attributes hidden)
    }

  # module.linear_bot_worker[0].cloudflare_workers_deployment.this must be replaced
-/+ resource "cloudflare_workers_deployment" "this" {
      ~ annotations  = {
          + workers_message      = (known after apply)
          ~ workers_triggered_by = "deployment" -> (known after apply)
        } -> (known after apply)
      ~ author_email = "nicolas@primo.la" -> (known after apply)
      ~ created_on   = "2026-08-26T12:04:33Z" -> (known after apply)
      ~ id           = "6986a9e1-4d49-41fa-a780-f4ad5e481b34" -> (known after apply)
      ~ source       = "terraform" -> (known after apply)
      ~ versions     = [ # forces replacement
          ~ {
              ~ version_id = "a55bcc0f-a65c-41a5-8441-05d7b4af3966" -> (known after apply)
                # (1 unchanged attribute hidden)
            },
        ]
        # (3 unchanged attributes hidden)
    }

  # module.modal_app[0].null_resource.modal_deploy must be replaced
-/+ resource "null_resource" "modal_deploy" {
      ~ id       = "5768192879312552892" -> (known after apply)
      ~ triggers = { # forces replacement
          ~ "source_hash"       = "7dea31102eed27234e88e6b28e35595256d20715e6ae85a373d2dfd7edb707e3" -> "c0fb145fe5171850bde6c89627f35e72ca291ee651794422d089699ef9a96054"
            # (3 unchanged elements hidden)
        }
    }

  # module.slack_bot_worker[0].cloudflare_worker.this will be updated in-place
  ~ resource "cloudflare_worker" "this" {
        id             = "5200e96d69804ea296e1f3a6b39e4243"
        name           = "open-inspect-slack-bot-primo"
      ~ observability  = {
          ~ logs               = {
              + destinations       = (known after apply)
                # (4 unchanged attributes hidden)
            }
          ~ traces             = {
              + destinations       = (known after apply)
                # (3 unchanged attributes hidden)
            }
            # (2 unchanged attributes hidden)
        }
      ~ references     = {
          ~ dispatch_namespace_outbounds = [] -> (known after apply)
          ~ domains                      = [] -> (known after apply)
          ~ durable_objects              = [] -> (known after apply)
          ~ queues                       = [
              - {
                  - queue_consumer_id = "a755a290fd92417fb11c298f9c1d1f40" -> null
                  - queue_id          = "56ef0f3e13bd46a3a39f30c79ec547fa" -> null
                  - queue_name        = "open-inspect-slack-completion-primo" -> null
                },
            ] -> (known after apply)
          ~ workers                      = [
              - {
                  - id   = "c208a60c393e45e38eb502346bb7ce1e" -> null
                  - name = "open-inspect-control-plane-primo" -> null
                },
            ] -> (known after apply)
        } -> (known after apply)
        tags           = []
      ~ updated_on     = "2026-08-26T12:04:31Z" -> (known after apply)
        # (6 unchanged attributes hidden)
    }

  # module.slack_bot_worker[0].cloudflare_worker_version.this must be replaced
-/+ resource "cloudflare_worker_version" "this" {
      ~ annotations         = {
          + workers_message      = (known after apply)
          + workers_tag          = (known after apply)
          ~ workers_triggered_by = "create_version_api" -> (known after apply)
        } -> (known after apply)
      ~ bindings            = (sensitive value) # forces replacement
      ~ created_on          = "2026-08-26T12:04:32Z" -> (known after apply)
      ~ id                  = "e87b85ce-d5a2-4b3a-8fbe-23e6200e98fe" -> (known after apply)
      + limits              = (known after apply)
      + main_script_base64  = (known after apply)
      + migration_tag       = (known after apply)
      ~ modules             = [
          - { # forces replacement
              - content_file   = "../../..//packages/slack-bot/dist/index.js" -> null
              - content_sha256 = "6911837e8156b867d1069efd3fbac032f460149d1226b260fddb6d37d5233cee" -> null
              - content_type   = "application/javascript+module" -> null
              - name           = "index.js" -> null
            },
          + { # forces replacement
              + content_file   = "../../..//packages/slack-bot/dist/index.js"
              + content_sha256 = "f688ec7f11644a7fa157e0f0db8e99ded504de49f25944e5b4bb908637d6dd68"
              + content_type   = "application/javascript+module"
              + name           = "index.js"
            },
        ]
      ~ number              = 71 -> (known after apply)
      ~ source              = "terraform" -> (known after apply)
      ~ startup_time_ms     = 99 -> (known after apply)
      ~ urls                = [
          - "https://e87b85ce-open-inspect-slack-bot-primo.primo-bf6.workers.dev",
        ] -> (known after apply)
        # (6 unchanged attributes hidden)
    }

  # module.slack_bot_worker[0].cloudflare_workers_deployment.this must be replaced
-/+ resource "cloudflare_workers_deployment" "this" {
      ~ annotations  = {
          + workers_message      = (known after apply)
          ~ workers_triggered_by = "deployment" -> (known after apply)
        } -> (known after apply)
      ~ author_email = "nicolas@primo.la" -> (known after apply)
      ~ created_on   = "2026-08-26T12:04:33Z" -> (known after apply)
      ~ id           = "ba95e23c-c5c2-40d9-a17e-823adba5df2a" -> (known after apply)
      ~ source       = "terraform" -> (known after apply)
      ~ versions     = [ # forces replacement
          ~ {
              ~ version_id = "e87b85ce-d5a2-4b3a-8fbe-23e6200e98fe" -> (known after apply)
                # (1 unchanged attribute hidden)
            },
        ]
        # (3 unchanged attributes hidden)
    }

Plan: 20 to add, 4 to change, 16 to destroy.

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

Saved the plan to: tfplan

To perform exactly these actions, run the following command to apply:
    terraform apply "tfplan"

Pushed by: @NicolasWalter

@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: c9edfdbc71

ℹ️ 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".

if (autofixEnvelope) {
try {
await c.env.AUTOFIX_QUEUE.send(autofixEnvelope);
} catch (err) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Retry the webhook when Autofix enqueueing fails

When AUTOFIX_QUEUE.send() rejects during a transient queue outage, this catch only logs the error; processing continues, the webhook returns 200, and the asynchronous handler marks the delivery as processed in the dedupe cache. GitHub therefore will not redeliver the event, permanently dropping Autofix feedback. Clear the processing dedupe entry and return a non-success response (or otherwise durably retry the enqueue) instead of swallowing this failure.

Useful? React with 👍 / 👎.

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.

3 participants