@codyswann/lisa 4.28.4 → 4.29.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 (218) hide show
  1. package/README.md +3 -1
  2. package/all/copy-overwrite/scripts/lisa-gates.mjs +2 -1
  3. package/all/copy-overwrite/scripts/lisa-hooks/sonar-secrets.sh +3 -2
  4. package/all/copy-overwrite/scripts/lisa-postinstall.mjs +6 -2
  5. package/cdk/create-only/vitest.config.local.ts +5 -1
  6. package/cdk/package-lisa/package.lisa.json +14 -7
  7. package/dist/cli/apply.d.ts +20 -0
  8. package/dist/cli/apply.d.ts.map +1 -1
  9. package/dist/cli/apply.js +70 -3
  10. package/dist/cli/apply.js.map +1 -1
  11. package/dist/cli/doctor-apply-freshness.d.ts.map +1 -1
  12. package/dist/cli/doctor-apply-freshness.js +10 -4
  13. package/dist/cli/doctor-apply-freshness.js.map +1 -1
  14. package/dist/cli/lisa-test-run-bootstrap.d.ts +3 -0
  15. package/dist/cli/lisa-test-run-bootstrap.d.ts.map +1 -0
  16. package/dist/cli/lisa-test-run-bootstrap.js +201 -0
  17. package/dist/cli/lisa-test-run-bootstrap.js.map +1 -0
  18. package/dist/cli/lisa-test-run-ipc.d.ts +42 -0
  19. package/dist/cli/lisa-test-run-ipc.d.ts.map +1 -0
  20. package/dist/cli/lisa-test-run-ipc.js +200 -0
  21. package/dist/cli/lisa-test-run-ipc.js.map +1 -0
  22. package/dist/cli/lisa-test-run-process-group.d.ts +30 -0
  23. package/dist/cli/lisa-test-run-process-group.d.ts.map +1 -0
  24. package/dist/cli/lisa-test-run-process-group.js +123 -0
  25. package/dist/cli/lisa-test-run-process-group.js.map +1 -0
  26. package/dist/cli/lisa-test-run-reaper.d.ts +3 -0
  27. package/dist/cli/lisa-test-run-reaper.d.ts.map +1 -0
  28. package/dist/cli/lisa-test-run-reaper.js +195 -0
  29. package/dist/cli/lisa-test-run-reaper.js.map +1 -0
  30. package/dist/cli/lisa-test-run-runtime.d.ts +46 -0
  31. package/dist/cli/lisa-test-run-runtime.d.ts.map +1 -0
  32. package/dist/cli/lisa-test-run-runtime.js +123 -0
  33. package/dist/cli/lisa-test-run-runtime.js.map +1 -0
  34. package/dist/cli/lisa-test-run-supervisor.d.ts +13 -0
  35. package/dist/cli/lisa-test-run-supervisor.d.ts.map +1 -0
  36. package/dist/cli/lisa-test-run-supervisor.js +268 -0
  37. package/dist/cli/lisa-test-run-supervisor.js.map +1 -0
  38. package/dist/cli/lisa-test-run.d.ts +3 -0
  39. package/dist/cli/lisa-test-run.d.ts.map +1 -0
  40. package/dist/cli/lisa-test-run.js +66 -0
  41. package/dist/cli/lisa-test-run.js.map +1 -0
  42. package/dist/cli/shared-options.d.ts +11 -4
  43. package/dist/cli/shared-options.d.ts.map +1 -1
  44. package/dist/cli/shared-options.js +8 -8
  45. package/dist/cli/shared-options.js.map +1 -1
  46. package/dist/configs/vitest/base.d.ts.map +1 -1
  47. package/dist/configs/vitest/base.js +5 -2
  48. package/dist/configs/vitest/base.js.map +1 -1
  49. package/dist/configs/vitest/cdk.d.ts +4 -0
  50. package/dist/configs/vitest/cdk.d.ts.map +1 -1
  51. package/dist/configs/vitest/cdk.js +5 -0
  52. package/dist/configs/vitest/cdk.js.map +1 -1
  53. package/dist/configs/vitest/harper-fabric.d.ts.map +1 -1
  54. package/dist/configs/vitest/harper-fabric.js +1 -0
  55. package/dist/configs/vitest/harper-fabric.js.map +1 -1
  56. package/dist/configs/vitest/nestjs.d.ts.map +1 -1
  57. package/dist/configs/vitest/nestjs.js +1 -0
  58. package/dist/configs/vitest/nestjs.js.map +1 -1
  59. package/dist/configs/vitest/phaser.d.ts.map +1 -1
  60. package/dist/configs/vitest/phaser.js +1 -0
  61. package/dist/configs/vitest/phaser.js.map +1 -1
  62. package/dist/configs/vitest/scratch-authority.d.ts +25 -0
  63. package/dist/configs/vitest/scratch-authority.d.ts.map +1 -0
  64. package/dist/configs/vitest/scratch-authority.js +107 -0
  65. package/dist/configs/vitest/scratch-authority.js.map +1 -0
  66. package/dist/configs/vitest/scratch-bound-cleanup-programs.d.ts +5 -0
  67. package/dist/configs/vitest/scratch-bound-cleanup-programs.d.ts.map +1 -0
  68. package/dist/configs/vitest/scratch-bound-cleanup-programs.js +219 -0
  69. package/dist/configs/vitest/scratch-bound-cleanup-programs.js.map +1 -0
  70. package/dist/configs/vitest/scratch-bound-cleanup.d.ts +28 -0
  71. package/dist/configs/vitest/scratch-bound-cleanup.d.ts.map +1 -0
  72. package/dist/configs/vitest/scratch-bound-cleanup.js +155 -0
  73. package/dist/configs/vitest/scratch-bound-cleanup.js.map +1 -0
  74. package/dist/configs/vitest/scratch-bound-root-cleanup.d.ts +17 -0
  75. package/dist/configs/vitest/scratch-bound-root-cleanup.d.ts.map +1 -0
  76. package/dist/configs/vitest/scratch-bound-root-cleanup.js +149 -0
  77. package/dist/configs/vitest/scratch-bound-root-cleanup.js.map +1 -0
  78. package/dist/configs/vitest/scratch-global-setup.d.ts +17 -9
  79. package/dist/configs/vitest/scratch-global-setup.d.ts.map +1 -1
  80. package/dist/configs/vitest/scratch-global-setup.js +103 -35
  81. package/dist/configs/vitest/scratch-global-setup.js.map +1 -1
  82. package/dist/configs/vitest/scratch-leak-setup.d.ts +2 -0
  83. package/dist/configs/vitest/scratch-leak-setup.d.ts.map +1 -0
  84. package/dist/configs/vitest/scratch-leak-setup.js +108 -0
  85. package/dist/configs/vitest/scratch-leak-setup.js.map +1 -0
  86. package/dist/configs/vitest/scratch-namespace-authority.d.ts +80 -0
  87. package/dist/configs/vitest/scratch-namespace-authority.d.ts.map +1 -0
  88. package/dist/configs/vitest/scratch-namespace-authority.js +291 -0
  89. package/dist/configs/vitest/scratch-namespace-authority.js.map +1 -0
  90. package/dist/configs/vitest/scratch-owner.d.ts +131 -0
  91. package/dist/configs/vitest/scratch-owner.d.ts.map +1 -0
  92. package/dist/configs/vitest/scratch-owner.js +297 -0
  93. package/dist/configs/vitest/scratch-owner.js.map +1 -0
  94. package/dist/configs/vitest/scratch-route-profile.d.ts +36 -0
  95. package/dist/configs/vitest/scratch-route-profile.d.ts.map +1 -0
  96. package/dist/configs/vitest/scratch-route-profile.js +217 -0
  97. package/dist/configs/vitest/scratch-route-profile.js.map +1 -0
  98. package/dist/configs/vitest/scratch-run-root-compatibility.d.ts +33 -0
  99. package/dist/configs/vitest/scratch-run-root-compatibility.d.ts.map +1 -0
  100. package/dist/configs/vitest/scratch-run-root-compatibility.js +137 -0
  101. package/dist/configs/vitest/scratch-run-root-compatibility.js.map +1 -0
  102. package/dist/configs/vitest/scratch-run-root-intent.d.ts +66 -0
  103. package/dist/configs/vitest/scratch-run-root-intent.d.ts.map +1 -0
  104. package/dist/configs/vitest/scratch-run-root-intent.js +183 -0
  105. package/dist/configs/vitest/scratch-run-root-intent.js.map +1 -0
  106. package/dist/configs/vitest/scratch-setup.d.ts.map +1 -1
  107. package/dist/configs/vitest/scratch-setup.js +45 -15
  108. package/dist/configs/vitest/scratch-setup.js.map +1 -1
  109. package/dist/configs/vitest/scratch-supervision-intent.d.ts +8 -0
  110. package/dist/configs/vitest/scratch-supervision-intent.d.ts.map +1 -0
  111. package/dist/configs/vitest/scratch-supervision-intent.js +125 -0
  112. package/dist/configs/vitest/scratch-supervision-intent.js.map +1 -0
  113. package/dist/configs/vitest/scratch-supervision-protocol.d.ts +12 -0
  114. package/dist/configs/vitest/scratch-supervision-protocol.d.ts.map +1 -0
  115. package/dist/configs/vitest/scratch-supervision-protocol.js +230 -0
  116. package/dist/configs/vitest/scratch-supervision-protocol.js.map +1 -0
  117. package/dist/configs/vitest/scratch-supervision.d.ts +64 -0
  118. package/dist/configs/vitest/scratch-supervision.d.ts.map +1 -0
  119. package/dist/configs/vitest/scratch-supervision.js +209 -0
  120. package/dist/configs/vitest/scratch-supervision.js.map +1 -0
  121. package/dist/configs/vitest/scratch-sweep.d.ts +66 -0
  122. package/dist/configs/vitest/scratch-sweep.d.ts.map +1 -0
  123. package/dist/configs/vitest/scratch-sweep.js +82 -0
  124. package/dist/configs/vitest/scratch-sweep.js.map +1 -0
  125. package/dist/configs/vitest/scratch.d.ts +14 -187
  126. package/dist/configs/vitest/scratch.d.ts.map +1 -1
  127. package/dist/configs/vitest/scratch.js +18 -285
  128. package/dist/configs/vitest/scratch.js.map +1 -1
  129. package/dist/configs/vitest/typescript.d.ts.map +1 -1
  130. package/dist/configs/vitest/typescript.js +1 -0
  131. package/dist/configs/vitest/typescript.js.map +1 -1
  132. package/dist/core/apply-mode.d.ts +80 -29
  133. package/dist/core/apply-mode.d.ts.map +1 -1
  134. package/dist/core/apply-mode.js +23 -2
  135. package/dist/core/apply-mode.js.map +1 -1
  136. package/dist/core/apply-receipt.d.ts +5 -1
  137. package/dist/core/apply-receipt.d.ts.map +1 -1
  138. package/dist/core/apply-receipt.js.map +1 -1
  139. package/dist/core/config.d.ts +24 -12
  140. package/dist/core/config.d.ts.map +1 -1
  141. package/dist/core/config.js.map +1 -1
  142. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  143. package/dist/core/lisa-owned-hash-ledger.js +8 -0
  144. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  145. package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
  146. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  147. package/dist/core/upstream-evidence-manifest.js +63 -15
  148. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  149. package/dist/migrations/ensure-lisa-postinstall.d.ts +1 -1
  150. package/dist/migrations/ensure-lisa-postinstall.d.ts.map +1 -1
  151. package/dist/migrations/ensure-lisa-postinstall.js +28 -5
  152. package/dist/migrations/ensure-lisa-postinstall.js.map +1 -1
  153. package/dist/utils/postinstall-trampoline-source.js +10 -1
  154. package/dist/utils/postinstall-trampoline-source.js.map +1 -1
  155. package/expo/package-lisa/package.lisa.json +14 -14
  156. package/harper-fabric/package-lisa/package.lisa.json +8 -3
  157. package/nestjs/create-only/vitest.config.local.ts +5 -1
  158. package/nestjs/package-lisa/package.lisa.json +14 -8
  159. package/npm-package/package-lisa/package.lisa.json +8 -0
  160. package/package.json +17 -13
  161. package/phaser/package-lisa/package.lisa.json +9 -3
  162. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  163. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  164. package/plugins/lisa/hooks/sonar-secrets.sh +3 -2
  165. package/plugins/lisa-agy/plugin.json +1 -1
  166. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  167. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  168. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  169. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  170. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  171. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  172. package/plugins/lisa-copilot/hooks/sonar-secrets.sh +3 -2
  173. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  174. package/plugins/lisa-cursor/hooks/sonar-secrets.sh +3 -2
  175. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  176. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  177. package/plugins/lisa-expo-agy/plugin.json +1 -1
  178. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  179. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  180. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  181. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  182. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  183. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  184. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  185. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  186. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  187. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  188. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  189. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  190. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  191. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  192. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  193. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  194. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  195. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  196. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  197. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  198. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  199. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  200. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  201. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  202. package/plugins/lisa-rails-agy/plugin.json +1 -1
  203. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  204. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  205. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  206. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  207. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  208. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  209. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  210. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  211. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  212. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  213. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  214. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  215. package/plugins/src/base/hooks/sonar-secrets.sh +3 -2
  216. package/scripts/install-claude-plugins.sh +7 -2
  217. package/typescript/create-only/vitest.config.local.ts +5 -1
  218. package/typescript/package-lisa/package.lisa.json +14 -8
@@ -0,0 +1,155 @@
1
+ /** Kernel-bound deletion programs for scratch roots and their direct children. */
2
+ import { spawnSync } from "node:child_process";
3
+ import * as fs from "node:fs";
4
+ import * as path from "node:path";
5
+ import { scratchPathIdentity, } from "./scratch-owner.js";
6
+ import { BOUND_CHILDREN_CLEANUP_PROGRAM } from "./scratch-bound-cleanup-programs.js";
7
+ export { BOUND_DIRECTORY_CLEANUP_PROGRAM } from "./scratch-bound-cleanup-programs.js";
8
+ /** Exit status used when the child cwd does not match the expected inode. */
9
+ const BOUND_CLEANUP_IDENTITY_EXIT = 73;
10
+ /**
11
+ * Demand one inert direct basename, never a path.
12
+ * @param basename - Candidate direct-child name
13
+ */
14
+ function validateBasename(basename) {
15
+ if (basename === "" ||
16
+ basename === "." ||
17
+ basename === ".." ||
18
+ path.basename(basename) !== basename ||
19
+ basename.includes("/") ||
20
+ basename.includes("\\")) {
21
+ throw new Error(`Scratch candidate must be a direct basename: ${basename}`);
22
+ }
23
+ if (Buffer.byteLength(basename, "utf8") > 1_024) {
24
+ throw new Error("Scratch candidate basename exceeds 1024 bytes");
25
+ }
26
+ }
27
+ /**
28
+ * Read one candidate without converting absence into an exception.
29
+ * @param candidate - Candidate path
30
+ * @returns Candidate metadata, or undefined when already absent
31
+ */
32
+ function lstatIfPresent(candidate) {
33
+ try {
34
+ return fs.lstatSync(candidate);
35
+ }
36
+ catch (error) {
37
+ if (error.code === "ENOENT")
38
+ return undefined;
39
+ throw error;
40
+ }
41
+ }
42
+ /**
43
+ * Assert that an already-owned parent still has its pinned identity.
44
+ * @param expected - Pinned parent identity
45
+ */
46
+ function assertScratchPathIdentity(expected) {
47
+ const current = scratchPathIdentity(expected.canonicalPath);
48
+ if (current.canonicalPath !== expected.canonicalPath ||
49
+ current.dev !== expected.dev ||
50
+ current.ino !== expected.ino) {
51
+ throw new Error("Scratch parent identity changed before child cleanup");
52
+ }
53
+ }
54
+ /**
55
+ * Inspect a direct child after its pinned parent passes revalidation.
56
+ * @param parent - Pinned parent identity
57
+ * @param basename - Direct-child name
58
+ * @param child - Candidate path
59
+ * @returns Candidate metadata, or undefined when already absent
60
+ */
61
+ function inspectAuthorizedScratchChild(parent, basename, child) {
62
+ validateBasename(basename);
63
+ assertScratchPathIdentity(parent);
64
+ return lstatIfPresent(child);
65
+ }
66
+ /**
67
+ * Capture every present candidate while the parent remains pinned.
68
+ * @param options - Batched child-removal options
69
+ * @returns Bound cleanup descriptions for present children
70
+ */
71
+ function collectAuthorizedChildren(options) {
72
+ assertScratchPathIdentity(options.parent);
73
+ return options.basenames.flatMap(basename => {
74
+ const child = path.join(options.parent.canonicalPath, basename);
75
+ options.beforeIdentityCheck?.(child);
76
+ const stat = inspectAuthorizedScratchChild(options.parent, basename, child);
77
+ if (stat === undefined)
78
+ return [];
79
+ options.afterIdentityCheck?.(child);
80
+ return [
81
+ {
82
+ basename,
83
+ dev: String(stat.dev),
84
+ ino: String(stat.ino),
85
+ directory: stat.isDirectory(),
86
+ symlink: stat.isSymbolicLink(),
87
+ },
88
+ ];
89
+ });
90
+ }
91
+ /**
92
+ * Execute the inode-bound child-cleanup worker.
93
+ * @param options - Batched child-removal options
94
+ * @param input - Validated serialized child descriptions
95
+ * @returns Worker result
96
+ */
97
+ function executeBoundChildrenCleanup(options, input) {
98
+ options.beforeBoundCleanup?.();
99
+ return spawnSync(process.execPath, [
100
+ "--input-type=commonjs",
101
+ "--eval",
102
+ BOUND_CHILDREN_CLEANUP_PROGRAM,
103
+ String(options.parent.dev),
104
+ String(options.parent.ino),
105
+ ], {
106
+ cwd: options.parent.canonicalPath,
107
+ encoding: "utf8",
108
+ input,
109
+ maxBuffer: 64 * 1024,
110
+ });
111
+ }
112
+ /**
113
+ * Remove direct children through one process bound to the parent inode.
114
+ * @param options - Batched child-removal options
115
+ */
116
+ export function removeAuthorizedScratchChildren(options) {
117
+ if (new Set(options.basenames).size !== options.basenames.length) {
118
+ throw new Error("Scratch child cleanup refuses duplicate basenames");
119
+ }
120
+ const items = collectAuthorizedChildren(options);
121
+ if (items.length === 0)
122
+ return;
123
+ const input = JSON.stringify(items);
124
+ if (Buffer.byteLength(input, "utf8") > 32 * 1024 * 1024) {
125
+ throw new Error("Scratch child cleanup batch exceeds 33554432 bytes");
126
+ }
127
+ const result = executeBoundChildrenCleanup(options, input);
128
+ if (result.error !== undefined)
129
+ throw result.error;
130
+ if (result.status === BOUND_CLEANUP_IDENTITY_EXIT) {
131
+ throw new Error(result.stderr.trim() ||
132
+ "Scratch child identity changed before bound cleanup");
133
+ }
134
+ if (result.status !== 0 || result.signal !== null) {
135
+ throw new Error(`Scratch bound cleanup failed: ${result.stderr.trim() || result.signal || String(result.status)}`);
136
+ }
137
+ assertScratchPathIdentity(options.parent);
138
+ }
139
+ /**
140
+ * Remove one direct child through the batched authority.
141
+ * @param options - Single child-removal options
142
+ */
143
+ export function removeAuthorizedScratchChild(options) {
144
+ removeAuthorizedScratchChildren({
145
+ parent: options.parent,
146
+ basenames: [options.basename],
147
+ ...(options.beforeIdentityCheck === undefined
148
+ ? {}
149
+ : { beforeIdentityCheck: options.beforeIdentityCheck }),
150
+ ...(options.afterIdentityCheck === undefined
151
+ ? {}
152
+ : { afterIdentityCheck: options.afterIdentityCheck }),
153
+ });
154
+ }
155
+ //# sourceMappingURL=scratch-bound-cleanup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scratch-bound-cleanup.js","sourceRoot":"","sources":["../../../src/configs/vitest/scratch-bound-cleanup.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import { type ScratchNamespaceAuthority } from "./scratch-namespace-authority.js";
2
+ import { type ScratchPathIdentity } from "./scratch-owner.js";
3
+ /** Options for an authorised root removal. */
4
+ export interface RemoveAuthorizedScratchRootOptions {
5
+ readonly authority: ScratchNamespaceAuthority;
6
+ readonly basename: string;
7
+ readonly expectedToken?: string;
8
+ readonly expectedIdentity?: ScratchPathIdentity;
9
+ readonly afterIdentityCheck?: (candidate: string) => void;
10
+ }
11
+ /**
12
+ * Quarantine and delete one direct owned root.
13
+ * @param options - Authorized root-removal options
14
+ * @returns Whether an existing root was removed
15
+ */
16
+ export declare function removeAuthorizedScratchRoot(options: RemoveAuthorizedScratchRootOptions): boolean;
17
+ //# sourceMappingURL=scratch-bound-root-cleanup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scratch-bound-root-cleanup.d.ts","sourceRoot":"","sources":["../../../src/configs/vitest/scratch-bound-root-cleanup.ts"],"names":[],"mappings":"AAMA,OAAO,EAGL,KAAK,yBAAyB,EAC/B,MAAM,kCAAkC,CAAC;AAE1C,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,oBAAoB,CAAC;AAE5B,8CAA8C;AAC9C,MAAM,WAAW,kCAAkC;IACjD,QAAQ,CAAC,SAAS,EAAE,yBAAyB,CAAC;IAC9C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,mBAAmB,CAAC;IAChD,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3D;AAsJD;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,kCAAkC,GAC1C,OAAO,CA8BT"}
@@ -0,0 +1,149 @@
1
+ /** Kernel-bound removal of one token-authorized scratch run root. */
2
+ import { randomBytes } from "node:crypto";
3
+ import { spawnSync } from "node:child_process";
4
+ import * as fs from "node:fs";
5
+ import * as path from "node:path";
6
+ import { SCRATCH_QUARANTINE_PREFIX, assertScratchNamespaceAuthority, } from "./scratch-namespace-authority.js";
7
+ import { BOUND_DIRECTORY_CLEANUP_PROGRAM } from "./scratch-bound-cleanup-programs.js";
8
+ import { readScratchOwnerRecord, } from "./scratch-owner.js";
9
+ /** Exit status used when a child cwd does not match its expected inode. */
10
+ const BOUND_CLEANUP_IDENTITY_EXIT = 73;
11
+ /**
12
+ * Demand one inert direct basename, never a path.
13
+ * @param basename - Candidate direct-child name
14
+ */
15
+ function validateBasename(basename) {
16
+ if (basename === "" ||
17
+ basename === "." ||
18
+ basename === ".." ||
19
+ path.basename(basename) !== basename ||
20
+ basename.includes("/") ||
21
+ basename.includes("\\")) {
22
+ throw new Error(`Scratch candidate must be a direct basename: ${basename}`);
23
+ }
24
+ }
25
+ /**
26
+ * Read one candidate without converting absence into an exception.
27
+ * @param candidate - Candidate path
28
+ * @returns Candidate metadata, or undefined when already absent
29
+ */
30
+ function lstatIfPresent(candidate) {
31
+ try {
32
+ return fs.lstatSync(candidate);
33
+ }
34
+ catch (error) {
35
+ if (error.code === "ENOENT")
36
+ return undefined;
37
+ throw error;
38
+ }
39
+ }
40
+ /**
41
+ * Clear a directory through a cwd bound to the inspected inode.
42
+ * @param candidate - Directory path
43
+ * @param expected - Pinned directory identity
44
+ */
45
+ function clearDirectoryThroughBoundCwd(candidate, expected) {
46
+ const result = spawnSync(process.execPath, [
47
+ "--input-type=commonjs",
48
+ "--eval",
49
+ BOUND_DIRECTORY_CLEANUP_PROGRAM,
50
+ String(expected.dev),
51
+ String(expected.ino),
52
+ ], { cwd: candidate, encoding: "utf8", maxBuffer: 64 * 1024 });
53
+ if (result.error !== undefined)
54
+ throw result.error;
55
+ if (result.status === BOUND_CLEANUP_IDENTITY_EXIT) {
56
+ throw new Error("Scratch directory identity changed before bound cleanup");
57
+ }
58
+ if (result.status !== 0 || result.signal !== null) {
59
+ throw new Error(`Scratch bound cleanup failed: ${result.stderr.trim() || result.signal || String(result.status)}`);
60
+ }
61
+ }
62
+ /**
63
+ * Bind an optional armed inode identity to the inspected candidate.
64
+ * @param options - Authorized root-removal options
65
+ * @param stat - Candidate metadata when present
66
+ * @returns Validated metadata, or undefined when already absent
67
+ */
68
+ function validateExpectedRootIdentity(options, stat) {
69
+ if (stat !== undefined &&
70
+ options.expectedIdentity !== undefined &&
71
+ (stat.dev !== options.expectedIdentity.dev ||
72
+ stat.ino !== options.expectedIdentity.ino)) {
73
+ throw new Error("Scratch root identity does not match armed intent");
74
+ }
75
+ return stat;
76
+ }
77
+ /**
78
+ * Validate authority before reading a destructive candidate.
79
+ * @param options - Authorized root-removal options
80
+ * @param candidate - Candidate path
81
+ * @returns Candidate metadata, or undefined when already absent
82
+ */
83
+ function inspectAuthorizedCandidate(options, candidate) {
84
+ validateBasename(options.basename);
85
+ assertScratchNamespaceAuthority(options.authority);
86
+ return validateExpectedRootIdentity(options, lstatIfPresent(candidate));
87
+ }
88
+ /**
89
+ * Revalidate an expected marker token at a specific path.
90
+ * @param candidate - Candidate path
91
+ * @param expectedToken - Token authorized by the caller
92
+ * @param phase - Whether validation occurs before or after quarantine
93
+ */
94
+ function assertOwnerToken(candidate, expectedToken, phase) {
95
+ if (expectedToken === undefined)
96
+ return;
97
+ const owner = readScratchOwnerRecord(candidate);
98
+ if (owner.token !== expectedToken) {
99
+ throw new Error(`Scratch owner token changed ${phase} quarantine`);
100
+ }
101
+ }
102
+ /**
103
+ * Revalidate and unlink the renamed candidate.
104
+ * @param quarantine - Quarantined path
105
+ * @param before - Pinned pre-rename metadata
106
+ * @param expectedToken - Token authorized by the caller
107
+ * @param afterIdentityCheck - Optional adversarial test hook
108
+ * @returns True after successful removal
109
+ */
110
+ function removeVerifiedQuarantine(quarantine, before, expectedToken, afterIdentityCheck) {
111
+ const after = fs.lstatSync(quarantine);
112
+ if (after.isSymbolicLink() ||
113
+ after.dev !== before.dev ||
114
+ after.ino !== before.ino) {
115
+ throw new Error("Scratch root identity changed during quarantine");
116
+ }
117
+ afterIdentityCheck?.(quarantine);
118
+ assertOwnerToken(quarantine, expectedToken, "after");
119
+ clearDirectoryThroughBoundCwd(quarantine, after);
120
+ fs.rmdirSync(quarantine);
121
+ return true;
122
+ }
123
+ /**
124
+ * Quarantine and delete one direct owned root.
125
+ * @param options - Authorized root-removal options
126
+ * @returns Whether an existing root was removed
127
+ */
128
+ export function removeAuthorizedScratchRoot(options) {
129
+ const candidate = path.join(options.authority.namespace.canonicalPath, options.basename);
130
+ const quarantineName = `${SCRATCH_QUARANTINE_PREFIX}${randomBytes(16).toString("hex")}`;
131
+ const quarantine = path.join(options.authority.namespace.canonicalPath, quarantineName);
132
+ const before = inspectAuthorizedCandidate(options, candidate);
133
+ if (before === undefined)
134
+ return false;
135
+ if (before.isSymbolicLink() || !before.isDirectory()) {
136
+ throw new Error(`Scratch root authority refuses non-directory or symlink: ${candidate}`);
137
+ }
138
+ assertOwnerToken(candidate, options.expectedToken, "before");
139
+ try {
140
+ fs.renameSync(candidate, quarantine);
141
+ }
142
+ catch (error) {
143
+ if (error.code === "ENOENT")
144
+ return false;
145
+ throw error;
146
+ }
147
+ return removeVerifiedQuarantine(quarantine, before, options.expectedToken, options.afterIdentityCheck);
148
+ }
149
+ //# sourceMappingURL=scratch-bound-root-cleanup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scratch-bound-root-cleanup.js","sourceRoot":"","sources":["../../../src/configs/vitest/scratch-bound-root-cleanup.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { processBirthFingerprintSnapshot } from "./scratch-owner.js";
1
2
  /**
2
3
  * Upper bound on entries the namespace may hold that **nobody owns**.
3
4
  *
@@ -35,7 +36,7 @@ export declare const MAX_NAMESPACE_ENTRIES = 512;
35
36
  export interface NamespaceResidue {
36
37
  /** Entries whose owning process is gone but which were not removed */
37
38
  readonly orphaned: readonly string[];
38
- /** Entries that do not follow the run-root naming scheme at all */
39
+ /** Entries whose exact dead ownership cannot be established */
39
40
  readonly unrecognised: readonly string[];
40
41
  /** Total entries present */
41
42
  readonly total: number;
@@ -50,26 +51,33 @@ export interface NamespaceResidue {
50
51
  * that regression into a failure instead of a slow leak.
51
52
  * @param dir - Namespace directory to inspect
52
53
  * @param alive - Liveness probe, overridable for tests
54
+ * @param birth - Process-birth probe, overridable for tests
53
55
  * @returns The classified residue.
54
56
  */
55
- export declare const inspectNamespace: (dir?: string, alive?: (pid: number) => boolean) => NamespaceResidue;
57
+ export declare const inspectNamespace: (dir?: string, alive?: (pid: number) => boolean, birth?: (pid: number) => string | undefined) => NamespaceResidue;
56
58
  /**
57
59
  * Builds the failure message for a namespace that is accumulating.
58
60
  *
59
61
  * Every branch names the directory and the offending entries, because the
60
62
  * failure this replaces was a 60-second timeout that named nothing.
61
63
  *
62
- * `unrecognised` is deliberately NOT a failure. It was, and it wedged: one
63
- * foreign directory in the namespace failed every subsequent run, forever, with
64
- * a message a downstream user could not act on. Those entries are reclaimed on
65
- * age by the sweep instead. What remains here are the two conditions that mean
66
- * this fix itself has stopped working — residue the sweep should have taken and
67
- * did not, and a namespace growing without bound.
64
+ * `unrecognised` is deliberately a fail-closed refusal. A foreign, malformed,
65
+ * or identity-mismatched entry is preserved because age and naming are not
66
+ * deletion authority; proceeding would silently permit unbounded residue, and
67
+ * deleting it could destroy a live sibling's work. The diagnostic therefore
68
+ * names the bounded entries and gives the only safe manual recovery rule.
68
69
  * @param dir - Namespace directory inspected
69
70
  * @param residue - What the inspection found
70
71
  * @returns The message, or `undefined` when the namespace is healthy.
71
72
  */
72
73
  export declare const describeResidueFailure: (dir: string, residue: NamespaceResidue) => string | undefined;
74
+ /**
75
+ * Sweeps the namespace and reports what survived.
76
+ * @param alive - Process liveness probe shared by both phases
77
+ * @param snapshot - One bulk process-birth snapshot provider
78
+ * @returns The residue remaining after the sweep.
79
+ */
80
+ export declare const sweepThenInspect: (alive?: (pid: number) => boolean, snapshot?: typeof processBirthFingerprintSnapshot) => NamespaceResidue;
73
81
  /**
74
82
  * Renders the banner printed at the moment of refusal.
75
83
  *
@@ -193,7 +201,7 @@ export declare const announceRefusal: (failure: string, writeNotice?: (text: str
193
201
  * A refusal speaks twice — a banner before collection and a summary line at
194
202
  * exit — and the two are not redundant. They are the top and the bottom of a
195
203
  * transcript nobody reads in full.
196
- * @throws When residue is present that the sweep cannot or did not reclaim.
204
+ * @throws {Error} When residue is present that the sweep cannot or did not reclaim.
197
205
  */
198
206
  export declare const setup: () => void;
199
207
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"scratch-global-setup.d.ts","sourceRoot":"","sources":["../../../src/configs/vitest/scratch-global-setup.ts"],"names":[],"mappings":"AAkDA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAEzC,iDAAiD;AACjD,MAAM,WAAW,gBAAgB;IAC/B,sEAAsE;IACtE,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,mEAAmE;IACnE,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,4BAA4B;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,gBAAgB,GAC3B,MAAK,MAA8B,EACnC,QAAO,CAAC,GAAG,EAAE,MAAM,KAAK,OAAwB,KAC/C,gBAWF,CAAC;AAUF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,sBAAsB,GACjC,KAAK,MAAM,EACX,SAAS,gBAAgB,KACxB,MAAM,GAAG,SA2BX,CAAC;AA2BF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,eAAO,MAAM,mBAAmB,GAAI,SAAS,MAAM,KAAG,MAcxC,CAAC;AAKf;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,oBAAoB,GAAI,SAAS,MAAM,KAAG,MAG/B,CAAC;AAEzB;;;;;GAKG;AACH,eAAO,MAAM,eAAe,mBAAmB,CAAC;AAQhD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,iBAAiB,GAC5B,SAAS,MAAM,EACf,QAAO,CAAC,IAAI,EAAE,MAAM,KAAK,IAExB,KACA,IAIF,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,eAAe,GAC1B,SAAS,MAAM,EACf,cAAa,CAAC,IAAI,EAAE,MAAM,KAAK,IAE9B,KACA,IAMF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,KAAK,QAAO,IAexB,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,QAAQ,QAAO,IAE3B,CAAC"}
1
+ {"version":3,"file":"scratch-global-setup.d.ts","sourceRoot":"","sources":["../../../src/configs/vitest/scratch-global-setup.ts"],"names":[],"mappings":"AAmDA,OAAO,EAGL,+BAA+B,EAGhC,MAAM,oBAAoB,CAAC;AAE5B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAEzC,iDAAiD;AACjD,MAAM,WAAW,gBAAgB;IAC/B,sEAAsE;IACtE,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,+DAA+D;IAC/D,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,4BAA4B;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AA4DD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,gBAAgB,GAC3B,MAAK,MAA8B,EACnC,QAAO,CAAC,GAAG,EAAE,MAAM,KAAK,OAAwB,EAChD,QAAO,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,SAAmC,KACnE,gBAgBF,CAAC;AAeF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,sBAAsB,GACjC,KAAK,MAAM,EACX,SAAS,gBAAgB,KACxB,MAAM,GAAG,SAsCX,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAC3B,QAAO,CAAC,GAAG,EAAE,MAAM,KAAK,OAAwB,EAChD,WAAU,OAAO,+BAAiE,KACjF,gBAQF,CAAC;AAgBF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,eAAO,MAAM,mBAAmB,GAAI,SAAS,MAAM,KAAG,MAcxC,CAAC;AAKf;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,oBAAoB,GAAI,SAAS,MAAM,KAAG,MAG/B,CAAC;AAEzB;;;;;GAKG;AACH,eAAO,MAAM,eAAe,mBAAmB,CAAC;AAQhD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,iBAAiB,GAC5B,SAAS,MAAM,EACf,QAAO,CAAC,IAAI,EAAE,MAAM,KAAK,IAExB,KACA,IAIF,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,eAAe,GAC1B,SAAS,MAAM,EACf,cAAa,CAAC,IAAI,EAAE,MAAM,KAAK,IAE9B,KACA,IAMF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,KAAK,QAAO,IAexB,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,QAAQ,QAAO,IAE3B,CAAC"}
@@ -3,10 +3,11 @@
3
3
  *
4
4
  * Runs once in the main Vitest process, on either side of the whole run.
5
5
  *
6
- * `setup` reclaims residue left by runs that were killed and then REFUSES TO
7
- * START a run whose namespace is accumulating — the accumulation being the
8
- * entire defect this exists to prevent, arriving one directory at a time.
9
- * `teardown` reclaims what this run allocated.
6
+ * `setup` reclaims residue only when a valid owner marker proves the exact root
7
+ * belongs to a dead process. It REFUSES TO START when a root cannot be removed
8
+ * with that authority or when proven-dead residue survives the sweep. That
9
+ * fail-closed distinction prevents a cleanup guard from deleting a live
10
+ * sibling's work. `teardown` reclaims what this run allocated.
10
11
  *
11
12
  * The check is deliberately a hard failure. A previous version of this problem
12
13
  * was invisible for a working day because every instrument that met the
@@ -29,16 +30,18 @@
29
30
  * a hook the runner ignores is the exact defect this file was written to stop,
30
31
  * reproduced inside the guard itself.
31
32
  *
32
- * Checking at the start rather than the end costs nothing that matters: a run's
33
- * own residue is reclaimed by the next run's sweep, and the next run's check
34
- * sees whatever that sweep could not take. Accumulation is a trend, and a trend
35
- * is still visible one run later.
33
+ * Checking at the start rather than the end costs nothing that matters: the
34
+ * next run reclaims roots whose exact dead ownership it can prove, preserves
35
+ * uncertain paths, and refuses before collection if anything unsafe remains.
36
+ * Accumulation is a trend, and a trend is still visible one run later.
36
37
  * @see {@link module:configs/vitest/scratch} for the reclaim rules
37
38
  * @module configs/vitest/scratch-global-setup
38
39
  */
39
- import { writeSync } from "node:fs";
40
+ import { lstatSync, realpathSync, writeSync } from "node:fs";
41
+ import * as path from "node:path";
40
42
  import { env } from "node:process";
41
- import { isProcessAlive, parseRunRootName, readNamespaceEntries, scratchNamespaceDir, sweepScratchNamespace, } from "./scratch.js";
43
+ import { isProcessAlive, liveOwnerBirthProbe, readNamespaceEntries, scratchNamespaceDir, sweepScratchNamespace, } from "./scratch.js";
44
+ import { classifyScratchOwner, processBirthFingerprint, processBirthFingerprintSnapshot, readScratchOwnerRecord, } from "./scratch-owner.js";
42
45
  /**
43
46
  * Upper bound on entries the namespace may hold that **nobody owns**.
44
47
  *
@@ -72,6 +75,50 @@ import { isProcessAlive, parseRunRootName, readNamespaceEntries, scratchNamespac
72
75
  * against measured harm and belongs in its own ticket.
73
76
  */
74
77
  export const MAX_NAMESPACE_ENTRIES = 512;
78
+ /**
79
+ * Verify that a durable marker names the physical root and namespace inspected.
80
+ * @param dir - Canonical namespace path
81
+ * @param root - Direct child root
82
+ * @param owner - Valid owner marker
83
+ * @returns True when device, inode, and canonical identities all match
84
+ */
85
+ function markerMatchesInspectedPath(dir, root, owner) {
86
+ const namespaceStat = lstatSync(dir);
87
+ const rootStat = lstatSync(root);
88
+ return (!rootStat.isSymbolicLink() &&
89
+ rootStat.isDirectory() &&
90
+ owner.namespace.canonicalPath === realpathSync(dir) &&
91
+ owner.namespace.dev === namespaceStat.dev &&
92
+ owner.namespace.ino === namespaceStat.ino &&
93
+ owner.root.canonicalPath === realpathSync(root) &&
94
+ owner.root.dev === rootStat.dev &&
95
+ owner.root.ino === rootStat.ino);
96
+ }
97
+ /**
98
+ * Classify one direct namespace child without treating age as authority.
99
+ * @param dir - Canonical namespace path
100
+ * @param name - Direct child basename
101
+ * @param alive - Pid liveness probe
102
+ * @param birth - Process-birth probe
103
+ * @returns Fail-closed entry disposition
104
+ */
105
+ function classifyNamespaceEntry(dir, name, alive, birth) {
106
+ const root = path.join(dir, name);
107
+ try {
108
+ const owner = readScratchOwnerRecord(root);
109
+ if (!markerMatchesInspectedPath(dir, root, owner))
110
+ return "unrecognised";
111
+ return classifyScratchOwner(owner, {
112
+ isProcessAlive: alive,
113
+ processBirthFingerprint: birth,
114
+ }) === "preserve"
115
+ ? "live"
116
+ : "orphaned";
117
+ }
118
+ catch {
119
+ return "unrecognised";
120
+ }
121
+ }
75
122
  /**
76
123
  * Classifies what is left in the namespace.
77
124
  *
@@ -82,16 +129,22 @@ export const MAX_NAMESPACE_ENTRIES = 512;
82
129
  * that regression into a failure instead of a slow leak.
83
130
  * @param dir - Namespace directory to inspect
84
131
  * @param alive - Liveness probe, overridable for tests
132
+ * @param birth - Process-birth probe, overridable for tests
85
133
  * @returns The classified residue.
86
134
  */
87
- export const inspectNamespace = (dir = scratchNamespaceDir(), alive = isProcessAlive) => {
135
+ export const inspectNamespace = (dir = scratchNamespaceDir(), alive = isProcessAlive, birth = processBirthFingerprint) => {
88
136
  const entries = readNamespaceEntries(dir);
137
+ const classified = entries.map(name => ({
138
+ name,
139
+ disposition: classifyNamespaceEntry(dir, name, alive, birth),
140
+ }));
89
141
  return {
90
- orphaned: entries.filter(name => {
91
- const owner = parseRunRootName(name);
92
- return owner !== undefined && !alive(owner.pid);
93
- }),
94
- unrecognised: entries.filter(name => parseRunRootName(name) === undefined),
142
+ orphaned: classified
143
+ .filter(entry => entry.disposition === "orphaned")
144
+ .map(entry => entry.name),
145
+ unrecognised: classified
146
+ .filter(entry => entry.disposition === "unrecognised")
147
+ .map(entry => entry.name),
95
148
  total: entries.length,
96
149
  };
97
150
  };
@@ -101,18 +154,20 @@ export const inspectNamespace = (dir = scratchNamespaceDir(), alive = isProcessA
101
154
  * @returns A comma-separated sample, elided beyond five.
102
155
  */
103
156
  const sample = (names) => names.slice(0, 5).join(", ") + (names.length > 5 ? ", …" : "");
157
+ /** Safe operator recovery when automatic deletion authority is unavailable. */
158
+ const MANUAL_RECOVERY_GUIDANCE = "Remove only an exact entry whose dead owner you can independently verify. " +
159
+ "Do not clear the shared namespace or remove a live sibling's root.";
104
160
  /**
105
161
  * Builds the failure message for a namespace that is accumulating.
106
162
  *
107
163
  * Every branch names the directory and the offending entries, because the
108
164
  * failure this replaces was a 60-second timeout that named nothing.
109
165
  *
110
- * `unrecognised` is deliberately NOT a failure. It was, and it wedged: one
111
- * foreign directory in the namespace failed every subsequent run, forever, with
112
- * a message a downstream user could not act on. Those entries are reclaimed on
113
- * age by the sweep instead. What remains here are the two conditions that mean
114
- * this fix itself has stopped working — residue the sweep should have taken and
115
- * did not, and a namespace growing without bound.
166
+ * `unrecognised` is deliberately a fail-closed refusal. A foreign, malformed,
167
+ * or identity-mismatched entry is preserved because age and naming are not
168
+ * deletion authority; proceeding would silently permit unbounded residue, and
169
+ * deleting it could destroy a live sibling's work. The diagnostic therefore
170
+ * names the bounded entries and gives the only safe manual recovery rule.
116
171
  * @param dir - Namespace directory inspected
117
172
  * @param residue - What the inspection found
118
173
  * @returns The message, or `undefined` when the namespace is healthy.
@@ -122,7 +177,15 @@ export const describeResidueFailure = (dir, residue) => {
122
177
  return (`Test scratch namespace ${dir} still holds ${String(residue.orphaned.length)} ` +
123
178
  `root(s) whose owning process is gone, after a sweep that should have ` +
124
179
  `removed them: ${sample(residue.orphaned)}. Reclaim-on-start is not ` +
125
- `working, so a killed run's residue is now permanent.`);
180
+ `working, so a killed run's residue is now permanent. ${MANUAL_RECOVERY_GUIDANCE}`);
181
+ }
182
+ if (residue.unrecognised.length > 0) {
183
+ return (`Test scratch namespace ${dir} holds ${String(residue.unrecognised.length)} ` +
184
+ `root(s) without valid owner-marker authority: ${sample(residue.unrecognised)}. ` +
185
+ `Lisa preserved the uncertain residue instead of deleting a path that ` +
186
+ `cannot be bound to a token and process-birth fingerprint. ` +
187
+ `There are ${String(residue.total)} entries in total; the historical ` +
188
+ `accumulating-residue ceiling is ${String(MAX_NAMESPACE_ENTRIES)}. ${MANUAL_RECOVERY_GUIDANCE}`);
126
189
  }
127
190
  // Entries no live process owns. Written as the general expression rather
128
191
  // than as `unrecognised` alone: the orphaned branch above returns first
@@ -134,18 +197,24 @@ export const describeResidueFailure = (dir, residue) => {
134
197
  `live process owns, past the ceiling of ${String(MAX_NAMESPACE_ENTRIES)} ` +
135
198
  `(${String(residue.total)} entries in total, the rest being work in ` +
136
199
  `flight). Scratch space is accumulating rather than being reclaimed — ` +
137
- `the condition this guard exists to prevent.`);
200
+ `the condition this guard exists to prevent. ${MANUAL_RECOVERY_GUIDANCE}`);
138
201
  }
139
202
  return undefined;
140
203
  };
141
204
  /**
142
205
  * Sweeps the namespace and reports what survived.
143
- * @param dir - Namespace directory
206
+ * @param alive - Process liveness probe shared by both phases
207
+ * @param snapshot - One bulk process-birth snapshot provider
144
208
  * @returns The residue remaining after the sweep.
145
209
  */
146
- const sweepThenInspect = (dir) => {
147
- sweepScratchNamespace({ dir });
148
- return inspectNamespace(dir);
210
+ export const sweepThenInspect = (alive = isProcessAlive, snapshot = processBirthFingerprintSnapshot) => {
211
+ const dir = scratchNamespaceDir();
212
+ const birth = liveOwnerBirthProbe(dir, alive, snapshot);
213
+ sweepScratchNamespace({
214
+ isProcessAlive: alive,
215
+ processBirthFingerprint: birth,
216
+ });
217
+ return inspectNamespace(dir, alive, birth);
149
218
  };
150
219
  /**
151
220
  * Audits the namespace, tolerating a sibling run's worker exiting mid-audit.
@@ -154,12 +223,11 @@ const sweepThenInspect = (dir) => {
154
223
  * inspection, through no fault of this run. Sweeping once more before treating
155
224
  * an orphan as a defect keeps a guard built to catch a permanent leak from
156
225
  * failing on a transient one.
157
- * @param dir - Namespace directory
158
226
  * @returns The residue to judge.
159
227
  */
160
- const auditNamespace = (dir) => {
161
- const first = sweepThenInspect(dir);
162
- return first.orphaned.length > 0 ? sweepThenInspect(dir) : first;
228
+ const auditNamespace = () => {
229
+ const first = sweepThenInspect();
230
+ return first.orphaned.length > 0 ? sweepThenInspect() : first;
163
231
  };
164
232
  /**
165
233
  * Renders the banner printed at the moment of refusal.
@@ -321,7 +389,7 @@ export const announceRefusal = (failure, writeNotice = text => {
321
389
  * A refusal speaks twice — a banner before collection and a summary line at
322
390
  * exit — and the two are not redundant. They are the top and the bottom of a
323
391
  * transcript nobody reads in full.
324
- * @throws When residue is present that the sweep cannot or did not reclaim.
392
+ * @throws {Error} When residue is present that the sweep cannot or did not reclaim.
325
393
  */
326
394
  export const setup = () => {
327
395
  // The namespace directory is not created here. `createRunRoot` makes it
@@ -329,7 +397,7 @@ export const setup = () => {
329
397
  // inspection treat an absent namespace as an empty one — so creating it would
330
398
  // be a side effect ahead of the audit that buys nothing.
331
399
  const dir = scratchNamespaceDir();
332
- const residue = auditNamespace(dir);
400
+ const residue = auditNamespace();
333
401
  const failure = describeResidueFailure(dir, residue);
334
402
  if (failure !== undefined) {
335
403
  // Both halves of the announcement happen before the throw, because the
@@ -354,6 +422,6 @@ export const setup = () => {
354
422
  * approximately true.
355
423
  */
356
424
  export const teardown = () => {
357
- sweepScratchNamespace({ dir: scratchNamespaceDir() });
425
+ sweepScratchNamespace();
358
426
  };
359
427
  //# sourceMappingURL=scratch-global-setup.js.map