Releases: junghan0611/entwurf
Release list
v0.12.7
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, andentwurf meta-bridge-prunedispatch throughrun.sh, whoseREPO_DIRsits undernode_modulesonce installed — so each one executed a raw.tsand died onERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING.new-session-idis the aliasdocs/setup-clean-host.mdtells operators to run, anddoctor-pi-provideris 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.tsentrypoints inrun.shnow route through a singlerun_tshelper 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.tsor exiting 0 as a silent no-op. - The install surface is now verified, not assumed.
check-pack-installpacked 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 undernode_modulesand asserts MEANING, not the absence of a crash: the session id must matchSESSION_ID_RE, the doctor must reach its own verdict body, and prune must walk the 0-record store it was handed. A new staticcheck-install-surfacecloses the other half:run_tsis the only fence crossing (S1), every operator subcommand has a compiled twin whether it callsrun_tsdirectly or through a helper — the house style (S2), no npm bin points at a raw.tsand every.shbin that execs one branches onnode_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-.tsbin, 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)inpermissions.allow, so a host whose operator had granted a broadmcp(*)was told the bridge was "registered and unusable — agy prompts on EVERY entwurf_v2 call". That was false: agy matchesmcp(*)andmcp(<server>)against our tool, which the doctor already knew — it read exactly that coverage in thedeny/askdirection to detect shadowing, and then refused to read it in theallowdirection. 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-configensure_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
HOMEto/tmpbut inherited the operator's realXDG_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 failFOREIGN TARGETwhen their normalized managed path differs from the live target, and failCORRUPTwhen 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-citizenredirectedPI_CODING_AGENT_DIRto fake settings but leftXDG_DATA_HOMEreal. Itsrun.sh remove-user-scopedrive therefore consumed the operator's real ownership state, followed that state's recordedmanagedSettingsPath, removedentwurfProvider.mcpServers.entwurf-bridgefrom the real~/.pi/agent/settings.json, and deleted the real state. This made the final bundled-MCP LIVE gate fail withConnected MCP servers: (none registered)afterpnpm checkhad 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/entwurftree, 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 anexportone line up walked the identical leak past a green gate (mutation-proven), and its command list blessedinstall/setupdrives that sandboxPI_CODING_AGENT_DIR— which those commands ignore, sinceensure_agent_dir_symlinkshard-codes$HOME/.pi/agentand 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 sandboxexport XDG_DATA_HOMEcounts 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-installno longer leaks into the operator's real XDG roots — and proves it on every return path. The gate swappedHOMEper drive but inherited the operator's exported XDG roots, so itsrun.sh installdrive wrote a foreign pi-provider install-state into the real~/.local/share/entwurfand 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: insiderun.shitself, where S5/S5b (which scan onlyscripts/*.sh) cannot see. Every sandbox drive now exportsXDG_DATA_HOME/XDG_STATE_HOME/XDG_CACHE_HOMEalongsideHOME. 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, producingcache-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 restoredsource=assembled=installedparity and a fresh Claude probe automatically birthed garden id20260714T121134-5effc4(record count 116→117).
Changed
- Backend drift pins moved, each with an explicit verdict. agy
1.0 → 1.1after live re-verification on the new minor (2026-07-14, agy 1.1.0:entwurf_selfwithout a permission prompt, bidirectional native-push reply on the same gid,LIVE=1 smoke-agy-native-push-live13/13 — evidence inDELIVERY.md§Antigravity). codex0.136 → 0.144as 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 andDELIVERY.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 checkis a dev-clone floor by construction: every fence bug this repo has shipped was green on it.check-pack-installwas 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-surfaceS5 flags an offline smoke that writes a live~/.claude/~/.gemini/~/.pipath 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 allexport HOMEto a sandbox first, andsmoke-resident-garden-guard'srm -rftargets amktemp -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...
v0.12.6
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 ornode_modulestree is now only the source origin. This removes the class wherepnpm 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.tsvs compiled.jshook 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
assembledMarketplacePathis the SSOT for uninstall, doctor, and state checks. Uninstall validates the full…/entwurf/meta-bridge/.assembledsuffix before removing Claude plugin/MCP registrations or deleting state, so malformed paths such as/or…/entwurf/meta-bridge/not-assembledfail loud with zero Claude side effects and leave state/artifacts intact.state.py checkalso 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 setupregistersentwurfin user-scope pi settings so--entwurf-controland the provider surface load from foreign cwd sessions, not only from a project-local.pi/settings.json. A hermeticsmoke-user-scope-citizencovers 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
packageManagerpin and.npmrc, moved pnpm policy topnpm-workspace.yaml, updated CI to pnpm 11.9, and treats./run.sh setupas the single local install path instead of a splitpi installflow. - The next delivery lane is pinned before implementation.
NEXT.mdnow 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 checkpassed 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-statepassed with the new direct install→XDG proof, recorded-path mismatch case, corrupt basename/missing-field fail-loud cases, and checkout-internal.assembledboundary fingerprint../run.sh doctor-meta-bridgepassed 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-installpassed 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.ddCcmzpassed on 2026-07-03; log/tmp/pi-tmux-entwurf-release-gate-0126.log; summaryMUST: PASS=17 FAIL=0 SKIP=0, advisoryBEHAVIOR: PASS=0 FAIL=1(smoke-resident-garden-guardpositive/gnew T3model-in-loop identity turn; non-blocking).
v0.12.5
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 belownode_modules(a global npm/pnpm install), Claude executes that.tsdirectly 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-bridgenow emitsdist/pi-extensions/meta-bridge-hook.jsand the installer copies that compiled closure (dev clones still run the.tsfor transparent editing), mirroring the same installed-vs-dev splitstart.sh(0.12.1) and the store-doctor (0.12.4) already use.check-pack-installnow runs the installed compiled hook from undernode_moduleswith plainnodeand proves a raw.tsat the same location is refused — the exact fence that broke real hosts is a green regression. The doctor's writer-version parity comparesmeta-session.<js|ts>by install mode so a compiled bundle no longer false-STALEs against the.tssource. - Installed meta-bridge paths are now stable across package upgrades. The installed statusline uses a new
entwurf-statuslinebin shim (matching the existingentwurf-bridgeMCP shim), and installed meta-bridge marketplace assemblies move out of versioned pnpm store paths into a version-stable operator data directory. Package upgrades still requireentwurf install-meta-bridgeto refresh the Claude plugin bundle/cache, but old-store dead links no longer strandstatusLineor 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.jsruns from undernode_moduleswith plainnode, while the raw.tsat the same location fails specifically withERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING. ./run.sh check-packand./run.sh check-pack-installpassed on 2026-07-01 with the hook-JS tarball requirements, bakedhooks.json.jsassertion, and raw-.tsfence reproduction.pnpm checkpassed on 2026-07-01 after the hook-JS + stable installed-path changes../run.sh install-meta-bridge && ./run.sh doctor-meta-bridgepassed from the development clone after re-materializing the gitignored.assembledbundle.
v0.12.4
Fixed
- Installed
doctor-meta-bridgeno longer false-fails on node_modules strip-types. The doctor previously ranscripts/meta-bridge-store-doctor.tsandscripts/check-entwurf-v2-surface.tsthrough Node strip-types even from an npm/pnpm-installed package. Node refuses type stripping undernode_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 prebuiltmcp/entwurf-bridge/dist/scripts/meta-bridge-store-doctor.jswith plainnodeand defer the repo-only v2 source-shape gate after confirming the shipped source and runtime wiring; development clones keep the raw.tsgates. - The store-doctor helper now ships as a node_modules-safe dist artifact. The bridge build emits
dist/scripts/meta-bridge-store-doctor.jsalongside the MCP bridge closure, reusing the already-shippedmeta-session.jsdependency without adding a new runtime package lane.check-pack/check-pack-installrequire the artifact in the tarball and prove it scans a real fixture store undernode_moduleswith plainnode. - 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_STRIPPINGas a pre-0.12.4 or broken-tarball signal.
Verification
pnpm checkpassed on 2026-07-01 after the doctor/install-floor fix../run.sh check-pack-installpassed 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.PliLsdpassed on 2026-07-01; log/tmp/pi-tmux-entwurf-release-gate-0124.log; summaryMUST: PASS=17 FAIL=0 SKIP=0, advisoryBEHAVIOR: PASS=0 FAIL=1(smoke-resident-garden-guardpost-/gnewautonomousentwurf_selfidentity turn; non-blocking). hejdev6real floor host reproduced the pre-fix strip-types failure from an installed package and passed after installing the patched tarball: compiled store-doctor scanned1 record(s)with plainnode, and installeddoctor-meta-bridgereported store-scan ok plus v2-surface deferred.
v0.12.3
Changed
- Claude ACP dependency lane refreshed for Sonnet 5. The ACP runtime pins now use
@agentclientprotocol/claude-agent-acp@0.54.1with@agentclientprotocol/sdk@1.1.0, while keeping the direct@anthropic-ai/sdk@0.100.1peer-resolution pin. The pi adapter floor is now@earendil-works/pi-* >=0.80.3 <0.81, matching the runtime catalog that exposesclaude-sonnet-5. - Curated Claude surface moves from Sonnet 4.6 to Sonnet 5.
claude-sonnet-5replacesclaude-sonnet-4-6across 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.xstate, 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.
enableWorkflowsandworkflowKeywordTriggerEnabledare 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-codexprovider only;entwurf/gpt-5.4andentwurf/gpt-5.5are removed until an ACP Codex backend exists.
Verification
pnpm checkpassed on 2026-07-01 after the dependency/model/doc updates; prep log/tmp/pi-tmux-entwurf-prep-check-0123.logended withEXIT=0.LIVE=1 ./run.sh smoke-acp-raw-turn-livepassed withclaude-sonnet-5and exited promptly after PASS.LIVE=1 ./run.sh smoke-acp-provider-livepassed withclaude-sonnet-5through the pi provider path.LIVE=1 ./run.sh smoke-acp-session-reuse-livepassed 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.ZcRc8wpassed on 2026-07-01 withMUST: PASS=17 FAIL=0 SKIP=0andBEHAVIOR: PASS=1 FAIL=0; log/tmp/pi-tmux-entwurf-release-gate-0123.log.
v0.12.2
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.jsondescriptionpassed on Claude 2.1.195 but failed on the floor host's 2.1.97 withUnrecognized 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-bridgebin instead of$REPO/mcp/entwurf-bridge/start.sh, whose$REPOcan be a.pnpm/@junghanacs+entwurf@.../node_modules/...hash path that goes stale on version or peer changes. Development clones still pin to that clone'sstart.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 assertsmeta-bridge-state.py::desired_mcp()chooses the installed-vs-clone MCP wiring without invoking a real Claude CLI. The gate is wired intopnpm checkso future decorative closed-schema keys fail before release.
Verification
pnpm checkpasses with the newcheck-meta-manifest-schemastatic guard included../run.sh smoke-meta-install-statepasses, proving the state manager/doctor consumers still followdesired_mcp()without drift.- Installed-location regression probe passes: running
scripts/check-meta-manifest-schema.pyfrom a syntheticnode_modules/@junghanacs/entwurfpackage no longer self-fails. - Claude Code 2.1.97 floor validation passed on
hejdev6after removing the root marketplacedescription; current Claude 2.1.195 validation also passes with warnings only. LIVE=1 ./run.sh release-gate /tmp/psa-release-gate-0.12.2.NLGhetpassed on 2026-06-29 withMUST: PASS=17 FAIL=0 SKIP=0andBEHAVIOR: PASS=1 FAIL=0; log/tmp/entwurf-release-gate-0.12.2-20260629T205339.log.
v0.12.1
Fixed
- Installed MCP bridge boots from npm/node_modules. The 0.12.0 launcher ran
src/index.tsthrough Node strip-types, but Node refuses type stripping belownode_modules(ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING). The npm tarball now carries a clean tsc-emitted bridge JS closure undermcp/entwurf-bridge/dist/, andstart.shruns that dist path when installed while keeping the strip-types source path for development clones. - Stale dist files cannot ship.
build-bridgeremovesmcp/entwurf-bridge/distbefore emit, andcheck-pack-installplants a stale sentinel beforenpm packto 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, sonode_modules/.bin/entwurfandnode_modules/.bin/entwurf-bridgework like direct package paths. - Two live ACP MUST smokes no longer flake on model-in-loop phrasing.
smoke-acp-carrier-augment-liveproved augment delivery by asking the model to echo aSECRET_PROJECT_CODEplanted in a/tmpAGENTS.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-liveasked 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 truncatedsocketState; 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 theentwurf/entwurf-bridgebins, 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 andtypeboxare 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_v2instead of hand-picking transports.
Verification
./run.sh check-pack-installpasses with the neutral npm install regression: package bins present, optional pi peers absent, installed dist bridge answerstools/list, and pi-loader registration still passes on the explicit pi-peer lane.- Remote
hejdev6real-install probe from the packed tarball passed: local npm install, package bins, optional pi peers absent, installedentwurf-bridgetools/list,entwurf installwith isolated HOME, andentwurf check-bridge. The host's real HOME also exposed a pre-existing stale~/.pi/agent/entwurf-targets.jsonsymlink topi-shell-acp; fix withentwurf setup:links --forceor an explicitENTWURF_TARGETS_PATHif that old registry is intentional. LIVE=1 ./run.sh release-gate /tmp/psa-release-gate-0.12.1.GUFDUbtiersMUST: PASS=17 FAIL=0 SKIP=0withBEHAVIOR: PASS=1 FAIL=0on 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 fromMUST FAIL=1to green; the install change is harness-neutral and touches no ACP code path).
v0.12.0
This release hard-cuts the project from
pi-shell-acptoentwurf. It is not a compatibility rename: the package/provider/model/MCP identity is nowentwurf, v1 entwurf verbs are gone, andentwurf_v2is 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, andLIVE=1 ./run.sh release-gate <scratch>MUST tierPASS=17 FAIL=0 SKIP=0on 2026-06-29, with BEHAVIOR (advisory model-in-loop autonomous MCP tool-selection)PASS=1 FAIL=0and recorded separately from the cut decision.
Added
entwurfpackage/provider identity. Package metadata, provider id, model prefix, MCP server name, repo URL, env namespace, install surface, and package-source resolver now useentwurf/@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 installnow preflights broken/missingnode_modulesand fails loud before writing settings.check-install-preflightlocks the relocation/reinstall gap found after the repo move. - Published-package smoke.
check-packandcheck-pack-installpack the release tarball, install it into a fresh temp project with pi 0.80.x peers, and prove the pi loader registers theentwurfprovider and curated Claude model anchors. - ACP plugin on the v2 core. Provider registration via
pi-extensions/acp-provider.tspluspi-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
entwurfnaming surface; stalepi-tools-bridgeMCP 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-controlpi 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), withgetModelsreached through the loader-compatible/compatentrypoint. Claude ACP moved to@agentclientprotocol/claude-agent-acp@0.50.0and@agentclientprotocol/sdk@0.29.0; model forcing usessession/set_config_option(configId="model"). The deprecatedClientSideConnectionpath was replaced by the fluentclient().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 pathcheck-package-source-routingpins); thegit:source path and a local clone are documented as alternatives for trackingmainor hacking on the bridge. - Release hero refreshed for the entwurf identity. The README hero and pi package gallery image now use a GLGMAN Universe
entwurfrelease-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.mdwas reduced from 621 to 260 lines andBASELINE.mdfrom 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_resumereferences in current-source comments and the absorbedNEXT--acp-on-v2.mdbranch 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-nativeentwurf_send,spawn_async_resume, and/entwurf*commands are removed. Current surface:entwurf_v2,entwurf_peers,entwurf_self,entwurf_inbox_read, plus/entwurf-sessionsand/gnewon the pi adapter. - Legacy
pi-shell-acpruntime 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 retiredentwurf_sendtool. - Branch-lane handoff pointers no longer dangle.
AGENTS.mdnow points at the main-laneNEXT.mdrather than a deleted branch ledger. - npm-managed installs resolve hoisted runtime dependencies before writing settings.
run.sh installwalks Node's module-resolution paths, sopi install npm:@junghanacs/entwurfworks from~/.pi/agent/npm/node_moduleswith no package-localnode_modules; the@earendil-works/pi-*peer trio is treated as loader-provided.check-pack-installlocks the layout with a real npm-managedrun.sh installregression under an isolated HOME.
Deferred
- npm publish / old package deprecation — maintainer-owned final step after the release cut.
package.jsonis already@junghanacs/entwurfwithpublishConfig.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_sendflow, 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-freshlane. - 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 split —
smoke-acp-bundled-mcp-livestill exercises a model-in-loopentwurf_selfcall 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.0tag was realigned to3360b3bbefore npm publication so the GitHub release tag and the npm tarball can describe the same source tree. - Removed stale Biome
noExplicitAnysuppression comments from ACP session-reuse verification scripts;pnpm lintnow 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
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 v1entwurfverb (0.12 cutover lane); thespawn-bg resumerow 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 + MCPentwurf_v2tool. On top of it the spawn-bg resident lifecycle — the headline guarantee of #35 — was proven LIVE for the first time (a realpi --entwurf-controlchild 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 flatLIVE=1 → PASS=18run (log…20260615T152058) predates BOTH changes and no longer describes the current tree. FreshLIVE=1 release-gateon 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 issentinel(cell4/5S7Bash-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 (theentwurf_peersfact 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, plusdoctorsurface sanity and thePI_ENTWURF_PREFIX_ROOTSoperator-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-matrixis the reachability + lock-policy table;smoke-entwurf-v2-matrix-liveis a 3-cell LIVE sentinel (control / mailbox / guard) wired intorelease-gateafter the bridge check with an honestLIVE!=1skip.
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 detachedpi --entwurf-controlchild 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 detachedspawn()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, butmaybeSetResidentNamerefused anyentwurf-tagged resident (the tag is theentwurf_resumemarker), so the resume self-terminated before doing any work. Resolved by narrowing the invariant, not dropping it: operator residents still reject theentwurftag, 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:isV2OnlyModetrue only for exact"1",checkV1EntwurfAllowedpure return,assertV1EntwurfAllowedthrow wrapper) is the single source of truth; whenPI_SHELL_ACP_V2_ONLY=1, each of the 10 v1 entrypoints (9 surface groups —/entwurftool + command count as two) refuses before any side effect through its own existing hard-refusal channel (tool throw, command notify, isError result, RPCrespond(false), startup error report, MCPtextErr). The MCPentwurf_resumehandler and the control RPCspawn_async_resumeare 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 theentwurf_v2core stays flag-clean.
Added (0.11.0 entwurf v2 — fact surface)
listEntwurfFacts/entwurf_peers— the entwurf fact surface (4-value liveness). A purePeerFactcore (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_v2contract surface was frozen and gated before the verb was wired (step 4-pre). project_trusthandler + inherited-trust preflight (Trust 2층). The pi trust preflight carries inherited-trust evidence and a deny formatter (F5a/N3b); aproject_trusthandler 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_sendmailbox-fallback gates, amailboxConversationalDeliverablepredicate, 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_selfstop reporting false delivery to unreachable targets. Previously a send to a citizen with no live control socket, or areplyable: trueself-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✓ deliveredsignal and stray.msgmailbox files behind. Now such targets are rejected honestly (no enqueue, no mailbox garbage), andentwurf_selfreportsreplyable: trueonly when a real inbound path exists. This is an observable change forentwurf_send/entwurf_selfconsumers, 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_v2now reaches a record-less but live pi control socket (socket-only fire-and-forget target).entwurf_peerslists a livepi --entwurf-controlsession as alive whether or not it has a meta-record, but the v2 productionresolveTargetpreviously rejected any record-less gid asbad-target— so anentwurf_v2to an operator-greeted live pi peer failed while a legacyentwurf_sendto the same gid succeeded (a fact-surface ↔ dispatch contract gap; thebad-targetwas 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 sharedsocket-discoveryclassifier, so l...
v0.10.0
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_sendlearns 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-scopepi-tools-bridgeMCP has noPI_SESSION_ID, so the bridge resolves identity from a sender marker theSessionStarthook writes, keyed by the shared Claude parent pid (process.ppidof the MCP child IS the Claude process the hook ran under — never cwd inference;PI_META_SENDER_MARKERoverrides for tests). A trusted marker promotes the send to a replyablemeta-sessionsender addressed by its garden-id. The marker carriesownerPid+ a boot-uniqueownerStartKey, 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 anonymousexternal-mcp— "if we don't know who sent it, we don't send it." The sender envelopeorigingains"meta-session"; the receiver render shows a[meta-session]badge. Deterministic E2E insmoke-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→.readarchive +lastReadAtreceipt) is covered E2E bysmoke-meta-mailbox. Both gates are inpnpm check.entwurf_resumeasync-capability discriminant fixed:origin === "pi-session", notreplyablealone. A meta-session isentwurf_send-replyable (it owns a garden-id mailbox) but has no pi control socket, so it cannot host an async-resumefollowUpany more than an external host can. The old auto-resolve keyed onreplyableand so routed a meta-session resume into a control-socket lookup that always fails. The async discriminant is nowasyncCapable = replyable && origin === "pi-session": a meta-session (like an external host) auto-resolves to sync, and an explicitmode="async"from either is rejected with the canonical reason text.check-async-resume-gateadds 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'sstate.py checkis 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 lateragent-configjq merge (.[0] * .[1], which replaces arrays and overwrites scalars) cannot silently clobber pi-owned policy or vice-versa.smoke-meta-keyset-guardproves a disjoint fragment passes and exact/array/parent-child collisions fail loud while unrelated sibling keys (permissions.defaultMode,language) stay clean; inpnpm check.
Fixed (0.10.0 meta-bridge — cut-time identity symmetry)
entwurf_selfcloses 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 whenPI_SESSION_ID/PI_AGENT_IDwere absent — even when the same MCP process had a trusted sender marker.entwurf_selfnow uses the same authoritative identity resolver asentwurf_send: pi sessions returnorigin="pi-session"+socketPath; trusted meta-sessions returnorigin="meta-session",agentId="meta-session/claude-code",replyable=true, the garden id, andmailboxPath; 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 insmoke-meta-sender-identity, and the fix was live-confirmed fromagent-configvia native Claude Code (entwurf_selfreturned the session's garden id andreplyable: true).
Fixed (0.10.0 meta-bridge — doctor fail-loud drift detail)
doctor-meta-bridgesurfaces a managed-config drift instead of dying silently. The[managed config state]section captured thestate.py checkoutput with a bareCHECK_ERR="$(… check …)"assignment underset -euo pipefail. The instantcheckexited nonzero (drift), the assignment trippedset -eand 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 anif, so its nonzero status is consumed (not a death) and the drift detail + the entire rest of the chain always print. A hermetic regression insmoke-meta-install-stateforces 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) printsDrift detail:, (c) names the concrete drifted key, and (d) runs to its final summary line — i.e. no earlyset -edeath 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-sessionasserts the level-triggered body-drain robustness basis (#34). The wake signal (inbox.signal) is edge-triggered — rapid pokes can coalesce into oneFileChanged, or a signal can be lost entirely — but each message body is a separate level-state file on disk, so onereadMetaInboxdrains the whole directory. #34 names this theD8robustness basis and asks the gate to assert it, not just letDELIVERY.mdclaim 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 onelastReadAtreceipt 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-bridgereds on corrupt JSON / duplicatenativeSessionId/ 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 scansdefaultMetaSessionsDir()(override with a positional dir; stale window via--ttl-days, default 30) and classifies every record into orphan (parse OK buttranscriptPathno 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,lastSeenolder 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 manualrmcommands for orphan/stale candidates and deletes nothing — no--applyin 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 offlinesmoke-meta-prunegate 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 inpnpm check.
Added (0.10.0 meta-bridge — Phase 3 statusline)
- Repo-owned Claude Code statusline with garden identity. `scripts/meta-br...