@junghanacs/entwurf 0.18.2 → 0.20.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 +2 -2
- package/BASELINE.md +23 -5
- package/CHANGELOG.md +415 -0
- package/DELIVERY.md +59 -6
- package/README.md +43 -12
- package/VERIFY.md +10 -2
- package/docs/acp-backend-rail.md +9 -1
- package/docs/external-mcp-host.md +8 -5
- package/docs/setup-clean-host.md +93 -7
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +18 -7
- 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/mux-fresh-call.js +76 -9
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-placement.js +54 -6
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/resolve-tmux-session.js +114 -0
- package/mcp/entwurf-bridge/src/index.ts +22 -7
- package/package.json +2 -2
- package/pi-extensions/acp-provider.ts +1 -1
- package/pi-extensions/entwurf-control.ts +27 -3
- package/pi-extensions/lib/acp/models.ts +3 -3
- package/pi-extensions/lib/acp/overlay.ts +6 -2
- package/pi-extensions/lib/meta-session.ts +25 -0
- package/pi-extensions/lib/mux-fresh-call.ts +98 -11
- package/pi-extensions/lib/mux-placement.ts +65 -10
- package/pi-extensions/lib/resolve-tmux-session.ts +137 -0
- package/run.sh +71 -26
- 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-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-fresh-cut-gate.sh +54 -2
- package/scripts/check-gate-qualification.ts +13 -12
- package/scripts/check-hook-launch-topology.ts +6 -5
- package/scripts/check-install-surface.ts +2 -1
- 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-mux-launch-tmux.ts +77 -2
- package/scripts/check-mux-launch.ts +17 -0
- package/scripts/check-mux-placement-tmux.ts +61 -1
- package/scripts/check-mux-placement.ts +33 -0
- 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/fresh-cut.json +26 -0
- package/scripts/mutants/mux-boundary.json +24 -0
- package/scripts/mutants/mux-fresh-call.json +81 -0
- package/scripts/mutants/omp-birth.json +33 -3
- package/scripts/mutants/setup-verdict.json +13 -0
- package/scripts/omp-bridge-doctor.sh +110 -31
- package/scripts/omp-bridge-install.sh +6 -2
- package/scripts/omp-receive-install.sh +7 -2
- 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/smoke-meta-async-drift.sh +7 -5
- package/scripts/smoke-meta-install-state.sh +70 -19
- package/scripts/smoke-mux-fresh-call-live.ts +175 -3
- package/scripts/smoke-omp-bridge-state.sh +43 -2
- package/scripts/smoke-setup-verdict.sh +100 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// Reclaim a gate's OWN process resources when that gate exits — however it exits.
|
|
2
|
+
//
|
|
3
|
+
// WHY THIS EXISTS (test-infra, not a product capability):
|
|
4
|
+
// A gate ends red by THROWING (`ok()` is `assert.ok`), and ^C or a runner's SIGTERM
|
|
5
|
+
// ends it with no statement running at all. Teardown written as the last line of the
|
|
6
|
+
// file is therefore skipped on every path except the green one, and the mutant runner
|
|
7
|
+
// makes that the COMMON path: qualification runs each committed mutant expecting the
|
|
8
|
+
// gate to go red. Measured on `oracle` 2026-09-08, before this leaf existed: ~9,200
|
|
9
|
+
// stale temp roots and 3.8G under `/tmp`, plus 360 reparented fixture Node processes
|
|
10
|
+
// from `check-copilot-receive-arm` alone. `pnpm check` was still minting a fresh one
|
|
11
|
+
// per run at that point.
|
|
12
|
+
//
|
|
13
|
+
// WHAT IT IS NOT. It is not error handling and it does not soften a failure: the exit
|
|
14
|
+
// code is untouched (a red gate stays red, a signal still ends as 128+n), nothing is
|
|
15
|
+
// logged, and no failure is converted into a warning (Hard Rule 15). It reclaims
|
|
16
|
+
// process resources only — never a record, transcript or operator artifact (Hard Rule
|
|
17
|
+
// 8). A SIGKILL of the gate still runs nothing here, by construction; that residue is
|
|
18
|
+
// the release protocol's `P9. Reclaim the host` step, and a fixture child that must
|
|
19
|
+
// outlive nothing needs its own parent-death watchdog.
|
|
20
|
+
|
|
21
|
+
import type { ChildProcess } from "node:child_process";
|
|
22
|
+
import { rmSync } from "node:fs";
|
|
23
|
+
|
|
24
|
+
const roots: string[] = [];
|
|
25
|
+
const children: ChildProcess[] = [];
|
|
26
|
+
let wired = false;
|
|
27
|
+
let done = false;
|
|
28
|
+
|
|
29
|
+
/** Sync-only, idempotent: an `exit` handler may not await anything. */
|
|
30
|
+
function reclaim(): void {
|
|
31
|
+
if (done) return;
|
|
32
|
+
done = true;
|
|
33
|
+
for (const c of children) {
|
|
34
|
+
try {
|
|
35
|
+
c.kill("SIGKILL");
|
|
36
|
+
} catch {
|
|
37
|
+
// kill() throws ESRCH on an already-reaped pid — gone either way
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
for (const dir of roots) {
|
|
41
|
+
try {
|
|
42
|
+
rmSync(dir, { recursive: true, force: true });
|
|
43
|
+
} catch {
|
|
44
|
+
// Best-effort teardown must never become the reason a GREEN gate exits
|
|
45
|
+
// non-zero: a throw inside an `exit` handler would flip the verdict of a
|
|
46
|
+
// run that had already passed every assertion it makes.
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function wire(): void {
|
|
52
|
+
if (wired) return;
|
|
53
|
+
wired = true;
|
|
54
|
+
process.on("exit", reclaim);
|
|
55
|
+
// A signal is not an exit: without these, `exit` never fires. Re-raising as
|
|
56
|
+
// 128+n keeps the shell's own convention rather than inventing a code.
|
|
57
|
+
for (const [signal, n] of [
|
|
58
|
+
["SIGINT", 2],
|
|
59
|
+
["SIGTERM", 15],
|
|
60
|
+
["SIGHUP", 1],
|
|
61
|
+
] as const) {
|
|
62
|
+
process.on(signal, () => {
|
|
63
|
+
reclaim();
|
|
64
|
+
process.exit(128 + n);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Register a temp root to remove on exit. Returns it, so it wraps `mkdtempSync`. */
|
|
70
|
+
export function reclaimOnExit(dir: string): string {
|
|
71
|
+
wire();
|
|
72
|
+
roots.push(dir);
|
|
73
|
+
return dir;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Register a spawned child to SIGKILL on exit. Returns it, so it wraps `spawn`. */
|
|
77
|
+
export function killOnExit<T extends ChildProcess>(child: T): T {
|
|
78
|
+
wire();
|
|
79
|
+
children.push(child);
|
|
80
|
+
return child;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Run the registered teardown now (the green tail); later exits become no-ops. */
|
|
84
|
+
export function reclaimNow(): void {
|
|
85
|
+
reclaim();
|
|
86
|
+
}
|
|
@@ -66,13 +66,18 @@ echo "meta-bridge doctor"
|
|
|
66
66
|
echo "config=$CLAUDE_CFG agent-dir=$AGENT"
|
|
67
67
|
|
|
68
68
|
echo "[platform]"
|
|
69
|
-
#
|
|
70
|
-
#
|
|
71
|
-
#
|
|
72
|
-
#
|
|
69
|
+
# WHAT THIS FENCE IS ABOUT, NARROWLY. It is not "GNU tools" and it is not the
|
|
70
|
+
# start-key: the python join below now mints the SAME `ps:` string the TS core mints
|
|
71
|
+
# (audit Q2), so start-key and ppid are portable in this doctor too and that half of
|
|
72
|
+
# the old reason is retired. What is left is bridge DISCOVERY — finding the live
|
|
73
|
+
# entwurf MCP children by their per-process ENVIRONMENT — which has no measured
|
|
74
|
+
# portable equivalent, so the live-owner tier is unreachable rather than merely
|
|
75
|
+
# unobserved (the rc=3 branch below is the same verdict, stated where it fires).
|
|
76
|
+
# Darwin therefore stays nonzero here: a doctor that cannot measure its central axis
|
|
77
|
+
# must never downgrade "unsupported" to a static/synthetic PASS.
|
|
73
78
|
case "$(uname -s)" in
|
|
74
79
|
Linux) ok "Linux supported — the certified live axis (live owner join is instrumentable)" ;;
|
|
75
|
-
Darwin) bad "macOS is NOT
|
|
80
|
+
Darwin) bad "macOS is NOT CERTIFIED for this cut: the live-owner join needs per-process ENVIRONMENT discovery of the live bridge children, and no portable Darwin equivalent has been MEASURED yet (start-key/ppid are portable here now — that half of the old reason is retired). This doctor stays nonzero on Darwin for diagnosis and legacy cleanup until a physical Darwin receipt exists." ;;
|
|
76
81
|
*) bad "$(uname -s) unsupported (Claude meta-bridge repair cut is Linux-only)" ;;
|
|
77
82
|
esac
|
|
78
83
|
|
|
@@ -982,7 +987,7 @@ fi
|
|
|
982
987
|
echo "[live Claude MCP owner join]"
|
|
983
988
|
if command -v python3 >/dev/null; then
|
|
984
989
|
if JOIN_OUT="$(python3 - "$AGENT" "$META_SESSIONS" <<'PY' 2>&1
|
|
985
|
-
import json,
|
|
990
|
+
import json, re, subprocess, sys
|
|
986
991
|
from pathlib import Path
|
|
987
992
|
|
|
988
993
|
agent = Path(sys.argv[1]).expanduser().resolve()
|
|
@@ -990,23 +995,52 @@ store = Path(sys.argv[2]).expanduser().resolve()
|
|
|
990
995
|
proc = Path("/proc")
|
|
991
996
|
if not proc.is_dir():
|
|
992
997
|
# rc=3 is a PLATFORM verdict, distinct from rc=2 ("no session open right now").
|
|
993
|
-
#
|
|
994
|
-
#
|
|
995
|
-
#
|
|
996
|
-
|
|
998
|
+
# NARROWLY: start-key and ppid are NOT the reason — `parent()`/`start_key()` below
|
|
999
|
+
# carry the same two-tier `/proc`-then-`ps` fallback the runtime writer has, so they
|
|
1000
|
+
# answer on any platform with a `ps`. Bridge DISCOVERY is the reason: it needs each
|
|
1001
|
+
# candidate's per-process ENVIRONMENT, which has no measured portable equivalent, so
|
|
1002
|
+
# the live tier is unreachable here rather than merely unobserved.
|
|
1003
|
+
print("live owner join is NOT INSTRUMENTABLE on this platform: what holds the lane closed is per-process environment DISCOVERY alone — start-key and ppid are portable there (`ps` fallback, same string the TS core mints), but no measured Darwin equivalent exists for reading each live bridge child's environment")
|
|
997
1004
|
raise SystemExit(3)
|
|
998
1005
|
|
|
999
1006
|
def stat_fields(pid):
|
|
1000
1007
|
text = Path(f"/proc/{pid}/stat").read_text()
|
|
1001
1008
|
return text[text.rfind(")") + 2:].split()
|
|
1002
1009
|
|
|
1010
|
+
def ps_field(pid, fmt):
|
|
1011
|
+
# The SAME argv vector the writer uses (meta-session.ts parentPid/processStartKey),
|
|
1012
|
+
# so the two writers cannot disagree by construction.
|
|
1013
|
+
try:
|
|
1014
|
+
out = subprocess.run(["ps", "-o", fmt, "-p", str(pid)], capture_output=True, text=True).stdout
|
|
1015
|
+
except Exception:
|
|
1016
|
+
return ""
|
|
1017
|
+
return out.strip()
|
|
1018
|
+
|
|
1003
1019
|
def parent(pid):
|
|
1020
|
+
if not isinstance(pid, int) or pid <= 0: return None
|
|
1004
1021
|
try: return int(stat_fields(pid)[1])
|
|
1005
|
-
except Exception:
|
|
1022
|
+
except Exception: pass
|
|
1023
|
+
# `ps -o ppid=` — literally the fallback parentPid() already ships.
|
|
1024
|
+
ppid = ps_field(pid, "ppid=")
|
|
1025
|
+
return int(ppid) if ppid.isdigit() and int(ppid) > 0 else None
|
|
1006
1026
|
|
|
1007
1027
|
def start_key(pid):
|
|
1008
|
-
|
|
1009
|
-
|
|
1028
|
+
# TWO WRITERS, ONE STRING. The runtime mints this key (meta-session.ts
|
|
1029
|
+
# processStartKey) and this doctor RE-mints it to compare against the marker; if the
|
|
1030
|
+
# two ever mint different strings for the same live pid the join fails for a reason
|
|
1031
|
+
# that has nothing to do with the install. So this is not "a portable start key" —
|
|
1032
|
+
# it is the same two tiers in the same order with the same prefixes: `/proc/<pid>/stat`
|
|
1033
|
+
# field 22 as `linux:<ticks>`, else `ps -o lstart= -p <pid>` trimmed as `ps:<lstart>`.
|
|
1034
|
+
# python `.strip()` and JS `.trim()` both strip ASCII whitespace from this single-line
|
|
1035
|
+
# output, and the argv vector is identical, so the bytes are identical (audit Q2).
|
|
1036
|
+
# "" stays the UNKNOWN answer and never matches a marker — fail-closed, unchanged.
|
|
1037
|
+
if not isinstance(pid, int) or pid <= 0: return ""
|
|
1038
|
+
try:
|
|
1039
|
+
starttime = stat_fields(pid)[19]
|
|
1040
|
+
if re.fullmatch(r"\d+", starttime): return "linux:" + starttime
|
|
1041
|
+
except Exception: pass
|
|
1042
|
+
lstart = ps_field(pid, "lstart=")
|
|
1043
|
+
return "ps:" + lstart if lstart else ""
|
|
1010
1044
|
|
|
1011
1045
|
def process_agent(env):
|
|
1012
1046
|
raw = env.get("PI_CODING_AGENT_DIR")
|
|
@@ -1088,7 +1122,7 @@ PY
|
|
|
1088
1122
|
# send an operator to completely different places.
|
|
1089
1123
|
case "$JOIN_RC" in
|
|
1090
1124
|
2) bad "NOT CERTIFIED — $JOIN_OUT. Nothing here says the install is broken: the evidence simply does not exist yet. Open a Claude Code session (or restart the affected one) and run this doctor again." ;;
|
|
1091
|
-
3) bad "NOT CERTIFIED on this platform — $JOIN_OUT. This repair cut
|
|
1125
|
+
3) bad "NOT CERTIFIED on this platform — $JOIN_OUT. This repair cut certifies the Claude meta-bridge on Linux only; static/synthetic evidence cannot certify $(uname -s), and only a physical native run can reopen that lane." ;;
|
|
1092
1126
|
*) bad "$JOIN_OUT. Re-run install-meta-bridge, restart the affected Claude session(s), then run doctor again." ;;
|
|
1093
1127
|
esac
|
|
1094
1128
|
fi
|
|
@@ -1121,7 +1155,14 @@ livewrite_schema() { # $1=meta-session.<ext> → v3|v2|v1|absent
|
|
|
1121
1155
|
elif grep -q "serializeMetaIdentity" "$1"; then echo "v2"
|
|
1122
1156
|
else echo "v1"; fi
|
|
1123
1157
|
}
|
|
1124
|
-
|
|
1158
|
+
# 12-hex digest, portable. NOT `sha256sum` (coreutils, absent on macOS) and NOT
|
|
1159
|
+
# `shasum -a 256` — that would be a second digest convention, while python3 is already
|
|
1160
|
+
# a hard prerequisite of this whole doctor (line 31 reads install-state through it). So
|
|
1161
|
+
# this reuses the python3 hashlib form omp-receive-doctor.sh:99 already ships. An absent
|
|
1162
|
+
# file still yields `------------`, and a hashing failure now falls into that same
|
|
1163
|
+
# placeholder instead of an empty string — either way the parity rows below read it as
|
|
1164
|
+
# drift and go red, which is the fail-closed side.
|
|
1165
|
+
hash12() { [ -f "$1" ] && python3 -c 'import hashlib,sys;print(hashlib.sha256(open(sys.argv[1],"rb").read()).hexdigest()[:12])' "$1" 2>/dev/null || echo "------------"; }
|
|
1125
1166
|
registry_for_ms() { # $1=bundle meta-session.ts → sibling plugin-root registry path
|
|
1126
1167
|
[ -f "$1" ] || { echo ""; return; }
|
|
1127
1168
|
dirname "$(dirname "$1")"
|
|
@@ -25,9 +25,13 @@
|
|
|
25
25
|
# Idempotent: re-running removes the prior marketplace/plugin first, so a
|
|
26
26
|
# `nix rebuild` that moved node just re-bakes and re-installs cleanly.
|
|
27
27
|
#
|
|
28
|
-
# Platform: Linux
|
|
29
|
-
#
|
|
30
|
-
#
|
|
28
|
+
# Platform: Linux and Darwin. This installer's own platform dependency is python3 +
|
|
29
|
+
# node + the claude CLI (all checked below); it touches no /proc, no systemd, no
|
|
30
|
+
# GNU-only tool. Windows and every other platform fail fast. Live-owner rail
|
|
31
|
+
# certification is a SEPARATE evidence axis owned by doctor-meta-bridge (Hard Rule 17:
|
|
32
|
+
# installation portability and rail/runtime support are separate evidence axes) — a
|
|
33
|
+
# green install here does not certify the Darwin rail; run ./run.sh doctor-meta-bridge
|
|
34
|
+
# for that verdict.
|
|
31
35
|
set -euo pipefail
|
|
32
36
|
|
|
33
37
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
@@ -64,11 +68,13 @@ esac
|
|
|
64
68
|
|
|
65
69
|
die() { echo "meta-bridge-install: $*" >&2; exit 1; }
|
|
66
70
|
|
|
67
|
-
# --- platform gate (
|
|
71
|
+
# --- platform gate (installer's own dependency, not rail certification) -----
|
|
72
|
+
# Darwin is open on the same precedent as meta-bridge-uninstall.sh:20-23: this
|
|
73
|
+
# installer touches no /proc and no GNU-only tool, so its own mechanism is portable
|
|
74
|
+
# even while the live-owner rail (doctor-meta-bridge) stays a separate, unproven axis.
|
|
68
75
|
case "$(uname -s)" in
|
|
69
|
-
Linux) ;;
|
|
70
|
-
|
|
71
|
-
*) die "unsupported platform '$(uname -s)'. The Claude meta-bridge repair cut supports Linux only (no unverified fallback)." ;;
|
|
76
|
+
Linux | Darwin) ;;
|
|
77
|
+
*) die "unsupported platform '$(uname -s)'. This installer requires Linux or Darwin." ;;
|
|
72
78
|
esac
|
|
73
79
|
|
|
74
80
|
# --- toolchain gate, part 1: this machine's own runtime ---------------------
|
|
@@ -14,12 +14,14 @@ PLUGIN="entwurf-meta-receive"
|
|
|
14
14
|
|
|
15
15
|
die() { echo "meta-bridge-uninstall: $*" >&2; exit 1; }
|
|
16
16
|
|
|
17
|
-
# Darwin
|
|
18
|
-
#
|
|
19
|
-
# support narrows
|
|
17
|
+
# Darwin is accepted on both halves as of 0.20.0. It was accepted HERE first, while new
|
|
18
|
+
# installs were still refused, because removing the inverse would strand an older macOS
|
|
19
|
+
# install precisely when support narrows. That asymmetry is now gone — the installer
|
|
20
|
+
# refuses on its real dependency instead of the platform name — so the two halves agree
|
|
21
|
+
# and this fence only turns away platforms neither half has ever run on.
|
|
20
22
|
case "$(uname -s)" in
|
|
21
23
|
Linux | Darwin) ;;
|
|
22
|
-
*) die "unsupported platform '$(uname -s)'.
|
|
24
|
+
*) die "unsupported platform '$(uname -s)'. Both halves of the Claude meta-bridge repair cut run on Linux and Darwin only. Rail certification is a separate axis owned by doctor-meta-bridge." ;;
|
|
23
25
|
esac
|
|
24
26
|
|
|
25
27
|
command -v python3 >/dev/null || die "'python3' not on PATH. It is required for stateful uninstall."
|
|
@@ -26,6 +26,19 @@
|
|
|
26
26
|
"signature": "[QK:CORTEX-CURATED-FOUR-ROWS]",
|
|
27
27
|
"signatureSource": "scripts/check-acp-cortex.ts"
|
|
28
28
|
},
|
|
29
|
+
{
|
|
30
|
+
"claim": "CLAUDE-CURATED-THREE-ROWS",
|
|
31
|
+
"title": "dropping a curated CLAUDE row shrinks the operator's model list while the exact-set comparison stays green — expected derives from the same constant, so only the independent count catches it",
|
|
32
|
+
"subject": "pi-extensions/lib/acp/models.ts",
|
|
33
|
+
"find": [
|
|
34
|
+
"export const SUPPORTED_ANTHROPIC_MODEL_IDS = [\"claude-sonnet-5\", \"claude-opus-5\", \"claude-fable-5-1\"] as const;"
|
|
35
|
+
],
|
|
36
|
+
"replace": ["export const SUPPORTED_ANTHROPIC_MODEL_IDS = [\"claude-sonnet-5\", \"claude-opus-5\"] as const;"],
|
|
37
|
+
"gate": ["bash", "run.sh", "check-acp-provider-surface"],
|
|
38
|
+
"timeoutSeconds": 300,
|
|
39
|
+
"signature": "[QK:CLAUDE-CURATED-THREE-ROWS]",
|
|
40
|
+
"signatureSource": "scripts/check-acp-provider-surface.ts"
|
|
41
|
+
},
|
|
29
42
|
{
|
|
30
43
|
"claim": "CORTEX-AUTH-NARROW-CREDENTIAL-CACHE",
|
|
31
44
|
"title": "widening the auth passthrough to the whole cortex/cache leaks operator tool_outputs/tip_history into the child",
|
|
@@ -56,6 +69,17 @@
|
|
|
56
69
|
"signature": "[QK:CORTEX-AUTOUPDATE-OFF]",
|
|
57
70
|
"signatureSource": "scripts/check-acp-cortex.ts"
|
|
58
71
|
},
|
|
72
|
+
{
|
|
73
|
+
"claim": "CORTEX-REALHOME-PLATFORM-NEUTRAL",
|
|
74
|
+
"title": "re-deriving realHome absoluteness from the POSIX flavor alone makes a drive/UNC HOME read as relative — the guard would state the host it runs on instead of the contract (PR #77, @yizixu)",
|
|
75
|
+
"subject": "pi-extensions/lib/acp/overlay.ts",
|
|
76
|
+
"find": ["\tif (!params.realHome || !(isAbsolute(params.realHome) || win32.isAbsolute(params.realHome))) {"],
|
|
77
|
+
"replace": ["\tif (!params.realHome || !params.realHome.startsWith(\"/\")) {"],
|
|
78
|
+
"gate": ["bash", "run.sh", "check-acp-cortex"],
|
|
79
|
+
"timeoutSeconds": 300,
|
|
80
|
+
"signature": "[QK:CORTEX-REALHOME-PLATFORM-NEUTRAL]",
|
|
81
|
+
"signatureSource": "scripts/check-acp-cortex.ts"
|
|
82
|
+
},
|
|
59
83
|
{
|
|
60
84
|
"claim": "CORTEX-BRIDGE-DUAL-HOME",
|
|
61
85
|
"title": "dropping the bridge-only real-HOME restore leaves the garden store cut off — tools reach the model but see an empty garden (D10)",
|
|
@@ -182,14 +206,14 @@
|
|
|
182
206
|
"signatureSource": "scripts/check-acp-session-reuse.ts"
|
|
183
207
|
},
|
|
184
208
|
{
|
|
185
|
-
"claim": "CORTEX-PROVIDER-
|
|
209
|
+
"claim": "CORTEX-PROVIDER-EXACT-ROW-SURFACE",
|
|
186
210
|
"title": "filtering the cortex rows out of the REAL provider entry drops the whole backend from the operator's model list while models.ts and check-acp-cortex stay green",
|
|
187
211
|
"subject": "pi-extensions/acp-provider.ts",
|
|
188
212
|
"find": ["\t\tmodels: allCuratedModels(),"],
|
|
189
213
|
"replace": ["\t\tmodels: allCuratedModels().filter((m) => !m.id.startsWith(\"cortex-\")),"],
|
|
190
214
|
"gate": ["bash", "run.sh", "check-acp-provider-surface"],
|
|
191
215
|
"timeoutSeconds": 300,
|
|
192
|
-
"signature": "[QK:CORTEX-PROVIDER-
|
|
216
|
+
"signature": "[QK:CORTEX-PROVIDER-EXACT-ROW-SURFACE]",
|
|
193
217
|
"signatureSource": "scripts/check-acp-provider-surface.ts"
|
|
194
218
|
}
|
|
195
219
|
]
|
|
@@ -233,6 +233,17 @@
|
|
|
233
233
|
"signature": "[QK:COPILOT-RECEIVE-DOCTOR-UNREADABLE-ENVIRON-IS-RED]",
|
|
234
234
|
"signatureSource": "scripts/check-copilot-receive-arm.ts"
|
|
235
235
|
},
|
|
236
|
+
{
|
|
237
|
+
"claim": "COPILOT-RECEIVE-DOCTOR-UNREADABLE-ARGV-IS-UNVERIFIABLE",
|
|
238
|
+
"title": "collapsing every OSError on `/proc/<pid>/cmdline` back to None makes a REFUSED identity read indistinguishable from a process that left: the candidate is skipped before identity is decided, all counters stay zero, and the doctor prints the benign `no live GitHub Copilot CLI process` note — claiming ABSENCE while a live CLI it was never allowed to identify may be sitting there inert. The environ claim above cannot cover this one: that branch is reachable only AFTER a successful argv match",
|
|
239
|
+
"subject": "scripts/copilot-receive-bridge.sh",
|
|
240
|
+
"find": ["\t\tif exc.errno in (errno.ENOENT, errno.ESRCH):", "\t\t\treturn None", "\t\treturn UNREADABLE"],
|
|
241
|
+
"replace": ["\t\treturn None"],
|
|
242
|
+
"gate": ["bash", "run.sh", "check-copilot-receive-arm"],
|
|
243
|
+
"timeoutSeconds": 300,
|
|
244
|
+
"signature": "[QK:COPILOT-RECEIVE-DOCTOR-UNREADABLE-ARGV-IS-UNVERIFIABLE]",
|
|
245
|
+
"signatureSource": "scripts/check-copilot-receive-arm.ts"
|
|
246
|
+
},
|
|
236
247
|
{
|
|
237
248
|
"claim": "COPILOT-RECEIVE-DOCTOR-OWNS-SCANNER-FAILURE",
|
|
238
249
|
"title": "letting the scanner's exit status reach the assignment under `set -e` kills the doctor mid-section: no verdict line is printed at all, so the operator cannot tell a healthy host from a broken doctor — the same silent-death shape this lane already fixed once in the log grep",
|
|
@@ -258,6 +269,21 @@
|
|
|
258
269
|
"timeoutSeconds": 300,
|
|
259
270
|
"signature": "[QK:COPILOT-RECEIVE-DOCTOR-PI-CARRIER-IS-RED]",
|
|
260
271
|
"signatureSource": "scripts/check-copilot-receive-arm.ts"
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
"claim": "COPILOT-RECEIVE-STUB-CHILD-DIES-WITH-PARENT",
|
|
275
|
+
"title": "a stub child with no parent-death watchdog outlives the gate forever — measured on `oracle` as 360 reparented Node receivers, because a red gate exits by THROWING and never reaches any teardown written below it",
|
|
276
|
+
"subject": "scripts/check-copilot-receive-arm.ts",
|
|
277
|
+
"find": [
|
|
278
|
+
"if (parentAtStart === 1) process.exit(0);",
|
|
279
|
+
"setInterval(() => {",
|
|
280
|
+
"\tif (process.ppid !== parentAtStart) process.exit(0);"
|
|
281
|
+
],
|
|
282
|
+
"replace": ["void parentAtStart;", "setInterval(() => {", "\t/* the vendor bootstrap's parent check, removed */"],
|
|
283
|
+
"gate": ["bash", "run.sh", "check-copilot-receive-arm"],
|
|
284
|
+
"timeoutSeconds": 300,
|
|
285
|
+
"signature": "[QK:COPILOT-RECEIVE-STUB-CHILD-DIES-WITH-PARENT]",
|
|
286
|
+
"signatureSource": "scripts/check-copilot-receive-arm.ts"
|
|
261
287
|
}
|
|
262
288
|
]
|
|
263
289
|
}
|
|
@@ -12,6 +12,32 @@
|
|
|
12
12
|
"timeoutSeconds": 180,
|
|
13
13
|
"signature": "[QK:FRESH-CUT-COLLISION-NO-MOVE]",
|
|
14
14
|
"signatureSource": "scripts/check-fresh-cut-gate.sh"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"claim": "FRESHCUT-SYNCAUTH-TRIPWIRE-FIRES",
|
|
18
|
+
"title": "the retired-credential tripwire goes blind again. This replants the ACTUAL defect rather than a stand-in: under pipefail a `-q` grep exits at the first match and SIGPIPEs the awk still writing ~200KB of a 433KB run.sh, so the pipeline reports 141 and the `if` takes the CLEAN branch — the cell reported no sync_auth surface EXACTLY when one existed. It was deterministic here (100/100 measured) because the producer dwarfs the pipe buffer, which is why the self-test plants its surface near the TOP of the real run.sh: a small synthetic fixture would pass under this mutant and prove nothing",
|
|
19
|
+
"subject": "scripts/check-fresh-cut-gate.sh",
|
|
20
|
+
"find": [" awk '!/^[[:space:]]*#/' \"$1\" | grep -c 'sync_auth' || true"],
|
|
21
|
+
"replace": [" awk '!/^[[:space:]]*#/' \"$1\" | grep -q 'sync_auth' && echo 1 || echo 0"],
|
|
22
|
+
"gate": ["bash", "run.sh", "check-fresh-cut-gate"],
|
|
23
|
+
"timeoutSeconds": 180,
|
|
24
|
+
"signature": "[QK:FRESHCUT-SYNCAUTH-TRIPWIRE-FIRES]",
|
|
25
|
+
"signatureSource": "scripts/check-fresh-cut-gate.sh"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"claim": "FRESHCUT-PREFLIGHT-CUT-DETECTED",
|
|
29
|
+
"title": "the cell forbidding an install from cutting a generation by itself stops SEEING the call it forbids — the context window silently narrows to the matched line, so a fresh-cut invocation one line into preflight_v3_store() reads as absent and D7 stays green. A forbidding cell that is green on a clean tree cannot tell blindness from compliance, which is what the self-test exists to separate",
|
|
30
|
+
"subject": "scripts/check-fresh-cut-gate.sh",
|
|
31
|
+
"find": [
|
|
32
|
+
" grep -n 'preflight_v3_store()' -A 40 \"$1\" | grep -cE 'run_ts scripts/meta-bridge-fresh-cut\\.ts' || true"
|
|
33
|
+
],
|
|
34
|
+
"replace": [
|
|
35
|
+
" grep -n 'preflight_v3_store()' -A 0 \"$1\" | grep -cE 'run_ts scripts/meta-bridge-fresh-cut\\.ts' || true"
|
|
36
|
+
],
|
|
37
|
+
"gate": ["bash", "run.sh", "check-fresh-cut-gate"],
|
|
38
|
+
"timeoutSeconds": 180,
|
|
39
|
+
"signature": "[QK:FRESHCUT-PREFLIGHT-CUT-DETECTED]",
|
|
40
|
+
"signatureSource": "scripts/check-fresh-cut-gate.sh"
|
|
15
41
|
}
|
|
16
42
|
]
|
|
17
43
|
}
|
|
@@ -191,6 +191,30 @@
|
|
|
191
191
|
"timeoutSeconds": 60,
|
|
192
192
|
"signature": "[QK:MUX-LAUNCH-CORE-IMPORT-FREE]",
|
|
193
193
|
"signatureSource": "scripts/check-mux-launch.ts"
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
"claim": "MUX-CLOSE-SERVER-BOUND",
|
|
197
|
+
"title": "the close-side predicate accepts a foreign SERVER — a restarted server hands out the same `@id`, and a close would kill whatever window now wears it",
|
|
198
|
+
"subject": "pi-extensions/lib/mux-placement.ts",
|
|
199
|
+
"find": ["\treturn origin.serverPid === now.serverPid;\n}"],
|
|
200
|
+
"replace": ["\treturn true;\n}"],
|
|
201
|
+
"gate": ["bash", "run.sh", "check-mux-placement"],
|
|
202
|
+
"timeoutSeconds": 120,
|
|
203
|
+
"signature": "[QK:MUX-CLOSE-SERVER-BOUND]",
|
|
204
|
+
"signatureSource": "scripts/check-mux-placement.ts"
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
"claim": "RESOLVE-TMUX-SESSION-IMPORT-FREE",
|
|
208
|
+
"title": "the session-lookup leaf reaches into the placement leaf for runTmux — the one decision leaf that must not be able to run tmux acquires exactly that power, and the injected-runner seam its whole safety argument rests on stops being the only way in",
|
|
209
|
+
"subject": "pi-extensions/lib/resolve-tmux-session.ts",
|
|
210
|
+
"find": ["export type TmuxSessionRejectReason = \"tmux-session-name-invalid\" | \"tmux-session-missing\";"],
|
|
211
|
+
"replace": [
|
|
212
|
+
"import { runTmux } from \"./mux-placement.ts\";\nvoid runTmux;\nexport type TmuxSessionRejectReason = \"tmux-session-name-invalid\" | \"tmux-session-missing\";"
|
|
213
|
+
],
|
|
214
|
+
"gate": ["bash", "run.sh", "check-mux-launch"],
|
|
215
|
+
"timeoutSeconds": 120,
|
|
216
|
+
"signature": "[QK:RESOLVE-TMUX-SESSION-IMPORT-FREE]",
|
|
217
|
+
"signatureSource": "scripts/check-mux-launch.ts"
|
|
194
218
|
}
|
|
195
219
|
]
|
|
196
220
|
}
|
|
@@ -438,6 +438,87 @@
|
|
|
438
438
|
"timeoutSeconds": 180,
|
|
439
439
|
"signature": "[QK:FRESHCALL-BACKEND-SET-SURFACE-PARITY-PI]",
|
|
440
440
|
"signatureSource": "test/fresh-call-surfaces.contract.test.ts"
|
|
441
|
+
},
|
|
442
|
+
{
|
|
443
|
+
"claim": "TMUXSESSION-LOOKUP-ENGINE",
|
|
444
|
+
"title": "the seat lookup goes back to the `-f` filter engine, which was measured to match EVERY session when the name contains a '}' — a fresh sibling then opens in whichever session tmux listed first",
|
|
445
|
+
"subject": "pi-extensions/lib/resolve-tmux-session.ts",
|
|
446
|
+
"find": ["\treturn [\"list-windows\", \"-t\", `=${name}`, \"-F\", \"#{session_id}\"];"],
|
|
447
|
+
"replace": ["\treturn [\"list-sessions\", \"-f\", `#{==:#{session_name},${name}}`, \"-F\", \"#{session_id}\"];"],
|
|
448
|
+
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
449
|
+
"timeoutSeconds": 180,
|
|
450
|
+
"signature": "[QK:TMUXSESSION-LOOKUP-ENGINE]",
|
|
451
|
+
"signatureSource": "test/mux-fresh-call.test.ts"
|
|
452
|
+
},
|
|
453
|
+
{
|
|
454
|
+
"claim": "TMUXSESSION-NAME-GRAMMAR",
|
|
455
|
+
"title": "the seat name grammar is widened to 'anything non-empty' — tmux then stores a DIFFERENT name than the one requested (or splits it on '.'/':') and the seat can never be found, reported as 'missing' instead of as a shape this rail does not address",
|
|
456
|
+
"subject": "pi-extensions/lib/resolve-tmux-session.ts",
|
|
457
|
+
"find": ["const SESSION_NAME = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;"],
|
|
458
|
+
"replace": ["const SESSION_NAME = /^.+$/;"],
|
|
459
|
+
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
460
|
+
"timeoutSeconds": 180,
|
|
461
|
+
"signature": "[QK:TMUXSESSION-NAME-GRAMMAR]",
|
|
462
|
+
"signatureSource": "test/mux-fresh-call.test.ts"
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
"claim": "FRESHCALL-PLACEMENT-MISSING-REJECTS",
|
|
466
|
+
"title": "an absent seat is read as success — tmux's exit code is ignored, so a missing session falls through to the caller's own session and the launch looks like the seat worked. Proof is in two halves: this mutant dies on the leaf's injected-runner assert plus the source-text ordering assert in the fast lane, and the behavioural oracle independent of that source is the real-tmux seat cell in check-mux-launch-tmux (refusal by name with a byte-identical list-windows -a before and after)",
|
|
467
|
+
"subject": "pi-extensions/lib/resolve-tmux-session.ts",
|
|
468
|
+
"find": ["\tif (result.status !== 0) return { ok: false, reason: \"tmux-session-missing\" };"],
|
|
469
|
+
"replace": [
|
|
470
|
+
"\tif (result.status !== 0 && result.stdout.length > 0) return { ok: false, reason: \"tmux-session-missing\" };"
|
|
471
|
+
],
|
|
472
|
+
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
473
|
+
"timeoutSeconds": 180,
|
|
474
|
+
"signature": "[QK:FRESHCALL-PLACEMENT-MISSING-REJECTS]",
|
|
475
|
+
"signatureSource": "test/mux-fresh-call.test.ts"
|
|
476
|
+
},
|
|
477
|
+
{
|
|
478
|
+
"claim": "FRESHCALL-PLACEMENT-TARGET-ARGV",
|
|
479
|
+
"title": "the launch argv targets the caller's own session again — a seated fresh call silently opens beside the caller instead of in the operator's project session, and the receipt still looks right",
|
|
480
|
+
"subject": "pi-extensions/lib/mux-fresh-call.ts",
|
|
481
|
+
"find": ["\t\t`${targetSessionId}:{end}`,"],
|
|
482
|
+
"replace": ["\t\t`$0:{end}`,"],
|
|
483
|
+
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
484
|
+
"timeoutSeconds": 180,
|
|
485
|
+
"signature": "[QK:FRESHCALL-PLACEMENT-TARGET-ARGV]",
|
|
486
|
+
"signatureSource": "test/mux-fresh-call.test.ts"
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
"claim": "FRESHCALL-PLACEMENT-BACKEND-PARITY",
|
|
490
|
+
"title": "the project seat becomes a pi capability — the grammar refusal is gated on the backend, so claude-code, copilot and omp fall past the seat check into runtime resolution and answer with the wrong reason. Every seat assertion that existed when #105 landed was written against backend pi alone, so nothing would have noticed",
|
|
491
|
+
"subject": "pi-extensions/lib/mux-fresh-call.ts",
|
|
492
|
+
"find": ["\t\tif (badSeat) return { ok: false, reason: badSeat };"],
|
|
493
|
+
"replace": ["\t\tif (badSeat && params.backend === \"pi\") return { ok: false, reason: badSeat };"],
|
|
494
|
+
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
495
|
+
"timeoutSeconds": 180,
|
|
496
|
+
"signature": "[QK:FRESHCALL-PLACEMENT-BACKEND-PARITY]",
|
|
497
|
+
"signatureSource": "test/mux-fresh-call.test.ts"
|
|
498
|
+
},
|
|
499
|
+
{
|
|
500
|
+
"claim": "FRESHCALL-PLACEMENT-RECEIPT-TARGET",
|
|
501
|
+
"title": "the receipt copies the CALLER's session id again — the window is in the seat, the receipt names the caller's session, and every later close/lookup built on that receipt addresses the wrong session. Proof is in two halves: this mutant dies on the source-text assembly assert in the fast lane, and the behavioural oracle independent of that source is the real-tmux seat cell in check-mux-launch-tmux (receipt.sessionId === seatId !== callerId, read from the server's own inventory)",
|
|
502
|
+
"subject": "pi-extensions/lib/mux-fresh-call.ts",
|
|
503
|
+
"find": ["\t\t\tsessionId: targetSessionId,"],
|
|
504
|
+
"replace": ["\t\t\tsessionId: placement.sessionId,"],
|
|
505
|
+
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
506
|
+
"timeoutSeconds": 180,
|
|
507
|
+
"signature": "[QK:FRESHCALL-PLACEMENT-RECEIPT-TARGET]",
|
|
508
|
+
"signatureSource": "test/mux-fresh-call.test.ts"
|
|
509
|
+
},
|
|
510
|
+
{
|
|
511
|
+
"claim": "FRESHCALL-PLACEMENT-SURFACE-PARITY",
|
|
512
|
+
"title": "one surface drops the optional seat from its schema, so opening a sibling in the operator's project session works from pi and silently cannot exist from the MCP bridge",
|
|
513
|
+
"subject": "mcp/entwurf-bridge/src/index.ts",
|
|
514
|
+
"find": [
|
|
515
|
+
"\t\tplacement: z\n\t\t\t.object({\n\t\t\t\ttmuxSession: z\n\t\t\t\t\t.string()\n\t\t\t\t\t.describe(\n\t\t\t\t\t\t\"EXACT name of an EXISTING session on this agent's own tmux server. Nothing is created: an absent session is refused as tmux-session-missing, and a name outside [A-Za-z0-9][A-Za-z0-9_-]* as tmux-session-name-invalid.\",\n\t\t\t\t\t),\n\t\t\t})\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t\"Optional project seat: open the sibling in ONE EXISTING tmux session of this agent's own server instead of the caller's session. Nothing is ever created — an absent session is a refusal, not a new session. Independent of cwd; neither is inferred from the other. The receipt echoes the REQUESTED name and reports the resolved target session id.\",\n\t\t\t),\n"
|
|
516
|
+
],
|
|
517
|
+
"replace": [""],
|
|
518
|
+
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
519
|
+
"timeoutSeconds": 180,
|
|
520
|
+
"signature": "[QK:FRESHCALL-PLACEMENT-SURFACE-PARITY]",
|
|
521
|
+
"signatureSource": "test/fresh-call-surfaces.contract.test.ts"
|
|
441
522
|
}
|
|
442
523
|
]
|
|
443
524
|
}
|
|
@@ -158,16 +158,46 @@
|
|
|
158
158
|
},
|
|
159
159
|
{
|
|
160
160
|
"claim": "OMP-DOCTOR-CARRIER-TRIMS",
|
|
161
|
-
"title": "treating an empty or whitespace-only PI carrier as an inherited identity makes Bundle C's deliberate tmux `-e NAME=` scrub fail the deterministic floor whenever a visible fresh omp citizen is alive",
|
|
161
|
+
"title": "treating an empty or whitespace-only PI carrier as an inherited identity makes Bundle C's deliberate tmux `-e NAME=` scrub fail the deterministic floor whenever a visible fresh omp citizen is alive. Re-anchored twice, and the replanted defect is unchanged both times — only the line it attaches to moved: first when the carrier scan went from an early-exiting `grep -q` to `grep -c` (the SIGPIPE/pipefail repair), then when the scan lost an indentation level because the `[ -d /proc ]` wrapper whose `else` arm was a fail-open `note` was removed (#78 D1)",
|
|
162
162
|
"subject": "scripts/omp-bridge-doctor.sh",
|
|
163
163
|
"find": [
|
|
164
|
-
"
|
|
164
|
+
" HITS=\"$(tr '\\0' '\\n' < \"$ENVIRON\" 2>/dev/null | grep -cE '^(PI_SESSION_ID|PI_AGENT_ID)=.*[^[:space:]]' || true)\""
|
|
165
|
+
],
|
|
166
|
+
"replace": [
|
|
167
|
+
" HITS=\"$(tr '\\0' '\\n' < \"$ENVIRON\" 2>/dev/null | grep -cE '^(PI_SESSION_ID|PI_AGENT_ID)=' || true)\""
|
|
165
168
|
],
|
|
166
|
-
"replace": [" if tr '\\0' '\\n' < \"$ENVIRON\" 2>/dev/null | grep -qE '^(PI_SESSION_ID|PI_AGENT_ID)='; then"],
|
|
167
169
|
"gate": ["bash", "run.sh", "smoke-omp-bridge-state"],
|
|
168
170
|
"timeoutSeconds": 300,
|
|
169
171
|
"signature": "[QK:OMP-DOCTOR-CARRIER-TRIMS]",
|
|
170
172
|
"signatureSource": "scripts/smoke-omp-bridge-state.sh"
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
"claim": "OMP-DOCTOR-CARRIER-NONBLANK-IS-RED",
|
|
176
|
+
"title": "the doctor stops flagging a live omp process that carries a NONBLANK inherited PI_SESSION_ID/PI_AGENT_ID, so a session whose MCP children would speak under the parent pi garden id reads as clean and the doctor prints PASS. The real defect was subtler than this replant, which is why the replant is written this way: `grep -q` exited at the first match, SIGPIPEd `tr` mid-environ, and pipefail turned a MATCH into a failed pipeline — a race on how far `tr` got, misreading ~15% of runs, so a `-q` replant would SURVIVE most of the time and prove nothing. This one makes the same false-clean deterministic. Re-anchored at one indentation level less when the `[ -d /proc ]` wrapper was removed (#78 D1)",
|
|
177
|
+
"subject": "scripts/omp-bridge-doctor.sh",
|
|
178
|
+
"find": [" if [ \"${HITS:-0}\" -gt 0 ]; then"],
|
|
179
|
+
"replace": [" if [ \"${HITS:-0}\" -gt 99 ]; then"],
|
|
180
|
+
"gate": ["bash", "run.sh", "smoke-omp-bridge-state"],
|
|
181
|
+
"timeoutSeconds": 180,
|
|
182
|
+
"signature": "[QK:OMP-DOCTOR-CARRIER-NONBLANK-IS-RED]",
|
|
183
|
+
"signatureSource": "scripts/smoke-omp-bridge-state.sh"
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
"claim": "OMP-DOCTOR-UNENUMERATED-IS-UNVERIFIABLE",
|
|
187
|
+
"title": "restoring `|| true` on the candidate-discovery `pgrep` folds a FAILED enumeration (exit 2, or a `pgrep` that is not on PATH at all) into `nothing matched` (exit 1): the loop receives an empty candidate set, the doctor prints the clean carrier line and PASSes, and a live omp session carrying a foreign pi garden id reads as CLEAN because the tool that would have found it never ran. The documented `ENTWURF_OMP_CARRIER_PIDS` seam cannot substitute for this — it narrows candidates and short-circuits the very enumeration that failed",
|
|
188
|
+
"subject": "scripts/omp-bridge-doctor.sh",
|
|
189
|
+
"find": [
|
|
190
|
+
" ENUM_RC=0",
|
|
191
|
+
" CARRIER_CANDIDATES=\"$(pgrep -x omp 2>/dev/null)\" || ENUM_RC=$?",
|
|
192
|
+
" if [ \"$ENUM_RC\" -gt 1 ]; then",
|
|
193
|
+
" ENUM_FAILED=\"$ENUM_RC\"",
|
|
194
|
+
" fi"
|
|
195
|
+
],
|
|
196
|
+
"replace": [" CARRIER_CANDIDATES=\"$(pgrep -x omp 2>/dev/null || true)\""],
|
|
197
|
+
"gate": ["bash", "run.sh", "smoke-omp-bridge-state"],
|
|
198
|
+
"timeoutSeconds": 300,
|
|
199
|
+
"signature": "[QK:OMP-DOCTOR-UNENUMERATED-IS-UNVERIFIABLE]",
|
|
200
|
+
"signatureSource": "scripts/smoke-omp-bridge-state.sh"
|
|
171
201
|
}
|
|
172
202
|
]
|
|
173
203
|
}
|
|
@@ -178,6 +178,19 @@
|
|
|
178
178
|
"timeoutSeconds": 180,
|
|
179
179
|
"signature": "[QK:SETUP-OMP-CONFIG-NO-OVERWRITE]",
|
|
180
180
|
"signatureSource": "scripts/check-setup-qualification.sh"
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"claim": "SETUP-DARWIN-RAIL-COSMETIC-PASS",
|
|
184
|
+
"title": "the platform rail check is dropped, so a harness whose install completes on a platform with no rail receipt reads PASS and the whole setup reads green again — the 0.20.0 hole the installer's retired Darwin refusal used to cover",
|
|
185
|
+
"subject": "run.sh",
|
|
186
|
+
"find": [
|
|
187
|
+
" if harness_rail_certified_platform; then\n setup_result \"$1\" PASS \"$2 — verify: $3\"\n return 0\n fi"
|
|
188
|
+
],
|
|
189
|
+
"replace": [" setup_result \"$1\" PASS \"$2 — verify: $3\"\n return 0"],
|
|
190
|
+
"gate": ["bash", "scripts/check-setup-qualification.sh"],
|
|
191
|
+
"timeoutSeconds": 180,
|
|
192
|
+
"signature": "[QK:SETUP-DARWIN-RAIL-COSMETIC-PASS]",
|
|
193
|
+
"signatureSource": "scripts/check-setup-qualification.sh"
|
|
181
194
|
}
|
|
182
195
|
]
|
|
183
196
|
}
|