Phase 0 namespace note. All environment variables in this file use the COLIBRI_* namespace (S17 §3). The legacy AMS_* namespace is not read by the Phase 0 runtime. The Phase 0 env floor is COLIBRI_DB_PATH, COLIBRI_LOG_LEVEL, COLIBRI_STARTUP_TIMEOUT_MS; integration variables like COLIBRI_MCP_TIMEOUT and COLIBRI_WEBHOOK_URL are added here as they’re earned.

Not in Phase 0. agent.spawned notifications, agent_spawn/agent_status tools, and src/domains/agents/ are all deferred to Phase 1.5 per ADR-005. P0.9.2’s “Unblocks P0.5.2” line below refers to a δ Model Router that is spec-only in Phase 0 per ADR-005 — P0.9.2 ships the wrapper; the router that consumes it is deferred.

P0.9 — ν Integrations — Agent Prompts

Copy-paste-ready prompts for agents tackling each task in this group. Canonical spec: task-breakdown.md §P0.9 Master bootstrap prompt: agent-bootstrap.md

Group summary

Task ID Title Depends on Effort Unblocks
P0.9.1 MCP Bridge P0.2.1 M federated MCP routing (Slack, Gmail, Linear MCPs)
P0.9.2 Claude API Wrappers P0.1.4 M Phase 1.5 δ Model Router (deferred per ADR-005)
P0.9.3 Notification Channels P0.2.1 S external observability + Slack/webhook notifications

P0.9.1 — MCP Bridge

Spec source: task-breakdown.md §P0.9.1 Extraction reference: docs/reference/extractions/nu-integrations-extraction.md (bridge section) Worktree: feature/p0-9-1-mcp-bridge Branch command: git worktree add .worktrees/claude/p0-9-1-mcp-bridge -b feature/p0-9-1-mcp-bridge origin/main Estimated effort: M (Medium — 2-3 hours) Depends on: P0.2.1 (Core server infrastructure) Unblocks: federated MCP routing (Slack, Gmail, Linear MCPs)

Files to create

  • src/domains/integrations/mcp-bridge.ts — Outbound MCP client wrapper
  • tests/domains/integrations/mcp-bridge.test.ts — Connection, retry, timeout tests

Acceptance criteria

  • McpBridge wraps outbound MCP client calls to external servers
  • connectToServer(url) creates client, returns connected bridge
  • callTool(bridge, name, args) calls remote tool, returns result
  • Timeout: 30s default, configurable via COLIBRI_MCP_TIMEOUT
  • Retry: 3 attempts with exponential backoff on transient errors
  • Test: mock MCP server → verify roundtrip tool call

Pre-flight reading

  • CLAUDE.md — execution rules
  • docs/guides/implementation/task-breakdown.md §P0.9.1 — full spec
  • docs/reference/extractions/nu-integrations-extraction.md (bridge section)
  • MCP Python SDK or Node.js MCP client documentation (pick available version)

Ready-to-paste agent prompt

You are a Phase 0 builder agent for Colibri.

TASK: P0.9.1 — MCP Bridge
Wrap outbound MCP client calls with timeout, retry, and error handling.

FILES TO READ FIRST:
1. CLAUDE.md (execution rules)
2. docs/guides/implementation/task-breakdown.md §P0.9.1
3. docs/reference/extractions/nu-integrations-extraction.md (bridge section)
4. MCP SDK documentation (Node.js @modelcontextprotocol/sdk or similar)

WORKTREE SETUP:
git fetch origin
git worktree add .worktrees/claude/p0-9-1-mcp-bridge -b feature/p0-9-1-mcp-bridge origin/main
cd .worktrees/claude/p0-9-1-mcp-bridge

DEPENDENCIES:
npm install @modelcontextprotocol/sdk

FILES TO CREATE:
- src/domains/integrations/mcp-bridge.ts
  * Class McpBridge:
    - Constructor(url: string, timeout?: number): url is remote MCP server endpoint
    - Properties:
      - client: MCP client instance (StdioClient, HttpClient, WebSocketClient as appropriate)
      - url: remote server URL
      - timeout: milliseconds (default 30000 from COLIBRI_MCP_TIMEOUT env var)
      - retryAttempts: 3
      - retryBackoff: exponential (100ms, 200ms, 400ms)
  * Static async connectToServer(url: string, options?: {timeout?: number}): Promise<McpBridge>
    - Create client based on URL scheme (http://, ws://, stdio://, etc.)
    - Connect to remote server
    - Verify connection (call _list_resources or similar)
    - Return connected McpBridge instance
    - Throw error if connection fails
  * Async callTool(name: string, args: Record<string, any>): Promise<ToolResult>
    - Validate name (non-empty string)
    - Validate args (object or null)
    - Wrap call with timeout (reject if exceeds this.timeout)
    - Retry logic:
      - Attempt 1: call tool
      - If transient error (timeout, 429, 5xx): wait backoff[1] ms, attempt 2
      - If still transient: wait backoff[2] ms, attempt 3
      - If still failed: throw error after all attempts exhausted
    - Log: start, end, latency, status
    - Return {result, latency_ms}
  * Private reconnect(): Promise<void>
    - If connection drops, attempt reconnect up to 2 times
    - Backoff between reconnect attempts
  * Type ToolResult = {result?: any, error?: string, latency_ms: number}

- tests/domains/integrations/mcp-bridge.test.ts
  * Test connectToServer: valid URL → connected bridge, invalid URL → error
  * Test callTool success: call _list_resources on connected server → returns resource list
  * Test callTool with args: call example tool with {param1: value1} → receives result
  * Test timeout: set timeout=100ms, call slow endpoint (simulate 5s delay) → rejects with timeout error
  * Test retry on transient error: mock 429 response on attempt 1, success on attempt 2 → succeeds after retry
  * Test retry exhaustion: mock failure on all 3 attempts → throws error with retry_count=3
  * Test backoff timing: measure retry delays, verify exponential backoff (100ms, 200ms, 400ms)
  * Test latency_ms: measure elapsed time, verify it includes all retries + network latency
  * Test per-call timeout override: create bridge with timeout=30s, call with timeout=5s override → uses 5s

ACCEPTANCE CRITERIA (headline):
✓ McpBridge wraps outbound MCP client calls
✓ connectToServer(url) creates + connects, returns bridge
✓ callTool(name, args) calls remote tool, returns result
✓ Timeout: 30s default, configurable via COLIBRI_MCP_TIMEOUT
✓ Retry: 3 attempts, exponential backoff on transient errors
✓ Mock MCP server roundtrip test

SUCCESS CHECK:
cd .worktrees/claude/p0-9-1-mcp-bridge && npm test && npm run lint

WRITEBACK (after success):
task_update(task_id="P0.9.1", status="done", progress=100)
thought_record(task_id="P0.9.1", branch="feature/p0-9-1-mcp-bridge",
  commit_sha=<your-sha>, tests_run=["npm test","npm run lint"],
  summary="Implemented McpBridge wrapper for outbound MCP client calls. connectToServer(url) creates client, verifies connection. callTool(name, args) executes remote tool with 30s default timeout (configurable via COLIBRI_MCP_TIMEOUT). Retry: 3 attempts with exponential backoff (100ms, 200ms, 400ms) on transient errors (timeout, 429, 5xx). Returns {result, latency_ms}. Tests: successful roundtrip, timeout rejection, retry on 429, retry exhaustion, backoff timing verification.")

FORBIDDENS:
✗ Do not hardcode timeouts (read COLIBRI_MCP_TIMEOUT env var at startup)
✗ Do not silently swallow transient errors (log each retry, expose retry_count)
✗ Do not allow unlimited retries (max 3 attempts)
✗ Do not edit main checkout

NEXT:
P0.9.2 — Claude API Wrappers (wraps Anthropic API calls with rate limiting, logging)

Verification checklist (for reviewer agent)

  • McpBridge class with connectToServer and callTool methods
  • connectToServer verifies connection (not just creates client)
  • callTool wraps with timeout (default 30s from COLIBRI_MCP_TIMEOUT)
  • Retry logic: 3 attempts, exponential backoff (100/200/400ms)
  • Returns {result, latency_ms}
  • Transient error detection (timeout, 429, 5xx)
  • Logging for each attempt and final status
  • Tests: success roundtrip, timeout, retry, backoff, latency measurement
  • npm test and npm run lint pass

Writeback template

task_update:
  task_id: P0.9.1
  status: done
  progress: 100

thought_record:
  task_id: P0.9.1
  branch: feature/p0-9-1-mcp-bridge
  commit_sha: <sha>
  tests_run: ["npm test", "npm run lint"]
  summary: "Implemented McpBridge class wrapping outbound MCP client calls. connectToServer(url) creates client from URL scheme, verifies connection, returns bridge. callTool(name, args) executes remote tool with 30s default timeout (COLIBRI_MCP_TIMEOUT env var, per-call override supported). Retry: 3 attempts with exponential backoff (100ms  200ms  400ms) on transient errors (timeout, 429, 5xx). Returns {result?, error?, latency_ms}. Logging: start, retry attempt, final status. Tests: successful roundtrip, timeout rejection, 429 retry success, retry exhaustion (3 failures), exponential backoff timing, latency measurement."
  blockers: []

Common gotchas

  • Outbound MCP timeouts must be configurable per-call — Some MCPs are slow (e.g., Slack search). Do not enforce a global 30s limit if a caller needs 2 minutes. Provide per-call override in callTool options.
  • Transient error detection is critical — 429 (rate limit), 5xx (server error), and timeout are transient. 4xx (except 429), 403 (forbidden), 401 (auth) are NOT transient; don’t retry. Test this carefully.
  • Exponential backoff prevents thundering herd — If 10 instances retry simultaneously, exponential backoff helps spread the load. Don’t use fixed delays; vary by attempt number.
  • Log every retry — If a caller is debugging a slow integration, they need to see the retry timeline. Log each attempt with timestamp and result. Don’t hide retries inside McpBridge.

P0.9.2 — Claude API Wrappers

Spec source: task-breakdown.md §P0.9.2 Extraction reference: docs/reference/extractions/nu-integrations-extraction.md (Claude API section) Worktree: feature/p0-9-2-claude-api Branch command: git worktree add .worktrees/claude/p0-9-2-claude-api -b feature/p0-9-2-claude-api origin/main Estimated effort: M (Medium — 2-3 hours) Depends on: P0.1.4 (Configuration + env var setup) Unblocks: Phase 1.5 δ Model Router (spec-only in Phase 0 per ADR-005) — P0.9.2 ships the client wrapper now so the router can pick it up when agent runtime comes online

Files to create

  • src/domains/integrations/claude.ts — Anthropic API wrapper
  • tests/domains/integrations/claude.test.ts — API calls, rate limiting, logging tests

Acceptance criteria

  • createCompletion(prompt, options) calls Anthropic API with configured model
  • createCompletionWithTools(prompt, tools, options) tool-use completion
  • API key from ANTHROPIC_API_KEY env var — reconciled R75 Wave H: declared .optional() in the Zod schema (src/config.ts:79); validated at call-time by createCompletion / createCompletionWithTools, which throw AnthropicConfigError if absent. This is Design Invariant 5 — the server boots cleanly when the key is unset for deployments that don’t use the Claude API integration. The original AC wording “validated at startup” is preserved here historically; the shipped contract is “validated before use”.
  • Rate limit handling: 429 → exponential backoff, max 3 retries
  • All API calls logged with: model, prompt_tokens, completion_tokens, latency_ms
  • Test (mock): verify retry logic and logging

Pre-flight reading

  • CLAUDE.md — execution rules
  • docs/guides/implementation/task-breakdown.md §P0.9.2 — full spec
  • docs/reference/extractions/nu-integrations-extraction.md (Claude API section)
  • Anthropic SDK: @anthropic-ai/sdk npm package
  • .env.example (API key env var names)

Ready-to-paste agent prompt

You are a Phase 0 builder agent for Colibri.

TASK: P0.9.2 — Claude API Wrappers
Wrap Anthropic API calls with rate limiting, logging, and error handling.

FILES TO READ FIRST:
1. CLAUDE.md (execution rules)
2. docs/guides/implementation/task-breakdown.md §P0.9.2
3. docs/reference/extractions/nu-integrations-extraction.md (Claude API section)
4. .env.example (API key env var names, check ANTHROPIC_API_KEY)
5. Anthropic SDK documentation (@anthropic-ai/sdk)

WORKTREE SETUP:
git fetch origin
git worktree add .worktrees/claude/p0-9-2-claude-api -b feature/p0-9-2-claude-api origin/main
cd .worktrees/claude/p0-9-2-claude-api

DEPENDENCIES:
npm install @anthropic-ai/sdk

FILES TO CREATE:
- src/domains/integrations/claude.ts
  * Class ClaudeClient:
    - Constructor: read ANTHROPIC_API_KEY from env, validate at startup (throw if missing)
    - Properties:
      - apiKey: string (from ANTHROPIC_API_KEY)
      - model: string (configured, e.g., "claude-3-5-sonnet-20241022")
      - anthropicClient: Anthropic instance
      - rateLimitCooldown: number (default 1000ms, increases with 429 responses)
  * Async createCompletion(prompt: string, options?: {model?: string, max_tokens?: number, temperature?: number}): Promise<CompletionResult>
    - Prepare message: [{role: "user", content: prompt}]
    - Call client.messages.create({model, messages, ...options})
    - Validate response, extract text content
    - Log: model, prompt_tokens, completion_tokens, latency_ms, stop_reason
    - Return {text: string, tokens: {prompt, completion}, latency_ms}
  
  * Async createCompletionWithTools(prompt: string, tools: Tool[], options?: {...}): Promise<ToolCompletionResult>
    - Call client.messages.create({model, messages, tools, tool_choice: "auto"})
    - Handle tool_use content blocks: extract tool_name, tool_input
    - Return {text?: string, tool_calls: [{name, input}], tokens, latency_ms}
  
  * Rate limit handling (both methods):
    - Wrap with retry: max 3 attempts
    - On 429 response: exponential backoff (1s, 2s, 4s)
    - Increase rateLimitCooldown for next call (adaptive)
    - Return after success or all attempts exhausted
  
  * Logging (all calls):
    - Start: {method, model, prompt_length, timestamp}
    - End: {method, model, prompt_tokens, completion_tokens, latency_ms, stop_reason, status}
    - Use logger (not console.log)
  
  * Type CompletionResult = {text: string, tokens: {prompt, completion}, latency_ms}
  * Type ToolCompletionResult = {text?: string, tool_calls: Array<{name, input}>, tokens, latency_ms}
  * Type Tool = {name, description, input_schema}

- tests/domains/integrations/claude.test.ts
  * Test createCompletion: mock API response, verify text extraction + logging
  * Test createCompletionWithTools: mock tool_use response, extract tool_name + input
  * Test API key validation: missing ANTHROPIC_API_KEY → throw at startup
  * Test rate limit 429: mock 429 on attempt 1, success on attempt 2 → succeeds after retry
  * Test rate limit exhaustion: mock 429 on all 3 attempts → throw error after retries
  * Test exponential backoff: measure retry delays, verify 1s → 2s → 4s backoff
  * Test logging: call API, verify logs include model, tokens, latency_ms, stop_reason
  * Test token counting: mock response with usage {input_tokens: 100, output_tokens: 50}, verify logged correctly
  * Test error logging: API error, verify error + status logged, no sensitive data exposed

ACCEPTANCE CRITERIA (headline):
✓ createCompletion(prompt, options) calls Anthropic API
✓ createCompletionWithTools(prompt, tools, options) tool-use completion
✓ API key from ANTHROPIC_API_KEY env var, validated at startup
✓ Rate limit 429 → exponential backoff, max 3 retries
✓ All calls logged: model, prompt_tokens, completion_tokens, latency_ms
✓ Tests verify retry + logging behavior

SUCCESS CHECK:
cd .worktrees/claude/p0-9-2-claude-api && npm test && npm run lint

WRITEBACK (after success):
task_update(task_id="P0.9.2", status="done", progress=100)
thought_record(task_id="P0.9.2", branch="feature/p0-9-2-claude-api",
  commit_sha=<your-sha>, tests_run=["npm test","npm run lint"],
  summary="Implemented ClaudeClient wrapper for Anthropic API. createCompletion(prompt, options) extracts text response. createCompletionWithTools(prompt, tools, options) handles tool_use content blocks. API key validated at startup (ANTHROPIC_API_KEY env var). Rate limiting: 429 responses trigger exponential backoff (1s, 2s, 4s), max 3 retries. All calls logged with model, prompt_tokens, completion_tokens, latency_ms, stop_reason. Tests: successful completions, tool-use extraction, 429 retry/exhaustion, exponential backoff timing, token logging, error cases without credential exposure.")

FORBIDDENS:
✗ Do not log API keys or sensitive tokens (validate but never echo)
✗ Do not allow null/undefined API key at runtime (validate at startup)
✗ Do not expose rate limit backoff times to callers (hide implementation detail)
✗ Do not edit main checkout

NEXT:
P0.9.3 — Notification Channels (dispatch events to log, MCP, webhook)

Verification checklist (for reviewer agent)

  • ClaudeClient constructor validates ANTHROPIC_API_KEY at startup
  • createCompletion returns {text, tokens, latency_ms}
  • createCompletionWithTools handles tool_use blocks, returns tool_calls array
  • Rate limit 429 handling: exponential backoff (1/2/4s), max 3 retries
  • All API calls logged with: model, prompt_tokens, completion_tokens, latency_ms
  • No API key or sensitive data in logs or error messages
  • Tests: successful calls, tool-use, 429 retry, rate limit exhaustion, logging
  • npm test and npm run lint pass

Writeback template

task_update:
  task_id: P0.9.2
  status: done
  progress: 100

thought_record:
  task_id: P0.9.2
  branch: feature/p0-9-2-claude-api
  commit_sha: <sha>
  tests_run: ["npm test", "npm run lint"]
  summary: "Implemented ClaudeClient wrapper for Anthropic API calls. createCompletion(prompt, options) sends message, extracts text response, returns {text, tokens: {prompt, completion}, latency_ms}. createCompletionWithTools(prompt, tools, options) handles tool_use content blocks, returns {text?, tool_calls: [{name, input}], tokens, latency_ms}. API key from ANTHROPIC_API_KEY env var, validated at startup (throws if missing). Rate limiting: 429 responses trigger exponential backoff (1s  2s  4s), max 3 retries. All calls logged with: model, prompt_tokens, completion_tokens, latency_ms, stop_reason. No sensitive data in logs. Tests: completions, tool-use, 429 retry, exhaustion, backoff timing, logging, error cases."
  blockers: []

Common gotchas

  • Anthropic API key handling — never log it, never echo it in errors — If an API call fails, the error response must not include the API key (should be filtered by SDK, but verify). When logging start/end, log {method, model, latency}, NOT {method, model, apiKey}. Add explicit filters in error logging.
  • Token counting from usage field — The API response includes usage: {input_tokens, output_tokens}. Log these fields exactly as provided. Don’t try to estimate tokens; let the API report them.
  • Rate limit backoff is exponential per instance — If you have 5 agents all calling Claude, each will back off independently. This is fine; it naturally spreads the load. But if a global rate limiter is needed, that’s a future concern.
  • Validate API key at startup, not on first request — If API key is missing, the server should fail to start, not on the first user request. This prevents surprise failures in production.

P0.9.3 — Notification Channels

Spec source: task-breakdown.md §P0.9.3 Extraction reference: docs/reference/extractions/nu-integrations-extraction.md (notifications section) Worktree: feature/p0-9-3-notifications Branch command: git worktree add .worktrees/claude/p0-9-3-notifications -b feature/p0-9-3-notifications origin/main Estimated effort: S (Small — 1-2 hours) Depends on: P0.2.1 (Core server infrastructure) Unblocks: external observability + Slack/webhook notifications

Files to create

  • src/domains/integrations/notifications.ts — Event dispatch, channel routing
  • tests/domains/integrations/notifications.test.ts — Channel tests, fire-and-forget verification

Acceptance criteria

  • notify(event, payload) dispatches event to configured channels
  • Channels: log (always on), mcp (MCP notification), webhook (optional)
  • COLIBRI_WEBHOOK_URL env var enables webhook channel
  • Events: task.completed, merkle.finalized, error.critical (no agent.spawned — agent runtime is deferred to Phase 1.5 per ADR-005)
  • Fire-and-forget: notification failures do not block main execution
  • Test: verify each channel receives correct payload for each event type

Pre-flight reading

  • CLAUDE.md — execution rules
  • docs/guides/implementation/task-breakdown.md §P0.9.3 — full spec
  • docs/reference/extractions/nu-integrations-extraction.md (notifications section)
  • .env.example (COLIBRI_WEBHOOK_URL env var)

Ready-to-paste agent prompt

You are a Phase 0 builder agent for Colibri.

TASK: P0.9.3 — Notification Channels
Dispatch events to logging, MCP, and webhook channels (fire-and-forget).

FILES TO READ FIRST:
1. CLAUDE.md (execution rules)
2. docs/guides/implementation/task-breakdown.md §P0.9.3
3. docs/reference/extractions/nu-integrations-extraction.md (notifications section)
4. .env.example (COLIBRI_WEBHOOK_URL env var)

WORKTREE SETUP:
git fetch origin
git worktree add .worktrees/claude/p0-9-3-notifications -b feature/p0-9-3-notifications origin/main
cd .worktrees/claude/p0-9-3-notifications

FILES TO CREATE:
- src/domains/integrations/notifications.ts
  * Type NotificationEvent = "task.completed" | "merkle.finalized" | "error.critical"
    // NOTE: "agent.spawned" is NOT a Phase 0 event — agent runtime is deferred to Phase 1.5 per ADR-005.
  * Type NotificationPayload = Record<string, any> (flexible, event-specific)
  * Type Channel = "log" | "mcp" | "webhook"
  * Function notify(event: NotificationEvent, payload: NotificationPayload): void (fire-and-forget)
    - Do NOT await or handle errors (fire-and-forget)
    - Dispatch to all enabled channels asynchronously (Promise-based, no await)
    - Log dispatch start (debug level)
    - Let errors be swallowed (but log them at the handler level)
  * Private async dispatchToLog(event, payload)
    - Log event at INFO level: {event, timestamp, payload_keys: Object.keys(payload)}
  * Private async dispatchToMcp(event, payload)
    - Call notification MCP tool (via MCP client or internal tool handler)
    - Tool signature: notification_emit(event: string, payload: object) → {success: bool}
  * Private async dispatchToWebhook(event, payload)
    - Read COLIBRI_WEBHOOK_URL env var
    - If not set: skip (webhook disabled)
    - POST to webhook URL: {event, payload, timestamp}
    - Timeout: 5s
    - On failure: log error at WARN level, do NOT rethrow
  * Static singleton: export instance
  * Configuration at startup:
    - Validate COLIBRI_WEBHOOK_URL if set (valid URL format)
    - Log enabled channels on startup

- tests/domains/integrations/notifications.test.ts
  * Test notify with task.completed event: verify log channel receives event
  * Test notify with merkle.finalized event: verify log + MCP channels receive event
  * Test notify with error.critical event: verify all channels notified
  * Test that `agent.spawned` is NOT accepted (TypeScript should reject it, runtime should reject it too) — agent runtime is Phase 1.5
  * Test webhook disabled: notify without COLIBRI_WEBHOOK_URL set → webhook handler skipped
  * Test webhook enabled: set COLIBRI_WEBHOOK_URL to mock endpoint, notify → POST received
  * Test webhook timeout: mock slow endpoint (10s), notify with 5s timeout → logs warning, doesn't throw
  * Test webhook error: mock 500 response, notify → logs warning, doesn't throw
  * Test fire-and-forget: notify returns immediately (no await), handlers execute async
  * Test MCP notification tool: mock tool invocation, verify event + payload passed
  * Test payload flexibility: notify with task.completed + custom fields → all fields in logs

ACCEPTANCE CRITERIA (headline):
✓ notify(event, payload) dispatches to enabled channels
✓ Channels: log (always), mcp, webhook (if COLIBRI_WEBHOOK_URL)
✓ Events: task.completed, merkle.finalized, error.critical  (no agent.spawned — Phase 1.5)
✓ Fire-and-forget: errors swallowed (logged only)
✓ Each channel receives correct payload for each event

SUCCESS CHECK:
cd .worktrees/claude/p0-9-3-notifications && npm test && npm run lint

WRITEBACK (after success):
task_update(task_id="P0.9.3", status="done", progress=100)
thought_record(task_id="P0.9.3", branch="feature/p0-9-3-notifications",
  commit_sha=<your-sha>, tests_run=["npm test","npm run lint"],
  summary="Implemented notify(event, payload) dispatcher for task.completed, merkle.finalized, error.critical events (agent.spawned deferred to Phase 1.5 per ADR-005). Channels: log (always on), mcp (MCP notification tool), webhook (if COLIBRI_WEBHOOK_URL set). Fire-and-forget: no await, errors logged (not thrown). Log channel: INFO level with event + timestamp. MCP channel: calls notification_emit tool. Webhook: POST {event, payload, timestamp}, 5s timeout, failures logged at WARN. Tests: all event types, channels enabled/disabled, webhook timeout/error handling, fire-and-forget verification.")

FORBIDDENS:
✗ Do not block main execution on notification failures (fire-and-forget)
✗ Do not allow unhandled promise rejections (catch all async errors)
✗ Do not validate webhook URL format at dispatch time (validate at startup)
✗ Do not edit main checkout

NEXT:
Phase 0 complete: P0.1–P0.9 ready for integration testing

Verification checklist (for reviewer agent)

  • notify function is fire-and-forget (no await in caller)
  • Supported events: task.completed, merkle.finalized, error.critical (NO agent.spawned — Phase 1.5)
  • Log channel: always enabled, logs at INFO level
  • MCP channel: calls notification_emit tool with {event, payload}
  • Webhook channel: enabled if COLIBRI_WEBHOOK_URL set, POSTs {event, payload, timestamp}
  • Webhook timeout: 5s max, logs WARN on timeout or error
  • All errors caught and logged, never rethrown
  • Tests: all events, all channels, webhook enabled/disabled, error handling
  • npm test and npm run lint pass

Writeback template

task_update:
  task_id: P0.9.3
  status: done
  progress: 100

thought_record:
  task_id: P0.9.3
  branch: feature/p0-9-3-notifications
  commit_sha: <sha>
  tests_run: ["npm test", "npm run lint"]
  summary: "Implemented notify(event: NotificationEvent, payload: NotificationPayload): void dispatcher (fire-and-forget). Supported events: task.completed, merkle.finalized, error.critical (agent.spawned deferred to Phase 1.5 per ADR-005). Channels: (1) log—always on, logs event + payload at INFO; (2) mcp—calls notification_emit MCP tool; (3) webhook—enabled if COLIBRI_WEBHOOK_URL set, POSTs {event, payload, timestamp} with 5s timeout. All errors caught, logged at WARN, never rethrown. Main execution never blocked. Tests: all event types, channel dispatch verification, webhook enabled/disabled, timeout/error handling (no exception), fire-and-forget immediate return."
  blockers: []

Common gotchas

  • Fire-and-forget means swallow errors — but log them — Do not throw errors from notification handlers. Catch and log them at WARN level. This prevents notification failures from crashing the main task. But if you don’t log, debugging becomes impossible.
  • Webhook configuration validation at startup, not dispatch time — Check COLIBRI_WEBHOOK_URL format (valid URL) when the server starts. Don’t wait until the first notify() call. This catches configuration errors early.
  • Timeout must be shorter than main operation — If a webhook hangs, don’t let it block the task. Use a 5s timeout; if webhook doesn’t respond, log and continue. The webhook can be optional.
  • Flexible payload structure — Don’t enforce a strict schema for payload. Each event type can have different fields. Tests should verify that custom fields are preserved through the notification pipeline.

Phase 0 Summary

P0.1–P0.9 implementation roadmap complete. These 32 tasks build the foundation:

  • Execution (P0.1–P0.6): 18 tasks — infrastructure, core, pipeline, lifecycle, router, skills
  • Legitimacy (P0.7–P0.8): 6 tasks — decision trail, proof store (hash chains, Merkle trees)
  • Integrations (P0.9): 3 tasks — MCP bridge, Claude API, notifications

All three agent-prompt files cover headline acceptance criteria, forbiddens, gotchas, and writeback templates. Ready for dog-fooding.

Back to task-prompts index


Back to top

Colibri — documentation-first MCP runtime. Apache 2.0 + Commons Clause.

This site uses Just the Docs, a documentation theme for Jekyll.