@codyswann/lisa 4.28.5 → 4.29.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 (188) hide show
  1. package/all/copy-overwrite/scripts/lisa-gates.mjs +2 -1
  2. package/all/copy-overwrite/scripts/lisa-hooks/sonar-secrets.sh +3 -2
  3. package/cdk/create-only/vitest.config.local.ts +5 -1
  4. package/cdk/package-lisa/package.lisa.json +14 -7
  5. package/dist/cli/lisa-test-run-bootstrap.d.ts +3 -0
  6. package/dist/cli/lisa-test-run-bootstrap.d.ts.map +1 -0
  7. package/dist/cli/lisa-test-run-bootstrap.js +201 -0
  8. package/dist/cli/lisa-test-run-bootstrap.js.map +1 -0
  9. package/dist/cli/lisa-test-run-ipc.d.ts +42 -0
  10. package/dist/cli/lisa-test-run-ipc.d.ts.map +1 -0
  11. package/dist/cli/lisa-test-run-ipc.js +200 -0
  12. package/dist/cli/lisa-test-run-ipc.js.map +1 -0
  13. package/dist/cli/lisa-test-run-process-group.d.ts +30 -0
  14. package/dist/cli/lisa-test-run-process-group.d.ts.map +1 -0
  15. package/dist/cli/lisa-test-run-process-group.js +123 -0
  16. package/dist/cli/lisa-test-run-process-group.js.map +1 -0
  17. package/dist/cli/lisa-test-run-reaper.d.ts +3 -0
  18. package/dist/cli/lisa-test-run-reaper.d.ts.map +1 -0
  19. package/dist/cli/lisa-test-run-reaper.js +195 -0
  20. package/dist/cli/lisa-test-run-reaper.js.map +1 -0
  21. package/dist/cli/lisa-test-run-runtime.d.ts +46 -0
  22. package/dist/cli/lisa-test-run-runtime.d.ts.map +1 -0
  23. package/dist/cli/lisa-test-run-runtime.js +123 -0
  24. package/dist/cli/lisa-test-run-runtime.js.map +1 -0
  25. package/dist/cli/lisa-test-run-supervisor.d.ts +13 -0
  26. package/dist/cli/lisa-test-run-supervisor.d.ts.map +1 -0
  27. package/dist/cli/lisa-test-run-supervisor.js +268 -0
  28. package/dist/cli/lisa-test-run-supervisor.js.map +1 -0
  29. package/dist/cli/lisa-test-run.d.ts +3 -0
  30. package/dist/cli/lisa-test-run.d.ts.map +1 -0
  31. package/dist/cli/lisa-test-run.js +66 -0
  32. package/dist/cli/lisa-test-run.js.map +1 -0
  33. package/dist/configs/vitest/base.d.ts.map +1 -1
  34. package/dist/configs/vitest/base.js +5 -2
  35. package/dist/configs/vitest/base.js.map +1 -1
  36. package/dist/configs/vitest/cdk.d.ts +4 -0
  37. package/dist/configs/vitest/cdk.d.ts.map +1 -1
  38. package/dist/configs/vitest/cdk.js +5 -0
  39. package/dist/configs/vitest/cdk.js.map +1 -1
  40. package/dist/configs/vitest/harper-fabric.d.ts.map +1 -1
  41. package/dist/configs/vitest/harper-fabric.js +1 -0
  42. package/dist/configs/vitest/harper-fabric.js.map +1 -1
  43. package/dist/configs/vitest/nestjs.d.ts.map +1 -1
  44. package/dist/configs/vitest/nestjs.js +1 -0
  45. package/dist/configs/vitest/nestjs.js.map +1 -1
  46. package/dist/configs/vitest/phaser.d.ts.map +1 -1
  47. package/dist/configs/vitest/phaser.js +1 -0
  48. package/dist/configs/vitest/phaser.js.map +1 -1
  49. package/dist/configs/vitest/scratch-authority.d.ts +25 -0
  50. package/dist/configs/vitest/scratch-authority.d.ts.map +1 -0
  51. package/dist/configs/vitest/scratch-authority.js +107 -0
  52. package/dist/configs/vitest/scratch-authority.js.map +1 -0
  53. package/dist/configs/vitest/scratch-bound-cleanup-programs.d.ts +5 -0
  54. package/dist/configs/vitest/scratch-bound-cleanup-programs.d.ts.map +1 -0
  55. package/dist/configs/vitest/scratch-bound-cleanup-programs.js +219 -0
  56. package/dist/configs/vitest/scratch-bound-cleanup-programs.js.map +1 -0
  57. package/dist/configs/vitest/scratch-bound-cleanup.d.ts +28 -0
  58. package/dist/configs/vitest/scratch-bound-cleanup.d.ts.map +1 -0
  59. package/dist/configs/vitest/scratch-bound-cleanup.js +155 -0
  60. package/dist/configs/vitest/scratch-bound-cleanup.js.map +1 -0
  61. package/dist/configs/vitest/scratch-bound-root-cleanup.d.ts +17 -0
  62. package/dist/configs/vitest/scratch-bound-root-cleanup.d.ts.map +1 -0
  63. package/dist/configs/vitest/scratch-bound-root-cleanup.js +149 -0
  64. package/dist/configs/vitest/scratch-bound-root-cleanup.js.map +1 -0
  65. package/dist/configs/vitest/scratch-global-setup.d.ts +17 -9
  66. package/dist/configs/vitest/scratch-global-setup.d.ts.map +1 -1
  67. package/dist/configs/vitest/scratch-global-setup.js +103 -35
  68. package/dist/configs/vitest/scratch-global-setup.js.map +1 -1
  69. package/dist/configs/vitest/scratch-leak-setup.d.ts +2 -0
  70. package/dist/configs/vitest/scratch-leak-setup.d.ts.map +1 -0
  71. package/dist/configs/vitest/scratch-leak-setup.js +108 -0
  72. package/dist/configs/vitest/scratch-leak-setup.js.map +1 -0
  73. package/dist/configs/vitest/scratch-namespace-authority.d.ts +80 -0
  74. package/dist/configs/vitest/scratch-namespace-authority.d.ts.map +1 -0
  75. package/dist/configs/vitest/scratch-namespace-authority.js +291 -0
  76. package/dist/configs/vitest/scratch-namespace-authority.js.map +1 -0
  77. package/dist/configs/vitest/scratch-owner.d.ts +131 -0
  78. package/dist/configs/vitest/scratch-owner.d.ts.map +1 -0
  79. package/dist/configs/vitest/scratch-owner.js +297 -0
  80. package/dist/configs/vitest/scratch-owner.js.map +1 -0
  81. package/dist/configs/vitest/scratch-route-profile.d.ts +36 -0
  82. package/dist/configs/vitest/scratch-route-profile.d.ts.map +1 -0
  83. package/dist/configs/vitest/scratch-route-profile.js +217 -0
  84. package/dist/configs/vitest/scratch-route-profile.js.map +1 -0
  85. package/dist/configs/vitest/scratch-run-root-compatibility.d.ts +33 -0
  86. package/dist/configs/vitest/scratch-run-root-compatibility.d.ts.map +1 -0
  87. package/dist/configs/vitest/scratch-run-root-compatibility.js +137 -0
  88. package/dist/configs/vitest/scratch-run-root-compatibility.js.map +1 -0
  89. package/dist/configs/vitest/scratch-run-root-intent.d.ts +66 -0
  90. package/dist/configs/vitest/scratch-run-root-intent.d.ts.map +1 -0
  91. package/dist/configs/vitest/scratch-run-root-intent.js +183 -0
  92. package/dist/configs/vitest/scratch-run-root-intent.js.map +1 -0
  93. package/dist/configs/vitest/scratch-setup.d.ts.map +1 -1
  94. package/dist/configs/vitest/scratch-setup.js +45 -15
  95. package/dist/configs/vitest/scratch-setup.js.map +1 -1
  96. package/dist/configs/vitest/scratch-supervision-intent.d.ts +8 -0
  97. package/dist/configs/vitest/scratch-supervision-intent.d.ts.map +1 -0
  98. package/dist/configs/vitest/scratch-supervision-intent.js +125 -0
  99. package/dist/configs/vitest/scratch-supervision-intent.js.map +1 -0
  100. package/dist/configs/vitest/scratch-supervision-protocol.d.ts +12 -0
  101. package/dist/configs/vitest/scratch-supervision-protocol.d.ts.map +1 -0
  102. package/dist/configs/vitest/scratch-supervision-protocol.js +230 -0
  103. package/dist/configs/vitest/scratch-supervision-protocol.js.map +1 -0
  104. package/dist/configs/vitest/scratch-supervision.d.ts +64 -0
  105. package/dist/configs/vitest/scratch-supervision.d.ts.map +1 -0
  106. package/dist/configs/vitest/scratch-supervision.js +209 -0
  107. package/dist/configs/vitest/scratch-supervision.js.map +1 -0
  108. package/dist/configs/vitest/scratch-sweep.d.ts +66 -0
  109. package/dist/configs/vitest/scratch-sweep.d.ts.map +1 -0
  110. package/dist/configs/vitest/scratch-sweep.js +82 -0
  111. package/dist/configs/vitest/scratch-sweep.js.map +1 -0
  112. package/dist/configs/vitest/scratch.d.ts +14 -187
  113. package/dist/configs/vitest/scratch.d.ts.map +1 -1
  114. package/dist/configs/vitest/scratch.js +18 -285
  115. package/dist/configs/vitest/scratch.js.map +1 -1
  116. package/dist/configs/vitest/typescript.d.ts.map +1 -1
  117. package/dist/configs/vitest/typescript.js +1 -0
  118. package/dist/configs/vitest/typescript.js.map +1 -1
  119. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  120. package/dist/core/lisa-owned-hash-ledger.js +4 -0
  121. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  122. package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
  123. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  124. package/dist/core/upstream-evidence-manifest.js +59 -13
  125. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  126. package/expo/package-lisa/package.lisa.json +14 -14
  127. package/harper-fabric/package-lisa/package.lisa.json +8 -3
  128. package/nestjs/create-only/vitest.config.local.ts +5 -1
  129. package/nestjs/package-lisa/package.lisa.json +14 -8
  130. package/npm-package/package-lisa/package.lisa.json +8 -0
  131. package/package.json +17 -13
  132. package/phaser/package-lisa/package.lisa.json +9 -3
  133. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  134. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  135. package/plugins/lisa/hooks/sonar-secrets.sh +3 -2
  136. package/plugins/lisa-agy/plugin.json +1 -1
  137. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  138. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  139. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  140. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  141. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  142. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  143. package/plugins/lisa-copilot/hooks/sonar-secrets.sh +3 -2
  144. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  145. package/plugins/lisa-cursor/hooks/sonar-secrets.sh +3 -2
  146. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  147. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  148. package/plugins/lisa-expo-agy/plugin.json +1 -1
  149. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  150. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  151. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  152. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  153. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  154. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  155. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  156. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  157. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  158. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  159. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  160. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  161. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  162. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  163. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  164. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  165. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  166. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  167. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  168. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  169. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  170. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  171. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  172. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  173. package/plugins/lisa-rails-agy/plugin.json +1 -1
  174. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  175. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  176. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  177. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  178. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  179. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  180. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  181. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  182. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  183. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  184. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  185. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  186. package/plugins/src/base/hooks/sonar-secrets.sh +3 -2
  187. package/typescript/create-only/vitest.config.local.ts +5 -1
  188. package/typescript/package-lisa/package.lisa.json +14 -8
@@ -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
@@ -1 +1 @@
1
- {"version":3,"file":"scratch-global-setup.js","sourceRoot":"","sources":["../../../src/configs/vitest/scratch-global-setup.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"scratch-global-setup.js","sourceRoot":"","sources":["../../../src/configs/vitest/scratch-global-setup.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=scratch-leak-setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scratch-leak-setup.d.ts","sourceRoot":"","sources":["../../../src/configs/vitest/scratch-leak-setup.ts"],"names":[],"mappings":""}
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Per-suite backstop that makes a leaked fixture fail the suite that leaked it.
3
+ *
4
+ * The worker-wide run root is still removed wholesale on exit and reclaimed by
5
+ * a successor after SIGKILL. This hook is the attribution layer: it snapshots
6
+ * the run-root children before collection, runs last after every user
7
+ * `afterAll`, removes anything added by the suite, and fails on additions whose
8
+ * prefix was not registered before collection.
9
+ * @module configs/vitest/scratch-leak-setup
10
+ */
11
+ import * as fs from "node:fs";
12
+ import { tmpdir } from "node:os";
13
+ import * as path from "node:path";
14
+ import { afterAll } from "vitest";
15
+ import { readBoundedScratchNames, removeAuthorizedScratchChildren, } from "./scratch-authority.js";
16
+ import { SCRATCH_OWNER_FILE, readScratchOwnerRecord } from "./scratch-owner.js";
17
+ /** Run root whose children this suite owns. */
18
+ const runRoot = tmpdir();
19
+ /** Fail immediately if the setup order did not install scratch first. */
20
+ if (!path.basename(runRoot).startsWith("worker-")) {
21
+ throw new Error(`Scratch leak guard started before scratch redirection: ${runRoot}`);
22
+ }
23
+ /** Owner record binds the prefix registry and path identity to this root. */
24
+ const owner = readScratchOwnerRecord(runRoot);
25
+ /** Direct children present before this suite imports any test module. */
26
+ const baseline = new Set([
27
+ ...readBoundedScratchNames(runRoot),
28
+ SCRATCH_OWNER_FILE,
29
+ ]);
30
+ /**
31
+ * Render the prefix a mkdtemp-shaped basename came from.
32
+ * @param name - Direct scratch child basename
33
+ * @returns Display prefix
34
+ */
35
+ function displayedPrefix(name) {
36
+ return /^[\s\S]*[A-Za-z0-9]{6}$/u.test(name) ? `${name.slice(0, -6)}*` : name;
37
+ }
38
+ /**
39
+ * Remove every classified addition through one inode-bound cleanup process.
40
+ * @param names - Direct scratch child basenames
41
+ */
42
+ function removeInternalChildren(names) {
43
+ for (const name of names) {
44
+ if (path.basename(name) !== name || name === "." || name === "..") {
45
+ throw new Error(`Scratch leak guard refused non-basename child: ${name}`);
46
+ }
47
+ }
48
+ const before = fs.lstatSync(runRoot);
49
+ if (before.dev !== owner.root.dev ||
50
+ before.ino !== owner.root.ino ||
51
+ fs.realpathSync(runRoot) !== owner.root.canonicalPath) {
52
+ throw new Error("Scratch run-root identity changed before leak cleanup");
53
+ }
54
+ const marker = readScratchOwnerRecord(runRoot);
55
+ if (marker.token !== owner.token) {
56
+ throw new Error("Scratch owner marker changed before leak cleanup");
57
+ }
58
+ removeAuthorizedScratchChildren({
59
+ parent: owner.root,
60
+ basenames: names,
61
+ });
62
+ if (readScratchOwnerRecord(runRoot).token !== owner.token) {
63
+ throw new Error("Scratch owner marker changed during leak cleanup");
64
+ }
65
+ }
66
+ /**
67
+ * Read the run root or throw a suite-attributed diagnostic.
68
+ * @returns Current direct-child basenames
69
+ */
70
+ function readRunRootNames() {
71
+ try {
72
+ return readBoundedScratchNames(runRoot);
73
+ }
74
+ catch (error) {
75
+ throw new Error(`Scratch leak guard could not read suite root ${runRoot}: ${String(error)}`);
76
+ }
77
+ }
78
+ /**
79
+ * Whether one addition belongs to a prefix registered before collection.
80
+ * @param name - Added direct-child basename
81
+ * @returns Whether the stack registered ownership
82
+ */
83
+ function isRegisteredAddition(name) {
84
+ return owner.registeredPrefixes.some(prefix => name.startsWith(prefix));
85
+ }
86
+ /**
87
+ * Audit direct additions after user hooks, clean every one, and fail on any
88
+ * prefix the stack did not register before collection.
89
+ */
90
+ afterAll(() => {
91
+ const names = readRunRootNames();
92
+ const additions = names.filter(name => !baseline.has(name));
93
+ const unregistered = additions.filter(name => !isRegisteredAddition(name));
94
+ try {
95
+ removeInternalChildren(additions);
96
+ }
97
+ catch (error) {
98
+ throw new Error(`Scratch leak guard could not clean ${additions.join(", ")} for suite ${owner.suiteLabel}: ${String(error)}`);
99
+ }
100
+ if (unregistered.length > 0) {
101
+ const details = [...unregistered]
102
+ .sort((left, right) => left.localeCompare(right))
103
+ .map(name => `${name} (${displayedPrefix(name)})`)
104
+ .join(", ");
105
+ throw new Error(`Suite ${owner.suiteLabel} leaked ${String(unregistered.length)} unregistered scratch fixture(s): ${details}`);
106
+ }
107
+ });
108
+ //# sourceMappingURL=scratch-leak-setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scratch-leak-setup.js","sourceRoot":"","sources":["../../../src/configs/vitest/scratch-leak-setup.ts"],"names":[],"mappings":"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"}