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
@@ -1,90 +1,125 @@
1
1
  /**
2
- * MindForge — Audit Writer (Async Buffered)
3
- * Extracted from auto-runner.js — handles all JSONL audit append operations.
4
- * Buffers entries and flushes every 100ms or when buffer reaches 10 entries.
2
+ * MindForge — Audit Writer (Synchronous Durable, Hash-Chained)
3
+ *
4
+ * Provides the ONE unified audit-append primitive {@link appendAuditEntrySync}
5
+ * that every audit write site funnels through, producing a single verifiable
6
+ * hash chain (UC-04 / UC-04b).
7
+ *
8
+ * Retired in UC-04b: the old buffered async writer (`createAuditWriter`) and its
9
+ * AuditRotator-based 5000-line rotation. Rotation BROKE the hash chain — archiving
10
+ * + truncating AUDIT.jsonl orphaned the carried head's `previous_hash` from an entry
11
+ * no longer on disk, so the verifier failed closed on a rotated-but-untampered file.
12
+ * As a result AUDIT.jsonl now grows UNBOUNDED. That is an accepted short-term tradeoff
13
+ * at single-operator/dev scale. The proper fix — chain-aware compaction (archive old
14
+ * entries AND re-anchor the first carried entry to previous_hash=null so the live tail
15
+ * verifies standalone) — is a DEFERRED future feature, intentionally NOT in scope here.
5
16
  */
6
17
  'use strict';
7
18
 
8
19
  const fs = require('fs');
20
+ const path = require('path');
9
21
  const crypto = require('crypto');
10
-
11
- const FLUSH_INTERVAL_MS = 100;
12
- const FLUSH_THRESHOLD = 10;
22
+ const { hashAuditEntry } = require('../governance/audit-hash');
13
23
 
14
24
  /**
15
- * Creates a buffered async audit writer.
16
- * @param {string} auditPath Path to the AUDIT.jsonl file
17
- * @returns {{ write: (entry: object) => void, flush: () => Promise<void>, close: () => Promise<void> }}
25
+ * Computes the SHA-256 hash of an entry chained to its predecessor (UC-04).
26
+ * Delegates to the canonical {@link hashAuditEntry} (bin/governance/audit-hash.js)
27
+ * so the writer and verifier share ONE hasher material drift is impossible.
28
+ * The material is {...entry, previous_hash} where `entry` does NOT contain `_hash`.
29
+ * @param {object} entry — entry WITHOUT a `_hash` field
30
+ * @param {string|null} previousHash — prior entry's `_hash` (null for the first link)
31
+ * @returns {string} hex-encoded SHA-256 digest
18
32
  */
19
- function createAuditWriter(auditPath) {
20
- let buffer = [];
21
- let flushTimer = null;
22
- let isClosed = false;
23
-
24
- function scheduleFlush() {
25
- if (flushTimer !== null) return;
26
- flushTimer = setTimeout(async () => {
27
- flushTimer = null;
28
- await flush();
29
- }, FLUSH_INTERVAL_MS);
30
- }
31
-
32
- /**
33
- * Adds an entry to the buffer. Triggers flush if threshold reached.
34
- * Stamps entry with id and timestamp if missing (immutable — creates new object).
35
- */
36
- function write(entry) {
37
- if (isClosed) {
38
- throw new Error('AuditWriter is closed — cannot write after close()');
39
- }
40
-
41
- const stamped = Object.assign(Object.create(null), entry, {
42
- id: entry.id || crypto.randomBytes(8).toString('hex'),
43
- timestamp: entry.timestamp || new Date().toISOString(),
44
- });
33
+ function hashEntry(entry, previousHash) {
34
+ return hashAuditEntry(entry, previousHash);
35
+ }
45
36
 
46
- buffer = [...buffer, stamped];
37
+ /**
38
+ * Reads the `_hash` of the last entry already on disk so a re-opened writer
39
+ * continues the existing chain instead of starting a fresh, disconnected one.
40
+ * @param {string} auditPath — path to the AUDIT.jsonl file
41
+ * @returns {string|null} last `_hash`, or null if absent/unreadable/empty
42
+ */
43
+ function readLastHash(auditPath) {
44
+ try {
45
+ const lines = fs.readFileSync(auditPath, 'utf8').split('\n').filter(Boolean);
46
+ if (lines.length === 0) return null;
47
+ const last = JSON.parse(lines[lines.length - 1]);
48
+ return last._hash || null;
49
+ } catch { return null; }
50
+ }
47
51
 
48
- if (buffer.length >= FLUSH_THRESHOLD) {
49
- // Immediate flush don't wait for timer
50
- if (flushTimer !== null) {
51
- clearTimeout(flushTimer);
52
- flushTimer = null;
53
- }
54
- flush();
55
- } else {
56
- scheduleFlush();
57
- }
52
+ // ── Unified synchronous-durable chained append (UC-04b) ───────────────────────
53
+ // ONE primitive that ALL audit write sites funnel through, producing a single
54
+ // verifiable chain via the canonical hashAuditEntry. Synchronous + fsync'd so an
55
+ // acknowledged write is on disk before the call returns (UC-09 durability) — this
56
+ // is what lets us delete the old raw `appendFileSync` shadow-writes: the durable
57
+ // sync write gives in-process consumers (e.g. StuckMonitor, which is fed the event
58
+ // object directly but may also re-read the file) immediate, durable data.
59
+ //
60
+ // Chain head caching: re-reading the file's tail on every append is O(file) — bad
61
+ // on hot paths. Instead we keep a per-path in-memory lastHash (Map keyed by the
62
+ // RESOLVED absolute path), seeded ONCE from the file's last entry on the first
63
+ // append, then advanced in-process for O(1) appends. If the cache is cold (new
64
+ // process, or a path never written in this process) we seed from disk — so a
65
+ // second process correctly continues the on-disk chain from its tail.
66
+ //
67
+ // Concurrency: within a process this is fully synchronous, so calls cannot
68
+ // interleave and the cached lastHash is always current. ACROSS processes, each
69
+ // process seeds from the file tail on its first append; this is correct only under
70
+ // the single-operator model (no two processes appending CONCURRENTLY to the same
71
+ // audit file). MindForge runs one autonomous operator at a time, so this holds.
72
+ const _lastHashCache = new Map(); // resolvedPath -> last `_hash` written/seen
58
73
 
59
- return stamped;
60
- }
74
+ /**
75
+ * Synchronously appends ONE hash-chained, durable entry to an audit JSONL file.
76
+ * This is the single unified append used by every audit write site (UC-04b).
77
+ *
78
+ * NOTE: performs an fsync (openSync('a') + writeSync + fsyncSync + closeSync) on
79
+ * EVERY call. This is deliberate for audit integrity/durability — but it makes the
80
+ * call relatively expensive, so it is intended for audit-grade events, NOT for
81
+ * high-frequency telemetry. Hot-path callers (e.g. nexus-tracer span/reasoning
82
+ * events) pay one fsync per event; keep that cost in mind before adding new hot
83
+ * write sites.
84
+ *
85
+ * @param {string} auditPath — path to the AUDIT.jsonl file
86
+ * @param {object} event — the event payload (id/timestamp stamped if missing)
87
+ * @returns {object} the stamped + chained entry actually written
88
+ */
89
+ function appendAuditEntrySync(auditPath, event) {
90
+ const resolved = path.resolve(auditPath);
61
91
 
62
- /**
63
- * Flushes the buffer to disk using async appendFile.
64
- */
65
- async function flush() {
66
- if (buffer.length === 0) return;
92
+ // 1. Stamp id + timestamp if missing — immutable (new object, never mutate input).
93
+ const stamped = {
94
+ ...event,
95
+ id: event.id || crypto.randomBytes(8).toString('hex'),
96
+ timestamp: event.timestamp || new Date().toISOString(),
97
+ };
67
98
 
68
- const toWrite = buffer;
69
- buffer = [];
99
+ // 2. Seed previous_hash: prefer the warm in-process cache; fall back to the
100
+ // file's last entry when cold (first append in this process for this path).
101
+ let previous_hash = _lastHashCache.has(resolved)
102
+ ? _lastHashCache.get(resolved)
103
+ : readLastHash(resolved);
70
104
 
71
- const payload = toWrite.map(e => JSON.stringify(e)).join('\n') + '\n';
72
- await fs.promises.appendFile(auditPath, payload);
73
- }
105
+ // 3. Compute _hash over {...stamped, previous_hash} WITHOUT _hash in the material.
106
+ const _hash = hashEntry(stamped, previous_hash);
74
107
 
75
- /**
76
- * Flushes remaining entries and stops the timer. After close(), write() will throw.
77
- */
78
- async function close() {
79
- isClosed = true;
80
- if (flushTimer !== null) {
81
- clearTimeout(flushTimer);
82
- flushTimer = null;
83
- }
84
- await flush();
108
+ // 4. Write {...stamped, previous_hash, _hash} as one JSON line, durably+synchronously
109
+ // (openSync('a') + writeSync + fsyncSync + closeSync mirrors appendDurableSync).
110
+ const chained = { ...stamped, previous_hash, _hash };
111
+ fs.mkdirSync(path.dirname(resolved), { recursive: true });
112
+ const fd = fs.openSync(resolved, 'a');
113
+ try {
114
+ fs.writeSync(fd, JSON.stringify(chained) + '\n');
115
+ fs.fsyncSync(fd);
116
+ } finally {
117
+ fs.closeSync(fd);
85
118
  }
86
119
 
87
- return Object.freeze({ write, flush, close });
120
+ // 5. Advance the in-process chain head and return the written entry.
121
+ _lastHashCache.set(resolved, _hash);
122
+ return chained;
88
123
  }
89
124
 
90
- module.exports = { createAuditWriter };
125
+ module.exports = { appendAuditEntrySync };