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 variables or built inside an embedded heredoc would slip past it. The real guarantee — running the whole offline floor under a swapped HOME — is recorded inNEXT.mdas open, not claimed here. S5b (review follow-up) pins the axis the 2026-07-14 pollution actually used: any offline smoke that swaps HOME into a sandbox must swapXDG_DATA_HOMEwith it, because HOME alone still writes real install-state below the inherited XDG root. -
Install/package hygiene guards now seal three post-0.12.6 edges. Package tarballs exclude Python bytecode residue even though
scripts/ships as a whole, pack gates serialize the fullnpm packdist-read window and use per-run tarball destinations, and the user-scope pi package inverse is exposed as explicitremove-user-scopewith a read-only--remove --dry-runpreview.