@junghanacs/entwurf 0.34.1 → 0.35.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/demo/README.md CHANGED
@@ -1,193 +1,127 @@
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
- > **Model ids here are archived, not recommended (2026-08-20).** The `entwurf/gpt-5.4` rows below record what these GIFs actually ran on. They are stale twice over: `entwurf/gpt-5.4` is no longer a curated `entwurf` ACP id at all — that provider serves only the `claude-*` and `cortex-*` rows in `pi-extensions/lib/acp/models.ts` — and the live-spend default has since moved to `openai-codex/gpt-5.6-luna` under a cheaper plan. Read them as a record of the recording, never as a default to copy; the current defaults live in `VERIFY.md` and `NEXT.md`.
6
-
7
- Two recorded GIF scripts live here. Both use `asciinema` for capture and
8
- `agg` for cast → GIF conversion. Both share the same gitignore policy
9
- (`*.cast` global, `demo/*.gif`, `demo/*.log` — only the `.sh` and this
10
- README are tracked).
11
-
12
- ## `demo-baseline.sh` — single-pane baseline + entwurf surface
13
-
14
- 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:
15
-
16
- 1. **Baseline self-awareness** — the `Q-B0` + `Q-B0-CARRIER` interview,
17
- English answer enforced. The pane exposes how the running model
18
- names its harness, distinguishes native tools from MCP / custom
19
- tools, names the carrier surface for each cited piece of
20
- information, and admits unknowns instead of fabricating.
21
- 2. **Entwurf surface** — the same pane spawns a sibling via the
22
- `entwurf` tool (`mode=sync`, cross-model — driver `claude-sonnet-5`,
23
- sibling `gpt-5.4`), prints the Session ID, and quotes the sibling's
24
- one-line reply verbatim.
25
-
26
- Run from the repo root (or `demo/`):
1
+ # Recorded demos — source checkout recipes
2
+
3
+ ## Whole Herdr: Pi ↔ native Claude Code (#138)
4
+
5
+ This is the scene shown by the [README GIF](../README.md). The recording is the **whole
6
+ Herdr client**, with sidebar and tabs, without captions. It observes a real Pi citizen
7
+ opening native Claude Code, receiving its callback, sending one task, and receiving the
8
+ sibling's native Bash result. Camera focus is a view, never an address or delivery receipt.
9
+
10
+ ### Original publication versus a new take
11
+
12
+ [Original provenance](./herdr-pi-cc-20261011.provenance.json) records the approved GIF hash,
13
+ original cast hash, measured runtime/models, audited source hashes and render arguments.
14
+ The original raw cast, transcripts, native records and full manifests remain private.
15
+ **A fresh checkout cannot replay that original capture.** The command below creates a
16
+ **new LIVE take of the same scene**, not byte-identical GIF reproduction or renewed
17
+ acceptance of the original. Model text, timing, ids and terminal banners vary. The original
18
+ agg/ffprobe versions were not recorded; current tool versions cannot retroactively fill them.
19
+
20
+ ### Prerequisites — no automatic install or repair
21
+
22
+ Run from an installed development **source checkout**, not an npm consumer. The `.ts`
23
+ takes/helpers/tests are maintainer tooling and are deliberately excluded from the tarball.
24
+
25
+ - Node at the `package.json#engines.node` floor; the checkout's pinned dependencies and
26
+ current compiled bridge. After a source/bootstrap change, build with `pnpm run build-bridge`.
27
+ - Pi at the checkout's supported floor (**current floor 1.1.0**); `entwurf` on PATH must be
28
+ the intended source stable-bin. Source evidence is not registry-package acceptance.
29
+ - `herdr` on PATH at exactly `scripts/fixtures/herdr-supply.json#version`; `asciinema` v3.
30
+ - Herdr's existing Pi integration at
31
+ `${PI_CODING_AGENT_DIR:-$HOME/.pi/agent}/extensions/herdr-agent-state.ts`.
32
+ - The operator's existing Claude launcher/auth and vendor data at
33
+ `${XDG_DATA_HOME:-$HOME/.local/share}/claude`. Missing or drifted ownership is a refusal,
34
+ not permission to install, copy credentials or fix global hooks/settings.
35
+ - For offline rendering only: `agg` and `ffprobe` on PATH. Rendering is optional and
36
+ requires the original private `take.cast` **and its same-run manifest**.
37
+
38
+ Selectors for this scene are fixed: Pi `openai-codex/gpt-6.1-sol`, thinking `low`, native
39
+ Claude Code alias `sonnet`. Overrides may only restate these. An OpenRouter base-URL
40
+ redirect is refused before inference; no fallback model or automatic retry is used.
41
+ A requested alias and the native model id observed in its transcript are recorded separately.
42
+
43
+ ### Offline verification first
27
44
 
28
45
  ```bash
29
- bash demo/demo-baseline.sh
46
+ ./run.sh check-demo-reproducibility
47
+ pnpm exec tsc --noEmit -p scripts/tsconfig.json
30
48
  ```
31
49
 
32
- Output lands directly in the publish surface — re-running the script
33
- overwrites the previous take so the gallery preview always matches the
34
- current branch:
35
-
36
- - `docs/assets/entwurf-demo.cast` — raw asciinema recording (gitignored)
37
- - `docs/assets/entwurf-demo.gif` — tracked, referenced by the top-level `README.md` as archived pre-0.12 evidence
38
- - `demo/baseline-debug.log` — `ENTWURF_DEBUG=1` stderr (gitignored, stays next to the script)
50
+ The named source-only floor verb discovers `demo/**/*.test.ts` and runs Node's own test
51
+ runner, including fake-tool/child-process fixtures. It is reached by `pnpm check` and
52
+ `pnpm run check:full`; these are not Vitest tests and are not reached by the adapter beside
53
+ Vitest globs. HOME, writable XDG roots and identity carriers are sandboxed by the verb.
54
+ `agg` and `ffmpeg` are faked, but the renderer/caption fixtures decode real GIF bytes, so
55
+ this floor needs a real `ffprobe` on PATH; without it they fail (`ffprobe ENOENT`) rather
56
+ than skip. CI installs Ubuntu's `ffmpeg` package (which carries `ffprobe`) in the check job.
57
+ The receiving type fence is `scripts/tsconfig.json`, paired with the root `demo` exclusion.
39
58
 
40
- ## `demo.sh` — two-pane entwurf flow (regression guard)
59
+ ### Explicit new LIVE take
41
60
 
42
- Three-scene entwurf flow used for regression evidence, not for the
43
- public README. Two pi sessions run side-by-side in one tmux window;
44
- a background driver types the scene prompts into the sender pane.
45
-
46
- 1. **Spawn** a sonnet sibling (memory write — "tempered indigo").
47
- 2. **Resume** that same sibling (memory recall — identity preservation,
48
- the #9 regression guard).
49
- 3. **Cross-session greeting** via `entwurf_send`. *(Historical: `entwurf_send` was retired in the
50
- 0.12 cutover, and the "sent box" half of that pairing was never actually reachable — the
51
- sender-side renderer had zero producers and was removed in #120 P3. What the recording shows on
52
- the receive side is real; the send side is a box the operator could not have seen.)*
53
-
54
- Run:
61
+ Only run after deciding to spend real subscription turns:
55
62
 
56
63
  ```bash
57
- bash demo/demo.sh
64
+ LIVE=1 node demo/take-herdr-pi-cc.ts
58
65
  ```
59
66
 
60
- Output mirrors the baseline demo's layout — recording artifacts land in
61
- `docs/assets/` so the publish surface and the demo script share one
62
- naming convention:
67
+ With approved selectors/policy and without `LIVE=1`, it exits 97 **before tool preflight
68
+ or resource creation**. Invalid selectors or an OpenRouter redirect can refuse earlier
69
+ with exit 2, still without tools or writes. Once authorized, it creates a private Herdr
70
+ server/fence, starts the scene, and stops only resources whose
71
+ ownership it can prove. Failure ends observation; it cannot undo a citizen's completed work.
72
+ No automatic retry, rerender or promotion follows a failure. Failed evidence is retained.
63
73
 
64
- - `docs/assets/entwurf-entwurf.cast` — raw asciinema recording (gitignored via global `*.cast`)
65
- - `docs/assets/entwurf-entwurf.gif` — tracked, referenced by the top-level `README.md` Entwurf section
66
- - `demo/peer-debug.log` — `ENTWURF_DEBUG=1` stderr from the top pane (gitignored, stays next to the script)
67
- - `demo/sender-debug.log` — same from the bottom pane
74
+ The fence isolates Herdr XDG state and Entwurf record/mailbox roots, **not the whole host**:
75
+ HOME/auth/vendor stores remain real; the existing Claude data directory is linked into the
76
+ private fence. Pi's control socket and the addressed v2 lock use real `~/.pi/` directories.
77
+ The native transcripts, records and fence are kept, not erased by teardown. Do not run this
78
+ against an operator server or broaden cleanup to unrelated sessions.
68
79
 
69
- Watch debug live (separate terminal, before `bash demo.sh`):
80
+ The default output is `.tmp-verify/138-demo-gifs/takes/<take-id>/` (ignored); an explicit
81
+ `ENTWURF_DEMO_OUT` changes its parent. `manifest.json` contains assertions, camera views,
82
+ raw receipts, measured models and the cast digest. The stage describes source **tooling**;
83
+ `entwurfOnPath` is observed, not checked against this checkout's stable-bin. Confirm that
84
+ binding separately before calling a take source-runtime evidence. Git HEAD and dirty state
85
+ are observed; a dirty HEAD is **not** an immutable source fingerprint.
86
+ Keep the exact source and manifest with any subsequent take you intend to audit.
70
87
 
71
- ```bash
72
- tail -F demo/sender-debug.log demo/peer-debug.log
73
- ```
74
-
75
- Quick post-run greps:
76
-
77
- ```bash
78
- grep 'entwurf:debug' demo/sender-debug.log
79
- grep -E '(entwurf|model-switch)' demo/sender-debug.log
80
- ```
88
+ ### Offline rendering and publication boundary
81
89
 
82
- ## Tunables (env vars)
83
-
84
- ### `demo.sh` (entwurf two-pane)
85
-
86
- | Var | Default | Meaning |
87
- |---|---|---|
88
- | `PEER_MODEL` | `entwurf/gpt-5.4` (≡ `piat`) | top pane backend (receives greeting) |
89
- | `SENDER_MODEL` | `entwurf/claude-sonnet-5` (≡ `pias`) | bottom pane backend (drives scenes) |
90
- | `SCENE_DELAY` | `25` | seconds to wait for each scene's agent work |
91
- | `WARMUP` | `3` | seconds to wait for both pi banners |
92
- | `FINAL_PAUSE` | `5` | extra wait after scene 3 (peer reply lag) |
93
- | `GIF_SPEED` | `2.8` | agg playback multiplier (cast time → GIF time) |
94
- | `SESSION` | `entwurf-demo` | tmux session name |
95
- | `OUTDIR` | `demo/` | debug log directory (cast + gif live in `docs/assets/`) |
96
-
97
- ### `demo-baseline.sh` (single-pane baseline + entwurf)
98
-
99
- | Var | Default | Meaning |
100
- |---|---|---|
101
- | `DRIVER_MODEL` | `entwurf/claude-sonnet-5` (≡ `pias`) | the driven pane — answers Q-B0 and spawns the sibling |
102
- | `SIBLING_MODEL` | `entwurf/gpt-5.4` (≡ `piat`) | cross-backend sibling spawned in scene 2 |
103
- | `SIBLING_CWD` | repo root | cwd passed to the entwurf sibling (drives `<project-context>` injection) |
104
- | `SCENE1_DELAY` | `60` | seconds for the long English baseline answer |
105
- | `SCENE2_DELAY` | `30` | seconds for the sync entwurf round-trip |
106
- | `WARMUP` | `3` | seconds to wait for the pi banner |
107
- | `FINAL_PAUSE` | `4` | extra wait after scene 2 |
108
- | `GIF_SPEED` | `2.0` | agg playback multiplier (slower than `demo.sh` — the answer rewards reading) |
109
- | `GIF_COMPRESS` | `1` | run gifsicle after agg (`0` to skip) |
110
- | `GIF_LOSSY` | `200` | gifsicle `--lossy` strength (drop to `80` if a future scenario shows artifacts) |
111
- | `GIF_COLORS` | `64` | gifsicle `--colors` palette size |
112
- | `SESSION` | `entwurf-baseline-demo` | tmux session name |
113
- | `OUTDIR` | `demo/` | debug log directory (cast + gif live in `docs/assets/`) |
114
-
115
- ### Model ↔ alias map
116
-
117
- The demo launch lines correspond to your shell aliases:
118
-
119
- | Alias | Model id | Use as |
120
- |---|---|---|
121
- | `piao` | `entwurf/claude-opus-5` | `SENDER_MODEL` (precision scenes) |
122
- | `pias` | `entwurf/claude-sonnet-5` | `SENDER_MODEL` (default) |
123
- | `piat` | `entwurf/gpt-5.4` | `PEER_MODEL` (default) |
124
- | `piat5` | `entwurf/gpt-5.5` | either |
125
- | `piag` | `entwurf/gemini-3.1-pro-preview` | either |
126
-
127
- Each pane runs with `ENTWURF_DEBUG=1` baked in — same shape as
128
- the aliases — so every spawn/model-switch/entwurf trace lands in the
129
- debug log files (see below).
130
-
131
- Examples:
90
+ Given an existing same-run private take, no model turn is needed:
132
91
 
133
92
  ```bash
134
- # Swap peer to gemini
135
- PEER_MODEL=entwurf/gemini-3.1-pro-preview bash demo.sh
136
-
137
- # Faster pacing for short retake
138
- SCENE_DELAY=30 FINAL_PAUSE=10 bash demo.sh
139
-
140
- # All three backends in one demo would need a 3-pane variant — out of scope here.
93
+ node demo/render-take.ts <private-take-dir> <private-output.gif>
141
94
  ```
142
95
 
143
- ## Prerequisites
144
-
145
- - `pi` on PATH (current floor 1.1.0)
146
- - `entwurf` provider configured + auth ready for the selected sender/peer models
147
- - `asciinema` installed
148
- - `agg` installed (optional — only for GIF conversion)
149
- - `gifsicle` installed (optional — `demo-baseline.sh` post-compress step; skip with `GIF_COMPRESS=0`. On NixOS: `nix-shell -p gifsicle` covers both scripts)
150
- - `tmux` installed
151
-
152
- If you watch the demo live: open another terminal and run
153
- `tmux attach -t entwurf-demo -r` (read-only attach) **before** the script
154
- starts recording. Do not attach writable — keystrokes from your terminal
155
- would collide with the driver.
156
-
157
- ## Why pre-baked prompts
158
-
159
- Each scene's prompt asks the inner agent to discover state itself
160
- (`entwurf_peers`, prior Session ID from this conversation). So the driver
161
- script types static strings only — no sessionId injection, no
162
- output parsing. That's what makes it reproducible.
163
-
164
- ## Editing scenes
165
-
166
- Open `demo.sh`, edit the three `tmux send-keys -t "$SENDER_PANE" -l '...'`
167
- blocks under the `drive()` function. The `-l` flag tells tmux to send the
168
- argument literally (no key-name interpretation), so quotes inside the prompt
169
- are safe as long as you don't use single quotes inside the single-quoted
170
- argument.
171
-
172
- ## Tracked vs. ignored
173
-
174
- Tracked (committed):
175
-
176
- - `demo/demo.sh`
177
- - `demo/demo-baseline.sh`
178
- - `demo/README.md`
179
- - `docs/assets/entwurf-demo.gif` — archived pre-0.12 README demo GIF (no longer `package.json#pi.image`)
180
- - `docs/assets/entwurf-entwurf.gif` — archived pre-0.12 Entwurf section illustration
181
-
182
- Ignored (regenerable recording artifacts — both demos):
183
-
184
- - `*.cast` (global rule — applies under `docs/assets/` too)
185
- - `demo/*.log`
186
-
187
- Both demos write their recording artifacts directly into `docs/assets/`
188
- using the names above. Re-running a demo overwrites the previous take so
189
- the published preview always matches `main`. The `.cast` source stays
190
- local (global `*.cast` ignore); only the `.gif` rides the publish surface.
191
- The `gifsicle` step inside `demo-baseline.sh` makes the take reproducible
192
- without a manual compression pass — see the Tunables table for tuning the
193
- lossy/colors flags.
96
+ The renderer verifies cast digest, trims only recorder-only teardown, and promotes the
97
+ private GIF only after agg exits successfully and ffprobe decodes it. Each render keeps a
98
+ uniquely named `.render.cast` beside its output (also on failure), with name/digest in a
99
+ successful sidecar; the original take's `take.render.cast` is never overwritten. Non-live-pass takes
100
+ can render only to a private FAILED name. A mixed subject/teardown event is retained whole.
101
+ The renderer refuses repository `docs/assets/` destinations, including parent-directory
102
+ aliases: **publication is a separate reviewed copy**, never a rendering side effect.
103
+
104
+ Its email/company-path scan is deliberately narrow, **not a privacy/public-readability
105
+ certificate**: personal paths, hostnames, footers and native progress language can remain.
106
+ The operator approved the original rough pixels with their disclosed limitations. A new
107
+ render requires its own review; it must not overwrite the approved original implicitly.
108
+ The GIF and sidecar rename separately; a crash between them leaves a mismatched pair, not
109
+ atomic two-file publication. Optional `caption-take.ts` produces a separate derivative;
110
+ it is not this uncaptioned publication recipe.
111
+
112
+ ## Other source scenes
113
+
114
+ `take-pi-durable.ts`, `take-pi-acp.ts` and `take-pi-cc.ts` record **different**, private tmux
115
+ scenes. Each requires its own explicit `LIVE=1` and, like the Herdr scene, refuses a
116
+ non-approved selector or an OpenRouter base-URL redirect first (exit 2, no tools or writes).
117
+ They are not the Herdr GIF's recipe.
118
+ Their helper tests remain in the same offline discovery verb. Original FAILED takes and
119
+ offline rejudgements are distinct evidence, never replacement LIVE acceptance.
120
+
121
+ ## Archived pre-0.12 scripts
122
+
123
+ `demo-baseline.sh` and `demo.sh` retain their historical bodies for comparison, but now
124
+ **refuse immediately with exit 2**, even with `LIVE=1`. They cannot launch retired v1 flows
125
+ or overwrite today's published assets. Historical commands, selectors and old output paths
126
+ inside those bodies are tombstones, not runnable advice. Scene 3's current deterministic
127
+ delivery proof is `./run.sh check-bridge-delivery`; do not revive the v1 route.
@@ -25,7 +25,11 @@
25
25
 
26
26
  set -euo pipefail
27
27
 
28
- # ---------- config ----------
28
+ # Hard retirement: the preserved v1 body must never launch or overwrite today's approved GIF.
29
+ printf '%s\n' 'entwurf demo: archived v1 baseline refused; see demo/README.md for the source-only Herdr recipe.' >&2
30
+ exit 2
31
+
32
+ # ---------- historical body (unreachable) ----------
29
33
  SESSION=${SESSION:-entwurf-baseline-demo}
30
34
  SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
31
35
  REPO_ROOT=$(cd "$SCRIPT_DIR/.." && pwd)
package/demo/demo.sh CHANGED
@@ -5,7 +5,7 @@
5
5
  # retired v1 verbs (`entwurf` / `entwurf_resume` / `entwurf_send`), which the 0.12
6
6
  # cutover removed; a run would fail at scene 1. Kept for historical comparison only.
7
7
  # See demo/README.md. Scene 3's delivery proof was recovered as a deterministic gate
8
- # (scripts/check-bridge-delivery.ts); the v2-native retake is a post-0.12 follow-up.
8
+ # (scripts/check-bridge-delivery.ts); current v2-native source recipes live in demo/README.md.
9
9
  #
10
10
  #
11
11
  # MODEL NOTE (2026-08-20). The model ids below are the ARCHIVED values these
@@ -28,7 +28,11 @@
28
28
 
29
29
  set -euo pipefail
30
30
 
31
- # ---------- config ----------
31
+ # Hard retirement: preserve historical source, not a second runnable v1 route.
32
+ printf '%s\n' 'entwurf demo: archived v1 peer scene refused; see demo/README.md for current source recipes.' >&2
33
+ exit 2
34
+
35
+ # ---------- historical body (unreachable) ----------
32
36
  SESSION=${SESSION:-entwurf-demo}
33
37
  SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
34
38
  REPO_ROOT=$(cd "$SCRIPT_DIR/.." && pwd)
@@ -0,0 +1,64 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "scene": "whole-Herdr Pi to native Claude Code",
4
+ "take": "take-herdr-pi-cc-2026-10-11T11-57-22-297Z",
5
+ "startedAt": "2026-10-11T11:57:22.299Z",
6
+ "stage": "SOURCE checkout, not an npm consumer",
7
+ "gif": {
8
+ "path": "docs/assets/entwurf-demo.gif",
9
+ "sha256": "b686739b5c68fa458dd47c844711aea0f6ab5819eb3eb5d2850449922fa084c0",
10
+ "bytes": 1835518,
11
+ "seconds": 43.5,
12
+ "width": 1126,
13
+ "height": 764,
14
+ "frames": 340,
15
+ "captions": false
16
+ },
17
+ "cast": {
18
+ "sha256": "bbf8a409c58f8b48e88346d232a66ee41ac0c2acf8e605a3f568fd7d40857cf2",
19
+ "public": false
20
+ },
21
+ "runtime": {
22
+ "entwurf": "0.34.2",
23
+ "pi": "1.1.0",
24
+ "herdr": "0.9.3",
25
+ "gitHead": "9f4c9983fed1dd0ba619bc5a50f0f5abccefeef4",
26
+ "gitDirty": true,
27
+ "measuredPiModel": "openai-codex/gpt-6.1-sol",
28
+ "requestedClaudeAlias": "sonnet",
29
+ "measuredClaudeNativeModel": "claude-sonnet-5-5"
30
+ },
31
+ "auditedOriginalSourceSha256": {
32
+ "demo/take-herdr-pi-cc.ts": "f9cd30f1bb5b06236559a9f1283f0e030b300f6a17858d778c40d246327a6349",
33
+ "demo/lib/take-recorder.ts": "d47d824b90a7265c5ab6e7c4bf313b4f81b27cef836b4ddd15f971f3de386256",
34
+ "demo/render-take.ts": "f6e68157d7f8fb1f0e00a359efaa04aa30eeca1112ce2571661f39aea826b55d",
35
+ "demo/lib/herdr-take.ts": "57089d883323b2582aadd7f54f63cfce541f3a2c584bb6940313fbcdc940d83e"
36
+ },
37
+ "render": {
38
+ "aggArgs": [
39
+ "--idle-time-limit",
40
+ "2",
41
+ "--speed",
42
+ "1",
43
+ "--last-frame-duration",
44
+ "8",
45
+ "--font-size",
46
+ "14",
47
+ "--theme",
48
+ "monokai"
49
+ ],
50
+ "trimmedTailEvents": 2,
51
+ "trimmedWhat": "recorder teardown: herdr client exit (alt-screen exit + server shut down line)",
52
+ "renderedAt": "2026-10-11T11:58:32.828Z",
53
+ "aggVersion": null,
54
+ "ffprobeVersion": null,
55
+ "toolVersionEvidence": "not recorded for this original; current versions cannot fill historical fields"
56
+ },
57
+ "limits": [
58
+ "Original cast, manifests and native records remain private; this tree cannot replay that capture.",
59
+ "The tracked command is a new LIVE take recipe, not byte-identical reproduction.",
60
+ "Current maintainer source is amended; original source hashes are historical observations, not a complete import-closure seal.",
61
+ "Operator approved the rough original pixels; this is not privacy or public-readability certification.",
62
+ "No claim of 0.35.0 release, published-package acceptance or future Herdr plugin observation follows from this take."
63
+ ]
64
+ }
Binary file
@@ -195,8 +195,8 @@ root-bound — an install from another root rewrites it rather than refusing; no
195
195
  `PI_CODING_AGENT_DIR`, Claude config) changed between runs or a host that changes between the plugin's
196
196
  early owner check and its installed recheck, which can still refuse after the download; and the plugin's
197
197
  deactivate, finding no activation ledger, reports what is left at its addresses and neither reclaims nor
198
- reuses it. The no-ledger diagnostic is not in the published 0.34.0 runtime; a plugin host receives it only
199
- from a later published runtime the plugin pins.
198
+ reuses it. The 0.34.0 runtime predates that no-ledger diagnostic; a plugin host runs the runtime its
199
+ `runtime-lock.json` pins.
200
200
 
201
201
  ## 2. Optional pi adapter / ACP plugin
202
202
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@junghanacs/entwurf",
3
- "version": "0.34.1",
3
+ "version": "0.35.0",
4
4
  "description": "Garden-citizen dispatch substrate and meta-bridge for pi, Claude Code, Copilot CLI, OMP, Antigravity, and Codex CLI; Claude and Snowflake Cortex Code ACP backends.",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -40,6 +40,8 @@
40
40
  "scripts/",
41
41
  "prompts/",
42
42
  "demo/README.md",
43
+ "demo/herdr-pi-cc-20261011.provenance.json",
44
+ "!demo/**/*.ts",
43
45
  "demo/demo.sh",
44
46
  "demo/demo-baseline.sh",
45
47
  "docs/setup-clean-host.md",
@@ -186,7 +188,7 @@
186
188
  "check:core": "pnpm run check:toolchain && pnpm run check:vitest && pnpm run check:contracts",
187
189
  "check:toolchain": "pnpm lint && pnpm typecheck",
188
190
  "check:vitest": "./run.sh check-mux-fresh-call && ./run.sh check-acp-sdk-surface && ./run.sh check-pi-queue-oracle && ./run.sh check-tests-beside-behavior",
189
- "check:contracts": "./run.sh check-shell-quote && ./run.sh check-entwurf-session-identity && ./run.sh check-meta-session && ./run.sh check-meta-v3-record && ./run.sh check-mailbox-receipt-state && ./run.sh check-entwurf-capabilities && ./run.sh check-harness-admission-parity && ./run.sh check-capability-bundle-reach && ./run.sh check-meta-mailbox-state-write && ./run.sh check-meta-receiver-marker && ./run.sh check-meta-capability-source && ./run.sh check-model-lock && ./run.sh check-dep-versions && ./run.sh check-node-floor-coherence && ./run.sh check-claude-floor-coherence && ./run.sh check-copilot-statusline && ./run.sh check-pi-import-surface && ./run.sh check-env-namespace && ./run.sh check-pi-runtime-version && ./run.sh check-pi-preflight && ./run.sh check-project-trust-handler && ./run.sh check-entwurf-v2-contract && ./run.sh check-entwurf-v2-send && ./run.sh check-entwurf-v2-runner && ./run.sh check-control-send-receipt && ./run.sh check-compaction-send-guard && ./run.sh check-entwurf-resume-args && ./run.sh check-entwurf-v2-native-push && ./run.sh check-entwurf-v2-production && ./run.sh check-entwurf-v2-surface && ./run.sh check-entwurf-bridge-pi-free && ./run.sh check-resume-launch-identity && ./run.sh check-mux-placement && ./run.sh check-mux-launch && ./run.sh check-mux-resume-call && ./run.sh check-mux-parent-artifact && ./run.sh check-mux-launcher-fence && ./run.sh check-entwurf-v2-visible-resume && ./run.sh check-control-socket-path && ./run.sh check-meta-listing && ./run.sh check-entwurf-fact-provider && ./run.sh check-herdr-placement && ./run.sh check-herdr-fresh-call && ./run.sh check-herdr-supply && ./run.sh check-fresh-call-dispatch && ./run.sh check-entwurf-peers-surface && ./run.sh check-entwurf-self-address && ./run.sh check-entwurf-deliverability && ./run.sh check-native-push-adapter && ./run.sh check-native-push-register && ./run.sh check-auth-boundary && ./run.sh check-acp-overlay && ./run.sh check-acp-tool-surface && ./run.sh check-acp-event-mapper && ./run.sh check-acp-prompt-builder && ./run.sh check-acp-config && ./run.sh check-acp-session-store && ./run.sh check-acp-carrier-augment && ./run.sh check-acp-foreground-only && ./run.sh check-acp-vendor-rejection && ./run.sh check-codex-sender-identity && ./run.sh check-codex-native-push && ./run.sh check-pi-durable-beside",
191
+ "check:contracts": "./run.sh check-demo-reproducibility && ./run.sh check-shell-quote && ./run.sh check-entwurf-session-identity && ./run.sh check-meta-session && ./run.sh check-meta-v3-record && ./run.sh check-mailbox-receipt-state && ./run.sh check-entwurf-capabilities && ./run.sh check-harness-admission-parity && ./run.sh check-capability-bundle-reach && ./run.sh check-meta-mailbox-state-write && ./run.sh check-meta-receiver-marker && ./run.sh check-meta-capability-source && ./run.sh check-model-lock && ./run.sh check-dep-versions && ./run.sh check-node-floor-coherence && ./run.sh check-claude-floor-coherence && ./run.sh check-copilot-statusline && ./run.sh check-pi-import-surface && ./run.sh check-env-namespace && ./run.sh check-pi-runtime-version && ./run.sh check-pi-preflight && ./run.sh check-project-trust-handler && ./run.sh check-entwurf-v2-contract && ./run.sh check-entwurf-v2-send && ./run.sh check-entwurf-v2-runner && ./run.sh check-control-send-receipt && ./run.sh check-compaction-send-guard && ./run.sh check-entwurf-resume-args && ./run.sh check-entwurf-v2-native-push && ./run.sh check-entwurf-v2-production && ./run.sh check-entwurf-v2-surface && ./run.sh check-entwurf-bridge-pi-free && ./run.sh check-resume-launch-identity && ./run.sh check-mux-placement && ./run.sh check-mux-launch && ./run.sh check-mux-resume-call && ./run.sh check-mux-parent-artifact && ./run.sh check-mux-launcher-fence && ./run.sh check-entwurf-v2-visible-resume && ./run.sh check-control-socket-path && ./run.sh check-meta-listing && ./run.sh check-entwurf-fact-provider && ./run.sh check-herdr-placement && ./run.sh check-herdr-fresh-call && ./run.sh check-herdr-supply && ./run.sh check-fresh-call-dispatch && ./run.sh check-entwurf-peers-surface && ./run.sh check-entwurf-self-address && ./run.sh check-entwurf-deliverability && ./run.sh check-native-push-adapter && ./run.sh check-native-push-register && ./run.sh check-auth-boundary && ./run.sh check-acp-overlay && ./run.sh check-acp-tool-surface && ./run.sh check-acp-event-mapper && ./run.sh check-acp-prompt-builder && ./run.sh check-acp-config && ./run.sh check-acp-session-store && ./run.sh check-acp-carrier-augment && ./run.sh check-acp-foreground-only && ./run.sh check-acp-vendor-rejection && ./run.sh check-codex-sender-identity && ./run.sh check-codex-native-push && ./run.sh check-pi-durable-beside",
190
192
  "check:hermetic": "./run.sh check-bridge-delivery && ./run.sh smoke-pi-attach && ./run.sh check-fresh-cut-gate && ./run.sh check-hook-launch-topology && ./run.sh check-meta-hook-session-switch && ./run.sh check-copilot-birth-hook && ./run.sh check-copilot-receive-arm && ./run.sh check-copilot-launch && ./run.sh check-pi-launch && ./run.sh check-codex-app-server-launch && ./run.sh check-meta-identity-consumers && ./run.sh check-socket-probe && ./run.sh smoke-meta-honesty && ./run.sh check-meta-doctor-oracle && ./run.sh check-agy-permission-matrix && ./run.sh smoke-agy-statusline-state && ./run.sh smoke-copilot-statusline-state && ./run.sh smoke-copilot-mcp-state && ./run.sh smoke-omp-bridge-state && ./run.sh smoke-omp-mcp-state && ./run.sh smoke-omp-receive-state && ./run.sh check-omp-birth-hook && ./run.sh check-omp-receive-arm && ./run.sh check-omp-fresh-preflight && ./run.sh smoke-agy-hooks-state && ./run.sh smoke-pi-provider-state && ./run.sh smoke-user-scope-citizen && ./run.sh smoke-meta-prune && ./run.sh smoke-meta-keyset-guard && ./run.sh check-meta-manifest-schema && ./run.sh check-entwurf-control-rpc && ./run.sh check-entwurf-bridge-boot && ./run.sh check-probe-bridge-command && ./run.sh check-meta-facts && ./run.sh check-peer-facts && ./run.sh check-herdr-plugin && ./run.sh check-herdr-plugin-profile && ./run.sh check-herdr-plugin-build && ./run.sh check-herdr-runtime-bootstrap && ./run.sh check-herdr-activation && ./run.sh check-agy-sender-identity && ./run.sh check-acp-provider-surface && ./run.sh check-acp-stop-reason && ./run.sh check-acp-prompt-lifecycle && ./run.sh check-acp-usage-accounting && ./run.sh check-acp-launch-namespace && ./run.sh check-acp-stream-hooks && ./run.sh check-acp-backend-preflight && ./run.sh check-acp-session-reuse && ./run.sh check-typing-call-fence && ./run.sh check-gate-manifests && ./run.sh check-release-gate-outcomes && ./run.sh check-probe-ordering && ./run.sh check-probe-cli-shim && ./run.sh check-acp-cortex && ./run.sh check-codex-bridge-identity && ./run.sh check-pi-bridge-sender && ./run.sh check-pi-mcp-register && ./run.sh check-pi-mcp-bridge && ./run.sh check-codex-birth-hook && ./run.sh smoke-codex-config-state && ./run.sh smoke-codex-birth && ./run.sh check-herdr-sandbox",
191
193
  "check:package": "./run.sh check-install-surface && ./run.sh smoke-meta-install-state && ./run.sh smoke-agy-install-state && ./run.sh smoke-setup-verdict && ./run.sh check-pack-pin-matcher && ./run.sh check-pack-consumer-roots && ./run.sh check-package-source-routing && ./run.sh check-install-preflight && ./run.sh check-pack"
192
194
  },
package/run.sh CHANGED
@@ -134,6 +134,7 @@ Usage:
134
134
  ./run.sh check-omp-birth-hook # #87 gate: drives the real OMP assembler into a temp dir, imports the ASSEMBLED index.ts into a MOCK omp host and fires session_start/session_switch. tui mints one backend:"omp" record + a sender marker keyed to the host's OWN pid (the one-process join) + the garden id on the status line; print/rpc/json mint NOTHING (hasUI is true on the rpc rows, as in the vendor); switch attaches on the same native id and mints the replacement on a new one; the CERTIFIED receiver reader still finds no marker. Also the four-root joint binding: extension and omp-labeled bridge child resolve the same sessions/mailbox/senders/receivers bundle against test-built literals (never the production resolver as its own oracle), under a poisoned PI_CODING_AGENT_DIR and under four distinct ENTWURF_META_* overrides; the override grammar is absolute-or-~ and both halves plus the doctor refuse anything else by name; and a drifted provenance label selects no root policy at all. Hermetic; no omp, no model turn
135
135
  ./run.sh check-pi-durable-contact # #129 L-identity gate, CHECKOUT-ONLY (an installed package refuses it in run_ts). Opens the REAL durable app from THIS checkout's verified carrier (pi/pi-durable/carrier/ under its resolver, against the checkout's pinned pi SDK set — refused by name first) with the pi-durable contact, births the citizen and runs its tools adapter-direct through a privately emitted branch bridge (ENTWURF_PI_DURABLE_BRIDGE_ENTRY; its registry must match this checkout). Matrix roots {default, explicit} x {fresh, --continue}: one record keyed by the durable session dir, sender marker on the spawned host pid, exactly one bridge child whose /proc PPID is that host, poisoned identity carriers kept from it, its four root carriers bound to the roots written, the receiver ARMED (raw marker owned by the host pid + start key as pi-durable-host, joined to the sender marker; removed on close), self = the record and replyable, peers from an outside bridge, same garden id on reopen; the bundle's compiled owner join must admit pi-durable-host. Reaps only the host/bridges it owns on any failure. Harness-bypassed: no model turn, no ToolTask. Missing inputs → SKIP (97), never a pass
136
136
  ./run.sh check-pi-durable-send # #129 L-send FIRST CELL, CHECKOUT-ONLY. Same input as check-pi-durable-contact. One native durable turn: the app's own controller input → GenerationTask → ToolTask → the contact's entwurf_v2 → the private bridge → a seeded self-fetch target (C2 recipe, owned by a separate idle child). S = a 127.0.0.1 scripted OpenAI-compatible endpoint configured through the app's documented models.json (dummy key, PI_OFFLINE); H = the native ToolTask path; V (a vendor turn) is NOT run. Oracles: the wire (tools offered, v2 schema = the bridge's tools/list, exactly two requests, the tool result + receipt in the second), the raw mailbox (.msg 1, signal, sender = the host record), the native view (one call, its settled result, root current). Not exactly-once, not recovery, not a recipient turn. Missing inputs → SKIP (97)
137
+ ./run.sh check-demo-reproducibility # source-checkout-only: discover offline demo Node tests; no LIVE, model, render or publication
137
138
  ./run.sh check-pi-durable-beside # #129 mutant execution coordinate (VERIFY rule 3): run_vitest over the six pi-durable beside tests (adapter, carrier/SDK-set verifier, preflight, owner join, carrier relocation/resolver/emitter/task footer, native-module file and directory ingress) — the pi-durable qualification lane's gate argv. Same files check-tests-beside-behavior already runs.
138
139
  ./run.sh check-pi-durable-receive # #129 L-receive FIRST CELL, CHECKOUT-ONLY. Same input (the bundle must carry the pi-durable-host owner join). The host driver gives the app NO input; a seeded claude-code sender (marker owned by a separate idle child, handed to a gate-owned sending bridge through ENTWURF_META_SENDER_MARKER) enqueues one message, the host's doorbell admits its notice to the ROOT conversation, and the scripted model drains it with entwurf_inbox_read. S withholds response #1 until the ENQUEUE receipt, the .delivered bytes, the signal, the host-owned receiver marker and the notice are captured. Oracles: receiver() admission, native view, raw archive (.delivered.read + lastReadAt), wire (exactly two requests, drain result = view), and after a green reap a forensic read-only SQLite copy (submission row by request id = reported id, root, input, done, placed entry = the notice). V NOT run. Missing inputs → SKIP (97)
139
140
  ./run.sh check-copilot-receive-arm # #82 RAIL 5 gate: the REAL receiver installer + the REAL extension.mjs forked with a stubbed SDK. Arms only after birth, marker owned by the WATCHER pid, self-fetch dispatch answer, doorbell carries the garden id and NOT the body, id-drift/foreign-parent refusals. Hermetic; no Copilot, no model turn
@@ -1029,6 +1030,37 @@ check_capability_bundle_reach() {
1029
1030
  run_ts scripts/check-capability-bundle-reach.ts
1030
1031
  }
1031
1032
 
1033
+ check_demo_reproducibility() {
1034
+ # Source-only Node strip-types/CLI and subprocess fixtures, not adapter Vitest tests.
1035
+ # Discovery lives beside demo behavior; no filename list or npm TS crossing.
1036
+ section "demo reproducibility (offline source fixtures, no model or recording)"
1037
+ case "$REPO_DIR" in */node_modules/*) fail "check-demo-reproducibility is source-checkout-only"; return 1 ;; esac
1038
+ local -a tests=()
1039
+ local f
1040
+ while IFS= read -r f; do [ -n "$f" ] && tests+=("$f"); done < <(cd "$REPO_DIR" && find demo -type f -name '*.test.ts' -print | LC_ALL=C sort)
1041
+ if [ "${#tests[@]}" -eq 0 ]; then
1042
+ fail "[QK:DEMO-TEST-DISCOVERY] demo test discovery is empty — no fixtures is not a passing floor"
1043
+ return 1
1044
+ fi
1045
+ (
1046
+ local sandbox name
1047
+ sandbox=$(mktemp -d /tmp/entwurf-demo-unit.XXXXXX)
1048
+ trap 'rm -rf -- "$sandbox"' EXIT
1049
+ while IFS= read -r name; do
1050
+ case "$name" in ENTWURF_*|HERDR_*|PI_SESSION_ID|PI_AGENT_ID|CLAUDECODE|CLAUDE_CODE_SESSION_ID|TMUX|TMUX_PANE|LIVE) unset "$name" ;; esac
1051
+ done < <(compgen -e)
1052
+ export HOME="$sandbox/home" PI_CODING_AGENT_DIR="$sandbox/pi"
1053
+ export XDG_CONFIG_HOME="$sandbox/config" XDG_DATA_HOME="$sandbox/data" XDG_STATE_HOME="$sandbox/state" XDG_CACHE_HOME="$sandbox/cache" XDG_RUNTIME_DIR="$sandbox/runtime"
1054
+ mkdir -m 700 "$HOME" "$PI_CODING_AGENT_DIR" "$XDG_CONFIG_HOME" "$XDG_DATA_HOME" "$XDG_STATE_HOME" "$XDG_CACHE_HOME" "$XDG_RUNTIME_DIR" "$sandbox/bin"
1055
+ # Take.finish records PATH versions; fixtures must not depend on an installed Pi or run it.
1056
+ printf '#!/bin/sh\n[ "$1" = --version ] && { printf "offline-fixture\\n"; exit 0; }\nexit 81\n' > "$sandbox/bin/pi"
1057
+ chmod +x "$sandbox/bin/pi"
1058
+ export PATH="$sandbox/bin:$PATH"
1059
+ cd "$REPO_DIR"
1060
+ node --test "${tests[@]}"
1061
+ )
1062
+ }
1063
+
1032
1064
  check_bridge_delivery() {
1033
1065
  # demo/demo.sh scene 3, recovered as a deterministic gate. demo drove two real pi
1034
1066
  # panes so it proved DELIVERY through the installed binary (at model cost); what
@@ -3756,6 +3788,8 @@ check_pack() {
3756
3788
  # pi-extensions/ so it also covers any future allowlist entry; `plugins/` needs no
3757
3789
  # companion negation because `^plugins/` above already refuses that whole tree.
3758
3790
  '\.test\.[cm]?[jt]s$'
3791
+ # Maintainer take/renderer/helper TS is source-only, including non-test implementations.
3792
+ '^demo/.*\.ts$'
3759
3793
  )
3760
3794
 
3761
3795
  local pass=1 f pat hit
@@ -7634,6 +7668,8 @@ case "$cmd" in
7634
7668
  # global plugin install, USER MCP reach, meta-record store integrity, hook log
7635
7669
  # no-ERROR, and actual SessionStart creation evidence. A plugin present with
7636
7670
  # zero claude-code meta-records is a SILENT MISS -> non-zero exit.
7671
+ # #136: the only argument is `--plugin-runtime <root>`; shift the verb so the doctor sees it.
7672
+ shift || true
7637
7673
  (cd "$REPO_DIR" && bash scripts/meta-bridge-doctor.sh "$@")
7638
7674
  ;;
7639
7675
  install-copilot-bridge)
@@ -8173,6 +8209,9 @@ case "$cmd" in
8173
8209
  check-pi-queue-oracle)
8174
8210
  check_pi_queue_oracle
8175
8211
  ;;
8212
+ check-demo-reproducibility)
8213
+ check_demo_reproducibility
8214
+ ;;
8176
8215
  check-tests-beside-behavior)
8177
8216
  check_tests_beside_behavior
8178
8217
  ;;
@@ -3455,6 +3455,7 @@ let manifestCount: number;
3455
3455
  "codex-native": 82,
3456
3456
  "compaction-send-guard": 7,
3457
3457
  "control-socket-disconnect": 4,
3458
+ "demo-reproducibility": 12,
3458
3459
  "copilot-birth": 19,
3459
3460
  "copilot-launch": 14,
3460
3461
  "pi-launch": 9,
@@ -3471,7 +3472,7 @@ let manifestCount: number;
3471
3472
  "herdr-placement": 13,
3472
3473
  "herdr-plugin": 25,
3473
3474
  "herdr-plugin-profile": 14,
3474
- "herdr-activation": 28,
3475
+ "herdr-activation": 34,
3475
3476
  "herdr-plugin-build": 20,
3476
3477
  "herdr-runtime-bootstrap": 31,
3477
3478
  "herdr-fresh-call": 38,
@@ -3493,7 +3494,7 @@ let manifestCount: number;
3493
3494
  "omp-fresh": 24,
3494
3495
  "omp-receive": 11,
3495
3496
  "probe-ordering": 1,
3496
- "release-gate": 22,
3497
+ "release-gate": 24,
3497
3498
  "retired-sent-surface": 2,
3498
3499
  "resume-args": 6,
3499
3500
  "resume-launch-identity": 6,