This change uses separate http clients/transports in TestValidateToken
subtests. Previously parallel subtests of TestValidateToken shared
a http.DefaultTransport. When one subtest's httptest.Server.Close() ran in
t.Cleanup, it called http.DefaultTransport.CloseIdleConnections, which
could interrupt connection(s) used in another subtest.
## Description
Adds automatic key failover for centralized OpenAI provider, covering both chat completions and responses APIs. Same shape as the Anthropic PR: each upstream call walks the configured key pool, keys are marked **temporary** on 429 (with cooldown from `Retry-After`) and **permanent** on 401/403. Each agentic-loop iteration gets its own fresh walker so a tool-call continuation can fail over independently of the initial request.
BYOK is unchanged: BYOK requests run as a single attempt with no failover.
## Changes
- `config.OpenAI` carries a `KeyPool`. `Key` remains for BYOK Authorization Bearer set per interception.
- Chat completions blocking interceptor: walks the pool via `newChatCompletionWithKeyFailover`, marks keys on key-specific failures, returns on first success or non-failover error.
- Chat completions streaming interceptor: per-iteration walker. Pre-stream failures fail over to the next key; mid-stream errors are relayed as SSE events.
- Responses blocking interceptor: extracts `newResponseWithKeyFailover` parallel to chatcompletions.
- Responses streaming interceptor: per-iteration walker, retains the existing buffer-then-forward design.
## Related Issues
Related to: https://github.com/coder/internal/issues/1446
Related to: https://linear.app/codercom/issue/AIGOV-197/aibridge-automatic-key-failover-for-bridged-and-passthrough-routes
## Follow-up PRs
- Bedrock multi-key support.
- Refactor provider vs interceptor config separation.
- Record the actually-used key in the interception credential hint after failover.
> [!NOTE]
> Initially generated by Claude Opus 4.7, modified and reviewed by @ssncferreira
## Description
Adds automatic key failover for centralized Anthropic provider. When a key pool is configured, each upstream call walks the pool and tries keys in order until one succeeds or the pool is exhausted. Keys are marked **temporary** on 429 (with cooldown from `Retry-After`) and **permanent** on 401/403. Errors that aren't key-specific don't trigger failover. Each agentic-loop iteration gets its own fresh walker, so a tool-call continuation can fail over independently of the initial request.
BYOK is unchanged: BYOK requests run as a single attempt with no failover.
## Changes
- `config.Anthropic` carries a `KeyPool`. `Key` remains for BYOK X-Api-Key set per interception.
- Blocking interceptor: walks the pool, marks keys on key-specific failures, returns on first success or non-failover error.
- Streaming interceptor: per-iteration walker. Pre-stream failures fail over to the next key; mid-stream errors are relayed as SSE events.
- New `keypool` error types: `TransientExhaustionError` (carries soonest cooldown) and `ErrPermanentExhaustion`. Replace the prior `ErrAllKeysExhausted`.
- Error responses now consistently include the outer `"type": "error"` field.
## Related Issues
Related to: https://github.com/coder/internal/issues/1446
Related to: https://linear.app/codercom/issue/AIGOV-197/aibridge-automatic-key-failover-for-bridged-and-passthrough-routes
## Follow-up PRs
- Bedrock multi-key support.
- Refactor provider vs interceptor config separation.
- Record the actually-used key in the interception credential hint after failover.
> [!NOTE]
> Initially generated by Claude Opus 4.7, modified and reviewed by @ssncferreira
The async title-generation and turn-summary goroutines launched from
processChat run autocommit UPDATEs on the chat row after finishActiveChat
has set the chat to pending and signalWake has fired. If the row lock
from one of those UPDATEs is held while acquireLoop's processOnce runs,
AcquireChats's FOR UPDATE SKIP LOCKED skips the freshly-pending chat and
returns no rows. The wake is then consumed with no acquisition, and the
chat sits in pending until the next acquireTicker (default 1s).
Wake again after each UPDATE commits. The second wake covers the race
window without changing the transaction semantics.
Closescoder/internal#1500
## Summary
- switch the Ubuntu 26.04 dogfood image to Docker's jammy apt repository
so Docker 27 remains available
- pin `docker-ce` and `docker-ce-cli` to the Docker 27 line and keep
`containerd.io` pinned to `1.7.23-1`
- fold the containerd pin into the Docker preferences file, remove the
duplicate containerd preferences file, and hold the installed Docker
packages in the image
## Notes
Docker 28+ requires `containerd.io >= 1.7.27`, but sysbox /
Docker-in-Docker currently requires `containerd.io=1.7.23-1`, so the
image needs the older Docker 27 packages from the jammy repo.
## Testing
- Not run locally; verified the branch diff only.
Adds a persisted, draggable left sidebar width for the agents page. The
resize handle uses the same pointer-capture resize technique as the
existing right panel and clamps the expanded sidebar between 240px and
`min(520px, 50vw)`.
Updates the agents page skeleton to read the same stored sidebar width
and adds Storybook interaction coverage for resize clamping and
persistence.
Polishes the advisor tool card so the header uses a clearer lightbulb icon, inline pill metadata, wrapped/clamped question text, and a separate advice pill before the rendered response.
Adds Storybook coverage for a long advice plus 1.8k-character question peak state, including collapse/expand behavior.
Refs https://linear.app/codercom/issue/CODAGT-322/improve-advisor-icon-and-duplicate-loading-ui
<details>
<summary>Coder Agents disclosure</summary>
This PR was generated by Coder Agents.
</details>
Skips `TestExploreChatSendMessageCannotMutateMCPSnapshot` while the
chatd redesign is in flight. The test exposes a self-interrupt race in
`processChat`'s control-pubsub subscriber that is structurally fixed by
the redesign in #24444; skipping until then matches the existing
`TestSubscribeRelayEstablishedMidStream` skip in
`enterprise/coderd/x/chatd/chatd_test.go`.
Relates to https://github.com/coder/internal/issues/1493.
This adds Homebrew and mise to the Ubuntu dogfood images and makes mise
shims win
PATH resolution for the `coder` user. It installs Homebrew in
`/home/linuxbrew/.linuxbrew`, installs the latest mise release
(`v2026.4.19`) via
its verified GitHub release artifact, exposes mise at
`/usr/local/bin/mise`, wires
`HOMEBREW_*` and `MISE_DATA_DIR`, and adds build-time checks for both
tools. The
mise executable target lives in writable `/opt/mise/bin` so `mise
self-update`
can replace it as the `coder` user. This also adds `libc6-dev` to the Go
utility
stages so the existing CGO-backed tool installs keep building on newer
Ubuntu
bases.
The dogfood template now mounts a dedicated `/home/linuxbrew/` Docker
volume in
addition to `/home/coder/`. Fresh volumes are seeded from the
image-baked
Homebrew tree on first mount, while user-installed formulae persist
across
workspace container recreation.
I revalidated the bootstrap on jammy and resolute base images with fresh
mounted
`/home/coder` and `/home/linuxbrew` volumes. In those runs, `brew
install hello`
succeeded, `mise doctor` reported no PATH or activation problems, `mise
self-update --force --yes --no-plugins 2026.4.19` succeeded as `coder`,
and
`mise use --global github:BurntSushi/ripgrep@14.1.1` moved `rg`
resolution to
the mise shim after container recreation.
---
<details>
<summary>📋 Implementation Plan</summary>
# Plan: add `mise` and Homebrew to the dogfood Ubuntu images with
mise-first PATH
## Goal
- Make both dogfood Ubuntu images ship `brew` and `mise`.
- Ensure `mise doctor` does **not** complain about activation/PATH
ordering in the shell entrypoints we support.
- Keep the implementation robust against the persistent `/home/coder`
volume used by the dogfood template.
## Verified context
- The relevant image definitions are:
- `dogfood/coder/ubuntu-22.04/Dockerfile`
- `dogfood/coder/ubuntu-26.04/Dockerfile`
- The dogfood template mounts a persistent home volume at `/home/coder/`
in
`dogfood/coder/main.tf:840-843`, so required image-baked state should
not
live only under `/home/coder`.
- Both Dockerfiles already manipulate PATH in multiple places:
- Go appended early (`:26`)
- Cargo prepended (`ubuntu-26.04/Dockerfile:202-206`; mirrored in 22.04)
- Node via nvm prepended (`ubuntu-26.04/Dockerfile:245-255`; mirrored in
22.04)
- Final `coder` PATH prepends `/home/coder/go/bin`
(`ubuntu-26.04/Dockerfile:348-358`; mirrored in 22.04)
- `COPY files /` is already present in both Dockerfiles, so adding new
global
shell-init files is possible without Terraform changes.
- `scripts/lib.sh:94-124` uses `command -v` for dependency detection, so
PATH
order is the practical repo-level behavior we need to control.
- `.github/workflows/dogfood.yaml:99-126` builds both Ubuntu variants,
and the
22.04 image is still tagged `latest`, so both Dockerfiles must be
updated in
the same change.
## Recommended implementation
### Phase 1 — Bootstrap Homebrew and `mise` in both Ubuntu Dockerfiles
1. Update both Dockerfiles in parallel:
- `dogfood/coder/ubuntu-22.04/Dockerfile`
- `dogfood/coder/ubuntu-26.04/Dockerfile`
2. Add the minimum explicit Homebrew prerequisites that are missing from
the
current apt package set.
- The images already install `build-essential`, `curl`, `file`, and
`git`.
- Audit whether `procps` must be added explicitly for Homebrew’s Linux
requirements.
3. Install Homebrew in the supported Linux prefix:
- Prefix: `/home/linuxbrew/.linuxbrew`
- Keep the install/build logic in the Dockerfile, before `USER coder`.
- Make the resulting prefix writable by `coder` before switching users.
Prefer the smallest-diff approach that leaves `brew install ...` usable
as
`coder`.
4. Install `mise` to a stable image-owned path instead of relying on
`~/.local/bin`:
- Preferred binary path: `/usr/local/bin/mise`
- Use a pinned installation method that fits the current Dockerfile
style
(versioned release asset or otherwise explicitly pinned installer path).
5. Add defensive build-time sanity checks near the install steps so the
image
fails early if assumptions are wrong:
- `test -x /usr/local/bin/mise`
- `test -x /home/linuxbrew/.linuxbrew/bin/brew`
- `brew --version`
- `mise --version`
**Quality gate:** both Dockerfiles build locally, and the resulting
container can
run `brew --version` and `mise --version` as `coder`.
### Phase 2 — Make `mise` win PATH resolution by default
1. After `USER coder` in both Dockerfiles, define stable environment
variables
for the final shell/runtime behavior:
- `HOMEBREW_PREFIX=/home/linuxbrew/.linuxbrew`
- `MISE_DATA_DIR=/home/coder/.local/share/mise`
- `MISE_ACTIVATE_AGGRESSIVE=1` only if later shell activation proves
necessary
2. Replace the final PATH composition so it resolves in this order:
1. `mise` shims
2. Homebrew `bin`/`sbin`
3. Existing `/home/coder/go/bin`
4. Existing image/system PATH
3. Keep the current Go/Rust/Node setup intact aside from the final PATH
ordering. Add a short Dockerfile comment explaining that `mise` shims
must be
first so `mise doctor` and `command -v` resolve `mise`-managed tools
ahead of
Homebrew/system binaries.
4. Do **not** rely on image-baked `mise` state under `/home/coder` for
the
initial implementation. The goal here is binary availability and path
precedence, not preinstalling shared `mise` toolchains.
**Quality gate:** in a fresh container as `coder`, `echo "$PATH"` shows
`mise` shims before Homebrew, and `mise doctor`/`mise doctor path` show
no PATH
or activation problem in the tested shell entrypoints.
### Phase 3 — Add shell-init hardening only if smoke tests prove it is
needed
1. Start with the Dockerfile `ENV PATH` solution as the default
behavior.
2. If dogfooding shows that supported login shells still need shell
integration
beyond the final `ENV PATH`, add minimal global shell-init files under:
- `dogfood/coder/ubuntu-22.04/files/etc/profile.d/`
- `dogfood/coder/ubuntu-26.04/files/etc/profile.d/`
3. If these files are needed, keep them narrowly scoped:
- a Homebrew file that exports/evals `brew shellenv`
- a `mise` file that only reinforces the intended shims-first behavior
4. Avoid touching per-user dotfiles in `/home/coder`; they are the wrong
place
for required image behavior because of the persistent home volume.
**Quality gate:** if profile.d files are added, login-shell smoke tests
pass and
we do not introduce new PATH-order regressions versus the
Dockerfile-only path.
## Acceptance criteria
- Both Ubuntu dogfood Dockerfiles are updated in one change and still
build.
- `brew` is installed in `/home/linuxbrew/.linuxbrew` and is usable as
`coder`.
- `mise` is installed at `/usr/local/bin/mise` and is usable as `coder`.
- `mise doctor` does not report an activation/PATH-ordering problem in
the
shell entrypoints we verify.
- Final PATH precedence is:
1. `mise` shims
2. Homebrew `bin`/`sbin`
3. existing user/tool paths
4. system paths
- Existing dogfood workflows still work for Go/Rust/Node tooling after
the PATH
change.
- The change passes the dogfood image CI path in
`.github/workflows/dogfood.yaml`.
## Dogfooding and verification
1. Build both images locally:
- `dogfood/coder/ubuntu-22.04`
- `dogfood/coder/ubuntu-26.04`
2. Run each image with an empty mounted home volume at `/home/coder` to
mimic
the actual dogfood runtime constraint instead of only testing the
image’s
baked filesystem.
3. Capture a short terminal recording and screenshots for each variant
showing:
- `brew --prefix`
- `brew --version`
- `mise --version`
- `echo "$PATH"`
- `mise doctor`
- `mise doctor path`
4. Verify at least one login-shell path and one non-login-shell path, so
we can
tell whether Dockerfile `ENV PATH` is sufficient or whether
`/etc/profile.d`
hardening is required.
5. Add one tool-resolution smoke test that proves `mise` wins when
configured:
- install/use a small `mise`-managed runtime as `coder`
- run `which -a <tool>`
- run `<tool> --version`
6. Verify existing image behavior did not regress:
- `go version`
- `node --version`
- any other must-have image tools that were already on PATH
7. Preserve the artifacts from dogfooding for review:
- screenshots attached to the change summary
- a short screen recording (or terminal recording) covering the smoke
test
## Risks and decision points
- **Homebrew ownership model:** installing Homebrew during `docker
build` is not
enough by itself; the prefix must end up writable for `coder`.
- **Scope control:** the initial change should solve `mise doctor` by
fixing PATH
precedence, not by introducing a larger `mise`-managed tool bootstrap.
- **Shell-init uncertainty:** if the dogfood terminal entrypoints do not
source
`/etc/profile`, a Dockerfile `ENV PATH` fix may be sufficient and
profile.d
may be unnecessary. This should be decided by smoke tests, not by
assumption.
- **Persistent home behavior:** avoid any required implementation detail
that
only works if fresh volumes copy image-baked `/home/coder` contents.
<details>
<summary>Why this is the lowest-risk path</summary>
This plan keeps the initial implementation focused on the user’s stated
goal:
install Homebrew and `mise`, then guarantee that `mise`-controlled paths
win so
`mise doctor` stays quiet.
The main repo-specific constraint is the persistent `/home/coder`
volume. That
pushes required binaries and ownership-sensitive state out of
`/home/coder`
where possible, and it argues against relying on user dotfiles for
required
image behavior.
Starting with Dockerfile-level install steps plus a final PATH reorder
keeps the
diff small, makes behavior consistent across shells, and gives us a
clean place
to add shell-init hardening only if the smoke tests prove it is
necessary.
</details>
</details>
---
_Generated with [`mux`](https://github.com/coder/mux) • Model:
`openai:gpt-5.5` • Thinking: `xhigh`_
`launchHeartbeat` could miss a stale-threshold update during startup if
`SetStaleAfter` ran after the heartbeat ticker was created but before
the goroutine subscribed to `thresholdChan`. In that case, the heartbeat
kept the old interval until a future tick, and the mock-clock test could
time out waiting for `Ticker.Reset` without advancing time.
Subscribe to `thresholdChan` before reading the heartbeat interval so
the channel consistently invalidates the interval. The regression test
now changes the threshold while ticker creation is trapped, making the
startup race deterministic.
Closes https://github.com/coder/internal/issues/1513
`TestAdvisorChainMode_SnapshotKeepsFullHistory` was using the generic
active chatd test server, which leaves periodic pending-chat polling
enabled. That made the test inconsistent with the other OpenAI Responses
API tests and allowed stale pending pubsub notifications to interrupt
the second turn before the advisor request was observed.
Use the existing OpenAI Responses test server helper so pending-chat
acquisition is delayed and the test only starts processing after the
SendMessage pending notification has been published.
Closes https://github.com/coder/internal/issues/1510
## Summary
When a workspace build fails because the user is over their group quota,
the chat tools currently surface the failure as a bare `"workspace build
failed: insufficient quota"` string with no machine-readable error code
and no visibility into the user's current usage. Agents and the UI
cannot distinguish quota failures from any other Terraform error, so
users see an opaque message and have no clear path to recovery.
This PR tags quota failures with a typed error code at the source and
propagates it through the chat tool layer so callers can react to it
explicitly.
Relates to CODAGT-20
## Changes
**Provisioner runner**
- Add `InsufficientQuotaErrorCode = "INSUFFICIENT_QUOTA"` and set it
explicitly at the `commitQuota` failure site via a new
`failedWorkspaceBuildfCode` helper, so `provisioner_jobs.error_code` is
populated only on the genuine quota path. The substring matcher used for
externally produced sentinels (e.g. `"missing parameter"`, `"required
template variables"`) is intentionally not extended; provider errors
that happen to mention "insufficient quota" stay classified as generic
build failures.
**SDK and API contract**
- Add `JobErrorCodeInsufficientQuota` and a
`JobIsInsufficientQuotaErrorCode` helper to `codersdk`.
- Extend the swagger `enums` tag on `ProvisionerJob.ErrorCode` to
include `INSUFFICIENT_QUOTA`.
- Regenerate `coderd/apidoc`, `docs/reference/api/*`, and
`site/src/api/typesGenerated.ts`.
**chattool create_workspace / start_workspace**
- `waitForBuild` now returns a typed `*workspaceBuildError` carrying
both the message and the `JobErrorCode`, instead of a bare error string.
- New `quotaerror.go` introduces a structured `quotaErrorResult` (with
`error_code`, `title`, `message`, `build_id`, and optional `quota`) and
a best-effort `workspaceQuotaDetails` lookup that wraps owner
authorization internally and fetches `credits_consumed` and `budget`
from the database. Quota lookup failures (including authorization
failures) never block the failure payload.
- On quota-coded build failures, both `create_workspace` and
`start_workspace` now return the structured response (with the recovery
guidance inlined into `message`) instead of the bare `"insufficient
quota"` string. This applies to all three failure paths: post-creation,
an in-progress existing build, and a freshly triggered start build.
Non-quota build failures continue to use the existing
`buildToolResponse` / `newBuildError` path.
- Owner authorization is wrapped only on the call sites that need it
(the `CreateFn` and `StartFn` invocations and the quota-detail lookup),
so idempotent fast paths (already running, already in progress,
existing-workspace early returns) do not pay for an extra RBAC
round-trip or fail when role lookup is transient.
## Out of scope
- No changes to quota math, allowances, or bypass behavior.
- No automatic retries.
- No new quota-inspection tools and no changes to MCP
`coder_create_workspace` (which returns immediately and never observed
the build outcome here).
- No frontend UI changes; those will land in a follow-up PR that
consumes the new `INSUFFICIENT_QUOTA` code.
Previously, the `CreateWorkspaceBuild` toolsdk tests only exercised a
start where the workspace's prior template version was also the
template's active version, so they did not prove that a plain start
keeps using the previously built version.
Replace that tautological coverage with an isolated fixture that
advances the template's active version and asserts a start without
`TemplateVersionID` still reuses the prior build's version.
Reduces the beta badge size in the Coder Agents UI from `sm` to `xs` for
better visual balance with the logo.
## Changes
- Added `xs` size variant to `FeatureStageBadge`, styled to match the
existing `Badge` component's `xs` variant (`text-2xs`, `h-[18px]`,
`border-0`, `rounded`)
- Updated both usages in `AgentPageHeader` (mobile) and `AgentsSidebar`
(desktop) from `size="sm"` to `size="xs"`
- Added `ExtraSmallBeta` Storybook story for the new size
> Generated by Coder Agents
This pull-request looks at all (most) of our instances of `const styles
= { ... }` and attempts to smooth them down into the minimum viable
Tailwind equivalent 🙂
PromoteQueued now branches on chat status: synth tool results before
the user message on requires_action, deferred reorder + Waiting on
running so the worker's persist+auto-promote keeps partial output.
Stale heartbeat falls through to the synchronous path; GetStaleChats
picks up Waiting+queue to recover post-cleanup-crash. Endpoint
returns 202.
Closes CODAGT-119
Persists the agent-generated turn-end summary on `chats` and shows it as
the Agents sidebar subtitle when present, falling back to the model
name. Errors still take precedence.
> Mux is acting on Mike's behalf.
## What changes
**Storage.** New nullable `last_turn_summary` column on `chats`
(migration `000486`). New `UpdateChatLastTurnSummary` query normalizes
blank/whitespace input to `NULL`, preserves `updated_at` (so the chat
does not jump to the top of the sidebar on summary writes), and uses an
`expected_updated_at` stale-write guard so an older async summary cannot
overwrite a newer turn.
**Backend.** `coderd/x/chatd/chatd.go` decouples summary generation from
webpush. Generated summaries persist for completed parent turns even
when webpush is unconfigured or has no subscriptions. The same generated
text is reused as the webpush body when webpush is configured, so the
summary model is not called twice. Generic fallback push text is no
longer persisted; it clears any stale summary instead.
Error/interrupt/pending-action terminal paths clear `last_turn_summary`
for the latest turn.
**Frontend.** `AgentsSidebar.tsx` subtitle priority is now `errorReason
|| lastTurnSummary || modelName`, normalized via the existing
`asNonEmptyString` helper from `blockUtils.ts`.
## Tests
- `TestUpdateChatLastTurnSummary` (database): success,
whitespace-to-NULL, stale guard rejects, `updated_at` preserved.
- `TestUpdateLastTurnSummaryRejectsStaleWrites` (chatd internal): direct
stale-`expected_updated_at` test.
- `TestSuccessfulChatPersistsTurnSummaryWithoutWebPush`: persistence
works without webpush subscriptions.
- `TestSuccessfulChatSendsWebPushWithSummary`: same generated text
drives both DB and push body.
-
`TestSuccessfulChatSendsWebPushFallbackWithoutSummaryForEmptyAssistantText`:
fallback text is not persisted.
- `TestErroredChatClearsLastTurnSummaryAndSendsWebPush`: error path
clears the field.
- `TestInterruptChatDoesNotSendWebPushNotification`: interrupt path
clears the field, no push fires.
- `AgentsSidebar.test.tsx`: subtitle priority for summary-present,
error-wins, no-summary fallback, whitespace fallback.
- `AgentsSidebar.stories.tsx`: `ChatWithTurnSummary` and
`ChatWithTurnSummaryAndError`.
## Notes
- No backfill. Existing chats keep showing the model name until their
next turn completes.
- Parent chats only in this iteration; the field is rendered on any
`Chat` if a future change extends generation to children.
- Decoupling generation from webpush adds quickgen model calls for
completed parent turns that previously skipped generation when no
subscriptions existed. Existing parent-only, assistant-text-present,
`PushSummaryModel` configured, and bounded-timeout gates keep this
behavior bounded.
Add a new Quickstart starter template that lets users pick programming
languages, editors, and an optional Git repo to clone. The template uses
Docker under the hood but presents a developer-focused experience: pick
your tools, start coding.
## What's included
- **Languages parameter** (multi-select): Python, Node.js, Go, Rust,
Java, C/C++
- **IDEs parameter** (multi-select): VS Code (Browser), VS Code Desktop,
Cursor, JetBrains, Zed, Windsurf
- **Git repo parameter**: Optional URL to clone on workspace start
- **JetBrains filtering**: Maps selected languages to relevant IDE codes
(Python → PyCharm, Go → GoLand, etc.)
- **Docker precondition check**: Uses `data "external"` +
`terraform_data` precondition to surface a friendly error when Docker is
unavailable, before the Docker provider fails with a cryptic message
- **4 presets**: Web Development, Backend (Go), Data Science, Full Stack
- **Single install script**: All languages install in one `coder_script`
to avoid apt-get lock conflicts (agent scripts run in parallel via
`errgroup`)
<details><summary>Design decisions</summary>
- **Docker as invisible backend**: Docker is required on the Coder
server but never mentioned in the user-facing parameter UI. The
experience is entirely "pick languages, pick editors, start coding."
- **`coder_script` over startup_script**: Language installs use a
templated script file (`install-languages.sh.tftpl`) driven by the
languages parameter. A single script avoids dpkg lock contention since
`coder_script` resources execute concurrently.
- **`data "external"` for Docker check**: The external provider probes
Docker availability independently of the Docker provider. If Docker is
down, the `terraform_data` precondition fails with a human-readable
message before any `docker_*` resource is evaluated. This depends on the
Docker provider connecting lazily (at resource eval time, not at
provider init), which current behavior confirms.
- **JetBrains filtering by language**: Rather than showing all 9
JetBrains IDEs, the template computes relevant IDE codes from the
language selection (e.g. Python → PY, Go → GO) and passes them as
`default` to the JetBrains module.
- **Arch-aware Go install**: The install script detects `uname -m` to
download the correct Go binary for amd64 or arm64.
</details>
<details><summary>Screenshots and recordings from the UI</summary>
<p>
<img width="1851" height="1471" alt="Screenshot 2026-05-05 at 2 14
20 PM"
src="https://github.com/user-attachments/assets/d4c9cdc5-d311-43a5-9e2e-f90b0019eda7"
/>
<img width="1851" height="1471" alt="Screenshot 2026-05-05 at 2 15
06 PM"
src="https://github.com/user-attachments/assets/cf3023fe-b6db-4503-a6c4-eaa0ec0659f8"
/>
https://github.com/user-attachments/assets/7507fd7d-ddb5-457a-9f7d-cbf89b36eb20
</p>
</details>
> [!NOTE]
> This PR was authored by Coder Agents.
## Summary
Bumps the repository Go toolchain from 1.25.9 to 1.26.2 across local
development, CI, dogfood Docker images, and Nix builds.
## Changes
- Update `go.mod` and the shared setup-go action to Go 1.26.2.
- Update dogfood Ubuntu image Go versions and the official linux-amd64
tarball checksum.
- Move Nix Go module builds from `buildGo125Module` to
`buildGo126Module`.
- Regenerate API docs affected by Go 1.26 stdlib URL documentation
changes.
## Validation
- `./scripts/check_go_versions.sh`
- `make fmt`
- `make lint`
- `make build-slim`
- `make test TEST_SHORT=1`
- `make pre-commit`
> 🤖 This PR was created with the help of Coder Agents, and needs a human
review. 🧑💻
Bumps the dogfood template to the refactored Claude Code and Codex
modules and removes the Coder Tasks integration.
Claude and Codex now use slim-window app buttons that launch each tool
in its own tmux session. This replaces the task-specific `develop.sh`
and `preview` apps that were only created for Coder Tasks workspaces.
The PR also wires the OpenAI dogfood secret through the deployment
template so Codex can fall back to template configured BYOK when AI
Gateway is disabled.
Tested with this template version:
[https://dev.coder.com/templates/coder/coder/versions/outstanding_hermann97](<https://dev.coder.com/templates/coder/coder/versions/outstanding_hermann97>)
Fixes CODAGT-175
Addresses a review finding in https://github.com/coder/coder/pull/23827
that the nil-guards for both `database.Store` and `chatID` are both dead
code in practice in the `chattool` package.
- Modifies the return signatures require passing both `database.Store`
and `chatID` explicitly as positional arguments instead of just
parameter struct keys.
- Drops the nil-guards for `database.Store` and `chatID`.
Tightens the chat stream subscription path on a few related axes. None
of these changes touch the steady-state event flow; they all concern the
subscribe handshake.
## Motivation
`Server.Subscribe` carries three responsibilities that were entangled:
1. Authorize the caller against the chat row.
2. Arm local + pubsub subscriptions before any DB reads
(subscribe-first-then-query).
3. Build the initial snapshot from a fresh chat row, message history,
and queue.
When all three live in one function and share the request context, a few
unfortunate behaviors fall out:
- The HTTP handler's middleware already loaded and authorized the chat
row, but `Subscribe(chatID)` discarded it and re-fetched on every
WebSocket connection.
- The chat row used to populate the initial `status` event was loaded
*before* the pubsub subscription was armed, so a status transition that
happened in that window was silently lost.
- Control-path DB reads inherited whatever context the caller passed in.
A caller without a deadline could wedge a subscriber goroutine
indefinitely on a stalled DB.
- A transient failure of the chat re-read collapsed the entire
subscription instead of degrading gracefully.
## What changes
**Split the auth boundary out into the type signature.** A new
`SubscribeAuthorized(ctx, chat, ...)` takes the already-authorized row
directly. The HTTP handler in `coderd/exp_chats.go` calls it with the
chat row from `httpmw.ChatParam`, eliminating the redundant
`GetChatByID`. `Subscribe(chatID)` is preserved as a thin wrapper for
callers that don't have a chat row in hand (tests, internal callers); it
does the auth lookup and delegates.
**Re-read the chat after arming subscriptions.** Inside
`SubscribeAuthorized`, after the local stream and pubsub subscriptions
are active, we reload the chat row to populate the initial `status`
event and any enterprise relay setup. Combined with the existing
subscribe-first-then-query pattern, this closes the gap where a status
transition between the middleware's load and the subscription arming
would not appear in either the initial snapshot or a live notification.
**Fall back to the middleware row on refresh failure.** If the
post-subscription refresh fails (transient DB blip, brief pool
exhaustion), we log a warning and reuse the row that proved
authorization in the first place. Messages, queue, and pubsub are all
independent of this row, so the stream still works; the initial `status`
is just slightly stale and self-corrects via the next pubsub event.
**Bound subscriber control-path DB reads.** A new
`streamSubscriberControlFetchContext` helper applies a 5-second fallback
timeout only when the caller has no deadline of their own. Used at the
chat refresh, the initial queue load, and the queue-update goroutine
following pubsub notifications. HTTP-driven callers pass through
unchanged; background callers can no longer hang forever on a stalled DB
and leak subscriber goroutines, pubsub subscriptions, and `chatStreams`
entries.
Follow-up to #24955 (`refactor: move chat error kinds into codersdk`),
which moved `ChatErrorKind` into `codersdk` but did not refresh the
generated apidoc artifacts. As a result, `make gen` was producing a
dirty tree on `main`.
This PR is the output of running `make gen -B` on a clean checkout of
`main`. Only generated files are touched:
- `coderd/apidoc/docs.go`
- `coderd/apidoc/swagger.json`
- `docs/reference/api/chats.md`
- `docs/reference/api/schemas.md`
The diff adds the `codersdk.ChatErrorKind` schema and replaces the
previously-untyped `kind: string` fields on `codersdk.ChatError` and
`codersdk.ChatRetryEvent` with references to the new enum.
Moves the chat error kind taxonomy from `coderd/x/chatd/chaterror` into
`codersdk.ChatErrorKind` and types `ChatError.Kind` /
`ChatStreamRetry.Kind` so generated TypeScript exposes an SDK-owned
union, including `usage_limit`. Backend chat classification now
references the SDK constants directly while preserving the existing JSON
string values.
Keeps chat usage-limit admission failures on their existing 409 response
shape. The frontend maps structured usage-limit responses to the
SDK-owned `usage_limit` kind, uses generated `TypesGen.ChatErrorKind`
directly, and removes the local string union and alias.
## Summary
This fixes the stale helper-binary class of generator bugs in the
Makefile by adding the repo packages and embedded files that are
compiled into each affected `_gen/bin/*` helper as real prerequisites of
the helper binary target.
The concrete issue that prompted this was an audit docs regeneration
after a rebase. `docs/admin/security/audit-logs.md` depends on
`enterprise/audit/table.go`, so the docs target reran, but
`_gen/bin/auditdocgen` was only an order-only prerequisite and its own
rule only depended on `scripts/auditdocgen/*.go`. Because the stale
local `auditdocgen` binary had been compiled before `UserSecret` was
added to `enterprise/audit/table.go`, it regenerated the audit docs
without the `UserSecret` row even though the source table still
contained it.
This is the same failure mode I recently fixed for `_gen/bin/clidocgen`
in #24302 and `_gen/bin/modeloptionsgen` in #24543. Those fixes made the
binaries depend on the package sources and embedded template files whose
compile-time data they read at runtime, rather than relying on output
targets to mention those files. This PR applies that pattern to the
other high-value helper binaries with the same risk.
## Changes
- Rebuild `_gen/bin/auditdocgen` when `enterprise/audit/*.go` changes,
so audit docs are generated from the current `AuditableResources` and
`AuditActionMap` data.
- Rebuild `_gen/bin/apitypings` when `codersdk/*.go` changes, and make
`typesGenerated.ts` rerun when the health packages it emits change.
- Rebuild `_gen/bin/check-scopes` and `_gen/bin/apikeyscopesgen` when
RBAC or policy sources change.
- Rebuild `_gen/bin/dbdump` when migration Go or SQL files change, since
the migrations package embeds SQL into the binary.
- Rebuild `_gen/bin/typegen` when its Go sources, embedded templates,
RBAC/policy inputs, string helper, or country data change. Generated
RBAC files are deliberately excluded from the typegen binary input set
to avoid cycles with typegen outputs.
## Why this covers the class
Most generated output targets keep helper binaries as order-only
prerequisites. That is fine for avoiding unnecessary output churn, but
it means the helper binary target must be the cache boundary and must
list everything baked into the compiled binary. The affected helpers
import repo packages that expose maps, constants, struct tags, embedded
templates, or embedded SQL. Without those files on the binary rule, Make
can rerun an output target with an old executable and write semantically
stale generated content.
The fix keeps the existing order-only output structure and instead makes
each binary rule track its compile-time inputs directly. That matches
the previous clidocgen and modeloptionsgen fixes while avoiding a broad
`$(GO_SRC_FILES)` dependency for helpers that only need a small set of
packages.
> Written by Mux, reviewed by a human
macOS ARM reports arm64 via uname -m, but typos GitHub release assets
use aarch64 in their filenames. The mismatch produces a 404, so the
build/typos-$(VERSION) target fails silently and Apple Silicon users
fall back to whatever typos binary their environment provides, such as
the one from nix. That binary may be a different version than the one
pinned in CI, creating a skew where local lint/typos rejects strings
that CI accepts.
<!--
If you have used AI to produce some or all of this PR, please ensure you
have read our [AI Contribution
guidelines](https://coder.com/docs/about/contributing/AI_CONTRIBUTING)
before submitting.
-->
> Mux is acting on Mike's behalf.
Adds configurable retention for chat debug data, including the purge
query, updated_at index, site config, experimental API, SDK types,
frontend lifecycle setting, and docs.
The purge deletes debug runs older than the configured retention window
and relies on existing cascades to delete steps. The default retention
is 30 days, and setting the value to 0 disables the purge.
## Summary
Restores `v2.33.0-rc.2`-equivalent query cost for agent
instance-identity auth on `v2.33.0-rc.3`, which currently saturates the
pgx pool when multiple agents share an instance ID. Customer report
against rc.3 traced 233× `Internal error fetching provisioner job
resource. fetch related workspace build: context canceled` 500s during a
50-minute incident window to this path.
Backport to `release/2.33` will follow as a separate PR after this
merges.
## Root cause
[#24325](https://github.com/coder/coder/pull/24325) ("support multiple
agents with shared instance-identity auth") rewrote
`coderd/workspaceresourceauth.go::handleAuthInstanceID` to use the new
`:many` agent lookup followed by a per-candidate filter loop. Each
iteration synchronously calls `GetWorkspaceResourceByID` and
`GetProvisionerJobByID`. Both go through `dbauthz`, and both fan out
into the same `provisioner_job → workspace_build → workspace` cascade
because `authorizeProvisionerJob` always re-authorizes the workspace via
`GetWorkspaceBuildByJobID → GetWorkspaceByID`. The handler then
re-fetches resource and job again for the surviving agent.
Net effect on the agent-auth happy path:
| | SQL | RBAC |
|---|---|---|
| rc.2 baseline | 13 | 5 |
| rc.3 today, 1 agent | 19 | 7 |
| rc.3 today, 2 agents | 26 | 9 |
| **After this PR, 1 agent** | **6** | **3** |
| **After this PR, 2 agents** | **7** | **3** |
Under load, the rc.3 chain blocks on pool acquire and the request blows
past the 30s HTTP write timeout.
## Changes
### 1. System fast-path on `authorizeProvisionerJob`
(`coderd/database/dbauthz/dbauthz.go`)
Add an `AsSystemRestricted` early-return at the top of
`authorizeProvisionerJob`. Instance-identity auth has already proven
cloud identity before reaching the DB layer, so re-authorizing the
workspace on every provisioner-job lookup is pure overhead. Existing
`GetWorkspaceAgentsByInstanceID` already uses the same fast-path
pattern.
```go
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceSystem); err == nil {
return nil
}
```
### 2. Drop survivor re-fetch in `handleAuthInstanceID`
(`coderd/workspaceresourceauth.go`)
Capture the provisioner job alongside each candidate during the filter
loop so the survivor lookup does not re-fetch resource and job after
selection. The previous code fired the resource→job→build→workspace
cascade twice for the surviving agent.
## Tests
Adds `TestAuthorizeProvisionerJob_SystemFastPath` in
`coderd/database/dbauthz/dbauthz_test.go` with two sub-tests:
- `AsSystemRestricted/SkipsCascade` — strict mock fails the test if
`GetWorkspaceBuildByJobID` or `GetWorkspaceByID` is called.
- `NonSystemActor/StillCascades` — auditor (no `ResourceSystem`) still
pays the cascade and produces a `NotAuthorized` error, proving the
fast-path is gated correctly.
Updates 12 existing dbauthz suite cases to expect the new
`ResourceSystem.Read` check ahead of the workspace/template-version
check, with `FailSystemObjectChecks()` to force the slow path.
Existing integration coverage in
`TestPostWorkspaceAuthAWSInstanceIdentity/Ambiguous/{SingleAgent,
MultipleAgentsWithSelector, MultipleAgentsNoSelector, SubAgentExcluded,
...}` exercises Part 2 end-to-end and continues to pass.
## Footprint
- 3 files changed, +166/-48
- No SQL changes
- No `make gen`
- No migrations
- No audit-table updates
## Validation
- [x] `go test ./coderd/database/dbauthz/` — full suite, ~6s
- [x] `go test -run TestPostWorkspaceAuth ./coderd/` — instance-identity
handler tests
- [x] `go test -run TestProvisionerJob ./coderd/`
- [x] `go test -run TestWorkspaceAgent ./coderd/`
- [x] `go test ./coderd/provisionerdserver/`
- [x] `gofmt -l` clean
## Alternatives considered
- **SQL-side filter:** rewrite `GetWorkspaceAgentsByInstanceID` to join
`workspace_resources`/`provisioner_jobs` and filter `job.type =
'workspace_build'` server-side, eliminating the filter loop entirely.
Cleaner long-term, but changes generated SQL and is too much surface for
a release-branch hotfix. Worth doing as a follow-up.
- **Full revert of #24325:** removes the multi-agent feature outright;
conflicts with downstream commits
([#24441](https://github.com/coder/coder/pull/24441),
[#24438](https://github.com/coder/coder/pull/24438),
[#24313](https://github.com/coder/coder/pull/24313)). Reserved as
fallback if the surgical fix doesn't hold under load testing.
Improves the Docker daemon troubleshooting in the quickstart and Docker
install docs:
- Renames the quickstart entry from "Cannot connect to the Docker daemon
on Linux" to cover all platforms.
- Adds a plain-English explanation of what the error means (Docker is
not installed or not running).
- Adds tabbed macOS/Linux/Windows instructions to the quickstart (macOS
and Windows were missing).
- Simplifies the Linux steps to match what Step 1 of the quickstart
already teaches.
- Adds a matching entry to `docs/install/docker.md` with a cross-link to
the quickstart for platform-specific steps.
Supersedes #24907 which was closed without merging.
Fixes https://linear.app/codercom/issue/DEVREL-23
> Generated with [Coder Agents](https://coder.com/agents)
Swap the order of the `Coder Agents` and `Coder Tasks` entries inside
the AI Coder section of `docs/manifest.json` so `Coder Agents` appears
before `Coder Tasks` in the docs sidebar.
No content changes; the two top-level child objects and their subtrees
are swapped, with trailing-comma placement adjusted to keep the JSON
valid.
---
PR generated with Coder Agents