npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@junghanacs/entwurf

v0.25.0

Published

Garden-citizen dispatch substrate and meta-bridge for pi, Claude Code, Copilot CLI, OMP, Antigravity, and Codex CLI; Claude and Snowflake Cortex Code ACP backends.

Readme

entwurf

entwurf is a garden-citizen dispatch substrate: a thin bridge that lets already-existing agent harnesses address one another by garden id without pretending to own each other's transcript, auth, or runtime.

entwurf — a forged screwdriver for garden-citizen dispatch

npm · maintained by junghanacs.com

npm package: https://www.npmjs.com/package/@junghanacs/entwurf

Short answers for common questions: FAQ.md.

Legacy package: @junghanacs/pi-shell-acp. entwurf is its 0.12+ successor line: the same work renamed around the garden-citizen dispatch substrate rather than the pi adapter.

Repository shape. This repo is entwurf-core (v2 dispatch) + native-harness bridges + a pi adapter + an ACP plugin. Pi is one adapter, not the project subject. Claude Code, GitHub Copilot CLI, and OMP (omp) are shipped self-fetch citizens; Antigravity (agy) is shipped native-push. Codex CLI is a native-push citizen, supported in 0.21.0 on Linux over the operator-owned app-server: vendor-trusted user-scope birth, strict request-scoped identity, loaded-thread probe, one-shot queue delivery, and visible fresh beside the CALLER's own pane. Unrestricted attached-TUI seat inference is not supported or claimed; #95 D1 retired the fixed codex tmux home on 2026-09-16, so the operator seats the app-server wherever they like and a Codex caller opens its sibling beside its own TUI pane. Codex remains native so it keeps its vendor tools, delegation, and work context; this is not another way to buy or expose GPT through ACP.

Claude Code / Copilot / Codex / agy / omp / pi
  → garden id
    → entwurf_v2
      → control-socket | meta-mailbox | native-push

entwurf_v2 is the canonical dispatch surface over existing garden citizens — live control-socket send, meta-mailbox enqueue, and native-push into a live Antigravity conversation or loaded Codex thread. It starts no process on any rail. entwurf_fresh_call is separate lifecycle. With placement omitted the seat follows the CALLER: a Codex citizen opens beside its own TUI pane, found by the thread-id in that pane's terminal title, and 0 or 2+ matching panes reject with no fallback; every other caller opens in its own session. A missing app-server rejects before launch, and Entwurf never creates or supervises it. An explicit placement.tmuxSession remains an expert override. Exact 0.153.4 source confirms that arbitrary attached-TUI request→seat inference is unavailable, so Entwurf makes no such claim. Codex has no resume surface.

Garden id is deliberate vocabulary. It is not a decorative synonym for session id, worker, delegate, or subagent. The unfamiliar word is a guard: each harness keeps its own identity and transcript, while entwurf supplies a narrow addressable surface between siblings.

A narrow harness tool surface is discipline, not a missing feature. When entwurf drives a backend the way pi taught — the ACP Claude session, a pi-native sibling — it runs without a sub-agent tool or a todo tool, on a narrow tool surface in auto-approve (yolo) mode (the ACP backend yolo-runs inside its isolated overlay). That restraint is the point: it keeps the one forged screwdriver from drifting into a second orchestrator, and keeps the operator's own driver — not a hidden agent swarm — the thing actually steering. See AGENTS.md North Star.

The ACP plugin is one ingress, not the boundary. It re-enters as a pi provider/model on a host --entwurf-control session that is already a v2 socket-citizen; it does not mint its own socket / peers / citizen layer (see AGENTS.md §ACP Plugin Boundary). No OAuth proxy, no subscription bypass, no CLI transcript scraping, no Claude Code emulation.

pi --entwurf-control
  → entwurf ACP plugin
    → claude-agent-acp
      → Claude backend under the operator's local auth

Native bridges reach beyond ACP transport. A global SessionStart hook registers native Claude Code sessions as garden-native meta-sessions with a garden id, a mailbox, and a trusted sender marker. That makes an already-running Claude Code terminal addressable through entwurf_v2 (the mailbox path), self-identifying through entwurf_self, and replyable by garden id — without turning pi into a second harness or importing Claude's transcript.

native Claude Code
  → SessionStart hook
    → mailbox-backed meta-session <garden-id>
      → entwurf-bridge MCP
        → entwurf_self | entwurf_v2 | entwurf_inbox_read

Copilot uses another self-fetch rail. Its birth hook mints the V3 record on the first prompt, the MCP hand supplies entwurf_inbox_read, and a first-party extension arms the receiver marker and rings a doorbell for queued bodies. Supported launch is entwurf copilot; visible fresh uses that same managed invocation so extension scan, receiver preconditions, model, and permission policy are present before the sibling calls back with its garden id.

Antigravity uses a separate shipped rail. Its PreInvocation hook births or re-attaches the conversation by native conversationId, writes a record-backed sender marker, and leaves delivery to the live native LS gRPC route. There is no mailbox or receiver marker on this rail: entwurf_v2 probes the conversation and direct-injects with native-push.

native Antigravity / agy
  → PreInvocation imprint → meta-session <garden-id>
  → entwurf-bridge MCP sender identity
  ↔ entwurf_v2 native-push

Claude's install-meta-bridge, Copilot's four install-copilot-* surfaces, agy's install-agy-{bridge,statusline,hooks}, OMP's four install-omp-{bridge,mcp,config,receive}, and Codex's birth, MCP and statusline units are distinct because their lifecycle and delivery transports are genuinely different. See DELIVERY.md.

Direction. Inverse of pi-acp. pi-acp lets external ACP clients talk to pi; entwurf lets garden citizens talk across harness boundaries — with pi as one adapter, not the center.

Concept primer

A few words that look unusual for a coding tool.

  • Entwurf (기투, projection-of-self) — sibling sessions with their own runtime boundary. Not "delegate," not "worker," not "sub-agent." Opening a visible sibling (entwurf_fresh_call), live peer messaging (entwurf_v2) and reopening a dormant one (entwurf_resume_call) are first-class; the hidden background resume that preceded the last of those was withdrawn under the visible-first rule.
  • Garden / garden id — the garden is the shared address space where independent harness sessions become citizens without losing their own runtime or transcript. A garden id is the stable address of one such citizen (for pi, a garden-native session id like YYYYMMDDTHHMMSS-<6hex>; for native harnesses, a meta-session id minted from an authoritative lifecycle hook — Claude SessionStart, Codex SessionStart, Copilot's first-prompt birth hook, agy PreInvocation, and for OMP an in-process extension bound to both session edges that mints only the visible mode === "tui" host). It is not a worker name and not proof that pi owns the session. The same-looking id may name a live control socket, a dormant pi record, a mailbox-backed native session, or a native-push conversation/thread, so routing always reads the record and live rail facts first.
  • Engraving — optional short operator text delivered through each backend's native identity carrier. Not a giant hidden prompt, not a tool catalog.
  • MCP — in this repo, MCP is just the transport by which ACP-backed sessions receive pi capabilities that native pi exposes directly as extensions. It is not a general MCP platform. Explicit entwurfProvider.mcpServers only; no ambient ~/.mcp.json scanning, no automatic retrieval. The same entwurf-bridge entry can also be wired into another host's MCP catalog (Claude Code, Copilot, Codex, Antigravity, OMP, …) when the operator chooses. entwurf_self returns an authoritative pi-session or trusted meta-session identity envelope; entwurf_v2 requires an authoritative sender by default (#50 C4) — a plain external MCP host with no identity lane is refused unless the operator explicitly wires the documented anonymous hatch, and even then it is never replyable.
  • Session persistence — re-attaches pi to the same remote ACP session. Does not hydrate backend transcripts into pi history.

Install

Two installation routes, and the one you want depends on where you work.

  • Direct — npm install -g @junghanacs/entwurf, then entwurf setup <project>. This is the general route: it composes every harness it FINDS on the host — pi, Claude Code, Codex, Copilot, Antigravity, the ACP rail — and reports each one PASS / SKIP / FAIL. Use it in an ordinary shell, in tmux, anywhere.
  • Herdr workbench — herdr plugin install from inside a Herdr session. It activates Entwurf for the pi and Claude Code sessions Herdr has integrated, and only those. Contract, evidence grades and its refusals: plugins/herdr/README.md.

Neither route installs a harness, a subscription, or a login (Hard Rule 17). Everything below this paragraph describes the direct route.

Platform evidence, in one breath. The Entwurf-only install surface on macOS is CERTIFIED (CI) (macos-install-surface). On macOS every garden-native harness rail, marker/request join, ACP turn, and mux is NOT CERTIFIED — pending physical host. On Linux the released harness rails are the certified axis; Codex joined them in 0.21.0. Its explicit-home first admission was measured on 2026-09-12 — a real Pi outside codex opened Codex and outbound Pi inside it, with exact callbacks and delivery both ways — and the release acceptance that followed closed check:full, the LIVE release gate at MUST 24/0/0, and a 475/475 qualification. Unrestricted attached-TUI placement is unsupported rather than a release blocker. Native Windows is UNSUPPORTED. CERTIFIED (CI) is weaker than a physical-host doctor green. The npm package has no os restriction; that is installability, not a support claim.

macOS landed in 0.20.0. That release added macOS support for Entwurf's install surface: the npm package installs, entwurf --help and entwurf check-bridge boot, and entwurf setup composes the integrations it finds. The four installers that previously rejected Darwin by platform name no longer do. That surface was validated only in macOS CI because the developers did not have a physical Mac. So macOS native-harness rails—marker join, an ACP turn, and mux included—are NOT CERTIFIED — pending physical host; setup writes their wiring but reports them non-green. Linux's certified rail status is unchanged.

On Darwin, entwurf setup that detects a harness is intentionally non-green: the wiring WAS written and nothing failed to install, but the harness rail is NOT CERTIFIED — pending physical host. That FAIL is not an install defect. An Entwurf-only host (no harness on PATH) stays green.

A physical Mac can send host facts without installing or logging in: scripts/raw-macos-measure/probe.sh (/bin/sh, ~3.5s). Measurement, not certification.

entwurf is a neutral npm package first. Get the package, then run entwurf setup <project> — one command, the same front door from an npm global install, an npm project-local install, or a source checkout. It composes every harness it finds on the host and reports each one PASS / SKIP / FAIL. You are not meant to assemble the parts by hand; the per-harness installers further down are the repair surface for when one unit needs to be redone alone. Pi is still the adapter that hosts the ACP plugin and live control-socket surface, but the base install is not pi install npm:... anymore.

The package exposes six bins:

  • entwurf → run.sh (installer, checks, native-bridge doctors/installers)
  • entwurf-bridge → the MCP stdio launcher (mcp/entwurf-bridge/start.sh)
  • entwurf-statusline → the Claude Code statusline renderer (scripts/meta-bridge-statusline.sh)
  • entwurf-agy-statusline → the Antigravity garden-id statusline renderer (scripts/agy-statusline.sh)
  • entwurf-agy-imprint → the Antigravity PreInvocation birth/sender hook (scripts/agy-imprint.sh)
  • entwurf-copilot-statusline → the Copilot CLI garden-id footer renderer (scripts/copilot-statusline.sh)

The bridge/renderers/hook use stable bin names so package upgrades do not bake versioned package-store paths into native-harness settings.

Installing Entwurf installs Entwurf only: its package bytes, six bins, bridge, and integration artifacts. It does not install pi, Claude Code, Copilot CLI, Codex, agy, omp, Cortex, or any other harness runtime. Those are operator choices and may all be absent. The bridge also does not provide credentials, tokens, subscription access, or an auth bypass; whatever an operator-installed harness already trusts is what Entwurf can use. setup is composition, not recruitment: it may wire a harness that is present, but never downloads one to make a matrix cell look complete.

From npm — user/global install

npm install -g @junghanacs/entwurf

entwurf setup /path/to/your-project
entwurf check-bridge

setup wires the target project for the pi adapter / ACP plugin lane and composes whatever native harnesses are present. The global install is the easiest path when Claude Code's USER-scope MCP registration should work from every cwd.

Codex: what you still type by hand

setup writes every byte Entwurf owns, and three things are deliberately left to you. They are the only manual steps between a fresh install and calling a Codex sibling, and all three are answers only you can give:

  1. Trust the birth hook once, in a visible plain Codex. Answer Trust all and continue and send one first turn. Nobody can do this for you: the receipt is the vendor's record of your security decision, and writing it ourselves would turn a prompt into a silent install. Until it exists, setup and doctor-codex-birth stay honestly non-green and say exactly this.

  2. Start the app-server, in a detached tmux session that is NOT where you work.

    entwurf codex-app-server

    One command for the vendor string nobody wants to type. It execs codex app-server --listen unix://<default socket> right there — so Ctrl-C is yours, and Entwurf neither supervises nor restarts it. The session matters because the MCP bridge is this server's child and inherits its TMUX: that tmux server is the one caller-seat lookups read. The launcher prints which seat it got rather than guessing one for you.

  3. Answer the folder-consent prompt once for each directory you open siblings in. Codex records a direct decision per EXACT directory — not the parent, not the git root — and a directory with no answer anywhere opens a consent screen instead of running a first turn. entwurf_fresh_call prints codex-launch-cwd-undecided and opens the window anyway — the screen is self-repairing, and answering it once is the whole fix. (Two neighbours differ: a project .codex layer can consent with no entry at all, and a directory inside an explicitly untrusted project is noted as codex-launch-cwd-untrusted-ancestor because the vendor answers that one with an error naming the repository root instead of a prompt.) You can also do it up front:

    codex -C <the directory you will launch siblings in>   # answer the prompt, then quit

    Same reason as step 1: it is your security decision, and writing it ourselves would turn a prompt into a silent install.

Then entwurf_fresh_call with backend: "codex" works. Attaching your own visible Codex TUI (codex --remote "unix://$CODEX_HOME/app-server-control/app-server-control.sock") is optional and independent — a sibling Entwurf opens brings its own.

Observation, not a verb (#95). That --remote string is exactly as unfriendly as the --listen one was, and it has no managed spelling. It is deliberately left alone: it is how an operator attaches their OWN terminal, not how a sibling is opened, and entwurf_fresh_call already spells it internally for the siblings it launches. Whether the operator-facing half deserves its own verb is open.

From npm — project-local install

cd /path/to/your-project
npm install --save-dev @junghanacs/entwurf

npx entwurf setup .
npx entwurf check-bridge

For an npm upgrade, rerun setup in the same scope (use @junghanacs/entwurf@latest when you want the registry's stable line explicitly), then make the first check from that same scope: entwurf check-bridge for a global install or npx entwurf check-bridge for a project-local install. Native-harness repair and process restarts remain a separate post-upgrade step below.

entwurf install <project> is the narrower repair leaf: it writes only .pi/settings.json in the target project, with the absolute path to the installed entwurf-bridge launcher, and composes no harness. Reach for it when the pi wiring alone needs redoing. (The old ~/.pi/agent/ target-registry link is gone — #50 C3; nothing reads it.)

To register the bridge in an MCP host by hand from a project-local install, point it at node_modules/.bin/entwurf-bridge — see External MCP registration.

From source — development clone

git clone https://github.com/junghan0611/entwurf ~/repos/gh/entwurf
cd ~/repos/gh/entwurf

./run.sh setup /path/to/your-project
./run.sh check-bridge

The full source setup requires Node 24, pnpm, and Python 3 on PATH. Harnesses are optional-by-presence: setup runs the frozen dependency install, then composes what the operator already installed — a compatible pi (>=0.87.1 <0.88), Claude Code, agy, and the Copilot CLI each get their wiring completed when detected, an absent harness is an explicit zero-state SKIP, and a detected harness that cannot be completed (including a below-floor pi) is a named FAIL that makes setup exit nonzero. setup never installs a harness binary or touches a credential store. It also exposes stable commands under ~/.local/bin, including entwurf → this checkout's run.sh, so managed Copilot fresh does not depend on an unrelated global npm/pnpm installation. A detected copilot composes all four of its native units (birth → MCP → receiver → visible footer) in one go. Package consumers run the same entwurf setup <project> through their npm-provided bin: installed mode is decided by name first, skips the source-only pnpm bootstrap entirely, and reports the stable commands as already provided by npm bin linking.

The pi user-scope registration is ONE shared entry with a recorded owner (#86 C2): installing from a second checkout or npm root does not silently steal it — normal install/setup refuse with zero settings bytes written, entwurf takeover-user-scope is the operator-explicit move, entwurf doctor-pi-package names the ownership verdict, and entwurf remove-user-scope is same-owner-only. The full contract — atomicity across the package and provider halves, the split verdict over an operator's own override, legacy adoption, and the managedSettingsPath binding — is docs/setup-clean-host.md §1.1.

A development clone runs the bridge source through Node's strip-types path; an npm-installed package runs the prebuilt JS under mcp/entwurf-bridge/dist/ because Node refuses to strip .ts files under node_modules. The dev launcher's source path means ./run.sh check-bridge needs no build. After git pull, however, run pnpm install when the lockfile changed and run pnpm run build-bridge before artifact-consuming checks such as ./run.sh check-bridge-delivery: dist/ is gitignored and may be absent or stale immediately after a checkout or pull.

Pi adapter / ACP plugin lane

This rail is CERTIFIED on Linux; on macOS it is NOT CERTIFIED — pending physical host (control sockets, mux, ACP plugin hosting).

To use the entwurf provider inside pi, install a compatible pi binary separately (@earendil-works/pi-coding-agent >=0.87.1 <0.88). Then point pi at the npm-installed package or development clone:

# global npm install path
pi -e "$(npm root -g)/@junghanacs/entwurf" --list-models entwurf

# project-local install path
pi -e ./node_modules/@junghanacs/entwurf --list-models entwurf

For daily operator sessions, launch pi with --entwurf-control — no id injection; the meta-record mints the garden address (see Garden launcher). Older pi versions may silently miss the provider/extension surface, so treat the pi floor as release-critical for the ACP/plugin lane. A host that only uses entwurf-bridge from Claude Code / Copilot / Codex / Antigravity / OMP does not need pi at all for delivery: no entwurf_v2 rail launches a pi process. OMP is a pi fork, but it is its own binary and resolves its own agent directory, so that lineage does not reintroduce a pi requirement either. That external-only shape works with the same setup command: pi is optional-by-presence there, so a pi-less host simply gets an explicit pi SKIP while the detected harnesses are composed.

Native harness repair and doctors

A plain MCP registration exposes the bridge tools; a garden-native session also needs entwurf's lifecycle and identity unit. setup composes every user-scope unit for each detected harness, including Codex's birth hook — its paths are the operator's own, so nothing here asks for root. What setup cannot do is answer the vendor's one-time hook-trust prompt: only the operator can, in their own visible Codex, so a first setup on a Codex host writes every byte and stays honestly non-green until they have. This is the repair surface: each unit has its own installer, doctor, and inverse.

  • Claude Code (Linux CERTIFIED; macOS NOT CERTIFIED — pending physical host) — install-meta-bridge, doctor-meta-bridge.
  • Antigravity / agy (Linux CERTIFIED; macOS NOT CERTIFIED — pending physical host) — install-agy-bridge, install-agy-statusline, install-agy-hooks, each with a matching doctor-agy-*.
  • GitHub Copilot CLI (Linux CERTIFIED; macOS NOT CERTIFIED — pending physical host) — four independent units, four independent failure modes: install-copilot-bridge (birth: garden id + who-sent, on the first prompt), install-copilot-mcp (the entwurf tool hand, where entwurf_inbox_read lives), install-copilot-receive (the receiver extension: doorbell + receiver marker), install-copilot-statusline (optional for a manual citizen, required for supported fresh) — each with a matching doctor-copilot-* and uninstall-copilot-*.
  • OMP (omp) (Linux CERTIFIED; macOS NOT CERTIFIED — pending physical host) — four units, in-process extensions rather than launchers: install-omp-bridge (birth: the mode === "tui" visible host, its garden id on the status line, and who-sent), install-omp-mcp (the omp-native entwurf-bridge entry), install-omp-config (the one operator setting tools: xdev: false, without which the vendor mounts MCP tools as xd:// devices the model cannot call), install-omp-receive (the receiver extension: mailbox watch + announce-only doorbell) — each with a matching uninstall-omp-*, and a doctor-omp-* for all but the setting, whose runtime axis doctor-omp-mcp owns. The setting writer owns exactly the lines it adds and refuses an explicit operator tools: xdev: true by name rather than overwriting it.
  • OpenAI Codex CLI (supported in 0.21.0 on Linux; macOS NOT CERTIFIED — pending physical host) — install-codex-birth publishes the SessionStart declaration into $CODEX_HOME/hooks.json with its launcher closure under $XDG_DATA_HOME/entwurf/codex-birth, all operator-owned; it owns that declaration, not the file — another integration's SessionStart group coexists (the vendor keys trust per <path>:<event>:<group>:<handler>), so install appends, uninstall splices only ours out, foreign bytes survive both, and doctor-codex-birth reports neighbours in a FOREIGN section it certifies nothing in; the vendor trust receipt is read at the index our declaration was measured at, is the operator's single answer, and doctor-codex-birth reports it as its own axis (present, or red with the exact instruction — never computed, never written); install-codex-mcp owns [mcp_servers.entwurf-bridge], including the env_vars boundary for CODEX_HOME, Entwurf garden/control roots, and the app-server's TMUX/TMUX_PANE; install-codex-statusline owns thread-title in tui.status_line; install-codex-terminal-title owns thread-id in tui.terminal_title. Each has a matching doctor and inverse. One Codex precondition is the vendor's own and no installer supplies it: the directory a sibling starts in must already be answered in this Codex. A direct consent decision is recorded per exact directory on this rail — not the parent, not the git root — and a directory the vendor has no answer for at all opens a consent screen instead of running a first turn, so entwurf_fresh_call prints codex-launch-cwd-undecided and opens the window anyway; that screen is self-repairing, and answering it once teaches the vendor the directory for good. Nothing here refuses a launch — the note is a diagnostic, and the one caller that treats it as a precondition is smoke-codex-fresh-live, where nobody is at the keyboard. Two neighbouring cases read differently: a directory answered untrusted is not noted at all (the vendor skips its screen on this rail, so that turn starts), and a directory INSIDE an explicitly untrusted project is noted as codex-launch-cwd-untrusted-ancestor because the vendor answers that one with an error naming the repository root rather than a prompt. The check is a narrow local read of the operator's own config, never the vendor's verdict: the vendor also consents through project layers it reads from its app-server, and everything the check cannot see stays silent. The operator owns the app-server and seats it wherever they like; Entwurf never creates or supervises it. Omitted fresh placement follows the CALLER, never the backend being opened: an explicit placement always wins, then a Codex CALLER opens beside its own TUI pane — located by the thread-id that pane's terminal title carries, with 0 or 2+ matching panes refused and no fallback — then the caller's own session. (#95 first shipped a fixed codex home for omitted-placement Codex TARGETS; #95 D1 retired it on 2026-09-16 once the anchor made the room unnecessary.) A pane title is a placement input only, never an address, liveness or delivery fact. Exact 0.153.4 source's missing per-client carrier still bounds placing a sibling beside a TUI whose thread nobody named as unsupported.

Run them as entwurf <command>. Which unit a doctor's refusal names, and the clean-host walk-through for each harness, live in docs/setup-clean-host.md.

Launching Copilot as a garden citizen — entwurf copilot

Copilot only scans for extensions when its CLI is started with COPILOT_CLI_ENABLED_FEATURE_FLAGS=EXTENSIONS, and when that flag is absent it skips the scan silently — no error, no log line, no receiver. entwurf does not own your shell and writes nothing to your rc files, so it owns one invocation instead:

entwurf copilot                 # managed launch, in this terminal
entwurf copilot -p "…" --model gpt-5.4
copilot                         # the plain vendor CLI, untouched

entwurf copilot execs the vendor CLI in your current terminal — same cwd, same pid, same exit status, no tmux window and no new citizen (a Copilot session is still born on its first prompt). Before it launches it verifies that the receiver unit it is about to promise is actually installed, and refuses with entwurf install-copilot-receive if it is not, rather than starting a session that can never be delivered to.

Running it is your consent to its profile. For that one invocation it adds:

| Injected | When | |---|---| | COPILOT_CLI_ENABLED_FEATURE_FLAGS=EXTENSIONS | always — your other feature-flag tokens are preserved, in order, deduplicated | | --model auto | only when you passed no --model | | --yolo | only when you passed no explicit permission or surface policy flag |

The escape hatch is simply to state your own policy: any of --yolo, --allow-all, --allow-all-tools, --allow-all-paths, --allow-all-urls, --allow-tool, --deny-tool, --allow-url, --deny-url, --available-tools or --excluded-tools suppresses the injected --yolo. The narrowing flags are in that list on purpose — adding --yolo beside your --allow-url=… would silently widen exactly what you were restricting. --allow-all-mcp-server-instructions (prompt content, not authorization) and --autopilot (a mode) are deliberately not policy flags. Everything you pass is forwarded byte-identically, injected defaults land before any -- terminator, and nothing after the terminator is read as policy. Nothing is written to disk; run plain copilot and none of this applies.

Why --yolo is the default: the managed lane exists so a sibling can wake an idle session, and draining the mailbox with entwurf_inbox_read costs two interactive approvals under the default permission prompts — which an idle, unattended session is not there to answer.

Claude Code uses the supported floor >=2.1.217; older versions silently discard the exec-hook args, so install and doctor fail loud rather than falling back. After any upgrade, rerun entwurf setup <project> — it re-composes every detected harness, all four Copilot units included — and restart its existing processes; reach for a single install-* only when one unit needs repair on its own. A claimed Claude host is certified only when a new session using the installed artifact makes doctor-meta-bridge exit 0 with the live owner join.

The Claude meta-bridge rail is CERTIFIED on Linux. On macOS it is NOT CERTIFIED — pending physical host. The Darwin install fence is open; uninstall was already open. Detailed diagnosis and clean-host steps live in docs/setup-clean-host.md.

The active citizen store is V3-only. A store that fails certification is never silently migrated: quiesce the native sessions, run entwurf meta-bridge-fresh-cut, then reinstall. The cut archives routing records only—never native transcripts or external memory—and no runtime reads the archive. Exit meanings and the complete operator contract are in docs/fresh-cut-policy.md.

entwurf check-bridge proves the MCP surface boots without backend auth. A real ACP turn requires LIVE=1 entwurf smoke-acp-provider-live; the full release protocol and host evidence boundaries are VERIFY.md and BASELINE.md.

Extension set — do not filter. The ACP provider, entwurf-control, and model-lock extensions ship as one set. Disable the package as a whole rather than filtering individual entries into a partially wired state.

Backend prerequisites

Claude is the reference ACP backend. The Claude ACP server package (@agentclientprotocol/claude-agent-acp, pinned with @agentclientprotocol/sdk) ships as a pinned dependency of entwurf; backend authentication still belongs to the operator's local claude CLI / runtime. Once the bridge is installed, the resolver picks the ACP server in this order:

  1. CLAUDE_AGENT_ACP_COMMAND env override — explicit override for an alternative binary or a wrapper command.
  2. require.resolve(...) against the bundled package dependency (@agentclientprotocol/claude-agent-acp). This is the default path; no extra global install needed.
  3. PATH:claude-agent-acp fallback — used when the package resolution fails (e.g. a hand-edited node_modules).

The curated model registry exposes unprefixed Claude ids — claude-opus-5, claude-sonnet-5, claude-fable-5-1 — plus the cortex- rows below. Codex is not an ACP backend. Its native-citizen lane, supported in 0.21.0, is independent so the session retains Codex's native tools, delegation, and work context: trust-gated hook birth, strict request _meta identity, app-server probe, one-shot native-push, and visible fresh.

Snowflake Cortex Code is the second ACP backend (contract and audit: docs/acp-backend-rail.md). Curated ids are cortex-auto, cortex-claude-opus-5, cortex-claude-sonnet-5, and cortex-openai-gpt-5.4.

The operator supplies an authenticated cortex CLI and selects a connection with entwurfProvider.cortexConnection or ENTWURF_ACP_CORTEX_CONNECTION. CORTEX_HOME must be absent: the adapter refuses it because it would bypass the session-scoped HOME containment. Explicit MCP servers are projected into the overlay-private cortex/mcp.json; only the bridge receives the real operator HOME needed for the garden store.

check-acp-cortex runs in pnpm run check:full. Real acceptance is deliberately on demand:

LIVE=1 ENTWURF_ACP_CORTEX_CONNECTION=<conn> entwurf smoke-acp-cortex-live

The aggregate Claude floor does not run this smoke, so silence is not a Cortex PASS.

Emacs frontends

Works from terminals and from Emacs frontends that launch pi-coding-agent.

entwurf in Doom Emacs

For a dedicated agent socket, pass the socket name:

(setq pi-coding-agent-extra-args
      '("--entwurf-control" "--emacs-agent-socket" "pi"))

The bridge exports the socket name to ACP children as PI_EMACS_AGENT_SOCKET, so skills call Emacs without hardcoding:

emacsclient -s "${PI_EMACS_AGENT_SOCKET:-server}" --eval '(...)'

Settings

Reference shape lives in pi/settings.reference.json. Minimum:

{
  "compaction": { "enabled": false },
  "entwurfProvider": {
    "appendSystemPrompt": false,
    "settingSources": [],
    "strictMcpConfig": true,
    "showToolNotifications": true,
    "tools": ["Read", "Bash", "Edit", "Write"],
    "skillPlugins": [],
    "permissionAllow": ["Read(*)", "Bash(*)", "Edit(*)", "Write(*)", "mcp__*"],
    "mcpServers": {
      "entwurf-bridge": {
        "command": "/path/to/entwurf/mcp/entwurf-bridge/start.sh",
        "args": []
      }
    }
  }
}

mcpServers is the only ACP MCP injection path. In practice this repo is about the bundled entwurf-bridge, which carries pi capabilities into ACP-backed sessions — not about being a general MCP catalog. Invalid entries throw McpServerConfigError — broken tool state surfaces as broken tool state. ./run.sh install writes the bundled entwurf-bridge entry and prunes the legacy bundled session-bridge entry from older installs.

appendSystemPrompt: false is intentional. Pi / AGENTS context rides the first-user augment; putting it into the Claude _meta.systemPrompt carrier can route OAuth sessions to metered "extra usage" billing.

Which keys reach which backend. entwurfProvider is one block for both backends, but its keys are not universal. tools / permissionAllow / disallowedTools / settingSources / skillPlugins / appendSystemPrompt are Claude's declaration surface: they do not shape a cortex session, which runs its own native tools and reaches MCP through the overlay-private mcp.json projection instead. They are not inert, though — the bridge still reads tools for its backend-invariant exclude-tools preflight and folds all of them into the config signature, so editing one still forces a fresh cortex session. cortexConnection is cortex-only. mcpServers is the one declaration surface that reaches both, each through its own transport. (compaction is a top-level pi key, not an entwurfProvider one — it is pi's own transcript policy and is not a backend knob at all; see §Compaction.) Unknown and retired keys are ignored rather than rejected, so a key aimed at the wrong backend fails silently: if a cortex session does not show the surface you configured, check that the key is one cortex actually consumes before suspecting the bridge.

External MCP registration

entwurf-bridge can also be registered in a separate MCP-aware harness (Claude Code, Copilot CLI, Codex CLI, Antigravity, OMP). Two shapes exist and they are not interchangeable:

  • plain external MCP host — no garden meta-record or sender marker. It can read the surfaces, but entwurf_v2 is refused by default: there is no authoritative sender.
  • garden-native session — a trusted lifecycle hook minted a garden id, so it is addressable and replyable by that id.
claude mcp add --scope user entwurf-bridge entwurf-bridge

Per-harness registration (Claude Code ~/.mcp.json, Codex ~/.codex/config.toml, the managed install-agy-* surfaces, and OMP's managed install-omp-mcp into <omp agent dir>/mcp.json — whose pinned server key is what shadows a borrowed Claude import, see docs/external-mcp-host.md §OMP), the PATH/env boundary for GUI-launched MCP servers, the anonymous-sender hatch, and the full external/meta-session semantics are in docs/external-mcp-host.md. For the maintained multi-harness setup and skill/command packaging details, see agent-config. See also the MCP entry in Concept primer, the sender envelope contract in AGENTS.md, and Custom skills for the in-pi ACP skill surface.

Per-backend operating surface

The Claude ACP backend keeps its native model / API / tools; entwurf shapes only what enters from pi. Claude honors an explicit CLAUDE_CONFIG_DIR export when set by the operator.

Claude uses _meta.systemPrompt for the engraving carrier (kept short and pure — billing-safe; rich operator context rides the first user message instead, see Context carriers) and CLAUDE_CONFIG_DIR for a whitelist overlay so auth/runtime entries stay available while operator memory, hooks, agents, history, local settings, and project memory remain hidden. The overlay writes an explicit empty hooks: {} because Claude SDK organic compaction needs the configured-empty shape; no operator hook definitions are inherited. It also pins permissions.defaultMode: "bypassPermissions" so an unattended ACP turn cannot suspend on an interactive permission prompt; explicit tools / disallowedTools still constrain the callable surface and backend authentication remains the operator's. The four-tool baseline is Read, Bash, Edit, and Write; permissionAllow carries their allow declarations, and Skill is added automatically when skillPlugins is non-empty. Operator context cap override: ENTWURF_ACP_CLAUDE_CONTEXT=<int>.

Codex is not an ACP backend here. Its native-push lane, supported in 0.21.0, uses SessionStart for birth, strict request _meta for who-sent, and a loaded app-server thread as the live route. A Codex CALLER with placement omitted opens its sibling beside its own TUI pane, matched by the thread-id its terminal title carries; a pane whose thread nobody named is never inferred.

Antigravity is also not an ACP backend. It is a native-push citizen: PreInvocation supplies birth/sender identity, entwurf_v2 probes and direct-injects replies into the live conversation, and no mailbox/receiver marker is involved.

entwurf owns no memory layer at all — the ACP plugin's boundary explicitly excludes a memory DB (see AGENTS.md §ACP Plugin Boundary), and no backend is a memory authority for another. What this overlay does is narrower: Claude's native memory layer is pinned off so operator memory, project state, and history never leak into an ACP session. Whatever semantic-memory / Denote tooling an operator runs is their own skill surface on whichever harness hosts it — deliberately kept out of the MCP bridge, and not a pi privilege.

Smoke commands

pnpm check                              # everyday core (prints wall time; <=60s on the reference host)
pnpm run check:full                     # full deterministic floor (adds the hermetic + package/install tiers)
./run.sh check-bridge                   # entwurf-bridge direct MCP smoke (no backend auth)
./run.sh smoke-agy-install-state        # agy MCP + exact permission ownership lifecycle (install/uninstall/doctor/inverse)
./run.sh smoke-agy-statusline-state     # agy ambient garden-id install surface
./run.sh smoke-agy-hooks-state          # agy PreInvocation birth hook
./run.sh check-agy-sender-identity      # record-backed pid/start-key sender identity
./run.sh check-codex-native-push       # deterministic Codex UDS probe + no-replay queue contract
./run.sh smoke-codex-config-state      # Codex MCP/status-line install, doctor, and inverse
./run.sh check-codex-birth-hook        # sandboxed payload + declaration: one V3 per top-level thread, the envelope's own axes, no markers, and the declaration leaf (normalized digest, selection by launcher command, byte-preserving splice)

# source-maintainer only — qualification snapshots the git work surface, and both
# commands are source-contract gates rather than installed operator checks:
./run.sh check-agy-permission-matrix    # AGY permission contract space as a literal table (declared cells + stated exclusions)
./run.sh check-gate-manifests           # the qualification HEAD alone: self-test + manifest-set validation + declared lane inventory, zero mutants run (in check:hermetic)
./run.sh check-gate-qualification       # kill-proof: committed defect mutants must turn their gates red for the claimed reason

# agy LIVE acceptance — requires an already-running conversation:
LIVE=1 AGY_CONVERSATION_ID=<id> ./run.sh smoke-agy-native-push-live

# Codex loaded-thread probe (pre-amendment evidence; not first-release acceptance):
LIVE=1 CODEX_LIVE_THREAD_ID=<threadId> ./run.sh smoke-codex-native-push-live

# Codex caller-seat acceptance. The operator-owned app-server must sit in a session OTHER than
# the one the Pi/Codex pair runs in — that separation is what tells the caller-pane anchor apart
# from the app-server's inherited environment. This smoke ALSO needs its one stable launch
# directory answered once (manual step 3 above); VERIFY.md's Codex section owns that procedure,
# including how to derive the exact path instead of retyping it. Record all four coordinates:
LIVE=1 ENTWURF_CODEX_APP_SERVER_PID=<existing-app-server-pid> \
  ENTWURF_CODEX_FRESH_MODEL=<codex-model> \
  ENTWURF_CODEX_FRESH_PI_MODEL=<pi-model> \
  ./run.sh smoke-codex-fresh-live
# This accepts only a real visible Pi → visible Codex → visible Pi run: initial Pi in a session
# other than the app-server's, then Codex and its outbound Pi beside it. A fixture first leg may
# collect receipts but does not satisfy the topology. No arbitrary attached-TUI parity is claimed.

# ACP plugin LIVE acceptance — need the operator's local Claude auth/credit:
LIVE=1 ./run.sh smoke-acp-socket-citizen-live   # turn-free socket citizenship (S1)
LIVE=1 ./run.sh smoke-acp-raw-turn-live         # pinned ACP pipe + raw 1 turn (S2a)
LIVE=1 ./run.sh smoke-acp-overlay-live          # config overlay + hooks:{} + tool meta (S2b)
LIVE=1 ./run.sh smoke-acp-provider-live         # real pi provider path + progress/L3 (S2c/S2f)
LIVE=1 ./run.sh smoke-acp-session-reuse-live    # process-scoped reuse + codeword recall (S2d)
LIVE=1 ./run.sh smoke-acp-carrier-augment-live  # augment delivery + empty-carrier billing clean (S2e-1)

LIVE=1 ./run.sh release-gate /tmp/scratch --cut # the single cut gate (MUST + BEHAVIOR; --cut refuses any MUST SKIP)
LIVE=1 ENTWURF_ACP_CORTEX_CONNECTION=<conn> ./run.sh smoke-acp-cortex-live  # Cortex is on-demand: the aggregate does not re-certify it

pnpm run check:full includes the AGY permission contract matrix and the qualification HEAD (check-gate-manifests — runner self-test, manifest-set validation, declared lane inventory, zero mutants executed); the committed-mutant EXECUTION is scheduled separately (./run.sh check-gate-qualification — the CI check job runs it on a branch push that touched the qualification surface, unconditionally on workflow_dispatch -f qualify=true and a weekly schedule, and release-gate carries it as a MUST step; a tag push runs no CI at all). A branch run whose body was filtered out is not release evidence: the exact-SHA oracle requires the run it reads to carry that step concluding success. A gate a release touches must kill its known defect for the claimed [QK:<claim>] reason — the descriptions above name what each smoke covers, and no check count is quality evidence on its own. Gate qualification needs the git work surface, while the matrix is the source permission-contract gate; both run from a clone, never as a post-install operator step.

Custom skills

Claude sessions accept custom skills through skillPlugins — an array of absolute paths to directories matching the Claude Agent SDK plugin layout:

<your-plugin-root>/
├── .claude-plugin/
│   └── plugin.json
└── skills/
    └── <skill-name>/
        └── SKILL.md

A self-contained example lives at pi/skill-plugin-example/. Put plugin roots anywhere on disk except under ~/.pi/agent/ (pi's internal cache).

{
  "entwurfProvider": {
    "skillPlugins": ["/absolute/path/to/your-plugin-root"]
  }
}

Skill is auto-added to tools and Skill(*) to permissionAllow whenever skillPlugins is non-empty. Each entry is validated at settings parse time and throws when the path is missing, not absolute, not a directory, or missing .claude-plugin/plugin.json. The Claude session does not start until the violation is fixed. The bridge does not validate plugin.json contents or SKILL.md bodies — that is the Claude Agent SDK's contract.

To verify, start a fresh Claude session and ask the model to list its skills; the names declared in your SKILL.md frontmatter should appear among the visible skills. The operator-driven version of this check is Q-SKILL-CALLABLE in VERIFY.md.

skillPlugins is a Claude-backend-only install surface. Codex exposes skills through native ~/.codex/skills/ passthrough.

For a real consumer arranging many skills, see agent-config.

Entwurf orchestration

Entwurf is one dispatch capability with native-pi and MCP surfaces. Native pi exposes it directly as extension tools; ACP-backed and external native-harness sessions reach it through entwurf-bridge. The purpose is not to invent a different sub-agent system, but to preserve the same sibling-based model across harnesses.

A sibling has its own runtime boundary and its own provider/model identity — not a worker, delegate, or sub-agent. Today every transport targets an existing garden citizen, and none of them starts a process. entwurf_v2 routes from rail-specific liveness: live pi fire-and-forget → control socket; dormant pi → an honest reject; active self-fetch → meta-mailbox; probe-alive agy → native-push. A control-socket-domain dispatch takes the per-target lock, which now serializes concurrent live sends at one garden id; mailbox and native-push use their own deliverability evidence and remain lock-free.

A two-pane recording covers the pre-0.12 v1 surface end-to-end — sibling resume, cross-process MCP dispatch across a different cwd, and a live peer greeting. It is archived evidence: it shows a resume verb this repo no longer has (see demo/README.md).

entwurf demo

Live peer messaging carries a sender envelope { sessionId, agentId, cwd, timestamp, origin?, replyable? }; entwurf_self returns that authoritative envelope for the current pi session or trusted meta-session. Plain external MCP hosts are non-replyable. A garden-native meta-session carries a trusted meta-session envelope, but replyable is a fact its own rail decides, not a consequence of being trusted — a self-fetch citizen needs a live armed receiver, a native-push citizen needs an alive adapter probe, and a pi session needs its control socket. entwurf_self also reports which rail a meta-session reply would ride, because a native-push citizen has no mailbox to name. wants_reply is an etiquette marker rendered as a (wants reply) badge — not a transport contract, no wait, no polling. v2 never gates on it: a wants_reply from an external/non-replyable caller is passed through and surfaced honestly beside that sender's replyable: false, not rejected — the decider routes on target + intent, never on sender replyability. (The retired v1 entwurf_send did reject it; that behaviour went with the verb.)

In ACP-backed and external native-harness sessions, entwurf-bridge exposes eight tools: entwurf_v2, entwurf_peers, entwurf_fresh_call, entwurf_resume_call, entwurf_callback, entwurf_self, entwurf_inbox_read, and the explicit/manual entwurf_register_native fallback. Native pi exposes the shared capability directly through the extension surface (entwurf_v2, entwurf_peers, entwurf_fresh_call, entwurf_resume_call, entwurf_callback tools; the socket-scan /entwurf-sessions command is gone — #50 C4). For garden-id delivery/reply use entwurf_v2 — the canonical surface that classifies the target and routes to live-pi / self-fetch meta-mailbox (Claude Code, Copilot, OMP) / native-push (Antigravity, Codex), and refuses a dormant target instead of waking it. To open a sibling that does not exist yet use entwurf_fresh_call — WHERE it opens is a fact about the calling process, not a parameter: inside herdr (HERDR_ENV=1) it opens a NEW TAB in the caller's own workspace, without taking focus, and admits only pi and claude-code, refusing placement by name; everywhere else it launches one of five fixed backends (pi, claude-code, copilot, omp, codex) as a visible window on the operator's own tmux server. There is no fallback between the two — an incomplete herdr context is refused by its herdr reason rather than quietly opening a tmux window nobody inside herdr can see. Everything that follows in this paragraph describes the tmux rail; the herdr rail's coordinates, refusals and reclaim are owned by docs/herdr-launch-rail.md. On tmux it requires an explicit model, accepts one optional literal absolute cwd (omit it or pass "" to use the caller's cwd) and one optional placement.tmuxSession naming an EXISTING session on that server to open the sibling in instead of the caller's own (the operator's project seat — nothing is ever created: an absent session is tmux-session-missing and a name outside the addressed grammar [A-Za-z0-9][A-Za-z0-9_-]* is tmux-session-name-invalid, with no window and no session left behind. The two inputs never infer each other, and the positive form of that matters more than the prohibition: with cwd omitted, a seated sibling starts in THIS agent's own working directory, measured — it does not inherit the target session's path or its active pane, so naming the org seat does not put the sibling in the org project. Pass cwd when you want a different directory. entwurf never resolves that path: it hands the literal string to tmux, so with a SYMLINKED directory the receipt echoes what you asked for while the record, the transcript path, the callback envelope and pane_current_path all show the path the OS resolved it to — two spellings of one place, and the resolved one is what entwurf_resume_call later reopens), passes the model and selected directory through the runtime's visible launch path, and hands it a first task; a copilot launch goes through entwurf's own managed invocation and is refused before any window opens if this host lacks the Copilot birth, MCP, receiver or visible-footer units, and an omp launch carries its task in the --entwurf-bootstrap payload instead of an argv prompt because that vendor connects its MCP tools after the session has already started. Omitted placement follows the CALLER rather than the backend being opened: a Codex CALLER opens its sibling beside its own TUI pane, located by the thread-id that pane's terminal title carries (0 or 2+ matching panes reject with no fallback); every other caller opens in its own seat. (0.21.0 shipped a backend-selected default instead — an omitted-placement codex TARGET resolved a fixed operator-owned session named codex — and #95 D1 retired it on 2026-09-16.) Any of the five can still be sent to an operator-owned existing seat with an explicit placement.tmuxSession, and named claude/pi seats remain available that way, but they are not omitted-placement defaults. Read tmux here as the VISIBLE LAUNCH SEAT, never as an address: the garden id is the address, and it arrives in the callback envelope. Sibling symmetry means each backend keeps its own transcript, auth, native tools and visibility — not that the transports are made identical. The sibling's first action is a nonce callback whose sender envelope carries its garden id. The launch receipt records the requested model/cwd/seat plus tmux coordinates — including the RESOLVED target session id, which is where the window actually is — and never claims that the runtime accepted them or completed delivery. (The v1 verbs entwurf / entwurf_resume / entwurf_send are gone.) Garden-native operator commands require --entwurf-control. There is no spawn target allowlist — the target registry is gone (#50 C3): entwurf_v2 and entwurf_resume_call address an existing record-backed citizen, while entwurf_fresh_call takes its explicit backend/model/task and optional cwd directly rather than resolving a model tuple from a registry file. To reopen a DORMANT pi citizen use entwurf_resume_call {target} — the record supplies transcript, model, provider and cwd, so it takes no prompt, no task and no model override; it runs no turn, returns a LAUNCH receipt and a separate OBSERVATION receipt (only the second says the citizen is back), refuses a non-pi target as target-not-pi, and on an unobserved socket leaves the visible window open and releases its lock rather than retrying.

entwurf_v2 — canonical dispatch verb

entwurf_v2 / runEntwurfV2 is the canonical v2 dispatch verb over existing garden targets — record-backed citizens only (#50 C4: the record is the sole address authority; a record-less control socket rejects pre-probe as record-less-socket, a diagnostic state, never a delivery target). You give a target garden id plus an intent (fire-and-forget — the axis is single-valued since the visible-first cut); one decider reads the target's liveness as a fact and picks the transport from a frozen table keyed on both the target's state and the intent — never on state alone — then reports one outcome under the v2 lock policy. A control-socket-domain dispatch takes a per-target lock; mailbox and native-push are lock-free, with deliverability guarded by their own receiver/probe evidence:

| target state | intent | transport | |---|---|---| | live pi | fire-and-forget | control-socket send | | dormant pi | fire-and-forget | reject (dormant-fire-forget-unsupported — nothing is launched) | | any pi | indeterminate probe | reject (indeterminate-no-spawn — an unestablished probe is not a measured death) | | active self-fetch receiver | fire-and-forget | meta-mailbox enqueue + doorbell | | inactive / terminated self-fetch receiver | fire-and-forget | reject (mailbox-undeliverable — no .msg, no doorbell) | | live native-push conversation | fire-and-forget | native-push direct injection | | dead / indeterminate native-push conversation | fire-and-forget | reject (native-push-target-dead / native-push-probe-indeterminate) | | record-less control socket (no meta-record) | any | reject (record-less-socket — pre-probe; diagnostic state, #50 C4) |

entwurf_v2 is the canonical surface for garden-id delivery. When you have a garden id and want to reach whoever it names — message, reply, or hand-off — entwurf_v2 is the one surface that reads whether the target is live pi, dormant pi, mailbox-backed Claude Code, Copilot or OMP, or native-push Antigravity or Codex, and routes correctly; when unsure which transport, use entwurf_v2. This prevents callers from guessing a rail from the shape of an id.

What v2 provides is a deterministic dispatch substrate that moves the "which transport?" decision out of the fallible caller/model and into the decider, with transport-appropriate locking and an honest reject (no ✓ delivered, no .msg garbage) when a target cannot receive. It still does not mint siblings, and it does not relaunch one either: every row above either reaches a citizen that is already running or refuses. Reopening a dormant pi citizen is entwurf_resume_call, a separate lifecycle verb that never routes through this decider. Fresh creation is the separate entwurf_fresh_call verb. Inside herdr it opens a Pi or Claude Code sibling in a new tab in the caller's own workspace; everywhere else it opens one of five fixed runtimes — Pi, Claude Code, Copilot, OMP or Codex — visibly on the caller's tmux server. The rest of this paragraph is the tmux rail. Omitting placement follows the CALLER rather than the backend being opened: an explicit seat always wins; then a Codex CALLER opens beside its own TUI pane, found by the thread-id in that pane's terminal title (0 or 2+ matching panes reject, with no fallback); then the caller's own session. An explicit placement.tmuxSession sends ANY of the five to an existing named session instead (see the entwurf_fresh_call paragraph above for the owning detail). Each launch carries a required explicit model passed in that runtime's measured CLI dialect and one optional literal absolute cwd; omitted or "" means the caller's cwd. Copilot opens through entwurf copilot, never the bare vendor; OMP is the opposite — the bare omp runtime with no positional prompt at all, because that vendor connects its MCP tools after the session has started, so the task rides a two-stage --entwurf-bootstrap payload the installed birth extension releases once the callback tool has actually answered. It returns only a synchronous launch receipt and lets the sibling report its new address asynchronously through the sender envelope of a nonce callback. Use this cwd input for a new cross-repository sibling; do not resume a dormant citizen as a cwd substitute. The meta-mailbox row requires an active self-fetch receiver; native-push requires a record-backed, probe-alive native conversation and never borrows mailbox state. The mux launch lane owns placement, fixed-runtime launch, and the two narrow compositions above it (fresh-call and resume-call placement); delivery does not import launch, and mux is not a delivery transport.

A live pi target is reached over its control socket, but the socket is dispatch-internal transport, never identity (#50 C4). A control socket that no meta-record claims — a pre-record-era resident, an unreadable store, or a stale/planted file — is refused for every intent as record-less-socket, and the reject names the fix (restart the resident so session_start births its record, or quiesce and run the fresh-cut). entwurf_peers reports the same state as an aggregated record-less-socket diagnostic rather than a peer row.

Direction. An Entwurf core (peer identity / garden id / inbox / liveness / dispatch / replyability / evidence) could later extract into its own repo with per-backend plugins; today this repo holds the v2 core + meta-bridge + ACP plugin together. ACP is one plugin, not the boundary — rationale: #38.

Garden launcher

A --entwurf-control session needs no special launcher (#50 C2): pi mints its own session id (a uuidv7 is normal), and session_start attaches the session to its meta-record — the record mints the garden id, keys the control socket on it, and PI_SESSION_ID carries that garden address to every MCP child. The old --session-id "$(run.sh new-session-id)" injection contract (and the header-id guard it fed) is gone; run.sh new-session-id survives only as the garden-id generator the record layer itself uses. Launch is simply:

pi --entwurf-control

Opening pi inside herdr. herdr starts a supported agent with its canonical executable and no extra argv — measured on herdr 0.9.0, herdr agent start <name> --kind pi --pane <id> runs plain pi. Citizenship is argv-gated (pi-extensions/entwurf-control.ts), so a pi opened that way is a running pi and not a garden citizen: no record, no control socket, no garden id. Pass the flag through herdr's own argv passthrough:

herdr agent start <name> --kind pi --pane <pane-id> -- --entwurf-control

A sibling that entwurf itself opens already carries the flag; this is only for a pi you start from herdr by hand. The claude axis needs nothing extra — its record is minted by the meta-bridge plugin's own SessionStart hook, which entwurf installs and herdr does not touch. Where such a citizen is visible is reported as evidence in entwurf_peers (placement=herdr <pane>), never as an address: docs/mux-launch-rail.md §7-a.

The wiring those two axes need does not have to be installed by hand on a Herdr host: the Entwurf Herdr plugin's install-time build reads Herdr's own integration status, and for every harness in {pi, claude-code} that Herdr reports as integrated it acquires an Entwurf runtime at a stable Entwurf-owned address and registers the user-scope wiring against it — no Entwurf clone, no manual npm install. herdr plugin install junghan0611/entwurf/plugins/herdr is the whole command, every reinstall is the refresh trigger, and entwurf herdr-plugin-deactivate is the explicit inverse (Herdr's own uninstall removes only its checkout and calls no cleanup hook). plugins/herdr/README.md owns that lane, including which acquisition source the committed lock names.

Resuming an existing garden session. Use entwurf_resume_call {target}. It reopens a DORMANT pi citizen under the SAME garden id in a visible window in the caller's own tmux session, resolving the transcript, model, provider and cwd from the record — so it takes only the target id, and it runs no turn: the window comes back with the conversation and waits, and talking to it is still entwurf_v2. Two receipts arrive and mean different things: a LAUNCH receipt (tmux made a window and was asked to start pi) and an OBSERVATION receipt (the control socket answered under the same id, or resume-unobserved). Unobserved is a real outcome, not an error to retry — the window is visible, so read it. A citizen that is already live is refused; so is a non-pi target (target-not-pi), because only pi stands a control socket up. The predecessor, entwurf_v2 intent=owned-outcome, resumed by launching a hidden window-less background child and was withdrawn under the visible-first rule; delivery still starts no process. Identity preconditions live in resume-launch-identity.ts, gated by check-resume-launch-identity.

Starting a new session in-process — pi's own /new. Since the #50 C2 cut there is nothing to replace it with: /new, /fork, /clone and RPC session replacement are pi's again. The replacement session fires session_start, which upserts its own meta-record and rebinds the control socket to that record's garden id; the old socket is dropped. pi's session id (a uuidv7) is recorded as the citizen's nativeSessionId and is never an address. Gate: run.sh smoke-resident-garden-guard REPLACEMENT section (0-token RPC E2E).

Context carriers

System / developer carriers and rich pi context are separate.

The carrier holds an optional short operator engraving; empty or missing is fine. The runtime default is the bundled pi-extensions/lib/acp/prompts/engraving.md (the # Engraving Here placeholder, pinned non-empty by a gate); prompts/engraving.md is a documented sample you copy and point the runtime at with ENTWURF_ACP_ENGRAVING_PATH=/path/to/alt.md. Template variables: {{backend}}, {{mcp_servers}}. Do not put AGENTS.md, bridge narrative, or tool catalogs he