ADRs: - Update 0008-honor, 0009-courage, 0013-overflow, 0015-plausibility - Add 0017-hosted-coding-assistant-architecture RFCs: - 0032: per-repo AWS profile configuration (draft) - 0033: round-scoped dialogue files (impl + plan) - 0034: comprehensive config architecture (accepted) - 0036: expert output discipline (impl) - 0037: single source protocol authority (draft) - 0038: SDLC workflow discipline (draft) - 0039: ADR architecture greenfield clarifications (impl) - 0040: divorce financial analysis (draft) - 0042: alignment dialogue defensive publication (draft) Spikes: - Read tool token limit on assembled dialogues - RFC ID collision root cause - Expert agent output too long - Judge writes expert outputs - Blue MCP server on superviber infrastructure - Playwright MCP multiple window isolation Dialogues: 16 alignment dialogue records Code: - blue-core: forge module enhancements - blue-mcp: env handlers and server updates - alignment-expert agent improvements - alignment-play skill refinements - install.sh script Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
11 KiB
RFC 0034: Comprehensive Config Architecture
| Status | Accepted |
| Date | 2026-01-26 |
| Supersedes | RFC 0032 |
| Dialogue | blue-config-architecture.dialogue.recorded.md |
Summary
This RFC defines a comprehensive .blue/config.yaml architecture that serves as the single source of truth for repo-level Blue configuration. It addresses four requirements: (1) worktree initialization, (2) release constraints, (3) AWS profile per-repo, and (4) forge configuration—all unified under a versioned schema with clear separation between configuration and runtime state.
Problem
Blue currently lacks a cohesive configuration architecture:
- No schema versioning — Config changes can break existing tooling silently
- Worktree initialization undefined — No spec for what happens when creating isolated environments
- Release constraints scattered — Branch policies hardcoded in Rust, not configurable per-repo
- State/config conflation — Unclear what belongs in config vs runtime state
RFC 0032 proposed adding AWS profile configuration but didn't address these broader architectural concerns.
Design
Core Principles
- Config declares intent — Branch roles, dependencies, requirements
- Blue validates — Pre-flight checks, warnings, guidance
- Forge enforces — Branch protection, required reviews, CI gates
- State separation — config.yaml (pure declarations), blue.db (runtime state)
- Single source — One versioned file with semantic sections
Schema (Version 1)
version: 1
forge:
type: github # github | forgejo | gitlab | bitbucket
host: github.com # or self-hosted (e.g., git.example.com)
owner: superviber
repo: blue
aws:
profile: cultivarium # AWS profile from ~/.aws/config
release:
develop_branch: develop # where active development happens
main_branch: main # protected release branch
worktree:
env:
# Additional environment variables injected into .env.isolated
# LOG_LEVEL: debug
# RUST_BACKTRACE: 1
Section Definitions
version (required)
Schema version for migration support. Blue validates this field and rejects unknown versions with actionable error messages.
version: 1
forge (required)
Forge connection details. Blue uses these for PR creation, issue tracking, and API operations.
forge:
type: github # github | forgejo | gitlab | bitbucket
host: github.com # API host (self-hosted URLs supported)
owner: superviber # Repository owner/organization
repo: blue # Repository name
Forgejo/Gitea Note: Forgejo uses a GitHub-compatible API for most operations. Set type: forgejo and host to your instance URL (e.g., git.example.com).
aws (optional)
AWS profile configuration. Inherits RFC 0032's precedence rules.
aws:
profile: cultivarium # Profile name from ~/.aws/config
Precedence: Shell AWS_PROFILE > config > Blue defaults
release (optional)
Branch topology for release workflows. Blue observes these for intelligent operations; Forge enforces protection rules.
release:
develop_branch: develop # Default PR target, RFC work branch
main_branch: main # Release target, protected branch
Blue's role: Validate PR targets, warn on policy violations, generate correct release notes. Forge's role: Enforce branch protection, required reviews, CI gates.
worktree (optional)
Worktree initialization configuration. Defines environment variables injected into .env.isolated during blue_env_mock.
worktree:
env:
AWS_PROFILE: "${aws.profile}" # Reference other config values
LOG_LEVEL: debug
RUST_BACKTRACE: 1
Worktree Initialization
"Worktree initialization" means: the operations Blue performs when creating an isolated work environment.
- Read config — Load
.blue/config.yaml - Generate
.env.isolated— Write environment variables fromaws.profileandworktree.env - Validate state — Check branches exist, forge is reachable (warn, don't block)
This happens in blue_env_mock and ensure_state().
Observe vs Enforce
Blue operates in the knowledge layer—it knows branch policies exist, validates conformance, warns on deviations. The forge operates in the enforcement layer—it blocks merges, requires reviews, enforces protection rules.
| Aspect | Blue's Role | Forge's Role |
|---|---|---|
| Branch topology | Observes, validates | Enforces protection |
| PR targets | Suggests correct branch | Rejects invalid targets |
| Direct commits to main | Warns user | Blocks if protected |
| Release workflow | Generates docs from correct branch | Enforces CI gates |
Why not enforce in Blue?
- Blue cannot prevent
git push --force main—only Forge can - Enforcement creates brittle duplication of forge logic
- Sync problems between local config and remote reality
- False sense of security
State Separation
| Category | Location | Example |
|---|---|---|
| Declarations | .blue/config.yaml |
AWS profile, branch names |
| Runtime state | blue.db |
Active RFC, session data |
| Generated artifacts | .env.isolated |
Environment for worktree |
| Ephemeral state | .blue/state/ |
Lock files, cache |
Config is read-only after load. Rust's ownership model enforces this—pass &BlueConfig references, never &mut.
Rust Implementation
1. Config Struct
File: crates/blue-core/src/config.rs
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BlueConfig {
pub version: u32,
pub forge: ForgeConfig,
#[serde(default)]
pub aws: Option<AwsConfig>,
#[serde(default)]
pub release: Option<ReleaseConfig>,
#[serde(default)]
pub worktree: Option<WorktreeConfig>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ForgeConfig {
#[serde(rename = "type")]
pub forge_type: String,
pub host: String,
pub owner: String,
pub repo: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AwsConfig {
pub profile: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReleaseConfig {
#[serde(default = "default_develop")]
pub develop_branch: String,
#[serde(default = "default_main")]
pub main_branch: String,
}
fn default_develop() -> String { "develop".to_string() }
fn default_main() -> String { "main".to_string() }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorktreeConfig {
#[serde(default)]
pub env: std::collections::HashMap<String, String>,
}
2. Schema Validation
File: crates/blue-core/src/config.rs
impl BlueConfig {
pub fn validate(&self) -> Result<(), ConfigError> {
// Version check
if self.version != 1 {
return Err(ConfigError::UnsupportedVersion {
found: self.version,
supported: vec![1],
});
}
// Forge validation
if !["github", "forgejo", "gitlab", "bitbucket"].contains(&self.forge.forge_type.as_str()) {
return Err(ConfigError::InvalidForgeType(self.forge.forge_type.clone()));
}
Ok(())
}
}
3. Environment Injection
File: crates/blue-mcp/src/server.rs in ensure_state()
// Inject AWS_PROFILE from config (RFC 0032 precedence: shell wins)
if let Some(aws) = &state.config.aws {
if std::env::var("AWS_PROFILE").is_err() {
std::env::set_var("AWS_PROFILE", &aws.profile);
tracing::info!(profile = %aws.profile, "AWS profile set from config");
}
}
4. Worktree Environment Generation
File: crates/blue-mcp/src/handlers/env.rs
pub fn generate_env_isolated(config: &BlueConfig) -> Vec<String> {
let mut lines = vec![
"# Generated by Blue - do not edit manually".to_string(),
format!("# Source: .blue/config.yaml (version {})", config.version),
];
// AWS profile
if let Some(aws) = &config.aws {
lines.push(format!("AWS_PROFILE={}", aws.profile));
}
// Custom worktree env vars
if let Some(worktree) = &config.worktree {
for (key, value) in &worktree.env {
lines.push(format!("{}={}", key, value));
}
}
lines
}
Migration Path
From RFC 0032 (proposed but not implemented)
No migration needed—RFC 0032 was never implemented.
From existing config.yaml
Existing configs without version field:
# Before (version-less)
forge:
type: github
host: github.com
owner: superviber
repo: blue
Blue CLI provides migration command:
blue config migrate # Adds version: 1, preserves existing fields
blue config validate # Checks config-to-reality alignment
Validation Timing
Blue validates config at multiple points:
| Timing | Validation | Action |
|---|---|---|
ensure_state() |
Schema version, required fields | Error if invalid |
blue_env_mock |
AWS profile exists | Warn if missing |
blue_worktree_create |
Branches exist | Warn if missing |
blue_pr_create |
PR target matches policy | Warn if mismatch |
Warnings are surfaced; operations proceed. Enforcement is Forge's responsibility.
Test Plan
- Schema v1 loads and validates correctly
- Unknown version rejected with actionable error
forgesection required, others optional- AWS profile injected into environment (shell precedence honored)
- Release branch defaults applied when section missing
.env.isolatedgenerated with all worktree.env vars- Config validation warns on missing AWS profile
- PR creation warns when target mismatches release.develop_branch
blue config migrateadds version to legacy configsblue config validatechecks forge reachability
Alternatives Considered
1. Layered config files (.blue/config.yaml + .blue/policies.yaml)
Rejected: Adds operational complexity. Where do I look when things break? Single file with semantic sections provides the same separation without filesystem fragmentation.
2. Blue enforces branch policies
Rejected: Blue cannot prevent git push --force main. Attempting enforcement creates false security and brittle duplication of forge logic. Blue's role is validation and guidance; Forge owns enforcement.
3. Config contains runtime state
Rejected: Violates config-as-contract semantics. Git doesn't put "is working tree clean?" in .git/config. Config declares identity; state tracks operations.
4. Per-worktree config overrides
Deferred: Edge case raised in dialogue. Could add .blue/worktrees/{name}/config.yaml layering later if real need emerges. YAGNI for now.
References
- Alignment Dialogue: blue-config-architecture — 12-expert deliberation, 100% convergence
- RFC 0032 (superseded) — Original AWS profile proposal
.blue/context.manifest.yaml— Precedent for versioned YAML schemas in Blue
"Configuration declares reality; validation observes consistency; tooling enforces policy at appropriate boundaries."
— Blue