# Agentic Bridle — Phased Implementation Plan **Status:** approved 2026-08-08 by Colby Whitlock (org lead) **Source design:** [`Agentic Bridle MCP Plan.md`](../Agentic%20Bridle%20MCP%20Plan.md) **Decision records:** [`docs/adr/`](adr/) This document turns the design doc into an ordered, dependency-correct build plan. Every architectural decision it depends on is recorded as an ADR; this plan is the schedule, the ADRs are the reasoning. --- ## Locked decisions | Area | Decision | ADR | |---|---|---| | Deployment | One Rust binary (gateway, rules, memory, admin API, panel); Qdrant / Kanboard / Gitea MCP / embeddings as separate compose services, all traffic routed through the binary | [0001](adr/0001-single-binary-with-routed-services.md) | | Transport | Streamable HTTP only (`/mcp`) | [0002](adr/0002-streamable-http-only.md) | | Auth | Bridle-issued per-agent bearer tokens; `clientInfo` is a display hint, never a permission source | [0003](adr/0003-per-agent-bearer-tokens.md) | | Storage | SQLite (`bridle.db`) | [0004](adr/0004-sqlite-datastore.md) | | Rule delivery | `get_rules` tool, with hard gating — every other tool errors until `get_rules` succeeds in that session | [0005](adr/0005-get-rules-tool-with-session-gating.md) | | Rule scope | Full rendered document, never a dynamic subset | [0006](adr/0006-full-document-rule-delivery.md) | | Multi-project | `project_id` in schema and tool signatures from day one; single-project UX at 1.0 | [0007](adr/0007-project-id-from-day-one.md) | | `enforcement` tier | Scoped to Bridle-mediated actions; bypass documented as a known limit | [0008](adr/0008-enforcement-scoped-to-mediated-actions.md) | | Degradation | Stable tool list always; downed upstreams return structured errors | [0009](adr/0009-stable-tool-list-on-upstream-failure.md) | | Embeddings | Generic OpenAI-compatible endpoint; model + dimension recorded in collection metadata, mismatched writes refused | [0010](adr/0010-openai-compatible-embeddings.md) | | Pattern RAG | Gated behind a throwaway validation spike | [0011](adr/0011-pattern-rag-behind-spike.md) | | Renderers v1 | `CLAUDE.md` + `AGENTS.md` (Codex); Kimi/DeepSeek dialects post-1.0 | [0012](adr/0012-v1-renderer-targets.md) | --- ## Phase 0 — Foundations **Size:** small Cargo workspace, `docs/adr/` populated, compose skeleton (bridle plus profiles for `with-qdrant`, `with-kanboard`, `with-embeddings`), Gitea CI on pull request, `.env.example`, Docker secrets wiring. **Exit criterion:** `docker compose up` yields a health-checked binary that completes an MCP `initialize` over Streamable HTTP and advertises zero tools. ## Phase 1 — Gateway core **Size:** large The security and accountability spine. Everything later assumes it exists. - Streamable HTTP MCP server; session lifecycle and per-session state, including the *rules-fetched* flag that phase 2's gate depends on. - Token auth → agent identity → permissions lookup. Token issuance, rotation, and revocation through the admin API. - SQLite schema carrying `project_id` throughout: `projects`, `agents`, `tokens`, `permissions`, `audit_log`. - Append-only audit log written from the first request, with source attribution (user / agent / vendor). - Docker-secrets loading, structured logging, per-request timeouts. - Admin API skeleton — the Web Panel's contract starts here so it is not written twice. **Exit criterion:** two agents connect under distinct authenticated identities with different permission sets; a forged `clientInfo` changes nothing about what either may do; every call appears in the audit log correctly attributed. ## Phase 2 — Rules server, renderers, gating **Size:** large The core thesis of the project. - Canonical schema v1 with `schema_version` and JSON Schema validation: guardrails, patterns, provenance, agent profiles, permissions. - Storage and full CRUD admin API. - `get_rules(project_id, agent_type)` plus the gate: any other tool call before it returns a structured error naming the required call. - `CLAUDE.md` and `AGENTS.md` renderers under golden-file tests. Renderer correctness is the deliverable of this phase, so it carries real test coverage. - Agent-proposal tools (`propose_guardrail`, `add_pattern`) writing to an approval queue. Nothing auto-applies. **Exit criterion:** one edit in Bridle demonstrably changes behavior in a live Claude Code session *and* a live Codex session; a session that skips `get_rules` cannot use any tool. ## Phase 3 — Upstream proxy and Gitea **Size:** medium - Generic upstream-MCP client with connection pooling and health tracking. - Tool namespacing, stable across restarts (see open item 1). - Per-agent tool filtering driven by the phase-1 permissions table. - Credential injection per request; upstreams never hold their own tokens. - Failure isolation per [ADR-0009](adr/0009-stable-tool-list-on-upstream-failure.md). **Exit criterion:** an agent completes a full Gitea PR workflow through Bridle while holding no Gitea token itself; killing the Gitea MCP container degrades only Gitea tools. ## Phase 4 — Kanboard wrapper **Size:** medium Thin REST-to-MCP translation inside the binary. Validates both the author-our-own-upstream path and the bundled-versus-external compose story. **Exit criterion:** task CRUD from an agent against a bundled Kanboard and against an external one, switched by environment variable alone. ## Phase 5 — Session memory **Size:** medium Qdrant integration, pluggable embeddings with model/dimension guarding, `_sessions` collections, `remember` and `recall` tools, retention and purge. **Exit criterion:** a decision recorded by Claude Code is recalled by Codex in a later session; swapping embedding models is detected and refused rather than silently corrupting the collection. ## Phase 5.5 — Pattern RAG spike **Size:** small, throwaway Index one real example repo with naive chunking, run roughly ten realistic queries, and judge whether the retrieved chunks would actually improve an agent's output. This is an explicit go/no-go gate on phase 6 — the spike code is discarded either way. **Exit criterion:** a written go/no-go recommendation with the queries and retrieved chunks as evidence. ## Phase 6 — Pattern-example RAG **Size:** large — **conditional on phase 5.5 passing** Repo fetch → tree-sitter AST-aware chunking → tagged upsert; `search_pattern_examples(query, arch_tag?)`; `refresh_pattern_example(reference_tag)` with clean replacement of prior vectors. **Exit criterion:** a named pattern's example repo is indexed and returns relevant code for a natural-language query; a refresh fully replaces prior vectors for that tag. ## Phase 7 — Web Panel **Size:** large Served by the same binary. The admin API already exists from phases 1–6, so this phase is a client against it: approval queue, rules editor, secrets (write, rotate, revoke — never display), audit browser, memory browser, integration status. **Exit criterion:** every phase 1–6 operation is achievable through the UI with no CLI or database access. ## Phase 8 — Guardrail sourcing **Size:** small Manual paste-a-document flow producing proposed guardrails into the existing approval queue. Automated vendor-doc fetching only if the manual workflow proves its worth. **Exit criterion:** a vendor guidance document becomes a reviewable proposal that requires explicit approval before entering the canonical ruleset. ## Phase 9 — Multi-project, hardening, 1.0 **Size:** medium Project switcher on the day-one schema, backup and restore, install documentation, packaging, release. **Exit criterion:** a second person can stand up Bridle from the README against their own Gitea and reach a working agent session. --- ## Cross-cutting - Security review on every phase that touches authentication or secrets. - Documentation written as features land, not retrofitted. - One ADR per architectural decision. - Golden-file and integration tests from phase 2 onward. --- ## Deviations from the design doc's original 1–8 ordering - **Audit log and multi-project schema moved to phase 1.** Both are cheap to add upfront and expensive to retrofit across five subsystems. - **Web Panel moved after memory,** but its admin API is built incrementally from phase 1 rather than all at the end. - **Vendor guardrail sourcing downgraded to manual-first.** Scraping vendor documentation is fragile; the manual path captures most of the value at a fraction of the cost. - **Pattern-example RAG gated behind a spike** rather than committed to outright. --- ## Open items Not blocking phase 0. Sensible defaults noted where they exist. 1. **Tool namespacing convention.** Proposed default: `gitea__create_pr` (double underscore). Effectively permanent once agents start depending on it. 2. **Audit log integrity.** Plain append-only table, or hash-chained and tamper-evident? 3. **Approval queue notifications.** Panel-only, or out-of-band webhook/email when an agent proposes a change? 4. **Web Panel stack.** Server-rendered Rust templates versus an embedded SPA. Not blocking until phase 7. 5. **Kanboard instance and auth model.** API user versus application token. 6. **CI.** Gitea runner availability, and whether PR-gated CI starts at phase 0. 7. **Repository visibility** and whether GPLv3 remains the license.