mindforge-cc 10.7.0 → 11.2.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.
Files changed (85) hide show
  1. package/.agent/hooks/mindforge-statusline.js +2 -2
  2. package/.mindforge/MINDFORGE-V2-SCHEMA.json +43 -10
  3. package/.mindforge/config.json +18 -4
  4. package/CHANGELOG.md +165 -0
  5. package/MINDFORGE.md +3 -3
  6. package/README.md +49 -4
  7. package/RELEASENOTES.md +81 -1
  8. package/SECURITY.md +20 -8
  9. package/bin/autonomous/audit-writer.js +105 -70
  10. package/bin/autonomous/auto-runner.js +377 -34
  11. package/bin/autonomous/context-refactorer.js +26 -11
  12. package/bin/autonomous/dependency-dag.js +59 -0
  13. package/bin/autonomous/state-manager.js +62 -6
  14. package/bin/autonomous/stuck-monitor.js +46 -7
  15. package/bin/autonomous/wave-executor.js +86 -26
  16. package/bin/council-cli.js +161 -0
  17. package/bin/dashboard/api-router.js +43 -0
  18. package/bin/dashboard/approval-handler.js +3 -1
  19. package/bin/dashboard/metrics-aggregator.js +28 -1
  20. package/bin/dashboard/server.js +68 -5
  21. package/bin/dashboard/sse-bridge.js +10 -13
  22. package/bin/engine/council-runtime.js +124 -0
  23. package/bin/engine/feedback-loop.js +8 -0
  24. package/bin/engine/intelligence-interlock.js +32 -15
  25. package/bin/engine/logic-drift-detector.js +2 -1
  26. package/bin/engine/nexus-tracer.js +3 -2
  27. package/bin/engine/otel-exporter.js +123 -0
  28. package/bin/engine/remediation-engine.js +155 -32
  29. package/bin/engine/self-corrective-synthesizer.js +84 -10
  30. package/bin/engine/sre-manager.js +12 -4
  31. package/bin/engine/temporal-cli.js +4 -2
  32. package/bin/engine/temporal-hub.js +131 -34
  33. package/bin/engine/verification-runner.js +131 -0
  34. package/bin/engine/verify-cli.js +34 -0
  35. package/bin/eval/eval-harness.js +82 -0
  36. package/bin/eval/golden-set-retrieval.json +46 -0
  37. package/bin/governance/approve.js +41 -5
  38. package/bin/governance/audit-hash.js +12 -0
  39. package/bin/governance/audit-verifier.js +60 -0
  40. package/bin/governance/impact-analyzer.js +28 -0
  41. package/bin/governance/policy-engine.js +10 -3
  42. package/bin/governance/quantum-crypto.js +95 -28
  43. package/bin/governance/rbac-manager.js +74 -2
  44. package/bin/governance/ztai-manager.js +79 -9
  45. package/bin/hindsight-injector.js +8 -9
  46. package/bin/hooks/instinct-capture-hook.js +186 -0
  47. package/bin/memory/auto-shadow.js +32 -3
  48. package/bin/memory/eis-client.js +71 -34
  49. package/bin/memory/embedding-engine.js +61 -0
  50. package/bin/memory/identity-synthesizer.js +2 -2
  51. package/bin/memory/knowledge-graph.js +58 -5
  52. package/bin/memory/knowledge-indexer.js +53 -6
  53. package/bin/memory/knowledge-store.js +52 -6
  54. package/bin/memory/retrieval-fusion.js +58 -0
  55. package/bin/memory/semantic-hub.js +2 -2
  56. package/bin/memory/vector-hub.js +111 -6
  57. package/bin/migrations/10.7.0-to-11.0.0.js +110 -0
  58. package/bin/migrations/schema-versions.js +13 -0
  59. package/bin/mindforge-cli.js +4 -5
  60. package/bin/models/anthropic-provider.js +58 -4
  61. package/bin/models/cloud-broker.js +68 -20
  62. package/bin/models/cost-tracker.js +3 -1
  63. package/bin/models/difficulty-scorer.js +54 -0
  64. package/bin/models/gemini-provider.js +57 -2
  65. package/bin/models/model-client.js +20 -0
  66. package/bin/models/model-router.js +59 -26
  67. package/bin/models/openai-provider.js +50 -3
  68. package/bin/models/pricing-registry.js +128 -0
  69. package/bin/review/ads-engine.js +1 -1
  70. package/bin/security/trust-boundaries.js +102 -0
  71. package/bin/security/trust-gate-hook.js +39 -0
  72. package/bin/skill-registry.js +3 -2
  73. package/bin/skills-builder/marketplace-cli.js +5 -3
  74. package/bin/skills-builder/skill-registrar.js +4 -6
  75. package/bin/sre/sentinel.js +7 -5
  76. package/bin/utils/append-queue.js +55 -0
  77. package/bin/utils/file-io.js +90 -38
  78. package/bin/utils/index.js +58 -0
  79. package/bin/utils/version-check.js +59 -0
  80. package/bin/verify-audit.js +12 -0
  81. package/bin/wizard/theme.js +1 -2
  82. package/docs/getting-started.md +1 -1
  83. package/docs/user-guide.md +2 -2
  84. package/package.json +2 -2
  85. package/bin/dashboard/team-tracker.js +0 -0
@@ -15,6 +15,30 @@ const path = require('path');
15
15
  const os = require('os');
16
16
  const crypto = require('crypto');
17
17
 
18
+ // ── Durable append (UC-09) ──────────────────────────────────────────────────
19
+ // The knowledge-store public API (add/deprecate/reinforce) is SYNCHRONOUS and
20
+ // callers read-after-write synchronously (e.g. `const id = Store.add(...)`
21
+ // immediately followed by `Store.readAll()`). Routing through the async
22
+ // append-queue would make those reads observe stale data and would require an
23
+ // API change across 9+ consumers — out of scope for UC-09.
24
+ //
25
+ // Instead we centralize every append through one durable, fsync'd, synchronous
26
+ // writer. This delivers UC-09's durability guarantee (acknowledged writes are on
27
+ // disk before the call returns) and a single serialized append path per file,
28
+ // while preserving the synchronous read-after-write contract. appendFileSync's
29
+ // per-call append is atomic on POSIX, so concurrent in-process appends do not
30
+ // interleave at the byte level.
31
+ function appendDurableSync(filePath, line) {
32
+ const record = line.endsWith('\n') ? line : line + '\n';
33
+ const fd = fs.openSync(filePath, 'a');
34
+ try {
35
+ fs.writeSync(fd, record);
36
+ fs.fsyncSync(fd);
37
+ } finally {
38
+ fs.closeSync(fd);
39
+ }
40
+ }
41
+
18
42
  // ── ID Index for fast lookups (built lazily, invalidated on writes) ───────────
19
43
  let _idIndex = null; // Map<id, entry> — latest version per ID
20
44
  let _indexDirty = true; // Invalidated whenever entries are appended
@@ -126,6 +150,22 @@ function getFilePath(type) {
126
150
  }
127
151
  }
128
152
 
153
+ // ── File Integrity ────────────────────────────────────────────────────────────
154
+
155
+ /**
156
+ * Ensures a JSONL file doesn't end with a partial/truncated line.
157
+ * Appends a trailing newline if missing — prevents corruption from propagating.
158
+ */
159
+ function verifyFileIntegrity(filePath) {
160
+ if (!fs.existsSync(filePath)) return true;
161
+ const content = fs.readFileSync(filePath, 'utf8');
162
+ if (content.length === 0) return true;
163
+ if (!content.endsWith('\n')) {
164
+ fs.appendFileSync(filePath, '\n');
165
+ }
166
+ return true;
167
+ }
168
+
129
169
  // ── Write operations ──────────────────────────────────────────────────────────
130
170
 
131
171
  /**
@@ -184,11 +224,13 @@ function add(entry) {
184
224
  };
185
225
 
186
226
  const filePath = getFilePath(entry.type);
187
- fs.appendFileSync(filePath, JSON.stringify(full) + '\n');
227
+ verifyFileIntegrity(filePath);
228
+ appendDurableSync(filePath, JSON.stringify(full));
188
229
 
189
230
  // Also append to unified knowledge-base.jsonl for cross-type queries
190
231
  if (filePath !== paths.KB_PATH) {
191
- fs.appendFileSync(paths.KB_PATH, JSON.stringify(full) + '\n');
232
+ verifyFileIntegrity(paths.KB_PATH);
233
+ appendDurableSync(paths.KB_PATH, JSON.stringify(full));
192
234
  }
193
235
 
194
236
  _invalidateIndex();
@@ -217,9 +259,11 @@ function deprecate(id, reason, supersededBy = null) {
217
259
  deprecated_at: new Date().toISOString(),
218
260
  };
219
261
 
220
- fs.appendFileSync(filePath, JSON.stringify(deprecated) + '\n');
262
+ verifyFileIntegrity(filePath);
263
+ appendDurableSync(filePath, JSON.stringify(deprecated));
221
264
  if (filePath !== paths.KB_PATH) {
222
- fs.appendFileSync(paths.KB_PATH, JSON.stringify(deprecated) + '\n');
265
+ verifyFileIntegrity(paths.KB_PATH);
266
+ appendDurableSync(paths.KB_PATH, JSON.stringify(deprecated));
223
267
  }
224
268
 
225
269
  _invalidateIndex();
@@ -246,9 +290,11 @@ function reinforce(id) {
246
290
  };
247
291
 
248
292
  const filePath = getFilePath(entry.type);
249
- fs.appendFileSync(filePath, JSON.stringify(reinforced) + '\n');
293
+ verifyFileIntegrity(filePath);
294
+ appendDurableSync(filePath, JSON.stringify(reinforced));
250
295
  if (filePath !== paths.KB_PATH) {
251
- fs.appendFileSync(paths.KB_PATH, JSON.stringify(reinforced) + '\n');
296
+ verifyFileIntegrity(paths.KB_PATH);
297
+ appendDurableSync(paths.KB_PATH, JSON.stringify(reinforced));
252
298
  }
253
299
 
254
300
  _invalidateIndex();
@@ -0,0 +1,58 @@
1
+ 'use strict';
2
+ /**
3
+ * MindForge — Reciprocal Rank Fusion (UC-20).
4
+ * Merges multiple ranked lists using scale-free RRF scoring.
5
+ *
6
+ * RRF eliminates the need for score normalization across retrieval paths
7
+ * with incomparable scoring functions (embedding similarity, BM25, graph
8
+ * traversal, FTS rank). Only ordinal rank matters, not score magnitude.
9
+ *
10
+ * Formula:
11
+ * rrfScore(item) = SUM( 1 / (K + rank_i) ) for all lists containing the item
12
+ *
13
+ * Where K=60 is the standard constant from the original RRF paper
14
+ * (Cormack, Clarke, Butt — 2009).
15
+ */
16
+
17
+ const K = 60; // Standard RRF constant — dampens the influence of high ranks
18
+
19
+ /**
20
+ * Fuse multiple ranked result lists using Reciprocal Rank Fusion.
21
+ *
22
+ * Each list is an array of objects with at least an `id` field.
23
+ * Items appearing in multiple lists accumulate RRF score and rank higher.
24
+ *
25
+ * @param {Array<Array<{id: string, [key: string]: any}>>} rankedLists
26
+ * Array of ranked lists. Each list is ordered by relevance (index 0 = most relevant).
27
+ * @returns {Array<{id: string, rrfScore: number, [key: string]: any}>}
28
+ * Fused results sorted by RRF score descending. Each item retains its
29
+ * original properties from the first list it appeared in.
30
+ */
31
+ function fuseResults(rankedLists) {
32
+ if (!rankedLists || rankedLists.length === 0) return [];
33
+
34
+ const scores = new Map(); // id -> merged item with rrfScore
35
+
36
+ for (const list of rankedLists) {
37
+ if (!Array.isArray(list)) continue;
38
+
39
+ for (let rank = 0; rank < list.length; rank++) {
40
+ const item = list[rank];
41
+ if (!item || !item.id) continue;
42
+
43
+ const id = item.id;
44
+ const rrfContribution = 1 / (K + rank + 1); // rank is 0-based, +1 makes it 1-based
45
+
46
+ if (scores.has(id)) {
47
+ const existing = scores.get(id);
48
+ existing.rrfScore += rrfContribution;
49
+ } else {
50
+ scores.set(id, { ...item, rrfScore: rrfContribution });
51
+ }
52
+ }
53
+ }
54
+
55
+ return [...scores.values()].sort((a, b) => b.rrfScore - a.rrfScore);
56
+ }
57
+
58
+ module.exports = { fuseResults, K };
@@ -83,7 +83,7 @@ class SemanticHub {
83
83
  try {
84
84
  const data = await fs.readFile(this.syncManifest, 'utf8');
85
85
  manifest = JSON.parse(data);
86
- } catch (e) {}
86
+ } catch (e) { /* intentionally empty */ }
87
87
 
88
88
  manifest[libraryName] = {
89
89
  lastSync: new Date().toISOString(),
@@ -106,7 +106,7 @@ class SemanticHub {
106
106
  sqliteTraces = await vectorHub.searchTraces(skillFilter);
107
107
  } else {
108
108
  sqliteTraces = vectorHub.query(
109
- "SELECT * FROM traces WHERE event = ? LIMIT 20",
109
+ 'SELECT * FROM traces WHERE event = ? LIMIT 20',
110
110
  ['reasoning_trace']
111
111
  );
112
112
  }
@@ -23,6 +23,30 @@ class VectorHub {
23
23
  this.initialized = false;
24
24
  this._writeCount = 0;
25
25
  this._batchSize = 10;
26
+ // UC-09: serialized async persistence chain. Successive save() calls queue
27
+ // behind one another so two exports never write the .db file concurrently
28
+ // (a corrupted half-written database would otherwise be possible).
29
+ this._saveChain = Promise.resolve();
30
+ // Count of async save()s that have been SCHEDULED but not yet COMPLETED their
31
+ // durable disk write. A boolean here is unsafe: with two rapid saves the chain
32
+ // is [writeA → clear → writeB → clear], leaving a window where the flag reads
33
+ // "clean" while writeB is still pending — a hard process.exit() in that window
34
+ // would make the exit guard skip saveSync() and lose the last batch (the exact
35
+ // data loss this guard exists to prevent). A counter has no such gap: it only
36
+ // returns to 0 once EVERY scheduled save has completed. saveSync() always
37
+ // exports the current in-memory DB, so over-flushing on exit is harmless — we
38
+ // deliberately bias toward flushing.
39
+ this._pendingSaves = 0;
40
+ this._exitGuardInstalled = false;
41
+ }
42
+
43
+ _installExitGuard() {
44
+ if (this._exitGuardInstalled) return;
45
+ this._exitGuardInstalled = true;
46
+ // 'exit' handlers can only run synchronous code — saveSync() fits exactly.
47
+ process.once('exit', () => {
48
+ if (this._db && this._pendingSaves > 0) this.saveSync();
49
+ });
26
50
  }
27
51
 
28
52
  _ensureDir() {
@@ -167,22 +191,74 @@ class VectorHub {
167
191
  this._db.run('CREATE UNIQUE INDEX IF NOT EXISTS idx_migrations_name ON _migrations(name)');
168
192
 
169
193
  this.initialized = true;
194
+ this._installExitGuard();
170
195
  this.save();
171
196
  console.log(`[VectorHub] Initialized WASM SQLite persistence at ${this.dbPath}`);
172
197
  }
173
198
 
174
199
  /**
175
- * Persist the in-memory database to disk.
200
+ * Persist the in-memory database to disk (UC-09).
201
+ *
202
+ * sql.js export() is intrinsically synchronous, but the (potentially large)
203
+ * FILE WRITE no longer blocks the event loop: we snapshot the bytes
204
+ * synchronously, then write+fsync them asynchronously. Successive saves are
205
+ * serialized on a single chain so two exports never write the .db file
206
+ * concurrently. The write is crash-safe (tmp file + atomic rename + fsync),
207
+ * so a partial write can never leave a corrupted database on disk.
208
+ *
209
+ * @returns {Promise<void>} Resolves once the snapshot is durably on disk.
176
210
  */
177
211
  save() {
212
+ if (!this._db) return Promise.resolve();
213
+
214
+ let buffer;
215
+ try {
216
+ this._ensureDir();
217
+ // Snapshot the DB synchronously so the bytes reflect this exact moment.
218
+ buffer = Buffer.from(this._db.export());
219
+ } catch (err) {
220
+ console.warn(`[VectorHub] Failed to export database: ${err.message}`);
221
+ return Promise.resolve();
222
+ }
223
+
224
+ const dbPath = this.dbPath;
225
+ // Increment when SCHEDULED; decrement only once this specific save has
226
+ // COMPLETED (success or failure). The exit guard fires saveSync() while any
227
+ // scheduled save is still outstanding — see _installExitGuard().
228
+ this._pendingSaves++;
229
+ this._saveChain = this._saveChain.then(() => writeDbDurable(dbPath, buffer))
230
+ .catch((err) => {
231
+ console.warn(`[VectorHub] Failed to save database: ${err.message}`);
232
+ })
233
+ .then(() => { this._pendingSaves--; });
234
+ return this._saveChain;
235
+ }
236
+
237
+ /**
238
+ * Synchronous, crash-safe persistence — used only on shutdown to GUARANTEE
239
+ * no acknowledged write is lost if the process exits before the async save
240
+ * chain drains. Correctness over non-blocking here.
241
+ */
242
+ saveSync() {
178
243
  if (!this._db) return;
179
244
  try {
180
245
  this._ensureDir();
181
- const data = this._db.export();
182
- const buffer = Buffer.from(data);
183
- fs.writeFileSync(this.dbPath, buffer);
246
+ const buffer = Buffer.from(this._db.export());
247
+ const tmpPath = `${this.dbPath}.tmp.${process.pid}`;
248
+ const fd = fs.openSync(tmpPath, 'w');
249
+ try {
250
+ fs.writeSync(fd, buffer);
251
+ fs.fsyncSync(fd);
252
+ } finally {
253
+ fs.closeSync(fd);
254
+ }
255
+ fs.renameSync(tmpPath, this.dbPath);
256
+ // A sync export captures the full in-memory DB — a superset of anything the
257
+ // outstanding async saves would have written — so the pending work is now
258
+ // durably satisfied. Clearing the counter prevents a redundant second flush.
259
+ this._pendingSaves = 0;
184
260
  } catch (err) {
185
- console.warn(`[VectorHub] Failed to save database: ${err.message}`);
261
+ console.warn(`[VectorHub] Failed to save database (sync): ${err.message}`);
186
262
  }
187
263
  }
188
264
 
@@ -199,10 +275,13 @@ class VectorHub {
199
275
 
200
276
  /**
201
277
  * Close the database and save final state to disk.
278
+ * Drains any pending async saves, then performs a guaranteed synchronous
279
+ * durable write so no acknowledged data is lost on shutdown (UC-09).
202
280
  */
203
281
  async close() {
204
282
  if (this._db) {
205
- this.save();
283
+ try { await this._saveChain; } catch { /* logged in save() */ }
284
+ this.saveSync();
206
285
  this._db.close();
207
286
  this._db = null;
208
287
  this.initialized = false;
@@ -455,6 +534,32 @@ class VectorHub {
455
534
  }
456
535
  }
457
536
 
537
+ // ── Durable async DB file write (UC-09) ───────────────────────────────────────
538
+ // Crash-safe: write to a tmp file, fsync, then atomically rename over the target.
539
+ // A crash mid-write leaves the previous good .db intact (rename is atomic on POSIX).
540
+ function writeDbDurable(dbPath, buffer) {
541
+ return new Promise((resolve, reject) => {
542
+ const tmpPath = `${dbPath}.tmp.${process.pid}`;
543
+ const fail = (err) => { fs.unlink(tmpPath, () => reject(err)); };
544
+ fs.open(tmpPath, 'w', (openErr, fd) => {
545
+ if (openErr) return reject(openErr);
546
+ fs.write(fd, buffer, 0, buffer.length, 0, (writeErr) => {
547
+ if (writeErr) { fs.close(fd, () => fail(writeErr)); return; }
548
+ fs.fsync(fd, (syncErr) => {
549
+ fs.close(fd, (closeErr) => {
550
+ if (syncErr) return fail(syncErr);
551
+ if (closeErr) return fail(closeErr);
552
+ fs.rename(tmpPath, dbPath, (renameErr) => {
553
+ if (renameErr) return fail(renameErr);
554
+ resolve();
555
+ });
556
+ });
557
+ });
558
+ });
559
+ });
560
+ });
561
+ }
562
+
458
563
  // ── Factory Function ──────────────────────────────────────────────────────────
459
564
 
460
565
  /**
@@ -0,0 +1,110 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ const MIGRATION_ID = '10.7.0-to-11.0.0';
7
+ const TARGET_VERSION = '11.0.0';
8
+
9
+ async function migrate(projectRoot) {
10
+ const results = { steps: [], success: true };
11
+
12
+ // Step 1: Backup config.json
13
+ const configPath = path.join(projectRoot, '.mindforge', 'config.json');
14
+ if (fs.existsSync(configPath)) {
15
+ const backupPath = configPath + '.v10-backup';
16
+ fs.copyFileSync(configPath, backupPath);
17
+ results.steps.push({ step: 'backup_config', status: 'done', path: backupPath });
18
+
19
+ // Step 2: Add new config sections
20
+ try {
21
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
22
+
23
+ if (!config.temporal) {
24
+ config.temporal = { max_snapshots: 50, max_age_days: 7 };
25
+ }
26
+ if (!config.rate_limiting) {
27
+ config.rate_limiting = { dashboard_rpm: 100, model_rpm: {} };
28
+ }
29
+ if (!config.session) {
30
+ config.session = { token_expiry_hours: 24 };
31
+ }
32
+ if (!config.wave_execution) {
33
+ config.wave_execution = { max_concurrency: 3 };
34
+ }
35
+
36
+ config.version = TARGET_VERSION;
37
+
38
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
39
+ results.steps.push({ step: 'update_config', status: 'done' });
40
+ } catch (e) {
41
+ results.steps.push({ step: 'update_config', status: 'warning', error: e.message });
42
+ }
43
+ }
44
+
45
+ // Step 3: Archive old AUDIT lines if > 5000
46
+ const auditPath = path.join(projectRoot, '.planning', 'AUDIT.jsonl');
47
+ if (fs.existsSync(auditPath)) {
48
+ try {
49
+ const content = fs.readFileSync(auditPath, 'utf8');
50
+ const lines = content.split('\n').filter(l => l.trim());
51
+ if (lines.length > 5000) {
52
+ const archiveDir = path.join(projectRoot, '.planning', 'audit-archive');
53
+ if (!fs.existsSync(archiveDir)) fs.mkdirSync(archiveDir, { recursive: true });
54
+
55
+ const zlib = require('zlib');
56
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
57
+ const archivePath = path.join(archiveDir, `AUDIT-pre-v11-${timestamp}.jsonl.gz`);
58
+ const toArchive = lines.slice(0, -500).join('\n') + '\n';
59
+ fs.writeFileSync(archivePath, zlib.gzipSync(toArchive));
60
+
61
+ const remaining = lines.slice(-500).join('\n') + '\n';
62
+ fs.writeFileSync(auditPath, remaining);
63
+ results.steps.push({ step: 'archive_audit', status: 'done', archived: lines.length - 500 });
64
+ } else {
65
+ results.steps.push({ step: 'archive_audit', status: 'skipped', reason: 'under_threshold' });
66
+ }
67
+ } catch (e) {
68
+ results.steps.push({ step: 'archive_audit', status: 'warning', error: e.message });
69
+ }
70
+ }
71
+
72
+ // Step 4: GC old snapshots
73
+ try {
74
+ const TemporalHub = require('../engine/temporal-hub');
75
+ const gcResult = await TemporalHub.gc({ maxSnapshots: 50, maxAgeDays: 30 });
76
+ results.steps.push({ step: 'snapshot_gc', status: 'done', deleted: gcResult.deleted });
77
+ } catch (e) {
78
+ results.steps.push({ step: 'snapshot_gc', status: 'warning', error: e.message });
79
+ }
80
+
81
+ // Step 5: Bump schema_version in HANDOFF.json
82
+ const handoffPath = path.join(projectRoot, '.planning', 'HANDOFF.json');
83
+ if (fs.existsSync(handoffPath)) {
84
+ try {
85
+ const handoff = JSON.parse(fs.readFileSync(handoffPath, 'utf8'));
86
+ handoff.schema_version = TARGET_VERSION;
87
+ fs.writeFileSync(handoffPath, JSON.stringify(handoff, null, 2) + '\n');
88
+ results.steps.push({ step: 'bump_handoff_version', status: 'done' });
89
+ } catch (e) {
90
+ results.steps.push({ step: 'bump_handoff_version', status: 'warning', error: e.message });
91
+ }
92
+ }
93
+
94
+ // Step 6: Update MINDFORGE.md VERSION
95
+ const mindforgeFile = path.join(projectRoot, 'MINDFORGE.md');
96
+ if (fs.existsSync(mindforgeFile)) {
97
+ try {
98
+ let content = fs.readFileSync(mindforgeFile, 'utf8');
99
+ content = content.replace(/VERSION\s*=\s*[\d.]+/, `VERSION = ${TARGET_VERSION}`);
100
+ fs.writeFileSync(mindforgeFile, content);
101
+ results.steps.push({ step: 'bump_mindforge_version', status: 'done' });
102
+ } catch (e) {
103
+ results.steps.push({ step: 'bump_mindforge_version', status: 'warning', error: e.message });
104
+ }
105
+ }
106
+
107
+ return results;
108
+ }
109
+
110
+ module.exports = { MIGRATION_ID, TARGET_VERSION, migrate };
@@ -71,6 +71,19 @@ const SCHEMA_HISTORY = [
71
71
  'Plugin API version upgraded to 2.0.0',
72
72
  ],
73
73
  },
74
+ {
75
+ version: '11.0.0',
76
+ date: '2026-05-28',
77
+ description: 'v11.0.0 - Persona Expansion: temporal config, rate limiting, wave execution, audit archival',
78
+ handoff_fields_added: [],
79
+ handoff_fields_removed: [],
80
+ audit_fields_added: [],
81
+ breaking: [
82
+ 'config.json gains temporal, rate_limiting, session, wave_execution sections',
83
+ 'AUDIT.jsonl auto-archived if exceeding 5000 lines',
84
+ 'MINDFORGE.md VERSION format drops suffix (was X.Y.Z-SUFFIX, now X.Y.Z)',
85
+ ],
86
+ },
74
87
  ];
75
88
 
76
89
  module.exports = { SCHEMA_HISTORY };
@@ -115,11 +115,6 @@ const COMMANDS = {
115
115
  script: 'bin/autonomous/mesh-self-healer.js',
116
116
  description: 'Auto-detect and repair reasoning drifts in the active swarm'
117
117
  },
118
- 'quantum-verify': {
119
- script: 'bin/governance/quantum-crypto.js',
120
- description: 'Verify framework integrity using post-quantum signatures',
121
- defaultArgs: ['--verify', '.mindforge/engine/']
122
- },
123
118
  // Planned: jira-sync, confluence-sync (not yet implemented)
124
119
  'metrics': {
125
120
  script: 'bin/dashboard/metrics-aggregator.js',
@@ -138,6 +133,10 @@ const COMMANDS = {
138
133
  script: 'bin/engine/learning-manager.js',
139
134
  description: 'Append a new Learning Entry to the Evolution Log',
140
135
  defaultArgs: ['record']
136
+ },
137
+ 'verify': {
138
+ script: 'bin/engine/verify-cli.js',
139
+ description: 'Run unified verification (tests, lint, audit, typecheck) and write report'
141
140
  }
142
141
  };
143
142
 
@@ -15,7 +15,7 @@ class AnthropicProvider {
15
15
 
16
16
  const data = JSON.stringify({
17
17
  model,
18
- system: systemPrompt,
18
+ system: [{ type: 'text', text: systemPrompt, cache_control: { type: 'ephemeral' } }],
19
19
  messages: [{ role: 'user', content: userMessage }],
20
20
  max_tokens: maxTokens,
21
21
  temperature,
@@ -45,15 +45,24 @@ class AnthropicProvider {
45
45
 
46
46
  const inputTokens = json.usage.input_tokens;
47
47
  const outputTokens = json.usage.output_tokens;
48
-
49
- // Basic cost calculation (Sonnet 3.5 prices)
50
- const cost = (inputTokens * 0.000003) + (outputTokens * 0.000015);
48
+ const cacheRead = json.usage.cache_read_input_tokens || 0;
49
+ const cacheCreate = json.usage.cache_creation_input_tokens || 0;
50
+
51
+ const { priceCall } = require('./pricing-registry');
52
+ const cost = priceCall(json.model, {
53
+ input_tokens: inputTokens,
54
+ output_tokens: outputTokens,
55
+ cache_read_input_tokens: cacheRead,
56
+ cache_creation_input_tokens: cacheCreate,
57
+ });
51
58
 
52
59
  resolve({
53
60
  model: json.model,
54
61
  content: json.content[0].text,
55
62
  input_tokens: inputTokens,
56
63
  output_tokens: outputTokens,
64
+ cache_read_input_tokens: cacheRead,
65
+ cache_creation_input_tokens: cacheCreate,
57
66
  cost_usd: cost,
58
67
  provider: 'anthropic'
59
68
  });
@@ -72,6 +81,51 @@ class AnthropicProvider {
72
81
  req.end();
73
82
  });
74
83
  }
84
+
85
+ async streamComplete(messages, options = {}) {
86
+ const model = options.model || 'claude-sonnet-4-6';
87
+ const maxTokens = options.maxTokens || 4096;
88
+
89
+ const data = JSON.stringify({
90
+ model,
91
+ messages,
92
+ max_tokens: maxTokens,
93
+ stream: true,
94
+ });
95
+
96
+ return new Promise((resolve, reject) => {
97
+ const req = https.request({
98
+ hostname: 'api.anthropic.com',
99
+ path: '/v1/messages',
100
+ method: 'POST',
101
+ headers: {
102
+ 'Content-Type': 'application/json',
103
+ 'x-api-key': this.apiKey,
104
+ 'anthropic-version': '2023-06-01',
105
+ 'Content-Length': Buffer.byteLength(data),
106
+ },
107
+ timeout: 300_000,
108
+ }, res => {
109
+ if (res.statusCode !== 200) {
110
+ let body = '';
111
+ res.on('data', chunk => body += chunk);
112
+ res.on('end', () => {
113
+ reject(new Error(`Anthropic streaming failed: ${res.statusCode}`));
114
+ });
115
+ return;
116
+ }
117
+ resolve(res);
118
+ });
119
+
120
+ req.on('error', reject);
121
+ req.on('timeout', () => {
122
+ req.destroy();
123
+ reject(new Error('Anthropic stream timeout'));
124
+ });
125
+ req.write(data);
126
+ req.end();
127
+ });
128
+ }
75
129
  }
76
130
 
77
131
  module.exports = AnthropicProvider;