@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
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,467 @@ All notable changes to this project will be documented here. Format follows [Kee
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 0.22.0 - 2026-09-17
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **A Codex sibling opened into a directory the vendor has no answer for now SAYS SO
|
|
12
|
+
(`codex-launch-cwd-undecided`).** `entwurf_fresh_call` reads the launch directory against the
|
|
13
|
+
operator's Codex config and prints what it saw — and then opens the window anyway. **It is a
|
|
14
|
+
diagnostic, not a gate, and that is the product decision (GLG, 2026-09-17):** the vendor's
|
|
15
|
+
consent screen is self-repairing for the human this rail exists to put a window in front of, one
|
|
16
|
+
answer teaches the vendor that directory for good, and a refusal would replace that with "no
|
|
17
|
+
window, go run codex yourself, then call again" while having to be right about a decision this
|
|
18
|
+
process cannot fully see. The UNATTENDED case is answered where it belongs —
|
|
19
|
+
`smoke-codex-fresh-live` asserts the same leaf up front, so a release gate with nobody at the
|
|
20
|
+
keyboard reads a named precondition instead of a callback timeout. `[source rust-v0.153.4]` a `--remote` startup — which this
|
|
21
|
+
argv always is — runs `check_directory_trust` on the `-C` value (`tui/src/lib.rs:1699-1725`), and
|
|
22
|
+
nothing on that path consults the approval or sandbox policy
|
|
23
|
+
(`tui/src/onboarding/directory_trust.rs:33-130`): the
|
|
24
|
+
`--dangerously-bypass-approvals-and-sandbox` token this launcher already carries turns off
|
|
25
|
+
approvals, never folder consent. A DIRECT decision is keyed to the exact directory and nothing else — for
|
|
26
|
+
`ProjectTrustHost::Remote` the lookup is `vec![cwd_key]` (`tui/src/config_update.rs:290-296`), with
|
|
27
|
+
no project-root marker, no git root and no parent inheritance. **The question is whether the
|
|
28
|
+
directory has been ANSWERED, not whether it is trusted:** a saved `untrusted` folder is
|
|
29
|
+
explicitly skipped on a remote target (`onboarding/directory_trust.rs:94-96`), so that turn
|
|
30
|
+
starts too and is not noted at all. A cwd inside an explicitly `untrusted` project gets its OWN
|
|
31
|
+
note (`codex-launch-cwd-untrusted-ancestor`) because the vendor answers that one with an error
|
|
32
|
+
rather than a screen — `pass the repository root explicitly with --cd`
|
|
33
|
+
(`config_update.rs:357-371`) — so its repair names a different directory, not a prompt.
|
|
34
|
+
|
|
35
|
+
**The leaf is deliberately NARROWER than the vendor's own judgment and says so rather than
|
|
36
|
+
claiming equivalence.** It reads the operator's `config.toml`; the vendor reads an effective
|
|
37
|
+
config — system, managed and cloud layers merged around that file — through its app-server, and
|
|
38
|
+
an enabled PROJECT LAYER consents on a directory's behalf with no entry at all
|
|
39
|
+
(`config_update.rs:346-354`). That gap is affordable precisely because nothing here refuses:
|
|
40
|
+
everything the leaf cannot see stays silent, and a note that turns out to be unnecessary costs a
|
|
41
|
+
line of stderr. **`[측정 2026-09-16]` this is the
|
|
42
|
+
failure it retires, not a hypothetical:** four `smoke-codex-fresh-live` runs, one scratch directory
|
|
43
|
+
each; the two whose directory carried a trust entry (`…-db65N2`, `…-pBXxOJ`, both answered by a
|
|
44
|
+
human at the keyboard) passed, and the two unattended release-gate runs (`…-2zznHl`, `…-kSsoAn`)
|
|
45
|
+
died with no rollout written after 21:28 at all — the sibling had opened on a consent screen and
|
|
46
|
+
started no turn, so there was no birth, no callback, and nothing to address. The caller then waits
|
|
47
|
+
out its entire timeout on a window that is merely waiting to be answered, which reads as a delivery
|
|
48
|
+
defect and is a folder question. The directory asked about is read back off codex's own `-C` token
|
|
49
|
+
rather than recomputed, so the consent question cannot drift from the thread's real start
|
|
50
|
+
directory, and the inherited default stays resolved in exactly one place.
|
|
51
|
+
|
|
52
|
+
- **A Codex caller opens its sibling beside its own pane (#95 lane B).** Until now the seat a
|
|
53
|
+
Codex citizen got came from the app-server's inherited `TMUX`/`TMUX_PANE`, so a sibling opened
|
|
54
|
+
from Codex landed wherever the operator happened to have started the app-server rather than
|
|
55
|
+
beside the TUI the operator was looking at. `[측정 2026-09-16, codex-cli 0.153.4]` with
|
|
56
|
+
`thread-id` in `[tui].terminal_title` the TUI writes its thread UUID into the OSC 0 title and
|
|
57
|
+
tmux answers with it as `#{pane_title}` — truncated to 32 chars, i.e. 29 graphemes plus `...`
|
|
58
|
+
(`status_surfaces.rs:892-894`, `:1027-1043`; unchanged at `rust-v0.154.0`). So when the
|
|
59
|
+
reconciled sender of a fresh call is a record-backed `codex` citizen and `placement` is
|
|
60
|
+
omitted, that request's own `_meta.threadId` selects the pane whose title carries the token and
|
|
61
|
+
its `$session` becomes the target. **No vendor patch, no new carrier: one config item.**
|
|
62
|
+
- **`entwurf install-codex-terminal-title` / `uninstall-` / `doctor-`** — a fourth Codex
|
|
63
|
+
ownership atom, owning only `thread-id` membership within `tui.terminal_title`. A different key
|
|
64
|
+
and a different axis from `install-codex-statusline`: `status_line` is what a human reads
|
|
65
|
+
inside the TUI, `terminal_title` is what the multiplexer reports back. The seeded list is
|
|
66
|
+
`["activity", "project-name", "thread-id"]` — `activity` leads because the herdr Codex detector
|
|
67
|
+
keys on the spinner and `Action Required` prefix — and an existing operator list is APPENDED to,
|
|
68
|
+
never reordered. `setup` composes it on a detected Codex host.
|
|
69
|
+
- **The seat now follows the CALLER, never the backend being opened, on both fresh-call surfaces.**
|
|
70
|
+
An explicit `placement.tmuxSession` always wins; then a Codex CALLER's own pane
|
|
71
|
+
(`codex-title-anchor`); then the caller's own session. The anchored receipt reports its SOURCE and
|
|
72
|
+
no session name, because a session was observed rather than a name requested.
|
|
73
|
+
- **`entwurf codex-app-server` — the one command that starts the operator's Codex app-server.**
|
|
74
|
+
Every Codex rail needs a server the operator starts themselves, and the command was
|
|
75
|
+
`codex app-server --listen "unix://$CODEX_HOME/app-server-control/app-server-control.sock"`:
|
|
76
|
+
correct, in three documents, and in the text of a refusal — which meant most operators met it
|
|
77
|
+
only AFTER something had already failed. This verb owns that SPELLING and nothing else. It
|
|
78
|
+
`exec`s the vendor in the terminal it was typed in (cwd, pid, tty and exit status are the
|
|
79
|
+
server's; Ctrl-C is the operator's), creates the control directory, forwards the rest of the
|
|
80
|
+
argv byte-identical, and strips `PI_SESSION_ID`/`PI_AGENT_ID` so the server every bridge child
|
|
81
|
+
inherits from cannot carry someone else's identity. The address comes from the SAME leaf
|
|
82
|
+
delivery, preflight and the LIVE smoke read: the launcher ASKS that resolver through the new
|
|
83
|
+
internal `entwurf codex-socket-path` rather than re-deriving the path, so there is exactly one
|
|
84
|
+
spelling of this address instead of two that agree on the inputs somebody thought of.
|
|
85
|
+
Refusals are named and happen before the exec: a live socket (naming the pid whose `/proc`
|
|
86
|
+
cmdline actually carries that socket, and saying plainly when the host holds none — never an
|
|
87
|
+
inferred owner), a resolved address that is not absolute or carries a control character
|
|
88
|
+
(refused before anything is created: a relative socket is a different file for every process
|
|
89
|
+
that resolves it, and the bridge that will look for it is the app-server's MCP child with its
|
|
90
|
+
own cwd), an unidentifiable path, a second
|
|
91
|
+
`--listen`, a recursive launch, a socket classification nobody wrote, no `codex` on PATH. A dead
|
|
92
|
+
socket file is reported and launched over. **It is a managed spelling, not a managed
|
|
93
|
+
lifecycle**: no supervisor, no restart, no daemon, no pid file, and `entwurf_fresh_call` still
|
|
94
|
+
REJECTS a missing app-server rather than starting one. The tmux seat this server gives caller-seat lookups is reported as a fact, not
|
|
95
|
+
enforced as a requirement. Typing the raw vendor command remains equivalent.
|
|
96
|
+
|
|
97
|
+
### Verification
|
|
98
|
+
|
|
99
|
+
- **Release acceptance for this cut.** `[LIVE 2026-09-17, oracle]` `LIVE=1 ./run.sh release-gate
|
|
100
|
+
/tmp/entwurf-release-gate-0.22.0.royX2L --cut` → **`MUST: PASS=24 FAIL=0 SKIP=0`**, **`BEHAVIOR:
|
|
101
|
+
PASS=1 FAIL=0 SKIP=0`**, **`cut: OK`**, exit 0, 18:47–20:02 KST; log preserved at
|
|
102
|
+
`<scratch>/release-gate.log`. Inside it: `check:full` exit 0 in 521s, `check-gate-qualification`
|
|
103
|
+
**520/520 killed**, `smoke-entwurf-chain-live` 24 assertions, `smoke-codex-fresh-live` **66
|
|
104
|
+
assertions** — one more than lane C's 65, because the launch-directory precondition this release
|
|
105
|
+
adds is asserted before anything launches. Gate env `ENTWURF_CODEX_FRESH_MODEL=gpt-5.6-luna`,
|
|
106
|
+
`ENTWURF_CODEX_FRESH_PI_MODEL=openai-codex/gpt-5.6-luna`, `ENTWURF_CODEX_APP_SERVER_PID` naming an
|
|
107
|
+
operator-started app-server in its own tmux session. Exact-SHA CI at the pre-release commit:
|
|
108
|
+
run [`35202801617`](https://github.com/junghan0611/entwurf/actions/runs/35202801617)
|
|
109
|
+
`event=workflow_dispatch` @ `f5d6e10`, all four required jobs success and the `check` job's
|
|
110
|
+
qualification BODY step success.
|
|
111
|
+
- **The host's Codex hook declaration was measured with the herdr SessionStart group temporarily
|
|
112
|
+
removed, and that is a debt this release does not pay.** `~/.codex/hooks.json` on `oracle` carries
|
|
113
|
+
a second SessionStart group installed by herdr, so `doctor-codex-birth` reports the declaration as
|
|
114
|
+
edited after install and `smoke-codex-fresh-live` cannot run. For this cut the file was moved to
|
|
115
|
+
`hooks.json.herdr-bak` and re-published by `install-codex-birth`; the vendor's trust receipt for
|
|
116
|
+
`…/hooks.json:session_start:0:0` survived that re-publication, measured by a real Codex TUI
|
|
117
|
+
starting with no consent screen. The herdr group is restored after the cut and the doctor goes RED
|
|
118
|
+
again on purpose. **#117 owns the real repair** — entwurf requires a whole-file digest where the
|
|
119
|
+
unit it actually owns is one group.
|
|
120
|
+
- **Two gate defects found by the release gate itself, both repaired in test/gate code with zero
|
|
121
|
+
production bytes.** `check-gate-qualification` scored **518/520** on the first prepared commit and
|
|
122
|
+
both survivors were launch-directory claims whose assertion could not see the defect its mutant
|
|
123
|
+
plants. `CODEX-LAUNCH-CWD-ANCESTOR-PLAIN-PATHS-ONLY` guards two predicates — the cwd side and the
|
|
124
|
+
key side — and every non-plain cell put the encoded character in the KEY, so the key-side filter
|
|
125
|
+
answered alone and deleting the cwd-side guard changed nothing; cells that vary the CWD against a
|
|
126
|
+
plain untrusted key now close it. `FRESHCALL-CODEX-LAUNCH-CWD-NOTES-NEVER-REFUSES` asserted the
|
|
127
|
+
`indexOf("-C")` lookup but not the value read at it, so a note that asked about the REQUESTED cwd
|
|
128
|
+
instead of codex's own `-C` token passed; the three statements are now pinned contiguously.
|
|
129
|
+
Neither mutant was stale — each still matched its production line exactly once, which is what made
|
|
130
|
+
the hole an assertion hole rather than drift.
|
|
131
|
+
- **`smoke-entwurf-chain-live` went red because a model read the operator's policy correctly, and
|
|
132
|
+
the repair moved the authority rather than the wording.** The native Claude Code hop declined to
|
|
133
|
+
forward, citing the operator's global AGENTS.md; B, C and D were all born and addressable and the
|
|
134
|
+
terminus simply received nothing. A first repair put the provenance in the payload and the hop
|
|
135
|
+
named those sentences as the tell — content that vouches for itself is the shape of an injection,
|
|
136
|
+
so no sentence inside the payload can settle it. The payload is therefore byte-identical to
|
|
137
|
+
before, and the fact now rides the disposable world the turn starts in: the gate mints that
|
|
138
|
+
directory, every `ENTWURF_META_*_DIR` resolves inside it, and it exists because the operator ran
|
|
139
|
+
`release-gate`, so an `AGENTS.md`/`CLAUDE.md` written there is a standing operator instruction of
|
|
140
|
+
exactly the kind the operator's own global policy defers to for directory-scoped behaviour. **No
|
|
141
|
+
negative instruction was added**: a "do not check your policy" line would buy the same green by
|
|
142
|
+
asking the model to stop reading, and would keep passing after the chain broke. Accepted on the
|
|
143
|
+
host default model with no model pin.
|
|
144
|
+
|
|
145
|
+
- **The Codex LIVE card now measures WHERE a sibling starts, and the acceptance is recorded.**
|
|
146
|
+
`[LIVE 2026-09-16, Codex 0.153.4, 65 assertions, exit 0]` hop 1 requests a cwd and joins four
|
|
147
|
+
authorities that cannot borrow from each other — the pane's `#{pane_current_path}`, the vendor
|
|
148
|
+
rollout's `session_meta.cwd`, the Entwurf record and the requested scratch; hop 2 requests NEITHER
|
|
149
|
+
placement NOR cwd, so its directory can only come from the Codex caller's own record. Both are
|
|
150
|
+
asserted different from the app-server's live `/proc/<pid>/cwd`, which is the failure mode staying
|
|
151
|
+
visible rather than coincidentally equal. 65 and 56 are different contracts: lane C added the cwd
|
|
152
|
+
axis on top of lane B's seat axis. Receipts and digests in `DELIVERY.md` / `BASELINE.md`.
|
|
153
|
+
- **Caller-seat LIVE accepted on Linux, 2026-09-16 (Codex 0.153.4): 56 assertions, exit 0.** The
|
|
154
|
+
operator-owned app-server stayed alone in session A (`$30`/`@41`/`%45`, pid `737636`) while the
|
|
155
|
+
initial Pi, the omitted-placement Codex it opened, and that Codex's own outbound Pi all landed in a
|
|
156
|
+
different session S (`$2`: `@46`, `@47`, `@48`). A ≠ S is what makes the run decisive: the
|
|
157
|
+
app-server's inherited `TMUX` names A, so the pre-#95 environment fallback would have put the
|
|
158
|
+
outbound Pi there. Its receipt instead records `seat-source=codex-title-anchor`. `DELIVERY.md` owns
|
|
159
|
+
the coordinates, garden ids and digests, including the 43-assertion run kept as the D1 measurement.
|
|
160
|
+
- **`check-codex-app-server-launch` — 29 assertions, hermetic, plus 9 mutants all killed by
|
|
161
|
+
their own claim.** A fake vendor on a sandbox PATH under the real name reports the argv, parent
|
|
162
|
+
and environment it was handed; nothing is asserted by reading the launcher's source.
|
|
163
|
+
`check-pack-install` proves the INSTALLED verb reaches a vendor at the address the INSTALLED
|
|
164
|
+
compiled resolver computes — a file-list assertion cannot tell a shipped script from one that
|
|
165
|
+
resolves its siblings wrongly under `node_modules`.
|
|
166
|
+
- **Independent review caught a false success this gate had certified, and the repair removed the
|
|
167
|
+
class rather than the instance.** The launcher's first version re-derived the socket path in
|
|
168
|
+
bash and the gate compared the two spellings over four ASCII-normal inputs they agreed on.
|
|
169
|
+
`[측정 2026-09-16]` they disagree on `CODEX_HOME=$'\ufeff'` — JS `trim` strips U+FEFF, a POSIX
|
|
170
|
+
`[:space:]` trim does not, so the managed launch would have started a server at
|
|
171
|
+
`<BOM>/app-server-control/app-server-control.sock` while delivery looked at `$HOME/.codex` —
|
|
172
|
+
and on `path.join`'s normalization of a trailing slash or a `..` segment. Widening the matrix
|
|
173
|
+
would only have added the inputs somebody thought of, so the second spelling was deleted: the
|
|
174
|
+
launcher asks, the gate's hostile cells (BOM, trailing slash, `..`) keep it asking, and the
|
|
175
|
+
mutant that used to drop `CODEX_HOME` now re-derives the path in bash instead. The same review
|
|
176
|
+
found the `/proc` holder assertion over-determined (it accepted the fallback text OR a pid, so
|
|
177
|
+
deleting the scan passed) and the socket classifier not fail-closed on an unrecognised reading.
|
|
178
|
+
A second review pass then found that asking one authority for the address does not make the
|
|
179
|
+
ANSWER safe: the resolver returns `CODEX_HOME` faithfully, so
|
|
180
|
+
`CODEX_HOME=$'relative\ncontrol-home'` had the launcher create that directory under its caller's
|
|
181
|
+
cwd and bind a relative socket — a different file for every process that resolves it, while the
|
|
182
|
+
gate still passed. This launcher is the only Codex surface that CREATES and binds rather than
|
|
183
|
+
reads, so it now refuses a non-absolute or control-character address before the first write.
|
|
184
|
+
Every one of those is a discriminating cell with a mutant of its own; the lane is 9.
|
|
185
|
+
- **Two gate defects surfaced on the way and were fixed rather than worked around.** A mutant whose
|
|
186
|
+
`find` no longer matched had been silently untested since a rename — a stale mutant is a claim
|
|
187
|
+
nobody is checking, so `check-gate-qualification` scores it red. And the LIVE source-call oracle
|
|
188
|
+
compared tool arguments with `isDeepStrictEqual`, so a model that omitted the optional `wants_reply`
|
|
189
|
+
rather than passing it explicitly read as drift; it now normalizes that one key to its schema
|
|
190
|
+
default. Both are recorded in `VERIFY.md`'s affected-set rules so the inner loop stops missing them.
|
|
191
|
+
|
|
192
|
+
### Changed
|
|
193
|
+
|
|
194
|
+
- **`smoke-codex-fresh-live` launches in ONE stable directory instead of a fresh `mkdtemp` per
|
|
195
|
+
run.** Per-run random scratch made every run an undecided folder — a trust prompt per run, an
|
|
196
|
+
entry per run in the operator's `~/.codex/config.toml`, and unattended failure whenever nobody was
|
|
197
|
+
there to answer. The path is now one stable `os.tmpdir()`-rooted directory, answered `Trust`
|
|
198
|
+
once; the smoke PRINTS it as `launch-cwd <dir>` on every run and asserts that consent by name
|
|
199
|
+
BEFORE it launches anything, so a missing answer reads as `codex-launch-cwd-undecided` — with the
|
|
200
|
+
exact `codex -C <dir>` repair command — at the top of the run rather than as a callback timeout
|
|
201
|
+
twenty assertions later. The directory is printed rather than documented as a literal because
|
|
202
|
+
`os.tmpdir()` is a host fact: a doc spelling it `$TMPDIR/...` would send an operator with no
|
|
203
|
+
`TMPDIR` to `/entwurf-codex-fresh-live` in the filesystem root, which is a different directory
|
|
204
|
+
the vendor consents to separately. Lane C is unaffected: its only requirement of that directory is that it differ
|
|
205
|
+
from the app-server's own, which the assertion still proves.
|
|
206
|
+
- **The Codex live-spend default comes down to `gpt-5.6-luna`.** The `entwurf-dev` skill's Codex
|
|
207
|
+
backend default and the `smoke-codex-fresh-live` recommendation in `VERIFY.md` move off
|
|
208
|
+
`gpt-5.6-sol`. **This is the Codex axis alone** — OMP's fresh default is deliberately unchanged at
|
|
209
|
+
`openai-codex/gpt-5.6-sol` (`scripts/smoke-omp-fresh-live.ts:14`), because that is the model its
|
|
210
|
+
two-stage bootstrap callback was actually measured on and moving it would invalidate that
|
|
211
|
+
receipt. A cost decision, not a gate change: no step moved tier, and the acceptance receipt for
|
|
212
|
+
the Codex cell must be the run that actually happened on the new default rather than a `sol` run
|
|
213
|
+
inherited from before.
|
|
214
|
+
|
|
215
|
+
- **The fixed `codex` tmux home is RETIRED (#95 D1).** 0.21.0 shipped it three days earlier: an
|
|
216
|
+
omitted-placement Codex TARGET selected an already-existing session named exactly `codex`, where
|
|
217
|
+
the operator was asked to seat the app-server and every Codex TUI, and the launch receipt labelled
|
|
218
|
+
that source `Codex home`. GLG retired it the moment the caller-pane anchor stood up — *"무조건
|
|
219
|
+
은퇴야 … 코덱스의 거처를 만들어주자는 말은 그냥 기술이 안돼서 무마한 개념이야. 제대로 가야지."* The
|
|
220
|
+
room was a workaround for a mapping that did not exist yet, and keeping it after the anchor landed
|
|
221
|
+
would have left Codex alone answering "where does a sibling open?" differently from every other
|
|
222
|
+
backend. `CODEX_HOME_TMUX_SESSION`, the `codex-home` receipt source, its renderer branch, its
|
|
223
|
+
deterministic cells and its mutant are gone; an omitted seat is the caller's own session for every
|
|
224
|
+
backend, and the operator seats the app-server wherever they like — Entwurf still never creates,
|
|
225
|
+
moves or supervises it. The 2026-09-12 home acceptance stays recorded in `DELIVERY.md` and
|
|
226
|
+
`docs/setup-clean-host.md` as history, not as a live contract. The reshaped LIVE card now REQUIRES
|
|
227
|
+
the app-server to sit in a different session from the Pi/Codex pair: that separation is what tells
|
|
228
|
+
the caller-pane anchor apart from the app-server's inherited environment.
|
|
229
|
+
|
|
230
|
+
### Fixed
|
|
231
|
+
|
|
232
|
+
- **An in-band refusal now reaches the sender with the reason the RECEIVER gave it
|
|
233
|
+
(`d49e6cb`, `0c8a42c`).** A completed control-socket RPC that came back `success:false` was
|
|
234
|
+
finalized as `rejected` with no reason at all, so every named receiver refusal — including the
|
|
235
|
+
0.21.0 compaction guard's own `compacting` / `busy` — arrived at the caller as a reasonless
|
|
236
|
+
rejection indistinguishable from "no live route". The send hand now carries that error verbatim
|
|
237
|
+
when the receiver supplied one, and invents nothing when it did not: receiver evidence is not
|
|
238
|
+
reclassified into the resolver's taxonomy (`dormant-fire-forget-unsupported`,
|
|
239
|
+
`mailbox-undeliverable`, …), and the two remain distinguishable at the surface. The gap was one
|
|
240
|
+
hop wide and entirely invisible to the unit lane — the production `sendOverSocket` closure was
|
|
241
|
+
what dropped `response.error` before `driveSend` could see it — so the guard that holds it is a
|
|
242
|
+
production-wiring assertion with its own mutant (`V2PROD-INBAND-ERROR-WIRED`), not another test
|
|
243
|
+
of the pure function.
|
|
244
|
+
- **A Codex sibling's THREAD now opens where its pane is (#95 lane C).** The seat was right and the
|
|
245
|
+
directory was not: `[측정 2026-09-16]` a pi → Codex → Claude Code chain recorded the app-server's
|
|
246
|
+
`~/repos/gh/entwurf` for all three citizens while the panes sat in `~/repos/gh/agent-config`.
|
|
247
|
+
`[source rust-v0.153.4]` an explicit `--remote <endpoint>` resolves to `AppServerTarget::Remote`
|
|
248
|
+
(`codex-rs/tui/src/lib.rs:875-876`), and a Remote target takes its new thread's cwd from
|
|
249
|
+
`remote_cwd_override` alone (`app_server_session.rs:2022-2033`) — absent, the app-server opens the
|
|
250
|
+
thread in its OWN directory, and the birth hook then records that vendor-supplied cwd honestly.
|
|
251
|
+
The codex argv now always carries that override as `-C <dir>`, the flag the shared interactive
|
|
252
|
+
options already expose beside `--dangerously-bypass-approvals-and-sandbox`
|
|
253
|
+
(`utils/cli/src/shared_options.rs:53-68`), preserved for a remote target at
|
|
254
|
+
`startup_orchestration.rs:191-194`. **One flag, no vendor patch, no new layer.**
|
|
255
|
+
- **A Codex CALLER that names no cwd opens its sibling in its OWN directory.** This bridge runs as
|
|
256
|
+
the operator-owned app-server's MCP child, so the directory its process reports is the
|
|
257
|
+
app-server's rather than the caller's — an omitted `cwd` used to put every sibling of every Codex
|
|
258
|
+
caller in the app-server's repo, whatever backend was being opened. The surface now hands the
|
|
259
|
+
composition that citizen's RECORD cwd under the same condition that already carries its thread
|
|
260
|
+
id, and it is consulted only when the call requested none. One value, two carriers: tmux `-c`
|
|
261
|
+
places the pane, codex `-C` places the thread. Every other caller's argv is byte-identical, and
|
|
262
|
+
the receipt names which rule chose the directory (`requested` / the Codex caller's own record
|
|
263
|
+
directory) instead of calling a caller-record path "requested".
|
|
264
|
+
- **Three named refusals replace a silent wrong seat.** Zero matching panes is
|
|
265
|
+
`codex-caller-seat-unresolved` (the TUI is on another tmux server, its config carries no
|
|
266
|
+
`thread-id`, or that server has `allow-set-title off`, which `[측정]` replaces every pane title
|
|
267
|
+
with the hostname); two or more is `codex-caller-seat-ambiguous`; and a Codex caller whose own
|
|
268
|
+
`[tui].terminal_title` lacks `thread-id` is refused pre-mutation as `codex-caller-title-missing`
|
|
269
|
+
with the installer as its repair. None of them falls back to another session, and none opens a
|
|
270
|
+
window. That caller-side check runs only when the anchor is actually consulted — an explicit
|
|
271
|
+
seat never reads a title.
|
|
272
|
+
- **A pane title is a placement input and nothing else (Hard Rule 16).** Any process in any pane
|
|
273
|
+
can emit the same OSC string, so the leaf's answer may only reach a `-t` target; identity,
|
|
274
|
+
delivery and liveness keep the record + `_meta` join they already had, and the first-turn
|
|
275
|
+
framing still carries the caller's garden id rather than the thread id a title matched. The leaf
|
|
276
|
+
(`pi-extensions/lib/codex-caller-seat.ts`) imports nothing at all, which is how that boundary is
|
|
277
|
+
held by the code rather than by a comment.
|
|
278
|
+
- **The title match is over tokens, not ` | ` segments, and the count is over panes.**
|
|
279
|
+
`title_setup.rs:183-193` joins the `activity` item to its neighbour with a plain space rather
|
|
280
|
+
than ` | `, so on a host whose operator list ends in `activity` a working TUI renders
|
|
281
|
+
`<spinner> <thread-id>` inside one segment and a segment-equality rule would lose the seat
|
|
282
|
+
exactly while the caller is busy. Counting panes rather than tokens keeps a single pane that
|
|
283
|
+
shows the same thread twice (`thread-title` beside `thread-id`) from reading as an ambiguity.
|
|
284
|
+
|
|
285
|
+
## 0.21.0 - 2026-09-13
|
|
286
|
+
|
|
287
|
+
### Added
|
|
288
|
+
|
|
289
|
+
- **This is a minor release because Codex support adds a new visible citizen axis, not merely a
|
|
290
|
+
patch to an existing rail.** The operator-owned exact `codex` tmux home gives the app-server and
|
|
291
|
+
supported Codex TUIs a stable visible place without making Entwurf their lifecycle manager. UX
|
|
292
|
+
symmetry means each backend remains a sibling with its own transcript, auth, native tools, and
|
|
293
|
+
visible seat—not that unlike transports are relabeled as identical. The release also carries the
|
|
294
|
+
independent #111 and #112 bug repairs.
|
|
295
|
+
- **OpenAI Codex CLI becomes an app-server-backed native-push citizen (#95).**
|
|
296
|
+
A user-scope `SessionStart` hook, declared in the operator's own `$CODEX_HOME/hooks.json`,
|
|
297
|
+
mints the V3 record on the first turn and sets the visible thread title. It needs no root:
|
|
298
|
+
the installer refuses to run as uid 0, and the declaration is trust-gated rather than
|
|
299
|
+
prompt-free — the operator answers the vendor's prompt with "Trust all" ONCE in their own
|
|
300
|
+
visible Codex, and the vendor records that decision itself. Entwurf never computes, writes,
|
|
301
|
+
pre-seeds or reads a `trusted_hash`, and never touches `config.toml`. Because the vendor
|
|
302
|
+
hashes the declaration identity (event, matcher, handler path) and not the launcher's
|
|
303
|
+
contents, the payload closure can be upgraded on later releases without asking again. The bridge strictly joins each request's Codex metadata to
|
|
304
|
+
that record, so multiple visible TUIs may share one app-server without sharing a
|
|
305
|
+
process marker. The purpose is to preserve Codex's native tools, delegation, and work
|
|
306
|
+
context as a citizen — not to add an ACP Codex backend or duplicate GPT access.
|
|
307
|
+
- **Codex gets three independently owned install/doctor/inverse atoms.**
|
|
308
|
+
`install-codex-birth` owns only the fixed `$CODEX_HOME/hooks.json` declaration plus its
|
|
309
|
+
launcher/payload closure under `${XDG_DATA_HOME:-~/.local/share}/entwurf/codex-birth`, with a
|
|
310
|
+
digest inventory that licenses an exact inverse and refuses drifted bytes;
|
|
311
|
+
`install-codex-mcp` owns only `[mcp_servers.entwurf-bridge]`; and
|
|
312
|
+
`install-codex-statusline` owns only `thread-title`. The MCP atom also owns the exact
|
|
313
|
+
`env_vars` names that forward `CODEX_HOME`, Entwurf garden/control roots, and the
|
|
314
|
+
operator-owned app-server's `TMUX`/`TMUX_PANE`. `setup` never escalates or starts it.
|
|
315
|
+
- **`entwurf_fresh_call` accepts `backend: "codex"` on both public tool surfaces and the
|
|
316
|
+
operator skill.** It requires an explicit model and uses the measured
|
|
317
|
+
`codex --remote unix://<default-socket> --model … --dangerously-bypass-approvals-and-sandbox`
|
|
318
|
+
shape. The birth unit, MCP/env boundary, visible title, and the operator-owned app-server
|
|
319
|
+
socket are checked before tmux mutation. With placement omitted, Codex resolves the exact
|
|
320
|
+
existing `codex` tmux home; a missing home rejects without creating one. Explicit placement
|
|
321
|
+
remains an expert override. This does not infer an attached TUI seat from request identity.
|
|
322
|
+
- **Omitted placement becomes a backend-selected policy, and Codex is its first implementation.**
|
|
323
|
+
Every backend could already be sent to an operator-owned existing seat through explicit
|
|
324
|
+
`placement.tmuxSession`; that is unchanged, and named `claude`/`pi` seats stay available that
|
|
325
|
+
way without becoming omitted-placement defaults. What is new is that omitting placement no
|
|
326
|
+
longer means one thing for every backend: `pi`, `claude-code`, `copilot` and `omp` still open
|
|
327
|
+
in the caller's seat, while `codex` resolves its exact operator-owned home. tmux remains the
|
|
328
|
+
visible launch seat, never an address — the garden id is the address and arrives in the
|
|
329
|
+
callback envelope. Sibling symmetry is transcript, auth, native tools and visibility; it is
|
|
330
|
+
not a claim that the transports are the same.
|
|
331
|
+
- **Why a client-side home rather than a vendor seat carrier.** Upstream
|
|
332
|
+
[openai/codex#44774](https://github.com/openai/codex/issues/44774) records the maintainer's
|
|
333
|
+
preference for client-side terminal/window management, citing multi-client steer semantics and
|
|
334
|
+
network propagation. Rather than wait for a request→pane carrier or guess one, 0.21.0 takes the
|
|
335
|
+
client-side route the exact `rust-v0.153.4` source supports: an operator-owned exact home plus
|
|
336
|
+
explicit placement, with the wider attached-TUI topology left unsupported and unclaimed.
|
|
337
|
+
|
|
338
|
+
### Changed
|
|
339
|
+
|
|
340
|
+
- **The native-push rail is adapter-specific instead of Antigravity-shaped.** Codex liveness
|
|
341
|
+
is `thread/loaded/list` over the app-server WebSocket UDS; delivery is the measured one-shot
|
|
342
|
+
`codex queue --remote … --thread … --message …`. It has no mailbox, receiver marker, or
|
|
343
|
+
resume surface. Unlike Antigravity, Codex delivery is never retried because vendor acceptance
|
|
344
|
+
may precede the CLI receipt; replay would duplicate the user's message.
|
|
345
|
+
- **Codex remains outside ACP and Entwurf remains outside Codex lifecycle ownership.**
|
|
346
|
+
Entwurf does not install Codex, supply auth, expose a generic app-server API/manager, or
|
|
347
|
+
start/supervise/restart the app-server or create its tmux home. The supported topology is
|
|
348
|
+
one operator-owned existing session named `codex` containing the app-server and supported
|
|
349
|
+
Codex TUIs. N arbitrary attached TUIs across sessions still have no request→TUI seat join;
|
|
350
|
+
that wider topology is unsupported and unclaimed rather than a blocker to the explicit home.
|
|
351
|
+
- **`entwurf_peers` bounds expensive historical observation to its 32 rendered rows (#112).**
|
|
352
|
+
Full-store parsing, duplicate/conflict authority, socket liveness, diagnostics, and all 1,012
|
|
353
|
+
measured payload rows remain intact; older receiver/transcript cells say `unobserved` rather
|
|
354
|
+
than fabricating facts. On the 1,012-record fixture, repeated warm composition fell from about
|
|
355
|
+
3.2s to 70–103ms. Generic fact-provider callers remain unbounded unless they explicitly opt in.
|
|
356
|
+
- **Version-coherence gates no longer require concise `AGENTS.md` to duplicate package pins.**
|
|
357
|
+
The owning runtime/package documents remain checked; removing redundant pin prose from the
|
|
358
|
+
maintainer guide no longer makes an otherwise coherent floor fail.
|
|
359
|
+
|
|
360
|
+
### Upgrade note
|
|
361
|
+
|
|
362
|
+
Codex native delivery requires three user-scope atoms and one operator trust answer. No step
|
|
363
|
+
needs root:
|
|
364
|
+
|
|
365
|
+
```bash
|
|
366
|
+
entwurf install-codex-birth
|
|
367
|
+
entwurf install-codex-mcp
|
|
368
|
+
entwurf install-codex-statusline
|
|
369
|
+
|
|
370
|
+
# Then, ONCE, in a visible plain Codex: answer the hook prompt with "Trust all and continue"
|
|
371
|
+
# and send one first turn. Until the vendor records that receipt the hook is declared and
|
|
372
|
+
# never runs, and `doctor-codex-birth` / `setup` stay non-green and say so.
|
|
373
|
+
|
|
374
|
+
# First create/open the operator-owned tmux session named exactly `codex`.
|
|
375
|
+
# From a pane in that session:
|
|
376
|
+
CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"
|
|
377
|
+
mkdir -p "$CODEX_HOME/app-server-control"
|
|
378
|
+
codex app-server --listen "unix://$CODEX_HOME/app-server-control/app-server-control.sock"
|
|
379
|
+
# Seat supported Codex TUIs in this same `codex` session.
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
Then restart/open Codex and send its first turn. `entwurf setup <project>` never runs sudo and
|
|
383
|
+
never starts the app-server.
|
|
384
|
+
|
|
385
|
+
### Verification
|
|
386
|
+
|
|
387
|
+
- The GPT-6 review reopened five surfaces in the candidate: terminal parser, explicit
|
|
388
|
+
env-name boundary, setup acceptance, fresh preflight safety, and clause-7 composition.
|
|
389
|
+
The prior “source-complete; only host ownership remains” wording is withdrawn.
|
|
390
|
+
- The 2026-09-11 Codex CLI 0.153.4 run remains pre-amendment evidence: native-push and a
|
|
391
|
+
public MCP call woke one visible loaded thread; authenticated request identity resolved
|
|
392
|
+
`agentId=meta-session/codex`, `replyable=true`; and a source hook minted a record in an
|
|
393
|
+
isolated store. It does not qualify the amended candidate.
|
|
394
|
+
- The 2026-09-12 Codex 0.153.4 explicit-home LIVE passed 57 assertions. A real initial Pi
|
|
395
|
+
ran at `$150/@397`; the operator app-server remained at `$158/@390/%390`; omitted-placement
|
|
396
|
+
Codex opened at `$158/@398`; and Codex opened outbound Pi at `$158/@399`. Exact callbacks
|
|
397
|
+
and addressed delivery succeeded both ways. `DELIVERY.md` carries the garden ids, thread,
|
|
398
|
+
nonces, preserved log path, and SHA-256.
|
|
399
|
+
- Two red attempts repaired the acceptance itself. “Wait” had let Codex call one-hour
|
|
400
|
+
`wait_agent`; the task now ends its turn and forbids wait tools. A model also reformatted
|
|
401
|
+
a copied LAUNCH receipt; the gate compares source-owned tool results rather than treating that prose
|
|
402
|
+
as raw evidence. Failure cleanup interrupts only the exact smoke-owned active Codex turn and removes
|
|
403
|
+
only source-receipt-named windows.
|
|
404
|
+
- The second full prepare P5 remained honestly red at **MUST 23 PASS / 1 FAIL / 0 SKIP**. The initial
|
|
405
|
+
Pi's source transcript proved Codex launch, exact callback, native-push delivery, outbound Pi launch
|
|
406
|
+
and callback, and Codex's final mailbox enqueue; it also showed the missing observation's direct
|
|
407
|
+
cause: the model shortened expected launch-report token `…1G5XEKQH` to `…1G5XEKQ`. Enqueue did not
|
|
408
|
+
prove the fixture read, and cleanup had removed the mailbox needed to distinguish every downstream
|
|
409
|
+
observer hypothesis. The amendment removes intermediate model coordinate reports entirely: Pi JSONL
|
|
410
|
+
call/result rows are joined by call id, exact tool name/arguments, non-error result, and uniqueness;
|
|
411
|
+
Codex uses the corresponding structured app-server MCP items; raw callback events supply garden ids.
|
|
412
|
+
Mailbox prose can never authorize a window kill. Every drain now persists filename, byte length,
|
|
413
|
+
SHA-256, observed token candidates, phase-specific selection/exclusion reason and a 0600 body artifact
|
|
414
|
+
immediately, plus resolved-root/run/source-path manifest and a mandatory fixture/record/transcript/thread
|
|
415
|
+
snapshot before destructive cleanup on every outcome. That snapshot uses the record owner's `.meta.json`
|
|
416
|
+
filename, records missing expected sources, and still copies known raw files when optional identity recovery
|
|
417
|
+
throws; final acceptance also requires known `completed` Codex turn/items state and logs payload validation
|
|
418
|
+
separately from token/sender candidate selection. The preserved failed-rollout extraction
|
|
419
|
+
(`c3a28938…`) confirms raw outbound launch → callback → final enqueue but still not fixture read; its tracked
|
|
420
|
+
positive fixture is explicitly labelled a vendor-source-derived v2 projection, not a captured `thread/read`
|
|
421
|
+
response. A second defect the amendment itself introduced was then measured and closed: the smoke read the
|
|
422
|
+
initial Pi's `transcriptPath` once at callback correlation, and a record that gained that path 34s later
|
|
423
|
+
left every subsequent source observation permanently empty — one 300s red whose chain had in fact run to
|
|
424
|
+
completion, proven by the Codex thread's own completed final `entwurf_v2` result. The resolution is a pure
|
|
425
|
+
leaf that re-reads the record while its cached path is empty (an unreadable record stays fail-loud; only
|
|
426
|
+
"no path yet" is pending), its own `CODEX-LIVE-SOURCE-PATH-RE-RESOLVED` claim and mutant, and an explicit
|
|
427
|
+
`no unverified child window killed` line so a run that never obtains source evidence names its
|
|
428
|
+
unrecoverable windows instead of leaking them. Declared inventory is now 475 mutants across 43 lanes.
|
|
429
|
+
The amended standalone LIVE then passed: **48 assertions, exit 0**, final source audit `initial-pi=3/3`
|
|
430
|
+
and `codex=3/3` completed exact, all three opened windows reclaimed by their own receipts, artifact
|
|
431
|
+
`.probe-artifacts/codex-fresh-live-fZccoK/`. Assertion count is not comparable with the pre-amendment 57:
|
|
432
|
+
that contract counted model coordinate reports this one removes.
|
|
433
|
+
- **Release acceptance for this candidate: `check:full` exit 0 in 502s, then the LIVE release gate
|
|
434
|
+
`--cut` at MUST PASS=24 FAIL=0 SKIP=0, BEHAVIOR PASS=1 FAIL=0 SKIP=0, `cut: OK`.** Its
|
|
435
|
+
`check-gate-qualification` MUST step killed 475/475 across 43 lanes with the origin HEAD and
|
|
436
|
+
work-surface hash identical before and after. The aggregate's own Codex leg repeated the
|
|
437
|
+
48-assertion source-owned acceptance (artifact `.probe-artifacts/codex-fresh-live-2oId4C/`), and the
|
|
438
|
+
mux lifecycle MUST passed in the same run. Scratch log sha256 `942d5fa0…`.
|
|
439
|
+
- **Observation, not a repaired defect.** The first acceptance attempt at the identical source
|
|
440
|
+
fingerprint failed two model-in-loop MUST steps: a callback nonce arrived one character short
|
|
441
|
+
(`…c70cfb` for `…c70cfb2`) and an addressed instruction carried the wrong payload. Both oracles
|
|
442
|
+
refused fail-closed and named their cause, and the joined source receipts proved delivery and rails
|
|
443
|
+
were correct, so nothing in the product was changed. The same candidate, model, and environment then
|
|
444
|
+
passed. That leaves an intermittent exact-byte transfer failure in the model lane as a recorded
|
|
445
|
+
observation; the retry does not establish a cause for it.
|
|
446
|
+
- The first 460-mutant qualification honestly stopped at 454/460. One wait-task mutant was
|
|
447
|
+
stale after the prompt repair; the birth safe-umask mutant was killed earlier by the new
|
|
448
|
+
package-root mode doctor but carried the wrong assertion label; and four setup claims shared
|
|
449
|
+
one control-red because `smoke-setup-verdict` ran real pnpm inside the qualifier's
|
|
450
|
+
node_modules-symlink snapshot. The manifest was re-pinned, safe-umask attribution now has one
|
|
451
|
+
QK helper at both valid failure leaves, and the setup behavior gate uses a strict fixture
|
|
452
|
+
pnpm accepting only a no-op `install --frozen-lockfile` or the source-owned `build-bridge`
|
|
453
|
+
emit, whose snapshot-local dist is removed on exit. The next run killed all 460 mutants but
|
|
454
|
+
correctly rejected its own impure snapshot: the fixture had called the production dist-lock
|
|
455
|
+
wrapper, which removed its lock but left the ignored `.tmp-verify` parent. The fixture now
|
|
456
|
+
invokes the process-exclusive snapshot emit directly; a focused pre/post tree-manifest replay
|
|
457
|
+
was byte-identical. The fresh run then killed **460/460** mutants across 43 lanes in 53m42s
|
|
458
|
+
with the origin and snapshot surfaces pure. Biome only collapsed the new manifest arrays onto
|
|
459
|
+
one line afterward (claim/subject/find/replace bytes and semantics unchanged), and the frozen
|
|
460
|
+
full floor passed in 506s. The receipt-bearing final candidate passed the full floor again
|
|
461
|
+
in 501s before the implementation commit and local-main merge.
|
|
462
|
+
|
|
463
|
+
### Fixed
|
|
464
|
+
|
|
465
|
+
- **The Copilot receiver parent-death mutant no longer leaves one PID-1 fixture child per qualification run.** P9 found ten reparented Node stubs from ten qualifications. The gate did detect the mutant, but its failing assertion threw before the cleanup statement below it; cleanup now kills the surviving mutant child before asserting, preserving the same attributed red while leaving no process behind.
|
|
466
|
+
- **Control-socket send during Pi compaction is no longer a false `delivered:true` (#111).** A compacting Pi citizen is neither idle nor an ordinary streaming turn, but the receiver only sampled `ctx.isIdle()` and forwarded every non-idle send into `pi.sendMessage({ triggerTurn: true })`. Installed Pi 0.85.1 `sendCustomMessage` then starts `_runAgentPrompt` when not streaming — racing `compact()`'s session rewrite, which is the field path that can make compaction "풀린다". There is no public `ExtensionContext.isCompacting()`. The resident arms on `session_before_compact` and refuses as `compacting`; quiet unknown non-idle (`!idle && ctx.signal === undefined`) is fail-closed as `busy`, not `compacting`, because `ctx.signal` is `agent.activeRun?.abortController.signal` and is already gone during `_handlePostAgentRun` retry/continuation while `_isAgentRunActive` is still true — that cell also covers the manual-compact start race and branch summary, which the public API cannot tell apart. Either token: no `pi.sendMessage`, no `delivered:true`. Idle and live-run steer/followUp are unchanged. Frozen v2 reject taxonomy is untouched — this is an in-band RPC refusal.
|
|
467
|
+
|
|
7
468
|
## 0.20.1 - 2026-09-10
|
|
8
469
|
|
|
9
470
|
### Fixed
|
|
@@ -124,17 +585,15 @@ Hosts with no detected harness, and every Linux host, see no new prompt.
|
|
|
124
585
|
by the promotion above; it is where a borrowed Mac produces the physical-host
|
|
125
586
|
evidence the CI runner cannot.
|
|
126
587
|
- **`scripts/raw-codex-measure/` — Codex 0.153.4 vendor measurement (#95 step 1).**
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
`PIN_CODEX_MINOR` 0.144 → 0.153 is a re-verification of the archived drift
|
|
137
|
-
probe, not a product pin.
|
|
588
|
+
**At the measurement landing on 2026-09-08**, no Entwurf Codex unit had been
|
|
589
|
+
installed or built, no product record/doctor existed, and fresh did not include
|
|
590
|
+
Codex. Those are dated step-1 facts, not the current candidate state. The ledger
|
|
591
|
+
established first-turn `SessionStart`, callback spelling
|
|
592
|
+
`mcp__entwurf_bridge__entwurf_v2`, the shared-app-server refutation of a pid-keyed
|
|
593
|
+
citizen join, root-owned prompt-free hooks, and `thread/name/set`. Later #95 work
|
|
594
|
+
added the record/native-push/fresh candidate described under Unreleased.
|
|
595
|
+
`PIN_CODEX_MINOR` 0.144 → 0.153 was a re-verification of the archived drift probe,
|
|
596
|
+
not a product pin.
|
|
138
597
|
|
|
139
598
|
### Changed
|
|
140
599
|
|
|
@@ -261,9 +720,9 @@ Lane receipts (oracle, 2026-09-09) plus prepare P4/P5 on this host. There is
|
|
|
261
720
|
CommandLineTools stub. Both are cells the borrowed-Mac probe
|
|
262
721
|
(`scripts/raw-macos-measure/probe.sh`, M6 and M3) is built to answer. Do
|
|
263
722
|
not fill them from a CI image spec.
|
|
264
|
-
- **Codex
|
|
265
|
-
|
|
266
|
-
|
|
723
|
+
- **On the 0.20.0 release date (2026-09-09), Codex was not a garden backend in that
|
|
724
|
+
cut.** Step 1 of #95 was measurement only and `entwurf_fresh_call` did not include
|
|
725
|
+
Codex. This is release archaeology; 0.21.0 support supersedes it.
|
|
267
726
|
|
|
268
727
|
## 0.19.0 - 2026-09-07
|
|
269
728
|
|
package/CONTRIBUTING.md
CHANGED
|
@@ -4,7 +4,7 @@ This is a daily-driver bridge. Correctness beats feature breadth. Read this befo
|
|
|
4
4
|
|
|
5
5
|
## What this repo is
|
|
6
6
|
|
|
7
|
-
`entwurf` is a **garden-citizen dispatch bridge** — entwurf-core (v2 dispatch) + a meta-bridge + a pi adapter + an **ACP plugin on a two-backend adapter rail** — that lets already-running harnesses address one another by garden id; pi is one adapter, not the subject. The ACP plugin borrows the backend's identity (system prompt preset, model behavior, tool implementations) and shapes the *operating surface* — tools, MCP, skills, permissions — to match pi's own policy **wherever that backend exposes a knob for it**. Where it does not, the plugin does not fake one: a cortex session keeps its own native tool surface and receives MCP through an overlay projection, so "shaped to pi's policy" is a Claude-strength claim, not a universal one. Claude is the reference ACP backend and Snowflake Cortex Code is the second (landed 0.13.0, `cortex-` prefixed ids — [docs/acp-backend-rail.md](./docs/acp-backend-rail.md#cortex-code-audit-d1d10)); Codex
|
|
7
|
+
`entwurf` is a **garden-citizen dispatch bridge** — entwurf-core (v2 dispatch) + a meta-bridge + a pi adapter + an **ACP plugin on a two-backend adapter rail** — that lets already-running harnesses address one another by garden id; pi is one adapter, not the subject. The ACP plugin borrows the backend's identity (system prompt preset, model behavior, tool implementations) and shapes the *operating surface* — tools, MCP, skills, permissions — to match pi's own policy **wherever that backend exposes a knob for it**. Where it does not, the plugin does not fake one: a cortex session keeps its own native tool surface and receives MCP through an overlay projection, so "shaped to pi's policy" is a Claude-strength claim, not a universal one. Claude is the reference ACP backend and Snowflake Cortex Code is the second (landed 0.13.0, `cortex-` prefixed ids — [docs/acp-backend-rail.md](./docs/acp-backend-rail.md#cortex-code-audit-d1d10)); Codex is not on this rail at all — it is an unreleased native-push candidate with record birth and delivery-probe evidence (see AGENTS.md, DELIVERY.md), never ACP-managed — and Gemini is not a shipped backend. That is the entire ACP-rail scope.
|
|
8
8
|
|
|
9
9
|
**How a backend joins the rail** (the shape a PR must take): one adapter object in `pi-extensions/lib/acp/backend-adapter.ts` + its own curated rows/overlay modules + its own `check-acp-*` gate and mutant lane. Backend-specific *behavior* must stay behind the adapter, and backend-specific settings ride the opaque `adapterSettings` seam rather than growing the common config. That is not a ban on ever touching the common layer (`backend.ts`'s turn loop, `acp-client.ts`, `event-mapper.ts`, `session-store.ts`, `config.ts`) — cortex's landing did change `backend.ts` to pass the authoritative session key through the generic `ensureOverlay` seam. The rule is narrower and stricter: a common-layer change must be **backend-invariant** (it reads no backend name and branches on no backend) and **separately gated**. A common file that grows an `if (backend === …)` is the thing to reject.
|
|
10
10
|
|