`TestCircuitBreaker_FullRecoveryCycle/OpenAI` flaked once on macOS CI.
The most likely cause is that the circuit breaker `Timeout`
(open-to-half-open transition) was too short relative to the time
between test phases. On a slow runner, the breaker could transition to
half-open before the test verified it was still open, so the request
went through as a half-open probe instead of being rejected.
Increases `Timeout` to `testutil.IntervalMedium` (250ms) across all
circuit breaker integration tests.
**Note:** Ideally, these tests would use a mock clock for deterministic
timing, but https://github.com/sony/gobreaker (the library used for
circuit breaker logic) uses real time internally and doesn't expose a
clock interface.
Closes https://linear.app/codercom/issue/AIGOV-438
> Generated with [Coder Agents](https://coder.com/agents) on behalf of
@ssncferreira
Applies follow-ups from the key pool failover work:
- Add a test verifying key pool state is shared across bridged and passthrough routes.
- Refactor the key failover and passthrough tests to use the shared `MockUpstream` helper.
- Simplify how the request body option is passed through the Anthropic messages interceptor.
- Make `ResponseErrorFromKeyPool` nil-safe and cover it with a test.
Closes: https://linear.app/codercom/issue/AIGOV-398/small-follow-up-cleanups-for-key-failover
> [!NOTE]
> Initially generated by Claude Opus 4.7, modified and reviewed by @ssncferreira
## Description
Separates the aibridge provider configuration from the per-request configuration an interceptor actually needs, and introduces a single `Credential` type that each provider resolves per request. Previously a provider handed its full config to the interceptor (including fields the interceptor didn't use) while other request data was passed as loose arguments, and authentication was spread across config fields and arguments.
## Changes
- Add `intercept.Config`: the per-request, provider-agnostic configuration an interceptor needs (`ProviderName`, `BaseURL`, `APIDumpDir`, `SendActorHeaders`).
- Introduce a single `Credential` interface (`BYOK` and `Centralized`) that each provider resolves per request in `resolveCredential`, and have interceptors route on the credential kind.
- Fail fast with `ErrNoCredential` when a request is neither BYOK nor backed by a centralized key pool.
- Remove unused provider config fields (`Key`, `BYOKBearerToken`, `ExtraHeaders`).
Closes: coder/aibridge#266
Closes: https://linear.app/codercom/issue/AIGOV-221/refactor-separate-provider-and-interceptor-configs
> [!NOTE]
> Initially generated by Claude Opus 4.7, modified and reviewed by @ssncferreira
Prevents slow chat auto-archive runs from causing a constant archival
loop by resetting the ticker only after each run completes.
Also documents the UTC midnight cutoff used for archive eligibility so
chats with activity on the same UTC calendar date stay eligible or
ineligible for the full day.
Addresses deferred review comments:
- https://github.com/coder/coder/pull/26109#discussion_r3380197310
- https://github.com/coder/coder/pull/26109#discussion_r3380219922
Generated by Coder Agents and closely reviewed by Hugo.
The dormancy notification's "will be automatically deleted in X"
sentence rendered the dormancy threshold instead of the auto-delete
duration. A 30-day threshold rendered as "4 weeks" even when auto-delete
was 90 days; a 60-day threshold rendered as "1 month" with a 7-day
auto-delete. Render the countdown from the auto-delete setting, and skip
the deletion sentence entirely when auto-delete is disabled so the
notification no longer promises a deletion that will never happen.
## Overview
Split from #26466, scoped to **agent-only** changes. This PR exposes the
agent's context sources and snapshots over the existing agent socket.
There
are no changes outside `agent/`.
## What's included
- **agentsocket**: context source CRUD (`ContextSources`,
`GetContextSource`,
`AddContextSource`, `RemoveContextSource`) plus `GetContextSnapshot` and
`ResyncContext` RPCs, with matching client methods and proto. The server
receives the context `Manager` via `WithContextManager` and returns a
clean
error when it is absent.
- **agentcontext**: the resync JSON response now carries the
per-resource
`Name`, keeping the HTTP resync payload in sync with the drpc
`PushContextState` path in `agentsocket`.
- **agent**: passes the context `Manager` to the socket server via
`WithContextManager`.
## What's intentionally NOT here
- No MCP wiring. There are no MCP additions in `agent.go` or
`agentcontext`.
MCP ownership will land later in `agentcontext`; this PR does not build
on
`agent/x/agentmcp`.
- No changes to `agent/x/agentmcp` or the `agentcontext` resolver. The
socket
serves whatever context resources the `Manager` already resolves.
<details>
<summary>Context for reviewers</summary>
This is one of several PRs split out of #26466. Earlier revisions also
wired
live MCP servers through the socket; that scope was removed so this PR
stays
purely socket + context plumbing inside `agent/`. The agentcontext
resolver,
`agent/x/agentmcp`, and `agent.go` MCP startup behavior are unchanged
from
`main`.
</details>
---
_Created by Coder Agents on behalf of @kylecarbs._
fixes#22420
ref DEVEX-369
ref DEVEX-269
The bug on `CreateWorkspacePage`, where clicking one external auth
provider login button disabled all providers' login buttons, was caused
by providers all sharing a single polling status (`"idle" | "polling" |
"abandoned"`) in the `useExternalAuth` hook.
## changes
- Instead of setting one status across all providers, the polling status
in `useExternalAuth` is now tracked for each provider in a record whose
keys are the providers' IDs.
- The biggest diff is a new Storybook file
CreateWorkspacePage.stories.tsx which reproduces the bug behavior from
the issue.
- Until now we've only had CreateWorkspacePageView.stories.tsx, which
isn't able to model the user interactions / API responses needed to
verify the bugfix. This file is unchanged.
- Also deletes `CreateWorkspacePage`'s `useExternalAuth` hook in favor
of the global `useExternalAuth` hook (see #26310)
(co-written with Coder Agents)
Add a GET endpoint at `/api/v2/agent-firewall/sessions/{id}` that
returns agent firewall session metadata (`id`, `workspace_id`,
`owner_id`, `confined_process`, `started_at`). The handler authorizes
against the `boundary_log` resource with `ActionRead` via dbauthz.
The endpoint is enterprise-only, gated behind the `FeatureBoundary`
entitlement.
The `GetBoundarySessionByID` SQL query JOINs through `workspace_agents`
→ `workspace_resources` → `workspace_builds` → `workspaces` to return
`workspace_id` and `workspace_owner_id` directly, avoiding a separate
query.
Also adds an `owner_id` column to the `boundary_logs` table (migration
000526) with a FK to `users(id)` and a backfill from
`boundary_sessions`. This enables user-scoped RBAC authorization for
`InsertBoundaryLogs` via `.WithOwner()`, ensuring workspace agents can
only insert logs for their own owner.
Depends on #24810
**RBAC behaviour:**
| Role | Result |
|---------|--------|
| Owner | read |
| Auditor | read |
| Member | 404 |
> [!NOTE]
> This PR was authored by Coder Agents.
Implement the module settings wizard step, which renders a
`ModuleConfiguration` card per selected module with variable
configuration fields.
- Map non-sensitive variables to `ConfigurationFieldDefinition` (switch
for bool, text input for string/number)
- Show info notice with `code` tags for sensitive variables that will be
collected from developers at workspace creation
- Disable Continue button until all required non-sensitive variables
across all selected modules have values (`moduleSettingsComplete`
helper)
- Step is automatically skipped when no selected modules have
configurable variables
Relates to [DEVEX-286](https://linear.app/codercom/issue/DEVEX-286).
> [!NOTE]
> This PR was authored with Coder Agents.
---------
Co-authored-by: Andrew Aquino <dawneraq@gmail.com>
ref DEVEX-449
## before
In `AgentDevcontainerCard`, the devcontainer's display name truncated
with an ellipsis--but only when the viewport is <768px (parent has
`md:w-full`), which is also when the display name width gets capped at
320px (`max-w-xs`). At larger screen sizes, the display name overlaps
with the rebuild button to the right.
https://github.com/user-attachments/assets/47af30a2-f446-4b5f-bf63-24afb0532b19
## after
Now, the devcontainer display name fills up as much width as possible at
all screen sizes (not just >=768px), and it also gets truncated with an
ellipsis at all screen sizes.
https://github.com/user-attachments/assets/68579438-6fb5-40ea-93cf-c3814dfaac1a
---
(If we have a way to automate resizing screens in Storybook, lmk!)
The trick is to set a `min-width` on the text we want to truncate, see
this CSS Tricks article: https://css-tricks.com/flexbox-truncated-text/
I changed `max-w-xs` to `min-w-[284px]`. In addition to constraining the
display name's width (see "before" above), the `max-width` caused the
rebuild button to wrap to the next line if the screen is too narrow to
show the display name at that width. This behavior on small screens is
preserved. 🙂 However, since the display name's width isn't constrained
anymore, it's written out fully if possible, which causes the rebuild
button to wrap lines on wider screens if the display name is very long.
Closes
[CODAGT-629](https://linear.app/codercom/issue/CODAGT-629/agents-can-get-stuck-and-ignore-stop-or-nudge).
A stuck chat had these logs associated with it:
```
1781735334322 2026-06-17T22:28:54.322Z 2026-06-17 22:28:54.322 [debu] coderd.chatd.processor: workspace context build: workspace agent not resolvable chat_id=d4524ebb-4494-47df-b258-d933c0248942 owner_id=d96bf761-3f94-46b3-a1da-6316e2e4735d
1781735334298 2026-06-17T22:28:54.298Z 2026-06-17 22:28:54.298 [debu] coderd.chatd.processor: plan path instruction: agent not reachable chat_id=d4524ebb-4494-47df-b258-d933c0248942 owner_id=d96bf761-3f94-46b3-a1da-6316e2e4735d chat_id=d4524ebb-4494-47df-b258-d933c0248942 ...
error= workspace has no running agent: the workspace is likely stopped. Use the start_workspace tool to start it:
github.com/coder/coder/v2/coderd/x/chatd.init
<autogenerated>:1
```
"workspace agent not resolvable" is printed by
[`fetchContextForBuild`](<https://github.com/coder/coder/blob/fecc991ac9f9d673d7a772b0042ca821d19b6296/coderd/x/chatd/workspace_context_builder.go#L145>).
this causes
[`buildWorkspaceContext`](<https://github.com/coder/coder/blob/fecc991ac9f9d673d7a772b0042ca821d19b6296/coderd/x/chatd/workspace_context_builder.go#L60>)
to exit with a `errWorkspaceContextUnavailable` error. That in turn is
interpreted by
[`persistWorkspaceContext`](<https://github.com/coder/coder/blob/fecc991ac9f9d673d7a772b0042ca821d19b6296/coderd/x/chatd/generation.go#L838>)
as an "expected exit" scenario. That's a bug: because the task exits
without changing the chat state, the runner never issues another task to
process the chat any further. But even if it did, it'd go through the
same code path and exit again. We need to ensure that
`persistWorkspaceContext` commits a marker file even if it cannot reach
the agent.
Closes CODAGT-572
## Overview
Bumps `charm.land/fantasy` to the head of `coder_2_33`
(`v0.0.0-20260617050554-2e3ddbca75dd`) and adapts `chatd` to it.
The fantasy bump:
- Syncs upstream `charmbracelet/fantasy` main (v0.31.0) into
`coder_2_33` (coder/fantasy#42).
- Mirrors the request region when prefixing cross-region inference
profiles, so a legacy (un-qualified) Bedrock model ID is prefixed for
the same region the request is actually signed for.
Pulling in the new fantasy version propagates its required transitive
dependency upgrades (aws-sdk-go-v2, OpenTelemetry, google genai,
`golang.org/x/*`, etc.) through MVS, which accounts for the bulk of the
`go.mod`/`go.sum` churn.
## chatd changes
- Thread a per-provider `Region` through `ConfiguredProvider` and
`ProviderAPIKeys` (`RegionByProvider`), and merge/prune/resolve it
alongside API keys and base URLs.
- Source the Bedrock region from AI provider settings in `chatd` and
pass `fantasybedrock.WithRegion` when a region is configured.
- Migrate the runtime Bedrock title-generation model ID to a
fully-qualified `global.anthropic.*` ID.
- Emit a `finish_reason` in the test OpenAI streaming server so streams
close on a terminal event, matching fantasy's fail-closed stream
handling.
## Heads-up: most of this is short-lived
Almost all of the `chatd` code in this PR only executes on the **direct
(non-gateway) routing path** — the branch taken when
`AIGatewayRoutingEnabled` is `false`. That flag was a transition crutch
for AI Gateway routing, and it (plus the entire direct path /
`x/chatd/chatprovider` package that backs it) is slated for removal in
CODAGT-598. Under AI Gateway routing — which is the path every
deployment is expected to run — the Bedrock region is resolved by
aibridge directly from provider settings (`cli/aibridged.go` builds
`aibridge.AWSBedrockConfig{Region: settings.Bedrock.Region}`), so none
of the region plumbing added here is reached.
Concretely, expect the following to be deleted alongside the direct
path:
- The `RegionByProvider` map, the `Region()` accessor, and the region
preservation in merge/resolve plus the region pruning in
`PruneDisabledProviderKeys` (`chatprovider.go`).
- The `fantasybedrock.WithRegion(region)` branch in `ModelFromConfig` —
only reachable on the direct path; the gateway path builds a
`fantasyanthropic` client with no region key.
- Reading `settings.Bedrock.Region` in `aiProviderConfigFromKeys`
(`chatd.go`).
- The region-specific tests in `chatprovider_test.go`, and the
`chattest`/`model_coverage` adjustments that support direct-path
testing.
What survives the cleanup (independent of routing):
- The `charm.land/fantasy` bump and its `go.mod`/`go.sum` transitive
churn.
- The fully-qualified `global.anthropic.*` Bedrock title-generation
model ID in `quickgen.go` (a runtime-valid model identifier, not
direct-path-specific).
We're landing the full change anyway so the direct path stays correct
for the remaining transition window; just don't be surprised when
CODAGT-598 reclaims most of it.
## Notes
Depends on coder/fantasy `coder_2_33` already containing the upstream
sync and Bedrock region fix (merged via coder/fantasy#42 and
coder/fantasy#43).
Implement the module selection wizard step with multi-select toggle and
conflict warnings.
- Add `getTemplateBuilderModules` API client method with optional `base`
parameter for OS filtering
- Add react-query wrapper with `staleTime: Infinity` to prevent
re-fetches on step navigation
- Render a flat grid of `ModuleCard` components with checkbox-style
multi-select
- Show non-blocking conflict warnings when selected modules declare
`conflicts_with` each other
- Map selections to `TemplateBuilderComposeModule[]` and
`SelectedModuleMeta[]` for wizard state
Relates to [DEVEX-285](https://linear.app/codercom/issue/DEVEX-285).
> [!NOTE]
> This PR was authored with Coder Agents.
---------
Co-authored-by: Andrew Aquino <dawneraq@gmail.com>
Fixes ENG-2930
Fixescoder/internal#1597
Refactors TestWSWatcher to reduce flake occurrences.
The flaky tests were using polling-based assertions which may flake
based on goroutine scheduling.
Fixed by using fake connections and channel synchronization where
appropriate.
Note: No coverage of ProbeCanceled, pre-existing.
> Generated by a human, spot-checked by several robots.
Adds `coder ai-gateway keys` commands:
* `create <name>` creates key with given name
* `list` lists existing keys (alias `ls`)
* `delete <name | id>` removes key matching by name or key id, name has
priority (alias `rm`)
Closes https://github.com/coder/internal/issues/1601. Fixes a stream
parts WebSocket close race. If the peer closed first, the session read
loop could close the connection before `StreamPartsSession.Close()` ran,
causing cleanup to return a wrapped `net.ErrClosed`. The fix treats
expected transport close errors as successful cleanup.
Closes
[CODAGT-610](https://linear.app/codercom/issue/CODAGT-610/add-an-architecturemd-file-to-chatd).
Adds an ARCHITECTURE.md file which describes the architecture of the
chatd subsystem. It's meant for reading by both humans, who would like
to understand chatd better, and agents. It's an edited version of the
chatd stabilization RFC.
closes CODAGT-203
## Summary
`list_templates` now returns a ranked shortlist with a recommendation,
so the chat agent can pick the right template the way a colleague would:
prefer what matches the request, what the user already uses, and what
the rest of the organization uses. Instead of teaching the model an enum
protocol in prompts, every result carries a fixed `next_step`
instruction telling the agent what to do.
## How list_templates works
1. **Fetch**: active, non-deprecated templates in the chat's
organization, filtered by the admin template allowlist, authorized as
the chat owner (no system escalation).
2. **Query relevance** (optional `query` argument): each template
receives the highest tier any of its fields matches, and a higher tier
always outranks a lower one regardless of usage:
| Tier | Match |
|------|-------|
| 4 | name or display name equals the query |
| 3 | name or display name starts with the query |
| 2 | name or display name contains the query |
| 1 | description contains the query (checked only when no name field
matched) |
| 0 | no match; the template is excluded |
Matching is case-insensitive and ignores spaces/hyphens/underscores
(`python gpu` matches `python-gpu`).
3. **Usage signals**: a new `GetTemplateRankingSignalsByOwnerID` query
returns, per template, the owner's active and recently-deleted workspace
counts within a 60-day window, the last in-window usage, and the count
of distinct developers with an active workspace (unclaimed prebuilds
excluded).
4. **Affinity score** (computed in Go, per template, from that
template's signals only):
```text
affinity = 10 x (active + 0.5 x deleted) x 0.5^(days_since_last_use /
14)
+ ln(1 + active_developers)
```
`active`/`deleted` are the owner's in-window workspace counts,
`days_since_last_use` is measured from the most recent in-window usage
(the personal term is zero without in-window usage), and
`active_developers` is the org-wide count. Personal usage carries 10x
the weight of org popularity; the confidence floor is the score of two
active developers (`ln 3`) and the required lead over the runner-up is
`ln 3 - ln 2`.
5. **Rank**: query tier first (when a query is present), then affinity
score, then name/ID for determinism. Results paginate 10 per page with
`next_page` present only when more exist.
## Recommendation contract
The result tells the agent what to do next instead of describing
confidence levels:
- `recommended_template_id` is present only when the top template is a
clear winner: the only available template, a decisive query match, or an
affinity score that clears a floor and leads the runner-up by a derived
margin.
- `next_step` is always present and is one of four fixed sentences: use
the recommendation, ask the user to choose, retry a query that matched
nothing, or report that no templates are available.
Per-template items carry raw evidence (`active_developers`,
`your_workspace_count`, `last_used_by_you`) rather than derived labels.
When signals fail to load, the tool logs and degrades to asking the user
unless the query alone is decisive.
Prompts and the `create_workspace`/`read_template` descriptions
reference the field through the `chattool.NextStepField` constant, so
the instruction lives in one place and cannot drift. `create_workspace`
remains idempotent and allowlist-enforced.
## Authorization
The signals query runs with the chat owner's permissions: reading the
owner's own workspaces plus a template-metadata read for the cross-user
popularity count. dbauthz rejects the call if any requested template is
not readable by the owner (covered by allow and deny method tests).
## Docs
Adds `docs/ai-coder/agents/tools/` explaining how agent tool calls work,
with `list_templates` ranking and the `next_step` contract as the first
documented tools.
`TestInvalidateTemplatePrebuilds` assumed a stable ordering from preset
invalidation results, but the underlying `UPDATE ... RETURNING` query
does not guarantee row order. Local WIP schema changes were enough to
surface that latent flake when running tests.
Update the test to compare invalidated presets as a set instead of by
slice position. This keeps the behavior under test the same, while
removing dependence on unspecified database row ordering.
## Problem
`scripts/check_emdash.sh` is a diff gate for pull requests: it resolves
the merge-base against the target branch and only inspects added lines.
When it cannot resolve a base ref, it fell back to scanning **every
tracked file**.
Push builds on release branches hit exactly this case: the `lint` job
checks out with `fetch-depth: 1`, so `origin/main` is absent, and
`GITHUB_BASE_REF` is only set for `pull_request` events. With no base
ref, the whole-tree scan flags the many pre-existing emdash/endash
characters already in the repo and fails `make lint` (`lint/emdash`),
even though the build introduced none of them. Observed on
`release/2.34` CI (run
[27704528068](https://github.com/coder/coder/actions/runs/27704528068/job/81949529546)).
## Fix
When no base ref can be determined (i.e. outside a pull request), skip
the check instead of scanning the entire tree. A full scan remains
available on demand via `scripts/check_emdash.sh --all`.
## Testing
- **No base ref** (release-push simulation, no `GITHUB_BASE_REF`, no
`origin/main`): old script scans all files and fails on a pre-existing
emdash; new script skips and exits 0.
- **PR path** (diff vs merge-base): `OK: no emdash or endash characters
found.`
- **`--all`**: still scans the full tree (flags pre-existing characters
as before).
- `shellcheck` and `shfmt` clean.
## Backports
Backport PRs target `release/2.33` and `release/2.34` (same bug, older
script variant). `release/2.29` and `release/2.32` do **not** contain
`scripts/check_emdash.sh`, so there is nothing to backport there.
<details>
<summary>Decision log</summary>
Considered alternatives to the skip:
1. **Compare against `github.event.before`** on push events. Rejected:
the before-SHA is frequently unreachable in a `fetch-depth: 1` clone,
and wiring it in requires per-workflow env changes that complicate
backports.
2. **Fetch `origin/main` / deepen history** in the release lint job.
Rejected for the same backport-surface reason and because it only masks
the design intent.
The check exists to stop *new* emdashes from landing via PRs; that gate
already ran on the originating PRs. On non-PR builds there is no
meaningful diff base, so skipping is correct and self-contained in the
script (clean to backport). The explicit `--all` mode is preserved for
intentional full-tree audits.
</details>
---
Generated by Coder Agents on behalf of @f0ssel.
Implement the base template parameters wizard step, which renders a
configuration form for base template variables using the existing
`TemplateConfiguration` and `ConfigurationField` components.
- Map `TemplateBuilderModuleVariable` to `ConfigurationFieldDefinition`
(switch for bool, text input for string/number)
- Read variable definitions from the cached bases query
- Disable Continue button until all required non-sensitive variables
have values (`baseParametersComplete` helper)
- Step is automatically skipped when the selected base has no parameters
(e.g. Docker)
- Update `toComposeRequest` to include `base_variable_values` in the API
payload
Relates to [DEVEX-284](https://linear.app/codercom/issue/DEVEX-284).
> [!NOTE]
> This PR was authored with Coder Agents.
Closes
[DOCS-257](https://linear.app/codercom/issue/DOCS-257/b-vitest-validate-docs-literals-against-docsmanifestjson)
Extends `weekly-docs` with a new `audit-docs-paths` job that
cross-references TS/TSX `docs()` calls against
`coder.com/redirects.json` and fails when any path resolves via a
redirect (i.e. is stale). Also fixes two bugs in the existing
`check-docs` job:
- **Scheduled runs were a no-op** — `github-pr-review` reporter silently
exits 0 without a PR context. Now uses `local` reporter on schedule so
broken links actually fail the job.
- **Slack notification was broken** — payload used `"msg"` (invalid)
instead of `"text"` (the standard Slack webhook field).
Sample Slack output:

Safe to merge in any order relative to #25740 — the audit job checks for
the script and skips gracefully if not yet available.
---
> Generated by [Coder Agents](https://coder.com) on behalf of @bpmct.
Using `WaitGroup.Go` must be synchronized with `WaitGroup.Wait`
according to [go docs](https://pkg.go.dev/sync#WaitGroup.Go):
> If the WaitGroup is empty, Go must happen before a
[WaitGroup.Wait](https://pkg.go.dev/sync#WaitGroup.Wait).
There were a couple of places in chatd that violated this principle.
This was caught as a data race in
https://github.com/coder/internal/issues/1599. This PR ensures that all
functions that spawn inflight goroutines synchronize with each other.
I also noticed that inflight goroutines may be spawned after the server
is closed, which was surprising and looked like a bug. This PR therefore
also introduces a mechanism that disallows spawning inflight goroutines
after the server is closed, and ensures that any code that tries doing
it logs an error.
Closes https://github.com/coder/internal/issues/1599.
## Summary
Replaces 9 stale docs paths and 2 stale doc anchors in `site/src/`, and
adds a TS/TSX audit script (`site/scripts/audit-docs-paths.mjs`) plus
unit tests that scan the codebase for paths that resolve via
`coder.com/redirects.json`.
### Redirect-target updates (9)
Each of these paths' `/docs/...` source matches a Next.js redirect rule,
so requests today produce a 302 on `coder.com`. The audit script
identifies them by cross-referencing against `redirects.json`.
- Five update product-code references to `/ai-coder/ai-bridge` (renamed
to `/ai-coder/ai-gateway` in v2.33).
- One updates a commented-out reference to
`/templates#template-filtering` in `TemplatesFilter.tsx`.
- Three update notification-template mock data in
`testHelpers/entities.ts` that pointed at the renamed
`/docs/templates/schedule`.
### Anchor-only updates (2)
These paths are still live on `coder.com` (no redirect), but their
`#fragment` no longer matches a heading on the destination page.
Fragments are evaluated client-side and never sent to the server, so the
audit script does not catch them. Found and verified manually against
the current docs.
- `AuditFilter.tsx`: `/admin/security/audit-logs#filtering-logs` →
`/admin/security/audit-logs#how-to-filter-audit-logs`. The current
heading is `## How to Filter Audit Logs` in
[`docs/admin/security/audit-logs.md`](https://github.com/coder/coder/blob/main/docs/admin/security/audit-logs.md).
- `UserAuthSettingsPageView.tsx`: drops the stale `#openid-connect`
anchor; the path itself (`/admin/users/oidc-auth`) is unchanged. The
page H1 is now `# OpenID Connect`, so the bare path lands at the same
place the anchor used to.
None are user-visible label changes; only the doc target URLs change.
## Audit script
`site/scripts/audit-docs-paths.mjs` cross-references TS/TSX docs-URL
references against `coder.com/redirects.json` and reports anything that
resolves via a redirect (which means stale source). It catches four
forms:
- `docs("/...")` / `docs('/...')` / `` docs(`/...`) ``
- `` docs(`/.../${expr}/...`) `` (literal prefix, flagged as dynamic)
- `"https://coder.com/docs/..."` and other quoted forms
- `](https://coder.com/docs/...)` and `](/docs/...)` markdown-link forms
The full audit (26 findings: 9 in `coder/coder/site/`, 17 in
`coder/coder.com/src/`) lives in [DOCS-253 on
Linear](https://linear.app/codercom/issue/DOCS-253) rather than being
committed to the repo. Re-run locally with:
```bash
node site/scripts/audit-docs-paths.mjs \
--redirects=/path/to/coder.com/redirects.json \
--roots=/path/to/coder/site/src,/path/to/coder.com/src
```
Default output goes to `docs/.audit/redirects-audit-YYYY-MM-DD.md`,
which is gitignored.
## Tests
`site/scripts/audit-docs-paths.test.mjs` has 59 cases covering the four
regexes, `matchRedirect` (exact, `:path*`, `:slug(.*)`, miss),
`findMatchingRedirect`, `stripQueryAndFragment`, `literalPrefix`,
`extractReferences` end-to-end with line numbers and multi-line `docs()`
calls, `buildReport` (empty input, repo grouping and sort order,
fragment annotation, unclassified section), `walk` against a real temp
filesystem (recursion, extension filter, `SKIP_DIRS` pruning,
missing/file inputs, seeded results), and `runCli` (missing-root
warning, real-but-empty root). Run with `pnpm exec vitest run
scripts/audit-docs-paths.test.mjs --project=unit` from `site/`.
## Notes
- User-visible "AI Bridge" label text is not changed here. Renaming the
product surface from "AI Bridge" to "AI Gateway" is tracked separately
by the AI Governance team in AIGOV-233.
- A vitest assertion that no literal path in `site/src/` resolves via a
redirect will land in a follow-up PR (DOCS-257), so future drift fails
fast in CI.
- A generated `DocsPath` type for the `docs()` helper is planned in
DOCS-254.
- The `/docs/templates/schedule` drift in `entities.ts` also appears in
`coderd/notifications/testdata/*.golden` test fixtures and in historical
SQL migrations under `coderd/database/migrations/`. Those are tracked
under DOCS-256 (A2: non-TS audit) and not in scope here.
## Related work
- Linear: DOCS-253 (this PR), parent DOCS-209.
- Companion redirect rule on the coder.com side: coder/coder.com#826.
- Companion fixes for the 17 coder.com findings: coder/coder.com#876
(supersedes the closedcoder/coder.com#827, which was made redundant by
coder/coder.com#832).
<details>
<summary>Implementation plan (Linear DOCS-209)</summary>
| Phase | Scope | Linear | Status |
|---|---|---|---|
| D | Versioned redirect for `/docs/@v2.33.x/ai-coder/ai-bridge` in
`coder.com/redirects.json` | DOCS-255 | coder/coder.com#826 open |
| A1 | TS/TSX audit + autofix in `coder/coder/site/` | DOCS-253 | This
PR |
| A1 follow-up | Same autofix in `coder/coder.com/src/` (3 remaining
findings after coder/coder.com#832) | DOCS-281 | coder/coder.com#876
open |
| A2 | Non-TS audit in `coder/coder` (Go, comments, markdown) | DOCS-256
| Backlog |
| A3 | code-server audit | DOCS-252 | Backlog |
| B | vitest assertion against `redirects.json` | DOCS-257 | Blocked by
A1 |
| C | Generated `DocsPath` type | DOCS-254 | Blocked by B |
</details>
---
Generated by Coder Agent on behalf of @nickvigilante.
---------
Co-authored-by: Coder <coder@users.noreply.github.com>
Implement the first wizard step for selecting a base infrastructure
template.
- Add `getTemplateBuilderBases` API client method and react-query
wrapper
- Render a responsive grid of `TemplateCard` components fetched from
`GET /api/v2/templatebuilder/bases`
- Map `TemplateBuilderBase` to `SelectedBaseMeta` with `hasParameters`
derived from the base variables
- Single-choice selection that dispatches `SET_BASE` to wizard state,
preserving selection on back navigation
Relates to [DEVEX-283](https://linear.app/codercom/issue/DEVEX-283).
> [!NOTE]
> This PR was authored with Coder Agents.
Adds
diagram showing how AI Bridge Proxy works in tunnel and MITM modes.
diagram showing how AI Bridge Proxy integrates with upstream proxies.
Extends Troubleshooting section.
Adds a registry link for the AI Bridge Proxy module for Coder
workspaces.
`GetAuditLogsOffset` orders by `"time" DESC` with no tiebreaker, and
`dbtime.Now` rounds to microseconds, so two audit logs emitted in quick
succession (especially on platforms with coarser clock resolution like
Windows) can land in the same microsecond and Postgres is free to return
them in either order. The test then assumes positional ordering and
breaks.
Sort `rows` by `Action` descending before indexing so `rows[0]` is the
update log and `rows[1]` is the create log regardless of timestamp
collisions.
Closes CODAGT-585
Closes https://github.com/coder/internal/issues/1551
Make `newInternalTestServer` use option functions for logger, clock, and
worker startup, and make it passive by default so internal chatd tests
only opt into background execution when they need a real worker.
Use the passive server path in `TestAwaitSubagentCompletion` for the
state-driven subtests, keep `ContextCanceled` explicitly active for real
provider cancellation coverage, and keep the fail-fast default AI
provider base URL so accidental provider calls still fail immediately.
Closes CODAGT-586
Closes https://github.com/coder/internal/issues/1549
Follow-up to #26443. Documents the new `coder exp sync list` command in
the startup coordination guides.
**troubleshooting.md:**
- New "List All Units" section after "Check Unit Status" with example
output
- Added `coder exp sync list` to the "Workspace startup script hangs"
checklist, since users debugging hanging scripts may not know which unit
to query
**usage.md:**
- New "Inspect Unit State" section covering `list`, `status`, and `ping`
- Updated "Test your changes" checklist to reference `coder exp sync
list`
> Generated by Coder Agents on behalf of @SasSwart
Adds a private contributor-tooling directory at `docs/.style/` that will
host the canonical prose style guide and the custom Vale rules used to
enforce it. The directory's contents do not deploy to `coder.com/docs`.
This PR is the scaffold only. The Vale configuration, the rule set, and
the per-rule style-guide sections all land in follow-up PRs.
## What changes
- New `docs/.style/` directory with:
- `README.md` explaining the convention
- `style-guide.md` as a table-of-contents scaffold
- `styles/Coder/README.md` placeholder so Git tracks the empty Vale
rules dir
- `.github/workflows/deploy-docs.yaml`: skip the workflow on
`.style`-only pushes, and exclude `.style` paths from the
surgical-reindex git diff on mixed commits. Defense-in-depth on top of
the manifest-driven coder.com routing.
- `.github/.linkspector.yml`: add `docs/.style` to `excludedDirs`
- `AGENTS.md` and `.claude/docs/DOCS_STYLE_GUIDE.md`: cross-link to the
new style guide for agents
## Verification
- `make pre-commit-light` clean (`fmt/markdown`, `lint/markdown`,
`lint/typos`, `lint/emdash`, `lint/actions/actionlint`,
`lint/shellcheck`).
- `markdown-table-formatter --check` and `markdownlint-cli2` both
process the new files (existing globs are `find docs -name '*.md'`).
- `actionlint` clean on the modified workflow.
- coder.com exclusion works because route discovery and Algolia indexing
are manifest-driven; this directory is not in `docs/manifest.json`. The
workflow changes are defense in depth.
<details>
<summary>Implementation plan and decision log</summary>
### Decisions
- **Location**: `docs/.style/` (leading dot, mirrors `.github/`,
`.vscode/`, `.claude/`). Vale's `StylesPath` will be
`docs/.style/styles/`; `.vale.ini` lands at repo root in a follow-up.
- **Existing public page `docs/about/contributing/documentation.md`**:
untouched in this PR. Nick's separate information-architecture rework
will redirect it to GitHub at the right time.
- **Placeholder for empty `styles/Coder/`**: real `README.md`, not
`.gitkeep`. Discoverable on GitHub, lints with the existing tooling,
lists the planned starter rules.
- **CONTRIBUTING.md**: not touched. It's a 2-line redirect to
`coder.com/docs/CONTRIBUTING`; bloating it would defeat the redirect.
- **`.claude/docs/DOCS_STYLE_GUIDE.md`**: kept as the structure/research
companion. A blockquote at the top points at the new canonical prose
guide.
### coder.com exclusion mechanism (verified by inspection)
Direct inspection of `coder/coder.com`:
- Route discovery in
[`src/utils/docs/docs.ts`](https://github.com/coder/coder.com/blob/master/src/utils/docs/docs.ts)
iterates `routes` from `docs/manifest.json`. Files not in the manifest
never become routes.
- The Algolia surgical indexer at
[`src/utils/algoliaDocs/surgical.ts`](https://github.com/coder/coder.com/blob/master/src/utils/algoliaDocs/surgical.ts)
explicitly skips paths not in the manifest, incrementing `pathsSkipped`.
Net result: not adding anything from `docs/.style/` to `manifest.json`
is the only thing that has to be true for the exclusion to work. The
`deploy-docs.yaml` tweaks are defense in depth.
### deploy-docs.yaml changes (pre-mortem)
1. Trigger path negation `!docs/.style/**` skips the workflow on
`.style`-only pushes. GitHub Actions only suppresses when every changed
file matches a negation, so mixed commits still trigger.
2. The git-diff pathspec `:(exclude)docs/.style/**` drops `.style` paths
from the surgical-reindex payload on mixed commits.
Risks considered:
- **Test contract**: `.github/workflows/test-deploy-docs-diff.sh` only
exercises the downstream awk parser, not the git-diff invocation. The
exclusion happens at git-diff time; the parser sees the same
`<status>\0<path>\0` format. No test change needed.
- **First push to a brand-new branch**: the workflow falls back to
whole-branch reindex when `BEFORE_SHA` is all zeros. Whole-branch
reindex re-extracts records from the manifest, which still excludes
`.style` files because they are not in the manifest.
- **Workflow-dispatch**: takes the whole-branch path; same reasoning.
Safe.
### Why a real README in `styles/Coder/` instead of `.gitkeep`
It explains intent, lists the upcoming rules, and lints with the
existing tooling. The cost is one extra Markdown file; the upside is
that a contributor browsing GitHub sees the plan without clicking
around.
</details>
---
*Filed via [Coder Agents](https://coder.com/docs/ai-coder/agents) on
Nick's behalf.*
Linear: DOCS-180
Removes the coder agents PR Insights page (`/agents/settings/insights`) and all of its backend support. The page had previously been hidden and was only reachable via deep link. It had previously been hidden due to the dubious value provided in the current iteration.
> AI Tools where used in this request.
Registers `coder_aibridged_*` and `coder_aibridgeproxyd_*` metrics under
new prefixes: `coder_ai_gateway_*` and `coder_ai_gateway_proxy_*`.
Old prefix is still exported. Will be removed in later release.
Also updated the `metricsdocgen` static fixture. Added 4
previously-undocumented metrics `key_pool_state`,
`key_pool_state_transitions_total`, `key_pool_exhaustions_total`,
`key_pool_failover_attempts` added the `client` label to the existing
interception, prompt, and token counter samples.
Updated AI Gateway documentation.
Add a new subcommand to list all registered sync units and their current
statuses. This provides a quick overview of the dependency coordination
state in a workspace without needing to query each unit individually.
The command supports both table (default) and JSON output formats.
```
$ coder exp sync list
UNIT STATUS READY
unit-a started true
unit-b completed true
unit-c pending false
$ coder exp sync list --output json
[
{
"unit_name": "my-unit",
"status": "started",
"is_ready": true
}
]
```
When no units are registered, the command prints `No units registered`.
<details><summary>Changes across layers</summary>
- `agent/unit`: add `Manager.ListUnits()` method
- `agent/agentsocket/proto`: add `SyncList` RPC, bump API to v1.2
- `agent/agentsocket`: add service and client implementations
- `cli`: add `sync_list.go` command, register in `sync.go`
- Tests: three golden-file test cases (empty list, multiple units, JSON)
</details>
> Generated by Coder Agents on behalf of @SasSwart
---------
Co-authored-by: Cian Johnston <cian@coder.com>
Add an AI budget section to the group settings page, gated by the
aibridge feature and the ai-gateway-cost-control experiment. Saves a
per-member monthly budget via the group AI budget endpoints alongside
the group patch: empty is uncapped (deletes the budget), 0 disables,
and any value >= 0 is accepted.
Closes AIGOV-294
This PR adds logging when the chat runner retries and exits because of
an error. It also adds a 15-minute task timeout to ensure that stuck
tasks do not hang forever.