Skip to content

Zed Affine Earth Coding Agent Developer Guide

rg78803 edited this page Sep 19, 2026 · 2 revisions

Affine.Earth in Zed — the coding agent, for developers

Zed's agent panel, driven by Affine.Earth. The model behind the panel is franklin-membrane at https://affine.earth/v1, and it is not a language model. It plays a bounded coding language game: it classifies what you asked into a move, reconstructs the board from the tool results already in the thread, emits the hands that advance the board while the move is open, and closes with an account of what it measured. Nothing is sampled. Every turn carries one line — what you asked, what was measured, what is sealed — so the meaning of the turn is validated back to you while the tools run.

Everything below was measured on 2026-09-18 against Zed 1.20.2 (stable) and the live nine-cell fleet. Where a number appears, it came from a run, not a doc.

1. Setup

Launch Zed through the helper, never from the Dock:

~/.config/zed/launch-affine-earth-agent.zsh

It reads ~/.gaiaftcl/zed/affine-earth-api.key (mode 600) into AFFINE_EARTH_API_KEY and starts Zed with it. Zed's custom provider id affine-earth reads exactly that variable; a Dock launch has no environment and shows Credentials Missing. The helper no longer exports OPENAI_API_KEY — that armed Zed's built-in OpenAI provider with the Affine key and put its model list in the selector.

~/.config/zed/settings.json — the agent block as Zed 1.20.2 documents it. Zed hot-reloads it:

"language_models": {
  "openai_compatible": {
    "affine-earth": {
      "api_url": "https://affine.earth/v1",
      "available_models": [{
        "name": "franklin-membrane",
        "display_name": "Franklin Membrane (Affine.Earth)",
        "max_tokens": 12800000,
        "capabilities": { "tools": true, "images": true, "parallel_tool_calls": true,
                          "prompt_cache_key": false, "chat_completions": true,
                          "interleaved_reasoning": false, "max_tokens_parameter": false }
      }]
    }
  }
},
"agent": {
  "default_profile": "write",
  "default_model":          { "provider": "affine-earth", "model": "franklin-membrane" },
  "inline_assistant_model": { "provider": "affine-earth", "model": "franklin-membrane" },
  "thread_summary_model":   { "provider": "affine-earth", "model": "franklin-membrane" },
  "commit_message_model":   { "provider": "affine-earth", "model": "franklin-membrane" },
  "compaction_model":       { "provider": "affine-earth", "model": "franklin-membrane" },
  "subagent_model":         { "provider": "affine-earth", "model": "franklin-membrane" },
  "tool_permissions": { "default": "allow" },
  "sandbox_permissions": {
    "allow_unsandboxed": true,
    "allow_all_hosts": true,
    "network_hosts": ["huggingface.co", "affine.earth", "github.com"],
    "write_paths": ["/Users/<you>/.gaiaftcl", "/Users/<you>/.swiftpm",
                    "/Users/<you>/Library/Caches/org.swift.swiftpm",
                    "/Users/<you>/Library/Logs/GaiaFTCL",
                    "/tmp", "/private/tmp", "/private/var/folders"]
  },
  "enable_feedback": false
},
"context_servers": {
  "affine_earth_mcp": { "url": "https://affine.earth/language-invariant/mcp", "timeout": 300 }
}

Why each line is there:

  • Every feature-specific model falls back to default_model when unset; all six are pinned so a model experiment can never move titles, compaction, commit messages or subagents off the membrane.
  • default_profile: "write". The ask profile advertises eight read-only tools and no MCP tools; write advertises the full built-in set and, because enable_all_context_servers is true there, the ~51 affine.earth MCP tools ride in the same tools[] under their raw names.
  • The sandbox (Seatbelt) is a second gate on terminal and fetch, separate from tool_permissions. The LAW 7 build the agent emits takes its lock at ~/.gaiaftcl/locks and writes SwiftPM caches — outside the project — so those paths must be writable or the build dies silently. /tmp and /private/var/folders are there because the Minecraft court's own scripts log to /tmp and build under $TMPDIR; ~/Library/Logs/GaiaFTCL because the build law appends its queue log there (scripts/gaiaftcl-mac-build.sh:75) — measured in Zed as Operation not permitted on the first live build.
  • allow_unsandboxed: true. Measured on the first live LAW 7 build inside Zed's terminal (2026-09-19): SwiftPM compiles the package manifest under its own sandbox-exec, and Seatbelt refuses a nested sandbox — sandbox-exec: sandbox_apply: Operation not permitted — so swift build cannot run in a sandboxed terminal however many paths are granted; and this repo's cells/xcode/.build is a symlink into ~/Library/Caches, outside the worktree. This is Zed's documented persistent off-switch (agent_settings.rs:427-434): the plain terminal tool runs. The grants below stay as the record of what a sandbox would have needed.
  • allow_all_hosts: true. Measured in Zed 1.20.2's source (crates/agent/src/sandboxing.rs:181-189, crates/sandbox/src/macos_seatbelt.rs:357-369): with a host allowlist the sandboxed terminal may reach only one loopback proxy port — it cannot bind a port of its own or curl 127.0.0.1. Bringing a program to life means binding ports (the play bus on 4223, the court on 8799) and probing them, so the allowlist alone is the wrong shape; allow_all_hosts emits (allow network*). allow_unsandboxed: true is the other way out and removes the write gates too.
  • enable_feedback: false: rating a reply sends the whole thread — your messages, the replies, repo diffs — to Zed's servers.
  • timeout: 300: the umc_* and execute_* MCP tools exceed the 60 s default.

.rules at the repository root. Zed injects exactly one project instruction file into every request, first match in its list, and .rules is first. Before it existed the match was AGENTS.md — 59,246 bytes whose own first line declares it historical — on every turn, while CLAUDE.md never rode. The membrane does not read the system prompt at all; those bytes carried nothing. .rules is short and says so.

Threads keep the model they were created with. default_model and default_profile seed new threads only. After changing either, press ⌘N. The selector at the bottom of the panel should read Franklin Membrane (Affine.Earth) · Write.

2. Keys

action key
toggle the agent panel ⌘? (also ⌃⌘I under the VSCode base keymap)
new thread ⌘N
send ⏎ · ⌘⏎ send and follow · ⇧⌘⏎ interrupt the running turn
threads sidebar ⌥⌘J · ⌃⇥ thread switcher · ⌘G history in the sidebar
profile selector / model selector ⌘I / ⌥⌘/
permission card ⌘Y allow once · ⌥⌘Y always · ⌥⌘Z deny
review the agent's changes ⇧⌃R
agent settings / manage skills ⌥⌘C / ⌥⌘L

Zed's docs print these as action names; the literal keys above are from the keymaps embedded in the 1.20.2 binary.

3. What a turn does

The prompt goes to POST /v1/chat/completions with the whole thread and the profile's tools[]. The membrane:

  1. Classifies the move — EdgeOpenAIHumanLanguageLayer matches the prose against sealed English faces projected through the translate court into the 25 mined languages, anchored at token boundaries. There is no contains("build"): the word compile inside InvariantCompiler cannot fire a build. The bounded coding moves are INGEST, LOCATE, READ, AUTHOR, BUILD, VERIFY and CLARIFY — a bring-up ("bring … to life", "run it so it stays up") that searches, opens what it found, and closes with a numbered question whose options are commands read byte-for-byte from files it opened; TEACH is a move too, on any turn. A line that answers a question — 1, the arena, option 2, a number for a slot — is a follow-up: it keeps the move that asked and carries the answer, resolved by the same token-anchored matcher.
  2. Rebuilds the board from the tool results in the thread — every file listed, read, written, built — with two layers: what the thread knows (cumulative) and what the current move has done (since your last message). State lives on the client and in NATS; no cell keeps a session, so any of the nine cells can take any turn.
  3. Measures progress as an exact rational p/q and projects it onto the coding attractor as amplitudes [3p/5q, 4p/5q], so the game's torsion (q²−p²)/q² is the distance from done: 1 on a fresh task, 0 exactly when the move's goal is met. The game seals CURE while work remains and CALORIE when it is done — never CALORIE on work never done.
  4. Emits the hands that advance the board — Zed executes every tool_calls element and returns one tool message per call — or closes with a synthesized account when torsion reaches 0 or the board stops moving. Zed 1.20.2 has no per-turn request cap; the membrane is the only terminator, which is what the stagnation guard is for.
  5. Speaks the bond on every turn as content beside the tool calls — one sentence a colleague would say: Bond — looking for “uum8d minecraft”. → Bond — found “uum8d minecraft”; reading what it has (8 files so far). → Bond — I found “uum8d minecraft” and read 21 files; what's left is your call. → Bond — running your pick: See the minecraft court run, locally, end to end. → Bond — done: … · 21 files sealed under 12629e789e4a. The geometry behind those words — Intent, Position (p/q, amplitudes [3p/5q, 4p/5q] against the coding attractor [3/5, 4/5], distance from done (q²−p²)/q²), Origin (this turn's root, the sealed workspace hash, the play tip and its NATS subject) and Next — rides in every turn's receipt as receipt.audit, where the harness checks it against the receipt's other fields. It is not printed into the chat: the founder's ear, not a rule, decided that ("intent and origin geometry must map to human language" means map, not print).
  6. Teaches, at the level you ask for. A CLARIFY close says what the thing is (the README's first sentences, the script's own header), what alive means here (the script's === 1. … === section lines), the fork in words with the command behind each choice, then — at the default teach level — each script in its own words, the lines it will print (its echos with its own defaults substituted), and where the files explain the things the phases name (“play bus” — RUNBOOK.md:16: …). Say brief for short answers, engineer level for the middle, teach me for the full walk-through; the register is read from your own lines in the thread and the last one wins. Everything quoted, nothing composed: it is a template over bytes it read.

What each move emits:

move hands close
INGEST — "learn the code in dir" list_directory + find_path dir/**/* (paged 50 at a time), then eight read_file per turn coverage, bytes, the workspace hash
LOCATE — "where is …" grep + find_path, then the top-ranked hit opened the paths, which are open
READ — "read the file …" read_file with the root-prefixed path; an outlined file in 400-line windows; a question is answered by citation (path:line: text, byte-equal) and a route literal is followed to its server-side handler with one grep and one window read the cited lines
CLARIFY — "bring … to life", "run it so it stays up" grep per needle, find_path in both spellings and both shapes (**/*X*, **/*X*/**), two batches of manifests and scripts, then the question; on the answer, the selected command through terminal, and for a serve line the page's HTTP code and the court's own selftest the numbered options, then what the command printed
BUILD / VERIFY the LAW 7 terminal build, diagnostics on red; VERIFY then replays every command the thread ran and says what changed exit status, tail, replay comparison
AUTHOR — "add a called beside the one in " hydrate the target, find the sibling case, follow its symbol to the declaration, ask for the one unmeasured value, clone both anchors as edit_file, LAW 7 see §6
TEACH — "teach me Affine.Earth from Study 44" the studies index, the lesson page, the named study what was opened

Every read_file body in the thread is hashed; the workspace hash is sha256 over the sorted path\tsha256(body) lines and rides inside the sealed statement, so an ingest that outgrows the 24-message transcript window is still sealed whole. Files over 16,384 bytes come back from Zed as an outline, not the text — most Swift sources here — and the coverage line names them.

Zed also sends three service prompts of its own: a thread title after every turn, a detailed summary, and a compaction handoff. Under the pins they land on the membrane, which answers them from the board (a 3–7-word title from the move and its target, a Goal/State/Context/Next/Pitfalls handoff) and never seals a coding turn for them.

4. The wire, as Zed's openai_compatible provider sends it

stream: true, stream_options: {include_usage: true}, temperature: 1.0 always, parallel_tool_calls: true whenever tools are present, no max_tokens, no stop, no tool_choice. Tool messages are {role: "tool", content, tool_call_id} with no name. The membrane answers with SSE: a role chunk, the bond as content deltas, one indexed header chunk and one arguments chunk per tool call, finish_reason: "tool_calls", [DONE]; usage is reported as bytes/4 and labelled so. A JSON body to a stream: true request reads in Zed as No response from the agent — the SSE path is the only one Zed uses, which is why the harness streams.

What rides on every request from this repository in the write profile, before your first byte: a ~77 KB system prompt when AGENTS.md was the rules file (now .rules), the 51 MCP tool definitions (~78 KB) and the built-ins — about 160 KB, ~40k prompt tokens. Zed's auto_compact fires at 90 % of the declared window; at 12.8M it never does.

5. Verifying it yourself — the closure is autonomous

Nothing here needs a human at the keyboard. The harness plays Zed's half of the loop with the provider's real wire and Zed's real tool result formats, executes every hand against the workspace, and feeds the bytes back:

bash scripts/zed-coding-game/evidence.sh <label>                 # against the apex round-robin
RESOLVE=37.27.7.9 bash scripts/zed-coding-game/evidence.sh <label>  # pinned to one cell — a measurement, never routing

Eight scenarios, each a discriminating instrument: ingest a folder; locate (the question that once read a nutrition script); read a path; the InvariantCompiler control (must emit read_file, never terminal); teach on turn two; the bond on every close; Zed's title prompt (3–7 words, no punctuation); Zed's compaction prompt. The verdict is ZED_CODING_GAME_PROVEN only at 8/8 and is sealed under evidence/zed-coding-game-<stamp>-<label>/VERDICT.json with every transcript.

To run the candidate binary locally before any cell sees it:

bash scripts/zed-coding-game/local-serve.sh        # the Mac product on :18787, auth off, NATS closed
URL=http://127.0.0.1:18787/v1/chat/completions bash scripts/zed-coding-game/evidence.sh local

6. What it will not do, stated plainly

  • It writes one class of edit and no other. The sibling clone: a measured anchor line with one token renamed, plus a declaration cloned beside its sibling with a numeric slot the person supplied in the thread. Every other edit is refused by name (REFUSED_CLONE_NEEDS_LAW, REFUSED_NO_SIBLING, REFUSED_ANCHOR_NOT_UNIQUE, REFUSED_NEW_BYTES_CARRY_FLOAT) with the nearest sibling handed back, or closes asking for a sealed plan ({"affine_zed_tools":[{"name":"edit_file","arguments":{…}}]}) or an artifact brief for execute_artifact_crucible. Generation stays the caller's half.
  • Eight files per ingest turn; a 350-file folder is ~50 turns. Zed executes each batch in parallel.
  • A bring-up asks after two batches of hydration (sixteen files) — the person is the next hand, not a seventeenth file.
  • The MCP tools are on the wire in the write profile but the game does not yet emit them as moves.

The full two-person acceptance — bring the UUM8D Minecraft to life, clarify twice, read by citation, edit with a slot the person supplies, verify by replay — is written up with its sealed text at Zed — Minecraft comes to life; run it with bash scripts/zed-coding-game/minecraft/acceptance.sh <label>.

7. Reading a session back

Zed persists every thread in ~/Library/Application Support/Zed/threads/threads.db — table threads, one zstd blob per thread. Copy the database first; Zed holds a WAL.

sqlite3 threads.db "SELECT writefile('t.zst', data) FROM threads ORDER BY updated_at DESC LIMIT 1;"
zstd -d t.zst -o t.json

The JSON carries model, every message with its tool_results, cumulative_token_usage and request_token_usage. Read model first: the panel's model, not the settings comment, is who answered. Six threads on this Mac had run on LM Studio while their titles read "Hi, I'm Franklin…" — the membrane had only ever written the titles.

8. The record

commit what evidence
b8636e3c6 the bounded coding game replaces the substring bridge evidence/zed-coding-game-20260918T133318Z-local-7a7fc8bf — local 6/6
09f68f193 fleet artifact aaf9c047 on 8/9 …T140009Z-canary-hel04-aaf9c047 6/6 pinned · …T142150Z-apex-roundrobin-aaf9c047 6/6
6be4e547f · a7a536aee omni-miner: bounded row fetch ends the OOM wall; DeepSeek V4.1 Flash mined first evidence/omni-miner-cure-20260918T150500Z — 9/9, 0 OOM, cursors on 9/9
5c1e1f7f0 the harness is Zed-faithful (stream, SSE, exact schemas) —
0b870ea7b the bond streams before tool_calls …T152840Z-apex-sse-6e7cdc54 — 6/6 on the stream, 9/9 running inode
3c5250bf0 .rules, helper, settings from Zed's docs and source —
20a70e20f title/compaction answered from the board; 202 for notifications; Retry-After …T160628Z-apex-sse-aa7f7b13 — PROVEN 8/8 on the apex, aa7f7b13 on 9/9 file and running inode
56453b607 the agent asks: CODE_CLARIFY, the audit block, citation, the sibling clone, verify by replay; the two-person Minecraft acceptance …T183940Z-minecraft-local — ZED_MINECRAFT_ACCEPTANCE_PROVEN 16/16, court hook REFUSED_UNKNOWN_BUDGET → WIN
(this commit) fleet artifact a7576aaaac5ca5ac on 9/9 as file and running host-systemd inode, C⁴ tip 9-way identical …T193…Z-minecraft-apex — PROVEN 16/16 on the apex round-robin, control arm discriminates

🧬 CURES — read in this order

Each step is the reason the next one exists. Nothing here is medical advice, and no page calls any medicine safe or unsafe.

1 · Why an exact safety screen at all

2 · The three libraries, which grow rather than close

3 · The maps — every place a molecule could act, counted

4 · One medicine at a time

  • Zilganersen — the first treatment for Alexander disease, screened on the real approved sequence
  • A drug an AI designed — rentosertib for pulmonary fibrosis, and exactly what our instruments reach
  • CAR-T, halted — the verdict a regulator could re-derive
  • N-of-1 antisense — the only safety net at a population of one
  • VERVE-102 — the off-target lattice a stranger can re-derive
  • PM359 — prime editing, certified before anyone is dosed
  • Del-Zota — the one safety question that can be made exact

5 · What keeps a disease alive, and what moves it

⚖️ How to read any page here

🔬 The method — exact against float, domain by domain

The same move every time: take a domain where a floating-point model is the accepted instrument, compute the same quantity in exact integers, and seal the cases where the two render opposite verdicts. The subject under grading is always the instrument, never the phenomenon.

⚡ Fusion — the energy case

🌍 The planet, and the sky

🏛 Markets, money and risk

⚛️ Run a court yourself

📒 Program ledger — every study by lifecycle

A study appears here under the state its evidence has earned, and above under the question it answers. The two are different filings of the same work, on purpose.

✅ LAW FROZEN · DATA SEALED

🔴 LIVE CLAIM — standing, not sealed

🌊 CHARTER · OPEN — the findings, published either way

☀️🌑 Eclipse 2026 — Study 01, DATA SEALED

🔬 Discoveries and flows

Clone this wiki locally