Skip to content

fix: cache ACP configs by path - #8893

Closed
matt2e wants to merge 1 commit into
mainfrom
concurrent-sacp-config
Closed

fix: cache ACP configs by path#8893
matt2e wants to merge 1 commit into
mainfrom
concurrent-sacp-config

Conversation

@matt2e

@matt2e matt2e commented Apr 29, 2026

Copy link
Copy Markdown
Collaborator

Now that SACP handles requests concurrently, the previous approach of creating a new Config per request could have race conditions if multiple requests tried to change config.

Summary

This fixes ACP config races by sharing a Config handle for each config path instead of creating a new config object per request. The default Goose config directory still uses Config::global(), while custom ACP config directories use a process-wide cache keyed by the normalized config.yaml path.

  • Add Config::for_config_dir(...) and ConfigHandle to select either the default global config or a cached custom-path config.
  • Store the selected config handle on GooseAcpAgent and reuse it for session setup, provider/model resolution, config CRUD, and secret requests.
  • Update AcpServer creation to read Goose mode from the same path-aware config handle.
  • Keep custom config directories isolated while serializing concurrent writes for the same path.
  • Add ACP regression tests for default/global selection, custom path sharing, equivalent path normalization, custom-dir coexistence, shared writes, and concurrent upserts.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 36d4ff286a

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread crates/goose/src/acp/server_factory.rs Outdated
Comment thread crates/goose/src/acp/server.rs Outdated
Base automatically changed from jamadeo/ws-concurrency to main April 29, 2026 13:54
@kalvinnchau

Copy link
Copy Markdown
Collaborator

AI comment:

This removes the agent-owned config path, so GooseAcpAgent::new(..., config_dir, ...) no longer guarantees config reads/writes use that directory. That already affects the ACP test fixture, and it would
affect any caller that wants an ACP agent with a custom config path.

If ACP should always use process-global config, should we remove config_dir from GooseAcpAgent::new or initialize/validate the global config there? Otherwise this constructor still looks like it accepts
a custom config dir even though config access now depends on prior global initialization.

@matt2e
matt2e force-pushed the concurrent-sacp-config branch from 70382ad to 66b8f26 Compare April 30, 2026 06:43

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 66b8f26323

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread crates/goose/src/acp/server.rs Outdated
Comment thread crates/goose/src/config/base.rs Outdated

@kalvinnchau kalvinnchau left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm!

@matt2e
matt2e marked this pull request as draft May 1, 2026 01:15
@matt2e
matt2e marked this pull request as ready for review May 1, 2026 04:12
@matt2e matt2e changed the title refactor: use Config::global() instead of per-instance config in SACP server fix: cache ACP configs by path May 1, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 36d2e72982

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +398 to +399
let config = Arc::new(Self::new(config_path, KEYRING_SERVICE)?);
cache.insert(cache_key, Arc::clone(&config));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Recreate cached Config when keyring mode changes

Config::for_config_dir now caches a single Config instance per path, but Config::new decides SecretStorage once from GOOSE_DISABLE_KEYRING/config at construction time. After an ACP client updates GOOSE_DISABLE_KEYRING via _goose/config/upsert, later secret RPCs keep using the old backend (keyring vs secrets.yaml) until restart, whereas the previous per-call Config::new(...) path re-evaluated this setting each request. This can make secret reads/writes fail or go to the wrong store immediately after a runtime toggle.

Useful? React with 👍 / 👎.

@matt2e
matt2e force-pushed the concurrent-sacp-config branch from 36d2e72 to 36b5244 Compare May 1, 2026 04:29

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

let provider = self
.create_provider(&provider_name, model_config, extensions)
.await

P2 Badge Invalidate cached secrets before rebuilding providers

Now that ACP reuses a cached Config handle, this provider rebuild path can read stale secrets_cache entries after out-of-band credential rotation (for example, keyring/secrets file updated while the process stays up). Before this commit, these flows created a fresh Config per request, so model/provider changes saw current secret values. Add config.invalidate_secrets_cache() before create_provider(...) here (and in update_provider) so set_model/provider switches don't continue using stale credentials.

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Share Config handles by normalized config path so ACP custom directories use the same mutex and secrets cache without forcing them through the global singleton.

Store the selected handle on GooseAcpAgent and use it for config, secret, and provider/model resolution paths. Add regression coverage for default/global config, custom directory coexistence, path normalization, shared state, and concurrent upserts.

Signed-off-by: Matt Toohey <contact@matttoohey.com>
@matt2e
matt2e force-pushed the concurrent-sacp-config branch from c54d972 to e240c37 Compare May 1, 2026 05:56

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e240c373ba

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +417 to +419
let config = Arc::new(Self::new(config_path, KEYRING_SERVICE)?);
cache.insert(cache_key, Arc::clone(&config));
Ok(ConfigHandle::Cached(config))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Rebuild cached config after keyring fallback switches mode

Caching a single Config instance per path here means secret backend selection is frozen at first use, but handle_keyring_fallback_error can later set GOOSE_DISABLE_KEYRING=1 at runtime when keyring access fails. In ACP custom-config deployments that start in keyring mode and then hit that fallback, subsequent requests keep reusing the stale keyring-backed Config instead of switching to file-backed secrets for that config directory, so secret reads/writes continue through the fallback path rather than the intended custom secrets.yaml location. Before this change, recreating Config::new(...) per request re-evaluated the mode and recovered on the next call.

Useful? React with 👍 / 👎.

@matt2e
matt2e marked this pull request as draft May 1, 2026 06:59
@github-actions

Copy link
Copy Markdown
Contributor

This pull request has been automatically marked as stale because it has not had recent activity for 23 days.

What happens next?

  • If no further activity occurs, this PR will be automatically closed in 7 days
  • To keep this PR active, simply add a comment, push new commits, or add the keep-open label
  • If you believe this PR was marked as stale in error, please comment and we'll review it

Thank you for your contribution! 🚀

@github-actions github-actions Bot added the stale label May 25, 2026
@github-actions

github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

This pull request has been automatically closed due to inactivity.

Why was this closed?

  • No activity for 30 days total (23 days + 7 day grace period)
  • Marked as stale 7 days ago with no subsequent activity

Want to reopen?

  • You can reopen this PR at any time if you want to continue working on it
  • Consider rebasing against the latest main branch before reopening
  • Feel free to reach out if you need any assistance

Thank you for your contribution! We appreciate your effort. 🙏

@github-actions github-actions Bot closed this Jun 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants