@junghanacs/entwurf 0.20.0 → 0.21.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 (112) hide show
  1. package/AGENTS.md +85 -190
  2. package/BASELINE.md +6 -5
  3. package/CHANGELOG.md +220 -14
  4. package/CONTRIBUTING.md +1 -1
  5. package/DELIVERY.md +202 -60
  6. package/README.md +54 -26
  7. package/VERIFY.md +37 -7
  8. package/docs/acp-backend-rail.md +31 -15
  9. package/docs/external-mcp-host.md +58 -35
  10. package/docs/fresh-cut-policy.md +3 -2
  11. package/docs/setup-clean-host.md +110 -17
  12. package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +101 -109
  13. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/backend-adapter.js +2 -2
  14. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/tool-surface.js +6 -0
  15. package/mcp/entwurf-bridge/dist/pi-extensions/lib/codex-fresh-preflight.js +326 -0
  16. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-fact-provider.js +22 -2
  17. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-peers-render.js +3 -1
  18. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-contract.js +6 -7
  19. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-native-push.js +30 -17
  20. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-production.js +7 -1
  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 +50 -15
  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/meta-bridge-fresh-cut.js +6 -1
  30. package/mcp/entwurf-bridge/src/index.ts +115 -111
  31. package/mcp/entwurf-bridge/tsconfig.build.json +1 -0
  32. package/package.json +14 -5
  33. package/pi-extensions/entwurf-control.ts +71 -19
  34. package/pi-extensions/lib/acp/acp-client.ts +3 -3
  35. package/pi-extensions/lib/acp/backend-adapter.ts +3 -3
  36. package/pi-extensions/lib/acp/backend.ts +3 -3
  37. package/pi-extensions/lib/acp/event-mapper.ts +4 -4
  38. package/pi-extensions/lib/acp/tool-surface.ts +6 -0
  39. package/pi-extensions/lib/codex-fresh-preflight.ts +363 -0
  40. package/pi-extensions/lib/compaction-send-guard.ts +80 -0
  41. package/pi-extensions/lib/entwurf-fact-provider.ts +29 -3
  42. package/pi-extensions/lib/entwurf-peers-render.ts +4 -1
  43. package/pi-extensions/lib/entwurf-v2-contract.ts +6 -7
  44. package/pi-extensions/lib/entwurf-v2-native-push.ts +35 -18
  45. package/pi-extensions/lib/entwurf-v2-production.ts +10 -3
  46. package/pi-extensions/lib/entwurf-v2-surface.ts +7 -4
  47. package/pi-extensions/lib/meta-mailbox-body.ts +22 -13
  48. package/pi-extensions/lib/meta-sender-identity.ts +305 -0
  49. package/pi-extensions/lib/mux-fresh-call.ts +64 -19
  50. package/pi-extensions/lib/native-push/adapter.ts +21 -24
  51. package/pi-extensions/lib/native-push/codex-ws-client.ts +506 -0
  52. package/pi-extensions/lib/native-push/register.ts +7 -9
  53. package/pi-extensions/meta-bridge-hook-codex.ts +371 -0
  54. package/run.sh +251 -35
  55. package/scripts/check-acp-usage-accounting.ts +9 -9
  56. package/scripts/check-agy-sender-identity.ts +1 -1
  57. package/scripts/check-codex-birth-hook.ts +264 -0
  58. package/scripts/check-codex-bridge-identity.ts +179 -0
  59. package/scripts/check-codex-native-push.ts +386 -0
  60. package/scripts/check-codex-sender-identity.ts +495 -0
  61. package/scripts/check-compaction-send-guard.ts +130 -0
  62. package/scripts/check-copilot-receive-arm.ts +4 -1
  63. package/scripts/check-entwurf-fact-provider.ts +38 -0
  64. package/scripts/check-entwurf-peers-surface.ts +13 -1
  65. package/scripts/check-entwurf-self-address.ts +15 -16
  66. package/scripts/check-entwurf-v2-contract.ts +4 -3
  67. package/scripts/check-entwurf-v2-decider.ts +7 -5
  68. package/scripts/check-entwurf-v2-native-push.ts +35 -7
  69. package/scripts/check-entwurf-v2-production.ts +203 -11
  70. package/scripts/check-entwurf-v2-runner.ts +1 -1
  71. package/scripts/check-entwurf-v2-surface.ts +1 -1
  72. package/scripts/check-gate-qualification.ts +7 -4
  73. package/scripts/check-harness-admission-parity.ts +0 -1
  74. package/scripts/check-install-surface.ts +23 -7
  75. package/scripts/check-mux-launch-tmux.ts +47 -2
  76. package/scripts/check-native-push-adapter.ts +20 -16
  77. package/scripts/check-native-push-register.ts +5 -1
  78. package/scripts/check-release-gate-outcomes.ts +47 -1
  79. package/scripts/check-setup-qualification.sh +3 -1
  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-statusline-config.py +434 -0
  85. package/scripts/codex_toml_io.py +532 -0
  86. package/scripts/lib/codex-fresh-live-protocol.ts +113 -0
  87. package/scripts/lib/codex-fresh-source-receipts.ts +399 -0
  88. package/scripts/lib/launch-receipt-windows.ts +46 -0
  89. package/scripts/lib/tmux-coordinate-row.ts +58 -0
  90. package/scripts/meta-bridge-fresh-cut.ts +6 -1
  91. package/scripts/mutants/codex-native.json +838 -0
  92. package/scripts/mutants/compaction-send-guard.json +103 -0
  93. package/scripts/mutants/entwurf-peers.json +19 -0
  94. package/scripts/mutants/mux-fresh-call.json +19 -8
  95. package/scripts/mutants/omp-fresh.json +6 -4
  96. package/scripts/mutants/pi-package-ownership.json +26 -0
  97. package/scripts/mutants/release-gate.json +13 -0
  98. package/scripts/mutants/v2-surface.json +53 -1
  99. package/scripts/pi_settings_io.py +3 -1
  100. package/scripts/raw-async-delivery/README.md +2 -1
  101. package/scripts/raw-codex-measure/README.md +114 -46
  102. package/scripts/register-pi-package.py +38 -30
  103. package/scripts/register-pi-provider.py +3 -2
  104. package/scripts/smoke-acp-raw-turn-live.ts +1 -1
  105. package/scripts/smoke-agy-native-push-live.ts +3 -1
  106. package/scripts/smoke-codex-birth.sh +347 -0
  107. package/scripts/smoke-codex-config-state.sh +511 -0
  108. package/scripts/smoke-codex-fresh-live.ts +1186 -0
  109. package/scripts/smoke-codex-native-push-live.ts +75 -0
  110. package/scripts/smoke-setup-verdict.sh +123 -10
  111. package/scripts/smoke-user-scope-citizen.sh +199 -71
  112. package/scripts/tsconfig.json +1 -0
@@ -0,0 +1,511 @@
1
+ #!/usr/bin/env bash
2
+ # smoke-codex-config-state — hermetic install/doctor/inverse contract for the
3
+ # two Codex config atoms (codex-mcp-config.py + codex-statusline-config.py).
4
+ # No Codex process, no model turn: the vendor's config.toml surface only.
5
+ set -euo pipefail
6
+
7
+ REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
8
+ MCP="$REPO_DIR/scripts/codex-mcp-config.py"
9
+ SL="$REPO_DIR/scripts/codex-statusline-config.py"
10
+ pass=0
11
+ ok() { printf ' ok %s\n' "$1"; pass=$((pass + 1)); }
12
+ die() { printf 'FAIL: %s\n' "$1" >&2; exit 1; }
13
+ want() { eval "$2" && ok "$1" || die "$1"; }
14
+ refuses() { # <label> <expected-substring-on-stderr> <cmd...>
15
+ local label="$1" needle="$2"; shift 2
16
+ local err rc=0
17
+ err="$("$@" 2>&1 >/dev/null)" || rc=$?
18
+ [ "$rc" -ne 0 ] && printf '%s' "$err" | grep -q "$needle" || die "$label (rc=$rc, stderr: $err)"
19
+ ok "$label"
20
+ }
21
+
22
+ REPO_BEFORE="$(cd "$REPO_DIR" && git status --porcelain)"
23
+ SB="$(mktemp -d)"
24
+ trap 'rm -rf "$SB"' EXIT
25
+ export HOME="$SB/home"
26
+ export XDG_DATA_HOME="$SB/xdg"
27
+ export CODEX_HOME="$HOME/.codex" # the only surface both atoms touch
28
+ export ENTWURF_TEST_IGNORED="$SB" # prove nothing else leaks in
29
+ export ENTWURF_DIR="$SB/custom-entwurf"
30
+ export PI_CODING_AGENT_DIR="$SB/custom-pi"
31
+ export ENTWURF_META_SESSIONS_DIR="$SB/custom-meta/sessions"
32
+ export ENTWURF_META_MAILBOX_DIR="$SB/custom-meta/mailbox"
33
+ export ENTWURF_META_SENDERS_DIR="$SB/custom-meta/senders"
34
+ export ENTWURF_META_RECEIVERS_DIR="$SB/custom-meta/receivers"
35
+ export TMUX="$SB/custom-tmux/default,123,0"
36
+ export TMUX_PANE="%77"
37
+ export PYTHONDONTWRITEBYTECODE=1 # imported parser helpers must not write into the checkout
38
+ mkdir -p "$CODEX_HOME" "$(dirname "$XDG_DATA_HOME")"
39
+ CFG="$CODEX_HOME/config.toml"
40
+ MCP_STATE="$XDG_DATA_HOME/entwurf/codex-mcp/install-state.json"
41
+ SL_STATE="$XDG_DATA_HOME/entwurf/codex-statusline/install-state.json"
42
+
43
+ seed_config() { # the operator's hand-edited file: quoted header keys, nested
44
+ # tables, an unrelated MCP server, and NO entwurf atom content.
45
+ cat > "$CFG" <<'EOF'
46
+ model = "gpt-5.6-sol"
47
+ approval_policy = "never"
48
+ [notice]
49
+ hide_full_access_warning = true
50
+ [projects."/home/op/work"]
51
+ trust_level = "trusted"
52
+ [hooks.state."~/c.toml:session_start:0:0"]
53
+ trusted_hash = "sha256:abc"
54
+ [tui]
55
+ status_line = ["model-with-reasoning", "current-dir"]
56
+ theme = "zenburn"
57
+ [tui.model_availability_nux]
58
+ "gpt-5.5" = 4
59
+ [mcp_servers.other]
60
+ command = "keep-me"
61
+ [plugins."github@openai-curated"]
62
+ enabled = false
63
+ EOF
64
+ }
65
+ operator_bridge() { # a manual entwurf-bridge entry the operator owns already
66
+ cat >> "$CFG" <<'EOF'
67
+ [mcp_servers.entwurf-bridge]
68
+ command = "/opt/agent-config/bridge-start.sh"
69
+ startup_timeout_sec = 30
70
+ [mcp_servers.entwurf-bridge.env]
71
+ EXTRA = "operator"
72
+ EOF
73
+ }
74
+ json_field() { python3 -c 'import json,sys; d=json.load(open(sys.argv[1])); print(d[sys.argv[2]])' "$1" "$2"; }
75
+ toml_ok() { python3 -c 'import tomllib,sys; tomllib.load(open(sys.argv[1],"rb"))' "$1"; }
76
+ context_values_absent() {
77
+ 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"
78
+ }
79
+
80
+ ###############################################################################
81
+ # Atom 1 — [mcp_servers.entwurf-bridge]
82
+ ###############################################################################
83
+
84
+ # unowned manual config is an honest note, not a failure (this host's shape)
85
+ seed_config
86
+ operator_bridge
87
+ python3 "$MCP" doctor-static "$CFG" entwurf-bridge "$MCP_STATE" >"$SB/out" 2>&1
88
+ want "unowned manual entry doctor is an honest note" "grep -q '^configured /opt/agent-config/bridge-start.sh unowned$' '$SB/out'"
89
+
90
+ # adoption captures the operator's exact bytes as the FIRST preimage, once
91
+ cp "$CFG" "$SB/with-bridge.toml"
92
+ python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE" >"$SB/out"
93
+ want "install adopts the regular file" "grep -q '^adopt-regular-file ' '$SB/out'"
94
+ toml_ok "$CFG"; ok "installed config still parses as TOML"
95
+ want "first preimage captured" "[ \"\$(json_field '$MCP_STATE' entryExistedBefore)\" = True ] && json_field '$MCP_STATE' preimage | grep -q 'bridge-start.sh'"
96
+ want "our stdio shape is live" "grep -q '^command = \"entwurf-bridge\"$' '$CFG' && grep -q '^ENTWURF_BRIDGE_NATIVE_HOST = \"codex\"$' '$CFG'"
97
+ want "vendor env_vars forwards the exact operator context names in order" \
98
+ "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'"
99
+ want "no install-time context value is baked into config" "context_values_absent '$CFG'"
100
+ python3 "$MCP" doctor-static "$CFG" entwurf-bridge "$MCP_STATE" >"$SB/out"
101
+ want "owned+configured doctor is green" "grep -q '^configured entwurf-bridge owned$' '$SB/out'"
102
+ python3 "$MCP" doctor-invocation "$CFG" >"$SB/out"
103
+ want "doctor-invocation emits only the literal boot-probe environment" "grep -x '{\"command\":\"entwurf-bridge\",\"args\":\[\],\"env\":{\"ENTWURF_BRIDGE_NATIVE_HOST\":\"codex\"}}' '$SB/out'"
104
+
105
+ # idempotence is an untouched file, and the FIRST preimage survives re-runs
106
+ MT1="$(stat -c %Y "$CFG")"; sleep 1.1
107
+ python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE" >/dev/null
108
+ MT2="$(stat -c %Y "$CFG")"
109
+ want "reinstall does not rewrite the config" "[ '$MT1' = '$MT2' ]"
110
+ python3 "$MCP" install "$CFG" a-different-command "$MCP_STATE" >/dev/null
111
+ want "reinstall with a new command keeps the FIRST preimage" "json_field '$MCP_STATE' preimage | grep -q 'bridge-start.sh'"
112
+ python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE" >/dev/null
113
+
114
+ # drift the operator introduces turns the doctor red by name, not green
115
+ sed -i 's/^command = "entwurf-bridge"$/command = "evil-bridge"/' "$CFG"
116
+ python3 "$MCP" doctor-static "$CFG" entwurf-bridge "$MCP_STATE" >"$SB/out" 2>&1 && die "command drift doctor should be red"
117
+ want "command drift is red" "grep -q '^configured evil-bridge drift command-mismatch' '$SB/out'"
118
+ sed -i 's/^command = "evil-bridge"$/command = "entwurf-bridge"/' "$CFG"
119
+ sed -i '/^ENTWURF_BRIDGE_NATIVE_HOST = /d' "$CFG"
120
+ python3 "$MCP" doctor-static "$CFG" entwurf-bridge "$MCP_STATE" >"$SB/out" 2>&1 && die "env-label drift doctor should be red"
121
+ want "removed provenance label is red" "grep -q 'drift env-label-missing' '$SB/out'"
122
+ sed -i 's/^\[mcp_servers.entwurf-bridge.env\]$/[mcp_servers.entwurf-bridge.env]\nENTWURF_BRIDGE_NATIVE_HOST = "not-codex"/' "$CFG"
123
+ python3 "$MCP" doctor-static "$CFG" entwurf-bridge "$MCP_STATE" >"$SB/out" 2>&1 && die "foreign label value doctor should be red"
124
+ want "foreign provenance value is red" "grep -q 'drift env-label-foreign' '$SB/out'"
125
+ python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE" >/dev/null
126
+
127
+ sed -i 's/, "ENTWURF_DIR"//' "$CFG"
128
+ python3 "$MCP" doctor-static "$CFG" entwurf-bridge "$MCP_STATE" >"$SB/out" 2>&1 && die "missing context env_vars name doctor should be red"
129
+ want "missing context env_vars name is red by name" "grep -q 'drift context-env-vars-missing (ENTWURF_DIR)' '$SB/out'"
130
+ python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE" >/dev/null
131
+ sed -i 's/"TMUX_PANE"]/"TMUX_PANE", "FOREIGN_CONTEXT"]/' "$CFG"
132
+ python3 "$MCP" doctor-static "$CFG" entwurf-bridge "$MCP_STATE" >"$SB/out" 2>&1 && die "foreign context env_vars name doctor should be red"
133
+ want "foreign context env_vars name is red by name" "grep -q 'drift context-env-vars-foreign (FOREIGN_CONTEXT)' '$SB/out'"
134
+ python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE" >/dev/null
135
+ sed -i 's/"CODEX_HOME", "ENTWURF_DIR"/"ENTWURF_DIR", "CODEX_HOME"/' "$CFG"
136
+ python3 "$MCP" doctor-static "$CFG" entwurf-bridge "$MCP_STATE" >"$SB/out" 2>&1 && die "reordered context env_vars doctor should be red"
137
+ want "reordered context env_vars is red" "grep -q 'drift context-env-vars-reordered-or-duplicated' '$SB/out'"
138
+ cp "$CFG" "$SB/drifted-mcp.toml"
139
+ python3 "$MCP" uninstall "$MCP_STATE" >"$SB/out" 2>&1 &&
140
+ die "[QK:CODEX-MCP-UNINSTALL-REFUSES-DRIFT] uninstall accepted a drifted owned MCP table"
141
+ want "uninstall refusal names exact post-install table drift" "grep -q 'drifted from the exact post-install table' '$SB/out'"
142
+ cmp -s "$SB/drifted-mcp.toml" "$CFG" && ok "drifted MCP table is byte-identical after refused inverse" || die "drifted MCP table changed"
143
+
144
+ # the inverse restores the operator's exact bytes after drift repairs
145
+ python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE" >/dev/null
146
+ python3 "$MCP" uninstall "$MCP_STATE" >"$SB/out"
147
+ want "uninstall reports the managed path" "grep -q '^uninstalled ' '$SB/out'"
148
+ cmp -s "$SB/with-bridge.toml" "$CFG" && ok "inverse restores the operator entry byte-exact" || die "inverse byte mismatch"
149
+ want "state is gone" "[ ! -e '$MCP_STATE' ]"
150
+ python3 "$MCP" doctor-static "$CFG" entwurf-bridge "$MCP_STATE" >"$SB/out"
151
+ want "post-uninstall doctor is an unowned note again" "grep -q 'unowned' '$SB/out'"
152
+
153
+ # A NEIGHBOUR APPENDED AFTER OUR BLOCK — the shape this host actually grew into.
154
+ # Our managed table is written at the end of the file, so for a long time nothing
155
+ # ever followed it and no gate could see where the family stopped. Then the Codex
156
+ # vendor appended its own `[hooks.state]` (the operator's hook-trust decision) right
157
+ # after ours, separated by one blank line, and a plain idempotent reinstall deleted
158
+ # that blank line: semantically nothing, but a byte outside our atom, and the one
159
+ # thing this writer promises never to touch. The separator belongs to the boundary,
160
+ # not to the family.
161
+ seed_config
162
+ cp "$CFG" "$SB/neighbour-seed.toml" # the operator bytes the inverse owes back
163
+ cat > "$SB/neighbour-block.toml" <<'EOF'
164
+
165
+ [hooks.state]
166
+
167
+ [hooks.state."/sandbox/.codex/hooks.json:session_start:0:0"]
168
+ trusted_hash = "sha256:4647c53b6948cd38f2283a3fdf63e40e83b7ff29ecf14650b7e61eec9315e1cc"
169
+ EOF
170
+ python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE" >/dev/null
171
+ cat "$SB/neighbour-block.toml" >> "$CFG"
172
+ cp "$CFG" "$SB/neighbour-expected.toml"
173
+ # What the inverse owes: the operator's seed bytes, then the neighbour EXACTLY as it was
174
+ # appended — its leading blank separator included. Built by concatenation, never by
175
+ # describing the result, so the assertion cannot be satisfied by a file that merely ends
176
+ # the right way.
177
+ cat "$SB/neighbour-seed.toml" "$SB/neighbour-block.toml" > "$SB/neighbour-after-inverse-expected.toml"
178
+ toml_ok "$CFG"; ok "our block plus an appended vendor neighbour parses"
179
+ python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE" >/dev/null
180
+ cmp -s "$SB/neighbour-expected.toml" "$CFG" \
181
+ || 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')"
182
+ ok "reinstall beside a vendor neighbour is byte-exact, separator included"
183
+ want "the vendor's trust record survives verbatim" "grep -Fxq 'trusted_hash = \"sha256:4647c53b6948cd38f2283a3fdf63e40e83b7ff29ecf14650b7e61eec9315e1cc\"' '$CFG'"
184
+ # ...and the inverse gives back exactly what it found: the operator's seed bytes plus the
185
+ # neighbour verbatim. Asserted with `cmp` against bytes built by concatenation — an
186
+ # "ends with the neighbour" shape test would pass on a file whose separator was eaten,
187
+ # which is the very byte this cell exists to protect.
188
+ python3 "$MCP" uninstall "$MCP_STATE" >/dev/null
189
+ cmp -s "$SB/neighbour-after-inverse-expected.toml" "$CFG" \
190
+ || die "the inverse did not restore seed+neighbour byte-exact: $(diff -u "$SB/neighbour-after-inverse-expected.toml" "$CFG" | sed -n '3,12p')"
191
+ ok "the inverse restores the operator seed plus the neighbour byte-exact, separator included"
192
+ toml_ok "$CFG"; ok "config after the neighbour-aware inverse still parses"
193
+ rm -f "$MCP_STATE"
194
+
195
+ # created-new: the file we made is removed by the inverse
196
+ rm -f "$CFG"
197
+ python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE" >/dev/null
198
+ toml_ok "$CFG"; ok "created-new config parses"
199
+ python3 "$MCP" uninstall "$MCP_STATE" >/dev/null
200
+ want "created-new file removed by inverse" "[ ! -e '$CFG' ] && [ ! -e '$MCP_STATE' ]"
201
+
202
+ # refusals — symlink, malformed, foreign state, retargeted state, contrary values
203
+ seed_config
204
+ printf '[mcp_servers.other]\ncommand = "foreign"\n' > "$SB/foreign.toml"
205
+ ln -sf "$SB/foreign.toml" "$CFG"
206
+ refuses "symlink config is refused" "symlink" python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE"
207
+ want "symlink refusal leaves the foreign file and no state" "grep -q foreign '$SB/foreign.toml' && [ ! -e '$MCP_STATE' ]"
208
+ rm -f "$CFG"
209
+ printf 'not = toml {{{\n[mcp_servers\n' > "$CFG"
210
+ refuses "malformed TOML is refused" "not valid TOML" python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE"
211
+ want "malformed refusal writes no state" "[ ! -e '$MCP_STATE' ]"
212
+ seed_config
213
+ python3 "$SL" install "$CFG" "$SL_STATE" >/dev/null # a FOREIGN atom's receipt
214
+ cp "$SL_STATE" "$MCP_STATE" # …parked on OUR path
215
+ refuses "foreign-atom state is refused" "belongs to atom" python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE"
216
+ python3 "$SL" uninstall "$SL_STATE" >/dev/null
217
+ rm -f "$MCP_STATE" # the parked foreign receipt must not poison the next case
218
+ seed_config
219
+ python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE" >/dev/null
220
+ sed -i "s|$(json_field "$MCP_STATE" managedConfigPath)|$SB/elsewhere.toml|" "$MCP_STATE"
221
+ refuses "retargeted state is refused" "refusing to retarget" python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE"
222
+ rm -f "$MCP_STATE"
223
+ seed_config; operator_bridge_with_url() {
224
+ cat >> "$CFG" <<'EOF'
225
+ [mcp_servers.entwurf-bridge]
226
+ url = "https://bridge.example/mcp"
227
+ EOF
228
+ }
229
+ operator_bridge_with_url
230
+ refuses "streamable_http entry refused BY NAME (url)" "\`url\`" python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE"
231
+ sed -i 's|^url = .*|command = "x"\nenabled = false|' "$CFG"
232
+ refuses "explicitly disabled entry refused BY NAME (enabled)" "\`enabled\`" python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE"
233
+ want "contrary refusals wrote no state" "[ ! -e '$MCP_STATE' ]"
234
+
235
+ # a receipt whose config vanished is red, not silently green
236
+ seed_config
237
+ python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE" >/dev/null
238
+ rm -f "$CFG"
239
+ python3 "$MCP" doctor-static "$CFG" entwurf-bridge "$MCP_STATE" >"$SB/out" 2>&1 && die "file-absent drift doctor should be red"
240
+ want "file-absent with a receipt is red" "grep -q '^file-absent drift entry-gone$' '$SB/out'"
241
+ python3 "$MCP" uninstall "$MCP_STATE" >/dev/null
242
+ want "uninstall clears the orphaned receipt" "[ ! -e '$MCP_STATE' ]"
243
+
244
+ ###############################################################################
245
+ # Fault injection — the state receipt is durable BEFORE the operator's config
246
+ # is ever touched, at each of atomic_write's three fault points
247
+ ###############################################################################
248
+ for stage in mkdir write replace; do
249
+ seed_config
250
+ cp "$CFG" "$SB/state-fault-pre.toml"
251
+ ENTWURF_TEST_CODEX_TOML_FAULT="$stage:.codex-state-" python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE" >"$SB/out" 2>&1 \
252
+ && die "injected $stage state fault did not stop install"
253
+ grep -q "injected $stage fault (.codex-state-)" "$SB/out" || die "state $stage fault refusal is unnamed: $(cat "$SB/out")"
254
+ ok "state $stage fault refuses by name"
255
+ cmp -s "$SB/state-fault-pre.toml" "$CFG" && ok "state $stage fault leaves operator config untouched" \
256
+ || die "[QK:CODEX-TOML-STATE-BEFORE-CONFIG] state $stage fault mutated operator config"
257
+ want "state $stage fault leaves no state file" "[ ! -e '$MCP_STATE' ]"
258
+ if [ "$stage" = replace ]; then
259
+ find "$(dirname "$MCP_STATE")" -maxdepth 1 -name '.codex-state-*' | grep -q . \
260
+ && die "a failed state replace left a stray temp file" \
261
+ || ok "failed state replace leaves no stray temp file"
262
+ fi
263
+ done
264
+
265
+ ###############################################################################
266
+ # Config replace fault — atomic_write's own rollback leaves the operator's
267
+ # bytes byte-exact, and a fault-free retry + inverse still recovers them
268
+ ###############################################################################
269
+ seed_config
270
+ operator_bridge
271
+ cp "$CFG" "$SB/replace-fault-pre.toml"
272
+ ENTWURF_TEST_CODEX_TOML_FAULT="replace:.codex-mcp-" python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE" >"$SB/out" 2>&1 \
273
+ && die "[QK:CODEX-TOML-CONFIG-REPLACE-ROLLBACK] injected config replace fault did not stop install"
274
+ grep -q "injected replace fault (.codex-mcp-)" "$SB/out" || die "config replace fault refusal is unnamed: $(cat "$SB/out")"
275
+ ok "config replace fault refuses by name"
276
+ cmp -s "$SB/replace-fault-pre.toml" "$CFG" && ok "config replace fault leaves operator bytes untouched" \
277
+ || die "config replace fault mutated operator config"
278
+ find "$CODEX_HOME" -maxdepth 1 -name '.codex-mcp-*' | grep -q . \
279
+ && die "a failed config replace left a stray temp file" \
280
+ || ok "failed config replace leaves no stray temp file"
281
+ want "the durable state still carries the operator's first preimage" \
282
+ "[ -e '$MCP_STATE' ] && json_field '$MCP_STATE' preimage | grep -q 'bridge-start.sh'"
283
+
284
+ # a fault-free retry recovers cleanly, and the inverse restores the SAME
285
+ # preimage the interrupted install already made durable
286
+ python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE" >"$SB/out"
287
+ want "retry after the replace fault adopts" "grep -q '^adopt-regular-file ' '$SB/out'"
288
+ toml_ok "$CFG"; ok "retried config still parses as TOML"
289
+ python3 "$MCP" uninstall "$MCP_STATE" >"$SB/out"
290
+ want "uninstall reports the managed path" "grep -q '^uninstalled ' '$SB/out'"
291
+ cmp -s "$SB/replace-fault-pre.toml" "$CFG" && ok "inverse after fault-then-retry restores the operator's exact original bytes" \
292
+ || die "inverse after retry did not restore the durable preimage"
293
+ want "state cleared after inverse" "[ ! -e '$MCP_STATE' ]"
294
+
295
+ ###############################################################################
296
+ # Reshaped-owned entry — our table survives as a value but not as a literal
297
+ # header block; the inverse must refuse and keep the receipt, never resurrect
298
+ # or silently drop it
299
+ ###############################################################################
300
+ seed_config
301
+ python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE" >/dev/null
302
+ sed -i '/^\[mcp_servers\.entwurf-bridge\]$/,/^ENTWURF_BRIDGE_NATIVE_HOST = "codex"$/d' "$CFG"
303
+ sed -i '1i mcp_servers.entwurf-bridge = { command = "entwurf-bridge" }' "$CFG"
304
+ toml_ok "$CFG"; ok "reshaped config still parses as TOML"
305
+ cp "$CFG" "$SB/reshaped-mcp.toml"
306
+ python3 "$MCP" uninstall "$MCP_STATE" >"$SB/out" 2>&1 \
307
+ && die "[QK:CODEX-MCP-RESHAPED-REFUSES-KEEPS-STATE] uninstall accepted a reshaped owned entry"
308
+ grep -q "not as a literal table block" "$SB/out" || die "reshaped refusal is unnamed: $(cat "$SB/out")"
309
+ ok "reshaped entry refuses to uninstall, by name"
310
+ cmp -s "$SB/reshaped-mcp.toml" "$CFG" && ok "reshaped config untouched by the refused inverse" \
311
+ || die "reshaped config mutated by refused inverse"
312
+ want "reshaped refusal retains the receipt" "[ -e '$MCP_STATE' ]"
313
+ rm -f "$MCP_STATE" # hand-cleared: the reshaped entry has no normal inverse path
314
+
315
+ ###############################################################################
316
+ # Atom 2 — [tui].status_line includes thread-title
317
+ ###############################################################################
318
+
319
+ seed_config
320
+ cp "$CFG" "$SB/sl-seed.toml"
321
+ python3 "$SL" install "$CFG" "$SL_STATE" >"$SB/out"
322
+ want "existing operator list is merged" "grep -q '^merged-item ' '$SB/out'"
323
+ want "thread-title prepended, operator items keep their order" "grep -q '^status_line = \[\"thread-title\", \"model-with-reasoning\", \"current-dir\"\]$' '$CFG'"
324
+ toml_ok "$CFG"; ok "merged config still parses"
325
+ python3 "$SL" doctor-static "$CFG" "$SL_STATE" >"$SB/out"
326
+ want "owned status doctor is green" "grep -q '^thread-title-present owned$' '$SB/out'"
327
+ MT1="$(stat -c %Y "$CFG")"; sleep 1.1
328
+ python3 "$SL" install "$CFG" "$SL_STATE" >/dev/null
329
+ MT2="$(stat -c %Y "$CFG")"
330
+ want "status reinstall writes nothing" "[ '$MT1' = '$MT2' ]"
331
+
332
+ # the state receipt never changes the effective verdict
333
+ rm -f "$SL_STATE"
334
+ python3 "$SL" doctor-static "$CFG" "$SL_STATE" >"$SB/out"
335
+ want "unowned thread-title is green (state-independent)" "grep -q '^thread-title-present unowned$' '$SB/out'"
336
+
337
+ # ...and being unowned never rescues a MISSING required axis. The doctor's
338
+ # subject is visible identity, not our bookkeeping: `thread-title-present` is
339
+ # the only green, so every other effective token is red even with no receipt.
340
+ # Without this, a host whose config the operator keeps elsewhere reports exit 0
341
+ # while no Codex thread can ever show its garden id.
342
+ cp "$CFG" "$SB/sl-required-axis.toml"
343
+ sed -i 's/"thread-title", //' "$CFG"
344
+ python3 "$SL" doctor-static "$CFG" "$SL_STATE" >"$SB/out" 2>&1 \
345
+ && die "[QK:CODEX-SL-DOCTOR-REQUIRES-VISIBLE-IDENTITY] unowned config WITHOUT thread-title reported green: $(cat "$SB/out")"
346
+ want "unowned thread-title-absent is red, both axes preserved" "grep -q '^thread-title-absent unowned$' '$SB/out'"
347
+ sed -i '/^status_line = /d' "$CFG"
348
+ python3 "$SL" doctor-static "$CFG" "$SL_STATE" >"$SB/out" 2>&1 \
349
+ && die "unowned config with NO status_line key reported green: $(cat "$SB/out")"
350
+ want "unowned status-line-absent is red, both axes preserved" "grep -q '^status-line-absent unowned$' '$SB/out'"
351
+ ln -s "$SB/sl-required-axis.toml" "$SB/sl-symlinked.toml"
352
+ python3 "$SL" doctor-static "$SB/sl-symlinked.toml" "$SL_STATE" >"$SB/out" 2>&1 \
353
+ && die "a symlinked config the doctor cannot read through reported green: $(cat "$SB/out")"
354
+ want "unowned symlink is red, both axes preserved" "grep -q '^symlink unowned$' '$SB/out'"
355
+ rm -f "$SB/sl-symlinked.toml"
356
+ cp "$SB/sl-required-axis.toml" "$CFG"
357
+
358
+ # drift: our item removed under a receipt is red; reinstall repairs. The seed
359
+ # is rebuilt first: the file still carries OUR merged item from the checks
360
+ # above, and adopting it back as "already-present" would make the inverse a
361
+ # (honest, but here unproven) no-op.
362
+ seed_config
363
+ python3 "$SL" install "$CFG" "$SL_STATE" >/dev/null
364
+ sed -i 's/"thread-title", //' "$CFG"
365
+ python3 "$SL" doctor-static "$CFG" "$SL_STATE" >"$SB/out" 2>&1 && die "removed thread-title doctor should be red"
366
+ want "removed thread-title under receipt is red and still names ownership" "grep -q '^thread-title-absent owned drift$' '$SB/out'"
367
+ python3 "$SL" install "$CFG" "$SL_STATE" >/dev/null
368
+ python3 "$SL" uninstall "$SL_STATE" >/dev/null
369
+ cmp -s "$SB/sl-seed.toml" "$CFG" && ok "status inverse restores operator bytes" || die "status inverse byte mismatch"
370
+ want "status state gone" "[ ! -e '$SL_STATE' ]"
371
+
372
+ ###############################################################################
373
+ # Genuinely absent — the operator removes the whole status_line key we own;
374
+ # the inverse clears state cleanly instead of refusing or resurrecting it
375
+ ###############################################################################
376
+ seed_config
377
+ python3 "$SL" install "$CFG" "$SL_STATE" >/dev/null
378
+ sed -i '/^status_line = /d' "$CFG"
379
+ toml_ok "$CFG"; ok "config with status_line key removed still parses"
380
+ python3 "$SL" uninstall "$SL_STATE" >"$SB/out" 2>&1 \
381
+ || die "[QK:CODEX-SL-GENUINELY-ABSENT-CLEARS-STATE] genuinely-absent inverse refused instead of clearing state: $(cat "$SB/out")"
382
+ want "genuinely-absent inverse reports absence, not a restore" "grep -q 'status_line already absent' '$SB/out'"
383
+ want "genuinely-absent inverse clears state" "[ ! -e '$SL_STATE' ]"
384
+
385
+ # [tui] absent → the whole block is appended, and taken back whole
386
+ seed_config
387
+ sed -i '/^\[tui\]/,/^\[mcp_servers.other\]$/d' "$CFG" # drop the [tui] family
388
+ cp "$CFG" "$SB/no-tui.toml"
389
+ python3 "$SL" install "$CFG" "$SL_STATE" >"$SB/out"
390
+ want "[tui] appended when absent" "grep -q '^appended-table ' '$SB/out' && grep -q '^\[tui\]$' '$CFG'"
391
+ python3 "$SL" uninstall "$SL_STATE" >/dev/null
392
+ cmp -s "$SB/no-tui.toml" "$CFG" && ok "appended [tui] block taken back byte-exact" || die "appended-block inverse mismatch"
393
+
394
+ # status_line absent inside [tui] → the key is inserted; unrelated keys stay
395
+ seed_config
396
+ sed -i '/^status_line = /d' "$CFG"
397
+ cp "$CFG" "$SB/no-key.toml"
398
+ python3 "$SL" install "$CFG" "$SL_STATE" >"$SB/out"
399
+ want "key inserted into existing [tui]" "grep -q '^inserted-key ' '$SB/out' && grep -q '^status_line = \[\"thread-title\"\]$' '$CFG'"
400
+ python3 "$SL" uninstall "$SL_STATE" >/dev/null
401
+ cmp -s "$SB/no-key.toml" "$CFG" && ok "inserted key taken back, theme survived" || die "inserted-key inverse mismatch"
402
+
403
+ # already-present: adoption without a single byte written
404
+ seed_config
405
+ sed -i 's/^status_line = .*/status_line = ["thread-title", "model-with-reasoning"]/' "$CFG"
406
+ cp "$CFG" "$SB/already.toml"
407
+ MT1="$(stat -c %Y "$CFG")"; sleep 1.1
408
+ python3 "$SL" install "$CFG" "$SL_STATE" >"$SB/out"
409
+ MT2="$(stat -c %Y "$CFG")"
410
+ want "already-present adopt writes nothing" "grep -q '^already-present ' '$SB/out' && [ '$MT1' = '$MT2' ]"
411
+ python3 "$SL" uninstall "$SL_STATE" >"$SB/out"
412
+ want "already-present uninstall takes back nothing" "grep -q 'nothing to take back' '$SB/out' && [ '$(stat -c %Y "$CFG")' = '$MT1' ]"
413
+
414
+ # multi-line operator array and the empty array are both merged byte-honestly
415
+ seed_config
416
+ python3 - "$CFG" <<'PY'
417
+ import sys
418
+ p = sys.argv[1]
419
+ t = open(p).read()
420
+ t = t.replace('status_line = ["model-with-reasoning", "current-dir"]',
421
+ 'status_line = [\n "model-with-reasoning", # operator note\n "current-dir",\n]')
422
+ open(p, 'w').write(t)
423
+ PY
424
+ cp "$CFG" "$SB/multiline.toml"
425
+ python3 "$SL" install "$CFG" "$SL_STATE" >/dev/null
426
+ toml_ok "$CFG"; ok "multi-line array merged and parseable"
427
+ python3 "$SL" uninstall "$SL_STATE" >/dev/null
428
+ cmp -s "$SB/multiline.toml" "$CFG" && ok "multi-line array restored byte-exact" || die "multiline inverse mismatch"
429
+ seed_config # the multiline value above spans lines; build the empty list fresh
430
+ sed -i 's/^status_line = .*/status_line = []/' "$CFG"
431
+ cp "$CFG" "$SB/empty.toml"
432
+ python3 "$SL" install "$CFG" "$SL_STATE" >/dev/null
433
+ want "empty list becomes exactly [\"thread-title\"]" "grep -q '^status_line = \[\"thread-title\"\]$' '$CFG'"
434
+ python3 "$SL" uninstall "$SL_STATE" >/dev/null
435
+ cmp -s "$SB/empty.toml" "$CFG" && ok "empty list restored byte-exact" || die "empty-list inverse mismatch"
436
+
437
+ # refusals — symlink, malformed, contrary value by name, foreign/retargeted state
438
+ seed_config
439
+ ln -sf "$SB/foreign.toml" "$CFG"
440
+ refuses "symlink config refused (statusline)" "symlink" python3 "$SL" install "$CFG" "$SL_STATE"
441
+ rm -f "$CFG"
442
+ printf 'garbage [[[\n' > "$CFG"
443
+ refuses "malformed TOML refused (statusline)" "not valid TOML" python3 "$SL" install "$CFG" "$SL_STATE"
444
+ seed_config
445
+ sed -i 's/^status_line = .*/status_line = "thread-title"/' "$CFG"
446
+ refuses "non-array status_line refused BY NAME" "status_line.*explicitly" python3 "$SL" install "$CFG" "$SL_STATE"
447
+ want "statusline refusals wrote no state" "[ ! -e '$SL_STATE' ]"
448
+ seed_config
449
+ python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE" >/dev/null
450
+ cp "$MCP_STATE" "$SL_STATE" # the foreign receipt, on our path
451
+ refuses "foreign-atom state refused (statusline)" "belongs to atom" python3 "$SL" install "$CFG" "$SL_STATE"
452
+ python3 "$MCP" uninstall "$MCP_STATE" >/dev/null
453
+ rm -f "$SL_STATE" # the parked foreign receipt must not poison the next case
454
+ seed_config
455
+ python3 "$SL" install "$CFG" "$SL_STATE" >/dev/null
456
+ sed -i "s|$(json_field "$SL_STATE" managedConfigPath)|$SB/elsewhere.toml|" "$SL_STATE"
457
+ refuses "retargeted state refused (statusline)" "refusing to retarget" python3 "$SL" install "$CFG" "$SL_STATE"
458
+ rm -f "$SL_STATE"
459
+
460
+ # operator restructures the list after install: the take-back refuses, by name
461
+ seed_config
462
+ python3 "$SL" install "$CFG" "$SL_STATE" >/dev/null
463
+ sed -i 's/^status_line = .*/status_line = [\n "weather",\n "thread-title",\n]/' "$CFG"
464
+ refuses "restructured list refuses blind take-back" "changed since install" python3 "$SL" uninstall "$SL_STATE"
465
+ rm -f "$SL_STATE"
466
+
467
+ ###############################################################################
468
+ # Unrelated operator config survives the whole cycle, byte for byte
469
+ ###############################################################################
470
+ seed_config
471
+ cp "$CFG" "$SB/final-seed.toml"
472
+ python3 "$MCP" install "$CFG" entwurf-bridge "$MCP_STATE" >/dev/null
473
+ python3 "$SL" install "$CFG" "$SL_STATE" >/dev/null
474
+ toml_ok "$CFG"; ok "fully installed config parses as TOML"
475
+ python3 - "$CFG" <<'PY'
476
+ import tomllib, sys
477
+ d = tomllib.load(open(sys.argv[1], "rb"))
478
+ assert d["hooks"]["state"]["~/c.toml:session_start:0:0"] == {"trusted_hash": "sha256:abc"}, d
479
+ assert d["projects"]["/home/op/work"] == {"trust_level": "trusted"}, d
480
+ assert d["mcp_servers"]["other"] == {"command": "keep-me"}, d
481
+ assert d["mcp_servers"]["entwurf-bridge"] == {
482
+ "command": "entwurf-bridge",
483
+ "env_vars": [
484
+ "CODEX_HOME",
485
+ "ENTWURF_DIR",
486
+ "PI_CODING_AGENT_DIR",
487
+ "ENTWURF_META_SESSIONS_DIR",
488
+ "ENTWURF_META_MAILBOX_DIR",
489
+ "ENTWURF_META_SENDERS_DIR",
490
+ "ENTWURF_META_RECEIVERS_DIR",
491
+ "TMUX",
492
+ "TMUX_PANE",
493
+ ],
494
+ "env": {"ENTWURF_BRIDGE_NATIVE_HOST": "codex"},
495
+ }, d
496
+ assert d["tui"]["status_line"] == ["thread-title", "model-with-reasoning", "current-dir"], d
497
+ assert d["tui"]["theme"] == "zenburn" and d["tui"]["model_availability_nux"] == {"gpt-5.5": 4}, d
498
+ assert d["plugins"]["github@openai-curated"] == {"enabled": False}, d
499
+ PY
500
+ ok "unrelated tables intact and both atoms semantically live"
501
+ # the vendor writes trust state back between install and uninstall (M1)
502
+ printf '[hooks.state."~/c.toml:stop:0:0"]\ntrusted_hash = "sha256:def"\n' >> "$CFG"
503
+ python3 "$MCP" uninstall "$MCP_STATE" >/dev/null
504
+ python3 "$SL" uninstall "$SL_STATE" >/dev/null
505
+ printf '[hooks.state."~/c.toml:stop:0:0"]\ntrusted_hash = "sha256:def"\n' >> "$SB/final-seed.toml"
506
+ cmp -s "$SB/final-seed.toml" "$CFG" && ok "full cycle leaves only vendor-added bytes (unrelated config survives)" || die "final byte mismatch"
507
+ want "both states cleared" "[ ! -e '$MCP_STATE' ] && [ ! -e '$SL_STATE' ]"
508
+
509
+ REPO_AFTER="$(cd "$REPO_DIR" && git status --porcelain)"
510
+ [ "$REPO_BEFORE" = "$REPO_AFTER" ] || die "smoke changed the checkout"
511
+ printf '\nsmoke-codex-config-state: %d checks passed\n' "$pass"