@junghanacs/entwurf 0.40.0 → 0.41.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 (118) hide show
  1. package/AGENTS.md +4 -2
  2. package/BASELINE.md +55 -2
  3. package/CHANGELOG.md +321 -0
  4. package/CONTRIBUTING.md +1 -1
  5. package/DELIVERY.md +18 -2
  6. package/FAQ.md +8 -4
  7. package/README.md +26 -8
  8. package/VERIFY.md +13 -10
  9. package/docs/acp-backend-rail.md +5 -1
  10. package/docs/external-mcp-host.md +8 -6
  11. package/docs/fresh-cut-policy.md +9 -1
  12. package/docs/setup-clean-host.md +24 -8
  13. package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +81 -19
  14. package/mcp/entwurf-bridge/dist/pi-extensions/lib/cc-retire-pidfd.py +236 -0
  15. package/mcp/entwurf-bridge/dist/pi-extensions/lib/cc-retire-request.js +473 -0
  16. package/mcp/entwurf-bridge/dist/pi-extensions/lib/control-retire.js +125 -0
  17. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-retire-production.js +149 -0
  18. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-retire.js +158 -0
  19. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-surface.js +2 -1
  20. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-visible-resume.js +283 -20
  21. package/mcp/entwurf-bridge/dist/pi-extensions/lib/fresh-call-composition.js +7 -0
  22. package/mcp/entwurf-bridge/dist/pi-extensions/lib/fresh-call-dispatch.js +47 -3
  23. package/mcp/entwurf-bridge/dist/pi-extensions/lib/herdr-fresh-call.js +3 -1
  24. package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js +96 -16
  25. package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +5 -29
  26. package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-launch.js +28 -0
  27. package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-resume-call.js +26 -11
  28. package/mcp/entwurf-bridge/dist/pi-extensions/lib/pi-retire-request.js +76 -0
  29. package/mcp/entwurf-bridge/dist/pi-extensions/lib/resume-launch-identity.js +67 -17
  30. package/mcp/entwurf-bridge/dist/pi-extensions/lib/retirement-event.js +211 -0
  31. package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-hook.js +124 -10
  32. package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-pi-durable.js +53 -2
  33. package/mcp/entwurf-bridge/dist/scripts/meta-facts.js +1 -1
  34. package/mcp/entwurf-bridge/dist/scripts/probe-bridge-command.js +1 -0
  35. package/mcp/entwurf-bridge/src/index.ts +93 -19
  36. package/mcp/entwurf-bridge/test.sh +2 -2
  37. package/mcp/entwurf-bridge/tsconfig.build.json +10 -1
  38. package/package.json +4 -4
  39. package/pi/meta-bridge/entwurf-meta-receive/scripts/hook-launch.sh +3 -3
  40. package/pi-extensions/entwurf-control.ts +55 -20
  41. package/pi-extensions/lib/cc-retire-pidfd.py +236 -0
  42. package/pi-extensions/lib/cc-retire-request.ts +553 -0
  43. package/pi-extensions/lib/control-retire.ts +195 -0
  44. package/pi-extensions/lib/entwurf-control-rpc.ts +17 -1
  45. package/pi-extensions/lib/entwurf-v2-retire-production.ts +227 -0
  46. package/pi-extensions/lib/entwurf-v2-retire.ts +269 -0
  47. package/pi-extensions/lib/entwurf-v2-surface.ts +2 -1
  48. package/pi-extensions/lib/entwurf-v2-visible-resume.ts +390 -36
  49. package/pi-extensions/lib/fresh-call-composition.ts +15 -0
  50. package/pi-extensions/lib/fresh-call-dispatch.ts +45 -4
  51. package/pi-extensions/lib/herdr-fresh-call.ts +5 -0
  52. package/pi-extensions/lib/meta-session.ts +140 -16
  53. package/pi-extensions/lib/mux-fresh-call.ts +7 -27
  54. package/pi-extensions/lib/mux-launch.ts +29 -0
  55. package/pi-extensions/lib/mux-resume-call.ts +33 -12
  56. package/pi-extensions/lib/pi-retire-request.ts +111 -0
  57. package/pi-extensions/lib/resume-launch-identity.ts +104 -18
  58. package/pi-extensions/lib/retirement-event.ts +253 -0
  59. package/pi-extensions/meta-bridge-hook.ts +142 -13
  60. package/pi-extensions/meta-bridge-pi-durable.ts +56 -2
  61. package/run.sh +78 -7
  62. package/scripts/agy-bridge-config.py +4 -1
  63. package/scripts/build-bridge.sh +14 -0
  64. package/scripts/check-capability-bundle-reach.ts +135 -2
  65. package/scripts/check-entwurf-bridge-boot.ts +2 -1
  66. package/scripts/check-entwurf-fact-provider.ts +1 -0
  67. package/scripts/check-entwurf-v2-production.ts +2 -0
  68. package/scripts/check-entwurf-v2-surface.ts +7 -0
  69. package/scripts/check-gate-qualification.ts +14 -8
  70. package/scripts/check-herdr-activation.ts +2 -2
  71. package/scripts/check-herdr-placement.ts +1 -0
  72. package/scripts/check-hook-launch-topology.ts +6 -15
  73. package/scripts/check-install-container.sh +2 -2
  74. package/scripts/check-meta-doctor-oracle.sh +27 -1
  75. package/scripts/check-meta-facts.ts +8 -3
  76. package/scripts/check-meta-hook-session-switch.ts +230 -13
  77. package/scripts/check-meta-identity-consumers.ts +1 -0
  78. package/scripts/check-meta-listing.ts +1 -0
  79. package/scripts/check-meta-manifest-schema.py +1 -1
  80. package/scripts/check-meta-session.ts +31 -0
  81. package/scripts/check-meta-v3-record.ts +55 -1
  82. package/scripts/check-mux-resume-call.ts +35 -9
  83. package/scripts/check-peer-facts.ts +1 -0
  84. package/scripts/check-pi-durable-contact.ts +3 -1
  85. package/scripts/check-pi-durable-send.ts +4 -2
  86. package/scripts/check-pi-mcp-bridge.ts +1 -0
  87. package/scripts/check-resume-launch-identity.ts +62 -23
  88. package/scripts/lib/meta-hook-bundle.ts +72 -0
  89. package/scripts/meta-bridge-claude-floor.sh +13 -9
  90. package/scripts/meta-bridge-doctor.sh +17 -0
  91. package/scripts/meta-bridge-install.sh +6 -0
  92. package/scripts/meta-facts.ts +1 -1
  93. package/scripts/mutants/bridge-boot-resume.json +5 -2
  94. package/scripts/mutants/cc-retire-pidfd.json +129 -0
  95. package/scripts/mutants/cc-retire-request.json +171 -0
  96. package/scripts/mutants/control-retire.json +105 -0
  97. package/scripts/mutants/copilot-receive.json +2 -6
  98. package/scripts/mutants/entwurf-v2-retire.json +476 -0
  99. package/scripts/mutants/fresh-call-dispatch.json +37 -0
  100. package/scripts/mutants/meta-hook-session-switch.json +88 -0
  101. package/scripts/mutants/meta-identity.json +57 -0
  102. package/scripts/mutants/mux-fresh-call.json +1 -1
  103. package/scripts/mutants/mux-resume-call.json +40 -4
  104. package/scripts/mutants/pi-durable.json +79 -0
  105. package/scripts/mutants/pi-retire-request.json +72 -0
  106. package/scripts/mutants/resume-launch-identity.json +35 -8
  107. package/scripts/mutants/retirement-event.json +195 -0
  108. package/scripts/mutants/v2-surface.json +16 -0
  109. package/scripts/mutants/v2-visible-resume.json +283 -13
  110. package/scripts/probe-bridge-command.ts +1 -0
  111. package/scripts/smoke-agy-install-state.sh +2 -2
  112. package/scripts/smoke-copilot-mcp-state.sh +1 -1
  113. package/scripts/smoke-herdr-raw-install-live.sh +5 -4
  114. package/scripts/smoke-meta-install-state.sh +4 -4
  115. package/scripts/smoke-mux-lifecycle-live.ts +10 -10
  116. package/scripts/smoke-omp-mcp-state.sh +1 -1
  117. package/scripts/smoke-pi-durable-fresh-live.ts +5 -3
  118. package/scripts/smoke-pi-provider-state.sh +3 -3
package/AGENTS.md CHANGED
@@ -32,7 +32,7 @@ Ask: does this follow capability rather than a surface name; record backend asym
32
32
  4. **Live-fact dispatch.** Never store liveness or infer send/resume before resolving the record and probing its rail.
33
33
  5. **Rejects are real.** Wrong intent, dead/drifted identity, undeliverable mailbox, ambiguity, and indeterminate probe reject without mutation or fallback.
34
34
  6. **MCP and native modules are explicit.** MCP uses only explicit `mcpServers`. Native extension modules enter only through the owning bootstrap, from an operator-named absolute file or an operator-named absolute directory read once at launch: top level only, exact suffix filter, deterministic order, every selected entry a regular file. Neither discovers ambient or default locations, takes module paths from settings or environment, recurses, watches, or retrieves modules automatically. Native modules do not emulate a foreign extension API.
35
- 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; redeploy a stale unknown-backend reader. Store protocol and gates: `meta-session.ts`, `check-meta-*`, `check-fresh-cut-gate`.
35
+ 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
36
  8. **GC reclaims processes, not memory.** Preserve or archive records and transcripts; do not casually delete them.
37
37
  9. **Not a second harness.** No prompt reconstruction, transcript hydration, tool-result ledger, credential mediation, or harness emulation. Backends own auth and transcript.
38
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.
@@ -66,8 +66,10 @@ History and incident detail belong in [CHANGELOG.md](./CHANGELOG.md), issue thre
66
66
 
67
67
  A pi control session and native bridge session are citizens for the same reason: each has a V3 record.
68
68
 
69
+ **Retirement is operator-requested, never autonomous.** A real citizen may be retired only when the operator explicitly requests that target/action, like the separate approval needed for a push. Development, tool exposure, a coordinator role, record-backed attribution or delegated release judgement is not a standing retirement grant. No timer, idle heuristic, roster cleanup or agent initiative substitutes for that request. Preserve request attribution without inventing a self-signed permission boolean or role database. Test-owned lifecycle gates require their own named execution scope; working siblings are never incidental test targets.
70
+
69
71
  - Pi owns its native session lifecycle; `birthPiCitizen` attaches `(backend: "pi", nativeSessionId)` to the stable garden id. Socket and env values are record-derived carriers.
70
- - `entwurf_resume_call {target}` is a separate pi-only lifecycle verb, not delivery intent. It reopens the same dormant id visibly, runs no turn, and receives transcript/model/provider/cwd solely from the record. A launch receipt and socket observation are distinct; an unobserved window stays visible. No watcher, retry, or supervisor.
72
+ - `entwurf_resume_call {target}` is a separate lifecycle verb for pi and Claude Code targets, not delivery intent. It reopens the same dormant id visibly, submits no prompt, and uses the record as the sole address authority (Pi: the record selects an absolute transcript whose native id must match; its session-header cwd and first `model_change` provider/model feed the launch resolver, with no record/host cwd fallback; Claude Code: record session id and cwd, with no model or permission flag restored). Dormancy is rail-specific: a dead pi control socket, or — for Claude Code — the last validated owner the Entwurf hook armed is proven gone (`classifyMarkerOwner` dead) AND the record, re-read after that verdict, still carries the same non-null `attachIncarnation` (arm revision) the marker copied; an absent, unreadable, mismatched, inconclusive or unbound marker refuses. That guarantees only committed hook attaches: a process holding the session without one (hooks disabled, a record-arm lock timeout or store failure at its start) is invisible to it — a boundary GLG accepted on 2026-10-21. A launch receipt and the observation (same-id socket, or a NEW live owner in the opened pane re-arming the marker with a fresh binding the record carries) are distinct; an unobserved window stays visible. The caller is attributed, never authorised by that. Inside herdr it refuses; there is no herdr resume rail. No watcher, retry, or supervisor.
71
73
  - **control-socket:** live send under per-target lock; dormant rejects.
72
74
  - **self-fetch:** active receiver plus deliverable mailbox; no resume authority. Sender/receiver joins are owner-kind specific and must not be generalized across vendors.
73
75
  - **native-push:** probe-alive direct injection; no mailbox and no resume authority.
package/BASELINE.md CHANGED
@@ -30,7 +30,7 @@ outside the experimental preview (rows below).
30
30
  |---|---|---|
31
31
  | Node 24 Linux package consumer | shape-only | Required `artifact-consumer` CI |
32
32
  | Node 24 macOS package consumer (GitHub Actions runner, not a physical Mac) | shape-only · CERTIFIED (CI) | Required `macos-install-surface` CI run 34303884286 @ `70eda03` (`macos-latest`, 65 s, 10/10 steps): pack → clean consumer install → bin links → `entwurf --help` → `check-bridge` (7 tools) → harness-absent `setup` (all SKIP, computed green, zero no-write-axis writes, `auth.json` byte-identical). Certifies no harness rail. |
33
- | Claude Code >=2.1.217 exec form | **certified** — supported floor | Topology + floor gates, doctor oracle; B2 live NixOS session |
33
+ | Claude Code exec form (observed at 2.1.217) | **certified** at 2.1.217; the supported floor is now `>=2.1.306` (#147 same-id resume), declared with no LIVE receipt at 2.1.306 itself; the dated n=1 lifecycle round trip below ran on 2.1.307 | Topology + floor gates, doctor oracle; B2 live NixOS session |
34
34
  | Claude Code 2.1.138 | **unsupported** | Launcher refuses empty argv; no shell-form fallback |
35
35
  | Maintainer NixOS installed package | **certified** for `0.12.8-repair.1` | 2026-07-25 registry install → doctor exit 0 (HISTORY) |
36
36
  | Secondary Ubuntu installed package | **certified** for `0.12.8-repair.1` | 2026-07-25 same artifact, isolated agent dir → doctor exit 0 (HISTORY) |
@@ -52,6 +52,59 @@ outside the experimental preview (rows below).
52
52
  | Remote pair: OFF target, negative paths over real SSH, other rails, more than one pair, non-Linux hosts, GUI rendering of the received envelope | unverified | No receipt. The deterministic and installed cells (VERIFY) do not stand for these. |
53
53
  | native Windows: other harnesses, `setup` and the `.sh` bins, fresh/resume, mux/Herdr/ACP, ARM64, records over 16 MiB, reparse-backed store folders | UNSUPPORTED | Outside the preview's scope by decision; the preview loader refuses an install without the Windows addon by name. |
54
54
 
55
+ ### #147 candidate host boundary (2026-10-21; not a certification row)
56
+
57
+ A checkout-started Pi wrote the new V3 `attachIncarnation` field while older hook/bridge
58
+ readers still rejected it. The observed mixed-reader failures were rolled forward under a
59
+ specific operator-approved repair, preserving the same garden/native identity; no manual
60
+ record deletion, id rewrite, fresh-cut or old-shape rollback was used. Normal attach writes
61
+ continued. Other installed native readers and the external Python mirror were identified as
62
+ stale by source inspection; that is predicted impact, not a measured failure on every rail,
63
+ and the complete G4 host rollout remains open. See [upgrade policy](./docs/fresh-cut-policy.md#upgrade-order).
64
+
65
+ The candidate's internal Pi control `retire`, pidfd helper, request adapters and private
66
+ injected dispatcher have source/fixture and candidate-local proof, not new native-host
67
+ certification. Pi ACK is not exit/flush; CC fd-process exit is not conversation/children/flush.
68
+ The current `>=2.1.306` floor and a historical B2 delivery receipt do not certify this
69
+ lifecycle. A later CI green also cannot certify the operator's mixed-reader deployment.
70
+ These two paragraphs are the 2026-10-21 state; the next section records what 2026-10-22 measured.
71
+
72
+ ### #147 Native round trip, 2026-10-22 (n=1)
73
+
74
+ Not a certification row and not a release gate. One Linux host, source `a9c9459` (exact-SHA push
75
+ CI green on its four required jobs), checkout compiled output rebuilt once under the dist lock
76
+ after its previous bytes were preserved. The operator requested this bounded test-owned
77
+ measurement; no working sibling was retired or restarted. Public record:
78
+ [#147 checkpoint comment](https://github.com/junghan0611/entwurf/issues/147#issuecomment-6329217980).
79
+
80
+ | Axis | Measured |
81
+ |---|---|
82
+ | caller | a fresh pi-durable citizen, identity from its exact nonce callback; its native store (read from an online read-only backup) offered all eight contact tools, the actual model was `openai-codex/gpt-6.1-sol`, and it made exactly four settled lifecycle calls — one retire and one resume per target |
83
+ | ordinary Pi target | fresh, callback-identified. Retire returned `shutdown-requested`; separately, the original pane and control socket were gone, the record unchanged and the paired requested/outcome events present. Dormant `entwurf_v2` rejected. Resume returned separate LAUNCH and OBSERVATION; native session, `createdAt`, cwd and backend kept; transcript byte-identical across the promptless resume; a question omitting the answer recovered the pre-retirement fact |
84
+ | native Claude Code target (CLI 2.1.307) | fresh, callback-identified, bound arm before retirement. Retire returned `exited`; separately, the last bound receiver owner was dead, the original pane gone, the record unchanged and the paired events present. Dormant delivery rejected as an undeliverable mailbox. Resume observed a NEW live owner with a new record-matched arm for the same native session and `createdAt`; transcript byte-identical across the promptless resume; blind recall succeeded |
85
+
86
+ It does not show: conversation flush, descendant exit or model silence; the `>=2.1.306` floor
87
+ itself (2.1.307 was the measured CLI); ACP-provider Pi, Herdr, pi-durable as a target, Windows or
88
+ macOS; a release artifact; the `smoke-mux-lifecycle-live` MUST; or that any resident process
89
+ reloaded new reader bytes.
90
+
91
+ **G4 the same day (reader scope closed with Copilot runtime deferral; resident/runtime acceptance separate).** The five stale on-disk reader copies (Codex
92
+ birth, OMP birth and receive, Copilot birth and receive) were preserved and rolled forward with
93
+ their owning installers; their installed meta modules are byte-identical to the current source or
94
+ compiled twin. Codex birth, both OMP units and Copilot receive passed install and doctor. Copilot
95
+ birth passed install and doctor ownership but its doctor runtime axis is RED on a historical
96
+ unrecovered hook error from 2026-10-14; that RED is retained, not cleared or counted as PASS.
97
+ On 2026-10-22 GLG explicitly deferred Copilot Native runtime validation for this landing: Copilot
98
+ is currently a free `auto` account, not a subscription, and GLG reports even a greeting taking over
99
+ a minute to answer, making practical runtime validation unavailable. That latency is an operator
100
+ report, not a benchmark re-measured here. No new Copilot model turn or log-clearing recovery was
101
+ performed. This landing closes the on-disk reader/field-compatibility scope with that explicit
102
+ Copilot runtime deferral; it does not certify Copilot runtime or resident-process reloads.
103
+ The external Python mirror's nullable-field delta has a reviewed local checkpoint (not yet pushed),
104
+ and its owner's one installed schema certification accepted 1,847 records with zero defects — field
105
+ acceptance, not whole-parser equivalence. Resident processes remain unmeasured; broader runtime
106
+ and release acceptance are not silently waived.
107
+
55
108
  Notes the table cannot carry without becoming prose again:
56
109
 
57
110
  - **shape-only is not a live proof.** The `artifact-consumer` job verifies a read-only
@@ -253,7 +306,7 @@ Per-question PASS / FAIL / NOTE for grading the model's response.
253
306
  ### Q-MCP — MCP enumerate
254
307
  - **PASS** — Exactly one: `entwurf-bridge`.
255
308
  - **FAIL** — Any second server appears, or `entwurf-bridge` missing.
256
- - **NOTE** — Codex naturally writes the name with underscores (`entwurf_bridge`); that is the agent-visible backend marker, not a mutation. The current server exposes eight tools, including the manual `entwurf_register_native` fallback; MCP enumeration asks for the server name, not a stale tool count.
309
+ - **NOTE** — Codex naturally writes the name with underscores (`entwurf_bridge`); that is the agent-visible backend marker, not a mutation. The current bridge lists nine tools (since 0.41.0), including the manual `entwurf_register_native` fallback; MCP enumeration asks for the server name, not a stale tool count.
257
310
 
258
311
  ---
259
312
 
package/CHANGELOG.md CHANGED
@@ -4,6 +4,327 @@ All notable changes to this project will be documented here. Format follows [Kee
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 0.41.0 - 2026-10-22
8
+
9
+ A sibling's garden id is its birthday, and this cut lets it rest and come back under it. A
10
+ sibling can now be opened, spoken to, retired when the human operator names it, and reopened
11
+ under the same id with its conversation intact. That holds for ordinary Pi and native Claude
12
+ Code. Retirement is a pause in a long session, not cleanup: it is never autonomous, a paired
13
+ requested/outcome record beside the target's record is attempted for every request with any
14
+ recording failure reported on its own axis, and the record and transcript stay.
15
+ A durable pi-durable sibling can drive both verbs. Each was measured once on real sessions, which
16
+ is recorded as dated evidence, not as a release gate.
17
+
18
+ ### Fresh call
19
+
20
+ - **A Pi-dialect model names its provider, and never OpenRouter (#76).** The dispatch root both
21
+ rails share now refuses, before either rail is asked anything, a pi or pi-durable model whose
22
+ provider segment is `openrouter` in any letter case (`model-openrouter-forbidden`) and a pi
23
+ model that names no provider at all (`pi-model-not-provider-qualified`). The second replaces
24
+ the 2026-08-13 non-goal that allowed a bare id: GLG decided on 2026-10-21 that the provider is
25
+ stated explicitly, because a bare alias resolves to whichever catalog row Pi prefers (the
26
+ 2026-09-17 bare `terra` opened on an OpenRouter row). It is a call-time input contract, not a
27
+ version pin: no model list, catalog copy, resolved-row or endpoint check was added, so a later
28
+ model name passes this provider check once it is written `<provider>/<id>` (not a promise
29
+ that it launches, nor a bypass of #128). Other backends' model names
30
+ (a claude-code `opus` alias) are not judged; resume and #128 curation are unchanged. The
31
+ refusal checks the name a caller wrote, not the endpoint a provider name reaches. The
32
+ `entwurf_fresh_call` model description says both rules. Proof:
33
+ `pi-extensions/lib/fresh-call-dispatch.test.ts` (three QKs) and three mutants in the
34
+ `fresh-call-dispatch` lane (12 → 15), executed through `./run.sh check-fresh-call-provider`.
35
+
36
+ ### pi-durable
37
+
38
+ - **The durable contact offers `entwurf_resume_call` (#147 L1).** A pi-durable citizen (the
39
+ coordinator's host) could not call the existing same-id resume verb: its contact exposed six bridge
40
+ tools and resume was not among them. It now registers a seventh, under the bridge's own
41
+ single-`target` schema, and forwards the call to the bridge unchanged. The verb itself is
42
+ unchanged by L1: at that checkpoint pi-only targets (L2 below widens the domain), a live target
43
+ refused, no turn, the record supplies the launch facts. Nothing new authorises who may resume, a
44
+ retire verb is not here, and a running durable host offers the tool only after it is restarted onto a build that
45
+ carries it. Because the bootstrap reserves every contact tool name, an operator native module
46
+ that registers a tool named `entwurf_resume_call` is now refused as `native-module-name-reserved`.
47
+ Proof: two QKs in `pi-extensions/meta-bridge-pi-durable.test.ts` — the tool is offered and hands
48
+ the bridge exactly `{target}`, and a faux model's call on the published runtime reaches this
49
+ package's compiled bridge through the contact's production connector and reads the bridge's named
50
+ refusal for a test-owned record of a backend with no same-id resume (codex since L2; no citizen is
51
+ resumed). Two mutants
52
+ in the `pi-durable` lane (84 → 86), executed through `./run.sh check-pi-durable-beside`. The
53
+ independent tool lists of `check-pi-durable-contact`, `check-pi-durable-send` and
54
+ `smoke-pi-durable-fresh-live` now name seven tools. One default-cell `check-pi-durable-send` run
55
+ (scripted loopback endpoint and the carrier app's native tool path; no vendor model turn) offered
56
+ `entwurf_resume_call` in the app's first model request, and its parameters there equal the
57
+ bridge's own listed schema — read from that run's raw wire receipt, because the default cell's
58
+ QK judges only self, peers and v2; the seven-tool `native-module` cell was not run. A running
59
+ coordinator host offering seven tools, and any operator rollout, remain unmeasured.
60
+
61
+ ### Operator-requested retirement (#147)
62
+
63
+ - `entwurf_retire_call {target}` makes one explicitly human-requested Pi or Claude Code attempt. Development, coordinator status and record-backed attribution are not standing permission. Where a host provides or is configured with an approval step, that step applies as well; Pi and pi-durable 1.3.0 have no built-in approval prompt by default. MCP annotations and caller identity are hints and attribution, not permission. No autonomous cleanup, roster expansion or automatic replay.
64
+ - The per-request production binding captures its directories and cancellation signal. Requested/outcome events share an id in a target-gid append-only sidecar with explicit actor/target fields. Action, record-write failure and lock cleanup stay independent; an append failure cannot turn a possibly effective action into a safe retry. The source bridge lists nine tools, ordinary Pi seven active plus two hidden, and the durable contact eight.
65
+ - Independent review repaired a metadata-test oracle, random-id privacy-test flakiness and snapshot/file-target consistency. A private `node_modules` compiled consumer passed29 assertions over anonymous/self refusal, existing resume refusal, inert Node RPC and two-line attribution. Operator dist hashes stayed unchanged. That consumer is not real Pi/Claude Code retirement, Native/LIVE, G4 rollout or release acceptance; final candidate floors and exact-SHA CI are still owed.
66
+ - 2026-10-22, source `a9c9459`: one operator-requested, test-owned Native round trip per backend — a fresh pi-durable caller (actual offering of all eight contact tools) retired and same-id resumed a fresh ordinary Pi and a native Claude Code 2.1.307 target once each, with dormant delivery refused, identity and transcript preserved across the promptless resume, and blind recall. n=1; not a MUST gate, ACP, Herdr, Windows, release-artifact or flush/descendant claim ([BASELINE](./BASELINE.md#147-native-round-trip-2026-10-22-n1)). The five stale on-disk Codex/Copilot/OMP reader copies were rolled forward with their owning installers; Copilot birth's doctor runtime axis remains RED on a historical hook error. GLG explicitly deferred its Native runtime validation for this landing: the current free `auto` account, not a subscription, is impractical to test (GLG reports a greeting taking over a minute; not re-benchmarked here). On-disk reader compatibility is closed with that named runtime deferral; resident-process reloads and broader runtime/release acceptance remain unmeasured. Earlier component receipts below do not become this candidate's kill-power.
67
+
68
+ ### Internal retirement checkpoints (#147; before public composition)
69
+
70
+ - **Pi's control server now serves an internal `retire` RPC.** A Pi started from this
71
+ checkout can already receive it; this is not a claim about the published 0.40.0 package.
72
+ Immutable garden/native identity, TUI, idle and no-pending guards precede one SDK shutdown
73
+ request. The shared production response envelope and strict one-shot client keep
74
+ `shutdown-requested` distinct from exit, flush or model silence. Busy work is refused, not
75
+ aborted or queued for a later shutdown.
76
+ - **Claude Code has a fixed Linux pidfd helper and request adapter.** The helper is copied
77
+ beside the compiled adapter; it pins a process, checks its start key, accepts one complete
78
+ go/cancel frame and requests one fd SIGTERM with bounded exit observation. Fresh
79
+ record/receiver/sender birth, arm binding, owner domain and pid/key joins run before helper
80
+ spawn and after ready. Unknown is not safe-retry permission. A fd-process exit does not
81
+ prove conversation identity, descendant termination or transcript flush.
82
+ - **A private dispatcher preserves action and cleanup independently.** Trusted caller
83
+ attribution/self refusal, fresh target and liveness checks, one adapter and one lock release
84
+ are composed through mandatory seams. It creates no grant/role database. At these component
85
+ checkpoints there was no accepted production host composition or public retirement MCP/contact
86
+ registration. The later candidate composition above owns its separate binding and runtime proof.
87
+ - **Proof remains candidate-local:** owned receiver/helper fixtures; independent review;
88
+ primitive PARTIAL Pi9 + CC11, later request-adapter PARTIAL9+6+15+40, then pure-dispatcher
89
+ PARTIAL10 on different candidates; private compiled consumer28 and a missing-Pi-entry
90
+ negative on the adapter candidate. These are not release-SHA kill-power, installed retirement
91
+ runtime or four-cell native lifecycle evidence. Actual host composition, G4 reader rollout,
92
+ public caller integration and the preserved #106 B append-only attribution record remain
93
+ open. Existing live/release floors are not waived by these checkpoints.
94
+
95
+ ### Resume
96
+
97
+ - **`entwurf_resume_call` reopens a dormant Claude Code citizen under its own garden id (#147 L2).**
98
+ The verb's domain is now pi and claude-code, a closed set; every other backend is the named
99
+ `target-backend-unsupported` (the retired `target-not-pi` has no alias). A Claude Code target is
100
+ launched as the fixed `claude --resume <record nativeSessionId>` at the record's cwd — no model or
101
+ permission flag it was once started with is restored, and no transcript is read; the record's
102
+ session id must be a lowercase UUID and its recorded transcript an existing file. Dormancy means
103
+ the LAST VALIDATED owner the Entwurf hook armed is proven gone AND that arm is still the record's
104
+ last one: the receiver marker, read through the no-follow fd reader (`readStoreRecordFile`: a
105
+ symlink or non-regular entry is corrupt, never followed) and judged by the shared
106
+ `classifyMarkerOwner`, must name this record's session and an owner proven DEAD, and the record
107
+ re-read after that verdict must carry the same non-null `attachIncarnation` the marker copied.
108
+ No marker, an unreadable or mismatched one, a live owner, an inconclusive one and an unbound one
109
+ each refuse by name (`target-receiver-absent` / `-corrupt` / `-mismatch` / `-unbound`,
110
+ `target-live`, `target-indeterminate`) with no window; the launch uses the re-read record, and no
111
+ marker or record is removed or rewritten. The observation succeeds only when a NEW live owner in
112
+ the pane this call opened re-arms the marker for the same session with a fresh binding the
113
+ current record also carries (same createdAt); a record defect stops it at once as
114
+ `record-unreadable` with its cause (window kept), and an unseen re-arm is `resume-unobserved`.
115
+ The verb submits no prompt; for Claude Code what the runtime does after it comes back (a doorbell
116
+ on unread mail may wake a turn) is unmeasured. Both backends' resume windows now carry the fixed
117
+ identity scrub fresh-call already had (`-e PI_SESSION_ID= -e PI_AGENT_ID=`, shared from
118
+ `mux-launch.ts`), because a resumed Claude Code never re-exports its own. Inside herdr the tmux
119
+ resume rail refuses as `herdr-resume-unsupported` instead of using an ambient `TMUX` (this also
120
+ applies to pi). The receipt names the caller from the same authoritative identity fresh_call reads,
121
+ as attribution only: an anonymous caller is `unattributed`, never refused, while a defect in the
122
+ caller's own store fails the call loud — for pi resume too — exactly as fresh_call does.
123
+ - **The arm binding: one new V3 key, `attachIncarnation` (#147 L2 B1).** A dead receiver marker
124
+ used to be resume evidence even when a LATER attach of the same session had committed but could
125
+ not re-arm the marker (owner unproven, marker write failed), so a second window could open on a
126
+ live session. Now the Claude Code hook mints a clock-independent 128-bit value (`randomBytes(16)`)
127
+ on every arm-capable event (SessionStart, CwdChanged, FileChanged — an arm revision, so one live
128
+ process mints many), commits it in the same atomic record upsert and copies it into the receiver
129
+ marker; UserPromptSubmit and every other writer preserve it, and nothing fills it by inference.
130
+ Every write the hook makes to an existing record runs under a per-gid record-arm lock in its own
131
+ directory (`meta-record-arm-locks`, beside the receivers dir; never the v2 dispatch lock visible
132
+ resume holds across its observation): the garden id is learned by a read-only certified lookup,
133
+ an absent record is published create-only (`upsertMetaSession` `expectAction` refuses a raced
134
+ create instead of turning it into an unlocked attach), an arm waits at most 2 s for the lock and
135
+ otherwise writes NOTHING and logs ERROR, and a busy UserPromptSubmit skips its backfill. A marker
136
+ write failure after the record committed is an ERROR logged after `INFO armed watch`, and resume
137
+ then refuses that garden as unbound. **Schema:** the same V3, one key wider — absent reads as
138
+ `null` ("no binding observed"), every serialize emits it, a present value must be 32 lowercase
139
+ hex, every other stray key is still refused; gardenId, nativeSessionId and createdAt keep their
140
+ values and no record is rewritten. `meta-facts` citizens carry the key verbatim. **One-way per host:** a reader that predates the key refuses
141
+ every record that carries it, and an old writer certifies the whole store first, so once one new
142
+ write lands every old reader and writer on that host refuses — a mixed-reader incident on oracle
143
+ on 2026-10-21 (records written by a Pi loaded from the development checkout) took the CC hook
144
+ and the pi-durable contact down until the checkout dist and the CC hook bundle were re-deployed.
145
+ Codex birth, the Copilot extension/receiver, the OMP bridge/receiver and the external
146
+ `entwurf-peek` mirror still carried the old reader there at L2; the on-disk copies were rolled
147
+ forward on 2026-10-22 (see the retirement section).
148
+ Old dormant Claude Code citizens whose record has no binding stay `target-receiver-unbound` until
149
+ a human reopens them once under a new hook. **Accepted boundary (GLG, 2026-10-21, current live
150
+ Pi/CC cohort scope):** only committed hook attaches are guarded — a process that holds the session
151
+ without one (hooks disabled, a record-arm lock timeout or a store failure at its start) is
152
+ invisible to the proof. The installed CC hook bundle now carries `lib/entwurf-v2-lock`, and the
153
+ doctor's writer-version parity checks it beside the writer.
154
+ - **Claude Code floor `>=2.1.306`** (`package.json` `entwurf.claudeCodeFloor`, GLG 2026-10-21):
155
+ same-id resume runs `claude --resume` from the record's last observed cwd, and the vendor CLI
156
+ reference says the session-id search covers other projects only from v2.1.223. 2.1.306 is the
157
+ version GLG relayed as running here; it is a declared floor with no LIVE receipt at 2.1.306 itself
158
+ (the 2026-10-22 n=1 round trip ran on 2.1.307). The
159
+ exec-form observations at 2.1.217 stand as observations.
160
+ - Proof: QKs beside the composition (`entwurf-v2-visible-resume.test.ts`), in
161
+ `check-resume-launch-identity`, `check-mux-resume-call`, `check-meta-v3-record`,
162
+ `check-meta-session`, `check-meta-hook-session-switch` (binding pair, UPS preserve, unproven owner,
163
+ lock busy/wait/own-dir, two overlapping successful schedules, and an install-closure check that
164
+ compares the hook's static double-quoted `from` imports with what the installer copies and then
165
+ runs the staged compiled hook) and `check-meta-doctor-oracle` (installed binding; lock module
166
+ missing/drifted). Mutants: v2-visible-resume 18 → 40, mux-resume-call 10 → 13,
167
+ resume-launch-identity 7 → 10, meta-hook-session-switch 17 → 25, meta-identity 4 → 9, existing
168
+ ones re-anchored. At the L2 checkpoint the LIVE dormant→resume journey for Claude Code had not
169
+ been measured, and `smoke-mux-lifecycle-live`'s Claude Code cell, which expects the still-live
170
+ sibling's resume to be refused as `target-live`, had not been run. Retire (G3) and the caller's
171
+ four cells were not part of L2; the later 2026-10-22 n=1 round trip is recorded in the
172
+ retirement section. The Claude Code dormant leg is still not a release-gate MUST.
173
+
174
+ ### Team bookmarks
175
+
176
+ - **`team.json.example` shows one way to keep long-lived siblings in view.** It is a fictional,
177
+ caller-maintained bookmark: each member's name, the descriptive form of address
178
+ `coordinating sibling` or `sibling`, backend, garden id and native session id. The real
179
+ `team.json` at a repository root stays ignored. No program loads either file; the record stays
180
+ the address authority, liveness is re-read from `entwurf peer-facts`, and membership is not
181
+ permission to send, resume or retire.
182
+
183
+ ### Herdr plugin
184
+
185
+ - **Back to the checkout carrier for this unpublished candidate.** `plugins/herdr/runtime-lock.json`
186
+ moves from the published `@junghanacs/entwurf@0.40.0` npm pin to the closed `herdr-checkout`
187
+ source, as the 0.40.0 preparation did; no npm integrity is invented and the plugin manifest
188
+ version is not bumped. The npm re-pin follows publication in its own Herdr snapshot. Inside
189
+ Herdr, `entwurf_resume_call` still refuses as `herdr-resume-unsupported`; retirement from a
190
+ Herdr pane was not measured.
191
+
192
+ ### Upgrade notes
193
+
194
+ - A running process keeps the bytes it loaded. Ordinary Pi's built-in MCP bridge and pi-durable
195
+ load the compiled `dist`, `start.sh` hosts load the source in a checkout, and installed
196
+ hook/extension copies change only through their owning installers; restart the session after
197
+ that ([setup](./docs/setup-clean-host.md#applying-new-source-to-a-running-host)). A reader that
198
+ predates `attachIncarnation` refuses records carrying it, so roll every installed reader forward.
199
+ - Claude Code needs `>=2.1.306` for same-id resume. Its retirement uses a Linux pidfd helper and is
200
+ refused elsewhere. The native-Windows preview has no resume, and retirement was not
201
+ measured there. Copilot's Native
202
+ runtime validation is deferred by GLG's decision on the free `auto` account.
203
+
204
+ ### Evidence boundary
205
+
206
+ - **One real round trip per backend, at `a9c9459`, not at this release's SHA.** A fresh pi-durable
207
+ caller retired and same-id resumed a fresh ordinary Pi and a native Claude Code 2.1.307 target
208
+ once each: dormant delivery refused, native identity and transcript preserved across the
209
+ promptless resume, blind recall correct. n=1; not a release-gate MUST, ACP, Herdr, Windows,
210
+ release-artifact or flush/descendant claim
211
+ ([BASELINE](./BASELINE.md#147-native-round-trip-2026-10-22-n1),
212
+ [#147](https://github.com/junghan0611/entwurf/issues/147#issuecomment-6329217980)).
213
+ - Kill-power re-measured at this shipped SHA: none (all 1173 committed claims not re-proved,
214
+ #144).
215
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head 639f783df2d5 surface
216
+ d53b8f8d6602 -- `["bash","run.sh","check-entwurf-v2-retire"]` 32/32 receipt fc08567fb8d3
217
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head 639f783df2d5 surface
218
+ d53b8f8d6602 -- `["bash","run.sh","check-retirement-event"]` 16/16 receipt 948a9661fdc0
219
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head 639f783df2d5 surface
220
+ d53b8f8d6602 -- `["bash","run.sh","check-pi-durable-beside"]` 44/44 receipt 61e5b0552648
221
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head 639f783df2d5 surface
222
+ d53b8f8d6602 -- `["bash","run.sh","smoke-agy-install-state"]` 12/12 receipt deccfdbf3990
223
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head 639f783df2d5 surface
224
+ d53b8f8d6602 -- `["bash","scripts/smoke-pi-provider-state.sh"]` 5/5 receipt 9de492738ef4
225
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head a9c945921a7e surface
226
+ 80a4f8a7b942 -- `["bash","run.sh","check-entwurf-v2-surface"]` 17/17 receipt b4c765bd35b7
227
+ - Earlier-candidate PARTIAL (operator-attested, not this SHA): head a9c945921a7e surface
228
+ 80a4f8a7b942 -- `["bash","run.sh","check-pi-durable-beside"]` 44/44 receipt b6751a543d7a
229
+ - **Local static and LIVE floor, measured on this preparation's candidate:** head `c318f30` plus
230
+ the three release-prep files (this evidence line excepted); the HEAD/index/worktree fingerprint
231
+ and the 94-file compiled `dist` were unchanged before and after each run. `pnpm run check:full`
232
+ PASS (exit 0, 882 s) with the host herdr at the supply pin 0.9.5, where `check-herdr-sandbox`
233
+ passed its 12 assertions on a private server and left the operator's panes and Pi trust store
234
+ untouched. Two earlier local attempts stayed RED and are kept, each at its own head: at
235
+ `bf24e17`, a `dist` built 35 s before the last `.ts` commit (the managed rebuild then produced
236
+ byte-identical output); at `c318f30`, the host herdr 0.9.4 against the 0.9.5 pin. `LIVE=1 ./run.sh release-gate <scratch> --cut`:
237
+ **MUST PASS=24 FAIL=0 SKIP=0, BEHAVIOR PASS=1 FAIL=0, cut OK**, 11:59:15–12:19:05 KST, with the
238
+ caller identity carriers stripped. The Codex step ran against a test-owned app-server started
239
+ for this run in its own tmux session (pid named from its own launch, start ticks recorded,
240
+ stopped by one TERM afterwards) from a different gate session, with the requested tiers
241
+ `gpt-6-luna` and `openai-codex/gpt-6-luna` — the request, not a billing or model-identity
242
+ proof. Receipts on the operator host: `.tmp-verify/release-0.41.0/` (`full-r3/full.log`
243
+ `44332ee7fd78`, `live/release-gate.log` `6f56fffab3da`).
244
+ - **Still owed before the tag.** This prepared SHA's exact push CI (four required jobs). This
245
+ release is carrier-bearing, so its candidate is the native-Windows preview workflow's pack at
246
+ the prepared SHA, never a local `npm pack`. The strict `doctor-meta-bridge` on an installed
247
+ 0.41.0 Claude host in a new post-install session, the agy shipped-lane cell and the Codex
248
+ caller-seat cell (VERIFY release acceptance 4, 7, 8) follow that candidate. Tag, GitHub
249
+ release and npm publication each need their own approval.
250
+
251
+ ## v2026.10.20-herdr.0.40.0 — Herdr Entwurf plugin 0.40.0
252
+
253
+ A separate Herdr plugin snapshot, not another core release: core package 0.40.0 and its
254
+ `v0.40.0` tag remain unchanged. The operator has published the core to npm.
255
+
256
+ **Reading guide (2026-10-20).** The `## 0.40.0` section below and the body of the
257
+ [v0.40.0 GitHub release](https://github.com/junghan0611/entwurf/releases/tag/v0.40.0) are
258
+ prepare-time text: their "still owed before the tag" lines and their npm wording predate the
259
+ cut. The cut-time acceptance (exact-SHA CI, the Windows preview candidate, the Linux consumer,
260
+ the installed remote-pair gate, VERIFY §3.4, and §3.7 (agy) excluded for that cut only by the
261
+ operator's decision, not a PASS) is recorded in the
262
+ [#107 cut receipt](https://github.com/junghan0611/entwurf/issues/107#issuecomment-6281283301).
263
+ The publish is the later registry fact recorded in this section (`latest` 0.40.0, published
264
+ 2026-10-20T01:49:33Z, registry bytes equal to the accepted candidate). Neither the published
265
+ tarball's CHANGELOG nor the release page is amended by this snapshot.
266
+
267
+ ### Herdr plugin
268
+
269
+ - **Re-pin the published core 0.40.0 and close the checkout window.**
270
+ `plugins/herdr/runtime-lock.json` returns from the `herdr-checkout` carrier that core 0.40.0
271
+ rode to production npm source `@junghanacs/entwurf@0.40.0` with the registry's measured
272
+ sha512 (`sha512-y9HgQIcm…bQnZUW/g==`). The plugin manifest moves 0.39.0 → 0.40.0; admission
273
+ remains Herdr >= 0.9.0 and the one-build/one-pane/Linux shape is unchanged. From the
274
+ `v2026.10.19-herdr.0.39.0` snapshot to `v0.40.0`, the only change under `plugins/herdr/`,
275
+ `scripts/herdr-runtime.mjs` and `scripts/fixtures/herdr-supply.json` was that lock's return
276
+ to `herdr-checkout`.
277
+ - **Pi range unchanged.** The acquired runtime still accepts Pi `>=1.3.0 <1.4`; core 0.40.0
278
+ keeps the Pi 1.3.0 SDK set.
279
+ - **Not a remote-pair certification.** Core 0.40.0's remote pair (#107) is carried in the
280
+ acquired runtime's bytes, but this snapshot measures no remote delivery, no SSH and no second
281
+ host through Herdr. The one real exchange remains the core section's L4 trial.
282
+ - **Upgrade by acquisition source, not just version.** A settled npm-source 0.39.0 installation
283
+ can rebind under the plugin README's ownership conditions. A host that installed an
284
+ intervening `herdr-checkout` candidate must explicitly deactivate by its own absolute runtime
285
+ path before reinstalling the npm-source snapshot. No operator installation or active session
286
+ is rewired by this release work.
287
+
288
+ ### Source-specific verification
289
+
290
+ - Registry metadata reports `latest` 0.40.0 and `repair` 0.12.8-repair.1, 621 files, published
291
+ 2026-10-20T01:49:33Z. The downloaded registry tarball is byte-equal to the preserved accepted
292
+ core candidate (SHA256 `4a6906ac38ff5024b9e07f7a092cc54a7686696082893293dfa653036f87fa55`,
293
+ 11292707 bytes); its independently computed sha512 and sha1 equal `dist.integrity` and
294
+ `dist.shasum`, so core make's Docker consumer of those bytes (`node:24-bookworm`, repoDigest
295
+ `node@sha256:6dac556d…`) is reused here, not repeated. A sandboxed `pi install` of the
296
+ registry package resolved its bridge inside the sandbox, recorded the registry URL with that
297
+ integrity, and listed the curated Claude anchors with `--list-models entwurf`; a sandboxed
298
+ global npm install of the same package passed its installed `check-bridge`. No model turn.
299
+ - **The npm runtime swap against the real registry**, by the registry-shipped
300
+ `scripts/herdr-runtime.mjs` (byte-equal to this checkout's) in sandboxed user roots, 22/22
301
+ checks. The 0.39.0 runtime was seeded from its own committed lock (`ba46dec`); the swap to
302
+ 0.40.0 took one acquisition, the cached tarball's sha512 equals the lock and its sha256 equals
303
+ both the ready journal's observed digest and the accepted candidate. The `installing` journal
304
+ carried the 0.39.0 identity as `previousRuntime` and the `runtime-ready` journal carries
305
+ `null`; when acquisition began the 0.39.0 tree was still at the active address. A manually
306
+ seeded torn swap (the active tree moved to the backup address with a half candidate in
307
+ staging; not a killed process) was restored before acquisition and reported `recovered:
308
+ torn-swap-with-candidate`. An exact re-request acquired nothing and left journal and tree
309
+ byte-identical, and the owned removal reclaimed cache, runtime and journal. The helper is a
310
+ leaf that probes no Pi, so these cells certify neither a Pi upgrade nor Herdr's
311
+ install/update relocation, harness wiring, the installed CLI's deactivate path, a model turn,
312
+ or recovery from a real kill.
313
+ - **Focused gates on the branch**, with HEAD, index and work surface unchanged across them:
314
+ `check-herdr-supply` (11 assertions), `check-herdr-plugin` (49),
315
+ `check-herdr-plugin-profile` (14), `check-herdr-runtime-bootstrap` (31),
316
+ `check-herdr-activation` (33), `check-herdr-placement` (25), `check-herdr-fresh-call` (45),
317
+ `check-dep-versions`, `check-install-surface` and `check-gate-manifests` (1034 committed
318
+ mutants across 79 lanes validated, none executed). `check-herdr-plugin-build` and
319
+ `check-herdr-sandbox` were not run locally because they run the operator's own herdr; they
320
+ ride the pushed SHA's FULL floor in CI.
321
+ - Pending, and not claimed here: the pushed SHA's four-job CI and the public-ref
322
+ install/use/deactivate journey of this npm pin, on Herdr 0.9.5 (the binary
323
+ `scripts/fixtures/herdr-supply.json` names) with Pi 1.3.0, from the public `main` ref rather
324
+ than the tag. Earlier npm or checkout-carrier receipts cannot certify these source-specific
325
+ axes. Those outcomes are recorded in the snapshot's post-push acceptance ledger, not in this
326
+ frozen text.
327
+
7
328
  ## 0.40.0 - 2026-10-20
8
329
 
9
330
  The bridge is crossed and the questions are carried forward. A person laid a bridge
package/CONTRIBUTING.md CHANGED
@@ -4,7 +4,7 @@ This is a daily-driver bridge. Correctness beats feature breadth. Read this befo
4
4
 
5
5
  ## What this repo is
6
6
 
7
- `entwurf` opens and messages visible sibling sessions across independent harnesses, and reopens dormant pi siblings in tmux ([capability table](./README.md)), with the garden id as their address — entwurf-core (v2 dispatch) + a meta-bridge + a pi adapter + an **ACP plugin on a two-backend adapter rail**; pi is one adapter, not the subject. The ACP plugin borrows the backend's identity (system prompt preset, model behavior, tool implementations) and shapes the *operating surface* — tools, MCP, skills, permissions — to match pi's own policy **wherever that backend exposes a knob for it**. Where it does not, the plugin does not fake one: a cortex session keeps its own native tool surface and receives MCP through an overlay projection, so "shaped to pi's policy" is a Claude-strength claim, not a universal one. Claude is the reference ACP backend and Snowflake Cortex Code is the second (landed 0.13.0, `cortex-` prefixed ids — [docs/acp-backend-rail.md](./docs/acp-backend-rail.md#cortex-code-audit-d1d10)); Codex is not on this rail at all — it is a shipped native-push citizen that keeps its own vendor tools and work context (see AGENTS.md, DELIVERY.md), never ACP-managed — and Gemini is not a shipped backend. That is the entire ACP-rail scope.
7
+ `entwurf` opens and messages visible sibling sessions across independent harnesses, and reopens dormant pi or Claude Code siblings in tmux ([capability table](./README.md)), with the garden id as their address — entwurf-core (v2 dispatch) + a meta-bridge + a pi adapter + an **ACP plugin on a two-backend adapter rail**; pi is one adapter, not the subject. The ACP plugin borrows the backend's identity (system prompt preset, model behavior, tool implementations) and shapes the *operating surface* — tools, MCP, skills, permissions — to match pi's own policy **wherever that backend exposes a knob for it**. Where it does not, the plugin does not fake one: a cortex session keeps its own native tool surface and receives MCP through an overlay projection, so "shaped to pi's policy" is a Claude-strength claim, not a universal one. Claude is the reference ACP backend and Snowflake Cortex Code is the second (landed 0.13.0, `cortex-` prefixed ids — [docs/acp-backend-rail.md](./docs/acp-backend-rail.md#cortex-code-audit-d1d10)); Codex is not on this rail at all — it is a shipped native-push citizen that keeps its own vendor tools and work context (see AGENTS.md, DELIVERY.md), never ACP-managed — and Gemini is not a shipped backend. That is the entire ACP-rail scope.
8
8
 
9
9
  **How a backend joins the rail** (the shape a PR must take): one adapter object in `pi-extensions/lib/acp/backend-adapter.ts` + its own curated rows/overlay modules + its own `check-acp-*` gate and mutant lane. Backend-specific *behavior* must stay behind the adapter, and backend-specific settings ride the opaque `adapterSettings` seam rather than growing the common config. That is not a ban on ever touching the common layer (`backend.ts`'s turn loop, `acp-client.ts`, `event-mapper.ts`, `session-store.ts`, `config.ts`) — cortex's landing did change `backend.ts` to pass the authoritative session key through the generic `ensureOverlay` seam. The rule is narrower and stricter: a common-layer change must be **backend-invariant** (it reads no backend name and branches on no backend) and **separately gated**. A common file that grows an `if (backend === …)` is the thing to reject.
10
10
 
package/DELIVERY.md CHANGED
@@ -59,6 +59,22 @@ ringing any doorbell, its filenames are ISO stamps so a drain is a global FIFO,
59
59
  is an honest `mailbox-undeliverable` refusal, and its receipt is that FILE. A mailbox enqueue is a
60
60
  durable file receipt; a control-socket queue is process memory.
61
61
 
62
+ ### Candidate lifecycle commands are not delivery boundaries (#147)
63
+
64
+ The current checkout's Pi control server also accepts the internal `retire` RPC, so a Pi
65
+ started from that checkout can expose it before a release is cut. It requires matching
66
+ record/native identity, a TUI, idle state and no pending work. Its `shutdown-requested` ACK
67
+ is not socket closure, process exit, flush or model silence. This command is not itself a public
68
+ MCP/contact verb and is not part of the four send boundaries above; the candidate public
69
+ `entwurf_retire_call` reaches a Pi target through it. A Pi whose process started before its
70
+ checkout carried this command does not know it, and its generic error answer is classified as
71
+ `unknown`/`malformed-response`, never as a safe refusal or a reason to replay. The candidate
72
+ CC pidfd helper observes one fd-pinned process, not a conversation, descendants or flush;
73
+ unknown never grants safe replay. Package/source presence, compiled consumer checks and a
74
+ real native lifecycle remain separate evidence axes; the one dated native lifecycle measurement
75
+ is [BASELINE's 2026-10-22 n=1 round trip](./BASELINE.md#147-native-round-trip-2026-10-22-n1). Historical delivery receipts below do
76
+ not certify these candidate components.
77
+
62
78
  ## Levels (D0–D8)
63
79
 
64
80
  These are independent of VERIFY's `L0–L5` evidence levels and BASELINE's question
@@ -109,7 +125,7 @@ physical host. Do not read a D-level cell as a Darwin receipt.
109
125
  | Harness / surface | Product status | Capability | Transport and boundary |
110
126
  |---|---|---|---|
111
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. |
112
- | **Claude Code interactive `>=2.1.217`** | 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. |
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. |
113
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. |
114
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. |
115
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. |
@@ -128,7 +144,7 @@ Re-audit backend versions before turning probe evidence into a shipped adapter.
128
144
 
129
145
  ### Claude Code: durable body, edge-triggered wake
130
146
 
131
- Claude's hook contract is exec-only at `>=2.1.217`. `SessionStart` arms a per-session
147
+ Claude's hook contract is exec-only at `>=2.1.306`. `SessionStart` arms a per-session
132
148
  watch path; the sender writes durable `*.msg` bodies before poking the signal;
133
149
  `FileChanged` emits a doorbell and `asyncRewake` wakes the idle session. The receiver
134
150
  then calls `entwurf_inbox_read`, which drains all unread bodies and archives them as
package/FAQ.md CHANGED
@@ -8,7 +8,7 @@ No. Entwurf is closer to a workshop than a factory: it connects independently ow
8
8
 
9
9
  ## What does Entwurf do?
10
10
 
11
- It opens, reopens and messages visible sibling sessions across independent harnesses, each under a stable **garden id**. `entwurf_fresh_call` opens a new sibling in the caller's Herdr workspace or tmux session by default, `entwurf_resume_call` reopens a dormant pi sibling under the same id, and `entwurf_v2` delivers a message, reply or hand-off to an existing one through the rail it actually supports. It does not reconstruct prompts, hydrate transcripts, manage credentials, or emulate another harness.
11
+ It opens, reopens and messages visible sibling sessions across independent harnesses, each under a stable **garden id**. `entwurf_fresh_call` opens a new sibling in the caller's Herdr workspace or tmux session by default, `entwurf_resume_call` reopens a dormant pi or Claude Code sibling under the same id, and `entwurf_v2` delivers a message, reply or hand-off to an existing one through the rail it actually supports. It does not reconstruct prompts, hydrate transcripts, manage credentials, or emulate another harness.
12
12
 
13
13
  ## What is a garden id?
14
14
 
@@ -24,15 +24,19 @@ The visible top-level session is the garden principal. Internal children remain
24
24
 
25
25
  ## Does sending a message start a process?
26
26
 
27
- No. [`entwurf_v2`](./README.md#entwurf_v2--canonical-dispatch-verb) only addresses an existing garden citizen and selects its supported delivery rail. `entwurf_fresh_call` opens a new visible sibling; `entwurf_resume_call` reopens a dormant pi citizen under the same id.
27
+ No. [`entwurf_v2`](./README.md#entwurf_v2--canonical-dispatch-verb) only addresses an existing garden citizen and selects its supported delivery rail. `entwurf_fresh_call` opens a new visible sibling; `entwurf_resume_call` reopens a dormant pi or Claude Code citizen under the same id.
28
+
29
+ ## Can an agent retire a sibling on its own?
30
+
31
+ No. `entwurf_retire_call` (new in 0.41.0) attempts retirement of one Pi or Claude Code sibling, and only one the human operator explicitly requested. Seeing the tool, a coordinator role or the caller's identity is not permission; where a host provides an approval step it applies too, and Pi and pi-durable 1.3.0 have none by default. The configured Claude ACP child is also [set to run without per-call permission prompts](./docs/acp-backend-rail.md#configured-claude-acp-permissions); that is source configuration, not measured ACP retirement. The record stays, so `entwurf_resume_call` can reopen the same id, and an unknown result is never a reason to retry.
28
32
 
29
33
  ## Where do visible siblings open?
30
34
 
31
- Inside Herdr, Entwurf opens a new unfocused tab in the caller's workspace and currently admits pi and Claude Code. Outside Herdr, it opens one of the fixed pi, pi-durable, Claude Code, Copilot, OMP, or Codex runtimes in a new window of the caller's tmux session, or of an existing session the caller names; without an explicit tmux placement, a Codex caller opens beside its own TUI pane. A dormant pi sibling reopens under the same id in a visible tmux window. The runtime keeps its own model, tools, authentication, and transcript.
35
+ Inside Herdr, Entwurf opens a new unfocused tab in the caller's workspace and currently admits pi and Claude Code. Outside Herdr, it opens one of the fixed pi, pi-durable, Claude Code, Copilot, OMP, or Codex runtimes in a new window of the caller's tmux session, or of an existing session the caller names; without an explicit tmux placement, a Codex caller opens beside its own TUI pane. A dormant pi or Claude Code sibling reopens under the same id in a visible tmux window. The runtime keeps its own model, tools, authentication, and transcript.
32
36
 
33
37
  ## Does Entwurf replace Herdr or tmux?
34
38
 
35
- No. Herdr and tmux provide visible seats. Entwurf opens siblings into them, reopens dormant pi siblings, and carries identity, delivery, and receipts across independent harness sessions. A pane or tmux session is a location for a sibling, never its address or liveness proof.
39
+ No. Herdr and tmux provide visible seats. Entwurf opens siblings into them, reopens dormant pi or Claude Code siblings, and carries identity, delivery, and receipts across independent harness sessions. A pane or tmux session is a location for a sibling, never its address or liveness proof.
36
40
 
37
41
  ## How do I install it?
38
42