@holmes-lab/holmes-kit 0.19.2 → 0.19.3

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.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,46 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
  <!-- @implements A-SPEC-209 -->
8
+ ## [0.19.3] - 2026-09-10
9
+
10
+ `doctor` told every third-party install its SQLite binding was missing while it sat on disk. No API change.
11
+
12
+ ### Fixed
13
+ - **A false FAIL on the default install layout** (REQ-583). `gatherNativeEvidence` located
14
+ `better-sqlite3` by joining `packageRoot/node_modules/better-sqlite3`, and npm HOISTS
15
+ dependencies to the project root — so for a normal install at
16
+ `<proj>/node_modules/@holmes-lab/holmes-kit` the joined path never existed. Measured against the
17
+ published 0.19.2: the binding was present, `require('better-sqlite3')` executed a `:memory:`
18
+ query, and `doctor` still reported `no binary`. Hoisting is npm's default, which made this the
19
+ default experience of a correct install. The package directory is now RESOLVED
20
+ (`require.resolve('<name>/package.json', { paths: [root] })`) — Node's own rule, so hoisted,
21
+ nested and linked layouts all answer correctly, and a genuinely absent dependency still fails.
22
+ Its sibling — the `node version` check reading `engines.node` — carried the same join and moved
23
+ with it; it had been reporting `engines.node=(unknown)` for the same reason.
24
+ - **The release gate ran the suite in the operator's environment** (REQ-582). `npm publish`
25
+ refused a green tree: `verify-release.js` called `npm test` with the shell's `HOLMES_APPROVAL`,
26
+ which the spawned-hook parity test (A-SPEC-338) legitimately reacts to — the spawned child
27
+ inherits the token while its in-process twin is handed `{approval: undefined}`. The gate now runs
28
+ under `cleanTestEnv`, the same scrub `test_run` uses (borrowed, never copied), and refuses to
29
+ proceed unscrubbed if it cannot load it. It also names the failing tests instead of saying only
30
+ "red" — finding out which test had failed previously cost two extra full-suite runs.
31
+
32
+ - **A live session could block its own publish** (REQ-583). The `APPEND_ONLY_GOVERNANCE` list that
33
+ tells the release gate which dirty files are governance churn rather than unshipped code held
34
+ only the approval queue and provenance — while REQ-564/566/569/578 added `session-context`,
35
+ `impact-advisories`, `anchor-density` and `cycle-observations`, and `test-outcomes` was promoted
36
+ to tracking. Measured: `npm publish` refused because one `session-context` line was appended
37
+ between two `git status` snapshots inside the tree-untouched assertion, and the same list guards
38
+ the dirty-tree check a user meets first. The list now follows the ledgers, and a test pins that
39
+ nothing which ships (`src/`, `dist/`, the manifest, specs) is ever excused.
40
+
41
+ ### Verified against the published package
42
+ - Installed `@holmes-lab/holmes-kit@0.19.2` from the registry into a clean project, applied this
43
+ fix, and re-diagnosed from an unrelated working directory: **0 FAIL**, three-harness handshakes
44
+ (claude / codex / antigravity) all PASS on the version-pinned npx wiring, `target wiring — 3
45
+ holmes hook(s) resolve to this install`, and `better-sqlite3 — loads and executes against
46
+ :memory:`.
47
+
8
48
  ## [0.19.2] - 2026-09-10
9
49
 
10
50
  Wiring diagnosis no longer depends on where you run it. No API change.
package/dist/.build-id CHANGED
@@ -1 +1 @@
1
- e63e589-mtuemhhb
1
+ fb773a8-mtug045o
@@ -104,6 +104,21 @@ export declare function grammarProbe(packageRoot: string): {
104
104
  * local dependency. A global or npx install has no project package.json that npm consults, so its
105
105
  * coverage is `uncovered` by construction and the remedy is the per-command `--allow-scripts`.
106
106
  */
107
+ /**
108
+ * Where an installed dependency actually lives, asked of NODE rather than guessed from a path.
109
+ *
110
+ * 0.19.2 shipped a false FAIL to every third-party consumer: it built the directory by joining
111
+ * `packageRoot/node_modules/<name>`, and npm HOISTS dependencies to the project root, so for an
112
+ * install at `<proj>/node_modules/@holmes-lab/holmes-kit` the joined path never existed — while the
113
+ * binding sat at `<proj>/node_modules/better-sqlite3` and loaded fine. Hoisting is npm's default,
114
+ * which made the false verdict the default path of a normal install.
115
+ *
116
+ * `package.json` is the resolution target because we want the DIRECTORY, not the entry point: a
117
+ * package whose `exports` hides its main still resolves its own manifest by convention. A failure
118
+ * to resolve returns null, and the caller reports "absent" exactly as before — the repair changes
119
+ * how we look, never whether a real miss is reported.
120
+ */
121
+ export declare function resolveDepDir(fromRoot: string, name: string): string | null;
107
122
  export declare function gatherNativeEvidence(packageRoot: string): NativeEvidence;
108
123
  /** The parent environment minus the variables that legitimately change a gate decision. Pure. */
109
124
  export declare function probeEnv(parent: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
@@ -38,6 +38,7 @@ exports.globalInstallDir = globalInstallDir;
38
38
  exports.npmCliEntry = npmCliEntry;
39
39
  exports.prefixVerdict = prefixVerdict;
40
40
  exports.grammarProbe = grammarProbe;
41
+ exports.resolveDepDir = resolveDepDir;
41
42
  exports.gatherNativeEvidence = gatherNativeEvidence;
42
43
  exports.probeEnv = probeEnv;
43
44
  exports.resolveWiringPath = resolveWiringPath;
@@ -241,13 +242,39 @@ function observe(file, args) {
241
242
  * local dependency. A global or npx install has no project package.json that npm consults, so its
242
243
  * coverage is `uncovered` by construction and the remedy is the per-command `--allow-scripts`.
243
244
  */
245
+ // @implements A-SPEC-583.1
246
+ /**
247
+ * Where an installed dependency actually lives, asked of NODE rather than guessed from a path.
248
+ *
249
+ * 0.19.2 shipped a false FAIL to every third-party consumer: it built the directory by joining
250
+ * `packageRoot/node_modules/<name>`, and npm HOISTS dependencies to the project root, so for an
251
+ * install at `<proj>/node_modules/@holmes-lab/holmes-kit` the joined path never existed — while the
252
+ * binding sat at `<proj>/node_modules/better-sqlite3` and loaded fine. Hoisting is npm's default,
253
+ * which made the false verdict the default path of a normal install.
254
+ *
255
+ * `package.json` is the resolution target because we want the DIRECTORY, not the entry point: a
256
+ * package whose `exports` hides its main still resolves its own manifest by convention. A failure
257
+ * to resolve returns null, and the caller reports "absent" exactly as before — the repair changes
258
+ * how we look, never whether a real miss is reported.
259
+ */
260
+ function resolveDepDir(fromRoot, name) {
261
+ if (name === '')
262
+ return null;
263
+ try {
264
+ return path.dirname(require.resolve(`${name}/package.json`, { paths: [fromRoot] }));
265
+ }
266
+ catch {
267
+ return null;
268
+ }
269
+ }
244
270
  function gatherNativeEvidence(packageRoot) {
245
271
  const name = 'better-sqlite3';
246
- const pkgDir = path.join(packageRoot, 'node_modules', name);
247
- const bindingPresent = fs.existsSync(path.join(pkgDir, 'build', 'Release', 'better_sqlite3.node'));
272
+ // @implements A-SPEC-583.1 — resolved, not joined. A hoisted install is the npm default.
273
+ const pkgDir = resolveDepDir(packageRoot, name);
274
+ const bindingPresent = pkgDir !== null && fs.existsSync(path.join(pkgDir, 'build', 'Release', 'better_sqlite3.node'));
248
275
  let packageVersion = '(unknown)';
249
276
  try {
250
- packageVersion = JSON.parse(fs.readFileSync(path.join(pkgDir, 'package.json'), 'utf8')).version ?? packageVersion;
277
+ packageVersion = JSON.parse(fs.readFileSync(path.join(pkgDir ?? '', 'package.json'), 'utf8')).version ?? packageVersion;
251
278
  }
252
279
  catch { /* not installed */ }
253
280
  let loadError;
@@ -354,7 +381,8 @@ async function runDoctor(packageRoot, target, opts, extraChecks) {
354
381
  // 2. Node version vs better-sqlite3's declared range (WARN only — a mismatch still often works).
355
382
  let sqliteRange = '(unknown)';
356
383
  try {
357
- sqliteRange = JSON.parse(fs.readFileSync(path.join(packageRoot, 'node_modules', 'better-sqlite3', 'package.json'), 'utf8')).engines?.node ?? '(none)';
384
+ // @implements A-SPEC-583.1 the SIBLING of the evidence lookup; both move together.
385
+ sqliteRange = JSON.parse(fs.readFileSync(path.join(resolveDepDir(packageRoot, 'better-sqlite3') ?? '', 'package.json'), 'utf8')).engines?.node ?? '(none)';
358
386
  }
359
387
  catch { /* not resolvable from here */ }
360
388
  add('node version', 'PASS', `node ${process.version}; better-sqlite3 declares engines.node=${sqliteRange}`, 'If a native module fails to load after a Node major upgrade, reinstall holmes-kit.');
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "//": "@implements A-SPEC-209",
3
3
  "name": "@holmes-lab/holmes-kit",
4
- "version": "0.19.2",
4
+ "version": "0.19.3",
5
5
  "description": "Holmes-Kit — deterministic Agentic Software Engineering (ASE) harness with causal traceability (spec chain + D-CPG + RTM + phase guardrail)",
6
6
  "main": "dist/holmes/mcp/server.js",
7
7
  "types": "dist/holmes/mcp/server.d.ts",