What problem does this solve?
When a repository commits supacode.json to share team defaults (this repo does, for runScript: "make run-app", the scripts list, worktree-copy flags), that file becomes the source of truth for all repository settings — including purely personal preferences.
I hit this by changing my default editor in the UI (openActionID: xcode → zed). Supacode wrote the entire settings blob back into the committed supacode.json, leaving a permanently dirty working tree. My options are to commit my personal editor choice to the team file, revert it and lose the preference next time any repo setting is saved, or carry local noise in git status forever.
The underlying issue is that RepositorySettings is one flat struct persisted wholesale to whichever store wins (RepositorySettingsKey.load/save): if <repoRoot>/supacode.json exists, every UI settings edit rewrites it. There is no layer separating "belongs to the team" from "belongs to me". #550 is an adjacent symptom of the same write-back behavior (machine-specific absolute paths contaminating the committed file).
Proposed solution
Layered settings, git-config style: committed supacode.json provides team defaults; per-user overrides live in the existing user-level store.
- Load: base = repo
supacode.json (if present, else global defaults), overlaid with a sparse per-user patch stored in the global settings.json under the existing repositories[repositoryID] key. Only explicitly overridden fields are in the patch, so users without an override automatically pick up team changes to supacode.json.
- Save: the app never writes the repo file as a side effect of a preference change. UI edits are diffed against the base: fields that differ go into the user patch; fields set back to the team value are removed from the patch (clean un-override).
- Editing team defaults becomes explicit: hand-edit
supacode.json (v1), with room for an explicit "save to project" affordance later.
Mechanically: a patch representation of RepositorySettings with all-optional fields (the decoder already uses decodeIfPresent per key), and RepositorySettingsKey.load/save change from "whichever file exists wins wholesale" to "base + patch". Existing full blobs in the user store decode as valid patches, so migration is a no-op. The settings UI should indicate when a value is a personal override, with a "reset to project value" action, so "I edited supacode.json and nothing changed" doesn't become a support case.
This would also resolve the #550 class of bug: the app stops rewriting the committed file, so authored relative paths survive.
Alternatives considered
- Split the schema into team fields vs. personal fields (e.g.
openActionID always user-level): smaller change, but hard-codes the classification — a team can't share a default editor, and a user can't personally override runScript. Solves this symptom, not the class.
- Gitignored overlay file (
supacode.local.json / .git/supacode.json): duplicates the user-level store that already exists, and Supacode can't guarantee it's gitignored without touching .gitignore (which itself dirties the repo).
- Workaround today: revert the file after every settings change, or commit personal preferences to the team file.
Supacode version
No response
Are you planning to build this yourself?
Before submitting
What problem does this solve?
When a repository commits
supacode.jsonto share team defaults (this repo does, forrunScript: "make run-app", the scripts list, worktree-copy flags), that file becomes the source of truth for all repository settings — including purely personal preferences.I hit this by changing my default editor in the UI (
openActionID:xcode→zed). Supacode wrote the entire settings blob back into the committedsupacode.json, leaving a permanently dirty working tree. My options are to commit my personal editor choice to the team file, revert it and lose the preference next time any repo setting is saved, or carry local noise ingit statusforever.The underlying issue is that
RepositorySettingsis one flat struct persisted wholesale to whichever store wins (RepositorySettingsKey.load/save): if<repoRoot>/supacode.jsonexists, every UI settings edit rewrites it. There is no layer separating "belongs to the team" from "belongs to me". #550 is an adjacent symptom of the same write-back behavior (machine-specific absolute paths contaminating the committed file).Proposed solution
Layered settings, git-config style: committed
supacode.jsonprovides team defaults; per-user overrides live in the existing user-level store.supacode.json(if present, else global defaults), overlaid with a sparse per-user patch stored in the globalsettings.jsonunder the existingrepositories[repositoryID]key. Only explicitly overridden fields are in the patch, so users without an override automatically pick up team changes tosupacode.json.supacode.json(v1), with room for an explicit "save to project" affordance later.Mechanically: a patch representation of
RepositorySettingswith all-optional fields (the decoder already usesdecodeIfPresentper key), andRepositorySettingsKey.load/savechange from "whichever file exists wins wholesale" to "base + patch". Existing full blobs in the user store decode as valid patches, so migration is a no-op. The settings UI should indicate when a value is a personal override, with a "reset to project value" action, so "I edited supacode.json and nothing changed" doesn't become a support case.This would also resolve the #550 class of bug: the app stops rewriting the committed file, so authored relative paths survive.
Alternatives considered
openActionIDalways user-level): smaller change, but hard-codes the classification — a team can't share a default editor, and a user can't personally overriderunScript. Solves this symptom, not the class.supacode.local.json/.git/supacode.json): duplicates the user-level store that already exists, and Supacode can't guarantee it's gitignored without touching.gitignore(which itself dirties the repo).Supacode version
No response
Are you planning to build this yourself?
ready.Before submitting
ready.