@junghanacs/entwurf 0.41.1 → 0.45.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/AGENTS.md +2 -1
  2. package/BASELINE.md +7 -5
  3. package/CHANGELOG.md +344 -0
  4. package/DELIVERY.md +37 -20
  5. package/ENTWURF.json +236 -0
  6. package/README.md +75 -65
  7. package/VERIFY.md +9 -9
  8. package/docs/external-mcp-host.md +1 -1
  9. package/docs/setup-clean-host.md +59 -33
  10. package/mcp/entwurf-bridge/dist/ENTWURF.json +236 -0
  11. package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +32 -190
  12. package/mcp/entwurf-bridge/dist/pi-extensions/lib/agent-guidance.js +299 -0
  13. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-self-address.js +10 -0
  14. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-contract.js +23 -2
  15. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-decider.js +7 -1
  16. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-surface.js +23 -24
  17. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-visible-resume.js +15 -11
  18. package/mcp/entwurf-bridge/dist/pi-extensions/lib/fresh-call-composition.js +21 -10
  19. package/mcp/entwurf-bridge/dist/pi-extensions/lib/herdr-fresh-call.js +4 -2
  20. package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-sender-identity.js +5 -0
  21. package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +29 -24
  22. package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-resume-call.js +18 -16
  23. package/mcp/entwurf-bridge/dist/pi-extensions/lib/native-push/register.js +13 -2
  24. package/mcp/entwurf-bridge/dist/pi-extensions/lib/omp-fresh-preflight.js +2 -2
  25. package/mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-fresh-preflight.js +1 -1
  26. package/mcp/entwurf-bridge/dist/pi-extensions/lib/remote-pair-receive.js +25 -17
  27. package/mcp/entwurf-bridge/dist/pi-extensions/lib/remote-pair-send.js +101 -32
  28. package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-omp.js +57 -7
  29. package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-pi-durable.js +23 -45
  30. package/mcp/entwurf-bridge/dist/scripts/remote-pair-receive.js +12 -12
  31. package/mcp/entwurf-bridge/src/index.ts +126 -321
  32. package/native/win32-x64/entwurf_win.node +0 -0
  33. package/native/win32-x64/manifest.json +2 -2
  34. package/package.json +5 -4
  35. package/pi-extensions/lib/agent-guidance.ts +341 -0
  36. package/pi-extensions/lib/entwurf-self-address.ts +13 -0
  37. package/pi-extensions/lib/entwurf-v2-contract.ts +25 -2
  38. package/pi-extensions/lib/entwurf-v2-decider.ts +7 -0
  39. package/pi-extensions/lib/entwurf-v2-surface.ts +27 -30
  40. package/pi-extensions/lib/entwurf-v2-visible-resume.ts +15 -16
  41. package/pi-extensions/lib/fresh-call-composition.ts +21 -10
  42. package/pi-extensions/lib/herdr-fresh-call.ts +4 -2
  43. package/pi-extensions/lib/meta-sender-identity.ts +4 -0
  44. package/pi-extensions/lib/mux-fresh-call.ts +29 -30
  45. package/pi-extensions/lib/mux-resume-call.ts +18 -19
  46. package/pi-extensions/lib/native-push/register.ts +17 -2
  47. package/pi-extensions/lib/omp-fresh-preflight.ts +2 -2
  48. package/pi-extensions/lib/pi-durable-fresh-preflight.ts +1 -1
  49. package/pi-extensions/lib/remote-pair-receive.ts +27 -19
  50. package/pi-extensions/lib/remote-pair-send.ts +124 -34
  51. package/pi-extensions/meta-bridge-omp.ts +69 -7
  52. package/pi-extensions/meta-bridge-pi-durable.ts +23 -64
  53. package/run.sh +182 -22
  54. package/scripts/build-bridge.sh +10 -0
  55. package/scripts/check-agy-sender-identity.ts +6 -4
  56. package/scripts/check-capability-bundle-reach.ts +86 -0
  57. package/scripts/check-entwurf-self-address.ts +37 -6
  58. package/scripts/check-entwurf-v2-contract.ts +24 -2
  59. package/scripts/check-entwurf-v2-production.ts +45 -31
  60. package/scripts/check-entwurf-v2-surface.ts +94 -98
  61. package/scripts/check-gate-qualification.ts +17 -13
  62. package/scripts/check-mux-resume-call.ts +20 -3
  63. package/scripts/check-native-push-adapter.ts +4 -0
  64. package/scripts/check-native-push-register.ts +32 -82
  65. package/scripts/check-remote-pair-installed.ts +2 -2
  66. package/scripts/dev-bin.sh +112 -1
  67. package/scripts/lib/chain-terminus-publication.ts +58 -0
  68. package/scripts/lib/codex-fresh-live-protocol.ts +30 -0
  69. package/scripts/lib/codex-fresh-source-receipts.ts +112 -3
  70. package/scripts/lib/pi-durable-fresh-live-receipts.ts +29 -1
  71. package/scripts/mutants/agent-guidance.json +160 -0
  72. package/scripts/mutants/bridge-boot-resume.json +6 -8
  73. package/scripts/mutants/capability-bundle-reach.json +124 -0
  74. package/scripts/mutants/chain-terminus-publication.json +17 -0
  75. package/scripts/mutants/codex-native.json +98 -0
  76. package/scripts/mutants/control-send-receipt.json +23 -6
  77. package/scripts/mutants/mux-fresh-call.json +30 -4
  78. package/scripts/mutants/mux-resume-call.json +28 -3
  79. package/scripts/mutants/native-push-paused.json +17 -0
  80. package/scripts/mutants/omp-fresh.json +13 -2
  81. package/scripts/mutants/pack-install.json +46 -0
  82. package/scripts/mutants/pi-durable.json +17 -5
  83. package/scripts/mutants/remote-pair-display.json +3 -3
  84. package/scripts/mutants/remote-pair-receive.json +41 -11
  85. package/scripts/mutants/remote-pair-roundtrip.json +67 -7
  86. package/scripts/mutants/remote-pair-send.json +177 -19
  87. package/scripts/mutants/remote-pair-transport.json +4 -4
  88. package/scripts/mutants/self-address.json +10 -10
  89. package/scripts/mutants/source-install.json +66 -0
  90. package/scripts/mutants/v2-surface.json +50 -64
  91. package/scripts/mutants/v2-visible-resume.json +11 -0
  92. package/scripts/remote-pair-receive.ts +12 -12
  93. package/scripts/smoke-agy-install-state.sh +121 -0
  94. package/scripts/smoke-agy-native-push-live.ts +13 -0
  95. package/scripts/smoke-codex-fresh-live.ts +50 -27
  96. package/scripts/smoke-entwurf-chain-live.ts +7 -9
  97. package/scripts/smoke-herdr-fresh-call-live.ts +4 -4
  98. package/scripts/smoke-herdr-plugin-build-live.ts +179 -50
  99. package/scripts/smoke-mux-lifecycle-live.ts +140 -8
  100. package/scripts/smoke-omp-fresh-live.ts +5 -4
  101. package/scripts/smoke-pi-durable-fresh-live.ts +29 -7
  102. package/scripts/smoke-setup-verdict.sh +27 -2
package/AGENTS.md CHANGED
@@ -16,6 +16,7 @@ Ask: does this follow capability rather than a surface name; record backend asym
16
16
 
17
17
  ## Architecture
18
18
 
19
+ - **Product guidance:** root [ENTWURF.json](./ENTWURF.json) is the sole editable source for its extracted English payload families, currently MCP metadata, durable short variants (#154 C1-A), the static entwurf_v2 reject hints (C1-B), the static visible-resume (record/liveness) reject hints of entwurf_resume_call (C1-C), the static tmux launch-stage reject hints of entwurf_fresh_call and entwurf_resume_call (C1-D), five static delivery-result clauses of the entwurf_v2 dirty-lock and remote-pair receipts (E1a) and the text on either side of the record-less-socket reject hint's one code-owned slot (E1b). Compiled copies are deterministic transport, never another authority. Keep independent pre-extraction oracles intact; current metadata is checked against those originals plus explicitly reviewed literal amendment layers, not regenerated expectations. Payload revision is independent of releases/history-only edits. Metadata is not delivered to models; existing engraving/user-config contracts remain unchanged. Bootstrap, notices, the protocol-bearing mailbox envelope, receipt syntax and its annotations, other hints (including the slot-bearing model-invalid and task-too-long hints, the peers record-less-socket diagnostics, the shared ACP model hint, the preflight repair text kept beside its predicates and every herdr rail hint) and ACP text are not yet centralized.
19
20
  - **entwurf-core (v2):** garden-id addressing, record facts, rail liveness, dispatch decisions, and delivery evidence.
20
21
  - **Record authority:** every citizen uses V3 `MetaIdentity`; `backend` selects rail capability, never identity rank.
21
22
  - **pi adapter:** attaches its native session to a record, hosts the record-keyed control socket, and registers the compiled `entwurf-bridge` with Pi’s built-in MCP under that record’s identity.
@@ -35,7 +36,7 @@ Ask: does this follow capability rather than a surface name; record backend asym
35
36
  7. **One meta-store contract.** Record body and filename agree; active records are readable regular non-symlink V3 files; `nativeSessionId` is unique. Writers and doctor certify the active store before writing. Address-bearing reads use the addressable reader; `readStoreRecordFile`/`O_NOFOLLOW` and lstat policy both remain load-bearing. Quiesce and fresh-cut an unreadable generation; roll forward a stale reader that rejects a newly known backend or V3 key. Reader-version skew is not a generation change: preserve identities and records, and verify every participating reader before claiming rollout complete. Store protocol and gates: `meta-session.ts`, `check-meta-*`, `check-fresh-cut-gate`.
36
37
  8. **GC reclaims processes, not memory.** Preserve or archive records and transcripts; do not casually delete them.
37
38
  9. **Not a second harness.** No prompt reconstruction, transcript hydration, tool-result ledger, credential mediation, or harness emulation. Backends own auth and transcript.
38
- 10. **Native-push is distinct.** It is neither mailbox nor pi socket. Native-push citizens are record-backed plus probe-alive, with no receiver/watch state or resume authority. Antigravity retries only after bounded re-probe; Codex never replays queue acceptance. Codex sender identity is request-scoped metadata; app-server environment carries root/placement, never citizen identity.
39
+ 10. **Native-push is distinct.** It is neither mailbox nor pi socket. Native-push citizens are record-backed plus probe-alive, with no receiver/watch state or resume authority. Antigravity retries only after bounded re-probe, and its native push is paused (#152: one contract constant refuses dispatch and registration, and makes the sender non-replyable, before any probe); Codex never replays queue acceptance. Codex sender identity is request-scoped metadata; app-server environment carries root/placement, never citizen identity.
39
40
  11. **Package proof models a consumer.** Installed operator entrypoints reach compiled JS; `run_ts` is the sole TS crossing. Keep checkout, tarball, and consumer proofs distinct.
40
41
  12. **Offline verification never rewires the operator.** Sandbox HOME, `PI_CODING_AGENT_DIR`, and writable XDG roots. Only named LIVE gates may touch the host, and must say so.
41
42
  13. **Doctors report runtime and ownership independently.** Working runtime does not prove ownership; broken ownership does not erase runtime observation. Required failure on either axis is red.
package/BASELINE.md CHANGED
@@ -312,7 +312,9 @@ Per-question PASS / FAIL / NOTE for grading the model's response.
312
312
 
313
313
  ## Native-citizen baseline — Antigravity / agy (shipped)
314
314
 
315
- This is not an ACP overlay interview. Run it in a **fresh agy conversation** after
315
+ This is not an ACP overlay interview, and while agy native push is paused (#152) it is an
316
+ optional diagnostic run on request, not a release-cut requirement (VERIFY criterion 7).
317
+ Run it in a **fresh agy conversation** after
316
318
  `install-agy-bridge`, `install-agy-statusline`, and `install-agy-hooks`, with all
317
319
  three doctors green. `PreInvocation` is the earliest lifecycle event, so a brief
318
320
  `🪛 ? agy` before the first model invocation is honest; after that first invocation
@@ -321,13 +323,13 @@ the same conversation must have a garden id.
321
323
  | ID | Check | PASS | FAIL |
322
324
  |---|---|---|---|
323
325
  | Q-AGY-BIRTH | Automatic birth | First invocation creates/attaches one record by native `conversationId`; statusline becomes `🪛 <garden-id> agy`. | Manual cwd matching or `entwurf_register_native` is required for normal birth; a new id appears on every turn. |
324
- | Q-AGY-SELF | Sender identity | `entwurf_self` reports the same garden id, `origin=meta-session`, `agentId=meta-session/antigravity`, and `replyable:true` while the native probe is alive. | Anonymous `external-mcp`, unbacked marker accepted, model name substituted into `agentId`, or mailbox evidence used to infer replyability. |
325
- | Q-AGY-SEND | Outbound attribution | `entwurf_v2` from agy reaches a sibling carrying that same sender garden id and `replyable:true`. | Receiver sees unknown host/wrong garden id, or sender ambiguity is silently guessed. |
326
- | Q-AGY-REPLY | Same-conversation reply | Sibling replies with `entwurf_v2(target=<agy-gid>, intent=fire-and-forget)` and the message direct-injects into the same live agy conversation. | New conversation/spawn, mailbox file/doorbell, or a cosmetic delivered result with no live native route. |
326
+ | Q-AGY-SELF | Sender identity | `entwurf_self` reports the same garden id, `origin=meta-session`, `agentId=meta-session/antigravity`, and, while native push is paused (#152), `replyable:false` with a reason naming the pause and no probe run. | Anonymous `external-mcp`, unbacked marker accepted, model name substituted into `agentId`, or mailbox evidence used to infer replyability. |
327
+ | Q-AGY-SEND | Outbound attribution | `entwurf_v2` from agy reaches a sibling carrying that same sender garden id; while native push is paused (#152) the sender is `replyable:false`. | Receiver sees unknown host/wrong garden id, or sender ambiguity is silently guessed. |
328
+ | Q-AGY-REPLY | Same-conversation reply (paused, #152) | While the pause holds, a sibling's `entwurf_v2(target=<agy-gid>, intent=fire-and-forget)` returns `native-push-paused` with nothing probed or sent, and the agy record is unchanged. When the pause lifts: the message direct-injects into the same live agy conversation. | New conversation/spawn, mailbox file/doorbell, a probe or send while paused, or a cosmetic delivered result with no live native route. |
327
329
  | Q-AGY-OWNERSHIP | Install scope | MCP owns one server plus one narrow `mcp(entwurf-bridge/<tool>)` rule per normal-path tool (`entwurf_v2`, `entwurf_peers`, `entwurf_self`) and nothing else; statusline owns its subtree; hooks own one named hook. | Installer broadens YOLO policy (`command(*)`, `unsandboxed(*)`), grants tools it does not need (`entwurf_inbox_read`, `entwurf_register_native`), or overwrites unrelated settings/hooks. |
328
330
  | Q-AGY-CONCURRENCY | Evidence boundary | Separate agy processes have separate pid/start-key markers; same-pid concurrent model invocation is explicitly reported unsupported. | Claims that one pid can safely identify two simultaneously invoking conversations. |
329
331
 
330
- The replyability formula is **record-backed identity AND live native-push probe**.
332
+ The replyability formula is **record-backed identity AND live native-push probe**; while agy native push is paused (#152) the probe does not run and agy replyability is false by name.
331
333
  There is intentionally no receiver marker, `watchArmed`, mailbox, or resume
332
334
  authority (no rail has one since the visible-first cut). The model field may exist in the meta-record/status display,
333
335
  but `agentId=meta-session/antigravity` is the stable sender contract.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,350 @@ All notable changes to this project will be documented here. Format follows [Kee
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 0.45.0 — 2026-10-26
8
+
9
+ The text Entwurf hands to models is product code. A model chooses whether to resume, retry, wait or ask a person from the words in a tool description or a reject hint, so that wording is part of the contract, not decoration. Before this release those words lived as string literals scattered through the delivery, resume and launch modules, with no single place that said which text was current or which change had been reviewed. 0.45.0 gives the extracted text one versioned source, root `ENTWURF.json`, read by one strict reader and carried unchanged into the source, compiled and installed package, with the original pre-extraction text kept as an oracle and every semantic change recorded as a separate, reviewed amendment layer. The version moves from 0.41.2 straight to 0.45.0 to mark that milestone; there are no 0.42–0.44 releases.
10
+
11
+ What this release is not: it adds no wire value, algorithm, tool or permission, and changes no routing or authority decision. It does not centralize every authored string — bootstrap, notices, the protocol-bearing mailbox envelope, receipt syntax, the slot-bearing model-invalid and task-too-long hints and ACP text stay in code (listed below). It makes no claim that any model now behaves differently in practice; it fixes what the text says and where it is owned.
12
+
13
+ Herdr is not re-pinned to npm here. An npm lock must name a published version, and 0.45.0 has no published bytes while it is prepared, so `plugins/herdr/runtime-lock.json` returns to the closed `herdr-checkout` verification carrier, as earlier release candidates did. From the moment the prepare commit reaches main, a main-tracking Herdr install builds its runtime from the checked-out commit instead of npm, and a host already on the npm 0.41.2 runtime must run `herdr-plugin-deactivate` before it reinstalls. The published 0.41.2 pairing stays pinned by the Herdr tag `v2026.10.24-herdr.0.41.2`. The Herdr manifest version is unchanged; a separate snapshot re-pins npm after 0.45.0 is published.
14
+
15
+ ### Agent guidance has one source (#154)
16
+
17
+ - **Root `ENTWURF.json`** (schemaVersion 6, payload revision 11) is the sole editable source for these families: the MCP tool descriptions and their durable short variants; the five static `entwurf_v2` reject hints; the eleven static visible-resume (record/liveness) reject hints of `entwurf_resume_call`; the static tmux launch-stage reject hints of `entwurf_fresh_call` (19) and `entwurf_resume_call` (15), kept as two records because shared text between the verbs is a coincidence, not a contract; five delivery-result clauses of the `entwurf_v2` dirty-lock and remote-pair receipts; and the text on either side of the record-less-socket hint's one code-owned slot (the fresh-cut command stays owned by `meta-session.ts`).
18
+ - **One strict reader.** `pi-extensions/lib/agent-guidance.ts` imports only `node:fs`, checks every key in order and the schema version, and exports deep-frozen values. A missing, extra or empty key, or a wrong schema version, throws at load: invalid guidance crashes rather than falling back to an inline string.
19
+ - **The same bytes in every projection.** The package `files` array ships `ENTWURF.json`; `build-bridge` copies it byte for byte to the compiled bridge root, as transport, never as a second source. `check-capability-bundle-reach` proves the compiled reader exports the Root families and that the compiled delivery surface renders the slotted hint from the Root text around the compiled owner's command.
20
+ - **Payload revision is its own counter.** Each accepted payload change appends a history entry and advances `revision`; releases and history-only edits do not. `schemaVersion` tracks reader compatibility.
21
+ - **Originals stay as oracles.** Each extraction was compared byte for byte with an independently recorded inventory of `94d0594` before the product JSON was edited; those originals live in `pi-extensions/lib/fixtures/agent-guidance/` and are never regenerated. Current text is checked as original plus the explicitly reviewed amendment layers, each checking its exact old value. `check-agent-guidance-beside` joins `check:contracts`.
22
+
23
+ ### Agent-facing wording corrected
24
+
25
+ Each change below is a reviewed semantic amendment layer over the preserved original; wire reason ids, branch conditions and slots are unchanged.
26
+
27
+ - **Delivery, lifecycle and receipt are separate (revision 2).** The tool descriptions separate delivery, lifecycle and receipt authority, qualify what a resume or callback outcome does and does not show, and add short guidance for colleague-to-colleague messages.
28
+ - **Dormant socket wording is scoped (revision 3).** The intent parameter's `dormant-fire-forget-unsupported` rejection is stated for socket citizens only.
29
+ - **A paused dispatch probed nothing and sent nothing (revision 6).** `native-push-paused` now says so, because the decider refuses before any probe.
30
+ - **Uncertain outcomes are not stated as facts (revision 10).** A dirty lock no longer implies prior acceptance or a certain duplicate; `target-invalid` names address syntax, not a missing record; an indeterminate native probe no longer asserts that the host is running.
31
+ - **Recovery hints name the human operator (revision 11).** `target-receiver-absent`, `target-receiver-unbound` and `native-push-target-dead` say that reopening a window or conversation is the operator's act, not something the calling verb does; `entwurf_register_native` points to `entwurf_fresh_call` instead of calling a supported capability deferred; the `entwurf_v2` description names `mailbox-enqueued` on a re-resolved mailbox leg instead of claiming the four receiver boundaries are the whole union.
32
+
33
+ ### Still code-owned
34
+
35
+ Bootstrap and notices, the protocol-bearing mailbox envelope, receipt syntax and its annotations, the slot-bearing model-invalid and task-too-long hints, the peers record-less-socket diagnostics, the shared ACP model hint, the preflight repair text kept beside its predicates, every herdr rail hint, and ACP text. Moving any of them is later work, not implied by this release.
36
+
37
+ ### Upgrade
38
+
39
+ Nothing to convert. An installed package carries `ENTWURF.json`; a checkout-run operator reads it from the tree. Herdr hosts follow the source-switch note above.
40
+
41
+ ### Release evidence
42
+
43
+ - Kill-power re-measured at this shipped SHA: none (all 1245 committed claims not re-proved, #144).
44
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head 149fe24da006 surface 514b6f14eaa0 -- ["bash","run.sh","check-agent-guidance-beside"] 7/7 receipt d5a3342fd8f0
45
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head 149fe24da006 surface 514b6f14eaa0 -- ["bash","run.sh","check-entwurf-bridge-boot"] 5/5 receipt 2b18b407508b
46
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head 149fe24da006 surface 514b6f14eaa0 -- ["bash","run.sh","check-entwurf-self-address"] 8/8 receipt dc031f386bf5
47
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head 149fe24da006 surface 514b6f14eaa0 -- ["bash","run.sh","check-entwurf-v2-surface"] 17/17 receipt 7249f97714b1
48
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head 149fe24da006 surface 514b6f14eaa0 -- ["bash","run.sh","check-mux-fresh-call"] 160/160 receipt a44b36b8b7f3
49
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head 149fe24da006 surface 514b6f14eaa0 -- ["bash","run.sh","check-mux-resume-call"] 13/13 receipt d5e923601730
50
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head 149fe24da006 surface 514b6f14eaa0 -- ["bash","run.sh","check-pi-durable-beside"] 45/45 receipt f83b1385d9cf
51
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head 149fe24da006 surface 43b183b15789 -- ["bash","run.sh","check-agent-guidance-beside"] 8/8 receipt ab8eb9dc4248
52
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head 149fe24da006 surface 43b183b15789 -- ["bash","run.sh","check-capability-bundle-reach"] 1/1 receipt 279757fa6ef6
53
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head 149fe24da006 surface 43b183b15789 -- ["bash","run.sh","check-entwurf-v2-surface"] 17/17 receipt e695187eae31
54
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head a3f2abc26ed9 surface adea41b8f979 -- ["bash","run.sh","check-agent-guidance-beside"] 9/9 receipt 833b2055df1c
55
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head a3f2abc26ed9 surface adea41b8f979 -- ["bash","run.sh","check-capability-bundle-reach"] 2/2 receipt faddb8eb7995
56
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head a3f2abc26ed9 surface adea41b8f979 -- ["bash","run.sh","check-entwurf-v2-surface"] 18/18 receipt d1da0cff8802
57
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head 4a50aba502dd surface 563ff466619b -- ["bash","run.sh","check-agent-guidance-beside"] 10/10 receipt 9a9ffe7c4252
58
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head 4a50aba502dd surface 563ff466619b -- ["bash","run.sh","check-capability-bundle-reach"] 3/3 receipt 9fc14648c19e
59
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head 4a50aba502dd surface 563ff466619b -- ["bash","run.sh","check-entwurf-v2-visible-resume"] 41/41 receipt c194610cae09
60
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head 297ff9ea63fe surface 6e05a69e4c6d -- ["bash","run.sh","check-agent-guidance-beside"] 10/10 receipt 6f4149456809
61
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head d174fef0b943 surface a39c5f90d434 -- ["bash","run.sh","check-agent-guidance-beside"] 12/12 receipt 2ecc8270ebc5
62
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head d174fef0b943 surface a39c5f90d434 -- ["bash","run.sh","check-capability-bundle-reach"] 4/4 receipt 8bac9d11fa92
63
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head d174fef0b943 surface a39c5f90d434 -- ["bash","run.sh","check-mux-fresh-call"] 161/161 receipt 14d498185caf
64
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head d174fef0b943 surface a39c5f90d434 -- ["bash","run.sh","check-mux-resume-call"] 15/15 receipt a1c4efe73741
65
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head 08242648863b surface 6571de821ed1 -- ["bash","run.sh","check-agent-guidance-beside"] 13/13 receipt daaf09c7c9ae
66
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head 08242648863b surface 6571de821ed1 -- ["bash","run.sh","check-capability-bundle-reach"] 5/5 receipt 1bf305912814
67
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head 08242648863b surface 6571de821ed1 -- ["bash","run.sh","check-entwurf-v2-surface"] 19/19 receipt 696ec4d6e85c
68
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head 08242648863b surface 6571de821ed1 -- ["bash","run.sh","check-remote-pair-beside"] 48/48 receipt 90824d56e3a6
69
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head 3e8ade22b1c2 surface cdd2bf224af8 -- ["bash","run.sh","check-agent-guidance-beside"] 14/14 receipt 92182844e8df
70
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head 3e8ade22b1c2 surface cdd2bf224af8 -- ["bash","run.sh","check-capability-bundle-reach"] 7/7 receipt 9476e8787d6d
71
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head 3e8ade22b1c2 surface cdd2bf224af8 -- ["bash","run.sh","check-entwurf-v2-surface"] 20/20 receipt 6d1b40c6713f
72
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head 3e8ade22b1c2 surface 2d64d05cd9b9 -- ["bash","run.sh","check-agent-guidance-beside"] 14/14 receipt be9f054e160e
73
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head 3e8ade22b1c2 surface 2d64d05cd9b9 -- ["bash","run.sh","check-capability-bundle-reach"] 7/7 receipt a7d0564d8398
74
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head 3e8ade22b1c2 surface 2d64d05cd9b9 -- ["bash","run.sh","check-entwurf-v2-surface"] 20/20 receipt e619be0f0e3b
75
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head d3963c2d16a9 surface 8a3ad98f6cf1 -- ["bash","run.sh","check-agent-guidance-beside"] 14/14 receipt 8c108523fc45
76
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head d3963c2d16a9 surface 8a3ad98f6cf1 -- ["bash","run.sh","check-entwurf-v2-surface"] 20/20 receipt b2e06e3122d1
77
+ - Those groups were measured on uncommitted working trees with green controls, each on its own candidate; groups repeat across candidates and do not sum to release coverage.
78
+ - A stopped run at `7b24e5b` (first group non-zero) is not a passing receipt; its groups were re-measured at `149fe24` above. No group receipt is recorded for the first extraction commit `1657eca`.
79
+ - Pre-version landing: main `a4bea2d`, exact-SHA push CI run 40296075141, `check`, `install-surface`, `artifact-consumer` and `macos-install-surface` all success. That run belongs to the pre-version SHA, not this prepared SHA.
80
+ - Prepare-time floor, measured on the working tree over `a4bea2d` (tracked patch `7a8d5581fd82`, source manifest `f2dd32bb4fee`, before this record's final wording), not on an exact SHA: in an isolated copy, managed build 6 s and `check:full` 918 s exit 0; in the checkout, one managed build of 6 s produced a compiled tree byte-identical to that copy's.
81
+ - `release-gate --cut` on that same tree in the checkout: `check:full` 918 s exit 0, MUST 24/0/0, BEHAVIOR 1/0/0, cut OK, log `9b0bf7bbee8a`. Earlier attempts on this tree stopped for harness reasons — one RED run in a copy that had no compiled tree, one launcher stop before the gate began — and are not counted. The census afterwards was read-only.
82
+ - Exact prepared-SHA CI, the M3w artifact with its container acceptance, the installed remote-pair check on that same artifact, and native installed-host acceptance belong to the make phase and produce their own receipts.
83
+
84
+ ## v2026.10.24-herdr.0.41.2 — Herdr Entwurf plugin 0.41.2
85
+
86
+ A separate Herdr plugin snapshot, not another core release: core package 0.41.2 and its
87
+ `v0.41.2` tag remain unchanged. Core 0.41.2 is now published to npm; the publication is
88
+ verified from the registry, not from observing who ran it.
89
+
90
+ **Reading guide (2026-10-24).** The `## 0.41.2` section below is the text that shipped inside
91
+ the 0.41.2 tarball. Its statements that the Herdr lock rides the `herdr-checkout` carrier
92
+ while 0.41.2 is unpublished predate later facts: `v0.41.2` and its GitHub release point at
93
+ `a2980d6`, that SHA's push CI ran its four required jobs green, and npm `latest` is 0.41.2,
94
+ byte-equal to the accepted Windows-bearing candidate. Until this snapshot lands, a
95
+ main-tracking `herdr plugin install` acquires the checkout commit rather than an npm artifact.
96
+ Neither the published tarball's CHANGELOG nor the core release page is amended by this
97
+ snapshot.
98
+
99
+ ### Herdr plugin
100
+
101
+ - **Re-pin the published core 0.41.2 and close the checkout window.**
102
+ `plugins/herdr/runtime-lock.json` returns from the `herdr-checkout` carrier that the 0.41.2
103
+ candidate rode to production npm source `@junghanacs/entwurf@0.41.2` with the registry's
104
+ measured sha512 (`sha512-vONpYfVw…D5Et+A==`). The plugin manifest moves 0.41.1 → 0.41.2;
105
+ admission remains Herdr >= 0.9.0 and the one-build/one-pane/Linux shape is unchanged. From
106
+ the `v2026.10.23-herdr.0.41.1` snapshot to `v0.41.2`, the changes under `plugins/herdr/`,
107
+ `scripts/herdr-runtime.mjs` and `scripts/fixtures/herdr-supply.json` were that lock's return
108
+ to `herdr-checkout` and the plugin README's candidate wording; the runtime helper and the
109
+ Herdr supply pin did not change.
110
+ - **Pi stays at 1.4.** The acquired runtime carries the Pi 1.4.0 SDK set and accepts Pi
111
+ `>=1.4.0 <1.5`, the same range as the previous snapshot's 0.41.1.
112
+ - **Upgrade by acquisition source, not just version.** A settled npm-source 0.41.1
113
+ installation can rebind under the plugin README's ownership conditions. A host that
114
+ installed the 0.41.2 `herdr-checkout` candidate from main must explicitly deactivate by its
115
+ own absolute runtime path before reinstalling the npm-source snapshot. No operator
116
+ installation or active session is rewired by this release work.
117
+ - **What the acquired runtime carries, and what this path does not.** The runtime ships core
118
+ 0.41.2's bytes, including its nine-tool bridge, the pi-durable carrier and the paused agy
119
+ native push. Herdr wires and opens only `{pi, claude-code}`, so this plugin path launches no
120
+ pi-durable or agy session. Inside Herdr, `entwurf_resume_call` still refuses as
121
+ `herdr-resume-unsupported`.
122
+ - **README.** The runtime paragraph and the Pi requirement name npm 0.41.2 and point at the
123
+ previous snapshot's 0.41.1 on the same Pi 1.4.0 SDK set.
124
+ - **Current install docs.** The root README and the clean-host setup guide name the published
125
+ 0.41.2 in their install commands and tool counts, and the root README names this snapshot as
126
+ the Herdr pin. Moving a live host onto 0.41.2 remains a separate operator decision for each
127
+ host.
128
+
129
+ ### Source-specific verification
130
+
131
+ - Registry metadata reports `latest` 0.41.2 and `repair` 0.12.8-repair.1, 645 files. The
132
+ downloaded registry tarball is byte-equal (SHA256 and `cmp`) to the preserved accepted core
133
+ candidate (SHA256 `fad4cfe7e903e1d1f4cacb0e926814b32f3d73358d446b433f63ba6e13a2cafc`,
134
+ 11452583 bytes); its independently computed sha512 and sha1 equal `dist.integrity` and
135
+ `dist.shasum`.
136
+ - **Package consumer, reused.** Core make already ran its container consumer against those
137
+ same 0.41.2 bytes (acceptance log `5fd18a4d294e`, repoDigest `node@sha256:6dac556d…`, image
138
+ ID `sha256:55ab623b…`, recorded in make's artifact receipt), so that result is reused here,
139
+ not repeated.
140
+ - **Not re-measured for this pin:** the separate sandboxed global `npm install -g` form of the
141
+ registry package. The axes that form touched for Herdr (npm acquisition and integrity, the
142
+ installed runtime with its compiled entry, three bins and `check-bridge`, and the installed
143
+ CLI's deactivate path) belong to the public-ref journey below, which acquires through Herdr
144
+ itself.
145
+ - **The npm runtime swap against the real registry**, by the registry-shipped
146
+ `scripts/herdr-runtime.mjs` (byte-equal to this checkout's) in sandboxed user roots, 22/22
147
+ checks. The 0.41.1 runtime was seeded from its own committed lock (`439b399`); the swap to
148
+ 0.41.2 took one acquisition, the cached tarball's sha512 equals the lock and its sha256 equals
149
+ the accepted candidate. A manually seeded torn swap (not a killed process) was restored before
150
+ acquisition and reported `recovered: torn-swap-with-candidate`; an exact re-request acquired
151
+ nothing, and the owned removal reclaimed cache, runtime and journal. The helper probes no Pi,
152
+ so these cells certify neither a Pi upgrade nor Herdr's install/update relocation, harness
153
+ wiring, the installed CLI's deactivate path, a model turn, or recovery from a real kill.
154
+ - **Registry-installed package (Pi).** A sandboxed `pi install npm:@junghanacs/entwurf@0.41.2`
155
+ (Pi 1.4.0) resolved its bridge to the installed root inside the sandbox; the lock records the
156
+ registry tarball URL, version 0.41.2 and the integrity above; the installed compiled bridge
157
+ entry equals the accepted tarball's member; `--list-models entwurf` printed seven `entwurf`
158
+ rows including the curated Claude anchors, with no unknown-provider error. No model turn. The
159
+ run stripped every `PI_`, `ENTWURF_`, `CLAUDE`, `CODEX_HOME`, `TMUX`, `HERDR_` and npm
160
+ carrier. npm 11.16 listed the package's install scripts as not yet covered by `allowScripts`.
161
+ - Pending, and not claimed here: the pushed SHA's four-job CI and the public-ref
162
+ install/use/deactivate journey. That journey is a main-ref receipt for the SHA it resolved,
163
+ not a tag-ref install claim. Earlier npm or checkout-carrier receipts cannot certify these
164
+ axes.
165
+
166
+ ## 0.41.2 — 2026-10-24
167
+
168
+ A bug-fix release for the setup, pack and mux verification surfaces. It also carries #149's remote-pair declaration change, a hard cut: every host that has an `entwurf-remote-pair.json` must convert it by hand (Remote pairs below, setup §2c). It also pauses agy native push (#152, Antigravity below), a scope added while this release was being made. The release evidence below separates what was measured while this release was prepared from what the MAKE phase measures on the exact artifact and installed hosts. Herdr is not re-pinned to npm here. An npm lock must name this checkout's exact version, and 0.41.2 had no published bytes when this release was prepared, so `plugins/herdr/runtime-lock.json` returned to the closed `herdr-checkout` verification carrier, as earlier release candidates did. Since the prepare commit `1e66466` reached main, a main-tracking Herdr install builds its runtime from the checked-out commit instead of npm, and a host already on the npm 0.41.1 runtime must run `herdr-plugin-deactivate` before it reinstalls. The published 0.41.1 pairing stays pinned by the Herdr tag `v2026.10.23-herdr.0.41.1` (its section below). The Herdr manifest version is unchanged; a separate snapshot re-pins npm after 0.41.2 is published.
169
+
170
+ ### Release evidence
171
+
172
+ - Kill-power re-measured at this shipped SHA: none (all 1215 committed claims not re-proved, #144).
173
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head 439b399c64ef surface de3c1bcf6db9 -- ["bash","run.sh","check-remote-pair-beside"] 47/47 receipt 55bd9563fe43
174
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head 439b399c64ef surface de3c1bcf6db9 -- ["bash","run.sh","check-tests-beside-behavior"] 6/6 receipt aef18acbe804
175
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head 156bb6231cee surface c883d0df88b5 -- ["bash","run.sh","check-pack-argv-matcher"] 4/4 receipt 11470c62389c
176
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head e7ed0576ea65 surface 9bff5948bcb3 -- ["bash","run.sh","smoke-agy-install-state"] 18/18 receipt 13b86f54457e
177
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head e7ed0576ea65 surface 9bff5948bcb3 -- ["bash","run.sh","smoke-setup-verdict"] 4/4 receipt ba3113f9661e
178
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head 3e2673ba9a1b surface 2b1c5c7c1d52 -- ["bash","run.sh","check-mux-launcher-fence"] 8/8 receipt d27f3ded70ab
179
+ - Not measured for this release: the real two-host exchange (T3) over the v2 remote-pair file. It is opt-in operator evidence, not a release-gate MUST (VERIFY, "Real two-host exchange"), and it was not re-run for this candidate.
180
+ - `LIVE=1 ./run.sh smoke-herdr-plugin-build-live` on Herdr 0.9.5, measured on an earlier working tree (diff `40f12860ffbf`, smoke `15744e13f9bf`), not on this SHA: 635 s, 5 passed, 0 skipped. The Claude cell ran the real vendor CLI. The gap install exited 1 with the registry unchanged and the ledger on the gap commit, and the reconcile then re-bound to HEAD with 0. Log `0a9f293a155c`. A fixture pack is not a release artifact.
181
+ - Prepare-time floor and release gate, measured on the working tree `80c40705809c` (fixture source `15744e13f9bf`, before this record's final wording), not on an exact SHA: managed build 6 s with dist byte-identical, `check:full` 905 s exit 0. `release-gate --cut`: MUST 24/0/0, BEHAVIOR 1/0/0, cut OK, log `8c18f08880d3`. The census afterwards was read-only.
182
+ - Prior candidate, kept and not inherited: the prepare commit `1e66466`, its push CI, its M3w artifact `df407c70c0b2` and that artifact's container and Claude installed acceptance were measured before the #152 pause. They are evidence for that candidate only; the paused product bytes do not inherit them.
183
+ - #152 PARTIAL (operator-attested, not this SHA): head 1e664667a663 surface 81cbb2888465 -- ["bash","run.sh","check-native-push-paused"] 1/1 receipt 2f262170bf98
184
+ - #152 PARTIAL (operator-attested, not this SHA): head 1e664667a663 surface 81cbb2888465 -- ["bash","run.sh","check-entwurf-v2-contract"] 1/1 receipt 0c624bad9b67
185
+ - #152 PARTIAL (operator-attested, not this SHA): head 1e664667a663 surface 81cbb2888465 -- ["bash","run.sh","check-entwurf-v2-production"] 5/5 receipt f7dd4d94c275
186
+ - #152 PARTIAL (operator-attested, not this SHA): head 1e664667a663 surface 81cbb2888465 -- ["bash","run.sh","check-entwurf-self-address"] 8/8 receipt 3c223a9034d8
187
+ - Those four groups were measured on an uncommitted working tree with green controls; the other 1189 of 1204 committed mutants were not run for them. They are not shipped-SHA kill-power and do not sum to release coverage.
188
+ - #152 candidate floor, before this record's final doc amendment: `check:full` 900 s exit 0 on the uncommitted working tree with tracked patch `f883ba29c22d` (log `ce9ca5df53eb`), not this SHA.
189
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head 100f618e2b42 surface 61f6ab3d25c1 -- ["bash","run.sh","check-chain-terminus-publication"] 1/1 receipt 6a8af23eb259
190
+ - That group was measured on an uncommitted working tree over `100f618` with green controls; the other 1204 of 1205 committed mutants were not run for it, and it is not shipped-SHA kill-power.
191
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head ed6490fdc949 surface e966ed7447e8 -- ["bash","run.sh","check-tests-beside-behavior"] 6/6 receipt cecf3a83a675
192
+ - `LIVE=1 ./run.sh smoke-entwurf-chain-live` on the chain-terminus candidate (uncommitted, tracked patch `4df5f793fb04`), not this SHA: 24 assertions, 24 s, exit 0, log `e5115bbeb06d`; every identity and read-receipt check after the doorbell was reached.
193
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head bad44f4beb6c surface cd1627ae7b7c -- ["bash","run.sh","check-mux-fresh-call"] 160/160 receipt 33b6f81cff24
194
+ - That group was measured on an uncommitted working tree over `bad44f4` (the bootstrap repair) with green controls; the other 1055 of 1215 committed mutants were not run for it, and it is not shipped-SHA kill-power.
195
+ - Guided caller check, `LIVE=1 ./run.sh smoke-codex-fresh-live` on an earlier uncommitted candidate (tracked patch `a5b3833b9ebd`), not this SHA: 66 assertions, exit 0, the initial Pi's peers prefix 1, log `753dd4b24b6c`. It is one run of the path the task asked for, not evidence of a sibling's own judgement, and the earlier outbound-callback timeout (P5 r4) is still unexplained.
196
+ - Exact prepared-SHA CI and the M3w artifact with its container acceptance belong to the final prepared SHA and produce their own receipts. §3.7 is VERIFY criterion 7's deterministic paused proof inside `check:full`; it needs no install.
197
+ - Release gate and native Claude acceptance after install (§3.4), measured on `4f14a4d` (the bootstrap checkpoint, before the docs-only agy-scope change), not this SHA: `release-gate --cut` log `fcb5c64dd70a`, its `check:full` 923 s exit 0, MUST 24/0/0, BEHAVIOR 1/0/0, cut OK. Installed `doctor-meta-bridge` exited 0 (log `0065d7a78d8e`). A new Claude Code session ran the acceptance prefix's MCP entry `8ca6d4cb4b90`, the same bytes as the tarball member, and sent its own self-report. The checkout re-registration and its doctor passed afterwards.
198
+ - Also on `4f14a4d`, not this SHA: `smoke-omp-fresh-live` 21 assertions after the managed OMP install (log `e2c91d7683cc`), and `smoke-herdr-fresh-call-live` 32 assertions (log `bbfe0386b2c6`).
199
+ - Those `4f14a4d` receipts stand for this SHA only if the final artifact differs from the `4f14a4d` artifact `37b4670837d2` in exactly BASELINE.md, CHANGELOG.md, VERIFY.md and docs/setup-clean-host.md. That comparison is an operator-handoff receipt, not part of this record.
200
+
201
+ ### Docs
202
+
203
+ - **remote-sibling-principles now carries #149.** Several relationships coexist, each one exact sender–target edge, never a union of memberships or a local fallback. Record, process, screen and conversational continuity stay separate. Disjoint routes do not narrow SSH account trust. Implementation, switch-over and a real exchange are kept as separate evidence; no version-2 nonce round trip is claimed. The page keeps the why and links the mechanism to DELIVERY and setup.
204
+ - **README's orchestration section is an entrance, not a second manual.** It drops the lifecycle table it repeated from the overview and the acceptance-boundary detail DELIVERY owns, and adds two short entries: a minimal v2 remote-pair file with its mirror rule, linked to the principles page, DELIVERY and setup §2c; and `team.json`, a person's bookmark copied from `team.json.example`, which Entwurf does not load as runtime configuration.
205
+
206
+ ### Remote pairs (#149)
207
+
208
+ - **One host's declaration now carries several exact pairs.** `entwurf-remote-pair.json` keeps its name and location. Its content is now exactly `{"version": 2, "routes": [...]}`, and each route has the four keys of the #107 file. One local citizen may pair with several remotes, one remote with several locals, and one alias may carry several routes. The file is refused if a pair repeats, a garden id is both local and remote, one remote garden id has two aliases or files, or one alias has two files. A route is selected by the exact (sender, target) pair. A target declared remote with no route for the calling sender is refused with zero SSH and no local fallback. The receiving entry matches both ends of one route at once, so a sender declared in one route cannot reach another route's local. It still refuses with the two existing reasons (`remote-pair-from-mismatch`, `remote-pair-target-mismatch`). The claim, SSH argv, exit codes and receipt are unchanged.
209
+ - **Hard cut, converted by hand.** The versionless single-pair file is refused by name with its manual rewrite; there is no legacy reader. Setup §2c gives the per-host order. While a host's bytes and its file disagree, every `entwurf_v2` call of its bridges is refused, local targets included; a running bridge keeps the code it loaded. Trust stays same-account SSH: every key that account accepts can claim any garden id its file declares.
210
+ - **Evidence.**
211
+ - T1 cells cover the v2 shape, the legacy refusal, the set rules, exact-pair selection with no local fallback, the cross-pair refusal and the matched-route label.
212
+ - The T2 surface-in scene adds a third host and a second citizen on one root, giving three routes in one file: fan-out, a shared remote, the reverse, an undeclared sender with zero SSH, and a cross claim refused on the receiving host.
213
+ - The remote-pair mutants run through the new narrow coordinate `check-remote-pair-beside`, which is in `check:contracts`.
214
+ - Not measured: a real exchange over the v2 file (T3 must be re-run for the schema change) and a sender and a receiver on different releases.
215
+
216
+ ### Antigravity native push paused (#152)
217
+
218
+ - **agy native push is paused.** On the external send path entwurf measured, the agy language server refused the `agentapi` call with `Unauthenticated: missing CSRF token` (#128 on agy 1.2.16, again on agy 1.4.6). Whether a first-party own-tool shell holds a token entwurf could legitimately present is unmeasured; this pause does not claim it is impossible. One fixed constant in the v2 contract drives three refusals before any probe: `entwurf_v2` rejects an agy target as the new pre-probe reason `native-push-paused` with `observedLiveness: null`, an agy sender's `replyable` is false with a reason naming the pause, and `entwurf_register_native` refuses without probing or minting a garden id. Existing agy records, the backend enum, the record reader and the schema are unchanged, so agy citizens stay readable and listed with their birth hook and statusline. Codex native push is unaffected. The fresh-cut quiescence probe stays outside the pause and keeps failing closed.
219
+ - **Acceptance changes with it.** VERIFY criterion 7 proves the pause with the deterministic `[QK:AGY-NATIVE-PUSH-PAUSED]` cell, which needs no agy conversation; the doctors and the fresh-conversation identity check of the retained legacy identity surfaces are an optional diagnostic run on request, not a cut requirement, and are not send evidence. A release does not claim the installed agy host is certified or can receive. A same-gid reply is not claimed while the pause holds. `smoke-agy-native-push-live` exits 2 with `[entwurf:paused]` before it reads a conversation id, and that is not a PASS. The mutant for that cell runs through the narrow coordinate `./run.sh check-native-push-paused`, which is also part of `check:contracts`.
220
+
221
+ ### Fixed
222
+
223
+ - **A fresh sibling may check its caller before calling back, and may hold back.** The framing no longer calls the callback the literal first action. A read-only `entwurf_peers` check may come first, and a capped listing that does not show the caller proves no absence. A sibling whose check does not hold up may hold the callback and the task; the caller then has no address for it. The callback correlates an address; it grants no trust and no authority, and Entwurf decides nothing on the sibling's behalf. omp's stage one reads the caller id from the launch env for display only, and a requested bootstrap without a well-formed one is refused with its task unsent. The LIVE audits admit only successful exact checks before one exact callback; that is those fixtures' condition, not a list of what a sibling may do. Pi and pi-durable prove the result order by row ordinals, Codex only the call order.
224
+ - **`smoke-entwurf-chain-live` could report a false RED while the terminus message was still being published.** `enqueueMetaMessage` writes the `.msg` body first and `inbox.signal` last. The smoke stopped waiting at the first body and checked the signal at once, and in the 0.41.2 release gate on 2026-10-24 the signal landed about 5 ms later. The wait now ends only when the body and the signal are both there, inside the same deadline. A missing body, a missing signal and a duplicate still fail, and the identity and read-receipt checks after it are unchanged. Product mailbox behavior is unchanged. The wait lives in `scripts/lib/chain-terminus-publication.ts` and is proven offline by `test/chain-terminus-publication.test.ts` through the narrow coordinate `check-chain-terminus-publication` (in `check:vitest`), with one mutant that restores the early stop.
225
+ - **The release MUST now drives `entwurf_fresh_call`'s explicit seat over the real MCP wire (#108).** Before this, `placement.tmuxSession` was checked on the bridge only through its schema and a source regex, and the seat was proven only by calling the composition directly. In `smoke-mux-lifecycle-live`, the native pi cell's existing `tools/call` now names a seat session made on the same private tmux server, with no extra model turn. The harness asks tmux itself (not the receipt, whose session id is the target the product chose) that the pane sits in that seat, that the caller's windows are unchanged, and that the stable-handle close removes only that window while the seat's original anchor session, window and pane stand. The ACP and Claude Code cells keep the omitted-placement path. Product code is unchanged.
226
+ - **`check-pack-install`'s two dispatcher-verb leak refusals could pass a leak on a SIGPIPE.** The copilot and codex-app-server cells searched the vendor stand-in's argv with `printf '%s' "$op_out" | grep -qF …` under `pipefail` and refused only when that pipeline exited 0. bash's `printf` writes a multi-line value one line at a time, so a `-q` that closes the pipe at the first match can kill the producer with 141 and pass the cell. The defect was latent: no false green was observed in CI, and the product carries no leak. Both cells now return through one verdict, `pack_install_refuse_argv_leak`. It searches a here-string, which has no producer to kill. It passes on a clean search, and refuses with the cell's existing diagnostic on a leak, or by name when the search itself fails.
227
+ - `check-pack-argv-matcher` is a snapshot-safe gate in `check:package`. Like `check-pack-pin-matcher`, it also runs first inside `check-pack-install`. It drives that same verdict: it must refuse a leak at the front or end of about 260 KB, pass clean output silently, and refuse a failing `grep`. A text check holds both cells to the verdict.
228
+ - A teeth control first requires the retired pipe shape to miss the front-loaded leak on the host. The mutant that restores that shape dies deterministically only because of this large fixture; on the real small output it would survive.
229
+ - The other pipe-to-`-q` lines reviewed alongside are unchanged: the pi-provider doctor and dev-only gate fence checks, which the next confirm-type check backstops; the meta-sessions `ls | grep -q .` cell; the `takeover-user-scope` and uninstall notes; and the confirm-type greps.
230
+ - **A source checkout's setup could report a developer install while an old global entwurf was still selectable.** The bins step certified only the PATH winner: `~/.local/bin/entwurf` into this checkout. An old pnpm global shim later in the same PATH stayed in place, and a non-interactive SSH shell, whose PATH puts that directory first, ran it with the old reader. Source setup now also refuses, by name, any other root under the six package bin names in every PATH entry and in the global bin directory that `pnpm bin -g` and `npm prefix -g` report.
231
+ - The same resolved target is the same install, so duplicate PATH entries and second links pass. An unreadable package manifest, or a package manager that fails or hangs, is refused rather than read as clean. pnpm 11 refuses `bin -g` while its global bin dir is off PATH, so that case is refused too.
232
+ - Nothing is removed and no profile is edited. The refusal prints the path, the conditional package-manager removal command and the next `./run.sh setup`. Installed mode is unchanged.
233
+ - A PASS covers only the directories checked: another shell's PATH, another package manager, a running bridge and the other host are outside it.
234
+ - The two setup smokes now drop host PATH entries that carry a package bin name and answer the global-bin queries with fixtures, so the host's own installation cannot reach their verdict.
235
+ - **`smoke-herdr-plugin-build-live` could not go green on the current Herdr and manifest.** Three fixture defects are fixed in the smoke alone; product code is unchanged.
236
+ - The snapshot and gap-probe clones now install their own frozen, offline, ignore-scripts dependencies before their ordinary commit, which the strict hook needs.
237
+ - Cell 1 locates the managed checkout from Herdr's own registration: exactly one `plugins.json` entry, whose `plugin_root` must resolve inside the sandbox. It no longer scans a `plugins/github/` layout that Herdr no longer writes.
238
+ - Cell 4's probe had replaced a `version = "0.1.0"` literal that the manifest stopped carrying at dd84ac0, so it changed nothing. It now derives the change from the version line it reads, and records the runner's code and the written change in a JSON receipt. The smoke accepts the post-build gap only when that receipt shows code 0 and a real change.
239
+
240
+ ## v2026.10.23-herdr.0.41.1 — Herdr Entwurf plugin 0.41.1
241
+
242
+ A separate Herdr plugin snapshot, not another core release: core package 0.41.1 and its
243
+ `v0.41.1` tag remain unchanged. Core 0.41.1 is now published to npm; the publication is
244
+ verified from the registry, not from observing who ran it.
245
+
246
+ **Reading guide (2026-10-23).** The `## 0.41.1` section below is prepare-time text. Its opening
247
+ "Unpublished candidate; release acceptance is not complete", its "Main still carries the
248
+ published 0.41.0 Herdr lock" and its closing "No tag, GitHub release, npm publication, Herdr
249
+ re-pin or laptop rollout has occurred" predate later facts: `v0.41.1` and its GitHub release
250
+ point at `6a88436` and that SHA's main push CI ran its four required jobs green ([#148 make
251
+ record](https://github.com/junghan0611/entwurf/issues/148#issuecomment-6353660427)), and npm
252
+ `latest` is 0.41.1 (published 2026-10-22T23:49:43.983Z), byte-equal to the accepted
253
+ Windows-bearing candidate ([#148 publication
254
+ record](https://github.com/junghan0611/entwurf/issues/148#issuecomment-6354276182)). When main
255
+ moved to `6a88436`, its Herdr lock was the `herdr-checkout` carrier, so until this snapshot
256
+ lands a main-tracking `herdr plugin install` acquires the checkout commit rather than an npm
257
+ artifact. Neither the published tarball's CHANGELOG nor the core release page is amended by
258
+ this snapshot.
259
+
260
+ ### Herdr plugin
261
+
262
+ - **Re-pin the published core 0.41.1 and close the checkout window.**
263
+ `plugins/herdr/runtime-lock.json` returns from the `herdr-checkout` carrier that the Pi 1.4.0
264
+ SDK candidate rode to production npm source `@junghanacs/entwurf@0.41.1` with the registry's
265
+ measured sha512 (`sha512-GEKfcpFC…xWqw==`). The plugin manifest moves 0.41.0 → 0.41.1;
266
+ admission remains Herdr >= 0.9.0 and the one-build/one-pane/Linux shape is unchanged. From
267
+ the `v2026.10.22-herdr.0.41.0` snapshot to `v0.41.1`, the changes under `plugins/herdr/`,
268
+ `scripts/herdr-runtime.mjs` and `scripts/fixtures/herdr-supply.json` were that lock's return
269
+ to `herdr-checkout` and the plugin README's candidate wording; the runtime helper and the
270
+ Herdr supply pin did not change.
271
+ - **Pi 1.4 is now required.** The acquired runtime carries the Pi 1.4.0 SDK set and accepts Pi
272
+ `>=1.4.0 <1.5`; the previous snapshot's runtime accepted Pi `>=1.3.0 <1.4`. Upgrade Pi to 1.4
273
+ first, then install or reinstall the plugin: a host still on Pi 1.3 is refused at activation
274
+ by name (`outside the supported range`), the check `entwurf setup` applies, and no Pi
275
+ settings are written.
276
+ - **Upgrade by acquisition source, not just version.** A settled npm-source 0.41.0
277
+ installation can rebind under the plugin README's ownership conditions. A host that
278
+ installed a `herdr-checkout` candidate — from main at `6a88436` or from a validation branch —
279
+ must explicitly deactivate by its own absolute runtime path before reinstalling the
280
+ npm-source snapshot. No operator installation or active session is rewired by this release
281
+ work.
282
+ - **What the acquired runtime carries, and what this path does not.** The runtime ships core
283
+ 0.41.1's bytes, including its nine-tool bridge and the pi-durable carrier. Herdr wires and
284
+ opens only `{pi, claude-code}`, so this plugin path launches no pi-durable session and makes
285
+ no claim about migrating durable-session storage. Inside Herdr, `entwurf_resume_call` still
286
+ refuses as `herdr-resume-unsupported`.
287
+ - **README.** The quick start and nine steps drop the candidate's `--ref "$CANDIDATE_REF"`
288
+ instructions and install against the ref Herdr resolves; the Pi step keeps `>=1.4.0 <1.5`,
289
+ and the runtime paragraph names npm 0.41.1 and points at the previous snapshot for Pi 1.3.x.
290
+ - **Current install docs.** The root README and the clean-host setup guide replace their "not
291
+ published to npm yet / newest published 0.41.0" install text with the published 0.41.1 and its
292
+ Pi 1.4.x requirement, and point at the previous 0.41.0 for Pi 1.3.x. Moving a live host onto
293
+ 0.41.1 remains a separate operator decision for each host, with the durable-session backup,
294
+ owner-exit and no-rollback boundaries unchanged.
295
+
296
+ ### Source-specific verification
297
+
298
+ - Registry metadata reports `latest` 0.41.1 and `repair` 0.12.8-repair.1, 642 files, published
299
+ 2026-10-22T23:49:43.983Z. The downloaded registry tarball is byte-equal (SHA256 and `cmp`) to
300
+ the preserved accepted core candidate (SHA256
301
+ `0172b049edde352d404b1119e330852b92ce235e3f2809e06d1ef0e71c95b95f`, 11406938 bytes); its
302
+ independently computed sha512 and sha1 equal `dist.integrity` and `dist.shasum`. Core make
303
+ already ran its container consumer against those same 0.41.1 bytes (repoDigest
304
+ `node@sha256:6dac556d…`, image ID `sha256:55ab623b…`, both recorded in make's artifact
305
+ receipt), so that result is reused here, not repeated. The new registry acquisition and
306
+ dependency resolution are closed separately: a sandboxed global npm install of the registry
307
+ package passed its installed `check-bridge` with nine tools, and a sandboxed `pi install`
308
+ resolved its bridge inside the sandbox, recorded the registry URL with that integrity, and
309
+ printed seven `entwurf` model rows (plus the header) including the curated Claude anchors.
310
+ The only checkout input of that `pi install` check, the bridge resolver script, is unchanged
311
+ on this branch. No model turn. The accepted run stripped every `PI_`, `ENTWURF_`, `CLAUDE`,
312
+ `CODEX_HOME`, `TMUX` and `HERDR_` carrier. These rows do not cover the runtime Herdr acquires
313
+ in use — its bins, its `check-bridge`, or the installed CLI's deactivate path — which belong
314
+ to the public-ref journey below. npm 11.16 listed the package's `postinstall` chmod as not
315
+ yet covered by `allowScripts`; whether that script ran was not checked. The installed
316
+ `entwurf` of the global install ran and its `check-bridge` exited 0.
317
+ - **The npm runtime swap against the real registry**, by the registry-shipped
318
+ `scripts/herdr-runtime.mjs` (byte-equal to this checkout's) in sandboxed user roots, 22/22
319
+ checks. The 0.41.0 runtime was seeded from its own committed lock (`addcb8c`); the swap to
320
+ 0.41.1 took one acquisition, the cached tarball's sha512 equals the lock and its sha256 equals
321
+ both the ready journal's observed digest and the accepted candidate. The `installing` journal
322
+ carried the 0.41.0 identity as `previousRuntime` and the `runtime-ready` journal carries
323
+ `null`; when acquisition began the 0.41.0 tree was still at the active address. A manually
324
+ seeded torn swap (the active tree moved to the backup address with a half candidate in
325
+ staging; not a killed process) was restored before acquisition and reported `recovered:
326
+ torn-swap-with-candidate`. An exact re-request acquired nothing and left journal and tree
327
+ byte-identical, and the owned removal reclaimed cache, runtime and journal. The helper is a
328
+ leaf that probes no Pi, so these cells certify neither a Pi upgrade nor Herdr's
329
+ install/update relocation, harness wiring, the installed CLI's deactivate path, a model turn,
330
+ or recovery from a real kill.
331
+ - **Focused gates, by surface.** On the pin surface (lock, manifest, plugin README and this
332
+ section before its later amendments), with HEAD, index and work surface unchanged across them:
333
+ `check-herdr-supply` (11 assertions), `check-herdr-plugin` (49),
334
+ `check-herdr-plugin-profile` (14), `check-herdr-runtime-bootstrap` (31),
335
+ `check-herdr-activation` (33), `check-herdr-placement` (25), `check-herdr-fresh-call` (45),
336
+ `check-herdr-plugin-build` (21, its real-binary cell on herdr 0.9.5), `check-herdr-sandbox`
337
+ (12, a private herdr 0.9.5 server), `check-dep-versions` (Pi 1.4.0 coherent),
338
+ `check-install-surface`, `check-claude-floor-coherence` and `check-gate-manifests` (1178
339
+ committed mutants across 85 lanes validated, none executed). After the root README, setup
340
+ guide and NEXT changes, the seven gates those documents affect ran again on that later
341
+ surface, unchanged across them: `check-dep-versions`, `check-node-floor-coherence`,
342
+ `check-claude-floor-coherence`, `check-pi-runtime-version`, `check-herdr-plugin` (49),
343
+ `check-install-surface` and `check-entwurf-v2-surface` (99). Neither set certifies the final
344
+ amended surface; that is closed by the frozen FULL.
345
+ - Pending, and not claimed here: the pushed SHA's four-job CI and the public-ref
346
+ install/use/deactivate journey of this npm pin, on Herdr 0.9.5 (the binary
347
+ `scripts/fixtures/herdr-supply.json` names) with Pi 1.4.0. Earlier npm or checkout-carrier
348
+ receipts cannot certify these source-specific axes. Those outcomes are recorded in the
349
+ snapshot's post-push acceptance ledger, not in this frozen text.
350
+
7
351
  ## 0.41.1 — 2026-10-23
8
352
 
9
353
  **Unpublished candidate; release acceptance is not complete.** The actual operator
package/DELIVERY.md CHANGED
@@ -126,7 +126,7 @@ physical host. Do not read a D-level cell as a Darwin receipt.
126
126
  |---|---|---|---|
127
127
  | **pi Entwurf** | shipped | D7; D8 partial | Record-addressed Unix control socket. A record-less socket is diagnostic only and never dispatched. A pi session calls the verbs through Pi's built-in MCP running the compiled `entwurf-bridge` (#125); the receive rail is unchanged. |
128
128
  | **Claude Code interactive `>=2.1.306`** | shipped; Linux certified | D6; D7/D8 partial | Per-session mailbox + exec-form `FileChanged`/`asyncRewake`. B2 proved idle wake and same-session continuity on one NixOS host. `>=2.1.306` is the current declared floor; that historical B2 receipt does not certify #147 resume/retire lifecycle on this floor. |
129
- | **Antigravity / agy** | shipped | D6; D7 partial | Record-backed native-push through LS gRPC `agentapi send-message`; no mailbox or receiver marker. Admitted before the #82 step 9 contract and not re-evaluated under it, so it is legacy citizen evidence, not a step-9 supported harness: `entwurf_fresh_call` cannot open an agy sibling, and nothing here should be read as claiming visible lifecycle parity. |
129
+ | **Antigravity / agy** | shipped, native push paused (#152) | D6; D7 partial | Record-backed native-push through LS gRPC `agentapi send-message` is paused: on the measured external path the agy LS refused `agentapi` calls for a missing CSRF token (#128; a first-party token path is unmeasured), so dispatch refuses an agy target pre-probe as `native-push-paused`. Birth, statusline and sender identity stay; no mailbox or receiver marker. Admitted before the #82 step 9 contract and not re-evaluated under it, so it is legacy citizen evidence, not a step-9 supported harness: `entwurf_fresh_call` cannot open an agy sibling, and nothing here should be read as claiming visible lifecycle parity. |
130
130
  | **Codex CLI app-server citizen** | supported in 0.21.0; caller-seat topology ACCEPTED on Linux at 0.153.4, 2026-09-16 (56 assertions, exit 0), and the caller-DIRECTORY axis ACCEPTED the same day on the same host (#95 lane C, 65 assertions, exit 0; the 2026-09-12 explicit-home acceptance is history — #95 D1 retired that room) | D6 accepted; D7 partial; D8 unproven | Vendor-trusted user-scope `SessionStart` birth plus strict per-request metadata join; no shared-pid sender marker. `thread/loaded/list` probes the operator-owned app-server UDS and one-shot `codex queue` delivers with no retry. The operator owns the app-server and seats it wherever they like; Entwurf creates, moves and supervises none of it. Since #95 lane B a Codex CALLER with no explicit placement opens its sibling beside its OWN pane, located by the `thread-id` its `[tui].terminal_title` writes into the pane title; 0 or 2+ matching panes refuse with no fallback, and the title is a placement input only — never an address, liveness or delivery fact. #95 D1 (2026-09-16) retired the fixed `codex` home that omitted-placement Codex TARGETS used to select, so an omitted seat is the caller's own session for every backend. Exact 0.153.4 source still bounds placing a sibling beside a TUI whose thread nobody named as unsupported; the anchor resolves only the pane showing the caller's own thread. |
131
131
  | **Codex embedded TUI** | deferred | D0 partial | At the 2026-09-08 Codex 0.153.4 measurement, standalone mode had no `watchPaths`/`FileChanged`/`asyncRewake` analogue or supported idle receive route. This is dated vendor evidence, not a claim that the current candidate lacks records or fresh. |
132
132
  | **Copilot CLI first-party extension** | raw transport probe; superseded by the owned product unit | D7 path observed; D3 control receipt incomplete; D8 unproven | CLI-spawned extension over stdio JSON-RPC; `joinSession()` + documented `fs.watch` → `session.send({mode:"enqueue"})`. Idle wake, exact-marker reply, and completion passed on 2026-08-23 (CLI 1.0.80, L4, one Linux host). Two-process isolation was observed but its decisive B log was not preserved. Kept as the transport receipt the owned receive unit was built on; the shipped unit differs deliberately — it announces the inbox instead of injecting the body. |
@@ -156,13 +156,24 @@ coalescing bounds, re-arm gaps, and crash/re-poke behavior are measured. A synth
156
156
  container doctor proves package/oracle shape, not a real Claude wake; a claimed host
157
157
  needs the installed strict doctor against a new native session.
158
158
 
159
- ### Antigravity: native push
159
+ ### Antigravity: native push (paused, #152)
160
160
 
161
161
  `PreInvocation` births or reattaches a citizen by native `conversationId` and writes a
162
- record-backed sender marker. `entwurf_v2` probes the live conversation and injects
163
- directly through the native adapter, with one bounded re-probe retry. Replyability is
164
- `record-backed identity ∧ probe-alive`; mailbox state does not exist on this rail, and no
165
- rail has resume authority since the visible-first cut.
162
+ record-backed sender marker. Native push is paused: on the external send path entwurf
163
+ measured, the agy language server refused the `agentapi` call with `Unauthenticated: missing
164
+ CSRF token` (#128 on agy 1.2.16, again on agy 1.4.6). Whether a first-party own-tool shell
165
+ holds a token entwurf could legitimately present is unmeasured, not ruled out. One fixed constant,
166
+ `NATIVE_PUSH_PAUSED_BACKENDS` in the v2 contract, drives three refusals before any probe:
167
+ `entwurf_v2` rejects an agy target as `native-push-paused` with `observedLiveness: null`, a
168
+ sender's `replyable` is false with a reason naming the pause, and `entwurf_register_native`
169
+ refuses without minting a garden id. Existing records, the backend enum and the record
170
+ reader are unchanged, so agy citizens stay readable and listed. The fresh-cut quiescence
171
+ probe is deliberately not behind the pause: it protects an operator-named archive and keeps
172
+ failing closed. When the pause lifts, the adapter path below applies again: `entwurf_v2`
173
+ probes the live conversation and injects directly through the native adapter, with one
174
+ bounded re-probe retry, and replyability is `record-backed identity ∧ probe-alive`.
175
+ Mailbox state does not exist on this rail, and no rail has resume authority since the
176
+ visible-first cut.
166
177
 
167
178
  The owned bridge, statusline, and hook installers own separate configuration atoms.
168
179
  Same-pid concurrent model invocation by multiple conversations is not claimed because
@@ -587,39 +598,45 @@ same class of capability through the CLI-owned stdio lifecycle. Absence of Claud
587
598
  `FileChanged` / `asyncRewake` / `watchPaths` therefore means only that Claude's hook
588
599
  mechanism cannot be copied — it never proved Copilot had no vendor wake surface.
589
600
 
590
- ### Remote pair (#107): one declared pair across two hosts
601
+ ### Remote pairs (#107, #149): declared pairs across hosts
591
602
 
592
- This is an opt-in route between two existing citizens on two hosts. Why it keeps these boundaries, and which questions it leaves open, is [remote-sibling-principles](./docs/remote-sibling-principles.md); this section owns the mechanism. It is off unless the operator places a file on each host. The operator owns the connection between the hosts (a private network such as Tailscale is recommended, not required) and owns SSH authentication and authorization. Entwurf installs, configures, discovers and retries none of it. It adds no relay, listener, queue or registry. One SSH process runs per send and then exits.
603
+ This is an opt-in route between existing citizens on two hosts. Why it keeps these boundaries, and which questions it leaves open, is [remote-sibling-principles](./docs/remote-sibling-principles.md); this section owns the mechanism. It is off unless the operator places a file on each host. The operator owns the connection between the hosts (a private network such as Tailscale is recommended, not required) and owns SSH authentication and authorization. Entwurf installs, configures, discovers and retries none of it. It adds no relay, listener, queue or registry. One SSH process runs per send and then exits.
593
604
 
594
- - **Declaration.** Each host has at most one `entwurf-remote-pair.json`, beside its meta-record store (`remotePairOptInPath()`: the pi agent dir, unless `ENTWURF_META_SESSIONS_DIR` moves the store and the file with it). It has exactly four keys:
595
- - `remoteGardenId`.
605
+ - **Declaration (#149).** Each host has at most one `entwurf-remote-pair.json`, beside its meta-record store (`remotePairOptInPath()`: the pi agent dir, unless `ENTWURF_META_SESSIONS_DIR` moves the store and the file with it). It is exactly `{"version": 2, "routes": [...]}` with a non-empty `routes` array. Each route is one exact pair with exactly four keys:
606
+ - `localGardenId`: the citizen on this host.
607
+ - `remoteGardenId`: the citizen on the other host.
596
608
  - `ssh`: an existing SSH alias whose characters need no quoting.
597
- - `localGardenId`.
598
609
  - `remotePairFile`: the other host's canonical absolute path to its own file.
599
610
 
600
- One file serves both directions. An absent file means off. A symlink, non-regular, unreadable or invalid file is refused. The file is read at every send and receive and never cached. The sending bridge and the SSH-spawned receive entry must resolve the same root; an override set in only one of those environments moves the file.
611
+ One route serves both directions of its pair. An absent file means off. A symlink, non-regular, unreadable or invalid file is refused, and so is an empty `routes`. The versionless single-pair object of 0.41 and earlier is refused by name with the manual rewrite (`{"version":2,"routes":[<that object>]}`); there is no legacy reader. The file is read at every send and receive and never cached. The sending bridge and the SSH-spawned receive entry must resolve the same root; an override set in only one of those environments moves the file.
612
+
613
+ The set of routes is checked as a whole, never by position:
614
+ - Allowed: one local citizen paired with several remotes, one remote paired with several locals, and several routes over one alias.
615
+ - Refused: the same exact pair twice; a garden id that is local in one route and remote in another (a route to itself included); one remote garden id with two different aliases or files; one alias with two different `remotePairFile` values, because the receive entry behind that alias accepts only its one canonical file.
616
+
617
+ A broken file is refused at every `entwurf_v2` call of that bridge, local targets included, because the bridge reads the file before it can tell a remote target from a local one. That is not a silent fallback, and it is a cost to keep in mind when editing the file.
601
618
  - **Send.** The sending host's `entwurf_v2` reads the file and then decides:
602
- - A target other than `remoteGardenId` takes the unchanged local path.
619
+ - A target that no route names as remote takes the unchanged local path.
603
620
  - A declared target that also has a local trace on this host (a record in any state, or a control socket) is refused as `remote-route-local-conflict`, with zero SSH.
604
- - A sender other than `localGardenId` is refused as `remote-sender-not-declared`, with zero SSH.
605
- - Otherwise it spawns one `ssh -o BatchMode=yes -o ConnectTimeout=10 <alias> entwurf remote-pair-receive --pair <remotePairFile>`, with no shell and the claim on stdin.
606
- - **Receive.** The entry accepts only when `--pair` equals its own canonical file path. Canonical means the normalized expected path, not realpath equivalence: a spelling through a symlinked home alias is refused. That file is the entry's whole scope. It refuses a `from` or `target` outside the declared pair before any mailbox mutation. A match goes to that host's existing local `entwurf_v2` dispatch, so every deliverability rule of the target's rail applies there; the measured pair used the self-fetch mailbox.
621
+ - Otherwise the route is the one whose two ends are this sender and this target. If there is none, the call is refused as `remote-sender-not-declared`, with zero SSH and no local fallback. That also covers an anonymous sender.
622
+ - That route spawns one `ssh -o BatchMode=yes -o ConnectTimeout=10 <alias> entwurf remote-pair-receive --pair <remotePairFile>`, with no shell and the claim on stdin.
623
+ - **Receive.** The entry accepts only when `--pair` equals its own canonical file path. Canonical means the normalized expected path, not realpath equivalence: a spelling through a symlinked home alias is refused. That file is the entry's whole scope. It looks for the one route whose remote end is the claim's `from` and whose local end is the claim's `target`, comparing both ends in that same route. A `from` that no route declares is refused as `remote-pair-from-mismatch`. A declared `from` addressing anything but its own declared local, including another route's local, is refused as `remote-pair-target-mismatch`. Both refusals come before any mailbox mutation. A match goes to that host's existing local `entwurf_v2` dispatch, so every deliverability rule of the target's rail applies there; the measured pairs used the self-fetch mailbox.
607
624
  - **Outcomes.** The send ends in one of three outcomes:
608
625
  - the other host's receipt, quoted verbatim (its paths are on the other host). This is either an acceptance or an error the other host reported while processing (`remote-reported-error`, exit 4). That error can follow a write, for example an enqueue whose later step threw and is reported `retry-safe: false`. The sender's header therefore claims neither non-delivery nor safety to resend; the quoted text is the fact;
609
626
  - the other host's named refusal, made before any mutation: a pair mismatch (exit 3) or invalid input (exit 2);
610
627
  - `remote-indeterminate`.
611
628
 
612
629
  A result is decided only when the exit code and the JSON kind agree. There is no retry. An unanswered message stays unconfirmed. Acceptance is not a read or a reply. Neither side learns whether its last message was read unless the other side answers, and there is no third ACK.
613
- - **Display.** The received envelope names the remote origin and route, marks the sender's cwd as a sender-host path, and carries `replyable=false`, with "reply route declared, not probed" stated separately. Display is proven only for the self-fetch mailbox body; control-socket and native-push rendering are not proven. `entwurf_callback` stays local.
614
- - **Trust.** The model is same-operator SSH account trust. The receiving host checks the declared pair. It does not cryptographically authenticate a citizen, and the same account could claim either declared id.
630
+ - **Display.** The received envelope names the remote origin and the matched route's alias, marks the sender's cwd as a sender-host path, and carries `replyable=false`, with "reply route declared, not probed" stated separately. Display is proven only for the self-fetch mailbox body; control-socket and native-push rendering are not proven. `entwurf_callback` stays local.
631
+ - **Trust.** The model is same-operator SSH account trust. The receiving host checks the declared pairs. It does not cryptographically authenticate a citizen, and the same account could claim any declared id. With several routes, everything allowed to log in to that account is inside this boundary. That includes the keys of every other host. Separating them is the operator's SSH policy; Entwurf adds no per-route or per-key binding.
615
632
  - **Visibility.** For the measured self-fetch pair, delivery requires backing records, live owners and armed watches, so an OFF citizen of that rail is undeliverable. Any other target keeps its own rail-specific local admission. The remote route has no launch, fresh or resume path. The code does not check that the target's screen is open: an owner pid, a TTY or a garden-id label is not visibility. Live use addresses only siblings the user has open, and delivery to an active pi-durable can start a turn at once.
616
633
  - **SSH side effects.** The transport adds exactly two options, `BatchMode=yes` and `ConnectTimeout=10`. Host-key checking, known-hosts writes and every other SSH policy remain the operator's, as configured. Their side effects on a real exchange were not measured.
617
634
 
618
635
  Evidence:
619
636
 
620
- - T1 (executor-in) and T2 (surface-in) are deterministic. T2 drives the real bridge over MCP stdio, and a stand-in `ssh` on PATH runs the other host's real entry under a separate HOME. An installed-tarball gate runs the T2 cells against installed bytes.
637
+ - T1 (executor-in) and T2 (surface-in) are deterministic. T2 drives the real bridge over MCP stdio, and a stand-in `ssh` on PATH runs the other host's real entry under a separate HOME. Since #149 it also runs three hosts, where one host's one file carries three routes: fan-out, a shared remote, both directions, an undeclared sender with zero SSH, and a cross claim refused on the receiving host. An installed-tarball gate runs the T2 cells against installed bytes.
621
638
  - One real exchange is recorded in [#107](https://github.com/junghan0611/entwurf/issues/107#issuecomment-6278665895), at branch commit `e632294` on 2026-10-20. A nonce went B→A→B between two already-open pi-durable siblings on two Linux hosts. That is L4: one pair, one run.
622
- - Unmeasured: an OFF target, negative paths over real SSH, other rails, more than one pair, and non-Linux hosts.
639
+ - Unmeasured: an OFF target, negative paths over real SSH, other rails, more than one pair over real SSH (the #149 schema has no T3 yet, so the #107 exchange does not carry over to it), a sender and a receiver on different releases (the claim, argv, exit codes and receipt are unchanged, so that interoperation is inferred, not measured), and non-Linux hosts.
623
640
 
624
641
  ## Recording a new claim
625
642