entireio / entireio/cli

Adding an external agent writes merged settings back to one scope, leaking values between `settings.json` and `settings.local.json`

Open
#2,257 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Go
Stars
5.1k
Forks
475
Avg merge
1d 11h
Merged PRs (30d)
178

Description

Summary

Adding an external agent through the interactive agent-management flow (entire agent add / entire agent) auto-enables the external_agents setting. The write path loaded the merged project+local settings view and serialized the whole merged object into a single file. With project scope selected, that committed a developer's personal overrides into the shared .entire/settings.json. With local scope, it froze inherited project values into .entire/settings.local.json as stale overrides. Fixed by #2192.

Affected path

applyAgentChanges in cmd/entire/cli/setup.go. When any newly added or reinstalled agent is external, it persists external_agents: true:

s, loadErr := LoadEntireSettings(ctx)   // merged: local overlaid on project
if loadErr != nil {
    s = &EntireSettings{}
}
if !s.ExternalAgents {
    s.ExternalAgents = true
    if opts.UseLocalSettings {
        saveErr = SaveEntireSettingsLocal(ctx, s)  // whole merged object into settings.local.json
    } else {
        saveErr = SaveEntireSettings(ctx, s)       // whole merged object into settings.json
    }
}
Root cause

LoadEntireSettings overlays .entire/settings.local.json on .entire/settings.json and returns the effective runtime configuration. That merged view is right for reads, but it discards which file supplied each field. Writing the whole struct back to one of those files persists fields that belong to the other scope, plus typed defaults the struct materializes.

The repo already knew about this hazard. setEnabledRaw exists precisely so enable and disable edit only the "enabled" key inside each file's own raw content, and its doc comment spells out the leak scenario. The external-agents path kept the load-merged, save-whole pattern anyway.

Failure modes
  1. Local leaks into the committed project file. With project scope selected, every local-only override in the merged view (log_level, absolute_git_hook_path, personal strategy_options, a personal checkpoint_remote) lands in .entire/settings.json. These values are gitignored because they are personal, and the next commit publishes them to every collaborator. redaction.openai_privacy_filter.command can be copied too. The OPF trust gate refuses to execute a command from the project file, so this is disclosure rather than code execution, but the developer's private command path still ends up in version control.

  2. Project values freeze into the local file. With local scope selected, inherited project values are written into .entire/settings.local.json. The local layer overrides the project layer, so later changes to the project file silently stop applying on that clone. A project-wide log_level change never reaches that developer.

  3. Defaults appear out of nowhere. enabled has no omitempty, so the write always emits "enabled": true/false into the target file even when neither file set it.

  4. The write is skipped when the flag lives in the other scope. The if !s.ExternalAgents guard checks the merged view. If external_agents: true exists only in the developer's local file and they add an external agent with project scope, the project file never gets the key. Other clones don't receive the setting the flow claimed to enable.

  5. A malformed settings file gets replaced with defaults. On any load error the code substituted &EntireSettings{} and saved it, wiping the target file's content instead of surfacing the parse error.

Reproduction
  1. In an Entire-enabled repo, put {"log_level": "debug"} in .entire/settings.local.json and {"strategy_options": {"push": false}} in .entire/settings.json.
  2. Run the interactive agent-management flow, add an external agent, choose project scope.
  3. .entire/settings.json now contains "log_level": "debug" and "enabled", not just "external_agents": true. Selecting local scope instead copies project values into the local file.
Fix

#2192 routes the write through the same raw-JSON mechanism enabled uses. settingsTargetFile resolves the selected scope, the matching LoadProjectRaw or LoadLocalRaw reads only that file as map[string]json.RawMessage, only the "external_agents" key is set, and the matching scoped writer saves it back. Unrelated and unknown keys survive, and the other scope is never touched. setEnabledRaw was generalized to setSettingsBoolRaw and shared by both flags. Load errors now propagate instead of being replaced with defaults.

The regression test TestManageAgents_ExternalAgentSettingDoesNotLeakAcrossScopes drives the real interactive flow with a mock external agent and asserts both contamination directions, key preservation, and that exactly one key was added.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

The affected entry point is applyAgentChanges in cmd/entire/cli/setup.go, with scoped settings handling and raw JSON helpers described in the issue. Start by reading that flow and TestManageAgents_ExternalAgentSettingDoesNotLeakAcrossScopes; completion is covered by the regression assertions for both scopes, preserved keys, and load-error handling. The issue notes that #2192 already provides the fix.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
cli
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.