Alignment dialogues now use custom `alignment-expert` subagents with max_turns: 10, tool restrictions (Read/Grep/Glob), and hard 400-word output limits. Judge protocol injects as prose via RFC 0023. Moved Blue voice patterns from CLAUDE.md to MCP server instructions field for cross-repo portability. Includes RFCs 0017-0026, spikes, and alignment dialogues from 2026-01-25/26 sessions. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
11 KiB
RFC 0025: Blue Next Cortex
| Status | Draft |
| Date | 2026-01-26 |
| Source | Alignment Dialogue: Postmortem & Runbook SDLC Integration |
Summary
blue_next and blue_status only query RFC state (draft/accepted/in-progress). They are blind to postmortem actions, runbook staleness, pending reminders, index state, and ADR relevance. Since these are the only two tools developers call unprompted, every other tool remains invisible and underutilized.
This RFC wires existing query functions into blue_next and blue_status with zero new schema, zero new tools — just richer responses from the two entry points that matter.
Problem
The current blue_next handler (server.rs:2440-2483) queries only status_summary(), which checks four RFC categories: stalled, ready, drafts, active. It returns a single recommendation string. It knows nothing about:
- Open postmortem action items (especially P1/P2)
- Stale or relevant runbooks
- Overdue reminders
- Index/search state
- ADR alignment concerns
The current blue_status handler (server.rs:2381-2438) returns the same four RFC categories plus index drift. Postmortems, runbooks, reminders, and other document types are invisible.
Result: 88 of 100 tools are undiscoverable unless the user already knows they exist.
Design
Principle
The router IS the product. — Alignment Dialogue, 12-expert consensus
blue_next becomes the system's cortex by querying all existing data sources before making recommendations. blue_status becomes the system's dashboard by showing all active work across document types.
No new tools. No new schema. No new tables. Every query function already exists.
Change 1: blue_status — Full System View
Current response:
{
"active": [...rfcs...],
"ready": [...rfcs...],
"stalled": [...rfcs...],
"drafts": [...rfcs...],
"hint": "...",
"index_drift": { ... }
}
New response:
{
"active": [...rfcs...],
"ready": [...rfcs...],
"stalled": [...rfcs...],
"drafts": [...rfcs...],
"postmortem_actions": [
{ "postmortem": "Database Outage", "action": "Add connection pooling", "severity": "P1", "status": "open", "due": "2026-02-01" }
],
"runbooks": {
"total": 3,
"stale": [ { "title": "deploy-staging", "last_updated": "2025-09-15" } ]
},
"reminders": {
"overdue": [ { "title": "Review spike results", "due_date": "2026-01-20" } ],
"due_today": [ ... ]
},
"hint": "...",
"index_drift": { ... }
}
Implementation in handle_status():
-
Query postmortems with open actions:
state.store.list_documents(DocType::Postmortem)— get all postmortems- For each, read file, call
parse_all_actions(content)— extract action items - Filter to actions where status column != "done"/"closed"
- Extract severity from postmortem markdown (P1-P4)
- Sort: P1 first, then P2, P3, P4
-
Query runbook staleness:
state.store.list_documents(DocType::Runbook)— get all runbooks- Check
updated_atfield — flag any >90 days old as stale - Return count + stale list
-
Query reminders:
state.store.list_reminders(Some(ReminderStatus::Pending), false)— pending, not snoozed- Partition into
overdue(due_date < today) anddue_today
-
Reconciliation: add
DocType::PostmortemandDocType::Runbookto the drift check loop (currently only checks RFC, Spike, ADR, Decision).
Change 2: blue_next — Informed Recommendations
Current logic (priority chain):
stalled RFC → ready RFC → draft RFC → active RFC → "nothing pressing"
New logic (priority chain):
1. Overdue P1/P2 postmortem actions → "Postmortem action overdue: '{action}'. This takes priority."
2. Overdue reminders → "Reminder overdue: '{title}'. Check on this."
3. Stalled RFCs → (existing behavior)
4. Ready RFCs → (existing behavior, enhanced)
5. Draft RFCs → (existing behavior)
6. Active RFCs → (existing behavior)
7. Stale runbooks → "Runbook '{title}' hasn't been reviewed in {N} days. Still current?"
8. Due-today reminders → "Reminder due today: '{title}'."
9. Nothing → "Nothing pressing. Good time to plan something new."
Enhanced "ready RFC" step (priority 4): When recommending a ready RFC, also:
- Call
get_runbook_actions()to check if relevant runbooks exist for the RFC's domain - If match found: append
"Runbook '{runbook}' may be relevant. Run blue_runbook_lookup."
Implementation in handle_next():
Replace the single if/else if chain with a recommendations: Vec<String> builder:
fn handle_next(&mut self, _args: &Option<Value>) -> Result<Value, ServerError> {
let state = self.ensure_state()?;
let summary = state.status_summary();
let mut recommendations: Vec<String> = Vec::new();
// 1. Overdue P1/P2 postmortem actions
let postmortem_actions = collect_open_postmortem_actions(state);
for action in postmortem_actions.iter().filter(|a| a.severity <= 2 && a.is_overdue()) {
recommendations.push(format!(
"P{} postmortem action overdue: '{}'. This takes priority.",
action.severity, action.description
));
}
// 2. Overdue reminders
if let Ok(reminders) = state.store.list_reminders(Some(ReminderStatus::Pending), false) {
for r in reminders.iter().filter(|r| r.is_overdue()) {
recommendations.push(format!(
"Reminder overdue: '{}'. Check on this.",
r.title
));
}
}
// 3-6. Existing RFC logic (stalled → ready → drafts → active)
if recommendations.is_empty() {
// ... existing logic, but push to recommendations vec
}
// 7. Stale runbooks (only if no higher-priority items)
if recommendations.is_empty() {
let stale = find_stale_runbooks(state, 90);
for rb in stale {
recommendations.push(format!(
"Runbook '{}' hasn't been reviewed in {} days. Still current?",
rb.title, rb.days_since_update
));
}
}
// 8. Due-today reminders (informational, always append if present)
if let Ok(reminders) = state.store.list_reminders(Some(ReminderStatus::Pending), false) {
for r in reminders.iter().filter(|r| r.is_due_today()) {
recommendations.push(format!("Reminder due today: '{}'.", r.title));
}
}
// 9. Nothing
if recommendations.is_empty() {
recommendations.push("Nothing pressing. Good time to plan something new.".into());
}
Ok(json!({
"recommendations": recommendations,
"hint": summary.hint
}))
}
Change 3: ADR Auto-Check at RFC Creation
In handle_rfc_create():
After creating the RFC document, call the ADR relevance logic:
// After RFC creation succeeds
let adr_results = adr::find_relevant_adrs(state, &problem_text);
if !adr_results.is_empty() {
// Append to response
response["relevant_adrs"] = json!(adr_results);
response["adr_hint"] = json!("These ADRs may apply. Consider them in your design.");
}
This uses the existing load_adr_summaries() and keyword matching. No new code path — just calling existing functions from a new location.
Change 4: Proactive Runbook Lookup
In handle_spike_complete() and handle_rfc_update_status() (when status → "in-progress"):
After the primary operation succeeds, check for relevant runbooks:
// After spike completion or RFC status change
let runbooks = state.store.list_documents(DocType::Runbook)?;
let relevant = find_relevant_runbooks(state, &title_or_context);
if !relevant.is_empty() {
response["relevant_runbooks"] = json!(relevant);
response["runbook_hint"] = json!(format!(
"Runbook '{}' may be relevant. Use blue_runbook_lookup.",
relevant[0].title
));
}
Uses existing calculate_match_score() from runbook.rs.
Helper Functions Needed
Two small helper functions, both built from existing primitives:
collect_open_postmortem_actions(state) -> Vec<PostmortemAction>
struct PostmortemAction {
postmortem_title: String,
description: String,
severity: u8, // 1-4
status: String,
due: Option<String>,
}
Iterates list_documents(DocType::Postmortem), reads each file, calls existing parse_all_actions(), filters to non-closed items. Extracts severity from the postmortem markdown header.
find_stale_runbooks(state, threshold_days) -> Vec<StaleRunbook>
struct StaleRunbook {
title: String,
days_since_update: u64,
}
Iterates list_documents(DocType::Runbook), checks updated_at against current date, returns those exceeding threshold.
Files Changed
| File | Change |
|---|---|
crates/blue-mcp/src/server.rs |
handle_status() and handle_next() enrichment |
crates/blue-mcp/src/handlers/postmortem.rs |
Extract collect_open_postmortem_actions() as pub function; make parse_all_actions() pub |
crates/blue-mcp/src/handlers/runbook.rs |
Extract find_stale_runbooks() as pub function; make calculate_match_score() pub |
crates/blue-mcp/src/handlers/adr.rs |
Extract find_relevant_adrs() wrapper as pub function |
No new files. No schema changes. No new MCP tool definitions.
What This Does NOT Do
- No post-hooks registry (Phase 2)
- No
blue_postmortem_action_to_runbookbridge (Phase 2) - No auto-reminder creation from lifecycle events (Phase 2)
- No courage gates / blocking behavior (Phase 2)
- No tool surface compression (Phase 3)
- No new schema tables (Phase 2)
ADR Alignment
| ADR | How Served |
|---|---|
| 0. Never Give Up | Overdue postmortem actions surface first — issues don't get dropped |
| 2. Presence | User sees full system state, not just RFCs |
| 3. Home | blue_next always orients you regardless of document type |
| 4. Evidence | Status shows evidence from all sources |
| 5. Single Source | One command (blue_status) shows everything |
| 6. Relationships | Cross-type awareness (RFC ↔ runbook ↔ postmortem) |
| 7. Integrity | Complete picture, no hidden state |
| 10. No Dead Code | Stale runbooks surfaced for review or deletion |
Test Plan
blue_statusreturns postmortem_actions array with open itemsblue_statusreturns stale runbooks (>90 days since update)blue_statusreturns overdue and due-today remindersblue_statusreconciliation includes Postmortem and Runbook typesblue_nextprioritizes P1/P2 postmortem actions over RFC workblue_nextsurfaces overdue reminders before stalled RFCsblue_nextmentions stale runbooks when nothing else is pressingblue_nextappends due-today reminders as informational itemsblue_rfc_createreturns relevant ADRs in responseblue_spike_completereturns relevant runbooks in responseblue_rfc_update_statusto "in-progress" returns relevant runbooks- Empty state: no postmortems/runbooks/reminders produces same output as before
cargo testpassescargo clippyclean
"The tools exist. The data exists. Connect them through next."
— Blue