@junghanacs/entwurf 0.22.0 → 0.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +4 -1
- package/BASELINE.md +1 -0
- package/CHANGELOG.md +514 -0
- package/DELIVERY.md +10 -2
- package/README.md +43 -4
- package/VERIFY.md +47 -0
- package/docs/acp-backend-rail.md +96 -17
- package/docs/setup-clean-host.md +11 -1
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +39 -40
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/backend-adapter.js +2 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/tool-surface.js +2 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/codex-declaration.js +609 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/codex-fresh-preflight.js +95 -56
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-control-rpc.js +40 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-fact-provider.js +6 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-facts.js +7 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-peer-observe.js +67 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-peers-render.js +9 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-self-address.js +24 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-contract.js +2 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-lock.js +1 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/fresh-call-composition.js +423 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/fresh-call-dispatch.js +98 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/herdr-fresh-call.js +1018 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/herdr-placement.js +213 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +46 -273
- package/mcp/entwurf-bridge/dist/scripts/peer-facts.js +109 -0
- package/mcp/entwurf-bridge/src/index.ts +41 -41
- package/mcp/entwurf-bridge/tsconfig.build.json +1 -0
- package/package.json +9 -8
- package/pi-extensions/entwurf-control.ts +91 -52
- package/pi-extensions/lib/acp/acp-client.ts +3 -3
- package/pi-extensions/lib/acp/backend-adapter.ts +3 -3
- package/pi-extensions/lib/acp/backend.ts +4 -4
- package/pi-extensions/lib/acp/event-mapper.ts +5 -5
- package/pi-extensions/lib/acp/tool-surface.ts +2 -1
- package/pi-extensions/lib/codex-declaration.js +612 -0
- package/pi-extensions/lib/codex-fresh-preflight.ts +107 -51
- package/pi-extensions/lib/entwurf-control-rpc.ts +46 -0
- package/pi-extensions/lib/entwurf-fact-provider.ts +13 -2
- package/pi-extensions/lib/entwurf-facts.ts +14 -1
- package/pi-extensions/lib/entwurf-peer-observe.ts +72 -2
- package/pi-extensions/lib/entwurf-peers-render.ts +9 -1
- package/pi-extensions/lib/entwurf-self-address.ts +33 -0
- package/pi-extensions/lib/entwurf-v2-contract.ts +2 -2
- package/pi-extensions/lib/entwurf-v2-lock.ts +1 -1
- package/pi-extensions/lib/entwurf-v2-runner.ts +1 -1
- package/pi-extensions/lib/fresh-call-composition.ts +493 -0
- package/pi-extensions/lib/fresh-call-dispatch.ts +142 -0
- package/pi-extensions/lib/herdr-fresh-call.ts +1378 -0
- package/pi-extensions/lib/herdr-placement.ts +276 -0
- package/pi-extensions/lib/mux-fresh-call.ts +95 -301
- package/run.sh +588 -204
- package/scripts/check-acp-usage-accounting.ts +9 -9
- package/scripts/check-codex-birth-hook.ts +187 -0
- package/scripts/check-entwurf-control-rpc.ts +138 -0
- package/scripts/check-entwurf-fact-provider.ts +4 -2
- package/scripts/check-entwurf-peers-surface.ts +3 -1
- package/scripts/check-entwurf-self-address.ts +68 -0
- package/scripts/check-fresh-call-dispatch.ts +301 -0
- package/scripts/check-gate-qualification.ts +17 -4
- package/scripts/check-herdr-activation.ts +1293 -0
- package/scripts/check-herdr-fresh-call.ts +1250 -0
- package/scripts/check-herdr-placement.ts +383 -0
- package/scripts/check-herdr-plugin-build.ts +842 -0
- package/scripts/check-herdr-plugin-profile.ts +371 -0
- package/scripts/check-herdr-plugin.ts +523 -0
- package/scripts/check-herdr-runtime-bootstrap.ts +1264 -0
- package/scripts/check-herdr-sandbox.ts +456 -0
- package/scripts/check-herdr-supply.ts +184 -0
- package/scripts/check-mux-launcher-fence.ts +138 -0
- package/scripts/check-peer-facts.ts +331 -0
- package/scripts/check-pi-launch.ts +358 -0
- package/scripts/check-release-gate-outcomes.ts +19 -0
- package/scripts/check-typing-call-fence.ts +296 -0
- package/scripts/codex-birth-doctor.sh +159 -16
- package/scripts/codex-birth-install.sh +178 -72
- package/scripts/codex-birth-uninstall.sh +162 -7
- package/scripts/fixtures/herdr-supply.json +29 -0
- package/scripts/herdr-activation.mjs +536 -0
- package/scripts/herdr-plugin-activate.mjs +270 -0
- package/scripts/herdr-plugin-deactivate.mjs +193 -0
- package/scripts/herdr-runtime.mjs +1203 -0
- package/scripts/install-herdr-ci.sh +96 -0
- package/scripts/inventory-verification-surface.ts +86 -10
- package/scripts/lib/codex-fresh-live-protocol.ts +2 -2
- package/scripts/lib/tree-digest.ts +97 -0
- package/scripts/meta-bridge-install.sh +19 -2
- package/scripts/meta-bridge-state.py +167 -11
- package/scripts/mutants/codex-caller-seat.json +6 -8
- package/scripts/mutants/codex-native.json +195 -29
- package/scripts/mutants/control-socket-disconnect.json +57 -0
- package/scripts/mutants/fresh-call-dispatch.json +154 -0
- package/scripts/mutants/herdr-activation.json +305 -0
- package/scripts/mutants/herdr-fresh-call.json +448 -0
- package/scripts/mutants/herdr-placement.json +161 -0
- package/scripts/mutants/herdr-plugin-build.json +160 -0
- package/scripts/mutants/herdr-plugin-profile.json +202 -0
- package/scripts/mutants/herdr-plugin.json +133 -0
- package/scripts/mutants/herdr-runtime-bootstrap.json +411 -0
- package/scripts/mutants/herdr-supply.json +133 -0
- package/scripts/mutants/meta-hook-session-switch.json +2 -2
- package/scripts/mutants/mux-fresh-call.json +104 -17
- package/scripts/mutants/mux-launcher-fence.json +13 -0
- package/scripts/mutants/omp-fresh.json +4 -4
- package/scripts/mutants/peer-facts.json +98 -0
- package/scripts/mutants/pi-launch.json +113 -0
- package/scripts/mutants/resume-args.json +6 -6
- package/scripts/mutants/self-address.json +33 -0
- package/scripts/mutants/typing-call-fence.json +17 -0
- package/scripts/mutants/v2-surface.json +2 -2
- package/scripts/mutants/v2-visible-resume.json +17 -17
- package/scripts/peer-facts.ts +120 -0
- package/scripts/register-pi-package.py +8 -0
- package/scripts/register-pi-provider.py +88 -12
- package/scripts/smoke-acp-raw-turn-live.ts +1 -1
- package/scripts/smoke-codex-birth.sh +281 -12
- package/scripts/smoke-entwurf-v2-matrix-live.ts +1 -1
- package/scripts/smoke-herdr-fresh-call-live.ts +1027 -0
- package/scripts/smoke-herdr-plugin-build-live.ts +543 -0
- package/scripts/smoke-herdr-raw-install-live.sh +849 -0
- package/scripts/tsconfig.json +5 -0
- package/scripts/check-entwurf-facts.ts +0 -385
- package/scripts/check-entwurf-resume-args.ts +0 -122
- package/scripts/check-entwurf-v2-decider.ts +0 -657
- package/scripts/check-entwurf-v2-lock.ts +0 -371
- package/scripts/check-entwurf-v2-mailbox.ts +0 -261
- package/scripts/check-entwurf-v2-matrix.ts +0 -401
- package/scripts/check-entwurf-v2-native-push.ts +0 -221
- package/scripts/check-entwurf-v2-release.ts +0 -151
- package/scripts/check-entwurf-v2-runner.ts +0 -448
- package/scripts/check-entwurf-v2-send-fallback.ts +0 -350
- package/scripts/check-entwurf-v2-send.ts +0 -458
- package/scripts/check-entwurf-v2-visible-resume.ts +0 -445
- package/scripts/check-socket-discovery.ts +0 -401
package/run.sh
CHANGED
|
@@ -144,7 +144,7 @@ Usage:
|
|
|
144
144
|
./run.sh check-codex-birth-hook # sandboxed payload + declaration: first-turn V3 birth, one record per thread, top-level event predicate, no markers
|
|
145
145
|
./run.sh check-codex-fresh-preflight # pre-mutation birth/MCP/thread-title/default-app-server readiness
|
|
146
146
|
./run.sh smoke-codex-config-state # sandboxed MCP + status-line ownership/install/doctor/inverse lifecycle
|
|
147
|
-
./run.sh smoke-codex-birth # sandboxed birth unit: exact trust identity, the vendor trust receipt read (never written), config.toml untouched, foreign/
|
|
147
|
+
./run.sh smoke-codex-birth # sandboxed birth unit: exact trust identity, the vendor trust receipt read at our MEASURED index (never written), config.toml untouched, coexistence with a foreign SessionStart group in either ordering (joined not adopted, reported not certified, byte-preserved across install and inverse), own-declaration drift/duplication refusals, drift-refusing inverse
|
|
148
148
|
./run.sh check-harness-admission-parity # #87 C: the EDGE the two closed parity loops never had. Every citizen backend is fresh-openable or a declared pre-#82 legacy admission whose exception a reader finds in DELIVERY.md — so a post-contract harness that mints records but cannot be opened by entwurf_fresh_call blocks the release package instead of only carrying an `unsupported` note (docs/adding-a-harness.md step 9)
|
|
149
149
|
./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
|
|
150
150
|
./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
|
|
@@ -158,31 +158,27 @@ Usage:
|
|
|
158
158
|
./run.sh check-socket-probe # deterministic gate (0.11 Stage 0, F3): three-valued control-socket liveness (alive|dead|indeterminate) — GC reclaims dead only, indeterminate survives; pure classify + 2-socket integration, no API
|
|
159
159
|
./run.sh check-project-trust-handler # deterministic gate (0.11 Stage 0, Trust 2층): project_trust handler — decideProjectTrust matrix (escape=inherited-false+interactive+trust-here→{yes,remember:true}; non-interactive→undecided; never undefined) + adapter single-writer, fake prompt, no UI
|
|
160
160
|
./run.sh check-entwurf-v2-contract # FROZEN entwurf_v2 contract — control-socket liveness domain is currently pi; self-fetch/native-push citizens are out of that domain, not globally unsupported; pure, no IO
|
|
161
|
-
./run.sh check-entwurf-v2-lock # deterministic gate (0.11 Stage 0 step 5a, 버킷 B F2): per-gid dispatch LOCK primitive — openSync wx atomic acquire, second-acquire=target-locked conflict (holder JSON for human cleanup), nonce-owned release (successor survives late release), stale reclaim same-host+ESRCH-only (EPERM/remote/alive/unknown fail-closed), empty/corrupt=conflict not auto-deleted, F2-P1 malformed gid throws; real temp dir, deps injected
|
|
162
|
-
./run.sh check-entwurf-v2-decider # deterministic gate (0.11 Stage 0 step 5b): PURE dispatch decider decideDispatch — frozen 7-step order over injected fakes, lock acquire+release tracked so reject⇒no-plan-no-lock proven; pre-probe rejects observedLiveness=null, send/resume execute keep lock + mailbox no-lock (?7), resume plan no mode/provider/model, invalid gid throws (F2-P1); pure, no IO
|
|
163
|
-
./run.sh check-entwurf-v2-matrix # deterministic gate (0.11 Stage 0 step 5d-5 a): REACHABILITY + LOCK SSOT table — drives REAL decideDispatch over fakes, fixes every (target kind → transport → lock class) cell as one table (control-socket/meta-mailbox/native-push + bad-target/conflict/locked/undeliverable/dormant/indeterminate rejects), coverage pass fails on a dropped cell; thin coverage not a decider re-impl; pure, no IO
|
|
164
|
-
./run.sh check-entwurf-v2-release # deterministic gate (0.11 Stage 0 step 5c-1): PURE release-policy reducer (decideReleasePolicy + reduceRelease) — the spawn-observation policy and its four release events went with the transport in the visible-first cut, so the shipped machine is meta-mailbox=never release (no lock) + control-socket=release once on send-final; decideReleasePolicy still enforces the lock-nullness invariant; pure, no IO
|
|
165
|
-
./run.sh check-entwurf-v2-send # deterministic gate (0.11 Stage 0 step 5c-2a): control-socket SEND hand (executeControlSocketSend) wiring transport IO onto the 5c-1 reducer — ack→sent, in-band reject→rejected (no fallback), dead→same-lock one-shot re-resolve (control retry / mailbox enqueue), indeterminate→failed+rethrow with NO fallback (no double-delivery); release exactly once, releaseLock throw never masks the send error; IO-via-dep
|
|
166
161
|
./run.sh check-compaction-send-guard # deterministic gate (#111): control-socket send during Pi compaction — event-armed refuse compacting, quiet unknown non-idle refuse busy (ctx.signal is not isStreaming); no pi.sendMessage, no delivered:true; idle/live-run steer/followUp preserved; pure, no IO
|
|
167
|
-
./run.sh check-entwurf-v2-send-fallback # deterministic gate (0.11 Stage 0 step 5c-2b): same-lock re-resolve RESOLVER (resolveDeadControlSendFallback) — fire-and-forget re-resolve: alive→control retry, dead→reject (nothing is ever launched), indeterminate→reject, unsupported+deliverable→mailbox plan, undeliverable/bad-target/conflict→reject; resolver never releases, mis-wire fails loud, inspect/probe throws propagate; no IO (fakes)
|
|
168
|
-
./run.sh check-entwurf-v2-runner # deterministic gate (0.11 Stage 0 step 5d-1): execute-router (executeDispatch) routing an already-decided DispatchDecision to its 5c transport hand → one outcome-rich EntwurfV2RunResult. reject→rejected (no hand) / control/mailbox→matching hand with decision.lock verbatim / N3 rejectReason carried / N1 SendDeliveredReleaseFailedError→execution-failed{finalizedOutcome,releaseFailed,retrySafe:false}; fake hands, no IO
|
|
169
|
-
./run.sh check-entwurf-v2-mailbox # deterministic gate (0.11 Stage 0 step 5c-4, LAST 5c transport slice): ENQUEUE-ONLY meta-mailbox SEND body (executeMetaMailboxSend) + production sendViaMailbox adapter — sender→formatMetaMailboxBody with plan.wantsReply threaded (divergence from legacy hard false), sender absent→raw plan.message, enqueue opts EXACTLY {gardenId,body,sessionsDir,mailboxDir}, enqueue throw PROPAGATES (no success:false fold — mailbox has no in-band refuse); adapter NEVER touches lock (release is the hand's job); source guard: no release/routing seam
|
|
170
|
-
./run.sh check-entwurf-resume-args # deterministic gate: resume-argv SSOT (buildResumePiArgs) for the S1 VISIBLE resume. The headless shape was measured wrong for a window before the consumer was written (`-p` is pi's own non-interactive mode), so the one shipped posture is `--entwurf-control` FIRST + ext args exactly once + `--session <abs file>` + optional `--provider` + `--model <m>` — and the gate pins the ABSENCES as hard as the presences: no --mode, no -p, no positional prompt (a resume runs no turn), no --no-extensions, never --session-id (which MINTS a session instead of resuming one)
|
|
171
162
|
./run.sh check-mux-resume-call # deterministic gate: S1 resume PLACEMENT composition (mux-resume-call.ts). No fake tmux. cwd rules are MEASURED tmux 3.6a behaviours, each a way a resume looks successful while being wrong: a nonexistent `-c` exits 0 and lands the child in $HOME (so it is refused HERE), `-c` is FORMAT-EXPANDED so `#{…}` silently rewrites the path and `#(…)` was observed running a command (so `#` is refused), and whitespace measured SAFE (so no escaping layer is owed). Also pins `-c` reaching tmux, runtime after `--`, carrier-free argv, zero identity in this module, and the surface seam that keeps the v2 composition from importing mux
|
|
172
163
|
./run.sh check-mux-parent-artifact # deterministic gate for the tracked scrubbed parent-transcript fixture — a version-pinned sample of the PARENT-SIDE shape (fresh_call toolResult + the later callback custom_message) so downstream never opens a private transcript. Pins event order, the toolCallId join on the RESULT (pi writes no separate toolCall row), the launch nonce reappearing verbatim in the callback body, the <sender_info> envelope field names, and the absence of operator paths / real garden ids / real uuids. NOT placement evidence
|
|
173
164
|
./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
|
|
174
|
-
./run.sh check-
|
|
165
|
+
./run.sh check-typing-call-fence # deterministic gate for the boundary nobody has broken: this product composes ARGUMENTS and LETTERS, never keystrokes. Scans every tracked production source under pi-extensions/ and mcp/ — comments blanked, string literals kept — for tmux send-keys/paste-buffer/load-buffer and herdr's PTY verbs (agent.prompt, agent.send_keys, pane.send_text, pane.input.set). Prose is exempt on purpose: the sentence stating the rule names the very calls it forbids, and a scanner that read prose could be made green by deleting the law. Carries its own oracle — the detector is proven on planted code, proven blind to comments, and the scanned inventory is measured
|
|
175
166
|
./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
|
|
176
167
|
./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
|
|
177
168
|
./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
|
|
178
169
|
./run.sh smoke-codex-native-push-live # on-demand LIVE acceptance for Codex native-push — needs LIVE=1 + CODEX_LIVE_THREAD_ID loaded by a visible TUI attached to the default app-server; probes the real UDS, routes through production runEntwurfV2, queues one exact-token turn without retry, re-probes, and leaves the visible TUI to show the model result
|
|
179
170
|
./run.sh smoke-codex-fresh-live # #95 RELEASE MUST: record-backed receipt fixture -> PUBLIC initial Pi fresh/callback in a session S that is NOT the app-server's -> Pi PUBLIC omitted-placement Codex fresh/exact callback, also in S -> Pi-to-Codex addressed native-push v2 -> Codex PUBLIC omitted-placement AND omitted-cwd fresh Pi/exact callback IN S beside its own TUI (lane B claim: the app-server env names another session, so S can only come from the caller's pane title; lane C claim: no cwd was asked for, so the directory can only come from the Codex caller's own record — and the Codex thread's own rollout session_meta.cwd, its pane and its record must all be the requested scratch, not the app-server's repo) -> Codex final v2 evidence. Needs LIVE=1 + ENTWURF_CODEX_APP_SERVER_PID + ENTWURF_CODEX_FRESH_MODEL + ENTWURF_CODEX_FRESH_PI_MODEL, and its ONE stable launch directory answered 'Trust' once in a plain codex -C there: codex records its direct consent per exact directory, so a directory with no answer at all opens a consent screen instead of a first turn. Only THIS gate treats that as a precondition and fails up front as codex-launch-cwd-undecided rather than timing out on a callback — an ordinary entwurf_fresh_call only prints the note and opens the window, because a human can answer the screen. The smoke PRINTS that directory as 'launch-cwd <dir>' and repeats it in the failure as a ready-to-run repair command — do not retype it from memory, and do not write it as \$TMPDIR/... (os.tmpdir() falls back to /tmp when TMPDIR is unset, so that spelling names a different directory in the filesystem root). The PID names the operator-owned app-server, which must sit in a DIFFERENT session from S; the smoke checks/prints all four coordinates, never starts/stops/guesses the app-server/session, never reads screen text, interrupts only its exact still-running Codex turn, and cleans only receipt-named window ids
|
|
180
171
|
./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
|
|
181
|
-
./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
|
|
182
|
-
./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
|
|
183
172
|
./run.sh check-meta-facts # deterministic gate for the meta-facts projection (#65): drives the REAL CLI — full-record join, parse-before-uniqueness, no-winner duplicates, drift/symlink/invalid-UTF-8 defects in-band, deterministic bytes, exit contract 0/2/3, dispatch+emit reachability
|
|
173
|
+
./run.sh check-herdr-plugin # deterministic gate for the #116 M2-b herdr plugin (`plugins/herdr/`): STATIC manifest shape for herdr 0.9.0 + the forbidden sections ([[startup]]/[[events]]/[[actions]]/[[link_handlers]]) asserted absent ([[build]] is check-herdr-plugin-build's subject since #116 M3-b3); BEHAVIOURAL drive of the REAL pane entry against a stub `entwurf` and stub HERDR_BIN_PATH that LOG every call — exactly one peer-facts + one agent list per open (counted, not claimed), skip-by-name when Entwurf is absent, four distinct named refusals instead of an empty table, ambiguous never first-wins, diagnostics shown, activity in its own column, and zero writes to the plugin state/config dirs. No herdr binary, no Entwurf install
|
|
174
|
+
./run.sh check-herdr-runtime-bootstrap # deterministic gate for the #116 M3-b1 runtime leaf (`plugins/herdr/lib/runtime-bootstrap.mjs`): the Entwurf-owned stable active root as a REAL directory (nothing is put on PATH — the scoped wiring above names ABSOLUTE commands under it), ownership decided BEFORE the first mkdir, disk facts from lstat (a dangling symlink is not 'absent' and a link into another tree is not a directory), exact name@version + compiled entry + all three required bins present AND executable + a real `check-bridge`, the plugin-owned artifact lock coherent with the checkout, the owned npm cache and --ignore-scripts, a CERTIFIED journal (non-object/array/scalar/blank-identity/phase-contradicting-digest all refused) as the only ownership proof, all EIGHT active/staging/previous combinations named, the last good runtime never lost (torn swap AND corrupt-active beside a good backup), prior provenance carried across an install that may not finish, a failed candidate leaving the running runtime byte-identical, idempotent same-spec reinstall, a journal believed only while the disk backs it, a preflight-then-mutate inverse taking runtime last, and the refusal to record Herdr's commit as ours. NETWORK ZERO, npm ZERO — it drives a FIXTURE package, so the ACTUAL package proof lives in check-pack-install
|
|
175
|
+
./run.sh check-herdr-activation # deterministic gate for the #116 M3-b2 scoped activation: the pi user-scope-ONLY forward seam (no project write, OpenCode byte-untouched), the absolute command DERIVED from the runtime root for BOTH harnesses (relative/unnormalised refused), default bare/clone bytes unchanged when no mode is asked, the pi inverse refusing a drifted command instead of deleting an override, a certified activation ledger that can never name an atom outside {pi, claude-code}, add-only reinstall (a shrinking H retains, never removes), a roots-drift refusal before the first byte, the teardown order components->runtime->ledger LAST, one refused preflight leaving every byte untouched, and no deferred module load on the path that deletes its own runtime. Drives the REAL pi writers; the vendor `claude` CLI is NOT invoked and its acceptance of an absolute executable stays a named LIVE boundary
|
|
176
|
+
./run.sh check-herdr-plugin-profile # deterministic gate for the #116 M3-a PURE activation leaf (`plugins/herdr/lib/integration-profile.mjs`): the closed {pi→pi, claude→claude-code} table, exactly-one-row-or-named-refusal, the FRONT-ANCHORED state grammar (a `/srv/current (v9)/` directory name may not decide a verdict), no path in the plan, outdated/needs-repair as named FAILs, not-installed as a zero-write SKIP, `current` as herdr's admission with no floor of our own, OpenCode observed but never planned, and the leaf's purity. Listing strings only — no herdr binary, no install, no environment, no writes
|
|
177
|
+
./run.sh check-herdr-plugin-build # deterministic gate for the #116 M3-b3 build runner (`plugins/herdr/lib/build.mjs`): the ONE command Herdr's [[build]] calls, composing integration status -> pure profile -> runtime bootstrap -> activation THROUGH THE INSTALLED PACKAGE (never this checkout, which herdr deletes on uninstall with no cleanup hook). Pins one [[build]] with argv `node lib/build.mjs` and a runner npm does not ship, missing-herdr vs unanswerable-status as separate named refusals, a selected outdated/needs-repair/malformed/duplicate atom failing BEFORE any runtime work, A-empty as exit 0 with ZERO writes, absent/incapable activation entries as distinct refusals, the two-stage {pi} -> {pi, claude-code} composition with OpenCode never named, and the post-build herdr commit gap named rather than called atomic. Listing via the runner's own spawn seam (no fake herdr); one cell drives the REAL binary, named SKIP without it
|
|
178
|
+
./run.sh check-peer-facts # deterministic gate for the peer-facts projection (#116 M2-a): drives the REAL CLI with every ambient root sandboxed — placement crosses STRUCTURED (never the human `herdr <pane>` string), the peer keyset is exactly the provider's facts (no herdr agent_status may enter an entwurf payload), no socket coordinate is published (#50 C4), diagnostics in-band, the probed socket world is the one ENTWURF_DIR names (proved by a record-less socket surfacing — there is no field to echo), no observation budget, exit contract 0/2/3, dispatch+emit reachability. No herdr binary
|
|
184
179
|
./run.sh check-meta-listing # deterministic gate: META-STORE facts axis — kind-carrying entries; non-regular records are never read, parse/drift become diagnostics, duplicate nativeSessionId quarantines every rival but not unrelated citizens; strict throws / collect partial; pure injected IO
|
|
185
180
|
./run.sh check-entwurf-fact-provider # deterministic gate (0.11 Stage 0 step 4, fact-provider slice 4b): ASSEMBLY listEntwurfFacts — full-store parse/probe/quarantine/resolve stays intact; #112 bounds expensive receiver/transcript observation to the newest 32 rendered rows while older machine rows say unobserved; Q112 pins full payload + exact observer budget; C-원칙 keeps corruption diagnostic and impossible wiring loud; deps injected, no IO
|
|
181
|
+
./run.sh check-herdr-placement # deterministic gate (#116 S1): the PURE placement-evidence axis — herdr `pane list` parse (unreadable payload → null → `unobserved`, never `none`), the two measured join rules (claude kind=id byte equality / pi kind=path uuid tail of a .jsonl basename, both strict), unmeasured kind → NO key, two panes on one key → `ambiguous` (never last-write-wins), provider reads the placement owner EXACTLY ONCE per listing (anti-watcher, docs/mux-launch-rail.md §7), and no entwurf_v2 dispatch module names or imports the axis (Hard Rule 16 — a pane is a view, not a rail). No fake herdr, no binary, no IO
|
|
186
182
|
./run.sh check-entwurf-peers-surface # deterministic gate (0.11 Stage 0 step 4, fact-provider slice 4c): MCP entwurf_peers RENDER renderEntwurfPeers (#50 C4) — payload keyset exactly {peers, diagnostics}; FORBIDDEN keys sessions/socketOnly/controlDir/socketPath/count + no .sock in text (socket is transport, never identity); record-less socket = aggregated record-less-socket diagnostic (F8, liveness-keyed message, alive names fresh-cut); NO verb-routing key (JSON deep scan) NOR word (text), diagnostics both surfaces, empty→(none), unsupported shown; WIRING guard: both surfaces call provider+render, getLiveSessions + /entwurf-sessions gone; facts fabricated, no IO
|
|
187
183
|
./run.sh check-entwurf-self-address # deterministic gate (SE-1/SE-2 slice 1): self-addressability honesty predicate computeSelfAddressability — pi replyable ⟺ live socket; meta splits by RAIL: self-fetch ⟺ recordBacked ∧ ownerAlive ∧ watchArmed (regression-proof record-present rows), native-push ⟺ recordBacked ∧ probeAlive (separate axis — no mailbox fact may rescue or sink it), unsupplied rail fail-closed; SOURCE GUARD buildStrictPiSenderEnvelope drops hardcoded replyable:true + existsSync-probes socket, entwurf_self renders alive vs expected AND renders the meta rail per-rail (mailbox only inside the self-fetch branch; native-push denies an inbox and gates injection on the probe)
|
|
188
184
|
./run.sh check-entwurf-deliverability # deterministic gate (SE-1/SE-2 slice 2c): conversational-mailbox deliverability predicate — computeMetaReceiverActive (recordBacked ∧ ownerAlive ∧ watchArmed) + mailboxConversationalDeliverable (self-fetch AND active); direct-inject pi refused (SE-1), self-fetch dead/unarmed refused (SE-2); self-address shares the same atom
|
|
@@ -251,9 +247,9 @@ Usage:
|
|
|
251
247
|
./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)
|
|
252
248
|
./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.
|
|
253
249
|
./run.sh doctor-agy-bridge # fail-loud doctor: MCP config + exact permission rule + state + live probe label
|
|
254
|
-
./run.sh install-codex-birth # publish $CODEX_HOME/hooks.json + the helper closure; no root, and the operator trusts the declaration once in their own Codex
|
|
255
|
-
./run.sh uninstall-codex-birth # exact inverse from the install-state digest inventory; refuses drifted bytes instead of deleting them
|
|
256
|
-
./run.sh doctor-codex-birth # RUNTIME + UNIT bytes/ownership + the vendor TRUST receipt
|
|
250
|
+
./run.sh install-codex-birth # publish ONE SessionStart declaration inside $CODEX_HOME/hooks.json (a file it shares) + the helper closure; no root, and the operator trusts the declaration once in their own Codex
|
|
251
|
+
./run.sh uninstall-codex-birth # exact inverse from the install-state digest inventory; removes only entwurf's own declaration (foreign groups byte-for-byte) and refuses drifted bytes instead of deleting them
|
|
252
|
+
./run.sh doctor-codex-birth # RUNTIME + UNIT bytes/ownership + FOREIGN neighbours (reported, never certified) + the vendor TRUST receipt at this declaration's measured index (--unit-only skips the receipt; sandboxes only)
|
|
257
253
|
./run.sh install-codex-mcp # own only [mcp_servers.entwurf-bridge] in the user's Codex config
|
|
258
254
|
./run.sh uninstall-codex-mcp # remove/restore only the recorded MCP atom
|
|
259
255
|
./run.sh doctor-codex-mcp # effective config + ownership + actual bridge boot
|
|
@@ -274,6 +270,7 @@ Usage:
|
|
|
274
270
|
./run.sh meta-bridge-prune # 1.0.0 meta-bridge Phase 4: LISTING-ONLY store hygiene — classify orphan/stale/ambiguous/keep, print manual rm commands, delete NOTHING ([dir] [--ttl-days N])
|
|
275
271
|
./run.sh meta-bridge-fresh-cut # the ONE generation verb (the verb every v3-only rejection names): quiesce-check live sockets/markers/native-push conversations (refusing any surface it cannot inspect), archive meta-sessions/ + meta-mailbox/ to `<dir>.archive-<ts>`, clear dead transport residue, open an empty v3 generation. No migration, no restore — the archive is forensic only. EXIT CONTRACT (#54, `--help` prints it): 0 complete / 1 NOTHING MOVED (re-run, do not setup) / 2 usage / 3 cut transition incomplete (inspect) / 4 cut complete but residue cleanup failed (`setup` may run; re-run only before new citizen birth, otherwise remove residue manually)
|
|
276
272
|
./run.sh meta-facts # #65 owner-normalized READ-ONLY store projection: deterministic JSON {schemaVersion:1, storeDir, citizens: full v3 records sorted by gardenId, defects: {filename,message}} — THE listing contract emitted by the owner so consumers stop copying the certification. No liveness/sockets/transcript contents. EXIT: 0 readable (defects in-band; missing store = empty), 2 usage, 3 unreadable ([dir])
|
|
273
|
+
./run.sh peer-facts # #116 M2-a owner-normalized READ-ONLY peer projection: the same listEntwurfFacts provider through the same renderer entwurf_peers uses, emitted as deterministic JSON {schemaVersion:1, storeDir, peers, diagnostics} so a consumer never re-implements the placement join (whose pi half is a measured vendor floor). NOT the same bytes as an entwurf_peers call and deliberately so: that surface is a human listing (observationLimit, text over the wire), this is a machine projection with UNBOUNDED observation, because a rationed row says `unobserved` and a machine cannot tell that from "nobody could look". `placement` stays a STRUCTURED tagged union ({kind:"herdr-pane",paneId} / unobserved / none / ambiguous) — never the human `herdr <pane>` string. Publishes NO socket coordinate (ENTWURF_DIR selects what is PROBED, #50 C4) and NO herdr agent_status / interactive_ready / screen text. One placement read per listing, no watcher. EXIT: 0 readable (diagnostics in-band; missing store = empty), 2 usage, 3 unreadable ([dir])
|
|
277
274
|
./run.sh meta-bridge-managed-keys # 0.10.0 meta-bridge: print the SSOT of settings keys entwurf OWNS (consumers read this to stay disjoint — keyset-owner invariant)
|
|
278
275
|
./run.sh check-keyset-overlap <fragment.json...> # 0.10.0 meta-bridge: PREVENTIVE keyset guard — fail if a consumer fragment collides with any pi-owned key (cross-repo; not in pnpm check)
|
|
279
276
|
./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)
|
|
@@ -281,10 +278,13 @@ Usage:
|
|
|
281
278
|
./run.sh check-pack # publish gate (dry-run): npm pack --dry-run + tarball invariants (runtime-critical present, dev residue absent)
|
|
282
279
|
./run.sh check-pack-pin-matcher # pure self-test of check-pack-install's pin-leak matcher against synthetic .pnpm lookalikes, one cell per property: version boundary (@0.85.10 must leak, @0.85.1 bare or with either measured peer-hash must pass) and closure prefix (an off-pin @earendil-works/chord must leak — it carries no `pi-` prefix); snapshot-safe qualification oracle, also run first inside check-pack-install
|
|
283
280
|
./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
|
|
284
|
-
./run.sh check-pack-install # heavy publish gate (prepublishOnly): actual npm pack + tar -tf + fresh-temp install smoke with the pinned pi peers (pins derived from the package.json devDep; check-dep-versions binds them) + the npm-installed bridge BOOTS (tools/list) and DELIVERS (tools/call entwurf_v2 → .msg lands) + the installed all-absent and copilot-present (four-unit fake-vendor) `entwurf setup` rows + 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) + the INSTALLED-PACKAGE branch of the Copilot and OMP birth installers actually RUN (compiled entry selected, no raw .ts, and a real birth edge mints a citizen — the half a required-artifact list can never stand in for)
|
|
281
|
+
./run.sh check-pack-install # heavy publish gate (prepublishOnly): actual npm pack + tar -tf + fresh-temp install smoke + the #116 M3-b1 herdr-plugin RUNTIME VERIFIER run against that actual installed tarball (the focused check-herdr-runtime-bootstrap drives a fixture; THIS is where the real package is proven) with the pinned pi peers (pins derived from the package.json devDep; check-dep-versions binds them) + the npm-installed bridge BOOTS (tools/list) and DELIVERS (tools/call entwurf_v2 → .msg lands) + the installed all-absent and copilot-present (four-unit fake-vendor) `entwurf setup` rows + 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) + the INSTALLED-PACKAGE branch of the Copilot and OMP birth installers actually RUN (compiled entry selected, no raw .ts, and a real birth edge mints a citizen — the half a required-artifact list can never stand in for)
|
|
285
282
|
./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)
|
|
286
283
|
./run.sh install [project-dir] # INTERNAL part of `setup` (project .pi/settings.json wiring) + npm-consumer entry — prefer `setup`, don't call directly for dev
|
|
287
284
|
./run.sh remove [project-dir] # remove entwurf entries from project .pi/settings.json (project scope only; global user-scope citizen left intact)
|
|
285
|
+
./run.sh install-user-scope [--plugin-runtime <root>] # #116 M3-b2 PLUGIN-ONLY forward seam: compose ONLY the global pi citizen (user-scope packages[] + provider), never a project `.pi/settings.json` — a Herdr plugin activation has no project and must not invent one. With --plugin-runtime the provider's managed command becomes the absolute bridge under that certified stable runtime (a clean host has nothing entwurf on PATH); without it every byte is the historical bare-bin install. Inverse: remove-user-scope
|
|
286
|
+
./run.sh herdr-plugin-activate <backend...> # #116 M3-b2 public forward activation. Takes a subset of {pi, claude-code} and NOTHING else — the stable runtime root is DERIVED from XDG here and handed down, and both harness writers accept a named root only when it IS that derived one. Certifies the runtime-ready journal AND the installed tree, certifies the existing ledger and its recorded harness roots, plans ADD-ONLY (a backend absent from this request is retained, never removed), preflights every selected component before the first byte, then writes each component `pending` BEFORE mutating and checkpoints `active` the moment its writer returns — so a run that dies halfway leaves a record of how far it got. Which backends is M3-b3's question: this verb is handed the answer, it does not read Herdr's integration status
|
|
287
|
+
./run.sh herdr-plugin-deactivate # #116 M3-b2 public teardown of a Herdr-plugin activation. Herdr's `plugin uninstall` deletes its checkout and calls NO cleanup hook, so the harness wiring and the stable runtime are retained, not cleaned — this is the shipped verb that takes them back. Certifies the activation ledger, the resolved Pi/Claude roots and BOTH component inverses read-only first; mutates only when every preflight is green; then Claude -> Pi user scope -> runtime -> ledger LAST. A component failure stops with the runtime and ledger intact as retry authority. Static imports only: the process deletes the runtime it is running from, and a lazy import after that fails
|
|
288
288
|
./run.sh remove-user-scope # explicit GLOBAL inverse of install's user-scope citizen: drop entwurf from ~/.pi/agent/settings.json packages[] (affects ALL cwds — shared entry, not per-project). #86 C2: same-owner-only — a LIVE foreign owner refuses; a MISSING owner is removed only when package entry + package state + provider installerRoot all align (reported orphan cleanup)
|
|
289
289
|
./run.sh takeover-user-scope # #86 C2: operator-EXPLICIT ownership move of the shared user-scope registration to THIS root (old→new reported, packages[] entry + provider installerRoot together). The only writer over another owner — normal install/setup/remove refuse instead. No --force exists
|
|
290
290
|
./run.sh doctor-pi-package # #86 C2: package-side user-scope ownership verdict — unregistered / owned / owned-by-other(live) / legacy-no-state / mismatch / missing-owner (nonzero on defect verdicts). Provider runtime verdicts stay with doctor-pi-provider
|
|
@@ -656,8 +656,15 @@ codex_terminal_title() {
|
|
|
656
656
|
esac
|
|
657
657
|
}
|
|
658
658
|
|
|
659
|
+
# $1 (optional): a certified stable plugin runtime root. Given, the provider half is written in
|
|
660
|
+
# PLUGIN MODE — its managed command becomes the absolute bridge under that root instead of the bare
|
|
661
|
+
# bin. That is the whole difference, and it exists because a Herdr plugin activates on a host where
|
|
662
|
+
# nothing entwurf is on PATH, so a bare command cannot resolve. Absent, every byte is what it has
|
|
663
|
+
# always been (#116 M3-b2).
|
|
659
664
|
register_user_scope_citizen() {
|
|
660
665
|
local agent_dir="${PI_CODING_AGENT_DIR:-$HOME/.pi/agent}"
|
|
666
|
+
local plugin_mode=()
|
|
667
|
+
[ -n "${1:-}" ] && plugin_mode=(--plugin-runtime "$1")
|
|
661
668
|
# Shared idempotent implementation (also driven by smoke-user-scope-citizen).
|
|
662
669
|
# #86 C2: user scope carries a recorded OWNER (packageRoot in the pi-package
|
|
663
670
|
# install-state). Another owner — live or missing — makes this a zero-write
|
|
@@ -670,7 +677,7 @@ register_user_scope_citizen() {
|
|
|
670
677
|
python3 "$REPO_DIR/scripts/register-pi-package.py" "$agent_dir/settings.json" "$REPO_DIR" \
|
|
671
678
|
--scope user --state "$(_pi_package_state)" --preflight >/dev/null
|
|
672
679
|
python3 "$REPO_DIR/scripts/register-pi-provider.py" install "$agent_dir/settings.json" "$REPO_DIR" \
|
|
673
|
-
--scope user --state "$(_pi_provider_state)" --preflight >/dev/null
|
|
680
|
+
--scope user --state "$(_pi_provider_state)" ${plugin_mode[@]+"${plugin_mode[@]}"} --preflight >/dev/null
|
|
674
681
|
python3 "$REPO_DIR/scripts/register-pi-package.py" "$agent_dir/settings.json" "$REPO_DIR" \
|
|
675
682
|
--scope user --state "$(_pi_package_state)"
|
|
676
683
|
# #46 Task 2: own entwurfProvider.mcpServers.entwurf-bridge as the bare stable bin at USER scope
|
|
@@ -679,7 +686,33 @@ register_user_scope_citizen() {
|
|
|
679
686
|
# local and covered by `run.sh remove` (no state) — deliberate, reasoned asymmetry.
|
|
680
687
|
# #86 C2: the provider state records installerRoot, so the same foreign-owner refusal
|
|
681
688
|
# holds here — and the package refusal above fires FIRST, before any provider write.
|
|
682
|
-
python3 "$REPO_DIR/scripts/register-pi-provider.py" install "$agent_dir/settings.json" "$REPO_DIR" --scope user --state "$(_pi_provider_state)"
|
|
689
|
+
python3 "$REPO_DIR/scripts/register-pi-provider.py" install "$agent_dir/settings.json" "$REPO_DIR" --scope user --state "$(_pi_provider_state)" ${plugin_mode[@]+"${plugin_mode[@]}"}
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
# install-user-scope — the PLUGIN-ONLY forward seam (#116 M3-b2). Generic `install` writes both a
|
|
693
|
+
# project `.pi/settings.json` and the global user-scope citizen; a Herdr plugin activation has no
|
|
694
|
+
# project and must not invent one, so this verb composes the user-scope half and nothing else. Its
|
|
695
|
+
# inverse is the one that already existed, `remove-user-scope` — the asymmetry this closes is that
|
|
696
|
+
# the inverse was public and the forward was not.
|
|
697
|
+
install_user_scope() {
|
|
698
|
+
command -v python3 >/dev/null 2>&1 || { echo "[install-user-scope] requires python3 on PATH; nothing was written." >&2; exit 1; }
|
|
699
|
+
local plugin_runtime=""
|
|
700
|
+
while [ $# -gt 0 ]; do
|
|
701
|
+
case "$1" in
|
|
702
|
+
--plugin-runtime)
|
|
703
|
+
shift
|
|
704
|
+
# A flag with no value must not fall through to bare mode: the caller asked for the plugin
|
|
705
|
+
# runtime and would otherwise be told, silently, that it got it.
|
|
706
|
+
[ -n "${1:-}" ] || { echo "run.sh install-user-scope: --plugin-runtime requires a value" >&2; exit 2; }
|
|
707
|
+
plugin_runtime="$1" ;;
|
|
708
|
+
*) echo "usage: run.sh install-user-scope [--plugin-runtime <stable active root>]" >&2; exit 2 ;;
|
|
709
|
+
esac
|
|
710
|
+
shift
|
|
711
|
+
done
|
|
712
|
+
preflight_v3_store install-user-scope
|
|
713
|
+
section "install-user-scope: the GLOBAL pi citizen only (no project wiring)"
|
|
714
|
+
register_user_scope_citizen "$plugin_runtime"
|
|
715
|
+
ok "install-user-scope: user-scope packages[] + provider registered${plugin_runtime:+ (plugin runtime: $plugin_runtime)}"
|
|
683
716
|
}
|
|
684
717
|
|
|
685
718
|
# takeover-user-scope — the operator-explicit ownership move (#86 C2). The ONLY
|
|
@@ -1050,6 +1083,14 @@ check_copilot_launch() {
|
|
|
1050
1083
|
run_ts scripts/check-copilot-launch.ts
|
|
1051
1084
|
}
|
|
1052
1085
|
|
|
1086
|
+
check_pi_launch() {
|
|
1087
|
+
# #118 홉 2. Same oracle as check-copilot-launch and for the same reason: the subject is
|
|
1088
|
+
# a process replacement, so a fake vendor named `pi` on a sandbox PATH reports the argv,
|
|
1089
|
+
# env and pid it was handed — never a reading of the launcher's source. Also carries the
|
|
1090
|
+
# --entwurf-control literal cross-check, because the shell cannot import the TS constant.
|
|
1091
|
+
run_ts scripts/check-pi-launch.ts
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1053
1094
|
check_codex_app_server_launch() {
|
|
1054
1095
|
# #95: the managed app-server SPELLING, proved against a fake vendor. There is no second
|
|
1055
1096
|
# spelling of the address to compare any more — the launcher asks `codex-socket-path` — so
|
|
@@ -1140,123 +1181,28 @@ check_entwurf_v2_contract() {
|
|
|
1140
1181
|
run_ts scripts/check-entwurf-v2-contract.ts
|
|
1141
1182
|
}
|
|
1142
1183
|
|
|
1143
|
-
check_entwurf_v2_lock() {
|
|
1144
|
-
# Deterministic gate for 0.11 Stage 0 step 5a (버킷 B F2): the per-gid dispatch
|
|
1145
|
-
# LOCK primitive — what serializes concurrent in-domain dispatch at one
|
|
1146
|
-
# target (pi self-guards CREATE but not RESUME, 검증원장 F2). acquire =
|
|
1147
|
-
# openSync(lockPath,"wx") atomic; a second acquire without release =
|
|
1148
|
-
# target-locked conflict carrying the holder JSON (F2-P2 human cleanup). release
|
|
1149
|
-
# = unlink ONLY when the on-disk nonce is still ours (a successor's re-acquire
|
|
1150
|
-
# survives a late release). Stale reclaim ONLY for same host + ESRCH; EPERM
|
|
1151
|
-
# (other user's live pid) / different host / alive pid / unknown error all
|
|
1152
|
-
# fail-closed to conflict. Empty/corrupt lockfile = conflict, never
|
|
1153
|
-
# auto-deleted. F2-P1: a malformed gid throws before any path is built. Real
|
|
1154
|
-
# temp dir (wx atomicity under test); clock/nonce/pid/host/kill injected.
|
|
1155
|
-
run_ts scripts/check-entwurf-v2-lock.ts
|
|
1156
|
-
}
|
|
1157
|
-
|
|
1158
|
-
check_entwurf_v2_decider() {
|
|
1159
|
-
# Deterministic gate for 0.11 Stage 0 step 5b: the PURE dispatch decider
|
|
1160
|
-
# decideDispatch. Drives the frozen 7-step order over INJECTED fakes (target
|
|
1161
|
-
# lookup / lock / socket inspect+probe / preflight / capability), tracking lock
|
|
1162
|
-
# acquire+release so "reject ⇒ no plan AND no lock retained" is PROVEN. Covers:
|
|
1163
|
-
# bad-target/target-locked/target-address-conflict carry observedLiveness=null
|
|
1164
|
-
# (pre-probe), every other reject carries a measured value; a control-socket send
|
|
1165
|
-
# executes KEEPING the lock, meta-mailbox send takes NO lock (?7); an invalid gid throws before
|
|
1166
|
-
# any lookup (F2-P1). Pure, no IO, no API.
|
|
1167
|
-
run_ts scripts/check-entwurf-v2-decider.ts
|
|
1168
|
-
}
|
|
1169
|
-
|
|
1170
|
-
check_entwurf_v2_matrix() {
|
|
1171
|
-
# Deterministic gate for 0.11 Stage 0 step 5d-5 (a): the REACHABILITY + LOCK SSOT
|
|
1172
|
-
# TABLE. Drives the REAL decideDispatch over minimal injected fakes and fixes, as
|
|
1173
|
-
# one readable table, every (target kind → transport → lock class) cell the 5d-5
|
|
1174
|
-
# claim covers: bad-target/address-conflict/target-locked rejects, unsupported
|
|
1175
|
-
# meta-mailbox (deliverable) vs mailbox-undeliverable (inactive) reject,
|
|
1176
|
-
# in-domain control-socket (live) / released rejects
|
|
1177
|
-
# (ff-dormant, indeterminate, under-lock conflict). A coverage pass
|
|
1178
|
-
# FAILS if any transport / lock class / pre-probe reject is missing — a dropped
|
|
1179
|
-
# decider cell cannot pass silently. Thin coverage, NOT a decider re-impl; surface
|
|
1180
|
-
# parity stays in check-entwurf-v2-surface. Pure, no IO, no API.
|
|
1181
|
-
run_ts scripts/check-entwurf-v2-matrix.ts
|
|
1182
|
-
}
|
|
1183
|
-
|
|
1184
|
-
check_entwurf_v2_release() {
|
|
1185
|
-
# Deterministic gate for 0.11 Stage 0 step 5c-1: the PURE release-policy reducer
|
|
1186
|
-
# (decideReleasePolicy + reduceRelease) for the 5c transport hand. Proves the
|
|
1187
|
-
# Fable-3 "release-after-observation" timing as a pure state machine BEFORE any
|
|
1188
|
-
# send IO: meta-mailbox=never release (no lock), control-socket=release once
|
|
1189
|
-
# on send-final. The spawn-observation policy and its four release events went with
|
|
1190
|
-
# the transport in the visible-first cut; decideReleasePolicy still enforces the
|
|
1191
|
-
# lock-nullness invariant (?7). Pure, no IO.
|
|
1192
|
-
run_ts scripts/check-entwurf-v2-release.ts
|
|
1193
|
-
}
|
|
1194
|
-
|
|
1195
1184
|
check_entwurf_v2_send() {
|
|
1196
|
-
#
|
|
1197
|
-
#
|
|
1198
|
-
#
|
|
1199
|
-
#
|
|
1200
|
-
# re-
|
|
1201
|
-
#
|
|
1202
|
-
#
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
}
|
|
1206
|
-
|
|
1207
|
-
check_entwurf_v2_send_fallback() {
|
|
1208
|
-
# Deterministic gate for 0.11 Stage 0 step 5c-2b: the same-lock re-resolve RESOLVER
|
|
1209
|
-
# (resolveDeadControlSendFallback) the 5c-2a hand calls on a dead connect. Proves the
|
|
1210
|
-
# fire-and-forget re-resolve routing over injected fakes (no filesystem): alive->
|
|
1211
|
-
# control-socket retry (inspected socketPath) / dead(absent)->reject (dormant-fire-
|
|
1212
|
-
# forget-unsupported, and nothing is launched) / indeterminate->reject / unsupported+deliverable
|
|
1213
|
-
# ->meta-mailbox plan (mini-table, no inspect/probe) / unsupported+undeliverable->reject
|
|
1214
|
-
# / bad-target + address-conflict->reject pre-probe. Mis-wire (plan/lock gid) fails loud
|
|
1215
|
-
# before IO; inspect/probe throws PROPAGATE (the hand owns failed+release); the resolver
|
|
1216
|
-
# has NO release seam; every execute plan keeps the held gid.
|
|
1217
|
-
run_ts scripts/check-entwurf-v2-send-fallback.ts
|
|
1218
|
-
}
|
|
1219
|
-
|
|
1220
|
-
check_entwurf_v2_mailbox() {
|
|
1221
|
-
# Deterministic gate for 0.11 Stage 0 step 5c-4 (the LAST 5c transport slice): the
|
|
1222
|
-
# ENQUEUE-ONLY meta-mailbox SEND body (executeMetaMailboxSend) + its production
|
|
1223
|
-
# sendViaMailbox adapter (makeProductionSendViaMailbox). Proves the wiring over an
|
|
1224
|
-
# injected fake enqueue (no filesystem): sender present -> formatMetaMailboxBody with
|
|
1225
|
-
# plan.wantsReply threaded (yes/no in body, the deliberate divergence from legacy's
|
|
1226
|
-
# hard-coded false) / sender absent -> raw plan.message / enqueue opts EXACTLY
|
|
1227
|
-
# {gardenId: plan.targetGardenId, body, sessionsDir, mailboxDir} (no re-derivation) /
|
|
1228
|
-
# enqueue throw PROPAGATES (never folded into success:false — a mailbox has no in-band
|
|
1229
|
-
# refuse) / success -> {success:true}. Production adapter resolves {success:true},
|
|
1230
|
-
# consults senderProvider once, and NEVER touches the lock (a poison LockClaim whose
|
|
1231
|
-
# every access throws still resolves). Source guard: the lib code has NO release seam
|
|
1232
|
-
# and NO routing seam (no releaseLock / inspect / probe / resolve) — a lock leak or
|
|
1233
|
-
# re-route is structurally impossible.
|
|
1234
|
-
run_ts scripts/check-entwurf-v2-mailbox.ts
|
|
1185
|
+
# MUTANT EXECUTION COORDINATE, not a discovery path — the test is found by
|
|
1186
|
+
# check-tests-beside-behavior like every other beside-behaviour lane. This case exists
|
|
1187
|
+
# because the V2SEND-INBAND-REJECT-REASON mutant names it as gate argv, and attribution is
|
|
1188
|
+
# read from the JSON test titles `run_vitest` emits (run.sh:104-113). Narrow file filter: a
|
|
1189
|
+
# mutant pointed at the glob shim would re-run every beside-behaviour test once per mutant.
|
|
1190
|
+
# It stays inside check:contracts even though the discovery door also runs this file — see
|
|
1191
|
+
# check-release-gate-outcomes: a gate a mutant names must be reachable outside qualification.
|
|
1192
|
+
section "control-socket send hand (mutant execution coordinate)"
|
|
1193
|
+
run_vitest pi-extensions/lib/entwurf-v2-send.test.ts
|
|
1235
1194
|
}
|
|
1236
1195
|
|
|
1237
1196
|
check_entwurf_v2_native_push() {
|
|
1238
|
-
#
|
|
1239
|
-
#
|
|
1240
|
-
#
|
|
1241
|
-
#
|
|
1242
|
-
#
|
|
1243
|
-
#
|
|
1244
|
-
#
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
}
|
|
1248
|
-
|
|
1249
|
-
check_entwurf_v2_runner() {
|
|
1250
|
-
# Deterministic gate for 0.11 Stage 0 step 5d-1: the execute-router (executeDispatch) that
|
|
1251
|
-
# routes an already-decided DispatchDecision to its 5c transport hand and maps the outcome
|
|
1252
|
-
# to one outcome-rich EntwurfV2RunResult. Proves over injected fake hands (no socket/spawn/
|
|
1253
|
-
# timer): reject -> rejected (receipt+diagnostic carried, NO hand called) / control-socket ->
|
|
1254
|
-
# sendControl(plan, lock) / meta-mailbox -> sendMailbox(plan, NULL
|
|
1255
|
-
# lock, ?7). Carry-overs: N3 control `rejected` carries rejectReason verbatim; N1
|
|
1256
|
-
# SendDeliveredReleaseFailedError -> execution-failed{finalizedOutcome, releaseFailed,
|
|
1257
|
-
# retrySafe:false}; a plain hand throw -> execution-failed{retrySafe:false} with no
|
|
1258
|
-
# finalizedOutcome. Exactly one hand runs per execute.
|
|
1259
|
-
run_ts scripts/check-entwurf-v2-runner.ts
|
|
1197
|
+
# MUTANT EXECUTION COORDINATE, not a discovery path — the test is found by
|
|
1198
|
+
# check-tests-beside-behavior like every other beside-behaviour lane. This case exists
|
|
1199
|
+
# because the NATIVE-PUSH-RETRY-BYTE-IDENTITY mutant names it as gate argv, and attribution
|
|
1200
|
+
# is read from the JSON test titles `run_vitest` emits (run.sh:104-113). Narrow file filter:
|
|
1201
|
+
# a mutant pointed at the glob shim would re-run every beside-behaviour test once per mutant.
|
|
1202
|
+
# It stays inside check:contracts even though the discovery door also runs this file — see
|
|
1203
|
+
# check-release-gate-outcomes: a gate a mutant names must be reachable outside qualification.
|
|
1204
|
+
section "native-push send hand (mutant execution coordinate)"
|
|
1205
|
+
run_vitest pi-extensions/lib/entwurf-v2-native-push.test.ts
|
|
1260
1206
|
}
|
|
1261
1207
|
|
|
1262
1208
|
check_entwurf_v2_surface() {
|
|
@@ -1323,15 +1269,22 @@ check_compaction_send_guard() {
|
|
|
1323
1269
|
}
|
|
1324
1270
|
|
|
1325
1271
|
check_entwurf_resume_args() {
|
|
1326
|
-
#
|
|
1327
|
-
#
|
|
1328
|
-
#
|
|
1329
|
-
#
|
|
1330
|
-
#
|
|
1331
|
-
#
|
|
1332
|
-
#
|
|
1333
|
-
# file
|
|
1334
|
-
|
|
1272
|
+
# NOT a discovery path — the test is found by check-tests-beside-behavior like every other
|
|
1273
|
+
# beside-behaviour lane. This case exists as the MUTANT EXECUTION COORDINATE: the six
|
|
1274
|
+
# resume-args mutants name it as their gate argv, the qualification runner re-invokes that
|
|
1275
|
+
# argv once per mutant, and attribution is read from the JSON test titles `run_vitest` emits
|
|
1276
|
+
# under ENTWURF_MUTATION_VITEST_REPORT (run.sh:104-113). A gate argv that called vitest
|
|
1277
|
+
# directly would skip that reporter and every kill would come back unattributed.
|
|
1278
|
+
#
|
|
1279
|
+
# The file filter is NARROW on purpose. Pointing a mutant at the glob shim would make the
|
|
1280
|
+
# runner re-run every beside-behaviour test once per mutant.
|
|
1281
|
+
#
|
|
1282
|
+
# It stays inside check:contracts even though the discovery door also runs this file, and the
|
|
1283
|
+
# duplicate is deliberate: check-release-gate-outcomes requires every gate a mutant names to
|
|
1284
|
+
# run inside check:full, because a gate reachable ONLY through qualification makes the
|
|
1285
|
+
# 28-minute mutant body the only thing that can notice it going red on a clean tree.
|
|
1286
|
+
section "resume-argv SSOT (mutant execution coordinate)"
|
|
1287
|
+
run_vitest pi-extensions/lib/entwurf-resume-args.test.ts
|
|
1335
1288
|
}
|
|
1336
1289
|
|
|
1337
1290
|
check_mux_launcher_fence() {
|
|
@@ -1349,6 +1302,17 @@ check_mux_launcher_fence() {
|
|
|
1349
1302
|
run_ts scripts/check-mux-launcher-fence.ts
|
|
1350
1303
|
}
|
|
1351
1304
|
|
|
1305
|
+
check_typing_call_fence() {
|
|
1306
|
+
# The rule kept since day one, now kept by NAME: entwurf composes arguments and letters, never
|
|
1307
|
+
# keystrokes. An outside reader measured the tree on 2026-09-17 and found the law perfectly kept
|
|
1308
|
+
# and nothing keeping it — zero typing calls in pi-extensions/, zero checks and zero mutants that
|
|
1309
|
+
# would notice one arriving. Scans tracked production sources under pi-extensions/ and mcp/ with
|
|
1310
|
+
# comments blanked and string literals kept, because a typing call spelled as a string is the
|
|
1311
|
+
# call. Comment exemption is load-bearing: the sentence that states the rule contains the names
|
|
1312
|
+
# the rule forbids. Offline, no binaries, no fixtures on disk.
|
|
1313
|
+
run_ts scripts/check-typing-call-fence.ts
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1352
1316
|
check_mux_parent_artifact() {
|
|
1353
1317
|
# Deterministic gate for the tracked scrubbed parent-transcript fixture
|
|
1354
1318
|
# (scripts/fixtures/mux-parent-transcript.scrubbed.jsonl). The fixture is a version-pinned
|
|
@@ -1376,16 +1340,16 @@ check_mux_resume_call() {
|
|
|
1376
1340
|
}
|
|
1377
1341
|
|
|
1378
1342
|
check_entwurf_v2_visible_resume() {
|
|
1379
|
-
#
|
|
1380
|
-
#
|
|
1381
|
-
#
|
|
1382
|
-
#
|
|
1383
|
-
#
|
|
1384
|
-
#
|
|
1385
|
-
#
|
|
1386
|
-
#
|
|
1387
|
-
|
|
1388
|
-
|
|
1343
|
+
# MUTANT EXECUTION COORDINATE, not a discovery path — the test is found by
|
|
1344
|
+
# check-tests-beside-behavior like every other beside-behaviour lane. This case exists
|
|
1345
|
+
# because SEVENTEEN mutants name it as gate argv, and attribution is read from the JSON test
|
|
1346
|
+
# titles `run_vitest` emits (run.sh:104-113), which is why each of those claims is its own
|
|
1347
|
+
# test title. Narrow file filter: pointing them at the glob shim would re-run every
|
|
1348
|
+
# beside-behaviour test seventeen times. It stays inside check:contracts even though the
|
|
1349
|
+
# discovery door also runs this file — see check-release-gate-outcomes: a gate a mutant names
|
|
1350
|
+
# must be reachable outside qualification.
|
|
1351
|
+
section "visible same-id resume (mutant execution coordinate)"
|
|
1352
|
+
run_vitest pi-extensions/lib/entwurf-v2-visible-resume.test.ts
|
|
1389
1353
|
}
|
|
1390
1354
|
|
|
1391
1355
|
check_resume_launch_identity() {
|
|
@@ -1449,7 +1413,7 @@ check_mux_fresh_call() {
|
|
|
1449
1413
|
# codex-caller-seat rides here for the same reason as the two preflights: the leaf exists
|
|
1450
1414
|
# only as freshCall's Codex placement input (#95 lane B), so certifying it apart from the
|
|
1451
1415
|
# composition it feeds would let the two halves of one seat decision drift.
|
|
1452
|
-
run_vitest test/mux-fresh-call.test.ts test/copilot-fresh-preflight.test.ts test/codex-fresh-preflight.test.ts test/fresh-call-surfaces.contract.test.ts test/fresh-call-provider.contract.test.ts test/omp-fresh-bootstrap.contract.test.ts test/tmux-coordinate-row.test.ts test/codex-fresh-live-protocol.test.ts test/codex-caller-seat.test.ts
|
|
1416
|
+
run_vitest test/mux-fresh-call.test.ts test/fresh-call-composition.contract.test.ts test/copilot-fresh-preflight.test.ts test/codex-fresh-preflight.test.ts test/fresh-call-surfaces.contract.test.ts test/fresh-call-provider.contract.test.ts test/omp-fresh-bootstrap.contract.test.ts test/tmux-coordinate-row.test.ts test/codex-fresh-live-protocol.test.ts test/codex-caller-seat.test.ts
|
|
1453
1417
|
}
|
|
1454
1418
|
|
|
1455
1419
|
smoke_mux_fresh_call_live() {
|
|
@@ -1732,7 +1696,7 @@ smoke_acp_cortex_live() {
|
|
|
1732
1696
|
|
|
1733
1697
|
smoke_entwurf_v2_matrix_live() {
|
|
1734
1698
|
# LIVE sentinel for 0.11 Stage 0 step 5d-5 (D4-b) — kept OUT of `pnpm check`. The deterministic
|
|
1735
|
-
# sibling (
|
|
1699
|
+
# sibling (pi-extensions/lib/entwurf-v2-decider.matrix.test.ts) fixes every (target kind → transport → lock) cell over fakes
|
|
1736
1700
|
# with ZERO IO; this drives the REAL production runEntwurfV2 deps against REAL OS objects on the
|
|
1737
1701
|
# substrate happy path across 3 cells: C1 control-socket (a real `pi --entwurf-control` resident
|
|
1738
1702
|
# → control-socket RPC send → lock acquire→release ×1), C2 meta-mailbox deliverable (armed
|
|
@@ -1750,18 +1714,6 @@ smoke_entwurf_v2_matrix_live() {
|
|
|
1750
1714
|
run_ts scripts/smoke-entwurf-v2-matrix-live.ts
|
|
1751
1715
|
}
|
|
1752
1716
|
|
|
1753
|
-
check_entwurf_facts() {
|
|
1754
|
-
# Deterministic gate for 0.11 Stage 0 step 4 (fact-provider slice 1): the PURE
|
|
1755
|
-
# fact core. Locks the PeerFact shape + R1/R3b liveness invariant before any IO
|
|
1756
|
-
# wiring (gate-first). R1: out-of-domain backend (claude-code/codex/antigravity)
|
|
1757
|
-
# → unsupported for EVERY socket input (never coerced to the socket value or
|
|
1758
|
-
# dead). R3b: in-domain pi → alive/dead/indeterminate, null → indeterminate
|
|
1759
|
-
# (no proof ≠ dead). facts-only keyset: identity facts + liveness, NO
|
|
1760
|
-
# verb-routing (resumable/sendable/transport/dispatch/action) and NO
|
|
1761
|
-
# transcriptPath (동결결정 10). Pure, no IO, no API.
|
|
1762
|
-
run_ts scripts/check-entwurf-facts.ts
|
|
1763
|
-
}
|
|
1764
|
-
|
|
1765
1717
|
check_control_socket_path() {
|
|
1766
1718
|
# Deterministic gate: the control-socket path grammar `<dir>/<gid>.sock` has ONE
|
|
1767
1719
|
# definition (pi-extensions/lib/control-socket-path.js) instead of one per
|
|
@@ -1772,18 +1724,6 @@ check_control_socket_path() {
|
|
|
1772
1724
|
run_ts scripts/check-control-socket-path.ts
|
|
1773
1725
|
}
|
|
1774
1726
|
|
|
1775
|
-
check_socket_discovery() {
|
|
1776
|
-
# Deterministic gate for 0.11 Stage 0 step 4 (fact-provider slice 3): the
|
|
1777
|
-
# SOCKET-axis wiring scanSocketProbes. Probes the union of (dir sockets) ∪
|
|
1778
|
-
# (every in-domain pi citizen's canonical path) so a dormant citizen with no
|
|
1779
|
-
# socket file reads dead (ENOENT) → resumable, never an unprobed gap (slice 2
|
|
1780
|
-
# throws on that). Three-valued throughout — a stalled socket stays
|
|
1781
|
-
# indeterminate (F3), never folded to dead by an alive-only listing. Dir
|
|
1782
|
-
# hygiene (non-.sock / malformed names ignored), dedup, missing-dir, sort, and
|
|
1783
|
-
# an end-to-end scanSocketProbes→resolveFactList. readdir/probe injected, no IO.
|
|
1784
|
-
run_ts scripts/check-socket-discovery.ts
|
|
1785
|
-
}
|
|
1786
|
-
|
|
1787
1727
|
check_meta_listing() {
|
|
1788
1728
|
# Deterministic gate for 0.11 Stage 0 step 4 (fact-provider slice 4a): the
|
|
1789
1729
|
# meta-store axis listAllMetaIdentities. Explicit-partial: a parse failure or
|
|
@@ -1806,6 +1746,117 @@ check_meta_facts() {
|
|
|
1806
1746
|
run_ts scripts/check-meta-facts.ts
|
|
1807
1747
|
}
|
|
1808
1748
|
|
|
1749
|
+
check_herdr_plugin() {
|
|
1750
|
+
# Deterministic gate for the #116 M2-b herdr plugin (`plugins/herdr/`). Two halves.
|
|
1751
|
+
# STATIC: the manifest is parsed with the package's own TOML reader and held to the
|
|
1752
|
+
# shape herdr 0.9.0 accepts (min_herdr_version required + semver, dot-free pane id,
|
|
1753
|
+
# popup-only sizing, argv arrays), and the ABSENT sections are asserted as hard as the
|
|
1754
|
+
# present ones — [[startup]]/[[events]] are where a watcher would grow, [[actions]]
|
|
1755
|
+
# would send a report nobody can read into a 64 KiB log. ([[build]] LEFT that list in
|
|
1756
|
+
# #116 M3-b3; the one runner it may call is check-herdr-plugin-build's subject.)
|
|
1757
|
+
# BEHAVIOURAL: the REAL pane entry is
|
|
1758
|
+
# spawned with a stub `entwurf` and a stub HERDR_BIN_PATH that log every call, so
|
|
1759
|
+
# "exactly one read each per open" is COUNTED; success, skip-by-name, four distinct
|
|
1760
|
+
# named refusals, the ambiguous pane, diagnostics and the no-write claim are each
|
|
1761
|
+
# driven for real. The stub stands for the CONSUMER's input, never for herdr's
|
|
1762
|
+
# behaviour — no claim about herdr is made here, which is why this is not the fake
|
|
1763
|
+
# fixture check-herdr-sandbox forbids. No herdr binary, no Entwurf install, no writes
|
|
1764
|
+
# outside mkdtemp.
|
|
1765
|
+
run_ts scripts/check-herdr-plugin.ts
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
check_herdr_activation() {
|
|
1769
|
+
# Deterministic gate for the #116 M3-b2 scoped activation. The pi cells drive the REAL writers
|
|
1770
|
+
# (run.sh install-user-scope, register-pi-provider.py, remove-user-scope) against a sandboxed
|
|
1771
|
+
# HOME/XDG/PI_CODING_AGENT_DIR, so the bytes asserted are bytes those writers produced. The Claude
|
|
1772
|
+
# cells drive the REAL state owner's pure surfaces; the vendor `claude` CLI is NOT invoked and its
|
|
1773
|
+
# acceptance of an absolute executable stays a named LIVE boundary. The oracle for a roundtrip is
|
|
1774
|
+
# JSON equality plus unrelated-key survival, never byte equality: the pi settings writer preserves
|
|
1775
|
+
# semantics but may reindent (measured 2026-09-16). No network, no operator HOME.
|
|
1776
|
+
run_ts scripts/check-herdr-activation.ts
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
check_herdr_runtime_bootstrap() {
|
|
1780
|
+
# Deterministic gate for the #116 M3-b1 runtime transaction. Every root comes from XDG, so the
|
|
1781
|
+
# gate hands the module a mkdtemp HOME and asserts nothing appeared outside the two XDG roots it
|
|
1782
|
+
# was given. `acquire` is a FUNCTION seam: the fixture stands in for the REGISTRY, so this gate
|
|
1783
|
+
# proves the transaction (ownership certification, torn-swap recovery, leftover states, rollback,
|
|
1784
|
+
# idempotence, the inverse) but NOT that the real Entwurf package boots — that actual-package
|
|
1785
|
+
# proof is a named cell inside check-pack-install, which runs this module's own
|
|
1786
|
+
# verifyInstalledRuntime against the tarball it packed and installed. npm is pinned here the only
|
|
1787
|
+
# way a hermetic gate honestly can: the production pack/install argv and the npm environment are
|
|
1788
|
+
# pure functions and are asserted directly. No network, no npm, no operator HOME, no writes
|
|
1789
|
+
# outside mkdtemp.
|
|
1790
|
+
run_ts scripts/check-herdr-runtime-bootstrap.ts
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
check_herdr_plugin_profile() {
|
|
1794
|
+
# Deterministic gate for the #116 M3-a pure leaf: the function that turns ONE
|
|
1795
|
+
# `herdr integration status` listing into this plugin's activation plan. Every listing
|
|
1796
|
+
# is an INPUT STRING (verbatim herdr 0.9.0 rows measured 2026-09-16 in a sandboxed
|
|
1797
|
+
# HOME/XDG, plus the two forms cross-checked in herdr source at c77af189), so nothing
|
|
1798
|
+
# here claims a fact about herdr. Pins: the closed atom->backend table, exactly one row
|
|
1799
|
+
# per selected atom or a NAMED refusal (never a first/last-match guess), the state read
|
|
1800
|
+
# FRONT-ANCHORED so an operator's directory name cannot spell a verdict, the display
|
|
1801
|
+
# path leaving no trace in the plan, outdated/needs-repair as named FAILs (measured:
|
|
1802
|
+
# empty, unreadable and marker-less files all collapse into `outdated`), not-installed
|
|
1803
|
+
# as a zero-write SKIP, `current` as herdr's admission with no second floor invented
|
|
1804
|
+
# here, OpenCode observed but never planned, and the leaf's purity. No herdr binary, no
|
|
1805
|
+
# Entwurf install, no environment read, no writes.
|
|
1806
|
+
run_ts scripts/check-herdr-plugin-profile.ts
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1809
|
+
check_herdr_plugin_build() {
|
|
1810
|
+
# Deterministic gate for the #116 M3-b3 build runner (`plugins/herdr/lib/build.mjs`) — the ONE
|
|
1811
|
+
# command Herdr's `[[build]]` calls, and the order it composes: integration status -> pure
|
|
1812
|
+
# profile -> runtime bootstrap -> activation THROUGH THE INSTALLED PACKAGE. There is no fake
|
|
1813
|
+
# herdr binary here (the repo forbids one); the listing arrives through the runner's own spawn
|
|
1814
|
+
# seam as the prose shape 0.9.0 emits, and one cell drives the REAL binary with a named SKIP
|
|
1815
|
+
# when the host has none (ENTWURF_REQUIRE_HERDR=1 makes absence red). Pins: exactly one
|
|
1816
|
+
# [[build]] whose argv is `node lib/build.mjs` and a runner the npm package does NOT ship;
|
|
1817
|
+
# missing herdr vs unanswerable status as separate named refusals with zero writes; a selected
|
|
1818
|
+
# atom that is outdated/needs-repair/malformed/duplicated failing BEFORE any runtime work;
|
|
1819
|
+
# A-empty as a clean exit 0 that writes nothing (no runtime, no wiring, no removal); the
|
|
1820
|
+
# activation entry resolved under the stable runtime with absent/incapable as distinct named
|
|
1821
|
+
# refusals and no checkout fallback; the two-stage {pi} -> {pi, claude-code} composition where a
|
|
1822
|
+
# new commit reinstalls and OpenCode is never named; and the post-build Herdr commit gap being a
|
|
1823
|
+
# NAMED gap, not an atomic transaction. The real `herdr plugin install` + real `npm pack
|
|
1824
|
+
# git+https://...#<sha>` journey needs the network and lives in smoke-herdr-plugin-build-live.
|
|
1825
|
+
run_ts scripts/check-herdr-plugin-build.ts
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
smoke_herdr_plugin_build_live() {
|
|
1829
|
+
# #116 M3-b3 LIVE: a REAL `herdr plugin install` drives this plugin's REAL `[[build]]`, which
|
|
1830
|
+
# packs the FIXED product git spec for real. Needs LIVE=1, herdr/git/npm, and the NETWORK (a
|
|
1831
|
+
# git-spec pack builds the bridge through `prepare`, which installs devDependencies) — which is
|
|
1832
|
+
# the axis check-herdr-plugin-build cannot have and does not claim. Hermetic by two substitutions
|
|
1833
|
+
# only: sandbox HOME/XDG, and git `insteadOf` redirecting the unchanged product remote to a local
|
|
1834
|
+
# bare clone of this worktree, so an unpushed candidate is testable without weakening the argv.
|
|
1835
|
+
# Cells 1/2/4 are the real-Herdr journey: remote-commit available, reinstall widening to
|
|
1836
|
+
# {pi, claude-code} with OpenCode zero-write and ONE COUNTED vendor MCP owner entry, and the
|
|
1837
|
+
# post-build gap where herdr's own commit fails after our runner already rebound the activation.
|
|
1838
|
+
# Cell 3 is deliberately NOT a journey: it calls the shipped runtime leaf directly with the fixed
|
|
1839
|
+
# product argv at a commit the remote lacks, so it is acquisition-leaf evidence — named refusal,
|
|
1840
|
+
# active runtime/ledger/harness bytes preserved, journal left as the certified retry authority.
|
|
1841
|
+
run_ts scripts/smoke-herdr-plugin-build-live.ts
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1844
|
+
check_peer_facts() {
|
|
1845
|
+
# Deterministic gate for the #116 M2-a observed-peer projection. Drives the REAL
|
|
1846
|
+
# scripts/peer-facts.ts as a subprocess with every ambient root replaced (store,
|
|
1847
|
+
# ENTWURF_DIR, HOME, XDG_DATA_HOME, pi agent dir), so the live store, sockets and
|
|
1848
|
+
# markers are never read. Owns only what this verb ADDS on top of the provider:
|
|
1849
|
+
# placement crosses as the structured tagged union rather than the human string,
|
|
1850
|
+
# the peer keyset is exactly the provider's facts (no herdr agent_status may enter
|
|
1851
|
+
# an entwurf payload — that is the door observed activity would use to become
|
|
1852
|
+
# delivery liveness) and no socket coordinate is published at all, diagnostics ride
|
|
1853
|
+
# in-band, the probed socket world is the one ENTWURF_DIR names (proved by a
|
|
1854
|
+
# record-less socket surfacing — there is no field to echo), no observation budget
|
|
1855
|
+
# rations a machine projection, exit contract 0/2/3,
|
|
1856
|
+
# plus dispatch + compiled-twin reachability. No herdr binary, no model turn.
|
|
1857
|
+
run_ts scripts/check-peer-facts.ts
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1809
1860
|
check_entwurf_fact_provider() {
|
|
1810
1861
|
# Deterministic gate for 0.11 Stage 0 step 4 (fact-provider slice 4b): the
|
|
1811
1862
|
# ASSEMBLY layer listEntwurfFacts. listAllMetaIdentities → scanSocketProbes →
|
|
@@ -1820,6 +1871,94 @@ check_entwurf_fact_provider() {
|
|
|
1820
1871
|
run_ts scripts/check-entwurf-fact-provider.ts
|
|
1821
1872
|
}
|
|
1822
1873
|
|
|
1874
|
+
check_herdr_placement() {
|
|
1875
|
+
# Deterministic gate for #116 S1: the placement evidence axis
|
|
1876
|
+
# (pi-extensions/lib/herdr-placement.ts) plus the ONE read the fact provider is
|
|
1877
|
+
# allowed. Pins only what is decidable WITHOUT a herdr binary — payload parse, the
|
|
1878
|
+
# two measured join rules, the ambiguity rule, the four-word vocabulary, the render,
|
|
1879
|
+
# the once-per-listing read, and the STRUCTURAL absence of placement from every
|
|
1880
|
+
# entwurf_v2 dispatch module. There is no fake herdr here for the same reason
|
|
1881
|
+
# check-mux-placement refuses a fake tmux: a stand-in authors the contract before the
|
|
1882
|
+
# real thing is measured. Every payload literal is verbatim herdr 0.9.0 output
|
|
1883
|
+
# recorded 2026-09-14. The real-binary half is an isolated private herdr server
|
|
1884
|
+
# (sandbox HOME/XDG, no client attach); the join/callback round trip stays LIVE.
|
|
1885
|
+
run_ts scripts/check-herdr-placement.ts
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
check_herdr_fresh_call() {
|
|
1889
|
+
# Deterministic gate for #116 S2-c1: the herdr LAUNCH rail core
|
|
1890
|
+
# (pi-extensions/lib/herdr-fresh-call.ts). Pins only what is decidable WITHOUT a herdr
|
|
1891
|
+
# binary — the import fence that keeps the two rails separately deletable, the closed
|
|
1892
|
+
# pi|claude-code pilot, the option-G one-line JSON encoding (exact round trip, zero
|
|
1893
|
+
# Unicode Cc including the C1 block JSON.stringify leaves literal, max public task),
|
|
1894
|
+
# the pre-mutation refusals with the CLI never invoked, the ONE placement policy (a new
|
|
1895
|
+
# tab in the caller's own workspace, read from herdr's own answer about the caller's pane
|
|
1896
|
+
# and never from a pane id's shape, focus left alone), tab-create/start/get/close argv
|
|
1897
|
+
# grammar, strict response parsing, the opaque pane id, the receipt's missing address,
|
|
1898
|
+
# and the conditional-close matrix. No fake herdr executable and no fake server, for the
|
|
1899
|
+
# reason check-mux-placement refuses a fake tmux. Payload literals are verbatim herdr
|
|
1900
|
+
# 0.9.0 output (pane/tab replies 2026-09-15, agent_started 2026-09-14); the real-binary
|
|
1901
|
+
# half is an isolated private server (c2).
|
|
1902
|
+
run_ts scripts/check-herdr-fresh-call.ts
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1905
|
+
check_herdr_sandbox() {
|
|
1906
|
+
# REAL herdr acceptance for #116 C2a: a PRIVATE herdr server in a fully sandboxed
|
|
1907
|
+
# HOME/XDG/PI_CODING_AGENT_DIR creates a tab with the production placement argv, installs
|
|
1908
|
+
# herdr's own pi integration, and starts a BLANK pi in that tab's initial pane from ONE
|
|
1909
|
+
# checkout package registration. Asserts the official witness
|
|
1910
|
+
# triple, the strict path->native id join, exactly one sandbox V3 record, a control socket
|
|
1911
|
+
# that answers the production probe, and the conditional-close decision in both directions
|
|
1912
|
+
# (refuses a pane holding an agent, reclaims one we own and with it the tab it was the
|
|
1913
|
+
# whole of). No fake herdr, no model turn, no
|
|
1914
|
+
# credentials, no keystrokes; the fixture's `pi --approve` is a one-run authorisation that
|
|
1915
|
+
# writes no trust state and never enters production argv. herdr is an OPTIONAL rail: an
|
|
1916
|
+
# absent binary prints a named SKIP and exits 0, a PRESENT-but-incomplete rail FAILS, and
|
|
1917
|
+
# ENTWURF_REQUIRE_HERDR=1 turns absence itself into a failure (CI admission is C2b).
|
|
1918
|
+
run_ts scripts/check-herdr-sandbox.ts
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1921
|
+
check_herdr_supply() {
|
|
1922
|
+
# Deterministic supply-authority gate for #116 C2b: WHICH herdr bytes CI runs, and who
|
|
1923
|
+
# decides. No herdr binary and no network — it reads scripts/fixtures/herdr-supply.json,
|
|
1924
|
+
# scripts/install-herdr-ci.sh and .github/workflows/ci.yml as text. Pins the manifest as the
|
|
1925
|
+
# only copy of version/tag/digest, the digest shape, the fail-closed architecture map, the
|
|
1926
|
+
# verify-before-chmod order, the absence of curl-pipe/latest/package-manager/global install,
|
|
1927
|
+
# the absence of a `gh attestation verify` dependency (measured: gh 2.97.0 cannot close
|
|
1928
|
+
# herdr's in-toto release predicate), the CI install-before-floor order with
|
|
1929
|
+
# ENTWURF_REQUIRE_HERDR=1, Linux-job-only wiring, that no product script or bin reaches the
|
|
1930
|
+
# installer (Hard Rule 17), and that the x86_64 asset stays recorded as never executed.
|
|
1931
|
+
run_ts scripts/check-herdr-supply.ts
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1934
|
+
check_fresh_call_dispatch() {
|
|
1935
|
+
# Deterministic gate for #116 C3: the seam ABOVE the two rails. Pins that the rail is a
|
|
1936
|
+
# capability fact (exactly HERDR_ENV=1) and never a caller parameter, that neither direction
|
|
1937
|
+
# falls back, that a non-pilot backend refuses inside herdr before any preflight or mutation,
|
|
1938
|
+
# that the Codex preflight keeps its pre-existing ordering on the tmux path only, that one
|
|
1939
|
+
# nonce per call reaches the selected rail, that BOTH public surfaces reach one composition
|
|
1940
|
+
# root (no second copy of rail choice, preflight ordering or rendering), that pi still uses
|
|
1941
|
+
# its lazy dynamic import, and that herdr rendering shows view-not-address plus which recovery
|
|
1942
|
+
# happened. No tmux and no herdr binary: the seam runs with an injected spawn.
|
|
1943
|
+
run_ts scripts/check-fresh-call-dispatch.ts
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1946
|
+
smoke_herdr_fresh_call_live() {
|
|
1947
|
+
# LIVE acceptance for the #116 herdr rail: a REAL caller that is itself inside a herdr pane
|
|
1948
|
+
# invokes the REAL PUBLIC entwurf_fresh_call, and a REAL child's first model action is the
|
|
1949
|
+
# nonce callback. Opt-in (LIVE=1), four model turns, one private herdr server PER CELL —
|
|
1950
|
+
# herdr's socket follows XDG_CONFIG_HOME and each cell is fenced into its own.
|
|
1951
|
+
# `[측정 2026-09-14]` BOTH runtimes keep the operator's REAL HOME and real auth roots: a pi
|
|
1952
|
+
# given a fixture HOME dies before readiness (an installed extension resolves its npm
|
|
1953
|
+
# dependency through $HOME) and a claude given one enters first-run onboarding. Only
|
|
1954
|
+
# Entwurf-OWNED writes are fenced into the fixture (XDG roots, the meta roots, the v2 lock
|
|
1955
|
+
# dir). Evidence is source receipts only, in two named grades: pi from its own vendor
|
|
1956
|
+
# transcript, claude from the runtime's own entwurf-bridge MCP activity log joined by exact
|
|
1957
|
+
# sessionId plus entwurf's mailbox artifact and hook journal — never screen text, never a
|
|
1958
|
+
# keystroke. The operator's herdr panes are snapshotted before and after.
|
|
1959
|
+
run_ts scripts/smoke-herdr-fresh-call-live.ts
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1823
1962
|
check_entwurf_peers_surface() {
|
|
1824
1963
|
# Deterministic gate for 0.11 Stage 0 step 4 (fact-provider slice 4c; #50 C4
|
|
1825
1964
|
# re-author): the MCP entwurf_peers RENDER/PAYLOAD layer renderEntwurfPeers.
|
|
@@ -2820,6 +2959,70 @@ check_acp_sdk_surface() {
|
|
|
2820
2959
|
run_vitest test/acp-sdk-surface.contract.test.ts
|
|
2821
2960
|
}
|
|
2822
2961
|
|
|
2962
|
+
check_tests_beside_behavior() {
|
|
2963
|
+
# The DISCOVERY door (issue #119 V2). A `.test.ts` beside a pi extension or a
|
|
2964
|
+
# `.test.mjs` beside the herdr plugin lib is run by public `pnpm check` without
|
|
2965
|
+
# anyone adding its filename anywhere. That is the whole contract: the other two
|
|
2966
|
+
# vitest shims list their files, and a door that also listed files would shut
|
|
2967
|
+
# again the first time someone forgot.
|
|
2968
|
+
#
|
|
2969
|
+
# The globs are EXPANDED here, not handed to vitest as positional filters. The first
|
|
2970
|
+
# cut passed `pi-extensions plugins/herdr` as positionals, and vitest reads a positional
|
|
2971
|
+
# as a path SUBSTRING — so `test/pi-extensions-probe.test.ts` was selected too, which is
|
|
2972
|
+
# the opposite of the two-locations contract this gate exists to hold (measured: that file
|
|
2973
|
+
# ran as "Test Files 1 passed"). Expanding first keeps the selected set exactly the two
|
|
2974
|
+
# globs while the shim still contains no filename: it computes them every run.
|
|
2975
|
+
#
|
|
2976
|
+
# Keeping these globs out of the other two shims stays deliberate — those are mutant gate
|
|
2977
|
+
# argv (mux-fresh-call carries 56), and widening their include would rerun unrelated tests
|
|
2978
|
+
# once per mutant.
|
|
2979
|
+
#
|
|
2980
|
+
# An empty expansion RETURNS rather than calling vitest with no arguments, because a
|
|
2981
|
+
# vitest run with no filter runs the whole include — that is, all of test/**. The honest
|
|
2982
|
+
# steady state is "the door is open and unused", and it must not be spelled as a run.
|
|
2983
|
+
section "tests beside behavior (discovery, not a filename list)"
|
|
2984
|
+
local cfg="$REPO_DIR/vitest.config.ts" glob
|
|
2985
|
+
for glob in 'pi-extensions/**/*.test.ts' 'plugins/herdr/**/*.test.mjs'; do
|
|
2986
|
+
if ! grep -qF "\"$glob\"" "$cfg"; then
|
|
2987
|
+
fail "[check-tests-beside-behavior] vitest.config.ts no longer includes $glob — the discovery door is shut, and an expansion that found nothing would still pass"
|
|
2988
|
+
return 1
|
|
2989
|
+
fi
|
|
2990
|
+
done
|
|
2991
|
+
|
|
2992
|
+
local -a selected=()
|
|
2993
|
+
local f
|
|
2994
|
+
while IFS= read -r f; do
|
|
2995
|
+
[ -n "$f" ] && selected+=("$f")
|
|
2996
|
+
done < <(cd "$REPO_DIR" && {
|
|
2997
|
+
find pi-extensions -name node_modules -prune -o -type f -name '*.test.ts' -print
|
|
2998
|
+
find plugins/herdr -name node_modules -prune -o -type f -name '*.test.mjs' -print
|
|
2999
|
+
} 2>/dev/null | LC_ALL=C sort)
|
|
3000
|
+
|
|
3001
|
+
# Independent of HOW the expansion was produced: every selected path must be under one of
|
|
3002
|
+
# the two declared locations, and none may come from test/. The second clause is the one
|
|
3003
|
+
# that names the defect above, so it is asserted rather than left implied by the first.
|
|
3004
|
+
for f in "${selected[@]}"; do
|
|
3005
|
+
case "$f" in
|
|
3006
|
+
test/*)
|
|
3007
|
+
fail "[check-tests-beside-behavior] selected $f — this gate covers the two behavior-adjacent locations, never test/**"
|
|
3008
|
+
return 1
|
|
3009
|
+
;;
|
|
3010
|
+
pi-extensions/*.test.ts | plugins/herdr/*.test.mjs) ;;
|
|
3011
|
+
*)
|
|
3012
|
+
fail "[check-tests-beside-behavior] selected $f, which matches neither declared glob"
|
|
3013
|
+
return 1
|
|
3014
|
+
;;
|
|
3015
|
+
esac
|
|
3016
|
+
done
|
|
3017
|
+
|
|
3018
|
+
if [ "${#selected[@]}" -eq 0 ]; then
|
|
3019
|
+
echo " the door is open and unused — 0 tests beside behavior at this HEAD"
|
|
3020
|
+
return 0
|
|
3021
|
+
fi
|
|
3022
|
+
echo " selected ${#selected[@]}: ${selected[*]}"
|
|
3023
|
+
run_vitest "${selected[@]}"
|
|
3024
|
+
}
|
|
3025
|
+
|
|
2823
3026
|
check_acp_overlay() {
|
|
2824
3027
|
# Deterministic gate for the S2b Claude config overlay materializer. Drives
|
|
2825
3028
|
# ensureClaudeConfigOverlay against injected temp realDir/overlayDir (no
|
|
@@ -3135,6 +3338,10 @@ check_pack() {
|
|
|
3135
3338
|
# #65 — the owner-normalized store projection consumers call INSTEAD of
|
|
3136
3339
|
# parsing the store; installed hosts are exactly where those consumers live.
|
|
3137
3340
|
"mcp/entwurf-bridge/dist/scripts/meta-facts.js"
|
|
3341
|
+
# #116 M2-a — the owner-normalized PEER projection, for the same reason one step
|
|
3342
|
+
# out: the placement join it emits is the copy a herdr-side consumer would
|
|
3343
|
+
# otherwise fork, and such a consumer runs against an installed host.
|
|
3344
|
+
"mcp/entwurf-bridge/dist/scripts/peer-facts.js"
|
|
3138
3345
|
# #87 Bundle B — omp-receive-doctor drives this operator projection through
|
|
3139
3346
|
# run.sh, so installed node_modules must have the compiled twin too.
|
|
3140
3347
|
"mcp/entwurf-bridge/dist/scripts/omp-receive-facts.js"
|
|
@@ -3219,6 +3426,13 @@ check_pack() {
|
|
|
3219
3426
|
# tripwire that fails loud if that negation is ever dropped (결합 규칙).
|
|
3220
3427
|
'__pycache__'
|
|
3221
3428
|
'\.pyc$'
|
|
3429
|
+
# Tests written beside behavior (#119 V2) are a dev surface, and `pi-extensions/`
|
|
3430
|
+
# ships WHOLE through the files allowlist, so the `!pi-extensions/**/*.test.ts`
|
|
3431
|
+
# negation is the only thing keeping them out — same 결합 규칙 as __pycache__ above,
|
|
3432
|
+
# this pattern is its tripwire. Written loose rather than anchored under
|
|
3433
|
+
# pi-extensions/ so it also covers any future allowlist entry; `plugins/` needs no
|
|
3434
|
+
# companion negation because `^plugins/` above already refuses that whole tree.
|
|
3435
|
+
'\.test\.[cm]?[jt]s$'
|
|
3222
3436
|
)
|
|
3223
3437
|
|
|
3224
3438
|
local pass=1 f pat hit
|
|
@@ -3430,6 +3644,8 @@ _check_pack_install_impl() {
|
|
|
3430
3644
|
"mcp/entwurf-bridge/dist/scripts/meta-bridge-fresh-cut.js"
|
|
3431
3645
|
# #65 — the owner-normalized store projection (see check-pack).
|
|
3432
3646
|
"mcp/entwurf-bridge/dist/scripts/meta-facts.js"
|
|
3647
|
+
# #116 M2-a — the owner-normalized peer projection (see check-pack).
|
|
3648
|
+
"mcp/entwurf-bridge/dist/scripts/peer-facts.js"
|
|
3433
3649
|
# #87 Bundle B — omp-receive-doctor reaches this through the installed dispatcher.
|
|
3434
3650
|
"mcp/entwurf-bridge/dist/scripts/omp-receive-facts.js"
|
|
3435
3651
|
# 0.12.5 — node_modules-safe plugin hook + lib (see check-pack). The installed
|
|
@@ -3617,6 +3833,60 @@ _check_pack_install_impl() {
|
|
|
3617
3833
|
}
|
|
3618
3834
|
echo "[check-pack-install] installed: $probe"
|
|
3619
3835
|
|
|
3836
|
+
# #116 M3-b1/b2 — THE ACTUAL-PACKAGE RUNTIME PROOF, THROUGH THE SHIPPED OWNER.
|
|
3837
|
+
# The Herdr plugin's runtime verifier decides whether a tree npm placed is a runtime the scoped
|
|
3838
|
+
# wiring may name: exact name@version, the compiled entry, all three required bins present AND
|
|
3839
|
+
# executable, and a real `entwurf check-bridge`. Its own focused gate drives that verifier against
|
|
3840
|
+
# a FIXTURE package, which proves the transaction but not this package — so the real artifact is
|
|
3841
|
+
# verified HERE, against the tarball this gate already packed and installed. No second pack, no
|
|
3842
|
+
# network.
|
|
3843
|
+
#
|
|
3844
|
+
# It imports from the INSTALLED tree's own scripts/herdr-runtime.mjs, not from the checkout. That
|
|
3845
|
+
# is the point: Herdr's `plugin uninstall` deletes the plugin checkout and calls no cleanup hook,
|
|
3846
|
+
# so the code that retires a runtime has to be IN the package. Reaching into $REPO_DIR here would
|
|
3847
|
+
# prove the checkout works and say nothing about what a clean host receives.
|
|
3848
|
+
local runtime_proof
|
|
3849
|
+
runtime_proof=$(cd "$tmp" && node --input-type=module -e "
|
|
3850
|
+
const pkg = process.cwd() + '/node_modules/@junghanacs/entwurf';
|
|
3851
|
+
const { verifyInstalledRuntime, readCheckoutPackageSpec, REQUIRED_BINS } =
|
|
3852
|
+
await import(pkg + '/scripts/herdr-runtime.mjs');
|
|
3853
|
+
const spec = readCheckoutPackageSpec(pkg);
|
|
3854
|
+
const seen = verifyInstalledRuntime(process.cwd(), spec);
|
|
3855
|
+
console.log(seen.name + '@' + seen.version + ' verified through the SHIPPED owner (' + REQUIRED_BINS.join(', ') + ')');
|
|
3856
|
+
" 2>&1) || {
|
|
3857
|
+
fail "[check-pack-install] #116 runtime verifier REFUSED the actual installed package (or does not ship):"
|
|
3858
|
+
echo "$runtime_proof" | sed 's/^/ /' >&2
|
|
3859
|
+
return 1
|
|
3860
|
+
}
|
|
3861
|
+
echo "[check-pack-install] herdr-plugin runtime verifier: $runtime_proof"
|
|
3862
|
+
|
|
3863
|
+
# #116 M3-b2 — THE DEACTIVATE VERB MUST ACTUALLY TEAR DOWN, WITH NO CHECKOUT. Herdr's `plugin
|
|
3864
|
+
# uninstall` deletes the plugin checkout and calls no cleanup hook, so the teardown entry has to
|
|
3865
|
+
# be IN the package AND has to survive deleting the runtime it is executing from. So this does not
|
|
3866
|
+
# settle for the "nothing to undo" verdict: it stages the real shape — the installed package
|
|
3867
|
+
# sitting AT the stable active root, a certified runtime journal, and a certified activation
|
|
3868
|
+
# ledger whose components are already removed — then runs THAT copy's own entry with a throwaway
|
|
3869
|
+
# HOME/XDG and no reference to this repo. What it proves is the self-delete: the process removes
|
|
3870
|
+
# the tree it is executing from and still finishes retiring the ledger.
|
|
3871
|
+
local deact_home deact_active deact_pkg deact_proof deact_rc runtime_left ledger_left
|
|
3872
|
+
deact_home=$(mktemp -d -t entwurf-deact.XXXXXX)
|
|
3873
|
+
deact_active="$deact_home/.data/entwurf/herdr-plugin/runtime/active"
|
|
3874
|
+
mkdir -p "$deact_active" "$deact_home/.state/entwurf/herdr-plugin"
|
|
3875
|
+
cp -R "$tmp/node_modules" "$deact_active/node_modules"
|
|
3876
|
+
deact_pkg="$deact_active/node_modules/@junghanacs/entwurf"
|
|
3877
|
+
node -e 'const fs=require("fs"),path=require("path");const [home,active]=process.argv.slice(1);const pkg=path.join(active,"node_modules","@junghanacs","entwurf");const version=JSON.parse(fs.readFileSync(path.join(pkg,"package.json"),"utf8")).version;fs.writeFileSync(path.join(home,".data","entwurf","herdr-plugin","journal.json"),JSON.stringify({schemaVersion:2,phase:"runtime-ready",runtimeRoot:active,artifactIdentity:{kind:"herdr-checkout",repository:"junghan0611/entwurf",commit:"c".repeat(40),packageName:"@junghanacs/entwurf",packageVersion:version,observedDigest:"sha256-"+"a".repeat(64)},previousRuntime:null}));fs.writeFileSync(path.join(home,".state","entwurf","herdr-plugin","activation.json"),JSON.stringify({schemaVersion:2,phase:"deactivating",runtimeRoot:active,artifactIdentity:{kind:"herdr-checkout",repository:"junghan0611/entwurf",commit:"c".repeat(40),packageName:"@junghanacs/entwurf",packageVersion:version,observedDigest:"sha256-"+"a".repeat(64)},piAgentDir:{path:path.join(home,".pi","agent"),source:"default"},claudeConfigDir:{path:path.join(home,".claude"),source:"default"},claudeUserConfig:{path:path.join(home,".claude.json"),source:"HOME"},activatedBackends:["pi"],components:[{backend:"pi",state:"removed"}]}));' "$deact_home" "$deact_active"
|
|
3878
|
+
deact_proof=$(env -i PATH="$PATH" HOME="$deact_home" XDG_DATA_HOME="$deact_home/.data" XDG_STATE_HOME="$deact_home/.state" XDG_CACHE_HOME="$deact_home/.cache" node "$deact_pkg/scripts/herdr-plugin-deactivate.mjs" 2>&1)
|
|
3879
|
+
deact_rc=$?
|
|
3880
|
+
runtime_left=$([ -e "$deact_home/.data/entwurf/herdr-plugin/runtime" ] && echo yes || echo no)
|
|
3881
|
+
ledger_left=$([ -e "$deact_home/.state/entwurf/herdr-plugin/activation.json" ] && echo yes || echo no)
|
|
3882
|
+
rm -rf "$deact_home"
|
|
3883
|
+
if [ "$deact_rc" -ne 0 ] || [ "$runtime_left" != "no" ] || [ "$ledger_left" != "no" ]; then
|
|
3884
|
+
fail "[check-pack-install] #116 M3-b2 installed herdr-plugin-deactivate did not complete the self-delete (rc=$deact_rc runtime-left=$runtime_left ledger-left=$ledger_left):"
|
|
3885
|
+
echo "$deact_proof" | sed 's/^/ /' >&2
|
|
3886
|
+
return 1
|
|
3887
|
+
fi
|
|
3888
|
+
echo "[check-pack-install] installed herdr-plugin-deactivate removed its OWN runtime and retired the ledger with no checkout: $deact_proof"
|
|
3889
|
+
|
|
3620
3890
|
# Pi package loader smoke — actual `pi` reads the manifest and
|
|
3621
3891
|
# registers the provider. rc=0 + the curated model list in the
|
|
3622
3892
|
# output means pi accepted the package as a real extension, not
|
|
@@ -6090,6 +6360,9 @@ case "$cmd" in
|
|
|
6090
6360
|
check-copilot-launch)
|
|
6091
6361
|
check_copilot_launch
|
|
6092
6362
|
;;
|
|
6363
|
+
check-pi-launch)
|
|
6364
|
+
check_pi_launch
|
|
6365
|
+
;;
|
|
6093
6366
|
check-copilot-receive-arm)
|
|
6094
6367
|
check_copilot_receive_arm
|
|
6095
6368
|
;;
|
|
@@ -6105,33 +6378,12 @@ case "$cmd" in
|
|
|
6105
6378
|
check-entwurf-v2-contract)
|
|
6106
6379
|
check_entwurf_v2_contract
|
|
6107
6380
|
;;
|
|
6108
|
-
check-entwurf-v2-lock)
|
|
6109
|
-
check_entwurf_v2_lock
|
|
6110
|
-
;;
|
|
6111
|
-
check-entwurf-v2-decider)
|
|
6112
|
-
check_entwurf_v2_decider
|
|
6113
|
-
;;
|
|
6114
|
-
check-entwurf-v2-matrix)
|
|
6115
|
-
check_entwurf_v2_matrix
|
|
6116
|
-
;;
|
|
6117
|
-
check-entwurf-v2-release)
|
|
6118
|
-
check_entwurf_v2_release
|
|
6119
|
-
;;
|
|
6120
6381
|
check-entwurf-v2-send)
|
|
6121
6382
|
check_entwurf_v2_send
|
|
6122
6383
|
;;
|
|
6123
|
-
check-entwurf-v2-send-fallback)
|
|
6124
|
-
check_entwurf_v2_send_fallback
|
|
6125
|
-
;;
|
|
6126
|
-
check-entwurf-v2-mailbox)
|
|
6127
|
-
check_entwurf_v2_mailbox
|
|
6128
|
-
;;
|
|
6129
6384
|
check-entwurf-v2-native-push)
|
|
6130
6385
|
check_entwurf_v2_native_push
|
|
6131
6386
|
;;
|
|
6132
|
-
check-entwurf-v2-runner)
|
|
6133
|
-
check_entwurf_v2_runner
|
|
6134
|
-
;;
|
|
6135
6387
|
check-entwurf-control-rpc)
|
|
6136
6388
|
check_entwurf_control_rpc
|
|
6137
6389
|
;;
|
|
@@ -6177,6 +6429,9 @@ case "$cmd" in
|
|
|
6177
6429
|
check-mux-launcher-fence)
|
|
6178
6430
|
check_mux_launcher_fence
|
|
6179
6431
|
;;
|
|
6432
|
+
check-typing-call-fence)
|
|
6433
|
+
check_typing_call_fence
|
|
6434
|
+
;;
|
|
6180
6435
|
check-entwurf-v2-visible-resume)
|
|
6181
6436
|
check_entwurf_v2_visible_resume
|
|
6182
6437
|
;;
|
|
@@ -6249,24 +6504,71 @@ case "$cmd" in
|
|
|
6249
6504
|
smoke-acp-socket-citizen-live)
|
|
6250
6505
|
smoke_acp_socket_citizen_live
|
|
6251
6506
|
;;
|
|
6252
|
-
check-entwurf-facts)
|
|
6253
|
-
check_entwurf_facts
|
|
6254
|
-
;;
|
|
6255
6507
|
check-control-socket-path)
|
|
6256
6508
|
check_control_socket_path
|
|
6257
6509
|
;;
|
|
6258
|
-
check-socket-discovery)
|
|
6259
|
-
check_socket_discovery
|
|
6260
|
-
;;
|
|
6261
6510
|
check-meta-listing)
|
|
6262
6511
|
check_meta_listing
|
|
6263
6512
|
;;
|
|
6264
6513
|
check-meta-facts)
|
|
6265
6514
|
check_meta_facts
|
|
6266
6515
|
;;
|
|
6516
|
+
check-herdr-activation)
|
|
6517
|
+
check_herdr_activation
|
|
6518
|
+
;;
|
|
6519
|
+
check-herdr-runtime-bootstrap)
|
|
6520
|
+
check_herdr_runtime_bootstrap
|
|
6521
|
+
;;
|
|
6522
|
+
check-herdr-plugin-profile)
|
|
6523
|
+
check_herdr_plugin_profile
|
|
6524
|
+
;;
|
|
6525
|
+
check-herdr-plugin-build)
|
|
6526
|
+
check_herdr_plugin_build
|
|
6527
|
+
;;
|
|
6528
|
+
smoke-herdr-plugin-build-live)
|
|
6529
|
+
smoke_herdr_plugin_build_live
|
|
6530
|
+
;;
|
|
6531
|
+
smoke-herdr-raw-install-live)
|
|
6532
|
+
# #118 홉 1: the FIRST USER PATH. Every other herdr gate proves the plugin against a
|
|
6533
|
+
# source we control — a stub, or the product remote redirected by git insteadOf to a
|
|
6534
|
+
# local bare clone asserting identity.kind === "herdr-checkout". None has watched the
|
|
6535
|
+
# plugin acquire its runtime from npm on a machine that has never seen this repo, which
|
|
6536
|
+
# is the one axis VERIFY.md:61 leaves open while the production lock says npm. Clean
|
|
6537
|
+
# container, public remote, no mounts, no insteadOf. Its first question is whether
|
|
6538
|
+
# `plugin install --yes` completes with no herdr server; a "no" is #118 investigation
|
|
6539
|
+
# output, not a completion. on-demand LIVE, never an aggregate MUST.
|
|
6540
|
+
# shift is load-bearing: this dispatcher keeps the verb in "$@" and each branch that
|
|
6541
|
+
# forwards arguments drops it for itself. Without it `--ref` arrives behind the gate name.
|
|
6542
|
+
shift
|
|
6543
|
+
(cd "$REPO_DIR" && bash scripts/smoke-herdr-raw-install-live.sh "$@")
|
|
6544
|
+
;;
|
|
6545
|
+
check-peer-facts)
|
|
6546
|
+
check_peer_facts
|
|
6547
|
+
;;
|
|
6548
|
+
check-herdr-plugin)
|
|
6549
|
+
check_herdr_plugin
|
|
6550
|
+
;;
|
|
6267
6551
|
check-entwurf-fact-provider)
|
|
6268
6552
|
check_entwurf_fact_provider
|
|
6269
6553
|
;;
|
|
6554
|
+
check-herdr-placement)
|
|
6555
|
+
check_herdr_placement
|
|
6556
|
+
;;
|
|
6557
|
+
check-herdr-fresh-call)
|
|
6558
|
+
check_herdr_fresh_call
|
|
6559
|
+
;;
|
|
6560
|
+
check-herdr-sandbox)
|
|
6561
|
+
check_herdr_sandbox
|
|
6562
|
+
;;
|
|
6563
|
+
check-herdr-supply)
|
|
6564
|
+
check_herdr_supply
|
|
6565
|
+
;;
|
|
6566
|
+
check-fresh-call-dispatch)
|
|
6567
|
+
check_fresh_call_dispatch
|
|
6568
|
+
;;
|
|
6569
|
+
smoke-herdr-fresh-call-live)
|
|
6570
|
+
smoke_herdr_fresh_call_live
|
|
6571
|
+
;;
|
|
6270
6572
|
check-entwurf-peers-surface)
|
|
6271
6573
|
check_entwurf_peers_surface
|
|
6272
6574
|
;;
|
|
@@ -6755,6 +7057,54 @@ case "$cmd" in
|
|
|
6755
7057
|
shift || true
|
|
6756
7058
|
exec bash "$REPO_DIR/scripts/copilot-launch.sh" "$@"
|
|
6757
7059
|
;;
|
|
7060
|
+
pi)
|
|
7061
|
+
# #118 홉 2: the managed pi launch. Same shape and same reasons as `copilot` above —
|
|
7062
|
+
# `exec`, no subshell and no cd, because this operates on the operator's session; and
|
|
7063
|
+
# `shift`, because this branch forwards straight into the VENDOR argv where a stray
|
|
7064
|
+
# "pi" would arrive as a prompt argument.
|
|
7065
|
+
#
|
|
7066
|
+
# NO separate launcher script, and no precondition check, because the precondition is
|
|
7067
|
+
# ZERO and that was measured rather than assumed: on a host where entwurf's pi extension
|
|
7068
|
+
# is not registered, `pi --entwurf-control` already fails as
|
|
7069
|
+
# `Error: Unknown option: --entwurf-control` with exit 1. A pre-check here would only
|
|
7070
|
+
# duplicate a refusal pi owns and would go stale the day pi renames it.
|
|
7071
|
+
#
|
|
7072
|
+
# ONE recursion fence, not copilot's two, and the difference is measured. The sentinel
|
|
7073
|
+
# closes the only real loop: a PATH executable named `pi` that shells back to
|
|
7074
|
+
# `entwurf pi` re-enters this branch, and finds the flag already exported. Copilot's
|
|
7075
|
+
# second fence — resolving the binary and refusing our own entrypoints — closes a case
|
|
7076
|
+
# that is not a loop here: a PATH `pi` symlinked to run.sh would be exec'd as
|
|
7077
|
+
# `run.sh --entwurf-control …`, which is an unknown verb this dispatcher already
|
|
7078
|
+
# refuses. `type -P` is what keeps the operator's own shell wrappers out of it
|
|
7079
|
+
# entirely: it returns a PATH file only, never a function or alias, so a
|
|
7080
|
+
# `pi() { entwurf pi "$@"; }` in a profile can never be re-entered through it.
|
|
7081
|
+
#
|
|
7082
|
+
# The flag is added, never deduplicated: `pit`/`pius`-style wrappers already pass
|
|
7083
|
+
# `--entwurf-control`, and passing it twice was measured to be byte-identical to
|
|
7084
|
+
# passing it once. An argv scan would be code earning nothing.
|
|
7085
|
+
#
|
|
7086
|
+
# Nothing else is injected. `--emacs-agent-socket` and friends are operator taste, and
|
|
7087
|
+
# a launcher that decides them stops being a spelling (Hard Rule 9).
|
|
7088
|
+
shift || true
|
|
7089
|
+
if [ -n "${ENTWURF_PI_LAUNCH_ACTIVE:-}" ]; then
|
|
7090
|
+
echo "entwurf: recursive managed launch detected (ENTWURF_PI_LAUNCH_ACTIVE is already set)." >&2
|
|
7091
|
+
echo " Something on PATH named 'pi' resolves back to this launcher. Fix PATH so 'pi'" >&2
|
|
7092
|
+
echo " is the pi coding agent, or run the vendor binary by its full path." >&2
|
|
7093
|
+
exit 1
|
|
7094
|
+
fi
|
|
7095
|
+
entwurf_pi_bin="$(type -P pi 2>/dev/null || true)"
|
|
7096
|
+
if [ -z "$entwurf_pi_bin" ]; then
|
|
7097
|
+
echo "entwurf: no 'pi' executable found on PATH." >&2
|
|
7098
|
+
echo " 'entwurf pi' launches the pi coding agent; it does not install one." >&2
|
|
7099
|
+
exit 1
|
|
7100
|
+
fi
|
|
7101
|
+
if [ ! -f "$entwurf_pi_bin" ] || [ ! -x "$entwurf_pi_bin" ]; then
|
|
7102
|
+
echo "entwurf: '$entwurf_pi_bin' is not an executable regular file — refusing to exec it." >&2
|
|
7103
|
+
exit 1
|
|
7104
|
+
fi
|
|
7105
|
+
export ENTWURF_PI_LAUNCH_ACTIVE=1
|
|
7106
|
+
exec "$entwurf_pi_bin" --entwurf-control "$@"
|
|
7107
|
+
;;
|
|
6758
7108
|
install-copilot-receive)
|
|
6759
7109
|
# #82 RAIL 5: install the RECEIVER extension into the Copilot USER extensions dir.
|
|
6760
7110
|
# Artifact ownership only — it sets no launch flag (`run.sh copilot` owns one
|
|
@@ -6926,6 +7276,22 @@ case "$cmd" in
|
|
|
6926
7276
|
shift || true
|
|
6927
7277
|
run_ts scripts/meta-facts.ts "$@"
|
|
6928
7278
|
;;
|
|
7279
|
+
peer-facts)
|
|
7280
|
+
# #116 M2-a READ-ONLY projection of the OBSERVED peer listing: one provider
|
|
7281
|
+
# (`listEntwurfFacts`) through one renderer (`renderEntwurfPeers().payload`), so
|
|
7282
|
+
# the payload SHAPE is the entwurf_peers payload shape and the join is never
|
|
7283
|
+
# re-implemented — the pi half of that join is a measured vendor floor, and a
|
|
7284
|
+
# forked copy decays silently. It is NOT the same bytes as an entwurf_peers call:
|
|
7285
|
+
# that surface rations observation and returns text, this one is unbounded because
|
|
7286
|
+
# a rationed row says `unobserved` and a machine consumer cannot tell that from
|
|
7287
|
+
# "nobody could look". `placement` stays the structured tagged union. No socket
|
|
7288
|
+
# coordinate is published (#50 C4 retired the legacy listing with the controlDir it
|
|
7289
|
+
# exposed; ENTWURF_DIR only selects what is probed) and herdr's own
|
|
7290
|
+
# agent_status/screen verdicts are absent (docs/herdr-launch-rail.md §9). No
|
|
7291
|
+
# writes, no watcher, no dispatch. Same exit contract as meta-facts.
|
|
7292
|
+
shift || true
|
|
7293
|
+
run_ts scripts/peer-facts.ts "$@"
|
|
7294
|
+
;;
|
|
6929
7295
|
meta-bridge-managed-keys)
|
|
6930
7296
|
# 0.10.0 meta-bridge: emit the SSOT of settings.json/~/.claude.json keys that
|
|
6931
7297
|
# entwurf's install OWNS. Consumers (agent-config fragment, future
|
|
@@ -6976,6 +7342,9 @@ case "$cmd" in
|
|
|
6976
7342
|
check-acp-sdk-surface)
|
|
6977
7343
|
check_acp_sdk_surface
|
|
6978
7344
|
;;
|
|
7345
|
+
check-tests-beside-behavior)
|
|
7346
|
+
check_tests_beside_behavior
|
|
7347
|
+
;;
|
|
6979
7348
|
check-acp-overlay)
|
|
6980
7349
|
check_acp_overlay
|
|
6981
7350
|
;;
|
|
@@ -7084,6 +7453,21 @@ case "$cmd" in
|
|
|
7084
7453
|
remove)
|
|
7085
7454
|
remove_local_package "$TARGET_PROJECT_DIR"
|
|
7086
7455
|
;;
|
|
7456
|
+
herdr-plugin-activate)
|
|
7457
|
+
# #116 M3-b2 public forward verb. Takes a subset of {pi, claude-code} and NOTHING else — the
|
|
7458
|
+
# stable runtime root is derived, never supplied. Plain `node` in both modes: shipped .mjs.
|
|
7459
|
+
shift
|
|
7460
|
+
(cd "$REPO_DIR" && node "$REPO_DIR/scripts/herdr-plugin-activate.mjs" "$@")
|
|
7461
|
+
;;
|
|
7462
|
+
herdr-plugin-deactivate)
|
|
7463
|
+
# #116 M3-b2 public verb. Plain `node` in BOTH modes on purpose: this is shipped .mjs, not TS,
|
|
7464
|
+
# because it has to run from an installed package after Herdr deleted the plugin checkout.
|
|
7465
|
+
(cd "$REPO_DIR" && node "$REPO_DIR/scripts/herdr-plugin-deactivate.mjs" "$@")
|
|
7466
|
+
;;
|
|
7467
|
+
install-user-scope)
|
|
7468
|
+
shift
|
|
7469
|
+
install_user_scope "$@"
|
|
7470
|
+
;;
|
|
7087
7471
|
remove-user-scope)
|
|
7088
7472
|
remove_user_scope_citizen
|
|
7089
7473
|
;;
|