@junghanacs/entwurf 0.14.2 → 0.15.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.
- package/AGENTS.md +8 -5
- package/CHANGELOG.md +79 -0
- package/DELIVERY.md +72 -59
- package/README.md +55 -4
- package/VERIFY.md +2 -2
- package/demo/README.md +1 -1
- package/docs/acp-backend-rail.md +1 -1
- package/docs/external-mcp-host.md +26 -4
- package/docs/setup-clean-host.md +3 -3
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +8 -6
- package/mcp/entwurf-bridge/dist/pi-extensions/entwurf-capabilities.json +1 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/copilot-fresh-preflight.js +253 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-sender-identity.js +10 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js +102 -28
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +77 -11
- package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-hook-copilot.js +288 -0
- package/mcp/entwurf-bridge/src/index.ts +10 -6
- package/mcp/entwurf-bridge/tsconfig.build.json +2 -1
- package/package.json +22 -11
- package/pi/copilot-receive/entwurf-receive/extension.mjs +323 -0
- package/pi/entwurf-capabilities.json +1 -0
- package/pi/meta-bridge-copilot/.claude-plugin/marketplace.json +11 -0
- package/pi/meta-bridge-copilot/entwurf-meta-receive-copilot/.claude-plugin/plugin.json +5 -0
- package/pi/meta-bridge-copilot/entwurf-meta-receive-copilot/hooks/hooks.json +7 -0
- package/pi/meta-bridge-copilot/entwurf-meta-receive-copilot/scripts/copilot-hook-launch.sh +85 -0
- package/pi-extensions/entwurf-control.ts +17 -8
- package/pi-extensions/lib/copilot-fresh-preflight.ts +251 -0
- package/pi-extensions/lib/meta-sender-identity.ts +10 -2
- package/pi-extensions/lib/meta-session.ts +102 -29
- package/pi-extensions/lib/mux-fresh-call.ts +82 -11
- package/pi-extensions/meta-bridge-hook-copilot.ts +323 -0
- package/run.sh +334 -16
- package/scripts/check-copilot-birth-hook.ts +497 -0
- package/scripts/check-copilot-launch.ts +395 -0
- package/scripts/check-copilot-receive-arm.ts +734 -0
- package/scripts/check-copilot-statusline.ts +122 -0
- package/scripts/check-entwurf-capabilities.ts +25 -6
- package/scripts/check-fresh-cut-gate.sh +92 -23
- package/scripts/check-gate-qualification.ts +7 -1
- package/scripts/check-install-container.sh +2 -2
- package/scripts/check-install-surface.ts +1 -1
- package/scripts/check-meta-capability-source.ts +89 -5
- package/scripts/check-meta-doctor-oracle.sh +26 -0
- package/scripts/check-meta-manifest-schema.py +38 -0
- package/scripts/check-meta-receiver-marker.ts +25 -2
- package/scripts/check-meta-session.ts +32 -0
- package/scripts/copilot-bridge-doctor.sh +209 -0
- package/scripts/copilot-bridge-install.sh +224 -0
- package/scripts/copilot-launch.sh +236 -0
- package/scripts/copilot-mcp-bridge.sh +177 -0
- package/scripts/copilot-mcp-config.py +221 -0
- package/scripts/copilot-receive-bridge.sh +483 -0
- package/scripts/copilot-statusline-bridge.sh +149 -0
- package/scripts/copilot-statusline-config.py +201 -0
- package/scripts/copilot-statusline.sh +68 -0
- package/scripts/dev-bin.sh +4 -1
- package/scripts/meta-bridge-hook-log.sh +17 -5
- package/scripts/mutants/capability-cache.json +64 -0
- package/scripts/mutants/copilot-birth.json +155 -0
- package/scripts/mutants/copilot-launch.json +187 -0
- package/scripts/mutants/copilot-receive.json +263 -0
- package/scripts/mutants/fresh-cut.json +17 -0
- package/scripts/mutants/mux-fresh-call.json +216 -2
- package/scripts/mutants/pack-install.json +17 -0
- package/scripts/raw-async-delivery/README.md +170 -125
- package/scripts/raw-async-delivery/copilot-enqueue-addressed.sh +35 -0
- package/scripts/raw-async-delivery/copilot-extension-receive/extension.mjs +123 -0
- package/scripts/smoke-copilot-mcp-state.sh +153 -0
- package/scripts/smoke-copilot-statusline-state.sh +131 -0
- package/scripts/smoke-mux-fresh-call-live.ts +2 -0
- package/scripts/smoke-mux-lifecycle-live.ts +3 -1
- package/scripts/tsconfig.json +1 -0
package/AGENTS.md
CHANGED
|
@@ -28,9 +28,9 @@ For agents that own this repo: invariant principles and reproducible verificatio
|
|
|
28
28
|
- **entwurf-core (v2)** owns garden-id addressing, peer facts, liveness interfaces, dispatch decisions, rail choice, and delivery evidence.
|
|
29
29
|
- **Record authority** owns citizen identity. Every addressable citizen uses the same V3 `MetaIdentity` schema. `backend` selects capability/rail behavior; it does not create an identity hierarchy.
|
|
30
30
|
- **pi adapter** attaches a pi session to a record at `session_start`, hosts the record-keyed control socket, and exposes the native pi tool surface.
|
|
31
|
-
- **Native bridges** register already-running native sessions without taking over their transcript or auth: Claude Code
|
|
31
|
+
- **Native bridges** register already-running native sessions without taking over their transcript or auth: Claude Code and Copilot CLI are mailbox/self-fetch (a Claude hook arms the watch; a forked Copilot extension holds it); Antigravity is probe-backed native-push. Codex has archived probe evidence, but its managed native lane was declined because pi already supplies the official GPT provider path; do not duplicate it as a native citizen or ACP backend.
|
|
32
32
|
- **ACP plugin** registers provider `entwurf` inside a pi host session and drives a backend under an isolated overlay. The host pi session is already a record-backed socket citizen; the plugin does not mint another citizen/socket/peer layer.
|
|
33
|
-
- **mux is launch-only and deliberately small.** Shipped: the tmux placement leaf (`mux-placement.ts` — inspect/append/close inside the caller's own session), the visible runtime launch composition (`mux-launch.ts`), the fresh-call composition (`mux-fresh-call.ts`), and the cwd-aware resume placement composition (`mux-resume-call.ts`). Fresh call is exposed as `entwurf_fresh_call`; visible same-id resume is `entwurf_resume_call`, composed at the two public surfaces by injecting `mux-resume-call` into the v2-side `entwurf-v2-visible-resume.ts`. The exact import graph is enumerated in [docs/mux-launch-rail.md](./docs/mux-launch-rail.md) §11. `entwurf_v2` behavior is unchanged and delivery still never imports launch. Fresh call learns a new sibling's garden id from the CALLBACK it makes, never from a lookup — so the pre-injected-token → identity-lookup design (rail §6) is **closed, not paused**. Do not reopen it without new evidence and explicit GLG re-approval. Fresh-call requires one explicit model and passes it in each runtime's measured CLI dialect (Pi `--model <provider/model>`, Claude Code `--model=<id-or-alias>`); it also takes ONE optional literal absolute `cwd` (#73 — omitted/`""` means the caller's own directory; no trim, no realpath, no project-name resolver; classified by the shared `classify-tmux-cwd.ts` leaf; the receipt echoes the REQUEST only), so a cross-repo fresh sibling is a fresh-call fact and never a reason to resume a dormant record. Resume takes only an existing target id and gets transcript/model/provider/cwd from its record. Do not grow either narrow carrier into a generic driver, harness profile, arbitrary command/env selection, or a second creation API. Ownership and import prohibitions: [docs/mux-launch-rail.md](./docs/mux-launch-rail.md) §11.
|
|
33
|
+
- **mux is launch-only and deliberately small.** Shipped: the tmux placement leaf (`mux-placement.ts` — inspect/append/close inside the caller's own session), the visible runtime launch composition (`mux-launch.ts`), the fresh-call composition (`mux-fresh-call.ts`), and the cwd-aware resume placement composition (`mux-resume-call.ts`). Fresh call is exposed as `entwurf_fresh_call`; visible same-id resume is `entwurf_resume_call`, composed at the two public surfaces by injecting `mux-resume-call` into the v2-side `entwurf-v2-visible-resume.ts`. The exact import graph is enumerated in [docs/mux-launch-rail.md](./docs/mux-launch-rail.md) §11. `entwurf_v2` behavior is unchanged and delivery still never imports launch. Fresh call learns a new sibling's garden id from the CALLBACK it makes, never from a lookup — so the pre-injected-token → identity-lookup design (rail §6) is **closed, not paused**. Do not reopen it without new evidence and explicit GLG re-approval. Fresh-call requires one explicit model and passes it in each runtime's measured CLI dialect (Pi `--model <provider/model>`, Claude Code `--model=<id-or-alias>`, Copilot `--model <name>` beside `--interactive <prompt>` and the explicit `--yolo` policy — a GLG width decision, 2026-08-25, after a callback-only `--allow-tool` grant stopped the sibling at every task tool); the Copilot backend opens through entwurf's OWN managed invocation (`entwurf copilot`), never the bare vendor, and its four required capabilities — birth, MCP hand, receiver, visible footer — are decided by `copilot-fresh-preflight.ts` BEFORE the tmux mutation, so a missing unit is a named refusal instead of a dead window; it also takes ONE optional literal absolute `cwd` (#73 — omitted/`""` means the caller's own directory; no trim, no realpath, no project-name resolver; classified by the shared `classify-tmux-cwd.ts` leaf; the receipt echoes the REQUEST only), so a cross-repo fresh sibling is a fresh-call fact and never a reason to resume a dormant record. Resume takes only an existing target id and gets transcript/model/provider/cwd from its record. Do not grow either narrow carrier into a generic driver, harness profile, arbitrary command/env selection, or a second creation API. Ownership and import prohibitions: [docs/mux-launch-rail.md](./docs/mux-launch-rail.md) §11.
|
|
34
34
|
- **One delivery verb:** `entwurf_v2` addresses an existing garden id. Current routes are live control-socket send, active self-fetch mailbox enqueue, and probe-alive native-push. NO route starts a process: the hidden background resume for a dormant citizen was withdrawn under the visible-first rule, so a dormant in-domain citizen rejects as `dormant-fire-forget-unsupported` and the intent axis is single-valued. Every other complementary state pair rejects honestly too. Fresh creation is a separate verb — `entwurf_fresh_call` — and it returns a launch receipt only; the new sibling's address arrives asynchronously as the sender envelope of its nonce callback.
|
|
35
35
|
|
|
36
36
|
## Hard Rules
|
|
@@ -41,7 +41,7 @@ For agents that own this repo: invariant principles and reproducible verificatio
|
|
|
41
41
|
4. **Dispatch is computed from live facts.** Never store liveness. Never infer send/resume from session type before resolving the target and probing its rail.
|
|
42
42
|
5. **Rejects are real.** Wrong intent, dead/drifted identity, undeliverable mailbox, ambiguous address, or indeterminate probe returns a reject and mutates nothing. No cosmetic success or silent fallback.
|
|
43
43
|
6. **MCP is explicit.** Only explicit `mcpServers` wiring. No ambient `~/.mcp.json` scan or automatic retrieval.
|
|
44
|
-
7. **Meta-record store contract is one contract.** Record body is authority; filename must agree with the body; every active entry is a regular non-symlink file readable by the live V3 schema; `nativeSessionId` ownership is unique. Identity writers and doctor certify the whole active store before writing. Address-bearing reads use `readAddressableMetaIdentity`; targeted relay reads keep the documented per-entry contract. `readStoreRecordFile`/`O_NOFOLLOW` and the lstat policy layer are both load-bearing—do not merge or bypass them. No legacy reader/migrator: quiesce and run `entwurf meta-bridge-fresh-cut` when the generation is unreadable. Source and gates: `meta-session.ts`, `check-meta-*`, `check-fresh-cut-gate`.
|
|
44
|
+
7. **Meta-record store contract is one contract.** Record body is authority; filename must agree with the body; every active entry is a regular non-symlink file readable by the live V3 schema; `nativeSessionId` ownership is unique. Identity writers and doctor certify the whole active store before writing. Address-bearing reads use `readAddressableMetaIdentity`; targeted relay reads keep the documented per-entry contract. `readStoreRecordFile`/`O_NOFOLLOW` and the lstat policy layer are both load-bearing—do not merge or bypass them. No legacy reader/migrator: quiesce and run `entwurf meta-bridge-fresh-cut` when the generation is unreadable. An unknown-backend defect is a stale deployed reader, not a rotten generation: redeploy the sibling unit; do not fresh-cut. `doctor-meta-bridge` already names that — run it after `META_BACKENDS` changes. Source and gates: `meta-session.ts`, `check-meta-*`, `check-fresh-cut-gate`.
|
|
45
45
|
8. **GC reclaims process resources, never memory/data.** Records and transcripts are preserved or archived; they are not casually deleted.
|
|
46
46
|
9. **This is not a second harness.** No prompt reconstruction, transcript hydration, tool-result ledger, credential mediation, or harness emulation. Each backend owns auth and transcript state.
|
|
47
47
|
10. **Native-push is its own rail.** It is not a mailbox or pi socket in disguise. Antigravity replyability is record-backed plus probe-alive; no receiver marker, watch state, or resume authority is invented for it.
|
|
@@ -89,7 +89,7 @@ A `--entwurf-control` pi session is a citizen for the same reason a native bridg
|
|
|
89
89
|
|
|
90
90
|
- **control-socket domain (currently `pi`)**: socket liveness, per-target lock, live send, and a dormant cell that rejects.
|
|
91
91
|
- **No relaunch transport exists inside delivery.** `spawn-bg` — a detached, window-less resume child — was removed under the visible-first rule, not deprecated behind a reject, and `entwurf_v2` still starts no process: a dormant socket-domain citizen is refused as `dormant-fire-forget-unsupported`. Reopening one is the separate lifecycle verb `entwurf_resume_call`, which is pi-only (`target-not-pi` otherwise), returns a LAUNCH receipt and an OBSERVATION receipt that are never merged, releases its per-gid lock on every path, and on an unobserved socket leaves the visible window open rather than retrying. No watcher, no retry, no supervisor. Do not re-route it through `entwurf_v2` and do not describe a spawn domain that does not exist.
|
|
92
|
-
- **self-fetch domain (
|
|
92
|
+
- **self-fetch domain (Claude Code, Copilot CLI)**: active receiver + mailbox deliverability; no resume authority. The two arm that receiver through different vendor surfaces — a Claude hook that emits watchPaths, a Copilot first-party extension that holds the watch in a forked child — and the marker records which, because the pid a reader verifies differs.
|
|
93
93
|
- **native-push domain (currently Antigravity)**: adapter probe + direct injection; no mailbox and no resume authority.
|
|
94
94
|
- `origin: "pi-session" | "meta-session" | "external-mcp"` records sender provenance. It is not the citizen identity schema and not a hierarchy.
|
|
95
95
|
- `entwurf_peers` reports record citizens and liveness facts only. It never embeds routing verbs or socket addresses for peers.
|
|
@@ -111,6 +111,7 @@ A `--entwurf-control` pi session is a citizen for the same reason a native bridg
|
|
|
111
111
|
- A new issue brings the best available observation plus an executable next measurement. Recovering a missing signature may be the first acceptance; an idea with neither evidence nor a next measurement is not an issue.
|
|
112
112
|
- No collection points, umbrella trackers, or fallout buckets. Merge issues only when they share one cause and one acceptance.
|
|
113
113
|
- Sweep manually after a release and before opening an issue. Solved work closes at its durable SHA; it does not wait for the next release.
|
|
114
|
+
- **An issue body is a snapshot; the thread is the live contract.** A body written before GLG stated what the work is *for* will disagree with the thread, and a reader who opens only the body inherits the stale half — including its fences, which is how a fence once forbade the very deliverable the lane existed to produce (#82, 2026-08-20). When they disagree the thread wins. Whoever notices owes the body an edit that strikes the withdrawn clause and says why it was there; deleting it silently makes the queue tidy and the lesson invisible.
|
|
114
115
|
|
|
115
116
|
## Verification
|
|
116
117
|
|
|
@@ -180,7 +181,7 @@ implement → affected focused gates → independent review → one amendment bu
|
|
|
180
181
|
- Every `.ts` file belongs to one typecheck fence: root emit-capable config, MCP strip-types config, or scripts strip-types config. Do not hide files with `exclude`.
|
|
181
182
|
- Root pi extensions import TypeBox through `@earendil-works/pi-ai`; do not mix direct `@sinclair/typebox` types.
|
|
182
183
|
- MCP/scripts use explicit `.ts` imports where Node strip-types requires them. Installed operator surfaces route to compiled JS.
|
|
183
|
-
- pi runtime range is `>=0.84.
|
|
184
|
+
- pi runtime range is `>=0.84.3 <0.85` with devDep exact `0.84.3`; re-evaluate loader aliases and `/compat` at every minor ceiling. The ceiling moves on measurement, never on assumption, and a previous bump's argument is never reused: at 0.83.0→0.84.0 `compat.ts` was still byte-identical but `loader.ts` was NOT, so the diff itself had to be read and judged reachable-or-not. Per-bump hashes, diff judgments, and reachability findings live in the ROADMAP **Dep bump(별도 트랙)** ledger — keep them there, not here.
|
|
184
185
|
- ACP pins are recorded in `package.json` and checked by `check-dep-versions`/`check-acp-sdk-surface`; do not describe a dependency bump as a behavioral fix without evidence.
|
|
185
186
|
|
|
186
187
|
## Working Style
|
|
@@ -206,12 +207,14 @@ An overgrowth is built from locally correct steps; what fails is the absence of
|
|
|
206
207
|
- **Unrelated meta-infra never rides a capability lane.** The source-adjacent gate/mutant that proves a capability's contract belongs in the same change as that capability — the removal/repair rule above is untouched. What stays out of a feature commit candidate is unrelated verification machinery: scheduling rework, selectors, caches, receipts, floor restructuring. That pain is recorded and handled in its own subtraction lane later.
|
|
207
208
|
- **Stop signal — evidence outgrowing the product.** When verification/meta-tool changes grow larger than the capability change they serve, stop and report to GLG. This is an operator-judgment trigger, deliberately not a mechanized ratio gate.
|
|
208
209
|
- **Claim only what the evidence carries.** "The full floor was green once on the declared candidate" is strong enough; a stronger sentence mints proof obligations, and those obligations mint subsystems.
|
|
210
|
+
- **A claim that crosses sessions carries its evidence state.** Measured here (name the receipt), read at `file:line`, read from an external artifact (name the path), inherited and unchecked (name the source) — and design proposals in a box of their own, since a proposal is adopted or decided differently, never measured. This repo is where siblings from many models each get a few turns, so a bare sentence costs the next one its turn re-deriving the ground, and leaves it only "believe or refute" when the honest move is "that carries no receipt, so I measured it." Retire claims, not people. Receipts in host-local paths do not travel: paste the decisive lines into the artifact that crosses.
|
|
209
211
|
- **Risk classes are not equal.** Data loss, identity authority, false delivery success, install destruction, and secrets get fail-closed strength. Doc tense, future possibility, and total environment-byte binding get repaired when seen — they do not justify new gate machinery.
|
|
210
212
|
|
|
211
213
|
## Next and References
|
|
212
214
|
|
|
213
215
|
- [NEXT.md](./NEXT.md) — current priority and exact next move; branch work uses disposable `NEXT--<branch>.md`.
|
|
214
216
|
- [ROADMAP.md](./ROADMAP.md) — forward direction and deferred lanes.
|
|
217
|
+
- [docs/adding-a-harness.md](./docs/adding-a-harness.md) — **first entry point for putting a NEW harness in the garden.** The order the eight steps are actually walked in (lane choice, vendor measurement, backend registration, birth, statusline, MCP hand, sender identity, receive, grade), what each owes before the next may start, and which of the other docs owns each slice. It routes; it grants nothing — Hard Rule 7 remains the authority on the meta-record store contract its registration step touches, and the gates and doctors it names remain the truth.
|
|
215
218
|
- [docs/acp-backend-rail.md](./docs/acp-backend-rail.md) — ACP adapter contract and current entry conditions.
|
|
216
219
|
- [DELIVERY.md](./DELIVERY.md) — delivery capability/evidence coordinates.
|
|
217
220
|
- [VERIFY.md](./VERIFY.md) / [BASELINE.md](./BASELINE.md) — verification protocol and recorded evidence.
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,85 @@ All notable changes to this project will be documented here. Format follows [Kee
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 0.15.0 — 2026-08-25
|
|
8
|
+
|
|
9
|
+
This release admits Copilot CLI as a full self-fetch garden citizen — birth, an owned
|
|
10
|
+
MCP hand, record-backed sender identity, an installed first-party receive extension, and
|
|
11
|
+
a visible-fresh launch path — closing #82, and fixes the fresh-launch YOLO regression the
|
|
12
|
+
admission work exposed.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- **Copilot CLI is a self-fetch garden citizen.** `birthCopilotCitizen` upserts
|
|
17
|
+
`(backend:"copilot", nativeSessionId)` into the shared V3 record store, gives the
|
|
18
|
+
session a garden id, an owned `entwurf` MCP hand, a footer statusline slot showing that
|
|
19
|
+
id, and record-backed sender identity on every dispatched message. The receive rail is
|
|
20
|
+
an installed first-party extension (`run.sh install-copilot-receive`) that binds to the
|
|
21
|
+
V3 record, arms a watcher, and rings a doorbell the model drains with
|
|
22
|
+
`entwurf_inbox_read`; `wakeMode` is `self-fetch`, so dispatch reaches the mailbox rail —
|
|
23
|
+
armed sessions get `delivered`, unarmed or stale sessions get the honest
|
|
24
|
+
`mailbox-undeliverable` refusal. Launch is exclusively through the owned invocation
|
|
25
|
+
`entwurf copilot`, which sets `COPILOT_CLI_ENABLED_FEATURE_FLAGS=EXTENSIONS` for that one
|
|
26
|
+
process; a session started any other way is silently inert by design, and
|
|
27
|
+
`doctor-copilot-receive` reads the live CLI environment to catch that class. (#82)
|
|
28
|
+
- **Fresh Copilot siblings are admitted through the same preflight contract as birth.**
|
|
29
|
+
`copilot-fresh-preflight.ts` decides birth, MCP hand, receiver, and visible footer
|
|
30
|
+
BEFORE the tmux mutation, so a missing capability is a named refusal instead of a dead
|
|
31
|
+
window. `entwurf_fresh_call` takes one explicit model in Copilot's measured CLI dialect
|
|
32
|
+
(`--model <name>` beside `--interactive <prompt>`) and opens through `entwurf`'s own
|
|
33
|
+
managed invocation, never the bare vendor binary. (#82)
|
|
34
|
+
- **Visible-fresh and D6 receive are LIVE-accepted on one host.** 2026-08-23 D6 receive
|
|
35
|
+
acceptance (garden `20260823T181316-d9f6ba`, CLI 1.0.80): `joined → armed → doorbell →
|
|
36
|
+
rang`, mailbox enqueue/read timestamps, and a model reply on the same
|
|
37
|
+
record/native/garden-id chain. 2026-08-25 visible-fresh acceptance: launch window `@89`,
|
|
38
|
+
nonce `mux-fresh-call-690529ae99f99faa2252aefb`, exact-callback garden
|
|
39
|
+
`20260825T085721-f68be0`, one `entwurf_v2` mailbox enqueue plus a same-garden reply, and
|
|
40
|
+
an operator-observed footer garden id on a healthy multi-turn window. Both rows are
|
|
41
|
+
evidence level L4 (one host) and stay unmerged; visible fresh is operator-metered and is
|
|
42
|
+
not a release-gate MUST.
|
|
43
|
+
|
|
44
|
+
### Fixed
|
|
45
|
+
|
|
46
|
+
- **Fresh Copilot siblings now default to `--yolo`, matching the profile a human gets
|
|
47
|
+
from `entwurf copilot`.** `mux-fresh-call.ts` previously passed a callback-only
|
|
48
|
+
`--allow-tool=entwurf-bridge(entwurf_v2)` grant, which caused `copilot-launch.sh`'s
|
|
49
|
+
explicit-policy scan to suppress its own `--yolo` injection — every task-tool call then
|
|
50
|
+
stopped on a confirmation prompt the new sibling could not answer, making it practically
|
|
51
|
+
unusable. GLG operator LIVE on 2026-08-25 confirmed the missing `YOLO` footer and the
|
|
52
|
+
per-task-tool stalls; the fix restores the default without adding a permission
|
|
53
|
+
parameter to the fresh-call API or changing the manual `entwurf copilot` override
|
|
54
|
+
behavior. (#82 follow-up)
|
|
55
|
+
- **Pi runtime pin moves to `0.84.3`.** Certified development pin and closed peer range
|
|
56
|
+
are now `0.84.3` / `>=0.84.3 <0.85`. `compat.ts` stayed byte-identical at the previous
|
|
57
|
+
bump but `loader.ts` did not, so the diff was read and judged reachable before moving
|
|
58
|
+
the ceiling; the argument is not reused across bumps and each future ceiling move is
|
|
59
|
+
judged on its own diff.
|
|
60
|
+
|
|
61
|
+
### Verification
|
|
62
|
+
|
|
63
|
+
- **`pnpm run check:full`** — PASS, exit 0, 406s standalone on the prepared HEAD.
|
|
64
|
+
- **`LIVE=1 ./run.sh release-gate <scratch> --cut`** — **MUST PASS=21 FAIL=0 SKIP=0,
|
|
65
|
+
BEHAVIOR PASS=1 FAIL=0 SKIP=0, `cut: OK`.** `check-gate-qualification` inside the gate
|
|
66
|
+
killed **252/252** mutants across 30 lanes. Log:
|
|
67
|
+
`/tmp/entwurf-release-gate-0.15.0.run2.fB2LLP/release-gate.log` (SHA-256
|
|
68
|
+
`9a00a22e8b4ca715ad0b955b51c99a2bacc385bf0c225180026948a4618e5ff6`).
|
|
69
|
+
- A first attempt on the same prepared HEAD (scratch `.../entwurf-release-gate-0.15.0.7yUIFH`)
|
|
70
|
+
hit one MUST FAIL in `smoke-mux-lifecycle-live`'s cleanup launcher-integrity self-fence:
|
|
71
|
+
all 78 lifecycle assertions passed, but the Claude Code launcher symlink changed target
|
|
72
|
+
and content mid-run (`2.1.243`→`2.1.245`) because auto-update was still on the default
|
|
73
|
+
channel. GLG pinned `autoUpdatesChannel: stable` before the rerun above; this was an
|
|
74
|
+
environment race during the LIVE run, not a defect in the 0.15.0 candidate.
|
|
75
|
+
|
|
76
|
+
### Notes
|
|
77
|
+
|
|
78
|
+
- Copilot's D7 grade is PARTIAL: reply and read receipts are observed; completion
|
|
79
|
+
taxonomy and long-haul operation are not. D3 (second-session isolation of an owned
|
|
80
|
+
invocation) is PENDING — observed once, but the decisive log was lost to scratch
|
|
81
|
+
cleanup before it could be preserved; re-measurement is tracked outside this release.
|
|
82
|
+
- #76 (subscription-first refusal of `openrouter/*` sibling launches), #78
|
|
83
|
+
(macOS/native-Windows portability evidence), #80 (public vocabulary), #83, #84, #85, and
|
|
84
|
+
the remaining #72 field-cause investigation stay outside this release.
|
|
85
|
+
|
|
7
86
|
## 0.14.2 — 2026-08-20
|
|
8
87
|
|
|
9
88
|
This patch makes bridge availability a property of the exact invocation each harness will run, preserves child-exit facts across ACP transport closure, and records Copilot delivery as probe evidence without admitting a new citizen rail.
|
package/DELIVERY.md
CHANGED
|
@@ -74,14 +74,22 @@ D8 robustness: partial reason="..."
|
|
|
74
74
|
|
|
75
75
|
## Current matrix
|
|
76
76
|
|
|
77
|
+
**Vocabulary.** *Owned* in this file names entwurf ownership of a concrete invocation,
|
|
78
|
+
installer, or config writer. Historical receipts may say *managed* for that same ownership
|
|
79
|
+
axis; neither word is an admission grade or a synonym of *supported*. A pre-contract probe
|
|
80
|
+
can be owned without being supported; a supported harness still has to walk
|
|
81
|
+
[`docs/adding-a-harness.md`](./docs/adding-a-harness.md) step 9.
|
|
82
|
+
|
|
77
83
|
| Harness / surface | Product status | Capability | Transport and boundary |
|
|
78
84
|
|---|---|---|---|
|
|
79
85
|
| **pi native Entwurf** | shipped | D7; D8 partial | Record-addressed Unix control socket. A record-less socket is diagnostic only and never dispatched. |
|
|
80
86
|
| **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. |
|
|
81
87
|
| **Antigravity / agy** | shipped | D6; D7 partial | Record-backed native-push through LS gRPC `agentapi send-message`; no mailbox or receiver marker. |
|
|
82
|
-
| **Codex app-server-backed TUI** | verified probe | D7; D8 unproven | WebSocket-over-UDS `turn/start` into a live `threadId`; status events expose completion. No
|
|
88
|
+
| **Codex app-server-backed TUI** | verified probe | D7; D8 unproven | WebSocket-over-UDS `turn/start` into a live `threadId`; status events expose completion. No owned native-citizen install/invocation lane. |
|
|
83
89
|
| **Codex embedded TUI** | deferred | D0 partial | No supported receive socket/hook on the measured standalone shape. |
|
|
84
|
-
| **Copilot CLI
|
|
90
|
+
| **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. |
|
|
91
|
+
| **Copilot CLI garden citizen** | branch candidate; send + receive + visible fresh accepted on one host | D6; D7 partial; D3 pending; D8 unproven | Birth, garden id, MCP hand and record-backed sender identity are accepted; the RECEIVER is an installed first-party extension that binds to the V3 record, writes a receiver marker owned by the WATCHER pid, and rings a doorbell the model drains with `entwurf_inbox_read`. `wakeMode` is `self-fetch`, so dispatch reaches the mailbox rail: armed → delivered, unarmed/stale → the honest `mailbox-undeliverable` refusal. **D6 is the owned-invocation LIVE acceptance of 2026-08-23** — garden `20260823T181316-d9f6ba`, native `20fe30c8-b2bc-4600-91a0-8a409131be51`, CLI 1.0.80: receive log `joined`→`armed`→`doorbell fresh=1`→`rang`, mailbox `lastEnqueuedAt 09:23:41.235Z` / `lastReadAt 09:23:56.480Z`, and a model reply on the same record/native/gid chain. **Visible fresh (step 9 clause 7) is a separate LIVE, 2026-08-25** — launch window `@89`/`%89` nonce `mux-fresh-call-690529ae99f99faa2252aefb`; exact-callback garden `20260825T085721-f68be0`; one `entwurf_v2` → `meta-mailbox → enqueued`; same garden `lastReadAt 2026-08-24T23:57:47.784Z` plus same-gid reply; GLG saw footer garden id and a healthy multi-turn window. Those rows stay unmerged. D7 is PARTIAL: reply and read receipt were observed, the completion taxonomy and long-haul operation were not. D3 (second-session isolation of an owned invocation) is PENDING — observed once, decisive log lost to scratch cleanup. Evidence level L4: one host. Launch through the owned invocation `entwurf copilot`, which sets `COPILOT_CLI_ENABLED_FEATURE_FLAGS=EXTENSIONS` for that one process; `doctor-copilot-receive` reads live CLI environments because a session launched without it is silently inert. Visible fresh is operator-metered and is not a release-gate MUST. |
|
|
92
|
+
| **Copilot CLI TUI+server** — *withdrawn lane, kept as evidence* | rejected | D7; D8 unproven | Older official-SDK probe over hidden `--ui-server`; idle enqueue worked, but loopback RPC authentication was not established. The bundled extension supersedes this candidate without reviving it. |
|
|
85
93
|
| **ACP Claude / Cortex** | shipped runtime, outside this matrix | — | ACP sessions are children launched by entwurf's pi adapter, not already-running native sessions to wake. |
|
|
86
94
|
|
|
87
95
|
“Verified probe” means the transport worked in a reproducible raw probe but entwurf
|
|
@@ -112,7 +120,7 @@ directly through the native adapter, with one bounded re-probe retry. Replyabili
|
|
|
112
120
|
`record-backed identity ∧ probe-alive`; mailbox state does not exist on this rail, and no
|
|
113
121
|
rail has resume authority since the visible-first cut.
|
|
114
122
|
|
|
115
|
-
The
|
|
123
|
+
The owned bridge, statusline, and hook installers own separate configuration atoms.
|
|
116
124
|
Same-pid concurrent model invocation by multiple conversations is not claimed because
|
|
117
125
|
the pid/start-key sender marker would be last-writer-wins. Current operator checks are
|
|
118
126
|
in [BASELINE.md](./BASELINE.md); deterministic ownership and sender gates run in
|
|
@@ -123,65 +131,70 @@ in [BASELINE.md](./BASELINE.md); deterministic ownership and sender gates run in
|
|
|
123
131
|
Do not describe “Codex” as one delivery shape. The measured app-server-backed TUI can
|
|
124
132
|
accept `turn/start` for a live thread and report completion; the standalone embedded
|
|
125
133
|
TUI exposed no equivalent receive route. This remains archived method evidence, not a
|
|
126
|
-
shipping commitment: GLG
|
|
134
|
+
shipping commitment: GLG declined to own a native Codex invocation/install lane on 2026-08-01 because pi
|
|
127
135
|
already supplies the official GPT provider path. Entwurf will not duplicate it as a
|
|
128
136
|
native citizen or ACP backend. `turn/steer` is active-turn steering, not idle wake.
|
|
129
137
|
|
|
130
|
-
### Copilot CLI:
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
`
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
138
|
+
### Copilot CLI: one citizen, two rails, one pending receipt
|
|
139
|
+
|
|
140
|
+
The branch product owns the native citizen's birth, garden id, statusline, MCP hand,
|
|
141
|
+
outbound sender identity — and, since RAIL 5, the receiver. A real Copilot CLI 1.0.80
|
|
142
|
+
session minted a V3 record and sent under that record-backed garden id on 2026-08-21.
|
|
143
|
+
That proved who SENDS. Whether a reply LANDS is a different fact on a different process,
|
|
144
|
+
and the paragraphs below are the two halves of it: where the transport came from, and
|
|
145
|
+
what the owned product unit had to add before it could be dispatched to.
|
|
146
|
+
|
|
147
|
+
The missing receive transport was found and measured on 2026-08-23. Copilot's platform
|
|
148
|
+
package bundles its first-party extension SDK and bootstrap. With
|
|
149
|
+
`COPILOT_CLI_ENABLED_FEATURE_FLAGS=EXTENSIONS`, the CLI forks an installed extension and
|
|
150
|
+
speaks JSON-RPC over the child's stdio; `joinSession()` binds the foreground session, and
|
|
151
|
+
the vendor-documented `fs.watch` → `session.send({mode:"enqueue"})` pattern wakes it.
|
|
152
|
+
An idle, never-typed-into session received a unique marker and returned it, then emitted
|
|
153
|
+
`session.idle`. A second armed process was observed to remain untouched, but its decisive
|
|
154
|
+
B log was not preserved before scratch cleanup, so D3 isolation remains an admission
|
|
155
|
+
rerun rather than a durable acceptance. This is L4 direct-native evidence on one Linux
|
|
156
|
+
workstation. The travelling receipt and reproduction are in
|
|
157
|
+
[`scripts/raw-async-delivery/README.md`](./scripts/raw-async-delivery/README.md).
|
|
158
|
+
|
|
159
|
+
This extension rail has no network listener, so the rejected `--ui-server` loopback
|
|
160
|
+
transport's authentication blocker does not apply. The owned product unit then took that
|
|
161
|
+
transport and gave it the obligations a product owes (#82 RAIL 5):
|
|
162
|
+
|
|
163
|
+
- **Artifact.** `run.sh install-copilot-receive` installs the receiver into the user
|
|
164
|
+
extensions directory from an install-state file it owns, refuses a unit it did not
|
|
165
|
+
put there, and removes only what that state names.
|
|
166
|
+
- **Identity.** The extension arms only when the CLI pid's sender marker, the V3 record
|
|
167
|
+
and the SDK's `session.sessionId` all agree; a drifted id or a parent-pid carrier that
|
|
168
|
+
disagrees with the real parent is a refusal, logged, never a best guess.
|
|
169
|
+
- **Liveness.** The marker's owner is the EXTENSION child — the process that actually
|
|
170
|
+
holds the watch — so a crashed receiver stops being deliverable at the next start-key
|
|
171
|
+
read, and the vendor's bootstrap already exits that child when the CLI goes.
|
|
172
|
+
- **Dispatch.** `wakeMode: self-fetch` puts Copilot on the existing mailbox rail. Armed
|
|
173
|
+
and matching → enqueue + doorbell; anything else → `mailbox-undeliverable`.
|
|
174
|
+
- **The flag.** entwurf still does not own the operator's shell and writes nothing to it,
|
|
175
|
+
but it owns ONE invocation: `entwurf copilot` execs the vendor CLI in the caller's own
|
|
176
|
+
terminal with `COPILOT_CLI_ENABLED_FEATURE_FLAGS=EXTENSIONS` set for that process only,
|
|
177
|
+
and refuses to launch at all unless the receiver unit it is promising is really
|
|
178
|
+
installed. `doctor-copilot-receive` still reads the live CLI environments, because a
|
|
179
|
+
session started any other way without the flag is silently inert. Plain `copilot` is
|
|
180
|
+
untouched; running the owned invocation IS the consent to its profile (EXTENSIONS,
|
|
181
|
+
`--model auto` when no model was given, `--yolo` when no explicit permission or surface
|
|
182
|
+
policy flag was given).
|
|
183
|
+
|
|
184
|
+
The owned receive invocation has now been accepted LIVE (2026-08-23, receipts in the matrix row
|
|
185
|
+
above), which is what moved receive from D0 to D6. Visible fresh is a later, separate LIVE
|
|
186
|
+
(2026-08-25, same row) and does not reopen D3 or D8. What remains owed is still EVIDENCE,
|
|
187
|
+
not code: D3 isolation lost its decisive log to a scratch cleanup and is pending, and the
|
|
188
|
+
active-turn case, `/clear` re-arm and flag durability across vendor releases are
|
|
189
|
+
unmeasured. The hermetic gate drives the real installer and the real extension against a
|
|
190
|
+
stubbed SDK — it proves everything on entwurf's side of the fork and nothing about the
|
|
191
|
+
vendor turn on the other side of it, so a green gate is still not a wake.
|
|
192
|
+
|
|
193
|
+
The hidden `--ui-server` probe remains retired evidence, not a fallback. It found a real
|
|
194
|
+
idle-enqueue capability through an unauthenticated loopback door; the extension finds the
|
|
195
|
+
same class of capability through the CLI-owned stdio lifecycle. Absence of Claude's
|
|
196
|
+
`FileChanged` / `asyncRewake` / `watchPaths` therefore means only that Claude's hook
|
|
197
|
+
mechanism cannot be copied — it never proved Copilot had no vendor wake surface.
|
|
185
198
|
|
|
186
199
|
## Recording a new claim
|
|
187
200
|
|
package/README.md
CHANGED
|
@@ -74,13 +74,14 @@ A few words that look unusual for a coding tool.
|
|
|
74
74
|
adapter that hosts the ACP plugin and live control-socket surface, but the base
|
|
75
75
|
install is **not** `pi install npm:...` anymore.
|
|
76
76
|
|
|
77
|
-
The package exposes
|
|
77
|
+
The package exposes six bins:
|
|
78
78
|
|
|
79
79
|
- `entwurf` → `run.sh` (installer, checks, native-bridge doctors/installers)
|
|
80
80
|
- `entwurf-bridge` → the MCP stdio launcher (`mcp/entwurf-bridge/start.sh`)
|
|
81
81
|
- `entwurf-statusline` → the Claude Code statusline renderer (`scripts/meta-bridge-statusline.sh`)
|
|
82
82
|
- `entwurf-agy-statusline` → the Antigravity garden-id statusline renderer (`scripts/agy-statusline.sh`)
|
|
83
83
|
- `entwurf-agy-imprint` → the Antigravity `PreInvocation` birth/sender hook (`scripts/agy-imprint.sh`)
|
|
84
|
+
- `entwurf-copilot-statusline` → the Copilot CLI garden-id footer renderer (`scripts/copilot-statusline.sh`)
|
|
84
85
|
|
|
85
86
|
The bridge/renderers/hook use stable bin names so package upgrades do not bake versioned package-store paths into native-harness settings.
|
|
86
87
|
|
|
@@ -154,7 +155,7 @@ gitignored and may be absent or stale immediately after a checkout or pull.
|
|
|
154
155
|
### Pi adapter / ACP plugin lane
|
|
155
156
|
|
|
156
157
|
To use the `entwurf` provider inside pi, install a compatible pi binary
|
|
157
|
-
separately (`@earendil-works/pi-coding-agent >=0.84.
|
|
158
|
+
separately (`@earendil-works/pi-coding-agent >=0.84.3 <0.85`). Then point pi at
|
|
158
159
|
the npm-installed package or development clone:
|
|
159
160
|
|
|
160
161
|
```bash
|
|
@@ -190,8 +191,58 @@ entwurf install-agy-hooks
|
|
|
190
191
|
entwurf doctor-agy-bridge
|
|
191
192
|
entwurf doctor-agy-statusline
|
|
192
193
|
entwurf doctor-agy-hooks
|
|
194
|
+
|
|
195
|
+
# GitHub Copilot CLI — four independent surfaces, four independent failure modes
|
|
196
|
+
entwurf install-copilot-bridge # birth: garden id + who-sent, on the first prompt
|
|
197
|
+
entwurf install-copilot-mcp # the entwurf tool hand (entwurf_inbox_read lives here)
|
|
198
|
+
entwurf install-copilot-receive # the receiver extension: doorbell + receiver marker
|
|
199
|
+
entwurf install-copilot-statusline # optional for a manual citizen; required for supported fresh
|
|
200
|
+
entwurf doctor-copilot-bridge
|
|
201
|
+
entwurf doctor-copilot-receive
|
|
193
202
|
```
|
|
194
203
|
|
|
204
|
+
#### Launching Copilot as a garden citizen — `entwurf copilot`
|
|
205
|
+
|
|
206
|
+
Copilot only scans for extensions when its CLI is started with
|
|
207
|
+
`COPILOT_CLI_ENABLED_FEATURE_FLAGS=EXTENSIONS`, and when that flag is absent it skips the
|
|
208
|
+
scan **silently** — no error, no log line, no receiver. entwurf does not own your shell
|
|
209
|
+
and writes nothing to your rc files, so it owns one invocation instead:
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
entwurf copilot # managed launch, in this terminal
|
|
213
|
+
entwurf copilot -p "…" --model gpt-5.4
|
|
214
|
+
copilot # the plain vendor CLI, untouched
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
`entwurf copilot` execs the vendor CLI in your current terminal — same cwd, same pid,
|
|
218
|
+
same exit status, no tmux window and no new citizen (a Copilot session is still born on
|
|
219
|
+
its first prompt). Before it launches it verifies that the receiver unit it is about to
|
|
220
|
+
promise is actually installed, and refuses with `entwurf install-copilot-receive` if it
|
|
221
|
+
is not, rather than starting a session that can never be delivered to.
|
|
222
|
+
|
|
223
|
+
**Running it is your consent to its profile.** For that one invocation it adds:
|
|
224
|
+
|
|
225
|
+
| Injected | When |
|
|
226
|
+
|---|---|
|
|
227
|
+
| `COPILOT_CLI_ENABLED_FEATURE_FLAGS=EXTENSIONS` | always — your other feature-flag tokens are preserved, in order, deduplicated |
|
|
228
|
+
| `--model auto` | only when you passed no `--model` |
|
|
229
|
+
| `--yolo` | only when you passed no explicit permission or surface policy flag |
|
|
230
|
+
|
|
231
|
+
The escape hatch is simply to state your own policy: any of `--yolo`, `--allow-all`,
|
|
232
|
+
`--allow-all-tools`, `--allow-all-paths`, `--allow-all-urls`, `--allow-tool`,
|
|
233
|
+
`--deny-tool`, `--allow-url`, `--deny-url`, `--available-tools` or `--excluded-tools`
|
|
234
|
+
suppresses the injected `--yolo`. The narrowing flags are in that list on purpose — adding
|
|
235
|
+
`--yolo` beside your `--allow-url=…` would silently widen exactly what you were
|
|
236
|
+
restricting. `--allow-all-mcp-server-instructions` (prompt content, not authorization) and
|
|
237
|
+
`--autopilot` (a mode) are deliberately not policy flags. Everything you pass is forwarded
|
|
238
|
+
byte-identically, injected defaults land before any `--` terminator, and nothing after the
|
|
239
|
+
terminator is read as policy. Nothing is written to disk; run plain `copilot` and none of
|
|
240
|
+
this applies.
|
|
241
|
+
|
|
242
|
+
Why `--yolo` is the default: the managed lane exists so a sibling can wake an idle session,
|
|
243
|
+
and draining the mailbox with `entwurf_inbox_read` costs two interactive approvals under
|
|
244
|
+
the default permission prompts — which an idle, unattended session is not there to answer.
|
|
245
|
+
|
|
195
246
|
Claude Code uses the supported floor `>=2.1.217`; older versions silently discard the
|
|
196
247
|
exec-hook `args`, so install and doctor fail loud rather than falling back. After any
|
|
197
248
|
upgrade, rerun the installer for the native harness you use and restart its existing
|
|
@@ -427,7 +478,7 @@ A two-pane recording covers the pre-0.12 v1 surface end-to-end — sibling resum
|
|
|
427
478
|
|
|
428
479
|
Live peer messaging carries a sender envelope `{ sessionId, agentId, cwd, timestamp, origin?, replyable? }`; `entwurf_self` returns that authoritative envelope for the current pi session or trusted meta-session. Plain external MCP hosts are non-replyable. A garden-native meta-session carries a trusted `meta-session` envelope, but **`replyable` is a fact its own rail decides, not a consequence of being trusted** — a self-fetch citizen needs a live armed receiver, a native-push citizen needs an alive adapter probe, and a pi session needs its control socket. `entwurf_self` also reports which rail a meta-session reply would ride, because a native-push citizen has no mailbox to name. `wants_reply` is an etiquette marker rendered as a `(wants reply)` badge — not a transport contract, no wait, no polling. **v2 never gates on it:** a `wants_reply` from an external/non-replyable caller is passed through and surfaced honestly beside that sender's `replyable: false`, not rejected — the decider routes on target + intent, never on sender replyability. (The retired v1 `entwurf_send` did reject it; that behaviour went with the verb.)
|
|
429
480
|
|
|
430
|
-
In ACP-backed and external native-harness sessions, `entwurf-bridge` exposes seven tools: `entwurf_v2`, `entwurf_peers`, `entwurf_fresh_call`, `entwurf_resume_call`, `entwurf_self`, `entwurf_inbox_read`, and the explicit/manual `entwurf_register_native` fallback. Native pi exposes the shared capability directly through the extension surface (`entwurf_v2`, `entwurf_peers`, `entwurf_fresh_call`, `entwurf_resume_call` tools; the socket-scan `/entwurf-sessions` command is gone — #50 C4). **For garden-id delivery/reply use `entwurf_v2`** — the canonical surface that classifies the target and routes to live-pi / Claude-Code-meta-mailbox / Antigravity-native-push, and refuses a dormant target instead of waking it. **To open a sibling that does not exist yet use `entwurf_fresh_call`** — it launches one fixed backend (`pi
|
|
481
|
+
In ACP-backed and external native-harness sessions, `entwurf-bridge` exposes seven tools: `entwurf_v2`, `entwurf_peers`, `entwurf_fresh_call`, `entwurf_resume_call`, `entwurf_self`, `entwurf_inbox_read`, and the explicit/manual `entwurf_register_native` fallback. Native pi exposes the shared capability directly through the extension surface (`entwurf_v2`, `entwurf_peers`, `entwurf_fresh_call`, `entwurf_resume_call` tools; the socket-scan `/entwurf-sessions` command is gone — #50 C4). **For garden-id delivery/reply use `entwurf_v2`** — the canonical surface that classifies the target and routes to live-pi / Claude-Code-meta-mailbox / Antigravity-native-push, and refuses a dormant target instead of waking it. **To open a sibling that does not exist yet use `entwurf_fresh_call`** — it launches one fixed backend (`pi`, `claude-code`, or `copilot`) as a visible window in the operator's own tmux session, requires an explicit model, accepts one optional literal absolute `cwd` (omit it or pass `""` to use the caller's cwd), passes the model and selected directory through the runtime's visible launch path, and hands it a first task; a `copilot` launch goes through entwurf's own managed invocation and is refused before any window opens if this host lacks the Copilot birth, MCP, receiver or visible-footer units. The sibling's first action is a nonce callback whose sender envelope carries its garden id. The launch receipt records the requested model/cwd plus tmux coordinates and never claims that the runtime accepted them or completed delivery. (The v1 verbs `entwurf` / `entwurf_resume` / `entwurf_send` are gone.) Garden-native operator commands require `--entwurf-control`. There is no spawn target allowlist — the target registry is gone (#50 C3): `entwurf_v2` and `entwurf_resume_call` address an existing record-backed citizen, while `entwurf_fresh_call` takes its explicit backend/model/task and optional cwd directly rather than resolving a model tuple from a registry file. **To reopen a DORMANT pi citizen use `entwurf_resume_call {target}`** — the record supplies transcript, model, provider and cwd, so it takes no prompt, no task and no model override; it runs no turn, returns a LAUNCH receipt and a separate OBSERVATION receipt (only the second says the citizen is back), refuses a non-pi target as `target-not-pi`, and on an unobserved socket leaves the visible window open and releases its lock rather than retrying.
|
|
431
482
|
|
|
432
483
|
### `entwurf_v2` — canonical dispatch verb
|
|
433
484
|
|
|
@@ -446,7 +497,7 @@ In ACP-backed and external native-harness sessions, `entwurf-bridge` exposes sev
|
|
|
446
497
|
|
|
447
498
|
**`entwurf_v2` is the canonical surface for garden-id delivery.** When you have a garden id and want to reach whoever it names — message, reply, or hand-off — `entwurf_v2` is the one surface that reads whether the target is live pi, dormant pi, mailbox-backed Claude Code, or native-push Antigravity and routes correctly; *when unsure which transport, use `entwurf_v2`*. This prevents callers from guessing a rail from the shape of an id.
|
|
448
499
|
|
|
449
|
-
What v2 provides is a **deterministic dispatch substrate** that moves the "which transport?" decision out of the fallible caller/model and into the decider, with transport-appropriate locking and an honest reject (no `✓ delivered`, no `.msg` garbage) when a target cannot receive. It still does **not** mint siblings, and it does not relaunch one either: every row above either reaches a citizen that is already running or refuses. Reopening a dormant pi citizen is `entwurf_resume_call`, a separate lifecycle verb that never routes through this decider. Fresh creation is the separate `entwurf_fresh_call` verb. It opens one fixed Pi
|
|
500
|
+
What v2 provides is a **deterministic dispatch substrate** that moves the "which transport?" decision out of the fallible caller/model and into the decider, with transport-appropriate locking and an honest reject (no `✓ delivered`, no `.msg` garbage) when a target cannot receive. It still does **not** mint siblings, and it does not relaunch one either: every row above either reaches a citizen that is already running or refuses. Reopening a dormant pi citizen is `entwurf_resume_call`, a separate lifecycle verb that never routes through this decider. Fresh creation is the separate `entwurf_fresh_call` verb. It opens one fixed Pi, Claude Code, or Copilot runtime visibly in the caller's tmux session with a required explicit model passed in that runtime's measured CLI dialect and one optional literal absolute `cwd`; omitted or `""` means the caller's cwd. Copilot opens through `entwurf copilot`, never the bare vendor. It returns only a synchronous launch receipt and lets the sibling report its new address asynchronously through the sender envelope of a nonce callback. Use this cwd input for a new cross-repository sibling; do not resume a dormant citizen as a cwd substitute. The meta-mailbox row requires an **active** self-fetch receiver; native-push requires a record-backed, probe-alive native conversation and never borrows mailbox state. The [mux launch lane](./docs/mux-launch-rail.md) owns placement, fixed-runtime launch, and the two narrow compositions above it (fresh-call and resume-call placement); delivery does not import launch, and mux is not a delivery transport.
|
|
450
501
|
|
|
451
502
|
A live pi target is *reached* over its control socket, but the socket is dispatch-internal transport, never identity (#50 C4). A control socket that no meta-record claims — a pre-record-era resident, an unreadable store, or a stale/planted file — is refused for **every** intent as `record-less-socket`, and the reject names the fix (restart the resident so `session_start` births its record, or quiesce and run the fresh-cut). `entwurf_peers` reports the same state as an aggregated `record-less-socket` diagnostic rather than a peer row.
|
|
452
503
|
|
package/VERIFY.md
CHANGED
|
@@ -58,7 +58,7 @@ Verification here is not a benchmark. In production we exchange short turns and
|
|
|
58
58
|
>
|
|
59
59
|
> Be honest about its cost, because it is a MUST: it spends real model turns on the operator's configured runtimes (two pi siblings, each resumed once, plus one Claude Code sibling), it READS the operator's real runtime config, and the siblings' native session transcripts stay in the real pi agent dir — they are not cleaned up, and they are part of the evidence a resume was real. The fence is per-axis rather than absolute: the four meta roots (records, mailbox, receivers, senders) are fixture for every cell, so no garden record is ever minted outside it; the native pi cell's control socket and v2 lock are fixture too; the ACP pi cell's are REAL and transient (see the paragraph above); and the Claude cell runs its runtime under the real HOME while its mailbox and meta stay fixture. The run ends by proving the operator's SIX real roots — the four meta roots plus `~/.pi/entwurf-control` and `~/.pi/entwurf-v2-locks` — hold the entry sets they started with, with no fixture garden id anywhere, and that proof now runs on the failure path as well as the green one. Missing prerequisites decline with the protocol SKIP (97), which `release-gate --cut` refuses rather than rounding up.
|
|
60
60
|
>
|
|
61
|
-
> **Fresh-call LIVE is on-demand, not part of `release-gate`.** `LIVE=1 ./run.sh smoke-mux-fresh-call-live` opens configured Pi and Claude Code siblings, spends two model turns, and preserves their native transcripts; run it directly whenever a cut changes mux fresh-call/launch behavior. Its private tmux servers and fixture-bound entwurf write axes make it deterministic about garden-record/socket residue, but those real native turns should not appear unexpectedly in every aggregate cut.
|
|
61
|
+
> **Fresh-call LIVE is on-demand, not part of `release-gate`.** `LIVE=1 ./run.sh smoke-mux-fresh-call-live` opens configured Pi and Claude Code siblings, spends two model turns, and preserves their native transcripts; run it directly whenever a cut changes mux fresh-call/launch behavior. Its private tmux servers and fixture-bound entwurf write axes make it deterministic about garden-record/socket residue, but those real native turns should not appear unexpectedly in every aggregate cut. **Both this smoke and `smoke-mux-lifecycle-live` enumerate `pi` and `claude-code` only — that exclusion is intentional.** A Copilot visible-fresh (step 9 clause 7) was accepted on 2026-08-25 as an operator-metered LIVE (receipts in `DELIVERY.md`'s Copilot matrix row and the issue #82 thread). It still spends Copilot premium quota, still needs its own grant to re-run, and is never folded into those loops or into the release-gate MUST tier.
|
|
62
62
|
>
|
|
63
63
|
> The aggregate release gate does not own a live agy conversation id, so agy's real native-push round trip is a separate acceptance axis: three fail-loud doctors plus `LIVE=1 AGY_CONVERSATION_ID=<id> ./run.sh smoke-agy-native-push-live`, followed by a fresh-conversation sender/reply check after package install. Its deterministic install/sender gates are already inside `pnpm run check:full`; do not misreport the aggregate gate as live agy evidence. **Cost fence:** the agy conversation this smoke drives runs on a free account, so open it on `gemini-3.6-flash` — never a Pro tier. The model is the operator's choice at conversation-open time; entwurf never selects it, and no assertion reads it (see the shipped-lane note: model display is not part of the agy contract).
|
|
64
64
|
>
|
|
@@ -146,7 +146,7 @@ git clone https://github.com/junghan0611/entwurf /path/to/entwurf && cd $_
|
|
|
146
146
|
# re-run the SAME command any time to repair a broken install
|
|
147
147
|
```
|
|
148
148
|
|
|
149
|
-
Expected tail: `DONE: entwurf setup (pi adapter + detected native bridges + v2 install smoke) green.` On a host with `claude`, verify `./run.sh doctor-meta-bridge`. On a host with `agy`, verify all three: `doctor-agy-bridge`, `doctor-agy-statusline`, and `doctor-agy-hooks`. Setup keeps optional-harness failures non-fatal so pi/Claude hosts are not bricked; the doctors are the fail-loud acceptance surface.
|
|
149
|
+
Expected tail: `DONE: entwurf setup (pi adapter + detected native bridges + v2 install smoke) green.` On a host with `claude`, verify `./run.sh doctor-meta-bridge`. On a host with `agy`, verify all three: `doctor-agy-bridge`, `doctor-agy-statusline`, and `doctor-agy-hooks`. After adding a backend to `META_BACKENDS`, re-run the sibling install then the doctor — a green checkout with a stale deployed plugin is a silent write-stop on that rail. Setup keeps optional-harness failures non-fatal so pi/Claude hosts are not bricked; the doctors are the fail-loud acceptance surface.
|
|
150
150
|
|
|
151
151
|
The wiring / meta-bridge / smoke steps are internal building blocks of `setup` (`install_local_package`, `scripts/meta-bridge-install.sh`, `validate_entwurf_bridge`) — call `setup`, never the parts. Consumers who `npm install @junghanacs/entwurf` get the obvious npm surface; that path is not the developer concern here.
|
|
152
152
|
|
package/demo/README.md
CHANGED
|
@@ -139,7 +139,7 @@ SCENE_DELAY=30 FINAL_PAUSE=10 bash demo.sh
|
|
|
139
139
|
|
|
140
140
|
## Prerequisites
|
|
141
141
|
|
|
142
|
-
- `pi` on PATH (current floor 0.84.
|
|
142
|
+
- `pi` on PATH (current floor 0.84.3)
|
|
143
143
|
- `entwurf` provider configured + auth ready for the selected sender/peer models
|
|
144
144
|
- `asciinema` installed
|
|
145
145
|
- `agg` installed (optional — only for GIF conversion)
|
package/docs/acp-backend-rail.md
CHANGED
|
@@ -93,7 +93,7 @@ undifferentiated "supported" column is what let a Claude PASS read as if it also
|
|
|
93
93
|
| Surface | Declaration | Class | What a green actually says |
|
|
94
94
|
|---|---|---|---|
|
|
95
95
|
| Entwurf package | `0.14.1` | shipped baseline | the package contract these rows belong to |
|
|
96
|
-
| pi runtime | devDep exact `0.84.
|
|
96
|
+
| pi runtime | devDep exact `0.84.3`, peer `>=0.84.3 <0.85` | **exact** oracle + **closed range** | built and certified against 0.84.3; hosts inside the range are accepted, and the ceiling moves only on measurement |
|
|
97
97
|
| ACP wire SDK | `@agentclientprotocol/sdk 1.3.0` | **exact** | the shared wire oracle both adapters speak |
|
|
98
98
|
| Claude ACP adapter | `@agentclientprotocol/claude-agent-acp 0.70.0` | **exact**, bundled | the adapter we ship and certify; resolved before any PATH fallback |
|
|
99
99
|
| Claude Agent SDK | `0.3.232` (transitive) | **exact** oracle | the runtime risk surface behind the adapter |
|
|
@@ -7,17 +7,39 @@ the one-line registration; everything an operator needs to actually wire a host
|
|
|
7
7
|
`entwurf-bridge` can also be registered in a separate MCP-aware harness (Claude Code, Codex CLI, Antigravity/`agy`, …). That host does **not** become a pi session and does **not** need to be ACP-backed. There are now two honest cases:
|
|
8
8
|
|
|
9
9
|
- **plain external MCP host**: no garden meta-record / sender marker. It can call the read surfaces (`entwurf_peers`, `entwurf_inbox_read`), but `entwurf_v2` sends are **refused by default** (#50 C4: "if we don't know who sent it, we don't send it"). The operator may wire the explicit hatch below; the send then goes out external/non-replyable.
|
|
10
|
-
- **garden-native native session**: a trusted lifecycle hook minted a garden id and sender marker — `SessionStart` for Claude Code, `PreInvocation` for agy. It is not a pi control-socket session, but it can be replyable by garden id when its own mailbox/probe rail says so.
|
|
10
|
+
- **garden-native native session**: a trusted lifecycle hook minted a garden id and sender marker — `SessionStart` for Claude Code, `PreInvocation` for agy, `userPromptSubmitted`/`sessionStart` for GitHub Copilot CLI. It is not a pi control-socket session, but it can be replyable by garden id when its own mailbox/probe rail says so.
|
|
11
11
|
|
|
12
|
-
**
|
|
12
|
+
**Being garden-native is not the same as being replyable, and Copilot is the case that separates them.** Its hook writes a sender marker, so an `entwurf_v2` send carries its own garden id and the receiver learns who wrote — measured 2026-08-21 on Copilot CLI 1.0.80, where a live send arrived under its own garden id with `origin: "meta-session"` and `replyable: false`. Replyability arrived later and through a different process: a first-party extension (`run.sh install-copilot-receive`) that the CLI forks, which binds to the same V3 record and writes a receiver marker owned by its own pid (#82 RAIL 5). So a Copilot citizen is `replyable: true` exactly while that extension is armed for it, and `replyable: false` — honestly, with a real garden identity — when it is not installed, not launched with `COPILOT_CLI_ENABLED_FEATURE_FLAGS=EXTENSIONS` (which is what `entwurf copilot` sets for one invocation), not yet born, or gone. Read the two facts off different rails: the sender marker answers *who sent this*; the receiver marker answers *can a reply land*. The onboarding obligations are in [`adding-a-harness.md`](./adding-a-harness.md) step 7, and the evidence boundary — the managed LIVE acceptance that moved receive to D6, and what D7-partial / D3-pending still exclude — is in [`DELIVERY.md`](../DELIVERY.md).
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
**Which verb an external agent should reach for:** to deliver to / reply to a garden id, use **`entwurf_v2`** — it is the canonical delivery surface and the only one that reads whether the target is live pi, dormant pi, mailbox-backed Claude Code, or native-push Antigravity and routes correctly. Discover targets with `entwurf_peers`, confirm your own identity with `entwurf_self`, drain a mailbox with `entwurf_inbox_read`, and use `entwurf_register_native` only as the explicit/manual fallback for binding an already-running agy conversation (normal agy birth is automatic through the installed hook). Open a NEW sibling with **`entwurf_fresh_call {backend, model, task, cwd?}`**, and reopen a DORMANT pi citizen under its own garden id with **`entwurf_resume_call {target}`**. Fresh call accepts one optional literal absolute `cwd`: omit it or pass `""` for the caller's cwd; otherwise it must name an existing directory and may not contain `#`. Use that input for a new cross-repository sibling — resume preserves a dormant Pi citizen's recorded continuity and is not a cwd substitute. Fresh call needs its selected runtime on the server's PATH (`pi`, `claude`, or `entwurf` — Copilot opens through the managed `entwurf copilot` invocation, never the bare vendor); resume call always needs `pi`. Both start a runtime, while delivery does not. (The old v1 verbs `entwurf` / `entwurf_resume` / `entwurf_send` are gone.)
|
|
15
|
+
|
|
16
|
+
Observed: Claude Code, Codex CLI, Antigravity CLI and GitHub Copilot CLI all reach the read surfaces through this MCP bridge from a plain external host — `entwurf_peers` is a pure fact projection, while `entwurf_inbox_read` is a **mutating drain** (it archives the messages and stamps the read-receipt), so "read" here does not mean side-effect-free; **sending** needs an identity lane. Claude and Copilot become symmetric/replyable through a mailbox-backed meta-session — Claude's watch armed by its own hook, Copilot's by the forked extension it installs; agy becomes symmetric/replyable through its record-backed sender marker plus live native-push probe. Codex has no managed citizen lifecycle yet, so a Codex host cannot send without the explicit anonymous hatch below.
|
|
15
17
|
|
|
16
18
|
Prerequisites on the host running the external MCP client:
|
|
17
19
|
|
|
18
20
|
- A live pi session launched with `--entwurf-control` populates `~/.pi/entwurf-control/<gardenId>.sock` — the key is the **record's** garden id, never a transcript/session id (`PI_SESSION_ID` only carries the id record birth already established). Required for `entwurf_v2` control-socket dispatch and `entwurf_peers`.
|
|
19
21
|
|
|
20
|
-
> **PATH boundary.** MCP servers are often launched by GUI/editor daemons and may not inherit the interactive shell's PATH. No `entwurf_v2` rail launches a process, so this does not affect delivery — but `entwurf_fresh_call` and `entwurf_resume_call` do open a fixed runtime. If that runtime works in your terminal but an external-host call fails with `spawn pi ENOENT` or `spawn
|
|
22
|
+
> **PATH boundary.** MCP servers are often launched by GUI/editor daemons and may not inherit the interactive shell's PATH. No `entwurf_v2` rail launches a process, so this does not affect delivery — but `entwurf_fresh_call` and `entwurf_resume_call` do open a fixed runtime. If that runtime works in your terminal but an external-host call fails with `spawn pi ENOENT`, `spawn claude ENOENT`, or `spawn entwurf ENOENT`, pass a full PATH in the MCP server `env`, set `ENTWURF_BRIDGE_ENV_FILE` to a small shell file that exports PATH, or point the host at a wrapper that can find the runtime. `start.sh` sources only the explicit `ENTWURF_BRIDGE_ENV_FILE`; it never reads personal dotfiles automatically.
|
|
23
|
+
|
|
24
|
+
> **Identity-carrier boundary.** A complete `PI_SESSION_ID` + `PI_AGENT_ID` pair wins before a
|
|
25
|
+
> native sender marker in the bridge's authoritative-self resolution. Those variables are correct
|
|
26
|
+
> inside the pi process that planted them from record birth; they are foreign identity in another
|
|
27
|
+
> native harness. Starting that harness from a pi citizen's bash can otherwise make the new host —
|
|
28
|
+
> and any internal agents borrowing its MCP manager — speak under the parent pi garden id. Every
|
|
29
|
+
> managed native launcher therefore removes both variables before exec and lets the launched
|
|
30
|
+
> harness establish its own identity through its trusted birth marker. Clearing only one is not a
|
|
31
|
+
> repair: an incomplete pair merely changes the failure wording, while retaining either carrier
|
|
32
|
+
> invites future partial-reader drift. An unmanaged launch that inherits them is unsupported. A
|
|
33
|
+
> native-harness admission made under this contract must add a doctor cell that names this
|
|
34
|
+
> contamination rather than silently preferring the marker.
|
|
35
|
+
>
|
|
36
|
+
> **Certified so far: Copilot only** (#82 RAIL 9). `scripts/copilot-launch.sh` unsets both before
|
|
37
|
+
> `exec`, `check-copilot-launch` asserts their absence from the vendor's OWN report of its
|
|
38
|
+
> environment (and that clearing one alone does not pass), and `doctor-copilot-receive` reports any
|
|
39
|
+
> live Copilot CLI still carrying either as RED on its own axis — a session can be perfectly armed
|
|
40
|
+
> and still speak under the parent's garden id, so the flag verdict must not absorb it. The other
|
|
41
|
+
> native launchers have NOT been certified against this failure mode; do not read Copilot's cells
|
|
42
|
+
> as coverage for them.
|
|
21
43
|
|
|
22
44
|
Example env file:
|
|
23
45
|
|