@junghanacs/entwurf 0.14.1 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/AGENTS.md +8 -5
  2. package/CHANGELOG.md +113 -0
  3. package/DELIVERY.md +73 -3
  4. package/README.md +55 -4
  5. package/VERIFY.md +6 -6
  6. package/demo/README.md +3 -1
  7. package/demo/demo-baseline.sh +12 -1
  8. package/demo/demo.sh +9 -1
  9. package/docs/acp-backend-rail.md +103 -4
  10. package/docs/external-mcp-host.md +26 -4
  11. package/docs/setup-clean-host.md +3 -3
  12. package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +8 -6
  13. package/mcp/entwurf-bridge/dist/pi-extensions/entwurf-capabilities.json +1 -0
  14. package/mcp/entwurf-bridge/dist/pi-extensions/lib/copilot-fresh-preflight.js +253 -0
  15. package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-sender-identity.js +10 -2
  16. package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js +102 -28
  17. package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +77 -11
  18. package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-hook-copilot.js +288 -0
  19. package/mcp/entwurf-bridge/dist/scripts/doctor-pi-provider.js +139 -47
  20. package/mcp/entwurf-bridge/dist/scripts/probe-bridge-command.js +294 -0
  21. package/mcp/entwurf-bridge/src/index.ts +10 -6
  22. package/mcp/entwurf-bridge/tsconfig.build.json +17 -6
  23. package/package.json +23 -12
  24. package/pi/copilot-receive/entwurf-receive/extension.mjs +323 -0
  25. package/pi/entwurf-capabilities.json +1 -0
  26. package/pi/meta-bridge-copilot/.claude-plugin/marketplace.json +11 -0
  27. package/pi/meta-bridge-copilot/entwurf-meta-receive-copilot/.claude-plugin/plugin.json +5 -0
  28. package/pi/meta-bridge-copilot/entwurf-meta-receive-copilot/hooks/hooks.json +7 -0
  29. package/pi/meta-bridge-copilot/entwurf-meta-receive-copilot/scripts/copilot-hook-launch.sh +85 -0
  30. package/pi-extensions/entwurf-control.ts +17 -8
  31. package/pi-extensions/lib/acp/backend.ts +229 -9
  32. package/pi-extensions/lib/copilot-fresh-preflight.ts +251 -0
  33. package/pi-extensions/lib/meta-sender-identity.ts +10 -2
  34. package/pi-extensions/lib/meta-session.ts +102 -29
  35. package/pi-extensions/lib/mux-fresh-call.ts +82 -11
  36. package/pi-extensions/meta-bridge-hook-copilot.ts +323 -0
  37. package/run.sh +389 -26
  38. package/scripts/agy-bridge-config.py +47 -13
  39. package/scripts/agy-bridge.sh +73 -23
  40. package/scripts/check-acp-prompt-lifecycle.ts +221 -9
  41. package/scripts/check-copilot-birth-hook.ts +497 -0
  42. package/scripts/check-copilot-launch.ts +395 -0
  43. package/scripts/check-copilot-receive-arm.ts +734 -0
  44. package/scripts/check-copilot-statusline.ts +122 -0
  45. package/scripts/check-entwurf-bridge-boot.ts +28 -0
  46. package/scripts/check-entwurf-capabilities.ts +25 -6
  47. package/scripts/check-fresh-cut-gate.sh +92 -23
  48. package/scripts/check-gate-qualification.ts +10 -3
  49. package/scripts/check-install-container.sh +2 -2
  50. package/scripts/check-install-surface.ts +1 -1
  51. package/scripts/check-meta-capability-source.ts +89 -5
  52. package/scripts/check-meta-doctor-oracle.sh +26 -0
  53. package/scripts/check-meta-manifest-schema.py +38 -0
  54. package/scripts/check-meta-receiver-marker.ts +25 -2
  55. package/scripts/check-meta-session.ts +32 -0
  56. package/scripts/check-probe-bridge-command.ts +201 -0
  57. package/scripts/check-release-gate-outcomes.ts +54 -1
  58. package/scripts/copilot-bridge-doctor.sh +209 -0
  59. package/scripts/copilot-bridge-install.sh +224 -0
  60. package/scripts/copilot-launch.sh +236 -0
  61. package/scripts/copilot-mcp-bridge.sh +177 -0
  62. package/scripts/copilot-mcp-config.py +221 -0
  63. package/scripts/copilot-receive-bridge.sh +483 -0
  64. package/scripts/copilot-statusline-bridge.sh +149 -0
  65. package/scripts/copilot-statusline-config.py +201 -0
  66. package/scripts/copilot-statusline.sh +68 -0
  67. package/scripts/dev-bin.sh +4 -1
  68. package/scripts/doctor-pi-provider.ts +155 -51
  69. package/scripts/meta-bridge-hook-log.sh +17 -5
  70. package/scripts/mutants/acp-prompt-lifecycle.json +25 -3
  71. package/scripts/mutants/bridge-command-boot.json +107 -0
  72. package/scripts/mutants/capability-cache.json +64 -0
  73. package/scripts/mutants/copilot-birth.json +155 -0
  74. package/scripts/mutants/copilot-launch.json +187 -0
  75. package/scripts/mutants/copilot-receive.json +263 -0
  76. package/scripts/mutants/fresh-cut.json +17 -0
  77. package/scripts/mutants/mux-fresh-call.json +216 -2
  78. package/scripts/mutants/pack-install.json +17 -0
  79. package/scripts/mutants/release-gate.json +13 -0
  80. package/scripts/probe-bridge-command.ts +330 -0
  81. package/scripts/raw-async-delivery/README.md +203 -1
  82. package/scripts/raw-async-delivery/copilot-enqueue-addressed.sh +35 -0
  83. package/scripts/raw-async-delivery/copilot-extension-receive/extension.mjs +123 -0
  84. package/scripts/raw-async-delivery/copilot-ui-server-probe.mjs +337 -0
  85. package/scripts/smoke-acp-raw-turn-live.ts +1 -1
  86. package/scripts/smoke-agy-install-state.sh +76 -2
  87. package/scripts/smoke-copilot-mcp-state.sh +153 -0
  88. package/scripts/smoke-copilot-statusline-state.sh +131 -0
  89. package/scripts/smoke-entwurf-chain-live.ts +1 -1
  90. package/scripts/smoke-entwurf-v2-matrix-live.ts +2 -2
  91. package/scripts/smoke-mux-fresh-call-live.ts +3 -1
  92. package/scripts/smoke-mux-lifecycle-live.ts +4 -2
  93. package/scripts/smoke-pi-provider-state.sh +135 -6
  94. package/scripts/smoke-resident-garden-guard.sh +2 -2
  95. package/scripts/tsconfig.json +1 -0
package/run.sh CHANGED
@@ -120,7 +120,7 @@ usage() {
120
120
  cat <<'EOF'
121
121
  Usage:
122
122
  ./run.sh setup [project-dir] # ONE confident install: pnpm install + install + meta-bridge (if native harness) + v2 install smoke (LIVE substrate = release-gate)
123
- ./run.sh release-gate [project-dir] [--cut] [--allow-skip-gemini] # SINGLE release gate: full static (pnpm run check:full) + the v2-native live gates (v2 matrix-live, check-bridge, RGG) + the ACP plugin acceptance floor (12 LIVE smokes: socket-citizen/raw-turn/overlay/provider/session-reuse/carrier-augment/memory-containment/rgg/mcp/skill/bundled-mcp/v2-send) + the one surviving axis the aggregate used to omit silently (claude-native-resume; Cortex stays a documented on-demand direct call) + the cross-harness delivery chain (smoke-entwurf-chain-live). TWO-TIER summary: MUST (release-blocking, owns the exit code — "green" applies here) + BEHAVIOR (advisory, non-blocking: RGG positives model-in-loop turn). STEP OUTCOME protocol: every step is INVOKED and reports its own PASS / SKIP (exit 97, a prerequisite it does not have) / FAIL — a skip is never counted as a pass. Without --cut this is the unattended diagnostic (SKIPs reported, exit 0). WITH --cut it is read as release acceptance and ANY MUST SKIP is red, which is what makes "a CUT needs LIVE=1, SKIP=0" executable instead of prose. --allow-skip-gemini accepted-but-ignored (back-compat). final cut authorization is GLG's.
123
+ ./run.sh release-gate [project-dir] [--cut] [--allow-skip-gemini] # SINGLE release gate: full static (pnpm run check:full) + the v2-native live gates (v2 matrix-live, check-bridge, doctor-pi-provider, RGG) + the ACP plugin acceptance floor (12 LIVE smokes: socket-citizen/raw-turn/overlay/provider/session-reuse/carrier-augment/memory-containment/rgg/mcp/skill/bundled-mcp/v2-send) + the one surviving axis the aggregate used to omit silently (claude-native-resume; Cortex stays a documented on-demand direct call) + the cross-harness delivery chain (smoke-entwurf-chain-live). TWO-TIER summary: MUST (release-blocking, owns the exit code — "green" applies here) + BEHAVIOR (advisory, non-blocking: RGG positives model-in-loop turn). STEP OUTCOME protocol: every step is INVOKED and reports its own PASS / SKIP (exit 97, a prerequisite it does not have) / FAIL — a skip is never counted as a pass. Without --cut this is the unattended diagnostic (SKIPs reported, exit 0). WITH --cut it is read as release acceptance and ANY MUST SKIP is red, which is what makes "a CUT needs LIVE=1, SKIP=0" executable instead of prose. --allow-skip-gemini accepted-but-ignored (back-compat). final cut authorization is GLG's.
124
124
  ./run.sh check-bridge # entwurf-bridge direct MCP smoke + protocol/negative-path test.sh (live substrate = v2 live smokes)
125
125
  ./run.sh check-entwurf-bridge-boot # deterministic gate (5d-5-pre, G1a/G1b/G1e/G1f, IN pnpm run check:full): boot start.sh under strip-types + assert v2 fence graph loads + entwurf_v2 and entwurf_resume_call registered/schema + the tools/list surface is EXACTLY the seven shipped garden verbs; tools/list only, no auth/side-effect
126
126
  ./run.sh check-entwurf-bridge-pi-free # deterministic gate (0.12.1 A, IN pnpm check): static — bridge index eager value-import closure must carry no @earendil-works/pi-* (type-only + dynamic import excluded); proves the meta-bridge boots pi-free
@@ -130,6 +130,10 @@ Usage:
130
130
  ./run.sh check-meta-session # deterministic gate (#30 step 2, V3-only): fs store — idempotent decideUpsert/upsertMetaSession + mailbox enqueue/read + receipt state, no API
131
131
  ./run.sh check-meta-v3-record # deterministic gate: the ONE live record schema (v3) — canonical serialize/round-trip/mint, foreign-generation rejections name fresh-cut with the actual version value, strict keyset, no API
132
132
  ./run.sh check-mailbox-receipt-state # deterministic gate (0.11 Stage 0 step 3B): mailbox receipt state schema + store (stamp→persist→read-back) in a temp mailbox, strict keyset, no API
133
+ ./run.sh check-copilot-birth-hook # #82 gate: drives the real Copilot assembler into a temp dir, fires the baked launcher with NO ARGV (the way Copilot's `exec`-string schema forces), and requires a backend:"copilot" v3 record + attach + peer row + a SENDER marker the resolver joins back to that record, and still zero mailbox/receiver marker (who-sent needs a shared parent; a receiver needs a doorbell this backend has not got). Hermetic; no Copilot, no model turn
134
+ ./run.sh check-copilot-receive-arm # #82 RAIL 5 gate: the REAL receiver installer + the REAL extension.mjs forked with a stubbed SDK. Arms only after birth, marker owned by the WATCHER pid, self-fetch dispatch answer, doorbell carries the garden id and NOT the body, id-drift/foreign-parent refusals. Hermetic; no Copilot, no model turn
135
+ ./run.sh check-copilot-launch # #82 RAIL 7 gate: the MANAGED launch `entwurf copilot`, driven through its public address against a FAKE VENDOR on a sandbox PATH. Receiver precondition refusals, EXTENSIONS token + operator token preservation, injected defaults before the `--` terminator, byte-identical argv, the 11 explicit permission/surface policy overrides that suppress `--yolo`, exec (not fork) pid identity, exit passthrough, recursion refusal. Hermetic; no Copilot, no model turn
136
+ ./run.sh check-copilot-statusline # #82 Copilot custom-footer renderer. session_id → ready/?/gid + rail `cop`; exit 0. IN pnpm check. No Copilot, no model turn
133
137
  ./run.sh check-entwurf-capabilities # deterministic gate (0.11 Stage 0 step 3C): backend capability registry (pi/entwurf-capabilities.json) — coverage==META_CITIZEN_BACKENDS + agrees with live META_BACKEND_DESCRIPTORS + strict keyset, no API
134
138
  ./run.sh check-capability-bundle-reach # deterministic gate (IN pnpm check): re-ask EVERY shipped copy of meta-session (source + bridge bundle emit) whether metaCapabilitiesFilePath() reaches the registry — the artifact-depth check the source-path gates cannot make; needs a built dist, missing dist FAILS
135
139
  ./run.sh smoke-pi-attach # deterministic gate (#50 C2 checkpoint + C3 ACP tail): a pi session attaches as a V3 meta-record citizen (backend:"pi"), the gardenId is the RECORD's not pi's session id, the control socket is keyed on it, a re-open ATTACHES to the same address (never a second mint), the BUILT DIST ENTRY driven over MCP stdio lists the citizen + delivers entwurf_v2 to that socket with an RPC ack, and the ACP identity chain lands a send AS the host record (enrichMcpServersWithEnvelope env → bridge sender = host gardenId). mkdtemp-isolated; the live store is never read
@@ -156,8 +160,8 @@ Usage:
156
160
  ./run.sh check-mux-launcher-fence # deterministic gate for the shared operator-launcher fence (issue #67): scripts/lib/claude-launcher-fence.ts + its wiring into BOTH mux LIVE smokes. Replants the observed install-destruction shape (real HOME + fixture XDG_DATA_HOME → self-update retargets the real `claude` launcher into the fixture tree, teardown deletes it) wholly inside disposable mkdtemp roots — the real launcher is never inspected. Pins fail-closed preflight, retarget/content-change detection before cleanup, removal BLOCKED on fixture reference / unproven safety / surviving tracked panes, exact operator-parity XDG restore (absent = DELETED, not canonical defaults), the lifecycle cell-branch topology, and one shared helper consumed by both smokes
157
161
  ./run.sh check-entwurf-v2-visible-resume # deterministic gate: S1 visible-resume COMPOSITION (entwurf-v2-visible-resume.ts) with every seam injected — the whole state machine incl. the timeout branch runs with no tmux/lock/socket/clock. Pins lock BEFORE liveness, identity under the lock and before any window (no-transcript citizen fails loud, opens nothing), live/indeterminate/address-conflict refused unlaunched, observation as a BOUNDED WAIT (measured: socket answers ~2–4s after launch, so one immediate probe would call a successful resume unobserved), exactly ONE launch on every path, timeout → lock released + window left open + nothing retried/killed, failed release throws, and the two receipts staying separate in type and text
158
162
  ./run.sh check-resume-launch-identity # deterministic gate for resume-launch-identity.ts, the record-authoritative launch-identity leaf preserved through the visible-first cut (spawn-bg and all its callers are gone; this leaf answers "which being is this, and which conversation is theirs"). Temp meta-store fixture: gardenId→record.transcriptPath happy path with header cwd/provider/model; C3 integrity (header id ≠ record.nativeSessionId → refused, never resumed); #52 ADDRESSABLE read (a gid that no longer holds its nativeSessionId alone is refused from EITHER side — the plain targeted read would resume one transcript twice under two locks); cause fidelity per impossible resume incl. the F7 pin (recorded-but-deleted transcript → MISSING, not "no recorded model"); header↔gate SSOT. No spawn/socket/timer
159
- ./run.sh smoke-entwurf-v2-matrix-live # LIVE sentinel (0.11 Stage 0 step 5d-5, D4-b) — OUT of pnpm check, needs LIVE=1. Drives REAL production runEntwurfV2 deps over REAL OS objects, 4 cells: C1 control-socket (real pi --entwurf-control resident → RPC send → lock acquire→release ×1), C1b record-less socket (#50 C4: live record-less pi → EVERY intent rejected pre-probe record-less-socket, no lock, rendered hint names record authority + fresh-cut), C2 meta-mailbox deliverable (armed self-fetch citizen → real .msg enqueue, lock-free), C3 meta-mailbox guard (no armed receiver → reject, no garbage). Model-in-loop OUT (transport/lock/enqueue gate, GPT Q2); negative/timeout stay deterministic. Model: ENTWURF_LIVE_TARGET=<provider>/<model> (default openai-codex/gpt-5.4). LIVE=1 ./run.sh smoke-entwurf-v2-matrix-live
160
- ./run.sh smoke-agy-native-push-live # 봉인 8 LIVE acceptance for the native-push (agy) rail — OUT of pnpm check, needs LIVE=1 + AGY_CONVERSATION_ID (a live agy conversation). Drives the REAL antigravity adapter + register core + runEntwurfV2 (production deps): doctor-static preflight (dangling→FAIL, the ③ gate), probe route, register create/attach idempotency, fire→native-push delivered, post-send re-probe (D7 partial), bogus-conv→native-push-probe-indeterminate. Meta-store isolated to a temp dir (only the agy round-trip is real; no real-store residue). LIVE=1 AGY_CONVERSATION_ID=<convId> ./run.sh smoke-agy-native-push-live
163
+ ./run.sh smoke-entwurf-v2-matrix-live # LIVE sentinel (0.11 Stage 0 step 5d-5, D4-b) — OUT of pnpm check, needs LIVE=1. Drives REAL production runEntwurfV2 deps over REAL OS objects, 4 cells: C1 control-socket (real pi --entwurf-control resident → RPC send → lock acquire→release ×1), C1b record-less socket (#50 C4: live record-less pi → EVERY intent rejected pre-probe record-less-socket, no lock, rendered hint names record authority + fresh-cut), C2 meta-mailbox deliverable (armed self-fetch citizen → real .msg enqueue, lock-free), C3 meta-mailbox guard (no armed receiver → reject, no garbage). Model-in-loop OUT (transport/lock/enqueue gate, GPT Q2); negative/timeout stay deterministic. Model: ENTWURF_LIVE_TARGET=<provider>/<model> (default openai-codex/gpt-5.6-luna). LIVE=1 ./run.sh smoke-entwurf-v2-matrix-live
164
+ ./run.sh smoke-agy-native-push-live # 봉인 8 LIVE acceptance for the native-push (agy) rail — OUT of pnpm check, needs LIVE=1 + AGY_CONVERSATION_ID (a live agy conversation). Drives the REAL antigravity adapter + register core + runEntwurfV2 (production deps): doctor-static preflight (dangling→FAIL, the ③ gate), probe route, register create/attach idempotency, fire→native-push delivered, post-send re-probe (D7 partial), bogus-conv→native-push-probe-indeterminate. Meta-store isolated to a temp dir (only the agy round-trip is real; no real-store residue). COST FENCE: open that agy conversation on gemini-3.6-flash (free account) — never a Pro tier; entwurf never selects the agy model and no assertion reads it. LIVE=1 AGY_CONVERSATION_ID=<convId> ./run.sh smoke-agy-native-push-live
161
165
  ./run.sh smoke-mux-lifecycle-live # RELEASE MUST integrated LIVE lifecycle acceptance for mux, through the REAL MCP surface — OUT of pnpm check, needs LIVE=1 and spends model turns (two pi siblings: native + recorded-ACP provider, each resumed once; one Claude Code sibling). tools/call fresh_call -> nonce callback sender envelope -> v2 control send landing in the sibling's own transcript -> resume_call REFUSED while live (window count unchanged) -> stable-handle close (pane gone, socket dead, record kept) -> dormant delivery refused honestly -> public entwurf_resume_call with LAUNCH and OBSERVATION receipts kept apart, same-gid socket alive, zero new citizens, zero lock residue, resumed pane_start_path == RECORD cwd (separate tmux query), transcript byte-identical across the resume -> v2 recall of the pre-close fact. claude-code resume refused target-not-pi, no window opened and no lock residue. LIVE=1 ./run.sh smoke-mux-lifecycle-live
162
166
  ./run.sh check-entwurf-facts # deterministic gate (0.11 Stage 0 step 4, fact-provider slice 1+2): PURE PeerFact core + resolveFactList union — R1 out-of-domain→unsupported, R3b socket-domain 4-value, facts-only keyset; union: PeerFact + RecordLessSocketFact by gardenId (#50 C4: record-less socket = diagnostic subject, gid+liveness only), dormant→dead, F3 indeterminate preserved, out-of-socket-domain+socket fail-loud; pure, no IO
163
167
  ./run.sh check-socket-discovery # deterministic gate (0.11 Stage 0 step 4, fact-provider slice 3): SOCKET-axis scanSocketProbes — probes (dir sockets) ∪ (in-domain citizen canonical paths) 3-valued; dormant citizen no-file → dead (resumable, not unprobed), stall → indeterminate (F3), dir hygiene/dedup/missing-dir + e2e → resolveFactList; readdir/probe injected, no IO
@@ -185,6 +189,8 @@ Usage:
185
189
  ./run.sh check-probe-ordering # §11-7 ordering-probe deterministic gate: raw-client SAMENESS pinned to backend.ts (sequence/args/timeouts/permission policy — the probe may never measure a lookalike), phase attribution incl. set-model, probe-mode fixture wire markers (delay honored, probeRunId REQUIRED, smoke-acp-mcp-live legacy compat), event-log door integrity (reserved keys refused at write; unknown marker name, broken sort axis, or a payload the classifier cannot judge on is MALFORMED, never a quiet event), and the §11-7 paired-verdict truth table (P0/I0 outside the space, phase-qualified D, B promotion ladder, C, A two-delay rule). Offline/deterministic; one product claim (no production prompt cutoff) is replant-qualified via scripts/mutants/probe-ordering.json, while the other assertions are direct [CHECK:*] contracts
186
190
  ./run.sh check-probe-cli-shim # §11-7-c B-name-snapshot PRODUCER gate: the CLI shim driven as a REAL process against fake CLIs (no API, no cost). Proves what a defect would buy — FABRICATED evidence (a malformed init is never reported as an empty name set; the boot report carries the true target path+sha256 the classifier verifies against the roster), a DESTROYED turn (byte transparency across mid-UTF8/CRLF/oversized/unterminated framing, exit-code fidelity, signal re-raise, inbound signal forwarding, stderr passthrough, stdout backpressure), and LEAKED operator state (exact-allowlist env scrub, no argv/env/prompt body in the log). Offline/deterministic; 20 direct [CHECK:*] contracts, deliberately no longer replant-qualified after #70 subtraction
187
191
  ./run.sh smoke-agy-statusline-state # agy ambient garden-id statusLine install/doctor/inverse regression. Offline/deterministic
192
+ ./run.sh smoke-copilot-statusline-state # Copilot custom-footer install/doctor/inverse regression. Offline/deterministic
193
+ ./run.sh smoke-copilot-mcp-state # Copilot MCP install/doctor/inverse regression. Offline/deterministic
188
194
  ./run.sh smoke-agy-hooks-state # agy PreInvocation birth/sender hook install/doctor/inverse + direct stdin→meta-record regression. Offline/deterministic
189
195
  ./run.sh smoke-user-scope-citizen # 0.12.6 install-boundary: pi packages[] registration SSOT (register-pi-package.py) — idempotent + preserves unrelated + normalizes stale + remove symmetry + fails loud. Offline/hermetic (deps: bash+python3)
190
196
  ./run.sh smoke-meta-prune # 1.0.0 meta-bridge Phase 4: listing-only store janitor regression gate — classify keep/orphan/stale/ambiguous, delete nothing. Offline/deterministic (deps: bash+node)
@@ -195,8 +201,21 @@ Usage:
195
201
  ./run.sh install-meta-bridge # INTERNAL part of `setup` (native-harness plugin) + doctor recovery path — prefer `setup`; stateful GLOBAL install (plugin + USER MCP + settings keyset, honest uninstall state)
196
202
  ./run.sh uninstall-meta-bridge # 1.0.0 meta-bridge Phase 2: stateful GLOBAL uninstall (restore only keys/items captured in install-state)
197
203
  ./run.sh doctor-meta-bridge # THE RELEASE ORACLE (#51, Linux-certified repair axis). exit 0 = every required layer was MEASURED on this Linux host: toolchain + state + plugin/MCP + resolved-artifact launch-form classification (all 3 owner hooks + doorbell static contract) + synthetic owner join + store scan + hook errors + SessionStart evidence + REQUIRED live MCP↔marker join + writer-version parity. Missing live evidence is NOT CERTIFIED (open a Claude session and re-run), never a pass; Darwin is not yet verified/certified and stays nonzero for this cut (future validation may reopen it). Detection power is held by check-meta-doctor-oracle
204
+ ./run.sh copilot [args...] # #82 RAIL 7: the MANAGED Copilot launch. exec()s the vendor CLI in THIS terminal (cwd/pid/exit preserved) with COPILOT_CLI_ENABLED_FEATURE_FLAGS=EXTENSIONS set for that one invocation — entwurf owns no part of your shell and writes nothing to it, but it owns the process it is about to become. Without that flag Copilot skips the extension scan SILENTLY. Refuses to launch unless the receiver unit is actually installed. Injects `--model auto` when no --model was given and `--yolo` when no explicit permission/surface policy flag was given, both BEFORE any `--`; every argument you pass is forwarded byte-identical. RUNNING THIS IS YOUR CONSENT to that profile — use plain `copilot` for stock vendor behaviour. Not tmux, not fresh-call, mints no citizen (birth is still the first prompt)
205
+ ./run.sh install-copilot-bridge # #82: GLOBAL install of the Copilot BIRTH plugin (own marketplace root; node+entry baked into the no-argv exec string). MCP wiring and the RECEIVER extension are separate install surfaces (install-copilot-mcp, install-copilot-receive). Also retires the stale Claude unit (--keep-stale-claude-unit opts out)
206
+ ./run.sh doctor-copilot-bridge # #82: fail-loud surface for that unit. Red = a hook that RAN and failed, or a broken/unbaked artifact. "Installed with zero records" is NOT red and is reported as NOT-YET: a Copilot session is born on its FIRST PROMPT, not when the window opens (measured)
207
+ ./run.sh install-copilot-statusline # own Copilot statusLine + footer.showCustom with an install-state preimage
208
+ ./run.sh uninstall-copilot-statusline # honest inverse of install-copilot-statusline
209
+ ./run.sh doctor-copilot-statusline # static ownership/config/bin doctor; never claims a render receipt
210
+ ./run.sh install-copilot-mcp # #82 RAIL 5: register ONE entwurf-bridge server in ~/.copilot/mcp-config.json (adopt / create / REFUSE symlink), type:local, install-state under $XDG_DATA_HOME/entwurf/copilot-mcp/
211
+ ./run.sh uninstall-copilot-mcp # honest inverse of install-copilot-mcp from install-state
212
+ ./run.sh doctor-copilot-mcp # static ownership/config/boot doctor; RED only when install-state exists
213
+ ./run.sh install-copilot-receive # #82 RAIL 5: install the RECEIVER extension (user scope ~/.copilot/extensions/entwurf-receive). Owns the artifact and CHECKS the launch flag; it never sets one (a launch does — see `./run.sh copilot`). Arms per session after birth
214
+ ./run.sh uninstall-copilot-receive # honest inverse from install-state; never removes a unit it did not install
215
+ ./run.sh doctor-copilot-receive # artifact + digest, COPILOT_CLI_ENABLED_FEATURE_FLAGS on the LIVE copilot processes (the silent failure), live receiver markers via the production reader, receiver log. RED only when install-state exists
198
216
  ./run.sh install-agy-bridge # 봉인 7: agy MCP install adapter — register ONE entwurf-bridge server in the agy mcp_config (adopt file / create / REFUSE symlink), stable bin command, install-state under $XDG_DATA_HOME/entwurf/agy-bridge/
199
217
  ./run.sh uninstall-agy-bridge # 봉인 7: honest inverse of install-agy-bridge from install-state (restore preimage / remove key; refuse if config became a symlink)
218
+ ./run.sh probe-bridge-command <cmd> [args...] # #81: BOOT the given bridge invocation and require the entwurf MCP tool surface back. `--invocation-json '{"command":"…","args":[],"env":{}}'` preserves a harness config exactly. It waits for a valid initialize response, then sends initialized + tools/list only (no tools/call, lock, record, or delivery). exit 0 = it serves the bridge; 1 = it does not. The pi/agy doctors use this leaf.
200
219
  ./run.sh doctor-agy-bridge # fail-loud doctor: MCP config + exact permission rule + state + live probe label
201
220
  ./run.sh install-agy-statusline # own the agy statusLine subtree with bare entwurf-agy-statusline; preserve unrelated settings
202
221
  ./run.sh uninstall-agy-statusline # honest inverse from statusline install-state
@@ -212,6 +231,7 @@ Usage:
212
231
  ./run.sh check-dep-versions # local deterministic check that the pi pin agrees across package.json (devDeps + peer range), run.sh (peer-install pins), and the baseline docs (AGENTS/README/ROADMAP/setup-clean-host/demo)
213
232
  ./run.sh check-node-floor-coherence # binds the Node floor (24+, single axis) across engines.node, run.sh setup preflight, meta-bridge install/doctor judgment logic, clean-host docs, the bridge launcher header, and the CI runner node-version — engines.node is the SSOT, everything else is derived; sweeps tracked contract text for an unregistered declaration
214
233
  ./run.sh check-pack # publish gate (dry-run): npm pack --dry-run + tarball invariants (runtime-critical present, dev residue absent)
234
+ ./run.sh check-pack-pin-matcher # pure self-test of check-pack-install's pin-leak matcher against synthetic .pnpm lookalikes (version boundary: @0.84.30 must leak, @0.84.3 bare/peer-hash must pass); snapshot-safe qualification oracle, also run first inside check-pack-install
215
235
  ./run.sh check-fresh-cut-gate # SOURCE cell of the generation-boundary proof (IN pnpm run check:full): drives real install/setup/fresh-cut in a sandbox; certification refusal is pre-write, quiescence is fail-closed, archives preserve bytes, and the #54 exit matrix distinguishes complete / no-move / usage / incomplete transition / complete-with-cleanup-residue. No model/network/cost
216
236
  ./run.sh check-pack-install # heavy publish gate (prepublishOnly): actual npm pack + tar -tf + fresh-temp install smoke with the pinned pi peers (0.82.x) + the npm-installed bridge BOOTS (tools/list) and DELIVERS (tools/call entwurf_v2 → .msg lands) + the INSTALLED generation lifecycle on a seeded previous-generation host (REFUSE before activation writes / zero Claude invocations → installed fresh-cut archives + opens empty → install-meta-bridge PASSES)
217
237
  ./run.sh check-install-container # 0.12.8 (#51 C): Linux artifact-CONSUMER gate — one candidate .tgz handed read-only to a checkout-invisible node:<engines-major>-bookworm cell. Default packs once to temp; ENTWURF_CANDIDATE_TGZ=/absolute/preserved.tgz consumes those exact bytes with no re-pack and prints canonical path+sha256 for release. Non-root global PATH install, frozen package, MCP tools/list, fake-Claude install-meta-bridge, path+sha256 fence, strict doctor, and the GENERATION host-state matrix (clean / v3-only store bytes unchanged / previous-generation REFUSE→fresh-cut→retry PASS) seeded inline. Docker missing = honest SKIP; ENTWURF_REQUIRE_DOCKER=1 makes that RED (required CI)
@@ -737,6 +757,41 @@ check_meta_receiver_marker() {
737
757
  run_ts scripts/check-meta-receiver-marker.ts
738
758
  }
739
759
 
760
+ check_copilot_birth_hook() {
761
+ # #82 gate: the Copilot BIRTH path, proven without Copilot. Drives the REAL
762
+ # assembler (--assemble-only, into a temp dir), then fires the baked launcher the
763
+ # way Copilot fires it — NO ARGV, envelope on stdin — and requires a v3 record with
764
+ # backend "copilot", an attach on the second event of the same prompt, no
765
+ # mailbox/marker of any kind, and a peer row with liveness `unsupported`. Refusals
766
+ # (disagreeing ids, missing cwd, no id, malformed) must refuse rather than guess.
767
+ # It proves the MECHANISM, never the admission: §6 acceptance is a record minted by
768
+ # a real Copilot session, which costs a model turn.
769
+ run_ts scripts/check-copilot-birth-hook.ts
770
+ }
771
+
772
+ check_copilot_statusline() {
773
+ # #82 Copilot custom-footer renderer. Drives scripts/copilot-statusline.sh with
774
+ # session_id on stdin in an isolated store. ready / ? / exact gid; exit 0.
775
+ run_ts scripts/check-copilot-statusline.ts
776
+ }
777
+
778
+ check_copilot_launch() {
779
+ # #82 RAIL 7. The subject is a process REPLACEMENT, so the oracle is a fake vendor
780
+ # executable on a sandboxed PATH that reports the argv/env/pid/cwd it was handed —
781
+ # never a reading of the launcher's source. HOME/XDG/PATH are all redirected, so the
782
+ # install-state, the extensions root and the resolved binary are the fixture's.
783
+ run_ts scripts/check-copilot-launch.ts
784
+ }
785
+
786
+ check_copilot_receive_arm() {
787
+ # #82 RAIL 5 gate: drives the REAL receiver installer into a temp extensions dir, then
788
+ # forks the REAL extension.mjs with the SDK specifier resolved by a loader hook (the
789
+ # vendor's own mechanism). Binds arm-after-birth, watcher-owned marker, the self-fetch
790
+ # dispatch answer, a doorbell that carries the id and not the body, and every refusal.
791
+ # Needs the compiled bridge closure; hermetic otherwise (no Copilot, no model turn).
792
+ run_ts scripts/check-copilot-receive-arm.ts
793
+ }
794
+
740
795
  check_hook_launch_topology() {
741
796
  # #51 gate 1, deliberately built only AFTER the B/B2 verdict. Binds the SHIPPED
742
797
  # hooks.json to real child topology: every leaf is exec form through the shipped
@@ -1098,7 +1153,10 @@ check_mux_fresh_call() {
1098
1153
  # could not see: real bridge boot → tools/list schema/description, Rust-regex-family pattern
1099
1154
  # validity on every emitted pattern (the #62 escape), and the schema riding the actual
1100
1155
  # anthropic-messages request body. No fake tmux: the real-window axis is smoke-mux-fresh-call-live.
1101
- run_vitest test/mux-fresh-call.test.ts test/fresh-call-surfaces.contract.test.ts test/fresh-call-provider.contract.test.ts
1156
+ # copilot-fresh-preflight rides here rather than in its own gate: it exists only as
1157
+ # freshCall's pre-mutation branch (#82 RAIL 9), and splitting it would let the two halves
1158
+ # of one refusal be certified in different runs.
1159
+ run_vitest test/mux-fresh-call.test.ts test/copilot-fresh-preflight.test.ts test/fresh-call-surfaces.contract.test.ts test/fresh-call-provider.contract.test.ts
1102
1160
  }
1103
1161
 
1104
1162
  smoke_mux_fresh_call_live() {
@@ -1356,7 +1414,7 @@ smoke_acp_cortex_live() {
1356
1414
  # (ACP rail Cortex verification boundary). Drives one real
1357
1415
  # cortex ACP turn through the entwurf provider path (outbound entwurf_v2 +
1358
1416
  # dual-HOME overlay facts + process-group reclaim — CP2).
1359
- # HONEST-SKIP (exit 0) when LIVE!=1 OR `cortex` is not on PATH OR no
1417
+ # HONEST-SKIP (protocol exit 97, never 0) when LIVE!=1 OR `cortex` is not on PATH OR no
1360
1418
  # connection is pinned — the live turn needs `cortex` installed with the
1361
1419
  # operator's own web-login auth already present (there is no `cortex auth`
1362
1420
  # subcommand — CP0 D6), reached through the overlay's narrow credential
@@ -1389,7 +1447,7 @@ smoke_entwurf_v2_matrix_live() {
1389
1447
  # reject, no garbage). Model-in-loop is OUT (GPT Q2): "does the sender model call entwurf_v2"
1390
1448
  # is a separate behavior test — this is a transport/lock/enqueue gate. Negative/timeout/contention
1391
1449
  # stay deterministic. Honest skip when LIVE!=1 so the release-gate is runnable unattended.
1392
- # Model: ENTWURF_LIVE_TARGET=<provider>/<model> (default openai-codex/gpt-5.4).
1450
+ # Model: ENTWURF_LIVE_TARGET=<provider>/<model> (default openai-codex/gpt-5.6-luna).
1393
1451
  # LIVE=1 ./run.sh smoke-entwurf-v2-matrix-live
1394
1452
  entwurf_require_step_outcome
1395
1453
  if [ "${LIVE:-}" != "1" ]; then
@@ -1628,13 +1686,13 @@ assert.equal(peerTui, piAi,
1628
1686
  // floor tracks the devDep pin so a consumer can't install against a pi lacking
1629
1687
  // the public trust exports the bridge imports at the pinned minor, AND an upper
1630
1688
  // bound at the next minor stops a fresh install from silently pulling a future
1631
- // pi (past the declared ceiling — 0.85+ at the current 0.84.1 pin) whose
1689
+ // pi (past the declared ceiling — 0.85+ at the current 0.84.3 pin) whose
1632
1690
  // internal export surface has drifted from the one we typecheck against.
1633
1691
  // pi moves its public surface every minor (the 0.79→0.80 getModels→provider-
1634
1692
  // factory churn is exactly this), so an open `>=` floor is exactly how the next
1635
1693
  // installer re-acquires the drift. The floor is also the HARD MINIMUM a consumer
1636
- // install resolves: at `>=0.84.1` an existing 0.83.x host is upgraded, not kept.
1637
- // Expected shape: `>=<devDep> <0.<minor+1>` (e.g. `>=0.84.1 <0.85`).
1694
+ // install resolves: at `>=0.84.3` an existing 0.83.x host is upgraded, not kept.
1695
+ // Expected shape: `>=<devDep> <0.<minor+1>` (e.g. `>=0.84.3 <0.85`).
1638
1696
  const [piMaj, piMin] = piAi.split('.').map(Number);
1639
1697
  assert.equal(piMaj, 0,
1640
1698
  `pi pin major must stay 0 for the next-minor ceiling rule (got ${piAi}); revisit check-dep-versions when pi reaches 1.x`);
@@ -2741,6 +2799,9 @@ check_pack() {
2741
2799
  "mcp/entwurf-bridge/dist/scripts/doctor-pi-provider.js"
2742
2800
  "mcp/entwurf-bridge/dist/scripts/new-session-id.js"
2743
2801
  "mcp/entwurf-bridge/dist/scripts/meta-bridge-prune.js"
2802
+ # #81 — the boot probe doctor-pi-provider imports (and `entwurf probe-bridge-command` runs).
2803
+ # Without the twin the installed doctor dies on the import before printing a verdict.
2804
+ "mcp/entwurf-bridge/dist/scripts/probe-bridge-command.js"
2744
2805
  # The generation verb. The hosts that need it are installed hosts on a
2745
2806
  # previous-generation store; without this twin the prescription every
2746
2807
  # v3-only rejection names would be dead exactly where it matters.
@@ -2753,6 +2814,9 @@ check_pack() {
2753
2814
  # strip-types under node_modules). meta-session.js is shared with the store-doctor
2754
2815
  # above; listed here too so the hook axis fails loud if the emit graph drops it.
2755
2816
  "mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-hook.js"
2817
+ # #82 — the Copilot birth entry's compiled closure, for the same node_modules
2818
+ # strip-types refusal that forces the Claude one.
2819
+ "mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-hook-copilot.js"
2756
2820
  "mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js"
2757
2821
  "scripts/postinstall-chmod.cjs"
2758
2822
  "pi/entwurf-capabilities.json"
@@ -2765,6 +2829,23 @@ check_pack() {
2765
2829
  # It rides a per-FILE entry in the files array (not a whole directory), so one
2766
2830
  # deleted line drops it from the tarball while every other gate stays green.
2767
2831
  "pi/meta-bridge/entwurf-meta-receive/scripts/hook-launch.sh"
2832
+ # #82 — the Copilot BIRTH unit. It is a SECOND marketplace root, not a second
2833
+ # plugin inside the Claude one: the Claude installer assembles one root and copies
2834
+ # one plugin out of it, so a shared marketplace.json would publish a `source` the
2835
+ # assembly does not contain. Same per-FILE listing discipline as the Claude unit.
2836
+ "pi/meta-bridge-copilot/.claude-plugin/marketplace.json"
2837
+ "pi/meta-bridge-copilot/entwurf-meta-receive-copilot/.claude-plugin/plugin.json"
2838
+ "pi/meta-bridge-copilot/entwurf-meta-receive-copilot/hooks/hooks.json"
2839
+ "pi/meta-bridge-copilot/entwurf-meta-receive-copilot/scripts/copilot-hook-launch.sh"
2840
+ "pi-extensions/meta-bridge-hook-copilot.ts"
2841
+ "scripts/copilot-bridge-install.sh"
2842
+ "scripts/copilot-bridge-doctor.sh"
2843
+ # #82 RAIL 5 — the Copilot RECEIVER. A vendor EXTENSION (forked child, stdio
2844
+ # JSON-RPC), not a plugin, so it ships as its own unit and installs into the user
2845
+ # extensions dir. `extension.mjs` is the vendor's required entry NAME: a rename in
2846
+ # the tarball is not a degraded path, it is a unit Copilot never discovers.
2847
+ "pi/copilot-receive/entwurf-receive/extension.mjs"
2848
+ "scripts/copilot-receive-bridge.sh"
2768
2849
  "pi-extensions/meta-bridge-hook.ts"
2769
2850
  "pi-extensions/lib/meta-session.ts"
2770
2851
  "pi-extensions/lib/session-id.js"
@@ -2832,7 +2913,41 @@ check_pack() {
2832
2913
  return 1
2833
2914
  }
2834
2915
 
2916
+ # The pin-leak filter for the install tree's .pnpm listing, shared by the real scan and its
2917
+ # self-test below. The version BOUNDARY is load-bearing: a pnpm .pnpm entry is
2918
+ # `<name>@<version>` followed by either `_<peer-hash>` or end-of-name (measured pnpm 11.20.0
2919
+ # on this tree), so an unbounded substring match would bless a lookalike such as `@0.84.30`
2920
+ # while announcing the pinned floor — a false-green oracle (found by independent review,
2921
+ # 2026-08-25).
2922
+ pack_install_leaked_pi() {
2923
+ grep '^@earendil-works+pi-' | grep -Ev '@0\.84\.3(_|$)' || true
2924
+ }
2925
+
2926
+ # Matcher self-test on SYNTHETIC lookalikes: a healthy install tree cannot exercise the
2927
+ # false-green shape (it contains no 0.84.30), so the oracle is proven against a fixture
2928
+ # listing. Expected: the two lookalikes leak, the pinned version passes bare and with a
2929
+ # peer-hash suffix. Exposed as its own snapshot-safe subcommand because the heavy
2930
+ # check-pack-install cannot run inside the qualification snapshot (no install environment),
2931
+ # and a kill-proof needs a control-green gate there; check-pack-install still runs this
2932
+ # first so the heavy gate cannot proceed on a broken oracle.
2933
+ check_pack_pin_matcher() {
2934
+ local matcher_probe
2935
+ matcher_probe=$(printf '%s\n' \
2936
+ '@earendil-works+pi-ai@0.84.3' \
2937
+ '@earendil-works+pi-ai@0.84.3_@modelcontextprotocol+sdk@1.29.0_zod@4.3.6' \
2938
+ '@earendil-works+pi-ai@0.84.30' \
2939
+ '@earendil-works+pi-agent-core@0.84.2' | pack_install_leaked_pi)
2940
+ if [ "$matcher_probe" != '@earendil-works+pi-ai@0.84.30
2941
+ @earendil-works+pi-agent-core@0.84.2' ]; then
2942
+ fail "[QK:PACK-INSTALL-PIN-MATCHER-BOUNDED] the pin-leak matcher must flag the 0.84.30/0.84.2 lookalikes and pass 0.84.3 bare or with a peer-hash — got: ${matcher_probe:-<nothing leaked>}"
2943
+ return 1
2944
+ fi
2945
+ echo "[check-pack-pin-matcher] ok — the pin-leak matcher is version-bounded (lookalikes leak, pinned version passes bare and with a peer-hash)"
2946
+ }
2947
+
2835
2948
  _check_pack_install_impl() {
2949
+ check_pack_pin_matcher || return 1
2950
+
2836
2951
  # Heavy publish gate. Runs the remaining three checks in #13's
2837
2952
  # publish checklist that check_pack (dry-run only) does not cover:
2838
2953
  #
@@ -2940,6 +3055,8 @@ _check_pack_install_impl() {
2940
3055
  "mcp/entwurf-bridge/dist/scripts/doctor-pi-provider.js"
2941
3056
  "mcp/entwurf-bridge/dist/scripts/new-session-id.js"
2942
3057
  "mcp/entwurf-bridge/dist/scripts/meta-bridge-prune.js"
3058
+ # #81 — the boot probe the installed doctor imports (see check-pack).
3059
+ "mcp/entwurf-bridge/dist/scripts/probe-bridge-command.js"
2943
3060
  # The generation verb (see check-pack). The installed-command regression below
2944
3061
  # opens a fresh generation on a 0-record sandbox through the real bin.
2945
3062
  "mcp/entwurf-bridge/dist/scripts/meta-bridge-fresh-cut.js"
@@ -2948,6 +3065,9 @@ _check_pack_install_impl() {
2948
3065
  # 0.12.5 — node_modules-safe plugin hook + lib (see check-pack). The installed
2949
3066
  # hook regression below runs exactly this compiled JS from under node_modules.
2950
3067
  "mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-hook.js"
3068
+ # #82 — the Copilot birth entry's compiled closure, for the same node_modules
3069
+ # strip-types refusal that forces the Claude one.
3070
+ "mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-hook-copilot.js"
2951
3071
  "mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js"
2952
3072
  "scripts/postinstall-chmod.cjs"
2953
3073
  "pi/entwurf-capabilities.json"
@@ -2959,6 +3079,23 @@ _check_pack_install_impl() {
2959
3079
  # both callers source. The install smoke below asserts the ASSEMBLED launcher is
2960
3080
  # executable; this asserts the artifact it is assembled FROM actually shipped.
2961
3081
  "pi/meta-bridge/entwurf-meta-receive/scripts/hook-launch.sh"
3082
+ # #82 — the Copilot BIRTH unit. It is a SECOND marketplace root, not a second
3083
+ # plugin inside the Claude one: the Claude installer assembles one root and copies
3084
+ # one plugin out of it, so a shared marketplace.json would publish a `source` the
3085
+ # assembly does not contain. Same per-FILE listing discipline as the Claude unit.
3086
+ "pi/meta-bridge-copilot/.claude-plugin/marketplace.json"
3087
+ "pi/meta-bridge-copilot/entwurf-meta-receive-copilot/.claude-plugin/plugin.json"
3088
+ "pi/meta-bridge-copilot/entwurf-meta-receive-copilot/hooks/hooks.json"
3089
+ "pi/meta-bridge-copilot/entwurf-meta-receive-copilot/scripts/copilot-hook-launch.sh"
3090
+ "pi-extensions/meta-bridge-hook-copilot.ts"
3091
+ "scripts/copilot-bridge-install.sh"
3092
+ "scripts/copilot-bridge-doctor.sh"
3093
+ # #82 RAIL 5 — the Copilot RECEIVER. A vendor EXTENSION (forked child, stdio
3094
+ # JSON-RPC), not a plugin, so it ships as its own unit and installs into the user
3095
+ # extensions dir. `extension.mjs` is the vendor's required entry NAME: a rename in
3096
+ # the tarball is not a degraded path, it is a unit Copilot never discovers.
3097
+ "pi/copilot-receive/entwurf-receive/extension.mjs"
3098
+ "scripts/copilot-receive-bridge.sh"
2962
3099
  "pi-extensions/meta-bridge-hook.ts"
2963
3100
  "pi-extensions/lib/meta-session.ts"
2964
3101
  "pi-extensions/lib/session-id.js"
@@ -3021,7 +3158,7 @@ _check_pack_install_impl() {
3021
3158
  printf '%s\n' '{ "name": "entwurf-install-smoke", "version": "0.0.0", "private": true }' > "$tmp/package.json"
3022
3159
 
3023
3160
  # pi-agent-core is pinned even though we never import it: pi-coding-agent depends
3024
- # on it by CARET (`^0.84.1`), so with no lockfile in this fresh temp project it
3161
+ # on it by CARET (`^0.84.3`), so with no lockfile in this fresh temp project it
3025
3162
  # floats to whatever pi published last — and that newer core then drags a NESTED
3026
3163
  # pi-ai of its own. Measured 2026-07-21: pinning only the three we import left
3027
3164
  # pi-agent-core@0.80.10 + pi-ai@0.80.10 in the tree while the gate still announced
@@ -3040,12 +3177,12 @@ _check_pack_install_impl() {
3040
3177
  local install_log
3041
3178
  install_log=$(cd "$tmp" && pnpm add \
3042
3179
  "$tgz_path" \
3043
- "@earendil-works/pi-ai@0.84.1" \
3044
- "@earendil-works/pi-coding-agent@0.84.1" \
3045
- "@earendil-works/pi-tui@0.84.1" \
3046
- "@earendil-works/pi-agent-core@0.84.1" \
3047
- "@earendil-works/pi-client@0.84.1" \
3048
- "@earendil-works/pi-protocol@0.84.1" \
3180
+ "@earendil-works/pi-ai@0.84.3" \
3181
+ "@earendil-works/pi-coding-agent@0.84.3" \
3182
+ "@earendil-works/pi-tui@0.84.3" \
3183
+ "@earendil-works/pi-agent-core@0.84.3" \
3184
+ "@earendil-works/pi-client@0.84.3" \
3185
+ "@earendil-works/pi-protocol@0.84.3" \
3049
3186
  "typebox@latest" \
3050
3187
  --ignore-workspace --ignore-scripts 2>&1) || {
3051
3188
  fail "[check-pack-install] pnpm add failed:"
@@ -3055,17 +3192,17 @@ _check_pack_install_impl() {
3055
3192
 
3056
3193
  # A pin is a wish until the resolved tree is read back. Assert it: EVERY
3057
3194
  # @earendil-works pi package present — direct or transitive, top level or nested —
3058
- # must be the pinned 0.84.1. Anything else means an unpinned caret floated and the
3195
+ # must be the pinned 0.84.3. Anything else means an unpinned caret floated and the
3059
3196
  # rest of this gate would be exercising a runtime nobody verified, while still
3060
- # printing "pinned pi 0.84.1". Fail loud instead of proving the wrong floor.
3197
+ # printing "pinned pi 0.84.3". Fail loud instead of proving the wrong floor.
3061
3198
  local leaked_pi
3062
- leaked_pi=$(ls "$tmp/node_modules/.pnpm" 2>/dev/null | grep '^@earendil-works+pi-' | grep -v '@0\.84\.1' || true)
3199
+ leaked_pi=$(ls "$tmp/node_modules/.pnpm" 2>/dev/null | pack_install_leaked_pi)
3063
3200
  if [ -n "$leaked_pi" ]; then
3064
- fail "[check-pack-install] UNVERIFIED pi runtime resolved into the install tree (expected only 0.84.1):"
3201
+ fail "[check-pack-install] UNVERIFIED pi runtime resolved into the install tree (expected only 0.84.3):"
3065
3202
  printf '%s\n' "$leaked_pi" | sed 's/^/ /' >&2
3066
3203
  return 1
3067
3204
  fi
3068
- echo "[check-pack-install] pi runtime tree pin verified: every @earendil-works pi package is 0.84.1"
3205
+ echo "[check-pack-install] pi runtime tree pin verified: every @earendil-works pi package is 0.84.3"
3069
3206
 
3070
3207
  # Resolve the installed package.json and confirm pi.extensions
3071
3208
  # arrived intact. If pi.extensions is empty or missing, the
@@ -3218,6 +3355,11 @@ _check_pack_install_impl() {
3218
3355
  ls -l "$npmroot/node_modules/.bin" 2>/dev/null | sed 's/^/ /' >&2 || true
3219
3356
  return 1
3220
3357
  fi
3358
+ if [ ! -x "$npmroot/node_modules/.bin/entwurf-copilot-statusline" ]; then
3359
+ fail "[check-pack-install] npm-managed neutral install missing package bin entwurf-copilot-statusline"
3360
+ ls -l "$npmroot/node_modules/.bin" 2>/dev/null | sed 's/^/ /' >&2 || true
3361
+ return 1
3362
+ fi
3221
3363
  wire_log=$(HOME="$npmhome" XDG_DATA_HOME="$npmhome/.local/share" XDG_STATE_HOME="$npmhome/.local/state" XDG_CACHE_HOME="$npmhome/.cache" "$npm_pkg/run.sh" install "$npmproj" 2>&1) || {
3222
3364
  fail "[check-pack-install] npm-managed run.sh install failed (preflight rejected hoisted deps?):"
3223
3365
  echo "$wire_log" | tail -15 | sed 's/^/ /' >&2
@@ -3388,6 +3530,48 @@ SH
3388
3530
  fi
3389
3531
  rm -rf "$nm_probe"
3390
3532
  echo "[check-pack-install] installed hook is node_modules-safe compiled JS (hooks.json points at .js; runs under node_modules with plain node; raw .ts refused there by the strip-types fence)"
3533
+
3534
+ # --- #82: the Copilot birth unit's INSTALLED-PACKAGE branch ------------------
3535
+ # check-copilot-birth-hook drives the installer from a DEV CLONE, so it only ever
3536
+ # exercises the `.ts` branch. The installed-package branch — compiled
3537
+ # meta-bridge-hook-copilot.js, its rewritten `./lib/meta-session.js` import, the
3538
+ # registry copied to the plugin root — was covered by pack LISTS and by nothing that
3539
+ # RUNS it, which is the shape Hard Rule 11 exists to refuse (cross-review, terra +
3540
+ # glm independently, 2026-08-21).
3541
+ #
3542
+ # `--assemble-only` stops before the Copilot CLI, so this needs no Copilot and no fake
3543
+ # of one. A fake Copilot CLI would only prove our script calls our fake the way we
3544
+ # wrote it — the vendor contract is unmeasured, and the first real execution is the
3545
+ # §6 admission itself. This asserts the CITIZEN, not merely that a node process ran.
3546
+ local cop_asm="$npm_tmp/copilot-asm" cop_store cop_log
3547
+ cop_store="$(mktemp -d)"
3548
+ if ! cop_log=$(HOME="$mb_home" XDG_DATA_HOME="$mb_home/.local/share" ENTWURF_COPILOT_ASM="$cop_asm" PATH="$npmroot/node_modules/.bin:$PATH" "$npm_pkg/run.sh" install-copilot-bridge --assemble-only 2>&1); then
3549
+ fail "[check-pack-install] installed install-copilot-bridge --assemble-only failed:"
3550
+ echo "$cop_log" | tail -20 | sed 's/^/ /' >&2
3551
+ return 1
3552
+ fi
3553
+ local cop_unit="$cop_asm/entwurf-meta-receive-copilot"
3554
+ # Branch SELECTION, pinned: an installed package must assemble the compiled entry and
3555
+ # no raw .ts. Without this the fire below could pass on a layout that silently chose
3556
+ # the dev-clone branch.
3557
+ if [ ! -f "$cop_unit/meta-bridge-hook-copilot.js" ] || [ -f "$cop_unit/meta-bridge-hook-copilot.ts" ]; then
3558
+ fail "[check-pack-install] installed install-copilot-bridge did not select the compiled branch (want meta-bridge-hook-copilot.js, no raw .ts) in $cop_unit"
3559
+ return 1
3560
+ fi
3561
+ local cop_out
3562
+ # Fired the way Copilot fires it: NO ARGV, envelope on stdin.
3563
+ if ! cop_out="$(printf '%s' '{"sessionId":"pack-copilot-probe","cwd":"/tmp","source":"new"}' | env PI_CODING_AGENT_DIR="$cop_store" "$cop_unit/scripts/copilot-hook-launch.sh" 2>&1)"; then
3564
+ fail "[check-pack-install] the installed Copilot launcher failed: $(printf '%s' "$cop_out" | tr '\n' ' ' | cut -c1-200)"
3565
+ return 1
3566
+ fi
3567
+ local cop_records
3568
+ cop_records="$(grep -l '"backend": "copilot"' "$cop_store"/meta-sessions/*.meta.json 2>/dev/null | wc -l | tr -d ' ')"
3569
+ if [ "$cop_records" != "1" ]; then
3570
+ fail "[check-pack-install] the installed Copilot unit did not mint exactly one copilot citizen (found ${cop_records:-0}); log: $(tail -1 "$cop_store/meta-bridge-hook.log" 2>/dev/null)"
3571
+ return 1
3572
+ fi
3573
+ rm -rf "$cop_store"
3574
+ echo "[check-pack-install] installed Copilot birth unit assembles its compiled branch and mints a citizen from a no-argv launch"
3391
3575
  python3 - "$mb_cfg/settings.json" "$mb_home/.claude.json" "$stable_asm" <<'PY'
3392
3576
  import json, sys
3393
3577
  settings = json.load(open(sys.argv[1]))
@@ -3590,6 +3774,60 @@ JS
3590
3774
  return 1
3591
3775
  fi
3592
3776
 
3777
+ # #82 RAIL 7 — the managed launch, from the INSTALLED package. Hard Rule 11: a green
3778
+ # clone is not a green tarball. `entwurf copilot` is a bash leaf, so its risk here is
3779
+ # not the strip-types fence but PACKAGING — `scripts/copilot-launch.sh` has to be in
3780
+ # the tarball and reachable from the installed `run.sh`, and a file-list assertion
3781
+ # cannot tell a shipped script from a shipped script that resolves its siblings wrongly
3782
+ # once it lives under node_modules. So the cell RUNS it, against a fake vendor on a
3783
+ # sandbox PATH and a fake receiver install-state, and reads what the vendor was handed.
3784
+ local cop_launch_home="$npm_tmp/cop-launch-home" cop_launch_xdg="$npm_tmp/cop-launch-xdg" cop_launch_bin="$npm_tmp/cop-launch-bin"
3785
+ local cop_unit_dir="$cop_launch_home/.copilot/extensions/entwurf-receive"
3786
+ local cop_state_dir="$cop_launch_xdg/entwurf/copilot-receive"
3787
+ mkdir -p "$cop_unit_dir" "$cop_state_dir" "$cop_launch_bin"
3788
+ printf '// fixture\n' > "$cop_unit_dir/extension.mjs"
3789
+ printf '{"schemaVersion":1,"unit":"entwurf-receive","path":"%s"}\n' "$cop_unit_dir" > "$cop_state_dir/install-state.json"
3790
+ # The stand-in is installed under the REAL name on a sandbox PATH: the launcher resolves
3791
+ # `copilot` and nothing else, so there is no env switch here to prove the wrong contract with.
3792
+ {
3793
+ printf '#!/usr/bin/env bash\n'
3794
+ printf 'echo "FLAG=[${COPILOT_CLI_ENABLED_FEATURE_FLAGS-<unset>}]"\n'
3795
+ printf 'for a in "$@"; do printf "ARG<%%s>\\n" "$a"; done\n'
3796
+ } > "$cop_launch_bin/copilot"
3797
+ chmod +x "$cop_launch_bin/copilot"
3798
+ # `env -u` for BOTH carriers, not just a reassignment: an operator running this gate with
3799
+ # their own COPILOT_CLI_ENABLED_FEATURE_FLAGS exported would otherwise inherit those tokens
3800
+ # into the oracle below and read a FALSE RED, and an inherited launch sentinel would trip
3801
+ # the recursion fence before the vendor was ever reached. The cell must measure the
3802
+ # installed launcher, not the shell that happened to start it.
3803
+ if ! op_out=$(env -u COPILOT_CLI_ENABLED_FEATURE_FLAGS -u ENTWURF_COPILOT_LAUNCH_ACTIVE \
3804
+ HOME="$cop_launch_home" XDG_DATA_HOME="$cop_launch_xdg" PATH="$cop_launch_bin:$PATH" \
3805
+ "$installed_entwurf" copilot -p hi 2>&1); then
3806
+ fail "[check-pack-install] installed 'entwurf copilot' FAILED to reach the vendor:"
3807
+ echo "$op_out" | tail -8 | sed 's/^/ /' >&2
3808
+ return 1
3809
+ fi
3810
+ # Assert MEANING: the scan flag this whole surface exists to set, the managed defaults,
3811
+ # and the operator's own argument, all arriving at the vendor from the installed tree.
3812
+ if ! printf '%s' "$op_out" | grep -q 'FLAG=\[EXTENSIONS\]'; then
3813
+ fail "[check-pack-install] installed 'entwurf copilot' did not hand the vendor COPILOT_CLI_ENABLED_FEATURE_FLAGS=EXTENSIONS:"
3814
+ echo "$op_out" | tail -8 | sed 's/^/ /' >&2
3815
+ return 1
3816
+ fi
3817
+ for want in 'ARG<-p>' 'ARG<hi>' 'ARG<--model>' 'ARG<auto>' 'ARG<--yolo>'; do
3818
+ if ! printf '%s' "$op_out" | grep -qF "$want"; then
3819
+ fail "[check-pack-install] installed 'entwurf copilot' did not forward the managed argv (missing $want):"
3820
+ echo "$op_out" | tail -8 | sed 's/^/ /' >&2
3821
+ return 1
3822
+ fi
3823
+ done
3824
+ if printf '%s' "$op_out" | grep -qF 'ARG<copilot>'; then
3825
+ fail "[check-pack-install] installed 'entwurf copilot' leaked the dispatcher verb into the vendor's argv:"
3826
+ echo "$op_out" | tail -8 | sed 's/^/ /' >&2
3827
+ return 1
3828
+ fi
3829
+ echo "[check-pack-install] installed 'entwurf copilot' reached the vendor with the scan flag and the managed argv"
3830
+
3593
3831
  # The generation verb must reach its verdict from under node_modules. On the
3594
3832
  # sandbox 0-record agent dir it archives nothing and opens a fresh generation.
3595
3833
  # The exit code alone can't tell a verdict from a fence crash, so read the line.
@@ -4463,6 +4701,23 @@ release_gate() {
4463
4701
  # is gated behind SMOKE_RGG_POSITIVE=1 in the BEHAVIOR lane below.
4464
4702
  run_step "smoke-resident-garden-guard (3c citizen: record birth / record-keyed socket / attach-on-reopen, 0-token)" gate env SMOKE_RGG_POSITIVE=0 bash "$self" smoke-resident-garden-guard
4465
4703
  run_step "check-bridge" gate bash "$self" check-bridge
4704
+ # check-bridge booted the launcher THIS CHECKOUT ships; this boots the invocation the operator's
4705
+ # pi provider actually EXECS. Two different strings, and only the second one reaches a live ACP
4706
+ # session. 2026-08-19 measured the gap at full price: `~/.local/bin/entwurf-bridge` had been
4707
+ # relocated onto a pnpm cmd-shim (basedir derived from $0, so not relocatable), it exited 127, the
4708
+ # bundled bridge never booted, and the model had no `mcp__entwurf-bridge__*` tool — while
4709
+ # `command -v` answered yes throughout. #81's probe already settles that in under a second and
4710
+ # doctor-pi-provider consumes it; it was simply never a step, so the verdict first surfaced at
4711
+ # smoke-acp-bundled-mcp-live, sixteen LIVE steps and ~20 minutes of real model spend later.
4712
+ # `run_step`, not `run_live_step`: this doctor is not LIVE-gated and never emits 97, so it has no
4713
+ # prerequisite to decline. It exits 1 for a configured invocation that will not boot and for
4714
+ # state-owned drift; a host that never installed the circuit keeps its existing exit 0 note, and
4715
+ # the bundled smokes below still fail loud there. So this tightens nothing — it only moves an
4716
+ # existing red earlier. cwd is REPO_DIR regardless of `gate` (run_ts cds there), so the scope
4717
+ # judged is this checkout's `.pi/settings.json` shadowing the operator's global: deterministic,
4718
+ # but not in principle the smokes' global-only subject if the two ever diverge.
4719
+ # Position and classifier arm are pinned by [QK:PI-DOCTOR-IS-RELEASE-MUST].
4720
+ run_step "doctor-pi-provider (#81: the operator's CONFIGURED bridge invocation actually boots)" gate bash "$self" doctor-pi-provider
4466
4721
  # D4-c: the v2 dispatch substrate sentinel (5d-5). A SINGLE run (NOT backend-looped — it proves
4467
4722
  # production runEntwurfV2 deps + real pi control-socket RPC + real mailbox enqueue + v2 lock, not
4468
4723
  # per-backend model behavior). Placed right after check-bridge: the MCP/protocol substrate must be
@@ -4654,6 +4909,18 @@ case "$cmd" in
4654
4909
  check-hook-launch-topology)
4655
4910
  check_hook_launch_topology
4656
4911
  ;;
4912
+ check-copilot-birth-hook)
4913
+ check_copilot_birth_hook
4914
+ ;;
4915
+ check-copilot-statusline)
4916
+ check_copilot_statusline
4917
+ ;;
4918
+ check-copilot-launch)
4919
+ check_copilot_launch
4920
+ ;;
4921
+ check-copilot-receive-arm)
4922
+ check_copilot_receive_arm
4923
+ ;;
4657
4924
  check-meta-capability-source)
4658
4925
  check_meta_capability_source
4659
4926
  ;;
@@ -5038,6 +5305,77 @@ case "$cmd" in
5038
5305
  # zero claude-code meta-records is a SILENT MISS -> non-zero exit.
5039
5306
  (cd "$REPO_DIR" && bash scripts/meta-bridge-doctor.sh "$@")
5040
5307
  ;;
5308
+ install-copilot-bridge)
5309
+ # #82: the Copilot BIRTH install. Deliberately not a mode of install-meta-bridge —
5310
+ # it assembles its OWN marketplace root (the Claude assembler copies one plugin out
5311
+ # of one root, so a shared marketplace.json would publish a missing `source`), it
5312
+ # bakes node+entry into the launcher rather than into the manifest (Copilot's exec
5313
+ # form is a single string with no argv beside it), and it wires NO MCP: a drain tool
5314
+ # for a mailbox nothing rings would advertise delivery this backend does not have.
5315
+ shift || true
5316
+ (cd "$REPO_DIR" && bash scripts/copilot-bridge-install.sh "$@")
5317
+ ;;
5318
+ doctor-copilot-bridge)
5319
+ # #82: the fail-loud surface for the Copilot unit. Its red conditions differ from
5320
+ # the Claude doctor's on purpose: a Copilot session mints on its FIRST PROMPT, not
5321
+ # at session open, so "installed with zero records" is reported as NOT-YET rather
5322
+ # than as a silent miss. Red is a hook that RAN and failed.
5323
+ shift || true
5324
+ (cd "$REPO_DIR" && bash scripts/copilot-bridge-doctor.sh "$@")
5325
+ ;;
5326
+ install-copilot-statusline)
5327
+ (cd "$REPO_DIR" && bash scripts/copilot-statusline-bridge.sh install "$@")
5328
+ ;;
5329
+ uninstall-copilot-statusline)
5330
+ (cd "$REPO_DIR" && bash scripts/copilot-statusline-bridge.sh uninstall "$@")
5331
+ ;;
5332
+ doctor-copilot-statusline)
5333
+ (cd "$REPO_DIR" && bash scripts/copilot-statusline-bridge.sh doctor "$@")
5334
+ ;;
5335
+ smoke-copilot-statusline-state)
5336
+ (cd "$REPO_DIR" && bash scripts/smoke-copilot-statusline-state.sh)
5337
+ ;;
5338
+ install-copilot-mcp)
5339
+ (cd "$REPO_DIR" && bash scripts/copilot-mcp-bridge.sh install "$@")
5340
+ ;;
5341
+ uninstall-copilot-mcp)
5342
+ (cd "$REPO_DIR" && bash scripts/copilot-mcp-bridge.sh uninstall "$@")
5343
+ ;;
5344
+ doctor-copilot-mcp)
5345
+ (cd "$REPO_DIR" && bash scripts/copilot-mcp-bridge.sh doctor "$@")
5346
+ ;;
5347
+ smoke-copilot-mcp-state)
5348
+ (cd "$REPO_DIR" && bash scripts/smoke-copilot-mcp-state.sh)
5349
+ ;;
5350
+ copilot)
5351
+ # #82 RAIL 7: the managed launch. `exec` and NO subshell/cd on purpose — the vendor
5352
+ # must inherit this terminal exactly: the caller's cwd, this pid, this tty, and its own
5353
+ # exit status. The sibling install/doctor verbs wrap themselves in `(cd "$REPO_DIR" && …)`
5354
+ # because they operate on the repo; this one operates on the operator's session, and a
5355
+ # subshell would make run.sh a parent that outlives nothing and owns nothing.
5356
+ #
5357
+ # `shift` is load-bearing here, not cosmetic: this dispatcher keeps the verb in "$@"
5358
+ # and each branch drops it for itself. The neighbouring install/doctor verbs get away
5359
+ # without one because their bridge scripts take a leading subcommand and ignore the
5360
+ # trailing noise; this branch forwards straight into the VENDOR's argv, where a stray
5361
+ # "copilot" would arrive as a prompt argument.
5362
+ shift || true
5363
+ exec bash "$REPO_DIR/scripts/copilot-launch.sh" "$@"
5364
+ ;;
5365
+ install-copilot-receive)
5366
+ # #82 RAIL 5: install the RECEIVER extension into the Copilot USER extensions dir.
5367
+ # Artifact ownership only — it sets no launch flag (`run.sh copilot` owns one
5368
+ # invocation's environment; this owns the unit on disk), and it arms
5369
+ # nothing by itself: a session arms after birth, on a CLI launched with
5370
+ # COPILOT_CLI_ENABLED_FEATURE_FLAGS=EXTENSIONS.
5371
+ (cd "$REPO_DIR" && bash scripts/copilot-receive-bridge.sh install "$@")
5372
+ ;;
5373
+ uninstall-copilot-receive)
5374
+ (cd "$REPO_DIR" && bash scripts/copilot-receive-bridge.sh uninstall "$@")
5375
+ ;;
5376
+ doctor-copilot-receive)
5377
+ (cd "$REPO_DIR" && bash scripts/copilot-receive-bridge.sh doctor "$@")
5378
+ ;;
5041
5379
  install-agy-bridge)
5042
5380
  # 봉인 7: the agy (Antigravity) MCP install ADAPTER (SEPARATE from the Claude
5043
5381
  # marketplace install — only runner/reporting is shared). Registers ONE entwurf-bridge
@@ -5103,12 +5441,34 @@ case "$cmd" in
5103
5441
  # Fail-loud doctor for agy hooks.json imprint wiring.
5104
5442
  (cd "$REPO_DIR" && bash scripts/agy-hooks-bridge.sh doctor "$@")
5105
5443
  ;;
5444
+ check-probe-bridge-command)
5445
+ # #81: contract gate for the boot probe both doctors stake their verdict on — the reason
5446
+ # taxonomy (each value = a different operator repair) plus the one side effect the probe owns,
5447
+ # reaping the child it spawned. Hermetic stubs only; boots no bridge of ours.
5448
+ run_ts scripts/check-probe-bridge-command.ts
5449
+ ;;
5450
+ probe-bridge-command)
5451
+ # #81: does a configured bridge command actually BOOT and serve MCP? `command -v` answering
5452
+ # yes is not that claim — a relocated launcher can resolve and still exit 127, which is how a
5453
+ # host ran with NO bridge in its ACP turns while every doctor printed ok. It waits for the
5454
+ # initialize RESPONSE before it sends notifications/initialized + `tools/list` (no tools/call),
5455
+ # so it takes no lock, writes no record, and delivers nothing. Both doctors route their boot
5456
+ # cell here so one leaf owns the verdict — agy for every configured {command,args,env}, pi for
5457
+ # every effective stdio invocation.
5458
+ shift || true
5459
+ run_ts scripts/probe-bridge-command.ts "$@"
5460
+ ;;
5106
5461
  doctor-pi-provider)
5107
- # #46 Task 2: read-only fail-loud doctor for the pi provider ownership (entwurfProvider.
5108
- # mcpServers.entwurf-bridge). Uses config.ts readProviderSettingsFile SSOT for the EFFECTIVE
5109
- # (project-shadows-user) command never a re-implemented merge. Reports user/project/effective,
5110
- # gates on stable-bin resolvability, and distinguishes state-owned drift (FAIL) from an
5111
- # unowned user override (honest note). No agy/pi process needed — pure settings inspection.
5462
+ # #46 Task 2 + #81: fail-loud doctor for the pi provider ownership (entwurfProvider.
5463
+ # mcpServers.entwurf-bridge). Uses the config.ts SSOT for the EFFECTIVE (project-shadows-user)
5464
+ # entry — per-name merge then ONE normalize, exactly as resolveProviderConfig does; never a
5465
+ # re-implemented merge. Reports user/project/effective and distinguishes state-owned drift
5466
+ # (FAIL) from an unowned user override (honest note).
5467
+ # This doctor no longer merely reads files: it BOOTS every effective stdio invocation and
5468
+ # requires the entwurf verb set back, because `command -v` succeeding was never evidence that
5469
+ # pi gets a bridge. Runtime evidence and ownership are separate: an unowned override is not
5470
+ # repaired, but a dead one is still red. It writes no operator state, but it does exec the
5471
+ # configured command on this host. No agy/pi process is needed.
5112
5472
  run_ts scripts/doctor-pi-provider.ts "$@"
5113
5473
  ;;
5114
5474
  wire-agy-statusline)
@@ -5271,6 +5631,9 @@ case "$cmd" in
5271
5631
  check-pack)
5272
5632
  check_pack
5273
5633
  ;;
5634
+ check-pack-pin-matcher)
5635
+ check_pack_pin_matcher
5636
+ ;;
5274
5637
  check-pack-install)
5275
5638
  check_pack_install
5276
5639
  ;;