mindforge-cc 11.5.0 → 11.5.1
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/.mindforge/config.json +2 -2
- package/.mindforge/memory/sync-manifest.json +1 -1
- package/CHANGELOG.md +45 -0
- package/MINDFORGE.md +3 -3
- package/README.md +0 -1
- package/RELEASENOTES.md +45 -0
- package/bin/dashboard/api-router.js +10 -1
- package/bin/governance/approve.js +5 -1
- package/bin/memory/federated-sync.js +11 -2
- package/bin/memory/knowledge-capture.js +10 -1
- package/bin/memory/pillar-health-tracker.js +9 -1
- package/bin/review/ads-engine.js +2 -2
- package/bin/security/trust-boundaries.js +5 -0
- package/package.json +1 -1
package/.mindforge/config.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "11.5.
|
|
2
|
+
"version": "11.5.1",
|
|
3
3
|
"environment": "development",
|
|
4
4
|
"governance": {
|
|
5
5
|
"drift_threshold": 0.75,
|
|
6
6
|
"critical_drift_threshold": 0.5,
|
|
7
7
|
"res_threshold": 0.8,
|
|
8
|
-
"active_did": "did:mindforge:
|
|
8
|
+
"active_did": "did:mindforge:7dffeae7-eb70-45c9-9001-701fba7809d2"
|
|
9
9
|
},
|
|
10
10
|
"revops": {
|
|
11
11
|
"market_registry": {
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,50 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [11.5.1] - 2026-06-11 — Robustness + governance-gate patch (Wave 8)
|
|
4
|
+
|
|
5
|
+
A fast-follow patch from a fresh adversarial audit of the shipped v11.5.0 tree.
|
|
6
|
+
Hardens crash-prone JSON parsing in the autonomous/memory pipelines, closes a
|
|
7
|
+
CI governance-gate gap, and tightens two security surfaces. No new features.
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **Crash-proof AUDIT.jsonl parsing** (`bin/memory/pillar-health-tracker.js`) —
|
|
12
|
+
`summarizePhase()` parsed every audit line with an unguarded `JSON.parse`, so a
|
|
13
|
+
single malformed/torn line crashed the knowledge-capture pipeline. Now parses
|
|
14
|
+
per-line in try/catch and skips bad lines.
|
|
15
|
+
- **Crash-proof compaction capture** (`bin/memory/knowledge-capture.js`) — a
|
|
16
|
+
malformed `handoff.json` no longer throws out of `captureFromCompaction()`; it
|
|
17
|
+
logs and returns `[]`, mirroring the missing-file path.
|
|
18
|
+
- **Resilient federated-sync stats** (`bin/memory/federated-sync.js`) — the two
|
|
19
|
+
unguarded `JSON.parse` calls on `sync-stats.json` (`handleSyncFailure`,
|
|
20
|
+
`resetFailures`) now fall back to `{failures:0}` on corruption, matching the
|
|
21
|
+
sibling `getLastSyncTimestamp` pattern.
|
|
22
|
+
|
|
23
|
+
### Security
|
|
24
|
+
|
|
25
|
+
- **CI Tier-3 governance gate now validates content** (`.github/workflows/control-plane.yml`)
|
|
26
|
+
— the gate counted approval files but never checked them; it now requires at
|
|
27
|
+
least one approval with `identity_verification.verified === true` and a
|
|
28
|
+
signature, and rejects any unverified/empty file. Completes the Wave-6
|
|
29
|
+
fail-closed `approve.js` work (a hand-committed empty approval no longer passes).
|
|
30
|
+
- **Dashboard approval attribution** (`bin/dashboard/api-router.js`) —
|
|
31
|
+
`POST /api/approve/:id` no longer records the client-supplied `approver`
|
|
32
|
+
(forgeable audit identity); it attributes the action to a fixed authenticated
|
|
33
|
+
actor. The dashboard remains localhost-bound + token-gated.
|
|
34
|
+
- **Destructive-command detector blocks Unix `truncate`** (`bin/security/trust-boundaries.js`)
|
|
35
|
+
— the SQL-only `truncate table` pattern missed `truncate -s 0 <path>` (in-place
|
|
36
|
+
file zeroing). Added a size-flag pattern so it is gated; benign uses stay allowed.
|
|
37
|
+
- **CI Tier-3 gate accepts an explicitly-acknowledged unverified approval**
|
|
38
|
+
(`.github/workflows/control-plane.yml`, `bin/governance/approve.js`) — since this
|
|
39
|
+
repo has no GPG signing infra, the gate accepts an approval that is either
|
|
40
|
+
GPG-verified OR an opted-in `unverified_ack` record (`approve.js` under
|
|
41
|
+
`MINDFORGE_ALLOW_UNVERIFIED_APPROVAL=1`), while still rejecting bare/stale
|
|
42
|
+
`verified:false` files. Replaced the stale v11.4.0 approval with a fresh one.
|
|
43
|
+
- **`uuid` dependency removed from `ads-engine`** (`bin/review/ads-engine.js`) — it
|
|
44
|
+
required the uninstalled `uuid` package, making `ads-engine` and (transitively)
|
|
45
|
+
`federated-sync` un-loadable in a clean install. Swapped to the built-in
|
|
46
|
+
`crypto.randomUUID()` (zero-native-deps); both modules now load.
|
|
47
|
+
|
|
3
48
|
## [11.5.0] - 2026-06-11 — Governance hardening + autonomous-engine repair (Waves 4–7)
|
|
4
49
|
|
|
5
50
|
This release bundles four waves of work: orchestration primitives, an **inert** manifest
|
package/MINDFORGE.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
# MINDFORGE.md — Parameter Registry (v11.5.
|
|
1
|
+
# MINDFORGE.md — Parameter Registry (v11.5.1)
|
|
2
2
|
|
|
3
3
|
## 1. IDENTITY & VERSIONING
|
|
4
4
|
|
|
5
5
|
[NAME] = MindForge
|
|
6
|
-
[VERSION] = 11.5.
|
|
6
|
+
[VERSION] = 11.5.1
|
|
7
7
|
[STABLE] = true
|
|
8
8
|
[MODE] = "Platform Sovereign"
|
|
9
|
-
[REQUIRED_CORE_VERSION] = 11.5.
|
|
9
|
+
[REQUIRED_CORE_VERSION] = 11.5.1
|
|
10
10
|
[SOVEREIGN_IDENTITY] = true
|
|
11
11
|
[SRE_LAYER_ENABLED] = true
|
|
12
12
|
|
package/README.md
CHANGED
package/RELEASENOTES.md
CHANGED
|
@@ -1,5 +1,50 @@
|
|
|
1
1
|
# Release Notes
|
|
2
2
|
|
|
3
|
+
## v11.5.1 — Robustness + governance-gate patch
|
|
4
|
+
|
|
5
|
+
**Release Date**: 2026-06-11
|
|
6
|
+
**Type**: Patch (no API changes; one CI-gate behavior change)
|
|
7
|
+
**Upgrade Path**: `npx mindforge-cc@latest`
|
|
8
|
+
|
|
9
|
+
A fast-follow patch driven by a fresh adversarial audit of the shipped v11.5.0
|
|
10
|
+
tree. It hardens crash-prone JSON parsing in the autonomous/memory pipelines,
|
|
11
|
+
closes a governance-gate gap left by the v11.5.0 approval work, and tightens two
|
|
12
|
+
security surfaces. No features, no API changes.
|
|
13
|
+
|
|
14
|
+
### Robustness — no more crashes on a torn JSONL line
|
|
15
|
+
|
|
16
|
+
Three pipelines parsed JSON without guards, so one malformed/partially-written
|
|
17
|
+
line could crash them:
|
|
18
|
+
|
|
19
|
+
- `summarizePhase()` (pillar-health) parsed every `AUDIT.jsonl` line unguarded —
|
|
20
|
+
it now skips bad lines and keeps the valid ones.
|
|
21
|
+
- `captureFromCompaction()` (knowledge-capture) now returns `[]` on a malformed
|
|
22
|
+
`handoff.json` instead of throwing.
|
|
23
|
+
- `federated-sync` now tolerates a corrupted `sync-stats.json` in both
|
|
24
|
+
`handleSyncFailure` and `resetFailures` (falls back to `{failures:0}`).
|
|
25
|
+
|
|
26
|
+
### Security & governance
|
|
27
|
+
|
|
28
|
+
- **The CI Tier-3 gate now actually validates approvals.** Previously it only
|
|
29
|
+
counted approval files; a hand-committed empty file would pass. It now requires
|
|
30
|
+
each approval to carry a signature and be EITHER GPG-verified
|
|
31
|
+
(`verified: true`) OR an explicitly opted-in unverified approval
|
|
32
|
+
(`unverified_ack`, minted by `approve.js` under
|
|
33
|
+
`MINDFORGE_ALLOW_UNVERIFIED_APPROVAL=1` for repos without GPG infra). Bare or
|
|
34
|
+
stale `verified:false` files are still rejected — completing the v11.5.0
|
|
35
|
+
fail-closed `approve.js` work.
|
|
36
|
+
- **Dashboard approvals can't forge an identity.** `POST /api/approve/:id` no
|
|
37
|
+
longer records a client-supplied `approver` into the audit trail; it attributes
|
|
38
|
+
the action to a fixed authenticated actor. (The dashboard is already
|
|
39
|
+
localhost-bound and Bearer-token gated, so this is attribution hardening.)
|
|
40
|
+
- **The destructive-command guard now blocks Unix `truncate -s`.** In-place file
|
|
41
|
+
zeroing (`truncate -s 0 <path>`) was missed by the SQL-only pattern; it is now
|
|
42
|
+
gated, with benign uses unaffected.
|
|
43
|
+
- **A shipped module that couldn't load is fixed.** `bin/review/ads-engine.js`
|
|
44
|
+
required the uninstalled `uuid` package — so it (and the `federated-sync` that
|
|
45
|
+
imports it) threw on load in a clean install. Swapped to the built-in
|
|
46
|
+
`crypto.randomUUID()`; no new dependency.
|
|
47
|
+
|
|
3
48
|
## v11.5.0 — Governance hardening + autonomous-engine repair
|
|
4
49
|
|
|
5
50
|
**Release Date**: 2026-06-11
|
|
@@ -75,12 +75,21 @@ function register(app) {
|
|
|
75
75
|
app.post('/api/approve/:id', (req, res) => {
|
|
76
76
|
try {
|
|
77
77
|
const { id } = req.params;
|
|
78
|
-
const { decision, comment
|
|
78
|
+
const { decision, comment } = req.body || {};
|
|
79
79
|
|
|
80
80
|
if (!decision) {
|
|
81
81
|
return res.status(400).json({ error: 'Missing "decision" field (approve|reject)' });
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
// SECURITY (v11.5.1): do NOT trust a client-supplied `approver` for the
|
|
85
|
+
// recorded identity — it is forgeable and would let any caller write a
|
|
86
|
+
// false approval audit trail (e.g. resolved_by: 'admin'). requireAuth
|
|
87
|
+
// (server.js) proves the caller holds the owner-only dashboard token but
|
|
88
|
+
// exposes no named principal, so we attribute the action to a FIXED
|
|
89
|
+
// trusted actor. (A future RBAC pass can map a Bearer token -> DID and
|
|
90
|
+
// record the real principal; until then, never echo req.body.approver.)
|
|
91
|
+
const approver = 'dashboard-authenticated';
|
|
92
|
+
|
|
84
93
|
const result = Approval.processDecision(id, decision, comment, approver);
|
|
85
94
|
|
|
86
95
|
if (!result.success) {
|
|
@@ -59,7 +59,11 @@ function verifyApproverIdentity(approver) {
|
|
|
59
59
|
}
|
|
60
60
|
console.warn('[GOVERNANCE] No GPG key — minting an UNVERIFIED approval (MINDFORGE_ALLOW_UNVERIFIED_APPROVAL=1). ' +
|
|
61
61
|
'git identity is spoofable; this approval is NOT cryptographically attributed.');
|
|
62
|
-
|
|
62
|
+
// unverified_ack=true is the EXPLICIT, audited override the CI Tier-3 gate
|
|
63
|
+
// looks for. A bare verified:false record WITHOUT this marker (e.g. a stale
|
|
64
|
+
// pre-fail-closed file or a hand-forged empty one) is still rejected by the
|
|
65
|
+
// gate — only a deliberately opted-in unverified approval is accepted.
|
|
66
|
+
return { verified: false, method: 'git_identity_unverified', identity: approver, unverified_ack: true };
|
|
63
67
|
}
|
|
64
68
|
|
|
65
69
|
return { verified: true, method: 'gpg_key', identity: approver, keyId: gpgKey };
|
|
@@ -58,7 +58,11 @@ class FederatedSync {
|
|
|
58
58
|
const statsPath = path.join(this.localStore.getPaths().MEMORY_DIR, 'sync-stats.json');
|
|
59
59
|
let stats = { failures: 0 };
|
|
60
60
|
if (fs.existsSync(statsPath)) {
|
|
61
|
-
|
|
61
|
+
try {
|
|
62
|
+
stats = JSON.parse(fs.readFileSync(statsPath, 'utf8'));
|
|
63
|
+
} catch {
|
|
64
|
+
stats = { failures: 0 };
|
|
65
|
+
}
|
|
62
66
|
}
|
|
63
67
|
stats.failures = (stats.failures || 0) + 1;
|
|
64
68
|
stats.last_error = err.message;
|
|
@@ -122,7 +126,12 @@ class FederatedSync {
|
|
|
122
126
|
resetFailures() {
|
|
123
127
|
const statsPath = path.join(this.localStore.getPaths().MEMORY_DIR, 'sync-stats.json');
|
|
124
128
|
if (fs.existsSync(statsPath)) {
|
|
125
|
-
|
|
129
|
+
let stats = { failures: 0 };
|
|
130
|
+
try {
|
|
131
|
+
stats = JSON.parse(fs.readFileSync(statsPath, 'utf8'));
|
|
132
|
+
} catch {
|
|
133
|
+
stats = { failures: 0 };
|
|
134
|
+
}
|
|
126
135
|
stats.failures = 0;
|
|
127
136
|
fs.writeFileSync(statsPath, JSON.stringify(stats, null, 2));
|
|
128
137
|
}
|
|
@@ -238,7 +238,16 @@ function captureFromPhaseCompletion(phaseNum) {
|
|
|
238
238
|
function captureFromCompaction(handoffPath) {
|
|
239
239
|
if (!fs.existsSync(handoffPath)) return [];
|
|
240
240
|
|
|
241
|
-
|
|
241
|
+
let handoff;
|
|
242
|
+
try {
|
|
243
|
+
handoff = JSON.parse(fs.readFileSync(handoffPath, 'utf8'));
|
|
244
|
+
} catch (err) {
|
|
245
|
+
// Malformed handoff.json must not crash the capture pipeline — mirror the
|
|
246
|
+
// missing-file path and return [] after logging the parse failure.
|
|
247
|
+
console.error(`[knowledge-capture] Failed to parse handoff file ${handoffPath}: ${err.message}`);
|
|
248
|
+
return [];
|
|
249
|
+
}
|
|
250
|
+
|
|
242
251
|
const items = handoff.implicit_knowledge || [];
|
|
243
252
|
const project = getProjectName();
|
|
244
253
|
const captured = [];
|
|
@@ -18,7 +18,15 @@ class PillarHealthTracker {
|
|
|
18
18
|
if (!fs.existsSync(auditPath)) return null;
|
|
19
19
|
|
|
20
20
|
const lines = fs.readFileSync(auditPath, 'utf8').trim().split('\n');
|
|
21
|
-
const events = lines
|
|
21
|
+
const events = lines
|
|
22
|
+
.map(l => {
|
|
23
|
+
try {
|
|
24
|
+
return JSON.parse(l);
|
|
25
|
+
} catch {
|
|
26
|
+
return null; // Skip malformed lines rather than crashing the pipeline.
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
.filter(Boolean);
|
|
22
30
|
|
|
23
31
|
// 1. RSA (Mission Fidelity) Analysis
|
|
24
32
|
const rsaEvents = events.filter(e => e.type === 'mission_fidelity' || e.event === 'scs_homing_injected');
|
package/bin/review/ads-engine.js
CHANGED
|
@@ -8,7 +8,7 @@ const fs = require('fs');
|
|
|
8
8
|
const path = require('path');
|
|
9
9
|
const ModelClient = require('../models/model-client');
|
|
10
10
|
const { calculateSoulScore, parseMetrics, synthesizeADSPlan } = require('./ads-synthesizer');
|
|
11
|
-
const
|
|
11
|
+
const crypto = require('crypto');
|
|
12
12
|
|
|
13
13
|
async function runADSSynthesis(params) {
|
|
14
14
|
const {
|
|
@@ -89,7 +89,7 @@ Finalize the PLAN.md. Include the [ADS_VERDICT]: [MERGED|BLUE|RED] (Score: X.XXX
|
|
|
89
89
|
process.stdout.write('done.\n');
|
|
90
90
|
|
|
91
91
|
// Finalize outputs
|
|
92
|
-
const adsUuid =
|
|
92
|
+
const adsUuid = crypto.randomUUID();
|
|
93
93
|
const adrDir = path.join(process.cwd(), '.planning', 'decisions');
|
|
94
94
|
if (!fs.existsSync(adrDir)) fs.mkdirSync(adrDir, { recursive: true });
|
|
95
95
|
|
|
@@ -124,6 +124,11 @@ function isHighImpact(command) {
|
|
|
124
124
|
/git\s+reset\s+--hard/i,
|
|
125
125
|
/delete\s+from/i,
|
|
126
126
|
/truncate\s+table/i,
|
|
127
|
+
// Unix `truncate -s <size> <path>` zeroes/shrinks a file in place — a
|
|
128
|
+
// destructive data-loss op the SQL-only `truncate table` pattern above
|
|
129
|
+
// misses. Match the size flag (-s, -s0, --size) so `truncate -s 0 <path>`
|
|
130
|
+
// is gated; benign words ("truncated output") and the SQL form are not.
|
|
131
|
+
/\btruncate\s+(-{1,2}s\w*|--size)\b/i,
|
|
127
132
|
/\bmkfs(\.\w+)?\s+\/dev\//i,
|
|
128
133
|
// #11: any dd write target, not just /dev/ (dd if=... of=important.db).
|
|
129
134
|
// Original /dev/-only check is a subset of this, so it stays covered.
|
package/package.json
CHANGED