@junghanacs/entwurf 0.40.0 → 0.41.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 +4 -2
- package/BASELINE.md +55 -2
- package/CHANGELOG.md +321 -0
- package/CONTRIBUTING.md +1 -1
- package/DELIVERY.md +18 -2
- package/FAQ.md +8 -4
- package/README.md +26 -8
- package/VERIFY.md +13 -10
- package/docs/acp-backend-rail.md +5 -1
- package/docs/external-mcp-host.md +8 -6
- package/docs/fresh-cut-policy.md +9 -1
- package/docs/setup-clean-host.md +24 -8
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +81 -19
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/cc-retire-pidfd.py +236 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/cc-retire-request.js +473 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/control-retire.js +125 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-retire-production.js +149 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-retire.js +158 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-surface.js +2 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-visible-resume.js +283 -20
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/fresh-call-composition.js +7 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/fresh-call-dispatch.js +47 -3
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/herdr-fresh-call.js +3 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js +96 -16
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +5 -29
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-launch.js +28 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-resume-call.js +26 -11
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/pi-retire-request.js +76 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/resume-launch-identity.js +67 -17
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/retirement-event.js +211 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-hook.js +124 -10
- package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-pi-durable.js +53 -2
- package/mcp/entwurf-bridge/dist/scripts/meta-facts.js +1 -1
- package/mcp/entwurf-bridge/dist/scripts/probe-bridge-command.js +1 -0
- package/mcp/entwurf-bridge/src/index.ts +93 -19
- package/mcp/entwurf-bridge/test.sh +2 -2
- package/mcp/entwurf-bridge/tsconfig.build.json +10 -1
- package/package.json +4 -4
- package/pi/meta-bridge/entwurf-meta-receive/scripts/hook-launch.sh +3 -3
- package/pi-extensions/entwurf-control.ts +55 -20
- package/pi-extensions/lib/cc-retire-pidfd.py +236 -0
- package/pi-extensions/lib/cc-retire-request.ts +553 -0
- package/pi-extensions/lib/control-retire.ts +195 -0
- package/pi-extensions/lib/entwurf-control-rpc.ts +17 -1
- package/pi-extensions/lib/entwurf-v2-retire-production.ts +227 -0
- package/pi-extensions/lib/entwurf-v2-retire.ts +269 -0
- package/pi-extensions/lib/entwurf-v2-surface.ts +2 -1
- package/pi-extensions/lib/entwurf-v2-visible-resume.ts +390 -36
- package/pi-extensions/lib/fresh-call-composition.ts +15 -0
- package/pi-extensions/lib/fresh-call-dispatch.ts +45 -4
- package/pi-extensions/lib/herdr-fresh-call.ts +5 -0
- package/pi-extensions/lib/meta-session.ts +140 -16
- package/pi-extensions/lib/mux-fresh-call.ts +7 -27
- package/pi-extensions/lib/mux-launch.ts +29 -0
- package/pi-extensions/lib/mux-resume-call.ts +33 -12
- package/pi-extensions/lib/pi-retire-request.ts +111 -0
- package/pi-extensions/lib/resume-launch-identity.ts +104 -18
- package/pi-extensions/lib/retirement-event.ts +253 -0
- package/pi-extensions/meta-bridge-hook.ts +142 -13
- package/pi-extensions/meta-bridge-pi-durable.ts +56 -2
- package/run.sh +78 -7
- package/scripts/agy-bridge-config.py +4 -1
- package/scripts/build-bridge.sh +14 -0
- package/scripts/check-capability-bundle-reach.ts +135 -2
- package/scripts/check-entwurf-bridge-boot.ts +2 -1
- package/scripts/check-entwurf-fact-provider.ts +1 -0
- package/scripts/check-entwurf-v2-production.ts +2 -0
- package/scripts/check-entwurf-v2-surface.ts +7 -0
- package/scripts/check-gate-qualification.ts +14 -8
- package/scripts/check-herdr-activation.ts +2 -2
- package/scripts/check-herdr-placement.ts +1 -0
- package/scripts/check-hook-launch-topology.ts +6 -15
- package/scripts/check-install-container.sh +2 -2
- package/scripts/check-meta-doctor-oracle.sh +27 -1
- package/scripts/check-meta-facts.ts +8 -3
- package/scripts/check-meta-hook-session-switch.ts +230 -13
- package/scripts/check-meta-identity-consumers.ts +1 -0
- package/scripts/check-meta-listing.ts +1 -0
- package/scripts/check-meta-manifest-schema.py +1 -1
- package/scripts/check-meta-session.ts +31 -0
- package/scripts/check-meta-v3-record.ts +55 -1
- package/scripts/check-mux-resume-call.ts +35 -9
- package/scripts/check-peer-facts.ts +1 -0
- package/scripts/check-pi-durable-contact.ts +3 -1
- package/scripts/check-pi-durable-send.ts +4 -2
- package/scripts/check-pi-mcp-bridge.ts +1 -0
- package/scripts/check-resume-launch-identity.ts +62 -23
- package/scripts/lib/meta-hook-bundle.ts +72 -0
- package/scripts/meta-bridge-claude-floor.sh +13 -9
- package/scripts/meta-bridge-doctor.sh +17 -0
- package/scripts/meta-bridge-install.sh +6 -0
- package/scripts/meta-facts.ts +1 -1
- package/scripts/mutants/bridge-boot-resume.json +5 -2
- package/scripts/mutants/cc-retire-pidfd.json +129 -0
- package/scripts/mutants/cc-retire-request.json +171 -0
- package/scripts/mutants/control-retire.json +105 -0
- package/scripts/mutants/copilot-receive.json +2 -6
- package/scripts/mutants/entwurf-v2-retire.json +476 -0
- package/scripts/mutants/fresh-call-dispatch.json +37 -0
- package/scripts/mutants/meta-hook-session-switch.json +88 -0
- package/scripts/mutants/meta-identity.json +57 -0
- package/scripts/mutants/mux-fresh-call.json +1 -1
- package/scripts/mutants/mux-resume-call.json +40 -4
- package/scripts/mutants/pi-durable.json +79 -0
- package/scripts/mutants/pi-retire-request.json +72 -0
- package/scripts/mutants/resume-launch-identity.json +35 -8
- package/scripts/mutants/retirement-event.json +195 -0
- package/scripts/mutants/v2-surface.json +16 -0
- package/scripts/mutants/v2-visible-resume.json +283 -13
- package/scripts/probe-bridge-command.ts +1 -0
- package/scripts/smoke-agy-install-state.sh +2 -2
- package/scripts/smoke-copilot-mcp-state.sh +1 -1
- package/scripts/smoke-herdr-raw-install-live.sh +5 -4
- package/scripts/smoke-meta-install-state.sh +4 -4
- package/scripts/smoke-mux-lifecycle-live.ts +10 -10
- package/scripts/smoke-omp-mcp-state.sh +1 -1
- package/scripts/smoke-pi-durable-fresh-live.ts +5 -3
- package/scripts/smoke-pi-provider-state.sh +3 -3
|
@@ -48,6 +48,63 @@
|
|
|
48
48
|
"timeoutSeconds": 120,
|
|
49
49
|
"signature": "[QK:META-CREATE-PUBLISHED-CLEANUP]",
|
|
50
50
|
"signatureSource": "scripts/check-meta-session.ts"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"claim": "META-V3-INCARNATION-ABSENT-NULL",
|
|
54
|
+
"title": "a V3 body without attachIncarnation is refused, so every untouched record becomes unreadable",
|
|
55
|
+
"subject": "pi-extensions/lib/meta-session.ts",
|
|
56
|
+
"find": [
|
|
57
|
+
"\t\tattachIncarnation: requireAttachIncarnation(obj.attachIncarnation === undefined ? null : obj.attachIncarnation),"
|
|
58
|
+
],
|
|
59
|
+
"replace": ["\t\tattachIncarnation: requireAttachIncarnation(obj.attachIncarnation),"],
|
|
60
|
+
"gate": ["bash", "run.sh", "check-meta-v3-record"],
|
|
61
|
+
"timeoutSeconds": 120,
|
|
62
|
+
"signature": "[QK:META-V3-INCARNATION-ABSENT-NULL]",
|
|
63
|
+
"signatureSource": "scripts/check-meta-v3-record.ts"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"claim": "META-V3-INCARNATION-MALFORMED",
|
|
67
|
+
"title": "any string is accepted as a binding",
|
|
68
|
+
"subject": "pi-extensions/lib/meta-session.ts",
|
|
69
|
+
"find": ["\tif (typeof value !== \"string\" || !ATTACH_INCARNATION_RE.test(value)) {"],
|
|
70
|
+
"replace": ["\tif (typeof value !== \"string\") {"],
|
|
71
|
+
"gate": ["bash", "run.sh", "check-meta-v3-record"],
|
|
72
|
+
"timeoutSeconds": 120,
|
|
73
|
+
"signature": "[QK:META-V3-INCARNATION-MALFORMED]",
|
|
74
|
+
"signatureSource": "scripts/check-meta-v3-record.ts"
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"claim": "META-V3-INCARNATION-ALWAYS-EMITTED",
|
|
78
|
+
"title": "the serializer drops attachIncarnation, so a committed binding never reaches disk",
|
|
79
|
+
"subject": "pi-extensions/lib/meta-session.ts",
|
|
80
|
+
"find": ["\t\tattachIncarnation: identity.attachIncarnation,"],
|
|
81
|
+
"replace": [""],
|
|
82
|
+
"gate": ["bash", "run.sh", "check-meta-v3-record"],
|
|
83
|
+
"timeoutSeconds": 120,
|
|
84
|
+
"signature": "[QK:META-V3-INCARNATION-ALWAYS-EMITTED]",
|
|
85
|
+
"signatureSource": "scripts/check-meta-v3-record.ts"
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"claim": "META-UPSERT-PRESERVES-INCARNATION",
|
|
89
|
+
"title": "an attach that does not mint clears the binding",
|
|
90
|
+
"subject": "pi-extensions/lib/meta-session.ts",
|
|
91
|
+
"find": ["\t\t\t? existing.attachIncarnation"],
|
|
92
|
+
"replace": ["\t\t\t? null"],
|
|
93
|
+
"gate": ["bash", "run.sh", "check-meta-v3-record"],
|
|
94
|
+
"timeoutSeconds": 120,
|
|
95
|
+
"signature": "[QK:META-UPSERT-PRESERVES-INCARNATION]",
|
|
96
|
+
"signatureSource": "scripts/check-meta-v3-record.ts"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"claim": "META-UPSERT-EXPECT-ACTION",
|
|
100
|
+
"title": "upsert ignores expectAction, so a raced create silently becomes an unlocked attach",
|
|
101
|
+
"subject": "pi-extensions/lib/meta-session.ts",
|
|
102
|
+
"find": ["\tif (opts.expectAction !== undefined && decision.action !== opts.expectAction) {"],
|
|
103
|
+
"replace": ["\tif (false) {"],
|
|
104
|
+
"gate": ["bash", "run.sh", "check-meta-session"],
|
|
105
|
+
"timeoutSeconds": 120,
|
|
106
|
+
"signature": "[QK:META-UPSERT-EXPECT-ACTION]",
|
|
107
|
+
"signatureSource": "scripts/check-meta-session.ts"
|
|
51
108
|
}
|
|
52
109
|
]
|
|
53
110
|
}
|
|
@@ -151,7 +151,7 @@
|
|
|
151
151
|
"title": "MCP drops the required model field from fresh-call while the composition still expects one",
|
|
152
152
|
"subject": "mcp/entwurf-bridge/src/index.ts",
|
|
153
153
|
"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, provider/<exact catalog model id> for pi-durable, a Claude Code model id/alias, or a Copilot/OMP/Codex model name.\",\n\t\t\t),\n"
|
|
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"
|
|
155
155
|
],
|
|
156
156
|
"replace": [""],
|
|
157
157
|
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
@@ -72,8 +72,11 @@
|
|
|
72
72
|
"claim": "MUXRESUME-NO-IDENTITY",
|
|
73
73
|
"title": "identity vocabulary enters the placement module, which is the first step of a leaf learning whose citizen it is opening",
|
|
74
74
|
"subject": "pi-extensions/lib/mux-resume-call.ts",
|
|
75
|
-
"find": ["export const RESUME_CALL_RUNTIME = \"pi\";"],
|
|
76
|
-
"replace": [
|
|
75
|
+
"find": ["export const RESUME_CALL_RUNTIME = { pi: \"pi\", \"claude-code\": \"claude\" } as const;"],
|
|
76
|
+
"replace": [
|
|
77
|
+
"export const RESUME_CALL_RUNTIME = { pi: \"pi\", \"claude-code\": \"claude\" } as const;",
|
|
78
|
+
"export const RESUME_CALL_TARGET_KEY = \"gardenId\";"
|
|
79
|
+
],
|
|
77
80
|
"gate": ["bash", "run.sh", "check-mux-resume-call"],
|
|
78
81
|
"timeoutSeconds": 60,
|
|
79
82
|
"signature": "[QK:MUXRESUME-NO-IDENTITY]",
|
|
@@ -97,10 +100,10 @@
|
|
|
97
100
|
"title": "the MCP description drops the dormant-only scope, inviting callers to aim the verb at live citizens it will refuse",
|
|
98
101
|
"subject": "mcp/entwurf-bridge/src/index.ts",
|
|
99
102
|
"find": [
|
|
100
|
-
"\t\"Reopen ONE DORMANT pi citizen under its OWN garden id, in a visible window in the operator's
|
|
103
|
+
"\t\"Reopen ONE DORMANT pi or Claude Code citizen under its OWN garden id, in a visible window in the operator's \" +"
|
|
101
104
|
],
|
|
102
105
|
"replace": [
|
|
103
|
-
"\t\"Reopen ONE pi citizen under its OWN garden id, in a visible window in the operator's
|
|
106
|
+
"\t\"Reopen ONE pi or Claude Code citizen under its OWN garden id, in a visible window in the operator's \" +"
|
|
104
107
|
],
|
|
105
108
|
"gate": ["bash", "run.sh", "check-mux-resume-call"],
|
|
106
109
|
"timeoutSeconds": 60,
|
|
@@ -119,6 +122,39 @@
|
|
|
119
122
|
"timeoutSeconds": 60,
|
|
120
123
|
"signature": "[QK:MUXRESUME-MCP-TARGET-PASSTHROUGH]",
|
|
121
124
|
"signatureSource": "scripts/check-mux-resume-call.ts"
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"claim": "MUXRESUME-RUNTIME-TABLE",
|
|
128
|
+
"title": "a Claude Code resume is started under pi, so the window opens the wrong runtime on the right session id",
|
|
129
|
+
"subject": "pi-extensions/lib/mux-resume-call.ts",
|
|
130
|
+
"find": ["export const RESUME_CALL_RUNTIME = { pi: \"pi\", \"claude-code\": \"claude\" } as const;"],
|
|
131
|
+
"replace": ["export const RESUME_CALL_RUNTIME = { pi: \"pi\", \"claude-code\": \"pi\" } as const;"],
|
|
132
|
+
"gate": ["bash", "run.sh", "check-mux-resume-call"],
|
|
133
|
+
"timeoutSeconds": 60,
|
|
134
|
+
"signature": "[QK:MUXRESUME-RUNTIME-TABLE]",
|
|
135
|
+
"signatureSource": "scripts/check-mux-resume-call.ts"
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"claim": "MUXRESUME-HERDR-REFUSED",
|
|
139
|
+
"title": "inside herdr the tmux resume rail no longer refuses, so an ambient TMUX opens the window where the operator is not looking",
|
|
140
|
+
"subject": "pi-extensions/lib/mux-resume-call.ts",
|
|
141
|
+
"find": ["\tif (env.HERDR_ENV === \"1\") return { ok: false, reason: \"herdr-resume-unsupported\" };"],
|
|
142
|
+
"replace": [""],
|
|
143
|
+
"gate": ["bash", "run.sh", "check-mux-resume-call"],
|
|
144
|
+
"timeoutSeconds": 60,
|
|
145
|
+
"signature": "[QK:MUXRESUME-HERDR-REFUSED]",
|
|
146
|
+
"signatureSource": "scripts/check-mux-resume-call.ts"
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"claim": "MUXRESUME-ARGV-IDENTITY-SCRUB",
|
|
150
|
+
"title": "the resume window stops emptying the inherited pi identity carriers, so a resumed Claude Code presents a stale identity to its bridge",
|
|
151
|
+
"subject": "pi-extensions/lib/mux-resume-call.ts",
|
|
152
|
+
"find": ["\t\t...SCRUBBED_INHERITED_ENV.flatMap((assignment) => [\"-e\", assignment]),"],
|
|
153
|
+
"replace": [""],
|
|
154
|
+
"gate": ["bash", "run.sh", "check-mux-resume-call"],
|
|
155
|
+
"timeoutSeconds": 60,
|
|
156
|
+
"signature": "[QK:MUXRESUME-ARGV-IDENTITY-SCRUB]",
|
|
157
|
+
"signatureSource": "scripts/check-mux-resume-call.ts"
|
|
122
158
|
}
|
|
123
159
|
]
|
|
124
160
|
}
|
|
@@ -2,6 +2,61 @@
|
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"lane": "pi-durable",
|
|
4
4
|
"mutants": [
|
|
5
|
+
{
|
|
6
|
+
"claim": "PI-DURABLE-RETIRE-CALL",
|
|
7
|
+
"title": "retirement alone drops the actual request cancellation signal",
|
|
8
|
+
"subject": "pi-extensions/meta-bridge-pi-durable.ts",
|
|
9
|
+
"find": [
|
|
10
|
+
"\t\t\tconst call = () => client.callTool(name, (args ?? {}) as Record<string, unknown>, context?.abortSignal);"
|
|
11
|
+
],
|
|
12
|
+
"replace": [
|
|
13
|
+
"\t\t\tconst call = () => client.callTool(name, (args ?? {}) as Record<string, unknown>, name === \"entwurf_retire_call\" ? undefined : context?.abortSignal);"
|
|
14
|
+
],
|
|
15
|
+
"gate": ["bash", "run.sh", "check-pi-durable-beside"],
|
|
16
|
+
"timeoutSeconds": 300,
|
|
17
|
+
"signature": "[QK:PI-DURABLE-RETIRE-CALL]",
|
|
18
|
+
"signatureSource": "pi-extensions/meta-bridge-pi-durable.test.ts",
|
|
19
|
+
"build": {
|
|
20
|
+
"argv": ["bash", "scripts/build-bridge.sh"],
|
|
21
|
+
"output": "mcp/entwurf-bridge/dist"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"claim": "PI-DURABLE-RETIRE-POLICY",
|
|
26
|
+
"title": "the native retirement description treats actor identity as a standing grant",
|
|
27
|
+
"subject": "pi-extensions/meta-bridge-pi-durable.ts",
|
|
28
|
+
"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 \" +"
|
|
33
|
+
],
|
|
34
|
+
"gate": ["bash", "run.sh", "check-pi-durable-beside"],
|
|
35
|
+
"timeoutSeconds": 300,
|
|
36
|
+
"signature": "[QK:PI-DURABLE-RETIRE-POLICY]",
|
|
37
|
+
"signatureSource": "pi-extensions/meta-bridge-pi-durable.test.ts",
|
|
38
|
+
"build": {
|
|
39
|
+
"argv": ["bash", "scripts/build-bridge.sh"],
|
|
40
|
+
"output": "mcp/entwurf-bridge/dist"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"claim": "PI-DURABLE-RETIRE-CALL-REACHES-BRIDGE",
|
|
45
|
+
"title": "the compiled retirement handler discards the actual host-authoritative caller",
|
|
46
|
+
"subject": "mcp/entwurf-bridge/src/index.ts",
|
|
47
|
+
"find": [
|
|
48
|
+
"\t\t\t\t\tresolveCaller: async () => (await resolveAuthoritativeSender({ requestMeta: extra._meta }))?.envelope ?? null,"
|
|
49
|
+
],
|
|
50
|
+
"replace": ["\t\t\t\t\tresolveCaller: async () => null,"],
|
|
51
|
+
"gate": ["bash", "run.sh", "check-pi-durable-beside"],
|
|
52
|
+
"timeoutSeconds": 300,
|
|
53
|
+
"signature": "[QK:PI-DURABLE-RETIRE-CALL-REACHES-BRIDGE]",
|
|
54
|
+
"signatureSource": "pi-extensions/meta-bridge-pi-durable.test.ts",
|
|
55
|
+
"build": {
|
|
56
|
+
"argv": ["bash", "scripts/build-bridge.sh"],
|
|
57
|
+
"output": "mcp/entwurf-bridge/dist"
|
|
58
|
+
}
|
|
59
|
+
},
|
|
5
60
|
{
|
|
6
61
|
"claim": "PI-DURABLE-ENTRY-GUARD-FLOOR-SAFE",
|
|
7
62
|
"title": "the verifier's entry guard goes back to import.meta.main, which the floor's runner can misread as the process entry",
|
|
@@ -1223,6 +1278,30 @@
|
|
|
1223
1278
|
"signature": "[QK:PI-DURABLE-RESUME-CANCEL-OPENS-NOTHING]",
|
|
1224
1279
|
"signatureSource": "pi-extensions/lib/pi-durable-carrier.test.ts",
|
|
1225
1280
|
"build": { "argv": ["bash", "scripts/build-bridge.sh"], "output": "mcp/entwurf-bridge/dist" }
|
|
1281
|
+
},
|
|
1282
|
+
{
|
|
1283
|
+
"claim": "PI-DURABLE-RESUME-CALL-EXPOSED",
|
|
1284
|
+
"title": "entwurf_resume_call drops out of the durable contact's tools, so the coordinator citizen cannot reopen a dormant pi sibling",
|
|
1285
|
+
"subject": "pi-extensions/meta-bridge-pi-durable.ts",
|
|
1286
|
+
"find": ["\t\ttools: PI_DURABLE_TOOL_NAMES.map(tool),"],
|
|
1287
|
+
"replace": ["\t\ttools: PI_DURABLE_TOOL_NAMES.filter((name) => name !== \"entwurf_resume_call\").map(tool),"],
|
|
1288
|
+
"gate": ["bash", "run.sh", "check-pi-durable-beside"],
|
|
1289
|
+
"timeoutSeconds": 300,
|
|
1290
|
+
"signature": "[QK:PI-DURABLE-RESUME-CALL-EXPOSED]",
|
|
1291
|
+
"signatureSource": "pi-extensions/meta-bridge-pi-durable.test.ts",
|
|
1292
|
+
"build": { "argv": ["bash", "scripts/build-bridge.sh"], "output": "mcp/entwurf-bridge/dist" }
|
|
1293
|
+
},
|
|
1294
|
+
{
|
|
1295
|
+
"claim": "PI-DURABLE-RESUME-CALL-REACHES-BRIDGE",
|
|
1296
|
+
"title": "the production stdio connector stops carrying a tool's arguments: every stub-bridge case still passes, but the real bridge never sees the target the model named",
|
|
1297
|
+
"subject": "pi-extensions/meta-bridge-pi-durable.ts",
|
|
1298
|
+
"find": ["const result = await client.callTool({ name, arguments: args }, undefined, { signal });"],
|
|
1299
|
+
"replace": ["const result = await client.callTool({ name, arguments: {} }, undefined, { signal });"],
|
|
1300
|
+
"gate": ["bash", "run.sh", "check-pi-durable-beside"],
|
|
1301
|
+
"timeoutSeconds": 300,
|
|
1302
|
+
"signature": "[QK:PI-DURABLE-RESUME-CALL-REACHES-BRIDGE]",
|
|
1303
|
+
"signatureSource": "pi-extensions/meta-bridge-pi-durable.test.ts",
|
|
1304
|
+
"build": { "argv": ["bash", "scripts/build-bridge.sh"], "output": "mcp/entwurf-bridge/dist" }
|
|
1226
1305
|
}
|
|
1227
1306
|
]
|
|
1228
1307
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"lane": "pi-retire-request",
|
|
4
|
+
"mutants": [
|
|
5
|
+
{
|
|
6
|
+
"claim": "PI-RETIRE-PREFLIGHT",
|
|
7
|
+
"title": "a mismatched native session escapes preflight and sends a retirement RPC",
|
|
8
|
+
"subject": "pi-extensions/lib/pi-retire-request.ts",
|
|
9
|
+
"find": ["\t\ttarget.nativeSessionId !== expected.nativeSessionId ||\n"],
|
|
10
|
+
"replace": [""],
|
|
11
|
+
"gate": ["bash", "run.sh", "check-pi-retire-request"],
|
|
12
|
+
"timeoutSeconds": 600,
|
|
13
|
+
"signature": "[QK:PI-RETIRE-PREFLIGHT]",
|
|
14
|
+
"signatureSource": "pi-extensions/lib/pi-retire-request.test.ts"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"claim": "PI-RETIRE-WIRE",
|
|
18
|
+
"title": "the one fixed retirement request carries a different native session than the certified record",
|
|
19
|
+
"subject": "pi-extensions/lib/pi-retire-request.ts",
|
|
20
|
+
"find": ["{ type: \"retire\", gardenId: target.gardenId, nativeSessionId: target.nativeSessionId }"],
|
|
21
|
+
"replace": ["{ type: \"retire\", gardenId: target.gardenId, nativeSessionId: \"wrong-native-id\" }"],
|
|
22
|
+
"gate": ["bash", "run.sh", "check-pi-retire-request"],
|
|
23
|
+
"timeoutSeconds": 600,
|
|
24
|
+
"signature": "[QK:PI-RETIRE-WIRE]",
|
|
25
|
+
"signatureSource": "pi-extensions/lib/pi-retire-request.test.ts"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"claim": "PI-RETIRE-PARSE-CLOSED",
|
|
29
|
+
"title": "a failure response with a requested boundary is incorrectly accepted",
|
|
30
|
+
"subject": "pi-extensions/lib/pi-retire-request.ts",
|
|
31
|
+
"find": ["if (response.success === true && data.boundary === \"shutdown-requested\") {"],
|
|
32
|
+
"replace": ["if (data.boundary === \"shutdown-requested\") {"],
|
|
33
|
+
"gate": ["bash", "run.sh", "check-pi-retire-request"],
|
|
34
|
+
"timeoutSeconds": 600,
|
|
35
|
+
"signature": "[QK:PI-RETIRE-PARSE-CLOSED]",
|
|
36
|
+
"signatureSource": "pi-extensions/lib/pi-retire-request.test.ts"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"claim": "PI-RETIRE-REFUSALS",
|
|
40
|
+
"title": "the known receiver refusal domain is dropped and every named refusal becomes unknown",
|
|
41
|
+
"subject": "pi-extensions/lib/pi-retire-request.ts",
|
|
42
|
+
"find": ["if (data.boundary === \"refused\" && RECEIVER_REFUSALS.includes(data.reason as ReceiverRefusal)) {"],
|
|
43
|
+
"replace": ["if (false) {"],
|
|
44
|
+
"gate": ["bash", "run.sh", "check-pi-retire-request"],
|
|
45
|
+
"timeoutSeconds": 600,
|
|
46
|
+
"signature": "[QK:PI-RETIRE-REFUSALS]",
|
|
47
|
+
"signatureSource": "pi-extensions/lib/pi-retire-request.test.ts"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"claim": "PI-RETIRE-TRANSPORT",
|
|
51
|
+
"title": "a timeout or lost response is misreported as a certain receiver refusal",
|
|
52
|
+
"subject": "pi-extensions/lib/pi-retire-request.ts",
|
|
53
|
+
"find": ["\t\treturn { boundary: \"unknown\", reason: \"transport-error\" };"],
|
|
54
|
+
"replace": ["\t\treturn { boundary: \"refused\", phase: \"receiver\", reason: \"target-closing\" };"],
|
|
55
|
+
"gate": ["bash", "run.sh", "check-pi-retire-request"],
|
|
56
|
+
"timeoutSeconds": 600,
|
|
57
|
+
"signature": "[QK:PI-RETIRE-TRANSPORT]",
|
|
58
|
+
"signatureSource": "pi-extensions/lib/pi-retire-request.test.ts"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"claim": "PI-RETIRE-ENVELOPE",
|
|
62
|
+
"title": "the production receiver drops its error reason so the strict adapter cannot interpret refusals",
|
|
63
|
+
"subject": "pi-extensions/lib/control-retire.ts",
|
|
64
|
+
"find": ["\t\t...(result.boundary !== \"shutdown-requested\" ? { error: result.reason } : {}),\n"],
|
|
65
|
+
"replace": [""],
|
|
66
|
+
"gate": ["bash", "run.sh", "check-pi-retire-request"],
|
|
67
|
+
"timeoutSeconds": 600,
|
|
68
|
+
"signature": "[QK:PI-RETIRE-ENVELOPE]",
|
|
69
|
+
"signatureSource": "pi-extensions/lib/pi-retire-request.test.ts"
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
}
|
|
@@ -55,14 +55,8 @@
|
|
|
55
55
|
"claim": "RESUME-ID-GATE-SSOT",
|
|
56
56
|
"title": "the preserved leaf's header stops naming the gate that certifies it, so a consumer-zero identity module ships on prose again",
|
|
57
57
|
"subject": "pi-extensions/lib/resume-launch-identity.ts",
|
|
58
|
-
"find": [
|
|
59
|
-
|
|
60
|
-
" * `check-resume-launch-identity` holds its contract so the record-integrity rules below —"
|
|
61
|
-
],
|
|
62
|
-
"replace": [
|
|
63
|
-
" * So this leaf is preserved deliberately for the later visible lane.",
|
|
64
|
-
" * The record-integrity rules below —"
|
|
65
|
-
],
|
|
58
|
+
"find": [" * `check-resume-launch-identity` holds the record-integrity rules below — the transcript header id"],
|
|
59
|
+
"replace": [" * the record-integrity rules below — the transcript header id"],
|
|
66
60
|
"gate": ["bash", "run.sh", "check-resume-launch-identity"],
|
|
67
61
|
"timeoutSeconds": 60,
|
|
68
62
|
"signature": "[QK:RESUME-ID-GATE-SSOT]",
|
|
@@ -106,6 +100,39 @@
|
|
|
106
100
|
"timeoutSeconds": 60,
|
|
107
101
|
"signature": "[QK:RESUME-ID-ACP-EXACT-CURATION]",
|
|
108
102
|
"signatureSource": "scripts/check-resume-launch-identity.ts"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"claim": "RESUME-ID-CC-RESOLVES",
|
|
106
|
+
"title": "a claude-code record falls back to the out-of-domain refusal, so the widened domain exists only in prose",
|
|
107
|
+
"subject": "pi-extensions/lib/resume-launch-identity.ts",
|
|
108
|
+
"find": ["\tif (record.backend === \"claude-code\") return resolveClaudeCodeLaunchIdentity(gardenId, record);"],
|
|
109
|
+
"replace": [""],
|
|
110
|
+
"gate": ["bash", "run.sh", "check-resume-launch-identity"],
|
|
111
|
+
"timeoutSeconds": 60,
|
|
112
|
+
"signature": "[QK:RESUME-ID-CC-RESOLVES]",
|
|
113
|
+
"signatureSource": "scripts/check-resume-launch-identity.ts"
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"claim": "RESUME-ID-CC-SESSION-GRAMMAR",
|
|
117
|
+
"title": "a recorded Claude Code session id outside the UUID grammar reaches claude --resume, where a value like --dangerously-skip-permissions would be read as a flag",
|
|
118
|
+
"subject": "pi-extensions/lib/resume-launch-identity.ts",
|
|
119
|
+
"find": ["\tif (!CLAUDE_SESSION_ID.test(record.nativeSessionId)) {"],
|
|
120
|
+
"replace": ["\tif (false && !CLAUDE_SESSION_ID.test(record.nativeSessionId)) {"],
|
|
121
|
+
"gate": ["bash", "run.sh", "check-resume-launch-identity"],
|
|
122
|
+
"timeoutSeconds": 60,
|
|
123
|
+
"signature": "[QK:RESUME-ID-CC-SESSION-GRAMMAR]",
|
|
124
|
+
"signatureSource": "scripts/check-resume-launch-identity.ts"
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"claim": "RESUME-ID-CC-TRANSCRIPT-PRESENT",
|
|
128
|
+
"title": "a Claude Code record whose transcript is gone still resolves, so the window opens on a session the vendor cannot find",
|
|
129
|
+
"subject": "pi-extensions/lib/resume-launch-identity.ts",
|
|
130
|
+
"find": ["\tif (!isFile) {"],
|
|
131
|
+
"replace": ["\tif (false && !isFile) {"],
|
|
132
|
+
"gate": ["bash", "run.sh", "check-resume-launch-identity"],
|
|
133
|
+
"timeoutSeconds": 60,
|
|
134
|
+
"signature": "[QK:RESUME-ID-CC-TRANSCRIPT-PRESENT]",
|
|
135
|
+
"signatureSource": "scripts/check-resume-launch-identity.ts"
|
|
109
136
|
}
|
|
110
137
|
]
|
|
111
138
|
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"lane": "retirement-event",
|
|
4
|
+
"mutants": [
|
|
5
|
+
{
|
|
6
|
+
"claim": "RETIRE-EVENT-FD-KIND",
|
|
7
|
+
"title": "an opened non-regular inode passes admission and reaches write",
|
|
8
|
+
"subject": "pi-extensions/lib/retirement-event.ts",
|
|
9
|
+
"find": ["\t\tif (!st.isFile()) throw new RetirementEventError(`${path.basename(file)} is not a regular file`);"],
|
|
10
|
+
"replace": ["\t\t// MUTANT: opened inode kind is not checked."],
|
|
11
|
+
"gate": ["bash", "run.sh", "check-retirement-event"],
|
|
12
|
+
"timeoutSeconds": 120,
|
|
13
|
+
"signature": "[QK:RETIRE-EVENT-FD-KIND]",
|
|
14
|
+
"signatureSource": "pi-extensions/lib/retirement-event.test.ts"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"claim": "RETIRE-EVENT-CLOSED-KEYS",
|
|
18
|
+
"title": "serialized event grows an undeclared field",
|
|
19
|
+
"subject": "pi-extensions/lib/retirement-event.ts",
|
|
20
|
+
"find": ["\tconst base = { v, kind, eventId, at, actorGardenId, targetGardenId, targetBackend };"],
|
|
21
|
+
"replace": [
|
|
22
|
+
"\tconst base = { v, kind, eventId, at, actorGardenId, targetGardenId, targetBackend, extra: true };"
|
|
23
|
+
],
|
|
24
|
+
"gate": ["bash", "run.sh", "check-retirement-event"],
|
|
25
|
+
"timeoutSeconds": 120,
|
|
26
|
+
"signature": "[QK:RETIRE-EVENT-CLOSED-KEYS]",
|
|
27
|
+
"signatureSource": "pi-extensions/lib/retirement-event.test.ts"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"claim": "RETIRE-EVENT-FORBIDDEN",
|
|
31
|
+
"title": "free text is admitted as a retirement reason",
|
|
32
|
+
"subject": "pi-extensions/lib/retirement-event.ts",
|
|
33
|
+
"find": ["\tif (reason !== null && (typeof reason !== \"string\" || !REASON_RE.test(reason))) {"],
|
|
34
|
+
"replace": ["\tif (false) {"],
|
|
35
|
+
"gate": ["bash", "run.sh", "check-retirement-event"],
|
|
36
|
+
"timeoutSeconds": 120,
|
|
37
|
+
"signature": "[QK:RETIRE-EVENT-FORBIDDEN]",
|
|
38
|
+
"signatureSource": "pi-extensions/lib/retirement-event.test.ts"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"claim": "RETIRE-EVENT-INSTANT",
|
|
42
|
+
"title": "calendar rollover is accepted as an ISO instant",
|
|
43
|
+
"subject": "pi-extensions/lib/retirement-event.ts",
|
|
44
|
+
"find": ["\treturn !Number.isNaN(ms) && new Date(ms).toISOString() === value;"],
|
|
45
|
+
"replace": ["\treturn !Number.isNaN(ms);"],
|
|
46
|
+
"gate": ["bash", "run.sh", "check-retirement-event"],
|
|
47
|
+
"timeoutSeconds": 120,
|
|
48
|
+
"signature": "[QK:RETIRE-EVENT-INSTANT]",
|
|
49
|
+
"signatureSource": "pi-extensions/lib/retirement-event.test.ts"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"claim": "RETIRE-EVENT-BACKEND-DOMAIN",
|
|
53
|
+
"title": "one backend records the other backend's outcome as its own",
|
|
54
|
+
"subject": "pi-extensions/lib/retirement-event.ts",
|
|
55
|
+
"find": ["\tconst allowed: readonly unknown[] = RETIREMENT_OUTCOME_STATUSES_BY_BACKEND[targetBackend];"],
|
|
56
|
+
"replace": [
|
|
57
|
+
"\tconst allowed: readonly unknown[] = [\"shutdown-requested\", \"exited\", \"refused\", \"not-signalled\", \"unknown\"];"
|
|
58
|
+
],
|
|
59
|
+
"gate": ["bash", "run.sh", "check-retirement-event"],
|
|
60
|
+
"timeoutSeconds": 120,
|
|
61
|
+
"signature": "[QK:RETIRE-EVENT-BACKEND-DOMAIN]",
|
|
62
|
+
"signatureSource": "pi-extensions/lib/retirement-event.test.ts"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"claim": "RETIRE-EVENT-SNAPSHOT",
|
|
66
|
+
"title": "foreign prototypes stop being refused at the snapshot boundary",
|
|
67
|
+
"subject": "pi-extensions/lib/retirement-event.ts",
|
|
68
|
+
"find": [
|
|
69
|
+
"\tif (proto !== Object.prototype && proto !== null) throw new RetirementEventError(`${what} must be a plain object`);"
|
|
70
|
+
],
|
|
71
|
+
"replace": ["\t// MUTANT: foreign prototype admitted."],
|
|
72
|
+
"gate": ["bash", "run.sh", "check-retirement-event"],
|
|
73
|
+
"timeoutSeconds": 120,
|
|
74
|
+
"signature": "[QK:RETIRE-EVENT-SNAPSHOT]",
|
|
75
|
+
"signatureSource": "pi-extensions/lib/retirement-event.test.ts"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"claim": "RETIRE-EVENT-ONE-SNAPSHOT",
|
|
79
|
+
"title": "filename re-reads the original object after validating a different target snapshot",
|
|
80
|
+
"subject": "pi-extensions/lib/retirement-event.ts",
|
|
81
|
+
"find": ["\tconst file = retirementEventFile(retirementsDir, targetGardenId);"],
|
|
82
|
+
"replace": ["\tconst file = retirementEventFile(retirementsDir, event.targetGardenId);"],
|
|
83
|
+
"gate": ["bash", "run.sh", "check-retirement-event"],
|
|
84
|
+
"timeoutSeconds": 120,
|
|
85
|
+
"signature": "[QK:RETIRE-EVENT-ONE-SNAPSHOT]",
|
|
86
|
+
"signatureSource": "pi-extensions/lib/retirement-event.test.ts"
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"claim": "RETIRE-EVENT-APPEND",
|
|
90
|
+
"title": "an append opens at offset zero and overwrites the preserved prefix",
|
|
91
|
+
"subject": "pi-extensions/lib/retirement-event.ts",
|
|
92
|
+
"find": ["\tconst fd = fs.openSync(file, O_WRONLY | O_APPEND | O_CREAT | O_NOFOLLOW | O_NONBLOCK, 0o600);"],
|
|
93
|
+
"replace": ["\tconst fd = fs.openSync(file, O_WRONLY | O_CREAT | O_NOFOLLOW | O_NONBLOCK, 0o600);"],
|
|
94
|
+
"gate": ["bash", "run.sh", "check-retirement-event"],
|
|
95
|
+
"timeoutSeconds": 120,
|
|
96
|
+
"signature": "[QK:RETIRE-EVENT-APPEND]",
|
|
97
|
+
"signatureSource": "pi-extensions/lib/retirement-event.test.ts"
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"claim": "RETIRE-EVENT-CREATE",
|
|
101
|
+
"title": "new event files abandon the literal target-gid JSONL filename contract",
|
|
102
|
+
"subject": "pi-extensions/lib/retirement-event.ts",
|
|
103
|
+
"find": ["export const RETIREMENT_EVENT_SUFFIX = \".jsonl\";"],
|
|
104
|
+
"replace": ["export const RETIREMENT_EVENT_SUFFIX = \".log\";"],
|
|
105
|
+
"gate": ["bash", "run.sh", "check-retirement-event"],
|
|
106
|
+
"timeoutSeconds": 120,
|
|
107
|
+
"signature": "[QK:RETIRE-EVENT-CREATE]",
|
|
108
|
+
"signatureSource": "pi-extensions/lib/retirement-event.test.ts"
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"claim": "RETIRE-EVENT-NOFOLLOW",
|
|
112
|
+
"title": "a final symlink is followed and its victim is appended to",
|
|
113
|
+
"subject": "pi-extensions/lib/retirement-event.ts",
|
|
114
|
+
"find": ["\tconst fd = fs.openSync(file, O_WRONLY | O_APPEND | O_CREAT | O_NOFOLLOW | O_NONBLOCK, 0o600);"],
|
|
115
|
+
"replace": ["\tconst fd = fs.openSync(file, O_WRONLY | O_APPEND | O_CREAT | O_NONBLOCK, 0o600);"],
|
|
116
|
+
"gate": ["bash", "run.sh", "check-retirement-event"],
|
|
117
|
+
"timeoutSeconds": 120,
|
|
118
|
+
"signature": "[QK:RETIRE-EVENT-NOFOLLOW]",
|
|
119
|
+
"signatureSource": "pi-extensions/lib/retirement-event.test.ts"
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"claim": "RETIRE-EVENT-NONREGULAR",
|
|
123
|
+
"title": "a multiply linked event inode can be appended to",
|
|
124
|
+
"subject": "pi-extensions/lib/retirement-event.ts",
|
|
125
|
+
"find": [
|
|
126
|
+
"\t\tif (st.nlink !== 1) throw new RetirementEventError(`${path.basename(file)} has ${st.nlink} links; refusing`);"
|
|
127
|
+
],
|
|
128
|
+
"replace": ["\t\t// MUTANT: hardlink guard omitted."],
|
|
129
|
+
"gate": ["bash", "run.sh", "check-retirement-event"],
|
|
130
|
+
"timeoutSeconds": 120,
|
|
131
|
+
"signature": "[QK:RETIRE-EVENT-NONREGULAR]",
|
|
132
|
+
"signatureSource": "pi-extensions/lib/retirement-event.test.ts"
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"claim": "RETIRE-EVENT-DIR",
|
|
136
|
+
"title": "a symlinked retirement directory is admitted",
|
|
137
|
+
"subject": "pi-extensions/lib/retirement-event.ts",
|
|
138
|
+
"find": ["\tif (st.isSymbolicLink() || !st.isDirectory()) {"],
|
|
139
|
+
"replace": ["\tif (false) {"],
|
|
140
|
+
"gate": ["bash", "run.sh", "check-retirement-event"],
|
|
141
|
+
"timeoutSeconds": 120,
|
|
142
|
+
"signature": "[QK:RETIRE-EVENT-DIR]",
|
|
143
|
+
"signatureSource": "pi-extensions/lib/retirement-event.test.ts"
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
"claim": "RETIRE-EVENT-PATH",
|
|
147
|
+
"title": "a relative sessions directory becomes an ambient-CWD-dependent path",
|
|
148
|
+
"subject": "pi-extensions/lib/retirement-event.ts",
|
|
149
|
+
"find": [
|
|
150
|
+
"\tif (!path.isAbsolute(sessionsDir)) throw new RetirementEventError(\"sessionsDir must be an absolute path\");"
|
|
151
|
+
],
|
|
152
|
+
"replace": ["\t// MUTANT: relative sessions directory admitted."],
|
|
153
|
+
"gate": ["bash", "run.sh", "check-retirement-event"],
|
|
154
|
+
"timeoutSeconds": 120,
|
|
155
|
+
"signature": "[QK:RETIRE-EVENT-PATH]",
|
|
156
|
+
"signatureSource": "pi-extensions/lib/retirement-event.test.ts"
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"claim": "RETIRE-EVENT-NO-AMBIENT",
|
|
160
|
+
"title": "the append leaf begins reading ambient environment",
|
|
161
|
+
"subject": "pi-extensions/lib/retirement-event.ts",
|
|
162
|
+
"find": ["export function retirementsDirBesideSessions(sessionsDir: string): string {"],
|
|
163
|
+
"replace": [
|
|
164
|
+
"export function retirementsDirBesideSessions(sessionsDir: string): string {",
|
|
165
|
+
"\tvoid process.env.HOME;"
|
|
166
|
+
],
|
|
167
|
+
"gate": ["bash", "run.sh", "check-retirement-event"],
|
|
168
|
+
"timeoutSeconds": 120,
|
|
169
|
+
"signature": "[QK:RETIRE-EVENT-NO-AMBIENT]",
|
|
170
|
+
"signatureSource": "pi-extensions/lib/retirement-event.test.ts"
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
"claim": "RETIRE-EVENT-PRIMITIVES",
|
|
174
|
+
"title": "FIFO admission loses NONBLOCK without a semantic open-flag assertion",
|
|
175
|
+
"subject": "pi-extensions/lib/retirement-event.ts",
|
|
176
|
+
"find": ["\tconst fd = fs.openSync(file, O_WRONLY | O_APPEND | O_CREAT | O_NOFOLLOW | O_NONBLOCK, 0o600);"],
|
|
177
|
+
"replace": ["\tconst fd = fs.openSync(file, O_WRONLY | O_APPEND | O_CREAT | O_NOFOLLOW, 0o600);"],
|
|
178
|
+
"gate": ["bash", "run.sh", "check-retirement-event"],
|
|
179
|
+
"timeoutSeconds": 120,
|
|
180
|
+
"signature": "[QK:RETIRE-EVENT-PRIMITIVES]",
|
|
181
|
+
"signatureSource": "pi-extensions/lib/retirement-event.test.ts"
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
"claim": "RETIRE-EVENT-FAILURE-MATRIX",
|
|
185
|
+
"title": "the file flush disappears so a flush failure cannot be reported",
|
|
186
|
+
"subject": "pi-extensions/lib/retirement-event.ts",
|
|
187
|
+
"find": ["\t\tfs.fsyncSync(fd);"],
|
|
188
|
+
"replace": ["\t\t// MUTANT: file flush omitted."],
|
|
189
|
+
"gate": ["bash", "run.sh", "check-retirement-event"],
|
|
190
|
+
"timeoutSeconds": 120,
|
|
191
|
+
"signature": "[QK:RETIRE-EVENT-FAILURE-MATRIX]",
|
|
192
|
+
"signatureSource": "pi-extensions/lib/retirement-event.test.ts"
|
|
193
|
+
}
|
|
194
|
+
]
|
|
195
|
+
}
|
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"lane": "v2-surface",
|
|
4
4
|
"mutants": [
|
|
5
|
+
{
|
|
6
|
+
"claim": "V2SURF-RESUME-HINT-SCOPE",
|
|
7
|
+
"title": "the dormant capability hint returns to a Pi-only automatic-resume invitation",
|
|
8
|
+
"subject": "pi-extensions/lib/entwurf-v2-surface.ts",
|
|
9
|
+
"find": [
|
|
10
|
+
"\t\t\t\t\"Only on an explicit operator request, use entwurf_resume_call {target} for Pi or Claude Code; \" +",
|
|
11
|
+
"\t\t\t\t\"it submits no prompt. This capability hint is not permission to resume automatically.\""
|
|
12
|
+
],
|
|
13
|
+
"replace": [
|
|
14
|
+
"\t\t\t\t\"Reopen the citizen with entwurf_resume_call {target} (pi targets only; it runs no turn), then dispatch again.\""
|
|
15
|
+
],
|
|
16
|
+
"gate": ["bash", "run.sh", "check-entwurf-v2-surface"],
|
|
17
|
+
"timeoutSeconds": 60,
|
|
18
|
+
"signature": "[QK:V2SURF-RESUME-HINT-SCOPE]",
|
|
19
|
+
"signatureSource": "scripts/check-entwurf-v2-surface.ts"
|
|
20
|
+
},
|
|
5
21
|
{
|
|
6
22
|
"claim": "V2SURF-MCP-LOCK-DOMAIN",
|
|
7
23
|
"title": "MCP long description reverts to the false \"socket paths take a per-target lock\" claim",
|