@junghanacs/entwurf 0.12.7 → 0.12.8-repair.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 +5 -2
- package/BASELINE.md +40 -0
- package/CHANGELOG.md +24 -0
- package/DELIVERY.md +20 -2
- package/README.md +34 -4
- package/VERIFY.md +73 -6
- package/demo/README.md +1 -1
- package/docs/setup-clean-host.md +67 -11
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-sender-identity.js +8 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js +7 -4
- package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-hook.js +86 -22
- package/mcp/entwurf-bridge/start.sh +2 -1
- package/package.json +17 -9
- package/pi/meta-bridge/entwurf-meta-receive/hooks/hooks.json +8 -4
- package/pi/meta-bridge/entwurf-meta-receive/scripts/hook-launch.sh +77 -0
- package/pi-extensions/lib/meta-sender-identity.ts +8 -2
- package/pi-extensions/lib/meta-session.ts +8 -5
- package/pi-extensions/meta-bridge-hook.ts +87 -22
- package/run.sh +609 -37
- package/scripts/check-hook-launch-topology.ts +312 -0
- package/scripts/check-install-container.sh +458 -0
- package/scripts/check-install-surface.ts +207 -1
- package/scripts/check-meta-doctor-oracle.sh +479 -0
- package/scripts/check-meta-manifest-schema.py +45 -3
- package/scripts/check-meta-receiver-marker.ts +30 -5
- package/scripts/meta-bridge-claude-floor.sh +84 -0
- package/scripts/meta-bridge-doctor.sh +559 -40
- package/scripts/meta-bridge-install.sh +54 -14
- package/scripts/meta-bridge-uninstall.sh +4 -1
- package/scripts/smoke-meta-honesty.sh +13 -1
- package/scripts/smoke-meta-install-state.sh +100 -1
package/AGENTS.md
CHANGED
|
@@ -88,9 +88,10 @@ Warnings make agents blame themselves and flail. Broken tool state must surface
|
|
|
88
88
|
7. **This is not a second harness**: no prompt reconstruction, no transcript hydration, no tool result ledger, no harness emulation. Native bridges front only a garden id plus their narrow delivery rail (Claude mailbox or agy native-push); they do not scrape transcripts or run a replacement control daemon.
|
|
89
89
|
8. **Auth boundary is deployment-surface-agnostic**. This repo does not provide, copy, proxy, decrypt, or mediate any backend's credentials. Native-harness sessions read whatever auth state is visible in their own process filesystem; nothing here moves that.
|
|
90
90
|
9. **Native-push is not a mailbox or pi socket in disguise.** Antigravity replyability is `recordBacked ∧ probeAlive`; it gets no receiver marker, no `watchArmed`, and no spawn/resume authority. Its `agentId` remains `meta-session/antigravity`. The pid+start-key sender join assumes serialized model invocation per agy process: two conversations concurrently invoking under one pid are unsupported and must never be claimed safe.
|
|
91
|
-
10. **A green dev clone is not a working package.** Node refuses `--experimental-strip-types` below `node_modules`, so any surface an operator can invoke must reach compiled JS when installed. This class has shipped four times (start.sh 0.12.1, store-doctor 0.12.4, plugin hook 0.12.5, agy imprint + three operator commands 0.12.7) because the fence was crossed by hand, per surface, and the source-tree floor cannot see it. There is now exactly one crossing — `run_ts` in `run.sh` — and two gates that hold it: `check-install-surface` (structural) and `check-pack-install` (drives the real tarball, in CI). A new `.ts` entrypoint routes through `run_ts` or it does not ship. Dev-only gates have no compiled twin by design and must be REFUSED under an installed package, never silently skipped.
|
|
91
|
+
10. **A green dev clone is not a working package — and a green package on the maintainer's host is not a working consumer.** Node refuses `--experimental-strip-types` below `node_modules`, so any surface an operator can invoke must reach compiled JS when installed. This class has shipped four times (start.sh 0.12.1, store-doctor 0.12.4, plugin hook 0.12.5, agy imprint + three operator commands 0.12.7) because the fence was crossed by hand, per surface, and the source-tree floor cannot see it. There is now exactly one crossing — `run_ts` in `run.sh` — and two gates that hold it: `check-install-surface` (structural) and `check-pack-install` (drives the real tarball, in CI). A new `.ts` entrypoint routes through `run_ts` or it does not ship. Dev-only gates have no compiled twin by design and must be REFUSED under an installed package, never silently skipped — a `.sh` dev gate refuses in its own body, since `scripts/` ships whole and run.sh's dispatch is not the only way in. **`check-pack-install` is still a maintainer-shaped proof**: the checkout is present, every tree is operator-owned, and the install is project-local, so a surface that writes beside the installed package or depends on the repo being nearby is green there and broken for a real consumer. `check-install-container` (#51 C, own required CI job) closes that: one candidate tarball, read-only, into a container that has never seen this repo — non-root `npm install -g`, resolution through the PATH shim, a frozen package root, and a regular-file path+sha256 manifest fence across `install-meta-bridge`; the evidence line records the canonical tarball path + sha256 and the Node image id/repository digest. Default CI packs once into a temp dir; release acceptance passes a caller-preserved tarball through `ENTWURF_CANDIDATE_TGZ` and the gate consumes that exact file without re-packing, so `npm publish <same.tgz> --tag repair` can publish the accepted bytes. The two are not redundant detectors of one defect: the **freeze is a permission-level consumer fact** (the cell actually refuses the write, EACCES, the way a real consumer's host would), while the **manifest fence is the detector** — and it is exactly a regular-file path+sha256 comparison, not a whole-tree guarantee: it reads no permissions, ownership or symlink targets. A freeze at the package root alone is demonstrably insufficient (a write one directory down sails past it and only the fence sees it). Model the consumer's world, never a stricter one: a blanket `chmod -R a-w` freeze produced false reds because `cp -r` propagates modes into the installer's own assembly target, which no `sudo npm i -g` consumer can reach.
|
|
92
92
|
11. **Verification must not rewire the operator's own install.** An offline smoke that writes a live `~/.claude` / `~/.gemini` / `~/.pi` path uninstalls the operator as a side effect of "testing". Swap `HOME` **and every already-exported writable `XDG_*` root** (`XDG_DATA_HOME`: install-state · `XDG_STATE_HOME`: the imprint log · `XDG_CACHE_HOME`: the statusline gid cache): moving HOME alone still writes below the inherited roots. This class struck three times in two days — hard-verify 2026-07-13 (DATA, scratch scripts), `check-pack-install`'s own drives 2026-07-14 (DATA + STATE, inside run.sh), and `smoke-user-scope-citizen` 2026-07-14 (fake `PI_CODING_AGENT_DIR` paired with the real XDG ownership state, so its inverse followed the real `managedSettingsPath` and removed the live MCP key). `check-install-surface` S5 is a static **tripwire** over `scripts/*.sh` source only: it catches a literal live path, one hop of aliasing, (S5b) HOME-without-XDG swaps, and (S5c) a mutating `run.sh` drive left unsandboxed at any root that command writes — the agent dir, `XDG_DATA_HOME`, and, for `install`/`setup`, `HOME` itself, because `ensure_agent_dir_symlinks` hard-codes `$HOME/.pi/agent` and never reads the agent-dir override (so sandboxing `PI_CODING_AGENT_DIR` is not isolation for those commands) — but it cannot see a path assembled across variables, an embedded heredoc, or run.sh itself. **A tripwire keyed to one syntactic form is not a tripwire**: S5c first shipped matching only the inline-env drive, and a review mutation walked the identical leak straight past it by hoisting the same override into an `export` one line up. Match the drive, then demand the isolation — never the other way round. The dynamic complement is `check-pack-install`'s **outer self-fence**, which runs after every success or early-failure path: the operator's real `$XDG_DATA_HOME/entwurf` tree must be byte-identical, and the gate-specific fake agy marker count in the real `$XDG_STATE_HOME/entwurf/agy-imprint.log` must not increase (mutation-checked). Read a green S5 as "no obvious destructive line", never as "verification is sandboxed" — the real guarantee is running the offline floor under a swapped HOME+XDG, which is still open. LIVE gates are the only surfaces that may drive the real host, and they say so in their name.
|
|
93
93
|
12. **A doctor reports runtime truth and ownership truth separately.** Read the target's own semantics before calling a host broken. agy matches `mcp(*)` and `mcp(<server>)` against our tool wherever those rules appear, so an operator's broad `allow` already grants `entwurf_v2` — reporting that host as "NOT granted, agy prompts on every call" was a false red about a working surface. Installers still take the narrowest rule they need; doctors distinguish **we own this** from **someone else's rule is carrying it** from **it is genuinely broken**. Install-state is evidence only when it parses, names its required managed-path field as an absolute path, and that normalized path equals the live target this host reads; corrupt or foreign-target state is a failure even when the live command itself resolves. Ownership beats coverage: an element the state records as ours that has since vanished stays a failure even while an operator's broader rule keeps the surface working (a whole-file settings relink produces exactly this shape). Conversely, broken ownership state does not justify saying a visibly configured runtime command is absent — report both axes honestly and keep the final verdict red.
|
|
94
|
+
13. **A native-hook owner is structural, not a topology guess — and the structure is the exec form.** Shell-form command hooks do not expose one portable process tree: under the same Claude Code version we observed both a direct hook→Claude join and a retained `/bin/bash -c` wrapper, and ordinary tail-exec tests never reproduced the trigger. That form is retired, not patched. The meta-bridge declares the **exec form** — `command` is the shipped `hook-launch.sh`, `args` is the real argv — so no shell exists on the launch path, the launcher `exec`s the payload and preserves the pid, and the hook's parent IS Claude on every host (#51 B2, measured at Claude Code 2.1.217). The hook therefore reads `process.ppid` directly; the `$PPID` carrier, the ancestry walk, and the missing-carrier contract are **gone**, and re-introducing any of them means the manifest stopped feeding the owner. **But `process.ppid` is only the owner when the launcher was actually on the path, so `hook-launch.sh` stamps a non-identity `ENTWURF_META_HOOK_LAUNCH` provenance token and the hook writes NO sender/receiver marker without it.** This is not the retired carrier wearing a new name: the carrier smuggled a *pid* that had to be ancestry-checked, while this token carries no identity at all and answers only "was the authorized launch path taken". It is what keeps the upgrade mismatch fail-closed — an already-open Claude session still holding the OLD cached command reaches the new hook with a shell wrapper as its parent, and without the token that wrapper would be minted as an owner. Deleting it is never a cleanup. **entwurf requires Claude Code `>=2.1.217` and enforces that floor itself, because upstream gives no fail-loud:** an older Claude passes `plugin validate` on the exec manifest (unknown-key passthrough), then at runtime drops `args`, runs `command` alone, and reports the hook as `exit_code: 0, outcome: success` — measured at 2.1.138. `hook-launch.sh` refusing an empty argv is that silence made loud; installer and doctor refuse the version outright; there is no shell-form fallback for older versions. `check-hook-launch-topology` drives the shipped argv for real — including a plugin path containing a space, `$`, a backtick, and `;&` — and `check-claude-floor-coherence` keeps the floor one number derived from `package.json` `entwurf.claudeCodeFloor`. Evidence stays tiered: B/B2 are direct-native observations from actual 2.1.138/2.1.217 sessions on one NixOS host; the Linux artifact-consumer's fake Claude, planted cache, stand-in owner and `/proc` bridge are fixtures that prove package/oracle behavior, never a second native-host acceptance. **The doctor is the release oracle, so its exit 0 must mean every required layer was measured, never that a layer was skipped.** It resolves the ONE artifact Claude loads (`claude plugin list --json`.installPath; an ambiguous multi-version cache is refused, never guessed), classifies the installed *launch form* by name across all three owner hooks — a shell-form or launcher-less exec manifest is refused by name, not reported as unreadable drift — and then requires the live MCP↔marker join. Missing live evidence is `NOT CERTIFIED`, a failure worded distinctly from a broken install. The #51 repair cut has **Linux as its only currently certified axis**: install refuses Darwin because `/proc`-based live bridge discovery cannot certify it yet, doctor stays `NOT CERTIFIED`/nonzero there, and uninstall alone retains Darwin support so legacy state is not stranded. This is an evidence boundary, not a permanent macOS impossibility; future native validation may reopen the lane. `check-meta-doctor-oracle` holds this: a healthy fixture must reach PASS and twenty-one planted defects must each turn it red *naming their own cause*. An oracle with an optional central evidence layer is not an oracle.
|
|
94
95
|
|
|
95
96
|
## ACP Plugin Boundary
|
|
96
97
|
|
|
@@ -127,6 +128,7 @@ pnpm check # full static floor: lint + typechec
|
|
|
127
128
|
./run.sh check-entwurf-v2-matrix # the decider's state×intent table, read as an SSOT (REAL decideDispatch)
|
|
128
129
|
./run.sh check-entwurf-v2-decider # + -contract / -lock / -release / -send / -send-fallback / -mailbox / -runner / -production / -surface / -spawn / -spawn-production
|
|
129
130
|
./run.sh check-meta-session # + -record-v2 / -dual-read / -migration / -mailbox-state-write / -receiver-marker / -capability-source / -dual-consumers / -listing
|
|
131
|
+
./run.sh check-meta-doctor-oracle # detection power of the release oracle: healthy fixture reaches `doctor: PASS`, 21 planted defects each turn it FAIL naming their own cause
|
|
130
132
|
./run.sh check-native-push-adapter # agy probe/route leaf; separate from pi socket and mailbox liveness
|
|
131
133
|
./run.sh check-agy-sender-identity # record-backed pid/start-key sender resolution + ambiguity refusal
|
|
132
134
|
./run.sh smoke-agy-install-state # MCP + exact permission ownership + honest inverse (140)
|
|
@@ -134,6 +136,7 @@ pnpm check # full static floor: lint + typechec
|
|
|
134
136
|
./run.sh smoke-agy-hooks-state # PreInvocation birth/sender hook install surface (44)
|
|
135
137
|
./run.sh check-entwurf-bridge-boot # the MCP entwurf-bridge stands up + exposes the v2/native-register tool set
|
|
136
138
|
./run.sh check-install-surface # structural strip-types fence: run_ts is the only crossing, every operator command has a compiled twin, offline smokes never write the real $HOME
|
|
139
|
+
./run.sh check-install-container # Linux artifact CONSUMER (#51 C, own CI job): one candidate .tgz, read-only, into a checkout-invisible node:<engines-major> cell — non-root `npm install -g`, PATH shim, frozen package root, MCP tools/list, install-meta-bridge under a path+sha256 byte-fence, strict doctor. Default pack-once temp; ENTWURF_CANDIDATE_TGZ consumes an exact preserved file without re-pack. SKIP without Docker; ENTWURF_REQUIRE_DOCKER=1 makes that RED
|
|
137
140
|
./run.sh check-bridge /path/to/project # entwurf-bridge direct MCP smoke (tools/list + protocol/negative-path)
|
|
138
141
|
./run.sh check-auth-boundary # ACP plugin no-auth sentinel present + no legacy-ENV apiKey literal (trust invariant, code-level)
|
|
139
142
|
./run.sh check-acp-provider-surface # provider registers curated Claude anchor + streamSimple wired to the real streamShellAcp backend
|
|
@@ -232,7 +235,7 @@ Code-level invariants pinned at the same time:
|
|
|
232
235
|
## Runtime Dependencies
|
|
233
236
|
|
|
234
237
|
- `@modelcontextprotocol/sdk` and `zod` are the substrate runtime deps. With the Claude-first ACP plugin shipped, the Claude/ACP backend deps are pinned alongside them: `@agentclientprotocol/claude-agent-acp` (`0.54.1`), `@agentclientprotocol/sdk` (`1.1.0`), `@anthropic-ai/sdk` (`0.100.1`). Codex/Gemini ACP packages stay out of scope; Codex is native/probe, agy is the shipped native-push Google lane, and Gemini ACP remains compatibility history rather than a current target.
|
|
235
|
-
- `pi` (`@earendil-works/pi-ai`) on PATH at the pinned range (`>= 0.80.
|
|
238
|
+
- `pi` (`@earendil-works/pi-ai`) on PATH at the pinned range (`>= 0.80.7 < 0.81` — devDep exact `0.80.7` + next-minor ceiling). Mismatches are caught by `check-dep-versions` / `check-pi-runtime-version`. 0.80 moved the standalone root `getModels()` to the deprecated `@earendil-works/pi-ai/compat` entrypoint; the curated Claude surface (`pi-extensions/lib/acp/models.ts`) imports `getModels` from `/compat` — the single subpath allowlisted in `check-pi-import-surface`. NOT the 0.80 provider-factory `providers/anthropic` subpath: although it typechecks, pi's extension loader (jiti alias map in pi-coding-agent `core/extensions/loader.ts`) resolves only the bare root, `/compat`, and `/oauth` for extensions — a `providers/*` import resolves to the unresolvable `dist/compat.js/providers/…` and crashes extension load (caught live by `smoke-resident-garden-guard`, not by static typecheck). This `/compat` use is an **extension-loader compatibility shim** chosen by loader constraint, not a preference for a deprecated API — the `<0.81` ceiling guards it; when 0.81 changes `compat` or the loader alias map, re-evaluate against whatever root/loader surface 0.81 then exposes.
|
|
236
239
|
|
|
237
240
|
## Working Style
|
|
238
241
|
|
package/BASELINE.md
CHANGED
|
@@ -13,6 +13,28 @@ baseline below instead of being forced into Claude's overlay questions. Codex
|
|
|
13
13
|
(pi-native / delivery probe) and Gemini (historical non-goal ACP probe) remain
|
|
14
14
|
reference axes, not the shipped ACP baseline.
|
|
15
15
|
|
|
16
|
+
## Release-host baseline — #51 repair cut
|
|
17
|
+
|
|
18
|
+
This table is the operator-facing support/certification view. It complements the
|
|
19
|
+
model interview below; a persuasive answer from the model cannot turn an unmeasured
|
|
20
|
+
host into a certified one.
|
|
21
|
+
|
|
22
|
+
| Surface | Automated artifact evidence | Direct/native evidence | Current verdict |
|
|
23
|
+
|---|---|---|---|
|
|
24
|
+
| Node 24 Linux package consumer | Required `artifact-consumer` CI: read-only candidate `.tgz`, checkout-invisible, non-root global install, PATH shims, frozen package root, path+sha256 regular-file fence, strict doctor fixture | None required for the package layout itself | Package-consumer shape verified. The planted Claude cache/owner/bridge are synthetic and prove no real Claude lifecycle. |
|
|
25
|
+
| Claude Code 2.1.217 exec form | `check-hook-launch-topology` + `check-claude-floor-coherence`; doctor oracle healthy fixture + 21 defect mutations | B2 actual Claude session on NixOS: args per element, literal `${HOME}`, direct parent, FileChanged exit 2 → idle wake | Runtime behavior verified at 2.1.217 on one host; this is the supported floor. |
|
|
26
|
+
| Claude Code 2.1.138 negative | Launcher empty-argv refusal + installer/doctor floor checks | B actual Claude session on NixOS: args discarded while runtime reported success | Unsupported; no shell-form fallback. |
|
|
27
|
+
| Maintainer NixOS installed package | Gates and B/B2 are green, but the installed artifact is intentionally stale before release | Post-release clean reinstall → new Claude session → installed doctor exit 0 **pending** | Not yet host-certified for the repair artifact. |
|
|
28
|
+
| hejdev6 Ubuntu installed package | Linux artifact-consumer gate models the package shape, not this machine | Post-release clean reinstall → new Claude session → installed doctor exit 0 **pending** | Recovery remains open; hand-patched hooks and validate output are not acceptance. |
|
|
29
|
+
| macOS Claude meta-bridge | No artifact-consumer job and no `/proc` live join | None | **Not yet verified/certified for this repair cut.** Installer refuses Darwin and doctor remains nonzero; uninstall permits Darwin to remove older managed state. This is not permanent—future native validation may reopen it, and package-level `os` stays unrestricted. |
|
|
30
|
+
| WSL2 / Windows | None | None | Unverified / outside this repair cut. |
|
|
31
|
+
|
|
32
|
+
**Operator acceptance rule:** on a claimed Claude host, reinstall from the released
|
|
33
|
+
artifact, restart every old Claude process, open a new session, and run the doctor
|
|
34
|
+
from that installed package. PASS means exit 0 with the live MCP↔sender↔receiver
|
|
35
|
+
join. `plugin validate`, a hand-inspected marker, or a synthetic fixture cannot
|
|
36
|
+
supersede doctor RED.
|
|
37
|
+
|
|
16
38
|
## How to use
|
|
17
39
|
|
|
18
40
|
Each question carries a **stable ID** so a future operator can spot a
|
|
@@ -252,6 +274,24 @@ prompt, and if so quote the visible text exactly:
|
|
|
252
274
|
|
|
253
275
|
# HISTORY (pointer)
|
|
254
276
|
|
|
277
|
+
2026-07-22 repair evidence: Linux artifact-consumer C is committed locally as
|
|
278
|
+
`328c66e` (not yet pushed at the time of this baseline update); B/B2 direct-native
|
|
279
|
+
observations and the exec-only production cut are documented in issue #51 and
|
|
280
|
+
VERIFY's host matrix. **Post-provenance C was re-proven rather than inheriting the
|
|
281
|
+
earlier green:** the first rerun correctly went RED because its stand-in Claude was
|
|
282
|
+
container PID 1, which the product rejects as an impossible/reparented owner. The
|
|
283
|
+
fixture now keeps an outer PID-1 shell and runs the consumer as pid 8; both default
|
|
284
|
+
pack-once and caller-preserved exact-tgz modes reached doctor PASS with marker
|
|
285
|
+
`ownerPid=8 (>1)` and identical artifact sha256. The preserved file's
|
|
286
|
+
inode/size/mtime/sha tuple was unchanged across acceptance. Evidence logs:
|
|
287
|
+
`/tmp/pi-tmux-entwurf-exact-final.log` and
|
|
288
|
+
`/tmp/pi-tmux-entwurf-default-final.log`; the digest belongs in the external cut log,
|
|
289
|
+
not inside this shipped file (embedding it would mutate the tarball it names).
|
|
290
|
+
This was the current `0.12.7-1` gate candidate, **not** the approved release artifact;
|
|
291
|
+
repeat exact mode after the separate `0.12.8-repair.0` version commit. Maintainer/
|
|
292
|
+
hejdev6 installed doctor GREEN remains deliberately pending until after release.
|
|
293
|
+
|
|
294
|
+
|
|
255
295
|
Per-release baselines — the 0.9.0 garden-native identity cut (17 PASS / 0 FAIL /
|
|
256
296
|
0 SKIP `/gnew`-inclusive gate, #28), and the older 0.8.x / 0.5.0 context-pressure
|
|
257
297
|
baselines — live in **CHANGELOG.md and git history**, including the gate names of
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,30 @@ All notable changes to this project will be documented here. Format follows [Kee
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 0.12.8-repair.0 — 2026-07-22
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **ACTION REQUIRED — reinstall the Claude meta-bridge and restart every already-open Claude Code session after upgrading to this release.** The old hook keyed sender and receiver liveness to `process.ppid`; on an observed installed host Claude retained a `/bin/bash -c` wrapper, so all markers were written under short-lived shell pids while the MCP child looked under the still-live Claude pid. The same Claude Code version also produced a direct join elsewhere and ordinary tail-exec tests never reproduced the trigger — so rather than keep fighting for a portable topology inside the shell form, **this release leaves the shell form entirely**. Every meta-bridge hook is now declared in Claude's **exec form**: `command` is a shipped `hook-launch.sh` and `args` is the real argv, so no shell is on the launch path at all. The launcher `exec`s its argv, which preserves the pid, and the hook's parent is therefore the Claude process itself on every host — structurally, not conditionally. Measured at Claude Code 2.1.217 (#51 B2): `args` elements arrive verbatim, a literal `${HOME}` is never expanded, and a `FileChanged` `asyncRewake` hook exiting 2 really does wake an idle session. The shell `$PPID` carrier, its ancestry walk and its missing-carrier contract are **removed**; the hook reads `process.ppid` directly. **entwurf now requires Claude Code `>=2.1.217` and enforces that floor itself, because upstream gives no fail-loud:** an older Claude passes `claude plugin validate` on the exec manifest and then, at runtime, DROPS the `args` array, runs `command` alone, and reports the hook as `exit_code: 0, outcome: success` — measured at 2.1.138. There is no shell-form fallback lane. `install-meta-bridge` and `doctor-meta-bridge` refuse an older version outright, and `hook-launch.sh` refuses an empty argv so that silence becomes a visible error on the host where it happens. **Upgrade mismatch stays deliberately fail-closed.** An already-open session still holding the OLD cached command reaches the new hook without the launcher, so the launcher stamps an explicit exec-launch provenance token and the hook mints NO sender/receiver marker without it: the meta-record still lands, but a session with no trusted send identity or deliverability evidence never claims one, and any older marker still on disk is not proof that the upgraded session joined correctly. Run `entwurf install-meta-bridge`, restart all existing Claude sessions so they load the new manifest, then run `entwurf doctor-meta-bridge`. The doctor refuses a shell-form or launcher-less manifest **by name**, requires the installed manifest to equal the shipped template modulo the two baked values, execs the installed argv directly (no shell) for a synthetic owner join, and on Linux requires live MCP↔sender↔receiver agreement — **missing live evidence is `NOT CERTIFIED` and a nonzero exit, not a WARN**. Linux is the only certified axis for this repair cut: `install-meta-bridge` refuses macOS because its live bridge cannot yet be discovered without `/proc`, doctor reports `NOT CERTIFIED`/nonzero there, and `uninstall-meta-bridge` alone keeps Darwin support so an older install can still be removed honestly. This is not a permanent impossibility claim; future native validation may reopen the macOS lane. New gates: `check-hook-launch-topology` drives the shipped argv for real, including a plugin path containing a space, `$`, a backtick and `;&`, and asserts the upgrade-mismatch fail-closed; `check-claude-floor-coherence` keeps the floor a single number derived from `package.json` `entwurf.claudeCodeFloor`.
|
|
12
|
+
- **The install gate pinned three of the four packages that make up the pi runtime, so it verified a runtime nobody verified.** `check-pack-install` builds a fresh temp project with no lockfile and pinned `pi-ai` / `pi-coding-agent` / `pi-tui` at the declared `0.80.7` — but `pi-coding-agent` depends on `@earendil-works/pi-agent-core` by CARET, so that fourth package floated to whatever pi published last and then dragged a NESTED `pi-ai` of its own. Measured 2026-07-21: the tree held `pi-agent-core@0.80.10` + `pi-ai@0.80.10` while the gate still printed `pinned pi 0.80.7`. Only the operator's stale pnpm metadata cache had been hiding it — refreshing the cache does not unblock the gate so much as let the unverified runtime in, which is why the earlier "npm registry partial publish" reading was wrong on both counts (`@aws-sdk/token-providers@3.1088.0` and `@earendil-works/pi-*@0.80.10` are both published; the local cache simply had not seen them). The gate now pins every `@earendil-works` package that constitutes the runtime **and reads the resolved tree back**, failing loud if any of them is not `0.80.7` — a pin is a wish until something asserts it.
|
|
13
|
+
- **A gate that cannot name which pi it proved has proved nothing.** `check-pack-install`'s loader and foreign-cwd smokes resolved `pi` through the **host PATH**, so the release commit's `install-surface` job went RED on a CI runner that carries no global pi — and the mirror image was worse: the gate had been green locally only because the dev box happened to carry a *newer* global pi (0.80.6) than the repo pinned (0.80.3). It was never driving the runtime it declares. Both smokes now drive `$tmp/node_modules/.bin/pi` — the pinned peer this gate already installs next to the tarball — and **assert `--version` against the `package.json` devDep** before proving anything with it. Review caught the fix reintroducing the very coupling it removes: the `--version` probe itself ran unsandboxed, and **pi reads settings before it prints its version** (`bootstrapSettingsManager` precedes the `--version` branch in pi's `main`), so the probe opened the operator's real `~/.pi/agent/settings.json` — 1 access before, 0 after (strace-verified). Every pi invocation in the gate now runs under one throwaway `HOME`/XDG/agent-dir env array. The rule this leaves behind: **a gate may not READ the operator's global install any more than it may WRITE it.** `AGENTS.md` rule 11 forbade only the write half, and the read half is the more insidious one — locally it is always green, so without CI it never surfaces.
|
|
14
|
+
- **The runtime-floor gate carried a pin that no gate enforced, and verified only half the range it declared.** `check-pi-runtime-version` held `const FLOOR = '0.80.3'` as a hand-kept literal that `check-dep-versions` had never seen: its assertions bind the devDeps to the peer range and to the `check-pack-install` peer-install pins, but nothing bound this constant, so a bump that forgot it would leave the runtime gate still blessing the OLD floor while every other pin moved. The floor is now **derived from the `package.json` devDep** (an exact `x.y.z` pin is required, or the gate fails loud): one pin to move, and the diagnostic names the real floor instead of a frozen string. The check is also **closed at the top** now, matching the range we actually declare (`>=<devDep> <0.<minor+1>`) — a floor-only comparison blessed any *future* pi, and "the runtime moved past the range while every gate stayed green" is this cut's entire subject. Mutation-checked in both directions: devDep `0.80.9` against the installed 0.80.6 fails the floor; devDep `0.79.9` (ceiling `0.80.0`) fails the ceiling.
|
|
15
|
+
- **`check-dep-versions` outlived its own doc coverage and kept advertising it.** The gate was born reading a doc: 362becd added it after the 21de0f9 pin drift (package bumped to 0.12.0, README left at 0.11.1) and asserted README's codex-acp install pin against `package.json`. bf4a533 then dropped the openclaw/ACP lane and removed that assertion with it — while leaving the coverage claim standing in the usage line and the function comment. So the doc half of the promise has been prose ever since, and the pi baseline docs were never bound to the gate at all. The pi pin lives in five of them, this bump touched all five, and what kept `demo/README.md` from being left behind was a hand-grep, not a gate. The docs are now **in** the gate: every closed-range declaration (`>=<floor> <0.<ceiling>`), every exact install pin (`@earendil-works/pi-<pkg>@<version>`), and four prose declarations that carry the pin in sentences those patterns cannot see (`current floor …`, `pi … fence`, `floor = **…**`, ``devDep exact `…` ``) must equal the devDep pin. A missing prose anchor fails loud rather than passing vacuously — a reworded sentence may not quietly drop the pin out of the gate — and a floor on the match counts guards against the whole doc scan silently matching nothing. History (`CHANGELOG` / `NEXT`) keeps its old versions; only sentences that *declare* the current pin are in scope. Mutation-checked: reverting `demo/README.md` alone to the old floor exits 1.
|
|
16
|
+
- **An unidentifiable Claude version could kill the installer/doctor before either printed its intended diagnosis.** The shared detector used `claude --version | head | grep | head` under the callers' inherited `set -euo pipefail`, and both callers captured it in a bare assignment. A nonzero CLI or output with no dotted triple therefore exited at the assignment instead of reaching the explicit install refusal / `NOT CERTIFIED` branch; a verbose writer also retained the known early-reader SIGPIPE shape. The detector now consumes the complete output, returns success-with-empty on failed/unparseable probes, and lets each caller own the diagnosis. `check-claude-floor-coherence` drives normal multi-line, unparseable, nonzero, and 128-KiB long-writer fixtures under `set -euo pipefail`, then invokes the real installer and doctor to prove they reach their own install-refusal / `NOT CERTIFIED` branches (and the doctor's final FAIL summary).
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- **A required Linux artifact-consumer CI lane now tests the package in a world that has never seen the checkout.** `check-install-container` makes one candidate tarball on the host, mounts only that file read-only into a Node-24 Linux container, installs globally as a non-root user through an isolated prefix, resolves all five bins through PATH, freezes the package root, boots MCP `tools/list`, and runs `install-meta-bridge` plus the strict doctor under a regular-file path+sha256 fence. The output records canonical artifact path + sha256 and container image id/repository digest. Default CI keeps its pack-once temp mode; release acceptance may set `ENTWURF_CANDIDATE_TGZ` to a caller-preserved `npm pack` output, in which case the gate verifies name/version and consumes that exact file without chmod/copy/re-pack so the accepted bytes can be passed directly to `npm publish <same.tgz> --tag repair`. Its fake Claude CLI, planted plugin cache, stand-in owner, and `/proc` bridge are labelled fixtures: this is L3 package/oracle evidence, not proof that a real Claude process installed the plugin or woke. Direct runtime evidence remains #51 B/B2 (actual 2.1.138/2.1.217 sessions on one NixOS host), and a production host is accepted only by the installed doctor against a new live session. The post-provenance rerun caught one fixture lie before final acceptance: the container runner itself was PID 1, so the product correctly refused it as an impossible owner. C now keeps an outer PID-1 shell and runs the stand-in Claude below it; both default and exact-artifact modes assert marker owner `>1`, reached doctor PASS, and consumed byte-identical candidates (recorded in BASELINE). The earlier pre-provenance green is not reused.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- **Release-preparation evidence:** the pre-version landing HEAD `8f566e01dc9c4510f7485d08bcdf370eb364a614` passed the exact-SHA GitHub Actions run [29899948565](https://github.com/junghan0611/entwurf/actions/runs/29899948565) with `check`, `install-surface`, and `artifact-consumer` all green. After the `0.12.8-repair.0` version change, `pnpm check` passed and `LIVE=1 ./run.sh release-gate /tmp/entwurf-release-gate-0.12.8-repair.0.LMTFbr` completed with `MUST: PASS=17 FAIL=0 SKIP=0` and advisory `BEHAVIOR: PASS=1 FAIL=0`; full log: `/tmp/entwurf-release-gate-0.12.8-repair.0.LMTFbr/release-gate.log`. The final preserved tarball, second exact-SHA CI, and artifact acceptance remain deliberately deferred to `make`.
|
|
25
|
+
- **Release operation is now one repo-local Agent Skill shared by Claude Code and pi, instead of two pi-only prompt files.** `.claude/skills/entwurf-release/SKILL.md` owns four explicit authority modes: `land` pushes a reviewed pre-version HEAD and requires its exact-SHA CI; `prepare` edits, gates, and commits without pushing; `make` pushes the prepared HEAD, requires the second exact-SHA CI, accepts one preserved candidate, and only then tags and creates the GitHub release; `publish` alone may send those accepted bytes to an explicitly named npm dist-tag. The sibling `verify-exact-ci.sh` oracle binds both CI checkpoints to the requested `headSha` and all three required jobs (`check`, `install-surface`, `artifact-consumer`) instead of trusting a branch badge. `.pi/settings.json` points pi at the same project skill directory, and the former `.pi/prompts/prepare-release.md` / `make-release.md` copies are removed, so the two harnesses no longer see different release hands. The shared version contract accepts ordinary SemVer prereleases such as `0.12.8-repair.0` (still no leading `v`).
|
|
26
|
+
- **The pi runtime pin moves 0.80.6 → 0.80.7 — a fix+minor release that carries none of 0.80.6's anchor risk.** Where 0.80.6 shrank the Anthropic catalog 24→14 and put `curatedClaudeModels()`'s `claude-opus-4-8` anchor one dropped row from a crash, 0.80.7 leaves `anthropic.models.ts` **byte-identical** — the anchor and `claude-sonnet-5` both survive untouched (source-diffed against `pi-mono v0.80.6..v0.80.7`). The `getModels` slice this repo consumes via `/compat` is unchanged; `compat.ts` also adds an `AMBIENT_AUTH_MARKER` ambient-auth path in `withEnvApiKey()`, but that behavior change never reaches the catalog/getModels surface entwurf imports, so it is irrelevant to this path. The extension loader's jiti alias map (`root` / `/compat` / `/oauth`) is byte-identical, so the `/compat` exception holds. The one breaking change — `OpenAIResponsesCompat.sendSessionIdHeader` replaced by `sessionAffinityFormat` — is referenced zero times in this repo (grep-verified), as are `supportsToolReferences` and the deferred-tool `addedToolNames`. `package-manager`'s new `--legacy-peer-deps` is on the npm uninstall path only (`packageManagerName !== "pnpm"`), so entwurf's pnpm install/uninstall is byte-identical; the dynamic-tool `wrapper.ts` change is a no-op for a static tool set (zero `getActiveTools` deltas mid-execution); `agent-session.ts` is a private `_getCompactionRequestAuth` → `_getSummarizationRequestAuth` rename (an ambient-auth branch-summary fix). `system-prompt.ts` drops the `Current date:` line for a prompt-cache win — safe here because entwurf's code and contract consume no default prompt date. devDeps / peer range (`>=0.80.7 <0.81`) / the `check-pack-install` peer pins / the five baseline docs move together under `check-dep-versions`, and the lockfile resolved with no transitive change beyond the four pi packages. `pnpm-workspace.yaml` gains four `minimumReleaseAgeExclude` entries that pnpm 11.9 records automatically for the <24h-old 0.80.7 pins.
|
|
27
|
+
- **The pi runtime pin moves 0.80.3 → 0.80.6, so the runtime we declare is the runtime that exists.** The global pi had already moved to 0.80.6 while the repo still pinned 0.80.3; that gap is what made the `check-pack-install` fix above matter, and closing it is the other half of the same repair. devDeps (`pi-ai` / `pi-coding-agent` / `pi-tui`), the peer range (`>=0.80.6 <0.81`), the `check-pack-install` peer-install pins, and the baseline statements in `AGENTS.md` / `README.md` / `ROADMAP.md` / `docs/setup-clean-host.md` / `demo/README.md` all move together, and `check-dep-versions` now fails if any one of them lags. The peer floor rises with the devDep on purpose: keeping `>=0.80.3` would declare support for a version no gate verifies. The lockfile resolved with no transitive change beyond the four pi packages (`pi-agent-core` is coding-agent's own dependency, not a new pin of ours).
|
|
28
|
+
- **What only the gates could answer, they answered.** 0.80.4 reworked `package-manager` (an `autoload:false` package delta plus a dedupe rewrite), `settings-manager`, and `resource-loader` — the exact three files our install surface stands on, and typecheck can say nothing about any of it. Under a **deliberately failing fake `pi` planted first on PATH** (stronger than merely unsetting the global one: an unqualified `pi` call exits 97 instead of silently working), `check-pack-install` drove the pinned 0.80.6 out of the install-smoke tree and kept both regressions green — the npm-managed install writing settings through a hoisted dep, and the user-scope citizen loading from a foreign cwd.
|
|
29
|
+
- **The Anthropic catalog SHRANK in 0.80.6, and that is a risk no type could have shown.** The registry drops ten legacy rows (`claude-3-*`, `claude-opus-4-0`, `claude-sonnet-4-0`, …), 24 model ids down to 14. `curatedClaudeModels()` calls `requireRegistryModel` on `claude-opus-4-8` and **crashes rather than fabricating a row** if the anchor is gone, so a bump that dropped it would have taken the provider surface down at extension load. Both curated rows survive with byte-identical `cost` / `contextWindow` / `maxTokens`. The only metadata added is `thinkingLevelMap` — Opus 4.8 goes `{xhigh}` → `{xhigh, max}` and Sonnet 5 gains the map outright (it had none) — and the curated rows copy neither, so the registered surface is unchanged. `ThinkingLevel` likewise gains `"max"` (a union widening this repo never consumes: it holds no exhaustive map over the type), and `cost` becomes the `ModelCost` superset with optional `tiers[]`, which passes through untouched because the curated surface copies `cost` wholesale and the provider gate asserts field presence, not an exact key set. Wiring thinking/effort remains a separate lane (#49 D), not a consequence of the bump.
|
|
30
|
+
|
|
7
31
|
## 0.12.7 — 2026-07-14
|
|
8
32
|
|
|
9
33
|
### Fixed
|
package/DELIVERY.md
CHANGED
|
@@ -95,7 +95,7 @@ When a level is **not applicable** or **conditional**, say so explicitly. For
|
|
|
95
95
|
example, Codex app-server delivery is conditional on a loaded thread and control
|
|
96
96
|
socket; direct Codex TUI is a different surface.
|
|
97
97
|
|
|
98
|
-
## Current capability matrix (2026-07-
|
|
98
|
+
## Current capability matrix (2026-07-22)
|
|
99
99
|
|
|
100
100
|
This matrix is a snapshot of what the raw probes have established. It should be
|
|
101
101
|
updated when a backend version changes the delivery surface.
|
|
@@ -110,7 +110,7 @@ the `D0–D8` capability level:
|
|
|
110
110
|
| Harness / surface | Status | Highest current level | Transport | Notes |
|
|
111
111
|
|---|---|---:|---|---|
|
|
112
112
|
| **pi native Entwurf** | shipped | D7+ | Unix control socket + pi followUp/custom messages | Replyable pi session. This is the resident baseline, not an external meta-session. `entwurf_v2` treats a record-less but live pi control socket as a socket-only `fire-and-forget` target; record-less *dormant* resume is intentionally not claimed. |
|
|
113
|
-
| **Claude Code interactive 2.1.
|
|
113
|
+
| **Claude Code interactive >=2.1.217** | shipped *(Linux is the only certified axis in this repair cut)* | D6, D7 partial, D8 partial | Exec-form global plugin: `SessionStart` arms `watchPaths`; external write triggers exec-form `FileChanged`; `asyncRewake` wakes idle session | B2 direct-native at 2.1.217 on one NixOS host proved per-element argv, no shell expansion, parent join, and exit-2 idle wake. B at 2.1.138 proved the negative: `args` discarded while Claude reported success, so installer/doctor enforce 2.1.217 and there is no shell fallback. The launcher provenance token keeps an old cached command fail-closed. Active idle wake is D6; D7/D8 remain partial as before. The Linux container's planted cache/owner/bridge are fixtures, not a second native-host proof. |
|
|
114
114
|
| **Antigravity / agy** | shipped | D6, D7 partial | Native LS gRPC `agentapi send-message` (native-push) | `PreInvocation` automatically births/attaches by native `conversationId` and writes the record-backed pid/start-key sender marker; `entwurf_v2` fire-and-forget probes and direct-injects through the antigravity adapter with a one-shot re-probe retry. Three managed adapters own MCP+one exact permission, statusline, and hook separately. `entwurf_register_native` remains an explicit/manual fallback, not the normal birth path. Live sender→sibling→same-gid reply passed on 2026-07-13, re-verified at **agy 1.1.0** on 2026-07-14 (13/13 LIVE checks); D7 stays partial because there is no canonical transcript/content receipt owned by the smoke. |
|
|
115
115
|
| **Codex app-server-backed TUI 0.136.0** | verified-probe | D6, D7 (status) | WebSocket-over-UDS `turn/start` into the live `threadId` | **Demonstrated, no managed standalone, no cloud.** `codex app-server --listen unix://<owned 0700 dir>` + plain `codex` auto-attach (or `--remote unix://`). Full message injection (agy-like, not a doorbell); `thread/status/changed` gives completion observation. D8 robustness (dedupe / crash recovery / ordering policy) is not tested. `turn/steer` is active-turn steering, not idle wake. |
|
|
116
116
|
| **Codex embedded TUI 0.136.0** | deferred | D0 partial | Native state DB / rollout transcript only | Standalone Embedded TUI binds no socket; no `FileChanged`/`asyncRewake` in Codex hooks; not retrofittable. Identify-only via state DB / rollout. |
|
|
@@ -121,6 +121,24 @@ the `D0–D8` capability level:
|
|
|
121
121
|
|
|
122
122
|
### Claude Code — filesystem event wake, not socket push
|
|
123
123
|
|
|
124
|
+
The current launch contract is **exec-only at Claude Code >=2.1.217**. All four
|
|
125
|
+
hook leaves run the shipped `hook-launch.sh` as `command` with the real argv in
|
|
126
|
+
`args`; the launcher stamps non-identity launch provenance and `exec`s the payload.
|
|
127
|
+
A hook reached through an old cached shell command still mints its record but writes
|
|
128
|
+
no sender/receiver marker, so an upgrade mismatch is fail-closed. Reinstall the
|
|
129
|
+
meta-bridge and restart all old Claude sessions before judging delivery.
|
|
130
|
+
|
|
131
|
+
Evidence boundary: B/B2 were real Claude sessions and therefore direct-native
|
|
132
|
+
runtime evidence, but both ran on one NixOS host. `check-hook-launch-topology` is a
|
|
133
|
+
deterministic execution proof of the shipped argv; `check-install-container` uses a
|
|
134
|
+
fake Claude, planted plugin cache, stand-in owner, and fake live bridge. Those fixtures
|
|
135
|
+
prove package/oracle behavior, not actual native session wake. A claimed Linux host
|
|
136
|
+
is accepted only when its **installed** strict doctor sees the live owner join and
|
|
137
|
+
exits 0; missing evidence is `NOT CERTIFIED`, not a partial delivery PASS. macOS is
|
|
138
|
+
not yet verified/certified for this repair cut: install refuses Darwin, doctor stays
|
|
139
|
+
nonzero, and only the uninstaller keeps Darwin support so an older managed install
|
|
140
|
+
can be removed. Future native validation may reopen that lane.
|
|
141
|
+
|
|
124
142
|
A missing local listening socket does **not** imply idle wake is impossible.
|
|
125
143
|
Claude Code interactive can be woken by a supported filesystem-event path:
|
|
126
144
|
|
package/README.md
CHANGED
|
@@ -159,7 +159,7 @@ because Node refuses to strip `.ts` files under `node_modules`.
|
|
|
159
159
|
### Pi adapter / ACP plugin lane
|
|
160
160
|
|
|
161
161
|
To use the `entwurf` provider inside pi, install a compatible pi binary
|
|
162
|
-
separately (`@earendil-works/pi-coding-agent >=0.80.
|
|
162
|
+
separately (`@earendil-works/pi-coding-agent >=0.80.7 <0.81`). Then point pi at
|
|
163
163
|
the npm-installed package or development clone:
|
|
164
164
|
|
|
165
165
|
```bash
|
|
@@ -188,19 +188,49 @@ claude mcp add --scope user entwurf-bridge \
|
|
|
188
188
|
|
|
189
189
|
If the host does not inherit the npm bin directory, use an absolute path to the
|
|
190
190
|
bin or `start.sh`. For a garden-native Claude Code meta-session (replyable by
|
|
191
|
-
garden id), run
|
|
191
|
+
garden id), run this on Linux. The #51 repair cut refuses new macOS meta-bridge
|
|
192
|
+
installs because its strict live-owner doctor currently depends on `/proc`; macOS
|
|
193
|
+
is **not yet verified/certified for this cut**, not permanently impossible, and
|
|
194
|
+
future native validation may reopen it. Package-level `os` is intentionally
|
|
195
|
+
unrestricted, and Darwin uninstall remains available for legacy cleanup.
|
|
192
196
|
|
|
193
197
|
```bash
|
|
194
198
|
entwurf install-meta-bridge
|
|
195
199
|
entwurf doctor-meta-bridge
|
|
196
200
|
```
|
|
197
201
|
|
|
202
|
+
> **Upgrade action:** after installing a package that moves the hook launch form, run `entwurf install-meta-bridge` and restart **every already-open Claude Code session** before trusting send/receive. A new hook reached through an old cached command fails closed: it may still mint a garden record, but the owner join it depends on is not the one the old command produces. Reinstall materializes the matching manifest; restart makes live Claude processes load it. This release moves to the exec form and requires Claude Code `>=2.1.217`; `install-meta-bridge` and `doctor-meta-bridge` refuse anything older outright, because an older Claude drops the hook's `args` silently and still reports success.
|
|
203
|
+
|
|
198
204
|
On npm/pnpm-installed packages, `doctor-meta-bridge` must use prebuilt JS for its
|
|
199
205
|
store scan and defer repo-only source-shape gates; Node refuses strip-types for
|
|
200
|
-
raw `.ts` helpers under `node_modules`.
|
|
206
|
+
raw `.ts` helpers under `node_modules`. It also refuses any Claude Code below the
|
|
207
|
+
supported floor `>=2.1.217` (an older one silently drops the hook's `args` and still
|
|
208
|
+
reports success, so nothing else in the output could be trusted), checks Claude's
|
|
209
|
+
installed hooks are the exec form through the shipped `hook-launch.sh`, and on Linux
|
|
210
|
+
verifies every live Claude MCP process joins to live sender/receiver markers.
|
|
211
|
+
`launch form is UNSUPPORTED` means reinstall the meta-bridge; a live-owner-join failure after
|
|
212
|
+
that means restart the affected Claude session so it loads the new manifest. If no
|
|
213
|
+
matching MCP child exists the doctor reports `NOT CERTIFIED` and **exits nonzero** — a
|
|
214
|
+
host whose live tier could not be measured is not a certified host, and that is worded
|
|
215
|
+
differently from a broken install on purpose. If the doctor reports
|
|
201
216
|
`ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING`, reinstall a current package before
|
|
202
217
|
trusting the floor result.
|
|
203
218
|
|
|
219
|
+
**Repair-cut evidence boundary.** The required Linux `artifact-consumer` CI job
|
|
220
|
+
installs one read-only candidate tarball globally as a non-root user in a Node 24
|
|
221
|
+
container that cannot see the checkout, records the tarball digest and image
|
|
222
|
+
identity, freezes the package root, and drives the strict doctor. Its Claude cache,
|
|
223
|
+
owner process, and live bridge are deliberately synthetic fixtures; that job proves
|
|
224
|
+
the package-consumer/oracle shape, not a real Claude lifecycle. The direct B/B2
|
|
225
|
+
runtime evidence came from actual Claude 2.1.138/2.1.217 sessions on one NixOS host.
|
|
226
|
+
A production host is certified only when a **new session using the installed
|
|
227
|
+
artifact** makes `doctor-meta-bridge` exit 0 with the live join. See the explicit
|
|
228
|
+
support matrix and release order in [VERIFY.md](./VERIFY.md). For the release
|
|
229
|
+
artifact, first preserve one `npm pack` output, then run
|
|
230
|
+
`ENTWURF_CANDIDATE_TGZ=/absolute/path/to/candidate.tgz ./run.sh check-install-container`;
|
|
231
|
+
the gate prints that canonical path and sha256 and consumes it without re-packing.
|
|
232
|
+
Only that accepted file may be published with `--tag repair`.
|
|
233
|
+
|
|
204
234
|
After upgrading a globally installed package, reinstall the native-harness surface you use before trusting it:
|
|
205
235
|
|
|
206
236
|
```bash
|
|
@@ -221,7 +251,7 @@ For manual configuration, [`pi/settings.reference.json`](./pi/settings.reference
|
|
|
221
251
|
shows the pi adapter settings shape, and the external-host examples below show
|
|
222
252
|
plain MCP registrations.
|
|
223
253
|
|
|
224
|
-
> **First time on a clean Ubuntu / Debian /
|
|
254
|
+
> **First time on a clean Linux host (Ubuntu / Debian / NixOS)?** See the [clean-host walk-through](./docs/setup-clean-host.md) — Node/npm install, auth-free bridge boot, optional pi adapter verification, and authenticated runtime smokes. The neutral package may install elsewhere, but Linux is this repair cut's only currently certified Claude meta-bridge axis: its installer refuses macOS and its doctor remains `NOT CERTIFIED`/nonzero because the live owner join is not yet instrumented. Future native validation may reopen the macOS lane.
|
|
225
255
|
|
|
226
256
|
> **Post-install checks.** `entwurf check-bridge` (or `./run.sh check-bridge` from a clone) proves the `entwurf-bridge` MCP surface loads with no backend auth needed. To prove the **ACP backend actually answers** — the bridge spawns Claude through the pi provider path and a real turn comes back — run `LIVE=1 entwurf smoke-acp-provider-live` from an installed package/clone with pi and Claude auth available. Package-source routing is pinned deterministically by `run.sh check-package-source-routing`, which runs inside `pnpm check` and the release gate.
|
|
227
257
|
|
package/VERIFY.md
CHANGED
|
@@ -43,6 +43,71 @@ Verification here is not a benchmark. In production we exchange short turns and
|
|
|
43
43
|
>
|
|
44
44
|
> The authoritative per-cut counts live in BASELINE.md's HISTORY and CHANGELOG/git, not inline here (they drift against `run.sh`). Most recent recorded aggregate floor: **2026-06-27 — MUST 17/0/0 + BEHAVIOR 1/0**.
|
|
45
45
|
|
|
46
|
+
### Artifact / host certification matrix — #51 repair cut
|
|
47
|
+
|
|
48
|
+
Do not collapse package evidence, fixture evidence, and a certified native host into
|
|
49
|
+
one word such as “green.” They answer different questions.
|
|
50
|
+
|
|
51
|
+
| Axis | Current evidence | Level / limit | Release reading |
|
|
52
|
+
|---|---|---|---|
|
|
53
|
+
| Source checkout | `pnpm check` on Node 24 Linux | Deterministic source floor; not an installed artifact | Necessary; cannot certify a consumer install. |
|
|
54
|
+
| Project-local tarball | `check-pack-install` | Real `.tgz`, but checkout-visible, operator-owned, project-local | Installed-shape evidence; still maintainer-shaped. |
|
|
55
|
+
| Linux artifact consumer | `check-install-container` in the required `artifact-consumer` CI job | **L3 package evidence:** one read-only candidate `.tgz`; checkout/repo `node_modules` invisible; non-root `npm install -g`; PATH shims; frozen package root; regular-file path+sha256 fence; canonical artifact path+sha256 and Node 24 image identity printed. Default CI packs once; `ENTWURF_CANDIDATE_TGZ` consumes a preserved caller artifact without re-pack. | Certifies the Linux package-consumer shape. Its fake Claude, planted plugin cache, stand-in owner, and `/proc` bridge are explicitly **fixtures**: they do not prove Claude installed the cache or that a real native Claude session woke. |
|
|
56
|
+
| Direct Claude negative (B) | Claude Code 2.1.138 actual session | **L4 direct-native**, one NixOS host: fixture loaded (shell canary), `args` dropped, hook reported `exit_code: 0, outcome: success` | Justifies entwurf-side fail-loud and no old-version fallback. It did not run the final production argv shape. |
|
|
57
|
+
| Direct Claude positive (B2) | Claude Code 2.1.217 actual session | **L4 direct-native**, one NixOS host: per-element args, literal `${HOME}`, direct parent join, FileChanged exit 2 → idle wake | Justifies the proven floor and exec-form contract. It is not a second-OS acceptance run. |
|
|
58
|
+
| Linux installed host | `doctor-meta-bridge` after package install, a **new** Claude session, and a live MCP child | **L3 host corroboration:** installed artifact + live `/proc` owner join | Exit 0 is certification. Missing live evidence is `NOT CERTIFIED` and nonzero; static/synthetic success never substitutes. Maintainer and hejdev6 acceptance remains post-release work. |
|
|
59
|
+
| macOS Claude meta-bridge | New install is refused: strict live-owner certification cannot yet discover the MCP process without `/proc` | **Not yet verified/certified for this repair cut**; doctor nonzero | Linux is the only current certified axis. Darwin uninstall remains the honest inverse for older installs; the neutral package has no `os` restriction. This is not a permanent impossibility claim—future native validation may reopen macOS. |
|
|
60
|
+
| WSL2 / Windows | No release lane | Unverified | Not supported by this repair cut. |
|
|
61
|
+
|
|
62
|
+
The artifact-consumer run prints both the tarball sha256 and container image
|
|
63
|
+
identity. Preserve those in the cut record. A synthetic doctor PASS proves the
|
|
64
|
+
oracle can recognize a fully supplied fixture; only the installed doctor against a
|
|
65
|
+
new native session proves that a real host supplied those layers.
|
|
66
|
+
|
|
67
|
+
### Repair-cut order (future execution; authority is mode-specific)
|
|
68
|
+
|
|
69
|
+
The repo-local `entwurf-release` skill is a checkpointed state machine. Each mode
|
|
70
|
+
is a separate authorization; one mode never implies the next.
|
|
71
|
+
|
|
72
|
+
1. Finish and review the atomic production hard-cut + gates + documentation while
|
|
73
|
+
keeping the already-committed artifact-consumer change as its separate commit.
|
|
74
|
+
2. `land 0.12.8-repair.0` pushes only that clean **pre-version landing HEAD** and
|
|
75
|
+
requires a push-triggered `ci.yml` run whose `headSha` is exactly that commit.
|
|
76
|
+
All three jobs must be green: `check`, `install-surface`, and
|
|
77
|
+
`artifact-consumer`. This first run is an isolation/provenance checkpoint; the
|
|
78
|
+
later version-HEAD run also contains the production changes.
|
|
79
|
+
3. `prepare 0.12.8-repair.0` promotes the changelog, sets the package version,
|
|
80
|
+
runs the deterministic and LIVE gates, and creates the release-prep commit. It
|
|
81
|
+
never pushes.
|
|
82
|
+
4. `make 0.12.8-repair.0` pushes that clean prepared HEAD and requires the same
|
|
83
|
+
three jobs on that exact version commit. Only after the second exact-SHA CI is
|
|
84
|
+
green does it preserve and accept one candidate without repacking:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
ARTIFACT_DIR=$(mktemp -d /tmp/entwurf-release-candidate-0.12.8-repair.0.XXXXXX)
|
|
88
|
+
bash scripts/with-dist-lock.sh npm pack --dry-run=false --pack-destination "$ARTIFACT_DIR"
|
|
89
|
+
CANDIDATE="$(realpath "$ARTIFACT_DIR/junghanacs-entwurf-0.12.8-repair.0.tgz")"
|
|
90
|
+
sha256sum "$CANDIDATE"
|
|
91
|
+
ENTWURF_REQUIRE_DOCKER=1 ENTWURF_CANDIDATE_TGZ="$CANDIDATE" \
|
|
92
|
+
./run.sh check-install-container | tee "$ARTIFACT_DIR/acceptance.log"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
The gate must print `candidate mode: caller-preserved exact artifact (no repack)`,
|
|
96
|
+
the same canonical path, the same SHA-256, and the image identity. `make` then
|
|
97
|
+
tags that exact prepared SHA and creates the GitHub release. Keep the candidate
|
|
98
|
+
and acceptance log; do not let a later step silently repack different bytes.
|
|
99
|
+
5. Only an explicit `publish 0.12.8-repair.0 <absolute-candidate> repair`
|
|
100
|
+
invocation may run `npm publish "$CANDIDATE" --tag repair`. It verifies
|
|
101
|
+
`repair=0.12.8-repair.0`, `latest=0.12.7`, and a registry-installed smoke. The
|
|
102
|
+
current `0.12.7-1` is lower than `0.12.7` and must never be published.
|
|
103
|
+
6. Only after publication, clean-reinstall the maintainer and hejdev6 hosts.
|
|
104
|
+
Restart all old Claude sessions, open a new session, then require the
|
|
105
|
+
**installed** `doctor-meta-bridge` to exit 0. A validate result or manual marker
|
|
106
|
+
observation cannot override doctor RED.
|
|
107
|
+
|
|
108
|
+
Invoking `land`, `prepare`, `make`, or `publish` grants only that named mode's
|
|
109
|
+
authority. Host reinstall remains a separate GLG authorization.
|
|
110
|
+
|
|
46
111
|
### Verifying the two capabilities a gate cannot fully judge
|
|
47
112
|
|
|
48
113
|
- **Garden-id delivery:** discover a target with `entwurf_peers`, then `entwurf_v2` with the correct intent — `fire-and-forget` for live pi, mailbox-backed meta, or native-push targets; `owned-outcome` only to wake a dormant record-backed pi citizen. Picking the wrong intent is rejected, never auto-fixed.
|
|
@@ -218,12 +283,14 @@ Pass: user/assistant turns accumulate normally; the transcript is not broken/emp
|
|
|
218
283
|
The minimum passing bar:
|
|
219
284
|
|
|
220
285
|
1. **Deterministic floor green:** `pnpm check` passes (lint + typecheck + the `check-*` gate set + `check-pack`).
|
|
221
|
-
2. **
|
|
222
|
-
3. **
|
|
223
|
-
4. **
|
|
224
|
-
5. **
|
|
225
|
-
6. **
|
|
226
|
-
7. **
|
|
286
|
+
2. **All three CI jobs green on the exact release commit:** `check`, `install-surface`, and the required Linux `artifact-consumer`; preserve the latter's tarball digest and image identity.
|
|
287
|
+
3. **Live floor MUST green:** `LIVE=1 ./run.sh release-gate <dir>` reports `MUST PASS=N FAIL=0 SKIP=0`; a BEHAVIOR FAIL is advisory, not blocking.
|
|
288
|
+
4. **Native-host doctor green where the Claude meta-bridge is claimed:** a new post-install Claude session exists, live evidence is present, and the installed `doctor-meta-bridge` exits 0. `NOT CERTIFIED` is a release failure for that host, not a skip.
|
|
289
|
+
5. **Honest self-recognition:** the bridged model identifies its actual harness/backend, lists `entwurf-bridge` as the single MCP server with its five current tools, and presents a backend-native (not normalized) tool surface.
|
|
290
|
+
6. **Carrier separation honored:** engraving vs pi-context-augment kept distinct (§1A.0); no bridge-identity narrative attributed to the engraving carrier.
|
|
291
|
+
7. **agy shipped lane accepted:** all three agy doctors are green; automatic birth/statusline/sender identity and same-gid native-push reply are confirmed in a fresh conversation. `agentId=meta-session/antigravity` is correct; model display is not part of that contract. Same-pid concurrent conversation invocation is not claimed.
|
|
292
|
+
8. **Boundary preservation across backends/machines:** for every shipped or explicitly probed backend, regardless of install path or host, no cross-backend tool-surface contamination and no confabulation about pi internals.
|
|
293
|
+
9. **Hygiene:** no orphan ACP children; no unexpected persisted session garbage (a turn-scoped `cwd:` fallback is never a persisted reuse).
|
|
227
294
|
|
|
228
295
|
Passing establishes a **release verification floor**, not an 8-hour/day operational guarantee. The floor says: gates hold, the agent honestly recognizes its environment, no tool surface is normalized away, no identity leaks, no orphans. It does **not** say a real-day workload (50–100+ turns, tool bursts, partial MCP failures, auth/version drift) survives — that needs L3–L5 evidence (appendix).
|
|
229
296
|
|
package/demo/README.md
CHANGED
|
@@ -137,7 +137,7 @@ SCENE_DELAY=30 FINAL_PAUSE=10 bash demo.sh
|
|
|
137
137
|
|
|
138
138
|
## Prerequisites
|
|
139
139
|
|
|
140
|
-
- `pi` on PATH (current floor 0.80.
|
|
140
|
+
- `pi` on PATH (current floor 0.80.7)
|
|
141
141
|
- `entwurf` provider configured + auth ready for the selected sender/peer models
|
|
142
142
|
- `asciinema` installed
|
|
143
143
|
- `agg` installed (optional — only for GIF conversion)
|
package/docs/setup-clean-host.md
CHANGED
|
@@ -18,22 +18,54 @@ or the pi adapter read whatever auth the user already trusts on the host
|
|
|
18
18
|
|
|
19
19
|
## Reference target
|
|
20
20
|
|
|
21
|
-
Written against a clean Ubuntu / Debian /
|
|
22
|
-
called `cleanhost`. `nvm` keeps the path
|
|
21
|
+
Written against a clean **Linux** host (Ubuntu / Debian / NixOS) reachable via
|
|
22
|
+
SSH, here called `cleanhost`. `nvm` keeps the Node path independent of the distro.
|
|
23
|
+
The neutral npm package may install elsewhere, but Linux is this repair cut's only
|
|
24
|
+
currently certified Claude meta-bridge axis. macOS has no `/proc` bridge discovery
|
|
25
|
+
and is not yet verified/certified for this cut, so its installer refuses new wiring
|
|
26
|
+
and its strict doctor stays `NOT CERTIFIED`/nonzero.
|
|
27
|
+
This is not permanent; future native validation may reopen the lane, while Darwin
|
|
28
|
+
uninstall remains available for older managed state.
|
|
23
29
|
|
|
24
30
|
```bash
|
|
25
31
|
ssh cleanhost 'uname -a; whoami; which git node npm pi claude agy 2>/dev/null'
|
|
26
32
|
# expect on a fully clean host: git present, node/npm/pi/claude absent
|
|
27
33
|
```
|
|
28
34
|
|
|
35
|
+
### What the automated Linux consumer already proves
|
|
36
|
+
|
|
37
|
+
The required CI job `artifact-consumer` runs `check-install-container` against one
|
|
38
|
+
candidate tarball in a Node 24 Linux image that has never seen the checkout. It
|
|
39
|
+
records the artifact sha256 plus image id/repository digest, mounts only that tarball
|
|
40
|
+
read-only, installs globally as non-root through an isolated npm prefix, resolves all
|
|
41
|
+
five bins through PATH, freezes the package root, checks the regular-file path+sha256
|
|
42
|
+
manifest across `install-meta-bridge`, boots MCP `tools/list`, and drives the strict
|
|
43
|
+
doctor. This closes the installed package shape; it does **not** replace this real-host
|
|
44
|
+
walk-through. Its fake Claude CLI, planted plugin cache, stand-in owner, and `/proc`
|
|
45
|
+
bridge are fixtures, so they cannot prove native plugin installation, real hook spawn,
|
|
46
|
+
or idle wake.
|
|
47
|
+
|
|
48
|
+
Default CI lets the gate pack once into a temporary directory. Release acceptance
|
|
49
|
+
instead preserves the `npm pack` output and passes its absolute path as
|
|
50
|
+
`ENTWURF_CANDIDATE_TGZ`; the gate verifies package name/version, prints canonical
|
|
51
|
+
path+sha256, and consumes that exact file without chmod/copy/re-pack. The accepted
|
|
52
|
+
file is the one later published with `--tag repair` (full commands in VERIFY.md).
|
|
53
|
+
|
|
54
|
+
The direct runtime complement is #51 B/B2: actual Claude sessions on one NixOS host
|
|
55
|
+
showed 2.1.138 dropping `args` while reporting success and 2.1.217 honoring exec argv
|
|
56
|
+
and waking on FileChanged exit 2. A target host is still accepted only after installing
|
|
57
|
+
the released artifact, opening a new Claude session, and obtaining installed-doctor
|
|
58
|
+
exit 0.
|
|
59
|
+
|
|
29
60
|
## Pin matrix
|
|
30
61
|
|
|
31
62
|
| Component | Pin / floor | Source of truth |
|
|
32
63
|
|---|---|---|
|
|
33
|
-
| Node |
|
|
64
|
+
| Node | **`>=24.0.0`** — single supported axis, no Node 22 lane | `engines.node` (bound by `check-node-floor-coherence`) |
|
|
65
|
+
| Claude Code | **`>=2.1.217`** — the exec-form hook floor; an older Claude drops the hook's `args` silently and still reports success, so there is no fallback lane | `entwurf.claudeCodeFloor` (bound by `check-claude-floor-coherence`) |
|
|
34
66
|
| npm | bundled with Node 24 | public package install path |
|
|
35
67
|
| entwurf | `@junghanacs/entwurf` | neutral npm package; exposes `entwurf`, `entwurf-bridge`, `entwurf-statusline`, `entwurf-agy-statusline`, and `entwurf-agy-imprint` bins |
|
|
36
|
-
| pi binary | **optional**, `@earendil-works/pi-coding-agent >=0.80.
|
|
68
|
+
| pi binary | **optional**, `@earendil-works/pi-coding-agent >=0.80.7 <0.81` | needed only for the pi adapter / ACP provider / spawn-bg resume lane |
|
|
37
69
|
| Antigravity `agy` | **optional**, operator-installed/authenticated native CLI | needed only for the shipped native-push citizen lane; entwurf never moves its auth |
|
|
38
70
|
|
|
39
71
|
## Stage 0 — Node 24 via nvm
|
|
@@ -123,7 +155,7 @@ If the host will run pi sessions or the Claude ACP provider through pi, install
|
|
|
123
155
|
a compatible pi binary separately and wire the target project.
|
|
124
156
|
|
|
125
157
|
```bash
|
|
126
|
-
npm install -g @earendil-works/pi-coding-agent@0.80.
|
|
158
|
+
npm install -g @earendil-works/pi-coding-agent@0.80.7
|
|
127
159
|
pi --version
|
|
128
160
|
|
|
129
161
|
mkdir -p ~/entwurf-smoke
|
|
@@ -140,7 +172,7 @@ Drift points:
|
|
|
140
172
|
`entwurf-bridge`, and links `~/.pi/agent/entwurf-targets.json` to the package's
|
|
141
173
|
`pi/entwurf-targets.json`.
|
|
142
174
|
- Older pi versions may silently miss the provider/extension surface. Use the
|
|
143
|
-
pinned floor (`>=0.80.
|
|
175
|
+
pinned floor (`>=0.80.7 <0.81`) for release verification.
|
|
144
176
|
- A host that only uses the external MCP bridge can skip this stage until it
|
|
145
177
|
needs `owned-outcome` spawn-bg resume or pi-native control sockets.
|
|
146
178
|
|
|
@@ -148,23 +180,41 @@ Drift points:
|
|
|
148
180
|
|
|
149
181
|
For an external Claude Code session to be replyable by garden id, install the
|
|
150
182
|
meta-bridge plugin globally. This is still a neutral npm-package command; it
|
|
151
|
-
registers Claude Code USER-scope MCP + the SessionStart hook.
|
|
183
|
+
registers Claude Code USER-scope MCP + the SessionStart hook. For this repair cut,
|
|
184
|
+
perform and certify this stage on Linux only. The installer rejects Darwin with a
|
|
185
|
+
“not yet verified/certified for this repair cut” diagnosis and the macOS doctor stays
|
|
186
|
+
nonzero until future native validation supplies a real live-owner measurement.
|
|
152
187
|
|
|
153
188
|
```bash
|
|
154
189
|
entwurf install-meta-bridge
|
|
155
190
|
entwurf doctor-meta-bridge
|
|
156
191
|
```
|
|
157
192
|
|
|
193
|
+
> **Upgrades are not live-reload safe across this hook-launch cut.** Re-run `install-meta-bridge`, then restart **all already-open Claude Code sessions** before trusting send/receive. A new hook reached through the old cached command does not get the owner join it depends on, even though a meta-record may still land; reinstall pairs the artifact and the manifest, and restart makes the native process load that pair. This release also refuses Claude Code below `>=2.1.217` at install and doctor time — an older Claude silently drops the hook's `args`, runs the command alone, and still reports the hook as successful, so there is no fallback lane to fall into.
|
|
194
|
+
|
|
158
195
|
On an installed package (`.../node_modules/@junghanacs/entwurf`), the doctor must
|
|
159
196
|
not try to strip-types-run raw `.ts` helpers or hooks. In the output, check for
|
|
160
|
-
these
|
|
197
|
+
these floor-regression signals:
|
|
161
198
|
|
|
162
199
|
```text
|
|
163
|
-
ok
|
|
200
|
+
ok claude 2.1.217 (>= 2.1.217, exec-form launch contract supported)
|
|
201
|
+
ok launch form: exec form through the shipped hook-launch.sh (no shell on the path)
|
|
202
|
+
ok installed owner argv execs directly (no shell) through hook-launch.sh and keys its sender marker to the live host pid
|
|
203
|
+
ok <N> live Claude MCP process(es): sender + receiver owner join is live and record-backed
|
|
164
204
|
ok full store scan: no corrupt records, duplicate nativeSessionId, body/filename drift, or backend↔wakeMode contradiction
|
|
165
205
|
ok check-entwurf-v2-surface: shipped surface source present; exhaustive source-shape gate is a repo/release invariant (not run under node_modules)
|
|
166
206
|
```
|
|
167
207
|
|
|
208
|
+
The live-process line is **not** a warning any more. If no matching Claude MCP child
|
|
209
|
+
is open — or the host has no `/proc` — the doctor reports `NOT CERTIFIED` and exits
|
|
210
|
+
nonzero, because only the static + synthetic checks were possible and neither of them
|
|
211
|
+
can measure the live join. A host whose live tier was never measured is an
|
|
212
|
+
unmeasured host, not a passing one. An `UNSUPPORTED` launch form needs reinstall; a
|
|
213
|
+
failed live owner join after reinstall means the already-open Claude process still
|
|
214
|
+
holds the old hook definition in memory and must be restarted — the hook itself
|
|
215
|
+
refuses to write markers in that state rather than keying them to whatever the old
|
|
216
|
+
command's shell left behind.
|
|
217
|
+
|
|
168
218
|
If any of those sections reports `ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING`,
|
|
169
219
|
the host is still running a pre-0.12.5 package or a broken tarball. Reinstall the
|
|
170
220
|
current package and re-run `entwurf install-meta-bridge && entwurf doctor-meta-bridge`.
|
|
@@ -290,5 +340,11 @@ rm -rf ~/.nvm
|
|
|
290
340
|
This walk-through is a verification floor underneath release cuts: neutral npm
|
|
291
341
|
install, installed bridge boot, optional pi adapter registration, Claude
|
|
292
342
|
meta-bridge verification where used, all three agy doctors plus a fresh native
|
|
293
|
-
round trip where used, and at least one authenticated ACP runtime smoke.
|
|
294
|
-
the
|
|
343
|
+
round trip where used, and at least one authenticated ACP runtime smoke. For the
|
|
344
|
+
repair cut, preserve the exact package version, candidate tarball sha256, container
|
|
345
|
+
image identity, host OS, Claude version, and installed doctor output together. The
|
|
346
|
+
approved candidate is `0.12.8-repair.0` under dist-tag `repair`; registry `latest`
|
|
347
|
+
must stay `0.12.7`. The local `0.12.7-1` is lower than `0.12.7` and must not be
|
|
348
|
+
published. The version bump still waits for the separate post-production-commit
|
|
349
|
+
version commit and its own three CI jobs. GLG owns every version, tag, publish, push, and host-reinstall
|
|
350
|
+
decision; the complete ordered checklist is in VERIFY.md §Repair-cut order.
|
|
@@ -7,8 +7,14 @@
|
|
|
7
7
|
* NOT cwd (one repo can hold many sessions) and not a wire field (neither host carries one).
|
|
8
8
|
*
|
|
9
9
|
* Measured 2026-07-13 on both backends: hook.ppid == bridge.ppid == the native host pid, same
|
|
10
|
-
* start-key.
|
|
11
|
-
*
|
|
10
|
+
* start-key. Later, the same Claude Code version produced both that direct join and a retained
|
|
11
|
+
* `/bin/bash -c` command-hook wrapper; ordinary shell tests did not reproduce the difference, so
|
|
12
|
+
* its trigger inside Claude's spawn path stayed unknown — and that is why the shell form was
|
|
13
|
+
* abandoned rather than patched. The plugin now declares the EXEC form, which puts no shell on
|
|
14
|
+
* the launch path at all (#51 B2, 2026-07-22), so the hook's parent IS Claude structurally and
|
|
15
|
+
* the marker is written under plain `process.ppid`. The extra `parentPid(ppid)` read candidate
|
|
16
|
+
* remains compatibility for an MCP host wrapper; the hook never writes a blind grandparent
|
|
17
|
+
* marker because that may be the long-lived login shell.
|
|
12
18
|
*
|
|
13
19
|
* Two guards make a marker an IDENTITY rather than a hint, and a candidate is only trusted after
|
|
14
20
|
* BOTH pass:
|
|
@@ -886,10 +886,13 @@ export function defaultMetaMailboxDir() {
|
|
|
886
886
|
* MCP process does not know which garden-id session it belongs to, so the sender
|
|
887
887
|
* envelope degrades to anonymous `external-mcp` and the receiver has no reply
|
|
888
888
|
* address. The hook DOES know the garden-id (it just minted the record), and the
|
|
889
|
-
* hook + the MCP child run under the SAME Claude Code
|
|
890
|
-
*
|
|
891
|
-
* `
|
|
892
|
-
*
|
|
889
|
+
* hook + the MCP child run under the SAME Claude Code owner process. Under the
|
|
890
|
+
* exec-form launch contract the hook's parent IS Claude — Claude execs
|
|
891
|
+
* `hook-launch.sh`, which `exec`s the hook and hands it that same pid — so the hook
|
|
892
|
+
* writes the marker under `process.ppid` and the MCP reads the marker for its OWN
|
|
893
|
+
* `process.ppid`. Both sides name the one owner on every host, with no shell on the
|
|
894
|
+
* path to be mistaken for it and nothing to carry in an env var.
|
|
895
|
+
* This uses process ancestry, NOT cwd inference (same repo / multiple sessions would
|
|
893
896
|
* make cwd ambiguous). `ENTWURF_META_SENDERS_DIR` overrides for tests.
|
|
894
897
|
*/
|
|
895
898
|
export function defaultMetaSendersDir() {
|