@junghanacs/entwurf 0.12.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 (183) hide show
  1. package/AGENTS.md +240 -0
  2. package/BASELINE.md +227 -0
  3. package/CHANGELOG.md +1210 -0
  4. package/CONTRIBUTING.md +63 -0
  5. package/DELIVERY.md +209 -0
  6. package/LICENSE +21 -0
  7. package/README.md +504 -0
  8. package/VERIFY.md +260 -0
  9. package/demo/README.md +188 -0
  10. package/demo/demo-baseline.sh +156 -0
  11. package/demo/demo.sh +183 -0
  12. package/docs/assets/entwurf-demo.gif +0 -0
  13. package/docs/assets/entwurf-doomemacs.gif +0 -0
  14. package/docs/assets/entwurf-entwurf.gif +0 -0
  15. package/docs/assets/entwurf-hero.jpg +0 -0
  16. package/docs/setup-clean-host.md +305 -0
  17. package/mcp/entwurf-bridge/src/index.ts +513 -0
  18. package/mcp/entwurf-bridge/start.sh +25 -0
  19. package/mcp/entwurf-bridge/test.sh +54 -0
  20. package/mcp/tsconfig.json +29 -0
  21. package/package.json +130 -0
  22. package/pi/entwurf-capabilities.json +9 -0
  23. package/pi/entwurf-targets.json +20 -0
  24. package/pi/meta-bridge/.claude-plugin/marketplace.json +12 -0
  25. package/pi/meta-bridge/entwurf-meta-receive/.claude-plugin/plugin.json +5 -0
  26. package/pi/meta-bridge/entwurf-meta-receive/hooks/hooks.json +50 -0
  27. package/pi/meta-bridge/entwurf-meta-receive/scripts/doorbell.sh +54 -0
  28. package/pi/settings.reference.json +43 -0
  29. package/pi/skill-plugin-example/.claude-plugin/plugin.json +4 -0
  30. package/pi/skill-plugin-example/skills/hello/SKILL.md +18 -0
  31. package/pi-extensions/acp-provider.ts +63 -0
  32. package/pi-extensions/entwurf-control.ts +1692 -0
  33. package/pi-extensions/lib/acp/acp-client.ts +90 -0
  34. package/pi-extensions/lib/acp/augment.ts +238 -0
  35. package/pi-extensions/lib/acp/backend-adapter.ts +319 -0
  36. package/pi-extensions/lib/acp/backend.ts +873 -0
  37. package/pi-extensions/lib/acp/config.ts +552 -0
  38. package/pi-extensions/lib/acp/context.ts +177 -0
  39. package/pi-extensions/lib/acp/engraving.ts +123 -0
  40. package/pi-extensions/lib/acp/event-mapper.ts +339 -0
  41. package/pi-extensions/lib/acp/models.ts +102 -0
  42. package/pi-extensions/lib/acp/overlay.ts +220 -0
  43. package/pi-extensions/lib/acp/prompts/engraving.md +1 -0
  44. package/pi-extensions/lib/acp/session-store.ts +418 -0
  45. package/pi-extensions/lib/acp/tool-surface.ts +184 -0
  46. package/pi-extensions/lib/entwurf-control-rpc.ts +208 -0
  47. package/pi-extensions/lib/entwurf-core.ts +2033 -0
  48. package/pi-extensions/lib/entwurf-deliverability.ts +123 -0
  49. package/pi-extensions/lib/entwurf-fact-provider.ts +158 -0
  50. package/pi-extensions/lib/entwurf-facts.ts +251 -0
  51. package/pi-extensions/lib/entwurf-mailbox-guard.ts +100 -0
  52. package/pi-extensions/lib/entwurf-peers-render.ts +154 -0
  53. package/pi-extensions/lib/entwurf-preflight.ts +247 -0
  54. package/pi-extensions/lib/entwurf-resume-args.ts +88 -0
  55. package/pi-extensions/lib/entwurf-self-address.ts +117 -0
  56. package/pi-extensions/lib/entwurf-v2-contract.ts +427 -0
  57. package/pi-extensions/lib/entwurf-v2-decider.ts +441 -0
  58. package/pi-extensions/lib/entwurf-v2-lock.ts +412 -0
  59. package/pi-extensions/lib/entwurf-v2-mailbox.ts +87 -0
  60. package/pi-extensions/lib/entwurf-v2-production.ts +326 -0
  61. package/pi-extensions/lib/entwurf-v2-release.ts +149 -0
  62. package/pi-extensions/lib/entwurf-v2-resume-marker.ts +34 -0
  63. package/pi-extensions/lib/entwurf-v2-runner.ts +196 -0
  64. package/pi-extensions/lib/entwurf-v2-send-fallback.ts +166 -0
  65. package/pi-extensions/lib/entwurf-v2-send.ts +271 -0
  66. package/pi-extensions/lib/entwurf-v2-spawn-production.ts +337 -0
  67. package/pi-extensions/lib/entwurf-v2-spawn.ts +323 -0
  68. package/pi-extensions/lib/entwurf-v2-surface.ts +205 -0
  69. package/pi-extensions/lib/meta-mailbox-body.ts +80 -0
  70. package/pi-extensions/lib/meta-session.ts +1951 -0
  71. package/pi-extensions/lib/project-trust-handler.ts +154 -0
  72. package/pi-extensions/lib/session-id.js +57 -0
  73. package/pi-extensions/lib/socket-discovery.ts +346 -0
  74. package/pi-extensions/lib/socket-probe.ts +92 -0
  75. package/pi-extensions/meta-bridge-hook.ts +250 -0
  76. package/pi-extensions/model-lock.ts +235 -0
  77. package/prompts/engraving.md +27 -0
  78. package/protocol.js +31 -0
  79. package/run.sh +2832 -0
  80. package/scripts/check-acp-backend-preflight.ts +130 -0
  81. package/scripts/check-acp-carrier-augment.ts +297 -0
  82. package/scripts/check-acp-config.ts +322 -0
  83. package/scripts/check-acp-event-mapper.ts +253 -0
  84. package/scripts/check-acp-overlay.ts +154 -0
  85. package/scripts/check-acp-prompt-builder.ts +191 -0
  86. package/scripts/check-acp-provider-surface.ts +159 -0
  87. package/scripts/check-acp-sdk-surface.ts +211 -0
  88. package/scripts/check-acp-session-reuse.ts +756 -0
  89. package/scripts/check-acp-session-store.ts +387 -0
  90. package/scripts/check-acp-tool-surface.ts +159 -0
  91. package/scripts/check-entwurf-bridge-boot.ts +199 -0
  92. package/scripts/check-entwurf-capabilities.ts +123 -0
  93. package/scripts/check-entwurf-control-rpc.ts +187 -0
  94. package/scripts/check-entwurf-deliverability.ts +158 -0
  95. package/scripts/check-entwurf-fact-provider.ts +316 -0
  96. package/scripts/check-entwurf-facts.ts +358 -0
  97. package/scripts/check-entwurf-mailbox-guard.ts +264 -0
  98. package/scripts/check-entwurf-peers-surface.ts +235 -0
  99. package/scripts/check-entwurf-resume-args.ts +149 -0
  100. package/scripts/check-entwurf-self-address.ts +209 -0
  101. package/scripts/check-entwurf-session-identity.ts +703 -0
  102. package/scripts/check-entwurf-v2-contract.ts +536 -0
  103. package/scripts/check-entwurf-v2-decider.ts +795 -0
  104. package/scripts/check-entwurf-v2-lock.ts +368 -0
  105. package/scripts/check-entwurf-v2-mailbox.ts +228 -0
  106. package/scripts/check-entwurf-v2-matrix.ts +437 -0
  107. package/scripts/check-entwurf-v2-production.ts +406 -0
  108. package/scripts/check-entwurf-v2-release.ts +245 -0
  109. package/scripts/check-entwurf-v2-runner.ts +452 -0
  110. package/scripts/check-entwurf-v2-send-fallback.ts +371 -0
  111. package/scripts/check-entwurf-v2-send.ts +454 -0
  112. package/scripts/check-entwurf-v2-spawn-production.ts +452 -0
  113. package/scripts/check-entwurf-v2-spawn.ts +398 -0
  114. package/scripts/check-entwurf-v2-surface.ts +352 -0
  115. package/scripts/check-keyset-overlap.py +120 -0
  116. package/scripts/check-mailbox-receipt-state.ts +170 -0
  117. package/scripts/check-meta-capability-source.ts +112 -0
  118. package/scripts/check-meta-dual-consumers.ts +154 -0
  119. package/scripts/check-meta-dual-read.ts +158 -0
  120. package/scripts/check-meta-listing.ts +138 -0
  121. package/scripts/check-meta-mailbox-state-write.ts +135 -0
  122. package/scripts/check-meta-migration.ts +212 -0
  123. package/scripts/check-meta-receiver-marker.ts +185 -0
  124. package/scripts/check-meta-record-v2.ts +191 -0
  125. package/scripts/check-meta-session.ts +673 -0
  126. package/scripts/check-model-lock.ts +408 -0
  127. package/scripts/check-package-source-routing.ts +253 -0
  128. package/scripts/check-pi-preflight.ts +304 -0
  129. package/scripts/check-project-trust-handler.ts +265 -0
  130. package/scripts/check-shell-quote.ts +121 -0
  131. package/scripts/check-socket-discovery.ts +428 -0
  132. package/scripts/check-socket-probe.ts +106 -0
  133. package/scripts/fixtures/probe-mcp-server.ts +33 -0
  134. package/scripts/gnew-rpc-drive.ts +211 -0
  135. package/scripts/lib/acp-child-cleanup.ts +116 -0
  136. package/scripts/meta-bridge-doctor.sh +315 -0
  137. package/scripts/meta-bridge-hook-log.sh +26 -0
  138. package/scripts/meta-bridge-install.sh +135 -0
  139. package/scripts/meta-bridge-prune.ts +199 -0
  140. package/scripts/meta-bridge-state.py +549 -0
  141. package/scripts/meta-bridge-statusline.sh +192 -0
  142. package/scripts/meta-bridge-store-doctor.ts +64 -0
  143. package/scripts/meta-bridge-uninstall.sh +39 -0
  144. package/scripts/new-session-id.ts +25 -0
  145. package/scripts/postinstall-chmod.cjs +58 -0
  146. package/scripts/raw-async-delivery/README.md +258 -0
  147. package/scripts/raw-async-delivery/cc-enqueue-addressed.sh +35 -0
  148. package/scripts/raw-async-delivery/cc-mailbox-rewake.sh +38 -0
  149. package/scripts/raw-async-delivery/cc-watch-filechanged.sh +19 -0
  150. package/scripts/raw-async-delivery/cc-watch-sessionstart.sh +15 -0
  151. package/scripts/raw-async-delivery/codex-local-appserver.sh +39 -0
  152. package/scripts/raw-async-delivery/plugin-entwurf-receive/.claude-plugin/plugin.json +5 -0
  153. package/scripts/raw-async-delivery/plugin-entwurf-receive/hooks/hooks.json +28 -0
  154. package/scripts/raw-async-delivery/plugin-entwurf-receive/scripts/watch-filechanged.sh +28 -0
  155. package/scripts/raw-async-delivery/plugin-entwurf-receive/scripts/watch-sessionstart.sh +27 -0
  156. package/scripts/raw-async-delivery/raw-agy-send.sh +29 -0
  157. package/scripts/raw-async-delivery/raw-claude-enqueue.sh +30 -0
  158. package/scripts/raw-async-delivery/raw-codex-ws-turn-start.py +164 -0
  159. package/scripts/raw-async-delivery/repro-addressed-routing.sh +96 -0
  160. package/scripts/raw-async-delivery/repro-plugin-idle-wake.sh +104 -0
  161. package/scripts/resolve-acp-bridge.ts +25 -0
  162. package/scripts/smoke-acp-bundled-mcp-live.ts +292 -0
  163. package/scripts/smoke-acp-carrier-augment-live.ts +192 -0
  164. package/scripts/smoke-acp-mcp-live.ts +129 -0
  165. package/scripts/smoke-acp-memory-containment-live.ts +389 -0
  166. package/scripts/smoke-acp-overlay-live.ts +314 -0
  167. package/scripts/smoke-acp-provider-live.ts +162 -0
  168. package/scripts/smoke-acp-raw-turn-live.ts +261 -0
  169. package/scripts/smoke-acp-session-reuse-live.ts +172 -0
  170. package/scripts/smoke-acp-skill-live.ts +144 -0
  171. package/scripts/smoke-acp-socket-citizen-live.ts +168 -0
  172. package/scripts/smoke-claude-native-resume-live.sh +198 -0
  173. package/scripts/smoke-entwurf-v2-matrix-live.ts +398 -0
  174. package/scripts/smoke-entwurf-v2-spawn-live.ts +175 -0
  175. package/scripts/smoke-entwurf-v2-spawn-resume-live.ts +428 -0
  176. package/scripts/smoke-meta-async-drift.sh +171 -0
  177. package/scripts/smoke-meta-honesty.sh +147 -0
  178. package/scripts/smoke-meta-install-state.sh +403 -0
  179. package/scripts/smoke-meta-keyset-guard.sh +111 -0
  180. package/scripts/smoke-meta-prune.sh +174 -0
  181. package/scripts/smoke-resident-garden-guard.sh +433 -0
  182. package/scripts/smoke-session-id-name.ts +187 -0
  183. package/scripts/tsconfig.json +34 -0
package/VERIFY.md ADDED
@@ -0,0 +1,260 @@
1
+ # VERIFY.md
2
+
3
+ Agent-driven verification guide for `entwurf` (0.12.0 surface).
4
+
5
+ > **Current surface.** The live release surface is one bundled MCP server, `entwurf-bridge`, exposing four tools: `entwurf_v2`, `entwurf_peers`, `entwurf_self`, `entwurf_inbox_read`. The shipped ACP backend is **Claude**; Codex is pi-native by default (`ENTWURF_ACP_FOR_CODEX=1` opts a Codex target into ACP), and Gemini is a **non-goal/probe** on 0.12 — historical Gemini rows are kept for context, not as a current expectation. The 0.4.x `session-bridge` adapter, the 0.11.0 fat-bridge (`acp-bridge.ts` / `ensureBridgeSession`), and the v1 `entwurf` / `entwurf_resume` / `entwurf_send` verbs are **retired** — rows mentioning them survive in CHANGELOG/git as historical baseline, never as a runnable recipe.
6
+
7
+ This is a **working document, not a metrics document**. The deterministic and live gates carry the machine-checkable invariants; this file carries only what a gate cannot judge — the human/agent reading of *whether the bridge is honestly itself*. Where a former manual procedure is now a gate, it is named as a pointer rather than re-spelled as a runnable script.
8
+
9
+ VERIFY.md is the **agent-driven** surface; [BASELINE.md](./BASELINE.md) is the operator-driven one. One ACP-bridged model runs the checks against another and writes down what it sees — if the bridge is faithful, two replicants looking at the same mirror describe the mirror the same way. This is in-bridge cross-validation, not external evidence: verifier and subject share the same bridge, MCP servers, and overlay, so a uniform corruption of those would not surface here (that gap is what the L3+ rungs close).
10
+
11
+ ## Evidence Levels
12
+
13
+ Every claim — and every History entry — sits on one of these rungs. Make the rung explicit so neither narrative nor reader overreaches.
14
+
15
+ > **Namespace note.** These `L0–L5` rungs measure *evidence quality* for bridge verification. Native async delivery has its own capability namespace `D0–D8` in [DELIVERY.md](./DELIVERY.md); operator-driven identity baseline uses `Q-L1..Q-L5` *surface-isolation layers* in [BASELINE.md](./BASELINE.md). Same letters, different axes — do not conflate "high-quality evidence" with "high delivery capability".
16
+
17
+ | Level | What it is | Closes | Does not close |
18
+ |---|---|---|---|
19
+ | **L0** | Narrative / self-report | Agent description of the system | Anything depending on actual behaviour |
20
+ | **L1** | Transcript cross-check | Two+ bridged identities agree on what they see | Echo-chamber risk (shared prompt/carrier) |
21
+ | **L2** | Objective MCP tool call | Real on-disk/on-socket payload through the bridge | Shared-implementation corruption |
22
+ | **L3** | On-disk/process/socket corroboration *outside* the bridge | Bridge claim ↔ `ls`/`pgrep`/`lsof`/session JSONL | Time-extended drift (auth, version, cache) |
23
+ | **L4** | Human or direct-native side-by-side | A person (or non-bridged direct path) reaches the same answer for matched prompts | Production-shape workload |
24
+ | **L5** | Long-haul soak | Bridge stays correct over hours-to-days incl. partial failure | Operational ceiling for now |
25
+
26
+ When you write a new entry, mark its rung. "L1 only" is honest; "L2 reached" is stronger but does not silently imply L3.
27
+
28
+ ---
29
+
30
+ ## 0A. Execution Policy — Transparent Mode
31
+
32
+ Verification here is not a benchmark. In production we exchange short turns and stop immediately to isolate a cause before resuming when something looks off. This document records **verification intent (what we look at) and pass criteria (how to judge)**; the execution shape is the agent's choice as long as the criteria are met.
33
+
34
+ ### The canonical floor — two entry points
35
+
36
+ - **Deterministic floor:** `pnpm check` — the full `check-*` gate set (~60 gates). Run first; it is the cheap, machine-checkable layer.
37
+ - **Live floor:** `LIVE=1 ./run.sh release-gate <scratch-project-dir>` — `pnpm check` + the v2-native live gates + the ACP plugin acceptance floor. It reports a **two-tier summary**:
38
+ - **MUST tier** (release-blocking — owns the exit code; "green" applies only here): `pnpm check`, `smoke-entwurf-v2-spawn-resume-live`, `smoke-entwurf-v2-matrix-live`, `check-bridge`, `smoke-session-id-name`, the resident-garden-guard negative/id-safety + `/gnew` zero-token half, and the `smoke-acp-*-live` ACP plugin smokes (socket-citizen / raw-turn / overlay / provider / session-reuse / carrier-augment / memory-containment / rgg / mcp / skill / bundled-mcp).
39
+ - **BEHAVIOR tier** (advisory, non-blocking): the resident-garden-guard positive (a model-in-loop `entwurf_self` turn). A BEHAVIOR FAIL is surfaced with its artifact path but **never blocks the cut**.
40
+ - LIVE-gated MUST steps honest-skip when `LIVE!=1`; a real cut needs `LIVE=1` with `SKIP=0`. A green MUST gate is **necessary, not sufficient** — GLG authorizes the cut.
41
+
42
+ > The authoritative per-cut counts live in BASELINE.md's HISTORY and CHANGELOG/git, not inline here (they drift against `run.sh`). Most recent recorded floor: **2026-06-27 — MUST 17/0/0 + BEHAVIOR 1/0**.
43
+
44
+ ### Verifying the two capabilities a gate cannot fully judge
45
+
46
+ - **Garden-id delivery:** discover a target with `entwurf_peers`, then `entwurf_v2` with the correct intent — `fire-and-forget` for a live/replyable or meta-session target, `owned-outcome` only to wake a dormant record-backed pi citizen. Picking the wrong intent is rejected, never auto-fixed.
47
+ - **ACP continuity:** a direct `pi --provider entwurf --model claude-sonnet-4-6` turn, or the `smoke-acp-session-reuse-live` gate (process-scoped reuse + recall). Multi-turn reuse is proven by that gate, not by any v1 resume tool.
48
+
49
+ ### What NOT to do — bypassing the operational path
50
+
51
+ These bypass the very delegation logic under test; passing them proves nothing about production health.
52
+
53
+ - ✗ Minting session files directly (`mktemp …jsonl`) and feeding them to `pi --session`.
54
+ - ✗ Faking multi-turn by passing the same session file twice.
55
+ - ✗ Using pty/tmux `send-keys` keystrokes or transcript scraping as delivery evidence.
56
+ - ✗ Mimicking entwurf by recursively calling `pi` via `bash`.
57
+
58
+ The manual `pi --session` path is used only when (a) the entwurf path itself is broken and an isolated debug bypass is needed, or (b) a boundary check must hit a bridge internal directly.
59
+
60
+ ### Operational principles
61
+
62
+ - Execute one command at a time (no `;`-chaining). Preserve full stdout/stderr at each step.
63
+ - On anything wrong, **stop and hold** — preserve session/cache/process state before proceeding.
64
+
65
+ ### Wording — avoid safety-interpretation contamination
66
+
67
+ When injecting a fact for a continuity check, use **plaintext that does not trigger model safety interpretation**. Avoid `secret token`, `password`, `API key`, `credential`, and meta-directives like "do not leak" — such wording makes the model treat the prompt as an exfiltration attempt and refuse, which makes **continuity look broken even when it is alive** (this happened once with `test-token-123`, misdiagnosed as a delegation failure). Instead: `The password is owl → reply in one word → owl`; code names / colors / animal names. Do not mix continuity and safety-behavior verification in one prompt.
68
+
69
+ ### bridge continuity vs semantic continuity
70
+
71
+ - **bridge continuity:** same `sessionKey` / same `acpSessionId` via in-memory reuse or persisted resume/load (bootstrap `path=reuse|resume|load`).
72
+ - **semantic continuity:** a fact from a prior turn is retrievable in a later turn.
73
+
74
+ Either can be alive while the other looks dead (the wording case above is bridge-alive / semantic-looks-dead). When in doubt, change the wording and retry once, and check the `[entwurf:bootstrap]` lines in bridge stderr. No automated smoke separates these yet.
75
+
76
+ ## 0. Quality Criteria
77
+
78
+ The goal is not merely "invoke Claude Code." We want:
79
+
80
+ 1. **Session continuity at the agent-shell level** — through ACP session resume/load/new, not re-throwing a text blob.
81
+ 2. **Preservation of pi harness semantics** — pi session files / transcripts / memory pipeline stay a shared axis.
82
+ 3. **restart-hygienic** — process-scoped reuse continues the same ACP session across turns inside a long-lived resident; persisted records are written/validated for the future resume-load lane, not the live continuity path today.
83
+ 4. **Thin bridge** — no second harness built inside this repo.
84
+ 5. **Explicit capability boundary** — pi custom tool / user MCP visibility is determined solely by `entwurfProvider.mcpServers`; no automatic `~/.mcp.json` loading.
85
+ 6. **Operational hygiene** — no orphan subprocesses, no excess persisted session garbage.
86
+
87
+ ---
88
+
89
+ ## 1. Setup
90
+
91
+ entwurf supports two install paths; both end in the same runtime state (a valid `.pi/settings.json` with `entwurfProvider.mcpServers` wired) and differ only in who owns the checkout.
92
+
93
+ | Path | Who | Shape |
94
+ |------|-----|-------|
95
+ | **A — Consumer** | end-user of pi | `pi install git:…` + one `run.sh install .` |
96
+ | **B — Developer** | contributor / first user | `git clone …` + `pi install ./` + `run.sh install …` |
97
+
98
+ ### 1.1 Path A — consumer install
99
+
100
+ ```bash
101
+ pi install git:github.com/junghan0611/entwurf # pi auto-clones + installs deps
102
+ cd /path/to/consumer-project
103
+ ~/.pi/agent/git/github.com/junghan0611/entwurf/run.sh install . # wire bundled mcpServers
104
+ pi --list-models entwurf # curated model surface
105
+ pi --provider entwurf --model claude-sonnet-4-6 -p "reply with ok only" # one-turn smoke
106
+ ```
107
+
108
+ Expected: the package appears under pi's `User packages`; `install .` logs `added entwurfProvider.mcpServers.entwurf-bridge` + `updated <project>/.pi/settings.json`; the smoke returns a one-word reply (full bootstrap → ACP session → bridge → clean shutdown). Note: `~/.pi/agent/git/.../entwurf` is pi-managed — do not edit it (a `pi update` overwrites). Step 2 is still required after `pi install git:…`.
109
+
110
+ ### 1.2 Path B — developer install
111
+
112
+ ```bash
113
+ git clone https://github.com/junghan0611/entwurf /path/to/entwurf && cd $_
114
+ pnpm install # pnpm is the pinned packageManager
115
+ pi install ./
116
+ ./run.sh install /path/to/consumer-project
117
+ pnpm check # full deterministic floor
118
+ pi --provider entwurf --model claude-sonnet-4-6 -p "reply with ok only"
119
+ LIVE=1 ./run.sh release-gate /path/to/consumer-project
120
+ ```
121
+
122
+ `install` is idempotent; user-authored `mcpServers.<name>` overrides survive a re-run (`preserved (user override: …)`). `run.sh remove <dir>` deletes only entries whose command matches the repo-authored launcher.
123
+
124
+ ### 1.3 Setup shortcut + variables
125
+
126
+ ```bash
127
+ export REPO_DIR=... # Path A: $HOME/.pi/agent/git/github.com/junghan0611/entwurf ; Path B: your clone
128
+ export PROJECT_DIR=/path/to/consumer-project
129
+ export CACHE_DIR=$HOME/.pi/agent/cache/entwurf/sessions
130
+ cd "$REPO_DIR" && ./run.sh setup "$PROJECT_DIR"
131
+ ```
132
+
133
+ `setup` runs `pnpm install` + `install` + meta-bridge (native harness) + the v2 install smoke; a green `setup` implies the settings.json wiring and install surface are healthy. The full live floor is still `LIVE=1 ./run.sh release-gate`.
134
+
135
+ ### 1.4 Cross-install / cross-backend parity (optional, high-value)
136
+
137
+ Compare a fresh self-awareness report across axes: (1) same backend, different install path — answer must be path-invariant; (2) same backend, different machine — identical native tool list + MCP server/tool set; (3) different backend, same bridge — same harness id (`entwurf`) and MCP surface but **different** native tool surface (a Claude session reporting `apply_patch` as native, or normalized cross-backend tools, is a fail); (4) native pi routing vs ACP-bridged, same model — the native target reports **no `entwurf-bridge` MCP** (capability via pi's extension surface) while the ACP target reports it as the single MCP server. Honest "native: I cannot tell" hedging is PASS on the native side. Status: Claude axes 1–4 closed; Gemini is probe-only on 0.12.
138
+
139
+ ---
140
+
141
+ ## 1A. Main Agent Evaluation — Is `entwurf` Claude strong enough?
142
+
143
+ Separate from continuity gates. Gates prove "sessions continue"; this questionnaire examines tool self-awareness / native tool usability / MCP-boundary awareness / long-turn focus / quality vs direct Claude Code. Run it against `entwurf/claude-sonnet-4-6` via a direct `pi --provider entwurf` turn (or a live ACP session); accumulate turns by re-prompting the same target.
144
+
145
+ ### 1A.0 Two carrier surfaces — engraving vs pi-context-augment (load-bearing)
146
+
147
+ `entwurf` delivers identity-relevant text through **two structurally distinct surfaces**. Collapsing them into "the system prompt" is the most common verifier-side mistake. (BASELINE Q-B0/Q-L1 grade the same separation operator-side.)
148
+
149
+ | Surface | Source | Delivery shape | Default content |
150
+ |---|---|---|---|
151
+ | **Engraving carrier** | `pi-extensions/lib/acp/prompts/engraving.md` (or `ENTWURF_ACP_ENGRAVING_PATH`) | Claude `_meta.systemPrompt` — full-replacement identity slot | Operator-authored, optional opt-out; tiny non-empty by default on Claude ACP (replaces the `claude_code` preset + strips its auto-memory advertisement). Emptying the file is the opt-out. |
152
+ | **pi-context-augment** | `pi-extensions/lib/acp/augment.ts` (`enrichTaskWithProjectContext`) | First-user-message prepend (not the system slot) | Always populated on ACP-routed targets: (1) the bridge identity line, (2) `~/AGENTS.md` body, (3) the cwd repo's `AGENTS.md` in a `<project-context path="…">` block. |
153
+
154
+ Pass (carrier honesty): the subject distinguishes engraving from pi-context-augment by name or structure without prompting; on ACP targets confirms all three augment components arrived; may quote the engraving but must **not** attribute bridge identity / AGENTS / memory policy to it. Fail: attributes the bridge-identity narrative to the engraving carrier; claims the augment is empty on an ACP run; invents engraving content. Native pi exception: on native targets the bridge-identity line and `~/AGENTS.md` are not part of the augment — the PASS criterion is honesty about what arrived, not the three-component checklist.
155
+
156
+ ### 1A.1 Layers
157
+
158
+ - **Layer 0 — self-awareness:** ask environment self-awareness / MCP visibility / upstream-instruction awareness, guessing prohibited. Pass: recognizes native tool family, says "I don't know" honestly, answers MCP visibility only as configured, describes upstream instruction type without reproducing internal prompts. Fail: claims a nonexistent tool, conflates pi-custom and native tools, hallucinates MCP visibility, or conflates the two carriers (§1A.0).
159
+ - **Layer 1 — native tool use:** throw file-reading / structure-analysis / regression-hunting tasks. Pass: Read/Edit/Bash/Grep/Glob selection is natural; no detour through MCP or recursive `pi`. Fail: strange detours for simple reads; speaks from memory without reading.
160
+ - **Layer 2 — MCP boundary:** by default the four v2 MCP tools are not visible (they appear only when `entwurf-bridge` is registered). Pass: says invisible tools are not visible; explains the native-vs-MCP boundary. Fail: pretends to use an unseen tool; mimics entwurf via recursive `pi`.
161
+ - **Layer 3 — focus across turns:** inject a fact, then accumulate turns mixing retrieval/exploration. Pass (post-0.4.1): after **8 turns** holds **3+ early facts** incl. **one verbatim string injected before turn 5**; no repeated exploration, no self-contradiction, no tool-strategy drift. Fail: forgets early reads; paraphrases instead of returning the verbatim string. Note: entwurf exposes no user-facing compaction; use the backend's `usage_update` footer as an overflow-risk signal (it follows the ACP backend's `used/size`, not pi's visible-transcript estimate).
162
+ - **Layer 4 — vs direct Claude Code:** requires a verifier holding **both** the `entwurf` and a direct path (human-in-loop, or both transport handles). Compare latency / native tool accuracy / detours / boundary confusion / quality around turns 10–15. Repeated tool confusion, long-turn forgetting, or boundary workarounds are a fail.
163
+
164
+ Interpretation: Layers 0–2 healthy → basic qualifications confirmed. Layer 3 weak → strengthen prompt shape + corroborate with bootstrap logs / process state / sentinel recall. Layer 4 much weaker than direct → revisit bridge handoff. This questionnaire does not replace gates.
165
+
166
+ ---
167
+
168
+ ## 2. Manual judgement checks — what the gates cannot fully judge
169
+
170
+ The single-turn / multi-turn / cross-process / persistence-boundary / shutdown invariants that earlier editions hand-ran against the retired v1 verbs are now **deterministic or live gates**. Verify them through the gate, and reserve manual time for the human-judgement surfaces below.
171
+
172
+ | Invariant | Current gate (pointer) |
173
+ |---|---|
174
+ | Single-turn prompt extraction, SessionStart hook not mistaken for prompt | `smoke-acp-raw-turn-live`, `check-acp-prompt-builder` |
175
+ | Multi-turn continuity + recall (process-scoped reuse) | `smoke-acp-session-reuse-live`, `check-acp-session-reuse` |
176
+ | Cross-process continuity / cache before-after | `check-acp-session-store` (signature, decideBootstrap, persist/parse) |
177
+ | Lifecycle policy — a turn-scoped `cwd:` fallback is never a persisted resume/load path; process-scoped records are hashed-`sessionKey` records | `check-acp-session-store` (`resolveLifecyclePolicy` turn-scoped→always-new, `decideBootstrap`, sha256 `SessionRecord` build/parse/roundtrip) — the former inline `acp-bridge.ts` repro is retired with the fat-bridge |
178
+ | Tool-call / event mapping | `check-acp-event-mapper`, `smoke-acp-provider-live` |
179
+ | Operator mcpServers / skills reach the live session | `smoke-acp-mcp-live`, `smoke-acp-skill-live`, `check-acp-config` |
180
+ | Overlay isolation + memory containment | `check-acp-overlay`, `smoke-acp-memory-containment-live`, `check-acp-tool-surface` |
181
+
182
+ ### 2.1 MCP callable-identifier shape (verified property, gate-external)
183
+
184
+ The literal callable identifier differs per backend — probe by asking the agent to print it **verbatim** (do not ask "hyphen or underscore" — ambiguous between outer separator and inner server name):
185
+
186
+ | Backend | Literal identifier | Outer sep | Inner server name |
187
+ |---|---|---|---|
188
+ | Claude | `mcp__entwurf-bridge__entwurf_v2` | `__` | `entwurf-bridge` (hyphen) |
189
+ | Codex | `mcp__entwurf_bridge__.entwurf_v2` | `__` | `entwurf_bridge` (underscore) + **literal dot** |
190
+ | Gemini *(probe)* | `mcp_entwurf-bridge_entwurf_v2` | `_` (single) | `entwurf-bridge`, no dot |
191
+
192
+ A Claude session reporting the underscore form, or any cross-shape leak, is a backend-identification leak. Shipped 0.12 baseline is Claude; the Codex/Gemini rows are reference for the probe lanes.
193
+
194
+ ### 2.2 MCP injection visibility — equal across resume/load/new
195
+
196
+ The sole MCP responsibility of `entwurf` is to inject `entwurfProvider.mcpServers` equally into `newSession` / `resumeSession` / `loadSession`. Ask "list the visible MCP server names": the registered `entwurf-bridge` appears, unregistered MCPs do not (no automatic `~/.mcp.json` loading); the list is identical every turn; changing `entwurfProvider.mcpServers` changes `bridgeConfigSignature` and forces a new session. `check-acp-config` + `smoke-acp-mcp-live` pin this; the manual check is an honesty corroboration.
197
+
198
+ ### 2.3 Process / cache hygiene — the orphan bound (§gate-external judgement)
199
+
200
+ Apply per backend under test:
201
+
202
+ ```
203
+ AFTER_<BACKEND> ≤ BEFORE_<BACKEND> + (distinct alive
204
+ (sessionKey, backend, modelId, bridgeConfigSignature) tuples this run holds open)
205
+ ```
206
+
207
+ An **upper bound**, not an equation: child reuse (one `entwurf` + N resumes share one child → delta 0 is expected) and idle reaping push `AFTER` below it; a config-signature or `(provider, model)` switch pushes it up by 1. `AFTER > BEFORE + alive_tuples` is the actionable signal — an unexpected child appeared. Walk the parent chain (`pgrep -af 'claude-agent-acp|codex-acp'` → `ps -o ppid=`); any ACP child whose parent `pi` has exited is an **orphan** — flag and preserve as evidence.
208
+
209
+ ### 2.4 pi session record as a shared memory axis
210
+
211
+ The key invariant: **pi session files stay the shared record source even under ACP**. After a reuse pair finishes, locate the child pi session JSONL and confirm turns accumulated:
212
+
213
+ ```bash
214
+ ls ~/.pi/agent/sessions/--*--/*_<SESSION_ID>.jsonl # path pattern, not a naive grep (which also hits the parent)
215
+ jq -r '.message.role // .type' "$F" | sort | uniq -c # role lives at .message.role
216
+ ```
217
+
218
+ Pass: user/assistant turns accumulate normally; the transcript is not broken/empty because ACP was used. We preserve "Claude via ACP, memory via the pi axis (JSONL → Denote/andenken)" — the AI does not run its own memory layer.
219
+
220
+ ---
221
+
222
+ ## 3. Pass criteria — the 0.12 release floor
223
+
224
+ The minimum passing bar:
225
+
226
+ 1. **Deterministic floor green:** `pnpm check` passes (lint + typecheck + the `check-*` gate set + `check-pack`).
227
+ 2. **Live floor MUST green:** `LIVE=1 ./run.sh release-gate <dir>` reports `MUST PASS=N FAIL=0 SKIP=0`; a BEHAVIOR FAIL is advisory, not blocking.
228
+ 3. **Honest self-recognition:** the bridged model identifies the harness as `entwurf`, names its backend, lists `entwurf-bridge` as the single MCP server with its four v2 tools, and presents a **backend-native** (not normalized) tool surface.
229
+ 4. **Carrier separation honored:** engraving vs pi-context-augment kept distinct (§1A.0); no bridge-identity narrative attributed to the engraving carrier.
230
+ 5. **Boundary preservation across backends/machines:** for every shipped or explicitly probed backend, regardless of install path or host, no cross-backend tool-surface contamination and no confabulation about pi internals.
231
+ 6. **Hygiene:** no orphan ACP children; no unexpected persisted session garbage (a turn-scoped `cwd:` fallback is never a persisted reuse).
232
+
233
+ Passing establishes a **release verification floor**, not an 8-hour/day operational guarantee. The floor says: gates hold, the agent honestly recognizes its environment, no tool surface is normalized away, no identity leaks, no orphans. It does **not** say a real-day workload (50–100+ turns, tool bursts, partial MCP failures, auth/version drift) survives — that needs L3–L5 evidence (appendix).
234
+
235
+ ---
236
+
237
+ ## Appendix — troubleshooting & history
238
+
239
+ ### Troubleshooting hooks
240
+
241
+ - **`ENTWURF_CHILD_STDERR_LOG`** mirrors child stderr to a file for bootstrap-path visibility — but it must be present at **bridge-process spawn time**; `export` from a shell already bound to a running bridge does not propagate. Restart the parent session with it exported, then `grep -E '\[entwurf:(bootstrap|model-switch|cancel|shutdown)\]' "$ENTWURF_CHILD_STDERR_LOG"`.
242
+ - **Retired dedicated smokes, live code invariants** (manual/troubleshooting only — *not* part of the release floor):
243
+ - *Model-switch lock* — entwurf sessions are locked to their starting model. Gate: `check-model-lock` (in `pnpm check`). The dedicated live `smoke-model-switch` was retired in v2; the invariant lives in `pi-extensions/model-lock.ts` (extension guard) + `session-store.ts` `SessionModelLockedError` (the `decideBootstrap` fail-loud model lock).
244
+ - *Cancel / abort cleanup* — `onAbort` → `cancelActivePrompt()` (session stays reusable); the stream catch closes the bridge only on `stopReason === "error"`. Dedicated `smoke-cancel` retired; invariant in code.
245
+ - *Transcript-poison invalidation (#12)* — **historical (0.11):** a poisoned backend transcript (empty text block ± `cache_control`) returned the same Anthropic 400 forever, handled by a dedicated classifier + `verify-transcript-poison` smoke. Both were retired in the 0.12 cutover; there is **no dedicated classifier or gate on the current surface** — recorded only so the failure mode is not forgotten.
246
+
247
+ ### Evidence preservation when a problem occurs
248
+
249
+ ```bash
250
+ pgrep -af 'claude-agent-acp|codex-acp' || true
251
+ find "$CACHE_DIR" -maxdepth 1 -type f | sort
252
+ ls ~/.pi/agent/sessions/--*--/*_${SESSION_ID}.jsonl 2>/dev/null
253
+ [ -n "$ENTWURF_CHILD_STDERR_LOG" ] && grep -E '\[entwurf:(bootstrap|model-switch|cancel|shutdown)\]' "$ENTWURF_CHILD_STDERR_LOG"
254
+ ```
255
+
256
+ Also preserve: the exact calls used, full stdout/stderr, the child pi session file path, cache-directory changes, and the expected-vs-actual difference.
257
+
258
+ ### History (pointer)
259
+
260
+ The full R2R run history (2026-04-27 → 2026-05-29, pi-shell-acp era), the per-claim evidence ledger (load-bearing claims with level-reached / blind spot / next test, maintained through 0.5.x–0.8.x), and the experimental L3–L5 tracks (4-cell verifier×subject matrix with on-disk corroboration; long-haul soak; direct-native parity panel) live in **CHANGELOG.md and git history**. Evidence reached **L2** (cross-vendor + reverse-direction MCP calls); L3 is partially exercised by the process/session-file checks above; the honest gap is **L3 → L5**. The most recent recorded floor baseline is in [BASELINE.md](./BASELINE.md)'s HISTORY section.
package/demo/README.md ADDED
@@ -0,0 +1,188 @@
1
+ # entwurf recorded demos
2
+
3
+ > **Archived pre-0.12 evidence.** These scripts still describe the retired v1 `entwurf` / `entwurf_resume` / `entwurf_send` demo flow. They are kept for historical comparison and will be replaced by a v2-native retake in a post-0.12 follow-up; do not treat them as the current 0.12 runnable recipe.
4
+
5
+ Two recorded GIF scripts live here. Both use `asciinema` for capture and
6
+ `agg` for cast → GIF conversion. Both share the same gitignore policy
7
+ (`*.cast` global, `demo/*.gif`, `demo/*.log` — only the `.sh` and this
8
+ README are tracked).
9
+
10
+ ## `demo-baseline.sh` — single-pane baseline + entwurf surface
11
+
12
+ Historical pre-0.12 demo, still linked from the top-level `README.md` as archived evidence. `package.json#pi.image` now points at the `entwurf` release hero instead. Two scenes driven into one pi pane:
13
+
14
+ 1. **Baseline self-awareness** — the `Q-B0` + `Q-B0-CARRIER` interview,
15
+ English answer enforced. The pane exposes how the running model
16
+ names its harness, distinguishes native tools from MCP / custom
17
+ tools, names the carrier surface for each cited piece of
18
+ information, and admits unknowns instead of fabricating.
19
+ 2. **Entwurf surface** — the same pane spawns a sibling via the
20
+ `entwurf` tool (`mode=sync`, cross-model — driver `claude-sonnet-4-6`,
21
+ sibling `gpt-5.4`), prints the Session ID, and quotes the sibling's
22
+ one-line reply verbatim.
23
+
24
+ Run from the repo root (or `demo/`):
25
+
26
+ ```bash
27
+ bash demo/demo-baseline.sh
28
+ ```
29
+
30
+ Output lands directly in the publish surface — re-running the script
31
+ overwrites the previous take so the gallery preview always matches the
32
+ current branch:
33
+
34
+ - `docs/assets/entwurf-demo.cast` — raw asciinema recording (gitignored)
35
+ - `docs/assets/entwurf-demo.gif` — tracked, referenced by the top-level `README.md` as archived pre-0.12 evidence
36
+ - `demo/baseline-debug.log` — `ENTWURF_DEBUG=1` stderr (gitignored, stays next to the script)
37
+
38
+ ## `demo.sh` — two-pane entwurf flow (regression guard)
39
+
40
+ Three-scene entwurf flow used for regression evidence, not for the
41
+ public README. Two pi sessions run side-by-side in one tmux window;
42
+ a background driver types the scene prompts into the sender pane.
43
+
44
+ 1. **Spawn** a sonnet sibling (memory write — "tempered indigo").
45
+ 2. **Resume** that same sibling (memory recall — identity preservation,
46
+ the #9 regression guard).
47
+ 3. **Cross-session greeting** via `entwurf_send` (sent box ↔ received box).
48
+
49
+ Run:
50
+
51
+ ```bash
52
+ bash demo/demo.sh
53
+ ```
54
+
55
+ Output mirrors the baseline demo's layout — recording artifacts land in
56
+ `docs/assets/` so the publish surface and the demo script share one
57
+ naming convention:
58
+
59
+ - `docs/assets/entwurf-entwurf.cast` — raw asciinema recording (gitignored via global `*.cast`)
60
+ - `docs/assets/entwurf-entwurf.gif` — tracked, referenced by the top-level `README.md` Entwurf section
61
+ - `demo/peer-debug.log` — `ENTWURF_DEBUG=1` stderr from the top pane (gitignored, stays next to the script)
62
+ - `demo/sender-debug.log` — same from the bottom pane
63
+
64
+ Watch debug live (separate terminal, before `bash demo.sh`):
65
+
66
+ ```bash
67
+ tail -F demo/sender-debug.log demo/peer-debug.log
68
+ ```
69
+
70
+ Quick post-run greps:
71
+
72
+ ```bash
73
+ grep 'entwurf:debug' demo/sender-debug.log
74
+ grep -E '(entwurf|model-switch)' demo/sender-debug.log
75
+ ```
76
+
77
+ ## Tunables (env vars)
78
+
79
+ ### `demo.sh` (entwurf two-pane)
80
+
81
+ | Var | Default | Meaning |
82
+ |---|---|---|
83
+ | `PEER_MODEL` | `entwurf/gpt-5.4` (≡ `piat`) | top pane backend (receives greeting) |
84
+ | `SENDER_MODEL` | `entwurf/claude-sonnet-4-6` (≡ `pias`) | bottom pane backend (drives scenes) |
85
+ | `SCENE_DELAY` | `25` | seconds to wait for each scene's agent work |
86
+ | `WARMUP` | `3` | seconds to wait for both pi banners |
87
+ | `FINAL_PAUSE` | `5` | extra wait after scene 3 (peer reply lag) |
88
+ | `GIF_SPEED` | `2.8` | agg playback multiplier (cast time → GIF time) |
89
+ | `SESSION` | `entwurf-demo` | tmux session name |
90
+ | `OUTDIR` | `demo/` | debug log directory (cast + gif live in `docs/assets/`) |
91
+
92
+ ### `demo-baseline.sh` (single-pane baseline + entwurf)
93
+
94
+ | Var | Default | Meaning |
95
+ |---|---|---|
96
+ | `DRIVER_MODEL` | `entwurf/claude-sonnet-4-6` (≡ `pias`) | the driven pane — answers Q-B0 and spawns the sibling |
97
+ | `SIBLING_MODEL` | `entwurf/gpt-5.4` (≡ `piat`) | cross-backend sibling spawned in scene 2 |
98
+ | `SIBLING_CWD` | repo root | cwd passed to the entwurf sibling (drives `<project-context>` injection) |
99
+ | `SCENE1_DELAY` | `60` | seconds for the long English baseline answer |
100
+ | `SCENE2_DELAY` | `30` | seconds for the sync entwurf round-trip |
101
+ | `WARMUP` | `3` | seconds to wait for the pi banner |
102
+ | `FINAL_PAUSE` | `4` | extra wait after scene 2 |
103
+ | `GIF_SPEED` | `2.0` | agg playback multiplier (slower than `demo.sh` — the answer rewards reading) |
104
+ | `GIF_COMPRESS` | `1` | run gifsicle after agg (`0` to skip) |
105
+ | `GIF_LOSSY` | `200` | gifsicle `--lossy` strength (drop to `80` if a future scenario shows artifacts) |
106
+ | `GIF_COLORS` | `64` | gifsicle `--colors` palette size |
107
+ | `SESSION` | `entwurf-baseline-demo` | tmux session name |
108
+ | `OUTDIR` | `demo/` | debug log directory (cast + gif live in `docs/assets/`) |
109
+
110
+ ### Model ↔ alias map
111
+
112
+ The demo launch lines correspond to your shell aliases:
113
+
114
+ | Alias | Model id | Use as |
115
+ |---|---|---|
116
+ | `piao` | `entwurf/claude-opus-4-8` | `SENDER_MODEL` (precision scenes) |
117
+ | `pias` | `entwurf/claude-sonnet-4-6` | `SENDER_MODEL` (default) |
118
+ | `piat` | `entwurf/gpt-5.4` | `PEER_MODEL` (default) |
119
+ | `piat5` | `entwurf/gpt-5.5` | either |
120
+ | `piag` | `entwurf/gemini-3.1-pro-preview` | either |
121
+
122
+ Each pane runs with `ENTWURF_DEBUG=1` baked in — same shape as
123
+ the aliases — so every spawn/model-switch/entwurf trace lands in the
124
+ debug log files (see below).
125
+
126
+ Examples:
127
+
128
+ ```bash
129
+ # Swap peer to gemini
130
+ PEER_MODEL=entwurf/gemini-3.1-pro-preview bash demo.sh
131
+
132
+ # Faster pacing for short retake
133
+ SCENE_DELAY=30 FINAL_PAUSE=10 bash demo.sh
134
+
135
+ # All three backends in one demo would need a 3-pane variant — out of scope here.
136
+ ```
137
+
138
+ ## Prerequisites
139
+
140
+ - `pi` on PATH (current floor 0.80.2)
141
+ - `entwurf` provider configured + auth ready for the selected sender/peer models
142
+ - `asciinema` installed
143
+ - `agg` installed (optional — only for GIF conversion)
144
+ - `gifsicle` installed (optional — `demo-baseline.sh` post-compress step; skip with `GIF_COMPRESS=0`. On NixOS: `nix-shell -p gifsicle` covers both scripts)
145
+ - `tmux` installed
146
+
147
+ If you watch the demo live: open another terminal and run
148
+ `tmux attach -t entwurf-demo -r` (read-only attach) **before** the script
149
+ starts recording. Do not attach writable — keystrokes from your terminal
150
+ would collide with the driver.
151
+
152
+ ## Why pre-baked prompts
153
+
154
+ Each scene's prompt asks the inner agent to discover state itself
155
+ (`entwurf_peers`, prior Session ID from this conversation). So the driver
156
+ script types static strings only — no sessionId injection, no
157
+ output parsing. That's what makes it reproducible.
158
+
159
+ ## Editing scenes
160
+
161
+ Open `demo.sh`, edit the three `tmux send-keys -t "$SENDER_PANE" -l '...'`
162
+ blocks under the `drive()` function. The `-l` flag tells tmux to send the
163
+ argument literally (no key-name interpretation), so quotes inside the prompt
164
+ are safe as long as you don't use single quotes inside the single-quoted
165
+ argument.
166
+
167
+ ## Tracked vs. ignored
168
+
169
+ Tracked (committed):
170
+
171
+ - `demo/demo.sh`
172
+ - `demo/demo-baseline.sh`
173
+ - `demo/README.md`
174
+ - `docs/assets/entwurf-demo.gif` — archived pre-0.12 README demo GIF (no longer `package.json#pi.image`)
175
+ - `docs/assets/entwurf-entwurf.gif` — archived pre-0.12 Entwurf section illustration
176
+
177
+ Ignored (regenerable recording artifacts — both demos):
178
+
179
+ - `*.cast` (global rule — applies under `docs/assets/` too)
180
+ - `demo/*.log`
181
+
182
+ Both demos write their recording artifacts directly into `docs/assets/`
183
+ using the names above. Re-running a demo overwrites the previous take so
184
+ the published preview always matches `main`. The `.cast` source stays
185
+ local (global `*.cast` ignore); only the `.gif` rides the publish surface.
186
+ The `gifsicle` step inside `demo-baseline.sh` makes the take reproducible
187
+ without a manual compression pass — see the Tunables table for tuning the
188
+ lossy/colors flags.
@@ -0,0 +1,156 @@
1
+ #!/usr/bin/env bash
2
+ # demo-baseline.sh — single-pane recorded demo of entwurf.
3
+ #
4
+ # Two scenes, both driven into one pi session:
5
+ # 1. Baseline self-awareness — Q-B0 + Q-B0-CARRIER (English answer enforced).
6
+ # Surfaces system prompt / tool surface / MCP boundary / identity carriers.
7
+ # 2. Entwurf surface — spawn a sibling via the entwurf tool and receive its
8
+ # reply in the same pane (cross-model, mode=sync).
9
+ #
10
+ # Layout (tmux, 220x50):
11
+ # pane 0 (single) — driven pi (claude-sonnet-4-6) — receives both prompts.
12
+ #
13
+ # Recording: asciinema → baseline.cast → agg → baseline.gif
14
+
15
+ set -euo pipefail
16
+
17
+ # ---------- config ----------
18
+ SESSION=${SESSION:-entwurf-baseline-demo}
19
+ SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
20
+ REPO_ROOT=$(cd "$SCRIPT_DIR/.." && pwd)
21
+ # Recording artifacts land in the publish surface (docs/assets/) so the cast
22
+ # + gif are versioned alongside the README/gallery references. The *.cast
23
+ # file remains gitignored (global `*.cast` rule); only the .gif is tracked
24
+ # via the `files` allowlist in package.json. Debug log stays next to the
25
+ # script (gitignored via `demo/*.log`).
26
+ OUTDIR=${OUTDIR:-$SCRIPT_DIR} # debug log dir (local)
27
+ PUBLISH_DIR=${PUBLISH_DIR:-$REPO_ROOT/docs/assets} # cast + gif (publish surface)
28
+ CAST="$PUBLISH_DIR/entwurf-demo.cast"
29
+ GIF="$PUBLISH_DIR/entwurf-demo.gif"
30
+ DRIVER_LOG="$OUTDIR/baseline-debug.log"
31
+
32
+ # Driven pi runs the bridge surface and answers both prompts.
33
+ DRIVER_MODEL=${DRIVER_MODEL:-entwurf/claude-sonnet-4-6} # pias
34
+ # Sibling spawned via entwurf in scene 2 — different backend for contrast.
35
+ SIBLING_MODEL=${SIBLING_MODEL:-entwurf/gpt-5.4} # piat
36
+ SIBLING_CWD=${SIBLING_CWD:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}
37
+
38
+ # Pacing in seconds.
39
+ # Scene 1 produces a long English answer (system-prompt audit); 60 s is the
40
+ # observed upper bound for sonnet-4-6 on this prompt. Scene 2 is shorter
41
+ # (sync entwurf round-trip ≈ 15-25 s).
42
+ WARMUP=${WARMUP:-3}
43
+ SCENE1_DELAY=${SCENE1_DELAY:-60}
44
+ SCENE2_DELAY=${SCENE2_DELAY:-30}
45
+ FINAL_PAUSE=${FINAL_PAUSE:-4}
46
+
47
+ # Slower default than demo.sh (2.8) — Scene 1 produces a long English answer
48
+ # and the audit value comes from being readable, not zippy. Override via env
49
+ # if the recorded GIF still feels too slow for the gallery card.
50
+ GIF_SPEED=${GIF_SPEED:-2.0}
51
+
52
+ # Post-recording gifsicle compression. agg writes ~3 MB for this scenario;
53
+ # gifsicle reliably reproduces ~2 MB with no visible quality loss at lossy=200
54
+ # / colors=64 (text stays legible because monokai uses few distinct hues).
55
+ # Set GIF_COMPRESS=0 to skip; bump GIF_LOSSY down (e.g. 80) for lighter
56
+ # compression if a future scenario shows visible artifacts.
57
+ GIF_COMPRESS=${GIF_COMPRESS:-1}
58
+ GIF_LOSSY=${GIF_LOSSY:-200}
59
+ GIF_COLORS=${GIF_COLORS:-64}
60
+
61
+ EMACS_SOCKET=${PI_EMACS_AGENT_SOCKET:-server}
62
+
63
+ # ---------- prep ----------
64
+ mkdir -p "$OUTDIR" "$PUBLISH_DIR"
65
+
66
+ cleanup() {
67
+ tmux kill-session -t "$SESSION" 2>/dev/null || true
68
+ }
69
+ trap cleanup EXIT
70
+
71
+ tmux kill-session -t "$SESSION" 2>/dev/null || true
72
+ : > "$DRIVER_LOG"
73
+
74
+ # ENTWURF_DEBUG=1 on; stderr appended to debug log so the recorded pane
75
+ # stays clean. --entwurf-control gives the driven session a control socket so
76
+ # entwurf siblings can address it if a follow-up demo needs it.
77
+ COMMON_ENV="ENTWURF_DEBUG=1 PI_EMACS_AGENT_SOCKET=$EMACS_SOCKET"
78
+ COMMON_ARGS="--entwurf-control --emacs-agent-socket $EMACS_SOCKET"
79
+ new_session_id() { bash "$REPO_ROOT/run.sh" new-session-id; }
80
+
81
+ # ---------- start driver (single pane) ----------
82
+ DRIVER_LAUNCH_ID=$(new_session_id)
83
+ tmux new-session -d -s "$SESSION" -n demo -x 220 -y 50 \
84
+ "$COMMON_ENV pi --session-id $DRIVER_LAUNCH_ID --model $DRIVER_MODEL $COMMON_ARGS 2>>$DRIVER_LOG"
85
+ DRIVER_PANE=$(tmux list-panes -s -t "$SESSION" -F '#{pane_id}' | head -1)
86
+
87
+ # Give pi time to print its banner and reach the prompt.
88
+ sleep "$WARMUP"
89
+
90
+ # ---------- driver: types both prompts into the single pane ----------
91
+ drive() {
92
+ # Scene 1 — baseline self-awareness (Q-B0 + Q-B0-CARRIER). Answer in English.
93
+ tmux send-keys -t "$DRIVER_PANE" -l '[Q-B0] What does your system prompt say? Answer without speculation. 1. What harness / tool environment are you in right now? 2. Distinguish native tools from MCP / custom tools. 3. What is the basis for that understanding? 4. Do not pretend to see what you do not see — say "I do not know" when you do not. [Q-B0-CARRIER] For each piece of information cited above, identify the surface: 1. Actual system prompt (or, on Codex, the developer instruction). 2. First-user-message prepend. 3. Tool function schema. 4. Separate system-reminder block. Also: (a) if asked to commit something to memory, how do you handle it; (b) can you check today personal Google Calendar via the provided skills? Please answer in English.'
94
+ tmux send-keys -t "$DRIVER_PANE" Enter
95
+ sleep "$SCENE1_DELAY"
96
+
97
+ # Scene 2 — entwurf surface: spawn a sibling, receive its reply inline.
98
+ tmux send-keys -t "$DRIVER_PANE" -l "Now demonstrate the entwurf surface. Spawn a sibling via the entwurf tool — provider: entwurf, model: gpt-5.4, mode: sync, cwd: $SIBLING_CWD. Task body: \"You are a sibling spawned for a recorded demo. Reply in one English sentence: which backend ACP model are you running on, and what does the entwurf_self envelope say about your identity (sessionId, agentId, cwd)?\". After the entwurf returns, print the Session ID and quote the sibling reply verbatim in one line."
99
+ tmux send-keys -t "$DRIVER_PANE" Enter
100
+ sleep $((SCENE2_DELAY + FINAL_PAUSE))
101
+
102
+ # End: detach asciinema by killing tmux session.
103
+ tmux kill-session -t "$SESSION" 2>/dev/null || true
104
+ }
105
+
106
+ drive &
107
+ DRIVE_PID=$!
108
+
109
+ # ---------- record ----------
110
+ asciinema rec --overwrite --quiet \
111
+ --command "tmux attach -t $SESSION" \
112
+ "$CAST" || true
113
+
114
+ wait "$DRIVE_PID" 2>/dev/null || true
115
+
116
+ # ---------- convert to gif ----------
117
+ if command -v agg >/dev/null 2>&1; then
118
+ echo "Converting cast → gif via agg (speed=${GIF_SPEED})..."
119
+ agg --speed "$GIF_SPEED" --theme monokai "$CAST" "$GIF"
120
+ AGG_SIZE=$(stat -c%s "$GIF" 2>/dev/null || stat -f%z "$GIF" 2>/dev/null || echo "?")
121
+ echo "GIF (uncompressed): $GIF (${AGG_SIZE} bytes)"
122
+ else
123
+ echo "agg not found; keeping cast only: $CAST"
124
+ fi
125
+
126
+ # ---------- compress gif (gifsicle) ----------
127
+ # Same flags that produced the committed docs/assets/entwurf-demo.gif —
128
+ # rerunning this script reproduces a comparable artifact, not a 3 MB raw take.
129
+ if [ "$GIF_COMPRESS" = "1" ] && [ -f "$GIF" ]; then
130
+ if command -v gifsicle >/dev/null 2>&1; then
131
+ echo "Compressing GIF via gifsicle (-O3 --lossy=${GIF_LOSSY} --colors ${GIF_COLORS})..."
132
+ TMP_GIF="${GIF}.uncompressed"
133
+ mv "$GIF" "$TMP_GIF"
134
+ gifsicle -O3 --lossy="$GIF_LOSSY" --colors "$GIF_COLORS" "$TMP_GIF" -o "$GIF"
135
+ rm -f "$TMP_GIF"
136
+ GIFS_SIZE=$(stat -c%s "$GIF" 2>/dev/null || stat -f%z "$GIF" 2>/dev/null || echo "?")
137
+ echo "GIF (compressed): $GIF (${GIFS_SIZE} bytes)"
138
+ else
139
+ echo "gifsicle not found; GIF left uncompressed (set GIF_COMPRESS=0 to silence)."
140
+ echo " Install: 'nix-shell -p gifsicle' or your distro's package."
141
+ fi
142
+ elif [ "$GIF_COMPRESS" = "0" ]; then
143
+ echo "GIF_COMPRESS=0 — skipping gifsicle pass."
144
+ fi
145
+
146
+ # ---------- summary ----------
147
+ echo "Cast: $CAST"
148
+ echo "Driver log: $DRIVER_LOG ($(wc -l < "$DRIVER_LOG" 2>/dev/null || echo 0) lines)"
149
+ echo
150
+ echo "Quick debug peek:"
151
+ echo " grep 'entwurf:debug' $DRIVER_LOG | head -20"
152
+ echo " grep -E '(entwurf|model-switch)' $DRIVER_LOG | head -20"
153
+ echo
154
+ echo "Recording artifacts land directly in the publish surface:"
155
+ echo " $CAST"
156
+ echo " $GIF (tracked — referenced by package.json#pi.image + README)"