blue/.blue/docs/rfcs/0022-filesystem-authority.md
Eric Garcia 16d45d9a11 feat: alignment dialogue subagents, MCP instructions, and document batch
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>
2026-01-26 07:09:39 -05:00

9 KiB

RFC 0022: Filesystem Authority

Status Accepted
Date 2026-01-26
Supersedes RFC 0017, RFC 0018, RFC 0021
Alignment 12-expert dialogue, 94% convergence (2 rounds)

Principle

Filesystem is truth. Database is derived index.

The .blue/docs/ directory is the single source of truth for all Blue documents. SQLite (blue.db) is a rebuildable cache that accelerates queries but holds no authoritative state. If the database is deleted, the system must fully recover from filesystem content alone.

Scope

This RFC establishes filesystem authority for:

  • Plan files (.plan.md companions) - task state tracking
  • Document sync - RFC, Spike, ADR, Dialogue import/reconciliation
  • Number allocation - collision-free document numbering

Architecture

                     ┌─────────────────────────────┐
                     │    .blue/docs/ (TRUTH)      │
                     │                             │
                     │  rfcs/*.md                  │
                     │  spikes/*.md                │
                     │  adrs/*.md                  │
                     │  dialogues/*.md             │
                     │  *.plan.md                  │
                     └──────────────┬──────────────┘
                                    │
                         scan / hash / rebuild
                                    │
                                    ▼
                     ┌─────────────────────────────┐
                     │   blue.db (DERIVED INDEX)   │
                     │                             │
                     │  Fast queries               │
                     │  Relationship cache         │
                     │  Rebuildable on demand      │
                     └─────────────────────────────┘

Mechanisms

1. Rebuild-on-Read

When accessing document state, check filesystem freshness:

fn get_document(&self, doc_type: DocType, query: &str) -> Result<Document> {
    // Try database first (fast path)
    if let Ok(doc) = self.find_in_db(doc_type, query) {
        if !self.is_stale(&doc) {
            return Ok(doc);
        }
    }

    // Fall back to filesystem scan (authoritative)
    self.scan_and_register(doc_type, query)
}

1b. Slug-Based Document Lookup

find_document() must match documents by slug (kebab-case), not just exact title. When a tool calls find_document(DocType::Rfc, "filesystem-authority"), it should match the file 0022-filesystem-authority.md regardless of whether the DB stores the title as "Filesystem Authority".

fn find_document(&self, doc_type: DocType, query: &str) -> Result<Document> {
    // Try exact title match in DB
    if let Ok(doc) = self.find_by_title(doc_type, query) {
        return Ok(doc);
    }

    // Try slug match (kebab-case → title)
    if let Ok(doc) = self.find_by_slug(doc_type, query) {
        return Ok(doc);
    }

    // Fall back to filesystem scan
    self.scan_and_register(doc_type, query)
}

Observed bug: After creating 0022-filesystem-authority.md on disk, blue_worktree_create with title filesystem-authority failed even after blue sync. Only the exact title Filesystem Authority worked. All tool-facing lookups must support slug matching.

2. Staleness Detection

Two-tier check (from RFC 0018):

fn is_stale(&self, doc: &Document) -> bool {
    let Some(path) = &doc.file_path else { return true };

    // Fast path: mtime comparison
    let file_mtime = fs::metadata(path).modified().ok();
    let indexed_at = doc.indexed_at.as_ref().and_then(|t| parse_timestamp(t));

    if let (Some(fm), Some(ia)) = (file_mtime, indexed_at) {
        if fm <= ia { return false; }  // Not modified since indexing
    }

    // Slow path: content hash verification
    let content = fs::read_to_string(path).ok();
    content.map(|c| hash_content(&c) != doc.content_hash.as_deref().unwrap_or(""))
           .unwrap_or(true)
}

3. Filesystem-Aware Numbering

When allocating document numbers, scan both sources (from RFC 0021):

pub fn next_number(&self, doc_type: DocType) -> Result<i32> {
    // Database max (fast, possibly stale)
    let db_max: Option<i32> = self.conn.query_row(
        "SELECT MAX(number) FROM documents WHERE doc_type = ?1",
        params![doc_type.as_str()],
        |row| row.get(0),
    )?;

    // Filesystem max (authoritative)
    let fs_max = self.scan_filesystem_max(doc_type)?;

    // Take max of both - filesystem wins
    Ok(std::cmp::max(db_max.unwrap_or(0), fs_max) + 1)
}

4. Reconciliation (blue sync)

Explicit command to align database with filesystem:

blue sync              # Full reconciliation
blue sync --dry-run    # Report drift without fixing
blue sync rfcs/        # Scope to directory

Reconciliation rules:

Condition Action
File exists, no DB record Create DB record from file
DB record exists, no file Soft-delete (deleted_at = now())
Both exist, hash mismatch Update DB from file

5. Plan File Authority

Plan files (.plan.md) are authoritative for task state:

.blue/docs/rfcs/
├── 0022-filesystem-authority.md       # Design (permanent)
└── 0022-filesystem-authority.plan.md  # Tasks (operational)

The plan file is parsed on access; SQLite task state is derived and rebuilt as needed.

Database Schema

Required fields for filesystem authority:

ALTER TABLE documents ADD COLUMN content_hash TEXT;
ALTER TABLE documents ADD COLUMN indexed_at TEXT;
ALTER TABLE documents ADD COLUMN deleted_at TEXT;  -- Soft-delete

CREATE INDEX idx_documents_deleted
    ON documents(deleted_at) WHERE deleted_at IS NOT NULL;

Guardrails

  1. Never auto-fix - blue status reports drift; blue sync fixes it explicitly
  2. Soft-delete only - DB records for missing files get deleted_at, never hard-deleted
  3. 30-day retention - Soft-deleted records purged via blue purge --older-than 30d
  4. Gitignore blue.db - Database is generated artifact, not version-controlled

ADR Alignment

ADR How Honored
ADR 0005 (Single Source) Filesystem is the one truth; database derives from it
ADR 0006 (Relationships) Links stored in DB but validated against filesystem
ADR 0007 (Integrity) Structure (filesystem) matches principle (authority)
ADR 0010 (No Dead Code) Three RFCs consolidated; redundancy eliminated

Test Plan

Core Authority Tests

  • Delete blue.db, run any command - full rebuild succeeds
  • Manual file creation detected and indexed on next access
  • File deletion soft-deletes DB record
  • Hash mismatch triggers re-index

Document Lookup Tests

  • find_document("filesystem-authority") matches title "Filesystem Authority"
  • find_document("Filesystem Authority") matches slug "filesystem-authority"
  • File created on disk, tool lookup finds it without explicit blue sync
  • blue_worktree_create works with slug after file-only creation

Numbering Tests (from RFC 0021)

  • next_number() returns correct value when DB empty but files exist
  • next_number() returns max(db, fs) + 1
  • Handles missing directory gracefully

Plan File Tests (from RFC 0017)

  • Task completion updates .plan.md
  • Plan file changes detected via mtime
  • SQLite rebuilds from plan on access

Sync Tests (from RFC 0018)

  • blue sync creates records for unindexed files
  • blue sync soft-deletes orphan records
  • blue sync --dry-run reports without modifying
  • blue status warns when drift detected

Migration

  1. Add content_hash, indexed_at, deleted_at columns to documents table
  2. Add blue.db to .gitignore
  3. Run blue sync to populate hashes for existing documents
  4. Mark RFCs 0017, 0018, 0021 as superseded-by: 0022

What This RFC Does NOT Cover

RFC 0020 (Source Link Resolution) remains separate. It addresses how to render links in document metadata, not what the source of truth is. RFC 0020 may reference this RFC for path resolution mechanics.

Superseded RFCs

RFC Title Disposition
0017 Plan File Authority Merged - Section 5
0018 Document Import/Sync Merged - Sections 2, 4
0021 Filesystem-Aware Numbering Merged - Section 3

These RFCs should be marked status: superseded with reference to this document.

References


"The filesystem is truth. The database is cache. Git remembers everything else."

— Blue