@junghanacs/entwurf 0.23.0 → 0.24.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 (102) hide show
  1. package/AGENTS.md +3 -1
  2. package/BASELINE.md +2 -0
  3. package/CHANGELOG.md +307 -0
  4. package/FAQ.md +47 -0
  5. package/README.md +5 -3
  6. package/VERIFY.md +43 -2
  7. package/demo/README.md +1 -1
  8. package/docs/acp-backend-rail.md +97 -18
  9. package/docs/setup-clean-host.md +8 -7
  10. package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +33 -1
  11. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/backend-adapter.js +2 -2
  12. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/context.js +19 -5
  13. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/tool-surface.js +2 -1
  14. package/mcp/entwurf-bridge/dist/pi-extensions/lib/callback-env.js +68 -0
  15. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-contract.js +2 -2
  16. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-lock.js +1 -1
  17. package/mcp/entwurf-bridge/dist/pi-extensions/lib/fresh-call-composition.js +54 -22
  18. package/mcp/entwurf-bridge/dist/pi-extensions/lib/herdr-fresh-call.js +7 -3
  19. package/mcp/entwurf-bridge/dist/pi-extensions/lib/herdr-placement.js +13 -4
  20. package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +19 -10
  21. package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-omp.js +35 -42
  22. package/mcp/entwurf-bridge/dist/scripts/probe-bridge-command.js +1 -0
  23. package/mcp/entwurf-bridge/src/index.ts +41 -1
  24. package/mcp/entwurf-bridge/test.sh +2 -2
  25. package/package.json +15 -13
  26. package/pi-extensions/entwurf-control.ts +56 -0
  27. package/pi-extensions/lib/acp/acp-client.ts +3 -3
  28. package/pi-extensions/lib/acp/backend-adapter.ts +3 -3
  29. package/pi-extensions/lib/acp/backend.ts +20 -10
  30. package/pi-extensions/lib/acp/context.ts +30 -10
  31. package/pi-extensions/lib/acp/event-mapper.ts +5 -5
  32. package/pi-extensions/lib/acp/session-store.ts +2 -2
  33. package/pi-extensions/lib/acp/tool-surface.ts +2 -1
  34. package/pi-extensions/lib/callback-env.ts +78 -0
  35. package/pi-extensions/lib/compaction-send-guard.ts +4 -1
  36. package/pi-extensions/lib/entwurf-v2-contract.ts +2 -2
  37. package/pi-extensions/lib/entwurf-v2-lock.ts +1 -1
  38. package/pi-extensions/lib/entwurf-v2-runner.ts +1 -1
  39. package/pi-extensions/lib/fresh-call-composition.ts +56 -22
  40. package/pi-extensions/lib/herdr-fresh-call.ts +12 -4
  41. package/pi-extensions/lib/herdr-placement.ts +13 -4
  42. package/pi-extensions/lib/mux-fresh-call.ts +23 -8
  43. package/pi-extensions/meta-bridge-omp.ts +35 -48
  44. package/run.sh +327 -246
  45. package/scripts/check-acp-backend-preflight.ts +117 -9
  46. package/scripts/check-acp-carrier-augment.ts +6 -4
  47. package/scripts/check-acp-event-mapper.ts +17 -6
  48. package/scripts/check-acp-prompt-builder.ts +17 -9
  49. package/scripts/check-acp-prompt-lifecycle.ts +23 -6
  50. package/scripts/check-acp-session-reuse.ts +64 -51
  51. package/scripts/check-acp-session-store.ts +31 -8
  52. package/scripts/check-acp-stop-reason.ts +20 -2
  53. package/scripts/check-acp-stream-hooks.ts +22 -5
  54. package/scripts/check-acp-usage-accounting.ts +42 -18
  55. package/scripts/check-entwurf-bridge-boot.ts +16 -1
  56. package/scripts/check-fresh-call-dispatch.ts +4 -5
  57. package/scripts/check-gate-qualification.ts +45 -5
  58. package/scripts/check-herdr-activation.ts +73 -0
  59. package/scripts/check-herdr-fresh-call.ts +22 -6
  60. package/scripts/check-herdr-sandbox.ts +14 -2
  61. package/scripts/check-install-container.sh +1 -1
  62. package/scripts/check-pi-launch.ts +361 -0
  63. package/scripts/check-release-gate-outcomes.ts +7 -0
  64. package/scripts/inventory-verification-surface.ts +86 -10
  65. package/scripts/lib/mutation-qualify.ts +1 -1
  66. package/scripts/mutants/acp-backend-preflight.json +20 -0
  67. package/scripts/mutants/bridge-boot-resume.json +26 -0
  68. package/scripts/mutants/herdr-activation.json +16 -0
  69. package/scripts/mutants/herdr-fresh-call.json +11 -0
  70. package/scripts/mutants/herdr-plugin-build.json +1 -1
  71. package/scripts/mutants/mux-fresh-call.json +21 -8
  72. package/scripts/mutants/omp-fresh.json +5 -9
  73. package/scripts/mutants/pack-install.json +3 -3
  74. package/scripts/mutants/pi-launch.json +113 -0
  75. package/scripts/mutants/resume-args.json +6 -6
  76. package/scripts/mutants/v2-surface.json +2 -2
  77. package/scripts/mutants/v2-visible-resume.json +17 -17
  78. package/scripts/probe-bridge-command.ts +1 -0
  79. package/scripts/smoke-acp-raw-turn-live.ts +1 -1
  80. package/scripts/smoke-acp-session-reuse-live.ts +23 -5
  81. package/scripts/smoke-agy-install-state.sh +2 -2
  82. package/scripts/smoke-copilot-mcp-state.sh +1 -1
  83. package/scripts/smoke-entwurf-v2-matrix-live.ts +1 -1
  84. package/scripts/smoke-herdr-fresh-call-live.ts +56 -28
  85. package/scripts/smoke-herdr-raw-install-live.sh +877 -0
  86. package/scripts/smoke-omp-fresh-live.ts +4 -2
  87. package/scripts/smoke-omp-mcp-state.sh +1 -1
  88. package/scripts/smoke-pi-provider-state.sh +2 -2
  89. package/scripts/tsconfig.json +5 -0
  90. package/scripts/check-entwurf-facts.ts +0 -391
  91. package/scripts/check-entwurf-resume-args.ts +0 -122
  92. package/scripts/check-entwurf-v2-decider.ts +0 -657
  93. package/scripts/check-entwurf-v2-lock.ts +0 -371
  94. package/scripts/check-entwurf-v2-mailbox.ts +0 -261
  95. package/scripts/check-entwurf-v2-matrix.ts +0 -401
  96. package/scripts/check-entwurf-v2-native-push.ts +0 -221
  97. package/scripts/check-entwurf-v2-release.ts +0 -151
  98. package/scripts/check-entwurf-v2-runner.ts +0 -448
  99. package/scripts/check-entwurf-v2-send-fallback.ts +0 -350
  100. package/scripts/check-entwurf-v2-send.ts +0 -458
  101. package/scripts/check-entwurf-v2-visible-resume.ts +0 -445
  102. package/scripts/check-socket-discovery.ts +0 -401
@@ -26,7 +26,7 @@ write_mcp_fake() {
26
26
  while IFS= read -r line; do
27
27
  case "$line" in
28
28
  *'"id":1'*) printf '%s\n' '{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2024-11-05","capabilities":{},"serverInfo":{"name":"fake-entwurf-bridge","version":"0"}}}' ;;
29
- *'"id":2'*) printf '%s\n' '{"jsonrpc":"2.0","id":2,"result":{"tools":[{"name":"entwurf_v2"},{"name":"entwurf_self"},{"name":"entwurf_peers"},{"name":"entwurf_inbox_read"},{"name":"entwurf_register_native"},{"name":"entwurf_fresh_call"},{"name":"entwurf_resume_call"}]}}' ;;
29
+ *'"id":2'*) printf '%s\n' '{"jsonrpc":"2.0","id":2,"result":{"tools":[{"name":"entwurf_v2"},{"name":"entwurf_self"},{"name":"entwurf_peers"},{"name":"entwurf_inbox_read"},{"name":"entwurf_register_native"},{"name":"entwurf_fresh_call"},{"name":"entwurf_resume_call"},{"name":"entwurf_callback"}]}}' ;;
30
30
  esac
31
31
  done
32
32
  FAKE
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * smoke-entwurf-v2-matrix-live — the 5d-5 D4-b LIVE sentinel for the release-gate matrix.
3
3
  *
4
- * The deterministic sibling (check-entwurf-v2-matrix) drives the REAL decider over fakes with
4
+ * The deterministic sibling (pi-extensions/lib/entwurf-v2-decider.matrix.test.ts) drives the REAL decider over fakes with
5
5
  * ZERO IO — it fixes every (target kind → transport → lock) cell as a table. This LIVE sentinel
6
6
  * drives the REAL production `runEntwurfV2` deps against REAL OS objects on the substrate's
7
7
  * happy path, to catch what fakes cannot: a real `pi --entwurf-control` control socket + RPC,
@@ -186,17 +186,38 @@ function transcriptRecords(file: string): string[] {
186
186
  }
187
187
 
188
188
  /**
189
- * The id of the `entwurf_v2` toolCall in this record that IS the callback, or null. Pi writes a call
190
- * and its result as two records; this is one half of the join that replaces a same-record read.
189
+ * The BARE verb names, as the two surfaces this smoke reads actually spell them NOT the
190
+ * model-facing dialect. `[측정 2026-09-20, this smoke's own red fixture]` the claude MCP activity
191
+ * log carries `Calling MCP tool: entwurf_callback` / `Tool 'entwurf_callback' completed`, and pi
192
+ * writes `"type":"toolCall","name":"entwurf_callback"`; neither records
193
+ * `mcp__entwurf-bridge__entwurf_callback`. The per-host dialect is owned by
194
+ * `FRESH_CALL_CALLBACK_TOOL` / `FRESH_CALL_DELIVERY_TOOL` in `fresh-call-composition.ts` and is
195
+ * what the FRAMING says; these two are what an OBSERVER sees, and conflating them is what this
196
+ * pair of constants exists to stop.
197
+ */
198
+ const CALLBACK_VERB = "entwurf_callback";
199
+ const DELIVERY_VERB = "entwurf_v2";
200
+
201
+ /**
202
+ * The id of the CALLBACK toolCall in this record, or null. Pi writes a call and its result as two
203
+ * records; this is one half of the join that replaces a same-record read.
191
204
  *
192
- * WHAT MAKES IT THE CALLBACK, READ STRUCTURALLY `[sol 재검 2026-09-18]`. An earlier version only
193
- * asked whether the serialised arguments CONTAINED the nonce, which a call carrying `prefix+nonce`,
194
- * or the right nonce to the wrong target, satisfies just as well and then the later, correct call
195
- * is the one that produces the artifact, so the predicate could join a wrong call to a right
196
- * delivery. The arguments are an object, so they are read as one: the message must BE the nonce,
197
- * the target must be the caller that minted it, and the intent must be the one the framing names.
205
+ * WHAT MAKES IT THE CALLBACK `[ff09522]`. It used to be the ARGUMENTS: message === nonce, target
206
+ * === the caller, intent === fire-and-forget. That predicate is gone because the thing it read is
207
+ * gone the birth verb takes ZERO arguments and reads the caller and the nonce out of its own
208
+ * process env, so there is nothing in the call for a model to get wrong and nothing here to
209
+ * compare. What identifies the callback now is the verb plus the ABSENCE of an address: a call
210
+ * carrying a target or a message is a model supplying an address, which is the second address axis
211
+ * Hard Rule 2 refuses, and it is not this act.
212
+ *
213
+ * The nonce↔sender correlation the old predicate carried did not disappear with it — it moved to
214
+ * the DELIVERED ARTIFACT, which this smoke checks independently and earlier ("the child's callback
215
+ * ARRIVED … its body is a production nonce and its sender is the child the direct witness resolved
216
+ * to"). Two records, one event, same as before; only the half that names the call has changed.
198
217
  */
199
218
  function entwurfCallIdFor(record: string, nonce: string, callerGid: string): string | null {
219
+ void nonce;
220
+ void callerGid;
200
221
  let parsed: unknown;
201
222
  try {
202
223
  parsed = JSON.parse(record);
@@ -207,16 +228,14 @@ function entwurfCallIdFor(record: string, nonce: string, callerGid: string): str
207
228
  if (!Array.isArray(content)) return null;
208
229
  for (const part of content) {
209
230
  const call = part as { type?: unknown; name?: unknown; id?: unknown; arguments?: unknown };
210
- if (call.type !== "toolCall" || call.name !== "entwurf_v2") continue;
211
- const args = call.arguments as
212
- | { message?: unknown; target?: unknown; intent?: unknown; wants_reply?: unknown }
213
- | undefined;
214
- if (typeof args !== "object" || args === null) continue;
215
- if (args.message !== nonce) continue;
216
- if (args.target !== callerGid) continue;
217
- if (args.intent !== "fire-and-forget") continue;
218
- // The framing asks for no reply; a call that asked for one is a different act.
219
- if (args.wants_reply === true) continue;
231
+ if (call.type !== "toolCall" || call.name !== CALLBACK_VERB) continue;
232
+ // Zero-argument verb: absent, null and `{}` are all the shape it is called with. A supplied
233
+ // target or message is a model naming an address the env already owns — not this act.
234
+ const args = call.arguments as { message?: unknown; target?: unknown } | undefined | null;
235
+ if (args !== undefined && args !== null) {
236
+ if (typeof args !== "object" || Array.isArray(args)) continue;
237
+ if (args.target !== undefined || args.message !== undefined) continue;
238
+ }
220
239
  return typeof call.id === "string" ? call.id : null;
221
240
  }
222
241
  return null;
@@ -864,23 +883,27 @@ async function main(): Promise<void> {
864
883
  // OFFERS exactly that corroboration ("you can corroborate the caller first if you
865
884
  // want to"), so the old oracle contradicted our own prompt and would have forbidden
866
885
  // the behaviour we asked for. What still must hold is everything the claim was
867
- // actually about — the callback lands before any work, it completes, and no
868
- // entwurf_v2 in the log failed or timed out.
886
+ // actually about — the callback lands before any work, it completes, and neither the
887
+ // callback verb nor the delivery verb failed or timed out anywhere in the log.
888
+ // `[ff09522]` the verb this joins on MOVED: the birth callback is `entwurf_callback`
889
+ // and `entwurf_v2` is now only the DELIVERY the closing line asks for. Joining on the
890
+ // old name does not merely miss — it mis-attributes, finding the result-delivery call
891
+ // and then reporting the real callback as forbidden work before it.
869
892
  // ONLY what the framing actually offers. `entwurf_self` used to sit in this set and
870
893
  // nothing ever proposed it to the child — an allowance for a tool we do not mention
871
894
  // widens the oracle without widening the contract (sol D1, 2026-09-18).
872
895
  const READ_ONLY_FIRST = new Set(["entwurf_peers"]);
873
- const beforeCallback = order.slice(0, Math.max(order.indexOf("entwurf_v2"), 0));
896
+ const beforeCallback = order.slice(0, Math.max(order.indexOf(CALLBACK_VERB), 0));
874
897
  // THE JOIN THIS AXIS CAN ACTUALLY MAKE. The claude MCP log records WHICH tool was
875
898
  // called and whether it completed — never its arguments or its result body — so
876
- // "the first entwurf_v2 completed" alone would also be true of a call that delivered
899
+ // "the first callback completed" alone would also be true of a call that delivered
877
900
  // somebody else's nonce or came back as a semantic reject over a successful
878
901
  // transport. The second artifact closes it: the caller's own delivered message
879
902
  // carries the EXACT nonce and the child as its sender, and its enqueue timestamp has
880
903
  // to fall inside the window of that first call. Two independent records, one event.
881
- const callAt = indexOfEntry(childActivity, (e) => (e.debug ?? "") === "Calling MCP tool: entwurf_v2");
904
+ const callAt = indexOfEntry(childActivity, (e) => (e.debug ?? "") === `Calling MCP tool: ${CALLBACK_VERB}`);
882
905
  const doneAt = indexOfEntry(childActivity, (e) =>
883
- (e.debug ?? "").startsWith("Tool 'entwurf_v2' completed successfully"),
906
+ (e.debug ?? "").startsWith(`Tool '${CALLBACK_VERB}' completed successfully`),
884
907
  );
885
908
  const callbackStamp = deliveredMessages(String(fenced.ENTWURF_META_MAILBOX_DIR), callerGid)[0]?.file ?? "";
886
909
  const stampedAt = Date.parse(
@@ -902,14 +925,19 @@ async function main(): Promise<void> {
902
925
  // here, and a claim about "any work" is wider than the oracle. What IS observed, and
903
926
  // is the thing the framing actually asks for, is the ORDER OF TOOLS: the first
904
927
  // non-read-only tool this child called was the callback.
905
- `${cell.label}: the child's FIRST non-read-only tool call was the callbackthe FIRST entwurf_v2 completed successfully and the delivered callback carrying this exact nonce was enqueued inside that call's own window, preceded only by the read-only corroboration the framing offers, with no failed or timed-out entwurf_v2 anywhere in its log (this cell reads tool activity only; assistant text is not observable on this rail)`,
928
+ `${cell.label}: the child's FIRST non-read-only tool call was the zero-argument ${CALLBACK_VERB} it completed successfully and the delivered callback carrying this exact nonce was enqueued inside that call's own window, preceded only by the read-only corroboration the framing offers, with neither ${CALLBACK_VERB} nor ${DELIVERY_VERB} failing or timing out anywhere in its log (this cell reads tool activity only; assistant text is not observable on this rail)`,
906
929
  childActivity.length > 0 &&
907
- order.includes("entwurf_v2") &&
930
+ order.includes(CALLBACK_VERB) &&
908
931
  beforeCallback.every((name) => READ_ONLY_FIRST.has(name)) &&
909
932
  within &&
910
933
  callbackArrived &&
911
- !childActivity.some(
912
- (e) => (e.debug ?? "").startsWith("Tool 'entwurf_v2' failed") || (e.error ?? "").includes("entwurf_v2"),
934
+ // BOTH verbs, because the child uses both: the birth callback and, after the
935
+ // work, the delivery verb the framing's closing line names. A failed delivery
936
+ // is as fatal to this rail as a failed callback.
937
+ ![CALLBACK_VERB, DELIVERY_VERB].some((verb) =>
938
+ childActivity.some(
939
+ (e) => (e.debug ?? "").startsWith(`Tool '${verb}' failed`) || (e.error ?? "").includes(verb),
940
+ ),
913
941
  ),
914
942
  );
915
943
  }