@junghanacs/entwurf 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -0
- package/CHANGELOG.md +299 -0
- package/DELIVERY.md +4 -3
- package/README.md +99 -67
- package/VERIFY.md +13 -7
- package/docs/acp-backend-rail.md +1 -1
- package/docs/external-mcp-host.md +147 -9
- package/docs/setup-clean-host.md +221 -16
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +65 -20
- package/mcp/entwurf-bridge/dist/pi-extensions/entwurf-capabilities.json +1 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-self-address.js +17 -6
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-sender-identity.js +1 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js +172 -5
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +152 -17
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/omp-fresh-preflight.js +271 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-omp.js +988 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-receive-omp.js +574 -0
- package/mcp/entwurf-bridge/dist/scripts/omp-receive-facts.js +84 -0
- package/mcp/entwurf-bridge/src/index.ts +65 -19
- package/mcp/entwurf-bridge/tsconfig.build.json +19 -3
- package/mcp/tsconfig.json +10 -0
- package/package.json +14 -4
- package/pi/entwurf-capabilities.json +1 -0
- package/pi/meta-bridge-omp/entwurf-meta-omp/package.json +7 -0
- package/pi/omp-receive/entwurf-receive-omp/package.json +7 -0
- package/pi-extensions/entwurf-control.ts +12 -9
- package/pi-extensions/lib/entwurf-self-address.ts +18 -7
- package/pi-extensions/lib/meta-sender-identity.ts +1 -1
- package/pi-extensions/lib/meta-session.ts +219 -5
- package/pi-extensions/lib/mux-fresh-call.ts +171 -17
- package/pi-extensions/lib/omp-fresh-preflight.ts +275 -0
- package/pi-extensions/meta-bridge-omp.ts +1244 -0
- package/pi-extensions/meta-bridge-receive-omp.ts +666 -0
- package/run.sh +1041 -174
- package/scripts/agy-bridge.sh +4 -2
- package/scripts/check-copilot-birth-hook.ts +700 -23
- package/scripts/check-entwurf-self-address.ts +31 -0
- package/scripts/check-fresh-cut-gate.sh +12 -4
- package/scripts/check-gate-qualification.ts +8 -2
- package/scripts/check-harness-admission-parity.ts +143 -0
- package/scripts/check-meta-doctor-oracle.sh +20 -0
- package/scripts/check-omp-birth-hook.ts +1049 -0
- package/scripts/check-omp-fresh-preflight.ts +181 -0
- package/scripts/check-omp-receive-arm.ts +516 -0
- package/scripts/check-setup-qualification.sh +126 -0
- package/scripts/copilot-bridge-doctor.sh +104 -71
- package/scripts/copilot-bridge-install.sh +180 -20
- package/scripts/copilot-bridge-oracle.sh +223 -0
- package/scripts/copilot-bridge-uninstall.sh +137 -0
- package/scripts/dev-bin.sh +50 -9
- package/scripts/fake-copilot-vendor.sh +67 -0
- package/scripts/inventory-verification-surface.ts +1 -1
- package/scripts/meta-bridge-hook-log.sh +9 -1
- package/scripts/mutants/copilot-birth.json +96 -5
- package/scripts/mutants/mux-fresh-call.json +22 -22
- package/scripts/mutants/omp-birth.json +173 -0
- package/scripts/mutants/omp-fresh.json +300 -0
- package/scripts/mutants/omp-receive.json +135 -0
- package/scripts/mutants/pi-package-ownership.json +92 -0
- package/scripts/mutants/self-address.json +34 -0
- package/scripts/mutants/setup-verdict.json +148 -0
- package/scripts/mutants/source-install.json +32 -0
- package/scripts/omp-bridge-doctor.sh +315 -0
- package/scripts/omp-bridge-install.sh +221 -0
- package/scripts/omp-bridge-oracle.sh +154 -0
- package/scripts/omp-bridge-uninstall.sh +57 -0
- package/scripts/omp-mcp-bridge.sh +320 -0
- package/scripts/omp-mcp-config.py +392 -0
- package/scripts/omp-receive-doctor.sh +246 -0
- package/scripts/omp-receive-facts.ts +106 -0
- package/scripts/omp-receive-install.sh +228 -0
- package/scripts/omp-receive-uninstall.sh +60 -0
- package/scripts/omp-tool-surface.py +349 -0
- package/scripts/pi_settings_io.py +22 -0
- package/scripts/raw-omp-measure/README.md +420 -0
- package/scripts/raw-omp-measure/probe-extension.ts +76 -0
- package/scripts/raw-omp-measure/probe-receive-surface.ts +250 -0
- package/scripts/raw-omp-measure/source-audit.md +414 -0
- package/scripts/register-pi-package.py +525 -7
- package/scripts/register-pi-provider.py +162 -12
- package/scripts/smoke-agy-install-state.sh +86 -30
- package/scripts/smoke-agy-statusline-state.sh +9 -6
- package/scripts/smoke-omp-bridge-state.sh +221 -0
- package/scripts/smoke-omp-fresh-live.ts +497 -0
- package/scripts/smoke-omp-mcp-state.sh +327 -0
- package/scripts/smoke-omp-receive-live.ts +340 -0
- package/scripts/smoke-omp-receive-state.sh +196 -0
- package/scripts/smoke-setup-verdict.sh +291 -0
- package/scripts/smoke-user-scope-citizen.sh +388 -1
- package/scripts/tsconfig.json +2 -0
package/run.sh
CHANGED
|
@@ -119,7 +119,7 @@ run_vitest() {
|
|
|
119
119
|
usage() {
|
|
120
120
|
cat <<'EOF'
|
|
121
121
|
Usage:
|
|
122
|
-
./run.sh setup [project-dir] # ONE
|
|
122
|
+
./run.sh setup [project-dir] # ONE presence-driven composition (#86): mode-first (source bootstrap only on a checkout), then per-component PASS/SKIP/FAIL for pi (presence+floor)/claude/agy/copilot (all four units: birth→MCP→receiver→footer, independently) + stable dev bins + v2 install smoke; absent harness = zero-state SKIP, detected-incomplete = named FAIL + nonzero exit. Never installs a harness or touches credentials
|
|
123
123
|
./run.sh release-gate [project-dir] [--cut] [--allow-skip-gemini] # SINGLE release gate: full static (pnpm run check:full) + the v2-native live gates (v2 matrix-live, check-bridge, doctor-pi-provider, RGG) + the ACP plugin acceptance floor (12 LIVE smokes: socket-citizen/raw-turn/overlay/provider/session-reuse/carrier-augment/memory-containment/rgg/mcp/skill/bundled-mcp/v2-send) + the one surviving axis the aggregate used to omit silently (claude-native-resume; Cortex stays a documented on-demand direct call) + the cross-harness delivery chain (smoke-entwurf-chain-live). TWO-TIER summary: MUST (release-blocking, owns the exit code — "green" applies here) + BEHAVIOR (advisory, non-blocking: RGG positives model-in-loop turn). STEP OUTCOME protocol: every step is INVOKED and reports its own PASS / SKIP (exit 97, a prerequisite it does not have) / FAIL — a skip is never counted as a pass. Without --cut this is the unattended diagnostic (SKIPs reported, exit 0). WITH --cut it is read as release acceptance and ANY MUST SKIP is red, which is what makes "a CUT needs LIVE=1, SKIP=0" executable instead of prose. --allow-skip-gemini accepted-but-ignored (back-compat). final cut authorization is GLG's.
|
|
124
124
|
./run.sh check-bridge # entwurf-bridge direct MCP smoke + protocol/negative-path test.sh (live substrate = v2 live smokes)
|
|
125
125
|
./run.sh check-entwurf-bridge-boot # deterministic gate (5d-5-pre, G1a/G1b/G1e/G1f, IN pnpm run check:full): boot start.sh under strip-types + assert v2 fence graph loads + entwurf_v2 and entwurf_resume_call registered/schema + the tools/list surface is EXACTLY the seven shipped garden verbs; tools/list only, no auth/side-effect
|
|
@@ -131,10 +131,13 @@ Usage:
|
|
|
131
131
|
./run.sh check-meta-v3-record # deterministic gate: the ONE live record schema (v3) — canonical serialize/round-trip/mint, foreign-generation rejections name fresh-cut with the actual version value, strict keyset, no API
|
|
132
132
|
./run.sh check-mailbox-receipt-state # deterministic gate (0.11 Stage 0 step 3B): mailbox receipt state schema + store (stamp→persist→read-back) in a temp mailbox, strict keyset, no API
|
|
133
133
|
./run.sh check-copilot-birth-hook # #82 gate: drives the real Copilot assembler into a temp dir, fires the baked launcher with NO ARGV (the way Copilot's `exec`-string schema forces), and requires a backend:"copilot" v3 record + attach + peer row + a SENDER marker the resolver joins back to that record, and still zero mailbox/receiver marker (who-sent needs a shared parent; a receiver needs a doorbell this backend has not got). Hermetic; no Copilot, no model turn
|
|
134
|
+
./run.sh check-omp-birth-hook # #87 gate: drives the real OMP assembler into a temp dir, imports the ASSEMBLED index.ts into a MOCK omp host and fires session_start/session_switch. tui mints one backend:"omp" record + a sender marker keyed to the host's OWN pid (the one-process join) + the garden id on the status line; print/rpc/json mint NOTHING (hasUI is true on the rpc rows, as in the vendor); switch attaches on the same native id and mints the replacement on a new one; the CERTIFIED receiver reader still finds no marker. Also the four-root joint binding: extension and omp-labeled bridge child resolve the same sessions/mailbox/senders/receivers bundle against test-built literals (never the production resolver as its own oracle), under a poisoned PI_CODING_AGENT_DIR and under four distinct ENTWURF_META_* overrides; the override grammar is absolute-or-~ and both halves plus the doctor refuse anything else by name; and a drifted provenance label selects no root policy at all. Hermetic; no omp, no model turn
|
|
134
135
|
./run.sh check-copilot-receive-arm # #82 RAIL 5 gate: the REAL receiver installer + the REAL extension.mjs forked with a stubbed SDK. Arms only after birth, marker owned by the WATCHER pid, self-fetch dispatch answer, doorbell carries the garden id and NOT the body, id-drift/foreign-parent refusals. Hermetic; no Copilot, no model turn
|
|
135
136
|
./run.sh check-copilot-launch # #82 RAIL 7 gate: the MANAGED launch `entwurf copilot`, driven through its public address against a FAKE VENDOR on a sandbox PATH. Receiver precondition refusals, EXTENSIONS token + operator token preservation, injected defaults before the `--` terminator, byte-identical argv, the 11 explicit permission/surface policy overrides that suppress `--yolo`, exec (not fork) pid identity, exit passthrough, recursion refusal. Hermetic; no Copilot, no model turn
|
|
136
137
|
./run.sh check-copilot-statusline # #82 Copilot custom-footer renderer. session_id → ready/?/gid + rail `cop`; exit 0. IN pnpm check. No Copilot, no model turn
|
|
137
138
|
./run.sh check-entwurf-capabilities # deterministic gate (0.11 Stage 0 step 3C): backend capability registry (pi/entwurf-capabilities.json) — coverage==META_CITIZEN_BACKENDS + agrees with live META_BACKEND_DESCRIPTORS + strict keyset, no API
|
|
139
|
+
./run.sh check-omp-fresh-preflight # #87 C: the OMP fresh preflight reproduces omp_agent_dir and the tools.xdev read in TS (it runs from two emit depths and cannot call a sibling script). This drives the SHIPPED shell/python leaves over the same inputs — refusals included — and requires the TS half to agree, so the reproduction cannot silently drift from the installer's own oracle
|
|
140
|
+
./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)
|
|
138
141
|
./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
|
|
139
142
|
./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
|
|
140
143
|
./run.sh check-bridge-delivery # deterministic gate (IN pnpm run check:full): demo scene 3 recovered — seed strict meta-sender + armed receiver citizens in an isolated temp world, scrub ambient pi/sender carriers, drive the BUILT DIST ENTRY over MCP stdio through a real tools/call entwurf_v2, assert the .msg landed under the seeded sender + doorbell poked. DELIVERS through the artifact, not from source. ENTWURF_DELIVERY_SUBJECT=<launcher> replays the same scene against another consumer artifact (check-pack-install passes the npm-installed bin). No model/network/cost; stale or missing dist FAILS
|
|
@@ -181,8 +184,11 @@ Usage:
|
|
|
181
184
|
./run.sh smoke-meta-honesty # 1.0.0 meta-bridge: honesty regression gate (#30 blockers) — doorbell counts ALL msgs honestly + hook logs failures as ERROR (best-effort, no scream). Offline/deterministic (deps: bash+node+python3)
|
|
182
185
|
./run.sh smoke-meta-install-state # 1.0.0 meta-bridge Phase 2: stateful install/uninstall + store-doctor regression gate. Offline/deterministic (deps: bash+node+python3)
|
|
183
186
|
./run.sh check-meta-doctor-oracle # 0.12.8 (#51): detection power of the release ORACLE — healthy fixture must reach `doctor: PASS`, then 21 planted defects (retired shell form, partial hand-patch, launcher bypass/repoint/provenance loss, malformed exec args, owner type drift, extra leaf/group, doorbell asyncRewake/path/timeout, no live bridge, stale receiver, ambiguous/missing cache, missing hook log/writer, failing CLI probes) must each turn it FAIL naming their own cause, plus a positive case pinning that a long-writing CLI is NOT a false negative. Offline/deterministic (deps: bash+node+python3)
|
|
184
|
-
./run.sh smoke-agy-install-state # agy MCP + exact permission ownership regression: isolated HOME+XDG, adopt/state/inverse, symlink refuse, setup
|
|
187
|
+
./run.sh smoke-agy-install-state # agy MCP + exact permission ownership regression: isolated HOME+XDG, adopt/state/inverse, symlink refuse, truthful setup outcomes. Offline/deterministic
|
|
188
|
+
./run.sh smoke-setup-verdict # #86 C1+C3b aggregate setup verdict fixture: all-absent SKIP/green, pi presence+floor, detected-FAIL nonzero, copilot four-unit composition (present=independent PASS/FAIL rows, absent=zero-state SKIP), installed-mode named branch, credential store untouched. Offline/deterministic
|
|
185
189
|
./run.sh check-agy-permission-matrix # AGY permission CONTRACT SPACE as a literal table (55 cells): parser-state × operation × settings × ownership × precedence with stated exclusion rules; expectations are hand-written literals, never read from the SUT. Offline/deterministic (deps: python3)
|
|
190
|
+
./run.sh smoke-omp-fresh-live # #87 bundle C acceptance, RELEASE MUST: omp opened as ONE visible fresh sibling through the PUBLIC entwurf_fresh_call surface (never raw tmux), exact nonce callback -> garden id from the SENDER ENVELOPE -> record/backend identity -> addressed receive -> lastReadAt -> the drain visible in that same native session's own transcript. Needs LIVE=1 and spends real model turns. Self-deciding: omp outside FRESH_CALL_BACKENDS or without a mailbox rail is a protocol SKIP, which --cut reads as RED
|
|
191
|
+
./run.sh smoke-omp-receive-live # #87 bundle B acceptance: another harness wakes an ALREADY-OPEN omp citizen, which reads its inbox and answers in the same session. Decides its own outcome from the capability registry — omp without a drainable mailbox is a protocol SKIP (no receiver unit exists to arm), which an unattended release-gate reports and `--cut` reads as RED; a registry that claims a receive rail with no acceptance body here is a FAIL
|
|
186
192
|
./run.sh smoke-entwurf-chain-live # LIVE cross-harness delivery chain: native Claude Code -> pi GPT -> pi ACP Sonnet -> mailbox terminus, proving sender identity/replyable at every hop and a real read receipt at the end. Prerequisites (claude on PATH, pi credentials per backend) report protocol SKIP, never a pass
|
|
187
193
|
./run.sh check-release-gate-outcomes # release-gate STEP OUTCOME protocol (P1): one skip exit code shared by the shell + TS halves, classifier never rounds a skip up to a pass, `--cut` refuses a MUST SKIP while a bare diagnostic stays exit 0, no LIVE smoke keeps the old exit-0 skip shape, and both real skip surfaces are INVOKED and observed to propagate the code
|
|
188
194
|
./run.sh check-gate-qualification # kill-proof qualification (the gate-of-gates): runner self-test (classifier truth table + synthetic negatives incl. wrong-reason/hang/control-red/impurity) + committed mutant manifests (scripts/mutants/*.json) run in an isolated snapshot repo under control→mutant→restore→control; the real checkout is never written. Evidence = claim IDs + killed mutant IDs, never assertion counts
|
|
@@ -191,8 +197,9 @@ Usage:
|
|
|
191
197
|
./run.sh smoke-agy-statusline-state # agy ambient garden-id statusLine install/doctor/inverse regression. Offline/deterministic
|
|
192
198
|
./run.sh smoke-copilot-statusline-state # Copilot custom-footer install/doctor/inverse regression. Offline/deterministic
|
|
193
199
|
./run.sh smoke-copilot-mcp-state # Copilot MCP install/doctor/inverse regression. Offline/deterministic
|
|
200
|
+
./run.sh smoke-omp-bridge-state # OMP birth-extension install/doctor/inverse regression: placement, stale-writer detection, honest inverse, no-state refusal (structurally VALID as well as foreign), symlink refusal, ambiguous-agent-dir refusal (ledger M6), and a poisoned PI_CODING_AGENT_DIR that attracts no artifact. Fully sandboxed HOME/PI/XDG. Offline/deterministic
|
|
194
201
|
./run.sh smoke-agy-hooks-state # agy PreInvocation birth/sender hook install/doctor/inverse + direct stdin→meta-record regression. Offline/deterministic
|
|
195
|
-
./run.sh smoke-user-scope-citizen # 0.12.6 install-boundary: pi packages[] registration SSOT (register-pi-package.py) — idempotent + preserves unrelated + normalizes stale
|
|
202
|
+
./run.sh smoke-user-scope-citizen # 0.12.6 install-boundary: pi packages[] registration SSOT (register-pi-package.py) — idempotent + preserves unrelated + remove symmetry + fails loud, and the #86 C2 explicit ownership cells (project scope still normalizes ITS OWN stale entries; user scope refuses other owners and only takeover-user-scope moves the shared entry). Offline/hermetic (deps: bash+python3)
|
|
196
203
|
./run.sh smoke-meta-prune # 1.0.0 meta-bridge Phase 4: listing-only store janitor regression gate — classify keep/orphan/stale/ambiguous, delete nothing. Offline/deterministic (deps: bash+node)
|
|
197
204
|
./run.sh smoke-meta-keyset-guard # 0.10.0 meta-bridge: keyset-owner guard regression — check-keyset-overlap + managed-keys SSOT (disjoint passes, collisions fail). Offline/hermetic (deps: bash+python3)
|
|
198
205
|
./run.sh check-meta-manifest-schema # 0.12.2 meta-bridge: CLI-version-INDEPENDENT static guard — plugin manifests pinned to the minimal keyset that validates on the lowest supported Claude (closed-schema regression that broke 0.12.1 install on floor) + desired_mcp installed-vs-clone dual-mode. Offline (deps: python3)
|
|
@@ -203,13 +210,25 @@ Usage:
|
|
|
203
210
|
./run.sh doctor-meta-bridge # THE RELEASE ORACLE (#51, Linux-certified repair axis). exit 0 = every required layer was MEASURED on this Linux host: toolchain + state + plugin/MCP + resolved-artifact launch-form classification (all 3 owner hooks + doorbell static contract) + synthetic owner join + store scan + hook errors + SessionStart evidence + REQUIRED live MCP↔marker join + writer-version parity. Missing live evidence is NOT CERTIFIED (open a Claude session and re-run), never a pass; Darwin is not yet verified/certified and stays nonzero for this cut (future validation may reopen it). Detection power is held by check-meta-doctor-oracle
|
|
204
211
|
./run.sh copilot [args...] # #82 RAIL 7: the MANAGED Copilot launch. exec()s the vendor CLI in THIS terminal (cwd/pid/exit preserved) with COPILOT_CLI_ENABLED_FEATURE_FLAGS=EXTENSIONS set for that one invocation — entwurf owns no part of your shell and writes nothing to it, but it owns the process it is about to become. Without that flag Copilot skips the extension scan SILENTLY. Refuses to launch unless the receiver unit is actually installed. Injects `--model auto` when no --model was given and `--yolo` when no explicit permission/surface policy flag was given, both BEFORE any `--`; every argument you pass is forwarded byte-identical. RUNNING THIS IS YOUR CONSENT to that profile — use plain `copilot` for stock vendor behaviour. Not tmux, not fresh-call, mints no citizen (birth is still the first prompt)
|
|
205
212
|
./run.sh install-copilot-bridge # #82: GLOBAL install of the Copilot BIRTH plugin (own marketplace root; node+entry baked into the no-argv exec string). MCP wiring and the RECEIVER extension are separate install surfaces (install-copilot-mcp, install-copilot-receive). Also retires the stale Claude unit (--keep-stale-claude-unit opts out)
|
|
206
|
-
./run.sh
|
|
213
|
+
./run.sh uninstall-copilot-bridge # #86 C3a: honest inverse of install-copilot-bridge from the package-owned install-state (exact qualified id + exact marketplace name/path + recorded assembly only; state deleted LAST; a failing vendor list is UNKNOWN and refuses; never --force, never the stale Claude unit)
|
|
214
|
+
./run.sh doctor-copilot-bridge # #82/#86: fail-loud surface for that unit — runtime axis (red = a hook that RAN and failed, or a broken/unbaked artifact; "installed with zero records" is NOT red and is reported as NOT-YET: a Copilot session is born on its FIRST PROMPT, not when the window opens, measured) + ownership axis (install-state shape/binding, marketplace path drift, UNKNOWN vendor lists; legacy no-state install = named non-green, repair = install-copilot-bridge adoption). Either axis red = red
|
|
207
215
|
./run.sh install-copilot-statusline # own Copilot statusLine + footer.showCustom with an install-state preimage
|
|
208
216
|
./run.sh uninstall-copilot-statusline # honest inverse of install-copilot-statusline
|
|
209
217
|
./run.sh doctor-copilot-statusline # static ownership/config/bin doctor; never claims a render receipt
|
|
210
218
|
./run.sh install-copilot-mcp # #82 RAIL 5: register ONE entwurf-bridge server in ~/.copilot/mcp-config.json (adopt / create / REFUSE symlink), type:local, install-state under $XDG_DATA_HOME/entwurf/copilot-mcp/
|
|
211
219
|
./run.sh uninstall-copilot-mcp # honest inverse of install-copilot-mcp from install-state
|
|
212
220
|
./run.sh doctor-copilot-mcp # static ownership/config/boot doctor; RED only when install-state exists
|
|
221
|
+
./run.sh install-omp-bridge # #87: install the OMP BIRTH extension into <omp agent dir>/extensions/entwurf-meta-omp (index.ts|js + lib + capability registry). No launcher and no bake — an omp hook is an in-process extension. Refuses when an inherited PI_CODING_AGENT_DIR/PI_CONFIG_DIR/PI_PROFILE makes the target agent dir ambiguous (ledger M6), and refuses ANY pre-existing artifact at the unit path that entwurf holds no ownership state for — a shape is not a proof of ownership
|
|
222
|
+
./run.sh uninstall-omp-bridge # honest inverse from install-state (exact unit dir + recorded entry; no-state host REFUSES; state deleted LAST). Records already minted are preserved
|
|
223
|
+
./run.sh doctor-omp-bridge # #87: runtime axis (importable unit, writer/registry parity, mint vs sender-marker errors on SEPARATE axes, scope-fence receipts, a root-grammar preflight that goes RED on a relative ENTWURF_META_* override instead of reporting on some other directory, CERTIFIED omp record count via meta-facts under the omp root policy — never a text grep, live omp processes carrying inherited PI_SESSION_ID/PI_AGENT_ID) + ownership axis. PI_CODING_AGENT_DIR is the vendor's own agent dir here and is reported as ignored, never as contamination. Zero records = NOT-YET, never red
|
|
224
|
+
./run.sh install-omp-receive # #87 bundle B: install the OMP RECEIVER extension into <omp agent dir>/extensions/entwurf-receive-omp. Own unit, own install-state, own doctor, own inverse. It joins the citizen BIRTH minted in the same process (sender marker for this pid + V3 record + the vendor's current session id, all three or refuse), holds an fs.watch on that citizen's mailbox signal, and rings an ANNOUNCE-ONLY doorbell via pi.sendUserMessage. It never assumes it loads after birth: extension handlers run in directory-name order and a before-sorting unit was MEASURED to see no marker, so the arm retries on a bounded ctx.setInterval cancelled with ctx.clearTimer (the vendor exposes no clearInterval)
|
|
225
|
+
./run.sh uninstall-omp-receive # honest inverse from install-state; records and sender identity untouched. A session that ALREADY armed keeps its marker until its process exits
|
|
226
|
+
./run.sh doctor-omp-receive # runtime axis (importable unit, writer parity, arm failures and doorbell failures on SEPARATE axes, who is armed RIGHT NOW through the production marker reader) + ownership axis. Zero armed receivers = NOT-YET, never red. Reports a marker for exactly what a marker proves — a live owner reached the arm emit — and never upgrades that into "a wake will happen"
|
|
227
|
+
./run.sh omp-receive-facts # read-only JSON projection of the omp receive rail: every receiver marker through readMetaReceiverMarker in BOTH readings (live / as-written) plus mailbox counts. The shared oracle so no consumer answers "is a doorbell held?" from a filename
|
|
228
|
+
./run.sh install-omp-mcp # #87 step 5: write ONE omp-native entwurf-bridge server into <omp agent dir>/mcp.json with env ENTWURF_BRIDGE_EXTERNAL_AGENT_ID=external-mcp/omp. The server key is a PINNED LITERAL, byte-identical to the Claude import's — same-key first-wins at native=100 > claude=80 is what SHADOWS the import (never disabledServers, which kills both). Adopt / create / REFUSE symlink, preimage recorded
|
|
229
|
+
./run.sh uninstall-omp-mcp # honest inverse from install-state (restores the preimage, or removes a file we created); the Claude import becomes effective again
|
|
230
|
+
./run.sh doctor-omp-mcp # ownership + config + boot doctor, plus the EFFECTIVE-source read (native-wins / native-invalid / import-wins / both-suppressed) — a configuration read, never a runtime receipt. Runtime validity and ownership are SEPARATE axes: foreign provenance, a disabledServers denylist, and a malformed entry under our key are each RED even with no install-state. tools.xdev is a RUNTIME cell: absent file/key applies the vendor default (true, no inline allowlist) and is RED while the native hand is the effective source; xdev false is ok; a covering xdevInlineDevices glob is ok-with-note
|
|
231
|
+
./run.sh smoke-omp-mcp-state # OMP MCP install/doctor/inverse regression incl. shadowing, the denylist refusal, target confinement + retarget refusal, and a malformed entry going RED with zero install-state. Fully sandboxed HOME/PI/XDG. Offline/deterministic
|
|
213
232
|
./run.sh install-copilot-receive # #82 RAIL 5: install the RECEIVER extension (user scope ~/.copilot/extensions/entwurf-receive). Owns the artifact and CHECKS the launch flag; it never sets one (a launch does — see `./run.sh copilot`). Arms per session after birth
|
|
214
233
|
./run.sh uninstall-copilot-receive # honest inverse from install-state; never removes a unit it did not install
|
|
215
234
|
./run.sh doctor-copilot-receive # artifact + digest, COPILOT_CLI_ENABLED_FEATURE_FLAGS on the LIVE copilot processes (the silent failure), live receiver markers via the production reader, receiver log. RED only when install-state exists
|
|
@@ -233,12 +252,13 @@ Usage:
|
|
|
233
252
|
./run.sh check-pack # publish gate (dry-run): npm pack --dry-run + tarball invariants (runtime-critical present, dev residue absent)
|
|
234
253
|
./run.sh check-pack-pin-matcher # pure self-test of check-pack-install's pin-leak matcher against synthetic .pnpm lookalikes (version boundary: @0.84.30 must leak, @0.84.3 bare/peer-hash must pass); snapshot-safe qualification oracle, also run first inside check-pack-install
|
|
235
254
|
./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
|
|
236
|
-
./run.sh check-pack-install # heavy publish gate (prepublishOnly): actual npm pack + tar -tf + fresh-temp install smoke with the pinned pi peers (
|
|
255
|
+
./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)
|
|
237
256
|
./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)
|
|
238
|
-
./run.sh sync-auth # copy ~/.pi/agent/auth.json anthropic OAuth credentials to entwurf alias
|
|
239
257
|
./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
|
|
240
258
|
./run.sh remove [project-dir] # remove entwurf entries from project .pi/settings.json (project scope only; global user-scope citizen left intact)
|
|
241
|
-
./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)
|
|
259
|
+
./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)
|
|
260
|
+
./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
|
|
261
|
+
./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
|
|
242
262
|
|
|
243
263
|
Notes:
|
|
244
264
|
- project-dir defaults to current directory
|
|
@@ -268,44 +288,12 @@ print(os.path.abspath(os.path.expanduser(sys.argv[1])))
|
|
|
268
288
|
PY
|
|
269
289
|
}
|
|
270
290
|
|
|
271
|
-
sync_auth()
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
auth_path = Path(sys.argv[1]).expanduser()
|
|
278
|
-
provider_id = sys.argv[2]
|
|
279
|
-
auth_path.parent.mkdir(parents=True, exist_ok=True)
|
|
280
|
-
|
|
281
|
-
if auth_path.exists():
|
|
282
|
-
data = json.loads(auth_path.read_text())
|
|
283
|
-
if not isinstance(data, dict):
|
|
284
|
-
raise SystemExit("auth.json is not an object")
|
|
285
|
-
else:
|
|
286
|
-
data = {}
|
|
287
|
-
|
|
288
|
-
anthropic = data.get("anthropic")
|
|
289
|
-
if not isinstance(anthropic, dict):
|
|
290
|
-
print("sync-auth: skipped (no anthropic OAuth credentials in ~/.pi/agent/auth.json)")
|
|
291
|
-
raise SystemExit(0)
|
|
292
|
-
|
|
293
|
-
before = json.dumps(data.get(provider_id), sort_keys=True)
|
|
294
|
-
after = json.dumps(anthropic, sort_keys=True)
|
|
295
|
-
if before == after:
|
|
296
|
-
print(f"sync-auth: already synced ({provider_id})")
|
|
297
|
-
raise SystemExit(0)
|
|
298
|
-
|
|
299
|
-
data[provider_id] = anthropic
|
|
300
|
-
backup = auth_path.with_suffix(auth_path.suffix + ".bak")
|
|
301
|
-
if auth_path.exists():
|
|
302
|
-
backup.write_text(auth_path.read_text())
|
|
303
|
-
auth_path.write_text(json.dumps(data, indent=2) + "\n")
|
|
304
|
-
print(f"sync-auth: wrote {provider_id} alias to {auth_path}")
|
|
305
|
-
if backup.exists():
|
|
306
|
-
print(f"sync-auth: backup -> {backup}")
|
|
307
|
-
PY
|
|
308
|
-
}
|
|
291
|
+
# The old `sync_auth` credential mutation is GONE (#86 A5): setup composes what
|
|
292
|
+
# the operator already installed and NEVER reads, copies, aliases or backs up a
|
|
293
|
+
# harness credential store — the `entwurf` alias it wrote had no in-repo reader
|
|
294
|
+
# (the ACP provider passes pi's auth check with the no-auth sentinel), and the
|
|
295
|
+
# `auth.json.bak` it left is documented manual-operator-cleanup ONLY. Do not
|
|
296
|
+
# reintroduce credential copying as a convenience helper in any install path.
|
|
309
297
|
|
|
310
298
|
# Repo dependency integrity is a HARD requirement for install — NOT gated on
|
|
311
299
|
# backend (Claude/ACP) auth. A cloned-but-not-installed or a moved/renamed repo
|
|
@@ -514,6 +502,14 @@ PY
|
|
|
514
502
|
}
|
|
515
503
|
|
|
516
504
|
install_local_package() {
|
|
505
|
+
# Named interpreter prerequisite BEFORE any preflight/write (#86 E5): the
|
|
506
|
+
# packages[]/provider writers below are python3-backed and `entwurf install`
|
|
507
|
+
# dispatches here directly, so a python3-less host gets this verdict instead
|
|
508
|
+
# of an accidental mid-write failure.
|
|
509
|
+
command -v python3 >/dev/null 2>&1 || {
|
|
510
|
+
echo "[install] entwurf install requires python3 on PATH (settings writers are python3-backed); nothing was written." >&2
|
|
511
|
+
exit 1
|
|
512
|
+
}
|
|
517
513
|
local project_dir agent_dir
|
|
518
514
|
project_dir=$(normalize_project_dir "$1")
|
|
519
515
|
agent_dir="${PI_CODING_AGENT_DIR:-$HOME/.pi/agent}"
|
|
@@ -553,19 +549,79 @@ install_local_package() {
|
|
|
553
549
|
# launchers and the npm consumer's "installs → just works" both need the entry
|
|
554
550
|
# in ~/.pi/agent/settings.json's packages[]. This is the wiring that dropped when
|
|
555
551
|
# `pi install` was removed from setup (2026-07-03: `--entwurf-control` unknown in
|
|
556
|
-
# a foreign cwd). Idempotent: absent → append
|
|
557
|
-
#
|
|
558
|
-
#
|
|
552
|
+
# a foreign cwd). Idempotent for THIS root: absent → append + owner state, present
|
|
553
|
+
# same-root → no-op. #86 C2 retires the silent normalization of other roots'
|
|
554
|
+
# entries: a different owner (live or missing) is a zero-write refusal that names
|
|
555
|
+
# takeover-user-scope. Every other package and key is preserved untouched.
|
|
556
|
+
_pi_package_state() { echo "${XDG_DATA_HOME:-$HOME/.local/share}/entwurf/pi-package/install-state.json"; }
|
|
557
|
+
_pi_provider_state() { echo "${XDG_DATA_HOME:-$HOME/.local/share}/entwurf/pi-provider/install-state.json"; }
|
|
558
|
+
|
|
559
559
|
register_user_scope_citizen() {
|
|
560
560
|
local agent_dir="${PI_CODING_AGENT_DIR:-$HOME/.pi/agent}"
|
|
561
561
|
# Shared idempotent implementation (also driven by smoke-user-scope-citizen).
|
|
562
|
-
|
|
562
|
+
# #86 C2: user scope carries a recorded OWNER (packageRoot in the pi-package
|
|
563
|
+
# install-state). Another owner — live or missing — makes this a zero-write
|
|
564
|
+
# ownership refusal (exit 6) that propagates: install fails, setup records the
|
|
565
|
+
# pi component FAIL. The only writer over another owner is takeover-user-scope.
|
|
566
|
+
#
|
|
567
|
+
# ATOMIC (#86 C2 amendment): BOTH ownership halves are decided READ-ONLY before
|
|
568
|
+
# either writer runs — a provider-side refusal must leave the package side
|
|
569
|
+
# byte-identical (and vice versa), never a half-installed user scope.
|
|
570
|
+
python3 "$REPO_DIR/scripts/register-pi-package.py" "$agent_dir/settings.json" "$REPO_DIR" \
|
|
571
|
+
--scope user --state "$(_pi_package_state)" --preflight >/dev/null
|
|
572
|
+
python3 "$REPO_DIR/scripts/register-pi-provider.py" install "$agent_dir/settings.json" "$REPO_DIR" \
|
|
573
|
+
--scope user --state "$(_pi_provider_state)" --preflight >/dev/null
|
|
574
|
+
python3 "$REPO_DIR/scripts/register-pi-package.py" "$agent_dir/settings.json" "$REPO_DIR" \
|
|
575
|
+
--scope user --state "$(_pi_package_state)"
|
|
563
576
|
# #46 Task 2: own entwurfProvider.mcpServers.entwurf-bridge as the bare stable bin at USER scope
|
|
564
577
|
# (GLOBAL/durable →파급s to every cwd), so its inverse needs an install-state honest inverse
|
|
565
578
|
# under $XDG_DATA_HOME/entwurf/pi-provider/ (Task 0/1 discipline). project scope is checkout-
|
|
566
579
|
# local and covered by `run.sh remove` (no state) — deliberate, reasoned asymmetry.
|
|
567
|
-
|
|
568
|
-
|
|
580
|
+
# #86 C2: the provider state records installerRoot, so the same foreign-owner refusal
|
|
581
|
+
# holds here — and the package refusal above fires FIRST, before any provider write.
|
|
582
|
+
python3 "$REPO_DIR/scripts/register-pi-provider.py" install "$agent_dir/settings.json" "$REPO_DIR" --scope user --state "$(_pi_provider_state)"
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
# takeover-user-scope — the operator-explicit ownership move (#86 C2). The ONLY
|
|
586
|
+
# writer that replaces another root's user-scope registration: normal install/
|
|
587
|
+
# setup/remove refuse instead. Reports old→new for both the packages[] entry
|
|
588
|
+
# (packageRoot) and the provider key (installerRoot). No --force variant exists.
|
|
589
|
+
takeover_user_scope() {
|
|
590
|
+
local agent_dir="${PI_CODING_AGENT_DIR:-$HOME/.pi/agent}" pp_out
|
|
591
|
+
command -v python3 >/dev/null 2>&1 || { echo "[takeover] requires python3 on PATH; nothing was written." >&2; exit 1; }
|
|
592
|
+
section "takeover-user-scope: move the shared user-scope registration to this root"
|
|
593
|
+
# ATOMIC (#86 C2 amendment): both halves decided READ-ONLY first — an exact-entry
|
|
594
|
+
# mismatch on the package side or a refusal on the provider side leaves the other
|
|
595
|
+
# half byte-identical.
|
|
596
|
+
python3 "$REPO_DIR/scripts/register-pi-package.py" "$agent_dir/settings.json" "$REPO_DIR" \
|
|
597
|
+
--scope user --state "$(_pi_package_state)" --takeover --preflight >/dev/null
|
|
598
|
+
python3 "$REPO_DIR/scripts/register-pi-provider.py" install "$agent_dir/settings.json" "$REPO_DIR" \
|
|
599
|
+
--scope user --state "$(_pi_provider_state)" --takeover --preflight >/dev/null
|
|
600
|
+
python3 "$REPO_DIR/scripts/register-pi-package.py" "$agent_dir/settings.json" "$REPO_DIR" \
|
|
601
|
+
--scope user --state "$(_pi_package_state)" --takeover
|
|
602
|
+
pp_out=$(python3 "$REPO_DIR/scripts/register-pi-provider.py" install "$agent_dir/settings.json" "$REPO_DIR" \
|
|
603
|
+
--scope user --state "$(_pi_provider_state)" --takeover)
|
|
604
|
+
printf '%s\n' "$pp_out"
|
|
605
|
+
# Split verdict (#86 C2 amendment): a user-override provider key is preserved and
|
|
606
|
+
# stays UNOWNED — say exactly which half moved instead of a false "both owned".
|
|
607
|
+
if printf '%s' "$pp_out" | grep -q 'provider override preserved'; then
|
|
608
|
+
ok "takeover-user-scope: package owner moved to this root ($REPO_DIR); provider key preserved as the operator's override (unowned — doctor-pi-provider reports it as such)"
|
|
609
|
+
else
|
|
610
|
+
ok "takeover-user-scope: this root now owns the user-scope registration ($REPO_DIR)"
|
|
611
|
+
fi
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
# doctor-pi-package — package-side ownership verdict only (#86 C2): unregistered /
|
|
615
|
+
# owned / owned-by-other(live) / legacy-no-state / mismatch / missing-owner.
|
|
616
|
+
# Deliberately NOT mixed into doctor-pi-provider (provider runtime verdicts stay there).
|
|
617
|
+
doctor_pi_package() {
|
|
618
|
+
local agent_dir="${PI_CODING_AGENT_DIR:-$HOME/.pi/agent}"
|
|
619
|
+
command -v python3 >/dev/null 2>&1 || { echo "[doctor-pi-package] requires python3 on PATH." >&2; exit 1; }
|
|
620
|
+
# #86 C2 amendment: the provider state rides along for the OWNERSHIP coupling
|
|
621
|
+
# verdict only (installerRoot vs packageRoot mismatch = FAIL); provider RUNTIME
|
|
622
|
+
# verdicts stay with doctor-pi-provider.
|
|
623
|
+
python3 "$REPO_DIR/scripts/register-pi-package.py" "$agent_dir/settings.json" "$REPO_DIR" \
|
|
624
|
+
--scope user --state "$(_pi_package_state)" --provider-state "$(_pi_provider_state)" --doctor
|
|
569
625
|
}
|
|
570
626
|
|
|
571
627
|
# The honest inverse of register_user_scope_citizen: drop entwurf from the GLOBAL
|
|
@@ -581,11 +637,54 @@ register_user_scope_citizen() {
|
|
|
581
637
|
# reported rather than deleted — install never writes that form (#53 B).
|
|
582
638
|
remove_user_scope_citizen() {
|
|
583
639
|
local agent_dir="${PI_CODING_AGENT_DIR:-$HOME/.pi/agent}"
|
|
584
|
-
|
|
640
|
+
local pkg_state pp_state orphan_args=()
|
|
641
|
+
pkg_state="$(_pi_package_state)"; pp_state="$(_pi_provider_state)"
|
|
642
|
+
# #86 C2 aligned orphan path: --orphan-cleanup is passed ONLY when the package
|
|
643
|
+
# entry, the package state's packageRoot and the provider state's installerRoot
|
|
644
|
+
# all name the SAME root and that root is MISSING on disk. Any live foreign
|
|
645
|
+
# owner, or any mismatch between the three, refuses inside the SSOTs instead.
|
|
646
|
+
if python3 - "$agent_dir/settings.json" "$pkg_state" "$pp_state" <<'PY'
|
|
647
|
+
import json, os, sys
|
|
648
|
+
settings_path, pkg_state_path, pp_state_path = sys.argv[1:4]
|
|
649
|
+
def load(p):
|
|
650
|
+
try:
|
|
651
|
+
with open(p) as fh: return json.load(fh)
|
|
652
|
+
except (OSError, json.JSONDecodeError): return None
|
|
653
|
+
pkg = load(pkg_state_path)
|
|
654
|
+
if not isinstance(pkg, dict) or not isinstance(pkg.get("packageRoot"), str): sys.exit(1)
|
|
655
|
+
owner = pkg["packageRoot"]
|
|
656
|
+
if os.path.isdir(owner): sys.exit(1) # live owner: never orphan
|
|
657
|
+
# Alignment REQUIRES the provider state to exist AND to be bound to the same
|
|
658
|
+
# missing root (#86 C2 amendment): an absent, legacy (no installerRoot) or
|
|
659
|
+
# elsewhere-bound provider state is NOT alignment — both sides refuse instead.
|
|
660
|
+
pp = load(pp_state_path)
|
|
661
|
+
if not isinstance(pp, dict): sys.exit(1)
|
|
662
|
+
if pp.get("installerRoot") != owner: sys.exit(1)
|
|
663
|
+
settings = load(settings_path)
|
|
664
|
+
pkgs = settings.get("packages") if isinstance(settings, dict) else None
|
|
665
|
+
if not isinstance(pkgs, list): sys.exit(1)
|
|
666
|
+
def src(x): return x.get("source") if isinstance(x, dict) else x
|
|
667
|
+
if not any(isinstance(src(x), str) and src(x).rstrip("/") == owner for x in pkgs): sys.exit(1)
|
|
668
|
+
sys.exit(0)
|
|
669
|
+
PY
|
|
670
|
+
then
|
|
671
|
+
orphan_args=(--orphan-cleanup)
|
|
672
|
+
log "remove-user-scope: package entry + package state + provider installerRoot align on a MISSING owner — reported orphan cleanup"
|
|
673
|
+
fi
|
|
674
|
+
# ATOMIC (#86 C2 amendment): both removal halves are decided READ-ONLY first, so
|
|
675
|
+
# a provider-side refusal (live-other, legacy, mismatch) leaves the package
|
|
676
|
+
# entry/state byte-identical instead of a half-removed user scope.
|
|
677
|
+
python3 "$REPO_DIR/scripts/register-pi-package.py" "$agent_dir/settings.json" "$REPO_DIR" \
|
|
678
|
+
--scope user --state "$pkg_state" --remove ${orphan_args[@]+"${orphan_args[@]}"} --preflight >/dev/null
|
|
679
|
+
python3 "$REPO_DIR/scripts/register-pi-provider.py" remove "$agent_dir/settings.json" "$REPO_DIR" \
|
|
680
|
+
--scope user --state "$pp_state" ${orphan_args[@]+"${orphan_args[@]}"} --preflight >/dev/null
|
|
681
|
+
python3 "$REPO_DIR/scripts/register-pi-package.py" "$agent_dir/settings.json" "$REPO_DIR" \
|
|
682
|
+
--scope user --state "$pkg_state" --remove ${orphan_args[@]+"${orphan_args[@]}"}
|
|
585
683
|
# #46 Task 2: honest inverse of the user-scope entwurfProvider ownership — the install-state
|
|
586
684
|
# drives it (absent/managed-* → remove OUR key; a user-override we never owned is untouched).
|
|
587
|
-
|
|
588
|
-
python3 "$REPO_DIR/scripts/register-pi-provider.py" remove "$agent_dir/settings.json" "$REPO_DIR"
|
|
685
|
+
# #86 C2: installerRoot makes this same-owner-only; the aligned orphan flag rides along.
|
|
686
|
+
python3 "$REPO_DIR/scripts/register-pi-provider.py" remove "$agent_dir/settings.json" "$REPO_DIR" \
|
|
687
|
+
--scope user --state "$pp_state" ${orphan_args[@]+"${orphan_args[@]}"}
|
|
589
688
|
}
|
|
590
689
|
|
|
591
690
|
# The ~/.pi/agent/entwurf-targets.json symlink machinery (ensure_agent_dir_symlinks
|
|
@@ -692,6 +791,42 @@ check_entwurf_capabilities() {
|
|
|
692
791
|
run_ts scripts/check-entwurf-capabilities.ts
|
|
693
792
|
}
|
|
694
793
|
|
|
794
|
+
smoke_omp_fresh_live() {
|
|
795
|
+
# #87 Bundle C acceptance, RELEASE MUST. Opens omp as ONE visible fresh sibling through
|
|
796
|
+
# the PUBLIC entwurf_fresh_call surface in a private tmux server, correlates the exact
|
|
797
|
+
# nonce callback to a garden id from the SENDER ENVELOPE, then delivers an addressed
|
|
798
|
+
# message the sibling drains in that same native session. Decides its own outcome: omp
|
|
799
|
+
# absent from FRESH_CALL_BACKENDS or without a mailbox rail is a protocol SKIP, LIVE!=1
|
|
800
|
+
# is a protocol SKIP, and once the composition offers the backend every missing
|
|
801
|
+
# prerequisite is a FAIL. Writes into the REAL meta roots — the extensions run inside the
|
|
802
|
+
# launched omp process and no env carrier on the fresh argv could fence them.
|
|
803
|
+
run_ts scripts/smoke-omp-fresh-live.ts
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
check_omp_fresh_preflight() {
|
|
807
|
+
# #87 Bundle C. The OMP fresh preflight REPRODUCES two resolvers that already exist
|
|
808
|
+
# in this repo in another language — omp_agent_dir (scripts/omp-bridge-oracle.sh) and
|
|
809
|
+
# the tools.xdev half of scripts/omp-tool-surface.py — because the preflight runs from
|
|
810
|
+
# two different emit depths and cannot resolve a sibling script by relative path. A
|
|
811
|
+
# reproduction nothing compares is a divergence with a date on it, so this drives the
|
|
812
|
+
# SHIPPED shell and python leaves and requires the TS half to agree, refusals included.
|
|
813
|
+
# Never asks the resolver under test to confirm itself. Fixtures only; never reads ~/.omp.
|
|
814
|
+
run_ts scripts/check-omp-fresh-preflight.ts
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
check_harness_admission_parity() {
|
|
818
|
+
# #87 Bundle C. The EDGE between two parity loops that were each closed and had
|
|
819
|
+
# nothing between them: check-entwurf-capabilities held registry ==
|
|
820
|
+
# META_CITIZEN_BACKENDS, the fresh-call surfaces contract held surfaces ==
|
|
821
|
+
# FRESH_CALL_BACKENDS, and no file read both constants. So a harness could be a
|
|
822
|
+
# full D6 citizen that entwurf_fresh_call cannot open, with every gate green and
|
|
823
|
+
# only prose saying it was not supported — which is how an `unsupported` label
|
|
824
|
+
# started reading as a partial-release permit. Asserts every citizen backend is
|
|
825
|
+
# either fresh-openable or a DECLARED pre-#82 legacy admission whose exception a
|
|
826
|
+
# reader can find in DELIVERY.md, both directions, no third state. Pure parse.
|
|
827
|
+
run_ts scripts/check-harness-admission-parity.ts
|
|
828
|
+
}
|
|
829
|
+
|
|
695
830
|
check_capability_bundle_reach() {
|
|
696
831
|
# The gate check-entwurf-capabilities structurally cannot be. That gate imports
|
|
697
832
|
# metaCapabilitiesFilePath() from ../pi-extensions/lib/ — the one location where
|
|
@@ -1156,7 +1291,11 @@ check_mux_fresh_call() {
|
|
|
1156
1291
|
# copilot-fresh-preflight rides here rather than in its own gate: it exists only as
|
|
1157
1292
|
# freshCall's pre-mutation branch (#82 RAIL 9), and splitting it would let the two halves
|
|
1158
1293
|
# of one refusal be certified in different runs.
|
|
1159
|
-
|
|
1294
|
+
# omp-fresh-bootstrap.contract rides here for the same reason and one more: it is the ONLY
|
|
1295
|
+
# place the launcher's payload and the installed birth extension's decoder are read together.
|
|
1296
|
+
# They ship in different directories and cannot import each other (#87 Bundle C), so this
|
|
1297
|
+
# lane is what keeps a deliberate duplication from becoming drift.
|
|
1298
|
+
run_vitest test/mux-fresh-call.test.ts test/copilot-fresh-preflight.test.ts test/fresh-call-surfaces.contract.test.ts test/fresh-call-provider.contract.test.ts test/omp-fresh-bootstrap.contract.test.ts
|
|
1160
1299
|
}
|
|
1161
1300
|
|
|
1162
1301
|
smoke_mux_fresh_call_live() {
|
|
@@ -2809,6 +2948,9 @@ check_pack() {
|
|
|
2809
2948
|
# #65 — the owner-normalized store projection consumers call INSTEAD of
|
|
2810
2949
|
# parsing the store; installed hosts are exactly where those consumers live.
|
|
2811
2950
|
"mcp/entwurf-bridge/dist/scripts/meta-facts.js"
|
|
2951
|
+
# #87 Bundle B — omp-receive-doctor drives this operator projection through
|
|
2952
|
+
# run.sh, so installed node_modules must have the compiled twin too.
|
|
2953
|
+
"mcp/entwurf-bridge/dist/scripts/omp-receive-facts.js"
|
|
2812
2954
|
# 0.12.5 — the node_modules-safe plugin hook + its lib. install-meta-bridge copies
|
|
2813
2955
|
# these compiled JS into the assembled plugin when installed (raw .ts can't
|
|
2814
2956
|
# strip-types under node_modules). meta-session.js is shared with the store-doctor
|
|
@@ -2817,6 +2959,13 @@ check_pack() {
|
|
|
2817
2959
|
# #82 — the Copilot birth entry's compiled closure, for the same node_modules
|
|
2818
2960
|
# strip-types refusal that forces the Claude one.
|
|
2819
2961
|
"mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-hook-copilot.js"
|
|
2962
|
+
# #87 — the OMP birth entry's compiled closure. install-omp-bridge SELECTS this path
|
|
2963
|
+
# under node_modules, and docs/setup-clean-host.md tells operators to run that installer,
|
|
2964
|
+
# so its absence is not a degraded path: the installed installer dies at its own artifact
|
|
2965
|
+
# check before assembling anything. The unit skeleton's package.json below is the other
|
|
2966
|
+
# half — the installer copies it, and `pi/` ships per-FILE.
|
|
2967
|
+
"mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-omp.js"
|
|
2968
|
+
"pi/meta-bridge-omp/entwurf-meta-omp/package.json"
|
|
2820
2969
|
"mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js"
|
|
2821
2970
|
"scripts/postinstall-chmod.cjs"
|
|
2822
2971
|
"pi/entwurf-capabilities.json"
|
|
@@ -2839,6 +2988,8 @@ check_pack() {
|
|
|
2839
2988
|
"pi/meta-bridge-copilot/entwurf-meta-receive-copilot/scripts/copilot-hook-launch.sh"
|
|
2840
2989
|
"pi-extensions/meta-bridge-hook-copilot.ts"
|
|
2841
2990
|
"scripts/copilot-bridge-install.sh"
|
|
2991
|
+
"scripts/copilot-bridge-uninstall.sh"
|
|
2992
|
+
"scripts/copilot-bridge-oracle.sh"
|
|
2842
2993
|
"scripts/copilot-bridge-doctor.sh"
|
|
2843
2994
|
# #82 RAIL 5 — the Copilot RECEIVER. A vendor EXTENSION (forked child, stdio
|
|
2844
2995
|
# JSON-RPC), not a plugin, so it ships as its own unit and installs into the user
|
|
@@ -3062,12 +3213,21 @@ _check_pack_install_impl() {
|
|
|
3062
3213
|
"mcp/entwurf-bridge/dist/scripts/meta-bridge-fresh-cut.js"
|
|
3063
3214
|
# #65 — the owner-normalized store projection (see check-pack).
|
|
3064
3215
|
"mcp/entwurf-bridge/dist/scripts/meta-facts.js"
|
|
3216
|
+
# #87 Bundle B — omp-receive-doctor reaches this through the installed dispatcher.
|
|
3217
|
+
"mcp/entwurf-bridge/dist/scripts/omp-receive-facts.js"
|
|
3065
3218
|
# 0.12.5 — node_modules-safe plugin hook + lib (see check-pack). The installed
|
|
3066
3219
|
# hook regression below runs exactly this compiled JS from under node_modules.
|
|
3067
3220
|
"mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-hook.js"
|
|
3068
3221
|
# #82 — the Copilot birth entry's compiled closure, for the same node_modules
|
|
3069
3222
|
# strip-types refusal that forces the Claude one.
|
|
3070
3223
|
"mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-hook-copilot.js"
|
|
3224
|
+
# #87 — the OMP birth entry's compiled closure. install-omp-bridge SELECTS this path
|
|
3225
|
+
# under node_modules, and docs/setup-clean-host.md tells operators to run that installer,
|
|
3226
|
+
# so its absence is not a degraded path: the installed installer dies at its own artifact
|
|
3227
|
+
# check before assembling anything. The unit skeleton's package.json below is the other
|
|
3228
|
+
# half — the installer copies it, and `pi/` ships per-FILE.
|
|
3229
|
+
"mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-omp.js"
|
|
3230
|
+
"pi/meta-bridge-omp/entwurf-meta-omp/package.json"
|
|
3071
3231
|
"mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js"
|
|
3072
3232
|
"scripts/postinstall-chmod.cjs"
|
|
3073
3233
|
"pi/entwurf-capabilities.json"
|
|
@@ -3089,6 +3249,8 @@ _check_pack_install_impl() {
|
|
|
3089
3249
|
"pi/meta-bridge-copilot/entwurf-meta-receive-copilot/scripts/copilot-hook-launch.sh"
|
|
3090
3250
|
"pi-extensions/meta-bridge-hook-copilot.ts"
|
|
3091
3251
|
"scripts/copilot-bridge-install.sh"
|
|
3252
|
+
"scripts/copilot-bridge-uninstall.sh"
|
|
3253
|
+
"scripts/copilot-bridge-oracle.sh"
|
|
3092
3254
|
"scripts/copilot-bridge-doctor.sh"
|
|
3093
3255
|
# #82 RAIL 5 — the Copilot RECEIVER. A vendor EXTENSION (forked child, stdio
|
|
3094
3256
|
# JSON-RPC), not a plugin, so it ships as its own unit and installs into the user
|
|
@@ -3144,9 +3306,9 @@ _check_pack_install_impl() {
|
|
|
3144
3306
|
# to our pnpm-workspace.yaml; --ignore-scripts blocks the husky
|
|
3145
3307
|
# prepare hook (and any future install scripts) from running inside
|
|
3146
3308
|
# the consumer project. Peer deps are pinned to the CURRENT pi release
|
|
3147
|
-
# baseline (
|
|
3148
|
-
#
|
|
3149
|
-
# pi user would have after `pi install`.
|
|
3309
|
+
# baseline (the package.json devDep pin — the pins below are what
|
|
3310
|
+
# check-dep-versions binds to that devDep) so the smoke matches the same
|
|
3311
|
+
# shape an external pi user would have after `pi install`.
|
|
3150
3312
|
local tmp npm_tmp
|
|
3151
3313
|
tmp=$(mktemp -d -t entwurf-install-smoke.XXXXXX)
|
|
3152
3314
|
# Separate tree for the npm-managed regression below: npm install must NOT be
|
|
@@ -3171,8 +3333,15 @@ _check_pack_install_impl() {
|
|
|
3171
3333
|
# {pi-agent-core, pi-ai, pi-tui} at 0.83.0 to {pi-agent-core, pi-ai, pi-client,
|
|
3172
3334
|
# pi-protocol, pi-tui}, so pi-client and pi-protocol are pinned here for the
|
|
3173
3335
|
# first time. Unpinned they would float exactly like pi-agent-core did in the
|
|
3174
|
-
# 2026-07-21 incident above.
|
|
3175
|
-
#
|
|
3336
|
+
# 2026-07-21 incident above. pi-telemetry joined the explicit pin list on
|
|
3337
|
+
# 2026-08-31: it arrives transitively (pi-agent-core and pi-ai both carry
|
|
3338
|
+
# `^0.84.3` carets on it), and upstream's 0.84.4 patch publish (2026-08-28
|
|
3339
|
+
# 22:04Z) floated that caret in this lockfile-less temp install, turning CI
|
|
3340
|
+
# red through the leak assertion below. The bump itself stays a separate
|
|
3341
|
+
# hard-cut lane (#87 thread / NEXT "Do not touch: Pi 0.84.4") — pinning the
|
|
3342
|
+
# drift shut is how the verified 0.84.3 runtime stays OURS to hold. The leak
|
|
3343
|
+
# assertion below still covers every other pi package, including any package
|
|
3344
|
+
# a future pi bump adds to the closure.
|
|
3176
3345
|
echo "[check-pack-install] pnpm add into $tmp (with 0.84.x peers + typebox)"
|
|
3177
3346
|
local install_log
|
|
3178
3347
|
install_log=$(cd "$tmp" && pnpm add \
|
|
@@ -3183,6 +3352,7 @@ _check_pack_install_impl() {
|
|
|
3183
3352
|
"@earendil-works/pi-agent-core@0.84.3" \
|
|
3184
3353
|
"@earendil-works/pi-client@0.84.3" \
|
|
3185
3354
|
"@earendil-works/pi-protocol@0.84.3" \
|
|
3355
|
+
"@earendil-works/pi-telemetry@0.84.3" \
|
|
3186
3356
|
"typebox@latest" \
|
|
3187
3357
|
--ignore-workspace --ignore-scripts 2>&1) || {
|
|
3188
3358
|
fail "[check-pack-install] pnpm add failed:"
|
|
@@ -3414,6 +3584,230 @@ sys.exit(0 if any(isinstance(s,str) and s.endswith('/node_modules/@junghanacs/en
|
|
|
3414
3584
|
fi
|
|
3415
3585
|
echo "[check-pack-install] user-scope citizen regression pass (npm consumer: --entwurf-control loads from a foreign cwd)"
|
|
3416
3586
|
|
|
3587
|
+
# Two-root ownership row (#86 C2, L1b extension): a SECOND npm root under the
|
|
3588
|
+
# SAME consumer HOME must not silently steal the user-scope registration the
|
|
3589
|
+
# first root owns — normal install refuses (naming takeover-user-scope) and the
|
|
3590
|
+
# operator-explicit takeover is what moves the shared entry, old→new reported.
|
|
3591
|
+
local npmroot2="$npm_tmp/npmroot2" npmproj2="$npm_tmp/npmproj2" npm2_log two_root_out two_root_rc npm2_pkg
|
|
3592
|
+
mkdir -p "$npmroot2" "$npmproj2"
|
|
3593
|
+
npm2_log=$(cd "$npmroot2" && npm install "$tgz_path" --no-audit --no-fund 2>&1) || {
|
|
3594
|
+
fail "[check-pack-install] second npm root install failed:"
|
|
3595
|
+
echo "$npm2_log" | tail -10 | sed 's/^/ /' >&2
|
|
3596
|
+
return 1
|
|
3597
|
+
}
|
|
3598
|
+
npm2_pkg="$npmroot2/node_modules/@junghanacs/entwurf"
|
|
3599
|
+
set +e
|
|
3600
|
+
two_root_out=$(HOME="$npmhome" XDG_DATA_HOME="$npmhome/.local/share" XDG_STATE_HOME="$npmhome/.local/state" XDG_CACHE_HOME="$npmhome/.cache" "$npm2_pkg/run.sh" install "$npmproj2" 2>&1)
|
|
3601
|
+
two_root_rc=$?
|
|
3602
|
+
set -e
|
|
3603
|
+
if [ "$two_root_rc" -eq 0 ] || ! grep -q "takeover-user-scope" <<<"$two_root_out"; then
|
|
3604
|
+
fail "[check-pack-install] a second npm root's normal install must REFUSE the owned user-scope registration and name takeover-user-scope (rc=$two_root_rc):"
|
|
3605
|
+
echo "$two_root_out" | tail -10 | sed 's/^/ /' >&2
|
|
3606
|
+
return 1
|
|
3607
|
+
fi
|
|
3608
|
+
if ! python3 -c "
|
|
3609
|
+
import json,sys
|
|
3610
|
+
p=json.load(open('$npm_user_settings')).get('packages',[])
|
|
3611
|
+
srcs=[(x if isinstance(x,str) else x.get('source')) for x in p]
|
|
3612
|
+
ok_a=any(isinstance(s,str) and s.rstrip('/')== '$npm_pkg' for s in srcs)
|
|
3613
|
+
bad_b=any(isinstance(s,str) and s.rstrip('/')== '$npm2_pkg' for s in srcs)
|
|
3614
|
+
sys.exit(0 if ok_a and not bad_b else 1)
|
|
3615
|
+
"; then
|
|
3616
|
+
fail "[check-pack-install] the refused second-root install still altered the user-scope packages[] entry:"
|
|
3617
|
+
sed 's/^/ /' "$npm_user_settings" >&2
|
|
3618
|
+
return 1
|
|
3619
|
+
fi
|
|
3620
|
+
set +e
|
|
3621
|
+
two_root_out=$(HOME="$npmhome" XDG_DATA_HOME="$npmhome/.local/share" XDG_STATE_HOME="$npmhome/.local/state" XDG_CACHE_HOME="$npmhome/.cache" "$npm2_pkg/run.sh" takeover-user-scope 2>&1)
|
|
3622
|
+
two_root_rc=$?
|
|
3623
|
+
set -e
|
|
3624
|
+
if [ "$two_root_rc" -ne 0 ] || ! grep -q "takeover: user-scope entwurf registration moved" <<<"$two_root_out"; then
|
|
3625
|
+
fail "[check-pack-install] explicit takeover-user-scope from the second root failed (rc=$two_root_rc):"
|
|
3626
|
+
echo "$two_root_out" | tail -10 | sed 's/^/ /' >&2
|
|
3627
|
+
return 1
|
|
3628
|
+
fi
|
|
3629
|
+
if ! python3 -c "
|
|
3630
|
+
import json,sys
|
|
3631
|
+
p=json.load(open('$npm_user_settings')).get('packages',[])
|
|
3632
|
+
srcs=[(x if isinstance(x,str) else x.get('source')) for x in p]
|
|
3633
|
+
ok_b=any(isinstance(s,str) and s.rstrip('/')== '$npm2_pkg' for s in srcs)
|
|
3634
|
+
bad_a=any(isinstance(s,str) and s.rstrip('/')== '$npm_pkg' for s in srcs)
|
|
3635
|
+
sys.exit(0 if ok_b and not bad_a else 1)
|
|
3636
|
+
"; then
|
|
3637
|
+
fail "[check-pack-install] takeover did not move the user-scope entry old→new:"
|
|
3638
|
+
sed 's/^/ /' "$npm_user_settings" >&2
|
|
3639
|
+
return 1
|
|
3640
|
+
fi
|
|
3641
|
+
# #86 C2 amendment: BOTH ownership halves moved — the provider installerRoot must
|
|
3642
|
+
# now name the new root, and the OLD root's inverse must refuse (live foreign owner).
|
|
3643
|
+
if ! python3 -c "
|
|
3644
|
+
import json,sys
|
|
3645
|
+
st=json.load(open('$npmhome/.local/share/entwurf/pi-provider/install-state.json'))
|
|
3646
|
+
sys.exit(0 if st.get('installerRoot','').rstrip('/')== '$npm2_pkg' else 1)
|
|
3647
|
+
"; then
|
|
3648
|
+
fail "[check-pack-install] takeover did not rebind the provider installerRoot to the new root:"
|
|
3649
|
+
sed 's/^/ /' "$npmhome/.local/share/entwurf/pi-provider/install-state.json" >&2
|
|
3650
|
+
return 1
|
|
3651
|
+
fi
|
|
3652
|
+
set +e
|
|
3653
|
+
two_root_out=$(HOME="$npmhome" XDG_DATA_HOME="$npmhome/.local/share" XDG_STATE_HOME="$npmhome/.local/state" XDG_CACHE_HOME="$npmhome/.cache" "$npm_pkg/run.sh" remove-user-scope 2>&1)
|
|
3654
|
+
two_root_rc=$?
|
|
3655
|
+
set -e
|
|
3656
|
+
if [ "$two_root_rc" -eq 0 ] || ! python3 -c "
|
|
3657
|
+
import json,sys
|
|
3658
|
+
p=json.load(open('$npm_user_settings')).get('packages',[])
|
|
3659
|
+
srcs=[(x if isinstance(x,str) else x.get('source')) for x in p]
|
|
3660
|
+
sys.exit(0 if any(isinstance(s,str) and s.rstrip('/')== '$npm2_pkg' for s in srcs) else 1)
|
|
3661
|
+
"; then
|
|
3662
|
+
fail "[check-pack-install] the OLD root's remove-user-scope must refuse after takeover (rc=$two_root_rc) and leave the new owner's entry intact:"
|
|
3663
|
+
echo "$two_root_out" | tail -8 | sed 's/^/ /' >&2
|
|
3664
|
+
return 1
|
|
3665
|
+
fi
|
|
3666
|
+
echo "[check-pack-install] two-root ownership row pass (second root: normal install refused naming takeover-user-scope; explicit takeover moved entry + provider installerRoot old->new; old root's inverse refused)"
|
|
3667
|
+
|
|
3668
|
+
# Installed-package aggregate `setup` row (#86 C1, review blocker 2026-08-26):
|
|
3669
|
+
# the composed public command itself, driven through the consumer bin from the
|
|
3670
|
+
# SAME installed candidate, on a FRESH sandbox home with every harness absent.
|
|
3671
|
+
# This is the first living consumer of package-installed `entwurf setup`:
|
|
3672
|
+
# mode=installed decided by name before anything else, NO pnpm bootstrap inside
|
|
3673
|
+
# node_modules, pi/claude/agy explicit zero-state SKIPs, stable bins PASS as
|
|
3674
|
+
# npm-provided, core bridge boundary PASS, computed green, zero harness/auth
|
|
3675
|
+
# writes. Harness probes are pinned absent via the PI_BIN/CLAUDE_BIN/AGY_BIN/
|
|
3676
|
+
# COPILOT_BIN seams so the gate host's real harnesses never leak in.
|
|
3677
|
+
local setup_home="$npm_tmp/setuphome" setup_proj="$npm_tmp/setupproj" setup_out setup_rc setup_auth
|
|
3678
|
+
mkdir -p "$setup_home/.pi/agent" "$setup_proj"
|
|
3679
|
+
setup_auth="$setup_home/.pi/agent/auth.json"
|
|
3680
|
+
printf '{\n "anthropic": {\n "type": "oauth",\n "access": "consumer-oauth-token"\n }\n}\n' > "$setup_auth"
|
|
3681
|
+
local setup_auth_before
|
|
3682
|
+
setup_auth_before="$(sha256sum "$setup_auth" | cut -d' ' -f1)"
|
|
3683
|
+
set +e
|
|
3684
|
+
setup_out=$(HOME="$setup_home" XDG_DATA_HOME="$setup_home/.local/share" XDG_STATE_HOME="$setup_home/.local/state" \
|
|
3685
|
+
XDG_CACHE_HOME="$setup_home/.cache" XDG_CONFIG_HOME="$setup_home/.config" PI_CODING_AGENT_DIR="$setup_home/.pi/agent" \
|
|
3686
|
+
PI_BIN="$npm_tmp/definitely-absent" CLAUDE_BIN="$npm_tmp/definitely-absent" AGY_BIN="$npm_tmp/definitely-absent" \
|
|
3687
|
+
COPILOT_BIN="$npm_tmp/definitely-absent" \
|
|
3688
|
+
"$npmroot/node_modules/.bin/entwurf" setup "$setup_proj" 2>&1)
|
|
3689
|
+
setup_rc=$?
|
|
3690
|
+
set -e
|
|
3691
|
+
if [ "$setup_rc" -ne 0 ]; then
|
|
3692
|
+
fail "[check-pack-install] installed all-absent setup exited $setup_rc — core must stay green with every harness absent, and an installed package must never re-enter the source pnpm bootstrap:"
|
|
3693
|
+
echo "$setup_out" | tail -25 | sed 's/^/ /' >&2
|
|
3694
|
+
return 1
|
|
3695
|
+
fi
|
|
3696
|
+
if ! head -n 1 <<<"$setup_out" | grep -q "mode: installed package"; then
|
|
3697
|
+
fail "[check-pack-install] installed setup did not name its mode FIRST:"
|
|
3698
|
+
echo "$setup_out" | head -5 | sed 's/^/ /' >&2
|
|
3699
|
+
return 1
|
|
3700
|
+
fi
|
|
3701
|
+
if grep -Eqi "pnpm install|Lockfile is up to date|Progress: resolved" <<<"$setup_out"; then
|
|
3702
|
+
fail "[check-pack-install] installed setup reached the source pnpm bootstrap inside node_modules:"
|
|
3703
|
+
echo "$setup_out" | grep -Ei "pnpm|Lockfile|Progress" | sed 's/^/ /' >&2
|
|
3704
|
+
return 1
|
|
3705
|
+
fi
|
|
3706
|
+
local skip_probe
|
|
3707
|
+
for skip_probe in "pi: SKIP" "claude: SKIP" "agy: SKIP" "copilot: SKIP"; do
|
|
3708
|
+
if ! grep -q "$skip_probe" <<<"$setup_out"; then
|
|
3709
|
+
fail "[check-pack-install] installed all-absent setup missing explicit zero-state '$skip_probe':"
|
|
3710
|
+
echo "$setup_out" | tail -25 | sed 's/^/ /' >&2
|
|
3711
|
+
return 1
|
|
3712
|
+
fi
|
|
3713
|
+
done
|
|
3714
|
+
if ! grep -q "bins: PASS — provided by npm bin linking" <<<"$setup_out"; then
|
|
3715
|
+
fail "[check-pack-install] installed setup must report stable bins PASS as npm-provided (capability present, not absent):"
|
|
3716
|
+
echo "$setup_out" | tail -25 | sed 's/^/ /' >&2
|
|
3717
|
+
return 1
|
|
3718
|
+
fi
|
|
3719
|
+
if ! grep -q "core: PASS" <<<"$setup_out" || ! grep -q "result: green (computed from the component outcomes above)" <<<"$setup_out"; then
|
|
3720
|
+
fail "[check-pack-install] installed setup core/computed-green missing:"
|
|
3721
|
+
echo "$setup_out" | tail -25 | sed 's/^/ /' >&2
|
|
3722
|
+
return 1
|
|
3723
|
+
fi
|
|
3724
|
+
if [ -e "$setup_proj/.pi" ] || [ -e "$setup_home/.pi/agent/settings.json" ] || [ -e "$setup_home/.gemini" ] || [ -e "$setup_home/.copilot" ]; then
|
|
3725
|
+
fail "[check-pack-install] installed all-absent setup wrote harness state (must be zero-write)"
|
|
3726
|
+
return 1
|
|
3727
|
+
fi
|
|
3728
|
+
if [ "$(sha256sum "$setup_auth" | cut -d' ' -f1)" != "$setup_auth_before" ] || [ -e "$setup_auth.bak" ]; then
|
|
3729
|
+
fail "[check-pack-install] installed setup touched the credential store (byte drift or .bak)"
|
|
3730
|
+
return 1
|
|
3731
|
+
fi
|
|
3732
|
+
echo "[check-pack-install] installed all-absent setup pass (mode-first, no bootstrap, 4x SKIP, bins npm-provided, core PASS, computed green, zero writes)"
|
|
3733
|
+
|
|
3734
|
+
# Installed copilot-present aggregate `setup` row (#86 C3b): the SAME consumer
|
|
3735
|
+
# bin on a fresh sandbox home, with the shared fake vendor
|
|
3736
|
+
# (scripts/fake-copilot-vendor.sh) on PATH. The presence-driven composition
|
|
3737
|
+
# must run all FOUR Copilot units from the installed package: birth assembles
|
|
3738
|
+
# the COMPILED hook (no raw .ts under node_modules) and drives the real
|
|
3739
|
+
# marketplace-add→plugin-install sequence, MCP + receiver + footer land their
|
|
3740
|
+
# configs/units in the sandbox, each unit keeps its package-owned
|
|
3741
|
+
# install-state (its inverse authority), and the verdict is computed green.
|
|
3742
|
+
local cop_setup_home="$npm_tmp/copsetuphome" cop_setup_proj="$npm_tmp/copsetupproj" cop_fake="$npm_tmp/fake-copilot-vendor"
|
|
3743
|
+
mkdir -p "$cop_setup_home/.pi/agent" "$cop_setup_proj"
|
|
3744
|
+
printf '{\n "anthropic": {\n "type": "oauth",\n "access": "consumer-oauth-token"\n }\n}\n' > "$cop_setup_home/.pi/agent/auth.json"
|
|
3745
|
+
local cop_auth_before
|
|
3746
|
+
cop_auth_before="$(sha256sum "$cop_setup_home/.pi/agent/auth.json" | cut -d' ' -f1)"
|
|
3747
|
+
# EXECUTED, never sourced: this impl holds a `trap … RETURN`, and bash fires a
|
|
3748
|
+
# RETURN trap when a sourced script finishes — a `. factory` here deleted the
|
|
3749
|
+
# whole npm sandbox mid-gate (measured 2026-08-27, C3b review).
|
|
3750
|
+
bash "$REPO_DIR/scripts/fake-copilot-vendor.sh" "$cop_fake" "$npm_pkg/pi/meta-bridge-copilot/entwurf-meta-receive-copilot/.claude-plugin/plugin.json" || {
|
|
3751
|
+
fail "[check-pack-install] could not build the fake copilot vendor from the PACKED plugin.json (is pi/meta-bridge-copilot missing from the tarball?)"
|
|
3752
|
+
return 1
|
|
3753
|
+
}
|
|
3754
|
+
set +e
|
|
3755
|
+
setup_out=$(HOME="$cop_setup_home" XDG_DATA_HOME="$cop_setup_home/.local/share" XDG_STATE_HOME="$cop_setup_home/.local/state" \
|
|
3756
|
+
XDG_CACHE_HOME="$cop_setup_home/.cache" XDG_CONFIG_HOME="$cop_setup_home/.config" PI_CODING_AGENT_DIR="$cop_setup_home/.pi/agent" \
|
|
3757
|
+
PI_BIN="$npm_tmp/definitely-absent" CLAUDE_BIN="$npm_tmp/definitely-absent" AGY_BIN="$npm_tmp/definitely-absent" \
|
|
3758
|
+
COPILOT_BIN="$cop_fake/copilot" PATH="$cop_fake:$npmroot/node_modules/.bin:$PATH" \
|
|
3759
|
+
"$npmroot/node_modules/.bin/entwurf" setup "$cop_setup_proj" 2>&1)
|
|
3760
|
+
setup_rc=$?
|
|
3761
|
+
set -e
|
|
3762
|
+
if [ "$setup_rc" -ne 0 ]; then
|
|
3763
|
+
fail "[check-pack-install] installed copilot-present setup exited $setup_rc — the four-unit composition must be green against the fake vendor:"
|
|
3764
|
+
echo "$setup_out" | tail -30 | sed 's/^/ /' >&2
|
|
3765
|
+
return 1
|
|
3766
|
+
fi
|
|
3767
|
+
local cop_row
|
|
3768
|
+
for cop_row in "copilot-birth: PASS" "copilot-mcp: PASS" "copilot-receive: PASS" "copilot-statusline: PASS"; do
|
|
3769
|
+
if ! grep -q "$cop_row" <<<"$setup_out"; then
|
|
3770
|
+
fail "[check-pack-install] installed copilot-present setup missing unit verdict '$cop_row':"
|
|
3771
|
+
echo "$setup_out" | tail -30 | sed 's/^/ /' >&2
|
|
3772
|
+
return 1
|
|
3773
|
+
fi
|
|
3774
|
+
done
|
|
3775
|
+
if ! grep -q "result: green (computed from the component outcomes above)" <<<"$setup_out"; then
|
|
3776
|
+
fail "[check-pack-install] installed copilot-present setup did not compute green:"
|
|
3777
|
+
echo "$setup_out" | tail -10 | sed 's/^/ /' >&2
|
|
3778
|
+
return 1
|
|
3779
|
+
fi
|
|
3780
|
+
local cop_setup_asm="$cop_setup_home/.local/share/entwurf/meta-bridge-copilot/.assembled/entwurf-meta-receive-copilot"
|
|
3781
|
+
if [ ! -f "$cop_setup_asm/meta-bridge-hook-copilot.js" ] || [ -f "$cop_setup_asm/meta-bridge-hook-copilot.ts" ]; then
|
|
3782
|
+
fail "[check-pack-install] installed copilot-present setup did not assemble the compiled birth hook (want meta-bridge-hook-copilot.js, no raw .ts) in $cop_setup_asm"
|
|
3783
|
+
return 1
|
|
3784
|
+
fi
|
|
3785
|
+
if ! grep -q "^plugin marketplace add " "$cop_fake/calls.log" || ! grep -q "^plugin install entwurf-meta-receive-copilot@meta-bridge-copilot-local$" "$cop_fake/calls.log"; then
|
|
3786
|
+
fail "[check-pack-install] installed copilot-present setup did not drive the vendor sequence (marketplace add + plugin install) — calls:"
|
|
3787
|
+
sed 's/^/ /' "$cop_fake/calls.log" >&2
|
|
3788
|
+
return 1
|
|
3789
|
+
fi
|
|
3790
|
+
if [ ! -f "$cop_setup_home/.copilot/extensions/entwurf-receive/extension.mjs" ] || [ ! -f "$cop_setup_home/.copilot/extensions/entwurf-receive/lib/meta-session.js" ]; then
|
|
3791
|
+
fail "[check-pack-install] installed copilot-present setup did not deploy the receiver unit from the shipped dist closure"
|
|
3792
|
+
return 1
|
|
3793
|
+
fi
|
|
3794
|
+
if ! grep -q "entwurf-bridge" "$cop_setup_home/.copilot/mcp-config.json" || ! grep -q "entwurf-copilot-statusline" "$cop_setup_home/.copilot/settings.json"; then
|
|
3795
|
+
fail "[check-pack-install] installed copilot-present setup did not land the MCP config / footer settings in the sandbox home"
|
|
3796
|
+
return 1
|
|
3797
|
+
fi
|
|
3798
|
+
local cop_state
|
|
3799
|
+
for cop_state in copilot-bridge copilot-mcp copilot-receive copilot-statusline; do
|
|
3800
|
+
if [ ! -f "$cop_setup_home/.local/share/entwurf/$cop_state/install-state.json" ]; then
|
|
3801
|
+
fail "[check-pack-install] installed copilot-present setup left no package-owned install-state for $cop_state (its inverse authority)"
|
|
3802
|
+
return 1
|
|
3803
|
+
fi
|
|
3804
|
+
done
|
|
3805
|
+
if [ "$(sha256sum "$cop_setup_home/.pi/agent/auth.json" | cut -d' ' -f1)" != "$cop_auth_before" ] || [ -e "$cop_setup_home/.pi/agent/auth.json.bak" ]; then
|
|
3806
|
+
fail "[check-pack-install] installed copilot-present setup touched the credential store (byte drift or .bak)"
|
|
3807
|
+
return 1
|
|
3808
|
+
fi
|
|
3809
|
+
echo "[check-pack-install] installed copilot-present setup pass (four units PASS with install-states, compiled birth hook, vendor sequence driven, computed green, credentials untouched)"
|
|
3810
|
+
|
|
3417
3811
|
# Installed meta-bridge ownership regression (0.12.5): package upgrades must not
|
|
3418
3812
|
# bake versioned pnpm-store paths into Claude settings. MCP already uses the
|
|
3419
3813
|
# stable `entwurf-bridge` bin; statusLine must now use `entwurf-statusline`, and
|
|
@@ -3572,6 +3966,76 @@ SH
|
|
|
3572
3966
|
fi
|
|
3573
3967
|
rm -rf "$cop_store"
|
|
3574
3968
|
echo "[check-pack-install] installed Copilot birth unit assembles its compiled branch and mints a citizen from a no-argv launch"
|
|
3969
|
+
|
|
3970
|
+
# --- #87: the OMP birth unit's INSTALLED-PACKAGE branch ---------------------
|
|
3971
|
+
# Same shape and same reason as the Copilot cell above, and it is here because the gap it
|
|
3972
|
+
# closes was REAL, not theoretical: the compiled entry this installer selects under
|
|
3973
|
+
# node_modules was in no emit include and no artifact list, so `entwurf install-omp-bridge`
|
|
3974
|
+
# — which docs/setup-clean-host.md tells operators to run — died at its own artifact check
|
|
3975
|
+
# on every installed host while every gate stayed green (Terra amendment review, #87 A1).
|
|
3976
|
+
# The list entries above make that artifact's ABSENCE loud; this RUNS it, which is the half
|
|
3977
|
+
# Hard Rule 11 says a list can never stand in for.
|
|
3978
|
+
#
|
|
3979
|
+
# No omp binary is faked into doing anything: the installer only asks whether one is on
|
|
3980
|
+
# PATH (entwurf never installs a harness), and the unit is a module the vendor imports, not
|
|
3981
|
+
# a process we launch — so the exercise imports the placed artifact the way omp does.
|
|
3982
|
+
local omp_agent="$npm_tmp/omp-agent" omp_home="$npm_tmp/omp-home" omp_bin="$npm_tmp/omp-bin" omp_log
|
|
3983
|
+
mkdir -p "$omp_agent" "$omp_home" "$omp_bin"
|
|
3984
|
+
printf '#!/usr/bin/env bash\nexit 0\n' > "$omp_bin/omp"
|
|
3985
|
+
chmod +x "$omp_bin/omp"
|
|
3986
|
+
if ! omp_log=$(HOME="$omp_home" XDG_DATA_HOME="$omp_home/.local/share" ENTWURF_OMP_AGENT_DIR="$omp_agent" PATH="$omp_bin:$npmroot/node_modules/.bin:$PATH" "$npm_pkg/run.sh" install-omp-bridge 2>&1); then
|
|
3987
|
+
fail "[check-pack-install] installed install-omp-bridge failed:"
|
|
3988
|
+
echo "$omp_log" | tail -20 | sed 's/^/ /' >&2
|
|
3989
|
+
return 1
|
|
3990
|
+
fi
|
|
3991
|
+
local omp_unit="$omp_agent/extensions/entwurf-meta-omp"
|
|
3992
|
+
# Branch SELECTION, pinned exactly as the Copilot cell pins its own: an installed package
|
|
3993
|
+
# must place the compiled entry and NO raw .ts, or the fire below could pass on a layout
|
|
3994
|
+
# that silently chose the dev-clone branch.
|
|
3995
|
+
if [ ! -f "$omp_unit/index.js" ] || [ -f "$omp_unit/index.ts" ]; then
|
|
3996
|
+
fail "[check-pack-install] installed install-omp-bridge did not select the compiled branch (want index.js, no raw .ts) in $omp_unit"
|
|
3997
|
+
ls -l "$omp_unit" 2>/dev/null | sed 's/^/ /' >&2 || true
|
|
3998
|
+
return 1
|
|
3999
|
+
fi
|
|
4000
|
+
if [ ! -f "$omp_unit/lib/meta-session.js" ] || [ ! -f "$omp_unit/entwurf-capabilities.json" ] || [ ! -f "$omp_unit/package.json" ]; then
|
|
4001
|
+
fail "[check-pack-install] installed omp unit is incomplete (want lib/meta-session.js + entwurf-capabilities.json + package.json) in $omp_unit"
|
|
4002
|
+
return 1
|
|
4003
|
+
fi
|
|
4004
|
+
# Fired the way omp fires it: import the module, call the exported factory with a mock
|
|
4005
|
+
# ExtensionAPI, and hand the tui host context to the birth edge.
|
|
4006
|
+
local omp_fire
|
|
4007
|
+
if ! omp_fire=$(OMP_UNIT="$omp_unit" HOME="$omp_home" node --input-type=module <<'JS' 2>&1
|
|
4008
|
+
const mod = await import(process.env.OMP_UNIT + "/index.js");
|
|
4009
|
+
const handlers = [];
|
|
4010
|
+
(mod.default ?? mod)({ on: (event, handler) => { if (event === "session_start") handlers.push(handler); } });
|
|
4011
|
+
const ctx = {
|
|
4012
|
+
mode: "tui",
|
|
4013
|
+
cwd: "/tmp",
|
|
4014
|
+
ui: { setStatus: () => {} },
|
|
4015
|
+
sessionManager: { getSessionId: () => "pack-omp-probe", getCwd: () => "/tmp", getSessionFile: () => null },
|
|
4016
|
+
};
|
|
4017
|
+
for (const handler of handlers) await handler({ type: "session_start" }, ctx);
|
|
4018
|
+
process.stdout.write("fired " + handlers.length + "\n");
|
|
4019
|
+
JS
|
|
4020
|
+
); then
|
|
4021
|
+
fail "[check-pack-install] the installed omp unit failed to import/run from its placed location: $(printf '%s' "$omp_fire" | tr '\n' ' ' | cut -c1-300)"
|
|
4022
|
+
return 1
|
|
4023
|
+
fi
|
|
4024
|
+
# Counted through the OWNER's certified projection, not a grep — and through the INSTALLED
|
|
4025
|
+
# bin, so this also proves dist/scripts/meta-facts.js is reachable from a consumer. `env -u
|
|
4026
|
+
# PI_CODING_AGENT_DIR` is the omp root policy: for backend omp that variable is the vendor's
|
|
4027
|
+
# own agent dir and never a garden root (#87 B1).
|
|
4028
|
+
local omp_citizens
|
|
4029
|
+
omp_citizens=$(env -u PI_CODING_AGENT_DIR HOME="$omp_home" "$npm_pkg/run.sh" meta-facts 2>&1 | python3 -c '
|
|
4030
|
+
import json, sys
|
|
4031
|
+
facts = json.load(sys.stdin)
|
|
4032
|
+
print(sum(1 for c in (facts.get("citizens") or []) if c.get("backend") == "omp"))
|
|
4033
|
+
' 2>/dev/null) || omp_citizens=""
|
|
4034
|
+
if [ "$omp_citizens" != "1" ]; then
|
|
4035
|
+
fail "[check-pack-install] the installed omp unit did not mint exactly one CERTIFIED omp citizen (found ${omp_citizens:-<unreadable>}); log: $(tail -1 "$omp_home/.pi/agent/meta-bridge-hook.log" 2>/dev/null)"
|
|
4036
|
+
return 1
|
|
4037
|
+
fi
|
|
4038
|
+
echo "[check-pack-install] installed OMP birth unit places its compiled branch and mints a certified citizen from a tui birth edge"
|
|
3575
4039
|
python3 - "$mb_cfg/settings.json" "$mb_home/.claude.json" "$stable_asm" <<'PY'
|
|
3576
4040
|
import json, sys
|
|
3577
4041
|
settings = json.load(open(sys.argv[1]))
|
|
@@ -4226,24 +4690,109 @@ check_bridge() {
|
|
|
4226
4690
|
}
|
|
4227
4691
|
|
|
4228
4692
|
|
|
4229
|
-
#
|
|
4693
|
+
# ── setup verdict engine (#86 C1) ────────────────────────────────────────────
|
|
4694
|
+
# setup composes harnesses the operator ALREADY installed. Per component the
|
|
4695
|
+
# verdict is PASS (detected + completed), SKIP (truly absent — zero state
|
|
4696
|
+
# written), or FAIL (detected but incomplete/below-floor/refused). The final
|
|
4697
|
+
# summary and exit status are COMPUTED from these outcomes: core survival never
|
|
4698
|
+
# relabels a detected harness failure as green, and a FAIL owns a nonzero setup
|
|
4699
|
+
# result while every valid earlier write stays installed (re-running setup is
|
|
4700
|
+
# the documented repair action).
|
|
4701
|
+
SETUP_RESULTS=()
|
|
4702
|
+
setup_result() { # $1=component $2=PASS|SKIP|FAIL $3=detail
|
|
4703
|
+
SETUP_RESULTS+=("$1|$2|$3")
|
|
4704
|
+
case "$2" in
|
|
4705
|
+
PASS) ok "$1: PASS — $3" ;;
|
|
4706
|
+
SKIP) log "$1: SKIP — $3" ;;
|
|
4707
|
+
*) fail "$1: FAIL — $3" ;;
|
|
4708
|
+
esac
|
|
4709
|
+
}
|
|
4710
|
+
|
|
4711
|
+
# Installed-vs-source is decided by PACKAGE LOCATION — the same `*/node_modules/*`
|
|
4712
|
+
# seam run_ts and the bridge launcher already branch on — never by which of
|
|
4713
|
+
# pnpm/pi happens to be missing (an accidental failure is not a mode gate).
|
|
4714
|
+
setup_mode() {
|
|
4715
|
+
case "$REPO_DIR" in
|
|
4716
|
+
*/node_modules/*) echo installed ;;
|
|
4717
|
+
*) echo source ;;
|
|
4718
|
+
esac
|
|
4719
|
+
}
|
|
4720
|
+
|
|
4721
|
+
# Supported pi range, derived at runtime from the package.json devDependencies
|
|
4722
|
+
# pin (the SSOT check-dep-versions binds: peer range == `>=<pin> <0.<minor+1>`).
|
|
4723
|
+
# Never retyped here as a second literal.
|
|
4724
|
+
pi_supported_range() {
|
|
4725
|
+
node -e '
|
|
4726
|
+
const pkg = require(process.argv[1]);
|
|
4727
|
+
const pin = pkg.devDependencies?.["@earendil-works/pi-coding-agent"];
|
|
4728
|
+
if (!/^0\.\d+\.\d+$/.test(pin ?? "")) { console.error(`unparseable pi pin: ${pin}`); process.exit(1); }
|
|
4729
|
+
const [, min] = pin.split(".").map(Number);
|
|
4730
|
+
console.log(`>=${pin} <0.${min + 1}`);
|
|
4731
|
+
' "$REPO_DIR/package.json"
|
|
4732
|
+
}
|
|
4733
|
+
|
|
4734
|
+
pi_version_in_range() { # $1=detected version $2=range ">=a.b.c <x.y"; exit 0 in-range
|
|
4735
|
+
node -e '
|
|
4736
|
+
const [ver, range] = process.argv.slice(1);
|
|
4737
|
+
const m = range.match(/^>=(\d+)\.(\d+)\.(\d+) <(\d+)\.(\d+)$/);
|
|
4738
|
+
const v = ver.match(/^(\d+)\.(\d+)\.(\d+)$/);
|
|
4739
|
+
if (!m || !v) process.exit(2);
|
|
4740
|
+
const [maj, min, pat] = v.slice(1).map(Number);
|
|
4741
|
+
const lo = m.slice(1, 4).map(Number);
|
|
4742
|
+
const hi = m.slice(4, 6).map(Number);
|
|
4743
|
+
const geFloor = maj > lo[0] || (maj === lo[0] && (min > lo[1] || (min === lo[1] && pat >= lo[2])));
|
|
4744
|
+
const ltCeil = maj < hi[0] || (maj === hi[0] && min < hi[1]);
|
|
4745
|
+
process.exit(geFloor && ltCeil ? 0 : 1);
|
|
4746
|
+
' "$1" "$2"
|
|
4747
|
+
}
|
|
4748
|
+
|
|
4749
|
+
# setup_all — one-command presence-driven composition (#86).
|
|
4230
4750
|
#
|
|
4231
|
-
#
|
|
4232
|
-
#
|
|
4233
|
-
#
|
|
4234
|
-
#
|
|
4751
|
+
# Entwurf installs itself only: this command completes the integration of each
|
|
4752
|
+
# harness the operator already installed and NEVER installs a harness binary,
|
|
4753
|
+
# subscription, credential or login — `pi` has no privileged exception. It
|
|
4754
|
+
# verifies the installed bridge boundary; ACP/v1 backend interview gates are
|
|
4755
|
+
# deliberately not part of setup on v2-only (the heavier live v2 substrate
|
|
4756
|
+
# proof is release-gate's job). Copilot's four native units (birth / MCP /
|
|
4757
|
+
# receiver / visible footer) compose presence-driven since the birth inverse
|
|
4758
|
+
# landed (#86 C3a→C3b); the explicit install-copilot-* / uninstall-copilot-*
|
|
4759
|
+
# surfaces remain the operator-selected repair and inverse path.
|
|
4235
4760
|
#
|
|
4236
4761
|
# An external harness that consumes entwurf (e.g. agent-config as a
|
|
4237
4762
|
# pi package + skills set) may still have its own install/setup for its
|
|
4238
4763
|
# own concerns; those are outside the scope of this script.
|
|
4239
4764
|
setup_all() {
|
|
4765
|
+
# MODE FIRST (#86 C1): the installed-vs-source branch is a NAMED decision
|
|
4766
|
+
# ahead of every prerequisite check and write, so an installed consumer is
|
|
4767
|
+
# never mode-gated by an accidental missing `pnpm`/`pi`.
|
|
4768
|
+
local mode
|
|
4769
|
+
mode=$(setup_mode)
|
|
4770
|
+
case "$mode" in
|
|
4771
|
+
installed)
|
|
4772
|
+
echo "[setup] mode: installed package — composition only (no source bootstrap; setup never runs npm/pnpm inside node_modules)" ;;
|
|
4773
|
+
*)
|
|
4774
|
+
echo "[setup] mode: source checkout — repo dev bootstrap, then the same composition semantics" ;;
|
|
4775
|
+
esac
|
|
4776
|
+
|
|
4777
|
+
# Named interpreter prerequisite, decided BEFORE any write (#86 C1/E5):
|
|
4778
|
+
# setup's project-path normalization and every pi/agy wiring writer are
|
|
4779
|
+
# python3-backed today. `entwurf --help` / `entwurf check-bridge` stay
|
|
4780
|
+
# python3-free; only the commands that invoke those writers declare it.
|
|
4781
|
+
command -v python3 >/dev/null 2>&1 || {
|
|
4782
|
+
echo "[setup] entwurf setup requires python3 on PATH (project-path normalization + wiring writers)." >&2
|
|
4783
|
+
echo "[setup] Install python3, or use python3-free surfaces (--help, check-bridge)." >&2
|
|
4784
|
+
exit 1
|
|
4785
|
+
}
|
|
4786
|
+
require_cmd node
|
|
4787
|
+
|
|
4240
4788
|
local project_dir
|
|
4241
4789
|
project_dir=$(normalize_project_dir "$1")
|
|
4242
4790
|
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4791
|
+
if [ "$mode" = "source" ]; then
|
|
4792
|
+
# Source-only dev fixtures: the pinned repo dependencies are build/test
|
|
4793
|
+
# fixtures, never a product promise that consumers receive them (#86 A4).
|
|
4794
|
+
require_cmd pnpm
|
|
4795
|
+
fi
|
|
4247
4796
|
|
|
4248
4797
|
# Node 24+ is the SINGLE supported axis (GLG, 2026-07-21). The floor is not
|
|
4249
4798
|
# derived from a feature gate: type-stripping alone would only demand 23.6.
|
|
@@ -4264,81 +4813,217 @@ setup_all() {
|
|
|
4264
4813
|
echo "[setup] scope: entwurf v2 package + detected native-harness bridges + pi adapter"
|
|
4265
4814
|
echo "[setup] verification: v2 install smoke (entwurf-bridge; LIVE substrate = release-gate)"
|
|
4266
4815
|
|
|
4267
|
-
# Store gate FIRST — ahead of
|
|
4268
|
-
#
|
|
4269
|
-
#
|
|
4270
|
-
#
|
|
4271
|
-
#
|
|
4272
|
-
#
|
|
4273
|
-
# runs before dependencies exist.
|
|
4816
|
+
# Store gate FIRST — ahead of the source bootstrap and every settings writer
|
|
4817
|
+
# below. On an existing development host this is the only step standing
|
|
4818
|
+
# between a `git pull` and a V3-only activation over a store the live schema
|
|
4819
|
+
# cannot read; putting it inside install_local_package would already be one
|
|
4820
|
+
# write too late. The store-doctor imports repo-local libs and node builtins
|
|
4821
|
+
# only, so it runs before dependencies exist.
|
|
4274
4822
|
preflight_v3_store setup
|
|
4275
4823
|
|
|
4276
|
-
(
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
#
|
|
4286
|
-
|
|
4824
|
+
SETUP_RESULTS=()
|
|
4825
|
+
|
|
4826
|
+
if [ "$mode" = "source" ]; then
|
|
4827
|
+
# Source bootstrap is the ONLY install-mode difference (#86): the pinned
|
|
4828
|
+
# repo dev dependencies are fixtures this checkout builds/tests with. A
|
|
4829
|
+
# broken bootstrap is a broken checkout — crash, don't degrade.
|
|
4830
|
+
(cd "$REPO_DIR" && pnpm install --frozen-lockfile)
|
|
4831
|
+
fi
|
|
4832
|
+
|
|
4833
|
+
# ── pi (presence + floor, #86 C1/C9) ── pi is OPTIONAL-BY-PRESENCE: absent is
|
|
4834
|
+
# an explicit zero-state SKIP (setup never installs pi and writes no Pi
|
|
4835
|
+
# wiring), below-floor/unreadable is detected-incomplete FAIL — never a SKIP,
|
|
4836
|
+
# so a stale pi is told the truth instead of being silently skipped. PI_BIN
|
|
4837
|
+
# pins the probe for the hermetic smoke (same seam as AGY_BIN); production
|
|
4838
|
+
# leaves it unset.
|
|
4839
|
+
local pi_bin="${PI_BIN:-pi}" pi_range pi_ver
|
|
4840
|
+
if ! command -v "$pi_bin" >/dev/null 2>&1; then
|
|
4841
|
+
setup_result pi SKIP "pi not on PATH — zero Pi wiring written (the operator installs pi; setup never does)"
|
|
4842
|
+
else
|
|
4843
|
+
pi_range=$(pi_supported_range)
|
|
4844
|
+
# Bounded capture: a resolvable pi whose --version crashes or prints garbage
|
|
4845
|
+
# must land in the computed FAIL verdict below, never abort setup via set -e
|
|
4846
|
+
# before the summary (review defect, 2026-08-26).
|
|
4847
|
+
pi_ver="$("$pi_bin" --version 2>/dev/null | head -n 1 | tr -d '[:space:]')" || pi_ver=""
|
|
4848
|
+
if ! pi_version_in_range "${pi_ver:-0}" "$pi_range"; then
|
|
4849
|
+
setup_result pi FAIL "detected pi ${pi_ver:-<unreadable version>} is outside the supported range $pi_range — Pi wiring not written; align pi, then re-run setup"
|
|
4850
|
+
elif (install_local_package "$project_dir"); then
|
|
4851
|
+
setup_result pi PASS "project + user-scope wiring complete (pi $pi_ver in $pi_range) — verify: ./run.sh doctor-pi-provider"
|
|
4852
|
+
else
|
|
4853
|
+
setup_result pi FAIL "pi wiring did not complete (see above) — repair, then re-run setup"
|
|
4854
|
+
fi
|
|
4855
|
+
fi
|
|
4856
|
+
|
|
4857
|
+
# ── claude (meta-bridge) ── single confident install command (GLG 2026-06-23):
|
|
4858
|
+
# setup ALSO wires the native-harness meta-bridge so a relocate/clone needs ONE
|
|
4859
|
+
# command. Detection-gated ("있으면 설정, 없으면 담아준다");
|
|
4860
|
+
# meta-bridge-install.sh is idempotent and enforces the Claude floor + its own
|
|
4861
|
+
# named prerequisites itself, so a below-floor or refused host lands here as a
|
|
4862
|
+
# detected FAIL, never a cosmetic green. CLAUDE_BIN pins the probe for the
|
|
4863
|
+
# hermetic smoke; production leaves it unset.
|
|
4864
|
+
local claude_bin="${CLAUDE_BIN:-claude}"
|
|
4865
|
+
if ! command -v "$claude_bin" >/dev/null 2>&1; then
|
|
4866
|
+
setup_result claude SKIP "claude not on PATH — zero meta-bridge wiring written"
|
|
4867
|
+
else
|
|
4287
4868
|
section "meta-bridge install (native harness detected: Claude Code)"
|
|
4288
|
-
(cd "$REPO_DIR" && bash scripts/meta-bridge-install.sh)
|
|
4869
|
+
if (cd "$REPO_DIR" && bash scripts/meta-bridge-install.sh); then
|
|
4870
|
+
setup_result claude PASS "meta-bridge wired — verify: ./run.sh doctor-meta-bridge"
|
|
4871
|
+
else
|
|
4872
|
+
setup_result claude FAIL "detected Claude Code, but the meta-bridge integration did not complete (see above) — repair, then re-run setup"
|
|
4873
|
+
fi
|
|
4874
|
+
fi
|
|
4875
|
+
|
|
4876
|
+
# ── stable command exposure ── source mode exposes entwurf's STABLE bins on
|
|
4877
|
+
# PATH for this DEV checkout (막힘 ②) BEFORE wiring agy: the operator command
|
|
4878
|
+
# `entwurf` that Copilot fresh resolves as its managed runtime, plus the bare
|
|
4879
|
+
# bridge/statusline/imprint names native-harness configs record. An installed
|
|
4880
|
+
# consumer already has the same commands from npm bin linking — nothing to
|
|
4881
|
+
# expose there. The core operator stays fail-loud; helper units are attempted
|
|
4882
|
+
# independently and a refused helper is a named FAIL, not a cosmetic green.
|
|
4883
|
+
local bins_rc
|
|
4884
|
+
if [ "$mode" = "installed" ]; then
|
|
4885
|
+
# PASS, not SKIP (review, 2026-08-26): the capability IS present — npm bin
|
|
4886
|
+
# linking provides the same stable commands the source lane must expose.
|
|
4887
|
+
# SKIP stays reserved for a truly absent capability with zero state.
|
|
4888
|
+
setup_result bins PASS "provided by npm bin linking (installed package needs no source exposure)"
|
|
4289
4889
|
else
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
# Fold agy (Antigravity) MCP bridge wiring into setup (막힘 ①, GLG 2026-07-04: install
|
|
4300
|
-
# ownership moves to entwurf). Same detection-gated, idempotent, NON-FATAL posture as the
|
|
4301
|
-
# meta-bridge block above — agy is an OPTIONAL harness, so a refused/corrupt agy config warns
|
|
4302
|
-
# but never bricks a pi/Claude setup. The hard gate stays doctor-agy-bridge.
|
|
4303
|
-
wire_agy_bridge
|
|
4304
|
-
|
|
4305
|
-
# Fold agy statusLine wiring into setup (#46 Task 1: own the ambient status area so it shows
|
|
4306
|
-
# driver + garden id, the claude meta-bridge symmetry). Same detection-gated NON-FATAL posture
|
|
4307
|
-
# — an explicit install-agy-statusline is fail-loud, but the setup wrapper WARNs + continues.
|
|
4308
|
-
wire_agy_statusline
|
|
4309
|
-
|
|
4310
|
-
# Fold agy PreInvocation birth imprint wiring into setup (#46 close criterion: manual register 0).
|
|
4311
|
-
# The hook runs the thin `entwurf-agy-imprint` bin and returns {"injectSteps":[]} so agy's loop
|
|
4312
|
-
# survives while the meta-record is created/attached by conversationId.
|
|
4313
|
-
wire_agy_hooks
|
|
4314
|
-
|
|
4315
|
-
# Deterministic preflight lives in `pnpm run check:full`; live substrate acceptance lives
|
|
4316
|
-
# in `LIVE=1 ./run.sh release-gate <scratch> --cut`. Setup is the install path, so it
|
|
4317
|
-
# verifies the installed MCP bridge boundary only and does NOT run the legacy
|
|
4318
|
-
# ACP/v1 session-messaging/sentinel gates.
|
|
4319
|
-
section "v2 install smoke: entwurf-bridge (direct MCP protocol)"
|
|
4320
|
-
validate_entwurf_bridge
|
|
4890
|
+
bins_rc=0; expose_dev_bin || bins_rc=$?
|
|
4891
|
+
if [ "$bins_rc" -eq 0 ]; then
|
|
4892
|
+
setup_result bins PASS "source operator + helper bins exposed and certified (PATH winner: this checkout)"
|
|
4893
|
+
elif [ "$bins_rc" -eq 3 ]; then
|
|
4894
|
+
setup_result bins FAIL "foreign helper bin(s) refused (named above; every unit was attempted) — repair them, then re-run setup"
|
|
4895
|
+
else
|
|
4896
|
+
setup_result bins FAIL "source operator command not certified (see above) — repair PATH/link, then re-run setup"
|
|
4897
|
+
fi
|
|
4898
|
+
fi
|
|
4321
4899
|
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4900
|
+
# ── agy (Antigravity) ── detection-gated (막힘 ①, GLG 2026-07-04: install
|
|
4901
|
+
# ownership moves to entwurf); AGY_BIN pins the probe for the hermetic smoke.
|
|
4902
|
+
# agy absent is one zero-state SKIP. agy PRESENT runs all three leaves
|
|
4903
|
+
# (bridge / statusLine / PreInvocation imprint) independently; a refused or
|
|
4904
|
+
# corrupt config is a named component FAIL that keeps the rest of setup alive
|
|
4905
|
+
# but owns a nonzero final result — the old WARN-then-green posture is retired
|
|
4906
|
+
# (#86 C6/C7). Hard per-leaf verdicts stay with the doctor commands.
|
|
4907
|
+
local agy_rc
|
|
4908
|
+
if ! command -v "${AGY_BIN:-agy}" >/dev/null 2>&1; then
|
|
4909
|
+
setup_result agy SKIP "agy not on PATH — zero agy wiring written"
|
|
4910
|
+
else
|
|
4911
|
+
agy_rc=0; wire_agy_bridge || agy_rc=$?
|
|
4912
|
+
if [ "$agy_rc" -eq 0 ]; then
|
|
4913
|
+
setup_result agy-bridge PASS "MCP bridge wired — verify: ./run.sh doctor-agy-bridge"
|
|
4914
|
+
else
|
|
4915
|
+
setup_result agy-bridge FAIL "detected agy, but the MCP bridge integration did not complete (reason above) — repair, then re-run setup"
|
|
4916
|
+
fi
|
|
4917
|
+
agy_rc=0; wire_agy_statusline || agy_rc=$?
|
|
4918
|
+
if [ "$agy_rc" -eq 0 ]; then
|
|
4919
|
+
setup_result agy-statusline PASS "statusLine wired — verify: ./run.sh doctor-agy-statusline"
|
|
4920
|
+
else
|
|
4921
|
+
setup_result agy-statusline FAIL "detected agy, but the statusLine integration did not complete (reason above) — repair, then re-run setup"
|
|
4922
|
+
fi
|
|
4923
|
+
agy_rc=0; wire_agy_hooks || agy_rc=$?
|
|
4924
|
+
if [ "$agy_rc" -eq 0 ]; then
|
|
4925
|
+
setup_result agy-hooks PASS "birth imprint wired — verify: ./run.sh doctor-agy-hooks"
|
|
4926
|
+
else
|
|
4927
|
+
setup_result agy-hooks FAIL "detected agy, but the birth-imprint integration did not complete (reason above) — repair, then re-run setup"
|
|
4928
|
+
fi
|
|
4326
4929
|
fi
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4930
|
+
|
|
4931
|
+
# ── copilot (GitHub Copilot CLI) ── presence-driven composition (#86 C3b):
|
|
4932
|
+
# the operator's copilot on PATH is the ONLY trigger; absent is one zero-state
|
|
4933
|
+
# SKIP (setup never installs a harness). Present runs all FOUR units the
|
|
4934
|
+
# fresh-call preflight requires — birth → MCP → receiver → visible footer —
|
|
4935
|
+
# INDEPENDENTLY: a failed unit is a named component FAIL that keeps the other
|
|
4936
|
+
# units and the rest of setup alive but owns a nonzero final result. Every
|
|
4937
|
+
# unit is an operator-selectable install with a package-owned inverse (#86
|
|
4938
|
+
# C3a closed the birth inverse), so setup composes only lifecycles it can
|
|
4939
|
+
# also undo. COPILOT_BIN pins the PROBE for the hermetic gates (same seam
|
|
4940
|
+
# spirit as PI_BIN/CLAUDE_BIN/AGY_BIN); the unit scripts address the vendor
|
|
4941
|
+
# as `copilot` on PATH, so production leaves it unset and the two agree.
|
|
4942
|
+
local copilot_rc
|
|
4943
|
+
if ! command -v "${COPILOT_BIN:-copilot}" >/dev/null 2>&1; then
|
|
4944
|
+
setup_result copilot SKIP "copilot not on PATH — zero Copilot wiring written"
|
|
4945
|
+
else
|
|
4946
|
+
section "copilot units (native harness detected: GitHub Copilot CLI)"
|
|
4947
|
+
copilot_rc=0; (cd "$REPO_DIR" && bash scripts/copilot-bridge-install.sh) || copilot_rc=$?
|
|
4948
|
+
if [ "$copilot_rc" -eq 0 ]; then
|
|
4949
|
+
setup_result copilot-birth PASS "birth plugin installed — verify: ./run.sh doctor-copilot-bridge"
|
|
4950
|
+
else
|
|
4951
|
+
setup_result copilot-birth FAIL "detected copilot, but the birth plugin install did not complete (see above) — repair, then re-run setup"
|
|
4952
|
+
fi
|
|
4953
|
+
copilot_rc=0; (cd "$REPO_DIR" && bash scripts/copilot-mcp-bridge.sh install) || copilot_rc=$?
|
|
4954
|
+
if [ "$copilot_rc" -eq 0 ]; then
|
|
4955
|
+
setup_result copilot-mcp PASS "MCP server registered — verify: ./run.sh doctor-copilot-mcp"
|
|
4956
|
+
else
|
|
4957
|
+
setup_result copilot-mcp FAIL "detected copilot, but the MCP registration did not complete (see above) — repair, then re-run setup"
|
|
4958
|
+
fi
|
|
4959
|
+
# The receiver deploys the COMPILED dist closure on every install shape
|
|
4960
|
+
# (Copilot's extension runtime cannot strip-types). The tarball ships dist;
|
|
4961
|
+
# a source checkout may not have built it yet, and those bytes are
|
|
4962
|
+
# entwurf's OWN artifact (Hard Rule 17: the source bootstrap supplies
|
|
4963
|
+
# entwurf's own bytes), so the source lane builds it here once when absent.
|
|
4964
|
+
copilot_rc=0
|
|
4965
|
+
if [ "$mode" = "source" ] && [ ! -f "$REPO_DIR/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js" ]; then
|
|
4966
|
+
(cd "$REPO_DIR" && pnpm run build-bridge) || copilot_rc=$?
|
|
4967
|
+
fi
|
|
4968
|
+
if [ "$copilot_rc" -ne 0 ]; then
|
|
4969
|
+
setup_result copilot-receive FAIL "detected copilot, but the receiver's compiled dist closure failed to build (see above) — repair, then re-run setup"
|
|
4970
|
+
else
|
|
4971
|
+
copilot_rc=0; (cd "$REPO_DIR" && bash scripts/copilot-receive-bridge.sh install) || copilot_rc=$?
|
|
4972
|
+
if [ "$copilot_rc" -eq 0 ]; then
|
|
4973
|
+
setup_result copilot-receive PASS "receiver extension installed — verify: ./run.sh doctor-copilot-receive"
|
|
4974
|
+
else
|
|
4975
|
+
setup_result copilot-receive FAIL "detected copilot, but the receiver extension install did not complete (see above) — repair, then re-run setup"
|
|
4976
|
+
fi
|
|
4977
|
+
fi
|
|
4978
|
+
copilot_rc=0; (cd "$REPO_DIR" && bash scripts/copilot-statusline-bridge.sh install) || copilot_rc=$?
|
|
4979
|
+
if [ "$copilot_rc" -eq 0 ]; then
|
|
4980
|
+
setup_result copilot-statusline PASS "visible footer wired — verify: ./run.sh doctor-copilot-statusline"
|
|
4981
|
+
else
|
|
4982
|
+
setup_result copilot-statusline FAIL "detected copilot, but the visible-footer integration did not complete (see above) — repair, then re-run setup"
|
|
4983
|
+
fi
|
|
4984
|
+
fi
|
|
4985
|
+
|
|
4986
|
+
# ── core bridge boundary ── deterministic preflight lives in `pnpm run
|
|
4987
|
+
# check:full`; live substrate acceptance lives in `LIVE=1 ./run.sh
|
|
4988
|
+
# release-gate <scratch> --cut`. Setup is the install path, so it verifies the
|
|
4989
|
+
# installed MCP bridge boundary only and does NOT run the legacy ACP/v1
|
|
4990
|
+
# session-messaging/sentinel gates.
|
|
4991
|
+
section "v2 install smoke: entwurf-bridge (direct MCP protocol)"
|
|
4992
|
+
if (validate_entwurf_bridge); then
|
|
4993
|
+
setup_result core PASS "installed bridge boots and lists the exact v2 tool set"
|
|
4994
|
+
else
|
|
4995
|
+
setup_result core FAIL "installed bridge boundary did not validate (see above)"
|
|
4996
|
+
fi
|
|
4997
|
+
|
|
4998
|
+
# ── computed summary (#86 C7/C8) ── the final verdict is DERIVED from the
|
|
4999
|
+
# component outcomes above, never printed independently of them. A detected
|
|
5000
|
+
# integration FAIL owns a nonzero exit while valid components stay installed;
|
|
5001
|
+
# re-running the same setup is the documented repair action (idempotent).
|
|
5002
|
+
section "setup summary (computed from component outcomes)"
|
|
5003
|
+
local entry rest s_name s_verdict s_fails=""
|
|
5004
|
+
for entry in "${SETUP_RESULTS[@]}"; do
|
|
5005
|
+
s_name="${entry%%|*}"; rest="${entry#*|}"; s_verdict="${rest%%|*}"
|
|
5006
|
+
printf ' %-14s %-4s %s\n' "$s_name" "$s_verdict" "${rest#*|}"
|
|
5007
|
+
if [ "$s_verdict" = "FAIL" ]; then s_fails="$s_fails $s_name"; fi
|
|
5008
|
+
done
|
|
5009
|
+
echo ""
|
|
5010
|
+
if [ -n "$s_fails" ]; then
|
|
5011
|
+
echo "DONE: entwurf setup — result: NON-GREEN (FAIL:$s_fails). Valid components above remain installed; repair the named ones and re-run setup."
|
|
5012
|
+
return 1
|
|
4331
5013
|
fi
|
|
5014
|
+
echo "DONE: entwurf setup — result: green (computed from the component outcomes above)."
|
|
4332
5015
|
echo "Run 'LIVE=1 ./run.sh release-gate <scratch> --cut' for live substrate acceptance (--cut refuses any MUST SKIP)."
|
|
4333
5016
|
}
|
|
4334
5017
|
|
|
4335
|
-
# wire_agy_bridge — detection-gated
|
|
5018
|
+
# wire_agy_bridge — detection-gated agy (Antigravity) MCP bridge wiring, folded into
|
|
4336
5019
|
# setup so a relocate/clone needs ONE idempotent command (막힘 ①). Mirrors the meta-bridge
|
|
4337
5020
|
# block: agy on PATH → idempotent install-agy-bridge; no agy → honest skip, NO state ("있으면
|
|
4338
|
-
# 설정, 없으면 담아준다").
|
|
4339
|
-
#
|
|
4340
|
-
#
|
|
4341
|
-
#
|
|
5021
|
+
# 설정, 없으면 담아준다"). A refused/corrupt agy config still never bricks the rest of setup —
|
|
5022
|
+
# later components are attempted — but it RETURNS 1 so the aggregate verdict records a named
|
|
5023
|
+
# component FAIL and setup exits non-green (#86 C6/C7: the old WARN-then-exit-0 posture was the
|
|
5024
|
+
# false-success shape). The hard per-leaf gate stays doctor-agy-bridge (issue #45). WARNs are
|
|
5025
|
+
# reason-specific so a transitional symlink (someone else's SSOT) and a corrupt config
|
|
5026
|
+
# (invalid JSON) are never conflated.
|
|
4342
5027
|
wire_agy_bridge() {
|
|
4343
5028
|
# Detection = the agy binary on PATH. AGY_BIN pins the target (default `agy`) so the
|
|
4344
5029
|
# hermetic smoke can point at a fake agy or a definitely-absent path without depending on
|
|
@@ -4359,7 +5044,8 @@ wire_agy_bridge() {
|
|
|
4359
5044
|
echo "[setup] agy bridge wired (idempotent). Verify with: ./run.sh doctor-agy-bridge"
|
|
4360
5045
|
return 0
|
|
4361
5046
|
fi
|
|
4362
|
-
#
|
|
5047
|
+
# Keep setup alive for the remaining components, surface the reason honestly, and return 1
|
|
5048
|
+
# so the aggregate verdict records this component as FAIL (never a silent pass).
|
|
4363
5049
|
# ORDER MATTERS: the permission failures carry the same words as the mcp_config ones ("refused
|
|
4364
5050
|
# (symlink)", "invalid JSON") and MUST be matched first — otherwise a settings.json problem gets
|
|
4365
5051
|
# reported as an mcp_config problem and sends the operator to repair the wrong file.
|
|
@@ -4388,14 +5074,14 @@ wire_agy_bridge() {
|
|
|
4388
5074
|
echo "[setup] Bridge NOT wired; verify with ./run.sh doctor-agy-bridge. setup continues." >&2
|
|
4389
5075
|
;;
|
|
4390
5076
|
esac
|
|
4391
|
-
return
|
|
5077
|
+
return 1
|
|
4392
5078
|
}
|
|
4393
5079
|
|
|
4394
|
-
# wire_agy_statusline — detection-gated
|
|
5080
|
+
# wire_agy_statusline — detection-gated agy statusLine wiring, folded into setup (#46
|
|
4395
5081
|
# Task 1). Mirrors wire_agy_bridge: agy on PATH → idempotent install-agy-statusline; no agy →
|
|
4396
|
-
# honest skip, NO state
|
|
4397
|
-
#
|
|
4398
|
-
# stable bin entwurf-agy-statusline, exposed by expose_dev_bin above.
|
|
5082
|
+
# honest skip, NO state; a refused/corrupt settings keeps the rest of setup alive but returns 1
|
|
5083
|
+
# for a named component FAIL (#86 C6/C7). The hard gate is doctor-agy-statusline. The renderer
|
|
5084
|
+
# is the stable bin entwurf-agy-statusline, exposed by expose_dev_bin above.
|
|
4399
5085
|
wire_agy_statusline() {
|
|
4400
5086
|
if ! command -v "${AGY_BIN:-agy}" >/dev/null 2>&1; then
|
|
4401
5087
|
echo "[setup] no agy on PATH — skipping agy statusLine wiring (no state; this host runs no Antigravity)"
|
|
@@ -4428,12 +5114,13 @@ wire_agy_statusline() {
|
|
|
4428
5114
|
echo "[setup] statusLine NOT wired; verify with ./run.sh doctor-agy-statusline. setup continues." >&2
|
|
4429
5115
|
;;
|
|
4430
5116
|
esac
|
|
4431
|
-
return
|
|
5117
|
+
return 1
|
|
4432
5118
|
}
|
|
4433
5119
|
|
|
4434
|
-
# wire_agy_hooks — detection-gated
|
|
4435
|
-
# as the MCP/statusLine adapters
|
|
4436
|
-
# garden id after the first
|
|
5120
|
+
# wire_agy_hooks — detection-gated agy PreInvocation imprint wiring. Same setup posture
|
|
5121
|
+
# as the MCP/statusLine adapters (skip on absence, named FAIL on detected incompleteness), but
|
|
5122
|
+
# this one is the birth writer that turns statusLine '?' into a garden id after the first
|
|
5123
|
+
# invocation.
|
|
4437
5124
|
wire_agy_hooks() {
|
|
4438
5125
|
if ! command -v "${AGY_BIN:-agy}" >/dev/null 2>&1; then
|
|
4439
5126
|
echo "[setup] no agy on PATH — skipping agy hooks wiring (no state; this host runs no Antigravity)"
|
|
@@ -4464,30 +5151,39 @@ wire_agy_hooks() {
|
|
|
4464
5151
|
echo "[setup] Birth imprint NOT wired; verify with ./run.sh doctor-agy-hooks. setup continues." >&2
|
|
4465
5152
|
;;
|
|
4466
5153
|
esac
|
|
4467
|
-
return
|
|
5154
|
+
return 1
|
|
4468
5155
|
}
|
|
4469
5156
|
|
|
4470
|
-
# expose_dev_bin — make the
|
|
4471
|
-
# setup IS the dev install command (consumers get
|
|
4472
|
-
# managed ~/.local/bin
|
|
4473
|
-
#
|
|
4474
|
-
#
|
|
4475
|
-
#
|
|
5157
|
+
# expose_dev_bin — make the package's stable commands resolve for a DEV checkout (막힘 ②).
|
|
5158
|
+
# setup IS the dev install command (consumers get these names from npm bin-linking), so setup owns
|
|
5159
|
+
# managed ~/.local/bin symlinks into this checkout via scripts/dev-bin.sh, each recorded for an
|
|
5160
|
+
# honest inverse. The set starts with `entwurf` → run.sh: Copilot fresh deliberately launches the
|
|
5161
|
+
# managed `entwurf copilot`, never the raw vendor. That operator command is CORE and is certified
|
|
5162
|
+
# after exposure by dev-bin.sh's own target authority: our symlink, our target, and the exact PATH
|
|
5163
|
+
# winner. A foreign/off-PATH/shadowed operator therefore fails setup (return 1). Helper units are
|
|
5164
|
+
# attempted INDEPENDENTLY by dev-bin.sh (#86 C10) and any refusal propagates as return 3 — the
|
|
5165
|
+
# aggregate verdict records it as a named FAIL instead of the old WARN-then-green; the refused
|
|
5166
|
+
# names are already printed truthfully by dev-bin's attempted/ok/refused summary.
|
|
4476
5167
|
expose_dev_bin() {
|
|
4477
|
-
section "dev
|
|
5168
|
+
section "dev bins: expose stable package commands on PATH (dev checkout)"
|
|
4478
5169
|
local rc
|
|
4479
5170
|
set +e
|
|
4480
5171
|
bash "$REPO_DIR/scripts/dev-bin.sh" expose
|
|
4481
5172
|
rc=$?
|
|
4482
5173
|
set -e
|
|
5174
|
+
|
|
5175
|
+
if ! bash "$REPO_DIR/scripts/dev-bin.sh" verify entwurf; then
|
|
5176
|
+
return 1
|
|
5177
|
+
fi
|
|
5178
|
+
|
|
4483
5179
|
[ "$rc" -eq 0 ] && return 0
|
|
4484
5180
|
if [ "$rc" -eq 3 ]; then
|
|
4485
|
-
echo "[setup]
|
|
4486
|
-
echo "[setup]
|
|
5181
|
+
echo "[setup] dev-bin: foreign helper bin path(s) refused, never clobbered — the refused names are in the summary above." >&2
|
|
5182
|
+
echo "[setup] Every unit was attempted independently; the source operator itself is certified." >&2
|
|
4487
5183
|
else
|
|
4488
|
-
echo "[setup]
|
|
5184
|
+
echo "[setup] dev-bin: helper exposure did not complete (rc=$rc; see above)." >&2
|
|
4489
5185
|
fi
|
|
4490
|
-
return
|
|
5186
|
+
return "$rc"
|
|
4491
5187
|
}
|
|
4492
5188
|
|
|
4493
5189
|
# release-gate — the single command that, when GREEN, is sufficient to cut
|
|
@@ -4775,6 +5471,21 @@ release_gate() {
|
|
|
4775
5471
|
run_live_step "smoke-claude-native-resume-live (native Claude Code resume + meta-bridge neutrality)" gate bash "$self" smoke-claude-native-resume-live
|
|
4776
5472
|
run_live_step "smoke-entwurf-chain-live (P3: Claude Code -> pi GPT -> pi ACP Sonnet -> mailbox, identity + receipt)" gate bash "$self" smoke-entwurf-chain-live
|
|
4777
5473
|
run_live_step "smoke-mux-lifecycle-live (mux public-harness lifecycle: fresh -> send -> dormant -> same-id visible resume -> recall)" gate bash "$self" smoke-mux-lifecycle-live
|
|
5474
|
+
# #87 bundle B: MUST, and since bundle B moved omp's wakeMode to self-fetch this is a
|
|
5475
|
+
# REAL demand under LIVE=1, not a standing SKIP — it reads the registry, so an honest
|
|
5476
|
+
# SKIP now only ever names a missing prerequisite, and a registry claiming a receive
|
|
5477
|
+
# rail with no acceptance body here is a FAIL.
|
|
5478
|
+
run_live_step "smoke-omp-receive-live (#87 bundle B: other harness -> open omp citizen, addressed receive + roundtrip)" gate bash "$self" smoke-omp-receive-live
|
|
5479
|
+
# #87 bundle C: the step 9 clause 7 receipt, wired as a MUST rather than left to an
|
|
5480
|
+
# operator's memory. A schema is not a product — omp's bootstrap-payload submission
|
|
5481
|
+
# PARSES correctly, and parsing is exactly what pi got right while submitting no message
|
|
5482
|
+
# at all, so a release whose fresh_call(omp) opens a window that never runs its turn
|
|
5483
|
+
# would pass every deterministic gate here. That is not hypothetical for this backend:
|
|
5484
|
+
# the retired positional candidate parsed perfectly and still answered `ACK` with zero
|
|
5485
|
+
# tool calls, because the turn began before the callback tool existed. New contract,
|
|
5486
|
+
# applied from omp onward; it does not retroactively redesign Copilot's operator-metered
|
|
5487
|
+
# exclusion.
|
|
5488
|
+
run_live_step "smoke-omp-fresh-live (#87 bundle C: entwurf_fresh_call opens omp, exact nonce callback, addressed receive)" gate bash "$self" smoke-omp-fresh-live
|
|
4778
5489
|
|
|
4779
5490
|
# 4. BEHAVIOR lane (advisory, non-blocking). Model-in-loop gates that probe
|
|
4780
5491
|
# whether the model AUTONOMOUSLY drives the MCP entwurf surface. These never
|
|
@@ -4888,6 +5599,15 @@ case "$cmd" in
|
|
|
4888
5599
|
check-entwurf-capabilities)
|
|
4889
5600
|
check_entwurf_capabilities
|
|
4890
5601
|
;;
|
|
5602
|
+
check-harness-admission-parity)
|
|
5603
|
+
check_harness_admission_parity
|
|
5604
|
+
;;
|
|
5605
|
+
check-omp-fresh-preflight)
|
|
5606
|
+
check_omp_fresh_preflight
|
|
5607
|
+
;;
|
|
5608
|
+
smoke-omp-fresh-live)
|
|
5609
|
+
smoke_omp_fresh_live
|
|
5610
|
+
;;
|
|
4891
5611
|
check-capability-bundle-reach)
|
|
4892
5612
|
check_capability_bundle_reach
|
|
4893
5613
|
;;
|
|
@@ -4912,6 +5632,18 @@ case "$cmd" in
|
|
|
4912
5632
|
check-copilot-birth-hook)
|
|
4913
5633
|
check_copilot_birth_hook
|
|
4914
5634
|
;;
|
|
5635
|
+
check-omp-birth-hook)
|
|
5636
|
+
# #87 gate: the OMP BIRTH path without omp. Drives the real assembler into a temp dir,
|
|
5637
|
+
# then imports the ASSEMBLED index.ts into a mock omp host (mock ExtensionAPI + mock
|
|
5638
|
+
# ExtensionContext) and fires the two birth edges. Requires: a backend:"omp" v3 record
|
|
5639
|
+
# only under mode "tui"; NOTHING under print/rpc/json (a task subagent is not a citizen,
|
|
5640
|
+
# and hasUI is deliberately true on the rpc rows because it is true in the vendor too);
|
|
5641
|
+
# session_switch attaching on the same native id and minting the replacement on a new
|
|
5642
|
+
# one; a sender marker keyed to the HOST process's OWN pid (never its parent — the
|
|
5643
|
+
# one-process join) that the production resolver joins back to the record; the garden id
|
|
5644
|
+
# on the status line; and still zero mailbox/receiver marker. Hermetic; no omp, no model turn
|
|
5645
|
+
run_ts scripts/check-omp-birth-hook.ts
|
|
5646
|
+
;;
|
|
4915
5647
|
check-copilot-statusline)
|
|
4916
5648
|
check_copilot_statusline
|
|
4917
5649
|
;;
|
|
@@ -5044,6 +5776,12 @@ case "$cmd" in
|
|
|
5044
5776
|
smoke-acp-bundled-mcp-live)
|
|
5045
5777
|
smoke_acp_bundled_mcp_live
|
|
5046
5778
|
;;
|
|
5779
|
+
smoke-omp-receive-live)
|
|
5780
|
+
# #87 bundle B — the acceptance that makes omp's one-way garden executable.
|
|
5781
|
+
# Reads the capability registry and reports its own PASS/SKIP/FAIL; see the
|
|
5782
|
+
# header of the script for the three branches.
|
|
5783
|
+
run_ts scripts/smoke-omp-receive-live.ts
|
|
5784
|
+
;;
|
|
5047
5785
|
smoke-entwurf-chain-live)
|
|
5048
5786
|
# P3 — the cross-harness delivery CHAIN on real authenticated rails:
|
|
5049
5787
|
# native Claude Code -> pi GPT -> pi ACP Claude Sonnet -> mailbox terminus,
|
|
@@ -5181,6 +5919,16 @@ case "$cmd" in
|
|
|
5181
5919
|
# Offline + deterministic (deps: bash+python3).
|
|
5182
5920
|
(cd "$REPO_DIR" && bash scripts/smoke-agy-install-state.sh)
|
|
5183
5921
|
;;
|
|
5922
|
+
smoke-setup-verdict)
|
|
5923
|
+
# #86 C1 aggregate setup verdict fixture — the first automated consumer of
|
|
5924
|
+
# `entwurf setup` itself: all-absent SKIP/green, pi below-floor detected FAIL
|
|
5925
|
+
# (never SKIP), pi-present wiring PASS, agy detected+corrupt named FAIL with
|
|
5926
|
+
# NON-GREEN nonzero exit, installed-mode named branch before the bootstrap,
|
|
5927
|
+
# and the credential store byte-identical in every cell. Sandboxed HOME/XDG/
|
|
5928
|
+
# agent/dev-bin roots; presence pinned via PI_BIN/CLAUDE_BIN/AGY_BIN.
|
|
5929
|
+
# Offline + deterministic (deps: bash+node+python3+pnpm).
|
|
5930
|
+
(cd "$REPO_DIR" && bash scripts/smoke-setup-verdict.sh)
|
|
5931
|
+
;;
|
|
5184
5932
|
check-agy-permission-matrix)
|
|
5185
5933
|
# The agy permission engine's CONTRACT SPACE as a literal table (55 cells + stated
|
|
5186
5934
|
# exclusions R2/R4-R7): parser-state × operation × settings × ownership × precedence.
|
|
@@ -5237,8 +5985,8 @@ case "$cmd" in
|
|
|
5237
5985
|
# own the statusLine subtree WHOLE (preserve unrelated keys) + state (stable command, prior
|
|
5238
5986
|
# subtree as preimage), doctor static-clean/live-SKIP (+live-consistent with a fake agy) /
|
|
5239
5987
|
# drift-FAIL / dangling-command-FAIL / not-ours note, honest-inverse uninstall, symlink +
|
|
5240
|
-
# dangling-symlink refuse, create-new inverse, the
|
|
5241
|
-
# impurity 0. Offline + deterministic (deps: bash+python3).
|
|
5988
|
+
# dangling-symlink refuse, create-new inverse, the truthful wire wrapper (detected refusal
|
|
5989
|
+
# → nonzero, #86 C6/C7), and checkout impurity 0. Offline + deterministic (deps: bash+python3).
|
|
5242
5990
|
(cd "$REPO_DIR" && bash scripts/smoke-agy-statusline-state.sh)
|
|
5243
5991
|
;;
|
|
5244
5992
|
smoke-agy-hooks-state)
|
|
@@ -5268,8 +6016,10 @@ case "$cmd" in
|
|
|
5268
6016
|
# 0.12.6 install-boundary gate: register-pi-package.py is the shared
|
|
5269
6017
|
# packages[] SSOT for project/user install and remove; user scope makes
|
|
5270
6018
|
# --entwurf-control load from any cwd. Idempotent, preserves unrelated
|
|
5271
|
-
# packages/keys,
|
|
5272
|
-
#
|
|
6019
|
+
# packages/keys, remove is symmetric, corrupt settings fail loud; project
|
|
6020
|
+
# scope normalizes ITS OWN stale entries while user scope carries the #86 C2
|
|
6021
|
+
# explicit ownership contract (other owners refuse; takeover-user-scope moves).
|
|
6022
|
+
# The tripwire the 2026-07-03 `pi install` removal lacked.
|
|
5273
6023
|
(cd "$REPO_DIR" && bash scripts/smoke-user-scope-citizen.sh)
|
|
5274
6024
|
;;
|
|
5275
6025
|
smoke-claude-native-resume-live)
|
|
@@ -5315,6 +6065,15 @@ case "$cmd" in
|
|
|
5315
6065
|
shift || true
|
|
5316
6066
|
(cd "$REPO_DIR" && bash scripts/copilot-bridge-install.sh "$@")
|
|
5317
6067
|
;;
|
|
6068
|
+
uninstall-copilot-bridge)
|
|
6069
|
+
# #86 C3a: the package-owned inverse of install-copilot-bridge. The ownership state
|
|
6070
|
+
# is the sole removal authority (exact qualified id, exact marketplace name+path,
|
|
6071
|
+
# recorded assembly only); a failing vendor list is UNKNOWN and refuses; state is
|
|
6072
|
+
# deleted LAST so any partial failure keeps a rerun-repair authority. Never touches
|
|
6073
|
+
# the stale Claude unit and never uses --force.
|
|
6074
|
+
shift || true
|
|
6075
|
+
(cd "$REPO_DIR" && bash scripts/copilot-bridge-uninstall.sh "$@")
|
|
6076
|
+
;;
|
|
5318
6077
|
doctor-copilot-bridge)
|
|
5319
6078
|
# #82: the fail-loud surface for the Copilot unit. Its red conditions differ from
|
|
5320
6079
|
# the Claude doctor's on purpose: a Copilot session mints on its FIRST PROMPT, not
|
|
@@ -5347,6 +6106,102 @@ case "$cmd" in
|
|
|
5347
6106
|
smoke-copilot-mcp-state)
|
|
5348
6107
|
(cd "$REPO_DIR" && bash scripts/smoke-copilot-mcp-state.sh)
|
|
5349
6108
|
;;
|
|
6109
|
+
smoke-omp-bridge-state)
|
|
6110
|
+
(cd "$REPO_DIR" && bash scripts/smoke-omp-bridge-state.sh)
|
|
6111
|
+
;;
|
|
6112
|
+
install-omp-mcp)
|
|
6113
|
+
(cd "$REPO_DIR" && bash scripts/omp-mcp-bridge.sh install "$@")
|
|
6114
|
+
;;
|
|
6115
|
+
uninstall-omp-mcp)
|
|
6116
|
+
(cd "$REPO_DIR" && bash scripts/omp-mcp-bridge.sh uninstall "$@")
|
|
6117
|
+
;;
|
|
6118
|
+
doctor-omp-mcp)
|
|
6119
|
+
(cd "$REPO_DIR" && bash scripts/omp-mcp-bridge.sh doctor "$@")
|
|
6120
|
+
;;
|
|
6121
|
+
smoke-omp-mcp-state)
|
|
6122
|
+
(cd "$REPO_DIR" && bash scripts/smoke-omp-mcp-state.sh)
|
|
6123
|
+
;;
|
|
6124
|
+
install-omp-bridge)
|
|
6125
|
+
# #87: the OMP BIRTH install. Not a mode of the Claude or Copilot installer, and for a
|
|
6126
|
+
# structural reason rather than a stylistic one: an omp "hook" IS an in-process
|
|
6127
|
+
# extension, so there is no launcher to bake a node path into and no hook manifest to
|
|
6128
|
+
# declare events in — the vendor imports the module itself. What this owns is one
|
|
6129
|
+
# directory under the omp agent dir; the MCP hand is install-omp-mcp.
|
|
6130
|
+
shift || true
|
|
6131
|
+
(cd "$REPO_DIR" && bash scripts/omp-bridge-install.sh "$@")
|
|
6132
|
+
;;
|
|
6133
|
+
uninstall-omp-bridge)
|
|
6134
|
+
# The package-owned inverse: the ownership state is the sole removal authority (exact
|
|
6135
|
+
# unit dir, recorded entry name, recorded assembly), a no-state host REFUSES instead of
|
|
6136
|
+
# cleaning up what it cannot prove is ours, and the state is deleted LAST.
|
|
6137
|
+
shift || true
|
|
6138
|
+
(cd "$REPO_DIR" && bash scripts/omp-bridge-uninstall.sh "$@")
|
|
6139
|
+
;;
|
|
6140
|
+
doctor-omp-bridge)
|
|
6141
|
+
# #87: the fail-loud surface for the OMP unit. Red is a unit omp cannot import, a STALE
|
|
6142
|
+
# deployed writer, an unrecovered mint ERROR, a failed sender-marker write, or a live omp
|
|
6143
|
+
# process carrying inherited PI_SESSION_ID/PI_AGENT_ID. "Installed with zero records" is
|
|
6144
|
+
# NOT-YET, not red.
|
|
6145
|
+
shift || true
|
|
6146
|
+
(cd "$REPO_DIR" && bash scripts/omp-bridge-doctor.sh "$@")
|
|
6147
|
+
;;
|
|
6148
|
+
install-omp-receive)
|
|
6149
|
+
# #87 bundle B: install the OMP RECEIVER extension into
|
|
6150
|
+
# <omp agent dir>/extensions/entwurf-receive-omp. Its own unit, install-state, doctor and
|
|
6151
|
+
# inverse — birth says who sends, this says a reply can land, and neither grants the other.
|
|
6152
|
+
# Same ownership discipline as the birth installer: no adoption of an artifact entwurf holds
|
|
6153
|
+
# no state for, no writing through a symlink, refusal when the agent dir is ambiguous.
|
|
6154
|
+
shift || true
|
|
6155
|
+
(cd "$REPO_DIR" && bash scripts/omp-receive-install.sh "$@")
|
|
6156
|
+
;;
|
|
6157
|
+
uninstall-omp-receive)
|
|
6158
|
+
# honest inverse from install-state (exact unit dir + recorded entry; no-state host REFUSES;
|
|
6159
|
+
# state deleted LAST). Records, sender identity and any ALREADY-armed live session are
|
|
6160
|
+
# untouched — an omp TUI that armed before this ran stays addressable until it exits.
|
|
6161
|
+
shift || true
|
|
6162
|
+
(cd "$REPO_DIR" && bash scripts/omp-receive-uninstall.sh "$@")
|
|
6163
|
+
;;
|
|
6164
|
+
doctor-omp-receive)
|
|
6165
|
+
# #87 bundle B: runtime axis (importable unit, writer parity, arm vs doorbell failures on
|
|
6166
|
+
# SEPARATE axes, and WHO IS ARMED right now read through the PRODUCTION marker reader via
|
|
6167
|
+
# omp-receive-facts — never a filename) + ownership axis. Zero armed receivers is NOT-YET.
|
|
6168
|
+
# It reports a marker for exactly what a marker proves: a live owner reached the arm emit.
|
|
6169
|
+
# It never claims the vendor's watch is still registered — see the header.
|
|
6170
|
+
shift || true
|
|
6171
|
+
(cd "$REPO_DIR" && bash scripts/omp-receive-doctor.sh "$@")
|
|
6172
|
+
;;
|
|
6173
|
+
smoke-omp-receive-state)
|
|
6174
|
+
# #87 bundle B: OMP RECEIVER install/doctor/inverse regression — placement, stale-writer
|
|
6175
|
+
# detection, honest inverse, no-state refusal (structurally VALID as well as foreign),
|
|
6176
|
+
# symlink refusal, ambiguous-agent-dir refusal, a poisoned PI_CODING_AGENT_DIR that
|
|
6177
|
+
# attracts no artifact, plus the two this surface owns: installing a doorbell ARMS
|
|
6178
|
+
# NOTHING, and a host with no birth unit is a green doctor with a NAMED dependency note
|
|
6179
|
+
# rather than a red one. Fully sandboxed HOME/PI/XDG. Offline/deterministic
|
|
6180
|
+
shift || true
|
|
6181
|
+
(cd "$REPO_DIR" && bash scripts/smoke-omp-receive-state.sh "$@")
|
|
6182
|
+
;;
|
|
6183
|
+
check-omp-receive-arm)
|
|
6184
|
+
# #87 bundle B gate: drives the REAL receive assembler into a temp dir, imports the
|
|
6185
|
+
# ASSEMBLED index.ts into a MOCK omp host and proves everything on entwurf's side of the
|
|
6186
|
+
# vendor boundary — the tui-only scope fence, deferral before birth and the BOUNDED retry
|
|
6187
|
+
# that arms once the sender marker appears, the refusal to start a timer this build cannot
|
|
6188
|
+
# cancel, the refusal to arm when the wake surface is missing, id-drift refusal, the /new
|
|
6189
|
+
# unarm that the start-key guard structurally cannot catch, no-watch-no-marker ordering,
|
|
6190
|
+
# an announce-only doorbell that never carries the body, a vanished signal giving the
|
|
6191
|
+
# marker back, and an identity-guarded teardown. Hermetic; no omp, no model turn. What it
|
|
6192
|
+
# deliberately cannot prove — that the vendor really wakes an idle host — is
|
|
6193
|
+
# smoke-omp-receive-live's job, and neither receipt substitutes for the other
|
|
6194
|
+
shift || true
|
|
6195
|
+
run_ts scripts/check-omp-receive-arm.ts "$@"
|
|
6196
|
+
;;
|
|
6197
|
+
omp-receive-facts)
|
|
6198
|
+
# #87 bundle B: read-only JSON projection of the omp receive rail — every receiver marker
|
|
6199
|
+
# read through readMetaReceiverMarker in BOTH readings (live / as-written), plus the mailbox
|
|
6200
|
+
# counts behind each. The shared oracle for doctor-omp-receive, the state smoke and the LIVE
|
|
6201
|
+
# acceptance, so none of them answers "is a doorbell held?" from a filename.
|
|
6202
|
+
shift || true
|
|
6203
|
+
run_ts scripts/omp-receive-facts.ts "$@"
|
|
6204
|
+
;;
|
|
5350
6205
|
copilot)
|
|
5351
6206
|
# #82 RAIL 7: the managed launch. `exec` and NO subshell/cd on purpose — the vendor
|
|
5352
6207
|
# must inherit this terminal exactly: the caller's cwd, this pid, this tty, and its own
|
|
@@ -5400,11 +6255,12 @@ case "$cmd" in
|
|
|
5400
6255
|
(cd "$REPO_DIR" && bash scripts/agy-bridge.sh doctor "$@")
|
|
5401
6256
|
;;
|
|
5402
6257
|
wire-agy-bridge)
|
|
5403
|
-
# 막힘 ①: the detection-gated,
|
|
5404
|
-
# PATH → idempotent install; no agy → honest skip, no state
|
|
5405
|
-
#
|
|
5406
|
-
#
|
|
5407
|
-
#
|
|
6258
|
+
# 막힘 ①: the detection-gated, truthful setup wrapper around install-agy-bridge (agy on
|
|
6259
|
+
# PATH → idempotent install; no agy → honest skip, no state; detected refusal/corrupt →
|
|
6260
|
+
# reason-specific WARN + nonzero so the aggregate records a named component FAIL while later
|
|
6261
|
+
# components stay attempted, #86 C6/C7). HIDDEN/internal — setup calls this; it is exposed
|
|
6262
|
+
# as a subcommand only so smoke-agy-install-state can drive it deterministically. The hard
|
|
6263
|
+
# per-leaf gate stays doctor-agy-bridge (issue #45).
|
|
5408
6264
|
wire_agy_bridge
|
|
5409
6265
|
;;
|
|
5410
6266
|
install-agy-statusline)
|
|
@@ -5472,9 +6328,10 @@ case "$cmd" in
|
|
|
5472
6328
|
run_ts scripts/doctor-pi-provider.ts "$@"
|
|
5473
6329
|
;;
|
|
5474
6330
|
wire-agy-statusline)
|
|
5475
|
-
# #46 Task 1: the detection-gated,
|
|
5476
|
-
#
|
|
5477
|
-
#
|
|
6331
|
+
# #46 Task 1: the detection-gated, truthful setup wrapper around install-agy-statusline
|
|
6332
|
+
# (detected refusal/corrupt → WARN + nonzero, named component FAIL in the aggregate, #86
|
|
6333
|
+
# C6/C7). HIDDEN/internal — setup calls this; exposed so smoke-agy-statusline-state can
|
|
6334
|
+
# drive it deterministically. The hard per-leaf gate stays doctor-agy-statusline.
|
|
5478
6335
|
wire_agy_statusline
|
|
5479
6336
|
;;
|
|
5480
6337
|
wire-agy-hooks)
|
|
@@ -5482,14 +6339,16 @@ case "$cmd" in
|
|
|
5482
6339
|
wire_agy_hooks
|
|
5483
6340
|
;;
|
|
5484
6341
|
expose-dev-bin)
|
|
5485
|
-
# 막힘 ②: expose the
|
|
5486
|
-
#
|
|
5487
|
-
#
|
|
5488
|
-
#
|
|
6342
|
+
# 막힘 ②: expose the stable package commands on PATH for a DEV checkout, including the
|
|
6343
|
+
# `entwurf` operator command Copilot fresh resolves. HIDDEN/internal — setup requires the
|
|
6344
|
+
# core operator certification and attempts every helper unit independently; a foreign helper
|
|
6345
|
+
# propagates rc=3 as a named bins FAIL (#86 C10). Exposed here so the install smoke can
|
|
6346
|
+
# drive ownership/refusal.
|
|
6347
|
+
# The exposure logic lives in scripts/dev-bin.sh.
|
|
5489
6348
|
expose_dev_bin
|
|
5490
6349
|
;;
|
|
5491
6350
|
remove-dev-bin)
|
|
5492
|
-
# 막힘 ②: honest inverse of expose-dev-bin — remove ONLY our managed
|
|
6351
|
+
# 막힘 ②: honest inverse of expose-dev-bin — remove ONLY our managed links + states (REFUSE if
|
|
5493
6352
|
# it became foreign). The raw script (no wrapper) so an operator sees a loud failure.
|
|
5494
6353
|
shift || true
|
|
5495
6354
|
(cd "$REPO_DIR" && bash scripts/dev-bin.sh remove "$@")
|
|
@@ -5647,9 +6506,6 @@ case "$cmd" in
|
|
|
5647
6506
|
# from under node_modules rather than re-packing the installed copy.
|
|
5648
6507
|
(cd "$REPO_DIR" && bash scripts/check-install-container.sh "$@")
|
|
5649
6508
|
;;
|
|
5650
|
-
sync-auth)
|
|
5651
|
-
sync_auth
|
|
5652
|
-
;;
|
|
5653
6509
|
install)
|
|
5654
6510
|
install_local_package "$TARGET_PROJECT_DIR"
|
|
5655
6511
|
;;
|
|
@@ -5659,6 +6515,17 @@ case "$cmd" in
|
|
|
5659
6515
|
remove-user-scope)
|
|
5660
6516
|
remove_user_scope_citizen
|
|
5661
6517
|
;;
|
|
6518
|
+
takeover-user-scope)
|
|
6519
|
+
# #86 C2: operator-explicit ownership move — the ONLY writer allowed to replace
|
|
6520
|
+
# another root's user-scope registration (normal install/setup/remove refuse).
|
|
6521
|
+
takeover_user_scope
|
|
6522
|
+
;;
|
|
6523
|
+
doctor-pi-package)
|
|
6524
|
+
# #86 C2: package-side ownership verdict (unregistered/owned/owned-by-other/
|
|
6525
|
+
# legacy-no-state/mismatch/missing-owner). Provider runtime stays with
|
|
6526
|
+
# doctor-pi-provider — the two doctors are deliberately not mixed.
|
|
6527
|
+
doctor_pi_package
|
|
6528
|
+
;;
|
|
5662
6529
|
-h|--help|help|"")
|
|
5663
6530
|
usage
|
|
5664
6531
|
;;
|