@skillsmith/core 0.11.4 → 0.11.6

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 (240) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/README.md +10 -7
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/src/api/client.d.ts.map +1 -1
  5. package/dist/src/api/client.js +20 -5
  6. package/dist/src/api/client.js.map +1 -1
  7. package/dist/src/api/client.private-registry.d.ts +80 -0
  8. package/dist/src/api/client.private-registry.d.ts.map +1 -0
  9. package/dist/src/api/client.private-registry.js +97 -0
  10. package/dist/src/api/client.private-registry.js.map +1 -0
  11. package/dist/src/api/client.private-registry.test.d.ts +9 -0
  12. package/dist/src/api/client.private-registry.test.d.ts.map +1 -0
  13. package/dist/src/api/client.private-registry.test.js +127 -0
  14. package/dist/src/api/client.private-registry.test.js.map +1 -0
  15. package/dist/src/api/client.test.js +39 -0
  16. package/dist/src/api/client.test.js.map +1 -1
  17. package/dist/src/api/client.toSkill.test.d.ts +25 -0
  18. package/dist/src/api/client.toSkill.test.d.ts.map +1 -0
  19. package/dist/src/api/client.toSkill.test.js +97 -0
  20. package/dist/src/api/client.toSkill.test.js.map +1 -0
  21. package/dist/src/api/client.token-refresh.d.ts +8 -0
  22. package/dist/src/api/client.token-refresh.d.ts.map +1 -1
  23. package/dist/src/api/client.token-refresh.js +26 -0
  24. package/dist/src/api/client.token-refresh.js.map +1 -1
  25. package/dist/src/api/client.token-refresh.test.js +82 -0
  26. package/dist/src/api/client.token-refresh.test.js.map +1 -1
  27. package/dist/src/api/index.d.ts +2 -0
  28. package/dist/src/api/index.d.ts.map +1 -1
  29. package/dist/src/api/index.js +9 -0
  30. package/dist/src/api/index.js.map +1 -1
  31. package/dist/src/api/security-summary.d.ts +73 -0
  32. package/dist/src/api/security-summary.d.ts.map +1 -0
  33. package/dist/src/api/security-summary.js +88 -0
  34. package/dist/src/api/security-summary.js.map +1 -0
  35. package/dist/src/api/security-summary.test.d.ts +9 -0
  36. package/dist/src/api/security-summary.test.d.ts.map +1 -0
  37. package/dist/src/api/security-summary.test.js +94 -0
  38. package/dist/src/api/security-summary.test.js.map +1 -0
  39. package/dist/src/compatibility/slugs.d.ts +7 -3
  40. package/dist/src/compatibility/slugs.d.ts.map +1 -1
  41. package/dist/src/compatibility/slugs.js +11 -4
  42. package/dist/src/compatibility/slugs.js.map +1 -1
  43. package/dist/src/config/config-atomic-write.d.ts +30 -10
  44. package/dist/src/config/config-atomic-write.d.ts.map +1 -1
  45. package/dist/src/config/config-atomic-write.js +33 -80
  46. package/dist/src/config/config-atomic-write.js.map +1 -1
  47. package/dist/src/config/config-atomic-write.test.d.ts +8 -0
  48. package/dist/src/config/config-atomic-write.test.d.ts.map +1 -1
  49. package/dist/src/config/config-atomic-write.test.js +64 -12
  50. package/dist/src/config/config-atomic-write.test.js.map +1 -1
  51. package/dist/src/config/owned-lock.acquire.d.ts +53 -0
  52. package/dist/src/config/owned-lock.acquire.d.ts.map +1 -0
  53. package/dist/src/config/owned-lock.acquire.js +140 -0
  54. package/dist/src/config/owned-lock.acquire.js.map +1 -0
  55. package/dist/src/config/owned-lock.claim.d.ts +84 -0
  56. package/dist/src/config/owned-lock.claim.d.ts.map +1 -0
  57. package/dist/src/config/owned-lock.claim.js +310 -0
  58. package/dist/src/config/owned-lock.claim.js.map +1 -0
  59. package/dist/src/config/owned-lock.d.ts +113 -0
  60. package/dist/src/config/owned-lock.d.ts.map +1 -0
  61. package/dist/src/config/owned-lock.js +121 -0
  62. package/dist/src/config/owned-lock.js.map +1 -0
  63. package/dist/src/config/owned-lock.test.d.ts +12 -0
  64. package/dist/src/config/owned-lock.test.d.ts.map +1 -0
  65. package/dist/src/config/owned-lock.test.js +412 -0
  66. package/dist/src/config/owned-lock.test.js.map +1 -0
  67. package/dist/src/config/owned-lock.types.d.ts +78 -0
  68. package/dist/src/config/owned-lock.types.d.ts.map +1 -0
  69. package/dist/src/config/owned-lock.types.js +30 -0
  70. package/dist/src/config/owned-lock.types.js.map +1 -0
  71. package/dist/src/db/createDatabase.d.ts.map +1 -1
  72. package/dist/src/db/createDatabase.js +19 -7
  73. package/dist/src/db/createDatabase.js.map +1 -1
  74. package/dist/src/db/drivers/sqljsDriver.d.ts +8 -0
  75. package/dist/src/db/drivers/sqljsDriver.d.ts.map +1 -1
  76. package/dist/src/db/drivers/sqljsDriver.js +24 -2
  77. package/dist/src/db/drivers/sqljsDriver.js.map +1 -1
  78. package/dist/src/db/migration-runner.d.ts.map +1 -1
  79. package/dist/src/db/migration-runner.js +23 -2
  80. package/dist/src/db/migration-runner.js.map +1 -1
  81. package/dist/src/db/schema.d.ts.map +1 -1
  82. package/dist/src/db/schema.js +29 -3
  83. package/dist/src/db/schema.js.map +1 -1
  84. package/dist/src/embeddings/hnsw-store.exports.d.ts +11 -0
  85. package/dist/src/embeddings/hnsw-store.exports.d.ts.map +1 -0
  86. package/dist/src/embeddings/hnsw-store.exports.js +10 -0
  87. package/dist/src/embeddings/hnsw-store.exports.js.map +1 -0
  88. package/dist/src/embeddings/index.d.ts +1 -2
  89. package/dist/src/embeddings/index.d.ts.map +1 -1
  90. package/dist/src/embeddings/index.js +17 -3
  91. package/dist/src/embeddings/index.js.map +1 -1
  92. package/dist/src/embeddings/probe.d.ts.map +1 -1
  93. package/dist/src/embeddings/probe.js +4 -8
  94. package/dist/src/embeddings/probe.js.map +1 -1
  95. package/dist/src/exports/services.d.ts +5 -1
  96. package/dist/src/exports/services.d.ts.map +1 -1
  97. package/dist/src/exports/services.js +27 -0
  98. package/dist/src/exports/services.js.map +1 -1
  99. package/dist/src/index.d.ts +3 -3
  100. package/dist/src/index.d.ts.map +1 -1
  101. package/dist/src/index.js +9 -3
  102. package/dist/src/index.js.map +1 -1
  103. package/dist/src/install/index.d.ts +2 -2
  104. package/dist/src/install/index.d.ts.map +1 -1
  105. package/dist/src/install/index.js +1 -1
  106. package/dist/src/install/index.js.map +1 -1
  107. package/dist/src/install/paths.d.ts +165 -1
  108. package/dist/src/install/paths.d.ts.map +1 -1
  109. package/dist/src/install/paths.js +254 -1
  110. package/dist/src/install/paths.js.map +1 -1
  111. package/dist/src/install/paths.test.js +174 -2
  112. package/dist/src/install/paths.test.js.map +1 -1
  113. package/dist/src/security/scanner/multiline-category-closure.test.d.ts +80 -0
  114. package/dist/src/security/scanner/multiline-category-closure.test.d.ts.map +1 -0
  115. package/dist/src/security/scanner/multiline-category-closure.test.js +287 -0
  116. package/dist/src/security/scanner/multiline-category-closure.test.js.map +1 -0
  117. package/dist/src/services/context-words.d.ts +40 -0
  118. package/dist/src/services/context-words.d.ts.map +1 -0
  119. package/dist/src/services/context-words.js +178 -0
  120. package/dist/src/services/context-words.js.map +1 -0
  121. package/dist/src/services/context-words.test.d.ts +2 -0
  122. package/dist/src/services/context-words.test.d.ts.map +1 -0
  123. package/dist/src/services/context-words.test.js +94 -0
  124. package/dist/src/services/context-words.test.js.map +1 -0
  125. package/dist/src/services/recommend-guard.d.ts +28 -0
  126. package/dist/src/services/recommend-guard.d.ts.map +1 -0
  127. package/dist/src/services/recommend-guard.js +34 -0
  128. package/dist/src/services/recommend-guard.js.map +1 -0
  129. package/dist/src/services/recommend-guard.test.d.ts +2 -0
  130. package/dist/src/services/recommend-guard.test.d.ts.map +1 -0
  131. package/dist/src/services/recommend-guard.test.js +28 -0
  132. package/dist/src/services/recommend-guard.test.js.map +1 -0
  133. package/dist/src/services/skill-installation.content.d.ts +96 -0
  134. package/dist/src/services/skill-installation.content.d.ts.map +1 -0
  135. package/dist/src/services/skill-installation.content.js +319 -0
  136. package/dist/src/services/skill-installation.content.js.map +1 -0
  137. package/dist/src/services/skill-installation.helpers.d.ts +32 -1
  138. package/dist/src/services/skill-installation.helpers.d.ts.map +1 -1
  139. package/dist/src/services/skill-installation.helpers.js +62 -7
  140. package/dist/src/services/skill-installation.helpers.js.map +1 -1
  141. package/dist/src/services/skill-installation.helpers.test.js +108 -2
  142. package/dist/src/services/skill-installation.helpers.test.js.map +1 -1
  143. package/dist/src/services/skill-installation.io.d.ts +22 -1
  144. package/dist/src/services/skill-installation.io.d.ts.map +1 -1
  145. package/dist/src/services/skill-installation.io.js +110 -6
  146. package/dist/src/services/skill-installation.io.js.map +1 -1
  147. package/dist/src/services/skill-installation.service.d.ts +22 -1
  148. package/dist/src/services/skill-installation.service.d.ts.map +1 -1
  149. package/dist/src/services/skill-installation.service.js +30 -5
  150. package/dist/src/services/skill-installation.service.js.map +1 -1
  151. package/dist/src/services/skill-installation.types.d.ts +42 -1
  152. package/dist/src/services/skill-installation.types.d.ts.map +1 -1
  153. package/dist/src/services/skill-installation.types.js.map +1 -1
  154. package/dist/src/services/skill-manifest.d.ts +21 -0
  155. package/dist/src/services/skill-manifest.d.ts.map +1 -1
  156. package/dist/src/services/skill-manifest.js +51 -6
  157. package/dist/src/services/skill-manifest.js.map +1 -1
  158. package/dist/src/services/skill-manifest.test.d.ts +2 -0
  159. package/dist/src/services/skill-manifest.test.d.ts.map +1 -0
  160. package/dist/src/services/skill-manifest.test.js +202 -0
  161. package/dist/src/services/skill-manifest.test.js.map +1 -0
  162. package/dist/src/services/skill-resolution.d.ts +67 -0
  163. package/dist/src/services/skill-resolution.d.ts.map +1 -0
  164. package/dist/src/services/skill-resolution.js +67 -0
  165. package/dist/src/services/skill-resolution.js.map +1 -0
  166. package/dist/src/services/skill-resolution.test.d.ts +2 -0
  167. package/dist/src/services/skill-resolution.test.d.ts.map +1 -0
  168. package/dist/src/services/skill-resolution.test.js +143 -0
  169. package/dist/src/services/skill-resolution.test.js.map +1 -0
  170. package/dist/src/types/skill.d.ts +10 -0
  171. package/dist/src/types/skill.d.ts.map +1 -1
  172. package/dist/src/types.d.ts +9 -5
  173. package/dist/src/types.d.ts.map +1 -1
  174. package/dist/src/utils/index.d.ts +1 -0
  175. package/dist/src/utils/index.d.ts.map +1 -1
  176. package/dist/src/utils/index.js +4 -0
  177. package/dist/src/utils/index.js.map +1 -1
  178. package/dist/src/utils/quiet-mode.d.ts +21 -0
  179. package/dist/src/utils/quiet-mode.d.ts.map +1 -0
  180. package/dist/src/utils/quiet-mode.js +26 -0
  181. package/dist/src/utils/quiet-mode.js.map +1 -0
  182. package/dist/tests/EmbeddingService.test.js +58 -1
  183. package/dist/tests/EmbeddingService.test.js.map +1 -1
  184. package/dist/tests/SecurityScanner.performance.test.js +5 -2
  185. package/dist/tests/SecurityScanner.performance.test.js.map +1 -1
  186. package/dist/tests/db/database-abstraction.test.js +45 -1
  187. package/dist/tests/db/database-abstraction.test.js.map +1 -1
  188. package/dist/tests/db/migration-runner-race.test.d.ts +2 -0
  189. package/dist/tests/db/migration-runner-race.test.d.ts.map +1 -0
  190. package/dist/tests/db/migration-runner-race.test.js +75 -0
  191. package/dist/tests/db/migration-runner-race.test.js.map +1 -0
  192. package/dist/tests/db/schema-async.test.js +37 -1
  193. package/dist/tests/db/schema-async.test.js.map +1 -1
  194. package/dist/tests/db/sqljsDriver.test.js +63 -1
  195. package/dist/tests/db/sqljsDriver.test.js.map +1 -1
  196. package/dist/tests/helpers/deterministic-dead-pid.d.ts +13 -0
  197. package/dist/tests/helpers/deterministic-dead-pid.d.ts.map +1 -0
  198. package/dist/tests/helpers/deterministic-dead-pid.js +31 -0
  199. package/dist/tests/helpers/deterministic-dead-pid.js.map +1 -0
  200. package/dist/tests/integration/owned-lock-lost-update.test.d.ts +12 -0
  201. package/dist/tests/integration/owned-lock-lost-update.test.d.ts.map +1 -0
  202. package/dist/tests/integration/owned-lock-lost-update.test.js +52 -0
  203. package/dist/tests/integration/owned-lock-lost-update.test.js.map +1 -0
  204. package/dist/tests/integration/owned-lock-reclaim-race.test.d.ts +19 -0
  205. package/dist/tests/integration/owned-lock-reclaim-race.test.d.ts.map +1 -0
  206. package/dist/tests/integration/owned-lock-reclaim-race.test.js +177 -0
  207. package/dist/tests/integration/owned-lock-reclaim-race.test.js.map +1 -0
  208. package/dist/tests/schema.test.js +43 -1
  209. package/dist/tests/schema.test.js.map +1 -1
  210. package/dist/tests/unit/services/skill-installation.content.test.d.ts +10 -0
  211. package/dist/tests/unit/services/skill-installation.content.test.d.ts.map +1 -0
  212. package/dist/tests/unit/services/skill-installation.content.test.js +290 -0
  213. package/dist/tests/unit/services/skill-installation.content.test.js.map +1 -0
  214. package/dist/tests/unit/services/skill-installation.io.client-agent-path.test.d.ts +34 -0
  215. package/dist/tests/unit/services/skill-installation.io.client-agent-path.test.d.ts.map +1 -0
  216. package/dist/tests/unit/services/skill-installation.io.client-agent-path.test.js +246 -0
  217. package/dist/tests/unit/services/skill-installation.io.client-agent-path.test.js.map +1 -0
  218. package/dist/tests/unit/services/skill-installation.io.symlink.test.d.ts +8 -0
  219. package/dist/tests/unit/services/skill-installation.io.symlink.test.d.ts.map +1 -0
  220. package/dist/tests/unit/services/skill-installation.io.symlink.test.js +72 -0
  221. package/dist/tests/unit/services/skill-installation.io.symlink.test.js.map +1 -0
  222. package/dist/tests/unit/services/skill-installation.service.companion-basedir.test.d.ts +17 -0
  223. package/dist/tests/unit/services/skill-installation.service.companion-basedir.test.d.ts.map +1 -0
  224. package/dist/tests/unit/services/skill-installation.service.companion-basedir.test.js +112 -0
  225. package/dist/tests/unit/services/skill-installation.service.companion-basedir.test.js.map +1 -0
  226. package/dist/tests/unit/services/skill-installation.service.error-codes.test.d.ts +15 -0
  227. package/dist/tests/unit/services/skill-installation.service.error-codes.test.d.ts.map +1 -0
  228. package/dist/tests/unit/services/skill-installation.service.error-codes.test.js +190 -0
  229. package/dist/tests/unit/services/skill-installation.service.error-codes.test.js.map +1 -0
  230. package/dist/tests/unit/services/skill-installation.service.multi-client.test.d.ts +17 -0
  231. package/dist/tests/unit/services/skill-installation.service.multi-client.test.d.ts.map +1 -0
  232. package/dist/tests/unit/services/skill-installation.service.multi-client.test.js +126 -0
  233. package/dist/tests/unit/services/skill-installation.service.multi-client.test.js.map +1 -0
  234. package/dist/tests/unit/services/skill-installation.service.test.js +7 -112
  235. package/dist/tests/unit/services/skill-installation.service.test.js.map +1 -1
  236. package/dist/tests/utils/quiet-mode.test.d.ts +12 -0
  237. package/dist/tests/utils/quiet-mode.test.d.ts.map +1 -0
  238. package/dist/tests/utils/quiet-mode.test.js +42 -0
  239. package/dist/tests/utils/quiet-mode.test.js.map +1 -0
  240. package/package.json +17 -5
@@ -0,0 +1,75 @@
1
+ /**
2
+ * SMI-6003: regression test for the unguarded concurrent-migration race in
3
+ * runMigrations() (packages/core/src/db/migration-runner.ts).
4
+ *
5
+ * Two processes opening the same fresh DB concurrently can both read
6
+ * currentVersion = getSchemaVersion(db) BEFORE either has stamped a row,
7
+ * both decide the same migration is pending, and the loser's plain
8
+ * `INSERT INTO schema_version (version) VALUES (?)` throws `UNIQUE
9
+ * constraint failed: schema_version.version`. This is the exact real-world
10
+ * failure crash-handler-integration.test.ts's SMI-5999 fix sidesteps by
11
+ * isolating each spawned mcp-server process's HOME — that test proves the
12
+ * SYMPTOM is contained by isolation; this test proves the underlying
13
+ * migration-runner bug itself is fixed, independent of any caller-side
14
+ * isolation.
15
+ *
16
+ * Genuine concurrency is required to reproduce this — see
17
+ * migration-runner-race-child.mjs's header for why a single-process
18
+ * Promise.all() over synchronous better-sqlite3 calls cannot do it. This
19
+ * spawns two REAL child processes (same established pattern as
20
+ * owned-lock-lost-update.test.ts), each opening its own connection to the
21
+ * same DB file, barrier-synced so both call the real `runMigrations()` at
22
+ * effectively the same instant.
23
+ */
24
+ import { describe, it, expect } from 'vitest';
25
+ import { spawn } from 'node:child_process';
26
+ import { mkdirSync, rmSync } from 'node:fs';
27
+ import * as os from 'node:os';
28
+ import * as path from 'node:path';
29
+ import { fileURLToPath } from 'node:url';
30
+ import { createDatabaseSync } from '../../src/db/createDatabase.js';
31
+ import { SCHEMA_SQL } from '../../src/db/schema-sql.js';
32
+ import { closeDatabase } from '../../src/db/schema.js';
33
+ import { isBetterSqlite3Available } from '../../src/db/drivers/betterSqlite3Driver.js';
34
+ // Evaluated once at module load — used by describe.skipIf() to skip this
35
+ // suite when the native SQLite driver is unavailable (e.g. WASM fallback
36
+ // mode); the race under test is specific to a real, file-backed SQLite
37
+ // connection shared across processes, not the in-memory WASM driver.
38
+ const skipIfNoSqlite = !isBetterSqlite3Available();
39
+ const testDir = path.dirname(fileURLToPath(import.meta.url));
40
+ const childPath = path.join(testDir, '..', 'helpers', 'migration-runner-race-child.mjs');
41
+ function spawnChild(dbPath, barrierDir, id) {
42
+ return new Promise((resolve) => {
43
+ const child = spawn(process.execPath, ['--import', 'tsx', childPath, dbPath, barrierDir, String(id)], { stdio: ['ignore', 'ignore', 'pipe'] });
44
+ let stderr = '';
45
+ child.stderr.on('data', (d) => (stderr += d.toString()));
46
+ child.on('exit', (code) => resolve({ code, stderr }));
47
+ });
48
+ }
49
+ describe.skipIf(skipIfNoSqlite)('SMI-6003 runMigrations concurrent-migration race', () => {
50
+ it('two concurrent runMigrations() calls against the same fresh DB file do not throw', async () => {
51
+ const dir = path.join(os.tmpdir(), `migration-runner-race-${Date.now()}-${Math.random().toString(36).slice(2)}`);
52
+ mkdirSync(dir, { recursive: true });
53
+ const dbPath = path.join(dir, 'race.db');
54
+ // Set up a "fresh" DB already stamped at v16 — only migration v17 (the
55
+ // last entry in MIGRATIONS) is pending, so BOTH children will race to
56
+ // apply and INSERT the exact same version row. This isolates the test
57
+ // to the check-then-act bug itself (one contested INSERT) rather than
58
+ // incidental concurrent DDL from re-running the full 17-migration chain
59
+ // (which would also happen to trip the same bug, just less
60
+ // deterministically — v17's own apply() short-circuits to a cheap SELECT
61
+ // probe on a fresh SCHEMA_SQL base, since the base schema already
62
+ // includes 'curated' in the trust_tier CHECK — see
63
+ // migrations/v17-curated-trust-tier.ts).
64
+ const setupDb = createDatabaseSync(dbPath);
65
+ setupDb.exec(SCHEMA_SQL);
66
+ setupDb.prepare('INSERT INTO schema_version (version) VALUES (16)').run();
67
+ closeDatabase(setupDb);
68
+ const results = await Promise.all([spawnChild(dbPath, dir, 0), spawnChild(dbPath, dir, 1)]);
69
+ for (const r of results) {
70
+ expect(r.code, `child stderr:\n${r.stderr}`).toBe(0);
71
+ }
72
+ rmSync(dir, { recursive: true, force: true });
73
+ }, 30_000);
74
+ });
75
+ //# sourceMappingURL=migration-runner-race.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migration-runner-race.test.js","sourceRoot":"","sources":["../../../tests/db/migration-runner-race.test.ts"],"names":[],"mappings":"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"}
@@ -6,7 +6,7 @@
6
6
  * correctly with automatic driver selection.
7
7
  */
8
8
  import { describe, it, expect, afterEach } from 'vitest';
9
- import { existsSync, unlinkSync, mkdirSync, rmSync } from 'node:fs';
9
+ import { existsSync, unlinkSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
10
10
  import { join } from 'node:path';
11
11
  import { tmpdir } from 'node:os';
12
12
  import { createDatabaseAsync, openDatabaseAsync, closeDatabase, getSchemaVersion, SCHEMA_VERSION, } from '../../src/db/schema.js';
@@ -235,6 +235,42 @@ describe('Async Schema Functions (SMI-2206)', () => {
235
235
  expect(version).toBe(SCHEMA_VERSION);
236
236
  });
237
237
  });
238
+ describe('openDatabaseAsync — empty/corrupt database detection (SMI-5997)', () => {
239
+ it('throws an actionable error for a 0-byte file (no tables, no schema_version)', async () => {
240
+ if (!isBetterSqlite3Available()) {
241
+ console.log('Skipping test: no SQLite driver available');
242
+ return;
243
+ }
244
+ mkdirSync(TEST_DIR, { recursive: true });
245
+ const testPath = join(TEST_DIR, `test-empty-corrupt-${Date.now()}.db`);
246
+ testPaths.push(testPath);
247
+ // Reproduces the SMI-5997 incident: a 0-byte file left behind by an
248
+ // interrupted persist() (or any other truncation). SQLite treats a
249
+ // 0-byte file as a valid, openable, empty database, so this must be
250
+ // caught by schema.ts's zero-table check rather than SQLITE_CANTOPEN.
251
+ writeFileSync(testPath, Buffer.alloc(0));
252
+ await expect(openDatabaseAsync(testPath)).rejects.toThrow(/empty or corrupt/);
253
+ });
254
+ it('still treats a file with real tables but no schema_version as a legacy import', async () => {
255
+ if (!isBetterSqlite3Available()) {
256
+ console.log('Skipping test: no SQLite driver available');
257
+ return;
258
+ }
259
+ mkdirSync(TEST_DIR, { recursive: true });
260
+ const testPath = join(TEST_DIR, `test-legacy-import-${Date.now()}.db`);
261
+ testPaths.push(testPath);
262
+ // Build a database with a table but no schema_version, without going
263
+ // through createDatabaseAsync (which would stamp schema_version itself).
264
+ const seedDb = await createDatabaseAsync(testPath);
265
+ seedDb.exec('DROP TABLE schema_version');
266
+ closeDatabase(seedDb);
267
+ const db = await openDatabaseAsync(testPath);
268
+ testDatabases.push(db);
269
+ expect(db.open).toBe(true);
270
+ const version = getSchemaVersion(db);
271
+ expect(version).toBe(SCHEMA_VERSION);
272
+ });
273
+ });
238
274
  describe('FTS5 functionality with async creation', () => {
239
275
  it('should sync FTS on insert', async () => {
240
276
  if (!isBetterSqlite3Available()) {
@@ -1 +1 @@
1
- {"version":3,"file":"schema-async.test.js","sourceRoot":"","sources":["../../../tests/db/schema-async.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"schema-async.test.js","sourceRoot":"","sources":["../../../tests/db/schema-async.test.ts"],"names":[],"mappings":"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"}
@@ -5,8 +5,29 @@
5
5
  * and isSqlJsAvailable. The better-sqlite3 mock is scoped with vi.isolateModules()
6
6
  * to prevent contaminating betterSqlite3Driver.test.ts.
7
7
  */
8
- import { describe, it, expect, afterEach } from 'vitest';
8
+ import { describe, it, expect, afterEach, beforeEach, vi } from 'vitest';
9
+ import { existsSync, readFileSync, mkdtempSync, rmSync } from 'node:fs';
10
+ import { tmpdir } from 'node:os';
11
+ import { join } from 'node:path';
9
12
  import { createSqlJsDatabase, SqlJsDatabaseAdapter, isSqlJsAvailable, } from '../../src/db/drivers/sqljsDriver.js';
13
+ // SMI-5997: sqljsDriver.ts imports writeFileSync from 'node:fs' as a live ESM
14
+ // binding, which vi.spyOn cannot redefine ("Module namespace is not
15
+ // configurable in ESM"). vi.mock + importOriginal is the supported way to
16
+ // intercept a single named export while passing everything else through.
17
+ const failNextWrite = vi.hoisted(() => ({ value: false }));
18
+ vi.mock('node:fs', async (importOriginal) => {
19
+ const actual = await importOriginal();
20
+ return {
21
+ ...actual,
22
+ writeFileSync: (...args) => {
23
+ if (failNextWrite.value) {
24
+ failNextWrite.value = false;
25
+ throw new Error('simulated crash mid-write');
26
+ }
27
+ return actual.writeFileSync(...args);
28
+ },
29
+ };
30
+ });
10
31
  describe('sqljsDriver — edge cases', () => {
11
32
  describe('isSqlJsAvailable', () => {
12
33
  it('returns true when fts5-sql-bundle is resolvable', () => {
@@ -71,5 +92,46 @@ describe('sqljsDriver — edge cases', () => {
71
92
  await expect(createSqlJsDatabase(nonExistentPath, { fileMustExist: true })).rejects.toThrow(/SQLITE_CANTOPEN/);
72
93
  });
73
94
  });
95
+ describe('persist() — atomic write (SMI-5997)', () => {
96
+ let dir;
97
+ let dbPath;
98
+ beforeEach(() => {
99
+ dir = mkdtempSync(join(tmpdir(), 'sqljs-persist-'));
100
+ dbPath = join(dir, 'test.db');
101
+ });
102
+ afterEach(() => {
103
+ failNextWrite.value = false;
104
+ rmSync(dir, { recursive: true, force: true });
105
+ });
106
+ it('writes via temp file + rename, leaving no leftover .tmp file', async () => {
107
+ const db = await createSqlJsDatabase(dbPath);
108
+ db.exec('CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT)');
109
+ db.exec("INSERT INTO t (name) VALUES ('alpha')");
110
+ db.persist();
111
+ expect(existsSync(dbPath)).toBe(true);
112
+ expect(existsSync(`${dbPath}.tmp`)).toBe(false);
113
+ expect(readFileSync(dbPath).length).toBeGreaterThan(0);
114
+ db.close();
115
+ });
116
+ it('leaves the original file byte-for-byte untouched if the write is interrupted', async () => {
117
+ const db = await createSqlJsDatabase(dbPath);
118
+ db.exec('CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT)');
119
+ db.exec("INSERT INTO t (name) VALUES ('alpha')");
120
+ db.persist();
121
+ const originalContent = readFileSync(dbPath);
122
+ expect(originalContent.length).toBeGreaterThan(0);
123
+ db.exec("INSERT INTO t (name) VALUES ('beta')");
124
+ // Simulate a crash/kill mid-write: writeFileSync (to the temp file)
125
+ // throws before rename() ever runs.
126
+ failNextWrite.value = true;
127
+ expect(() => db.persist()).toThrow('simulated crash mid-write');
128
+ // Reproduces the SMI-5997 incident check in reverse: the real file
129
+ // must never be truncated by a failed write, only ever replaced
130
+ // wholesale by a successful rename().
131
+ expect(readFileSync(dbPath)).toEqual(originalContent);
132
+ expect(existsSync(`${dbPath}.tmp`)).toBe(false);
133
+ db.close();
134
+ });
135
+ });
74
136
  });
75
137
  //# sourceMappingURL=sqljsDriver.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sqljsDriver.test.js","sourceRoot":"","sources":["../../../tests/db/sqljsDriver.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sqljsDriver.test.js","sourceRoot":"","sources":["../../../tests/db/sqljsDriver.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Deterministic dead-PID helper for owned-lock tests (SMI-5883 §8).
3
+ * @module @skillsmith/core/tests/helpers/deterministic-dead-pid
4
+ *
5
+ * Replaces the round-3 assumption that a hard-coded PID (e.g. `'99999'`) is
6
+ * dead — flaky on any machine whose PID space reaches that value. Spawns a
7
+ * real, short-lived child process, waits for it to exit, then verifies
8
+ * `process.kill(pid, 0)` throws `ESRCH` before handing the PID back. Retries
9
+ * (PID reuse) up to 5 times, then fails loudly with a clear message rather
10
+ * than silently returning a live PID.
11
+ */
12
+ export declare function mintDeadPid(): number;
13
+ //# sourceMappingURL=deterministic-dead-pid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deterministic-dead-pid.d.ts","sourceRoot":"","sources":["../../../tests/helpers/deterministic-dead-pid.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,wBAAgB,WAAW,IAAI,MAAM,CAgBpC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Deterministic dead-PID helper for owned-lock tests (SMI-5883 §8).
3
+ * @module @skillsmith/core/tests/helpers/deterministic-dead-pid
4
+ *
5
+ * Replaces the round-3 assumption that a hard-coded PID (e.g. `'99999'`) is
6
+ * dead — flaky on any machine whose PID space reaches that value. Spawns a
7
+ * real, short-lived child process, waits for it to exit, then verifies
8
+ * `process.kill(pid, 0)` throws `ESRCH` before handing the PID back. Retries
9
+ * (PID reuse) up to 5 times, then fails loudly with a clear message rather
10
+ * than silently returning a live PID.
11
+ */
12
+ import { spawnSync } from 'node:child_process';
13
+ export function mintDeadPid() {
14
+ for (let attempt = 0; attempt < 5; attempt++) {
15
+ const result = spawnSync(process.execPath, ['-e', '']);
16
+ const pid = result.pid;
17
+ if (typeof pid !== 'number')
18
+ continue;
19
+ try {
20
+ process.kill(pid, 0);
21
+ continue; // still signalable somehow (should not happen for an exited child) -- retry
22
+ }
23
+ catch (err) {
24
+ if (err.code === 'ESRCH')
25
+ return pid;
26
+ }
27
+ }
28
+ throw new Error('[owned-lock tests] could not mint a deterministically-dead PID after 5 attempts -- ' +
29
+ 'every spawned child still answered a liveness probe.');
30
+ }
31
+ //# sourceMappingURL=deterministic-dead-pid.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deterministic-dead-pid.js","sourceRoot":"","sources":["../../../tests/helpers/deterministic-dead-pid.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAE9C,MAAM,UAAU,WAAW;IACzB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC;QAC7C,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAA;QACtD,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAA;QACtB,IAAI,OAAO,GAAG,KAAK,QAAQ;YAAE,SAAQ;QACrC,IAAI,CAAC;YACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;YACpB,SAAQ,CAAC,4EAA4E;QACvF,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAK,GAA6B,CAAC,IAAI,KAAK,OAAO;gBAAE,OAAO,GAAG,CAAA;QACjE,CAAC;IACH,CAAC;IACD,MAAM,IAAI,KAAK,CACb,qFAAqF;QACnF,sDAAsD,CACzD,CAAA;AACH,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * SMI-5883 §8f -- end-to-end lost-update stress test, deliberately kept
3
+ * SEPARATE from the §8b reclaim-race proof: 8 real child processes each
4
+ * acquire -> read -> add a distinct key -> atomicWriteFile -> release, 5
5
+ * rounds each, with NO seam, NO barrier, and NO injection. This exercises
6
+ * the primitive under real contention among genuinely-alive processes (no
7
+ * staleness/reclaim path is ever exercised here), catching whole-mechanism
8
+ * regressions that a seam-driven test could mask. Neither this test nor
9
+ * §8b substitutes for the other.
10
+ */
11
+ export {};
12
+ //# sourceMappingURL=owned-lock-lost-update.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"owned-lock-lost-update.test.d.ts","sourceRoot":"","sources":["../../../tests/integration/owned-lock-lost-update.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * SMI-5883 §8f -- end-to-end lost-update stress test, deliberately kept
3
+ * SEPARATE from the §8b reclaim-race proof: 8 real child processes each
4
+ * acquire -> read -> add a distinct key -> atomicWriteFile -> release, 5
5
+ * rounds each, with NO seam, NO barrier, and NO injection. This exercises
6
+ * the primitive under real contention among genuinely-alive processes (no
7
+ * staleness/reclaim path is ever exercised here), catching whole-mechanism
8
+ * regressions that a seam-driven test could mask. Neither this test nor
9
+ * §8b substitutes for the other.
10
+ */
11
+ import { describe, it, expect } from 'vitest';
12
+ import { spawn } from 'node:child_process';
13
+ import { existsSync, mkdirSync, readFileSync, rmSync } from 'node:fs';
14
+ import * as os from 'node:os';
15
+ import * as path from 'node:path';
16
+ import { fileURLToPath } from 'node:url';
17
+ const testDir = path.dirname(fileURLToPath(import.meta.url));
18
+ const childPath = path.join(testDir, '..', 'helpers', 'owned-lock-stress-child.mjs');
19
+ const WORKERS = 8;
20
+ const ROUNDS = 5;
21
+ function spawnWorker(id, target) {
22
+ return new Promise((resolve) => {
23
+ const child = spawn(process.execPath, ['--import', 'tsx', childPath, String(id), target, String(ROUNDS)], { stdio: ['ignore', 'ignore', 'pipe'] });
24
+ let stderr = '';
25
+ child.stderr.on('data', (d) => (stderr += d.toString()));
26
+ child.on('exit', (code) => resolve({ code, stderr }));
27
+ });
28
+ }
29
+ describe('owned-lock end-to-end lost update (§8f)', () => {
30
+ it(`${WORKERS} workers x ${ROUNDS} rounds under real contention lose no updates`, async () => {
31
+ const dir = path.join(os.tmpdir(), `owned-lock-lost-update-${Date.now()}-${Math.random().toString(36).slice(2)}`);
32
+ mkdirSync(dir, { recursive: true });
33
+ const target = path.join(dir, 'store.json');
34
+ const results = await Promise.all(Array.from({ length: WORKERS }, (_, i) => spawnWorker(i, target)));
35
+ for (const r of results) {
36
+ expect(r.code, `worker stderr:\n${r.stderr}`).toBe(0);
37
+ }
38
+ expect(existsSync(target)).toBe(true);
39
+ const store = JSON.parse(readFileSync(target, 'utf-8'));
40
+ const keys = Object.keys(store);
41
+ expect(keys).toHaveLength(WORKERS * ROUNDS);
42
+ for (let id = 0; id < WORKERS; id++) {
43
+ for (let round = 0; round < ROUNDS; round++) {
44
+ expect(store[`${id}-${round}`]).toBe(true);
45
+ }
46
+ }
47
+ expect(existsSync(`${target}.lock`)).toBe(false);
48
+ expect(existsSync(`${target}.lock.reclaim`)).toBe(false);
49
+ rmSync(dir, { recursive: true, force: true });
50
+ }, 60_000);
51
+ });
52
+ //# sourceMappingURL=owned-lock-lost-update.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"owned-lock-lost-update.test.js","sourceRoot":"","sources":["../../../tests/integration/owned-lock-lost-update.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * SMI-5883 §8b -- cross-process proof that the two-level lock closes the
3
+ * round-3 lock-theft race (two concurrent reclaimers can both validate the
4
+ * same stale claim; the delayed one steals the winner's freshly-recreated
5
+ * LIVE lock). This is the single most important regression guard in the
6
+ * whole design: it is the test that specifically proves the round-3 race is
7
+ * closed, with a negative control proving the test would actually catch a
8
+ * regression if the fix were removed.
9
+ *
10
+ * Real child processes (`owned-lock-race-child.mjs`, spawned via
11
+ * `node --import tsx`) synchronize on a filesystem barrier at the exact seam
12
+ * (`onReclaimBoundary`) the reviewer required: after BOTH have independently
13
+ * validated the SAME seeded dead claim, before EITHER has touched the
14
+ * reclaim lock or removed anything. No `skipIf` -- every wait is bounded and
15
+ * throws a labelled error on expiry (see `waitForAsync` below), so a
16
+ * regression fails loudly instead of hanging the suite.
17
+ */
18
+ export {};
19
+ //# sourceMappingURL=owned-lock-reclaim-race.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"owned-lock-reclaim-race.test.d.ts","sourceRoot":"","sources":["../../../tests/integration/owned-lock-reclaim-race.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG"}
@@ -0,0 +1,177 @@
1
+ /**
2
+ * SMI-5883 §8b -- cross-process proof that the two-level lock closes the
3
+ * round-3 lock-theft race (two concurrent reclaimers can both validate the
4
+ * same stale claim; the delayed one steals the winner's freshly-recreated
5
+ * LIVE lock). This is the single most important regression guard in the
6
+ * whole design: it is the test that specifically proves the round-3 race is
7
+ * closed, with a negative control proving the test would actually catch a
8
+ * regression if the fix were removed.
9
+ *
10
+ * Real child processes (`owned-lock-race-child.mjs`, spawned via
11
+ * `node --import tsx`) synchronize on a filesystem barrier at the exact seam
12
+ * (`onReclaimBoundary`) the reviewer required: after BOTH have independently
13
+ * validated the SAME seeded dead claim, before EITHER has touched the
14
+ * reclaim lock or removed anything. No `skipIf` -- every wait is bounded and
15
+ * throws a labelled error on expiry (see `waitForAsync` below), so a
16
+ * regression fails loudly instead of hanging the suite.
17
+ */
18
+ import { describe, it, expect } from 'vitest';
19
+ import { spawn } from 'node:child_process';
20
+ import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
21
+ import { hostname } from 'node:os';
22
+ import * as os from 'node:os';
23
+ import * as path from 'node:path';
24
+ import { fileURLToPath } from 'node:url';
25
+ import { mintDeadPid } from '../helpers/deterministic-dead-pid.js';
26
+ const testDir = path.dirname(fileURLToPath(import.meta.url));
27
+ const childPath = path.join(testDir, '..', 'helpers', 'owned-lock-race-child.mjs');
28
+ function spawnChild(argsList) {
29
+ return new Promise((resolve) => {
30
+ const child = spawn(process.execPath, ['--import', 'tsx', childPath, ...argsList], {
31
+ stdio: ['ignore', 'pipe', 'pipe'],
32
+ });
33
+ let stdout = '';
34
+ let stderr = '';
35
+ child.stdout.on('data', (d) => (stdout += d.toString()));
36
+ child.stderr.on('data', (d) => (stderr += d.toString()));
37
+ child.on('exit', (code) => resolve({ code, stdout, stderr }));
38
+ });
39
+ }
40
+ async function waitForAsync(predicate, label, capMs = 10_000) {
41
+ const deadline = Date.now() + capMs;
42
+ while (!predicate()) {
43
+ if (Date.now() >= deadline) {
44
+ throw new Error(`[owned-lock-reclaim-race.test] timed out waiting for ${label}`);
45
+ }
46
+ await new Promise((r) => setTimeout(r, 5));
47
+ }
48
+ }
49
+ function makeTempDir(prefix) {
50
+ const dir = path.join(os.tmpdir(), `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2)}`);
51
+ const barrierDir = path.join(dir, 'barrier');
52
+ mkdirSync(barrierDir, { recursive: true });
53
+ return { dir, barrierDir };
54
+ }
55
+ function seedStaleLock(lockPath, deadPid) {
56
+ writeFileSync(lockPath, JSON.stringify({
57
+ v: 1,
58
+ pid: deadPid,
59
+ token: 'stale-token-0000',
60
+ host: hostname(),
61
+ acquiredAt: Date.now() - 60_000,
62
+ }) + '\n');
63
+ return readFileSync(lockPath);
64
+ }
65
+ function readIfExists(p) {
66
+ return existsSync(p) ? readFileSync(p) : null;
67
+ }
68
+ describe('owned-lock cross-process reclaim race (§8b) -- the required test', () => {
69
+ it('a delayed contender cannot steal the winner’s freshly-recreated live lock', async () => {
70
+ const { dir, barrierDir } = makeTempDir('owned-lock-race');
71
+ const target = path.join(dir, 'store.json');
72
+ const lockPath = `${target}.lock`;
73
+ const reclaimPath = `${lockPath}.reclaim`;
74
+ const deadPid = mintDeadPid();
75
+ const staleBytes = seedStaleLock(lockPath, deadPid);
76
+ const pendingA = spawnChild([
77
+ 'A',
78
+ target,
79
+ barrierDir,
80
+ '--signal-held=held-A',
81
+ '--wait-release=release-A',
82
+ ]);
83
+ const pendingB = spawnChild(['B', target, barrierDir, '--gate-on=held-A']);
84
+ // Wait for A to hold and B to have recorded ITS reclaim-attempt outcome
85
+ // (must come from the authoritative re-read finding a live owner, not
86
+ // luck) -- while A STILL holds, i.e. before releasing it.
87
+ await waitForAsync(() => existsSync(path.join(barrierDir, 'held-A')), "child A's held-A marker");
88
+ await waitForAsync(() => readdirSync(barrierDir).some((f) => f.startsWith('attempted-B-')), "child B's reclaim-attempt outcome");
89
+ // ---- Assertions while A still holds ----
90
+ // A-0 (anti-vacuity): the reclaim path actually executed.
91
+ expect(existsSync(path.join(barrierDir, 'attempted-A-reclaimed'))).toBe(true);
92
+ // A-1: the delayed contender did not enter concurrently.
93
+ expect(existsSync(path.join(barrierDir, 'entered-B'))).toBe(false);
94
+ // A-2: the lock is A's LIVE claim -- not removed/replaced by B.
95
+ const duringClaim = JSON.parse(readFileSync(lockPath, 'utf-8'));
96
+ expect(duringClaim.v).toBe(1);
97
+ expect(duringClaim.host).toBe(hostname());
98
+ expect(duringClaim.token).not.toBe('stale-token-0000');
99
+ expect(readFileSync(lockPath).equals(staleBytes)).toBe(false);
100
+ // A-3: B's refusal came from the authoritative re-read, not luck.
101
+ expect(existsSync(path.join(barrierDir, 'attempted-B-not-stale'))).toBe(true);
102
+ // A-4: both reclaim-lock acquisitions released.
103
+ expect(existsSync(reclaimPath)).toBe(false);
104
+ // ---- Liveness: the refusal is a refusal, not a deadlock ----
105
+ writeFileSync(path.join(barrierDir, 'release-A'), '');
106
+ await waitForAsync(() => existsSync(path.join(barrierDir, 'entered-B')), "child B's entered-B marker");
107
+ const [resultA, resultB] = await Promise.all([pendingA, pendingB]);
108
+ expect(resultA.code, `child A stderr:\n${resultA.stderr}`).toBe(0);
109
+ expect(resultB.code, `child B stderr:\n${resultB.stderr}`).toBe(0);
110
+ expect(existsSync(lockPath)).toBe(false);
111
+ expect(existsSync(reclaimPath)).toBe(false);
112
+ expect(readdirSync(dir).some((f) => f.endsWith('.tmp'))).toBe(false);
113
+ rmSync(dir, { recursive: true, force: true });
114
+ }, 20_000);
115
+ it('negative control: removing the re-validation lets the delayed contender steal the live lock (proves the test discriminates)', async () => {
116
+ const { dir, barrierDir } = makeTempDir('owned-lock-race-negctrl');
117
+ const target = path.join(dir, 'store.json');
118
+ const deadPid = mintDeadPid();
119
+ seedStaleLock(`${target}.lock`, deadPid);
120
+ const pendingA = spawnChild([
121
+ 'A',
122
+ target,
123
+ barrierDir,
124
+ '--signal-held=held-A',
125
+ '--wait-release=release-A',
126
+ '--unsafe-skip-revalidation',
127
+ ]);
128
+ const pendingB = spawnChild([
129
+ 'B',
130
+ target,
131
+ barrierDir,
132
+ '--gate-on=held-A',
133
+ '--unsafe-skip-revalidation',
134
+ ]);
135
+ await waitForAsync(() => existsSync(path.join(barrierDir, 'held-A')), "child A's held-A marker");
136
+ // Under the unsafe build, B does not wait for a genuine refusal -- it
137
+ // proceeds straight to an unconditional unlink once it holds the
138
+ // reclaim lock, so it reports 'reclaimed' too (never 'not-stale').
139
+ await waitForAsync(() => readdirSync(barrierDir).some((f) => f.startsWith('attempted-B-')), "child B's reclaim-attempt outcome");
140
+ const reclaimedCount = readdirSync(barrierDir).filter((f) => /^attempted-[AB]-reclaimed$/.test(f)).length;
141
+ // THIS is the failure the safe build's A-2/A-3 assertions exist to
142
+ // catch: with the authoritative re-read removed, BOTH children believe
143
+ // they reclaimed the stale lock -- the exact round-3 lock-theft race.
144
+ expect(reclaimedCount).toBe(2);
145
+ expect(existsSync(path.join(barrierDir, 'attempted-B-not-stale'))).toBe(false);
146
+ writeFileSync(path.join(barrierDir, 'release-A'), '');
147
+ const [resultA, resultB] = await Promise.all([pendingA, pendingB]);
148
+ // Best-effort cleanup -- one of the two children's release() may warn
149
+ // (ownership already stolen) but must not hang the suite.
150
+ void resultA;
151
+ void resultB;
152
+ rmSync(dir, { recursive: true, force: true });
153
+ }, 20_000);
154
+ it('symmetric stress (50 iterations): exactly one reclaimed outcome per iteration, never two', async () => {
155
+ const ITERATIONS = 50;
156
+ for (let i = 0; i < ITERATIONS; i++) {
157
+ const { dir, barrierDir } = makeTempDir(`owned-lock-race-stress-${i}`);
158
+ const target = path.join(dir, 'store.json');
159
+ const deadPid = mintDeadPid();
160
+ seedStaleLock(`${target}.lock`, deadPid);
161
+ const pending1 = spawnChild(['1', target, barrierDir]);
162
+ const pending2 = spawnChild(['2', target, barrierDir]);
163
+ const [result1, result2] = await Promise.all([pending1, pending2]);
164
+ expect(result1.code, `child 1 stderr:\n${result1.stderr}`).toBe(0);
165
+ expect(result2.code, `child 2 stderr:\n${result2.stderr}`).toBe(0);
166
+ const reclaimedCount = readdirSync(barrierDir).filter((f) => /^attempted-[12]-reclaimed$/.test(f)).length;
167
+ expect(reclaimedCount, `iteration ${i}: expected exactly one reclaimed outcome`).toBe(1);
168
+ // Never a `gone` for a participant that ALSO shows `reclaimed` (would
169
+ // mean two successful reclaims were both recorded for one lock).
170
+ const goneAndReclaimed = readIfExists(path.join(barrierDir, 'attempted-1-gone')) !== null &&
171
+ readIfExists(path.join(barrierDir, 'attempted-1-reclaimed')) !== null;
172
+ expect(goneAndReclaimed).toBe(false);
173
+ rmSync(dir, { recursive: true, force: true });
174
+ }
175
+ }, 120_000);
176
+ });
177
+ //# sourceMappingURL=owned-lock-reclaim-race.test.js.map