@junghanacs/entwurf 0.13.0 → 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.
- package/AGENTS.md +52 -17
- package/BASELINE.md +60 -206
- package/CHANGELOG.md +64 -0
- package/CONTRIBUTING.md +14 -10
- package/DELIVERY.md +118 -261
- package/README.md +91 -431
- package/VERIFY.md +74 -95
- package/demo/README.md +1 -1
- package/demo/demo-baseline.sh +1 -3
- package/demo/demo.sh +2 -5
- package/docs/acp-backend-rail.md +236 -0
- package/docs/external-mcp-host.md +131 -0
- package/docs/fresh-cut-policy.md +99 -0
- package/docs/setup-clean-host.md +124 -328
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +160 -67
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/acp-client.js +12 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/backend-adapter.js +11 -10
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/config.js +1 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/engraving.js +42 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/models.js +4 -4
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/overlay.js +11 -9
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-control-rpc.js +7 -5
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-core.js +13 -14
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-resume-args.js +45 -40
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-contract.js +117 -95
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-decider.js +23 -57
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-lock.js +16 -7
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-production.js +5 -53
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-release.js +21 -36
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-runner.js +3 -15
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-send-fallback.js +12 -11
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-send.js +2 -7
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-surface.js +30 -67
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-visible-resume.js +256 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js +91 -5
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +258 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-launch.js +202 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-placement.js +289 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-resume-call.js +170 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/resume-launch-identity.js +136 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/session-id.js +8 -5
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/socket-discovery.js +3 -3
- package/mcp/entwurf-bridge/dist/scripts/meta-facts.js +51 -0
- package/mcp/entwurf-bridge/dist/scripts/new-session-id.js +9 -4
- package/mcp/entwurf-bridge/src/index.ts +176 -67
- package/mcp/entwurf-bridge/start.sh +2 -2
- package/mcp/entwurf-bridge/test.sh +23 -9
- package/mcp/entwurf-bridge/tsconfig.build.json +11 -2
- package/package.json +25 -11
- package/pi-extensions/entwurf-control.ts +220 -71
- package/pi-extensions/lib/acp/acp-client.ts +22 -0
- package/pi-extensions/lib/acp/augment.ts +42 -3
- package/pi-extensions/lib/acp/backend-adapter.ts +12 -11
- package/pi-extensions/lib/acp/backend.ts +366 -38
- package/pi-extensions/lib/acp/config.ts +1 -1
- package/pi-extensions/lib/acp/engraving.ts +43 -1
- package/pi-extensions/lib/acp/event-mapper.ts +16 -13
- package/pi-extensions/lib/acp/models.ts +4 -4
- package/pi-extensions/lib/acp/overlay.ts +11 -9
- package/pi-extensions/lib/entwurf-control-rpc.ts +7 -5
- package/pi-extensions/lib/entwurf-core.ts +15 -15
- package/pi-extensions/lib/entwurf-resume-args.ts +41 -52
- package/pi-extensions/lib/entwurf-v2-contract-schema.ts +1 -1
- package/pi-extensions/lib/entwurf-v2-contract.ts +120 -99
- package/pi-extensions/lib/entwurf-v2-decider.ts +30 -91
- package/pi-extensions/lib/entwurf-v2-lock.ts +16 -7
- package/pi-extensions/lib/entwurf-v2-production.ts +4 -78
- package/pi-extensions/lib/entwurf-v2-release.ts +25 -49
- package/pi-extensions/lib/entwurf-v2-runner.ts +6 -21
- package/pi-extensions/lib/entwurf-v2-send-fallback.ts +12 -11
- package/pi-extensions/lib/entwurf-v2-send.ts +2 -7
- package/pi-extensions/lib/entwurf-v2-surface.ts +36 -76
- package/pi-extensions/lib/entwurf-v2-visible-resume.ts +370 -0
- package/pi-extensions/lib/meta-session.ts +93 -5
- package/pi-extensions/lib/mux-fresh-call.ts +328 -0
- package/pi-extensions/lib/mux-launch.ts +267 -0
- package/pi-extensions/lib/mux-placement.ts +387 -0
- package/pi-extensions/lib/mux-resume-call.ts +221 -0
- package/pi-extensions/lib/resume-launch-identity.ts +162 -0
- package/pi-extensions/lib/session-id.js +8 -5
- package/pi-extensions/lib/socket-discovery.ts +3 -3
- package/prompts/engraving.md +13 -5
- package/run.sh +688 -282
- package/scripts/agy-bridge-config.py +5 -1
- package/scripts/check-acp-backend-preflight.ts +1 -1
- package/scripts/check-acp-carrier-augment.ts +246 -10
- package/scripts/check-acp-cortex.ts +5 -5
- package/scripts/check-acp-overlay.ts +13 -3
- package/scripts/check-acp-prompt-lifecycle.ts +565 -0
- package/scripts/check-acp-stop-reason.ts +342 -0
- package/scripts/check-acp-stream-hooks.ts +504 -0
- package/scripts/check-elapsed.sh +25 -0
- package/scripts/check-entwurf-bridge-boot.ts +51 -4
- package/scripts/check-entwurf-bridge-pi-free.ts +6 -5
- package/scripts/check-entwurf-control-rpc.ts +4 -3
- package/scripts/check-entwurf-resume-args.ts +72 -70
- package/scripts/check-entwurf-session-identity.ts +14 -10
- package/scripts/check-entwurf-v2-contract.ts +34 -59
- package/scripts/check-entwurf-v2-decider.ts +17 -177
- package/scripts/check-entwurf-v2-lock.ts +5 -2
- package/scripts/check-entwurf-v2-matrix.ts +3 -53
- package/scripts/check-entwurf-v2-production.ts +2 -91
- package/scripts/check-entwurf-v2-release.ts +10 -105
- package/scripts/check-entwurf-v2-runner.ts +4 -85
- package/scripts/check-entwurf-v2-send-fallback.ts +5 -6
- package/scripts/check-entwurf-v2-send.ts +0 -28
- package/scripts/check-entwurf-v2-surface.ts +198 -128
- package/scripts/check-entwurf-v2-visible-resume.ts +445 -0
- package/scripts/check-fresh-cut-gate.sh +1 -1
- package/scripts/check-gate-qualification.ts +101 -7
- package/scripts/check-install-container.sh +10 -2
- package/scripts/check-install-surface.ts +1 -1
- package/scripts/check-keyset-overlap.py +1 -1
- package/scripts/check-meta-facts.ts +249 -0
- package/scripts/check-meta-identity-consumers.ts +1 -1
- package/scripts/check-meta-session.ts +169 -0
- package/scripts/check-mux-launch-tmux.ts +316 -0
- package/scripts/check-mux-launch.ts +288 -0
- package/scripts/check-mux-launcher-fence.ts +264 -0
- package/scripts/check-mux-parent-artifact.ts +195 -0
- package/scripts/check-mux-placement-tmux.ts +322 -0
- package/scripts/check-mux-placement.ts +323 -0
- package/scripts/check-mux-resume-call.ts +282 -0
- package/scripts/check-probe-cli-shim.ts +25 -22
- package/scripts/check-probe-ordering.ts +121 -81
- package/scripts/check-release-gate-outcomes.ts +452 -0
- package/scripts/check-resume-launch-identity.ts +244 -0
- package/scripts/check-socket-discovery.ts +1 -1
- package/scripts/fixtures/mux-parent-transcript.scrubbed.jsonl +3 -0
- package/scripts/inventory-verification-surface.ts +349 -0
- package/scripts/lib/claude-launcher-fence.ts +322 -0
- package/scripts/lib/live-skip.ts +33 -0
- package/scripts/lib/mutation-qualify.ts +109 -3
- package/scripts/lib/probe-acp-turn.ts +33 -12
- package/scripts/lib/step-outcome.sh +88 -0
- package/scripts/meta-bridge-doctor.sh +6 -8
- package/scripts/meta-facts.ts +60 -0
- package/scripts/mutants/acp-augment.json +106 -0
- package/scripts/mutants/acp-cortex.json +2 -2
- package/scripts/mutants/acp-overlay.json +17 -0
- package/scripts/mutants/acp-prompt-lifecycle.json +100 -0
- package/scripts/mutants/acp-stop-reason.json +80 -0
- package/scripts/mutants/acp-stream-hooks.json +158 -0
- package/scripts/mutants/bridge-boot-resume.json +45 -0
- package/scripts/mutants/meta-facts.json +50 -0
- package/scripts/mutants/meta-identity.json +36 -0
- package/scripts/mutants/mux-boundary.json +196 -0
- package/scripts/mutants/mux-fresh-call.json +185 -0
- package/scripts/mutants/mux-launcher-fence.json +123 -0
- package/scripts/mutants/mux-parent-artifact.json +39 -0
- package/scripts/mutants/mux-resume-call.json +148 -0
- package/scripts/mutants/probe-ordering.json +6 -1021
- package/scripts/mutants/release-gate.json +140 -0
- package/scripts/mutants/resume-args.json +76 -0
- package/scripts/mutants/resume-launch-identity.json +96 -0
- package/scripts/mutants/v2-surface.json +74 -23
- package/scripts/mutants/v2-visible-resume.json +215 -0
- package/scripts/new-session-id.ts +9 -4
- package/scripts/smoke-acp-bundled-mcp-live.ts +4 -3
- package/scripts/smoke-acp-carrier-augment-live.ts +2 -2
- package/scripts/smoke-acp-cortex-live.ts +8 -14
- package/scripts/smoke-acp-long-turn-live.ts +185 -0
- package/scripts/smoke-acp-mcp-live.ts +2 -2
- package/scripts/smoke-acp-memory-containment-live.ts +2 -2
- package/scripts/smoke-acp-ordering-probe-live.ts +2 -2
- package/scripts/smoke-acp-overlay-live.ts +2 -2
- package/scripts/smoke-acp-provider-live.ts +2 -2
- package/scripts/smoke-acp-raw-turn-live.ts +3 -3
- package/scripts/smoke-acp-session-reuse-live.ts +2 -2
- package/scripts/smoke-acp-skill-live.ts +2 -2
- package/scripts/smoke-acp-socket-citizen-live.ts +4 -3
- package/scripts/smoke-acp-v2-send-live.ts +4 -3
- package/scripts/smoke-agy-native-push-live.ts +10 -20
- package/scripts/smoke-claude-native-resume-live.sh +13 -3
- package/scripts/smoke-entwurf-chain-live.ts +352 -0
- package/scripts/smoke-entwurf-v2-matrix-live.ts +3 -3
- package/scripts/smoke-meta-honesty.sh +1 -1
- package/scripts/smoke-meta-install-state.sh +4 -0
- package/scripts/smoke-mux-fresh-call-live.ts +365 -0
- package/scripts/smoke-mux-lifecycle-live.ts +1136 -0
- package/scripts/smoke-pi-attach.ts +1 -1
- package/scripts/smoke-user-scope-citizen.sh +1 -1
- package/scripts/tsconfig.json +1 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-preflight.js +0 -160
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-spawn-production.js +0 -273
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-spawn.js +0 -216
- package/pi-extensions/lib/entwurf-v2-spawn-production.ts +0 -373
- package/pi-extensions/lib/entwurf-v2-spawn.ts +0 -323
- package/scripts/check-acp-sdk-surface.ts +0 -236
- package/scripts/check-entwurf-v2-spawn-production.ts +0 -551
- package/scripts/check-entwurf-v2-spawn.ts +0 -399
- package/scripts/smoke-entwurf-v2-spawn-live.ts +0 -188
- package/scripts/smoke-entwurf-v2-spawn-resume-live.ts +0 -469
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# shellcheck shell=bash
|
|
2
|
+
# Release-gate STEP OUTCOME protocol — the one place PASS / SKIP / FAIL is decided.
|
|
3
|
+
#
|
|
4
|
+
# SOURCE-ONLY. It carries the executable bit because check-pack requires every
|
|
5
|
+
# shipped `.sh` to have one (a blanket packaging invariant worth more than an
|
|
6
|
+
# exception carved for this file), not because anyone should run it.
|
|
7
|
+
#
|
|
8
|
+
# Sourced by run.sh's `release_gate`; sourced directly by
|
|
9
|
+
# scripts/check-release-gate-outcomes.ts so the truth table is exercised as a
|
|
10
|
+
# unit instead of being re-derived by eye from the aggregate's output.
|
|
11
|
+
#
|
|
12
|
+
# WHY A PROTOCOL AND NOT A PER-BACKEND BRANCH (P1, 2026-07-31). The aggregate's
|
|
13
|
+
# prose said a cut needs `LIVE=1` and `SKIP=0`, but its exit authority read only
|
|
14
|
+
# the FAIL counter, so `./run.sh release-gate` returned 0 with 14 SKIPs and the
|
|
15
|
+
# summary still printed "all green". Worse, a step that WAS invoked could decide
|
|
16
|
+
# it lacked a prerequisite and exit 0 — Cortex with no
|
|
17
|
+
# `ENTWURF_ACP_CORTEX_CONNECTION` is the measured case — and the aggregate
|
|
18
|
+
# counted that as PASS. Both holes have the same shape: a skip that cannot be
|
|
19
|
+
# told from an acceptance. So the fix is one outcome protocol every step speaks,
|
|
20
|
+
# never a special case per backend or per prerequisite.
|
|
21
|
+
#
|
|
22
|
+
# EXIT CONTRACT (house style: the verdict IS the exit code)
|
|
23
|
+
# 0 PASS — the step ran and its assertions held.
|
|
24
|
+
# ENTWURF_STEP_SKIP_EXIT SKIP — the step declined a prerequisite it does
|
|
25
|
+
# not have. NOT acceptance, and never rounded up.
|
|
26
|
+
# anything else FAIL — including a crash or a signal death.
|
|
27
|
+
#
|
|
28
|
+
# WHY 97. The repo already spends 0..4 on per-tool exit contracts that mean
|
|
29
|
+
# different things per tool (meta-bridge-fresh-cut's 3 = "cut transition
|
|
30
|
+
# incomplete", meta-bridge-store-doctor's 3 = "unreadable store", dev-bin's 3 =
|
|
31
|
+
# "refused a foreign link"), and 126+ belongs to the shell. A LIVE smoke shells
|
|
32
|
+
# out to some of those tools, so reusing a low number would let a dependency's
|
|
33
|
+
# unrelated verdict surface as a false SKIP — the exact confusion this protocol
|
|
34
|
+
# exists to remove. 97 is reserved for THIS cross-tool protocol and nothing else.
|
|
35
|
+
ENTWURF_STEP_SKIP_EXIT=97
|
|
36
|
+
|
|
37
|
+
# Classify one step's exit code. Prints PASS | SKIP | FAIL on stdout.
|
|
38
|
+
entwurf_step_outcome() {
|
|
39
|
+
case "${1:-}" in
|
|
40
|
+
0) printf 'PASS\n' ;;
|
|
41
|
+
"$ENTWURF_STEP_SKIP_EXIT") printf 'SKIP\n' ;;
|
|
42
|
+
*) printf 'FAIL\n' ;;
|
|
43
|
+
esac
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# The release authority. Returns 0 when the run may be read as a cut floor.
|
|
47
|
+
#
|
|
48
|
+
# $1 MUST fail count $2 MUST skip count $3 cut mode (1 = a real cut)
|
|
49
|
+
#
|
|
50
|
+
# A FAIL always blocks. A SKIP blocks ONLY in cut mode: an ordinary diagnostic
|
|
51
|
+
# `./run.sh release-gate <dir>` must stay runnable unattended and still exit 0
|
|
52
|
+
# while honestly reporting what it did not call. `--cut` is the executable half
|
|
53
|
+
# of "a CUT needs LIVE=1, SKIP=0" — and it needs no separate LIVE assertion,
|
|
54
|
+
# because with LIVE unset every LIVE-gated step skips and the skip count blocks.
|
|
55
|
+
entwurf_release_releasable() {
|
|
56
|
+
local failc="${1:-0}" skipc="${2:-0}" cut="${3:-0}"
|
|
57
|
+
[ "$failc" -gt 0 ] && return 1
|
|
58
|
+
if [ "$cut" = "1" ] && [ "$skipc" -gt 0 ]; then
|
|
59
|
+
return 1
|
|
60
|
+
fi
|
|
61
|
+
return 0
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
# The same decision as ONE greppable token, because "why is this red" must be
|
|
65
|
+
# machine-readable and not parsed out of prose.
|
|
66
|
+
#
|
|
67
|
+
# $1 MUST fail count $2 MUST skip count $3 cut mode (1 = a real cut)
|
|
68
|
+
#
|
|
69
|
+
# A step that RAN AND BROKE and a step that NEVER RAN are different facts, and a
|
|
70
|
+
# release record that blurs them is worthless: the first is a defect to fix, the
|
|
71
|
+
# second is a prerequisite to supply. So the counters are never fudged either —
|
|
72
|
+
# a policy block keeps `FAIL=0 SKIP=n` and says `BLOCKED (MUST SKIP)`. Reporting
|
|
73
|
+
# a synthetic `FAIL=1` for the policy block would destroy exactly the
|
|
74
|
+
# distinction this protocol exists to create.
|
|
75
|
+
entwurf_release_verdict() {
|
|
76
|
+
local failc="${1:-0}" skipc="${2:-0}" cut="${3:-0}"
|
|
77
|
+
if [ "$failc" -gt 0 ]; then
|
|
78
|
+
printf 'cut: BLOCKED (MUST FAIL)\n'
|
|
79
|
+
elif [ "$cut" = "1" ] && [ "$skipc" -gt 0 ]; then
|
|
80
|
+
printf 'cut: BLOCKED (MUST SKIP)\n'
|
|
81
|
+
elif [ "$cut" = "1" ]; then
|
|
82
|
+
printf 'cut: OK\n'
|
|
83
|
+
elif [ "$skipc" -gt 0 ]; then
|
|
84
|
+
printf 'cut: n/a (diagnostic, %s SKIP)\n' "$skipc"
|
|
85
|
+
else
|
|
86
|
+
printf 'cut: n/a (diagnostic)\n'
|
|
87
|
+
fi
|
|
88
|
+
}
|
|
@@ -1207,14 +1207,12 @@ case "$REPO" in
|
|
|
1207
1207
|
fi
|
|
1208
1208
|
;;
|
|
1209
1209
|
esac
|
|
1210
|
-
#
|
|
1211
|
-
#
|
|
1212
|
-
#
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
ok "ENTWURF_PREFIX_ROOTS set → operator prefix-approve roots: $ENTWURF_PREFIX_ROOTS"
|
|
1217
|
-
fi
|
|
1210
|
+
# NOT REPORTED: ENTWURF_PREFIX_ROOTS. It was the operator-policy SSOT that both v2 surfaces
|
|
1211
|
+
# read to feed the dispatch-path trust preflight, and that preflight guarded the resume verdict
|
|
1212
|
+
# — so it left with `owned-outcome` in the visible-first cut. Nothing on the dispatch path reads
|
|
1213
|
+
# the variable now, and a doctor line saying "set → operator prefix-approve roots" would report a
|
|
1214
|
+
# knob that does nothing. Reporting an inert setting as live policy is the failure this doctor
|
|
1215
|
+
# exists to prevent, so the row is gone rather than softened.
|
|
1218
1216
|
|
|
1219
1217
|
echo
|
|
1220
1218
|
if [ "$fail" -eq 0 ]; then echo "meta-bridge doctor: PASS"; else echo "meta-bridge doctor: FAIL (see above)"; exit 1; fi
|
|
@@ -0,0 +1,60 @@
|
|
|
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
|
+
|
|
36
|
+
import { defaultMetaSessionsDir, listAllMetaIdentitiesDir } from "../pi-extensions/lib/meta-session.ts";
|
|
37
|
+
|
|
38
|
+
const arg = process.argv[2];
|
|
39
|
+
// A dash argv is a flag this command does not have, not a store directory — treating
|
|
40
|
+
// it as a path would answer `--help` with "empty store, exit 0", a silent wrong fact.
|
|
41
|
+
if (process.argv.length > 3 || (arg !== undefined && arg.startsWith("-"))) {
|
|
42
|
+
console.error("usage: entwurf meta-facts [meta-sessions-dir]");
|
|
43
|
+
process.exit(2);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let facts: ReturnType<typeof listAllMetaIdentitiesDir>;
|
|
47
|
+
try {
|
|
48
|
+
facts = listAllMetaIdentitiesDir(arg ?? defaultMetaSessionsDir());
|
|
49
|
+
} catch (err) {
|
|
50
|
+
console.error(`FAIL: ${err instanceof Error ? err.message : String(err)}`);
|
|
51
|
+
process.exit(3); // see the EXIT CONTRACT above: 3 = unreadable store, never an empty one
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
process.stdout.write(
|
|
55
|
+
`${JSON.stringify(
|
|
56
|
+
{ schemaVersion: 1, storeDir: facts.dir, citizens: facts.identities, defects: facts.errors },
|
|
57
|
+
null,
|
|
58
|
+
2,
|
|
59
|
+
)}\n`,
|
|
60
|
+
);
|
|
@@ -25,6 +25,112 @@
|
|
|
25
25
|
"timeoutSeconds": 60,
|
|
26
26
|
"signature": "[QK:AUGMENT-TRUNC-MARKER]",
|
|
27
27
|
"signatureSource": "scripts/check-acp-carrier-augment.ts"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"claim": "CARRIER-PROVENANCE-STATED",
|
|
31
|
+
"title": "the augment stops saying what it is — the model is back to reading a first-user prepend as its system prompt (the measured 2026-07-30 misattribution)",
|
|
32
|
+
"subject": "pi-extensions/lib/acp/augment.ts",
|
|
33
|
+
"find": [
|
|
34
|
+
"\t\"This block is prepended to the FIRST USER MESSAGE of this session. It is not your system prompt.\";"
|
|
35
|
+
],
|
|
36
|
+
"replace": ["\t\"\";"],
|
|
37
|
+
"gate": ["bash", "run.sh", "check-acp-carrier-augment"],
|
|
38
|
+
"timeoutSeconds": 60,
|
|
39
|
+
"signature": "[QK:CARRIER-PROVENANCE-STATED]",
|
|
40
|
+
"signatureSource": "scripts/check-acp-carrier-augment.ts"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"claim": "CARRIER-FRAME-NAMES-CLAUDE-CARRIER",
|
|
44
|
+
"title": "the claude frame stops naming its real engraving-only carrier — the model is told what its system prompt is NOT, but never what it IS",
|
|
45
|
+
"subject": "pi-extensions/lib/acp/augment.ts",
|
|
46
|
+
"find": [
|
|
47
|
+
"\t\t\t\t\t`The ${params.backend} rail does have a system-prompt carrier (\\`_meta.systemPrompt\\`), and entwurf keeps it deliberately tiny: it carries the operator engraving only — never this narrative, never AGENTS.md.`,"
|
|
48
|
+
],
|
|
49
|
+
"replace": ["\t\t\t\t\t`The ${params.backend} rail carries no system-prompt carrier at all.`,"],
|
|
50
|
+
"gate": ["bash", "run.sh", "check-acp-carrier-augment"],
|
|
51
|
+
"timeoutSeconds": 60,
|
|
52
|
+
"signature": "[QK:CARRIER-FRAME-NAMES-CLAUDE-CARRIER]",
|
|
53
|
+
"signatureSource": "scripts/check-acp-carrier-augment.ts"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"claim": "CARRIER-FRAME-DENIES-CORTEX-CARRIER",
|
|
57
|
+
"title": "the carrier-less rail is told it has a tiny system-prompt carrier — a flat lie, since cortex's buildSessionMeta sends no _meta at all",
|
|
58
|
+
"subject": "pi-extensions/lib/acp/augment.ts",
|
|
59
|
+
"find": [
|
|
60
|
+
"\t\t\t\t\t`The ${params.backend} rail carries no system-prompt carrier at all — entwurf sends no \\`_meta.systemPrompt\\`, so everything entwurf tells you, including any operator engraving above, arrives here as user-message text.`,"
|
|
61
|
+
],
|
|
62
|
+
"replace": [
|
|
63
|
+
"\t\t\t\t\t`The ${params.backend} rail does have a system-prompt carrier (\\`_meta.systemPrompt\\`), and entwurf keeps it deliberately tiny: it carries the operator engraving only.`,"
|
|
64
|
+
],
|
|
65
|
+
"gate": ["bash", "run.sh", "check-acp-carrier-augment"],
|
|
66
|
+
"timeoutSeconds": 60,
|
|
67
|
+
"signature": "[QK:CARRIER-FRAME-DENIES-CORTEX-CARRIER]",
|
|
68
|
+
"signatureSource": "scripts/check-acp-carrier-augment.ts"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"claim": "CARRIER-RAIL-DIFF-IS-SOURCE-PINNED",
|
|
72
|
+
"title": "cortex grows a session carrier while the augment still tells it there is none — the frame becomes a lie and no prose check would notice",
|
|
73
|
+
"subject": "pi-extensions/lib/acp/backend-adapter.ts",
|
|
74
|
+
"find": ["\tbuildSessionMeta() {\n\t\treturn undefined;\n\t},"],
|
|
75
|
+
"replace": ["\tbuildSessionMeta() {\n\t\treturn { systemPrompt: \"x\" };\n\t},"],
|
|
76
|
+
"gate": ["bash", "run.sh", "check-acp-carrier-augment"],
|
|
77
|
+
"timeoutSeconds": 60,
|
|
78
|
+
"signature": "[QK:CARRIER-RAIL-DIFF-IS-SOURCE-PINNED]",
|
|
79
|
+
"signatureSource": "scripts/check-acp-carrier-augment.ts"
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"claim": "CARRIER-LEADS-ITS-OWN-BLOCK",
|
|
83
|
+
"title": "the carrier loses its leading boundary — the Claude SDK's fixed sentence swallows the operator engraving again (the measured 2026-07-31 A-join)",
|
|
84
|
+
"subject": "pi-extensions/lib/acp/engraving.ts",
|
|
85
|
+
"find": ["export const CARRIER_LEAD_SEPARATOR = \"\\n\\n\";"],
|
|
86
|
+
"replace": ["export const CARRIER_LEAD_SEPARATOR = \"\";"],
|
|
87
|
+
"gate": ["bash", "run.sh", "check-acp-carrier-augment"],
|
|
88
|
+
"timeoutSeconds": 60,
|
|
89
|
+
"signature": "[QK:CARRIER-LEADS-ITS-OWN-BLOCK]",
|
|
90
|
+
"signatureSource": "scripts/check-acp-carrier-augment.ts"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"claim": "CARRIER-OPT-OUT-SURVIVES-SEPARATOR",
|
|
94
|
+
"title": "the boundary is attached before the emptiness test — an emptied engraving file stops being the opt-out and becomes a whitespace carrier that still replaces the claude_code preset",
|
|
95
|
+
"subject": "pi-extensions/lib/acp/engraving.ts",
|
|
96
|
+
"find": [
|
|
97
|
+
"\tconst body = interpolate(source, params).trim();",
|
|
98
|
+
"\tif (body.length === 0) return \"\";",
|
|
99
|
+
"\treturn `${CARRIER_LEAD_SEPARATOR}${body}`;"
|
|
100
|
+
],
|
|
101
|
+
"replace": [
|
|
102
|
+
"\tconst body = `${CARRIER_LEAD_SEPARATOR}${interpolate(source, params).trim()}`;",
|
|
103
|
+
"\tif (body.length === 0) return \"\";",
|
|
104
|
+
"\treturn body;"
|
|
105
|
+
],
|
|
106
|
+
"gate": ["bash", "run.sh", "check-acp-carrier-augment"],
|
|
107
|
+
"timeoutSeconds": 60,
|
|
108
|
+
"signature": "[QK:CARRIER-OPT-OUT-SURVIVES-SEPARATOR]",
|
|
109
|
+
"signatureSource": "scripts/check-acp-carrier-augment.ts"
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"claim": "CARRIER-STAYS-TINY",
|
|
113
|
+
"title": "the shipped carrier grows into a real prompt — size, not shape, is what routes an OAuth subscription call to metered extra usage",
|
|
114
|
+
"subject": "pi-extensions/lib/acp/prompts/engraving.md",
|
|
115
|
+
"find": ["# Engraving Here"],
|
|
116
|
+
"replace": [
|
|
117
|
+
"# Engraving Here\n\n## Operator standing orders\n\n- Address the operator by name and keep the reply language matched to the prompt.\n- Prefer surgical edits over rewrites; verify every claim you make about behavior.\n- Never bypass a commit hook, and never push without an explicit request.\n- Indentation is tabs unless the project's linter says otherwise.\n- When docs and behavior disagree, report it and fix the source of truth.\n- Long-running commands belong in tmux, not in a foreground shell.\n- Record the next concrete move before you stop working.\n\n## Project context\n\nThis checkout is the bridge itself, so treat the adapter contract in the rail doc\nas authoritative and re-read it before touching a gate. The qualification lane is\nthe oracle for every contract claim; a green assertion count proves nothing on\nits own."
|
|
118
|
+
],
|
|
119
|
+
"gate": ["bash", "run.sh", "check-acp-carrier-augment"],
|
|
120
|
+
"timeoutSeconds": 60,
|
|
121
|
+
"signature": "[QK:CARRIER-STAYS-TINY]",
|
|
122
|
+
"signatureSource": "scripts/check-acp-carrier-augment.ts"
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"claim": "CARRIER-META-SENDS-CARRIER-VERBATIM",
|
|
126
|
+
"title": "the meta hop normalizes the carrier — the boundary is stripped back off on the wire while bridgeConfigSignature still folds the bounded value, so the A-join returns and reuse keys on a string the backend never received",
|
|
127
|
+
"subject": "pi-extensions/lib/acp/tool-surface.ts",
|
|
128
|
+
"find": ["\t\tmeta.systemPrompt = normalizedSystemPrompt;"],
|
|
129
|
+
"replace": ["\t\tmeta.systemPrompt = normalizedSystemPrompt.trim();"],
|
|
130
|
+
"gate": ["bash", "run.sh", "check-acp-carrier-augment"],
|
|
131
|
+
"timeoutSeconds": 60,
|
|
132
|
+
"signature": "[QK:CARRIER-META-SENDS-CARRIER-VERBATIM]",
|
|
133
|
+
"signatureSource": "scripts/check-acp-carrier-augment.ts"
|
|
28
134
|
}
|
|
29
135
|
]
|
|
30
136
|
}
|
|
@@ -142,13 +142,13 @@
|
|
|
142
142
|
"\t\tawait setConfig.call(connection, { sessionId: acpSessionId, configId: \"model\", value: nativeModelId });",
|
|
143
143
|
"\t},",
|
|
144
144
|
"",
|
|
145
|
-
"\t// A connection change must invalidate a reused session (
|
|
145
|
+
"\t// A connection change must invalidate a reused session (rail: Adapter contract). Flat,"
|
|
146
146
|
],
|
|
147
147
|
"replace": [
|
|
148
148
|
"\t\tawait setConfig.call(connection, { sessionId: acpSessionId, configId: \"model\", value: \"auto\" });",
|
|
149
149
|
"\t},",
|
|
150
150
|
"",
|
|
151
|
-
"\t// A connection change must invalidate a reused session (
|
|
151
|
+
"\t// A connection change must invalidate a reused session (rail: Adapter contract). Flat,"
|
|
152
152
|
],
|
|
153
153
|
"gate": ["bash", "run.sh", "check-acp-cortex"],
|
|
154
154
|
"timeoutSeconds": 300,
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"lane": "acp-overlay",
|
|
4
|
+
"mutants": [
|
|
5
|
+
{
|
|
6
|
+
"claim": "ACP-OVERLAY-UNATTENDED-BYPASS",
|
|
7
|
+
"title": "the isolated Claude overlay goes back to authoring permissions.defaultMode \"default\" — the overlay is the only settings authority the ACP child reads, so an unattended entwurf turn suspends on an interactive permission prompt nobody is there to answer, and the session stalls rather than fails",
|
|
8
|
+
"subject": "pi-extensions/lib/acp/overlay.ts",
|
|
9
|
+
"find": ["\t\t\tpermissions: { defaultMode: \"bypassPermissions\" },"],
|
|
10
|
+
"replace": ["\t\t\tpermissions: { defaultMode: \"default\" },"],
|
|
11
|
+
"gate": ["bash", "run.sh", "check-acp-overlay"],
|
|
12
|
+
"timeoutSeconds": 180,
|
|
13
|
+
"signature": "[QK:ACP-OVERLAY-UNATTENDED-BYPASS]",
|
|
14
|
+
"signatureSource": "scripts/check-acp-overlay.ts"
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"lane": "acp-prompt-lifecycle",
|
|
4
|
+
"mutants": [
|
|
5
|
+
{
|
|
6
|
+
"claim": "PROMPT-STALL-NOT-KILLED",
|
|
7
|
+
"title": "an absolute wall clock re-planted on the in-flight prompt — a turn that is still working is cut for being long",
|
|
8
|
+
"subject": "pi-extensions/lib/acp/backend.ts",
|
|
9
|
+
"find": ["\t\treturn await Promise.race([session.connection.prompt(promptArgs), lifecycle]);"],
|
|
10
|
+
"replace": [
|
|
11
|
+
"\t\treturn await Promise.race([\n\t\t\tsession.connection.prompt(promptArgs),\n\t\t\tlifecycle,\n\t\t\tnew Promise<never>((_, reject) => setTimeout(() => reject(new Error(\"prompt timed out after 100ms\")), 100)),\n\t\t]);"
|
|
12
|
+
],
|
|
13
|
+
"gate": ["bash", "run.sh", "check-acp-prompt-lifecycle"],
|
|
14
|
+
"timeoutSeconds": 180,
|
|
15
|
+
"signature": "[QK:PROMPT-STALL-NOT-KILLED]",
|
|
16
|
+
"signatureSource": "scripts/check-acp-prompt-lifecycle.ts"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"claim": "ABORT-SENDS-PROTOCOL-CANCEL",
|
|
20
|
+
"title": "abort stops asking the agent to cancel its own turn — back to killing a child mid-tool instead of the protocol ending",
|
|
21
|
+
"subject": "pi-extensions/lib/acp/backend.ts",
|
|
22
|
+
"find": ["\t\t\tsession.connection.cancel?.({ sessionId: promptArgs.sessionId });"],
|
|
23
|
+
"replace": ["\t\t\tvoid promptArgs;"],
|
|
24
|
+
"gate": ["bash", "run.sh", "check-acp-prompt-lifecycle"],
|
|
25
|
+
"timeoutSeconds": 180,
|
|
26
|
+
"signature": "[QK:ABORT-SENDS-PROTOCOL-CANCEL]",
|
|
27
|
+
"signatureSource": "scripts/check-acp-prompt-lifecycle.ts"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"claim": "ABORT-ESCALATION-BOUNDED",
|
|
31
|
+
"title": "the post-abort grace ignores the bound it was given — an abort against a wedged agent waits on a clock the caller does not control",
|
|
32
|
+
"subject": "pi-extensions/lib/acp/backend.ts",
|
|
33
|
+
"find": ["\t\tgraceTimer = setTimeout(escalateAbort, opts.graceMs);"],
|
|
34
|
+
"replace": ["\t\tgraceTimer = setTimeout(escalateAbort, 5_000);"],
|
|
35
|
+
"gate": ["bash", "run.sh", "check-acp-prompt-lifecycle"],
|
|
36
|
+
"timeoutSeconds": 180,
|
|
37
|
+
"signature": "[QK:ABORT-ESCALATION-BOUNDED]",
|
|
38
|
+
"signatureSource": "scripts/check-acp-prompt-lifecycle.ts"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"claim": "CHILD-EXIT-DIAGNOSED",
|
|
42
|
+
"title": "a mid-prompt child death stops reporting HOW it ended — the operator is back to an undiagnosable connection error",
|
|
43
|
+
"subject": "pi-extensions/lib/acp/backend.ts",
|
|
44
|
+
"find": [
|
|
45
|
+
"\tconst parts = [\n\t\texit.code !== null ? `exit code ${exit.code}` : undefined,\n\t\texit.signal ? `signal ${exit.signal}` : undefined,\n\t].filter(Boolean);\n\treturn parts.length > 0 ? parts.join(\", \") : \"no exit status\";"
|
|
46
|
+
],
|
|
47
|
+
"replace": ["\treturn \"no exit status\";"],
|
|
48
|
+
"gate": ["bash", "run.sh", "check-acp-prompt-lifecycle"],
|
|
49
|
+
"timeoutSeconds": 180,
|
|
50
|
+
"signature": "[QK:CHILD-EXIT-DIAGNOSED]",
|
|
51
|
+
"signatureSource": "scripts/check-acp-prompt-lifecycle.ts"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"claim": "REUSE-CARRIES-CHILD-DIAGNOSTICS",
|
|
55
|
+
"title": "the reuse path drops the child's stderr tail again — exactly the live sonnet failure that showed only \"ACP connection closed\"",
|
|
56
|
+
"subject": "pi-extensions/lib/acp/backend.ts",
|
|
57
|
+
"find": ["\t\t\tfinishError(err, aborted, session.stderrTail);"],
|
|
58
|
+
"replace": ["\t\t\tfinishError(err, aborted);"],
|
|
59
|
+
"gate": ["bash", "run.sh", "check-acp-prompt-lifecycle"],
|
|
60
|
+
"timeoutSeconds": 180,
|
|
61
|
+
"signature": "[QK:REUSE-CARRIES-CHILD-DIAGNOSTICS]",
|
|
62
|
+
"signatureSource": "scripts/check-acp-prompt-lifecycle.ts"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"claim": "PROMPT-ERROR-NOT-TRANSIENT",
|
|
66
|
+
"title": "a prompt-phase lifecycle failure worded as a timeout — pi reclassifies it as transient and cold-replays the whole prompt",
|
|
67
|
+
"subject": "pi-extensions/lib/acp/backend.ts",
|
|
68
|
+
"find": ["\t\t\t\"this turn has no answer\","],
|
|
69
|
+
"replace": ["\t\t\t\"the prompt timed out\","],
|
|
70
|
+
"gate": ["bash", "run.sh", "check-acp-prompt-lifecycle"],
|
|
71
|
+
"timeoutSeconds": 180,
|
|
72
|
+
"signature": "[QK:PROMPT-ERROR-NOT-TRANSIENT]",
|
|
73
|
+
"signatureSource": "scripts/check-acp-prompt-lifecycle.ts"
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"claim": "IDLE-DEATH-ANNOUNCED",
|
|
77
|
+
"title": "a backend session that died between turns is cleared but never announced — the next turn respawns silently and the operator never learns the session is gone",
|
|
78
|
+
"subject": "pi-extensions/lib/acp/backend.ts",
|
|
79
|
+
"find": [
|
|
80
|
+
"\t\t\tconst priorEnd = takeUnreportedChildEnd(sessionKey);\n\t\t\tif (priorEnd) {\n\t\t\t\tpushAcpLifecycleNotice(\n\t\t\t\t\tstate,\n\t\t\t\t\t`previous ${adapter.backend} session ended between turns (${describeChildEnd(priorEnd)}) — opening a new one`,\n\t\t\t\t);\n\t\t\t}"
|
|
81
|
+
],
|
|
82
|
+
"replace": ["\t\t\ttakeUnreportedChildEnd(sessionKey);"],
|
|
83
|
+
"gate": ["bash", "run.sh", "check-acp-prompt-lifecycle"],
|
|
84
|
+
"timeoutSeconds": 180,
|
|
85
|
+
"signature": "[QK:IDLE-DEATH-ANNOUNCED]",
|
|
86
|
+
"signatureSource": "scripts/check-acp-prompt-lifecycle.ts"
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"claim": "RETIRED-TEARDOWN-NOT-ANNOUNCED",
|
|
90
|
+
"title": "a teardown we performed ourselves is reported as a death — every ordinary turn-scoped turn cries wolf about its own cleanup",
|
|
91
|
+
"subject": "pi-extensions/lib/acp/backend.ts",
|
|
92
|
+
"find": ["\t} else if (!session.retiring) {"],
|
|
93
|
+
"replace": ["\t} else {"],
|
|
94
|
+
"gate": ["bash", "run.sh", "check-acp-prompt-lifecycle"],
|
|
95
|
+
"timeoutSeconds": 180,
|
|
96
|
+
"signature": "[QK:RETIRED-TEARDOWN-NOT-ANNOUNCED]",
|
|
97
|
+
"signatureSource": "scripts/check-acp-prompt-lifecycle.ts"
|
|
98
|
+
}
|
|
99
|
+
]
|
|
100
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"lane": "acp-stop-reason",
|
|
4
|
+
"mutants": [
|
|
5
|
+
{
|
|
6
|
+
"claim": "STOP-REFUSAL-NOT-SUCCESS",
|
|
7
|
+
"title": "a model refusal reported as a finished answer — the turn has no content, yet pi seals it done/stop",
|
|
8
|
+
"subject": "pi-extensions/lib/acp/backend.ts",
|
|
9
|
+
"find": [
|
|
10
|
+
"\t\tcase \"refusal\":\n\t\t\treturn {\n\t\t\t\tstopReason: \"error\",\n\t\t\t\trawStopReason: stopReason,\n\t\t\t\terrorMessage: \"ACP backend stopped with: refusal (the model declined to answer; the turn is incomplete)\",\n\t\t\t};"
|
|
11
|
+
],
|
|
12
|
+
"replace": ["\t\tcase \"refusal\":\n\t\t\treturn { stopReason: \"stop\", rawStopReason: stopReason };"],
|
|
13
|
+
"gate": ["bash", "run.sh", "check-acp-stop-reason"],
|
|
14
|
+
"timeoutSeconds": 120,
|
|
15
|
+
"signature": "[QK:STOP-REFUSAL-NOT-SUCCESS]",
|
|
16
|
+
"signatureSource": "scripts/check-acp-stop-reason.ts"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"claim": "STOP-TURNBUDGET-NOT-SUCCESS",
|
|
20
|
+
"title": "an exhausted per-turn request budget reported as success — the answer is cut short but pi seals it done",
|
|
21
|
+
"subject": "pi-extensions/lib/acp/backend.ts",
|
|
22
|
+
"find": [
|
|
23
|
+
"\t\tcase \"max_turn_requests\":\n\t\t\treturn {\n\t\t\t\tstopReason: \"error\",\n\t\t\t\trawStopReason: stopReason,\n\t\t\t\terrorMessage:\n\t\t\t\t\t\"ACP backend stopped with: max_turn_requests (the backend's per-turn request budget was exhausted; the turn is incomplete)\",\n\t\t\t};"
|
|
24
|
+
],
|
|
25
|
+
"replace": ["\t\tcase \"max_turn_requests\":\n\t\t\treturn { stopReason: \"stop\", rawStopReason: stopReason };"],
|
|
26
|
+
"gate": ["bash", "run.sh", "check-acp-stop-reason"],
|
|
27
|
+
"timeoutSeconds": 120,
|
|
28
|
+
"signature": "[QK:STOP-TURNBUDGET-NOT-SUCCESS]",
|
|
29
|
+
"signatureSource": "scripts/check-acp-stop-reason.ts"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"claim": "STOP-UNKNOWN-NOT-SUCCESS",
|
|
33
|
+
"title": "the collapse-to-stop default returns: any reason this build has never seen becomes a clean success",
|
|
34
|
+
"subject": "pi-extensions/lib/acp/backend.ts",
|
|
35
|
+
"find": [
|
|
36
|
+
"\t\tdefault:\n\t\t\treturn {\n\t\t\t\tstopReason: \"error\",\n\t\t\t\trawStopReason: stopReason,\n\t\t\t\terrorMessage: `ACP backend stopped with an unrecognized reason: ${stopReason}`,\n\t\t\t};"
|
|
37
|
+
],
|
|
38
|
+
"replace": ["\t\tdefault:\n\t\t\treturn { stopReason: \"stop\", rawStopReason: stopReason };"],
|
|
39
|
+
"gate": ["bash", "run.sh", "check-acp-stop-reason"],
|
|
40
|
+
"timeoutSeconds": 120,
|
|
41
|
+
"signature": "[QK:STOP-UNKNOWN-NOT-SUCCESS]",
|
|
42
|
+
"signatureSource": "scripts/check-acp-stop-reason.ts"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"claim": "STOP-ABSENT-NOT-SUCCESS",
|
|
46
|
+
"title": "a prompt result carrying NO stop reason treated as a finished turn instead of an error",
|
|
47
|
+
"subject": "pi-extensions/lib/acp/backend.ts",
|
|
48
|
+
"find": [
|
|
49
|
+
"\t\tcase undefined:\n\t\t\treturn {\n\t\t\t\tstopReason: \"error\",\n\t\t\t\terrorMessage: \"ACP backend ended the turn without a stop reason\",\n\t\t\t};"
|
|
50
|
+
],
|
|
51
|
+
"replace": ["\t\tcase undefined:\n\t\t\treturn { stopReason: \"stop\" };"],
|
|
52
|
+
"gate": ["bash", "run.sh", "check-acp-stop-reason"],
|
|
53
|
+
"timeoutSeconds": 120,
|
|
54
|
+
"signature": "[QK:STOP-ABSENT-NOT-SUCCESS]",
|
|
55
|
+
"signatureSource": "scripts/check-acp-stop-reason.ts"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"claim": "STOP-RAW-PRESERVED",
|
|
59
|
+
"title": "the raw ACP reason is dropped on the way out, so no consumer can see what the backend actually said",
|
|
60
|
+
"subject": "pi-extensions/lib/acp/backend.ts",
|
|
61
|
+
"find": ["\t\tif (verdict.rawStopReason !== undefined) state.output.rawStopReason = verdict.rawStopReason;\n"],
|
|
62
|
+
"replace": [""],
|
|
63
|
+
"gate": ["bash", "run.sh", "check-acp-stop-reason"],
|
|
64
|
+
"timeoutSeconds": 120,
|
|
65
|
+
"signature": "[QK:STOP-RAW-PRESERVED]",
|
|
66
|
+
"signatureSource": "scripts/check-acp-stop-reason.ts"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"claim": "STOP-PENDING-SEED",
|
|
70
|
+
"title": "the stream state seeds success: an in-flight turn already reads as a finished stop before any terminal arrives",
|
|
71
|
+
"subject": "pi-extensions/lib/acp/event-mapper.ts",
|
|
72
|
+
"find": ["\t\tstopReason: \"pending\","],
|
|
73
|
+
"replace": ["\t\tstopReason: \"stop\","],
|
|
74
|
+
"gate": ["bash", "run.sh", "check-acp-stop-reason"],
|
|
75
|
+
"timeoutSeconds": 120,
|
|
76
|
+
"signature": "[QK:STOP-PENDING-SEED]",
|
|
77
|
+
"signatureSource": "scripts/check-acp-stop-reason.ts"
|
|
78
|
+
}
|
|
79
|
+
]
|
|
80
|
+
}
|