LOGIC.md ↔ Archon Integration Evaluation
May 7, 2026 · View on GitHub
Workspace: /Users/rainierpotgieter/development/archon-test
Date: 2026-04-19
Total trials: 60 (3 fixtures × 2 cases × 10 runs)
Trials completed successfully: 60 / 60 (after one rate-limit re-run)
TL;DR Verdict
LOGIC.md is a real capability add for Archon, with caveats. On the data: at the severity-count layer Case B (LOGIC.md spec via MCP) was strictly more consistent than Case A (stock prompt) on every fixture where there was anything to vary on — most strikingly on the SQL-injection auth fixture, where Case B produced identical structured output 10/10 runs vs. Case A's 5/10. Case B also makes one capability the YAML layer cannot express today: an explicit, declarative decision rule ("first-match wins") that maps issue counts to a verdict. We can see that working in the data — Case B correctly split feature-weak-tests between NEEDS_DISCUSSION and REQUEST_CHANGES based on whether the model found 1 or 2 HIGH issues, while Case A flattened both into REQUEST_CHANGES regardless. Cost: Case B is ~2.6× slower per run (~91s vs ~34s) due to MCP roundtrips, and headline "verdict agreement" looks worse for B (87% vs 100%) — but that lower number is rule-driven differentiation, not noise. Recommended: worth flagging to Cole as a complement to YAML for review/validation nodes where reasoning rules matter, not as a replacement for YAML orchestration.
1. What Was Built
Integration architecture
Archon DAG (YAML — orchestration)
┌──────────┐ ┌────────────┐
PR diff ─────▶│ load-diff├──▶│ code-review│ ──▶ structured verdict
└──────────┘ └─────┬──────┘
│
Case B only: │ mcp: .archon/mcp/logic-md.json
▼
┌────────────────────────┐
│ @logic-md/mcp (stdio) │
│ logic_md_compile_ │
│ workflow tool │
└────────────┬───────────┘
│
▼
specs/code-review.logic.md
(declarative reasoning steps)
Concrete artifacts
archon/.archon/workflows/experiment-case-a.yaml— stock prompt-driven review nodearchon/.archon/workflows/experiment-case-b.yaml— same node +mcp: .archon/mcp/logic-md.jsonarchon/.archon/mcp/logic-md.json— MCP server config ({ "logic-md": { "command": "logic-md-mcp" } })archon/specs/code-review.logic.md— the LOGIC.md spec compiled at runtime by the MCP serverfixtures/{auth-sql-injection,feature-weak-tests,clean-refactor}/diff.patch— input diffsharness/run.ts— drives all 60 trials, captures structured verdicts, copies workflow JSONL tracesharness/analyze.ts— producesresults/SUMMARY.{md,json}
How the MCP handoff actually works (verified end-to-end)
- Archon's executor calls
loadMcpConfig(".archon/mcp/logic-md.json", cwd)and passes the resultingmcpServersJSON straight to@anthropic-ai/claude-agent-sdk'squery(). - Claude Agent SDK spawns
logic-md-mcpas a stdio subprocess and auto-allowsmcp__logic-md__*tools. - The Case B prompt instructs Claude to call
mcp__logic-md__logic_md_compile_workflowwith{file_path: "specs/code-review.logic.md"}. - The MCP server parses the spec, validates it, runs the DAG resolver, and returns a structured
executionPlan: [{stepName, dagLevel, systemPromptSegment, outputSchema, qualityGates}, …]. - Claude follows each step's
systemPromptSegmentindagLevelorder and returns the final structured verdict via Archon'soutput_format:schema.
Verified by inspecting workflow event JSONL at ~/.archon/workspaces/coleam00/Archon/logs/<run-id>.jsonl:
- Case A traces show exactly 1 tool call:
StructuredOutput(the JSON capture). - Case B traces show:
ToolSearch→mcp__logic-md__logic_md_compile_workflow→ assistant reasoning →StructuredOutput.
Zero patches to Archon
Archon already has first-class MCP support (packages/providers/src/claude/provider.ts's loadMcpConfig + applyNodeConfig). The integration is two YAML files and one JSON config; no TypeScript changes were made to Archon itself. The YAML reasoning prompt in Case B explicitly tells the model to call the MCP tool; if Archon's docs surfaced this pattern as a first-class workflow, no instruction would be needed.
2. Fixtures Used
| Fixture | Concern Exercised | Expected verdict (informal) |
|---|---|---|
auth-sql-injection | Three security regressions in one diff: SQL string interpolation, hardcoded JWT secret, plaintext password compare | REQUEST_CHANGES (CRITICAL) |
feature-weak-tests | A new Stripe-charge feature with one happy-path test, no negative paths, no edge cases | REQUEST_CHANGES or NEEDS_DISCUSSION (borderline) |
clean-refactor | Extract formatCurrency utility from two duplicated call sites with full test coverage | APPROVE |
The fixtures live under fixtures/<name>/diff.patch and are version-controlled with the workspace.
3. Quantitative Results
Determinism — verdict-label and structural-hash agreement
Hash is sha256({verdict, critical_count, high_count}) — i.e., we strip the prose summary and other noise, and ask: across 10 identical runs, how often does the model land on the same (verdict + critical + high) tuple?
| Fixture | Case | Runs | Modal verdict | Verdict agreement | Modal hash count | Hash agreement | Unique full-output hashes (verdict + counts + summary text) |
|---|---|---|---|---|---|---|---|
| auth-sql-injection | A | 10 | REQUEST_CHANGES | 100% | 5 / 10 | 50% | 10 |
| auth-sql-injection | B | 10 | REQUEST_CHANGES | 100% | 10 / 10 | 100% | 10 |
| feature-weak-tests | A | 10 | REQUEST_CHANGES | 100% | 6 / 10 | 60% | 10 |
| feature-weak-tests | B | 10 | NEEDS_DISCUSSION | 60% | 6 / 10 | 60% | 10 |
| clean-refactor | A | 10 | APPROVE | 100% | 10 / 10 | 100% | 10 |
| clean-refactor | B | 10 | APPROVE | 100% | 10 / 10 | 100% | 10 |
Aggregate (averaged over fixtures)
| Metric | Case A (stock) | Case B (LOGIC.md) | Δ |
|---|---|---|---|
| Verdict agreement | 100% | 87% | −13% |
| Hash agreement (verdict + crit + high) | 70% | 87% | +17% |
| Avg run duration | 34.5 s | 91.0 s | +56.5 s (+164%) |
Severity-count means (per fixture × case)
| Fixture | Case | avg crit | avg high | avg med | avg low |
|---|---|---|---|---|---|
| auth-sql-injection | A | 3.00 | 1.70 | 1.40 | 0.60 |
| auth-sql-injection | B | 2.00 | 2.00 | 1.10 | 1.50 |
| feature-weak-tests | A | 0.00 | 3.20 | 2.70 | 1.50 |
| feature-weak-tests | B | 0.00 | 1.50 | 3.50 | 0.80 |
| clean-refactor | A | 0.00 | 0.00 | 0.10 | 2.20 |
| clean-refactor | B | 0.00 | 0.00 | 0.20 | 1.90 |
Reading the results honestly
- Case B beats Case A handily on the auth fixture (100% vs 50% hash) — the headline win.
- Case B beats Case A also on clean-refactor at the maximum: both at 100%, and importantly, Case B does not introduce noise on inputs where the answer is unambiguous.
- Case B "loses" on feature-weak-tests verdict agreement (60% vs 100%) — but this is an honest disagreement, not noise. The LOGIC.md spec's
synthesize_verdictstep has the explicit rule:exactly 1 HIGH issue → NEEDS_DISCUSSION. Across 10 runs the model found 1 HIGH in 6 runs, 2 HIGHs in 3 runs, and 3 HIGHs in 1 run. The structured rule then deterministically mapped those counts to NEEDS_DISCUSSION (6) / REQUEST_CHANGES (4). Case A doesn't have such a rule — its prose prompt collapses 1-HIGH and 2-HIGH cases into a single REQUEST_CHANGES verdict, so it looks "more consistent" but only because it has lower verdict resolution. - Case B is 2.6× slower. The MCP subprocess spawn + tool call + tool result round-trip costs ~50–60 s per run. This is currently a real cost.
So the right two-line summary of the determinism numbers:
- Within a fixed verdict bucket, Case B is more structurally consistent than Case A (the auth fixture is a near-perfect demonstration).
- Case B's verdict labels reflect the model's underlying issue-count variance more faithfully (which Case A masks).
4. Qualitative Findings
4.1 Auditability — side-by-side trace from auth-sql-injection / run-01
Case A workflow event log (relevant lines, all 5 events):
[01:07:44.022Z] workflow_start
[01:07:44.032Z] node_start (load-diff)
[01:07:44.051Z] node_complete (load-diff)
[01:07:44.054Z] node_start (code-review)
[01:08:06.480Z] tool StructuredOutput ← only tool call, the JSON capture
[01:08:09.249Z] node_complete (code-review)
[01:08:09.250Z] workflow_complete
There is one tool event in the entire trace, and it's just the JSON output bookkeeping. The reasoning the model used to get from "diff" to "verdict" is opaque — it happened in a single inference call that left no breadcrumb in the trace.
Case B workflow event log:
[01:12:30.947Z] workflow_start
[01:12:30.953Z] node_start (load-diff)
[01:12:30.965Z] node_complete (load-diff)
[01:12:30.968Z] node_start (code-review)
[01:12:40.840Z] tool ToolSearch ← model selecting MCP tool
[01:12:43.083Z] tool mcp__logic-md__logic_md_compile_workflow
input: { file_path: "specs/code-review.logic.md" }
[01:13:39.648Z] assistant (reasoning over the compiled executionPlan)
[01:13:43.265Z] tool StructuredOutput
[01:13:49.627Z] assistant
[01:13:49.691Z] node_complete (code-review)
[01:13:49.692Z] workflow_complete
The trace shows what reasoning structure the model was handed: compile_workflow returns an executionPlan of named steps (scan, assess_security, check_test_coverage, evaluate_quality, synthesize_verdict), each with its own systemPromptSegment and outputSchema. An auditor can:
- Open
specs/code-review.logic.mdand see the rules the agent was told to apply. - Open the trace and confirm that the model received those rules (the MCP tool call + result are recorded).
- Compare summary text against the spec's decision rules to verify the model applied them.
For Case A, none of these are possible. The same auditor sees only "Claude reasoned, Claude returned JSON." If that JSON looks wrong, the only artifact to reach for is the prompt — but the prompt is prose, and there's no checkpoint between "received prompt" and "produced verdict."
Concrete consequence: the Case B trace is verifiable against an external spec. The Case A trace requires re-reading the prompt to imagine what the model "should have" done.
4.2 Modifiability — diff to add one new rule
Rule change applied to both cases: "Any HIGH-severity security issue should block the PR (REQUEST_CHANGES), even on its own. A lone HIGH non-security issue still goes to NEEDS_DISCUSSION."
Case A (prose prompt edit)
## How to decide verdict
- APPROVE: no issues, or only LOW issues
- - REQUEST_CHANGES: any CRITICAL, or multiple HIGH issues
- - NEEDS_DISCUSSION: borderline cases (one HIGH, or many MEDIUM)
+ - REQUEST_CHANGES: any CRITICAL, OR any HIGH-severity *security* issue
+ (security takes priority — a single HIGH security finding blocks merge),
+ OR multiple HIGH issues of any kind
+ - NEEDS_DISCUSSION: borderline cases (one HIGH non-security issue, or many MEDIUM)
+ When in doubt about whether an issue is a "security" issue, treat
+ injection, secrets, auth, and credential-handling concerns as security.
+
Return a structured JSON object with the verdict, severity counts, and a one-paragraph summary.
Lines added: 6. Lines removed: 2. Files: 1 (experiment-case-a.yaml).
The change is a prose edit. It cannot be syntactically validated. To gain any confidence the rule is being followed, you re-run the experiment and read 10 summaries. The clarification clause ("when in doubt…") was needed because prose is ambiguous — without it, the model has to infer what "security" means, and infers inconsistently.
Case B (LOGIC.md spec edit)
1. ANY issue with severity CRITICAL → verdict = REQUEST_CHANGES
- 2. >= 2 issues with severity HIGH → verdict = REQUEST_CHANGES
- 3. exactly 1 HIGH issue → verdict = NEEDS_DISCUSSION
- 4. only MEDIUM/LOW issues → verdict = NEEDS_DISCUSSION if MEDIUM count >= 2, else APPROVE
- 5. no issues → verdict = APPROVE
+ 2. ANY issue with severity HIGH that came from `assess_security` → verdict = REQUEST_CHANGES
+ 3. >= 2 issues with severity HIGH (any source) → verdict = REQUEST_CHANGES
+ 4. exactly 1 HIGH issue (non-security) → verdict = NEEDS_DISCUSSION
+ 5. only MEDIUM/LOW issues → verdict = NEEDS_DISCUSSION if MEDIUM count >= 2, else APPROVE
+ 6. no issues → verdict = APPROVE
Lines added: 5. Lines removed: 4. Files: 1 (specs/code-review.logic.md).
The change inserts a new explicit rule at a known position ("first match wins" guarantees it overrides #3). The assess_security reference is typed — that step name has to exist in the spec, and logic_md_validate can mechanically check it (@logic-md/cli validate). The disambiguation prose Case A needs ("when in doubt…") is unnecessary because the rule names its source step.
Modifiability comparison
| Property | Case A (prompt) | Case B (LOGIC.md spec) |
|---|---|---|
| Lines changed | 8 (6 added, 2 removed) | 9 (5 added, 4 removed) |
| Files touched | 1 | 1 |
| Static-validatable? | No | Yes (@logic-md/cli validate can syntax-check; assess_security step name is referenced from the rule and validated against the spec) |
| Disambiguation prose required? | Yes ("when in doubt…") | No (the rule cites its source step by name) |
| Diff scrutability for non-author reviewer | Medium — reviewer must re-read prose and reason about prompt contract | High — reviewer reads structured rules in declared order |
The diffs are roughly the same size. The kinds of change are very different: Case A edits are prose engineering with no contract; Case B edits are structured rule edits with a checkable contract.
4.3 Capabilities Case B exposes that Case A genuinely cannot
This is the falsifiable question. Three capabilities surfaced in this experiment that are real (not "you could in principle do this in Archon's prose prompt"):
-
Explicit decision rules with deterministic precedence.
synthesize_verdictsays "apply these rules in order, first match wins." When the model finds 1 HIGH, the rule fires NEEDS_DISCUSSION; 2 HIGHs fire REQUEST_CHANGES. The data shows this actually working: feature-weak-tests Case B's verdict tracks the model's HIGH count exactly. Case A's prose has an analogous-looking rule, but since it's interpreted by the model rather than applied as a rule, all 10 feature-weak-tests Case A runs collapsed to REQUEST_CHANGES regardless of HIGH count. The structured rule is a decision contract; the prose is a suggestion. -
Cross-step dependencies (
needs:) that name where information comes from. The Case B modifiability change keys on "HIGH issue fromassess_security." That cross-reference is semantic, not textual — the model sees a structuredpreviousOutputsmap keyed by step name. To express the same in Case A, you have to either give the model an instruction to "tag" issues with their source category in the prose, or rely on the model to infer. Both are fragile in ways the structured cross-reference isn't. -
A trace whose tool-call structure mirrors the reasoning structure. Case B's
mcp__logic-md__logic_md_compile_workflowcall is recorded with input + result in the workflow event JSONL. An audit script can extract the executionPlan that was handed to the model and diff it against the spec on disk to confirm the spec wasn't tampered with. There's no analogous artifact in Case A — the prompt becomes part of the LLM call and is gone.
What Case A wins on
Case A is 2.6× faster and verdict-stable by virtue of being verdict-coarse. For workflows where:
- The verdict label is the only thing that downstream nodes consume, and
- The reasoning is unambiguous (e.g., "is this a typo? yes/no"),
- Then the LOGIC.md indirection is overhead.
Most review/validation work in a coding agent isn't that simple, so Case A's win is narrow.
5. Honest Limitations
-
Determinism is structural, not arithmetic. Archon does not pass a
temperatureto the Claude Agent SDK; both cases use whatever sampling defaults the SDK applies. There is no way to make the comparison bit-for-bit. We are measuring how often the model lands on the same labeled outcome, not whether it computes the same logits. Confirmed by readingpackages/providers/src/claude/provider.ts— notemperature/top_pparameter is forwarded. -
Sample size is small. 10 runs per (fixture, case) is enough to see headline differences but not enough for tight confidence intervals. The 50% hash agreement on auth/case-A could plausibly be 40–60%; the 87% aggregate could be 80–94%. The directional findings are robust; the precise percentages are not.
-
Three fixtures isn't a benchmark. The fixtures were picked to span the verdict surface (CRITICAL, ambiguous, clean). They are not a curated benchmark of real PR diffs. Generalizing to "Case B is always more consistent on review tasks" requires more fixtures and a harder confound: input difficulty.
-
Both cases call the same model with the same input — Case B's reasoning still happens inside Claude. LOGIC.md compiles a more structured prompt; it does not perform any of the reasoning itself. The "determinism" we observe is therefore the prompt structure's effect on Claude's output variance, not deterministic computation.
-
The 60s MCP cold-start risk documented in the research was avoided by
npm install -g @logic-md/mcp@1.4.0. In a deployment scenario this would need handling (a vendored binary or a long-running MCP server). -
One rate-limit hit during the experiment. 29 of 60 trials failed initially when Claude's 5-hour rate limit was reached. After the limit reset, the same 29 trials were re-run successfully. This is unrelated to the integration — it's a cap on this account — but it does mean the experiment is sensitive to API-side caps in a way Cole would need to consider for a CI use case.
-
The Case B spec is small (~150 lines). Larger LOGIC.md specs may have meaningful token cost (
compile_workflowreturns atokenEstimateper step). Not measured here. -
Archon's existing validation/retry primitives could narrow the gap. Archon has
output_format:(structured Zod schema) andtrigger_rule:join semantics, and could in principle add rule-application primitives at the DAG level instead of at the per-node-reasoning level. We did not benchmark Case B against a hypothetical "Case A++" with hand-rolled validation downstream of the review node.
6. Recommendation
(a) Worth flagging to Cole — as a complement, not a replacement.
The data supports a focused pitch:
- Archon's YAML layer is great at orchestration. Nothing in this experiment suggests replacing it.
- For nodes whose value comes from applied reasoning rules (review, validation, classification gates with multiple criteria, anything where "did we follow the policy" matters), LOGIC.md gives a real, measurable improvement to structural consistency and a meaningful improvement to auditability and modifiability. The integration cost is one JSON config and one new node-type pattern.
- Concretely, the integration vector to propose is a first-class
logic_spec:field on a node (sugar over today'smcp:+prompt:pattern), so users don't have to write the "call this MCP tool, follow the executionPlan" prompt themselves. Archon already builds the MCP plumbing; LOGIC.md already produces the compiled reasoning. The gap is documentation and ergonomics, not a code change to either system. - The current cost (~2.6× slower per node) is real but localized. It's worth it for nodes whose output is consumed downstream by humans for decisions, and not worth it for fast classification gates.
What would change the recommendation:
- If Archon's roadmap already includes structured per-step output schemas with rule application (i.e., bringing decision-rule semantics into YAML directly), this becomes redundant overlap. Worth checking with Cole before the pitch.
- If Cole's users overwhelmingly want a single end-to-end agent and aren't asking for auditability of the inside of a node, this becomes a nice-to-have without product pull.
The pitch is honest if framed as: "We measured this on three fixtures, 60 runs. Here's where it wins, here's what it costs, here's the integration shape that requires zero changes to Archon's executor."
7. Decisions Made (no clarifications were sought from the user)
- Used
prompt:+mcp:rather than forking acommand:node. Archon'scommand: archon-code-review-agentloads a markdown prompt that is hard-coupled to GitHub-PR semantics (gh pr diff,$ARTIFACTS_DIR/review/scope.md). For a fair comparison with a local-fixture experiment, both Case A and Case B use aprompt:node with the same input shape. Stock Archon production review would still go through thecommand:path; the experiment isolates the reasoning step, not the surrounding pipeline. - Hashing strategy strips
summarytext and only hashes{verdict, critical, high}. Includingsummarymakes every run unique on prose noise; excluding it isolates the structured outcome. (full_hashover the full structured object is also computed and reported separately.) - Used global install of
@logic-md/mcp(logic-md-mcpbinary) to avoid the 60s npx cold-start documented in the research as Risk R7. - Did not use Archon's
--worktreeisolation. Worktrees per run would 2× the disk footprint and complicate path resolution for the spec;--no-worktreeis documented as a valid CLI flag and works. - Used
model: sonnetexplicitly on both nodes to avoid the Haiku-MCP silent-failure risk documented as R6. - Re-ran failed cells after the rate-limit reset by adding skip-if-exists logic to the harness, then invoking again. Re-runs were not mixed with original runs at the analysis layer.
8. Artifacts
archon/— full Archon clone (dev branch, version 0.3.6, bun 1.3.12)logic-md/— full LOGIC.md clone (main branch,@logic-md/mcp1.4.0)archon/.archon/workflows/experiment-case-a.yaml— Case A workflowarchon/.archon/workflows/experiment-case-b.yaml— Case B workflowarchon/.archon/mcp/logic-md.json— MCP server configarchon/specs/code-review.logic.md— LOGIC.md reasoning specfixtures/auth-sql-injection/diff.patch— Fixture 1fixtures/feature-weak-tests/diff.patch— Fixture 2fixtures/clean-refactor/diff.patch— Fixture 3harness/run.ts— 60-run driver (withRUNS/FIXTURES/CASESenv overrides + skip-if-exists)harness/analyze.ts— analyzer producingresults/SUMMARY.{md,json}results/<fixture>/case-{A,B}/run-NN.json— per-run structured recordsresults/<fixture>/case-{A,B}/run-NN-trace.jsonl— per-run workflow event tracesresults/SUMMARY.json,results/SUMMARY.md— aggregated metricsresults/harness.log,results/harness-rerun.log— full harness console outputmodifiability/case-{a-original,a-modified}.yaml— Case A modifiability before/aftermodifiability/case-{b-original,b-modified}.logic.md— Case B modifiability before/aftermodifiability/case-{a,b}.diff— unified diffs for the rule change.planning/REQUIREMENTS.md,.planning/STATE.md,.planning/phases/001-logic-md-archon-integration/— project tracking.planning/phases/001-logic-md-archon-integration/001-RESEARCH.md— pre-execution research used to plan the integration
Reproducing the experiment
# From workspace root, with bun on PATH:
PATH="$HOME/.bun/bin:$PATH" bun run harness/run.ts # 60 trials (skips already-completed)
PATH="$HOME/.bun/bin:$PATH" bun run harness/analyze.ts # regenerate SUMMARY.{md,json}
# Single-cell smoke:
RUNS=1 FIXTURES=clean-refactor CASES=A bun run harness/run.ts