cd /news/ai-tools/marvin-open-source-macos-ai-coding-i… · home topics ai-tools article
[ARTICLE · art-120071] src=github.com ↗ pub= topic=ai-tools verified=true sentiment=↑ positive

Marvin – Open-source macOS AI coding IDE that learns from its own sessions

Robert Ilisei released MARVIN, an open-source macOS AI coding IDE that uses a single assistant with enforced phases and a knowledge graph to reduce first-message context from 566K to ~13.4K tokens, cutting token costs to about 1/27th. The app, currently at v0.1.104, runs locally with a bundled sidecar and requires Anthropic credentials, available via Homebrew as 'marvin-ai'.

read39 min views1 publishedSep 3, 2026
Marvin – Open-source macOS AI coding IDE that learns from its own sessions
Image: Michielbdejong (auto-discovered)

M oderately A dvanced R obotic V irtual I ntelligence N etwork.

A pair-programming AI assistant. You drive vision and business decisions. MARVIN drives architecture, infrastructure, code, tests, docs, and security.

You say "let's build the login page" — MARVIN dives in: reads the codebase, proposes the schema + wiring + tests, executes with explicit confirms, commits.

"Here I am, brain the size of a planet, and they ask me to build a login page." — MARVIN, probably

A real session on MARVIN's own repo, unedited: two graph queries before the first file read, an answer with file and line citations, 19 seconds.

[One assistant, enforced discipline — a design for AI pair-programming that survives real projects]Why AI coding assistance decays on real projects, and the four bets MARVIN makes against the current grain:

One assistant with enforced phases— not an agent team; subagents are structurally unable to writeThe knowledge graph before the file read— structural answers at ~1/27th the token cost (measured on this repo), withfile:line

citationsDeterministic contracts at the tool gate— enforced, not politely requested in the promptLocal-first, no backend— inference straight to Anthropic; credentials never leave your machineWith measured results (first-message context: 566K → ~13.4K tokens), five architecture diagrams, and anonymized transcripts from real production sessions. The exhaustive companion — every subsystem down to the code — is the

[Technical Reference]. Both are also available as PDFs:[white paper]·[technical reference].

MARVIN has two components that work together:

Component Location Role
macOS app
macos/
Native SwiftUI app — IDE shell, chat, file tree, source control, terminal, diff viewer
Sidecar
sidecar/
Next.js 16 server on :3030 — Claude Agent SDK runner, tool policy, git API, file API, session storage

The Swift app talks to the sidecar over localhost:3030

. In a brew install the sidecar is bundled inside MARVIN.app/Contents/Resources/

(alongside a pinned Node 22 runtime) and spawned by the SwiftUI process on launch; quitting MARVIN cleans it up. See ADR-0023.

Releases.Homebrew installs the latest tagged release (currentlyv0.1.104).main

anddevelopment

are fast-forwarded together at each release;development

is where in-progress changes land between them. To build from source on either branch,git checkout <branch>

thenbin/marvin install-macos-app

.

brew tap RobertIlisei/marvin
brew install --cask marvin-ai

That's it. MARVIN.app appears in ~/Applications

, the bundled sidecar starts with the app, quitting MARVIN cleans it up. No Swift, Node, pnpm, Xcode, or Apple Developer account required on your machine.

First launch — one-time Gatekeeper step (macOS 26+).MARVIN is ad-hoc signed (no paid Apple Developer Programme membership). On first double-click macOS shows "Apple could not verify…": clickDone, then openSystem Settings → Privacy & Security, scroll to theSecuritysection, find "MARVIN.app was blocked from use…", and clickOpen Anyway. This whitelist persists for the life of the install — you only do it once. ([ADR-0027]has the technical detail.)

You'll need Anthropic credentials to use it — either run claude login

(the Claude CLI handles it) or paste an API key in MARVIN → Settings → Authentication.

Updates: brew upgrade --cask marvin-ai

. Uninstall: brew uninstall --cask marvin-ai

(add --zap

to also wipe ~/.marvin

).

Note: the cask token is

marvin-ai

, notmarvin

— the plain token is taken by the unrelated "Amazing Marvin" productivity app in the official homebrew-cask repo.

If you've cloned the repo and want to build locally:

bin/marvin install-macos-app   # build → ~/Applications/MARVIN.app
bin/marvin uninstall-macos-app # remove app (both ~/Applications and legacy /Applications)

Default install mode is bundled (per ADR-0023) — same shape as the brew artefact. Pass --launchd

for the legacy mode that runs the sidecar from the source repo via a user-agent plist.

Requires xcodegen

  • Xcode, or just the Swift Command Line Tools (xcode-select --install

). If xcodegen

is missing, swift build

is used automatically as a fallback — no Developer account needed in either path.

main

and development

track together at each release; development

carries any work in progress between releases (ADR-0028 multi-graph landed this way). Build either from source:

git clone https://github.com/RobertIlisei/MARVIN.git ~/marvin
cd ~/marvin
git checkout development   # or: main
bin/marvin install-macos-app

Back to the signed release artefact at any time:

brew install --cask marvin-ai

graphify-out/knowledge/

is gitignored and harmless to leave behind when switching branches.

Requirement How to get it
macOS 14+ System update
Xcode ≥ 15 or Swift CLT
xcode-select --install
xcodegen (optional, preferred)
brew install xcodegen
Requirement How to get it
Node.js ≥ 22
brew install node@22

npm install -g pnpm

npm install -g @anthropic-ai/claude-code

claude auth login

— or set ANTHROPIC_API_KEY

in envOptional:

npx playwright install chromium

— needed for browser automation (MARVIN shells out tonpx playwright

when a turn needs a browser)pip install graphifyy

— needed for the knowledge graph (/graphify

, graph-aware chat)

After claude auth login

, also visit claude.ai once with the same email to accept the latest Consumer Terms — the CLI returns 400 until you do.

pnpm install                    # once — installs deps across all packages
bash scripts/setup.sh           # once — prompts for optional deps (Playwright, graphify)
bash scripts/install-skills.sh  # once — installs skills to ~/.claude/skills/ (clones upstream on demand)

bin/marvin start                # production mode (builds if stale, then starts)
bin/marvin stop
bin/marvin restart
bin/marvin status               # auth + model + data dir
bin/marvin logs                 # tail .marvin/dev.log
bin/marvin doctor               # preflight checks only, no start
bin/marvin help

For raw Next.js (no pid tracking, skips preflight):

pnpm build && pnpm start
cd macos
xcodegen generate               # regenerate MARVIN.xcodeproj from project.yml
open MARVIN.xcodeproj           # then build + run in Xcode

Or with swift build (Command Line Tools only, no Xcode IDE):

cd macos
swift build -c release
bin/marvin install-macos-app    # build + install + launchd
bin/marvin start

open ~/Applications/MARVIN.app
cd macos && xcodebuild -scheme MARVIN -configuration Debug build && open build/...

Single assistant, not an agent team. Published research on sequential coding tasks shows multi-agent autonomy degrades quality up to ~70 % and amplifies error rates 17× in flat-topology setups. MARVIN is one assistant moving through an 8-phase workflow in one conversation, with the user as continuous overwatch.Plan-first, execute-second, verify-third. Sketch the approach, ship, then verify. In-flight + shipped work tracked in.docs/roadmap.md

Per-project isolation. MARVIN holds zero cross-session knowledge about other projects. Memory, ADRs, and knowledge graph live inside each user project, not in MARVIN's own data dir — and the running IDE only ever reads or builds theactive project'sgraph, never its own source.Built on a knowledge graph it maintains for you. Queriesgraphifyfirst on architecture/impact questions (27.5× cheaper than reading raw files — measured withgraphify benchmark

, 2026-08-15), and builds/refreshes the active project's code + knowledge graphs itself (AST-only, free) so they're always current (ADR-0041).Memory is durable facts, not a log..marvin/memory.md

is a curated, one-line-per-fact index written only through theremember

tool — invariants and gotchas the next session can't re-derive from ADRs, git, or the changelog (ADR-0042).It learns from its own sessions — and proposes, never rewrites. Once a night a read-only pass over a project's transcripts finds the failures that repeat across sessions (and the same act done right, so each carries a rate), proposes a rule at three sessions, and — if you approve — enforces it at the tool gate and measures whether it held. No model reads a transcript; nothing changes without your click (ADR-0105). The measurement behind it is also why code review and security audit are now enforced atgit commit

instead of requested in the prompt (ADR-0104).

macOS app

  • 🍎 Native SwiftUI IDE shell — 3-pane layout (file tree · chat · brain/graph)
  • 📁 File tree with icons, click-to-open, context menu (create / rename / delete / move)
  • 🗒️ Syntax-highlighted file viewer (Swift, TS, JS, Go, Rust, JSON, YAML, Markdown, images)
  • 🔎 Find in file — ⌘F / ⌘G / ⇧⌘G / ⌘E in the editor, plus a header button
  • 🌿 Source control — stage/unstage, commit, push, pull, fetch, diff viewer, branch line
  • 🔍 Project-wide search — ripgrep-backed, include glob filter, replace all
  • 🔣 Symbol search and file history
  • 🏗️ Build task panel — run build tasks, see diagnostics inline
  • 🧩 Problems panel — diagnostics from a bounded, ignore-aware sweep of the whole tree (tsc · eslint flat + legacy · biome · ruff · go vet · cargo · swift build · maven), project-local wrappers preferred, fast checks automatic and minute-scale builds on demand. Grouped by file, severity chips that filter, rows jump to file:line:col

, and three distinct empty states — never-run, no-toolchain-found, genuinely-clean - 🔌 Language server client — diagnostics from the buffer, not from disk, so the list describes the file as it is now rather than as it was last saved (ADR-0099). Full-text sync, a three-strike crash budget, and a missing server surfacedas a diagnosticrather than as silence - ⌨️ Command registry — the menu bar, the ⇧⌘P command palette and the ⌘/ shortcut sheet are three renderings of one array, so a binding cannot drift between them. Save All · Revert · New Text File · Auto Save · Word Wrap · Toggle Line/Block Comment · Move/Copy/Duplicate Line · Expand/Shrink Selection · Go to Bracket · Go to Line · Next/Previous Problem · Next/Previous Change · Back/Forward · Run Active File - 🧭 Outline · Timeline · Tasks — the sidebar sections VS Code has, built language-agnostically: the outline reads the knowledge graph rather than tree-sitter (which covers 12 languages and would silently do nothing in the rest), and Tasks is a generic runner rather than the reference's Java-only Maven panel - 🛑 Stop Session & All Work (⇧⌘.) — Stop cancels the turn; this cancels the turn plus the background jobs and scheduled wakeups it leaves behind, which otherwise outlive it and can start a new turn on their own. Confirmation names the scope by count, and the whole thing is scoped to one session - ⌨️ Embedded terminal (PTY-backed)

  • 🕐 Session history — click any past session in the header to restore it
  • 🧠 MARVIN brain — live animated state indicator (idle / thinking / tool / writing / error)
  • 📎 Image paste in chat (⌘V, screenshots, dragged images)
  • 🌓 Light / dark theme — respects system preference
  • ✅ Agent change review — VS Code / Cursor-style: a live "N files changed" strip while MARVIN edits opens its own resizable window with a side-by-side (original │ modified) diff, line numbers, and a Split/Inline toggle. Per-hunk / per-file accept-reject against pre-agent baselines (rejecting restores youruncommitted state, not git HEAD); committing a change clears it from the review the way it leaves VS Code's Source Control list (ADR-0034) - 🎚️ Per-role reasoning effort — independent Low→Max effort pickers for the executor and the advisor (ADR-0033)
  • 🧭 Ask · Agent · Plan modes — read-only Ask (enforced at the gate), full-autonomy Agent, and plan-first Plan that drafts a plan + live to-do checklist and waits for your approval before executing (ADR-0036). Cursor-style controls live in the input box; chat tabs open/close and persist per project
  • 🗂️ Two-tier to-do / plan — a neutral Task list for bareTodoWrite

runs vs a purplePlan — <title> that persists, ticks off in place, and saves to.marvin/plans/<slug>.md

opened in the editor ("Open plan"); a completed plan collapses instead of re-prompting to approve (ADR-0036 two-tier addendum). The executor tags eachTodoWrite

item[N]

(plan step) /[N.M]

(sub-task) so tasks link to the right step by a stable key, sub-tasks roll up to complete their parent (a step is done only when all of its are), and theactive plan + live status is re-injected into the model's context every turn— so MARVIN never loses the plan across a chat switch or context compaction (ADR-0049 · ADR-0050 · ADR-0051) - ❓ Clickable decisions — when the model hits a real fork it calls AskUserQuestion and you pick from native option buttons (single/multi-select + "Other"); your choice returns to the model as the tool result, in every mode (ADR-0040) - 🧩 Per-project skill enablement — the fingerprint picks the installed skills relevant to thisproject and tells MARVIN to ignore the rest; per-skill toggles in the Skills pane (ADR-0037) - ⬇️ Fetch skills from Git — "Add from GitHub" pulls a skill from any repo, a …/tree/…

sub-path, or a plugin marketplace (ADR-0039); clone-and-copy only, never executes the repo - 🛰️ Event-based background jobs — run_background_job

runs a build/test/deploy past the turn and fires a real follow-up turn when it exits (no more orphaned "I'll be notified" promises); shell&

/nohup

denied at the gate (ADR-0038). The completion turn now renders in anidle session without a switch/relaunch — a per-project announce channel re-attaches the app to any server-initiated turn (ADR-0043), with a "background job running" chip so in-flight is distinct from done - 📋 Project backlog — a durable parking lot for the "noticed in flight, not in scope" follow-ups that used to evaporate with the chat: backlog_add

parks them (consent-gated at the scope-met handoff), they resurface in next session's context, and a macOS panel + tray chip lets you browse / Done / Dismiss /Promote to plan. A parking lot the user revisits — never a queue agents pull from (ADR-0044) - 🧹 Backlog review ( /groom

·Review button · View ▸ Backlog ⌘⇧B) — a backlog that only grows stops being read, so MARVIN reviews it: near-duplicates the exact-title dedup can't see, auto-captures nobody ever triaged, items untouched for weeks, references to files that no longer exist, HIGH items left sitting.Read-only by construction— it reports, you decide; the report carries that instructionwith the data, not just in the prompt. Findings render as badges on the rows they concern (ADR-0063) - 🏷️ Backlog classification — kind

(bug · feature · investigate · test · docs · chore) alongside severity, plus— a blocked bug and a blocked feature are both blocked, and an item waiting on an accountant shouldn't look pickable. Both change what the groomer nags about: a bug ages at a lower bar, whileblocked

/blockedOn

as its own axisinvestigate

andblocked

items are never called stale. No backfill — existing items stayunspecified

rather than take a guessed label (ADR-0064) - 🌙 Practice pane — Findings · Working · Rules · Runs: what MARVIN keeps getting wrong (and right) across this project's sessions, scored, with approve / dismiss / fixed in MARVIN per finding, run-now, backtest and a nightly schedule. The four built-in gates are rows you can re-tier or switch off; score weights fit from real outcomes on a click; a read-only model can draft a rule's wording from aggregates. A project with fewer than three sessions shows a calibration counter and offers rulesproven in your other projects to adopt (ADR-0105 ·guide)

Web sidecar

  • 🔒 Structural confirm gate — every Edit/Write/Bash pre-flight, auto-mode audit log
  • 🚢 Ship-review gate — pr-review

/security-audit

enforced atgit commit

from the diff the commit seals: boundary paths (auth · creds · CI · sudoers ·.env

· shell scripts · migrations) need both, >3 files or >50 lines needspr-review

, docs-only and lockfiles pass; two denies per skill per turn, then allow and log (ADR-0104) - 📏 Practice rules — rules you accepted enforce at prompt

/nudge

/deny

from a data table in the design hooks, a deny only where a machine-checkable discharge exists; a recurrence after acceptance isregressed

, a quiet windowconfirmed

(ADR-0105) - ⏰ Self-scheduled wakeups — MARVIN's "I'll check back in 10 minutes" is real: the schedule_wakeup

tool arms a bounded server-side timer that starts an actual follow-up turn (ADR-0031); background-and-forget Bash is gate-denied so a build can't finish unreported (ADR-0032) - 💸 Cost tracker — daily/weekly/lifetime spend per project

  • 🔀 Monaco diff viewer — see exactly what MARVIN is about to do before allowing
  • 🧰 Model picker — executor + advisor slots, live model list from Anthropic
  • 🌐 Browser automation — by default MARVIN drives real browsers against localhost

/ LAN URLs by shelling out tonpx playwright

(one-shot captures + fullplaywright test

). Opt-in,off by default: a gated Playwright** MCP**server (ADR-0045) for first-class, statefulbrowser_*

tools (navigate → snapshot → click → assert) — observation auto-runs, interaction/navigation confirm, andbrowser_run_code_unsafe

is denied - 🔄 Resume across reloads — closing the window doesn't kill a running turn

  • 📊 Graph-aware chat — in-process MCP exposes graph_summary

,graph_search

,graph_neighbors

,graph_path

; MARVIN builds + refreshes the active project's code and knowledge graphs itself (AST-only, free) so they stay current (ADR-0041) - 🧠 Durable-facts memory — a marvin-memory

MCP (remember

/recall

) is the enforced write path for.marvin/memory.md

: one fact per file + a one-line index, with caps + content-class guards so it can't bloat into a redundant log;/memory-compact

distills an existing one (ADR-0042)

macos/                         # SwiftUI macOS app (Xcode / SPM)
  MARVIN/                      # Swift sources
  project.yml                  # xcodegen manifest
  Package.swift                # SPM manifest (swift build fallback)
sidecar/                       # Next.js 16 sidecar, port 3030
  src/
    app/api/                   # REST endpoints (chat, git, files, sessions, health)
  packages/
    runtime/                   # Agent SDK runner, auth, session, cost, models, confirm gate
    tools/                     # Tool policy — auto / confirm / deny
    project-context/           # Spec + ADR + memory + graph-header injection
    graphify-bridge/           # Knowledge-graph read + in-process MCP server
    git-watch/                 # Per-workDir commit stream watcher
    ui/                        # shadcn primitives
bin/
  marvin                     # Lifecycle CLI (start/stop/status/logs/doctor/install/uninstall)
scripts/
  install.sh                 # Remote one-liner installer (curl | bash)
  uninstall.sh               # Remote one-liner uninstaller
  setup.sh                   # Interactive optional-dep prompts
  install-skills.sh          # Install skills to ~/.claude/skills/ (clones upstream on demand)
docs/
  decisions/                 # ADRs
  roadmap.md                 # In-flight + shipped features
  history/CHANGELOG.md       # Chronological record

macOS app

  • Swift 5.10 · SwiftUI · Observation framework
  • STTextView (code editor) · SwiftTreeSitter (syntax highlighting)
  • URLSession (loopback HTTP to sidecar)

Web sidecar

  • Next.js 16 · TypeScript · Tailwind 4 · shadcn/ui @anthropic-ai/claude-agent-sdk

0.3 — withTodoWrite

opted back in and in-process MCP serversalwaysLoad

(ADR-0073)- pnpm workspaces · Turbo

  • In-process MCP server: marvin-graph

(browser automation is vianpx playwright

shell-out, not an MCP)

v0.1.102–0.1.104 — the practice loop, the ship-review gate, and a night of measuring MARVIN against itself. A session audit (ADR-0104) found every mechanical rule held and every prompt-only one did not: .gitlab-ci.yml

, a script that fetches secrets, a production sudoers grant and SMTP credentials shipped across eight pushes with pr-review and security-audit invoked 0× — the same ~0× the 2026-05-22 audit measured for soft-nudge language. The review skills are now enforced at

git commit

by a gate that reads the diff. Then the general form of that audit became a feature (ADR-0105): a nightly, read-only pass over a project's transcripts runs deterministic extractors for repeat failures (unreviewed boundary commits, graph-first skipped, stalled turns, missing handoffs, skills read by hand instead of invoked, reviews whose findings went unacted, plans left behind, commands re-run verbatim) and their paired successes, scores them with weights fit from outcomes, proposes a rule at three sessions, enforces an approved one at

prompt

/ nudge

/ deny

, and measures whether it held. The first backtest on a real project — 397 sessions in 11 s— produced six proposals; three were MARVIN's own and were fixed in the runtime the same night. v0.1.103 made the four hand-written gates rows the Practice pane controls; v0.1.104 adds the cold start (a calibration counter, and rules proven in your other projects offered for adoption), the

Practice guide, and three fixes from the first report: a bare project-local skill name rewritten to its plugin-namespaced form at the gate (29 "Unknown skill" failures, every success namespaced), an advisory nudge on an identical re-run of a failed command, and a once-per-turn block on a three-edit turn under a plan whose checklist never moved. Also: a transcript made unloadable by half an emoji (a UTF-16 slice through a surrogate pair — three fixes, nothing lost), and a chat at 100 % CPU because every row re-laid-out on every streamed event (

ChatMessageRow

was not Equatable

).v0.1.87–0.1.98 — IDE parity, and four bugs that only measurement found. Two halves. The first is parity with the reference IDE (Antigravity, a VS Code fork), audited item by item into a parity matrix: a language-server client (ADR-0099) so diagnostics come from the buffer rather than from disk — verified against a real sourcekit-lsp

, which reported a type error on an unsaved edit while the file on disk still said otherwise, the exact capability a CLI runner cannot have; a command registry making the menus, the ⇧⌘P palette and the ⌘/ sheet three renderings of one array (it immediately surfaced two double-bound keys); source control reaching the reference's feature set, including four routes that had shipped with zero callers — the branch name was a Text

, so the feature was complete, tested, documented and unreachable; a Problems panel that searches the whole tree rather than the repo root, where "found nothing" had been rendering as "your code is clean"; and a tranche of self-contained editor commands. The second half is a lesson worth more than the features: four bugs in a row were diagnosed wrongly from reading source, and settled immediately once something was measured. A file tree drawing over the title bar took three wrong guesses about ScrollView ideal heights before a geometry probe said container at y=52, tree at y=0 — not too tall, 52pt too high, exactly the title bar, because VSplitView

does not inherit the safe area. An app frozen at 100 % CPU with two sessions open was a width latch with no deadband: collapsing changed the measured width, and one threshold in both directions let it cross back, each cycle re-forming the split view's panes. A Stop-All button was permanently disabled because it gated on a property nothing had set since the WebView was removed. And two sessions that looked "interconnected" had genuinely separate conversations — distinct SDK session ids per transcript — but collided on the working tree, which Golden Rule 1 forbids and nothing enforces. Also shipped: CLAUDE.md

is finally injected into project context (it never had been), chat prose is selectable across lines, and Stop Session & All Work

cancels the jobs and wakeups a turn leaves behind.

v0.1.77–0.1.86 — a day of failures that looked like nothing happening. Ten releases, one recurring shape. ** ADR-0097 — verify against what runs.** The Claude-plan usage bars survived two previous fixes because the SDK never resolves

claude

from PATH

: it spawns the native binary its own package links to, and the bundler picked that with find | head -n1

, linking 0.2.113 beside a 0.3.251 SDK. Every turn ran a CLI 138 versions behind while the About panel confirmed the right one. The same pass found

Skill

had been called 29 times across every transcript ever recorded, failing every time, with zero successes— the pane listed skills the had skipped, because

description:

is the load-bearing frontmatter key and the registered identity is always the directory. That upgrade removed

ADR-0098— a rail keyed on vendor tool names is only as durable as those names.Grep

/Glob

; all four graphify-first guards keyed on them with no Bash

branch, so searching moved where the rail is blind — 15 of 18 Bash calls search-shaped against 2 graph calls.

And a run of silent failures: a

ScrollView

under a frame with no idealHeight

measured the whole transcript (eight 61-second hangs); the bottom panel mounted tabs only on an activeTab

change, so opening it on the selected tab gave an empty pane with no shell; extended thinking rendered as

unhandled block: thinking

; _ = try await URLSession.data(for:)

treated an HTTP 500 as success, so Discover ran two minutes and showed nothing; which("tsc")

under a Finder-launched app's bare PATH

returned []

, rendered as "No problems detected"; and the terminal broke on project switch — sessions keyed by workDir

, SwiftUI keeping the representable's identity, updateNSView

swapping the session but not the view. Skills work on OpenRouter (tools: []

, and the proxy answers count_tokens

with an estimate rather than a fabricated zero).v0.1.65 — the SDK catches up, and two things that only looked broken. Agent SDK

0.2.113 → 0.3.245(

ADR-0073) — MARVIN was behind the end of its own 0.2 line, on a version predating

TodoWrite

's deprecation. Every 0.3 default that would change behaviour is pinned back with its reason: TodoWrite

opted in over the Task tools so the plan spine keeps receiving snapshots; all five in-process MCP servers alwaysLoad

, because 0.3 defers MCP tools behind ToolSearch

and the graphify-first hooks would deadlock a turn with no graph_*

tools. Verified live on Sonnet 5. "I lost all my sessions"(

ADR-0072) — nothing was lost: the session list

JSON.parse

d 2.6 GB per request (23 s), the client cancelled and restarted it on every layout rebuild, and hydration waited on it. Scan + cache: 36 ms; hydration no longer depends on the list. "MARVIN is skipping plan steps"(

ADR-0068add. 4) — a 10-step plan was tracked as 16 because its

Sources:

bibliography parsed as steps and one URL was in_progress

; the parser now stops at a reference heading. Skills and plugins can be updated, not just installed (

ADR-0071); pane actions moved out of the window toolbar, with tooltips.

v0.1.56–0.1.64 — stability, backlog review, and find-in-file. Four app-killing crashes traced and fixed. The file tree was retired off SwiftUI's OutlineGroup

entirely (ADR-0061): four separate fixes each closed one way for its outline coordinator to disagree with AppKit, and the fourth crash was caused by the third — so the tree now flattens to a plain row list and the whole failure class is structurally gone. A second crash resisted two fixes aimed at a mechanism inferred from the stack alone, so MARVIN now captures the exception itself (ADR-0062) — name, reason and symbols to ~/Library/Logs/MARVIN/exceptions.log

— which identified it on the first occurrence as AppKit's non-converging-layout breaker, now survived rather than fatal. The check-back guard stopped swallowing timed promises (ADR-0055 addendum): a background dev server counted as follow-through for "I'll check in ~2.5 minutes", though it never exits and so never reports. Backlog review (ADR-0063) and classification (ADR-0064) landed read-only: the groomer reports, you decide. Find in file returned (⌘F), and bin/marvin doctor

stopped telling you to kill your own running app.

v0.1.55 — verify-then-remediate contract: bounded self-fix, gated scope-fix. MARVIN's Phase 6/7 loop had a "verify, then what?" gap — it walked the Definition of Done but had no explicit contract for what to do when a check failed. Now split by failure class. Phase 6 (mechanical): typecheck / test / build failures MUST self-remediate without asking — capped at 3 attempts per milestone with an early no-progress stop (identical errors twice = spinning → stop), then an honest failure report; MUST NOT claim landed, weaken the DoD, or skip the failing check. Phase 7 (scope): an unmet DoD bullet gets surface-and-offer — state the gap plus the one concrete next step, then gate ("one gap, one gate"); MUST NOT loop back into Phase 6 unprompted. A fully autonomous retry-until-DoD mode was deliberately not built — it institutionalizes the Golden-Rule-8 "helpful spiral"; revisit only as an explicit opt-in with its own ADR, cost budget, and progress metric. Prompt-only change in personality.ts

; also fixed 9 pre-existing typecheck errors in can-use-tool-dispatch.test.ts

(readonly-array fixture vs the SDK's mutable PermissionUpdate[]

).

v0.1.54 — the IDE no longer resets on a transient health blip. The window "kept resetting" mid-work — pane layout, file-tree expansion, terminal, editor, chat scroll all snapping to default. ContentView.mainContent

switches its whole view tree on health.state

, and HealthMonitor.pollOnce

flipped to .offline

on any single failed /api/health

poll (3 s timeout, no hysteresis) — a healthy-but-busy sidecar answering slowly tore down and rebuilt the entire IDE. Fix: demote to .offline

only after 3 consecutive misses (hold .online

/.connecting

through blips), poll fast while misses are pending so a genuine outage still surfaces in seconds, and bump the poll timeout to 5 s.

v0.1.53 — backlog "Promote to plan" actually plans. Promoting a backlog item did nothing: promoteBacklog

sent "Implement this item…" in whatever mode was active and never switched to Plan mode — but the turn-completed ingest only mints a tier-2 Plan + approval chip when mode == "plan"

— and if a turn was in flight sendControl

's !isSending

guard silently dropped it while the panel closed anyway. Now it switches to Plan mode and asks MARVIN to present a plan inline (read-only first), and queues the request when busy instead of dropping it.

v0.1.52 — file-tree crash fixed. The app trapped (EXC_BREAKPOINT

in OutlineListCoordinator.recursivelyDiffRows

) during a file-tree row diff: FileNode.outlineChildren

returned a non-nil empty array []

for empty directories, but SwiftUI's OutlineGroup

expects nil

(leaf) or a non-empty array — an agent mutating files mid-session flipped a node into the []

shape and the next diff crashed. Empty dirs now return nil

(leaf). A companion build fix: the install smoke-probe now reaps the forked next-server

worker (it bound the probe port and survived the parent kill) so installs stop leaking orphan sidecars.

v0.1.51 — plan-in-context: the model is aware of the active plan every turn. The plan was UI-only state — a strip rehydrated from the transcript, never injected into the model's prompt — so after a chat switch or context compaction the model lost the plan while the strip still showed it ("MARVIN stopped tracking / won't continue it"). The client now sends a compact planContext

snapshot (title + [x]/[~]/[ ]

steps + sub-tasks, marked authoritative) every turn, and the runtime appends it as a <system-reminder>

suffix on the user message — the uncached volatile tail, so it's prompt-cache-safe (Anthropic's caching rules) and never persisted to turn.user

. Mirrors how Claude Code re-injects its todo list. ADR-0051.

v0.1.49–0.1.50 — plan integrity. A Plan-mode turn that hit a transient 529 Overloaded got its error text ingested as a plan (no # Plan

heading → fallback title "Plan" → plan.md

), hijacking the active plan; the turn-completed ingest is now gated on PlanCard.isPlan

(0.1.49). And a step could read "done" while all its sub-tasks were still open — completion is now a hard invariant: a step with sub-tasks completes iff every sub-task is completed (0.1.50). ADR-0046 / ADR-0049 addenda.

v0.1.44–0.1.45 — plan-step join key + Continue anchoring. Tasks linked to plan steps by fuzzy text match, so a reworded TodoWrite

item landed as an orphan and the plan never advanced. The executor now tags each item [N]

(plan step) / [N.M]

(sub-task), giving a stable join key, with upward roll-up (sub-tasks done → parent done) — ADR-0049. And the Continue control re-injects the active plan's concrete steps + a "resume only this, don't re-audit the project" guardrail, so a d plan resumes itself instead of triggering a whole-project re-audit — ADR-0050.

v0.1.46–0.1.48 — browser/MCP + background-job polish. The Playwright MCP didn't start under a Finder-launched app (minimal launchd PATH → bare npx

ENOENT'd); PATH is now enriched at the sidecar launch and on the spawned server's env (0.1.46). MCP-vs-CLI browser selection became a deterministic MUST trigger instead of a soft nudge (0.1.47). And a long-running background job (a dev server) SIGTERM'd on app quit no longer fires a spurious "job did NOT succeed" turn that resurfaced on every relaunch — shutdown/stop signals are treated as "stopped, not finished" (0.1.48). ADR-0045 / ADR-0038 addenda.

v0.1.43 — full session history via incremental paging. Restoring a session on cold start loaded only the last 200 cli.event

lines (hydrate(tail:200)

  • the server's turns.slice(-tail)

) — and since one exchange spans many events, that was just the last few turns, with no signal it had been clipped. The server now reports truncated

/totalTurns

; the client paints the last 200 lines instantly, then a top-of-list control loads the next 200 (or jumps to the full log) on demand, with a live "N of M lines" count. Fast first paint plus user-controlled completeness — the full transcript is always reachable, and the 120 MB worst case is never auto-loaded. ADR-0048.

v0.1.42 — plan persistence + review-window fixes + backlog capture-at-discovery. Three changes. (1) Plan persists across chat switches (ADR-0046 follow-up): the plan strip was in-memory/session-scoped, so switching chats or relaunching lost it; replay

now reconstructs the plan + checklist from the transcript on session load (last # Plan

reply + latest TodoWrite

for step progress), and a later TodoWrite

reconciles into the restored plan instead of orphaning as a task list. (2) Review window (ADR-0034 bugfix): a newly-written file (one all-added hunk) showed a half-empty side-by-side and hung the window; added/deleted files now render single-column with a banner, the diff flattens to a virtualized row-level LazyVStack

, and a >1500-line diff is gated behind "Show anyway" (mirrors GitHub/VS Code). (3) Backlog capture-at-discovery (ADR-0047): "noticed in flight" items were lost when a turn ended without a handoff; a new provisional

status + backlog_add … provisional:true

auto-park a discovery the instant it's noticed, with a keep/dismiss review at the handoff.

v0.1.41 — plan as the durable spine. Two plan-tracking bugs: a TodoWrite

emitted mid-plan wholesale-replaced the checklist, so sub-tasks erased the plan's steps and a sub-task-only list fired a false "Plan complete"; and a second plan overwrote the single plan slot, making the original untrackable. The active plan now owns hierarchical steps — incoming TodoWrite

s reconcile into them (matched step → status update, unmatched item → nested sub-task) instead of replacing the list, completion is computed over top-level steps only, and plans live in a revision-aware session list with a strip picker so prior plans stay navigable. personality.ts

  • the approve-to-execute instruction now require a full carry-forward TodoWrite

(never a partial list). ADR-0046, revising ADR-0036.

v0.1.40 — fix: AskUserQuestion's "Send choice" did nothing. The interactive decision sheet (ADR-0040) registered its confirm with the default 5-minute auto-deny timeout — the one meant for permission confirms. A human weighing detailed options for >5 min was silently auto-DENIED (the turn proceeded ignoring the choice; the registry entry was deleted), so a later "Send choice" click hit a dead confirm and did nothing. AskUserQuestion is the model explicitly blocking on a human decision, so it now registers with NO auto-deny timer — it waits for you; the turn's finally

(clearTurnConfirms

) + Stop unwind an abandoned one. Regression test in confirm-registry-timeout.test.ts

.

v0.1.39 — Playwright MCP, opt-in + gated. MARVIN's first EXTERNAL (stdio) MCP server (npx @playwright/mcp@latest

), off by default. The gate previously blanket-allowed every MCP tool — safe for the in-process graph/memory/backlog servers, unsafe for Playwright's code-exec/egress tools. A mcpToolPolicy

now classifies the playwright

tools (observation auto · interaction/navigation confirm · browser_run_code_unsafe

deny), the subagent read-only invariant restricts scouts to observation, and a playwrightEnabled

toggle is threaded to the web Setup popover + macOS Settings ▸ Browser (ADR-0045).

v0.1.38 — Project backlog. A durable, per-project parking lot for actionable "noticed in flight, not in scope" follow-ups that used to evaporate with the chat. A marvin-backlog

MCP (backlog_add

/ list

/ resolve

, content-class enforced) + GET/POST/PATCH /api/backlog

share one store (file-per-item + index, mirroring memory); open items re-inject into next session's context; a macOS panel + tray chip gives Done / Dismiss / Promote-to-plan. Consent-gated capture; a parking lot, never a queue agents pull from (ADR-0044).

v0.1.37 — server-initiated turns reach an idle client. Background-job completion (ADR-0038) and timed wakeups (ADR-0031) fire a real turn server-side, but the idle app only attached to a turn's stream on session hydrate — so the completion turn ran into the bus with no listener and was invisible until a session switch. A per-project always-on SSE (/api/chat/announce

) now re-attaches the idle app to any server-started turn, with a "background job running" chip (ADR-0043).

v0.1.33–0.1.36 — turn-lifecycle hardening + context panel. One live turn per session (POST /api/chat

returns 409

instead of evicting a running turn, and eviction aborts the displaced agent — v0.1.33); Stop is authoritative (cancelLiveTurn

force-ends a wedged turn so the session can't lock — v0.1.34); a click-to-open context-usage panel on the status-bar ctx

chip with a per-category breakdown from live SDK usage (v0.1.35); and a fired wakeup yields to a live interactive turn instead of evicting it (v0.1.36). (v0.1.34 also ended a stray-tag version divergence, resetting the line to 0.1.x.)

v0.1.32 — memory as a curated durable-facts layer. A real project's .marvin/memory.md

had bloated to 419 KB / ~99 % redundant with ADRs, git, and the changelog (the model mirrored its Ship summaries into it). memory now holds ONLY what the next session can't re-derive from those — invariants, gotchas, constraints, external facts. A new in-process ** marvin-memory** MCP (

remember

/ recall

) is the enforcedwrite path: one fact →

.marvin/memory/<slug>.md

  • a one-line index, supersede-by-name, with length caps + content-class guards that reject activity/status. personality.ts

carries a MUST/MUST-NOT firm surface; a command distills an existing log. The Scope-met chip is retargeted to

/memory-compact

.marvin/session-notes.md

so it can't pollute the index. ADR-0042.v0.1.31 — "Prompt is too long" fixed. On a mature project the first message overflowed the model's 200 K window — buildProjectContext

injected every ADR in full + the whole memory.md (~566 K tokens measured). Two layers (ADR-0041): MARVIN now builds/maintains the active project's graphs (code + knowledge, AST-only/free, scoped to the project — never its own repo), and the first-message context is budgeted — ADRs as a titles index (details via the knowledge graph + targeted reads), memory as a recent tail, curated docs whole. Measured 566 K → ~13 K tokens.

v0.1.30 — interactive AskUserQuestion + Node-24 CI. When the model hits a real decision it calls AskUserQuestion and MARVIN renders the options as clickable buttons (single/multi-select + "Other"), returning your pick to the model as the tool result — instead of prose "(a)/(b)" you could only answer by typing. Routed through the existing confirm channel in every mode (ADR-0040). Also bumped every release-workflow action to its Node-24 major ahead of GitHub's cutoff.

v0.1.27–29 — two-tier to-do / plan polish. The checklist strip splits into a neutral "Task list" (bare TodoWrite

, no plan) and a purple "Plan — <title>" (plan-backed, ticks off in place); a presented plan is auto-written to .marvin/plans/<slug>.md

and opened in the editor pane ("Open plan"); the plan title/filename derive from the # Plan

heading wherever it sits (no more garbage slugs); and a completed plan no longer shows a contradictory "Approve & execute" chip. ADR-0036 (two-tier addendum). Plus the Homebrew "MARVIN.app is damaged" fix — the cask now strips com.apple.quarantine

in a postflight

(modern Homebrew quarantines casks by default; ad-hoc bundle + quarantine reads as "damaged" on macOS 26).

v0.1.26 — The plan card. Plan-mode plans render as a structured, collapsible Cursor-style plan card (title, step count, styled headings/steps/code) instead of a plain-text bubble — the plan-mode prompt mandates a # Plan — <title>

opening heading, detected live and on transcript replay. Approving seeds the To-dos checklist from the plan's steps, and the d chip now names the next step and what there concretely is to review (the stopping error, or the changed-file count).

v0.1.25 — Plan-mode UX polish. Session-scoped strips (no stale plan in a new chat); Approve/Continue as hidden control actions (no fake user message in the chat); Save plan to a Markdown file you can follow alongside the chat; collapse/dismiss the checklist (auto-collapses when done); and the checklist relabeled "To-dos" — it's the task tracker (used in any mode), while the plan stays a distinct inline message + file.

v0.1.24 — Plan mode decoupled, chat strip tray.

Plan mode(ADR-0036 rev) — a read-only planning turn on your chosen** advisormodel presents a numbered plan inlinein the chat (no modal); an"Approve & execute"** chip then runs it in a separateAgent turn on yourexecutor model. Models routed by role; no re-planning.Chat strip tray— the plan checklist, changed-files Review, and session controls now live in one opaque, divider-separated tray that no longer overlaps the message log.

v0.1.23 — Background jobs, fetch skills from Git, Plan follow-through.

Background jobs(ADR-0038) —run_background_job

runs a long command past the turn and fires a real follow-up turn on exit with the result; shell backgrounding is denied at the gate.Fetch skills from Git(ADR-0039) — "Add from GitHub" installs a skill from any repo, a…/tree/…

sub-path, or a plugin marketplace (clone + copy only).Plan-mode follow-through(ADR-0036) — the plan persists in the chat and becomes the tracked to-do checklist (○→◌→✓); the prompt requires liveTodoWrite

updates.Skills pane reorganised by state: active here · installed-off-here · recommended to add.

v0.1.22 — Modes, Cursor-style chat surface, skill enablement.

Ask · Agent · Plan modes(ADR-0036) — amode

axis orthogonal to the auto/gated strategy. Ask is read-only (hard-denied at the gate); Plan runs under the SDK's plan mode and surfaces an approval card before executing; Agent is the unchanged default. The model'sTodoWrite

renders as a live checklist.Cursor-style chat surface— mode + reasoning controls moved into the input box; chat tabs you can open and close, persisted per project.** Per-project skill enablement**(ADR-0037) — installed ≠ active: a core/domain catalog + fingerprint default names the skills relevant to this project and tells the model to ignore the rest (20→7 on this repo). Skills-pane toggles +.marvin/skills.json

.

v0.1.21 — Change-review diff editor.

VS Code / Cursor-style diff editor— the review surface is its own resizable window: side-by-side original │ modified, line numbers, and a Split/Inline toggle (v0.1.20). The editor's diff gutter now tracks lines exactly on scroll — markers come from STTextView's real layout geometry, cached, instead of a line-height guess that drifted (v0.1.21). Andcommitting a change clears it from the review the way it leaves VS Code's Source Control list — a committed change is an accepted one (reconcileCommitted

, drops only, never rewrites a baseline). ADR-0034.Agent change review— the permission gate snapshots every file's pre-image on first agent touch per session; accept advances the baseline, reject reverse-applies to disk — nevergit discard

, which would destroy uncommitted user work. v1 blind spot: Bash-driven mutations aren't pre-imaged. ADR-0034.Per-role reasoning effort— the advisor is a registered agent definition carrying its own model + effort, settable independently of the executor (the SDK'sadvisorModel

option turned out to be unwired; the agents-map registration is what actually works). ADR-0033.Self-scheduled wakeupsschedule_wakeup

/cancel_wakeup

/list_wakeups

MCP tools backed by a bounded, persistent, boot-re-armed scheduler; a fired wakeup starts a real turn that resumes the session. Bashrun_in_background

is gate-denied (the runtime can't deliver completion notifications, so the capability shouldn't exist). ADRs 0031, 0032.The bundled app owns its port— launch reclaims:3030

from any stale sidecar before spawning, and/api/health

reports the serving process's app version, so "new app on disk, old code in memory" can't recur. ADR-0035.

v0.1.6 — Brew-installable, project-aware.

Brew caskbrew install --cask marvin-ai

produces a working IDE on a fresh Mac with no Swift / Node / pnpm / Xcode required. Bundled Node 22 + Next.js standalone sidecar insideMARVIN.app/Contents/Resources/

(ADR-0023).Project-aware skill recommendations— fingerprint detector emits namespaced tags (framework:next

,architecture:multi-tenant

,test:playwright

, …) from a project's manifests + memory file; the suggestion engine maps tags to skills you can either install user-global or build project-local. ADR-0024.Skills pane— fourth tab in the left pane (Files / Search / Source Control / Skills): suggestions for the current project, your user-global skill catalog, and project-local skills. One-click "park all" closes the audit loop. ADR-0025.

v1.3 — Fully native IDE surface (shipped 2026-05-05).

The WebView is gone. The macOS app is a pure SwiftUI IDE shell backed by the Next.js sidecar over loopback. Full feature parity with the web-era UI plus IDE features the browser couldn't provide:

WebView removed— all UI surfaces are native Swift; no Tauri, no WKWebView** Syntax highlighting**— tree-sitter grammars for Swift, TS/TSX, JS/JSX, Go, Rust** Image preview**— binary image files (PNG, JPEG, GIF, WebP, HEIC) open inline** Image paste**— ⌘V in chat accepts screenshots and dragged images** Find in files**— ripgrep-backed with glob filter and replace-all** Push / pull / fetch**— full remote ops in the source control panel** Session history**— clock menu in chat header restores any past session** Right-pane resize**— min-width fixed so brain + chat never overlap other panes

See docs/roadmap.md and

.

docs/history/CHANGELOG.md

First diagnostic — always:

curl -s http://localhost:3030/api/health | jq .
Symptom Likely cause Fix
auth.mode: "none"
No credentials detected ANTHROPIC_API_KEY in env, or claude auth login
binaryError in /api/health
Claude CLI not on PATH npm install -g @anthropic-ai/claude-code or set MARVIN_CLAUDE_BIN
Every turn → 400 Consumer Terms
Anthropic account hasn't accepted latest Terms Open

EADDRINUSE :::3030

lsof -iTCP:3030 -sTCP:LISTEN

→ kill itcd <workDir> && /graphify .

~/Library/Logs/MARVIN/sidecar.log

for the cause; relaunch MARVINNo module 'STTextView'

cd macos && swift package resolve

ANTHROPIC_API_KEY

directlyLifecycle helpers:

bin/marvin status   # auth + model + data dir
bin/marvin doctor   # preflight checks
bin/marvin logs     # tail .marvin/dev.log

Still stuck? Open an issue at github.com/RobertIlisei/MARVIN/issues with /api/health

output, the last 20 lines of ~/Library/Logs/MARVIN/sidecar.log

, and your macOS version.

White paper— the design position, the evidence, and the results;Technical Referenceis the exhaustive companion (both also asPDF/PDF)Overview— what MARVIN is, who it's forQuickstart— install → first sessionModes & workflows— Ask / Agent / Plan with worked examples, and how MARVIN takes decisionsPractice— how MARVIN learns from its own sessions: findings, rules, tiers, verification, and the pane that manages themArchitectureHTTP API referenceADRs— design decisions

Every release zip published since v0.1.x is signed with minisign. The signature lives next to the zip on each GitHub Release as MARVIN-<version>-arm64.zip.minisig

.

Public key (pinned here, in the homebrew-marvin tap's README, and in

):

Casks/marvin-ai.rb

untrusted comment: minisign public key 0794CFDFA5E629D5
RWTVKeal38+UBwQ3tC8ETdPZkv8fFLchoXdtwi7UI9XMhaJWuUwx4QAQ

The same key is mirrored in .minisign-pubkey, in the

tap's README, and in

homebrew-marvin

as the

Casks/marvin-ai.rb

MARVIN_MINISIGN_PUBKEY

constant. Three pinned copies across two repos — a tap-repo compromise that swapped the cask's pubkey would be visibly inconsistent with this repo's record.Verify a downloaded release:

brew install minisign
VERSION=0.1.9   # whichever version you downloaded
curl -fLO "https://github.com/RobertIlisei/MARVIN/releases/download/v${VERSION}/MARVIN-${VERSION}-arm64.zip"
curl -fLO "https://github.com/RobertIlisei/MARVIN/releases/download/v${VERSION}/MARVIN-${VERSION}-arm64.zip.minisig"
curl -fLO https://raw.githubusercontent.com/RobertIlisei/MARVIN/main/.minisign-pubkey
minisign -V -p .minisign-pubkey -m "MARVIN-${VERSION}-arm64.zip"

A successful verify prints Signature and comment signature verified

and exits 0. If the signature doesn't verify, do not install the artefact — and please open an issue, because either:

  • the tap repo or the release was tampered with, or
  • our private key was lost (in which case we'll publish a rotation announcement, also signed)

See ADR-0026 for the full signing model, the threat shapes this defends against, and the key-rotation policy.

The cask install path (brew install --cask marvin-ai

) does not yet auto-verify the signature — Phase 2 of ADR-0026 will add a preflight

step. Until then, manual verification is the canonical path for users who care.

MIT · © 2026 Robert Ilisei

── more in #ai-tools 4 stories · sorted by recency
── more on @robert ilisei 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/marvin-open-source-m…] indexed:0 read:39min 2026-09-03 ·