Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ADR-024: SYN-185: batch approval over syn pending, not a syn agent runner

Date: 2026-06-08 Status: Accepted (owner, 2026-06-08) Author: Rômulo Febasi


Context

SYN-185 was filed as “syn agent: autonomous loop with batch approval”, with the acceptance line: “BR-12.3 is a hard criterion: nothing applies without human sign-off; batch approval means the human approves N proposals at once, never the agent auto-applying.”

Two things changed between filing and implementation, making the literal “autonomous loop” framing the wrong thing to build:

  1. The autonomous loop already exists, and it is the external agent. Synapse is local-first: it ships no LLM, holds no API keys, and (since SYN-219) no longer uses MCP server-initiated sampling. A loop in which “the AI works a task across multiple steps and queues writes” is exactly what an MCP client (Claude Code, etc.) does today over syn mcp. A syn agent command that ran a model itself would contradict the product’s foundations and duplicate the MCP session. The genuinely missing piece is the human side: sanctioning many queued proposals in one action instead of one syn pending accept <id> at a time.

  2. ADR-022 superseded the “never auto-apply” line. When SYN-185 was filed, BR-12.3 was an absolute gate. ADR-022 (accepted 2026-06-07, implemented in SYN-218) evolved it into a confidence-tiered policy: the opt-in safe tier auto-applies low-risk, reversible writes (a link_entity between two existing non-self entities), stamped auto-accepted. So “never the agent auto-applying” is no longer the rule, the rule is “the human sanctions everything the policy does not auto-apply.”

Decision

Deliver SYN-185 as batch approval on the existing syn pending queue, not as a new syn agent command and not as an LLM orchestrator:

  • syn pending accept <id>... accepts one or more ids; syn pending accept --all sanctions every pending proposal in one action.
  • syn pending reject <id>... | --all [--prune] mirrors it.
  • Each proposal applies independently and atomically (SYN-216): the batch is best-effort: a failure on one is reported, it never half-applies and never blocks the others. Cross-proposal all-or-nothing remains the job of a single propose_batch row (SYN-202), which is the unit when atomicity across several writes is wanted.
  • There is no syn agent command. The autonomous loop is the external MCP agent, unchanged.

Consequences

  • Scope change (recorded): SYN-185’s syn agent noun and any “Synapse-runs-the-agent” reading are dropped. The issue’s deliverable, “human approves N at once”: is met by syn pending batch flags. The milestone item is satisfied; the name is not.
  • Business-rule implications (recorded):
    • BR-12.3 is intact and unchanged. A human still sanctions every non-auto-applied write; --all is one human action over the queue, not a bypass. The provenance + audit trail is unchanged.
    • The issue’s original “never the agent auto-applying” clause is superseded by ADR-022 / SYN-218: the safe tier (opt-in, default manual) auto-applies the low-risk, reversible class. Batch approval covers exactly what the policy leaves queued.
  • Improves: the high-friction part of an agent-driven session (reviewing many queued writes one id at a time) collapses to one command, without weakening the gate.
  • Open: if a richer “review the agent’s run” UX (showing auto-applied + pending together, interactive select) is ever wanted, it layers on top of these flags: revisit on demand.

Alternatives considered

  • A syn agent run orchestrator that shells out to an external agent CLI and drives a task loop. Rejected: depends on an external LLM/CLI, breaks local-first / no-API-key, and the loop already runs via syn mcp.
  • A dedicated syn agent review command with run transparency (auto-applied + pending, interactive). Reasonable, but more product than the deliverable needs; deferred (owner chose the minimal form).
  • Cross-proposal all-or-nothing for accept --all. Rejected: one bad proposal would block every good one in a review-many flow; per-proposal atomicity + a clear failure report is the better UX. propose_batch already provides all-or-nothing when that is the intent.