diff --git a/site/AGENTS.md b/site/AGENTS.md
index d89d2959c6..fe43e999b3 100644
--- a/site/AGENTS.md
+++ b/site/AGENTS.md
@@ -117,6 +117,15 @@ Debug logs and pprof dumps use the same job name and commit SHA convention.
- For JSX boolean props that are `true`, use the shorthand form
(``) instead of ``. The two are
equivalent; the shorthand is the React convention and reduces noise.
+- Use **sentence case** for user-facing UI labels: capitalize only the
+ first word and proper nouns (`Personal instructions`, `API key`), not
+ Title Case (`Personal Instructions`, `API Key`). Under
+ `src/pages/AgentsPage`, a Biome GritQL plugin
+ (`biome-rules/use-sentence-case-labels.grit`) enforces this for
+ `label`/`title`/`sectionLabel`/`aria-label` attributes and `label:`/
+ `title:` object properties. It cannot see labels rendered as JSX text
+ or built from variables, so those still need a human eye. Add genuine
+ proper nouns to the allowlist in that file.
- **Avoid unnecessary indirection.** Inline single-use module-level
constants, single-use aliases, and one-line helpers that just return a
single field at the call site. Do not create wrapper hooks that only
diff --git a/site/biome-rules/use-sentence-case-labels.grit b/site/biome-rules/use-sentence-case-labels.grit
new file mode 100644
index 0000000000..b87f1f6d06
--- /dev/null
+++ b/site/biome-rules/use-sentence-case-labels.grit
@@ -0,0 +1,35 @@
+// Enforce sentence case for user-facing UI labels.
+//
+// Flags Title Case in label-bearing JSX attributes (label, title,
+// sectionLabel, aria-label) and object-property labels (label:, title:,
+// sectionLabel:). Sentence case capitalizes only the first word and
+// proper nouns: "Personal instructions", not "Personal Instructions".
+//
+// Heuristic: a space followed by a capitalized word ([A-Z][a-z]). This
+// also catches acronym-prefixed Title Case like "MCP Servers". Genuine
+// multi-word proper nouns (e.g. "VS Code") are false positives and must
+// be added to the allowlist below.
+//
+// Diagnostic-only: Biome GritQL plugins cannot autofix yet.
+language js
+
+or {
+ JsxAttribute(name = $name) as $node where {
+ $name <: r"^(?:label|title|sectionLabel|aria-label)$",
+ $node <: contains JsxString() as $value
+ },
+ JsPropertyObjectMember() as $node where {
+ $node <: contains JsLiteralMemberName() as $key,
+ $key <: r"^(?:label|title|sectionLabel)$",
+ $node <: contains JsStringLiteralExpression() as $value
+ }
+} where {
+ $value <: r".* [A-Z][a-z].*",
+ // Allowlist: proper nouns / product names that are correctly multi-cap.
+ not $value <: r".*(?:VS Code|Coder Agents|GitHub Actions|JetBrains Fleet|Cmd/Ctrl).*",
+ register_diagnostic(
+ span = $value,
+ message = "Use sentence case for UI labels (e.g. \"Personal instructions\"), not Title Case. If this is a proper noun, add it to the allowlist in use-sentence-case-labels.grit.",
+ severity = "warn"
+ )
+}
diff --git a/site/biome.jsonc b/site/biome.jsonc
index 1721a0853c..6df5385abe 100644
--- a/site/biome.jsonc
+++ b/site/biome.jsonc
@@ -3,5 +3,16 @@
"files": {
"includes": ["!e2e/**/*Generated.ts", "!scripts/*.mjs"]
},
+ "overrides": [
+ {
+ "includes": [
+ "src/pages/AgentsPage/**/*.ts",
+ "src/pages/AgentsPage/**/*.tsx",
+ "!**/*.stories.tsx",
+ "!**/*.test.tsx"
+ ],
+ "plugins": ["./biome-rules/use-sentence-case-labels.grit"]
+ }
+ ],
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json"
}
diff --git a/site/src/pages/AgentsPage/components/ChatModelAdminPanel/ProviderForm.tsx b/site/src/pages/AgentsPage/components/ChatModelAdminPanel/ProviderForm.tsx
index fe817705f7..49678327ed 100644
--- a/site/src/pages/AgentsPage/components/ChatModelAdminPanel/ProviderForm.tsx
+++ b/site/src/pages/AgentsPage/components/ChatModelAdminPanel/ProviderForm.tsx
@@ -256,7 +256,7 @@ export const ProviderForm: FC = ({
>