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>
359 lines
11 KiB
Markdown
359 lines
11 KiB
Markdown
# RFC 0034: Comprehensive Config Architecture
|
|
|
|
| | |
|
|
|---|---|
|
|
| **Status** | Accepted |
|
|
| **Date** | 2026-01-26 |
|
|
| **Supersedes** | RFC 0032 |
|
|
| **Dialogue** | [blue-config-architecture.dialogue.recorded.md](../dialogues/2026-01-26T1829Z-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:
|
|
|
|
1. **No schema versioning** — Config changes can break existing tooling silently
|
|
2. **Worktree initialization undefined** — No spec for what happens when creating isolated environments
|
|
3. **Release constraints scattered** — Branch policies hardcoded in Rust, not configurable per-repo
|
|
4. **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
|
|
|
|
1. **Config declares intent** — Branch roles, dependencies, requirements
|
|
2. **Blue validates** — Pre-flight checks, warnings, guidance
|
|
3. **Forge enforces** — Branch protection, required reviews, CI gates
|
|
4. **State separation** — config.yaml (pure declarations), blue.db (runtime state)
|
|
5. **Single source** — One versioned file with semantic sections
|
|
|
|
### Schema (Version 1)
|
|
|
|
```yaml
|
|
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.
|
|
|
|
```yaml
|
|
version: 1
|
|
```
|
|
|
|
#### `forge` (required)
|
|
|
|
Forge connection details. Blue uses these for PR creation, issue tracking, and API operations.
|
|
|
|
```yaml
|
|
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.
|
|
|
|
```yaml
|
|
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.
|
|
|
|
```yaml
|
|
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`.
|
|
|
|
```yaml
|
|
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**.
|
|
|
|
1. **Read config** — Load `.blue/config.yaml`
|
|
2. **Generate `.env.isolated`** — Write environment variables from `aws.profile` and `worktree.env`
|
|
3. **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`
|
|
|
|
```rust
|
|
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`
|
|
|
|
```rust
|
|
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()`
|
|
|
|
```rust
|
|
// 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`
|
|
|
|
```rust
|
|
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:
|
|
|
|
```yaml
|
|
# Before (version-less)
|
|
forge:
|
|
type: github
|
|
host: github.com
|
|
owner: superviber
|
|
repo: blue
|
|
```
|
|
|
|
Blue CLI provides migration command:
|
|
|
|
```bash
|
|
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
|
|
- [ ] `forge` section required, others optional
|
|
- [ ] AWS profile injected into environment (shell precedence honored)
|
|
- [ ] Release branch defaults applied when section missing
|
|
- [ ] `.env.isolated` generated with all worktree.env vars
|
|
- [ ] Config validation warns on missing AWS profile
|
|
- [ ] PR creation warns when target mismatches release.develop_branch
|
|
- [ ] `blue config migrate` adds version to legacy configs
|
|
- [ ] `blue config validate` checks 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](../dialogues/2026-01-26T1829Z-blue-config-architecture.dialogue.recorded.md) — 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
|