Skip to content

Per-user overrides for repository settings so personal preferences don't dirty a committed supacode.json #640

Description

@jeremybower

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: xcodezed). 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?

  • I intend to open a pull request once this is approved and marked ready.

Before submitting

  • I searched existing issues and this is not a duplicate.
  • I understand feature pull requests are closed until the issue is marked ready.
  • I agree to follow this project's Code of Conduct.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions