@junghanacs/entwurf 0.51.0 → 0.53.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 (88) hide show
  1. package/AGENTS.md +1 -1
  2. package/BASELINE.md +116 -397
  3. package/CHANGELOG.md +191 -2
  4. package/CONTRIBUTING.md +4 -4
  5. package/DELIVERY.md +2 -2
  6. package/ENTWURF-CLI.json +1 -1
  7. package/ENTWURF.json +10 -4
  8. package/README.md +11 -8
  9. package/VERIFY.md +333 -143
  10. package/demo/README.md +1 -1
  11. package/demo/demo-baseline.sh +1 -1
  12. package/demo/demo.sh +1 -1
  13. package/docs/acp-backend-rail.md +248 -34
  14. package/docs/setup-clean-host.md +75 -80
  15. package/mcp/entwurf-bridge/dist/ENTWURF.json +10 -4
  16. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/acp-client.js +1 -1
  17. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/backend-adapter.js +90 -14
  18. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/config.js +4 -9
  19. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/context.js +15 -0
  20. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/model-ids.js +9 -12
  21. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/models.js +41 -56
  22. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/tool-surface.js +33 -6
  23. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-control-rpc.js +4 -1
  24. package/mcp/entwurf-bridge/dist/pi-extensions/lib/fresh-call-composition.js +34 -3
  25. package/mcp/entwurf-bridge/dist/pi-extensions/lib/herdr-fresh-call.js +3 -1
  26. package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-mailbox-body.js +1 -1
  27. package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +2 -1
  28. package/package.json +14 -14
  29. package/pi/pi-durable/carrier/harness-setup.js +1 -1
  30. package/pi/pi-durable/carrier/subagent.js +1 -1
  31. package/pi/pi-durable/overlay/upstream-pin.json +15 -15
  32. package/pi/settings.reference.json +0 -1
  33. package/pi-extensions/acp-provider.ts +12 -6
  34. package/pi-extensions/lib/acp/acp-client.ts +6 -7
  35. package/pi-extensions/lib/acp/backend-adapter.ts +139 -15
  36. package/pi-extensions/lib/acp/backend.ts +261 -32
  37. package/pi-extensions/lib/acp/config.ts +4 -9
  38. package/pi-extensions/lib/acp/context.ts +17 -0
  39. package/pi-extensions/lib/acp/event-mapper.ts +236 -17
  40. package/pi-extensions/lib/acp/model-ids.js +9 -12
  41. package/pi-extensions/lib/acp/models.ts +46 -57
  42. package/pi-extensions/lib/acp/tool-surface.ts +34 -6
  43. package/pi-extensions/lib/entwurf-control-rpc.ts +5 -1
  44. package/pi-extensions/lib/fresh-call-composition.ts +37 -3
  45. package/pi-extensions/lib/herdr-fresh-call.ts +3 -1
  46. package/pi-extensions/lib/meta-mailbox-body.ts +1 -1
  47. package/pi-extensions/lib/mux-fresh-call.ts +3 -1
  48. package/pi-extensions/meta-bridge-pi-durable.ts +1 -1
  49. package/run.sh +62 -120
  50. package/scripts/check-acp-carrier-augment.ts +3 -21
  51. package/scripts/check-acp-prompt-lifecycle.ts +24 -3
  52. package/scripts/check-acp-provider-surface.ts +17 -26
  53. package/scripts/check-acp-session-reuse.ts +23 -4
  54. package/scripts/check-acp-stop-reason.ts +22 -3
  55. package/scripts/check-acp-stream-hooks.ts +24 -3
  56. package/scripts/check-acp-usage-accounting.ts +53 -24
  57. package/scripts/check-copilot-receive-arm.ts +19 -2
  58. package/scripts/check-entwurf-v2-surface.ts +7 -21
  59. package/scripts/check-fresh-call-dispatch.ts +9 -3
  60. package/scripts/check-gate-qualification.ts +6 -6
  61. package/scripts/check-herdr-fresh-call.ts +1 -0
  62. package/scripts/check-install-surface.ts +6 -0
  63. package/scripts/check-pi-durable-send.ts +4 -1
  64. package/scripts/check-probe-ordering.ts +34 -24
  65. package/scripts/check-release-gate-outcomes.ts +9 -23
  66. package/scripts/lib/codex-fresh-source-receipts.ts +12 -6
  67. package/scripts/lib/probe-cli-target.ts +3 -2
  68. package/scripts/mutants/acp-augment.json +0 -26
  69. package/scripts/mutants/acp-cortex.json +10 -183
  70. package/scripts/mutants/acp-foreground-only.json +11 -0
  71. package/scripts/mutants/acp-launch-namespace.json +3 -1
  72. package/scripts/mutants/acp-prompt-lifecycle.json +2 -2
  73. package/scripts/mutants/acp-set-model-readback.json +488 -0
  74. package/scripts/mutants/agent-guidance.json +2 -2
  75. package/scripts/mutants/codex-native.json +2 -2
  76. package/scripts/mutants/control-send-receipt.json +47 -7
  77. package/scripts/mutants/mux-fresh-call.json +12 -1
  78. package/scripts/mutants/pack-install.json +4 -4
  79. package/scripts/mutants/pi-floor.json +8 -10
  80. package/scripts/mutants/release-gate.json +3 -3
  81. package/scripts/mutants/v2-surface.json +4 -6
  82. package/scripts/raw-acp-compaction-measure/probe.ts +8 -2
  83. package/scripts/smoke-acp-cortex-live.ts +4 -408
  84. package/scripts/smoke-acp-ordering-probe-live.ts +7 -1
  85. package/scripts/smoke-acp-session-reuse-live.ts +11 -2
  86. package/scripts/smoke-acp-v2-send-live.ts +1 -1
  87. package/scripts/smoke-herdr-raw-install-live.sh +4 -4
  88. package/scripts/check-acp-cortex.ts +0 -710
@@ -260,30 +260,18 @@ function runSubcommand(sub: string, env: Record<string, string | undefined>): {
260
260
  // is the second skip surface: some smokes never reach their .ts file at all.
261
261
  // ===========================================================================
262
262
  {
263
- const viaWrapper = runSubcommand("smoke-acp-cortex-live", { LIVE: undefined });
263
+ const viaWrapper = runSubcommand("smoke-entwurf-v2-matrix-live", { LIVE: undefined });
264
264
  assert.equal(
265
265
  viaWrapper.code,
266
266
  LIVE_SKIP_EXIT,
267
267
  `[QK:WRAPPER-SKIP-IS-PROTOCOL-EXIT] a run.sh smoke WRAPPER that declines a prerequisite must return the protocol ` +
268
- `SKIP code, not 0 — got exit ${viaWrapper.code}. The wrapper is a skip surface of its own (cortex and matrix ` +
269
- "both decline before their .ts is ever reached), so fixing only the TypeScript " +
268
+ `SKIP code, not 0 — got exit ${viaWrapper.code}. The matrix wrapper declines before its .ts is reached, so fixing only the TypeScript ` +
270
269
  `half would leave the aggregate counting those as PASS. Output: ${JSON.stringify(viaWrapper.output.slice(-300))}`,
271
270
  );
272
271
 
273
- // The measured Cortex cell: LIVE IS set, but the connection the adapter needs
274
- // is not. Which branch declines (the wrapper's `cortex` PATH check or the
275
- // smoke's own connection check) depends on the host; the OUTCOME must not.
276
- const missingPrereq = runSubcommand("smoke-acp-cortex-live", {
277
- LIVE: "1",
278
- ENTWURF_ACP_CORTEX_CONNECTION: undefined,
279
- });
280
- assert.equal(
281
- missingPrereq.code,
282
- LIVE_SKIP_EXIT,
283
- "LIVE=1 with no ENTWURF_ACP_CORTEX_CONNECTION must be a SKIP, not a PASS — this is the exact cell that made a " +
284
- "cortex-less host look like cortex acceptance. Output: " +
285
- JSON.stringify(missingPrereq.output.slice(-300)),
286
- );
272
+ const disabled = runSubcommand("smoke-acp-cortex-live", { LIVE: undefined });
273
+ assert.equal(disabled.code, 2, "disabled Cortex smoke must refuse before a vendor probe");
274
+ assert.match(disabled.output, /cortex-acp-disabled/);
287
275
  }
288
276
 
289
277
  // ===========================================================================
@@ -312,11 +300,9 @@ function runSubcommand(sub: string, env: Record<string, string | undefined>): {
312
300
  "smoke-agy-native-push-live": ["VERIFY.md", "Aggregate release-gate does not own an agy conversation id"],
313
301
  "smoke-codex-native-push-live": ["VERIFY.md", "Aggregate release-gate does not own a loaded Codex thread"],
314
302
  "smoke-acp-ordering-probe-live": ["docs/acp-backend-rail.md", "opt-in paired observation"],
315
- // Cortex needs an external Snowflake connection the HOST owns, so an aggregate
316
- // that required it would block every cut taken without that account. Excluded —
317
- // NOT waived: its direct call stays required for a Cortex-rail cut, and running
318
- // it without the connection still reports protocol SKIP rather than a pass.
319
- "smoke-acp-cortex-live": ["VERIFY.md", "The release aggregate does not re-certify Cortex"],
303
+ // Cortex ACP is disabled. Its former direct LIVE requirement is historical;
304
+ // the retained command refuses by name without starting a vendor probe.
305
+ "smoke-acp-cortex-live": ["VERIFY.md", "Cortex ACP is disabled"],
320
306
  // #116 C4: first evidence precedes promotion. The herdr rail's LIVE acceptance exists and
321
307
  // runs on demand, but a rail whose first real acceptance run is days old has not earned a
322
308
  // place in the aggregate that blocks every cut — and herdr is an OPTIONAL rail, so a cut
@@ -1066,7 +1052,7 @@ console.log(
1066
1052
  "everything else→FAIL (never rounding a skip up), `--cut` refuses a MUST SKIP while a bare diagnostic run stays " +
1067
1053
  "exit 0, no LIVE smoke still carries the pre-P1 exit-0 skip shape, and both real skip surfaces were INVOKED and " +
1068
1054
  "observed to propagate the code: a smoke with LIVE unset (through run_ts, with its operator-readable marker) and " +
1069
- "a run.sh wrapper declining its own prerequisite (including the measured LIVE=1 no-cortex-connection cell); and every " +
1055
+ "a run.sh wrapper declining its own prerequisite; the retired Cortex smoke refuses by name; and every " +
1070
1056
  "LIVE smoke is either wired into release_gate or excluded by a sentence the docs still carry; and the moved " +
1071
1057
  "check-gate-qualification stays reachable only as the schedule/dispatch drift diagnostic (absent from the " +
1072
1058
  "default chain, from release_gate, from every push and from the release skill; exactly once in CI) over the " +
@@ -452,14 +452,20 @@ export function codexSourceToolReceipts(thread: unknown): SourceToolReceipt[] {
452
452
  }
453
453
 
454
454
  function exactSenderInfoSuffix(text: string, nonce: string): string | null {
455
- const prefix = `${nonce}\n\n<sender_info>`;
455
+ // Independent expected frame: never import the producer's guidance as the receipt oracle.
456
+ const guidance =
457
+ "\n\n[entwurf: When a reply is requested, send the result via entwurf_v2 to the requested recipient (default: sender_info.sessionId); a local final is not delivery. Honor routing and no-send instructions; an absent or false wants_reply does not cancel a request. A replyable address alone needs no acknowledgment.]";
458
+ const open = "<sender_info>";
456
459
  const suffix = "</sender_info>";
457
- if (!text.startsWith(prefix) || !text.endsWith(suffix)) return null;
458
- const json = text.slice(prefix.length, -suffix.length);
459
- if (json.includes("\n") || json.includes("<sender_info>")) return null;
460
+ const at = text.lastIndexOf(open);
461
+ if (at < 0 || !text.endsWith(suffix)) return null;
462
+ const json = text.slice(at + open.length, -suffix.length);
463
+ if (json.includes("\n") || json.includes(open)) return null;
460
464
  try {
461
- const sender = JSON.parse(json) as { sessionId?: unknown };
462
- return typeof sender.sessionId === "string" ? sender.sessionId : null;
465
+ const sender = JSON.parse(json) as { sessionId?: unknown; replyable?: unknown };
466
+ const prefix = `${nonce}${sender.replyable === true ? guidance : ""}\n\n${open}`;
467
+ if (!text.startsWith(prefix) || !text.endsWith(suffix)) return null;
468
+ return at === prefix.length - open.length && typeof sender.sessionId === "string" ? sender.sessionId : null;
463
469
  } catch {
464
470
  return null;
465
471
  }
@@ -92,8 +92,9 @@ export function hashFileSha256(path: string): string {
92
92
 
93
93
  /** Refuse if the given env carries the upstream override — KEY PRESENCE is the
94
94
  * predicate, an empty string included. Upstream consumers disagree about
95
- * empty (`??` at acp-agent.js:4083 treats "" as set and passes it on; a
96
- * truthy check treats it as unset), and the probe refuses the ambiguity
95
+ * empty (0.85.1's `??` at acp-agent.js:4083 treated "" as set and passed it
96
+ * on; 0.106.0's truthy check at dist/claude-cli.js:20 treats it as unset), and
97
+ * the probe refuses the ambiguity
97
98
  * instead of picking a side. Used twice: on the runner's own process.env
98
99
  * before resolving, and on the COMPOSED spawn env of every ACP child (adapter
99
100
  * launch defaults / overlay overrides could inject what process.env did not
@@ -52,32 +52,6 @@
52
52
  "signature": "[QK:CARRIER-FRAME-NAMES-CLAUDE-CARRIER]",
53
53
  "signatureSource": "scripts/check-acp-carrier-augment.ts"
54
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
55
  {
82
56
  "claim": "CARRIER-LEADS-ITS-OWN-BLOCK",
83
57
  "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)",
@@ -3,205 +3,32 @@
3
3
  "lane": "acp-cortex",
4
4
  "mutants": [
5
5
  {
6
- "claim": "CORTEX-CURATED-FOUR-ROWS",
7
- "title": "dropping a curated row silently shrinks the GLG-decided 4-row cortex surface",
8
- "subject": "pi-extensions/lib/acp/model-ids.js",
9
- "find": [
10
- "export const SUPPORTED_CORTEX_MODEL_IDS = /** @type {const} */ ([",
11
- "\t\"cortex-auto\",",
12
- "\t\"cortex-claude-opus-5\",",
13
- "\t\"cortex-claude-sonnet-5\",",
14
- "\t\"cortex-openai-gpt-5.4\",",
15
- "]);"
16
- ],
17
- "replace": [
18
- "export const SUPPORTED_CORTEX_MODEL_IDS = /** @type {const} */ ([",
19
- "\t\"cortex-auto\",",
20
- "\t\"cortex-claude-sonnet-5\",",
21
- "\t\"cortex-openai-gpt-5.4\",",
22
- "]);"
23
- ],
24
- "gate": ["bash", "run.sh", "check-acp-cortex"],
25
- "timeoutSeconds": 300,
26
- "signature": "[QK:CORTEX-CURATED-FOUR-ROWS]",
27
- "signatureSource": "scripts/check-acp-cortex.ts"
28
- },
29
- {
30
- "claim": "CLAUDE-CURATED-THREE-ROWS",
31
- "title": "dropping a curated CLAUDE row shrinks the operator's model list while the exact-set comparison stays green — expected derives from the same constant, so only the independent count catches it",
6
+ "claim": "CLAUDE-CURATED-FOUR-ROWS",
7
+ "title": "dropping a curated Claude row shrinks the operator's model list",
32
8
  "subject": "pi-extensions/lib/acp/model-ids.js",
33
9
  "find": [
34
10
  "export const SUPPORTED_ANTHROPIC_MODEL_IDS = /** @type {const} */ ([",
35
11
  "\t\"claude-sonnet-5-5\",",
36
12
  "\t\"claude-opus-5-5\",",
37
13
  "\t\"claude-fable-5-1\",",
14
+ "\t\"claude-fable-5-2\",",
38
15
  "]);"
39
16
  ],
40
17
  "replace": [
41
18
  "export const SUPPORTED_ANTHROPIC_MODEL_IDS = /** @type {const} */ ([",
42
19
  "\t\"claude-sonnet-5-5\",",
43
20
  "\t\"claude-opus-5-5\",",
21
+ "\t\"claude-fable-5-1\",",
44
22
  "]);"
45
23
  ],
46
24
  "gate": ["bash", "run.sh", "check-acp-provider-surface"],
47
25
  "timeoutSeconds": 300,
48
- "signature": "[QK:CLAUDE-CURATED-THREE-ROWS]",
26
+ "signature": "[QK:CLAUDE-CURATED-FOUR-ROWS]",
49
27
  "signatureSource": "scripts/check-acp-provider-surface.ts"
50
28
  },
51
- {
52
- "claim": "CORTEX-AUTH-NARROW-CREDENTIAL-CACHE",
53
- "title": "widening the auth passthrough to the whole cortex/cache leaks operator tool_outputs/tip_history into the child",
54
- "subject": "pi-extensions/lib/acp/overlay.ts",
55
- "find": [
56
- "\tcortexLinkIfExists(",
57
- "\t\tjoin(realSnowflake, \"cortex\", \"cache\", \"credential_cache\"),",
58
- "\t\tjoin(cortexDir, \"cache\", \"credential_cache\"),",
59
- "\t);"
60
- ],
61
- "replace": [
62
- "\trmSync(join(cortexDir, \"cache\"), { recursive: true, force: true });",
63
- "\tcortexLinkIfExists(join(realSnowflake, \"cortex\", \"cache\"), join(cortexDir, \"cache\"));"
64
- ],
65
- "gate": ["bash", "run.sh", "check-acp-cortex"],
66
- "timeoutSeconds": 300,
67
- "signature": "[QK:CORTEX-AUTH-NARROW-CREDENTIAL-CACHE]",
68
- "signatureSource": "scripts/check-acp-cortex.ts"
69
- },
70
- {
71
- "claim": "CORTEX-AUTOUPDATE-OFF",
72
- "title": "flipping autoUpdate back on lets the cortex runtime replace itself on launch mid-rail (D4)",
73
- "subject": "pi-extensions/lib/acp/overlay.ts",
74
- "find": ["\treturn `${JSON.stringify({ autoUpdate: false }, null, \"\\t\")}\\n`;"],
75
- "replace": ["\treturn `${JSON.stringify({ autoUpdate: true }, null, \"\\t\")}\\n`;"],
76
- "gate": ["bash", "run.sh", "check-acp-cortex"],
77
- "timeoutSeconds": 300,
78
- "signature": "[QK:CORTEX-AUTOUPDATE-OFF]",
79
- "signatureSource": "scripts/check-acp-cortex.ts"
80
- },
81
- {
82
- "claim": "CORTEX-REALHOME-PLATFORM-NEUTRAL",
83
- "title": "re-deriving realHome absoluteness from the POSIX flavor alone makes a drive/UNC HOME read as relative — the guard would state the host it runs on instead of the contract (PR #77, @yizixu)",
84
- "subject": "pi-extensions/lib/acp/overlay.ts",
85
- "find": ["\tif (!params.realHome || !(isAbsolute(params.realHome) || win32.isAbsolute(params.realHome))) {"],
86
- "replace": ["\tif (!params.realHome || !params.realHome.startsWith(\"/\")) {"],
87
- "gate": ["bash", "run.sh", "check-acp-cortex"],
88
- "timeoutSeconds": 300,
89
- "signature": "[QK:CORTEX-REALHOME-PLATFORM-NEUTRAL]",
90
- "signatureSource": "scripts/check-acp-cortex.ts"
91
- },
92
- {
93
- "claim": "CORTEX-BRIDGE-DUAL-HOME",
94
- "title": "dropping the bridge-only real-HOME restore leaves the garden store cut off — tools reach the model but see an empty garden (D10)",
95
- "subject": "pi-extensions/lib/acp/overlay.ts",
96
- "find": ["\t\tif (stdio.name === CORTEX_DUAL_HOME_BRIDGE_SERVER) env.HOME = realHome;"],
97
- "replace": ["\t\tvoid realHome;"],
98
- "gate": ["bash", "run.sh", "check-acp-cortex"],
99
- "timeoutSeconds": 300,
100
- "signature": "[QK:CORTEX-BRIDGE-DUAL-HOME]",
101
- "signatureSource": "scripts/check-acp-cortex.ts"
102
- },
103
- {
104
- "claim": "CORTEX-MCP-NON-STDIO-FAILLOUD",
105
- "title": "silently dropping a non-stdio server advertises a config the session does not have (D9 fail-loud)",
106
- "subject": "pi-extensions/lib/acp/overlay.ts",
107
- "find": [
108
- "\t\tif (\"type\" in server && (server.type === \"http\" || server.type === \"sse\")) {",
109
- "\t\t\tthrow new Error(",
110
- "\t\t\t\t`entwurf: cortex mcp.json projection cannot represent ${server.type} server ` +",
111
- "\t\t\t\t\t`${JSON.stringify(server.name)} — only stdio entries are measured through ` +",
112
- "\t\t\t\t\t`$SNOWFLAKE_HOME/cortex/mcp.json (CP0 D9). Remove it from entwurfProvider.mcpServers ` +",
113
- "\t\t\t\t\t`for cortex models or front it with a stdio bridge.`,",
114
- "\t\t\t);",
115
- "\t\t}"
116
- ],
117
- "replace": [
118
- "\t\tif (\"type\" in server && (server.type === \"http\" || server.type === \"sse\")) {",
119
- "\t\t\tcontinue;",
120
- "\t\t}"
121
- ],
122
- "gate": ["bash", "run.sh", "check-acp-cortex"],
123
- "timeoutSeconds": 300,
124
- "signature": "[QK:CORTEX-MCP-NON-STDIO-FAILLOUD]",
125
- "signatureSource": "scripts/check-acp-cortex.ts"
126
- },
127
- {
128
- "claim": "CORTEX-OVERLAY-SESSION-SCOPED",
129
- "title": "a static shared overlay dir lets two residents overwrite one mcp.json/envelope",
130
- "subject": "pi-extensions/lib/acp/overlay.ts",
131
- "find": ["\tconst scopeDir = join(overlaysRoot, cortexOverlayScopeId(params.scopeKey));"],
132
- "replace": ["\tconst scopeDir = join(overlaysRoot, \"shared\");"],
133
- "gate": ["bash", "run.sh", "check-acp-cortex"],
134
- "timeoutSeconds": 300,
135
- "signature": "[QK:CORTEX-OVERLAY-SESSION-SCOPED]",
136
- "signatureSource": "scripts/check-acp-cortex.ts"
137
- },
138
- {
139
- "claim": "CORTEX-HOME-PRESENCE-REFUSED",
140
- "title": "a truthy check instead of presence lets an empty-string CORTEX_HOME slip past the refusal (D3 E2b nuance)",
141
- "subject": "pi-extensions/lib/acp/backend-adapter.ts",
142
- "find": ["\t\tif (\"CORTEX_HOME\" in process.env) {"],
143
- "replace": ["\t\tif (process.env.CORTEX_HOME) {"],
144
- "gate": ["bash", "run.sh", "check-acp-cortex"],
145
- "timeoutSeconds": 300,
146
- "signature": "[QK:CORTEX-HOME-PRESENCE-REFUSED]",
147
- "signatureSource": "scripts/check-acp-cortex.ts"
148
- },
149
- {
150
- "claim": "CORTEX-LAUNCH-NO-MODEL-PIN",
151
- "title": "re-introducing the launch -m pin creates a second model authority that drifts from per-turn set-model",
152
- "subject": "pi-extensions/lib/acp/backend-adapter.ts",
153
- "find": [
154
- "\tresolveLaunch({ config }) {",
155
- "\t\tconst settings = config.adapterSettings as CortexAdapterSettings | undefined;",
156
- "\t\tconst connection = settings?.cortexConnection?.trim() || undefined;",
157
- "\t\tconst selectionArgs: string[] = [];"
158
- ],
159
- "replace": [
160
- "\tresolveLaunch({ config, nativeModelId }) {",
161
- "\t\tconst settings = config.adapterSettings as CortexAdapterSettings | undefined;",
162
- "\t\tconst connection = settings?.cortexConnection?.trim() || undefined;",
163
- "\t\tconst selectionArgs: string[] = [\"-m\", nativeModelId];"
164
- ],
165
- "gate": ["bash", "run.sh", "check-acp-cortex"],
166
- "timeoutSeconds": 300,
167
- "signature": "[QK:CORTEX-LAUNCH-NO-MODEL-PIN]",
168
- "signatureSource": "scripts/check-acp-cortex.ts"
169
- },
170
- {
171
- "claim": "CORTEX-ENFORCE-SET-MODEL",
172
- "title": "enforcing a constant instead of the native id silently answers with the wrong model (CP0-M)",
173
- "subject": "pi-extensions/lib/acp/backend-adapter.ts",
174
- "find": [
175
- "\t\tawait setConfig.call(connection, { sessionId: acpSessionId, configId: \"model\", value: nativeModelId });",
176
- "\t},",
177
- "",
178
- "\t// A connection change must invalidate a reused session (rail: Adapter contract). Flat,"
179
- ],
180
- "replace": [
181
- "\t\tawait setConfig.call(connection, { sessionId: acpSessionId, configId: \"model\", value: \"auto\" });",
182
- "\t},",
183
- "",
184
- "\t// A connection change must invalidate a reused session (rail: Adapter contract). Flat,"
185
- ],
186
- "gate": ["bash", "run.sh", "check-acp-cortex"],
187
- "timeoutSeconds": 300,
188
- "signature": "[QK:CORTEX-ENFORCE-SET-MODEL]",
189
- "signatureSource": "scripts/check-acp-cortex.ts"
190
- },
191
- {
192
- "claim": "CORTEX-OVERLAY-KEY-IS-SESSION-KEY",
193
- "title": "re-deriving the overlay scope from ambient env drops opts.sessionId — two same-process/cwd sessions alias one overlay/mcp.json",
194
- "subject": "pi-extensions/lib/acp/backend-adapter.ts",
195
- "find": ["\t\tconst overlay = ensureCortexDualHomeOverlay({", "\t\t\tscopeKey: sessionKey,"],
196
- "replace": ["\t\tconst overlay = ensureCortexDualHomeOverlay({", "\t\t\tscopeKey: piSessionId ?? \"static\","],
197
- "gate": ["bash", "run.sh", "check-acp-cortex"],
198
- "timeoutSeconds": 300,
199
- "signature": "[QK:CORTEX-OVERLAY-KEY-IS-SESSION-KEY]",
200
- "signatureSource": "scripts/check-acp-cortex.ts"
201
- },
202
29
  {
203
30
  "claim": "ACP-OVERLAY-SESSIONKEY-WIRED",
204
- "title": "passing a cwd-derived key instead of resolveSessionKey's value silently merges distinct sessions' overlay scopes at the production call site",
31
+ "title": "passing a cwd-derived key instead of resolveSessionKey's value merges distinct sessions' overlay scopes",
205
32
  "subject": "pi-extensions/lib/acp/backend.ts",
206
33
  "find": [
207
34
  "\t\t\tconst overlay = adapter.ensureOverlay({ cwd, modelId: model.id, nativeModelId, config, sessionKey });"
@@ -215,14 +42,14 @@
215
42
  "signatureSource": "scripts/check-acp-session-reuse.ts"
216
43
  },
217
44
  {
218
- "claim": "CORTEX-PROVIDER-EXACT-ROW-SURFACE",
219
- "title": "filtering the cortex rows out of the REAL provider entry drops the whole backend from the operator's model list while models.ts and check-acp-cortex stay green",
45
+ "claim": "ACP-PROVIDER-CLAUDE-ONLY",
46
+ "title": "the provider entry advertises a retired Cortex row despite the admission fence",
220
47
  "subject": "pi-extensions/acp-provider.ts",
221
48
  "find": ["\t\tmodels: allCuratedModels(),"],
222
- "replace": ["\t\tmodels: allCuratedModels().filter((m) => !m.id.startsWith(\"cortex-\")),"],
49
+ "replace": ["\t\tmodels: [...allCuratedModels(), { ...allCuratedModels()[0], id: \"cortex-auto\" }],"],
223
50
  "gate": ["bash", "run.sh", "check-acp-provider-surface"],
224
51
  "timeoutSeconds": 300,
225
- "signature": "[QK:CORTEX-PROVIDER-EXACT-ROW-SURFACE]",
52
+ "signature": "[QK:ACP-PROVIDER-CLAUDE-ONLY]",
226
53
  "signatureSource": "scripts/check-acp-provider-surface.ts"
227
54
  }
228
55
  ]
@@ -12,6 +12,17 @@
12
12
  "timeoutSeconds": 60,
13
13
  "signature": "[QK:ACP-CLAUDE-FOREGROUND-ONLY]",
14
14
  "signatureSource": "pi-extensions/lib/acp/backend-adapter.test.ts"
15
+ },
16
+ {
17
+ "claim": "ACP-CLAUDE-NO-IDLE-COMPACTION",
18
+ "title": "the claude session settings lose idleCompaction:false — claude-agent-acp 0.101+ sends its own /compact prompt on an idle retained session, a model turn pi never sees",
19
+ "subject": "pi-extensions/lib/acp/tool-surface.ts",
20
+ "find": ["\t\t\tidleCompaction: false,"],
21
+ "replace": [""],
22
+ "gate": ["bash", "run.sh", "check-acp-foreground-only"],
23
+ "timeoutSeconds": 60,
24
+ "signature": "[QK:ACP-CLAUDE-NO-IDLE-COMPACTION]",
25
+ "signatureSource": "pi-extensions/lib/acp/backend-adapter.test.ts"
15
26
  }
16
27
  ]
17
28
  }
@@ -7,7 +7,9 @@
7
7
  "title": "the default launch names the vendor bin directly again — the child re-enters the process-name space a janitor for another harness scans by substring, which is the whole of #72",
8
8
  "subject": "pi-extensions/lib/acp/backend-adapter.ts",
9
9
  "find": ["\tconst launcher = fileURLToPath(new URL(\"./claude-acp-launch.js\", import.meta.url));"],
10
- "replace": ["\tconst launcher = require.resolve(\"@agentclientprotocol/claude-agent-acp/dist/index.js\");"],
10
+ "replace": [
11
+ "\tconst launcher = require.resolve(\"@agentclientprotocol/claude-agent-acp/package.json\").replace(/package\\.json$/, \"dist/index.js\");"
12
+ ],
11
13
  "gate": ["bash", "run.sh", "check-acp-launch-namespace"],
12
14
  "timeoutSeconds": 240,
13
15
  "signature": "[QK:CLAUDE-LAUNCH-IS-NAMESPACED]",
@@ -77,9 +77,9 @@
77
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
78
  "subject": "pi-extensions/lib/acp/backend.ts",
79
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}"
80
+ "\t\t\tconst priorEnd = summary ? undefined : 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
81
  ],
82
- "replace": ["\t\t\ttakeUnreportedChildEnd(sessionKey);"],
82
+ "replace": ["\t\t\tif (!summary) takeUnreportedChildEnd(sessionKey);"],
83
83
  "gate": ["bash", "run.sh", "check-acp-prompt-lifecycle"],
84
84
  "timeoutSeconds": 180,
85
85
  "signature": "[QK:IDLE-DEATH-ANNOUNCED]",