@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.
Files changed (111) hide show
  1. package/AGENTS.md +85 -190
  2. package/BASELINE.md +6 -3
  3. package/CHANGELOG.md +473 -14
  4. package/CONTRIBUTING.md +1 -1
  5. package/DELIVERY.md +332 -60
  6. package/README.md +100 -22
  7. package/VERIFY.md +93 -7
  8. package/docs/acp-backend-rail.md +0 -1
  9. package/docs/external-mcp-host.md +64 -33
  10. package/docs/setup-clean-host.md +151 -17
  11. package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +130 -114
  12. package/mcp/entwurf-bridge/dist/pi-extensions/lib/codex-caller-seat.js +174 -0
  13. package/mcp/entwurf-bridge/dist/pi-extensions/lib/codex-fresh-preflight.js +519 -0
  14. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-fact-provider.js +22 -2
  15. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-peers-render.js +3 -1
  16. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-contract.js +6 -7
  17. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-native-push.js +30 -17
  18. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-production.js +7 -1
  19. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-runner.js +3 -2
  20. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-send.js +8 -4
  21. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-surface.js +7 -4
  22. package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-mailbox-body.js +20 -11
  23. package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-sender-identity.js +227 -1
  24. package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +189 -19
  25. package/mcp/entwurf-bridge/dist/pi-extensions/lib/native-push/adapter.js +10 -7
  26. package/mcp/entwurf-bridge/dist/pi-extensions/lib/native-push/codex-ws-client.js +403 -0
  27. package/mcp/entwurf-bridge/dist/pi-extensions/lib/native-push/register.js +4 -4
  28. package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-hook-codex.js +325 -0
  29. package/mcp/entwurf-bridge/dist/scripts/codex-socket-path.js +30 -0
  30. package/mcp/entwurf-bridge/dist/scripts/meta-bridge-fresh-cut.js +6 -1
  31. package/mcp/entwurf-bridge/src/index.ts +154 -116
  32. package/mcp/entwurf-bridge/tsconfig.build.json +10 -0
  33. package/package.json +13 -4
  34. package/pi-extensions/entwurf-control.ts +71 -19
  35. package/pi-extensions/lib/codex-caller-seat.ts +204 -0
  36. package/pi-extensions/lib/codex-fresh-preflight.ts +580 -0
  37. package/pi-extensions/lib/compaction-send-guard.ts +80 -0
  38. package/pi-extensions/lib/entwurf-fact-provider.ts +29 -3
  39. package/pi-extensions/lib/entwurf-peers-render.ts +4 -1
  40. package/pi-extensions/lib/entwurf-v2-contract.ts +6 -7
  41. package/pi-extensions/lib/entwurf-v2-native-push.ts +35 -18
  42. package/pi-extensions/lib/entwurf-v2-production.ts +10 -3
  43. package/pi-extensions/lib/entwurf-v2-runner.ts +3 -2
  44. package/pi-extensions/lib/entwurf-v2-send.ts +16 -11
  45. package/pi-extensions/lib/entwurf-v2-surface.ts +7 -4
  46. package/pi-extensions/lib/meta-mailbox-body.ts +22 -13
  47. package/pi-extensions/lib/meta-sender-identity.ts +305 -0
  48. package/pi-extensions/lib/mux-fresh-call.ts +233 -29
  49. package/pi-extensions/lib/native-push/adapter.ts +21 -24
  50. package/pi-extensions/lib/native-push/codex-ws-client.ts +506 -0
  51. package/pi-extensions/lib/native-push/register.ts +7 -9
  52. package/pi-extensions/meta-bridge-hook-codex.ts +371 -0
  53. package/run.sh +342 -28
  54. package/scripts/check-agy-sender-identity.ts +1 -1
  55. package/scripts/check-codex-app-server-launch.ts +445 -0
  56. package/scripts/check-codex-birth-hook.ts +264 -0
  57. package/scripts/check-codex-bridge-identity.ts +179 -0
  58. package/scripts/check-codex-native-push.ts +386 -0
  59. package/scripts/check-codex-sender-identity.ts +495 -0
  60. package/scripts/check-compaction-send-guard.ts +130 -0
  61. package/scripts/check-copilot-receive-arm.ts +4 -1
  62. package/scripts/check-entwurf-fact-provider.ts +38 -0
  63. package/scripts/check-entwurf-peers-surface.ts +13 -1
  64. package/scripts/check-entwurf-self-address.ts +15 -16
  65. package/scripts/check-entwurf-v2-contract.ts +4 -3
  66. package/scripts/check-entwurf-v2-decider.ts +7 -5
  67. package/scripts/check-entwurf-v2-native-push.ts +35 -7
  68. package/scripts/check-entwurf-v2-production.ts +245 -12
  69. package/scripts/check-entwurf-v2-runner.ts +1 -1
  70. package/scripts/check-entwurf-v2-send.ts +26 -7
  71. package/scripts/check-entwurf-v2-surface.ts +1 -1
  72. package/scripts/check-gate-qualification.ts +8 -3
  73. package/scripts/check-harness-admission-parity.ts +0 -1
  74. package/scripts/check-mux-launch-tmux.ts +345 -4
  75. package/scripts/check-native-push-adapter.ts +20 -16
  76. package/scripts/check-native-push-register.ts +5 -1
  77. package/scripts/check-release-gate-outcomes.ts +47 -1
  78. package/scripts/check-setup-qualification.sh +3 -1
  79. package/scripts/codex-app-server-launch.sh +275 -0
  80. package/scripts/codex-birth-doctor.sh +276 -0
  81. package/scripts/codex-birth-install.sh +414 -0
  82. package/scripts/codex-birth-uninstall.sh +170 -0
  83. package/scripts/codex-mcp-config.py +435 -0
  84. package/scripts/codex-socket-path.ts +33 -0
  85. package/scripts/codex-statusline-config.py +434 -0
  86. package/scripts/codex-terminal-title-config.py +500 -0
  87. package/scripts/codex_toml_io.py +653 -0
  88. package/scripts/lib/codex-fresh-live-protocol.ts +121 -0
  89. package/scripts/lib/codex-fresh-source-receipts.ts +426 -0
  90. package/scripts/lib/launch-receipt-windows.ts +46 -0
  91. package/scripts/lib/tmux-coordinate-row.ts +58 -0
  92. package/scripts/meta-bridge-fresh-cut.ts +6 -1
  93. package/scripts/mutants/codex-app-server-launch.json +157 -0
  94. package/scripts/mutants/codex-caller-seat.json +336 -0
  95. package/scripts/mutants/codex-native.json +838 -0
  96. package/scripts/mutants/compaction-send-guard.json +103 -0
  97. package/scripts/mutants/entwurf-peers.json +19 -0
  98. package/scripts/mutants/mux-fresh-call.json +93 -10
  99. package/scripts/mutants/omp-fresh.json +6 -4
  100. package/scripts/mutants/release-gate.json +13 -0
  101. package/scripts/mutants/v2-surface.json +75 -1
  102. package/scripts/raw-async-delivery/README.md +2 -1
  103. package/scripts/raw-codex-measure/README.md +114 -46
  104. package/scripts/smoke-agy-native-push-live.ts +3 -1
  105. package/scripts/smoke-codex-birth.sh +347 -0
  106. package/scripts/smoke-codex-config-state.sh +700 -0
  107. package/scripts/smoke-codex-fresh-live.ts +1426 -0
  108. package/scripts/smoke-codex-native-push-live.ts +75 -0
  109. package/scripts/smoke-entwurf-chain-live.ts +50 -0
  110. package/scripts/smoke-setup-verdict.sh +125 -10
  111. 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"