Coding in Marina
A coding session in Marina lives inside a persistent world. Start with one coder and one task; add other participants when the work calls for them. People and agents can exchange messages, inspect recorded checks, review submitted work, and share what they learn while independent world activity continues. Verification and approval describe specific candidates, not a promise that every edit has already been checked or can automatically be reversed.
This guide gets you from zero to a working coding session in about five minutes, then shows the parts that make it more than a CLI.
Start in your project folder
Section titled “Start in your project folder”Run marina in a project and describe a task. The default runtime is Marina’s coding
agent, using your configured provider. You can also use an installed coding tool and
its existing credentials through the same terminal:
marina --agent claudemarina --agent codexmarina --agent pimarina --agent marina --model openrouter/<model-id> --profile claudeRuntime and command dialect are independent: --agent claude runs Claude Code’s native
tools; --agent marina --profile claude uses Marina’s tools with its Claude-style command
aliases. --model selects a model understood by the chosen runtime. Native agents keep
their own configuration and permission systems; Marina’s --allow-exec flags apply
only to Marina’s own Code Mode commands. There is no automatic provider failover.
For the fullscreen workspace, run marina --tui (or add --tui to marina attach
or a connected --url … --name … --session … invocation). It keeps the project location, active
conversation, task status and unread counts visible around the transcript and composer.
Coding remains a single session; World messages and independent agents continue alongside it.
Omit --tui to use the scrollback terminal described below.
At 110 columns or wider, Coding and World appear side by side. F6 changes which conversation receives your input; the marked pane header and composer label identify the destination. F7 opens Requests in the left pane while World stays visible; F8 does the same for your panel. Incoming requests never take over your draft. Each pane retains its scroll position, and each conversation retains its draft when focus or terminal size changes.
Use F2 to cycle layouts, or /layout auto, /layout focus, and /layout split to choose
directly. Explicit split needs at least 80 columns and enough height for readable panes;
smaller windows fall back to the focused view. Resizing back restores the selected arrangement.
Unread counts track activity outside your focused conversation even when its pane is visible.
These controls only arrange views; they do not spawn workers or change coding sessions.
In the workspace, type / for command suggestions with descriptions. Up/Down selects;
Tab or Enter inserts the selection into your draft, and a separate Enter sends it.
Argument hints follow the selected command. Paste inserts a multiline draft without sending;
Shift+Enter (where supported) or a trailing \ adds another line. F1 opens help, F6 switches
Coding/World, F7 opens pending requests, and F8 opens the published panel view.
PageUp/PageDown scroll the focused pane;
Alt+Up/Down or /view older and /view newer fetch another retained local page. /view latest
jumps back to recent output and follows new updates in the focused conversation. Ctrl+D exits
an empty composer. Each conversation retains its editor, cursor and history; cancelled
requests discard their partial answers. The workspace restores the shell on exit without
copying unsent drafts into scrollback. It requires interactive input and output; one-shot
-p and redirected output keep their existing stream behavior. printf "…" | marina -p reads
the one-shot task from stdin for shell composition. NO_COLOR=1 disables accents.
The first screen is short: the Marina version, the folder, the agent and its model, whether the
session is new or resumed, and a one-line hint. With no provider key, one line names the
missing key and offers marina init. --verbose adds the database, the server endpoints and
the federation address; /status and /project show the rest whenever you want them.
Inside the terminal, /help shows one screen of essentials and /help all lists every control;
Tab completes their names and the /verify and /review actions. /quit, /exit and Ctrl+D
leave. Streaming
output preserves the current draft and cursor, including wrapped lines and terminal resizing.
The prompt names the folder and what the agent is doing (myapp · working ›), and adds what
is waiting only when something is (myapp · ready · 1 request · World 3 ›); transcript labels distinguish world messages, check
receipts, results, submitted work and review. Permission details appear above a short answer
prompt. End a line with \ to continue a task on another line.
In an interactive terminal, Coding is the initial view. F6 switches between Coding and
World, preserving each view’s draft, cursor, multiline input and command history. The
prompt counts unread output in the other conversation (World 3, Coding 2). Use /view coding or
/view world when function keys are unavailable. World input uses normal Marina commands;
/world <command> also works from either conversation. Incoming world events and coding
output continue to arrive while you focus elsewhere.
F7 or /view approvals opens pending permission questions deliberately; a new question
does not replace your draft or turn a world message into an answer. /view older and
/view newer browse bounded local transcript pages. Eviction and excerpt notices identify
missing history; these pages are not a durable server transcript. Redirected output remains
one continuous plain-text stream.
For a local Git project with a Marina worker, /task <request> requires current candidate
verification before the worker can submit its result. Start with a small change, for example
/task Fix the pagination boundary and add a regression test. Early summaries remain progress;
blockers are reported explicitly. Ordinary freeform requests retain their existing behavior.
Interactive Marina sessions inspect the project at startup. /project repeats this inspection:
workspace and execution location, selected model, root instruction sources, Git state, and the
effective verification recipe (including a saved default recipe). It does not call the model,
run the recipe, install dependencies, or recruit a worker. A whitespace-only recipe is flagged;
configure meaningful checks with, for example, /world code recipe save default typecheck then test.
The report is an observation, not proof that a provider is reachable or permission to execute.
Connected mode inspects the server workspace; a sandbox report identifies host-only observations.
For the selected Marina session, /status shows the task, /diff shows working changes,
/verify starts candidate checks, and /review inspects the latest attempt and its evidence.
/checks lists recorded verification results; /status shows checks still in progress.
/history lists recent task attempts, and /show <artifact-id> opens a recorded artifact.
These are bounded server listings; local transcript pages are available through /view older.
The session’s day-to-day controls are one word away:
/model <id> switch Marina's model for this session/fast cheapest reachable model across vendors/plan <request> research and propose, write nothing/compact compact the conversation now/cost (or /usage) today's model spend against the daily cap/continue resume the most recent session in this folder/memory [save <line> | recall <query>] remember and recall the folder/model switches the active session model (list every model you hold a provider key for with no
argument); /fast picks the cheapest reachable one. /plan runs the worker read-only — it
researches and emits a proposal without editing files. /compact compacts older conversation
messages even below the automatic threshold, retaining the initial request and recent turns.
It waits for durable archival before replacing history; if the agent is working or the conversation
changes during compaction, it keeps the live history and asks you to retry. Short histories stay
unchanged. Summary calls count toward agent usage and spend limits; a provider failure or reached
cap uses the deterministic summary. /cost and /usage report today’s model spend against the daily cap.
/continue resumes the most recent session whose workspace is this folder. /memory save <line>
teaches the world about this folder (a note) and /memory recalls it; /memory recall <query>
retrieves scored notes.
Diffs are rendered by the terminal from the structured result, not from the server’s text. A
per-file stat comes first (3 files · +42 −7, then one line per file), followed by the hunks:
added lines green, removed lines red, @@ hunk headers cyan and file headers bold. Colour is
added only on an interactive terminal and never when NO_COLOR is set; redirected output stays
plain. A diff entry is kept whole in local history up to the server’s 64 KB limit. When the
server cuts a larger diff, the terminal says so, with the bytes shown versus the total, and
suggests code diff <path> to read one file in full. In marina --tui, /diff also opens a
diff view: n/p jump between files, ]/[ between hunks, arrows and PageUp/PageDown scroll,
and q (or Escape) returns to the conversation with your draft unchanged.
Complete an ordinary verified task from this terminal:
/project/task Fix the pagination boundary and add a regression test/status/diff/checks/review/review approve <attempt-id>Copy the attempt ID from the review output. /review reject <attempt-id> rejects that submission;
/review <attempt-id> inspects a historical attempt. Decisions require an explicit ID and use
Marina’s existing ownership checks. Approval rechecks source freshness, so a direct filesystem
edit after inspection can withhold approval. Approval records the canonical task decision; it
does not commit, merge, or push files. The prompt distinguishes approval, rejection, and explicit
unverified acceptance. Unverified acceptance remains the deliberate owner command
/world code review accept-unverified <attempt-id> <reason> and never marks checks as passed.
/verify live starts background checks in the live workspace; those results remain unbound to
an immutable candidate. For a Bun project needing dependencies, /verify candidate dependencies:bun
explicitly prepares the captured lockfile inside the candidate with lifecycle scripts disabled.
There is no automatic install; a project whose environment is not ready reports not_run. These shortcuts use the same server commands and permissions as other
interfaces. Native runtimes retain their own tools. Redirected output stays plain text.
The prompt also shows worker pauses, budget exhaustion and upstream recovery when reported
by the selected Marina worker. /status includes the reason; use the suggested agent status
command to inspect its limits. A pause is not a completed task, and switching views does not
raise budgets, resume workers or change task ownership.
Use /world <command> while a runtime is launching or a permission question is pending
to send a message, inspect the world, or participate in another task. World input does
not answer or dismiss a permission question, and your unfinished coding draft is kept.
Coding input stays ordered behind setup. World commands still use normal server admission:
a long synchronous command already running as your resident can delay subsequent commands.
Use code verify start for finite local checks that return a receipt and leave your resident
free to participate while they run.
Approval prompts require both terminal input and visible terminal output; redirected or
closed input/output never counts as consent.
/agents/spawn codex reviewer/use reviewerReview the current implementation; send your findings to the other participant through Marina./use claude/world task list/dashboard/use switches to a launched agent or starts an installed runtime. The first native
worker uses your project folder. Additional workers use isolated Git worktrees starting
at committed HEAD; uncommitted source edits are not copied. /spawn always requests
a worktree and reports an error if it cannot create one. Agent output is labeled in the
terminal and recorded through Marina’s existing participant routing. There is no fixed
three-agent limit. /agents lists this terminal’s managed roster; independently joined
participants remain discoverable through marina route and the browser Streams view.
When a native agent asks to change a file (Claude Edit, MultiEdit or Write, or a Codex or
pi patch request), the terminal shows a unified diff against the current file, with its stat,
before Allow? [y/N]. If the text to replace is not in the current file, it shows the requested
replacement and says the file could not be compared. Other requests still show their JSON input.
The answer means the same thing either way: only y or yes allows.
/dashboard opens an authenticated Streams workspace with output replay, agent controls,
permissions and delivery history, without reconnecting the terminal’s chat session.
The browser consumes the credential from the URL fragment into tab storage and removes
the fragment immediately. “Disconnect this view” clears that tab’s credential. The
normal dashboard remains available at the printed Dashboard URL.
Use /stop or Ctrl+C to interrupt the selected worker. A second Ctrl+C or /quit stops
all native processes owned by this terminal and closes its local Marina. Worktrees and
the output journal remain available for inspection. Restarting preserves history but
does not replay uncertain work or resume a native process automatically. Run /agents to
restore the recorded roster, inspect its history and workspace, then /resume <exact-name-or-id>
to reconnect a stopped Claude, Codex or pi session. Recovery preserves its directory and model, refuses
missing or changed identities, and sends no prior task again. The dashboard Streams controls offer
the same action. Claude reports which conversation it opened only after it reads the first new
prompt, so a Claude resume is confirmed in two steps: Marina refuses to start unless Claude has a
recorded session with that id in that directory, and it denies every tool until Claude reports the
recorded id. If Claude reports a different conversation, Marina interrupts it and marks the run
failed with Claude did not resume the recorded session. The remaining risk is that this first
prompt’s text reaches the opened conversation before confirmation; no tool can run on it.
Complete names from your current work
Section titled “Complete names from your current work”After /world code files [directory], Tab suggests the displayed paths for
/world code read <path> and code read <path> in the World pane. /checks and
/history populate /show <artifact> and /review <attempt> hints. Observed native
participants appear under /use and supported disconnected sessions under /resume.
Both terminal renderers use these same local hints; the TUI shows a dropdown.
Suggestions only edit the draft. They do not scan the filesystem, fetch more data, run a command or approve work. File and artifact hints clear when you switch coding sessions; approval answers have no command completion. Refresh a listing if its contents changed.
Keep a Coding desk beside your work
Section titled “Keep a Coding desk beside your work”In the dashboard, select an existing session in Work to open a personal desk. Its repository may be Marina itself or any external project. To publish a shared desk, choose Workspace → Canvas → Published panels → Create coding desk. Select an existing Marina coding session; optionally attach a task and a visible participant. Publish and open desk opens its activity, artifacts, recorded verification, request composer and world feed beside Chat. Each coding request is reviewed and targets that exact session, without changing Chat’s selected session. Native agents can be followed through participant resources and Streams; a native participant ID is not a Marina coding session ID.
The same publication is usable from the coding terminal:
/panel list/panel desk/panel views/panel publish <canvas-id>/view panel/panel desk [session-id] opens a personal view of the current or explicit session without
publishing it. /panel publish <canvas-id> [session-id] explicitly publishes a shared desk;
/panel list supplies canvas IDs. Open a publication with /panel open <canvas-id> <node-id>.
Up to four open views keep independent drafts and captured reviews. /panel use <number> or
Alt+Left / Alt+Right in the TUI panel form switches views; /panel close closes only the current view.
In --tui, F8 focuses the panel. Tab/Shift+Tab selects fields and buttons; type into a field,
use Space for a checkbox, and Enter to review an action. Review starts on Cancel; select
Confirm to submit, or Escape to dismiss it. Scrollback mode uses /panel field,
/panel act and /panel confirm; see Published panels.
Live updates, view switching and reconnects preserve drafts within the open application. Closing a panel only closes that view; it never stops the worker. Saved browser layouts restore references and geometry, not unsent drafts across reloads. Terminal exit still follows the process ownership rules above. Other conversations remain active throughout.
Remember and share a harness
Section titled “Remember and share a harness”/harness save daily/harness list/harness exportSaving makes this runtime/model/dialect selection the default for subsequent bare
marina launches in the same folder. Preferences live beside the folder’s database in
~/.marina/projects/<slug>/harnesses.json. Explicit --agent overrides the saved default.
Export prints a portable, versioned JSON definition, for example:
{"version":1,"agent":"marina","model":"openrouter/<model-id>","profile":"claude"}Save that JSON to a file and use marina --harness ./daily.json, or select a personal
definition with marina --harness daily. /harness use <name-or-path> selects one inside
the terminal. Harness definitions in a repository are never loaded implicitly. These definitions
select a runtime, model and dialect; they do not yet package teams, Scores, roles,
credentials or approval policies. Changing a bound Marina worker’s model takes effect
between tasks; stop or finish active work first.
Native runtimes also support marina --agent codex -p "<task>". Marina does not verify a native
turn, so a finished turn exits 3 (“Native turn finished; Marina did not verify it”);
--allow-unverified makes it 0. Neither means Marina approved the work.
Missing terminal input denies native permission requests. Marina-native tasks retain
the canonical task/submission/review workflow described below.
Project instructions
Section titled “Project instructions”Marina workers receive CLAUDE.md, AGENTS.md and .marina.md from the effective workspace
root when assigned work. code files <directory> and code read <path> refresh instructions
along that path, from the root to the inspected directory. Deeper instructions apply within
their subtree; same-directory documents retain their stated precedence. Workers are prompted
to inspect a path before editing it. This is guidance, not an additional write permission gate.
Each delivery identifies its sources, scope, loaded byte count and any omissions or read errors. Assignment artifacts and inspection events record source metadata and SHA-256 hashes of the raw source excerpts. Automatic loading is bounded to 4 KiB per file, 16 KiB in total and 32 directory levels. Read truncated or omitted files explicitly before relying on them. Instructions are refreshed from disk on inspection, so subsequent edits can change the guidance. Symlinks and nonregular instruction files are refused.
For internal workers, exact repeated instruction excerpts are represented once in the retained conversation. Later reads keep a reference and their original source details. If compaction removes the full copy, a surviving reference restores it; changed or differently scoped rules are delivered in full. Raw inspections and the durable journal remain available. This avoids paying to resend the same conventions on every file read without treating old delivery as proof that the model still has them.
Use code search <query> path:<relative-path> to limit a search to one file or directory.
The marina_code and typed search tools expose the same path field.
For a path with spaces, use code search path:"src/my folder" -- <query>. Use -- before a query
that contains a literal modifier, for example code search path:src -- path:literal.
Worktree sessions use the worktree’s instructions. Sandbox sessions report that host instructions were not loaded; inspect the actual execution workspace. Repository instructions never grant execution permission. Native external runtimes keep their own instruction loaders.
Code inside an existing world
Section titled “Code inside an existing world”Use connected coding when your Marina already contains conversations, agents and ongoing work. Authenticate as your own resident, select a workspace configured on that server, and start a session:
marina connect Owner --url ws://localhost:3300code workspace use /srv/projects/my-projectcode start Repair the applicationDisconnect that terminal with Ctrl+D, then attach the coding terminal:
marina attach ws://localhost:3300marina attach [url] [--name <account>] [--session <id>] [--tui] fills in what you leave
out:
| Piece | Resolution |
|---|---|
| Server | The argument, then MARINA_URL, then the server of your only cached identity. |
| Account | --name, then the only identity cached for that server in ~/.marina/sessions/. Several: a numbered choice in a terminal, otherwise an error that names them. |
| Credential | MARINA_TOKEN, then the cached credential. None: in a terminal, the marina connect <name> login runs first; otherwise attach stops and asks you to authenticate. |
| Session | --session, then your active coding sessions on that server (code list). One is attached directly; several give a numbered choice in a terminal, otherwise an error listing them. |
With no active session, attach stops with
No active coding sessions for <name> on <url>. Start one there with: marina <folder>.
Attach only chooses among identities and sessions you already have. It never creates one.
The explicit form still works, and MARINA_URL stands in for --url:
marina --url ws://localhost:3300 --name Owner --session code_<id>If a piece is missing, the error names exactly what is missing and suggests marina attach.
Attaching reconnects the same resident using its cached credential for that server; an explicit
MARINA_TOKEN also works. An invalid credential or inaccessible session is refused. It
does not create a replacement identity or world. Reconnection rotates and privately saves
the credential, including when a later session check refuses the attach. A resident can
have one controlling connection: disconnect its existing chat/CLI before attaching.
Describe a task in ordinary language. The server’s coding agent uses the session’s existing
model, dialect, permissions and workspace. The printed Server workspace path belongs
to that server; files on your terminal’s machine are not synchronized. Configure provider
credentials and allowed workspace roots on the server. Task dispatch still requires
code.exec, and launching a new worker requires agent.spawn.
Fix the failing parser test, run the checks, and summarize the changes./world tell Reviewer I am working on the parser/world channel send project Verification is running/world task listverify startWorld messages and worker output continue while you work. /world uses normal command
admission; background verification releases the command slot while finite checks run.
Closing with /quit or Ctrl+D disconnects this view and leaves the world, agents and tasks
running. /stop explicitly interrupts the selected coding worker. Reattaching restores
its output stream and task indicator without replaying work. After a network failure,
inspect status, history and artifacts before retrying an uncertain command.
Automatic recruitment only considers idle coding agents without a goal, focus, pending perceptions, task claim, active coding run or crew membership. An existing bound worker continues to receive its session’s steering; other autonomous workers keep their work. Additional workers remain an explicit or permission-gated choice.
Connected coding currently supports Marina’s server-side worker in an interactive terminal.
Local native runtimes (/use codex, /spawn claude, etc.) require a separate workspace
bridge and are refused here. Use the folder launcher for those runtimes. Simultaneous
controlling views of one resident and connected one-shot mode are not yet supported.
Address a session without selecting it
Section titled “Address a session without selecting it”SDK clients can direct an operation to an existing coding session without changing the resident’s selected session, modal input mode, or saved coding context:
await client.command("code status", { codingTarget: { sessionId } });await client.command("code observe reproduced the failure", { codingTarget: { sessionId, runId }, signal: controller.signal,});sessionId is required. The caller must be the session creator or its bound coding agent,
matching code resume authority; holding its writer lock alone does not grant access.
Existing write locks, competence gates, workspace boundaries and transport restrictions
still apply. An optional runId is an execution precondition: it must still identify that
session’s active attempt when the command starts. It is not a historical query selector.
A rejected or uncertain request must be inspected before retrying.
Targeting currently supports session inspection (status, history, artifacts, show,
thread, patches, review), owner dispatch (do, assign), evidence
(plan, decision, observe, summary, blocked, assume), and workspace
operations (files, read, search, diff, patch, propose, apply, reject, edit,
write, checkpoint, revert, verify, run, test, lint, typecheck, build,
dashboard:build, recipe). Use explicit code … input. Conflicting session IDs, selection changes,
settings, separate recruitment/lifecycle operations, macros and room overrides are refused when
a target is supplied. Commands without a target keep their existing behavior.
The WebSocket envelope carries coding_target: { sessionId, runId? }; successful login
and authentication advertise codingTargetProtocol: "session-run-v1". The SDK refuses
to send a targeted command if that support is absent, even on a server that supports
correlated command completion. Gate challenges and held shell-execution approvals retain
the target for replay through the normal bounded command queue. A shell approval for one
execution cannot authorize another request or survive a stale/rejected replay. Explicit
session-wide shell approvals retain their broader scope. Late results and approval audit
records retain the attempt captured when execution began; replay output arrives as a world
event, separate from the command that granted approval.
code exec-mode off revokes pending shell approvals and enforces allowlist-only execution,
including under the local profile. Session exec-mode overrides last until server restart.
This adds addressing to the existing command path. Commands from one participant still execute in FIFO order; it does not add a second execution lane or change world lifetime. Other participants and incoming world perceptions remain independent.
First autonomous fix (copy and paste)
Section titled “First autonomous fix (copy and paste)”This path uses a disposable, intentionally broken TypeScript project included with Marina. It is the shortest way to see the coding agent inspect code, change it, run checks, and report evidence without risking one of your repositories.
1. Prepare the demo
Section titled “1. Prepare the demo”From the Marina repository root:
rm -rf /tmp/marina-coding-agent-democp -R examples/coding-agent-demo /tmp/marina-coding-agent-democd /tmp/marina-coding-agent-demobun installgit initgit add .git -c user.name="Marina Demo" -c user.email="demo@localhost" commit -m "demo baseline"bun testThe last command should fail. That failure is the agent’s starting point.
2. Provide one model key
Section titled “2. Provide one model key”The folder-scoped launcher uses provider keys from the shell that starts it. Choose one provider; do not paste a real key into a command you intend to save or share:
export ANTHROPIC_API_KEY="your-key-here"# Automatic model selection also supports OPENAI_API_KEY, GEMINI_API_KEY,# GROQ_API_KEY, and OPENROUTER_API_KEY.For another provider or a local model, also export an explicit routable model such as
MARINA_DEFAULT_MODEL=provider/model-id; see Configuration. A key previously
saved in a different Marina database is not copied into this disposable folder-scoped session.
3. Launch Marina against only that folder
Section titled “3. Launch Marina against only that folder”Return to the Marina repository and pass the demo path explicitly:
cd /path/to/marinabun run code /tmp/marina-coding-agent-demoWait for the » prompt, then paste this task exactly:
Fix the percentage-discount bug. Treat percent as a value from 0 through 100, reject invalid cents or percent inputs, add regression tests for boundaries and invalid inputs, then run the test and typecheck scripts. Do not change dependencies.Marina creates a temporary local world, binds one coding agent to the demo directory, and streams
its progress. The expected lifecycle is received → inspect → plan → patch → apply → verify → complete. The agent may phrase its messages differently, but a completion must identify changed
paths and successful checks. It must not claim success from a proposed patch alone.
4. Inspect or steer while it works
Section titled “4. Inspect or steer while it works”At the same » prompt, these are safe to paste:
statusdiffshow last patchfocus on input validation before changing anything elseBecause this launcher is already in Code Mode, omit the code prefix. A plain sentence is steering
or a new task; status, diff, and show are commands. Use Ctrl-C to stop the launcher. The
session’s database persists per folder (see below), so relaunching resumes where you left off;
edits in the demo folder remain available to inspect. Pass --fresh for the old throwaway
behavior.
5. Verify independently
Section titled “5. Verify independently”Do not rely only on the agent’s summary:
cd /tmp/marina-coding-agent-demobun testbun run typecheckgit diff --no-index \ /path/to/marina/examples/coding-agent-demo \ /tmp/marina-coding-agent-demo || trueBoth checks should pass. The diff should show changes limited to the copied demo. Delete the copy when finished:
rm -rf /tmp/marina-coding-agent-demoIf it does not start
Section titled “If it does not start”- “No model provider is configured”: the server found no provider key. Put one in the Marina
repository’s
.env(or runmarina init), or export it in the same shell, then relaunch. A native agent (/use claude,/use codex,/use pi) needs no Marina key. - Server timeout: run
bun installin the Marina repository, confirm Bun is at least 1.4.2, and retry. - “Marina is already open in this folder”: another terminal has this folder’s session open
(the message names its process). Close it, or use
--freshfor a separate disposable session. A leftover record from a crashed session is detected and cleared automatically. - Stale project database: a per-folder DB written by an older Marina version can block boot.
The failure hint prints the exact path (
~/.marina/projects/<slug>/marina.db) — remove it, or relaunch with--freshto use a throwaway DB. - Agent cannot run checks: use the folder-scoped launcher above; it boots
coderas the local operator. In a shared Marina,code.execremains safety-gated. - Wrong files appear: exit immediately and relaunch with the explicit absolute demo path. The
first screen names the folder (
--verboseprints its full path);/statusshows the workspace Marina is confined to. Verify it before sending the task.
Once this works, replace the demo path with a clean branch or disposable worktree of your own project. Keep the task bounded and name the checks that define completion.
Where it runs today: every coding session has an explicit execution target. The default is a real host workspace behind a safe allowlist (
bunscripts such astest/lint/typecheck/build, plus a constrainedgitsubset). When the operator configures Flywheel, an entity can create one durable isolated sandbox, materialize guest projects, run open finite commands there, and manage guest services. Selection is per session and never falls back silently to the host. See Optional isolated execution with Flywheel.Running or applying code is an earned capability. Reading, searching, diffing, and proposing patches are open to everyone, but
code run/verify/testandcode apply/revertrequire thecode.execsafety gate (low bar — standing 3; operators are granted it) so a freshly-spawned, untrusted agent can’t execute arbitrary host code. Ifcode runis refused, you haven’t earnedcode.execyet: the run is held, and once your owner or an operator approves it, it runs — three approvals from outside your own lineage make the gate yours — or an operator grants it. UnderMARINA_AUTONOMY=earnedsupervised attempts run freely pending an operator’s count; underopenthis gate auto-passes (only the operator-grant core stays gated).
TL;DR — just talk to it (like Codex / Claude Code / Cursor)
Section titled “TL;DR — just talk to it (like Codex / Claude Code / Cursor)”Zero-config, in any folder — boots a folder-scoped Marina and drops you straight into agentic Code Mode (needs an LLM provider key in your env):
bun run code # the current directorybun run code ~/projects/acme # …or any directory# It evaluates the directory, then you say what you want, in plain English:» fix the off-by-one in the tokenizer and add a regression testSessions persist per folder. Each directory gets its own Marina database at
~/.marina/projects/<slug>/marina.db (slug = folder basename + a short hash of the absolute
path), so sessions, artifacts, and agent memory accrete across launches — relaunching in the same
folder prints Resuming session <id> — started <age>, workspace <path> and picks up where the
last run left off. Pass --fresh (or set MARINA_CODE_FRESH=1) for the old behavior: a
throwaway database, deleted on exit. Only the ephemeral DB is ever deleted; the per-folder one is
yours to keep or remove.
One-shot mode (marina -p "<task>" [dir], alias --print) dispatches a single task
non-interactively: it boots (persistent DB by default, so -p runs accrete history), streams the
agent’s work as usual, then waits for the structured completion signal. On completion it prints
the session diff and the agent’s summary. The exit code says what you can rely on:
| Code | Meaning |
|---|---|
0 |
completed, and the run’s checks passed (verification passed; under delivery, every stated requirement observed by a passing check or declared not machine-checkable) |
3 |
completed but not verified: no checks ran, they failed, the source changed after them, or (under delivery) a stated requirement was left unobserved. The reason is printed on stderr. --allow-unverified makes this 0 (the notice still prints). |
1 |
failed: the agent died mid-task, the run was stopped, or it ended blocked (the reason is printed; edits it made stay in the folder and are listed) |
2 |
timed out after MARINA_CODE_TASK_TIMEOUT_MS (default 600000 ms); code stop was sent |
--verification <mode> sets what a one-shot task must verify before it can submit: checks
(the project’s own checks), candidate (a captured Git snapshot), or delivery (default, or
MARINA_CODE_VERIFICATION — a JSON manifest of output files and checks, run alone in a clean
directory with code verify delivery manifest:<path>). The mode is the task’s code do verification:<mode>
requirement; agents still choose the implementation and the checks.
Time budget. A one-shot hands its coder a budget slightly inside MARINA_CODE_TASK_TIMEOUT_MS
(code do budget:<duration>, the shared duration grammar). The coder sees its deadline, is told to
converge at 75 % of the time, and, a reserve before the deadline (a tenth of the budget, 15 s to
2 min), may only submit (marina_code summary) or report (blocked / assume). A submission
after that point is the forced best effort: it is submitted with whatever verification it has and
labelled budgetForced ({"reason":"deadline","used":…,"cap":…} in --json, and in the task’s
evidence). It is never reported as verified unless its checks passed.
Spend stop. When the model budget runs out mid-task (the daily or scope spend cap, or an
upstream refusing for money), no model can be asked to finish. The attempt ends anyway: if the
coder changed the workspace, Marina submits that work for review. It uses a summary assembled
without a model, keeps the recorded verification and adds the label
budgetForced ({"reason":"spend",…}). If nothing changed, the attempt ends blocked with the same
label. The submission verifier is not asked.
Driver. --driver crew runs the one-shot with the crew driver. A task with a contract
(required verification, owner:unattended, a budget) stays ONE tracked attempt: a lead coder owns
it, keeps the write lock and submits it under the contract, and a crew assembled around it reviews,
tests and reports to the lead. The crew’s roster is recorded on the attempt, so when the lead
cannot submit, a member may submit it for the crew with code summary submit:crew -- <summary>.
That submission takes the same path as the lead’s: the same required checks and budget-forced
label, the lead’s task claim, and still one attempt (a later summary from anyone does not submit
again). It is attributed to the member (submittedBy on the attempt, a Submitted by: line in
the task evidence, the member as actor of the submit event), and the lead is notified. Only a
member on both the recorded roster and the live crew may do this: an outsider, a removed member
or a dissolved crew is refused, and a member’s plain code summary stays progress. Without a
contract, code do on a crew session still stores and dispatches a crew plan.
--json prints one JSON object as the last line of stdout and sends everything else to stderr:
{"status":"verified|unverified|failed|timeout","exitCode":…,"verification":…,"verificationReason":…,"requirements":…,"reason":…,"assumptions":[{"text":…,"source":"worker|block"}],"budgetForced":…,"taskId":…,"runId":…,"files":[{"path":…,"added":…,"removed":…}],"durationMs":…}.
files is what the run changed in the session, also when it failed or ended blocked; reason is
the full reason a run failed (null otherwise); assumptions lists the interpretations made
where no person could answer (below). The human-readable output prints the same assumptions
after the summary.
With --verification delivery, requirements is {"listed":…,"verified":[…],"unverified":[…],"notCheckable":[{"text":…,"reason":…}]}
(otherwise null). A run whose checks passed but left a stated requirement unverified, recorded
no checklist, or declared every requirement not machine-checkable is unverified (exit 3) and
the notice names those requirements. A requirement declared not machine-checkable beside observed
ones does not change the status: the run is verified (exit 0), the requirement stays under
notCheckable (never verified) and stderr names it. status and exitCode in the object are
always the process’s own.
marina -p "fix the off-by-one in the tokenizer and add a regression test" ~/projects/acmeecho $? # 0 verified · 3 completed, not verified · 1 failed · 2 timed outmarina -p "…" ~/projects/acme --json | tail -1 | jq .statusOr inside an already-running Marina:
bun run startbun run scripts/connect.ts coder
# Enter Code Mode — it evaluates the current directory and waits for a task:code# Then say what you want, in plain English:fix the off-by-one in the tokenizer and add a regression testEntering code binds a coding agent to your workspace. Type a natural-language
task and it works autonomously — explores the repo, edits via reviewable patches,
runs the test/lint chain, and iterates — streaming its progress back. Type again to
steer it; code status to watch. This is the single-agent driver (the default).
The default coding agent follows an observable operating contract: received → inspect → plan →
patch → apply → verify → submit → review. Dispatch through the single-agent driver or code assign
creates a canonical Marina task and a durable attempt artifact. Follow-up instructions steer the
same active attempt. Its tool events, changes, checks and summary carry the attempt and task IDs.
A worker can hold one active coding attempt at a time.
code summary submits the worker’s task after storing its summary. Calling the tool or writing
an operator note does not complete the task. Candidate verification is reported as passed,
failed, missing, stale or unavailable. Submission and code review read actual included source
bytes, so edits made outside Marina also make evidence stale. Live-workspace checks are reported
as unbound: their output remains useful, but does not certify an immutable candidate. Only
task review marks the canonical task completed:
code review # inspect the latest attempt, summary and verificationcode review approve # accept its submitted task (task creator only)code review reject # return its task to open workcode review <attempt-id> # inspect an older attemptThe dashboard renders the same task/evidence links and review actions. Ordinary task info,
task approve and task reject still operate on the same task. A one-shot exit 0 means the
worker stored and submitted its result; it does not mean the operator approved it or that every
check passed. Check the reported verification and review evidence before accepting changes.
code review approve withholds approval of candidate-bound work unless its checks still pass
for the observed source. Inspect stale work and reverify in a new attempt. Ordinary task approve
remains a manual task decision; it does not refresh evidence or mark changed source verified.
Freshness is an observation at the displayed time, not a continuous lock on external writers.
For a task that must have current candidate evidence before submission, use:
/task Fix the pagination boundary and add a regression test# Equivalent world commands:code do verification:candidate -- Fix the pagination boundary and add a regression testcode assign alice verification:candidate -- Fix the pagination boundary/task applies to Marina workers in local Git workspaces with the single-agent driver.
Ordinary freeform dispatch retains its existing behavior. The owner sets the requirement
when the attempt begins; steering preserves it. An early summary is saved as progress,
keeps the same task active, and returns the missing step. Pending, failed, stale, unavailable,
or live-workspace checks cannot satisfy the requirement. The worker inspects the candidate
receipt/result, resolves failures, and submits another summary. The terminal shows server
states: verification required, checks running, needs attention, or ready for review. Source
freshness is reassessed at status, submission, and review; readiness is an observation.
The configured check recipe determines what is tested: a whitespace-only fallback does not
establish functional correctness. Use a project test recipe for a meaningful completion check.
Workers keep the completion requirement and a link to the full task artifact ahead of any
abbreviated task reminder. Finish source and regression-test edits before verification;
background completion reports the observed candidate state, and any later edit requires fresh
checks. Coding note tools use single-line text; exact replacements use edit with
oldText/newText, while a new file uses write with content.
If work cannot proceed, code blocked [kind:decision|impossible] <reason> saves a handoff,
interrupts this attempt, and releases its claim (kind defaults to decision). It preserves edits and evidence. Configured worker budgets still apply;
this requirement adds no retry scheduler or automatic dependency installation. An active task
continues until it submits, reports a blocker, reaches its configured limits, or is stopped.
An owner can deliberately accept a saved worker summary with
code review accept-unverified <attempt-id> <reason>. This records who accepted the risk and
why; it never changes missing or failed verification to passed. Normal approval still requires
current evidence for a candidate-required task. World communication and other agents continue
while background checks run.
For generated artifacts and service work, dispatch with code do verification:checks -- <task>.
This requires passing checks from the same attempt and execution workspace, with no subsequent
recorded file change or command. A whitespace-only receipt is insufficient. Use code recipe save default <validation command> to check the requested output or read back the service state, then
code verify. Receipts distinguish tests, type checks and other checks. Passing live checks means
those checks passed at the recorded time; it does not claim immutable source or complete coverage.
Use verification:candidate when you require source-bound evidence.
For a package or set of files that must work away from the active workspace, use
code do verification:delivery -- <task>. The worker can choose its implementation and
checks; submission requires current delivery evidence. To verify manually, write a JSON
manifest such as delivery.json:
{ "files": ["report.ts", "helper.ts", "report.test.ts", "order.test.ts"], "checks": ["bun test report.test.ts", "bun test order.test.ts"], "requirements": [ { "text": "the report total is 11", "checks": [0] }, { "text": "items are listed in input order", "checks": ["bun test order.test.ts"] }, { "text": "the wording suits a non-technical reader", "notCheckable": "a judgement, not a property" } ]}requirements is the acceptance checklist: each explicit requirement of the task, in the
worker’s words, mapped to the checks (by 0-based index or exact text) that observe it. Each
check should measure the timing, ordering or output the requirement names, against the delivered
files. Restating a requirement in prose does not count. Only a mapped check that ran and passed
in the current delivery verification counts. Those checks are bound to the current files and the
hashed manifest, so mapping a requirement after the checks ran makes the evidence stale. The
receipt lists each requirement as verified, not machine-checkable (with its reason), or
UNVERIFIED.
Enforcement under verification:delivery: a submission whose checks passed but which leaves a
requirement unverified, or has no checklist at all, is refused once. The summary is kept as
progress, and the reply lists exactly which requirements lack an observing check. The worker can
add and map checks, or mark a requirement notCheckable with a reason; that marking is recorded
and shown, never counted as verified. The next submission proceeds (bounded, never a loop). Any
requirement still unverified is written into the submitted evidence and the terminal lifecycle
requirements field. marina -p reports such a run as unverified (exit 3). That still
permits submission for owner review; --allow-unverified permits exit 0 while retaining the
unverified status. A requirement explicitly marked not machine-checkable (a prohibition such as
“do not read /tests”, which no check over delivered files can observe) is accepted by the same
gate and does not by itself make a run unverified: when the checks passed and every other
requirement was observed, marina -p exits 0 and names the not-machine-checkable ones on
stderr. A checklist with no observed requirement at all stays unverified. Other verification
modes are unchanged.
Run code verify delivery manifest:delivery.json, then inspect the returned receipt with
code show <id>. Marina copies only the listed regular files into a clean directory and
runs those finite commands using the session’s configured host or mount-sync container runner. Missing
companion files therefore cannot be supplied accidentally by the active workspace. Checks use
the runner’s environment and the selected dependency preparation; this is file isolation, not a new security sandbox or a
guarantee that every recipient has the same dependencies. Files changed by the checks or after
verification require fresh evidence before submission. The manifest itself is also hashed.
Delivery mode supports local file workspaces, requires explicit relative file paths (no globs,
directories, symlinks or .git). Include the files needed by both the deliverable and its checks,
including dependency manifests and lockfiles. dependencies:check (the default) diagnoses a
missing environment without installing; dependencies:auto prepares locked dependencies in
the disposable snapshot. On the host, supported locks are Bun text locks and npm version 3
package-lock.json, using public-registry packages without install scripts or inherited registry
credentials. Npm workspaces, local links, project .npmrc and shrinkwrap overrides are refused.
Container preparation follows the existing mount-sync and network policy; no host fallback occurs.
A manifest may contain up to 128 files. Capture and freshness checks stream files in 64 KiB
chunks, with default limits of 1 GiB per file and 4 GiB total. Operators can set positive byte
limits with MARINA_CODE_DELIVERY_MAX_FILE_BYTES and MARINA_CODE_DELIVERY_MAX_TOTAL_BYTES.
Background checks retain the normal execution gates
and finite-command allowlist. Patch-sync and unsupported targets are refused without host fallback.
Personal and published coding desks offer Review delivery check for an existing manifest. Opening a desk or its evidence does not execute checks. Chat and canvas show the same recorded file inventory and digests. Passing checks establishes their recorded scope; owner acceptance and the adequacy of test coverage remain separate. Whitespace-only checks cannot satisfy a required task verification contract, including candidate mode.
Unattended blocks become assumptions. On an owner:unattended attempt nobody can answer a
question, so a worker’s code blocked <reason> about a decision a person would normally make
(ambiguity, conflicting requirements, missing sign-off for an in-scope choice) does not end the
task. It is answered at once: the reply tells the worker to take the most defensible
interpretation, record it with code assume <interpretation>, and continue to delivery. The
block itself is recorded as an assumption too. Assumptions are assumption artifacts on the
attempt; they appear in the submitted receipt, the terminal lifecycle event and the -p result.
An answer is never authority: gates, permissions, dependency installation and the command policy
are unchanged, so the worker proceeds without anything it was not allowed and records what it
skipped. At most two blocks per attempt are answered this way; the next one ends the attempt with
Blocked: <reason> (after 2 automatic answers). code blocked kind:impossible <reason> ends it
at once, for what cannot be worked around (missing data or credentials, a broken environment).
Either way the edits already made stay in the workspace. Only the attempt’s worker gets automatic
answers; attended tasks keep the behavior above.
Marina’s one-shot -p path selects verification:checks owner:unattended and exits successfully
only after a matching attempt submits with current verification. Submission remains separate from
owner approval. Old completions from another attempt cannot finish the current task. The
owner:unattended contract also lets the designated active worker run code writer <its-name>
to reclaim a lock handed back to the owner. It cannot reclaim a collaborator’s lock, transfer the
lock to somebody else, or revive a stopped or expired attempt. A handoff without to: remains a
note. These are owner-selected contracts; agents choose their work and coordination methods.
code stop retains changes and ends the attempt as cancelled. Worker death records failure.
On server restart, unfinished attempts become interrupted and their claims are released;
Marina does not replay uncertain host actions. Inspect code review and artifacts before retrying.
Closing a coding session with code done requires its active task to be submitted or stopped.
The existing crew driver remains available; this task-attempt lifecycle currently covers the
single Marina worker and explicit assignment paths.
Want a team instead of one agent? code driver crew (or code crew <goal>) fans the
work out to an implementer / reviewer / tester. The driver is a seam — single today,
multi-agent / multi-backend as it grows.
The manual loop is still there
Section titled “The manual loop is still there”Every step the agent takes is a command you can drive yourself:
code start Fix the parser # open a coding sessioncode doctor # check the workspace is readycode files # look aroundcode read src/parser.ts # read a filecode run test # run the test suite (allowlisted)code patch Fix off-by-one # propose a diff (paste it on the next line)code apply last patch # apply the proposed patchcode checkpoint before-refactor # snapshot you can revert tocode done Fixed the parser # close the session with a summaryThat’s the whole loop. Everything below is detail and the good parts.
Your first session (5-minute walkthrough)
Section titled “Your first session (5-minute walkthrough)”1. Connect. Any surface works — the dashboard at http://localhost:3300, the compact web chat
at http://localhost:3300/chat,
telnet, the SDK, or the marina CLI:
bun run scripts/connect.ts coder2. Start a session. Code Mode opens against your workspace:
> code start Fix the parserCoding session started: code_8f3a1c2b-7deTitle: Fix the parserWorkspace: /home/you/projects/acmeTry: code files | code search <query> | code read <path> | code diff(Tip: bare code enters Code Mode, where you can drop the prefix — files, read <path>,
run test, exit. The explicit code <verb> form always works too.)
3. Check readiness. code doctor tells you exactly what’s available and what to fix:
> code doctorWorkspace: /home/you/projects/acme (git repo, clean)git: ok ripgrep: ok package manager: bunVerification: typecheck, lint, test detectedReady.4. Explore. Read-only and fast:
> code files src> code read src/parser.ts> code search off by one> code diff5. Run a check. In the default local target, allowlisted commands run in the host workspace. In an explicitly selected Flywheel target, finite guest commands run in the active sandbox project. Either way, normalized output and execution evidence are stored on the session:
> code run test$ bun test 412 pass 0 failexit 0 · 8.1scode verify runs the detected chain in one go, where the session runs (the host, or its
container runner):
- Prepare for the detected project type. A probe checks that the environment has what the
checks need (
python -m pytest --version,go version,node_modulesfor a package with dependencies or workspace links, and so on). A JavaScript installer never runs on a Python project, and the reverse. When the detected runner is pytest and the probe shows pytest is not importable (and nothing could install it), the tests run instead with the project’s own test script (tests/run_tests.py,tests/run_all.py,tests/run.py,test/run_tests.py,test/run_all.py, unless it imports pytest itself) orpython -m unittest(discovery, or the relevant test modules by dotted name). The report says so —runner: unittest (pytest unavailable), andtestRunnerin the artifact metadata — and such a run passes only when it reports tests ran (Ran N tests,N passed); zero tests or silence isnot_run.code testfollows the same switch. The fallback command runs through the same allowlist and approval as any other check. - Type-check, when the project configures it and the chain does not already run it:
tsc --noEmitfor atsconfig.json,mypyorpyrighton the changed Python files. - Checks and tests: typecheck → lint → test → build for JavaScript, the project’s test runner otherwise. Tests relevant to the change run first (see below).
Every verification ends in one of four states:
| State | Meaning |
|---|---|
passed |
the checks ran and passed |
failed |
the checks ran and at least one failed (a timed-out check counts as failed) |
not_run |
nothing could be checked: no tests were found, the runner is missing, or the environment is not ready. The reason is recorded. |
error |
the infrastructure failed: the container runtime could not start, or the pending diff did not apply |
not_run and error are never counted as a pass or a failure: not by task submission, not in
lessons, and not by the SWE-bench adapter’s counts. A task that requires candidate verification
stays active on not_run; report it with code blocked <reason>, or the owner may accept the
work unverified.
Changing source or tests invalidates earlier candidate verification. The submission feedback includes a retry command preserving the recorded verification options; finish your edits, review the options, rerun checks and inspect the completed receipt before submitting again.
Options (each also has an operator default, MARINA_CODE_VERIFY_*):
code verify scope:auto # default: relevant tests, else the full suitecode verify scope:changed # relevant tests only (not_run when there are none)code verify scope:full # the whole suitecode verify scope:changed+full budget:10m # relevant tests, then the full suite within 10 minutescode verify dependencies:check # default: probe the environment, never installcode verify dependencies:none # skip the probecode verify dependencies:auto # also install locked dependencies where that is isolatedcode verify typecheck:off # skip the configured type-checkWorkspace roots require preparation even when dependencies are declared only in child
packages. dependencies:check still never installs; dependencies:bun uses the locked,
isolated candidate installer with lifecycle scripts disabled. An operator can set
MARINA_CODE_VERIFY_DEPENDENCIES for a deployment’s normal policy; explicit command
modifiers override it. Use none only for checks known to need no installed dependencies.
Verification receipt IDs are artifacts, not files. Inspect them with code show <id>,
or marina_code with action=show and artifactId=<id>. A completed request displays
its linked result, including failed-check output and the full-output artifact pointer.
Relevant tests are the test files you changed, tests named after the files you changed
(test_parser.py, parser.test.ts, ParserTest.java), and tests that import them. Go runs the
packages you changed. JavaScript is scoped only when the test script’s runner accepts file
paths (Jest, Vitest, Mocha, bun test, node --test). Rust runs the whole suite. At most 25
test targets are selected.
Installing dependencies. dependencies:auto, or the project’s own manager
(dependencies:npm, uv, …), installs locked dependencies only where the install is isolated
and persists: a candidate or delivery snapshot with a Bun text lockfile or npm v3 lockfile on
the host (below), or a container runner
with sync:mount and network:on (npm ci --ignore-scripts, pnpm/yarn/bun install --frozen-lockfile --ignore-scripts, uv sync --frozen). Elsewhere, a missing environment is
reported as not_run with the reason. A sync:patch runner starts every command from its image,
so the image must already hold the environment. Python projects with requirements.txt can use
dependencies:pip: the container installs hash-locked binary wheels into
.venv/marina-site-packages, and checks use env PYTHONPATH=.venv/marina-site-packages python ….
Unhashed or source-only requirements fail preparation instead of falling back to unrestricted
installation. UV checks use the declared UV environment. Explicit installation modes reconcile
locked dependencies even if a test-runner probe succeeds; dependencies:check only probes.
Commands use argv, so a bare PYTHONPATH=… python … is not shell assignment syntax.
For a local workspace, code verify start starts that same chain and returns a durable
verification_request artifact immediately. Its command completion acknowledges admission;
it does not mean checks passed. Continue chatting or inspecting other sessions. Completion
arrives as a later world event; code show <receipt_id> displays the status, any error, and
the result artifact. Target the original session when inspecting it from another session.
The existing code verify and code recipe run still wait for their results.
Background verification is bounded to four workspaces per engine, one request per real
workspace path, and at most eight commands per recipe. It accepts only the existing local
command allowlist and requires unattended code.exec competence (or the local trust profile).
Interactive shell approvals, supervised checks, and Flywheel checks use foreground verification.
Session access, writer authority, execution target, transport, and the current coding attempt
are rechecked before each check process starts, including after waiting for the workspace lock.
Revocation stops subsequent processes; it does not undo or cancel one already executing.
Results remain attached to the original session and attempt. code verify and code verify start
use the live workspace and existing per-command workspace lock; they are unbound checks.
Graceful shutdown drains admitted checks before closing persistence,
subject to the server’s existing 30-second forced-shutdown watchdog.
After an interrupted process, unfinished receipts are marked interrupted with an unknown
execution outcome and are never replayed automatically. Inspect the workspace before retrying.
For immutable source evidence in a supported local Git repository, use code verify candidate.
This uses the same background admission and authority checks, with a separate source directory
and Git index. It captures actual tracked and untracked, non-ignored files into a Git tree using
an alternate index, without changing your index, staging choices, branch or HEAD. Tracked
deletions, executable modes, binary files and internal relative symlinks are represented.
Git stat caches and assume-unchanged flags cannot hide source edits. The Git working-tree root
must be the session root; sparse checkouts, unresolved merges, submodules, filters/LFS, encoding
or ident attributes, escaping symlinks and non-UTF-8/newline paths are refused explicitly.
The candidate artifact records the tree, base commit, repository, capture policy, source hash
and retained ref. Each command receipt names the candidate and temporary execution directory;
the verification artifact records commands, exit status, runtime, final source hash and freshness.
The directory is disposed after checking. If checks modify included source, Marina captures a
successor candidate and withholds evidence for it until it is separately verified. Those changes
are never copied back automatically. The fallback whitespace check compares the candidate index
with its base (git diff --cached --check); it is labeled whitespace-only, not a test suite.
Capture is bounded to 8,192 paths, 16 MiB per file and 128 MiB total. Ignored untracked files
(including node_modules, generated files and ignored secrets) are excluded. Included
credential-shaped files such as .env.local, .npmrc and private-key files cause refusal;
this filename rule is not a content secret scanner. External dependencies are excluded by
default. When the checks need dependencies the snapshot lacks, verification reports not_run.
Candidate checks run where the session runs: with a container runner, the snapshot is mounted
(sync:mount) or its diff applied (sync:patch) inside the same image, under the same allowlist,
approvals and gates. They never fall back to the host. The verification artifact records the
runner (executionRunner).
For a Bun project on the host, opt into preparation with:
code verify candidate dependencies:auto # or dependencies:bun# From the terminal:/world code verify candidate dependencies:bunMarina requires a captured bun.lock, installs into the disposable snapshot with
--frozen-lockfile --ignore-scripts, and records preparation separately from checks. It uses
an isolated cache and copied packages; it never copies or links the writer’s node_modules.
The supported dependency sources are integrity-locked public npm packages and captured
workspaces. Repository/global install settings and credentials are not inherited; custom
registries, Git/URL/file dependencies, uncaptured workspaces, unsafe patch paths and captured
node_modules are refused. Native packages that need install scripts may therefore fail their
checks. Preparation has a 120-second deadline and the existing background admission limits.
A preparation failure stops the check chain and produces not_run evidence, with an
inspectable output artifact. Successful evidence records the preparation policy and lockfile SHA-256;
generated dependencies are excluded from source freshness only inside the private copy.
Npm projects can use dependencies:npm or dependencies:auto with a captured version 3
package-lock.json. The snapshot runs npm ci --ignore-scripts --no-audit --no-fund, including
development dependencies, with private user/global config and a disposable cache. Only
integrity-locked public npm packages are supported. Workspace links, project .npmrc,
shrinkwrap overrides and captured node_modules are refused. The same preparation and
failure reporting apply to code verify delivery manifest:<path> dependencies:auto.
The source tree does not certify the external environment, services or dependencies, and the temporary directory is not a security sandbox. Host-local checks retain their existing trust model; Flywheel remains a separate execution target with no host fallback.
Git refs under refs/marina/candidates/ retain each snapshot and its base ancestry across Git GC.
At 64 retained candidates, further captures refuse instead of deleting review evidence. Inspect
the exact ref in code show <candidate-id> with git show <ref>:<path> or
git diff <base-commit> <ref>. An operator may retire a finished candidate with
git update-ref -d <ref> <recorded-commit> (the commit is in the artifact metadata). Retire only
after its review/evidence retention needs end; later freshness checks report retired evidence
unavailable. Refs are local Git metadata and are not pushed automatically.
Agents can use marina_code with action: "verify", verificationMode: "candidate", or the same
optional verificationMode on marina_code_verify. The default remains live verification.
Both tools also take dependencies (none, check, auto or a manager) and scope; ask for
user authorization before installing dependencies.
For either background mode, inspect the receipt’s result before writing the final summary.
6. Propose a change. You (or an agent) propose a unified diff as a reviewable patch, rather than editing blindly:
> code patch Fix off-by-one in tokenizerdiff --git a/src/parser.ts b/src/parser.ts@@ -42,1 +42,1 @@- for (let i = 0; i <= tokens.length; i++) {+ for (let i = 0; i < tokens.length; i++) {Marina checks it applies cleanly and stores it as a pending patch. The reply shows the patch’s
stat and its first hunk; code show <patch-id> prints the whole diff. Apply it when you’re happy:
> code apply last patchPatch applied: patch_2a9f…1 file · +1 −1 src/parser.ts +1 −1The apply reply lists what that patch changed, not the whole workspace; code diff shows all
working changes.
7. Stay safe. Snapshot before risky work and reverse it instantly if needed:
> code checkpoint before-refactor> code revert before-refactor8. Close the loop. Finishing a session leaves a durable trail — a summary that becomes part of the project’s memory:
> code done Fixed the off-by-one; added a regression test.You just did a full review-grade coding loop: inspect → run → propose → review → apply → checkpoint → summarize. Now the parts that make Marina different.
What makes it more than a CLI
Section titled “What makes it more than a CLI”It’s persistent. Sessions, patches, checkpoints, and summaries don’t vanish when you
disconnect. code list shows your sessions; code resume <id> picks one back up; code history
replays what happened. Work compounds instead of restarting.
It’s multi-agent and people-native. A human in WebChat and an autonomous agent are peers — they issue the same commands. You can pull a team together:
> code roles # see suggested roles (implementer, reviewer, tester…)> code crew Refactor the auth module # auto-assembles a crew and dispatches the goal> code crew Refactor auth with alice,bob # …or name the members yourselfWhen a crew shares a session, a write lock keeps changes coherent — one writer at a time, with explicit handoff — so a reviewer testing the code never clobbers the implementer’s work:
> code writer # who currently holds the write lock> code handoff ready for review to:alice # hand the lock to alice (a session participant)The recipient must be a session participant: the creator, the bound agent, a dispatched crew member,
or anyone who has acted in the session. Only the current holder or the creator can pass a held lock
on. An unknown to: recipient refuses the handoff and keeps the lock. The older spelling
code handoff <notes> to alice still works when to alice ends the notes and names a participant.
Otherwise the notes are stored as written and the lock is left alone, so a “to” in the prose never
moves it.
Approvals are first-class, auditable artifacts. Risky actions can be surfaced as approvals — request/approve/deny artifacts that leave a visible decision trail and render as cards with Approve/Deny buttons in the dashboard. (Today these are advisory: they record the decision but don’t yet block the underlying action, and the requester can decide their own — enforced approvals with separation-of-duties are on the roadmap.) From any surface:
> code approvals # pending requests> code approve <id> / code deny <id>It speaks your tool’s dialect. Coming from Claude Code, Codex, or Pi? Switch the profile and keep your muscle memory — the vocabulary maps onto Marina’s primitives:
> code profile use claude # accept→apply, bash→run, compact→summary, …> code profile help codexIt remembers and teaches. Summaries deposit into the project’s shared pools; skills learned
during a session (code skill add …) outlive it, so the next agent — human or AI — starts ahead
instead of from scratch.
The core commands
Section titled “The core commands”| Do this | Command |
|---|---|
| Start / resume / finish | code start [title] · code resume <id> · code done [summary] · code list |
| Look around | code files [path] · code read <path> · code search <query> [path:<relative-path>] · code diff |
| Run things | code run <cmd> · code verify · code test / lint / typecheck · code recipe run <name> |
| Change code | code patch <title> → code apply last patch · code checkpoint [title] · code revert <id> |
| Review | code approvals · code approve|deny <id> |
| Team up | code roles · code crew <goal> [with a,b] · code writer [agent] · code handoff <notes> [to:agent] |
| Capture | code summary <notes> · code skill add <name> <text> · code task <title> |
| Orient | code doctor · code onboard · code status · code history |
Full reference any time: code help.
Point it at the right workspace
Section titled “Point it at the right workspace”By default a session uses the server’s working directory. For real projects, configure roots:
# env (e.g. in docker-compose or your shell)MARINA_CODE_ROOTS=/srv/repos/acme,/srv/repos/widgetsMARINA_CODE_DEFAULT_ROOT=/srv/repos/acmeThen code workspace list, code workspace discover (find likely projects), and
code workspace use <path> choose where new sessions open. code doctor confirms git + ripgrep
are present (they power diff/checkpoint/revert and fast search).
Separate task data from source with operator-configured roots:
MARINA_CODE_INPUT_ROOTS=/srv/task-inputsMARINA_CODE_OUTPUT_ROOTS=/srv/task-outputsThese existing directories must be separate from the workspace and from one another. File tools
use their absolute paths; inputs are read-only, outputs support normal writer-authorized write
and edit. They do not become selectable executable workspace roots. Git patches and candidate
snapshots still belong to the primary workspace. Symlinks cannot escape a granted root.
With container sync:mount, input directories map read-only to /marina-task/inputs/0, /1, etc.;
outputs map to /marina-task/outputs/0, /1, etc. code status and assignment context show the
mapping. Container commands use guest paths; file tools use server paths. sync:patch refuses
extra roots because it mounts no host files. Host processes retain their existing execution
permissions; file-tool grants alone are not an OS sandbox. Use a container runner to enforce
read-only inputs for executed programs.
The project’s own test runner
Section titled “The project’s own test runner”code test, code verify and code recipe run detected follow the project’s language rather
than assuming JavaScript. Detection reads root markers (pyproject.toml, setup.py, setup.cfg,
tox.ini, pytest.ini, manage.py, tests/runtests.py, Cargo.toml, go.mod, pom.xml,
build.gradle, package.json) and prefers the language of the files you changed, so a Python
repository that also carries a package.json for tooling is tested with its Python runner.
Lockfiles name the dependency manager (bun.lock, pnpm-lock.yaml, yarn.lock,
package-lock.json, uv.lock, poetry.lock):
| Project | Runner |
|---|---|
Django’s own repository (tests/runtests.py) |
python tests/runtests.py [labels] |
Django project (manage.py) |
python manage.py test [labels] |
| Other Python | python -m pytest [paths or node ids] [-q -x -v]; without pytest, the project’s test script or python -m unittest (see step 1 above) |
| Rust | cargo test [filter] |
| Go | go test ./... [-count=N -short -v] |
| Java (Maven / Gradle) | mvn -B -q test [-Dtest=Class,…] / gradle test -q [--tests Class] |
| JavaScript / TypeScript | the typecheck / lint / test / build package scripts, run with the lockfile’s manager (bun run, npm run, pnpm run, yarn run) |
These join the code run allowlist only in those fixed shapes: relative selectors that stay
inside the workspace and a few inert flags, never an arbitrary script or interpreter. code doctor
shows the detected runner and why. A saved default recipe (code recipe save default …)
overrides detection for a workspace.
Run commands in a container image
Section titled “Run commands in a container image”code workspace runner container image:<ref> keeps the session’s files on the host but runs its
finite commands (tests, scripts, code verify) inside a container image: a project’s CI image, a
language toolchain, or a benchmark environment. Podman or Docker is required.
code workspace runner # where commands run nowcode workspace runner container image:python:3.12 workdir:/workcode workspace runner container image:<ref> sync:patch workdir:/testbed shell:bashcode workspace runner local # back to the host (if the operator allows)sync:mount(default) binds the worktree atworkdirread-write on a read-only container root, with the repository’s.gitbound read-only so code in the container cannot change git metadata that Marina’s host git reads. The workspace root must be a repository root. The process runs as your user.sync:patchmounts nothing from the host. The image already holds the project atworkdir; Marina applies the session’s pending diff (tracked changes and new files) inside a throwaway container before each command.- An environment preamble (for example activating a conda env) is operator configuration:
MARINA_CODE_CONTAINER_INIT. Text after--is refused in-world, because a preamble would run outside the allowlist and approvals.
The same code.exec gate, allowlist and exec approvals apply as for host runs; configuring a
runner is itself gated. Choosing an image yourself, or adding network:on, also needs the
code.exec.unrestricted gate (a refusal asks your creator or an admin). All capabilities are
dropped, CPU, memory and process limits apply (cpus:, memory: in MB, timeout:), and every
container is removed after its command. If the runtime or image is missing, commands fail. Marina
never quietly runs them on the host instead. code test, code verify and
code verify start|candidate all use the session’s runner; a verification dependency install runs
only in a mount-sync container with network, otherwise it is reported as not run.
Operators set the container for every local session with MARINA_CODE_CONTAINER_IMAGE (and
_SYNC, _WORKDIR, _INIT, _SHELL, _NETWORK, _RUNTIME). That is policy, not a default:
code workspace runner local is refused and sessions may change only cpus:, memory: and
timeout:. MARINA_CODE_CONTAINER_REQUIRED=false makes the image a default a session may leave
for the host; MARINA_CODE_CONTAINER_REQUIRED=true requires a container even without an operator
image. Neither can be changed in-world.
The container runtime uses your own container setup: the images you have already pulled, your
containers.conf/storage.conf (or Docker context), and your registry logins, taken from the
server’s environment at startup. The command inside the container gets none of that environment.
To keep images elsewhere with Podman, set MARINA_CODE_CONTAINER_STORAGE (and optionally
MARINA_CODE_CONTAINER_RUNROOT) to absolute paths. code doctor shows where images are stored
and warns if that is on tmpfs or under /tmp, where images would fill memory.
Optional isolated execution with Flywheel
Section titled “Optional isolated execution with Flywheel”When the Marina server has FLYWHEEL_TOKEN, each entity can create one durable isolated workspace.
This is shipped functionality; “optional” means Marina and local Code Mode do not require Flywheel:
code sandbox startcode sandbox use # active session now runs finite commands in Flywheelcode project init demo # /workspace/projects/demo becomes the durable guest cwd# or: code project clone https://github.com/example/project.gitcode run bun testcode project statuscode project diff # inspect tracked changes without marking them exportedcode project export # bounded Git patch artifact for tracked workcode project export archive # complete bounded archive, including binary/untracked files# code project import <project_archive_artifact> restoredcode project delete restored confirm # refuses unexported work without `discard confirm`code project reconcile # remove metadata belonging to a replaced sandboxcode service start web --port 3000 -- bun run devcode service logs webcode service probe web /healthcode service probes web # durable health historycode service screenshot web / # PNG evidence when Chromium is present in the imagecode approval request network publish:<service-id># approve the returned artifact before publishingcode service publish webcode service revoke webcode service stop webcode sandbox local # explicitly return this session to host-safe local mode# steward operations:code sandbox ops inventorycode sandbox ops reclaim # dry runcode sandbox ops reclaim confirm # recoverable idle hibernationSelection is stored per coding session. Sessions default to local, configuration alone
never changes the target, and a Flywheel error never retries on the host. Use code sandbox status,
hibernate, resume, and stop confirm for lifecycle management. code project list|diff|switch
selects among durable guest projects and refuses to leave unexported dirty work. Public clone URLs
must be credential-free HTTPS. Patch export remains the compact tracked-work path; bounded archive
export/import preserves complete project content through Flywheel’s typed byte stream, stages and
validates imports with digest, byte, expansion, member, path, and file-type limits, then atomically
promotes them. Private Git remains a broker extension. Local and guest
files are distinct and must not be treated as synchronized. Managed services run only in Flywheel,
keep durable restart recipes and bounded guest logs, and stop across hibernation until explicitly
restarted. PID plus process birth identity prevents a reused guest PID from being signaled as the
original service. Localhost HTTP probes store response status, latency, and a bounded redacted body as
verification evidence, with history available through code service probes. Publishing requires a
matching one-use network approval and is automatically leased (one hour by default); stop,
hibernate, explicit revoke, or lease expiry removes exposure. code sandbox network status reports
whether policy is provider-owned or verified. Credential-like command arguments are refused;
code sandbox credentials exposes only logical, secret-free binding state, and binding fails closed
until Flywheel exposes its direct-sandbox broker contract. Screenshot capture runs a guest-local
Chromium browser, transfers a size-bounded PNG, verifies its signature, and removes the guest temporary
file. It fails closed when the sandbox image has no supported Chromium binary.
Try it now
Section titled “Try it now”A runnable, narrated session lives in examples/coding-quickstart/ —
copy-paste or run the script to watch the whole loop end to end.
Where to go next
Section titled “Where to go next”- Commands Quick Reference — every Marina command by category
- Agent Development — drive coding sessions from the TypeScript SDK
- Coordination — crews, roles, projects, and tasks in depth
- Connecting — WebChat, WebSocket, Telnet, MCP, SDK, and the ACP editor bridge
Inspect workspace images
Section titled “Inspect workspace images”Use marina_see with source: "workspace:images/diagram.png", or
code see images/diagram.png What labels are visible?. PNG, JPEG, GIF and WebP
files are read through the active session’s workspace and explicit task input grants.
Absolute paths inside that workspace are accepted. Files outside those grants and
sandbox-only paths are refused; no copying into internal scratch storage is needed.
The result is a saved visual_evidence artifact with the question, model, source path
and SHA-256 of the observed bytes. It is model output, not independently verified fact.
After a context reset, use code artifacts kind visual_evidence and code show <id>
to recover the complete observation without another vision request. A changed image
requires another inspection; the saved observation identifies the bytes seen earlier.
The file list offers Inspect image. A canvas coding desk offers Review image inspection and displays the same saved evidence under Artifacts & verification. Opening a desk or reopening evidence does not start a model call. Canvas readers use their existing session permissions; publishing a desk never copies private image bytes.
Verification receipts cover only the checks shown. A successful workspace test does not establish that a separately collected deliverable works: include its dependencies in the delivery contract and check the collected files in the intended environment. Task review remains a separate decision.
code run groups single- or double-quoted arguments without invoking a shell:
code run python -c 'import json; print(json.load(open("result.json")))'.
For literal arguments, including nested quotes, use
code run --argv ["python", "-c", "print(1 + 2)"]. Both forms use the same
execution permissions, allowlist and approval audit as other workspace commands.
Variables, pipes, substitutions and globs are not expanded.
Plan mode can inspect original task and verification artifacts with marina_code_show,
using IDs from its artifact list. For exact constraints and identifiers, reopen the
original request: a saved proposal or context summary may paraphrase or miscopy it.
This read path uses the same session permissions as code show and does not grant
file writes or command execution.
