What Is Marcus, Briefly
Marcus is a multi-agent task coordination system. It breaks a software project into subtasks, places them on a shared kanban board, and dispatches independent AI agents to claim and complete those tasks. Agents communicate only through the board — never directly with each other.
Problem (User-Facing)
Right now, when an agent on a Marcus project goes down a wrong path — tries the wrong file, pursues the wrong fix, picks the wrong library — that failure stays private. The next agent assigned to a related task starts from scratch and may spend its entire budget rediscovering the same dead end.
This is a real cost problem. Consider a project with 10 agents working in parallel. If three of them each spend 30 minutes and 5,000 tokens pursuing a bad approach the first agent already ruled out, the project wastes 90 minutes and ~15,000 tokens (at $3.75/M tokens ≈ $0.06) on repeated exploration that could have been avoided with one shared note.
More subtly: when an agent discovers a hard constraint — "approach X is globally invalid because of Y" — that constraint currently lives only on that agent's task card. A later agent can unknowingly override or soften it, causing downstream failures that are hard to trace.
Motivation: What Stanford's DeLM Research Shows
A June 2026 Stanford paper, Decentralized Language Models (DeLM, arXiv:2606.10662), tested a multi-agent system built on exactly the design Marcus uses: a shared board, a pull-based task queue, and no central orchestrator. Their headline finding:
Agents that share compact, verified intermediate findings — including failures and constraints — solve tasks 10.5 percentage points more accurately at ~50% lower cost than agents working in independent parallel attempts.
The cost saving comes from three concrete mechanisms:
- Failure sharing. When one agent writes "I tried approach X, it failed because Y," later agents skip that dead end entirely.
- Constraint binding. When a verified constraint lives in shared state, no later agent can accidentally soften or override it on its way through a central controller.
- Compact sharing. Discoveries are shared as short, evidence-backed summaries — not full conversation logs — so agents absorb useful progress cheaply.
Marcus already has log_decision (src/marcus_mcp/tools/context.py:22) and log_artifact (src/marcus_mcp/tools/attachment.py:32) MCP tools that agents call during task execution. These cover what to build and design artifacts. What is missing is the complementary channel for what not to do, a verification gate to ensure what gets shared is trustworthy, and a compact summary layer so the shared context stays readable as projects grow.
MCP tool: a function Marcus exposes to agents via the Model Context Protocol — agents call these like API endpoints during their work.
Proposed Features
Four improvements to Marcus's shared-context layer, ranked by expected impact.
Feature 1 — log_failure MCP Tool ★ Highest Priority
What. A new MCP tool agents call when they discover an approach does not work. The failure is written to shared context and made visible to all agents on the same project.
Why. This is the single mechanism responsible for most of DeLM's cost savings. Failed paths are currently private. log_failure turns every dead end into shared knowledge.
Proposed interface:
async def log_failure(
agent_id: str,
task_id: str,
what_was_tried: str, # "Modified AbstractPythonCodePrinter to add trailing comma"
why_it_failed: str, # "Change did not affect lambdify — bypassed by _recursive_to_string"
constraint: str, # "Do not modify the printer layer; fix is in lambdify.py:964"
state: Any
) -> Dict[str, Any]
Where it goes. src/marcus_mcp/tools/context.py — alongside the existing log_decision tool at line 22.
What it writes.
- A
Failure entry to state.context.failures (in-memory, analogous to state.context.decisions)
- A
❌ DEAD END comment posted to the kanban card for task_id
- A persisted record to
data/project_history/{project_id}/failures.json
How later agents see it. get_task_context should include failures from upstream tasks and the foundation task in its response, the same way it already includes decisions and artifacts.
Foundation task: a special Marcus task that runs before agents spawn; its decisions, artifacts, and now failures are shared project-wide.
Concrete example (adapted from a DeLM paper trace):
An agent is working on a SymPy bug. It tries fixing the Python printer layer and calls:
log_failure(
what_was_tried="Modified AbstractPythonCodePrinter._print_tuple to add trailing comma",
why_it_failed="Change has no effect on lambdify output — lambdify.py:964 uses _recursive_to_string which bypasses the printer",
constraint="Do not modify the printer layer; the fix belongs in sympy/utilities/lambdify.py:964"
)
The next agent, assigned a related task, reads this failure via get_task_context. It skips the printer entirely and goes straight to lambdify.py:964. One full round of redundant exploration is eliminated.
Feature 2 — Admission-Time Verification Gate ★ High Priority
What. Before a log_decision or log_failure entry is admitted to shared context, a lightweight LLM check verifies the claim is grounded in the agent's actual task output. Unsupported or hallucinated claims are rejected, and the agent is asked to revise before the entry is broadcast.
Why. The shared context is the communication substrate for all agents. A false claim admitted to shared context propagates to every downstream agent as if it were fact. DeLM's ablation shows that removing verification drops accuracy by 4.9 percentage points (60.1% → 55.2%), even when everything else is the same.
Concrete failure mode the gate prevents (from the DeLM paper):
An agent writes:
log_decision("Pennzoil punitive damages were reduced from USD 3B to USD 1B — see source S5")
But source S5 only says "conditional affirmance with remittitur" — no dollar amounts appear anywhere. Without a gate, this hallucinated number enters shared context as grounded fact. Every subsequent agent treats it as true.
With the gate, a verifier rejects it:
"WRONG: the dollar amounts are not supported by S5. Regenerate the decision without unsupported specifics."
Where it goes. src/marcus_mcp/tools/context.py — add a _verify_before_admit(entry, supporting_context) step inside both log_decision and log_failure before the entry is written to state.context.
Implementation note. The verifier can be a cheap, fast model (e.g., claude-haiku-4-5-20251001). DeLM's ablation found that summarizer/verifier model quality has almost no effect on final accuracy — a cheap model matches expensive ones on this task (§4.3.1 of the paper).
Feature 3 — Compact Gist Layer for Artifact Context ★ Medium Priority
What. Currently, when an agent calls get_task_context, it receives full artifact documents — complete spec files, full design docs. DeLM shows that giving agents a compact ~100-token summary (a "gist") by default, with the option to request the full document on demand, is both cheaper and more accurate than delivering full content upfront.
Gist: a compact (~100 token) plain-English summary of what an artifact contains and why it matters, generated at write time by log_artifact.
Why. A 10-task project might accumulate 5 design artifacts, each 2 KB. Every get_task_context call delivers ~10 KB of artifact content regardless of how relevant it is to the requesting agent's task. This inflates prompt sizes, increases cost, and can push task-specific instructions out of the agent's context window. DeLM's gist length ablation (§4.3.1) found that 100 tokens per artifact is the sweet spot — accuracy plateaus beyond that.
Proposed change to get_task_context response:
{
"dependency_artifacts": [
{
"gist": "REST API spec for user auth: POST /login, POST /refresh, DELETE /logout. JWT-based, rate-limited at 100 req/min. See full spec for error codes.",
"filename": "api_spec.json",
"location": "docs/api/api_spec.json",
"artifact_type": "api",
"full_content_available": true
}
]
}
A new get_artifact_content(filename) tool lets an agent retrieve the full document when it needs precise details.
Where it goes.
src/marcus_mcp/tools/context.py — modify get_task_context to return gists instead of full content by default
src/marcus_mcp/tools/attachment.py — generate and store a gist when log_artifact is called (line 32)
- New tool in
src/marcus_mcp/tools/attachment.py → get_artifact_content(filename)
Feature 4 — Selective Unfolding for Decisions and Failures ★ Lower Priority
What. Extend the gist layer to decisions and failures, not just artifacts. Agents read compact summaries of all upstream decisions and failures by default. They call unfold_context(entry_id) to retrieve the full reasoning behind a specific decision or the full trace behind a specific failure.
Why. On longer projects, get_task_context can return many kilobytes of decision and failure text. The unfolding mechanism gives agents a lightweight index of what happened upstream, with opt-in drill-down only for entries that are actually relevant to their current subtask.
Where it goes. src/core/context.py (the Context class managing state.context.decisions and the new state.context.failures) and src/marcus_mcp/tools/context.py.
Implementation Order
log_failure tool — highest impact, most self-contained, ships independently
- Verification gate — adds trust to both
log_failure and log_decision; ships independently
- Gist layer for artifacts — requires coordinated changes to
get_task_context and log_artifact
- Selective unfolding — builds on Feature 3; lowest urgency
Where to Look in the Code First
| File |
Purpose |
src/marcus_mcp/tools/context.py |
log_decision (line 22), get_task_context — primary file for Features 1, 2, 4 |
src/marcus_mcp/tools/attachment.py |
log_artifact (line 32) — primary file for Feature 3 |
src/core/context.py |
Context class; in-memory decision storage; cross-task propagation logic (line 487 for dependency filtering) |
src/core/project_history.py |
Persistent JSON/SQLite storage for decisions and artifacts; failures.json would be added here for Feature 1 |
tests/unit/mcp/ |
Unit tests for MCP tools — new tests go here |
tests/integration/ |
Integration tests for cross-task context propagation |
Glossary
| Term |
Meaning |
| Agent |
An AI instance that claims and completes one task from the kanban board |
| Kanban board |
The shared task board that is the only communication channel between agents; Marcus manages it |
| MCP tool |
A function Marcus exposes to agents via the Model Context Protocol; agents call these like API endpoints |
| Blackboard architecture |
Design pattern where independent agents read/write a shared board instead of talking directly |
| DeLM |
Decentralized Language Models — Stanford research framework (arXiv:2606.10662) achieving 50% cost reduction via verified shared context |
| Gist |
A compact (~100 token) plain-English summary of a finding, artifact, or failure |
| Admission-time verification |
Checking a claim against its supporting evidence before it enters shared context |
get_task_context |
MCP tool agents call to receive all shared context relevant to their current task |
log_decision |
Existing MCP tool (src/marcus_mcp/tools/context.py:22) for agents to record architectural decisions |
log_artifact |
Existing MCP tool (src/marcus_mcp/tools/attachment.py:32) for agents to share files (specs, designs, APIs) |
| Foundation task |
A special pre-agent task whose outputs (decisions, artifacts, and proposed: failures) are shared project-wide |
| Selective unfolding |
Mechanism to request progressively more detail about a gist entry — compact by default, full on demand |
How to Verify It Works
After Feature 1 (log_failure):
- Start a Marcus project with at least two tasks that share a dependency (one upstream, one downstream).
- Simulate the upstream agent calling
log_failure with a concrete failure description and constraint.
- Call
get_task_context for the downstream task.
- Expected: the response includes a
failures key containing the failure logged in step 2.
- Check
data/project_history/{project_id}/failures.json — expected: a failure entry exists with correct task_id, agent_id, constraint, and timestamp fields.
- Check the kanban board card for the upstream task — expected: a
❌ DEAD END comment is visible on the card.
After Feature 2 (verification gate):
- Call
log_decision with a claim that cannot be verified against the task's context (e.g., a specific number that does not appear in any artifact or task output).
- Expected: the tool returns
{"success": false, "reason": "claim not grounded in provided evidence"} and nothing is written to state.context.decisions.
- Call
get_task_context for a downstream task — expected: the unverified decision does NOT appear.
After Feature 3 (gist layer):
- Log an artifact via
log_artifact with a large document (>500 tokens).
- Call
get_task_context from a downstream task.
- Expected:
dependency_artifacts contains a gist field (~100 tokens) and a full_content_available: true flag, but NOT the full document body.
- Call the new
get_artifact_content(filename) tool.
- Expected: full document content is returned.
Related
- DeLM paper: Decentralized Multi-Agent Systems with Shared Context, Mao & Mirhoseini, Stanford, arXiv:2606.10662 (June 2026)
log_decision implementation: src/marcus_mcp/tools/context.py
log_artifact implementation: src/marcus_mcp/tools/attachment.py
get_task_context implementation: src/marcus_mcp/tools/context.py
What Is Marcus, Briefly
Marcus is a multi-agent task coordination system. It breaks a software project into subtasks, places them on a shared kanban board, and dispatches independent AI agents to claim and complete those tasks. Agents communicate only through the board — never directly with each other.
Problem (User-Facing)
Right now, when an agent on a Marcus project goes down a wrong path — tries the wrong file, pursues the wrong fix, picks the wrong library — that failure stays private. The next agent assigned to a related task starts from scratch and may spend its entire budget rediscovering the same dead end.
This is a real cost problem. Consider a project with 10 agents working in parallel. If three of them each spend 30 minutes and 5,000 tokens pursuing a bad approach the first agent already ruled out, the project wastes 90 minutes and ~15,000 tokens (at $3.75/M tokens ≈ $0.06) on repeated exploration that could have been avoided with one shared note.
More subtly: when an agent discovers a hard constraint — "approach X is globally invalid because of Y" — that constraint currently lives only on that agent's task card. A later agent can unknowingly override or soften it, causing downstream failures that are hard to trace.
Motivation: What Stanford's DeLM Research Shows
A June 2026 Stanford paper, Decentralized Language Models (DeLM, arXiv:2606.10662), tested a multi-agent system built on exactly the design Marcus uses: a shared board, a pull-based task queue, and no central orchestrator. Their headline finding:
The cost saving comes from three concrete mechanisms:
Marcus already has
log_decision(src/marcus_mcp/tools/context.py:22) andlog_artifact(src/marcus_mcp/tools/attachment.py:32) MCP tools that agents call during task execution. These cover what to build and design artifacts. What is missing is the complementary channel for what not to do, a verification gate to ensure what gets shared is trustworthy, and a compact summary layer so the shared context stays readable as projects grow.Proposed Features
Four improvements to Marcus's shared-context layer, ranked by expected impact.
Feature 1 —
log_failureMCP Tool ★ Highest PriorityWhat. A new MCP tool agents call when they discover an approach does not work. The failure is written to shared context and made visible to all agents on the same project.
Why. This is the single mechanism responsible for most of DeLM's cost savings. Failed paths are currently private.
log_failureturns every dead end into shared knowledge.Proposed interface:
Where it goes.
src/marcus_mcp/tools/context.py— alongside the existinglog_decisiontool at line 22.What it writes.
Failureentry tostate.context.failures(in-memory, analogous tostate.context.decisions)❌ DEAD ENDcomment posted to the kanban card fortask_iddata/project_history/{project_id}/failures.jsonHow later agents see it.
get_task_contextshould includefailuresfrom upstream tasks and the foundation task in its response, the same way it already includesdecisionsandartifacts.Concrete example (adapted from a DeLM paper trace):
An agent is working on a SymPy bug. It tries fixing the Python printer layer and calls:
The next agent, assigned a related task, reads this failure via
get_task_context. It skips the printer entirely and goes straight tolambdify.py:964. One full round of redundant exploration is eliminated.Feature 2 — Admission-Time Verification Gate ★ High Priority
What. Before a
log_decisionorlog_failureentry is admitted to shared context, a lightweight LLM check verifies the claim is grounded in the agent's actual task output. Unsupported or hallucinated claims are rejected, and the agent is asked to revise before the entry is broadcast.Why. The shared context is the communication substrate for all agents. A false claim admitted to shared context propagates to every downstream agent as if it were fact. DeLM's ablation shows that removing verification drops accuracy by 4.9 percentage points (60.1% → 55.2%), even when everything else is the same.
Concrete failure mode the gate prevents (from the DeLM paper):
An agent writes:
But source S5 only says "conditional affirmance with remittitur" — no dollar amounts appear anywhere. Without a gate, this hallucinated number enters shared context as grounded fact. Every subsequent agent treats it as true.
With the gate, a verifier rejects it:
Where it goes.
src/marcus_mcp/tools/context.py— add a_verify_before_admit(entry, supporting_context)step inside bothlog_decisionandlog_failurebefore the entry is written tostate.context.Implementation note. The verifier can be a cheap, fast model (e.g.,
claude-haiku-4-5-20251001). DeLM's ablation found that summarizer/verifier model quality has almost no effect on final accuracy — a cheap model matches expensive ones on this task (§4.3.1 of the paper).Feature 3 — Compact Gist Layer for Artifact Context ★ Medium Priority
What. Currently, when an agent calls
get_task_context, it receives full artifact documents — complete spec files, full design docs. DeLM shows that giving agents a compact ~100-token summary (a "gist") by default, with the option to request the full document on demand, is both cheaper and more accurate than delivering full content upfront.Why. A 10-task project might accumulate 5 design artifacts, each 2 KB. Every
get_task_contextcall delivers ~10 KB of artifact content regardless of how relevant it is to the requesting agent's task. This inflates prompt sizes, increases cost, and can push task-specific instructions out of the agent's context window. DeLM's gist length ablation (§4.3.1) found that 100 tokens per artifact is the sweet spot — accuracy plateaus beyond that.Proposed change to
get_task_contextresponse:{ "dependency_artifacts": [ { "gist": "REST API spec for user auth: POST /login, POST /refresh, DELETE /logout. JWT-based, rate-limited at 100 req/min. See full spec for error codes.", "filename": "api_spec.json", "location": "docs/api/api_spec.json", "artifact_type": "api", "full_content_available": true } ] }A new
get_artifact_content(filename)tool lets an agent retrieve the full document when it needs precise details.Where it goes.
src/marcus_mcp/tools/context.py— modifyget_task_contextto return gists instead of full content by defaultsrc/marcus_mcp/tools/attachment.py— generate and store a gist whenlog_artifactis called (line 32)src/marcus_mcp/tools/attachment.py→get_artifact_content(filename)Feature 4 — Selective Unfolding for Decisions and Failures ★ Lower Priority
What. Extend the gist layer to decisions and failures, not just artifacts. Agents read compact summaries of all upstream decisions and failures by default. They call
unfold_context(entry_id)to retrieve the full reasoning behind a specific decision or the full trace behind a specific failure.Why. On longer projects,
get_task_contextcan return many kilobytes of decision and failure text. The unfolding mechanism gives agents a lightweight index of what happened upstream, with opt-in drill-down only for entries that are actually relevant to their current subtask.Where it goes.
src/core/context.py(theContextclass managingstate.context.decisionsand the newstate.context.failures) andsrc/marcus_mcp/tools/context.py.Implementation Order
log_failuretool — highest impact, most self-contained, ships independentlylog_failureandlog_decision; ships independentlyget_task_contextandlog_artifactWhere to Look in the Code First
src/marcus_mcp/tools/context.pylog_decision(line 22),get_task_context— primary file for Features 1, 2, 4src/marcus_mcp/tools/attachment.pylog_artifact(line 32) — primary file for Feature 3src/core/context.pyContextclass; in-memory decision storage; cross-task propagation logic (line 487 for dependency filtering)src/core/project_history.pyfailures.jsonwould be added here for Feature 1tests/unit/mcp/tests/integration/Glossary
get_task_contextlog_decisionsrc/marcus_mcp/tools/context.py:22) for agents to record architectural decisionslog_artifactsrc/marcus_mcp/tools/attachment.py:32) for agents to share files (specs, designs, APIs)How to Verify It Works
After Feature 1 (
log_failure):log_failurewith a concrete failure description and constraint.get_task_contextfor the downstream task.failureskey containing the failure logged in step 2.data/project_history/{project_id}/failures.json— expected: a failure entry exists with correcttask_id,agent_id,constraint, andtimestampfields.❌ DEAD ENDcomment is visible on the card.After Feature 2 (verification gate):
log_decisionwith a claim that cannot be verified against the task's context (e.g., a specific number that does not appear in any artifact or task output).{"success": false, "reason": "claim not grounded in provided evidence"}and nothing is written tostate.context.decisions.get_task_contextfor a downstream task — expected: the unverified decision does NOT appear.After Feature 3 (gist layer):
log_artifactwith a large document (>500 tokens).get_task_contextfrom a downstream task.dependency_artifactscontains agistfield (~100 tokens) and afull_content_available: trueflag, but NOT the full document body.get_artifact_content(filename)tool.Related
log_decisionimplementation:src/marcus_mcp/tools/context.pylog_artifactimplementation:src/marcus_mcp/tools/attachment.pyget_task_contextimplementation:src/marcus_mcp/tools/context.py