@junghanacs/entwurf 0.23.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +3 -1
- package/BASELINE.md +2 -0
- package/CHANGELOG.md +307 -0
- package/FAQ.md +47 -0
- package/README.md +5 -3
- package/VERIFY.md +43 -2
- package/demo/README.md +1 -1
- package/docs/acp-backend-rail.md +97 -18
- package/docs/setup-clean-host.md +8 -7
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +33 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/backend-adapter.js +2 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/context.js +19 -5
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/tool-surface.js +2 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/callback-env.js +68 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-contract.js +2 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-lock.js +1 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/fresh-call-composition.js +54 -22
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/herdr-fresh-call.js +7 -3
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/herdr-placement.js +13 -4
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +19 -10
- package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-omp.js +35 -42
- package/mcp/entwurf-bridge/dist/scripts/probe-bridge-command.js +1 -0
- package/mcp/entwurf-bridge/src/index.ts +41 -1
- package/mcp/entwurf-bridge/test.sh +2 -2
- package/package.json +15 -13
- package/pi-extensions/entwurf-control.ts +56 -0
- package/pi-extensions/lib/acp/acp-client.ts +3 -3
- package/pi-extensions/lib/acp/backend-adapter.ts +3 -3
- package/pi-extensions/lib/acp/backend.ts +20 -10
- package/pi-extensions/lib/acp/context.ts +30 -10
- package/pi-extensions/lib/acp/event-mapper.ts +5 -5
- package/pi-extensions/lib/acp/session-store.ts +2 -2
- package/pi-extensions/lib/acp/tool-surface.ts +2 -1
- package/pi-extensions/lib/callback-env.ts +78 -0
- package/pi-extensions/lib/compaction-send-guard.ts +4 -1
- package/pi-extensions/lib/entwurf-v2-contract.ts +2 -2
- package/pi-extensions/lib/entwurf-v2-lock.ts +1 -1
- package/pi-extensions/lib/entwurf-v2-runner.ts +1 -1
- package/pi-extensions/lib/fresh-call-composition.ts +56 -22
- package/pi-extensions/lib/herdr-fresh-call.ts +12 -4
- package/pi-extensions/lib/herdr-placement.ts +13 -4
- package/pi-extensions/lib/mux-fresh-call.ts +23 -8
- package/pi-extensions/meta-bridge-omp.ts +35 -48
- package/run.sh +327 -246
- package/scripts/check-acp-backend-preflight.ts +117 -9
- package/scripts/check-acp-carrier-augment.ts +6 -4
- package/scripts/check-acp-event-mapper.ts +17 -6
- package/scripts/check-acp-prompt-builder.ts +17 -9
- package/scripts/check-acp-prompt-lifecycle.ts +23 -6
- package/scripts/check-acp-session-reuse.ts +64 -51
- package/scripts/check-acp-session-store.ts +31 -8
- package/scripts/check-acp-stop-reason.ts +20 -2
- package/scripts/check-acp-stream-hooks.ts +22 -5
- package/scripts/check-acp-usage-accounting.ts +42 -18
- package/scripts/check-entwurf-bridge-boot.ts +16 -1
- package/scripts/check-fresh-call-dispatch.ts +4 -5
- package/scripts/check-gate-qualification.ts +45 -5
- package/scripts/check-herdr-activation.ts +73 -0
- package/scripts/check-herdr-fresh-call.ts +22 -6
- package/scripts/check-herdr-sandbox.ts +14 -2
- package/scripts/check-install-container.sh +1 -1
- package/scripts/check-pi-launch.ts +361 -0
- package/scripts/check-release-gate-outcomes.ts +7 -0
- package/scripts/inventory-verification-surface.ts +86 -10
- package/scripts/lib/mutation-qualify.ts +1 -1
- package/scripts/mutants/acp-backend-preflight.json +20 -0
- package/scripts/mutants/bridge-boot-resume.json +26 -0
- package/scripts/mutants/herdr-activation.json +16 -0
- package/scripts/mutants/herdr-fresh-call.json +11 -0
- package/scripts/mutants/herdr-plugin-build.json +1 -1
- package/scripts/mutants/mux-fresh-call.json +21 -8
- package/scripts/mutants/omp-fresh.json +5 -9
- package/scripts/mutants/pack-install.json +3 -3
- package/scripts/mutants/pi-launch.json +113 -0
- package/scripts/mutants/resume-args.json +6 -6
- package/scripts/mutants/v2-surface.json +2 -2
- package/scripts/mutants/v2-visible-resume.json +17 -17
- package/scripts/probe-bridge-command.ts +1 -0
- package/scripts/smoke-acp-raw-turn-live.ts +1 -1
- package/scripts/smoke-acp-session-reuse-live.ts +23 -5
- package/scripts/smoke-agy-install-state.sh +2 -2
- package/scripts/smoke-copilot-mcp-state.sh +1 -1
- package/scripts/smoke-entwurf-v2-matrix-live.ts +1 -1
- package/scripts/smoke-herdr-fresh-call-live.ts +56 -28
- package/scripts/smoke-herdr-raw-install-live.sh +877 -0
- package/scripts/smoke-omp-fresh-live.ts +4 -2
- package/scripts/smoke-omp-mcp-state.sh +1 -1
- package/scripts/smoke-pi-provider-state.sh +2 -2
- package/scripts/tsconfig.json +5 -0
- package/scripts/check-entwurf-facts.ts +0 -391
- package/scripts/check-entwurf-resume-args.ts +0 -122
- package/scripts/check-entwurf-v2-decider.ts +0 -657
- package/scripts/check-entwurf-v2-lock.ts +0 -371
- package/scripts/check-entwurf-v2-mailbox.ts +0 -261
- package/scripts/check-entwurf-v2-matrix.ts +0 -401
- package/scripts/check-entwurf-v2-native-push.ts +0 -221
- package/scripts/check-entwurf-v2-release.ts +0 -151
- package/scripts/check-entwurf-v2-runner.ts +0 -448
- package/scripts/check-entwurf-v2-send-fallback.ts +0 -350
- package/scripts/check-entwurf-v2-send.ts +0 -458
- package/scripts/check-entwurf-v2-visible-resume.ts +0 -445
- package/scripts/check-socket-discovery.ts +0 -401
package/AGENTS.md
CHANGED
|
@@ -82,6 +82,8 @@ Keep at most ten open issues, including at most five implementation issues. Clas
|
|
|
82
82
|
|
|
83
83
|
Two independent axes are required: deterministic/package gates and opt-in LIVE evidence. Exact commands, evidence levels, and release acceptance are owned by [VERIFY.md](./VERIFY.md); recorded host evidence by [BASELINE.md](./BASELINE.md). Run LIVE gates from scratch with ambient identity carriers stripped so artifacts and callbacks cannot bleed from the operator session.
|
|
84
84
|
|
|
85
|
+
A new contract's test goes beside the behavior it certifies — `pi-extensions/**/*.test.ts` or `plugins/herdr/**/*.test.mjs` — and `check-tests-beside-behavior` discovers it by glob, so landing one edits no filename list. `scripts/check-*` stays the home of LIVE, install, spawning and mixed gates; a new hand-built gate there needs a reason the vitest lane could not carry it.
|
|
86
|
+
|
|
85
87
|
### Scheduling
|
|
86
88
|
|
|
87
89
|
```text
|
|
@@ -97,7 +99,7 @@ implement → affected focused gates → independent review → one amendment bu
|
|
|
97
99
|
|
|
98
100
|
## Type and Working Boundaries
|
|
99
101
|
|
|
100
|
-
- Each `.ts` file belongs to one declared typecheck fence;
|
|
102
|
+
- Each `.ts` file belongs to one declared typecheck fence; `exclude` may move a file between fences but never out of all of them, so an exclude states the receiving `include` in the same change. Root pi extensions use TypeBox through `@earendil-works/pi-ai`; MCP/scripts use explicit `.ts` imports required by Node strip-types. Installed surfaces route to compiled JS. Behavior-adjacent `.test.ts` takes the fence `test/` takes — `scripts/tsconfig.json`, never the emit-capable root program — and is kept out of the tarball by a files-array negation whose tripwire is `check-pack`.
|
|
101
103
|
- Make surgical, one-contract changes. Removal repairs source and its proof together; a green gate never proves retired behavior.
|
|
102
104
|
- Before commit, sweep repository-wide for retired authority vocabulary and landed-plan future tense. Historical tombstones may remain; live docs, source, gates, and usage must agree.
|
|
103
105
|
- Prefer capability-domain language over identity rank. Use tabs unless the project formatter requires otherwise. GLG decides commit, push, and release gates.
|
package/BASELINE.md
CHANGED
|
@@ -34,6 +34,8 @@ not **UNSUPPORTED** — the deliberate never, which today is native Windows only
|
|
|
34
34
|
| Maintainer NixOS installed package | **certified** for `0.12.8-repair.1` | 2026-07-25 registry install → doctor exit 0 (HISTORY) |
|
|
35
35
|
| Secondary Ubuntu installed package | **certified** for `0.12.8-repair.1` | 2026-07-25 same artifact, isolated agent dir → doctor exit 0 (HISTORY) |
|
|
36
36
|
| macOS Claude meta-bridge | NOT CERTIFIED — pending physical host | No physical-Mac doctor yet; a CI runner has no Claude login. Physical-host rental remains unscheduled; no cut has carried it. |
|
|
37
|
+
| 0.23.1 release acceptance (observer-amended) | accepted by GLG | `pnpm run check:full` P4 exit 0 (577 s); second aggregate `release-gate --cut` at `/tmp/entwurf-release-gate-0.23.1-retry.UIFctR` reached MUST 23/1/0 and BEHAVIOR 1/0, with only `smoke-codex-fresh-live` red because luna dropped `$`/`@` sigils in its relay (`release-gate.log:6156-6157`). The observer-amended standalone cell reran that exact leg with Codex `gpt-5.6-terra`, Pi `openai-codex/gpt-5.6-luna`: 66 assertions, exit 0; A app-server PID `3969303` `$227` ≠ fixture S `$231`; artifact `.probe-artifacts/codex-fresh-live-eZmf6p/`; pipe-pane stdout sha256 `56ee73e046e16fa95ef093fccf037670bf28b53a054bb5eec2a3b3b3ff08d93a`. Luna sigil handling is a post-cut issue, not a production-byte change. |
|
|
38
|
+
| 0.24.0 release acceptance (observer-amended) | accepted by GLG | `pnpm run build-bridge` then `pnpm run check:full` P4 exit 0 (594 s) on the prepared tree; aggregate `release-gate --cut` at `/tmp/entwurf-release-gate-0.24.0.anVBvk` (1h34m) reached MUST 22/2/0 and BEHAVIOR 1/0 with `check-gate-qualification` 739/739 killed at the candidate (`release-gate.log:5507`). The two MUST reds: `smoke-codex-fresh-live` (the Pi→Codex `<placeholder>` template relay audited for byte equality, ROADMAP 0.24.0 follow-up #3, same class as the 0.23.1 amendment) and `smoke-entwurf-chain-live` — delivery A→B (`entwurf_v2 control-socket → sent`) and B→C (B's sender envelope in C's stream) both landed, then hop 3 (ACP `claude-sonnet-5`) declined the relay on principle, naming the scratch `AGENTS.md` authority as self-vouching content (`release-gate.log:5841`); the same class hop 1 refused on the 0.22.0 gate (`f5d6e10`), not rerun (a second run is a coin flip on a model's judgement), ROADMAP follow-up #5. Standalone LIVE before the aggregate, each once with ambient identity stripped: mux-fresh-call 39/39, mux-lifecycle 81/81, omp-receive 11/11, herdr-fresh-call 31/32 after `149bef7` (the red is a claude child that called back and never attempted delivery, follow-up #1), omp-fresh 21/21 after `install-omp-bridge` (stale v1 birth unit on the host, doctor named it). P9 after the aggregate: 548 → 546 dirs, 2 roots / 756K reclaimed, operator store and control-socket dir unchanged. GLG: "릴리즈 하는데 문제 없는것 같은데? 기록하고 진행하자." |
|
|
37
39
|
| Native Codex Linux caller-DIRECTORY axis (current; #95 lane C) | **certified** | 2026-09-16, Codex 0.153.4: 65-assertion LIVE, exit 0, at `b3e07dc` with a clean worktree. Same host and topology as the lane B row below (app-server `737636` in A `$30/@41/%45`; initial Pi `20260916T154902-556eb5` `$2/@58`, Codex `20260916T155037-10ddaa` `$2/@59`, Codex-opened Pi `20260916T155102-9add8a` `$2/@61`, `seat-source=codex-title-anchor`). **hop 1 (cwd requested):** pane `#{pane_current_path}` = rollout `session_meta.cwd` = record cwd = requested scratch = `/tmp/entwurf-codex-fresh-live-db65N2`, against app-server cwd `/home/junghan/repos/gh/entwurf` read from `/proc/737636/cwd`. **hop 2 (cwd NOT requested):** the outbound Pi's pane, its own birth-written record and the Codex caller's record all read that same scratch, and its receipt names the caller-record rule. Artifact `.probe-artifacts/codex-fresh-live-w4yJBw/`; stdout `.probe-artifacts/lane-c-live-20260916T154900.log` sha256 `712050e7a8cf03ece98e7f34029ae98a92e9b2a7fd96aff87d0c1e6ba2ee9af2`. Cleanup reclaimed `@58`/`@59`/`@61` with no failure; records and transcripts preserved; app-server and `$30` untouched. 65 and 56 are different contracts — lane C added the cwd axis on top of lane B. Long floors (qualification body, frozen `check:full`) NOT run at this SHA; GLG decides host vs CI. |
|
|
38
40
|
| Native Codex Linux caller-seat topology (current; #95 lane B) | **certified** | 2026-09-16, Codex 0.153.4: 56-assertion LIVE, exit 0. App-server `737636` alone in session A `$30/@41/%45`; initial Pi `20260916T144205-e876ff` `$2/@46`, omitted-placement Codex `20260916T144230-d050d7` `$2/@47`, Codex-opened Pi `$2/@48`. A ≠ S is the decisive half: the app-server env names A, so S can only have come from the caller's pane title — receipt `seat-source=codex-title-anchor`. Artifact `.probe-artifacts/codex-fresh-live-nYcGC1/run-stdout.log` sha256 `ed60c2bd…`; the 43-assertion hop-1 run that measured the D1 retirement is kept at `codex-fresh-live-4aFCDD/run-stdout.log` sha256 `8392a603…`. Cleanup reclaimed `@46`/`@47`/`@48` with no failure; app-server and `$30` untouched. |
|
|
39
41
|
| Native Codex Linux home topology (PAST CONTRACT — #95 D1 retired the fixed home 2026-09-16) | **certified** for 0.21.0 as shipped | 2026-09-12, Codex 0.153.4: 57-assertion explicit-home LIVE. Initial Pi `$150/@397`; operator app-server `$158/@390/%390`; omitted-placement Codex `$158/@398`; Codex-opened Pi `$158/@399`; exact callbacks and addressed delivery both ways. Receipt and digest: `DELIVERY.md`. Deterministic closure of that axis: qualification 460/460 across 43 lanes with origin/snapshot purity green, then frozen full floor exit 0 in 506s — both pre-amendment. **Observer-amended acceptance, 2026-09-12:** standalone LIVE 48 assertions exit 0, source audit `initial-pi=3/3`/`codex=3/3` completed exact, initial Pi `$150/@431`, Codex `$158/@432`, Codex-opened Pi `$158/@433`, all three reclaimed by their own source receipts; artifact `.probe-artifacts/codex-fresh-live-fZccoK/`. The 48 and 57 counts are different contracts, not a regression. **Release acceptance at that same fingerprint:** `check:full` exit 0 502s, LIVE release gate `--cut` MUST 24/0/0 · BEHAVIOR 1/0/0 · `cut: OK`, `check-gate-qualification` 475/475 across 43 lanes with origin purity green, and the aggregate's Codex leg repeating the 48-assertion acceptance (artifact `codex-fresh-live-2oId4C`). Unrestricted attached-TUI placement remains unsupported and unclaimed. |
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,313 @@ All notable changes to this project will be documented here. Format follows [Kee
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 0.24.0 - 2026-09-20
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **`entwurf_callback` — the eighth garden verb, and it takes no arguments.** The fresh-call
|
|
12
|
+
callback was the one place in the garden where the transcript was the address axis: the caller
|
|
13
|
+
garden id and the launch nonce reached a new sibling only as prose in its first prompt, and the
|
|
14
|
+
sibling had to retype both into `entwurf_v2`. Three releases of symptoms were one cause — 0.23.1
|
|
15
|
+
dropped `$`/`@` sigils, this lane's first release gate filled in `<placeholders>` early, its second lost the
|
|
16
|
+
trailing character of a garden id (`…-26428c` → `…-26428`, refused correctly by the decider) —
|
|
17
|
+
and the protocol had no error-detecting property: a mangled id is indistinguishable from a
|
|
18
|
+
different one, so the decider could only refuse, never repair. GLG's ruling: "luna만 못하는게
|
|
19
|
+
아니라 구조적 문제". Now both launch rails inject `ENTWURF_CALLBACK_TARGET` and
|
|
20
|
+
`ENTWURF_CALLBACK_NONCE` beside the identity scrub (tmux `-e` in `mux-fresh-call`,
|
|
21
|
+
`tab create --env` in `herdr-fresh-call`), computed by the rail and never caller-chosen;
|
|
22
|
+
`callback-env.ts` is the single leaf that formats and parses the pair, so a drifted regex cannot
|
|
23
|
+
inject what the verb rejects; and `entwurf_callback {}` reads the pair from its own process,
|
|
24
|
+
validates garden-id and nonce grammar (exactly what `mintNonce` emits), and dispatches through
|
|
25
|
+
the existing v2 runner so the target is still re-resolved by record and decider (Hard Rule 2).
|
|
26
|
+
Absent or malformed env is a named refusal with no fallback to a model-supplied target (Hard
|
|
27
|
+
Rule 5). Framing for pi, claude-code, copilot and omp now says only "call the callback with no
|
|
28
|
+
arguments"; a separate `FRESH_CALL_DELIVERY_TOOL` names the v2 dialect for the task result, and
|
|
29
|
+
Claude's `--allowedTools` and ToolSearch hint carry callback, delivery and peers together (the
|
|
30
|
+
review found the first cut loaded only two, which would have left a claude-code sibling unable
|
|
31
|
+
to send its result). OMP's bootstrap payload moves to v2 with the closed shape `{v, task}`; a
|
|
32
|
+
leftover `target`/`nonce` key is refused. Measured before the seam was written: the
|
|
33
|
+
entwurf-bridge MCP child inherits the pane env for claude-code, copilot and omp (canary in
|
|
34
|
+
`/proc/<child>/environ`, private tmux, fixture roots); pi is in-process. **Codex is the named
|
|
35
|
+
exception:** the bridge serving a Codex tool call is the operator app-server's child (ppid
|
|
36
|
+
measured) and its identity is request-scoped `_meta.threadId`, so a window env never reaches
|
|
37
|
+
it; `entwurf_callback` refuses there as `codex-callback-env-unsupported`, the Codex framing
|
|
38
|
+
keeps the argument form and says why, and a per-thread carrier is a follow-up after a
|
|
39
|
+
measurement that does not exist yet. The separate Pi→Codex template relay in
|
|
40
|
+
`codex-fresh-live-protocol.ts` (a semantic `<placeholder>` template pushed through a model and
|
|
41
|
+
audited for byte equality) is recorded, not fixed. Surface gates moved with the verb
|
|
42
|
+
(`check-entwurf-bridge-boot` exact eight-verb set + `QK:BRIDGEBOOT-CALLBACK-ZERO-ARG`,
|
|
43
|
+
`check-probe-bridge-command`, `check-bridge`, install/state smokes, README/VERIFY); manifests
|
|
44
|
+
735 → 739 mutants, 59 lanes. Commits `cb86d44`, `ff09522`, `ca04c1b`; designs by
|
|
45
|
+
`claude-opus-5` and `openai-codex/gpt-5.6-terra`, Codex verdict and implementation by
|
|
46
|
+
`xai/grok-4.6`, closing review by a fresh `claude-opus-5`.
|
|
47
|
+
- **`plugins/herdr` 0.4.0.** The plugin's install-time build now reaches a pi range check
|
|
48
|
+
(`install-user-scope`, above), its README installs pi at the published peer range, and its
|
|
49
|
+
runtime lock rides the 0.24.0 candidate window. A minor rather than a patch because a host that
|
|
50
|
+
activated 0.3.1 against an out-of-range pi was silently accepted and is now refused by name.
|
|
51
|
+
|
|
52
|
+
### Changed
|
|
53
|
+
|
|
54
|
+
- **pi floor moves to 0.86.0 (`>=0.86.0 <0.87`), and the ACP provider reads the 0.86 shape
|
|
55
|
+
honestly.** pi 0.86 hands a custom provider a branded `TranscriptContext`: `systemPrompt`
|
|
56
|
+
and `tools` are folded into a leading `role:"system"` message (`normalizeContext`,
|
|
57
|
+
pi-mono `model-runtime.ts:639-642`, `v0.86.0` = `ecac0a9c`). The ACP preflight read
|
|
58
|
+
`context.tools`, which is always `undefined` there, and fell back to the full builtin set —
|
|
59
|
+
so `assertExcludeToolsHonored` could never fire, while `tsc` (the old parameter type is
|
|
60
|
+
assignable) and `check-acp-backend-preflight` (a 0.85-shaped literal fed straight to
|
|
61
|
+
`streamAcpTurn`) both stayed green. Measured on real `pi-ai@0.86.0`: raw Context with `bash`
|
|
62
|
+
excluded → THROW; the same input through `normalizeContext` → PASS with no throw. The adapter
|
|
63
|
+
now replays the transcript (`getCurrentTools(context.messages)`, no fallback — an empty
|
|
64
|
+
replayed list is "the operator excluded everything" and is rejected as such),
|
|
65
|
+
`renderMessage` drops system messages by an explicit `case` so the never-forward-systemPrompt
|
|
66
|
+
invariant is a decision rather than a `default`, and every provider-path gate builds its
|
|
67
|
+
fixture through `normalizeContext()`. New lane `acp-backend-preflight`
|
|
68
|
+
(`[QK:ACP-PREFLIGHT-REPLAYS-TRANSCRIPT-TOOLS]`, exact-once mutant = the old fallback).
|
|
69
|
+
Session-reuse signatures now hash the folded leading system message, so a record persisted
|
|
70
|
+
under 0.85.1 is not a prefix of a 0.86 turn and falls back to `new` (the designed safe path).
|
|
71
|
+
- **Pin move, with every mirror.** devDeps 0.86.0; peers `>=0.86.0 <0.87`; the eight explicit
|
|
72
|
+
`check-pack-install` pins and its leak regex (`@0\.86\.0(_|$)`, boundary lookalike now the
|
|
73
|
+
prefix-extended `0.86.0-beta.1`); lockfile (typebox resolves to a single 1.3.27); baseline
|
|
74
|
+
docs; ROADMAP bump ledger. `check-dep-versions` now excludes dated ledger entries from its
|
|
75
|
+
baseline scan (`stripBumpLedger`, guarded both ways), because a ledger line is a receipt of
|
|
76
|
+
the range that landed THAT day and the first bump after an entry is written is what exposed
|
|
77
|
+
it. Vendor floors were re-measured rather than carried: a real 0.86.0 session file
|
|
78
|
+
(`herdr-placement.ts`), the installed `types.d.ts` (`compaction-send-guard.ts`), the
|
|
79
|
+
unregistered `--entwurf-control` refusal (`check-pi-launch.ts:18`); the registered-host
|
|
80
|
+
duplicate-flag receipt at `check-pi-launch.ts:29` stays dated 0.85.1 with the reason.
|
|
81
|
+
Constellation shrank for the first time (`pi-client`/`pi-protocol` left pi-coding-agent's
|
|
82
|
+
direct deps at 0.86.0); all eight pins are kept on purpose — a retired pin is how a caret
|
|
83
|
+
re-enters unwatched.
|
|
84
|
+
- **The two doors onto one pi registration tell the same truth (#119).** `entwurf setup`
|
|
85
|
+
refused an out-of-range pi by name, but `run.sh install-user-scope` — the verb herdr plugin
|
|
86
|
+
activation actually reaches (`build.mjs` → `herdr-plugin-activate.mjs`) — had no range check
|
|
87
|
+
and wrote the citizen in silence. It now enforces the same closed range through the same
|
|
88
|
+
helpers (`pi_supported_range`/`pi_version_in_range`, one parser), refuses with
|
|
89
|
+
`[install-user-scope] pi: FAIL … outside the supported range` and zero bytes written; absent
|
|
90
|
+
pi is deliberately unchanged (this verb invents no presence verdict). `check-herdr-activation`
|
|
91
|
+
gains `[QK:HAC-PI-FLOOR-BOTH-DOORS]` (24 → 25, the out-of-range/in-range PAIR, fixtures
|
|
92
|
+
derived from the package.json pin) and pins `PI_BIN` in its sandbox `world()` — without that
|
|
93
|
+
the gate's verdict depended on the operator's installed pi. `plugins/herdr/README.md` now
|
|
94
|
+
installs pi at the published peer range and joined `check-dep-versions` BASELINE_DOCS, so
|
|
95
|
+
the number cannot drift. Since 0.86.0 published (2026-09-19T23:15Z) the old unpinned line
|
|
96
|
+
installed a pi the shipped range rejects; the plugin had no known installs, so the repair is
|
|
97
|
+
a correction, not a migration.
|
|
98
|
+
- **Provider-path fixtures are built through `normalizeContext`, and a cast past the brand is
|
|
99
|
+
now a red gate.** The full floor on the bump commit went red in four ACP gates
|
|
100
|
+
(`check-acp-stop-reason`, `-prompt-lifecycle`, `-stream-hooks`, `-usage-accounting`) as
|
|
101
|
+
`'error' !== 'done'` — the new preflight correctly rejecting seven fixture helpers that cast a
|
|
102
|
+
tool-less literal `as Context`, which is exactly the operation that silences the
|
|
103
|
+
`TranscriptContext` brand at typecheck time. Every helper now declares the four pi builtins
|
|
104
|
+
through `normalizeContext`; no production source or assertion text changed.
|
|
105
|
+
`check-acp-backend-preflight` gains a static sweep (`[QK:ACP-FIXTURE-NO-CONTEXT-CAST]`) over
|
|
106
|
+
`scripts/check-acp-*.ts` and the lib tests so that class cannot come back silently; no mutant
|
|
107
|
+
by decision (the claim is the absence of a token and has no production subject — its
|
|
108
|
+
kill-proof is an injected offender named by `file:line`). Full floor on `5d3140c`:
|
|
109
|
+
`check:full` exit 0 in 598s. Manifests: 733 → 735 mutants, 58 → 59 lanes.
|
|
110
|
+
- **The first release gate of this lane (then numbered 0.23.2) was red on three MUST steps, and
|
|
111
|
+
two of them were this lane's own holes** (`/tmp/entwurf-release-gate-0.23.2.3s5A0o`, `MUST: PASS=21 FAIL=3 SKIP=0`).
|
|
112
|
+
`check-gate-qualification` 733/735: both `pack-install` mutants still quoted the old run.sh
|
|
113
|
+
`@0\.85\.1(_|$)` line after the pin mirror moved it (MUTANT-STALE), and nothing in
|
|
114
|
+
`check:full` looks at whether a `find` still matches — `check-gate-manifests` proved the
|
|
115
|
+
subject was tracked but never opened it. The finds now mirror run.sh byte-exact and the head
|
|
116
|
+
gains `[QK:MUTANT-FIND-MATCHES-SUBJECT]` (every `find` matches its subject exactly once, using
|
|
117
|
+
the body's own `countOccurrences`), which would have caught this at commit time instead of
|
|
118
|
+
fifty minutes into the gate. `smoke-acp-session-reuse-live`: `turn1`/`turn2` were annotated
|
|
119
|
+
`: Context` with no tools and the preflight rejected them — the third fixture of this class,
|
|
120
|
+
and the sweep written to stop it had globbed only `check-acp-*` and only the cast spelling.
|
|
121
|
+
Both turns build through `normalizeContext`; the sweep now covers `scripts/smoke-*.ts` and the
|
|
122
|
+
annotation form (indexed-access types such as `Context["messages"]` excluded), and retyped
|
|
123
|
+
`startTurn` in `check-acp-prompt-lifecycle` on the way (`5aeb8d4`). The third red,
|
|
124
|
+
`smoke-codex-fresh-live`, was a host prerequisite: the gate shell did not export
|
|
125
|
+
`ENTWURF_CODEX_APP_SERVER_PID` / `ENTWURF_CODEX_FRESH_MODEL` / `ENTWURF_CODEX_FRESH_PI_MODEL`
|
|
126
|
+
(the app-server was alive, PID 3969303); the rerun carries them. An independent audit
|
|
127
|
+
(`openai-codex/gpt-5.6-terra`, read-only) re-measured every mutant `find` against its subject
|
|
128
|
+
(0 stale), every direct provider-path fixture (0 tool-less), and named four stale declarations
|
|
129
|
+
fixed before the rerun: `docs/acp-backend-rail.md` support table, `pnpm-workspace.yaml`
|
|
130
|
+
release-age exclusions for pi-client/pi-protocol, `check-pack-install` diagnostic wording, and
|
|
131
|
+
the filename-floor sentence in `docs/mux-launch-rail.md`.
|
|
132
|
+
The qualification body on `ca04c1b` then reported 737/739 with two WRONG-REASON verdicts —
|
|
133
|
+
mutants dying at a neighbouring claim: `check-herdr-fresh-call` asserted the umbrella
|
|
134
|
+
whole-argv cell before the callback-pair cell, and the `pack-install` COVERS-CLOSURE `replace`
|
|
135
|
+
still carried the 0.85.1 pin. Both fixed in `3a9ae38`; a lane-scoped replay through the real
|
|
136
|
+
`qualifyMutants` runner (herdr-fresh-call + pack-install, 40/40 killed, 504s) is the fix
|
|
137
|
+
receipt, and the exact-SHA full body is read from `release-gate --cut`. Bridge source changes
|
|
138
|
+
need `pnpm run build-bridge` before any gate: `check-bridge-delivery` compares the gitignored
|
|
139
|
+
`dist` against source and reports staleness as red, which it did once in this lane.
|
|
140
|
+
The standalone LIVE pass that followed (each smoke once, ambient identity stripped) found the
|
|
141
|
+
one surface the verb move had not reached: `smoke-herdr-fresh-call-live` still joined both of
|
|
142
|
+
its cells on `entwurf_v2` as the callback — the pi cell on an argument predicate the
|
|
143
|
+
zero-argument verb can never satisfy, the claude cell on `indexOf("entwurf_v2")`, which landed
|
|
144
|
+
on the result delivery and reported the real callback as forbidden work before the callback.
|
|
145
|
+
The same red run is the receipt that production was right: the claude child's MCP activity
|
|
146
|
+
log reads `entwurf_peers → entwurf_callback (completed) → entwurf_v2 (completed)`, the pi
|
|
147
|
+
child's own tool result says the callback was delivered, and every delivery/fence cell passed
|
|
148
|
+
(2 FAILED / 30 ok, preserved fixture `/tmp/entwurf-opus2/herdr-live-fixture-red`). The oracle
|
|
149
|
+
now joins on the bare `entwurf_callback` the observing surfaces spell and on the ABSENCE of an
|
|
150
|
+
address in the pi call (`149bef7`); no deterministic gate covers this on-demand axis, which is
|
|
151
|
+
why it surfaced only here.
|
|
152
|
+
The same pass found the host behind the tree: `smoke-omp-fresh-live` timed out waiting for the
|
|
153
|
+
callback because the INSTALLED omp birth unit still spoke bootstrap v1 (no
|
|
154
|
+
`entwurf_bridge_entwurf_callback`, `{v:1}`), so the v2 payload the checkout launcher sent was
|
|
155
|
+
refused as `version-unsupported` — the exact condition `doctor-omp-bridge` exists to name, and
|
|
156
|
+
it did (runtime FAIL / ownership PASS). `install-omp-bridge` with no omp session open, doctor
|
|
157
|
+
green on both axes, re-run 21/21. The herdr re-run on `149bef7` is 31/32: the two repaired
|
|
158
|
+
cells pass; the remaining red is a claude-code child that called back and then never attempted
|
|
159
|
+
the delivery verb, recorded in ROADMAP as a follow-up rather than patched on n=2.
|
|
160
|
+
- **The plugin runtime lock names `herdr-checkout` again, for the length of the 0.24.0
|
|
161
|
+
candidate window** — the same two steps 0.23.0 (`dd84ac0`) and 0.23.1 (`3c3a67d`/`194e800`)
|
|
162
|
+
took: an `npm` lock must name a version that is already published and coherent with this
|
|
163
|
+
checkout's `package.json` (`certifyLockCoherence`, `check-herdr-runtime-bootstrap` 8a), and the
|
|
164
|
+
version bump lands before the publish. Lock and `check-herdr-plugin-build` moved together
|
|
165
|
+
(`47f6d36`, the measured inverse of `194e800`); the npm pin returns with its published sha512
|
|
166
|
+
once 0.24.0 is on the registry.
|
|
167
|
+
|
|
168
|
+
- **`plugins/herdr` 0.3.1 leads with the workbench a visitor is looking at.** Its manifest and
|
|
169
|
+
README now say that Entwurf brings sibling AI sessions into Herdr, then name its actual narrow
|
|
170
|
+
integration scope: the install-time build activates only Herdr-integrated pi and Claude Code,
|
|
171
|
+
while the pane is read-only. This is an affordance correction, not a new runtime claim.
|
|
172
|
+
- **The plugin runtime lock is pinned to npm `@junghanacs/entwurf@0.23.1`** and its published
|
|
173
|
+
sha512, closing the candidate window 0.23.1 shipped in. The pair moves together, as that entry
|
|
174
|
+
says it must: the lock and `check-herdr-plugin-build`'s committed-lock expectations, with the
|
|
175
|
+
`IDENTITY` fixture's sha512 travelling with its version rather than being left on the previous
|
|
176
|
+
release's bytes. The integrity was read from the registry in this session
|
|
177
|
+
(`npm view @junghanacs/entwurf@0.23.1 dist.integrity`) rather than copied from another
|
|
178
|
+
session's report, which is the whole point of a pin.
|
|
179
|
+
|
|
180
|
+
## 0.23.1 - 2026-09-19
|
|
181
|
+
|
|
182
|
+
### Changed
|
|
183
|
+
|
|
184
|
+
- **The plugin runtime lock names `herdr-checkout` again, for the length of the 0.23.1 candidate
|
|
185
|
+
window.** This is a state, not a direction: an `npm` lock must name a version that is ALREADY
|
|
186
|
+
published and coherent with this checkout's `package.json`
|
|
187
|
+
(`certifyLockCoherence`, asserted on the committed lock by `check-herdr-runtime-bootstrap` 8a,
|
|
188
|
+
which sits inside `check:hermetic`), and the version bump necessarily lands before the publish.
|
|
189
|
+
So a cut cannot ride an npm lock naming the version it is about to create. `dd84ac0` is the
|
|
190
|
+
precedent and the exit: 0.23.0 was tagged on the checkout carrier and pinned to npm afterwards,
|
|
191
|
+
and 0.23.1 follows the same two steps. While this window is open, `smoke-herdr-raw-install-live`
|
|
192
|
+
reports the npm acquisition axis as a NAMED SKIP rather than a failure, and still asserts that
|
|
193
|
+
the runtime came from the source this ref locks.
|
|
194
|
+
|
|
195
|
+
**A pin and its inverse move the lock AND `check-herdr-plugin-build` together**, which is a fact
|
|
196
|
+
worth writing down because it has now been rediscovered twice. That gate reads the COMMITTED lock
|
|
197
|
+
in three places — the two-stage journey expects one acquisition and the locked `kind@version`, the
|
|
198
|
+
activation-authority cell seeds its ledger from the committed kind, and the progress cell pins the
|
|
199
|
+
long-step wording — so `dd84ac0` changed 90 lines of it alongside the eight lines of lock, and
|
|
200
|
+
this commit is the measured inverse of both halves (14/14 assertions, exit 0). No mutant is
|
|
201
|
+
disturbed: every claim in `scripts/mutants/herdr-plugin-build.json` names a production file
|
|
202
|
+
(`herdr-plugin.toml`, `lib/build.mjs`, `lib/build-progress.mjs`), none names the gate, and the
|
|
203
|
+
`HPB-PROGRESS-NAMED-SEQUENCE` needle re-aimed by `37b81e7` still occurs exactly once in
|
|
204
|
+
`lib/build.mjs`, which this commit does not touch. Teaching the gate to read either lock kind,
|
|
205
|
+
so neither direction needs it flipped again, is deferred to its own issue after the cut.
|
|
206
|
+
|
|
207
|
+
### Added
|
|
208
|
+
|
|
209
|
+
- **A test written beside the behaviour it certifies is run by public `pnpm check`, and nobody
|
|
210
|
+
names the file** (#119 V2). `vitest.config.ts` include grows two globs —
|
|
211
|
+
`pi-extensions/**/*.test.ts` and `plugins/herdr/**/*.test.mjs` — and `check-tests-beside-behavior`
|
|
212
|
+
expands them every run and hands the resulting paths to `run_vitest`. Both halves were measured
|
|
213
|
+
the hard way. The first cut passed `pi-extensions plugins/herdr` as vitest POSITIONALS, which are
|
|
214
|
+
path substrings, so `test/pi-extensions-probe.test.ts` was selected too (measured: it ran);
|
|
215
|
+
expanding first keeps the selected set exactly the two globs while the shim still contains no
|
|
216
|
+
filename. And an empty expansion RETURNS rather than calling vitest with no arguments, because a
|
|
217
|
+
vitest run with no filter runs the whole include. The literal cross-check against
|
|
218
|
+
`vitest.config.ts` stays, because the shell cannot read vitest's resolved config and a deleted
|
|
219
|
+
glob would otherwise pass. Proven by three still photos in one public `pnpm check`: two failing
|
|
220
|
+
beside-behaviour tests turn it red (`selected 2`), and a third file planted under `test/` is not
|
|
221
|
+
selected.
|
|
222
|
+
- **`entwurf pi`** (#118 hop 2) — `exec pi --entwurf-control "$@"`, a dispatcher case with no new
|
|
223
|
+
bin. Two things it deliberately does NOT do, both measured on pi 0.85.1: there is no
|
|
224
|
+
precondition check, because an unregistered host already gets `Error: Unknown option:
|
|
225
|
+
--entwurf-control` and exit 1 from pi itself; and the flag is added, never deduplicated, because
|
|
226
|
+
passing it twice was measured byte-identical to passing it once. One recursion fence, not
|
|
227
|
+
copilot's two — the sentinel closes the real loop, and the case copilot's second fence covers is
|
|
228
|
+
measured here to be a fail-closed unknown verb rather than a loop. `check-pi-launch` reuses
|
|
229
|
+
`check-copilot-launch`'s fake-vendor posture and carries the `--entwurf-control` literal
|
|
230
|
+
cross-check, since the shell cannot import `ENTWURF_CONTROL_FLAG`. Nine mutants, each killed by
|
|
231
|
+
exactly its own claim; exit-status passthrough is asserted without one, because `set -euo
|
|
232
|
+
pipefail` couples it to `exec` and a mutant that kills two claims attributes to neither.
|
|
233
|
+
- **`smoke-herdr-raw-install-live`** (#118 hop 1) — the plugin's first user path, on a clean
|
|
234
|
+
`node:24` container with no host socket/config/cache mounted, no git `insteadOf`, and the public
|
|
235
|
+
remote spelled as a user would type it. It closes the one npm re-proof axis VERIFY.md demanded
|
|
236
|
+
and the production lock had left empty since `dd84ac0`. Receipts: **(a)** `--ref main` →
|
|
237
|
+
`37b81e725cde4d0a548f1c0faab4fcb5c62942b2`, **(b)** `--ref set/119-verify-herdr` →
|
|
238
|
+
`11ec0c38af36d5cd4a59614c3c8e07c5b5b0f9ae`, both `kind=npm @junghanacs/entwurf@0.23.0` with the
|
|
239
|
+
ledger widening `["pi"]` → `["pi","claude-code"]` and the pi wiring byte-identical across the
|
|
240
|
+
reinstall. Three facts it measured rather than assumed: herdr's offline-persist path carries
|
|
241
|
+
0.9.1, so `plugin install --yes` completes with no server; `herdr integration install` refuses a
|
|
242
|
+
harness that has never run, because it creates `~/.pi/agent/extensions` only when the parent
|
|
243
|
+
exists and refuses Claude without `~/.claude`; and `--ref` takes a REMOTE REF, not an arbitrary
|
|
244
|
+
commit (`--ref <sha>` fails as `couldn't find remote ref`).
|
|
245
|
+
|
|
246
|
+
### Changed
|
|
247
|
+
|
|
248
|
+
- **`plugins/herdr` 0.3.0 — the Install path, rewritten as the nine steps a raw box needs.** The
|
|
249
|
+
eighth and ninth are the ones nobody guesses and they come from the container measurement above:
|
|
250
|
+
each harness must be started once (`pi --help`, `claude mcp list`) before herdr will integrate
|
|
251
|
+
it. The release says what it does NOT give you, because the honest combination is this plugin
|
|
252
|
+
plus the runtime its lock still names: `entwurf pi` exists in the repository and is not in
|
|
253
|
+
`@junghanacs/entwurf@0.23.0`, so the user command stays `pi --entwurf-control` until the next
|
|
254
|
+
cut carries the launcher. **`runtime-lock.json` is untouched on purpose** — a pin can only move
|
|
255
|
+
after the version it names is published, so the 0.23.1 pin is a follow-up commit after that
|
|
256
|
+
release, the same two-step `dd84ac0` took for 0.23.0.
|
|
257
|
+
- **Thirteen v2-spine gates moved out of `scripts/` and beside the modules they certify** (#119
|
|
258
|
+
V3), assertion for assertion, with the inventory's denominator fixed FIRST so the move reads as
|
|
259
|
+
a move. `inventory-verification-surface.ts` now counts both vitest locations; without that, the
|
|
260
|
+
migration would have subtracted 2,799 lines from the one number that exists to make migration
|
|
261
|
+
honest. What actually left is 133 lines of plumbing — per-file `ok()` scaffolding, `main()`
|
|
262
|
+
wrappers, summary trailers — and the combined file count is unchanged, seven out and seven in
|
|
263
|
+
per slice.
|
|
264
|
+
- The six mutant-carrying lanes needed four rules the mutant-free seven did not, each learned by
|
|
265
|
+
getting it wrong: a QK claim must BE an `it` title (attribution reads the failed test title);
|
|
266
|
+
the token must appear exactly once per file (title keeps the token, assertion keeps the
|
|
267
|
+
sentence); the `run.sh` case survives as a mutant EXECUTION COORDINATE and keeps its
|
|
268
|
+
`check:contracts` entry (branch CI caught the removal — a gate a mutant names must run inside
|
|
269
|
+
`check:full`); and attribution is containment, not exclusivity (vitest runs every test, so one
|
|
270
|
+
mutation surfaces 1–15 failed titles). 25/25 mutants verified killed and attributed by hand;
|
|
271
|
+
733 mutants across 58 lanes unchanged, `signatureSource` moved, gate argv did not.
|
|
272
|
+
- `entwurf-v2-visible-resume.test.ts` grew 445 → 908 lines and the growth is the design:
|
|
273
|
+
seventeen claims that shared four drives of the subject now each replay their own, because
|
|
274
|
+
nearly every one is an "X happened BEFORE Y" reading a call-order trace, and a trace polluted
|
|
275
|
+
by a neighbouring test answers wrongly while staying green.
|
|
276
|
+
- Three gates stay under `scripts/` with a reason: `check-entwurf-v2-contract` and
|
|
277
|
+
`check-entwurf-v2-production` are cross-lane (`mux-boundary.json` and `codex-native.json` call
|
|
278
|
+
them as gate argv), and `check-entwurf-v2-surface` generates its seven QK labels inside shared
|
|
279
|
+
helpers called twice from `await` blocks, so the established method would either lose
|
|
280
|
+
attribution or require an async `describe`.
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
- **The bundled Claude ACP adapter is `@agentclientprotocol/claude-agent-acp` 0.79.0** (from
|
|
284
|
+
0.76.0, crossing 0.77.0/0.78.0/0.79.0; `@anthropic-ai/claude-agent-sdk` 0.3.257 → 0.3.274,
|
|
285
|
+
ACP wire SDK 1.4.0 and `@anthropic-ai/sdk` 0.100.1 unchanged). Three upstream releases, three
|
|
286
|
+
changes with a reachable shape, and **no entwurf code change** — each "inert" is a fresh
|
|
287
|
+
measurement, not the previous bump's argument reused:
|
|
288
|
+
- 0.77.0's BREAKING removal of `claudeCode.options.agent` misses us twice: our
|
|
289
|
+
`buildClaudeSessionMeta` never set that key, and we spawn the adapter's binary rather than
|
|
290
|
+
importing the removed agent-picker exports (repo-wide grep: 0 hits).
|
|
291
|
+
- 0.77.0's `allowDangerouslySkipPermissions` host opt-out is a NEW lever we do not pull. We set
|
|
292
|
+
the option nowhere, `ALLOW_BYPASS` is byte-identical across both versions, and the overlay's
|
|
293
|
+
`permissions.defaultMode: "bypassPermissions"` resolves the same under each, so a sibling's
|
|
294
|
+
effective permission mode is unchanged.
|
|
295
|
+
- 0.78.0's `compaction_update` / `compaction_summary_chunk` are the first new `sessionUpdate`
|
|
296
|
+
kinds since the pin moved, and they are gated off by our `clientCapabilities: {}` — with the
|
|
297
|
+
mapper's forward-compatible `default` arm as an independent second reason. The `stopReason`
|
|
298
|
+
axis, where "unknown is an error" actually applies, is unchanged.
|
|
299
|
+
- 0.79.0's shell-command permission prompts reorder the option array reject-first under the new
|
|
300
|
+
`defaultToNo` hint. Our approve-all policy selects by option KIND, not position, and every
|
|
301
|
+
upstream option builder emits an allow option, so the order-sensitive fallback is unreachable.
|
|
302
|
+
Re-measured and unchanged: the #96 `mcpServerStatus` evidence (2 call sites, now at
|
|
303
|
+
`v0.79.0 src/acp-agent.ts:1773`/`:1866`, surrounding 200-line window byte-identical) and all four
|
|
304
|
+
model-forcing/accounting wire calls (`setSessionConfigOption`, `sessionUsage`, `turnQuotaMeta`,
|
|
305
|
+
`resolveModelPreference` — byte-identical). Full per-change classification with evidence state:
|
|
306
|
+
[ROADMAP.md](./ROADMAP.md) 2026-09-18 bump entry and
|
|
307
|
+
[docs/acp-backend-rail.md](./docs/acp-backend-rail.md) capability posture.
|
|
308
|
+
|
|
309
|
+
- **The Herdr plugin is 0.2.0 and pins its runtime to npm `@junghanacs/entwurf@0.23.0`.** Its
|
|
310
|
+
committed lock carries the published sha512
|
|
311
|
+
`sha512-ZR2VCui7JjK3w56rQSDs3AuAJMMuiXCNWH7HB52SQ3E/7p0oPhcxD+fb6Gdzi0VcBnheqxPzvJHMPQQcdYtNiw==`;
|
|
312
|
+
the 0.1.0 `herdr-checkout` source remains the candidate-verification path.
|
|
313
|
+
|
|
7
314
|
## 0.23.0 - 2026-09-18
|
|
8
315
|
|
|
9
316
|
### Added
|
package/FAQ.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Entwurf FAQ
|
|
2
|
+
|
|
3
|
+
Short answers for people meeting Entwurf from a different agent-harness tradition.
|
|
4
|
+
|
|
5
|
+
## Is Entwurf an agent factory or a subagent framework?
|
|
6
|
+
|
|
7
|
+
No. Entwurf is closer to a workshop than a factory: it connects independently owned, visible sibling sessions without turning them into workers of a new central runtime. A harness may run its own subagents or teams, but that remains the harness's responsibility.
|
|
8
|
+
|
|
9
|
+
## What does Entwurf do?
|
|
10
|
+
|
|
11
|
+
It gives an existing session a stable **garden id** and routes a message, reply, or visible sibling launch through the rail that session actually supports. It does not reconstruct prompts, hydrate transcripts, manage credentials, or emulate another harness.
|
|
12
|
+
|
|
13
|
+
## What is a garden id?
|
|
14
|
+
|
|
15
|
+
A garden id is the stable address of one visible, record-backed session. Think of it as an invitation to a shared workshop table, not a worker name, a pane id, a transcript id, or a worktree lease.
|
|
16
|
+
|
|
17
|
+
## How are session state and project state separated?
|
|
18
|
+
|
|
19
|
+
Entwurf does not merge or isolate either one. Each harness keeps its own authentication, tools, transcript, and workspace policy. Git branches, worktrees, file locks, task allocation, and any coordination around a shared checkout belong to the operator and the harnesses involved; a garden id is never a file-isolation claim.
|
|
20
|
+
|
|
21
|
+
## What happens to a harness's internal subagents?
|
|
22
|
+
|
|
23
|
+
The visible top-level session is the garden principal. Internal children remain inside that harness and do not receive a second garden id, separate delivery authority, or an Entwurf-managed access-control layer.
|
|
24
|
+
|
|
25
|
+
## Does sending a message start a process?
|
|
26
|
+
|
|
27
|
+
No. [`entwurf_v2`](./README.md#entwurf_v2--canonical-dispatch-verb) only addresses an existing garden citizen and selects its supported delivery rail. `entwurf_fresh_call` opens a new visible sibling; `entwurf_resume_call` reopens a dormant pi citizen under the same id.
|
|
28
|
+
|
|
29
|
+
## Where do visible siblings open?
|
|
30
|
+
|
|
31
|
+
Inside Herdr, Entwurf opens a new unfocused tab in the caller's workspace and currently admits pi and Claude Code. Outside Herdr, it opens one of the fixed pi, Claude Code, Copilot, OMP, or Codex runtimes in a visible tmux window. The runtime keeps its own model, tools, authentication, and transcript.
|
|
32
|
+
|
|
33
|
+
## Does Entwurf replace Herdr or tmux?
|
|
34
|
+
|
|
35
|
+
No. Herdr and tmux provide visible seats. Entwurf provides identity, delivery, and receipts across independent harness sessions. A pane or tmux session is a location for a sibling, never its address or liveness proof.
|
|
36
|
+
|
|
37
|
+
## How do I install it?
|
|
38
|
+
|
|
39
|
+
Use the [direct installation route](./README.md#install) for the broader harness surface. In an existing Herdr workbench, use the [Herdr integration guide](./plugins/herdr/README.md); it activates only Herdr-integrated pi and Claude Code and leaves the rest to the direct route.
|
|
40
|
+
|
|
41
|
+
## How do I start pi as a citizen?
|
|
42
|
+
|
|
43
|
+
After the direct install, run `entwurf pi`; it starts pi with Entwurf's control surface. The Herdr integration does not put `entwurf` on `PATH`, so its documented command remains `pi --entwurf-control`.
|
|
44
|
+
|
|
45
|
+
## Where is the precise contract?
|
|
46
|
+
|
|
47
|
+
[AGENTS.md](./AGENTS.md) owns the project invariants. [DELIVERY.md](./DELIVERY.md), [VERIFY.md](./VERIFY.md), and [docs/adding-a-harness.md](./docs/adding-a-harness.md) own capability, evidence, and admission details.
|
package/README.md
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
npm package: <https://www.npmjs.com/package/@junghanacs/entwurf>
|
|
10
10
|
|
|
11
|
+
**Short answers for common questions:** [FAQ.md](./FAQ.md).
|
|
12
|
+
|
|
11
13
|
Legacy package: [`@junghanacs/pi-shell-acp`](https://www.npmjs.com/package/@junghanacs/pi-shell-acp). `entwurf` is its 0.12+ successor line: the same work renamed around the garden-citizen dispatch substrate rather than the pi adapter.
|
|
12
14
|
|
|
13
15
|
> **Repository shape.** This repo is **entwurf-core (v2 dispatch) + native-harness bridges + a pi adapter + an ACP plugin**. Pi is one adapter, not the project subject. Claude Code, GitHub Copilot CLI, and OMP (`omp`) are shipped self-fetch citizens; Antigravity (`agy`) is shipped native-push. Codex CLI is a **native-push citizen, supported in 0.21.0 on Linux** over the operator-owned app-server: vendor-trusted user-scope birth, strict request-scoped identity, loaded-thread probe, one-shot queue delivery, and visible fresh beside the CALLER's own pane. Unrestricted attached-TUI seat inference is not supported or claimed; #95 D1 retired the fixed `codex` tmux home on 2026-09-16, so the operator seats the app-server wherever they like and a Codex caller opens its sibling beside its own TUI pane. Codex remains native so it keeps its vendor tools, delegation, and work context; this is not another way to buy or expose GPT through ACP.
|
|
@@ -252,7 +254,7 @@ cd ~/repos/gh/entwurf
|
|
|
252
254
|
|
|
253
255
|
The full source setup requires Node 24, pnpm, and Python 3 on PATH. Harnesses are
|
|
254
256
|
optional-by-presence: `setup` runs the frozen dependency install, then composes what the operator
|
|
255
|
-
already installed — a compatible `pi` (`>=0.
|
|
257
|
+
already installed — a compatible `pi` (`>=0.86.0 <0.87`), Claude Code, agy, and the Copilot CLI
|
|
256
258
|
each get their wiring completed when detected, an absent harness is an explicit zero-state SKIP,
|
|
257
259
|
and a detected harness that cannot be completed (including a below-floor `pi`) is a named FAIL
|
|
258
260
|
that makes setup exit nonzero. `setup` never installs a harness binary or touches a credential
|
|
@@ -286,7 +288,7 @@ This rail is CERTIFIED on Linux; on macOS it is NOT CERTIFIED — pending
|
|
|
286
288
|
physical host (control sockets, mux, ACP plugin hosting).
|
|
287
289
|
|
|
288
290
|
To use the `entwurf` provider inside pi, install a compatible pi binary
|
|
289
|
-
separately (`@earendil-works/pi-coding-agent >=0.
|
|
291
|
+
separately (`@earendil-works/pi-coding-agent >=0.86.0 <0.87`). Then point pi at
|
|
290
292
|
the npm-installed package or development clone:
|
|
291
293
|
|
|
292
294
|
```bash
|
|
@@ -636,7 +638,7 @@ A two-pane recording covers the pre-0.12 v1 surface end-to-end — sibling resum
|
|
|
636
638
|
|
|
637
639
|
Live peer messaging carries a sender envelope `{ sessionId, agentId, cwd, timestamp, origin?, replyable? }`; `entwurf_self` returns that authoritative envelope for the current pi session or trusted meta-session. Plain external MCP hosts are non-replyable. A garden-native meta-session carries a trusted `meta-session` envelope, but **`replyable` is a fact its own rail decides, not a consequence of being trusted** — a self-fetch citizen needs a live armed receiver, a native-push citizen needs an alive adapter probe, and a pi session needs its control socket. `entwurf_self` also reports which rail a meta-session reply would ride, because a native-push citizen has no mailbox to name. `wants_reply` is an etiquette marker rendered as a `(wants reply)` badge — not a transport contract, no wait, no polling. **v2 never gates on it:** a `wants_reply` from an external/non-replyable caller is passed through and surfaced honestly beside that sender's `replyable: false`, not rejected — the decider routes on target + intent, never on sender replyability. (The retired v1 `entwurf_send` did reject it; that behaviour went with the verb.)
|
|
638
640
|
|
|
639
|
-
In ACP-backed and external native-harness sessions, `entwurf-bridge` exposes
|
|
641
|
+
In ACP-backed and external native-harness sessions, `entwurf-bridge` exposes eight tools: `entwurf_v2`, `entwurf_peers`, `entwurf_fresh_call`, `entwurf_resume_call`, `entwurf_callback`, `entwurf_self`, `entwurf_inbox_read`, and the explicit/manual `entwurf_register_native` fallback. Native pi exposes the shared capability directly through the extension surface (`entwurf_v2`, `entwurf_peers`, `entwurf_fresh_call`, `entwurf_resume_call`, `entwurf_callback` tools; the socket-scan `/entwurf-sessions` command is gone — #50 C4). **For garden-id delivery/reply use `entwurf_v2`** — the canonical surface that classifies the target and routes to live-pi / self-fetch meta-mailbox (Claude Code, Copilot, OMP) / native-push (Antigravity, Codex), and refuses a dormant target instead of waking it. **To open a sibling that does not exist yet use `entwurf_fresh_call`** — WHERE it opens is a fact about the calling process, not a parameter: inside herdr (`HERDR_ENV=1`) it opens a NEW TAB in the caller's own workspace, without taking focus, and admits only `pi` and `claude-code`, refusing `placement` by name; everywhere else it launches one of five fixed backends (`pi`, `claude-code`, `copilot`, `omp`, `codex`) as a visible window on the operator's own tmux server. There is no fallback between the two — an incomplete herdr context is refused by its herdr reason rather than quietly opening a tmux window nobody inside herdr can see. Everything that follows in this paragraph describes the tmux rail; the herdr rail's coordinates, refusals and reclaim are owned by [docs/herdr-launch-rail.md](./docs/herdr-launch-rail.md). On tmux it requires an explicit model, accepts one optional literal absolute `cwd` (omit it or pass `""` to use the caller's cwd) and one optional `placement.tmuxSession` naming an EXISTING session on that server to open the sibling in instead of the caller's own (the operator's project seat — nothing is ever created: an absent session is `tmux-session-missing` and a name outside the addressed grammar `[A-Za-z0-9][A-Za-z0-9_-]*` is `tmux-session-name-invalid`, with no window and no session left behind. The two inputs never infer each other, and the positive form of that matters more than the prohibition: **with `cwd` omitted, a seated sibling starts in THIS agent's own working directory**, measured — it does not inherit the target session's path or its active pane, so naming the `org` seat does not put the sibling in the `org` project. Pass `cwd` when you want a different directory. entwurf never resolves that path: it hands the literal string to tmux, so with a SYMLINKED directory the receipt echoes what you asked for while the record, the transcript path, the callback envelope and `pane_current_path` all show the path the OS resolved it to — two spellings of one place, and the resolved one is what `entwurf_resume_call` later reopens), passes the model and selected directory through the runtime's visible launch path, and hands it a first task; a `copilot` launch goes through entwurf's own managed invocation and is refused before any window opens if this host lacks the Copilot birth, MCP, receiver or visible-footer units, and an `omp` launch carries its task in the `--entwurf-bootstrap` payload instead of an argv prompt because that vendor connects its MCP tools after the session has already started. Omitted placement follows the CALLER rather than the backend being opened: a Codex CALLER opens its sibling beside its own TUI pane, located by the `thread-id` that pane's terminal title carries (0 or 2+ matching panes reject with no fallback); every other caller opens in its own seat. (0.21.0 shipped a backend-selected default instead — an omitted-placement `codex` TARGET resolved a fixed operator-owned session named `codex` — and #95 D1 retired it on 2026-09-16.) Any of the five can still be sent to an operator-owned existing seat with an explicit `placement.tmuxSession`, and named `claude`/`pi` seats remain available that way, but they are not omitted-placement defaults. Read tmux here as the VISIBLE LAUNCH SEAT, never as an address: the garden id is the address, and it arrives in the callback envelope. Sibling symmetry means each backend keeps its own transcript, auth, native tools and visibility — not that the transports are made identical. The sibling's first action is a nonce callback whose sender envelope carries its garden id. The launch receipt records the requested model/cwd/seat plus tmux coordinates — including the RESOLVED target session id, which is where the window actually is — and never claims that the runtime accepted them or completed delivery. (The v1 verbs `entwurf` / `entwurf_resume` / `entwurf_send` are gone.) Garden-native operator commands require `--entwurf-control`. There is no spawn target allowlist — the target registry is gone (#50 C3): `entwurf_v2` and `entwurf_resume_call` address an existing record-backed citizen, while `entwurf_fresh_call` takes its explicit backend/model/task and optional cwd directly rather than resolving a model tuple from a registry file. **To reopen a DORMANT pi citizen use `entwurf_resume_call {target}`** — the record supplies transcript, model, provider and cwd, so it takes no prompt, no task and no model override; it runs no turn, returns a LAUNCH receipt and a separate OBSERVATION receipt (only the second says the citizen is back), refuses a non-pi target as `target-not-pi`, and on an unobserved socket leaves the visible window open and releases its lock rather than retrying.
|
|
640
642
|
|
|
641
643
|
### `entwurf_v2` — canonical dispatch verb
|
|
642
644
|
|
package/VERIFY.md
CHANGED
|
@@ -5,7 +5,7 @@ invariants live in gates; this file defines evidence strength, release acceptanc
|
|
|
5
5
|
and the manual judgements a gate cannot make.
|
|
6
6
|
|
|
7
7
|
> **Current surface.** `entwurf-bridge` exposes `entwurf_v2`, `entwurf_peers`,
|
|
8
|
-
> `entwurf_fresh_call`, `entwurf_resume_call`, `entwurf_self`, `entwurf_inbox_read`, and `entwurf_register_native`. The ACP
|
|
8
|
+
> `entwurf_fresh_call`, `entwurf_resume_call`, `entwurf_callback`, `entwurf_self`, `entwurf_inbox_read`, and `entwurf_register_native`. The ACP
|
|
9
9
|
> backends are Claude and Snowflake Cortex Code. Antigravity is a separate shipped
|
|
10
10
|
> native-push citizen lane. Codex is a native-push citizen supported in 0.21.0, whose birth hook is
|
|
11
11
|
> operator-owned and gated on one vendor trust receipt, with strict request-scoped identity and
|
|
@@ -44,6 +44,45 @@ When you write a new entry, mark its rung. "L1 only" is honest; "L2 reached" is
|
|
|
44
44
|
|
|
45
45
|
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.
|
|
46
46
|
|
|
47
|
+
### Where a verification lives — two locations, and what decides
|
|
48
|
+
|
|
49
|
+
A new contract's test goes BESIDE the behaviour it certifies: `pi-extensions/**/*.test.ts` or
|
|
50
|
+
`plugins/herdr/**/*.test.mjs`. Nothing names those files. `check-tests-beside-behavior` expands
|
|
51
|
+
the two globs every run and hands the resulting paths to `run_vitest`, so landing a test edits no
|
|
52
|
+
list — and a failure is attributed from the JSON test TITLE (`run.sh:104-113`), never from the
|
|
53
|
+
code frame, which quotes an adjacent passing test's own title.
|
|
54
|
+
|
|
55
|
+
`scripts/check-*` is not a legacy pile; it is the home of what a vitest lane cannot hold. What
|
|
56
|
+
stays there, and why: LIVE lanes that need real accounts, models, tmux or a Docker daemon;
|
|
57
|
+
install and pack gates that consume a tarball; gates that spawn a subprocess or stand a server up;
|
|
58
|
+
gates that read product source as text; and the mutant EXECUTION COORDINATES described below. A
|
|
59
|
+
new hand-built gate needs a reason one of those five applies.
|
|
60
|
+
|
|
61
|
+
**Four rules bind a migrated gate that carries mutants**, and each of them was measured rather
|
|
62
|
+
than assumed (#119 V3 slice 2):
|
|
63
|
+
|
|
64
|
+
1. **Every QK claim is its own `it` title.** Attribution reads the failed test title, so a QK
|
|
65
|
+
that lives only in an assertion message leaves its mutant unattributable.
|
|
66
|
+
2. **The token appears exactly once in the file.** Putting it in the title AND the assertion
|
|
67
|
+
label is refused by the manifest set-integrity contract — the title keeps the token, the
|
|
68
|
+
assertion keeps the sentence.
|
|
69
|
+
3. **The `run.sh` case survives as a mutant execution coordinate, and keeps its `check:contracts`
|
|
70
|
+
entry.** The case is not a discovery path: the door already finds the test. It exists because
|
|
71
|
+
the qualification runner re-invokes a gate's argv once per mutant, and a gate argv calling
|
|
72
|
+
vitest directly would skip the reporter attribution depends on. Its file filter is narrow,
|
|
73
|
+
because a mutant pointed at the glob shim would re-run every beside-behaviour test once per
|
|
74
|
+
mutant. It stays in `check:contracts` because `check-release-gate-outcomes` requires a gate a
|
|
75
|
+
mutant names to run inside `check:full` — reachable only through qualification, its going red
|
|
76
|
+
on a clean tree would be noticed by nothing but the mutant body.
|
|
77
|
+
4. **Attribution is containment, not exclusivity.** `assert.ok` aborted at the first broken claim,
|
|
78
|
+
so exactly one label was ever reported; vitest runs every test, so one mutation surfaces
|
|
79
|
+
several failed titles (measured: 1–15). A kill is attributed when the claim's token is AMONG
|
|
80
|
+
them.
|
|
81
|
+
|
|
82
|
+
Migration moves lines BETWEEN the two axes `inventory-verification-surface.ts` counts, and only a
|
|
83
|
+
drop in the COMBINED number is subtraction. That is why the inventory reads both locations: a gate
|
|
84
|
+
that moved out of `scripts/` and into a file beside its subject would otherwise read as deleted.
|
|
85
|
+
|
|
47
86
|
### The canonical floor — two entry points
|
|
48
87
|
|
|
49
88
|
- **Deterministic floor — two tiers (#70):** `pnpm check` is the everyday CORE — toolchain (lint + typecheck), the vitest lanes, and the pure-unit / behavioral-contract / source-topology gates plus the cheap static coherence checks. It prints its own total wall time; acceptance is ≤60s on the reference host `oracle` (an operator measurement, never a hard wall-clock gate on arbitrary hosts). `pnpm run check:full` is the FULL deterministic floor — the core plus the hermetic-integration and package/install tiers — and is what the frozen-candidate protocol, push CI, release-gate, and `prepublishOnly` run. Exact membership is the named `check:*` group scripts in `package.json` (the executable SSOT — this document records meaning and principles, not the command list); a gate changes tier by semantic-class decision, never by getting faster or slower. The FULL tier carries the qualification HEAD (`check-gate-manifests`, through `check:hermetic`); the everyday core does not, and neither tier carries the separately scheduled mutant-executing body (`check-gate-qualification`).
|
|
@@ -56,6 +95,8 @@ Verification here is not a benchmark. In production we exchange short turns and
|
|
|
56
95
|
|
|
57
96
|
> **The herdr rail is an on-demand axis until it has run.** `smoke-herdr-fresh-call-live` opens a real caller inside a real herdr pane, has it invoke the PUBLIC `entwurf_fresh_call`, and reads the child's nonce callback — the one axis no deterministic gate reaches, since the rail is selected from the caller's own process environment. **The release aggregate does not yet require the herdr rail**: herdr is optional (a cut on a host without it must not block), and first evidence precedes promotion — a rail whose first acceptance run is days old has not earned a place in the gate that blocks every cut. Run it directly with `LIVE=1 ./run.sh smoke-herdr-fresh-call-live` whenever a cut changes herdr rail code, and promote it to a MUST only after the axis has a run history to point at. Without `LIVE=1`, or without herdr/pi/claude on PATH, it reports protocol SKIP — never a pass.
|
|
58
97
|
|
|
98
|
+
> **The plugin's FIRST USER PATH is its own on-demand axis, and until it runs the npm acquisition has no receipt (#118 홉 1).** Every other herdr gate proves the plugin against a source this repository controls: `check-herdr-plugin-build` drives a stub, and `smoke-herdr-plugin-build-live` redirects the product remote to a local bare clone with git `insteadOf` and asserts `identity.kind === "herdr-checkout"`. The production lock has said `npm` since `dd84ac0`, and the re-proof this same document demands above (§ source switch) still has an empty acquisition row. `LIVE=1 ./run.sh smoke-herdr-raw-install-live [--ref <REF>]` fills that row in a clean `node:24` container with no socket, config, cache or repo mounted, no `insteadOf`, the public remote spelled as a user would type it, and pi / herdr / Claude Code installed in the IMAGE BUILD as scaffolding (Hard Rule 17 — the product face begins at `herdr integration install pi`). herdr itself comes from `scripts/install-herdr-ci.sh` against `scripts/fixtures/herdr-supply.json`, so no version or digest lives here. `--ref` defaults to `main` rather than a pinned SHA because receipt (a) is defined as what a user gets today; the resolved commit is printed, so the receipt stays exact. It takes a REMOTE REF and not an arbitrary commit — measured on herdr 0.9.1, `--ref <sha>` fails as `couldn't find remote ref`, because the checkout fetches by name — so a candidate is addressed by its branch and pinned by the resolved commit the gate prints. **Its first question is a measurement, not an assumption:** herdr registers a plugin through a running server and falls back to `persist_plugin_offline` only on a connection error (`src/cli/plugin.rs:914-981,1016-1021` @ `c77af189`, 0.9.0 source), and a container has no server — whether 0.9.1 carries that fallback is unmeasured, and a "no" is #118 investigation output rather than a completion. `--yes` is always passed so a non-interactive exit 2 (`:189`) can never be misread as a missing server. **At a ref whose lock names `herdr-checkout` the acquisition axis reports a NAMED SKIP rather than a red, and the rest of the journey still runs.** That lock is the candidate carrier a cut legitimately sits on — 0.23.0 was tagged that way and `dd84ac0` pinned npm afterwards — so a red there would report a broken install when what is true is that the ref does not lock npm. What is asserted at BOTH lock kinds is that the runtime came from the source the ref locks, and the package name is read from the checkout manifest rather than the lock, because a `herdr-checkout` lock carries no `name` (`herdr-runtime.mjs:317-328`) and joining `undefined` throws instead of failing an assertion. **Cells [6]–[9] carry it past the install into the USE path (#118 H1-4/H1-5).** An install is not a use, so the same container then runs what those bytes became: the compiled bridge entry, the three `REQUIRED_BINS` as executables, and a real `entwurf check-bridge` printing its exact seven-verb set — the installed-runtime row below, measured rather than inferred from the bootstrap's refusal to install without them. Then the pi that this plugin WIRED is started with no `-e` and no `--no-extensions`, because the subject is the wiring and not this checkout: one `get_state` in `--mode rpc` costs zero tokens and needs no provider argument (a bogus `--provider` exits 1 before session_start; omitting the pair lets pi resolve the default the extension itself registers), and while that session is alive the cell reads its V3 record, its gardenId-keyed control socket, and the installed `entwurf peer-facts` reporting it `alive`. The plugin's own status fan is then driven with NEITHER side stubbed, in both states a user can be in: with no herdr server it must name `herdr-agent-list-failed` and go red rather than draw an empty table, and under a headless `herdr server` — scaffolding for that second read only, stopped again before the teardown — it exits 0 and COUNTS the citizen as `unobserved` instead of dropping it. Last, the shipped `entwurf herdr-plugin-deactivate` runs from the installed bin, deletes the runtime it is executing from, and the plugin reinstalls onto the host it fully left — the deactivation row, on the npm source. **What these cells still do not close:** swap / torn-swap recovery on the npm source, the package-consumer proof, and the fan drawing a citizen as a ROW — that needs `placement.kind === "herdr-pane"`, so the pi session must live in a herdr pane a session reference joins, and a headless container has no panes. **This smoke carries no test beside behavior and no mutant**, and that is a property of its class rather than an omission: it is a real-live container journey whose oracle is the container's own end state, so there is no pure seam to assert against and nothing a source mutation could kill deterministically. Like the other two herdr LIVE lanes it is **on-demand and not an aggregate MUST** — herdr is optional, a cut on a host without it must not block, and first evidence precedes promotion. Without `LIVE=1`, or without a usable Docker daemon, it reports a named SKIP; `ENTWURF_REQUIRE_DOCKER=1` turns the Docker absence red. Neither skip is a pass.
|
|
99
|
+
|
|
59
100
|
> **The Herdr plugin's install-time build has two axes, and one of them needs the network (#116 M3-b3).** `check-herdr-plugin-build` is deterministic and rides `check:hermetic`: the listing reaches the runner through its own spawn seam (there is no fake `herdr` binary in this repo), and the one cell that drives the REAL binary reports a named SKIP on a host without it — `ENTWURF_REQUIRE_HERDR=1` makes that absence red. What it cannot have is the real acquisition: `npm pack git+https://github.com/junghan0611/entwurf.git#<full sha>` builds the bridge through `prepare`, which installs devDependencies from the registry. So `LIVE=1 ./run.sh smoke-herdr-plugin-build-live` owns that axis, and its cells are **two kinds of evidence, labelled as such**: three are the real Herdr journey — a real `herdr plugin install` driving the real `[[build]]`, with the UNCHANGED product remote redirected to a local bare clone by git `insteadOf` (the one substitution, which is what makes an unpushed candidate testable) — covering remote-commit available, the `{pi}` → `{pi, claude-code}` reinstall with OpenCode as a zero-write negative control and exactly ONE counted vendor MCP owner entry, and the post-build Herdr-commit gap (where the ledger has already rebound to the gap commit, so the honest assertion is that transition and not unchanged bytes). The fourth cell — remote-commit **unavailable** — calls the shipped runtime leaf directly with the fixed product argv and is therefore **acquisition-leaf evidence, not a journey**; routing it through Herdr would add a second explanation for one red. Its oracle is the honest one too: active runtime, ledger and harness bytes preserved, while the journal is left as the certified `installing` retry authority the transaction deliberately writes before acquiring, and a normal install afterwards is green. Its Claude cell is where the vendor question the deterministic gates refuse to claim gets answered — whether Claude Code accepts an ABSOLUTE executable as an MCP command — and it reports a named skip on a host without `claude`, never a pass. Like the herdr fresh-call rail, this smoke is **on-demand and not an aggregate MUST**. The release aggregate does not require the plugin build smoke: herdr is optional, a cut on a host without it must not block, and first evidence precedes promotion.
|
|
60
101
|
|
|
61
102
|
> **Switching the plugin's production acquisition source is a re-proof, not a configuration change (#116 M3-b3).** `runtime-lock.json` carries a closed `source` discriminant: `npm` (exact `name@version` plus the published sha512, compared against the tarball's own bytes) is the production authority, and `herdr-checkout` (the exact commit Herdr checked out, from the fixed remote) is a VERIFICATION-ONLY carrier that exists so a candidate needs no npm release. Evidence does not transfer between them. Before a release that moves the production source — to npm, or to a GitHub Release tarball plus its sha512 — the exact acquisition and integrity comparison, the installed runtime (name@version, compiled entry, three executable bins, a real `check-bridge`), the swap and torn-swap recovery, activation and deactivation, and the package-consumer proof must all be re-run against THAT source. A candidate proof green on the checkout carrier says nothing about a registry artifact, and the reverse is equally true. **Records from before this contract are refused, not migrated:** a v1 runtime journal or activation ledger fails certification by name, and the way forward on such a host is an explicit `entwurf herdr-plugin-deactivate` (or a hand-cleared runtime/ledger state) before the next install — there is no reader that accepts both schemas, because the older shape cannot say WHICH artifact it was.
|
|
@@ -228,7 +269,7 @@ The goal is not merely "invoke Claude Code." We want:
|
|
|
228
269
|
**One install command to remember: `./run.sh setup <project>`.** It is idempotent — re-run the exact same command whenever anything looks wrong. There is no second install surface to juggle: from a clone `setup` runs the whole floor in order.
|
|
229
270
|
|
|
230
271
|
1. `pnpm install` — installs the pinned development dependencies and builds the bridge (source-checkout bootstrap only; an installed package never runs npm/pnpm inside `node_modules`)
|
|
231
|
-
2. pi wiring → `<project>/.pi/settings.json` + user-scope registration — only when a `pi` inside the supported range (`>=0.
|
|
272
|
+
2. pi wiring → `<project>/.pi/settings.json` + user-scope registration — only when a `pi` inside the supported range (`>=0.86.0 <0.87`) is on PATH; absent pi is an explicit zero-state SKIP, a below-floor pi is a detected FAIL. The user-scope entry is owner-recorded (#86 C2): another root's live-or-missing ownership makes this step a zero-write refusal (setup: pi FAIL) that names `takeover-user-scope`; the install-states bind the exact managed settings path (a drifted/symlinked/corrupt target is a zero-write refusal) and the inverse removes only the recorded owner's exact entry; `doctor-pi-package` reports the verdict
|
|
232
273
|
3. Claude meta-bridge global plugin — only when `claude` is on PATH; otherwise skipped cleanly
|
|
233
274
|
4. source stable-bin exposure — including certified `entwurf` → this checkout's `run.sh`, the managed runtime Copilot fresh resolves; helper units are attempted independently and a foreign helper is a named FAIL
|
|
234
275
|
5. agy bridge + exact permission + statusline + `PreInvocation` hook — only when `agy` is on PATH; each adapter is idempotent and independently doctorable
|
package/demo/README.md
CHANGED
|
@@ -139,7 +139,7 @@ SCENE_DELAY=30 FINAL_PAUSE=10 bash demo.sh
|
|
|
139
139
|
|
|
140
140
|
## Prerequisites
|
|
141
141
|
|
|
142
|
-
- `pi` on PATH (current floor 0.
|
|
142
|
+
- `pi` on PATH (current floor 0.86.0)
|
|
143
143
|
- `entwurf` provider configured + auth ready for the selected sender/peer models
|
|
144
144
|
- `asciinema` installed
|
|
145
145
|
- `agg` installed (optional — only for GIF conversion)
|