@junghanacs/entwurf 0.18.0 → 0.18.2

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 CHANGED
@@ -139,7 +139,7 @@ LIVE=1 ENTWURF_ACP_CORTEX_CONNECTION=<conn> ./run.sh smoke-acp-cortex-live # o
139
139
  LIVE=1 AGY_CONVERSATION_ID=<id> ./run.sh smoke-agy-native-push-live
140
140
  ```
141
141
 
142
- - The deterministic floor is tiered (#70). `pnpm check` is the everyday core — toolchain (lint + typecheck), the vitest lanes, and the pure-unit / behavioral-contract / source-topology gates plus the cheap static coherence checks. It prints its own total wall time; acceptance is ≤60s on the reference host `oracle` (an operator measurement, never a hard wall-clock gate on arbitrary hosts). `pnpm run check:full` is the full deterministic floor — the core plus the hermetic-integration and package/install tiers — and is what the candidate protocol, push CI, release-gate, and `prepublishOnly` run. Exact membership is the named `check:*` group scripts in `package.json` (the executable SSOT); a gate changes tier by semantic-class decision, never because it happened to get faster or slower. Neither tier includes `check-gate-qualification`, which is scheduled separately (below).
142
+ - The deterministic floor is tiered (#70). `pnpm check` is the everyday core — toolchain (lint + typecheck), the vitest lanes, and the pure-unit / behavioral-contract / source-topology gates plus the cheap static coherence checks. It prints its own total wall time; acceptance is ≤60s on the reference host `oracle` (an operator measurement, never a hard wall-clock gate on arbitrary hosts). `pnpm run check:full` is the full deterministic floor — the core plus the hermetic-integration and package/install tiers — and is what the candidate protocol, push CI, release-gate, and `prepublishOnly` run. Exact membership is the named `check:*` group scripts in `package.json` (the executable SSOT); a gate changes tier by semantic-class decision, never because it happened to get faster or slower. The full tier carries `check-gate-manifests` (the qualification HEAD, through `check:hermetic`); the everyday core does not, and neither tier carries the mutant-executing `check-gate-qualification`, which is scheduled separately (below).
143
143
  - **Kill-proof discipline (gate qualification).** A gate is a test only if re-planting a closed defect turns it red for the claimed reason. `check-gate-qualification` proves that automatically: committed mutants in `scripts/mutants/` must be KILLED at their `[QK:<claim>]` signature inside an isolated snapshot repo (control→mutant→restore→control; the real checkout is never written). Gates a release touches carry such manifests; assertion counts are never evidence — claim IDs + killed mutant IDs are. `check-agy-permission-matrix` holds the enumerated permission contract space; matrix cells change by axis/rule edits, never by appending cases.
144
144
  - **When changing a contract/gate:** name the production subject and an oracle independent of it; give the failing assertion a stable `[QK:<claim>]` label and add/update the exact-once mutant in `scripts/mutants/*.json`; if the contract is combinatorial, update the literal matrix axes/cells/exclusions together with their declared counts; then verify the focused gate, and let qualification and the full floor follow the scheduling contract below — once on the frozen candidate, not once per amendment. `MUTANT-STALE`/`SURVIVED`/`WRONG-REASON`/`CONTROL-RED`/`HANG`/`IMPURE` are red — never substitute an assertion count for a kill.
145
145
  - Run LIVE gates with `PWD` in scratch so session artifacts do not land in the repo. Strip `CLAUDE_CONFIG_DIR`, `PI_SESSION_ID`, and `PI_AGENT_ID` from the gate process — a live pi/ACP session exports them into children, where they strip Claude hooks or capture a fresh-call callback.
@@ -157,7 +157,7 @@ implement → affected focused gates → independent review → one amendment bu
157
157
 
158
158
  - **Inner loop:** run only the gates whose subject changed. Do not open the full floor to learn what a focused gate already answers.
159
159
  - **Review before floor:** independent review and its corrections close as one bundle before the full floor runs.
160
- - **Qualification is scheduled, not ambient.** `check-gate-qualification` is not in the default check chains (core or full), so the operator inner loop never re-pays the full mutant inventory. It runs standalone once when a lane changed a gate, mutant, or matrix; machine time re-proves it everywhere else — the CI `check` job runs it on every push, and release-gate carries it as a MUST step.
160
+ - **Qualification is scheduled, not ambient — but its HEAD is not.** The mutant-EXECUTING body, `check-gate-qualification`, is not in the default check chains (core or full), so the operator inner loop never re-pays the full mutant inventory. It runs standalone once when a lane changed a gate, mutant, or matrix; machine time re-proves it everywhere else — the CI `check` job runs it on a branch push whose two-dot range touched the qualification surface (`scripts/ci-qualify-decide.sh` derives that path set from the manifests themselves), unconditionally on `workflow_dispatch -f qualify=true` and a weekly schedule, and release-gate carries it as a MUST step. The exact-SHA release oracle refuses a SHA whose body step did not conclude success, so a filtered-out release commit is a named failure with a documented dispatch recovery, never a quiet pass. Its head — runner self-test, manifest-set validation, declared lane inventory — is `check-gate-manifests`, which executes no mutant and never snapshots the repo, and it IS in `check:hermetic`: three of the five reds qualification has ever produced in CI died there in under five seconds. A tag push runs no CI at all, and the exact-SHA evidence a release quotes is whichever run at that commit carries the body concluding success — the branch run when the push moved the qualification surface, else the dispatch run the release skill's recovery creates.
161
161
  - **Full floor once** (`pnpm run check:full`), on the frozen commit candidate. While it runs, nothing edits the worktree or index — including the NEXT boot sectors; a moved candidate voids the run's evidence.
162
162
  - **pre-commit is not the floor.** `.husky/pre-commit` carries only fast static checks (whitespace, lint, typecheck); the full floor is owned by this protocol, not by the hook. Do not grow the hook back, and do not build receipt/cache machinery to prove the protocol was followed.
163
163
  - **Release/LIVE acceptance is untouched.** VERIFY.md floors keep full strength; a shorter inner loop never lowers release evidence.
@@ -185,8 +185,8 @@ implement → affected focused gates → independent review → one amendment bu
185
185
  - Every `.ts` file belongs to one typecheck fence: root emit-capable config, MCP strip-types config, or scripts strip-types config. Do not hide files with `exclude`.
186
186
  - Root pi extensions import TypeBox through `@earendil-works/pi-ai`; do not mix direct `@sinclair/typebox` types.
187
187
  - MCP/scripts use explicit `.ts` imports where Node strip-types requires them. Installed operator surfaces route to compiled JS.
188
- - pi runtime range is `>=0.84.4 <0.85` with devDep exact `0.84.4`; re-evaluate loader aliases and `/compat` at every minor ceiling. The ceiling moves on measurement, never on assumption, and a previous bump's argument is never reused: at 0.83.0→0.84.0 `compat.ts` was still byte-identical but `loader.ts` was NOT, so the diff itself had to be read and judged reachable-or-not. Per-bump hashes, diff judgments, and reachability findings live in the ROADMAP **Dep bump(별도 트랙)** ledger — keep them there, not here.
189
- - ACP pins are recorded in `package.json` and checked by `check-dep-versions`/`check-acp-sdk-surface`; do not describe a dependency bump as a behavioral fix without evidence.
188
+ - pi runtime range is `>=0.85.1 <0.86` with devDep exact `0.85.1`; re-evaluate loader aliases and `/compat` at every minor ceiling. The ceiling moves on measurement, never on assumption, and a previous bump's argument is never reused: at 0.83.0→0.84.0 `compat.ts` was still byte-identical but `loader.ts` was NOT, so the diff itself had to be read and judged reachable-or-not. Per-bump hashes, diff judgments, and reachability findings live in the ROADMAP **Dep bump(별도 트랙)** ledger — keep them there, not here.
189
+ - ACP pins are recorded in `package.json` and owned by `check-acp-sdk-surface`, which is the vitest contract `test/acp-sdk-surface.contract.test.ts` (`./run.sh check-acp-sdk-surface` is a transition shim into it, not a `scripts/` gate). `check-dep-versions` is the **pi** pin's oracle and reads no ACP pin — do not cite it for one. Do not describe a dependency bump as a behavioral fix without evidence.
190
190
 
191
191
  ## Working Style
192
192
 
package/CHANGELOG.md CHANGED
@@ -4,6 +4,247 @@ All notable changes to this project will be documented here. Format follows [Kee
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 0.18.2 - 2026-09-07
8
+
9
+ One lane: #103, the second stage of the CI evidence budget (#99). The mutant body — 373 replants,
10
+ ~30 minutes — stops running on every branch push, and the release oracle stops accepting a run that
11
+ did not carry it. Nothing in the runtime surface moves.
12
+
13
+ ### Changed
14
+
15
+ - **The exact-SHA release oracle requires the qualification BODY, not three green job names (#103
16
+ piece 1).** `verify-exact-ci.sh` reads a fourth axis over one guard: the `check` job's
17
+ `Run ./run.sh check-gate-qualification` step must conclude `success`, with absent, skipped and
18
+ failed each named separately in the refusal. It also stops filtering to `--event push`, so a
19
+ `workflow_dispatch` run at the same SHA is admissible evidence — which is exactly when the
20
+ always-present `headSha` check starts carrying load, because `gh workflow run --ref` takes a
21
+ branch and never a SHA. A skipped body is not evidence: the oracle refuses that SHA and prints the
22
+ dispatch command that fixes it. This landed BEFORE the skip could exist.
23
+ - **The body runs only on a branch push that moved the qualification surface (#103 piece 2).**
24
+ `scripts/ci-qualify-decide.sh` diffs the two-dot push range GitHub itself compares and intersects
25
+ it with a path set **derived from `scripts/mutants/*.json` at runtime** — no copy in YAML, so a new
26
+ mutant subject cannot land outside the filter. Five numbered fail-opens, each naming itself in the
27
+ log: an all-zero base, a forced push (whose base GitHub does not document), a dispatch without
28
+ `qualify=true` or a schedule, an unreadable range, and `pull_request`. `workflow_dispatch -f
29
+ qualify=true` and a weekly schedule run the body unconditionally; `fetch-depth: 0` on the check job
30
+ is what keeps the base readable, and it measured free (2s checkout, the same as depth 1).
31
+ - **Measured on real runs, not argued.** A code push ran the body and took 36m24s
32
+ ([34047559085](https://github.com/junghan0611/entwurf/actions/runs/34047559085)); a docs-only push
33
+ skipped it and took 6m13s
34
+ ([34065841309](https://github.com/junghan0611/entwurf/actions/runs/34065841309)); the four-axis
35
+ oracle **refused** that second SHA while all three of its jobs were green, and the documented
36
+ dispatch recovery turned the refusal back into a pass
37
+ ([34066181211](https://github.com/junghan0611/entwurf/actions/runs/34066181211)). The historical
38
+ justification is replayed rather than remembered: all five qualification reds this repo's CI has
39
+ ever produced still run the body under the filter.
40
+
41
+ ### Added
42
+
43
+ - Three kill-qualified claims and their exact-once replants, inventory 370 → 373:
44
+ `RELEASE-SHA-QUALIFIED-IN-CI` (the fourth axis), `QUALIFY-FILTER-COVERS-SUBJECTS` (every manifest
45
+ subject and signatureSource decides `run_body=true`, asserted behaviourally against the shipped
46
+ script), and `QUALIFY-FILTER-READS-PUSH-RANGE` (a hermetic throwaway repo with a docs-only tip over
47
+ a commit that touched a subject — the two-dot reading, proven without repo history).
48
+ `QUALIFY-FILTER-REPLAYS-PAST-CATCHES` replays the five reds from recorded file lists.
49
+ - `scripts/fixtures/qualify-replay.json` — the five historical reds as the push ranges GitHub
50
+ compared, measured once here because history is not readable everywhere the gate runs.
51
+
52
+ ### Verification
53
+
54
+ All on oracle (Linux, node 24.18.1, pi 0.85.1, omp 18.1.12 — updated from 18.0.0 during this lane).
55
+
56
+ - **`LIVE=1 ./run.sh release-gate /tmp/entwurf-release-gate-0.18.2.JlEU88 --cut` → `cut: OK`.**
57
+ **MUST: PASS=23 FAIL=0 SKIP=0**, BEHAVIOR: PASS=1 FAIL=0 SKIP=0, exit 0, 3305s (55m05s).
58
+ `check-gate-qualification` as its MUST step: **373/373 KILLED**.
59
+ - `pnpm run check:full` on the prepared tree: exit 0, 504s.
60
+ - Pre-version landing run for `0f6667d`: CI run
61
+ [34070192960](https://github.com/junghan0611/entwurf/actions/runs/34070192960), all three jobs
62
+ success — and this is the first release landing judged by the fourth axis the same release adds:
63
+ the oracle read that run's qualification step, not just its job names.
64
+
65
+ ### Notes
66
+
67
+ - **A gate that reads repo history cannot live in the qualification snapshot.** The replay cell first
68
+ read live git objects and went CONTROL-RED inside the snapshot's fresh baseline, costing the whole
69
+ release-gate lane its 17 kills. That is why the fixture carries file lists and the range semantics
70
+ are proven hermetically instead.
71
+ - **Key order in `ci.yml` is a contract.** The body step keeps `run:` as its leading key: cell 8a
72
+ counts that sequence-item form exactly once, and the oracle matches GitHub's rendered
73
+ `Run <command>` step name, so an `if:` placed first — or any `name:` — would break both. Confirmed
74
+ on a real run after the filter landed.
75
+
76
+ ## 0.18.1 - 2026-09-06
77
+
78
+ Two lanes, both landing on `main` after `v0.18.0`: the CI evidence-budget stage 1 (#102, from
79
+ research #99) and the dependency bump (#104 — pi, claude-agent-acp, and the OMP adoption rule
80
+ GLG decided to carry with them). They are not one cause; they are one release.
81
+
82
+ ### Upgrade note — this one asks something of the operator
83
+
84
+ **The pi floor moved, and entwurf does not install harnesses.** The supported range is now
85
+ `>=0.85.1 <0.86`, so a host still carrying pi 0.84.x is BELOW it. `entwurf setup` says so by
86
+ name — `pi FAIL — detected pi <version> is outside the supported range >=0.85.1 <0.86 — Pi
87
+ wiring not written` — and writes no pi wiring rather than wiring a runtime it cannot vouch
88
+ for. That refusal is the design (Hard Rule 17), not a bug to work around.
89
+
90
+ The order matters:
91
+
92
+ 1. **Upgrade pi yourself first.** `pi update`, or `pnpm add -g @earendil-works/pi-coding-agent@0.85.1`
93
+ for a pnpm-global install. entwurf never does this for you.
94
+ 2. **Then `entwurf setup`** — one command, as always. This release changes no deployment
95
+ writer (`pi-extensions/lib/meta-session.ts`, the four `install-*` paths and the packaged
96
+ plugins are byte-identical to 0.18.0), so unlike the 0.17.2 and 0.18.0 upgrades there is no
97
+ stale-writer trap here. What setup does need to redo is the pi wiring it refused to write
98
+ while the runtime was below floor.
99
+ 3. **Restart any pi session you had open.** A running pi process keeps the binary it started
100
+ with, and this release's changed extension surfaces (`entwurf-control.ts` and four ACP
101
+ modules) are loaded at session start. A live 0.84.4 session stays a 0.84.4 session.
102
+
103
+ Hosts without pi are unaffected: absence is still an explicit setup SKIP, not a failure.
104
+
105
+ ### Verification
106
+
107
+ All of the following ran on oracle (Linux, Claude Code 2.1.263, node 24.18.1, pi 0.85.1,
108
+ omp 18.0.0).
109
+
110
+ - **`LIVE=1 ./run.sh release-gate /tmp/entwurf-release-gate-0.18.1.PtUm0r --cut` → `cut: OK`.**
111
+ **MUST: PASS=23 FAIL=0 SKIP=0**, BEHAVIOR: PASS=1 FAIL=0 SKIP=0, exit 0.
112
+ 2026-09-06 18:30:51 → 19:23:40 KST (52m49s). Log preserved at
113
+ `<scratch>/release-gate.log`.
114
+ - `check-gate-qualification` as its MUST step: **369/369 KILLED**.
115
+ - `smoke-acp-raw-turn-live` — the Dep-bump track's designated lock for an ACP adapter
116
+ move — PASS on 0.75.1: launch source `package:@agentclientprotocol/claude-agent-acp`
117
+ (not a PATH fallback), model `claude-sonnet-5`, `protocolVersion=1`,
118
+ `stopReason=end_turn`, 65,780 bytes of NDJSON captured.
119
+ - Both #91 drift sentinels green: `smoke-omp-receive-live`, and `smoke-omp-fresh-live`
120
+ with 21 assertions (step 9 clause 7) — on omp **18.0.0**, below the documented weak
121
+ floor of 18.1.10, which is exactly what "the floor records what was proven, not what is
122
+ permitted" means.
123
+ - `pnpm run check:full` on the prepared tree: exit 0, 480s.
124
+ - Pre-push landing run for the implementation HEAD `c247594`: CI run `34018205091`,
125
+ `check` + `install-surface` + `artifact-consumer` all `success` (`check` 34m14s,
126
+ `check:full` 328s, qualification 369/369).
127
+
128
+ **The first `--cut` was BLOCKED, and the reason was not this release.** MUST PASS=20 FAIL=3
129
+ SKIP=0: `smoke-entwurf-chain-live`, `smoke-mux-lifecycle-live` and `smoke-omp-fresh-live`
130
+ each failed waiting on a sibling that never took its turn, and all three drive their sibling
131
+ on `openai-codex/*` while every Claude-rail step passed. Measured at the time: the codex
132
+ weekly window was at **100%**. The chain's own timeout instrumentation is what made this
133
+ readable rather than mysterious — it printed `terminus fixture at timeout: ownerPid=… alive=true
134
+ ownerAlive=true watchArmed=true`, so the mailbox was provably healthy and the chain had simply
135
+ stalled upstream. After the quota reset the same gate went green with no code change. Recorded
136
+ because an exhausted subscription rail looks exactly like a broken product until someone
137
+ measures it.
138
+
139
+ ### Changed
140
+
141
+ - **The qualification HEAD runs in the everyday floor; the mutant BODY stays scheduled (#102).**
142
+ Across 549 CI runs the qualification step went red five times and **none of the five came from
143
+ mutant execution** — three died in the head in four or five seconds, two were a gate already red
144
+ on a clean tree (#99 B-3). So the head stops waiting behind the ~28-minute body.
145
+ `check-gate-qualification` gains a `--manifests-only` entrypoint, shipped as
146
+ `run.sh check-gate-manifests` and placed in `check:hermetic` (~8s): runner self-test, manifest-set
147
+ validation against the origin index, and the declared lane inventory — **zero mutants executed and
148
+ no snapshot of this repo**. The body runs the same head first; its contract, output and
149
+ mutant-execution semantics are unchanged.
150
+ - **CI push is filtered to branch refs (#102).** All 66 semver-tag runs in this repo's history
151
+ rebuilt a SHA a branch push had already built, and not one reported a fact its branch run had not
152
+ (the single non-green tag run failed at the same step as its main run, two seconds later). The
153
+ exact-SHA evidence a release quotes is the branch run, which is what the release skill's oracle
154
+ already selects.
155
+ - **`check-omp-birth-hook` joins `check:hermetic` (#102).** It was the one gate a committed mutant
156
+ named that ran nowhere else, so the control-pre of a 28-minute run was the only thing in the repo
157
+ that could notice it going red. `check-release-gate-outcomes` cell 9 now owns that as a contract:
158
+ every mutant-named gate is inside `check:full` **or** states its exclusion in prose an operator
159
+ reads (`scripts/check-setup-qualification.sh` takes the second arm, as its own header declared).
160
+ - **pi runtime 0.84.4 → 0.85.1** (#104): devDep exact ×3, peer range `>=0.85.1 <0.86` ×3, the
161
+ `run.sh` pack-install pins, `pnpm-workspace.yaml`, the lockfile, and the five baseline docs
162
+ `check-dep-versions` binds. **0.85.0 is deliberately skipped**: its published `exports` advertised
163
+ `./client` and `./experimental/plugin` at `dist/*` paths the tarball does not contain, which
164
+ upstream named and fixed in 0.85.1 ("SDK import failures caused by unintentionally publishing
165
+ internal experimental code and dependencies in 0.85.0") by making both subpaths source-only.
166
+ entwurf imports neither, so it never reached us — but the landing coordinate is 0.85.1.
167
+ - **`@agentclientprotocol/claude-agent-acp` 0.73.0 → 0.75.1** (#104). The other two ACP pins do
168
+ **not** move: all four tags v0.73.0–v0.75.1 declare the same `@agentclientprotocol/sdk 1.4.0`,
169
+ `@anthropic-ai/claude-agent-sdk 0.3.257`, `zod ^4.0.0` and `engines.node >=22`, so
170
+ `@anthropic-ai/sdk` stays pinned at `0.100.1` against an unchanged `>=0.93.0` peer floor.
171
+ - **Compaction now reaches operators as a tool lifecycle, not assistant text** (upstream #991, in
172
+ 0.75.0). A compacting turn shows a `[tool:start] Compact conversation` / `[tool:…]` notice pair
173
+ where 0.73.0 wrote `Compacting completed.` into the assistant's own text. No mapper change was
174
+ needed and no accounting path is touched; the measurement, the join through the production event
175
+ mapper, and the limits are in `scripts/raw-acp-compaction-measure/README.md` and
176
+ `docs/acp-backend-rail.md` §11-8.
177
+ - **OMP gets a documented *weak* floor instead of a version gate (#91).** entwurf still detects omp
178
+ by presence alone — no `entwurf.ompFloor`, no coherence gate, no exact pin — because a floor is
179
+ the answer to a vendor that fails SILENTLY, which OMP has never been observed to do, and its
180
+ contact surface is guarded by two release-gate MUST smokes that go loudly red. What is recorded
181
+ instead is the last version with a LIVE receipt (**18.1.10**, 2026-09-04 thinkpad), the rule that
182
+ the number moves only when a new receipt exists, and the two sentinels
183
+ (`smoke-omp-fresh-live`, `smoke-omp-receive-live`). Code change: none.
184
+ `docs/setup-clean-host.md` §4b.
185
+
186
+ ### Fixed
187
+
188
+ - **An undeclared pi 0.85.x break, caught by our own typecheck (#104).** `pi-tui`'s `Container`
189
+ gained a `private mouseLayout?` in the 0.85.0 mouse work (0.84.4's `Container` had no private
190
+ member at all) while `Box` declares a separate private field of the same name, so TypeScript's
191
+ private-member identity rule broke the `Box → Container` structural assignment that had held
192
+ through 0.84.4 (`TS2322` at `entwurf-control.ts`). Upstream's Breaking section names only
193
+ `createGatewayBindingFetch`. The repair narrows the annotation to what the vendor contract
194
+ actually asks for — `MessageRenderer` returns `Component | undefined` — which is what the helper
195
+ always needed.
196
+ - **The pack-install pin-leak matcher was blind to a new closure member (#104).** pi 0.85.0 added
197
+ `@earendil-works/chord` as a runtime dependency of pi-coding-agent, pi-agent-core, pi-client and
198
+ pi-protocol — inside the runtime closure, with a name carrying no `pi-` prefix. Measured on a real
199
+ 0.85.1 install tree, `@earendil-works+chord@0.85.1` sits beside the seven pi entries and the
200
+ `^@earendil-works+pi-` filter did not see it, so an unpinned caret would have floated while
201
+ `check-pack-install` printed a verified pin — the same class as the 2026-07-21 `pi-agent-core`
202
+ incident, and it made the "covers every other pi package" comment false. The matcher now filters on
203
+ the org prefix, chord is pinned explicitly, and the self-test splits into two cells so each
204
+ property fails under its own name. New claim `[QK:PACK-INSTALL-PIN-MATCHER-COVERS-CLOSURE]` with
205
+ its exact-once replant; inventory **368 → 369 mutants across 40 lanes**.
206
+ - **`check-pack-install`'s "every harness absent" row was not actually absent of OMP.** The row
207
+ pins harness probes away with explicit `*_BIN` seams, and `OMP_BIN` was never added when 0.16.0
208
+ admitted OMP — the two sibling fixtures (`smoke-setup-verdict.sh`,
209
+ `check-setup-qualification.sh`) both got it. On a host that HAS `omp` on PATH the row's premise
210
+ was therefore false: setup detected omp, and the omp config/receiver installers refused —
211
+ correctly — because the row also exports `PI_CODING_AGENT_DIR`, which omp reads too, leaving the
212
+ target directory ambiguous. The product was right and the fixture was wrong. It stayed invisible
213
+ because CI runners carry no omp **and** `release_gate()` does not run this gate (it is
214
+ `prepublishOnly` plus the CI install-surface job), so no green floor ever covered it. Measured
215
+ here with a control: a clean clone of the pre-bump HEAD fails at the same row for the same
216
+ reason. The seam is added to both setup rows, and the zero-state probe now requires **five**
217
+ SKIPs so a sixth harness cannot be admitted while this seam is left behind again.
218
+ - **`VERIFY.md` still advertised a pi range two bumps stale, and now nothing can leave it there.**
219
+ It sat outside `check-dep-versions`' `BASELINE_DOCS`, so nothing read it; it has joined that
220
+ list (six docs). Its declaration is a plain range, so the existing range scan binds it — no new
221
+ prose pattern was added for it.
222
+ - **`AGENTS.md` claimed `check-dep-versions` checks the ACP pins.** It does not — it is the **pi**
223
+ pin's oracle and reads no ACP pin (`run.sh:1820-1919`). The ACP pins are owned by
224
+ `check-acp-sdk-surface`, which is the vitest contract `test/acp-sdk-surface.contract.test.ts`;
225
+ the `run.sh` name is a transition shim into it.
226
+
227
+ ### Added
228
+
229
+ - **`scripts/raw-acp-compaction-measure/`** — a raw measurement probe (not a gate, in no check
230
+ tier) that drives one live `/compact` turn on the pinned adapter and replays the captured
231
+ notifications through the production event mapper, so the vendor wire and our rendered notice sit
232
+ in one receipt.
233
+ - Four new qualification claims from #102, each with its exact-once replant:
234
+ `MANIFEST-SET-INTEGRITY-REFUSED` and `LANE-INVENTORY-DECLARED` (new `gate-qualification` lane,
235
+ replanting the 08-20 / 08-21 / 08-28 catches), `MUTANT-GATES-INSIDE-FULL-FLOOR` and
236
+ `CI-TAG-PUSH-NOT-REBUILT` (`release-gate` lane).
237
+
238
+ ### Notes
239
+
240
+ - **`allowBuilds: esbuild: false`.** chord's only dependency is esbuild, and its arrival made
241
+ `pnpm install` stop on `ERR_PNPM_IGNORED_BUILDS` until the key carried a decision. Denied like the
242
+ other two entries: nothing here executes esbuild.
243
+ - **`bridge-command-boot`'s three agy gates moved to the `run.sh` argv the other manifests use**,
244
+ merging a duplicate group (#102). Group count is unchanged at 52 — the merge removes one and the
245
+ new head gate adds one — so the saving is a 66s control pair traded for a 16s one, not fewer
246
+ groups.
247
+
7
248
  ## 0.18.0 - 2026-09-04
8
249
 
9
250
  ### Fixed
package/CONTRIBUTING.md CHANGED
@@ -30,7 +30,7 @@ pnpm check # everyday core (prints wall time; <=60s on the referenc
30
30
  pnpm run check:full # full deterministic floor — the required PR gate
31
31
  ```
32
32
 
33
- The deterministic floor is tiered (#70). `pnpm check` is the everyday core (biome, tsc, the vitest lanes, and the fast contract gates); `pnpm run check:full` adds the hermetic-integration and package/install tiers — all but the separately scheduled `check-gate-qualification`, which CI runs on every push and a gate-changing PR must run once itself. Exact membership is the named `check:*` scripts in `package.json`. Run `check:full` once on your frozen commit candidate — the pre-commit hook (`.husky/pre-commit`) carries only fast static checks (whitespace, lint, typecheck), not the full floor, so a green `pnpm run check:full` before commit is the evidence that your change holds (scheduling contract: AGENTS.md "Verification scheduling").
33
+ The deterministic floor is tiered (#70). `pnpm check` is the everyday core (biome, tsc, the vitest lanes, and the fast contract gates); `pnpm run check:full` adds the hermetic-integration and package/install tiers — including `check-gate-manifests`, the qualification HEAD (runner self-test, manifest-set validation, declared lane inventory, zero mutants executed, ~8s), but not the separately scheduled mutant-EXECUTING body `check-gate-qualification`, which CI runs on a branch push whose diff touched the qualification surface (and unconditionally on dispatch or the weekly schedule) and a gate-changing PR must run once itself. Exact membership is the named `check:*` scripts in `package.json`. Run `check:full` once on your frozen commit candidate — the pre-commit hook (`.husky/pre-commit`) carries only fast static checks (whitespace, lint, typecheck), not the full floor, so a green `pnpm run check:full` before commit is the evidence that your change holds (scheduling contract: AGENTS.md "Verification scheduling").
34
34
 
35
35
  For changes that touch backend launch, session lifecycle, or `_meta` shape, also run
36
36
  the live ACP smokes that cover the touched rail — at minimum:
package/README.md CHANGED
@@ -157,7 +157,7 @@ cd ~/repos/gh/entwurf
157
157
 
158
158
  The full source setup requires Node 24, pnpm, and Python 3 on PATH. Harnesses are
159
159
  optional-by-presence: `setup` runs the frozen dependency install, then composes what the operator
160
- already installed — a compatible `pi` (`>=0.84.4 <0.85`), Claude Code, agy, and the Copilot CLI
160
+ already installed — a compatible `pi` (`>=0.85.1 <0.86`), Claude Code, agy, and the Copilot CLI
161
161
  each get their wiring completed when detected, an absent harness is an explicit zero-state SKIP,
162
162
  and a detected harness that cannot be completed (including a below-floor `pi`) is a named FAIL
163
163
  that makes setup exit nonzero. `setup` never installs a harness binary or touches a credential
@@ -188,7 +188,7 @@ gitignored and may be absent or stale immediately after a checkout or pull.
188
188
  ### Pi adapter / ACP plugin lane
189
189
 
190
190
  To use the `entwurf` provider inside pi, install a compatible pi binary
191
- separately (`@earendil-works/pi-coding-agent >=0.84.4 <0.85`). Then point pi at
191
+ separately (`@earendil-works/pi-coding-agent >=0.85.1 <0.86`). Then point pi at
192
192
  the npm-installed package or development clone:
193
193
 
194
194
  ```bash
@@ -433,6 +433,7 @@ pnpm run check:full # full deterministic floor (adds the her
433
433
  # source-maintainer only — qualification snapshots the git work surface, and both
434
434
  # commands are source-contract gates rather than installed operator checks:
435
435
  ./run.sh check-agy-permission-matrix # AGY permission contract space as a literal table (declared cells + stated exclusions)
436
+ ./run.sh check-gate-manifests # the qualification HEAD alone: self-test + manifest-set validation + declared lane inventory, zero mutants run (in check:hermetic)
436
437
  ./run.sh check-gate-qualification # kill-proof: committed defect mutants must turn their gates red for the claimed reason
437
438
 
438
439
  # agy LIVE acceptance — requires an already-running conversation:
@@ -450,9 +451,14 @@ LIVE=1 ./run.sh release-gate /tmp/scratch --cut # the single cut gate (MUST + BE
450
451
  LIVE=1 ENTWURF_ACP_CORTEX_CONNECTION=<conn> ./run.sh smoke-acp-cortex-live # Cortex is on-demand: the aggregate does not re-certify it
451
452
  ```
452
453
 
453
- `pnpm run check:full` includes the AGY permission contract matrix; the committed-mutant
454
- gate qualification is scheduled separately (`./run.sh check-gate-qualification` — the CI
455
- `check` job runs it on every push, and release-gate carries it as a MUST step). A gate a
454
+ `pnpm run check:full` includes the AGY permission contract matrix and the qualification
455
+ HEAD (`check-gate-manifests` — runner self-test, manifest-set validation, declared lane
456
+ inventory, zero mutants executed); the committed-mutant EXECUTION is scheduled separately
457
+ (`./run.sh check-gate-qualification` — the CI `check` job runs it on a branch push that touched the qualification surface,
458
+ unconditionally on `workflow_dispatch -f qualify=true` and a weekly schedule, and release-gate
459
+ carries it as a MUST step; a tag push runs no CI at all). A branch run whose body was filtered out
460
+ is not release evidence: the exact-SHA oracle requires the run it reads to carry that step
461
+ concluding success. A gate a
456
462
  release touches must kill its known defect for the claimed `[QK:<claim>]` reason —
457
463
  the descriptions above name what each smoke covers, and no check count is quality
458
464
  evidence on its own. Gate qualification needs the git work surface, while the matrix
package/VERIFY.md CHANGED
@@ -40,8 +40,8 @@ Verification here is not a benchmark. In production we exchange short turns and
40
40
 
41
41
  ### The canonical floor — two entry points
42
42
 
43
- - **Deterministic floor — two tiers (#70):** `pnpm check` is the everyday CORE — toolchain (lint + typecheck), the vitest lanes, and the pure-unit / behavioral-contract / source-topology gates plus the cheap static coherence checks. It prints its own total wall time; acceptance is ≤60s on the reference host `oracle` (an operator measurement, never a hard wall-clock gate on arbitrary hosts). `pnpm run check:full` is the FULL deterministic floor — the core plus the hermetic-integration and package/install tiers — and is what the frozen-candidate protocol, push CI, release-gate, and `prepublishOnly` run. Exact membership is the named `check:*` group scripts in `package.json` (the executable SSOT — this document records meaning and principles, not the command list); a gate changes tier by semantic-class decision, never by getting faster or slower. Both tiers exclude the separately scheduled `check-gate-qualification`.
44
- - **Discriminating power of that floor:** `./run.sh check-gate-qualification` (scheduled, not ambient: standalone when a lane changes a gate/mutant/matrix, in the CI `check` job on every push, and as a release-gate MUST step — no longer inside the default check chains) re-plants committed defect mutants (`scripts/mutants/*.json`, one per closed defect class) in an isolated snapshot repo and requires each to turn its gate red **bounded and at its claimed `[QK:<claim>]` signature** — a wrong-reason red fails, a baseline-red control voids the whole group, and the runner is negative-controlled on every run (zero-match/multi-match/survived/wrong-reason/hang/control-red/impurity). This measures whether the deterministic gates still *block* what they claim to block; it is **not a new evidence level** (L0–L5 are untouched) and never substitutes for LIVE evidence. Per-cut records cite claim IDs + killed mutant IDs — "N checks passed" alone is not evidence. `check-agy-permission-matrix` complements it with the enumerated permission contract space (literal cells + stated exclusion rules, oracle independent of the SUT).
43
+ - **Deterministic floor — two tiers (#70):** `pnpm check` is the everyday CORE — toolchain (lint + typecheck), the vitest lanes, and the pure-unit / behavioral-contract / source-topology gates plus the cheap static coherence checks. It prints its own total wall time; acceptance is ≤60s on the reference host `oracle` (an operator measurement, never a hard wall-clock gate on arbitrary hosts). `pnpm run check:full` is the FULL deterministic floor — the core plus the hermetic-integration and package/install tiers — and is what the frozen-candidate protocol, push CI, release-gate, and `prepublishOnly` run. Exact membership is the named `check:*` group scripts in `package.json` (the executable SSOT — this document records meaning and principles, not the command list); a gate changes tier by semantic-class decision, never by getting faster or slower. The FULL tier carries the qualification HEAD (`check-gate-manifests`, through `check:hermetic`); the everyday core does not, and neither tier carries the separately scheduled mutant-executing body (`check-gate-qualification`).
44
+ - **Discriminating power of that floor:** `./run.sh check-gate-qualification` (scheduled, not ambient: standalone when a lane changes a gate/mutant/matrix, in the CI `check` job on a branch push that touched the qualification surface, and as a release-gate MUST step — no longer inside the default check chains; a semver TAG push triggers no CI at all, and the exact-SHA evidence a release quotes is whichever run at that commit CARRIES the body — the branch run when the push touched the qualification surface, else a dispatch run; a push that touched none of the qualification surface skips the body, and `workflow_dispatch -f qualify=true` plus a weekly `schedule` run it unconditionally — the exact-SHA release oracle refuses a SHA whose body step did not conclude success) re-plants committed defect mutants (`scripts/mutants/*.json`, one per closed defect class) in an isolated snapshot repo and requires each to turn its gate red **bounded and at its claimed `[QK:<claim>]` signature** — a wrong-reason red fails, a baseline-red control voids the whole group, and the runner is negative-controlled on every run (zero-match/multi-match/survived/wrong-reason/hang/control-red/impurity). This measures whether the deterministic gates still *block* what they claim to block; it is **not a new evidence level** (L0–L5 are untouched) and never substitutes for LIVE evidence. Per-cut records cite claim IDs + killed mutant IDs — "N checks passed" alone is not evidence. `check-agy-permission-matrix` complements it with the enumerated permission contract space (literal cells + stated exclusion rules, oracle independent of the SUT). The gate's HEAD is separable and IS in the floor: `check-gate-manifests` runs the runner self-test, validates the committed manifest set against the origin index, and asserts the declared lane inventory, while executing zero mutants and never snapshotting the repo — three of the five reds qualification has ever produced in CI died in that head, in under five seconds.
45
45
  - **Live floor:** `LIVE=1 ./run.sh release-gate <scratch-project-dir> --cut` — `pnpm run check:full` + the v2-native live gates + the ACP plugin acceptance floor. It reports a **two-tier summary**:
46
46
  - **MUST tier** (release-blocking — owns the exit code; "green" applies only here): `pnpm run check:full`, `check-gate-qualification`, `smoke-entwurf-v2-matrix-live`, `check-bridge`, `doctor-pi-provider` (#81 — `check-bridge` proves the launcher this checkout SHIPS; this proves the invocation the operator's pi provider actually EXECS, by booting it and requiring the entwurf verb set back, because `command -v` answers yes for a command that exits 127; wired as a step on 2026-08-19 after a relocated pnpm cmd-shim cost a cut sixteen LIVE steps before the same fact surfaced at `smoke-acp-bundled-mcp-live`), the resident-garden-guard zero-token half (record birth / record-keyed socket / attach-on-reopen), the `smoke-acp-*-live` ACP plugin smokes (socket-citizen / raw-turn / overlay / provider / session-reuse / carrier-augment / memory-containment / rgg / mcp / skill / bundled-mcp / v2-send), the axis wired in on 2026-07-31 that the aggregate had simply never listed (`smoke-claude-native-resume-live`), `smoke-entwurf-chain-live` — the cross-harness delivery chain (native Claude Code → pi GPT → pi ACP Sonnet → mailbox terminus) proving sender identity and replyability at every hop plus a real read receipt at the end — the integrated `smoke-mux-lifecycle-live` (its own note below), and the two OMP steps 0.16.0 wired: `smoke-omp-receive-live` (the addressed roundtrip into a live omp citizen; it reads the capability registry and decides its own outcome rather than passing by default) and `smoke-omp-fresh-live` (the clause 7 receipt the release stop now requires). (`smoke-session-id-name` is gone — #50 C3: its `--session-id`/`--name` substrate has no entwurf consumer anymore.)
47
47
  - **BEHAVIOR tier** (advisory, non-blocking): the resident-garden-guard positive (a model-in-loop `entwurf_self` turn). A BEHAVIOR FAIL is surfaced with its artifact path but **never blocks the cut**. The lane holds what the model *chooses*, never what our wiring fails to deliver — a gate that TELLS the model which tool to call stays MUST, because its failure is ours — measured 2026-07-24, when the tool turned out to be absent from the session schema in both observed failures (the bundled-MCP readiness gap recorded in `scripts/smoke-acp-v2-send-live.ts`).
@@ -76,7 +76,7 @@ Do not collapse source, package, fixture, and native-host evidence into one “g
76
76
  | Source | `pnpm run check:full` + `./run.sh check-gate-qualification` | Does not prove an installed consumer. |
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
- | Exact release commit | all required CI jobs green at the exact SHA | A different green SHA is not transferable evidence. |
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
80
  | 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
81
  | Native Claude host | installed strict doctor against a new real session | Missing live join is `NOT CERTIFIED`, not a fixture PASS. |
82
82
  | Native agy host | three doctors plus conversation-id-gated native-push round trip | Aggregate release-gate does not own an agy conversation id. |
@@ -137,7 +137,7 @@ The goal is not merely "invoke Claude Code." We want:
137
137
  **One install command to remember: `./run.sh setup <project>`.** It is idempotent — re-run the exact same command whenever anything looks wrong. There is no second install surface to juggle: from a clone `setup` runs the whole floor in order.
138
138
 
139
139
  1. `pnpm install` — installs the pinned development dependencies and builds the bridge (source-checkout bootstrap only; an installed package never runs npm/pnpm inside `node_modules`)
140
- 2. pi wiring → `<project>/.pi/settings.json` + user-scope registration — only when a `pi` inside the supported range (`>=0.84.3 <0.85`) is on PATH; absent pi is an explicit zero-state SKIP, a below-floor pi is a detected FAIL. The user-scope entry is owner-recorded (#86 C2): another root's live-or-missing ownership makes this step a zero-write refusal (setup: pi FAIL) that names `takeover-user-scope`; the install-states bind the exact managed settings path (a drifted/symlinked/corrupt target is a zero-write refusal) and the inverse removes only the recorded owner's exact entry; `doctor-pi-package` reports the verdict
140
+ 2. pi wiring → `<project>/.pi/settings.json` + user-scope registration — only when a `pi` inside the supported range (`>=0.85.1 <0.86`) is on PATH; absent pi is an explicit zero-state SKIP, a below-floor pi is a detected FAIL. The user-scope entry is owner-recorded (#86 C2): another root's live-or-missing ownership makes this step a zero-write refusal (setup: pi FAIL) that names `takeover-user-scope`; the install-states bind the exact managed settings path (a drifted/symlinked/corrupt target is a zero-write refusal) and the inverse removes only the recorded owner's exact entry; `doctor-pi-package` reports the verdict
141
141
  3. Claude meta-bridge global plugin — only when `claude` is on PATH; otherwise skipped cleanly
142
142
  4. source stable-bin exposure — including certified `entwurf` → this checkout's `run.sh`, the managed runtime Copilot fresh resolves; helper units are attempted independently and a foreign helper is a named FAIL
143
143
  5. agy bridge + exact permission + statusline + `PreInvocation` hook — only when `agy` is on PATH; each adapter is idempotent and independently doctorable
package/demo/README.md CHANGED
@@ -139,7 +139,7 @@ SCENE_DELAY=30 FINAL_PAUSE=10 bash demo.sh
139
139
 
140
140
  ## Prerequisites
141
141
 
142
- - `pi` on PATH (current floor 0.84.4)
142
+ - `pi` on PATH (current floor 0.85.1)
143
143
  - `entwurf` provider configured + auth ready for the selected sender/peer models
144
144
  - `asciinema` installed
145
145
  - `agg` installed (optional — only for GIF conversion)
@@ -93,9 +93,9 @@ undifferentiated "supported" column is what let a Claude PASS read as if it also
93
93
  | Surface | Declaration | Class | What a green actually says |
94
94
  |---|---|---|---|
95
95
  | Entwurf package | `0.17.0` | shipped baseline | the package contract these rows belong to |
96
- | pi runtime | devDep exact `0.84.4`, peer `>=0.84.4 <0.85` | **exact** oracle + **closed range** | built and certified against 0.84.4; hosts inside the range are accepted, and the ceiling moves only on measurement |
96
+ | pi runtime | devDep exact `0.85.1`, peer `>=0.85.1 <0.86` | **exact** oracle + **closed range** | built and certified against 0.85.1; hosts inside the range are accepted, and the ceiling moves only on measurement |
97
97
  | ACP wire SDK | `@agentclientprotocol/sdk 1.4.0` | **exact** | the shared wire oracle both adapters speak |
98
- | Claude ACP adapter | `@agentclientprotocol/claude-agent-acp 0.73.0` | **exact**, bundled | the adapter we ship and certify; resolved before any PATH fallback |
98
+ | Claude ACP adapter | `@agentclientprotocol/claude-agent-acp 0.75.1` | **exact**, bundled | the adapter we ship and certify; resolved before any PATH fallback |
99
99
  | Claude Agent SDK | `0.3.257` (transitive) | **exact** oracle | the runtime risk surface behind the adapter |
100
100
  | Anthropic SDK | `0.100.1` | **exact**, peer-resolution only | satisfies the Agent SDK peer floor (0.93.0+); never an API client here (gate L4) |
101
101
  | Claude Code runtime | `>=2.1.217` (`entwurf.claudeCodeFloor`) | **floor** | below it, hook args are silently dropped; entwurf enforces this itself |
@@ -144,9 +144,19 @@ different reasons, and collapsing them would hide a real risk**:
144
144
  - **Advertised but never called.** Some surfaces carry no capability prerequisite at all — the
145
145
  `providers/list` / `providers/set` / `providers/disable` trio added in 0.70.0 is advertised
146
146
  unconditionally, and 0.71.0–0.73.0 add native subagents, async tasks, message-specific session
147
- forks, AI-generated session titles and permission-mode kinds on the same footing. They are
147
+ forks, AI-generated session titles and permission-mode kinds on the same footing. 0.74.0–0.75.1
148
+ extend the same list: the `authStatus` extension (0.75.0, #1080), Markdown-rendered usage
149
+ statistics (0.75.0, #1085) and restored session forks (0.75.1, #1089). The `--hide-claude-auth`
150
+ subscription refusal (0.74.0, #1079) is unreachable for a second reason — `[측정 2026-09-06]`
151
+ entwurf passes that flag nowhere (repo grep, 0 hits). They are
148
152
  unreachable only because the common loop never invokes them (nor `logout`). Nothing upstream
149
153
  enforces that; it is our own call-site discipline, and it stops holding the moment we use one.
154
+ - **The one 0.73.0 → 0.75.1 change that DOES reach us:** context compaction is now surfaced as a
155
+ synthetic ACP tool lifecycle (0.75.0, #991) — a `tool_call` with `kind: "think"`, title
156
+ `Compact conversation`, and `_meta.contextCompaction` schema v1 — where it used to arrive as
157
+ assistant text. Our mapper routes every `tool_call`/`tool_call_update` through
158
+ `renderToolUpdate` (`event-mapper.ts`), so this is not a type break; what changes is what an
159
+ operator SEES in a compacting turn. See §11-8 for the measurement.
150
160
 
151
161
  Adopting either class requires a separate observed need plus a complete rendering/lifecycle/evidence
152
162
  contract. An optional upstream feature is not a core-value gap.
@@ -257,17 +267,19 @@ caller-session `_meta`, and cross-machine certification.
257
267
 
258
268
  A backend can return `newSession` before its declared MCP server is callable. This was
259
269
  observed intermittently on the Claude rail and directly on Cortex's private `mcp.json`
260
- path. Neither `claude-agent-acp` 0.73.0 nor the Cortex landing adds a client-side
270
+ path. Neither `claude-agent-acp` 0.75.1 nor the Cortex landing adds a client-side
261
271
  readiness fence over a session's declared MCP servers, and entwurf's common loop
262
272
  calls `mcpServerStatus()` nowhere.
263
- (Re-measured at the 0.70.0 → 0.73.0 bump, not inherited from the previous one.
264
- `mcpServerStatus` call sites in `src/acp-agent.ts` went 0 2, new in 0.71.0 via
265
- `0cbbaf3` (MCP OAuth, LLM-25012) so the ADAPTER now calls it where it previously
266
- did not. Both sites were read at `v0.73.0 src/acp-agent.ts:1618` and `:1711`: each is
267
- gated behind `supportsMcpOAuth(query)`, and the polling one waits only on a server
268
- that already reported `needs-auth`, never on every declared server before
269
- `newSession` returns. That is an auth handshake, not a readiness fence, so the
270
- boundary below is unchanged. The other reachable-surface findings also re-measured:
273
+ (Re-measured at the 0.73.0 → 0.75.1 bump, not inherited from the previous one — and the
274
+ 0.70.0 0.73.0 argument is not reused either. `mcpServerStatus` call sites in
275
+ `src/acp-agent.ts` are **2 at both v0.73.0 and v0.75.1** `[측정 2026-09-06, git grep -c]`;
276
+ they first appeared in 0.71.0 via `0cbbaf3` (MCP OAuth, LLM-25012), so the ADAPTER calls it
277
+ where it once did not. Both were re-read at `v0.75.1 src/acp-agent.ts:1736` and `:1829`
278
+ (the 0.73.0 coordinates were `:1618` and `:1711`): the first sits inside
279
+ `authenticateMcpServers` behind `supportsMcpOAuth(query)` and skips every status that is not
280
+ `needs-auth`; the second polls a SINGLE named server to `connected` under an OAuth deadline.
281
+ Neither waits on every declared server before `newSession` returns. That is an auth
282
+ handshake, not a readiness fence, so the boundary below is unchanged. The other reachable-surface findings also re-measured:
271
283
  AIR typed failures and the AIR file-change report stay capability-gated and
272
284
  unadvertised by entwurf; `providers/set` / `providers/disable` stay advertised
273
285
  unconditionally and uncalled; native subagents, async tasks, session forks, session
@@ -338,6 +350,28 @@ a cut that changes or ships the Cortex rail must run and read its dedicated smok
338
350
  Per-cut counts, digests, versions, and host observations belong in BASELINE/CHANGELOG,
339
351
  not this standing contract.
340
352
 
353
+ ## 11-8. Compaction is a tool lifecycle (0.75.0 onward)
354
+
355
+ The one change in the `0.73.0 → 0.75.1` bump that REACHES the common loop. Upstream #991
356
+ (`f74a517`) replaced compaction's assistant text with a synthetic ACP tool call: `kind:
357
+ "think"`, title `Compact conversation`, `_meta.contextCompaction` schema v1.
358
+
359
+ `[측정 2026-09-06, oracle, adapter 0.75.1, claude-sonnet-5]` one live `/compact` turn emitted
360
+ exactly two notifications (`tool_call` in-progress → `tool_call_update`), and those verbatim
361
+ objects replayed through the production `applyAcpSessionUpdate` produced a
362
+ `[tool:start] Compact conversation` / `[tool:…] Compact conversation` notice pair. No new
363
+ mapper branch is needed — `renderToolUpdate` routes every tool call regardless of `kind` —
364
+ and `_meta.contextCompaction` is dropped by the mapper, so no accounting path sees it.
365
+
366
+ What changed is therefore what an OPERATOR sees in a compacting turn, not what entwurf
367
+ computes. The post-compaction occupancy refresh is NOT new: `v0.73.0 src/acp-agent.ts:3460`
368
+ already emitted a `usage_update` at `compact_boundary` and 0.75.1 still does
369
+ (`dist/acp-agent.js:2740-2752`, now reading `compact_metadata.post_tokens` instead of a
370
+ `getContextUsage` control request), so the shrinking-`used_end` case that
371
+ `backend.ts:1286-1288` names as #96's weak floor gains no new trigger here.
372
+
373
+ Receipt, limits and the `completed`-branch gap: `scripts/raw-acp-compaction-measure/README.md`.
374
+
341
375
  ## Open work
342
376
 
343
377
  - causal MCP-readiness diagnosis and, only with proof, a backend-invariant fence;
@@ -12,7 +12,7 @@ only on Linux because its strict live-owner join uses `/proc`.
12
12
  | npm/pnpm | npm is bundled with Node; pnpm is required for source setup | package or source installation |
13
13
  | Python 3 | required by `setup`/`install` (project-path normalization + settings writers); `--help`/`check-bridge` stay Python-free | pi/Claude/agy/Copilot wiring writers |
14
14
  | entwurf | global/project-local `@junghanacs/entwurf`, or a source checkout | operator command and garden capability |
15
- | pi | optional-by-presence, `>=0.84.4 <0.85` — absent is an explicit setup SKIP, below-floor is a named FAIL | ACP provider, control sockets |
15
+ | pi | optional-by-presence, `>=0.85.1 <0.86` — absent is an explicit setup SKIP, below-floor is a named FAIL | ACP provider, control sockets |
16
16
  | Claude Code | optional, **`>=2.1.217`** — the exec-form hook floor | Claude ACP auth/runtime and mailbox-backed native citizen |
17
17
  | GitHub Copilot CLI | optional-by-presence, operator-installed and authenticated — absent is an explicit setup SKIP; detected composes all four units (birth/MCP/receiver/footer) | self-fetch citizen and visible fresh |
18
18
  | OMP (`omp`) | optional-by-presence, operator-installed — absent is an explicit setup SKIP; detected composes all four units (birth/MCP/`tools.xdev` setting/receiver) | self-fetch citizen and visible fresh (accepted on one host — see §4b) |
@@ -110,7 +110,7 @@ packageRoot↔installerRoot coupling mismatch and a package/provider managed-pat
110
110
  Install the exact release floor, then wire the project:
111
111
 
112
112
  ```bash
113
- npm install -g @earendil-works/pi-coding-agent@0.84.4
113
+ npm install -g @earendil-works/pi-coding-agent@0.85.1
114
114
  pi --version
115
115
 
116
116
  cd ~/entwurf-smoke
@@ -118,7 +118,7 @@ entwurf install .
118
118
  pi -e "$(npm root -g)/@junghanacs/entwurf" --list-models entwurf
119
119
  ```
120
120
 
121
- The supported range is `>=0.84.4 <0.85`. It is a hard minimum: installing this
121
+ The supported range is `>=0.85.1 <0.86`. It is a hard minimum: installing this
122
122
  release onto a 0.83.x pi host upgrades the runtime rather than keeping the older
123
123
  minor. A host using only the external MCP bridge can skip pi until it needs a
124
124
  control socket; no delivery rail launches a pi process.
@@ -239,6 +239,38 @@ announces a tool the MCP hand provides. Install it without them and it will log
239
239
  `arm-deferred`, give up after ~20s, and `doctor-omp-receive` will name the missing sibling
240
240
  as a note rather than a fault.
241
241
 
242
+ ### The OMP version rule — a weak floor, deliberately (#91)
243
+
244
+ **entwurf sets no OMP version floor in code, and will not grow one on schedule.** Detection is
245
+ presence-only (`command -v "${OMP_BIN:-omp}"`); there is no `entwurf.ompFloor`, no coherence
246
+ gate, and no exact pin — unlike Node (`engines.node`), pi (`>=0.85.1 <0.86`) and Claude Code
247
+ (`entwurf.claudeCodeFloor`), each of which has an enforcement point. That asymmetry is a
248
+ decision, not an omission. A floor is the answer to a vendor that fails SILENTLY — Claude Code
249
+ earned one because an older binary validates the exec manifest, drops `args` at runtime, and
250
+ reports success. OMP has never been observed to fail that way, it publishes at close to a
251
+ daily cadence, and when its contact surface breaks the two LIVE smokes go loudly red.
252
+
253
+ What stands instead is a **weak floor: the last version with a LIVE receipt.**
254
+
255
+ > **OMP minimum: `18.1.10`** — `[측정 2026-09-04, thinkpad, Linux x86-64, omp/18.1.10]`
256
+ > `smoke-omp-receive-live` 11 assertions ok (garden `20260904T224103-d36fed`) and
257
+ > `smoke-omp-fresh-live` 21 assertions ok (garden `20260904T224132-351877`, model
258
+ > `openai-codex/gpt-5.6-sol`), with `doctor-omp-receive` PASS and `doctor-omp-mcp` ok. That
259
+ > update skipped one minor and ten patches from 18.0.0 and broke nothing.
260
+ >
261
+ > **This number moves only when a NEW LIVE receipt exists** — never on a release cadence, a
262
+ > changelog read, or a static gate. Run newer OMP freely; the floor records what was proven,
263
+ > not what is permitted.
264
+ >
265
+ > **The drift sentinels are `smoke-omp-fresh-live` and `smoke-omp-receive-live`**, both
266
+ > release-gate MUST steps. Green after an update means keep going. Red means open a NEW issue
267
+ > carrying the first vendor contact point that broke and its reproduction receipt — do not
268
+ > reopen the closed adoption question. If a SILENT failure is ever observed (green smokes over
269
+ > a dead contact point), that is the evidence a real floor needs, and it earns its own issue
270
+ > for the same reason Claude Code's floor exists.
271
+
272
+ The evidence and the reasoning are in **#91**; this paragraph is its durable form.
273
+
242
274
  **The receiver arms per session, and only for the visible TUI host.** Opening omp arms it;
243
275
  `/new` re-arms it for the replacement citizen and retires the previous one; closing omp
244
276
  retires it. A task subagent arms nothing. While nothing is armed, dispatch to that garden
@@ -123,8 +123,8 @@ export const claudeAdapter = {
123
123
  },
124
124
  // MEASURED (2026-09-02, #93): claude-agent-acp's `usage_update` carries a
125
125
  // session-cumulative ESTIMATED cost (`cost.amount = message.total_cost_usd`)
126
- // and a context-occupancy scalar (`used`; both read at 0.73.0
127
- // `dist/acp-agent.js:2918-2924`). Both are measured, so backend.ts seals them.
126
+ // and a context-occupancy scalar (`used`; both read at 0.75.1
127
+ // `dist/acp-agent.js:3467-3482`). Both are measured, so backend.ts seals them.
128
128
  // The token partition is NOT declared here: ACP's only token carrier is a
129
129
  // per-turn round-trip aggregate, which is not what pi's four fields mean.
130
130
  sealsTurnAccounting: true,