oo-claude v0.1.0 — the Claude-house transport (inbox doorman, send, resume, start, read) #1
Loading…
Reference in a new issue
No description provided.
Delete branch "oss-oo/oo-claude-v0.1.0"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What
oo-claude v0.1.0 — the Claude house's transport (the counterpart of
oo-appserverfor the Codex house): steer a running Claude Code session, resume a sleeping one for one headless turn, start a new one the family can steer while it works, read transcripts, list sessions with riders (sub-agents), inboxes and lineage. One helperoo-claude, modules inlibexec/, Python 3.12 stdlib only.The law it builds on
Claude Code (≥ 2.1.308) keeps one Unix-socket door per running session, but the receiver carries a line to the model only when the sending process descends from the receiving session's own
claudeprocess (measured 2026-10-28/30,PINGPONG-20261030.md). So senders only append a line to the session's inbox file, and the session's own doorman — its MCP stdio server, a child of itsclaudeprocess — delivers it through the door. Verified live in this round (preflight + proof B): the line surfaces mid-turn, the transcript recordsorigin.kind: peerwithverifiedPeerPid= the doorman,absorbed_mid_turn, and the session quotes it.Contract
Implements the inbox file contract v1 (
~/.zcode/tmp/claude-inbox/CONTRACT-v1.md, quoted verbatim in the README) — paths, heartbeat (fresh < 15 s), ack JSON with the sha256 of the exact line bytes, receiver offset, sender classes and exit codes — and the lineage ledger v0. Extensions are extra keys only (ackline_offset/inode/from/…, heartbeatstate/peer_pid/…, ack classrefusedfor undeliverable lines, a.doorman.lock).Verbs, classes, exit codes
send SESSION "line" [--from] [--json] [--wait] [--min-interval] [--no-tag]doorman --mcp(registration,start) ·doorman --allow-foreground [--session] [--once]server/discover→ -32601 →initialize); foreground exit 0 / 2 no--allow-foreground/ 3 not deliverable here / 4 delivery failed, line waitsresume SID "line" [--model] [--max-turns] [--budget] [--tools] [--cwd] [--timeout] [--json]start "prompt" [...] [--systemd] [--no-doorman | --user-mcp]read SESSION [--turns N] [--json]from+msg_idsessions/riders [SESSION][--json] [--all] [--write]oo-claude/sessions/v0(reference field names kept) incl. riders, inbox,host_session/parent_*/childrenlineage register --parent … / lineage showbychild|parent|dispatcherdoctorDoorman safety: the owner is the nearest ancestor with a live registry row (else a
claudeexecutable); an env socket is used only when served by the owner (<pid>.sockname orSO_PEERCRED), every delivery checks the listener withSO_PEERCRED(measured: the session'sclaudepid) — an inherited socket variable of a grand-parent session or aclaude mcp listhealth check never carries a line into the wrong session. One deliverer per inbox (flock), no double delivery on replay, monitor-era inboxes adopted without replaying history.Tests
Hermetic suite (fake registry, fake door socket, fake
claude, fakesystemd-run, a listener outside the test's process tree, aclaude-named wrapper; subprocesses never see the calling session's door variables, but keep every otherCLAUDE*name plus a fake desktop identity so an env leak shows): 104 tests, locallypython3 -m unittest discover -s tests→Ran 104 tests … OK(pytest is not installed on the station); CIci / check (pull_request)green on0cf2232withpython3 -m pytest -q tests→104 passed, pluspy_compile,bash -nand an install into a scratch HOME with--check. Mutation check: the per-deliverySO_PEERCREDcheck, the env-socket ownership check and the explicit---sessionrule each fail the suite when disabled (evidence/proof-A2/mutants.txt).Scope addenda (coordinator, 2026-10-30 20:4x–20:5x)
Riders (
oo-claude riders, riders column insessions), the observatory schemaoo-claude/sessions/v0with--write(field names ofclaude-riders.pykept, fields only added; riding = changed within 600 s and not ended on a final answer), and the lineage ledger v0 (lineage register|show,host_session/parent_host/parent_session/childreninsessions --json, children indented;startrecords the calling session as parent per the contract).Live proofs (2026-10-30, Claude Code 2.1.308,
claude-haiku-5-5; evidence~/.zcode/tmp/OSS-OO/oo-claude/evidence/)claude -p; the session answered with it (0.014 USD)preflight/delivered-steer, ack after 0.6 s; transcript peer row withverifiedPeerPid= doorman pid 877283,absorbed_mid_turn; answer quotes the line (0.015 USD). The requestedsleep 25was refused by the session's harness ("standalone sleep"), the turn ran ~11 s of tool rounds and the line landed between themproof-B/send-receipt.json,session-A-result.json,transcript-rows.jsonl,session-A-debug-excerpt.txt-p(no idle state) — the coordinator proves it in a desktop session after the installdelivered-resume, resultPONG(0.028 USD); second running session →held-by-session(registry row + process scan), untouchedproof-D/resume-receipt.json,held-receipt.jsoncompletedREADY(0.015 USD);--systemdreturned at once with unitoo-claude-ffde609f, completed in 4 s (0.001 USD)proof-E/sessions,riders,read,doctor,lineage showoutputsproof-F/89ad161delivered-steer(ack 1.0 s, peer rowverifiedPeerPid= doorman,absorbed_mid_turn, quoted; 0.014 USD); E2--systemdcompletedREADY, result file names the unit at return and keeps the lineage block (0.012 USD)proof-B2-head/,proof-E2-head/| B3, D2, E3 on head
a3825ec(after the fold) | B3delivered-steer(ack 0.8 s; the line arrived 0.23 s after the last tool round and was answered as the next turn,turnOrigin: peer); D2delivered-resumePONG, resumed rowsentrypoint: sdk-cli; E3 foreground +--systemdcompletedREADY,doorman: injected,doorman_heartbeat= the injected doorman |proof-B3-fold/,proof-D2-fold/,proof-E3-fold/|| R1 measurement on
96f0c3a|start --max-turns 1 --budget 0.05, one line steered in during turn 1: a follow-up turn ran aftermax_turns_reached(--max-turnsper turn); its budgetusedcarried over (--max-budget-usdper process); the single result saidsuccess/STEERED/num_turns 1(turn 1's outcome not in it),total_cost_usd= the process total;doorman_delivered 1+ note |r1-measure/MEASUREMENT.txt|Total model cost of all live runs: ≈ 0.20 USD. Proofs B, D, E ran on the working tree before three small fixes (start's result file keeps unit + lineage, sender-name check + upper-case ids in
send, a type guard for rider meta); B2/E2 repeat B and the systemd start on the pushed head89ad161.bf15561added two hardening changes covered by the suite (single-write JSONL appends; a heartbeat whose state is notfollowingis not fresh); the review fold is4bc135d..c536951.Review v1 fold (REVIEW-OO-CLAUDE-v010-v1: no must-fix, six should-fix, 17 nits)
resume/start(foreground) anddoctordrop everyCLAUDE*variable exceptCLAUDE_CONFIG_DIR+ provider switches;--systemduses the same keep-list. Live: resumed/started turns recordentrypoint: sdk-cli(D2, E3)refused(sha256 over the whole line), offset moved past it; testpeer-mismatch, nothing written) and a free-named env socket (→socket-not-mine); mutants killed--sessionwithout a registry rowsession-unverifiable(an explicit id is never trusted on the environment alone); test via aclaude-named owner without a rowstart's injection; the foreground mode needs--allow-foregroundstartinjects--mcp-configholding only/usr/bin/python3 -I <libexec>/doorman.py --mcp+--strict-mcp-config;--no-doorman,--user-mcp; result JSONdoorman+doorman_heartbeat(E3: the injected doorman ran)pending_bytes; N3 at-least-once wording; N4echo 0 > <inbox>.offset+ retire the Monitor tail; N5-32603per-message guard +thread_alive; N7 name fragments ≥ 4 chars; N8--from/ structuredfromsanitised; N9 per-session resume lock; N11 lineage idempotent against the newest link; N12 parent from the nearest registered ancestor; N13 no inbox GC (documented); N14/usr/bin/python3 -Ireciperunningis defined by the registry; doorman freshness is indoorman.fresh), N10 (documented under Known gaps instead of a--forceflag), N15 (Claude Code's own labelling of MCP stderr), N16 (the not-descendant branch needs a tree without anyclaudeancestor — CI covers it; the new wrapper tests make the other identity branches deterministic), N17 (the doorman never changes files it did not create; the dir is 0700)Review v2 fold (re-review of
c536951: one should-fix, three nits)startrecords only the final turndoorman_delivered+ anotewhen > 0; README Known gaps +start --help; measured once (row above):--max-turnsper turn,--max-budget-usdper process,total_cost_usd= process total (corrects the reading of B3: its 0.0148 is the process total, turn 2 cost about 0.0009) — onlyresult/subtype/num_turnsdescribe the final turn; v0.2:--output-format stream-json, keep every result--systemdnever passes tokens or descriptors (nor companion variables — documented)--no-doormanand--user-mcpare mutually exclusive (exit 2); testNot done (the coordinator's steps)
claude mcp add --scope user claude-inbox -- python3 /home/claudiu/tools/oo-claude/libexec/doorman.py --mcp) andclaude mcp list✔ Connected; install from the tag; merge and release.Open questions
None open from round 1 — the coordinator decided Q1–Q4 (table above).
🤖 Generated with Claude Code
WIP: oo-claude v0.1.0 — the Claude-house transport (inbox doorman, send, resume, start, read)to oo-claude v0.1.0 — the Claude-house transport (inbox doorman, send, resume, start, read)