@junghanacs/entwurf 0.14.2 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +8 -5
- package/CHANGELOG.md +79 -0
- package/DELIVERY.md +72 -59
- package/README.md +55 -4
- package/VERIFY.md +2 -2
- package/demo/README.md +1 -1
- package/docs/acp-backend-rail.md +1 -1
- package/docs/external-mcp-host.md +26 -4
- package/docs/setup-clean-host.md +3 -3
- 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 +334 -16
- package/scripts/check-copilot-birth-hook.ts +497 -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 +92 -23
- package/scripts/check-gate-qualification.ts +7 -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/copilot-bridge-doctor.sh +209 -0
- package/scripts/copilot-bridge-install.sh +224 -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 +4 -1
- package/scripts/meta-bridge-hook-log.sh +17 -5
- package/scripts/mutants/capability-cache.json +64 -0
- package/scripts/mutants/copilot-birth.json +155 -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/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/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/tsconfig.json +1 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"lane": "copilot-launch",
|
|
4
|
+
"mutants": [
|
|
5
|
+
{
|
|
6
|
+
"claim": "COPILOT-LAUNCH-REQUIRES-RECEIVER",
|
|
7
|
+
"title": "launching with the scan flag while no receiver unit is installed promises a doorbell nothing can ring — the session looks managed and is permanently undeliverable, with no error anywhere",
|
|
8
|
+
"subject": "scripts/copilot-launch.sh",
|
|
9
|
+
"find": [
|
|
10
|
+
"[ -f \"$STATE_FILE\" ] \\",
|
|
11
|
+
"\t|| fail \"no receiver install-state at $STATE_FILE.",
|
|
12
|
+
" '$VENDOR_CMD' launched with $FLAG_ENV=$FLAG_VALUE would scan for an extension that is",
|
|
13
|
+
" not there, and the session would never become deliverable. Run: $REPAIR\""
|
|
14
|
+
],
|
|
15
|
+
"replace": [
|
|
16
|
+
"if [ ! -f \"$STATE_FILE\" ]; then",
|
|
17
|
+
"\tmkdir -p \"$STATE_DIR\"",
|
|
18
|
+
"\tprintf '{\"schemaVersion\":1,\"unit\":\"%s\",\"path\":\"%s\"}\\n' \"$UNIT\" \"$DEST\" > \"$STATE_FILE\"",
|
|
19
|
+
"fi"
|
|
20
|
+
],
|
|
21
|
+
"gate": ["bash", "run.sh", "check-copilot-launch"],
|
|
22
|
+
"timeoutSeconds": 300,
|
|
23
|
+
"signature": "[QK:COPILOT-LAUNCH-REQUIRES-RECEIVER]",
|
|
24
|
+
"signatureSource": "scripts/check-copilot-launch.ts"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"claim": "COPILOT-LAUNCH-REFUSES-PATH-MISMATCH",
|
|
28
|
+
"title": "accepting an install-state that names a different extensions root lets the CLI scan a directory nobody installed into, while every artifact check stays green",
|
|
29
|
+
"subject": "scripts/copilot-launch.sh",
|
|
30
|
+
"find": [
|
|
31
|
+
"[ \"$claimed_path\" = \"$DEST\" ] \\",
|
|
32
|
+
"\t|| fail \"receiver install-state points at '$claimed_path' but this environment scans '$DEST'.",
|
|
33
|
+
" The CLI would read a different extensions root than the one that was installed",
|
|
34
|
+
" (COPILOT_EXTENSIONS_DIR / HOME differ from install time). Run: $REPAIR\""
|
|
35
|
+
],
|
|
36
|
+
"replace": ["# whichever root the state names is close enough"],
|
|
37
|
+
"gate": ["bash", "run.sh", "check-copilot-launch"],
|
|
38
|
+
"timeoutSeconds": 300,
|
|
39
|
+
"signature": "[QK:COPILOT-LAUNCH-REFUSES-PATH-MISMATCH]",
|
|
40
|
+
"signatureSource": "scripts/check-copilot-launch.ts"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"claim": "COPILOT-LAUNCH-SETS-EXTENSIONS-TOKEN",
|
|
44
|
+
"title": "not guaranteeing the EXTENSIONS token makes the managed launch identical to a plain one — Copilot skips the extension scan silently and the whole surface is decoration",
|
|
45
|
+
"subject": "scripts/copilot-launch.sh",
|
|
46
|
+
"find": ["[ \"$seen_extensions\" -eq 1 ] || flags_out=\"${flags_out:+$flags_out,}$FLAG_VALUE\""],
|
|
47
|
+
"replace": ["# assume the operator already set it"],
|
|
48
|
+
"gate": ["bash", "run.sh", "check-copilot-launch"],
|
|
49
|
+
"timeoutSeconds": 300,
|
|
50
|
+
"signature": "[QK:COPILOT-LAUNCH-SETS-EXTENSIONS-TOKEN]",
|
|
51
|
+
"signatureSource": "scripts/check-copilot-launch.ts"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"claim": "COPILOT-LAUNCH-PRESERVES-OPERATOR-TOKENS",
|
|
55
|
+
"title": "overwriting the feature-flag variable instead of extending it silently disables every other vendor feature flag the operator had enabled",
|
|
56
|
+
"subject": "scripts/copilot-launch.sh",
|
|
57
|
+
"find": ["export \"$FLAG_ENV=$flags_out\""],
|
|
58
|
+
"replace": ["export \"$FLAG_ENV=$FLAG_VALUE\""],
|
|
59
|
+
"gate": ["bash", "run.sh", "check-copilot-launch"],
|
|
60
|
+
"timeoutSeconds": 300,
|
|
61
|
+
"signature": "[QK:COPILOT-LAUNCH-PRESERVES-OPERATOR-TOKENS]",
|
|
62
|
+
"signatureSource": "scripts/check-copilot-launch.ts"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"claim": "COPILOT-LAUNCH-DEFAULTS-BEFORE-TERMINATOR",
|
|
66
|
+
"title": "appending the injected defaults after the argv terminator turns them from options into positional data the vendor reads as prompt text, so the managed profile silently stops applying",
|
|
67
|
+
"subject": "scripts/copilot-launch.sh",
|
|
68
|
+
"find": [
|
|
69
|
+
"final_args=()",
|
|
70
|
+
"for (( i = 0; i < scan_end; i++ )); do final_args+=(\"${args[$i]}\"); done",
|
|
71
|
+
"if [ \"${#injected[@]}\" -gt 0 ]; then final_args+=(\"${injected[@]}\"); fi",
|
|
72
|
+
"for (( i = scan_end; i < ${#args[@]}; i++ )); do final_args+=(\"${args[$i]}\"); done"
|
|
73
|
+
],
|
|
74
|
+
"replace": [
|
|
75
|
+
"final_args=(\"${args[@]}\")",
|
|
76
|
+
"if [ \"${#injected[@]}\" -gt 0 ]; then final_args+=(\"${injected[@]}\"); fi"
|
|
77
|
+
],
|
|
78
|
+
"gate": ["bash", "run.sh", "check-copilot-launch"],
|
|
79
|
+
"timeoutSeconds": 300,
|
|
80
|
+
"signature": "[QK:COPILOT-LAUNCH-DEFAULTS-BEFORE-TERMINATOR]",
|
|
81
|
+
"signatureSource": "scripts/check-copilot-launch.ts"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"claim": "COPILOT-LAUNCH-NO-DEFAULT-OVER-EXPLICIT-POLICY",
|
|
85
|
+
"title": "treating only the broadening flags as explicit policy means an operator who NARROWS permissions (--allow-url, --deny-tool, --excluded-tools) still gets --yolo appended — the managed default silently widens exactly what they were restricting",
|
|
86
|
+
"subject": "scripts/copilot-launch.sh",
|
|
87
|
+
"find": ["\t--allow-url", "\t--deny-url", "\t--available-tools", "\t--excluded-tools"],
|
|
88
|
+
"replace": ["\t# narrowing flags are not policy"],
|
|
89
|
+
"gate": ["bash", "run.sh", "check-copilot-launch"],
|
|
90
|
+
"timeoutSeconds": 300,
|
|
91
|
+
"signature": "[QK:COPILOT-LAUNCH-NO-DEFAULT-OVER-EXPLICIT-POLICY]",
|
|
92
|
+
"signatureSource": "scripts/check-copilot-launch.ts"
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"claim": "COPILOT-LAUNCH-PREFIX-IS-NOT-POLICY",
|
|
96
|
+
"title": "matching policy flags by prefix instead of exactly makes --allow-all-mcp-server-instructions read as --allow-all, so a prompt-content flag suppresses the managed permission default",
|
|
97
|
+
"subject": "scripts/copilot-launch.sh",
|
|
98
|
+
"find": ["\t\tif [ \"$head\" = \"$ov\" ]; then"],
|
|
99
|
+
"replace": ["\t\tif case \"$head\" in \"$ov\"*) true ;; *) false ;; esac; then"],
|
|
100
|
+
"gate": ["bash", "run.sh", "check-copilot-launch"],
|
|
101
|
+
"timeoutSeconds": 300,
|
|
102
|
+
"signature": "[QK:COPILOT-LAUNCH-PREFIX-IS-NOT-POLICY]",
|
|
103
|
+
"signatureSource": "scripts/check-copilot-launch.ts"
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"claim": "COPILOT-LAUNCH-KEEPS-OPERATOR-MODEL",
|
|
107
|
+
"title": "injecting the default model regardless of what the operator asked for silently overrides an explicit --model",
|
|
108
|
+
"subject": "scripts/copilot-launch.sh",
|
|
109
|
+
"find": ["\t\thas_model=1"],
|
|
110
|
+
"replace": ["\t\thas_model=0"],
|
|
111
|
+
"gate": ["bash", "run.sh", "check-copilot-launch"],
|
|
112
|
+
"timeoutSeconds": 300,
|
|
113
|
+
"signature": "[QK:COPILOT-LAUNCH-KEEPS-OPERATOR-MODEL]",
|
|
114
|
+
"signatureSource": "scripts/check-copilot-launch.ts"
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"claim": "COPILOT-LAUNCH-EXECS-NOT-FORKS",
|
|
118
|
+
"title": "running the vendor as a child instead of exec'ing it leaves an entwurf shell sitting between the operator's terminal and the CLI, owning a pid and a signal path that belong to the vendor",
|
|
119
|
+
"subject": "scripts/copilot-launch.sh",
|
|
120
|
+
"find": ["exec \"$copilot_bin\" \"${final_args[@]}\""],
|
|
121
|
+
"replace": ["\"$copilot_bin\" \"${final_args[@]}\""],
|
|
122
|
+
"gate": ["bash", "run.sh", "check-copilot-launch"],
|
|
123
|
+
"timeoutSeconds": 300,
|
|
124
|
+
"signature": "[QK:COPILOT-LAUNCH-EXECS-NOT-FORKS]",
|
|
125
|
+
"signatureSource": "scripts/check-copilot-launch.ts"
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"claim": "COPILOT-LAUNCH-REFUSES-RECURSION",
|
|
129
|
+
"title": "dropping the sentinel lets a PATH entry that calls back into the managed launch spin forever; the operator sees a hung terminal, not an error",
|
|
130
|
+
"subject": "scripts/copilot-launch.sh",
|
|
131
|
+
"find": ["if [ -n \"${ENTWURF_COPILOT_LAUNCH_ACTIVE:-}\" ]; then"],
|
|
132
|
+
"replace": ["if false; then"],
|
|
133
|
+
"gate": ["bash", "run.sh", "check-copilot-launch"],
|
|
134
|
+
"timeoutSeconds": 300,
|
|
135
|
+
"signature": "[QK:COPILOT-LAUNCH-REFUSES-RECURSION]",
|
|
136
|
+
"signatureSource": "scripts/check-copilot-launch.ts"
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"claim": "COPILOT-LAUNCH-REFUSES-ABSENT-VENDOR",
|
|
140
|
+
"title": "not checking that the vendor resolved turns a missing CLI into an opaque exec failure instead of the one sentence that says what is missing",
|
|
141
|
+
"subject": "scripts/copilot-launch.sh",
|
|
142
|
+
"find": [
|
|
143
|
+
"[ -n \"$copilot_bin\" ] \\",
|
|
144
|
+
"\t|| fail \"no '$VENDOR_CMD' executable found on PATH.",
|
|
145
|
+
" The managed launch runs the GitHub Copilot CLI; install it, or put it on PATH.\""
|
|
146
|
+
],
|
|
147
|
+
"replace": ["# an empty resolution will surface itself at exec time"],
|
|
148
|
+
"gate": ["bash", "run.sh", "check-copilot-launch"],
|
|
149
|
+
"timeoutSeconds": 300,
|
|
150
|
+
"signature": "[QK:COPILOT-LAUNCH-REFUSES-ABSENT-VENDOR]",
|
|
151
|
+
"signatureSource": "scripts/check-copilot-launch.ts"
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
"claim": "COPILOT-LAUNCH-KEEPS-CALLER-CWD",
|
|
155
|
+
"title": "dispatching the managed launch through the repo-scoped subshell every neighbouring verb uses would start the vendor in the entwurf checkout instead of the operator's own directory",
|
|
156
|
+
"subject": "run.sh",
|
|
157
|
+
"find": [" shift || true", " exec bash \"$REPO_DIR/scripts/copilot-launch.sh\" \"$@\""],
|
|
158
|
+
"replace": [" shift || true", " (cd \"$REPO_DIR\" && bash scripts/copilot-launch.sh \"$@\")"],
|
|
159
|
+
"gate": ["bash", "run.sh", "check-copilot-launch"],
|
|
160
|
+
"timeoutSeconds": 300,
|
|
161
|
+
"signature": "[QK:COPILOT-LAUNCH-KEEPS-CALLER-CWD]",
|
|
162
|
+
"signatureSource": "scripts/check-copilot-launch.ts"
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
"claim": "COPILOT-LAUNCH-STRIPS-PI-IDENTITY",
|
|
166
|
+
"title": "the managed launch forwards inherited PI_SESSION_ID/PI_AGENT_ID — the Copilot session and every child it spawns then speak under the PARENT pi citizen's garden id, with a perfectly correct native marker sitting unused beside it",
|
|
167
|
+
"subject": "scripts/copilot-launch.sh",
|
|
168
|
+
"find": ["unset PI_SESSION_ID PI_AGENT_ID\n"],
|
|
169
|
+
"replace": [""],
|
|
170
|
+
"gate": ["bash", "run.sh", "check-copilot-launch"],
|
|
171
|
+
"timeoutSeconds": 300,
|
|
172
|
+
"signature": "[QK:COPILOT-LAUNCH-STRIPS-PI-IDENTITY]",
|
|
173
|
+
"signatureSource": "scripts/check-copilot-launch.ts"
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
"claim": "COPILOT-LAUNCH-STRIPS-BOTH-CARRIERS",
|
|
177
|
+
"title": "only one half of the identity pair is cleared — the failure merely changes wording while a carrier stays behind for the next partial reader to believe",
|
|
178
|
+
"subject": "scripts/copilot-launch.sh",
|
|
179
|
+
"find": ["unset PI_SESSION_ID PI_AGENT_ID"],
|
|
180
|
+
"replace": ["unset PI_SESSION_ID"],
|
|
181
|
+
"gate": ["bash", "run.sh", "check-copilot-launch"],
|
|
182
|
+
"timeoutSeconds": 300,
|
|
183
|
+
"signature": "[QK:COPILOT-LAUNCH-STRIPS-BOTH-CARRIERS]",
|
|
184
|
+
"signatureSource": "scripts/check-copilot-launch.ts"
|
|
185
|
+
}
|
|
186
|
+
]
|
|
187
|
+
}
|
|
@@ -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
|
+
}
|