@junghanacs/entwurf 0.39.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.
Files changed (137) hide show
  1. package/AGENTS.md +4 -2
  2. package/BASELINE.md +60 -5
  3. package/CHANGELOG.md +557 -0
  4. package/CONTRIBUTING.md +1 -1
  5. package/DELIVERY.md +52 -2
  6. package/FAQ.md +8 -4
  7. package/README.md +37 -17
  8. package/VERIFY.md +23 -10
  9. package/docs/acp-backend-rail.md +5 -1
  10. package/docs/external-mcp-host.md +8 -6
  11. package/docs/fresh-cut-policy.md +9 -1
  12. package/docs/setup-clean-host.md +64 -9
  13. package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +100 -24
  14. package/mcp/entwurf-bridge/dist/pi-extensions/lib/cc-retire-pidfd.py +236 -0
  15. package/mcp/entwurf-bridge/dist/pi-extensions/lib/cc-retire-request.js +473 -0
  16. package/mcp/entwurf-bridge/dist/pi-extensions/lib/control-retire.js +125 -0
  17. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-retire-production.js +149 -0
  18. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-retire.js +158 -0
  19. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-surface.js +2 -1
  20. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-visible-resume.js +283 -20
  21. package/mcp/entwurf-bridge/dist/pi-extensions/lib/fresh-call-composition.js +7 -0
  22. package/mcp/entwurf-bridge/dist/pi-extensions/lib/fresh-call-dispatch.js +47 -3
  23. package/mcp/entwurf-bridge/dist/pi-extensions/lib/herdr-fresh-call.js +3 -1
  24. package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-mailbox-body.js +10 -4
  25. package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js +96 -16
  26. package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +5 -29
  27. package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-launch.js +28 -0
  28. package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-resume-call.js +26 -11
  29. package/mcp/entwurf-bridge/dist/pi-extensions/lib/pi-retire-request.js +76 -0
  30. package/mcp/entwurf-bridge/dist/pi-extensions/lib/remote-pair-receive.js +123 -0
  31. package/mcp/entwurf-bridge/dist/pi-extensions/lib/remote-pair-send.js +259 -0
  32. package/mcp/entwurf-bridge/dist/pi-extensions/lib/remote-pair-transport.js +80 -0
  33. package/mcp/entwurf-bridge/dist/pi-extensions/lib/resume-launch-identity.js +67 -17
  34. package/mcp/entwurf-bridge/dist/pi-extensions/lib/retirement-event.js +211 -0
  35. package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-hook.js +124 -10
  36. package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-pi-durable.js +53 -2
  37. package/mcp/entwurf-bridge/dist/scripts/meta-facts.js +1 -1
  38. package/mcp/entwurf-bridge/dist/scripts/probe-bridge-command.js +1 -0
  39. package/mcp/entwurf-bridge/dist/scripts/remote-pair-receive.js +65 -0
  40. package/mcp/entwurf-bridge/src/index.ts +118 -24
  41. package/mcp/entwurf-bridge/test.sh +2 -2
  42. package/mcp/entwurf-bridge/tsconfig.build.json +17 -1
  43. package/package.json +5 -5
  44. package/pi/meta-bridge/entwurf-meta-receive/scripts/hook-launch.sh +3 -3
  45. package/pi-extensions/entwurf-control.ts +55 -20
  46. package/pi-extensions/lib/cc-retire-pidfd.py +236 -0
  47. package/pi-extensions/lib/cc-retire-request.ts +553 -0
  48. package/pi-extensions/lib/control-retire.ts +195 -0
  49. package/pi-extensions/lib/entwurf-control-rpc.ts +17 -1
  50. package/pi-extensions/lib/entwurf-v2-retire-production.ts +227 -0
  51. package/pi-extensions/lib/entwurf-v2-retire.ts +269 -0
  52. package/pi-extensions/lib/entwurf-v2-surface.ts +2 -1
  53. package/pi-extensions/lib/entwurf-v2-visible-resume.ts +390 -36
  54. package/pi-extensions/lib/fresh-call-composition.ts +15 -0
  55. package/pi-extensions/lib/fresh-call-dispatch.ts +45 -4
  56. package/pi-extensions/lib/herdr-fresh-call.ts +5 -0
  57. package/pi-extensions/lib/meta-mailbox-body.ts +13 -4
  58. package/pi-extensions/lib/meta-session.ts +140 -16
  59. package/pi-extensions/lib/mux-fresh-call.ts +7 -27
  60. package/pi-extensions/lib/mux-launch.ts +29 -0
  61. package/pi-extensions/lib/mux-resume-call.ts +33 -12
  62. package/pi-extensions/lib/pi-retire-request.ts +111 -0
  63. package/pi-extensions/lib/remote-pair-receive.ts +184 -0
  64. package/pi-extensions/lib/remote-pair-send.ts +325 -0
  65. package/pi-extensions/lib/remote-pair-transport.ts +81 -0
  66. package/pi-extensions/lib/resume-launch-identity.ts +104 -18
  67. package/pi-extensions/lib/retirement-event.ts +253 -0
  68. package/pi-extensions/meta-bridge-hook.ts +142 -13
  69. package/pi-extensions/meta-bridge-pi-durable.ts +56 -2
  70. package/run.sh +122 -7
  71. package/scripts/agy-bridge-config.py +4 -1
  72. package/scripts/build-bridge.sh +14 -0
  73. package/scripts/check-capability-bundle-reach.ts +135 -2
  74. package/scripts/check-entwurf-bridge-boot.ts +2 -1
  75. package/scripts/check-entwurf-fact-provider.ts +1 -0
  76. package/scripts/check-entwurf-v2-production.ts +2 -0
  77. package/scripts/check-entwurf-v2-surface.ts +7 -0
  78. package/scripts/check-gate-qualification.ts +20 -8
  79. package/scripts/check-herdr-activation.ts +2 -2
  80. package/scripts/check-herdr-placement.ts +1 -0
  81. package/scripts/check-hook-launch-topology.ts +6 -15
  82. package/scripts/check-install-container.sh +2 -2
  83. package/scripts/check-meta-doctor-oracle.sh +27 -1
  84. package/scripts/check-meta-facts.ts +8 -3
  85. package/scripts/check-meta-hook-session-switch.ts +230 -13
  86. package/scripts/check-meta-identity-consumers.ts +1 -0
  87. package/scripts/check-meta-listing.ts +1 -0
  88. package/scripts/check-meta-manifest-schema.py +1 -1
  89. package/scripts/check-meta-session.ts +31 -0
  90. package/scripts/check-meta-v3-record.ts +55 -1
  91. package/scripts/check-mux-resume-call.ts +35 -9
  92. package/scripts/check-peer-facts.ts +1 -0
  93. package/scripts/check-pi-durable-contact.ts +3 -1
  94. package/scripts/check-pi-durable-send.ts +4 -2
  95. package/scripts/check-pi-mcp-bridge.ts +1 -0
  96. package/scripts/check-remote-pair-installed.ts +242 -0
  97. package/scripts/check-resume-launch-identity.ts +62 -23
  98. package/scripts/lib/meta-hook-bundle.ts +72 -0
  99. package/scripts/lib/remote-pair-installed-audit.ts +25 -0
  100. package/scripts/meta-bridge-claude-floor.sh +13 -9
  101. package/scripts/meta-bridge-doctor.sh +17 -0
  102. package/scripts/meta-bridge-install.sh +6 -0
  103. package/scripts/meta-facts.ts +1 -1
  104. package/scripts/mutants/bridge-boot-resume.json +5 -2
  105. package/scripts/mutants/cc-retire-pidfd.json +129 -0
  106. package/scripts/mutants/cc-retire-request.json +171 -0
  107. package/scripts/mutants/control-retire.json +105 -0
  108. package/scripts/mutants/control-send-receipt.json +2 -2
  109. package/scripts/mutants/copilot-receive.json +2 -6
  110. package/scripts/mutants/entwurf-v2-retire.json +476 -0
  111. package/scripts/mutants/fresh-call-dispatch.json +37 -0
  112. package/scripts/mutants/meta-hook-session-switch.json +88 -0
  113. package/scripts/mutants/meta-identity.json +57 -0
  114. package/scripts/mutants/mux-fresh-call.json +1 -1
  115. package/scripts/mutants/mux-resume-call.json +40 -4
  116. package/scripts/mutants/pi-durable.json +79 -0
  117. package/scripts/mutants/pi-retire-request.json +72 -0
  118. package/scripts/mutants/remote-pair-display.json +53 -0
  119. package/scripts/mutants/remote-pair-installed-audit.json +28 -0
  120. package/scripts/mutants/remote-pair-receive.json +126 -0
  121. package/scripts/mutants/remote-pair-roundtrip.json +83 -0
  122. package/scripts/mutants/remote-pair-send.json +194 -0
  123. package/scripts/mutants/remote-pair-transport.json +66 -0
  124. package/scripts/mutants/resume-launch-identity.json +35 -8
  125. package/scripts/mutants/retirement-event.json +195 -0
  126. package/scripts/mutants/v2-surface.json +16 -0
  127. package/scripts/mutants/v2-visible-resume.json +283 -13
  128. package/scripts/probe-bridge-command.ts +1 -0
  129. package/scripts/remote-pair-receive.ts +73 -0
  130. package/scripts/smoke-agy-install-state.sh +2 -2
  131. package/scripts/smoke-copilot-mcp-state.sh +1 -1
  132. package/scripts/smoke-herdr-raw-install-live.sh +5 -4
  133. package/scripts/smoke-meta-install-state.sh +4 -4
  134. package/scripts/smoke-mux-lifecycle-live.ts +10 -10
  135. package/scripts/smoke-omp-mcp-state.sh +1 -1
  136. package/scripts/smoke-pi-durable-fresh-live.ts +5 -3
  137. package/scripts/smoke-pi-provider-state.sh +3 -3
@@ -0,0 +1,194 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "lane": "remote-pair-send",
4
+ "mutants": [
5
+ {
6
+ "claim": "REMOTE-ROUTE-UNDECLARED-LOCAL-UNCHANGED",
7
+ "title": "a declared route captures every target, so a gid the declaration never named is sent to the other host instead of the local decider",
8
+ "subject": "pi-extensions/lib/remote-pair-send.ts",
9
+ "find": ["\tif (route === null || params.target !== route.remoteGardenId) {"],
10
+ "replace": ["\tif (route === null) {"],
11
+ "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
12
+ "timeoutSeconds": 600,
13
+ "signature": "[QK:REMOTE-ROUTE-UNDECLARED-LOCAL-UNCHANGED]",
14
+ "signatureSource": "pi-extensions/lib/remote-pair-send.test.ts",
15
+ "build": {
16
+ "argv": ["bash", "scripts/build-bridge.sh"],
17
+ "output": "mcp/entwurf-bridge/dist"
18
+ }
19
+ },
20
+ {
21
+ "claim": "REMOTE-ROUTE-LOCAL-CONFLICT-FAILS-CLOSED",
22
+ "title": "a declared remote gid that this host also answers to is sent away anyway, so two hosts' citizens share one address and the sender silently picks the remote",
23
+ "subject": "pi-extensions/lib/remote-pair-send.ts",
24
+ "find": ["\tif (await opts.localTrace(route.remoteGardenId)) {"],
25
+ "replace": ["\tif (Boolean(0)) {"],
26
+ "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
27
+ "timeoutSeconds": 600,
28
+ "signature": "[QK:REMOTE-ROUTE-LOCAL-CONFLICT-FAILS-CLOSED]",
29
+ "signatureSource": "pi-extensions/lib/remote-pair-send.test.ts",
30
+ "build": {
31
+ "argv": ["bash", "scripts/build-bridge.sh"],
32
+ "output": "mcp/entwurf-bridge/dist"
33
+ }
34
+ },
35
+ {
36
+ "claim": "REMOTE-ROUTE-SENDER-MUST-BE-DECLARED",
37
+ "title": "any present envelope may use the route, so an anonymous caller or another citizen on this host speaks to the other host as the declared sender",
38
+ "subject": "pi-extensions/lib/remote-pair-send.ts",
39
+ "find": ["\tif (!sender || sender.origin === \"external-mcp\" || sender.sessionId !== route.localGardenId) {"],
40
+ "replace": ["\tif (!sender || Boolean(0)) {"],
41
+ "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
42
+ "timeoutSeconds": 600,
43
+ "signature": "[QK:REMOTE-ROUTE-SENDER-MUST-BE-DECLARED]",
44
+ "signatureSource": "pi-extensions/lib/remote-pair-send.test.ts",
45
+ "build": {
46
+ "argv": ["bash", "scripts/build-bridge.sh"],
47
+ "output": "mcp/entwurf-bridge/dist"
48
+ }
49
+ },
50
+ {
51
+ "claim": "REMOTE-ROUTE-EXACT-ARGV-ONCE",
52
+ "title": "the transport runs ssh without BatchMode, so a missing key turns into an interactive prompt nobody can answer instead of a bounded failure",
53
+ "subject": "pi-extensions/lib/remote-pair-send.ts",
54
+ "find": ["\t\t\"BatchMode=yes\","],
55
+ "replace": ["\t\t\"BatchMode=no\","],
56
+ "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
57
+ "timeoutSeconds": 600,
58
+ "signature": "[QK:REMOTE-ROUTE-EXACT-ARGV-ONCE]",
59
+ "signatureSource": "pi-extensions/lib/remote-pair-send.test.ts",
60
+ "build": {
61
+ "argv": ["bash", "scripts/build-bridge.sh"],
62
+ "output": "mcp/entwurf-bridge/dist"
63
+ }
64
+ },
65
+ {
66
+ "claim": "REMOTE-ROUTE-NO-RETRY",
67
+ "title": "the send runs the transport a second time, so an uncertain first attempt can enqueue the same message twice on the other host",
68
+ "subject": "pi-extensions/lib/remote-pair-send.ts",
69
+ "find": ["\tconst result = await opts.transport.run("],
70
+ "replace": [
71
+ "\tawait opts.transport.run(remoteReceiveArgv(route), JSON.stringify(claim), 1);\n\tconst result = await opts.transport.run("
72
+ ],
73
+ "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
74
+ "timeoutSeconds": 600,
75
+ "signature": "[QK:REMOTE-ROUTE-NO-RETRY]",
76
+ "signatureSource": "pi-extensions/lib/remote-pair-send.test.ts",
77
+ "build": {
78
+ "argv": ["bash", "scripts/build-bridge.sh"],
79
+ "output": "mcp/entwurf-bridge/dist"
80
+ }
81
+ },
82
+ {
83
+ "claim": "REMOTE-RECEIPT-EXIT-AND-KIND-AGREE",
84
+ "title": "an accepted-looking JSON line counts as accepted whatever the exit code says, so a refusal or a crash after printing reads as delivery",
85
+ "subject": "pi-extensions/lib/remote-pair-send.ts",
86
+ "find": [
87
+ "\tif (r.exitCode === 0 && keys === \"isError,kind,text\" && o.kind === \"dispatched\" && o.isError === false) {"
88
+ ],
89
+ "replace": ["\tif (keys === \"isError,kind,text\" && o.kind === \"dispatched\" && o.isError === false) {"],
90
+ "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
91
+ "timeoutSeconds": 600,
92
+ "signature": "[QK:REMOTE-RECEIPT-EXIT-AND-KIND-AGREE]",
93
+ "signatureSource": "pi-extensions/lib/remote-pair-send.test.ts",
94
+ "build": {
95
+ "argv": ["bash", "scripts/build-bridge.sh"],
96
+ "output": "mcp/entwurf-bridge/dist"
97
+ }
98
+ },
99
+ {
100
+ "claim": "REMOTE-ROUTE-DECL-NO-QUOTING-NEEDED",
101
+ "title": "the remote declaration path may carry spaces or shell metacharacters, which ssh joins into the remote shell string and the other host reinterprets",
102
+ "subject": "pi-extensions/lib/remote-pair-send.ts",
103
+ "find": ["const REMOTE_PATH = /^\\/[A-Za-z0-9._/-]+$/;"],
104
+ "replace": ["const REMOTE_PATH = /^\\/.+$/;"],
105
+ "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
106
+ "timeoutSeconds": 600,
107
+ "signature": "[QK:REMOTE-ROUTE-DECL-NO-QUOTING-NEEDED]",
108
+ "signatureSource": "pi-extensions/lib/remote-pair-send.test.ts",
109
+ "build": {
110
+ "argv": ["bash", "scripts/build-bridge.sh"],
111
+ "output": "mcp/entwurf-bridge/dist"
112
+ }
113
+ },
114
+ {
115
+ "claim": "REMOTE-ROUTE-LOCAL-TRACE-ANY-STATE",
116
+ "title": "a socket at the gid's control path in a named socket dir is not counted as a local trace, so a live local citizen without a record is bypassed for the remote",
117
+ "subject": "pi-extensions/lib/remote-pair-send.ts",
118
+ "find": ["\t\t\tif (inspection.kind !== \"absent\") return true;"],
119
+ "replace": ["\t\t\tif (false) return true;"],
120
+ "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
121
+ "timeoutSeconds": 600,
122
+ "signature": "[QK:REMOTE-ROUTE-LOCAL-TRACE-ANY-STATE]",
123
+ "signatureSource": "pi-extensions/lib/remote-pair-send.test.ts",
124
+ "build": {
125
+ "argv": ["bash", "scripts/build-bridge.sh"],
126
+ "output": "mcp/entwurf-bridge/dist"
127
+ }
128
+ },
129
+ {
130
+ "claim": "REMOTE-ROUTE-RECEIPT-PATHS-OTHER-HOST",
131
+ "title": "the accepted receipt drops the note that its paths are on the other host, so the other host's messagePath reads as a file the sender could open",
132
+ "subject": "pi-extensions/lib/remote-pair-send.ts",
133
+ "find": ["read/answer unknown until a reply arrives; paths below are on the other host)"],
134
+ "replace": ["read/answer unknown until a reply arrives)"],
135
+ "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
136
+ "timeoutSeconds": 600,
137
+ "signature": "[QK:REMOTE-ROUTE-RECEIPT-PATHS-OTHER-HOST]",
138
+ "signatureSource": "pi-extensions/lib/remote-pair-send.test.ts",
139
+ "build": {
140
+ "argv": ["bash", "scripts/build-bridge.sh"],
141
+ "output": "mcp/entwurf-bridge/dist"
142
+ }
143
+ },
144
+ {
145
+ "claim": "REMOTE-PAIR-OPT-IN-ABSENT-IS-OFF",
146
+ "title": "an absent opt-in file throws instead of meaning off, so every local send on a host that never opted in fails",
147
+ "subject": "pi-extensions/lib/remote-pair-send.ts",
148
+ "find": ["\t\tif ((err as NodeJS.ErrnoException).code === \"ENOENT\") return null;"],
149
+ "replace": ["\t\tif (false) return null;"],
150
+ "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
151
+ "timeoutSeconds": 600,
152
+ "signature": "[QK:REMOTE-PAIR-OPT-IN-ABSENT-IS-OFF]",
153
+ "signatureSource": "pi-extensions/lib/remote-pair-send.test.ts",
154
+ "build": {
155
+ "argv": ["bash", "scripts/build-bridge.sh"],
156
+ "output": "mcp/entwurf-bridge/dist"
157
+ }
158
+ },
159
+ {
160
+ "claim": "REMOTE-PAIR-OPT-IN-BROKEN-REFUSED",
161
+ "title": "a symlinked or non-regular opt-in is followed, so the route the bridge walks is whatever the link points at today",
162
+ "subject": "pi-extensions/lib/remote-pair-send.ts",
163
+ "find": ["\tif (!st.isFile()) {\n\t\tthrow new RemoteRouteDeclarationError(`remote pair opt-in"],
164
+ "replace": ["\tif (false) {\n\t\tthrow new RemoteRouteDeclarationError(`remote pair opt-in"],
165
+ "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
166
+ "timeoutSeconds": 600,
167
+ "signature": "[QK:REMOTE-PAIR-OPT-IN-BROKEN-REFUSED]",
168
+ "signatureSource": "pi-extensions/lib/remote-pair-send.test.ts",
169
+ "build": {
170
+ "argv": ["bash", "scripts/build-bridge.sh"],
171
+ "output": "mcp/entwurf-bridge/dist"
172
+ }
173
+ },
174
+ {
175
+ "claim": "REMOTE-REPORTED-ERROR-NO-DELIVERY-INFERENCE",
176
+ "title": "an error the other host reports after a possible write is headed as a non-delivery, inviting a resend that would deliver twice",
177
+ "subject": "pi-extensions/lib/remote-pair-send.ts",
178
+ "find": [
179
+ "\t\t\t\ttext: `entwurf_v2 remote ${via} → the other host reported an error while processing; read its receipt below, delivery is not inferred from this line; not retried (paths below are on the other host)\\n${o.text}`,"
180
+ ],
181
+ "replace": [
182
+ "\t\t\t\ttext: `entwurf_v2 remote ${via} → the other host did not deliver (paths below are on the other host)\\n${o.text}`,"
183
+ ],
184
+ "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
185
+ "timeoutSeconds": 600,
186
+ "signature": "[QK:REMOTE-REPORTED-ERROR-NO-DELIVERY-INFERENCE]",
187
+ "signatureSource": "pi-extensions/lib/remote-pair-send.test.ts",
188
+ "build": {
189
+ "argv": ["bash", "scripts/build-bridge.sh"],
190
+ "output": "mcp/entwurf-bridge/dist"
191
+ }
192
+ }
193
+ ]
194
+ }
@@ -0,0 +1,66 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "lane": "remote-pair-transport",
4
+ "mutants": [
5
+ {
6
+ "claim": "REMOTE-TRANSPORT-NO-SHELL-SEPARATE-STREAMS",
7
+ "title": "the transport runs its argv through a shell, so a remote declaration word is expanded on the sending host before ssh ever sees it",
8
+ "subject": "pi-extensions/lib/remote-pair-transport.ts",
9
+ "find": ["{ shell: false, stdio: [\"pipe\", \"pipe\", \"pipe\"] }"],
10
+ "replace": ["{ shell: true, stdio: [\"pipe\", \"pipe\", \"pipe\"] }"],
11
+ "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
12
+ "timeoutSeconds": 600,
13
+ "signature": "[QK:REMOTE-TRANSPORT-NO-SHELL-SEPARATE-STREAMS]",
14
+ "signatureSource": "pi-extensions/lib/remote-pair-transport.test.ts",
15
+ "build": {
16
+ "argv": ["bash", "scripts/build-bridge.sh"],
17
+ "output": "mcp/entwurf-bridge/dist"
18
+ }
19
+ },
20
+ {
21
+ "claim": "REMOTE-TRANSPORT-TIMEOUT-REAPS-OWN-CHILD",
22
+ "title": "the bound only sends TERM, so an ssh child that ignores it outlives the send and the call never returns",
23
+ "subject": "pi-extensions/lib/remote-pair-transport.ts",
24
+ "find": ["\t\t\t\t\tchild.kill(\"SIGKILL\");"],
25
+ "replace": ["\t\t\t\t\tvoid 0;"],
26
+ "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
27
+ "timeoutSeconds": 600,
28
+ "signature": "[QK:REMOTE-TRANSPORT-TIMEOUT-REAPS-OWN-CHILD]",
29
+ "signatureSource": "pi-extensions/lib/remote-pair-transport.test.ts",
30
+ "build": {
31
+ "argv": ["bash", "scripts/build-bridge.sh"],
32
+ "output": "mcp/entwurf-bridge/dist"
33
+ }
34
+ },
35
+ {
36
+ "claim": "REMOTE-TRANSPORT-SPAWN-FAILURE-INDETERMINATE",
37
+ "title": "a child that never started reports exit 0, so a missing ssh binary reads as a clean exit instead of an unknown outcome",
38
+ "subject": "pi-extensions/lib/remote-pair-transport.ts",
39
+ "find": ["\t\t\t\t\texitCode: spawnError ? null : code,"],
40
+ "replace": ["\t\t\t\t\texitCode: spawnError ? 0 : code,"],
41
+ "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
42
+ "timeoutSeconds": 600,
43
+ "signature": "[QK:REMOTE-TRANSPORT-SPAWN-FAILURE-INDETERMINATE]",
44
+ "signatureSource": "pi-extensions/lib/remote-pair-transport.test.ts",
45
+ "build": {
46
+ "argv": ["bash", "scripts/build-bridge.sh"],
47
+ "output": "mcp/entwurf-bridge/dist"
48
+ }
49
+ },
50
+ {
51
+ "claim": "REMOTE-TRANSPORT-HELD-PIPES-STILL-SETTLE",
52
+ "title": "the send waits for a close that a pipe-holding descendant never lets happen, so one stuck remote leaves entwurf_v2 hanging forever",
53
+ "subject": "pi-extensions/lib/remote-pair-transport.ts",
54
+ "find": ["\t\t\t\t\tfinalTimer = setTimeout(() => {"],
55
+ "replace": ["\t\t\t\t\tfinalTimer = setTimeout(() => {\n\t\t\t\t\t\treturn;"],
56
+ "gate": ["bash", "run.sh", "check-tests-beside-behavior"],
57
+ "timeoutSeconds": 600,
58
+ "signature": "[QK:REMOTE-TRANSPORT-HELD-PIPES-STILL-SETTLE]",
59
+ "signatureSource": "pi-extensions/lib/remote-pair-transport.test.ts",
60
+ "build": {
61
+ "argv": ["bash", "scripts/build-bridge.sh"],
62
+ "output": "mcp/entwurf-bridge/dist"
63
+ }
64
+ }
65
+ ]
66
+ }
@@ -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
- " * So this leaf is preserved deliberately, with no consumer in the shipped tree today.",
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",