@junghanacs/entwurf 0.14.1 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +8 -5
- package/CHANGELOG.md +113 -0
- package/DELIVERY.md +73 -3
- package/README.md +55 -4
- package/VERIFY.md +6 -6
- package/demo/README.md +3 -1
- package/demo/demo-baseline.sh +12 -1
- package/demo/demo.sh +9 -1
- package/docs/acp-backend-rail.md +103 -4
- package/docs/external-mcp-host.md +26 -4
- package/docs/setup-clean-host.md +3 -3
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +8 -6
- package/mcp/entwurf-bridge/dist/pi-extensions/entwurf-capabilities.json +1 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/copilot-fresh-preflight.js +253 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-sender-identity.js +10 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js +102 -28
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +77 -11
- package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-hook-copilot.js +288 -0
- package/mcp/entwurf-bridge/dist/scripts/doctor-pi-provider.js +139 -47
- package/mcp/entwurf-bridge/dist/scripts/probe-bridge-command.js +294 -0
- package/mcp/entwurf-bridge/src/index.ts +10 -6
- package/mcp/entwurf-bridge/tsconfig.build.json +17 -6
- package/package.json +23 -12
- package/pi/copilot-receive/entwurf-receive/extension.mjs +323 -0
- package/pi/entwurf-capabilities.json +1 -0
- package/pi/meta-bridge-copilot/.claude-plugin/marketplace.json +11 -0
- package/pi/meta-bridge-copilot/entwurf-meta-receive-copilot/.claude-plugin/plugin.json +5 -0
- package/pi/meta-bridge-copilot/entwurf-meta-receive-copilot/hooks/hooks.json +7 -0
- package/pi/meta-bridge-copilot/entwurf-meta-receive-copilot/scripts/copilot-hook-launch.sh +85 -0
- package/pi-extensions/entwurf-control.ts +17 -8
- package/pi-extensions/lib/acp/backend.ts +229 -9
- package/pi-extensions/lib/copilot-fresh-preflight.ts +251 -0
- package/pi-extensions/lib/meta-sender-identity.ts +10 -2
- package/pi-extensions/lib/meta-session.ts +102 -29
- package/pi-extensions/lib/mux-fresh-call.ts +82 -11
- package/pi-extensions/meta-bridge-hook-copilot.ts +323 -0
- package/run.sh +389 -26
- package/scripts/agy-bridge-config.py +47 -13
- package/scripts/agy-bridge.sh +73 -23
- package/scripts/check-acp-prompt-lifecycle.ts +221 -9
- package/scripts/check-copilot-birth-hook.ts +497 -0
- package/scripts/check-copilot-launch.ts +395 -0
- package/scripts/check-copilot-receive-arm.ts +734 -0
- package/scripts/check-copilot-statusline.ts +122 -0
- package/scripts/check-entwurf-bridge-boot.ts +28 -0
- package/scripts/check-entwurf-capabilities.ts +25 -6
- package/scripts/check-fresh-cut-gate.sh +92 -23
- package/scripts/check-gate-qualification.ts +10 -3
- package/scripts/check-install-container.sh +2 -2
- package/scripts/check-install-surface.ts +1 -1
- package/scripts/check-meta-capability-source.ts +89 -5
- package/scripts/check-meta-doctor-oracle.sh +26 -0
- package/scripts/check-meta-manifest-schema.py +38 -0
- package/scripts/check-meta-receiver-marker.ts +25 -2
- package/scripts/check-meta-session.ts +32 -0
- package/scripts/check-probe-bridge-command.ts +201 -0
- package/scripts/check-release-gate-outcomes.ts +54 -1
- package/scripts/copilot-bridge-doctor.sh +209 -0
- package/scripts/copilot-bridge-install.sh +224 -0
- package/scripts/copilot-launch.sh +236 -0
- package/scripts/copilot-mcp-bridge.sh +177 -0
- package/scripts/copilot-mcp-config.py +221 -0
- package/scripts/copilot-receive-bridge.sh +483 -0
- package/scripts/copilot-statusline-bridge.sh +149 -0
- package/scripts/copilot-statusline-config.py +201 -0
- package/scripts/copilot-statusline.sh +68 -0
- package/scripts/dev-bin.sh +4 -1
- package/scripts/doctor-pi-provider.ts +155 -51
- package/scripts/meta-bridge-hook-log.sh +17 -5
- package/scripts/mutants/acp-prompt-lifecycle.json +25 -3
- package/scripts/mutants/bridge-command-boot.json +107 -0
- package/scripts/mutants/capability-cache.json +64 -0
- package/scripts/mutants/copilot-birth.json +155 -0
- package/scripts/mutants/copilot-launch.json +187 -0
- package/scripts/mutants/copilot-receive.json +263 -0
- package/scripts/mutants/fresh-cut.json +17 -0
- package/scripts/mutants/mux-fresh-call.json +216 -2
- package/scripts/mutants/pack-install.json +17 -0
- package/scripts/mutants/release-gate.json +13 -0
- package/scripts/probe-bridge-command.ts +330 -0
- package/scripts/raw-async-delivery/README.md +203 -1
- package/scripts/raw-async-delivery/copilot-enqueue-addressed.sh +35 -0
- package/scripts/raw-async-delivery/copilot-extension-receive/extension.mjs +123 -0
- package/scripts/raw-async-delivery/copilot-ui-server-probe.mjs +337 -0
- package/scripts/smoke-acp-raw-turn-live.ts +1 -1
- package/scripts/smoke-agy-install-state.sh +76 -2
- package/scripts/smoke-copilot-mcp-state.sh +153 -0
- package/scripts/smoke-copilot-statusline-state.sh +131 -0
- package/scripts/smoke-entwurf-chain-live.ts +1 -1
- package/scripts/smoke-entwurf-v2-matrix-live.ts +2 -2
- package/scripts/smoke-mux-fresh-call-live.ts +3 -1
- package/scripts/smoke-mux-lifecycle-live.ts +4 -2
- package/scripts/smoke-pi-provider-state.sh +135 -6
- package/scripts/smoke-resident-garden-guard.sh +2 -2
- package/scripts/tsconfig.json +1 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# smoke-copilot-mcp-state — hermetic install/doctor/inverse contract for
|
|
3
|
+
# the Copilot MCP-config adapter. No Copilot process or model turn.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
7
|
+
RUN="$REPO_DIR/run.sh"
|
|
8
|
+
pass=0
|
|
9
|
+
ok() { printf ' ok %s\n' "$1"; pass=$((pass + 1)); }
|
|
10
|
+
die() { printf 'FAIL: %s\n' "$1" >&2; exit 1; }
|
|
11
|
+
want() { eval "$2" && ok "$1" || die "$1"; }
|
|
12
|
+
|
|
13
|
+
REPO_BEFORE="$(cd "$REPO_DIR" && git status --porcelain)"
|
|
14
|
+
SB="$(mktemp -d)"
|
|
15
|
+
trap 'rm -rf "$SB"' EXIT
|
|
16
|
+
export HOME="$SB/home"
|
|
17
|
+
export XDG_DATA_HOME="$SB/xdg"
|
|
18
|
+
export COPILOT_MCP_CONFIG="$HOME/.copilot/mcp-config.json"
|
|
19
|
+
export COPILOT_MCP_COMMAND="entwurf-bridge"
|
|
20
|
+
STATE="$XDG_DATA_HOME/entwurf/copilot-mcp/install-state.json"
|
|
21
|
+
mkdir -p "$(dirname "$COPILOT_MCP_CONFIG")" "$SB/bin"
|
|
22
|
+
|
|
23
|
+
write_mcp_fake() {
|
|
24
|
+
cat > "$1" <<'FAKE'
|
|
25
|
+
#!/usr/bin/env bash
|
|
26
|
+
while IFS= read -r line; do
|
|
27
|
+
case "$line" in
|
|
28
|
+
*'"id":1'*) printf '%s\n' '{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2024-11-05","capabilities":{},"serverInfo":{"name":"fake-entwurf-bridge","version":"0"}}}' ;;
|
|
29
|
+
*'"id":2'*) printf '%s\n' '{"jsonrpc":"2.0","id":2,"result":{"tools":[{"name":"entwurf_v2"},{"name":"entwurf_self"},{"name":"entwurf_peers"},{"name":"entwurf_inbox_read"},{"name":"entwurf_register_native"},{"name":"entwurf_fresh_call"},{"name":"entwurf_resume_call"}]}}' ;;
|
|
30
|
+
esac
|
|
31
|
+
done
|
|
32
|
+
FAKE
|
|
33
|
+
chmod +x "$1"
|
|
34
|
+
}
|
|
35
|
+
write_dead_fake() {
|
|
36
|
+
cat > "$1" <<'FAKE'
|
|
37
|
+
#!/usr/bin/env bash
|
|
38
|
+
echo "bash: /nonexistent/entwurf-bridge: No such file or directory" >&2
|
|
39
|
+
exit 127
|
|
40
|
+
FAKE
|
|
41
|
+
chmod +x "$1"
|
|
42
|
+
}
|
|
43
|
+
write_mcp_fake "$SB/bin/$COPILOT_MCP_COMMAND"
|
|
44
|
+
|
|
45
|
+
_clean_path=""
|
|
46
|
+
while IFS= read -r dir; do
|
|
47
|
+
[ -n "$dir" ] || continue
|
|
48
|
+
[ -e "$dir/$COPILOT_MCP_COMMAND" ] || [ -L "$dir/$COPILOT_MCP_COMMAND" ] && continue
|
|
49
|
+
_clean_path="${_clean_path:+$_clean_path:}$dir"
|
|
50
|
+
done < <(printf '%s\n' "$PATH" | tr ':' '\n')
|
|
51
|
+
export PATH="$SB/bin:$_clean_path"
|
|
52
|
+
|
|
53
|
+
write_config() {
|
|
54
|
+
printf '{\n "mcpServers": {\n "other": { "type": "local", "command": "keep-me" },\n "entwurf-bridge": { "type": "local", "command": "manual-bridge", "args": [] }\n }\n}\n' > "$COPILOT_MCP_CONFIG"
|
|
55
|
+
}
|
|
56
|
+
json() { python3 - "$COPILOT_MCP_CONFIG" "$@" <<'PY'
|
|
57
|
+
import json, sys
|
|
58
|
+
data = json.load(open(sys.argv[1]))
|
|
59
|
+
mode = sys.argv[2]
|
|
60
|
+
servers = data["mcpServers"]
|
|
61
|
+
ours = {"type": "local", "command": "entwurf-bridge", "args": []}
|
|
62
|
+
if mode == "installed":
|
|
63
|
+
assert servers["entwurf-bridge"] == ours
|
|
64
|
+
assert servers["other"] == {"type": "local", "command": "keep-me"}
|
|
65
|
+
assert set(data.keys()) == {"mcpServers"}
|
|
66
|
+
elif mode == "manual":
|
|
67
|
+
assert servers["entwurf-bridge"] == {"type": "local", "command": "manual-bridge", "args": []}
|
|
68
|
+
assert servers["other"] == {"type": "local", "command": "keep-me"}
|
|
69
|
+
elif mode == "created":
|
|
70
|
+
assert data == {"mcpServers": {"entwurf-bridge": ours}}
|
|
71
|
+
else:
|
|
72
|
+
raise AssertionError(mode)
|
|
73
|
+
PY
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
# Existing matching/manual config is adopted, not treated as a fresh file.
|
|
77
|
+
write_config
|
|
78
|
+
"$RUN" doctor-copilot-mcp >/dev/null
|
|
79
|
+
want "unowned manual config doctor is an honest note" "[ $? -eq 0 ]"
|
|
80
|
+
"$RUN" install-copilot-mcp >/dev/null
|
|
81
|
+
json installed
|
|
82
|
+
want "install adopts regular config and preserves unrelated servers" "[ $? -eq 0 ]"
|
|
83
|
+
want "install writes state" "[ -f '$STATE' ]"
|
|
84
|
+
"$RUN" doctor-copilot-mcp >/dev/null
|
|
85
|
+
want "owned configured doctor is green" "[ $? -eq 0 ]"
|
|
86
|
+
python3 - "$STATE" <<'PY'
|
|
87
|
+
import json, sys
|
|
88
|
+
state = json.load(open(sys.argv[1]))
|
|
89
|
+
assert state["preimage"] == {"type": "local", "command": "manual-bridge", "args": []}
|
|
90
|
+
assert state["serverKey"] == "entwurf-bridge"
|
|
91
|
+
PY
|
|
92
|
+
ok "first install captures manual entwurf-bridge preimage"
|
|
93
|
+
"$RUN" install-copilot-mcp >/dev/null
|
|
94
|
+
"$RUN" uninstall-copilot-mcp >/dev/null
|
|
95
|
+
json manual
|
|
96
|
+
want "reinstall keeps the first preimage for inverse" "[ $? -eq 0 ] && [ ! -f '$STATE' ]"
|
|
97
|
+
|
|
98
|
+
# The live-host shape is already our command with no state: adopt that exact preimage too.
|
|
99
|
+
write_config
|
|
100
|
+
python3 -c 'import json,sys; p=sys.argv[1]; d=json.load(open(p)); d["mcpServers"]["entwurf-bridge"]={"type":"local","command":"entwurf-bridge","args":[]}; json.dump(d,open(p,"w"))' "$COPILOT_MCP_CONFIG"
|
|
101
|
+
"$RUN" install-copilot-mcp >/dev/null
|
|
102
|
+
python3 - "$STATE" <<'PY'
|
|
103
|
+
import json, sys
|
|
104
|
+
state = json.load(open(sys.argv[1]))
|
|
105
|
+
assert state["preimage"] == {"type": "local", "command": "entwurf-bridge", "args": []}
|
|
106
|
+
PY
|
|
107
|
+
"$RUN" uninstall-copilot-mcp >/dev/null
|
|
108
|
+
json installed
|
|
109
|
+
want "matching manual Copilot MCP config is adopted and restored unchanged" "[ $? -eq 0 ] && [ ! -f '$STATE' ]"
|
|
110
|
+
|
|
111
|
+
# State turns a later config drift into a red doctor rather than a silent pass.
|
|
112
|
+
write_config
|
|
113
|
+
"$RUN" install-copilot-mcp >/dev/null
|
|
114
|
+
python3 -c 'import json,sys; p=sys.argv[1]; d=json.load(open(p)); d["mcpServers"]["entwurf-bridge"]={"type":"stdio","command":"entwurf-bridge","args":[]}; json.dump(d,open(p,"w"))' "$COPILOT_MCP_CONFIG"
|
|
115
|
+
if "$RUN" doctor-copilot-mcp >/dev/null 2>&1; then die "type=stdio doctor should fail"; fi
|
|
116
|
+
ok "state-present type=stdio entry is red"
|
|
117
|
+
"$RUN" install-copilot-mcp >/dev/null
|
|
118
|
+
python3 -c 'import json,sys; p=sys.argv[1]; d=json.load(open(p)); d["mcpServers"]["entwurf-bridge"]={"type":"local","command":"other","args":[]}; json.dump(d,open(p,"w"))' "$COPILOT_MCP_CONFIG"
|
|
119
|
+
if "$RUN" doctor-copilot-mcp >/dev/null 2>&1; then die "command drift doctor should fail"; fi
|
|
120
|
+
ok "state-present command drift is red"
|
|
121
|
+
"$RUN" install-copilot-mcp >/dev/null
|
|
122
|
+
|
|
123
|
+
# A stable-bin command that boots then dies is red without touching config.
|
|
124
|
+
write_dead_fake "$SB/bin/$COPILOT_MCP_COMMAND"
|
|
125
|
+
if "$RUN" doctor-copilot-mcp >/dev/null 2>&1; then die "dead command doctor should fail"; fi
|
|
126
|
+
ok "state-present command that does not serve MCP is red"
|
|
127
|
+
write_mcp_fake "$SB/bin/$COPILOT_MCP_COMMAND"
|
|
128
|
+
"$RUN" uninstall-copilot-mcp >/dev/null
|
|
129
|
+
json manual
|
|
130
|
+
want "inverse restores manual config after drift repairs" "[ $? -eq 0 ] && [ ! -f '$STATE' ]"
|
|
131
|
+
|
|
132
|
+
# Created-new is removed only when it still contains only the adapter's key.
|
|
133
|
+
rm -f "$COPILOT_MCP_CONFIG"
|
|
134
|
+
"$RUN" install-copilot-mcp >/dev/null
|
|
135
|
+
json created
|
|
136
|
+
"$RUN" uninstall-copilot-mcp >/dev/null
|
|
137
|
+
want "created-new empty mcp-config is removed by inverse" "[ ! -e '$COPILOT_MCP_CONFIG' ] && [ ! -f '$STATE' ]"
|
|
138
|
+
|
|
139
|
+
# A foreign/corrupt config file is never adopted or given a state record.
|
|
140
|
+
printf '{"mcpServers":{}}\n' > "$SB/foreign.json"
|
|
141
|
+
ln -s "$SB/foreign.json" "$COPILOT_MCP_CONFIG"
|
|
142
|
+
if "$RUN" install-copilot-mcp >/dev/null 2>&1; then die "symlink install should refuse"; fi
|
|
143
|
+
ok "symlink config is refused"
|
|
144
|
+
want "symlink refusal leaves foreign config and no state" "! grep -q entwurf-bridge '$SB/foreign.json' && [ ! -f '$STATE' ]"
|
|
145
|
+
rm -f "$COPILOT_MCP_CONFIG"
|
|
146
|
+
printf 'not-json{{' > "$COPILOT_MCP_CONFIG"
|
|
147
|
+
if "$RUN" install-copilot-mcp >/dev/null 2>&1; then die "corrupt install should fail"; fi
|
|
148
|
+
ok "corrupt config fails loud"
|
|
149
|
+
want "corrupt refusal writes no state" "[ ! -f '$STATE' ]"
|
|
150
|
+
|
|
151
|
+
REPO_AFTER="$(cd "$REPO_DIR" && git status --porcelain)"
|
|
152
|
+
[ "$REPO_BEFORE" = "$REPO_AFTER" ] || die "smoke changed the checkout"
|
|
153
|
+
printf '\nsmoke-copilot-mcp-state: %d checks passed\n' "$pass"
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# smoke-copilot-statusline-state — hermetic install/doctor/inverse contract for
|
|
3
|
+
# the Copilot custom-footer settings adapter. No Copilot process or model turn.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
7
|
+
RUN="$REPO_DIR/run.sh"
|
|
8
|
+
pass=0
|
|
9
|
+
ok() { printf ' ok %s\n' "$1"; pass=$((pass + 1)); }
|
|
10
|
+
die() { printf 'FAIL: %s\n' "$1" >&2; exit 1; }
|
|
11
|
+
want() { eval "$2" && ok "$1" || die "$1"; }
|
|
12
|
+
|
|
13
|
+
REPO_BEFORE="$(cd "$REPO_DIR" && git status --porcelain)"
|
|
14
|
+
SB="$(mktemp -d)"
|
|
15
|
+
trap 'rm -rf "$SB"' EXIT
|
|
16
|
+
export HOME="$SB/home"
|
|
17
|
+
export XDG_DATA_HOME="$SB/xdg"
|
|
18
|
+
export COPILOT_SETTINGS_CONFIG="$HOME/.copilot/settings.json"
|
|
19
|
+
export COPILOT_STATUSLINE_COMMAND="entwurf-copilot-statusline"
|
|
20
|
+
STATE="$XDG_DATA_HOME/entwurf/copilot-statusline/install-state.json"
|
|
21
|
+
mkdir -p "$(dirname "$COPILOT_SETTINGS_CONFIG")" "$SB/bin"
|
|
22
|
+
printf '#!/usr/bin/env bash\necho fake-copilot-statusline\n' > "$SB/bin/$COPILOT_STATUSLINE_COMMAND"
|
|
23
|
+
chmod +x "$SB/bin/$COPILOT_STATUSLINE_COMMAND"
|
|
24
|
+
|
|
25
|
+
_clean_path=""
|
|
26
|
+
while IFS= read -r dir; do
|
|
27
|
+
[ -n "$dir" ] || continue
|
|
28
|
+
[ -e "$dir/$COPILOT_STATUSLINE_COMMAND" ] || [ -L "$dir/$COPILOT_STATUSLINE_COMMAND" ] && continue
|
|
29
|
+
_clean_path="${_clean_path:+$_clean_path:}$dir"
|
|
30
|
+
done < <(printf '%s\n' "$PATH" | tr ':' '\n')
|
|
31
|
+
export PATH="$SB/bin:$_clean_path"
|
|
32
|
+
|
|
33
|
+
write_settings() {
|
|
34
|
+
printf '{\n "enabledPlugins": { "other@marketplace": true },\n "footer": { "showCustom": true, "showDirectory": true },\n "statusLine": { "command": "manual-statusline" }\n}\n' > "$COPILOT_SETTINGS_CONFIG"
|
|
35
|
+
}
|
|
36
|
+
json() { python3 - "$COPILOT_SETTINGS_CONFIG" "$@" <<'PY'
|
|
37
|
+
import json, sys
|
|
38
|
+
settings = json.load(open(sys.argv[1]))
|
|
39
|
+
mode = sys.argv[2]
|
|
40
|
+
if mode == "installed":
|
|
41
|
+
assert settings["statusLine"] == {"command": "entwurf-copilot-statusline"}
|
|
42
|
+
assert settings["footer"] == {"showCustom": True, "showDirectory": True}
|
|
43
|
+
assert settings["enabledPlugins"] == {"other@marketplace": True}
|
|
44
|
+
elif mode == "manual":
|
|
45
|
+
assert settings["statusLine"] == {"command": "manual-statusline"}
|
|
46
|
+
assert settings["footer"] == {"showCustom": True, "showDirectory": True}
|
|
47
|
+
elif mode == "created":
|
|
48
|
+
assert settings == {"statusLine": {"command": "entwurf-copilot-statusline"}, "footer": {"showCustom": True}}
|
|
49
|
+
else:
|
|
50
|
+
raise AssertionError(mode)
|
|
51
|
+
PY
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
# Existing matching/manual settings are adopted, not treated as a fresh file.
|
|
55
|
+
write_settings
|
|
56
|
+
"$RUN" doctor-copilot-statusline >/dev/null
|
|
57
|
+
want "unowned manual settings doctor is an honest note" "[ $? -eq 0 ]"
|
|
58
|
+
"$RUN" install-copilot-statusline >/dev/null
|
|
59
|
+
json installed
|
|
60
|
+
want "install adopts regular settings and preserves unrelated keys" "[ $? -eq 0 ]"
|
|
61
|
+
want "install writes state" "[ -f '$STATE' ]"
|
|
62
|
+
python3 - "$STATE" <<'PY'
|
|
63
|
+
import json, sys
|
|
64
|
+
state = json.load(open(sys.argv[1]))
|
|
65
|
+
assert state["statusLinePreimage"] == {"command": "manual-statusline"}
|
|
66
|
+
assert state["showCustomPreimage"] is True
|
|
67
|
+
PY
|
|
68
|
+
ok "first install captures manual statusLine preimage"
|
|
69
|
+
"$RUN" install-copilot-statusline >/dev/null
|
|
70
|
+
"$RUN" uninstall-copilot-statusline >/dev/null
|
|
71
|
+
json manual
|
|
72
|
+
want "reinstall keeps the first preimage for inverse" "[ $? -eq 0 ] && [ ! -f '$STATE' ]"
|
|
73
|
+
|
|
74
|
+
# The live-host shape is already our command with no state: adopt that exact preimage too.
|
|
75
|
+
write_settings
|
|
76
|
+
python3 -c 'import json,sys; p=sys.argv[1]; d=json.load(open(p)); d["statusLine"]={"command":"entwurf-copilot-statusline"}; json.dump(d,open(p,"w"))' "$COPILOT_SETTINGS_CONFIG"
|
|
77
|
+
"$RUN" install-copilot-statusline >/dev/null
|
|
78
|
+
python3 - "$STATE" <<'PY'
|
|
79
|
+
import json, sys
|
|
80
|
+
state = json.load(open(sys.argv[1]))
|
|
81
|
+
assert state["statusLinePreimage"] == {"command": "entwurf-copilot-statusline"}
|
|
82
|
+
assert state["showCustomPreimage"] is True
|
|
83
|
+
PY
|
|
84
|
+
"$RUN" uninstall-copilot-statusline >/dev/null
|
|
85
|
+
json installed
|
|
86
|
+
want "matching manual Copilot config is adopted and restored unchanged" "[ $? -eq 0 ] && [ ! -f '$STATE' ]"
|
|
87
|
+
|
|
88
|
+
# State turns a later setting drift into a red doctor rather than a silent pass.
|
|
89
|
+
write_settings
|
|
90
|
+
"$RUN" install-copilot-statusline >/dev/null
|
|
91
|
+
python3 -c 'import json,sys; p=sys.argv[1]; d=json.load(open(p)); d["footer"]["showCustom"]=False; json.dump(d,open(p,"w"))' "$COPILOT_SETTINGS_CONFIG"
|
|
92
|
+
if "$RUN" doctor-copilot-statusline >/dev/null 2>&1; then die "custom-disabled doctor should fail"; fi
|
|
93
|
+
ok "state-present custom-disabled footer is red"
|
|
94
|
+
"$RUN" install-copilot-statusline >/dev/null
|
|
95
|
+
python3 -c 'import json,sys; p=sys.argv[1]; d=json.load(open(p)); d["statusLine"]={"command":"other"}; json.dump(d,open(p,"w"))' "$COPILOT_SETTINGS_CONFIG"
|
|
96
|
+
if "$RUN" doctor-copilot-statusline >/dev/null 2>&1; then die "statusLine drift doctor should fail"; fi
|
|
97
|
+
ok "state-present statusLine drift is red"
|
|
98
|
+
"$RUN" install-copilot-statusline >/dev/null
|
|
99
|
+
|
|
100
|
+
# A stable-bin command that stops resolving is red without touching settings.
|
|
101
|
+
rm -f "$SB/bin/$COPILOT_STATUSLINE_COMMAND"
|
|
102
|
+
if "$RUN" doctor-copilot-statusline >/dev/null 2>&1; then die "dangling command doctor should fail"; fi
|
|
103
|
+
ok "dangling bare command is red"
|
|
104
|
+
printf '#!/usr/bin/env bash\nexit 0\n' > "$SB/bin/$COPILOT_STATUSLINE_COMMAND"
|
|
105
|
+
chmod +x "$SB/bin/$COPILOT_STATUSLINE_COMMAND"
|
|
106
|
+
"$RUN" uninstall-copilot-statusline >/dev/null
|
|
107
|
+
json manual
|
|
108
|
+
want "inverse restores manual settings after drift repairs" "[ $? -eq 0 ] && [ ! -f '$STATE' ]"
|
|
109
|
+
|
|
110
|
+
# Created-new is removed only when it still contains only the adapter's keys.
|
|
111
|
+
rm -f "$COPILOT_SETTINGS_CONFIG"
|
|
112
|
+
"$RUN" install-copilot-statusline >/dev/null
|
|
113
|
+
json created
|
|
114
|
+
"$RUN" uninstall-copilot-statusline >/dev/null
|
|
115
|
+
want "created-new empty settings file is removed by inverse" "[ ! -e '$COPILOT_SETTINGS_CONFIG' ] && [ ! -f '$STATE' ]"
|
|
116
|
+
|
|
117
|
+
# A foreign/corrupt settings file is never adopted or given a state record.
|
|
118
|
+
printf '{"footer":{}}\n' > "$SB/foreign.json"
|
|
119
|
+
ln -s "$SB/foreign.json" "$COPILOT_SETTINGS_CONFIG"
|
|
120
|
+
if "$RUN" install-copilot-statusline >/dev/null 2>&1; then die "symlink install should refuse"; fi
|
|
121
|
+
ok "symlink settings are refused"
|
|
122
|
+
want "symlink refusal leaves foreign settings and no state" "! grep -q statusLine '$SB/foreign.json' && [ ! -f '$STATE' ]"
|
|
123
|
+
rm -f "$COPILOT_SETTINGS_CONFIG"
|
|
124
|
+
printf 'not-json{{' > "$COPILOT_SETTINGS_CONFIG"
|
|
125
|
+
if "$RUN" install-copilot-statusline >/dev/null 2>&1; then die "corrupt install should fail"; fi
|
|
126
|
+
ok "corrupt settings fail loud"
|
|
127
|
+
want "corrupt refusal writes no state" "[ ! -f '$STATE' ]"
|
|
128
|
+
|
|
129
|
+
REPO_AFTER="$(cd "$REPO_DIR" && git status --porcelain)"
|
|
130
|
+
[ "$REPO_BEFORE" = "$REPO_AFTER" ] || die "smoke changed the checkout"
|
|
131
|
+
printf '\nsmoke-copilot-statusline-state: %d checks passed\n' "$pass"
|
|
@@ -63,7 +63,7 @@ const REPO_EXTENSION_ARGS = ["--no-extensions", "-e", REPO_ROOT] as const;
|
|
|
63
63
|
const REAL_CONTROL_DIR = path.join(os.homedir(), ".pi", "entwurf-control");
|
|
64
64
|
const SOCKET_SUFFIX = ".sock";
|
|
65
65
|
|
|
66
|
-
const GPT_TARGET = process.env.ENTWURF_CHAIN_GPT_TARGET?.trim() || "openai-codex/gpt-5.
|
|
66
|
+
const GPT_TARGET = process.env.ENTWURF_CHAIN_GPT_TARGET?.trim() || "openai-codex/gpt-5.6-luna";
|
|
67
67
|
const ACP_TARGET = process.env.ENTWURF_CHAIN_ACP_TARGET?.trim() || "entwurf/claude-sonnet-5";
|
|
68
68
|
const BOOT_TIMEOUT_MS = 45_000;
|
|
69
69
|
const CLAUDE_TURN_TIMEOUT_MS = Number(process.env.ENTWURF_CHAIN_CLAUDE_TIMEOUT_MS) || 240_000;
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
*
|
|
37
37
|
* LIVE-only (spawns a real pi, opens a real socket) — kept OUT of `pnpm check`; honest skip when
|
|
38
38
|
* LIVE!=1 (a release-gate that hard-fails without auth/model is unrunnable unattended). Model:
|
|
39
|
-
* ENTWURF_LIVE_TARGET = "<provider>/<model>" (default "openai-codex/gpt-5.
|
|
39
|
+
* ENTWURF_LIVE_TARGET = "<provider>/<model>" (default "openai-codex/gpt-5.6-luna")
|
|
40
40
|
* (or split: ENTWURF_LIVE_PROVIDER + ENTWURF_LIVE_MODEL)
|
|
41
41
|
* LIVE=1 ./run.sh smoke-entwurf-v2-matrix-live
|
|
42
42
|
*
|
|
@@ -101,7 +101,7 @@ function resolveTarget(): { provider: string; model: string } {
|
|
|
101
101
|
}
|
|
102
102
|
return {
|
|
103
103
|
provider: process.env.ENTWURF_LIVE_PROVIDER?.trim() || "openai-codex",
|
|
104
|
-
model: process.env.ENTWURF_LIVE_MODEL?.trim() || "gpt-5.
|
|
104
|
+
model: process.env.ENTWURF_LIVE_MODEL?.trim() || "gpt-5.6-luna",
|
|
105
105
|
};
|
|
106
106
|
}
|
|
107
107
|
|
|
@@ -43,7 +43,7 @@ import { skipLive } from "./lib/live-skip.ts";
|
|
|
43
43
|
const LABEL = "smoke-mux-fresh-call-live";
|
|
44
44
|
const CALLBACK_WAIT_MS = 180_000;
|
|
45
45
|
const LIVE_MODEL = {
|
|
46
|
-
pi: "openai-codex/gpt-5.6-
|
|
46
|
+
pi: "openai-codex/gpt-5.6-luna",
|
|
47
47
|
"claude-code": "claude-sonnet-5",
|
|
48
48
|
} as const;
|
|
49
49
|
|
|
@@ -213,6 +213,8 @@ async function main(): Promise<void> {
|
|
|
213
213
|
// onboarding instead of exercising the configured runtime. Separate servers preserve
|
|
214
214
|
// those backend-native environments without adding an env carrier to the product.
|
|
215
215
|
const nonces = new Map<string, string>();
|
|
216
|
+
// Intentional: pi + claude-code only. Copilot clause-7 LIVE is operator-metered
|
|
217
|
+
// and is not a release MUST — do not add `copilot` to this loop (VERIFY.md).
|
|
216
218
|
for (const backend of ["pi", "claude-code"] as const) {
|
|
217
219
|
const socket = path.join(root, `${backend}.sock`);
|
|
218
220
|
privateSockets.add(socket);
|
|
@@ -108,7 +108,7 @@ const BRIDGE_LAUNCHER = path.join(REPO, "mcp", "entwurf-bridge", "start.sh");
|
|
|
108
108
|
|
|
109
109
|
/** The two pi shapes. They differ in exactly one thing that matters here — whether the record
|
|
110
110
|
* carries `provider=entwurf`, which is what makes the resume argv re-inject the bridge (#29). */
|
|
111
|
-
const PI_NATIVE_MODEL = "openai-codex/gpt-5.6-
|
|
111
|
+
const PI_NATIVE_MODEL = "openai-codex/gpt-5.6-luna";
|
|
112
112
|
const PI_ACP_MODEL = "entwurf/claude-sonnet-5";
|
|
113
113
|
const CLAUDE_MODEL = "claude-sonnet-5";
|
|
114
114
|
|
|
@@ -557,7 +557,9 @@ async function main(): Promise<void> {
|
|
|
557
557
|
return sender;
|
|
558
558
|
}
|
|
559
559
|
|
|
560
|
-
/** One private tmux server per CELL, plus the client env that anchors into it.
|
|
560
|
+
/** One private tmux server per CELL, plus the client env that anchors into it.
|
|
561
|
+
* Intentional: pi + claude-code only. Copilot clause-7 LIVE is operator-metered
|
|
562
|
+
* and is not a release MUST — do not widen this type (VERIFY.md). */
|
|
561
563
|
function startPrivateServer(
|
|
562
564
|
cell: string,
|
|
563
565
|
backend: "pi" | "claude-code",
|
|
@@ -31,6 +31,14 @@ REPO_BEFORE="$(cd "$REPO_DIR" && git status --porcelain)"
|
|
|
31
31
|
|
|
32
32
|
SB="$(mktemp -d)"
|
|
33
33
|
trap 'rm -rf "$SB"' EXIT
|
|
34
|
+
# This smoke drives register-pi-provider.py, which imports the sibling pi_settings_io module — and
|
|
35
|
+
# CPython writes scripts/__pycache__/*.pyc for that import, INSIDE the checkout. The purity cell
|
|
36
|
+
# below never caught it because those bytes are gitignored, so `git status --porcelain` stays clean
|
|
37
|
+
# while the working tree is not. That difference is invisible here and load-bearing under gate
|
|
38
|
+
# qualification, whose snapshot judges tree cleanliness rather than porcelain output. Write no
|
|
39
|
+
# bytecode instead of cleaning it up afterwards: a smoke that leaves artifacts and then sweeps them
|
|
40
|
+
# is one early `exit` away from leaving them for real.
|
|
41
|
+
export PYTHONDONTWRITEBYTECODE=1
|
|
34
42
|
export HOME="$SB/home"
|
|
35
43
|
export XDG_DATA_HOME="$SB/xdg"
|
|
36
44
|
GLOBAL="$SB/global.json"
|
|
@@ -46,8 +54,52 @@ LEGACY_CMD="$FAKE_REPO/mcp/entwurf-bridge/start.sh"
|
|
|
46
54
|
# authoritative: drop EVERY original PATH dir that carries a real entwurf-bridge. A maintainer
|
|
47
55
|
# can have both the managed dev link and the registry package shim; filtering only command -v's
|
|
48
56
|
# first hit lets the second one mask the dangling-command cell.
|
|
49
|
-
|
|
50
|
-
|
|
57
|
+
#
|
|
58
|
+
# #81: the doctor no longer stops at resolvability — it BOOTS the effective command and requires
|
|
59
|
+
# the entwurf MCP tool surface back. So the healthy fake must actually speak MCP: answer the
|
|
60
|
+
# `tools/list` frame (id 2) with a tools array carrying entwurf_v2. A fake that only echoed a word
|
|
61
|
+
# would now (correctly) read as "resolves but does not serve MCP", which is the NEGATIVE cell
|
|
62
|
+
# below — keeping the two fakes distinct is what makes this an A/B rather than a rename.
|
|
63
|
+
write_mcp_fake() { # $1 = path
|
|
64
|
+
cat > "$1" <<'FAKE'
|
|
65
|
+
#!/usr/bin/env bash
|
|
66
|
+
# minimal MCP stdio server: enough frames for the boot probe, nothing else.
|
|
67
|
+
while IFS= read -r line; do
|
|
68
|
+
case "$line" in
|
|
69
|
+
*'"id":1'*) printf '%s\n' '{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2024-11-05","capabilities":{},"serverInfo":{"name":"fake-entwurf-bridge","version":"0"}}}' ;;
|
|
70
|
+
*'"id":2'*) printf '%s\n' '{"jsonrpc":"2.0","id":2,"result":{"tools":[{"name":"entwurf_v2"},{"name":"entwurf_self"},{"name":"entwurf_peers"},{"name":"entwurf_inbox_read"},{"name":"entwurf_register_native"},{"name":"entwurf_fresh_call"},{"name":"entwurf_resume_call"}]}}' ;;
|
|
71
|
+
esac
|
|
72
|
+
done
|
|
73
|
+
FAKE
|
|
74
|
+
chmod +x "$1"
|
|
75
|
+
}
|
|
76
|
+
# A launcher that RESOLVES and dies on exec — the shape of a relocated package-manager shim whose
|
|
77
|
+
# $0-derived target is gone (observed: exit 127 with a "No such file or directory" on stderr).
|
|
78
|
+
write_dead_fake() { # $1 = path
|
|
79
|
+
cat > "$1" <<'FAKE'
|
|
80
|
+
#!/usr/bin/env bash
|
|
81
|
+
echo "bash: /nonexistent/../global/v11/deadbeef/node_modules/@junghanacs/entwurf/mcp/entwurf-bridge/start.sh: No such file or directory" >&2
|
|
82
|
+
exit 127
|
|
83
|
+
FAKE
|
|
84
|
+
chmod +x "$1"
|
|
85
|
+
}
|
|
86
|
+
# A launcher that boots and speaks MCP but is NOT this bridge: it serves entwurf_v2 while missing
|
|
87
|
+
# entwurf_self. This is the shape of the observed #81 session (its schema had no entwurf_self) and
|
|
88
|
+
# of a stale build or a foreign binary that happens to own the name — a probe keyed on one tool
|
|
89
|
+
# would call it healthy, which is why identity is the exact verb SET.
|
|
90
|
+
write_stale_fake() { # $1 = path
|
|
91
|
+
cat > "$1" <<'FAKE'
|
|
92
|
+
#!/usr/bin/env bash
|
|
93
|
+
while IFS= read -r line; do
|
|
94
|
+
case "$line" in
|
|
95
|
+
*'"id":1'*) printf '%s\n' '{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2024-11-05","capabilities":{},"serverInfo":{"name":"stale-bridge","version":"0"}}}' ;;
|
|
96
|
+
*'"id":2'*) printf '%s\n' '{"jsonrpc":"2.0","id":2,"result":{"tools":[{"name":"entwurf_v2"},{"name":"entwurf_peers"},{"name":"entwurf_inbox_read"},{"name":"entwurf_register_native"},{"name":"entwurf_fresh_call"},{"name":"entwurf_resume_call"}]}}' ;;
|
|
97
|
+
esac
|
|
98
|
+
done
|
|
99
|
+
FAKE
|
|
100
|
+
chmod +x "$1"
|
|
101
|
+
}
|
|
102
|
+
write_mcp_fake "$SB/bin/entwurf-bridge"
|
|
51
103
|
_clean_path=""
|
|
52
104
|
while IFS= read -r _dir; do
|
|
53
105
|
[ -n "$_dir" ] || continue
|
|
@@ -136,14 +188,91 @@ printf '{}\n' > "$PROJECT"; rm -f "$STATE"
|
|
|
136
188
|
OUT="$(doctor)"; RC=$?
|
|
137
189
|
want "doctor(user-only bare): exits 0" "[ '$RC' -eq 0 ]"
|
|
138
190
|
want "doctor(user-only bare): ok — bare bin resolves" "printf '%s' \"\$OUT\" | grep -q 'bare stable bin'"
|
|
191
|
+
# The green must be about BOOTING, not about resolving — anchored on the verdict word so a doctor
|
|
192
|
+
# that regressed to the resolvability-only ok cannot keep this cell green.
|
|
193
|
+
want "doctor(user-only bare): green says the bin BOOTS (#81), not merely resolves" "printf '%s' \"\$OUT\" | grep -q 'and it BOOTS'"
|
|
194
|
+
|
|
195
|
+
# ── H-merge: the doctor normalizes what PRODUCTION normalizes, not what each FILE holds ──
|
|
196
|
+
# resolveProviderConfig merges the two mcpServers maps per-name and normalizes the RESULT once. A
|
|
197
|
+
# doctor that normalized each file separately would validate entries production never sees: a
|
|
198
|
+
# malformed global entry the project map shadows is gone before pi ever looks, yet the per-file
|
|
199
|
+
# doctor throws and calls a healthy session red. Both directions are pinned here.
|
|
200
|
+
python3 -c 'import json,sys; json.dump({"entwurfProvider":{"mcpServers":{"entwurf-bridge":{"command":123}}}}, open(sys.argv[1],"w"))' "$GLOBAL"
|
|
201
|
+
printf '{"entwurfProvider":{"mcpServers":{"entwurf-bridge":{"command":"entwurf-bridge"}}}}\n' > "$PROJECT"
|
|
202
|
+
set +e
|
|
203
|
+
OUT="$(doctor 2>&1)"; RC=$?
|
|
204
|
+
set -e
|
|
205
|
+
want "doctor(invalid global SHADOWED by valid project): green — production never sees the bad entry" "[ '$RC' -eq 0 ]"
|
|
206
|
+
want "doctor(invalid global shadowed): EFFECTIVE scope is project" "printf '%s' \"\$OUT\" | grep -q 'EFFECTIVE (project)'"
|
|
207
|
+
# An unrelated malformed server in the global map is likewise production's problem only if it
|
|
208
|
+
# survives the merge — here the project map does not shadow it, so it MUST stay red.
|
|
209
|
+
python3 -c 'import json,sys; json.dump({"entwurfProvider":{"mcpServers":{"entwurf-bridge":{"command":"entwurf-bridge"},"other":{"command":[]}}}}, open(sys.argv[1],"w"))' "$GLOBAL"
|
|
210
|
+
printf '{}\n' > "$PROJECT"
|
|
211
|
+
if doctor >/dev/null 2>&1; then die "doctor(unshadowed malformed sibling): should FAIL — the merged map is what production normalizes"; fi
|
|
212
|
+
ok "doctor(unshadowed malformed sibling): FAILS — an invalid entry that SURVIVES the merge is red"
|
|
213
|
+
# The effective entry itself malformed → red regardless of which scope wrote it.
|
|
214
|
+
printf '{"entwurfProvider":{"mcpServers":{"entwurf-bridge":{"command":"entwurf-bridge"}}}}\n' > "$GLOBAL"
|
|
215
|
+
python3 -c 'import json,sys; json.dump({"entwurfProvider":{"mcpServers":{"entwurf-bridge":{"command":123}}}}, open(sys.argv[1],"w"))' "$PROJECT"
|
|
216
|
+
if doctor >/dev/null 2>&1; then die "doctor(effective malformed): should FAIL"; fi
|
|
217
|
+
ok "doctor(effective malformed in project): FAILS — shadowing cannot rescue the EFFECTIVE entry"
|
|
218
|
+
# restore the H-1 baseline for the cells below
|
|
219
|
+
printf '{"entwurfProvider":{"mcpServers":{"entwurf-bridge":{"command":"entwurf-bridge"}}}}\n' > "$GLOBAL"
|
|
220
|
+
printf '{}\n' > "$PROJECT"
|
|
221
|
+
|
|
222
|
+
# ── H-boot: #81 A/B — a bare bin that RESOLVES but does not serve MCP ─────────
|
|
223
|
+
# This is the whole defect in one cell: before #81 the doctor printed ok here, and a host in this
|
|
224
|
+
# exact state ran Pi ACP turns with NO mcp__entwurf-bridge__* tool at all. PATH/HOME/XDG are the
|
|
225
|
+
# sandbox's, so the operator's real launcher is never touched by this A/B.
|
|
226
|
+
write_dead_fake "$SB/bin/entwurf-bridge"
|
|
227
|
+
# `set -e` is on: a doctor that (correctly) exits non-zero here would kill the smoke before it could
|
|
228
|
+
# judge the failure. Fence just the drive, never the assertions — a silently swallowed rc is how a
|
|
229
|
+
# negative cell turns into decoration.
|
|
230
|
+
set +e
|
|
231
|
+
OUT="$(doctor 2>&1)"; RC=$?
|
|
232
|
+
set -e
|
|
233
|
+
want "[QK:PI-DOCTOR-BOOT-NEGATIVE] doctor(bare resolves, does not boot): FAILS instead of printing ok" "[ '$RC' -ne 0 ]"
|
|
234
|
+
want "doctor(bare resolves, does not boot): names the boot failure reason" "printf '%s' \"\$OUT\" | grep -q 'does NOT serve MCP \[exited-before-tools-list\]'"
|
|
235
|
+
want "doctor(bare resolves, does not boot): carries the launcher's own stderr" "printf '%s' \"\$OUT\" | grep -q 'No such file or directory'"
|
|
236
|
+
# Remediation must NOT be a fix-all: entwurf may not clobber a launcher it does not own, so the
|
|
237
|
+
# foreign case has to be named as the operator's repair.
|
|
238
|
+
want "doctor(bare resolves, does not boot): refuses to clobber a foreign launcher" "printf '%s' \"\$OUT\" | grep -q 'will not overwrite'"
|
|
239
|
+
want "doctor(bare resolves, does not boot): still distinguishes 'resolves' from 'boots'" "printf '%s' \"\$OUT\" | grep -q 'RESOLVES — probing'"
|
|
240
|
+
# A bridge that BOOTS and speaks MCP but is not this one — the #81 symptom exactly.
|
|
241
|
+
write_stale_fake "$SB/bin/entwurf-bridge"
|
|
242
|
+
set +e
|
|
243
|
+
OUT="$(doctor 2>&1)"; RC=$?
|
|
244
|
+
set -e
|
|
245
|
+
want "doctor(boots, wrong verb set): FAILS — booting an MCP server is not being THIS bridge" "[ '$RC' -ne 0 ]"
|
|
246
|
+
want "doctor(boots, wrong verb set): names the missing verb (#81 symptom was entwurf_self)" "printf '%s' \"\$OUT\" | grep -q 'missing entwurf_self'"
|
|
247
|
+
want "doctor(boots, wrong verb set): reason is the tool-set mismatch, not a boot failure" "printf '%s' \"\$OUT\" | grep -q 'tool-set-mismatch'"
|
|
139
248
|
|
|
140
|
-
|
|
249
|
+
write_mcp_fake "$SB/bin/entwurf-bridge"
|
|
250
|
+
OUT="$(doctor)"; RC=$?
|
|
251
|
+
want "doctor(after repair): the SAME unchanged doctor goes green once the launcher boots" "[ '$RC' -eq 0 ]"
|
|
252
|
+
want "doctor(after repair): green names the exact verb set as the evidence" "printf '%s' \"\$OUT\" | grep -q 'exact entwurf verb set'"
|
|
253
|
+
|
|
254
|
+
# The review A/B: an unowned/non-bare override shadows the healthy user bridge in production.
|
|
255
|
+
# Ownership remains the operator's, but runtime truth cannot become a green note merely because
|
|
256
|
+
# entwurf will not overwrite it. Keep this QK cell before the healthy legacy override: a mutant
|
|
257
|
+
# that skips all non-bare runtime probes must be attributed to THIS failure, not the later positive.
|
|
258
|
+
write_dead_fake "$SB/bin/dead-override"
|
|
141
259
|
printf '{"entwurfProvider":{"mcpServers":{"entwurf-bridge":{"command":"entwurf-bridge"}}}}\n' > "$GLOBAL"
|
|
260
|
+
python3 -c 'import json,sys; json.dump({"entwurfProvider":{"mcpServers":{"entwurf-bridge":{"command":sys.argv[2],"args":[]}}}}, open(sys.argv[1],"w"))' "$PROJECT" "$SB/bin/dead-override"
|
|
261
|
+
set +e
|
|
262
|
+
OUT="$(doctor 2>&1)"; RC=$?
|
|
263
|
+
set -e
|
|
264
|
+
want "[QK:PI-DOCTOR-PROBES-OVERRIDE] doctor(dead unowned override): FAILS runtime independently of ownership" "[ '$RC' -ne 0 ]"
|
|
265
|
+
want "doctor(dead unowned override): names the exact invocation failure" "printf '%s' \"\$OUT\" | grep -q 'effective invocation does NOT serve MCP'"
|
|
266
|
+
|
|
267
|
+
# H-2: project legacy override still gets an independent runtime verdict before its ownership note.
|
|
268
|
+
mkdir -p "$(dirname "$LEGACY_CMD")"
|
|
269
|
+
write_mcp_fake "$LEGACY_CMD"
|
|
142
270
|
python3 -c 'import json,sys; json.dump({"entwurfProvider":{"mcpServers":{"entwurf-bridge":{"command":sys.argv[2]}}}}, open(sys.argv[1],"w"))' "$PROJECT" "$LEGACY_CMD"
|
|
143
271
|
OUT="$(doctor)"; RC=$?
|
|
144
|
-
want "doctor(project-
|
|
145
|
-
want "doctor(project-
|
|
146
|
-
want "doctor(project-
|
|
272
|
+
want "doctor(project-legacy): exits 0 when the exact override boots" "[ '$RC' -eq 0 ]"
|
|
273
|
+
want "doctor(project-legacy): EFFECTIVE is project (shadows user)" "printf '%s' \"\$OUT\" | grep -q 'EFFECTIVE (project)'"
|
|
274
|
+
want "doctor(project-legacy): runtime BOOTS before the ownership note" "printf '%s' \"\$OUT\" | grep -q 'configured override BOOTS'"
|
|
275
|
+
want "doctor(project-legacy): flags legacy managed path (not adopted)" "printf '%s' \"\$OUT\" | grep -q 'LEGACY managed repo path'"
|
|
147
276
|
|
|
148
277
|
# H-3: state-owned DRIFT → FAIL (state says managed but effective is foreign)
|
|
149
278
|
printf '{"entwurfProvider":{"mcpServers":{"entwurf-bridge":{"command":"/foreign/thing"}}}}\n' > "$GLOBAL"
|
|
@@ -70,7 +70,7 @@ fi
|
|
|
70
70
|
SESSIONS_BASE="$RGG_AGENT_DIR/sessions"
|
|
71
71
|
cleanup() { rm -rf "$RGG_AGENT_DIR"; }
|
|
72
72
|
trap cleanup EXIT
|
|
73
|
-
# Default target stays pi-native (openai-codex/gpt-5.
|
|
73
|
+
# Default target stays pi-native (openai-codex/gpt-5.6-luna), but honor the SAME target
|
|
74
74
|
# knob as the other live smokes: ENTWURF_LIVE_TARGET="<provider>/<model>". On
|
|
75
75
|
# acp-on-v2 the ACP `entwurf` provider is back, and `./run.sh smoke-acp-rgg-live`
|
|
76
76
|
# drives THIS runner against it (deterministic half) — the citizen logic (record +
|
|
@@ -83,7 +83,7 @@ if [ -n "${ENTWURF_LIVE_TARGET:-}" ]; then
|
|
|
83
83
|
esac
|
|
84
84
|
else
|
|
85
85
|
_rgg_provider="${ENTWURF_LIVE_PROVIDER:-openai-codex}"
|
|
86
|
-
_rgg_model="${ENTWURF_LIVE_MODEL:-gpt-5.
|
|
86
|
+
_rgg_model="${ENTWURF_LIVE_MODEL:-gpt-5.6-luna}"
|
|
87
87
|
fi
|
|
88
88
|
PROVIDER="${SMOKE_RGG_PROVIDER:-$_rgg_provider}"
|
|
89
89
|
MODEL="${SMOKE_RGG_MODEL:-$_rgg_model}"
|