@junghanacs/entwurf 0.20.1 → 0.22.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 +85 -190
- package/BASELINE.md +6 -3
- package/CHANGELOG.md +473 -14
- package/CONTRIBUTING.md +1 -1
- package/DELIVERY.md +332 -60
- package/README.md +100 -22
- package/VERIFY.md +93 -7
- package/docs/acp-backend-rail.md +0 -1
- package/docs/external-mcp-host.md +64 -33
- package/docs/setup-clean-host.md +151 -17
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +130 -114
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/codex-caller-seat.js +174 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/codex-fresh-preflight.js +519 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-fact-provider.js +22 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-peers-render.js +3 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-contract.js +6 -7
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-native-push.js +30 -17
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-production.js +7 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-runner.js +3 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-send.js +8 -4
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-surface.js +7 -4
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-mailbox-body.js +20 -11
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-sender-identity.js +227 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +189 -19
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/native-push/adapter.js +10 -7
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/native-push/codex-ws-client.js +403 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/native-push/register.js +4 -4
- package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-hook-codex.js +325 -0
- package/mcp/entwurf-bridge/dist/scripts/codex-socket-path.js +30 -0
- package/mcp/entwurf-bridge/dist/scripts/meta-bridge-fresh-cut.js +6 -1
- package/mcp/entwurf-bridge/src/index.ts +154 -116
- package/mcp/entwurf-bridge/tsconfig.build.json +10 -0
- package/package.json +13 -4
- package/pi-extensions/entwurf-control.ts +71 -19
- package/pi-extensions/lib/codex-caller-seat.ts +204 -0
- package/pi-extensions/lib/codex-fresh-preflight.ts +580 -0
- package/pi-extensions/lib/compaction-send-guard.ts +80 -0
- package/pi-extensions/lib/entwurf-fact-provider.ts +29 -3
- package/pi-extensions/lib/entwurf-peers-render.ts +4 -1
- package/pi-extensions/lib/entwurf-v2-contract.ts +6 -7
- package/pi-extensions/lib/entwurf-v2-native-push.ts +35 -18
- package/pi-extensions/lib/entwurf-v2-production.ts +10 -3
- package/pi-extensions/lib/entwurf-v2-runner.ts +3 -2
- package/pi-extensions/lib/entwurf-v2-send.ts +16 -11
- package/pi-extensions/lib/entwurf-v2-surface.ts +7 -4
- package/pi-extensions/lib/meta-mailbox-body.ts +22 -13
- package/pi-extensions/lib/meta-sender-identity.ts +305 -0
- package/pi-extensions/lib/mux-fresh-call.ts +233 -29
- package/pi-extensions/lib/native-push/adapter.ts +21 -24
- package/pi-extensions/lib/native-push/codex-ws-client.ts +506 -0
- package/pi-extensions/lib/native-push/register.ts +7 -9
- package/pi-extensions/meta-bridge-hook-codex.ts +371 -0
- package/run.sh +342 -28
- package/scripts/check-agy-sender-identity.ts +1 -1
- package/scripts/check-codex-app-server-launch.ts +445 -0
- package/scripts/check-codex-birth-hook.ts +264 -0
- package/scripts/check-codex-bridge-identity.ts +179 -0
- package/scripts/check-codex-native-push.ts +386 -0
- package/scripts/check-codex-sender-identity.ts +495 -0
- package/scripts/check-compaction-send-guard.ts +130 -0
- package/scripts/check-copilot-receive-arm.ts +4 -1
- package/scripts/check-entwurf-fact-provider.ts +38 -0
- package/scripts/check-entwurf-peers-surface.ts +13 -1
- package/scripts/check-entwurf-self-address.ts +15 -16
- package/scripts/check-entwurf-v2-contract.ts +4 -3
- package/scripts/check-entwurf-v2-decider.ts +7 -5
- package/scripts/check-entwurf-v2-native-push.ts +35 -7
- package/scripts/check-entwurf-v2-production.ts +245 -12
- package/scripts/check-entwurf-v2-runner.ts +1 -1
- package/scripts/check-entwurf-v2-send.ts +26 -7
- package/scripts/check-entwurf-v2-surface.ts +1 -1
- package/scripts/check-gate-qualification.ts +8 -3
- package/scripts/check-harness-admission-parity.ts +0 -1
- package/scripts/check-mux-launch-tmux.ts +345 -4
- package/scripts/check-native-push-adapter.ts +20 -16
- package/scripts/check-native-push-register.ts +5 -1
- package/scripts/check-release-gate-outcomes.ts +47 -1
- package/scripts/check-setup-qualification.sh +3 -1
- package/scripts/codex-app-server-launch.sh +275 -0
- package/scripts/codex-birth-doctor.sh +276 -0
- package/scripts/codex-birth-install.sh +414 -0
- package/scripts/codex-birth-uninstall.sh +170 -0
- package/scripts/codex-mcp-config.py +435 -0
- package/scripts/codex-socket-path.ts +33 -0
- package/scripts/codex-statusline-config.py +434 -0
- package/scripts/codex-terminal-title-config.py +500 -0
- package/scripts/codex_toml_io.py +653 -0
- package/scripts/lib/codex-fresh-live-protocol.ts +121 -0
- package/scripts/lib/codex-fresh-source-receipts.ts +426 -0
- package/scripts/lib/launch-receipt-windows.ts +46 -0
- package/scripts/lib/tmux-coordinate-row.ts +58 -0
- package/scripts/meta-bridge-fresh-cut.ts +6 -1
- package/scripts/mutants/codex-app-server-launch.json +157 -0
- package/scripts/mutants/codex-caller-seat.json +336 -0
- package/scripts/mutants/codex-native.json +838 -0
- package/scripts/mutants/compaction-send-guard.json +103 -0
- package/scripts/mutants/entwurf-peers.json +19 -0
- package/scripts/mutants/mux-fresh-call.json +93 -10
- package/scripts/mutants/omp-fresh.json +6 -4
- package/scripts/mutants/release-gate.json +13 -0
- package/scripts/mutants/v2-surface.json +75 -1
- package/scripts/raw-async-delivery/README.md +2 -1
- package/scripts/raw-codex-measure/README.md +114 -46
- package/scripts/smoke-agy-native-push-live.ts +3 -1
- package/scripts/smoke-codex-birth.sh +347 -0
- package/scripts/smoke-codex-config-state.sh +700 -0
- package/scripts/smoke-codex-fresh-live.ts +1426 -0
- package/scripts/smoke-codex-native-push-live.ts +75 -0
- package/scripts/smoke-entwurf-chain-live.ts +50 -0
- package/scripts/smoke-setup-verdict.sh +125 -10
- package/scripts/tsconfig.json +1 -0
|
@@ -0,0 +1,700 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# smoke-codex-config-state — hermetic install/doctor/inverse contract for the
|
|
3
|
+
# three Codex config atoms (codex-mcp-config.py + codex-statusline-config.py +
|
|
4
|
+
# codex-terminal-title-config.py).
|
|
5
|
+
# No Codex process, no model turn: the vendor's config.toml surface only.
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
9
|
+
MCP="$REPO_DIR/scripts/codex-mcp-config.py"
|
|
10
|
+
SL="$REPO_DIR/scripts/codex-statusline-config.py"
|
|
11
|
+
TT="$REPO_DIR/scripts/codex-terminal-title-config.py"
|
|
12
|
+
pass=0
|
|
13
|
+
ok() { printf ' ok %s\n' "$1"; pass=$((pass + 1)); }
|
|
14
|
+
die() { printf 'FAIL: %s\n' "$1" >&2; exit 1; }
|
|
15
|
+
want() { eval "$2" && ok "$1" || die "$1"; }
|
|
16
|
+
refuses() { # <label> <expected-substring-on-stderr> <cmd...>
|
|
17
|
+
local label="$1" needle="$2"; shift 2
|
|
18
|
+
local err rc=0
|
|
19
|
+
err="$("$@" 2>&1 >/dev/null)" || rc=$?
|
|
20
|
+
[ "$rc" -ne 0 ] && printf '%s' "$err" | grep -q "$needle" || die "$label (rc=$rc, stderr: $err)"
|
|
21
|
+
ok "$label"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
REPO_BEFORE="$(cd "$REPO_DIR" && git status --porcelain)"
|
|
25
|
+
SB="$(mktemp -d)"
|
|
26
|
+
trap 'rm -rf "$SB"' EXIT
|
|
27
|
+
export HOME="$SB/home"
|
|
28
|
+
export XDG_DATA_HOME="$SB/xdg"
|
|
29
|
+
export CODEX_HOME="$HOME/.codex" # the only surface both atoms touch
|
|
30
|
+
export ENTWURF_TEST_IGNORED="$SB" # prove nothing else leaks in
|
|
31
|
+
export ENTWURF_DIR="$SB/custom-entwurf"
|
|
32
|
+
export PI_CODING_AGENT_DIR="$SB/custom-pi"
|
|
33
|
+
export ENTWURF_META_SESSIONS_DIR="$SB/custom-meta/sessions"
|
|
34
|
+
export ENTWURF_META_MAILBOX_DIR="$SB/custom-meta/mailbox"
|
|
35
|
+
export ENTWURF_META_SENDERS_DIR="$SB/custom-meta/senders"
|
|
36
|
+
export ENTWURF_META_RECEIVERS_DIR="$SB/custom-meta/receivers"
|
|
37
|
+
export TMUX="$SB/custom-tmux/default,123,0"
|
|
38
|
+
export TMUX_PANE="%77"
|
|
39
|
+
export PYTHONDONTWRITEBYTECODE=1 # imported parser helpers must not write into the checkout
|
|
40
|
+
mkdir -p "$CODEX_HOME" "$(dirname "$XDG_DATA_HOME")"
|
|
41
|
+
CFG="$CODEX_HOME/config.toml"
|
|
42
|
+
MCP_STATE="$XDG_DATA_HOME/entwurf/codex-mcp/install-state.json"
|
|
43
|
+
SL_STATE="$XDG_DATA_HOME/entwurf/codex-statusline/install-state.json"
|
|
44
|
+
TT_STATE="$XDG_DATA_HOME/entwurf/codex-terminal-title/install-state.json"
|
|
45
|
+
|
|
46
|
+
seed_config() { # the operator's hand-edited file: quoted header keys, nested
|
|
47
|
+
# tables, an unrelated MCP server, and NO entwurf atom content.
|
|
48
|
+
cat > "$CFG" <<'EOF'
|
|
49
|
+
model = "gpt-5.6-sol"
|
|
50
|
+
approval_policy = "never"
|
|
51
|
+
[notice]
|
|
52
|
+
hide_full_access_warning = true
|
|
53
|
+
[projects."/home/op/work"]
|
|
54
|
+
trust_level = "trusted"
|
|
55
|
+
[hooks.state."~/c.toml:session_start:0:0"]
|
|
56
|
+
trusted_hash = "sha256:abc"
|
|
57
|
+
[tui]
|
|
58
|
+
status_line = ["model-with-reasoning", "current-dir"]
|
|
59
|
+
theme = "zenburn"
|
|
60
|
+
[tui.model_availability_nux]
|
|
61
|
+
"gpt-5.5" = 4
|
|
62
|
+
[mcp_servers.other]
|
|
63
|
+
command = "keep-me"
|
|
64
|
+
[plugins."github@openai-curated"]
|
|
65
|
+
enabled = false
|
|
66
|
+
EOF
|
|
67
|
+
}
|
|
68
|
+
operator_bridge() { # a manual entwurf-bridge entry the operator owns already
|
|
69
|
+
cat >> "$CFG" <<'EOF'
|
|
70
|
+
[mcp_servers.entwurf-bridge]
|
|
71
|
+
command = "/opt/agent-config/bridge-start.sh"
|
|
72
|
+
startup_timeout_sec = 30
|
|
73
|
+
[mcp_servers.entwurf-bridge.env]
|
|
74
|
+
EXTRA = "operator"
|
|
75
|
+
EOF
|
|
76
|
+
}
|
|
77
|
+
json_field() { python3 -c 'import json,sys; d=json.load(open(sys.argv[1])); print(d[sys.argv[2]])' "$1" "$2"; }
|
|
78
|
+
toml_ok() { python3 -c 'import tomllib,sys; tomllib.load(open(sys.argv[1],"rb"))' "$1"; }
|
|
79
|
+
context_values_absent() {
|
|
80
|
+
python3 -c 'import os,sys; t=open(sys.argv[1]).read(); names=("CODEX_HOME","ENTWURF_DIR","PI_CODING_AGENT_DIR","ENTWURF_META_SESSIONS_DIR","ENTWURF_META_MAILBOX_DIR","ENTWURF_META_SENDERS_DIR","ENTWURF_META_RECEIVERS_DIR","TMUX","TMUX_PANE"); assert all(os.environ[n] not in t for n in names)' "$1"
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
###############################################################################
|
|
84
|
+
# Atom 1 — [mcp_servers.entwurf-bridge]
|
|
85
|
+
###############################################################################
|
|
86
|
+
|
|
87
|
+
# unowned manual config is an honest note, not a failure (this host's shape)
|
|
88
|
+
seed_config
|
|
89
|
+
operator_bridge
|
|
90
|
+
python3 "$MCP" doctor-static "$CFG" entwurf-bridge "$MCP_STATE" >"$SB/out" 2>&1
|
|
91
|
+
want "unowned manual entry doctor is an honest note" "grep -q '^configured /opt/agent-config/bridge-start.sh unowned$' '$SB/out'"
|
|
92
|
+
|
|
93
|
+
# adoption captures the operator's exact bytes as the FIRST preimage, once
|
|
94
|
+
cp "$CFG" "$SB/with-bridge.toml"
|
|
95
|
+
python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE" >"$SB/out"
|
|
96
|
+
want "install adopts the regular file" "grep -q '^adopt-regular-file ' '$SB/out'"
|
|
97
|
+
toml_ok "$CFG"; ok "installed config still parses as TOML"
|
|
98
|
+
want "first preimage captured" "[ \"\$(json_field '$MCP_STATE' entryExistedBefore)\" = True ] && json_field '$MCP_STATE' preimage | grep -q 'bridge-start.sh'"
|
|
99
|
+
want "our stdio shape is live" "grep -q '^command = \"entwurf-bridge\"$' '$CFG' && grep -q '^ENTWURF_BRIDGE_NATIVE_HOST = \"codex\"$' '$CFG'"
|
|
100
|
+
want "vendor env_vars forwards the exact operator context names in order" \
|
|
101
|
+
"grep -Fxq 'env_vars = [\"CODEX_HOME\", \"ENTWURF_DIR\", \"PI_CODING_AGENT_DIR\", \"ENTWURF_META_SESSIONS_DIR\", \"ENTWURF_META_MAILBOX_DIR\", \"ENTWURF_META_SENDERS_DIR\", \"ENTWURF_META_RECEIVERS_DIR\", \"TMUX\", \"TMUX_PANE\"]' '$CFG'"
|
|
102
|
+
want "no install-time context value is baked into config" "context_values_absent '$CFG'"
|
|
103
|
+
python3 "$MCP" doctor-static "$CFG" entwurf-bridge "$MCP_STATE" >"$SB/out"
|
|
104
|
+
want "owned+configured doctor is green" "grep -q '^configured entwurf-bridge owned$' '$SB/out'"
|
|
105
|
+
python3 "$MCP" doctor-invocation "$CFG" >"$SB/out"
|
|
106
|
+
want "doctor-invocation emits only the literal boot-probe environment" "grep -x '{\"command\":\"entwurf-bridge\",\"args\":\[\],\"env\":{\"ENTWURF_BRIDGE_NATIVE_HOST\":\"codex\"}}' '$SB/out'"
|
|
107
|
+
|
|
108
|
+
# idempotence is an untouched file, and the FIRST preimage survives re-runs
|
|
109
|
+
MT1="$(stat -c %Y "$CFG")"; sleep 1.1
|
|
110
|
+
python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE" >/dev/null
|
|
111
|
+
MT2="$(stat -c %Y "$CFG")"
|
|
112
|
+
want "reinstall does not rewrite the config" "[ '$MT1' = '$MT2' ]"
|
|
113
|
+
python3 "$MCP" install "$CFG" a-different-command "$MCP_STATE" >/dev/null
|
|
114
|
+
want "reinstall with a new command keeps the FIRST preimage" "json_field '$MCP_STATE' preimage | grep -q 'bridge-start.sh'"
|
|
115
|
+
python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE" >/dev/null
|
|
116
|
+
|
|
117
|
+
# drift the operator introduces turns the doctor red by name, not green
|
|
118
|
+
sed -i 's/^command = "entwurf-bridge"$/command = "evil-bridge"/' "$CFG"
|
|
119
|
+
python3 "$MCP" doctor-static "$CFG" entwurf-bridge "$MCP_STATE" >"$SB/out" 2>&1 && die "command drift doctor should be red"
|
|
120
|
+
want "command drift is red" "grep -q '^configured evil-bridge drift command-mismatch' '$SB/out'"
|
|
121
|
+
sed -i 's/^command = "evil-bridge"$/command = "entwurf-bridge"/' "$CFG"
|
|
122
|
+
sed -i '/^ENTWURF_BRIDGE_NATIVE_HOST = /d' "$CFG"
|
|
123
|
+
python3 "$MCP" doctor-static "$CFG" entwurf-bridge "$MCP_STATE" >"$SB/out" 2>&1 && die "env-label drift doctor should be red"
|
|
124
|
+
want "removed provenance label is red" "grep -q 'drift env-label-missing' '$SB/out'"
|
|
125
|
+
sed -i 's/^\[mcp_servers.entwurf-bridge.env\]$/[mcp_servers.entwurf-bridge.env]\nENTWURF_BRIDGE_NATIVE_HOST = "not-codex"/' "$CFG"
|
|
126
|
+
python3 "$MCP" doctor-static "$CFG" entwurf-bridge "$MCP_STATE" >"$SB/out" 2>&1 && die "foreign label value doctor should be red"
|
|
127
|
+
want "foreign provenance value is red" "grep -q 'drift env-label-foreign' '$SB/out'"
|
|
128
|
+
python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE" >/dev/null
|
|
129
|
+
|
|
130
|
+
sed -i 's/, "ENTWURF_DIR"//' "$CFG"
|
|
131
|
+
python3 "$MCP" doctor-static "$CFG" entwurf-bridge "$MCP_STATE" >"$SB/out" 2>&1 && die "missing context env_vars name doctor should be red"
|
|
132
|
+
want "missing context env_vars name is red by name" "grep -q 'drift context-env-vars-missing (ENTWURF_DIR)' '$SB/out'"
|
|
133
|
+
python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE" >/dev/null
|
|
134
|
+
sed -i 's/"TMUX_PANE"]/"TMUX_PANE", "FOREIGN_CONTEXT"]/' "$CFG"
|
|
135
|
+
python3 "$MCP" doctor-static "$CFG" entwurf-bridge "$MCP_STATE" >"$SB/out" 2>&1 && die "foreign context env_vars name doctor should be red"
|
|
136
|
+
want "foreign context env_vars name is red by name" "grep -q 'drift context-env-vars-foreign (FOREIGN_CONTEXT)' '$SB/out'"
|
|
137
|
+
python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE" >/dev/null
|
|
138
|
+
sed -i 's/"CODEX_HOME", "ENTWURF_DIR"/"ENTWURF_DIR", "CODEX_HOME"/' "$CFG"
|
|
139
|
+
python3 "$MCP" doctor-static "$CFG" entwurf-bridge "$MCP_STATE" >"$SB/out" 2>&1 && die "reordered context env_vars doctor should be red"
|
|
140
|
+
want "reordered context env_vars is red" "grep -q 'drift context-env-vars-reordered-or-duplicated' '$SB/out'"
|
|
141
|
+
cp "$CFG" "$SB/drifted-mcp.toml"
|
|
142
|
+
python3 "$MCP" uninstall "$MCP_STATE" >"$SB/out" 2>&1 &&
|
|
143
|
+
die "[QK:CODEX-MCP-UNINSTALL-REFUSES-DRIFT] uninstall accepted a drifted owned MCP table"
|
|
144
|
+
want "uninstall refusal names exact post-install table drift" "grep -q 'drifted from the exact post-install table' '$SB/out'"
|
|
145
|
+
cmp -s "$SB/drifted-mcp.toml" "$CFG" && ok "drifted MCP table is byte-identical after refused inverse" || die "drifted MCP table changed"
|
|
146
|
+
|
|
147
|
+
# the inverse restores the operator's exact bytes after drift repairs
|
|
148
|
+
python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE" >/dev/null
|
|
149
|
+
python3 "$MCP" uninstall "$MCP_STATE" >"$SB/out"
|
|
150
|
+
want "uninstall reports the managed path" "grep -q '^uninstalled ' '$SB/out'"
|
|
151
|
+
cmp -s "$SB/with-bridge.toml" "$CFG" && ok "inverse restores the operator entry byte-exact" || die "inverse byte mismatch"
|
|
152
|
+
want "state is gone" "[ ! -e '$MCP_STATE' ]"
|
|
153
|
+
python3 "$MCP" doctor-static "$CFG" entwurf-bridge "$MCP_STATE" >"$SB/out"
|
|
154
|
+
want "post-uninstall doctor is an unowned note again" "grep -q 'unowned' '$SB/out'"
|
|
155
|
+
|
|
156
|
+
# A NEIGHBOUR APPENDED AFTER OUR BLOCK — the shape this host actually grew into.
|
|
157
|
+
# Our managed table is written at the end of the file, so for a long time nothing
|
|
158
|
+
# ever followed it and no gate could see where the family stopped. Then the Codex
|
|
159
|
+
# vendor appended its own `[hooks.state]` (the operator's hook-trust decision) right
|
|
160
|
+
# after ours, separated by one blank line, and a plain idempotent reinstall deleted
|
|
161
|
+
# that blank line: semantically nothing, but a byte outside our atom, and the one
|
|
162
|
+
# thing this writer promises never to touch. The separator belongs to the boundary,
|
|
163
|
+
# not to the family.
|
|
164
|
+
seed_config
|
|
165
|
+
cp "$CFG" "$SB/neighbour-seed.toml" # the operator bytes the inverse owes back
|
|
166
|
+
cat > "$SB/neighbour-block.toml" <<'EOF'
|
|
167
|
+
|
|
168
|
+
[hooks.state]
|
|
169
|
+
|
|
170
|
+
[hooks.state."/sandbox/.codex/hooks.json:session_start:0:0"]
|
|
171
|
+
trusted_hash = "sha256:4647c53b6948cd38f2283a3fdf63e40e83b7ff29ecf14650b7e61eec9315e1cc"
|
|
172
|
+
EOF
|
|
173
|
+
python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE" >/dev/null
|
|
174
|
+
cat "$SB/neighbour-block.toml" >> "$CFG"
|
|
175
|
+
cp "$CFG" "$SB/neighbour-expected.toml"
|
|
176
|
+
# What the inverse owes: the operator's seed bytes, then the neighbour EXACTLY as it was
|
|
177
|
+
# appended — its leading blank separator included. Built by concatenation, never by
|
|
178
|
+
# describing the result, so the assertion cannot be satisfied by a file that merely ends
|
|
179
|
+
# the right way.
|
|
180
|
+
cat "$SB/neighbour-seed.toml" "$SB/neighbour-block.toml" > "$SB/neighbour-after-inverse-expected.toml"
|
|
181
|
+
toml_ok "$CFG"; ok "our block plus an appended vendor neighbour parses"
|
|
182
|
+
python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE" >/dev/null
|
|
183
|
+
cmp -s "$SB/neighbour-expected.toml" "$CFG" \
|
|
184
|
+
|| die "[QK:CODEX-TOML-FAMILY-EXCLUDES-SEPARATOR] reinstall changed bytes outside the managed table: $(diff -u "$SB/neighbour-expected.toml" "$CFG" | sed -n '3,12p')"
|
|
185
|
+
ok "reinstall beside a vendor neighbour is byte-exact, separator included"
|
|
186
|
+
want "the vendor's trust record survives verbatim" "grep -Fxq 'trusted_hash = \"sha256:4647c53b6948cd38f2283a3fdf63e40e83b7ff29ecf14650b7e61eec9315e1cc\"' '$CFG'"
|
|
187
|
+
# ...and the inverse gives back exactly what it found: the operator's seed bytes plus the
|
|
188
|
+
# neighbour verbatim. Asserted with `cmp` against bytes built by concatenation — an
|
|
189
|
+
# "ends with the neighbour" shape test would pass on a file whose separator was eaten,
|
|
190
|
+
# which is the very byte this cell exists to protect.
|
|
191
|
+
python3 "$MCP" uninstall "$MCP_STATE" >/dev/null
|
|
192
|
+
cmp -s "$SB/neighbour-after-inverse-expected.toml" "$CFG" \
|
|
193
|
+
|| die "the inverse did not restore seed+neighbour byte-exact: $(diff -u "$SB/neighbour-after-inverse-expected.toml" "$CFG" | sed -n '3,12p')"
|
|
194
|
+
ok "the inverse restores the operator seed plus the neighbour byte-exact, separator included"
|
|
195
|
+
toml_ok "$CFG"; ok "config after the neighbour-aware inverse still parses"
|
|
196
|
+
rm -f "$MCP_STATE"
|
|
197
|
+
|
|
198
|
+
# created-new: the file we made is removed by the inverse
|
|
199
|
+
rm -f "$CFG"
|
|
200
|
+
python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE" >/dev/null
|
|
201
|
+
toml_ok "$CFG"; ok "created-new config parses"
|
|
202
|
+
python3 "$MCP" uninstall "$MCP_STATE" >/dev/null
|
|
203
|
+
want "created-new file removed by inverse" "[ ! -e '$CFG' ] && [ ! -e '$MCP_STATE' ]"
|
|
204
|
+
|
|
205
|
+
# refusals — symlink, malformed, foreign state, retargeted state, contrary values
|
|
206
|
+
seed_config
|
|
207
|
+
printf '[mcp_servers.other]\ncommand = "foreign"\n' > "$SB/foreign.toml"
|
|
208
|
+
ln -sf "$SB/foreign.toml" "$CFG"
|
|
209
|
+
refuses "symlink config is refused" "symlink" python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE"
|
|
210
|
+
want "symlink refusal leaves the foreign file and no state" "grep -q foreign '$SB/foreign.toml' && [ ! -e '$MCP_STATE' ]"
|
|
211
|
+
rm -f "$CFG"
|
|
212
|
+
printf 'not = toml {{{\n[mcp_servers\n' > "$CFG"
|
|
213
|
+
refuses "malformed TOML is refused" "not valid TOML" python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE"
|
|
214
|
+
want "malformed refusal writes no state" "[ ! -e '$MCP_STATE' ]"
|
|
215
|
+
seed_config
|
|
216
|
+
python3 "$SL" install "$CFG" "$SL_STATE" >/dev/null # a FOREIGN atom's receipt
|
|
217
|
+
cp "$SL_STATE" "$MCP_STATE" # …parked on OUR path
|
|
218
|
+
refuses "foreign-atom state is refused" "belongs to atom" python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE"
|
|
219
|
+
python3 "$SL" uninstall "$SL_STATE" >/dev/null
|
|
220
|
+
rm -f "$MCP_STATE" # the parked foreign receipt must not poison the next case
|
|
221
|
+
seed_config
|
|
222
|
+
python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE" >/dev/null
|
|
223
|
+
sed -i "s|$(json_field "$MCP_STATE" managedConfigPath)|$SB/elsewhere.toml|" "$MCP_STATE"
|
|
224
|
+
refuses "retargeted state is refused" "refusing to retarget" python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE"
|
|
225
|
+
rm -f "$MCP_STATE"
|
|
226
|
+
seed_config; operator_bridge_with_url() {
|
|
227
|
+
cat >> "$CFG" <<'EOF'
|
|
228
|
+
[mcp_servers.entwurf-bridge]
|
|
229
|
+
url = "https://bridge.example/mcp"
|
|
230
|
+
EOF
|
|
231
|
+
}
|
|
232
|
+
operator_bridge_with_url
|
|
233
|
+
refuses "streamable_http entry refused BY NAME (url)" "\`url\`" python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE"
|
|
234
|
+
sed -i 's|^url = .*|command = "x"\nenabled = false|' "$CFG"
|
|
235
|
+
refuses "explicitly disabled entry refused BY NAME (enabled)" "\`enabled\`" python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE"
|
|
236
|
+
want "contrary refusals wrote no state" "[ ! -e '$MCP_STATE' ]"
|
|
237
|
+
|
|
238
|
+
# a receipt whose config vanished is red, not silently green
|
|
239
|
+
seed_config
|
|
240
|
+
python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE" >/dev/null
|
|
241
|
+
rm -f "$CFG"
|
|
242
|
+
python3 "$MCP" doctor-static "$CFG" entwurf-bridge "$MCP_STATE" >"$SB/out" 2>&1 && die "file-absent drift doctor should be red"
|
|
243
|
+
want "file-absent with a receipt is red" "grep -q '^file-absent drift entry-gone$' '$SB/out'"
|
|
244
|
+
python3 "$MCP" uninstall "$MCP_STATE" >/dev/null
|
|
245
|
+
want "uninstall clears the orphaned receipt" "[ ! -e '$MCP_STATE' ]"
|
|
246
|
+
|
|
247
|
+
###############################################################################
|
|
248
|
+
# Fault injection — the state receipt is durable BEFORE the operator's config
|
|
249
|
+
# is ever touched, at each of atomic_write's three fault points
|
|
250
|
+
###############################################################################
|
|
251
|
+
for stage in mkdir write replace; do
|
|
252
|
+
seed_config
|
|
253
|
+
cp "$CFG" "$SB/state-fault-pre.toml"
|
|
254
|
+
ENTWURF_TEST_CODEX_TOML_FAULT="$stage:.codex-state-" python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE" >"$SB/out" 2>&1 \
|
|
255
|
+
&& die "injected $stage state fault did not stop install"
|
|
256
|
+
grep -q "injected $stage fault (.codex-state-)" "$SB/out" || die "state $stage fault refusal is unnamed: $(cat "$SB/out")"
|
|
257
|
+
ok "state $stage fault refuses by name"
|
|
258
|
+
cmp -s "$SB/state-fault-pre.toml" "$CFG" && ok "state $stage fault leaves operator config untouched" \
|
|
259
|
+
|| die "[QK:CODEX-TOML-STATE-BEFORE-CONFIG] state $stage fault mutated operator config"
|
|
260
|
+
want "state $stage fault leaves no state file" "[ ! -e '$MCP_STATE' ]"
|
|
261
|
+
if [ "$stage" = replace ]; then
|
|
262
|
+
find "$(dirname "$MCP_STATE")" -maxdepth 1 -name '.codex-state-*' | grep -q . \
|
|
263
|
+
&& die "a failed state replace left a stray temp file" \
|
|
264
|
+
|| ok "failed state replace leaves no stray temp file"
|
|
265
|
+
fi
|
|
266
|
+
done
|
|
267
|
+
|
|
268
|
+
###############################################################################
|
|
269
|
+
# Config replace fault — atomic_write's own rollback leaves the operator's
|
|
270
|
+
# bytes byte-exact, and a fault-free retry + inverse still recovers them
|
|
271
|
+
###############################################################################
|
|
272
|
+
seed_config
|
|
273
|
+
operator_bridge
|
|
274
|
+
cp "$CFG" "$SB/replace-fault-pre.toml"
|
|
275
|
+
ENTWURF_TEST_CODEX_TOML_FAULT="replace:.codex-mcp-" python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE" >"$SB/out" 2>&1 \
|
|
276
|
+
&& die "[QK:CODEX-TOML-CONFIG-REPLACE-ROLLBACK] injected config replace fault did not stop install"
|
|
277
|
+
grep -q "injected replace fault (.codex-mcp-)" "$SB/out" || die "config replace fault refusal is unnamed: $(cat "$SB/out")"
|
|
278
|
+
ok "config replace fault refuses by name"
|
|
279
|
+
cmp -s "$SB/replace-fault-pre.toml" "$CFG" && ok "config replace fault leaves operator bytes untouched" \
|
|
280
|
+
|| die "config replace fault mutated operator config"
|
|
281
|
+
find "$CODEX_HOME" -maxdepth 1 -name '.codex-mcp-*' | grep -q . \
|
|
282
|
+
&& die "a failed config replace left a stray temp file" \
|
|
283
|
+
|| ok "failed config replace leaves no stray temp file"
|
|
284
|
+
want "the durable state still carries the operator's first preimage" \
|
|
285
|
+
"[ -e '$MCP_STATE' ] && json_field '$MCP_STATE' preimage | grep -q 'bridge-start.sh'"
|
|
286
|
+
|
|
287
|
+
# a fault-free retry recovers cleanly, and the inverse restores the SAME
|
|
288
|
+
# preimage the interrupted install already made durable
|
|
289
|
+
python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE" >"$SB/out"
|
|
290
|
+
want "retry after the replace fault adopts" "grep -q '^adopt-regular-file ' '$SB/out'"
|
|
291
|
+
toml_ok "$CFG"; ok "retried config still parses as TOML"
|
|
292
|
+
python3 "$MCP" uninstall "$MCP_STATE" >"$SB/out"
|
|
293
|
+
want "uninstall reports the managed path" "grep -q '^uninstalled ' '$SB/out'"
|
|
294
|
+
cmp -s "$SB/replace-fault-pre.toml" "$CFG" && ok "inverse after fault-then-retry restores the operator's exact original bytes" \
|
|
295
|
+
|| die "inverse after retry did not restore the durable preimage"
|
|
296
|
+
want "state cleared after inverse" "[ ! -e '$MCP_STATE' ]"
|
|
297
|
+
|
|
298
|
+
###############################################################################
|
|
299
|
+
# Reshaped-owned entry — our table survives as a value but not as a literal
|
|
300
|
+
# header block; the inverse must refuse and keep the receipt, never resurrect
|
|
301
|
+
# or silently drop it
|
|
302
|
+
###############################################################################
|
|
303
|
+
seed_config
|
|
304
|
+
python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE" >/dev/null
|
|
305
|
+
sed -i '/^\[mcp_servers\.entwurf-bridge\]$/,/^ENTWURF_BRIDGE_NATIVE_HOST = "codex"$/d' "$CFG"
|
|
306
|
+
sed -i '1i mcp_servers.entwurf-bridge = { command = "entwurf-bridge" }' "$CFG"
|
|
307
|
+
toml_ok "$CFG"; ok "reshaped config still parses as TOML"
|
|
308
|
+
cp "$CFG" "$SB/reshaped-mcp.toml"
|
|
309
|
+
python3 "$MCP" uninstall "$MCP_STATE" >"$SB/out" 2>&1 \
|
|
310
|
+
&& die "[QK:CODEX-MCP-RESHAPED-REFUSES-KEEPS-STATE] uninstall accepted a reshaped owned entry"
|
|
311
|
+
grep -q "not as a literal table block" "$SB/out" || die "reshaped refusal is unnamed: $(cat "$SB/out")"
|
|
312
|
+
ok "reshaped entry refuses to uninstall, by name"
|
|
313
|
+
cmp -s "$SB/reshaped-mcp.toml" "$CFG" && ok "reshaped config untouched by the refused inverse" \
|
|
314
|
+
|| die "reshaped config mutated by refused inverse"
|
|
315
|
+
want "reshaped refusal retains the receipt" "[ -e '$MCP_STATE' ]"
|
|
316
|
+
rm -f "$MCP_STATE" # hand-cleared: the reshaped entry has no normal inverse path
|
|
317
|
+
|
|
318
|
+
###############################################################################
|
|
319
|
+
# Atom 2 — [tui].status_line includes thread-title
|
|
320
|
+
###############################################################################
|
|
321
|
+
|
|
322
|
+
seed_config
|
|
323
|
+
cp "$CFG" "$SB/sl-seed.toml"
|
|
324
|
+
python3 "$SL" install "$CFG" "$SL_STATE" >"$SB/out"
|
|
325
|
+
want "existing operator list is merged" "grep -q '^merged-item ' '$SB/out'"
|
|
326
|
+
want "thread-title prepended, operator items keep their order" "grep -q '^status_line = \[\"thread-title\", \"model-with-reasoning\", \"current-dir\"\]$' '$CFG'"
|
|
327
|
+
toml_ok "$CFG"; ok "merged config still parses"
|
|
328
|
+
python3 "$SL" doctor-static "$CFG" "$SL_STATE" >"$SB/out"
|
|
329
|
+
want "owned status doctor is green" "grep -q '^thread-title-present owned$' '$SB/out'"
|
|
330
|
+
MT1="$(stat -c %Y "$CFG")"; sleep 1.1
|
|
331
|
+
python3 "$SL" install "$CFG" "$SL_STATE" >/dev/null
|
|
332
|
+
MT2="$(stat -c %Y "$CFG")"
|
|
333
|
+
want "status reinstall writes nothing" "[ '$MT1' = '$MT2' ]"
|
|
334
|
+
|
|
335
|
+
# the state receipt never changes the effective verdict
|
|
336
|
+
rm -f "$SL_STATE"
|
|
337
|
+
python3 "$SL" doctor-static "$CFG" "$SL_STATE" >"$SB/out"
|
|
338
|
+
want "unowned thread-title is green (state-independent)" "grep -q '^thread-title-present unowned$' '$SB/out'"
|
|
339
|
+
|
|
340
|
+
# ...and being unowned never rescues a MISSING required axis. The doctor's
|
|
341
|
+
# subject is visible identity, not our bookkeeping: `thread-title-present` is
|
|
342
|
+
# the only green, so every other effective token is red even with no receipt.
|
|
343
|
+
# Without this, a host whose config the operator keeps elsewhere reports exit 0
|
|
344
|
+
# while no Codex thread can ever show its garden id.
|
|
345
|
+
cp "$CFG" "$SB/sl-required-axis.toml"
|
|
346
|
+
sed -i 's/"thread-title", //' "$CFG"
|
|
347
|
+
python3 "$SL" doctor-static "$CFG" "$SL_STATE" >"$SB/out" 2>&1 \
|
|
348
|
+
&& die "[QK:CODEX-SL-DOCTOR-REQUIRES-VISIBLE-IDENTITY] unowned config WITHOUT thread-title reported green: $(cat "$SB/out")"
|
|
349
|
+
want "unowned thread-title-absent is red, both axes preserved" "grep -q '^thread-title-absent unowned$' '$SB/out'"
|
|
350
|
+
sed -i '/^status_line = /d' "$CFG"
|
|
351
|
+
python3 "$SL" doctor-static "$CFG" "$SL_STATE" >"$SB/out" 2>&1 \
|
|
352
|
+
&& die "unowned config with NO status_line key reported green: $(cat "$SB/out")"
|
|
353
|
+
want "unowned status-line-absent is red, both axes preserved" "grep -q '^status-line-absent unowned$' '$SB/out'"
|
|
354
|
+
ln -s "$SB/sl-required-axis.toml" "$SB/sl-symlinked.toml"
|
|
355
|
+
python3 "$SL" doctor-static "$SB/sl-symlinked.toml" "$SL_STATE" >"$SB/out" 2>&1 \
|
|
356
|
+
&& die "a symlinked config the doctor cannot read through reported green: $(cat "$SB/out")"
|
|
357
|
+
want "unowned symlink is red, both axes preserved" "grep -q '^symlink unowned$' '$SB/out'"
|
|
358
|
+
rm -f "$SB/sl-symlinked.toml"
|
|
359
|
+
cp "$SB/sl-required-axis.toml" "$CFG"
|
|
360
|
+
|
|
361
|
+
# drift: our item removed under a receipt is red; reinstall repairs. The seed
|
|
362
|
+
# is rebuilt first: the file still carries OUR merged item from the checks
|
|
363
|
+
# above, and adopting it back as "already-present" would make the inverse a
|
|
364
|
+
# (honest, but here unproven) no-op.
|
|
365
|
+
seed_config
|
|
366
|
+
python3 "$SL" install "$CFG" "$SL_STATE" >/dev/null
|
|
367
|
+
sed -i 's/"thread-title", //' "$CFG"
|
|
368
|
+
python3 "$SL" doctor-static "$CFG" "$SL_STATE" >"$SB/out" 2>&1 && die "removed thread-title doctor should be red"
|
|
369
|
+
want "removed thread-title under receipt is red and still names ownership" "grep -q '^thread-title-absent owned drift$' '$SB/out'"
|
|
370
|
+
python3 "$SL" install "$CFG" "$SL_STATE" >/dev/null
|
|
371
|
+
python3 "$SL" uninstall "$SL_STATE" >/dev/null
|
|
372
|
+
cmp -s "$SB/sl-seed.toml" "$CFG" && ok "status inverse restores operator bytes" || die "status inverse byte mismatch"
|
|
373
|
+
want "status state gone" "[ ! -e '$SL_STATE' ]"
|
|
374
|
+
|
|
375
|
+
###############################################################################
|
|
376
|
+
# Genuinely absent — the operator removes the whole status_line key we own;
|
|
377
|
+
# the inverse clears state cleanly instead of refusing or resurrecting it
|
|
378
|
+
###############################################################################
|
|
379
|
+
seed_config
|
|
380
|
+
python3 "$SL" install "$CFG" "$SL_STATE" >/dev/null
|
|
381
|
+
sed -i '/^status_line = /d' "$CFG"
|
|
382
|
+
toml_ok "$CFG"; ok "config with status_line key removed still parses"
|
|
383
|
+
python3 "$SL" uninstall "$SL_STATE" >"$SB/out" 2>&1 \
|
|
384
|
+
|| die "[QK:CODEX-SL-GENUINELY-ABSENT-CLEARS-STATE] genuinely-absent inverse refused instead of clearing state: $(cat "$SB/out")"
|
|
385
|
+
want "genuinely-absent inverse reports absence, not a restore" "grep -q 'status_line already absent' '$SB/out'"
|
|
386
|
+
want "genuinely-absent inverse clears state" "[ ! -e '$SL_STATE' ]"
|
|
387
|
+
|
|
388
|
+
# [tui] absent → the whole block is appended, and taken back whole
|
|
389
|
+
seed_config
|
|
390
|
+
sed -i '/^\[tui\]/,/^\[mcp_servers.other\]$/d' "$CFG" # drop the [tui] family
|
|
391
|
+
cp "$CFG" "$SB/no-tui.toml"
|
|
392
|
+
python3 "$SL" install "$CFG" "$SL_STATE" >"$SB/out"
|
|
393
|
+
want "[tui] appended when absent" "grep -q '^appended-table ' '$SB/out' && grep -q '^\[tui\]$' '$CFG'"
|
|
394
|
+
python3 "$SL" uninstall "$SL_STATE" >/dev/null
|
|
395
|
+
cmp -s "$SB/no-tui.toml" "$CFG" && ok "appended [tui] block taken back byte-exact" || die "appended-block inverse mismatch"
|
|
396
|
+
|
|
397
|
+
# status_line absent inside [tui] → the key is inserted; unrelated keys stay
|
|
398
|
+
seed_config
|
|
399
|
+
sed -i '/^status_line = /d' "$CFG"
|
|
400
|
+
cp "$CFG" "$SB/no-key.toml"
|
|
401
|
+
python3 "$SL" install "$CFG" "$SL_STATE" >"$SB/out"
|
|
402
|
+
want "key inserted into existing [tui]" "grep -q '^inserted-key ' '$SB/out' && grep -q '^status_line = \[\"thread-title\"\]$' '$CFG'"
|
|
403
|
+
python3 "$SL" uninstall "$SL_STATE" >/dev/null
|
|
404
|
+
cmp -s "$SB/no-key.toml" "$CFG" && ok "inserted key taken back, theme survived" || die "inserted-key inverse mismatch"
|
|
405
|
+
|
|
406
|
+
# already-present: adoption without a single byte written
|
|
407
|
+
seed_config
|
|
408
|
+
sed -i 's/^status_line = .*/status_line = ["thread-title", "model-with-reasoning"]/' "$CFG"
|
|
409
|
+
cp "$CFG" "$SB/already.toml"
|
|
410
|
+
MT1="$(stat -c %Y "$CFG")"; sleep 1.1
|
|
411
|
+
python3 "$SL" install "$CFG" "$SL_STATE" >"$SB/out"
|
|
412
|
+
MT2="$(stat -c %Y "$CFG")"
|
|
413
|
+
want "already-present adopt writes nothing" "grep -q '^already-present ' '$SB/out' && [ '$MT1' = '$MT2' ]"
|
|
414
|
+
python3 "$SL" uninstall "$SL_STATE" >"$SB/out"
|
|
415
|
+
want "already-present uninstall takes back nothing" "grep -q 'nothing to take back' '$SB/out' && [ '$(stat -c %Y "$CFG")' = '$MT1' ]"
|
|
416
|
+
|
|
417
|
+
# multi-line operator array and the empty array are both merged byte-honestly
|
|
418
|
+
seed_config
|
|
419
|
+
python3 - "$CFG" <<'PY'
|
|
420
|
+
import sys
|
|
421
|
+
p = sys.argv[1]
|
|
422
|
+
t = open(p).read()
|
|
423
|
+
t = t.replace('status_line = ["model-with-reasoning", "current-dir"]',
|
|
424
|
+
'status_line = [\n "model-with-reasoning", # operator note\n "current-dir",\n]')
|
|
425
|
+
open(p, 'w').write(t)
|
|
426
|
+
PY
|
|
427
|
+
cp "$CFG" "$SB/multiline.toml"
|
|
428
|
+
python3 "$SL" install "$CFG" "$SL_STATE" >/dev/null
|
|
429
|
+
toml_ok "$CFG"; ok "multi-line array merged and parseable"
|
|
430
|
+
python3 "$SL" uninstall "$SL_STATE" >/dev/null
|
|
431
|
+
cmp -s "$SB/multiline.toml" "$CFG" && ok "multi-line array restored byte-exact" || die "multiline inverse mismatch"
|
|
432
|
+
seed_config # the multiline value above spans lines; build the empty list fresh
|
|
433
|
+
sed -i 's/^status_line = .*/status_line = []/' "$CFG"
|
|
434
|
+
cp "$CFG" "$SB/empty.toml"
|
|
435
|
+
python3 "$SL" install "$CFG" "$SL_STATE" >/dev/null
|
|
436
|
+
want "empty list becomes exactly [\"thread-title\"]" "grep -q '^status_line = \[\"thread-title\"\]$' '$CFG'"
|
|
437
|
+
python3 "$SL" uninstall "$SL_STATE" >/dev/null
|
|
438
|
+
cmp -s "$SB/empty.toml" "$CFG" && ok "empty list restored byte-exact" || die "empty-list inverse mismatch"
|
|
439
|
+
|
|
440
|
+
# refusals — symlink, malformed, contrary value by name, foreign/retargeted state
|
|
441
|
+
seed_config
|
|
442
|
+
ln -sf "$SB/foreign.toml" "$CFG"
|
|
443
|
+
refuses "symlink config refused (statusline)" "symlink" python3 "$SL" install "$CFG" "$SL_STATE"
|
|
444
|
+
rm -f "$CFG"
|
|
445
|
+
printf 'garbage [[[\n' > "$CFG"
|
|
446
|
+
refuses "malformed TOML refused (statusline)" "not valid TOML" python3 "$SL" install "$CFG" "$SL_STATE"
|
|
447
|
+
seed_config
|
|
448
|
+
sed -i 's/^status_line = .*/status_line = "thread-title"/' "$CFG"
|
|
449
|
+
refuses "non-array status_line refused BY NAME" "status_line.*explicitly" python3 "$SL" install "$CFG" "$SL_STATE"
|
|
450
|
+
want "statusline refusals wrote no state" "[ ! -e '$SL_STATE' ]"
|
|
451
|
+
seed_config
|
|
452
|
+
python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE" >/dev/null
|
|
453
|
+
cp "$MCP_STATE" "$SL_STATE" # the foreign receipt, on our path
|
|
454
|
+
refuses "foreign-atom state refused (statusline)" "belongs to atom" python3 "$SL" install "$CFG" "$SL_STATE"
|
|
455
|
+
python3 "$MCP" uninstall "$MCP_STATE" >/dev/null
|
|
456
|
+
rm -f "$SL_STATE" # the parked foreign receipt must not poison the next case
|
|
457
|
+
seed_config
|
|
458
|
+
python3 "$SL" install "$CFG" "$SL_STATE" >/dev/null
|
|
459
|
+
sed -i "s|$(json_field "$SL_STATE" managedConfigPath)|$SB/elsewhere.toml|" "$SL_STATE"
|
|
460
|
+
refuses "retargeted state refused (statusline)" "refusing to retarget" python3 "$SL" install "$CFG" "$SL_STATE"
|
|
461
|
+
rm -f "$SL_STATE"
|
|
462
|
+
|
|
463
|
+
# operator restructures the list after install: the take-back refuses, by name
|
|
464
|
+
seed_config
|
|
465
|
+
python3 "$SL" install "$CFG" "$SL_STATE" >/dev/null
|
|
466
|
+
sed -i 's/^status_line = .*/status_line = [\n "weather",\n "thread-title",\n]/' "$CFG"
|
|
467
|
+
refuses "restructured list refuses blind take-back" "changed since install" python3 "$SL" uninstall "$SL_STATE"
|
|
468
|
+
rm -f "$SL_STATE"
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
###############################################################################
|
|
472
|
+
# Atom 3 — [tui].terminal_title includes thread-id (#95 lane B caller seat)
|
|
473
|
+
#
|
|
474
|
+
# Same writer shape as atom 2 and a DIFFERENT contract in two places, both of
|
|
475
|
+
# which have a wrong answer that still parses: the seeded list must LEAD with
|
|
476
|
+
# `activity` (the herdr Codex detector keys on the spinner/action-required
|
|
477
|
+
# prefix), and an existing operator list is APPENDED to, never prepended — a
|
|
478
|
+
# prepend would put thread-id ahead of an activity the operator already
|
|
479
|
+
# configured. The append position is the third: a trailing comma, a trailing
|
|
480
|
+
# comment or a `]` inside a string item each send a naive `rfind("]")` at the
|
|
481
|
+
# wrong bytes, and one of those emits `,,`.
|
|
482
|
+
###############################################################################
|
|
483
|
+
|
|
484
|
+
# seeded from nothing: the herdr-safe order, verbatim
|
|
485
|
+
rm -f "$CFG" "$TT_STATE"
|
|
486
|
+
python3 "$TT" install "$CFG" "$TT_STATE" >"$SB/out"
|
|
487
|
+
want "[QK:CODEX-TT-SEED-ACTIVITY-FIRST] seeded terminal_title leads with activity, ends with thread-id" \
|
|
488
|
+
"grep -q '^created-new ' '$SB/out' && grep -q '^terminal_title = \[\"activity\", \"project-name\", \"thread-id\"\]$' '$CFG'"
|
|
489
|
+
python3 "$TT" doctor-static "$CFG" "$TT_STATE" >"$SB/out"
|
|
490
|
+
want "seeded terminal-title doctor is green and owned" "grep -q '^thread-id-present owned$' '$SB/out'"
|
|
491
|
+
python3 "$TT" uninstall "$TT_STATE" >"$SB/out"
|
|
492
|
+
want "seeded file is removed whole by the inverse" "[ ! -e '$CFG' ] && [ ! -e '$TT_STATE' ]"
|
|
493
|
+
|
|
494
|
+
# an operator list is APPENDED to — the direction that is opposite to atom 2
|
|
495
|
+
seed_config
|
|
496
|
+
sed -i '/^theme = /i terminal_title = ["activity", "project-name"]' "$CFG"
|
|
497
|
+
cp "$CFG" "$SB/tt-seed.toml"
|
|
498
|
+
python3 "$TT" install "$CFG" "$TT_STATE" >"$SB/out"
|
|
499
|
+
want "[QK:CODEX-TT-APPENDS-AT-END] thread-id is appended LAST, operator order untouched" \
|
|
500
|
+
"grep -q '^merged-item ' '$SB/out' && grep -q '^terminal_title = \[\"activity\", \"project-name\", \"thread-id\"\]$' '$CFG'"
|
|
501
|
+
toml_ok "$CFG"; ok "merged terminal_title still parses"
|
|
502
|
+
MT1="$(stat -c %Y "$CFG")"; sleep 1.1
|
|
503
|
+
python3 "$TT" install "$CFG" "$TT_STATE" >/dev/null
|
|
504
|
+
want "terminal-title reinstall writes nothing" "[ '$MT1' = '$(stat -c %Y "$CFG")' ]"
|
|
505
|
+
python3 "$TT" uninstall "$TT_STATE" >/dev/null
|
|
506
|
+
cmp -s "$SB/tt-seed.toml" "$CFG" && ok "appended item restored byte-exact" || die "terminal-title inverse mismatch"
|
|
507
|
+
|
|
508
|
+
# an activity-TERMINATED operator list: the item still lands after it
|
|
509
|
+
seed_config
|
|
510
|
+
sed -i '/^theme = /i terminal_title = ["project-name", "activity"]' "$CFG"
|
|
511
|
+
python3 "$TT" install "$CFG" "$TT_STATE" >/dev/null
|
|
512
|
+
want "an activity-terminated list keeps activity in place and appends after it" \
|
|
513
|
+
"grep -q '^terminal_title = \[\"project-name\", \"activity\", \"thread-id\"\]$' '$CFG'"
|
|
514
|
+
python3 "$TT" uninstall "$TT_STATE" >/dev/null
|
|
515
|
+
|
|
516
|
+
# multi-line array with a TRAILING COMMA — the shape a naive append breaks
|
|
517
|
+
seed_config
|
|
518
|
+
python3 - "$CFG" <<'PY'
|
|
519
|
+
import sys
|
|
520
|
+
p = sys.argv[1]
|
|
521
|
+
t = open(p).read()
|
|
522
|
+
t = t.replace('theme = "zenburn"',
|
|
523
|
+
'terminal_title = [\n "activity", # operator note\n "project-name",\n]\ntheme = "zenburn"')
|
|
524
|
+
open(p, 'w').write(t)
|
|
525
|
+
PY
|
|
526
|
+
cp "$CFG" "$SB/tt-multiline.toml"
|
|
527
|
+
python3 "$TT" install "$CFG" "$TT_STATE" >/dev/null
|
|
528
|
+
toml_ok "$CFG"
|
|
529
|
+
want "[QK:CODEX-TT-TRAILING-COMMA] a trailing-comma array is appended to without emitting a double comma" \
|
|
530
|
+
"! grep -q ',[[:space:]]*,' '$CFG' && python3 -c \"import tomllib,sys; d=tomllib.load(open(sys.argv[1],'rb')); assert d['tui']['terminal_title']==['activity','project-name','thread-id'], d\" '$CFG'"
|
|
531
|
+
python3 "$TT" uninstall "$TT_STATE" >/dev/null
|
|
532
|
+
cmp -s "$SB/tt-multiline.toml" "$CFG" && ok "trailing-comma array restored byte-exact" || die "trailing-comma inverse mismatch"
|
|
533
|
+
|
|
534
|
+
# a trailing COMMENT that itself contains `]` — rfind("]") would splice into it
|
|
535
|
+
seed_config
|
|
536
|
+
sed -i '/^theme = /i terminal_title = ["activity"] # keep ] this' "$CFG"
|
|
537
|
+
cp "$CFG" "$SB/tt-comment.toml"
|
|
538
|
+
python3 "$TT" install "$CFG" "$TT_STATE" >/dev/null
|
|
539
|
+
want "[QK:CODEX-TT-COMMENT-NOT-SPLICED] the item lands inside the array and the operator's comment survives" \
|
|
540
|
+
"grep -q '^terminal_title = \[\"activity\", \"thread-id\"\] # keep \] this$' '$CFG'"
|
|
541
|
+
python3 "$TT" uninstall "$TT_STATE" >/dev/null
|
|
542
|
+
cmp -s "$SB/tt-comment.toml" "$CFG" && ok "commented line restored byte-exact" || die "comment inverse mismatch"
|
|
543
|
+
|
|
544
|
+
# a `]` inside a string item, and the empty list
|
|
545
|
+
seed_config
|
|
546
|
+
sed -i '/^theme = /i terminal_title = ["a]b", "activity"]' "$CFG"
|
|
547
|
+
python3 "$TT" install "$CFG" "$TT_STATE" >/dev/null
|
|
548
|
+
want "a ] inside a string item does not become the append point" \
|
|
549
|
+
"grep -q '^terminal_title = \[\"a\]b\", \"activity\", \"thread-id\"\]$' '$CFG'"
|
|
550
|
+
python3 "$TT" uninstall "$TT_STATE" >/dev/null
|
|
551
|
+
seed_config
|
|
552
|
+
sed -i '/^theme = /i terminal_title = []' "$CFG"
|
|
553
|
+
cp "$CFG" "$SB/tt-empty.toml"
|
|
554
|
+
python3 "$TT" install "$CFG" "$TT_STATE" >/dev/null
|
|
555
|
+
want "empty terminal_title becomes exactly [\"thread-id\"]" "grep -q '^terminal_title = \[\"thread-id\"\]$' '$CFG'"
|
|
556
|
+
python3 "$TT" uninstall "$TT_STATE" >/dev/null
|
|
557
|
+
cmp -s "$SB/tt-empty.toml" "$CFG" && ok "empty terminal_title restored byte-exact" || die "empty terminal_title inverse mismatch"
|
|
558
|
+
|
|
559
|
+
# an operator-authored item is UNOWNED, and nothing is written over it
|
|
560
|
+
seed_config
|
|
561
|
+
sed -i '/^theme = /i terminal_title = ["activity", "thread-id"]' "$CFG"
|
|
562
|
+
cp "$CFG" "$SB/tt-operator.toml"
|
|
563
|
+
rm -f "$TT_STATE"
|
|
564
|
+
MT1="$(stat -c %Y "$CFG")"; sleep 1.1
|
|
565
|
+
python3 "$TT" install "$CFG" "$TT_STATE" >"$SB/out"
|
|
566
|
+
want "[QK:CODEX-TT-OPERATOR-ITEM-UNOWNED] an operator-authored thread-id writes NO receipt and no config byte" \
|
|
567
|
+
"grep -q '^already-present ' '$SB/out' && [ ! -e '$TT_STATE' ] && [ '$MT1' = '$(stat -c %Y "$CFG")' ]"
|
|
568
|
+
python3 "$TT" doctor-static "$CFG" "$TT_STATE" >"$SB/out"
|
|
569
|
+
want "the doctor reports it UNOWNED — we claim no edit we did not make" \
|
|
570
|
+
"grep -q '^thread-id-present unowned$' '$SB/out'"
|
|
571
|
+
python3 "$TT" uninstall "$TT_STATE" >"$SB/out" 2>&1 \
|
|
572
|
+
&& die "uninstall claimed to take back an operator's own item"
|
|
573
|
+
grep -q 'nothing to undo' "$SB/out" || die "unowned uninstall refusal is unnamed: $(cat "$SB/out")"
|
|
574
|
+
cmp -s "$SB/tt-operator.toml" "$CFG" && ok "the refused inverse left the operator's config byte-identical" \
|
|
575
|
+
|| die "unowned uninstall mutated the operator's config"
|
|
576
|
+
|
|
577
|
+
# ...and that is what keeps the INVERSE honest across a hand edit. With a receipt parked over an
|
|
578
|
+
# operator's item, a reinstall after they removed it splices ours in while the stale receipt still
|
|
579
|
+
# says it was already there — and the inverse then reports "nothing to take back" and leaves our
|
|
580
|
+
# bytes behind for good. Writing nothing above makes that second install an ordinary merged-item.
|
|
581
|
+
seed_config
|
|
582
|
+
sed -i '/^theme = /i terminal_title = ["activity"]' "$CFG"
|
|
583
|
+
cp "$CFG" "$SB/tt-inverse.toml"
|
|
584
|
+
rm -f "$TT_STATE"
|
|
585
|
+
python3 "$TT" install "$CFG" "$TT_STATE" >/dev/null
|
|
586
|
+
sed -i 's/, "thread-id"\]/]/' "$CFG" # the operator takes our item back by hand
|
|
587
|
+
python3 "$TT" install "$CFG" "$TT_STATE" >"$SB/out"
|
|
588
|
+
want "a reinstall after a hand removal records what it ACTUALLY did" \
|
|
589
|
+
"grep -q '^merged-item ' '$SB/out' && grep -q '\"detectMode\": \"merged-item\"' '$TT_STATE'"
|
|
590
|
+
python3 "$TT" uninstall "$TT_STATE" >/dev/null
|
|
591
|
+
cmp -s "$SB/tt-inverse.toml" "$CFG" && ok "the inverse takes our re-added item back, leaving nothing behind" \
|
|
592
|
+
|| { printf 'left behind:\n%s\n' "$(cat "$CFG")"; die "reinstall inverse left our item in the operator's config"; }
|
|
593
|
+
rm -f "$TT_STATE"
|
|
594
|
+
|
|
595
|
+
# the doctor's subject is the REQUIRED caller-seat axis, receipt or no receipt
|
|
596
|
+
seed_config
|
|
597
|
+
sed -i '/^theme = /i terminal_title = ["activity", "thread-id"]' "$CFG"
|
|
598
|
+
rm -f "$TT_STATE"
|
|
599
|
+
python3 "$TT" doctor-static "$CFG" "$TT_STATE" >"$SB/out"
|
|
600
|
+
want "unowned thread-id is green (state-independent)" "grep -q '^thread-id-present unowned$' '$SB/out'"
|
|
601
|
+
sed -i 's/, "thread-id"\]/]/' "$CFG"
|
|
602
|
+
python3 "$TT" doctor-static "$CFG" "$TT_STATE" >"$SB/out" 2>&1 \
|
|
603
|
+
&& die "[QK:CODEX-TT-DOCTOR-REQUIRES-SEAT-AXIS] unowned config WITHOUT thread-id reported green: $(cat "$SB/out")"
|
|
604
|
+
want "[QK:CODEX-TT-DOCTOR-REQUIRES-SEAT-AXIS] a missing caller-seat axis is RED even unowned" \
|
|
605
|
+
"grep -q '^thread-id-absent unowned$' '$SB/out'"
|
|
606
|
+
python3 "$TT" install "$CFG" "$TT_STATE" >/dev/null
|
|
607
|
+
sed -i 's/, "thread-id"\]/]/' "$CFG"
|
|
608
|
+
python3 "$TT" doctor-static "$CFG" "$TT_STATE" >"$SB/out" 2>&1 || true
|
|
609
|
+
want "our receipt over a vanished item reports drift, not green" "grep -q '^thread-id-absent owned drift$' '$SB/out'"
|
|
610
|
+
rm -f "$TT_STATE"
|
|
611
|
+
|
|
612
|
+
# the two [tui] atoms are independent: neither sees the other's key
|
|
613
|
+
seed_config
|
|
614
|
+
python3 "$SL" install "$CFG" "$SL_STATE" >/dev/null
|
|
615
|
+
python3 "$TT" install "$CFG" "$TT_STATE" >/dev/null
|
|
616
|
+
want "[QK:CODEX-TT-ATOM-INDEPENDENCE] both [tui] atoms live side by side" \
|
|
617
|
+
"python3 -c \"import tomllib,sys; d=tomllib.load(open(sys.argv[1],'rb'))['tui']; assert d['status_line']==['thread-title','model-with-reasoning','current-dir'], d; assert d['terminal_title']==['activity','project-name','thread-id'], d\" '$CFG'"
|
|
618
|
+
python3 "$TT" uninstall "$TT_STATE" >/dev/null
|
|
619
|
+
want "[QK:CODEX-TT-ATOM-INDEPENDENCE] taking back terminal_title leaves status_line exactly as it was" \
|
|
620
|
+
"grep -q '^status_line = \[\"thread-title\", \"model-with-reasoning\", \"current-dir\"\]$' '$CFG' && ! grep -q 'terminal_title' '$CFG'"
|
|
621
|
+
python3 "$SL" uninstall "$SL_STATE" >/dev/null
|
|
622
|
+
|
|
623
|
+
# refusals — symlink, malformed, contrary value by name, foreign/retargeted state
|
|
624
|
+
seed_config
|
|
625
|
+
ln -sf "$SB/foreign.toml" "$CFG"
|
|
626
|
+
refuses "symlink config refused (terminal-title)" "symlink" python3 "$TT" install "$CFG" "$TT_STATE"
|
|
627
|
+
rm -f "$CFG"
|
|
628
|
+
printf 'garbage [[[\n' > "$CFG"
|
|
629
|
+
refuses "malformed TOML refused (terminal-title)" "not valid TOML" python3 "$TT" install "$CFG" "$TT_STATE"
|
|
630
|
+
seed_config
|
|
631
|
+
sed -i '/^theme = /i terminal_title = "thread-id"' "$CFG"
|
|
632
|
+
refuses "non-array terminal_title refused BY NAME" "terminal_title.*explicitly" python3 "$TT" install "$CFG" "$TT_STATE"
|
|
633
|
+
want "terminal-title refusals wrote no state" "[ ! -e '$TT_STATE' ]"
|
|
634
|
+
seed_config
|
|
635
|
+
python3 "$SL" install "$CFG" "$SL_STATE" >/dev/null
|
|
636
|
+
cp "$SL_STATE" "$TT_STATE" # atom 2's receipt, on our path
|
|
637
|
+
refuses "foreign-atom state refused (terminal-title)" "belongs to atom" python3 "$TT" install "$CFG" "$TT_STATE"
|
|
638
|
+
python3 "$SL" uninstall "$SL_STATE" >/dev/null
|
|
639
|
+
rm -f "$TT_STATE"
|
|
640
|
+
seed_config
|
|
641
|
+
python3 "$TT" install "$CFG" "$TT_STATE" >/dev/null
|
|
642
|
+
sed -i "s|$(json_field "$TT_STATE" managedConfigPath)|$SB/elsewhere.toml|" "$TT_STATE"
|
|
643
|
+
refuses "retargeted state refused (terminal-title)" "refusing to retarget" python3 "$TT" install "$CFG" "$TT_STATE"
|
|
644
|
+
rm -f "$TT_STATE"
|
|
645
|
+
|
|
646
|
+
# operator restructures the list after install: the take-back refuses, by name
|
|
647
|
+
seed_config
|
|
648
|
+
python3 "$TT" install "$CFG" "$TT_STATE" >/dev/null
|
|
649
|
+
sed -i 's/^terminal_title = .*/terminal_title = [\n "weather",\n "thread-id",\n]/' "$CFG"
|
|
650
|
+
refuses "restructured terminal_title refuses blind take-back" "changed since install" python3 "$TT" uninstall "$TT_STATE"
|
|
651
|
+
rm -f "$TT_STATE"
|
|
652
|
+
|
|
653
|
+
###############################################################################
|
|
654
|
+
# Unrelated operator config survives the whole cycle, byte for byte
|
|
655
|
+
###############################################################################
|
|
656
|
+
seed_config
|
|
657
|
+
cp "$CFG" "$SB/final-seed.toml"
|
|
658
|
+
python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE" >/dev/null
|
|
659
|
+
python3 "$SL" install "$CFG" "$SL_STATE" >/dev/null
|
|
660
|
+
python3 "$TT" install "$CFG" "$TT_STATE" >/dev/null
|
|
661
|
+
toml_ok "$CFG"; ok "fully installed config parses as TOML"
|
|
662
|
+
python3 - "$CFG" <<'PY'
|
|
663
|
+
import tomllib, sys
|
|
664
|
+
d = tomllib.load(open(sys.argv[1], "rb"))
|
|
665
|
+
assert d["hooks"]["state"]["~/c.toml:session_start:0:0"] == {"trusted_hash": "sha256:abc"}, d
|
|
666
|
+
assert d["projects"]["/home/op/work"] == {"trust_level": "trusted"}, d
|
|
667
|
+
assert d["mcp_servers"]["other"] == {"command": "keep-me"}, d
|
|
668
|
+
assert d["mcp_servers"]["entwurf-bridge"] == {
|
|
669
|
+
"command": "entwurf-bridge",
|
|
670
|
+
"env_vars": [
|
|
671
|
+
"CODEX_HOME",
|
|
672
|
+
"ENTWURF_DIR",
|
|
673
|
+
"PI_CODING_AGENT_DIR",
|
|
674
|
+
"ENTWURF_META_SESSIONS_DIR",
|
|
675
|
+
"ENTWURF_META_MAILBOX_DIR",
|
|
676
|
+
"ENTWURF_META_SENDERS_DIR",
|
|
677
|
+
"ENTWURF_META_RECEIVERS_DIR",
|
|
678
|
+
"TMUX",
|
|
679
|
+
"TMUX_PANE",
|
|
680
|
+
],
|
|
681
|
+
"env": {"ENTWURF_BRIDGE_NATIVE_HOST": "codex"},
|
|
682
|
+
}, d
|
|
683
|
+
assert d["tui"]["status_line"] == ["thread-title", "model-with-reasoning", "current-dir"], d
|
|
684
|
+
assert d["tui"]["terminal_title"] == ["activity", "project-name", "thread-id"], d
|
|
685
|
+
assert d["tui"]["theme"] == "zenburn" and d["tui"]["model_availability_nux"] == {"gpt-5.5": 4}, d
|
|
686
|
+
assert d["plugins"]["github@openai-curated"] == {"enabled": False}, d
|
|
687
|
+
PY
|
|
688
|
+
ok "unrelated tables intact and all three atoms semantically live"
|
|
689
|
+
# the vendor writes trust state back between install and uninstall (M1)
|
|
690
|
+
printf '[hooks.state."~/c.toml:stop:0:0"]\ntrusted_hash = "sha256:def"\n' >> "$CFG"
|
|
691
|
+
python3 "$MCP" uninstall "$MCP_STATE" >/dev/null
|
|
692
|
+
python3 "$SL" uninstall "$SL_STATE" >/dev/null
|
|
693
|
+
python3 "$TT" uninstall "$TT_STATE" >/dev/null
|
|
694
|
+
printf '[hooks.state."~/c.toml:stop:0:0"]\ntrusted_hash = "sha256:def"\n' >> "$SB/final-seed.toml"
|
|
695
|
+
cmp -s "$SB/final-seed.toml" "$CFG" && ok "full cycle leaves only vendor-added bytes (unrelated config survives)" || die "final byte mismatch"
|
|
696
|
+
want "all three states cleared" "[ ! -e '$MCP_STATE' ] && [ ! -e '$SL_STATE' ] && [ ! -e '$TT_STATE' ]"
|
|
697
|
+
|
|
698
|
+
REPO_AFTER="$(cd "$REPO_DIR" && git status --porcelain)"
|
|
699
|
+
[ "$REPO_BEFORE" = "$REPO_AFTER" ] || die "smoke changed the checkout"
|
|
700
|
+
printf '\nsmoke-codex-config-state: %d checks passed\n' "$pass"
|