@junghanacs/entwurf 0.19.0 → 0.20.1

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 (74) hide show
  1. package/AGENTS.md +1 -1
  2. package/BASELINE.md +23 -5
  3. package/CHANGELOG.md +261 -0
  4. package/DELIVERY.md +59 -6
  5. package/README.md +41 -9
  6. package/VERIFY.md +9 -1
  7. package/docs/acp-backend-rail.md +41 -16
  8. package/docs/external-mcp-host.md +10 -5
  9. package/docs/fresh-cut-policy.md +3 -2
  10. package/docs/setup-clean-host.md +93 -7
  11. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/backend-adapter.js +2 -2
  12. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/models.js +3 -3
  13. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/overlay.js +6 -2
  14. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/tool-surface.js +6 -0
  15. package/package.json +3 -3
  16. package/pi-extensions/acp-provider.ts +1 -1
  17. package/pi-extensions/lib/acp/acp-client.ts +3 -3
  18. package/pi-extensions/lib/acp/backend-adapter.ts +3 -3
  19. package/pi-extensions/lib/acp/backend.ts +3 -3
  20. package/pi-extensions/lib/acp/event-mapper.ts +4 -4
  21. package/pi-extensions/lib/acp/models.ts +3 -3
  22. package/pi-extensions/lib/acp/overlay.ts +6 -2
  23. package/pi-extensions/lib/acp/tool-surface.ts +6 -0
  24. package/pi-extensions/lib/meta-session.ts +25 -0
  25. package/run.sh +104 -33
  26. package/scripts/agy-bridge.sh +1 -1
  27. package/scripts/agy-imprint.sh +10 -7
  28. package/scripts/check-acp-carrier-augment.ts +6 -5
  29. package/scripts/check-acp-cortex.ts +41 -0
  30. package/scripts/check-acp-provider-surface.ts +9 -6
  31. package/scripts/check-acp-usage-accounting.ts +9 -9
  32. package/scripts/check-copilot-birth-hook.ts +3 -1
  33. package/scripts/check-copilot-receive-arm.ts +169 -21
  34. package/scripts/check-copilot-statusline.ts +3 -1
  35. package/scripts/check-gate-qualification.ts +11 -10
  36. package/scripts/check-hook-launch-topology.ts +6 -5
  37. package/scripts/check-install-surface.ts +25 -8
  38. package/scripts/check-meta-facts.ts +6 -4
  39. package/scripts/check-meta-hook-session-switch.ts +4 -3
  40. package/scripts/check-meta-identity-consumers.ts +8 -7
  41. package/scripts/check-meta-receiver-marker.ts +2 -1
  42. package/scripts/check-omp-birth-hook.ts +3 -2
  43. package/scripts/check-probe-bridge-command.ts +3 -1
  44. package/scripts/check-setup-qualification.sh +21 -0
  45. package/scripts/copilot-bridge-doctor.sh +15 -5
  46. package/scripts/copilot-bridge-install.sh +6 -2
  47. package/scripts/copilot-launch.sh +16 -2
  48. package/scripts/copilot-receive-bridge.sh +80 -13
  49. package/scripts/lib/reclaim-on-exit.ts +86 -0
  50. package/scripts/meta-bridge-doctor.sh +56 -15
  51. package/scripts/meta-bridge-install.sh +13 -7
  52. package/scripts/meta-bridge-uninstall.sh +6 -4
  53. package/scripts/mutants/acp-cortex.json +26 -2
  54. package/scripts/mutants/copilot-receive.json +26 -0
  55. package/scripts/mutants/omp-birth.json +23 -6
  56. package/scripts/mutants/pi-package-ownership.json +26 -0
  57. package/scripts/mutants/setup-verdict.json +13 -0
  58. package/scripts/omp-bridge-doctor.sh +110 -41
  59. package/scripts/omp-bridge-install.sh +6 -2
  60. package/scripts/omp-receive-install.sh +7 -2
  61. package/scripts/pi_settings_io.py +3 -1
  62. package/scripts/raw-async-delivery/README.md +10 -1
  63. package/scripts/raw-codex-measure/README.md +689 -0
  64. package/scripts/raw-codex-measure/source-audit.md +243 -0
  65. package/scripts/raw-macos-measure/README.md +148 -0
  66. package/scripts/raw-macos-measure/probe.sh +389 -0
  67. package/scripts/register-pi-package.py +38 -30
  68. package/scripts/register-pi-provider.py +3 -2
  69. package/scripts/smoke-acp-raw-turn-live.ts +1 -1
  70. package/scripts/smoke-meta-async-drift.sh +7 -5
  71. package/scripts/smoke-meta-install-state.sh +70 -19
  72. package/scripts/smoke-omp-bridge-state.sh +38 -0
  73. package/scripts/smoke-setup-verdict.sh +100 -0
  74. package/scripts/smoke-user-scope-citizen.sh +199 -71
package/AGENTS.md CHANGED
@@ -28,7 +28,7 @@ For agents that own this repo: invariant principles and reproducible verificatio
28
28
  - **entwurf-core (v2)** owns garden-id addressing, peer facts, liveness interfaces, dispatch decisions, rail choice, and delivery evidence.
29
29
  - **Record authority** owns citizen identity. Every addressable citizen uses the same V3 `MetaIdentity` schema. `backend` selects capability/rail behavior; it does not create an identity hierarchy.
30
30
  - **pi adapter** attaches a pi session to a record at `session_start`, hosts the record-keyed control socket, and exposes the native pi tool surface.
31
- - **Native bridges** register already-running native sessions without taking over their transcript or auth: Claude Code and Copilot CLI are mailbox/self-fetch (a Claude hook arms the watch; a forked Copilot extension holds it); Antigravity is probe-backed native-push. Codex has archived probe evidence, but its managed native lane was declined because pi already supplies the official GPT provider path; do not duplicate it as a native citizen or ACP backend.
31
+ - **Native bridges** register already-running native sessions without taking over their transcript or auth: Claude Code and Copilot CLI are mailbox/self-fetch (a Claude hook arms the watch; a forked Copilot extension holds it); Antigravity is probe-backed native-push. Codex's decline was reversed by GLG on 2026-09-08: lane #95 is open at step 1 (`scripts/raw-codex-measure/`), mints no record, and must not become an ACP backend.
32
32
  - **ACP plugin** registers provider `entwurf` inside a pi host session and drives a backend under an isolated overlay. The host pi session is already a record-backed socket citizen; the plugin does not mint another citizen/socket/peer layer.
33
33
  - **mux is launch-only and deliberately small.** Shipped: the tmux placement leaf (`mux-placement.ts` — inspect/append in the caller's own session, close on its whole SERVER), the visible runtime launch composition (`mux-launch.ts`), the fresh-call composition (`mux-fresh-call.ts`), the cwd-aware resume placement composition (`mux-resume-call.ts`), and the import-free leaves they inject (`classify-tmux-cwd.ts`, `resolve-tmux-session.ts`). Fresh call is exposed as `entwurf_fresh_call`; visible same-id resume is `entwurf_resume_call`, composed at the two public surfaces by injecting `mux-resume-call` into the v2-side `entwurf-v2-visible-resume.ts`. The exact import graph is enumerated in [docs/mux-launch-rail.md](./docs/mux-launch-rail.md) §11. `entwurf_v2` behavior is unchanged and delivery still never imports launch. Fresh call learns a new sibling's garden id from the CALLBACK it makes, never from a lookup — so the pre-injected-token → identity-lookup design (rail §6) is **closed, not paused**. Do not reopen it without new evidence and explicit GLG re-approval. Fresh-call requires one explicit model and passes it in each runtime's measured CLI dialect (Pi `--model <provider/model>`, Claude Code `--model=<id-or-alias>`, Copilot `--model <name>` beside `--interactive <prompt>` and the explicit `--yolo` policy — a GLG width decision, 2026-08-25, after a callback-only `--allow-tool` grant stopped the sibling at every task tool); the Copilot backend opens through entwurf's OWN managed invocation (`entwurf copilot`), never the bare vendor, and its four required capabilities — birth, MCP hand, receiver, visible footer — are decided by `copilot-fresh-preflight.ts` BEFORE the tmux mutation, so a missing unit is a named refusal instead of a dead window; it also takes ONE optional literal absolute `cwd` (#73 — omitted/`""` means the caller's own directory; no trim, no realpath, no project-name resolver; classified by the shared `classify-tmux-cwd.ts` leaf; the receipt echoes the REQUEST only), so a cross-repo fresh sibling is a fresh-call fact and never a reason to resume a dormant record. It also takes ONE optional project seat, `placement.tmuxSession` (#105 — the exact name of an EXISTING session on the caller's OWN server, turned into a `$id` by `resolve-tmux-session.ts` before anything reaches `-t`). **It creates nothing** — `tmux-session-missing`/`tmux-session-name-invalid` are named refusals, no `ifMissing`/`new-session` axis exists (GLG, 2026-09-07), seat and cwd never infer each other, and close binds to the SERVER half since a placed window may sit outside the caller's session. Resume takes only an existing target id and gets transcript/model/provider/cwd from its record. Do not grow either narrow carrier into a generic driver, harness profile, arbitrary command/env selection, or a second creation API. Ownership and import prohibitions: [docs/mux-launch-rail.md](./docs/mux-launch-rail.md) §11.
34
34
  - **One delivery verb:** `entwurf_v2` addresses an existing garden id. Current routes are live control-socket send, active self-fetch mailbox enqueue, and probe-alive native-push. NO route starts a process: the hidden background resume for a dormant citizen was withdrawn under the visible-first rule, so a dormant in-domain citizen rejects as `dormant-fire-forget-unsupported` and the intent axis is single-valued. Every other complementary state pair rejects honestly too. Fresh creation is a separate verb — `entwurf_fresh_call` — and it returns a launch receipt only; the new sibling's address arrives asynchronously as the sender envelope of its nonce callback.
package/BASELINE.md CHANGED
@@ -18,17 +18,28 @@ This table is the operator-facing support/certification view. It complements the
18
18
  model interview below; a persuasive answer from the model cannot turn an unmeasured
19
19
  host into a certified one. Verdicts are one of **certified** (a real host ran the
20
20
  installed doctor green), **shape-only** (gates model the package, no live host),
21
- or **unverified**.
21
+ or **unverified**. Since 0.20.0 the table also carries two qualifiers:
22
+ **CERTIFIED (CI)** marks a shape-only surface observed green on a CI runner — a
23
+ runner is not a physical host, so bare **certified** still means a physical-host
24
+ doctor green; and **NOT CERTIFIED — pending physical host** marks a rail whose
25
+ missing evidence only a physical host can supply. That is an evidence boundary,
26
+ not **UNSUPPORTED** — the deliberate never, which today is native Windows only.
22
27
 
23
28
  | Surface | Verdict | Evidence |
24
29
  |---|---|---|
25
30
  | Node 24 Linux package consumer | shape-only | Required `artifact-consumer` CI |
31
+ | Node 24 macOS package consumer (GitHub Actions runner, not a physical Mac) | shape-only · CERTIFIED (CI) | Required `macos-install-surface` CI run 34303884286 @ `70eda03` (`macos-latest`, 65 s, 10/10 steps): pack → clean consumer install → bin links → `entwurf --help` → `check-bridge` (7 tools) → harness-absent `setup` (all SKIP, computed green, zero no-write-axis writes, `auth.json` byte-identical). Certifies no harness rail. |
26
32
  | Claude Code >=2.1.217 exec form | **certified** — supported floor | Topology + floor gates, doctor oracle; B2 live NixOS session |
27
33
  | Claude Code 2.1.138 | **unsupported** | Launcher refuses empty argv; no shell-form fallback |
28
34
  | Maintainer NixOS installed package | **certified** for `0.12.8-repair.1` | 2026-07-25 registry install → doctor exit 0 (HISTORY) |
29
35
  | Secondary Ubuntu installed package | **certified** for `0.12.8-repair.1` | 2026-07-25 same artifact, isolated agent dir → doctor exit 0 (HISTORY) |
30
- | macOS Claude meta-bridge | unverified | Installer refuses Darwin; no `/proc` live join |
31
- | WSL2 / Windows | unverified | None |
36
+ | macOS Claude meta-bridge | NOT CERTIFIED — pending physical host | No physical-Mac doctor yet; a CI runner has no Claude login. Physical-host rental remains unscheduled; no cut has carried it. |
37
+ | Darwin install fence (meta/copilot/omp-bridge + omp-receive) | portable (Linux or Darwin) | Four installers accept Darwin; python3/node/harness presence holds the seat, not the platform name. A Darwin install is not a rail receipt. |
38
+ | Doctor fail-closed on unreadable environ | Linux measured; Darwin unmeasured | Copilot launch-flag and omp identity-carrier: missing `/proc/<pid>/environ` as predicate INPUT is `UNVERIFIABLE` (non-green), not a benign note. omp distinguishes `pgrep` exit 1 (absent) from exit 2 (enumeration failed); copilot distinguishes ENOENT/ESRCH (gone) from other errno (read denied). |
39
+ | setup on uncertified platform with harness present | named non-green | All 13 harness units ask `harness_rail_certified_platform()`; uncertified → FAIL whose wording is not "install failed". Harness-absent Darwin setup (the CI cell) can still be computed green. |
40
+ | macOS physical-host probe | shipped, unrun on a Mac | `scripts/raw-macos-measure/` — `/bin/sh`, cells M1–M9, 3.5 s; needs neither entwurf install nor login. Still unmeasured: Darwin foreign-process environ (`ps -Eww`) and whether macOS `python3` is a CommandLineTools stub. |
41
+ | WSL2 | unverified | Counts as Linux evidence, never a Windows surface (AGENTS Hard Rule 17) |
42
+ | native Windows | UNSUPPORTED | Locked pending GLG; a scope decision, not an evidence gap |
32
43
 
33
44
  Notes the table cannot carry without becoming prose again:
34
45
 
@@ -36,13 +47,20 @@ Notes the table cannot carry without becoming prose again:
36
47
  candidate `.tgz`, checkout-invisible non-root global install, PATH shims, a frozen
37
48
  package root, the path+sha256 regular-file fence, and a strict doctor fixture. Its
38
49
  planted Claude cache/owner/bridge are synthetic — no real Claude lifecycle runs there.
50
+ - **A CI runner is not a physical host.** The macOS consumer row is `macos-latest`
51
+ — an image, not the NixOS/Ubuntu "host" of the certified rows. Its green covers
52
+ the Entwurf-only install surface (pack, install, bins, `--help`, `check-bridge`,
53
+ harness-absent `setup`); it cannot log into a harness, so no rail, marker join,
54
+ ACP turn, or mux lifecycle is certified by it.
39
55
  - **Both certified hosts are certified for the published repair artifact only**, including
40
56
  physical `entwurf_v2` delivery and the live owner join. Stable cuts earn their own host
41
57
  proof; hand-patched hooks and `plugin validate` output are never acceptance.
42
58
  - **The maintainer host first went doctor-RED** on a managed dev-bin shadowing the registry
43
59
  bridge, and now runs dev wiring again.
44
- - **macOS is not permanently excluded.** Future native validation may reopen it, and the
45
- package-level `os` field stays unrestricted.
60
+ - **macOS evidence is split, not a support claim.** The Entwurf-only install surface is
61
+ CERTIFIED (CI) (row above); the harness rails are NOT CERTIFIED — pending
62
+ physical host (rental remains unscheduled). The package-level `os` field stays
63
+ unrestricted, and native Windows is UNSUPPORTED.
46
64
 
47
65
  **Operator acceptance rule:** on a claimed Claude host, reinstall from the released
48
66
  artifact, restart every old Claude process, open a new session, and run the doctor
package/CHANGELOG.md CHANGED
@@ -4,6 +4,267 @@ All notable changes to this project will be documented here. Format follows [Kee
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 0.20.1 - 2026-09-10
8
+
9
+ ### Fixed
10
+
11
+ - **A second checkout of this repo no longer hijacks `--entwurf-control` from the installed one (#110).** The tracked `.pi/settings.json` shipped `packages: [".."]`, which registers "whichever checkout pi is running in"; `run.sh install` separately registers this root in user scope by absolute path. pi's package identity is `local:<resolved path>` and its dedupe collapses only EQUAL identities, so in the installed checkout the two entries are one package — and in any OTHER checkout (worktree, release scratch) they are two. Both then load `pi-extensions/entwurf-control.ts`, the loader awards `--entwurf-control` and `--emacs-agent-socket` to whoever registered first (project scope, i.e. the branch copy), and the INSTALLED extension fails to load with an error naming the installed path as the loser. Not silent — designed to be misread, while all seven `entwurf` model rows still printed and the pi session looked healthy. entwurf's own checkout now carries no project-scope self-registration in tracked bytes, and `install_local_package` skips writing one when the project it is installing into IS this root; a foreign project is unaffected, which is what `entwurf install <project>` is for. No realpath rule could have fixed this: two checkouts are genuinely two directories.
12
+ - **The gates moved with the contract.** `check-install-surface` S7c stops pinning the SHAPE (`packages == [".."]`) and pins the invariant it was there for — no host-absolute path in the tracked, biome-governed file — which "no packages key" satisfies strictly more. `smoke-user-scope-citizen` cells 11/11b–11e and 14 no longer seed themselves from this repo's own committed bytes: a witness that depends on us shipping the shape it tests dies with the shape, while the predicate it guards (`is_settings_relative_self`, still the thing that keeps an unrelated relative package like `../../repos/gh/andenken` from reading as ours) does not. Cell 14 now demands the stronger end-to-end property — installing into entwurf's own checkout writes no packages entry and leaves the file byte-identical — and new cell 15 spawns the pinned devDep pi against two real stand-in checkouts, planting the retired registration first and requiring the collision to APPEAR before asserting it is gone, so a sandbox that silently loads nothing cannot report a green. Both halves are replant-qualified (`[QK:SECOND-CHECKOUT-EXTENSION-COLLISION]`, `[QK:SELF-CHECKOUT-PROJECT-REGISTRATION]`). That new probe also taught the gate a fence it did not have: `pi --print` reads a non-tty stdin as piped prompt input, so the probe inherited the caller's stdin and hung for 1h56m under a supervised runner while passing instantly under every shell it had been tried in. It now reads `/dev/null` under a bounded `timeout`, so a probe that cannot finish goes red with its own name instead of hanging.
13
+
14
+ ### Changed
15
+
16
+ - **The bundled Claude ACP adapter moves to `@agentclientprotocol/claude-agent-acp 0.76.0`.** One refactor plus one opt-in extension; nothing reaches our surface. Declared runtime deps are unchanged (`@agentclientprotocol/sdk 1.4.0`, `@anthropic-ai/claude-agent-sdk 0.3.257`), so the anthropic peer floor does not move either. The one feature — upstream #1111's `recommendedValue` on AIR config options — is enabled only when `initialize` advertises it, and entwurf sends `clientCapabilities: {}`; the label normalization, `default` row and effort selector riding it are out of reach for a second, independent reason as well (`configOptions` has zero call sites here, and `EFFORT_CONFIG_ID` is never set). `setSessionConfigOption`, our only model-forcing wire call, is byte-identical across the bump. One change has a shape that WOULD reach us and does not: a string-path `configuredSettings` now does its `readFile`/`JSON.parse` on the unconditional `session/new` path, and entwurf passes `settings` as an object — so "entwurf never hands the adapter a settings PATH" is now stated as a contract at that call site rather than left as an accident. #96's readiness argument was re-measured rather than inherited: `mcpServerStatus` still has exactly two call sites, re-read at the new upstream coordinates, both still an auth handshake and not a readiness fence.
17
+ - **Documentation matched to shipped behavior.** A repo-wide stale-prose sweep corrected live claims that had drifted: OMP described as three installer units when it ships four, a fresh-cut quiescence instruction that named four of six identity writers, a gate docstring promising byte-pinning that its gate never performed, README speaking of macOS as arriving rather than shipped in 0.20.0, and ROADMAP carrying #72 as an open failure after it was closed as an external-reaper cause. Historical records — the dep-bump ledger, measurement receipts, archived handoffs, and every occurrence that names a retired symbol AS retired — were deliberately left intact.
18
+
19
+ ### Upgrade note
20
+
21
+ **Nothing to run.** A host that has already registered entwurf in user scope (any host that ran `./run.sh setup` or installed the npm package) is unaffected: that entry is what loaded the extensions from a foreign cwd all along, and it is untouched. What changes is a checkout that has NEVER been set up — it used to pick up the extensions from the tracked `packages: [".."]` and now does not. That is a narrower loss than it reads: the same tracked file names `entwurf-bridge` as a bare bin, which is only on PATH after setup, so a never-set-up clone never had the MCP hand either. Setup was already the prerequisite; this makes it the only one. To run a branch checkout's extensions deliberately, use `pi -ne -e pi-extensions/entwurf-control.ts`, or move ownership explicitly with `./run.sh takeover-user-scope`.
22
+
23
+ ### Verification
24
+
25
+ Lane receipts (oracle, 2026-09-10) plus prepare P4/P5 on this host.
26
+
27
+ - **`pnpm run check:full`** exit 0, 479 s (inherited, omp handoff on this tree) / again as the release-gate static MUST step (this run).
28
+ - **Prepare P5** `LIVE=1 ./run.sh release-gate --cut`:
29
+ scratch `/tmp/entwurf-release-gate-0.20.1.uuZCm8`, pane exit 0, **MUST: PASS=23 FAIL=0 SKIP=0**, BEHAVIOR: PASS=1 FAIL=0 SKIP=0, `cut: OK`. Qualification **393/393 KILLED**, origin HEAD `d72225e`, purity held. stdin closed (`exec </dev/null`); PWD in scratch; `PI_SESSION_ID` / `PI_AGENT_ID` / `CLAUDE_CONFIG_DIR` unset. Wall ~55 m (16:49:37→17:44:48 KST).
30
+ - **P9 at verdict:** prefix-blind census top was 28 `entwurf-chain` roots (Sep 2–4; not an owned `mkdtempSync` prefix, so not in the delete set). 5 reparented `/tmp` processes: 4 `entwurf-copilot-receive` Node stubs (ppid=1, ages 6h54m / 6h09m / 5h18m / 29m) named and not killed, plus the operator emacs daemon. Reclaimed 4 owned `check-probe-ordering-*` roots (64 K). Scratch preserved.
31
+
32
+ ## 0.20.0 - 2026-09-09
33
+
34
+ This range carries three lanes that are not one cause: the macOS
35
+ install-surface axis (#78), `claude-fable-5-1` on the curated ACP surface,
36
+ and the Cortex `realHome` path-flavor fix (PR #77). GLG (2026-09-09)
37
+ deferred the physical-Mac loan: this cut is honest about a **CERTIFIED (CI)**
38
+ install surface and does not pretend the harness rails are certified.
39
+
40
+ **macOS in this release.** This release adds macOS support for Entwurf's
41
+ install surface: the npm package installs, `entwurf --help` and `entwurf
42
+ check-bridge` boot, and `entwurf setup` composes the integrations it finds.
43
+ The four installers that previously rejected Darwin by platform name no longer
44
+ do. We validated that surface only in macOS CI because the developers did not
45
+ have a physical Mac. So macOS native-harness rails—marker join, an ACP turn,
46
+ and mux included—are not yet certified; setup writes their wiring but reports
47
+ them non-green. Linux's certified rail status is unchanged.
48
+
49
+ ### Upgrade note — this one asks something of the operator
50
+
51
+ **On Darwin, `entwurf setup` no longer prints `result: green` when a harness
52
+ is already present.** Opening the four Darwin install fences (below) had a
53
+ side-effect the command an operator actually types did not stay honest for:
54
+ the installer that used to refuse on a platform name now writes the wiring,
55
+ and setup graded that `PASS` / `result: green` — while every harness doctor
56
+ still refuses on Darwin, and setup runs no doctor. That is cosmetic success
57
+ against Hard Rule 17 ("a detected-but-incomplete integration is named
58
+ non-green rather than cosmetic success").
59
+
60
+ All 13 harness units now ask one predicate
61
+ (`harness_rail_certified_platform`). On an uncertified platform they emit a
62
+ named non-green (FAIL) whose wording is lexically disjoint from an install
63
+ failure — `entwurf setup` says so by name:
64
+
65
+ `<unit> FAIL — the wiring WAS written and nothing failed to install, but this
66
+ harness rail is NOT CERTIFIED — pending physical host on <uname>. setup runs
67
+ no doctor; the rail axis is owned by '<doctor>'. Tracking: #78.`
68
+
69
+ `bins` and `core` are not rail axes and still PASS. A Darwin host with no
70
+ harness still five-SKIPs and `result: green` — that is the path
71
+ `macos-install-surface` asserts, and it did not move. Linux is unchanged: a
72
+ completed rail install is still PASS.
73
+ `[QK:SETUP-DARWIN-RAIL-COSMETIC-PASS]` plants the restoration of "a completed
74
+ install always PASSes" and was KILLED at that signature.
75
+
76
+ The four installers that used to refuse Darwin (`entwurf install-meta-bridge`,
77
+ `install-copilot-bridge`, `install-omp-bridge`, `install-omp-receive`) now
78
+ run; their own refusal is the toolchain check (python3 / node / harness
79
+ presence), never the platform name. A green Darwin install certifies the
80
+ install surface only, never a harness rail.
81
+
82
+ Hosts with no detected harness, and every Linux host, see no new prompt.
83
+
84
+ ### Added
85
+
86
+ - **`claude-fable-5-1` joins the curated ACP model surface.** The `entwurf` provider now registers
87
+ three unprefixed Claude ids (`claude-opus-5`, `claude-sonnet-5`, `claude-fable-5-1`) beside the
88
+ four `cortex-` rows. Routing needed no change: `backend-adapter.ts` derives `SUPPORTED_CLAUDE_IDS`
89
+ from the same constant, and `curatedClaudeModels()` filters the pi-ai registry — which carries
90
+ `claude-fable-5-1` (measured: `getModels("anthropic")` lists it beside `claude-fable-5`). The
91
+ curation comment's "verify across both axes" commitment is met by a live citizen: GLG opened B on
92
+ `entwurf/claude-fable-5-1` (garden `20260908T181437-30802a`, `backend=pi`, `liveness=alive`) and a
93
+ fire-and-forget send to it went out on the control-socket rail.
94
+ - **Two exact-set pins moved with the surface, and one of them was the real gap.**
95
+ `check-acp-provider-surface` counts the curated constants (6 → 7) and
96
+ `check-pack-install` enumerates the installed provider's own model list — that second one is a
97
+ package gate in `check:full`/`prepublishOnly` and would have gone red on the next release with a
98
+ drift message rather than a missing-support message. `check-acp-cortex` asks presence
99
+ (`includes`), not an exact set, so it needed nothing.
100
+ - **`[QK:CLAUDE-CURATED-THREE-ROWS]` — the claude curated list gets its own oracle.** The cortex list
101
+ has had `CORTEX-CURATED-FOUR-ROWS` since 0.13.0; the claude list had none, and it could not borrow
102
+ the exact-set comparison beside it: `expectedIds` derives from the same constant, so a dropped
103
+ claude row moves expected and captured together and that assertion stays green. The independent
104
+ count is what catches it, so the count now carries the claim and a committed mutant plants the
105
+ real defect (dropping `claude-fable-5-1` from `SUPPORTED_ANTHROPIC_MODEL_IDS`). Lane inventory
106
+ `acp-cortex` 12 → 13; total 386 → 387.
107
+
108
+ - **macOS gets its own evidence axis — an install surface, not a support claim.**
109
+ The `macos-install-surface` CI job (GitHub Actions `macos-latest`) was promoted
110
+ from non-voting to required after its first green at commit `70eda03`
111
+ (run 34303884286, 65 s, 10/10 steps): npm pack → checkout-invisible clean
112
+ consumer install → bin links → `entwurf --help` → `check-bridge` (direct MCP
113
+ smoke, 246 ms, the seven tools + `test.sh`) → harness-absent `setup` (pi/Claude/agy/Copilot/
114
+ OMP all SKIP, bins/core PASS, computed verdict green, zero writes on the five
115
+ no-write axes and `auth.json` byte-identical). Platform vocabulary across
116
+ README/BASELINE now separates three states: the Entwurf-only install surface on
117
+ macOS is **CERTIFIED (CI)** — a CI runner, never a physical host; the harness
118
+ rails (Claude/Copilot/OMP/agy wiring, marker join, a real ACP turn, mux) are
119
+ **NOT CERTIFIED — pending physical host**; native Windows is **UNSUPPORTED**.
120
+ - **`scripts/raw-macos-measure/` — the borrowed-Mac measurement ledger.** `probe.sh`
121
+ (`/bin/sh`, cells M1–M9) measures one host and writes nothing: no install, no
122
+ record, no state. The README maps each cell to the surface it measures and keeps a
123
+ `[host-linux]` control run so the probe itself is never the thing under test. Opened
124
+ by the promotion above; it is where a borrowed Mac produces the physical-host
125
+ evidence the CI runner cannot.
126
+ - **`scripts/raw-codex-measure/` — Codex 0.153.4 vendor measurement (#95 step 1).**
127
+ Nothing is installed: no birth hook, no record, no marker, no doctor, and
128
+ `entwurf_fresh_call` still cannot open a codex sibling. GLG reversed the
129
+ 2026-08-01 decline on 2026-09-08; live claims that still said otherwise
130
+ were repaired. The ledger closes step 1: SessionStart fires on the first
131
+ turn, not at window open; a fourth callback spelling
132
+ (`mcp__entwurf_bridge__entwurf_v2`); the delivery-capable app-server join
133
+ cannot use parent-pid as a citizen key; hook-trust is an ownership cost
134
+ (`/etc/codex`, root-owned), not a capability gap; clause 4 is
135
+ `thread/name/set` plus a status_line config. Step 2 is not opened.
136
+ `PIN_CODEX_MINOR` 0.144 → 0.153 is a re-verification of the archived drift
137
+ probe, not a product pin.
138
+
139
+ ### Changed
140
+
141
+ - **`[QK:CORTEX-PROVIDER-SIX-ROW-SURFACE]` → `[QK:CORTEX-PROVIDER-EXACT-ROW-SURFACE]`.** The claim's
142
+ substance is "the compiled entry registers the EXACT union of every adapter's curated rows", which
143
+ is count-independent; the old id carried a row count that this change made false and that any
144
+ future curation change would falsify again. Gate assertion, mutant manifest (`claim` +
145
+ `signature`), the `acp-provider.ts` comment and the `run.sh` usage line moved together. The
146
+ mutant's `find`/`replace` are untouched — it still plants the same defect (filtering the cortex
147
+ rows out of the real entry).
148
+ - **Four install fences now accept `Linux | Darwin`** — `meta-bridge-install.sh`,
149
+ `copilot-bridge-install.sh`, `omp-bridge-install.sh`, `omp-receive-install.sh`
150
+ (the `meta-bridge-uninstall.sh` precedent). The installers' own refusal is the
151
+ toolchain check (python3/node/harness presence), never the platform name. A green
152
+ Darwin install certifies the install surface only, never a harness rail —
153
+ installation portability and rail certification are separate axes (Hard Rule 17).
154
+ - **Doctor python `start_key`/`parent` now mint the same two-tier key the TS core
155
+ mints** (`/proc` stat field 22 → `linux:<ticks>`, else `ps -o lstart=` /
156
+ `ps -o ppid=`, the same argv vectors `meta-session.ts` uses). Darwin's remaining
157
+ doctor reason is per-process environment DISCOVERY, not start-key.
158
+ The `ps:` format is unchanged: 1-second resolution against `linux:` at
159
+ 10 ms, but a same-second pid reuse needs a full PID_MAX wrap inside one
160
+ second (≈99,900 spawn/s vs a measured pathological ceiling of 14,873/s).
161
+ Coarsening can mint a false `live` and never a false `dead`. The migration
162
+ axis is empty — 1,248 marker files, all `linux:`, zero `ps:`. Widening the
163
+ key with argv was measured insufficient (two same-argv children share it,
164
+ and same-argv restart is the normal harness shape).
165
+ - **Operator-surface portability substitutions (audit P1).** `grep -P` negative
166
+ lookaheads moved to awk (6 sites); `sha256sum` moved to the in-repo python3
167
+ hashlib helper (3 sites) — not a second `shasum` convention. `readlink -f` on the
168
+ operator path (`agy-imprint.sh`, `copilot-launch.sh`) replaced with the POSIX
169
+ symlink walk `mcp/entwurf-bridge/start.sh` already ships, and `agy-bridge.sh`
170
+ advice no longer prints a GNU-only command.
171
+
172
+ ### Fixed
173
+
174
+ - **The Cortex overlay's `realHome` guard now states path-flavor absoluteness, not the host it runs
175
+ on.** `ensureCortexDualHomeOverlay` refused a non-absolute `realHome` with `startsWith("/")`, so a
176
+ drive/UNC path (`C:\Users\x`, `\\server\share`) read as relative. The guard is now
177
+ `isAbsolute(realHome) || win32.isAbsolute(realHome)`; the error text and the D10 contract sentence
178
+ are unchanged. First evidence: PR #77 (@yizixu) found the defect. The flavor-explicit form is what
179
+ makes it killable on a Linux host — `check-acp-cortex` gained two positive cells (drive and UNC,
180
+ `realSnowflakeHome` pinned under tmp so only the guard is exercised) plus an empty-string negative
181
+ cell, and mutant `CORTEX-REALHOME-PLATFORM-NEUTRAL` replants `startsWith("/")` and dies at
182
+ `[QK:CORTEX-REALHOME-PLATFORM-NEUTRAL]`. This claims no native-Windows support; the certified
183
+ platform axis is unchanged.
184
+ - **Doctors no longer call an unmeasured host clean.** When `/proc` is absent, the
185
+ Copilot launch-flag and OMP identity-carrier doctors used to fail OPEN — a `note`
186
+ and a green verdict. They now emit `UNVERIFIABLE` through the existing `bad` path:
187
+ no new severity, emitter, or exit code. The same contract is pinned one step
188
+ further — absent and unknowable are no longer collapsed: a failed enumeration
189
+ (`pgrep` rc 2) or an unreadable `cmdline` is UNKNOWN and stays non-green, while a
190
+ genuine no-match or a vanished process remains the benign note it always was.
191
+ That distinction is bidirectional and holds on Linux too, where `/proc`'s presence
192
+ had merely hidden the defect.
193
+ - **Recovered-host selectors moved from GNU BRE alternation to ERE**
194
+ (`omp-bridge-doctor.sh`, `copilot-bridge-doctor.sh`), so BSD grep cannot report a
195
+ recovered host as unrecovered (a false RED that only Darwin's grep would mint).
196
+ - **A red or interrupted gate no longer leaves its children and temp roots
197
+ behind.** Teardown written as the last line ran only on the green path;
198
+ gates throw to go red, and `^C`/SIGTERM runs no statement. Measured on
199
+ oracle: 360 reparented Node receivers from the copilot-receive stub and
200
+ ~9,200 stale temp roots (3.8 G), one of them minted by a `pnpm check` that
201
+ had just reported clean. Teardown now belongs to `process.on("exit")` plus
202
+ the three signals (`scripts/lib/reclaim-on-exit.ts`); the SDK stub gains
203
+ the parent-death watchdog production already has.
204
+ `[QK:COPILOT-RECEIVE-STUB-CHILD-DIES-WITH-PARENT]` carries the watchdog.
205
+ The release skill gains P9 (prefix-blind census; delete only prefixes this
206
+ checkout's `mkdtempSync` calls mint), run at the release-gate verdict, at
207
+ the end of prepare, and after the publish smoke.
208
+
209
+ ### Verification
210
+
211
+ Lane receipts (oracle, 2026-09-09) plus prepare P4/P5 on this host. There is
212
+ **no** physical-Mac receipt.
213
+
214
+ - **`pnpm run check:full`** exit 0, 461 s (`9cc5b09`) / 469 s (`e09b84e`) /
215
+ 465 s (prepare P4, this worktree).
216
+ - **Prepare P5** `LIVE=1 ./run.sh release-gate --cut`:
217
+ scratch `/tmp/entwurf-release-gate-0.20.0.rerun`, **MUST PASS=23 FAIL=0
218
+ SKIP=0**, BEHAVIOR PASS=1, `cut: OK`. Qualification **391/391 KILLED**
219
+ inside that gate. The first attempt was `cut: BLOCKED` (MUST FAIL=2):
220
+ host `STALE writer` on the deployed omp-receive unit, and the omp-fresh
221
+ nonce callback captured by the preparing session's `PI_SESSION_ID`.
222
+ Rerun after `install-meta-bridge` / `install-omp-bridge` /
223
+ `install-omp-receive` and `env -u PI_SESSION_ID -u PI_AGENT_ID -u
224
+ CLAUDE_CONFIG_DIR`.
225
+ - **`check-gate-qualification` 390/390 → 391/391 KILLED** (`9cc5b09` then
226
+ `e09b84e`). New claims this range: `CLAUDE-CURATED-THREE-ROWS`,
227
+ `CORTEX-REALHOME-PLATFORM-NEUTRAL`,
228
+ `COPILOT-RECEIVE-STUB-CHILD-DIES-WITH-PARENT`,
229
+ `OMP-DOCTOR-UNENUMERATED-IS-UNVERIFIABLE`,
230
+ `COPILOT-RECEIVE-DOCTOR-UNREADABLE-ARGV-IS-UNVERIFIABLE`,
231
+ `SETUP-DARWIN-RAIL-COSMETIC-PASS`.
232
+ - **`check-pack-install`** exit 0. **`check-install-container`** exit 0.
233
+ - The macos-install-surface cell replayed against the packed candidate on
234
+ Linux: all 12 pins HOLD (`9cc5b09`).
235
+ - Doctors (4 files) HEAD-vs-worktree output IDENTICAL (`9cc5b09`).
236
+ - CI run
237
+ [`34303884286`](https://github.com/junghan0611/entwurf/actions/runs/34303884286)
238
+ @ `70eda03` — `macos-install-surface` required, `macos-latest`, 65 s,
239
+ steps 10/10.
240
+ - CI run
241
+ [`34316688064`](https://github.com/junghan0611/entwurf/actions/runs/34316688064)
242
+ @ `9cc5b09` — 4 jobs + qualification step `success`.
243
+ - Land exact-SHA CI run
244
+ [`34329931791`](https://github.com/junghan0611/entwurf/actions/runs/34329931791)
245
+ @ `a5a6dfe`, `event=workflow_dispatch` — 4 jobs + qualification body
246
+ `success` (the push run at this SHA skipped the body; L3 dispatch recovered it).
247
+
248
+ ### Notes
249
+
250
+ - **What this cut claims about macOS, and what it does not.** Claimed: the
251
+ Entwurf-only install surface is **CERTIFIED (CI)**; the shipped operator
252
+ path is portable (no `readlink -f`, no `grep -P`, no `sha256sum`, no GNU BRE
253
+ alternation on recovered-host selectors); doctors fail closed on an
254
+ uncertified discovery axis (`UNVERIFIABLE`, not a green note). Not claimed:
255
+ harness rails, marker join, a real ACP turn, mux, or a physical-host doctor
256
+ green — all **NOT CERTIFIED — pending physical host**. native Windows is
257
+ **UNSUPPORTED**. A CI `macos-latest` runner has no logged-in harness; it
258
+ cannot mint the stronger `certified`. macOS is never UNSUPPORTED.
259
+ - **Two cells are unmeasured, by name.** Whether Darwin can read another
260
+ process's environ (`ps -Eww`), and whether macOS `python3` is the
261
+ CommandLineTools stub. Both are cells the borrowed-Mac probe
262
+ (`scripts/raw-macos-measure/probe.sh`, M6 and M3) is built to answer. Do
263
+ not fill them from a CI image spec.
264
+ - **Codex is not a garden backend in this cut.** Step 1 of #95 closed as
265
+ measurement; step 2 is not opened. `entwurf_fresh_call` still cannot open a
266
+ codex sibling.
267
+
7
268
  ## 0.19.0 - 2026-09-07
8
269
 
9
270
  One lane: #105 slice ①, the project seat. `entwurf_fresh_call` gains an optional
package/DELIVERY.md CHANGED
@@ -80,13 +80,16 @@ axis; neither word is an admission grade or a synonym of *supported*. A pre-cont
80
80
  can be owned without being supported; a supported harness still has to walk
81
81
  [`docs/adding-a-harness.md`](./docs/adding-a-harness.md) step 9.
82
82
 
83
+ Every row in this table is Linux evidence. macOS rails are NOT CERTIFIED — pending
84
+ physical host. Do not read a D-level cell as a Darwin receipt.
85
+
83
86
  | Harness / surface | Product status | Capability | Transport and boundary |
84
87
  |---|---|---|---|
85
88
  | **pi native Entwurf** | shipped | D7; D8 partial | Record-addressed Unix control socket. A record-less socket is diagnostic only and never dispatched. |
86
89
  | **Claude Code interactive `>=2.1.217`** | shipped; Linux certified | D6; D7/D8 partial | Per-session mailbox + exec-form `FileChanged`/`asyncRewake`. B2 proved idle wake and same-session continuity on one NixOS host. |
87
90
  | **Antigravity / agy** | shipped | D6; D7 partial | Record-backed native-push through LS gRPC `agentapi send-message`; no mailbox or receiver marker. Admitted before the #82 step 9 contract and not re-evaluated under it, so it is legacy citizen evidence, not a step-9 supported harness: `entwurf_fresh_call` cannot open an agy sibling, and nothing here should be read as claiming visible lifecycle parity. |
88
- | **Codex app-server-backed TUI** | verified probe | D7; D8 unproven | WebSocket-over-UDS `turn/start` into a live `threadId`; status events expose completion. No owned native-citizen install/invocation lane. |
89
- | **Codex embedded TUI** | deferred | D0 partial | No supported receive socket/hook on the measured standalone shape. |
91
+ | **Codex app-server-backed TUI** | verified probe, re-verified at 0.153.4 | D7; D8 unproven | WebSocket-over-UDS `turn/start` into a live `threadId`; status events expose completion. Re-run as shipped on 2026-09-08 at codex-cli **0.153.4** (garden lane #95): idle plain `codex` auto-attached to a bare `codex app-server --listen`, woke with zero typing, and the model replied in the same visible session — `{"ok": true, "threadId": "01a08145-ce6d-77d0-b7a4-cfb57c608282", "turnId": "01a08146-7c06-7b83-9e65-32e3814ee490", "status_seen": ["active","active","idle"]}`. No owned native-citizen install/invocation lane. Step 1 measurement only — the lane is open (#95) but nothing is installed, so this row stays the declared pre-#82 exception `check-harness-admission-parity` reads. |
92
+ | **Codex embedded TUI** | deferred | D0 partial | No supported receive socket/hook on the measured standalone shape. Re-checked in source at 0.153.4: the hook vocabulary grew to 12 events but still has no `watchPaths`/`FileChanged`/`asyncRewake` analogue, so the negative stands. |
90
93
  | **Copilot CLI first-party extension** | raw transport probe; superseded by the owned product unit | D7 path observed; D3 control receipt incomplete; D8 unproven | CLI-spawned extension over stdio JSON-RPC; `joinSession()` + documented `fs.watch` → `session.send({mode:"enqueue"})`. Idle wake, exact-marker reply, and completion passed on 2026-08-23 (CLI 1.0.80, L4, one Linux host). Two-process isolation was observed but its decisive B log was not preserved. Kept as the transport receipt the owned receive unit was built on; the shipped unit differs deliberately — it announces the inbox instead of injecting the body. |
91
94
  | **Copilot CLI garden citizen** | shipped in 0.15.0; send + receive + visible fresh accepted on one host | D6; D7 partial; D3 pending; D8 unproven | Birth, garden id, MCP hand and record-backed sender identity are accepted; the RECEIVER is an installed first-party extension that binds to the V3 record, writes a receiver marker owned by the WATCHER pid, and rings a doorbell the model drains with `entwurf_inbox_read`. `wakeMode` is `self-fetch`, so dispatch reaches the mailbox rail: armed → delivered, unarmed/stale → the honest `mailbox-undeliverable` refusal. **D6 is the owned-invocation LIVE acceptance of 2026-08-23** — garden `20260823T181316-d9f6ba`, native `20fe30c8-b2bc-4600-91a0-8a409131be51`, CLI 1.0.80: receive log `joined`→`armed`→`doorbell fresh=1`→`rang`, mailbox `lastEnqueuedAt 09:23:41.235Z` / `lastReadAt 09:23:56.480Z`, and a model reply on the same record/native/gid chain. **Visible fresh (step 9 clause 7) is a separate LIVE, 2026-08-25** — launch window `@89`/`%89` nonce `mux-fresh-call-690529ae99f99faa2252aefb`; exact-callback garden `20260825T085721-f68be0`; one `entwurf_v2` → `meta-mailbox → enqueued`; same garden `lastReadAt 2026-08-24T23:57:47.784Z` plus same-gid reply; GLG saw footer garden id and a healthy multi-turn window. Those rows stay unmerged. D7 is PARTIAL: reply and read receipt were observed, the completion taxonomy and long-haul operation were not. D3 (second-session isolation of an owned invocation) is PENDING — observed once, decisive log lost to scratch cleanup. Evidence level L4: one host. Launch through the owned invocation `entwurf copilot`, which sets `COPILOT_CLI_ENABLED_FEATURE_FLAGS=EXTENSIONS` for that one process; `doctor-copilot-receive` reads live CLI environments because a session launched without it is silently inert. Visible fresh is operator-metered and is not a release-gate MUST. |
92
95
  | **OMP (`omp`) garden citizen** | shipped in 0.16.0; send + receive + visible fresh accepted on one host | D6; D3 proven; D7 partial; D8 partial | Two in-process extensions in the operator's own TUI: birth mints the `mode === "tui"` host (subagents mint nothing) and names it as sender; the RECEIVER unit joins that citizen in the SAME process, holds an `fs.watch` on its mailbox signal, and rings an announce-only doorbell through the vendor's `pi.sendUserMessage` — measured to be on the FACTORY object, not the event ctx, and measured to start a turn on an idle host with zero typing (`agent_start` +31ms). `wakeMode` is `self-fetch`: the model drains with `entwurf_inbox_read` and THAT read is the receipt. **D6 is the LIVE acceptance of 2026-08-30** (oracle, omp 18.0.0) — garden `20260830T140819-116f6a`, `lastEnqueuedAt 05:08:20.555Z` / `lastReadAt 05:08:23.958Z`, and the citizen's own transcript carrying `mcp__entwurf_bridge_entwurf_inbox_read` for its own garden id. **D3 is PROVEN, not pending**: with two live omp citizens armed, one addressed enqueue rang exactly one doorbell and the sibling persisted no transcript and kept an empty mailbox. D7 is PARTIAL (`lastReadAt` needs no scraping; the reply does). D8 is PARTIAL — dedupe and every stale-handling path are implemented and hermetically pinned, ordering/loop-guard/crash-recovery are not. The `/new` unarm is the rail-specific one: the watch lives in the operator's TUI, so pid + start-key cannot see a citizen change underneath a living process, and without an explicit unarm the previous garden id would keep reading deliverable. Requires `tools: xdev: false` in `~/.omp/agent/config.yml` — the vendor default hides MCP tool schemas from the prompt, so the doorbell would name a tool the model cannot call. The decisive receipt lines — the ordering probe, the D6 chain, the D3 isolation and the `/new` unarm — are pasted into `scripts/raw-omp-measure/README.md` §M7 rather than left in a host-local `/tmp` path. Evidence level L4: one Linux host, ARM. **Visible fresh (step 9) is ACCEPTED — the clause 7 LIVE went green on 2026-08-30:** `entwurf_fresh_call` opens omp on all three public surfaces through the bare `omp` runtime with an explicit `--approval-mode yolo` width and the `mcp__entwurf_bridge_entwurf_v` callback name, behind a five-axis pre-mutation preflight whose fifth axis is omp-specific (`tools.xdev !== true`, without which the model cannot call the callback tool at all). **The first turn is a TWO-STAGE BOOTSTRAP rather than a positional prompt, and that is a measured correction, not a preference.** `[LIVE 2026-08-30]` the positional-prompt candidate opened its window and minted its citizen (garden `20260830T181342-452167`, native `01a051f2-3107-7147-8806-fa2a6f527610`), delivered the byte-identical framing as a user message at `09:13:42.413Z`, and the model answered the literal text `ACK` at `09:13:47.105Z` with ZERO tool calls; the caller timed out at 240s. `[source]` the interactive UI defers MCP discovery (`sdk.ts:1847-1855`, `:1881-1905`) while the positional `initialMessage` prompts straight after `mode.init()` (`main.ts:540-565`), and `[측정]` an observer on the same runtime saw `turn_start` at +654ms with the entwurf tools absent and the callback tool present only at +1484ms. So the launcher now carries `{v,target,nonce,task}` on the fixed registered flag `--entwurf-bootstrap`, and the in-process birth extension polls `getAllTools`(`source:"mcp"`) AND `getActiveTools` for the exact callback name, sends a callback-ONLY prompt, arms the task only on a `tool_result` whose stored `toolCallId`, tool name, target, nonce and `isError === false` all match, and DELIVERS it at the next `turn_end` of that same session. `[LIVE 2026-08-30]` that last boundary is itself a measured correction: a first attempt sent the task from inside the `tool_result` handler with an explicit `deliverAs: "followUp"`, the hook log showed the full chain (`bootstrap-armed` → `bootstrap-ready` +440ms → `bootstrap-callback-observed` → `bootstrap-released`), and the task still never appeared in the session — `[source]` an explicit `deliverAs` queues without starting a turn in either state, while the omitted form starts one when idle (`agent-session.ts:6511-6513`), which the same transcript confirmed three seconds later when the Bundle B doorbell's omitted-option send landed and started a turn. `[측정 2026-08-30]` the callback-only half is what was proven to work: model `openai-codex/gpt-5.6-sol`, tool live at +1105ms, prompt injected at +1107ms, and the sibling calling `mcp__entwurf_bridge_entwurf_v` with the exact nonce (`omp-cb-btkvva4r87` → `20260830T184054-1aa1f2`, `meta-mailbox → enqueued`). **The clause 7 acceptance, `smoke-omp-fresh-live` (release-gate MUST), 2026-08-30, 21 assertions, omp 18.0.0 / `openai-codex/gpt-5.6-sol`:** launch through `tools/call entwurf_fresh_call` with nonce `mux-fresh-call-34faea228f3c3638684b40ff`; sibling garden `20260830T192913-df52b9` taken from the CALLBACK's sender envelope and never from the launch receipt; exactly one new citizen (§3.5); addressed `entwurf_v2` → `meta-mailbox → enqueued`; `lastEnqueuedAt 10:29:19.179Z` / `lastReadAt 10:29:24.673Z`; the drain visible in that session's own transcript for its own garden id. **Both bootstrap stages are in that transcript in order** — the callback-only prompt at offset 970, the caller's task at 4732, no four-backend positional framing anywhere, and an assistant message after the task proving it started a turn rather than sitting in a queue. The unit's own log carries the state machine: `bootstrap-armed 10:29:13.399Z` → `bootstrap-ready 10:29:13.819Z` (+420ms) → `bootstrap-callback-observed 10:29:18.663Z` (`toolCallId=call_KZJtkxfzy1IEE4ejKutCPWcm|fc_0858…`) → `bootstrap-released 10:29:18.731Z` → `bootstrap-task-sent 10:29:18.759Z`. Evidence level L4: one Linux host, ARM, one run. `check-harness-admission-parity` keeps the deterministic half of the admission from being a matter of memory. What this does NOT establish: multi-host, multi-model, or repeated fresh calls in one process. **Cross-harness leg, 2026-08-31 (first bidirectional live-turn roundtrip on the committed bytes + operator redeploy):** a claude-code citizen (`20260831T090152-f998ac`) opened a fresh omp sibling through the public surface (nonce `mux-fresh-call-807cb693b1913ce98ffdbfd1`, callback sender garden `20260831T124226-eac41a`), dispatched an addressed `entwurf_v2` → `meta-mailbox → enqueued`, and the omp citizen's own live turn drained and replied into the claude mailbox — its self-report (doorbell → `entwurf_inbox_read`; callback-only first user message, task second) agrees with every receipt on the chain. |
@@ -131,10 +134,60 @@ in [BASELINE.md](./BASELINE.md); deterministic ownership and sender gates run in
131
134
 
132
135
  Do not describe “Codex” as one delivery shape. The measured app-server-backed TUI can
133
136
  accept `turn/start` for a live thread and report completion; the standalone embedded
134
- TUI exposed no equivalent receive route. This remains archived method evidence, not a
135
- shipping commitment: GLG declined to own a native Codex invocation/install lane on 2026-08-01 because pi
136
- already supplies the official GPT provider path. Entwurf will not duplicate it as a
137
- native citizen or ACP backend. `turn/steer` is active-turn steering, not idle wake.
137
+ TUI exposed no equivalent receive route. `turn/steer` is active-turn steering, not idle wake.
138
+
139
+ **The 2026-08-01 decline is reversed.** GLG re-opened the Codex native lane on 2026-09-08
140
+ (“코덱스 레일 들어가자”) as one of the two 사용자층 확보 axes; the lane is
141
+ `#95` and the pi-already-supplies-GPT argument no longer settles it. Nothing here is a
142
+ support claim: no codex record has ever been minted, there is no birth hook, no sender or
143
+ receiver marker, and `entwurf_fresh_call` cannot open a codex sibling.
144
+
145
+ **But step 2 is already spent, and it is spent WRONG.** `codex` is in `META_BACKENDS`
146
+ (`meta-session.ts:84`) and `META_CITIZEN_BACKENDS` (`:308`), and `pi/entwurf-capabilities.json`
147
+ grades it `wakeMode: "direct-inject"`, `deliveryLevel: "D6"`, `nativeIdLabel: "threadId"`.
148
+ That D6 is a **registry promise with no channel behind it** — precisely the shape
149
+ `docs/adding-a-harness.md` step 8(c) names — and it predates this lane. Correcting the grade
150
+ is step 8 work; it is recorded here so the next reader does not inherit a D6 as evidence.
151
+
152
+ **Current coordinates are 0.153.4** (source `~/repos/3rd/codex` at `rust-v0.153.4`), measured
153
+ 2026-09-08 in `scripts/raw-codex-measure/` — step 1 of `docs/adding-a-harness.md` only. The
154
+ 0.136.0 rows above are preserved history. What the re-measurement changed:
155
+
156
+ - **Delivery survives 17 minor lines unchanged.** The shipped raw probe ran as-is (receipt in
157
+ the matrix row above). One method note: at 0.153.4 the rollout JSONL is materialized lazily,
158
+ so `thread/loaded/list` — not newest-rollout scraping — is the reliable `threadId` source.
159
+ - **Birth timing is first-turn, not window-open**, and the gap is ~47s wide on a live TUI.
160
+ - **A subagent raises `SubagentStart`, never `SessionStart`**, so §3.5's top-level predicate is
161
+ vendor-authoritative and costs nothing.
162
+ - **The callback dialect is `mcp__entwurf_bridge__entwurf_v2`** — a fourth spelling; the
163
+ sanitizer maps `-` to `_` but keeps the digit that omp's charset eats.
164
+ - **The step-6 join holds in both launch modes, but its owner moves — and in the
165
+ delivery-capable mode it cannot separate citizens.** Embedded:
166
+ `hook.ppid == mcp.ppid == the TUI`. App-server-attached: both resolve to the app-server, and
167
+ that is now measured rather than inferred — two live threads on one app-server, two separate
168
+ visible windows, **one `ppid` for every hook and every MCP child of both**, neither TUI in
169
+ the chain. A parent-pid sender marker would be one marker for N citizens, which Hard Rule 7's
170
+ `nativeSessionId` uniqueness forbids. **A different key was then found, and it is not a
171
+ marker at all:** every `tools/call` carries `_meta.threadId` plus an
172
+ `x-codex-turn-metadata.{session_id,thread_id,turn_id}` block, so the caller names itself on
173
+ each call — and that id is byte-identical to the hook's `session_id`, so birth, delivery and
174
+ tool calls share ONE identifier and `record.nativeSessionId = threadId` needs no mapping.
175
+ Both halves are vendor-traced, not just captured: `core/src/mcp_tool_call.rs:1328-1349`
176
+ inserts `threadId` unconditionally (constant at `:1184`) from the call site at `:506-516`,
177
+ and `:1238-1263` carries the turn-metadata block.
178
+ (The registry's pre-existing `nativeIdLabel: "threadId"` is right on that axis; its `D6` on
179
+ the same row still is not.) Consuming `_meta` is new bridge code and is step 6, unstarted.
180
+ - **Both admission questions this lane opened have since been CLOSED by measurement**, and
181
+ neither cost a design decision. The **hook trust prompt** has a real non-interactive path: a
182
+ hook in the managed `/etc/codex/config.toml` layer runs with no prompt, in both launch modes,
183
+ with `turn/start` wake intact — while `--dangerously-bypass-hook-trust` runs the hook but
184
+ kills auto-attach and is therefore unusable here. The remaining constraint is ownership:
185
+ `/etc/codex/` is root-owned, so that is a root-level operator step, not something `setup`
186
+ writes (Hard Rule 17). **Clause 4** has a working carrier: `thread/name/set` plus
187
+ `[tui] status_line = ["thread-title", …]` renders a garden id and survives turns in both
188
+ orderings, because the vendor's auto-titler is guarded on the thread being unnamed
189
+ (`tui/src/app/thread_routing.rs:1841`). It is reachable only over the app-server, and the
190
+ config key needs a writer that owns exactly it.
138
191
 
139
192
  ### Copilot CLI: one citizen, two rails, one pending receipt
140
193
 
package/README.md CHANGED
@@ -79,6 +79,34 @@ A few words that look unusual for a coding tool.
79
79
 
80
80
  ## Install
81
81
 
82
+ **Platform evidence, in one breath.** The Entwurf-only install surface on
83
+ macOS is CERTIFIED (CI) (`macos-install-surface`). On macOS every
84
+ garden-native harness rail (pi, Claude, Copilot, OMP, agy), marker join,
85
+ ACP turn, and mux is NOT CERTIFIED — pending physical host; on Linux those
86
+ same rails are the certified axis. native Windows is UNSUPPORTED.
87
+ CERTIFIED (CI) is weaker than a physical-host doctor green.
88
+ The npm package has no `os` restriction; that is installability, not a
89
+ support claim.
90
+
91
+ **macOS landed in 0.20.0.** That release added macOS support for Entwurf's
92
+ install surface: the npm package installs, `entwurf --help` and `entwurf
93
+ check-bridge` boot, and `entwurf setup` composes the integrations it finds.
94
+ The four installers that previously rejected Darwin by platform name no longer
95
+ do. That surface was validated only in macOS CI because the developers did not
96
+ have a physical Mac. So macOS native-harness rails—marker join, an ACP turn,
97
+ and mux included—are **NOT CERTIFIED — pending physical host**; setup writes
98
+ their wiring but reports them non-green. Linux's certified rail status is
99
+ unchanged.
100
+
101
+ On Darwin, `entwurf setup` that detects a harness is intentionally
102
+ non-green: the wiring WAS written and nothing failed to install, but the
103
+ harness rail is NOT CERTIFIED — pending physical host. That FAIL is not
104
+ an install defect. An Entwurf-only host (no harness on PATH) stays green.
105
+
106
+ A physical Mac can send host facts without installing or logging in:
107
+ `scripts/raw-macos-measure/probe.sh` (`/bin/sh`, ~3.5s). Measurement,
108
+ not certification.
109
+
82
110
  `entwurf` is a neutral npm package first. Get the package, then run **`entwurf setup
83
111
  <project>`** — one command, the same front door from an npm global install, an npm
84
112
  project-local install, or a source checkout. It composes every harness it finds on the
@@ -187,6 +215,9 @@ gitignored and may be absent or stale immediately after a checkout or pull.
187
215
 
188
216
  ### Pi adapter / ACP plugin lane
189
217
 
218
+ This rail is CERTIFIED on Linux; on macOS it is NOT CERTIFIED — pending
219
+ physical host (control sockets, mux, ACP plugin hosting).
220
+
190
221
  To use the `entwurf` provider inside pi, install a compatible pi binary
191
222
  separately (`@earendil-works/pi-coding-agent >=0.85.1 <0.86`). Then point pi at
192
223
  the npm-installed package or development clone:
@@ -217,10 +248,10 @@ for every harness it detects — you do not paste this list to install. This is
217
248
  surface: each unit has its own installer, its own doctor with a named refusal, and its own
218
249
  inverse, so a single broken unit can be redone without touching the rest.
219
250
 
220
- - **Claude Code** (Linux-certified axis) — `install-meta-bridge`, `doctor-meta-bridge`.
221
- - **Antigravity / agy** — `install-agy-bridge`, `install-agy-statusline`, `install-agy-hooks`, each with a matching `doctor-agy-*`.
222
- - **GitHub Copilot CLI** — four independent units, four independent failure modes: `install-copilot-bridge` (birth: garden id + who-sent, on the first prompt), `install-copilot-mcp` (the entwurf tool hand, where `entwurf_inbox_read` lives), `install-copilot-receive` (the receiver extension: doorbell + receiver marker), `install-copilot-statusline` (optional for a manual citizen, required for supported fresh) — each with a matching `doctor-copilot-*` and `uninstall-copilot-*`.
223
- - **OMP (`omp`)** — four units, in-process extensions rather than launchers: `install-omp-bridge` (birth: the `mode === "tui"` visible host, its garden id on the status line, and who-sent), `install-omp-mcp` (the omp-native `entwurf-bridge` entry), `install-omp-config` (the one operator setting `tools: xdev: false`, without which the vendor mounts MCP tools as `xd://` devices the model cannot call), `install-omp-receive` (the receiver extension: mailbox watch + announce-only doorbell) — each with a matching `uninstall-omp-*`, and a `doctor-omp-*` for all but the setting, whose runtime axis `doctor-omp-mcp` owns. The setting writer owns exactly the lines it adds and refuses an explicit operator `tools: xdev: true` by name rather than overwriting it.
251
+ - **Claude Code** (Linux CERTIFIED; macOS NOT CERTIFIED — pending physical host) — `install-meta-bridge`, `doctor-meta-bridge`.
252
+ - **Antigravity / agy** (Linux CERTIFIED; macOS NOT CERTIFIED pending physical host) — `install-agy-bridge`, `install-agy-statusline`, `install-agy-hooks`, each with a matching `doctor-agy-*`.
253
+ - **GitHub Copilot CLI** (Linux CERTIFIED; macOS NOT CERTIFIED pending physical host) — four independent units, four independent failure modes: `install-copilot-bridge` (birth: garden id + who-sent, on the first prompt), `install-copilot-mcp` (the entwurf tool hand, where `entwurf_inbox_read` lives), `install-copilot-receive` (the receiver extension: doorbell + receiver marker), `install-copilot-statusline` (optional for a manual citizen, required for supported fresh) — each with a matching `doctor-copilot-*` and `uninstall-copilot-*`.
254
+ - **OMP (`omp`)** (Linux CERTIFIED; macOS NOT CERTIFIED pending physical host) — four units, in-process extensions rather than launchers: `install-omp-bridge` (birth: the `mode === "tui"` visible host, its garden id on the status line, and who-sent), `install-omp-mcp` (the omp-native `entwurf-bridge` entry), `install-omp-config` (the one operator setting `tools: xdev: false`, without which the vendor mounts MCP tools as `xd://` devices the model cannot call), `install-omp-receive` (the receiver extension: mailbox watch + announce-only doorbell) — each with a matching `uninstall-omp-*`, and a `doctor-omp-*` for all but the setting, whose runtime axis `doctor-omp-mcp` owns. The setting writer owns exactly the lines it adds and refuses an explicit operator `tools: xdev: true` by name rather than overwriting it.
224
255
 
225
256
  Run them as `entwurf <command>`. Which unit a doctor's refusal names, and the clean-host
226
257
  walk-through for each harness, live in [docs/setup-clean-host.md](./docs/setup-clean-host.md).
@@ -275,10 +306,10 @@ Copilot units included — and restart its existing processes; reach for a singl
275
306
  only when a **new** session using the
276
307
  installed artifact makes `doctor-meta-bridge` exit 0 with the live owner join.
277
308
 
278
- Linux is the only currently certified Claude meta-bridge axis. New macOS wiring is
279
- refused because the strict live-owner doctor depends on `/proc`; Darwin uninstall
280
- remains available for legacy cleanup, and the neutral package itself has no `os`
281
- restriction. Detailed diagnosis and clean-host steps live in
309
+ The Claude meta-bridge rail is CERTIFIED on Linux. On macOS it is
310
+ NOT CERTIFIED pending physical host. The Darwin install fence is
311
+ open; uninstall was already open. Detailed diagnosis and clean-host
312
+ steps live in
282
313
  [docs/setup-clean-host.md](./docs/setup-clean-host.md).
283
314
 
284
315
  The active citizen store is V3-only. A store that fails certification is never
@@ -303,7 +334,8 @@ host evidence boundaries are [VERIFY.md](./VERIFY.md) and [BASELINE.md](./BASELI
303
334
  2. **`require.resolve(...)` against the bundled package dependency** (`@agentclientprotocol/claude-agent-acp`). This is the default path; no extra global install needed.
304
335
  3. **`PATH:claude-agent-acp` fallback** — used when the package resolution fails (e.g. a hand-edited `node_modules`).
305
336
 
306
- The curated model registry exposes unprefixed Claude ids plus `cortex-` rows.
337
+ The curated model registry exposes unprefixed Claude ids `claude-opus-5`, `claude-sonnet-5`,
338
+ `claude-fable-5-1` — plus the `cortex-` rows below.
307
339
  Codex is not an ACP backend or a shipped managed citizen lane: it has verified
308
340
  native-delivery probe evidence only. No managed Codex support is claimed; lifecycle,
309
341
  identity, installation, and doctors remain prerequisites for any such lane.
package/VERIFY.md CHANGED
@@ -77,6 +77,7 @@ Do not collapse source, package, fixture, and native-host evidence into one “g
77
77
  | Packed install | `check-pack-install` | Real tarball, but checkout-visible. |
78
78
  | Linux artifact consumer | required `check-install-container` CI job against one preserved candidate | Fixtures prove package/oracle shape, not a real Claude lifecycle. |
79
79
  | Exact release commit | all required CI jobs green, and the `check` job's qualification body step concluded success, at the exact SHA | A different green SHA is not transferable evidence. |
80
+ | macOS Entwurf-only install | required `macos-install-surface` CI job at the exact release SHA | Shape-only, CERTIFIED (CI): certifies the Entwurf-only install path on macOS only — never Claude/Copilot/OMP/agy wiring, the pi rail, ACP or mux. Promoted from non-voting (`continue-on-error`) to required in 0.20.0 after its first CERTIFIED (CI) green on a GitHub Actions `macos-latest` runner (not a physical host): run 34303884286 @ `70eda03` (2026-09-09), 65s, all 10 steps success — the packed candidate installed clean, `entwurf --help` and `entwurf check-bridge` ran (the latter listing the seven current `entwurf-bridge` MCP tools), an all-harness-absent `entwurf setup` reached `result: green` across the five documented SKIP probes (`pi`/`claude`/`agy`/`copilot`/`omp`), and five checked paths (`.pi`, `settings.json`, `.gemini`, `.copilot`, `auth.json.bak`) plus a byte-identical `auth.json` proved zero harness/credential writes. A macOS runner failure now blocks every release cut, the same as any other required job. |
80
81
  | LIVE runtime | `LIVE=1 ./run.sh release-gate <scratch> --cut` plus any shipped on-demand backend axis | `--cut` enforces `SKIP=0`; a red wired gate blocks the cut. |
81
82
  | Native Claude host | installed strict doctor against a new real session | Missing live join is `NOT CERTIFIED`, not a fixture PASS. |
82
83
  | Native agy host | three doctors plus conversation-id-gated native-push round trip | Aggregate release-gate does not own an agy conversation id. |
@@ -155,6 +156,13 @@ Expected tail on a fully green host: `DONE: entwurf setup — result: green (com
155
156
 
156
157
  The wiring / meta-bridge / smoke steps are internal building blocks of `setup` (`install_local_package`, `scripts/meta-bridge-install.sh`, `validate_entwurf_bridge`) — call `setup`, never the parts. Consumers who `npm install @junghanacs/entwurf` get the obvious npm surface; that path is not the developer concern here.
157
158
 
159
+ Installation portability and rail certification are separate axes (Hard Rule 17).
160
+ A harness-absent `setup` on the macOS CI runner reaching `result: green` is
161
+ CERTIFIED (CI) for the Entwurf-only install surface only. On a platform that is
162
+ not certified, a detected harness is named non-green (FAIL) whose wording is
163
+ not an install failure: the wiring WAS written, but the rail is NOT CERTIFIED —
164
+ pending physical host. That FAIL is the honest setup verdict, not a skip.
165
+
158
166
  ### 1.1 Variables (optional)
159
167
 
160
168
  ```bash
@@ -286,7 +294,7 @@ Pass: user/assistant turns accumulate normally; the transcript is not broken/emp
286
294
  The minimum passing bar:
287
295
 
288
296
  1. **Deterministic floor green:** `pnpm run check:full` passes (the core tier plus the hermetic-integration and package/install tiers, incl. `check-pack`), plus a `./run.sh check-gate-qualification` green on the same candidate.
289
- 2. **All three CI jobs green on the exact release commit:** `check`, `install-surface`, and the required Linux `artifact-consumer`; preserve the latter's tarball digest and image identity.
297
+ 2. **All four required CI jobs green on the exact release commit:** `check`, `install-surface`, the required Linux `artifact-consumer`, and `macos-install-surface` — promoted from non-voting to required in 0.20.0 (see the release acceptance axes table above for what its green run does and does not certify); preserve the Linux artifact-consumer's tarball digest and image identity. A macOS runner failure now blocks the cut like any other required job.
290
298
  3. **Live floor MUST green:** `LIVE=1 ./run.sh release-gate <dir> --cut` exits 0 reporting `MUST PASS=N FAIL=0 SKIP=0`; with `--cut` a single SKIP is red, so the exit code itself now carries this condition. A BEHAVIOR FAIL is advisory, not blocking.
291
299
  4. **Native-host doctor green where the Claude meta-bridge is claimed:** a new post-install Claude session exists, live evidence is present, and the installed `doctor-meta-bridge` exits 0. `NOT CERTIFIED` is a release failure for that host, not a skip.
292
300
  5. **Honest self-recognition:** the bridged model identifies its actual harness/backend, lists `entwurf-bridge` as the single MCP server with its seven current tools, and presents a backend-native (not normalized) tool surface.