mirror of
https://github.com/coder/coder.git
synced 2026-11-02 21:50:08 +08:00
f5e051918011c2f8bbc3162cd476354e22be3fcf
15622
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
f5e0519180 |
chore: bump github.com/aws/aws-sdk-go-v2/service/sts from 1.44.0 to 1.45.3 (#27831)
Bumps [github.com/aws/aws-sdk-go-v2/service/sts](https://github.com/aws/aws-sdk-go-v2) from 1.44.0 to 1.45.3. <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/aws/aws-sdk-go-v2/commit/4aeeb0d7a4293f0b31c2e0be83e65da6f7fd4ae2"><code>4aeeb0d</code></a> Release 2023-11-28.2</li> <li><a href="https://github.com/aws/aws-sdk-go-v2/commit/e09e153704d4da6ae2bb0ae3875058950d31206b"><code>e09e153</code></a> Regenerated Clients</li> <li><a href="https://github.com/aws/aws-sdk-go-v2/commit/8293e2ca285a7333a9931a0a3b37b2bcf7cdc05d"><code>8293e2c</code></a> Update endpoints model</li> <li><a href="https://github.com/aws/aws-sdk-go-v2/commit/713fb0f31a188015915d785556ebf16ddb08085a"><code>713fb0f</code></a> Update API model</li> <li><a href="https://github.com/aws/aws-sdk-go-v2/commit/830202d722c904c7e3da40e8dde7b9338d08752c"><code>830202d</code></a> Merge customizations for service s3</li> <li><a href="https://github.com/aws/aws-sdk-go-v2/commit/2de0027dc478a6ae80e9f2d24d904a425169a23b"><code>2de0027</code></a> Release 2023-11-28</li> <li><a href="https://github.com/aws/aws-sdk-go-v2/commit/f0c890c5eaf354ff23feb727ded9f50aaee9f1c4"><code>f0c890c</code></a> Regenerated Clients</li> <li><a href="https://github.com/aws/aws-sdk-go-v2/commit/e032d9ea8d98d366f2467a72834d2cc0ee865edd"><code>e032d9e</code></a> Update endpoints model</li> <li><a href="https://github.com/aws/aws-sdk-go-v2/commit/507661ff1edbc896fbdfe3ea2e4c2e74be3b4e3c"><code>507661f</code></a> Update API model</li> <li><a href="https://github.com/aws/aws-sdk-go-v2/commit/4128360684a451476e33c0f979921bc46ff63656"><code>4128360</code></a> fix: respect functional option modifications to RetryMaxAttempts (<a href="https://redirect.github.com/aws/aws-sdk-go-v2/issues/2390">#2390</a>)</li> <li>Additional commits viewable in <a href="https://github.com/aws/aws-sdk-go-v2/compare/service/s3/v1.44.0...service/iot/v1.45.3">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> |
||
|
|
11b38272f6 |
chore(docs): update release docs for v2.36.0 (#27828)
Automated docs update for v2.36.0 release. Created by `releasetui`. |
||
|
|
6b8f820493 |
feat: remove native chat cost tracking in favor of AI Gateway cost data (#27330)
## Stack Context This stack makes AI Gateway data and budgets the source of truth for AI spend controls. 1. Re-back the per-chat cost endpoint with AI Gateway data (#27328, merged). 2. Remove native chat usage limits (#27329, merged). 3. **This PR, now based on `main`:** remove native chat cost tracking and its dedicated admin UI. ## Summary Removes native per-message price calculation, model pricing fields, cost persistence, aggregate cost queries, and admin cost API types. It also deletes the Analytics and Spend pages plus their legacy redirects. The AI Gateway-backed per-chat cost row and compact budget indicators remain. The spend documentation is renamed to `spend-management.md` and updated for the remaining surfaces, group budget APIs, CSV export, upgrade handling for native pricing and cost history, and the absence of a deployment-wide spend dashboard. The per-chat cost API documents that data follows AI Gateway retention and reports zero after all matching requests are purged. No schema is dropped in this release. `chat_messages.total_cost_micros` remains nullable and unwritten so replicas from the previous release can continue inserting messages during rolling upgrades. #27600 tracks removal after the compatibility window. > Mux prepared this PR on Mike's behalf. |
||
|
|
0b8b48913f |
fix: fix port assignment race from aigatewaystart_internal_test (#27801)
The AI Gateway tests reserved a port with `testutil.RandomPort`, which binds and closes `127.0.0.1:0`, then bound it later in `serve`. If something took the port in between, `serve` failed at `net.Listen` and the error went unread, leaving only a 10s `Eventually` timeout. The gateway now exposes its bound address via `httpAddr` and a `listenerReady` latch, so tests listen on port 0 and read the address afterwards. `requireListening` reports `serve`'s error where an address must still be fixed upfront. --- Investigated and authored with Coder Agents. --------- Co-authored-by: Cian Johnston <cian@coder.com> |
||
|
|
f0e6ac64b3 |
feat: remove native chat usage limits in favor of AI Gateway budgets (#27329)
## Stack Context This stack makes AI Gateway data and budgets the source of truth for AI spend controls. 1. Re-back the per-chat cost endpoint with AI Gateway data (#27328, merged). 2. **This PR:** remove native chat usage limits. 3. Remove native chat cost tracking and its dedicated admin UI (#27330). ## Summary Removes the native usage-limit API, SDK types, SQL, and chat enforcement for deployment, user, and group chat limits. Compact AI Gateway budget indicators remain in the Agents sidebar, user menu, and group settings. Gateway budget rejections and provider quota failures continue to classify as usage-limit errors, including a 409 response for synchronous title generation. Budget-period labels now use the API's UTC boundaries, so users see the same dates in every browser timezone. The documentation explains the AI Gateway replacement, its licensing requirements, and the differences from native limits. No schema is dropped in this release. The usage-limit table, index, user and group columns, constraints, audit mappings, and generated scan fields remain for mixed-version rolling upgrades. #27600 tracks their removal after the compatibility window. ## Breaking change Native day, week, and month chat spend limits are removed and are not migrated. AI Gateway budgets are month-based, group-scoped with per-user overrides, and require the AI Gateway entitlement. Deployments without that entitlement no longer have chat spend enforcement. > Mux prepared this PR on Mike's behalf. |
||
|
|
a2287d6739 |
chore: fix port availability check flake from aigatewaystart_internal… (#27784)
Extends state tracking in `standaloneGateway` which is used in tests to simplify checks. Flaky `requireListenerAvailable` was removed. |
||
|
|
404bb2f663 |
refactor(coderd/x/chatd): own provider option construction in one function (#27705)
Stacked on #27704. Provider option conversion and reasoning effort injection both create OpenAI option structs, so each of the four call sites had to pair them in the right order and pick the same transport for each. `chatprovider.ProviderOptionsForCall` now owns both steps, and the two helpers it wraps are unexported. The advisor, main generation, compaction override, and quickgen paths each collapse to one call. The ARCHITECTURE section on transport selection is updated to match: `ProviderOptionsForCall` is described as the only entry point in `chatprovider` that builds provider options for a call, delegating OpenAI conversion to `chatopenai.ProviderOptionsFromChatConfig`, and it now records that the quickgen turn status label and chat summary paths deliberately send no provider options. > Mux prepared this PR on Mike's behalf. |
||
|
|
0e16e356b0 |
refactor(coderd/x/chatd): read the OpenAI transport from the model (#27704)
Stacked on #27703. Provider option conversion, reasoning effort injection, and file part acceptance each recomputed the OpenAI wire format from `(provider, modelID, override)`. They now read it from `chatprovider.Model`, so a decision cannot drift from the client it was built for. `ProviderOptionsFromChatConfig` takes a `Transport`, `ApplyReasoningEffort` takes a `Model`, and `AcceptsFilePartMediaType` becomes a `Model` method. `UsesResponsesAPI` and `UsesResponsesOptions` are deleted. The override extraction is unexported and reachable only from `ModelFromConfig`, which now takes the model's `ChatModelOpenAIConfig` directly, removing the six scattered extractions at call sites. That also resolves the computer-use mismatch. The computer-use model is a hardcoded default with no config row of its own: its client was built without an override while request preparation applied the chat model's. Preparation now reads the computer-use model's own transport, so the two agree without one model's client settings following a different model. Passing the chat model's `openai_config` into the computer-use client would have made them agree on the wrong value. `TestModelTransportConsumersAgree` pins the invariant in one test: the HTTP path the client actually hits, the concrete provider option struct type, the type created by reasoning effort, and text/image file acceptance. > Mux prepared this PR on Mike's behalf. |
||
|
|
b5c9e8e471 |
refactor(coderd/x/chatd): carry the resolved OpenAI transport on a model wrapper (#27703)
Stacked on #27683. The OpenAI wire format is decided when the client is built, then thrown away, so downstream sites recompute it from `(provider, modelID, override)`. Any disagreement fails silently: the SDK type-asserts the concrete provider options struct and discards every OpenAI option, and text attachments are dropped because Responses natively accepts only images and PDFs. This adds `chatprovider.Model`, which pairs a fantasy client with the transport resolved from that client's own identity. Its fields are unexported and only the constructor sets the transport, deriving it from the client, so no caller can pick a transport that disagrees with the client it wraps. `chatopenai.Transport`'s zero value is invalid and panics when read rather than defaulting to a wire format, following the existing precedent for construction invariants. `Model` is threaded through construction, the resolve paths, and the four struct fields that store a model for later request preparation. Terminal call sites keep taking `fantasy.LanguageModel` and receive `LanguageModel()`, which avoids a new package edge from `chatloop` and `chatadvisor` into `chatprovider`. No decisions move yet. The consumers still recompute the transport, and `UsesResponsesAPI` now delegates to `TransportFor` so the two agree by construction. #27704 makes the consumers read it from the model. > Mux prepared this PR on Mike's behalf. |
||
|
|
c6cee10e8b |
feat: add per-model OpenAI Responses API toggle (#27683)
chatd hardcoded `WithUseResponsesAPI()`, so the provider SDK's static known-model list decided whether an OpenAI model spoke the Responses API or Chat Completions. A model absent from that list silently fell back to Chat Completions until the fantasy fork was patched. This exposes the SDK's `WithResponsesAPIFunc` hook as a per-model setting, `openai_config.use_responses_api`, stored in the existing `chat_model_configs.options` JSONB. Unset keeps the known-model list, `true` forces Responses, `false` forces Chat Completions. There is no migration. It sits in a new construction-time `openai_config` section rather than in `provider_options.openai` because it selects the API when the client is built, while `provider_options` holds per-request parameters. That placement is also load-bearing: a config setting only this field would otherwise materialize an OpenAI request-options struct and turn on provider-side response storage, since `Store` defaults to true there. Three places independently decided the transport and would silently disagree with the client actually built: | Site | Effect when it disagrees | | --- | --- | | `ModelFromConfig` | the transport being overridden | | `AcceptsFilePartMediaType` | text attachments dropped, since Responses natively accepts only images and PDFs | | `UsesResponsesOptions` | the SDK type-asserts the concrete options struct, so every OpenAI option is discarded | They share one predicate here, `chatopenai.UsesResponsesAPI`, with the override threaded to each. The rest of the stack removes that threading by resolving the transport once and carrying it. Compaction overrides and the quickgen debug model built clients without `ConfigOptions`, so they now pass it and pick up both this setting and the existing Anthropic beta headers. The toggle also makes transport-conditional option handling admin-switchable, so two hardening changes ride along. `ServiceTierFromChat` now maps every tier the codersdk enum advertises (`auto`, `default`, `flex`, `scale`, `priority`); it previously returned nil for `default` and `scale`, so flipping a model to Responses silently dropped a configured `service_tier` that the API accepts (fantasy forwards the value unchanged). And a new `TestProviderOptionsTransportParity` pins, per `provider_options.openai` field, which transport honors it, against a table in ARCHITECTURE.md, so a field honored on one transport and silently ignored on the other fails the test unless recorded as intentional. Review rounds also caught two lifecycle gaps around the new field. `isZeroChatModelCallConfig` now inspects `OpenAIConfig`, so a stored options blob whose only setting is this toggle survives into GET/list responses instead of reading as `model_config: null`; `TestIsZeroChatModelCallConfigCoversEveryField` sets each config field in isolation and fails if any field is invisible to the zero check. And the model editor's update path sends an explicit empty `model_config` when an edit clears the last field, since an omitted property preserves the stored options server-side; covered by the `EditClearingLastOptionSendsEmptyConfig` story. Azure keeps following the known-model list, because the Azure provider exposes no equivalent hook. The model editor renders Azure with the OpenAI option schema, so instead of shipping a visible but inert control, the option schema generator gains a `providers` struct tag that it emits as `visible_for_providers`. Gating uses the raw provider type rather than the alias table, so the control appears only for openai-typed providers. No hand-written frontend field: the editor renders it from the generated schema. Closes https://linear.app/codercom/issue/CODAGT-874/add-completionsresponses-api-toggle-in-model-editor > Mux prepared this PR on Mike's behalf. |
||
|
|
84cdc17602 |
docs: bump AI Gateway client modules to latest and remove Coder Tasks refs (#27810)
Updates the AI Gateway client docs under `docs/ai-coder/ai-gateway/clients/` to reference the latest module versions on registry.coder.com and removes mentions of Coder Tasks from those pages. Uses pessimistic (`~>`) version constraints so users automatically pick up compatible non-breaking updates within the same major. ### Version bumps | Module | Old | New | |---|---|---| | [`coder/claude-code`](https://registry.coder.com/modules/coder/claude-code) | `4.7.3` | `~> 5.2` (latest `5.2.0`) | | [`coder-labs/codex`](https://registry.coder.com/modules/coder-labs/codex) | `~> 5.0` | `~> 5.3` (latest `5.3.0`) | | [`coder/mux`](https://registry.coder.com/modules/coder/mux) | `~> 1.0` | `~> 1.5` (latest `1.5.0`) | ### Coder Tasks removal - `claude-code.md`: removed the *Coder Tasks* example block (v5 of the module is a major refactor that dropped the `ai_prompt` input and `task_app_id` output). - Remaining inputs in the example (`agent_id`, `workdir`, `enable_ai_gateway`) still match the latest module. - `codex.md` and `mux.md` did not reference Tasks, so only version bumps were needed there. cc @matifali --------- Co-authored-by: blink-so[bot] <211532188+blink-so[bot]@users.noreply.github.com> |
||
|
|
9bab7a8e06 | fix(coderd): preseed AI providers for title tests (#27564) | ||
|
|
a60f393773 |
feat(site): show network calls list on AI session detail (#27426)
Renders the per-call network calls as a collapsible panel above the session threads, using the `network_call_logs` field added to the threads API. Each row shows the request method, allowed/blocked status, URL, and timestamp, and expands to show the protocol, matched rule, and full detail. The header shows the total count and a blocked-count badge from the session summary; when the server caps the list, the panel notes how many calls are shown. The panel is omitted when the session did not pass through Agent Firewall. ### PR map (merge strictly bottom-up) This change is a 4-PR stack. Each PR depends on all the ones below it, so merge in this exact order: 1. #27417 — backend network summary (base `main`) 2. #27418 — frontend summary rows (base #27417) 3. #27425 — backend per-call list `network_call_logs` (base #27418) 4. #27426 — frontend network-calls panel (base #27425) Refs AIGOV-464 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
a779320d87 |
feat(scaletest): llm-mock tool calls and paced streaming (#26850)
`coder exp scaletest llm-mock` used to return only canned text, so Coder Agents scaletests pointed at it never exercised the path that matters most: the agentic tool-call loop where the model asks for a tool, the workspace runs it, the result is fed back, and the model is re-prompted before it finally answers. This teaches the mock to reproduce that loop deterministically so scaletests actually drive real tool execution and hold streams open the way a live model would. It adds `--tool-calls-per-turn` and `--tool-call-command` so the OpenAI Chat Completions endpoint emits a controllable number of `execute` tool calls per turn (only when the request advertises an `execute` tool, otherwise it falls back to text, so it stays a safe drop-in). It also adds paced streaming with `--min-stream-duration`/`--max-stream-duration` (randomized per response) and `--response-payload-size`, so runs can simulate slow or long-lived responses instead of flushing everything at once. Closes CODAGT-307 Closes GRU-48 |
||
|
|
1f946bb50b |
ci(.github): retry Go module downloads (#27816)
Closes CODAGT-878 Closes https://github.com/coder/internal/issues/1621 The Go module proxy failure in the linked issue is outside our control, but `lint` and `test-e2e` currently download modules during their `make` targets without any retry. Other CI jobs already wrap `go mod download` with `./.github/scripts/retry.sh`, so this just copies that precedent to both affected jobs. |
||
|
|
cce5a21933 |
chore(site): remove unused SelectMenu component (#27800)
`SelectMenu` was only referenced by its Storybook story. Nothing in the app consumed it. Delete the component and story so we drop the unused MUI `MenuItem` / `MenuList` wrapper and avoid treating dead demui code as a shared primitive. |
||
|
|
d899f02e42 |
chore: bump terraform-google-modules/container-vm/google from 3.2.0 to 3.3.0 in /examples/templates/gcp-vm-container (#27813)
Bumps [terraform-google-modules/container-vm/google](https://github.com/terraform-google-modules/terraform-google-container-vm) from 3.2.0 to 3.3.0. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/terraform-google-modules/terraform-google-container-vm/releases">terraform-google-modules/container-vm/google's releases</a>.</em></p> <blockquote> <h2>v3.3.0</h2> <h2><a href="https://github.com/terraform-google-modules/terraform-google-container-vm/compare/v3.2.0...v3.3.0">3.3.0</a> (2026-03-06)</h2> <h3>Features</h3> <ul> <li><strong>deps:</strong> Update Terraform Google Provider to v7 (major) (<a href="https://redirect.github.com/terraform-google-modules/terraform-google-container-vm/issues/151">#151</a>) (<a href="https://github.com/terraform-google-modules/terraform-google-container-vm/commit/25aeb5e8eb9525fdac47cfeb79eb7b20295d9a10">25aeb5e</a>)</li> </ul> <h3>Bug Fixes</h3> <ul> <li><strong>TF>=1.3:</strong> Update cft/developer-tools Docker tag to v1.25 (<a href="https://redirect.github.com/terraform-google-modules/terraform-google-container-vm/issues/145">#145</a>) (<a href="https://github.com/terraform-google-modules/terraform-google-container-vm/commit/49bb1a86c908b8a626b78977d83b1c01f3b5e39b">49bb1a8</a>)</li> </ul> </blockquote> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/terraform-google-modules/terraform-google-container-vm/blob/main/CHANGELOG.md">terraform-google-modules/container-vm/google's changelog</a>.</em></p> <blockquote> <h2><a href="https://github.com/terraform-google-modules/terraform-google-container-vm/compare/v3.2.0...v3.3.0">3.3.0</a> (2026-03-06)</h2> <h3>Features</h3> <ul> <li><strong>deps:</strong> Update Terraform Google Provider to v7 (major) (<a href="https://redirect.github.com/terraform-google-modules/terraform-google-container-vm/issues/151">#151</a>) (<a href="https://github.com/terraform-google-modules/terraform-google-container-vm/commit/25aeb5e8eb9525fdac47cfeb79eb7b20295d9a10">25aeb5e</a>)</li> </ul> <h3>Bug Fixes</h3> <ul> <li><strong>TF>=1.3:</strong> Update cft/developer-tools Docker tag to v1.25 (<a href="https://redirect.github.com/terraform-google-modules/terraform-google-container-vm/issues/145">#145</a>) (<a href="https://github.com/terraform-google-modules/terraform-google-container-vm/commit/49bb1a86c908b8a626b78977d83b1c01f3b5e39b">49bb1a8</a>)</li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/terraform-google-modules/terraform-google-container-vm/commit/ab943364f6add0de89de1d3c79ca23e6852e5a12"><code>ab94336</code></a> chore(main): release 3.3.0 (<a href="https://redirect.github.com/terraform-google-modules/terraform-google-container-vm/issues/152">#152</a>)</li> <li><a href="https://github.com/terraform-google-modules/terraform-google-container-vm/commit/10d2424042cc02a4d3efff00db9b33d7e48f4452"><code>10d2424</code></a> chore: Migrate gsutil usage to gcloud storage (<a href="https://redirect.github.com/terraform-google-modules/terraform-google-container-vm/issues/159">#159</a>)</li> <li><a href="https://github.com/terraform-google-modules/terraform-google-container-vm/commit/f15666132398848678ba5fd299d7ff36cb46be67"><code>f156661</code></a> chore: update .github/workflows/lint.yaml</li> <li><a href="https://github.com/terraform-google-modules/terraform-google-container-vm/commit/1a9a202d61c2663333d943bc08badaef427aa2c4"><code>1a9a202</code></a> chore: update .github/workflows/lint.yaml</li> <li><a href="https://github.com/terraform-google-modules/terraform-google-container-vm/commit/9c708636cc9655b257b374773ea0068813fb736c"><code>9c70863</code></a> chore: update .github/workflows/stale.yml</li> <li><a href="https://github.com/terraform-google-modules/terraform-google-container-vm/commit/df594b380ee57cb246d02c188d0189ecdda04c0f"><code>df594b3</code></a> chore: update .github/workflows/stale.yml</li> <li><a href="https://github.com/terraform-google-modules/terraform-google-container-vm/commit/83a8daf400c4980e28e800b2b28f0c9690ce962c"><code>83a8daf</code></a> chore: update .github/workflows/lint.yaml</li> <li><a href="https://github.com/terraform-google-modules/terraform-google-container-vm/commit/ab4a4eb85321af9db8b0ebfd64d80925441c0931"><code>ab4a4eb</code></a> chore: update .github/workflows/lint.yaml</li> <li><a href="https://github.com/terraform-google-modules/terraform-google-container-vm/commit/604cf93040d5cc3b5e671c6eff9b7e1db4c0d60b"><code>604cf93</code></a> chore: update .github/workflows/lint.yaml</li> <li><a href="https://github.com/terraform-google-modules/terraform-google-container-vm/commit/4016c71b40be077379b38cf549c04382ceee3d82"><code>4016c71</code></a> chore: update .github/workflows/lint.yaml</li> <li>Additional commits viewable in <a href="https://github.com/terraform-google-modules/terraform-google-container-vm/compare/v3.2.0...v3.3.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> |
||
|
|
52423eb87b |
feat: promote MinimumImplicitMember experiment to GA (#27472)
Promotes the `minimum-implicit-member` experiment to GA and removes it.
## What changes
- The `minimum-implicit-member` experiment constant, its
`RoleOptions.MinimumImplicitMember` toggle, and the global
`rbac.MinimumImplicitMember()` accessor are deleted. The minimal-member
behavior is now the only behavior: `organization-member` and
`organization-service-account` carry only the floor (read-self records,
notifications, and similar) and grant **no workspace permissions**.
Workspace access lives exclusively on the
`organization-workspace-access` role.
- The experiment gate on customizing `default_org_member_roles` (`PATCH
/organizations/{org}`) is removed; the built-in-roles-only validation
remains.
- The dashboard's Default Roles section and the implied-roles display on
the members page are no longer experiment-gated.
- Admin docs: new "Default member roles" section in
`docs/admin/users/organizations.md`, cross-linked from
`groups-roles.md`.
## Why this is safe for existing deployments
Migration `000516` (shipped earlier) backfilled
`default_org_member_roles` with `['organization-workspace-access']` on
every organization. Members therefore keep exactly the effective
permissions they had with the experiment off; the workspace elevation
flows through the default role instead of being baked into
`organization-member`.
**Rollback caveat:** rolling back past this release restores the bundled
elevation, silently re-granting workspace access to members of
organizations that cleared their default roles.
## Review
Deep-review R1 findings are addressed in `chore: address deep-review
findings` (copy fixes, read-only Default Roles for viewers, removable
overlapping explicit grants, RBAC prose restoration, test
de-tautologizing, docs). Point-by-point disposition is in the PR
comments.
---
Generated by Coder Agents on behalf of @Emyrk.
|
||
|
|
fe4a73f8ab |
docs: note user secret target uniqueness and delivery boundary (#27532)
Two gaps in the user secrets guide, both independent of any pending secrets work. ## Changes **Target uniqueness (`Update a secret`, `File secrets`).** The guide teaches `coder secret update api-key --file ""` but never states that env names and file paths are unique per user, so the conflict you get when setting a target back has no explanation in the docs. The `File secrets` paragraph also read as though duplicate file paths are tolerated with a warning; an identical path is rejected outright, while two *different* paths resolving to the same location are both accepted and produce the agent warning. Both indexes are partial (`WHERE env_name != ''` / `WHERE file_path != ''`), so a cleared target really is released for another secret to claim — now stated. **Delivery boundary (`How your secrets reach a workspace`).** Coder controls where a secret is delivered, not whether it is still valid. Nothing done in Coder revokes a credential a workspace already received, so an exposed credential is rotated at its source. This applies to deletion as much as to clearing a target. No new headings, no changes to the existing tables or callouts. > This PR was generated by Coder Agents on behalf of @dylanhuff-at-coder. --------- Co-authored-by: Nick Vigilante <nickvigilante@users.noreply.github.com> |
||
|
|
f44e8c12d6 |
fix: support adding multiline secrets via the UI (#27253)
**Summary**
Root cause (confirmed by reading SecretDialog.tsx): the Value field on
the User Secrets "Add secret"/"Edit secret" dialog was rendered as
<Input type="text">, a single-line HTML control, so pasting a multiline
kubeconfig/PEM/YAML got visually collapsed and the newlines were lost
before the value ever reached the form state or the API request.
Fix applied in
site/src/pages/UserSettingsPage/SecretsPage/SecretDialog.tsx:
* Swapped the Value field's <Input> for the same <Textarea> component
already used for the Description field (which correctly preserves
multiline text).
* Added rows={4} so the box is usable for multiline content, and
font-mono for readability of config-like values.
* Kept the "mask typed value" obscuring behavior but switched
-webkit-text-security:circle to :disc (Chromium's supported keyword for
<textarea>; circle is input-specific and silently no-ops on textareas).
* Removed the now-unused Input import.
* All existing onFocus/onChange/onBlur masking/saved-value logic was
preserved as-is; FormHelpers types already supported
HTMLTextAreaElement, so no type changes were needed.
Test fix in
site/src/pages/UserSettingsPage/SecretsPage/SecretsPageView.stories.tsx:
the EditSecretClearValue story's toBeVisible() assertion on the helper
text was racing the render/layout commit (this test already used a
waitFor for an analogous transient-visibility check on the neighboring
Clear button). The taller textarea shifted that timing enough to expose
the race consistently when run as part of the full suite. Wrapped the
assertion in waitFor(...) to match the existing pattern.
*Created with the help of Mux and Sonnet, tested by a human*
Working with a secret file like this:
```
➜ Downloads cat ~/.kube/config
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJkakNDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdGMyVnkKZG1WeUxXTmhRREUzTWpBNU16VTFOelV3SGhjTk1qUXdOekUwTURVek9UTTFXaGNOTXpRd056RXlNRFV6T1RNMQpXakFqTVNFd0h3WURWUVFEREJock0zTXRjMlZ5ZG1WeUxXTmhRREUzTWpBNU16VTFOelV3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFTK1JRaUlLa0RVV0tKR0Y0ZFdnbWZSVTA1Wjg1ellQSWt6aWlHUXdBZ1gKMXdyQThhNVRRbzVuYlJmeWxRQXZtZDB1SE04a05GTmZIYXJwc3lxZkZyS0dvMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVTBmUHdjMWpnd1BraFN2Q0cwbjQ1CnZTV09ES1V3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnQ3RCRm5KR0VJVVRSZ1RRaXZFSWJIODVwQnlMdmx1RjMKQ2ZTRFhDc3hlaWtDSUF4a0JKWnJ2eUUzQzNWRWdVT0t2d2VzVUN4cFBOendpbkg5Qkd4TXJuTk4KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
server: https://192.168.50.23:6443
name: k3s-3-pi
```
Created via the UI in 2.35.1:
```
coder_dev=> select * from user_secrets;
-[ RECORD 1 ]+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
id | 12853b89-a56b-408a-a9b0-11591ed04898
user_id | 2702b6f3-b094-47ee-975e-47dc7887dbed
name | kubeconfig
description |
value | apiVersion: v1 clusters: - cluster: certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJkakNDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdGMyVnkKZG1WeUxXTmhRREUzTWpBNU16VTFOelV3SGhjTk1qUXdOekUwTURVek9UTTFXaGNOTXpRd056RXlNRFV6T1RNMQpXakFqTVNFd0h3WURWUVFEREJock0zTXRjMlZ5ZG1WeUxXTmhRREUzTWpBNU16VTFOelV3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFTK1JRaUlLa0RVV0tKR0Y0ZFdnbWZSVTA1Wjg1ellQSWt6aWlHUXdBZ1gKMXdyQThhNVRRbzVuYlJmeWxRQXZtZDB1SE04a05GTmZIYXJwc3lxZkZyS0dvMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVTBmUHdjMWpnd1BraFN2Q0cwbjQ1CnZTV09ES1V3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnQ3RCRm5KR0VJVVRSZ1RRaXZFSWJIODVwQnlMdmx1RjMKQ2ZTRFhDc3hlaWtDSUF4a0JKWnJ2eUUzQzNWRWdVT0t2d2VzVUN4cFBOendpbkg5Qkd4TXJuTk4KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= server: https://192.168.50.23:6443 name: k3s-3-pi
env_name |
file_path | ~/.kube/config
created_at | 2026-07-15 15:55:07.438234+10
updated_at | 2026-07-15 15:55:07.438234+10
value_key_id |
```
Post change:
```
coder=# select * from user_secrets ;
-[ RECORD 1 ]+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
id | 7c4858bc-ee87-45f0-b85d-0e9b1474d69c
user_id | 0f0a590b-0358-4a24-bb25-ccc936bcc8d6
name | kubeconfig
description |
value | apiVersion: v1 +
| clusters: +
| - cluster: +
| certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJkakNDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdGMyVnkKZG1WeUxXTmhRREUzTWpBNU16VTFOelV3SGhjTk1qUXdOekUwTURVek9UTTFXaGNOTXpRd056RXlNRFV6T1RNMQpXakFqTVNFd0h3WURWUVFEREJock0zTXRjMlZ5ZG1WeUxXTmhRREUzTWpBNU16VTFOelV3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFTK1JRaUlLa0RVV0tKR0Y0ZFdnbWZSVTA1Wjg1ellQSWt6aWlHUXdBZ1gKMXdyQThhNVRRbzVuYlJmeWxRQXZtZDB1SE04a05GTmZIYXJwc3lxZkZyS0dvMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVTBmUHdjMWpnd1BraFN2Q0cwbjQ1CnZTV09ES1V3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnQ3RCRm5KR0VJVVRSZ1RRaXZFSWJIODVwQnlMdmx1RjMKQ2ZTRFhDc3hlaWtDSUF4a0JKWnJ2eUUzQzNWRWdVT0t2d2VzVUN4cFBOendpbkg5Qkd4TXJuTk4KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=+
| server: https://192.168.50.23:6443 +
| name: k3s-3-pi
env_name |
file_path | ~/.kube/config
created_at | 2026-07-15 15:49:08.772126+10
updated_at | 2026-07-15 15:53:39.375332+10
value_key_id |
```
And displayed correct in the Coder workspace:
<img width="508" height="238" alt="image"
src="https://github.com/user-attachments/assets/a0e9208e-cb73-4636-94e4-a67dcb510da4"
/>
Add secret UI is now a multiline input box:
<img width="534" height="839" alt="image"
src="https://github.com/user-attachments/assets/6e2a89a3-bef2-40c0-852e-0c9ac8e2f0cd"
/>
Co-authored-by: Atif Ali <atif@coder.com>
|
||
|
|
8a4adeec32 |
fix(agent): detect IPv6-bound listening ports (#27765)
Closes https://github.com/coder/coder/issues/15675 Dev servers that bind to the IPv6 wildcard address (Next.js, Node's default `http.Server`, Go's `net.Listen` on wildcard addresses, and others) never showed up in the dashboard Ports panel, even though they were listening and reachable. The agent's port scanner only called `netstat.TCPSocks`, which reads `/proc/net/tcp` (IPv4 only); an `[::]` dual-stack socket lives exclusively in `/proc/net/tcp6`, so the panel reported "No open ports were detected." Also scan `netstat.TCP6Socks` and merge the results into the existing dedupe loop. The IPv6 scan failure is non-fatal so hosts with IPv6 disabled (missing `/proc/net/tcp6`, Windows with the v6 stack off) keep their IPv4 results instead of the Ports panel 500ing. Both captures below: `python3 -m http.server 5123 --bind ::` running in the same workspace, built from main vs this branch ([full recordings](https://github.com/coder/coder/tree/recordings/recordings/ipv6-listening-ports)). ## Before  ## After  <details> <summary>Debugging and decision log</summary> - Reproduced on a dogfood workspace: `next-server` listening on `*:3000` per `ss -tlnp`, `curl` returning 200, but `GET /api/v2/workspaceagents/{agent}/listening-ports` returning `{"ports": []}`. Port 3000 (hex `0BB8`) present in `/proc/net/tcp6`, absent from `/proc/net/tcp`. - Confirmed `agent/ports_supported.go` only calls `netstat.TCPSocks`; the library's `TCP6Socks` (parses `/proc/net/tcp6` on Linux, `GetTcp6Table2` on Windows) was never referenced. - Control test: an IPv4-bound (`0.0.0.0`) listener was detected correctly, isolating the bug to the missing IPv6 scan. - Made the IPv6 scan failure non-fatal after review: returning an error would break the entire ports endpoint (HTTP 500 on every poll) on IPv6-disabled hosts, a regression from the current behavior of "IPv4 ports only." Silent fallback matches the spirit of `ports_unsupported.go`, which returns an empty list rather than erroring. - The fix lives in the OS-backed `osListeningPortsGetter` behind the `ListeningPortsGetter` interface from #20842, so the fake used by coderd tests is unaffected. - Dedupe by port in the existing `seen` map covers dual-stack sockets that appear in both tables on Windows. - `TestOSListeningPortsGetter_IPv6` listens on `[::]:0` and asserts detection; it skips when the host can't bind IPv6. Verified it fails against the pre-fix code. </details> > 🤖 This PR was opened by [Coder Agents](https://coder.com/docs/ai-coder/agents) on behalf of @bpmct. |
||
|
|
71c7e92084 |
ci: broaden linkspector Stack Overflow ignore for weekly docs check (#27799)
## Problem The weekly docs link check ([`weekly-docs`](https://github.com/coder/coder/blob/main/.github/workflows/weekly-docs.yaml) -> [linkspector](https://github.com/UmbrellaDocs/linkspector)) is failing. - **Failing run:** https://github.com/coder/coder/actions/runs/30805217850/job/91691078170 - **Broken link:** `https://stackoverflow.com/a/55570286` in `docs/admin/users/idp-sync.md` (line 576), reported `Status: 403`. Stack Overflow returns `403` to the GitHub runner IPs (bot detection), so a valid link is flagged as broken. This is the same false-positive mode already handled for `npmjs.com`, `merriam-webster.com`, `splunk.com`, etc. in `.github/.linkspector.yml`. ## Fix / why a bare `stackoverflow.com` pattern linkspector matches `ignorePatterns` as regex substrings against each URL. The config already ignored `stackoverflow.com/questions`, but the failing URL is an **answer** link (`/a/...`) that the `/questions` pattern does not cover, and it has no `www.` prefix, so a `www.stackoverflow.com` entry would not match it either. Broadening to a bare `stackoverflow.com`: - fixes the failing `/a/55570286` link; - keeps the existing `/questions/...` link in `docs/about/contributing/frontend.md` covered (so the now-redundant `stackoverflow.com/questions` line is removed); - also covers any `www.` variant. ## Validation - Confirmed the `stackoverflow.com` regex matches `/a/`, `/questions/`, and `www.` variants; config parses; `typos` clean. - The `weekly-docs` `pull_request` trigger is path-filtered to `docs/**`, so this `.github/`-only change does not auto-run linkspector here. Validated end-to-end via a manual `workflow_dispatch` on this branch, which passed: https://github.com/coder/coder/actions/runs/30829301773 <details> <summary>Unrelated failing check: <code>Pixel / Review</code> (visual regression)</summary> `Pixel / Review` reports failed visual tests ([build 6916](https://pixel.coder.com/@coder/coder/builds/6916)). This PR only edits `.github/.linkspector.yml` (link-checker config) and changes no UI, components, or rendered output, so these are pre-existing visual-baseline diffs / flakes unrelated to this change. All path-relevant required checks (`fmt`, `gen`, `lint`, `changes`, `title`, `Storybook`, `Flake Check`) pass. </details> Linear: [DOCS-606](https://linear.app/codercom/issue/DOCS-606/fix-weekly-docs-link-check-allowlist-stack-overflow-in-linkspector) > This PR was created with AI assistance (Coder Agents). |
||
|
|
ba4779fc87 |
docs: lead with env vars in admin docs and add configuration reference (#26824)
## What & why Admin/setup docs lead with `coder server --flag` examples, but most operators configure Coder through `CODER_*` environment variables (system service, container, or Helm chart). There is no single page mapping a setting to its env var, CLI flag, YAML key, and default, so searching the docs for an env var name such as `CODER_PG_CONNECTION_URL` returns nothing. This adds a generated configuration reference and begins shifting admin docs to lead with the environment-variable form. ## Changes - **Generated configuration reference** (`docs/admin/setup/configuration-reference.md`): a searchable, per-setting list of every visible deployment option. Each option is a heading (grouped and nested by serpent group) followed by its description and the environment variable, CLI flag, YAML key, and default that apply to it. Generated from `codersdk.DeploymentValues` so it stays in sync. - **Generator + `make gen` wiring** (`scripts/configdocgen/`): new binary plus a Makefile target and `GEN_FILES` entry, mirroring the existing `clidocgen` / `auditdocgen` pattern. Output is host-independent (same env normalization as `clidocgen`). - **Demo conversion** (`docs/admin/users/github-auth.md`): inverted to lead with the `/etc/coder.d/coder.env` env-var form; the CLI-flag form becomes a closing note that links to the reference. H2 slugs preserved. - **Style guide** (`.claude/docs/DOCS_STYLE_GUIDE.md`): documents the env-var-first convention for admin/setup docs. - **Navigation**: manifest entry under Administration → Setup, plus a TIP callout on the setup index. ## Risk Docs + gen pipeline only; no runtime change. The page is regenerated by `make gen`; the `gen` and `check-docs` CI checks pass. ## Follow-up Several other admin pages still lead with flag walls. Recommend sweeping them incrementally in separate PRs rather than expanding scope here. <details> <summary>Implementation notes (provenance, conflict resolution, verification)</summary> - Continues prior work by @aslilac and @bpmct from the `kayla/docs-env-vars-first` branch. Both original commits are cherry-picked here with authorship preserved. - Rebased onto current `main`. Resolved two `Makefile` conflicts where `main` had since added the `feature-stages.md` gen target at the same locations; kept both targets (union) in `GEN_FILES`, `gen/mark-fresh`, and the recipe block. - The original branch's checked-in page predated recent `codersdk.DeploymentValues` changes, so it was **regenerated** against current `main` (adds `CODER_SCIM_USE_LEGACY`, the `Networking / Cluster` section with `CODER_CLUSTER_HOST`, `CODER_BOUNDARY_LOG_RETENTION`, and the AI Gateway description rename). The `gen` CI check enforces this stays current. - Fixed flag-link anchors for short-form flags (`--config`, `--log-filter`): the generator derives the anchor from `FlagShorthand` to match `clidocgen`'s heading (e.g. `#-l---log-filter`). - `linkspector` ignores the AWS Bedrock base URL that appears as an illustrative `<region>` placeholder in an option description, consistent with the existing `openai.com` ignore patterns. </details> <details> <summary>Configuration reference layout (2026-07-08 update)</summary> Reworked the reference from a wide table into a nested, per-setting list so it fits without horizontal scrolling and stops repeating the group name in every heading: - **List, not table.** Each option renders as a heading, its description, and a bullet list of only the configuration methods that apply to it (non-applicable methods are omitted instead of shown as `-`). - **Nested sections.** Sections nest by the serpent group hierarchy, so `Email / Email Authentication` becomes `Email` (h2) with an `Email authentication` (h3) subsection instead of a redundant flat title. - **Shorter, sentence-case headings.** The redundant group prefix is stripped from each option name and the remainder is lowercased to sentence case, preserving acronyms and mixed-case tokens (`URL`, `TLS`, `OAuth2`, `GitHub`) plus a small proper-noun allowlist (`Coder`, `Terraform`, `Honeycomb`, `Anthropic`, `Bedrock`, ...). Example: `AI Gateway Send Actor Headers` becomes `Send actor headers`. - **Deprecated options** sort to the end of each section and lead with an emphasized **Deprecated** marker. Headings stay clean (no `(deprecated)` suffix) so their anchors remain stable. - **Section intros** render from a group's `Description` when the source defines one (e.g. DERP); no hand-maintained prose or links are introduced. All transformations run in pure Go at `make gen` time (no AI at generation time). Generation is idempotent, and `markdownlint` and `golangci-lint` both pass. </details> --- 🤖 Opened by Coder Agents on behalf of @nickvigilante. Continues work by @aslilac and @bpmct. --------- Co-authored-by: Kayla (via Coder Agents) <kayla@coder.com> Co-authored-by: Coder Agents <noreply@coder.com> Co-authored-by: Ben Potter <me@bpmct.net> |
||
|
|
ee7e7ecb74 |
docs: add a glossary to the reference section (#27165)
Adds a reference glossary at `docs/reference/glossary.md` that defines
the Coder-specific terms and product names readers encounter across the
docs, and registers it in `docs/manifest.json` under **Reference**.
The page prioritizes the term collisions around "agent": it
disambiguates Coder Agents (the AI product), the workspace agent (the
in-workspace daemon), and the `coder_agent` Terraform resource with an
`[!IMPORTANT]` callout and cross-referenced entries. Every definition
was checked against the current docs, and every internal link is a
relative path that resolves in-repo.
Decision log and verification notes
**Scope of this PR**
- Creates the glossary page (`docs/reference/glossary.md`) and its
manifest entry.
- Adds glossary drift-prevention guidance in response to review feedback
(see **Follow-up from review** below).
- Out of scope (tracked separately): `Glossary: ` cross-link callouts
across the docs IA, the search-ranking boost, the workspace-daemon
rename decision, and the automated glossary lint guard
([DOCS-604](https://linear.app/codercom/issue/DOCS-604)).
**Follow-up from review (@bpmct)**
Ben flagged the risk that terms get introduced, renamed, or deprecated
without the glossary keeping up. Addressed in commit
|
||
|
|
76eb43a78e |
test(coderd): drain dormancy audit log before reset in StartWakesUpDormantWorkspace (#27541)
Closes PLAT-219 / [coder/internal#1532](https://github.com/coder/internal/issues/1532). The reported failure (`should have 2 item(s), but has 3`) was the stop build's outcome audit landing after `auditor.ResetLogs()`; that path was already deflaked by #26232 and the pre-reset drain added in #25037. This closes the remaining hole in the same class: the dormancy update's audit log is exported in a middleware `defer` after the API response, so it can also land after `ResetLogs` and break the exact `len == 2` assertion. Extend the pre-reset poll to drain the dormancy write entry alongside the stop build entry. Verified with `go test ./coderd -run TestWorkspaceDormant -count=5` and `-race`. > Generated by Coder Agents on behalf of @Emyrk. |
||
|
|
6e5f02bfbe |
fix: bound golangci-lint memory to stop lint OOM kills (#27637)
## Summary Reduce peak memory during linting so cold-cache runs do not exhaust memory on high-core development hosts or leave CI runners with little headroom. ## Problem `golangci-lint` v1 sizes its default concurrency from available CPUs, not the cgroup memory limit. On a 96-CPU development host capped at 32 GiB, a cold-cache full lint run peaked at about 33.9 GB and reached the cgroup limit. The CI workflow also used bare `make -j`, which allows every lint target to run concurrently. ## Fix Make the supported `make lint/go` path cap `golangci-lint` at the lower of detected CPUs and eight workers, preserving lower concurrency on smaller hosts. Preserve an inherited `GOMEMLIMIT`, use a configurable 8 GiB fallback when it is unset, and bound CI's parallel lint targets to the runner CPU count. In the same high-core cold-cache reproduction, peak memory for the full lint target fell to about 10.1 GB. Raw `golangci-lint run` invocations remain unchanged. A `golangci-lint` v2 migration remains separate because it does not size concurrency from the memory limit and requires broader configuration migration work. > Mux prepared and updated this PR on Mike's behalf. |
||
|
|
4e54f311bd |
Revert "chore: add @coder/docs as a CODEOWNER for docs content and tooling" (#27802)
Reverts coder/coder#27240 The emails got too spammy, and so we need to find another mechanism to track PRs and how well each PR is documented. |
||
|
|
24ff135801 |
feat(helm/coder): make HTTPS redirect status code configurable (#27360)
Add coder.httproute.httpsRedirect.statusCode to configure the HTTPRoute redirect response code, defaulting to 308. Validate that the value is one of 301, 302, 303, 307, or 308. <!-- 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. --> |
||
|
|
fc24c27dfd |
fix: reserve chat hook dispatch capacity for running turns (#27656)
## Context Follow-up fix from live UAT of the merged chat lifecycle hooks stack (#27430). Its companion UAT fix (#27655) has merged, so this targets `main` directly. ## Why? UAT measured a burst of 1,500 concurrent chat creations against a consumer with 1.2s latency. 255 were admitted and 1,245 got `502 hook_dispatch_failed (over_capacity)`, which is correct fail-closed behavior. The collateral wasn't: the same burst failed 24 `stop` dispatches, parking chats that had already been admitted and had already executed tools. One 256-slot semaphore served every event, so new-work admission could take every slot and kill turns in flight. Callers now classify each dispatch as admission or generation, and admission draws from a 192-slot gate held *before* the shared pool. At least 64 shared slots stay reachable only by dispatches for work a chat already admitted. The dispatcher is per `coderd` replica, so these limits are per replica, not deployment-wide, and the docs say so. **The caller classifies, not the event type.** Event type isn't a reliable proxy in either direction: a subagent spawn dispatches `user_prompt_submit` from inside a running turn, and the edit path dispatches `session_start` at admission time. `CapacityClassUnset` is rejected in `Dispatch`, so a new call site fails closed rather than silently inheriting a share. **Acquisition order is load-bearing.** Admission takes its own gate first. Taking a shared slot first would let admissions queued on the gate occupy the very capacity the reserve protects. `acquireCapacity` is the only path that takes either pool, so the order can't be bypassed. ## What this does not guarantee Nothing bounds how many turns generate concurrently, so the 192/64 split is a judgement call, not a derived ceiling. This stops an *admission* burst from consuming every slot; it does not make the remainder sufficient. A large enough generation load can still exhaust the reserve and error a running chat. The docs say so explicitly rather than promising a guarantee the code doesn't deliver. Generation can now take all 256 slots, so generation traffic starves admission harder than before. That's the intended priority: rejecting a new prompt is recoverable, ending a turn that already ran tools is not. ## Testing Red-green proved both new tests. Removing the release-on-failure path fails `RefusedSharedAcquireReleasesAdmission` deterministically; removing the expired-deadline check fails `ExpiredDeadlineRefusesFreeSlot` in 18/30 runs. That deadline check fixes a real race found in review. `acquire` previously shared one `time.Timer` across both acquires. Because `select` picks a ready case at random, an admission dispatch could take a slot after its capacity deadline had passed. Measured over 300 trials: 135 late acquisitions, worst overshoot 2.1ms. `acquire` now takes an absolute deadline and refuses an expired one before selecting, which measures 0/300. Go: `coderd/x/agenthooks/...` and `coderd/x/chatd/...`, plus `-race -count=3` on the dispatcher. > Mux opened this PR on Mike's behalf. |
||
|
|
7bd9f5ec93 |
fix: correct authorization header spelling in api docs (#27721)
Corrects the misspelled `Authorizaiton` Swagger header name to `Authorization` in the source annotation and checked-in generated API documentation. This prevents generated API specs and SCIM examples from documenting the wrong HTTP header name. |
||
|
|
df1c0f9710 |
feat: show what a chat lifecycle hook changed (#27655)
## Stack Context Follow-up fixes from live UAT of the merged chat lifecycle hooks stack (#27430). Two PRs: 1. **This PR**: make hook effects visible and correctly attributed in the transcript. 2. [`mike/chat-hooks-uat/dispatch-capacity`]: reserve dispatch capacity so an admission burst can't fail running turns. ## Why? UAT found three ways the transcript misrepresented what a lifecycle hook did. All three are user-visible and share the same surface (`chathooks/effects.go`, `codersdk.ChatMessagePart`, the conversation timeline), so they're reviewed together. **A prompt `input_override` silently discarded attachments.** `ComposeUserPromptContent` replaced the entire submitted part list with one text part, dropping `file` and `file-reference` parts along with their `chat_file_links`. The user saw their attachments vanish with no explanation. The override now replaces submitted *text* parts only and preserves non-text parts in order. A consumer that wants to block attachments uses `deny`, which is the documented mechanism for refusing a submission. **Every user-visible `system` row was labelled "Lifecycle hook".** The timeline keyed the notice off `role === "system"`. That was correct only by accident, because the hook `user_message` was the sole client-visible system row. The backend now emits the notice as a typed `hook-notice` part and the timeline renders on that, so a future system row can't be mislabelled as a policy notice. **Nothing marked a tool call the hook had rewritten.** A consumer could replace tool input via `input_override` and the transcript showed the rewritten input as if the model had produced it. `ChatMessagePart` gains `hook_rewritten`, set from `preflight.Overrides` on the same path that already carries `ToolCallCreatedAt`, and the tool row renders a "Modified by policy" badge. `ToolCall.PolicyProvider` renders the badge itself, at four wrap sites: the `Tool` dispatch wrapper, the `ReadFilesTool` aggregate and its per-file rows, and `ReadFileTimelineBlock` (grouped and single `read_file` rows bypass `Tool`). Renderer props do not include the flag; descendants consume it through the provider context. The badge is emitted by the provider rather than by the shared header because several renderer branches return early without one, including the auth-required `execute` card, a completed `ask_user_question`, and an empty question payload. Those branches would drop the attribution with no type or runtime error, and the gap is not greppable: every renderer file contains a header somewhere, only individual branches do not. Emitting at the provider removes the possibility instead of enumerating the cases. A rewritten call is wrapped in a group labelled by its badge, so one rewritten file inside a merged read is attributed on its own rather than inheriting the group's badge. `HeaderButton` still appends the policy wording to an explicit `ariaLabel`, since an explicit `aria-label` replaces the name computed from descendants. Provider-executed calls are excluded from attribution. Hooks never see them, and duplicate tool-call ID rejection deliberately skips them, so a reused ID would otherwise mark a provider-executed call as policy-rewritten. ## Testing Go: `coderd/x/chatd/...`, `coderd/x/agenthooks/...`, `codersdk/...`, and `coderd -run 'Hook|Chat'`. Frontend: `tsc` plus every `AgentsPage` story; the only failures are `MCP Tool Completed` and `Scroll To Bottom Button Works With Inverse Scroll`, both of which fail on trunk. A registry-wide story asserts every registered renderer shows the badge, verified against three inverted toggles: removing the badge, hiding it with `display:none`, and skipping the provider for one renderer (which names that renderer). Storybook also covers the rewritten subagent spawn, a completed empty question payload, a non-hook system message, and a failed `read_file` guarding the accessible name. > Mux opened this PR on Mike's behalf. |
||
|
|
4245e4e378 |
feat: expose dynamic client registration in deployment settings (#27480)
Adds the admin-controlled OAuth2 Dynamic Client Registration setting landed by #27316 (`GET`/`PUT /api/v2/oauth2-provider/settings`) to the OAuth2 Applications deployment settings page, since it was previously only reachable via the API or `coder oauth2-provider dcr enable|disable`. The page is now tabbed, **Applications** and **Settings**, so DCR has a home that further OAuth2 settings can share (an Initial Access Token setting is a likely next one). The active tab is backed by a `tab` search param, so `?tab=settings` links straight to it, and an unpermitted deep link falls back to **Applications** rather than selecting nothing. On the Settings tab, DCR renders as a titled section with a description, an `Enabled` badge when active, and an Enable/Disable button. Enabling opens a confirmation dialog, since it lets any OAuth2 client self-register against the deployment without prior admin approval (RFC 7591). Disabling is immediate, no confirmation. The control is a button rather than a switch on design feedback: a switch reads as an immediate on/off flip, which conflicts with a confirmation dialog standing in front of it, and it left the only explanation of the risk inside a dialog that disappears. A button carries the confirmation step without misrepresenting what a click costs, the always-visible description explains the setting on the page, and the `Enabled` badge gives the active state a persistent indicator. The layout follows Tracy's mockup on `tj/oauth2-apps-pagination`; the apps-table pagination work that shares that branch is deliberately not included here. Visibility and editability are gated on the same `ResourceDeploymentConfig` RBAC checks the endpoint itself enforces (`viewDeploymentConfig` / `editDeploymentConfig`), not a separate hardcoded check. The view takes the settings values as one optional `settings` prop, absent when the viewer lacks `viewDeploymentConfig`, so "cannot view" is the shape of the prop rather than a flag the caller keeps consistent with the values beside it, and the tab is not rendered at all. Closes https://github.com/coder/coder/issues/27432 ## Where this sits in the request path ```mermaid sequenceDiagram autonumber actor Admin participant View as OAuth2AppsSettingsPageView<br/>(Tabs + Enable/Disable + Dialog) participant Page as OAuth2AppsSettingsPage<br/>(React Query) participant S as coderd Note over Page: On mount Page->>S: GET /api/v2/oauth2-provider/settings S-->>Page: { dynamic_client_registration_enabled } Page-->>View: settings: { dynamicClientRegistrationEnabled, canEdit, ... } Note over Admin,View: Admin opens the Settings tab and enables DCR Admin->>View: click "Enable" View->>View: open confirmation dialog<br/>(no request sent yet) Admin->>View: click Confirm View->>Page: settings.onDynamicClientRegistrationChange(true) Page->>S: PUT /api/v2/oauth2-provider/settings<br/>{dynamic_client_registration_enabled: true} S-->>Page: 200 OK (audited) Page->>S: GET /api/v2/oauth2-provider/settings (refetch) S-->>Page: { dynamic_client_registration_enabled: true } Page-->>View: section shows the "Enabled" badge and a Disable button Note over Admin,View: Admin disables DCR Admin->>View: click "Disable" View->>Page: onDynamicClientRegistrationChange(false)<br/>(no dialog, disable is immediate) Page->>S: PUT ... {dynamic_client_registration_enabled: false} S-->>Page: 200 OK (audited) ``` ## Files changed All 10 files are hand-written; nothing in this PR is `make gen` output. | File | What changed | |---|---| | `site/src/api/api.ts` | New `getOAuth2ProviderSettings`/`putOAuth2ProviderSettings` methods, thin typed wrappers around the two endpoints #27316 added to `main`. | | `site/src/api/api.test.ts` | Covers both methods against the request they issue and the error they propagate. | | `site/src/api/queries/oauth2.ts` | A `getSettings` query and a `putSettings` mutation that invalidates the settings key on success. Both the app and settings keys now derive from a shared `oauth2ProviderKey` constant. | | `site/src/api/queries/oauth2.test.ts` | 4 tests: the key nesting, both delegations, and that a successful update invalidates the settings key without touching app queries. | | `.../OAuth2AppsSettingsPage.tsx` | Wires query and mutation into the page and passes the settings values down as one object, or omits it entirely without `viewDeploymentConfig`. The apps error stays its own prop, since the view gates the applications empty state on it. | | `.../OAuth2AppsSettingsPageView.tsx` | `Tabs` splitting Applications from Settings. The settings tab distinguishes loading, failed, and a value the server omitted rather than rendering nothing, and the header's "Add application" action is scoped to the applications tab. | | `.../OAuth2AppsSettingsPageView.stories.tsx` | 14 stories, covering the tab wiring, both permission boundaries, the header action's scope, and the settings tab's loading, fetch-error, update-error, and value-omitted states. | | `.../DynamicClientRegistrationSetting.tsx` | The section itself: heading, description including what disabling does not undo, `Enabled` badge, a permission explanation when the viewer cannot edit, and one button that confirms only in the enable direction. | | `.../DynamicClientRegistrationSetting.stories.tsx` | 11 stories, including focus surviving an in-flight request and the dialog ignoring a value that changes underneath it. | | `docs/admin/integrations/oauth2-provider.md` | Adds the web UI route to the DCR section, which previously enumerated only the CLI and the management API. | ## Suggested review order Follows the direction data actually flows, from the raw HTTP call up to the rendered section. 1. **`site/src/api/api.ts`**: the two new methods. Confirms they match the `codersdk.OAuth2ProviderSettings` shape #27316 landed and sit next to the existing OAuth2 app methods they mirror. 2. **`site/src/api/queries/oauth2.ts`**: the query/mutation pair. The mutation's `onSuccess` → `invalidateQueries` is the one detail worth double-checking: it's what makes the on-screen state catch up with what was just saved, rather than trusting the PUT payload. 3. **`OAuth2AppsSettingsPage.tsx`**: the container. Check the two separate permission gates (`viewDeploymentConfig` on the query's `enabled` option, `editDeploymentConfig` on the button's editability) match the RBAC the backend enforces. 4. **`OAuth2AppsSettingsPageView.tsx`**: the tabs and the settings tab's four states. The `settings` prop being optional is what hides the tab; the error inside the tab is deliberately separate from the page-level `error`, which gates the applications empty state. 5. **`DynamicClientRegistrationSetting.tsx`**: the section. Two things worth reading closely: the enable path opens the dialog while the disable path calls straight through, and lacking permission uses the native `disabled` attribute while an in-flight request uses `aria-disabled`, so a keyboard user is not blurred mid-flip. 6. **The two story files**: read last, as they exercise everything above without a real server. The dialog stories query `canvasElement.ownerDocument.body` rather than `canvasElement`, since the dialog renders into a portal attached to `<body>`. ## Deliberately not in this PR - **ENG-3116**: the applications list cannot distinguish self-registered clients from admin-created ones. Surfacing that needs a new field on `codersdk.OAuth2ProviderApp`, which is an API addition this PR does not need. - **ENG-3118**: reusing the shared `EnabledBadge` and `SettingsHeader` primitives for this section. Both hinge on what the mockup intends, and the badge in particular is a visible change either here or on the four other pages that share it. ## Screenshots Default (disabled): <img width="1676" height="497" alt="image" src="https://github.com/user-attachments/assets/cfa60266-8678-410e-9577-16ef474491e3" /> Enabling (confirmation dialog): <img width="1661" height="558" alt="image" src="https://github.com/user-attachments/assets/a7d54fdd-f65d-4fec-9ed9-3bfdcfdae5be" /> Enabled: <img width="1666" height="559" alt="image" src="https://github.com/user-attachments/assets/d39251c2-771c-4608-81c2-dda151b35c3d" /> --------- Co-authored-by: Tracy Johnson <tracy@coder.com> |
||
|
|
497ab9e1a6 |
refactor(site): demui PortForwardButton and popover view (#27786)
Migrate the port-forward popover off MUI and Emotion onto shadcn Select/FormField and Tailwind. Replaces FormControl, Select, MenuItem, TextField, Stack, and Link usage in `PortForwardButton` / `PortForwardPopoverView`, and updates the popover stories to match. |
||
|
|
0a30dc8184 |
refactor(site): demui template editor dialogs and create-template inputs (#27791)
Migrate remaining MUI usage in template create/editor flows to shared shadcn components (`FormField`, `Input`, `Textarea`, `Checkbox`, `RadioGroup`, `Link`). Also drops `disablePortal` on the publish-version help popover so it is no longer clipped by the dialog overflow. |
||
|
|
20ff957c74 | refactor(site): demui workspace topbar, schedule controls, and timings (#27790) | ||
|
|
4dbb3a236c |
test: fix tailnet connection teardown flake (#27768)
Closes https://github.com/coder/internal/issues/1620 Closes ENG-3043 The callback cleanup added in #20687 stops new node callbacks, but it can still race with one that's already in flight. That callback may call `UpdatePeers` after the destination `tailnet.Conn` closes, so the test fails with `connection closed` even though the redirect behaviour is correct. To fix, we'll just ignore `tailnet.ErrConnClosed` in the asynchronous `stitch` helpers, whilst continuing to assert on every other error. |
||
|
|
07073024ee |
docs: update 2.35 latest release to v2.35.3 (#27511)
Updates the release calendar in `docs/install/releases/index.md` so the 2.35 mainline row points at the latest patch release `v2.35.3` instead of `v2.35.2`. > [!NOTE] > This PR was generated by Coder Agents on behalf of @mtojek. --------- Co-authored-by: Nick Vigilante <nickvigilante@users.noreply.github.com> |
||
|
|
fc27714fcd |
refactor(site): demui workspace build data and outdated tooltip (#27787)
Replace Emotion `css` / `useTheme` and MUI `Link` in `WorkspaceBuildData` and `WorkspaceOutdatedTooltip` with Tailwind semantic classes and the shared `Link` component. |
||
|
|
0e1a9a9f05 |
ci: run test-go-pg mise tool install under bash on Windows (#27483)
## What Add `shell: bash` to the `Install Go mise tools` step in the `test-go-pg` job so it runs under Git bash on the `windows-2022` matrix leg. ## Why `test-go-pg (windows-2022)` has been failing in the **Normalize File and Directory Timestamps** step with: ``` mtimehash: command not found ##[error]Process completed with exit code 127. ``` before any tests run, which trips the aggregate `required` gate. Root cause: the `Install Go mise tools` step had no `shell:`, so on Windows it ran under **PowerShell**, while `./.github/scripts/retry.sh` is a bash script (`#!/usr/bin/env bash`). The step silently no-oped on Windows (zero output), so `mtimehash` was only present when the mise tool cache was warm. On a cache miss it was never installed, and the normalize step failed with exit 127. `main` stays green only because the cache is usually warm — both green and failing runs use the same `depot-windows-2022-16` runner, so this is **not** a fork/runner-routing issue. Pre-existing CI-tooling issue, unrelated to the change that surfaced it (a Helm-only community PR, #27360). ## Fix One line: pin the step to `shell: bash` so the install actually runs on Windows and repairs a cache miss instead of being silently skipped. ## Verification <details> <summary>Local proof that the step's install target builds and <code>mtimehash</code> runs under bash</summary> ``` $ bash ./.github/scripts/retry.sh --max-attempts 1 -- echo "retry.sh OK under bash" retry.sh OK under bash $ GOBIN=/tmp/demobin go install github.com/slsyy/mtimehash/cmd/mtimehash@v1.0.0 $ ls -l /tmp/demobin/mtimehash -rwxr-xr-x 1 coder coder 6692858 /tmp/demobin/mtimehash # the tool that was "command not found" now runs and rewrites the mtime: $ printf 'hello' > /tmp/mt.txt # mtime 2026-07-24 15:32 $ find /tmp/mt.txt | /tmp/demobin/mtimehash # exit 0 $ # mtime now 1997-11-30 (content-hash derived) ``` </details> Final confirmation is this PR's own `test-go-pg (windows-2022)` run. Note: if the mise cache is warm on this run the job passes regardless; the fix specifically hardens the cache-miss path this step exercises on Windows. ## Follow-up (not in this PR) - Make the normalize step resilient (skip when `mtimehash` is absent / invoke via `mise exec`) so a missing cache optimizer can't hard-fail the job. - Audit other Windows `run:` steps that invoke `retry.sh`/bash scripts without `shell: bash`. Linear: DOCS-605 > This PR was created with AI assistance (Coder Agents). |
||
|
|
ec9b0f04d1 |
feat(site): label AI spend as estimated (#27584)
AI spend is estimated by multiplying token usage by a snapshot of published model prices that ships with each Coder release, so it can differ from provider invoices. Label spend figures as estimated and link to the [How spend is estimated](https://coder.com/docs/@main/ai-coder/ai-gateway/cost-controls#how-spend-is-estimated) docs section. "Estimated spend" matches the terminology already defined in the cost controls doc. ## Changes - Group members tooltip: "Monthly AI spend for this user." becomes "Estimated monthly AI spend for this user.", followed by an inline docs link. - Groups table tooltip: "Current AI spend compared to the group's AI budget..." becomes "Estimated AI spend compared to...", with the same link. - User dropdown: "(AI spend/month)" becomes "(Estimated AI spend/month)". - `StatusIconTooltip` now exports `SpendEstimateDocsLink`, shared by both tooltips. > [!NOTE] > Initially generated by Claude Opus 5, modified and reviewed by @ssncferreira |
||
|
|
f4110122ec |
fix(site): link spend page alert to cost control migration section (#27782)
The "Read more here" link in the spend page alert pointed at the top of the cost controls doc, which doesn't explain the v2.36 move to AI Governance. Point it at the `#migrate-from-coder-agents-cost-control` section instead: https://coder.com/docs/@main/ai-coder/ai-gateway/cost-controls#migrate-from-coder-agents-cost-control Related to internal slack thread: https://codercom.slack.com/archives/C0AEHQGLW22/p1785747924721539 |
||
|
|
8886a5749a |
feat: add network calls list to AI session threads API (#27425)
The AI session threads API returned only a network call *summary* (total/blocked counts + top domains). This adds the per-call list so the session detail can render individual Agent Firewall network calls. `ListAIBridgeSessionNetworkCalls` reuses the same sequence-number windowing as the existing summary and includes all protocols. The list is exposed as `network_call_logs` on the threads response and is capped server-side at 100 rows. The summary (`network_calls.total`/`blocked`) remains authoritative for whole-session totals: the list length and its blocked count equal the summary only when a session has at most 100 calls, and are truncated beyond that. ### PR map (merge strictly bottom-up) This change is a 4-PR stack. Each PR depends on all the ones below it, so merge in this exact order: 1. #27417 — backend network summary 2. #27418 — frontend summary rows 3. #27425 — backend per-call list `network_call_logs` 4. #27426 — frontend network-calls panel Refs AIGOV-464 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
fba9f0d485 |
refactor(site): migrate <Markdown /> and <InlineMarkdown /> off MUI (#27724)
Replace Emotion `css` / MUI theme tokens in `Markdown` with Tailwind semantic classes, and swap `InlineMarkdown`'s MUI `Link` for the shared `Link` component. Code block colors now use theme tokens (`surface-secondary`, `syntax-key`, `content-destructive`) instead of hardcoded zinc/teal and `theme.palette`. Also adds a `WithLink` story for the full Markdown component. |
||
|
|
31ac9a4782 |
fix(site): use "Create token" wording in token settings (#27634)
> 🤖 This PR was written by Coder Agents on behalf of Jake Howell.
The token settings page button read **"Add token"**, while everything
else uses **"Create"**:
- The page the button navigates to is titled "Create Token" with a
"Create token" submit button (`CreateTokenPage` / `CreateTokenForm`).
- CLI: `coder tokens create`
- SDK: `CreateToken`
- API: `create-token-api-key` / `CreateTokenRequest`
This aligns the button with the dominant nomenclature by changing "Add
token" → "Create token".
|
||
|
|
c350710aeb |
refactor(site): remove mui components from <LicenseSettingsPage /> descendants (#27764)
Migrate license settings views off MUI by replacing `TextField` with `Textarea` on the add-license form and `MuiLink` with our `Link` component in the empty licenses state. |
||
|
|
a76c51dfc4 |
fix(site): correct autostop restart prompt on the workspace schedule page (#27632)
> 🤖 This PR was written by Coder Agents on behalf of Jake Howell. ## Problem Enabling autostop on a running workspace never showed the "restart now to apply?" dialog. The new TTL was saved, but a running build's deadline is only calculated when the build starts, so autostop silently did not take effect until a manual restart. Changing an already-enabled value did prompt, so the behavior was inconsistent. Separately, dismissing that dialog with **Apply later** navigated the user away to the workspace, exactly like confirming did. ## Root cause The dialog was gated on `getAutostop(workspace).autostopEnabled`, which derives from `workspace.ttl_ms` — the **pre-submit** state (the workspace is not refetched until after the mutation). That expression means "was autostop enabled *before* this change", which maps to: | Transition | Pre-submit enabled | Dialog | Correct? | |---|---|---|---| | Disabled → enabled (add) | `false` | not shown | ❌ the reported bug | | Enabled → new value (modify) | `true` | shown | ✅ | | Enabled → disabled (remove) | `true` | shown | ❌ (nothing to apply) | This guard came from #16085, which intended to *skip* the prompt when **removing** autostop (safe, because that PR also made the backend clear a running build's deadline server-side when TTL is set to null). By keying off the old state it actually skipped the prompt on **add** and still showed it on **remove** — the opposite of its intent. ## Fix - Key the prompt off the submitted form value (`values.autostopEnabled`), with an inline comment documenting the trigger conditions. This fixes the reported bug and restores #16085's intent: - add → prompt ✅ - modify → prompt ✅ - remove → no prompt ✅ (deadline cleared server-side) - stopped / unchanged → no prompt ✅ - **Apply later** now just closes the dialog and keeps the user on the schedule page; the saved value still applies on the next start. **Restart** is unchanged (restarts and navigates to the workspace). ## Testing Storybook `play`-function coverage on `WorkspaceSchedulePage` for all cases: enable, change value, disable, enable-while-stopped, autostart-only, and Apply-later-stays-on-page (the last also asserts `restartWorkspace` is not called). A prior story that asserted the pre-fix behavior (prompting on disable) was reworked. `tsc` and `biome` clean. <details> <summary>Investigation notes</summary> History of the gating condition: - Originally `if (data.autostopChanged) { ... }` — prompted on every autostop change (enable included). - #16085 ("allow removing deadline for running workspace", fixes #9775) added `&& getAutostop(workspace).autostopEnabled`. Its description states the goal was to "not show a confirmation dialog when the change is to remove autostop", and the same PR added backend logic in `putWorkspaceTTL` to clear a running build's deadline when TTL is null. - A later refactor added `&& workspace.latest_build.status === "running"`. Because the backend already clears the deadline live on removal, no restart is needed there; the frontend only needs to prompt when autostop ends up enabled on a running workspace. The fix keys off the submitted state so all three transitions behave correctly. </details> |
||
|
|
0028fea0e5 |
refactor(site): migrate VS Code button menus off MUI (#27730)
Replace MUI `Menu`/`MenuItem` (and the Emotion `css` prop) in `VSCodeDesktopButton` and `VSCodeDevContainerButton` with the shared shadcn `DropdownMenu`. The variant selector keeps the split-button layout, sizes the menu to the button group via an `inline-flex` anchor (so width is not the full parent), and uses `collisionPadding` so the menu stays inset from the viewport edge. |
||
|
|
5e29a878f2 |
refactor(site): demui <ProvisionerTagsField /> (#27763)
Replace MUI `TextField` in `ProvisionerTagsField` with the shared `Input` component. |
||
|
|
ce20ad8b2f |
refactor(site): demui <NotificationEvents /> into dropdown (#27732)
> [!NOTE] > We don't have a planned migration path for the MUI `<ToggleGroup />`. Therefore, I've swapped these to dropdowns as an inbetween. Migrate deployment notification event settings off MUI `Card`/`List`/`ToggleButtonGroup` and Emotion styles. Template groups now use the same Tailwind card layout as user notification settings, and the delivery method control is a shared `Select` dropdown with icon + label. | Old | New | | --- | --- | | <img width="1041" height="189" alt="NOTIFICATION_EVENTS_OLD" src="https://github.com/user-attachments/assets/2fe37dab-1ba2-43c2-9d01-3edf9883054a" /> | <img width="1041" height="203" alt="NOTIFICATION_EVENTS_NEW" src="https://github.com/user-attachments/assets/40afc42f-93f6-4972-8e02-dceaf95fb034" /> | |
||
|
|
26bae42c98 |
refactor(site): rename <404Page /> to <NotFoundPage /> (#27731)
The component was already named `NotFoundPage`, but it lived under `pages/404Page/404Page.tsx`. Rename the directory and file to match the component name, and update the lazy import and organization settings import paths. |