@junghanacs/entwurf 0.41.1 → 0.45.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/AGENTS.md +2 -1
  2. package/BASELINE.md +7 -5
  3. package/CHANGELOG.md +344 -0
  4. package/DELIVERY.md +37 -20
  5. package/ENTWURF.json +236 -0
  6. package/README.md +75 -65
  7. package/VERIFY.md +9 -9
  8. package/docs/external-mcp-host.md +1 -1
  9. package/docs/setup-clean-host.md +59 -33
  10. package/mcp/entwurf-bridge/dist/ENTWURF.json +236 -0
  11. package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +32 -190
  12. package/mcp/entwurf-bridge/dist/pi-extensions/lib/agent-guidance.js +299 -0
  13. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-self-address.js +10 -0
  14. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-contract.js +23 -2
  15. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-decider.js +7 -1
  16. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-surface.js +23 -24
  17. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-visible-resume.js +15 -11
  18. package/mcp/entwurf-bridge/dist/pi-extensions/lib/fresh-call-composition.js +21 -10
  19. package/mcp/entwurf-bridge/dist/pi-extensions/lib/herdr-fresh-call.js +4 -2
  20. package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-sender-identity.js +5 -0
  21. package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +29 -24
  22. package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-resume-call.js +18 -16
  23. package/mcp/entwurf-bridge/dist/pi-extensions/lib/native-push/register.js +13 -2
  24. package/mcp/entwurf-bridge/dist/pi-extensions/lib/omp-fresh-preflight.js +2 -2
  25. package/mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-fresh-preflight.js +1 -1
  26. package/mcp/entwurf-bridge/dist/pi-extensions/lib/remote-pair-receive.js +25 -17
  27. package/mcp/entwurf-bridge/dist/pi-extensions/lib/remote-pair-send.js +101 -32
  28. package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-omp.js +57 -7
  29. package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-pi-durable.js +23 -45
  30. package/mcp/entwurf-bridge/dist/scripts/remote-pair-receive.js +12 -12
  31. package/mcp/entwurf-bridge/src/index.ts +126 -321
  32. package/native/win32-x64/entwurf_win.node +0 -0
  33. package/native/win32-x64/manifest.json +2 -2
  34. package/package.json +5 -4
  35. package/pi-extensions/lib/agent-guidance.ts +341 -0
  36. package/pi-extensions/lib/entwurf-self-address.ts +13 -0
  37. package/pi-extensions/lib/entwurf-v2-contract.ts +25 -2
  38. package/pi-extensions/lib/entwurf-v2-decider.ts +7 -0
  39. package/pi-extensions/lib/entwurf-v2-surface.ts +27 -30
  40. package/pi-extensions/lib/entwurf-v2-visible-resume.ts +15 -16
  41. package/pi-extensions/lib/fresh-call-composition.ts +21 -10
  42. package/pi-extensions/lib/herdr-fresh-call.ts +4 -2
  43. package/pi-extensions/lib/meta-sender-identity.ts +4 -0
  44. package/pi-extensions/lib/mux-fresh-call.ts +29 -30
  45. package/pi-extensions/lib/mux-resume-call.ts +18 -19
  46. package/pi-extensions/lib/native-push/register.ts +17 -2
  47. package/pi-extensions/lib/omp-fresh-preflight.ts +2 -2
  48. package/pi-extensions/lib/pi-durable-fresh-preflight.ts +1 -1
  49. package/pi-extensions/lib/remote-pair-receive.ts +27 -19
  50. package/pi-extensions/lib/remote-pair-send.ts +124 -34
  51. package/pi-extensions/meta-bridge-omp.ts +69 -7
  52. package/pi-extensions/meta-bridge-pi-durable.ts +23 -64
  53. package/run.sh +182 -22
  54. package/scripts/build-bridge.sh +10 -0
  55. package/scripts/check-agy-sender-identity.ts +6 -4
  56. package/scripts/check-capability-bundle-reach.ts +86 -0
  57. package/scripts/check-entwurf-self-address.ts +37 -6
  58. package/scripts/check-entwurf-v2-contract.ts +24 -2
  59. package/scripts/check-entwurf-v2-production.ts +45 -31
  60. package/scripts/check-entwurf-v2-surface.ts +94 -98
  61. package/scripts/check-gate-qualification.ts +17 -13
  62. package/scripts/check-mux-resume-call.ts +20 -3
  63. package/scripts/check-native-push-adapter.ts +4 -0
  64. package/scripts/check-native-push-register.ts +32 -82
  65. package/scripts/check-remote-pair-installed.ts +2 -2
  66. package/scripts/dev-bin.sh +112 -1
  67. package/scripts/lib/chain-terminus-publication.ts +58 -0
  68. package/scripts/lib/codex-fresh-live-protocol.ts +30 -0
  69. package/scripts/lib/codex-fresh-source-receipts.ts +112 -3
  70. package/scripts/lib/pi-durable-fresh-live-receipts.ts +29 -1
  71. package/scripts/mutants/agent-guidance.json +160 -0
  72. package/scripts/mutants/bridge-boot-resume.json +6 -8
  73. package/scripts/mutants/capability-bundle-reach.json +124 -0
  74. package/scripts/mutants/chain-terminus-publication.json +17 -0
  75. package/scripts/mutants/codex-native.json +98 -0
  76. package/scripts/mutants/control-send-receipt.json +23 -6
  77. package/scripts/mutants/mux-fresh-call.json +30 -4
  78. package/scripts/mutants/mux-resume-call.json +28 -3
  79. package/scripts/mutants/native-push-paused.json +17 -0
  80. package/scripts/mutants/omp-fresh.json +13 -2
  81. package/scripts/mutants/pack-install.json +46 -0
  82. package/scripts/mutants/pi-durable.json +17 -5
  83. package/scripts/mutants/remote-pair-display.json +3 -3
  84. package/scripts/mutants/remote-pair-receive.json +41 -11
  85. package/scripts/mutants/remote-pair-roundtrip.json +67 -7
  86. package/scripts/mutants/remote-pair-send.json +177 -19
  87. package/scripts/mutants/remote-pair-transport.json +4 -4
  88. package/scripts/mutants/self-address.json +10 -10
  89. package/scripts/mutants/source-install.json +66 -0
  90. package/scripts/mutants/v2-surface.json +50 -64
  91. package/scripts/mutants/v2-visible-resume.json +11 -0
  92. package/scripts/remote-pair-receive.ts +12 -12
  93. package/scripts/smoke-agy-install-state.sh +121 -0
  94. package/scripts/smoke-agy-native-push-live.ts +13 -0
  95. package/scripts/smoke-codex-fresh-live.ts +50 -27
  96. package/scripts/smoke-entwurf-chain-live.ts +7 -9
  97. package/scripts/smoke-herdr-fresh-call-live.ts +4 -4
  98. package/scripts/smoke-herdr-plugin-build-live.ts +179 -50
  99. package/scripts/smoke-mux-lifecycle-live.ts +140 -8
  100. package/scripts/smoke-omp-fresh-live.ts +5 -4
  101. package/scripts/smoke-pi-durable-fresh-live.ts +29 -7
  102. package/scripts/smoke-setup-verdict.sh +27 -2
@@ -69,7 +69,9 @@
69
69
  "claim": "FRESHCALL-CALLBACK-PRECEDES-TASK",
70
70
  "title": "the task is framed before the callback instruction, so a sibling that stalls in the task never names itself",
71
71
  "subject": "pi-extensions/lib/fresh-call-composition.ts",
72
- "find": ["\t\t\t\t\t`FIRST ACTION, before reading files or anything else: call ${birthTool} with no arguments.`"],
72
+ "find": [
73
+ "\t\t\t\t\t`BEFORE THE TASK, and before reading files or running commands: call ${birthTool} with no arguments.`"
74
+ ],
73
75
  "replace": ["\t\t\t\t\t`FINAL ACTION, after completing the task: call ${birthTool} with no arguments.`"],
74
76
  "gate": ["bash", "run.sh", "check-mux-fresh-call"],
75
77
  "timeoutSeconds": 120,
@@ -125,7 +127,7 @@
125
127
  "title": "the fresh-call surface drops the optional cwd from its schema, so cross-repo fresh silently cannot exist from any caller — pi included, which reaches this bridge since #125",
126
128
  "subject": "mcp/entwurf-bridge/src/index.ts",
127
129
  "find": [
128
- "\t\tcwd: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t\"Optional literal ABSOLUTE path of an existing directory to start the sibling in (cross-repo fresh). Omit or pass \\\"\\\" to start where the CALLER is — this agent's own cwd on BOTH rails, or for a Codex caller its own record directory, because this bridge is the app-server's child and that process's directory is not the caller's. Taken exactly as given: no trim, no realpath, no project-name resolution. '#' is refused on the tmux rail only, because tmux format-expands a start directory; inside herdr it is an ordinary path character. The receipt echoes the directory that was REQUESTED or the caller record it came from, never an observation of where the pane landed.\",\n\t\t\t),\n"
130
+ "\t\tcwd: z.string().optional().describe(ENTWURF_TOOL_GUIDANCE.entwurf_fresh_call.parameters[\"cwd\"]),\n"
129
131
  ],
130
132
  "replace": [""],
131
133
  "gate": ["bash", "run.sh", "check-mux-fresh-call"],
@@ -151,7 +153,7 @@
151
153
  "title": "MCP drops the required model field from fresh-call while the composition still expects one",
152
154
  "subject": "mcp/entwurf-bridge/src/index.ts",
153
155
  "find": [
154
- "\t\tmodel: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(200)\n\t\t\t// The `\\[` below is USELESS TO JS AND LOAD-BEARING TO THE HOST. zod emits this source\n\t\t\t// text verbatim as the JSON Schema `pattern`, and the host's tool-schema validator is a\n\t\t\t// Rust regex engine that rejects an unescaped `[` inside a character class as \"unclosed\n\t\t\t// character class\" — a 400 on tools/list that stops EVERY Claude session from opening\n\t\t\t// (7-M-fix). Biome's safe fix removes it; do not accept that fix. The suppression must\n\t\t\t// stay on the line DIRECTLY above the expression, or it attaches to nothing and reads\n\t\t\t// as an unused suppression while the escape goes back to being fixable.\n\t\t\t// biome-ignore lint/complexity/noUselessEscapeInRegex: emitted to a Rust regex validator, see above\n\t\t\t.regex(/^[A-Za-z0-9][A-Za-z0-9._/:\\[\\]-]*$/)\n\t\t\t.describe(\n\t\t\t\t\"Required runtime model: canonical provider/model for pi (a bare alias is refused), provider/<exact catalog model id> for pi-durable, a Claude Code model id/alias, or a Copilot/OMP/Codex model name. Neither pi nor pi-durable may name the openrouter provider.\",\n\t\t\t),\n"
156
+ "\t\tmodel: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(200)\n\t\t\t// The `\\[` below is USELESS TO JS AND LOAD-BEARING TO THE HOST. zod emits this source\n\t\t\t// text verbatim as the JSON Schema `pattern`, and the host's tool-schema validator is a\n\t\t\t// Rust regex engine that rejects an unescaped `[` inside a character class as \"unclosed\n\t\t\t// character class\" — a 400 on tools/list that stops EVERY Claude session from opening\n\t\t\t// (7-M-fix). Biome's safe fix removes it; do not accept that fix. The suppression must\n\t\t\t// stay on the line DIRECTLY above the expression, or it attaches to nothing and reads\n\t\t\t// as an unused suppression while the escape goes back to being fixable.\n\t\t\t// biome-ignore lint/complexity/noUselessEscapeInRegex: emitted to a Rust regex validator, see above\n\t\t\t.regex(/^[A-Za-z0-9][A-Za-z0-9._/:\\[\\]-]*$/)\n\t\t\t.describe(ENTWURF_TOOL_GUIDANCE.entwurf_fresh_call.parameters[\"model\"]),\n"
155
157
  ],
156
158
  "replace": [""],
157
159
  "gate": ["bash", "run.sh", "check-mux-fresh-call"],
@@ -512,7 +514,7 @@
512
514
  "title": "the fresh-call surface drops the optional seat from its schema, so opening a sibling in the operator's project session silently cannot exist from any caller",
513
515
  "subject": "mcp/entwurf-bridge/src/index.ts",
514
516
  "find": [
515
- "\t\tplacement: z\n\t\t\t.object({\n\t\t\t\ttmuxSession: z\n\t\t\t\t\t.string()\n\t\t\t\t\t.describe(\n\t\t\t\t\t\t\"EXACT name of an EXISTING session on this agent's own tmux server. Nothing is created: an absent session is refused as tmux-session-missing, and a name outside [A-Za-z0-9][A-Za-z0-9_-]* as tmux-session-name-invalid.\",\n\t\t\t\t\t),\n\t\t\t})\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t\"Optional expert seat override, TMUX ONLY: open the sibling in ONE EXISTING tmux session of this agent's own server, and it always wins. When omitted the seat follows the CALLER, never the backend being opened: a Codex CALLER opens beside its own TUI pane, matched by thread-id in that pane's terminal title (0 or 2+ matching panes refuse, never fall back); every other caller opens in its own session. A pane title is a placement input only — never an address, liveness or delivery fact. Nothing is ever created. Inside herdr this field is refused by name — placement there belongs to herdr, and a tmux session name would silently place the sibling somewhere else. Independent of cwd; neither is inferred from the other. The receipt reports the selected name (absent for the caller-pane rule, which observed a session rather than requesting a name), its source, and resolved target session id.\",\n\t\t\t),\n"
517
+ "\t\tplacement: z\n\t\t\t.object({\n\t\t\t\ttmuxSession: z.string().describe(ENTWURF_TOOL_GUIDANCE.entwurf_fresh_call.parameters[\"placement.tmuxSession\"]),\n\t\t\t})\n\t\t\t.optional()\n\t\t\t.describe(ENTWURF_TOOL_GUIDANCE.entwurf_fresh_call.parameters[\"placement\"]),\n"
516
518
  ],
517
519
  "replace": [""],
518
520
  "gate": ["bash", "run.sh", "check-mux-fresh-call"],
@@ -766,6 +768,30 @@
766
768
  "timeoutSeconds": 60,
767
769
  "signature": "[QK:PIIMPORT-CARRIER-FENCE]",
768
770
  "signatureSource": "run.sh"
771
+ },
772
+ {
773
+ "claim": "PD-FRESH-LIVE-BOOTSTRAP-ORDER",
774
+ "title": "a pd call with no joined result read as COMPLETED instead of pending, so a callback whose result the native store never recorded passes pd's bootstrap order",
775
+ "subject": "scripts/lib/pi-durable-fresh-live-receipts.ts",
776
+ "find": ["\t\tstatus: call.result === null ? \"pending\" : call.result.isError ? \"failed\" : \"completed\","],
777
+ "replace": [
778
+ "\t\tstatus: call.result === null ? \"completed\" : call.result.isError ? \"failed\" : \"completed\","
779
+ ],
780
+ "gate": ["bash", "run.sh", "check-mux-fresh-call"],
781
+ "timeoutSeconds": 120,
782
+ "signature": "[QK:PD-FRESH-LIVE-BOOTSTRAP-ORDER]",
783
+ "signatureSource": "test/pi-durable-fresh-live-receipts.test.ts"
784
+ },
785
+ {
786
+ "claim": "FRESHCALL-LAUNCH-REJECT-HINTS-FROM-ROOT",
787
+ "title": "one static tmux launch-stage hint is inlined as a literal one byte away from the Root text, so the reader and its byte oracle stay green and only the rendered refusal can disagree with Root",
788
+ "subject": "pi-extensions/lib/mux-fresh-call.ts",
789
+ "find": ["\t\"anchor-malformed\": ENTWURF_FRESH_LAUNCH_REJECT_HINTS[\"anchor-malformed\"],"],
790
+ "replace": ["\t\"anchor-malformed\": \"TMUX_PANE is not a native pane id\","],
791
+ "gate": ["bash", "run.sh", "check-mux-fresh-call"],
792
+ "timeoutSeconds": 120,
793
+ "signature": "[QK:FRESHCALL-LAUNCH-REJECT-HINTS-FROM-ROOT]",
794
+ "signatureSource": "test/mux-fresh-call.test.ts"
769
795
  }
770
796
  ]
771
797
  }
@@ -98,12 +98,12 @@
98
98
  {
99
99
  "claim": "MUXRESUME-DESC-CONTRACT-MCP",
100
100
  "title": "the MCP description drops the dormant-only scope, inviting callers to aim the verb at live citizens it will refuse",
101
- "subject": "mcp/entwurf-bridge/src/index.ts",
101
+ "subject": "ENTWURF.json",
102
102
  "find": [
103
- "\t\"Reopen ONE DORMANT pi or Claude Code citizen under its OWN garden id, in a visible window in the operator's \" +"
103
+ "Only on an explicit operator request, reopen ONE DORMANT pi or Claude Code citizen under its OWN garden id, in a visible window in the operator's "
104
104
  ],
105
105
  "replace": [
106
- "\t\"Reopen ONE pi or Claude Code citizen under its OWN garden id, in a visible window in the operator's \" +"
106
+ "Only on an explicit operator request, reopen ONE pi or Claude Code citizen under its OWN garden id, in a visible window in the operator's "
107
107
  ],
108
108
  "gate": ["bash", "run.sh", "check-mux-resume-call"],
109
109
  "timeoutSeconds": 60,
@@ -155,6 +155,31 @@
155
155
  "timeoutSeconds": 60,
156
156
  "signature": "[QK:MUXRESUME-ARGV-IDENTITY-SCRUB]",
157
157
  "signatureSource": "scripts/check-mux-resume-call.ts"
158
+ },
159
+ {
160
+ "claim": "MUXRESUME-LAUNCH-REJECT-HINTS-FROM-ROOT",
161
+ "title": "one launch-stage resume hint is inlined as a literal one byte away from the Root text, so the reader and its byte oracle stay green and only the table-to-Root wiring can disagree",
162
+ "subject": "pi-extensions/lib/mux-resume-call.ts",
163
+ "find": ["\t\"anchor-malformed\": ENTWURF_RESUME_LAUNCH_REJECT_HINTS[\"anchor-malformed\"],"],
164
+ "replace": ["\t\"anchor-malformed\": \"TMUX_PANE is not a native pane id\","],
165
+ "gate": ["bash", "run.sh", "check-mux-resume-call"],
166
+ "timeoutSeconds": 60,
167
+ "signature": "[QK:MUXRESUME-LAUNCH-REJECT-HINTS-FROM-ROOT]",
168
+ "signatureSource": "scripts/check-mux-resume-call.ts"
169
+ },
170
+ {
171
+ "claim": "MUXRESUME-IMPORT-SET",
172
+ "title": "the resume composition gains a bare side-effect import, the weakest static form, which loads and runs fine, so only the exact import-set parser can see the new lane",
173
+ "subject": "pi-extensions/lib/mux-resume-call.ts",
174
+ "find": ["import { ENTWURF_RESUME_LAUNCH_REJECT_HINTS } from \"./agent-guidance.ts\";"],
175
+ "replace": [
176
+ "import { ENTWURF_RESUME_LAUNCH_REJECT_HINTS } from \"./agent-guidance.ts\";",
177
+ "import \"node:path\";"
178
+ ],
179
+ "gate": ["bash", "run.sh", "check-mux-resume-call"],
180
+ "timeoutSeconds": 60,
181
+ "signature": "[QK:MUXRESUME-IMPORT-SET]",
182
+ "signatureSource": "scripts/check-mux-resume-call.ts"
158
183
  }
159
184
  ]
160
185
  }
@@ -0,0 +1,17 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "lane": "native-push-paused",
4
+ "mutants": [
5
+ {
6
+ "claim": "AGY-NATIVE-PUSH-PAUSED",
7
+ "title": "the paused native-push backend set is emptied, so agy dispatch, sender replyability and register_native reach the agy language server again, whose measured external callers were refused for a missing CSRF token (#128, #152)",
8
+ "subject": "pi-extensions/lib/entwurf-v2-contract.ts",
9
+ "find": ["export const NATIVE_PUSH_PAUSED_BACKENDS: readonly NativePushBackend[] = [\"antigravity\"];"],
10
+ "replace": ["export const NATIVE_PUSH_PAUSED_BACKENDS: readonly NativePushBackend[] = [];"],
11
+ "gate": ["bash", "run.sh", "check-native-push-paused"],
12
+ "timeoutSeconds": 120,
13
+ "signature": "[QK:AGY-NATIVE-PUSH-PAUSED]",
14
+ "signatureSource": "pi-extensions/lib/native-push/paused.test.ts"
15
+ }
16
+ ]
17
+ }
@@ -169,8 +169,8 @@
169
169
  "claim": "OMP-BOOTSTRAP-CALLBACK-ONLY",
170
170
  "title": "the task folded back into the stage-one prompt, so the sibling is asked to call back AND to work in one message. This is the prompt the failed LIVE actually ran, minus the missing tool: a competing goal beside the callback is what the two-stage split exists to remove, and nothing about argv or readiness would notice it coming back",
171
171
  "subject": "pi-extensions/meta-bridge-omp.ts",
172
- "find": ["\t\t\tsend(buildOmpCallbackOnlyPrompt());"],
173
- "replace": ["\t\t\tsend(`${buildOmpCallbackOnlyPrompt()}\\n\\n${payload.task}`);"],
172
+ "find": ["\t\t\tsend(buildOmpCallbackOnlyPrompt(opts.callerGardenId));"],
173
+ "replace": ["\t\t\tsend(`${buildOmpCallbackOnlyPrompt(opts.callerGardenId)}\\n\\n${payload.task}`);"],
174
174
  "gate": ["bash", "run.sh", "check-mux-fresh-call"],
175
175
  "timeoutSeconds": 300,
176
176
  "signature": "[QK:OMP-BOOTSTRAP-CALLBACK-ONLY]",
@@ -295,6 +295,17 @@
295
295
  "timeoutSeconds": 300,
296
296
  "signature": "[QK:OMP-BOOTSTRAP-EPOCH-ENDS-BEFORE-ENVELOPE]",
297
297
  "signatureSource": "test/omp-fresh-bootstrap.contract.test.ts"
298
+ },
299
+ {
300
+ "claim": "OMP-BOOTSTRAP-CALLER-TARGET-REQUIRED",
301
+ "title": "a requested omp bootstrap with no well-formed caller target proceeds anyway — the stage silently drops the caller check (or names `undefined` as the caller) and the task is still released, instead of a named refusal",
302
+ "subject": "pi-extensions/meta-bridge-omp.ts",
303
+ "find": ["\tif (!caller.ok) {"],
304
+ "replace": ["\tif (false && !caller.ok) {"],
305
+ "gate": ["bash", "run.sh", "check-mux-fresh-call"],
306
+ "timeoutSeconds": 300,
307
+ "signature": "[QK:OMP-BOOTSTRAP-CALLER-TARGET-REQUIRED]",
308
+ "signatureSource": "test/omp-fresh-bootstrap.contract.test.ts"
298
309
  }
299
310
  ]
300
311
  }
@@ -60,6 +60,52 @@
60
60
  "timeoutSeconds": 120,
61
61
  "signature": "[QK:WIN32-PACK-DIGEST]",
62
62
  "signatureSource": "run.sh"
63
+ },
64
+ {
65
+ "claim": "PACK-INSTALL-ARGV-LEAK-CAUGHT",
66
+ "title": "the dispatcher-verb refusal goes back to the retired verdict, `printf | grep -qF` under pipefail refusing only when that pipeline exits 0, so a leak ahead of more output SIGPIPEs the producer and the cell passes \u2014 killed deterministically only because the self-test front-loads the leak ahead of ~260 KB, past the default 64 KiB pipe buffer (its teeth control measures that); on the real small stand-in output the same restoration would survive",
67
+ "subject": "run.sh",
68
+ "find": [" grep -qF -- \"$token\" <<<\"$out\" || argv_search_rc=$?"],
69
+ "replace": [" printf '%s' \"$out\" | grep -qF -- \"$token\" || argv_search_rc=1"],
70
+ "gate": ["bash", "run.sh", "check-pack-argv-matcher"],
71
+ "timeoutSeconds": 120,
72
+ "signature": "[QK:PACK-INSTALL-ARGV-LEAK-CAUGHT]",
73
+ "signatureSource": "run.sh"
74
+ },
75
+ {
76
+ "claim": "PACK-INSTALL-ARGV-CLEAN-PASSES",
77
+ "title": "the dispatcher-verb refusal stops accepting a clean search (grep rc=1), so every installed copilot / codex-app-server launch is refused, silently",
78
+ "subject": "run.sh",
79
+ "find": [" 1) return 0 ;;"],
80
+ "replace": [" 1) ;;"],
81
+ "gate": ["bash", "run.sh", "check-pack-argv-matcher"],
82
+ "timeoutSeconds": 120,
83
+ "signature": "[QK:PACK-INSTALL-ARGV-CLEAN-PASSES]",
84
+ "signatureSource": "run.sh"
85
+ },
86
+ {
87
+ "claim": "PACK-INSTALL-ARGV-SEARCH-ERROR-REFUSED",
88
+ "title": "a failed dispatcher-verb search (grep rc=2) is accepted as clean, so a search that never ran lets the leak refusal pass",
89
+ "subject": "run.sh",
90
+ "find": [
91
+ " *) fail \"[check-pack-install] installed 'entwurf $label': the dispatcher-verb search itself failed (rc=$argv_search_rc) \u2014 refused, never read as no leak\" ;;"
92
+ ],
93
+ "replace": [" *) return 0 ;;"],
94
+ "gate": ["bash", "run.sh", "check-pack-argv-matcher"],
95
+ "timeoutSeconds": 120,
96
+ "signature": "[QK:PACK-INSTALL-ARGV-SEARCH-ERROR-REFUSED]",
97
+ "signatureSource": "run.sh"
98
+ },
99
+ {
100
+ "claim": "PACK-INSTALL-ARGV-CELLS-BOUND",
101
+ "title": "the copilot leak refusal of check-pack-install goes back to its own `printf | grep -qF` instead of returning through the proved verdict, so the self-test stays green while the heavy cell regains the SIGPIPE window (the codex-app-server call is held by the same text check, with no mutant of its own)",
102
+ "subject": "run.sh",
103
+ "find": [" pack_install_refuse_argv_leak copilot \"$op_out\" 'ARG<copilot>' || return 1"],
104
+ "replace": [" if printf '%s' \"$op_out\" | grep -qF 'ARG<copilot>'; then return 1; fi"],
105
+ "gate": ["bash", "run.sh", "check-pack-argv-matcher"],
106
+ "timeoutSeconds": 120,
107
+ "signature": "[QK:PACK-INSTALL-ARGV-CELLS-BOUND]",
108
+ "signatureSource": "run.sh"
63
109
  }
64
110
  ]
65
111
  }
@@ -2,6 +2,20 @@
2
2
  "schemaVersion": 1,
3
3
  "lane": "pi-durable",
4
4
  "mutants": [
5
+ {
6
+ "claim": "PI-DURABLE-DESCRIPTION-DRIFT-REFUSED",
7
+ "title": "schema comparison ignores description-only drift while still comparing structural schema fields",
8
+ "subject": "pi-extensions/meta-bridge-pi-durable.ts",
9
+ "find": ["canonicalJson(found.inputSchema) !== canonicalJson(PI_DURABLE_TOOL_SCHEMAS[name])"],
10
+ "replace": [
11
+ "canonicalJson(JSON.parse(JSON.stringify(found.inputSchema, (key, value) => key === \"description\" ? undefined : value))) !== canonicalJson(JSON.parse(JSON.stringify(PI_DURABLE_TOOL_SCHEMAS[name], (key, value) => key === \"description\" ? undefined : value)))"
12
+ ],
13
+ "gate": ["bash", "run.sh", "check-pi-durable-beside"],
14
+ "timeoutSeconds": 300,
15
+ "signature": "[QK:PI-DURABLE-DESCRIPTION-DRIFT-REFUSED]",
16
+ "signatureSource": "pi-extensions/meta-bridge-pi-durable.test.ts",
17
+ "build": { "argv": ["bash", "scripts/build-bridge.sh"], "output": "mcp/entwurf-bridge/dist" }
18
+ },
5
19
  {
6
20
  "claim": "PI-DURABLE-RETIRE-CALL",
7
21
  "title": "retirement alone drops the actual request cancellation signal",
@@ -24,13 +38,11 @@
24
38
  {
25
39
  "claim": "PI-DURABLE-RETIRE-POLICY",
26
40
  "title": "the native retirement description treats actor identity as a standing grant",
27
- "subject": "pi-extensions/meta-bridge-pi-durable.ts",
41
+ "subject": "ENTWURF.json",
28
42
  "find": [
29
- "\t\t\"Only when the human operator explicitly requested this particular target/work: make ONE retirement attempt for that request \" +"
30
- ],
31
- "replace": [
32
- "\t\t\"A caller record alone authorizes this target/work: make ONE retirement attempt for that request \" +"
43
+ "Only when the human operator explicitly requested this particular target/work: make ONE retirement attempt for that request "
33
44
  ],
45
+ "replace": ["A caller record alone authorizes this target/work: make ONE retirement attempt for that request "],
34
46
  "gate": ["bash", "run.sh", "check-pi-durable-beside"],
35
47
  "timeoutSeconds": 300,
36
48
  "signature": "[QK:PI-DURABLE-RETIRE-POLICY]",
@@ -10,7 +10,7 @@
10
10
  "replace": [
11
11
  "\tconst remote = !replyable ? (sender.remote ?? { via: \"?\", replyRoute: \"none\" as const }) : undefined;"
12
12
  ],
13
- "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
13
+ "gate": ["bash", "run.sh", "check-remote-pair-beside"],
14
14
  "timeoutSeconds": 600,
15
15
  "signature": "[QK:REMOTE-DISPLAY-LOCAL-UNCHANGED]",
16
16
  "signatureSource": "pi-extensions/lib/meta-mailbox-body.test.ts",
@@ -25,7 +25,7 @@
25
25
  "subject": "pi-extensions/lib/meta-mailbox-body.ts",
26
26
  "find": ["\"reply route declared, not probed\""],
27
27
  "replace": ["\"reply via entwurf_v2 to this sessionId\""],
28
- "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
28
+ "gate": ["bash", "run.sh", "check-remote-pair-beside"],
29
29
  "timeoutSeconds": 600,
30
30
  "signature": "[QK:REMOTE-DISPLAY-SESSIONLINE]",
31
31
  "signatureSource": "pi-extensions/lib/meta-mailbox-body.test.ts",
@@ -40,7 +40,7 @@
40
40
  "subject": "pi-extensions/lib/meta-mailbox-body.ts",
41
41
  "find": ["\tconst where = remote ? `${sender.cwd} (sender-host path)` : abbreviateHome(sender.cwd);"],
42
42
  "replace": ["\tconst where = abbreviateHome(sender.cwd);"],
43
- "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
43
+ "gate": ["bash", "run.sh", "check-remote-pair-beside"],
44
44
  "timeoutSeconds": 600,
45
45
  "signature": "[QK:REMOTE-DISPLAY-SENDER-HOST-CWD]",
46
46
  "signatureSource": "pi-extensions/lib/meta-mailbox-body.test.ts",
@@ -6,9 +6,9 @@
6
6
  "claim": "REMOTE-PAIR-FROM-OUTSIDE-NO-MUTATION",
7
7
  "title": "the receiving entry stops checking the claimed sender against the declared pair, so any gid the SSH account writes is dispatched into the declared target's mailbox",
8
8
  "subject": "pi-extensions/lib/remote-pair-receive.ts",
9
- "find": ["\tif (claim.from !== decl.remoteGardenId) {"],
9
+ "find": ["\tif (fromRoutes.length === 0) {"],
10
10
  "replace": ["\tif (false) {"],
11
- "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
11
+ "gate": ["bash", "run.sh", "check-remote-pair-beside"],
12
12
  "timeoutSeconds": 600,
13
13
  "signature": "[QK:REMOTE-PAIR-FROM-OUTSIDE-NO-MUTATION]",
14
14
  "signatureSource": "pi-extensions/lib/remote-pair-receive.test.ts",
@@ -21,9 +21,9 @@
21
21
  "claim": "REMOTE-PAIR-TARGET-OUTSIDE-NO-MUTATION",
22
22
  "title": "the receiving entry stops checking the claimed target against the declared pair, so a declared remote can reach every deliverable citizen on this host",
23
23
  "subject": "pi-extensions/lib/remote-pair-receive.ts",
24
- "find": ["\tif (claim.target !== decl.localGardenId) {"],
25
- "replace": ["\tif (false) {"],
26
- "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
24
+ "find": ["\tconst decl = fromRoutes.find((d) => d.localGardenId === claim.target);"],
25
+ "replace": ["\tconst decl = fromRoutes.find((d) => d.localGardenId === claim.target) ?? fromRoutes[0];"],
26
+ "gate": ["bash", "run.sh", "check-remote-pair-beside"],
27
27
  "timeoutSeconds": 600,
28
28
  "signature": "[QK:REMOTE-PAIR-TARGET-OUTSIDE-NO-MUTATION]",
29
29
  "signatureSource": "pi-extensions/lib/remote-pair-receive.test.ts",
@@ -38,7 +38,7 @@
38
38
  "subject": "pi-extensions/lib/remote-pair-receive.ts",
39
39
  "find": ["\t\tsessionId: claim.from,"],
40
40
  "replace": ["\t\tsessionId: process.env.PI_SESSION_ID ?? claim.from,"],
41
- "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
41
+ "gate": ["bash", "run.sh", "check-remote-pair-beside"],
42
42
  "timeoutSeconds": 600,
43
43
  "signature": "[QK:REMOTE-PAIR-IGNORES-AMBIENT-SENDER]",
44
44
  "signatureSource": "pi-extensions/lib/remote-pair-receive.test.ts",
@@ -53,7 +53,7 @@
53
53
  "subject": "pi-extensions/lib/remote-pair-receive.ts",
54
54
  "find": ["\t\treplyable: false,"],
55
55
  "replace": ["\t\treplyable: true,"],
56
- "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
56
+ "gate": ["bash", "run.sh", "check-remote-pair-beside"],
57
57
  "timeoutSeconds": 600,
58
58
  "signature": "[QK:REMOTE-PAIR-MATCH-DISPATCHES-ONCE]",
59
59
  "signatureSource": "pi-extensions/lib/remote-pair-receive.test.ts",
@@ -68,7 +68,7 @@
68
68
  "subject": "pi-extensions/lib/remote-pair-receive.ts",
69
69
  "find": ["\tif (got.length !== want.length || got.some((k, i) => k !== want[i])) {"],
70
70
  "replace": ["\tif (false) {"],
71
- "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
71
+ "gate": ["bash", "run.sh", "check-remote-pair-beside"],
72
72
  "timeoutSeconds": 600,
73
73
  "signature": "[QK:REMOTE-PAIR-CLAIM-EXACT-SHAPE]",
74
74
  "signatureSource": "pi-extensions/lib/remote-pair-receive.test.ts",
@@ -83,7 +83,7 @@
83
83
  "subject": "scripts/remote-pair-receive.ts",
84
84
  "find": ["emit(receipt, receipt.kind === \"rejected\" ? 3 : receipt.isError ? 4 : 0);"],
85
85
  "replace": ["emit(receipt, receipt.kind === \"rejected\" ? 0 : receipt.isError ? 4 : 0);"],
86
- "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
86
+ "gate": ["bash", "run.sh", "check-remote-pair-beside"],
87
87
  "timeoutSeconds": 600,
88
88
  "signature": "[QK:REMOTE-PAIR-ENTRY-REFUSES-OUTSIDE]",
89
89
  "signatureSource": "pi-extensions/lib/remote-pair-receive.test.ts",
@@ -98,7 +98,7 @@
98
98
  "subject": "scripts/remote-pair-receive.ts",
99
99
  "find": ["\tif (!path.isAbsolute(pairFile) || path.normalize(pairFile) !== own) {"],
100
100
  "replace": ["\tif (false) {"],
101
- "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
101
+ "gate": ["bash", "run.sh", "check-remote-pair-beside"],
102
102
  "timeoutSeconds": 600,
103
103
  "signature": "[QK:REMOTE-PAIR-ENTRY-USAGE]",
104
104
  "signatureSource": "pi-extensions/lib/remote-pair-receive.test.ts",
@@ -113,7 +113,7 @@
113
113
  "subject": "scripts/remote-pair-receive.ts",
114
114
  "find": ["\treplyRoute: \"declared\","],
115
115
  "replace": ["\treplyRoute: \"none\","],
116
- "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
116
+ "gate": ["bash", "run.sh", "check-remote-pair-beside"],
117
117
  "timeoutSeconds": 600,
118
118
  "signature": "[QK:REMOTE-PAIR-ENTRY-REPLY-ROUTE-DECLARED]",
119
119
  "signatureSource": "pi-extensions/lib/remote-pair-receive.test.ts",
@@ -121,6 +121,36 @@
121
121
  "argv": ["bash", "scripts/build-bridge.sh"],
122
122
  "output": "mcp/entwurf-bridge/dist"
123
123
  }
124
+ },
125
+ {
126
+ "claim": "REMOTE-PAIR-CROSS-PAIR-REFUSED",
127
+ "title": "the two ends are checked as independent memberships, so a sender declared in one route reaches the local citizen of another route",
128
+ "subject": "pi-extensions/lib/remote-pair-receive.ts",
129
+ "find": ["\tconst decl = fromRoutes.find((d) => d.localGardenId === claim.target);"],
130
+ "replace": ["\tconst decl = decls.some((d) => d.localGardenId === claim.target) ? fromRoutes[0] : undefined;"],
131
+ "gate": ["bash", "run.sh", "check-remote-pair-beside"],
132
+ "timeoutSeconds": 600,
133
+ "signature": "[QK:REMOTE-PAIR-CROSS-PAIR-REFUSED]",
134
+ "signatureSource": "pi-extensions/lib/remote-pair-receive.test.ts",
135
+ "build": {
136
+ "argv": ["bash", "scripts/build-bridge.sh"],
137
+ "output": "mcp/entwurf-bridge/dist"
138
+ }
139
+ },
140
+ {
141
+ "claim": "REMOTE-PAIR-SELECTED-ROUTE-LABEL",
142
+ "title": "the body is labelled with the first declared route, so a message that came over one host is shown as arriving over another",
143
+ "subject": "pi-extensions/lib/remote-pair-receive.ts",
144
+ "find": ["\tconst sender = buildRemoteSenderEnvelope(claim, picked.decl,"],
145
+ "replace": ["\tconst sender = buildRemoteSenderEnvelope(claim, decls[0],"],
146
+ "gate": ["bash", "run.sh", "check-remote-pair-beside"],
147
+ "timeoutSeconds": 600,
148
+ "signature": "[QK:REMOTE-PAIR-SELECTED-ROUTE-LABEL]",
149
+ "signatureSource": "pi-extensions/lib/remote-pair-receive.test.ts",
150
+ "build": {
151
+ "argv": ["bash", "scripts/build-bridge.sh"],
152
+ "output": "mcp/entwurf-bridge/dist"
153
+ }
124
154
  }
125
155
  ]
126
156
  }
@@ -6,9 +6,9 @@
6
6
  "claim": "REMOTE-ROUNDTRIP-A-TO-B",
7
7
  "title": "the bridge never reads the opt-in, so a declared remote target falls through to the local decider as bad-target",
8
8
  "subject": "mcp/entwurf-bridge/src/index.ts",
9
- "find": ["\t\t\t\t\troute: readRemotePairOptIn(remotePairOptInPath()),"],
10
- "replace": ["\t\t\t\t\troute: null,"],
11
- "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
9
+ "find": ["\t\t\t\t\toptIn: readRemotePairOptIn(remotePairOptInPath()),"],
10
+ "replace": ["\t\t\t\t\toptIn: null,"],
11
+ "gate": ["bash", "run.sh", "check-remote-pair-beside"],
12
12
  "timeoutSeconds": 600,
13
13
  "signature": "[QK:REMOTE-ROUNDTRIP-A-TO-B]",
14
14
  "signatureSource": "pi-extensions/lib/remote-pair-roundtrip.test.ts",
@@ -25,7 +25,7 @@
25
25
  "replace": [
26
26
  "\t\t\t\t\ttransport: { run: async () => ({ exitCode: 1, signal: null, timedOut: false, stdout: \"\", stderr: \"\" }) },"
27
27
  ],
28
- "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
28
+ "gate": ["bash", "run.sh", "check-remote-pair-beside"],
29
29
  "timeoutSeconds": 600,
30
30
  "signature": "[QK:REMOTE-ROUNDTRIP-REVERSE-DISPATCH]",
31
31
  "signatureSource": "pi-extensions/lib/remote-pair-roundtrip.test.ts",
@@ -40,7 +40,7 @@
40
40
  "subject": "mcp/entwurf-bridge/src/index.ts",
41
41
  "find": ["\t\t\t\t\trunLocal: (params, opts) => runAndRenderEntwurfV2FromSurface(params, opts),"],
42
42
  "replace": ["\t\t\t\t\trunLocal: async () => ({ isError: false, text: \"local skipped\" }),"],
43
- "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
43
+ "gate": ["bash", "run.sh", "check-remote-pair-beside"],
44
44
  "timeoutSeconds": 600,
45
45
  "signature": "[QK:REMOTE-ROUNDTRIP-OPT-IN-ABSENT-OFF]",
46
46
  "signatureSource": "pi-extensions/lib/remote-pair-roundtrip.test.ts",
@@ -55,7 +55,7 @@
55
55
  "subject": "pi-extensions/lib/remote-pair-send.ts",
56
56
  "find": ["refused by the other host's pair: ${o.reason} (${o.detail})"],
57
57
  "replace": ["failed"],
58
- "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
58
+ "gate": ["bash", "run.sh", "check-remote-pair-beside"],
59
59
  "timeoutSeconds": 600,
60
60
  "signature": "[QK:REMOTE-ROUNDTRIP-OTHER-HOST-REFUSES]",
61
61
  "signatureSource": "pi-extensions/lib/remote-pair-roundtrip.test.ts",
@@ -70,7 +70,7 @@
70
70
  "subject": "mcp/entwurf-bridge/src/index.ts",
71
71
  "find": ["\t\t\t\t\t\tcontrolSocketDirs: [CONTROL_SOCKET_DIR, ENTWURF_DIR],"],
72
72
  "replace": ["\t\t\t\t\t\tcontrolSocketDirs: [ENTWURF_DIR],"],
73
- "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
73
+ "gate": ["bash", "run.sh", "check-remote-pair-beside"],
74
74
  "timeoutSeconds": 600,
75
75
  "signature": "[QK:REMOTE-ROUNDTRIP-LOCAL-TRACE-BOTH-SOCKET-DIRS]",
76
76
  "signatureSource": "pi-extensions/lib/remote-pair-roundtrip.test.ts",
@@ -78,6 +78,66 @@
78
78
  "argv": ["bash", "scripts/build-bridge.sh"],
79
79
  "output": "mcp/entwurf-bridge/dist"
80
80
  }
81
+ },
82
+ {
83
+ "claim": "REMOTE-ROUNDTRIP-MULTI-INDEPENDENT",
84
+ "title": "the receiving entry reads only the first route of its file, so a second citizen paired with this host is refused as an undeclared sender",
85
+ "subject": "scripts/remote-pair-receive.ts",
86
+ "find": ["\tdecls = optIn.routes.map("],
87
+ "replace": ["\tdecls = optIn.routes.slice(0, 1).map("],
88
+ "gate": ["bash", "run.sh", "check-remote-pair-beside"],
89
+ "timeoutSeconds": 600,
90
+ "signature": "[QK:REMOTE-ROUNDTRIP-MULTI-INDEPENDENT]",
91
+ "signatureSource": "pi-extensions/lib/remote-pair-roundtrip.test.ts",
92
+ "build": {
93
+ "argv": ["bash", "scripts/build-bridge.sh"],
94
+ "output": "mcp/entwurf-bridge/dist"
95
+ }
96
+ },
97
+ {
98
+ "claim": "REMOTE-ROUNDTRIP-MULTI-REVERSE",
99
+ "title": "the receiving side recognizes only the first route's remote as a sender, so a claim from a second host lands on the wrong route or is refused",
100
+ "subject": "pi-extensions/lib/remote-pair-receive.ts",
101
+ "find": ["\tconst fromRoutes = decls.filter((d) => d.remoteGardenId === claim.from);"],
102
+ "replace": ["\tconst fromRoutes = decls.filter((d) => d.remoteGardenId === decls[0].remoteGardenId);"],
103
+ "gate": ["bash", "run.sh", "check-remote-pair-beside"],
104
+ "timeoutSeconds": 600,
105
+ "signature": "[QK:REMOTE-ROUNDTRIP-MULTI-REVERSE]",
106
+ "signatureSource": "pi-extensions/lib/remote-pair-roundtrip.test.ts",
107
+ "build": {
108
+ "argv": ["bash", "scripts/build-bridge.sh"],
109
+ "output": "mcp/entwurf-bridge/dist"
110
+ }
111
+ },
112
+ {
113
+ "claim": "REMOTE-ROUNDTRIP-MULTI-UNDECLARED-SENDER-ZERO-SSH",
114
+ "title": "a sender with no route to a declared remote borrows another sender's route, so ssh runs and the other host is asked to accept a pair nobody declared",
115
+ "subject": "pi-extensions/lib/remote-pair-send.ts",
116
+ "find": ["? declared.find((r) => r.localGardenId === sender.sessionId) :"],
117
+ "replace": ["? (declared.find((r) => r.localGardenId === sender.sessionId) ?? declared[0]) :"],
118
+ "gate": ["bash", "run.sh", "check-remote-pair-beside"],
119
+ "timeoutSeconds": 600,
120
+ "signature": "[QK:REMOTE-ROUNDTRIP-MULTI-UNDECLARED-SENDER-ZERO-SSH]",
121
+ "signatureSource": "pi-extensions/lib/remote-pair-roundtrip.test.ts",
122
+ "build": {
123
+ "argv": ["bash", "scripts/build-bridge.sh"],
124
+ "output": "mcp/entwurf-bridge/dist"
125
+ }
126
+ },
127
+ {
128
+ "claim": "REMOTE-ROUNDTRIP-MULTI-CROSS-CLAIM-REFUSED",
129
+ "title": "the target is matched against every route rather than the sender's own, so a peer that declares an extra pair on its side widens this host's",
130
+ "subject": "pi-extensions/lib/remote-pair-receive.ts",
131
+ "find": ["\tconst decl = fromRoutes.find((d) => d.localGardenId === claim.target);"],
132
+ "replace": ["\tconst decl = decls.find((d) => d.localGardenId === claim.target);"],
133
+ "gate": ["bash", "run.sh", "check-remote-pair-beside"],
134
+ "timeoutSeconds": 600,
135
+ "signature": "[QK:REMOTE-ROUNDTRIP-MULTI-CROSS-CLAIM-REFUSED]",
136
+ "signatureSource": "pi-extensions/lib/remote-pair-roundtrip.test.ts",
137
+ "build": {
138
+ "argv": ["bash", "scripts/build-bridge.sh"],
139
+ "output": "mcp/entwurf-bridge/dist"
140
+ }
81
141
  }
82
142
  ]
83
143
  }