Turns the design doc into an ordered, dependency-correct build plan (phases 0-9) with an exit criterion per phase, and records the twelve architectural decisions it rests on as ADRs. Decisions: single Rust binary with routed compose services; Streamable HTTP only; per-agent bearer tokens with clientInfo as a display hint only; SQLite; get_rules delivered via session gating; full-document rule delivery; project_id from day one; enforcement tier scoped to Bridle-mediated actions; stable tool list on upstream failure; OpenAI-compatible embeddings with model/dim guarding; pattern RAG gated behind a spike; CLAUDE.md + AGENTS.md as v1 renderer targets. Deviates from the design doc's original 1-8 ordering by moving the audit log and multi-project schema into phase 1, building the admin API incrementally rather than all at the Web Panel phase, downgrading vendor guardrail sourcing to manual-first, and gating the pattern-example RAG behind a validation spike. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019RZerbsHGF9Ka3bKhCjJ9m
9.2 KiB
Agentic Bridle — Phased Implementation Plan
Status: approved 2026-08-08 by Colby Whitlock (org lead)
Source design: Agentic Bridle MCP Plan.md
Decision records: docs/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 |
| Transport | Streamable HTTP only (/mcp) |
0002 |
| Auth | Bridle-issued per-agent bearer tokens; clientInfo is a display hint, never a permission source |
0003 |
| Storage | SQLite (bridle.db) |
0004 |
| Rule delivery | get_rules tool, with hard gating — every other tool errors until get_rules succeeds in that session |
0005 |
| Rule scope | Full rendered document, never a dynamic subset | 0006 |
| Multi-project | project_id in schema and tool signatures from day one; single-project UX at 1.0 |
0007 |
enforcement tier |
Scoped to Bridle-mediated actions; bypass documented as a known limit | 0008 |
| Degradation | Stable tool list always; downed upstreams return structured errors | 0009 |
| Embeddings | Generic OpenAI-compatible endpoint; model + dimension recorded in collection metadata, mismatched writes refused | 0010 |
| Pattern RAG | Gated behind a throwaway validation spike | 0011 |
| Renderers v1 | CLAUDE.md + AGENTS.md (Codex); Kimi/DeepSeek dialects post-1.0 |
0012 |
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_idthroughout: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_versionand 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.mdandAGENTS.mdrenderers 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.
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, <project>_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.
- Tool namespacing convention. Proposed default:
gitea__create_pr(double underscore). Effectively permanent once agents start depending on it. - Audit log integrity. Plain append-only table, or hash-chained and tamper-evident?
- Approval queue notifications. Panel-only, or out-of-band webhook/email when an agent proposes a change?
- Web Panel stack. Server-rendered Rust templates versus an embedded SPA. Not blocking until phase 7.
- Kanboard instance and auth model. API user versus application token.
- CI. Gitea runner availability, and whether PR-gated CI starts at phase 0.
- Repository visibility and whether GPLv3 remains the license.