@junghanacs/entwurf 0.14.2 → 0.15.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.
- package/AGENTS.md +9 -5
- package/CHANGELOG.md +149 -0
- package/DELIVERY.md +72 -59
- package/README.md +120 -20
- package/VERIFY.md +10 -7
- package/demo/README.md +1 -1
- package/docs/acp-backend-rail.md +2 -2
- package/docs/external-mcp-host.md +26 -4
- package/docs/setup-clean-host.md +118 -18
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +8 -6
- package/mcp/entwurf-bridge/dist/pi-extensions/entwurf-capabilities.json +1 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/copilot-fresh-preflight.js +253 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-sender-identity.js +10 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js +102 -28
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +77 -11
- package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-hook-copilot.js +288 -0
- package/mcp/entwurf-bridge/src/index.ts +10 -6
- package/mcp/entwurf-bridge/tsconfig.build.json +2 -1
- package/package.json +22 -11
- package/pi/copilot-receive/entwurf-receive/extension.mjs +323 -0
- package/pi/entwurf-capabilities.json +1 -0
- package/pi/meta-bridge-copilot/.claude-plugin/marketplace.json +11 -0
- package/pi/meta-bridge-copilot/entwurf-meta-receive-copilot/.claude-plugin/plugin.json +5 -0
- package/pi/meta-bridge-copilot/entwurf-meta-receive-copilot/hooks/hooks.json +7 -0
- package/pi/meta-bridge-copilot/entwurf-meta-receive-copilot/scripts/copilot-hook-launch.sh +85 -0
- package/pi-extensions/entwurf-control.ts +17 -8
- package/pi-extensions/lib/copilot-fresh-preflight.ts +251 -0
- package/pi-extensions/lib/meta-sender-identity.ts +10 -2
- package/pi-extensions/lib/meta-session.ts +102 -29
- package/pi-extensions/lib/mux-fresh-call.ts +82 -11
- package/pi-extensions/meta-bridge-hook-copilot.ts +323 -0
- package/run.sh +1079 -186
- package/scripts/agy-bridge.sh +4 -2
- package/scripts/check-copilot-birth-hook.ts +1174 -0
- package/scripts/check-copilot-launch.ts +395 -0
- package/scripts/check-copilot-receive-arm.ts +734 -0
- package/scripts/check-copilot-statusline.ts +122 -0
- package/scripts/check-entwurf-capabilities.ts +25 -6
- package/scripts/check-fresh-cut-gate.sh +104 -27
- package/scripts/check-gate-qualification.ts +10 -1
- package/scripts/check-install-container.sh +2 -2
- package/scripts/check-install-surface.ts +1 -1
- package/scripts/check-meta-capability-source.ts +89 -5
- package/scripts/check-meta-doctor-oracle.sh +26 -0
- package/scripts/check-meta-manifest-schema.py +38 -0
- package/scripts/check-meta-receiver-marker.ts +25 -2
- package/scripts/check-meta-session.ts +32 -0
- package/scripts/check-setup-qualification.sh +126 -0
- package/scripts/copilot-bridge-doctor.sh +242 -0
- package/scripts/copilot-bridge-install.sh +384 -0
- package/scripts/copilot-bridge-oracle.sh +223 -0
- package/scripts/copilot-bridge-uninstall.sh +137 -0
- package/scripts/copilot-launch.sh +236 -0
- package/scripts/copilot-mcp-bridge.sh +177 -0
- package/scripts/copilot-mcp-config.py +221 -0
- package/scripts/copilot-receive-bridge.sh +483 -0
- package/scripts/copilot-statusline-bridge.sh +149 -0
- package/scripts/copilot-statusline-config.py +201 -0
- package/scripts/copilot-statusline.sh +68 -0
- package/scripts/dev-bin.sh +53 -9
- package/scripts/fake-copilot-vendor.sh +67 -0
- package/scripts/meta-bridge-hook-log.sh +17 -5
- package/scripts/mutants/capability-cache.json +64 -0
- package/scripts/mutants/copilot-birth.json +244 -0
- package/scripts/mutants/copilot-launch.json +187 -0
- package/scripts/mutants/copilot-receive.json +263 -0
- package/scripts/mutants/fresh-cut.json +17 -0
- package/scripts/mutants/mux-fresh-call.json +216 -2
- package/scripts/mutants/pack-install.json +17 -0
- package/scripts/mutants/pi-package-ownership.json +92 -0
- package/scripts/mutants/setup-verdict.json +148 -0
- package/scripts/mutants/source-install.json +32 -0
- package/scripts/pi_settings_io.py +22 -0
- package/scripts/raw-async-delivery/README.md +170 -125
- package/scripts/raw-async-delivery/copilot-enqueue-addressed.sh +35 -0
- package/scripts/raw-async-delivery/copilot-extension-receive/extension.mjs +123 -0
- package/scripts/register-pi-package.py +525 -7
- package/scripts/register-pi-provider.py +162 -12
- package/scripts/smoke-agy-install-state.sh +86 -30
- package/scripts/smoke-agy-statusline-state.sh +9 -6
- package/scripts/smoke-copilot-mcp-state.sh +153 -0
- package/scripts/smoke-copilot-statusline-state.sh +131 -0
- package/scripts/smoke-mux-fresh-call-live.ts +2 -0
- package/scripts/smoke-mux-lifecycle-live.ts +3 -1
- package/scripts/smoke-setup-verdict.sh +291 -0
- package/scripts/smoke-user-scope-citizen.sh +388 -1
- package/scripts/tsconfig.json +1 -0
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"lane": "copilot-receive",
|
|
4
|
+
"mutants": [
|
|
5
|
+
{
|
|
6
|
+
"claim": "COPILOT-RECEIVE-VENDOR-ENTRY-NAME",
|
|
7
|
+
"title": "installing the receiver under any other file name produces a unit Copilot never discovers — the vendor scans subdirectories for `extension.mjs` and says nothing when it finds none",
|
|
8
|
+
"subject": "scripts/copilot-receive-bridge.sh",
|
|
9
|
+
"find": ["\tcp \"$SRC/extension.mjs\" \"$stage/extension.mjs\""],
|
|
10
|
+
"replace": ["\tcp \"$SRC/extension.mjs\" \"$stage/index.mjs\""],
|
|
11
|
+
"gate": ["bash", "run.sh", "check-copilot-receive-arm"],
|
|
12
|
+
"timeoutSeconds": 300,
|
|
13
|
+
"signature": "[QK:COPILOT-RECEIVE-VENDOR-ENTRY-NAME]",
|
|
14
|
+
"signatureSource": "scripts/check-copilot-receive-arm.ts"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"claim": "COPILOT-RECEIVE-INSTALL-REFUSES-FOREIGN",
|
|
18
|
+
"title": "overwriting an unclaimed unit of the same name deletes somebody else's extension and installs ours in its place",
|
|
19
|
+
"subject": "scripts/copilot-receive-bridge.sh",
|
|
20
|
+
"find": [
|
|
21
|
+
"\tif [ -e \"$DEST\" ] && [ ! -f \"$STATE_FILE\" ]; then",
|
|
22
|
+
"\t\tfail \"$DEST already exists and no entwurf install-state claims it. Refusing to overwrite an extension this installer did not put there.\"",
|
|
23
|
+
"\tfi"
|
|
24
|
+
],
|
|
25
|
+
"replace": ["\t# an unclaimed unit is assumed to be ours"],
|
|
26
|
+
"gate": ["bash", "run.sh", "check-copilot-receive-arm"],
|
|
27
|
+
"timeoutSeconds": 300,
|
|
28
|
+
"signature": "[QK:COPILOT-RECEIVE-INSTALL-REFUSES-FOREIGN]",
|
|
29
|
+
"signatureSource": "scripts/check-copilot-receive-arm.ts"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"claim": "COPILOT-RECEIVE-ARMS-AFTER-BIRTH",
|
|
33
|
+
"title": "arming only at join leaves every ordinary session unarmed forever: a Copilot citizen is born on its FIRST PROMPT, which is always after the extension has already joined",
|
|
34
|
+
"subject": "pi/copilot-receive/entwurf-receive/extension.mjs",
|
|
35
|
+
"find": [
|
|
36
|
+
"for (const type of [\"user.message\", \"assistant.turn_start\", \"assistant.message\", \"session.idle\"]) {",
|
|
37
|
+
"\tsession.on(type, () => armOnce(type));",
|
|
38
|
+
"}"
|
|
39
|
+
],
|
|
40
|
+
"replace": ["// one shot at join, no retry"],
|
|
41
|
+
"gate": ["bash", "run.sh", "check-copilot-receive-arm"],
|
|
42
|
+
"timeoutSeconds": 300,
|
|
43
|
+
"signature": "[QK:COPILOT-RECEIVE-ARMS-AFTER-BIRTH]",
|
|
44
|
+
"signatureSource": "scripts/check-copilot-receive-arm.ts"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"claim": "COPILOT-RECEIVE-OWNER-IS-WATCHER",
|
|
48
|
+
"title": "naming the CLI as the marker's owner keeps a crashed extension 'armed' for as long as the TUI stays open — the citizen reads deliverable while nothing is watching its mailbox",
|
|
49
|
+
"subject": "pi/copilot-receive/entwurf-receive/extension.mjs",
|
|
50
|
+
"find": ["\t\t\townerPid: process.pid,"],
|
|
51
|
+
"replace": ["\t\t\townerPid: hostPid,"],
|
|
52
|
+
"gate": ["bash", "run.sh", "check-copilot-receive-arm"],
|
|
53
|
+
"timeoutSeconds": 300,
|
|
54
|
+
"signature": "[QK:COPILOT-RECEIVE-OWNER-IS-WATCHER]",
|
|
55
|
+
"signatureSource": "scripts/check-copilot-receive-arm.ts"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"claim": "COPILOT-RECEIVE-WAKE-MODE-SELF-FETCH",
|
|
59
|
+
"title": "a copilot row that still says direct-inject routes every dispatch into the fail-closed refusal — the armed receiver exists and no reply can ever reach it",
|
|
60
|
+
"subject": "pi/entwurf-capabilities.json",
|
|
61
|
+
"find": [
|
|
62
|
+
"\t\t\"copilot\": { \"wakeMode\": \"self-fetch\", \"deliveryLevel\": \"D6\", \"nativeIdLabel\": \"sessionId\" },"
|
|
63
|
+
],
|
|
64
|
+
"replace": [
|
|
65
|
+
"\t\t\"copilot\": { \"wakeMode\": \"direct-inject\", \"deliveryLevel\": \"D6\", \"nativeIdLabel\": \"sessionId\" },"
|
|
66
|
+
],
|
|
67
|
+
"gate": ["bash", "run.sh", "check-copilot-receive-arm"],
|
|
68
|
+
"timeoutSeconds": 300,
|
|
69
|
+
"signature": "[QK:COPILOT-RECEIVE-WAKE-MODE-SELF-FETCH]",
|
|
70
|
+
"signatureSource": "scripts/check-copilot-receive-arm.ts"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"claim": "COPILOT-RECEIVE-DOORBELL-NOT-BODY",
|
|
74
|
+
"title": "putting the message body in the doorbell turns a self-fetch announcement into an injection: untrusted content lands in the model's context with no read receipt, and `wakeMode: self-fetch` becomes a false label",
|
|
75
|
+
"subject": "pi/copilot-receive/entwurf-receive/extension.mjs",
|
|
76
|
+
"find": [
|
|
77
|
+
"\t\t\t\t\t`[entwurf inbox] ${unread} unread mailbox ${plural} available for garden ${armed.gardenId}. ` +"
|
|
78
|
+
],
|
|
79
|
+
"replace": [
|
|
80
|
+
"\t\t\t\t\t(await import(\"node:fs\")).readFileSync(path.join(armed.mailbox, `${fresh[0]}.delivered`), \"utf8\") +",
|
|
81
|
+
"\t\t\t\t\t`[entwurf inbox] ${unread} unread mailbox ${plural} available for garden ${armed.gardenId}. ` +"
|
|
82
|
+
],
|
|
83
|
+
"gate": ["bash", "run.sh", "check-copilot-receive-arm"],
|
|
84
|
+
"timeoutSeconds": 300,
|
|
85
|
+
"signature": "[QK:COPILOT-RECEIVE-DOORBELL-NOT-BODY]",
|
|
86
|
+
"signatureSource": "scripts/check-copilot-receive-arm.ts"
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"claim": "COPILOT-RECEIVE-NO-STALE-RERING",
|
|
90
|
+
"title": "ringing on any signal change re-announces a backlog the model already declined to read — every unrelated poke wakes an idle session for nothing",
|
|
91
|
+
"subject": "pi/copilot-receive/entwurf-receive/extension.mjs",
|
|
92
|
+
"find": ["\t\t\tif (fresh.length === 0) return;"],
|
|
93
|
+
"replace": ["\t\t\t// ring on every poke, fresh body or not"],
|
|
94
|
+
"gate": ["bash", "run.sh", "check-copilot-receive-arm"],
|
|
95
|
+
"timeoutSeconds": 300,
|
|
96
|
+
"signature": "[QK:COPILOT-RECEIVE-NO-STALE-RERING]",
|
|
97
|
+
"signatureSource": "scripts/check-copilot-receive-arm.ts"
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"claim": "COPILOT-RECEIVE-REFUSES-ID-DRIFT",
|
|
101
|
+
"title": "arming when the SDK session id disagrees with the marker's native id tells a sender that a reply lands in a session that will never see it",
|
|
102
|
+
"subject": "pi/copilot-receive/entwurf-receive/extension.mjs",
|
|
103
|
+
"find": [
|
|
104
|
+
"\tif (marker.nativeSessionId !== sessionId) {",
|
|
105
|
+
"\t\treturn {",
|
|
106
|
+
"\t\t\tidentity: null,",
|
|
107
|
+
"\t\t\treason: `id-drift marker=${marker.nativeSessionId} sdk=${sessionId}`,",
|
|
108
|
+
"\t\t};",
|
|
109
|
+
"\t}"
|
|
110
|
+
],
|
|
111
|
+
"replace": ["\t// a drifted id is tolerated: the marker wins"],
|
|
112
|
+
"gate": ["bash", "run.sh", "check-copilot-receive-arm"],
|
|
113
|
+
"timeoutSeconds": 300,
|
|
114
|
+
"signature": "[QK:COPILOT-RECEIVE-REFUSES-ID-DRIFT]",
|
|
115
|
+
"signatureSource": "scripts/check-copilot-receive-arm.ts"
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"claim": "COPILOT-RECEIVE-REFUSES-FOREIGN-PARENT",
|
|
119
|
+
"title": "trusting a parent-pid carrier that disagrees with the real parent lets a reparented or hand-run extension arm under somebody else's citizen",
|
|
120
|
+
"subject": "pi/copilot-receive/entwurf-receive/extension.mjs",
|
|
121
|
+
"find": [
|
|
122
|
+
"\tif (Number.isSafeInteger(declared) && declared > 0 && declared !== actual) {",
|
|
123
|
+
"\t\tlogLine(\"WARN\", `arm-refused: COPILOT_EXTENSION_PARENT_PID=${declared} disagrees with ppid ${actual}`);",
|
|
124
|
+
"\t\treturn null;",
|
|
125
|
+
"\t}"
|
|
126
|
+
],
|
|
127
|
+
"replace": ["\t// a disagreeing carrier is tolerated"],
|
|
128
|
+
"gate": ["bash", "run.sh", "check-copilot-receive-arm"],
|
|
129
|
+
"timeoutSeconds": 300,
|
|
130
|
+
"signature": "[QK:COPILOT-RECEIVE-REFUSES-FOREIGN-PARENT]",
|
|
131
|
+
"signatureSource": "scripts/check-copilot-receive-arm.ts"
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"claim": "COPILOT-RECEIVE-DEAD-EXTENSION-REFUSED",
|
|
135
|
+
"title": "dropping the receiver marker's liveness guard makes a dead watcher's marker read as an ACTIVE receiver — replies to a closed Copilot session are enqueued into a void and reported as delivered",
|
|
136
|
+
"subject": "pi-extensions/lib/meta-session.ts",
|
|
137
|
+
"find": [
|
|
138
|
+
"\t\t// as an ACTIVE RECEIVER and pull the mailbox rail into delivering to a void.",
|
|
139
|
+
"\t\tif (!isPlausibleOwnerPid(marker.ownerPid)) return null;",
|
|
140
|
+
"\t\tif (opts.verifyOwner !== false) {",
|
|
141
|
+
"\t\t\tconst liveKey = processStartKey(marker.ownerPid);",
|
|
142
|
+
"\t\t\tif (!liveKey || liveKey !== marker.ownerStartKey) return null;",
|
|
143
|
+
"\t\t}"
|
|
144
|
+
],
|
|
145
|
+
"replace": [
|
|
146
|
+
"\t\t// as an ACTIVE RECEIVER and pull the mailbox rail into delivering to a void.",
|
|
147
|
+
"\t\tif (!isPlausibleOwnerPid(marker.ownerPid)) return null;"
|
|
148
|
+
],
|
|
149
|
+
"gate": ["bash", "run.sh", "check-copilot-receive-arm"],
|
|
150
|
+
"timeoutSeconds": 300,
|
|
151
|
+
"signature": "[QK:COPILOT-RECEIVE-DEAD-EXTENSION-REFUSED]",
|
|
152
|
+
"signatureSource": "scripts/check-copilot-receive-arm.ts"
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
"claim": "COPILOT-RECEIVE-DOCTOR-CATCHES-STALE-WRITER",
|
|
156
|
+
"title": "reporting a stale deployed writer as ordinary state hides the one failure that has no other symptom: the arm throws inside the extension and the citizen simply never becomes deliverable",
|
|
157
|
+
"subject": "scripts/copilot-receive-bridge.sh",
|
|
158
|
+
"find": [
|
|
159
|
+
"\t\t\t\tbad \"the deployed writer is STALE: unit has ${dep:0:12}, this checkout builds ${cur:0:12}. An arm can fail on a contract the deployed copy does not know, and the only symptom is a session that never becomes deliverable. Re-run ./run.sh install-copilot-receive\""
|
|
160
|
+
],
|
|
161
|
+
"replace": ["\t\t\t\tnote \"the deployed writer differs from this checkout's (${dep:0:12} vs ${cur:0:12})\""],
|
|
162
|
+
"gate": ["bash", "run.sh", "check-copilot-receive-arm"],
|
|
163
|
+
"timeoutSeconds": 300,
|
|
164
|
+
"signature": "[QK:COPILOT-RECEIVE-DOCTOR-CATCHES-STALE-WRITER]",
|
|
165
|
+
"signatureSource": "scripts/check-copilot-receive-arm.ts"
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
"claim": "COPILOT-RECEIVE-DOCTOR-SEES-MISSING-FLAG",
|
|
169
|
+
"title": "downgrading a CLI launched without the scan flag to a note leaves the vendor's own silence unchallenged — the receiver is installed, nothing arms, and no surface anywhere says why",
|
|
170
|
+
"subject": "scripts/copilot-receive-bridge.sh",
|
|
171
|
+
"find": [
|
|
172
|
+
"\t\t\t\tbad \"$armed_missing live Copilot CLI process(es) lack $FLAG_ENV=$FLAG_VALUE while the receiver is installed (pids: $missing_pids) — relaunch them with 'entwurf copilot', or uninstall the receiver so nothing promises a doorbell\""
|
|
173
|
+
],
|
|
174
|
+
"replace": ["\t\t\t\tnote \"$armed_missing live Copilot CLI process(es) lack $FLAG_ENV=$FLAG_VALUE\""],
|
|
175
|
+
"gate": ["bash", "run.sh", "check-copilot-receive-arm"],
|
|
176
|
+
"timeoutSeconds": 300,
|
|
177
|
+
"signature": "[QK:COPILOT-RECEIVE-DOCTOR-SEES-MISSING-FLAG]",
|
|
178
|
+
"signatureSource": "scripts/check-copilot-receive-arm.ts"
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
"claim": "COPILOT-RECEIVE-DOCTOR-PASSES-CLEAN-LOG",
|
|
182
|
+
"title": "a doctor that dies mid-script on a healthy host prints no verdict at all — under `set -euo pipefail` an unguarded no-match grep (exit 1) ends the run before the PASS line, so the operator cannot tell 'fine' from 'crashed' and the silent death returns with the first zero-count log",
|
|
183
|
+
"subject": "scripts/copilot-receive-bridge.sh",
|
|
184
|
+
"find": [
|
|
185
|
+
"\t\ttotal_err=\"$({ grep -c ' ERROR \\[copilot-receive\\] ' \"$RECEIVE_LOG\" 2>/dev/null || [ \"$?\" -eq 1 ]; } | head -1)\""
|
|
186
|
+
],
|
|
187
|
+
"replace": [
|
|
188
|
+
"\t\ttotal_err=\"$(grep -c ' ERROR \\[copilot-receive\\] ' \"$RECEIVE_LOG\" 2>/dev/null | head -1)\""
|
|
189
|
+
],
|
|
190
|
+
"gate": ["bash", "run.sh", "check-copilot-receive-arm"],
|
|
191
|
+
"timeoutSeconds": 300,
|
|
192
|
+
"signature": "[QK:COPILOT-RECEIVE-DOCTOR-PASSES-CLEAN-LOG]",
|
|
193
|
+
"signatureSource": "scripts/check-copilot-receive-arm.ts"
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
"claim": "COPILOT-RECEIVE-DOCTOR-FINDS-EXECED-NATIVE",
|
|
197
|
+
"title": "identifying the CLI by command name finds nothing at all — the vendor shim execs node, so the surviving process never carries the name `copilot`, and the detector built to break a vendor silence reports the benign 'no live copilot' note forever",
|
|
198
|
+
"subject": "scripts/copilot-receive-bridge.sh",
|
|
199
|
+
"find": [
|
|
200
|
+
"\tis_native = any(\"/@github/copilot/\" in a and a.endswith(\".js\") for a in argv)",
|
|
201
|
+
"\tif not is_native and os.path.basename(argv[0]) == \"copilot\":",
|
|
202
|
+
"\t\tis_native = True"
|
|
203
|
+
],
|
|
204
|
+
"replace": ["\tis_native = os.path.basename(argv[0]) == \"copilot\""],
|
|
205
|
+
"gate": ["bash", "run.sh", "check-copilot-receive-arm"],
|
|
206
|
+
"timeoutSeconds": 300,
|
|
207
|
+
"signature": "[QK:COPILOT-RECEIVE-DOCTOR-FINDS-EXECED-NATIVE]",
|
|
208
|
+
"signatureSource": "scripts/check-copilot-receive-arm.ts"
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
"claim": "COPILOT-RECEIVE-DOCTOR-EXCLUDES-EXTENSION-CHILD",
|
|
212
|
+
"title": "counting our own extension children as Copilot sessions makes the doctor blame the operator for sessions that do not exist — every armed receiver would report itself as a CLI launched without the scan flag",
|
|
213
|
+
"subject": "scripts/copilot-receive-bridge.sh",
|
|
214
|
+
"find": ["\tif any(a.endswith(\"extension.mjs\") for a in argv):", "\t\tcontinue"],
|
|
215
|
+
"replace": ["\t# an extension child is close enough to a session"],
|
|
216
|
+
"gate": ["bash", "run.sh", "check-copilot-receive-arm"],
|
|
217
|
+
"timeoutSeconds": 300,
|
|
218
|
+
"signature": "[QK:COPILOT-RECEIVE-DOCTOR-EXCLUDES-EXTENSION-CHILD]",
|
|
219
|
+
"signatureSource": "scripts/check-copilot-receive-arm.ts"
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
"claim": "COPILOT-RECEIVE-DOCTOR-UNREADABLE-ENVIRON-IS-RED",
|
|
223
|
+
"title": "downgrading an identified CLI whose environment cannot be read to a note lets the doctor end in PASS while a session that can never arm is running — identity already succeeded, so 'unknown' here is the exact question the section exists to answer",
|
|
224
|
+
"subject": "scripts/copilot-receive-bridge.sh",
|
|
225
|
+
"find": [
|
|
226
|
+
"\t\t\t\tbad \"$unreadable live Copilot CLI process(es) have an unreadable environment (pids: $unreadable_pids) — their $FLAG_ENV state is UNKNOWN and is NOT assumed armed. Re-run this doctor as the user that owns those sessions.\""
|
|
227
|
+
],
|
|
228
|
+
"replace": [
|
|
229
|
+
"\t\t\t\tnote \"$unreadable live Copilot CLI process(es) have an unreadable environment (pids: $unreadable_pids) — their $FLAG_ENV state is UNKNOWN and is NOT assumed armed. Re-run this doctor as the user that owns those sessions.\""
|
|
230
|
+
],
|
|
231
|
+
"gate": ["bash", "run.sh", "check-copilot-receive-arm"],
|
|
232
|
+
"timeoutSeconds": 300,
|
|
233
|
+
"signature": "[QK:COPILOT-RECEIVE-DOCTOR-UNREADABLE-ENVIRON-IS-RED]",
|
|
234
|
+
"signatureSource": "scripts/check-copilot-receive-arm.ts"
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
"claim": "COPILOT-RECEIVE-DOCTOR-OWNS-SCANNER-FAILURE",
|
|
238
|
+
"title": "letting the scanner's exit status reach the assignment under `set -e` kills the doctor mid-section: no verdict line is printed at all, so the operator cannot tell a healthy host from a broken doctor — the same silent-death shape this lane already fixed once in the log grep",
|
|
239
|
+
"subject": "scripts/copilot-receive-bridge.sh",
|
|
240
|
+
"find": ["PY\n)\" || scan_rc=$?"],
|
|
241
|
+
"replace": ["PY\n)\""],
|
|
242
|
+
"gate": ["bash", "run.sh", "check-copilot-receive-arm"],
|
|
243
|
+
"timeoutSeconds": 300,
|
|
244
|
+
"signature": "[QK:COPILOT-RECEIVE-DOCTOR-OWNS-SCANNER-FAILURE]",
|
|
245
|
+
"signatureSource": "scripts/check-copilot-receive-arm.ts"
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
"claim": "COPILOT-RECEIVE-DOCTOR-PI-CARRIER-IS-RED",
|
|
249
|
+
"title": "downgrading inherited PI_SESSION_ID/PI_AGENT_ID to a note lets an armed Copilot speak under the parent pi garden id while the doctor still ends PASS — the flag axis cannot absorb this",
|
|
250
|
+
"subject": "scripts/copilot-receive-bridge.sh",
|
|
251
|
+
"find": [
|
|
252
|
+
"\t\t\t\tbad \"$contaminated live Copilot CLI process(es) inherited PI_SESSION_ID/PI_AGENT_ID (pids: $contaminated_pids) — they can speak under the PARENT pi citizen's garden id even with a correct native marker. Relaunch them with 'entwurf copilot', which unsets both before exec; a raw 'copilot' started from a pi citizen's shell is unsupported.\""
|
|
253
|
+
],
|
|
254
|
+
"replace": [
|
|
255
|
+
"\t\t\t\tnote \"$contaminated live Copilot CLI process(es) inherited PI_SESSION_ID/PI_AGENT_ID (pids: $contaminated_pids)\""
|
|
256
|
+
],
|
|
257
|
+
"gate": ["bash", "run.sh", "check-copilot-receive-arm"],
|
|
258
|
+
"timeoutSeconds": 300,
|
|
259
|
+
"signature": "[QK:COPILOT-RECEIVE-DOCTOR-PI-CARRIER-IS-RED]",
|
|
260
|
+
"signatureSource": "scripts/check-copilot-receive-arm.ts"
|
|
261
|
+
}
|
|
262
|
+
]
|
|
263
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"lane": "fresh-cut",
|
|
4
|
+
"mutants": [
|
|
5
|
+
{
|
|
6
|
+
"claim": "FRESH-CUT-COLLISION-NO-MOVE",
|
|
7
|
+
"title": "preflighting only the FIRST planned destination lets an occupied second destination through: the cut renames over it and reports a clean generation",
|
|
8
|
+
"subject": "scripts/meta-bridge-fresh-cut.ts",
|
|
9
|
+
"find": ["\tconst collisions = plan.filter(({ dest }) => archiveDestOccupied(dest));"],
|
|
10
|
+
"replace": ["\tconst collisions = plan.slice(0, 1).filter(({ dest }) => archiveDestOccupied(dest));"],
|
|
11
|
+
"gate": ["bash", "run.sh", "check-fresh-cut-gate"],
|
|
12
|
+
"timeoutSeconds": 180,
|
|
13
|
+
"signature": "[QK:FRESH-CUT-COLLISION-NO-MOVE]",
|
|
14
|
+
"signatureSource": "scripts/check-fresh-cut-gate.sh"
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
|
@@ -147,7 +147,7 @@
|
|
|
147
147
|
"title": "native pi drops the required model field from fresh-call while the composition still expects one",
|
|
148
148
|
"subject": "pi-extensions/entwurf-control.ts",
|
|
149
149
|
"find": [
|
|
150
|
-
"\t\t\tmodel: Type.String({\n\t\t\t\tminLength: 1,\n\t\t\t\tmaxLength: 200,\n\t\t\t\tpattern: \"^[A-Za-z0-9][A-Za-z0-9._/:\\\\[\\\\]-]*$\",\n\t\t\t\tdescription
|
|
150
|
+
"\t\t\tmodel: Type.String({\n\t\t\t\tminLength: 1,\n\t\t\t\tmaxLength: 200,\n\t\t\t\tpattern: \"^[A-Za-z0-9][A-Za-z0-9._/:\\\\[\\\\]-]*$\",\n\t\t\t\tdescription:\n\t\t\t\t\t\"Required runtime model: canonical provider/model for pi, a Claude Code model id/alias, or a Copilot model name (or auto).\",\n\t\t\t}),\n"
|
|
151
151
|
],
|
|
152
152
|
"replace": [""],
|
|
153
153
|
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
@@ -173,7 +173,7 @@
|
|
|
173
173
|
"title": "MCP drops the required model field from fresh-call while the composition still expects one",
|
|
174
174
|
"subject": "mcp/entwurf-bridge/src/index.ts",
|
|
175
175
|
"find": [
|
|
176
|
-
"\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(\"Required runtime model: canonical provider/model for pi,
|
|
176
|
+
"\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 Claude Code model id/alias, or a Copilot model name (or auto).\",\n\t\t\t),\n"
|
|
177
177
|
],
|
|
178
178
|
"replace": [""],
|
|
179
179
|
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
@@ -224,6 +224,220 @@
|
|
|
224
224
|
"timeoutSeconds": 60,
|
|
225
225
|
"signature": "[QK:VITEST-FAILED-TITLE-ATTRIBUTION]",
|
|
226
226
|
"signatureSource": "scripts/check-gate-qualification.ts"
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
"claim": "FRESHCALL-COPILOT-MANAGED-RUNTIME",
|
|
230
|
+
"title": "fresh call opens the BARE vendor instead of entwurf's managed invocation — the window opens, the EXTENSIONS scan is skipped silently, and the sibling can never be delivered to",
|
|
231
|
+
"subject": "pi-extensions/lib/mux-fresh-call.ts",
|
|
232
|
+
"find": ["\tcopilot: \"entwurf\","],
|
|
233
|
+
"replace": ["\tcopilot: \"copilot\","],
|
|
234
|
+
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
235
|
+
"timeoutSeconds": 180,
|
|
236
|
+
"signature": "[QK:FRESHCALL-COPILOT-MANAGED-RUNTIME]",
|
|
237
|
+
"signatureSource": "test/mux-fresh-call.test.ts"
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
"claim": "FRESHCALL-COPILOT-ARGV-INTERACTIVE",
|
|
241
|
+
"title": "the Copilot prompt rides -p instead of --interactive — the turn runs and the CLI EXITS, closing the window on a sibling that was supposed to stay open",
|
|
242
|
+
"subject": "pi-extensions/lib/mux-fresh-call.ts",
|
|
243
|
+
"find": ["\t\t\treturn [\"copilot\", \"--interactive\", prompt, \"--model\", model, \"--yolo\"];"],
|
|
244
|
+
"replace": ["\t\t\treturn [\"copilot\", \"-p\", prompt, \"--model\", model, \"--yolo\"];"],
|
|
245
|
+
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
246
|
+
"timeoutSeconds": 180,
|
|
247
|
+
"signature": "[QK:FRESHCALL-COPILOT-ARGV-INTERACTIVE]",
|
|
248
|
+
"signatureSource": "test/mux-fresh-call.test.ts"
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
"claim": "FRESHCALL-COPILOT-MODEL-ARGV",
|
|
252
|
+
"title": "the Copilot argv drops the explicit model — the managed launcher then injects `--model auto` and the caller's model choice is silently discarded",
|
|
253
|
+
"subject": "pi-extensions/lib/mux-fresh-call.ts",
|
|
254
|
+
"find": ["\t\t\treturn [\"copilot\", \"--interactive\", prompt, \"--model\", model, \"--yolo\"];"],
|
|
255
|
+
"replace": ["\t\t\treturn [\"copilot\", \"--interactive\", prompt, \"--yolo\"];"],
|
|
256
|
+
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
257
|
+
"timeoutSeconds": 180,
|
|
258
|
+
"signature": "[QK:FRESHCALL-COPILOT-MODEL-ARGV]",
|
|
259
|
+
"signatureSource": "test/mux-fresh-call.test.ts"
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
"claim": "FRESHCALL-COPILOT-YOLO-POLICY",
|
|
263
|
+
"title": "the policy token narrows back to the callback-only --allow-tool grant — the exact regression GLG measured on 2026-08-25: no YOLO in the footer, a confirmation prompt at every task tool, an impractical sibling",
|
|
264
|
+
"subject": "pi-extensions/lib/mux-fresh-call.ts",
|
|
265
|
+
"find": ["\t\t\treturn [\"copilot\", \"--interactive\", prompt, \"--model\", model, \"--yolo\"];"],
|
|
266
|
+
"replace": [
|
|
267
|
+
"\t\t\treturn [\"copilot\", \"--interactive\", prompt, \"--model\", model, \"--allow-tool=entwurf-bridge(entwurf_v2)\"];"
|
|
268
|
+
],
|
|
269
|
+
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
270
|
+
"timeoutSeconds": 180,
|
|
271
|
+
"signature": "[QK:FRESHCALL-COPILOT-YOLO-POLICY]",
|
|
272
|
+
"signatureSource": "test/mux-fresh-call.test.ts"
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
"claim": "FRESHCALL-COPILOT-CALLBACK-TOOL",
|
|
276
|
+
"title": "the Copilot callback names Claude Code's `mcp__server__tool` spelling — a dialect copied instead of measured, and the whole first turn is spent looking for a tool that is not there",
|
|
277
|
+
"subject": "pi-extensions/lib/mux-fresh-call.ts",
|
|
278
|
+
"find": ["\tcopilot: \"entwurf-bridge-entwurf_v2\","],
|
|
279
|
+
"replace": ["\tcopilot: \"mcp__entwurf-bridge__entwurf_v2\","],
|
|
280
|
+
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
281
|
+
"timeoutSeconds": 180,
|
|
282
|
+
"signature": "[QK:FRESHCALL-COPILOT-CALLBACK-TOOL]",
|
|
283
|
+
"signatureSource": "test/mux-fresh-call.test.ts"
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
"claim": "FRESHCALL-COPILOT-PREFLIGHT-PREMUTATION",
|
|
287
|
+
"title": "the capability preflight is removed — a host missing the birth, MCP, receiver or footer unit gets a real window and a launch receipt for a sibling that can never call home",
|
|
288
|
+
"subject": "pi-extensions/lib/mux-fresh-call.ts",
|
|
289
|
+
"find": [
|
|
290
|
+
"\tif (params.backend === \"copilot\") {\n\t\tconst missing = copilotFreshPreflight(env);\n\t\tif (missing) return { ok: false, reason: missing };\n\t}\n"
|
|
291
|
+
],
|
|
292
|
+
"replace": [""],
|
|
293
|
+
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
294
|
+
"timeoutSeconds": 180,
|
|
295
|
+
"signature": "[QK:FRESHCALL-COPILOT-PREFLIGHT-PREMUTATION]",
|
|
296
|
+
"signatureSource": "test/mux-fresh-call.test.ts"
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
"claim": "FRESHCALL-COPILOT-PREFLIGHT-AFTER-RUNTIME",
|
|
300
|
+
"title": "the preflight runs before the runtime is proven — an operator with no entwurf at all is told to install a Copilot unit, which is the wrong repair",
|
|
301
|
+
"subject": "pi-extensions/lib/mux-fresh-call.ts",
|
|
302
|
+
"find": ["\tlet runtimePath: string;"],
|
|
303
|
+
"replace": [
|
|
304
|
+
"\tif (params.backend === \"copilot\") {\n\t\tconst early = copilotFreshPreflight(env);\n\t\tif (early) return { ok: false, reason: early };\n\t}\n\tlet runtimePath: string;"
|
|
305
|
+
],
|
|
306
|
+
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
307
|
+
"timeoutSeconds": 180,
|
|
308
|
+
"signature": "[QK:FRESHCALL-COPILOT-PREFLIGHT-AFTER-RUNTIME]",
|
|
309
|
+
"signatureSource": "test/mux-fresh-call.test.ts"
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
"claim": "FRESHCALL-BACKEND-SET-SURFACE-PARITY",
|
|
313
|
+
"title": "a backend the composition can open is missing from the MCP surface enum — the capability exists and no external host can reach it",
|
|
314
|
+
"subject": "mcp/entwurf-bridge/src/index.ts",
|
|
315
|
+
"find": ["\t\t\t.enum([\"pi\", \"claude-code\", \"copilot\"])"],
|
|
316
|
+
"replace": ["\t\t\t.enum([\"pi\", \"claude-code\"])"],
|
|
317
|
+
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
318
|
+
"timeoutSeconds": 180,
|
|
319
|
+
"signature": "[QK:FRESHCALL-BACKEND-SET-SURFACE-PARITY]",
|
|
320
|
+
"signatureSource": "test/fresh-call-surfaces.contract.test.ts"
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
"claim": "COPILOT-PREFLIGHT-VISIBLE-IDENTITY-EFFECTIVE",
|
|
324
|
+
"title": "the visible-identity axis gates on entwurf's ownership RECEIPT instead of the settings Copilot actually reads — a host with a correct, resolvable footer and no install-state is refused for a working identity surface (measured: that is the acceptance host)",
|
|
325
|
+
"subject": "pi-extensions/lib/copilot-fresh-preflight.ts",
|
|
326
|
+
"find": ["\tif (state === null) return false; // absent state is not drift — see the doc comment above"],
|
|
327
|
+
"replace": ["\tif (state === null) return true;"],
|
|
328
|
+
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
329
|
+
"timeoutSeconds": 180,
|
|
330
|
+
"signature": "[QK:COPILOT-PREFLIGHT-VISIBLE-IDENTITY-EFFECTIVE]",
|
|
331
|
+
"signatureSource": "test/copilot-fresh-preflight.test.ts"
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
"claim": "COPILOT-PREFLIGHT-VISIBLE-IDENTITY-REQUIRED",
|
|
335
|
+
"title": "the visible-identity axis is dropped entirely — a Copilot citizen is admitted whose garden id can only be found by scraping records, which step 4 refuses to call lifecycle parity",
|
|
336
|
+
"subject": "pi-extensions/lib/copilot-fresh-preflight.ts",
|
|
337
|
+
"find": ["\tif (visibleIdentityMissing(env, data)) return \"copilot-visible-identity-missing\";"],
|
|
338
|
+
"replace": [""],
|
|
339
|
+
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
340
|
+
"timeoutSeconds": 180,
|
|
341
|
+
"signature": "[QK:COPILOT-PREFLIGHT-VISIBLE-IDENTITY-REQUIRED]",
|
|
342
|
+
"signatureSource": "test/copilot-fresh-preflight.test.ts"
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
"claim": "COPILOT-PREFLIGHT-MCP-CONFIG-DRIFT",
|
|
346
|
+
"title": "the MCP axis trusts the install-state alone — a config that lost the server key still reads as installed, and the first turn calls a tool that is not registered",
|
|
347
|
+
"subject": "pi-extensions/lib/copilot-fresh-preflight.ts",
|
|
348
|
+
"find": [
|
|
349
|
+
"\tif (!Object.hasOwn(servers as Record<string, unknown>, MCP_SERVER_KEY)) return \"copilot-mcp-hand-missing\";"
|
|
350
|
+
],
|
|
351
|
+
"replace": [""],
|
|
352
|
+
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
353
|
+
"timeoutSeconds": 180,
|
|
354
|
+
"signature": "[QK:COPILOT-PREFLIGHT-MCP-CONFIG-DRIFT]",
|
|
355
|
+
"signatureSource": "test/copilot-fresh-preflight.test.ts"
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
"claim": "COPILOT-PREFLIGHT-BIRTH-AXIS",
|
|
359
|
+
"title": "the birth axis is satisfied by the directory alone — an assembly root with no hooks.json passes as a hook unit, so a host that cannot mint a record reads as ready",
|
|
360
|
+
"subject": "pi-extensions/lib/copilot-fresh-preflight.ts",
|
|
361
|
+
"find": ["\tif (!isDir(birthUnit) || !existsSync(path.join(birthUnit, \"hooks\", \"hooks.json\"))) {"],
|
|
362
|
+
"replace": ["\tif (!isDir(birthUnit)) {"],
|
|
363
|
+
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
364
|
+
"timeoutSeconds": 180,
|
|
365
|
+
"signature": "[QK:COPILOT-PREFLIGHT-BIRTH-AXIS]",
|
|
366
|
+
"signatureSource": "test/copilot-fresh-preflight.test.ts"
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
"claim": "COPILOT-PREFLIGHT-RECEIVE-PATH-MISMATCH",
|
|
370
|
+
"title": "the receive axis ignores the launcher's DEST equality — a unit in a different extensions root than this env will scan still preflights green, then entwurf copilot refuses inside the new window",
|
|
371
|
+
"subject": "pi-extensions/lib/copilot-fresh-preflight.ts",
|
|
372
|
+
"find": [
|
|
373
|
+
"\tconst dest = receiveDest(env);\n\tif (dest === null || recvPath !== dest) return \"copilot-receive-unit-missing\";\n"
|
|
374
|
+
],
|
|
375
|
+
"replace": [""],
|
|
376
|
+
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
377
|
+
"timeoutSeconds": 180,
|
|
378
|
+
"signature": "[QK:COPILOT-PREFLIGHT-RECEIVE-PATH-MISMATCH]",
|
|
379
|
+
"signatureSource": "test/copilot-fresh-preflight.test.ts"
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
"claim": "COPILOT-PREFLIGHT-RECEIVE-AXIS",
|
|
383
|
+
"title": "the receive axis is satisfied by the directory alone — a leftover folder with no extension.mjs passes as our unit, so a host that cannot arm still reads as ready",
|
|
384
|
+
"subject": "pi-extensions/lib/copilot-fresh-preflight.ts",
|
|
385
|
+
"find": [
|
|
386
|
+
"\tif (!isDir(recvPath) || !existsSync(path.join(recvPath, \"extension.mjs\"))) return \"copilot-receive-unit-missing\";"
|
|
387
|
+
],
|
|
388
|
+
"replace": ["\tif (!isDir(recvPath)) return \"copilot-receive-unit-missing\";"],
|
|
389
|
+
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
390
|
+
"timeoutSeconds": 180,
|
|
391
|
+
"signature": "[QK:COPILOT-PREFLIGHT-RECEIVE-AXIS]",
|
|
392
|
+
"signatureSource": "test/copilot-fresh-preflight.test.ts"
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
"claim": "COPILOT-PREFLIGHT-COMPLETE-HOST-PASSES",
|
|
396
|
+
"title": "a correctly wired host is still refused — the preflight becomes a permanent closed door wearing a check's clothes",
|
|
397
|
+
"subject": "pi-extensions/lib/copilot-fresh-preflight.ts",
|
|
398
|
+
"find": [
|
|
399
|
+
"\tif (visibleIdentityMissing(env, data)) return \"copilot-visible-identity-missing\";\n\n\treturn null;"
|
|
400
|
+
],
|
|
401
|
+
"replace": [
|
|
402
|
+
"\tif (visibleIdentityMissing(env, data)) return \"copilot-visible-identity-missing\";\n\n\treturn \"copilot-birth-unit-missing\";"
|
|
403
|
+
],
|
|
404
|
+
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
405
|
+
"timeoutSeconds": 180,
|
|
406
|
+
"signature": "[QK:COPILOT-PREFLIGHT-COMPLETE-HOST-PASSES]",
|
|
407
|
+
"signatureSource": "test/copilot-fresh-preflight.test.ts"
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
"claim": "COPILOT-PREFLIGHT-STATUSLINE-STATE-DRIFT",
|
|
411
|
+
"title": "an install-state that manages a different settings file is accepted — two files then disagree about who owns the footer and the preflight pretends they do not",
|
|
412
|
+
"subject": "pi-extensions/lib/copilot-fresh-preflight.ts",
|
|
413
|
+
"find": ["\treturn path.resolve(managed) !== path.resolve(settingsPath);"],
|
|
414
|
+
"replace": ["\treturn false;"],
|
|
415
|
+
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
416
|
+
"timeoutSeconds": 180,
|
|
417
|
+
"signature": "[QK:COPILOT-PREFLIGHT-STATUSLINE-STATE-DRIFT]",
|
|
418
|
+
"signatureSource": "test/copilot-fresh-preflight.test.ts"
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
"claim": "FRESHCALL-COPILOT-EXPLICIT-POLICY",
|
|
422
|
+
"title": "the composition stops stating its policy and silently leans on the launcher to inject --yolo — the effective profile survives but the fresh contract loses its explicit permission statement",
|
|
423
|
+
"subject": "pi-extensions/lib/mux-fresh-call.ts",
|
|
424
|
+
"find": ["\t\t\treturn [\"copilot\", \"--interactive\", prompt, \"--model\", model, \"--yolo\"];"],
|
|
425
|
+
"replace": ["\t\t\treturn [\"copilot\", \"--interactive\", prompt, \"--model\", model];"],
|
|
426
|
+
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
427
|
+
"timeoutSeconds": 180,
|
|
428
|
+
"signature": "[QK:FRESHCALL-COPILOT-EXPLICIT-POLICY]",
|
|
429
|
+
"signatureSource": "test/mux-fresh-call.test.ts"
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
"claim": "FRESHCALL-BACKEND-SET-SURFACE-PARITY-PI",
|
|
433
|
+
"title": "a backend the composition can open is missing from the native pi surface enum — an in-process pi session cannot name a capability the module already opens",
|
|
434
|
+
"subject": "pi-extensions/entwurf-control.ts",
|
|
435
|
+
"find": ["\t\t\tbackend: StringEnum([\"pi\", \"claude-code\", \"copilot\"], {"],
|
|
436
|
+
"replace": ["\t\t\tbackend: StringEnum([\"pi\", \"claude-code\"], {"],
|
|
437
|
+
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
438
|
+
"timeoutSeconds": 180,
|
|
439
|
+
"signature": "[QK:FRESHCALL-BACKEND-SET-SURFACE-PARITY-PI]",
|
|
440
|
+
"signatureSource": "test/fresh-call-surfaces.contract.test.ts"
|
|
227
441
|
}
|
|
228
442
|
]
|
|
229
443
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"lane": "pack-install",
|
|
4
|
+
"mutants": [
|
|
5
|
+
{
|
|
6
|
+
"claim": "PACK-INSTALL-PIN-MATCHER-BOUNDED",
|
|
7
|
+
"title": "the pin-leak matcher loses its version boundary and blesses substring lookalikes — @0.84.30 reads as the pinned 0.84.3 and the gate announces a verified tree it never verified (independent review, 2026-08-25)",
|
|
8
|
+
"subject": "run.sh",
|
|
9
|
+
"find": [" grep '^@earendil-works+pi-' | grep -Ev '@0\\.84\\.3(_|$)' || true"],
|
|
10
|
+
"replace": [" grep '^@earendil-works+pi-' | grep -v '@0\\.84\\.3' || true"],
|
|
11
|
+
"gate": ["bash", "run.sh", "check-pack-pin-matcher"],
|
|
12
|
+
"timeoutSeconds": 120,
|
|
13
|
+
"signature": "[QK:PACK-INSTALL-PIN-MATCHER-BOUNDED]",
|
|
14
|
+
"signatureSource": "run.sh"
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"lane": "pi-package-ownership",
|
|
4
|
+
"mutants": [
|
|
5
|
+
{
|
|
6
|
+
"claim": "SILENT-TAKEOVER",
|
|
7
|
+
"title": "a normal user-scope install silently replaces another LIVE root's registration, resurrecting the last-writer-wins takeover the #86 C2 ownership contract retires (only the operator-explicit takeover-user-scope may move the shared entry)",
|
|
8
|
+
"subject": "scripts/register-pi-package.py",
|
|
9
|
+
"find": [
|
|
10
|
+
" if os.path.isdir(owner):\n _refuse(6, \"register-pi-package: the user-scope entwurf registration is owned by another LIVE root: \"\n f\"{owner}. Normal install/setup never replaces another owner — zero settings bytes written. \"\n f\"Run './run.sh takeover-user-scope' from {repo_dir} to explicitly move the shared entry.\")"
|
|
11
|
+
],
|
|
12
|
+
"replace": [
|
|
13
|
+
" if os.path.isdir(owner):\n return register_user(settings_path, repo_dir_arg, state_path, True)"
|
|
14
|
+
],
|
|
15
|
+
"gate": ["bash", "run.sh", "smoke-user-scope-citizen"],
|
|
16
|
+
"timeoutSeconds": 300,
|
|
17
|
+
"signature": "[QK:SILENT-TAKEOVER]",
|
|
18
|
+
"signatureSource": "scripts/smoke-user-scope-citizen.sh"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"claim": "FOREIGN-INVERSE-REMOVE",
|
|
22
|
+
"title": "an old root's inverse removes the CURRENT live owner's user-scope registration, so one stale checkout's remove-user-scope deletes the shared entry every other project depends on",
|
|
23
|
+
"subject": "scripts/register-pi-package.py",
|
|
24
|
+
"find": [
|
|
25
|
+
" if os.path.isdir(owner):\n _refuse(6, \"register-pi-package: the user-scope registration is owned by another LIVE root: \"\n f\"{owner}. This root's inverse must not remove someone else's registration — zero settings \"\n \"bytes written. Run remove from that root, or take the entry over first.\")"
|
|
26
|
+
],
|
|
27
|
+
"replace": [
|
|
28
|
+
" if os.path.isdir(owner):\n n, kept = remove(settings_path, owner)\n state_path.unlink()\n print(f\"remove: removed {n} entries (foreign owner ignored)\")\n return 0"
|
|
29
|
+
],
|
|
30
|
+
"gate": ["bash", "run.sh", "smoke-user-scope-citizen"],
|
|
31
|
+
"timeoutSeconds": 300,
|
|
32
|
+
"signature": "[QK:FOREIGN-INVERSE-REMOVE]",
|
|
33
|
+
"signatureSource": "scripts/smoke-user-scope-citizen.sh"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"claim": "BROAD-INSTALL-COLLAPSE",
|
|
37
|
+
"title": "a same-owner user-scope install routes through the broad project-scope register() collapse, so a second entwurf-shaped root in the GLOBAL packages[] is silently deleted as collateral of an ordinary install — the exact transition B reproduced against 096a5cd (#86 C2 corrective amendment, blocker 1)",
|
|
38
|
+
"subject": "scripts/register-pi-package.py",
|
|
39
|
+
"find": [
|
|
40
|
+
" _require_user_owner_entry(settings_path, owner)\n if preflight:\n print(\"preflight: install ok (this root owns one unambiguous registration)\")\n return 0\n print(f\"install: entwurf package already registered (no-op) -> {repo_dir}\")\n return 0"
|
|
41
|
+
],
|
|
42
|
+
"replace": [
|
|
43
|
+
" if preflight:\n print(\"preflight: install ok (this root owns one unambiguous registration)\")\n return 0\n register(settings_path, repo_dir_arg)\n print(f\"install: entwurf package already registered (no-op) -> {repo_dir}\")\n return 0"
|
|
44
|
+
],
|
|
45
|
+
"gate": ["bash", "run.sh", "smoke-user-scope-citizen"],
|
|
46
|
+
"timeoutSeconds": 300,
|
|
47
|
+
"signature": "[QK:BROAD-INSTALL-COLLAPSE]",
|
|
48
|
+
"signatureSource": "scripts/smoke-user-scope-citizen.sh"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"claim": "BROAD-DOCTOR-GREEN",
|
|
52
|
+
"title": "doctor-pi-package judges the recorded owner through the broad entwurf-shape matcher, so a store whose exact owner entry is GONE but which still holds some other entwurf-shaped root reads owned/exit-0 while the inverse refuses the very same state — contradictory verdicts on one store (#86 C2 corrective amendment, blocker 2)",
|
|
53
|
+
"subject": "scripts/register-pi-package.py",
|
|
54
|
+
"find": [
|
|
55
|
+
" owner_exact, owner_extra = _classify_user_owner_entries(settings_path, owner)\n if len(owner_exact) != 1 or owner_extra:\n print(f\"doctor-pi-package: mismatch — owner state records {owner} but the settings hold \"\n f\"{len(owner_exact)} exact entr{'y' if len(owner_exact) == 1 else 'ies'} and \"\n f\"{len(owner_extra)} additional entwurf-shaped entr{'y' if len(owner_extra) == 1 else 'ies'} \"\n \"(expected exactly one exact entry and no other entwurf-shaped root)\")\n return 1"
|
|
56
|
+
],
|
|
57
|
+
"replace": [
|
|
58
|
+
" owner_matches = _entwurf_matches(packages, owner, settings_dir)\n if not owner_matches:\n print(f\"doctor-pi-package: mismatch — owner state records {owner} but no packages[] entry names it\")\n return 1"
|
|
59
|
+
],
|
|
60
|
+
"gate": ["bash", "run.sh", "smoke-user-scope-citizen"],
|
|
61
|
+
"timeoutSeconds": 300,
|
|
62
|
+
"signature": "[QK:BROAD-DOCTOR-GREEN]",
|
|
63
|
+
"signatureSource": "scripts/smoke-user-scope-citizen.sh"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"claim": "PROVIDER-ROOT-TYPE-FAIL-OPEN",
|
|
67
|
+
"title": "the typed installerRoot classifier reports a wrong-TYPE value (number, empty string, bool, object, array) as LEGACY instead of CORRUPT, so provider install adopts and rebinds an unattributed state, remove proceeds on it, and the doctor coupling stays silent — the fall-through B reproduced against 096a5cd (#86 C2 corrective amendment, blocker 3)",
|
|
68
|
+
"subject": "scripts/pi_settings_io.py",
|
|
69
|
+
"find": [" if isinstance(root, str) and root:\n return \"owner\", root\n return \"corrupt\", None"],
|
|
70
|
+
"replace": [
|
|
71
|
+
" if isinstance(root, str) and root:\n return \"owner\", root\n return \"legacy\", None"
|
|
72
|
+
],
|
|
73
|
+
"gate": ["bash", "run.sh", "smoke-user-scope-citizen"],
|
|
74
|
+
"timeoutSeconds": 300,
|
|
75
|
+
"signature": "[QK:PROVIDER-ROOT-TYPE-FAIL-OPEN]",
|
|
76
|
+
"signatureSource": "scripts/smoke-user-scope-citizen.sh"
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"claim": "MISSING-OWNER-AUTO-TAKEOVER",
|
|
80
|
+
"title": "a normal install auto-claims a MISSING owner's registration, turning every moved or deleted checkout into a silent ownership transfer instead of the named missing-owner verdict plus explicit takeover/orphan path",
|
|
81
|
+
"subject": "scripts/register-pi-package.py",
|
|
82
|
+
"find": [
|
|
83
|
+
" _refuse(6, \"register-pi-package: the user-scope entwurf registration is owned by a MISSING root: \"\n f\"{owner} (doctor verdict: missing-owner). Normal install/setup still refuses — zero settings \"\n \"bytes written. Use './run.sh takeover-user-scope' to claim it explicitly, or \"\n \"'./run.sh remove-user-scope' for the aligned orphan cleanup.\")"
|
|
84
|
+
],
|
|
85
|
+
"replace": [" return register_user(settings_path, repo_dir_arg, state_path, True)"],
|
|
86
|
+
"gate": ["bash", "run.sh", "smoke-user-scope-citizen"],
|
|
87
|
+
"timeoutSeconds": 300,
|
|
88
|
+
"signature": "[QK:MISSING-OWNER-AUTO-TAKEOVER]",
|
|
89
|
+
"signatureSource": "scripts/smoke-user-scope-citizen.sh"
|
|
90
|
+
}
|
|
91
|
+
]
|
|
92
|
+
}
|