Repository: gsd-build/gsd-2 (2026-03-26T20:59:35Z)

Issue:
chore: rename preferences.md to PREFERENCES.md for consistency with other .gsd/ state files
## Summary

All top-level `.gsd/` state files use uppercase naming (`DECISIONS.md`, `REQUIREMENTS.md`, `PROJECT.md`, `KNOWLEDGE.md`, `OVERRIDES.md`, `QUEUE.md`, `STATE.md`) except `preferences.md`. This inconsistency was noted during review of #2684.

## Scope

- 36 files reference `preferences.md` across source, tests, and docs
- Needs a migration path: loader should check `PREFERENCES.md` first, fall back to `preferences.md`
- User-facing docs and configuration guides need updating

## Priority

Low — cosmetic consistency. No functional impact.

## Context

Raised by reviewer on #2693 during code review of the preferences.md worktree sync fix.


Discussion:
👋 Thanks for opening this issue!

This was automatically flagged for maintainer review.

**Flag:** Cosmetic refactor

This issue proposes renaming `preferences.md` to `PREFERENCES.md` purely for naming consistency. Per VISION.md, cosmetic refactors (renaming variables/files, reordering, reformatting code that works) are explicitly not accepted because they create merge conflicts and review burden for zero user value. The functional behavior would be identical; this is pure churn.

Please review our [VISION.md](https://github.com/gsd-build/GSD-2/blob/main/VISION.md) and [CONTRIBUTING.md](https://github.com/gsd-build/GSD-2/blob/main/CONTRIBUTING.md) for project guidelines.

A maintainer will review this shortly. If you believe this was flagged in error, no action is needed — we'll take a look.

---
*This is an automated triage. The maintainers make all final decisions.*



Patch:
diff --git a/.plans/issue-575-dynamic-model-routing.md b/.plans/issue-575-dynamic-model-routing.md
index c68eab6bfe..b321904050 100644
--- a/.plans/issue-575-dynamic-model-routing.md
+++ b/.plans/issue-575-dynamic-model-routing.md
@@ -11,7 +11,7 @@ Users on capped plans (e.g., Claude Pro) exhaust weekly token limits in 15-20 ho
 ## Current Architecture
 
 ### What Exists
-- **Phase-based model config:** Users can set different models per phase via `preferences.md` (research, planning, execution, completion)
+- **Phase-based model config:** Users can set different models per phase via `PREFERENCES.md` (research, planning, execution, completion)
 - **Fallback chains:** Each phase supports `fallbacks: [model1, model2]` for error recovery
 - **Pre-dispatch hooks:** `PreDispatchResult` has a `model` field but it's **never applied** in `auto.ts` — this is a ready-made extension point
 - **Model registry:** `ModelRegistry.getAvailable()` provides all configured models with metadata
diff --git a/.plans/onboarding-detection-wizard.md b/.plans/onboarding-detection-wizard.md
index 0f6d0044fd..5d1e5a2e2b 100644
--- a/.plans/onboarding-detection-wizard.md
+++ b/.plans/onboarding-detection-wizard.md
@@ -134,7 +134,7 @@ Quick filesystem scan (no heavy reads):
 
 ### Task 1.4: `isFirstEverLaunch(): boolean`
 
-Returns `true` if `~/.gsd/` doesn't exist or has no `preferences.md`.
+Returns `true` if `~/.gsd/` doesn't exist or has no `PREFERENCES.md`.
 
 ---
 
@@ -298,7 +298,7 @@ Step 8: Advanced (collapsed by default, expandable)
 
 Step 9: Bootstrap .gsd/ structure
    - Creates .gsd/milestones/
-   - Creates .gsd/preferences.md (from wizard answers)
+   - Creates .gsd/PREFERENCES.md (from wizard answers)
    - Creates .gitignore entries
    - Seeds CONTEXT.md with detected project signals
    - Commits "chore: init gsd" (if commit_docs enabled)
diff --git a/.plans/preferences-wizard-completeness.md b/.plans/preferences-wizard-completeness.md
index 5709d7f215..bb6a353d07 100644
--- a/.plans/preferences-wizard-completeness.md
+++ b/.plans/preferences-wizard-completeness.md
@@ -42,7 +42,7 @@ The `/gsd prefs wizard` currently only configures 6 of 18+ preference fields. Us
 - Added missing keys to `orderedKeys` in `serializePreferencesToFrontmatter()`
 
 ### Group 6: Update Template & Docs ✓
-- Updated `templates/preferences.md` with new fields
+- Updated `templates/PREFERENCES.md` with new fields
 - Updated `docs/preferences-reference.md` with budget, notifications, git, hooks
 
 ### Group 7: Tests ✓
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 1aa93fe5a7..a0f0db894c 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -53,7 +53,7 @@ git rebase origin/main
 GSD uses worktree-based isolation for multi-developer work. If you're contributing with GSD running, enable team mode in your project preferences:
 
 ```yaml
-# .gsd/preferences.md
+# .gsd/PREFERENCES.md
 ---
 version: 1
 mode: team
diff --git a/README.md b/README.md
index d7c6245525..9ef22069d2 100644
--- a/README.md
+++ b/README.md
@@ -521,7 +521,7 @@ An auto-generated `index.html` shows all reports with progression metrics across
 
 ### Preferences
 
-GSD preferences live in `~/.gsd/preferences.md` (global) or `.gsd/preferences.md` (project). Manage with `/gsd prefs`.
+GSD preferences live in `~/.gsd/PREFERENCES.md` (global) or `.gsd/PREFERENCES.md` (project). Manage with `/gsd prefs`.
 
 ```yaml
 ---
@@ -672,7 +672,7 @@ The best practice for working in teams is to ensure unique milestone names acros
 
 ### Unique Milestone Names
 
-Create or amend your `.gsd/preferences.md` file within the repo to include `unique_milestone_ids: true` e.g.
+Create or amend your `.gsd/PREFERENCES.md` file within the repo to include `unique_milestone_ids: true` e.g.
 
 ```markdown
 ---
@@ -681,7 +681,7 @@ unique_milestone_ids: true
 ---
 ```
 
-With the above `.gitignore` set up, the `.gsd/preferences.md` file is checked into the repo ensuring all teammates use unique milestone names to avoid collisions.
+With the above `.gitignore` set up, the `.gsd/PREFERENCES.md` file is checked into the repo ensuring all teammates use unique milestone names to avoid collisions.
 
 Milestone names will now be generated with a 6 char random string appended e.g. instead of `M001` you'll get something like `M001-ush8s3`
 
@@ -689,7 +689,7 @@ Milestone names will now be generated with a 6 char random string appended e.g.
 
 1. Ensure you are not in the middle of any milestones (clean state)
 2. Update the `.gsd/` related entries in your `.gitignore` to follow the `Suggested .gitignore setup` section under `Working in teams` (ensure you are no longer blanket ignoring the whole `.gsd/` directory)
-3. Update your `.gsd/preferences.md` file within the repo as per section `Unique Milestone Names`
+3. Update your `.gsd/PREFERENCES.md` file within the repo as per section `Unique Milestone Names`
 4. If you want to update all your existing milestones use this prompt in GSD: `I have turned on unique milestone ids, please update all old milestone ids to use this new format e.g. M001-abc123 where abc123 is a random 6 char lowercase alpha numeric string. Update all references in all .gsd file contents, file names and directory names. Validate your work once done to ensure referential integrity.`
 5. Commit to git
 
diff --git a/docs/configuration.md b/docs/configuration.md
index 01a8f31940..d632e83154 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -1,14 +1,14 @@
 # Configuration
 
-GSD preferences live in `~/.gsd/preferences.md` (global) or `.gsd/preferences.md` (project-local). Manage interactively with `/gsd prefs`.
+GSD preferences live in `~/.gsd/PREFERENCES.md` (global) or `.gsd/PREFERENCES.md` (project-local). Manage interactively with `/gsd prefs`.
 
 ## `/gsd prefs` Commands
 
 | Command | Description |
 |---------|-------------|
 | `/gsd prefs` | Open the global preferences wizard (default) |
-| `/gsd prefs global` | Interactive wizard for global preferences (`~/.gsd/preferences.md`) |
-| `/gsd prefs project` | Interactive wizard for project preferences (`.gsd/preferences.md`) |
+| `/gsd prefs global` | Interactive wizard for global preferences (`~/.gsd/PREFERENCES.md`) |
+| `/gsd prefs project` | Interactive wizard for project preferences (`.gsd/PREFERENCES.md`) |
 | `/gsd prefs status` | Show current preference files, merged values, and skill resolution status |
 | `/gsd prefs wizard` | Alias for `/gsd prefs global` |
 | `/gsd prefs setup` | Alias for `/gsd prefs wizard` — creates preferences file if missing |
@@ -42,8 +42,8 @@ token_profile: balanced
 
 | Scope | Path | Applies to |
 |-------|------|-----------|
-| Global | `~/.gsd/preferences.md` | All projects |
-| Project | `.gsd/preferences.md` | Current project only |
+| Global | `~/.gsd/PREFERENCES.md` | All projects |
+| Project | `.gsd/PREFERENCES.md` | Current project only |
 
 **Merge behavior:**
 - **Scalar fields** (`skill_discovery`, `budget_ceiling`): project wins if defined
diff --git a/docs/parallel-orchestration.md b/docs/parallel-orchestration.md
index 6b611291d9..40463fa956 100644
--- a/docs/parallel-orchestration.md
+++ b/docs/parallel-orchestration.md
@@ -126,7 +126,7 @@ File overlaps are warnings, not blockers. Both milestones work in separate workt
 
 ## Configuration
 
-Add to `~/.gsd/preferences.md` or `.gsd/preferences.md`:
+Add to `~/.gsd/PREFERENCES.md` or `.gsd/PREFERENCES.md`:
 
 ```yaml
 ---
diff --git a/docs/remote-questions.md b/docs/remote-questions.md
index 8e4ce3555f..8078a9c56d 100644
--- a/docs/remote-questions.md
+++ b/docs/remote-questions.md
@@ -16,7 +16,7 @@ The setup wizard:
 3. Lists servers the bot belongs to (or lets you pick)
 4. Lists text channels in the selected server
 5. Sends a test message to confirm permissions
-6. Saves the configuration to `~/.gsd/preferences.md`
+6. Saves the configuration to `~/.gsd/PREFERENCES.md`
 
 **Bot requirements:**
 - A Discord bot application with a token (from [Discord Developer Portal](https://discord.com/developers/applications))
@@ -65,7 +65,7 @@ The setup wizard:
 
 ## Configuration
 
-Remote questions are configured in `~/.gsd/preferences.md`:
+Remote questions are configured in `~/.gsd/PREFERENCES.md`:
 
 ```yaml
 remote_questions:
diff --git a/docs/token-optimization.md b/docs/token-optimization.md
index a622869d18..5c5ea34664 100644
--- a/docs/token-optimization.md
+++ b/docs/token-optimization.md
@@ -257,7 +257,7 @@ models:
 ## How the Pieces Fit Together
 
 ```
-preferences.md
+PREFERENCES.md
   └─ token_profile: balanced
        ├─ resolveProfileDefaults() → model defaults + phase skip defaults
        ├─ resolveInlineLevel() → standard
diff --git a/docs/working-in-teams.md b/docs/working-in-teams.md
index 71956d5ff8..fd5476813b 100644
--- a/docs/working-in-teams.md
+++ b/docs/working-in-teams.md
@@ -9,7 +9,7 @@ GSD supports multi-user workflows where several developers work on the same repo
 The simplest way to configure GSD for team use is to set `mode: team` in your project preferences. This enables unique milestone IDs, push branches, and pre-merge checks in one setting:
 
 ```yaml
-# .gsd/preferences.md (project-level, committed to git)
+# .gsd/PREFERENCES.md (project-level, committed to git)
 ---
 version: 1
 mode: team
@@ -38,7 +38,7 @@ Share planning artifacts (milestones, roadmaps, decisions) while keeping runtime
 ```
 
 **What gets shared** (committed to git):
-- `.gsd/preferences.md` — project preferences
+- `.gsd/PREFERENCES.md` — project preferences
 - `.gsd/PROJECT.md` — living project description
 - `.gsd/REQUIREMENTS.md` — requirement contract
 - `.gsd/DECISIONS.md` — architectural decisions
@@ -50,7 +50,7 @@ Share planning artifacts (milestones, roadmaps, decisions) while keeping runtime
 ### 3. Commit the Preferences
 
 ```bash
-git add .gsd/preferences.md
+git add .gsd/PREFERENCES.md
 git commit -m "chore: enable GSD team workflow"
 ```
 
@@ -71,7 +71,7 @@ If you have an existing project with `.gsd/` blanket-ignored:
 
 1. Ensure no milestones are in progress (clean state)
 2. Update `.gitignore` to use the selective pattern above
-3. Add `unique_milestone_ids: true` to `.gsd/preferences.md`
+3. Add `unique_milestone_ids: true` to `.gsd/PREFERENCES.md`
 4. Optionally rename existing milestones to use unique IDs:
    ```
    I have turned on unique milestone ids, please update all old milestone
diff --git a/mintlify-docs/guides/configuration.mdx b/mintlify-docs/guides/configuration.mdx
index cd74a40a03..4961d66b91 100644
--- a/mintlify-docs/guides/configuration.mdx
+++ b/mintlify-docs/guides/configuration.mdx
@@ -3,7 +3,7 @@ title: "Configuration"
 description: "Preferences, model selection, MCP servers, hooks, and all settings."
 ---
 
-GSD preferences live in `~/.gsd/preferences.md` (global) or `.gsd/preferences.md` (project-local). Manage interactively with `/gsd prefs`.
+GSD preferences live in `~/.gsd/PREFERENCES.md` (global) or `.gsd/PREFERENCES.md` (project-local). Manage interactively with `/gsd prefs`.
 
 ## Preferences commands
 
@@ -40,8 +40,8 @@ token_profile: balanced
 
 | Scope | Path | Applies to |
 |-------|------|-----------|
-| Global | `~/.gsd/preferences.md` | All projects |
-| Project | `.gsd/preferences.md` | Current project only |
+| Global | `~/.gsd/PREFERENCES.md` | All projects |
+| Project | `.gsd/PREFERENCES.md` | Current project only |
 
 **Merge behavior:**
 - **Scalar fields** — project wins if defined
diff --git a/mintlify-docs/guides/working-in-teams.mdx b/mintlify-docs/guides/working-in-teams.mdx
index 17f6f0c1d3..72baa19e20 100644
--- a/mintlify-docs/guides/working-in-teams.mdx
+++ b/mintlify-docs/guides/working-in-teams.mdx
@@ -10,7 +10,7 @@ GSD supports multi-user workflows where several developers work on the same repo
 ### 1. Set team mode
 
 ```yaml
-# .gsd/preferences.md (project-level, committed to git)
+# .gsd/PREFERENCES.md (project-level, committed to git)
 ---
 version: 1
 mode: team
@@ -43,7 +43,7 @@ Share planning artifacts while keeping runtime files local:
 ### 3. Commit
 
 ```bash
-git add .gsd/preferences.md
+git add .gsd/PREFERENCES.md
 git commit -m "chore: enable GSD team workflow"
 ```
 
diff --git a/src/remote-questions-config.ts b/src/remote-questions-config.ts
index e7f0d8cae2..7a66543a49 100644
--- a/src/remote-questions-config.ts
+++ b/src/remote-questions-config.ts
@@ -16,7 +16,7 @@ import { appRoot } from "./app-paths.js";
 // boundary — this file is compiled by tsc, but preferences.ts is loaded
 // via jiti at runtime. Importing it as .js fails because no .js exists
 // in dist/. See #592, #1110.
-const GLOBAL_PREFERENCES_PATH = join(appRoot, "preferences.md");
+const GLOBAL_PREFERENCES_PATH = join(appRoot, "PREFERENCES.md");
 
 export function saveRemoteQuestionsConfig(channel: "slack" | "discord" | "telegram", channelId: string): void {
   const prefsPath = GLOBAL_PREFERENCES_PATH;
diff --git a/src/resources/extensions/gsd/commands-prefs-wizard.ts b/src/resources/extensions/gsd/commands-prefs-wizard.ts
index 46e4b0a376..f006cca61c 100644
--- a/src/resources/extensions/gsd/commands-prefs-wizard.ts
+++ b/src/resources/extensions/gsd/commands-prefs-wizard.ts
@@ -771,7 +771,7 @@ export async function ensurePreferencesFile(
   scope: "global" | "project",
 ): Promise<void> {
   if (!existsSync(path)) {
-    const template = await loadFile(join(dirname(fileURLToPath(import.meta.url)), "templates", "preferences.md"));
+    const template = await loadFile(join(dirname(fileURLToPath(import.meta.url)), "templates", "PREFERENCES.md"));
     if (!template) {
       ctx.ui.notify("Could not load GSD preferences template.", "error");
       return;
diff --git a/src/resources/extensions/gsd/detection.ts b/src/resources/extensions/gsd/detection.ts
index 7507d427db..0bf69ddc92 100644
--- a/src/resources/extensions/gsd/detection.ts
+++ b/src/resources/extensions/gsd/detection.ts
@@ -359,8 +359,8 @@ function detectV2Gsd(basePath: string): V2Detection | null {
   if (!existsSync(gsdPath)) return null;
 
   const hasPreferences =
-    existsSync(join(gsdPath, "preferences.md")) ||
-    existsSync(join(gsdPath, "PREFERENCES.md"));
+    existsSync(join(gsdPath, "PREFERENCES.md")) ||
+    existsSync(join(gsdPath, "preferences.md"));
 
   const hasContext = existsSync(join(gsdPath, "CONTEXT.md"));
 
@@ -714,8 +714,8 @@ function detectVerificationCommands(
  */
 export function hasGlobalSetup(): boolean {
   return (
-    existsSync(join(gsdHome, "preferences.md")) ||
-    existsSync(join(gsdHome, "PREFERENCES.md"))
+    existsSync(join(gsdHome, "PREFERENCES.md")) ||
+    existsSync(join(gsdHome, "preferences.md"))
   );
 }
 
@@ -728,8 +728,8 @@ export function isFirstEverLaunch(): boolean {
 
   // If we have preferences, not first launch
   if (
-    existsSync(join(gsdHome, "preferences.md")) ||
-    existsSync(join(gsdHome, "PREFERENCES.md"))
+    existsSync(join(gsdHome, "PREFERENCES.md")) ||
+    existsSync(join(gsdHome, "preferences.md"))
   ) {
     return false;
   }
diff --git a/src/resources/extensions/gsd/docs/preferences-reference.md b/src/resources/extensions/gsd/docs/preferences-reference.md
index 5afeff2bdc..27a994a370 100644
--- a/src/resources/extensions/gsd/docs/preferences-reference.md
+++ b/src/resources/extensions/gsd/docs/preferences-reference.md
@@ -1,6 +1,6 @@
 # GSD Preferences Reference
 
-Full documentation for `~/.gsd/preferences.md` (global) and `.gsd/preferences.md` (project).
+Full documentation for `~/.gsd/PREFERENCES.md` (global) and `.gsd/PREFERENCES.md` (project).
 
 ---
 
@@ -51,8 +51,8 @@ skill_rules: []
 
 Preferences are loaded from two locations and merged:
 
-1. **Global:** `~/.gsd/preferences.md` — applies to all projects
-2. **Project:** `.gsd/preferences.md` — applies to the current project only
+1. **Global:** `~/.gsd/PREFERENCES.md` — applies to all projects
+2. **Project:** `.gsd/PREFERENCES.md` — applies to the current project only
 
 **Merge behavior** (see `mergePreferences()` in `preferences.ts`):
 
diff --git a/src/resources/extensions/gsd/gitignore.ts b/src/resources/extensions/gsd/gitignore.ts
index 71cf7c2ab1..da4b2ee915 100644
--- a/src/resources/extensions/gsd/gitignore.ts
+++ b/src/resources/extensions/gsd/gitignore.ts
@@ -1,8 +1,8 @@
 /**
- * GSD bootstrappers for .gitignore and preferences.md
+ * GSD bootstrappers for .gitignore and PREFERENCES.md
  *
  * Ensures baseline .gitignore exists with universally-correct patterns.
- * Creates an empty preferences.md template if it doesn't exist.
+ * Creates an empty PREFERENCES.md template if it doesn't exist.
  * Both idempotent — non-destructive if already present.
  */
 
@@ -216,16 +216,16 @@ export function untrackRuntimeFiles(basePath: string): void {
 }
 
 /**
- * Ensure basePath/.gsd/preferences.md exists as an empty template.
+ * Ensure basePath/.gsd/PREFERENCES.md exists as an empty template.
  * Creates the file with frontmatter only if it doesn't exist.
  * Returns true if created, false if already exists.
  *
- * Checks both lowercase (canonical) and uppercase (legacy) to avoid
- * creating a duplicate when an uppercase file already exists.
+ * Checks both uppercase (canonical) and lowercase (legacy) to avoid
+ * creating a duplicate when a lowercase file already exists.
  */
 export function ensurePreferences(basePath: string): boolean {
-  const preferencesPath = join(gsdRoot(basePath), "preferences.md");
-  const legacyPath = join(gsdRoot(basePath), "PREFERENCES.md");
+  const preferencesPath = join(gsdRoot(basePath), "PREFERENCES.md");
+  const legacyPath = join(gsdRoot(basePath), "preferences.md");
 
   if (existsSync(preferencesPath) || existsSync(legacyPath)) {
     return false;
diff --git a/src/resources/extensions/gsd/init-wizard.ts b/src/resources/extensions/gsd/init-wizard.ts
index de634ce995..f1a077dd83 100644
--- a/src/resources/extensions/gsd/init-wizard.ts
+++ b/src/resources/extensions/gsd/init-wizard.ts
@@ -422,9 +422,9 @@ function bootstrapGsdDirectory(
   const gsd = gsdRoot(basePath);
   mkdirSync(join(gsd, "milestones"), { recursive: true });
 
-  // Write preferences.md from wizard answers
+  // Write PREFERENCES.md from wizard answers
   const preferencesContent = buildPreferencesFile(prefs);
-  writeFileSync(join(gsd, "preferences.md"), preferencesContent, "utf-8");
+  writeFileSync(join(gsd, "PREFERENCES.md"), preferencesContent, "utf-8");
 
   // Seed CONTEXT.md with detected project signals
   const contextContent = buildContextSeed(signals);
diff --git a/src/resources/extensions/gsd/preferences-models.ts b/src/resources/extensions/gsd/preferences-models.ts
index 303c434709..2100b16e4a 100644
--- a/src/resources/extensions/gsd/preferences-models.ts
+++ b/src/resources/extensions/gsd/preferences-models.ts
@@ -308,7 +308,7 @@ export function resolveContextSelection(): import("./types.js").ContextSelection
 }
 
 /**
- * Resolve the search provider preference from preferences.md.
+ * Resolve the search provider preference from PREFERENCES.md.
  * Returns undefined if not configured (caller falls back to existing behavior).
  */
 export function resolveSearchProviderFromPreferences(): GSDPreferences["search_provider"] | undefined {
diff --git a/src/resources/extensions/gsd/preferences.ts b/src/resources/extensions/gsd/preferences.ts
index 0b0b82927e..7e25ede0a3 100644
--- a/src/resources/extensions/gsd/preferences.ts
+++ b/src/resources/extensions/gsd/preferences.ts
@@ -87,7 +87,7 @@ function gsdHome(): string {
 }
 
 function globalPreferencesPath(): string {
-  return join(gsdHome(), "preferences.md");
+  return join(gsdHome(), "PREFERENCES.md");
 }
 
 function legacyGlobalPreferencesPath(): string {
@@ -95,15 +95,15 @@ function legacyGlobalPreferencesPath(): string {
 }
 
 function projectPreferencesPath(): string {
-  return join(gsdRoot(process.cwd()), "preferences.md");
+  return join(gsdRoot(process.cwd()), "PREFERENCES.md");
 }
-// Bootstrap in gitignore.ts historically created PREFERENCES.md (uppercase) by mistake.
-// Check uppercase as a fallback so those files aren't silently ignored.
-function globalPreferencesPathUppercase(): string {
-  return join(gsdHome(), "PREFERENCES.md");
+// Legacy: older versions used lowercase preferences.md.
+// Check lowercase as a fallback so those files aren't silently ignored.
+function globalPreferencesPathLegacy(): string {
+  return join(gsdHome(), "preferences.md");
 }
-function projectPreferencesPathUppercase(): string {
-  return join(gsdRoot(process.cwd()), "PREFERENCES.md");
+function projectPreferencesPathLegacy(): string {
+  return join(gsdRoot(process.cwd()), "preferences.md");
 }
 
 export function getGlobalGSDPreferencesPath(): string {
@@ -122,13 +122,13 @@ export function getProjectGSDPreferencesPath(): string {
 
 export function loadGlobalGSDPreferences(): LoadedGSDPreferences | null {
   return loadPreferencesFile(globalPreferencesPath(), "global")
-    ?? loadPreferencesFile(globalPreferencesPathUppercase(), "global")
+    ?? loadPreferencesFile(globalPreferencesPathLegacy(), "global")
     ?? loadPreferencesFile(legacyGlobalPreferencesPath(), "global");
 }
 
 export function loadProjectGSDPreferences(): LoadedGSDPreferences | null {
   return loadPreferencesFile(projectPreferencesPath(), "project")
-    ?? loadPreferencesFile(projectPreferencesPathUppercase(), "project");
+    ?? loadPreferencesFile(projectPreferencesPathLegacy(), "project");
 }
 
 export function loadEffectiveGSDPreferences(): LoadedGSDPreferences | null {
@@ -223,7 +223,7 @@ export function parsePreferencesMarkdown(content: string): GSDPreferences | null
 
   if (!_warnedUnrecognizedFormat) {
     _warnedUnrecognizedFormat = true;
-    console.warn("[parsePreferencesMarkdown] preferences.md exists but uses an unrecognized format — skipping.");
+    console.warn("[parsePreferencesMarkdown] PREFERENCES.md exists but uses an unrecognized format — skipping.");
   }
   return null;
 }
@@ -502,7 +502,7 @@ export function resolvePreDispatchHooks(): PreDispatchHookConfig[] {
  * Resolve the effective git isolation mode from preferences.
  * Returns "none" (default), "worktree", or "branch".
  *
- * Default is "none" so GSD works out of the box without preferences.md.
+ * Default is "none" so GSD works out of the box without PREFERENCES.md.
  * Worktree isolation requires explicit opt-in because it depends on git
  * branch infrastructure that must be set up before use.
  */
diff --git a/src/resources/extensions/gsd/prompts/system.md b/src/resources/extensions/gsd/prompts/system.md
index 44671a14fd..0d1eb0ada0 100644
--- a/src/resources/extensions/gsd/prompts/system.md
+++ b/src/resources/extensions/gsd/prompts/system.md
@@ -92,7 +92,7 @@ Titles live inside file content (headings, frontmatter), not in file or director
 
 ### Isolation Model
 
-Auto-mode supports three isolation modes (configured in `.gsd/preferences.md` under `taskIsolation.mode`):
+Auto-mode supports three isolation modes (configured in `.gsd/PREFERENCES.md` under `taskIsolation.mode`):
 
 - **worktree** (default): Work happens in `.gsd/worktrees/<MID>/`, a full git worktree on the `milestone/<MID>` branch. Each worktree has its own working copy and `.gsd/` directory. Squash-merged back to the integration branch on milestone completion.
 - **branch**: Work happens in the project root on a `milestone/<MID>` branch. No worktree directory — files are checked out in-place.
diff --git a/src/resources/extensions/gsd/rule-registry.ts b/src/resources/extensions/gsd/rule-registry.ts
index e8ac7c13e6..e618936066 100644
--- a/src/resources/extensions/gsd/rule-registry.ts
+++ b/src/resources/extensions/gsd/rule-registry.ts
@@ -524,7 +524,7 @@ export class RuleRegistry {
   formatHookStatus(): string {
     const entries = this.getHookStatus();
     if (entries.length === 0) {
-      return "No hooks configured. Add post_unit_hooks or pre_dispatch_hooks to .gsd/preferences.md";
+      return "No hooks configured. Add post_unit_hooks or pre_dispatch_hooks to .gsd/PREFERENCES.md";
     }
 
     const lines: string[] = ["Configured Hooks:", ""];
diff --git a/src/resources/extensions/gsd/templates/preferences.md b/src/resources/extensions/gsd/templates/PREFERENCES.md
similarity index 100%
rename from src/resources/extensions/gsd/templates/preferences.md
rename to src/resources/extensions/gsd/templates/PREFERENCES.md
diff --git a/src/resources/extensions/search-the-web/native-search.ts b/src/resources/extensions/search-the-web/native-search.ts
index 0f78055283..5debc2b1bf 100644
--- a/src/resources/extensions/search-the-web/native-search.ts
+++ b/src/resources/extensions/search-the-web/native-search.ts
@@ -28,7 +28,7 @@ export const MAX_NATIVE_SEARCHES_PER_SESSION = 15;
 
 /** When true, skip native web search injection and keep Brave/custom tools active on Anthropic. */
 export function preferBraveSearch(): boolean {
-  // preferences.md takes priority over env var
+  // PREFERENCES.md takes priority over env var
   const prefsPref = resolveSearchProviderFromPreferences();
   if (prefsPref === "brave" || prefsPref === "tavily" || prefsPref === "ollama") return true;
   if (prefsPref === "native") return false;
diff --git a/src/resources/extensions/search-the-web/provider.ts b/src/resources/extensions/search-the-web/provider.ts
index e1f8b23128..cf7ae5b980 100644
--- a/src/resources/extensions/search-the-web/provider.ts
+++ b/src/resources/extensions/search-the-web/provider.ts
@@ -105,7 +105,7 @@ export function resolveSearchProvider(overridePreference?: string): SearchProvid
   if (overridePreference && VALID_PREFERENCES.has(overridePreference)) {
     pref = overridePreference as SearchProviderPreference
   } else {
-    // preferences.md takes priority over auth.json
+    // PREFERENCES.md takes priority over auth.json
     const mdPref = resolveSearchProviderFromPreferences()
     if (mdPref && mdPref !== 'auto' && mdPref !== 'native') {
       pref = mdPref as SearchProviderPreference
diff --git a/src/web/hooks-service.ts b/src/web/hooks-service.ts
index b8142dda4a..9eeac12763 100644
--- a/src/web/hooks-service.ts
+++ b/src/web/hooks-service.ts
@@ -38,7 +38,7 @@ export async function collectHooksData(projectCwdOverride?: string): Promise<Hoo
   }
 
   // getHookStatus() internally calls resolvePostUnitHooks() and resolvePreDispatchHooks()
-  // from preferences.ts, which read from process.cwd()/.gsd/preferences.md.
+  // from preferences.ts, which read from process.cwd()/.gsd/PREFERENCES.md.
   // We set cwd to projectCwd so preferences resolution finds the right files.
   // In a cold child process, cycleCounts is empty, so activeCycles will be {}.
   const script = [
diff --git a/web/app/api/experimental/route.ts b/web/app/api/experimental/route.ts
index 81b3ec6f7a..ea87edcaef 100644
--- a/web/app/api/experimental/route.ts
+++ b/web/app/api/experimental/route.ts
@@ -11,7 +11,7 @@ const NO_STORE = { "Cache-Control": "no-store" } as const
 // ─── Helpers (same pattern as remote-questions/route.ts) ─────────────────────
 
 function getPreferencesPath(): string {
-  return join(homedir(), ".gsd", "preferences.md")
+  return join(homedir(), ".gsd", "PREFERENCES.md")
 }
 
 function parseFrontmatter(content: string): { data: Record<string, unknown>; body: string } {
diff --git a/web/app/api/remote-questions/route.ts b/web/app/api/remote-questions/route.ts
index ae6e1cf4e3..0215e08b39 100644
--- a/web/app/api/remote-questions/route.ts
+++ b/web/app/api/remote-questions/route.ts
@@ -84,7 +84,7 @@ function maskToken(token: string): string {
 // ─── Helpers ──────────────────────────────────────────────────────────────────
 
 function getPreferencesPath(): string {
-  return join(homedir(), ".gsd", "preferences.md")
+  return join(homedir(), ".gsd", "PREFERENCES.md")
 }
 
 function clamp(value: number | undefined, defaultVal: number, min: number, max: number): number {
diff --git a/web/components/gsd/settings-panels.tsx b/web/components/gsd/settings-panels.tsx
index c80bf7d8ac..ea64dda5fe 100644
--- a/web/components/gsd/settings-panels.tsx
+++ b/web/components/gsd/settings-panels.tsx
@@ -1200,7 +1200,7 @@ export function ExperimentalPanel() {
       {data && (
         <p className="text-[11px] text-muted-foreground">
           Changes are written to{" "}
-          <span className="font-mono">{prefs?.path ?? "~/.gsd/preferences.md"}</span>
+          <span className="font-mono">{prefs?.path ?? "~/.gsd/PREFERENCES.md"}</span>
           {" "}and take effect on the next session.
         </p>
       )}


Test patch:
diff --git a/src/resources/extensions/gsd/tests/claude-import-tui.test.ts b/src/resources/extensions/gsd/tests/claude-import-tui.test.ts
index c3728cbceb..53a4284faa 100644
--- a/src/resources/extensions/gsd/tests/claude-import-tui.test.ts
+++ b/src/resources/extensions/gsd/tests/claude-import-tui.test.ts
@@ -126,7 +126,7 @@ describe(
 
 		before(() => {
 			tempDir = mkdtempSync(join(tmpdir(), 'gsd-tui-test-'));
-			prefsPath = join(tempDir, 'preferences.md');
+			prefsPath = join(tempDir, 'PREFERENCES.md');
 			prefs = { version: 1 };
 		});
 
diff --git a/src/resources/extensions/gsd/tests/detection.test.ts b/src/resources/extensions/gsd/tests/detection.test.ts
index b1a1647dc8..c1efd9d0fa 100644
--- a/src/resources/extensions/gsd/tests/detection.test.ts
+++ b/src/resources/extensions/gsd/tests/detection.test.ts
@@ -99,7 +99,7 @@ test("detectProjectState: detects preferences in .gsd/", (t) => {
   t.after(() => cleanup(dir));
 
   mkdirSync(join(dir, ".gsd", "milestones"), { recursive: true });
-  writeFileSync(join(dir, ".gsd", "preferences.md"), "---\nversion: 1\n---\n", "utf-8");
+  writeFileSync(join(dir, ".gsd", "PREFERENCES.md"), "---\nversion: 1\n---\n", "utf-8");
   const result = detectProjectState(dir);
   assert.ok(result.v2);
   assert.equal(result.v2!.hasPreferences, true);
diff --git a/src/resources/extensions/gsd/tests/doctor-git.test.ts b/src/resources/extensions/gsd/tests/doctor-git.test.ts
index cdffe17ae3..9b87d27142 100644
--- a/src/resources/extensions/gsd/tests/doctor-git.test.ts
+++ b/src/resources/extensions/gsd/tests/doctor-git.test.ts
@@ -64,11 +64,11 @@ _None_
   return dir;
 }
 
-/** Write a .gsd/preferences.md with the given git isolation mode. */
+/** Write a .gsd/PREFERENCES.md with the given git isolation mode. */
 function writePreferencesFile(dir: string, isolation: "none" | "worktree" | "branch"): void {
   const gsdDir = join(dir, ".gsd");
   mkdirSync(gsdDir, { recursive: true });
-  writeFileSync(join(gsdDir, "preferences.md"), `---\ngit:\n  isolation: "${isolation}"\n---\n`);
+  writeFileSync(join(gsdDir, "PREFERENCES.md"), `---\ngit:\n  isolation: "${isolation}"\n---\n`);
 }
 
 /** Create a repo with an in-progress milestone. */
@@ -302,7 +302,7 @@ describe('doctor-git', async () => {
     // ─── Test 7: none-mode skips orphaned worktree check ───────────────
     // NOTE: loadEffectiveGSDPreferences() resolves PROJECT_PREFERENCES_PATH
     // at module load time from process.cwd(). We write the prefs file to
-    // the test runner's cwd .gsd/preferences.md and clean up afterwards.
+    // the test runner's cwd .gsd/PREFERENCES.md and clean up afterwards.
     if (process.platform !== "win32") {
     test('none-mode skips orphaned worktree', async () => {
       const dir = createRepoWithCompletedMilestone();
@@ -409,7 +409,7 @@ describe('doctor-git', async () => {
       cleanups.push(dir);
 
       run("git branch trunk", dir);
-      writeFileSync(join(dir, ".gsd", "preferences.md"), `---\ngit:\n  isolation: "worktree"\n  main_branch: "trunk"\n---\n`);
+      writeFileSync(join(dir, ".gsd", "PREFERENCES.md"), `---\ngit:\n  isolation: "worktree"\n  main_branch: "trunk"\n---\n`);
 
       const metaPath = join(dir, ".gsd", "milestones", "M001", "M001-META.json");
       writeFileSync(metaPath, JSON.stringify({ integrationBranch: "feat/does-not-exist" }, null, 2));
diff --git a/src/resources/extensions/gsd/tests/doctor-proactive.test.ts b/src/resources/extensions/gsd/tests/doctor-proactive.test.ts
index 217769f686..29be69b331 100644
--- a/src/resources/extensions/gsd/tests/doctor-proactive.test.ts
+++ b/src/resources/extensions/gsd/tests/doctor-proactive.test.ts
@@ -297,7 +297,7 @@ describe('doctor-proactive', async () => {
       cleanups.push(dir);
 
       run("git branch trunk", dir);
-      writeFileSync(join(dir, ".gsd", "preferences.md"), `---\ngit:\n  main_branch: "trunk"\n---\n`);
+      writeFileSync(join(dir, ".gsd", "PREFERENCES.md"), `---\ngit:\n  main_branch: "trunk"\n---\n`);
       const metaPath = join(dir, ".gsd", "milestones", "M001", "M001-META.json");
       writeFileSync(metaPath, JSON.stringify({ integrationBranch: "feature/missing" }, null, 2));
 
diff --git a/src/resources/extensions/gsd/tests/doctor-providers.test.ts b/src/resources/extensions/gsd/tests/doctor-providers.test.ts
index c27d92e179..96f6abd3e8 100644
--- a/src/resources/extensions/gsd/tests/doctor-providers.test.ts
+++ b/src/resources/extensions/gsd/tests/doctor-providers.test.ts
@@ -419,7 +419,7 @@ test("runProviderChecks uses provider-qualified anthropic-vertex model IDs", ()
   const repo = realpathSync(mkdtempSync(join(tmpdir(), "gsd-providers-vertex-prefix-repo-")));
   mkdirSync(join(repo, ".gsd"), { recursive: true });
   writeFileSync(
-    join(repo, ".gsd", "preferences.md"),
+    join(repo, ".gsd", "PREFERENCES.md"),
     [
       "---",
       "models:",
@@ -454,7 +454,7 @@ test("runProviderChecks uses object provider field for anthropic-vertex models",
   const repo = realpathSync(mkdtempSync(join(tmpdir(), "gsd-providers-vertex-provider-repo-")));
   mkdirSync(join(repo, ".gsd"), { recursive: true });
   writeFileSync(
-    join(repo, ".gsd", "preferences.md"),
+    join(repo, ".gsd", "PREFERENCES.md"),
     [
       "---",
       "models:",
diff --git a/src/resources/extensions/gsd/tests/git-service.test.ts b/src/resources/extensions/gsd/tests/git-service.test.ts
index 3e4b3ffdaa..d6c0f3b8f9 100644
--- a/src/resources/extensions/gsd/tests/git-service.test.ts
+++ b/src/resources/extensions/gsd/tests/git-service.test.ts
@@ -1142,7 +1142,7 @@ describe('git-service', async () => {
     mkdirSync(join(repo, ".gsd", "runtime"), { recursive: true });
     mkdirSync(join(repo, ".gsd", "activity"), { recursive: true });
     writeFileSync(join(repo, ".gsd", "milestones", "M001", "ROADMAP.md"), "# Roadmap");
-    writeFileSync(join(repo, ".gsd", "preferences.md"), "---\nversion: 1\n---");
+    writeFileSync(join(repo, ".gsd", "PREFERENCES.md"), "---\nversion: 1\n---");
     writeFileSync(join(repo, ".gsd", "STATE.md"), "# State");
     writeFileSync(join(repo, ".gsd", "runtime", "units.json"), "{}");
     writeFileSync(join(repo, ".gsd", "activity", "log.jsonl"), "{}");
diff --git a/src/resources/extensions/gsd/tests/init-wizard.test.ts b/src/resources/extensions/gsd/tests/init-wizard.test.ts
index c3350a5a43..c173006824 100644
--- a/src/resources/extensions/gsd/tests/init-wizard.test.ts
+++ b/src/resources/extensions/gsd/tests/init-wizard.test.ts
@@ -123,7 +123,7 @@ test("init-wizard: v2 .gsd/ preferences detected", (t) => {
   const dir = makeTempDir("prefs-detect");
   try {
     mkdirSync(join(dir, ".gsd", "milestones"), { recursive: true });
-    writeFileSync(join(dir, ".gsd", "preferences.md"), "---\nversion: 1\nmode: solo\n---\n", "utf-8");
+    writeFileSync(join(dir, ".gsd", "PREFERENCES.md"), "---\nversion: 1\nmode: solo\n---\n", "utf-8");
 
     const detection = detectProjectState(dir);
     assert.ok(detection.v2);
diff --git a/src/resources/extensions/gsd/tests/none-mode-gates.test.ts b/src/resources/extensions/gsd/tests/none-mode-gates.test.ts
index bdadcfc1dc..0a002556d5 100644
--- a/src/resources/extensions/gsd/tests/none-mode-gates.test.ts
+++ b/src/resources/extensions/gsd/tests/none-mode-gates.test.ts
@@ -8,7 +8,7 @@
  * Uses the writeRunnerPreferences pattern from doctor-git.test.ts:
  * PROJECT_PREFERENCES_PATH is a module-level constant frozen at import
  * time, so process.chdir() won't redirect preference loading. We write
- * prefs to the runner's cwd .gsd/preferences.md and clean up in finally.
+ * prefs to the runner's cwd .gsd/PREFERENCES.md and clean up in finally.
  */
 
 import { mkdirSync, writeFileSync, rmSync, existsSync } from "node:fs";
@@ -24,7 +24,7 @@ import assert from 'node:assert/strict';
 
 // --- Preferences helpers (same pattern as doctor-git.test.ts K001) ---
 
-const RUNNER_PREFS_PATH = join(process.cwd(), ".gsd", "preferences.md");
+const RUNNER_PREFS_PATH = join(process.cwd(), ".gsd", "PREFERENCES.md");
 
 function writeRunnerPreferences(isolation: "none" | "worktree" | "branch"): void {
   mkdirSync(join(process.cwd(), ".gsd"), { recursive: true });
@@ -72,12 +72,12 @@ try {
 
 // Test 4: shouldUseWorktreeIsolation returns false for no prefs (default: none)
 // Worktree isolation requires explicit opt-in — default is "none" so GSD
-// works out of the box without preferences.md (#2480).
+// works out of the box without PREFERENCES.md (#2480).
 // Skip if global prefs exist — they override the default and this test
-// cannot control ~/.gsd/preferences.md.
+// cannot control ~/.gsd/PREFERENCES.md.
 
 test('shouldUseWorktreeIsolation returns false for no prefs (default: none)', () => {
-  const globalPrefsExist = existsSync(join(homedir(), ".gsd", "preferences.md"))
+  const globalPrefsExist = existsSync(join(homedir(), ".gsd", "PREFERENCES.md"))
     || existsSync(join(homedir(), ".gsd", "PREFERENCES.md"));
   if (!globalPrefsExist) {
     try {
@@ -91,9 +91,9 @@ test('shouldUseWorktreeIsolation returns false for no prefs (default: none)', ()
   }
 });
 
-// Test 5: getIsolationMode returns "none" when no preferences.md exists (#2480)
+// Test 5: getIsolationMode returns "none" when no PREFERENCES.md exists (#2480)
 test('getIsolationMode returns "none" with no prefs (default)', () => {
-  const globalPrefsExist = existsSync(join(homedir(), ".gsd", "preferences.md"))
+  const globalPrefsExist = existsSync(join(homedir(), ".gsd", "PREFERENCES.md"))
     || existsSync(join(homedir(), ".gsd", "PREFERENCES.md"));
   if (!globalPrefsExist) {
     try {
diff --git a/src/resources/extensions/gsd/tests/preferences.test.ts b/src/resources/extensions/gsd/tests/preferences.test.ts
index f2c033784c..1b337a9d36 100644
--- a/src/resources/extensions/gsd/tests/preferences.test.ts
+++ b/src/resources/extensions/gsd/tests/preferences.test.ts
@@ -45,7 +45,7 @@ test("getIsolationMode defaults to none when preferences have no isolation setti
   // Validate the default via validatePreferences: when no isolation is set,
   // preferences.git.isolation is undefined, and getIsolationMode returns "none".
   // Default changed from "worktree" to "none" so GSD works out of the box
-  // without preferences.md (#2480).
+  // without PREFERENCES.md (#2480).
   const { preferences } = validatePreferences({});
   assert.equal(preferences.git?.isolation, undefined, "no isolation in empty prefs");
   const isolation = preferences.git?.isolation;
diff --git a/src/resources/extensions/gsd/tests/token-cost-display.test.ts b/src/resources/extensions/gsd/tests/token-cost-display.test.ts
index e12d9e4db7..bbd7afc509 100644
--- a/src/resources/extensions/gsd/tests/token-cost-display.test.ts
+++ b/src/resources/extensions/gsd/tests/token-cost-display.test.ts
@@ -63,13 +63,13 @@ test("show_token_cost defaults to undefined (disabled) when not set", () => {
   assert.equal(preferences.show_token_cost, undefined);
 });
 
-test("empty preferences.md does not enable show_token_cost", () => {
+test("empty PREFERENCES.md does not enable show_token_cost", () => {
   const prefs = parsePreferencesMarkdown("---\nversion: 1\n---\n");
   assert.ok(prefs);
   assert.equal(prefs.show_token_cost, undefined);
 });
 
-test("preferences.md with show_token_cost: true enables the preference", () => {
+test("PREFERENCES.md with show_token_cost: true enables the preference", () => {
   const prefs = parsePreferencesMarkdown("---\nshow_token_cost: true\n---\n");
   assert.ok(prefs);
   assert.equal(prefs.show_token_cost, true);