From ba44400863d7d41a78267a969c70805f152a09ba Mon Sep 17 00:00:00 2001 From: Nick Vigilante Date: Thu, 3 Sep 2026 13:21:34 -0400 Subject: [PATCH 1/6] docs: generate the experiments list and document how to enable experiments The feature-stages page said no experimental features exist because its generator read ExperimentsSafe, which is empty, while ExperimentsKnown defines fourteen. Add scripts/experimentsdocgen, which writes docs/experiments.json from ExperimentsKnown (id, display name, the constant's comment as description, and whether --experiments=* enables it), and have the release script render the feature-stages table from that file so the two cannot disagree. Both are make gen targets, so CI's unstaged-files check fails a PR that changes experiments without regenerating. The enabling instructions now distinguish the opt-in set from flags that must be named. Assisted-by: AI --- .gitattributes | 1 + Makefile | 15 +- docs/experiments.json | 89 ++++++++++ docs/install/releases/feature-stages.md | 41 +++-- scripts/experimentsdocgen/main.go | 153 ++++++++++++++++++ scripts/experimentsdocgen/main_test.go | 80 +++++++++ scripts/release/docs_update_feature_stages.sh | 85 ++-------- 7 files changed, 379 insertions(+), 85 deletions(-) create mode 100644 docs/experiments.json create mode 100644 scripts/experimentsdocgen/main.go create mode 100644 scripts/experimentsdocgen/main_test.go diff --git a/.gitattributes b/.gitattributes index 78a8e2963d8..9313f457c6f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -41,3 +41,4 @@ site/src/api/typesGenerated.ts linguist-generated=true # AI Gateway prices coderd/aibridge/prices/data/prices.json linguist-generated=true site/src/pages/AgentsPage/components/ChatModelAdminPanel/knownModels/knownModelsGenerated.json linguist-generated=true +docs/experiments.json linguist-generated=true diff --git a/Makefile b/Makefile index 4070f892410..3dae55a2819 100644 --- a/Makefile +++ b/Makefile @@ -144,6 +144,10 @@ _gen/bin/auditdocgen: $(wildcard scripts/auditdocgen/*.go) $(wildcard enterprise @mkdir -p _gen/bin go build -o $@ ./scripts/auditdocgen +_gen/bin/experimentsdocgen: $(wildcard scripts/experimentsdocgen/*.go) codersdk/deployment.go | _gen + @mkdir -p _gen/bin + go build -o $@ ./scripts/experimentsdocgen + _gen/bin/check-scopes: $(wildcard scripts/check-scopes/*.go) $(RBAC_GO_FILES) | _gen @mkdir -p _gen/bin go build -o $@ ./scripts/check-scopes @@ -1017,6 +1021,7 @@ GEN_FILES := \ docs/admin/integrations/prometheus.md \ docs/reference/cli/index.md \ docs/admin/security/audit-logs.md \ + docs/experiments.json \ docs/install/releases/feature-stages.md \ docs/admin/setup/configuration-reference.md \ coderd/apidoc/swagger.json \ @@ -1116,6 +1121,7 @@ gen/mark-fresh: docs/admin/integrations/prometheus.md \ docs/reference/cli/index.md \ docs/admin/security/audit-logs.md \ + docs/experiments.json \ docs/install/releases/feature-stages.md \ docs/admin/setup/configuration-reference.md \ coderd/apidoc/swagger.json \ @@ -1353,10 +1359,17 @@ docs/admin/security/audit-logs.md: node_modules/.installed coderd/database/queri pnpm exec markdown-table-formatter "$$tmpfile" && \ mv "$$tmpfile" "$@" && rm -rf "$$tmpdir" +# Every experiment this version knows about, with its display name, description, +# and whether --experiments=* enables it. The feature-stages page renders its +# experiments table from this file, and the docs engine validates experimental +# content markers against it. +docs/experiments.json: codersdk/deployment.go $(wildcard scripts/experimentsdocgen/*.go) | _gen _gen/bin/experimentsdocgen + _gen/bin/experimentsdocgen --source codersdk/deployment.go --out $@ + docs/install/releases/feature-stages.md: \ node_modules/.installed \ scripts/release/docs_update_feature_stages.sh \ - codersdk/deployment.go \ + docs/experiments.json \ docs/manifest.json | _gen tmpdir=$$(mktemp -d -p _gen) && tmpfile=$$(realpath "$$tmpdir")/$(notdir $@) && cp "$@" "$$tmpfile" && \ ./scripts/release/docs_update_feature_stages.sh "$$tmpfile" && \ diff --git a/docs/experiments.json b/docs/experiments.json new file mode 100644 index 00000000000..14f87b3c3e0 --- /dev/null +++ b/docs/experiments.json @@ -0,0 +1,89 @@ +{ + "schemaVersion": 1, + "experiments": [ + { + "id": "agent-lifecycle-hooks", + "displayName": "Agent Lifecycle Hooks", + "description": "Enables chat lifecycle hook webhooks for agent chats.", + "safe": false + }, + { + "id": "ai-gateway-seat-exclusion", + "displayName": "AI Gateway Seat Exclusion", + "description": "Excludes AI Gateway (AI Bridge) usage from AI Governance seat consumption.", + "safe": false + }, + { + "id": "auto-fill-parameters", + "displayName": "Auto-fill Template Parameters", + "description": "This should not be taken out of experiments until we have redesigned the feature.", + "safe": false + }, + { + "id": "chat-advisor", + "displayName": "Chat Advisor", + "description": "Enables the advisor tool for root agent chats.", + "safe": false + }, + { + "id": "chat-virtual-desktop", + "displayName": "Chat Virtual Desktop", + "description": "Enables virtual desktop and computer use provider for agents.", + "safe": false + }, + { + "id": "example", + "displayName": "Example Experiment", + "description": "This isn't used for anything.", + "safe": false + }, + { + "id": "mcp-server-http", + "displayName": "MCP HTTP Server Functionality", + "description": "Enables the MCP HTTP server functionality.", + "safe": false + }, + { + "id": "mcp-tool-search", + "displayName": "Mcp Tool Search", + "description": "Defers MCP tool schemas behind a searchable catalog in agent chats.", + "safe": false + }, + { + "id": "nats_pubsub", + "displayName": "NATS Pubsub", + "description": "Enables embedded NATS pubsub.", + "safe": false + }, + { + "id": "notifications", + "displayName": "SMTP and Webhook Notifications", + "description": "Sends notifications via SMTP and webhooks following certain events.", + "safe": false + }, + { + "id": "oauth2", + "displayName": "OAuth2 Provider Functionality", + "description": "Enables OAuth2 provider functionality.", + "safe": false + }, + { + "id": "workspace-build-updates", + "displayName": "Workspace Build Updates Channel", + "description": "Enables publishing workspace build updates to the all builds pubsub channel.", + "safe": false + }, + { + "id": "workspace-capable-licensing", + "displayName": "Workspace-Capable Licensing", + "description": "Counts only users holding the workspace-create permission toward the license seat limit.", + "safe": false + }, + { + "id": "workspace-usage", + "displayName": "Workspace Usage Tracking", + "description": "Enables the new workspace usage tracking.", + "safe": false + } + ] +} diff --git a/docs/install/releases/feature-stages.md b/docs/install/releases/feature-stages.md index 57ccea3121d..5615462bcc9 100644 --- a/docs/install/releases/feature-stages.md +++ b/docs/install/releases/feature-stages.md @@ -38,33 +38,56 @@ staging deployment.
To enable early access features: -Use the [Coder CLI](../../install/cli.md) `--experiments` flag to enable early -access features: +Early access features are experiments, and each one has a flag. Use the +[Coder CLI](../../install/cli.md) `--experiments` flag on `coder server` to +enable them: -- Enable all early access features: +- Enable the opt-in set, which is every experiment marked **Yes** in the + `Enabled by --experiments=*` column below: ```sh coder server --experiments=* ``` -- Enable multiple early access features: +- Enable specific experiments by flag, which also works for experiments the + `*` set does not include: ```sh - coder server --experiments=feature1,feature2 + coder server --experiments=flag-one,flag-two ``` -You can also use the `CODER_EXPERIMENTS` -[environment variable](../../admin/setup/index.md). +You can also set the `CODER_EXPERIMENTS` +[environment variable](../../admin/setup/index.md) to the same value. -You can opt-out of a feature after you've enabled it. +Experiments that `*` does not enable are not ready for general use; enable them +only on a staging deployment, and only by name. You can opt out of a feature +after you've enabled it by removing its flag and restarting the server.
### Available early access features +This table lists every experiment this version of Coder knows about. It is +generated from the code, so it always matches the flags this version accepts. + -Currently no experimental features are available. +| Feature | Flag | Description | Enabled by `--experiments=*` | +|---------------------------------|-------------------------------|------------------------------------------------------------------------------------------|------------------------------| +| Agent Lifecycle Hooks | `agent-lifecycle-hooks` | Enables chat lifecycle hook webhooks for agent chats. | No | +| AI Gateway Seat Exclusion | `ai-gateway-seat-exclusion` | Excludes AI Gateway (AI Bridge) usage from AI Governance seat consumption. | No | +| Auto-fill Template Parameters | `auto-fill-parameters` | This should not be taken out of experiments until we have redesigned the feature. | No | +| Chat Advisor | `chat-advisor` | Enables the advisor tool for root agent chats. | No | +| Chat Virtual Desktop | `chat-virtual-desktop` | Enables virtual desktop and computer use provider for agents. | No | +| Example Experiment | `example` | This isn't used for anything. | No | +| MCP HTTP Server Functionality | `mcp-server-http` | Enables the MCP HTTP server functionality. | No | +| Mcp Tool Search | `mcp-tool-search` | Defers MCP tool schemas behind a searchable catalog in agent chats. | No | +| NATS Pubsub | `nats_pubsub` | Enables embedded NATS pubsub. | No | +| SMTP and Webhook Notifications | `notifications` | Sends notifications via SMTP and webhooks following certain events. | No | +| OAuth2 Provider Functionality | `oauth2` | Enables OAuth2 provider functionality. | No | +| Workspace Build Updates Channel | `workspace-build-updates` | Enables publishing workspace build updates to the all builds pubsub channel. | No | +| Workspace-Capable Licensing | `workspace-capable-licensing` | Counts only users holding the workspace-create permission toward the license seat limit. | No | +| Workspace Usage Tracking | `workspace-usage` | Enables the new workspace usage tracking. | No | ## Beta diff --git a/scripts/experimentsdocgen/main.go b/scripts/experimentsdocgen/main.go new file mode 100644 index 00000000000..50ddc0944f6 --- /dev/null +++ b/scripts/experimentsdocgen/main.go @@ -0,0 +1,153 @@ +// experimentsdocgen writes docs/experiments.json: every experiment this +// version of Coder knows about (codersdk.ExperimentsKnown), its display name, +// the description from the constant's comment in codersdk/deployment.go, and +// whether `--experiments=*` enables it (membership in codersdk.ExperimentsSafe). +// +// The documentation reads the file to list experiments per version and to +// label experimental content, so the list is generated from the code rather +// than maintained by hand. `make gen` runs it; CI fails on a stale copy. +package main + +import ( + "encoding/json" + "flag" + "go/ast" + "go/parser" + "go/token" + "log" + "os" + "sort" + "strconv" + "strings" + + "golang.org/x/xerrors" + + "github.com/coder/coder/v2/codersdk" + "github.com/coder/coder/v2/scripts/atomicwrite" +) + +// experimentsDoc is the on-disk shape of docs/experiments.json. +type experimentsDoc struct { + SchemaVersion int `json:"schemaVersion"` + Experiments []experimentEntry `json:"experiments"` +} + +type experimentEntry struct { + // ID is the flag value passed to --experiments. + ID string `json:"id"` + // DisplayName is codersdk.Experiment.DisplayName() for the flag. + DisplayName string `json:"displayName"` + // Description is the trailing comment on the constant, or empty. + Description string `json:"description"` + // Safe reports whether `--experiments=*` enables the flag. + Safe bool `json:"safe"` +} + +func main() { + var ( + source string + out string + dryRun bool + ) + flag.StringVar(&source, "source", "codersdk/deployment.go", "Go file declaring the Experiment constants") + flag.StringVar(&out, "out", "docs/experiments.json", "Path of the JSON file to write") + flag.BoolVar(&dryRun, "dry-run", false, "Print the JSON instead of writing it") + flag.Parse() + + src, err := os.ReadFile(source) + if err != nil { + log.Fatalf("read %s: %v", source, err) + } + descriptions, err := parseExperimentDescriptions(src) + if err != nil { + log.Fatalf("parse %s: %v", source, err) + } + + doc := buildExperimentsDoc(codersdk.ExperimentsKnown, codersdk.ExperimentsSafe, descriptions, codersdk.Experiment.DisplayName) + data, err := json.MarshalIndent(doc, "", " ") + if err != nil { + log.Fatalf("encode: %v", err) + } + data = append(data, '\n') + + if dryRun { + _, _ = os.Stdout.Write(data) + return + } + if err := atomicwrite.File(out, data); err != nil { + log.Fatalf("write %s: %v", out, err) + } +} + +// parseExperimentDescriptions maps each `Experiment` constant's string value to +// the comment beside it (`ExperimentFoo Experiment = "foo" // Enables foo.`), +// falling back to a doc comment above the constant. Constants of any other +// type are ignored, so the file may declare whatever else it likes. +func parseExperimentDescriptions(src []byte) (map[string]string, error) { + fset := token.NewFileSet() + file, err := parser.ParseFile(fset, "deployment.go", src, parser.ParseComments) + if err != nil { + return nil, xerrors.Errorf("parse: %w", err) + } + + descriptions := map[string]string{} + for _, decl := range file.Decls { + gen, ok := decl.(*ast.GenDecl) + if !ok || gen.Tok != token.CONST { + continue + } + for _, spec := range gen.Specs { + value, ok := spec.(*ast.ValueSpec) + if !ok || len(value.Names) != 1 || len(value.Values) != 1 { + continue + } + typeName, ok := value.Type.(*ast.Ident) + if !ok || typeName.Name != "Experiment" { + continue + } + lit, ok := value.Values[0].(*ast.BasicLit) + if !ok || lit.Kind != token.STRING { + continue + } + id, err := strconv.Unquote(lit.Value) + if err != nil { + return nil, xerrors.Errorf("unquote %s: %w", lit.Value, err) + } + description := "" + switch { + case value.Comment != nil: + description = value.Comment.Text() + case value.Doc != nil: + description = value.Doc.Text() + } + descriptions[id] = strings.TrimSpace(description) + } + } + return descriptions, nil +} + +// buildExperimentsDoc assembles the document for `known`, sorted by id, marking +// each entry safe when it also appears in `safe`. Descriptions missing from the +// map are left empty rather than failing: a flag with no comment is still a flag. +func buildExperimentsDoc( + known codersdk.Experiments, + safe codersdk.Experiments, + descriptions map[string]string, + displayName func(codersdk.Experiment) string, +) experimentsDoc { + safeSet := make(map[codersdk.Experiment]bool, len(safe)) + for _, experiment := range safe { + safeSet[experiment] = true + } + entries := make([]experimentEntry, 0, len(known)) + for _, experiment := range known { + entries = append(entries, experimentEntry{ + ID: string(experiment), + DisplayName: displayName(experiment), + Description: descriptions[string(experiment)], + Safe: safeSet[experiment], + }) + } + sort.Slice(entries, func(i, j int) bool { return entries[i].ID < entries[j].ID }) + return experimentsDoc{SchemaVersion: 1, Experiments: entries} +} diff --git a/scripts/experimentsdocgen/main_test.go b/scripts/experimentsdocgen/main_test.go new file mode 100644 index 00000000000..271a0457faf --- /dev/null +++ b/scripts/experimentsdocgen/main_test.go @@ -0,0 +1,80 @@ +package main + +import ( + "os" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/coder/coder/v2/codersdk" +) + +func TestParseExperimentDescriptions(t *testing.T) { + t.Parallel() + + src := []byte(`package codersdk + +type Experiment string + +const ( + ExperimentAlpha Experiment = "alpha" // Enables alpha. + // Beta has a doc comment instead of a trailing one. + ExperimentBeta Experiment = "beta" + ExperimentGamma Experiment = "gamma" + NotAnExperiment string = "ignored" // Different type. +) + +const Unrelated = 3 +`) + + descriptions, err := parseExperimentDescriptions(src) + require.NoError(t, err) + require.Equal(t, map[string]string{ + "alpha": "Enables alpha.", + "beta": "Beta has a doc comment instead of a trailing one.", + "gamma": "", + }, descriptions) +} + +func TestParseExperimentDescriptionsRejectsInvalidGo(t *testing.T) { + t.Parallel() + + _, err := parseExperimentDescriptions([]byte("package codersdk\nconst (")) + require.Error(t, err) +} + +func TestBuildExperimentsDoc(t *testing.T) { + t.Parallel() + + known := codersdk.Experiments{"zeta", "alpha", "mid"} + safe := codersdk.Experiments{"mid"} + descriptions := map[string]string{"alpha": "First.", "zeta": "Last."} + displayName := func(e codersdk.Experiment) string { return "Name of " + string(e) } + + doc := buildExperimentsDoc(known, safe, descriptions, displayName) + + require.Equal(t, 1, doc.SchemaVersion) + require.Equal(t, []experimentEntry{ + {ID: "alpha", DisplayName: "Name of alpha", Description: "First.", Safe: false}, + {ID: "mid", DisplayName: "Name of mid", Description: "", Safe: true}, + {ID: "zeta", DisplayName: "Name of zeta", Description: "Last.", Safe: false}, + }, doc.Experiments) +} + +// The real constants file must parse and every known experiment must have a +// display name, so a new experiment cannot land without documentation metadata. +func TestRealDeploymentFile(t *testing.T) { + t.Parallel() + + src, err := os.ReadFile("../../codersdk/deployment.go") + require.NoError(t, err) + descriptions, err := parseExperimentDescriptions(src) + require.NoError(t, err) + + doc := buildExperimentsDoc(codersdk.ExperimentsKnown, codersdk.ExperimentsSafe, descriptions, codersdk.Experiment.DisplayName) + require.Len(t, doc.Experiments, len(codersdk.ExperimentsKnown)) + for _, entry := range doc.Experiments { + require.NotEmpty(t, entry.DisplayName, "experiment %q has no display name", entry.ID) + require.Contains(t, descriptions, entry.ID, "experiment %q is not declared as a constant", entry.ID) + } +} diff --git a/scripts/release/docs_update_feature_stages.sh b/scripts/release/docs_update_feature_stages.sh index 4251ea8dbc3..ca47475ac30 100755 --- a/scripts/release/docs_update_feature_stages.sh +++ b/scripts/release/docs_update_feature_stages.sh @@ -17,36 +17,6 @@ if isdarwin; then awk() { gawk "$@"; } fi -parse_all_experiments() { - # Try ExperimentsSafe first, then fall back to ExperimentsAll if needed. - experiments_var="ExperimentsSafe" - experiments_output=$(go doc -all ./codersdk "${experiments_var}" 2>/dev/null || true) - - if [[ -z "${experiments_output}" ]]; then - experiments_var="ExperimentsAll" - experiments_output=$(go doc -all ./codersdk "${experiments_var}" 2>/dev/null || true) - - if [[ -z "${experiments_output}" ]]; then - log "Warning: Neither ExperimentsSafe nor ExperimentsAll found in ./codersdk" - return - fi - fi - - echo "${experiments_output}" | - tr -d $'\n\t ' | - grep -E -o "${experiments_var}=Experiments\{[^}]*\}" | - sed -e 's/.*{\(.*\)}.*/\1/' | - tr ',' '\n' -} - -parse_experiments() { - go doc -all ./codersdk Experiment | - sed \ - -e 's/\t\(Experiment[^ ]*\)\ \ *Experiment = "\([^"]*\)"\(.*\/\/ \(.*\)\)\?/\1|\2|\4/' \ - -e 's/\t\/\/ \(.*\)/||\1/' | - grep '|' -} - parse_beta_features() { jq -r ' # Collect paths that live under any beta-marked subtree. We exclude @@ -74,57 +44,22 @@ dest=${1:-docs/install/releases/feature-stages.md} log "Updating generated feature-stages sections in ${dest}" -# Collect experiments from the current codersdk package. -declare -A experiments=() -declare -A all_experiments=() -all_experiments_out="$(parse_all_experiments)" -if [[ -n "${all_experiments_out}" ]]; then - readarray -t all_experiments_tmp <<<"${all_experiments_out}" - for exp in "${all_experiments_tmp[@]}"; do - all_experiments[$exp]=1 - done -fi - -maybe_desc= -while read -r line; do - line=${line//$'\n'/} - readarray -d '|' -t parts <<<"$line" - - if [[ -z ${parts[0]} ]]; then - maybe_desc+="${parts[2]//$'\n'/ }" - continue - fi - - var="${parts[0]}" - key="${parts[1]}" - desc="${parts[2]}" - desc=${desc//$'\n'/} - - if [[ -z "${desc}" ]]; then - desc="${maybe_desc% }" - fi - maybe_desc= - - if [[ ! -v all_experiments[$var] ]]; then - log "Skipping ${var}, not listed in experiments list" - continue - fi - - experiments[$key]="$desc" -done < <(parse_experiments) +# The experiments table comes from docs/experiments.json, which +# scripts/experimentsdocgen generates from codersdk.ExperimentsKnown (every +# experiment this version knows about) and codersdk.ExperimentsSafe (the ones +# `--experiments=*` enables). Reading the generated file keeps the table and +# the machine-readable list identical by construction. +experiments_json="${EXPERIMENTS_JSON:-${PROJECT_ROOT}/docs/experiments.json}" table="$( - if [[ "${#experiments[@]}" -eq 0 ]]; then + if [[ ! -f "${experiments_json}" ]] || [[ "$(jq '.experiments | length' "${experiments_json}")" -eq 0 ]]; then echo "Currently no experimental features are available." exit 0 fi - echo "| Feature | Description |" - echo "| ------- | ----------- |" - for key in "${!experiments[@]}"; do - desc=${experiments[$key]} - echo "| \`$key\` | $desc |" - done + echo "| Feature | Flag | Description | Enabled by \`--experiments=*\` |" + echo "| ------- | ---- | ----------- | ---------------------------- |" + jq -r '.experiments[] | "| \(.displayName) | `\(.id)` | \(.description) | \(if .safe then "Yes" else "No" end) |"' "${experiments_json}" )" # Collect beta features from the current docs/manifest.json. Keying on the From ef4cf74fb40df79563098d9c5efedf4101b7dab9 Mon Sep 17 00:00:00 2001 From: Nick Vigilante Date: Thu, 3 Sep 2026 13:35:19 -0400 Subject: [PATCH 2/6] docs: user-facing description for auto-fill-parameters; omit the example experiment The generated experiments table published two constant comments that read as developer notes. Reword the auto-fill-parameters comment (mirrored into the generated apidoc files, which embed enum comments) and have the generator skip ExperimentExample, a placeholder kept for tests. Assisted-by: AI --- coderd/apidoc/docs.go | 4 ++-- coderd/apidoc/swagger.json | 4 ++-- codersdk/deployment.go | 2 +- docs/experiments.json | 8 +------ docs/install/releases/feature-stages.md | 31 ++++++++++++------------- scripts/experimentsdocgen/main.go | 24 ++++++++++++++++--- scripts/experimentsdocgen/main_test.go | 7 ++++-- 7 files changed, 47 insertions(+), 33 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 452e9329096..d0aa7f30f52 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -23396,7 +23396,7 @@ const docTemplate = `{ "x-enum-comments": { "ExperimentAIGatewaySeatExclusion": "Excludes AI Gateway (AI Bridge) usage from AI Governance seat consumption.", "ExperimentAgentLifecycleHooks": "Enables chat lifecycle hook webhooks for agent chats.", - "ExperimentAutoFillParameters": "This should not be taken out of experiments until we have redesigned the feature.", + "ExperimentAutoFillParameters": "Pre-fills a new workspace's parameter form with the values from the user's most recent build of that template.", "ExperimentChatAdvisor": "Enables the advisor tool for root agent chats.", "ExperimentChatVirtualDesktop": "Enables virtual desktop and computer use provider for agents.", "ExperimentExample": "This isn't used for anything.", @@ -23411,7 +23411,7 @@ const docTemplate = `{ }, "x-enum-descriptions": [ "This isn't used for anything.", - "This should not be taken out of experiments until we have redesigned the feature.", + "Pre-fills a new workspace's parameter form with the values from the user's most recent build of that template.", "Sends notifications via SMTP and webhooks following certain events.", "Enables the new workspace usage tracking.", "Enables OAuth2 provider functionality.", diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index ad9119be53b..fb153094ed7 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -21300,7 +21300,7 @@ "x-enum-comments": { "ExperimentAIGatewaySeatExclusion": "Excludes AI Gateway (AI Bridge) usage from AI Governance seat consumption.", "ExperimentAgentLifecycleHooks": "Enables chat lifecycle hook webhooks for agent chats.", - "ExperimentAutoFillParameters": "This should not be taken out of experiments until we have redesigned the feature.", + "ExperimentAutoFillParameters": "Pre-fills a new workspace's parameter form with the values from the user's most recent build of that template.", "ExperimentChatAdvisor": "Enables the advisor tool for root agent chats.", "ExperimentChatVirtualDesktop": "Enables virtual desktop and computer use provider for agents.", "ExperimentExample": "This isn't used for anything.", @@ -21315,7 +21315,7 @@ }, "x-enum-descriptions": [ "This isn't used for anything.", - "This should not be taken out of experiments until we have redesigned the feature.", + "Pre-fills a new workspace's parameter form with the values from the user's most recent build of that template.", "Sends notifications via SMTP and webhooks following certain events.", "Enables the new workspace usage tracking.", "Enables OAuth2 provider functionality.", diff --git a/codersdk/deployment.go b/codersdk/deployment.go index e79b94774e9..25a39b5b6be 100644 --- a/codersdk/deployment.go +++ b/codersdk/deployment.go @@ -5472,7 +5472,7 @@ type Experiment string const ( // Add new experiments here! ExperimentExample Experiment = "example" // This isn't used for anything. - ExperimentAutoFillParameters Experiment = "auto-fill-parameters" // This should not be taken out of experiments until we have redesigned the feature. + ExperimentAutoFillParameters Experiment = "auto-fill-parameters" // Pre-fills a new workspace's parameter form with the values from the user's most recent build of that template. ExperimentNotifications Experiment = "notifications" // Sends notifications via SMTP and webhooks following certain events. ExperimentWorkspaceUsage Experiment = "workspace-usage" // Enables the new workspace usage tracking. ExperimentOAuth2 Experiment = "oauth2" // Enables OAuth2 provider functionality. diff --git a/docs/experiments.json b/docs/experiments.json index 14f87b3c3e0..36b20fa85d7 100644 --- a/docs/experiments.json +++ b/docs/experiments.json @@ -16,7 +16,7 @@ { "id": "auto-fill-parameters", "displayName": "Auto-fill Template Parameters", - "description": "This should not be taken out of experiments until we have redesigned the feature.", + "description": "Pre-fills a new workspace's parameter form with the values from the user's most recent build of that template.", "safe": false }, { @@ -31,12 +31,6 @@ "description": "Enables virtual desktop and computer use provider for agents.", "safe": false }, - { - "id": "example", - "displayName": "Example Experiment", - "description": "This isn't used for anything.", - "safe": false - }, { "id": "mcp-server-http", "displayName": "MCP HTTP Server Functionality", diff --git a/docs/install/releases/feature-stages.md b/docs/install/releases/feature-stages.md index 5615462bcc9..28b518a86e4 100644 --- a/docs/install/releases/feature-stages.md +++ b/docs/install/releases/feature-stages.md @@ -72,22 +72,21 @@ generated from the code, so it always matches the flags this version accepts. -| Feature | Flag | Description | Enabled by `--experiments=*` | -|---------------------------------|-------------------------------|------------------------------------------------------------------------------------------|------------------------------| -| Agent Lifecycle Hooks | `agent-lifecycle-hooks` | Enables chat lifecycle hook webhooks for agent chats. | No | -| AI Gateway Seat Exclusion | `ai-gateway-seat-exclusion` | Excludes AI Gateway (AI Bridge) usage from AI Governance seat consumption. | No | -| Auto-fill Template Parameters | `auto-fill-parameters` | This should not be taken out of experiments until we have redesigned the feature. | No | -| Chat Advisor | `chat-advisor` | Enables the advisor tool for root agent chats. | No | -| Chat Virtual Desktop | `chat-virtual-desktop` | Enables virtual desktop and computer use provider for agents. | No | -| Example Experiment | `example` | This isn't used for anything. | No | -| MCP HTTP Server Functionality | `mcp-server-http` | Enables the MCP HTTP server functionality. | No | -| Mcp Tool Search | `mcp-tool-search` | Defers MCP tool schemas behind a searchable catalog in agent chats. | No | -| NATS Pubsub | `nats_pubsub` | Enables embedded NATS pubsub. | No | -| SMTP and Webhook Notifications | `notifications` | Sends notifications via SMTP and webhooks following certain events. | No | -| OAuth2 Provider Functionality | `oauth2` | Enables OAuth2 provider functionality. | No | -| Workspace Build Updates Channel | `workspace-build-updates` | Enables publishing workspace build updates to the all builds pubsub channel. | No | -| Workspace-Capable Licensing | `workspace-capable-licensing` | Counts only users holding the workspace-create permission toward the license seat limit. | No | -| Workspace Usage Tracking | `workspace-usage` | Enables the new workspace usage tracking. | No | +| Feature | Flag | Description | Enabled by `--experiments=*` | +|---------------------------------|-------------------------------|----------------------------------------------------------------------------------------------------------------|------------------------------| +| Agent Lifecycle Hooks | `agent-lifecycle-hooks` | Enables chat lifecycle hook webhooks for agent chats. | No | +| AI Gateway Seat Exclusion | `ai-gateway-seat-exclusion` | Excludes AI Gateway (AI Bridge) usage from AI Governance seat consumption. | No | +| Auto-fill Template Parameters | `auto-fill-parameters` | Pre-fills a new workspace's parameter form with the values from the user's most recent build of that template. | No | +| Chat Advisor | `chat-advisor` | Enables the advisor tool for root agent chats. | No | +| Chat Virtual Desktop | `chat-virtual-desktop` | Enables virtual desktop and computer use provider for agents. | No | +| MCP HTTP Server Functionality | `mcp-server-http` | Enables the MCP HTTP server functionality. | No | +| Mcp Tool Search | `mcp-tool-search` | Defers MCP tool schemas behind a searchable catalog in agent chats. | No | +| NATS Pubsub | `nats_pubsub` | Enables embedded NATS pubsub. | No | +| SMTP and Webhook Notifications | `notifications` | Sends notifications via SMTP and webhooks following certain events. | No | +| OAuth2 Provider Functionality | `oauth2` | Enables OAuth2 provider functionality. | No | +| Workspace Build Updates Channel | `workspace-build-updates` | Enables publishing workspace build updates to the all builds pubsub channel. | No | +| Workspace-Capable Licensing | `workspace-capable-licensing` | Counts only users holding the workspace-create permission toward the license seat limit. | No | +| Workspace Usage Tracking | `workspace-usage` | Enables the new workspace usage tracking. | No | ## Beta diff --git a/scripts/experimentsdocgen/main.go b/scripts/experimentsdocgen/main.go index 50ddc0944f6..62155abf588 100644 --- a/scripts/experimentsdocgen/main.go +++ b/scripts/experimentsdocgen/main.go @@ -1,5 +1,6 @@ -// experimentsdocgen writes docs/experiments.json: every experiment this -// version of Coder knows about (codersdk.ExperimentsKnown), its display name, +// experimentsdocgen writes docs/experiments.json: every user-facing experiment +// this version of Coder knows about (codersdk.ExperimentsKnown minus the +// placeholders in notDocumented), its display name, // the description from the constant's comment in codersdk/deployment.go, and // whether `--experiments=*` enables it (membership in codersdk.ExperimentsSafe). // @@ -63,7 +64,7 @@ func main() { log.Fatalf("parse %s: %v", source, err) } - doc := buildExperimentsDoc(codersdk.ExperimentsKnown, codersdk.ExperimentsSafe, descriptions, codersdk.Experiment.DisplayName) + doc := buildExperimentsDoc(documented(codersdk.ExperimentsKnown), codersdk.ExperimentsSafe, descriptions, codersdk.Experiment.DisplayName) data, err := json.MarshalIndent(doc, "", " ") if err != nil { log.Fatalf("encode: %v", err) @@ -79,6 +80,23 @@ func main() { } } +// notDocumented lists experiments that exist for the code's own purposes and +// never describe a user-facing feature, so the documentation omits them. +var notDocumented = map[codersdk.Experiment]bool{ + codersdk.ExperimentExample: true, // A placeholder kept for tests. +} + +// documented filters `known` down to the experiments the docs should list. +func documented(known codersdk.Experiments) codersdk.Experiments { + out := make(codersdk.Experiments, 0, len(known)) + for _, experiment := range known { + if !notDocumented[experiment] { + out = append(out, experiment) + } + } + return out +} + // parseExperimentDescriptions maps each `Experiment` constant's string value to // the comment beside it (`ExperimentFoo Experiment = "foo" // Enables foo.`), // falling back to a doc comment above the constant. Constants of any other diff --git a/scripts/experimentsdocgen/main_test.go b/scripts/experimentsdocgen/main_test.go index 271a0457faf..b409e506844 100644 --- a/scripts/experimentsdocgen/main_test.go +++ b/scripts/experimentsdocgen/main_test.go @@ -71,8 +71,11 @@ func TestRealDeploymentFile(t *testing.T) { descriptions, err := parseExperimentDescriptions(src) require.NoError(t, err) - doc := buildExperimentsDoc(codersdk.ExperimentsKnown, codersdk.ExperimentsSafe, descriptions, codersdk.Experiment.DisplayName) - require.Len(t, doc.Experiments, len(codersdk.ExperimentsKnown)) + known := documented(codersdk.ExperimentsKnown) + require.NotContains(t, known, codersdk.ExperimentExample, "the example experiment is never documented") + require.Len(t, known, len(codersdk.ExperimentsKnown)-1) + doc := buildExperimentsDoc(known, codersdk.ExperimentsSafe, descriptions, codersdk.Experiment.DisplayName) + require.Len(t, doc.Experiments, len(known)) for _, entry := range doc.Experiments { require.NotEmpty(t, entry.DisplayName, "experiment %q has no display name", entry.ID) require.Contains(t, descriptions, entry.ID, "experiment %q is not declared as a constant", entry.ID) From f592e4675981232ddea07fafc14b8dd89dca7544 Mon Sep 17 00:00:00 2001 From: Nick Vigilante Date: Thu, 3 Sep 2026 13:50:02 -0400 Subject: [PATCH 3/6] docs: one sentence per line in the experiments section; MCP Tool Search display name Assisted-by: AI --- codersdk/deployment.go | 2 ++ docs/experiments.json | 2 +- docs/install/releases/feature-stages.md | 26 +++++++++++-------------- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/codersdk/deployment.go b/codersdk/deployment.go index 25a39b5b6be..a886972bac3 100644 --- a/codersdk/deployment.go +++ b/codersdk/deployment.go @@ -5501,6 +5501,8 @@ func (e Experiment) DisplayName() string { return "OAuth2 Provider Functionality" case ExperimentMCPServerHTTP: return "MCP HTTP Server Functionality" + case ExperimentMCPToolSearch: + return "MCP Tool Search" case ExperimentWorkspaceBuildUpdates: return "Workspace Build Updates Channel" case ExperimentNATSPubsub: diff --git a/docs/experiments.json b/docs/experiments.json index 36b20fa85d7..81d5337f13f 100644 --- a/docs/experiments.json +++ b/docs/experiments.json @@ -39,7 +39,7 @@ }, { "id": "mcp-tool-search", - "displayName": "Mcp Tool Search", + "displayName": "MCP Tool Search", "description": "Defers MCP tool schemas behind a searchable catalog in agent chats.", "safe": false }, diff --git a/docs/install/releases/feature-stages.md b/docs/install/releases/feature-stages.md index 28b518a86e4..4496fe8db6b 100644 --- a/docs/install/releases/feature-stages.md +++ b/docs/install/releases/feature-stages.md @@ -38,37 +38,33 @@ staging deployment.
To enable early access features: -Early access features are experiments, and each one has a flag. Use the -[Coder CLI](../../install/cli.md) `--experiments` flag on `coder server` to -enable them: +Early access features are experiments, and each one has a flag. +Use the [Coder CLI](../../install/cli.md) `--experiments` flag on `coder server` to enable them: -- Enable the opt-in set, which is every experiment marked **Yes** in the - `Enabled by --experiments=*` column below: +- Enable the opt-in set, which is every experiment marked **Yes** in the `Enabled by --experiments=*` column below: ```sh coder server --experiments=* ``` -- Enable specific experiments by flag, which also works for experiments the - `*` set does not include: +- Enable specific experiments by flag, which also works for experiments the `*` set does not include: ```sh coder server --experiments=flag-one,flag-two ``` -You can also set the `CODER_EXPERIMENTS` -[environment variable](../../admin/setup/index.md) to the same value. +You can also set the `CODER_EXPERIMENTS` [environment variable](../../admin/setup/index.md) to the same value. -Experiments that `*` does not enable are not ready for general use; enable them -only on a staging deployment, and only by name. You can opt out of a feature -after you've enabled it by removing its flag and restarting the server. +Experiments that `*` does not enable are not ready for general use. +Enable them only on a staging deployment, and only by name. +You can opt out of a feature after you've enabled it by removing its flag and restarting the server.
### Available early access features -This table lists every experiment this version of Coder knows about. It is -generated from the code, so it always matches the flags this version accepts. +This table lists every experiment this version of Coder knows about. +It is generated from the code, so it always matches the flags this version accepts. @@ -80,7 +76,7 @@ generated from the code, so it always matches the flags this version accepts. | Chat Advisor | `chat-advisor` | Enables the advisor tool for root agent chats. | No | | Chat Virtual Desktop | `chat-virtual-desktop` | Enables virtual desktop and computer use provider for agents. | No | | MCP HTTP Server Functionality | `mcp-server-http` | Enables the MCP HTTP server functionality. | No | -| Mcp Tool Search | `mcp-tool-search` | Defers MCP tool schemas behind a searchable catalog in agent chats. | No | +| MCP Tool Search | `mcp-tool-search` | Defers MCP tool schemas behind a searchable catalog in agent chats. | No | | NATS Pubsub | `nats_pubsub` | Enables embedded NATS pubsub. | No | | SMTP and Webhook Notifications | `notifications` | Sends notifications via SMTP and webhooks following certain events. | No | | OAuth2 Provider Functionality | `oauth2` | Enables OAuth2 provider functionality. | No | From 2fe00f9282dfa45c3dabec945aa99bc7bfac0584 Mon Sep 17 00:00:00 2001 From: Nick Vigilante Date: Thu, 3 Sep 2026 13:56:34 -0400 Subject: [PATCH 4/6] docs: spell out how to enable each experiment instead of a Yes/No wildcard column Assisted-by: AI --- docs/install/releases/feature-stages.md | 34 +++++++++---------- scripts/release/docs_update_feature_stages.sh | 10 ++++-- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/docs/install/releases/feature-stages.md b/docs/install/releases/feature-stages.md index 4496fe8db6b..09d8bdfec19 100644 --- a/docs/install/releases/feature-stages.md +++ b/docs/install/releases/feature-stages.md @@ -41,7 +41,7 @@ staging deployment. Early access features are experiments, and each one has a flag. Use the [Coder CLI](../../install/cli.md) `--experiments` flag on `coder server` to enable them: -- Enable the opt-in set, which is every experiment marked **Yes** in the `Enabled by --experiments=*` column below: +- Enable the opt-in set, which is every experiment whose **Enable with** column below lists `--experiments=*`: ```sh coder server --experiments=* @@ -55,7 +55,7 @@ Use the [Coder CLI](../../install/cli.md) `--experiments` flag on `coder server` You can also set the `CODER_EXPERIMENTS` [environment variable](../../admin/setup/index.md) to the same value. -Experiments that `*` does not enable are not ready for general use. +Experiments that `*` does not enable are not ready for general use; when no experiment lists `--experiments=*`, the wildcard enables nothing in this version. Enable them only on a staging deployment, and only by name. You can opt out of a feature after you've enabled it by removing its flag and restarting the server. @@ -68,21 +68,21 @@ It is generated from the code, so it always matches the flags this version accep -| Feature | Flag | Description | Enabled by `--experiments=*` | -|---------------------------------|-------------------------------|----------------------------------------------------------------------------------------------------------------|------------------------------| -| Agent Lifecycle Hooks | `agent-lifecycle-hooks` | Enables chat lifecycle hook webhooks for agent chats. | No | -| AI Gateway Seat Exclusion | `ai-gateway-seat-exclusion` | Excludes AI Gateway (AI Bridge) usage from AI Governance seat consumption. | No | -| Auto-fill Template Parameters | `auto-fill-parameters` | Pre-fills a new workspace's parameter form with the values from the user's most recent build of that template. | No | -| Chat Advisor | `chat-advisor` | Enables the advisor tool for root agent chats. | No | -| Chat Virtual Desktop | `chat-virtual-desktop` | Enables virtual desktop and computer use provider for agents. | No | -| MCP HTTP Server Functionality | `mcp-server-http` | Enables the MCP HTTP server functionality. | No | -| MCP Tool Search | `mcp-tool-search` | Defers MCP tool schemas behind a searchable catalog in agent chats. | No | -| NATS Pubsub | `nats_pubsub` | Enables embedded NATS pubsub. | No | -| SMTP and Webhook Notifications | `notifications` | Sends notifications via SMTP and webhooks following certain events. | No | -| OAuth2 Provider Functionality | `oauth2` | Enables OAuth2 provider functionality. | No | -| Workspace Build Updates Channel | `workspace-build-updates` | Enables publishing workspace build updates to the all builds pubsub channel. | No | -| Workspace-Capable Licensing | `workspace-capable-licensing` | Counts only users holding the workspace-create permission toward the license seat limit. | No | -| Workspace Usage Tracking | `workspace-usage` | Enables the new workspace usage tracking. | No | +| Feature | Flag | Description | Enable with | +|---------------------------------|-------------------------------|----------------------------------------------------------------------------------------------------------------|---------------------------------------------| +| Agent Lifecycle Hooks | `agent-lifecycle-hooks` | Enables chat lifecycle hook webhooks for agent chats. | `--experiments=agent-lifecycle-hooks` | +| AI Gateway Seat Exclusion | `ai-gateway-seat-exclusion` | Excludes AI Gateway (AI Bridge) usage from AI Governance seat consumption. | `--experiments=ai-gateway-seat-exclusion` | +| Auto-fill Template Parameters | `auto-fill-parameters` | Pre-fills a new workspace's parameter form with the values from the user's most recent build of that template. | `--experiments=auto-fill-parameters` | +| Chat Advisor | `chat-advisor` | Enables the advisor tool for root agent chats. | `--experiments=chat-advisor` | +| Chat Virtual Desktop | `chat-virtual-desktop` | Enables virtual desktop and computer use provider for agents. | `--experiments=chat-virtual-desktop` | +| MCP HTTP Server Functionality | `mcp-server-http` | Enables the MCP HTTP server functionality. | `--experiments=mcp-server-http` | +| MCP Tool Search | `mcp-tool-search` | Defers MCP tool schemas behind a searchable catalog in agent chats. | `--experiments=mcp-tool-search` | +| NATS Pubsub | `nats_pubsub` | Enables embedded NATS pubsub. | `--experiments=nats_pubsub` | +| SMTP and Webhook Notifications | `notifications` | Sends notifications via SMTP and webhooks following certain events. | `--experiments=notifications` | +| OAuth2 Provider Functionality | `oauth2` | Enables OAuth2 provider functionality. | `--experiments=oauth2` | +| Workspace Build Updates Channel | `workspace-build-updates` | Enables publishing workspace build updates to the all builds pubsub channel. | `--experiments=workspace-build-updates` | +| Workspace-Capable Licensing | `workspace-capable-licensing` | Counts only users holding the workspace-create permission toward the license seat limit. | `--experiments=workspace-capable-licensing` | +| Workspace Usage Tracking | `workspace-usage` | Enables the new workspace usage tracking. | `--experiments=workspace-usage` | ## Beta diff --git a/scripts/release/docs_update_feature_stages.sh b/scripts/release/docs_update_feature_stages.sh index ca47475ac30..dc781b66882 100755 --- a/scripts/release/docs_update_feature_stages.sh +++ b/scripts/release/docs_update_feature_stages.sh @@ -57,9 +57,13 @@ table="$( exit 0 fi - echo "| Feature | Flag | Description | Enabled by \`--experiments=*\` |" - echo "| ------- | ---- | ----------- | ---------------------------- |" - jq -r '.experiments[] | "| \(.displayName) | `\(.id)` | \(.description) | \(if .safe then "Yes" else "No" end) |"' "${experiments_json}" + # The last column spells out the command: a flag in the opt-in set is + # enabled by `--experiments=*` as well as by name; every other flag must be + # named. Spelling it out keeps the table readable when the opt-in set is + # empty, which it is today. + echo "| Feature | Flag | Description | Enable with |" + echo "| ------- | ---- | ----------- | ----------- |" + jq -r '.experiments[] | "| \(.displayName) | `\(.id)` | \(.description) | \(if .safe then "`--experiments=*` or `--experiments=\(.id)`" else "`--experiments=\(.id)`" end) |"' "${experiments_json}" )" # Collect beta features from the current docs/manifest.json. Keying on the From 54eb7ce75013403e10371b4231a0f63c3332989b Mon Sep 17 00:00:00 2001 From: Nick Vigilante Date: Thu, 3 Sep 2026 14:03:03 -0400 Subject: [PATCH 5/6] docs: state what --experiments=* enables per version instead of a per-row column Assisted-by: AI --- docs/install/releases/feature-stages.md | 38 ++++++++++--------- scripts/release/docs_update_feature_stages.sh | 21 ++++++---- 2 files changed, 34 insertions(+), 25 deletions(-) diff --git a/docs/install/releases/feature-stages.md b/docs/install/releases/feature-stages.md index 09d8bdfec19..702843d21d9 100644 --- a/docs/install/releases/feature-stages.md +++ b/docs/install/releases/feature-stages.md @@ -41,13 +41,13 @@ staging deployment. Early access features are experiments, and each one has a flag. Use the [Coder CLI](../../install/cli.md) `--experiments` flag on `coder server` to enable them: -- Enable the opt-in set, which is every experiment whose **Enable with** column below lists `--experiments=*`: +- Enable the opt-in set, which is only the experiments Coder marks as ready for opt-in in this version (the note under the table lists them; it does not enable every experiment): ```sh coder server --experiments=* ``` -- Enable specific experiments by flag, which also works for experiments the `*` set does not include: +- Enable specific experiments by flag, which is the only way to enable an experiment outside the opt-in set: ```sh coder server --experiments=flag-one,flag-two @@ -55,7 +55,7 @@ Use the [Coder CLI](../../install/cli.md) `--experiments` flag on `coder server` You can also set the `CODER_EXPERIMENTS` [environment variable](../../admin/setup/index.md) to the same value. -Experiments that `*` does not enable are not ready for general use; when no experiment lists `--experiments=*`, the wildcard enables nothing in this version. +Experiments that `*` does not enable are not ready for general use. Enable them only on a staging deployment, and only by name. You can opt out of a feature after you've enabled it by removing its flag and restarting the server. @@ -68,21 +68,23 @@ It is generated from the code, so it always matches the flags this version accep -| Feature | Flag | Description | Enable with | -|---------------------------------|-------------------------------|----------------------------------------------------------------------------------------------------------------|---------------------------------------------| -| Agent Lifecycle Hooks | `agent-lifecycle-hooks` | Enables chat lifecycle hook webhooks for agent chats. | `--experiments=agent-lifecycle-hooks` | -| AI Gateway Seat Exclusion | `ai-gateway-seat-exclusion` | Excludes AI Gateway (AI Bridge) usage from AI Governance seat consumption. | `--experiments=ai-gateway-seat-exclusion` | -| Auto-fill Template Parameters | `auto-fill-parameters` | Pre-fills a new workspace's parameter form with the values from the user's most recent build of that template. | `--experiments=auto-fill-parameters` | -| Chat Advisor | `chat-advisor` | Enables the advisor tool for root agent chats. | `--experiments=chat-advisor` | -| Chat Virtual Desktop | `chat-virtual-desktop` | Enables virtual desktop and computer use provider for agents. | `--experiments=chat-virtual-desktop` | -| MCP HTTP Server Functionality | `mcp-server-http` | Enables the MCP HTTP server functionality. | `--experiments=mcp-server-http` | -| MCP Tool Search | `mcp-tool-search` | Defers MCP tool schemas behind a searchable catalog in agent chats. | `--experiments=mcp-tool-search` | -| NATS Pubsub | `nats_pubsub` | Enables embedded NATS pubsub. | `--experiments=nats_pubsub` | -| SMTP and Webhook Notifications | `notifications` | Sends notifications via SMTP and webhooks following certain events. | `--experiments=notifications` | -| OAuth2 Provider Functionality | `oauth2` | Enables OAuth2 provider functionality. | `--experiments=oauth2` | -| Workspace Build Updates Channel | `workspace-build-updates` | Enables publishing workspace build updates to the all builds pubsub channel. | `--experiments=workspace-build-updates` | -| Workspace-Capable Licensing | `workspace-capable-licensing` | Counts only users holding the workspace-create permission toward the license seat limit. | `--experiments=workspace-capable-licensing` | -| Workspace Usage Tracking | `workspace-usage` | Enables the new workspace usage tracking. | `--experiments=workspace-usage` | +| Feature | Flag | Description | +|---------------------------------|-------------------------------|----------------------------------------------------------------------------------------------------------------| +| Agent Lifecycle Hooks | `agent-lifecycle-hooks` | Enables chat lifecycle hook webhooks for agent chats. | +| AI Gateway Seat Exclusion | `ai-gateway-seat-exclusion` | Excludes AI Gateway (AI Bridge) usage from AI Governance seat consumption. | +| Auto-fill Template Parameters | `auto-fill-parameters` | Pre-fills a new workspace's parameter form with the values from the user's most recent build of that template. | +| Chat Advisor | `chat-advisor` | Enables the advisor tool for root agent chats. | +| Chat Virtual Desktop | `chat-virtual-desktop` | Enables virtual desktop and computer use provider for agents. | +| MCP HTTP Server Functionality | `mcp-server-http` | Enables the MCP HTTP server functionality. | +| MCP Tool Search | `mcp-tool-search` | Defers MCP tool schemas behind a searchable catalog in agent chats. | +| NATS Pubsub | `nats_pubsub` | Enables embedded NATS pubsub. | +| SMTP and Webhook Notifications | `notifications` | Sends notifications via SMTP and webhooks following certain events. | +| OAuth2 Provider Functionality | `oauth2` | Enables OAuth2 provider functionality. | +| Workspace Build Updates Channel | `workspace-build-updates` | Enables publishing workspace build updates to the all builds pubsub channel. | +| Workspace-Capable Licensing | `workspace-capable-licensing` | Counts only users holding the workspace-create permission toward the license seat limit. | +| Workspace Usage Tracking | `workspace-usage` | Enables the new workspace usage tracking. | + +In this version, `--experiments=*` enables no experiments; enable each experiment by name. ## Beta diff --git a/scripts/release/docs_update_feature_stages.sh b/scripts/release/docs_update_feature_stages.sh index dc781b66882..8b406796462 100755 --- a/scripts/release/docs_update_feature_stages.sh +++ b/scripts/release/docs_update_feature_stages.sh @@ -57,15 +57,22 @@ table="$( exit 0 fi - # The last column spells out the command: a flag in the opt-in set is - # enabled by `--experiments=*` as well as by name; every other flag must be - # named. Spelling it out keeps the table readable when the opt-in set is - # empty, which it is today. - echo "| Feature | Flag | Description | Enable with |" - echo "| ------- | ---- | ----------- | ----------- |" - jq -r '.experiments[] | "| \(.displayName) | `\(.id)` | \(.description) | \(if .safe then "`--experiments=*` or `--experiments=\(.id)`" else "`--experiments=\(.id)`" end) |"' "${experiments_json}" + echo "| Feature | Flag | Description |" + echo "| ------- | ---- | ----------- |" + jq -r '.experiments[] | "| \(.displayName) | `\(.id)` | \(.description) |"' "${experiments_json}" + echo + # Which flags `--experiments=*` enables is decided per version in + # codersdk.ExperimentsSafe and is usually a very short list, so it is + # stated as a sentence rather than repeated down a column. + safe=$(jq -r '[.experiments[] | select(.safe) | "`\(.id)`"] | join(", ")' "${experiments_json}") + if [[ -z "${safe}" ]]; then + echo "In this version, \`--experiments=*\` enables no experiments; enable each experiment by name." + else + echo "In this version, \`--experiments=*\` enables only: ${safe}. Enable any other experiment by name." + fi )" + # Collect beta features from the current docs/manifest.json. Keying on the # route path also dedupes routes that appear under more than one parent. declare -A beta_features=() beta_feature_descriptions=() From 0fee6acef403d257467db9907617781c345a4161 Mon Sep 17 00:00:00 2001 From: Nick Vigilante Date: Thu, 3 Sep 2026 14:09:06 -0400 Subject: [PATCH 6/6] docs: define safe and unsafe experiments; Safety column; wildcard plus named flags Assisted-by: AI --- docs/install/releases/feature-stages.md | 54 +++++++++++-------- scripts/release/docs_update_feature_stages.sh | 19 ++----- 2 files changed, 36 insertions(+), 37 deletions(-) diff --git a/docs/install/releases/feature-stages.md b/docs/install/releases/feature-stages.md index 702843d21d9..9000f324c42 100644 --- a/docs/install/releases/feature-stages.md +++ b/docs/install/releases/feature-stages.md @@ -39,24 +39,34 @@ staging deployment.
To enable early access features: Early access features are experiments, and each one has a flag. -Use the [Coder CLI](../../install/cli.md) `--experiments` flag on `coder server` to enable them: +Coder marks each experiment as **safe** or **unsafe** for opting in; the Safety column in the table below shows which is which for this version. -- Enable the opt-in set, which is only the experiments Coder marks as ready for opt-in in this version (the note under the table lists them; it does not enable every experiment): +- A safe experiment is ready for opt-in testing. + `--experiments=*` enables every safe experiment in this version and nothing else. +- An unsafe experiment is not ready for general use and must be named exactly; the wildcard never enables it. + Enable unsafe experiments only on a staging deployment. + +Use the [Coder CLI](../../install/cli.md) `--experiments` flag on `coder server`: + +- Enable every safe experiment: ```sh coder server --experiments=* ``` -- Enable specific experiments by flag, which is the only way to enable an experiment outside the opt-in set: +- Enable specific experiments by flag, safe or unsafe: ```sh coder server --experiments=flag-one,flag-two ``` -You can also set the `CODER_EXPERIMENTS` [environment variable](../../admin/setup/index.md) to the same value. +- Enable every safe experiment plus named unsafe ones in one flag: -Experiments that `*` does not enable are not ready for general use. -Enable them only on a staging deployment, and only by name. + ```sh + coder server --experiments=*,unsafe-flag-one,unsafe-flag-two + ``` + +You can also set the `CODER_EXPERIMENTS` [environment variable](../../admin/setup/index.md) to the same value. You can opt out of a feature after you've enabled it by removing its flag and restarting the server.
@@ -68,23 +78,21 @@ It is generated from the code, so it always matches the flags this version accep -| Feature | Flag | Description | -|---------------------------------|-------------------------------|----------------------------------------------------------------------------------------------------------------| -| Agent Lifecycle Hooks | `agent-lifecycle-hooks` | Enables chat lifecycle hook webhooks for agent chats. | -| AI Gateway Seat Exclusion | `ai-gateway-seat-exclusion` | Excludes AI Gateway (AI Bridge) usage from AI Governance seat consumption. | -| Auto-fill Template Parameters | `auto-fill-parameters` | Pre-fills a new workspace's parameter form with the values from the user's most recent build of that template. | -| Chat Advisor | `chat-advisor` | Enables the advisor tool for root agent chats. | -| Chat Virtual Desktop | `chat-virtual-desktop` | Enables virtual desktop and computer use provider for agents. | -| MCP HTTP Server Functionality | `mcp-server-http` | Enables the MCP HTTP server functionality. | -| MCP Tool Search | `mcp-tool-search` | Defers MCP tool schemas behind a searchable catalog in agent chats. | -| NATS Pubsub | `nats_pubsub` | Enables embedded NATS pubsub. | -| SMTP and Webhook Notifications | `notifications` | Sends notifications via SMTP and webhooks following certain events. | -| OAuth2 Provider Functionality | `oauth2` | Enables OAuth2 provider functionality. | -| Workspace Build Updates Channel | `workspace-build-updates` | Enables publishing workspace build updates to the all builds pubsub channel. | -| Workspace-Capable Licensing | `workspace-capable-licensing` | Counts only users holding the workspace-create permission toward the license seat limit. | -| Workspace Usage Tracking | `workspace-usage` | Enables the new workspace usage tracking. | - -In this version, `--experiments=*` enables no experiments; enable each experiment by name. +| Feature | Flag | Description | Safety | +|---------------------------------|-------------------------------|----------------------------------------------------------------------------------------------------------------|--------| +| Agent Lifecycle Hooks | `agent-lifecycle-hooks` | Enables chat lifecycle hook webhooks for agent chats. | Unsafe | +| AI Gateway Seat Exclusion | `ai-gateway-seat-exclusion` | Excludes AI Gateway (AI Bridge) usage from AI Governance seat consumption. | Unsafe | +| Auto-fill Template Parameters | `auto-fill-parameters` | Pre-fills a new workspace's parameter form with the values from the user's most recent build of that template. | Unsafe | +| Chat Advisor | `chat-advisor` | Enables the advisor tool for root agent chats. | Unsafe | +| Chat Virtual Desktop | `chat-virtual-desktop` | Enables virtual desktop and computer use provider for agents. | Unsafe | +| MCP HTTP Server Functionality | `mcp-server-http` | Enables the MCP HTTP server functionality. | Unsafe | +| MCP Tool Search | `mcp-tool-search` | Defers MCP tool schemas behind a searchable catalog in agent chats. | Unsafe | +| NATS Pubsub | `nats_pubsub` | Enables embedded NATS pubsub. | Unsafe | +| SMTP and Webhook Notifications | `notifications` | Sends notifications via SMTP and webhooks following certain events. | Unsafe | +| OAuth2 Provider Functionality | `oauth2` | Enables OAuth2 provider functionality. | Unsafe | +| Workspace Build Updates Channel | `workspace-build-updates` | Enables publishing workspace build updates to the all builds pubsub channel. | Unsafe | +| Workspace-Capable Licensing | `workspace-capable-licensing` | Counts only users holding the workspace-create permission toward the license seat limit. | Unsafe | +| Workspace Usage Tracking | `workspace-usage` | Enables the new workspace usage tracking. | Unsafe | ## Beta diff --git a/scripts/release/docs_update_feature_stages.sh b/scripts/release/docs_update_feature_stages.sh index 8b406796462..2b13a99248c 100755 --- a/scripts/release/docs_update_feature_stages.sh +++ b/scripts/release/docs_update_feature_stages.sh @@ -57,22 +57,13 @@ table="$( exit 0 fi - echo "| Feature | Flag | Description |" - echo "| ------- | ---- | ----------- |" - jq -r '.experiments[] | "| \(.displayName) | `\(.id)` | \(.description) |"' "${experiments_json}" - echo - # Which flags `--experiments=*` enables is decided per version in - # codersdk.ExperimentsSafe and is usually a very short list, so it is - # stated as a sentence rather than repeated down a column. - safe=$(jq -r '[.experiments[] | select(.safe) | "`\(.id)`"] | join(", ")' "${experiments_json}") - if [[ -z "${safe}" ]]; then - echo "In this version, \`--experiments=*\` enables no experiments; enable each experiment by name." - else - echo "In this version, \`--experiments=*\` enables only: ${safe}. Enable any other experiment by name." - fi + # Safety is decided per version in codersdk.ExperimentsSafe: a safe + # experiment is enabled by `--experiments=*`, an unsafe one only by name. + echo "| Feature | Flag | Description | Safety |" + echo "| ------- | ---- | ----------- | ------ |" + jq -r '.experiments[] | "| \(.displayName) | `\(.id)` | \(.description) | \(if .safe then "Safe" else "Unsafe" end) |"' "${experiments_json}" )" - # Collect beta features from the current docs/manifest.json. Keying on the # route path also dedupes routes that appear under more than one parent. declare -A beta_features=() beta_feature_descriptions=()