@junghanacs/entwurf 0.19.0 → 0.20.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -1
- package/BASELINE.md +23 -5
- package/CHANGELOG.md +261 -0
- package/DELIVERY.md +59 -6
- package/README.md +41 -9
- package/VERIFY.md +9 -1
- package/docs/acp-backend-rail.md +41 -16
- package/docs/external-mcp-host.md +10 -5
- package/docs/fresh-cut-policy.md +3 -2
- package/docs/setup-clean-host.md +93 -7
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/backend-adapter.js +2 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/models.js +3 -3
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/overlay.js +6 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/tool-surface.js +6 -0
- package/package.json +3 -3
- package/pi-extensions/acp-provider.ts +1 -1
- package/pi-extensions/lib/acp/acp-client.ts +3 -3
- package/pi-extensions/lib/acp/backend-adapter.ts +3 -3
- package/pi-extensions/lib/acp/backend.ts +3 -3
- package/pi-extensions/lib/acp/event-mapper.ts +4 -4
- package/pi-extensions/lib/acp/models.ts +3 -3
- package/pi-extensions/lib/acp/overlay.ts +6 -2
- package/pi-extensions/lib/acp/tool-surface.ts +6 -0
- package/pi-extensions/lib/meta-session.ts +25 -0
- package/run.sh +104 -33
- package/scripts/agy-bridge.sh +1 -1
- package/scripts/agy-imprint.sh +10 -7
- package/scripts/check-acp-carrier-augment.ts +6 -5
- package/scripts/check-acp-cortex.ts +41 -0
- package/scripts/check-acp-provider-surface.ts +9 -6
- package/scripts/check-acp-usage-accounting.ts +9 -9
- package/scripts/check-copilot-birth-hook.ts +3 -1
- package/scripts/check-copilot-receive-arm.ts +169 -21
- package/scripts/check-copilot-statusline.ts +3 -1
- package/scripts/check-gate-qualification.ts +11 -10
- package/scripts/check-hook-launch-topology.ts +6 -5
- package/scripts/check-install-surface.ts +25 -8
- package/scripts/check-meta-facts.ts +6 -4
- package/scripts/check-meta-hook-session-switch.ts +4 -3
- package/scripts/check-meta-identity-consumers.ts +8 -7
- package/scripts/check-meta-receiver-marker.ts +2 -1
- package/scripts/check-omp-birth-hook.ts +3 -2
- package/scripts/check-probe-bridge-command.ts +3 -1
- package/scripts/check-setup-qualification.sh +21 -0
- package/scripts/copilot-bridge-doctor.sh +15 -5
- package/scripts/copilot-bridge-install.sh +6 -2
- package/scripts/copilot-launch.sh +16 -2
- package/scripts/copilot-receive-bridge.sh +80 -13
- package/scripts/lib/reclaim-on-exit.ts +86 -0
- package/scripts/meta-bridge-doctor.sh +56 -15
- package/scripts/meta-bridge-install.sh +13 -7
- package/scripts/meta-bridge-uninstall.sh +6 -4
- package/scripts/mutants/acp-cortex.json +26 -2
- package/scripts/mutants/copilot-receive.json +26 -0
- package/scripts/mutants/omp-birth.json +23 -6
- package/scripts/mutants/pi-package-ownership.json +26 -0
- package/scripts/mutants/setup-verdict.json +13 -0
- package/scripts/omp-bridge-doctor.sh +110 -41
- package/scripts/omp-bridge-install.sh +6 -2
- package/scripts/omp-receive-install.sh +7 -2
- package/scripts/pi_settings_io.py +3 -1
- package/scripts/raw-async-delivery/README.md +10 -1
- package/scripts/raw-codex-measure/README.md +689 -0
- package/scripts/raw-codex-measure/source-audit.md +243 -0
- package/scripts/raw-macos-measure/README.md +148 -0
- package/scripts/raw-macos-measure/probe.sh +389 -0
- package/scripts/register-pi-package.py +38 -30
- package/scripts/register-pi-provider.py +3 -2
- package/scripts/smoke-acp-raw-turn-live.ts +1 -1
- package/scripts/smoke-meta-async-drift.sh +7 -5
- package/scripts/smoke-meta-install-state.sh +70 -19
- package/scripts/smoke-omp-bridge-state.sh +38 -0
- package/scripts/smoke-setup-verdict.sh +100 -0
- package/scripts/smoke-user-scope-citizen.sh +199 -71
|
@@ -962,31 +962,82 @@ else
|
|
|
962
962
|
bad "doctor did not surface enabled-but-cache-miss as a hard load failure:"$'\n'"$DOC_CACHE_OUT"
|
|
963
963
|
fi
|
|
964
964
|
|
|
965
|
-
#
|
|
966
|
-
#
|
|
967
|
-
#
|
|
968
|
-
#
|
|
969
|
-
#
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
965
|
+
# 0.20.0 D4: the platform fence moved from a bare `uname -s` refusal to the
|
|
966
|
+
# installer's own REAL dependency (python3; see meta-bridge-install.sh's platform
|
|
967
|
+
# gate comment). This gate must therefore pin the NEW contract, not the retired one:
|
|
968
|
+
# (a) Darwin install no longer refuses on platform name — it reaches PAST the
|
|
969
|
+
# platform line to its own toolchain gate, and dies there ONLY when a real
|
|
970
|
+
# dependency is actually missing;
|
|
971
|
+
# (b) with the real dependency present, Darwin clears the platform gate entirely
|
|
972
|
+
# and reaches the external claude-CLI toolchain step (never "unsupported
|
|
973
|
+
# platform", never the retired "not yet verified/certified" wording);
|
|
974
|
+
# (c) meta-bridge-doctor.sh's Darwin RAIL verdict is a SEPARATE, unmoved evidence
|
|
975
|
+
# axis (Hard Rule 17) — it still refuses (nonzero), on a narrowed reason (Q2:
|
|
976
|
+
# start-key no longer needs /proc; per-process environ discovery still does);
|
|
977
|
+
# (d) uninstall was ALREADY open to Darwin (precedent) and is untouched here.
|
|
978
|
+
FAKE_UNAME_BIN="$TMP/fake-uname-bin"; mkdir -p "$FAKE_UNAME_BIN"
|
|
979
|
+
# Bake the ABSOLUTE bash path into the shebang: the two cells below deliberately
|
|
980
|
+
# narrow PATH (no bash on it), and `#!/usr/bin/env bash` would need `env` to find
|
|
981
|
+
# `bash` on THAT narrowed PATH — this fixture's own launcher must not depend on it.
|
|
982
|
+
cat > "$FAKE_UNAME_BIN/uname" <<SH
|
|
983
|
+
#!$(command -v bash)
|
|
973
984
|
printf '%s\n' Darwin
|
|
974
985
|
SH
|
|
975
|
-
chmod +x "$
|
|
986
|
+
chmod +x "$FAKE_UNAME_BIN/uname"
|
|
987
|
+
|
|
988
|
+
# (a) A minimal PATH carrying ONLY `dirname` (the sole external command
|
|
989
|
+
# meta-bridge-install.sh calls before its own python3 check) plus the fake Darwin
|
|
990
|
+
# `uname`. No real python3/node/claude can leak in from the host — the death point
|
|
991
|
+
# is fully controlled by this fixture, not by what happens to be installed here.
|
|
992
|
+
NOPY_BIN="$TMP/nopy-bin"; mkdir -p "$NOPY_BIN"
|
|
993
|
+
ln -sf "$(command -v dirname)" "$NOPY_BIN/dirname"
|
|
994
|
+
set +e
|
|
995
|
+
DARWIN_NOPY_OUT="$(env HOME="$TMP/darwin-nopy-home" XDG_DATA_HOME="$TMP/darwin-nopy-xdg" PATH="$FAKE_UNAME_BIN:$NOPY_BIN" "$BASH" "$REPO/scripts/meta-bridge-install.sh" 2>&1)"
|
|
996
|
+
DARWIN_NOPY_CODE=$?
|
|
997
|
+
set -e
|
|
998
|
+
if [ "$DARWIN_NOPY_CODE" -ne 0 ] \
|
|
999
|
+
&& printf '%s\n' "$DARWIN_NOPY_OUT" | grep -q "python3' not on PATH" \
|
|
1000
|
+
&& ! printf '%s\n' "$DARWIN_NOPY_OUT" | grep -qi 'unsupported platform'; then
|
|
1001
|
+
ok "Darwin install clears the platform gate and refuses on the REAL missing dependency (python3), never on platform name"
|
|
1002
|
+
else bad "Darwin install did not reach the python3 dependency gate as the new fence contract requires:"$'\n'"$DARWIN_NOPY_OUT"; fi
|
|
1003
|
+
|
|
1004
|
+
# (b) The mirror proof: Darwin WITH python3+node present must clear the platform
|
|
1005
|
+
# gate AND the toolchain gate, refusing only because `claude` is absent from this
|
|
1006
|
+
# fixture's deliberately narrow PATH (never on platform name, never on the retired
|
|
1007
|
+
# "not yet verified/certified" wording).
|
|
1008
|
+
OK_BIN="$TMP/darwin-ok-bin"; mkdir -p "$OK_BIN"
|
|
1009
|
+
ln -sf "$(command -v dirname)" "$OK_BIN/dirname"
|
|
1010
|
+
ln -sf "$(command -v node)" "$OK_BIN/node"
|
|
1011
|
+
ln -sf "$(command -v python3)" "$OK_BIN/python3"
|
|
1012
|
+
set +e
|
|
1013
|
+
DARWIN_OK_OUT="$(env HOME="$TMP/darwin-ok-home" XDG_DATA_HOME="$TMP/darwin-ok-xdg" PI_CODING_AGENT_DIR="$TMP/darwin-ok-agent" PATH="$FAKE_UNAME_BIN:$OK_BIN" "$BASH" "$REPO/scripts/meta-bridge-install.sh" 2>&1)"
|
|
1014
|
+
DARWIN_OK_CODE=$?
|
|
1015
|
+
set -e
|
|
1016
|
+
if [ "$DARWIN_OK_CODE" -ne 0 ] \
|
|
1017
|
+
&& printf '%s\n' "$DARWIN_OK_OUT" | grep -q "'claude' CLI not on PATH" \
|
|
1018
|
+
&& ! printf '%s\n' "$DARWIN_OK_OUT" | grep -qi 'unsupported platform' \
|
|
1019
|
+
&& ! printf '%s\n' "$DARWIN_OK_OUT" | grep -qi 'not yet verified'; then
|
|
1020
|
+
ok "Darwin install with real python3+node clears BOTH the platform and toolchain-part-1 gates, reaching the external claude-CLI step"
|
|
1021
|
+
else bad "Darwin install with real python3+node did not clear the platform gate as the new contract requires:"$'\n'"$DARWIN_OK_OUT"; fi
|
|
1022
|
+
|
|
1023
|
+
# (c) The doctor's RAIL verdict is untouched by the fence change: still nonzero,
|
|
1024
|
+
# still reaches its final verdict line. Wording is narrowed under Q2/D1 (start-key
|
|
1025
|
+
# no longer blamed; only per-process environ discovery is), so pin the substring
|
|
1026
|
+
# that survives that rename, not the retired sentence.
|
|
976
1027
|
set +e
|
|
977
|
-
|
|
978
|
-
DARWIN_INSTALL_CODE=$?
|
|
979
|
-
DARWIN_DOCTOR_OUT="$(env HOME="$DOC_HOME" CLAUDE_CONFIG_DIR="$DOC_CFG" PI_CODING_AGENT_DIR="$DOC_AGENT" ENTWURF_META_SESSIONS_DIR="$DOC_STORE" PATH="$PLATFORM_BIN:$DOC_BIN:$PATH" bash "$REPO/scripts/meta-bridge-doctor.sh" 2>&1)"
|
|
1028
|
+
DARWIN_DOCTOR_OUT="$(env HOME="$DOC_HOME" CLAUDE_CONFIG_DIR="$DOC_CFG" PI_CODING_AGENT_DIR="$DOC_AGENT" ENTWURF_META_SESSIONS_DIR="$DOC_STORE" PATH="$FAKE_UNAME_BIN:$DOC_BIN:$PATH" bash "$REPO/scripts/meta-bridge-doctor.sh" 2>&1)"
|
|
980
1029
|
DARWIN_DOCTOR_CODE=$?
|
|
981
|
-
|
|
1030
|
+
set -e
|
|
1031
|
+
if [ "$DARWIN_DOCTOR_CODE" -ne 0 ] && printf '%s\n' "$DARWIN_DOCTOR_OUT" | grep -q 'NOT CERTIFIED' && printf '%s\n' "$DARWIN_DOCTOR_OUT" | grep -q 'meta-bridge doctor: FAIL'; then
|
|
1032
|
+
ok "Darwin doctor stays nonzero and reaches its final NOT-CERTIFIED verdict (rail certification is a separate, unmoved axis)"
|
|
1033
|
+
else bad "Darwin doctor did not hold the rail-certification evidence boundary:"$'\n'"$DARWIN_DOCTOR_OUT"; fi
|
|
1034
|
+
|
|
1035
|
+
# (d) Uninstall precedent, unchanged by this lane: it was ALREADY open to Darwin
|
|
1036
|
+
# before 0.20.0 (legacy-cleanup capability, never install certification).
|
|
1037
|
+
set +e
|
|
1038
|
+
DARWIN_UNINSTALL_OUT="$(env HOME="$TMP/darwin-clean-home" XDG_DATA_HOME="$TMP/darwin-clean-xdg" PATH="$FAKE_UNAME_BIN:$PATH" bash "$REPO/scripts/meta-bridge-uninstall.sh" 2>&1)"
|
|
982
1039
|
DARWIN_UNINSTALL_CODE=$?
|
|
983
1040
|
set -e
|
|
984
|
-
if [ "$DARWIN_INSTALL_CODE" -ne 0 ] && printf '%s\n' "$DARWIN_INSTALL_OUT" | grep -q 'not yet verified/certified for this repair cut'; then
|
|
985
|
-
ok "Darwin install is refused fail-loud as not-yet-certified (future validation may reopen)"
|
|
986
|
-
else bad "Darwin install did not enforce the repair-cut evidence boundary:"$'\n'"$DARWIN_INSTALL_OUT"; fi
|
|
987
|
-
if [ "$DARWIN_DOCTOR_CODE" -ne 0 ] && printf '%s\n' "$DARWIN_DOCTOR_OUT" | grep -q 'NOT YET VERIFIED/CERTIFIED for this repair cut' && printf '%s\n' "$DARWIN_DOCTOR_OUT" | grep -q 'meta-bridge doctor: FAIL'; then
|
|
988
|
-
ok "Darwin doctor stays nonzero and reaches its final NOT-YET-CERTIFIED verdict"
|
|
989
|
-
else bad "Darwin doctor did not hold the repair-cut evidence boundary:"$'\n'"$DARWIN_DOCTOR_OUT"; fi
|
|
990
1041
|
if [ "$DARWIN_UNINSTALL_CODE" -ne 0 ] && printf '%s\n' "$DARWIN_UNINSTALL_OUT" | grep -q 'install state missing' && ! printf '%s\n' "$DARWIN_UNINSTALL_OUT" | grep -q 'unsupported platform'; then
|
|
991
1042
|
ok "Darwin uninstall passes the platform gate and reaches honest state preflight (legacy inverse retained)"
|
|
992
1043
|
else bad "Darwin uninstall was blocked by the support hard-cut instead of reaching honest inverse preflight:"$'\n'"$DARWIN_UNINSTALL_OUT"; fi
|
|
@@ -114,6 +114,44 @@ kill "$CARRIER_PID"
|
|
|
114
114
|
wait "$CARRIER_PID" 2>/dev/null || true
|
|
115
115
|
CARRIER_PID=""
|
|
116
116
|
|
|
117
|
+
# THE CANDIDATE SET ITSELF CAN BE UNKNOWN, AND UNKNOWN IS NOT EMPTY (#78 B1). `pgrep`
|
|
118
|
+
# answers three ways and only two of them are facts about omp: 0 = it enumerated and
|
|
119
|
+
# matched, 1 = it enumerated and NOTHING matched (a real, positive absence), anything
|
|
120
|
+
# else = it never enumerated. The doctor used to `|| true` that third answer into the
|
|
121
|
+
# second, so a broken enumeration produced an empty candidate set, the clean carrier
|
|
122
|
+
# line and a PASS — with a contaminated session possibly live on the host.
|
|
123
|
+
#
|
|
124
|
+
# BOTH DIRECTIONS ARE PINNED, because a repair that also reddened the honest "nothing
|
|
125
|
+
# matched" would be a different defect. `ENTWURF_OMP_CARRIER_PIDS` must be UNSET for
|
|
126
|
+
# these two: that seam short-circuits the very enumeration under test, which is why it
|
|
127
|
+
# is a narrowing seam and never production proof.
|
|
128
|
+
PGREP_SHIM="$SB/pgrep-shim"
|
|
129
|
+
mkdir -p "$PGREP_SHIM"
|
|
130
|
+
printf '#!/usr/bin/env bash\nexit 1\n' > "$PGREP_SHIM/pgrep"
|
|
131
|
+
chmod +x "$PGREP_SHIM/pgrep"
|
|
132
|
+
if OUT="$(env -u ENTWURF_OMP_CARRIER_PIDS PATH="$PGREP_SHIM:$PATH" "$RUN" doctor-omp-bridge 2>&1)"; then
|
|
133
|
+
printf '%s\n' "$OUT" | grep -q "no live omp process carries" || die "an enumerated-and-empty candidate set did not print the clean carrier verdict"
|
|
134
|
+
ok "a \`pgrep\` that enumerated and matched NOTHING is a real absence and stays green"
|
|
135
|
+
else
|
|
136
|
+
printf '%s\n' "$OUT" >&2
|
|
137
|
+
die "a \`pgrep\` exit 1 (nothing matched) turned the carrier axis red"
|
|
138
|
+
fi
|
|
139
|
+
printf '#!/usr/bin/env bash\nexit 2\n' > "$PGREP_SHIM/pgrep"
|
|
140
|
+
if OUT="$(env -u ENTWURF_OMP_CARRIER_PIDS PATH="$PGREP_SHIM:$PATH" "$RUN" doctor-omp-bridge 2>&1)"; then
|
|
141
|
+
printf '%s\n' "$OUT" >&2
|
|
142
|
+
die "[QK:OMP-DOCTOR-UNENUMERATED-IS-UNVERIFIABLE] a FAILED process enumeration left the doctor green — it printed a carrier verdict with no candidate set, so a live omp session carrying a foreign pi garden id would read as clean"
|
|
143
|
+
fi
|
|
144
|
+
if ! printf '%s\n' "$OUT" | grep -q "UNVERIFIABLE — 'pgrep -x omp' FAILED (exit 2)"; then
|
|
145
|
+
printf '%s\n' "$OUT" >&2
|
|
146
|
+
die "the doctor went red without naming the failed enumeration as UNVERIFIABLE"
|
|
147
|
+
fi
|
|
148
|
+
if printf '%s\n' "$OUT" | grep -q "no live omp process carries"; then
|
|
149
|
+
printf '%s\n' "$OUT" >&2
|
|
150
|
+
die "the doctor claimed a clean carrier axis while the candidate set was unknown"
|
|
151
|
+
fi
|
|
152
|
+
ok "a FAILED enumeration is UNVERIFIABLE and red, and it claims neither contamination nor absence"
|
|
153
|
+
rm -rf "$PGREP_SHIM"
|
|
154
|
+
|
|
117
155
|
# ── 2. reinstall is idempotent ───────────────────────────────────────────────
|
|
118
156
|
"$RUN" install-omp-bridge >/dev/null || die "reinstall over our own state failed"
|
|
119
157
|
"$RUN" doctor-omp-bridge >/dev/null || die "doctor red after a reinstall"
|
|
@@ -287,6 +287,106 @@ rm -rf "$HOME/.omp" "$XDG_DATA_HOME/entwurf/omp-bridge" "$XDG_DATA_HOME/entwurf/
|
|
|
287
287
|
"$XDG_DATA_HOME/entwurf/omp-config" "$XDG_DATA_HOME/entwurf/omp-receive" \
|
|
288
288
|
"$XDG_DATA_HOME/entwurf/meta-bridge-omp" "$XDG_DATA_HOME/entwurf/omp-receive"
|
|
289
289
|
|
|
290
|
+
# ── S-9: rail certification is a SEPARATE axis from install success (#78 D1) ──
|
|
291
|
+
# The 0.20.0 macOS lane opened the installers to Darwin (meta-bridge-install.sh's
|
|
292
|
+
# platform gate became its own python3/node dependency instead of the platform
|
|
293
|
+
# name), and that removed the only thing which used to keep a detected Mac host
|
|
294
|
+
# non-green: the installer's refusal. A detected harness on Darwin now INSTALLS,
|
|
295
|
+
# every harness doctor still refuses there (NOT CERTIFIED — pending physical
|
|
296
|
+
# host), and `setup` runs no doctor. Hard Rule 17 names that shape non-green, so
|
|
297
|
+
# setup_harness_result does — and these cells are its ONLY executing consumer,
|
|
298
|
+
# because nothing else in this repo ever takes the Darwin branch.
|
|
299
|
+
#
|
|
300
|
+
# `uname` is faked on PATH exactly the way smoke-meta-install-state.sh already
|
|
301
|
+
# fakes it for the installer's own platform gate. Both directions are pinned so
|
|
302
|
+
# the fixture cannot pass for the wrong reason: the Linux twin below is the SAME
|
|
303
|
+
# sandbox, the SAME stub harnesses and the SAME fixture with one byte different,
|
|
304
|
+
# and S-3/S-8 above are the unfaked real-host Linux evidence.
|
|
305
|
+
FAKE_UNAME="$SB/fake-uname-darwin"; mkdir -p "$FAKE_UNAME"
|
|
306
|
+
# Absolute bash in the shebang, same reason as the sibling fixture: a cell may
|
|
307
|
+
# hand this script a PATH that does not carry `env`/`bash` itself.
|
|
308
|
+
cat > "$FAKE_UNAME/uname" <<SH
|
|
309
|
+
#!$(command -v bash)
|
|
310
|
+
printf '%s\n' Darwin
|
|
311
|
+
SH
|
|
312
|
+
chmod +x "$FAKE_UNAME/uname"
|
|
313
|
+
FAKE_UNAME_LINUX="$SB/fake-uname-linux"; mkdir -p "$FAKE_UNAME_LINUX"
|
|
314
|
+
cat > "$FAKE_UNAME_LINUX/uname" <<SH
|
|
315
|
+
#!$(command -v bash)
|
|
316
|
+
printf '%s\n' Linux
|
|
317
|
+
SH
|
|
318
|
+
chmod +x "$FAKE_UNAME_LINUX/uname"
|
|
319
|
+
|
|
320
|
+
omp_reset() {
|
|
321
|
+
rm -rf "$HOME/.omp" "$XDG_DATA_HOME/entwurf/omp-bridge" "$XDG_DATA_HOME/entwurf/omp-mcp" \
|
|
322
|
+
"$XDG_DATA_HOME/entwurf/omp-config" "$XDG_DATA_HOME/entwurf/omp-receive" \
|
|
323
|
+
"$XDG_DATA_HOME/entwurf/meta-bridge-omp"
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
# ── S-9a: fake Darwin + DETECTED harnesses → named non-green, install intact ──
|
|
327
|
+
# Two harness FAMILIES on purpose (the pi adapter rail and the four omp units):
|
|
328
|
+
# a rule applied to one backend and not the next is a new defect, not half a fix.
|
|
329
|
+
echo "[smoke-setup-verdict] S-9a detected harnesses on an uncertified platform"
|
|
330
|
+
PROJ9="$SB/proj9"; mkdir -p "$PROJ9"
|
|
331
|
+
seed_auth
|
|
332
|
+
set +e; OUT="$(PI_BIN="$SB/harness/pi-ok" OMP_BIN="$FAKE_OMP/omp" ENTWURF_OMP_AGENT_DIR="$HOME/.omp/agent" PATH="$FAKE_UNAME:$FAKE_OMP:$PATH" bash "$REPO_DIR/run.sh" setup "$PROJ9" 2>&1)"; RC=$?; set -e
|
|
333
|
+
want "S-9a: a detected harness on an uncertified platform owns a nonzero setup exit" "[ '$RC' -ne 0 ]"
|
|
334
|
+
want "S-9a: the pi adapter rail is named non-green, never a cosmetic PASS" \
|
|
335
|
+
"printf '%s' \"\$OUT\" | grep -q 'pi: FAIL' && ! printf '%s' \"\$OUT\" | grep -q 'pi: PASS'"
|
|
336
|
+
want "S-9a: all four omp units are named non-green, none cosmetically PASS" \
|
|
337
|
+
"printf '%s' \"\$OUT\" | grep -q 'omp-birth: FAIL' && printf '%s' \"\$OUT\" | grep -q 'omp-mcp: FAIL' && printf '%s' \"\$OUT\" | grep -q 'omp-config: FAIL' && printf '%s' \"\$OUT\" | grep -q 'omp-receive: FAIL' && ! printf '%s' \"\$OUT\" | grep -q 'omp-[a-z]*: PASS'"
|
|
338
|
+
want "S-9a: the reason says the wiring WAS written (this is not an install failure)" \
|
|
339
|
+
"printf '%s' \"\$OUT\" | grep -q 'the wiring WAS written and nothing failed to install'"
|
|
340
|
+
want "S-9a: the reason names the RAIL evidence state and the platform" \
|
|
341
|
+
"printf '%s' \"\$OUT\" | grep -q 'NOT CERTIFIED — pending physical host on Darwin'"
|
|
342
|
+
want "S-9a: the reason names the doctor that owns the rail axis, per component" \
|
|
343
|
+
"printf '%s' \"\$OUT\" | grep -qF \"owned by './run.sh doctor-omp-bridge'\" && printf '%s' \"\$OUT\" | grep -qF \"owned by './run.sh doctor-pi-provider'\""
|
|
344
|
+
want "S-9a: the reason points at the tracking issue" "printf '%s' \"\$OUT\" | grep -q 'Tracking: #78'"
|
|
345
|
+
want "S-9a: it never reads as an install refusal (no unsupported-platform / did-not-complete vocabulary)" \
|
|
346
|
+
"! printf '%s' \"\$OUT\" | grep -qi 'unsupported platform' && ! printf '%s' \"\$OUT\" | grep -q 'did not complete'"
|
|
347
|
+
want "S-9a: the summary is NON-GREEN and names the uncertified components" \
|
|
348
|
+
"printf '%s' \"\$OUT\" | grep -q 'NON-GREEN (FAIL:' && ! printf '%s' \"\$OUT\" | grep -q 'result: green'"
|
|
349
|
+
# The load-bearing half: the installs genuinely SUCCEEDED. Without this the cell
|
|
350
|
+
# would also pass if the platform had merely broken the installers.
|
|
351
|
+
want "S-9a: the wiring really landed — pi project settings plus both omp extensions and the MCP hand" \
|
|
352
|
+
"[ -f '$PROJ9/.pi/settings.json' ] && [ -d '$HOME/.omp/agent/extensions/entwurf-meta-omp' ] && [ -d '$HOME/.omp/agent/extensions/entwurf-receive-omp' ] && [ -f '$HOME/.omp/agent/mcp.json' ]"
|
|
353
|
+
want_auth_untouched "S-9a"
|
|
354
|
+
omp_reset
|
|
355
|
+
|
|
356
|
+
# ── S-9b: the Linux twin — same fixture, one byte different → still PASS/green ──
|
|
357
|
+
# This is the regression axis that matters most: the certified platform's behavior
|
|
358
|
+
# must not have moved at all.
|
|
359
|
+
echo "[smoke-setup-verdict] S-9b the same detected harnesses on the certified platform"
|
|
360
|
+
PROJ9B="$SB/proj9b"; mkdir -p "$PROJ9B"
|
|
361
|
+
seed_auth
|
|
362
|
+
set +e; OUT="$(PI_BIN="$SB/harness/pi-ok" OMP_BIN="$FAKE_OMP/omp" ENTWURF_OMP_AGENT_DIR="$HOME/.omp/agent" PATH="$FAKE_UNAME_LINUX:$FAKE_OMP:$PATH" bash "$REPO_DIR/run.sh" setup "$PROJ9B" 2>&1)"; RC=$?; set -e
|
|
363
|
+
want "S-9b: the certified platform still exits 0" "[ '$RC' -eq 0 ]"
|
|
364
|
+
want "S-9b: pi and all four omp units still compose as PASS" \
|
|
365
|
+
"printf '%s' \"\$OUT\" | grep -q 'pi: PASS' && printf '%s' \"\$OUT\" | grep -q 'omp-birth: PASS' && printf '%s' \"\$OUT\" | grep -q 'omp-mcp: PASS' && printf '%s' \"\$OUT\" | grep -q 'omp-config: PASS' && printf '%s' \"\$OUT\" | grep -q 'omp-receive: PASS'"
|
|
366
|
+
want "S-9b: a PASS row is byte-identical to the pre-#78 wording (the operator surface did not move)" \
|
|
367
|
+
"printf '%s' \"\$OUT\" | grep -q 'omp-birth: PASS — birth extension installed — verify: ./run.sh doctor-omp-bridge'"
|
|
368
|
+
want "S-9b: the computed summary is green and carries no rail-certification wording" \
|
|
369
|
+
"printf '%s' \"\$OUT\" | grep -q 'result: green (computed from the component outcomes above)' && ! printf '%s' \"\$OUT\" | grep -q 'NOT CERTIFIED'"
|
|
370
|
+
want_auth_untouched "S-9b"
|
|
371
|
+
omp_reset
|
|
372
|
+
|
|
373
|
+
# ── S-9c: fake Darwin + NO harness → still green (the macOS CI job's contract) ──
|
|
374
|
+
# The macos-install-surface job asserts exactly this shape on a real Darwin runner:
|
|
375
|
+
# five zero-state SKIPs, core PASS, computed green. The rule above must not reach
|
|
376
|
+
# an absent harness — SKIP is not a completed integration.
|
|
377
|
+
echo "[smoke-setup-verdict] S-9c harness-free host on an uncertified platform"
|
|
378
|
+
PROJ9C="$SB/proj9c"; mkdir -p "$PROJ9C"
|
|
379
|
+
seed_auth
|
|
380
|
+
set +e; OUT="$(PATH="$FAKE_UNAME:$PATH" bash "$REPO_DIR/run.sh" setup "$PROJ9C" 2>&1)"; RC=$?; set -e
|
|
381
|
+
want "S-9c: a harness-free uncertified host still exits 0" "[ '$RC' -eq 0 ]"
|
|
382
|
+
want "S-9c: all five harness probes are still zero-state SKIPs" \
|
|
383
|
+
"printf '%s' \"\$OUT\" | grep -q 'pi: SKIP' && printf '%s' \"\$OUT\" | grep -q 'claude: SKIP' && printf '%s' \"\$OUT\" | grep -q 'agy: SKIP' && printf '%s' \"\$OUT\" | grep -q 'copilot: SKIP' && printf '%s' \"\$OUT\" | grep -q 'omp: SKIP'"
|
|
384
|
+
want "S-9c: core still PASSes and the computed summary is still green" \
|
|
385
|
+
"printf '%s' \"\$OUT\" | grep -q 'core: PASS' && printf '%s' \"\$OUT\" | grep -q 'result: green (computed from the component outcomes above)'"
|
|
386
|
+
want "S-9c: an absent harness is never given a rail verdict it did not earn" \
|
|
387
|
+
"! printf '%s' \"\$OUT\" | grep -q 'NOT CERTIFIED'"
|
|
388
|
+
want_auth_untouched "S-9c"
|
|
389
|
+
|
|
290
390
|
# ── S-5: installed mode is a NAMED first verdict, never the source bootstrap ──
|
|
291
391
|
# A bare copy of run.sh under a fake node_modules root pins the mode seam
|
|
292
392
|
# cheaply: the copy is NOT a runnable installed package (no dist), so this cell
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# smoke-user-scope-citizen — deterministic gate for pi packages[] citizen
|
|
3
|
-
# registration/removal (register-pi-package.py). Offline/hermetic:
|
|
4
|
-
#
|
|
3
|
+
# registration/removal (register-pi-package.py). Offline/hermetic: fake settings
|
|
4
|
+
# files and fake REPO_DIRs under $TMP, sandboxed HOME/PI_CODING_AGENT_DIR/XDG,
|
|
5
|
+
# no network, nothing of the operator's ~/.pi read or written.
|
|
5
6
|
#
|
|
6
7
|
# Guards the wiring that dropped when `pi install` was removed from setup
|
|
7
8
|
# (2026-07-03: `--entwurf-control` / `--emacs-agent-socket` unknown in a foreign
|
|
@@ -10,10 +11,14 @@
|
|
|
10
11
|
# in another repo. This gate is that missing tripwire: if the registration ever
|
|
11
12
|
# regresses, `pnpm run check:full` goes red.
|
|
12
13
|
#
|
|
13
|
-
# Cells 11–13 close the second omission (#53 B): every case
|
|
14
|
-
# settings file with ABSOLUTE entries, so the portable, settings-relative form
|
|
15
|
-
#
|
|
16
|
-
#
|
|
14
|
+
# Cells 11–13 close the second omission (#53 B): every case before them drove a fake
|
|
15
|
+
# settings file with ABSOLUTE entries, so the portable, settings-relative form was
|
|
16
|
+
# never registered against — and `setup` duplicated and restyled a tracked file for
|
|
17
|
+
# four cuts without a single gate seeing it.
|
|
18
|
+
#
|
|
19
|
+
# Cell 15 closes the third (#110), and it is the one cell here that SPAWNS pi: the
|
|
20
|
+
# defect is not a byte, it is which of two checkouts wins `--entwurf-control`, and
|
|
21
|
+
# only the real loader can say. It uses the pinned devDep pi, never PATH.
|
|
17
22
|
set -euo pipefail
|
|
18
23
|
export PYTHONDONTWRITEBYTECODE=1 # snapshot purity: no ignored scripts/__pycache__ writes under qualification
|
|
19
24
|
|
|
@@ -193,69 +198,64 @@ assert d['defaultProvider']=='openai-codex', 'run.sh remove-user-scope dropped a
|
|
|
193
198
|
# idempotent: a second remove-user-scope is a clean no-op (no crash on absent entry)
|
|
194
199
|
if XDG_DATA_HOME="$TMP/xdg" PI_CODING_AGENT_DIR="$AGENT_DIR" bash "$RUN" remove-user-scope >/dev/null 2>&1; then ok "run.sh remove-user-scope is idempotent (no-op second run)"; else bad "run.sh remove-user-scope second run crashed"; fi
|
|
195
200
|
|
|
196
|
-
# ── 11–13. #53 B:
|
|
201
|
+
# ── 11–13. #53 B: a settings-relative self entry is source, never install state
|
|
197
202
|
# The cell whose ABSENCE let this ship. Every case above drove a fake settings file
|
|
198
|
-
# with
|
|
199
|
-
#
|
|
200
|
-
# against. `setup`
|
|
201
|
-
#
|
|
202
|
-
#
|
|
203
|
+
# with ABSOLUTE entries, so the portable, settings-relative form — the one shape
|
|
204
|
+
# register never WRITES, and must therefore never duplicate, absolutize or restyle —
|
|
205
|
+
# was never registered against. `setup` appended the absolute path BESIDE it and
|
|
206
|
+
# rewrote the file at indent=2, and `pnpm check` died at step 1 reading "format
|
|
207
|
+
# error" instead of "install wrote this".
|
|
203
208
|
#
|
|
204
|
-
# The seed is
|
|
205
|
-
# `".."
|
|
206
|
-
#
|
|
207
|
-
#
|
|
209
|
+
# The seed is a FIXTURE. It used to be this repo's OWN committed `.pi/settings.json`,
|
|
210
|
+
# because the repo shipped `packages: [".."]`; #110 retired that entry (entwurf's own
|
|
211
|
+
# checkout self-registers through user scope alone, so a second checkout can no
|
|
212
|
+
# longer register a rival copy of pi-extensions). A witness that depends on us still
|
|
213
|
+
# SHIPPING the shape it tests dies with the shape — and the shape it tests did not
|
|
214
|
+
# die. An operator may hand-write a settings-relative entry, and
|
|
215
|
+
# `is_settings_relative_self` is also what stops an unrelated relative package
|
|
216
|
+
# (`../../repos/gh/andenken`) from being read as ours. Dropping the fixture to a
|
|
217
|
+
# literal here makes the reason honest: the predicate is the subject, not our bytes.
|
|
208
218
|
CLONE="$TMP/selfclone"; mkdir -p "$CLONE/.pi"
|
|
209
219
|
CS="$CLONE/.pi/settings.json"
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
220
|
+
# Tab-indented with a compact array on purpose: cell 13 pins that a genuine rewrite
|
|
221
|
+
# preserves the file's own indent unit, and 11b below pins that this file is not
|
|
222
|
+
# rewritten at all. A seed written in the writer's default style could not tell the
|
|
223
|
+
# two apart.
|
|
224
|
+
printf '{\n\t"skills": ["../.claude/skills"],\n\t"packages": [".."]\n}\n' > "$CS"
|
|
225
|
+
BEFORE="$(sha256sum "$CS" | cut -d' ' -f1)"; MT_B="$(stat -c %Y "$CS")"; sleep 1
|
|
226
|
+
OUT_SELF="$(python3 "$REG" "$CS" "$CLONE")"
|
|
227
|
+
AFTER="$(sha256sum "$CS" | cut -d' ' -f1)"; MT_A="$(stat -c %Y "$CS")"
|
|
228
|
+
if printf '%s' "$OUT_SELF" | grep -q 'no-op'; then
|
|
229
|
+
ok "11 register against a settings-relative self entry is a no-op"
|
|
230
|
+
else
|
|
231
|
+
bad "11 register duplicated/absolutized the portable '..' entry" "$OUT_SELF"
|
|
215
232
|
fi
|
|
216
|
-
if [
|
|
217
|
-
|
|
218
|
-
elif ! python3 -c "
|
|
219
|
-
import json,sys
|
|
220
|
-
p=json.load(open(sys.argv[1])).get('packages')
|
|
221
|
-
sys.exit(0 if p==['..'] else 1)" "$CS"; then
|
|
222
|
-
bad "11 SETUP MISS: the seed ($seed_src) is not the committed portable form (packages != ['..']) — product verdict withheld"
|
|
233
|
+
if [ "$BEFORE" = "$AFTER" ] && [ "$MT_B" = "$MT_A" ]; then
|
|
234
|
+
ok "11b the formatter-governed bytes are UNCHANGED (sha256 + mtime)"
|
|
223
235
|
else
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
bad "11c --remove edited the repo's committed settings source" "$OUT_RM"
|
|
248
|
-
fi
|
|
249
|
-
if printf '%s' "$OUT_RM" | grep -q 'kept 1 settings-relative'; then
|
|
250
|
-
ok "11d the inverse REPORTS what it deliberately left behind (never a silent partial uninstall)"
|
|
251
|
-
else
|
|
252
|
-
bad "11d --remove left the entry without saying so" "$OUT_RM"
|
|
253
|
-
fi
|
|
254
|
-
if printf '%s' "$OUT_DRY" | grep -q 'no entwurf packages\[\] entry to remove'; then
|
|
255
|
-
ok "11e --dry-run agrees with remove (one predicate, no over-report)"
|
|
256
|
-
else
|
|
257
|
-
bad "11e --dry-run disagreed with what remove actually does" "$OUT_DRY"
|
|
258
|
-
fi
|
|
236
|
+
bad "11b register rewrote a settings file it only recognizes (sha $BEFORE -> $AFTER, mtime $MT_B -> $MT_A)"
|
|
237
|
+
fi
|
|
238
|
+
# The inverse direction of the same asymmetry: the shared matcher RECOGNIZES `".."`,
|
|
239
|
+
# so an uninstall that deleted it would edit source it never wrote — the same defect
|
|
240
|
+
# pointed the other way. remove leaves it and says so; dry-run agrees, because both
|
|
241
|
+
# ask one predicate.
|
|
242
|
+
BEFORE_R="$(sha256sum "$CS" | cut -d' ' -f1)"
|
|
243
|
+
OUT_RM="$(python3 "$REG" "$CS" "$CLONE" --remove)"
|
|
244
|
+
OUT_DRY="$(python3 "$REG" "$CS" "$CLONE" --remove --dry-run)"
|
|
245
|
+
if [ "$BEFORE_R" = "$(sha256sum "$CS" | cut -d' ' -f1)" ]; then
|
|
246
|
+
ok "11c --remove does NOT delete a settings-relative self entry (bytes unchanged)"
|
|
247
|
+
else
|
|
248
|
+
bad "11c --remove edited settings source it never authored" "$OUT_RM"
|
|
249
|
+
fi
|
|
250
|
+
if printf '%s' "$OUT_RM" | grep -q 'kept 1 settings-relative'; then
|
|
251
|
+
ok "11d the inverse REPORTS what it deliberately left behind (never a silent partial uninstall)"
|
|
252
|
+
else
|
|
253
|
+
bad "11d --remove left the entry without saying so" "$OUT_RM"
|
|
254
|
+
fi
|
|
255
|
+
if printf '%s' "$OUT_DRY" | grep -q 'no entwurf packages\[\] entry to remove'; then
|
|
256
|
+
ok "11e --dry-run agrees with remove (one predicate, no over-report)"
|
|
257
|
+
else
|
|
258
|
+
bad "11e --dry-run disagreed with what remove actually does" "$OUT_DRY"
|
|
259
259
|
fi
|
|
260
260
|
|
|
261
261
|
# 12. the state a pre-fix `setup` already left on real hosts: the portable entry AND
|
|
@@ -291,12 +291,20 @@ if grep -q $'^\t"packages"' "$S"; then ok "13 a rewrite preserves the file's tab
|
|
|
291
291
|
# indent=2 — semantically a no-op, byte-wise a RED `pnpm check` diagnosed as "formatting".
|
|
292
292
|
# A per-writer cell can never see that; only the real drive can. So this one runs
|
|
293
293
|
# `run.sh install <checkout>` for real and demands sha256 + mtime invariance.
|
|
294
|
-
#
|
|
295
294
|
# The checkout is a stand-in: run.sh resolves its OWN symlinks to find REPO_DIR, so
|
|
296
295
|
# run.sh is COPIED (a link would point the drive back at the operator's real repo) while
|
|
297
296
|
# the trees it only reads are linked. That makes REPO_DIR == the stand-in, which is the
|
|
298
|
-
# shape that matters —
|
|
299
|
-
#
|
|
297
|
+
# shape that matters — the drive installs entwurf INTO entwurf's own checkout, exactly
|
|
298
|
+
# as `./run.sh setup` does from a dev clone (PROJECT_DIR_DEFAULT=$(pwd)).
|
|
299
|
+
#
|
|
300
|
+
# #110 raised what this cell demands. "install is a no-op here" used to hold because
|
|
301
|
+
# the committed `packages: [".."]` already satisfied the writer. That entry is gone,
|
|
302
|
+
# so the writer must instead RECOGNIZE that the project it is installing into IS this
|
|
303
|
+
# root and skip project scope entirely — user scope already registers it, and a second
|
|
304
|
+
# entry naming a different checkout is what made pi load two rival copies of
|
|
305
|
+
# pi-extensions/entwurf-control.ts. The invariant, tested end to end below: entwurf's
|
|
306
|
+
# own checkout carries no project-scope self-registration, in tracked bytes OR in
|
|
307
|
+
# bytes a writer produced.
|
|
300
308
|
CK="$TMP/checkout"
|
|
301
309
|
mkdir -p "$CK/.pi"
|
|
302
310
|
cp "$REPO/run.sh" "$CK/run.sh"
|
|
@@ -317,8 +325,8 @@ if [ ! -s "$CKS" ]; then
|
|
|
317
325
|
elif ! python3 -c "
|
|
318
326
|
import json,sys
|
|
319
327
|
d=json.load(open(sys.argv[1]))
|
|
320
|
-
sys.exit(0 if d.get('packages')
|
|
321
|
-
bad "14 SETUP MISS: the seed ($ck_seed) is not the committed
|
|
328
|
+
sys.exit(0 if d.get('packages') is None and isinstance(d.get('entwurfProvider'),dict) else 1)" "$CKS"; then
|
|
329
|
+
bad "14 SETUP MISS: the seed ($ck_seed) is not the committed form (no packages key + entwurfProvider)"
|
|
322
330
|
else
|
|
323
331
|
CK_BEFORE="$(sha256sum "$CKS" | cut -d' ' -f1)"; CK_MT="$(stat -c %Y "$CKS")"
|
|
324
332
|
sleep 1
|
|
@@ -359,6 +367,124 @@ else
|
|
|
359
367
|
else
|
|
360
368
|
bad "14d the user-scope citizen was not written under the sandboxed PI_CODING_AGENT_DIR" "$ck_out"
|
|
361
369
|
fi
|
|
370
|
+
# The invariant, read off the file the drive just wrote through: no writer put a
|
|
371
|
+
# project-scope self-registration back. 14b already pins the bytes, but a writer
|
|
372
|
+
# that ADDED `packages` to a file with no `packages` key would fail 14b for a
|
|
373
|
+
# reason a reader could mistake for formatting; this says the thing #110 is about,
|
|
374
|
+
# in its own words.
|
|
375
|
+
L14E="14e install into entwurf's OWN checkout wrote no project-scope packages[] self-registration [QK:SELF-CHECKOUT-PROJECT-REGISTRATION]"
|
|
376
|
+
if python3 -c "
|
|
377
|
+
import json,sys
|
|
378
|
+
sys.exit(0 if json.load(open(sys.argv[1])).get('packages') is None else 1)" "$CKS"; then
|
|
379
|
+
ok "$L14E"
|
|
380
|
+
else
|
|
381
|
+
bad "$L14E — violated: install self-registered this checkout, so a second checkout will load a rival copy of pi-extensions" "$(cat "$CKS")"
|
|
382
|
+
fi
|
|
383
|
+
case "$ck_out" in
|
|
384
|
+
*"project-scope packages[] skipped"*) ok "14f the drive REPORTS the skip (a silent skip and a silent write read alike)" ;;
|
|
385
|
+
*) bad "14f install skipped project scope without saying so — 14e cannot tell that apart from a writer that never ran" "$ck_out" ;;
|
|
386
|
+
esac
|
|
387
|
+
fi
|
|
388
|
+
fi
|
|
389
|
+
|
|
390
|
+
# ── 15. #110: the operator-observable this whole contract exists for ───────────
|
|
391
|
+
# Cells 11–14 read settings bytes. Bytes are the mechanism; the SYMPTOM is what pi
|
|
392
|
+
# does with them, and that is what nobody could see: a second checkout booted pi,
|
|
393
|
+
# printed all seven `entwurf` model rows, and looked fine, while `--entwurf-control`
|
|
394
|
+
# — the flag that makes a pi session a garden citizen — was being served by the
|
|
395
|
+
# branch copy, and the INSTALLED extension failed to load with an error naming the
|
|
396
|
+
# installed path as the loser. Not silent. Designed to be misread.
|
|
397
|
+
#
|
|
398
|
+
# pi's package identity is `local:<resolved path>` and dedupePackages collapses only
|
|
399
|
+
# EQUAL identities (package-manager.js getPackageIdentity/dedupePackages), so two
|
|
400
|
+
# real checkouts can never merge — no realpath rule, ours or pi's, would fix this.
|
|
401
|
+
# The only lever is not registering the second one.
|
|
402
|
+
#
|
|
403
|
+
# The cell is control-then-assert on purpose. A sandbox where pi loads no project
|
|
404
|
+
# settings at all (untrusted cwd, missing extension deps, a probe that dies early)
|
|
405
|
+
# would report "no conflicts" for reasons that have nothing to do with the contract,
|
|
406
|
+
# and that green would be a lie. So it first PLANTS the retired entry and demands the
|
|
407
|
+
# conflict APPEAR; only a cell that has just watched the defect happen is allowed to
|
|
408
|
+
# assert its absence.
|
|
409
|
+
# Two different absences, and only one of them is allowed to be quiet. A tree with no
|
|
410
|
+
# `node_modules` at all is a genuinely reduced checkout and the property is untestable
|
|
411
|
+
# there — a named skip. A tree that HAS dependencies but no pinned pi is a broken dev
|
|
412
|
+
# checkout, and skipping it would hide the breakage behind an exit-0 gate, so that one
|
|
413
|
+
# fails loud. (The qualification snapshot is the first case only in principle: it
|
|
414
|
+
# symlinks the origin's node_modules in after its baseline commit — mutation-qualify.ts
|
|
415
|
+
# createRepoSnapshot — so cell 15 really runs there, which is what lets the two mutants
|
|
416
|
+
# below cover this skip. A skip nobody can reach with a planted defect would be exactly
|
|
417
|
+
# the quiet green this cell is built to refuse.)
|
|
418
|
+
PI_BIN_PINNED="$REPO/node_modules/.bin/pi"
|
|
419
|
+
if [ ! -d "$REPO/node_modules" ]; then
|
|
420
|
+
echo " skip 15 second-checkout probe skipped: this tree has no node_modules (reduced checkout); the property is untestable here, not violated"
|
|
421
|
+
elif [ ! -x "$PI_BIN_PINNED" ]; then
|
|
422
|
+
bad "15 the pinned pi is missing from a checkout that HAS dependencies ($PI_BIN_PINNED) — a broken dev checkout, not a reduced one; re-run pnpm install"
|
|
423
|
+
else
|
|
424
|
+
A15="$TMP/dual/installed"; B15="$TMP/dual/second"
|
|
425
|
+
AG15="$TMP/agent15"
|
|
426
|
+
mkdir -p "$A15/.pi" "$B15/.pi" "$AG15"
|
|
427
|
+
# pi-extensions is COPIED into each root, not linked: the defect is two DISTINCT
|
|
428
|
+
# real paths declaring the same flag, and a shared symlink target would be one path
|
|
429
|
+
# wearing two names — a fixture that cannot reproduce what two checkouts do.
|
|
430
|
+
for root in "$A15" "$B15"; do
|
|
431
|
+
cp -R "$REPO/pi-extensions" "$root/pi-extensions"
|
|
432
|
+
for entry in node_modules package.json protocol.js; do
|
|
433
|
+
[ -e "$REPO/$entry" ] && ln -s "$REPO/$entry" "$root/$entry"
|
|
434
|
+
done
|
|
435
|
+
done
|
|
436
|
+
# user scope registers the INSTALLED root, absolutely — what `register_user_scope_citizen`
|
|
437
|
+
# writes on a real host. Trust is seeded for the second checkout because `.pi/` is a
|
|
438
|
+
# trust-gated input: an untrusted cwd loads no project settings, which would silence
|
|
439
|
+
# the control below and make the assertion vacuous.
|
|
440
|
+
printf '{"packages": ["%s"]}\n' "$A15" > "$AG15/settings.json"
|
|
441
|
+
printf '{"%s": true}\n' "$B15" > "$AG15/trust.json"
|
|
442
|
+
# Two fences on the probe child, and BOTH are load-bearing.
|
|
443
|
+
#
|
|
444
|
+
# `< /dev/null`: `pi --print` reads a non-tty stdin as piped prompt input, so it
|
|
445
|
+
# inherits whatever the CALLER handed this gate. Under an interactive shell or a
|
|
446
|
+
# /dev/null stdin that ends immediately and the probe returns in ~2s; under a
|
|
447
|
+
# supervised runner — which hands the process an open socket — it blocks forever.
|
|
448
|
+
# Measured 2026-09-10: a release-gate run sat 1h56m on this line with `pi` alive
|
|
449
|
+
# and no output, and the gate has no timeout of its own to catch it. A gate must
|
|
450
|
+
# read the loader, never the stdin of whoever started it.
|
|
451
|
+
#
|
|
452
|
+
# `timeout 60`: even so, a probe that cannot finish must go RED rather than hang.
|
|
453
|
+
# 124 lands in the captured output, the conflict grep misses, and the cell fails
|
|
454
|
+
# with its own name — the honest outcome for "the loader never answered".
|
|
455
|
+
#
|
|
456
|
+
# Ambient identity carriers are stripped for the same reason: a live pi/ACP session
|
|
457
|
+
# exports PI_SESSION_ID/PI_AGENT_ID and CLAUDE_CONFIG_DIR into children.
|
|
458
|
+
probe15() {
|
|
459
|
+
(cd "$B15" && env -u PI_SESSION_ID -u PI_AGENT_ID -u CLAUDE_CONFIG_DIR \
|
|
460
|
+
HOME="$TMP/home" PI_CODING_AGENT_DIR="$AG15" \
|
|
461
|
+
XDG_DATA_HOME="$TMP/xdg" XDG_STATE_HOME="$TMP/state" XDG_CACHE_HOME="$TMP/cache" \
|
|
462
|
+
timeout 60 "$PI_BIN_PINNED" --model zzz-no-such-provider/zzz-no-such-model --print x \
|
|
463
|
+
</dev/null 2>&1 || true)
|
|
464
|
+
}
|
|
465
|
+
# CONTROL: the retired shape, re-planted. `".."` resolves against <B>/.pi, so the
|
|
466
|
+
# project entry names B while user scope names A — two identities, both loaded.
|
|
467
|
+
printf '{"packages": [".."]}\n' > "$B15/.pi/settings.json"
|
|
468
|
+
OUT15_CTL="$(probe15)"
|
|
469
|
+
if printf '%s' "$OUT15_CTL" | grep -q 'conflicts with'; then
|
|
470
|
+
ok "15 control: a second checkout that registers itself DOES collide on the extension flags"
|
|
471
|
+
else
|
|
472
|
+
bad "15 SETUP MISS: the planted second registration produced no conflict — this sandbox cannot observe the defect, so 15b would be vacuous" "$OUT15_CTL"
|
|
473
|
+
fi
|
|
474
|
+
# ASSERT: this repo's real `.pi/settings.json`, read from the WORKING TREE — not
|
|
475
|
+
# from the candidate index the way cells 11/14 read theirs. The subject here is
|
|
476
|
+
# what pi's loader does with the file that exists ON DISK when a session starts;
|
|
477
|
+
# the index is a different question and check-install-surface S7c already owns it.
|
|
478
|
+
# Reading the index here also made this cell blind to its own replant: a mutation
|
|
479
|
+
# writes the file, git show reads the staged bytes, and the gate stayed green over
|
|
480
|
+
# a planted defect (measured — qualification reported SURVIVED, 2026-09-10).
|
|
481
|
+
cp "$REPO/.pi/settings.json" "$B15/.pi/settings.json"
|
|
482
|
+
OUT15="$(probe15)"
|
|
483
|
+
L15B="15b a second checkout of this repo boots pi with zero extension conflicts [QK:SECOND-CHECKOUT-EXTENSION-COLLISION]"
|
|
484
|
+
if printf '%s' "$OUT15" | grep -q 'conflicts with'; then
|
|
485
|
+
bad "$L15B — violated: the branch copy wins --entwurf-control and the INSTALLED extension fails, printing the installed path as the loser" "$OUT15"
|
|
486
|
+
else
|
|
487
|
+
ok "$L15B"
|
|
362
488
|
fi
|
|
363
489
|
fi
|
|
364
490
|
|
|
@@ -740,14 +866,16 @@ if [ "$RC34B" -ne 0 ] && printf '%s' "$OUT34B" | grep -q 'CORRUPT'; then
|
|
|
740
866
|
ok "34b doctor coupling FAILs on a wrong-type provider installerRoot (named CORRUPT) $QK34"
|
|
741
867
|
else bad "34b doctor stayed green/silent over a corrupt installerRoot (rc=$RC34B): $OUT34B $QK34"; fi
|
|
742
868
|
|
|
743
|
-
#
|
|
869
|
+
# 35. WIRING: both writers of this one file must share the serializer, not copy it.
|
|
744
870
|
# A duplicated indent-detector is how the provider writer stayed open after the
|
|
745
871
|
# package writer was closed; a parity check is cheaper than a third round.
|
|
872
|
+
# (Numbered 35, after the ownership block: it used to be a second "15", which
|
|
873
|
+
# stopped being merely untidy once #110 added the real cell 15 above.)
|
|
746
874
|
for w in register-pi-package.py register-pi-provider.py; do
|
|
747
875
|
if grep -q "from pi_settings_io import" "$REPO/scripts/$w"; then
|
|
748
|
-
ok "
|
|
876
|
+
ok "35 $w routes through the shared pi_settings_io serializer"
|
|
749
877
|
else
|
|
750
|
-
bad "
|
|
878
|
+
bad "35 $w serializes settings on its own again (copied rule = the #53 B shape)"
|
|
751
879
|
fi
|
|
752
880
|
done
|
|
753
881
|
|