@junghanacs/entwurf 0.13.1 → 0.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. package/AGENTS.md +59 -15
  2. package/BASELINE.md +3 -3
  3. package/CHANGELOG.md +74 -0
  4. package/CONTRIBUTING.md +13 -9
  5. package/DELIVERY.md +7 -6
  6. package/README.md +37 -28
  7. package/VERIFY.md +22 -14
  8. package/demo/README.md +1 -1
  9. package/demo/demo-baseline.sh +1 -3
  10. package/demo/demo.sh +2 -5
  11. package/docs/acp-backend-rail.md +9 -4
  12. package/docs/external-mcp-host.md +4 -5
  13. package/docs/setup-clean-host.md +8 -7
  14. package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +155 -28
  15. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/overlay.js +8 -6
  16. package/mcp/entwurf-bridge/dist/pi-extensions/lib/classify-tmux-cwd.js +47 -0
  17. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-control-rpc.js +7 -5
  18. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-core.js +13 -14
  19. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-resume-args.js +45 -40
  20. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-contract.js +117 -95
  21. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-decider.js +23 -57
  22. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-lock.js +16 -7
  23. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-production.js +5 -53
  24. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-release.js +21 -36
  25. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-runner.js +3 -15
  26. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-send-fallback.js +12 -11
  27. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-send.js +2 -7
  28. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-surface.js +30 -67
  29. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-visible-resume.js +256 -0
  30. package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js +91 -5
  31. package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +300 -0
  32. package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-launch.js +202 -0
  33. package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-placement.js +289 -0
  34. package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-resume-call.js +141 -0
  35. package/mcp/entwurf-bridge/dist/pi-extensions/lib/resume-launch-identity.js +136 -0
  36. package/mcp/entwurf-bridge/dist/pi-extensions/lib/session-id.js +8 -5
  37. package/mcp/entwurf-bridge/dist/pi-extensions/lib/socket-discovery.js +3 -3
  38. package/mcp/entwurf-bridge/dist/scripts/meta-facts.js +51 -0
  39. package/mcp/entwurf-bridge/dist/scripts/new-session-id.js +9 -4
  40. package/mcp/entwurf-bridge/src/index.ts +173 -28
  41. package/mcp/entwurf-bridge/start.sh +2 -2
  42. package/mcp/entwurf-bridge/test.sh +23 -9
  43. package/mcp/entwurf-bridge/tsconfig.build.json +11 -2
  44. package/package.json +22 -11
  45. package/pi-extensions/entwurf-control.ts +227 -40
  46. package/pi-extensions/lib/acp/backend.ts +71 -12
  47. package/pi-extensions/lib/acp/overlay.ts +8 -6
  48. package/pi-extensions/lib/classify-tmux-cwd.ts +50 -0
  49. package/pi-extensions/lib/entwurf-control-rpc.ts +7 -5
  50. package/pi-extensions/lib/entwurf-core.ts +15 -15
  51. package/pi-extensions/lib/entwurf-resume-args.ts +41 -52
  52. package/pi-extensions/lib/entwurf-v2-contract-schema.ts +1 -1
  53. package/pi-extensions/lib/entwurf-v2-contract.ts +120 -99
  54. package/pi-extensions/lib/entwurf-v2-decider.ts +30 -91
  55. package/pi-extensions/lib/entwurf-v2-lock.ts +16 -7
  56. package/pi-extensions/lib/entwurf-v2-production.ts +4 -78
  57. package/pi-extensions/lib/entwurf-v2-release.ts +25 -49
  58. package/pi-extensions/lib/entwurf-v2-runner.ts +6 -21
  59. package/pi-extensions/lib/entwurf-v2-send-fallback.ts +12 -11
  60. package/pi-extensions/lib/entwurf-v2-send.ts +2 -7
  61. package/pi-extensions/lib/entwurf-v2-surface.ts +36 -76
  62. package/pi-extensions/lib/entwurf-v2-visible-resume.ts +370 -0
  63. package/pi-extensions/lib/meta-session.ts +93 -5
  64. package/pi-extensions/lib/mux-fresh-call.ts +381 -0
  65. package/pi-extensions/lib/mux-launch.ts +267 -0
  66. package/pi-extensions/lib/mux-placement.ts +387 -0
  67. package/pi-extensions/lib/mux-resume-call.ts +189 -0
  68. package/pi-extensions/lib/resume-launch-identity.ts +162 -0
  69. package/pi-extensions/lib/session-id.js +8 -5
  70. package/pi-extensions/lib/socket-discovery.ts +3 -3
  71. package/run.sh +471 -201
  72. package/scripts/agy-bridge-config.py +5 -1
  73. package/scripts/check-acp-backend-preflight.ts +1 -1
  74. package/scripts/check-acp-overlay.ts +13 -3
  75. package/scripts/check-acp-stream-hooks.ts +504 -0
  76. package/scripts/check-elapsed.sh +25 -0
  77. package/scripts/check-entwurf-bridge-boot.ts +51 -4
  78. package/scripts/check-entwurf-bridge-pi-free.ts +6 -5
  79. package/scripts/check-entwurf-control-rpc.ts +4 -3
  80. package/scripts/check-entwurf-resume-args.ts +72 -70
  81. package/scripts/check-entwurf-session-identity.ts +14 -10
  82. package/scripts/check-entwurf-v2-contract.ts +34 -59
  83. package/scripts/check-entwurf-v2-decider.ts +17 -177
  84. package/scripts/check-entwurf-v2-lock.ts +5 -2
  85. package/scripts/check-entwurf-v2-matrix.ts +3 -53
  86. package/scripts/check-entwurf-v2-production.ts +2 -91
  87. package/scripts/check-entwurf-v2-release.ts +10 -105
  88. package/scripts/check-entwurf-v2-runner.ts +4 -85
  89. package/scripts/check-entwurf-v2-send-fallback.ts +5 -6
  90. package/scripts/check-entwurf-v2-send.ts +0 -28
  91. package/scripts/check-entwurf-v2-surface.ts +157 -128
  92. package/scripts/check-entwurf-v2-visible-resume.ts +445 -0
  93. package/scripts/check-fresh-cut-gate.sh +1 -1
  94. package/scripts/check-gate-qualification.ts +99 -7
  95. package/scripts/check-install-container.sh +10 -2
  96. package/scripts/check-install-surface.ts +1 -1
  97. package/scripts/check-keyset-overlap.py +1 -1
  98. package/scripts/check-meta-facts.ts +249 -0
  99. package/scripts/check-meta-identity-consumers.ts +1 -1
  100. package/scripts/check-meta-session.ts +169 -0
  101. package/scripts/check-mux-launch-tmux.ts +316 -0
  102. package/scripts/check-mux-launch.ts +288 -0
  103. package/scripts/check-mux-launcher-fence.ts +264 -0
  104. package/scripts/check-mux-parent-artifact.ts +195 -0
  105. package/scripts/check-mux-placement-tmux.ts +322 -0
  106. package/scripts/check-mux-placement.ts +323 -0
  107. package/scripts/check-mux-resume-call.ts +283 -0
  108. package/scripts/check-probe-cli-shim.ts +25 -22
  109. package/scripts/check-probe-ordering.ts +84 -76
  110. package/scripts/check-release-gate-outcomes.ts +127 -7
  111. package/scripts/check-resume-launch-identity.ts +244 -0
  112. package/scripts/check-socket-discovery.ts +1 -1
  113. package/scripts/fixtures/mux-parent-transcript.scrubbed.jsonl +3 -0
  114. package/scripts/inventory-verification-surface.ts +349 -0
  115. package/scripts/lib/claude-launcher-fence.ts +322 -0
  116. package/scripts/lib/mutation-qualify.ts +109 -3
  117. package/scripts/meta-bridge-doctor.sh +6 -8
  118. package/scripts/meta-bridge-state.py +75 -1
  119. package/scripts/meta-facts.ts +60 -0
  120. package/scripts/mutants/acp-overlay.json +17 -0
  121. package/scripts/mutants/acp-stream-hooks.json +158 -0
  122. package/scripts/mutants/bridge-boot-resume.json +45 -0
  123. package/scripts/mutants/meta-facts.json +50 -0
  124. package/scripts/mutants/meta-identity.json +36 -0
  125. package/scripts/mutants/meta-retire.json +47 -0
  126. package/scripts/mutants/mux-boundary.json +196 -0
  127. package/scripts/mutants/mux-fresh-call.json +229 -0
  128. package/scripts/mutants/mux-launcher-fence.json +123 -0
  129. package/scripts/mutants/mux-parent-artifact.json +39 -0
  130. package/scripts/mutants/mux-resume-call.json +148 -0
  131. package/scripts/mutants/probe-ordering.json +0 -1037
  132. package/scripts/mutants/release-gate.json +35 -0
  133. package/scripts/mutants/resume-args.json +76 -0
  134. package/scripts/mutants/resume-launch-identity.json +96 -0
  135. package/scripts/mutants/v2-surface.json +58 -18
  136. package/scripts/mutants/v2-visible-resume.json +215 -0
  137. package/scripts/new-session-id.ts +9 -4
  138. package/scripts/smoke-acp-raw-turn-live.ts +1 -1
  139. package/scripts/smoke-agy-native-push-live.ts +6 -17
  140. package/scripts/smoke-entwurf-chain-live.ts +11 -3
  141. package/scripts/smoke-entwurf-v2-matrix-live.ts +1 -1
  142. package/scripts/smoke-meta-honesty.sh +1 -1
  143. package/scripts/smoke-meta-install-state.sh +169 -3
  144. package/scripts/smoke-mux-fresh-call-live.ts +365 -0
  145. package/scripts/smoke-mux-lifecycle-live.ts +1136 -0
  146. package/scripts/smoke-pi-attach.ts +1 -1
  147. package/scripts/smoke-user-scope-citizen.sh +1 -1
  148. package/scripts/tsconfig.json +1 -0
  149. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-preflight.js +0 -160
  150. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-spawn-production.js +0 -273
  151. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-spawn.js +0 -216
  152. package/pi-extensions/lib/entwurf-v2-spawn-production.ts +0 -373
  153. package/pi-extensions/lib/entwurf-v2-spawn.ts +0 -323
  154. package/scripts/check-acp-sdk-surface.ts +0 -275
  155. package/scripts/check-entwurf-v2-spawn-production.ts +0 -551
  156. package/scripts/check-entwurf-v2-spawn.ts +0 -399
  157. package/scripts/smoke-entwurf-v2-spawn-live.ts +0 -188
  158. package/scripts/smoke-entwurf-v2-spawn-resume-live.ts +0 -467
@@ -12,6 +12,42 @@
12
12
  "timeoutSeconds": 120,
13
13
  "signature": "[QK:META-FIFO-NONBLOCK]",
14
14
  "signatureSource": "scripts/check-meta-identity-consumers.ts"
15
+ },
16
+ {
17
+ "claim": "META-CREATE-EXCLUSIVE",
18
+ "title": "reverting the exclusive CREATE publish link to rename silently replaces an existing citizen record on a garden-id collision (#66)",
19
+ "subject": "pi-extensions/lib/meta-session.ts",
20
+ "find": ["\t\tfs.linkSync(tmp, file);"],
21
+ "replace": ["\t\tfs.renameSync(tmp, file);"],
22
+ "gate": ["bash", "run.sh", "check-meta-session"],
23
+ "timeoutSeconds": 120,
24
+ "signature": "[QK:META-CREATE-EXCLUSIVE]",
25
+ "signatureSource": "scripts/check-meta-session.ts"
26
+ },
27
+ {
28
+ "claim": "META-CREATE-WIRING",
29
+ "title": "routing the CREATE action back through the replacing attach write reopens the silent-overwrite path at the upsert choke point (#66)",
30
+ "subject": "pi-extensions/lib/meta-session.ts",
31
+ "find": [
32
+ "\tif (decision.action === \"create\") publishExclusiveIdentity(file, decision.record);",
33
+ "\telse atomicWriteIdentity(file, decision.record);"
34
+ ],
35
+ "replace": ["\tatomicWriteIdentity(file, decision.record);"],
36
+ "gate": ["bash", "run.sh", "check-meta-session"],
37
+ "timeoutSeconds": 120,
38
+ "signature": "[QK:META-CREATE-WIRING]",
39
+ "signatureSource": "scripts/check-meta-session.ts"
40
+ },
41
+ {
42
+ "claim": "META-CREATE-PUBLISHED-CLEANUP",
43
+ "title": "swallowing a post-publish temp-cleanup failure reports a clean create while a stale tmp lingers beside the record (#66 false success)",
44
+ "subject": "pi-extensions/lib/meta-session.ts",
45
+ "find": ["\t\tif ((err as { code?: unknown }).code === \"ENOENT\") return;"],
46
+ "replace": ["\t\treturn;"],
47
+ "gate": ["bash", "run.sh", "check-meta-session"],
48
+ "timeoutSeconds": 120,
49
+ "signature": "[QK:META-CREATE-PUBLISHED-CLEANUP]",
50
+ "signatureSource": "scripts/check-meta-session.ts"
15
51
  }
16
52
  ]
17
53
  }
@@ -0,0 +1,47 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "lane": "meta-retire",
4
+ "mutants": [
5
+ {
6
+ "claim": "META-RETIRE-COMPARE-TYPE",
7
+ "title": "comparing the current value without its JSON scalar type lets a truthy 1 pass as the true entwurf wrote, so relinquishment overwrites a value that was never ours (#71)",
8
+ "subject": "scripts/meta-bridge-state.py",
9
+ "find": [
10
+ " existed, value = get_nested(settings, path)",
11
+ " if existed and type(value) is type(last_managed_value) and value == last_managed_value:"
12
+ ],
13
+ "replace": [" existed, value = get_nested(settings, path)", " if existed and value == last_managed_value:"],
14
+ "gate": ["bash", "run.sh", "smoke-meta-install-state"],
15
+ "timeoutSeconds": 300,
16
+ "signature": "[QK:META-RETIRE-COMPARE-TYPE]",
17
+ "signatureSource": "scripts/smoke-meta-install-state.sh"
18
+ },
19
+ {
20
+ "claim": "META-RETIRE-MALFORMED-SILENT",
21
+ "title": "skipping a malformed retired entry instead of failing discards the only evidence that entwurf owns the key, leaving a still-dangerous value looking operator-clean (#71)",
22
+ "subject": "scripts/meta-bridge-state.py",
23
+ "find": [
24
+ " die(f\"retired scalar state entry {name} is malformed; refusing to discard ownership evidence\")"
25
+ ],
26
+ "replace": [" return"],
27
+ "gate": ["bash", "run.sh", "smoke-meta-install-state"],
28
+ "timeoutSeconds": 300,
29
+ "signature": "[QK:META-RETIRE-MALFORMED-SILENT]",
30
+ "signatureSource": "scripts/smoke-meta-install-state.sh"
31
+ },
32
+ {
33
+ "claim": "META-RETIRE-FRESH-TOUCH",
34
+ "title": "synthesising provenance when install-state carries none lets relinquishment rewrite a value the operator set themselves, the exact ownership violation #71 exists to stop",
35
+ "subject": "scripts/meta-bridge-state.py",
36
+ "find": [" if entry is None:", " return"],
37
+ "replace": [
38
+ " if entry is None:",
39
+ " entry = {\"kind\": \"scalar\", \"path\": path, \"original\": {\"existed\": False, \"value\": None}}"
40
+ ],
41
+ "gate": ["bash", "run.sh", "smoke-meta-install-state"],
42
+ "timeoutSeconds": 300,
43
+ "signature": "[QK:META-RETIRE-FRESH-TOUCH]",
44
+ "signatureSource": "scripts/smoke-meta-install-state.sh"
45
+ }
46
+ ]
47
+ }
@@ -0,0 +1,196 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "lane": "mux-boundary",
4
+ "mutants": [
5
+ {
6
+ "claim": "V2-DELIVERY-EXCLUDES-MUX",
7
+ "title": "a mux launch handle is reintroduced as an entwurf_v2 delivery receipt transport",
8
+ "subject": "pi-extensions/lib/entwurf-v2-contract.ts",
9
+ "find": [
10
+ "export const ENTWURF_V2_TRANSPORTS = [\"control-socket\", \"meta-mailbox\", \"native-push\"] as const;"
11
+ ],
12
+ "replace": [
13
+ "export const ENTWURF_V2_TRANSPORTS = [",
14
+ "\t\"control-socket\",",
15
+ "\t\"tmux-live\",",
16
+ "\t\"meta-mailbox\",",
17
+ "\t\"native-push\",",
18
+ "] as const;"
19
+ ],
20
+ "gate": ["bash", "run.sh", "check-entwurf-v2-contract"],
21
+ "timeoutSeconds": 60,
22
+ "signature": "[QK:V2-DELIVERY-EXCLUDES-MUX]",
23
+ "signatureSource": "scripts/check-entwurf-v2-contract.ts"
24
+ },
25
+ {
26
+ "claim": "MUX-ANCHOR-REQUIRED",
27
+ "title": "TMUX is no longer required, so a caller holding only a pane id makes a bare tmux resolve to the DEFAULT server",
28
+ "subject": "pi-extensions/lib/mux-placement.ts",
29
+ "find": [
30
+ "\tif (typeof server !== \"string\" || server.length === 0) return { ok: false, reason: \"no-tmux-context\" };"
31
+ ],
32
+ "replace": ["\tvoid server;"],
33
+ "gate": ["bash", "run.sh", "check-mux-placement"],
34
+ "timeoutSeconds": 60,
35
+ "signature": "[QK:MUX-ANCHOR-REQUIRED]",
36
+ "signatureSource": "scripts/check-mux-placement.ts"
37
+ },
38
+ {
39
+ "claim": "MUX-SELECTOR-NATIVE-ID",
40
+ "title": "selector validation is disabled, so a forged `-t` value reaches tmux's own parser",
41
+ "subject": "pi-extensions/lib/mux-placement.ts",
42
+ "find": ["\tif (!okShape) {"],
43
+ "replace": ["\tif (false && !okShape) {"],
44
+ "gate": ["bash", "run.sh", "check-mux-placement"],
45
+ "timeoutSeconds": 60,
46
+ "signature": "[QK:MUX-SELECTOR-NATIVE-ID]",
47
+ "signatureSource": "scripts/check-mux-placement.ts"
48
+ },
49
+ {
50
+ "claim": "MUX-TMUX-FAILURE-LOUD",
51
+ "title": "a nonzero or signalled tmux run is swallowed, so `no server running` is laundered into a fact about the pane",
52
+ "subject": "pi-extensions/lib/mux-placement.ts",
53
+ "find": ["\tif (run.status === 0) return;"],
54
+ "replace": ["\tif (run.status === 0 || run.status !== 0) return;"],
55
+ "gate": ["bash", "run.sh", "check-mux-placement"],
56
+ "timeoutSeconds": 60,
57
+ "signature": "[QK:MUX-TMUX-FAILURE-LOUD]",
58
+ "signatureSource": "scripts/check-mux-placement.ts"
59
+ },
60
+ {
61
+ "claim": "MUX-ANCHOR-ECHO-BACK",
62
+ "title": "the pane-id echo-back is dropped, so tmux's rc=0 answer about ANOTHER pane is accepted as the caller's placement",
63
+ "subject": "pi-extensions/lib/mux-placement.ts",
64
+ "find": ["\tif (paneId !== anchor) return { ok: false, reason: \"anchor-mismatch\" };"],
65
+ "replace": ["\t// echo-back dropped"],
66
+ "gate": ["bash", "run.sh", "check-mux-placement"],
67
+ "timeoutSeconds": 60,
68
+ "signature": "[QK:MUX-ANCHOR-ECHO-BACK]",
69
+ "signatureSource": "scripts/check-mux-placement.ts"
70
+ },
71
+ {
72
+ "claim": "MUX-APPEND-END-DETACHED",
73
+ "title": "append drops -a/{end} and becomes a lowest-free-index new-window, a different verb from appending at the session end",
74
+ "subject": "pi-extensions/lib/mux-placement.ts",
75
+ "find": [
76
+ "\treturn [\"new-window\", \"-d\", \"-a\", \"-t\", `${sessionId}:{end}`, \"-P\", \"-F\", APPEND_FORMAT];"
77
+ ],
78
+ "replace": ["\treturn [\"new-window\", \"-d\", \"-t\", `${sessionId}:`, \"-P\", \"-F\", APPEND_FORMAT];"],
79
+ "gate": ["bash", "run.sh", "check-mux-placement"],
80
+ "timeoutSeconds": 60,
81
+ "signature": "[QK:MUX-APPEND-END-DETACHED]",
82
+ "signatureSource": "scripts/check-mux-placement.ts"
83
+ },
84
+ {
85
+ "claim": "MUX-CLOSE-BY-WINDOW-ID",
86
+ "title": "a failed kill-window is reported as already-gone without proving absence, so a window that is still open reads as closed",
87
+ "subject": "pi-extensions/lib/mux-placement.ts",
88
+ "find": ["\treturn ids.includes(windowId) ? null : \"already-gone\";"],
89
+ "replace": ["\treturn \"already-gone\";"],
90
+ "gate": ["bash", "run.sh", "check-mux-placement"],
91
+ "timeoutSeconds": 60,
92
+ "signature": "[QK:MUX-CLOSE-BY-WINDOW-ID]",
93
+ "signatureSource": "scripts/check-mux-placement.ts"
94
+ },
95
+ {
96
+ "claim": "MUX-CONTEXT-BOUND-MUTATION",
97
+ "title": "context binding matches on the session id alone, so a restarted or foreign server with the same $N is accepted",
98
+ "subject": "pi-extensions/lib/mux-placement.ts",
99
+ "find": ["\treturn origin.serverPid === now.serverPid && origin.sessionId === now.sessionId;"],
100
+ "replace": ["\treturn origin.sessionId === now.sessionId;"],
101
+ "gate": ["bash", "run.sh", "check-mux-placement"],
102
+ "timeoutSeconds": 60,
103
+ "signature": "[QK:MUX-CONTEXT-BOUND-MUTATION]",
104
+ "signatureSource": "scripts/check-mux-placement.ts"
105
+ },
106
+ {
107
+ "claim": "MUX-LAUNCH-PRECONDITION-FIRST",
108
+ "title": "the runtime precondition is skipped, so a window is opened onto a runtime that cannot start and the receipt looks identical to a good launch",
109
+ "subject": "pi-extensions/lib/mux-launch.ts",
110
+ "find": ["\tif (!isAbsolute(runtimePath)) {"],
111
+ "replace": ["\tif (false && !isAbsolute(runtimePath)) {"],
112
+ "gate": ["bash", "run.sh", "check-mux-launch"],
113
+ "timeoutSeconds": 60,
114
+ "signature": "[QK:MUX-LAUNCH-PRECONDITION-FIRST]",
115
+ "signatureSource": "scripts/check-mux-launch.ts"
116
+ },
117
+ {
118
+ "claim": "MUX-LAUNCH-PATH-NO-SPLIT",
119
+ "title": "the whitespace guard is dropped, so tmux re-splits a spaced runtime path and launches a truncated binary with the remainder as an argument",
120
+ "subject": "pi-extensions/lib/mux-launch.ts",
121
+ "find": ["\tif (WHITESPACE.test(runtimePath)) {"],
122
+ "replace": ["\tif (false && WHITESPACE.test(runtimePath)) {"],
123
+ "gate": ["bash", "run.sh", "check-mux-launch"],
124
+ "timeoutSeconds": 60,
125
+ "signature": "[QK:MUX-LAUNCH-PATH-NO-SPLIT]",
126
+ "signatureSource": "scripts/check-mux-launch.ts"
127
+ },
128
+ {
129
+ "claim": "MUX-LAUNCH-PATH-HIT-NOT-SKIPPED",
130
+ "title": "the launchability proof is moved inside the PATH loop's catch, so an unlaunchable FIRST hit is silently skipped and a later entry launches a different install than the operator's own `pi`",
131
+ "subject": "pi-extensions/lib/mux-launch.ts",
132
+ "find": [
133
+ "\t\t} catch {",
134
+ "\t\t\tcontinue;",
135
+ "\t\t}",
136
+ "\t\tassertLaunchTarget(candidate);",
137
+ "\t\treturn candidate;"
138
+ ],
139
+ "replace": [
140
+ "\t\t\tassertLaunchTarget(candidate);",
141
+ "\t\t} catch {",
142
+ "\t\t\tcontinue;",
143
+ "\t\t}",
144
+ "\t\treturn candidate;"
145
+ ],
146
+ "gate": ["bash", "run.sh", "check-mux-launch"],
147
+ "timeoutSeconds": 60,
148
+ "signature": "[QK:MUX-LAUNCH-PATH-HIT-NOT-SKIPPED]",
149
+ "signatureSource": "scripts/check-mux-launch.ts"
150
+ },
151
+ {
152
+ "claim": "MUX-LAUNCH-NO-SHELL-ARGV",
153
+ "title": "the `--` separator is dropped, so tmux takes the runtime as a shell-command string instead of exec'ing it directly",
154
+ "subject": "pi-extensions/lib/mux-launch.ts",
155
+ "find": [
156
+ "\treturn [\"new-window\", \"-d\", \"-a\", \"-t\", `${sessionId}:{end}`, \"-P\", \"-F\", APPEND_FORMAT, \"--\", runtimePath];"
157
+ ],
158
+ "replace": [
159
+ "\treturn [\"new-window\", \"-d\", \"-a\", \"-t\", `${sessionId}:{end}`, \"-P\", \"-F\", APPEND_FORMAT, runtimePath];"
160
+ ],
161
+ "gate": ["bash", "run.sh", "check-mux-launch"],
162
+ "timeoutSeconds": 60,
163
+ "signature": "[QK:MUX-LAUNCH-NO-SHELL-ARGV]",
164
+ "signatureSource": "scripts/check-mux-launch.ts"
165
+ },
166
+ {
167
+ "claim": "MUX-LAUNCH-NO-CARRIER",
168
+ "title": "a window-name carrier is reintroduced into the launch argv, handing tmux's own parser a value this rail did not author",
169
+ "subject": "pi-extensions/lib/mux-launch.ts",
170
+ "find": [
171
+ "\treturn [\"new-window\", \"-d\", \"-a\", \"-t\", `${sessionId}:{end}`, \"-P\", \"-F\", APPEND_FORMAT, \"--\", runtimePath];"
172
+ ],
173
+ "replace": [
174
+ "\treturn [\"new-window\", \"-d\", \"-a\", \"-n\", PI_RUNTIME_COMMAND, \"-t\", `${sessionId}:{end}`, \"-P\", \"-F\", APPEND_FORMAT, \"--\", runtimePath];"
175
+ ],
176
+ "gate": ["bash", "run.sh", "check-mux-launch"],
177
+ "timeoutSeconds": 60,
178
+ "signature": "[QK:MUX-LAUNCH-NO-CARRIER]",
179
+ "signatureSource": "scripts/check-mux-launch.ts"
180
+ },
181
+ {
182
+ "claim": "MUX-LAUNCH-CORE-IMPORT-FREE",
183
+ "title": "the launch composition is imported by the delivery core, making fresh launch a dependency of entwurf_v2 delivery",
184
+ "subject": "pi-extensions/lib/entwurf-v2-production.ts",
185
+ "find": ["import { isOutOfSocketDomainGardenIdConflict } from \"./entwurf-facts.ts\";"],
186
+ "replace": [
187
+ "import { isOutOfSocketDomainGardenIdConflict } from \"./entwurf-facts.ts\";",
188
+ "import { launchPi as _launchPi } from \"./mux-launch.ts\";"
189
+ ],
190
+ "gate": ["bash", "run.sh", "check-mux-launch"],
191
+ "timeoutSeconds": 60,
192
+ "signature": "[QK:MUX-LAUNCH-CORE-IMPORT-FREE]",
193
+ "signatureSource": "scripts/check-mux-launch.ts"
194
+ }
195
+ ]
196
+ }
@@ -0,0 +1,229 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "lane": "mux-fresh-call",
4
+ "mutants": [
5
+ {
6
+ "claim": "FRESHCALL-PI-ARGV-PROMPT-FIRST",
7
+ "title": "pi gets the flag before the prompt again — the measured shape that opens a window whose first turn never runs",
8
+ "subject": "pi-extensions/lib/mux-fresh-call.ts",
9
+ "find": ["\t\t\treturn [prompt, \"--entwurf-control\", \"--model\", model];"],
10
+ "replace": ["\t\t\treturn [\"--entwurf-control\", prompt, \"--model\", model];"],
11
+ "gate": ["bash", "run.sh", "check-mux-fresh-call"],
12
+ "timeoutSeconds": 120,
13
+ "signature": "[QK:FRESHCALL-PI-ARGV-PROMPT-FIRST]",
14
+ "signatureSource": "test/mux-fresh-call.test.ts"
15
+ },
16
+ {
17
+ "claim": "FRESHCALL-CLAUDE-ARGV-EQUALS-FORM",
18
+ "title": "claude-code goes back to the variadic space form, which swallows the prompt as an option value",
19
+ "subject": "pi-extensions/lib/mux-fresh-call.ts",
20
+ "find": [
21
+ "\t\t\treturn [prompt, `--allowedTools=${FRESH_CALL_CALLBACK_TOOL[\"claude-code\"]}`, `--model=${model}`];"
22
+ ],
23
+ "replace": [
24
+ "\t\t\treturn [prompt, \"--allowedTools\", FRESH_CALL_CALLBACK_TOOL[\"claude-code\"], `--model=${model}`];"
25
+ ],
26
+ "gate": ["bash", "run.sh", "check-mux-fresh-call"],
27
+ "timeoutSeconds": 120,
28
+ "signature": "[QK:FRESHCALL-CLAUDE-ARGV-EQUALS-FORM]",
29
+ "signatureSource": "test/mux-fresh-call.test.ts"
30
+ },
31
+ {
32
+ "claim": "FRESHCALL-PI-MODEL-ARGV",
33
+ "title": "pi drops its measured two-token model option and silently resolves its ambient default again",
34
+ "subject": "pi-extensions/lib/mux-fresh-call.ts",
35
+ "find": ["\t\t\treturn [prompt, \"--entwurf-control\", \"--model\", model];"],
36
+ "replace": ["\t\t\treturn [prompt, \"--entwurf-control\"];"],
37
+ "gate": ["bash", "run.sh", "check-mux-fresh-call"],
38
+ "timeoutSeconds": 120,
39
+ "signature": "[QK:FRESHCALL-PI-MODEL-ARGV]",
40
+ "signatureSource": "test/mux-fresh-call.test.ts"
41
+ },
42
+ {
43
+ "claim": "FRESHCALL-PI-MODEL-DIALECT",
44
+ "title": "pi model argv is unified with Claude Code into the equals form that the real Pi parser rejects",
45
+ "subject": "pi-extensions/lib/mux-fresh-call.ts",
46
+ "find": ["\t\t\treturn [prompt, \"--entwurf-control\", \"--model\", model];"],
47
+ "replace": ["\t\t\treturn [prompt, \"--entwurf-control\", `--model=${model}`];"],
48
+ "gate": ["bash", "run.sh", "check-mux-fresh-call"],
49
+ "timeoutSeconds": 120,
50
+ "signature": "[QK:FRESHCALL-PI-MODEL-DIALECT]",
51
+ "signatureSource": "test/mux-fresh-call.test.ts"
52
+ },
53
+ {
54
+ "claim": "FRESHCALL-CLAUDE-MODEL-ARGV",
55
+ "title": "Claude Code drops the explicit model token and silently resolves its ambient Opus default again",
56
+ "subject": "pi-extensions/lib/mux-fresh-call.ts",
57
+ "find": [
58
+ "\t\t\treturn [prompt, `--allowedTools=${FRESH_CALL_CALLBACK_TOOL[\"claude-code\"]}`, `--model=${model}`];"
59
+ ],
60
+ "replace": ["\t\t\treturn [prompt, `--allowedTools=${FRESH_CALL_CALLBACK_TOOL[\"claude-code\"]}`];"],
61
+ "gate": ["bash", "run.sh", "check-mux-fresh-call"],
62
+ "timeoutSeconds": 120,
63
+ "signature": "[QK:FRESHCALL-CLAUDE-MODEL-ARGV]",
64
+ "signatureSource": "test/mux-fresh-call.test.ts"
65
+ },
66
+ {
67
+ "claim": "FRESHCALL-CALLBACK-PRECEDES-TASK",
68
+ "title": "the task is framed before the callback instruction, so a sibling that stalls in the task never names itself",
69
+ "subject": "pi-extensions/lib/mux-fresh-call.ts",
70
+ "find": ["\t\t`FIRST ACTION, before reading files or anything else: call ${tool} with ` +"],
71
+ "replace": ["\t\t`FINAL ACTION, after completing the task: call ${tool} with ` +"],
72
+ "gate": ["bash", "run.sh", "check-mux-fresh-call"],
73
+ "timeoutSeconds": 120,
74
+ "signature": "[QK:FRESHCALL-CALLBACK-PRECEDES-TASK]",
75
+ "signatureSource": "test/mux-fresh-call.test.ts"
76
+ },
77
+ {
78
+ "claim": "FRESHCALL-RECEIPT-WITHOUT-CORRELATION",
79
+ "title": "the launch receipt grows a sibling garden-id field, making an async correlation look like a synchronous return",
80
+ "subject": "pi-extensions/lib/mux-fresh-call.ts",
81
+ "find": ["\truntimePath: string;\n\tnonce: string;\n}"],
82
+ "replace": ["\truntimePath: string;\n\tnonce: string;\n\tgardenId?: string;\n}"],
83
+ "gate": ["bash", "run.sh", "check-mux-fresh-call"],
84
+ "timeoutSeconds": 120,
85
+ "signature": "[QK:FRESHCALL-RECEIPT-WITHOUT-CORRELATION]",
86
+ "signatureSource": "test/mux-fresh-call.test.ts"
87
+ },
88
+ {
89
+ "claim": "FRESHCALL-CWD-ARGV",
90
+ "title": "the requested cwd stops reaching tmux, so a cross-repo fresh sibling silently opens in the CALLER's repo and the launch looks successful",
91
+ "subject": "pi-extensions/lib/mux-fresh-call.ts",
92
+ "find": ["\t\t...(cwd === undefined ? [] : [\"-c\", cwd]),\n"],
93
+ "replace": [""],
94
+ "gate": ["bash", "run.sh", "check-mux-fresh-call"],
95
+ "timeoutSeconds": 120,
96
+ "signature": "[QK:FRESHCALL-CWD-ARGV]",
97
+ "signatureSource": "test/mux-fresh-call.test.ts"
98
+ },
99
+ {
100
+ "claim": "FRESHCALL-CWD-REFUSED-PREMUTATION",
101
+ "title": "the pre-mutation cwd classification is ignored, so a relative/'#'/deleted/file path rides on toward tmux instead of refusing by name",
102
+ "subject": "pi-extensions/lib/mux-fresh-call.ts",
103
+ "find": ["\t\tif (badCwd) return { ok: false, reason: badCwd };"],
104
+ "replace": ["\t\tvoid badCwd;"],
105
+ "gate": ["bash", "run.sh", "check-mux-fresh-call"],
106
+ "timeoutSeconds": 120,
107
+ "signature": "[QK:FRESHCALL-CWD-REFUSED-PREMUTATION]",
108
+ "signatureSource": "test/mux-fresh-call.test.ts"
109
+ },
110
+ {
111
+ "claim": "FRESHCALL-CWD-RECEIPT-REQUESTED",
112
+ "title": "production freshCall stops assembling the requested cwd into its receipt, so the operator-visible answer silently loses the one placement fact the caller asked for",
113
+ "subject": "pi-extensions/lib/mux-fresh-call.ts",
114
+ "find": ["\t\t\t...(cwd === undefined ? {} : { cwd }),\n"],
115
+ "replace": [""],
116
+ "gate": ["bash", "run.sh", "check-mux-fresh-call"],
117
+ "timeoutSeconds": 120,
118
+ "signature": "[QK:FRESHCALL-CWD-RECEIPT-REQUESTED]",
119
+ "signatureSource": "test/mux-fresh-call.test.ts"
120
+ },
121
+ {
122
+ "claim": "FRESHCALL-CWD-SURFACE-PARITY",
123
+ "title": "one surface drops the optional cwd from its schema, so cross-repo fresh works from pi and silently cannot exist from the MCP bridge",
124
+ "subject": "mcp/entwurf-bridge/src/index.ts",
125
+ "find": [
126
+ "\t\tcwd: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t\"Optional literal ABSOLUTE path of an existing directory to start the sibling in (cross-repo fresh). Omit or pass \\\"\\\" to start in this agent's own cwd. Taken exactly as given — no trim, no realpath, no project-name resolution; '#' is refused (tmux format expansion). The receipt echoes what was REQUESTED, never an observation.\",\n\t\t\t),\n"
127
+ ],
128
+ "replace": [""],
129
+ "gate": ["bash", "run.sh", "check-mux-fresh-call"],
130
+ "timeoutSeconds": 120,
131
+ "signature": "[QK:FRESHCALL-CWD-SURFACE-PARITY]",
132
+ "signatureSource": "test/fresh-call-surfaces.contract.test.ts"
133
+ },
134
+ {
135
+ "claim": "FRESHCALL-PI-SURFACE-IDENTITY",
136
+ "title": "native pi takes the caller garden id from the environment instead of its own resident closure — the uuidv7 confusion, reintroduced",
137
+ "subject": "pi-extensions/entwurf-control.ts",
138
+ "find": ["\t\t\t\t\tcallerGardenId: residentGardenId,"],
139
+ "replace": ["\t\t\t\t\tcallerGardenId: process.env.PI_SESSION_ID ?? null,"],
140
+ "gate": ["bash", "run.sh", "check-mux-fresh-call"],
141
+ "timeoutSeconds": 120,
142
+ "signature": "[QK:FRESHCALL-PI-SURFACE-IDENTITY]",
143
+ "signatureSource": "test/fresh-call-surfaces.contract.test.ts"
144
+ },
145
+ {
146
+ "claim": "FRESHCALL-PI-MODEL-SCHEMA",
147
+ "title": "native pi drops the required model field from fresh-call while the composition still expects one",
148
+ "subject": "pi-extensions/entwurf-control.ts",
149
+ "find": [
150
+ "\t\t\tmodel: Type.String({\n\t\t\t\tminLength: 1,\n\t\t\t\tmaxLength: 200,\n\t\t\t\tpattern: \"^[A-Za-z0-9][A-Za-z0-9._/:\\\\[\\\\]-]*$\",\n\t\t\t\tdescription: \"Required runtime model: canonical provider/model for pi, or a Claude Code model id/alias.\",\n\t\t\t}),\n"
151
+ ],
152
+ "replace": [""],
153
+ "gate": ["bash", "run.sh", "check-mux-fresh-call"],
154
+ "timeoutSeconds": 120,
155
+ "signature": "[QK:FRESHCALL-PI-MODEL-SCHEMA]",
156
+ "signatureSource": "test/fresh-call-surfaces.contract.test.ts"
157
+ },
158
+ {
159
+ "claim": "FRESHCALL-CLAUDE-SURFACE-IDENTITY",
160
+ "title": "the MCP bridge reads the caller garden id straight out of env instead of its authoritative self envelope",
161
+ "subject": "mcp/entwurf-bridge/src/index.ts",
162
+ "find": [
163
+ "\t\t\tconst self = await buildAuthoritativeSelfEnvelope();\n\t\t\tcallerGardenId = self.envelope.sessionId;"
164
+ ],
165
+ "replace": ["\t\t\tcallerGardenId = process.env.PI_SESSION_ID ?? null;"],
166
+ "gate": ["bash", "run.sh", "check-mux-fresh-call"],
167
+ "timeoutSeconds": 120,
168
+ "signature": "[QK:FRESHCALL-CLAUDE-SURFACE-IDENTITY]",
169
+ "signatureSource": "test/fresh-call-surfaces.contract.test.ts"
170
+ },
171
+ {
172
+ "claim": "FRESHCALL-CLAUDE-MODEL-SCHEMA",
173
+ "title": "MCP drops the required model field from fresh-call while the composition still expects one",
174
+ "subject": "mcp/entwurf-bridge/src/index.ts",
175
+ "find": [
176
+ "\t\tmodel: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(200)\n\t\t\t// The `\\[` below is USELESS TO JS AND LOAD-BEARING TO THE HOST. zod emits this source\n\t\t\t// text verbatim as the JSON Schema `pattern`, and the host's tool-schema validator is a\n\t\t\t// Rust regex engine that rejects an unescaped `[` inside a character class as \"unclosed\n\t\t\t// character class\" — a 400 on tools/list that stops EVERY Claude session from opening\n\t\t\t// (7-M-fix). Biome's safe fix removes it; do not accept that fix. The suppression must\n\t\t\t// stay on the line DIRECTLY above the expression, or it attaches to nothing and reads\n\t\t\t// as an unused suppression while the escape goes back to being fixable.\n\t\t\t// biome-ignore lint/complexity/noUselessEscapeInRegex: emitted to a Rust regex validator, see above\n\t\t\t.regex(/^[A-Za-z0-9][A-Za-z0-9._/:\\[\\]-]*$/)\n\t\t\t.describe(\"Required runtime model: canonical provider/model for pi, or a Claude Code model id/alias.\"),\n"
177
+ ],
178
+ "replace": [""],
179
+ "gate": ["bash", "run.sh", "check-mux-fresh-call"],
180
+ "timeoutSeconds": 120,
181
+ "signature": "[QK:FRESHCALL-CLAUDE-MODEL-SCHEMA]",
182
+ "signatureSource": "test/fresh-call-surfaces.contract.test.ts"
183
+ },
184
+ {
185
+ "claim": "FRESHCALL-MODEL-PATTERN-HOST-VALID",
186
+ "title": "the #62 escape replanted: biome's safe fix unescapes the zod model pattern, JS still accepts it, and the Rust-family host validator rejects the whole tool definition",
187
+ "subject": "mcp/entwurf-bridge/src/index.ts",
188
+ "find": ["\t\t\t.regex(/^[A-Za-z0-9][A-Za-z0-9._/:\\[\\]-]*$/)"],
189
+ "replace": ["\t\t\t.regex(/^[A-Za-z0-9][A-Za-z0-9._/:[\\]-]*$/)"],
190
+ "gate": ["bash", "run.sh", "check-mux-fresh-call"],
191
+ "timeoutSeconds": 120,
192
+ "signature": "[QK:FRESHCALL-MODEL-PATTERN-HOST-VALID]",
193
+ "signatureSource": "test/fresh-call-surfaces.contract.test.ts"
194
+ },
195
+ {
196
+ "claim": "FRESHCALL-MODEL-PATTERN-PROVIDER-VALID",
197
+ "title": "the same unescape planted on the TypeBox surface rides pi-ai's real anthropic-messages conversion into the request body as an invalid Rust-family pattern",
198
+ "subject": "pi-extensions/entwurf-control.ts",
199
+ "find": ["\t\t\t\tpattern: \"^[A-Za-z0-9][A-Za-z0-9._/:\\\\[\\\\]-]*$\","],
200
+ "replace": ["\t\t\t\tpattern: \"^[A-Za-z0-9][A-Za-z0-9._/:[\\\\]-]*$\","],
201
+ "gate": ["bash", "run.sh", "check-mux-fresh-call"],
202
+ "timeoutSeconds": 120,
203
+ "signature": "[QK:FRESHCALL-MODEL-PATTERN-PROVIDER-VALID]",
204
+ "signatureSource": "test/fresh-call-provider.contract.test.ts"
205
+ },
206
+ {
207
+ "claim": "PIIMPORT-WORK-SURFACE",
208
+ "title": "the measured #62 escape replanted on the floor itself: check-pi-import-surface falls back to the tracked index, so a brand-new forbidden private-subpath import is invisible until it is staged",
209
+ "subject": "run.sh",
210
+ "find": [" git ls-files -z --cached --others --exclude-standard -- '*.ts' '*.js' '*.mjs' '*.cjs'"],
211
+ "replace": [" git ls-files -z --cached -- '*.ts' '*.js' '*.mjs' '*.cjs'"],
212
+ "gate": ["bash", "run.sh", "check-pi-import-surface"],
213
+ "timeoutSeconds": 60,
214
+ "signature": "[QK:PIIMPORT-WORK-SURFACE]",
215
+ "signatureSource": "run.sh"
216
+ },
217
+ {
218
+ "claim": "VITEST-FAILED-TITLE-ATTRIBUTION",
219
+ "title": "the pilot's kill attribution collects PASSING test titles instead of failed ones, recreating the code-frame false-KILLED hole this lane exists to close",
220
+ "subject": "scripts/lib/mutation-qualify.ts",
221
+ "find": ["\t\t\tif (assertion.status !== \"failed\") continue;"],
222
+ "replace": ["\t\t\tif (assertion.status === \"failed\") continue;"],
223
+ "gate": ["node", "--experimental-strip-types", "scripts/check-gate-qualification.ts", "--attribution-self-test"],
224
+ "timeoutSeconds": 60,
225
+ "signature": "[QK:VITEST-FAILED-TITLE-ATTRIBUTION]",
226
+ "signatureSource": "scripts/check-gate-qualification.ts"
227
+ }
228
+ ]
229
+ }
@@ -0,0 +1,123 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "lane": "mux-launcher-fence",
4
+ "mutants": [
5
+ {
6
+ "claim": "LAUNCHFENCE-PREFLIGHT-FAIL-CLOSED",
7
+ "title": "the preflight stops checking where the launcher RESOLVES to, so a launcher already pointing into the fixture is pinned and launched anyway",
8
+ "subject": "scripts/lib/claude-launcher-fence.ts",
9
+ "find": [
10
+ "\tif (isInside(fixtureRoot, resolvedPath)) {",
11
+ "\t\tthrow new Error(",
12
+ "\t\t\t`claude-launcher-fence: fail-closed — the launcher's resolved target ${resolvedPath} is INSIDE the fixture cleanup root ${fixtureRoot}`,",
13
+ "\t\t);",
14
+ "\t}"
15
+ ],
16
+ "replace": ["\t// the launcher-path containment check above already walks the fixture axis"],
17
+ "gate": ["bash", "run.sh", "check-mux-launcher-fence"],
18
+ "timeoutSeconds": 120,
19
+ "signature": "[QK:LAUNCHFENCE-PREFLIGHT-FAIL-CLOSED]",
20
+ "signatureSource": "scripts/check-mux-launcher-fence.ts"
21
+ },
22
+ {
23
+ "claim": "LAUNCHFENCE-RETARGET-BLOCKS-CLEANUP",
24
+ "title": "the cleanup guard stops naming a KNOWN symlink-hop fixture reference, so the incident's dangling retarget is demoted to a vague unproven state",
25
+ "subject": "scripts/lib/claude-launcher-fence.ts",
26
+ "find": [
27
+ "\t\tif (isInside(snapshot.fixtureRoot, hop)) {",
28
+ "\t\t\treturn {",
29
+ "\t\t\t\tsafeToRemove: false,",
30
+ "\t\t\t\tproblems: [`the launcher's link target ${hop} lies inside the fixture cleanup root`],",
31
+ "\t\t\t};",
32
+ "\t\t}"
33
+ ],
34
+ "replace": ["\t\t// the realpath walk below covers the symlink hop"],
35
+ "gate": ["bash", "run.sh", "check-mux-launcher-fence"],
36
+ "timeoutSeconds": 120,
37
+ "signature": "[QK:LAUNCHFENCE-RETARGET-BLOCKS-CLEANUP]",
38
+ "signatureSource": "scripts/check-mux-launcher-fence.ts"
39
+ },
40
+ {
41
+ "claim": "LAUNCHFENCE-CONTENT-CHANGE-DETECTED",
42
+ "title": "the integrity oracle drops content identity, so an in-place rewrite of the resolved target passes teardown unreported",
43
+ "subject": "scripts/lib/claude-launcher-fence.ts",
44
+ "find": [
45
+ "\t\t} else {",
46
+ "\t\t\ttry {",
47
+ "\t\t\t\tif (sha256File(resolvedNow) !== snapshot.sha256) {",
48
+ "\t\t\t\t\tproblems.push(`the launcher's resolved target ${resolvedNow} changed content (sha256 mismatch)`);",
49
+ "\t\t\t\t}",
50
+ "\t\t\t} catch (err) {",
51
+ "\t\t\t\tproblems.push(",
52
+ "\t\t\t\t\t`the launcher's resolved target ${resolvedNow} could not be hashed: ${err instanceof Error ? err.message : String(err)}`,",
53
+ "\t\t\t\t);",
54
+ "\t\t\t}",
55
+ "\t\t}"
56
+ ],
57
+ "replace": ["\t\t}"],
58
+ "gate": ["bash", "run.sh", "check-mux-launcher-fence"],
59
+ "timeoutSeconds": 120,
60
+ "signature": "[QK:LAUNCHFENCE-CONTENT-CHANGE-DETECTED]",
61
+ "signatureSource": "scripts/check-mux-launcher-fence.ts"
62
+ },
63
+ {
64
+ "claim": "LAUNCHFENCE-XDG-EXACT-RESTORE",
65
+ "title": "an originally absent XDG variable is filled with a canonical default instead of being deleted — a guess about how the runtime reads its config, not operator parity",
66
+ "subject": "scripts/lib/claude-launcher-fence.ts",
67
+ "find": [
68
+ "\t\tif (original === null || original === undefined) delete env[name];",
69
+ "\t\telse env[name] = original;"
70
+ ],
71
+ "replace": [
72
+ "\t\tif (original === null || original === undefined) env[name] = path.join(env.HOME ?? \"\", \".local\", \"share\");",
73
+ "\t\telse env[name] = original;"
74
+ ],
75
+ "gate": ["bash", "run.sh", "check-mux-launcher-fence"],
76
+ "timeoutSeconds": 120,
77
+ "signature": "[QK:LAUNCHFENCE-XDG-EXACT-RESTORE]",
78
+ "signatureSource": "scripts/check-mux-launcher-fence.ts"
79
+ },
80
+ {
81
+ "claim": "LAUNCHFENCE-WIRED-LIFECYCLE",
82
+ "title": "the lifecycle smoke disconnects the integrity oracle from its teardown, so the fence exists but nothing runs it before fixture removal",
83
+ "subject": "scripts/smoke-mux-lifecycle-live.ts",
84
+ "find": ["\tconst launcherProblems = verifyClaudeLauncher(launcherSnapshot);"],
85
+ "replace": ["\tconst launcherProblems: string[] = [];"],
86
+ "gate": ["bash", "run.sh", "check-mux-launcher-fence"],
87
+ "timeoutSeconds": 120,
88
+ "signature": "[QK:LAUNCHFENCE-WIRED-LIFECYCLE]",
89
+ "signatureSource": "scripts/check-mux-launcher-fence.ts"
90
+ },
91
+ {
92
+ "claim": "LAUNCHFENCE-LIFECYCLE-CELL-BRANCH",
93
+ "title": "lifecycle's real-HOME cell env collapses to one branch — exact parity for every cell — silently dropping the ACP pi cell's MEASURED canonical XDG requirement",
94
+ "subject": "scripts/smoke-mux-lifecycle-live.ts",
95
+ "find": [
96
+ "\t\t\t\tif (backend === \"pi\") {",
97
+ "\t\t\t\t\tserverEnv.XDG_CONFIG_HOME = REAL_XDG_CONFIG_HOME;",
98
+ "\t\t\t\t\tserverEnv.XDG_DATA_HOME = REAL_XDG_DATA_HOME;",
99
+ "\t\t\t\t\tserverEnv.XDG_STATE_HOME = REAL_XDG_STATE_HOME;",
100
+ "\t\t\t\t\tserverEnv.XDG_CACHE_HOME = REAL_XDG_CACHE_HOME;",
101
+ "\t\t\t\t} else {",
102
+ "\t\t\t\t\trestoreOriginalXdg(serverEnv, ORIGINAL_XDG);",
103
+ "\t\t\t\t}"
104
+ ],
105
+ "replace": ["\t\t\t\trestoreOriginalXdg(serverEnv, ORIGINAL_XDG);"],
106
+ "gate": ["bash", "run.sh", "check-mux-launcher-fence"],
107
+ "timeoutSeconds": 120,
108
+ "signature": "[QK:LAUNCHFENCE-LIFECYCLE-CELL-BRANCH]",
109
+ "signatureSource": "scripts/check-mux-launcher-fence.ts"
110
+ },
111
+ {
112
+ "claim": "LAUNCHFENCE-WIRED-FRESH-CALL",
113
+ "title": "the fresh-call smoke disconnects the integrity oracle from its teardown, so only one of the two affected smokes is actually protected",
114
+ "subject": "scripts/smoke-mux-fresh-call-live.ts",
115
+ "find": ["\t\tconst launcherProblems = verifyClaudeLauncher(launcherSnapshot);"],
116
+ "replace": ["\t\tconst launcherProblems: string[] = [];"],
117
+ "gate": ["bash", "run.sh", "check-mux-launcher-fence"],
118
+ "timeoutSeconds": 120,
119
+ "signature": "[QK:LAUNCHFENCE-WIRED-FRESH-CALL]",
120
+ "signatureSource": "scripts/check-mux-launcher-fence.ts"
121
+ }
122
+ ]
123
+ }