@junghanacs/entwurf 0.16.0 → 0.17.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.
- package/AGENTS.md +5 -3
- package/CHANGELOG.md +337 -0
- package/README.md +8 -11
- package/VERIFY.md +8 -1
- package/demo/README.md +1 -1
- package/docs/acp-backend-rail.md +25 -14
- package/docs/setup-clean-host.md +24 -10
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/acp-client.js +1 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/backend-adapter.js +34 -10
- package/package.json +10 -10
- package/pi-extensions/lib/acp/acp-client.ts +57 -4
- package/pi-extensions/lib/acp/backend-adapter.ts +78 -9
- package/pi-extensions/lib/acp/backend.ts +578 -18
- package/pi-extensions/lib/acp/claude-acp-launch.js +100 -0
- package/pi-extensions/lib/acp/event-mapper.ts +43 -6
- package/run.sh +129 -32
- package/scripts/check-acp-launch-namespace.ts +127 -0
- package/scripts/check-acp-prompt-lifecycle.ts +145 -2
- package/scripts/check-acp-stop-reason.ts +8 -2
- package/scripts/check-acp-usage-accounting.ts +1074 -0
- package/scripts/check-copilot-birth-hook.ts +28 -1
- package/scripts/check-gate-qualification.ts +4 -2
- package/scripts/check-omp-fresh-preflight.ts +27 -0
- package/scripts/check-setup-qualification.sh +40 -2
- package/scripts/copilot-bridge-oracle.sh +14 -6
- package/scripts/fake-copilot-vendor.sh +4 -2
- package/scripts/lib/pi-record-discovery.ts +47 -0
- package/scripts/mutants/acp-launch-namespace.json +34 -0
- package/scripts/mutants/acp-prompt-lifecycle.json +67 -2
- package/scripts/mutants/acp-stream-hooks.json +4 -2
- package/scripts/mutants/acp-usage-accounting.json +181 -0
- package/scripts/mutants/copilot-birth.json +3 -5
- package/scripts/mutants/pack-install.json +2 -2
- package/scripts/mutants/setup-verdict.json +35 -0
- package/scripts/omp-config-xdev.py +310 -0
- package/scripts/omp-config-xdev.sh +76 -0
- package/scripts/omp-tool-surface.py +61 -10
- package/scripts/raw-acp-child-exit-measure/README.md +285 -0
- package/scripts/raw-acp-child-exit-measure/acp-turn-population.py +89 -0
- package/scripts/raw-acp-child-exit-measure/reaper-correlation.py +47 -0
- package/scripts/smoke-acp-bundled-mcp-live.ts +2 -2
- package/scripts/smoke-acp-cortex-live.ts +2 -2
- package/scripts/smoke-acp-raw-turn-live.ts +1 -1
- package/scripts/smoke-acp-socket-citizen-live.ts +2 -2
- package/scripts/smoke-acp-v2-send-live.ts +2 -2
- package/scripts/smoke-entwurf-v2-matrix-live.ts +60 -10
- package/scripts/smoke-mux-lifecycle-live.ts +46 -2
- package/scripts/smoke-setup-verdict.sh +48 -3
package/AGENTS.md
CHANGED
|
@@ -104,7 +104,9 @@ A `--entwurf-control` pi session is a citizen for the same reason a native bridg
|
|
|
104
104
|
|
|
105
105
|
## Issue queue — a manual sweeper, not a backlog
|
|
106
106
|
|
|
107
|
-
- OPEN issues are
|
|
107
|
+
- **Two caps, and the inner one is what disciplines the work.** OPEN issues are capped at **ten** in total; of those, **implementation issues — the ones that will go out on a branch — are capped at five**. A sixth implementation issue means one closes first; that inner cap does not bend.
|
|
108
|
+
- **What sits outside the implementation five:** research issues, and issues GLG keeps open to look at. These are not slots to be earned or swept — a research lane may stay open across releases, and a north-star issue other repos read may never close. They still count toward the ten, so the queue cannot grow without limit.
|
|
109
|
+
- Classify by destination, not by size: if closing it produces a diff, it is an implementation issue. If closing it produces a decision, a document, or nothing, it is not.
|
|
108
110
|
- A slot is earned by a current defect or executable contract, not by importance, age, or possible future value.
|
|
109
111
|
- Direction, philosophy, frozen invariants and observations without a current action live in `ROADMAP.md`, durable docs, or closed history.
|
|
110
112
|
- Nothing stays open "in case." If a closed problem recurs, it returns as a new issue carrying the new evidence.
|
|
@@ -140,7 +142,7 @@ LIVE=1 AGY_CONVERSATION_ID=<id> ./run.sh smoke-agy-native-push-live
|
|
|
140
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).
|
|
141
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.
|
|
142
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.
|
|
143
|
-
- Run LIVE gates with `PWD` in scratch so session artifacts do not land in the repo.
|
|
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.
|
|
144
146
|
- Release acceptance and evidence levels are defined in [VERIFY.md](./VERIFY.md); recorded host evidence is in [BASELINE.md](./BASELINE.md).
|
|
145
147
|
- A failed gate or evidence downgrade blocks commit/release. Pipes can be connected and the water can still taste wrong.
|
|
146
148
|
|
|
@@ -182,7 +184,7 @@ implement → affected focused gates → independent review → one amendment bu
|
|
|
182
184
|
- 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`.
|
|
183
185
|
- Root pi extensions import TypeBox through `@earendil-works/pi-ai`; do not mix direct `@sinclair/typebox` types.
|
|
184
186
|
- MCP/scripts use explicit `.ts` imports where Node strip-types requires them. Installed operator surfaces route to compiled JS.
|
|
185
|
-
- pi runtime range is `>=0.84.
|
|
187
|
+
- 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.
|
|
186
188
|
- 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.
|
|
187
189
|
|
|
188
190
|
## Working Style
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,343 @@ All notable changes to this project will be documented here. Format follows [Kee
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 0.17.1 - 2026-09-03
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **The C1b red that blocked two of the three 0.17.0 `--cut` runs: a LIVE smoke was waiting exactly
|
|
12
|
+
as long as pi's lock-stale window, and losing by 148ms.** The cause is measured end to end, and
|
|
13
|
+
none of it is a product regression:
|
|
14
|
+
1. pi guards `auth.json` AND `models-store.json` with `proper-lockfile` and reads through that
|
|
15
|
+
lock on every boot (`dist/core/auth-storage.js`), retrying a held lock for `staleMs = 30_000`
|
|
16
|
+
before taking it over.
|
|
17
|
+
2. `terminateChild`'s SIGTERM ends a resident before `proper-lockfile`'s release ever runs, so a
|
|
18
|
+
kill that lands inside the lock window orphans the lock directory. Sweeping 24 kill offsets
|
|
19
|
+
across a boot reproduced it once, at +375ms.
|
|
20
|
+
3. With `~/.pi/agent/models-store.json.lock` orphaned, the next boot → V3 record measured
|
|
21
|
+
**30_148ms** — and **1_114ms** immediately afterwards, once the stale takeover had cleared it.
|
|
22
|
+
4. `smoke-entwurf-v2-matrix-live`'s `BOOT_TIMEOUT_MS` was `30_000`: it stopped looking 148ms
|
|
23
|
+
before C1b's record landed. Hence the exact signature the blocked cuts left — empty stderr, no
|
|
24
|
+
record in any store, and (confirmed in both runs from the host's own process-audit trail) a
|
|
25
|
+
child that was alive for the entire 30s and was killed by the smoke at +30s.
|
|
26
|
+
The control was in the same three runs: `smoke-entwurf-chain-live` does the same two-resident
|
|
27
|
+
dance with `BOOT_TIMEOUT_MS = 45_000` and passed 3/3 while matrix-live failed 2/3.
|
|
28
|
+
- **The bound is now shared and carries its receipt.** `PI_BOOT_TIMEOUT_MS = 45_000` lives in
|
|
29
|
+
`scripts/lib/pi-record-discovery.ts` next to the measurements above, and the five smokes that
|
|
30
|
+
sat on the 30s cliff (`smoke-entwurf-v2-matrix-live`, `smoke-acp-socket-citizen-live`,
|
|
31
|
+
`smoke-acp-bundled-mcp-live`, `smoke-acp-v2-send-live`, `smoke-acp-cortex-live`) now derive
|
|
32
|
+
from it. 30s was the single worst value available: it expires *inside* the takeover.
|
|
33
|
+
`smoke-resident-garden-guard` already used 90s and needed no change.
|
|
34
|
+
- **A boot overrun now names the lock.** `describePiLockResidue()` reports which pi locks are held
|
|
35
|
+
at failure time — read-only, because a live holder and an orphan look identical from outside and
|
|
36
|
+
only pi's own stale protocol may arbitrate them.
|
|
37
|
+
- **Both blocked smokes now say WHICH failure they hit**, which is what made the cause findable.
|
|
38
|
+
`smoke-entwurf-v2-matrix-live` watches each resident (pid, exit code/signal and how many ms in, a
|
|
39
|
+
signal-0 liveness probe taken in the catch before the reaper runs, and a **per-child** stderr tail
|
|
40
|
+
— the old single shared buffer could not say whether C1 or C1b spoke; an empty tail now prints as
|
|
41
|
+
`(empty)` rather than being skipped). `smoke-mux-lifecycle-live` attaches window forensics when a
|
|
42
|
+
nonce callback never comes — pane-pid liveness, `list-panes`, and the last 40 lines of
|
|
43
|
+
`capture-pane`, for both cells. Its launch receipt already tells a human "the window is visible
|
|
44
|
+
and can be read directly"; on a headless gate nobody is there and the window is torn down seconds
|
|
45
|
+
later. Every diagnostic step is best-effort so it can never become the failure.
|
|
46
|
+
|
|
47
|
+
The mux pi-native nonce timeout (the second blocked cut) is NOT this bug and stays open: 300s is
|
|
48
|
+
ten times the stale window, and the codex rail was healthy in that same run (`chain-live` and
|
|
49
|
+
`smoke-omp-fresh-live` both passed on it). The forensics above are what the next occurrence will
|
|
50
|
+
answer with.
|
|
51
|
+
|
|
52
|
+
### Verification
|
|
53
|
+
|
|
54
|
+
- `pnpm run check:toolchain` (biome + `tsc` ×3) — green.
|
|
55
|
+
- **The repair measured against the exact failing condition.** With `models-store.json.lock`
|
|
56
|
+
planted as an orphan, `LIVE=1 ./run.sh smoke-entwurf-v2-matrix-live` is **17/17 PASS in 34s** —
|
|
57
|
+
the stale wait absorbed. The same condition measured 30_148ms to birth, i.e. red under the old
|
|
58
|
+
30_000 bound.
|
|
59
|
+
- **Boot cost, undisturbed**: 1008–1212ms across 80 consecutive boots in the smokes' spawn shape
|
|
60
|
+
(5.1–5.4s under 4× CPU oversubscription). The bound is not sized for boot cost; it is sized to
|
|
61
|
+
clear pi's stale window.
|
|
62
|
+
- **The new matrix-live diagnostic exercised on a real failure path**, at 0 model tokens, by
|
|
63
|
+
pointing the smoke at a bogus provider: `resident C1: pid=… EXITED code=1 signal=null at +1057ms
|
|
64
|
+
— it was gone before the wait ended`, with that child's own stderr beneath it.
|
|
65
|
+
- `LIVE=1 ./run.sh smoke-entwurf-v2-matrix-live` — **17/17 PASS** on the real
|
|
66
|
+
`openai-codex/gpt-5.6-luna` target (undisturbed run).
|
|
67
|
+
- `LIVE=1 ./run.sh smoke-mux-lifecycle-live` — **81 checks passed, exit 0** (real model turns on
|
|
68
|
+
both pi cells and the claude-code cell).
|
|
69
|
+
- 15 iterations of the gate's own C1b neighbourhood (`smoke-resident-garden-guard` → `check-bridge`
|
|
70
|
+
→ `doctor-pi-provider` → `smoke-entwurf-v2-matrix-live`) reproduced nothing — recorded because it
|
|
71
|
+
is what ruled out host load and prior-smoke residue and sent the search to pi's lock.
|
|
72
|
+
- **`LIVE=1 ./run.sh release-gate /tmp/entwurf-release-gate-0.17.1.hb5Q5j --cut`** — **MUST
|
|
73
|
+
PASS=23 FAIL=0 SKIP=0**, **BEHAVIOR PASS=1 FAIL=0 SKIP=0**, exit 0, `cut: OK`. Run on `665191d`,
|
|
74
|
+
2026-09-03 09:46:37 → 10:35:42 KST, with `env -u CLAUDE_CONFIG_DIR -u PI_SESSION_ID -u
|
|
75
|
+
PI_AGENT_ID`. Log: `/tmp/entwurf-release-gate-0.17.1.hb5Q5j/release-gate.log`. This carried
|
|
76
|
+
`pnpm run check:full` (451s) and `./run.sh check-gate-qualification` (**347/347 KILLED**) as MUST
|
|
77
|
+
steps. **This is the green `--cut` 0.17.0 never got**: both cells that blocked it — C1b in
|
|
78
|
+
`smoke-entwurf-v2-matrix-live` and the pi-native nonce in `smoke-mux-lifecycle-live` — passed
|
|
79
|
+
here, on the same host, in one run.
|
|
80
|
+
- **Exact-SHA CI on `665191d`** — `check` (34m20s, carrying `check:full` and
|
|
81
|
+
`check-gate-qualification`), `install-surface`, `artifact-consumer`, all `success`.
|
|
82
|
+
Run: https://github.com/junghan0611/entwurf/actions/runs/33697821117
|
|
83
|
+
|
|
84
|
+
## 0.17.0 - 2026-09-02
|
|
85
|
+
|
|
86
|
+
### Verification
|
|
87
|
+
|
|
88
|
+
Each receipt carries its own scope; none of them is transferable to another commit or host.
|
|
89
|
+
|
|
90
|
+
- **`LIVE=1 ./run.sh release-gate <scratch> --cut`** — **MUST PASS=23 FAIL=0 SKIP=0**,
|
|
91
|
+
BEHAVIOR PASS=1 FAIL=0 SKIP=0, exit 0, `cut: OK`. Run on commit `0379764`, tracked tree
|
|
92
|
+
`cb1a3dc69b521909f0fe6f956e5c0fa36e885d5955f91271dd8e63d30cd918fb`, 2026-09-02
|
|
93
|
+
20:14:51 → 21:04:41 KST. Frozen log: `/tmp/entwurf-0.17.0-evidence/ACCEPTED-cut-release-gate.log`.
|
|
94
|
+
This run carried `pnpm run check:full` and `./run.sh check-gate-qualification`
|
|
95
|
+
(**346/346 KILLED**) as MUST steps. **It is not the 0.17.0 acceptance SHA:** `9479750`
|
|
96
|
+
changed source after it.
|
|
97
|
+
`CHANGELOG.md` is in `package.json` `files` and `check:full → check:package → check-pack`
|
|
98
|
+
enumerates it (`npm pack --dry-run`, required/forbidden names — not a cardinality lock).
|
|
99
|
+
`NEXT.md` is not in `files`. Working-tree cleanliness at cut start was an operator
|
|
100
|
+
observation, not a line in the gate log.
|
|
101
|
+
- **Four reds along the way, all real, none smoothed.** A formatter rejection of the new mutant
|
|
102
|
+
manifest; a doc-floor gate catching the ROADMAP ledger entry quoting the ADAPTER's
|
|
103
|
+
`engines.node ">=22"` as though it were entwurf's own; a qualification run that aborted RED on its
|
|
104
|
+
own self-fence because the worktree was edited mid-run — kept in the record because a
|
|
105
|
+
qualification predating the final bytes is not a receipt, and the fence is what says so; and
|
|
106
|
+
`check-bridge-delivery` failing "artifact is not stale" after the review amendment edited three
|
|
107
|
+
`.ts` files without rebuilding the MCP bridge. That last one is why the tree hash above is quoted
|
|
108
|
+
twice: `mcp/entwurf-bridge/dist/` is gitignored, so the rebuild moved no candidate byte, and the
|
|
109
|
+
two receipts really do cover the same tree. None of the four would have been caught by typecheck
|
|
110
|
+
plus the focused gate.
|
|
111
|
+
- **The first `--cut` attempt was RED — MUST PASS=18 FAIL=5 SKIP=0 — and every one of the five was
|
|
112
|
+
the operator's own environment, not this release.** Recorded because the failure wore a
|
|
113
|
+
convincing disguise: it read as a broken host. `static` was the stale bridge artifact above,
|
|
114
|
+
re-created when a `git checkout`/`merge` bumped every tracked `.ts` mtime past the built `dist`.
|
|
115
|
+
The other four came from running the gate **inside a live entwurf ACP Claude session**, which
|
|
116
|
+
exports two carriers into every child it spawns. `CLAUDE_CONFIG_DIR` points at the ACP overlay,
|
|
117
|
+
whose `settings.json` is `hooks: {}` by design — so the Claude Code children the live smokes
|
|
118
|
+
launch are born with no entwurf SessionStart hook and mint no meta-record, failing
|
|
119
|
+
`smoke-claude-native-resume-live`, `smoke-entwurf-chain-live` and `smoke-mux-lifecycle-live` on
|
|
120
|
+
citizen birth. The same variable made `doctor-meta-bridge` report `installed: absent` and
|
|
121
|
+
`claude mcp list` deny `entwurf-bridge`; with it unset the doctor reads
|
|
122
|
+
`installed: v3 (229fef123589)` matching source and assembled exactly. `PI_SESSION_ID` did the
|
|
123
|
+
analogous damage on the omp axis: `smoke-omp-fresh-live` reported a 240s nonce-callback timeout,
|
|
124
|
+
but the sibling was alive — it was born at 19:58:21 KST and called back at 19:58:28 to the
|
|
125
|
+
AMBIENT caller id rather than the caller the smoke had minted, so the smoke watched the right
|
|
126
|
+
mailbox and saw nothing. That smoke's own source anticipates this exact disguise
|
|
127
|
+
(`scripts/smoke-omp-fresh-live.ts:186`, "the failure would masquerade as a silent sibling").
|
|
128
|
+
Re-run with `env -u CLAUDE_CONFIG_DIR -u PI_SESSION_ID -u PI_AGENT_ID`, all five turned green and
|
|
129
|
+
nothing else changed. That gap — VERIFY.md named `PWD` in scratch but not the carriers — is what
|
|
130
|
+
cost the first cut its 52 minutes; VERIFY.md and AGENTS.md now name the strip.
|
|
131
|
+
- **Qualification lane size.** On the `0379764` cut the total was **335 → 346**, eleven new claims
|
|
132
|
+
in `scripts/mutants/acp-usage-accounting.json` (absent on the 0.16.1 base). `9479750` adds a
|
|
133
|
+
twelfth, `ACP-REBILL-MAIN-LOOP-SCOPE`. Standalone `./run.sh check-gate-qualification` on
|
|
134
|
+
`fee89d3` is **347/347 KILLED** (36m19s, log `/home/junghan/.pi/background/1788355846036-bg03.log`).
|
|
135
|
+
That is a qualification receipt, not a `--cut` receipt.
|
|
136
|
+
- **Three recuts on the D1 tree, all `cut: BLOCKED`.** Each ran
|
|
137
|
+
`env -u CLAUDE_CONFIG_DIR -u PI_SESSION_ID -u PI_AGENT_ID LIVE=1 ./run.sh release-gate <scratch> --cut`.
|
|
138
|
+
All three: MUST PASS=22 FAIL=1 SKIP=0, BEHAVIOR PASS=1. None of the FAILs is D1.
|
|
139
|
+
1. `/tmp/entwurf-cut-0.17.0.WdTkH7` — `smoke-entwurf-v2-matrix-live` C1b: second
|
|
140
|
+
`pi --entwurf-control` wrote no hidden-store record in 30s, stderr empty. Isolated re-run:
|
|
141
|
+
17/17 PASS.
|
|
142
|
+
2. `/tmp/entwurf-cut2-0.17.0.XQcOuR` — `smoke-mux-lifecycle-live` pi-native nonce
|
|
143
|
+
`mux-fresh-call-4bd6391e9783fb24fb1ee74a` never arrived in 300s (claude-code cell passed;
|
|
144
|
+
v2-matrix passed this run). Same shape as the 0.16.1 mux-lifecycle retry.
|
|
145
|
+
3. `/tmp/entwurf-cut3-0.17.0.sfZ8WC` — C1b 30s again. Isolated re-run: 17/17 PASS.
|
|
146
|
+
mux-lifecycle PASS this run.
|
|
147
|
+
GLG 2026-09-03: no fourth cut. Make proceeds with this gap named here, not rounded up to
|
|
148
|
+
`cut: OK`.
|
|
149
|
+
- **`smoke-acp-raw-turn-live`** — PASS on the moved pin, quoted from the accepted `--cut` run's own
|
|
150
|
+
output rather than from a session message: launch source
|
|
151
|
+
`package:@agentclientprotocol/claude-agent-acp` (not PATH fallback), `protocolVersion=1`, model
|
|
152
|
+
`claude-sonnet-5`, `stopReason=end_turn`, reply `"OK"`, 62,998 bytes NDJSON. This is the dep-bump
|
|
153
|
+
track's own named lock, not a substitute for it. An earlier standalone run of the same smoke
|
|
154
|
+
reported 58,189 bytes and another 58,178; the byte count is per-run and is not a fixed
|
|
155
|
+
fingerprint, which is precisely why the number cited here is the one the gate printed.
|
|
156
|
+
- **Recurrence corpus measurement** — cited from the in-source comment at
|
|
157
|
+
`pi-extensions/lib/acp/backend.ts` (`priorTurnInputOutputSum`): 2,398 of 2,410 adjacent pairs
|
|
158
|
+
(99.50%), 2026-05 → 2026-09. The raw pair inventory is not in the frozen receipts; treat it as
|
|
159
|
+
an operator observation recorded in source, not as a cut receipt.
|
|
160
|
+
|
|
161
|
+
### Changed
|
|
162
|
+
|
|
163
|
+
- **ACTION REQUIRED — ACP accounting consumers must read `usage.acp`.** Claude ACP
|
|
164
|
+
`PromptResponse.usage` is the sum across a turn's API round trips, not one request's
|
|
165
|
+
prompt shape. The turn's accounting totals now travel at
|
|
166
|
+
`usage.acp.{input,output,cacheRead,cacheWrite}`; pi's four request-shaped fields remain
|
|
167
|
+
zero, while `usage.totalTokens` remains the vendor's context-occupancy reading. Consumers
|
|
168
|
+
that display ACP token/cache accounting must read the new key — `agent-config`'s
|
|
169
|
+
`pi-extensions/glg-footer.ts` does so at `47b9b95`. The numerator is taken from the
|
|
170
|
+
vendor's ACCOUNTING-GRADE `_meta.quota.model_usage` rows (summed) in preference to the
|
|
171
|
+
main-loop-only `PromptResponse.usage`, because the cost denominator is an adjacent diff of
|
|
172
|
+
the backend's running total and already has that wider scope. Deterministic receipts:
|
|
173
|
+
`scripts/check-acp-usage-accounting.ts` CELLs 1/1b drive pi's real overflow and context
|
|
174
|
+
readers with the incident-scale 4,185,084 cache-read aggregate on a 223,516-token context,
|
|
175
|
+
and CELL 1e drives both token carriers at once so a silent fallback to the narrow one
|
|
176
|
+
cannot pass for a preference.
|
|
177
|
+
- **ACTION REQUIRED — the certified Claude ACP dependency coordinates move together, and
|
|
178
|
+
this release REQUIRES them.** The bundled Claude adapter is
|
|
179
|
+
`@agentclientprotocol/claude-agent-acp` **0.70.0 → 0.73.0**, its wire SDK is
|
|
180
|
+
`@agentclientprotocol/sdk` **1.3.0 → 1.4.0**, and the resolved transitive
|
|
181
|
+
`@anthropic-ai/claude-agent-sdk` is **0.3.232 → 0.3.257**. This is not a refresh riding
|
|
182
|
+
along with the fix: `_meta.quota.model_usage` does not exist before adapter 0.71.0 (added
|
|
183
|
+
by upstream `fad4d10`, "report per-model token usage on prompt responses"), so the
|
|
184
|
+
accounting above has no accounting-grade numerator without this bump. Three adapter minors
|
|
185
|
+
are folded into one certification; the per-bump measurement — declared-dependency deltas
|
|
186
|
+
per tag, the zod floor narrowing at 0.71.0, lock peer-resolution, the reachable/unreachable
|
|
187
|
+
split of the new surface, and the re-measured MCP readiness boundary — is recorded in the
|
|
188
|
+
ROADMAP "Dep bump(별도 트랙)" ledger. Re-run the ACP support gates on any locally
|
|
189
|
+
overridden adapter command before treating that command as covered by these coordinates.
|
|
190
|
+
|
|
191
|
+
### Fixed
|
|
192
|
+
|
|
193
|
+
- **Claude ACP no longer presents a turn aggregate as one request to pi.** That projection
|
|
194
|
+
caused pi's raw overflow reader to compact a live 223,516-token session under a
|
|
195
|
+
1,000,000-token window, invented two phantom cache misses, and silenced the one real miss
|
|
196
|
+
after a 401-minute idle gap. The aggregate is retained as accounting evidence, context
|
|
197
|
+
occupancy remains separate and is carried forward across a turn that reports none, and a
|
|
198
|
+
material re-billed prefix is reported as a PROVEN LOWER BOUND rather than silently hidden.
|
|
199
|
+
Turn cost remains the adjacent difference of the SDK's cumulative estimate, never a local
|
|
200
|
+
reprice. A backend with no measured semantics (cortex) is still sealed not at all.
|
|
201
|
+
- **The re-billed-prefix bound no longer mixes main-context occupancy with wide accounting IO.**
|
|
202
|
+
`readTurnAccounting` prefers `_meta.quota.model_usage`, whose rows also count Task subagents,
|
|
203
|
+
sidechains, and internal compaction. Feeding that `cacheWrite` into a bound whose occupancy is
|
|
204
|
+
main-context inflates the lower bound through both remaining terms, so a warm main prefix can
|
|
205
|
+
announce a miss and attach this turn's dollar figure to it. The bound and its stored prior now
|
|
206
|
+
read `PromptResponse.usage` (main loop) only; `usage.acp` stays the wide totals. Deterministic
|
|
207
|
+
oracle: `scripts/check-acp-usage-accounting.ts` CELL 1f.
|
|
208
|
+
- **A backwards session total no longer asserts a cause it has not measured.** The operator
|
|
209
|
+
diagnostic for a decreasing cumulative cost previously named a conversation reset as "the
|
|
210
|
+
known cause". The adapter's `conversation_reset` handler only switches to a fresh
|
|
211
|
+
conversation and touches no cost (0.73.0 `dist/acp-agent.js:3675-3682`); the documented
|
|
212
|
+
mechanism lives in claude-agent-sdk instead, which states that a mid-session `/clear`
|
|
213
|
+
resets the running total (`sdk.d.ts:4884`). The notice now reports the observation, names
|
|
214
|
+
`/clear` in the vendor's own word rather than paraphrasing it into a different noun, states
|
|
215
|
+
explicitly that the adapter's `conversation_reset` event is NOT that mechanism, and says plainly
|
|
216
|
+
that the cause is not measured here. Naming the disproven event as the documented cause was the
|
|
217
|
+
release's own thesis being violated inside its own diagnostic; it was caught in review.
|
|
218
|
+
|
|
219
|
+
## 0.16.1 - 2026-09-01
|
|
220
|
+
|
|
221
|
+
### Verification
|
|
222
|
+
|
|
223
|
+
Each receipt carries its own scope; none of them is transferable to another commit or host.
|
|
224
|
+
|
|
225
|
+
- **`pnpm run check:full`** — PASS, exit 0, **454s** inside the gate, on the 0.16.1
|
|
226
|
+
versioned tree (HEAD `2e7ceb4` + the uncommitted changelog/version edits).
|
|
227
|
+
- **`./run.sh check-gate-qualification`** — **335/335 KILLED, NOT KILLED 0**, as the
|
|
228
|
+
release-gate MUST step. The first run of this cut reported **334/335**: the single
|
|
229
|
+
miss was `PACK-INSTALL-PIN-MATCHER-BOUNDED` reading `MUTANT-STALE` because `5c1bda5`
|
|
230
|
+
moved `run.sh`'s pin matcher to the 0.84.4 floor and left that mutant's anchor on
|
|
231
|
+
0.84.3, so it matched 0× and the gate had silently stopped asking its question.
|
|
232
|
+
Repaired in `2e7ceb4` and re-run green. A rotted anchor is recorded here rather than
|
|
233
|
+
quietly fixed, because "N killed" only means something when N is the whole set.
|
|
234
|
+
- **`LIVE=1 ./run.sh release-gate /tmp/entwurf-release-gate-0.16.1.m0LUjr --cut`** —
|
|
235
|
+
**MUST: PASS=23 FAIL=0 SKIP=0**, **BEHAVIOR: PASS=1 FAIL=0 SKIP=0**. Log at
|
|
236
|
+
`/tmp/entwurf-release-gate-0.16.1.m0LUjr/release-gate.log`. `smoke-mux-lifecycle-live`
|
|
237
|
+
failed the first run on a pi-native nonce callback that never arrived inside its 300s
|
|
238
|
+
window (the codex rail was measured healthy at the time: 5h 23%, weekly 77%), and
|
|
239
|
+
passed on re-run — recorded because a LIVE step that needed a retry is not the same
|
|
240
|
+
evidence as one that passed first time.
|
|
241
|
+
- **#72 launch/observation receipts** — the launcher's two field claims are not
|
|
242
|
+
unit-provable, so they were measured on the host that produced the failure:
|
|
243
|
+
`/proc/<pid>/cmdline` carries no vendor name, the janitor's own Phase 1 selector run
|
|
244
|
+
against the real `ps` row with its age threshold forced to 0 selects nothing, and a
|
|
245
|
+
real SIGTERM still ends at exit 0 with the frame on stderr. Commands and output in
|
|
246
|
+
`scripts/raw-acp-child-exit-measure/README.md`.
|
|
247
|
+
|
|
248
|
+
### Added
|
|
249
|
+
|
|
250
|
+
- **`setup` composes OMP — the fifth backend is now in the one command.** v0.16.0 admitted OMP
|
|
251
|
+
as a D6 citizen with installers, doctors and inverses for every unit, but `setup_all` had no
|
|
252
|
+
`omp` branch: on an operator host the released package printed a **green** setup summary while
|
|
253
|
+
OMP had nothing installed — no extension, no `mcp.json` entry, no garden id on the status line.
|
|
254
|
+
Presence-driven now, same shape as Copilot: `omp` on PATH composes four independent named
|
|
255
|
+
components (birth → MCP hand → the `tools.xdev` operator setting → receiver), absent is one
|
|
256
|
+
zero-state SKIP, and a detected-but-incomplete unit is a named FAIL owning a nonzero exit.
|
|
257
|
+
Gate: `smoke-setup-verdict` S-8 drives the real composition against a stub vendor and asserts
|
|
258
|
+
every install-state, both extension units in the sandbox agent dir, the effective `xdev-off`
|
|
259
|
+
reading, and idempotence on a second run; S-1 pins `OMP_BIN` absent and requires the SKIP row.
|
|
260
|
+
- **`install-omp-config` / `uninstall-omp-config` — the operator setting is a unit, not a
|
|
261
|
+
documentation step.** `tools: xdev: false` was a hand-edit in `docs/setup-clean-host.md`, so a
|
|
262
|
+
host that skipped it registered every entwurf tool behind the vendor's `xd://` wrapper and the
|
|
263
|
+
doorbell announced a tool the model could not call. The writer owns exactly the lines it adds
|
|
264
|
+
(recorded in its install-state), refuses a symlinked config, refuses a config it cannot parse,
|
|
265
|
+
and refuses an EXPLICIT operator `tools: xdev: true` **by name** rather than overwriting a
|
|
266
|
+
decision — setup surfaces that as a component FAIL for the operator to resolve. The inverse
|
|
267
|
+
takes back only the recorded lines and refuses when the file changed since install.
|
|
268
|
+
- **`docs/adding-a-harness.md` step 10 — an onboarding is not finished until `setup` composes
|
|
269
|
+
it.** The rule the OMP gap forced, with the five conditions a new harness owes (presence-only
|
|
270
|
+
trigger, one independent component row per unit, compose only what an inverse can undo,
|
|
271
|
+
operator settings are units with a refusing writer, pin the probe seam and add an aggregate
|
|
272
|
+
cell) and the shape to watch for: two closed parity loops with no gate owning the edge between
|
|
273
|
+
them.
|
|
274
|
+
|
|
275
|
+
### Changed
|
|
276
|
+
|
|
277
|
+
- **pi floor moved to 0.84.4.** devDependency exact pin, the three peer ranges (`>=0.84.4 <0.85`),
|
|
278
|
+
the `check-pack-install` runtime pins, the pin-leak matcher's version boundary with its synthetic
|
|
279
|
+
fixture, and the pnpm lockfile all move together — the floor is one fact with several spellings,
|
|
280
|
+
and a partial bump is how they drift apart.
|
|
281
|
+
|
|
282
|
+
### Fixed
|
|
283
|
+
|
|
284
|
+
- **#72 — the ACP Claude child is no longer selected by the host janitor that was killing it, and
|
|
285
|
+
a caught signal survives the vendor erasing it.** The child was not crashing: it was being **SIGTERMed from outside**. A
|
|
286
|
+
janitor installed on the operator's host for a *different* harness (openclaw's acpx, upstream
|
|
287
|
+
PR #245) selects the vendor process name `claude-agent-acp` by **argv substring** and kills
|
|
288
|
+
anything older than 900s. entwurf **retains** its child across turns, so that child's age is the
|
|
289
|
+
age of the SESSION, not of a turn — every session past 15 minutes was shot at every 5 minutes.
|
|
290
|
+
The vendor's own handler then turned the signal into `dispose(); process.exit(0)`, so the death
|
|
291
|
+
reached entwurf as exit code 0 with no signal, indistinguishable from a clean shutdown. That is
|
|
292
|
+
why three diagnosis passes retired four candidate causes and still missed it. Measured: **12 of
|
|
293
|
+
12** anomalous terminations across two boots correlate with a reap, pid- and timestamp-locked —
|
|
294
|
+
including the sample that opened the issue (2026-07-30) and the original field report
|
|
295
|
+
(2026-08-16); receipts in `scripts/raw-acp-child-exit-measure/`. Two changes, both inside the
|
|
296
|
+
issue's repair fence (no timeout, no replay, no watcher/supervisor/hidden retry): (a) the default
|
|
297
|
+
launch is now an **entwurf-owned launcher** (`pi-extensions/lib/acp/claude-acp-launch.js`) that
|
|
298
|
+
imports the vendor **in-process** — same process, so there is no child to restart and it cannot
|
|
299
|
+
become a supervisor — carrying a name no vendor-name scanner matches, consuming no argv (the
|
|
300
|
+
vendor's own `argv.slice(1)` self-reinvocation still works), standing down if it would otherwise
|
|
301
|
+
be the only signal listener (an observer that suppressed default termination would make the
|
|
302
|
+
process signal-immune), and dying nonzero without retry if the import fails; (b) a caught
|
|
303
|
+
SIGTERM/SIGINT is recorded as a **typed lifecycle fact** on its own line
|
|
304
|
+
(`launch observed SIGTERM before child exit (sender not attributed)`) via an exact full-line
|
|
305
|
+
control frame that is consumed out of the vendor stderr tail — the tail stays vendor evidence,
|
|
306
|
+
the observation is entwurf's, and sender attribution is explicitly NOT claimed (that needs the
|
|
307
|
+
host journal). `CLAUDE_AGENT_ACP_COMMAND` stays verbatim: an operator who names their own command
|
|
308
|
+
owns the result. **Known trade:** after the name split a host janitor can no longer collect
|
|
309
|
+
entwurf's children even when they genuinely leak, so entwurf now owns that cleanup story.
|
|
310
|
+
**Read the order right: the cause was the host's janitor, and its owner has retired it**
|
|
311
|
+
(`nixos-config e283d92`, 2026-09-01 — `acp-zombie-reaper.timer` disabled). Measured there:
|
|
312
|
+
PR #245 is CLOSED, not merged; that deployment runs `acp.enabled=false` with no ACP agent;
|
|
313
|
+
acpx is not even installed in the running image (`/app/node_modules/acpx` absent, its `.bin`
|
|
314
|
+
entries dangling symlinks); and every reap since the 2026-06-10 ACP removal was a single-process
|
|
315
|
+
misfire matching our pid table. So the change here is **not what stops the symptom** — the
|
|
316
|
+
retirement is. It is defense-in-depth against the class, and that class is wider than this
|
|
317
|
+
issue: the selector reads the WHOLE `ps` line, so on a harness that puts the prompt in argv, a
|
|
318
|
+
session merely *discussing* `claude-agent-acp` matched. Measured 2026-09-01 with zero ACP
|
|
319
|
+
processes alive, the script's own counter read `alive_acp=3` — three ordinary agent sessions,
|
|
320
|
+
one of them the session investigating it. A launcher whose name we own survives the next such
|
|
321
|
+
janitor too. Gates:
|
|
322
|
+
new `check-acp-launch-namespace` (2 mutants) plus `check-acp-prompt-lifecycle` CELLs 12–13,
|
|
323
|
+
where 13 is the negative sibling holding that vendor prose mentioning `SIGTERM` can never forge
|
|
324
|
+
the observation (3 mutants). The primary repair still belongs to the janitor's owner — a cleaner
|
|
325
|
+
should scope by a positive marker it owns, not by a name anyone may share.
|
|
326
|
+
|
|
327
|
+
- **A vendor-written `config.yml` no longer reads as `unreadable`.** omp's own settings writer
|
|
328
|
+
emits `modelRoles:` followed by an indented `{}`, and `scripts/omp-tool-surface.py`'s
|
|
329
|
+
block-only reader returned None for the WHOLE file on that shape — so an untouched operator
|
|
330
|
+
config classified as unreadable and `doctor-omp-mcp` went RED for a reason unrelated to
|
|
331
|
+
`tools.xdev` (measured on a real host, omp 18.0.0). The reader now parses flow collections in
|
|
332
|
+
value position and as a whole child block. `check-omp-fresh-preflight` gains the vendor shapes
|
|
333
|
+
plus `[QK:OMP-XDEV-VENDOR-SHAPE-READABLE]` direct assertions — agreement between the two
|
|
334
|
+
readers could never catch this, since both collapse `unreadable` and `true` into "not false".
|
|
335
|
+
- **The Copilot plugin-row grammar admits the vendor's state token.** Copilot CLI 1.0.81 prints
|
|
336
|
+
`<qualified> (v0.1.0) (enabled)` plus an indented `from <path>` continuation line;
|
|
337
|
+
`copilot_exact_row_version` read the version as `0.1.0) (enabled`, called the row malformed,
|
|
338
|
+
and refused the birth install on a host whose plugin was installed and enabled — `entwurf
|
|
339
|
+
setup` reported `copilot-birth: FAIL`. The grammar now admits exactly one optional
|
|
340
|
+
`(enabled)`/`(disabled)` token; any other trailing token is still malformed. The fake vendors
|
|
341
|
+
in `scripts/fake-copilot-vendor.sh` and `check-copilot-birth-hook.ts` emit the 1.0.81 shape,
|
|
342
|
+
with `[QK:COPILOT-ROW-STATE-TOKEN-ADMITTED]` holding both directions.
|
|
343
|
+
|
|
7
344
|
## 0.16.0 - 2026-08-31
|
|
8
345
|
|
|
9
346
|
This release admits **OMP (`omp`) as the fifth garden backend** and closes the admission
|
package/README.md
CHANGED
|
@@ -60,8 +60,8 @@ native Antigravity / agy
|
|
|
60
60
|
```
|
|
61
61
|
|
|
62
62
|
Claude's `install-meta-bridge`, Copilot's four `install-copilot-*` surfaces, agy's
|
|
63
|
-
`install-agy-{bridge,statusline,hooks}` and OMP's
|
|
64
|
-
|
|
63
|
+
`install-agy-{bridge,statusline,hooks}` and OMP's four `install-omp-{bridge,mcp,config,receive}`
|
|
64
|
+
units are distinct because their lifecycle and delivery transports are
|
|
65
65
|
genuinely different. Codex remains verified probe evidence, not a shipped managed native-citizen
|
|
66
66
|
lane; see [DELIVERY.md](./DELIVERY.md).
|
|
67
67
|
|
|
@@ -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.
|
|
160
|
+
already installed — a compatible `pi` (`>=0.84.4 <0.85`), 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.
|
|
191
|
+
separately (`@earendil-works/pi-coding-agent >=0.84.4 <0.85`). Then point pi at
|
|
192
192
|
the npm-installed package or development clone:
|
|
193
193
|
|
|
194
194
|
```bash
|
|
@@ -213,17 +213,14 @@ SKIP while the detected harnesses are composed.
|
|
|
213
213
|
|
|
214
214
|
A plain MCP registration exposes the bridge tools; a **garden-native** session also
|
|
215
215
|
needs entwurf's lifecycle hook and identity marker. `setup` already composes all of that
|
|
216
|
-
for every harness it detects — you do not paste this list to install.
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
wired. Otherwise this is the repair surface: each unit has its own installer, its own
|
|
220
|
-
doctor with a named refusal, and its own inverse, so a single broken unit can be redone
|
|
221
|
-
without touching the rest.
|
|
216
|
+
for every harness it detects — you do not paste this list to install. This is the repair
|
|
217
|
+
surface: each unit has its own installer, its own doctor with a named refusal, and its own
|
|
218
|
+
inverse, so a single broken unit can be redone without touching the rest.
|
|
222
219
|
|
|
223
220
|
- **Claude Code** (Linux-certified axis) — `install-meta-bridge`, `doctor-meta-bridge`.
|
|
224
221
|
- **Antigravity / agy** — `install-agy-bridge`, `install-agy-statusline`, `install-agy-hooks`, each with a matching `doctor-agy-*`.
|
|
225
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-*`.
|
|
226
|
-
- **OMP (`omp`)** —
|
|
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.
|
|
227
224
|
|
|
228
225
|
Run them as `entwurf <command>`. Which unit a doctor's refusal names, and the clean-host
|
|
229
226
|
walk-through for each harness, live in [docs/setup-clean-host.md](./docs/setup-clean-host.md).
|
package/VERIFY.md
CHANGED
|
@@ -165,8 +165,15 @@ cd "$REPO_DIR" && ./run.sh setup "$PROJECT_DIR"
|
|
|
165
165
|
|
|
166
166
|
### 1.2 Live acceptance (optional)
|
|
167
167
|
|
|
168
|
+
Put `PWD` in scratch so session artifacts do not land in the repo. LIVE gates inherit
|
|
169
|
+
the caller's environment: a pi or ACP session exports `PI_SESSION_ID` / `PI_AGENT_ID` /
|
|
170
|
+
`CLAUDE_CONFIG_DIR` into every child, which masquerades as the smoke's identity or
|
|
171
|
+
strips Claude hooks (`hooks: {}` overlay). Strip those carriers:
|
|
172
|
+
|
|
168
173
|
```bash
|
|
169
|
-
|
|
174
|
+
cd /path/to/scratch
|
|
175
|
+
env -u CLAUDE_CONFIG_DIR -u PI_SESSION_ID -u PI_AGENT_ID \
|
|
176
|
+
LIVE=1 /path/to/entwurf/run.sh release-gate /path/to/scratch --cut
|
|
170
177
|
pi --provider entwurf --model claude-sonnet-5 -p "reply with ok only" # one-turn smoke
|
|
171
178
|
```
|
|
172
179
|
|
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.
|
|
142
|
+
- `pi` on PATH (current floor 0.84.4)
|
|
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)
|
package/docs/acp-backend-rail.md
CHANGED
|
@@ -92,11 +92,11 @@ undifferentiated "supported" column is what let a Claude PASS read as if it also
|
|
|
92
92
|
|
|
93
93
|
| Surface | Declaration | Class | What a green actually says |
|
|
94
94
|
|---|---|---|---|
|
|
95
|
-
| Entwurf package | `0.
|
|
96
|
-
| pi runtime | devDep exact `0.84.
|
|
97
|
-
| ACP wire SDK | `@agentclientprotocol/sdk 1.
|
|
98
|
-
| Claude ACP adapter | `@agentclientprotocol/claude-agent-acp 0.
|
|
99
|
-
| Claude Agent SDK | `0.3.
|
|
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 |
|
|
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 |
|
|
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 |
|
|
102
102
|
| Node | `>=24` (`engines.node`) | **floor** | single axis, derived everywhere else |
|
|
@@ -141,8 +141,10 @@ different reasons, and collapsing them would hide a real risk**:
|
|
|
141
141
|
- **Capability-gated.** AIR typed session failures, the 0.69.0 AIR file-change report, terminal
|
|
142
142
|
output widgets and nested subagent transcripts each test a client capability entwurf does not
|
|
143
143
|
send, so the adapter itself keeps the legacy path.
|
|
144
|
-
- **Advertised but never called.** Some surfaces carry no capability prerequisite at all —
|
|
145
|
-
`providers/list` / `providers/set` / `providers/disable`
|
|
144
|
+
- **Advertised but never called.** Some surfaces carry no capability prerequisite at all — the
|
|
145
|
+
`providers/list` / `providers/set` / `providers/disable` trio added in 0.70.0 is advertised
|
|
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
|
|
146
148
|
unreachable only because the common loop never invokes them (nor `logout`). Nothing upstream
|
|
147
149
|
enforces that; it is our own call-site discipline, and it stops holding the moment we use one.
|
|
148
150
|
|
|
@@ -255,13 +257,22 @@ caller-session `_meta`, and cross-machine certification.
|
|
|
255
257
|
|
|
256
258
|
A backend can return `newSession` before its declared MCP server is callable. This was
|
|
257
259
|
observed intermittently on the Claude rail and directly on Cortex's private `mcp.json`
|
|
258
|
-
path. Neither `claude-agent-acp` 0.
|
|
259
|
-
readiness fence
|
|
260
|
-
(
|
|
261
|
-
|
|
262
|
-
`
|
|
263
|
-
|
|
264
|
-
|
|
260
|
+
path. Neither `claude-agent-acp` 0.73.0 nor the Cortex landing adds a client-side
|
|
261
|
+
readiness fence over a session's declared MCP servers, and entwurf's common loop
|
|
262
|
+
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:
|
|
271
|
+
AIR typed failures and the AIR file-change report stay capability-gated and
|
|
272
|
+
unadvertised by entwurf; `providers/set` / `providers/disable` stay advertised
|
|
273
|
+
unconditionally and uncalled; native subagents, async tasks, session forks, session
|
|
274
|
+
titles, permission-mode kinds and clear-context planning are all new-but-uncalled.
|
|
275
|
+
This bump changes no readiness behavior and closes no part of #72.)
|
|
265
276
|
|
|
266
277
|
### 11-7-a/b. Instrument and first measurement
|
|
267
278
|
|
package/docs/setup-clean-host.md
CHANGED
|
@@ -12,10 +12,10 @@ 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.
|
|
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 |
|
|
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
|
-
| OMP (`omp`) | optional, operator-installed;
|
|
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) |
|
|
19
19
|
| Antigravity `agy` | optional, operator-installed and authenticated | native-push citizen |
|
|
20
20
|
| Cortex Code | optional, operator-installed and authenticated | Cortex ACP backend |
|
|
21
21
|
|
|
@@ -72,11 +72,12 @@ cd ~/repos/gh/entwurf
|
|
|
72
72
|
|
|
73
73
|
This owns `~/.local/bin/entwurf` as a symlink to that checkout's `run.sh` and fails if the
|
|
74
74
|
link is foreign, outside PATH, or shadowed by another command. It detects and wires
|
|
75
|
-
pi/Claude/agy/Copilot by presence and prints a computed per-component PASS/SKIP/FAIL summary — a
|
|
75
|
+
pi/Claude/agy/Copilot/OMP by presence and prints a computed per-component PASS/SKIP/FAIL summary — a
|
|
76
76
|
detected harness that cannot be completed makes setup exit nonzero. A detected `copilot`
|
|
77
77
|
composes all four native units (birth → MCP → receiver → visible footer) with independent
|
|
78
|
-
per-unit verdicts (#86 C3b)
|
|
79
|
-
|
|
78
|
+
per-unit verdicts (#86 C3b), and a detected `omp` composes its own four (birth → MCP →
|
|
79
|
+
`tools.xdev` setting → receiver) the same way; §4 and §4b keep the explicit per-unit
|
|
80
|
+
install/doctor/inverse surfaces for repair.
|
|
80
81
|
|
|
81
82
|
### 1.1 User-scope ownership (one shared registration, one recorded owner)
|
|
82
83
|
|
|
@@ -109,7 +110,7 @@ packageRoot↔installerRoot coupling mismatch and a package/provider managed-pat
|
|
|
109
110
|
Install the exact release floor, then wire the project:
|
|
110
111
|
|
|
111
112
|
```bash
|
|
112
|
-
npm install -g @earendil-works/pi-coding-agent@0.84.
|
|
113
|
+
npm install -g @earendil-works/pi-coding-agent@0.84.4
|
|
113
114
|
pi --version
|
|
114
115
|
|
|
115
116
|
cd ~/entwurf-smoke
|
|
@@ -117,7 +118,7 @@ entwurf install .
|
|
|
117
118
|
pi -e "$(npm root -g)/@junghanacs/entwurf" --list-models entwurf
|
|
118
119
|
```
|
|
119
120
|
|
|
120
|
-
The supported range is `>=0.84.
|
|
121
|
+
The supported range is `>=0.84.4 <0.85`. It is a hard minimum: installing this
|
|
121
122
|
release onto a 0.83.x pi host upgrades the runtime rather than keeping the older
|
|
122
123
|
minor. A host using only the external MCP bridge can skip pi until it needs a
|
|
123
124
|
control socket; no delivery rail launches a pi process.
|
|
@@ -213,16 +214,26 @@ sibling launched onto a default-config host would start, look healthy, and be un
|
|
|
213
214
|
the callback tool at all. A refusal there names `omp-callback-tool-uncallable` and opens
|
|
214
215
|
nothing.
|
|
215
216
|
|
|
217
|
+
`setup` composes all four omp units when `omp` is on PATH, and the setting is one of them —
|
|
218
|
+
`entwurf setup` writes `tools: xdev: false` itself. The verbs below are the REPAIR path, not
|
|
219
|
+
the install:
|
|
220
|
+
|
|
216
221
|
```bash
|
|
217
222
|
entwurf install-omp-bridge # the birth extension, into <omp agent dir>/extensions/
|
|
218
223
|
entwurf install-omp-mcp # the omp-native entwurf-bridge server
|
|
224
|
+
entwurf install-omp-config # the operator setting: tools.xdev: false
|
|
219
225
|
entwurf install-omp-receive # the receiver extension: mailbox watch + doorbell
|
|
220
226
|
|
|
221
227
|
entwurf doctor-omp-bridge
|
|
222
|
-
entwurf doctor-omp-mcp
|
|
228
|
+
entwurf doctor-omp-mcp # also owns the tools.xdev runtime axis
|
|
223
229
|
entwurf doctor-omp-receive
|
|
224
230
|
```
|
|
225
231
|
|
|
232
|
+
The setting writer owns exactly the lines it adds and records them, so `uninstall-omp-config`
|
|
233
|
+
takes back its own bytes and nothing else. It refuses a symlinked config, a config it cannot
|
|
234
|
+
parse, and — deliberately — an EXPLICIT `tools: xdev: true`: that is your decision, not drift,
|
|
235
|
+
so setup names it as a component FAIL for you to resolve instead of overwriting it.
|
|
236
|
+
|
|
226
237
|
Order matters only in one direction: the receiver JOINS the citizen birth mints, and
|
|
227
238
|
announces a tool the MCP hand provides. Install it without them and it will log
|
|
228
239
|
`arm-deferred`, give up after ~20s, and `doctor-omp-receive` will name the missing sibling
|
|
@@ -234,8 +245,11 @@ retires it. A task subagent arms nothing. While nothing is armed, dispatch to th
|
|
|
234
245
|
id is the honest `mailbox-undeliverable` refusal — an unarmed receiver is a legible state,
|
|
235
246
|
not a broken one.
|
|
236
247
|
|
|
237
|
-
|
|
238
|
-
|
|
248
|
+
`[측정]` This section used to end by saying `setup` did not compose these and the verbs had to
|
|
249
|
+
be run by hand. That is what v0.16.0 actually shipped, and on an operator host it printed a
|
|
250
|
+
green `setup` summary with OMP entirely absent — no extension, no MCP entry, no visible garden
|
|
251
|
+
id. The composition landed afterwards; `docs/adding-a-harness.md` step 10 is the rule that
|
|
252
|
+
keeps the next harness from repeating it.
|
|
239
253
|
|
|
240
254
|
Both installers resolve the omp agent directory the way omp itself does, and REFUSE rather
|
|
241
255
|
than guess when an inherited `PI_CODING_AGENT_DIR`, `PI_CONFIG_DIR` or `PI_PROFILE` makes it
|