Configuration Management
Overview
The configuration system provides type-safe, persistent settings management using Pydantic models and YAML serialization. It follows the XDG Base Directory specification for configuration file placement and supports dotted-path access for nested settings.
Architecture
Schema Definition
Configuration schema defined using Pydantic models in src/gh_worker/config/schema.py.
Model Hierarchy:
AppConfig (root)
├── issues_path: Path
├── repository_path: Path
├── plan: PlanConfig
│ ├── parallelism: int
│ ├── agent: str | None
│ └── model: str | None
├── implement: ImplementConfig
│ ├── parallelism: int
│ ├── agent: str | None
│ └── model: str | None
├── sync: SyncConfig
│ └── frequency: str
└── agent: AgentConfig
├── default: str
├── claude_code_path: str | None
└── opencode_path: str | None
Configuration Models
AppConfig
Root configuration containing all application settings.
Fields:
issues_path- Directory for storing issue files (default: None)repository_path- Directory for cloning repositories (default: None)plan- Plan command configurationimplement- Implementation command configurationsync- Sync command configurationagent- Agent configuration
PlanConfig
Settings for plan generation.
Fields:
parallelism- Number of parallel plan executions (default: 1, minimum: 1)agent- Agent to use for planning (overrides agent.default when set, default: None)model- Model to use for planning (overrides agent.model when set, default: None)
ImplementConfig
Settings for plan implementation.
Fields:
parallelism- Number of parallel implementations (default: 1, minimum: 1)agent- Agent to use for implementation (overrides agent.default when set, default: None)model- Model to use for implementation (overrides agent.model when set, default: None)
SyncConfig
Settings for issue synchronization.
Fields:
frequency- Sync interval (default: "1h", examples: "10m", "1h", "1d")
AgentConfig
Settings for LLM agents.
Fields:
default- Default agent name (default: "claude-code")claude_code_path- Path to claude-code binary (default: None, uses PATH)opencode_path- Path to opencode binary (default: None, uses PATH)
Configuration Manager
Handles persistence and access, implemented in src/gh_worker/config/manager.py.
Core Methods:
load()- Load configuration from disk (returns defaults if file missing)save()- Persist configuration to diskget()- Retrieve value by dotted key pathset()- Update value by dotted key path (auto-saves)list_all()- Return all configuration values as flat dict of dotted keys to values
Internal Methods:
_default_config_path()- Resolve XDG-compliant config path_flatten_config()- Flatten nested config dict to dotted keys
File Format
Location
Configuration stored at: ~/.config/gh-worker/config.yaml
Follows XDG Base Directory specification:
- Respects
$XDG_CONFIG_HOMEif set - Falls back to
~/.config - Creates directory structure automatically
YAML Structure
issues_path: /var/gh-worker/issues
repository_path: /var/gh-worker/repos
plan:
parallelism: 3
agent: null # Override agent for planning (uses agent.default if null)
model: null # Override model for planning (uses agent.model if null)
implement:
parallelism: 2
agent: null # Override agent for implementation (uses agent.default if null)
model: null # Override model for implementation (uses agent.model if null)
sync:
frequency: 30m
agent:
default: claude-code
claude_code_path: /usr/local/bin/claude-code
opencode_path: null # Use PATH
Serialization
- Path objects converted to strings in JSON mode
- Keys not sorted (preserves logical grouping)
- Block style formatting (no flow style)
- Null values omitted for optional fields
Access Patterns
Dotted Key Path
Configuration values accessed via dot-separated paths:
plan.parallelism- Accessesconfig.plan.parallelismagent.default- Accessesconfig.agent.defaultissues_path- Accessesconfig.issues_path
Resolution Logic
- Split key by "." into parts
- Traverse object hierarchy using
getattr() - Raise
KeyErrorif any part not found - Return final value
Setting Values
- Load configuration if not cached
- Traverse to parent object
- Set attribute on parent
- Automatically save to disk
Validation
Pydantic Validation
All configuration validated through Pydantic:
- Type checking (int, str, Path, etc.)
- Range constraints (parallelism >= 1)
- Default value provision
- Field descriptions for documentation
Error Handling
- Invalid types raise
ValidationError - Missing required fields raise
ValidationError - Invalid keys raise
KeyError - File I/O errors propagate as filesystem exceptions
Default Behavior
Missing Configuration File
When config file doesn't exist:
- Returns
AppConfig()with all defaults - Does not create file automatically
- File created only on first
save()call
Default Values
- Parallelism: 1 (sequential execution)
- Sync frequency: "1h" (hourly)
- Agent: "claude-code"
- Paths: None (must be configured for relevant operations)
Requirements
Configuration Schema
MUST:
- Use Pydantic models for all configuration structures
- Provide default values for all non-required fields
- Validate field types and constraints
- Support serialization to/from dictionaries
- Include field descriptions
- Use Path type for filesystem paths
SHOULD:
- Group related settings into nested models
- Use descriptive field names
- Provide examples in descriptions
- Set reasonable default values
- Validate ranges for numeric fields (e.g., parallelism >= 1)
MAY:
- Add computed properties for derived values
- Provide field aliases for compatibility
- Support environment variable overrides
- Include field examples in schema
Configuration Manager
MUST:
- Follow XDG Base Directory specification
- Load configuration from
~/.config/gh-worker/config.yaml - Create parent directories automatically on save
- Parse YAML using safe_load
- Support dotted key path access (get/set)
- Auto-save after set operations
- Return defaults when file missing (not error)
- Cache loaded configuration
SHOULD:
- Create directory structure with appropriate permissions
- Use human-readable YAML formatting
- Preserve key order in output
- Handle Path serialization correctly
- Provide clear error messages for invalid keys
MAY:
- Support custom config file paths
- Provide config validation command
- Support config migration between versions
- Allow config inheritance or includes
YAML Serialization
MUST:
- Use block style (not flow style)
- Convert Path objects to strings
- Handle None/null values correctly
- Preserve nested structure
SHOULD:
- Maintain logical key grouping
- Use consistent indentation (2 spaces)
- Omit null values for cleaner output
MAY:
- Include comments in generated YAML
- Support multi-document YAML files
- Provide YAML validation
Dotted Path Access
MUST:
- Split paths on "." delimiter
- Traverse nested objects using attribute access
- Raise KeyError for invalid paths with full path in message
- Support both get and set operations
- Auto-save after set operations
SHOULD:
- Lazy-load configuration on first access
- Provide helpful error messages with valid path hints
- Validate path exists before setting
MAY:
- Support array indexing in paths (e.g., "items[0].name")
- Provide path validation function
- Support wildcard or glob patterns for bulk operations
Validation
MUST:
- Validate types match schema
- Enforce constraints (min/max values)
- Reject invalid configurations with clear errors
- Validate on load and set operations
SHOULD:
- Provide detailed validation error messages
- Suggest corrections for common mistakes
- Validate path existence for filesystem paths
MAY:
- Support custom validators per field
- Provide validation summary for multiple errors
- Support strict vs. lenient validation modes
Usage Examples
Load Configuration
from gh_worker.config.manager import ConfigManager
manager = ConfigManager()
config = manager.load()
print(config.plan.parallelism) # 1
print(config.agent.default) # "claude-code"
Save Configuration
from gh_worker.config.schema import AppConfig, PlanConfig
from pathlib import Path
config = AppConfig(
issues_path=Path("/var/gh-worker/issues"),
repository_path=Path("/var/gh-worker/repos"),
plan=PlanConfig(parallelism=3)
)
manager.save(config)
Get Value by Dotted Path
Set Value by Dotted Path
# Automatically saves after setting
manager.set("plan.parallelism", 5)
manager.set("agent.default", "opencode")
manager.set("sync.frequency", "15m")
Custom Config Path
from pathlib import Path
manager = ConfigManager(config_path=Path("/etc/gh-worker/config.yaml"))
config = manager.load()
Extension Points
The configuration system can be extended to support:
- Per-repository configuration overrides
- Environment variable expansion in values
- Configuration templates or presets
- Configuration validation command
- Configuration migration utilities
- Remote configuration backends
- Encrypted sensitive values
- Configuration change notifications