Ir al contenido

FAB Protocol

FAB Intake, Headless Execution & Workspace Isolation Guide

Sección titulada «FAB Intake, Headless Execution & Workspace Isolation Guide»

AI-First Engineering Framework v7.0 — Phase 1: Orchestration Layer

Sección titulada «AI-First Engineering Framework v7.0 — Phase 1: Orchestration Layer»

Versión: 1.0.0 | Estado: Activo | Fecha: Marzo 2026 | Tipo: Guide Framework: AI-First Engineering Framework v7.0 (Factory-Native) Prerequisito: CORE_F04_Fullstack_Agent_Builder.md (Phase 0) Fuentes: Anthropic Claude Code Docs (2026), Gas Town Orchestrator (2026), StrongDM Software Factory (2026), DealInspect Case Study (2026), Druce.ai Claude Code Guide (2026)


Phase 1 construye la capa de orquestación entre el humano director y los Fullstack Agent Builders:

┌─────────────────────────────────────────────────────────────────┐
│ PHASE 1: ORCHESTRATION LAYER │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌──────────────┐ │
│ │ 1. INTAKE │──►│ 2. HEADLESS │──►│ 3. WORKSPACE │ │
│ │ PROCESSOR │ │ EXECUTION │ │ ISOLATION │ │
│ │ │ │ PROTOCOL │ │ PLAYBOOK │ │
│ │ Issues, slack, │ │ Cómo lanzar y │ │ Git worktrees│ │
│ │ notas → intents │ │ monitorear FABs │ │ 1 repo = 1 │ │
│ │ estructurados │ │ autónomos │ │ FAB aislado │ │
│ └─────────────────┘ └─────────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────────┘

2. Intake Processor — De Input Crudo a Intent Estructurado

Sección titulada «2. Intake Processor — De Input Crudo a Intent Estructurado»

El humano director tiene ideas, issues, mensajes de Slack, notas de voz — todas en formatos diferentes. El FAB necesita un intent document estructurado y validable.

INPUT CRUDO INTENT ESTRUCTURADO
───────────── ────────────────────
"necesitamos login social" ──► intent/INTENT-001.yaml
GitHub Issue #42 ──► intent/INTENT-002.yaml
Nota en Apple Notes ──► intent/INTENT-003.yaml
Slack msg: "el search falla" ──► intent/INTENT-004.yaml
PRD.pdf de 10 páginas ──► intent/INTENT-005.yaml
intent/INTENT-006.yaml
intent/INTENT-007.yaml
┌──────────────────────────────────────────────────────────────────┐
│ INTAKE PROCESSOR FLOW │
│ │
│ 1. CAPTURE 2. NORMALIZE 3. VALIDATE │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Capturar │────►│ LLM │─────►│ Schema │ │
│ │ input │ │ normaliza│ │ validate │ │
│ │ crudo │ │ a YAML │ │ intent │ │
│ └──────────┘ └──────────┘ └────┬─────┘ │
│ │ │
│ 4. CLASSIFY 5. PRIORITIZE 6. QUEUE │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Tipo: │────►│ Score: │────►│ Agregar │ │
│ │ feature, │ │ urgency, │ │ a cola │ │
│ │ bug, │ │ value, │ │ y asignar│ │
│ │ refactor │ │ effort │ │ a FAB │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└──────────────────────────────────────────────────────────────────┘

El intent document es el contrato entre el humano director y el FAB:

intent/INTENT-2026-042.yaml
# Validar con: scripts/schemas/intent.schema.json
intent:
# ── Metadata ──
id: "INTENT-2026-042"
version: "1" # Se incrementa si el humano modifica
project: "project-a"
created_by: "human-director"
created_at: "2026-03-21T09:30:00Z"
updated_at: "2026-03-21T09:30:00Z"
source: "github_issue" # github_issue | slack | manual | prd | voice_note
source_ref: "https://github.com/org/project-a/issues/42"
# ── Clasificación ──
type: "new_feature" # new_feature | bug_fix | refactoring | documentation | security_fix | infrastructure | exploration
priority: "high" # critical | high | medium | low
estimated_effort: "medium" # tiny (<30min) | small (30min-2h) | medium (2-8h) | large (8h+)
# ── Problema y Solución ──
problem: |
Los usuarios no pueden hacer login con sus cuentas de Google o GitHub.
Actualmente no hay sistema de autenticación. Los usuarios acceden
directamente al dashboard sin protección.
context: |
El proyecto ya tiene Prisma configurado con PostgreSQL.
Hay un layout con header donde iría el botón de login.
No hay modelo User aún en el schema.
acceptance_criteria:
- id: "AC-01"
description: "OAuth2 flow funcional con Google y GitHub"
testable: true
test_type: "integration"
- id: "AC-02"
description: "JWT tokens para sesiones (1h expiry, refresh tokens)"
testable: true
test_type: "unit"
- id: "AC-03"
description: "Página de perfil muestra nombre, email, avatar"
testable: true
test_type: "e2e"
- id: "AC-04"
description: "Middleware de protección en rutas /api/* y /dashboard/*"
testable: true
test_type: "integration"
- id: "AC-05"
description: "Tests de integración para happy path y error cases"
testable: true
test_type: "meta"
- id: "AC-06"
description: "Documentación de setup (env vars, OAuth app config)"
testable: false
test_type: "manual_review"
# ── Restricciones ──
constraints:
stack:
- "Next.js 15, TypeScript, Prisma, PostgreSQL"
technical:
- "No dependencias pesadas (no NextAuth — usar jose + oauth4webapi)"
architectural:
- "Seguir ADR-001 (API patterns)"
- "Seguir ADR-003 (security baseline)"
regulatory: []
# ── Scope ──
scope_limits:
out_of_scope:
- "NO implementar 2FA en esta iteración"
- "NO implementar roles/permissions (será INTENT siguiente)"
dependencies:
- "Crear modelo User en Prisma (nuevo)"
affected_areas:
- "src/app/auth/"
- "src/middleware.ts"
- "prisma/schema.prisma"
- "src/app/dashboard/profile/"
# ── Budget ──
budget:
max_cost_usd: 30
max_time_hours: 4
model_preference: "sonnet" # sonnet | opus | haiku | auto
# ── Delivery ──
delivery:
branch: "feature/auth-oauth2"
pr_target: "main"
auto_merge: false # true solo para low-risk + all tests pass
requires_human_review: true # true para auth/security/infra
deploy_to: "staging" # staging | production | none
# ── FAB Execution ──
execution:
mode: "headless" # headless | interactive | hybrid
phases: ["F04", "F05", "F06", "F07", "F08", "F09"]
checkpoint_interval_min: 60
escalation_policy: "pause_and_notify" # pause_and_notify | continue_with_assumption | stop_immediately
# ── Estado (actualizado por FAB) ──
status: "queued" # draft | queued | in_progress | blocked | completed | cancelled
assigned_fab: "" # FAB ID asignado
started_at: ""
completed_at: ""
actual_cost_usd: 0
actual_time_hours: 0
pr_url: ""
blocker_notes: ""
# Script: Convertir GitHub Issue a Intent Document
# Uso: ./scripts/fab-intake.sh github <issue-url>
#!/bin/bash
set -euo pipefail
ISSUE_URL="$1"
INTENT_DIR="intent"
DATE=$(date +%Y-%m-%d)
INTENT_ID="INTENT-$(date +%Y)-$(printf '%03d' $(ls $INTENT_DIR/*.yaml 2>/dev/null | wc -l))"
mkdir -p "$INTENT_DIR"
# Extraer issue data via gh CLI
ISSUE_DATA=$(gh issue view "$ISSUE_URL" --json title,body,labels,assignees)
TITLE=$(echo "$ISSUE_DATA" | jq -r '.title')
BODY=$(echo "$ISSUE_DATA" | jq -r '.body')
LABELS=$(echo "$ISSUE_DATA" | jq -r '.labels[].name' | tr '\n' ', ')
# Usar Claude para normalizar a intent document
claude --print "
You are an Intake Processor for an AI-Driven Software Factory.
Convert this GitHub Issue into a structured intent document.
Title: $TITLE
Body: $BODY
Labels: $LABELS
Output ONLY valid YAML following the intent schema.
Include all required fields. Set status to 'queued'.
Set mode to 'headless' if requirements are clear, 'interactive' if ambiguous.
Estimate effort: tiny (<30min), small (30min-2h), medium (2-8h), large (8h+).
" > "$INTENT_DIR/$INTENT_ID.yaml"
echo "✅ Created: $INTENT_DIR/$INTENT_ID.yaml"
echo "📋 Review and adjust before launching FAB"
# Script: Convertir texto libre a Intent Document
# Uso: ./scripts/fab-intake.sh text "necesitamos que el search funcione con filtros"
#!/bin/bash
INPUT_TEXT="$2"
INTENT_DIR="intent"
INTENT_ID="INTENT-$(date +%Y)-$(printf '%03d' $(ls $INTENT_DIR/*.yaml 2>/dev/null | wc -l))"
mkdir -p "$INTENT_DIR"
claude --print "
You are an Intake Processor for an AI-Driven Software Factory.
Convert this informal request into a structured intent document.
Request: $INPUT_TEXT
Output ONLY valid YAML following the intent schema.
For any information you cannot infer, use reasonable defaults and mark
with REVIEW comments. Set status to 'draft' (requires human review).
Set mode to 'interactive' since requirements may be incomplete.
" > "$INTENT_DIR/$INTENT_ID.yaml"
echo "⚠️ Created DRAFT: $INTENT_DIR/$INTENT_ID.yaml"
echo "📋 This intent needs human review before launching FAB"
# Script: Descomponer PRD en múltiples intent documents
# Uso: ./scripts/fab-intake.sh prd docs/prd-v2.md
#!/bin/bash
PRD_PATH="$2"
INTENT_DIR="intent"
mkdir -p "$INTENT_DIR"
claude --print "
You are an Intake Processor for an AI-Driven Software Factory.
This is a Product Requirements Document. Decompose it into
INDIVIDUAL intent documents — one per feature/capability.
PRD content:
$(cat "$PRD_PATH")
Rules:
1. Each intent must be independently implementable
2. Order by dependency (foundations first)
3. Each intent should take 2-8 hours of agent work
4. If a feature is too large (>8h), split into sub-intents
5. Output as YAML array, one document per intent
Output format:
---
# INTENT-2026-001
intent:
...
---
# INTENT-2026-002
intent:
...
" > "$INTENT_DIR/prd_decomposition.yaml"
echo "✅ PRD decomposed. Review: $INTENT_DIR/prd_decomposition.yaml"
echo "📋 Split into individual files and review before launching FABs"
# intent/queue.yaml — Cola de intents priorizada
# Actualizado automáticamente por el sistema o manualmente por el humano director
queue:
last_updated: "2026-03-21T10:00:00Z"
active:
- intent_id: "INTENT-2026-042"
project: "project-a"
fab_id: "fab-001"
status: "in_progress"
started_at: "2026-03-21T09:45:00Z"
progress: "F06 - Build (60%)"
cost_so_far: "$12.30"
- intent_id: "INTENT-2026-043"
project: "project-b"
fab_id: "fab-002"
status: "in_progress"
started_at: "2026-03-21T10:00:00Z"
progress: "F04 - Architecture"
cost_so_far: "$3.20"
queued:
- intent_id: "INTENT-2026-044"
project: "project-a"
priority: "medium"
type: "refactoring"
estimated_effort: "small"
- intent_id: "INTENT-2026-045"
project: "project-c"
priority: "high"
type: "bug_fix"
estimated_effort: "tiny"
blocked:
- intent_id: "INTENT-2026-040"
project: "project-b"
blocker: "Waiting for API credentials from vendor"
blocked_since: "2026-03-20T14:00:00Z"
completed_today:
- intent_id: "INTENT-2026-041"
project: "project-a"
completed_at: "2026-03-21T08:30:00Z"
actual_cost: "$18.50"
pr_url: "https://github.com/org/project-a/pull/15"
# Validar intent document contra schema antes de lanzar FAB
# Uso: python3 scripts/validate-intent.py intent/INTENT-2026-042.yaml
#!/usr/bin/env python3
"""Validate intent document against JSON Schema."""
import sys
import yaml
import json
from jsonschema import validate, ValidationError
def validate_intent(intent_path: str, schema_path: str = "scripts/schemas/intent.schema.json"):
with open(intent_path) as f:
intent = yaml.safe_load(f)
with open(schema_path) as f:
schema = json.load(f)
try:
validate(instance=intent, schema=schema)
print(f"✅ {intent_path} is valid")
# Additional quality checks
warnings = []
intent_data = intent.get("intent", {})
if not intent_data.get("acceptance_criteria"):
warnings.append("⚠️ No acceptance criteria defined")
ac_list = intent_data.get("acceptance_criteria", [])
untestable = [ac for ac in ac_list if not ac.get("testable", True)]
if len(untestable) > len(ac_list) * 0.5:
warnings.append("⚠️ More than 50% of acceptance criteria are not testable")
if not intent_data.get("budget", {}).get("max_cost_usd"):
warnings.append("⚠️ No cost cap defined")
if not intent_data.get("scope_limits", {}).get("out_of_scope"):
warnings.append("⚠️ No out-of-scope items defined (risk of scope creep)")
if intent_data.get("execution", {}).get("mode") == "headless":
if intent_data.get("type") == "exploration":
warnings.append("⚠️ Exploration intents should use 'interactive' mode")
for w in warnings:
print(w)
return True
except ValidationError as e:
print(f"❌ {intent_path} is INVALID: {e.message}")
return False
if __name__ == "__main__":
sys.exit(0 if validate_intent(sys.argv[1]) else 1)

┌─────────────────────────────────────────────────────────────────┐
│ FAB EXECUTION MODES │
│ │
│ MODE 1: INTERACTIVE │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Humano + FAB en terminal, conversación real-time │ │
│ │ → F01-F04 (strategy, discovery, architecture) │ │
│ │ → Cuando hay ambigüedad o exploración │ │
│ │ → Usa: claude (en terminal directo) │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ MODE 2: HEADLESS │
│ ┌─────────────────────────────────────────────────┐ │
│ │ FAB opera solo, humano revisa después │ │
│ │ → F05-F09 (build, test, security, deploy) │ │
│ │ → Cuando intent es claro y testeble │ │
│ │ → Usa: claude --print o background agents │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ MODE 3: HYBRID │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Empieza interactivo (arch), continúa headless │ │
│ │ → F04 interactivo, F05-F09 headless │ │
│ │ → Para features que requieren decisión + build │ │
│ │ → Usa: sesión interactiva → checkpoint → headless │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘

Opción A: Claude Code Background Agent (Recomendado)

Sección titulada «Opción A: Claude Code Background Agent (Recomendado)»
Ventana de terminal
# Lanzar FAB headless usando background agent de Claude Code
# El agente trabaja en segundo plano, sin bloquear terminal
cd /path/to/project-a
# 1. Asegurarse de que el intent y harness están listos
cat intent/INTENT-2026-042.yaml # Verificar
python3 scripts/validate-intent.py intent/INTENT-2026-042.yaml
# 2. Lanzar background agent
claude --background \
"You are a Fullstack Agent Builder (FAB).
Read intent/INTENT-2026-042.yaml and execute the complete implementation.
Follow the FAB protocol:
1. Read the intent document completely
2. Create progress-tracker.md with your plan
3. Execute phases: F04 → F05 → F06 → F07 → F08 → F09
4. Checkpoint (commit + HANDOFF.md) every 60 minutes
5. Create PR when all acceptance criteria are met
6. Update intent status to 'completed'
IMPORTANT:
- If uncertain (confidence < 0.7), document assumption and continue
- If cost exceeds 80% of budget, pause and update intent status to 'blocked'
- If security finding, stop and update intent status to 'blocked'
- Tests MUST pass before creating PR"

Opción B: Claude Code —print (Para scripts/CI)

Sección titulada «Opción B: Claude Code —print (Para scripts/CI)»
Ventana de terminal
# Lanzar FAB usando --print (no-interactivo, output a stdout)
# Útil para GitHub Actions o cron jobs
cd /path/to/project-a
claude --print \
--max-turns 100 \
--output-format json \
"Read intent/INTENT-2026-042.yaml. Execute full FAB protocol.
Create PR when done." \
2>&1 | tee logs/fab-$(date +%Y%m%d-%H%M%S).log

Opción C: GitHub Action (Para CI/CD automatizado)

Sección titulada «Opción C: GitHub Action (Para CI/CD automatizado)»
.github/workflows/fab-execute.yaml
name: FAB Headless Execution
on:
workflow_dispatch:
inputs:
intent_file:
description: 'Path to intent YAML file'
required: true
type: string
jobs:
fab-execute:
runs-on: ubuntu-latest
timeout-minutes: 240 # 4h max
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v4
- name: Validate Intent
run: python3 scripts/validate-intent.py ${{ inputs.intent_file }}
- name: Execute FAB
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
claude --print \
--max-turns 100 \
"You are a FAB. Read ${{ inputs.intent_file }}.
Execute full protocol. Create PR when done." \
2>&1 | tee fab-execution.log
- name: Upload Execution Log
if: always()
uses: actions/upload-artifact@v4
with:
name: fab-log-${{ github.run_id }}
path: fab-execution.log

El FAB actualiza progress-tracker.md durante la ejecución:

<!-- progress-tracker.md — Auto-maintained by FAB -->
# FAB Execution: INTENT-2026-042 — OAuth2 Authentication
## Intent
- **ID**: INTENT-2026-042
- **Project**: project-a
- **Mode**: headless
- **Budget**: $30 / 4h
## Plan (6 phases)
- [x] F04: Architecture — ADR for auth strategy (done, commit a1b2c3)
- [x] F05: Contracts — Prisma schema + API specs (done, commit d4e5f6)
- [x] F06: Build — Implementation (done, commit g7h8i9)
- [ ] F07: TEVV — Tests (IN PROGRESS)
- [ ] F08: Security — OWASP scan
- [ ] F09: Deploy — Staging deploy + docs
## Current Phase: F07 — TEVV
- Started: 2026-03-21T11:15:00Z
- Context window: 52%
- Files modified this phase: tests/auth.test.ts, tests/middleware.test.ts
- Tests passing: 14/16 (2 pending)
## Cost
- Tokens used: ~85,000
- Estimated cost: $14.20 (47% of budget)
- Burn rate: $4.70/hour
## Decisions Made
- [10:00] Using jose for JWT (not jsonwebtoken) — lighter, ESM native
- [10:15] Prisma User model with provider + providerId (multi-provider support)
- [10:45] Middleware pattern: Edge Middleware for all /api/* and /dashboard/*
- [11:20] Using crypto.subtle for token signing (no external deps)
## Blockers
- None
## Assumptions Made (flagged for review)
- ⚠️ Assumed Google OAuth callback URL: /api/auth/callback/google
- ⚠️ Assumed JWT secret from env var: AUTH_SECRET
./scripts/fab-health-check.sh
#!/bin/bash
# scripts/fab-health-check.sh — Check health of all running FABs
set -euo pipefail
echo "╔══════════════════════════════════════════════════════════╗"
echo "║ FAB FACTORY HEALTH CHECK ║"
echo "║ $(date +%Y-%m-%d\ %H:%M:%S) ║"
echo "╚══════════════════════════════════════════════════════════╝"
echo ""
# Check each project worktree
for WORKTREE in ../fab-project-*; do
if [ -d "$WORKTREE" ]; then
PROJECT=$(basename "$WORKTREE")
echo "┌── $PROJECT ──────────────────────────────"
# Check last commit time
LAST_COMMIT=$(cd "$WORKTREE" && git log -1 --format="%ar" 2>/dev/null || echo "no commits")
echo "│ Last commit: $LAST_COMMIT"
# Check progress tracker
if [ -f "$WORKTREE/progress-tracker.md" ]; then
PHASE=$(grep "Current Phase:" "$WORKTREE/progress-tracker.md" | head -1 || echo "unknown")
echo "$PHASE"
COST=$(grep "Estimated cost:" "$WORKTREE/progress-tracker.md" | head -1 || echo "unknown")
echo "$COST"
else
echo "│ ⚠️ No progress-tracker.md found"
fi
# Check intent status
ACTIVE_INTENT=$(find "$WORKTREE/intent" -name "*.yaml" -newer "$WORKTREE/.git/index" 2>/dev/null | head -1)
if [ -n "$ACTIVE_INTENT" ]; then
STATUS=$(grep "status:" "$ACTIVE_INTENT" | head -1 | awk '{print $2}' | tr -d '"')
echo "│ Intent status: $STATUS"
fi
# Stale detection: no commit in 30+ minutes
LAST_COMMIT_EPOCH=$(cd "$WORKTREE" && git log -1 --format="%ct" 2>/dev/null || echo "0")
NOW_EPOCH=$(date +%s)
DIFF_MIN=$(( (NOW_EPOCH - LAST_COMMIT_EPOCH) / 60 ))
if [ "$DIFF_MIN" -gt 30 ]; then
echo "│ 🚨 STALE: No commit in ${DIFF_MIN} minutes!"
elif [ "$DIFF_MIN" -gt 15 ]; then
echo "│ ⚠️ WARNING: No commit in ${DIFF_MIN} minutes"
else
echo "│ ✅ Active (last commit ${DIFF_MIN}m ago)"
fi
echo "└──────────────────────────────────────────"
echo ""
fi
done
# Summary
TOTAL=$(ls -d ../fab-project-* 2>/dev/null | wc -l)
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Total projects: $TOTAL"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
# fab_notifications.yaml — Configuración de alertas
notifications:
channels:
slack:
webhook: "${FAB_SLACK_WEBHOOK}"
enabled: true
email:
to: "${FAB_DIRECTOR_EMAIL}"
enabled: false
github:
enabled: true # Comments on issues/PRs
events:
intent_started:
message: "🚀 FAB started: {intent_id} on {project}"
channels: ["slack"]
phase_completed:
message: "✅ {project}: Phase {phase} completed ({cost_so_far})"
channels: ["slack"]
blocker_detected:
message: "🚨 FAB BLOCKED: {intent_id} — {blocker_reason}"
channels: ["slack", "email"]
priority: "high"
cost_warning:
message: "💰 Cost alert: {project} at {percent}% of budget ({cost_so_far})"
channels: ["slack"]
pr_created:
message: "📋 PR ready for review: {pr_url} ({intent_id})"
channels: ["slack", "github"]
intent_completed:
message: "✅ DONE: {intent_id} — Cost: {actual_cost}, Time: {actual_time}"
channels: ["slack"]
stale_session:
message: "⚠️ Stale session: {project} — no commit in {minutes}min"
channels: ["slack"]
error:
message: "❌ FAB ERROR: {project} — {error_message}"
channels: ["slack", "email"]
priority: "critical"

Cuando el FAB termina un intent, genera un completion report en el PR:

<!-- Auto-generated by FAB upon PR creation -->
## FAB Completion Report
### Intent
- **ID**: INTENT-2026-042
- **Problem**: OAuth2 authentication with Google and GitHub
### Execution Summary
| Metric | Value |
|--------|-------|
| Total time | 2h 45min |
| Total cost | $18.50 |
| Phases completed | F04, F05, F06, F07, F08, F09 |
| Tests added | 16 |
| Test pass rate | 100% |
| Coverage delta | +12% |
| Security findings | 0 critical, 0 high |
| Files changed | 14 |
| Lines added | 842 |
| Lines removed | 23 |
| Checkpoints | 3 |
### Acceptance Criteria Status
- [x] AC-01: OAuth2 flow with Google and GitHub ✅
- [x] AC-02: JWT tokens (1h expiry, refresh) ✅
- [x] AC-03: Profile page with name, email, avatar ✅
- [x] AC-04: Middleware protection on /api/* and /dashboard/* ✅
- [x] AC-05: Integration tests for happy path + error cases ✅
- [x] AC-06: Setup documentation ✅
### Assumptions Made (review these)
- ⚠️ Google OAuth callback: `/api/auth/callback/google`
- ⚠️ JWT secret from env var: `AUTH_SECRET`
### Decisions Made
- Used `jose` for JWT (ADR-005 created)
- Edge Middleware pattern for route protection
- Multi-provider support in User model
### How to Test
```bash
# Setup env vars
cp .env.example .env.local
# Add GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, etc.
# Run tests
npm test
# Start dev server
npm run dev
# Visit http://localhost:3000/api/auth/login/google
---
## 4. Workspace Isolation Playbook
### 4.1 Estrategia: Un Repo = Un Worktree = Un FAB

┌────────────────────────────────────────────────────────────────┐ │ WORKSPACE ISOLATION │ │ │ │ MAIN REPO (origin) WORKTREES (local) │ │ ┌──────────────┐ ┌──────────────────┐ │ │ │ main branch │ │ ../fab-project-a/ │ ← FAB #1 │ │ │ (protected) │ │ branch: feat/x │ │ │ │ │ │ worktree │ │ │ │ │ ├──────────────────┤ │ │ │ │ │ ../fab-project-b/ │ ← FAB #2 │ │ │ │ │ branch: feat/y │ │ │ │ │ │ worktree │ │ │ │ │ ├──────────────────┤ │ │ │ │ │ ../fab-project-c/ │ ← FAB #3 │ │ │ │ │ branch: feat/z │ │ │ └──────────────┘ └──────────────────┘ │ │ │ │ SEPARADO (proyectos diferentes) │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ github/ │ │ github/ │ │ github/ │ │ │ │ org/proj-a │ │ org/proj-b │ │ org/proj-c │ │ │ │ (repo propio)│ │ (repo propio)│ │ (repo propio)│ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ ← FAB #1 ← FAB #2 ← FAB #3 │ └────────────────────────────────────────────────────────────────┘

### 4.2 Script de Setup: Factory Workspace
```bash
#!/bin/bash
# scripts/fab-workspace-setup.sh — Setup workspace isolation for FAB Factory
# Uso: ./scripts/fab-workspace-setup.sh <project-name> <repo-url> [branch]
set -euo pipefail
PROJECT_NAME="${1:?Usage: fab-workspace-setup.sh <project-name> <repo-url> [branch]}"
REPO_URL="${2:?Usage: fab-workspace-setup.sh <project-name> <repo-url> [branch]}"
BRANCH="${3:-main}"
FAB_DIR="../fab-${PROJECT_NAME}"
echo "🏭 Setting up FAB workspace: $PROJECT_NAME"
echo " Repo: $REPO_URL"
echo " Branch: $BRANCH"
echo " Path: $FAB_DIR"
# ── 1. Clone or create worktree ──
if [ -d "$FAB_DIR" ]; then
echo "⚠️ Directory $FAB_DIR already exists. Skipping."
else
echo "📂 Cloning repo..."
git clone "$REPO_URL" "$FAB_DIR"
cd "$FAB_DIR"
git checkout -b "fab/$PROJECT_NAME" "$BRANCH"
fi
cd "$FAB_DIR"
# ── 2. Create intent directory ──
mkdir -p intent
cat > intent/README.md << 'EOF'
# Intent Documents
Place intent YAML files here. Each intent represents one unit of work
for the Fullstack Agent Builder (FAB).
## File naming
- `INTENT-YYYY-NNN.yaml` — Individual intent
- `queue.yaml` — Priority queue (auto-managed)
## Workflow
1. Human director creates intent YAML (or uses intake script)
2. Validate: `python3 scripts/validate-intent.py intent/INTENT-YYYY-NNN.yaml`
3. FAB reads intent and executes
4. FAB updates status in intent file when done
## See also
- `../baseline/framework/guides/FAB_Intake_Headless_Workspace_Guide.md`
- `../baseline/framework/core/F04_.../CORE_F04_Fullstack_Agent_Builder.md`
EOF
# ── 3. Create progress tracker template ──
cat > progress-tracker.md << 'EOF'
<!-- progress-tracker.md — Auto-maintained by FAB -->
# FAB Progress Tracker
## Current Intent
- **ID**: (none)
- **Status**: idle
## Plan
(No active plan)
## Cost
- Tokens used: 0
- Estimated cost: $0.00
## Decisions Made
(none)
## Blockers
(none)
EOF
# ── 4. Create HANDOFF.md template ──
cat > HANDOFF.md << 'EOF'
<!-- HANDOFF.md — Written by FAB at checkpoints for session continuity -->
# Session Handoff
## Last Checkpoint
- **Date**: (none)
- **Phase**: (none)
- **Commit**: (none)
## State
(No active session)
## Next Steps
(none)
## Open Questions
(none)
EOF
# ── 5. Create .fab-config.yaml ──
cat > .fab-config.yaml << EOF
# FAB Configuration for $PROJECT_NAME
fab:
project_name: "$PROJECT_NAME"
fab_id: "fab-$(echo "$PROJECT_NAME" | md5sum | head -c 6)"
budget:
max_cost_per_session_usd: 50
max_cost_per_day_usd: 200
alert_at_percent: 80
execution:
default_mode: "headless"
checkpoint_interval_min: 60
max_session_hours: 4
stale_threshold_min: 30
notifications:
slack_webhook: "" # Set in environment: FAB_SLACK_WEBHOOK
on_blocker: true
on_completion: true
on_cost_warning: true
quality:
require_tests: true
min_coverage_delta: 5
security_scan: true
auto_merge: false
EOF
# ── 6. Verify setup ──
echo ""
echo "✅ FAB workspace ready: $FAB_DIR"
echo ""
echo " 📂 intent/ — Place intent documents here"
echo " 📋 progress-tracker.md — FAB updates during execution"
echo " 📝 HANDOFF.md — Session continuity document"
echo " ⚙️ .fab-config.yaml — FAB configuration"
echo ""
echo "Next steps:"
echo " 1. Create an intent: cp baseline/project-template/intent/intent-template.yaml intent/INTENT-2026-001.yaml"
echo " 2. Edit the intent with your requirements"
echo " 3. Validate: python3 scripts/validate-intent.py intent/INTENT-2026-001.yaml"
echo " 4. Launch FAB: cd $FAB_DIR && claude --background 'Read intent/INTENT-2026-001.yaml. Execute FAB protocol.'"
#!/bin/bash
# scripts/fab-factory.sh — Manage multiple FAB projects
# Uso: ./scripts/fab-factory.sh <command> [args]
set -euo pipefail
COMMAND="${1:-status}"
case "$COMMAND" in
status)
echo "╔══════════════════════════════════════════════════╗"
echo "║ FAB FACTORY STATUS ║"
echo "║ $(date +%Y-%m-%d\ %H:%M:%S) ║"
echo "╚══════════════════════════════════════════════════╝"
for DIR in ../fab-*/; do
if [ -d "$DIR" ]; then
NAME=$(basename "$DIR")
BRANCH=$(cd "$DIR" && git branch --show-current 2>/dev/null || echo "detached")
LAST=$(cd "$DIR" && git log -1 --format="%ar" 2>/dev/null || echo "n/a")
# Read FAB config
if [ -f "$DIR/.fab-config.yaml" ]; then
FAB_ID=$(grep "fab_id:" "$DIR/.fab-config.yaml" | awk '{print $2}' | tr -d '"')
else
FAB_ID="unconfigured"
fi
# Check status
if [ -f "$DIR/progress-tracker.md" ]; then
PHASE=$(grep -m1 "Current Phase:" "$DIR/progress-tracker.md" 2>/dev/null | sed 's/## //' || echo "idle")
else
PHASE="idle"
fi
printf " %-20s | %-10s | %-15s | %-20s | %s\n" "$NAME" "$FAB_ID" "$BRANCH" "$LAST" "$PHASE"
fi
done
;;
launch)
PROJECT="${2:?Usage: fab-factory.sh launch <project-name> <intent-file>}"
INTENT="${3:?Usage: fab-factory.sh launch <project-name> <intent-file>}"
FAB_DIR="../fab-${PROJECT}"
if [ ! -d "$FAB_DIR" ]; then
echo "❌ Project not found: $FAB_DIR"
echo " Run: fab-workspace-setup.sh $PROJECT <repo-url>"
exit 1
fi
echo "🚀 Launching FAB for $PROJECT with intent $INTENT"
cd "$FAB_DIR"
# Validate intent
python3 ../baseline/scripts/validate-intent.py "$INTENT" || exit 1
# Launch headless
claude --background \
"You are a Fullstack Agent Builder (FAB).
Read $INTENT and execute the complete implementation.
Follow the FAB protocol in CLAUDE.md.
Checkpoint every 60 minutes.
Create PR when all acceptance criteria are met."
echo "✅ FAB launched for $PROJECT"
;;
stop)
PROJECT="${2:?Usage: fab-factory.sh stop <project-name>}"
echo "🛑 Stopping FAB for $PROJECT"
# Signal to FAB to checkpoint and stop
echo "STOP_REQUESTED=$(date -Iseconds)" >> "../fab-${PROJECT}/.fab-signal"
echo "✅ Stop signal sent. FAB will checkpoint and stop at next iteration."
;;
cost)
echo "💰 FAB Factory Cost Report"
echo "━━━━━━━━━━━━━━━━━━━━━━━━"
TOTAL=0
for DIR in ../fab-*/; do
if [ -f "$DIR/progress-tracker.md" ]; then
NAME=$(basename "$DIR")
COST=$(grep -m1 "Estimated cost:" "$DIR/progress-tracker.md" 2>/dev/null | grep -oP '\$[\d.]+' || echo "\$0.00")
echo " $NAME: $COST"
fi
done
echo "━━━━━━━━━━━━━━━━━━━━━━━━"
;;
*)
echo "Usage: fab-factory.sh <status|launch|stop|cost> [args]"
;;
esac
# merge_strategy.yaml — How FABs merge their work back
merge:
# Para proyectos separados (repos distintos)
separate_repos:
strategy: "standard_pr"
description: "Cada FAB trabaja en su repo, crea PR a main"
conflict_risk: "none" # Repos separados = 0 conflictos
# Para worktrees del mismo repo (features paralelas)
same_repo_worktrees:
strategy: "serialized_merge_queue"
description: "Merge uno a la vez, CI completo en cada merge"
steps:
1: "FAB crea PR desde feature branch"
2: "CI runs on PR branch"
3: "Human approves (or auto-merge si confianza alta)"
4: "Merge queue procesa uno a la vez"
5: "Si conflicto post-merge: FAB re-resolve en siguiente sesión"
conflict_prevention:
- "Cada FAB trabaja en bounded contexts separados"
- "Archivos compartidos (routes, config) requieren human approval"
- "Contratos (schemas, API) son inmutables durante bolt"
# Para monorepo con paths separados
monorepo_paths:
strategy: "path_based_ownership"
description: "Cada FAB owns un path, merge libre"
codeowners: |
/apps/project-a/ @fab-001
/apps/project-b/ @fab-002
/packages/shared/ @human-director # Shared code = human only

Ventana de terminal
# 1. Setup primer proyecto FAB
./scripts/fab-workspace-setup.sh my-project https://github.com/you/my-project.git
# 2. Crear primer intent
cp project-template/intent/intent-template.yaml ../fab-my-project/intent/INTENT-2026-001.yaml
# Editar con tu problema/feature real
# 3. Validar intent
python3 scripts/validate-intent.py ../fab-my-project/intent/INTENT-2026-001.yaml
# 4. Lanzar FAB interactivo (primera vez)
cd ../fab-my-project
claude "Read intent/INTENT-2026-001.yaml. Execute FAB protocol."
# 5. Monitorear
./scripts/fab-health-check.sh
  • Intake Processor configurado (scripts de conversión por source type)
  • Intent Schema validado y funcionando (scripts/schemas/intent.schema.json)
  • Al menos 1 proyecto con workspace aislado (fab-workspace-setup.sh)
  • CLAUDE.md del proyecto contiene instrucciones FAB
  • progress-tracker.md template en cada worktree
  • HANDOFF.md template en cada worktree
  • .fab-config.yaml con budget limits en cada worktree
  • Health check script probado (fab-health-check.sh)
  • Factory management script disponible (fab-factory.sh)
  • Primer intent creado, validado y ejecutado (interactivo primero)
  • Primer intent headless ejecutado exitosamente
  • Merge strategy definida para el tipo de setup (repos separados vs worktrees)

Anti-patrónRiesgoSolución
Intent sin acceptance criteriaFAB no sabe cuándo “terminó”Mínimo 3 criteria testeables
Lanzar headless sin validar intentDesperdicio de tokens en intent mal formadoSiempre validate-intent.py primero
Múltiples FABs en mismo directorioConflictos de archivos, corrupción1 worktree/repo = 1 FAB
Sin checkpointsPérdida de trabajo en crashCheckpoint cada 60 min obligatorio
Sin monitoringFAB stuck sin saberloHealth check cada 15-30 min
Intent demasiado grande (>8h)FAB pierde coherenciaDescomponer en sub-intents
Headless para exploraciónOutput incoherenteUsar modo interactivo para F01-F04

7. Event-Driven Triggers — Automations (v7.4)

Sección titulada «7. Event-Driven Triggers — Automations (v7.4)»

Los triggers permiten que el Factory OS reaccione automaticamente a eventos externos, creando intents y lanzando FABs sin intervencion humana.

TipoFuenteEjemplo
github_eventGitHub (issues, PRs, CI)Issue con label fab-auto → crear intent
slack_messageSlack (mensajes en canal)Mensaje con patron @fab fix ... → intent bug_fix
cron_scheduleTimer (expresion cron)Cada dia a las 09:00 → health check
ci_resultCI/CD (GitHub Actions, etc.)CI falla en main → intent de fix automatico
webhookHTTP POST externoEvento de monitoring → intent de investigacion
file_changeFilesystem (watch)Cambio en *.yaml → validar artefactos

Archivo: project/F09_operations/fab_triggers.yaml

triggers:
- id: "auto-fix-ci"
name: "Fix CI failures automatically"
type: ci_result
conditions:
status_filter: "failure"
branch_pattern: "main"
action:
type: create_intent
intent_template: "intent/templates/bug_fix.yaml"
intent_overrides:
priority: "high"
cooldown_minutes: 30
Ventana de terminal
# Listar triggers configurados
python3 baseline/scripts/fab-trigger-dispatcher.py list --project-dir .
# Simular sin ejecutar
python3 baseline/scripts/fab-trigger-dispatcher.py dry-run --project-dir . --event event.json
# Modo escucha continuo
python3 baseline/scripts/fab-trigger-dispatcher.py listen --project-dir .
  • Solo triggers configurados explicitamente en fab_triggers.yaml se ejecutan
  • Cooldown obligatorio entre ejecuciones del mismo trigger
  • No se ejecuta codigo arbitrario — solo crear intents, despachar FABs, o correr scripts listados
  • Los intents generados pasan por el mismo pipeline de validacion que los manuales

Para intents que requieren mas de un dia de trabajo continuo.

En .fab-config.yaml:

long_running:
enabled: true
max_session_hours: 72
checkpoint_strategy: "adaptive"
adaptive_rules:
min_interval_min: 30
max_interval_min: 480
increase_on_green_streak: true
heartbeat_interval_min: 15
recovery:
auto_resume: true
max_retries: 3
state_persistence: "git"
EstrategiaIntervaloCuando usar
time_basedFijo (cada N minutos)Default, sesiones < 8h
phase_basedAl completar cada faseIntents multi-fase claros
adaptiveDinamico segun saludSesiones > 8h, exploratorias
  1. Inicia con min_interval_min (30 min)
  2. 3 checkpoints verdes consecutivos → intervalo sube 50%
  3. Error o test falla → intervalo baja a minimo
  4. Nunca supera max_interval_min (8h)
  • Heartbeat cada 15 min: commit ligero con progress-tracker.md
  • Si no hay heartbeat en 2x intervalo → FAB considerado muerto
  • Dashboard via: python3 baseline/scripts/fab-health-check.sh
  • Notificaciones via messaging bridge (Telegram/Discord/Slack)
  1. Leer HANDOFF.md → estado al ultimo checkpoint
  2. Leer progress-tracker.md → fase y paso actual
  3. Verificar tests → confirmar integridad del ultimo checkpoint
  4. Retomar desde siguiente accion pendiente
  5. Si max_retries alcanzado → marcar intent como blocked, notificar
  • fab-cost-guard.py usa rolling window diario
  • Si gasto del dia > 80% del budget diario → checkpoint + pausa
  • Budget total del intent se divide en budget_diario = total / dias_estimados
  • Metrica: costo acumulado vs costo proyectado (linear projection)
execution:
mode: headless
long_running: true
estimated_hours: 48
checkpoint_strategy: adaptive
budget:
max_usd: 150.0
daily_limit_usd: 60.0

El script fab-kill-switch-drill.sh permite ejecutar simulacros del mecanismo de parada de emergencia sin afectar FABs en produccion. Es una practica recomendada para validar que el kill switch responde correctamente antes de depender de el en una emergencia real.

Ventana de terminal
# Drill basico con output JSON
bash scripts/fab-kill-switch-drill.sh --json
# Drill con output detallado
bash scripts/fab-kill-switch-drill.sh --verbose
VerificacionDescripcion
Senal de paradaEl kill switch envia la senal correctamente
Checkpoint automaticoSe genera checkpoint antes del shutdown
Shutdown gracefulLos procesos terminan de forma ordenada
Tiempo de respuestaEl kill switch responde dentro del SLA definido
TrackFrecuencia
SoloMensual
LeanSemanal
FullSemanal + antes de cada deploy a produccion

DocumentoRelación
CORE_F04_Fullstack_Agent_Builder.mdPerfil del FAB que este guide operacionaliza
Multi_Agent_Orchestration_Guide.md §7Long-running sessions, checkpoints
Swarming_Strategy_Merge_Wall_Guide.mdMerge strategies para parallelism
Context_Engineering_Guide.mdContext management para FABs
Brownfield_Adoption_Guide.mdBootstrap del framework en proyectos existentes
REF_CROSS_FAB_Runtime_Comparison.yamlComparación de runtimes para FABs

Documento creado como parte de AI Software Factory OS v7.7 (Factory-Native) — Phase 1: Intake & Orchestration. Fuentes: Anthropic Claude Code Docs (2026), Gas Town Orchestrator (2026), StrongDM Software Factory (2026), DealInspect Case Study (2026), Druce.ai Claude Code Guide (2026). Kill switch drill agregado en v7.7.