diff --git a/docs/rfcs/0001-cross-repo-realms.md b/docs/rfcs/0001-cross-repo-realms.md index 98d8449..4f049df 100644 --- a/docs/rfcs/0001-cross-repo-realms.md +++ b/docs/rfcs/0001-cross-repo-realms.md @@ -6,6 +6,7 @@ | **Created** | 2026-01-24 | | **Source** | [Spike: cross-repo-coordination](../spikes/cross-repo-coordination.md) | | **Dialogue** | [cross-repo-realms.dialogue.md](../dialogues/cross-repo-realms.dialogue.md) | +| **Refinement** | [cross-repo-realms-refinement.dialogue.md](../dialogues/cross-repo-realms-refinement.dialogue.md) | --- @@ -32,6 +33,7 @@ When aperture adds a new S3 path, fungal's IAM policy must update. Currently: - Automatic code changes across repos (manual review required) - Public realm discovery (future scope) - Monorepo support (for MVP, each repo is a distinct participant) +- Cyclic dependencies between domains --- @@ -85,34 +87,36 @@ Index (~/.blue/index.yaml) Note: `fungal` participates in both domains with different roles. -### Realm Structure +--- -A realm is a git repository: +## Architecture + +### Directory Structure ``` -realm-letemcook/ -├── realm.yaml # Realm metadata and governance -├── repos/ # Registry of all participating repos -│ ├── aperture.yaml # Repo metadata (path, maintainers) -│ └── fungal-image-analysis.yaml -├── domains/ -│ ├── s3-access/ # A coordination context -│ │ ├── domain.yaml # What is being coordinated -│ │ ├── contracts/ -│ │ │ └── s3-permissions.yaml # The contract schema + value -│ │ └── bindings/ -│ │ ├── aperture.yaml # aperture exports this contract -│ │ └── fungal.yaml # fungal imports this contract -│ │ -│ └── training-pipeline/ # Another coordination context -│ ├── domain.yaml -│ ├── contracts/ -│ │ └── job-schema.yaml -│ └── bindings/ -│ ├── fungal.yaml # fungal exports here -│ └── ml-infra.yaml # ml-infra imports here -└── .github/ - └── CODEOWNERS +~/.blue/ +├── index.yaml # Realms this user participates in +└── credentials.yaml # Optional, prefer git credentials + +$XDG_RUNTIME_DIR/blue/ +└── sessions.db # Session coordination (SQLite) + +realm-{name}/ # Git repository +├── realm.yaml # Metadata, governance, trust +├── repos/ +│ └── {repo}.yaml # Registered repos +└── domains/ + └── {domain}/ + ├── domain.yaml # Coordination context + ├── governance.yaml # Optional domain-level governance + ├── contracts/ + │ └── {name}.yaml # Schema, version, validation, owner + └── bindings/ + └── {repo}.yaml # Export/import declarations + +{repo}/.blue/ +├── config.yaml # Realm membership, domains +└── cache.db # SQLite cache for exports, contracts ``` ### realm.yaml @@ -129,14 +133,21 @@ governance: breaking_changes: require_approval: true grace_period_days: 14 + +trust: + mode: collaborative # collaborative | vendor-customer | federation + require_signed_commits: false + + permissions: + repos/{repo}.yaml: [repo_maintainers] + domains/{domain}/domain.yaml: [domain_owners] + domains/{domain}/contracts/{name}.yaml: [contract_owner] + domains/{domain}/bindings/{repo}.yaml: [repo_maintainers] ``` ### repos/{repo}.yaml -Each participating repo is registered: - ```yaml -# repos/aperture.yaml name: aperture org: cultivarium # Optional org prefix path: /Users/ericg/letemcook/aperture @@ -149,30 +160,14 @@ maintainers: joined_at: 2026-01-24T10:00:00Z ``` -```yaml -# repos/fungal-image-analysis.yaml -name: fungal-image-analysis -org: fungal-org -path: /Users/ericg/letemcook/fungal-image-analysis - -maintainers: - - fungal-maintainer@example.com - -joined_at: 2026-01-24T10:00:00Z -``` - ### domains/{domain}/domain.yaml -Describes what is being coordinated: - ```yaml -# domains/s3-access/domain.yaml name: s3-access description: Coordinates S3 bucket access between aperture and fungal created_at: 2026-01-24T10:00:00Z -# Which repos participate in this coordination members: - aperture - fungal-image-analysis @@ -180,16 +175,18 @@ members: ### domains/{domain}/contracts/{contract}.yaml -The actual contract being coordinated: - ```yaml -# domains/s3-access/contracts/s3-permissions.yaml name: s3-permissions -version: "1.3.0" -description: S3 paths that need cross-account access +version: "1.4.0" +owner: aperture # Only this repo can modify the contract + +compatibility: + backwards: true # New version readable by old importers + forwards: false # Old version NOT readable by new importers schema: type: object + required: [read] properties: read: type: array @@ -197,33 +194,47 @@ schema: write: type: array items: { type: string } + default: [] + delete: + type: array + items: { type: string } + default: [] value: read: - "jobs/*/masks/*" - "jobs/*/*/config.json" - - "jobs/*/*/manifest.json" - "training-runs/*" - "training-metrics/*" write: - "jobs/*/*/manifest.json" - "training-metrics/*" + delete: [] -changelog: +validation: + exporter: scripts/validate-s3-paths.sh + importer: scripts/validate-iam-policy.sh + ci_only: + - scripts/integration-test.sh + +evolution: + - version: "1.0.0" + changes: ["Initial with read array"] + compatible: true - version: "1.3.0" - date: 2026-01-24 - changes: - - "Added training-metrics/* for experiment tracking" + changes: ["Added write array"] + compatible: true + - version: "1.4.0" + changes: ["Added delete array"] + compatible: true ``` ### domains/{domain}/bindings/{repo}.yaml -How each repo relates to the contracts in this domain: - +**Export binding:** ```yaml -# domains/s3-access/bindings/aperture.yaml repo: aperture -role: provider # This repo defines/exports the contract +role: provider exports: - contract: s3-permissions @@ -232,24 +243,22 @@ exports: - models/shared/data/parquet_export.py ``` +**Import binding:** ```yaml -# domains/s3-access/bindings/fungal.yaml repo: fungal-image-analysis -role: consumer # This repo implements/imports the contract +role: consumer imports: - contract: s3-permissions - version: ">=1.0.0" + version: ">=1.0.0 <2.0.0" # Semver range binding: cdk/training_tools_access_stack.py status: current - resolved_version: "1.3.0" + resolved_version: "1.4.0" resolved_at: 2026-01-24T12:00:00Z ``` ### Local Configuration -Each repo stores its realm participation: - ```yaml # aperture/.blue/config.yaml realm: @@ -258,7 +267,6 @@ realm: repo: aperture -# Domains this repo participates in (populated by realm join/sync) domains: - name: s3-access role: provider @@ -266,204 +274,352 @@ domains: - s3-permissions ``` -### Index File +--- + +## Coordination Model + +Blue uses a **hybrid coordination model**: + +1. **Real-time hints (SQLite IPC):** Fast, best-effort session awareness +2. **Durable changes (Git PRs):** Source of truth, auditable, PR-based + +``` +┌──────────────────────────────────────────────────────────────┐ +│ Coordination Layers │ +├──────────────────────────────────────────────────────────────┤ +│ Real-time Hints (SQLite) │ +│ ┌─────────────┐ sessions.db ┌─────────────┐ │ +│ │ Session A │◄───────────────────►│ Session B │ │ +│ │ (aperture) │ notifications │ (fungal) │ │ +│ └─────────────┘ └─────────────┘ │ +│ │ +│ Best-effort, sub-second latency, auto-cleanup │ +├──────────────────────────────────────────────────────────────┤ +│ Durable Changes (Git) │ +│ ┌─────────────┐ ┌─────────────┐ │ +│ │ Repo │────sync branch─────►│ Realm Repo │ │ +│ │ │◄───PR review────────│ │ │ +│ └─────────────┘ └─────────────┘ │ +│ │ +│ Source of truth, PR-based, auditable │ +└──────────────────────────────────────────────────────────────┘ +``` + +### Session Coordination (SQLite) + +Sessions register in a shared SQLite database for real-time awareness: + +```sql +-- $XDG_RUNTIME_DIR/blue/sessions.db + +CREATE TABLE sessions ( + id TEXT PRIMARY KEY, + repo TEXT NOT NULL, + realm TEXT NOT NULL, + pid INTEGER, + started_at TEXT, + last_heartbeat TEXT, + active_rfc TEXT, + active_domains JSON DEFAULT '[]', + exports_modified JSON DEFAULT '[]', + imports_watching JSON DEFAULT '[]' +); + +CREATE TABLE notifications ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + realm TEXT NOT NULL, + domain TEXT NOT NULL, + contract TEXT NOT NULL, + from_repo TEXT NOT NULL, + change_type TEXT NOT NULL, -- 'updated', 'breaking', 'new' + changes JSON, + created_at TEXT, + acknowledged_by JSON DEFAULT '[]' +); +``` + +**Heartbeat protocol:** +- Sessions update `last_heartbeat` every 10 seconds +- Stale sessions (>30s) are automatically cleaned up +- Crash recovery on startup cleans orphaned sessions + +### Sync Protocol (Git PRs) + +Contract changes go through a PR workflow for durability: + +1. `blue realm sync` creates branch `sync/{repo}/{timestamp}` +2. Pushes changes to realm repo +3. Creates PR with affected parties as reviewers +4. Merges when: + - All affected importers acknowledge, OR + - Grace period expires (for non-breaking changes) +5. On merge, notification broadcast to active sessions + +--- + +## Commands + +### MVP Commands (5) + +| Command | Purpose | +|---------|---------| +| `blue realm status` | Show realm state, domains, sessions, notifications | +| `blue realm sync` | Push local changes, pull remote changes via PR | +| `blue realm worktree` | Create linked worktrees across repos in domain | +| `blue realm pr` | Create coordinated PRs with merge order | +| `blue realm check` | Validate contracts (for CI) | + +### Admin Commands + +Under `blue realm admin`: + +| Command | Purpose | +|---------|---------| +| `blue realm admin init` | Create a new realm | +| `blue realm admin join` | Register repo in realm | +| `blue realm admin domain` | Create/manage domains | +| `blue realm admin leave` | Remove repo from realm | +| `blue realm admin cache` | Cache management | + +### Command Details + +#### blue realm status + +```bash +$ blue realm status +📊 aperture (in realm: letemcook) + +Domains: + s3-access (provider) + Contract: s3-permissions v1.4.0 + +Active sessions: + • aperture (you) - working on training-metrics-v2 + • fungal-image-analysis - idle + +Notifications: + ⚠️ 5 min ago: fungal acknowledged s3-permissions 1.4.0 +``` + +#### blue realm sync + +```bash +$ blue realm sync +📤 Syncing with realm 'letemcook'... + +Creating sync branch: sync/aperture/20260124-103000 +Contract changes detected: + s3-permissions: 1.4.0 → 1.5.0 + + training-metrics/experiments/* + +Opening PR for review... + ✓ PR #12: "aperture: s3-permissions 1.5.0" + Reviewers: fungal-image-analysis maintainers + +Notifying active sessions... + ✓ fungal-image-analysis notified + +Waiting for acknowledgment or grace period (14 days for non-breaking). +``` + +#### blue realm check + +```bash +# Validate exports match code +$ blue realm check --mode=exporter +✓ s3-permissions: code matches contract + +# Validate imports are current +$ blue realm check --mode=importer +✓ s3-permissions: binding at 1.4.0 (current) + +# Validate contract schema +$ blue realm check --mode=contract +✓ s3-permissions: valid schema, version bump correct + +# Check compatibility between versions +$ blue realm check --mode=compatibility --from=1.4.0 --to=1.5.0 +Checking s3-permissions 1.4.0 → 1.5.0 + ✓ read: unchanged + ✓ write: unchanged + ✓ delete: unchanged + + experiments: added (optional, default=[]) - COMPATIBLE + +Result: COMPATIBLE (minor version bump OK) +``` + +--- + +## Caching + +Blue uses SQLite for caching to handle concurrent access properly: + +```sql +-- .blue/cache.db + +CREATE TABLE export_cache ( + file_path TEXT PRIMARY KEY, + mtime INTEGER NOT NULL, + content_hash TEXT NOT NULL, + exports JSON, + cached_at INTEGER NOT NULL +); + +CREATE TABLE contract_cache ( + domain TEXT NOT NULL, + contract TEXT NOT NULL, + version TEXT NOT NULL, + schema JSON, + realm_commit TEXT, + cached_at INTEGER NOT NULL, + PRIMARY KEY (domain, contract, version) +); + +CREATE TABLE validation_cache ( + contract TEXT NOT NULL, + version TEXT NOT NULL, + binding_hash TEXT NOT NULL, + valid INTEGER NOT NULL, + output TEXT, + validated_at INTEGER NOT NULL, + PRIMARY KEY (contract, version, binding_hash) +); +``` + +**Invalidation rules:** +- Exports: Invalidate when source file mtime changes +- Contracts: Invalidate when realm repo pulls new commits +- Validation: Invalidate when contract version or binding changes + +**Escape hatches:** +```bash +blue realm sync --no-cache # Force fresh detection +blue realm admin cache clear # Wipe cache +blue realm admin cache stats # Show cache hit rates +``` + +--- + +## CI/CD Integration + +### GitHub Actions Example ```yaml -# ~/.blue/index.yaml -realms: - - name: letemcook - path: /Users/ericg/letemcook/realm-letemcook +name: Realm Contract Check - - name: ml-infra - url: git@github.com:org/realm-ml-infra.git - local_path: /Users/ericg/.blue/realms/ml-infra +on: [push, pull_request] + +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/cache@v3 + with: + path: .blue/cache.db + key: blue-cache-${{ hashFiles('.blue/config.yaml') }} + + - name: Install Blue + run: cargo install blue-cli + + - name: Check exports + run: blue realm check --mode=exporter + + - name: Check imports + run: blue realm check --mode=importer + env: + BLUE_REALM_TOKEN: ${{ secrets.REALM_TOKEN }} + + - name: Check compatibility + if: github.event_name == 'pull_request' + run: blue realm check --mode=compatibility ``` -### Session Coordination (IPC/Socket) +### Validation Hooks -Blue MCP servers communicate directly for real-time session awareness. - -**Architecture:** - -``` -┌─────────────────────┐ IPC Socket ┌─────────────────────┐ -│ Blue MCP Server │◄──────────────────►│ Blue MCP Server │ -│ (aperture) │ │ (fungal) │ -│ │ │ │ -│ Socket: /tmp/blue/ │ │ Socket: /tmp/blue/ │ -│ aperture.sock │ │ fungal.sock │ -└─────────────────────┘ └─────────────────────┘ - │ │ - └──────────────┬────────────────────────────┘ - │ - ▼ - ┌───────────────────────┐ - │ Realm Session Index │ - │ /tmp/blue/letemcook/ │ - │ sessions.json │ - └───────────────────────┘ -``` - -**Session Registration:** - -```json -// /tmp/blue/letemcook/sessions.json -{ - "sessions": [ - { - "repo": "aperture", - "socket": "/tmp/blue/aperture.sock", - "pid": 12345, - "started_at": "2026-01-24T10:00:00Z", - "active_rfc": "training-metrics-v2", - "active_domains": ["s3-access"], - "exports_modified": ["s3-permissions"] - }, - { - "repo": "fungal-image-analysis", - "socket": "/tmp/blue/fungal.sock", - "pid": 12346, - "started_at": "2026-01-24T10:05:00Z", - "active_rfc": null, - "active_domains": ["s3-access"], - "imports_watching": ["s3-permissions"] - } - ] -} -``` - -**Real-time Notifications:** - -When aperture modifies an export, it broadcasts to all sessions watching that export: - -```rust -// Session broadcast -fn notify_export_change(export_name: &str, changes: &ExportChanges) { - let sessions = load_realm_sessions(); - for session in sessions.watching(export_name) { - send_ipc_message(&session.socket, Message::ExportChanged { - from_repo: self.repo.clone(), - contract: export_name.to_string(), - changes: changes.clone(), - }); - } -} -``` - -**Receiving Notifications:** - -```bash -# In fungal-image-analysis terminal (real-time) -$ blue status -📊 fungal-image-analysis (in realm: letemcook) - -🔔 Live notification from repo 'aperture': - Contract 's3-permissions' in domain 's3-access' changed: - + training-metrics/experiments/* - - Your import is now stale. - Run 'blue realm worktree --domain s3-access' to start coordinated changes. -``` - -### Unified Worktrees (Cross-Repo Branches) - -When changes span multiple repos, create worktrees in all affected repos simultaneously with a shared branch name. - -**Workflow:** - -```bash -# In aperture: start cross-repo change -$ blue realm worktree --rfc training-metrics-v2 - -Creating unified worktree 'feat/training-metrics-v2': - ✓ aperture: .worktrees/feat-training-metrics-v2/ - ✓ fungal-image-analysis: .worktrees/feat-training-metrics-v2/ - -Branch 'feat/training-metrics-v2' created in both repos. -You are now in aperture worktree. - -Related worktree: - cd /Users/ericg/letemcook/fungal-image-analysis/.worktrees/feat-training-metrics-v2 -``` - -**Realm Worktree Tracking:** +Contracts can define validation scripts: ```yaml -# realm-letemcook/worktrees/feat-training-metrics-v2.yaml -name: feat/training-metrics-v2 -created_at: 2026-01-24T10:00:00Z -source_rfc: aperture:training-metrics-v2 -domain: s3-access # The coordination context this change affects - -repos: - - name: aperture - path: /Users/ericg/letemcook/aperture/.worktrees/feat-training-metrics-v2 - status: active - commits: 3 - - - name: fungal-image-analysis - path: /Users/ericg/letemcook/fungal-image-analysis/.worktrees/feat-training-metrics-v2 - status: active - commits: 1 - -coordination: - # Commits are linked across repos - commits: - - aperture: abc1234 - fungal-image-analysis: null - message: "feat: add experiment metrics export" - - - aperture: def5678 - fungal-image-analysis: ghi9012 - message: "feat: update IAM for experiment metrics" - linked: true # These commits are coordinated +validation: + exporter: scripts/validate-s3-paths.sh # Run on export + importer: scripts/validate-iam-policy.sh # Run on import + ci_only: + - scripts/integration-test.sh # Only in CI ``` -**Coordinated Commits:** +**Exit codes:** +- 0: Valid +- 1: Invalid (hard fail) +- 2: Warning (soft fail, can be overridden) + +--- + +## Credentials + +Blue uses a layered credential approach: + +``` +Priority order (first found wins): +1. Environment: BLUE_REALM_TOKEN, BLUE_GITHUB_TOKEN +2. Git credential helper: git credential fill +3. Keychain: macOS Keychain, Linux secret-service +4. Config file: ~/.blue/credentials.yaml (discouraged) +``` + +**For CI, GitHub App auth is supported:** +```yaml +env: + BLUE_GITHUB_APP_ID: 12345 + BLUE_GITHUB_APP_PRIVATE_KEY: ${{ secrets.APP_KEY }} + BLUE_GITHUB_APP_INSTALLATION_ID: 67890 +``` + +**Default behavior:** Uses existing git credentials. No additional setup for basic usage. + +--- + +## Conflict Resolution + +| Conflict Type | Detection | Resolution | +|---------------|-----------|------------| +| Concurrent sync | Git merge conflict | Rebase and retry | +| Same contract, two editors | Ownership check | Reject non-owner | +| Stale binding | Version mismatch | Soft warning, CI flag | +| Version incompatibility | Semver check | Hard error | +| Session collision | Unique ID + heartbeat | Auto-cleanup stale | + +### Contract Ownership + +Each contract has an `owner` field. Only the owner repo can modify the contract: + +```yaml +name: s3-permissions +owner: aperture # Only aperture can update this contract +``` + +Enforced at: +1. `blue realm sync`: Rejects if non-owner tries to modify +2. Realm repo CI: CODEOWNERS enforces in PRs + +--- + +## Cycle Prevention + +Cycles between domains are detected and prevented at domain creation: ```bash -# After making changes in both worktrees -$ blue realm commit -m "feat: add experiment metrics with IAM support" +$ blue realm admin domain create feedback-loop --repos a,b +Error: Adding this domain creates a cycle: + a → (s3-access) → b → (feedback-loop) → a -Coordinated commit across repos in domain 's3-access': - aperture: - ✓ Committed: abc1234 - Files: models/training/metrics_exporter.py - - fungal-image-analysis: - ✓ Committed: def5678 - Files: cdk/training_tools_access_stack.py - -Commits linked in realm worktree tracking. - -$ blue realm push -Pushing coordinated branches: - ✓ aperture: feat/training-metrics-v2 → origin - ✓ fungal-image-analysis: feat/training-metrics-v2 → origin - -Ready for coordinated PRs. Run 'blue realm pr' to create. +Consider: + - Merging domains into a single coordination context + - Restructuring the dependency direction + - Using a hub-and-spoke pattern ``` -**Coordinated PRs:** - -```bash -$ blue realm pr - -Creating coordinated pull requests: - -aperture: - ✓ PR #45: "feat: add experiment metrics export" - Base: main - Links to: fungal-image-analysis#23 - -fungal-image-analysis: - ✓ PR #23: "feat: IAM policy for experiment metrics" - Base: main - Links to: aperture#45 - Blocked by: aperture#45 (merge aperture first) - -PRs are linked. Merge order: aperture#45 → fungal-image-analysis#23 -``` +Detection uses topological sort on the domain dependency graph. --- @@ -473,27 +629,29 @@ PRs are linked. Merge order: aperture#45 → fungal-image-analysis#23 ```bash # 1. Create realm (one-time) -$ mkdir realm-letemcook && cd realm-letemcook -$ blue realm init --name letemcook +$ blue realm admin init --name letemcook ✓ Created realm.yaml ✓ Initialized git repository # 2. Register aperture in realm -$ cd ../aperture -$ blue realm join ../realm-letemcook +$ cd aperture +$ blue realm admin join ../realm-letemcook ✓ Created repos/aperture.yaml ✓ Auto-detected exports: s3-permissions ✓ Updated .blue/config.yaml -# 3. Create the s3-access domain (coordination context) -$ blue realm domain create s3-access --repos aperture,fungal-image-analysis +# 3. Create the s3-access domain +$ blue realm admin domain create s3-access \ + --repos aperture,fungal-image-analysis \ + --contract s3-permissions ✓ Created domains/s3-access/domain.yaml +✓ Created domains/s3-access/contracts/s3-permissions.yaml ✓ Created domains/s3-access/bindings/aperture.yaml (provider) ✓ Created domains/s3-access/bindings/fungal-image-analysis.yaml (consumer) # 4. Register fungal in realm $ cd ../fungal-image-analysis -$ blue realm join ../realm-letemcook +$ blue realm admin join ../realm-letemcook ✓ Created repos/fungal-image-analysis.yaml ✓ Detected import: s3-permissions in domain s3-access ✓ Updated .blue/config.yaml @@ -507,216 +665,82 @@ $ cd aperture $ vim models/training/metrics_exporter.py # Added: s3://bucket/training-metrics/experiments/* -# Blue status shows cross-realm impact -$ blue status +# Check status +$ blue realm status 📊 aperture (in realm: letemcook) -RFCs: - training-metrics-v2 [in-progress] 3/5 tasks - -⚠️ Cross-repo change detected: - Domain 's3-access' contract 's3-permissions' has local changes: +⚠️ Contract change detected: + s3-permissions has local changes: + training-metrics/experiments/* Affected repos: - - fungal-image-analysis (imports >=1.0.0 of s3-permissions) + - fungal-image-analysis (imports >=1.0.0 <2.0.0) Run 'blue realm sync' to update realm -# Sync changes to realm +# Sync changes $ blue realm sync 📤 Syncing with realm 'letemcook'... -Contracts updated in domain 's3-access': - s3-permissions: 1.3.0 → 1.4.0 - + training-metrics/experiments/* +Creating branch: sync/aperture/20260124-150000 +Contract update: s3-permissions 1.4.0 → 1.5.0 (compatible) -Notifying affected repos: - fungal-image-analysis: - ✓ Created GitHub issue #42 - "Update IAM policy: new S3 path training-metrics/experiments/*" - -✓ Realm synced (commit abc1234) +✓ PR #15 created for review +✓ fungal-image-analysis session notified ``` ### Consumer Response ```bash # Maintainer in fungal sees notification -$ cd fungal-image-analysis -$ blue status +$ blue realm status 📊 fungal-image-analysis (in realm: letemcook) -⚠️ Stale imports in domain 's3-access': - s3-permissions: 1.3.0 → 1.4.0 available - Binding: cdk/training_tools_access_stack.py +🔔 Notification from aperture (5 min ago): + Contract 's3-permissions' updated to 1.5.0 + + training-metrics/experiments/* - Changes: - + training-metrics/experiments/* (read/write) + Your import (>=1.0.0 <2.0.0) is still satisfied. + Update binding when ready. -# Update the IAM policy +# Update binding $ vim cdk/training_tools_access_stack.py -# Add new path to policy +# Add new path to IAM policy -# Mark import as resolved +# Sync to acknowledge $ blue realm sync 📤 Syncing with realm 'letemcook'... -Imports resolved in domain 's3-access': - s3-permissions: now at 1.4.0 +Binding updated: + s3-permissions: resolved_version 1.4.0 → 1.5.0 -✓ Realm synced (commit def5678) +✓ Acknowledged PR #15 +✓ Realm synced ``` ---- +### Coordinated Changes (Unified Worktrees) -## New Tools +```bash +# Start cross-repo change +$ blue realm worktree --rfc training-metrics-v2 -### blue_realm_init +Creating unified worktree 'feat/training-metrics-v2': + ✓ aperture: .worktrees/feat-training-metrics-v2/ + ✓ fungal-image-analysis: .worktrees/feat-training-metrics-v2/ -Create a new realm. +Branch 'feat/training-metrics-v2' created in both repos. -``` -blue_realm_init - --name: Realm name (kebab-case) - --path: Where to create realm repo (default: current directory) -``` +# Make changes in both worktrees... -### blue_realm_join +# Create coordinated PRs +$ blue realm pr --title "feat: training metrics v2" -Register a repository in a realm. +Creating coordinated PRs: + ✓ aperture PR #45 (merge first - exports) + ✓ fungal PR #23 (merge second - imports) + Blocked by: aperture#45 -``` -blue_realm_join - realm_path: Path to realm repo - --name: Repo name in realm (default: repo directory name) - --detect-exports: Auto-detect exports (default: true) -``` - -### blue_realm_domain - -Create or manage a coordination domain. - -``` -blue_realm_domain - create: Create a new domain - --name: Domain name (kebab-case) - --repos: Comma-separated list of participating repos - --contract: Initial contract name -``` - -### blue_realm_leave - -Remove a repository from a realm. - -``` -blue_realm_leave - --force: Leave even if other repos depend on our exports -``` - -### blue_realm_export - -Declare or update an export. - -``` -blue_realm_export - --name: Export name - --version: Semantic version - --value: JSON value (or --file for YAML file) - --detect: Auto-detect from code patterns -``` - -### blue_realm_import - -Declare an import dependency on a contract. - -``` -blue_realm_import - --contract: Contract name - --domain: Domain containing the contract - --version: Version requirement (semver) - --binding: Local file that uses this import -``` - -### blue_realm_sync - -Synchronize local state with realm. - -``` -blue_realm_sync - --dry-run: Show what would change without committing - --notify: Create GitHub issues for affected consumers (default: true) -``` - -### blue_realm_check - -Check realm status without syncing. - -``` -blue_realm_check - --exports: Check if local exports have changed - --imports: Check if any imports are stale -``` - -### blue_realm_graph - -Display the dependency graph. - -``` -blue_realm_graph - --format: text | mermaid | dot -``` - -### blue_realm_sessions - -List active Blue sessions in the realm. - -``` -blue_realm_sessions - --watch: Continuously update (real-time) -``` - -### blue_realm_worktree - -Create unified worktrees across affected repos. - -``` -blue_realm_worktree - --rfc: RFC title that drives the change - --branch: Branch name (default: feat/{rfc-title}) - --domain: Domain to coordinate (default: auto-detect from exports) - --repos: Specific repos to include (default: all in domain) -``` - -### blue_realm_commit - -Create coordinated commits across realm worktrees. - -``` -blue_realm_commit - -m: Commit message (applied to all repos) - --repos: Which repos to commit (default: all with changes) - --link: Link commits in realm tracking (default: true) -``` - -### blue_realm_push - -Push coordinated branches to remotes. - -``` -blue_realm_push - --repos: Which repos to push (default: all in worktree) -``` - -### blue_realm_pr - -Create linked pull requests across repos. - -``` -blue_realm_pr - --title: PR title (applied to all) - --body: PR body template - --draft: Create as draft PRs +Merge order: aperture#45 → fungal#23 ``` --- @@ -725,670 +749,137 @@ blue_realm_pr ### Phase Overview -| Phase | Scope | Duration | -|-------|-------|----------| -| 0 | Data model in blue-core | 1 week | -| 1 | Realm init | 1 week | -| 2 | Domain join | 1 week | -| 3 | Status integration | 1 week | -| 4 | Sync & notifications | 2 weeks | -| 5 | **Session coordination (IPC)** | 2 weeks | -| 6 | **Unified worktrees** | 2 weeks | -| 7 | **Coordinated commits & PRs** | 2 weeks | -| 8 | Polish & docs | 1 week | +| Phase | Scope | +|-------|-------| +| 0 | Data model + SQLite schemas | +| 1 | `blue realm admin init/join` | +| 2 | `blue realm status` with realm info | +| 3 | `blue realm sync` with PR workflow | +| 4 | `blue realm check` for CI | +| 5 | Session coordination (SQLite IPC) | +| 6 | `blue realm worktree` | +| 7 | `blue realm pr` | +| 8 | Caching layer | +| 9 | Polish + docs | -**Total:** 13 weeks (Phases 0-4 for basic functionality, 5-7 for full coordination) - -### Phase 0: Data Model (Week 1) - -Add to `blue-core/src/`: +### Phase 0: Data Model ```rust -// realm.rs - -use serde::{Deserialize, Serialize}; -use std::path::PathBuf; +// blue-core/src/realm.rs #[derive(Debug, Clone, Serialize, Deserialize)] pub struct RealmConfig { pub name: String, pub version: String, pub governance: Governance, + pub trust: TrustConfig, } #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Governance { - pub admission: AdmissionPolicy, - pub approvers: Vec, - pub breaking_changes: BreakingChangePolicy, +pub struct TrustConfig { + pub mode: TrustMode, + pub require_signed_commits: bool, + pub permissions: HashMap>, } #[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(rename_all = "kebab-case")] -pub enum AdmissionPolicy { - Open, - Approval, - InviteOnly, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct BreakingChangePolicy { - pub require_approval: bool, - pub grace_period_days: u32, -} - -/// A registered repository in the realm -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct RealmRepo { - pub name: String, - pub org: Option, - pub path: Option, - pub url: Option, - pub maintainers: Vec, - pub joined_at: String, -} - -/// A coordination context (edge between repos) -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Domain { - pub name: String, - pub description: Option, - pub members: Vec, // repo names - pub created_at: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Export { +pub struct Contract { pub name: String, pub version: String, - pub description: Option, - pub schema: Option, + pub owner: String, + pub compatibility: Compatibility, + pub schema: serde_json::Value, pub value: serde_json::Value, - pub changelog: Vec, + pub validation: Option, + pub evolution: Vec, } #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ChangelogEntry { - pub version: String, - pub date: String, - pub changes: Vec, +pub struct Compatibility { + pub backwards: bool, + pub forwards: bool, } #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Import { +pub struct ValidationConfig { + pub exporter: Option, + pub importer: Option, + pub ci_only: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Binding { + pub repo: String, + pub role: BindingRole, + pub exports: Vec, + pub imports: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ImportBinding { pub contract: String, - pub domain: String, // which domain contains this contract - pub version: String, // semver requirement - pub binding: String, // local file that uses this + pub version: String, // Semver range + pub binding: String, pub status: ImportStatus, pub resolved_version: Option, - pub resolved_at: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(rename_all = "lowercase")] -pub enum ImportStatus { - Current, - Stale, - Broken, } ``` -### Phase 1: Realm Init (Week 2) - -```rust -// handlers/realm.rs - -pub fn handle_init(args: &Value) -> Result { - let name = args.get("name").and_then(|v| v.as_str()) - .ok_or(ServerError::InvalidParams)?; - - let path = args.get("path") - .and_then(|v| v.as_str()) - .map(PathBuf::from) - .unwrap_or_else(|| PathBuf::from(".")); - - // Create realm directory - let realm_path = path.join(format!("realm-{}", name)); - fs::create_dir_all(&realm_path)?; - - // Create realm.yaml - let config = RealmConfig { - name: name.to_string(), - version: "1.0.0".to_string(), - governance: Governance { - admission: AdmissionPolicy::Approval, - approvers: vec![], - breaking_changes: BreakingChangePolicy { - require_approval: true, - grace_period_days: 14, - }, - }, - }; - - let yaml = serde_yaml::to_string(&config)?; - fs::write(realm_path.join("realm.yaml"), yaml)?; - - // Create directories - fs::create_dir_all(realm_path.join("domains"))?; - fs::create_dir_all(realm_path.join("contracts"))?; - - // Initialize git - Command::new("git") - .args(["init"]) - .current_dir(&realm_path) - .output()?; - - Ok(json!({ - "status": "success", - "message": format!("Created realm '{}' at {}", name, realm_path.display()), - "path": realm_path.display().to_string() - })) -} -``` - -### Phase 2: Repo Registration (Week 3) - -```rust -pub fn handle_join(args: &Value, repo_path: &Path) -> Result { - let realm_path = args.get("realm_path") - .and_then(|v| v.as_str()) - .map(PathBuf::from) - .ok_or(ServerError::InvalidParams)?; - - let repo_name = args.get("name") - .and_then(|v| v.as_str()) - .map(String::from) - .unwrap_or_else(|| { - repo_path.file_name() - .and_then(|n| n.to_str()) - .unwrap_or("unknown") - .to_string() - }); - - // Validate realm exists - let realm_yaml = realm_path.join("realm.yaml"); - if !realm_yaml.exists() { - return Err(ServerError::NotFound("Realm not found".into())); - } - - // Create repos directory if needed - let repos_dir = realm_path.join("repos"); - fs::create_dir_all(&repos_dir)?; - - // Register repo in realm - let repo = RealmRepo { - name: repo_name.clone(), - org: None, - path: Some(repo_path.to_path_buf()), - url: None, - maintainers: vec![], - joined_at: chrono::Utc::now().to_rfc3339(), - }; - fs::write( - repos_dir.join(format!("{}.yaml", repo_name)), - serde_yaml::to_string(&repo)? - )?; - - // Auto-detect exports (stored temporarily until domain is created) - let exports = detect_exports(repo_path)?; - - // Update repo's .blue/config.yaml - let blue_dir = repo_path.join(".blue"); - fs::create_dir_all(&blue_dir)?; - - let config = json!({ - "realm": { - "name": realm_name, - "path": realm_path.display().to_string(), - }, - "repo": repo_name, - "detected_exports": exports, - }); - fs::write(blue_dir.join("config.yaml"), serde_yaml::to_string(&config)?)?; - - // Commit to realm - Command::new("git") - .args(["add", "."]) - .current_dir(&realm_path) - .output()?; - - Command::new("git") - .args(["commit", "-m", &format!("Register repo: {}", repo_name)]) - .current_dir(&realm_path) - .output()?; - - Ok(json!({ - "status": "success", - "message": format!("Registered '{}' in realm", repo_name), - "exports_detected": exports.len(), - "next_step": "Run 'blue realm domain create' to create a coordination domain" - })) -} -``` - -### Phase 3: Status Integration (Week 4) - -Modify `handle_status` to include realm information: - -```rust -fn get_realm_status(state: &ProjectState) -> Option { - let config_path = state.repo_path.join(".blue/config.yaml"); - let config: Value = serde_yaml::from_str( - &fs::read_to_string(&config_path).ok()? - ).ok()?; - - let realm_path = config["realm"]["path"].as_str()?; - let repo_name = config["repo"].as_str()?; - - // Find domains this repo participates in - let domains = find_repo_domains(realm_path, repo_name).ok()?; - - // Check for local export changes in each domain - let local_exports = detect_exports(&state.repo_path).ok()?; - let mut export_changes = vec![]; - let mut stale_imports = vec![]; - - for domain in &domains { - let declared = load_domain_exports(realm_path, domain, repo_name).ok()?; - export_changes.extend(diff_exports(&local_exports, &declared, domain)); - - let imports = load_domain_imports(realm_path, domain, repo_name).ok()?; - stale_imports.extend(check_import_staleness(realm_path, domain, &imports)); - } - - Some(json!({ - "realm": config["realm"]["name"], - "repo": repo_name, - "domains": domains, - "export_changes": export_changes, - "stale_imports": stale_imports - })) -} -``` - -### Phase 4: Sync (Weeks 5-6) - -```rust -pub fn handle_sync(args: &Value, state: &ProjectState) -> Result { - let dry_run = args.get("dry_run").and_then(|v| v.as_bool()).unwrap_or(false); - let notify = args.get("notify").and_then(|v| v.as_bool()).unwrap_or(true); - - let config = load_realm_config(&state.repo_path)?; - let realm_path = PathBuf::from(&config.realm_path); - let repo_name = &config.repo; - - // Pull latest realm state - git_pull(&realm_path)?; - - // Find domains this repo participates in - let domains = find_repo_domains(&realm_path, repo_name)?; - - // Detect export changes across all domains - let local_exports = detect_exports(&state.repo_path)?; - let mut all_changes = vec![]; - - for domain in &domains { - let declared = load_domain_exports(&realm_path, domain, repo_name)?; - let changes = diff_exports(&local_exports, &declared); - if !changes.is_empty() { - all_changes.push((domain.clone(), changes)); - } - } - - if all_changes.is_empty() { - return Ok(json!({ - "status": "success", - "message": "No changes to sync" - })); - } - - if dry_run { - return Ok(json!({ - "status": "dry_run", - "changes": all_changes - })); - } - - // Update exports in each affected domain - let mut notifications = vec![]; - for (domain, changes) in &all_changes { - let contract = &changes[0].contract; - let new_version = bump_version(&changes[0].old_version, changes); - save_domain_export(&realm_path, domain, repo_name, contract, &new_version)?; - - // Find affected repos in this domain - let affected_repos = find_domain_consumers(&realm_path, domain, contract)?; - - if notify { - for affected_repo in &affected_repos { - let issue = create_github_issue(affected_repo, domain, changes)?; - notifications.push(issue); - } - } - } - - // Commit and push - git_commit(&realm_path, &format!( - "{}: sync exports from {}", - all_changes.iter().map(|(d, _)| d.as_str()).collect::>().join(", "), - repo_name - ))?; - git_push(&realm_path)?; - - Ok(json!({ - "status": "success", - "message": format!("Synced changes in {} domain(s)", all_changes.len()), - "notifications": notifications - })) -} -``` - -### Phase 5: Session Coordination (Weeks 8-9) - -**IPC Socket Infrastructure:** - -```rust -// blue-core/src/ipc.rs - -use std::os::unix::net::{UnixListener, UnixStream}; -use std::path::PathBuf; - -pub struct BlueIpcServer { - socket_path: PathBuf, - listener: UnixListener, - realm: String, - repo: String, -} - -impl BlueIpcServer { - pub fn start(realm: &str, repo: &str) -> Result { - let socket_path = PathBuf::from(format!("/tmp/blue/{}.sock", repo)); - std::fs::create_dir_all("/tmp/blue")?; - - let listener = UnixListener::bind(&socket_path)?; - - // Register in realm session index - register_session(realm, repo, &socket_path)?; - - Ok(Self { socket_path, listener, realm: realm.to_string(), repo: repo.to_string() }) - } - - pub fn broadcast_contract_change(&self, domain: &str, contract: &str, changes: &ContractChanges) { - let sessions = load_realm_sessions(&self.realm); - for session in sessions.watching_contract(domain, contract) { - if let Ok(mut stream) = UnixStream::connect(&session.socket) { - let msg = IpcMessage::ContractChanged { - from_repo: self.repo.clone(), - domain: domain.to_string(), - contract: contract.to_string(), - changes: changes.clone(), - }; - serde_json::to_writer(&mut stream, &msg).ok(); - } - } - } -} - -#[derive(Serialize, Deserialize)] -pub enum IpcMessage { - ContractChanged { from_repo: String, domain: String, contract: String, changes: ContractChanges }, - SessionStarted { repo: String, rfc: Option }, - SessionEnded { repo: String }, - Ping, - Pong, -} -``` - -**Session Index:** - -```rust -// blue-core/src/realm_sessions.rs - -pub fn register_session(realm: &str, repo: &str, socket: &Path) -> Result<(), Error> { - let index_path = PathBuf::from(format!("/tmp/blue/{}/sessions.json", realm)); - std::fs::create_dir_all(index_path.parent().unwrap())?; - - let mut sessions = load_sessions(&index_path)?; - sessions.push(Session { - repo: repo.to_string(), - socket: socket.to_path_buf(), - pid: std::process::id(), - started_at: chrono::Utc::now(), - active_rfc: None, - active_domains: vec![], - exports_modified: vec![], - imports_watching: vec![], - }); - - save_sessions(&index_path, &sessions) -} - -pub fn unregister_session(realm: &str, repo: &str) -> Result<(), Error> { - let index_path = PathBuf::from(format!("/tmp/blue/{}/sessions.json", realm)); - let mut sessions = load_sessions(&index_path)?; - sessions.retain(|s| s.repo != repo); - save_sessions(&index_path, &sessions) -} -``` - -### Phase 6: Unified Worktrees (Weeks 10-11) - -```rust -// handlers/realm_worktree.rs - -pub fn handle_realm_worktree(args: &Value, state: &ProjectState) -> Result { - let rfc_title = args.get("rfc").and_then(|v| v.as_str()) - .ok_or(ServerError::InvalidParams)?; - - let branch = args.get("branch").and_then(|v| v.as_str()) - .map(String::from) - .unwrap_or_else(|| format!("feat/{}", rfc_title)); - - let domain_filter = args.get("domain").and_then(|v| v.as_str()); - - let config = load_realm_config(&state.repo_path)?; - let realm_path = PathBuf::from(&config.realm_path); - let repo_name = &config.repo; - - // Find domains this repo participates in (or use filter) - let domains = match domain_filter { - Some(d) => vec![d.to_string()], - None => find_repo_domains(&realm_path, repo_name)?, - }; - - // Find all repos affected by changes in these domains - let mut affected_repos = HashSet::new(); - affected_repos.insert(repo_name.clone()); - - for domain in &domains { - let domain_repos = get_domain_members(&realm_path, domain)?; - affected_repos.extend(domain_repos); - } - - // Create worktree in our repo - let our_worktree = create_worktree(&state.repo_path, &branch)?; - - // Create worktrees in affected repos - let mut worktrees = vec![WorktreeInfo { - repo: repo_name.clone(), - path: our_worktree.clone(), - status: "active".to_string(), - }]; - - for repo in &affected_repos { - if repo == repo_name { continue; } // Skip ourselves - let repo_config = load_realm_repo(&realm_path, repo)?; - if let Some(repo_path) = &repo_config.path { - let wt = create_worktree(repo_path, &branch)?; - worktrees.push(WorktreeInfo { - repo: repo.clone(), - path: wt, - status: "active".to_string(), - }); - } - } - - // Track in realm - let tracking = RealmWorktree { - name: branch.clone(), - created_at: chrono::Utc::now().to_rfc3339(), - source_rfc: format!("{}:{}", repo_name, rfc_title), - domains: domains.clone(), - repos: worktrees.clone(), - commits: vec![], - }; - - save_realm_worktree(&realm_path, &tracking)?; - - Ok(json!({ - "status": "success", - "message": format!("Created unified worktree '{}'", branch), - "branch": branch, - "domains": domains, - "worktrees": worktrees - })) -} -``` - -### Phase 7: Coordinated Commits & PRs (Weeks 12-13) - -```rust -// handlers/realm_commit.rs - -pub fn handle_realm_commit(args: &Value, state: &ProjectState) -> Result { - let message = args.get("m").and_then(|v| v.as_str()) - .ok_or(ServerError::InvalidParams)?; - - let config = load_realm_config(&state.repo_path)?; - let realm_path = PathBuf::from(&config.realm_path); - - // Find current worktree tracking - let branch = get_current_branch(&state.repo_path)?; - let tracking = load_realm_worktree(&realm_path, &branch)?; - - let mut commits = vec![]; - - // Commit in each repo that has changes - for wt in &tracking.repos { - if has_uncommitted_changes(&wt.path)? { - let commit = git_commit(&wt.path, message)?; - commits.push(LinkedCommit { - repo: wt.repo.clone(), - sha: commit, - message: message.to_string(), - }); - } - } - - // Update realm tracking - let mut tracking = tracking; - tracking.commits.push(CoordinatedCommit { - commits: commits.clone(), - timestamp: chrono::Utc::now().to_rfc3339(), - }); - save_realm_worktree(&realm_path, &tracking)?; - - Ok(json!({ - "status": "success", - "message": format!("Committed in {} repo(s)", commits.len()), - "commits": commits - })) -} - -// handlers/realm_pr.rs - -pub fn handle_realm_pr(args: &Value, state: &ProjectState) -> Result { - let config = load_realm_config(&state.repo_path)?; - let realm_path = PathBuf::from(&config.realm_path); - - let branch = get_current_branch(&state.repo_path)?; - let tracking = load_realm_worktree(&realm_path, &branch)?; - - let title = args.get("title").and_then(|v| v.as_str()) - .unwrap_or(&branch); - - let mut prs = vec![]; - - // Determine merge order (repos that export should merge before repos that import) - let ordered_repos = topological_sort_repos(&realm_path, &tracking.domains, &tracking.repos)?; - - for (i, wt) in ordered_repos.iter().enumerate() { - let repo_config = load_realm_repo(&realm_path, &wt.repo)?; - if let Some(repo_path) = &repo_config.path { - // Create PR with links to other PRs - let body = generate_pr_body(&tracking, &prs, i)?; - let pr = create_github_pr(repo_path, &branch, title, &body)?; - - prs.push(LinkedPR { - repo: wt.repo.clone(), - number: pr.number, - url: pr.url, - blocked_by: if i > 0 { Some(prs[i-1].clone()) } else { None }, - }); - } - } - - Ok(json!({ - "status": "success", - "message": format!("Created {} linked PR(s)", prs.len()), - "prs": prs, - "merge_order": ordered_repos.iter().map(|r| &r.repo).collect::>() - })) -} -``` - -### Phase 8: Polish (Week 14) - -- Error handling for all edge cases -- User-friendly messages in Blue's voice -- Documentation -- Tests -- Socket cleanup on process exit - --- ## Test Plan -### Basic Realm Operations -- [ ] `blue realm init` creates valid realm structure -- [ ] `blue realm join` registers repo correctly in repos/ directory -- [ ] `blue realm join` auto-detects potential exports -- [ ] `blue realm domain create` creates domain with contracts and bindings -- [ ] `blue status` shows realm state and participating domains -- [ ] `blue status` detects local contract changes -- [ ] `blue status` shows stale imports across domains -- [ ] `blue realm sync` updates contracts in affected domains -- [ ] `blue realm sync` creates GitHub issues for affected repos +### Core Operations +- [ ] `blue realm admin init` creates valid realm structure +- [ ] `blue realm admin join` registers repo correctly +- [ ] `blue realm admin domain create` creates domain with contracts +- [ ] `blue realm admin domain create` rejects cycles +- [ ] `blue realm status` shows realm state, domains, notifications +- [ ] `blue realm sync` creates PR for contract changes +- [ ] `blue realm sync` rejects non-owner contract modifications - [ ] `blue realm sync --dry-run` shows changes without committing -- [ ] Multiple repos can coordinate through a domain -- [ ] Breaking changes are flagged appropriately -- [ ] CODEOWNERS prevents cross-repo writes to domain bindings + +### Validation +- [ ] `blue realm check --mode=exporter` validates exports match code +- [ ] `blue realm check --mode=importer` validates bindings +- [ ] `blue realm check --mode=contract` validates schema +- [ ] `blue realm check --mode=compatibility` detects breaking changes +- [ ] Validation hooks run with correct exit codes ### Session Coordination -- [ ] IPC socket created on Blue start -- [ ] Session registered in realm index with repo name -- [ ] Contract changes broadcast to watching sessions -- [ ] Sessions cleaned up on process exit -- [ ] `blue realm sessions` lists active sessions by repo +- [ ] SQLite sessions.db created on Blue start +- [ ] Session registered with heartbeat +- [ ] Stale sessions cleaned up after 30s +- [ ] Contract changes create notifications +- [ ] Notifications visible in `blue realm status` + +### Caching +- [ ] Export cache invalidates on file mtime change +- [ ] Contract cache invalidates on realm commit +- [ ] `--no-cache` forces fresh detection +- [ ] Cache survives concurrent access ### Unified Worktrees -- [ ] `blue realm worktree` creates worktrees in all affected repos in domain -- [ ] Worktree tracked in realm repo with domain reference -- [ ] `blue realm commit` commits in all repos with changes -- [ ] Commits linked in realm tracking -- [ ] `blue realm push` pushes all branches -- [ ] `blue realm pr` creates linked PRs with correct merge order (exporters first) +- [ ] `blue realm worktree` creates worktrees in all domain repos +- [ ] `blue realm pr` creates linked PRs with merge order +- [ ] Exporters ordered before importers + +### Conflict Resolution +- [ ] Concurrent syncs handled via git rebase +- [ ] Ownership violations rejected +- [ ] Semver incompatibility flagged as error +- [ ] Stale bindings flagged as warning --- ## Future Work -1. **Signature verification** - Domains sign their exports +1. **Signature verification** - Repos sign their exports 2. **Multiple realms** - One repo participates in multiple realms 3. **Cross-realm imports** - Import from domain in different realm 4. **Public registry** - Discover realms and contracts -5. **Infrastructure verification** - Check actual AWS state matches exports +5. **Infrastructure verification** - Check actual AWS state matches contracts +6. **Domain-level governance** - Override realm governance per domain --- @@ -1396,3 +887,4 @@ pub fn handle_realm_pr(args: &Value, state: &ProjectState) -> Result