Skip to content

refactor: use Config::global() instead of per-instance config in SACP server#8893

Draft
matt2e wants to merge 5 commits intomainfrom
concurrent-sacp-config
Draft

refactor: use Config::global() instead of per-instance config in SACP server#8893
matt2e wants to merge 5 commits intomainfrom
concurrent-sacp-config

Conversation

@matt2e
Copy link
Copy Markdown
Collaborator

@matt2e matt2e commented Apr 29, 2026

This matches how the older REST APIs handled concurrent requests regarding config

Summary

  • Replace all Config::new() calls with Config::global() across SACP server and server factory, eliminating redundant config file reads
  • Remove the config_dir field and load_config()/config() helper methods from GooseAcpAgent, since they are no longer needed
  • Remove vestigial block scope and fix needless borrows flagged by clippy

🤖 Generated with Claude Code

Copy link
Copy Markdown

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

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 and others added 4 commits April 30, 2026 10:52
Replace all Config::new(config_dir.join(...), "goose") calls in the
SACP server with Config::global(), which uses the same default path
but goes through the shared singleton mutex. This:

- Serializes all config read-modify-write operations (same protection
  as Goose 1's REST server)
- Fixes a keyring bug where Config::new() hardcoded SecretStorage::Keyring
  and ignored the GOOSE_DISABLE_KEYRING env var
- Eliminates inconsistency where some handlers already used Config::global()
  while config CRUD handlers used the per-instance Config::new() path
- Removes the now-unnecessary config_dir field, load_config/config methods,
  and CONFIG_YAML_NAME import from the SACP server

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…CP server

Remove the unnecessary bare block around Config::global() usage in
resolve_provider_and_model_with_inventory_refresh. Since Config::global()
returns &'static Config and cannot fail, the extra block scope (left over
from the old fallible Config::new() error-handling pattern) serves no
purpose. Removing it flattens indentation by one level.

Also fix three clippy::needless_borrow warnings where &config was passed
but config is already &'static Config, so the extra reference is
immediately dereferenced by the compiler.

Signed-off-by: Matt Toohey <contact@matttoohey.com>
Add Config::init_global(config_dir) that initializes the global singleton
with a caller-provided config directory instead of the default. This
addresses PR feedback that Config::global() silently ignored the
config_dir field from AcpServerFactoryConfig.

The server factory now calls init_global(config_dir) so that if a
non-default config directory is ever passed, the global Config will
use the correct path while still providing the singleton mutex
serialization benefit.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Build Config::init_global from the same config path stack as Config::default and return an error when an existing global config points at a different writable path. Validate that invariant from GooseAcpAgent::new so direct ACP callers cannot silently use a stale global config.

Update ACP fixtures to use one process-global test config directory and add a regression test for mismatched config directories.

Signed-off-by: Matt Toohey <contact@matttoohey.com>
@matt2e matt2e force-pushed the concurrent-sacp-config branch from 70382ad to 66b8f26 Compare April 30, 2026 06:43
Copy link
Copy Markdown

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

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 on lines +1022 to +1026
let config = Config::global();
let ext_state = EnabledExtensionsState::extensions_or_default(
Some(&goose_session.extension_data),
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 Invalidate secret cache before prebuilding providers

This session-init refresh path now constructs a provider from Config::global() without clearing secrets_cache, but the previous implementation explicitly invalidated it first. If secrets.yaml/keyring is updated out-of-band while the process stays up, a new session can still prebuild and refresh inventory with stale credentials until another endpoint happens to call invalidate_secrets_cache(), which regresses secret-rotation behavior during normal session startup.

Useful? React with 👍 / 👎.

Comment thread crates/goose/src/config/base.rs Outdated
Comment on lines +367 to +368
if self.write_path() == requested {
return Ok(());
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 Canonicalize paths before enforcing global config match

The new global-config guard compares raw PathBuf equality, so equivalent paths with different spellings (for example relative vs absolute, or symlinked aliases) are treated as mismatches and return GlobalConfigPathMismatch. That can make init_global fail nondeterministically for the same underlying config directory depending on caller path formatting, even though no real directory conflict exists.

Useful? React with 👍 / 👎.

Add Config::for_config_dir with a path-normalized cache so custom ACP config directories share one Config mutex and secrets cache without forcing them through the global singleton. Keep the default config path on Config::global().

Store the selected config handle on GooseAcpAgent and use it for the ACP paths that previously built a per-request Config, including session provider/model resolution and generic config/secret custom requests.

Update ACP fixtures and regression coverage for default/global handling, custom-directory coexistence, equivalent path normalization, shared state, and concurrent config upserts.

Signed-off-by: Matt Toohey <contact@matttoohey.com>
Copy link
Copy Markdown
Collaborator

@kalvinnchau kalvinnchau left a comment

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants