Repository navigation
chore: sync upstream e364a7ee (64 commits) - #117
Conversation
…ay#2213) ## Summary Supersedes ColeMurray#2191, taking over its original fix at `eec0f170` and resolving conflicts against current `main` (`4664486`). Credits the original author as a co-author. PR ColeMurray#2191 authorized team-owned child creation using `promptAuthor.canonicalUserId ?? parentSession.userId`. An unresolved author could therefore borrow the parent owner's current team membership and create a team-owned child using the parent sandbox token. - Require the active prompt author's own canonical identity and current membership in the parent's owning team before resolving the child repository, acquiring an admission lease, or creating the child. - Return `403 { error: "Not a team member", code: "not_member" }` for unresolved or nonmember authors in every `TEAMS_ENFORCEMENT` mode. - Preserve existing parent-owner fallback for workspace-owned child ownership, including private sessions; it is not used for team authorization. - Preserve newer repository permission/grant checks, fresh SCM repository-ID resolution, and inherited environment ownership checks when resolving the original merge conflicts. - Update successful team/environment test fixtures to use canonical member authors while retaining coverage of inheritance without human environment-use permission. ## Regression Coverage - Real workerd/D1 integration tests reject removed and unresolved prompt authors in `off`, `shadow`, and `on`, even when the parent owner remains a team member and the repository grant is valid. - Rejections leave no child or admission lease and do not reach SCM repository resolution. - Unit tests distinguish the request caller, parent owner, and active author, cover absent/null canonical identities and membership in another team, and permit a member author even when the parent owner is no longer a member. - Existing private/workspace ownership and credential-identity regressions remain passing. ## Verification Before correcting the original PR's fallback-based gate, the unresolved-author integration regression failed in all three modes with **expected 403, received 201**. With this fix, all targeted integration suites pass. - `npm run build -w @open-inspect/shared` - `npm test -w @open-inspect/control-plane -- --maxWorkers=1 --silent --reporter=dot`: **6,176 tests passed across 362 files**. - `npm run test:integration -w @open-inspect/control-plane -- test/integration/spawn-children.test.ts test/integration/session-environment-ownership.test.ts test/integration/child-session-ops.test.ts test/integration/session-access-routes.test.ts test/integration/rbac-routes.test.ts --maxWorkers=1 --silent`: **126 tests passed across 5 files**. - `npm run typecheck -w @open-inspect/control-plane`: passed, including integration test types. - `npm run build -w @open-inspect/control-plane`: Worker and Node builds passed. - ESLint and Prettier checks on all four changed files, `git diff --check`, and commit hooks passed. The full integration suite was not run locally. Membership lookup and child persistence remain separate operations, as in existing session creation; this change addresses missing or revoked membership before the request, not concurrent revocation during an authorized request. --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/cff7c2e448e88bb119050ab1c6b3c491)* <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Team-owned child sessions are now created only when the active prompt author is a member of the parent’s team. Requests from authors who are not members or cannot be identified are rejected with a 403 response before a child session is created. * When spawning is allowed, the child session reflects the active author’s team membership and canonical user identity. * For non-team child sessions, the parent’s user identity remains the fallback when the prompt author has no canonical identity. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com> Co-authored-by: Rahul Sethuram <rahul.eth@icloud.com> Co-authored-by: waclaude <colemurray.cs+ghwaclaude@gmail.com>
## Summary Supersedes ColeMurray#2190 by taking over its slug-rename fix on the current `main` branch. The original PR branch is unchanged, and @rhlsthrm's commit authorship is retained. - Keep the displayed team resolved by its ID while the team list revalidates after a slug rename, then replace the URL with the team's current encoded slug. - Resolve the `TeamPage` conflicts while preserving the shared workspace-admin check, automation permission checks, and repository/environment/automation tabs added on `main`. - Update the regression test's authorization mock and complete capability fixture for the current APIs. ## Verification - `npm run build -w @open-inspect/shared` - `npm test -w @open-inspect/web -- src/components/teams/ src/components/settings/teams-settings.test.tsx src/components/settings/teams-settings-cache.test.tsx --maxWorkers=1`: 7 files, 109 tests passed. - `npm run typecheck -w @open-inspect/web` - `npm run lint -w @open-inspect/web` - Prettier check on all three changed files and `git diff --check` passed. - Browser verification against the local Next.js app with mocked API responses: renaming from Settings updates the route and keeps the team available at desktop (1440x1000) and mobile (390x844) sizes. Mobile Settings can be reopened with the saved slug, with no horizontal overflow. --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/c671ab008b98ba858c7f1896c0bee706)* <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * After a team is renamed, its page remains visible and the URL updates to the team’s new slug, even if the directory is stale, refresh fails, or the old slug is reused by another team. * Archived teams are not treated as active matches when resolving a team page. Navigating to a missing or archived team slug continues to show “Team not found” without redirecting. * Team lists now reflect successful team updates without waiting for a refresh. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Rahul Sethuram <rahul.eth@icloud.com> Co-authored-by: waclaude <colemurray.cs+ghwaclaude@gmail.com>
…leMurray#2202) ## Summary Fixes COL-227. - Commit the local archive status through `beginTransition()` before invoking preservation. A local persistence failure throws synchronously and skips the save; the existing async `transition()` API retains its promise-rejection behavior. - Start preservation before awaiting the D1 status projection. The synchronous `archived` and `draining` writes remain in the same Durable Object turn, so a bridge reconnect receives HTTP 503 while saving still needs its sandbox. - Keep archive eligibility checks and final index confirmation unchanged. Missing/legacy shutdown records still receive HTTP 410. No migrations or persisted fields are added. - Cover the delayed projection and failed local write with integration regressions. Restore the projection spy before awaiting rejection-safe archive settlement, including when the gate is never reached. ## Verification - The original reconnect regression failed with HTTP 410 before the initial fix and passes with HTTP 503. - The new SQLite-abort regression failed because a rejected archive write still moved the sandbox to `draining`; it now leaves the shutdown record and session status unchanged. - `npm run test -w @open-inspect/control-plane -- --maxWorkers=1` passed: 6,115 tests. - `npm run test:integration -w @open-inspect/control-plane -- --maxWorkers=1 test/integration/sandbox-shutdown.test.ts test/integration/session-lifecycle.test.ts test/integration/session-batch-archive.test.ts test/integration/websocket-sandbox.test.ts` passed: 75 tests. - `npm run typecheck -w @open-inspect/control-plane` passed. - Targeted ESLint, Prettier, and commit hooks passed. --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/6d30e15bbef91458fcc84af0a844cc3f)* <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Session archiving now proceeds while sandbox preservation is underway, while still confirming the archived status before completing. * Sandbox reconnect attempts receive a temporary “Sandbox is being saved” response while archiving is pending. * Reconnect attempts for archived sessions with missing or legacy shutdown records receive a “Session is terminal” response. * If updating a session’s local status fails during archiving, sandbox shutdown state remains unchanged. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com> Co-authored-by: waclaude <colemurray.cs+ghwaclaude@gmail.com>
…lds (ColeMurray#2019) `ImageBuildReaper.reconcileUnresolvedOperations` can clear the provider-operation reference of a capture that is still running. ## Mechanism The `absent` branch settles on a `created_at`-anchored estimate: ```ts if (outcome.type === "absent" && now - row.created_at <= DEFAULT_STALE_BUILD_MAX_AGE_MS) { ``` while the row carries the exact bound the comment directly above it appeals to. The two are anchored to **different clocks**: - `DEFAULT_STALE_BUILD_MAX_AGE_MS` is 75 min measured from `created_at` = `registerBuild` (`src/image-builds/maintenance.ts:20-21`, `src/image-builds/timeouts.ts:6-13`). Its own comment (`maintenance.ts:14-18`) says the clock "starts at row registration, not sandbox start, so dispatch latency and provider queueing eat into the grace budget" — that budget is `IMAGE_BUILD_STALE_DISPATCH_GRACE_MS`, 5 min. - `provider_operation_deadline_at` is stamped at **reservation** time against the source's remaining lifetime (`src/image-builds/daytona-adapter.ts:209-223,333-339`), and is documented as a "Fixed wall-clock deadline (ms) for that operation; never extended by a retry" (`src/db/image-build-finalization.ts:73-74`). The capture attempt itself treats exhaustion as `now >= operation.deadlineAt` (`daytona-adapter.ts:305-313`). A build dispatched later than the 5-minute registration grace allows therefore holds a live deadline that outlasts the estimate. The reaper could not even see it: `UnresolvedProviderOperationRow` had no such field and `UNRESOLVED_PROVIDER_OPERATIONS_SQL` did not select the column (`src/db/image-builds.ts:179-185,227-233`). A row reaches that query while its deadline is still live via `supersedeScopeBuilds`, which flips `building` → `superseded` with no regard for a reserved operation (`src/db/image-builds.ts:786-793`), or via `markFailed`, which deliberately leaves the ref and deadline in place — unlike `recordArtifact` and `quarantineArtifact`, which NULL them (`src/db/image-build-finalization.ts:399-414` vs `:316-322,431-438`). Clearing the reference there drops the only handle to a capture still running. ## Fix Select the persisted deadline and retain the obligation while **either** bound still holds. A row that recorded no deadline (`?? 0`) is treated as already exhausted, so the estimate alone decides exactly as before — no behaviour change for rows predating the column. ## Verification `npm test -w @open-inspect/control-plane -- src/image-builds/reaper.test.ts` → 19 passed. The three new cases were falsified against the unfixed source: older than the max age but within a live deadline is **not** reaped (this one fails without the fix), an expired deadline **is** reaped, and a null deadline keeps today's behaviour. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Bug Fixes** - Prevented operations from being settled as absent before their reserved capture deadline has elapsed. - Operations without a recorded deadline continue to use the existing stale-age handling. - Improved reconciliation behavior and reporting for operations that may still be running. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Cole Murray <colemurray.cs@gmail.com>
) ## Summary - Add a **Session origins** card directly below the Analytics session summary, showing ranked source counts and shares for Slack, GitHub, Linear, user/app, agent sub-sessions, and automations. - Select a source to see its attributed users, session counts, and within-source shares; reset to all sources or change the existing date/scope controls. - Extend the existing dashboard snapshot with source-by-user counts in its single database batch. Reuse date-window, source-scope, and visibility predicates; private sessions remain excluded. - Preserve canonical user identities, separate same-name users, and include legacy-login and unknown-user buckets. Explain attribution limitations in the UI and correct outdated summary/scope copy. - Include responsive layouts, accessible source controls, a keyboard-focusable user list, loading/empty/cached states, and selection resets. ## Scope and Data Semantics This measures **session creation origins**, not subsequent message activity. Existing attribution can identify integration actors or automation owners rather than a direct human creator. Historical source defaults remain under User / app; no speculative backfill or schema migration is introduced. The existing Human default scope is retained; All includes agent and automation sessions. ## Verification - Shared package build passed. - Control-plane and web typechecks passed. - Repository ESLint and SQL portability checks passed. - Targeted web analytics tests: **38 passed**. - Targeted control-plane analytics tests: **54 passed**. - Real-D1 analytics integration tests: **16 passed**, including source/user grouping, all scopes, exact date boundaries, legacy/unknown attribution, and visibility. - Browser-tested the real `/analytics` page with mocked auth/API responses at **1440×1100** and **390×844**: source filtering, All sources reset, date/scope resets, and all six source categories. No browser errors. Backend correctness was checked separately with real D1. - Reviewed changes and fixed keyboard scrolling and stale-source-selection issues. `git diff --check` passed. ## Visual Evidence Viewport screenshots uploaded to the Open-Inspect session (mocked data, `http://localhost:3000/analytics`): - Desktop, all sources, 1440×1100: artifact `a10c57ac73c91449d85436c35bad1030` - Mobile, Slack selected, 390×844: artifact `f82481b838e0b10c8e664783129d2d62` --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/6b804adda7bf204945bdb55de6c5cc80)* <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added a Session Origins card to analytics, showing session counts and percentages by source and attributed user. * Added source filtering, with the selection resetting when the date range or scope changes and reverting to all sources if the selected source is no longer available. * Session origin data follows the selected date range and scope, and includes unattributed sessions. * Expanded active-user attribution to include legacy logins. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com> Co-authored-by: waclaude <colemurray.cs+ghwaclaude@gmail.com>
Closes [COL-208](https://linear.app/colemurray/issue/COL-208). ## Summary - Add provider-keyed channel binding storage, capability-protected team routes, Slack-only service lookup, and atomic `team.binding_added` / `team.binding_removed` audit events. - Validate bindings through the Slack bot's authenticated `conversations.info` endpoint. Refuse channels the bot has not joined and externally shared channels. - Resolve the channel's team before classification, scope repository/environment memory and KV caches, preserve scope through clarification interactions, and send `teamId` with session creation. - Preserve membership and repository-grant denial details. A forbidden follow-up reports lack of access without closing the thread for other users; unavailable sessions close rather than silently creating replacements. - Gate completion, tool progress, activity, automation completion, and `slack-notify` posts against the authoritative session row and current channel binding. Private and cross-team publication is refused, including workspace-visible sessions. - Add signed `channel` scope to completion reads. Queued publication also sends signed `purpose=slack-post`, applying the stricter outbound check at content/media reads without changing ordinary workspace-read semantics. Failed protected reads never publish queued error text or previously fetched content. - Persist thread closure independently of mapping/checkpoint writes, including early callbacks and automation threads without interactive mappings. - Enforce Slack automation/channel ownership at writes and candidate selection, while preserving workspace-owned automations in unbound channels. - Add the team's Channels tab, global Slack `unboundChannels` setting, and audit labels. The obsolete move warning and its provenance read contract have been removed. Update the changelog and both sandbox tool denial-guidance maps. ## Storage And Enforcement D1 migration `0083` already contains the binding and ownership schema; DO migration `56` is unchanged. No migrations, columns, or new deployment secrets are added. Bot endpoint authentication reuses the existing service callback signing credentials. Ordinary session reads preserve `off` / `shadow` / `on`: non-private team visibility is bypassed in `off`, audited in `shadow`, and enforced in `on`. Every non-read action on a team-owned session now requires current team membership in all modes, including for Owners and Administrators. Private restrictions remain active in every mode. Binding management and outbound publication checks enforce independently of that flag. The D1 session row is authoritative; the thread's stored `teamId` is only a cache. ## Checkpoint Report ### Commands And Results Validation ran sequentially with one Vitest worker. The control-plane and web suites below were rerun after rebasing onto `70b8ca4`. | Command | Final result | | --- | --- | | `npm run build -w @open-inspect/shared` | Passed | | `npm run typecheck` | All workspaces passed | | `npm run lint:fix` | Passed | | `npm run lint:sql-portability` | Clean; unchanged portability baseline | | `npm test -w @open-inspect/control-plane -- --maxWorkers=1 --silent` | 343 files, 5,774 tests passed | | `npm run test:integration -w @open-inspect/control-plane -- --maxWorkers=1 --silent --reporter=dot` | 129 files, 1,704 tests passed; 1 existing skip | | `npm test -w @open-inspect/web -- --maxWorkers=1 --silent` | 261 files, 2,607 tests passed | | `npm test -w @open-inspect/slack-bot -- --maxWorkers=1 --silent` | 41 files, 593 tests passed | | `npm test -w @open-inspect/shared -- --maxWorkers=1 --silent` | 65 files, 1,115 tests passed | | `uv run --extra dev pytest tests/test_claude_tools.py tests/test_tool_installation.py -q` in `packages/sandbox-runtime` | 40 tests passed | | `uv run --extra dev ruff check src/sandbox_runtime/harness/claude_tools.py` | Passed | | `uv run --extra dev ruff format --check src/sandbox_runtime/harness/claude_tools.py` | Passed | | `git diff --name-only -z origin/main...HEAD \| xargs -0 npx prettier --check --ignore-unknown` | Passed | | `git diff origin/main...HEAD --check` | Passed | Targeted integration runs verified binding refusals, service audience restrictions, signed cross-team concealment before any runtime call, outbound refusal for every posting path, automation ownership, and originating-thread snapshot enrichment. Admission/catalog snapshots were regenerated and verified after the rebase, retaining the merged Teams follow-ups. Initial red tests and validation failures were resolved, not ignored. Selected verbatim diagnostics: ```text Error: Cannot find module './channel-scope' imported from /workspace/background-agents/packages/control-plane/src/authorization/channel-scope.test.ts Error: D1_TYPE_ERROR: Type 'undefined' not supported for value 'undefined' AssertionError: expected 201 to be 409 // Object.is equality AssertionError: expected 200 to be 404 // Object.is equality AssertionError: expected [ 'workspace', 'matching', 'other' ] to deeply equal [ 'workspace' ] TypeError: (intermediate value).hasCompatibleBindings is not a function AssertionError: expected 'user-or-service' to be 'service' // Object.is equality AssertionError: expected 1 to be less than 0 src/classifier/environments.test.ts(74,78): error TS2345: Argument of type 'string[]' is not assignable to parameter of type 'string'. src/classifier/repos.test.ts(201,78): error TS2345: Argument of type 'string[]' is not assignable to parameter of type 'string'. src/completion/delivery.test.ts(223,44): error TS2709: Cannot use namespace 'ExtractorModule' as a type. AssertionError: expected [Function] to throw error including 'Missing events pagination cursor' but got 'Invalid events response' shell tool terminated command after exceeding timeout 600000 ms. ``` The initial integration fixture omitted required `joinPolicy`; that fixture was corrected before confirming the behavioral red cases. Other fixes included adapting existing mocks and route counts, making lookup authentication service-only, updating the status-order assertion for the new async closure check, correctly modeling SWR reset revalidation, and removing a redundant pagination check already enforced by the response schema. The first full integration run exceeded the 10-minute shell timeout; both subsequent full runs passed with a 30-minute budget, taking about 12 minutes each. ### Verified Facts And Drift Initial inspection was on `main` at `19e7993`; the branch was subsequently rebased onto `70b8ca4`, preserving both Teams follow-ups. - `slack-bot/src/events/message-handler.ts:326-330` and `sessions/session-launcher.ts:194-203` still classified and launched without ownership scope. `sessions/control-plane-client.ts:74-81` discarded create denial details, and the follow-up path treated non-404 failures as transient. These are now scoped and denial-aware. - `slack-bot/src/events/dispatcher.ts:27-65` routes ordinary watched-channel messages through the separate actorless automation trigger path, rather than the interactive handler. Existing ingress is preserved; automation candidate selection now enforces channel ownership. - `control-plane/src/session/types.ts:36-67` shows that the callback service's local DO row lacks ownership/visibility. Gates use injected D1 index/binding reads, not that local row or participant records. - `slack-bot/src/target-clarification.ts:164-179` and `interactions/target-selection.ts:181` reloaded unscoped catalogs. Pending request scope now survives those paths, and suggestions/selection recheck the live binding. - Completion delivery uses a durable queue. A callback-time check alone could become stale, and ordinary service reads intentionally allow workspace-visible sessions. Publication-purpose reads close that gap while preserving the access resolver's existing contract. - `control-plane/src/session/snapshot-reader.ts:94-128` previously exposed no originating Slack channel. That enrichment was initially added for the move warning and has now been removed with ownership moves. - Existing callback signing uses `SERVICE_AUTH_SECRET_SLACK_BOT` and the bot's `SERVICE_AUTH_SECRET`; no additional Slack signing-secret configuration was needed. ### Deliberately Excluded - No migration, per-channel auto-response policy, or later-phase credential/token changes. - Repository-grant and environment-ownership catalog filters are not implemented here; they landed on main separately. This PR scopes Slack catalog reads with a signed `channel=slack:<id>` coordinate, from which the control plane derives the channel's current team. - Slack posting and KV persistence are not an atomic exactly-once protocol. Closure markers prevent stale mapping writes from reopening threads, but existing distributed delivery/propagation races remain. A scope change after the final successful authority read cannot recall an already-in-flight Slack post. - Browser verification used fixture data and mocked APIs, not a live Slack workspace or production authentication. ## Follow-up Fixes - **Team-owned Slack automations at create (`0940757ef`).** `POST /automations` checked watched channels against workspace ownership instead of the automation's owner team. A team automation watching its own team's bound channel was refused with `channel_team_mismatch`. One watching an unbound channel was accepted, although candidate selection would never fire it. Creation now checks against the owner team, as update and candidate selection already did. New integration tests cover a bound channel (created, and selected as a candidate), an unbound channel (409) and another team's channel (409). The first two fail with the old check. - **Steering fixtures (`4408da48c`).** `scheduler-slack-team-steering.test.ts`, added on main with team-owned automations, seeded team automations in an unbound channel. This PR's candidate rule excludes that by design, which caused the 10 failures in control-plane integration 1/2. The fixtures now give each team automation the channel's team, and use an unbound channel for the workspace case; no runtime code changed. - **Closed threads reopen (`94253ebcb`).** A thread closed by a binding change or a private session used to stay closed until its marker expired, even after the binding or visibility was restored. Only threads that saw activity during the change were affected. A reply in a closed thread now re-checks the channel binding and the `slack-post` publication read. When both allow it, the bot clears the closure tombstone and notice marker and delivers the reply; otherwise the thread stays closed. The control plane still enforces scope on every prompt and post. Tests cover reopening, staying closed for another team's channel or denied publication, and a closure that lands during a reopen. | Command | Result | | --- | --- | | `npm run typecheck -w @open-inspect/control-plane` | Passed | | `npx prettier --check` and `npx eslint` on the changed files | Passed | | `npm test -w @open-inspect/control-plane -- --maxWorkers=2 --silent` | 365 files, 6,240 tests passed | | `npm run test:integration -w @open-inspect/control-plane` on the automation, scheduler and Slack files (final head) | 16 files, 282 tests passed | | `npm run typecheck -w @open-inspect/slack-bot` | Passed | | `npm test -w @open-inspect/slack-bot -- --silent` | 39 files, 621 tests passed | ## Visual Verification Real changed components and repository styles were verified in local component previews with fixture data. Binding/unbinding, primary/source selection, saving the unbound policy, move-warning conditions, and mobile fit were exercised. The corrected warning has 15.00:1 light-theme text contrast. All captures are viewport screenshots; URLs record local capture provenance and the preview server has been stopped. | Capture | Viewport | Source | Uploaded artifact ID | | --- | --- | --- | --- | | Channels desktop | 1440x1000 | `http://127.0.0.1:5173/channels` | `3ad98e81fe8e054b0930b52c95d41def` | | Channels mobile | 390x844 | `http://127.0.0.1:5173/channels` | `dcaf42617faf4aacb67e7a1f30869901` | | Slack settings desktop | 1440x1000 | `http://127.0.0.1:5173/slack` | `bd25ac2aa2443b82650960885228491f` | | Slack settings mobile | 390x844 | `http://127.0.0.1:5173/slack` | `4ebfe6cc73b82a7b3223d04891ffbc06` | | Historical move preview desktop (removed) | 1440x1000 | `http://127.0.0.1:5173/move` | `f1f6e4c6888eaa6ccf0620d8c3d20da5` | | Historical move preview mobile (removed) | 390x844 | `http://127.0.0.1:5173/move` | `1edfda15f09e9773408c2435eab18419` | --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/f9d5970ec4d60dc821b243d734ac1f4b)* <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Team administrators can manage Slack channel bindings from a Channels tab, assign primary or source channels, and review binding changes in the audit log. * Slack settings let administrators choose whether unbound channels create workspace-level sessions or reject requests. * Slack routing and automations respect channel team ownership, with clearer feedback when access or posting is denied. * **Bug Fixes** * Prevented notifications and completion content from being posted to private sessions or channels bound to another team. * Closed threads no longer trigger replacement sessions or receive later updates. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com> Co-authored-by: waclaude <colemurray.cs+ghwaclaude@gmail.com>
…ages/locks/python-tools in the uv group across 1 directory (ColeMurray#2231) Bumps the uv group with 1 update in the /packages/sandbox-images/locks/python-tools directory: [urllib3](https://github.com/urllib3/urllib3). Updates `urllib3` from 2.7.0 to 2.8.0 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/urllib3/urllib3/releases">urllib3's releases</a>.</em></p> <blockquote> <h2>2.8.0</h2> <h2>🚀 urllib3 is fundraising for HTTP/2 support</h2> <p><a href="https://sethmlarson.dev/urllib3-is-fundraising-for-http2-support">urllib3 is raising ~$40,000 USD</a> to release HTTP/2 support and ensure long-term sustainable maintenance of the project. If your company or organization uses Python and would benefit from HTTP/2 support in Requests, pip, cloud SDKs, and thousands of other projects <a href="https://opencollective.com/urllib3">please consider contributing financially</a> to ensure HTTP/2 support is developed sustainably and maintained for the long-haul.</p> <p>Thank you for your support.</p> <h2>Security</h2> <p>Fixed the following security issues:</p> <ul> <li>The TLS configuration for HTTPS proxies could be ignored or overridden. (High severity, GHSA-8988-9cw3-xx77)</li> <li><code>HTTPResponse.stream()</code> and <code>read_chunked()</code> could buffer a chunk-size line of unbounded length in memory. (High severity, GHSA-vxq7-64xx-v4gw)</li> <li>Chunked Deflate streaming could enter an infinite loop. (Medium severity, GHSA-gh4c-6fx4-qh6g)</li> </ul> <blockquote> <p>[!IMPORTANT] urllib3 2.8.0 fixes HTTPS proxy TLS configuration being ignored or overridden by destination settings. Configurations relying on that behavior may require changes.</p> <p>Configure proxy CA certificates and client certificates in <code>proxy_ssl_context</code>, and proxy identity checks with <code>proxy_assert_hostname</code> or <code>proxy_assert_fingerprint</code>. Destination client certificates and identity overrides no longer apply to HTTPS forwarding proxy connections.</p> </blockquote> <blockquote> <p>[!NOTE] CVE IDs had not yet been assigned to these advisories at the time of release due to a backlog at GitHub's CNA.</p> </blockquote> <h2>Deprecations & Removals</h2> <ul> <li>Deprecated using an empty collection as the <code>Retry</code> option <code>allowed_methods</code> to retry any verb. (<a href="https://redirect.github.com/urllib3/urllib3/issues/5044">#5044</a>)</li> </ul> <h2>Features</h2> <ul> <li>Added <code>Url.auth_decoded</code> and <code>Url.auth_decoded_joined</code> convenience properties to the result of <code>parse_url()</code>. (<a href="https://redirect.github.com/urllib3/urllib3/issues/4945">#4945</a>)</li> <li>Added <code>basic_auth_encoding</code> and <code>proxy_basic_auth_encoding</code> parameters to <code>urllib3.util.make_headers()</code>. (<a href="https://redirect.github.com/urllib3/urllib3/issues/5092">#5092</a>)</li> </ul> <h2>Bugfixes</h2> <ul> <li> <p>Fixed response header handling to replace obsolete folded header lines (<code>obs-fold</code>) with spaces in accordance with RFC 9112, preventing raw CRLF sequences from appearing in header values such as <code>Set-Cookie</code>. (<a href="https://redirect.github.com/urllib3/urllib3/issues/1362">#1362</a>)</p> </li> <li> <p>Fixed usage of <code>proxy_ssl_context</code> with <code>ProxyManager</code> when <code>use_forwarding_for_https=True</code>. Passing <code>ssl_context</code> instead of <code>proxy_ssl_context</code> for HTTPS proxies in this configuration now emits a <code>FutureWarning</code> and will raise an error in v3.0. (<a href="https://redirect.github.com/urllib3/urllib3/issues/2577">#2577</a>)</p> </li> <li> <p>Changed behavior of the default <code>ConnectionPool.pool</code> initialization. <code>LifoQueue</code> is now resolved from the <code>queue</code> module after the <code>ConnectionPool</code> is instantiated instead of using the default cached <code>QueueCls</code> class property. This is done because sometimes the <code>queue.LifoQueue</code> is monkey-patched late in the program, such as by gevent. (<a href="https://redirect.github.com/urllib3/urllib3/issues/3289">#3289</a>)</p> </li> <li> <p>Raised <code>UnrewindableBodyError</code> instead of <code>ValueError</code> when retrying a request whose body had <code>tell()</code> but not <code>seek()</code>. (<a href="https://redirect.github.com/urllib3/urllib3/issues/3779">#3779</a>)</p> </li> <li> <p>Decoded percent-encoded SOCKS proxy credentials before authenticating with the proxy server. (<a href="https://redirect.github.com/urllib3/urllib3/issues/3785">#3785</a>)</p> </li> <li> <p>Fixed <code>HTTPResponse.drain_conn()</code> to discard unread response data in 64 KiB chunks (same as the default <code>amt</code> when doing <code>HTTPResponse.stream(...)</code>). (<a href="https://redirect.github.com/urllib3/urllib3/issues/5019">#5019</a>)</p> </li> <li> <p>Fixed <code>is_ipaddress()</code> to detect non-standard IPv4 forms accepted by <code>socket.connect</code>, such as hex (<code>0x7f000001</code>), octal (<code>0177.0.0.1</code>), and decimal integers (<code>2130706433</code>), ensuring SSL certificate verification uses the correct mode for these addresses. (<a href="https://redirect.github.com/urllib3/urllib3/issues/5029">#5029</a>)</p> </li> <li> <p>Fixed <code>HTTPConnectionPool.urlopen</code> raising a misleading <code>FullPoolError</code> instead of <code>ValueError</code> when called with an invalid <code>timeout</code> argument on a pool created with <code>block=True</code>. (<a href="https://redirect.github.com/urllib3/urllib3/issues/5059">#5059</a>)</p> </li> <li> <p>Fixed port-zero handling to preserve explicit <code>:0</code> values instead of substituting the default ports 80 or 443 in URL parsing, pool selection, proxy configuration, <code>connection_from_url()</code>, and HTTP/2 request authority. (<a href="https://redirect.github.com/urllib3/urllib3/issues/5071">#5071</a>, <a href="https://redirect.github.com/urllib3/urllib3/issues/5101">#5101</a>)</p> </li> <li> <p>Fixed a bug where <code>PoolManager</code> passed the <code>assert_hostname</code> and <code>assert_fingerprint</code> parameters to HTTP connection pools. (<a href="https://redirect.github.com/urllib3/urllib3/issues/5077">#5077</a>)</p> </li> <li> <p>Fixed <code>HTTPConnectionPool.urlopen()</code> and HTTP proxy forwarding to strip URL fragments from absolute request targets before sending requests. (<a href="https://redirect.github.com/urllib3/urllib3/issues/5079">#5079</a>)</p> </li> <li> <p>Added safeguards to the proxy tunneling code to prevent potential security issues when handling invalid characters in the proxy host and HTTP headers. This change affects users of Python 3.10, Python 3.11, and Python 3.12 when the standard library does not contain the fix; those on newer Python versions should upgrade to 3.13.14+ or 3.14.5+ to get the same security fixes. (<a href="https://redirect.github.com/urllib3/urllib3/issues/5091">#5091</a>)</p> </li> <li> <p>Fixed <code>HTTPSConnection.connect()</code> overriding <code>ProxyConfig.ssl_context</code>'s certificate policy and proxy identity checks with the target connection's TLS settings when forwarding through an HTTPS proxy.</p> <p><code>HTTPSConnection</code> no longer applies target SNI, assertions, or client credentials to forwarding proxy handshakes and continues to use its <code>ssl_context</code> as a fallback when an HTTPS proxy forwards an HTTP target. (<a href="https://redirect.github.com/urllib3/urllib3/issues/5093">#5093</a>)</p> </li> <li> <p>Fixed URL parsing to more strictly enforce RFC 3986 host syntax, rejecting invalid host input such as raw spaces and control characters, malformed percent-encodings, and percent-encoded control characters in HTTP(S) hosts and IPv6 zone identifiers, including proxy CONNECT tunnel targets. Host normalization now also follows RFC 3986 normalization rules for percent-encoded octets by decoding percent-encoded unreserved characters and uppercasing the hexadecimal digits of retained percent-encoded octets. (<a href="https://redirect.github.com/urllib3/urllib3/issues/5095">#5095</a>)</p> </li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/urllib3/urllib3/blob/main/CHANGES.rst">urllib3's changelog</a>.</em></p> <blockquote> <h1>2.8.0 (2026-09-15)</h1> <h2>Security</h2> <p>Fixed the following security issues:</p> <ul> <li>The TLS configuration for HTTPS proxies could be ignored or overridden. (High severity, <code>GHSA-8988-9cw3-xx77 <https://github.com/urllib3/urllib3/security/advisories/GHSA-8988-9cw3-xx77></code>__)</li> <li><code>HTTPResponse.stream()</code> and <code>read_chunked()</code> could buffer a chunk-size line of unbounded length in memory. (High severity, <code>GHSA-vxq7-64xx-v4gw <https://github.com/urllib3/urllib3/security/advisories/GHSA-vxq7-64xx-v4gw></code>__)</li> <li>Chunked Deflate streaming could enter an infinite loop. (Medium severity, <code>GHSA-gh4c-6fx4-qh6g <https://github.com/urllib3/urllib3/security/advisories/GHSA-gh4c-6fx4-qh6g></code>__)</li> </ul> <p>.. caution::</p> <pre><code>urllib3 2.8.0 fixes HTTPS proxy TLS configuration being ignored or overridden by destination settings. Configurations relying on that behavior may require changes. <p>Configure proxy CA certificates and client certificates in <code>proxy_ssl_context</code>, and proxy identity checks with <code>proxy_assert_hostname</code> or <code>proxy_assert_fingerprint</code>. Destination client certificates and identity overrides no longer apply to HTTPS forwarding proxy connections. </code></pre></p> <h2>Deprecations & Removals</h2> <ul> <li>Deprecated using an empty collection as the <code>Retry</code> option <code>allowed_methods</code> to retry any verb. (<code>[#5044](urllib3/urllib3#5044) <https://github.com/urllib3/urllib3/issues/5044></code>__)</li> </ul> <h2>Features</h2> <ul> <li>Added <code>Url.auth_decoded</code> and <code>Url.auth_decoded_joined</code> convenience properties to the result of <code>parse_url()</code>. (<code>[#4945](urllib3/urllib3#4945) <https://github.com/urllib3/urllib3/issues/4945></code>__)</li> <li>Added <code>basic_auth_encoding</code> and <code>proxy_basic_auth_encoding</code> parameters to <code>urllib3.util.make_headers()</code>. (<code>[#5092](urllib3/urllib3#5092) <https://github.com/urllib3/urllib3/issues/5092></code>__)</li> </ul> <h2>Bugfixes</h2> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/urllib3/urllib3/commit/b1d30ab61fe0db8f11092805e8c5ac43e091064a"><code>b1d30ab</code></a> Release 2.8.0</li> <li><a href="https://github.com/urllib3/urllib3/commit/9016d7e8afc68185496ef07f3c3a4a743d04922e"><code>9016d7e</code></a> Skip <code>test_read_chunked_with_trailing_data_does_not_hang</code> for brotlicffi (<a href="https://redirect.github.com/urllib3/urllib3/issues/5258">#5258</a>)</li> <li><a href="https://github.com/urllib3/urllib3/commit/9101f581a8b3659af23b6ff335ae77200ca33533"><code>9101f58</code></a> Fix <code>nox -s docs</code> warning (<a href="https://redirect.github.com/urllib3/urllib3/issues/5256">#5256</a>)</li> <li><a href="https://github.com/urllib3/urllib3/commit/cd770b059b543be29298ea5c52afb0b1b090f5ed"><code>cd770b0</code></a> Merge commit from fork</li> <li><a href="https://github.com/urllib3/urllib3/commit/ea2ad7b21a80da3632f80016526a18864586077f"><code>ea2ad7b</code></a> Merge commit from fork</li> <li><a href="https://github.com/urllib3/urllib3/commit/0716e31534345dc1599ea95d903c79f276239bd8"><code>0716e31</code></a> Fix loading unencrypted client keys with a password in pyOpenSSL (<a href="https://redirect.github.com/urllib3/urllib3/issues/5255">#5255</a>)</li> <li><a href="https://github.com/urllib3/urllib3/commit/43c68c8b43a9dcb44ed2cf4ec91384ca0d46b37d"><code>43c68c8</code></a> Test pickling of <code>InvalidChunkLength</code> (<a href="https://redirect.github.com/urllib3/urllib3/issues/5247">#5247</a>)</li> <li><a href="https://github.com/urllib3/urllib3/commit/308b279b3fb28e7bee952e152ec5baeb5bfd0817"><code>308b279</code></a> Share security policy between GitHub and Read the Docs (<a href="https://redirect.github.com/urllib3/urllib3/issues/5253">#5253</a>)</li> <li><a href="https://github.com/urllib3/urllib3/commit/53fa0731b27d4b71ab0755ea5b896422d005d706"><code>53fa073</code></a> Add policy on duplicate pull requests (<a href="https://redirect.github.com/urllib3/urllib3/issues/5252">#5252</a>)</li> <li><a href="https://github.com/urllib3/urllib3/commit/5f2a6a843d0100d1351c3f94d58581ca98d17267"><code>5f2a6a8</code></a> Assert on the ALPN extension in test_tunnel_sets_http_11_alpn (<a href="https://redirect.github.com/urllib3/urllib3/issues/5232">#5232</a>)</li> <li>Additional commits viewable in <a href="https://github.com/urllib3/urllib3/compare/2.7.0...2.8.0">compare view</a></li> </ul> </details> <br /> [](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> Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com>
…urray#2232) ## Summary Removes the Save button from the session visibility control. Each control now saves on its own: - **Visibility dropdown** — choosing a different visibility saves right away. If child sessions are included and the new visibility isn't private, the existing confirmation dialog appears first. Cancelling leaves the visibility unchanged. - **Include child sessions checkbox** - **Checking it** applies the session's current visibility to its child sessions (the server already supports a same-visibility cascade). This also asks for confirmation when the visibility isn't private. - **Unchecking it** saves nothing, because there's no persisted setting to revert. It only means later visibility changes affect this session alone. - An "Updating..." label replaces the button label while a save is in flight, and all controls are disabled until it finishes. - If a save fails, the dropdown goes back to the previous visibility, unless "Retry without child sessions" is offered; then it keeps the failed choice so the retry can use it. - The team owner-membership warning also shows while the confirmation dialog is open for a change to team visibility. ## Testing - `vitest run src/components/session-controls.test.tsx` (27 passed), rewritten for autosave behavior - `tsc --noEmit`, eslint, prettier --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/ad4b14f9cdc0e17614c06cdb8b2e82af)* <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Session Visibility** * Visibility changes save immediately when selected; a separate Save action is no longer needed. * Applying visibility changes to child sessions requires confirmation. * Controls are unavailable during refreshes, when permission to change visibility is unavailable, or when the required owner is missing. * After a retryable failure, retry the selected visibility change without applying it to child sessions. Other failed changes revert to their previous settings. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com> Co-authored-by: waclaude <colemurray.cs+ghwaclaude@gmail.com>
) ## Summary Remove **3,414 test cases and 278 complete test files** across eight packages, reducing the measured suites from **14,587 to 11,173 cases (23.4%)**. The reduction primarily targets mocked SQL, handler delegation, orchestration, helper tests, and repeated parameter combinations. Every measured coverage metric remains within **three percentage points of its baseline, per package**. The largest observed decrease is **2.38 percentage points**, and all coverage denominators are unchanged between the before/after measurements. | Package | Tests Removed | Largest Coverage Drop | | --- | ---: | ---: | | Control Plane | 2,075 | 2.36 pp | | Web | 827 | 2.38 pp | | Shared | 190 | 2.25 pp | | Slack Bot | 115 | 2.27 pp | | Linear Bot | 39 | 1.85 pp | | GitHub Bot | 12 | 0.57 pp | | Sandbox Runtime | 126 | 0.90 pp | | Modal Infrastructure | 30 | 0.00 pp | ## Retained Coverage - Keep all **140 control-plane workerd integration files**, using real D1 and Durable Object storage. - Keep all Node-host and storage conformance suites, and web component/hook integration suites. - Keep core authentication, signature, cookie identity, migration, architecture, and type contracts. - Retain focused manager signature, spawn-admission, and late-provider-result race regressions. - Preserve real local-process, Git, shell, socket, and tool tests in the sandbox runtime. ## Coverage Tooling - Run control-plane Node and workerd coverage together using Istanbul; V8 coverage cannot run inside workerd. - Add shared-package coverage commands and Python `pytest-cov` dependencies. - Add JSON summaries and TypeScript coverage floors based on baseline minus three percentage points. - Document full before/after metrics, methodology, tradeoffs, and reproduction commands in `docs/TEST_REDUCTION.md`. - Ignore generated Python coverage data. No production application code changes. The existing CI test commands are unchanged and do not automatically enable the new coverage floors. Python coverage is measured but has no new automatic threshold. ## Validation - Affected suites: **11,169 passed, four unchanged skips**. - Combined control-plane unit/integration coverage passes; the ordinary Node test command also passes all 4,057 remaining cases. - All affected TypeScript and Python coverage suites pass, including standalone sandbox-runtime environment verification. - Docs tests and native Node runtime tests pass. - Repository ESLint, TypeScript typechecks, and Prettier checks pass. - Python Ruff lint/format checks and `git diff --check` pass. - Commit hooks pass without being skipped. ## Tradeoffs Coverage overlap is not assertion equivalence. Some isolated input permutations, error wording, provider error-classification matrices, and interleavings no longer have dedicated assertions. Retained integration tests cover their broader behavior; this PR does not claim to preserve every old assertion or provide a mathematically optimal minimum test set. The coverage comparison is against the session baseline at `d343cac`; full metrics are recorded in the documentation. --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/f244b739ed5b9f7f15ce35fc004131d3)* --------- Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com> Co-authored-by: waclaude <colemurray.cs+ghwaclaude@gmail.com>
## Summary When an artifact (screenshot/video) is expanded in the media lightbox, you can now press **←/→** to move to the previous/next artifact without closing and reopening. ## Design - `MediaLightbox` now owns navigation: it takes the ordered `artifacts` list, `selectedArtifactId`, and `onSelectArtifact(id | null)` (null closes) instead of a single `artifact` + `open`/`onOpenChange`. The session page passes `mediaArtifacts`, which uses the same filter/order as the sidebar Artifacts section, so left/right matches the visual order. - Arrow keys are handled by a `window` keydown listener while the lightbox is open, so navigation keeps working even if focus lands on a disabled button at either end. - Navigation stops at the first/last artifact (no wrap-around). - Arrow keys are left alone when focus is in a `<video>` (native seeking) or a text field, and modified arrows (Alt/Ctrl/Meta/Shift) are ignored. - For discoverability and pointer/touch users, prev/next buttons and an "N of M" counter (`aria-live="polite"`) appear when there is more than one artifact. - Adds `ChevronLeftIcon` to the shared icons. ## Testing - `npx vitest run src/components/screenshot-media.test.tsx src/app/(app)/(sidebar)/session/[id]/page.test.tsx`: 15 tests pass, including new tests for arrow-key navigation, end-of-list behavior, the buttons, and the video-focus/modifier cases. - Web package `tsc --noEmit` and ESLint pass. --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/7ff662ef5d768fbc1e4ca9cd646548a1)* <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Navigate between session media artifacts using previous and next buttons or the left and right arrow keys. A position indicator appears when multiple artifacts are available. * Arrow-key navigation respects editable fields, video controls, and modified key presses. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com> Co-authored-by: waclaude <colemurray.cs+ghwaclaude@gmail.com>
…eMurray#2225) ## Summary - Add a signed, actorless `github-bot` routing lookup using numeric repository identity: linked PR session team, sender's granted active team memberships, then workspace ownership. Multiple eligible memberships use the sender's most recently created session in that repository. - Match GitHub event automations by repository ID and repository/installation grants. Each matching automation executes with its own executor and team. Revoked grants produce a deduplicated, terminal `unauthorized` run with `repo_not_granted`, without creating a session or sandbox. - Carry repository IDs through webhook validation and normalization. Use current webhook display names for renamed repositories and refuse a different numeric identity returned during launch resolution. - Resolve routing after existing bot allowlist checks, send explicit `teamId` on creation, preserve membership/grant rechecks, and post explanatory refusal comments. Failed refusal posts use the existing delivery retry path. - Keep legacy auto-review workspace-owned, mark both settings deprecated, and link the existing **Review new PRs** automation template. Render grant-denied automation history distinctly. Closes https://linear.app/colemurray/issue/COL-209 ## Checkpoint Report ### Validation Heavy validation ran sequentially with one Vitest worker. The branch was created from `main` at `756d0dc` and rebased onto `b8c9a80` before the final control-plane validation. | Command | Result | | --- | --- | | `npm run build -w @open-inspect/shared` | Passed | | `npm run typecheck` | Passed across all workspaces; repeated after rebase | | `npm run lint:fix` | Passed; repeated after rebase | | `npm run lint:sql-portability` | Passed; repeated after rebase | | `npm test -w @open-inspect/control-plane -- --maxWorkers=1` | 6,180 passed after rebase | | `npm run test:integration -w @open-inspect/control-plane -- --maxWorkers=1` | 1,948 passed, 1 skipped after rebase; 671.81 seconds | | `npm test -w @open-inspect/web -- --maxWorkers=1` | 2,785 passed | | `npm test -w @open-inspect/github-bot -- --maxWorkers=1` | 249 passed | | `npm test -w @open-inspect/shared -- --maxWorkers=1` | 1,138 passed | | Focused final run-history tests | 11 passed | | Scoped Prettier check and `git diff --check` | Passed | Visual verification used the actual `/settings/integrations/github` application route with mocked browser API fixtures, at desktop `1512x982` and mobile `390x844`. Both deprecation notices fit the existing layout. Clicking the template pointer opened `/automations/new?template=review-new-prs` with the expected name, instructions, and PR-opened trigger. This verifies rendering and navigation, not real OAuth or persistence. ### Red Tests And Tool Failures Before implementation, the new pure status test had 1 failure and 1 pass. The new automation-routing integration tests had 6 failures and 3 passes. A subsequent late-revocation regression test also failed before its fix. Representative failure messages, verbatim: ```text AssertionError: expected 'completed' to be 'unauthorized' // Object.is equality AssertionError: expected { triggered: +0, skipped: +0, …(1) } to deeply equal { triggered: 3, skipped: 1, …(1) } AssertionError: expected [] to deeply equal [ ObjectContaining{…} ] ``` These cases are green in the final suites, including real D1 and SessionDO coverage for fanout, executor/team ownership, numeric-ID rename routing, revocation at candidate selection and guarded admission, and sessionless denied history. The first full integration attempt used a ten-minute shell timeout and did not complete: ```text shell tool terminated command after exceeding timeout 600000 ms. If this command is expected to take longer and is not waiting for interactive input, retry with a larger timeout value in milliseconds. ``` It passed when rerun with a 1,800,000 ms shell timeout, and passed again after rebase. An initial broad Prettier file list included generated Vitest snapshots, which Prettier cannot parse: ```text [error] No parser could be inferred for file "/workspace/background-agents/packages/control-plane/test/integration/__snapshots__/hono-route-catalog-conformance.test.ts.snap". [error] No parser could be inferred for file "/workspace/background-agents/packages/control-plane/test/integration/__snapshots__/route-admission-matrix.test.ts.snap". ``` The corrected check targeted changed TypeScript/TSX/Markdown files and passed; snapshot correctness is covered by integration tests. ### Verified Facts And Contract Changes - Team-owned environments and automations are merged. Candidate lookup has moved to `scheduler.ts:1114-1122` and `automation-store.ts:1535-1551` on the inspected base; the old query still matched owner/name without grants. GitHub now has an ID/grant-aware query; Linear retains its existing path. Route inventory becomes 208 routes and 157 paths. - Numeric IDs existed in `automation_repositories.repo_id` and `team_repository_grants.repo_external_id`, but both shared webhook validation and normalized GitHub event contracts omitted them. Those contracts and all supported GitHub normalizers now preserve the numeric ID. - The merged firing pipeline returned internal `unauthorized` results but persisted no denied event run. Its existing grant reason was `target_team_missing_grant`, and the public status unions did not include `unauthorized`. This PR implements the requested literal terminal run status, adds atomic denial persistence, and updates SQL/TypeScript derived invocation status and UI rendering. Other firing sources retain their existing denial behavior. - D1 `sessions` has no scalar `repo_id` column. Sender-session tie-breaking uses numeric IDs from `session_repositories`, including secondary repositories, with no display-name fallback. - Actorless environment reads cannot read team-owned environments. Routed-team bot lookups therefore use the existing sender-actor authorization path; incompatible environment ownership falls back to the trigger repository. - This uses existing D1 migration `0083`; there are no D1 or DO migrations, new tables, or columns. Enforcement defaults are unchanged. Routing/grant and creation membership checks apply in every mode; existing session `off`, `shadow`, and `on` behavior is untouched. ### Deliberately Left Out - Autofix routing, Linear bindings, and Slack routing are unchanged. - Auto-review removal is not included. The existing automation template is reused rather than introducing a replacement template here. - `requireTeamOnCreate` is not bypassed: workspace fallback and legacy auto-review remain subject to the existing creation policy. - Legacy GitHub automation selections without resolved IDs do not fall back to names. The changelog explains reselection and saving to resolve IDs, and the required coordinated bot/control-plane upgrade. - No numeric-ID index migration was added. The existing automation selection schema lacks that index; schema/index changes remain outside this PR. --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/93c11db4019aaaaabe6bc03e48e955b9)* <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * GitHub events route to teams based on repository access and pull request context, with workspace routing as a fallback. * Automation history shows repository-access denials as “Unauthorized,” with an explanatory message and no session link. * The “Review new PRs” template can require team ownership when opened from GitHub auto-review settings. * **Bug Fixes** * GitHub automation matching uses repository IDs, so repository renames don’t misdirect events. Events without a valid repository ID are rejected. * Grant changes during event processing are retried, and failed event forwarding can be retried without repeating completed processing. * **Documentation** * GitHub auto-review settings are marked deprecated and link to the template. Existing auto-review continues to create workspace-owned sessions. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com> Co-authored-by: waclaude <colemurray.cs+ghwaclaude@gmail.com>
…ay#2219) ## Problem Found in review. A mounted automation list keeps showing previously loaded automations after the list refetch is denied. Example: a user is viewing a team's automations, leaves the team (or loses access to it), and the next revalidation of `/api/automations?teamId=…` returns 403. `useAutomations` reports the 403 as `error`, but `automations` still contains the team's automation names, so the list and its row actions stay on screen. The detail and invocation hooks in the same file already hide cached data on 401/403/404 via `isTerminalAutomationError`; the list hook only applied that guard to its own `retained` copy. ## Cause `packages/web/src/hooks/use-automations.ts:71-75`: ```ts const pages = data ?? (retained && retained.key === listKey && !isTerminalAutomationError(error) ? retained.pages : undefined); ``` SWR keeps `data` when a revalidation throws, so after a terminal refetch failure `data` is still the last successful pages and wins the `??` before the terminal-error check is reached. ## Fix Check `isTerminalAutomationError(error)` before either cached source. Transient failures (network, 5xx, contract errors) still keep the last loaded pages, as the existing comment describes. ## Verified - New test `after a %i refetch shows %j for a loaded list` (403 / 404 / 500) in `use-automations.test.tsx`: the 403 and 404 cases fail on `main` (`expected [ { id: 'auto-2', … } ] to deeply equal []`, with `error` already set to the 403); all pass with the fix. The 500 case pins that transient failures still keep the list. - `npm run typecheck` exits 0. - `packages/web` vitest: 270 files / 2778 tests pass. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Automation lists no longer display previously loaded results after an authentication or access error (401 or 403) or when the requested resource is not found (404). * Previously loaded automations remain visible if a temporary server error occurs (500), so the list isn’t unnecessarily cleared. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
…leMurray#2221) ## Problem When editing, the environment form always submits the current repository list, even when it is unchanged. The update route treats a present `repositories` field as a replacement and always re-resolves and re-authorizes it. Edits the API accepts with the field omitted are therefore rejected from the UI: - **Revoked team grant.** A team environment's grant for one of its repositories is revoked. Disabling prebuilds is how ColeMurray#2205 says to handle this ("disabling prebuilds on an environment with revoked grants still works"). From the form it returns `409 target_team_missing_grant`, and so does renaming or re-describing an environment whose prebuilds are already off. `PUT /environments/:id` with `{ "prebuildEnabled": false }` (or a name/description change while prebuilds stay off) succeeds. - **No `repositories.use`.** A team lead who manages the environment but lacks `repositories.use` gets `403 permission_required` on every save from the form. The route lets that user edit metadata while leaving repositories alone (`allows granted unchanged members without repositories.use or replacing them` in `environments-target-denied.test.ts`). ## Cause `packages/web/src/components/settings/environment-form.tsx:129-143` builds `repositories` from the current selection on every submit. In `packages/control-plane/src/routes/environments.ts:328-344`, a present `repositories` field goes through `resolveAuthorizedRepositories` (the `repositories.use` preflight plus the team grant check) whatever the prebuild state. An omitted field only re-checks stored repositories when prebuilds stay enabled on a team environment. ## Fix In edit mode, the form leaves out `repositories` when the selection is unchanged: same repositories, same order (the order sets the primary repository) and same base branches. Names compare as lowercase full names, matching the selection keys. Create submissions and changed selections are unaffected. `EnvironmentFormValues.repositories` becomes optional to match `updateEnvironmentInputSchema`. ## Verified - New `environment-form.test.tsx` case: renaming a team environment without touching its repositories submits `{ name, description, prebuildEnabled }` with no `repositories`. The stored owner is mixed-case. Before the fix it fails because the submitted values include the unchanged `repositories` array. After the fix it passes. - Edit-mode changes still send the full selection. A new case changes only one repository's base branch (nested `group/subgroup` owner) and expects both repositories with the new branch. With the branch comparison replaced by `true` it fails, and it passes as written. The existing reorder case still sends the new order. Two edit-mode tests asserted the unchanged list as a side effect. The ownership test drops that assertion, and the nested-namespace parsing test now submits through create mode, where the list is always sent. - Control-plane behaviour, checked with the route test harness (not part of this PR). Prebuild-disabled team environment with no grant: `{ name, description, prebuildEnabled: false }` returns 200, and the same body plus the unchanged `repositories` returns 409 `target_team_missing_grant`. With only `environments.manage`, the resent list returns 403 `permission_required`. - `npm run typecheck` passes. The `@open-inspect/web` vitest suite passes.
…#2233) ## Summary Reorganizes the analytics page into a one-row header and five tabs. The page opened with a hero card that filled most of the first screen (title, a caveat paragraph, three badges, and the two filters with five lines of help text), then ran to about 8,000px of cards at 1440×900, with usage, cost, tokens and people interleaved and a heading only on the pull request section. - **Header**: one toolbar row with the title, the window ("Sep 3 – Oct 2"), a freshness indicator, and the scope and range filters as segmented controls. The caveats move behind **About this data** (an icon on phones) and the scope descriptions behind an info button. - **Overview**: headline numbers grouped by what they're scoped to (**Human sessions**: sessions, active users, spend, completion rate; **Pull requests · every source**: PRs merged, cost per merged PR), sessions-per-day and PRs opened/merged charts, and top-five lists for repositories, models and people that link into their tabs. About one screen at 1440×900. - **Usage**: sessions per day, where sessions start (source → attributed users, keeping the attribution notes from ColeMurray#2218), how sessions ended, repositories, and automations for the Automations and All scopes. - **Cost**: spend, cache hit ratio, subscription-billed share, tokens, and private-session spend for Owners and Administrators (the API already returned `privateSessionsCostUsd`; the page never showed it); cost by model, provider and harness; most expensive runs; token totals. - **Pull requests**: the existing PR metrics, plus the outcome mix and cost per merged PR by model or harness. The scope control is disabled on this tab because PR metrics ignore scope. - **People**: the sortable per-person table, with a daily-sessions sparkline per person in place of the "Sessions Over Time" chart, which drew one overlapping area per user. Behavior changes: - Range, scope and tab are in the URL (`/analytics?days=7&scope=automation&tab=cost`), so a view can be linked and survives a reload. Defaults are left out. Changes go through `window.history.replaceState`, which Next syncs into `useSearchParams`; each change builds on the live URL, so quick successive changes compose instead of overwriting each other, and no server render is involved. - `useAnalyticsDashboard` uses SWR `keepPreviousData` and returns `{ dashboard, loading, stale, validating, error }`. While a new range or scope loads, the previous snapshot stays on screen, dimmed and `aria-busy`, and the header reads "Loading…". If that request fails, the page says the selected range failed to load and the header reads "Showing the previous selection" instead of a fresh "Updated" time. - Daily series are zero-filled across the window. The API omits days without activity, and the old charts drew straight across them. - **PRs merged** on the overview counts merges during the window, the same population as its daily-merges sparkline and the average time to merge. Cost per merged PR stays on the PRs opened in the window, as before. - Completion rate is "—" until a session finishes, everywhere (previously tables showed "0%"); sessions per person divides attributed sessions by attributed people, leaving out sessions with no recorded user. - Costs show cents below $1,000 and whole dollars above; sub-dollar values used to show four decimals ("$0.8565 per session"). Analytics has its own `formatAnalyticsCost`; `formatSessionCost` is unchanged. API change: - The session timeseries (dashboard `timeseries` and `GET /analytics/timeseries`) now keys each day's groups by the same user key as the user breakdown (user ID, else SCM login, else `__unknown__`) instead of by display name. Two people with the same name were merged into one series, which was fine for the old chart's name legend but wrong for per-person sparklines. The removed chart was the only consumer. The integration tests that pinned name grouping now pin identity grouping. Code: - One sortable `AnalyticsTable` replaces the dimension table, harness cards, repository and model bar charts, and the user table. Sort values may be `null`; those rows sort last in both directions, so callers no longer invent sentinels. `AnalyticsKpiStrip`/`AnalyticsKpiGroups`, `AnalyticsRankedBars`, `AnalyticsTrendChart` and `AnalyticsPanel` replace the other card styles. `SegmentedControl` is added to `components/ui` as a thin `ToggleGroup` wrapper. - Removes the 11 components the tabs replace, with their tests. Their behaviors are covered by the new tests: origin attribution and duplicate display names, subscription "—" vs 0, completion over finished sessions, sorting, and the PR-funnel-only rule for PR counts in the headline. - `DEFAULT_ANALYTICS_DAYS` moves to `@open-inspect/shared` beside `DEFAULT_ANALYTICS_SCOPE`, so the page default and the API default are one constant. - The new styles avoid Tailwind opacity modifiers on theme colors. Theme colors are bare `var()`s, so classes like `bg-destructive/60` emit no CSS; that is why the old "Cancelled" status bar never rendered. The same pattern exists elsewhere in the web app and is left for a separate change. - `administration/analytics.mdx` is rewritten for the tabbed page. ## Testing - `npm test -w @open-inspect/web` (2,847 passed), `npm run typecheck`, eslint, prettier, `npm run build -w @open-inspect/web` - `npm test -w @open-inspect/shared` (1,133 passed); control-plane typecheck, `src/db/analytics-store.test.ts`, `src/routes/analytics.test.ts`, and `test/integration/analytics.test.ts` (16 passed in workerd) - `npm test -w @open-inspect/docs` (44 passed), `npm run build -w @open-inspect/docs` - Manually against synthetic dashboard data in a local preview: every tab at 1440px and 390px, light and dark; two filter clicks without waiting both land in the URL and the page re-renders; tabs and overview links; reload keeps the view; per-person sparklines for every row; About this data on phones; no console errors. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Redesigned Analytics as five tabs: Overview, Usage, Cost, Pull requests, and People, with metrics, charts, tables, and breakdowns tailored to each view. * Added URL-persisted time range, session scope, and tab selections. Previous results remain visible while new filters load. * Added sortable, expandable analytics tables and clearer session attribution, cost, and pull-request details. * **Documentation** * Updated Analytics guidance with tab contents, filtering behavior, calculations, refresh details, and troubleshooting information. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
…leMurray#2216) ## Problem With `web_platform = "cloudflare"`, every page of the web app logs `ReferenceError: __name is not defined` in the browser console. By default wrangler bundles the worker with esbuild's keep-names, which wraps named functions in `__name(fn, "name")`. next-themes serializes its theme function into an inline `<script>`. That script then carries the `__name(...)` call into the browser, where `__name` does not exist. The script throws before it applies the saved theme. React applies the theme only after hydration, so users whose saved theme is not the default see a flash of the wrong theme. This is the known OpenNext issue [opennextjs/opennextjs-cloudflare#1249](opennextjs/opennextjs-cloudflare#1249). OpenNext's guidance is to set `keep_names = false`: https://opennext.js.org/cloudflare/howtos/keep_names. ## Fix Set `keep_names = false` in the two places that produce the web worker's wrangler config: - `terraform/environments/production/web-cloudflare.tf`, which generates the production config; - `packages/web/wrangler.toml`, the checked-in local config. The only documented downside is less readable function names in debugging tools. ## Verified - Dry-run bundle with the repo's wrangler (4.141.0) of a worker that serializes a function containing a named function into an inline script, the same shape as next-themes: 2 `__name(` calls with the default setting, 0 with `keep_names = false`. - Deployed on a production Cloudflare web deployment: the served HTML's next-themes inline script (`localStorage.getItem(...)`) now contains no `__name(` call, on `/`, `/sessions` and `/access-denied`. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Resolved a browser error affecting the theme-switching script in the production web app. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Ojas Mor <ojas.m233@gmail.com> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…leMurray#2236) Supersedes ColeMurray#2224 (by @rhlsthrm). This version is rebased onto current `main` and includes one small follow-up from review. ## Changes from ColeMurray#2224 - **Merge conflict resolved.** Only `hono-route-catalog-conformance.test.ts.snap` conflicted, because new routes on `main` shifted the fixture indices. I kept `main`'s snapshot and reapplied the single `POST /automations` line change. - **Docs.** `administration/workspace-access.mdx` now says that creating an automation also requires permission to create sessions, matching the existing note that session creation requires repository/environment use. ## Review notes - `requireAll(...)` keeps the old route's audit setting (`auditAllowed: true`) and service policy (actor only, with no actorless grants). Only the `allOf` list changes. - `POST /automations` is the only path that inserts automations (`AutomationStore`), so the route check covers creation completely. - Every web create entry point (list, team tab, `/automations/new`, templates) goes through `useCanCreateAutomation`. ## Verification (after rebase) - `automation-executor-permissions` + `hono-route-catalog-conformance` integration tests: 24 passed - Web automations page/component tests: 180 passed - `typecheck` (control-plane, web): passed - `prettier --check` on touched files: clean --- ## Original description ## Problem Found in review. `POST /automations` only requires `automations.create`, but the creator becomes the automation's executor, and every run is authorized against the executor by `isAutomationExecutionAuthorized`, which requires `sessions.create`. A user whose custom role grants `automations.create` without `sessions.create` gets a `201` for an enabled automation, workspace- or team-owned, that can never run: - the first scheduled firing is recorded as a skipped invocation with `execution_authorization_denied` and the automation is paused; - event firings are skipped; - the creator's own **Trigger Now** is refused. The other executor writes already refuse this state. Executor reassignment (`PATCH /automations/:id`) rejects a candidate without `sessions.create`, and target edits refuse to leave the automation unrunnable by its executor. Create already checks the target-use half of the rule (`repositories.use` / `environments.use` through `authorizeSessionTarget`), so `sessions.create` is the only execution permission it skips. ## Cause - `packages/control-plane/src/routes/automation-crud.ts`: the create route authorizes `requirePermission("automations.create")` only. - `packages/control-plane/src/automation/authorization-guard.ts`: execution requires the executor to hold `sessions.create`. ## Fix Require `sessions.create` alongside `automations.create` in the create route's declarative authorization, composed with `requireAll(...)` as `POST /sessions/:id/children` does. A denial returns the standard route-level `permission_required` response for `sessions.create`. The web create gate (`useCanCreateAutomation`) now requires `sessions.create` too, so a user without it no longer sees **Create Automation**, **Browse templates**, or the create form only to get a `403` on save. The team Automations tab uses the same gate. Built-in roles are unaffected: every built-in role with `automations.create` also has `sessions.create`. The route catalog snapshot changes for this one route. ## Verified (original PR, pre-rebase) - New integration test in `automation-executor-permissions.test.ts`, `rejects {team,workspace} creation by an executor without sessions.create`: fails on `main` (`expected 201 to be 403`) and passes with the fix. - The list-page test `hides create and template entry points without sessions.create` fails against the old client gate and passes with the fix. The other automation page tests now grant both permissions. - On `main`, a throwaway integration test confirmed the failure mode for both ownerships. The created automation was enabled, the first tick returned `{ processed: 0, skipped: 1, failed: 0 }`, the automation was paused with a skipped `execution_authorization_denied` invocation and no session, and `trigger()` by the creator threw `AutomationExecutionUnauthorizedError`. - `npm run typecheck`: passed. - `npm test -w @open-inspect/control-plane`: 6,176 passed. - `npm run test:integration -w @open-inspect/control-plane`: 1,885 passed, 1 skipped. Only the `POST /automations` snapshot line changed. - Web tests for the automations list, new, and templates pages and `automation-collection`: 36 passed. - `prettier` and `eslint` on the touched files: clean. --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/92af5b49885b98f65256f86a6a14707e)* <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Access Changes** * Creating an automation now requires permission to create both automations and sessions. Users without session-creation permission receive an access-denied response, and no automation is created. * Automation creation and template links are hidden when either required permission is missing. * **Documentation** * Clarified that automation runs use the creator’s authority, so creating an automation requires session-creation permission. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Rahul Sethuram <rahul.eth@icloud.com> Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com>
…eMurray#2239) ## Problem Claude harness sessions run with two competing memory systems: 1. Open-Inspect's memory tools (`mcp__oi__memory_write` / `memory_search` / `memory_read`), which store memories server-side with personal, repository, and environment scopes and an approval flow. 2. Claude Code's built-in **auto memory**. It adds a detailed "Memory" section to the system prompt telling the agent to write markdown files to `$CLAUDE_CONFIG_DIR/projects/<cwd>/memory/` and index them in `MEMORY.md`. The harness never turns (2) off. Its system-prompt instructions are detailed, while the OI memory tools are deferred, so the agent sees only their names until it loads them. As a result, when a user asked the agent to "add to our personal memory", it wrote a local file instead of calling `mcp__oi__memory_write`. The file never reached Open-Inspect's memory store. ## Fix Set `CLAUDE_CODE_DISABLE_AUTO_MEMORY=1` in `harness_env`, next to the existing `CLAUDE_CODE_DISABLE_*` policy variables. With that, Open-Inspect's tools are the only memory system the agent is told about. The variable is checked in the bundled CLI (`claude-agent-sdk==0.2.161`, Claude Code 2.1.284). The auto-memory resolver returns `"off"` for a truthy `CLAUDE_CODE_DISABLE_AUTO_MEMORY` before it reads the `autoMemoryEnabled` setting. So a repository's `.claude/settings.json`, which is loaded through `setting_sources=["user","project"]`, cannot turn it back on. `docs/CLAUDE_AGENT.md` now has a **Memory** entry in the operational notes, alongside the sub-agent note. ## Verification - New `test_harness_env_disables_file_based_auto_memory` in `tests/test_claude_env.py` - `pytest tests/test_claude_env.py tests/test_claude_harness.py`: 57 passed - `ruff check` / `ruff format --check`: clean; `prettier --check docs/CLAUDE_AGENT.md`: clean Not verified end-to-end: no live Claude session was started with the new env, because that needs model credentials. The behavior relies on the CLI code path described above. ## Rollout Sandboxes pick this up when the sandbox-runtime code they run includes the change. Existing sandboxes keep auto memory on until they're restarted on the new runtime. --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/92af5b49885b98f65256f86a6a14707e)* <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Updates** * Claude Agent runs with file-based automatic memory disabled in the sandbox. Open-Inspect’s memory tools remain available as the memory system. * **Documentation** * Updated operational notes to reflect Claude Agent’s memory configuration. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com>
…ay#2237) ## Summary If the page refreshed while you were typing a long prompt, you lost the prompt. Drafts are now saved to `localStorage` as you type and restored when the page loads. This works in both composers: - **New-session composer** (`/`): uses the key `open-inspect-prompt-draft:new-session` - **Session composer** (`/session/[id]`): uses the key `open-inspect-prompt-draft:<sessionId>`, so each session keeps its own draft ### Behavior - Each keystroke writes the draft to storage synchronously, so a refresh right after typing doesn't lose anything. - Setting the prompt to empty removes the stored key. A successful send clears the prompt, so the key is removed then too, and storage doesn't build up keys for sent prompts. - If a send fails (rejected, timed out or disconnected), the draft stays so you can retry after a reload. - The draft is restored in an effect after hydration, so server and client render the same markup. This is the same approach as the other `localStorage` preferences in the web app. - Restoring a draft on the home page does **not** start warming a sandbox; warming still begins on the next keystroke. If you send before then, submit creates the session as it does today. - Storage errors (unavailable or quota exceeded) are caught, and the composer falls back to in-memory state. - A queued prompt restored into the composer (`restorePrompt`) is also saved, because it goes through the same setter. ### Implementation - New `usePromptDraft(draftId)` hook in `packages/web/src/hooks/use-prompt-draft.ts` - `usePromptInput` now gets its prompt state from `usePromptDraft(sessionId)`, replacing its own `useState`/`promptRef` pair - The home page uses `usePromptDraft(NEW_SESSION_PROMPT_DRAFT_ID)` and clears the draft after the first prompt is sent ## Testing - New tests in `use-prompt-input.test.tsx`: the draft is restored after a remount, drafts are kept separate per session, the draft is cleared after a successful send, and it is kept after a failed send - New tests in `page.test.tsx`: the draft is restored after a remount, cleared once the session starts, and kept when the first prompt fails - I checked that the new restore and keep-on-failure tests fail without the source change - `npm test -w @open-inspect/web`: 192 files, 2051 tests passed - `tsc --noEmit -p packages/web`, ESLint and Prettier: clean --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/74a5580e3fd4c378721b6c3fb94b4f6e)* <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Prompt drafts are saved automatically and restored when you return to a composer, including after navigating away and back. * Drafts are kept separate across users, sessions, and browser tabs, and remain available when a prompt fails to send. * Retrying a restored draft reuses its request identity until you edit the draft. * Drafts are cleared after a prompt is successfully sent or you sign out successfully. If sign-out fails, your draft remains available. * Completing an earlier send won’t clear a newer draft. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com> Co-authored-by: waclaude <colemurray.cs+ghwaclaude@gmail.com>
## Summary - Support primary/source Linear team bindings in the existing provider-keyed store and Channels tab, with provider-matching bot lookup and audited management. - Add the global-only Linear `unboundChannels` policy: `workspace` preserves workspace-level launches; `reject` posts binding instructions and creates no session. - Create Linear sessions with the resolved `teamId` and signed human/app-user actor. Surface membership refusals and name the repository for `target_team_missing_grant` refusals without linking or prompting a refused session. - Scope catalog, settings, stop, and completion requests with the signed external Linear team coordinate. Scoped catalogs re-read live bindings/grants and bypass the bot's unscoped caches. Preflight reads remain actorless so session creation retains first-time identity linking. - Persist the external team in callback contexts and issue-session mappings; safely recover older completion contexts or skip reads when the coordinate cannot be recovered. - Preserve Slack discovery and channel safeguards. Add an operator-facing changelog entry. Closes [COL-256](https://linear.app/colemurray/issue/COL-256/teams-pr-16b-control-plane-linear-bot-web-linear-team-bindings). ## Access And Schema Based on `main` at `5abc1fb`. Uses existing D1 migration `0083` and session DO schema version `56`; no migration, table, or column changes. Binding management, binding lookup, and scoped catalog checks enforce independently of `TEAMS_ENFORCEMENT`. Existing session-creation membership/grant checks are unchanged. Existing session admission remains responsible for completion reads: `off` retains legacy non-private admission, `shadow` audits would-be non-private denials, and `on` enforces resolver decisions. Private service reads remain refused in every mode. The unset enforcement default remains `shadow`. ## Checkpoint Report ### 1. Commands And Results Heavy checks ran sequentially with a single Vitest worker and 600000 ms shell timeouts. | Command | Final result | | --- | --- | | `npm run build -w @open-inspect/shared` | Passed | | `npm run typecheck` | Passed across every TypeScript workspace | | `npm run lint:fix` | Passed, including the commit hook | | `npm run lint:sql-portability` | Passed; 24 existing baselined occurrences | | `npm test -w @open-inspect/shared -- --maxWorkers=1` | 66 files, 1150 tests passed | | `npm test -w @open-inspect/control-plane -- --maxWorkers=1` | 365 files, 6261 tests passed | | `npm run test:integration -w @open-inspect/control-plane -- --maxWorkers=1` | 148 files, 2023 passed, 1 skipped | | `npm test -w @open-inspect/web -- --maxWorkers=1` | 274 files, 2839 tests passed | | `npm test -w @open-inspect/linear-bot -- --maxWorkers=1` | 19 files, 319 tests passed | | `npm test -w @open-inspect/slack-bot -- --maxWorkers=1` | 39 files, 621 tests passed | | `npm test -w @open-inspect/github-bot -- --maxWorkers=1` | 8 files, 146 tests passed | | `git ls-files --modified --others --exclude-standard -z \| xargs -0 npx prettier --write --ignore-unknown` | Passed | | `git diff --check` and `git diff --check origin/main...HEAD` | Passed | The successful web/integration runs emitted existing navigation/timer, forced-eviction, and D1 error-fixture diagnostics; these were not failed tests. Targeted regression runs and their initial failures are recorded below. All corresponding tests passed in the final full suites. <details> <summary>Initial failing validation output</summary> `npm test -w @open-inspect/linear-bot -- src/webhook-handler.test.ts --maxWorkers=1`, before handler wiring: ```text ❯ src/webhook-handler.test.ts (46 tests | 8 failed) 128ms × transitions an existing installation and creates an environment session 33ms × creates a session in the resolved ownership scope null 3ms × creates a session in the resolved ownership scope team_internal 7ms × refuses a failed or invalid binding lookup (404) 4ms × refuses a failed or invalid binding lookup (503) 3ms × refuses a failed or invalid binding lookup (200) 2ms × posts a membership error without linking or prompting an app-user fallback session 2ms × stops an existing session when Linear sends a stop signal 2ms Test Files 1 failed (1) Tests 8 failed | 38 passed (46) ``` `npm test -w @open-inspect/web -- src/components/settings/integrations/linear-integration-settings.test.tsx --maxWorkers=1`, before disabling the whole pending form: ```text ❯ src/components/settings/integrations/linear-integration-settings.test.tsx (10 tests | 1 failed) 1661ms × preserves dirty edits during revalidation and saves the policy in global defaults 162ms Received element is not disabled: Test Files 1 failed (1) Tests 1 failed | 9 passed (10) ``` `npm test -w @open-inspect/linear-bot -- src/webhook-handler.test.ts --maxWorkers=1`, before retaining actorless preflight reads and refusing an unavailable bound environment mapping: ```text ❯ src/webhook-handler.test.ts (47 tests | 3 failed) 102ms × refuses an unavailable bound environment mapping instead of falling back to a repository 6ms × creates a session in the resolved ownership scope null 3ms × creates a session in the resolved ownership scope team_internal 5ms AssertionError: expected { repoOwner: 'acme', …(5) } to be null AssertionError: expected true to be false // Object.is equality Test Files 1 failed (1) Tests 3 failed | 44 passed (47) ``` `npm run test:integration -w @open-inspect/control-plane -- test/integration/environments-catalog.test.ts --maxWorkers=1`, before actorless Linear catalog admission: ```text FAIL test/integration/environments-catalog.test.ts > team-scoped environment catalog > scopes actorless Linear catalog reads to the live binding without enrolling a user AssertionError: expected 403 to be 200 // Object.is equality Test Files 1 failed (1) Tests 1 failed | 8 passed (9) ``` `npm run test:integration -w @open-inspect/control-plane -- test/integration/environments-catalog.test.ts test/integration/team-grants.test.ts test/integration/team-channel-bindings.test.ts --maxWorkers=1`, before separating the preserved Slack actor requirement from Linear's actorless catalog contract: ```text FAIL test/integration/team-grants.test.ts > team repository grants > refuses invalid, actorless, and mismatched linear channel claims before reading catalogs AssertionError: expected 200 to be 403 // Object.is equality Test Files 1 failed | 2 passed (3) Tests 1 failed | 70 passed (71) ``` The initial formatting command without `--ignore-unknown` also failed on Vitest's snapshot extension: ```text [error] No parser could be inferred for file "/workspace/background-agents/packages/control-plane/test/integration/__snapshots__/hono-route-catalog-conformance.test.ts.snap". ``` Rerunning with `--ignore-unknown` passed; snapshot contents were validated by the integration suite. </details> ### 2. Verified Facts And Drift - Webhook dispatch, project/team mapping precedence, app-user fallback, and signed actor assertions matched the existing behavior. The operator-managed `config:team-repos` mapping stays intact. - The shared binding enum, store, SQL constraints, and signed session-channel parser already supported Linear. No additional session route-admission implementation was necessary; integration tests exercise Linear completion-event scopes in all enforcement modes. - Binding PUT and bot lookup were still Slack-only. Catalog scoping also had a Slack-only gate in `routes/team-ownership.ts`, so Linear support includes that existing read helper. - Actor-bearing reads finalize identity before RBAC (`routing/route-admission.ts`); this exposed the first-time-linking hazard of asserting an actor on preflight catalogs. Linear preflight reads therefore use the binding-derived service viewer, while creation remains the canonical actor/profile-claims seam. - Scoped environment lists already omit targets whose repositories lack grants. A bound, explicitly mapped environment now refuses rather than silently falling through to another target when it is unavailable in that catalog. - The existing integration test file is `team-channel-bindings.test.ts`. The current control-plane catalog has 212 routes and 160 paths; neither count changes. Only existing policy snapshot entries change. - The strict callback schema now explicitly permits an optional external `linearTeamId`, preserving persisted contexts while ensuring new callbacks have a scoped read coordinate. ### 3. Deliberately Excluded - GitHub routing, automation candidate selection, enforcement-default changes, new migrations, and shared session-creation/token-mint changes are outside this change. - No Linear team discovery API was added. The Channels tab accepts a Linear team ID manually and retains Slack's existing picker. - No new Linear publication-purpose or Slack-style outbound-post policy was introduced. Completion reads use existing service-viewer decisions. - Real sign-in, production API persistence, and live Linear delivery were not exercised locally. Browser verification used real components and repository CSS with explicitly mocked auth and API fixtures. ## Visual Verification Verified bind/unbind and policy save/reset at desktop 1440×1000 and mobile 390×844, including disabled controls during pending saves and no mobile horizontal overflow. The mobile reject label was shortened and rechecked. | Surface | Capture | Source | Uploaded artifact | | --- | --- | --- | --- | | Desktop Channels | Viewport, 1440×1000 | `http://127.0.0.1:4173/channels` | `75358db59fcd5ba04167aca8e1859f2f` | | Mobile Channels | Viewport, 390×844 | `http://127.0.0.1:4173/channels` | `28f77063fa3caffa6204da2ca92eca7d` | | Desktop Linear policy | Full-page, 1440×1000 viewport | `http://127.0.0.1:4173/linear-settings` | `3872fcfcd5df7d72cb4a7c38f4c84de3` | | Mobile Linear policy | Full-page, 390×844 viewport | `http://127.0.0.1:4173/linear-settings` | `b440e7ac27923a8fb95b4a8cea6718c3` | The local preview used mocked authentication, authorization, models, fixtures, and API responses, and browser fallback fonts rather than the complete Next.js app shell. --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/657787201d55309489d5251a9a87b083)* <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Team leads and workspace administrators can bind or unbind Linear teams in team Channels settings. * Workspace administrators can choose whether requests from unbound Linear teams create workspace-level sessions or are rejected. * Requests from bound teams use team-scoped repository, environment, and session access, with membership and repository-grant checks. * **Bug Fixes** * Requests stop when a team binding or required access cannot be verified. * Completion content is withheld if the issue’s team changes or scoped session data cannot be read. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com> Co-authored-by: waclaude <colemurray.cs+ghwaclaude@gmail.com>
…#2240) ## Summary The new-session composer's team context (and audience) picker in `SessionAccessSelector` only lived in React state, so it reset to the sidebar team defaults on every reload or navigation back to the home page. Users had to reselect their team each time. This persists the composer access draft (`contextKey`, `teamId`, `visibility`) to localStorage under `open-inspect-last-session-access`: - Saved only on explicit user changes (team or audience), not on automatic reconciliation. - Restored on mount and validated with zod (`parseStoredComposerAccess`); malformed values are ignored. - Still keyed by the sidebar `contextKey`, so a stored choice only applies while the sidebar team context (already persisted via `open-inspect-active-team`) matches. Switching the sidebar team keeps the existing "use team defaults" behavior. - Stale team IDs fall through the existing `resolveComposerAccess` validation against current memberships. - Storage access is wrapped in try/catch, matching `use-active-team`. ## Testing - New tests in `page-team-context.test.tsx`: restores team + audience after remount (verified failing without the fix), ignores a selection stored under a different sidebar context, and ignores malformed storage. - `npm test -w @open-inspect/web`: 192 files, 2049 tests passing - `tsc --noEmit` and eslint are clean --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/2b167816c4d25a733bd423bf07329672)* <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Team and visibility selections in the composer are saved and restored when you return, unless you’ve already made a new selection. * If browser storage is unavailable, your current selection continues to work for the session. * **Bug Fixes** * Invalid, inaccessible, or context-mismatched saved selections are ignored, preventing them from overriding the current sidebar context. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com> Co-authored-by: waclaude <colemurray.cs+ghwaclaude@gmail.com>
…rray#2211) ## Summary - correlate sandbox event receipt, completed dispatch, and failed processing with sandbox/message/tool/task/step/operation/ACK metadata - record critical-event ACK outcomes after successful dispatch, distinguishing local send success, unavailable socket, failed send, and legacy missing ACK IDs - retain DEBUG-only token/heartbeat logging; allowlist and bound metadata without logging content, arguments, output, error text, titles, or attachments - preserve dispatch exceptions, event persistence/broadcast behavior, and critical-event acknowledgement ordering - log receipt before attribution lookup and capture safe failures even when the persisted processing-message lookup fails; missing-ID critical ACK outcomes remain visible at INFO `processed` means the family handler's dispatch finished. It does not claim completion of background callbacks, client delivery, or remote ACK receipt. `sent` means the local WebSocket send succeeded. ## Validation - Node 24.19.0; shared package build passed - focused processor/runtime handler tests: 64 passed (`--maxWorkers=1`) - after review fixes: processor suite 59 passed, including lookup failure with and without an explicit message ID - control-plane package typecheck passed (Worker, Node, tests, integration config) - ESLint on changed files passed; Prettier applied; `git diff --check` passed - tests cover payload exclusion, bounded metadata, explicit/fallback message attribution, failure preservation/no ACK, and ACK outcomes/order ## Scope Independent of the Python logging PRs. No schema changes or deployment performed. Local tests do not establish production log ingestion; after deployment, confirm the same message/call/ACK IDs across Modal and control-plane logs. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Improvements** * Event-processing diagnostics now include timing, status, and attribution details, with long metadata shortened. Heartbeat and token details are logged at debug level. * Processing failures record the error type and duration before the original error is rethrown; failed events are not acknowledged. * Acknowledgment logs distinguish successful and failed sends, unavailable connections, and missing event IDs, and reflect that acknowledgments follow event processing. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com>
…eMurray#2243) ## Summary This supersedes ColeMurray#2194 (by @iamladi). It's rebased onto `main` with one review fix added. The original commit is kept with its authorship. The Slack and Linear classifiers send no `reasoning_effort`, so an OpenAI classification model always runs at its default effort. On some reasoning models that default is `medium`, which costs latency and reasoning tokens for a routing decision. This PR lets an operator choose the effort. - **New variable `classification_reasoning_effort`** (CI: the `CLASSIFICATION_REASONING_EFFORT` Actions variable). When it is set, both classifier Workers get a `CLASSIFICATION_REASONING_EFFORT` binding, and `callOpenAIStructured` sends it as `reasoning_effort`. - **Blank is the default and changes nothing.** No binding is created and no field is sent. - **OpenAI only.** A validation rule rejects the variable at plan time when `classification_model` is an Anthropic id. Without it, the setting would be silently ignored. ## Changes from ColeMurray#2194 - **Rebased onto `main`** and resolved the `CHANGELOG.md` conflict. The entry sits under the existing `### Added` section, next to the Linear team bindings entry. - **Format check on the value.** The effort is still not limited to a fixed list, since supported values differ by OpenAI model. But it must now be blank or a lowercase word (`^[a-z]+$`). Before, a value like `"low "` or `"Low"` passed `terraform plan`, was bound as-is, and made OpenAI return HTTP 400 on every classification. Every Slack/Linear request then quietly fell back to the repo picker or a clarifying question. Added a `rejects_padded_reasoning_effort` tftest. ## Testing - `npm test` for shared, slack-bot, and linear-bot - `npm run typecheck` - Prettier check on the changed files - `terraform fmt -check -recursive` - Full `terraform test` suite (66 passed) Not covered: no run against the live OpenAI API. --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/74d38ef76a4ce8e99cb46e12163a80c0)* <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added an optional reasoning-effort setting for Slack and Linear classification with OpenAI models. When unset, the model’s default is used. * Added validation to reject this setting for Anthropic models and invalid values. * **Documentation** * Updated setup guides and configuration examples with details on the new setting. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Ladislav Martincik <ladislav.martincik@gmail.com> Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com>
## Summary - Split the serial coverage workflow into six control-plane Vitest shards plus parallel web, small TypeScript, and Python jobs. - Preserve the existing `Coverage` check as the final gate: require every upstream job to pass, merge all control-plane blob reports, and enforce the unchanged full-suite coverage floor on the merged result. - Keep the combined `production-coverage` artifact and document the new layout. Ignore local Vitest shard reports. ## Validation - Built `@open-inspect/shared`, typechecked `@open-inspect/control-plane`, ran coverage gate tests, and checked formatting. - Smoke-tested two control-plane shards containing unit and Workers integration tests; merging their blobs produced a combined report. The merge correctly failed the full-suite threshold when deliberately run with only those two tests. CI on this PR will establish the actual wall time and whether the six-shard layout meets the 3–4 minute target. --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/f7f5be2008fc39113067520fa149b402)* <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Coverage checks now run in parallel for control-plane, web, other TypeScript packages, and Python. Each job uploads its results, which are collected into a combined coverage artifact. * The overall coverage check reports unsuccessful jobs and fails if any individual coverage check fails. * **Documentation** * Updated coverage guidance to describe the parallel jobs and combined artifact. Removed outdated instructions to limit TypeScript coverage runs to a single worker. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com> Co-authored-by: waclaude <colemurray.cs+ghwaclaude@gmail.com>
…y#2210) ## Summary Make Claude Agent SDK activity visible in sandbox/Modal logs without changing permissions, authentication, tool execution, persistence, or emitted session events. Claude stdout remains SDK-owned structured input; OpenCode process forwarding is unchanged. ## Implementation - Add structured INFO entries for tool starts/results, completed assistant updates, task lifecycle metadata, compaction/provider diagnostics, and completed/failed/cancelled turn outcomes with duration, usage, and available cost. - Generate wire events and trajectory records in one typed `ClaudeTranslator`. Render records before event delivery, with per-record failure isolation. - Keep prompt correlation and result state invocation-local. SDK stderr stays distinctly session-scoped because its connection-lived callback provides no reliable turn provenance. - Keep raw text/argument/output previews bounded to 2,048 UTF-8 bytes with explicit truncation. Multiline and non-ASCII content remains readable through the existing structured JSON formatter. - Use canonical `StructuredLogger` exception metadata and tracebacks rather than a custom sanitized exception path. - Preserve task-parent mappings across turns, clean up terminal/reset mappings, and bound orphan tracking. - Keep harness, trajectory, logger, and translator tests in focused modules with reusable SDK fakes. - Do not collect thinking blocks, streaming deltas, subagent text, task summaries, raw SDK messages, or automatically dump configuration/environment state. ### Logging Policy Per maintainer request, the extra credential sanitization and provenance layer has been removed for parity with the existing OpenCode setup. There is no new secret inventory, credential scanning, sensitive-key/header/CLI masking, or content-gating dependency. The control-plane and Modal environment assembly files match `main` again. **Content produced by the agent is logged as-is within preview limits and can contain credentials if a tool or assistant emits them.** Preview truncation is not credential redaction. Existing unrelated Git/boot redaction remains unchanged. ## Validation Run sequentially against the frozen dependencies and pinned `claude-agent-sdk==0.2.161`: - Focused Claude harness, trajectory, logger, and translator tests: **114 passed**. - Full sandbox-runtime Python suite: **1,476 passed, 3 skipped**. - Control-plane environment/provider tests with `--maxWorkers=1`: **340 passed**. - Modal environment/launch tests: **109 passed**, using `PYTHONPATH=../sandbox-runtime/src:../sandbox-images/src` to test the checkout rather than the image-installed runtime. - Python Ruff lint/format and affected runtime/Modal mypy: passed. - Full control-plane typecheck and affected ESLint/Prettier: passed. - `git diff --check`: passed. Tests cover unchanged content and wire events, UTF-8 preview bounds, normal exception tracebacks, logging/serialization failure isolation, interleaved turns, persistent SDK stderr readers, and task cleanup. **These are local checks, not live Modal proof.** No production deployment, image rollout, or public-to-production sync was performed; fresh CI results have not been awaited. ## Authorized Canary Only after merge and a separately authorized runtime rollout: - [ ] Start a **fresh Claude sandbox** and verify it contains the updated runtime commit. - [ ] Exercise shell execution, file reading/editing, a foreground subagent, and a harmless tool error. - [ ] Confirm Modal has correlated tool starts/results, completed assistant updates, task metadata, and turn outcomes with available duration/usage/cost. - [ ] Confirm follow-up prompts retain correct turn attribution and SDK stderr remains distinctly session-scoped. - [ ] Confirm the session UI receives its normal trajectory and terminal outcome. - [ ] Exercise large/multiline/non-ASCII output and verify preview limits and truncation indicators. Do not intentionally print real credentials or dump the environment/configuration. - [ ] Smoke-check OpenCode for unchanged process logging and UI behavior, with no duplicate Claude-style entries. - [ ] Capture live evidence that an operator can follow Claude activity and outcomes directly in Modal logs without opening the native transcript. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Improvements** * Claude interactions now provide more complete activity records, including tool use, task progress, usage, costs, and turn outcomes. * Interruption, connection, and provider issues are reported more clearly, while logging failures are prevented from disrupting interactions. * Activity records include bounded previews and relevant session context, helping make diagnostics more useful without exposing excessively long content. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com> Co-authored-by: waclaude <colemurray.cs+ghwaclaude@gmail.com>
…urray#2245) ## Summary On the team **Channels** page, the **Provider** and **Binding kind** fields were plain native `<select>` elements. They didn't match the styled dropdown next to them (the Slack channel picker) or the rest of the team pages. `team-repositories.tsx`, for example, already uses the shared Radix `Select`. This switches both fields to `@/components/ui/select` (`Select` / `SelectTrigger` / `SelectContent` / `SelectItem`). Behavior is the same: - Changing the provider still resets that provider's editor draft and clears the error banner. - The binding kind is still disabled when the provider is locked, for example when Slack discovery is denied. - The `<label htmlFor>` still points at the trigger, so both fields keep their accessible names ("Provider", "Binding kind"). ## Tests - `team-channels.test.tsx` now picks options through the Radix trigger and listbox using the same `chooseOption` helper and pointer-capture stubs as `team-repositories.test.tsx`. The assertions are unchanged. - `vitest run src/components/teams/team-channels.test.tsx`: 24 passed - `tsc --noEmit`, `eslint`, and `prettier --check` pass on the changed files --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/683d563a1a258c45660b77febf26eecb)* <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Improvements** * Updated provider and binding-kind menus to use the app’s shared selection controls for a more consistent interface. * The provider menu now reflects the form’s disabled state, preventing selection changes while the form is disabled. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com>
## Summary Control-plane coverage is still the critical path for the `Coverage` check: it takes 8–13 minutes (483s and 781s on recent runs) against a 15-minute job timeout. ColeMurray#2238's description mentions six control-plane shards, but its final commit reverted to a single uncapped job, so `main` never got sharding. Vitest's timing breakdown shows the problem is throughput, not any single slow test: - Duration: 446–692s. Cumulative worker time: setup 770–1250s, tests 200–260s, import 98–155s. - The slowest file takes 22s, so there's nothing to target individually. - Most of the setup cost is the Workers pool starting up for each integration file. Locally, stubbing out the D1 migrations only reduced setup from ~4.6s to ~4.2s per file, so squashing migrations wouldn't help much. That makes this work a good fit for spreading across runners. ## Changes - **Shard control-plane coverage 8 ways** (`Coverage (control-plane i/8)`). Each shard writes a Vitest blob report. Unlike the earlier attempt in ColeMurray#2238, workers aren't capped with `--maxWorkers=2`, so each shard uses the whole 4-vCPU runner. - **Merge in a dedicated `Coverage (control-plane)` job** that waits only for the shards. It runs `vitest run --merge-reports` (which re-applies the Vitest thresholds) and then `check-coverage.mjs`, and uploads the same `coverage-data-control-plane` artifact as before. The merge no longer waits for web, and the final `Coverage` gate is still a lightweight aggregator. - `COVERAGE_SHARD=true` turns off the full-suite floor for individual shards, since a partial run can't meet it. The floor is unchanged and is enforced on the merged result. - The final `Coverage` gate also checks the shard matrix result, so a failed shard is reported directly instead of showing up as a skipped merge job. - Ignore `.vitest-reports/` and update `docs/TEST_REDUCTION.md`. ## Expected impact From ColeMurray#2238's earlier sharded runs: 6 shards capped at 2 workers took ≤235s per shard, and 10 shards took ≤185s. With uncapped workers and the merge off the gate's critical path, control-plane coverage should come in around 3–3.5 minutes, down from 8–13. Web coverage (3–4.7 minutes) becomes the next bottleneck. It can be sharded the same way in a follow-up if needed. ## Validation - Ran two local shards over a mixed unit and integration subset with `COVERAGE_SHARD=true`. Both passed and wrote blob reports without tripping the floor. - `vitest run --merge-reports` combined both shards (4 files, 168 tests) and failed the floor as expected for a partial run. `check-coverage.mjs` reported FAIL on the merged summary. - The merge also worked with `packages/shared/dist` removed, which confirms the merge job doesn't need the shared build. - Workflow and coverage-gate contract tests pass; the control-plane test tsconfig typechecks; Prettier is clean. - This PR's CI run will show the actual wall time. --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/aa446e92f50d8e4f38def70ff69f7ce9)* <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Control-plane coverage checks now run in parallel shards, with results combined before the full-suite coverage threshold is checked. * The overall coverage check verifies that both the shard runs and report merge succeed. * Updated coverage workflow documentation and excluded generated test reports from version control. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com>
## Stack / review scope Depends on ColeMurray#2212 (safe agent logging foundation). Targeting `main` so the repository's main-only PR CI runs on this exact head. Until the parent is merged, the overall diff includes its changes; review this PR's additional commit or the [incremental diff](ColeMurray/background-agents@sandbox-agent-trajectory-logging...sandbox-transport-diagnostics). Merge ColeMurray#2212 first. ## Summary - report buffer evictions with event/message/ACK identities, critical status, cumulative counts, and rate-limited noncritical warnings; critical evictions are always reported - expose compact read-only buffer/pending/in-flight ACK/loss counters and enrich existing send/recovery failures - distinguish heartbeat scheduling delay from send duration, warn on abnormal delays, and emit approximately one aggregate health entry per minute - retain prompt failure category, exception type, source outcome, phase, redacted/bounded error detail, available cost, and emitted-event/tool-event counts in the turn summary - preserve upstream oversized-response failure handling and identify its `text_undelivered` category even when the underlying harness reports success - use monotonic turn duration and advance runtime/rebuild generation to 75 without tightening compatibility/preservation/harness floors Intentional unbuffered stale boot reports stay DEBUG. `evicted_events` means buffer eviction, not proof of permanent loss (an in-flight send may still complete). Event counters count emission attempts/lifecycle updates, not persisted or uniquely executed tools. No retention, ACK, replay, scheduling cadence, deadline, or execution semantics changed. No resource-sampling dependency added. ## Validation - Python 3.12 with frozen dependencies - 148 focused transport/forwarder/retirement/reconnect/cost/stop/message-tracking tests passed after stacking the foundation - real production formatter tests prove known credentials are redacted from failure summaries, oversized details are bounded, and terminal event error text remains unchanged - Ruff lint/format passed, targeted mypy passed for both source modules, diff check passed - combined runtime-stack verification is recorded in the snapshot follow-up - final combined stack after upstream rebase and logging review fixes: 1,486 runtime tests passed, 3 skipped; 551 Modal tests and 23 Node tests passed - oversized-response regression preserves failure delivery while recording source success and `text_undelivered` ## Rollout verification (not performed) In a fresh deployed sandbox, observe normal low-volume health summaries and correlated prompt outcomes. Use a staging transport interruption to verify warning counters and recovery identities; distinguish buffered events from delivered/ACKed events. Do not deliberately interrupt production sessions. No production sync/deployment performed. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Improvements** * Added periodic connection and event-delivery health reporting, including during reconnect attempts. * Heartbeat warnings now identify scheduling delays and slow sends. * Prompt summaries now report outcomes, execution details, and event and tool-call counts. * Event-delivery diagnostics now include buffer and acknowledgement status, with rate-limited warnings for noncritical evictions and warnings for critical evictions. * **Tests** * Expanded coverage for heartbeat, reconnect, prompt outcome, and event-delivery diagnostics. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com>
## Summary Fixes COL-260: Terraform plan output could become executable `github-script` source, and a failing plan could be masked by `tee`. - Capture plan stdout/stderr only in a file, disable the Terraform output wrapper, and use explicit Bash `pipefail` so Terraform failures survive the logging pipeline. - Suspend runner workflow-command processing while logging untrusted plan text, restoring it on exit even when the output lacks a trailing newline. - Generate comments with a small checked-in Node CLI that strips ANSI escapes, HTML-escapes plan text inside `<pre>`, and truncates the escaped text without splitting UTF-8 characters or HTML entities. - Transfer only the prepared comment as a short-lived artifact. A separate commenting job performs no checkout or execution of PR code and has only `pull-requests: write`; planning, validation, and apply retain read-only GitHub permissions. - Keep the final failed-plan gate unconditional with respect to reporting failures and pass validation outcomes through environment variables rather than interpolating outputs into JavaScript source. - Add malicious-text and failure-path regression coverage and trigger it for Terraform-workflow-only changes. ## Validation - `npm run test:terraform-workflow-contract`: 13 tests passed, covering hostile backticks/interpolation, quotes, shell-looking text, expression-looking strings, runner commands, ANSI escapes, HTML tags, oversized/multibyte output, missing final newlines, actual workflow capture/preparation/posting code, and Terraform exit codes 0, 1, and 42. - `npm run test:node-version-workflow-contract`: 1 test passed. - Targeted ESLint and Prettier checks passed. - `actionlint` passed for both modified workflows. - `git diff --check` passed. Live Terraform planning against production state and the GitHub artifact/comment exchange were not run locally. ## Scope The separate production-secret and state-access exposure tracked by COL-259 is not resolved by this change. This PR removes write-token exposure from planning/validation and fixes the plan-output injection and failure-masking paths. --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/ba765df4fa4c3ad2ae65a91da468243b)* <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Pull requests now receive Terraform validation results and, when available, a plan summary as comments. * Comments show whether planning succeeded or failed, and include plan details in an expandable section. * Validation and secret-availability status are also reported in pull request comments. * **Chores** * Changes to the Terraform workflow now trigger CI checks. * Terraform plan output in comments is sanitized and may be shortened when it exceeds the display limit. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com> Co-authored-by: waclaude <colemurray.cs+ghwaclaude@gmail.com>
…urray#2248) Follow-up to ColeMurray#2220 (ColeMurray#2220). ColeMurray#2220 comes from a fork, so this branch stacks on its commit (`c209ff5`). **Review only the commits after `c209ff5`.** Merge after ColeMurray#2220, or instead of it. ## Problem ColeMurray#2220 blocks an automatic target whenever its catalog has *any* error. The app-wide SWR fetcher already separates errors: it throws `SwrFetchError(status)`, and `use-automations.ts` already follows the rule *"only transient failures (network, 5xx) keep the last loaded data"*. `revalidateOnFocus` is on, so a brief failure is common. Reproduced with tests on ColeMurray#2220's code: - **A temporary error drops the auto target and retires the warm sandbox.** One 503 or network error during a focus revalidation sets `sessionTarget` to null. The warm request becomes null and `useWarmDraftSession` retires the pre-warmed sandbox. Before ColeMurray#2220, auto targets were unaffected. - **Workspace auto repos are blocked on any 5xx.** In workspace mode the repos catalog keeps cached data on error, and the new gate now blocks it anyway. The branch list clears too. - **Two places handle stale data, inconsistently.** Repos were emptied on any error in team mode only, environments never were, and the launch gate added a third rule on top. - **A held auto target shows as "Select repo".** While a failed catalog holds the auto target, the picker label fell back to "Select repo" instead of showing the held target the way explicit selections are shown. - The same-context comparison (`teamId` plus default environment) was written out twice. ## Fix - `lib/swr-fetch-error.ts` decides when cached data is still usable. `isRetryableFetchError` keeps it only for failures without an HTTP response (network, client-side validation) and for HTTP 408, 429 and 5xx. Every other status, including any not listed, makes it unusable. `usableFetchData(data, error)` applies that rule. `use-automations` uses it in place of its own 401/403/404 list. - `useRepos` and `useEnvironments` apply the rule themselves, so every consumer gets empty catalogs after a non-retryable failure and keeps cached rows after a retryable one. The picker no longer knows about fetch errors: its separate catalog-error launch check and the team-only rule for repos are both gone. `targetIsAvailable` against the hook output covers both cases. - The same-context check is now one `inSelectionContext` value, used by the launch gate. The "a failed catalog neither confirms nor replaces this context's target" hold from ColeMurray#2220 still applies, now as `catalogErrorHoldsTarget`. The selection effect and `pickerTarget` both use it, so the picker keeps showing a held auto target, the same way explicit ones are shown. Behavior change to note: an **explicit** target with cached catalog data now also stays launchable through transient errors. Before, any error blocked it. This matches the automations rule and avoids the same warm-sandbox churn. Explicit targets are still not invalidated on any catalog error. ## Not changed The review also said the hold stops a fallback to a healthy catalog's stored target in the same context. That reproduces only when localStorage holds a different target than the current auto target. In a single tab, localStorage always holds the current auto target, so this needs another tab writing to it. Holding the target until its catalog recovers is ColeMurray#2220's stated intent, so this is left as is. ## Verified - New tests, each failing on ColeMurray#2220 and passing here: - an auto environment target stays launchable through `Error("Failed to fetch")` and `SwrFetchError(503)` - an auto repository stays launchable through `SwrFetchError(500)`, in both workspace and team mode - an explicit target stays launchable through `SwrFetchError(503)` - an auto environment held after a 403 is not launchable and the picker still shows it - `swr-fetch-error.test.ts` checks each status code directly. `use-repos` and `use-environments` tests check that 400/403/404/422 give empty lists and that 500/503 and network errors keep cached rows; they fail against the previous hooks. - Picker tests now mock what the hooks really return after a denial: `SwrFetchError(403)` with empty catalogs. ColeMurray#2220's auto-environment test now also checks the held picker display. - `npm test -w @open-inspect/web`: 194 files / 2120 tests pass. `npm run typecheck` exits 0. ESLint and Prettier are clean. --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/fc780c804544d079c0a58c7f92151f07)* <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Bug Fixes** - Automation data remains visible during temporary fetch errors and is hidden after access-denied or not-found errors. - Repository and environment choices remain available during temporary catalog failures when cached data is usable. Access-denied and other non-retryable errors no longer expose stale catalog data. - When a catalog request fails, the session target picker preserves an existing selection in the same team and environment context, but blocks launching if that target is unavailable. Selections from a different context continue to follow the new context’s catalogs. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Rahul Sethuram <rahul.eth@icloud.com> Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com> Co-authored-by: waclaude <colemurray.cs+ghwaclaude@gmail.com>
…oleMurray#2244) Builds on ColeMurray#2241 (its commit is included unchanged) and fixes the issues found in review. Merging this supersedes ColeMurray#2241. ColeMurray#2241 makes a `ready` sandbox whose bridge dropped wait for the heartbeat alarm instead of being replaced. But nothing guaranteed that alarm would fire soon, and the same gap remained for other live statuses. ## Changes - **Arm the heartbeat deadline while waiting.** A new `await_reconnect` spawn action makes the manager schedule an alarm at the heartbeat staleness deadline: `heartbeatStaleAt(last_heartbeat)`, or immediately if the heartbeat is already stale. Previously the only alarm might be an `inactivity_warning` extension 5 minutes out, so a pending prompt could hang that long. Before ColeMurray#2241 it would have spawned after 60s. The scheduler keeps the earliest deadline, so this never delays an earlier alarm. - **One reconnect rule for every launched, live status.** The separate `hasConnected` checks for `spawning`/`connecting` and `ready` are replaced by one check that runs before the per-status branches. It also covers `snapshotting`, which still fell through to the cooldown check and spawned. While a checkpoint is in flight, `startupDecision()` already holds spawns, so this only matters in edge states, but the shared check covers it at no cost. `pending` is excluded because nothing has launched yet. - **Restore client state on the wait.** Callers broadcast `sandbox_spawning` / `sandbox_warming` before deciding. That makes the UI show "spawning" and clears sandbox access state. The wait now re-broadcasts the persisted status, plus `sandbox_access_changed` for `ready`. - **Remove `SpawnConfig.readyWaitMs`.** `ready` is only set by `markSandboxReady`, which needs an authorized runtime event over an attached socket, and socket accept writes `last_heartbeat` first (pinned by the `connection-authenticator` attach test). So the age-based `ready` branch was unreachable. A `ready` row that never connected is now governed by the 30s spawn cooldown, the same as any other unconnected generation. - `heartbeatStaleAt` is shared with `alarm-policy` so both use the same staleness deadline. ## Tests `manager-reconnect.test.ts` now covers: - no replacement for connected `spawning` / `connecting` / `ready` / `snapshotting` sources, with recent and stale heartbeats - the exact alarm deadline armed on wait (recent and already-stale heartbeat) - the status and access re-broadcast - end-to-end: an `inactivity_warning` alarm armed 5 minutes out, then a prompt arms the earlier heartbeat deadline, which retires the source, and the next prompt restores it - cooldown-only handling of a `ready` source that never connected The fake `status` parameter is gone. The stopped/stale/failed restore cases are kept: they check that the reconnect check runs after restore for dead statuses. Against ColeMurray#2241's code, 10 of the 21 cases fail. With this change all pass. ## Validation - `npm test -w @open-inspect/control-plane`: 4263 passed - Sandbox-related integration suites (`prompt-enqueue`, `sandbox-early-connect`, `sandbox-events`, `sandbox-shutdown`, `sandbox-state-retention`, `sandbox-vm-reconciliation`, `session-lifecycle-alarm-recovery`, `websocket-sandbox`): 118 passed. The full integration run exceeded my 10-minute command limit, so CI covers the rest. - control-plane `typecheck`, ESLint and Prettier: clean --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/d378941876d00fede711bc46f29fbc84)* <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Previously connected sandboxes retain their existing status while awaiting a WebSocket reconnection, rather than being replaced based on boot status or age. * Reconnected ready sandboxes continue to report their status to clients, and heartbeat deadlines are scheduled to retire stale connections. * Sandboxes that never connected can still be replaced after the spawn cooldown. * Spawn and warm-up announcements are sent only when a launch proceeds, avoiding misleading notices while a sandbox is queued or reconnecting. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: XuJian <265865031+xujiantop-crypto@users.noreply.github.com> Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com> Co-authored-by: waclaude <colemurray.cs+ghwaclaude@gmail.com>
Adds scheduled Dependabot **version updates**, scoped to the ecosystems where an automated bump can be validated by CI before merge. Replaces ColeMurray#1897 (thanks @rhlsthrm for the original proposal — this keeps its grouping approach and narrows the scope). Security updates are already enabled in repository settings and are unaffected; they continue to cover every ecosystem, including the ones excluded here. ## Schedule Roughly every two weeks: `cron` on the 1st and 15th at 09:00 UTC. Dependabot has no biweekly interval and cron can't express "every other week", so this is the closest equivalent. All entries use a 7-day `cooldown`, so a release must be at least a week old before it's proposed. This limits exposure to compromised releases that are typically pulled within days. ## Scope | Ecosystem | Directories | Grouping | |---|---|---| | npm | `/` (root lockfile spans all workspaces) | minor + patch grouped; majors individual | | github-actions | `/` | all grouped | | uv | `modal-infra`, `e2b-infra`, `daytona-infra`, `sandbox-images` | minor + patch grouped; majors individual | ## Intentionally excluded - **`packages/sandbox-runtime` and `packages/sandbox-images/locks/python-tools` (uv)**: their `uv.lock` feeds the hash-checked exports `locks/runtime.txt` / `locks/python-tools.txt`. Dependabot only edits `uv.lock`, so `cli.py lock --check` fails until someone regenerates the export by hand (see ColeMurray#2231). `python-tools/pyproject.toml` is also generated from `toolchain.json`, so Dependabot shouldn't edit it at all. - **`packages/sandbox-images/locks/{tools,plugins}` (npm)**: pinned through `toolchain.json`. - **Terraform**: the plan job needs production secrets that Dependabot PRs can't read, so plan is skipped, and merges to `main` auto-apply. Provider bumps should go through a regular branch where the plan runs. - **Docker / docker-compose**: only a handful of base images, and the useful bumps are majors that need manual review (e.g. `node:24` → non-LTS, `litestream` 0.3 → 0.5 config change). ## Verification - `check-jsonschema --builtin-schema vendor.dependabot .github/dependabot.yml` passes. Confirmed the schema rejects an invalid `interval` and unknown `cooldown` keys, so `cron` and `cooldown` are actually validated. - `prettier --check` passes. --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/c4ba5bd73ecf4f284f1445eed3cbdcfa)* <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Scheduled update checks are configured for npm, GitHub Actions, and uv on the 1st and 15th of each month. * npm and uv minor and patch updates are grouped, with up to five open update pull requests. GitHub Actions updates are grouped without a specified limit. * A seven-day cooldown applies between updates. Checks are configured for npm at the repository root and uv in the infrastructure and sandbox image packages. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com> Co-authored-by: waclaude <colemurray.cs+ghwaclaude@gmail.com>
## Problem `coverage.yml` had no path filters, so every push and PR to `main` ran all 12 coverage jobs (8 control-plane shards, the merge, web, shared/bots, Python), including docs-only changes. ## Change This adds change detection inside the workflow, following the pattern `compose-smoke.yml` already uses. A trigger-level `paths:` filter would leave the aggregate `Coverage` check stuck on "Expected" when it is required for merge, so the workflow stays unfiltered. - **New `changes` job:** diffs the PR (merge base) or push range and emits one output per suite: `control-plane`, `web`, `other-ts`, `python`. If the comparison can't be made (e.g. a force push), every suite runs. - **What each suite watches:** - its own package(s) - the coverage gate: the workflow itself, `.nvmrc`, `scripts/check-coverage.mjs`, `scripts/coverage-policy.ts` and `scripts/coverage-baseline.json` - for TypeScript suites, `packages/shared` and the root `package.json` and lockfile - files outside the package that its tests actually read or import, e.g. `vercel-infra`, `sandbox-images`, D1 migrations and `workers-control-plane.tf` for control-plane; three control-plane session files for web; `docs/AVAILABLE_MODELS.md` and `choosing-a-model.mdx` for shared - `packages/**/*.md` READMEs are ignored. - **Each suite job** is gated on its output. The control-plane merge job is skipped automatically when its shards are skipped. - **Aggregate `Coverage` job:** accepts `skipped` only when that suite's output is `false`. If `changes` fails, the check fails. It downloads only the artifacts of suites that ran, and skips the final upload when no suite ran. ## Verification - `prettier --check` passes, and `node --test scripts/node-version-workflow-contract.test.mjs scripts/terraform-workflow-contract.test.mjs` passes (4/4). - I dry-ran the detection step against the last 12 `main` commits: | Commit type | Before | Now | |---|---|---| | Docs-only (`80fd4cc`, `818a259`, `be36560`) | all jobs | none | | Web-only (`3abc4be`, `4d98ad1`, `d53df53`) | all jobs | web only | | Control-plane-only (`9bc6a4e`, `d605e35`) | all jobs | control-plane only | | Cross-cutting (`a05d417`) | all jobs | all suites | - An unresolvable diff range runs every suite. Because this PR changes `coverage.yml`, every suite should run on it. ## Trade-off The lists of files each suite reads from other packages are maintained by hand, like the filters in `ci.yml` and `ci-python.yml`. If a test starts reading a new file elsewhere in the repo, add it to that suite's list. A comment in the workflow says so. --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/7be0cf65aea1877955532f02fca58ce9)* <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Coverage checks now run only for suites affected by changes, while unavailable change comparisons still trigger checks. * The combined coverage report accounts for skipped suites and is uploaded when at least one suite runs. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com>
…leMurray#2273) Co-authored-by: Rahul Sethuram <rahul.eth@icloud.com>
…leMurray#2282) ## Summary Rebuilds the idea behind ColeMurray#619 (per-comment model overrides for the GitHub bot) on the current codebase. It follows the Slack `!model` / `!reasoning` flags instead of adding a second directive grammar. ```text @my-app[bot] !model openai/gpt-5.6-sol !reasoning high investigate the flaky test ``` - **One grammar for both bots.** `parseInlinePromptFlags` moves from `slack-bot` into a new `@open-inspect/shared/inline-prompt-flags` module. Slack and GitHub now parse the same syntax: flags must lead the request, each accepts a space or a colon before its value, and a duplicate or empty flag is an error. Slack keeps its own turn-plan resolution in `slack-bot/src/inline-flags.ts`. - **GitHub comment handling.** `handleIssueComment` and `handleReviewComment` parse flags from the comment after stripping the bot mention. The flags are removed from the prompt, and `startSession` resolves the session's model from the configured defaults plus the flags. - Each GitHub comment starts a new session, so the flags become that session's model settings. - A model flag must be a known model that is enabled under Settings › Models. A model flag without `!reasoning` keeps the configured effort if the new model supports it; otherwise the session uses the model's default. - A reasoning-only flag is checked against the configured model and does not fetch the enabled-models list. - Auto-reviews and review requests are unchanged. - **Errors are posted on the PR.** For an invalid flag, or when the enabled-models list can't be loaded, the bot posts a PR comment and skips session creation. This works like Slack's in-thread error message and the bot's existing refusal comments. User-supplied values are shown in code spans, so they can't inject markdown or `@mentions`. Both new skip reasons are checkpointed in the dispatch dedupe, so a redelivery doesn't post the comment twice. - **Control plane.** `GET /model-preferences` now grants actorless reads to `github-bot`, as it already does for `slack-bot`. GitHub senders may not have workspace accounts, so the bot reads the list without an actor. - **Logging.** `session.created` now logs `model`, `reasoning_effort`, and `inline_model_override`. - **Docs.** Updated `docs/integrations/GITHUB.md`, the public GitHub integration page, and the model-defaults table. ### Differences from ColeMurray#619 - **Flags instead of free-floating `model:` / `reasoning:` directives.** ColeMurray#619 matched directives anywhere in the comment and silently dropped invalid values. Here flags must lead the request, so prose like "the model: field" is never captured, and invalid flags get an explicit reply. - **No `allowInlineDirectiveOverride` toggle.** Slack has none. Only enabled models can be chosen, and only users who pass the existing trigger-user and write-permission gates can trigger the bot. - **No model aliases.** Neither bot supports `opus`-style aliases today. Bare ids such as `claude-sonnet-4-6` already normalize. - **No new settings UI or schema.** The default model and reasoning settings, plus per-repository overrides, already exist in the GitHub integration settings. ## Testing - `npx vitest run` in `packages/github-bot`: 302 passed. New tests: - `test/model-selection.test.ts` - inline-flag cases in `test/handlers.test.ts`, covering both comment handlers - a dedupe-checkpoint test in `test/webhook.test.ts`, mutation-checked: it fails when the skip reason is removed from the checkpoint list - `npx vitest run` in `packages/slack-bot`: 553 passed. - Shared tests `src/inline-prompt-flags.test.ts` and `src/module-boundaries.test.ts`: passed. - Control plane: - unit test `src/router.policy.test.ts`: passed - integration tests `service-auth`, `model-preferences`, `hono-route-catalog-conformance`, and `route-admission-matrix`: 75 passed - `npm test -w @open-inspect/docs`: passed. - `npm run typecheck` for shared, github-bot, slack-bot, and control-plane: clean. Supersedes ColeMurray#619. --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/b6e32d1cc1b67cec7b9245d445dc7d70)* <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * GitHub pull request comments and inline review comments can now begin with `!model` and `!reasoning` to override the defaults for that session. Flags support colon or space separators and must appear together at the start of the request. * Invalid or unsupported flags receive an explanatory comment, and no session is started. Automatic reviews and review requests continue to use configured settings. * **Documentation** * Updated the GitHub integration and model-selection guides with flag syntax, validation behavior, and override scope. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com>
…#2283) ## Summary Adds an October 4, 2026 `CHANGELOG.md` entry for the GitHub `!model` / `!reasoning` mention flags added in ColeMurray#2282. The entry says to upgrade the GitHub bot and control plane together, because the bot needs the new control-plane permission to read the enabled-models list. It links to the new section in `docs/integrations/GITHUB.md`. --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/b6e32d1cc1b67cec7b9245d445dc7d70)* <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Added changelog notes on selecting session models and reasoning effort in GitHub, with controls matching Slack. * Noted that the GitHub bot and control plane need to be upgraded together. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com>
…oleMurray#2280) Bumps [mypy](https://github.com/python/mypy) from 1.19.1 to 2.3.1. <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/python/mypy/blob/master/CHANGELOG.md">mypy's changelog</a>.</em></p> <blockquote> <h3>Mypy 2.3.1</h3> <ul> <li>Fix mypyc crash on double yielding Iterators (Daniël van Noord, PR <a href="https://redirect.github.com/python/mypy/pull/21826">21826</a>)</li> <li>Fix mypyc <code>default_factory</code> for inherited dataclass (Daniël van Noord, PR <a href="https://redirect.github.com/python/mypy/pull/21785">21785</a>)</li> <li>Clear mypyc coroutine env on coroutine completion (Piotr Sawicki, PR <a href="https://redirect.github.com/python/mypy/pull/21734">21734</a>)</li> <li>Fix crash when unpacking return value from overload (Shantanu, PR <a href="https://redirect.github.com/python/mypy/pull/21830">21830</a>)</li> </ul> <h3>Acknowledgements</h3> <p>Thanks to all mypy contributors who contributed to this release:</p> <ul> <li>Agriya Khetarpal</li> <li>Ethan Sarp</li> <li>Ivan Levkivskyi</li> <li>Jingchen Ye</li> <li>Jukka Lehtosalo</li> <li>Piotr Sawicki</li> <li>Shantanu</li> <li>Tom Bannink</li> <li>Viktor Szépe</li> <li>ygale</li> </ul> <p>I'd also like to thank my employer, Dropbox, for supporting mypy development.</p> <h2>Mypy 2.2</h2> <p>We've just uploaded mypy 2.2.0 to the Python Package Index (<a href="https://pypi.org/project/mypy/">PyPI</a>). Mypy is a static type checker for Python. This release includes new features, performance improvements and bug fixes. You can install it as follows:</p> <pre><code>python3 -m pip install -U mypy </code></pre> <p>You can read the full documentation for this release on <a href="http://mypy.readthedocs.io">Read the Docs</a>.</p> <h3>Support for Closed TypedDicts (PEP 728)</h3> <p>Mypy now supports closed TypedDicts as specified in PEP 728. A closed TypedDict cannot have extra keys beyond those explicitly defined. This allows the type checker to determine that certain operations are safe when they otherwise wouldn't be due to the potential presence of unknown keys.</p> <p>You can use the <code>closed</code> keyword argument with <code>TypedDict</code>:</p> <pre lang="python"><code>HasName = TypedDict("HasName", {"name": str}) HasOnlyName = TypedDict("HasOnlyName", {"name": str}, closed=True) Movie = TypedDict("Movie", {"name": str, "year": int}) <p>movie: Movie = {"name": "Nimona", "year": 2023} has_name: HasName = movie # OK: HasName is open (default) has_only_name: HasOnlyName = movie # Error: HasOnlyName is closed and Movie has extra "year" key </tr></table> </code></pre></p> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/python/mypy/commit/d642c4478e9e3acbe9233edbe17ffc569a1a778c"><code>d642c44</code></a> Bump version to 2.3.1</li> <li><a href="https://github.com/python/mypy/commit/a39242983d3c2cb85886a1eb6d5869180672784c"><code>a392429</code></a> [mypyc] Fix crash on double yielding Iterators (<a href="https://redirect.github.com/python/mypy/issues/21826">#21826</a>)</li> <li><a href="https://github.com/python/mypy/commit/4843e7773e7dc8fe3f1fd1319277d6d11cd6cdb3"><code>4843e77</code></a> [mypyc] Fix <code>default_factory</code> for inherited dataclass (<a href="https://redirect.github.com/python/mypy/issues/21785">#21785</a>)</li> <li><a href="https://github.com/python/mypy/commit/14f5df93ed8d1be4f4cc9c447eb2e6e619362e05"><code>14f5df9</code></a> [mypyc] Clear coroutine env on coroutine completion (<a href="https://redirect.github.com/python/mypy/issues/21734">#21734</a>)</li> <li><a href="https://github.com/python/mypy/commit/6dfa06dda6e34912279e498d35a43ba6dc30bfee"><code>6dfa06d</code></a> Fix crash when unpacking return value from overload (<a href="https://redirect.github.com/python/mypy/issues/21830">#21830</a>)</li> <li><a href="https://github.com/python/mypy/commit/a3857467da126d28b55724e8bb682019df9a503e"><code>a385746</code></a> Bump version to 2.3.1+dev</li> <li><a href="https://github.com/python/mypy/commit/8aabf8435357eaffceca7237f371e293b8168e54"><code>8aabf84</code></a> Drop +dev from version</li> <li><a href="https://github.com/python/mypy/commit/4d8ad2ab5e86c99581b73775f2c00b9b8265b589"><code>4d8ad2a</code></a> Update changelog for 2.3 release (<a href="https://redirect.github.com/python/mypy/issues/21728">#21728</a>)</li> <li><a href="https://github.com/python/mypy/commit/2c2154672040c52e481f423854d104e6cf172585"><code>2c21546</code></a> [mypyc] Update documentation of race conditions under free threading (<a href="https://redirect.github.com/python/mypy/issues/21726">#21726</a>)</li> <li><a href="https://github.com/python/mypy/commit/a9f62a3cf98a58a7a2607b7c81695802b39f5edc"><code>a9f62a3</code></a> [mypyc] Make attribute access memory safe on free-threaded builds (<a href="https://redirect.github.com/python/mypy/issues/21705">#21705</a>)</li> <li>Additional commits viewable in <a href="https://github.com/python/mypy/compare/v1.19.1...v2.3.1">compare view</a></li> </ul> </details> <br /> [](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 this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
) Bumps [lint-staged](https://github.com/lint-staged/lint-staged) from 16.4.0 to 17.6.0. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/lint-staged/lint-staged/releases">lint-staged's releases</a>.</em></p> <blockquote> <h2>v17.6.0</h2> <h3>Minor Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/lint-staged/lint-staged/pull/1850">#1850</a> <a href="https://github.com/lint-staged/lint-staged/commit/938d3f43d07cbd74f08052dfb8a7380ce8868180"><code>938d3f4</code></a> - Task functions like <code>{ title, task }</code> can now use a logger function <code>log()</code> to emit output while the task runs. By default, the output will only be visible if the task fails, unless the <code>--verbose</code> option was used. Additionally, when the task rejects, the error will be shown in the output.</p> <pre lang="js"><code>import { defineConfig } from 'lint-staged/config' <p>export default defineConfig({ '*': { title: 'Fail if PDF files are committed', task: async (filepaths, { log }) => { const pdfFiles = filepaths.filter((f) => f.toLowerCase().endsWith('.pdf')) if (pdfFiles.length > 0) { log('PDF files should not be committed: %s', pdfFiles) throw new Error('Failed') } }, }, }) </code></pre></p> </li> <li> <p><a href="https://redirect.github.com/lint-staged/lint-staged/pull/1854">#1854</a> <a href="https://github.com/lint-staged/lint-staged/commit/30562bcbc5267bdb733c257c79c0a65c34465ba3"><code>30562bc</code></a> - <em>lint-staged</em> now stages changes to <strong>all tracked files modified by tasks</strong>, including files that weren’t originally staged or didn’t match the configured globs. This can happen when your task has side-effects, or it's a function that ignores the staged files like <code>() => "prettier --write ."</code>.</p> <p>If you have unstaged changes in a file and the task also edits that file, your unstaged changes will be staged too. Use <code>--hide-unstaged</code> to hide your changes while tasks run.</p> </li> </ul> <h3>Patch Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/lint-staged/lint-staged/pull/1860">#1860</a> <a href="https://github.com/lint-staged/lint-staged/commit/4296532155fb893ab8f754a47f3518f691fd4d98"><code>4296532</code></a> - The assignment of staged files to <em>lint-staged</em> configuration files (when using multiple, for example in a monorepo) has been rewritten to be more efficient. As a reminder, each staged file is assigned to exactly one configuration (the closest one), even if that config doesn't match the file in its globs.</p> </li> <li> <p><a href="https://redirect.github.com/lint-staged/lint-staged/pull/1861">#1861</a> <a href="https://github.com/lint-staged/lint-staged/commit/c45f28af47a540730de2bf22fddedeb8fac5952a"><code>c45f28a</code></a> - Fix running parallel tasks for a single glob, when tasks are created by a function. Nesting one level of arrays inside an array of tasks will result in the inner tasks running in parallel. This behavior should now be consistent when creating tasks using functions. In the following example <code>eslint</code> and <code>prettier</code> will run in parallel (for all files, when any JS files are staged):</p> <pre lang="js"><code>import { defineConfig } from 'lint-staged/config' <p>export default defineConfig({ '*.js': () => [['eslint --max-warnings=0 .', 'prettier --list-different .']], }) </code></pre></p> </li> <li> <p><a href="https://redirect.github.com/lint-staged/lint-staged/pull/1859">#1859</a> <a href="https://github.com/lint-staged/lint-staged/commit/f0ea69dc67399e504a08db67594f697d4c24df6d"><code>f0ea69d</code></a> - Various performance improvements from skipping redundant internal Git calls.</p> </li> <li> <p><a href="https://redirect.github.com/lint-staged/lint-staged/pull/1856">#1856</a> <a href="https://github.com/lint-staged/lint-staged/commit/69d7d17ca6d3b10f2ececf8b2756c1927a627821"><code>69d7d17</code></a> - Partially staged changes are hidden in a uniquely-named patch file to avoid multiple invocations of <em>lint-staged</em> overwriting it. This makes it safer to run <em>lint-staged</em> in multiple worktrees at the same time.</p> </li> </ul> <h2>v17.5.1</h2> <h3>Patch Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/lint-staged/lint-staged/pull/1852">#1852</a> <a href="https://github.com/lint-staged/lint-staged/commit/bfcca94e61aef7078a7a0ec8165c0cc2c9baca99"><code>bfcca94</code></a> - Fix TypeScript issue <code>TS1254</code> from <code>defineConfig()</code> by changing the signature from <code>const</code> to a <code>function</code>:</p> <blockquote> <p>A 'const' initializer in an ambient context must be a string or numeric literal or literal enum reference.</p> </blockquote> </li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/lint-staged/lint-staged/blob/main/CHANGELOG.md">lint-staged's changelog</a>.</em></p> <blockquote> <h2>17.6.0</h2> <h3>Minor Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/lint-staged/lint-staged/pull/1850">#1850</a> <a href="https://github.com/lint-staged/lint-staged/commit/938d3f43d07cbd74f08052dfb8a7380ce8868180"><code>938d3f4</code></a> - Task functions like <code>{ title, task }</code> can now use a logger function <code>log()</code> to emit output while the task runs. By default, the output will only be visible if the task fails, unless the <code>--verbose</code> option was used. Additionally, when the task rejects, the error will be shown in the output.</p> <pre lang="js"><code>import { defineConfig } from 'lint-staged/config' <p>export default defineConfig({ '*': { title: 'Fail if PDF files are committed', task: async (filepaths, { log }) => { const pdfFiles = filepaths.filter((f) => f.toLowerCase().endsWith('.pdf')) if (pdfFiles.length > 0) { log('PDF files should not be committed: %s', pdfFiles) throw new Error('Failed') } }, }, }) </code></pre></p> </li> <li> <p><a href="https://redirect.github.com/lint-staged/lint-staged/pull/1854">#1854</a> <a href="https://github.com/lint-staged/lint-staged/commit/30562bcbc5267bdb733c257c79c0a65c34465ba3"><code>30562bc</code></a> - <em>lint-staged</em> now stages changes to <strong>all tracked files modified by tasks</strong>, including files that weren’t originally staged or didn’t match the configured globs. This can happen when your task has side-effects, or it's a function that ignores the staged files like <code>() => "prettier --write ."</code>.</p> <p>If you have unstaged changes in a file and the task also edits that file, your unstaged changes will be staged too. Use <code>--hide-unstaged</code> to hide your changes while tasks run.</p> </li> </ul> <h3>Patch Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/lint-staged/lint-staged/pull/1860">#1860</a> <a href="https://github.com/lint-staged/lint-staged/commit/4296532155fb893ab8f754a47f3518f691fd4d98"><code>4296532</code></a> - The assignment of staged files to <em>lint-staged</em> configuration files (when using multiple, for example in a monorepo) has been rewritten to be more efficient. As a reminder, each staged file is assigned to exactly one configuration (the closest one), even if that config doesn't match the file in its globs.</p> </li> <li> <p><a href="https://redirect.github.com/lint-staged/lint-staged/pull/1861">#1861</a> <a href="https://github.com/lint-staged/lint-staged/commit/c45f28af47a540730de2bf22fddedeb8fac5952a"><code>c45f28a</code></a> - Fix running parallel tasks for a single glob, when tasks are created by a function. Nesting one level of arrays inside an array of tasks will result in the inner tasks running in parallel. This behavior should now be consistent when creating tasks using functions. In the following example <code>eslint</code> and <code>prettier</code> will run in parallel (for all files, when any JS files are staged):</p> <pre lang="js"><code>import { defineConfig } from 'lint-staged/config' <p>export default defineConfig({ '*.js': () => [['eslint --max-warnings=0 .', 'prettier --list-different .']], }) </code></pre></p> </li> <li> <p><a href="https://redirect.github.com/lint-staged/lint-staged/pull/1859">#1859</a> <a href="https://github.com/lint-staged/lint-staged/commit/f0ea69dc67399e504a08db67594f697d4c24df6d"><code>f0ea69d</code></a> - Various performance improvements from skipping redundant internal Git calls.</p> </li> <li> <p><a href="https://redirect.github.com/lint-staged/lint-staged/pull/1856">#1856</a> <a href="https://github.com/lint-staged/lint-staged/commit/69d7d17ca6d3b10f2ececf8b2756c1927a627821"><code>69d7d17</code></a> - Partially staged changes are hidden in a uniquely-named patch file to avoid multiple invocations of <em>lint-staged</em> overwriting it. This makes it safer to run <em>lint-staged</em> in multiple worktrees at the same time.</p> </li> </ul> <h2>17.5.1</h2> <h3>Patch Changes</h3> <ul> <li><a href="https://redirect.github.com/lint-staged/lint-staged/pull/1852">#1852</a> <a href="https://github.com/lint-staged/lint-staged/commit/bfcca94e61aef7078a7a0ec8165c0cc2c9baca99"><code>bfcca94</code></a> - Fix TypeScript issue <code>TS1254</code> from <code>defineConfig()</code> by changing the signature from <code>const</code> to a <code>function</code>:</li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/lint-staged/lint-staged/commit/48f9f4ea4b88eb605de3546e4599083de7a394b2"><code>48f9f4e</code></a> Merge pull request <a href="https://redirect.github.com/lint-staged/lint-staged/issues/1857">#1857</a> from lint-staged/changeset-release/main</li> <li><a href="https://github.com/lint-staged/lint-staged/commit/16b2e213efeec330f7e1576a8d35776822bf698a"><code>16b2e21</code></a> chore(changeset): release</li> <li><a href="https://github.com/lint-staged/lint-staged/commit/195f15677da877431b63dfd001e1ed9369bb1479"><code>195f156</code></a> docs: improve changeset</li> <li><a href="https://github.com/lint-staged/lint-staged/commit/0ba6261cabfdade60e487385ebec4fc2b4b44309"><code>0ba6261</code></a> fix: create hidden directory only when required</li> <li><a href="https://github.com/lint-staged/lint-staged/commit/66ac2de6f71d19256cc5d11026d37e653af11549"><code>66ac2de</code></a> docs: fixes to changesets</li> <li><a href="https://github.com/lint-staged/lint-staged/commit/80af8d7f8246c4246cdf8b53229bd1bf7763f959"><code>80af8d7</code></a> Merge pull request <a href="https://redirect.github.com/lint-staged/lint-staged/issues/1863">#1863</a> from lint-staged/fix-issues</li> <li><a href="https://github.com/lint-staged/lint-staged/commit/e4334881d49d94669bc2b2fb5a73705d822c69bb"><code>e433488</code></a> fix: handle task editing a symlinked file to a regular file, and --fail-on-ch...</li> <li><a href="https://github.com/lint-staged/lint-staged/commit/e5019b32138e301c777c683f95e4a1e17362845f"><code>e5019b3</code></a> fix: handle trailing newlines when detecting changed files</li> <li><a href="https://github.com/lint-staged/lint-staged/commit/74efec85fa276618837d3c4602062ef30d16abf1"><code>74efec8</code></a> ci: run Cygwin and MSYS2 tests on Node.js 26</li> <li><a href="https://github.com/lint-staged/lint-staged/commit/655b7dcd9f7eb57a6f574f03be371190afeb9f5c"><code>655b7dc</code></a> fix: use TypeScript types instead of JSDoc</li> <li>Additional commits viewable in <a href="https://github.com/lint-staged/lint-staged/compare/v16.4.0...v17.6.0">compare view</a></li> </ul> </details> <br /> [](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 this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…oleMurray#2275) Bumps [@testing-library/jest-dom](https://github.com/testing-library/jest-dom) from 6.9.1 to 7.0.1. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/testing-library/jest-dom/releases">@testing-library/jest-dom's releases</a>.</em></p> <blockquote> <h2>v7.0.1</h2> <h2><a href="https://github.com/testing-library/jest-dom/compare/v7.0.0...v7.0.1">7.0.1</a> (2026-08-09)</h2> <h3>Bug Fixes</h3> <ul> <li>declare vitest as an optional peer dependency (<a href="https://redirect.github.com/testing-library/jest-dom/issues/733">#733</a>) (<a href="https://github.com/testing-library/jest-dom/commit/3782c78b3dc9824675afe0cb8f1722f8c96f494d">3782c78</a>)</li> </ul> <h2>v7.0.0</h2> <h1><a href="https://github.com/testing-library/jest-dom/compare/v6.10.0...v7.0.0">7.0.0</a> (2026-07-20)</h1> <h3>Features</h3> <ul> <li>add toContainAnyBy* and toContainOneBy* query matchers (<a href="https://github.com/testing-library/jest-dom/commit/1e39089d850408a583c83495d00d8aa27078933f">1e39089</a>)</li> </ul> <h3>BREAKING CHANGES</h3> <ul> <li><code>@testing-library/dom</code> is now a required peer dependency. The minimum supported Node.js version is now 22.</li> </ul> <p>Repaired release for <a href="https://redirect.github.com/testing-library/jest-dom/pull/731">testing-library/jest-dom#731</a></p> <h2>v6.10.0</h2> <h1><a href="https://github.com/testing-library/jest-dom/compare/v6.9.1...v6.10.0">6.10.0</a> (2026-07-20)</h1> <h3>Features</h3> <ul> <li>add toContainAnyBy* and toContainOneBy* query matchers (<a href="https://redirect.github.com/testing-library/jest-dom/issues/731">#731</a>) (<a href="https://github.com/testing-library/jest-dom/commit/cae44df901cf8e92e3febc0af6fa667b10be6d6a">cae44df</a>)</li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/testing-library/jest-dom/commit/3782c78b3dc9824675afe0cb8f1722f8c96f494d"><code>3782c78</code></a> fix: declare vitest as an optional peer dependency (<a href="https://redirect.github.com/testing-library/jest-dom/issues/733">#733</a>)</li> <li><a href="https://github.com/testing-library/jest-dom/commit/1e39089d850408a583c83495d00d8aa27078933f"><code>1e39089</code></a> feat: add toContainAnyBy* and toContainOneBy* query matchers</li> <li><a href="https://github.com/testing-library/jest-dom/commit/cae44df901cf8e92e3febc0af6fa667b10be6d6a"><code>cae44df</code></a> feat: add toContainAnyBy* and toContainOneBy* query matchers (<a href="https://redirect.github.com/testing-library/jest-dom/issues/731">#731</a>)</li> <li><a href="https://github.com/testing-library/jest-dom/commit/55c07ce5f1c489b5b9dc31a770a84d83a1178072"><code>55c07ce</code></a> ci: switch release to npm trusted publishing (<a href="https://redirect.github.com/testing-library/jest-dom/issues/726">#726</a>)</li> <li><a href="https://github.com/testing-library/jest-dom/commit/213256fa8e0aff45e47920a0bc564f708d1f67de"><code>213256f</code></a> docs: move toHaveSelection from the deprecated section (<a href="https://redirect.github.com/testing-library/jest-dom/issues/717">#717</a>)</li> <li>See full diff in <a href="https://github.com/testing-library/jest-dom/compare/v6.9.1...v7.0.1">compare view</a></li> </ul> </details> <details> <summary>Maintainer changes</summary> <p>This version was pushed to npm by <a href="https://www.npmjs.com/~GitHub%20Actions">GitHub Actions</a>, a new releaser for <code>@testing-library/jest-dom</code> since your current version.</p> </details> <br /> [](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 this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…ColeMurray#2272) Bumps the actions group with 7 updates in the / directory: | Package | From | To | | --- | --- | --- | | [actions/checkout](https://github.com/actions/checkout) | `6` | `7` | | [actions/setup-python](https://github.com/actions/setup-python) | `6` | `7` | | [actions/setup-node](https://github.com/actions/setup-node) | `6` | `7` | | [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) | `6` | `7` | | [actions/upload-artifact](https://github.com/actions/upload-artifact) | `4` | `7` | | [actions/download-artifact](https://github.com/actions/download-artifact) | `4` | `8` | | [actions/github-script](https://github.com/actions/github-script) | `8` | `9` | Updates `actions/checkout` from 6 to 7 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/actions/checkout/releases">actions/checkout's releases</a>.</em></p> <blockquote> <h2>v7.0.0</h2> <h2>What's Changed</h2> <ul> <li>block checking out fork pr for pull_request_target and workflow_run by <a href="https://github.com/aiqiaoy"><code>@aiqiaoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2454">actions/checkout#2454</a></li> <li>Bump actions/publish-immutable-action from 0.0.3 to 0.0.4 in the minor-actions-dependencies group across 1 directory by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/actions/checkout/pull/2458">actions/checkout#2458</a></li> <li>Bump flatted from 3.3.1 to 3.4.2 by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/actions/checkout/pull/2460">actions/checkout#2460</a></li> <li>Bump js-yaml from 4.1.0 to 4.2.0 by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/actions/checkout/pull/2461">actions/checkout#2461</a></li> <li>Bump <code>@actions/core</code> and <code>@actions/tool-cache</code> and Remove uuid by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/actions/checkout/pull/2459">actions/checkout#2459</a></li> <li>upgrade module to esm and update dependencies by <a href="https://github.com/aiqiaoy"><code>@aiqiaoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2463">actions/checkout#2463</a></li> <li>Bump the minor-npm-dependencies group across 1 directory with 3 updates by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/actions/checkout/pull/2462">actions/checkout#2462</a></li> <li>getting ready for checkout v7 release by <a href="https://github.com/aiqiaoy"><code>@aiqiaoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2464">actions/checkout#2464</a></li> <li>update error wording by <a href="https://github.com/aiqiaoy"><code>@aiqiaoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2467">actions/checkout#2467</a></li> </ul> <h2>New Contributors</h2> <ul> <li><a href="https://github.com/aiqiaoy"><code>@aiqiaoy</code></a> made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/2454">actions/checkout#2454</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/checkout/compare/v6.0.3...v7.0.0">https://github.com/actions/checkout/compare/v6.0.3...v7.0.0</a></p> <h2>v6.1.0</h2> <h2>What's Changed</h2> <ul> <li><strong>[BREAKING]</strong> backport <code>allow-unsafe-pr-checkout</code> to v6 by <a href="https://github.com/aiqiaoy"><code>@aiqiaoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2500">actions/checkout#2500</a></li> <li>backport fixes to releases-v6 by <a href="https://github.com/aiqiaoy"><code>@aiqiaoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2527">actions/checkout#2527</a></li> </ul> <p><a href="https://github.blog/changelog/2026-06-18-safer-pull_request_target-defaults-for-github-actions-checkout/">https://github.blog/changelog/2026-06-18-safer-pull_request_target-defaults-for-github-actions-checkout/</a> for more details about this breaking change</p> <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/checkout/compare/v6.0.3...v6.1.0">https://github.com/actions/checkout/compare/v6.0.3...v6.1.0</a></p> <h2>v6.0.3</h2> <h2>What's Changed</h2> <ul> <li>Update changelog by <a href="https://github.com/ericsciple"><code>@ericsciple</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2357">actions/checkout#2357</a></li> <li>fix: expand merge commit SHA regex and add SHA-256 test cases by <a href="https://github.com/yaananth"><code>@yaananth</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2414">actions/checkout#2414</a></li> <li>Fix checkout init for SHA-256 repositories by <a href="https://github.com/yaananth"><code>@yaananth</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2439">actions/checkout#2439</a></li> <li>Update changelog for v6.0.3 by <a href="https://github.com/yaananth"><code>@yaananth</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2446">actions/checkout#2446</a></li> </ul> <h2>New Contributors</h2> <ul> <li><a href="https://github.com/yaananth"><code>@yaananth</code></a> made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/2414">actions/checkout#2414</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/checkout/compare/v6...v6.0.3">https://github.com/actions/checkout/compare/v6...v6.0.3</a></p> <h2>v6.0.2</h2> <h2>What's Changed</h2> <ul> <li>Add orchestration_id to git user-agent when ACTIONS_ORCHESTRATION_ID is set by <a href="https://github.com/TingluoHuang"><code>@TingluoHuang</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2355">actions/checkout#2355</a></li> <li>Fix tag handling: preserve annotations and explicit fetch-tags by <a href="https://github.com/ericsciple"><code>@ericsciple</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2356">actions/checkout#2356</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/checkout/compare/v6.0.1...v6.0.2">https://github.com/actions/checkout/compare/v6.0.1...v6.0.2</a></p> <h2>v6.0.1</h2> <h2>What's Changed</h2> <ul> <li>Update all references from v5 and v4 to v6 by <a href="https://github.com/ericsciple"><code>@ericsciple</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2314">actions/checkout#2314</a></li> <li>Add worktree support for persist-credentials includeIf by <a href="https://github.com/ericsciple"><code>@ericsciple</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2327">actions/checkout#2327</a></li> <li>Clarify v6 README by <a href="https://github.com/ericsciple"><code>@ericsciple</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2328">actions/checkout#2328</a></li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/actions/checkout/blob/main/CHANGELOG.md">actions/checkout's changelog</a>.</em></p> <blockquote> <h1>Changelog</h1> <h2>v7.0.1</h2> <ul> <li>Skip running unsafe pr check if input is default by <a href="https://github.com/aiqiaoy"><code>@aiqiaoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2518">actions/checkout#2518</a></li> <li>Trim only ascii whitespace for branch by <a href="https://github.com/aiqiaoy"><code>@aiqiaoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2521">actions/checkout#2521</a></li> <li>Escape values passed to --unset by <a href="https://github.com/aiqiaoy"><code>@aiqiaoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2530">actions/checkout#2530</a></li> <li>Various dependency updates</li> </ul> <h2>v7.0.0</h2> <ul> <li>Block checking out fork PR for pull_request_target and workflow_run by <a href="https://github.com/aiqiaoy"><code>@aiqiaoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2454">actions/checkout#2454</a></li> <li>Various dependency updates</li> </ul> <h2>v6.0.3</h2> <ul> <li>Fix checkout init for SHA-256 repositories by <a href="https://github.com/yaananth"><code>@yaananth</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2439">actions/checkout#2439</a></li> <li>fix: expand merge commit SHA regex and add SHA-256 test cases by <a href="https://github.com/yaananth"><code>@yaananth</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2414">actions/checkout#2414</a></li> </ul> <h2>v6.0.2</h2> <ul> <li>Fix tag handling: preserve annotations and explicit fetch-tags by <a href="https://github.com/ericsciple"><code>@ericsciple</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2356">actions/checkout#2356</a></li> </ul> <h2>v6.0.1</h2> <ul> <li>Add worktree support for persist-credentials includeIf by <a href="https://github.com/ericsciple"><code>@ericsciple</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2327">actions/checkout#2327</a></li> </ul> <h2>v6.0.0</h2> <ul> <li>Persist creds to a separate file by <a href="https://github.com/ericsciple"><code>@ericsciple</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2286">actions/checkout#2286</a></li> <li>Update README to include Node.js 24 support details and requirements by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2248">actions/checkout#2248</a></li> </ul> <h2>v5.0.1</h2> <ul> <li>Port v6 cleanup to v5 by <a href="https://github.com/ericsciple"><code>@ericsciple</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2301">actions/checkout#2301</a></li> </ul> <h2>v5.0.0</h2> <ul> <li>Update actions checkout to use node 24 by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2226">actions/checkout#2226</a></li> </ul> <h2>v4.3.1</h2> <ul> <li>Port v6 cleanup to v4 by <a href="https://github.com/ericsciple"><code>@ericsciple</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2305">actions/checkout#2305</a></li> </ul> <h2>v4.3.0</h2> <ul> <li>docs: update README.md by <a href="https://github.com/motss"><code>@motss</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1971">actions/checkout#1971</a></li> <li>Add internal repos for checking out multiple repositories by <a href="https://github.com/mouismail"><code>@mouismail</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1977">actions/checkout#1977</a></li> <li>Documentation update - add recommended permissions to Readme by <a href="https://github.com/benwells"><code>@benwells</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2043">actions/checkout#2043</a></li> <li>Adjust positioning of user email note and permissions heading by <a href="https://github.com/joshmgross"><code>@joshmgross</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2044">actions/checkout#2044</a></li> <li>Update README.md by <a href="https://github.com/nebuk89"><code>@nebuk89</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2194">actions/checkout#2194</a></li> <li>Update CODEOWNERS for actions by <a href="https://github.com/TingluoHuang"><code>@TingluoHuang</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2224">actions/checkout#2224</a></li> <li>Update package dependencies by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2236">actions/checkout#2236</a></li> </ul> <h2>v4.2.2</h2> <ul> <li><code>url-helper.ts</code> now leverages well-known environment variables by <a href="https://github.com/jww3"><code>@jww3</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1941">actions/checkout#1941</a></li> <li>Expand unit test coverage for <code>isGhes</code> by <a href="https://github.com/jww3"><code>@jww3</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1946">actions/checkout#1946</a></li> </ul> <h2>v4.2.1</h2> <ul> <li>Check out other refs/* by commit if provided, fall back to ref by <a href="https://github.com/orhantoy"><code>@orhantoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1924">actions/checkout#1924</a></li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/actions/checkout/commit/3d3c42e5aac5ba805825da76410c181273ba90b1"><code>3d3c42e</code></a> prep v7.0.1 release (<a href="https://redirect.github.com/actions/checkout/issues/2531">#2531</a>)</li> <li><a href="https://github.com/actions/checkout/commit/28802689a136bfcdb721715abd713740beecbe07"><code>2880268</code></a> escape values passed to --unset (<a href="https://redirect.github.com/actions/checkout/issues/2530">#2530</a>)</li> <li><a href="https://github.com/actions/checkout/commit/12cd2235efa0937479335606d7c3ac9f6c0973b1"><code>12cd223</code></a> trim only ascii whitespace for branch (<a href="https://redirect.github.com/actions/checkout/issues/2521">#2521</a>)</li> <li><a href="https://github.com/actions/checkout/commit/62661c4e71a304b2823ed026347b8d34c3eac541"><code>62661c4</code></a> skip running unsafe pr check if input is default (<a href="https://redirect.github.com/actions/checkout/issues/2518">#2518</a>)</li> <li><a href="https://github.com/actions/checkout/commit/e8d4307400f9427dba7cb98e488d6ab85f1cec5f"><code>e8d4307</code></a> Bump the minor-actions-dependencies group with 2 updates (<a href="https://redirect.github.com/actions/checkout/issues/2499">#2499</a>)</li> <li><a href="https://github.com/actions/checkout/commit/631c942040754b6e095e929c1677c07e10ed4f87"><code>631c942</code></a> eslint 9 (<a href="https://redirect.github.com/actions/checkout/issues/2474">#2474</a>)</li> <li><a href="https://github.com/actions/checkout/commit/4f1f4aec02e41874fa0262ea8ff5172d7978ad1e"><code>4f1f4ae</code></a> Bump actions/upload-artifact from 4 to 7 (<a href="https://redirect.github.com/actions/checkout/issues/2476">#2476</a>)</li> <li><a href="https://github.com/actions/checkout/commit/ba097532fb203f7e88c9c3c0b899b49469908a92"><code>ba09753</code></a> Bump actions/checkout from 6 to 7 (<a href="https://redirect.github.com/actions/checkout/issues/2488">#2488</a>)</li> <li><a href="https://github.com/actions/checkout/commit/b9e0990d219a03df7633c93f6f005a8fecbcab22"><code>b9e0990</code></a> Bump docker/login-action from 3.3.0 to 4.2.0 (<a href="https://redirect.github.com/actions/checkout/issues/2479">#2479</a>)</li> <li><a href="https://github.com/actions/checkout/commit/e8cb398be4a550817e382abf69e4c12c76fce1f2"><code>e8cb398</code></a> Bump docker/build-push-action from 6.5.0 to 7.2.0 (<a href="https://redirect.github.com/actions/checkout/issues/2478">#2478</a>)</li> <li>Additional commits viewable in <a href="https://github.com/actions/checkout/compare/v6...v7">compare view</a></li> </ul> </details> <br /> Updates `actions/setup-python` from 6 to 7 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/actions/setup-python/releases">actions/setup-python's releases</a>.</em></p> <blockquote> <h2>v7.0.0</h2> <h2>What's Changed</h2> <h3>Enhancements</h3> <ul> <li>Migrate to ESM and upgrade dependencies by <a href="https://github.com/priyagupta108"><code>@priyagupta108</code></a> in <a href="https://redirect.github.com/actions/setup-python/pull/1330">actions/setup-python#1330</a></li> <li>Pin SHA commits and update docs with latest versions by <a href="https://github.com/HarithaVattikuti"><code>@HarithaVattikuti</code></a> in <a href="https://redirect.github.com/actions/setup-python/pull/1338">actions/setup-python#1338</a></li> <li>Remove the pip-install input by <a href="https://github.com/gowridurgad"><code>@gowridurgad</code></a> in <a href="https://redirect.github.com/actions/setup-python/pull/1336">actions/setup-python#1336</a></li> </ul> <h3>Bug Fix</h3> <ul> <li>Fix to Classify stderr warning messages as warnings instead of errors in annotations by <a href="https://github.com/lmvysakh"><code>@lmvysakh</code></a> in <a href="https://redirect.github.com/actions/setup-python/pull/1335">actions/setup-python#1335</a></li> <li>Validate and retry manifest fetch to prevent silent failures by <a href="https://github.com/priyagupta108"><code>@priyagupta108</code></a> in <a href="https://redirect.github.com/actions/setup-python/pull/1332">actions/setup-python#1332</a></li> </ul> <h3>Dependency Upgrade</h3> <ul> <li>Bump certifi from 2020.6.20 to 2024.7.4 in /<strong>tests</strong>/data by <a href="https://github.com/dependabot"><code>@dependabot</code></a> in <a href="https://redirect.github.com/actions/setup-python/pull/1328">actions/setup-python#1328</a></li> <li>Remove EOL Python versions and Bumps numpy text fixture by <a href="https://github.com/priya-kinthali"><code>@priya-kinthali</code></a> in <a href="https://redirect.github.com/actions/setup-python/pull/1333">actions/setup-python#1333</a></li> <li>Upgrade <code>@actions/cache</code> to 6.2.0 by <a href="https://github.com/philip-gai"><code>@philip-gai</code></a> in <a href="https://redirect.github.com/actions/setup-python/pull/1337">actions/setup-python#1337</a></li> </ul> <h2>New Contributors</h2> <ul> <li><a href="https://github.com/lmvysakh"><code>@lmvysakh</code></a> made their first contribution in <a href="https://redirect.github.com/actions/setup-python/pull/1335">actions/setup-python#1335</a></li> <li><a href="https://github.com/philip-gai"><code>@philip-gai</code></a> made their first contribution in <a href="https://redirect.github.com/actions/setup-python/pull/1337">actions/setup-python#1337</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/setup-python/compare/v6...v7.0.0">https://github.com/actions/setup-python/compare/v6...v7.0.0</a></p> <h2>v6.3.0</h2> <h2>What's Changed</h2> <h3>Enhancement</h3> <ul> <li>Add RHEL support and include Linux distro in cache keys by <a href="https://github.com/priyagupta108"><code>@priyagupta108</code></a> in <a href="https://redirect.github.com/actions/setup-python/pull/1323">actions/setup-python#1323</a></li> <li>Fix pip cache error handling on Windows by <a href="https://github.com/priyagupta108"><code>@priyagupta108</code></a> in <a href="https://redirect.github.com/actions/setup-python/pull/1040">actions/setup-python#1040</a></li> </ul> <h3>Dependency update</h3> <ul> <li>Upgrade minimatch from 3.1.2 to 3.1.5 by <a href="https://github.com/dependabot"><code>@dependabot</code></a> in <a href="https://redirect.github.com/actions/setup-python/pull/1281">actions/setup-python#1281</a></li> <li>Upgrade actions dependencies by <a href="https://github.com/gowridurgad"><code>@gowridurgad</code></a> with <a href="https://github.com/Copilot"><code>@Copilot</code></a> in <a href="https://redirect.github.com/actions/setup-python/pull/1303">actions/setup-python#1303</a></li> <li>Upgrade <code>@actions/cache</code> to 5.1.0, log cache write denied by <a href="https://github.com/jasongin"><code>@jasongin</code></a> in <a href="https://redirect.github.com/actions/setup-python/pull/1324">actions/setup-python#1324</a></li> <li>Upgrade dependency versions and test workflow configuration by <a href="https://github.com/HarithaVattikuti"><code>@HarithaVattikuti</code></a> in <a href="https://redirect.github.com/actions/setup-python/pull/1322">actions/setup-python#1322</a></li> </ul> <h3>Documentation</h3> <ul> <li>Update advanced-usage.md by <a href="https://github.com/Dunky-Z"><code>@Dunky-Z</code></a> in <a href="https://redirect.github.com/actions/setup-python/pull/811">actions/setup-python#811</a></li> </ul> <h2>New Contributors</h2> <ul> <li><a href="https://github.com/gowridurgad"><code>@gowridurgad</code></a> with <a href="https://github.com/Copilot"><code>@Copilot</code></a> made their first contribution in <a href="https://redirect.github.com/actions/setup-python/pull/1303">actions/setup-python#1303</a></li> <li><a href="https://github.com/jasongin"><code>@jasongin</code></a> made their first contribution in <a href="https://redirect.github.com/actions/setup-python/pull/1324">actions/setup-python#1324</a></li> <li><a href="https://github.com/Dunky-Z"><code>@Dunky-Z</code></a> made their first contribution in <a href="https://redirect.github.com/actions/setup-python/pull/811">actions/setup-python#811</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/setup-python/compare/v6.2.0...v6.3.0">https://github.com/actions/setup-python/compare/v6.2.0...v6.3.0</a></p> <h2>v6.2.0</h2> <h2>What's Changed</h2> <h3>Dependency Upgrades</h3> <ul> <li>Upgrade dependencies to Node 24 compatible versions by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/setup-python/pull/1259">actions/setup-python#1259</a></li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/actions/setup-python/commit/5fda3b95a4ea91299a34e894583c3862153e4b97"><code>5fda3b9</code></a> Pin SHA commits and update docs with latest versions (<a href="https://redirect.github.com/actions/setup-python/issues/1338">#1338</a>)</li> <li><a href="https://github.com/actions/setup-python/commit/4ab7e95f05e168b4356aebde89dd84f59c283d8e"><code>4ab7e95</code></a> Merge pull request <a href="https://redirect.github.com/actions/setup-python/issues/1337">#1337</a> from actions/philip-gai/bump-actions-cache-6-2-0</li> <li><a href="https://github.com/actions/setup-python/commit/0f3a009f475dbea83c0371cd85d099690fee8c5c"><code>0f3a009</code></a> Remove the pip-install input (<a href="https://redirect.github.com/actions/setup-python/issues/1336">#1336</a>)</li> <li><a href="https://github.com/actions/setup-python/commit/f8cf4291c8b8e273ddd26e569454615c7315d932"><code>f8cf429</code></a> Migrate to ESM and upgrade dependencies (<a href="https://redirect.github.com/actions/setup-python/issues/1330">#1330</a>)</li> <li><a href="https://github.com/actions/setup-python/commit/54baeea5b34417d10a7479663a23cca53ea209b5"><code>54baeea</code></a> Validate and retry manifest fetch to prevent silent failures (<a href="https://redirect.github.com/actions/setup-python/issues/1332">#1332</a>)</li> <li><a href="https://github.com/actions/setup-python/commit/c7092773a316760f4ecfe498e4af668a4dafeac5"><code>c709277</code></a> Annotation code fix (<a href="https://redirect.github.com/actions/setup-python/issues/1335">#1335</a>)</li> <li><a href="https://github.com/actions/setup-python/commit/6849080452e69b330395e8a6d23cf90f56d76a1a"><code>6849080</code></a> remove EOL Python versions and Bumps numpy text fixture (<a href="https://redirect.github.com/actions/setup-python/issues/1333">#1333</a>)</li> <li><a href="https://github.com/actions/setup-python/commit/0903b469fbf4441aadfe4f4b249dc5b1fba3a73e"><code>0903b46</code></a> Bump certifi from 2020.6.20 to 2024.7.4 in /<strong>tests</strong>/data (<a href="https://redirect.github.com/actions/setup-python/issues/1328">#1328</a>)</li> <li>See full diff in <a href="https://github.com/actions/setup-python/compare/v6...v7">compare view</a></li> </ul> </details> <br /> Updates `actions/setup-node` from 6 to 7 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/actions/setup-node/releases">actions/setup-node's releases</a>.</em></p> <blockquote> <h2>v7.0.0</h2> <h2>What's Changed</h2> <h3>Enhancements:</h3> <ul> <li>Add cache-primary-key and cache-matched-key as outputs by <a href="https://github.com/gowridurgad"><code>@gowridurgad</code></a> in <a href="https://redirect.github.com/actions/setup-node/pull/1577">actions/setup-node#1577</a></li> <li>Migrate to ESM and upgrade dependencies by <a href="https://github.com/gowridurgad"><code>@gowridurgad</code></a> in <a href="https://redirect.github.com/actions/setup-node/pull/1574">actions/setup-node#1574</a></li> </ul> <h3>Bug fixes:</h3> <ul> <li>Remove dummy NODE_AUTH_TOKEN export by <a href="https://github.com/gowridurgad"><code>@gowridurgad</code></a> in <a href="https://redirect.github.com/actions/setup-node/pull/1558">actions/setup-node#1558</a></li> <li>Only use <code>mirrorToken</code> in <code>getManifest</code> if it's provided by <a href="https://github.com/deiga"><code>@deiga</code></a> in <a href="https://redirect.github.com/actions/setup-node/pull/1548">actions/setup-node#1548</a></li> </ul> <h3>Documentation updates:</h3> <ul> <li>Add documentation for publishing to npm with Trusted Publisher (OIDC) by <a href="https://github.com/chiranjib-swain"><code>@chiranjib-swain</code></a> in <a href="https://redirect.github.com/actions/setup-node/pull/1536">actions/setup-node#1536</a></li> <li>docs: Update restore-only cache documentation by <a href="https://github.com/priya-kinthali"><code>@priya-kinthali</code></a> in <a href="https://redirect.github.com/actions/setup-node/pull/1550">actions/setup-node#1550</a></li> <li>docs: Update caching recommendations to mitigate cache poisoning risks by <a href="https://github.com/chiranjib-swain"><code>@chiranjib-swain</code></a> in <a href="https://redirect.github.com/actions/setup-node/pull/1567">actions/setup-node#1567</a></li> </ul> <h3>Dependency update:</h3> <ul> <li>Upgrade <code>@actions/cache</code> to 5.1.0, log cache write denied by <a href="https://github.com/jasongin"><code>@jasongin</code></a> in <a href="https://redirect.github.com/actions/setup-node/pull/1569">actions/setup-node#1569</a></li> </ul> <h2>New Contributors</h2> <ul> <li><a href="https://github.com/chiranjib-swain"><code>@chiranjib-swain</code></a> made their first contribution in <a href="https://redirect.github.com/actions/setup-node/pull/1536">actions/setup-node#1536</a></li> <li><a href="https://github.com/deiga"><code>@deiga</code></a> made their first contribution in <a href="https://redirect.github.com/actions/setup-node/pull/1548">actions/setup-node#1548</a></li> <li><a href="https://github.com/jasongin"><code>@jasongin</code></a> made their first contribution in <a href="https://redirect.github.com/actions/setup-node/pull/1569">actions/setup-node#1569</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/setup-node/compare/v6...v7.0.0">https://github.com/actions/setup-node/compare/v6...v7.0.0</a></p> <h2>v6.5.0</h2> <h2>What's Changed</h2> <ul> <li>Update <code>@actions/cache</code> to 5.1.0 and add security overrides for undici and fast-xml-parser by <a href="https://github.com/HarithaVattikuti"><code>@HarithaVattikuti</code></a> in <a href="https://redirect.github.com/actions/setup-node/pull/1579">actions/setup-node#1579</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/setup-node/compare/v6.4.0...v6.5.0">https://github.com/actions/setup-node/compare/v6.4.0...v6.5.0</a></p> <h2>v6.4.0</h2> <h2>What's Changed</h2> <h3>Dependency updates:</h3> <ul> <li>Upgrade <a href="https://github.com/actions"><code>@actions</code></a> dependencies by <a href="https://github.com/Copilot"><code>@Copilot</code></a> in <a href="https://redirect.github.com/actions/setup-node/pull/1525">actions/setup-node#1525</a></li> <li>Update Node.js versions in versions.yml and bump package to v6.4.0 by <a href="https://github.com/priya-kinthali"><code>@priya-kinthali</code></a> in <a href="https://redirect.github.com/actions/setup-node/pull/1533">actions/setup-node#1533</a></li> </ul> <h2>New Contributors</h2> <ul> <li><a href="https://github.com/Copilot"><code>@Copilot</code></a> made their first contribution in <a href="https://redirect.github.com/actions/setup-node/pull/1525">actions/setup-node#1525</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/setup-node/compare/v6...v6.4.0">https://github.com/actions/setup-node/compare/v6...v6.4.0</a></p> <h2>v6.3.0</h2> <h2>What's Changed</h2> <h3>Enhancements:</h3> <ul> <li>Support parsing <code>devEngines</code> field by <a href="https://github.com/susnux"><code>@susnux</code></a> in <a href="https://redirect.github.com/actions/setup-node/pull/1283">actions/setup-node#1283</a></li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/actions/setup-node/commit/820762786026740c76f36085b0efc47a31fe5020"><code>8207627</code></a> Migrate to ESM and upgrade dependencies (<a href="https://redirect.github.com/actions/setup-node/issues/1574">#1574</a>)</li> <li><a href="https://github.com/actions/setup-node/commit/04be95cf3511ea51ebf9f224ddfb99cc7ab87cd4"><code>04be95c</code></a> Add cache-primary-key and cache-matched-key as outputs (<a href="https://redirect.github.com/actions/setup-node/issues/1577">#1577</a>)</li> <li><a href="https://github.com/actions/setup-node/commit/7c2c68d20d402ed6a201ada70a81341941093140"><code>7c2c68d</code></a> docs: Update caching recommendations to mitigate cache poisoning risks (<a href="https://redirect.github.com/actions/setup-node/issues/1567">#1567</a>)</li> <li><a href="https://github.com/actions/setup-node/commit/6a61c0375d66246de94630495909f12cf8dac84d"><code>6a61c03</code></a> Merge pull request <a href="https://redirect.github.com/actions/setup-node/issues/1569">#1569</a> from jasongin/update-actions-cache-5.1.0</li> <li><a href="https://github.com/actions/setup-node/commit/30eb73b41ded577900c1ebf968ef95cdf8f7434f"><code>30eb73b</code></a> Resolve high-severity audit issues</li> <li><a href="https://github.com/actions/setup-node/commit/4e1a87a501d0302f99e30e2748568adcb388d09f"><code>4e1a87a</code></a> Update dist</li> <li><a href="https://github.com/actions/setup-node/commit/360237f0c01778d0c17291f75c56d6feae4f7574"><code>360237f</code></a> Strict equality</li> <li><a href="https://github.com/actions/setup-node/commit/4f8aac5beb2f0854bc79651567a18c67eb0b9de3"><code>4f8aac5</code></a> Bump <code>@actions/cache</code> to 5.1.0, log cache write denied</li> <li><a href="https://github.com/actions/setup-node/commit/f4a67bbeca970f103397d3d2b9462cf787cd2980"><code>f4a67bb</code></a> Only use <code>mirrorToken</code> in <code>getManifest</code> if it's provided (<a href="https://redirect.github.com/actions/setup-node/issues/1548">#1548</a>)</li> <li><a href="https://github.com/actions/setup-node/commit/0355742c943ddb13ca8a6b700f824231caa91e75"><code>0355742</code></a> Remove dummy NODE_AUTH_TOKEN export (<a href="https://redirect.github.com/actions/setup-node/issues/1558">#1558</a>)</li> <li>Additional commits viewable in <a href="https://github.com/actions/setup-node/compare/v6...v7">compare view</a></li> </ul> </details> <br /> Updates `astral-sh/setup-uv` from 6 to 7 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/astral-sh/setup-uv/releases">astral-sh/setup-uv's releases</a>.</em></p> <blockquote> <h2>v7.0.0 🌈 node24 and a lot of bugfixes</h2> <h2>Changes</h2> <p>This release comes with a load of bug fixes and a speed up. Because of switching from node20 to node24 it is also a breaking change. If you are running on GitHub hosted runners this will just work, if you are using self-hosted runners make sure, that your runners are up to date. If you followed the normal installation instructions your self-hosted runner will keep itself updated.</p> <p>This release also removes the deprecated input <code>server-url</code> which was used to download uv releases from a different server. The <a href="https://github.com/astral-sh/setup-uv?tab=readme-ov-file#manifest-file">manifest-file</a> input supersedes that functionality by adding a flexible way to define available versions and where they should be downloaded from.</p> <h3>Fixes</h3> <ul> <li>The action now respects when the environment variable <code>UV_CACHE_DIR</code> is already set and does not overwrite it. It now also finds <a href="https://docs.astral.sh/uv/reference/settings/#cache-dir">cache-dir</a> settings in config files if you set them.</li> <li>Some users encountered problems that <a href="https://github.com/astral-sh/setup-uv?tab=readme-ov-file#disable-cache-pruning">cache pruning</a> took forever because they had some <code>uv</code> processes running in the background. Starting with uv version <code>0.8.24</code> this action uses <code>uv cache prune --ci --force</code> to ignore the running processes</li> <li>If you just want to install uv but not have it available in path, this action now respects <code>UV_NO_MODIFY_PATH</code></li> <li>Some other actions also set the env var <code>UV_CACHE_DIR</code>. This action can now deal with that but as this could lead to unwanted behavior in some edgecases a warning is now displayed.</li> </ul> <h3>Improvements</h3> <p>If you are using minimum version specifiers for the version of uv to install for example</p> <pre lang="toml"><code>[tool.uv] required-version = ">=0.8.17" </code></pre> <p>This action now detects that and directly uses the latest version. Previously it would download all available releases from the uv repo to determine the highest matching candidate for the version specifier, which took much more time.</p> <p>If you are using other specifiers like <code>0.8.x</code> this action still needs to download all available releases because the specifier defines an upper bound (not 0.9.0 or later) and "latest" would possibly not satisfy that.</p> <h2>🚨 Breaking changes</h2> <ul> <li>Use node24 instead of node20 <a href="https://github.com/eifinger"><code>@eifinger</code></a> (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/608">#608</a>)</li> <li>Remove deprecated input server-url <a href="https://github.com/eifinger"><code>@eifinger</code></a> (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/607">#607</a>)</li> </ul> <h2>🐛 Bug fixes</h2> <ul> <li>Respect UV_CACHE_DIR and cache-dir <a href="https://github.com/eifinger"><code>@eifinger</code></a> (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/612">#612</a>)</li> <li>Use --force when pruning cache <a href="https://github.com/eifinger"><code>@eifinger</code></a> (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/611">#611</a>)</li> <li>Respect UV_NO_MODIFY_PATH <a href="https://github.com/eifinger"><code>@eifinger</code></a> (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/603">#603</a>)</li> <li>Warn when <code>UV_CACHE_DIR</code> has changed <a href="https://github.com/jamesbraza"><code>@jamesbraza</code></a> (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/601">#601</a>)</li> </ul> <h2>🚀 Enhancements</h2> <ul> <li>Shortcut to latest version for minimum version specifier <a href="https://github.com/eifinger"><code>@eifinger</code></a> (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/598">#598</a>)</li> </ul> <h2>🧰 Maintenance</h2> <ul> <li>Bump dependencies <a href="https://github.com/eifinger"><code>@eifinger</code></a> (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/613">#613</a>)</li> <li>Fix test-uv-no-modify-path <a href="https://github.com/eifinger"><code>@eifinger</code></a> (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/604">#604</a>)</li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/astral-sh/setup-uv/commit/37802adc94f370d6bfd71619e3f0bf239e1f3b78"><code>37802ad</code></a> Fetch uv from Astral's mirror by default (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/809">#809</a>)</li> <li><a href="https://github.com/astral-sh/setup-uv/commit/9f00d186ce05fb186322da2c89bac726cc9005f5"><code>9f00d18</code></a> chore(deps): bump zizmorcore/zizmor-action from 0.5.0 to 0.5.2 (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/808">#808</a>)</li> <li><a href="https://github.com/astral-sh/setup-uv/commit/fd8f376b22c1219b8021404baf54539061a0339a"><code>fd8f376</code></a> Switch to ESM for source and test, use CommonJS for dist (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/806">#806</a>)</li> <li><a href="https://github.com/astral-sh/setup-uv/commit/f9070de1eac8816a02afc90f4a12cc6fe70d141c"><code>f9070de</code></a> Bump deps (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/805">#805</a>)</li> <li><a href="https://github.com/astral-sh/setup-uv/commit/cadb67bdc9933e2f8795f82ab47acc5f41ed4d31"><code>cadb67b</code></a> chore: update known checksums for 0.10.10 (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/804">#804</a>)</li> <li><a href="https://github.com/astral-sh/setup-uv/commit/e06108dd0aef18192324c70427afc47652e63a82"><code>e06108d</code></a> Use astral-sh/versions as primary version provider (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/802">#802</a>)</li> <li><a href="https://github.com/astral-sh/setup-uv/commit/0f6ec07aafe46cff7a322c837e5ba37d95c82b00"><code>0f6ec07</code></a> docs: replace copilot instructions with AGENTS.md (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/794">#794</a>)</li> <li><a href="https://github.com/astral-sh/setup-uv/commit/821e5c98158b218f7c8e97d0ea3862c6d6425e8b"><code>821e5c9</code></a> docs: add cross-client dependabot rollup skill (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/793">#793</a>)</li> <li><a href="https://github.com/astral-sh/setup-uv/commit/6ee6290f1cbc4156c0bdd66691b2c144ef8df19a"><code>6ee6290</code></a> chore(deps): bump versions (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/792">#792</a>)</li> <li><a href="https://github.com/astral-sh/setup-uv/commit/9f332a133a60f39d23361d5262fa95793aa02590"><code>9f332a1</code></a> Add riscv64 architecture support to platform detection (<a href="https://redirect.github.com/astral-sh/setup-uv/issues/791">#791</a>)</li> <li>Additional commits viewable in <a href="https://github.com/astral-sh/setup-uv/compare/v6...v7">compare view</a></li> </ul> </details> <br /> Updates `actions/upload-artifact` from 4 to 7 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/actions/upload-artifact/releases">actions/upload-artifact's releases</a>.</em></p> <blockquote> <h2>v7.0.0</h2> <h2>v7 What's new</h2> <h3>Direct Uploads</h3> <p>Adds support for uploading single files directly (unzipped). Callers can set the new <code>archive</code> parameter to <code>false</code> to skip zipping the file during upload. Right now, we only support single files. The action will fail if the glob passed resolves to multiple files. The <code>name</code> parameter is also ignored with this setting. Instead, the name of the artifact will be the name of the uploaded file.</p> <h3>ESM</h3> <p>To support new versions of the <code>@actions/*</code> packages, we've upgraded the package to ESM.</p> <h2>What's Changed</h2> <ul> <li>Add proxy integration test by <a href="https://github.com/Link"><code>@Link</code></a>- in <a href="https://redirect.github.com/actions/upload-artifact/pull/754">actions/upload-artifact#754</a></li> <li>Upgrade the module to ESM and bump dependencies by <a href="https://github.com/danwkennedy"><code>@danwkennedy</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/762">actions/upload-artifact#762</a></li> <li>Support direct file uploads by <a href="https://github.com/danwkennedy"><code>@danwkennedy</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/764">actions/upload-artifact#764</a></li> </ul> <h2>New Contributors</h2> <ul> <li><a href="https://github.com/Link"><code>@Link</code></a>- made their first contribution in <a href="https://redirect.github.com/actions/upload-artifact/pull/754">actions/upload-artifact#754</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/upload-artifact/compare/v6...v7.0.0">https://github.com/actions/upload-artifact/compare/v6...v7.0.0</a></p> <h2>v6.0.0</h2> <h2>v6 - What's new</h2> <blockquote> <p>[!IMPORTANT] actions/upload-artifact@v6 now runs on Node.js 24 (<code>runs.using: node24</code>) and requires a minimum Actions Runner version of 2.327.1. If you are using self-hosted runners, ensure they are updated before upgrading.</p> </blockquote> <h3>Node.js 24</h3> <p>This release updates the runtime to Node.js 24. v5 had preliminary support for Node.js 24, however this action was by default still running on Node.js 20. Now this action by default will run on Node.js 24.</p> <h2>What's Changed</h2> <ul> <li>Upload Artifact Node 24 support by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/719">actions/upload-artifact#719</a></li> <li>fix: update <code>@actions/artifact</code> for Node.js 24 punycode deprecation by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/744">actions/upload-artifact#744</a></li> <li>prepare release v6.0.0 for Node.js 24 support by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/745">actions/upload-artifact#745</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/upload-artifact/compare/v5.0.0...v6.0.0">https://github.com/actions/upload-artifact/compare/v5.0.0...v6.0.0</a></p> <h2>v5.0.0</h2> <h2>What's Changed</h2> <p><strong>BREAKING CHANGE:</strong> this update supports Node <code>v24.x</code>. This is not a breaking change per-se but we're treating it as such.</p> <ul> <li>Update README.md by <a href="https://github.com/GhadimiR"><code>@GhadimiR</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/681">actions/upload-artifact#681</a></li> <li>Update README.md by <a href="https://github.com/nebuk89"><code>@nebuk89</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/712">actions/upload-artifact#712</a></li> <li>Readme: spell out the first use of GHES by <a href="https://github.com/danwkennedy"><code>@danwkennedy</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/727">actions/upload-artifact#727</a></li> <li>Update GHES guidance to include reference to Node 20 version by <a href="https://github.com/patrikpolyak"><code>@patrikpolyak</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/725">actions/upload-artifact#725</a></li> <li>Bump <code>@actions/artifact</code> to <code>v4.0.0</code></li> <li>Prepare <code>v5.0.0</code> by <a href="https://github.com/danwkennedy"><code>@danwkennedy</code></a> in <a href="https://redirect.github.com/actions/upload-artifact/pull/734">actions/upload-artifact#734</a></li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/actions/upload-artifact/commit/043fb46d1a93c77aae656e7c1c64a875d1fc6a0a"><code>043fb46</code></a> Merge pull request <a href="https://redirect.github.com/actions/upload-artifact/issues/797">#797</a> from actions/yacaovsnc/update-dependency</li> <li><a href="https://github.com/actions/upload-artifact/commit/634250c1388765ea7ed0f053e636f1f399000b94"><code>634250c</code></a> Include changes in typespec/ts-http-runtime 0.3.5</li> <li><a href="https://github.com/actions/upload-artifact/commit/e454baaac2be505c9450e11b8f3215c6fc023ce8"><code>e454baa</code></a> Readme: bump all the example versions to v7 (<a href="https://redirect.github.com/actions/upload-artifact/issues/796">#796</a>)</li> <li><a href="https://github.com/actions/upload-artifact/commit/74fad66b98a6d799dc004d3353ccd0e6f6b2530e"><code>74fad66</code></a> Update the readme with direct upload details (<a href="https://redirect.github.com/actions/upload-artifact/issues/795">#795</a>)</li> <li><a href="https://github.com/actions/upload-artifact/commit/bbbca2ddaa5d8feaa63e36b76fdaad77386f024f"><code>bbbca2d</code></a> Support direct file uploads (<a href="https://redirect.github.com/actions/upload-artifact/issues/764">#764</a>)</li> <li><a href="https://github.com/actions/upload-artifact/commit/589182c5a4cec8920b8c1bce3e2fab1c97a02296"><code>589182c</code></a> Upgrade the module to ESM and bump dependencies (<a href="https://redirect.github.com/actions/upload-artifact/issues/762">#762</a>)</li> <li><a href="https://github.com/actions/upload-artifact/commit/47309c993abb98030a35d55ef7ff34b7fa1074b5"><code>47309c9</code></a> Merge pull request <a href="https://redirect.github.com/actions/upload-artifact/issues/754">#754</a> from actions/Link-/add-proxy-integration-tests</li> <li><a href="https://github.com/actions/upload-artifact/commit/02a8460834e70dab0ce194c64360c59dc1475ef0"><code>02a8460</code></a> Add proxy integration test</li> <li><a href="https://github.com/actions/upload-artifact/commit/b7c566a772e6b6bfb58ed0dc250532a479d7789f"><code>b7c566a</code></a> Merge pull request <a href="https://redirect.github.com/actions/upload-artifact/issues/745">#745</a> from actions/upload-artifact-v6-release</li> <li><a href="https://github.com/actions/upload-artifact/commit/e516bc8500aaf3d07d591fcd4ae6ab5f9c391d5b"><code>e516bc8</code></a> docs: correct description of Node.js 24 support in README</li> <li>Additional commits viewable in <a href="https://github.com/actions/upload-artifact/compare/v4...v7">compare view</a></li> </ul> </details> <br /> Updates `actions/download-artifact` from 4 to 8 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/actions/download-artifact/releases">actions/download-artifact's releases</a>.</em></p> <blockquote> <h2>v8.0.0</h2> <h2>v8 - What's new</h2> <blockquote> <p>[!IMPORTANT] actions/download-artifact@v8 has been migrated to an ESM module. This should be transparent to the caller but forks might need to make significant changes.</p> </blockquote> <blockquote> <p>[!IMPORTANT] Hash mismatches will now error by default. Users can override this behavior with a setting change (see below).</p> </blockquote> <h3>Direct downloads</h3> <p>To support direct uploads in <code>actions/upload-artifact</code>, the action will no longer attempt to unzip all downloaded files. Instead, the action checks the <code>Content-Type</code> header ahead of unzipping and skips non-zipped files. Callers wishing to download a zipped file as-is can also set the new <code>skip-decompress</code> parameter to <code>true</code>.</p> <h3>Enforced checks (breaking)</h3> <p>A previous release introduced digest checks on the download. If a download hash didn't match the expected hash from the server, the action would log a warning. Callers can now configure the behavior on mismatch with the <code>digest-mismatch</code> parameter. To be secure by default, we are now defaulting the behavior to <code>error</code> which will fail the workflow run.</p> <h3>ESM</h3> <p>To support new versions of the @actions/* packages, we've upgraded the package to ESM.</p> <h2>What's Changed</h2> <ul> <li>Don't attempt to un-zip non-zipped downloads by <a href="https://github.com/danwkennedy"><code>@danwkennedy</code></a> in <a href="https://redirect.github.com/actions/download-artifact/pull/460">actions/download-artifact#460</a></li> <li>Add a setting to specify what to do on hash mismatch and default it to <code>error</code> by <a href="https://github.com/danwkennedy"><code>@danwkennedy</code></a> in <a href="https://redirect.github.com/actions/download-artifact/pull/461">actions/download-artifact#461</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/download-artifact/compare/v7...v8.0.0">https://github.com/actions/download-artifact/compare/v7...v8.0.0</a></p> <h2>v7.0.0</h2> <h2>v7 - What's new</h2> <blockquote> <p>[!IMPORTANT] actions/download-artifact@v7 now runs on Node.js 24 (<code>runs.using: node24</code>) and requires a minimum Actions Runner version of 2.327.1. If you are using self-hosted runners, ensure they are updated before upgrading.</p> </blockquote> <h3>Node.js 24</h3> <p>This release updates the runtime to Node.js 24. v6 had preliminary support for Node 24, however this action was by default still running on Node.js 20. Now this action by default will run on Node.js 24.</p> <h2>What's Changed</h2> <ul> <li>Update GHES guidance to include reference to Node 20 version by <a href="https://github.com/patrikpolyak"><code>@patrikpolyak</code></a> in <a href="https://redirect.github.com/actions/download-artifact/pull/440">actions/download-artifact#440</a></li> <li>Download Artifact Node24 support by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/download-artifact/pull/415">actions/download-artifact#415</a></li> <li>fix: update <code>@actions/artifact</code> to fix Node.js 24 punycode deprecation by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/download-artifact/pull/451">actions/download-artifact#451</a></li> <li>prepare release v7.0.0 for Node.js 24 support by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/download-artifact/pull/452">actions/download-artifact#452</a></li> </ul> <h2>New Contributors</h2> <ul> <li><a href="https://github.com/patrikpolyak"><code>@patrikpolyak</code></a> made their first contribution in <a href="https://redirect.github.com/actions/download-artifact/pull/440">actions/download-artifact#440</a></li> <li><a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> made their first contribution in <a href="https://redirect.github.com/actions/download-artifact/pull/415">actions/download-artifact#415</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/download-artifact/compare/v6.0.0...v7.0.0">https://github.com/actions/download-artifact/compare/v6.0.0...v7.0.0</a></p> <h2>v6.0.0</h2> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/actions/download-artifact/commit/3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c"><code>3e5f45b</code></a> Add regression tests for CJK characters (<a href="https://redirect.github.com/actions/download-artifact/issues/471">#471</a>)</li> <li><a href="https://github.com/actions/download-artifact/commit/e6d03f67377d4412c7aa56a8e2e4988e6ec479dd"><code>e6d03f6</code></a> Add a regression test for artifact name + content-type mismatches (<a href="https://redirect.github.com/actions/download-artifact/issues/472">#472</a>)</li> <li><a href="https://github.com/actions/download-artifact/commit/70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3"><code>70fc10c</code></a> Merge pull request <a href="https://redirect.github.com/actions/download-artifact/issues/461">#461</a> from actions/danwkennedy/digest-mismatch-behavior</li> <li><a href="https://github.com/actions/download-artifact/commit/f258da9a506b755b84a09a531814700b86ccfc62"><code>f258da9</code></a> Add change docs</li> <li><a href="https://github.com/actions/download-artifact/commit/ccc058e5fbb0bb2352213eaec3491e117cbc4a5c"><code>ccc058e</code></a> Fix linting issues</li> <li><a href="https://github.com/actions/download-artifact/commit/bd7976ba57ecea96e6f3df575eb922d11a12a9fd"><code>bd7976b</code></a> Add a setting to specify what to do on hash mismatch and default it to <code>error</code></li> <li><a href="https://github.com/actions/download-artifact/commit/ac21fcf45e0aaee541c0f7030558bdad38d77d6c"><code>ac21fcf</code></a> Merge pull request <a href="https://redirect.github.com/actions/download-artifact/issues/460">#460</a> from actions/danwkennedy/download-no-unzip</li> <li><a href="https://github.com/actions/download-artifact/commit/15999bff51058bc7c19b50ebbba518eaef7c26c0"><code>15999bf</code></a> Add note about package bumps</li> <li><a href="https://github.com/actions/download-artifact/commit/974686ed5098c7f9c9289ec946b9058e496a2561"><code>974686e</code></a> Bump the version to <code>v8</code> and add release notes</li> <li><a href="https://github.com/actions/download-artifact/commit/fbe48b1d2756394be4cd4358ed3bc1343b330e75"><code>fbe48b1</code></a> Update test names to make it clearer what they do</li> <li>Additional commits viewable in <a href="https://github.com/actions/download-artifact/compare/v4...v8">compare view</a></li> </ul> </details> <br /> Updates `actions/github-script` from 8 to 9 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/actions/github-script/releases">actions/github-script's releases</a>.</em></p> <blockquote> <h2>v9.0.0</h2> <p><strong>New features:</strong></p> <ul> <li><strong><code>getOctokit</code> factory function</strong> — Available directly in the script context. Create additional authenticated Octokit clients with different tokens for multi-token workflows, GitHub App tokens, and cross-org access. See <a href="https://github.com/actions/github-script#creating-additional-clients-with-getoctokit">Creating additional clients with <code>getOctokit</code></a> for details and examples.</li> <li><strong>Orchestration ID in user-agent</strong> — The <code>ACTIONS_ORCHESTRATION_ID</code> environment variable is automatically appended to the user-agent string for request tracing.</li> </ul> <p><strong>Breaking changes:</strong></p> <ul> <li><strong><code>require('@actions/github')</code> no longer works in scripts.</strong> The upgrade to <code>@actions/github</code> v9 (ESM-only) means <code>require('@actions/github')</code> will fail at runtime. If you previously used patterns like <code>const { getOctokit } = require('@actions/github')</code> to create secondary clients, use the new injected <code>getOctokit</code> function instead — it's available directly in the script context with no imports needed.</li> <li><code>getOctokit</code> is now an injected function parameter. Scripts that declare <code>const getOctokit = ...</code> or <code>let getOctokit = ...</code> will get a <code>SyntaxError</code> because JavaScript does not allow <code>const</code>/<code>let</code> redeclaration of function parameters. Use the injected <code>getOctokit</code> directly, or use <code>var getOctokit = ...</code> if you need to redeclare it.</li> <li>If your script accesses other <code>@actions/github</code> internals beyond the standard <code>github</code>/<code>octokit</code> client, you may need to update those references for v9 compatibility.</li> </ul> <h2>What's Changed</h2> <ul> <li>Add ACTIONS_ORCHESTRATION_ID to user-agent string by <a href="https://github.com/Copilot"><code>@Copilot</code></a> in <a href="https://redirect.github.com/actions/github-script/pull/695">actions/github-script#695</a></li> <li>ci: use deployment: false for integration test environments by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/github-script/pull/712">actions/github-script#712</a></li> <li>feat!: add getOctokit to script context, upgrade <code>@actions/github</code> v9, <code>@octokit/core</code> v7, and related packages by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/github-script/pull/700">actions/github-script#700</a></li> </ul> <h2>New Contributors</h2> <ul> <li><a href="https://github.com/Copilot"><code>@Copilot</code></a> made their first contribution in <a href="https://redirect.github.com/actions/github-script/pull/695">actions/github-script#695</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/github-script/compare/v8.0.0...v9.0.0">https://github.com/actions/github-script/compare/v8.0.0...v9.0.0</a></p> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/actions/github-script/commit/3a2844b7e9c422d3c10d287c895573f7108da1b3"><code>3a2844b</code></a> Merge pull request <a href="https://redirect.github.com/actions/github-script/issues/700">#700</a> from actions/salmanmkc/expose-getoctokit + prepare re...</li> <li><a href="https://github.com/actions/github-script/commit/ca10bbdd1a7739de09e99a200c7a59f5d73a4079"><code>ca10bbd</code></a> fix: use <code>@octokit/core/</code>types import for v7 compatibility</li> <li><a href="https://github.com/actions/github-script/commit/86e48e20ac85c970ed1f96e718fd068173948b7b"><code>86e48e2</code></a> merge: incorporate main branch changes</li> <li><a href="https://github.com/actions/github-script/commit/c1084728b5b935ec4ddc1e4cee877b01797b3ff9"><code>c108472</code></a> chore: rebuild dist for v9 upgrade and getOctokit factory</li> <li><a href="https://github.com/actions/github-script/commit/afff112e4f8b57c718168af75b89ce00bc8d091d"><code>afff112</code></a> Merge pull request <a href="https://redirect.github.com/actions/github-script/issues/712">#712</a> from actions/salmanmkc/deployment-false + fix user-ag...</li> <li><a href="https://github.com/actions/github-script/commit/ff8117e5b78c415f814f39ad6998f424fee7b817"><code>ff8117e</code></a> ci: fix user-agent test to handle orchestration ID</li> <li><a href="https://github.com/actions/github-script/commit/81c6b7876079abe10ff715951c9fc7b3e1ab389d"><code>81c6b78</code></a> ci: use deployment: false to suppress deployment noise from integration tests</li> <li><a href="https://github.com/actions/github-script/commit/3953caf8858d318f37b6cc53a9f5708859b5a7b7"><code>3953caf</code></a> docs: update README examples from <a href="https://github.com/v8"><code>@v8</code></a> to <a href="https://github.com/v9"><code>@v9</code></a>, add getOctokit docs and v9 brea...</li> <li><a href="https://github.com/actions/github-script/commit/c17d55b90dcdb3d554d0027a6c180a7adc2daf78"><code>c17d55b</code></a> ci: add getOctokit integration test job</li> <li><a href="https://github.com/actions/github-script/commit/a047196d9a02fe92098771cafbb98c2f1814e408"><code>a047196</code></a> test: add getOctokit integration tests via callAsyncFunction</li> <li>Additional commits viewable in <a href="https://github.com/actions/github-script/compare/v8...v9">compare view</a></li> </ul> </details> <br /> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…oleMurray#2281) Closes ColeMurray#2034. > **Note:** This is an alternative implementation to ColeMurray#2208 for the same issue. It was designed independently from `main` and the issue text. ## Summary Operators can now choose **OpenCode** or **Claude Agent** for Linear-triggered sessions, as a global default and per repository override. When nothing is configured, sessions still run on OpenCode with the same behavior as before. Because the harness is fixed at create, follow-ups on an existing issue session are unchanged. ## Design **A configured harness is a preference, and the resolved model decides the final harness.** A new shared helper, `resolveHarnessForModel(configured, model)` in `shared/harnesses.ts`, returns the configured harness when it can run the model. Otherwise it returns the built-in harness (OpenCode), which runs every model. The Linear bot keeps its model resolution as it was (label → user preference → repo/global config → `DEFAULT_MODEL`), then picks the harness from the result. A future GitHub or Slack harness setting can reuse the same helper. - **Schema:** `linearBotSettingsSchema` gets `harness: harnessIdSchema.optional()`, so it is available at both the global and repo level. - **Save validation:** the store's model/effort check is renamed `validateAgentSelection`. It now also calls `checkHarnessCompatibility` when a harness and a model are saved at the same level, which matches how automations validate on save. Any integration that later adds a `harness` field gets this check automatically. - **Resolved endpoint:** `GET /integration-settings/linear/resolved/...` returns a concrete `harness` (via `getValidHarnessOrDefault`). The bot's schema treats a missing value as OpenCode, so an older control plane during a deploy doesn't break config reads. - **Bot:** `resolveSessionAgentSettings` (renamed from `resolveSessionModelSettings`) returns `{ harness, model, reasoningEffort }`. `createSession` always sends `harness`. - **Web settings:** - The global form and each repo row get a harness picker (`HarnessSelect`, a reusable component); repo rows can also pick "Inherit (X)". - Model pickers only list models the effective harness can run (`filterModelOptionsForHarness`). Switching to a harness that can't run the current model clears the model and effort. - Saves stay sparse: the default harness and "inherit" are omitted. - Repo rows warn when the effective harness and effective model clash across levels (for example, a global `openai/gpt-5.4` with a repo override of Claude Agent). Save-time validation can't catch that case, and those sessions run on OpenCode. - **Provider auth:** Linear sessions are unattended. On Claude Agent they follow the **Automated authentication** policy and may use the default Claude account. The UI copy and the docs say this. ## Open questions - **Claude Agent selected but the resolved model isn't Anthropic: fall back or fail?** It falls back to OpenCode. Labels, user preferences and the env default are resolved at runtime by people other than the operator, so a hard failure would turn `model:gpt-*` into a dead end. The rule is deterministic, tested for each model source, and named in the Linear activity. The web composer already adjusts the model to fit the harness rather than failing. Operators who want strict Claude Agent can set an Anthropic default model and turn off label and user-preference overrides; the docs explain this. - **Per-issue `harness:*` label or per-user override?** No, operator-level only for now. The harness decides which credential pays: with Claude Agent, unattended sessions can draw on a connected subscription. Automations treat that as an operator decision too, and Linear labels are editable by anyone in the workspace. - **Should "Creating coding session…" mention the harness?** Yes. Because of the fallback, the harness isn't always obvious. ## Visible change: Linear activity wording Linear activities now name the agent, **including for default (OpenCode) sessions**: - `Creating coding session on <target> (agent: OpenCode, model: …)...` - `Working on \`<target>\` with **<model>** (OpenCode).` The `agent_session.session_created` log now includes `configured_harness` and `harness`. ## Docs - `docs/CLAUDE_AGENT.md`: replaced "Bots and integrations create OpenCode sessions" and added a "Linear sessions" section. - `docs/integrations/LINEAR.md` and `packages/docs/.../integrations/linear.mdx`: the new setting, how the harness is resolved, and troubleshooting. - `agent-harnesses.mdx`, `choosing-a-model.mdx`, `provider-accounts.mdx`: removed statements that Linear always runs on OpenCode. ## Tests, lint, typecheck Only the tests covering the changed files were run: | Command | Result | | --- | --- | | `npm run build -w @open-inspect/shared` | ✅ | | shared `vitest run src/harnesses.test.ts src/module-boundaries.test.ts src/public-api.test.ts` | ✅ 30 passed | | control-plane `vitest run --config vitest.integration.config.ts test/integration/linear-integration-settings.test.ts test/integration/integration-settings.test.ts` | ✅ 67 passed | | control-plane `vitest run src/db/integration-settings.test.ts` | ✅ 134 passed | | linear-bot `vitest run` on `webhook-handler.harness.test.ts` (new), `__tests__/pure-functions.test.ts`, `utils/integration-config.test.ts`, `webhook-handler.test.ts` | ✅ all passed | | web `vitest run src/components/settings/integrations/linear-integration-settings.test.tsx` | ✅ 15 passed | | `npm test -w @open-inspect/docs` | ✅ 44 passed | | `npm run typecheck` for shared, control-plane, linear-bot, web | ✅ clean | | `npm run lint` for shared, control-plane, linear-bot, web | ✅ clean | | `npm run lint:complexity` | ✅ no hotspots in touched files | ## Trade-offs - If Claude Agent is selected, the model is left on "Use system default", and `DEFAULT_MODEL` isn't Anthropic, sessions run on OpenCode. The form can't warn about this because the web app doesn't know the bot's env default. The UI help text, the Linear activity and the docs explain it. - Repos whose prebuilt images predate the Claude harness's minimum image generation will miss the prebuilt image on their first Claude Agent sessions. This is expected. --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/aad95f46ac3dbe5bffb0bd6a4f54bf6b)* <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Choose an agent harness for Linear sessions, with repository-level overrides that can inherit global settings. * Linear sessions use the selected harness when it supports the resolved model; otherwise, they use OpenCode. OpenCode remains the default. * Session activity now shows which harness is being used. * **Improvements** * Settings filter model choices based on the selected harness and flag incompatible defaults that will fall back to OpenCode. * Documentation clarifies how harness selection, authentication, and changes to existing versus new sessions work. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com> Co-authored-by: waclaude <colemurray.cs+ghwaclaude@gmail.com>
) ## Summary Takes over ColeMurray#2155 by @ffesseler, which now conflicts with `main`. The goal is unchanged. External callers such as CLIs, CI jobs, and tools like Herdr can submit work through an inbound webhook, but they get nothing back to correlate with it. Checking whether it finished currently requires a signed-in web session. This PR returns an invocation ID from the webhook and adds a status endpoint that accepts the same webhook key. It also addresses the review on ColeMurray#2155 (ColeMurray#2155 (review)). ## What changes **API** - `POST /webhooks/automation/:id` now also returns `invocationId`. It is `null` only when nothing was recorded: conditions did not match, the automation is paused, or execution was denied before admission. - New endpoint `GET /webhooks/automation/:id/invocations/:invocationId`, authenticated with the webhook key. It returns `{ invocationId, status, runs: [{ id, status, sessionId }] }` and nothing else: no session content, payload, or other history. It returns 404 for other automations' invocations and for firings the webhook didn't cause (manual or scheduled). - Response types `WebhookTriggerResponse` and `WebhookInvocationStatusResponse` live in `@open-inspect/shared`. **Idempotent retries keep their ID (fixes a gap in ColeMurray#2155)** - A retry with the same `idempotencyKey` now returns the **original** invocation ID. In ColeMurray#2155 it returned `null` if the first firing had finished. If the first firing was still running, it recorded a new *skipped* invocation and returned that ID, so a polling client concluded its task had been dropped. - `startInvocation` now looks up the invocation that owns the event's `trigger_key` before the overlap check, and does the same after an insert collision. This applies to every event source, so a redelivered GitHub, Sentry, or Slack event while its run is still active now resolves to the original firing. Previously it recorded a spurious "concurrent run active" skip, and for Slack posted an "already active" notice. Skipped invocations still never hold a `trigger_key`, so a skip doesn't take the dedup slot of a firing that never ran. **Structure** - `SchedulerEventResult.invocationIds: string[]` is filled for every source with no source-specific checks. ColeMurray#2155 added `webhookInvocationId` and `event.source === "webhook"` checks to the shared event loop. The webhook handler now returns `invocationIds[0] ?? null`, since a webhook event targets exactly one automation. - `StartInvocationResult` and the new `firingInvocationId()` helper moved to `scheduler/invocation-outcome.ts`, so GitHub admission can use them without a runtime import cycle. `scheduler.ts` re-exports the types. - New canonical `AutomationStore.getInvocation(automationId, invocationId)` reuses the `listInvocations` mappers and reads the invocation and its runs in one `db.batch`, so the status and runs always come from the same snapshot. `listInvocations` and `getInvocation` share the select SQL. The webhook handler narrows the result to the public status shape. - `authenticateWebhook` returns `{ ok: true; automation } | { ok: false; response }` instead of `Response | AutomationStore`. ## Testing - Unit (control-plane): scheduler, GitHub admission, webhooks, route policy, and automation routes. 480 tests pass. - Integration (control-plane): scheduler events (webhook, Sentry, Slack, Slack team steering), GitHub routing, team grants, service auth, Slack and GitHub-PR webhooks, inbound webhooks, invocation store, route catalog conformance, and the admission matrix. 200 tests pass. - New coverage: a same-key retry returns the original ID, both after the original completed and while it is still running; an overlap skip returns its own ID; a filtered body returns `invocationId: null`; the status endpoint's auth/404 matrix, its manual-invocation exclusion, and its minimal fields; and `getInvocation` equals the listing entry for each history shape. - `typecheck` (all control-plane configs), ESLint, Prettier, `lint:sql-portability`, and the docs package tests all pass. - About 47 existing `toEqual({ triggered, skipped, steered })` assertions now include an exact `invocationIds` value (`[]` or one `expect.any(String)` per recorded invocation). ## Docs - `packages/docs/.../inbound-webhooks.mdx`: adds `invocationId` to the responses section, a new "Checking status" section, and a note on idempotent retries. - `docs/AUTOMATIONS.md` and the control-plane README. Supersedes ColeMurray#2155. Co-authored-by: Florian Fesseler <florian@mozza.io> --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/2eed8e3e5f06ed75a5ccb0ac6255ecfe)* <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Webhook trigger responses now include an invocation ID when a firing is recorded. Repeated requests with the same idempotency key return the original ID, including while the firing is in progress. * Added an API-key-authenticated endpoint to check an invocation’s status and its runs’ statuses and session IDs. It does not expose session content. * **Documentation** * Updated webhook guides with response details, retry behavior, and instructions for checking invocation status. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com> Co-authored-by: Florian Fesseler <florian@mozza.io> Co-authored-by: waclaude <colemurray.cs+ghwaclaude@gmail.com>
This is an automated nightly unsafe-cast remediation sweep. It replaces qualifying unsafe TypeScript assertions with parse-don't-assert validation at boundary/persistence seams, following the TypeScript Coding Standards for unsafe casts and the Zod boundary-validation pattern established in PR ColeMurray#807. | Finding | Risk | Cast removed | Fix | | --- | --- | --- | --- | | `packages/control-plane/src/node/job-store.ts:278` | Medium | SQLite aggregate row cast `as { run_at: number \| null }` used for job scheduling | Added Zod parsing via `parseNullableRunAtRow`, including the valid `null` aggregate case | | `packages/control-plane/src/node/job-store.ts:305` | Medium | SQLite recovery row casts `as Array<{ id: string }>` used to recover leased jobs | Added Zod parsing via `parseJobIdRows` before returning recovered ids | | `packages/control-plane/src/node/job-store.ts:308` | Medium | SQLite status-count row cast `as Array<{ status: string; count: number }>` used for queue health state | Added Zod parsing via `parseStatusCountRows` before computing stats | | `packages/control-plane/src/media.ts:220` | Low | Parsed multipart dimensions object cast `as { width?: unknown; height?: unknown }` | Removed assertion and read properties only through `in`-guarded access after the existing JSON/object checks | Verification: | Command | Result | | --- | --- | | `npm run format -- --write packages/control-plane/src/node/job-store.ts packages/control-plane/src/node/job-store.test.ts packages/control-plane/src/media.ts` | Passed | | `npm test -w @open-inspect/control-plane -- src/node/job-store.test.ts src/media.test.ts` | Passed, 2 files / 63 tests | | `npm run build -w @open-inspect/shared` | Passed | | `npm run build -w @open-inspect/control-plane` | Passed | | `npm test -w @open-inspect/control-plane` | Passed, 329 files / 5300 tests | | `npm run typecheck` | Passed | | `npm run lint` | Passed | | `npm run format` | Passed | No new dependencies were added. --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/7af4401825306a304add9ced4aed4937)* --------- Co-authored-by: waclaude <colemurray.cs+ghwaclaude@gmail.com> Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com>
…rray#2287) ## Summary Adds four changelog entries for features merged recently that were missing from `CHANGELOG.md`. Each entry is dated by its merge date in US Pacific time, which is the convention the existing entries follow. | Entry | Date | PRs | | --- | --- | --- | | Persistent memory | October 3 | ColeMurray#2214, ColeMurray#2261 | | Analytics redesign | October 2 | ColeMurray#2233, ColeMurray#2218 | | Saved prompt drafts | October 2 | ColeMurray#2237 | | Session page redesign | October 1 | ColeMurray#2168, ColeMurray#2169 (Sep 30), ColeMurray#2199 (Oct 1) | The session redesign entry is dated October 1, when ColeMurray#2199 completed the tab layout. The artifact arrow-key navigation from ColeMurray#2234 is left out to keep the entry short. ## Verification - `npx prettier --check CHANGELOG.md` passes. --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/2b8fc4c8c153f4e0cd78f4a52a5317ee)* <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added persistent memory for personal facts in a private session; other agent memory writes require approval. * Redesigned analytics with Usage, Cost, Pull requests, and People tabs, selections preserved in the URL, and a Session origins breakdown. * Added prompt drafts that persist across reloads, with separate drafts for each session and the new-session composer. * Redesigned the session page with Info, Changes, Tasks, and Tools tabs. Captured media appears under Info’s Artifacts, and changed files open beside the sidebar. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com> Co-authored-by: waclaude <colemurray.cs+ghwaclaude@gmail.com>
…2268) Takes over ColeMurray#2021 from @rhlsthrm, rebuilt on current `main` with the fix that came out of review there. The original author is credited as co-author. ## Problem `SandboxShutdownBanner` showed a recovery failure message after the shutdown phase it described had already been replaced. `recoveryError` was cleared only when a new recovery request started. The page doesn't key the banner, so the message stayed through phase changes. For example, after a `failed`-phase retry timed out, a later `unknown` or `restoring` → `failed` update still showed "Recovery was not confirmed…". ## Fix The rule is that recovery state belongs to one phase. Instead of enforcing that with a phase-tagged error, a reset effect, and an in-flight generation counter (the ColeMurray#2021 approach), the stateful body is now a `ShutdownPhaseBanner` keyed by `shutdown.phase`. A new phase mounts a fresh instance, which covers every case: - **No stale frame:** the new phase commits with empty state in the same render. - **No resurfacing:** a phase that comes back also gets a fresh instance. - **In-flight results:** a request that settles after the phase changed updates the unmounted instance, which has no effect. **Behavior change:** if the phase changes while a request is pending, the buttons for the new phase start enabled. They no longer show the old phase's "Retrying save…". A second click is still refused by `recoverShutdown`'s own in-flight guard ("A recovery request is awaiting confirmation"). ## Tests - Keeps the four tests from ColeMurray#2021, including the `useLayoutEffect` commit probe that checks the stale message is never committed next to the newer phase. - Adds "ignores a result that settles after the phase left and returned" (failed → restoring → failed with a request pending). This is the case the ColeMurray#2021 suite didn't cover. - Asserts the pending indicator resets on a phase change. Verification: - `npx vitest run src/components/sandbox-shutdown-banner.test.tsx` (packages/web): 28 passed. Against `main`'s component, all 5 new tests fail. - `npm run typecheck -w @open-inspect/web`: passes. Supersedes ColeMurray#2021. --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/20c0e8e42bc2814f18744d5bf873e4bf)* <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Recovery messages now clear when the shutdown phase changes, preventing stale or delayed results from appearing in the wrong phase. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com> Co-authored-by: Rahul Sethuram <rahul.eth@icloud.com>
## Summary Apply only the Fumadocs portion of ColeMurray#2285 as a standalone change against main: - Upgrade `fumadocs-core` and `fumadocs-ui` from `16.15.6` to `16.15.14`, and `fumadocs-mdx` from `15.4.0` to `15.4.5`. - Keep one hoisted `fumadocs-core@16.15.14` shared by the docs loader, MDX, and UI. - Update only required Fumadocs transitives (`cn`, Fumadocs Tailwind, and the Yuku analyzer packages). Install MDX's required `zod@4.6.5` beneath MDX, preserving the existing root Zod version for other workspaces. No Better Auth changes, other grouped dependency updates, casts, source/schema changes, or unrelated formatting. PR ColeMurray#2285 was not modified or pushed. ## Root Cause PR ColeMurray#2285 retains root `node_modules/fumadocs-core@16.15.6` while installing `16.15.14` under `packages/docs/node_modules`. Its hoisted MDX declarations resolve the old core, while `packages/docs/src/lib/source.ts` resolves the new core. The required `LoaderOutput.getPageByUrl()` added in core 16.15.9 makes those loader types structurally incompatible. Source inference consequently widens to `PageData`, losing access to MDX fields such as `body`, `toc`, and `getText`, and schema fields such as `lastReviewed`. The public schema is already correctly supplied in `source.ts`; aligning the dependency graph fixes inference rather than masking it with casts. ## Verification All validation ran sequentially in the isolated sandbox: - `npm ci --ignore-scripts --no-audit --no-fund` succeeds with the final lockfile. - `npm ls fumadocs-core fumadocs-mdx fumadocs-ui --workspace @open-inspect/docs` shows MDX and UI deduped to core `16.15.14`. - Module-resolution assertions confirm the docs loader, MDX, and UI resolve the identical core source entry. - `npm run typecheck -w @open-inspect/docs` passes. - `npm test -w @open-inspect/docs -- src/lib/content-inventory.test.ts src/lib/content-policy.test.ts --maxWorkers=1` passes: 23 tests across 2 files, including compilation of the MDX corpus. - `NODE_ENV=production npm run build -w @open-inspect/docs` passes, generating all 185 static pages. - Prettier checks for both changed JSON files and `git diff --check` pass. Lockfile-entry comparison confirms all changes are confined to Fumadocs and required transitives. The initial build without an explicit `NODE_ENV` failed during prerendering with the sandbox's nonstandard environment value. Rerunning with `NODE_ENV=production` succeeds; no application changes were needed. --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/ff735b38dd74a103ddd8f948de214180)* <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Updated the documentation site’s supporting packages to newer versions. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com>
## Summary - Isolate the Prettier portion of ColeMurray#2285 into a standalone PR against main. - Upgrade only the root Prettier range from `^3.4.2` to `^3.9.9` and the locked version from `3.8.4` to `3.9.9`. - Apply configured Prettier 3.9.9 formatting to the same 72 existing files flagged by the grouped dependency update: 71 TypeScript/TSX source and test files plus one Markdown document containing TypeScript. - Leave Better Auth, Fumadocs, all other dependencies, formatter configuration, and the original PR untouched. No application behavior changes. ## Root Cause The grouped update changes the installed formatter version. Existing formatting produced under Prettier 3.8.4 differs from 3.9.9 output, notably for TypeScript unions, typed arrow callbacks, and mapped types. The cited Lint & Format job fails at `npm run format:check`, reporting 72 files: https://github.com/ColeMurray/background-agents/actions/runs/37238749141/job/111542997788 Verified PR head `3e2f1e0`, its base `b5bb5df`, and current main `31f2afa` before branching. Current main still locks 3.8.4. Installing only 3.9.9 on current main reproduced the 72-file failure; this PR contains the formatter upgrade and its required output, not the grouped dependency graph. ## Verification Checks ran sequentially with a 2 GiB Node heap cap and explicit time limits. - `npm ci --ignore-scripts --no-audit --no-fund`: passed using the narrowly updated lockfile. - `npm run format:check`: passed on a clean export of the committed repository, using the installed locked formatter. - `npm run format:check -- '!.opencode/tool/_memory-contract.js'`: passed in the working sandbox. The exclusion is solely for a sandbox-generated, Git-excluded tool file absent from a clean checkout; no repository ignore rules or generated tool files were changed. - `npm run lint`: passed for the full repository. - `git diff --check`: passed. - Programmatically confirmed all 72 reformatted files exactly equal configured Prettier 3.9.9 output applied to their original contents, and all 71 TypeScript/TSX ASTs are unchanged. - Deep-compared manifest and lockfile JSON: only the root Prettier range and the `node_modules/prettier` entry changed; the latter matches PR ColeMurray#2285 metadata exactly. No unrelated lockfile churn. - Commit hooks passed ESLint and Prettier. No behavioral test suite was run because the source changes are formatter-only and syntax trees are unchanged. --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/ac1bc1bbb1f6db4abea4f0b8ccb63761)* <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Updated the code formatter version. * Applied formatting-only changes throughout the project. No functionality or behavior changed. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com>
…leMurray#2284) ## Summary Fixes ColeMurray#1363. When a sandbox misses the 4-minute connect watchdog, the session gets stuck. The sandbox is failed and fenced, but the prompt it was booting for stays `pending` and nothing re-drives it. Recovery waits for "your next message", and a bot-triggered session (e.g. a github-bot review) never sends one. The prompt never runs and nothing is reported back to the PR. This is the part of ColeMurray#1600 (thanks @atirna) that is still open on `main`. The rest of ColeMurray#1600 has already landed separately: - connecting timeouts count toward the breaker (ColeMurray#1906) - the breaker resets on prompt dispatch (ColeMurray#1906) - Modal stop-by-id via `api_stop_sandbox` (ColeMurray#2146 and the lifecycle refactor) ## Change `failConnectTimeout` now tells the alarm handler what to do with the queue, based on what happened to the generation: | Generation after the timeout | Result | Queue | | --- | --- | --- | | Fenced and stopped, breaker still closed | `sandbox_terminated` | Re-driven onto a replacement right away, the same path the heartbeat watchdog already uses | | Fenced and stopped, breaker now open | `{ kind: "connect_retries_exhausted", reason }` | The boot prompt fails with `reason`, like `boot_budget_exceeded`. Bots get a final failure instead of silence. | | Held retained boot | `sandbox_failed` (unchanged) | Shutdown owns recovery | | Provider cannot stop (late bridge may self-heal) | `sandbox_failed` (unchanged) | Waits, as before | | Replaced by another launch during the stop | `sandbox_failed` | The replacement owns the queue | The existing breaker (3 failures in 5 minutes, measured from attempt start) limits how many times the re-drive repeats. The breaker is checked right after this failure is recorded, before the stop await. Other changes: - `alarm/handler.ts` fails the boot prompt for any object result, not just `boot_budget_exceeded`. - `SandboxAlarmResult` now documents what each variant means for the queue. - `toCircuitBreakerState` replaces three copies of the row→state mapping in `manager.ts`. - The user-facing error for the re-drive case changes from "It will be retried on your next message." to "Queued prompts will be retried on a fresh sandbox." ## Not covered If the watchdog fires while `createSandbox()` is still in flight, the re-drive's `spawnSandbox` is skipped (a spawn is already running). The late provider result is then destroyed as `startup_superseded` without pumping the queue. This was already the case before this PR, and it needs a provider call that takes longer than 4 minutes. ## Tests - Unit (`alarm-connecting-watchdog-effects.test.ts`): a stoppable timeout returns `sandbox_terminated`. The timeout that opens the breaker returns `connect_retries_exhausted` and still fences and stops. A held retained boot is left alone and returns `sandbox_failed`. Existing expectations updated for the new result and message. - Integration (`session-lifecycle-alarm-recovery.test.ts`, workerd + D1), both failing on `main`: - a pending github-sourced prompt behind a timed-out sandbox gets a replacement spawn and stays `pending` - with the breaker one failure short, the timeout fails that prompt with the "after repeated attempts" reason - the park helper now also keeps the warm spawn's `sandbox_preservation` generation in sync with the rewritten row. Otherwise admission reads it as a held foreign source, which never happens for a real boot. - `control-plane` unit: `src/sandbox` + `src/session`, 77 files / 1703 tests pass. - `control-plane` integration: `session-lifecycle-alarm-recovery`, `sandbox-shutdown`, `websocket-sandbox`, 49 tests pass. - `tsc --noEmit`, eslint, and prettier are clean. --- *Created with [Open-Inspect](https://open-inspect-prod.vercel.app/session/3c8c472777b15e6033a84357cd6aa188)* <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Pending prompts are retried on a replacement sandbox after a connection timeout when the previous sandbox is confirmed stopped. * Prompts now fail with a connection-timeout error when retries are exhausted or the provider cannot confirm the sandbox has stopped. * Queued work resumes after a sandbox launch completes, avoiding missed processing when a timeout occurs during startup. * **Documentation** * Updated the debugging playbook to describe connection-timeout recovery and failure conditions. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Cole Murray <2492022+ColeMurray@users.noreply.github.com> Co-authored-by: waclaude <colemurray.cs+ghwaclaude@gmail.com>
# Conflicts: # .github/dependabot.yml # .github/workflows/terraform.yml # docs/SECRETS.md # docs/integrations/GITHUB.md # package-lock.json # package.json # packages/control-plane/package.json # packages/control-plane/src/db/automation-store.test.ts # packages/control-plane/src/router.policy.test.ts # packages/control-plane/src/routes/automation-crud.ts # packages/control-plane/src/routes/catalog.ts # packages/control-plane/src/routing/request-lifecycle.test.ts # packages/control-plane/src/routing/route-admission.ts # packages/control-plane/src/scheduler/scheduler.test.ts # packages/control-plane/src/session/callback-notification-service.test.ts # packages/control-plane/src/session/create-session-input.test.ts # packages/control-plane/src/session/initialize.test.ts # packages/control-plane/src/session/initialize.ts # packages/control-plane/test/integration/__snapshots__/hono-route-catalog-conformance.test.ts.snap # packages/control-plane/test/integration/__snapshots__/route-admission-matrix.test.ts.snap # packages/control-plane/test/integration/automation-invocations.test.ts # packages/control-plane/test/integration/automations-slack-route.test.ts # packages/control-plane/test/integration/cleanup.ts # packages/control-plane/test/integration/hono-route-catalog-conformance.test.ts # packages/control-plane/test/integration/scheduler.test.ts # packages/control-plane/test/integration/slack-channel-store.test.ts # packages/docs/package.json # packages/github-bot/src/github-auth.ts # packages/github-bot/src/handlers.ts # packages/github-bot/src/payload-schemas.ts # packages/github-bot/test/github-auth.test.ts # packages/github-bot/test/handlers.test.ts # packages/github-bot/test/payload-schemas.test.ts # packages/github-bot/test/webhook.test.ts # packages/modal-infra/uv.lock # packages/shared/package.json # packages/shared/src/types/audit-events.test.ts # packages/shared/src/types/automations.test.ts # packages/web/package.json # packages/web/src/components/automations/automation-form-policy.test.ts # packages/web/src/components/settings/integrations/github-global-settings-section.tsx # packages/web/src/components/settings/integrations/github-repo-overrides-section.tsx # packages/web/src/components/settings/settings-registry.test.ts # packages/web/src/hooks/use-automations.test.tsx
|
Important Review skippedToo many files! This PR contains 1000 files, which is 700 over the limit of 300. To get a review, reduce the PR to 300 files or fewer by splitting it into smaller PRs or changing its base branch. Usage-priced reviews support at most 300 files. ⚙️ Run configuration
⛔ Files ignored due to path filters (5)
📒 Files selected for processing (1000)
You can disable this status message by setting the
Comment |
There was a problem hiding this comment.
Blocking: 4 · Non-blocking: 0
The upstream sync introduces two cross-boundary disclosures (personal-memory metadata to other session readers and raw tool content to runtime logs), permits a team lead to bind an unrelated private Slack channel by ID, and makes team-owned memory previews disagree with the session they preview. Please address the concrete cases in the inline comments. Targeted verification: 445 GitHub-bot tests and 11 memory-service tests passed; these cases are not covered by those suites.
Disposition of the four blocking findingsAll four findings are in files this PR carries byte-identical from upstream
Each thread below gets a reply pointing here and is then resolved. |
Terraform Validation ResultsValidation job: abandoned
Pushed by: @rhlsthrm, Action: |
Terraform Validation ResultsValidation job: success
Pushed by: @rhlsthrm, Action: |
Terraform Plan ResultsStatus: Success Show Planterraform_data.sign_in_provider_gate: Refreshing state... [id=b29a3d55-0be5-fb92-10a9-027df10c4b75]
data.external.modal_source_hash[0]: Reading...
terraform_data.access_control_gate: Refreshing state... [id=2b965617-b42b-4b42-5ea5-a1c3c05f10be]
terraform_data.cloudflare_custom_domain_gate: Refreshing state... [id=09b89c9b-996a-d28b-1f9c-08760a492dac]
module.modal_app[0].null_resource.modal_secrets[0]: Refreshing state... [id=4371755998621267773]
null_resource.web_app_cloudflare_build[0]: Refreshing state... [id=6918680661690155785]
null_resource.slack_bot_build[0]: Refreshing state... [id=9088235487192680958]
random_password.service_auth_secret_github_bot: Refreshing state... [id=none]
random_password.service_auth_secret_linear_bot: Refreshing state... [id=none]
random_bytes.provider_accounts_encryption_key: Refreshing state...
random_password.service_auth_secret_slack_bot: Refreshing state... [id=none]
null_resource.github_bot_build[0]: Refreshing state... [id=1073509195618209737]
null_resource.control_plane_build: Refreshing state... [id=2292824058761615604]
random_password.service_auth_secret_web: Refreshing state... [id=none]
random_password.image_callback_token_pepper: Refreshing state... [id=none]
null_resource.linear_bot_build[0]: Refreshing state... [id=7007435095112115998]
module.github_kv[0].cloudflare_workers_kv_namespace.this: Refreshing state... [id=e0848d433a4f466cafd4ed5d140aad7d]
module.session_index_kv.cloudflare_workers_kv_namespace.this: Refreshing state... [id=ea0a253d5cb64d75a841acb88040cd2f]
module.linear_kv[0].cloudflare_workers_kv_namespace.this: Refreshing state... [id=777f94c3595f4de680c256a4e5fc6653]
cloudflare_d1_database.main: Refreshing state... [id=f747a908-5c69-45a1-86ab-ceb5250cf5e0]
cloudflare_r2_bucket.media: Refreshing state... [id=open-inspect-media-codos]
cloudflare_queue.github_autofix_dlq[0]: Refreshing state... [id=3a27213aeba149d4b7cbf2d3551842f8]
cloudflare_queue.github_autofix[0]: Refreshing state... [id=033a23f13783415385b2f8799416c20f]
cloudflare_queue.slack_completion_delivery[0]: Refreshing state... [id=247b1100bac2408684d6a75c1bca0d28]
cloudflare_queue.image_build_finalization: Refreshing state... [id=a0647323f7424e778b9d59da50dc55cf]
data.external.modal_source_hash[0]: Read complete after 0s [id=-]
local_file.web_app_wrangler_production[0]: Refreshing state... [id=d58ccd8dd2962c70f7cff2ffac9821e9e711af31]
cloudflare_queue.slack_completion_delivery_dlq[0]: Refreshing state... [id=396865e4939b4160937b2dc9ad5dabe1]
module.slack_kv[0].cloudflare_workers_kv_namespace.this: Refreshing state... [id=ab5c371c8bc04a938ff2f71809933aa0]
cloudflare_queue.image_build_finalization_dlq: Refreshing state... [id=61535c686d8546099cfddcf39833572b]
module.modal_app[0].null_resource.modal_deploy: Refreshing state... [id=5162646839236869035]
module.linear_bot_worker[0].cloudflare_worker.this: Refreshing state... [id=049cd48117bc48b9b4332683a97d0a0e]
module.slack_bot_worker[0].cloudflare_worker.this: Refreshing state... [id=375c2c6875904657bce05c62c8048c76]
module.linear_bot_worker[0].cloudflare_worker_version.this: Refreshing state... [id=9098db83-cca6-40ee-a770-bef402cefeba]
null_resource.d1_migrations: Refreshing state... [id=263751651589333239]
module.slack_bot_worker[0].cloudflare_worker_version.this: Refreshing state... [id=151dcc3c-23b9-4cda-bf47-f75da09ee657]
module.linear_bot_worker[0].cloudflare_workers_deployment.this: Refreshing state... [id=949431e0-4b86-4a3f-aae3-291f40f045dd]
module.slack_bot_worker[0].cloudflare_workers_deployment.this: Refreshing state... [id=7a44c68a-0e0c-4953-a266-695313371f72]
module.control_plane_worker.cloudflare_worker.this: Refreshing state... [id=3457352971a74b89be5ed3700db48a8e]
cloudflare_queue_consumer.slack_completion_delivery[0]: Refreshing state...
module.control_plane_worker.cloudflare_worker_version.this: Refreshing state... [id=5bc2ef23-0e55-46e1-84b8-1ef9452db4f5]
module.control_plane_worker.cloudflare_workers_deployment.this: Refreshing state... [id=1fa8f9f0-fd72-46cf-8dcc-bcd786913f23]
module.control_plane_worker.cloudflare_workers_cron_trigger.this[0]: Refreshing state... [id=open-inspect-control-plane-codos]
null_resource.web_app_cloudflare_deploy[0]: Refreshing state... [id=6654088337346185031]
cloudflare_queue_consumer.image_build_finalization: Refreshing state...
module.github_bot_worker[0].cloudflare_worker.this: Refreshing state... [id=fa832fd890a14336bc3c63305e9bc36f]
null_resource.web_app_cloudflare_secrets[0]: Refreshing state... [id=8981633407656564074]
module.github_bot_worker[0].cloudflare_worker_version.this: Refreshing state... [id=4146f4b0-e799-4a13-8582-abfd42a7ac9e]
module.github_bot_worker[0].cloudflare_workers_deployment.this: Refreshing state... [id=44b501f8-0300-4b57-b392-4019cb8609e5]
cloudflare_queue_consumer.github_autofix[0]: Refreshing state...
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:
# local_file.web_app_wrangler_production[0] will be created
+ resource "local_file" "web_app_wrangler_production" {
+ content = <<-EOT
name = "open-inspect-web-codos"
main = ".open-next/worker.js"
compatibility_date = "2025-08-15"
compatibility_flags = ["nodejs_compat", "global_fetch_strictly_public"]
# Keep-names makes esbuild emit __name() calls, which leak into next-themes'
# inline script and throw in the browser.
keep_names = false
# A custom-domain deployment has one canonical browser origin.
workers_dev = true
[vars]
CONTROL_PLANE_URL = "https://open-inspect-control-plane-codos.opencodos.workers.dev"
NEXT_PUBLIC_WS_URL = "wss://open-inspect-control-plane-codos.opencodos.workers.dev"
NEXT_PUBLIC_SANDBOX_PROVIDER = "modal"
NEXT_PUBLIC_APP_NAME = "Open-Inspect"
NEXT_PUBLIC_APP_ICON_URL = ""
[assets]
directory = ".open-next/assets"
binding = "ASSETS"
[[services]]
binding = "CONTROL_PLANE_WORKER"
service = "open-inspect-control-plane-codos"
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 = "2292824058761615604" -> (known after apply)
~ triggers = { # forces replacement
~ "always_run" = "2026-10-05T14:25:23Z" -> (known after apply)
}
}
# null_resource.d1_migrations must be replaced
-/+ resource "null_resource" "d1_migrations" {
~ id = "263751651589333239" -> (known after apply)
~ triggers = { # forces replacement
~ "migrations_sha" = "892f8c9e8c97406e40bb30cde46ddb27e20071538596a544af88d48b300728b2" -> "fe214c9c4deb8e856064cb75c157291d2778c3033e71063a6c2a249004d785ed"
# (1 unchanged element hidden)
}
}
# null_resource.github_bot_build[0] must be replaced
-/+ resource "null_resource" "github_bot_build" {
~ id = "1073509195618209737" -> (known after apply)
~ triggers = { # forces replacement
~ "always_run" = "2026-10-05T14:25:23Z" -> (known after apply)
}
}
# null_resource.linear_bot_build[0] must be replaced
-/+ resource "null_resource" "linear_bot_build" {
~ id = "7007435095112115998" -> (known after apply)
~ triggers = { # forces replacement
~ "always_run" = "2026-10-05T14:25:23Z" -> (known after apply)
}
}
# null_resource.slack_bot_build[0] must be replaced
-/+ resource "null_resource" "slack_bot_build" {
~ id = "9088235487192680958" -> (known after apply)
~ triggers = { # forces replacement
~ "always_run" = "2026-10-05T14:25:23Z" -> (known after apply)
}
}
# null_resource.web_app_cloudflare_build[0] must be replaced
-/+ resource "null_resource" "web_app_cloudflare_build" {
~ id = "6918680661690155785" -> (known after apply)
~ triggers = { # forces replacement
~ "always_run" = "2026-10-05T14:25:23Z" -> (known after apply)
}
}
# null_resource.web_app_cloudflare_deploy[0] must be replaced
-/+ resource "null_resource" "web_app_cloudflare_deploy" {
~ id = "6654088337346185031" -> (known after apply)
~ triggers = { # forces replacement
~ "always_run" = "2026-10-05T14:26:14Z" -> (known after apply)
}
}
# module.control_plane_worker.cloudflare_worker.this will be updated in-place
~ resource "cloudflare_worker" "this" {
id = "3457352971a74b89be5ed3700db48a8e"
name = "open-inspect-control-plane-codos"
~ 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 = "34735ba6d2804d67a82cf0bdf5a3175f" -> null
- namespace_name = "open-inspect-control-plane-codos_SessionDO" -> null
- worker_id = "3457352971a74b89be5ed3700db48a8e" -> null
- worker_name = "open-inspect-control-plane-codos" -> null
},
] -> (known after apply)
~ queues = [
- {
- queue_consumer_id = "4e24da4810c84b3e9e80ea014860d145" -> null
- queue_id = "033a23f13783415385b2f8799416c20f" -> null
- queue_name = "open-inspect-github-autofix-codos" -> null
},
- {
- queue_consumer_id = "f37fe99c4658470aa36767dfb68c3d6f" -> null
- queue_id = "a0647323f7424e778b9d59da50dc55cf" -> null
- queue_name = "open-inspect-image-build-finalization-codos" -> null
},
] -> (known after apply)
~ workers = [
- {
- id = "049cd48117bc48b9b4332683a97d0a0e" -> null
- name = "open-inspect-linear-bot-codos" -> null
},
- {
- id = "7aa4fa7a556a48708d1ebd7bbba3263a" -> null
- name = "open-inspect-web-codos" -> null
},
- {
- id = "fa832fd890a14336bc3c63305e9bc36f" -> null
- name = "open-inspect-github-bot-codos" -> null
},
- {
- id = "375c2c6875904657bce05c62c8048c76" -> null
- name = "open-inspect-slack-bot-codos" -> null
},
] -> (known after apply)
} -> (known after apply)
tags = []
~ updated_on = "2026-10-05T14:25:30Z" -> (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-10-05T14:25:35Z" -> (known after apply)
~ id = "5bc2ef23-0e55-46e1-84b8-1ef9452db4f5" -> (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 = "3e33de33bcda44f9bd57d1c64347b2ac7d86f588c4a15276569f984a33c4cd91" -> null
- content_type = "application/javascript+module" -> null
- name = "index.js" -> null
},
+ { # forces replacement
+ content_file = "../../..//packages/control-plane/dist/index.js"
+ content_sha256 = "efdde37a149a677fd13f4fb28b2ab0496e50d92176ac6985a0fcb3ec5922a2ee"
+ content_type = "application/javascript+module"
+ name = "index.js"
},
]
~ number = 83 -> (known after apply)
~ source = "terraform" -> (known after apply)
~ startup_time_ms = 116 -> (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 = (known after apply)
~ created_on = "2026-10-05T14:25:40Z" -> (known after apply)
~ id = "1fa8f9f0-fd72-46cf-8dcc-bcd786913f23" -> (known after apply)
~ source = "terraform" -> (known after apply)
~ versions = [ # forces replacement
~ {
~ version_id = "5bc2ef23-0e55-46e1-84b8-1ef9452db4f5" -> (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 = "fa832fd890a14336bc3c63305e9bc36f"
name = "open-inspect-github-bot-codos"
~ 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 = "3457352971a74b89be5ed3700db48a8e" -> null
- name = "open-inspect-control-plane-codos" -> null
},
] -> (known after apply)
} -> (known after apply)
tags = []
~ updated_on = "2026-10-05T14:25: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-10-05T14:25:41Z" -> (known after apply)
~ id = "4146f4b0-e799-4a13-8582-abfd42a7ac9e" -> (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 = "23f9979b7b93a642f841ec5954878c060e916f0e5ea6346f182c0c0daf8b8efd" -> null
- content_type = "application/javascript+module" -> null
- name = "index.js" -> null
},
+ { # forces replacement
+ content_file = "../../..//packages/github-bot/dist/index.js"
+ content_sha256 = "956b5f1938147e55d3b480ac6201581881700e140ca79573722ff6d919bfb108"
+ content_type = "application/javascript+module"
+ name = "index.js"
},
]
~ number = 81 -> (known after apply)
~ source = "terraform" -> (known after apply)
~ startup_time_ms = 101 -> (known after apply)
~ urls = [
- "https://4146f4b0-open-inspect-github-bot-codos.opencodos.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 = (known after apply)
~ created_on = "2026-10-05T14:25:45Z" -> (known after apply)
~ id = "44b501f8-0300-4b57-b392-4019cb8609e5" -> (known after apply)
~ source = "terraform" -> (known after apply)
~ versions = [ # forces replacement
~ {
~ version_id = "4146f4b0-e799-4a13-8582-abfd42a7ac9e" -> (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 = "049cd48117bc48b9b4332683a97d0a0e"
name = "open-inspect-linear-bot-codos"
~ 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 = "3457352971a74b89be5ed3700db48a8e" -> null
- name = "open-inspect-control-plane-codos" -> null
},
] -> (known after apply)
} -> (known after apply)
tags = []
~ updated_on = "2026-10-05T14:25:24Z" -> (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-10-05T14:25:26Z" -> (known after apply)
~ id = "9098db83-cca6-40ee-a770-bef402cefeba" -> (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 = "7b42cf70800722f964d7272de95ecc31f5307b71f4a0a0b2d9d8f68aecc38417" -> null
- content_type = "application/javascript+module" -> null
- name = "index.js" -> null
},
+ { # forces replacement
+ content_file = "../../..//packages/linear-bot/dist/index.js"
+ content_sha256 = "1df7f712aece63700acf46161233a979cd1b69bd0555668623d62e294355f679"
+ content_type = "application/javascript+module"
+ name = "index.js"
},
]
~ number = 87 -> (known after apply)
~ source = "terraform" -> (known after apply)
~ startup_time_ms = 58 -> (known after apply)
~ urls = [
- "https://9098db83-open-inspect-linear-bot-codos.opencodos.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 = (known after apply)
~ created_on = "2026-10-05T14:25:28Z" -> (known after apply)
~ id = "949431e0-4b86-4a3f-aae3-291f40f045dd" -> (known after apply)
~ source = "terraform" -> (known after apply)
~ versions = [ # forces replacement
~ {
~ version_id = "9098db83-cca6-40ee-a770-bef402cefeba" -> (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 = "5162646839236869035" -> (known after apply)
~ triggers = { # forces replacement
~ "source_hash" = "c3018aefde76d57f99f467233c071f0aa960ce34f7e98214578fc62296b28d80" -> "d91c42dd4469285f05295b7648cfaac2b1d9bd87bb38b03a8edff9b11c585ae7"
# (4 unchanged elements hidden)
}
}
# module.slack_bot_worker[0].cloudflare_worker.this will be updated in-place
~ resource "cloudflare_worker" "this" {
id = "375c2c6875904657bce05c62c8048c76"
name = "open-inspect-slack-bot-codos"
~ 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 = "767c5dfe751c4852a536d98b836cdd94" -> null
- queue_id = "247b1100bac2408684d6a75c1bca0d28" -> null
- queue_name = "open-inspect-slack-completion-codos" -> null
},
] -> (known after apply)
~ workers = [
- {
- id = "3457352971a74b89be5ed3700db48a8e" -> null
- name = "open-inspect-control-plane-codos" -> null
},
] -> (known after apply)
} -> (known after apply)
tags = []
~ updated_on = "2026-10-05T14:25:24Z" -> (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-10-05T14:25:25Z" -> (known after apply)
~ id = "151dcc3c-23b9-4cda-bf47-f75da09ee657" -> (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 = "2d2f7f252c87f43ef3a09a72f1ab6c24f34c3bc26a45277bd78d5a5a0354bfca" -> null
- content_type = "application/javascript+module" -> null
- name = "index.js" -> null
},
+ { # forces replacement
+ content_file = "../../..//packages/slack-bot/dist/index.js"
+ content_sha256 = "c53be53064f2d8c1290a0585e487c6aea15cb2f69e7bc3bb3d918cf462b5e65b"
+ content_type = "application/javascript+module"
+ name = "index.js"
},
]
~ number = 85 -> (known after apply)
~ source = "terraform" -> (known after apply)
~ startup_time_ms = 55 -> (known after apply)
~ urls = [
- "https://151dcc3c-open-inspect-slack-bot-codos.opencodos.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 = (known after apply)
~ created_on = "2026-10-05T14:25:26Z" -> (known after apply)
~ id = "7a44c68a-0e0c-4953-a266-695313371f72" -> (known after apply)
~ source = "terraform" -> (known after apply)
~ versions = [ # forces replacement
~ {
~ version_id = "151dcc3c-23b9-4cda-bf47-f75da09ee657" -> (known after apply)
# (1 unchanged attribute hidden)
},
]
# (3 unchanged attributes hidden)
}
Plan: 17 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: @rhlsthrm |
Syncs upstream
ColeMurray/background-agentsb8c9a80b..e364a7ee(64 commits) into the fork.Conflict resolutions that change behavior
startSession): the fork's review layers are re-homed onto upstream's structure rather than resurrecting the deleted inline helpers. New fork-onlysrc/review-start.tsholds the feat(github-bot): supersede stale PR reviews and close out their statuses ColeMurray/background-agents#1370 supersession flow (freshness snapshot as the last await before the generation claim, fence on session create, uncoded 409 = superseded, any other failure releases the claim, sweep then start-marker lease + pending status, prompt rejection closes out the status), approval stand-down for automatic reviews, and reviewer-App prompt identity (feat(github-bot): support a separate reviewer GitHub App ColeMurray/background-agents#1862).session-startup.tskeeps the own-App-identity caller-gate bypass: upstream's "explicitly allowed App bot" mechanism is not a superset — in permission-check mode (allowlist null, production) the collaborator lookup 404s for a[bot]login, so App-opened PRs would never be reviewed. Coded 409 refusals (target_team_missing_grant,team_archived) roll back the claim and then follow upstream's refusal path.callback-notification-service.ts: upstream's new Slack post gate applied to every non-Linear completion, so GitHub review completions (fork feat(github-bot): supersede stale PR reviews and close out their statuses ColeMurray/background-agents#1370) were silently dropped asinvalid_callback_context. GitHub completions are now exempt, like Linear's.routing/team-admission.ts(upstream movedenforceTeamRequirementout ofroute-admission.ts): carries the fork's access-token principal (feat: personal access tokens and an MCP server ColeMurray/background-agents#1824), whose writes the method gate already refuses.POST /automations: upstream'srequireAll(automations.create, sessions.create)plus the fork'saccessTokenWrites: "allow".session/initialize.ts: the fork's fence-then-row ordering keeps upstream's newmemory/managedSkillsfields.Fixtures and dependencies
232 + (220 − 207) = 245routes,176 + (167 − 156) = 187unique paths; snapshots regenerated, the 13 added admission rows are exactly the fork's routes.@vitest/coverage-v8returns to^4.1.11(it peers on the exact vitest version and vitest is held below 5);@vitest/coverage-istanbulfrom upstream added at the same version; dependabot now holds@vitest/coverage-*with vitest. Prettier follows upstream to 3.9.9.package-lock.jsonis rebuilt from the fork's lock, with npm's dropped optional platform entries (workerd, nestedesbuild) restored and vitest 4 hoisted for@testing-library/jest-dom.npm cifrom a clean tree and an optional-dependency integrity check both pass.Verification (local, sequential)
typecheck; control-plane unit and integration; web, github-bot, slack-bot, linear-bot, shared, mcp-server, docs; sandbox-runtime node + pytest; modal-infra and sandbox-images pytest; terraform test (production + modal-app); eslint; prettier on tracked files; SQL-portability, workflow-contract, d1-migrate and coverage-gate script tests. Knip is advisory in CI (
--no-exit-code) and fails identically on pristine upstream.