Skip to content

chore: sync with upstream 2026-08-05 - #95

Merged
NicolasWalter merged 9 commits into
mainfrom
sync/upstream-2026-08-05
Aug 5, 2026
Merged

chore: sync with upstream 2026-08-05#95
NicolasWalter merged 9 commits into
mainfrom
sync/upstream-2026-08-05

Conversation

@NicolasWalter

@NicolasWalter NicolasWalter commented Aug 5, 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

    • Added support for processing Sentry issue webhook and metric alert events with clearer event details.
    • Session creator filters now persist across visits and safely recover from invalid or unavailable browser storage.
  • Bug Fixes

    • Improved repository loading during cache refreshes and client disconnects.
    • Prevented lifecycle-only or empty task activity sections from appearing in session timelines.
    • Added safer handling and clearer logging for unsupported or invalid Sentry webhook payloads.

ColeMurray and others added 9 commits August 3, 2026 23:13
## Summary

- normalize current Sentry issue.created custom-integration webhooks
separately from legacy event alerts
- route normalization using the Sentry-Hook-Resource header while
preserving legacy event_alert and metric_alert behavior
- emit a structured sentry.webhook_skipped warning with safe diagnostic
metadata when normalization rejects a signed delivery
- add unit and integration coverage proving issue.created deliveries
create automation runs and skipped payload contents are not logged

## Root cause

The webhook handler acknowledged unsupported payloads with HTTP 200 and
skipped=true. Its only issue schema required alert-only data.event and
data.triggered_rule fields, so current issue.created deliveries were
rejected before SchedulerDO and no invocation, run, or session was
created.

## Impact

Current Sentry issue.created webhooks now reach SchedulerDO and launch
their configured automation. Future normalization skips are visible in
Worker logs through the sentry.webhook_skipped event without logging
webhook payloads or secrets.

## Validation

- shared unit tests: 531 passed
- control-plane unit tests: 2,254 passed
- focused webhook integration tests: 16 passed
- full control-plane integration suite passed
- shared and control-plane typechecks passed
- ESLint, Prettier, and git diff checks passed

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

- **New Features**
- Added support for current Sentry `issue.created` webhooks, including
issue details and context in automation runs.
- Sentry webhook types are now identified using resource headers,
supporting issue, event-alert, and metric-alert payloads.
- **Bug Fixes**
- Invalid, unsupported, or mismatched payloads are safely skipped with
clear classifications.
- Improved structured logging for skipped and processed webhook events
without exposing sensitive payload data.
- Preserved compatibility with legacy Sentry event-alert and
metric-alert integrations.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary

- accept string and numeric identifiers in Sentry metric alert webhooks
- normalize both metric alert and alert rule identifiers to strings at
the schema boundary
- verify trigger, concurrency, and metadata keys use the canonical
parsed alert rule identifier

## Context

Follow-up to ColeMurray#1264. Sentry's documented metric alert payload represents
both `metric_alert.id` and `metric_alert.alert_rule.id` as strings,
while the merged schema only accepted numbers.

Documentation:
https://docs.sentry.io/organization/integrations/integration-platform/webhooks/metric-alerts/

## Validation

- `npm test -w @open-inspect/shared` (39 files, 533 tests)
- `npm run typecheck -w @open-inspect/shared`
- `npm run build -w @open-inspect/shared`
- `npm run test:integration -w @open-inspect/control-plane --
test/integration/webhooks.test.ts` (18 tests)
- `npm run typecheck -w @open-inspect/control-plane`
- `npm run format:check`
- focused ESLint and `git diff --check`


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

## Summary by CodeRabbit

* **Bug Fixes**
* Improved Sentry metric alert handling for identifiers provided as
either text or numbers.
* Standardized alert rule identifiers to consistent string values in
normalized metadata.
* **Tests**
* Expanded coverage to verify normalization across both supported
identifier formats.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
…rn group across 1 directory (ColeMurray#1268)

Bumps the npm_and_yarn group with 1 update in the / directory:
[postcss](https://github.com/postcss/postcss).

Updates `postcss` from 8.5.18 to 8.5.23
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/postcss/postcss/releases">postcss's
releases</a>.</em></p>
<blockquote>
<h2>8.5.23</h2>
<ul>
<li>Do not load source map without <code>opts.from</code> for security
reasons.</li>
</ul>
<h2>8.5.22</h2>
<ul>
<li>Fixed custom property losing semicolon before a comment (by <a
href="https://github.com/sarathfrancis90"><code>@​sarathfrancis90</code></a>).</li>
</ul>
<h2>8.5.21</h2>
<ul>
<li>Fixed childless at-rule losing semicolon before comment (by <a
href="https://github.com/sarathfrancis90"><code>@​sarathfrancis90</code></a>).</li>
<li>Fixed docs (by <a
href="https://github.com/isker"><code>@​isker</code></a>).</li>
</ul>
<h2>8.5.20</h2>
<ul>
<li>Fixed missing space if <code>AtRule#params</code> is set after (by
<a
href="https://github.com/sarathfrancis90"><code>@​sarathfrancis90</code></a>).</li>
<li>Fixed mixing AST error on warnings (by <a
href="https://github.com/MahinAnowar"><code>@​MahinAnowar</code></a>).</li>
</ul>
<h2>8.5.19</h2>
<ul>
<li>Fixed cleaning <code>before</code> for new nodes inserted to
<code>Root</code> (by <a
href="https://github.com/MahinAnowar"><code>@​MahinAnowar</code></a>).</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/postcss/postcss/blob/main/CHANGELOG.md">postcss's
changelog</a>.</em></p>
<blockquote>
<h2>8.5.23</h2>
<ul>
<li>Do not load source map without <code>opts.from</code> for security
reasons.</li>
</ul>
<h2>8.5.22</h2>
<ul>
<li>Fixed custom property losing semicolon before a comment (by <a
href="https://github.com/sarathfrancis90"><code>@​sarathfrancis90</code></a>).</li>
</ul>
<h2>8.5.21</h2>
<ul>
<li>Fixed childless at-rule losing semicolon before comment (by <a
href="https://github.com/sarathfrancis90"><code>@​sarathfrancis90</code></a>).</li>
<li>Fixed docs (by <a
href="https://github.com/isker"><code>@​isker</code></a>).</li>
</ul>
<h2>8.5.20</h2>
<ul>
<li>Fixed missing space if <code>AtRule#params</code> is set after (by
<a
href="https://github.com/sarathfrancis90"><code>@​sarathfrancis90</code></a>).</li>
<li>Fixed mixing AST error on warnings (by <a
href="https://github.com/MahinAnowar"><code>@​MahinAnowar</code></a>).</li>
</ul>
<h2>8.5.19</h2>
<ul>
<li>Fixed cleaning <code>before</code> for new nodes inserted to
<code>Root</code> (by <a
href="https://github.com/MahinAnowar"><code>@​MahinAnowar</code></a>).</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/postcss/postcss/commit/eb9e1fe793740bb3280bdf5bf98147f857f011bd"><code>eb9e1fe</code></a>
Release 8.5.23 version</li>
<li><a
href="https://github.com/postcss/postcss/commit/9d19c78ac91108b3f7d7130e55c6fa806c0efb84"><code>9d19c78</code></a>
Update dependencies</li>
<li><a
href="https://github.com/postcss/postcss/commit/7beca139e70f9075c6b19700fcb00dd8033e5da8"><code>7beca13</code></a>
Does no load source map file without opts.from</li>
<li><a
href="https://github.com/postcss/postcss/commit/decea51421682341401575b3740709fda0e12930"><code>decea51</code></a>
Typo</li>
<li><a
href="https://github.com/postcss/postcss/commit/c18e30d126395d42a0726aa00e03a8f1088985ae"><code>c18e30d</code></a>
Update EM banner</li>
<li><a
href="https://github.com/postcss/postcss/commit/98a39ad73d163a90be924d5126c771262110f1fc"><code>98a39ad</code></a>
Update EM banner</li>
<li><a
href="https://github.com/postcss/postcss/commit/a3e48c492ddec0e4879d513b8b995fee887af352"><code>a3e48c4</code></a>
Release 8.5.22 version</li>
<li><a
href="https://github.com/postcss/postcss/commit/f49d6911795f53b2cfe023bb686bf1144ec30618"><code>f49d691</code></a>
Fix custom property losing its semicolon before a comment (<a
href="https://redirect.github.com/postcss/postcss/issues/2117">#2117</a>)</li>
<li><a
href="https://github.com/postcss/postcss/commit/28e0daf8f2fe5ba9e19ea3f8c27c8fe176f9419e"><code>28e0daf</code></a>
Release 8.5.21 version</li>
<li><a
href="https://github.com/postcss/postcss/commit/3d2b4e43e38274f233b5609d09687cadad8215d9"><code>3d2b4e4</code></a>
Update dependencies</li>
<li>Additional commits viewable in <a
href="https://github.com/postcss/postcss/compare/8.5.18...8.5.23">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=postcss&package-manager=npm_and_yarn&previous-version=8.5.18&new-version=8.5.23)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/ColeMurray/background-agents/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…up across 1 directory (ColeMurray#1272)

Bumps the npm_and_yarn group with 1 update in the / directory:
[hono](https://github.com/honojs/hono).

Updates `hono` from 4.12.31 to 4.13.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/honojs/hono/releases">hono's
releases</a>.</em></p>
<blockquote>
<h2>v4.13.0</h2>
<p>Hono v4.13.0 is now available!</p>
<p>The highlight of this release is performance: a batch of low-level
optimizations makes the core request/response path significantly faster
— up to 1.25x on common routes in our benchmark. This release also adds
first-class support for the HTTP QUERY method, defined in <a
href="https://www.rfc-editor.org/rfc/rfc10008.html">RFC 10008</a>, a new
Method Not Allowed middleware, and more.</p>
<h2>Performance improvements</h2>
<p>This release includes a series of small optimizations: skipping
unnecessary <code>Headers</code> allocations, replacing regex tests with
<code>indexOf</code>, allocating internal state lazily, and more.</p>
<p>Here is <a
href="https://github.com/honojs/hono/tree/main/benchmarks/fetch"><code>benchmarks/fetch</code></a>
comparing v4.12 and v4.13 (<code>ROUNDS=5 ./compare.sh</code>, Bun
1.4.0, Apple Silicon — each measurement runs in a fresh process, and the
variant order is reversed every round to avoid warm-up bias):</p>
<table>
<thead>
<tr>
<th>Benchmark</th>
<th align="right">v4.12</th>
<th align="right">v4.13</th>
<th align="right">Speedup</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>ping</code> — <code>GET /</code></td>
<td align="right">165.83 ns</td>
<td align="right">163.99 ns</td>
<td align="right">1.01x</td>
</tr>
<tr>
<td><code>query</code> — <code>GET /id/1?name=bun</code></td>
<td align="right">674.40 ns</td>
<td align="right">616.99 ns</td>
<td align="right"><strong>1.09x</strong></td>
</tr>
<tr>
<td><code>json</code> — <code>GET /user</code></td>
<td align="right">528.99 ns</td>
<td align="right">422.44 ns</td>
<td align="right"><strong>1.25x</strong></td>
</tr>
<tr>
<td><code>body</code> — <code>POST /json</code></td>
<td align="right">1.16 µs</td>
<td align="right">1.00 µs</td>
<td align="right"><strong>1.15x</strong></td>
</tr>
</tbody>
</table>
<p>The individual changes:</p>
<ul>
<li>perf(context): iterate the header record with <code>for..in</code>
<a
href="https://redirect.github.com/honojs/hono/pull/5118">honojs/hono#5118</a></li>
<li>perf(url): replace regex tests with <code>indexOf</code> <a
href="https://redirect.github.com/honojs/hono/pull/5121">honojs/hono#5121</a></li>
<li>perf(context): skip <code>Headers</code> creation when there are no
headers to merge <a
href="https://redirect.github.com/honojs/hono/pull/5122">honojs/hono#5122</a></li>
<li>perf(urls): refactor <code>tryDecodeURIComponent</code> <a
href="https://redirect.github.com/honojs/hono/pull/5158">honojs/hono#5158</a></li>
<li>perf(request): allocate <code>#validatedData</code> lazily <a
href="https://redirect.github.com/honojs/hono/pull/5175">honojs/hono#5175</a></li>
<li>perf(request): probe the body cache without allocating <a
href="https://redirect.github.com/honojs/hono/pull/5176">honojs/hono#5176</a></li>
</ul>
<p>In addition, the RegExpRouter rewrite described below makes route
registration plus the first match roughly 20% faster.</p>
<p>Thanks <a
href="https://github.com/kibertoad"><code>@​kibertoad</code></a> for the
contributions!</p>
<h2>First-class QUERY method support</h2>
<p>The QUERY method — a safe, idempotent method that carries a request
body — is now a first-class citizen in Hono. You can define QUERY
handlers with <code>app.query()</code>:</p>
<pre lang="ts"><code>const app = new Hono()
<p>app.query('/search', async (c) =&gt; {<br />
const conditions = await c.req.json()<br />
return c.json(await search(conditions))<br />
})<br />
</code></pre></p>
<p>Thanks <a
href="https://github.com/shellhaki"><code>@​shellhaki</code></a>!</p>
<h2>QUERY support across built-in middleware</h2>
<p>The built-in middleware has been updated to handle QUERY requests
properly:</p>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/honojs/hono/commit/192768fbaf9aa99a45404dc2f171541227c11d20"><code>192768f</code></a>
4.13.0</li>
<li><a
href="https://github.com/honojs/hono/commit/b0c2d90eb07fefa6c06dc556c97ed8ecffc8b2c0"><code>b0c2d90</code></a>
Merge pull request <a
href="https://redirect.github.com/honojs/hono/issues/5154">#5154</a>
from honojs/next</li>
<li><a
href="https://github.com/honojs/hono/commit/8f0702827002f0ada02434d908853458c17f862d"><code>8f07028</code></a>
fix(compress): set Vary: Accept-Encoding on negotiated responses (<a
href="https://redirect.github.com/honojs/hono/issues/5137">#5137</a>)</li>
<li><a
href="https://github.com/honojs/hono/commit/8a0b18fd9b4d64dd2eb1d7f18e3536fc06cb54b2"><code>8a0b18f</code></a>
feat(reg-exp-router): throw UnsupportedPathError during route
registration (#...</li>
<li><a
href="https://github.com/honojs/hono/commit/3feb3551d46de1f633e82253f12cf1117316be93"><code>3feb355</code></a>
fix(jsx): allow a function component to return an array (<a
href="https://redirect.github.com/honojs/hono/issues/5179">#5179</a>)</li>
<li><a
href="https://github.com/honojs/hono/commit/5d911d2ab7bcb2adb2e974ddd5b17742fb5a0bca"><code>5d911d2</code></a>
feat(utils/headers): add HTTP fields newly registered with IANA (<a
href="https://redirect.github.com/honojs/hono/issues/5153">#5153</a>)</li>
<li><a
href="https://github.com/honojs/hono/commit/30277aee0ddd96b643a4958d6102abe6e54d9a29"><code>30277ae</code></a>
feat(jwt,jwk): add a configurable WWW-Authenticate realm (<a
href="https://redirect.github.com/honojs/hono/issues/5141">#5141</a>)</li>
<li><a
href="https://github.com/honojs/hono/commit/1f707c56378a4497bc0a6a44fef5f5a8517fdf6b"><code>1f707c5</code></a>
feat(middleware): add method-not-allowed middleware (<a
href="https://redirect.github.com/honojs/hono/issues/5132">#5132</a>)</li>
<li><a
href="https://github.com/honojs/hono/commit/2df0b47659165c6403abda5512ecf21541409a62"><code>2df0b47</code></a>
feat(jsx): add React-compatible overloads to useRef (<a
href="https://redirect.github.com/honojs/hono/issues/5063">#5063</a>)</li>
<li><a
href="https://github.com/honojs/hono/commit/3bc96ba915bf8d5b25430c955aec8f7df500ff21"><code>3bc96ba</code></a>
feat(cache): add first-class support for QUERY requests (<a
href="https://redirect.github.com/honojs/hono/issues/5119">#5119</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/honojs/hono/compare/v4.12.31...v4.13.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=hono&package-manager=npm_and_yarn&previous-version=4.12.31&new-version=4.13.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/ColeMurray/background-agents/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
## Summary

- render every parent `task` tool call through the Task-specific
timeline presentation, even when no correlated child events exist
- preserve child correlation behavior while leaving missing-correlation
child events at the top level
- omit the Task activity heading and container when there is no
displayable child activity
- exclude correlated `step_start` and `step_finish` lifecycle envelopes
from displayable Task activity

## Bug

The Task details UI introduced in `6860660` was only reached when
`buildTimelineItems` found correlated child activity. Completed Tasks
without child events, early failures, and historical Tasks missing
correlation metadata remained generic tool groups and exposed raw
Arguments/Output instead of Instructions/Result disclosures.

## TDD Coverage

Focused regression tests were added first and confirmed red with three
failures: no Task groups for missing correlation, no Instructions/Result
for completed Tasks without children, and no cleaned Result for failed
Tasks without children. Coverage now verifies:

- completed Tasks with prompt/output and no child events use
Instructions/Result rather than Arguments/Output
- failed Tasks containing only a `task_error` envelope show the cleaned
Result
- absent and lifecycle-only child activity does not render an empty Task
activity section
- uncorrelated child events remain outside Task groups

## Verification

- `npm test -w @open-inspect/web -- --run
src/components/session-timeline.test.tsx` (17 passed)
- `npm test -w @open-inspect/web` (879 passed)
- `npm run typecheck -w @open-inspect/web`
- `npx eslint "packages/web/src/lib/timeline-items.ts"
"packages/web/src/components/task-activity-item.tsx"
"packages/web/src/components/session-timeline.test.tsx"`
- `npx prettier --check "packages/web/src/lib/timeline-items.ts"
"packages/web/src/components/task-activity-item.tsx"
"packages/web/src/components/session-timeline.test.tsx"`
- `git diff --check origin/main...HEAD`

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

---------

Co-authored-by: waclaude <colemurray.cs+ghwaclaude@gmail.com>
## Summary
- persist the session sidebar `All`/`Mine` owner filter in local storage
- hydrate the saved preference before enabling session fetching to avoid
an incorrect initial request or selection
- keep the sidebar in a loading state during preference hydration and
degrade safely when storage is unavailable
- add coverage for persistence, refresh restoration, invalid values, and
unavailable storage

## Testing
- `npm test -w @open-inspect/web -- --run
src/components/session-sidebar.test.tsx`
- `npm test -w @open-inspect/web -- --run
src/lib/client-auth-boundary-eslint.test.ts`
- `npm run lint -w @open-inspect/web`
- `npm run typecheck -w @open-inspect/web`
- `git diff --check`

The full web suite passed 878/879 tests; the remaining client-auth
boundary test exceeded its 5-second timeout under full-suite load and
passed when rerun in isolation.

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

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

* **New Features**
* Added a persistent session creator filter with “All” and “Mine”
options.
* Restores the selected filter when returning to or refreshing the app.
* Safely defaults to showing all sessions when saved preferences are
invalid or unavailable.

* **Bug Fixes**
* Prevented the sidebar from displaying an incorrect filter before
preferences finish loading.
  * Kept session filtering functional when browser storage access fails.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com>
…oleMurray#1273)

## Summary

- On a cache miss, `GET /repos` fetches from the SCM provider, enriches
from D1, and writes KV inline with the response. If the caller gives up
before that finishes, the Worker is cancelled before the KV write — so
the cache stays empty and the next request repeats the same slow path.
- The stale-while-revalidate branch can't rescue it, because that path
requires an existing entry. Once the KV entry expires
(`REPOS_CACHE_KV_TTL_SECONDS`, 1h), a deployment whose repo listing is
slower than the caller's timeout gets stuck: every request races the
timeout and none ever repopulates the cache, so the repository picker
never loads.
- The web app makes this reachable by default:
`packages/web/src/lib/control-plane-transport.ts` aborts control-plane
requests at `CONTROL_PLANE_FETCH_TIMEOUT_MS` (15s) via
`AbortSignal.timeout`.
- Fix: register the refresh with `waitUntil` so it outlives the
response, and reuse `refreshReposCache` for the miss path instead of
duplicating the fetch/enrich/write logic in the handler.

We hit this on a deployment with ~100 repositories, where the listing
plus metadata batch exceeded 15s. Symptom was `GET /repos` repeatedly
ending in `canceled` at ~15.0s while every other route stayed sub-200ms,
with the KV key absent throughout.

## Notes

`refreshReposCache` now returns a small result type so the miss path can
distinguish "provider not configured" (500 `SCM provider not
configured`) from a fetch failure (500 `Failed to fetch repositories`),
preserving the previous responses. It takes an optional timer so the
miss path keeps recording the `scm_api` metric; the background path
passes nothing and behaves as before.

This makes the miss path resilient rather than fast — the first request
can still exceed a caller's timeout. It no longer poisons every
subsequent one, since the write now lands and the next request is served
from cache.

## Test plan

- [x] `npm test -w @open-inspect/control-plane` (2255 tests pass)
- [x] `npm run typecheck -w @open-inspect/control-plane`
- [x] New test in `src/routes/repos.test.ts` asserts the miss path
registers the refresh with `waitUntil` and still writes the cache — it
fails on `main` (`waitUntil` called 0 times) and passes with this change

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

## Summary by CodeRabbit

* **Bug Fixes**
* Improved repository listing reliability when cached data is
unavailable.
* Repository information now refreshes successfully in the background,
including when a connection closes early.
* Added clearer handling for refresh failures so users receive more
meaningful errors.
* Improved timing visibility for source-control requests, supporting
better monitoring of repository loading performance.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
…eMurray#1275)

Bumps the uv group with 2 updates in the /packages/modal-infra
directory: [aiohttp](https://github.com/aio-libs/aiohttp) and
[cryptography](https://github.com/pyca/cryptography).

Updates `aiohttp` from 3.14.1 to 3.14.3
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/aio-libs/aiohttp/blob/master/CHANGES.rst">aiohttp's
changelog</a>.</em></p>
<blockquote>
<h1>3.14.3 (2026-07-22)</h1>
<h2>Bug fixes</h2>
<ul>
<li>
<p>Fixed the client dropping only the first <code>Authorization</code>,
<code>Cookie</code> and
<code>Proxy-Authorization</code> header when a redirect crossed an
origin -- by :user:<code>arshsmith1</code>.</p>
<p><em>Related issues and pull requests on GitHub:</em>
:issue:<code>13180</code>.</p>
</li>
<li>
<p>Fixed error message construction in the C HTTP parser -- by
:user:<code>bdraco</code>.</p>
<p><em>Related issues and pull requests on GitHub:</em>
:issue:<code>13222</code>.</p>
</li>
</ul>
<hr />
<h1>3.14.2 (2026-07-20)</h1>
<h2>Bug fixes</h2>
<ul>
<li>
<p>Fixed :py:attr:<code>~aiohttp.web.StreamResponse.last_modified</code>
rounding a
:class:<code>datetime.datetime</code> with a fractional second down.</p>
<p><em>Related issues and pull requests on GitHub:</em>
:issue:<code>5303</code>.</p>
</li>
<li>
<p>Fixed resolving <code>localhost</code> on Windows to fall back
without <code>AI_ADDRCONFIG</code>
when the first lookup fails, so <code>localhost</code> still works
without an active
network.</p>
<p><em>Related issues and pull requests on GitHub:</em>
:issue:<code>5357</code>.</p>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/aio-libs/aiohttp/commit/5e392ce0456f5235a4ee6ad46f0e806df2f15873"><code>5e392ce</code></a>
Release v3.14.3 (<a
href="https://redirect.github.com/aio-libs/aiohttp/issues/13225">#13225</a>)</li>
<li><a
href="https://github.com/aio-libs/aiohttp/commit/49f65d54150397892f7bcc4aae887767d51c322d"><code>49f65d5</code></a>
[PR <a
href="https://redirect.github.com/aio-libs/aiohttp/issues/13222">#13222</a>/f4866933
backport][3.14] Build C parser error message from bounded...</li>
<li><a
href="https://github.com/aio-libs/aiohttp/commit/240099e5216a01b32919dcd8dd5c6c0b1bf83671"><code>240099e</code></a>
[PR <a
href="https://redirect.github.com/aio-libs/aiohttp/issues/13180">#13180</a>/ee53d655
backport][3.14] drop every copy of credential headers on ...</li>
<li><a
href="https://github.com/aio-libs/aiohttp/commit/d93f30a302f8b930074fe14a2be7b2088ba28111"><code>d93f30a</code></a>
Bump version (<a
href="https://redirect.github.com/aio-libs/aiohttp/issues/13202">#13202</a>)</li>
<li><a
href="https://github.com/aio-libs/aiohttp/commit/c1b9212ad3d93c24b5fc66ad0849597166bc816e"><code>c1b9212</code></a>
Release v3.14.2 (<a
href="https://redirect.github.com/aio-libs/aiohttp/issues/13201">#13201</a>)</li>
<li><a
href="https://github.com/aio-libs/aiohttp/commit/380d4b55e8df48dfd62f1addfb530426f6bc4106"><code>380d4b5</code></a>
[PR <a
href="https://redirect.github.com/aio-libs/aiohttp/issues/13054">#13054</a>/ed8b040c
backport][3.14] escape backslashes in digest auth quoted-...</li>
<li><a
href="https://github.com/aio-libs/aiohttp/commit/e1e1bee363dfba04a9a75c8801717da2ed5bdcb9"><code>e1e1bee</code></a>
Make llhttp method array size dynamic (<a
href="https://redirect.github.com/aio-libs/aiohttp/issues/13174">#13174</a>)
(<a
href="https://redirect.github.com/aio-libs/aiohttp/issues/13196">#13196</a>)</li>
<li><a
href="https://github.com/aio-libs/aiohttp/commit/aa4cf29b6a5ad6f4d21fa1dd3f69193dc2f5d505"><code>aa4cf29</code></a>
[PR <a
href="https://redirect.github.com/aio-libs/aiohttp/issues/13170">#13170</a>/2b906869
backport][3.14] Fix StreamResponse.last_modified rounding...</li>
<li><a
href="https://github.com/aio-libs/aiohttp/commit/71b57b40d85a0723c92b0a5a37ebdf518210d2ea"><code>71b57b4</code></a>
[PR <a
href="https://redirect.github.com/aio-libs/aiohttp/issues/13172">#13172</a>/a57747ed
backport][3.14] Fix C parser folding fragment into query_...</li>
<li><a
href="https://github.com/aio-libs/aiohttp/commit/64a03fb620b623e5a5a1b7103c07ae3e536a0d40"><code>64a03fb</code></a>
[PR <a
href="https://redirect.github.com/aio-libs/aiohttp/issues/13169">#13169</a>/1adc0cd7
backport][3.14] Upgrade http:// to https:// in README.rst...</li>
<li>Additional commits viewable in <a
href="https://github.com/aio-libs/aiohttp/compare/v3.14.1...v3.14.3">compare
view</a></li>
</ul>
</details>
<br />

Updates `cryptography` from 48.0.1 to 50.0.0
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst">cryptography's
changelog</a>.</em></p>
<blockquote>
<p>50.0.0 - 2026-07-31</p>
<pre><code>
* **SECURITY ISSUE**:

:func:`~cryptography.hazmat.primitives.serialization.pkcs7.pkcs7_decrypt_der`
and its PEM and S/MIME variants no longer expose distinguishable errors
or
timing when unwrapping a ``RecipientInfo``'s ``encryptedKey``, which
could
act as a Bleichenbacher oracle for callers that decrypt untrusted
messages.
A random key is now substituted on failure, as described in :rfc:`3218`.
  Credit to **@X1AOxiang** for reporting the issue. **CVE-2026-69247**
* Deprecated Diffie-Hellman key exchange over finite fields (FFDH).
  Everything FFDH is deprecated, including the types in
``cryptography.hazmat.primitives.asymmetric.dh`` and loading FFDH keys
or
  parameters with the key loading APIs. Users should migrate to a more
  modern key exchange algorithm.
* Added ``xof()`` class methods to
  :class:`~cryptography.hazmat.primitives.hashes.SHAKE128` and
:class:`~cryptography.hazmat.primitives.hashes.SHAKE256` for
constructing
  algorithm instances configured for use with
  :class:`~cryptography.hazmat.primitives.hashes.XOFHash`.
* The :mod:`X.509 verification &lt;cryptography.x509.verification&gt;`
APIs are now
  considered stable and are subject to our API stability policy.
* Added the :doc:`/cobblestone` recipe, an implementation of the
  Cobblestone-128 and Cobblestone-256 instantiations of the `C2SP
  chunked-encryption specification
&lt;https://c2sp.org/chunked-encryption&gt;`_ for streaming
authenticated
  encryption of large messages.
* Parsing a Signed Certificate Timestamp list now rejects encodings that
carry trailing bytes after the list or after an individual SCT, instead
of
  silently ignoring them.
* Added support for using :class:`~cryptography.x509.Name` as a field
type in
  the :doc:`/hazmat/asn1/index` module.
* Loading a public key or an EC private key now rejects DER where the
``subjectPublicKey`` (or EC ``publicKey``) ``BIT STRING`` declares a
non-zero
  number of unused bits, instead of silently ignoring it.
* Parsing a CRL entry's ``InvalidityDate`` extension now rejects a
``GeneralizedTime`` that carries fractional seconds or another non-DER
form,
matching the strict encoding already required for every other X.509 time
  field.
* :func:`~cryptography.x509.ocsp.load_der_ocsp_request` and
:func:`~cryptography.x509.ocsp.load_der_ocsp_response` now reject a
request
or response whose ``version`` field is not ``v1``, the only version
defined
by RFC 6960, matching the version validation already performed when
loading
  certificates, CSRs and CRLs.
* :class:`~cryptography.hazmat.primitives.hashes.XOFHash` is now
supported
  when building against AWS-LC.
* HMAC (and therefore PBKDF2-HMAC) with SHA-3 hashes is now supported
when
  building against AWS-LC.
* Diffie-Hellman (:doc:`/hazmat/primitives/asymmetric/dh`) is now
supported
  when building against AWS-LC.
&lt;/tr&gt;&lt;/table&gt; 
</code></pre>
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/pyca/cryptography/commit/dcb7050b807b00392fa9fe2eac7cb362fcf355cc"><code>dcb7050</code></a>
Prepare for 50.0.0 release (<a
href="https://redirect.github.com/pyca/cryptography/issues/15372">#15372</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/53fccd93413a8d7f07d6d8999681f27b75cffa3f"><code>53fccd9</code></a>
Don't leak how PKCS#7 encryptedKey decryption failed (<a
href="https://redirect.github.com/pyca/cryptography/issues/15369">#15369</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/d472f978470fbefa521b86d98b2ecccbbb4d1dd8"><code>d472f97</code></a>
Add <code>from __future__ import annotations</code> to all src/ Python
files (<a
href="https://redirect.github.com/pyca/cryptography/issues/15371">#15371</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/908773d53829fb1466c6db364b31321c3cd8eb9a"><code>908773d</code></a>
Bump downstream dependencies in CI (<a
href="https://redirect.github.com/pyca/cryptography/issues/15368">#15368</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/2cc07cc948948211899bcb0cddd1fddf86e95812"><code>2cc07cc</code></a>
Bump BoringSSL, OpenSSL, AWS-LC in CI (<a
href="https://redirect.github.com/pyca/cryptography/issues/15367">#15367</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/c94ede9f040fa44942f7139772603419000acf66"><code>c94ede9</code></a>
chore(deps): bump ruff from 0.16.0 to 0.16.1 (<a
href="https://redirect.github.com/pyca/cryptography/issues/15366">#15366</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/67a8308dc9ea4cce6056e0f1438f903c208c3f35"><code>67a8308</code></a>
chore(deps): bump virtualenv from 21.7.0 to 21.7.1 (<a
href="https://redirect.github.com/pyca/cryptography/issues/15365">#15365</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/95018ffcdbbc510fd92fc872e3a3e80aa6e58596"><code>95018ff</code></a>
Release the GIL in one-shot AEAD encrypt/decrypt (<a
href="https://redirect.github.com/pyca/cryptography/issues/15361">#15361</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/6954733eaf55a0074abf88f06f7242dfca3a5d02"><code>6954733</code></a>
Release the GIL during DH and DSA parameter generation (<a
href="https://redirect.github.com/pyca/cryptography/issues/15364">#15364</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/6893b94c33e948f6240082461424cfb5da2dacc6"><code>6893b94</code></a>
Import _serialization instead of serialization in x509/extensions (<a
href="https://redirect.github.com/pyca/cryptography/issues/15363">#15363</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/pyca/cryptography/compare/48.0.1...50.0.0">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/ColeMurray/background-agents/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown

Terraform Validation Results

Step Status
Format
Init
Validate

Pushed by: @NicolasWalter, Action: pull_request

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR updates repository cache refresh handling, adds resource-aware Sentry webhook normalization and logging, persists the session creator filter, refines task activity rendering, expands tests, and updates package dependency constraints.

Changes

Repository cache refresh

Layer / File(s) Summary
Typed repository refresh flow
packages/control-plane/src/routes/repos.ts
Repository refreshes return typed success or failure results, preserve SCM timing, write refreshed data, and map failures to HTTP responses.
Cold-cache route validation
packages/control-plane/src/routes/repos.test.ts
Tests verify cache mocks, cold-cache responses, cache writes, and waitUntil completion.

Sentry webhook normalization

Layer / File(s) Summary
Sentry payload contracts and context builders
packages/shared/src/triggers/sentry/payloads.ts, packages/shared/src/triggers/sentry/context.ts, packages/shared/src/triggers/sentry/index.ts, packages/shared/src/triggers/index.ts
Shared schemas, payload types, and context builders support issue webhooks, issue alerts, and metric alerts.
Resource-aware normalization results
packages/shared/src/triggers/sentry/normalizer.ts
Normalization selects resource-specific payloads and returns normalized events or structured skip reasons.
Normalization result coverage
packages/shared/src/triggers/sentry/normalizer.test.ts
Tests cover resource headers, actions, identifiers, invalid shapes, and skip reasons.
Webhook handling and integration coverage
packages/control-plane/src/webhooks/sentry.ts, packages/control-plane/test/integration/webhooks.test.ts
Webhook parsing accepts unknown JSON, logs skipped events, forwards normalized events, and covers issue, metric, signed, and redacted logging cases.

Session sidebar and timeline

Layer / File(s) Summary
Persistent creator filtering
packages/web/src/hooks/use-sidebar-sessions.ts, packages/web/src/components/session-sidebar.tsx, packages/web/src/components/session-sidebar.test.tsx
The creator filter loads from and writes to local storage. Fetching waits for initialization, and storage failures fall back safely.
Task grouping and activity rendering
packages/web/src/lib/timeline-items.ts, packages/web/src/components/session-timeline.tsx, packages/web/src/components/task-activity-item.tsx
Task groups are created without activity, lifecycle-only events are excluded, and empty activity sections are hidden.
Task and child-session rendering coverage
packages/web/src/components/session-timeline.test.tsx
Tests cover reused child-session IDs and completed or failed tasks without child events.

Package version updates

Layer / File(s) Summary
Dependency constraint updates
packages/github-bot/package.json, packages/linear-bot/package.json, packages/slack-bot/package.json, packages/web/package.json
Package manifests update Hono, Next, and PostCSS version constraints.

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

Sequence Diagram(s)

sequenceDiagram
  participant Sentry
  participant WebhookHandler
  participant normalizeSentryEvent
  participant AutomationRuns
  Sentry->>WebhookHandler: send signed webhook
  WebhookHandler->>normalizeSentryEvent: payload and resource header
  normalizeSentryEvent-->>WebhookHandler: normalized event or skip result
  WebhookHandler->>AutomationRuns: create run for normalized event
Loading
sequenceDiagram
  participant SessionSidebar
  participant useSidebarSessions
  participant LocalStorage
  participant SessionAPI
  SessionSidebar->>useSidebarSessions: set creator filter
  useSidebarSessions->>LocalStorage: persist filter
  useSidebarSessions->>SessionAPI: fetch filtered sessions
  SessionAPI-->>SessionSidebar: return session list
Loading

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 0.00% 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 clearly identifies this pull request as an upstream synchronization performed on August 5, 2026, which matches the stated objectives.
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 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch

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

@github-actions

github-actions Bot commented Aug 5, 2026

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.sign_in_provider_gate: Refreshing state... [id=7f4a67d1-6978-0b23-899d-c2a9004643bd]
terraform_data.access_control_gate: Refreshing state... [id=841ab6bc-98a7-018c-1031-ebad4f8b62bc]
local_file.web_app_wrangler_production[0]: Refreshing state... [id=4fd11b54fb693eced4839d030233ab1911f26ea1]
null_resource.github_bot_build[0]: Refreshing state... [id=7294943583288896375]
module.modal_app[0].null_resource.modal_secrets[0]: Refreshing state... [id=8757687985279342629]
data.external.modal_source_hash[0]: Reading...
random_password.image_callback_token_pepper: Refreshing state... [id=none]
null_resource.web_app_cloudflare_build[0]: Refreshing state... [id=7599918834973972838]
null_resource.slack_bot_build[0]: Refreshing state... [id=3517235685678107646]
null_resource.linear_bot_build[0]: Refreshing state... [id=8131685489368025167]
module.session_index_kv.cloudflare_workers_kv_namespace.this: Refreshing state... [id=7f18644fbed34121bbe3a196f373ea93]
cloudflare_r2_bucket.media: Refreshing state... [id=open-inspect-media-primo]
module.linear_kv[0].cloudflare_workers_kv_namespace.this: Refreshing state... [id=d003f1ad81384910a1f48a0a33f18c09]
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]
cloudflare_queue.image_build_finalization_dlq: Refreshing state... [id=cbbc2d8794c04396a550996e7f0cc129]
module.github_kv[0].cloudflare_workers_kv_namespace.this: Refreshing state... [id=87dbfaa1d9ce4a37a42e04c57c434a72]
cloudflare_queue.image_build_finalization: Refreshing state... [id=1ca823a150c54578a9ad1814325147a3]
null_resource.control_plane_build: Refreshing state... [id=712263983139718104]
cloudflare_queue.slack_completion_delivery_dlq[0]: Refreshing state... [id=06ce03d2663f4aea937b0c0c1c379c17]
cloudflare_d1_database.main: Refreshing state... [id=dba95b03-ace9-47a8-81e9-6e39d8d694c5]
cloudflare_queue.slack_completion_delivery[0]: Refreshing state... [id=56ef0f3e13bd46a3a39f30c79ec547fa]
data.external.modal_source_hash[0]: Read complete after 1s [id=-]
random_password.service_auth_secret_linear_bot: Refreshing state... [id=none]
module.slack_kv[0].cloudflare_workers_kv_namespace.this: Refreshing state... [id=729b357dbb5e4c9d99ec9212cc45766e]
module.modal_app[0].null_resource.modal_deploy: Refreshing state... [id=1192764756692112984]
module.linear_bot_worker[0].cloudflare_worker.this: Refreshing state... [id=33782d80e8ff4af9b30b92870084b674]
module.slack_bot_worker[0].cloudflare_worker.this: Refreshing state... [id=5200e96d69804ea296e1f3a6b39e4243]
null_resource.d1_migrations: Refreshing state... [id=1394827775839141124]
module.slack_bot_worker[0].cloudflare_worker_version.this: Refreshing state... [id=6b9fbaec-16ec-4a30-89ad-cc60fb5348b8]
module.linear_bot_worker[0].cloudflare_worker_version.this: Refreshing state... [id=8cf30ae8-a5dd-4214-ba14-7f922310794b]
module.slack_bot_worker[0].cloudflare_workers_deployment.this: Refreshing state... [id=ee7c9fc3-dff3-48b9-85c4-d4579ba23ca7]
module.linear_bot_worker[0].cloudflare_workers_deployment.this: Refreshing state... [id=9c108f32-d908-4b59-9574-175c0a90ad89]
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_workers_cron_trigger.this[0]: Refreshing state... [id=open-inspect-control-plane-primo]
module.github_bot_worker[0].cloudflare_worker.this: Refreshing state... [id=4b5e2696491a41eaaa124f4e2a9855f2]
null_resource.web_app_cloudflare_secrets[0]: Refreshing state... [id=8867783181576424643]

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_consumer.image_build_finalization will be created
  + resource "cloudflare_queue_consumer" "image_build_finalization" {
      + account_id        = "bf66240843ed90d19b82e4b90916d29a"
      + consumer_id       = (known after apply)
      + created_on        = (known after apply)
      + dead_letter_queue = "open-inspect-image-build-finalization-dlq-primo"
      + queue_id          = "1ca823a150c54578a9ad1814325147a3"
      + queue_name        = (known after apply)
      + script_name       = "open-inspect-control-plane-primo"
      + settings          = {
          + batch_size            = 1
          + max_concurrency       = 5
          + max_retries           = 12
          + max_wait_time_ms      = 1000
          + retry_delay           = 15
          + 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_SHORT_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       = "712263983139718104" -> (known after apply)
      ~ triggers = { # forces replacement
          ~ "always_run" = "2026-08-04T12:35:31Z" -> (known after apply)
        }
    }

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

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

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

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

  # null_resource.web_app_cloudflare_deploy[0] will be created
  + resource "null_resource" "web_app_cloudflare_deploy" {
      + id       = (known after apply)
      + triggers = {
          + "always_run" = (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
                },
              - {
                  - namespace_id   = "bf3328c8ebcb4039855ed7fcca6eb7e9" -> null
                  - namespace_name = "open-inspect-control-plane-primo_SchedulerDO" -> null
                  - worker_id      = "c208a60c393e45e38eb502346bb7ce1e" -> null
                  - worker_name    = "open-inspect-control-plane-primo" -> null
                },
            ] -> (known after apply)
          ~ queues                       = [] -> (known after apply)
          ~ workers                      = [
              - {
                  - id   = "33782d80e8ff4af9b30b92870084b674" -> null
                  - name = "open-inspect-linear-bot-primo" -> null
                },
              - {
                  - id   = "ac07332f8b0f4cdfa4ca04f966a9fa61" -> null
                  - name = "open-inspect-web-primo" -> null
                },
              - {
                  - id   = "4b5e2696491a41eaaa124f4e2a9855f2" -> null
                  - name = "open-inspect-github-bot-primo" -> null
                },
              - {
                  - id   = "5200e96d69804ea296e1f3a6b39e4243" -> null
                  - name = "open-inspect-slack-bot-primo" -> null
                },
            ] -> (known after apply)
        } -> (known after apply)
        tags           = []
      ~ updated_on     = "2026-07-30T19:37:01Z" -> (known after apply)
        # (6 unchanged attributes hidden)
    }

  # module.control_plane_worker.cloudflare_worker_version.this will be created
  + resource "cloudflare_worker_version" "this" {
      + account_id          = "bf66240843ed90d19b82e4b90916d29a"
      + annotations         = (known after apply)
      + bindings            = (sensitive value)
      + compatibility_date  = "2024-09-23"
      + compatibility_flags = [
          + "nodejs_compat",
        ]
      + created_on          = (known after apply)
      + id                  = (known after apply)
      + limits              = (known after apply)
      + main_module         = "index.js"
      + main_script_base64  = (known after apply)
      + migration_tag       = (known after apply)
      + modules             = [
          + {
              + content_file   = "../../..//packages/control-plane/dist/index.js"
              + content_sha256 = "b6a5fbfa0f6c78c14fb30f8dcd6c835a666500803bf4b2ce27a38403d8d40dff"
              + content_type   = "application/javascript+module"
              + name           = "index.js"
            },
        ]
      + number              = (known after apply)
      + source              = (known after apply)
      + startup_time_ms     = (known after apply)
      + urls                = (known after apply)
      + usage_model         = "standard"
      + worker_id           = "c208a60c393e45e38eb502346bb7ce1e"
    }

  # module.control_plane_worker.cloudflare_workers_cron_trigger.this[0] will be updated in-place
  ~ resource "cloudflare_workers_cron_trigger" "this" {
        id          = "open-inspect-control-plane-primo"
      ~ schedules   = [
          ~ {
              ~ created_on  = "2026-05-22T19:51:30.060015Z" -> (known after apply)
              ~ modified_on = "2026-05-22T19:51:30.060015Z" -> (known after apply)
                # (1 unchanged attribute hidden)
            },
          + {
              + created_on  = (known after apply)
              + cron        = "7,37 * * * *"
              + modified_on = (known after apply)
            },
        ]
        # (2 unchanged attributes hidden)
    }

  # module.control_plane_worker.cloudflare_workers_deployment.this will be created
  + resource "cloudflare_workers_deployment" "this" {
      + account_id   = "bf66240843ed90d19b82e4b90916d29a"
      + annotations  = (known after apply)
      + author_email = (known after apply)
      + created_on   = (known after apply)
      + id           = (known after apply)
      + script_name  = "open-inspect-control-plane-primo"
      + source       = (known after apply)
      + strategy     = "percentage"
      + versions     = [
          + {
              + percentage = 100
              + version_id = (known after apply)
            },
        ]
    }

  # 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-07-30T19:37:10Z" -> (known after apply)
        # (6 unchanged attributes hidden)
    }

  # module.github_bot_worker[0].cloudflare_worker_version.this will be created
  + resource "cloudflare_worker_version" "this" {
      + account_id          = "bf66240843ed90d19b82e4b90916d29a"
      + annotations         = (known after apply)
      + bindings            = (sensitive value)
      + compatibility_date  = "2024-09-23"
      + compatibility_flags = [
          + "nodejs_compat",
        ]
      + created_on          = (known after apply)
      + id                  = (known after apply)
      + limits              = (known after apply)
      + main_module         = "index.js"
      + main_script_base64  = (known after apply)
      + migration_tag       = (known after apply)
      + modules             = [
          + {
              + content_file   = "../../..//packages/github-bot/dist/index.js"
              + content_sha256 = "48f06ec5a8c212ab43a36f744754472e101760f32173ec4e19176cf833c20066"
              + content_type   = "application/javascript+module"
              + name           = "index.js"
            },
        ]
      + number              = (known after apply)
      + source              = (known after apply)
      + startup_time_ms     = (known after apply)
      + urls                = (known after apply)
      + usage_model         = "standard"
      + worker_id           = "4b5e2696491a41eaaa124f4e2a9855f2"
    }

  # module.github_bot_worker[0].cloudflare_workers_deployment.this will be created
  + resource "cloudflare_workers_deployment" "this" {
      + account_id   = "bf66240843ed90d19b82e4b90916d29a"
      + annotations  = (known after apply)
      + author_email = (known after apply)
      + created_on   = (known after apply)
      + id           = (known after apply)
      + script_name  = "open-inspect-github-bot-primo"
      + source       = (known after apply)
      + strategy     = "percentage"
      + versions     = [
          + {
              + percentage = 100
              + version_id = (known after apply)
            },
        ]
    }

  # 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-04T12:35: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-04T12:35:34Z" -> (known after apply)
      ~ id                  = "8cf30ae8-a5dd-4214-ba14-7f922310794b" -> (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 = "2f0414718c9f0ea3023176fbd3f9e7a699f51760054b3eadf8791534a074c053" -> null
              - content_type   = "application/javascript+module" -> null
              - name           = "index.js" -> null
            },
          + { # forces replacement
              + content_file   = "../../..//packages/linear-bot/dist/index.js"
              + content_sha256 = "cb7ec20bace9d25b59cf6379316354573388e15b0cc1f6f50be169f1fd318143"
              + content_type   = "application/javascript+module"
              + name           = "index.js"
            },
        ]
      ~ number              = 61 -> (known after apply)
      ~ source              = "terraform" -> (known after apply)
      ~ startup_time_ms     = 39 -> (known after apply)
      ~ urls                = [
          - "https://8cf30ae8-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-04T12:35:35Z" -> (known after apply)
      ~ id           = "9c108f32-d908-4b59-9574-175c0a90ad89" -> (known after apply)
      ~ source       = "terraform" -> (known after apply)
      ~ versions     = [ # forces replacement
          ~ {
              ~ version_id = "8cf30ae8-a5dd-4214-ba14-7f922310794b" -> (known after apply)
                # (1 unchanged attribute hidden)
            },
        ]
        # (3 unchanged attributes hidden)
    }

  # module.modal_app[0].null_resource.modal_deploy is tainted, so must be replaced
-/+ resource "null_resource" "modal_deploy" {
      ~ id       = "1192764756692112984" -> (known after apply)
      ~ triggers = {
          ~ "source_hash"       = "b8ca0bca092dcbd3335969169a8f2d08e454c57de021e33a7f4ca84e93804874" -> "9cba85cacc65ace137931f798809aa3be528307547e91307e2b764ec8a90c9dd"
            # (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-04T12:35:32Z" -> (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-04T12:35:33Z" -> (known after apply)
      ~ id                  = "6b9fbaec-16ec-4a30-89ad-cc60fb5348b8" -> (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 = "329a5e8c43da4df91320a17a8f32b9ca921876def0224489bd51167d47b2d111" -> null
              - content_type   = "application/javascript+module" -> null
              - name           = "index.js" -> null
            },
          + { # forces replacement
              + content_file   = "../../..//packages/slack-bot/dist/index.js"
              + content_sha256 = "ad4e445928a59ff0b8be03f3650b81cdbda4b77d4d6ea0f25f408696607cca69"
              + content_type   = "application/javascript+module"
              + name           = "index.js"
            },
        ]
      ~ number              = 64 -> (known after apply)
      ~ source              = "terraform" -> (known after apply)
      ~ startup_time_ms     = 40 -> (known after apply)
      ~ urls                = [
          - "https://6b9fbaec-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-04T12:35:34Z" -> (known after apply)
      ~ id           = "ee7c9fc3-dff3-48b9-85c4-d4579ba23ca7" -> (known after apply)
      ~ source       = "terraform" -> (known after apply)
      ~ versions     = [ # forces replacement
          ~ {
              ~ version_id = "6b9fbaec-16ec-4a30-89ad-cc60fb5348b8" -> (known after apply)
                # (1 unchanged attribute hidden)
            },
        ]
        # (3 unchanged attributes hidden)
    }

Plan: 17 to add, 5 to change, 10 to destroy.

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

Saved the plan to: tfplan

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

Pushed by: @NicolasWalter

@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 (1)
packages/web/src/hooks/use-sidebar-sessions.ts (1)

45-57: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Name the default creator filter.

Line 46 defines the default filter inline. Define DEFAULT_SESSION_CREATOR_FILTER once and initialize initialFilter from it.

Proposed change
 type SessionCreatorFilter = "all" | "mine";
+const DEFAULT_SESSION_CREATOR_FILTER: SessionCreatorFilter = "all";
 
 useEffect(() => {
-  let initialFilter: SessionCreatorFilter = "all";
+  let initialFilter = DEFAULT_SESSION_CREATOR_FILTER;

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/web/src/hooks/use-sidebar-sessions.ts` around lines 45 - 57, Define
a named DEFAULT_SESSION_CREATOR_FILTER constant for the "all" default and
initialize initialFilter from it in the useEffect hook. Reuse this constant
wherever the default creator filter is needed instead of repeating the literal.

Source: Coding guidelines

🤖 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/control-plane/src/routes/repos.ts`:
- Line 57: Define the identity ScmApiTimer function in a named constant near the
route timer definitions, then update the relevant constructor or function
parameter to use that constant as its default instead of the inline `(fn) =>
fn()` expression. Reuse the named constant wherever this identity default is
needed.

---

Nitpick comments:
In `@packages/web/src/hooks/use-sidebar-sessions.ts`:
- Around line 45-57: Define a named DEFAULT_SESSION_CREATOR_FILTER constant for
the "all" default and initialize initialFilter from it in the useEffect hook.
Reuse this constant wherever the default creator filter is needed instead of
repeating the literal.
🪄 Autofix

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: f0902cde-9307-4744-9713-a7f9f30905ac

📥 Commits

Reviewing files that changed from the base of the PR and between 29bc7a8 and 3003b52.

⛔ Files ignored due to path filters (2)
  • package-lock.json is excluded by !**/package-lock.json
  • packages/modal-infra/uv.lock is excluded by !**/*.lock
📒 Files selected for processing (21)
  • packages/control-plane/src/routes/repos.test.ts
  • packages/control-plane/src/routes/repos.ts
  • packages/control-plane/src/webhooks/sentry.ts
  • packages/control-plane/test/integration/webhooks.test.ts
  • packages/github-bot/package.json
  • packages/linear-bot/package.json
  • packages/shared/src/triggers/index.ts
  • packages/shared/src/triggers/sentry/context.ts
  • packages/shared/src/triggers/sentry/index.ts
  • packages/shared/src/triggers/sentry/normalizer.test.ts
  • packages/shared/src/triggers/sentry/normalizer.ts
  • packages/shared/src/triggers/sentry/payloads.ts
  • packages/slack-bot/package.json
  • packages/web/package.json
  • packages/web/src/components/session-sidebar.test.tsx
  • packages/web/src/components/session-sidebar.tsx
  • packages/web/src/components/session-timeline.test.tsx
  • packages/web/src/components/session-timeline.tsx
  • packages/web/src/components/task-activity-item.tsx
  • packages/web/src/hooks/use-sidebar-sessions.ts
  • packages/web/src/lib/timeline-items.ts

env: Env,
db: SqlDatabase,
traceId?: string,
timeScmApi: ScmApiTimer = (fn) => fn()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Name the identity timer default.

Define the identity ScmApiTimer in a named constant. Use that constant as the parameter default.

Proposed fix
 type ScmApiTimer = <T>(fn: () => Promise<T>) => Promise<T>;
+const IDENTITY_SCM_API_TIMER: ScmApiTimer = (fn) => fn();
 
 async function refreshReposCache(
   env: Env,
   db: SqlDatabase,
   traceId?: string,
-  timeScmApi: ScmApiTimer = (fn) => fn()
+  timeScmApi: ScmApiTimer = IDENTITY_SCM_API_TIMER
 ): Promise<ReposRefreshResult> {

As per coding guidelines, define each default value exactly once in a named constant and import or reuse that constant everywhere.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
timeScmApi: ScmApiTimer = (fn) => fn()
type ScmApiTimer = <T>(fn: () => Promise<T>) => Promise<T>;
const IDENTITY_SCM_API_TIMER: ScmApiTimer = (fn) => fn();
async function refreshReposCache(
env: Env,
db: SqlDatabase,
traceId?: string,
timeScmApi: ScmApiTimer = IDENTITY_SCM_API_TIMER
): Promise<ReposRefreshResult> {
🤖 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/control-plane/src/routes/repos.ts` at line 57, Define the identity
ScmApiTimer function in a named constant near the route timer definitions, then
update the relevant constructor or function parameter to use that constant as
its default instead of the inline `(fn) => fn()` expression. Reuse the named
constant wherever this identity default is needed.

Source: Coding guidelines

@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: 3003b52a30

ℹ️ About Codex in GitHub

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

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

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

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

Comment on lines +40 to +42
if (issueWebhookResult.data.action !== "created") {
if (sentryHookResource === "issue") {
return { status: "skipped", reason: "unsupported_action" };

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Handle issue.unresolved as a regression

When the webhook uses the current Sentry-Hook-Resource: issue shape, Sentry issue lifecycle actions include regressed/unresolved issues (the issue webhook docs list unresolved alongside created: https://docs.sentry.io/integrations/integration-platform/webhooks/issues/). This branch returns unsupported_action for every issue action except created, so an automation configured for the already-exposed issue.regression event never fires for the current Sentry issue webhook path; only legacy event-alert regressions can still work. Please map unresolved (and any regression action you expect) to issue.regression instead of skipping it.

Useful? React with 👍 / 👎.

@NicolasWalter
NicolasWalter merged commit b999bec into main Aug 5, 2026
19 checks passed
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