@viloforge/vfkb 0.4.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/broadcast.js CHANGED
@@ -8,9 +8,9 @@ import { execFileSync } from 'node:child_process';
8
8
  import { existsSync, readFileSync } from 'node:fs';
9
9
  import { basename, join, resolve } from 'node:path';
10
10
  import { addEntry } from './engine.js';
11
- import { writeManifest } from './manifest.js';
11
+ import { manifestNeedsStamp, writeManifest } from './manifest.js';
12
12
  import { defaultProject } from './storage.js';
13
- import { SCHEMA_VERSION } from './version.js';
13
+ import { ENGINE_COMMIT, SCHEMA_VERSION } from './version.js';
14
14
  /** The resident-pin tags a broadcast record must never carry (ADR-0063 §1). */
15
15
  const FORBIDDEN_TAGS = new Set(['handoff', 'next']);
16
16
  function targetBrainDir(target) {
@@ -76,7 +76,14 @@ export function broadcast(text, targets, opts = {}) {
76
76
  const repoDir = resolve(brain, '..');
77
77
  const manifestPath = join(brain, 'manifest.json');
78
78
  let healed = false;
79
- if (!existsSync(manifestPath)) {
79
+ let upgraded = false;
80
+ // #212 — the heal used to fire ONLY on absence, so a brain stamped by a
81
+ // dist-path build (engine_commit "dev") stayed unknown forever: nothing in
82
+ // any routine path ever re-stamped it, and doctor reported it against every
83
+ // real-sha engine. A running engine that knows its own commit is a genuine
84
+ // provenance upgrade for a brain that does not — unknown -> known.
85
+ const needsStamp = manifestNeedsStamp(brain, opts.engineCommit ?? ENGINE_COMMIT);
86
+ if (needsStamp) {
80
87
  // §3's rule is "never bootstrap a WIRE-LESS brain". A brain with a live
81
88
  // entries.jsonl is wired — the manifest is just missing because only
82
89
  // `vfkb init` ever writes it (plugin-born brains, vfkb#193). Heal it
@@ -86,16 +93,20 @@ export function broadcast(text, targets, opts = {}) {
86
93
  results.push({ target, ok: false, reason: `no brain (no entries.jsonl in ${brain}) — never bootstrap a wire-less brain (ADR-0063 §3)` });
87
94
  continue;
88
95
  }
96
+ const absent = !existsSync(manifestPath);
89
97
  try {
90
- writeManifest(brain);
98
+ writeManifest(brain, { engineCommit: opts.engineCommit, engineVersion: opts.engineVersion });
91
99
  }
92
100
  catch (err) {
93
101
  // Per-target and loud, never a thrown abort — a heal failure on one
94
102
  // target must not swallow the rest of a partial broadcast.
95
- results.push({ target, ok: false, reason: `manifest heal failed: ${err.message}` });
103
+ results.push({ target, ok: false, reason: `manifest ${absent ? 'heal' : 'provenance upgrade'} failed: ${err.message}` });
96
104
  continue;
97
105
  }
98
- healed = true;
106
+ if (absent)
107
+ healed = true;
108
+ else
109
+ upgraded = true;
99
110
  }
100
111
  let schema;
101
112
  try {
@@ -116,12 +127,12 @@ export function broadcast(text, targets, opts = {}) {
116
127
  process.env.VFKB_DATA_DIR = brain;
117
128
  const e = addEntry('fact', stamped, { role: 'executor', tags: [...new Set(['cross-repo', ...extraTags])] });
118
129
  written.add(brain);
119
- results.push({ target, ok: true, id: e.id, posture: gitPosture(repoDir), ...(healed ? { healed } : {}) });
130
+ results.push({ target, ok: true, id: e.id, posture: gitPosture(repoDir), ...(healed ? { healed } : {}), ...(upgraded ? { upgraded } : {}) });
120
131
  }
121
132
  catch (err) {
122
133
  // healed still reported on failure — the manifest stamp happened even
123
134
  // though the record did not land (heal is never silent).
124
- results.push({ target, ok: false, reason: err.message, ...(healed ? { healed } : {}) });
135
+ results.push({ target, ok: false, reason: err.message, ...(healed ? { healed } : {}), ...(upgraded ? { upgraded } : {}) });
125
136
  }
126
137
  finally {
127
138
  if (prev === undefined)
@@ -30954,6 +30954,9 @@ var StdioServerTransport = class {
30954
30954
 
30955
30955
  // src/engine.ts
30956
30956
  import { randomBytes as randomBytes2 } from "node:crypto";
30957
+ import { existsSync as existsSync4 } from "node:fs";
30958
+ import { dirname as dirname3, join as join6, resolve as resolvePath } from "node:path";
30959
+ import { fileURLToPath } from "node:url";
30957
30960
 
30958
30961
  // src/storage.ts
30959
30962
  import { basename, dirname as dirname2, join as join4, resolve } from "node:path";
@@ -31775,6 +31778,16 @@ function latestCrossRepo(all = readAll(), today = nowIso().slice(0, 10), superse
31775
31778
  }
31776
31779
  return latest;
31777
31780
  }
31781
+ var CLI_FACES = ["vfkb.mjs", "cli.js", "cli.mjs"];
31782
+ function resolveCliFace(engineDir) {
31783
+ return CLI_FACES.map((f) => join6(engineDir, f)).find((p) => existsSync4(p));
31784
+ }
31785
+ function renderCaptureFallback(engineDir = dirname3(fileURLToPath(import.meta.url))) {
31786
+ const cli = resolveCliFace(engineDir);
31787
+ const target = cli ? `node "${cli}"` : `node <vfkb CLI not found beside the engine at ${engineDir} \u2014 locate vfkb.mjs or cli.js>`;
31788
+ return `capture fallback: if the kb_* tools error or disappear, the CLI still writes (same engine, separate process; journaled per ADR-0064) \u2014
31789
+ VFKB_DATA_DIR="${resolvePath(brainDir())}" ${target} add <type> "\u2026" [--tags a,b] [--why "\u2026"]`;
31790
+ }
31778
31791
  function renderContextBundle(project = defaultProject(), budget = SESSION_BUDGET_CHARS) {
31779
31792
  const all = readAll();
31780
31793
  const today = nowIso().slice(0, 10);
@@ -31813,6 +31826,7 @@ function renderContextBundle(project = defaultProject(), budget = SESSION_BUDGET
31813
31826
  `;
31814
31827
  }
31815
31828
  body += renderContextMap() + "\n\n";
31829
+ body += renderCaptureFallback() + "\n\n";
31816
31830
  const kept = [];
31817
31831
  let keptLen = 0;
31818
31832
  let dropped = 0;
@@ -32007,7 +32021,7 @@ function queryExplained(opts = {}) {
32007
32021
  }
32008
32022
 
32009
32023
  // src/version.ts
32010
- var ENGINE_VERSION = true ? "0.4.1" : ownPackageVersion();
32024
+ var ENGINE_VERSION = true ? "0.6.0" : ownPackageVersion();
32011
32025
 
32012
32026
  // src/mcp-server.ts
32013
32027
  var SEARCH_DEFAULT_LIMIT = 25;