instar 0.28.69 → 0.28.70

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instar",
3
- "version": "0.28.69",
3
+ "version": "0.28.70",
4
4
  "description": "Persistent autonomy infrastructure for AI agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -24,7 +24,7 @@
24
24
  * upgrade naturally invalidates stale state.
25
25
  */
26
26
 
27
- const { execSync } = require('child_process');
27
+ const { execSync, execFileSync } = require('child_process');
28
28
  const fs = require('fs');
29
29
  const os = require('os');
30
30
  const path = require('path');
@@ -47,11 +47,58 @@ function getBetterSqliteVersion(pkgDir) {
47
47
  return pkg.version;
48
48
  }
49
49
 
50
- /** Spawn a fresh node process to avoid any require-cache contamination. */
50
+ /**
51
+ * Spawn a fresh node process to avoid any require-cache contamination.
52
+ *
53
+ * MUST use process.execPath (the Node running THIS script), not `node` from
54
+ * PATH. Rationale: when the script is invoked by the instar server via
55
+ * execFileSync(process.execPath, [fixScript], ...) but PATH has a different
56
+ * Node first (e.g. asdf Node 22 vs server's bundled Node 25), a bare `node`
57
+ * invocation tests the binary against the wrong ABI. A binary built for the
58
+ * server's Node 25 then "fails" under Node 22's testBinary, flipping the
59
+ * script into source-build; source-build via `npm rebuild` inherits PATH and
60
+ * compiles against Node 22's headers; testBinary (still Node 22) passes the
61
+ * ABI-127 output; the script reports success. Server then loads the binary
62
+ * under Node 25 and gets a NODE_MODULE_VERSION mismatch — the exact silent
63
+ * degradation we found on Inspec 2026-04-21.
64
+ *
65
+ * Defence in depth: before testing the binary, we also verify that the
66
+ * child spawned via process.execPath reports the SAME MODULE_VERSION as the
67
+ * in-process one. Divergence (e.g., symlink-behind-execPath was replaced
68
+ * mid-session by an OS update) means we'd build for a target that doesn't
69
+ * match what the caller actually needs — bail fast rather than produce
70
+ * another silently-wrong binary.
71
+ */
72
+ function verifyChildAbiMatches() {
73
+ try {
74
+ const out = execFileSync(
75
+ process.execPath,
76
+ ['-e', "process.stdout.write(process.versions.modules)"],
77
+ { stdio: ['ignore', 'pipe', 'pipe'], timeout: 5000 }
78
+ );
79
+ const childAbi = String(out).trim();
80
+ if (childAbi !== String(MODULE_VERSION)) {
81
+ console.warn(
82
+ `[fix-better-sqlite3] ABI mismatch between in-process (${MODULE_VERSION}) and child via execPath (${childAbi}). ` +
83
+ `Refusing to build — execPath may have been upgraded out from under this process.`
84
+ );
85
+ return false;
86
+ }
87
+ return true;
88
+ } catch (err) {
89
+ console.warn(`[fix-better-sqlite3] child ABI probe failed: ${err.message}`);
90
+ return false;
91
+ }
92
+ }
93
+
51
94
  function testBinary(pkgDir) {
52
95
  try {
53
- execSync(
54
- `node -e "const Database = require('better-sqlite3'); const db = new Database(':memory:'); db.pragma('journal_mode = WAL'); db.close();"`,
96
+ execFileSync(
97
+ process.execPath,
98
+ [
99
+ '-e',
100
+ "const Database = require('better-sqlite3'); const db = new Database(':memory:'); db.pragma('journal_mode = WAL'); db.close();",
101
+ ],
55
102
  { stdio: 'pipe', timeout: 10000, cwd: pkgDir }
56
103
  );
57
104
  return true;
@@ -163,15 +210,28 @@ function trySourceBuild(pkgDir) {
163
210
  if (nmIdx >= 0) {
164
211
  rebuildCwd = pkgDir.slice(0, nmIdx);
165
212
  }
213
+ // Prepend this script's node binary dir to PATH so any child that shells
214
+ // out to bare `node` (node-gyp internals, lifecycle scripts) picks the Node
215
+ // we're building FOR. Without this, a mixed environment (e.g. asdf Node 22
216
+ // on PATH + server's Node 25 as execPath) compiles against the wrong
217
+ // headers and produces an ABI-mismatched binary that silently fails later.
218
+ const execDir = path.dirname(process.execPath);
219
+ const childPath = `${execDir}${path.delimiter}${process.env.PATH || ''}`;
220
+
166
221
  try {
167
222
  console.log(`[fix-better-sqlite3] Source-building better-sqlite3 in ${rebuildCwd} (~30s)`);
168
- execSync(
169
- `"${process.execPath}" "${npmCli}" rebuild better-sqlite3 --build-from-source`,
223
+ execFileSync(
224
+ process.execPath,
225
+ [npmCli, 'rebuild', 'better-sqlite3', '--build-from-source'],
170
226
  {
171
227
  cwd: rebuildCwd,
172
228
  stdio: 'pipe',
173
229
  timeout: 180_000, // 3 min — source builds on older hardware are slow
174
- env: { ...process.env, npm_config_build_from_source: 'true' },
230
+ env: {
231
+ ...process.env,
232
+ PATH: childPath,
233
+ npm_config_build_from_source: 'true',
234
+ },
175
235
  }
176
236
  );
177
237
  return true;
@@ -199,6 +259,18 @@ function main() {
199
259
  return 0;
200
260
  }
201
261
 
262
+ // Defence in depth: confirm the child spawned via process.execPath has the
263
+ // SAME MODULE_VERSION as this process. If not, we can't safely build (the
264
+ // target is ambiguous).
265
+ if (!verifyChildAbiMatches()) {
266
+ console.error(
267
+ '[fix-better-sqlite3] Cannot proceed: execPath child ABI does not match in-process ABI. ' +
268
+ 'This usually means the Node binary behind process.execPath was replaced while the server was running. ' +
269
+ 'Restart the server under the current Node before retrying.'
270
+ );
271
+ return 1;
272
+ }
273
+
202
274
  const existing = readState(pkgDir);
203
275
  const currentKey = tupleKey(betterSqliteVersion);
204
276
 
@@ -263,4 +335,5 @@ module.exports = {
263
335
  testBinary,
264
336
  findBetterSqlite3,
265
337
  findNpmCli,
338
+ verifyChildAbiMatches,
266
339
  };
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "./builtin-manifest.schema.json",
3
3
  "schemaVersion": 1,
4
- "generatedAt": "2026-04-21T16:38:50.435Z",
5
- "instarVersion": "0.28.69",
4
+ "generatedAt": "2026-04-21T21:40:32.437Z",
5
+ "instarVersion": "0.28.70",
6
6
  "entryCount": 186,
7
7
  "entries": {
8
8
  "hook:session-start": {
@@ -343,7 +343,7 @@
343
343
  "id": "skill:autonomous",
344
344
  "type": "skill",
345
345
  "domain": "skills",
346
- "sourcePath": ".claude/skills/autonomous/skill.md",
346
+ "sourcePath": ".claude/skills/autonomous/SKILL.md",
347
347
  "contentHash": "9bc7dc9eddd4e345ed6564eff850898b66358ce309119c3047e6af275cf9070d",
348
348
  "since": "2025-01-01"
349
349
  },
@@ -351,7 +351,7 @@
351
351
  "id": "skill:build",
352
352
  "type": "skill",
353
353
  "domain": "skills",
354
- "sourcePath": ".claude/skills/build/skill.md",
354
+ "sourcePath": ".claude/skills/build/SKILL.md",
355
355
  "contentHash": "5b3557fa3662cf0c7b7bab85a252cc2e567b3fc64a04a92d430374ef1f264ca6",
356
356
  "since": "2025-01-01"
357
357
  },
@@ -359,7 +359,7 @@
359
359
  "id": "skill:secret-setup",
360
360
  "type": "skill",
361
361
  "domain": "skills",
362
- "sourcePath": ".claude/skills/secret-setup/skill.md",
362
+ "sourcePath": ".claude/skills/secret-setup/SKILL.md",
363
363
  "contentHash": "0f4365713d96c98576d19c818701abe96329f3652ed0d8d76ec4e4a1b46dac56",
364
364
  "since": "2025-01-01"
365
365
  },
@@ -367,7 +367,7 @@
367
367
  "id": "skill:setup-wizard",
368
368
  "type": "skill",
369
369
  "domain": "skills",
370
- "sourcePath": ".claude/skills/setup-wizard/skill.md",
370
+ "sourcePath": ".claude/skills/setup-wizard/SKILL.md",
371
371
  "contentHash": "813fd9164514fd11d1bdc67f4dbee02a74679b3ca46bf1b39893c62deb2e58cb",
372
372
  "since": "2025-01-01"
373
373
  },
@@ -0,0 +1,45 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- bump: patch -->
4
+ <!-- Valid values: patch, minor, major -->
5
+ <!-- patch = bug fixes, refactors, test additions, doc updates -->
6
+ <!-- minor = new features, new APIs, new capabilities (backwards-compatible) -->
7
+ <!-- major = breaking changes to existing APIs or behavior -->
8
+
9
+ ## What Changed
10
+
11
+ The better-sqlite3 self-heal script (`scripts/fix-better-sqlite3.cjs`, invoked by `ensureSqliteBindings()` at server startup when a native-binding mismatch is detected) now anchors its Node resolution to `process.execPath` instead of `node` from `$PATH`. Prior behavior: on machines with a mixed Node environment — asdf / NVM / Homebrew / system-node combinations where `$PATH`'s first `node` differs from the one running the instar server — the script could silently produce and "verify" a binary compiled for the wrong ABI, reporting success while the server's next load fails with NODE_MODULE_VERSION mismatch. This produced three cascading SQLite-subsystem degradations (TopicMemory, SemanticMemory, FeatureRegistry) with no loud-signal alarm — the exact failure mode observed on the Inspec agent on 2026-04-21.
12
+
13
+ Three changes in `scripts/fix-better-sqlite3.cjs`:
14
+
15
+ 1. `testBinary` now uses `execFileSync(process.execPath, ['-e', ...])` instead of a shelled `execSync("node -e ...")` — guarantees the test runs under the same Node that will load the binary.
16
+ 2. `trySourceBuild` prepends `path.dirname(process.execPath)` to the child's PATH and invokes npm via `execFileSync(process.execPath, [npmCli, ...])` — ensures node-gyp compiles against the right Node's headers even when PATH is non-standard.
17
+ 3. New `verifyChildAbiMatches()` defence-in-depth probe — before recovery, confirms the Node behind `process.execPath` reports the same MODULE_VERSION as the in-process one. Catches the narrow case of a symlink-behind-execPath being replaced mid-session.
18
+
19
+ Six new regression tests in `tests/unit/fix-better-sqlite3-state.test.ts` combine source inspection (protect the spawn shape), behavioural exercise (exercise verifyChildAbiMatches), and injection-guard (prevent future interpolation into `-e` payloads).
20
+
21
+ Already-affected agents self-heal on the next server restart after this patch lands — no operator action needed. The in-process detector re-fires on every startup, and the patched script now correctly targets the server's Node. Full technical detail in `docs/specs/fix-better-sqlite3-execpath.md` and side-effects artifact `upgrades/side-effects/fix-better-sqlite3-execpath.md`.
22
+
23
+ ## What to Tell Your User
24
+
25
+ - **Silent SQLite degradation on mixed-Node machines, fixed**: "If you've ever noticed I couldn't remember conversations or search my own memory properly, that was likely this bug. It should heal itself automatically on my next restart."
26
+
27
+ ## Summary of New Capabilities
28
+
29
+ | Capability | How to Use |
30
+ |-----------|-----------|
31
+ | Correct native-binding self-heal on mixed-Node machines | automatic (at server startup) |
32
+
33
+ ## Evidence
34
+
35
+ Reproduction on Inspec 2026-04-21:
36
+ - Server: Node 25.6.1 (ABI 141), bundled in `.instar/shadow-install/.../bin/node`.
37
+ - `$PATH` first `node`: asdf Node 22.18.0 (ABI 127).
38
+ - Observed: three degradations (TopicMemory, SemanticMemory, FeatureRegistry) all citing `NODE_MODULE_VERSION 127 ≠ 141`. better-sqlite3 binary on disk was ABI 127.
39
+ - Root cause: earlier self-heal attempt(s) under the buggy script produced ABI-127 binaries while testing them with asdf Node 22 (false positive → `source-ok` state recorded).
40
+
41
+ Post-fix verification:
42
+ - Running the updated script with `process.execPath = /Users/justin/Documents/Projects/monroe-workspace/.instar/bin/node` (Node 25): output `[fix-better-sqlite3] Prebuild installed and verified.` in one step.
43
+ - `require('better-sqlite3')` under Node 25 returned successfully: `OK ABI 141`.
44
+ - Tests: 14/14 passing in `tests/unit/fix-better-sqlite3-state.test.ts` (8 existing + 6 new).
45
+ - Type check: `tsc --noEmit` clean.
@@ -0,0 +1,135 @@
1
+ # Side-Effects Review — better-sqlite3 self-heal uses `process.execPath` instead of `node` from PATH
2
+
3
+ **Version / slug:** `fix-better-sqlite3-execpath`
4
+ **Date:** `2026-04-21`
5
+ **Author:** `echo`
6
+ **Second-pass reviewer:** `not required`
7
+
8
+ ## Summary of the change
9
+
10
+ `scripts/fix-better-sqlite3.cjs` is the self-heal script the server invokes from `ensureSqliteBindings()` when it detects a native-binding mismatch at startup. The existing implementation spawned verification (`testBinary`) and rebuild (`trySourceBuild`) using a bare `node` lookup from `PATH`. On machines where `PATH`'s `node` differs from the Node running the script (the common case for instar agents on asdf-managed systems: asdf Node 22 first on `PATH`, server's bundled Node 25 as `process.execPath`), this produced a silent ABI mismatch:
11
+
12
+ 1. The prebuild URL is constructed from `process.versions.modules` (ABI 141 on Node 25) — correct.
13
+ 2. The extracted binary is verified by spawning `node -e ...` against `PATH`'s Node (ABI 127 on Node 22) — wrong.
14
+ 3. The ABI-141 binary fails to load under Node 22, so `testBinary` returns false and the script falls through to source-build.
15
+ 4. Source-build runs `npm rebuild better-sqlite3 --build-from-source` via `execSync` with a shelled command that inherits `PATH`. npm lifecycle scripts that shell out to `node` pick up Node 22; node-gyp compiles against Node 22 headers; the output is an ABI-127 binary.
16
+ 5. `testBinary` (still Node 22) successfully loads the ABI-127 binary → returns true. Script records `source-ok` and exits 0.
17
+ 6. Server restarts, loads better-sqlite3 under its actual Node 25 → gets `NODE_MODULE_VERSION 127 ≠ 141` → degrades TopicMemory, SemanticMemory, and FeatureRegistry to fallback mode.
18
+
19
+ This is the exact silent-degradation condition found on the Inspec agent on 2026-04-21: three degradations, healthy-looking self-heal logs, and an ABI-mismatched binary on disk after multiple supervisor restart cycles. Messages arrived at the lifeline but couldn't be forwarded; the user saw unresponsive Telegram with no alarm.
20
+
21
+ **Change:** `testBinary` now spawns `process.execPath` via `execFileSync` with argv array (no shell). `trySourceBuild` also spawns `process.execPath` via `execFileSync` and prepends `path.dirname(process.execPath)` to `PATH` in the child's environment so any internal shell-outs to bare `node` (node-gyp internals, npm lifecycle scripts) resolve to the correct Node.
22
+
23
+ **Files touched:**
24
+ - `scripts/fix-better-sqlite3.cjs` (behavior + shelling hygiene)
25
+ - `tests/unit/fix-better-sqlite3-state.test.ts` (+3 structural regression tests)
26
+
27
+ ## Decision-point inventory
28
+
29
+ This change touches no decision points. The script is a build/install recovery helper; it does not gate information flow, block actions, or filter messages. It detects binding mismatches and attempts to fix them, with a loop-breaker for exhausted tuples (which *is* a brittle blocker but is covered under "safety guards on irreversible actions" in `docs/signal-vs-authority.md` — re-downloading the same broken tarball on every launchd respawn is the irreversible damage being guarded against, and the loop-breaker has no ambiguity: tuple matched = skip).
30
+
31
+ - `fix-better-sqlite3.cjs testBinary Node resolution` — modify — now uses `process.execPath` rather than PATH's `node`; closes a false-positive path where an ABI-wrong binary appears to verify successfully.
32
+ - `fix-better-sqlite3.cjs trySourceBuild Node resolution` — modify — same reasoning, applied to the source-build spawn path.
33
+
34
+ ---
35
+
36
+ ## 1. Over-block
37
+
38
+ **What legitimate inputs does this change reject that it shouldn't?**
39
+
40
+ No block/allow surface — over-block not applicable. The script is a recovery helper; its outputs are "fixed," "can't fix," or "already fine." None of those gate any user-visible flow.
41
+
42
+ ---
43
+
44
+ ## 2. Under-block
45
+
46
+ **What failure modes does this still miss?**
47
+
48
+ No block/allow surface — under-block not applicable in the decision-gate sense. But in the "failure modes the script still can't recover from" sense, worth naming:
49
+
50
+ - `process.execPath` points to a deleted/broken binary. If the parent that spawned the script has an `execPath` pointing to a now-deleted Node install (hypothetical: user removed the Homebrew Cellar version the server was launched against), `testBinary` will `ENOENT` and return false; the script will fall through to source-build which will fail the same way; the loop-breaker will kick in and degrade gracefully. Not a regression.
51
+ - `process.execPath` points to a Node with no npm bundled. The `findNpmCli()` candidate list has fallbacks (homebrew, /usr/local, /usr); if none match, source-build returns false, loop-breaker engages, degraded mode. Not a regression.
52
+ - The Node running the script is somehow different from the Node that will *consume* the library later. Example: script invoked manually by a user under an unusual shell. The script now builds for the Node running it, which is the documented contract; the pre-change contract was "whatever Node is first on PATH" which was wrong by design.
53
+
54
+ ---
55
+
56
+ ## 3. Level-of-abstraction fit
57
+
58
+ **Is this at the right layer?**
59
+
60
+ Yes. The script is a low-level recovery helper invoked by `ensureSqliteBindings()` (a higher-level policy in `src/commands/server.ts`). The policy says "make the native binding work for this process"; the helper implements "fix it or tell me you can't." Using `process.execPath` rather than PATH is the correct shape for a helper — it matches the contract the caller relies on (the caller invoked it *with this specific Node*; that's the Node the binding must work for).
61
+
62
+ No higher-level gate should own this. No lower-level primitive is being reimplemented.
63
+
64
+ ---
65
+
66
+ ## 4. Signal vs authority compliance
67
+
68
+ **Required reference:** [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
69
+
70
+ **Does this change hold blocking authority with brittle logic?**
71
+
72
+ - [x] No — this change has no block/allow surface.
73
+
74
+ The script is not a decision point under the principle. It detects a mismatch and attempts a fix; success/failure is a hard-invariant check (the binary either loads under the correct Node or it doesn't). Per `docs/signal-vs-authority.md` § "When this principle does NOT apply," structural validators at system boundaries are not judgment decisions and are allowed to be brittle.
75
+
76
+ ---
77
+
78
+ ## 5. Interactions
79
+
80
+ **Does this interact with existing checks, recovery paths, or infrastructure?**
81
+
82
+ - **Shadowing:** No. `ensureSqliteBindings()` is the sole upstream caller. The script runs synchronously; its result determines whether the server restarts to clear the ESM module cache.
83
+ - **Double-fire:** No. `ensureSqliteBindings()` is called once during server startup, guarded by `fs.existsSync(fixScript)`.
84
+ - **Races:** None with other instar processes. The script holds no locks; its state file (`.instar-fix-state.json`) is written under the package directory, which is process-private. If two different instar agents share a single node_modules (uncommon; shadow-install isolation exists precisely to avoid this), the existing tuple-keyed state design already handles that — each tuple includes `platform` and `arch`, so no collision possible on shared hardware.
85
+ - **Feedback loops:** None. The loop-breaker prevents the prior-observed "launchd respawn → broken prebuild redownload" pattern; that remains intact.
86
+
87
+ One interaction worth naming: the `UpdateChecker.ts` path that also claims to "rebuild better-sqlite3 native bindings in shadow install" after each update (src/core/UpdateChecker.ts:202). That path uses `npm rebuild better-sqlite3` (not this script) and has the same PATH-inheritance vulnerability. It is out of scope for this PR because the primary self-heal path — the one triggered when the server actually can't load the binding — is what this fix addresses. The UpdateChecker path is a prophylactic rebuild that typically succeeds (it runs during a fresh install, often before the bad-prebuild scenario applies) and a fix to it belongs in a separate PR with its own review.
88
+
89
+ ---
90
+
91
+ ## 6. External surfaces
92
+
93
+ **Does this change anything visible outside the immediate code path?**
94
+
95
+ - Other agents on the same machine: no (script is per-package-dir).
96
+ - Other users of the install base: yes, positively — after this ships, the Inspec-style silent-degradation pattern can no longer silently pass. Other agents running on asdf / NVM / mixed-Node environments were vulnerable to the same bug; this closes it.
97
+ - External systems: no change. Same prebuild URL format, same `npm rebuild` invocation from the same cwd.
98
+ - Persistent state: no new state, no schema change. `.instar-fix-state.json` format unchanged.
99
+ - Timing: end-to-end timing unchanged. `execFileSync` vs `execSync` is the same mechanism at the syscall level; argv-as-array avoids a shell fork but on the critical path this is microseconds.
100
+
101
+ ---
102
+
103
+ ## 7. Rollback cost
104
+
105
+ **If this turns out wrong in production, what's the back-out?**
106
+
107
+ Pure code change in a recovery helper. Rollback is `git revert` + patch release. No persistent state needs cleanup. No user-visible regression during rollback: agents that run the reverted version get back to the pre-fix behavior (which was the status quo for weeks; merely imperfect, not broken). Agents that already ran the fixed script and have a correctly-compiled binary keep working regardless of which version of the script ships next.
108
+
109
+ Estimated rollback: 5 minutes to revert + re-release, no migration, no operator action needed on any agent.
110
+
111
+ ---
112
+
113
+ ## Conclusion
114
+
115
+ Single-file behavior fix with structural regression tests. Root cause was a shell-spawn shape (`node -e` relying on PATH) that silently produced wrong-ABI binaries on mixed-Node machines. New behavior invokes `process.execPath` explicitly, matching the caller's contract. No decision-point surface, no interaction concerns, straightforward rollback. Clear to ship.
116
+
117
+ ---
118
+
119
+ ## Revisions from spec-converge round 1
120
+
121
+ In response to four parallel internal reviewers (security, scalability, adversarial, integration):
122
+
123
+ - **Added `verifyChildAbiMatches()` defence-in-depth probe** (adversarial-HIGH): before attempting recovery, confirm the Node behind `process.execPath` reports the same MODULE_VERSION as the in-process Node. Catches the narrow case where a symlink-behind-execPath was replaced mid-session.
124
+ - **Added explicit trust assumption** on `dirname(process.execPath)` for the PATH prepend (security-low).
125
+ - **Strengthened tests** from 3 structural to 6 (structural + behavioural): positive export canary for `testBinary`, behavioural exercise of `verifyChildAbiMatches`, injection guard asserting the `-e` payload is a string literal (security-low).
126
+ - **Expanded Open Questions** to explicitly name six deferred items with justification: UpdateChecker path has same bug (follow-up), postinstall authority (runtime is authoritative), source-failed TTL (acknowledged tradeoff), concurrent-recovery race (pre-existing, out of scope), prebuild signature verification (pre-existing), tmpfile path predictability (pre-existing), no end-to-end CI (acknowledged).
127
+ - **Added Platform scope** (darwin + linux) and **Caller invariants** (verified: only postinstall + ensureSqliteBindings invoke this script).
128
+ - **Added "Remediation for already-affected agents"** to Rollback: patched release heals on next startup via the in-process detector; no one-shot operator action needed.
129
+
130
+ ## Evidence pointers
131
+
132
+ - Reproduction: on Inspec (2026-04-21), server ran Node 25 (ABI 141), PATH's `node` was asdf Node 22 (ABI 127). Degradations showed NODE_MODULE_VERSION 127 mismatch. After the fix: running the updated script with `process.execPath = Node 25` produced `Prebuild installed and verified.` in one step, and `require('better-sqlite3')` under Node 25 returned `OK ABI 141`.
133
+ - Unit tests: `tests/unit/fix-better-sqlite3-state.test.ts` — 14 passed (8 existing + 6 new regression).
134
+ - Type check: `tsc --noEmit` clean.
135
+ - Caller audit: `grep -rn fix-better-sqlite3 src/ scripts/ package.json` confirms only two call sites (postinstall, ensureSqliteBindings) — no CLI or dashboard path.
package/upgrades/NEXT.md DELETED
@@ -1,53 +0,0 @@
1
- # Upgrade Guide — vNEXT
2
-
3
- <!-- bump: patch -->
4
- <!-- Valid values: patch, minor, major -->
5
- <!-- patch = bug fixes, refactors, test additions, doc updates -->
6
- <!-- minor = new features, new APIs, new capabilities (backwards-compatible) -->
7
- <!-- major = breaking changes to existing APIs or behavior -->
8
-
9
- ## What Changed
10
-
11
- <!-- Describe what changed technically. What new features, APIs, behavioral changes? -->
12
- <!-- Write this for the AGENT — they need to understand the system deeply. -->
13
-
14
- ## What to Tell Your User
15
-
16
- <!-- Write talking points the agent should relay to their user. -->
17
- <!-- This should be warm, conversational, user-facing — not a changelog. -->
18
- <!-- Focus on what THEY can now do, not internal plumbing. -->
19
- <!-- -->
20
- <!-- PROHIBITED in this section (will fail validation): -->
21
- <!-- camelCase config keys: silentReject, maxRetries, telegramNotify -->
22
- <!-- Inline code backtick references like silentReject: false -->
23
- <!-- Fenced code blocks -->
24
- <!-- Instructions to edit files or run commands -->
25
- <!-- -->
26
- <!-- CORRECT style: "I can turn that on for you" not "set X to false" -->
27
- <!-- The agent relays this to their user — keep it human. -->
28
-
29
- - **[Feature name]**: "[Brief, friendly description of what this means for the user]"
30
-
31
- ## Summary of New Capabilities
32
-
33
- | Capability | How to Use |
34
- |-----------|-----------|
35
- | [Capability] | [Endpoint, command, or "automatic"] |
36
-
37
- ## Evidence
38
-
39
- <!-- REQUIRED if this release claims to fix a bug. -->
40
- <!-- Unit tests passing is NOT evidence. Provide ONE of: -->
41
- <!-- (a) Reproduction steps + observed before/after on a live system. -->
42
- <!-- Include log excerpts, observed command output, or behavior -->
43
- <!-- description. Make it specific enough that a future reader can -->
44
- <!-- re-run it and see the same thing. -->
45
- <!-- (b) "Not reproducible in dev — [concrete reason]" if the failure -->
46
- <!-- mode truly can't be exercised locally (race conditions, -->
47
- <!-- event-driven paths requiring external signals, etc). -->
48
- <!-- -->
49
- <!-- If this release doesn't claim a bug fix (pure feature / refactor), -->
50
- <!-- leave this section blank or delete it — it's only enforced when -->
51
- <!-- "What Changed" describes a fix. -->
52
-
53
- [Describe reproduction + verified fix, OR "Not reproducible in dev — [concrete reason]"]