mindforge-cc 11.0.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.
- package/.agent/hooks/mindforge-statusline.js +2 -2
- package/.mindforge/config.json +13 -4
- package/CHANGELOG.md +101 -0
- package/MINDFORGE.md +3 -3
- package/RELEASENOTES.md +1 -1
- package/bin/autonomous/audit-writer.js +108 -86
- package/bin/autonomous/auto-runner.js +304 -19
- package/bin/autonomous/dependency-dag.js +59 -0
- package/bin/autonomous/wave-executor.js +20 -1
- package/bin/council-cli.js +161 -0
- package/bin/dashboard/approval-handler.js +3 -1
- package/bin/dashboard/server.js +1 -1
- package/bin/dashboard/sse-bridge.js +9 -12
- package/bin/engine/council-runtime.js +124 -0
- package/bin/engine/otel-exporter.js +123 -0
- package/bin/engine/remediation-engine.js +1 -1
- package/bin/engine/self-corrective-synthesizer.js +1 -1
- package/bin/engine/temporal-cli.js +4 -2
- package/bin/engine/verification-runner.js +131 -0
- package/bin/engine/verify-cli.js +34 -0
- package/bin/eval/eval-harness.js +82 -0
- package/bin/eval/golden-set-retrieval.json +46 -0
- package/bin/governance/audit-hash.js +12 -0
- package/bin/governance/audit-verifier.js +60 -0
- package/bin/governance/quantum-crypto.js +63 -9
- package/bin/governance/ztai-manager.js +30 -2
- package/bin/hindsight-injector.js +5 -6
- package/bin/hooks/instinct-capture-hook.js +186 -0
- package/bin/memory/auto-shadow.js +32 -3
- package/bin/memory/identity-synthesizer.js +2 -2
- package/bin/memory/knowledge-store.js +30 -6
- package/bin/memory/retrieval-fusion.js +58 -0
- package/bin/memory/semantic-hub.js +2 -2
- package/bin/memory/vector-hub.js +111 -6
- package/bin/mindforge-cli.js +4 -5
- package/bin/models/anthropic-provider.js +13 -4
- package/bin/models/cost-tracker.js +3 -1
- package/bin/models/difficulty-scorer.js +54 -0
- package/bin/models/gemini-provider.js +6 -2
- package/bin/models/model-router.js +31 -18
- package/bin/models/openai-provider.js +6 -3
- package/bin/models/pricing-registry.js +128 -0
- package/bin/review/ads-engine.js +1 -1
- package/bin/security/trust-boundaries.js +102 -0
- package/bin/security/trust-gate-hook.js +39 -0
- package/bin/skill-registry.js +3 -2
- package/bin/skills-builder/marketplace-cli.js +5 -3
- package/bin/skills-builder/skill-registrar.js +4 -6
- package/bin/sre/sentinel.js +7 -5
- package/bin/utils/append-queue.js +55 -0
- package/bin/utils/file-io.js +27 -37
- package/bin/utils/version-check.js +59 -0
- package/bin/verify-audit.js +12 -0
- package/bin/wizard/theme.js +1 -2
- package/package.json +1 -1
- package/bin/dashboard/team-tracker.js +0 -0
package/bin/skill-registry.js
CHANGED
|
@@ -215,7 +215,6 @@ function handleAudit() {
|
|
|
215
215
|
}
|
|
216
216
|
|
|
217
217
|
const entry = {
|
|
218
|
-
timestamp: new Date().toISOString(),
|
|
219
218
|
event: 'skill_installed',
|
|
220
219
|
skill_name: skillName,
|
|
221
220
|
skill_version: version,
|
|
@@ -224,7 +223,9 @@ function handleAudit() {
|
|
|
224
223
|
validation_passed: true
|
|
225
224
|
};
|
|
226
225
|
|
|
227
|
-
|
|
226
|
+
// UC-04b: unified, hash-chained, durable append into the single verifiable chain.
|
|
227
|
+
const { appendAuditEntrySync } = require('./autonomous/audit-writer');
|
|
228
|
+
appendAuditEntrySync(auditPath, entry);
|
|
228
229
|
console.log(` 📝 Audit entry written for ${skillName}`);
|
|
229
230
|
process.exit(0);
|
|
230
231
|
}
|
|
@@ -19,7 +19,7 @@ if (!CMD) {
|
|
|
19
19
|
async function main() {
|
|
20
20
|
try {
|
|
21
21
|
switch (CMD) {
|
|
22
|
-
case 'search':
|
|
22
|
+
case 'search': {
|
|
23
23
|
const results = await Marketplace.search(QUERY);
|
|
24
24
|
console.table(results.map(r => ({
|
|
25
25
|
name: r.name,
|
|
@@ -28,12 +28,14 @@ async function main() {
|
|
|
28
28
|
description: r.description.slice(0, 50) + '...'
|
|
29
29
|
})));
|
|
30
30
|
break;
|
|
31
|
-
|
|
31
|
+
}
|
|
32
|
+
|
|
32
33
|
case 'featured':
|
|
33
|
-
case 'trending':
|
|
34
|
+
case 'trending': {
|
|
34
35
|
const list = await Marketplace.getFeatured();
|
|
35
36
|
console.table(list);
|
|
36
37
|
break;
|
|
38
|
+
}
|
|
37
39
|
|
|
38
40
|
case 'install':
|
|
39
41
|
if (!QUERY) throw new Error('Package name required for install');
|
|
@@ -82,11 +82,10 @@ function register(params) {
|
|
|
82
82
|
);
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
// Write AUDIT entry
|
|
85
|
+
// Write AUDIT entry via the unified, hash-chained, durable append (UC-04b).
|
|
86
86
|
if (fs.existsSync(path.dirname(AUDIT_PATH))) {
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
timestamp: new Date().toISOString(),
|
|
87
|
+
const { appendAuditEntrySync } = require('../autonomous/audit-writer');
|
|
88
|
+
appendAuditEntrySync(AUDIT_PATH, {
|
|
90
89
|
event: 'skill_learned',
|
|
91
90
|
agent: 'mindforge-skills-builder',
|
|
92
91
|
phase: null,
|
|
@@ -97,8 +96,7 @@ function register(params) {
|
|
|
97
96
|
source_type: sourceType,
|
|
98
97
|
source: String(source).slice(0, 200),
|
|
99
98
|
skill_path: relativePath,
|
|
100
|
-
};
|
|
101
|
-
fs.appendFileSync(AUDIT_PATH, JSON.stringify(entry) + '\n');
|
|
99
|
+
});
|
|
102
100
|
}
|
|
103
101
|
|
|
104
102
|
return { registered: true, skillName, tier, qualityScore };
|
package/bin/sre/sentinel.js
CHANGED
|
@@ -109,13 +109,15 @@ class Sentinel {
|
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
logToAudit(event, targetPath) {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
112
|
+
// UC-04b: route through the unified, hash-chained, durable append so Sentinel's
|
|
113
|
+
// incident entries link into the single verifiable chain (was a raw appendFileSync
|
|
114
|
+
// that broke the chain). appendAuditEntrySync caches the chain head per resolved
|
|
115
|
+
// path, so an explicit targetPath is chained correctly too.
|
|
116
|
+
const { appendAuditEntrySync } = require('../autonomous/audit-writer');
|
|
117
|
+
appendAuditEntrySync(targetPath || this.auditPath, {
|
|
115
118
|
agent: 'mindforge-sentinel',
|
|
116
119
|
...event
|
|
117
|
-
};
|
|
118
|
-
fs.appendFileSync(targetPath || this.auditPath, JSON.stringify(logEntry) + '\n');
|
|
120
|
+
});
|
|
119
121
|
}
|
|
120
122
|
|
|
121
123
|
stop() {
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* MindForge — Single-writer serialized append queue (UC-09).
|
|
4
|
+
* Guarantees: (1) appends to a given file are serialized (no interleaving across
|
|
5
|
+
* concurrent callers in this process), (2) each append() resolves only after the
|
|
6
|
+
* bytes are fsync'd to disk (durability), (3) a trailing newline delimits records.
|
|
7
|
+
*
|
|
8
|
+
* Scope: protects against in-process concurrent-write interleaving and crash-loss
|
|
9
|
+
* of acknowledged writes. Cross-PROCESS locking is out of scope for the
|
|
10
|
+
* single-operator localhost model (documented; revisit if multi-process writers appear).
|
|
11
|
+
*/
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
|
|
14
|
+
// path -> Promise chain tail. NOTE: this is intended for a small, fixed set of
|
|
15
|
+
// known paths (e.g. AUDIT.jsonl). Per-path entries are NEVER evicted, so do NOT
|
|
16
|
+
// key this by high-cardinality dynamic paths — doing so would leak memory
|
|
17
|
+
// unboundedly. Revisit with an LRU/eviction policy if dynamic paths are needed.
|
|
18
|
+
const queues = new Map();
|
|
19
|
+
|
|
20
|
+
function createAppendQueue(filePath) {
|
|
21
|
+
if (!queues.has(filePath)) queues.set(filePath, Promise.resolve());
|
|
22
|
+
|
|
23
|
+
function append(line) {
|
|
24
|
+
const record = line.endsWith('\n') ? line : line + '\n';
|
|
25
|
+
const tail = queues.get(filePath).then(() => writeDurable(filePath, record));
|
|
26
|
+
queues.set(filePath, tail.catch(() => {}));
|
|
27
|
+
return tail;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function drain() {
|
|
31
|
+
return queues.get(filePath);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return Object.freeze({ append, drain });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function writeDurable(filePath, data) {
|
|
38
|
+
return new Promise((resolve, reject) => {
|
|
39
|
+
fs.open(filePath, 'a', (openErr, fd) => {
|
|
40
|
+
if (openErr) return reject(openErr);
|
|
41
|
+
fs.write(fd, data, (writeErr) => {
|
|
42
|
+
if (writeErr) { fs.close(fd, () => {}); return reject(writeErr); }
|
|
43
|
+
fs.fsync(fd, (syncErr) => {
|
|
44
|
+
fs.close(fd, (closeErr) => {
|
|
45
|
+
if (syncErr) return reject(syncErr);
|
|
46
|
+
if (closeErr) return reject(closeErr);
|
|
47
|
+
resolve();
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
module.exports = { createAppendQueue };
|
package/bin/utils/file-io.js
CHANGED
|
@@ -3,56 +3,46 @@
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const fsp = require('fs/promises');
|
|
5
5
|
const path = require('path');
|
|
6
|
-
const crypto = require('crypto');
|
|
7
6
|
const zlib = require('zlib');
|
|
8
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Hash-chained audit writer (class API preserved for nexus-tracer + policy-engine).
|
|
10
|
+
*
|
|
11
|
+
* UC-04b: this class previously maintained a SECOND, DIVERGENT chain implementation
|
|
12
|
+
* (it hashed {...entry, timestamp, previous_hash} — injecting timestamp into the
|
|
13
|
+
* material differently from the canonical writer), so entries it wrote could never
|
|
14
|
+
* verify against bin/governance/audit-verifier.js. It now delegates every write to
|
|
15
|
+
* the SINGLE shared `appendAuditEntrySync` (canonical hashAuditEntry, synchronous +
|
|
16
|
+
* fsync-durable), so there is ONE hasher and ONE on-disk chain per file.
|
|
17
|
+
*
|
|
18
|
+
* The async API (write/flush/close returning promises) is kept because callers do
|
|
19
|
+
* `await this._auditWriter.write(entry)`; the underlying append is now synchronous
|
|
20
|
+
* and durable, so flush()/close() are no-ops retained for API compatibility.
|
|
21
|
+
*/
|
|
9
22
|
class AuditWriter {
|
|
10
23
|
constructor(filePath) {
|
|
11
24
|
this._path = filePath;
|
|
12
|
-
|
|
13
|
-
|
|
25
|
+
// Retained for API compatibility; the unified append is synchronous so there
|
|
26
|
+
// is no longer an internal buffer or timer to manage.
|
|
14
27
|
this._lastHash = null;
|
|
15
28
|
}
|
|
16
29
|
|
|
17
|
-
write(entry) {
|
|
18
|
-
//
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const serialized = JSON.stringify(entryWithHash);
|
|
25
|
-
this._lastHash = crypto.createHash('sha256').update(serialized).digest('hex');
|
|
26
|
-
entryWithHash._hash = this._lastHash;
|
|
27
|
-
|
|
28
|
-
this._buffer.push(JSON.stringify(entryWithHash));
|
|
29
|
-
|
|
30
|
-
if (this._buffer.length >= 10) {
|
|
31
|
-
return this.flush();
|
|
32
|
-
}
|
|
33
|
-
if (!this._flushTimer) {
|
|
34
|
-
this._flushTimer = setTimeout(() => this.flush(), 100);
|
|
35
|
-
}
|
|
36
|
-
return Promise.resolve();
|
|
30
|
+
async write(entry) {
|
|
31
|
+
// Lazy require to avoid a require-cycle: audit-writer.js requires this file
|
|
32
|
+
// (AuditRotator) at load time, so we cannot require it at module top level.
|
|
33
|
+
const { appendAuditEntrySync } = require('../autonomous/audit-writer');
|
|
34
|
+
const chained = appendAuditEntrySync(this._path, entry);
|
|
35
|
+
this._lastHash = chained._hash;
|
|
36
|
+
return chained;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
async flush() {
|
|
40
|
-
if (this._buffer.length === 0) return;
|
|
41
|
-
clearTimeout(this._flushTimer);
|
|
42
|
-
this._flushTimer = null;
|
|
43
|
-
|
|
44
|
-
const lines = this._buffer.splice(0);
|
|
45
|
-
const content = lines.join('\n') + '\n';
|
|
39
|
+
async flush() { /* no-op: appendAuditEntrySync is synchronous + fsync-durable */ }
|
|
46
40
|
|
|
47
|
-
|
|
48
|
-
await fsp.appendFile(this._path, content);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
async close() {
|
|
52
|
-
await this.flush();
|
|
53
|
-
}
|
|
41
|
+
async close() { /* no-op: nothing buffered */ }
|
|
54
42
|
|
|
55
43
|
async initLastHash() {
|
|
44
|
+
// The unified append seeds its own chain head from the file tail; this remains
|
|
45
|
+
// for callers that expect to prime _lastHash explicitly.
|
|
56
46
|
try {
|
|
57
47
|
const content = await fsp.readFile(this._path, 'utf8');
|
|
58
48
|
const lines = content.trim().split('\n').filter(Boolean);
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* MindForge version single-source-of-truth + drift detector.
|
|
4
|
+
* package.json is canonical; everything else must agree.
|
|
5
|
+
*/
|
|
6
|
+
const path = require('path');
|
|
7
|
+
// Use the repo's stricter reader: returns null only on ENOENT and RE-THROWS on
|
|
8
|
+
// parse errors. Re-throwing on a corrupt JSON source is the fail-closed
|
|
9
|
+
// behavior we want — a file we cannot parse means we cannot establish truth.
|
|
10
|
+
const { readJSONSync } = require('./file-io');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @param {string} projectRoot
|
|
14
|
+
* @returns {{ canonical: string|null, sources: Record<string,string|null>, drift: string[] }}
|
|
15
|
+
*/
|
|
16
|
+
function checkVersionConsistency(projectRoot) {
|
|
17
|
+
const pkg = readJSONSync(path.join(projectRoot, 'package.json'));
|
|
18
|
+
const canonical = pkg ? pkg.version : null;
|
|
19
|
+
|
|
20
|
+
const configJson = readJSONSync(path.join(projectRoot, '.mindforge', 'config.json'));
|
|
21
|
+
// Runtime drift coverage is intentionally limited to package.json (canonical)
|
|
22
|
+
// vs .mindforge/config.json — the live config is the operational drift risk
|
|
23
|
+
// during `auto`. Wider agreement (sdk/package.json, MINDFORGE.md [VERSION]) is
|
|
24
|
+
// enforced by the test suite (tests/version-consistency.test.js), not at
|
|
25
|
+
// runtime — do not assume this checker provides full version coverage.
|
|
26
|
+
const sources = {
|
|
27
|
+
'package.json': canonical,
|
|
28
|
+
'.mindforge/config.json': configJson ? configJson.version : null,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const drift = [];
|
|
32
|
+
// Fail-closed: if we cannot establish the canonical version (package.json
|
|
33
|
+
// missing or its `version` field absent), treat it as a drift/error condition
|
|
34
|
+
// rather than silently passing. A genuinely corrupt package.json would have
|
|
35
|
+
// already thrown out of readJSONSync above.
|
|
36
|
+
if (!canonical) {
|
|
37
|
+
drift.push('package.json version could not be determined (canonical source missing or unparseable)');
|
|
38
|
+
return { canonical, sources, drift };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
for (const [file, version] of Object.entries(sources)) {
|
|
42
|
+
if (version && version !== canonical) {
|
|
43
|
+
drift.push(`${file} declares ${version} but canonical (package.json) is ${canonical}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return { canonical, sources, drift };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Fail-closed assertion for pre-flight. Throws on drift.
|
|
51
|
+
*/
|
|
52
|
+
function assertVersionConsistency(projectRoot) {
|
|
53
|
+
const { drift } = checkVersionConsistency(projectRoot);
|
|
54
|
+
if (drift.length > 0) {
|
|
55
|
+
throw new Error('Version drift detected (fail-closed):\n - ' + drift.join('\n - '));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module.exports = { checkVersionConsistency, assertVersionConsistency };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
const { verifyAuditChain } = require('./governance/audit-verifier');
|
|
4
|
+
const auditPath = process.argv[2] || '.planning/AUDIT.jsonl';
|
|
5
|
+
const result = verifyAuditChain(auditPath);
|
|
6
|
+
if (result.valid) {
|
|
7
|
+
process.stdout.write(`✅ audit chain valid: ${result.count} entries\n`);
|
|
8
|
+
process.exit(0);
|
|
9
|
+
} else {
|
|
10
|
+
process.stderr.write(`❌ audit chain BROKEN at entry ${result.brokenAt}: ${result.reason}\n`);
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
package/bin/wizard/theme.js
CHANGED
|
@@ -177,8 +177,7 @@ const Theme = {
|
|
|
177
177
|
},
|
|
178
178
|
|
|
179
179
|
// --- Aliases for legacy compatibility ---
|
|
180
|
-
status(label, state) { this.printStatus(label, state); }
|
|
181
|
-
printSuccess(runtime, scope, stats) { this.printSuccessV2(runtime, scope, stats); }
|
|
180
|
+
status(label, state) { this.printStatus(label, state); }
|
|
182
181
|
};
|
|
183
182
|
|
|
184
183
|
module.exports = Theme;
|
package/package.json
CHANGED
|
File without changes
|