Skip to content

Releases: junghan0611/entwurf

v0.12.7

Choose a tag to compare

@junghan0611 junghan0611 released this 14 Jul 05:15

Fixed

  • Three operator commands were dead in every installed package, and the class is now fenced in one place. entwurf doctor-pi-provider, entwurf new-session-id, and entwurf meta-bridge-prune dispatch through run.sh, whose REPO_DIR sits under node_modules once installed — so each one executed a raw .ts and died on ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING. new-session-id is the alias docs/setup-clean-host.md tells operators to run, and doctor-pi-provider is the pi-ownership verdict, so both shipped broken while every dev clone stayed green. This is the same fence start.sh (0.12.1), the store-doctor (0.12.4), the plugin hook (0.12.5), and the agy imprint hook already crossed by hand — the fourth recurrence, and the reason it is no longer hand-written. All 75 .ts entrypoints in run.sh now route through a single run_ts helper that dispatches to the prepack-emitted JS when installed and keeps transparent source execution in a dev clone; a dev-only gate, which has no compiled twin by design, is REFUSED with a legible message rather than falling back to raw .ts or exiting 0 as a silent no-op.
  • The install surface is now verified, not assumed. check-pack-install packed a real tarball but drove only bins — never a subcommand — which is precisely why the three commands passed every gate while being dead on arrival. It now executes them from the installed bin under node_modules and asserts MEANING, not the absence of a crash: the session id must match SESSION_ID_RE, the doctor must reach its own verdict body, and prune must walk the 0-record store it was handed. A new static check-install-surface closes the other half: run_ts is the only fence crossing (S1), every operator subcommand has a compiled twin whether it calls run_ts directly or through a helper — the house style (S2), no npm bin points at a raw .ts and every .sh bin that execs one branches on node_modules (S3), and dev-only gates stay out of the tarball (S4). Each S is mutation-checked against the bug it names; review found three bypasses in the first cut (a raw-.ts bin, an operator command hidden behind a helper, and a smoke that aliased the live path into a variable), and all three now fail the gate.
  • The agy bridge doctor no longer reports a working host as broken. It demanded the literal string mcp(entwurf-bridge/entwurf_v2) in permissions.allow, so a host whose operator had granted a broad mcp(*) was told the bridge was "registered and unusable — agy prompts on EVERY entwurf_v2 call". That was false: agy matches mcp(*) and mcp(<server>) against our tool, which the doctor already knew — it read exactly that coverage in the deny/ask direction to detect shadowing, and then refused to read it in the allow direction. Coverage is now read both ways. The installer still writes only the narrowest rule it needs; the doctor distinguishes a grant we own (allow → …) from one the operator's broader rule is carrying (a NOTE that names the covering rule and warns that narrowing it takes the grant away) from a genuinely missing one (DRIFT). Deny/ask precedence is unchanged and still fails loud, including when the same broad rule sits in both lists. Ownership beats coverage in the other direction too (review follow-up): when the permission-state records that WE added the exact rule and it has since vanished, an operator wildcard keeping calls alive does not make the doctor green — it reports both axes (our grant gone, their rule covering) and stays red; a whole-file settings relink (agent-config ensure_link) produces exactly this shape, and only the statusline doctor caught it before.
  • agy doctors now bind install-state to the live file this host actually reads. A hard-verification sweep moved HOME to /tmp but inherited the operator's real XDG_DATA_HOME, writing seven sandbox-target state records into ~/.local/share/entwurf; all three doctors inspected those foreign files and reported green. Bridge, permission, statusline, and hook state now fail FOREIGN TARGET when their normalized managed path differs from the live target, and fail CORRUPT when the state body is unreadable or lacks its required path. Permission state is checked independently even if bridge state is absent. Runtime and ownership evidence remain separate: a resolvable live command is still reported as present while foreign/corrupt provenance keeps the final verdict red. A relative managed path is CORRUPT too (review follow-up): install only ever records absolute paths, and normalizing a relative one against the doctor's own cwd could bless whatever directory it happens to run from. Regressions cover foreign targets, corrupt/relative state, the independent permission-state rail, and wildcard-masked owned drift (agy install/statusline/hooks: 140/69/44).

Fixed (post-review, 2026-07-14 PM)

  • The offline floor no longer removes the operator's live pi-provider wiring while reporting green. smoke-user-scope-citizen redirected PI_CODING_AGENT_DIR to fake settings but left XDG_DATA_HOME real. Its run.sh remove-user-scope drive therefore consumed the operator's real ownership state, followed that state's recorded managedSettingsPath, removed entwurfProvider.mcpServers.entwurf-bridge from the real ~/.pi/agent/settings.json, and deleted the real state. This made the final bundled-MCP LIVE gate fail with Connected MCP servers: (none registered) after pnpm check had passed. Both inverse calls now pair the fake agent dir with fake XDG state, and the gate sandboxes HOME and the whole XDG trio up front, so the next root run.sh reaches for is already fenced. A before/after byte comparison proves the smoke leaves the live settings, the real $XDG_DATA_HOME/entwurf tree, and the real imprint log unchanged. Review then found the first S5c too narrow to hold the class it was written for: it fired only on the inline-env form, so hoisting the same override into an export one line up walked the identical leak past a green gate (mutation-proven), and its command list blessed install/setup drives that sandbox PI_CODING_AGENT_DIR — which those commands ignore, since ensure_agent_dir_symlinks hard-codes $HOME/.pi/agent and would still relink the operator's real agent dir. S5c now matches the drive (in any env form, and never in prose or an assertion string) and then demands the isolation that command actually needs at each root it writes. Cross-review closed one more ordering hole in that rewrite: a sandbox export XDG_DATA_HOME counts as isolation only for drives that come after it, so a trailing export can no longer retroactively bless a mutation that already ran against the live state (mutation-checked).
  • check-pack-install no longer leaks into the operator's real XDG roots — and proves it on every return path. The gate swapped HOME per drive but inherited the operator's exported XDG roots, so its run.sh install drive wrote a foreign pi-provider install-state into the real ~/.local/share/entwurf and the agy-imprint drive appended fake birth lines to the real ~/.local/state/entwurf/agy-imprint.log — the same class as the 2026-07-13 hard-verify pollution, one layer deeper: inside run.sh itself, where S5/S5b (which scan only scripts/*.sh) cannot see. Every sandbox drive now exports XDG_DATA_HOME/XDG_STATE_HOME/XDG_CACHE_HOME alongside HOME. Review found the first self-fence was itself too narrow: it ran only on the success tail and fenced DATA while the known leak also touched STATE. The final outer self-fence runs after every success or early-failure path, requires the operator's real install-state tree to stay byte-identical, and requires the gate-specific fake agy marker count in the real imprint log not to increase. Dropping a drive's XDG swap is mutation-checked. A separate live audit also disproved the initial “all wiring intact” claim: Claude's XDG marketplace artifact was absent, producing cache-miss, no SessionStart records after 08:46, and an honest 🪛 ? cc; this was a real meta-bridge disconnect, not agy's documented pre-first-turn ?. Reinstalling the live meta-bridge restored source=assembled=installed parity and a fresh Claude probe automatically birthed garden id 20260714T121134-5effc4 (record count 116→117).

Changed

  • Backend drift pins moved, each with an explicit verdict. agy 1.0 → 1.1 after live re-verification on the new minor (2026-07-14, agy 1.1.0: entwurf_self without a permission prompt, bidirectional native-push reply on the same gid, LIVE=1 smoke-agy-native-push-live 13/13 — evidence in DELIVERY.md §Antigravity). codex 0.136 → 0.144 as an observed bump, not a re-verification: codex is not a shipped native-citizen lane in 0.12.x, so the probe evidence stays dated at 0.136.0 and DELIVERY.md §Codex now carries the explicit non-reverification verdict; re-run the raw probes before building any codex adapter.
  • CI now runs the install surface, not just the source tree. pnpm check is a dev-clone floor by construction: every fence bug this repo has shipped was green on it. check-pack-install was release-gate-only, so the installed axis had never been in CI at all. It is now its own job (~1 minute), which is what turns "we fixed it" into "it cannot come back".
  • Verification may not rewire the operator's own installation. check-install-surface S5 flags an offline smoke that writes a live ~/.claude / ~/.gemini / ~/.pi path before swapping the process HOME to a sandbox — or without swapping at all — including one hop of variable aliasing. The current tree is clean (the install smokes all export HOME to a sandbox first, and smoke-resident-garden-guard's rm -rf targets a mktemp -d), so this pins the existing contract rather than fixing a live break. It is a static tripwire, not a sandbox proof: it reads shell source, so a path assembled across several vari...
Read more

v0.12.6

Choose a tag to compare

@junghan0611 junghan0611 released this 03 Jul 09:52

Fixed

  • Claude Code meta-bridge live marketplace sources now live outside the checkout for dev and npm installs alike. Development clones and installed packages both assemble the plugin bundle under $XDG_DATA_HOME/entwurf/meta-bridge/.assembled; the checkout or node_modules tree is now only the source origin. This removes the class where pnpm check, git clean -xfd, or a repo-local uninstall smoke could delete a user-scope Claude marketplace source and leave new Claude Code sessions at statusline ?. The installer still chooses .ts vs compiled .js hook artifacts by source origin, but the live registration path is the same XDG artifact lane.
  • Meta-bridge uninstall now uses the recorded live-artifact path and fails before side effects when the state is corrupt. The install state's assembledMarketplacePath is the SSOT for uninstall, doctor, and state checks. Uninstall validates the full …/entwurf/meta-bridge/.assembled suffix before removing Claude plugin/MCP registrations or deleting state, so malformed paths such as / or …/entwurf/meta-bridge/not-assembled fail loud with zero Claude side effects and leave state/artifacts intact. state.py check also rejects missing, empty, or malformed recorded paths even when settings agree with the corruption.
  • User-scope pi package registration is now part of setup. ./run.sh setup registers entwurf in user-scope pi settings so --entwurf-control and the provider surface load from foreign cwd sessions, not only from a project-local .pi/settings.json. A hermetic smoke-user-scope-citizen covers idempotency, stale normalization, remove symmetry, unrelated-key preservation, and corrupt-shape fail-loud behavior.

Changed

  • The install toolchain is now aligned to one pnpm 11 lane and one setup surface. The repo dropped the obsolete packageManager pin and .npmrc, moved pnpm policy to pnpm-workspace.yaml, updated CI to pnpm 11.9, and treats ./run.sh setup as the single local install path instead of a split pi install flow.
  • The next delivery lane is pinned before implementation. NEXT.md now records the agy/Antigravity delivery adapter constraints: direct-inject/native-push is separate from pi's control-socket liveness domain, registration is explicit, receiver markers are not reused for native-push replyability, and install/doctor/uninstall must inherit the source-origin/live-artifact boundary proven by this release.
  • The future fresh-spawn lane stays mux-visible by default. The mux driver plan now keeps minting and mux transport separate, treats tmux/zmx as drivers behind a leaf interface, and keeps fresh pi-native GPT launch visible instead of reviving hidden background one-shots as the default.

Verification

  • pnpm check passed on 2026-07-03 after the XDG live-artifact move, recorded-path uninstall hardening, user-scope citizen smoke, pnpm 11 setup cleanup, and NEXT lane updates.
  • ./run.sh smoke-meta-install-state passed with the new direct install→XDG proof, recorded-path mismatch case, corrupt basename/missing-field fail-loud cases, and checkout-internal .assembled boundary fingerprint.
  • ./run.sh doctor-meta-bridge passed on 2026-07-03 with source/assembled/installed writer parity all v2 and the assembled bundle under $XDG_DATA_HOME/entwurf/meta-bridge/.assembled.
  • ./run.sh check-pack-install passed on 2026-07-03, proving the npm consumer path keeps the stable XDG marketplace source and user-scope pi citizen registration.
  • LIVE=1 ./run.sh release-gate /tmp/psa-release-gate-0.12.6.ddCcmz passed on 2026-07-03; log /tmp/pi-tmux-entwurf-release-gate-0126.log; summary MUST: PASS=17 FAIL=0 SKIP=0, advisory BEHAVIOR: PASS=0 FAIL=1 (smoke-resident-garden-guard positive /gnew T3 model-in-loop identity turn; non-blocking).

v0.12.5

Choose a tag to compare

@junghan0611 junghan0611 released this 01 Jul 08:30

Fixed

  • The plugin hook now runs as compiled JS when installed — the strip-types class is closed for good. The SessionStart/UserPromptSubmit hook ran as a raw meta-bridge-hook.ts. When the marketplace source sits below node_modules (a global npm/pnpm install), Claude executes that .ts directly and Node refuses strip-types there (ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING), so every session failed. The hook was the last .ts-at-runtime surface; build-bridge now emits dist/pi-extensions/meta-bridge-hook.js and the installer copies that compiled closure (dev clones still run the .ts for transparent editing), mirroring the same installed-vs-dev split start.sh (0.12.1) and the store-doctor (0.12.4) already use. check-pack-install now runs the installed compiled hook from under node_modules with plain node and proves a raw .ts at the same location is refused — the exact fence that broke real hosts is a green regression. The doctor's writer-version parity compares meta-session.<js|ts> by install mode so a compiled bundle no longer false-STALEs against the .ts source.
  • Installed meta-bridge paths are now stable across package upgrades. The installed statusline uses a new entwurf-statusline bin shim (matching the existing entwurf-bridge MCP shim), and installed meta-bridge marketplace assemblies move out of versioned pnpm store paths into a version-stable operator data directory. Package upgrades still require entwurf install-meta-bridge to refresh the Claude plugin bundle/cache, but old-store dead links no longer strand statusLine or marketplace source paths. The doctor now also executes the cached SessionStart hook once in an isolated temp agent dir, catching stale plugin-cache / hook-syntax failures before the next real Claude Code session.

Verification

  • Oracle and local review both reproduced the installed hook fence: compiled meta-bridge-hook.js runs from under node_modules with plain node, while the raw .ts at the same location fails specifically with ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING.
  • ./run.sh check-pack and ./run.sh check-pack-install passed on 2026-07-01 with the hook-JS tarball requirements, baked hooks.json .js assertion, and raw-.ts fence reproduction.
  • pnpm check passed on 2026-07-01 after the hook-JS + stable installed-path changes.
  • ./run.sh install-meta-bridge && ./run.sh doctor-meta-bridge passed from the development clone after re-materializing the gitignored .assembled bundle.

v0.12.4

Choose a tag to compare

@junghan0611 junghan0611 released this 01 Jul 05:34

Fixed

  • Installed doctor-meta-bridge no longer false-fails on node_modules strip-types. The doctor previously ran scripts/meta-bridge-store-doctor.ts and scripts/check-entwurf-v2-surface.ts through Node strip-types even from an npm/pnpm-installed package. Node refuses type stripping under node_modules (ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING), so real floor hosts reported false store-scan / v2-surface failures while development clones stayed green. The doctor now branches by package location: installed packages run the prebuilt mcp/entwurf-bridge/dist/scripts/meta-bridge-store-doctor.js with plain node and defer the repo-only v2 source-shape gate after confirming the shipped source and runtime wiring; development clones keep the raw .ts gates.
  • The store-doctor helper now ships as a node_modules-safe dist artifact. The bridge build emits dist/scripts/meta-bridge-store-doctor.js alongside the MCP bridge closure, reusing the already-shipped meta-session.js dependency without adding a new runtime package lane. check-pack / check-pack-install require the artifact in the tarball and prove it scans a real fixture store under node_modules with plain node.
  • Clean-host install docs call out the 0.12.4 doctor floor check. The walkthrough now tells operators exactly which doctor lines prove the installed-vs-dev split and flags any ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING as a pre-0.12.4 or broken-tarball signal.

Verification

  • pnpm check passed on 2026-07-01 after the doctor/install-floor fix.
  • ./run.sh check-pack-install passed with the new installed store-doctor scan and doctor-dispatch lock assertions.
  • Release-gate prep evidence: LIVE=1 ./run.sh release-gate /tmp/psa-release-gate-0.12.4.PliLsd passed on 2026-07-01; log /tmp/pi-tmux-entwurf-release-gate-0124.log; summary MUST: PASS=17 FAIL=0 SKIP=0, advisory BEHAVIOR: PASS=0 FAIL=1 (smoke-resident-garden-guard post-/gnew autonomous entwurf_self identity turn; non-blocking).
  • hejdev6 real floor host reproduced the pre-fix strip-types failure from an installed package and passed after installing the patched tarball: compiled store-doctor scanned 1 record(s) with plain node, and installed doctor-meta-bridge reported store-scan ok plus v2-surface deferred.

v0.12.3

Choose a tag to compare

@junghan0611 junghan0611 released this 01 Jul 02:58

Changed

  • Claude ACP dependency lane refreshed for Sonnet 5. The ACP runtime pins now use @agentclientprotocol/claude-agent-acp@0.54.1 with @agentclientprotocol/sdk@1.1.0, while keeping the direct @anthropic-ai/sdk@0.100.1 peer-resolution pin. The pi adapter floor is now @earendil-works/pi-* >=0.80.3 <0.81, matching the runtime catalog that exposes claude-sonnet-5.
  • Curated Claude surface moves from Sonnet 4.6 to Sonnet 5. claude-sonnet-5 replaces claude-sonnet-4-6 across the ACP provider, demo/default smoke targets, pi target registry, and release docs. The Sonnet-specific 200K cap was removed; Sonnet 5 now surfaces the verified 1M context window like the Opus anchor, with a 1M ceiling guard to prevent silent future inflation.
  • Release and backend docs are version-neutral where the repo shape is stable. The README now describes the durable repository shape instead of a point-in-time 0.12.x state, and the clean-host/demo/verification docs reflect the current pi floor and Sonnet 5 defaults.
  • Cortex and fresh-spawn planning docs realigned to the 0.12 substrate. The ACP backend rail documentation and NEXT/ROADMAP planning notes now describe the as-built adapter seam and the mux-visible fresh-sibling lane without re-centering old v1/fat-bridge assumptions.

Fixed

  • Live ACP smoke timeout cleanup no longer leaves stale timers after PASS. Raw-turn, overlay, memory-containment, and session-reuse live smokes now clear their timeout handles when the awaited operation wins, so a successful live check exits promptly instead of lingering until the old timeout expires.
  • Session-reuse live smoke now preserves its success evidence. The two-turn reuse smoke no longer calls process.exit(0) on success, allowing the turn-2/PASS log and cleanup to drain naturally while the retained ACP child is still handled by the backend exit cleanup.
  • Claude Code meta-bridge install state also disables workflow surfaces. enableWorkflows and workflowKeywordTriggerEnabled are now managed false alongside the existing auto-memory, compaction, prompt-suggestion, progress, and auto-mode settings.
  • Unsupported ACP Codex targets are not advertised. The target registry keeps Codex on the native openai-codex provider only; entwurf/gpt-5.4 and entwurf/gpt-5.5 are removed until an ACP Codex backend exists.

Verification

  • pnpm check passed on 2026-07-01 after the dependency/model/doc updates; prep log /tmp/pi-tmux-entwurf-prep-check-0123.log ended with EXIT=0.
  • LIVE=1 ./run.sh smoke-acp-raw-turn-live passed with claude-sonnet-5 and exited promptly after PASS.
  • LIVE=1 ./run.sh smoke-acp-provider-live passed with claude-sonnet-5 through the pi provider path.
  • LIVE=1 ./run.sh smoke-acp-session-reuse-live passed with a two-turn reused ACP child; turn 2 recalled the turn-1 codeword from delta-only prompt scope.
  • LIVE=1 ./run.sh release-gate /tmp/psa-release-gate-0.12.3.ZcRc8w passed on 2026-07-01 with MUST: PASS=17 FAIL=0 SKIP=0 and BEHAVIOR: PASS=1 FAIL=0; log /tmp/pi-tmux-entwurf-release-gate-0123.log.

v0.12.2

Choose a tag to compare

@junghan0611 junghan0611 released this 29 Jun 12:10

Fixed

  • Claude Code meta-bridge installs on the supported 2.1.x floor. Claude's plugin validator is a closed schema whose accepted marketplace keys differ by patch level; 0.12.1's root-level marketplace.json description passed on Claude 2.1.195 but failed on the floor host's 2.1.97 with Unrecognized key: "description". The marketplace manifest now keeps only the minimal root keyset confirmed on the floor (name, owner, plugins), leaving explanatory text in installer/docs comments rather than in closed-schema JSON.
  • Installed meta-bridge MCP wiring no longer bakes pnpm store paths. On npm/pnpm-installed hosts, the user-scope Claude MCP entry now points at the stable entwurf-bridge bin instead of $REPO/mcp/entwurf-bridge/start.sh, whose $REPO can be a .pnpm/@junghanacs+entwurf@.../node_modules/... hash path that goes stale on version or peer changes. Development clones still pin to that clone's start.sh; both branches preserve the external Claude sender env (ENTWURF_BRIDGE_EXTERNAL_AGENT_ID=external-mcp/claude-code, ENTWURF_BRIDGE_REQUIRE_META_SENDER=1).

Added

  • check-meta-manifest-schema — a CLI-version-independent meta-bridge manifest guard. The new deterministic gate pins the Claude marketplace/plugin/hooks manifests to the minimal keysets used by the meta-bridge, including the load-bearing hook event names (SessionStart, CwdChanged, UserPromptSubmit, FileChanged) and hook command keys. It also asserts meta-bridge-state.py::desired_mcp() chooses the installed-vs-clone MCP wiring without invoking a real Claude CLI. The gate is wired into pnpm check so future decorative closed-schema keys fail before release.

Verification

  • pnpm check passes with the new check-meta-manifest-schema static guard included.
  • ./run.sh smoke-meta-install-state passes, proving the state manager/doctor consumers still follow desired_mcp() without drift.
  • Installed-location regression probe passes: running scripts/check-meta-manifest-schema.py from a synthetic node_modules/@junghanacs/entwurf package no longer self-fails.
  • Claude Code 2.1.97 floor validation passed on hejdev6 after removing the root marketplace description; current Claude 2.1.195 validation also passes with warnings only.
  • LIVE=1 ./run.sh release-gate /tmp/psa-release-gate-0.12.2.NLGhet passed on 2026-06-29 with MUST: PASS=17 FAIL=0 SKIP=0 and BEHAVIOR: PASS=1 FAIL=0; log /tmp/entwurf-release-gate-0.12.2-20260629T205339.log.

v0.12.1

Choose a tag to compare

@junghan0611 junghan0611 released this 29 Jun 10:31

Fixed

  • Installed MCP bridge boots from npm/node_modules. The 0.12.0 launcher ran src/index.ts through Node strip-types, but Node refuses type stripping below node_modules (ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING). The npm tarball now carries a clean tsc-emitted bridge JS closure under mcp/entwurf-bridge/dist/, and start.sh runs that dist path when installed while keeping the strip-types source path for development clones.
  • Stale dist files cannot ship. build-bridge removes mcp/entwurf-bridge/dist before emit, and check-pack-install plants a stale sentinel before npm pack to prove prepack cleaned the tree. This closes the orphan-emit tarball contamination found during the 0.12.1 C review.
  • npm bin symlink installs resolve the package root. run.sh, start.sh, and the bridge protocol smoke now resolve symlinks before computing their root, so node_modules/.bin/entwurf and node_modules/.bin/entwurf-bridge work like direct package paths.
  • Two live ACP MUST smokes no longer flake on model-in-loop phrasing. smoke-acp-carrier-augment-live proved augment delivery by asking the model to echo a SECRET_PROJECT_CODE planted in a /tmp AGENTS.md — current Claude correctly refuses that as a prompt-injection/exfil pattern, so the MUST gate failed even though the augment rode the wire and the empty carrier billed clean. It now uses a benign factual marker (an internal build codename) asked back as a normal "answer from project context" task. smoke-acp-bundled-mcp-live asked the model for "values only, one per line" yet asserted on field-name-labeled lines (socketState: alive), so a compliant bare-value reply was dropped by the envelope filter while the [tool:done] notice truncated socketState; it now requests labeled lines so the three identity fields are deterministically observable. Neither change weakens the gate (same MUST assertions, same non-circular gid proof) — both only realign the observation contract with current model behavior.

Changed

  • Install docs are npm-first and pi-adapter-second. The README and clean-host walkthrough now lead with neutral npm install @junghanacs/entwurf, document the entwurf / entwurf-bridge bins, and move pi to the optional ACP-provider/control-socket adapter lane (@earendil-works/pi-coding-agent >=0.80.2 <0.81). pi install npm:... is no longer the public primary install recipe.
  • Pi peers are optional for the neutral package. The @earendil-works/* peer trio and typebox are marked optional so a plain npm install can boot the MCP bridge without pulling the pi adapter stack. The pi loader lane is still verified separately with explicit pi peers.
  • Garden id concept is documented up front. The concept primer now defines garden/garden id as the shared address space for independent harness citizens, not a worker name or proof of pi ownership, and points callers to entwurf_peers + entwurf_v2 instead of hand-picking transports.

Verification

  • ./run.sh check-pack-install passes with the neutral npm install regression: package bins present, optional pi peers absent, installed dist bridge answers tools/list, and pi-loader registration still passes on the explicit pi-peer lane.
  • Remote hejdev6 real-install probe from the packed tarball passed: local npm install, package bins, optional pi peers absent, installed entwurf-bridge tools/list, entwurf install with isolated HOME, and entwurf check-bridge. The host's real HOME also exposed a pre-existing stale ~/.pi/agent/entwurf-targets.json symlink to pi-shell-acp; fix with entwurf setup:links --force or an explicit ENTWURF_TARGETS_PATH if that old registry is intentional.
  • LIVE=1 ./run.sh release-gate /tmp/psa-release-gate-0.12.1.GUFDUb tiers MUST: PASS=17 FAIL=0 SKIP=0 with BEHAVIOR: PASS=1 FAIL=0 on 2026-06-29; log /tmp/entwurf-release-gate-0.12.1-20260629T191543.log (the two live-smoke observation fixes above were what moved the gate from MUST FAIL=1 to green; the install change is harness-neutral and touches no ACP code path).

v0.12.0

Choose a tag to compare

@junghan0611 junghan0611 released this 29 Jun 05:16

This release hard-cuts the project from pi-shell-acp to entwurf. It is not a compatibility rename: the package/provider/model/MCP identity is now entwurf, v1 entwurf verbs are gone, and entwurf_v2 is the canonical garden-id dispatch verb. The repo is entwurf-core (v2 dispatch) + meta-bridge + pi adapter + ACP plugin. Pi remains an important adapter and ACP host, but the project subject is the garden-citizen dispatch substrate. Verified release floor: pnpm check, check-pack, check-pack-install, and LIVE=1 ./run.sh release-gate <scratch> MUST tier PASS=17 FAIL=0 SKIP=0 on 2026-06-29, with BEHAVIOR (advisory model-in-loop autonomous MCP tool-selection) PASS=1 FAIL=0 and recorded separately from the cut decision.

Added

  • entwurf package/provider identity. Package metadata, provider id, model prefix, MCP server name, repo URL, env namespace, install surface, and package-source resolver now use entwurf / @junghanacs/entwurf / entwurf-bridge. Runtime aliases and legacy provider-id accept are intentionally absent.
  • 0.12.0 install surface. ./run.sh setup . is the one-command local setup path: pnpm install → project install → Claude Code meta-bridge install when available → v2 smoke. run.sh install now preflights broken/missing node_modules and fails loud before writing settings. check-install-preflight locks the relocation/reinstall gap found after the repo move.
  • Published-package smoke. check-pack and check-pack-install pack the release tarball, install it into a fresh temp project with pi 0.80.x peers, and prove the pi loader registers the entwurf provider and curated Claude model anchors.
  • ACP plugin on the v2 core. Provider registration via pi-extensions/acp-provider.ts plus pi-extensions/lib/acp/*, curated Claude models (claude-sonnet-4-6, claude-opus-4-8), no-auth sentinel, explicit config overlay, tool-surface preflight, ACP→pi event mapping, session reuse, first-user augment, and display-only lifecycle notices.
  • Meta-bridge install/state cutover. Claude Code meta-bridge install state, MCP entry, statusline, permissions, and sender/receiver markers now use the entwurf naming surface; stale pi-tools-bridge MCP entries are pruned as one-shot cutover state, not accepted as a runtime alias.

Changed

  • Project framing is entwurf-first. README / ROADMAP / DELIVERY / BASELINE / VERIFY were reframed around garden ids, shipped vs verified-probe harness lanes, thin bridge boundaries, tool narrowing, and the ACP plugin as one ingress rather than the boundary.
  • ACP is a plugin, not a second harness. The host --entwurf-control pi session supplies socket citizenship; the ACP plugin owns backend process lifecycle, overlay, per-backend dialect, and turn evidence only. No auth proxy, no subscription bypass, no transcript hydration, no ambient MCP scan.
  • Dependencies bumped and fenced. pi dev/runtime floor is now 0.80.2 (>=0.80.2 <0.81), with getModels reached through the loader-compatible /compat entrypoint. Claude ACP moved to @agentclientprotocol/claude-agent-acp@0.50.0 and @agentclientprotocol/sdk@0.29.0; model forcing uses session/set_config_option(configId="model"). The deprecated ClientSideConnection path was replaced by the fluent client().connect(stream) adapter with explicit teardown close.
  • Release gate remains two-tier. MUST owns the cut decision and now includes v2-native live gates plus ACP plugin live smokes (socket-citizen, raw-turn, overlay, provider, reuse, carrier augment, memory containment, RGG, operator MCP, skill, bundled bridge). BEHAVIOR is advisory model-in-loop autonomous tool-selection and never blocks the cut.
  • Install docs are npm-first. README and the clean-host walk-through lead with pi install npm:@junghanacs/entwurf (resolving to ~/.pi/agent/npm/node_modules/@junghanacs/entwurf, the path check-package-source-routing pins); the git: source path and a local clone are documented as alternatives for tracking main or hacking on the bridge.
  • Release hero refreshed for the entwurf identity. The README hero and pi package gallery image now use a GLGMAN Universe entwurf release-day illustration (four sibling schools, shared forged driver) instead of the old demo GIF as the public first impression.
  • Verification docs simplified to the 0.12 surface. VERIFY.md was reduced from 621 to 260 lines and BASELINE.md from 284 to 227 lines by deleting broken/dead v1 procedures, moving historical rows back to CHANGELOG/git, and keeping the load-bearing 0.12 evidence axes: install paths, identity interview, carrier separation, backend MCP identifiers, tuple formula, JSONL memory axis, and pass criteria.
  • v1 residue swept from runnable comments and stale ledgers. Retired entwurf_send / entwurf_resume references in current-source comments and the absorbed NEXT--acp-on-v2.md branch ledger were removed or rewritten to the v2 surface; historical CHANGELOG rows remain historical evidence, not current recipes.

Removed

  • v1 entwurf entrypoints. MCP tools entwurf / entwurf_resume / entwurf_send, pi-native entwurf_send, spawn_async_resume, and /entwurf* commands are removed. Current surface: entwurf_v2, entwurf_peers, entwurf_self, entwurf_inbox_read, plus /entwurf-sessions and /gnew on the pi adapter.
  • Legacy pi-shell-acp runtime identity. No permanent package/provider alias, no legacy provider accept, no dual-read of old runtime state. Old install/cache state is a one-shot operator cutover only.
  • OpenClaw install surface. The old plugin/importer path remains historical only; it is not a live consumer.
  • Phantom compaction surface. Documentation no longer claims bridge-owned compaction knobs or a compaction smoke surface; backend-native compaction remains backend-owned.

Fixed

  • Mailbox reply instructions point at the canonical v2 surface. Replyable meta-session bodies now tell agents to use entwurf_v2, not the retired entwurf_send tool.
  • Branch-lane handoff pointers no longer dangle. AGENTS.md now points at the main-lane NEXT.md rather than a deleted branch ledger.
  • npm-managed installs resolve hoisted runtime dependencies before writing settings. run.sh install walks Node's module-resolution paths, so pi install npm:@junghanacs/entwurf works from ~/.pi/agent/npm/node_modules with no package-local node_modules; the @earendil-works/pi-* peer trio is treated as loader-provided. check-pack-install locks the layout with a real npm-managed run.sh install regression under an isolated HOME.

Deferred

  • npm publish / old package deprecation — maintainer-owned final step after the release cut. package.json is already @junghanacs/entwurf with publishConfig.access: public; registry publication is intentionally last.
  • Demo / GIF retake — the recorded demo scripts and archived GIF still describe the retired v1 entwurf / entwurf_resume / entwurf_send flow, so they ship as pre-0.12 evidence; a v2-native demo retake is a post-0.12 minor follow-up.
  • Fresh sibling minting — v2 dispatch targets already-identified citizens only. Creating a brand-new sibling from nothing is a later spawn-fresh lane.
  • Persisted resume/load — current ACP reuse is in-memory plus record write; persisted read/use stays off.
  • Non-Claude ACP backends / Cortex lane — next backend work should first define the backend-addition rail and verification contract.
  • Codex / Antigravity shipped adapter lanes — delivery probes are verified, but 0.12.0 ships them as documented evidence, not managed install surfaces.
  • Bundled-MCP deterministic splitsmoke-acp-bundled-mcp-live still exercises a model-in-loop entwurf_self call inside the MUST floor; it passed on the current cut, but the deterministic surface proof vs autonomous echo split remains a follow-up before hardening the taxonomy.

Final hygiene before npm publish

  • The v0.12.0 tag was realigned to 3360b3b before npm publication so the GitHub release tag and the npm tarball can describe the same source tree.
  • Removed stale Biome noExplicitAny suppression comments from ACP session-reuse verification scripts; pnpm lint now runs without the prior unused-suppression warnings.
  • README now includes the explicit npm package URL: https://www.npmjs.com/package/@junghanacs/entwurf.

v0.11.0

Choose a tag to compare

@junghan0611 junghan0611 released this 16 Jun 06:14

0.11.0. The entwurf v2 dispatch substrate (#35): a single additive verb, entwurf_v2 / runEntwurfV2, that unifies dispatch to existing garden citizens behind one decider which reads peer liveness as a fact and picks transport from a frozen table keyed on target state AND intent (not state alone): live pi + fire-and-forget → control-socket send, dormant pi + owned-outcome → spawn-bg resume, active self-fetch + fire-and-forget → meta-mailbox enqueue; every other state×intent pair (live+owned, dormant+fire-and-forget, self-fetch+owned) is an honest reject. It does not mint new siblings — fresh sibling creation stays the v1 entwurf verb (0.12 cutover lane); the spawn-bg resume row resumes an already-identified dormant citizen, it does not create one. Built bottom-up as pi-only TypeScript substrate (Stage 0): contract freeze → per-gid dispatch lock → pure decider → release-policy reducer → send/spawn hands → production deps assembly → pi-native + MCP entwurf_v2 tool. On top of it the spawn-bg resident lifecycle — the headline guarantee of #35 — was proven LIVE for the first time (a real pi --entwurf-control child stands its socket up, resumes a dormant Entwurf session, and does a model turn), and that first LIVE run surfaced and closed a production contradiction in the resident-name guard. Finally a v2-only mode (PI_SHELL_ACP_V2_ONLY=1) hard-refuses every v1 entrypoint so v1 can be turned off ahead of its 0.12 removal. Scope is Stage 0 (pi-only substrate); Stage 1 (Claude↔Claude live) is explicitly out of scope. Release-gate restructured before the cut (2026-06-16): the pi floor was bumped to >=0.79.4 (runtime parity) and the live gate was split into a two-tier MUST (release-blocking, owns the exit code) + BEHAVIOR (advisory, model-in-loop autonomous MCP tool-selection) summary. The prior flat LIVE=1 → PASS=18 run (log …20260615T152058) predates BOTH changes and no longer describes the current tree. Fresh LIVE=1 release-gate on the 0.79.4 + two-tier tree (2026-06-16, log …20260616T141023): MUST PASS=17 FAIL=0 SKIP=0 (necessary condition met) + BEHAVIOR PASS=1 FAIL=1 — the lone BEHAVIOR-FAIL is sentinel (cell4/5 S7 Bash-bypass, advisory/non-blocking); resident-garden-guard positives flipped FAIL→PASS vs the earlier run with zero code change, confirming the advisory scoping. "green" applies to the MUST tier only.

Added (0.11.0 entwurf v2 — unified dispatch verb)

  • entwurf_v2 / runEntwurfV2 — one additive dispatch verb over three transports. The decider reads a peer's liveness as a fact (the entwurf_peers fact surface) and chooses transport from a frozen table keyed on state AND intent: alive pi + fire-and-forget → control-socket send, dormant pi + owned-outcome → spawn-bg resume, active/deliverable self-fetch + fire-and-forget → meta-mailbox enqueue; the complementary pairs (live + owned, dormant + fire-and-forget, self-fetch + owned, inactive self-fetch + fire-and-forget) are honest rejects, not silent fallbacks. Built as pure substrate first — the contract freeze, a per-gid lockfile primitive (F2), the pure decider, the release-policy reducer, the control-send + dead-control-send hands, the enqueue-only mailbox send body, the pure execute-router and the top-level decide→execute join — then surfaced as both a pi-native tool and an in-process MCP verb, plus doctor surface sanity and the PI_ENTWURF_PREFIX_ROOTS operator-policy SSOT. Each slice landed behind its own deterministic gate.
  • v2 dispatch reachability + lock SSOT and a 3-cell LIVE matrix sentinel. check-entwurf-v2-matrix is the reachability + lock-policy table; smoke-entwurf-v2-matrix-live is a 3-cell LIVE sentinel (control / mailbox / guard) wired into release-gate after the bridge check with an honest LIVE!=1 skip.

Added (0.11.0 entwurf v2 — spawn-bg resident lifecycle, #35 headline)

  • smoke-entwurf-v2-spawn-resume-live — the full spawn-bg resident loop, proven LIVE (22 checks). A real saved Entwurf session is resumed by a detached pi --entwurf-control child that stands its control socket up, does a model turn (nonce echoed in both the user and assistant turns of the session JSONL), releases its dispatch lock exactly once, and cleans up with zero litter. This is the first time the production spawn path (a detached spawn() pi resident — not the v1 tmux smoke fixture) was exercised end to end, closing the acceptance debt step 5c-3c had deferred to "the matrix proves it."
  • PI_SHELL_ACP_V2_RESUME_RESIDENT_SESSION_ID — sessionId-bound authorized Entwurf-child resident. The first LIVE run surfaced a production contradiction: a v2 spawn-bg resume launches its child with --entwurf-control, but maybeSetResidentName refused any entwurf-tagged resident (the tag is the entwurf_resume marker), so the resume self-terminated before doing any work. Resolved by narrowing the invariant, not dropping it: operator residents still reject the entwurf tag, but a v2 spawn-bg resume child is authorized by the sessionId-bound marker its launcher plants, keeps its tag, and stays re-resumable. The marker is a zero-import leaf so it is root-safe from both the .js-import (entwurf-control) and .ts-import (entwurf-v2-spawn-production) compilation worlds.

Added (0.11.0 entwurf v2 — v2-only mode)

  • PI_SHELL_ACP_V2_ONLY=1 — hard-refuse every v1 entwurf entrypoint ahead of v1 removal. A zero-import leaf helper (entwurf-v2-only.ts: isV2OnlyMode true only for exact "1", checkV1EntwurfAllowed pure return, assertV1EntwurfAllowed throw wrapper) is the single source of truth; when PI_SHELL_ACP_V2_ONLY=1, each of the 10 v1 entrypoints (9 surface groups — /entwurf tool + command count as two) refuses before any side effect through its own existing hard-refusal channel (tool throw, command notify, isError result, RPC respond(false), startup error report, MCP textErr). The MCP entwurf_resume handler and the control RPC spawn_async_resume are both guarded so the socket path cannot bypass the MCP guard. v1 code is neither deleted nor unregistered — invocation refusal only; the 11-scenario v2 replacement and v1 removal are the 0.12 lane. check-entwurf-v2-only (28 checks) proves the helper contract, all 10 guard sites, the double guard, and that the entwurf_v2 core stays flag-clean.

Added (0.11.0 entwurf v2 — fact surface)

  • listEntwurfFacts / entwurf_peers — the entwurf fact surface (4-value liveness). A pure PeerFact core (garden citizens from meta-records + record-less control sockets, each with alive / dead / indeterminate / unsupported liveness) assembled bottom-up with socket-axis hardening (symlink / dir-read / malformed diagnostics). It reports facts, never routing verbs — the dispatch decision is computed later by the v2 contract, not here.

Added (0.11.0 meta — delivery authority on v2 identity + trust)

  • Meta delivery authority cut to v2 identity + state store (3D-2 … 3D-4). A v2 identity-normalization gate, a dual-read identity seam with delivery-agnostic consumers, the live mailbox receipt dual-write to the state store, and backend capability sourced from the registry rather than a const. The entwurf_v2 contract surface was frozen and gated before the verb was wired (step 4-pre).
  • project_trust handler + inherited-trust preflight (Trust 2층). The pi trust preflight carries inherited-trust evidence and a deny formatter (F5a/N3b); a project_trust handler adds an inherited-distrust escape.

Added (0.11.0 entwurf v2 — SE active-receiver deliverability)

  • The SE-1/SE-2 deliverability seam — honest pi-native + meta-self replyability. A meta-receiver presence marker, MCP + pi-native entwurf_send mailbox-fallback gates, a mailboxConversationalDeliverable predicate, a guarded mailbox-enqueue wrapper, and the required v2 active-receiver deliverability seam, so a send to a deliverable self-fetch meta citizen (an active-receiver claude-code session) with no live socket can fall back to the mailbox transport, while direct-inject (pi) and inactive-receiver targets reject without leaving mailbox garbage instead of falsely succeeding.

Fixed (0.11.0 entwurf v2 — deliverability honesty, consumer-visible)

  • entwurf_send / entwurf_self stop reporting false delivery to unreachable targets. Previously a send to a citizen with no live control socket, or a replyable: true self-report, could be emitted even when the target could not actually receive — a socketless / record-less pi session, an inactive (human-terminated) meta receiver, or a direct-inject (pi) target — leaving a ✓ delivered signal and stray .msg mailbox files behind. Now such targets are rejected honestly (no enqueue, no mailbox garbage), and entwurf_self reports replyable: true only when a real inbound path exists. This is an observable change for entwurf_send / entwurf_self consumers, but it is a honesty bug-fix, not a behavior regression — a previously "successful" send to a dead/inactive target was always a false positive. Gates: check-entwurf-deliverability, check-entwurf-mailbox-guard, check-entwurf-self-address.
  • entwurf_v2 now reaches a record-less but live pi control socket (socket-only fire-and-forget target). entwurf_peers lists a live pi --entwurf-control session as alive whether or not it has a meta-record, but the v2 production resolveTarget previously rejected any record-less gid as bad-target — so an entwurf_v2 to an operator-greeted live pi peer failed while a legacy entwurf_send to the same gid succeeded (a fact-surface ↔ dispatch contract gap; the bad-target was masked whenever a caller fell back to v1). v2 now accepts a record-LESS gid whose canonical control socket is a confirmed non-symlink socket as a socket-only pi endpoint: a PROBE-FREE presence hint (one lstat, no connect — the shared socket-discovery classifier, so l...
Read more

v0.10.0

Choose a tag to compare

@junghan0611 junghan0611 released this 06 Jun 09:58

The first meta-bridge release (#30): garden-native async delivery into already-running, already-authenticated native coding-agent sessions — Claude Code only. Built bottom-up — a deterministic drift sentinel protecting the (then-undocumented) async-delivery path, the backend-agnostic meta-record authority (schema + pure functions), the idempotent fs upsert, the Claude SessionStart create/attach hook, and entwurf_send mailbox delivery + read-receipt — then the sender/addressee identity path (a native session becomes both wakeable AND a trusted, replyable sender) — and on top of it the operator surfaces: honesty gate (Phase 1), stateful install/uninstall/doctor (Phase 2), the garden-identity statusline (Phase 3), the listing-only meta-store prune janitor (Phase 4), and native sender identity + addressee delivery (Phase 5). A final cut-time fix closed the last identity asymmetry: entwurf_self now resolves trusted meta-session identity too, so a native Claude Code garden citizen can self-report the same replyable garden-id envelope it uses for entwurf_send. Hardening landed in the same cut: the doctor's drift surface is fail-loud again (a set -e early-death regression closed + gated), and #34's level-triggered body-drain robustness basis is now an asserted gate, not just a doc claim. The 0.9.0 evidence-closure entries below also strengthen two live gates so 0.9.0's guarantees are proven directly rather than indirectly and trim a stale follow-up. Scope stays Claude Code only; agy/Codex remain proven future adapters, not shipped surfaces.

Added (0.10.0 meta-bridge — Phase 5 native sender identity + addressee delivery)

  • entwurf_send learns WHO a native session is — authoritatively, not by cwd guess. Phases 1–4 made a native Claude session addressable (the receiver half: wakeable by garden-id, inbox-readable). Phase 5 closes the sender half. A native session sending through the user-scope pi-tools-bridge MCP has no PI_SESSION_ID, so the bridge resolves identity from a sender marker the SessionStart hook writes, keyed by the shared Claude parent pid (process.ppid of the MCP child IS the Claude process the hook ran under — never cwd inference; PI_META_SENDER_MARKER overrides for tests). A trusted marker promotes the send to a replyable meta-session sender addressed by its garden-id. The marker carries ownerPid + a boot-unique ownerStartKey, so a pid-reuse by an unrelated process fails the guard instead of granting a wrong-identity send, and a marker with no backing meta-record is refused. PI_TOOLS_BRIDGE_REQUIRE_META_SENDER=1 (set by the Claude user-scope install) closes the anonymous-send hole: a send with neither pi-session identity nor a trusted marker is refused rather than going out as anonymous external-mcp — "if we don't know who sent it, we don't send it." The sender envelope origin gains "meta-session"; the receiver render shows a [meta-session] badge. Deterministic E2E in smoke-meta-sender-identity (A→B garden-id sender, B→A reply, PPID path, unbacked-record reject, pid-reuse mismatch reject, anonymous reject); the mailbox round-trip (send → enqueue + signal → entwurf_inbox_read.read archive + lastReadAt receipt) is covered E2E by smoke-meta-mailbox. Both gates are in pnpm check.
  • entwurf_resume async-capability discriminant fixed: origin === "pi-session", not replyable alone. A meta-session is entwurf_send-replyable (it owns a garden-id mailbox) but has no pi control socket, so it cannot host an async-resume followUp any more than an external host can. The old auto-resolve keyed on replyable and so routed a meta-session resume into a control-socket lookup that always fails. The async discriminant is now asyncCapable = replyable && origin === "pi-session": a meta-session (like an external host) auto-resolves to sync, and an explicit mode="async" from either is rejected with the canonical reason text. check-async-resume-gate adds the meta-session cases (now 19 assertions).
  • Preventive keyset-overlap guard (check-keyset-overlap + smoke-meta-keyset-guard). The meta-bridge install owns a fixed set of ~/.claude/settings.json (+ ~/.claude.json) keys (SSOT: meta-bridge-state.py managed-keys); agent-config and any future consumer merge their own fragment into the same file. This is the preventive half (doctor's state.py check is the after-the-fact survival half): it fails loud when a consumer fragment collides with a pi-owned key — exact match OR ancestor/descendant — so a later agent-config jq merge (.[0] * .[1], which replaces arrays and overwrites scalars) cannot silently clobber pi-owned policy or vice-versa. smoke-meta-keyset-guard proves a disjoint fragment passes and exact/array/parent-child collisions fail loud while unrelated sibling keys (permissions.defaultMode, language) stay clean; in pnpm check.

Fixed (0.10.0 meta-bridge — cut-time identity symmetry)

  • entwurf_self closes the last pi-only identity gate for garden-native meta-sessions. Phase 5 made native Claude Code sends replyable by garden id, but the introspection twin still used the old strict pi-env path and threw when PI_SESSION_ID / PI_AGENT_ID were absent — even when the same MCP process had a trusted sender marker. entwurf_self now uses the same authoritative identity resolver as entwurf_send: pi sessions return origin="pi-session" + socketPath; trusted meta-sessions return origin="meta-session", agentId="meta-session/claude-code", replyable=true, the garden id, and mailboxPath; plain anonymous external hosts still fail because they have no authoritative reply address. This closes the cut-time asymmetry: a garden-native Claude Code session can now both send as and self-identify as a replyable garden citizen. Regression coverage lives in smoke-meta-sender-identity, and the fix was live-confirmed from agent-config via native Claude Code (entwurf_self returned the session's garden id and replyable: true).

Fixed (0.10.0 meta-bridge — doctor fail-loud drift detail)

  • doctor-meta-bridge surfaces a managed-config drift instead of dying silently. The [managed config state] section captured the state.py check output with a bare CHECK_ERR="$(… check …)" assignment under set -euo pipefail. The instant check exited nonzero (drift), the assignment tripped set -e and the doctor exited 1 after printing only the section header — the very "which key drifted" detail the section exists to print was lost, and every later section ([plugin install], [meta-record store], the SILENT-MISS guard) never ran. The substitution is now the condition of an if, so its nonzero status is consumed (not a death) and the drift detail + the entire rest of the chain always print. A hermetic regression in smoke-meta-install-state forces a real managed-config drift behind a fully-faked claude toolchain (no real claude, no real install) and proves the doctor (a) still exits 1, (b) prints Drift detail:, (c) names the concrete drifted key, and (d) runs to its final summary line — i.e. no early set -e death anywhere. Negative-tested: re-introducing the bare assignment fails the regression on exactly those assertions.

Added (0.10.0 meta-bridge — #34 D8 level-triggered drain gate)

  • check-meta-session asserts the level-triggered body-drain robustness basis (#34). The wake signal (inbox.signal) is edge-triggered — rapid pokes can coalesce into one FileChanged, or a signal can be lost entirely — but each message body is a separate level-state file on disk, so one readMetaInbox drains the whole directory. #34 names this the D8 robustness basis and asks the gate to assert it, not just let DELIVERY.md claim it. A new case enqueues three messages (two fresh .msg + one doorbell-rung .msg.delivered) and proves a single drain returns all of them, in deterministic chronological (sorted) order, with one lastReadAt receipt for the batch, every body archived to .read, and a re-read empty. Negative-tested: an edge-triggered "drain one per signal" regression fails on the all-three assertion while the single-message cases stay green.

Added (0.10.0 meta-bridge — Phase 4 prune listing)

  • ./run.sh meta-bridge-prune — listing-only meta-store janitor. doctor-meta-bridge reds on corrupt JSON / duplicate nativeSessionId / body↔filename drift, but it intentionally does not fail on transcript-gone records — so a green store still silently bloats with abandoned meta-records as native sessions come and go. This separate hygiene surface scans defaultMetaSessionsDir() (override with a positional dir; stale window via --ttl-days, default 30) and classifies every record into orphan (parse OK but transcriptPath no longer exists — a strong abandonment signal, not proof: a backend path migration / cleanup / config-dir change can also vacate it), stale (parse OK, transcript present, lastSeen older than the ttl), ambiguous (corrupt / drift / duplicate — manual-only: the operator decides which authority survives, never a blind rm of a duplicate pair), and keep (live transcript + recent). It prints the exact manual rm commands for orphan/stale candidates and deletes nothing — no --apply in 0.10.0 (this is the conservative "list, the operator removes" scope; actual GC / TTL automation / a global agent-skill wrapper are deferred). Exit 0 on any scannable store (corrupt records are classified, not fatal); a missing store is a clean 0-record listing. The offline smoke-meta-prune gate builds a synthetic store covering every class and proves correct classification, exit 0, and the no-deletion invariant (wording + on-disk file count unchanged); it is in pnpm check.

Added (0.10.0 meta-bridge — Phase 3 statusline)

  • Repo-owned Claude Code statusline with garden identity. `scripts/meta-br...
Read more