Architecture Decision Records¶
Snodo design decisions extracted from the development audit log and .snodo/bootstrap/decisions.md. Each ADR follows the Nygard format: Title, Status, Context, Decision, Consequences, Alternatives. One page or less each.
| ADR | Title | Date | Audit-log anchor |
|---|---|---|---|
| 001 | PyJWT over custom HMAC signing | 2025-05-27 | Task 7.7 (tokens.py) |
| 002 | Warn withholds approval in policy thresholds | 2025-06-01 | Policy semantic fix (policy.py) |
| 003 | ESCALATE as halt → resolve → resume | 2025-05-27 | Task 7.10 (loop.py) |
| 004 | Constraint predicate framework | 2025-05-27 | Task 7.8 (predicates/) |
| 005 | Protocol-adherence validator from mode profiles | 2025-05-27 | Task 7.11 |
| 006 | Severity cap for validators under evaluation | 2025-05-31 | Task 7.17 (models.py) |
| 007 | Coder adapter + code-host provider pattern | 2025-05-25 | Tasks 4.10, 4.6 |
| 008 | File-backed session checkpoint over in-memory state | 2025-05-27 | Task 7.3 (session.py) |
| 009 | FastMCP over custom MCP transport | 2025-05-25 | Task 4.5 (transport.py) |
| 010 | GitPython over subprocess for git operations | 2025-05-25 | Task 4.7 (git.py) |
| 011 | Typer over argparse for CLI | 2025-05-25 | Task 4.8 (main.py) |
| 013 | Kleene-closure auto-fix recovery loop | 2026-06-28 | Recovery driver (closure.py) |
| 014 | Trusted-repository threat model and init consent gate |
2026-08-21 | Threat-model decision (init_cmd.py) |
| 015 | Real validation on the MCP path + four-outcome validate_task |
2026-08-21 | handle_validate_task (server.py) |
| 016 | Shared SQLite store for validation-token single-use | 2026-08-22 | TokenStore (tokens.py) |
| 017 | WF1 relaxed to exclusivity on approval-conferring tools | 2026-08-23 | check_wf1 (verifier.py) |
| 018 | Auto-merge task branches on successful completion | 2026-08-24 | _merge_on_success (run_cmd.py) |
| 019 | Phase-aware validator prompts + read tools for repository-content validators | 2026-08-24 | _phase_frame (llm_validator.py) |
| 020 | Wave classification reads ClassifierConfig; classifier model resolved once |
2026-08-24 | _migrate_wave_classifier_keys (config.py) |
| 021 | Recovery builds from the original task, not the previous attempt | 2026-08-24 | _spawn_recovery_subtask (loop.py) |
| 022 | A versioned machine interface (--json) for integrations |
2026-08-24 | json_output.py + validate_cmd.py |
| 023 | Spec-authoring receives only spec-quality critique | 2026-08-24 | judges_spec (models.py) + _spec_authoring_reentry |
| 024 | Environment preparation before task execution | 2026-08-24 | environment.py + governance.py |
| 025 | Unborn-HEAD worktree creation fails loud, never degrades to no isolation | 2026-08-24 | create_worktree + setup_for_task (worktree.py) |
| 026 | Protocol and governance state protected from agent tool-surface mutation | 2026-08-24 | workspace.py + git.py + coders/ |
| 027 | In-place coder .snodo/ mutations are detected and halt as a blocker | 2026-08-25 | coders/base.py + engine/loop.py |
| 028 | Post-execute acceptance validator judges artifacts against the task's acceptance criteria | 2026-08-25 | validators/acceptance.py + validators/runner.py |
| 029 | Per-mode max_recovery_depth override | 2026-08-25 | resolve_mode_setting (models.py) |
| 030 | In-place coder adapters own the commit, so the review channel is the artifact channel | 2026-08-26 | coders/base.py + coders/opencode_adapter.py |
| 031 | First-class verification events in audit trail and blocking unverified merges | 2026-08-26 | quality.py + run_cmd.py |
| 032 | Patch coverage measurement over modified lines | 2026-08-26 | patch_coverage.py + ci.yml |
| 033 | Tool loop repeat read memory and result preservation | 2026-08-26 | litellm.py + llm_validator.py |
| 034 | The opencode coder path is experimental, not supported | 2026-08-26 | init_cmd.py + docs/protocol.md |
| 035 | Declared coder-adapter capability interface; "coder produced nothing" is always a fault | 2026-08-26 | core/interfaces.py + engine/nodes/executor.py |
| 036 | Operator human review tracking via audit log events | 2026-08-26 | task_cmd.py + audit.py |
| 037 | CHANGELOG.md merges with merge=union, not fragment files |
2026-08-27 | .gitattributes + test_changelog_union_merge.py |
| 038 | The Orchestrator Role & Audit Trail Contract | 2026-08-27 | docs/decisions/038-orchestrator-role-and-audit-contract.md |
| 039 | Validation is independent of the coder; engine builds its LLM client from configuration | 2026-08-30 | engine/loop.py (GraphBuilder.__init__) |