Skip to content

Architecture Decision Records

Decision log (MADR format, see the adr skill). A decision without an ADR doesn't exist.

# Title Status Date
0001 Development environment, AI governance and Kanban process accepted 2026-06-11
0002 Product architecture: FastAPI + React SPA, SQLite, Claude-primary LLM, plan-approve-execute + sanity checker accepted 2026-06-11
0003 CI-gated auto-merge replaces the manual pre-merge validation gate accepted 2026-07-01
0004 Right-size the solo-developer process: ceremony scales with thesis value accepted 2026-07-13
0005 Remove the ai-compliance-auditor agent; Álvaro owns §6 compliance directly accepted 2026-07-13
0006 Remove the enforced §6 data policy; Álvaro owns data handling directly accepted 2026-07-13
0007 PDF report ingestion: pdfplumber for extraction, a text seam to LLM structuring amended by 0047 2026-07-13
0008 Single trusted-user threat model: drop the security-auditor agent and PDF bomb-hardening accepted 2026-07-13
0009 LLM finding extraction: per-candidate Pydantic AI with a schema-validation gate superseded by 0047 2026-07-13
0010 Model-agnostic LLM config: REVALID_LLM_MODEL env var, Ollama via Pydantic AI accepted 2026-07-13
0011 Retest-plan generation: LLM-proposed typed actions, deterministically gated superseded by 0033 2026-07-13
0012 Server-side plan approval gate: versioned plan rows, single execution chokepoint superseded by 0033 2026-07-14
0013 React SPA architecture: PDF-ingest background jobs, /api prefix, FastAPI-served SPA accepted 2026-07-14
0014 Execution sanity checker: independent verifier — fail-closed plan-deviation block + conservative ambiguity downgrade superseded by 0033 2026-07-15
0015 Audit trail: verdicts re-derivable from stored evidence via a shared pure assessment superseded by 0033 2026-07-15
0016 Versioned run export: Pydantic-generated JSON document + published, drift-tested JSON schema accepted 2026-07-15
0017 Evaluation harness: score an FR-12 export against title-keyed ground truth with conservative NFR-01 buckets accepted 2026-07-15
0018 Browser-driven probes via Playwright (optional extra), as a swapped executor under the unchanged FR-08 guard superseded by 0033 2026-07-15
0019 Extensible retest-technique registry: kind-keyed assessors + command rendering, FR-04 kind tagging (scope stays human-validated) superseded by 0033 2026-07-15
0020 Manual report entry: human ingestion (form + JSON upload) bypassing the LLM accepted 2026-07-15
0021 User-configurable model/provider setting: DB-persisted, runtime-switchable, env-seeded accepted 2026-07-15
0022 Asynchronous plan generation: a persisted generating version settled by a background job superseded by 0033 2026-07-16
0023 Plan iteration: operator instructions + regenerate / revise (go back a step) superseded by 0033 2026-07-16
0024 Finding revision & annotation; pipeline stage wizard (supersedes ADR-0023's confirm-on-click) accepted 2026-07-16
0025 Agentic retest console (Slice 0): egress-locked sandbox, deferred-tool gating, transcript audit (supersedes FR-04/05/07-09 over time) accepted 2026-07-16
0026 Operator manual commands (!) via discrete exec — not a shared PTY; agent observes them on its next turn accepted 2026-07-16
0027 Agentic retest guiding plan: a gated set_plan tool (every plan change human-approved, reuses the command gate, budget-exempt) superseded by 0032 2026-07-16
0028 Agentic retest chat steering & Q&A: pure-queue messages delivered as a first-class user turn on the next decision; non-gated respond tool for prose Since: the parallel read-only Q&A stand-in was superseded by 0042 (one agent, one voice); the queue + respond tool remain. accepted 2026-07-16
0029 Agentic retest free-launch mode + budgets: auto-approve commands via the same gate (plan changes stay gated), configurable step budget + free-launch-only wall-clock budget, distinct give-up state Since: the step/wall-clock budgets were removed by 0035, the give-up state by 0034, and the gated set_plan by 0032 — the gate now only ever carries a command. Auto-approval itself stands. accepted 2026-07-17
0030 Agentic verdict integration + human adjudication: polymorphic VerdictRecord (batch/agentic), auto-persist on conclude, accept/override appends a superseding operator record, FR-10 transcript-integrity audit, FR-12 export flatten (schema 1.2) accepted 2026-07-18
0031 Flexible command-output evidence for agentic verdicts: tool-agnostic AgenticEvidence (explanation + real last command output) captured from the transcript on conclude; export schema 1.2 → 1.3 accepted 2026-07-18
0032 The guiding plan becomes a user-owned goal: generic generate_goal (repurposed FR-04) seeds it at start, the user edits/regenerates it live (pure-queue injection), and the agent's set_plan is removed (supersedes 0027) accepted 2026-07-19
0033 Retire the batch execution path (full deletion): delete approval/retest/sanity/browser + batch endpoints/domain/PlanRecord, collapse VerdictRecord/export/audit to agentic-only (schema 1.4), drop FR-14; realizes ADR-0025, supersedes 0011/0012/0014/0015/0018/0019/0022/0023 accepted 2026-07-19
0034 Pause-and-ask retest lifecycle — no give-up: non-terminal needs_guidance keeps the sandbox alive; the agent pauses (never quits) on a budget or an exhausted-options hand-back; operator keeps going or concludes Since: the budget trigger was removed by 0035 and needs_guidance folded into awaiting_operator by 0042; the console's prompt at that pause was removed by 0046. The pause-and-ask substance stands. accepted 2026-07-19
0035 Remove the retest step budget; pause only on agent hand-back. Emit the thinking status for a live indicator; redesign the cockpit (full-width goal, boxed chat, de-jargoned copy). Amends 0034, supersedes FR-17 Slice 9 Since: the thinking status was renamed working by 0042. accepted 2026-07-19
0036 Reports chat assistant (FR-18): a read-only Pydantic AI agent with DB query tools (exact corpus counts, not context-stuffing) + persisted chat threads; new Chat tab + /api/chats surface; reuses the FR-13 backend accepted 2026-07-20
0037 Enrich ingested findings with CVSS + MITRE ATT&CK, inferring (marked inferred) when the report is silent — a bounded exception to "never guess", justified because they are classificatory metadata, not verdict evidence; new CvssCode/MitreMapping, Finding/ExtractedFinding fields, finding_versions columns accepted 2026-07-20
0038 Reports chat streams its reply token-by-token over SSE (POST /api/chats/{id}/messages/stream), which forces an async endpoint — the sync run_stream_sync portal breaks inside StreamingResponse; blocking endpoint kept as fallback. Enhances FR-18 (0036) accepted 2026-07-21
0039 Operator control of in-flight LLM work: AwaitOperator conversational hand-back (new awaiting_operator state), turn restart/unstick (cross-thread cancel + re-run), messages_delivered marker, cancellable extraction (cancelled report status), and delete tears down live sessions/extraction. Enhances FR-17 (0025/0034/0035) + FR-01/FR-03 accepted 2026-07-23
0040 Guided retest is operator-driven: in gated mode the agent does one action per turn then parks in needs_guidance (no auto-chaining, no self-conclude — a verdict is a recommendation); Auto-run/free-launch is unchanged and is "go for it". Enhances FR-17 Since: the guided park was renamed awaiting_operator by 0042, and 0046 removed the prompt it rendered. One-action-then-hand-back and never-self-conclude stand. accepted 2026-07-23
0041 Retest scope drives the sandbox target: parse each target_set endpoint to its host and provision against it — lab mode (unchanged internal network) or online mode (allowlisting egress proxy: HTTP(S) to the scoped host(s) only, no other route out). Enhances FR-17/FR-06 superseded 2026-07-24
0042 One agent, one voice, five states: delete the parallel read-only Q&A; fold needs_guidanceawaiting_operator and collapse to idle/working/awaiting_command/awaiting_operator/stopped; a message at the gate withdraws the pending command and steers (_advance delivers queued messages at the turn boundary); questions trigger a lookup. Guided mode (0040), free-launch (0029), scope egress (0041) unchanged. Amends 0028/0034/0035/0040, extends 0039 accepted 2026-07-24
0043 Reopen a concluded retest session: withdraw the verdict (kept in the transcript as verdict_cancelled, removed from the verdicts projection) and return to idle so the operator can wake it and keep testing. Enhances FR-17 accepted 2026-07-24
0044 Containerised deployment: a two-stage app image (SPA + backend) and a compose stack that includes the pinned lab; the retest sandbox runs as sibling containers over the mounted host Docker socket (rejecting Docker-in-Docker and a socket proxy), which is root-equivalent on the host and accepted only under ADR-0008; LLM stays on the host, DB on a volume, loopback-only ports accepted 2026-07-25
0045 Online-scope egress moves from an L7 Squid proxy to a per-session L3 gateway: a helper container holds an iptables IP allowlist + NET_ADMIN, the sandbox shares its netns with NET_RAW only — so every tool (nmap, sqlmap, raw sockets), not just HTTP, reaches the scoped IP(s) and nothing else, and no command can widen scope. Scope resolved to IPv4 + pinned in /etc/hosts, one DNS resolver allowed; per-session resources reaped by name (report-delete no longer leaks). Supersedes 0041 accepted 2026-07-25
0046 A handed-back console waits: awaiting_operator renders no prompt (the agent's message is the hand-back; status reads "Waiting for you"), Conclude becomes a permanent toolbar control in every live state, and the agent proposes concluding only when it has a determination — the guided one-action report drops its options menu. Also stops reporting a verdict withdrawn by reopen. Amends 0042, fixes a reporting bug in 0043 accepted 2026-07-25
0047 Whole-document LLM ingestion: PyMuPDF4LLM Markdown, one call returning list[ExtractedFinding], regex heading segmentation deleted; context window is the new boundary (small local reports, hosted for full ones); AGPL accepted under ADR-0008. Supersedes 0009, amends 0007 proposed 2026-07-26