Claude Code Instructions — Baft Repository Build¶
Date: 2026-03-13
Repo: $ITP_ROOT/baft/
For: Claude Code (EXECUTOR role)
Your mission¶
Build the Baft repository from the skeleton provided. Baft is the ITP-specific configuration layer on top of Loom. All architectural decisions are already made — this is implementation work.
Read before starting:
CLAUDE.md— operating rules for this repodocs/architecture/ITP_MULTI_AGENT_ARCHITECTURE_v0_5.md— the canonical node spec$ITP_ROOT/loom/configs/workers/_template.yaml— Loom worker config format$ITP_ROOT/loom/configs/workers/summarizer.yaml— real example worker config
Session 1: Worker configs + MCP gateway (Sprint 1, Batch A)¶
Step 1.1 — Read the Loom worker template format¶
cat $ITP_ROOT/loom/configs/workers/_template.yaml
cat $ITP_ROOT/loom/configs/workers/summarizer.yaml
Understand the schema before writing any worker config. Every worker config needs:
name— matches the tool name exposed via MCPdescription— shown to MCP clientssystem_prompt— full prompt text (multi-line YAML string)input_schema— JSON Schema for the input payloadoutput_schema— JSON Schema for the outputknowledge_sources— list of files to inject into contextdefault_model_tier—local|standard|frontier
Step 1.2 — Write worker configs (Batch A: core operational)¶
Read architecture doc Section "Node Definitions" for each node. Extract:
- "System prompt core" →
system_promptfield - "Input spec" YAML block →
input_schema - "Output spec" YAML block →
output_schema - "Knowledge silo" list →
knowledge_sources
Write in this order:
File: configs/workers/de_database_engineer.yaml
- Node: DE (Database Engineer), architecture doc Node 2
- Tier: local
- This is a
processor(non-LLM) type if Loom supports it, otherwise LLM with strict output format - Knowledge: all JSON schemas from framework repo, current data files for ID lookup
- Input schema:
integration_request(operations list) - Output schema:
integration_resultwithoperations_attempted,operations_succeeded,validation_result,ga_notification - System prompt: include the mid-session GA trigger behavior
File: configs/workers/sp_source_processor.yaml
- Node: SP (Source Processor), architecture doc Node 1
- Tier: local (Haiku)
- Knowledge: source hierarchy (
itp_source_hierarchy.yaml), epistemic rules, entity name registry - Input schema:
source_bundle - Output schema:
extracted_claims - CRITICAL: knowledge_sources must NOT include framework module content or analytical data
File: configs/workers/ia_intelligence_analyst.yaml
- Node: IA (Intelligence Analyst), architecture doc Node 3
- Tier: frontier (Opus)
- Knowledge: full framework content (ITB + ISA outputs), full DB snapshot (variables, observations, scenarios, traps, gaps), epistemic rules, standing analytical rules
- Input schema:
analytical_input - Output schema:
analytical_outputincludingintegration_specfor DE - This is the longest system prompt — include all standing rules, sophisticated actor default, factional neutrality, anti-Islamophobic framing discipline
File: configs/workers/xv_cross_validator.yaml
- Node: XV (Cross-Validator)
- Tier: local (Haiku)
- Knowledge: entity ID registry only (list of valid IDs and module codes — generate from DB)
- Input: list of entity references to validate
- Output: validation result with any broken refs flagged
File: configs/workers/in_input_node.yaml
- Node: IN (Input Node)
- Tier: local (Haiku)
- Knowledge: minimal — just the inbox queue format
- Input: natural language note from human (time-sensitive finding)
- Output: structured
inbox_itemYAML appended toitp-workspace/inbox_queue.yaml
Step 1.3 — Write worker configs (Batch B: audit nodes)¶
CRITICAL SILO ISOLATION RULE:
LA, PA, RT must NOT have any knowledge_sources pointing to:
framework/data directory (any YAML entities)- Framework module content (ITB/ISA)
- ITP-specific documents
These nodes are blind. They receive only the neutralized analytical text and their evaluation rubric.
File: configs/workers/tn_terminology_neutralizer.yaml
- Node: TN, architecture doc Node 3.1
- Tier: local (Haiku)
- Knowledge:
pipeline/config/itp_terminology_registry.yamlONLY - Input: IA analytical output YAML
- Output: neutralized output with replacements_applied table + reverse_map
- System prompt: extremely short — it's a mechanical text transformation
File: configs/workers/la_logic_auditor.yaml
- Node: LA (Logic Auditor), architecture doc Node 4
- Tier: standard (Sonnet)
- Knowledge: ROBOTIC-LLM rubric (from repo:
docs/references/ROBOTIC-LLM.md) — no ITP content - Input: neutralized analytical text (post-TN)
- Output: Markdown table with [Dimension, Score, Justification] per ROBOTIC-LLM format
- System prompt: Use ROBOTIC-LLM Master Geopolitical Prompt verbatim as base, extend with:
- Dimension 1: Factual & Historical Accuracy (same)
- Dimension 2: Causal Logic & Second-Order Effects (same)
- Dimension 3: ITP-specific — "Evidence Chain Integrity: Does each conclusion trace explicitly to cited evidence, or does the text rely on asserted authority?"
- Dimension 4: ITP-specific — "Counterfactual Robustness: Does the analysis consider and explicitly rule out alternative explanations for the observed phenomena?"
File: configs/workers/pa_perspective_auditor.yaml
- Node: PA (Perspective Auditor), architecture doc Node 5
- Tier: standard (Sonnet)
- Knowledge: ROBOTIC-LLM rubric only — no ITP content
- Input: neutralized analytical text
- Output: Markdown table with [Dimension, Score, Justification]
- System prompt: ROBOTIC-LLM base, focused on Dimension 3 (Perspective Bias) extended:
- Dimension 1: Single-actor centrism (does analysis over-weight one faction's perspective?)
- Dimension 2: Temporal bias (does analysis treat current wartime context as permanent state?)
- Dimension 3: Western analytical frame contamination (are conclusions shaped by Western policy preferences?)
- Dimension 4: Diaspora vs. internal perspective balance
File: configs/workers/rt_red_teamer.yaml
- Node: RT (Red Teamer), architecture doc Node 6
- Tier: frontier (Opus) — or a different provider for Tier 4 (flag this in comments)
- Knowledge: no ITP framework — adversarial from position of ignorance
- Input: neutralized analytical text
- Output: structured challenge list with
challenge_id,claim_challenged,counter_argument,strength(1–10),evidence_basis - System prompt: "You are an adversarial reviewer. Your role is to find the strongest possible objections to the provided analysis. Do not soften challenges. For each challenge, rate strength 1–10 where 10 = the analysis is fundamentally wrong on this point. Output YAML only."
File: configs/workers/as_audit_synthesizer.yaml
- Node: AS (Audit Synthesizer), architecture doc Node 7
- Tier: standard (Sonnet)
- Knowledge:
itp-workspace/human_decision_log.yaml(for blind-spot detection) — NO ITP framework - Input: outputs from LA + PA + RT (all three)
- Output:
audit_reportwith:conflictslist,consensus_findings,recommended_human_decisions,blind_spot_alerts
Step 1.4 — Write worker configs (Batch C: scheduled/background)¶
File: configs/workers/sa_session_advisor.yaml
- Node: SA (Session Advisor), architecture doc Node 0.1
- Tier: local (Haiku)
- Knowledge:
pipeline/config/itp_cognitive_profile.yaml, tier rules, session transcript (injected at runtime) - Output:
sa_advisoryYAML with flags array - System prompt: "You are a session quality monitor. Observe the interaction transcript. Flag: fatigue indicators, priority drift, confirmation bias, tunnel vision, emotional escalation, quality gate bypass, tier mismatch. Output YAML flags only. Do not analyze geopolitical content. Do not speak to the human."
- Note: receives session transcript via
payload.transcriptfield
File: configs/workers/wt_watch_tower.yaml
- Node: WT (Watch Tower)
- Tier: standard (Sonnet)
- Knowledge:
pipeline/config/itp_watch_list.yaml, channel registry - Tool access: web_search enabled in Loom backend config
- Input:
watch_list_path+ optionalmode(scan | agenda_assembly) - Output:
watch_resultswith per-item findings + agenda_items for AP
File: configs/workers/ni_narrative_intelligence.yaml
- Node: NI (Narrative Intelligence)
- Tier: standard (Sonnet)
- Knowledge: channel registry (faction tags, source tiers), prior analysis statistics
- Input: bulk Telegram corpus (post-interleave) + time_range + analysis_modes
- Output:
ni_findingsarray (coordination, silence, divergence, velocity, tone findings) - System prompt: focused on pattern detection in media behavior, NOT geopolitical interpretation
Step 1.5 — Write orchestrator pipeline configs¶
Read $ITP_ROOT/loom/configs/orchestrators/rag_pipeline.yaml for format.
File: configs/orchestrators/itp_quick.yaml — single-stage, just DE
File: configs/orchestrators/itp_standard.yaml — SP → IA → DE (sequential, pass output forward)
File: configs/orchestrators/itp_audit.yaml — TN → [LA, PA, RT] (parallel) → AS
For the audit pipeline, LA/PA/RT must run in parallel (Loom orchestrator supports parallel decomposition).
Step 1.6 — Update configs/mcp/itp.yaml¶
The skeleton itp.yaml is already in the repo. Verify all worker names match the name fields in the worker configs. Add any missing tools.
Step 1.7 — Validate¶
# YAML syntax check all worker configs
for f in configs/workers/*.yaml; do
python3 -c "import yaml; yaml.safe_load(open('$f'))" && echo "OK: $f" || echo "FAIL: $f"
done
# YAML syntax check orchestrators
for f in configs/orchestrators/*.yaml; do
python3 -c "import yaml; yaml.safe_load(open('$f'))" && echo "OK: $f" || echo "FAIL: $f"
done
# Try starting MCP server dry run
loom mcp --config configs/mcp/itp.yaml --dry-run 2>/dev/null || echo "Note: --dry-run may not be supported; check loom mcp --help"
Session 2: Data layer (Sprint 1, Batch B)¶
Step 2.1 — Run DuckDB import¶
cd $ITP_ROOT/baft
python pipeline/scripts/itp_import_to_duckdb.py
python pipeline/scripts/itp_import_to_duckdb.py --stats
Fix any import errors. Common issues:
- YAML keys don't match expected entity type names → update
ENTITY_YAML_FILESdict in script - Missing
idfield on some entities → add fallback ID generation (use list index) - Encoding issues → ensure
encoding="utf-8"everywhere
Step 2.2 — Build supporting pipeline config files¶
These are short files needed by workers:
pipeline/config/itp_source_hierarchy.yaml — 5-tier source taxonomy:
- Tier 1: Official regime primary sources (KHAMENEI.IR, state broadcaster, official agencies)
- Tier 2: Semi-official / regime-affiliated media with editorial function (Fars, Tasnim, ISNA, IRNA)
- Tier 3: Independent media, diaspora outlets with transparent sourcing (Iran International, IranWire)
- Tier 4: Single-source, unverified, or agenda-driven
- Tier 5: Social media noise — requires heavy verification Include: calibration rules per tier, how epistemic_tag maps to tier
pipeline/config/itp_epistemic_rules.yaml — tagging definitions:
- Fact: Directly stated in Tier 1–2 source, independently corroborated
- Inference: Logically follows from Facts with stated reasoning chain
- Uncertain: Plausible but competing explanations exist; single source or Tier 3–4
- Speculation: Insufficient evidence; analytical hypothesis only Include: confidence band definitions (High/Medium/Low/Marginal) and combination rules
pipeline/config/itp_cognitive_profile.yaml — SA node input:
- Session duration thresholds: flag at 90min, alert at 150min, critical at 240min
- Hyperfocus indicators: 3+ consecutive exchanges on same sub-topic
- Fatigue markers: increasing terseness, typo frequency, "good enough" language
- Quality degradation: shortening response length, fewer epistemic tags
- Priority drift definition: session objective stated → time spent on lower-priority work
- Bias patterns: known tendency to dismiss RT challenges without new evidence
pipeline/config/itp_entity_name_registry.yaml — generate from framework data:
python3 -c "
import yaml
from pathlib import Path
data = yaml.safe_load(open('$ITP_ROOT/framework/data/variables.yaml'))
# Extract entity names and aliases
# Write to pipeline/config/itp_entity_name_registry.yaml
"
At minimum: all entity IDs, module codes (ITB-A1 through ITB-A13, ISA sections), observation IDs, scenario IDs.
Step 2.3 — Create workspace directories¶
mkdir -p itp-workspace/rag
mkdir -p itp-workspace/exports
echo "itp-workspace/" >> .gitignore # already in .gitignore, verify
Step 2.4 — Copy reference docs into repo¶
mkdir -p docs/architecture docs/references
cp ~/path/to/ITP_MULTI_AGENT_ARCHITECTURE_v0_5.md docs/architecture/
cp ~/path/to/ROBOTIC-LLM.md docs/references/
Note: ROBOTIC-LLM.md path is wherever Hooman has it. Ask if unclear.
Session 3: Loom gap fix + end-to-end validation (Sprint 1, Batch C)¶
Step 3.1 — Fix Loom streamable HTTP transport (Gap 1)¶
File: $ITP_ROOT/loom/src/loom/mcp/server.py
Function: run_streamable_http()
Read the current implementation first:
Replace the stub with a working implementation. The FastMCP library should provide an ASGI app wrapper. Check what's available:
cd $ITP_ROOT/loom
python3 -c "import mcp.server.fastmcp; help(mcp.server.fastmcp)" 2>/dev/null | head -50
If FastMCP.from_server() or similar exists, use it. Otherwise use the mcp.server.streamable_http module directly. Research the correct pattern from mcp-python documentation.
After fixing, validate:
cd $ITP_ROOT/baft
loom mcp --config configs/mcp/itp.yaml --transport streamable-http --port 8765 &
sleep 2
curl http://localhost:8765/health
kill %1
Commit the fix to the Loom repo separately:
cd $ITP_ROOT/loom
git add src/loom/mcp/server.py
git commit -m "Fix streamable HTTP MCP transport (was stub, now functional)"
Step 3.2 — End-to-end Tier 1 validation¶
cd $ITP_ROOT/baft
# Start infrastructure
nats-server &
valkey-server &
sleep 1
# Start DE worker only (Tier 1 test)
loom worker --config configs/workers/de_database_engineer.yaml --tier local --nats-url nats://localhost:4222 &
# Start MCP gateway (stdio)
# In another terminal, start Claude Code and connect to MCP
# Test via loom CLI submit:
loom submit '{"type": "integration_request", "operations": [{"action": "validate_only", "target": "data/variables.yaml"}]}' \
--worker-type de_database_engineer \
--nats-url nats://localhost:4222
Expected result: DE returns integration_result with validation_result: PASS.
Step 3.3 — End-to-end Tier 2 validation¶
Start SP + IA + DE workers. Run a test source bundle through the itp_standard pipeline:
# Create a minimal test source bundle
cat > /tmp/test_source_bundle.yaml << 'EOF'
type: source_bundle
items:
- url: "https://example.com/test"
language: en
source_tier: 3
retrieval_date: "2026-03-13"
raw_text: |
Iranian state media reported that IRGC commanders met with Larijani
to discuss succession arrangements following the February strikes.
EOF
loom submit "$(cat /tmp/test_source_bundle.yaml)" \
--worker-type sp_source_processor \
--nats-url nats://localhost:4222
Validate that SP returns extracted_claims with properly tagged claims.
Step 3.4 — Commit initial state¶
cd $ITP_ROOT/baft
git init
git add .
git commit -m "Session 1: Initial Baft repository — worker configs, MCP gateway, data layer"
Ongoing maintenance rules¶
- Before every commit:
for f in configs/**/*.yaml; do python3 -c "import yaml; yaml.safe_load(open('$f'))"; done - After any worker config change: restart the affected worker process
- After any MCP config change: restart the MCP gateway
- After framework data changes: re-run
python pipeline/scripts/itp_import_to_duckdb.py - Terminology registry additions: EP flags in session, Code adds entry with
first_sessionandpublishedfields - Watch list additions: Code adds from framework variables + human adds channel_routing manually
When to stop and ask¶
- If a Loom API or schema has changed since the architecture was written → check Loom CLAUDE.md
- If a worker config field is undocumented → read
_template.yamlandbuilding-workflows.mdin Loom docs - If an import or dependency conflicts → ask before trying to fix the dependency
- If the streamable HTTP fix requires more than ~100 lines → something is wrong; ask before continuing