@junghanacs/entwurf 0.12.9 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/AGENTS.md +135 -248
  2. package/BASELINE.md +46 -7
  3. package/CHANGELOG.md +35 -0
  4. package/CONTRIBUTING.md +4 -2
  5. package/DELIVERY.md +5 -4
  6. package/README.md +46 -18
  7. package/VERIFY.md +10 -6
  8. package/demo/demo.sh +7 -1
  9. package/docs/setup-clean-host.md +10 -4
  10. package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +159 -66
  11. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/backend-adapter.js +148 -5
  12. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/config.js +16 -4
  13. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/models.js +66 -7
  14. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/overlay.js +190 -3
  15. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/tool-surface.js +12 -4
  16. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-core.js +26 -9
  17. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-deliverability.js +10 -6
  18. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-fact-provider.js +15 -17
  19. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-facts.js +13 -13
  20. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-resume-args.js +31 -41
  21. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-self-address.js +8 -9
  22. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-contract.js +32 -25
  23. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-decider.js +4 -4
  24. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-lock.js +5 -4
  25. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-production.js +9 -11
  26. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-spawn-production.js +3 -1
  27. package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js +17 -20
  28. package/mcp/entwurf-bridge/src/index.ts +190 -82
  29. package/package.json +7 -3
  30. package/pi/settings.reference.json +1 -1
  31. package/pi-extensions/acp-provider.ts +20 -10
  32. package/pi-extensions/entwurf-control.ts +69 -27
  33. package/pi-extensions/lib/acp/augment.ts +60 -2
  34. package/pi-extensions/lib/acp/backend-adapter.ts +183 -8
  35. package/pi-extensions/lib/acp/backend.ts +5 -1
  36. package/pi-extensions/lib/acp/config.ts +19 -5
  37. package/pi-extensions/lib/acp/engraving.ts +3 -1
  38. package/pi-extensions/lib/acp/event-mapper.ts +10 -3
  39. package/pi-extensions/lib/acp/models.ts +69 -7
  40. package/pi-extensions/lib/acp/overlay.ts +234 -3
  41. package/pi-extensions/lib/acp/tool-surface.ts +12 -4
  42. package/pi-extensions/lib/entwurf-core.ts +26 -9
  43. package/pi-extensions/lib/entwurf-deliverability.ts +10 -6
  44. package/pi-extensions/lib/entwurf-fact-provider.ts +15 -17
  45. package/pi-extensions/lib/entwurf-facts.ts +13 -13
  46. package/pi-extensions/lib/entwurf-resume-args.ts +40 -46
  47. package/pi-extensions/lib/entwurf-self-address.ts +8 -9
  48. package/pi-extensions/lib/entwurf-v2-contract-schema.ts +7 -2
  49. package/pi-extensions/lib/entwurf-v2-contract.ts +32 -25
  50. package/pi-extensions/lib/entwurf-v2-decider.ts +8 -8
  51. package/pi-extensions/lib/entwurf-v2-lock.ts +5 -4
  52. package/pi-extensions/lib/entwurf-v2-production.ts +14 -11
  53. package/pi-extensions/lib/entwurf-v2-spawn-production.ts +3 -1
  54. package/pi-extensions/lib/meta-session.ts +17 -20
  55. package/run.sh +200 -62
  56. package/scripts/agy-bridge-config.py +249 -49
  57. package/scripts/agy-bridge.sh +59 -14
  58. package/scripts/check-acp-carrier-augment.ts +34 -2
  59. package/scripts/check-acp-cortex.ts +668 -0
  60. package/scripts/check-acp-provider-surface.ts +50 -6
  61. package/scripts/check-acp-sdk-surface.ts +22 -11
  62. package/scripts/check-acp-session-reuse.ts +64 -1
  63. package/scripts/check-agy-permission-matrix.py +655 -0
  64. package/scripts/check-entwurf-deliverability.ts +9 -6
  65. package/scripts/check-entwurf-fact-provider.ts +7 -7
  66. package/scripts/check-entwurf-facts.ts +12 -12
  67. package/scripts/check-entwurf-resume-args.ts +22 -60
  68. package/scripts/check-entwurf-self-address.ts +187 -4
  69. package/scripts/check-entwurf-v2-contract.ts +2 -2
  70. package/scripts/check-entwurf-v2-production.ts +5 -5
  71. package/scripts/check-entwurf-v2-spawn-production.ts +6 -2
  72. package/scripts/check-entwurf-v2-surface.ts +327 -14
  73. package/scripts/check-gate-qualification.ts +787 -0
  74. package/scripts/check-meta-identity-consumers.ts +1 -1
  75. package/scripts/check-probe-cli-shim.ts +879 -0
  76. package/scripts/check-probe-ordering.ts +2450 -0
  77. package/scripts/check-shell-quote.ts +5 -4
  78. package/scripts/fixtures/probe-cli-shim +20 -0
  79. package/scripts/fixtures/probe-mcp-server.ts +168 -12
  80. package/scripts/lib/mutation-qualify.ts +794 -0
  81. package/scripts/lib/probe-acp-turn.ts +207 -0
  82. package/scripts/lib/probe-cli-shim.ts +464 -0
  83. package/scripts/lib/probe-cli-target.ts +165 -0
  84. package/scripts/lib/probe-event-log.ts +383 -0
  85. package/scripts/lib/probe-verdict.ts +1213 -0
  86. package/scripts/mutants/acp-augment.json +30 -0
  87. package/scripts/mutants/acp-cortex.json +196 -0
  88. package/scripts/mutants/agy-permission.json +144 -0
  89. package/scripts/mutants/meta-identity.json +17 -0
  90. package/scripts/mutants/probe-ordering.json +1032 -0
  91. package/scripts/mutants/self-address.json +59 -0
  92. package/scripts/mutants/v2-surface.json +87 -0
  93. package/scripts/smoke-acp-cortex-live.ts +392 -0
  94. package/scripts/smoke-acp-ordering-probe-live.ts +848 -0
  95. package/scripts/smoke-acp-raw-turn-live.ts +1 -1
  96. package/scripts/smoke-acp-v2-send-live.ts +33 -18
  97. package/scripts/smoke-agy-install-state.sh +205 -20
  98. package/scripts/smoke-entwurf-v2-matrix-live.ts +3 -2
  99. package/pi-extensions/lib/entwurf-mailbox-guard.ts +0 -100
  100. package/scripts/check-entwurf-mailbox-guard.ts +0 -266
package/AGENTS.md CHANGED
@@ -1,285 +1,172 @@
1
1
  # AGENTS.md — Maintainer Guidelines for entwurf
2
2
 
3
- For agents that own this repo: invariant principles + reproducible verification, not release-story drift.
4
-
5
- > **Direction (read first).** This repo is the **entwurf capability package**:
6
- > a v2 garden-citizen dispatch substrate (entwurf-core) + a meta-bridge + an
7
- > ACP plugin + the pi adapter that hosts it today. v1 entwurf verbs are done
8
- > and gone; v2 is the spine. ACP is **not** the project's center — it is one
9
- > **plugin** that enters as a provider/model on a host `--entwurf-control`
10
- > pi session — which is *already* a v2 socket-citizen — rather than minting
11
- > citizens of its own (#38: *"ACP is a plugin, not the boundary."*).
12
- > Package/repo/runtime identity is now `entwurf`; pi remains an
13
- > adapter/runtime contract, not the center. The deferred #38 coordinate is a
14
- > later physical extraction of entwurf-core into a separate repo, not this
15
- > rename. Fresh sibling minting (the old v1 `entwurf` verb) stays deferred —
16
- > see the branch NEXT.
3
+ For agents that own this repo: invariant principles and reproducible verification, not release-story archaeology.
17
4
 
18
- ## North Star One Forged Screwdriver
19
-
20
- `entwurf`를 만질 때 먼저 이 형상을 붙든다.
21
- 이 저장소는 스위스 아미 나이프가 아니다. 두 번째 하네스도 아니다.
22
- 이것은 **담금질된 드라이버 한 자루**다. 작고, 명시적이고, 자기가 맡은 접점에서만 강해야 한다.
23
-
24
- 영문 원칙만 읽으면 이것이 스펙처럼 보일 수 있다. 여기서는 그렇게 읽지 마라.
25
- 이 섹션은 기능 목록이 아니라 **판단의 중심축**이다.
26
-
27
- ### 먼저 붙들 정체성
28
-
29
- - **entwurf가 주어이고 pi는 한 adapter다.** pi는 지금 이 repo가 가장 깊게 붙어 있는 하네스지만 4번째 하네스일 뿐이다. 이 repo는 pi의 세션 모델, transcript, UI, tool semantics와 경쟁하지 않는다.
30
- - **다른 하네스의 세션은 형제다.** Claude Code, Codex, Antigravity는 학교가 달라도 모두 frontier 친구들이다. native bridge는 증명된 lifecycle/transport가 있는 세션만 garden id로 호명 가능한 citizen으로 등록할 뿐, 누구를 다른 누구로 위장시키지 않는다.
31
- - **표면은 달라도 능력의 존엄은 낮추지 않는다.** 어떤 backend에서 `mcp__...`가 직접 보이지 않는다고 해서, 곧바로 그 backend를 "못하는 존재"로 취급하지 마라. 먼저 capability를 보고, 그 capability가 어떤 surface로 열리는지 확인하라.
32
- - **substrate는 결정적 dispatch만 맡는다.** target liveness를 fact로 읽고, intent와 곱해 transport를 고른다. 그 이상 마술을 부리면 안 된다.
33
- - **명시는 주변기류보다 강하다.** 숨겨진 transcript hydration, ambient MCP scanning, invisible tool claims, giant magical system prompt, 근거 없는 서사를 만들지 마라.
34
- - **하네스의 도구 표면을 좁히는 것은 결핍이 아니라 규율이다.** entwurf가 backend를 몰 때(ACP Claude · pi-native 분신) 서브에이전트도 투두 도구도 없이 좁은 tool surface를 yolo로 돈다(ACP backend는 격리된 overlay 안에서) — pi에서 배운 **힣의 드라이버**다. backend가 못해서가 아니라, 한 자루 드라이버가 두 번째 오케스트레이터로 번지지 않게 하는 강제다. 이걸 강제해야 "힣의 드라이버를 쓴다"고 말할 수 있다. (backlog 형태는 `ROADMAP.md` tool narrowing.)
35
- - **entwurf는 부속품이 아니라 분신을 연다.** spawn-bg resume된 세션은 runtime-isolated peer다. worker처럼 다루거나 권한 경계를 흐리지 마라.
36
- - **증거가 말을 훈육한다.** README / AGENTS / CHANGELOG / VERIFY / BASELINE / runtime smoke가 받쳐주지 않으면, 작업을 중지하고 사용자에게 질문하라!
37
-
38
- ### 이 섹션을 읽는 올바른 방식
39
-
40
- 이 문단의 목적은 에이전트가 다음 질문을 **스스로** 하게 만드는 것이다.
41
-
42
- - 나는 지금 tool 이름을 보고 있는가, 아니면 capability를 보고 있는가?
43
- - 나는 backend의 비대칭을 정직하게 기록하고 있는가, 아니면 그것을 핑계로 형제성을 포기하고 있는가?
44
- - 나는 사용자가 묻지 않아도 될 것을 되묻고 있는가?
45
- - 나는 지금 두 번째 하네스를 만들고 있는가, 아니면 드라이버 한 자루를 더 단단하게 만들고 있는가?
46
-
47
- ### 금지할 오독
5
+ > **Direction.** This repo is the **entwurf capability package**: a v2 garden-citizen dispatch substrate, native-harness bridges, an ACP plugin, and the pi adapter that hosts that plugin today. `entwurf` is the subject; pi is one harness adapter. ACP is a plugin, not the boundary (#38). V1 verbs are gone. V2 addresses existing citizens; fresh sibling creation remains deferred. Current work and ordering live in [NEXT.md](./NEXT.md).
48
6
 
49
- - 어떤 tool이 schema에 직접 보인다고 해서, 곧바로 "이 backend는 여기까지"라고 결론내리는 것
50
- - surface 차이를 capability 포기로 번역하는 것
51
- - 문서에 적힌 asymmetry를 면책조항처럼 사용하는 것
52
- - `entwurf`를 하네스 런타임이나 범용 AI 작업실로 설명하는 것 — pi가 하네스 중 하나이고, 이 repo는 garden-citizen dispatch capability다
53
- - MCP를 자동 맥락 검색이나 ambient tool scanning처럼 설명하는 것 — explicit injection만 허용된다
54
- - `entwurf_v2`를 "새 분신을 만드는 도구"로 설명하는 것 — v2의 4 transport(control-socket / spawn-bg resume / meta-mailbox / native-push)는 전부 **기존** garden citizen 대상이다. fresh sibling 생성은 별개 능력이다
55
- - 사용자가 이미 철학과 방향을 준 문제를 다시 사용자에게 되묻는 것
56
-
57
- 릴리즈 이야기와 개별 기능은 주변을 돈다.
58
- 중심은 언제나 이것이다: **thin substrate / explicit MCP / sibling-based entwurf / deterministic dispatch / observability / evidence-first language / capability dignity across sibling sessions**.
59
-
60
- ## What This Repo Is
61
-
62
- An **entwurf garden-citizen dispatch substrate** + a **meta-bridge** + an **ACP plugin** + a **pi adapter**. Pi stays a harness/runtime, not the project center; every addressed session keeps its own identity.
7
+ ## North Star One Forged Screwdriver
63
8
 
64
- - **Native-harness bridges**: Claude Code's global `SessionStart` hook creates a mailbox-backed garden meta-session; Antigravity's `PreInvocation` imprint creates/attaches a native-push garden citizen and writes its sender marker. Both preserve native transcript/auth/runtime ownership, but they are different rails and install surfaces. Codex has probe evidence only, not a shipped managed native-citizen lane.
65
- - **v2 dispatch (`entwurf_v2`)**: one verb that delivers to / wakes an *already-identified* garden citizen. A pure decider reads transport-specific liveness facts and picks from a frozen table keyed on **target state × intent**: live pi + fire-and-forget → **control-socket**; dormant pi + owned-outcome → **spawn-bg resume**; active self-fetch + fire-and-forget → **meta-mailbox**; probe-alive native-push + fire-and-forget → **native-push**. Every complementary pair is an honest reject. It does **not** mint new siblings.
66
- - **ACP plugin** (one pi-adapter ingress): registers the package provider `entwurf` as a pi session provider/model and drives the chosen ACP backend (Claude first; vendor/governed CLIs like Cortex next) under an isolated config overlay. It owns the backend process, the overlay, and the per-backend ACP dialect — **not** socket-citizenship. The host `--entwurf-control` pi session that selected the ACP model is *already* a v2 socket-citizen; the plugin does **not** mint a socket / peers / citizen layer. It is not the substrate and not a second harness. v1 entwurf verbs (`entwurf` / `entwurf_resume` / `entwurf_send`) are gone for good; the ACP plugin is a fresh build on the v2 core (0.11.0's `acp-bridge.ts` is a behavior oracle, not architecture to re-center). See §ACP Plugin Boundary.
9
+ `entwurf`는 스위스 아미 나이프나 번째 하네스가 아니다. 이것은 **담금질된 드라이버 자루**다: 작고, 명시적이고, 맡은 접점에서만 강해야 한다.
67
10
 
68
- ## Code Principle Crash, Don't Warn
11
+ - **entwurf가 주어이고 pi는 adapter다.** pi는 가장 깊게 붙은 하네스지만 다른 하네스보다 높은 정체성 계층이 아니다.
12
+ - **다른 하네스의 세션은 형제다.** Claude Code, Codex, Antigravity, pi는 각자의 transcript/auth/runtime을 소유한다. 증명된 lifecycle과 transport가 있을 때 같은 garden address space의 citizen이 된다.
13
+ - **능력을 surface 이름으로 재단하지 않는다.** 도구 이름이나 transport 비대칭은 capability 차이이지 존재의 등급이 아니다.
14
+ - **substrate는 결정적 dispatch만 맡는다.** record에서 identity를 읽고, rail별 liveness와 caller intent로 transport를 고른다. 숨은 hydration, ambient MCP scanning, 근거 없는 tool claim을 만들지 않는다.
15
+ - **좁은 tool surface는 규율이다.** entwurf가 backend를 몰 때 sub-agent/todo 없이 한 자루 드라이버로 움직인다. 두 번째 orchestrator로 자라지 않게 한다.
16
+ - **entwurf는 부속품이 아니라 분신을 연다.** resumed/spawned session은 runtime-isolated peer이지 disposable worker가 아니다.
17
+ - **증거가 말을 훈육한다.** README, source, gates, VERIFY, BASELINE이 받치지 않는 강한 주장은 멈춘다.
69
18
 
70
- Code in this repo is used by agents as infrastructure.
19
+ 판단할 묻는다:
71
20
 
72
- > **Never warn. Throw.**
21
+ 1. tool 이름을 보는가, capability를 보는가?
22
+ 2. backend 비대칭을 정직하게 기록하는가, 형제성을 포기하는 핑계로 쓰는가?
23
+ 3. 두 번째 하네스를 만드는가, 드라이버 한 자루를 단단하게 만드는가?
24
+ 4. 이미 주어진 방향을 되묻는가, 실행 가능한 다음 증거를 만드는가?
73
25
 
74
- Warnings make agents blame themselves and flail. Broken tool state must surface as broken tool state.
26
+ ## Architecture
75
27
 
76
- - Bad config throw (e.g. `McpServerConfigError`); same for bad path / bad model id. No fallback.
77
- - `catch {}` only for environment probing (optional package detection, ldd exit code variance).
78
- - `console.warn` only in stderr diagnostic lines (read by operators, not agents).
28
+ - **entwurf-core (v2)** owns garden-id addressing, peer facts, liveness interfaces, dispatch decisions, rail choice, and delivery evidence.
29
+ - **Record authority** owns citizen identity. Every addressable pi, Claude Code, Antigravity, or future Codex citizen uses the same V3 `MetaIdentity` schema. `backend` selects capability/rail behavior; it does not create an identity hierarchy.
30
+ - **pi adapter** attaches a pi session to a record at `session_start`, hosts the record-keyed control socket, and exposes the native pi tool surface.
31
+ - **Native bridges** register already-running native sessions without taking over their transcript or auth: Claude Code is mailbox/self-fetch; Antigravity is probe-backed native-push; Codex has probe evidence but no shipped managed citizen lane.
32
+ - **ACP plugin** registers provider `entwurf` inside a pi host session and drives a backend under an isolated overlay. The host pi session is already a record-backed socket citizen; the plugin does not mint another citizen/socket/peer layer.
33
+ - **One delivery verb:** `entwurf_v2` addresses an existing garden id. Current routes are live control-socket send, dormant spawn-bg resume, active self-fetch mailbox enqueue, and probe-alive native-push. Complementary state×intent pairs reject honestly. Fresh creation is a separate future capability.
79
34
 
80
35
  ## Hard Rules
81
36
 
82
- 1. **One surface name, hard-cut cutover**: provider/model/routing strings are `entwurf`. No permanent runtime aliases, legacy provider-id accept, or dual-read of old state. If existing operator state must be helped across, do it as an explicit one-shot cutover or a documented break, never as hidden dual routing. The `provider:` routing strings (`getRegistryRouting`, `model-lock.ts`) are **load-bearing** — they are identity, not residue.
83
- 2. **Dispatch is a function of liveness, not session type.** `entwurf_v2` never asks "is this a resume or a send" up front it probes the target on its own rail and routes: live pi→control-socket, dormant pi→spawn-bg resume, active self-fetch→meta-mailbox, live native conversation→native-push. State is computed, never stored (a stored liveness bit is a lie).
84
- 3. **A reject is honest, never cosmetic.** When a target cannot receive (dead, drifted identity, wrong state×intent), the decider returns a reject no `✓ delivered`, no `.msg` written, no signal poke. Silent degraded "delivery" is forbidden.
85
- 4. **MCP injection**: only via explicit `mcpServers` wiring. No ambient `~/.mcp.json` scanning, no automatic retrieval.
86
- 5. **Meta-record authority is the record body, never the filename — and the ACTIVE store is certified as a whole, by one contract.** Identity comes from the `.meta.json` body; no filename is ever parsed for authority. `certifyActiveStore` is the single contract the install doctor and all four identity writers (pi birth, Claude hook, agy imprint, `entwurf_register_native`) share: every `.meta.json` entry is a **regular file** (a symlink is refused, never followed — its bytes live where this store has no ownership), **readable by the live schema**, **named by its own body**, and the **unique holder of its `nativeSessionId`**. Body-is-authority and name-must-agree are not in tension: the body decides *what a record says*, while a record parked under the wrong name is unreachable by garden id and so corrupts the *active store*. Any defect fails the whole write BEFORE it happens, naming the fresh-cut verb — including a defect involving neither of the writer's own ids, because a narrow "is there a record for MY native id" scan is how a store ends up certified by the doctor and written to by the runtime under two different contracts (the weaker one always wins at runtime). **The contract binds both directions, at the granularity each surface can afford:** the store-WIDE scan runs on identity writes and in the doctor, while the plain TARGETED read (`readMetaIdentityByGardenId`, used by `entwurf_self`, mailbox primitives and sender-marker trust) holds the per-entry half — a non-regular-file record is refused and never followed, and body/filename drift fails loud, both naming the verb. V2 dispatch and pi resume use the stronger `readAddressableMetaIdentity` described next. Certifying only on write while the read followed a symlink made the rule true exactly where nobody was addressed. Store-wide *uniqueness* is checked on the read snapshot wherever it is both affordable and load-bearing (#52, 0.12.9): the **discovery** scan (`listAllMetaIdentities` → `entwurf_peers`) already holds every record, so a duplicated `nativeSessionId` costs nothing to see and neither rival is listed as a citizen — a facts surface may not mint the ownership the certification refuses; and the **dispatch** read (`readAddressableMetaIdentity`, used by v2 `resolveTarget` and the pi resume) fails loud, because that is the moment a record becomes an ADDRESS and a duplicate there injects one live conversation twice or resumes one transcript under two garden-id locks. It is deliberately NOT held on the per-message relay path (mailbox poke, sender-marker trust, `entwurf_self`) — those keep the per-entry half, and a call-relay that re-scans the store per message is a design this repo does not have. A RIVAL is a record that could be ADDRESSED INSTEAD, which is narrower than a file whose bytes carry the same id: a symlinked entry is not a candidate and is NEVER READ (rule 1 — following it to judge it would break the rule while enforcing it), and a drifted or unparseable neighbour is not a candidate because no garden id can reach it; all three stay certification defects and listing diagnostics, they just may not blind a healthy citizen. The inverse is loud: a REGULAR `.meta.json` this process cannot read might BE the duplicate, so it refuses rather than skipping (ENOENT alone is absence — a file that vanished mid-scan is not in the store). Both store-wide read scans take entries WITH THEIR KIND from one shared `readActiveStoreEntries`; the former name-only bindings read each path without first carrying its kind, which is exactly how both `entwurf_peers` surfaces followed a symlinked record the doctor refused. A kind attached to a NAME is still only half the rule, because `lstat`-then-`readFileSync(path)` classifies one entry and reads another — swap the final component in between and the read follows the symlink while every settled-store test stays green. Record bytes therefore come from exactly one reader, `readStoreRecordFile`, shared by the store-wide reader and the targeted read: `O_NOFOLLOW` (a symlink fails the OPEN, before a byte is read), `O_NONBLOCK` (deciding on the fd would otherwise let a fifo block the open forever, a hazard classify-then-open never had), kind decided by `fstat` on THAT file description, and `closeSync` in a `finally`. It does not flatten errno — raced-away (`ENOENT`, skipped), unreadable (`EACCES`, loud) and swapped (`ELOOP`, refused) must stay distinguishable, because the rival scan's raced-away skip is built on that distinction and a wrapper that erased it would turn every concurrent birth into a refused dispatch. **That reader does not replace the `lstat` classification in front of the targeted read: the two layers hold different things, and merging them into "one enforcement point" is a regression, not a cleanup** (caught in cross-review before shipping). The classification decides POLICY on a settled store WITHOUT OPENING anything, which is what lets a socket, a device or a mode-000 directory earn the certification's own sentence — `open` answers `ENXIO`/`EACCES` there, errnos about availability and permission rather than regularity, so an fd-only read starts calling the host unreadable where the doctor calls the entry non-regular, and one store again has two contracts. The fd layer decides the RACE after a regular snapshot, and its errno verdicts collapse back onto the settled sentences through one PURE classifier (`classifyRecordReadFailure`: `ENOENT`→absent; `ELOOP`/`ENXIO`/not-regular-on-fstat→irregular; everything else→unreadable) so a race never teaches a second vocabulary for one state of the world. Those branches are unreachable from any settled store precisely because the classification answers first — so they are pinned with synthetic errnos on the pure function, never by a store on disk. A gate cell that claims to pin a branch nothing can reach is worse than no cell: it passed with the whole mapping deleted (2026-07-26 cross-review). The threat is not only foreign corruption: `upsertMetaSession` certifies and then writes, which is not a transaction, so two concurrent births can mint two garden ids for one native session on a host where nothing was ever corrupted. A meta-record is nullable-at-birth (`model`/`transcriptPath` null until known). Production reads schemaVersion-3 records only, and the repo carries NO legacy reader or migrator: a record the live schema cannot read fails loud naming the fresh-cut verb (`entwurf meta-bridge-fresh-cut` — archive the whole generation, open an empty one). The active store provides no cross-generation address or resume continuity; sessions flow, and memory lives in the native transcripts and the embedding axes, never in the bridge.
87
- 6. **GC reclaims process resources only — never data.** meta-records and transcripts (the denote-id memory layer) are preserved; dormant/stale entries are archived/TTL'd, not deleted.
88
- 7. **This is not a second harness**: no prompt reconstruction, no transcript hydration, no tool result ledger, no harness emulation. Native bridges front only a garden id plus their narrow delivery rail (Claude mailbox or agy native-push); they do not scrape transcripts or run a replacement control daemon.
89
- 8. **Auth boundary is deployment-surface-agnostic**. This repo does not provide, copy, proxy, decrypt, or mediate any backend's credentials. Native-harness sessions read whatever auth state is visible in their own process filesystem; nothing here moves that.
90
- 9. **Native-push is not a mailbox or pi socket in disguise.** Antigravity replyability is `recordBacked probeAlive`; it gets no receiver marker, no `watchArmed`, and no spawn/resume authority. Its `agentId` remains `meta-session/antigravity`. The pid+start-key sender join assumes serialized model invocation per agy process: two conversations concurrently invoking under one pid are unsupported and must never be claimed safe.
91
- 10. **A green dev clone is not a working package — and a green package on the maintainer's host is not a working consumer.** Node refuses `--experimental-strip-types` below `node_modules`, so any surface an operator can invoke must reach compiled JS when installed. This class has shipped four times (start.sh 0.12.1, store-doctor 0.12.4, plugin hook 0.12.5, agy imprint + three operator commands 0.12.7) because the fence was crossed by hand, per surface, and the source-tree floor cannot see it. There is now exactly one crossing — `run_ts` in `run.sh` — and two gates that hold it: `check-install-surface` (structural) and `check-pack-install` (drives the real tarball, in CI). A new `.ts` entrypoint routes through `run_ts` or it does not ship. Dev-only gates have no compiled twin by design and must be REFUSED under an installed package, never silently skipped — a `.sh` dev gate refuses in its own body, since `scripts/` ships whole and run.sh's dispatch is not the only way in. **`check-pack-install` is still a maintainer-shaped proof**: the checkout is present, every tree is operator-owned, and the install is project-local, so a surface that writes beside the installed package or depends on the repo being nearby is green there and broken for a real consumer. `check-install-container` (#51 C, own required CI job) closes that: one candidate tarball, read-only, into a container that has never seen this repo — non-root `npm install -g`, resolution through the PATH shim, a frozen package root, and a regular-file path+sha256 manifest fence across `install-meta-bridge`; the evidence line records the canonical tarball path + sha256 and the Node image id/repository digest. Default CI packs once into a temp dir; release acceptance passes a caller-preserved tarball through `ENTWURF_CANDIDATE_TGZ` and the gate consumes that exact file without re-packing, so `npm publish <same.tgz> --tag repair` can publish the accepted bytes. The two are not redundant detectors of one defect: the **freeze is a permission-level consumer fact** (the cell actually refuses the write, EACCES, the way a real consumer's host would), while the **manifest fence is the detector** — and it is exactly a regular-file path+sha256 comparison, not a whole-tree guarantee: it reads no permissions, ownership or symlink targets. A freeze at the package root alone is demonstrably insufficient (a write one directory down sails past it and only the fence sees it). Model the consumer's world, never a stricter one: a blanket `chmod -R a-w` freeze produced false reds because `cp -r` propagates modes into the installer's own assembly target, which no `sudo npm i -g` consumer can reach.
92
- 11. **Verification must not rewire the operator's own install.** An offline smoke that writes a live `~/.claude` / `~/.gemini` / `~/.pi` path uninstalls the operator as a side effect of "testing". Swap `HOME` **and every already-exported writable `XDG_*` root** (`XDG_DATA_HOME`: install-state · `XDG_STATE_HOME`: the imprint log · `XDG_CACHE_HOME`: the statusline gid cache): moving HOME alone still writes below the inherited roots. This class struck three times in two days — hard-verify 2026-07-13 (DATA, scratch scripts), `check-pack-install`'s own drives 2026-07-14 (DATA + STATE, inside run.sh), and `smoke-user-scope-citizen` 2026-07-14 (fake `PI_CODING_AGENT_DIR` paired with the real XDG ownership state, so its inverse followed the real `managedSettingsPath` and removed the live MCP key). `check-install-surface` S5 is a static **tripwire** over `scripts/*.sh` source only: it catches a literal live path, one hop of aliasing, (S5b) HOME-without-XDG swaps, and (S5c) a mutating `run.sh` drive left unsandboxed at any root that command writes — the agent dir, `XDG_DATA_HOME`, and, for `install`/`setup`, `HOME` itself, because `ensure_agent_dir_symlinks` hard-codes `$HOME/.pi/agent` and never reads the agent-dir override (so sandboxing `PI_CODING_AGENT_DIR` is not isolation for those commands) — but it cannot see a path assembled across variables, an embedded heredoc, or run.sh itself. **A tripwire keyed to one syntactic form is not a tripwire**: S5c first shipped matching only the inline-env drive, and a review mutation walked the identical leak straight past it by hoisting the same override into an `export` one line up. Match the drive, then demand the isolation — never the other way round. The dynamic complement is `check-pack-install`'s **outer self-fence**, which runs after every success or early-failure path: the operator's real `$XDG_DATA_HOME/entwurf` tree must be byte-identical, and the gate-specific fake agy marker count in the real `$XDG_STATE_HOME/entwurf/agy-imprint.log` must not increase (mutation-checked). Read a green S5 as "no obvious destructive line", never as "verification is sandboxed" — the real guarantee is running the offline floor under a swapped HOME+XDG, which is still open. LIVE gates are the only surfaces that may drive the real host, and they say so in their name.
93
- 12. **A doctor reports runtime truth and ownership truth separately.** Read the target's own semantics before calling a host broken. agy matches `mcp(*)` and `mcp(<server>)` against our tool wherever those rules appear, so an operator's broad `allow` already grants `entwurf_v2` reporting that host as "NOT granted, agy prompts on every call" was a false red about a working surface. Installers still take the narrowest rule they need; doctors distinguish **we own this** from **someone else's rule is carrying it** from **it is genuinely broken**. Install-state is evidence only when it parses, names its required managed-path field as an absolute path, and that normalized path equals the live target this host reads; corrupt or foreign-target state is a failure even when the live command itself resolves. Ownership beats coverage: an element the state records as ours that has since vanished stays a failure even while an operator's broader rule keeps the surface working (a whole-file settings relink produces exactly this shape). Conversely, broken ownership state does not justify saying a visibly configured runtime command is absent — report both axes honestly and keep the final verdict red.
94
- 13. **A native-hook owner is structural, not a topology guess — and the structure is the exec form.** Shell-form command hooks do not expose one portable process tree: under the same Claude Code version we observed both a direct hook→Claude join and a retained `/bin/bash -c` wrapper, and ordinary tail-exec tests never reproduced the trigger. That form is retired, not patched. The meta-bridge declares the **exec form** — `command` is the shipped `hook-launch.sh`, `args` is the real argv — so no shell exists on the launch path, the launcher `exec`s the payload and preserves the pid, and the hook's parent IS Claude on every host (#51 B2, measured at Claude Code 2.1.217). The hook therefore reads `process.ppid` directly; the `$PPID` carrier, the ancestry walk, and the missing-carrier contract are **gone**, and re-introducing any of them means the manifest stopped feeding the owner. **But `process.ppid` is only the owner when the launcher was actually on the path, so `hook-launch.sh` stamps a non-identity `ENTWURF_META_HOOK_LAUNCH` provenance token and the hook writes NO sender/receiver marker without it.** **The authorized launch path is necessary, not sufficient: `process.ppid` must also be a pid that CAN own a session, and that question belongs to one shared predicate.** `isPlausibleOwnerPid` (safe integer, `> 1`, in `meta-session.ts` beside `classifyMarkerOwner`) is asked by both writers — the Claude hook and the agy imprint — by the `writeMetaSender/ReceiverMarker` boundary itself, by all three readers (fresh-cut inspect, sender-marker read, receiver-marker read) and by the sender-identity candidate filter. It is deliberately NOT part of `classifyMarkerOwner`, whose pure rule stays correct: asked about pid 1 that rule answers `live`, and honestly so. The layer above is where the claim fails. **That layer is a POLICY bound to the currently certified axis, not a law of processes**: on a Linux desktop/workstation host init is the service manager and every native harness descends from a login session, so `ownerPid <= 1` is always residue — but a container that runs the harness AS pid 1 is a real shape where it would name a genuine owner. Such a host is **unsupported and fails closed**: the writers refuse the marker, so that session keeps its meta-record and never claims reply-addressability. Widening the axis takes new evidence and a new contract, never a quiet loosening of the predicate. Splitting the two is what let the Claude hook refuse `<= 1` while the agy imprint still wrote `> 0` and every reader accepted `> 0`, and a single leftover `ownerPid: 1` marker — observed as a shell-form hook reparented to init before it read its own parent — then blocked that host's `meta-bridge-fresh-cut`: **the one action the refusal prescribes, quiescing the sessions, cannot refute an owner that is init**, and `0.12.8` named that same cut as the only repair for its pre-v3 store (#53 A, measured 2026-07-25). So a marker failing the predicate is **refuted by construction, not proven dead** — legacy (any pre-fix writer that was reparented) or corrupt (a non-integer pid no writer here ever produced), never claimed to have one single origin: the cut clears it as residue and reports it under its own `refuted:` line, never folded into the dead count. A new marker consumer asks the predicate or it reopens this. This is not the retired carrier wearing a new name: the carrier smuggled a *pid* that had to be ancestry-checked, while this token carries no identity at all and answers only "was the authorized launch path taken". It is what keeps the upgrade mismatch fail-closed — an already-open Claude session still holding the OLD cached command reaches the new hook with a shell wrapper as its parent, and without the token that wrapper would be minted as an owner. Deleting it is never a cleanup. **entwurf requires Claude Code `>=2.1.217` and enforces that floor itself, because upstream gives no fail-loud:** an older Claude passes `plugin validate` on the exec manifest (unknown-key passthrough), then at runtime drops `args`, runs `command` alone, and reports the hook as `exit_code: 0, outcome: success` — measured at 2.1.138. `hook-launch.sh` refusing an empty argv is that silence made loud; installer and doctor refuse the version outright; there is no shell-form fallback for older versions. `check-hook-launch-topology` drives the shipped argv for real — including a plugin path containing a space, `$`, a backtick, and `;&` — and `check-claude-floor-coherence` keeps the floor one number derived from `package.json` `entwurf.claudeCodeFloor`. Evidence stays tiered: B/B2 are direct-native observations from actual 2.1.138/2.1.217 sessions on one NixOS host; the Linux artifact-consumer's fake Claude, planted cache, stand-in owner and `/proc` bridge are fixtures that prove package/oracle behavior, never a second native-host acceptance. **The doctor is the release oracle, so its exit 0 must mean every required layer was measured, never that a layer was skipped.** It resolves the ONE artifact Claude loads (`claude plugin list --json`.installPath; an ambiguous multi-version cache is refused, never guessed), classifies the installed *launch form* by name across all three owner hooks — a shell-form or launcher-less exec manifest is refused by name, not reported as unreadable drift — and then requires the live MCP↔marker join. Missing live evidence is `NOT CERTIFIED`, a failure worded distinctly from a broken install. The #51 repair cut has **Linux as its only currently certified axis**: install refuses Darwin because `/proc`-based live bridge discovery cannot certify it yet, doctor stays `NOT CERTIFIED`/nonzero there, and uninstall alone retains Darwin support so legacy state is not stranded. This is an evidence boundary, not a permanent macOS impossibility; future native validation may reopen the lane. `check-meta-doctor-oracle` holds this: a healthy fixture must reach PASS and twenty-one planted defects must each turn it red *naming their own cause*. An oracle with an optional central evidence layer is not an oracle.
37
+ 1. **One surface name, hard cut.** Runtime/provider/routing identity is `entwurf`. No permanent aliases, legacy readers, or hidden dual routing. One-shot migration or documented break only.
38
+ 2. **The record is the sole garden address authority.** A socket, env var, marker, filename, model id, or transcript id is never an independent address axis. `PI_SESSION_ID` is only a child-process carrier for the garden id already established by pi's record birth.
39
+ 3. **All citizens share one identity contract; rails differ by capability.** The control-socket liveness domain currently contains backend `pi`; self-fetch and native-push have different predicates. Branch on capability/domain, not on a privileged notion of “pi citizen.”
40
+ 4. **Dispatch is computed from live facts.** Never store liveness. Never infer send/resume from session type before resolving the target and probing its rail.
41
+ 5. **Rejects are real.** Wrong intent, dead/drifted identity, undeliverable mailbox, ambiguous address, or indeterminate probe returns a reject and mutates nothing. No cosmetic success or silent fallback.
42
+ 6. **MCP is explicit.** Only explicit `mcpServers` wiring. No ambient `~/.mcp.json` scan or automatic retrieval.
43
+ 7. **Meta-record store contract is one contract.** Record body is authority; filename must agree with the body; every active entry is a regular non-symlink file readable by the live V3 schema; `nativeSessionId` ownership is unique. Identity writers and doctor certify the whole active store before writing. Address-bearing reads use `readAddressableMetaIdentity`; targeted relay reads keep the documented per-entry contract. `readStoreRecordFile`/`O_NOFOLLOW` and the lstat policy layer are both load-bearing—do not merge or bypass them. No legacy reader/migrator: quiesce and run `entwurf meta-bridge-fresh-cut` when the generation is unreadable. Source and gates: `meta-session.ts`, `check-meta-*`, `check-fresh-cut-gate`.
44
+ 8. **GC reclaims process resources, never memory/data.** Records and transcripts are preserved or archived; they are not casually deleted.
45
+ 9. **This is not a second harness.** No prompt reconstruction, transcript hydration, tool-result ledger, credential mediation, or harness emulation. Each backend owns auth and transcript state.
46
+ 10. **Native-push is its own rail.** It is not a mailbox or pi socket in disguise. Antigravity replyability is record-backed plus probe-alive; no receiver marker, watch state, or resume authority is invented for it.
47
+ 11. **Package proof must model a consumer.** Operator entrypoints reach compiled JS when installed; `run_ts` is the single TS crossing. Keep `check-install-surface`, `check-pack-install`, and the checkout-invisible `check-install-container` distinct. A green clone is not a green tarball or consumer.
48
+ 12. **Offline verification never rewires the operator.** Sandbox `HOME`, `PI_CODING_AGENT_DIR`, and every writable `XDG_*` root. LIVE gates alone may touch the real host and must say so. Keep static tripwires and dynamic outer self-fences; neither substitutes for the other.
49
+ 13. **Doctors report runtime truth and ownership truth separately.** Runtime coverage does not prove entwurf owns the configuration; broken ownership does not erase visibly working runtime configuration. Final verdict remains red when either required axis fails.
50
+ 14. **Native hook ownership is structural.** Claude hooks use the shipped exec-form launcher and provenance token; marker writers/readers share `isPlausibleOwnerPid`; no shell-form fallback, ancestry guess, or retired pid carrier. entwurf requires Claude Code `>=2.1.217` and enforces that floor itself because upstream gives no fail-loud — an older Claude validates the exec manifest, then drops `args` at runtime and reports success. The number is derived from `package.json` `entwurf.claudeCodeFloor`, never retyped as a second source. Currently certified axis is Linux desktop/workstation. Source/gates: `hook-launch.sh`, `meta-session.ts`, `check-hook-launch-topology`, `check-claude-floor-coherence`, `check-meta-doctor-oracle`.
51
+ 15. **Crash, don't warn.** Bad config/path/model/store state throws. Empty catches are only for bounded environment probing; operator diagnostics go to stderr.
52
+
53
+ Detailed incident histories belong in CHANGELOG/issues/BASELINE and source-adjacent comments, not in this prompt. When a concise rule and old archaeology disagree, verify source + gate and repair the stale prose.
95
54
 
96
55
  ## ACP Plugin Boundary
97
56
 
98
- `entwurf-core` (the v2 substrate) is the center; **ACP is one plugin**, never the boundary (#38). Plugins supply read-only facts the core already asks for; they do not become the core, a memory layer, or a second harness (#39).
99
-
100
57
  | Layer | Owns |
101
58
  |---|---|
102
- | **entwurf-core (v2)** | garden id · peer identity · liveness fact interface · dispatch decision · delivery evidence · rail choice (socket / mailbox / spawn / native-push) |
103
- | **ACP plugin** | ACP backend process lifecycle · config overlay (isolation + tool-narrowing + identity-carrier materialization) · per-backend ACP dialect quirks · backend health / turn evidence — **NOT** socket-citizen registration or liveness/addressability facts (those are the host `--entwurf-control` session's, supplied via socket-discovery) |
104
- | **ACP plugin MUST NOT become** | a memory DB · a task planner · an orchestrator · a second harness · a mailbox-citizen impersonation |
105
-
106
- - **Sibling equality is a citizen-level property, not a rail-level one.** Every sibling is addressable (peers-visible, garden-id-addressed, `entwurf_v2`-reachable, replyable when its rail proves a return path). The *rail* differs by lifecycle: an ACP-backed pi resident is a **socket-citizen**; Claude Code is a **mailbox-citizen**; agy is a **native-push citizen**. Missing a mailbox on socket/native-push rails is right-sizing, not discrimination.
107
- - **Durable memory is the authored common record** (`~/org`, botlog, agenda, Denote, andenken). entwurf lets peers move across that record layer; it never replaces it.
108
- - **ACP enters as a model/provider, not a socket layer.** The ACP plugin registers as a pi session's provider/model and spawns the backend under an overlay; **socket-citizenship is supplied by the host `--entwurf-control` pi session**, not minted by the plugin. The plugin never builds a new socket registry, peers layer, or citizen protocol — over-designing one is the failure mode to avoid (`socket-discovery` is model-agnostic, so an ACP-model session is already a citizen).
109
-
110
- ### Operating boundaries (trust invariants — survive any re-implementation, #15)
111
-
112
- These claims must stay true on every install surface; they are the first thing a re-implementation silently drops, so they are pinned here:
113
-
114
- - `entwurf` does **not** provide, resell, or bypass Claude/vendor credentials, tokens, or subscription access. It connects only to the operator's **existing local authenticated backend** through an explicit plugin boundary.
115
- - No auth bypass, no subscription sharing, no hidden transcript restoration.
116
- - Expert escape hatches are **explicit and documented**, never accidental backdoors.
117
- - The plugin **fails loud / fails closed** when an invariant is broken.
59
+ | **entwurf-core** | identity/fact interfaces · dispatch table · delivery evidence · rail choice |
60
+ | **ACP plugin** | backend process lifecycle · isolated overlay · tool narrowing · per-backend ACP dialect · turn evidence |
61
+ | **ACP plugin does not own** | citizen registration · socket registry · peer protocol · memory DB · planner/orchestrator · auth |
118
62
 
119
- ## Verification
63
+ - One `entwurf` provider, model-id routing, adapter resolved once at turn entry. Backend-specific settings remain opaque behind `adapterSettings`.
64
+ - The common turn sequence stays backend-invariant: spawn → initialize → newSession → enforceModel → prompt → event map.
65
+ - Rich operator/project context rides the **first user message augment**, not a large system prompt. The actual callable schema is the tool truth; prose never grants a tool.
66
+ - A backend may have no carrier or use launch-time model pinning; those asymmetries stay inside its adapter.
67
+ - Claude is the reference adapter. Cortex is the second landed adapter (0.13.0): session-scoped dual-HOME containment, overlay-private `mcp.json` projection (its ACP server ignores the wire `mcpServers` param), `CORTEX_HOME` presence refusal, per-turn set-model. Audit record and contract: `docs/acp-backend-rail.md` §11-8.
68
+ - entwurf never supplies, copies, proxies, decrypts, or bypasses vendor credentials/subscriptions. It uses the operator's existing local authenticated backend.
120
69
 
121
- Two axes, both required.
70
+ ## Citizen Identity and Dispatch
122
71
 
123
- **Deterministic + smoke gates** (`./run.sh`, wired into `pnpm check`):
72
+ ### One record axis
124
73
 
125
- ```bash
126
- pnpm typecheck # 3-config tsc fence (root + mcp + scripts)
127
- pnpm check # full static floor: lint + typecheck + every check-*/smoke-* below
128
- ./run.sh check-entwurf-v2-matrix # the decider's state×intent table, read as an SSOT (REAL decideDispatch)
129
- ./run.sh check-entwurf-v2-decider # + -contract / -lock / -release / -send / -send-fallback / -mailbox / -runner / -production / -surface / -spawn / -spawn-production
130
- ./run.sh check-meta-session # + -v3-record / -mailbox-state-write / -receiver-marker / -capability-source / -identity-consumers / -listing (one live schema; no legacy readers — see check-fresh-cut-gate for the generation boundary)
131
- ./run.sh check-meta-doctor-oracle # detection power of the release oracle: healthy fixture reaches `doctor: PASS`, 21 planted defects each turn it FAIL naming their own cause
132
- ./run.sh check-native-push-adapter # agy probe/route leaf; separate from pi socket and mailbox liveness
133
- ./run.sh check-agy-sender-identity # record-backed pid/start-key sender resolution + ambiguity refusal
134
- ./run.sh smoke-agy-install-state # MCP + exact permission ownership + honest inverse (140)
135
- ./run.sh smoke-agy-statusline-state # ambient garden identity install surface (69)
136
- ./run.sh smoke-agy-hooks-state # PreInvocation birth/sender hook install surface (44)
137
- ./run.sh check-entwurf-bridge-boot # the MCP entwurf-bridge stands up + exposes the v2/native-register tool set
138
- ./run.sh check-install-surface # structural strip-types fence: run_ts is the only crossing, every operator command has a compiled twin, offline smokes never write the real $HOME
139
- ./run.sh check-install-container # Linux artifact CONSUMER (#51 C, own CI job): one candidate .tgz, read-only, into a checkout-invisible node:<engines-major> cell — non-root `npm install -g`, PATH shim, frozen package root, MCP tools/list, install-meta-bridge under a path+sha256 byte-fence, strict doctor. Default pack-once temp; ENTWURF_CANDIDATE_TGZ consumes an exact preserved file without re-pack. SKIP without Docker; ENTWURF_REQUIRE_DOCKER=1 makes that RED
140
- ./run.sh check-bridge /path/to/project # entwurf-bridge direct MCP smoke (tools/list + protocol/negative-path)
141
- ./run.sh check-auth-boundary # ACP plugin no-auth sentinel present + no legacy-ENV apiKey literal (trust invariant, code-level)
142
- ./run.sh check-acp-provider-surface # provider registers curated Claude anchor + streamSimple wired to the real streamShellAcp backend
143
- # The legacy v1 ACP gates (sentinel / session-messaging / xt-tool-surface) and the
144
- # v1-only meta smokes (smoke-meta-mailbox / smoke-meta-sender-identity, both calling
145
- # the gone entwurf_send tool) were REMOVED in the 0.12 cutover (2026-06-27). Any
146
- # re-test of those axes belongs on the entwurf_v2 surface as a fresh gate.
147
- ```
74
+ A `--entwurf-control` pi session is a citizen for the same reason a native bridge session is: it has a V3 meta-record.
148
75
 
149
- **Live release gate** (opt-in, owns the merge decision):
76
+ - pi owns its native session id, filename, transcript, name, `/new`, `/fork`, `/clone`, and `/resume` lifecycle.
77
+ - `birthPiCitizen` upserts `(backend:"pi", nativeSessionId)` and receives the stable `gardenId` from the record.
78
+ - The control socket is `~/.pi/entwurf-control/<gardenId>.sock`; a record-less socket is a diagnostic, never a citizen.
79
+ - `PI_SESSION_ID` and `PI_AGENT_ID` propagate the record-established identity to child MCP processes. They are carriers, not a second authority.
80
+ - If record birth fails, no socket starts and no `PI_SESSION_ID` is exported.
81
+ - Reopening the same pi native session attaches to the same record; in-process replacement creates/attaches the replacement's own record.
82
+ - Dormant resume requires record existence, transcript-header ↔ `record.nativeSessionId` integrity, and model preservation.
150
83
 
151
- ```bash
152
- LIVE=1 ./run.sh release-gate /path/to/scratch # two-tier: MUST (release-blocking, owns exit code) + BEHAVIOR (advisory)
153
- LIVE=1 ./run.sh smoke-acp-socket-citizen-live # S1: a real ACP-model --entwurf-control resident is a first-class socket-citizen (peers + get_info), turn-free (no backend, no stub fire)
154
- LIVE=1 AGY_CONVERSATION_ID=<id> ./run.sh smoke-agy-native-push-live # real agy probe/register/direct-inject evidence; conversation-id gated, outside aggregate release-gate
155
- ```
84
+ ### Capability domains, not rank
156
85
 
157
- The MUST tier is the necessary condition ("green" = MUST PASS, FAIL=0); BEHAVIOR is advisory — the `smoke-resident-garden-guard` positives (a model-in-loop garden identity turn). Run every live gate with `PWD=scratch` so sessions never land in the repo's own session dir.
158
-
159
- **Agent-driven verification** ([VERIFY.md](./VERIFY.md)): self-recognition/transcript agreement L1; objective MCP calls L2; on-disk/process L3; direct-native L4; soak L5.
160
-
161
- If a gate fails or a claim drops below its needed evidence level, do not commit. Pipes can be connected and the water can still taste wrong.
162
-
163
- ## Entwurf
164
-
165
- Uses `entwurf` instead of `delegate` to avoid ecosystem collisions. spawn-bg resume creates a sibling, not a worker.
166
-
167
- - **Surface** — MCP `entwurf-bridge`: `entwurf_v2`, `entwurf_self`, `entwurf_peers`, `entwurf_inbox_read`, `entwurf_register_native` (explicit/manual fallback for an already-running native conversation). pi-native (`pi-extensions/entwurf-control.ts`): `entwurf_v2`, `entwurf_peers` tools (#50 C4 removed the socket-scan `/entwurf-sessions` command). The v1 `entwurf` / `entwurf_resume` / `entwurf_send` tools and the `/entwurf` / `/entwurf-send` / `/entwurf-status` commands are **removed**.
168
- - **`entwurf_v2` is the one delivery verb.** Given a garden id, it classifies the target (live pi vs. dormant pi vs. mailbox meta-session vs. native-push citizen — a bare garden id does not reveal this) and routes correctly. It does **not** mint a fresh sibling: spawn-bg resumes an *already-identified* citizen, while native-register binds an *already-running* conversation. Fresh creation was the v1 `entwurf` verb and remains deferred.
169
- - **`entwurf_peers`** is a read-only fact surface (liveness / capability / identity / cwd-history). Do not bake verb-routing (`resumable`/`sendable`) into the fact layer; routing is the decider's job.
170
- - **`entwurf_self`** returns the authoritative identity envelope (pi-session env, or a trusted meta-session sender marker) and is identity-required. Its socketPath/mailboxPath lines are the CALLER's own transport diagnostics, not an identity surface — that is why they survive #50 C4 while `entwurf_peers` forbids every socket-shaped key/path (the peers listing points at OTHER citizens, where a socket path is an address claim).
171
- - The target registry (`pi/entwurf-targets.json` + `setup:links`) is **gone** (#50 C3): v2 never spawns from a model tuple — `entwurf_v2` resumes an already-identified record-backed citizen, and dormant-resume authorization is record existence + the transcript-header ↔ `record.nativeSessionId` integrity check (the old `requireEntwurf` name-tag and resume-marker env are deleted). Bridge-extension routing survives as `getRegistryRouting` (caller-supplied tuple, ← resolve-acp-bridge). Identity Preservation Rule: no model override on resume.
172
- - `PI_SHELL_ACP_V2_ONLY=1` was the v1-refusal flag; with v1 removed on this branch its guard (`entwurf-v2-only.ts`) is gone too. `runEntwurfV2` was always flag-clean.
173
-
174
- > **Source-agnostic does not mean harness-agnostic.** 어디서 던지든 — GLG / sibling / external MCP host — entwurf 의 *target* 은 garden citizen 이다. spawn-bg resume 의 spawn surface 는 pi 자식 프로세스만 띄운다 (`pi --entwurf-control` keep-alive resident). 외부 MCP host 가 닿을 때도 target 은 이미 식별된 citizen 이어야 한다. *Model* 은 free axis (어느 형제 학교 모델이든), *spawn target* 은 harness 정합 axis.
175
-
176
- > **Naming pair.** *Entwurf* (기투, projection-of-self) — a resident agent throws siblings forward (resume / messaging). The resident-side counterpart is *Mitsein* (공존, being-with), defined in the resident's own knowledge base (cwd-scoped, not a global persona). This repo owns the entwurf substrate; resident-side conventions live where the resident wakes.
177
-
178
- ### Resident identity — the record is the address (#50 C2)
179
-
180
- A `--entwurf-control` session is a garden citizen because it has a **meta-record**, not
181
- because its session id has a particular shape. pi mints its own id (a uuidv7 is normal);
182
- `birthPiCitizen` upserts `(backend:"pi", nativeSessionId)` at `session_start`, the record
183
- mints the `gardenId`, and everything addressable hangs off that one string.
184
-
185
- - **Launch:** `pi --entwurf-control …`. No `--session-id` injection — that was the old
186
- launcher's job and it is gone, together with `run.sh new-session-id`'s role in launching
187
- (the generator itself stays; the record uses it to mint garden ids).
188
- - **In-process new/resume:** pi's own. `/new`, `/fork`, `/clone` and RPC session replacement
189
- all just fire `session_start`, which attaches the new session as its own citizen and
190
- rebinds the socket to its address. The `/gnew` command, the pre-switch cancels and the
191
- garden-format hard exit are deleted — there is no id to police (LOCKED PROTOCOL 2).
192
- - **Socket:** `~/.pi/entwurf-control/<gardenId>.sock`, keyed on the RECORD's id. A socket
193
- carrying pi's session id is the pre-cut address and a gate failure.
194
- - **`PI_SESSION_ID`** carries the gardenId, so every child MCP process reads back a
195
- routable address (`entwurf_self`), never pi's internal id.
196
- - **Failure is loud, not cosmetic:** if the record cannot be written (unreadable store,
197
- duplicate native id, an unreadable store naming the fresh-cut verb) the control server is
198
- refused, `PI_SESSION_ID` stays unset, and the reason is on stderr. An unaddressable
199
- resident must not survive quietly — that is the guard's surviving purpose.
200
- - **Status label = 🪛 (the forged screwdriver, the North Star), NOT the word "entwurf".**
201
- `🪛 ready` before the first assistant turn (session file not on disk → model changeable),
202
- `🪛 <gardenId>` after (file written → model locked).
203
- - **The resident session NAME is pi's.** The `control`-tagged garden name mirror, its
204
- `entwurf`-tag refusal and the sessionId-bound resume-marker exemption are gone with the id
205
- they mirrored. Dormant-resume authorization is record existence (LOCKED PROTOCOL 6) plus
206
- the transcript-header ↔ `record.nativeSessionId` integrity check (#50 C3).
207
- - Gates: `smoke-pi-attach` (deterministic, in `pnpm check`: record birth · record-keyed
208
- socket · attach-on-reopen · artifact delivers to the socket) + `smoke-resident-garden-guard`
209
- (LIVE, the same contract driven through a real `pi` process).
86
+ - **control-socket domain (currently `pi`)**: socket liveness, per-target lock, live send, and the dormant cell that selects spawn-bg.
87
+ - **spawn-bg resume** is a **separate relaunch transport**, not the control-socket rail: the dormant socket-liveness branch selects it, and the launch leaf (`resolveResumeLaunchIdentity`) is what checks backend authority. There is no separate spawn-domain predicate today — do not describe it as its own domain until one exists.
88
+ - **self-fetch domain (currently Claude Code)**: active receiver + mailbox deliverability; no owned resume.
89
+ - **native-push domain (currently Antigravity)**: adapter probe + direct injection; no mailbox or owned resume.
90
+ - `origin: "pi-session" | "meta-session" | "external-mcp"` records sender provenance. It is not the citizen identity schema and not a hierarchy.
91
+ - `entwurf_peers` reports record citizens and liveness facts only. It never embeds routing verbs or socket addresses for peers.
92
+ - `entwurf_self` is identity-required. For pi, its env carrier must have been planted from record birth; native marker identity must be backed by the matching record. Replyability is derived from the active rail, never hardcoded.
210
93
 
211
94
  ### Send-is-throw
212
95
 
213
- Messages are thrown, not awaited.
214
-
215
- - v2 delivery is fire-and-forget. There is no `wait_until` / `subscribe` / `turn_end` channel and no caller-side baseline correlation. For a control-socket send the RPC ack is the contract; for meta-mailbox it is the enqueue receipt; for native-push it is adapter acceptance plus the bounded post-send probe evidence. If you need a reply, say so in the message.
216
- - The sender envelope rides every send by default: `{ sessionId, agentId, cwd, timestamp, origin?, replyable? }`. `origin` distinguishes pi-session senders (`replyable: true`) and trusted meta-session senders. Claude meta replyability is mailbox-backed; native-push replyability is record-backed + probe-alive. `entwurf_self` is authoritative-identity-required.
217
- - **Human-greeted 담당자** is a first-class pattern: GLG may open a session in repo B, greet it directly, then hand its garden id to repo A. Spawned siblings and human-opened peers share the same messaging semantics; only the creation sequence differs.
218
-
219
- ## File Structure
220
-
221
- | File | Purpose |
222
- |------|---------|
223
- | `pi-extensions/acp-provider.ts` | ACP plugin entry: registers the package provider `entwurf` + curated Claude model surface; wires `streamSimple` to the real ACP backend |
224
- | `pi-extensions/lib/acp/*.ts` | ACP plugin internals: curated Claude surface + no-auth sentinel (`models.ts`), Claude config overlay (`overlay.ts`), tool surface + exclude-tools preflight (`tool-surface.ts`), ACP→pi event mapper (`event-mapper.ts`), pi Context→ACP prompt (`context.ts`), spawn-per-turn `streamSimple` backend (`backend.ts`) |
225
- | `pi-extensions/entwurf-control.ts` | control plane: record attach at session_start, `--entwurf-control` socket (keyed on the record gardenId), RPC, `entwurf_v2` / `entwurf_peers` tools |
226
- | `pi-extensions/lib/pi-citizen-birth.ts` | the #50 C2 attach seam: pi session → meta-record upsert → control-socket address |
227
- | `pi-extensions/model-lock.ts` | package-provider model lock (pi.extension) |
228
- | `pi-extensions/meta-bridge-hook.ts` | Claude Code `SessionStart` hook: register a mailbox-backed garden meta-session |
229
- | `pi-extensions/lib/entwurf-v2-*.ts` | v2 substrate: contract / lock / decider / matrix / release / send / mailbox / native-push / runner / production / surface / spawn(+production) |
230
- | `pi-extensions/lib/native-push/` | Antigravity adapter probe/route, direct-inject hand, explicit native registration core |
231
- | `pi-extensions/lib/meta-*.ts` | V3-only meta-record authority (`meta-session.ts`), mailbox state, receiver/sender identity — one live schema, no legacy readers |
232
- | `scripts/agy-{bridge,statusline-bridge,hooks-bridge}.*` | three state-backed agy install/doctor/inverse surfaces |
233
- | `scripts/agy-imprint.ts` | agy `PreInvocation` automatic birth + record-backed sender marker |
234
- | `pi-extensions/lib/entwurf-core.ts` | shared core (session-file lookup, identity read, explicit-extension args); some v1 exports now dead pending routing cleanup |
235
- | `protocol.js` | dependency-free shared wire constants (`<project-context` marker); single source for tsc emit + strip-types MCP paths |
236
- | `run.sh` | install (incl. `install-meta-bridge`), check-*/smoke-* gates, release-gate |
237
- | `mcp/entwurf-bridge/` | MCP server exposing `entwurf_v2`, `entwurf_self`, `entwurf_peers`, `entwurf_inbox_read` |
238
-
239
- ## Typecheck Boundary
96
+ - Delivery returns an ack/receipt, not the peer's turn result. If a reply is wanted, say so and set `wants_reply`; that flag is etiquette, not ownership.
97
+ - Sender envelope: `{ sessionId, agentId, cwd, timestamp, origin?, replyable? }`.
98
+ - Human-opened and spawned/resumed siblings use the same addressing and messaging semantics.
240
99
 
241
- Single fence — every `.ts` source file is reached by some `tsc --noEmit` pass. No opt-out file. Three configs because the surfaces run under different runtime models:
242
-
243
- | Config | Covers | Runtime model |
244
- |---|---|---|
245
- | `tsconfig.json` (root) | `pi-extensions/**` | emit-capable. The root config must not set `noEmit`. |
246
- | `mcp/tsconfig.json` (extends root) | `mcp/entwurf-bridge/**`, plus the `pi-extensions/lib/*` it imports | `node --experimental-strip-types`. Adds `allowImportingTsExtensions` + `noEmit` because the bridge imports the shared lib with explicit `.ts` suffixes — Node's strip-types resolver requires the suffix on the wire. |
247
- | `scripts/tsconfig.json` (extends root) | `scripts/**` (verification scripts), plus the `pi-extensions/lib/*` it imports | `node --experimental-strip-types`. Same trade-off: explicit `.ts` imports + `allowImportingTsExtensions` + `noEmit`. Scripts are runtime gates, not build inputs. |
100
+ ## Verification
248
101
 
249
- `pnpm typecheck` runs all three passes; `pnpm check` and the husky pre-commit hook run them as part of the gate. Adding a new `.ts` file outside all three configs is a fence breach — include it or split a fourth config with a documented runtime model, but never extend the root `exclude` to hide drift.
102
+ Two axes are required: deterministic/package gates and opt-in LIVE evidence.
250
103
 
251
- Code-level invariants pinned at the same time:
104
+ ```bash
105
+ pnpm typecheck
106
+ pnpm check
107
+ ./run.sh check-entwurf-v2-matrix
108
+ ./run.sh check-meta-session
109
+ ./run.sh check-entwurf-bridge-boot
110
+ ./run.sh check-install-surface
111
+ ./run.sh check-install-container # require Docker in release acceptance
112
+
113
+ LIVE=1 ./run.sh release-gate /path/to/scratch
114
+ LIVE=1 ./run.sh smoke-acp-socket-citizen-live
115
+ LIVE=1 ./run.sh smoke-acp-bundled-mcp-live
116
+ LIVE=1 ./run.sh smoke-acp-v2-send-live
117
+ LIVE=1 ENTWURF_ACP_CORTEX_CONNECTION=<conn> ./run.sh smoke-acp-cortex-live # on-demand; outside the claude release floor
118
+ LIVE=1 AGY_CONVERSATION_ID=<id> ./run.sh smoke-agy-native-push-live
119
+ ```
252
120
 
253
- - **typebox single-source.** `pi-extensions/entwurf-control.ts` imports `Type` / `StringEnum` from `@earendil-works/pi-ai` (which re-exports typebox 1.x). `@sinclair/typebox` is not a direct dependency. Mixing the two universes silently widens `StringEnum`-typed parameters to `unknown`.
254
- - **garden-id addressing for entwurf.** Every entwurf addressing surface takes a sessionId / garden id, never a session name. Entwurf / resident garden sessions use garden ids (`YYYYMMDDTHHMMSS-[0-9a-f]{6}`); generic live pi peers may still surface pi-assigned uuids.
255
- - **sender envelope contract.** `{ sessionId, agentId, cwd, timestamp, origin?, replyable? }`. `agentId` is one field (`<provider>/<model>` for `origin: "pi-session"`, `meta-session/<backend>` for `origin: "meta-session"`). `PI_SESSION_ID` + `PI_AGENT_ID` are the canonical pi-session carriers; meta-session markers are pid+start-key hints backed by the meta-record storeno cryptographic non-forgery; cross-process env injection is the operator's responsibility.
256
- - **entwurf provider session model lock.** After a session is anchored, a model switch touching the package provider `entwurf` is reverted by `pi-extensions/model-lock.ts`; native-to-native switching stays free; fresh startup/new sessions stay unlocked until the first prompt.
121
+ - `pnpm check` is the static floor and includes the detailed `check-*`/offline smoke matrix.
122
+ - **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.
123
+ - **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 focused gate → `check-gate-qualification` `pnpm check`, in that order. `MUTANT-STALE`/`SURVIVED`/`WRONG-REASON`/`CONTROL-RED`/`HANG`/`IMPURE` are red never substitute an assertion count for a kill.
124
+ - Run LIVE gates with `PWD` in scratch so session artifacts do not land in the repo.
125
+ - Release acceptance and evidence levels are defined in [VERIFY.md](./VERIFY.md); recorded host evidence is in [BASELINE.md](./BASELINE.md).
126
+ - A failed gate or evidence downgrade blocks commit/release. Pipes can be connected and the water can still taste wrong.
257
127
 
258
- ## Runtime Dependencies
128
+ ## Repository Map
259
129
 
260
- - `@modelcontextprotocol/sdk` and `zod` are the substrate runtime deps. With the Claude-first ACP plugin shipped, the Claude/ACP backend deps are pinned alongside them: `@agentclientprotocol/claude-agent-acp` (`0.61.0`), `@agentclientprotocol/sdk` (`1.3.0`), `@anthropic-ai/sdk` (`0.100.1` — kept: `claude-agent-sdk@0.3.217`'s peer floor is `>=0.93.0`, so 0.100.1 satisfies it and a mechanical rise to the newest SDK is NOT taken). Codex/Gemini ACP packages stay out of scope; Codex is native/probe, agy is the shipped native-push Google lane, and Gemini ACP remains compatibility history rather than a current target.
261
- - `pi` (`@earendil-works/pi-ai`) on PATH at the pinned range (`>= 0.82.1 < 0.83` — devDep exact `0.82.1` + next-minor ceiling). Mismatches are caught by `check-dep-versions` / `check-pi-runtime-version`. 0.80 moved the standalone root `getModels()` to the deprecated `@earendil-works/pi-ai/compat` entrypoint; the curated Claude surface (`pi-extensions/lib/acp/models.ts`) imports `getModels` from `/compat` — the single subpath allowlisted in `check-pi-import-surface`. NOT a provider-factory subpath such as `providers/anthropic`: although it typechecks, pi's extension loader (jiti alias map in pi-coding-agent `core/extensions/loader.ts`) resolves only FOUR pi-ai specifiers for extensions — the bare root, `/compat`, `/oauth`, and (added in 0.81) `/providers/all` — so any other `providers/*` import resolves to the unresolvable `dist/compat.js/providers/…` and crashes extension load (caught live by `smoke-resident-garden-guard`, not by static typecheck). This `/compat` use is an **extension-loader compatibility shim** chosen by loader constraint, not a preference for a deprecated API — the `<0.83` ceiling guards it.
262
- - **The minor-boundary re-evaluation was done 2026-07-24 against pi `v0.80.7..v0.82.0`.** The shim held: root / `/compat` / `/oauth` stayed in the alias map, `getModels` remained a `/compat` re-export of `getBuiltinModels`, and 0.81 added `/providers/all` without forcing migration. **The 2026-07-26 `v0.82.0..v0.82.1` patch was source-diffed and installed again:** loader aliases and `/compat` are unchanged; the builtin Anthropic catalog grows 14→15 only by adding `claude-opus-5`; `claude-sonnet-5` retains identical cost/context/max-token/reasoning metadata. The explicit model cut replaces the curated Opus 4.8 row with Opus 5 (1M context, 128K max output, reasoning enabled) rather than widening the two-model surface. Re-evaluate again at the `<0.83` ceiling.
130
+ | Path | Purpose |
131
+ |---|---|
132
+ | `pi-extensions/entwurf-control.ts` | pi adapter: record attach, record-keyed socket, RPC, native tools |
133
+ | `pi-extensions/lib/pi-citizen-birth.ts` | pi native session → shared V3 record → socket address |
134
+ | `pi-extensions/lib/meta-session.ts` | shared V3 record/store authority plus native marker/mailbox primitives |
135
+ | `pi-extensions/lib/entwurf-v2-*.ts` | v2 contract, decider, transports, runner, production wiring |
136
+ | `pi-extensions/lib/entwurf-fact*.ts` | record citizens + transport-specific liveness facts |
137
+ | `pi-extensions/lib/native-push/` | native-push adapter/probe/register leaf |
138
+ | `pi-extensions/acp-provider.ts` | `entwurf` provider registration |
139
+ | `pi-extensions/lib/acp/` | ACP adapter rail, config/overlay, augment, turn loop, event mapping |
140
+ | `mcp/entwurf-bridge/` | MCP surface for v2/self/peers/inbox/native-register |
141
+ | `scripts/` | deterministic gates, LIVE smokes, install/doctor surfaces |
142
+ | `run.sh` | installed command and gate dispatcher |
143
+
144
+ ## Type and Runtime Boundaries
145
+
146
+ - 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`.
147
+ - Root pi extensions import TypeBox through `@earendil-works/pi-ai`; do not mix direct `@sinclair/typebox` types.
148
+ - MCP/scripts use explicit `.ts` imports where Node strip-types requires them. Installed operator surfaces route to compiled JS.
149
+ - pi runtime range is `>=0.82.1 <0.83` with devDep exact `0.82.1`; re-evaluate loader aliases and `/compat` at the minor ceiling.
150
+ - 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.
263
151
 
264
152
  ## Working Style
265
153
 
266
- - Surgical changes. One thing at a time.
267
- - Ask: does this belong in pi? In the resident's own repo? Or here?
268
- - Removal on this branch is gate-verified: subtract source AND its gate/case/script together (the 결합 규칙) so `pnpm check` stays green and never goes silently red.
269
- - **Every repair/removal commit hunts the sentences it invalidated** — adding the new fact without deleting the old claim has now shipped **eight** times (dead registry link → stale NEXT diagnosis → A1/launcher prose surviving C4 → `docs/setup-clean-host.md` teaching the deleted `--session-id` launcher *and a hard-exit guard that no longer exists* → the `shouldListAsLive` removal leaving its own policy sentences behind → the store-doctor claiming dual-read three cuts after V3-only · a gate header claiming v2 writes → **the schema module itself: `MetaIdentity`/serializer/minter/`decideUpsert` doc comments calling the live v3 shape "v2", one of them defaulting a field #50 deleted, while the same function's body comment was already correct**). **Stop extending the directory list — that is what failed three rounds running (`docs/`, then `scripts/`, then the lib the cut actually rewrote).** The unit is the REPO, and the sweep is a method, two axes:
270
- 1. **Retired vocabulary → repo-wide grep.** Before committing, write down what the cut retired: deleted symbol names, the schema/authority words it demoted (`dual-read`, `v2 identity`, `socketOnly`), the command form it replaced. `grep -rn` each across the whole tree (not only touched files — a schema cut invalidates prose in consumers it never edits), then judge every hit: a *tombstone* ("`X` is GONE since #50") is the goal, a *live claim* ("`X` reads v1 AND v2") is the defect.
271
- 2. **Landed plans → future-tense grep.** A cut that LANDS invalidates its own plan sentences: `yet` · `not here` · `Today …` · `does NOT yet` · `lands in step N` · `will land`. Staged-build headers are written in future tense and nobody returns to them when the step ships.
272
- Surfaces to cover at minimum: README · AGENTS · DELIVERY · VERIFY · ROADMAP · `docs/**` (an install walkthrough is a new host's first surface, so a stale command there costs more than a stale README line) · run.sh usage lines AND dispatch/function comments · `scripts/**` operator & gate scripts · **the module the cut rewrote, all of it — being the file you edited is not evidence its other 40 comments were read**. A deleted symbol's *prose* is not deleted by the compiler or by biome — only by this sweep.
273
- - Keep docs calibrated: strong language is fine; unbacked language is not.
274
- - Resist the urge to make the substrate more magical than necessary.
275
-
276
- ## Next
277
-
278
- Current priority + open decisions: [NEXT.md](./NEXT.md) (main lane; per-branch work uses a disposable `NEXT--<branch>.md` lane file that is deleted before merge). Read at session start. `/recall` restores the past axis; NEXT fixes the future axis. Forward direction: [ROADMAP.md](./ROADMAP.md).
279
-
280
- ## References
281
-
282
- - [ROADMAP.md](./ROADMAP.md) — current + future direction (the ACP-plugin-on-v2 lane; #38's eventual `entwurf` package extraction is a deferred coordinate).
283
- - [VERIFY.md](./VERIFY.md) agent-driven verification guide (Evidence Levels L0–L5 + the §1A interview; independent axes, do not conflate).
284
- - [BASELINE.md](./BASELINE.md) — operator-driven verification record (companion to VERIFY.md).
285
- - [agent-config](https://github.com/junghan0611/agent-config) — real consumer repo.
154
+ - Surgical changes, one contract at a time. Ask whether a change belongs in core, a harness adapter, a backend adapter, or the resident's own repo.
155
+ - Removal/repair changes source and their gates together. Do not leave a green gate that only proves retired behavior.
156
+ - Before commit, perform a **repo-wide** stale-prose sweep on two axes:
157
+ 1. retired symbols/authority vocabulary (`dual-read`, old schema names, removed commands, privileged identity wording);
158
+ 2. landed-plan future tense (`yet`, `will land`, `not here`, stale step headers).
159
+ - Judge each grep hit: historical CHANGELOG tombstones may remain; live claims, comments, usage text, gates, README, docs, NEXT, and source-module prose must agree with current behavior.
160
+ - Prefer capability/domain names (`control-socket domain`, `self-fetch`, `native-push`, `out-of-domain`) over identity-rank names (`pi-only citizen`, `non-pi citizen`).
161
+ - Keep docs calibrated and compact. Implementation archaeology belongs in git/CHANGELOG/issues; AGENTS keeps only invariants needed before acting.
162
+ - Use tabs unless the existing file/linter requires otherwise.
163
+ - GLG decides commit, push, and release gates. Never infer push from a commit request.
164
+
165
+ ## Next and References
166
+
167
+ - [NEXT.md](./NEXT.md) — current priority and exact next move; branch work uses disposable `NEXT--<branch>.md`.
168
+ - [ROADMAP.md](./ROADMAP.md) — forward direction and deferred lanes.
169
+ - [docs/acp-backend-rail.md](./docs/acp-backend-rail.md) — ACP adapter contract and current entry conditions.
170
+ - [DELIVERY.md](./DELIVERY.md) — delivery capability/evidence coordinates.
171
+ - [VERIFY.md](./VERIFY.md) / [BASELINE.md](./BASELINE.md) verification protocol and recorded evidence.
172
+ - [README.md](./README.md) — operator-facing package contract.