@junghanacs/entwurf 0.13.0 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/AGENTS.md +5 -3
  2. package/BASELINE.md +57 -203
  3. package/CHANGELOG.md +19 -0
  4. package/CONTRIBUTING.md +1 -1
  5. package/DELIVERY.md +117 -261
  6. package/README.md +68 -408
  7. package/VERIFY.md +58 -87
  8. package/demo/README.md +1 -1
  9. package/docs/acp-backend-rail.md +231 -0
  10. package/docs/external-mcp-host.md +132 -0
  11. package/docs/fresh-cut-policy.md +99 -0
  12. package/docs/setup-clean-host.md +123 -328
  13. package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +21 -48
  14. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/acp-client.js +12 -0
  15. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/backend-adapter.js +11 -10
  16. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/config.js +1 -1
  17. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/engraving.js +42 -1
  18. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/models.js +4 -4
  19. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/overlay.js +3 -3
  20. package/mcp/entwurf-bridge/src/index.ts +21 -48
  21. package/package.json +12 -9
  22. package/pi-extensions/entwurf-control.ts +20 -49
  23. package/pi-extensions/lib/acp/acp-client.ts +22 -0
  24. package/pi-extensions/lib/acp/augment.ts +42 -3
  25. package/pi-extensions/lib/acp/backend-adapter.ts +12 -11
  26. package/pi-extensions/lib/acp/backend.ts +305 -36
  27. package/pi-extensions/lib/acp/config.ts +1 -1
  28. package/pi-extensions/lib/acp/engraving.ts +43 -1
  29. package/pi-extensions/lib/acp/event-mapper.ts +16 -13
  30. package/pi-extensions/lib/acp/models.ts +4 -4
  31. package/pi-extensions/lib/acp/overlay.ts +3 -3
  32. package/prompts/engraving.md +13 -5
  33. package/run.sh +244 -108
  34. package/scripts/check-acp-carrier-augment.ts +246 -10
  35. package/scripts/check-acp-cortex.ts +5 -5
  36. package/scripts/check-acp-prompt-lifecycle.ts +565 -0
  37. package/scripts/check-acp-sdk-surface.ts +60 -21
  38. package/scripts/check-acp-stop-reason.ts +342 -0
  39. package/scripts/check-entwurf-v2-surface.ts +41 -0
  40. package/scripts/check-gate-qualification.ts +6 -3
  41. package/scripts/check-probe-ordering.ts +39 -7
  42. package/scripts/check-release-gate-outcomes.ts +332 -0
  43. package/scripts/lib/live-skip.ts +33 -0
  44. package/scripts/lib/probe-acp-turn.ts +33 -12
  45. package/scripts/lib/step-outcome.sh +88 -0
  46. package/scripts/mutants/acp-augment.json +106 -0
  47. package/scripts/mutants/acp-cortex.json +2 -2
  48. package/scripts/mutants/acp-prompt-lifecycle.json +100 -0
  49. package/scripts/mutants/acp-stop-reason.json +80 -0
  50. package/scripts/mutants/probe-ordering.json +27 -5
  51. package/scripts/mutants/release-gate.json +105 -0
  52. package/scripts/mutants/v2-surface.json +26 -15
  53. package/scripts/smoke-acp-bundled-mcp-live.ts +4 -3
  54. package/scripts/smoke-acp-carrier-augment-live.ts +2 -2
  55. package/scripts/smoke-acp-cortex-live.ts +8 -14
  56. package/scripts/smoke-acp-long-turn-live.ts +185 -0
  57. package/scripts/smoke-acp-mcp-live.ts +2 -2
  58. package/scripts/smoke-acp-memory-containment-live.ts +2 -2
  59. package/scripts/smoke-acp-ordering-probe-live.ts +2 -2
  60. package/scripts/smoke-acp-overlay-live.ts +2 -2
  61. package/scripts/smoke-acp-provider-live.ts +2 -2
  62. package/scripts/smoke-acp-raw-turn-live.ts +3 -3
  63. package/scripts/smoke-acp-session-reuse-live.ts +2 -2
  64. package/scripts/smoke-acp-skill-live.ts +2 -2
  65. package/scripts/smoke-acp-socket-citizen-live.ts +4 -3
  66. package/scripts/smoke-acp-v2-send-live.ts +4 -3
  67. package/scripts/smoke-agy-native-push-live.ts +4 -3
  68. package/scripts/smoke-claude-native-resume-live.sh +13 -3
  69. package/scripts/smoke-entwurf-chain-live.ts +352 -0
  70. package/scripts/smoke-entwurf-v2-matrix-live.ts +2 -2
  71. package/scripts/smoke-entwurf-v2-spawn-resume-live.ts +2 -4
  72. package/scripts/smoke-meta-install-state.sh +4 -0
package/run.sh CHANGED
@@ -51,6 +51,22 @@ PROVIDER_ID="entwurf"
51
51
  # surfaces). Under an installed package it REFUSES rather than falling back to raw
52
52
  # .ts — a fallback would just re-raise the fence error with a worse message.
53
53
  # check-install-surface pins both halves statically.
54
+ # Release-gate STEP OUTCOME protocol (P1) — loaded LAZILY, never at top level.
55
+ #
56
+ # Only two surfaces need it: `release_gate`, which classifies each step's exit
57
+ # code, and the LIVE smoke wrappers, which return `$ENTWURF_STEP_SKIP_EXIT` when
58
+ # a prerequisite is missing. Both are dev-only. Sourcing it unconditionally at
59
+ # the top made EVERY subcommand depend on `scripts/lib/` being present, and
60
+ # check-fresh-cut-gate I9c caught the consequence immediately: on an installed
61
+ # tree assembled without that path, run.sh died with a bash "No such file"
62
+ # instead of printing the refusal it is supposed to print. An operator verb must
63
+ # never fail on a dev-only dependency, so the load happens where it is used.
64
+ entwurf_require_step_outcome() {
65
+ [ -n "${ENTWURF_STEP_SKIP_EXIT:-}" ] && return 0
66
+ # shellcheck source=scripts/lib/step-outcome.sh
67
+ . "$REPO_DIR/scripts/lib/step-outcome.sh"
68
+ }
69
+
54
70
  run_ts() {
55
71
  local rel="$1"; shift
56
72
  case "$REPO_DIR" in
@@ -73,7 +89,7 @@ usage() {
73
89
  cat <<'EOF'
74
90
  Usage:
75
91
  ./run.sh setup [project-dir] # ONE confident install: pnpm install + install + meta-bridge (if native harness) + v2 install smoke (LIVE substrate = release-gate)
76
- ./run.sh release-gate [project-dir] [--allow-skip-gemini] # SINGLE release gate: full static (pnpm check) + the v2-native live gates (v2 matrix/spawn-resume-live, check-bridge, RGG) + the ACP plugin acceptance floor (12 LIVE smokes: socket-citizen/raw-turn/overlay/provider/session-reuse/carrier-augment/memory-containment/rgg/mcp/skill/bundled-mcp/v2-send). TWO-TIER summary: MUST (release-blocking, owns the exit code — "green" applies here) + BEHAVIOR (advisory, non-blocking: RGG positives model-in-loop turn). LIVE-gated MUST steps HONEST-SKIP when LIVE!=1 (a CUT needs LIVE=1, SKIP=0). --allow-skip-gemini accepted-but-ignored (back-compat). final cut authorization is GLG's.
92
+ ./run.sh release-gate [project-dir] [--cut] [--allow-skip-gemini] # SINGLE release gate: full static (pnpm check) + the v2-native live gates (v2 matrix/spawn-resume-live, check-bridge, RGG) + the ACP plugin acceptance floor (12 LIVE smokes: socket-citizen/raw-turn/overlay/provider/session-reuse/carrier-augment/memory-containment/rgg/mcp/skill/bundled-mcp/v2-send) + the two axes the aggregate used to omit silently (spawn-live/claude-native-resume; Cortex stays a documented on-demand direct call) + the cross-harness delivery chain (smoke-entwurf-chain-live). TWO-TIER summary: MUST (release-blocking, owns the exit code — "green" applies here) + BEHAVIOR (advisory, non-blocking: RGG positives model-in-loop turn). STEP OUTCOME protocol: every step is INVOKED and reports its own PASS / SKIP (exit 97, a prerequisite it does not have) / FAIL — a skip is never counted as a pass. Without --cut this is the unattended diagnostic (SKIPs reported, exit 0). WITH --cut it is read as release acceptance and ANY MUST SKIP is red, which is what makes "a CUT needs LIVE=1, SKIP=0" executable instead of prose. --allow-skip-gemini accepted-but-ignored (back-compat). final cut authorization is GLG's.
77
93
  ./run.sh check-bridge # entwurf-bridge direct MCP smoke + protocol/negative-path test.sh (live substrate = v2 live smokes)
78
94
  ./run.sh check-entwurf-bridge-boot # deterministic gate (5d-5-pre, G1a/G1b, IN pnpm check): boot start.sh under strip-types + assert v2 fence graph loads + entwurf_v2 registered/schema; tools/list only, no auth/side-effect
79
95
  ./run.sh check-entwurf-bridge-pi-free # deterministic gate (0.12.1 A, IN pnpm check): static — bridge index eager value-import closure must carry no @earendil-works/pi-* (type-only + dynamic import excluded); proves the meta-bridge boots pi-free
@@ -129,6 +145,8 @@ Usage:
129
145
  ./run.sh check-meta-doctor-oracle # 0.12.8 (#51): detection power of the release ORACLE — healthy fixture must reach `doctor: PASS`, then 21 planted defects (retired shell form, partial hand-patch, launcher bypass/repoint/provenance loss, malformed exec args, owner type drift, extra leaf/group, doorbell asyncRewake/path/timeout, no live bridge, stale receiver, ambiguous/missing cache, missing hook log/writer, failing CLI probes) must each turn it FAIL naming their own cause, plus a positive case pinning that a long-writing CLI is NOT a false negative. Offline/deterministic (deps: bash+node+python3)
130
146
  ./run.sh smoke-agy-install-state # agy MCP + exact permission ownership regression: isolated HOME+XDG, adopt/state/inverse, symlink refuse, setup degrade. Offline/deterministic
131
147
  ./run.sh check-agy-permission-matrix # AGY permission CONTRACT SPACE as a literal table (55 cells): parser-state × operation × settings × ownership × precedence with stated exclusion rules; expectations are hand-written literals, never read from the SUT. Offline/deterministic (deps: python3)
148
+ ./run.sh smoke-entwurf-chain-live # LIVE cross-harness delivery chain: native Claude Code -> pi GPT -> pi ACP Sonnet -> mailbox terminus, proving sender identity/replyable at every hop and a real read receipt at the end. Prerequisites (claude on PATH, pi credentials per backend) report protocol SKIP, never a pass
149
+ ./run.sh check-release-gate-outcomes # release-gate STEP OUTCOME protocol (P1): one skip exit code shared by the shell + TS halves, classifier never rounds a skip up to a pass, `--cut` refuses a MUST SKIP while a bare diagnostic stays exit 0, no LIVE smoke keeps the old exit-0 skip shape, and both real skip surfaces are INVOKED and observed to propagate the code
132
150
  ./run.sh check-gate-qualification # kill-proof qualification (the gate-of-gates): runner self-test (classifier truth table + synthetic negatives incl. wrong-reason/hang/control-red/impurity) + committed mutant manifests (scripts/mutants/*.json) run in an isolated snapshot repo under control→mutant→restore→control; the real checkout is never written. Evidence = claim IDs + killed mutant IDs, never assertion counts
133
151
  ./run.sh check-probe-ordering # §11-7 ordering-probe deterministic gate: raw-client SAMENESS pinned to backend.ts (sequence/args/timeouts/permission policy — the probe may never measure a lookalike), phase attribution incl. set-model, probe-mode fixture wire markers (delay honored, probeRunId REQUIRED, smoke-acp-mcp-live legacy compat), event-log door integrity (reserved keys refused at write; unknown marker name, broken sort axis, or a payload the classifier cannot judge on is MALFORMED, never a quiet event), and the §11-7 paired-verdict truth table (P0/I0 outside the space, phase-qualified D, B promotion ladder, C, A two-delay rule). Offline/deterministic; kill-proofed via scripts/mutants/probe-ordering.json
134
152
  ./run.sh check-probe-cli-shim # §11-7-c B-name-snapshot PRODUCER gate: the CLI shim driven as a REAL process against fake CLIs (no API, no cost). Proves what a defect would buy — FABRICATED evidence (a malformed init is never reported as an empty name set; the boot report carries the true target path+sha256 the classifier verifies against the roster), a DESTROYED turn (byte transparency across mid-UTF8/CRLF/oversized/unterminated framing, exit-code fidelity, signal re-raise, inbound signal forwarding, stderr passthrough, stdout backpressure), and LEAKED operator state (exact-allowlist env scrub, no argv/env/prompt body in the log). Offline/deterministic; kill-proofed via scripts/mutants/probe-ordering.json
@@ -984,9 +1002,10 @@ smoke_entwurf_v2_spawn_live() {
984
1002
  # 5c-3a watcher's timeout→kill→child-exited→release integration on a live process. It does
985
1003
  # NOT spawn a real `pi --entwurf-control` resume (that is the 5d surface matrix). Run once
986
1004
  # before 5d and record the result: LIVE=1 ./run.sh smoke-entwurf-v2-spawn-live
1005
+ entwurf_require_step_outcome
987
1006
  if [ "${LIVE:-}" != "1" ]; then
988
- echo "[smoke-entwurf-v2-spawn-live] skipped — set LIVE=1 to run (spawns real children + opens a real unix socket)."
989
- return 0
1007
+ echo "[entwurf:skip] smoke-entwurf-v2-spawn-live — set LIVE=1 to run (spawns real children + opens a real unix socket)."
1008
+ return "$ENTWURF_STEP_SKIP_EXIT"
990
1009
  fi
991
1010
  run_ts scripts/smoke-entwurf-v2-spawn-live.ts
992
1011
  }
@@ -1051,6 +1070,18 @@ smoke_acp_memory_containment_live() {
1051
1070
  run_ts scripts/smoke-acp-memory-containment-live.ts
1052
1071
  }
1053
1072
 
1073
+ smoke_acp_long_turn_live() {
1074
+ # 0.13.1 LIVE acceptance for the prompt-lifecycle contract — OUT of pnpm check,
1075
+ # needs LIVE=1. Drives ONE real pi provider turn whose tool work (default
1076
+ # 3 x 240s foreground wait) deliberately outlasts the RETIRED 600s prompt cutoff, and
1077
+ # requires it to finish as ONE turn: the nonce arrives, elapsed > 600000ms, the
1078
+ # persisted transcript shows exactly ONE cold ACP bootstrap (no replay), and no
1079
+ # retry/timeout is reported anywhere. Takes >12 minutes by construction.
1080
+ # Overrides: ENTWURF_ACP_LONG_TURN_{MODEL,SLEEP_SECONDS,ROUNDS,HORIZON_MS}.
1081
+ # LIVE=1 ./run.sh smoke-acp-long-turn-live
1082
+ run_ts scripts/smoke-acp-long-turn-live.ts
1083
+ }
1084
+
1054
1085
  smoke_acp_provider_live() {
1055
1086
  # S2c acceptance smoke (ACP plugin on v2) — OUT of pnpm check, needs LIVE=1.
1056
1087
  # Drives the REAL pi PROVIDER path end to end: a real `pi` loads this
@@ -1181,16 +1212,16 @@ smoke_acp_rgg_live() {
1181
1212
 
1182
1213
  smoke_acp_cortex_live() {
1183
1214
  # On-demand LIVE smoke for the Cortex (Snowflake Cortex Code) ACP backend — the
1184
- # first NON-claude adapter on the rail (docs/acp-backend-rail.md §6). OUT of
1215
+ # first NON-claude adapter on the rail (docs/acp-backend-rail.md, Cortex audit). OUT of
1185
1216
  # `pnpm check` AND OUTSIDE the claude-only aggregate LIVE floor (capability
1186
1217
  # dignity, invariant #7): a host with no cortex install / no Snowflake auth must
1187
1218
  # not redden a release for a backend it does not run, so this is deliberately
1188
1219
  # absent from release-gate's 11-smoke ACP acceptance floor. That is a WIRING
1189
1220
  # decision, not a lower evidence bar — a cut that SHIPS cortex still owes a
1190
1221
  # deliberate run of this smoke, and the aggregate gate's silence is not a pass
1191
- # (rail §11-8 tail). Drives one real
1222
+ # (ACP rail Cortex verification boundary). Drives one real
1192
1223
  # cortex ACP turn through the entwurf provider path (outbound entwurf_v2 +
1193
- # dual-HOME overlay facts + process-group reclaim — CP2, rail §11-8).
1224
+ # dual-HOME overlay facts + process-group reclaim — CP2).
1194
1225
  # HONEST-SKIP (exit 0) when LIVE!=1 OR `cortex` is not on PATH OR no
1195
1226
  # connection is pinned — the live turn needs `cortex` installed with the
1196
1227
  # operator's own web-login auth already present (there is no `cortex auth`
@@ -1202,13 +1233,14 @@ smoke_acp_cortex_live() {
1202
1233
  # operator settings.json where a default would live. PR #40 called this
1203
1234
  # `smoke-cortex`; the canonical family name is smoke-acp-cortex-live.
1204
1235
  # LIVE=1 ENTWURF_ACP_CORTEX_CONNECTION=<conn> ./run.sh smoke-acp-cortex-live
1236
+ entwurf_require_step_outcome
1205
1237
  if [ "${LIVE:-}" != "1" ]; then
1206
- echo "[smoke-acp-cortex-live] skipped — set LIVE=1 (+ ENTWURF_ACP_CORTEX_CONNECTION) to run."
1207
- return 0
1238
+ echo "[entwurf:skip] smoke-acp-cortex-live — set LIVE=1 (+ ENTWURF_ACP_CORTEX_CONNECTION) to run."
1239
+ return "$ENTWURF_STEP_SKIP_EXIT"
1208
1240
  fi
1209
1241
  if ! command -v cortex >/dev/null 2>&1; then
1210
- echo "[smoke-acp-cortex-live] skipped — cortex not on PATH (install Snowflake Cortex Code and complete its own login flow)."
1211
- return 0
1242
+ echo "[entwurf:skip] smoke-acp-cortex-live — cortex not on PATH (install Snowflake Cortex Code and complete its own login flow)."
1243
+ return "$ENTWURF_STEP_SKIP_EXIT"
1212
1244
  fi
1213
1245
  run_ts scripts/smoke-acp-cortex-live.ts
1214
1246
  }
@@ -1225,9 +1257,10 @@ smoke_entwurf_v2_matrix_live() {
1225
1257
  # stay deterministic. Honest skip when LIVE!=1 so the release-gate is runnable unattended.
1226
1258
  # Model: ENTWURF_LIVE_TARGET=<provider>/<model> (default openai-codex/gpt-5.4).
1227
1259
  # LIVE=1 ./run.sh smoke-entwurf-v2-matrix-live
1260
+ entwurf_require_step_outcome
1228
1261
  if [ "${LIVE:-}" != "1" ]; then
1229
- echo "[smoke-entwurf-v2-matrix-live] skipped — set LIVE=1 to run (spawns a real pi --entwurf-control + opens a real socket)."
1230
- return 0
1262
+ echo "[entwurf:skip] smoke-entwurf-v2-matrix-live — set LIVE=1 to run (spawns a real pi --entwurf-control + opens a real socket)."
1263
+ return "$ENTWURF_STEP_SKIP_EXIT"
1231
1264
  fi
1232
1265
  run_ts scripts/smoke-entwurf-v2-matrix-live.ts
1233
1266
  }
@@ -1247,9 +1280,10 @@ smoke_entwurf_v2_spawn_resume_live() {
1247
1280
  # Model: ENTWURF_LIVE_TARGET=<provider>/<model> (default openai-codex/gpt-5.4);
1248
1281
  # ENTWURF_SPAWN_RESUME_ASSISTANT_TIMEOUT_MS (default 180000).
1249
1282
  # LIVE=1 ./run.sh smoke-entwurf-v2-spawn-resume-live
1283
+ entwurf_require_step_outcome
1250
1284
  if [ "${LIVE:-}" != "1" ]; then
1251
- echo "[smoke-entwurf-v2-spawn-resume-live] skipped — set LIVE=1 to run (spawns a real pi resume child + opens a real socket)."
1252
- return 0
1285
+ echo "[entwurf:skip] smoke-entwurf-v2-spawn-resume-live — set LIVE=1 to run (spawns a real pi resume child + opens a real socket)."
1286
+ return "$ENTWURF_STEP_SKIP_EXIT"
1253
1287
  fi
1254
1288
  run_ts scripts/smoke-entwurf-v2-spawn-resume-live.ts
1255
1289
  }
@@ -1472,12 +1506,13 @@ assert.equal(peerTui, piAi,
1472
1506
  // floor tracks the devDep pin so a consumer can't install against a pi lacking
1473
1507
  // the public trust exports the bridge imports at the pinned minor, AND an upper
1474
1508
  // bound at the next minor stops a fresh install from silently pulling a future
1475
- // pi (past the declared ceiling — 0.83+ at the current 0.82.1 pin) whose
1509
+ // pi (past the declared ceiling — 0.84+ at the current 0.83.0 pin) whose
1476
1510
  // internal export surface has drifted from the one we typecheck against.
1477
1511
  // pi moves its public surface every minor (the 0.79→0.80 getModels→provider-
1478
1512
  // factory churn is exactly this), so an open `>=` floor is exactly how the next
1479
- // installer re-acquires the drift. Expected
1480
- // shape: `>=<devDep> <0.<minor+1>` (e.g. `>=0.82.1 <0.83`).
1513
+ // installer re-acquires the drift. The floor is also the HARD MINIMUM a consumer
1514
+ // install resolves: at `>=0.83.0` an existing 0.82.x host is upgraded, not kept.
1515
+ // Expected shape: `>=<devDep> <0.<minor+1>` (e.g. `>=0.83.0 <0.84`).
1481
1516
  const [piMaj, piMin] = piAi.split('.').map(Number);
1482
1517
  assert.equal(piMaj, 0,
1483
1518
  `pi pin major must stay 0 for the next-minor ceiling rule (got ${piAi}); revisit check-dep-versions when pi reaches 1.x`);
@@ -2263,7 +2298,7 @@ check_acp_provider_surface() {
2263
2298
  check_acp_sdk_surface() {
2264
2299
  # Deterministic gate for the S2a ACP SDK dependency surface. Pins the three
2265
2300
  # ACP runtime deps to the current oracle versions (@agentclientprotocol/sdk
2266
- # 1.3.0 + claude-agent-acp 0.62.0 + @anthropic-ai/sdk 0.100.1), locks the
2301
+ # 1.3.0 + claude-agent-acp 0.64.0 + @anthropic-ai/sdk 0.100.1), locks the
2267
2302
  # peer-resolution that keeps claude-agent-sdk satisfiable (0.100.1, not the
2268
2303
  # stale 0.91.1), asserts the wire SDK still value-exports the symbols the raw
2269
2304
  # turn needs (silent-rename gate), and forbids any source-level anthropic SDK
@@ -2308,6 +2343,37 @@ check_acp_event_mapper() {
2308
2343
  run_ts scripts/check-acp-event-mapper.ts
2309
2344
  }
2310
2345
 
2346
+ check_acp_stop_reason() {
2347
+ # Deterministic gate for the ACP stop-reason contract. Drives every member of
2348
+ # the closed ACP terminal set (end_turn / max_tokens / max_turn_requests /
2349
+ # refusal / cancelled), plus an unrecognized reason and an ABSENT one, end to
2350
+ # end through streamAcpTurn against a fake connection — asserting the event
2351
+ # kind, the event reason, and the final message's stopReason / rawStopReason /
2352
+ # errorMessage together. Forbids the collapse-to-"stop" default that reported
2353
+ # refusals and exhausted turn budgets as finished answers. Also pins the
2354
+ # "pending" seed on the freshly created stream state.
2355
+ section "ACP stop reason (terminal set → pi verdict)"
2356
+ run_ts scripts/check-acp-stop-reason.ts
2357
+ }
2358
+
2359
+ check_acp_prompt_lifecycle() {
2360
+ # Deterministic gate for the ACP prompt LIFECYCLE contract. A prompt has no
2361
+ # wall-clock cutoff: it ends when it resolves, when the operator aborts, or
2362
+ # when the child dies / its stdio ends. Drives streamAcpTurn against a fake
2363
+ # ACP child + connection for eight cells — a quiet in-flight turn is not
2364
+ # sealed and later completes on its ORIGINAL prompt; an abort sends ACP
2365
+ # session/cancel first (cancelled→aborted, no signal to a cooperating child);
2366
+ # a wedged agent is torn down after the bounded grace and still returns; a
2367
+ # mid-prompt child death reports exit status + stderr tail on BOTH the new and
2368
+ # the reuse path; a death BETWEEN turns is announced once by the next turn
2369
+ # while a teardown WE performed stays silent — plus pi's own
2370
+ # isRetryableAssistantError as
2371
+ # the oracle that our prompt-phase failure text is not a transient
2372
+ # (cold-replay) error.
2373
+ section "ACP prompt lifecycle (no wall clock; abort/child-death endings)"
2374
+ run_ts scripts/check-acp-prompt-lifecycle.ts
2375
+ }
2376
+
2311
2377
  check_acp_prompt_builder() {
2312
2378
  # Deterministic gate for the S2d bootstrapPath-scoped ACP prompt builder (핀4).
2313
2379
  # Proves prompt SCOPE follows bootstrapPath: new=full transcript (history
@@ -2383,11 +2449,11 @@ check_acp_carrier_augment() {
2383
2449
  check_acp_cortex() {
2384
2450
  # Deterministic gate for the Cortex (Snowflake Cortex Code) ACP backend — the
2385
2451
  # first non-claude adapter on the rail, landed 0.13.0 on the CP0-measured
2386
- # contract (docs/acp-backend-rail.md §4/§6/§11-8). The cortex source is one
2452
+ # contract (docs/acp-backend-rail.md, Cortex audit D1–D10). The cortex source is one
2387
2453
  # `cortexAdapter` object (backend-adapter.ts) + its curated surface (models.ts)
2388
2454
  # + the dual-HOME overlay (overlay.ts); the 결합 규칙 requires the gate to land
2389
- # WITH it, and §6 said EXTEND the check-acp-* family, so cortex's whole
2390
- # deterministic axis lives here. Locks (each §11-8 D-pinned): the GLG-decided
2455
+ # WITH it, extending the check-acp-* family, so cortex's whole
2456
+ # deterministic axis lives here. Locks (each Cortex-audit D-pinned): the GLG-decided
2391
2457
  # 4-row curation rides real registry bases and the `cortex-` prefix routes to
2392
2458
  # cortexAdapter; launch is `cortex acp serve` with NO -m — the model is
2393
2459
  # enforced per turn via session/set_config_option with the native id (E);
@@ -2776,21 +2842,21 @@ _check_pack_install_impl() {
2776
2842
  printf '%s\n' '{ "name": "entwurf-install-smoke", "version": "0.0.0", "private": true }' > "$tmp/package.json"
2777
2843
 
2778
2844
  # pi-agent-core is pinned even though we never import it: pi-coding-agent depends
2779
- # on it by CARET (`^0.82.1`), so with no lockfile in this fresh temp project it
2845
+ # on it by CARET (`^0.83.0`), so with no lockfile in this fresh temp project it
2780
2846
  # floats to whatever pi published last — and that newer core then drags a NESTED
2781
2847
  # pi-ai of its own. Measured 2026-07-21: pinning only the three we import left
2782
2848
  # pi-agent-core@0.80.10 + pi-ai@0.80.10 in the tree while the gate still announced
2783
2849
  # "pinned pi 0.80.7". The gate would then be verifying an UNVERIFIED runtime — the
2784
2850
  # exact class this cut exists to close. Pin every @earendil-works package that
2785
2851
  # constitutes the pi runtime, not just the ones whose types we touch.
2786
- echo "[check-pack-install] pnpm add into $tmp (with 0.82.x peers + typebox)"
2852
+ echo "[check-pack-install] pnpm add into $tmp (with 0.83.x peers + typebox)"
2787
2853
  local install_log
2788
2854
  install_log=$(cd "$tmp" && pnpm add \
2789
2855
  "$tgz_path" \
2790
- "@earendil-works/pi-ai@0.82.1" \
2791
- "@earendil-works/pi-coding-agent@0.82.1" \
2792
- "@earendil-works/pi-tui@0.82.1" \
2793
- "@earendil-works/pi-agent-core@0.82.1" \
2856
+ "@earendil-works/pi-ai@0.83.0" \
2857
+ "@earendil-works/pi-coding-agent@0.83.0" \
2858
+ "@earendil-works/pi-tui@0.83.0" \
2859
+ "@earendil-works/pi-agent-core@0.83.0" \
2794
2860
  "typebox@latest" \
2795
2861
  --ignore-workspace --ignore-scripts 2>&1) || {
2796
2862
  fail "[check-pack-install] pnpm add failed:"
@@ -2800,17 +2866,17 @@ _check_pack_install_impl() {
2800
2866
 
2801
2867
  # A pin is a wish until the resolved tree is read back. Assert it: EVERY
2802
2868
  # @earendil-works pi package present — direct or transitive, top level or nested —
2803
- # must be the pinned 0.82.1. Anything else means an unpinned caret floated and the
2869
+ # must be the pinned 0.83.0. Anything else means an unpinned caret floated and the
2804
2870
  # rest of this gate would be exercising a runtime nobody verified, while still
2805
- # printing "pinned pi 0.82.1". Fail loud instead of proving the wrong floor.
2871
+ # printing "pinned pi 0.83.0". Fail loud instead of proving the wrong floor.
2806
2872
  local leaked_pi
2807
- leaked_pi=$(ls "$tmp/node_modules/.pnpm" 2>/dev/null | grep '^@earendil-works+pi-' | grep -v '@0\.82\.1' || true)
2873
+ leaked_pi=$(ls "$tmp/node_modules/.pnpm" 2>/dev/null | grep '^@earendil-works+pi-' | grep -v '@0\.83\.0' || true)
2808
2874
  if [ -n "$leaked_pi" ]; then
2809
- fail "[check-pack-install] UNVERIFIED pi runtime resolved into the install tree (expected only 0.82.1):"
2875
+ fail "[check-pack-install] UNVERIFIED pi runtime resolved into the install tree (expected only 0.83.0):"
2810
2876
  printf '%s\n' "$leaked_pi" | sed 's/^/ /' >&2
2811
2877
  return 1
2812
2878
  fi
2813
- echo "[check-pack-install] pi runtime tree pin verified: every @earendil-works pi package is 0.82.1"
2879
+ echo "[check-pack-install] pi runtime tree pin verified: every @earendil-works pi package is 0.83.0"
2814
2880
 
2815
2881
  # Resolve the installed package.json and confirm pi.extensions
2816
2882
  # arrived intact. If pi.extensions is empty or missing, the
@@ -3757,7 +3823,7 @@ setup_all() {
3757
3823
  # native-harness meta-bridge, so a relocate/clone needs ONE command — not the
3758
3824
  # install + install-meta-bridge two-pronged split that froze the Claude
3759
3825
  # statusLine on the old path (the 2026-06-23 relocation gap). Detection-gated:
3760
- # a pi-only host with no native harness skips it cleanly (§10 "있으면 설정,
3826
+ # a pi-only host with no native harness skips it cleanly (rail “Shipped adapters” — "있으면 설정,
3761
3827
  # 없으면 담아준다"); meta-bridge-install.sh is itself idempotent.
3762
3828
  if command -v claude >/dev/null 2>&1; then
3763
3829
  section "meta-bridge install (native harness detected: Claude Code)"
@@ -3789,7 +3855,7 @@ setup_all() {
3789
3855
  wire_agy_hooks
3790
3856
 
3791
3857
  # Deterministic preflight lives in `pnpm check`; live substrate acceptance lives
3792
- # in `LIVE=1 ./run.sh release-gate <scratch>`. Setup is the install path, so it
3858
+ # in `LIVE=1 ./run.sh release-gate <scratch> --cut`. Setup is the install path, so it
3793
3859
  # verifies the installed MCP bridge boundary only and does NOT run the legacy
3794
3860
  # ACP/v1 session-messaging/sentinel gates.
3795
3861
  section "v2 install smoke: entwurf-bridge (direct MCP protocol)"
@@ -3805,7 +3871,7 @@ setup_all() {
3805
3871
  echo " ./run.sh doctor-agy-statusline"
3806
3872
  echo " ./run.sh doctor-agy-hooks"
3807
3873
  fi
3808
- echo "Run 'LIVE=1 ./run.sh release-gate <scratch>' for live substrate acceptance."
3874
+ echo "Run 'LIVE=1 ./run.sh release-gate <scratch> --cut' for live substrate acceptance (--cut refuses any MUST SKIP)."
3809
3875
  }
3810
3876
 
3811
3877
  # wire_agy_bridge — detection-gated, NON-FATAL agy (Antigravity) MCP bridge wiring, folded into
@@ -3989,10 +4055,18 @@ expose_dev_bin() {
3989
4055
  # - Final release authorization is GLG's, not this script's: a green
3990
4056
  # run is necessary, and the operator closes the decision.
3991
4057
  release_gate() {
4058
+ entwurf_require_step_outcome
3992
4059
  local -a positional=()
3993
4060
  local a
4061
+ # --cut is the EXECUTABLE half of "a CUT needs LIVE=1, SKIP=0" (P1). Without it
4062
+ # this stays the unattended diagnostic it has always been: SKIPs are reported
4063
+ # and the run still exits 0. With it, any MUST SKIP is red — including the
4064
+ # LIVE!=1 case, which needs no separate assertion because every LIVE-gated step
4065
+ # then skips on its own and the skip counter blocks.
4066
+ local cut_mode=0
3994
4067
  for a in "$@"; do
3995
4068
  case "$a" in
4069
+ --cut) cut_mode=1 ;;
3996
4070
  --allow-skip-gemini) ;; # accepted-but-ignored: gemini removed from the claude-only floor (back-compat for existing scripts)
3997
4071
  *) positional+=("$a") ;;
3998
4072
  esac
@@ -4043,27 +4117,36 @@ release_gate() {
4043
4117
  }
4044
4118
 
4045
4119
  # LIVE-gated MUST step: a release-blocking gate that needs a real backend turn
4046
- # (auth/model/credit). When LIVE!=1 it is an HONEST SKIP counted as SKIP, NOT
4047
- # PASSso an unattended `./run.sh release-gate` stays runnable without faking
4048
- # coverage. A release CUT therefore requires `LIVE=1 ./run.sh release-gate
4049
- # <scratch>` to land MUST PASS with SKIP=0; a green run that still shows SKIP is
4050
- # CI safety, never live acceptance. (Same rule the v2 substrate sentinels below
4051
- # spell out inline; this helper applies it to the ACP plugin acceptance floor.)
4120
+ # (auth/model/credit). It is ALWAYS INVOKED and the step itself reports whether
4121
+ # it could run that is the whole point of P1. The old shape asked `LIVE=1?`
4122
+ # here and skipped without calling, which meant the summary asserted a skip it
4123
+ # had never confirmed, while a step that WAS called could still exit 0 on a
4124
+ # missing prerequisite (Cortex without a connection) and be counted PASS.
4125
+ # Now one protocol decides: exit 0 = PASS, $ENTWURF_STEP_SKIP_EXIT = SKIP,
4126
+ # anything else = FAIL. A SKIP is never rounded up, and `--cut` refuses any.
4127
+ #
4128
+ # `LIVE` is NOT forced here — it rides the operator's environment into the
4129
+ # child, so `LIVE=1 ./run.sh release-gate` runs the step for real and a bare
4130
+ # invocation gets the step's own honest skip.
4052
4131
  run_live_step() {
4053
4132
  local name="$1"; shift
4054
4133
  section "release-gate step: $name"
4055
- if [ "${LIVE:-}" = "1" ]; then
4056
- if "$@"; then
4134
+ local rc=0
4135
+ "$@" || rc=$?
4136
+ case "$(entwurf_step_outcome "$rc")" in
4137
+ PASS)
4057
4138
  ok "$name: PASS"
4058
4139
  results+=("PASS $name"); pass=$((pass + 1))
4059
- else
4060
- fail "$name: FAIL"
4140
+ ;;
4141
+ SKIP)
4142
+ warn "$name: SKIP — the step declined a prerequisite it does not have (see its [entwurf:skip] line above). NOT a live acceptance."
4143
+ results+=("SKIP $name"); skip=$((skip + 1))
4144
+ ;;
4145
+ *)
4146
+ fail "$name: FAIL (exit $rc)"
4061
4147
  results+=("FAIL $name"); failc=$((failc + 1))
4062
- fi
4063
- else
4064
- warn "$name: LIVE!=1 — skipped (opt-in: needs auth/model — NOT a live acceptance run)"
4065
- results+=("SKIP $name (LIVE!=1)"); skip=$((skip + 1))
4066
- fi
4148
+ ;;
4149
+ esac
4067
4150
  }
4068
4151
 
4069
4152
  # BEHAVIOR lane (0.11.0, GLG+GPT+Opus): a SEPARATE advisory counter for
@@ -4087,19 +4170,32 @@ release_gate() {
4087
4170
  # deterministic/programmatic must-pass gates above (check-entwurf-v2-*,
4088
4171
  # check-bridge) — this lane is autonomous-tool-selection *behavior*, not
4089
4172
  # *function*. Residual bypass → 0.11.x usability lane.
4090
- local behavior_pass=0 behavior_failc=0
4173
+ # The lane speaks the same STEP OUTCOME protocol (P1) so a missing prerequisite
4174
+ # here is a BEHAVIOR-SKIP, not a fabricated BEHAVIOR-PASS or a misread
4175
+ # BEHAVIOR-FAIL. Advisory semantics are untouched: none of these counters ever
4176
+ # reaches `failc`, and `--cut` reads the MUST skip counter only.
4177
+ local behavior_pass=0 behavior_failc=0 behavior_skip=0
4091
4178
  local -a behavior_results=()
4092
4179
 
4093
4180
  run_behavior_step() {
4094
4181
  local name="$1"; shift
4095
4182
  section "release-gate BEHAVIOR step (advisory, non-blocking): $name"
4096
- if "$@"; then
4097
- ok "$name: BEHAVIOR-PASS"
4098
- behavior_results+=("BEHAVIOR-PASS $name"); behavior_pass=$((behavior_pass + 1))
4099
- else
4100
- warn "$name: BEHAVIOR-FAIL (advisory — model-in-loop signal; S7=Bash-bypass stays hard-fail here; NOT a cut blocker)"
4101
- behavior_results+=("BEHAVIOR-FAIL $name"); behavior_failc=$((behavior_failc + 1))
4102
- fi
4183
+ local rc=0
4184
+ "$@" || rc=$?
4185
+ case "$(entwurf_step_outcome "$rc")" in
4186
+ PASS)
4187
+ ok "$name: BEHAVIOR-PASS"
4188
+ behavior_results+=("BEHAVIOR-PASS $name"); behavior_pass=$((behavior_pass + 1))
4189
+ ;;
4190
+ SKIP)
4191
+ warn "$name: BEHAVIOR-SKIP — prerequisite absent (advisory lane; not a signal either way)"
4192
+ behavior_results+=("BEHAVIOR-SKIP $name"); behavior_skip=$((behavior_skip + 1))
4193
+ ;;
4194
+ *)
4195
+ warn "$name: BEHAVIOR-FAIL (advisory — model-in-loop signal; S7=Bash-bypass stays hard-fail here; NOT a cut blocker)"
4196
+ behavior_results+=("BEHAVIOR-FAIL $name"); behavior_failc=$((behavior_failc + 1))
4197
+ ;;
4198
+ esac
4103
4199
  }
4104
4200
 
4105
4201
  # 1. Static floor (deterministic; includes the two folded gates).
@@ -4137,41 +4233,20 @@ release_gate() {
4137
4233
  # production runEntwurfV2 deps + real pi control-socket RPC + real mailbox enqueue + v2 lock, not
4138
4234
  # per-backend model behavior). Placed right after check-bridge: the MCP/protocol substrate must be
4139
4235
  # green first so a matrix-live failure reads as "v2 transport/lock/enqueue", not bridge basics.
4140
- # Opt-in LIVE: it spawns a real `pi --entwurf-control` (needs auth/model), so LIVE!=1 is an HONEST
4141
- # SKIP (not a PASS) — an unattended release-gate stays runnable without faking coverage. Independent
4142
- # of --allow-skip-gemini (now a no-op back-compat flag on the claude-only floor; this gates substrate auth).
4143
- section "release-gate step: smoke-entwurf-v2-matrix-live"
4144
- if [ "${LIVE:-}" = "1" ]; then
4145
- if gate env LIVE=1 bash "$self" smoke-entwurf-v2-matrix-live; then
4146
- ok "smoke-entwurf-v2-matrix-live: PASS"
4147
- results+=("PASS smoke-entwurf-v2-matrix-live"); pass=$((pass + 1))
4148
- else
4149
- fail "smoke-entwurf-v2-matrix-live: FAIL"
4150
- results+=("FAIL smoke-entwurf-v2-matrix-live"); failc=$((failc + 1))
4151
- fi
4152
- else
4153
- warn "smoke-entwurf-v2-matrix-live: LIVE!=1 — skipped (v2 substrate sentinel, opt-in: needs auth/model)"
4154
- results+=("SKIP smoke-entwurf-v2-matrix-live (LIVE!=1)"); skip=$((skip + 1))
4155
- fi
4236
+ # Opt-in LIVE: it spawns a real `pi --entwurf-control` (needs auth/model), so a missing
4237
+ # prerequisite is an HONEST SKIP (not a PASS) — an unattended release-gate stays runnable without
4238
+ # faking coverage. Independent of --allow-skip-gemini (now a no-op back-compat flag on the
4239
+ # claude-only floor; this gates substrate auth). It used to carry its own inline copy of the
4240
+ # PASS/FAIL/SKIP branch; P1 folded it onto `run_live_step` so there is exactly ONE classifier —
4241
+ # a second copy is how a lane silently keeps counting a skip as a pass.
4242
+ run_live_step "smoke-entwurf-v2-matrix-live (D4-c: v2 dispatch substrate sentinel)" gate bash "$self" smoke-entwurf-v2-matrix-live
4156
4243
  # 0.11.0 (A) acceptance: the FULL spawn-bg resident lifecycle — a real `pi` resume child stands
4157
4244
  # its control socket up, does a model turn, lock released ×1. Placed right after matrix-live (the
4158
4245
  # transport sentinel): a spawn-resume failure then reads as "resume/resident lifecycle", not
4159
- # transport basics. Same opt-in LIVE rule LIVE!=1 is an HONEST SKIP (model-in-loop, needs
4160
- # auth/model). NOTE: a 0.11.0 tag REQUIRES `LIVE=1 ./run.sh release-gate` (or this step direct)
4161
- # to PASS the SKIP is CI safety, never acceptance.
4162
- section "release-gate step: smoke-entwurf-v2-spawn-resume-live"
4163
- if [ "${LIVE:-}" = "1" ]; then
4164
- if gate env LIVE=1 bash "$self" smoke-entwurf-v2-spawn-resume-live; then
4165
- ok "smoke-entwurf-v2-spawn-resume-live: PASS"
4166
- results+=("PASS smoke-entwurf-v2-spawn-resume-live"); pass=$((pass + 1))
4167
- else
4168
- fail "smoke-entwurf-v2-spawn-resume-live: FAIL"
4169
- results+=("FAIL smoke-entwurf-v2-spawn-resume-live"); failc=$((failc + 1))
4170
- fi
4171
- else
4172
- warn "smoke-entwurf-v2-spawn-resume-live: LIVE!=1 — skipped (0.11.0 A acceptance, opt-in: needs auth/model)"
4173
- results+=("SKIP smoke-entwurf-v2-spawn-resume-live (LIVE!=1)"); skip=$((skip + 1))
4174
- fi
4246
+ # transport basics. Same opt-in LIVE rule. NOTE: a tag REQUIRES `LIVE=1 ./run.sh release-gate
4247
+ # --cut` (or this step direct) to PASS — a SKIP is CI safety, never acceptance, and `--cut` is
4248
+ # what now enforces that instead of prose.
4249
+ run_live_step "smoke-entwurf-v2-spawn-resume-live (0.11.0 A: full spawn-bg resident lifecycle)" gate bash "$self" smoke-entwurf-v2-spawn-resume-live
4175
4250
 
4176
4251
  # 3b. ACP plugin acceptance floor (S0~S2f live). These prove the ACP plugin's
4177
4252
  # programmatic transport/provider/backend invariants on the v2 core — NOT
@@ -4186,18 +4261,35 @@ release_gate() {
4186
4261
  # → first-user augment delivery + empty-carrier billing-clean (핀1) →
4187
4262
  # ACP-target garden guard (deterministic half). Opt-in LIVE: LIVE!=1 is an
4188
4263
  # HONEST SKIP via run_live_step (see its note) — a CUT needs LIVE=1, SKIP=0.
4189
- run_live_step "smoke-acp-socket-citizen-live (S1: turn-free socket citizenship)" gate env LIVE=1 bash "$self" smoke-acp-socket-citizen-live
4190
- run_live_step "smoke-acp-raw-turn-live (S2a: pinned ACP pipe + local auth)" gate env LIVE=1 bash "$self" smoke-acp-raw-turn-live
4191
- run_live_step "smoke-acp-overlay-live (S2b: config overlay + hooks:{} + tool meta)" gate env LIVE=1 bash "$self" smoke-acp-overlay-live
4192
- run_live_step "smoke-acp-provider-live (S2c/S2f: real pi provider path + progress/L3)" gate env LIVE=1 bash "$self" smoke-acp-provider-live
4193
- run_live_step "smoke-acp-session-reuse-live (S2d: process-scoped reuse + recall)" gate env LIVE=1 bash "$self" smoke-acp-session-reuse-live
4194
- run_live_step "smoke-acp-carrier-augment-live (S2e-1: augment delivery + 핀1 billing)" gate env LIVE=1 bash "$self" smoke-acp-carrier-augment-live
4195
- run_live_step "smoke-acp-memory-containment-live (Gate D: no overlay memory leak)" gate env LIVE=1 bash "$self" smoke-acp-memory-containment-live
4196
- run_live_step "smoke-acp-rgg-live (S2e-2: ACP-target garden guard, deterministic half)" gate env LIVE=1 bash "$self" smoke-acp-rgg-live
4197
- run_live_step "smoke-acp-mcp-live (S2g: operator mcpServers reach the live ACP session)" gate env LIVE=1 bash "$self" smoke-acp-mcp-live
4198
- run_live_step "smoke-acp-skill-live (S2g: operator skillPlugins reach the live ACP session)" gate env LIVE=1 bash "$self" smoke-acp-skill-live
4199
- run_live_step "smoke-acp-bundled-mcp-live (S2g axis 3: bundled entwurf-bridge via 0.11.0 resident/RPC circuit)" gate env LIVE=1 bash "$self" smoke-acp-bundled-mcp-live
4200
- run_live_step "smoke-acp-v2-send-live (S2g axis 4: an ACP model SENDS via entwurf_v2, landing as itself)" gate env LIVE=1 bash "$self" smoke-acp-v2-send-live
4264
+ run_live_step "smoke-acp-socket-citizen-live (S1: turn-free socket citizenship)" gate bash "$self" smoke-acp-socket-citizen-live
4265
+ run_live_step "smoke-acp-raw-turn-live (S2a: pinned ACP pipe + local auth)" gate bash "$self" smoke-acp-raw-turn-live
4266
+ run_live_step "smoke-acp-overlay-live (S2b: config overlay + hooks:{} + tool meta)" gate bash "$self" smoke-acp-overlay-live
4267
+ run_live_step "smoke-acp-provider-live (S2c/S2f: real pi provider path + progress/L3)" gate bash "$self" smoke-acp-provider-live
4268
+ run_live_step "smoke-acp-session-reuse-live (S2d: process-scoped reuse + recall)" gate bash "$self" smoke-acp-session-reuse-live
4269
+ run_live_step "smoke-acp-carrier-augment-live (S2e-1: augment delivery + 핀1 billing)" gate bash "$self" smoke-acp-carrier-augment-live
4270
+ run_live_step "smoke-acp-memory-containment-live (Gate D: no overlay memory leak)" gate bash "$self" smoke-acp-memory-containment-live
4271
+ run_live_step "smoke-acp-rgg-live (S2e-2: ACP-target garden guard, deterministic half)" gate bash "$self" smoke-acp-rgg-live
4272
+ run_live_step "smoke-acp-mcp-live (S2g: operator mcpServers reach the live ACP session)" gate bash "$self" smoke-acp-mcp-live
4273
+ run_live_step "smoke-acp-skill-live (S2g: operator skillPlugins reach the live ACP session)" gate bash "$self" smoke-acp-skill-live
4274
+ run_live_step "smoke-acp-bundled-mcp-live (S2g axis 3: bundled entwurf-bridge via 0.11.0 resident/RPC circuit)" gate bash "$self" smoke-acp-bundled-mcp-live
4275
+ run_live_step "smoke-acp-v2-send-live (S2g axis 4: an ACP model SENDS via entwurf_v2, landing as itself)" gate bash "$self" smoke-acp-v2-send-live
4276
+
4277
+ # 3c. The axes the aggregate used to leave out with no stated reason (P2,
4278
+ # 2026-07-31). Three LIVE smokes existed and were simply never wired, so a
4279
+ # green cut said nothing about them. Under the P1 outcome protocol wiring
4280
+ # them costs nothing on a host that cannot run them — they SKIP honestly and
4281
+ # `--cut` names the missing prerequisite — while a host that CAN run them now
4282
+ # must. Cortex is deliberately NOT here: its rail needs an external Snowflake
4283
+ # connection the HOST owns, so wiring it would block every cut taken without
4284
+ # that account; it stays a required DIRECT call for a Cortex-rail cut instead
4285
+ # (VERIFY 「Cortex is an on-demand axis」). Same for `smoke-acp-long-turn-live`,
4286
+ # `smoke-agy-native-push-live` and `smoke-acp-ordering-probe-live`: >12min by
4287
+ # construction, no aggregate-owned agy conversation id, and an opt-in instrument
4288
+ # rather than acceptance. check-release-gate-outcomes pins that every LIVE smoke
4289
+ # is either wired here or excluded by a reason the docs still carry.
4290
+ run_live_step "smoke-entwurf-v2-spawn-live (5c-3c: spawn-bg OS substrate on real children/sockets)" gate bash "$self" smoke-entwurf-v2-spawn-live
4291
+ run_live_step "smoke-claude-native-resume-live (native Claude Code resume + meta-bridge neutrality)" gate bash "$self" smoke-claude-native-resume-live
4292
+ run_live_step "smoke-entwurf-chain-live (P3: Claude Code -> pi GPT -> pi ACP Sonnet -> mailbox, identity + receipt)" gate bash "$self" smoke-entwurf-chain-live
4201
4293
 
4202
4294
  # 4. BEHAVIOR lane (advisory, non-blocking). Model-in-loop gates that probe
4203
4295
  # whether the model AUTONOMOUSLY drives the MCP entwurf surface. These never
@@ -4227,21 +4319,44 @@ release_gate() {
4227
4319
  if [ "${#behavior_results[@]}" -gt 0 ]; then
4228
4320
  printf ' %s\n' "${behavior_results[@]}"
4229
4321
  fi
4230
- echo " BEHAVIOR: PASS=$behavior_pass FAIL=$behavior_failc"
4322
+ echo " BEHAVIOR: PASS=$behavior_pass FAIL=$behavior_failc SKIP=$behavior_skip"
4231
4323
  echo " (per-step artifact paths are printed in each step's output above)"
4232
4324
  if [ "$behavior_failc" -gt 0 ]; then
4233
4325
  echo ""
4234
4326
  warn "BEHAVIOR FAIL present ($behavior_failc) — advisory model-in-loop signal (e.g. S7 Bash-bypass / entwurf_self not autonomously called). Tracked, NOT a cut blocker; see the 0.11.x usability lane."
4235
4327
  fi
4236
- # Exit authority = MUST `failc` ONLY. A BEHAVIOR fail never blocks the cut.
4237
- if [ "$failc" -gt 0 ]; then
4328
+ echo ""
4329
+ if [ "$cut_mode" = "1" ]; then
4330
+ echo " mode: --cut (a MUST SKIP is a BLOCKER — this run is being read as release acceptance)"
4331
+ else
4332
+ echo " mode: diagnostic (MUST SKIPs are reported, not blocking — re-run with --cut to read this as acceptance)"
4333
+ fi
4334
+ # The verdict as one greppable token, so release evidence can distinguish a
4335
+ # step that RAN AND BROKE from one that NEVER RAN without reading prose. The
4336
+ # counters above stay literal — a policy block is `FAIL=0 SKIP=n` plus
4337
+ # `BLOCKED (MUST SKIP)`, never a synthesized failure.
4338
+ echo " $(entwurf_release_verdict "$failc" "$skip" "$cut_mode")"
4339
+
4340
+ # Exit authority — MUST tier only, BEHAVIOR never blocks. The releasable
4341
+ # decision lives in scripts/lib/step-outcome.sh so it is one testable function
4342
+ # rather than a branch re-derived here (and re-derived wrong: the pre-P1 branch
4343
+ # read `failc` alone, so a run with 14 SKIPs still printed "all green").
4344
+ if ! entwurf_release_releasable "$failc" "$skip" "$cut_mode"; then
4238
4345
  echo ""
4239
- fail "release-gate MUST NOT green — $failc release-blocking step(s) failed. Current release is NOT releasable."
4346
+ if [ "$failc" -gt 0 ]; then
4347
+ fail "release-gate MUST NOT green — $failc release-blocking step(s) failed. Current release is NOT releasable."
4348
+ else
4349
+ fail "release-gate --cut REFUSED — $skip release-blocking step(s) SKIPPED, so this run does not prove they were called."
4350
+ echo " A skipped step is not acceptance. Supply each missing prerequisite (usually LIVE=1 plus the"
4351
+ echo " per-step env the [entwurf:skip] lines above name) and re-run, or drop --cut to keep this a diagnostic."
4352
+ fi
4240
4353
  echo " A green MUST gate is necessary but not sufficient; GLG closes the call."
4241
4354
  return 1
4242
4355
  fi
4243
- if [ "$behavior_failc" -gt 0 ]; then
4244
- ok "release-gate MUST PASS (all release-blocking steps green); BEHAVIOR FAIL present (advisory). Necessary condition met GLG authorizes the cut."
4356
+ if [ "$skip" -gt 0 ]; then
4357
+ warn "release-gate MUST has no failures, but $skip step(s) SKIPPED this is a DIAGNOSTIC run, not live acceptance. A cut needs \`LIVE=1 ./run.sh release-gate <scratch> --cut\` with SKIP=0."
4358
+ elif [ "$behavior_failc" -gt 0 ]; then
4359
+ ok "release-gate MUST PASS (all release-blocking steps ran and are green); BEHAVIOR FAIL present (advisory). Necessary condition met — GLG authorizes the cut."
4245
4360
  else
4246
4361
  ok "release-gate MUST PASS + BEHAVIOR PASS — all green. Necessary condition met — GLG authorizes the cut."
4247
4362
  fi
@@ -4393,6 +4508,9 @@ case "$cmd" in
4393
4508
  smoke-acp-provider-live)
4394
4509
  smoke_acp_provider_live
4395
4510
  ;;
4511
+ smoke-acp-long-turn-live)
4512
+ smoke_acp_long_turn_live
4513
+ ;;
4396
4514
  smoke-acp-session-reuse-live)
4397
4515
  smoke_acp_session_reuse_live
4398
4516
  ;;
@@ -4405,6 +4523,15 @@ case "$cmd" in
4405
4523
  smoke-acp-bundled-mcp-live)
4406
4524
  smoke_acp_bundled_mcp_live
4407
4525
  ;;
4526
+ smoke-entwurf-chain-live)
4527
+ # P3 — the cross-harness delivery CHAIN on real authenticated rails:
4528
+ # native Claude Code -> pi GPT -> pi ACP Claude Sonnet -> mailbox terminus,
4529
+ # with sender identity at every hop and a read receipt at the end. Needs
4530
+ # `claude` on PATH plus pi credentials for both pi backends; each missing
4531
+ # prerequisite is an honest protocol SKIP, never a pass.
4532
+ # LIVE=1 ./run.sh smoke-entwurf-chain-live
4533
+ run_ts scripts/smoke-entwurf-chain-live.ts
4534
+ ;;
4408
4535
  smoke-acp-v2-send-live)
4409
4536
  smoke_acp_v2_send_live
4410
4537
  ;;
@@ -4834,6 +4961,12 @@ case "$cmd" in
4834
4961
  check-acp-event-mapper)
4835
4962
  check_acp_event_mapper
4836
4963
  ;;
4964
+ check-acp-stop-reason)
4965
+ check_acp_stop_reason
4966
+ ;;
4967
+ check-acp-prompt-lifecycle)
4968
+ check_acp_prompt_lifecycle
4969
+ ;;
4837
4970
  check-acp-prompt-builder)
4838
4971
  check_acp_prompt_builder
4839
4972
  ;;
@@ -4849,6 +4982,9 @@ case "$cmd" in
4849
4982
  check-acp-session-reuse)
4850
4983
  check_acp_session_reuse
4851
4984
  ;;
4985
+ check-release-gate-outcomes)
4986
+ run_ts scripts/check-release-gate-outcomes.ts
4987
+ ;;
4852
4988
  check-acp-carrier-augment)
4853
4989
  check_acp_carrier_augment
4854
4990
  ;;