@junghanacs/entwurf 0.13.1 → 0.14.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 (152) hide show
  1. package/AGENTS.md +48 -15
  2. package/BASELINE.md +3 -3
  3. package/CHANGELOG.md +45 -0
  4. package/CONTRIBUTING.md +13 -9
  5. package/DELIVERY.md +7 -6
  6. package/README.md +27 -27
  7. package/VERIFY.md +22 -14
  8. package/demo/README.md +1 -1
  9. package/demo/demo-baseline.sh +1 -3
  10. package/demo/demo.sh +2 -5
  11. package/docs/acp-backend-rail.md +9 -4
  12. package/docs/external-mcp-host.md +4 -5
  13. package/docs/setup-clean-host.md +8 -7
  14. package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +148 -28
  15. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/overlay.js +8 -6
  16. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-control-rpc.js +7 -5
  17. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-core.js +13 -14
  18. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-resume-args.js +45 -40
  19. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-contract.js +117 -95
  20. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-decider.js +23 -57
  21. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-lock.js +16 -7
  22. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-production.js +5 -53
  23. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-release.js +21 -36
  24. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-runner.js +3 -15
  25. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-send-fallback.js +12 -11
  26. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-send.js +2 -7
  27. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-surface.js +30 -67
  28. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-visible-resume.js +256 -0
  29. package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js +91 -5
  30. package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +258 -0
  31. package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-launch.js +202 -0
  32. package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-placement.js +289 -0
  33. package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-resume-call.js +170 -0
  34. package/mcp/entwurf-bridge/dist/pi-extensions/lib/resume-launch-identity.js +136 -0
  35. package/mcp/entwurf-bridge/dist/pi-extensions/lib/session-id.js +8 -5
  36. package/mcp/entwurf-bridge/dist/pi-extensions/lib/socket-discovery.js +3 -3
  37. package/mcp/entwurf-bridge/dist/scripts/meta-facts.js +51 -0
  38. package/mcp/entwurf-bridge/dist/scripts/new-session-id.js +9 -4
  39. package/mcp/entwurf-bridge/src/index.ts +164 -28
  40. package/mcp/entwurf-bridge/start.sh +2 -2
  41. package/mcp/entwurf-bridge/test.sh +23 -9
  42. package/mcp/entwurf-bridge/tsconfig.build.json +11 -2
  43. package/package.json +22 -11
  44. package/pi-extensions/entwurf-control.ts +218 -40
  45. package/pi-extensions/lib/acp/backend.ts +71 -12
  46. package/pi-extensions/lib/acp/overlay.ts +8 -6
  47. package/pi-extensions/lib/entwurf-control-rpc.ts +7 -5
  48. package/pi-extensions/lib/entwurf-core.ts +15 -15
  49. package/pi-extensions/lib/entwurf-resume-args.ts +41 -52
  50. package/pi-extensions/lib/entwurf-v2-contract-schema.ts +1 -1
  51. package/pi-extensions/lib/entwurf-v2-contract.ts +120 -99
  52. package/pi-extensions/lib/entwurf-v2-decider.ts +30 -91
  53. package/pi-extensions/lib/entwurf-v2-lock.ts +16 -7
  54. package/pi-extensions/lib/entwurf-v2-production.ts +4 -78
  55. package/pi-extensions/lib/entwurf-v2-release.ts +25 -49
  56. package/pi-extensions/lib/entwurf-v2-runner.ts +6 -21
  57. package/pi-extensions/lib/entwurf-v2-send-fallback.ts +12 -11
  58. package/pi-extensions/lib/entwurf-v2-send.ts +2 -7
  59. package/pi-extensions/lib/entwurf-v2-surface.ts +36 -76
  60. package/pi-extensions/lib/entwurf-v2-visible-resume.ts +370 -0
  61. package/pi-extensions/lib/meta-session.ts +93 -5
  62. package/pi-extensions/lib/mux-fresh-call.ts +328 -0
  63. package/pi-extensions/lib/mux-launch.ts +267 -0
  64. package/pi-extensions/lib/mux-placement.ts +387 -0
  65. package/pi-extensions/lib/mux-resume-call.ts +221 -0
  66. package/pi-extensions/lib/resume-launch-identity.ts +162 -0
  67. package/pi-extensions/lib/session-id.js +8 -5
  68. package/pi-extensions/lib/socket-discovery.ts +3 -3
  69. package/run.sh +471 -201
  70. package/scripts/agy-bridge-config.py +5 -1
  71. package/scripts/check-acp-backend-preflight.ts +1 -1
  72. package/scripts/check-acp-overlay.ts +13 -3
  73. package/scripts/check-acp-stream-hooks.ts +504 -0
  74. package/scripts/check-elapsed.sh +25 -0
  75. package/scripts/check-entwurf-bridge-boot.ts +51 -4
  76. package/scripts/check-entwurf-bridge-pi-free.ts +6 -5
  77. package/scripts/check-entwurf-control-rpc.ts +4 -3
  78. package/scripts/check-entwurf-resume-args.ts +72 -70
  79. package/scripts/check-entwurf-session-identity.ts +14 -10
  80. package/scripts/check-entwurf-v2-contract.ts +34 -59
  81. package/scripts/check-entwurf-v2-decider.ts +17 -177
  82. package/scripts/check-entwurf-v2-lock.ts +5 -2
  83. package/scripts/check-entwurf-v2-matrix.ts +3 -53
  84. package/scripts/check-entwurf-v2-production.ts +2 -91
  85. package/scripts/check-entwurf-v2-release.ts +10 -105
  86. package/scripts/check-entwurf-v2-runner.ts +4 -85
  87. package/scripts/check-entwurf-v2-send-fallback.ts +5 -6
  88. package/scripts/check-entwurf-v2-send.ts +0 -28
  89. package/scripts/check-entwurf-v2-surface.ts +157 -128
  90. package/scripts/check-entwurf-v2-visible-resume.ts +445 -0
  91. package/scripts/check-fresh-cut-gate.sh +1 -1
  92. package/scripts/check-gate-qualification.ts +98 -7
  93. package/scripts/check-install-container.sh +10 -2
  94. package/scripts/check-install-surface.ts +1 -1
  95. package/scripts/check-keyset-overlap.py +1 -1
  96. package/scripts/check-meta-facts.ts +249 -0
  97. package/scripts/check-meta-identity-consumers.ts +1 -1
  98. package/scripts/check-meta-session.ts +169 -0
  99. package/scripts/check-mux-launch-tmux.ts +316 -0
  100. package/scripts/check-mux-launch.ts +288 -0
  101. package/scripts/check-mux-launcher-fence.ts +264 -0
  102. package/scripts/check-mux-parent-artifact.ts +195 -0
  103. package/scripts/check-mux-placement-tmux.ts +322 -0
  104. package/scripts/check-mux-placement.ts +323 -0
  105. package/scripts/check-mux-resume-call.ts +282 -0
  106. package/scripts/check-probe-cli-shim.ts +25 -22
  107. package/scripts/check-probe-ordering.ts +84 -76
  108. package/scripts/check-release-gate-outcomes.ts +127 -7
  109. package/scripts/check-resume-launch-identity.ts +244 -0
  110. package/scripts/check-socket-discovery.ts +1 -1
  111. package/scripts/fixtures/mux-parent-transcript.scrubbed.jsonl +3 -0
  112. package/scripts/inventory-verification-surface.ts +349 -0
  113. package/scripts/lib/claude-launcher-fence.ts +322 -0
  114. package/scripts/lib/mutation-qualify.ts +109 -3
  115. package/scripts/meta-bridge-doctor.sh +6 -8
  116. package/scripts/meta-facts.ts +60 -0
  117. package/scripts/mutants/acp-overlay.json +17 -0
  118. package/scripts/mutants/acp-stream-hooks.json +158 -0
  119. package/scripts/mutants/bridge-boot-resume.json +45 -0
  120. package/scripts/mutants/meta-facts.json +50 -0
  121. package/scripts/mutants/meta-identity.json +36 -0
  122. package/scripts/mutants/mux-boundary.json +196 -0
  123. package/scripts/mutants/mux-fresh-call.json +185 -0
  124. package/scripts/mutants/mux-launcher-fence.json +123 -0
  125. package/scripts/mutants/mux-parent-artifact.json +39 -0
  126. package/scripts/mutants/mux-resume-call.json +148 -0
  127. package/scripts/mutants/probe-ordering.json +0 -1037
  128. package/scripts/mutants/release-gate.json +35 -0
  129. package/scripts/mutants/resume-args.json +76 -0
  130. package/scripts/mutants/resume-launch-identity.json +96 -0
  131. package/scripts/mutants/v2-surface.json +58 -18
  132. package/scripts/mutants/v2-visible-resume.json +215 -0
  133. package/scripts/new-session-id.ts +9 -4
  134. package/scripts/smoke-acp-raw-turn-live.ts +1 -1
  135. package/scripts/smoke-agy-native-push-live.ts +6 -17
  136. package/scripts/smoke-entwurf-v2-matrix-live.ts +1 -1
  137. package/scripts/smoke-meta-honesty.sh +1 -1
  138. package/scripts/smoke-mux-fresh-call-live.ts +365 -0
  139. package/scripts/smoke-mux-lifecycle-live.ts +1136 -0
  140. package/scripts/smoke-pi-attach.ts +1 -1
  141. package/scripts/smoke-user-scope-citizen.sh +1 -1
  142. package/scripts/tsconfig.json +1 -0
  143. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-preflight.js +0 -160
  144. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-spawn-production.js +0 -273
  145. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-spawn.js +0 -216
  146. package/pi-extensions/lib/entwurf-v2-spawn-production.ts +0 -373
  147. package/pi-extensions/lib/entwurf-v2-spawn.ts +0 -323
  148. package/scripts/check-acp-sdk-surface.ts +0 -275
  149. package/scripts/check-entwurf-v2-spawn-production.ts +0 -551
  150. package/scripts/check-entwurf-v2-spawn.ts +0 -399
  151. package/scripts/smoke-entwurf-v2-spawn-live.ts +0 -188
  152. package/scripts/smoke-entwurf-v2-spawn-resume-live.ts +0 -467
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * meta-facts — owner-normalized, read-only projection of the meta-record store (#65).
4
+ *
5
+ * The one join a consumer cannot make anywhere else — `(gardenId, nativeSessionId,
6
+ * transcriptPath)` — lives only in the meta-record store, and reading that store
7
+ * correctly means carrying the whole certification contract (strict v3 keyset,
8
+ * parse-before-uniqueness, no-winner duplicates, O_NOFOLLOW regular-file reads).
9
+ * External consumers that copied it decayed the day the contract moved. This surface
10
+ * makes THE owner emit the join: it is `listAllMetaIdentities` over the real store —
11
+ * the same listing `entwurf_peers` trusts — serialized verbatim. No parallel parser,
12
+ * no liveness, no socket paths, no transcript contents, no state, no watching.
13
+ *
14
+ * stdout (deterministic, 2-space indent, trailing newline):
15
+ * {
16
+ * "schemaVersion": 1, // of THIS projection, not of the records
17
+ * "storeDir": "/abs/path",
18
+ * "citizens": [ <full verbatim strict 9-field v3 records, sorted by gardenId> ],
19
+ * "defects": [ { "filename", "message" }, … sorted by filename ]
20
+ * }
21
+ *
22
+ * Defects ride IN-BAND: a readable store with uncertifiable entries is a fact to
23
+ * report, not a failure of this command — the healthy citizens are still true, and
24
+ * the defect list is exactly what `listAllMetaIdentities` refused and why (a rival
25
+ * pair is refused on BOTH sides; a schema-invalid record can never quarantine a
26
+ * healthy neighbour, because parse runs before uniqueness).
27
+ *
28
+ * EXIT CONTRACT — the verdict is the exit code (store-doctor precedent):
29
+ * 0 — store readable; the JSON above is on stdout (defects, if any, in-band).
30
+ * A store that does not exist is a readable EMPTY store (ENOENT only).
31
+ * 2 — usage error (bad argv).
32
+ * 3 — the store could not be READ (EACCES/ENOTDIR/…). No JSON is emitted:
33
+ * an unreadable host must never look like an empty one.
34
+ */
35
+ import { defaultMetaSessionsDir, listAllMetaIdentitiesDir } from "../pi-extensions/lib/meta-session.js";
36
+ const arg = process.argv[2];
37
+ // A dash argv is a flag this command does not have, not a store directory — treating
38
+ // it as a path would answer `--help` with "empty store, exit 0", a silent wrong fact.
39
+ if (process.argv.length > 3 || (arg !== undefined && arg.startsWith("-"))) {
40
+ console.error("usage: entwurf meta-facts [meta-sessions-dir]");
41
+ process.exit(2);
42
+ }
43
+ let facts;
44
+ try {
45
+ facts = listAllMetaIdentitiesDir(arg ?? defaultMetaSessionsDir());
46
+ }
47
+ catch (err) {
48
+ console.error(`FAIL: ${err instanceof Error ? err.message : String(err)}`);
49
+ process.exit(3); // see the EXIT CONTRACT above: 3 = unreadable store, never an empty one
50
+ }
51
+ process.stdout.write(`${JSON.stringify({ schemaVersion: 1, storeDir: facts.dir, citizens: facts.identities, defects: facts.errors }, null, 2)}\n`);
@@ -1,11 +1,16 @@
1
1
  /**
2
2
  * new-session-id — print one fresh garden-native sessionId and exit.
3
3
  *
4
- * The garden launcher (operator alias / wrapper) calls this so every
5
- * `--entwurf-control` session is born a garden citizen:
4
+ * NOT a launcher contract. #50 C2 retired the injection form
5
+ * (`pi --session-id "$(./run.sh new-session-id)" --entwurf-control …`): pi mints its
6
+ * own session id (a `uuidv7` is normal), `session_start` attaches that session to its
7
+ * meta-record, and the RECORD mints the garden id and keys the control socket on it.
8
+ * Injecting a garden-shaped id gives a session two address-shaped strings of which only
9
+ * one is an address. Launch is plain `pi --entwurf-control` (README §Garden launcher).
6
10
  *
7
- * pi --session-id "$(./run.sh new-session-id)" --entwurf-control \
8
- * --emacs-agent-socket server
11
+ * What survives is this: the operator-facing generator for the same id grammar the
12
+ * record layer itself uses — one fresh id on stdout, for a caller that needs to mint a
13
+ * token deliberately, never as a launch argument.
9
14
  *
10
15
  * The id is `generateSessionId()` from entwurf-core — the single SSOT for the
11
16
  * locked `YYYYMMDDTHHMMSS-[0-9a-f]{6}` grammar. Do NOT reimplement the format in
@@ -11,7 +11,7 @@
11
11
  * Currently exposed tools (scope is deliberately narrow — anything that can live
12
12
  * as a local skill should live as a skill, not here):
13
13
  * - entwurf_v2 — canonical delivery surface for existing garden citizens; the decider
14
- * chooses live control-socket send / dormant spawn-bg resume / meta-mailbox.
14
+ * chooses live control-socket send / meta-mailbox enqueue / native-push.
15
15
  * - entwurf_peers — entwurf fact surface: garden citizens (meta-records) with liveness +
16
16
  * diagnostics (#50 C4: record-less sockets surface THERE, never as identity).
17
17
  * Brain = pi-extensions/lib/entwurf-fact-provider (listEntwurfFacts) +
@@ -24,6 +24,18 @@
24
24
  * garden record may call this — so the surface is "drain the inbox you were
25
25
  * pointed at", not "drain your own". A rung doorbell is a wake attempt;
26
26
  * this read is the receipt.
27
+ * - entwurf_register_native — explicit/manual fallback binding an ALREADY-RUNNING native
28
+ * conversation (antigravity) to a garden id. Never a spawn.
29
+ * - entwurf_fresh_call — open ONE fresh visible sibling in the operator's own tmux session;
30
+ * returns a LAUNCH receipt only, and the new address arrives later as the
31
+ * sender envelope of the sibling's nonce callback.
32
+ * - entwurf_resume_call — reopen ONE DORMANT pi citizen under its OWN garden id in a visible
33
+ * window; target-only, runs no turn, LAUNCH and OBSERVATION receipts stay
34
+ * apart.
35
+ *
36
+ * That list is the WHOLE public surface — seven verbs — and `check-entwurf-bridge-boot`
37
+ * (G1f) holds it as an exact set on the runtime tools/list, so a verb added or dropped
38
+ * here without a decision is red rather than merely undocumented.
27
39
  *
28
40
  * Removed from this v2-only surface: legacy MCP `entwurf`, `entwurf_resume`, and
29
41
  * `entwurf_send`. Use `entwurf_v2` for delivery to existing garden citizens.
@@ -59,6 +71,12 @@ import { renderEntwurfPeers } from "../../../pi-extensions/lib/entwurf-peers-ren
59
71
  import { computeSelfAddressability, type MetaDeliveryDomain } from "../../../pi-extensions/lib/entwurf-self-address.ts";
60
72
  import { nativePushSupported } from "../../../pi-extensions/lib/entwurf-v2-contract.ts";
61
73
  import { runAndRenderEntwurfV2FromSurface } from "../../../pi-extensions/lib/entwurf-v2-surface.ts";
74
+ import {
75
+ makeVisibleResumeDeps,
76
+ renderVisibleResume,
77
+ type VisibleResumeDeps,
78
+ visibleResume,
79
+ } from "../../../pi-extensions/lib/entwurf-v2-visible-resume.ts";
62
80
  import {
63
81
  probeNativeSenderAlive,
64
82
  resolveTrustedMetaSenderIdentity,
@@ -71,6 +89,8 @@ import {
71
89
  readMetaInbox,
72
90
  readMetaReceiverMarker,
73
91
  } from "../../../pi-extensions/lib/meta-session.ts";
92
+ import { freshCall, renderFreshCall } from "../../../pi-extensions/lib/mux-fresh-call.ts";
93
+ import { RESUME_CALL_REJECT_HINT, resumeCall } from "../../../pi-extensions/lib/mux-resume-call.ts";
74
94
  import { registerNativeConversation } from "../../../pi-extensions/lib/native-push/register.ts";
75
95
 
76
96
  const HOME = os.homedir();
@@ -330,13 +350,12 @@ function abbreviateHomeMcp(cwd: string): string {
330
350
 
331
351
  // entwurf_v2 — the unified v2 dispatch verb (0.11 step 5d-3b). It hands the
332
352
  // target + intent to the 5b decider, which chooses the transport (live
333
- // control-socket send / spawn-bg resume / meta-mailbox enqueue / native-push
334
- // direct injection) and reports one outcome. The per-target lock is NOT taken by
335
- // every rail: the decider locks only a control-socket-domain dispatch, which
336
- // covers the live send AND the dormant cell's spawn-bg resume; the mailbox and
337
- // native-push branches carry `lock: null` (entwurf-v2-decider.ts). It runs
353
+ // control-socket send / meta-mailbox enqueue / native-push direct injection) and
354
+ // reports one outcome. The per-target lock is NOT taken by every rail: the decider
355
+ // locks only a control-socket-domain dispatch; the mailbox and native-push branches
356
+ // carry `lock: null` (entwurf-v2-decider.ts). It runs
338
357
  // IN-PROCESS here (the same production runner pi-native uses) — NOT a delegating
339
- // RPC — so control, mailbox, native-push, AND spawn-bg all flow through
358
+ // RPC — so control, mailbox and native-push all flow through
340
359
  // `runEntwurfV2`. The sender envelope is
341
360
  // `buildSendSenderEnvelope()` verbatim (origin/replyable as resolved) — v2 does
342
361
  // NOT gate on replyability (a `wants_reply` from an external/non-replyable caller
@@ -346,11 +365,10 @@ server.tool(
346
365
  "entwurf_v2",
347
366
  "CANONICAL DELIVERY SURFACE for garden ids: message, reply, or hand off to whoever an id names. The id " +
348
367
  "alone does not say which rail that citizen answers on. Give target + intent; the decider picks transport " +
349
- "from liveness (live socket citizen → control-socket send; dormant socket citizen → spawn-bg resume; " +
350
- "deliverable self-fetch citizen → meta-bridge mailbox; probe-alive native-push citizen → direct injection " +
351
- "into its conversation) and reports ONE outcome (delivered / rejected / lock-retained / " +
352
- "delivered-but-lock-dirty). EXISTING targets only; discover with entwurf_peers. INTENT picking wrong is " +
353
- "rejected, never auto-converted. A peer entwurf_peers shows as liveness=alive → fire-and-forget. A " +
368
+ "from liveness (live socket citizen → control-socket send; deliverable self-fetch citizen → meta-bridge " +
369
+ "mailbox; probe-alive native-push citizen → direct injection into its conversation) and reports ONE " +
370
+ "outcome (delivered / rejected / delivered-but-lock-dirty). EXISTING targets only; discover with " +
371
+ "entwurf_peers. A peer entwurf_peers shows as liveness=alive fire-and-forget. A " +
354
372
  "citizen with NO socket liveness (liveness=unsupported) is ALSO fire-and-forget — unsupported means only " +
355
373
  '"no control-socket probe" — and the decider picks its own rail: a self-fetch backend (e.g. Claude Code) ' +
356
374
  "gets the mailbox, a native-push backend (e.g. Antigravity) gets direct injection and has NO mailbox at " +
@@ -358,26 +376,27 @@ server.tool(
358
376
  "session, or a backend with no adapter here (e.g. codex), is mailbox-undeliverable, not queued for an " +
359
377
  "inbox nobody drains. The native-push probe is 3-valued: alive → injected; dead → " +
360
378
  "native-push-target-dead; indeterminate → native-push-probe-indeterminate (unestablished ≠ gone). " +
361
- "owned-outcome wakes a DORMANT socket-domain citizen by spawn-bg resume ONLY live target → " +
362
- "owned-live-no-autosend, self-fetch backend-liveness-unsupported, native-push " +
363
- "native-push-no-resume-authority. LOCK: taken for a control-socket-DOMAIN dispatch the live send AND " +
364
- "the dormant cell's spawn-bg resume, a separate transport that still runs under that domain's lock. The " +
379
+ "DORMANT IS UNREACHABLE: a socket-domain citizen that is not running gets dormant-fire-forget-unsupported " +
380
+ " same receiver rule as the mailbox, no active drainer means no delivery. " +
381
+ "The intent that used to answer there, owned-outcome, resumed it by launching a hidden background child " +
382
+ "and was withdrawn under the visible-first rule re-open the session yourself, then dispatch again. " +
383
+ "LOCK: taken for a control-socket-DOMAIN dispatch. The " +
365
384
  "mailbox and native-push rails are lock-free — deliverability and the adapter probe guard them. mode " +
366
385
  "applies to a CONTROL-SOCKET send only; other plans carry no mode. wants_reply rides every rail. message " +
367
386
  "caps at 16000 chars; send an artifact path + digest for more.",
368
387
  {
369
388
  target: z.string().min(1).describe("Target garden id (use entwurf_peers to discover)"),
370
389
  intent: z
371
- .enum(["fire-and-forget", "owned-outcome"])
390
+ .enum(["fire-and-forget"])
372
391
  .describe(
373
392
  "fire-and-forget = send/reply/hand-off to a LIVE socket target or to any citizen with no " +
374
393
  "socket liveness — the decider picks its rail, and a rail can also REJECT (self-fetch → " +
375
394
  "mailbox when deliverable, else mailbox-undeliverable; native-push → alive: direct injection, " +
376
395
  "dead: native-push-target-dead, indeterminate: native-push-probe-indeterminate); set " +
377
- "wants_reply for an answer. owned-outcome = " +
378
- "wake a DORMANT socket-domain citizen via spawn-bg resume ONLY on a live target rejected " +
379
- "as owned-live-no-autosend, on self-fetch as backend-liveness-unsupported, on native-push as " +
380
- "native-push-no-resume-authority, and never auto-converted",
396
+ "wants_reply for an answer. This is the ONLY intent: owned-outcome, which resumed a dormant " +
397
+ "citizen by launching a hidden background child, was withdrawn under the visible-first rule. " +
398
+ "It is not selectable, not deprecated-but-tolerated; a dormant citizen is currently " +
399
+ "unreachable by this verb and rejects as dormant-fire-forget-unsupported.",
381
400
  ),
382
401
  message: z
383
402
  .string()
@@ -391,8 +410,8 @@ server.tool(
391
410
  .optional()
392
411
  .describe(
393
412
  "Injection style for a CONTROL-SOCKET send only: steer (interrupt the current turn) or " +
394
- "follow_up (queue after it). The mailbox, native-push, and spawn-bg plans carry no mode, so it " +
395
- "has no effect on those rails.",
413
+ "follow_up (queue after it). The mailbox and native-push plans carry no mode, so it has no " +
414
+ "effect on those rails.",
396
415
  ),
397
416
  wants_reply: z.boolean().optional().describe("Human-conversation reply hint (default false)"),
398
417
  },
@@ -403,8 +422,9 @@ server.tool(
403
422
  const sender = await buildSendSenderEnvelope();
404
423
  const rendered = await runAndRenderEntwurfV2FromSurface(
405
424
  { target, intent, message, mode, wants_reply },
406
- // agentDir / prefixRoots intentionally omitted: runAndRenderEntwurfV2FromSurface falls
407
- // back to the ENTWURF_PREFIX_ROOTS env SSOT for prefixRoots (5d-4); agentDir stays undefined.
425
+ // No trust-preflight inputs are passed, and none exist to pass: the preflight on this
426
+ // path guarded the resume verdict and left with `owned-outcome`. `senderProvider` is
427
+ // the whole options surface now (see the pi-native surface for the same note).
408
428
  { senderProvider: () => sender },
409
429
  );
410
430
  return rendered.isError ? textErr(rendered.text) : textOk(rendered.text);
@@ -497,7 +517,9 @@ server.tool(
497
517
  "`unsupported` for a backend with no control-socket probe such as claude-code); the dispatch " +
498
518
  "decision (send vs resume) is computed LATER by the entwurf_v2 contract from that liveness, " +
499
519
  "not here — this surface carries no per-row routing field, so do not read a transport off a " +
500
- "row. In particular `unsupported` does NOT mean mailbox: it means this backend has no " +
520
+ "row. A `dead` row is a REPORTED FACT and nothing more: that citizen is dormant and is " +
521
+ "currently unreachable by any verb, so listing it grants no action — appearing here is not " +
522
+ "an invitation to dispatch. In particular `unsupported` does NOT mean mailbox: it means this backend has no " +
501
523
  "control-socket probe. Which rail it answers on is a capability the decider resolves at dispatch " +
502
524
  "time, and there are THREE possible answers, not two — a self-fetch mailbox (only while that " +
503
525
  "mailbox is deliverable), native-push direct injection (only while its adapter probe is alive), " +
@@ -507,8 +529,8 @@ server.tool(
507
529
  "is not a departed host. So " +
508
530
  "`unsupported` does not promise reachability either: a record whose backend has no adapter on " +
509
531
  "this lane resolves to that reject. " +
510
- "Note: this is the *active* world. It is NOT a fresh-sibling creation surface; pass an " +
511
- "existing garden id to entwurf_v2.",
532
+ "Note: this is the *active* world and it is facts-only it creates nothing. Pass an " +
533
+ "existing garden id to entwurf_v2; to open a NEW sibling use entwurf_fresh_call.",
512
534
  {},
513
535
  async () => {
514
536
  try {
@@ -621,6 +643,120 @@ server.tool(
621
643
  },
622
644
  );
623
645
 
646
+ // The caller identity for THIS surface comes from the same authoritative resolution
647
+ // `entwurf_self` uses — BOTH of its certified paths: the pi carrier this child inherited
648
+ // (PI_SESSION_ID + PI_AGENT_ID, which a record minted at birth), or a trusted meta-sender marker
649
+ // the native host's own hook wrote. Neither is a guess and neither is deprecated here.
650
+ //
651
+ // What is refused is a caller-supplied id. The measured failure mode is exactly that: asked for
652
+ // its own garden id, a fresh cell answered with its uuidv7 `PI_SESSION_ID` value read out of the
653
+ // environment by an MCP server it had spawned itself — confidently, and wrong. A sibling launched
654
+ // against that answer would call home to a garden id nobody holds.
655
+ server.tool(
656
+ "entwurf_fresh_call",
657
+ "Open ONE fresh visible sibling in the operator's own tmux session and hand it a first task. Two fixed " +
658
+ "backends only: pi, claude-code. The sibling's FIRST action is a callback to you carrying a nonce, and the " +
659
+ "sender envelope of that callback is its garden id — that is how you learn the address of something that " +
660
+ "did not exist a moment ago. This returns a LAUNCH receipt (tmux window/pane plus that nonce) and nothing " +
661
+ "else: it does NOT mean the runtime started, the first turn ran, or the task was delivered. Nothing polls " +
662
+ "for the callback; if it never arrives the window is visible and can be read directly. For EXISTING " +
663
+ "citizens use entwurf_v2 — this tool only creates, and entwurf_peers only reports. Model is REQUIRED and " +
664
+ "is passed to the chosen runtime CLI (`provider/model` for pi; model id/alias for Claude Code); there are no " +
665
+ "arbitrary command/cwd/env knobs. Do not put secrets in the task — model and task argv are visible to " +
666
+ "same-user processes on this host. Requires that this agent itself runs " +
667
+ "inside tmux: without a pane anchor there is no session to open a sibling beside.",
668
+ {
669
+ backend: z
670
+ .enum(["pi", "claude-code"])
671
+ .describe("Which fixed runtime to open. Only these two; there is no arbitrary command."),
672
+ model: z
673
+ .string()
674
+ .min(1)
675
+ .max(200)
676
+ // The `\[` below is USELESS TO JS AND LOAD-BEARING TO THE HOST. zod emits this source
677
+ // text verbatim as the JSON Schema `pattern`, and the host's tool-schema validator is a
678
+ // Rust regex engine that rejects an unescaped `[` inside a character class as "unclosed
679
+ // character class" — a 400 on tools/list that stops EVERY Claude session from opening
680
+ // (7-M-fix). Biome's safe fix removes it; do not accept that fix. The suppression must
681
+ // stay on the line DIRECTLY above the expression, or it attaches to nothing and reads
682
+ // as an unused suppression while the escape goes back to being fixable.
683
+ // biome-ignore lint/complexity/noUselessEscapeInRegex: emitted to a Rust regex validator, see above
684
+ .regex(/^[A-Za-z0-9][A-Za-z0-9._/:\[\]-]*$/)
685
+ .describe("Required runtime model: canonical provider/model for pi, or a Claude Code model id/alias."),
686
+ task: z
687
+ .string()
688
+ .min(1)
689
+ .max(16000)
690
+ .describe(
691
+ "What the sibling should do after it calls you back. Plain instructions; no secrets (see the tool description).",
692
+ ),
693
+ },
694
+ async ({ backend, model, task }) => {
695
+ let callerGardenId: string | null = null;
696
+ try {
697
+ const self = await buildAuthoritativeSelfEnvelope();
698
+ callerGardenId = self.envelope.sessionId;
699
+ } catch (err) {
700
+ // ONE error is a legitimate answer here: this host has no authoritative identity at all
701
+ // (no pi carrier inherited, no trusted marker written), so the sibling would have nowhere
702
+ // to call home. That normalises to the named refusal.
703
+ //
704
+ // Everything else — a corrupt record, an unreadable store, a broken marker — keeps its
705
+ // own diagnosis and fails loud. Folding those into "you are anonymous" would relabel a
706
+ // store defect as a wiring choice, and the operator would go looking in the wrong place.
707
+ if (!(err instanceof EntwurfEnvelopeWiringError)) throw err;
708
+ callerGardenId = null;
709
+ }
710
+ try {
711
+ const rendered = renderFreshCall(freshCall({ backend, model, task, callerGardenId }));
712
+ return rendered.isError ? textErr(rendered.text) : textOk(rendered.text);
713
+ } catch (err) {
714
+ return textErr(`entwurf_fresh_call error: ${err instanceof Error ? err.message : String(err)}`);
715
+ }
716
+ },
717
+ );
718
+
719
+ // The composition root for a visible resume: this surface is the only layer that may know BOTH
720
+ // halves, so it is where the mux launcher is handed to the v2 composition. `entwurf-v2-visible-
721
+ // resume` cannot open a window on its own and `mux-resume-call` cannot read a record — that is
722
+ // the import fence in docs/mux-launch-rail.md §11, expressed as a function argument.
723
+ const visibleResumeLaunch: VisibleResumeDeps["launch"] = (input) => {
724
+ const launched = resumeCall({ cwd: input.cwd, runtimeArgs: input.runtimeArgs });
725
+ return launched.ok
726
+ ? { ok: true, handle: launched.receipt }
727
+ : { ok: false, reason: launched.reason, hint: RESUME_CALL_REJECT_HINT[launched.reason] };
728
+ };
729
+
730
+ server.tool(
731
+ "entwurf_resume_call",
732
+ "Reopen ONE DORMANT pi citizen under its OWN garden id, in a visible window in the operator's own tmux " +
733
+ "session. The record supplies everything — which transcript, which model, which provider, which cwd — so " +
734
+ "the only input is the target id: there is no model override, no task, and no prompt. This runs NO turn: " +
735
+ "the window comes back with the conversation and waits, and talking to it is still entwurf_v2 " +
736
+ "fire-and-forget on the socket this call stands up. You get TWO receipts and they mean different things: a " +
737
+ "LAUNCH receipt (tmux made a window and was asked to start pi) and an OBSERVATION receipt (the control " +
738
+ "socket answered under the same id, or resume-unobserved). Unobserved is a real outcome, not an error to " +
739
+ "retry — the window is visible, so read it. A citizen that is already LIVE is refused: address it with " +
740
+ "entwurf_v2 instead. Only pi citizens have a same-id resume, because only they stand a control socket up. " +
741
+ "Requires that this agent itself runs inside tmux.",
742
+ {
743
+ target: z
744
+ .string()
745
+ .min(1)
746
+ .regex(/^\d{8}T\d{6}-[0-9a-f]{6}$/)
747
+ .describe("Garden id of the DORMANT pi citizen to reopen (discover with entwurf_peers)."),
748
+ },
749
+ async ({ target }) => {
750
+ try {
751
+ const result = await visibleResume(target, makeVisibleResumeDeps(visibleResumeLaunch));
752
+ const rendered = renderVisibleResume(result);
753
+ return rendered.isError ? textErr(rendered.text) : textOk(rendered.text);
754
+ } catch (err) {
755
+ return textErr(`entwurf_resume_call error: ${err instanceof Error ? err.message : String(err)}`);
756
+ }
757
+ },
758
+ );
759
+
624
760
  // ============================================================================
625
761
  // Main
626
762
  // ============================================================================
@@ -12,7 +12,7 @@
12
12
  # DEV CLONE (path is NOT under node_modules): runs src/index.ts via
13
13
  # --experimental-strip-types. The clone lives outside node_modules so strip-types
14
14
  # is allowed, edits are picked up with no build step, and a leftover dist/ (e.g.
15
- # emitted by `prepack` during a `pnpm check` pack gate) can never silently shadow
15
+ # emitted by `prepack` during a `pnpm run check:full` pack gate) can never silently shadow
16
16
  # the source. Node >= 24 (engines.node in ../../package.json is the SSOT;
17
17
  # check-node-floor-coherence binds this comment to it).
18
18
  #
@@ -48,7 +48,7 @@ SRC_ENTRY="$HERE/src/index.ts"
48
48
  # Mode is decided by LOCATION, not by "does dist happen to exist". A dev clone
49
49
  # lives outside node_modules and ALWAYS runs the TS source via strip-types, so an
50
50
  # edit is picked up immediately and a stale dist (e.g. left behind by a prior
51
- # `npm pack`/`prepack` during `pnpm check`) can never silently shadow the source.
51
+ # `npm pack`/`prepack` during `pnpm run check:full`) can never silently shadow the source.
52
52
  # An installed package lives under node_modules, where Node refuses strip-types,
53
53
  # so it MUST run the prebuilt dist. All chatter goes to stderr so it never
54
54
  # confuses an MCP client reading JSON-RPC frames from stdout.
@@ -40,19 +40,33 @@ OUT="$(jsonrpc)"
40
40
  TOOLS_JSON="$(printf '%s\n' "$OUT" | grep '"id":2' | tail -1)"
41
41
  [[ -n "$TOOLS_JSON" ]] || fail "missing tools/list response: $OUT"
42
42
 
43
- python3 - <<'PY' <<<"$TOOLS_JSON"
44
- import json, sys
45
- obj=json.load(sys.stdin)
46
- names={t['name'] for t in obj['result']['tools']}
47
- expected={'entwurf_v2','entwurf_self','entwurf_peers','entwurf_inbox_read','entwurf_register_native'}
48
- missing=expected-names
43
+ # The payload rides an ENV VAR, not stdin. It used to be `python3 - <<'PY' <<<"$TOOLS_JSON"`,
44
+ # which is TWO redirections onto fd 0: the here-string wins, so python read the tools/list JSON
45
+ # as its PROGRAM (a bare dict literal — valid Python that does nothing) and the here-doc below
46
+ # never ran at all. Every assertion in this block was dead from the day it was written, and the
47
+ # `ok:` line beneath printed unconditionally. Keep exactly one stdin here.
48
+ TOOLS_JSON="$TOOLS_JSON" python3 - <<'PY'
49
+ import json, os
50
+ obj=json.loads(os.environ["TOOLS_JSON"])
51
+ raw=[t['name'] for t in obj['result']['tools']]
52
+ names=set(raw)
53
+ # EXACT set, not a floor. This suite ships inside the tarball and is what an installed
54
+ # `entwurf check-bridge` runs against the dist branch of start.sh, so a subset check here
55
+ # was an artifact-surface hole: a bundle missing entwurf_fresh_call / entwurf_resume_call
56
+ # answered tools/list and passed. Equality also refuses an undecided extra verb; the
57
+ # duplicate arm catches a name registered twice, which set membership cannot see.
58
+ expected={'entwurf_v2','entwurf_self','entwurf_peers','entwurf_inbox_read','entwurf_register_native','entwurf_fresh_call','entwurf_resume_call'}
49
59
  legacy={'entwurf','entwurf_resume','entwurf_send'} & names
50
- if missing:
51
- raise SystemExit(f"missing tools: {sorted(missing)}")
60
+ if len(raw)!=len(names):
61
+ raise SystemExit(f"duplicate tool registrations: {sorted(raw)}")
62
+ # Named negative first, so a resurrected v1 verb keeps its own diagnosis instead of
63
+ # arriving as a nameless "unexpected" entry in the equality message below.
52
64
  if legacy:
53
65
  raise SystemExit(f"legacy v1 tools still registered: {sorted(legacy)}")
66
+ if names!=expected:
67
+ raise SystemExit(f"tool set MISMATCH — missing {sorted(expected-names)}, unexpected {sorted(names-expected)}")
54
68
  PY
55
- ok "v2-only tool surface registered"
69
+ ok "public tool surface is EXACTLY the seven garden verbs (no v1 verb, no undecided extra, no duplicate)"
56
70
 
57
71
  SELF_JSON="$(printf '%s\n' "$OUT" | grep '"id":3' | tail -1)"
58
72
  [[ "$SELF_JSON" == *"isError"* ]] || fail "entwurf_self without identity should be an error: $SELF_JSON"
@@ -16,8 +16,10 @@
16
16
  // (TS 5.7+) instead of `noEmit`, so emitted JS references `.js` paths, including
17
17
  // the lazy `await import("./entwurf-preflight.ts")` → `./entwurf-preflight.js`.
18
18
  // That dynamic import stays dynamic in the output, so the eager boot closure
19
- // remains pi-free (check-entwurf-bridge-pi-free) while the owned-outcome resume
20
- // branch still lazy-loads the pi-backed preflight. This is an EMIT pass, not a
19
+ // remains pi-free (check-entwurf-bridge-pi-free). The branch that used to reach
20
+ // that lazy edge — the owned-outcome resume verdict was withdrawn, so nothing
21
+ // on the dispatch path loads the pi-backed preflight today; the dynamic form is
22
+ // kept because the eager-closure guarantee must not depend on that. This is an EMIT pass, not a
21
23
  // typecheck pass — the noEmit typecheck fence (scripts/tsconfig.json) is
22
24
  // untouched, so every `.ts` is still typechecked by exactly one `tsc --noEmit`.
23
25
  "extends": "../../tsconfig.json",
@@ -84,6 +86,12 @@
84
86
  // and run_ts refuses them under an installed package rather than emitting 70+
85
87
  // gate leaves into the tarball.
86
88
  //
89
+ // WHY meta-facts rides this build (#65): it is the operator projection external
90
+ // consumers call INSTEAD of parsing the store, and those consumers live on
91
+ // INSTALLED hosts — under node_modules, where run_ts needs a compiled twin.
92
+ // Its only repo import is meta-session.ts, already emitted for the
93
+ // store-doctor, so it adds ONE leaf JS and ZERO new deps.
94
+ //
87
95
  // WHY meta-bridge-fresh-cut rides this build: it is the ONE operator command
88
96
  // every v3-only rejection surface names, and the hosts that need it most are
89
97
  // INSTALLED hosts sitting on a previous-generation store — exactly where
@@ -102,6 +110,7 @@
102
110
  "../../scripts/new-session-id.ts",
103
111
  "../../scripts/meta-bridge-prune.ts",
104
112
  "../../scripts/meta-bridge-fresh-cut.ts",
113
+ "../../scripts/meta-facts.ts",
105
114
  "../../pi-extensions/meta-bridge-hook.ts"
106
115
  ]
107
116
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@junghanacs/entwurf",
3
- "version": "0.13.1",
3
+ "version": "0.14.0",
4
4
  "description": "Garden-citizen dispatch substrate and meta-bridge for Claude Code, Codex, Antigravity, and pi harnesses.",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -74,33 +74,35 @@
74
74
  "claudeCodeFloor": ">=2.1.217"
75
75
  },
76
76
  "dependencies": {
77
- "@agentclientprotocol/claude-agent-acp": "0.64.0",
77
+ "@agentclientprotocol/claude-agent-acp": "0.66.0",
78
78
  "@agentclientprotocol/sdk": "1.3.0",
79
79
  "@anthropic-ai/sdk": "0.100.1",
80
80
  "@modelcontextprotocol/sdk": "^1.12.1",
81
81
  "zod": "^3.25.0 || ^4.0.0"
82
82
  },
83
83
  "peerDependencies": {
84
- "@earendil-works/pi-ai": ">=0.83.0 <0.84",
85
- "@earendil-works/pi-coding-agent": ">=0.83.0 <0.84",
86
- "@earendil-works/pi-tui": ">=0.83.0 <0.84",
84
+ "@earendil-works/pi-ai": ">=0.84.1 <0.85",
85
+ "@earendil-works/pi-coding-agent": ">=0.84.1 <0.85",
86
+ "@earendil-works/pi-tui": ">=0.84.1 <0.85",
87
87
  "typebox": "*"
88
88
  },
89
89
  "devDependencies": {
90
90
  "@biomejs/biome": "^2.4.13",
91
- "@earendil-works/pi-ai": "0.83.0",
92
- "@earendil-works/pi-coding-agent": "0.83.0",
93
- "@earendil-works/pi-tui": "0.83.0",
91
+ "@earendil-works/pi-ai": "0.84.1",
92
+ "@earendil-works/pi-coding-agent": "0.84.1",
93
+ "@earendil-works/pi-tui": "0.84.1",
94
94
  "@types/node": "^24.3.0",
95
95
  "husky": "^9.1.7",
96
- "typescript": "^6.0.2"
96
+ "rregex": "1.13.1",
97
+ "typescript": "^6.0.2",
98
+ "vitest": "4.1.9"
97
99
  },
98
100
  "scripts": {
99
101
  "prepare": "husky 2>/dev/null || true; npm run --silent build-bridge",
100
102
  "postinstall": "node scripts/postinstall-chmod.cjs",
101
103
  "build-bridge": "bash scripts/with-dist-lock.sh bash scripts/build-bridge.sh",
102
104
  "prepack": "pnpm --silent run build-bridge",
103
- "prepublishOnly": "pnpm run check && pnpm run check-pack-install",
105
+ "prepublishOnly": "pnpm run check:full && pnpm run check-pack-install",
104
106
  "setup": "./run.sh setup",
105
107
  "check-shell-quote": "./run.sh check-shell-quote",
106
108
  "check-install-surface": "./run.sh check-install-surface",
@@ -129,13 +131,22 @@
129
131
  "check-pack": "./run.sh check-pack",
130
132
  "check-pack-install": "./run.sh check-pack-install",
131
133
  "check-install-container": "./run.sh check-install-container",
134
+ "test": "vitest run",
135
+ "test:watch": "vitest",
132
136
  "test:pack": "pnpm run check-pack && pnpm run check-pack-install",
133
137
  "typecheck": "tsc --noEmit && tsc -p mcp/tsconfig.json && tsc -p scripts/tsconfig.json",
134
138
  "lint": "biome check .",
135
139
  "format": "biome check --write .",
136
140
  "check-meta-manifest-schema": "./run.sh check-meta-manifest-schema",
137
141
  "check-package-source-routing": "./run.sh check-package-source-routing",
138
- "check": "pnpm lint && pnpm typecheck && ./run.sh check-shell-quote && ./run.sh check-install-surface && ./run.sh check-entwurf-session-identity && ./run.sh check-meta-session && ./run.sh check-meta-v3-record && ./run.sh check-mailbox-receipt-state && ./run.sh check-entwurf-capabilities && ./run.sh check-capability-bundle-reach && ./run.sh check-bridge-delivery && ./run.sh smoke-pi-attach && ./run.sh check-fresh-cut-gate && ./run.sh check-meta-mailbox-state-write && ./run.sh check-meta-receiver-marker && ./run.sh check-hook-launch-topology && ./run.sh check-meta-identity-consumers && ./run.sh check-meta-capability-source && ./run.sh check-socket-probe && ./run.sh smoke-meta-honesty && ./run.sh smoke-meta-install-state && ./run.sh check-meta-doctor-oracle && ./run.sh smoke-agy-install-state && ./run.sh check-agy-permission-matrix && ./run.sh smoke-agy-statusline-state && ./run.sh smoke-agy-hooks-state && ./run.sh smoke-pi-provider-state && ./run.sh smoke-user-scope-citizen && ./run.sh smoke-meta-prune && ./run.sh smoke-meta-keyset-guard && ./run.sh check-meta-manifest-schema && ./run.sh check-package-source-routing && ./run.sh check-model-lock && ./run.sh check-dep-versions && ./run.sh check-node-floor-coherence && ./run.sh check-claude-floor-coherence && ./run.sh check-install-preflight && ./run.sh check-pi-import-surface && ./run.sh check-env-namespace && ./run.sh check-pi-runtime-version && ./run.sh check-pi-preflight && ./run.sh check-project-trust-handler && ./run.sh check-entwurf-v2-contract && ./run.sh check-entwurf-v2-lock && ./run.sh check-entwurf-v2-decider && ./run.sh check-entwurf-v2-matrix && ./run.sh check-entwurf-v2-release && ./run.sh check-entwurf-v2-send && ./run.sh check-entwurf-v2-send-fallback && ./run.sh check-entwurf-v2-mailbox && ./run.sh check-entwurf-v2-native-push && ./run.sh check-entwurf-v2-runner && ./run.sh check-entwurf-control-rpc && ./run.sh check-entwurf-v2-production && ./run.sh check-entwurf-v2-surface && ./run.sh check-entwurf-bridge-boot && ./run.sh check-entwurf-bridge-pi-free && ./run.sh check-entwurf-v2-spawn && ./run.sh check-entwurf-resume-args && ./run.sh check-entwurf-v2-spawn-production && ./run.sh check-entwurf-facts && ./run.sh check-control-socket-path && ./run.sh check-socket-discovery && ./run.sh check-meta-listing && ./run.sh check-entwurf-fact-provider && ./run.sh check-entwurf-peers-surface && ./run.sh check-entwurf-self-address && ./run.sh check-entwurf-deliverability && ./run.sh check-native-push-adapter && ./run.sh check-native-push-register && ./run.sh check-agy-sender-identity && ./run.sh check-auth-boundary && ./run.sh check-acp-provider-surface && ./run.sh check-acp-sdk-surface && ./run.sh check-acp-overlay && ./run.sh check-acp-tool-surface && ./run.sh check-acp-event-mapper && ./run.sh check-acp-stop-reason && ./run.sh check-acp-prompt-lifecycle && ./run.sh check-acp-prompt-builder && ./run.sh check-acp-config && ./run.sh check-acp-session-store && ./run.sh check-acp-backend-preflight && ./run.sh check-acp-session-reuse && ./run.sh check-acp-carrier-augment && ./run.sh check-release-gate-outcomes && ./run.sh check-probe-ordering && ./run.sh check-probe-cli-shim && ./run.sh check-acp-cortex && ./run.sh check-gate-qualification && ./run.sh check-pack"
142
+ "check": "bash scripts/check-elapsed.sh check check:core",
143
+ "check:full": "bash scripts/check-elapsed.sh check:full check:core check:hermetic check:package",
144
+ "check:core": "pnpm run check:toolchain && pnpm run check:vitest && pnpm run check:contracts",
145
+ "check:toolchain": "pnpm lint && pnpm typecheck",
146
+ "check:vitest": "./run.sh check-mux-fresh-call && ./run.sh check-acp-sdk-surface",
147
+ "check:contracts": "./run.sh check-shell-quote && ./run.sh check-entwurf-session-identity && ./run.sh check-meta-session && ./run.sh check-meta-v3-record && ./run.sh check-mailbox-receipt-state && ./run.sh check-entwurf-capabilities && ./run.sh check-capability-bundle-reach && ./run.sh check-meta-mailbox-state-write && ./run.sh check-meta-receiver-marker && ./run.sh check-meta-capability-source && ./run.sh check-model-lock && ./run.sh check-dep-versions && ./run.sh check-node-floor-coherence && ./run.sh check-claude-floor-coherence && ./run.sh check-pi-import-surface && ./run.sh check-env-namespace && ./run.sh check-pi-runtime-version && ./run.sh check-pi-preflight && ./run.sh check-project-trust-handler && ./run.sh check-entwurf-v2-contract && ./run.sh check-entwurf-v2-lock && ./run.sh check-entwurf-v2-decider && ./run.sh check-entwurf-v2-matrix && ./run.sh check-entwurf-v2-release && ./run.sh check-entwurf-v2-send && ./run.sh check-entwurf-v2-send-fallback && ./run.sh check-entwurf-v2-mailbox && ./run.sh check-entwurf-v2-native-push && ./run.sh check-entwurf-v2-runner && ./run.sh check-entwurf-v2-production && ./run.sh check-entwurf-v2-surface && ./run.sh check-entwurf-bridge-pi-free && ./run.sh check-entwurf-resume-args && ./run.sh check-resume-launch-identity && ./run.sh check-mux-placement && ./run.sh check-mux-launch && ./run.sh check-mux-resume-call && ./run.sh check-mux-parent-artifact && ./run.sh check-mux-launcher-fence && ./run.sh check-entwurf-v2-visible-resume && ./run.sh check-entwurf-facts && ./run.sh check-control-socket-path && ./run.sh check-socket-discovery && ./run.sh check-meta-listing && ./run.sh check-entwurf-fact-provider && ./run.sh check-entwurf-peers-surface && ./run.sh check-entwurf-self-address && ./run.sh check-entwurf-deliverability && ./run.sh check-native-push-adapter && ./run.sh check-native-push-register && ./run.sh check-auth-boundary && ./run.sh check-acp-overlay && ./run.sh check-acp-tool-surface && ./run.sh check-acp-event-mapper && ./run.sh check-acp-prompt-builder && ./run.sh check-acp-config && ./run.sh check-acp-session-store && ./run.sh check-acp-carrier-augment",
148
+ "check:hermetic": "./run.sh check-bridge-delivery && ./run.sh smoke-pi-attach && ./run.sh check-fresh-cut-gate && ./run.sh check-hook-launch-topology && ./run.sh check-meta-identity-consumers && ./run.sh check-socket-probe && ./run.sh smoke-meta-honesty && ./run.sh check-meta-doctor-oracle && ./run.sh check-agy-permission-matrix && ./run.sh smoke-agy-statusline-state && ./run.sh smoke-agy-hooks-state && ./run.sh smoke-pi-provider-state && ./run.sh smoke-user-scope-citizen && ./run.sh smoke-meta-prune && ./run.sh smoke-meta-keyset-guard && ./run.sh check-meta-manifest-schema && ./run.sh check-entwurf-control-rpc && ./run.sh check-entwurf-bridge-boot && ./run.sh check-meta-facts && ./run.sh check-agy-sender-identity && ./run.sh check-acp-provider-surface && ./run.sh check-acp-stop-reason && ./run.sh check-acp-prompt-lifecycle && ./run.sh check-acp-stream-hooks && ./run.sh check-acp-backend-preflight && ./run.sh check-acp-session-reuse && ./run.sh check-release-gate-outcomes && ./run.sh check-probe-ordering && ./run.sh check-probe-cli-shim && ./run.sh check-acp-cortex",
149
+ "check:package": "./run.sh check-install-surface && ./run.sh smoke-meta-install-state && ./run.sh smoke-agy-install-state && ./run.sh check-package-source-routing && ./run.sh check-install-preflight && ./run.sh check-pack"
139
150
  },
140
151
  "pi": {
141
152
  "extensions": [