mindforge-cc 11.4.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.
Files changed (98) hide show
  1. package/.agent/CLAUDE.md +13 -0
  2. package/.agent/hooks/lib/hook-flags.js +78 -0
  3. package/.agent/hooks/lib/pretooluse-visible-output.js +46 -0
  4. package/.agent/hooks/mindforge-block-no-verify.js +552 -0
  5. package/.agent/hooks/mindforge-config-protection.js +144 -0
  6. package/.agent/hooks/run-with-flags.js +207 -0
  7. package/.agent/mindforge/checkpoint.md +76 -0
  8. package/.agent/mindforge/harness-audit.md +59 -0
  9. package/.agent/mindforge/instinct.md +46 -0
  10. package/.agent/mindforge/orch-add-feature.md +43 -0
  11. package/.agent/mindforge/orch-build-mvp.md +48 -0
  12. package/.agent/mindforge/orch-change-feature.md +45 -0
  13. package/.agent/mindforge/orch-fix-defect.md +43 -0
  14. package/.agent/mindforge/orch-refine-code.md +43 -0
  15. package/.claude/CLAUDE.md +13 -0
  16. package/.claude/commands/mindforge/checkpoint.md +76 -0
  17. package/.claude/commands/mindforge/execute-phase.md +47 -6
  18. package/.claude/commands/mindforge/harness-audit.md +59 -0
  19. package/.claude/commands/mindforge/instinct.md +46 -0
  20. package/.claude/commands/mindforge/orch-add-feature.md +43 -0
  21. package/.claude/commands/mindforge/orch-build-mvp.md +48 -0
  22. package/.claude/commands/mindforge/orch-change-feature.md +45 -0
  23. package/.claude/commands/mindforge/orch-fix-defect.md +43 -0
  24. package/.claude/commands/mindforge/orch-refine-code.md +43 -0
  25. package/.claude/commands/mindforge/plan-write.md +11 -0
  26. package/.claude/commands/mindforge/product-spec.md +76 -0
  27. package/.mindforge/config.json +2 -2
  28. package/.mindforge/engine/instincts/instinct-schema.md +17 -9
  29. package/.mindforge/imported-agents.jsonl +10 -0
  30. package/.mindforge/manifests/install-components.json +36 -0
  31. package/.mindforge/manifests/install-modules.json +193 -0
  32. package/.mindforge/manifests/install-profiles.json +57 -0
  33. package/.mindforge/memory/sync-manifest.json +1 -1
  34. package/.mindforge/personas/gan-evaluator.md +226 -0
  35. package/.mindforge/personas/gan-generator.md +151 -0
  36. package/.mindforge/personas/gan-planner.md +118 -0
  37. package/.mindforge/personas/harness-optimizer.md +55 -0
  38. package/.mindforge/personas/loop-operator.md +58 -0
  39. package/.mindforge/schemas/hooks.schema.json +199 -0
  40. package/.mindforge/schemas/install-modules.schema.json +44 -0
  41. package/.mindforge/schemas/install-state.schema.json +95 -0
  42. package/.mindforge/schemas/plugin.schema.json +75 -0
  43. package/.mindforge/schemas/provenance.schema.json +31 -0
  44. package/.mindforge/skills/agent-architecture-audit/SKILL.md +272 -0
  45. package/.mindforge/skills/continuous-learning/SKILL.md +16 -0
  46. package/.mindforge/skills/orch-pipeline/SKILL.md +284 -0
  47. package/.mindforge/skills/writing-plans/SKILL.md +76 -0
  48. package/CHANGELOG.md +120 -0
  49. package/MINDFORGE.md +3 -3
  50. package/README.md +0 -1
  51. package/RELEASENOTES.md +131 -0
  52. package/SECURITY.md +16 -0
  53. package/bin/autonomous/auto-runner.js +46 -5
  54. package/bin/autonomous/handoff-schema.js +114 -0
  55. package/bin/autonomous/session-guardian.sh +138 -0
  56. package/bin/autonomous/supervisor.js +98 -0
  57. package/bin/change-classifier.js +19 -5
  58. package/bin/dashboard/api-router.js +10 -1
  59. package/bin/governance/approve.js +65 -28
  60. package/bin/governance/config-manager.js +3 -1
  61. package/bin/governance/rbac-manager.js +14 -6
  62. package/bin/harness-audit.js +520 -0
  63. package/bin/hooks/instinct-capture-hook.js +16 -1
  64. package/bin/hooks/lib/detect-project.js +72 -0
  65. package/bin/installer/harness-adapter-compliance.js +321 -0
  66. package/bin/installer/install-manifests.js +200 -0
  67. package/bin/installer/install-state.js +243 -0
  68. package/bin/installer-core.js +1 -1
  69. package/bin/learning/instinct-cli.js +359 -0
  70. package/bin/learning/lib/ssrf-guard.js +252 -0
  71. package/bin/memory/eis-client.js +31 -10
  72. package/bin/memory/federated-sync.js +11 -2
  73. package/bin/memory/knowledge-capture.js +10 -1
  74. package/bin/memory/pillar-health-tracker.js +9 -1
  75. package/bin/models/llm-errors.js +79 -0
  76. package/bin/models/model-client.js +39 -4
  77. package/bin/models/ollama-provider.js +115 -0
  78. package/bin/models/openai-provider.js +40 -9
  79. package/bin/models/profiles-loader.js +147 -0
  80. package/bin/models/provider-registry.js +59 -0
  81. package/bin/review/ads-engine.js +2 -2
  82. package/bin/revops/market-evaluator.js +23 -2
  83. package/bin/revops/router-steering-v2.js +17 -2
  84. package/bin/security/trust-boundaries.js +20 -3
  85. package/bin/utils/readiness-gate.js +169 -0
  86. package/bin/worktree/engine.js +497 -0
  87. package/package.json +8 -2
  88. package/subagents/categories/04-quality-security/.claude-plugin/plugin.json +10 -0
  89. package/subagents/categories/04-quality-security/go-build-resolver.md +105 -0
  90. package/subagents/categories/04-quality-security/go-reviewer.md +87 -0
  91. package/subagents/categories/04-quality-security/python-reviewer.md +109 -0
  92. package/subagents/categories/04-quality-security/react-build-resolver.md +215 -0
  93. package/subagents/categories/04-quality-security/react-reviewer.md +167 -0
  94. package/subagents/categories/04-quality-security/rust-build-resolver.md +159 -0
  95. package/subagents/categories/04-quality-security/rust-reviewer.md +105 -0
  96. package/subagents/categories/04-quality-security/silent-failure-hunter.md +67 -0
  97. package/subagents/categories/04-quality-security/type-design-analyzer.md +58 -0
  98. package/subagents/categories/04-quality-security/typescript-reviewer.md +126 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,125 @@
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
+
48
+ ## [11.5.0] - 2026-06-11 — Governance hardening + autonomous-engine repair (Waves 4–7)
49
+
50
+ This release bundles four waves of work: orchestration primitives, an **inert** manifest
51
+ engine + the new instinct CLI + GAN harness personas, governance/security hardening, and a
52
+ repair that makes the autonomous engine (`/mindforge:auto`) actually functional. Several
53
+ items ship deliberately **inert** (scaffolding present, not wired into any live path) and
54
+ are flagged as such below — they introduce **no behavior change yet**.
55
+
56
+ ### Added
57
+
58
+ - **Instinct CLI** (`/mindforge:instinct`, `bin/learning/instinct-cli.js`) — deterministic,
59
+ no-LLM management of the JSONL instinct store: `list`, `export`, `import`,
60
+ `promote-candidates` (list + flag), and `prune`.
61
+ - **Typed Inter-Agent Message Protocol** (`bin/autonomous/handoff-schema.js`) — five message
62
+ kinds (`task_handoff`, `query`, `response`, `completed`, `conflict`) with priority levels
63
+ (`low`/`normal`/`high`/`critical`) and message validation. Internal orchestration primitive.
64
+ - **Manifest-driven install resolver** (`bin/installer/install-manifests.js`,
65
+ `.mindforge/manifests/install-*.json`) — profile-to-module expansion and dependency
66
+ detection. Ships **INERT**: the adapter wiring it into `bin/installer-core.js`'s live
67
+ `install()` path is deferred to a future PR. No behavior change.
68
+ - **GAN-style harness personas** (`.mindforge/personas/gan-evaluator.md`, `gan-generator.md`,
69
+ `gan-planner.md`) — fully scoped and documented. Ships **INERT**: not wired to any live
70
+ command or automated workflow. No behavior change.
71
+ - **Governance test coverage** — `tests/trust-verifier.test.js` (7 tests) and
72
+ `tests/rbac-manager.test.js` (8 tests) lock the fail-closed contracts for future
73
+ safety-critical governance changes.
74
+
75
+ ### Fixed
76
+
77
+ - **SSRF import-URL guard** (`bin/learning/lib/ssrf-guard.js`) — closed an IPv6 link-local
78
+ bypass (`fe81`–`fe8f` reachable via string-prefix check) and a symlink path-traversal
79
+ bypass, using numeric bitmask validation and canonical-path checking before system-dir
80
+ validation.
81
+ - **Federated EIS sync** (`bin/memory/eis-client.js`) — `getAuthHeader` no longer throws on
82
+ every call; it now registers a node identity via `ZTAI.registerAgent` and signs with
83
+ `ZTAI.signData`, restoring sync to non-localhost EIS endpoints.
84
+ - **RBAC tier elevation** (`bin/governance/rbac-manager.js`) — `getRolesByTier` now fails
85
+ safely for unregistered agents (no thrown exception) and resolves agent tier via the
86
+ correct ZTAI API.
87
+ - **Autonomous engine — wave crash** (`bin/autonomous/auto-runner.js`) — replaced ZTAI
88
+ singleton misuse (`getIdentity()`, non-existent) with `_getRunnerIdentity()` using the
89
+ real `registerAgent` API; `/mindforge:auto` no longer crashes on every wave.
90
+ - **Autonomous policy gate — fail-open** (`bin/autonomous/auto-runner.js`) — the async
91
+ policy verdict is now `await`ed (`this.policyEngine.evaluate(intent)`); the per-wave gate
92
+ previously always allowed.
93
+ - **Autonomous engine — fail-closed identity** (`bin/autonomous/auto-runner.js`) — if runner
94
+ identity cannot be established, the policy gate now denies and audits (`auto_mode_denied`)
95
+ instead of proceeding ungoverned.
96
+
97
+ ### Changed
98
+
99
+ - **Instinct store schema** (`.mindforge/engine/instincts/instinct-schema.md`) — added
100
+ `project_id` (stable scope key) and `source` (`auto-capture`/`manual`/`imported`/
101
+ `observer`), plus origin-based confidence scoring: auto-capture starts at `0.3`, manual
102
+ at `0.7`.
103
+ - **Cost-routing shadow mode** (`bin/revops/router-steering-v2.js`) — arbitrage steering now
104
+ respects `cost_routing.shadow_mode` (default `true`); in observe-only mode `steer()`
105
+ returns `{ shadow, authoritative: false }` with SHADOW vs LIVE logging.
106
+
107
+ ### Security
108
+
109
+ - **Tier-3 approvals now fail closed** (`bin/governance/approve.js`) — approvals require GPG
110
+ verification and **throw before writing any record** when no GPG key is configured, unless
111
+ `MINDFORGE_ALLOW_UNVERIFIED_APPROVAL=1` is set; unverified approvals are marked
112
+ `verified: false`.
113
+ - **Persona supply-chain scan** (`scripts/ci/validate-assets.js`) — persona asset validation
114
+ now detects dangerous invisible unicode (zero-width, bidi overrides, Unicode tags) across
115
+ `.mindforge/personas` to prevent ASCII-smuggling injection.
116
+ - **Destructive-command detector** (`bin/security/trust-boundaries.js`) — `normalizeShell`
117
+ now strips bare `#` tokens after quote-stripping, blocking quoted-hash evasion (e.g.
118
+ `rm "#" -rf /`).
119
+ - **Instinct import port allowlist** (`bin/learning/lib/ssrf-guard.js`) — `validateImportUrl`
120
+ enforces an `ALLOWED_IMPORT_PORTS` allowlist (`{'', '443'}`), blocking attempts to reach
121
+ internal services (Redis `:6379`, Mongo `:27017`, etc.) on otherwise-allowed public hosts.
122
+
3
123
  ## [11.4.0] - 2026-06-06 — Claude Code plugin distribution
4
124
 
5
125
  MindForge is now installable as a native **Claude Code plugin** from a marketplace, in
package/MINDFORGE.md CHANGED
@@ -1,12 +1,12 @@
1
- # MINDFORGE.md — Parameter Registry (v11.4.0)
1
+ # MINDFORGE.md — Parameter Registry (v11.5.1)
2
2
 
3
3
  ## 1. IDENTITY & VERSIONING
4
4
 
5
5
  [NAME] = MindForge
6
- [VERSION] = 11.4.0
6
+ [VERSION] = 11.5.1
7
7
  [STABLE] = true
8
8
  [MODE] = "Platform Sovereign"
9
- [REQUIRED_CORE_VERSION] = 11.4.0
9
+ [REQUIRED_CORE_VERSION] = 11.5.1
10
10
  [SOVEREIGN_IDENTITY] = true
11
11
  [SRE_LAYER_ENABLED] = true
12
12
 
package/README.md CHANGED
@@ -501,4 +501,3 @@ MindForge never stores credentials in files. Review:
501
501
 
502
502
  ## ⚖️ License
503
503
  MIT © 2026 MindForge Team
504
- eam
package/RELEASENOTES.md CHANGED
@@ -1,5 +1,136 @@
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
+
48
+ ## v11.5.0 — Governance hardening + autonomous-engine repair
49
+
50
+ **Release Date**: 2026-06-11
51
+ **Type**: Minor (one behavior change — see "Heads-up" below)
52
+ **Upgrade Path**: `npx mindforge-cc@latest`
53
+
54
+ This release consolidates four waves of work into a single minor: new orchestration and
55
+ learning primitives, a broad governance/security hardening pass, and — most importantly —
56
+ a repair that takes the autonomous engine from "crashes on every wave" to actually
57
+ functional. Some new pieces ship deliberately **inert** (present but not wired in); those
58
+ are called out so you know not to expect new behavior from them yet.
59
+
60
+ ### Heads-up — Tier-3 approvals now fail closed (the one behavior change)
61
+
62
+ `bin/governance/approve.js` no longer rubber-stamps approvals. Tier-3 approvals now
63
+ **require GPG verification** and will **fail before writing any record** if no GPG key is
64
+ configured. If you relied on the old, unverified path, you have two choices:
65
+
66
+ - Configure a GPG key (recommended), or
67
+ - Set `MINDFORGE_ALLOW_UNVERIFIED_APPROVAL=1` to keep the old behavior — in which case the
68
+ approval record is written with `verified: false` so the gap is auditable.
69
+
70
+ This is the only item in v11.5.0 that can change an existing workflow's outcome.
71
+
72
+ ### The autonomous engine is functional again
73
+
74
+ `/mindforge:auto` was effectively broken. Three fixes in `bin/autonomous/auto-runner.js`
75
+ bring it back:
76
+
77
+ - **No more per-wave crash.** The runner was calling a non-existent `getIdentity()` on the
78
+ ZTAI singleton and dying on every wave. It now establishes identity through the real
79
+ `registerAgent` API (`_getRunnerIdentity()`).
80
+ - **The policy gate actually enforces now.** The async policy verdict was never `await`ed,
81
+ so the gate silently allowed everything. It now awaits `policyEngine.evaluate(intent)` on
82
+ every wave.
83
+ - **It fails closed.** If runner identity can't be established, the gate now **denies and
84
+ audits** (`auto_mode_denied`) rather than running ungoverned.
85
+
86
+ ### Security & governance hardening
87
+
88
+ - **SSRF guard hardened (twice).** The import-URL guard now closes an IPv6 link-local
89
+ bypass and a symlink path-traversal bypass via numeric bitmask + canonical-path checks.
90
+ Separately, remote instinct imports now enforce a port allowlist (`443`/none only), so an
91
+ attacker can no longer pivot through an allowed public host to reach internal services
92
+ like Redis (`:6379`) or Mongo (`:27017`).
93
+ - **Persona supply-chain scan.** Persona asset validation now flags dangerous invisible
94
+ unicode (zero-width, bidi overrides, Unicode tags) across `.mindforge/personas`, closing
95
+ an ASCII-smuggling injection vector.
96
+ - **Destructive-command detector closed an evasion.** Quoted-hash tricks like
97
+ `rm "#" -rf /` are now caught.
98
+ - **Federated memory sync works again.** `eis-client`'s `getAuthHeader` was throwing on
99
+ every call; it now correctly registers a node identity and signs requests, so sync to
100
+ non-localhost EIS endpoints functions.
101
+ - **RBAC fails safe.** Tier elevation no longer throws for unregistered agents and resolves
102
+ tiers through the correct ZTAI API.
103
+ - **Fail-closed contracts are now tested.** New `trust-verifier` and `rbac-manager` test
104
+ suites lock in identity-verification and tier-authorization behavior so future governance
105
+ changes can't quietly regress them.
106
+
107
+ ### Learning & cost routing
108
+
109
+ - **New instinct CLI** — `/mindforge:instinct` manages the JSONL instinct store
110
+ deterministically (no LLM spawn): `list`, `export`, `import`, `promote-candidates`, and
111
+ `prune`.
112
+ - **Instinct store schema** gains `project_id` (stable scoping) and a `source` field
113
+ (`auto-capture`/`manual`/`imported`/`observer`), with origin-weighted confidence —
114
+ auto-captured instincts start at `0.3`, manually added ones at `0.7`.
115
+ - **Cost-routing shadow mode is now real.** Arbitrage steering respects
116
+ `cost_routing.shadow_mode` (default on); in observe-only mode, selections are returned as
117
+ `authoritative: false` and logged as SHADOW, so you can watch the router's
118
+ recommendations without it taking the wheel.
119
+
120
+ ### Shipped inert (no behavior change yet)
121
+
122
+ These landed as scaffolding and are **not** wired into any live path — they do nothing
123
+ until a follow-up enables them:
124
+
125
+ - **Manifest-driven install resolver** (`install-manifests.js`) — profile-to-module
126
+ expansion and dependency detection are implemented, but the adapter into the installer's
127
+ live `install()` path is deferred.
128
+ - **GAN-style harness personas** (`gan-evaluator`, `gan-generator`, `gan-planner`) — fully
129
+ scoped and documented, not yet attached to any command or workflow.
130
+ - **Typed Inter-Agent Message Protocol** (`handoff-schema.js`) — an internal orchestration
131
+ primitive (five message kinds, four priority levels, validation) for upcoming
132
+ agent-handoff work.
133
+
3
134
  ## v11.3.1 — Packaging hotfix
4
135
 
5
136
  **Release Date**: 2026-06-05
package/SECURITY.md CHANGED
@@ -108,6 +108,22 @@ Before submitting code that touches security-sensitive paths:
108
108
 
109
109
  ---
110
110
 
111
+ ## Agentic-Harness Threat Model
112
+
113
+ This document covers application/code vulnerabilities. The **outward** harness threat
114
+ model — prompt injection, poisoned project config / hooks / MCP, supply-chain risk in
115
+ skills/agents, the lethal trifecta, sandboxing, and the autonomous-agent minimum-bar
116
+ checklist — lives in **[MINDFORGE-AGENTIC-SECURITY.md](./MINDFORGE-AGENTIC-SECURITY.md)**.
117
+ Both are required reading before running MindForge autonomously.
118
+
119
+ Minimum bar (see that doc for detail): separate agent identities · short-lived scoped
120
+ creds · sandbox untrusted work · deny egress by default · `permissions.deny` on
121
+ secret-bearing paths · sanitize foreign content · human approval for shell/egress/deploy
122
+ (TrustGate + Tier-3) · log tool calls (AUDIT.jsonl) · process-group kill + heartbeat ·
123
+ narrow disposable memory · scan skills/hooks/MCP/agents as supply-chain artifacts.
124
+
125
+ ---
126
+
111
127
  ## Known Mitigations & Limitations
112
128
 
113
129
  - **ZK-proofs are simulated** — The Dilithium-5 / ZK-proof layer uses cryptographic simulation, not hardware-backed TEEs. It provides logical governance enforcement, not hardware-grade isolation.
@@ -687,12 +687,53 @@ class AutoRunner {
687
687
  fs.writeFileSync(this.statePath, JSON.stringify(state, null, 2));
688
688
  }
689
689
 
690
+ /**
691
+ * Lazily registers (once) the autonomous runner's own ZTAI identity and
692
+ * returns { did, tier }. ztai-manager is a SINGLETON (not a constructor) and
693
+ * exposes no getIdentity() — the runner must register a DID to obtain one.
694
+ * Cached on the instance so every wave evaluates under one stable identity.
695
+ * Tier 3: autonomous phase processing is a high-trust operation; the policy
696
+ * engine still runs its own blast-radius analysis on top, so this is an INPUT
697
+ * to evaluation, not a self-granted bypass.
698
+ */
699
+ async _getRunnerIdentity() {
700
+ if (!this._runnerIdentity) {
701
+ _ZTAIManager = lazyRequire(_ZTAIManager, '../governance/ztai-manager');
702
+ const did = await _ZTAIManager.registerAgent(
703
+ `auto-runner:${process.env.MF_PROJECT_ID || 'MF-ALPHA'}:phase-${this.phase}`,
704
+ 3,
705
+ this._sessionId
706
+ );
707
+ const agent = _ZTAIManager.getAgent(did);
708
+ this._runnerIdentity = { did, tier: agent && typeof agent.tier === 'number' ? agent.tier : 3 };
709
+ }
710
+ return this._runnerIdentity;
711
+ }
712
+
690
713
  async evaluateWavePolicy() {
691
- _ZTAIManager = lazyRequire(_ZTAIManager, '../governance/ztai-manager');
692
- const manager = _ZTAIManager;
693
- const identity = await manager.getIdentity();
694
- const intent = { did: identity.did, action: 'process_phase_wave', resource: `projects/${process.env.MF_PROJECT_ID || 'MF-ALPHA'}/phases/${this.phase}/*`, tier: identity.tier || 1, metadata: { engine: 'Nimbus-S4', mode: 'autonomous', wave_timestamp: new Date().toISOString() } };
695
- const result = this.policyEngine.evaluate(intent);
714
+ let identity;
715
+ try {
716
+ identity = await this._getRunnerIdentity();
717
+ } catch (err) {
718
+ // Fail CLOSED: if the runner cannot establish a verifiable identity, deny
719
+ // the wave rather than proceeding ungoverned.
720
+ console.warn(`[APO-DENY] Could not establish runner identity: ${err.message}`);
721
+ this.writeAudit({ event: 'auto_mode_denied', reason: `identity unavailable: ${err.message}`, phase: this.phase });
722
+ return false;
723
+ }
724
+
725
+ const intent = {
726
+ did: identity.did,
727
+ action: 'process_phase_wave',
728
+ resource: `projects/${process.env.MF_PROJECT_ID || 'MF-ALPHA'}/phases/${this.phase}/*`,
729
+ tier: identity.tier,
730
+ sessionId: this._sessionId,
731
+ metadata: { engine: 'Nimbus-S4', mode: 'autonomous', wave_timestamp: new Date().toISOString() }
732
+ };
733
+
734
+ // policyEngine.evaluate is ASYNC — must be awaited, or `result` is a Promise
735
+ // and `result.verdict === 'DENY'` is always false (the gate never fires).
736
+ const result = await this.policyEngine.evaluate(intent);
696
737
  if (result.verdict === 'DENY') { console.warn(`[APO-DENY] ${result.reason}`); return false; }
697
738
  return true;
698
739
  }
@@ -0,0 +1,114 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * MindForge — Typed inter-agent message protocol.
5
+ *
6
+ * Ports ECC's ecc2/src/comms/mod.rs MessageType + TaskPriority as a JSON-schema
7
+ * validator. Complements state-manager.js validateHandoff (which validates the
8
+ * HANDOFF.json envelope) by typing the individual messages agents exchange:
9
+ *
10
+ * kinds: task_handoff | query | response | completed | conflict
11
+ * priority: low | normal | high | critical (default normal; legacy fallback)
12
+ *
13
+ * The Conflict kind pairs with the worktree engine's merge-readiness output
14
+ * (bin/worktree/engine.js). This is discipline/typing, not new runtime behavior.
15
+ */
16
+
17
+ const MESSAGE_KINDS = ['task_handoff', 'query', 'response', 'completed', 'conflict'];
18
+ const PRIORITIES = ['low', 'normal', 'high', 'critical'];
19
+ const DEFAULT_PRIORITY = 'normal';
20
+
21
+ // Required fields per kind (mirrors ECC's MessageType variants).
22
+ const REQUIRED_FIELDS = {
23
+ task_handoff: ['task', 'context'],
24
+ query: ['question'],
25
+ response: ['answer'],
26
+ completed: ['summary'], // files_changed optional, defaults []
27
+ conflict: ['file', 'description'],
28
+ };
29
+
30
+ /**
31
+ * Validate a typed message object. Returns { valid, warnings }.
32
+ * Fail-open style (matches validateHandoff): collects warnings, never throws.
33
+ */
34
+ function validateMessage(msg) {
35
+ const warnings = [];
36
+ if (!msg || typeof msg !== 'object' || Array.isArray(msg)) {
37
+ return { valid: false, warnings: ['message is not an object'] };
38
+ }
39
+ if (!MESSAGE_KINDS.includes(msg.kind)) {
40
+ warnings.push(`invalid kind: "${msg.kind}". Expected one of: ${MESSAGE_KINDS.join(', ')}`);
41
+ return { valid: false, warnings };
42
+ }
43
+ for (const field of REQUIRED_FIELDS[msg.kind]) {
44
+ if (typeof msg[field] !== 'string' || msg[field].length === 0) {
45
+ warnings.push(`${msg.kind} missing required string field: ${field}`);
46
+ }
47
+ }
48
+ if (msg.kind === 'completed' && msg.files_changed !== undefined && !Array.isArray(msg.files_changed)) {
49
+ warnings.push('completed.files_changed must be an array');
50
+ }
51
+ if (msg.kind === 'task_handoff' && msg.priority !== undefined && !PRIORITIES.includes(msg.priority)) {
52
+ warnings.push(`invalid priority: "${msg.priority}". Expected one of: ${PRIORITIES.join(', ')}`);
53
+ }
54
+ return { valid: warnings.length === 0, warnings };
55
+ }
56
+
57
+ /**
58
+ * Resolve a handoff message's priority, defaulting to "normal" and tolerating
59
+ * legacy entries that lack a typed priority (ECC's legacy fallback).
60
+ */
61
+ function handoffPriority(msg) {
62
+ if (!msg || typeof msg !== 'object') return DEFAULT_PRIORITY;
63
+ const p = msg.priority;
64
+ return PRIORITIES.includes(p) ? p : DEFAULT_PRIORITY;
65
+ }
66
+
67
+ /**
68
+ * One-line human preview of a typed message (for status/log surfaces).
69
+ */
70
+ function preview(msg) {
71
+ const trunc = (s, n) => {
72
+ const t = String(s || '').trim();
73
+ return t.length <= n ? t : `${t.slice(0, n - 1)}…`;
74
+ };
75
+ switch (msg && msg.kind) {
76
+ case 'task_handoff': {
77
+ const p = handoffPriority(msg);
78
+ return p === DEFAULT_PRIORITY
79
+ ? `handoff ${trunc(msg.task, 56)}`
80
+ : `handoff [${p}] ${trunc(msg.task, 48)}`;
81
+ }
82
+ case 'query': return `query ${trunc(msg.question, 56)}`;
83
+ case 'response': return `response ${trunc(msg.answer, 56)}`;
84
+ case 'completed': {
85
+ const n = Array.isArray(msg.files_changed) ? msg.files_changed.length : 0;
86
+ return n === 0 ? `completed ${trunc(msg.summary, 48)}` : `completed ${trunc(msg.summary, 40)} | ${n} files`;
87
+ }
88
+ case 'conflict': return `conflict ${msg.file} | ${trunc(msg.description, 40)}`;
89
+ default: return `unknown ${trunc(JSON.stringify(msg), 56)}`;
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Build a well-formed message of a given kind (convenience + normalization).
95
+ * Throws if the result fails validation, so callers can't emit a bad message.
96
+ */
97
+ function makeMessage(kind, fields = {}) {
98
+ const msg = Object.assign({ kind }, fields);
99
+ if (kind === 'task_handoff' && msg.priority === undefined) msg.priority = DEFAULT_PRIORITY;
100
+ if (kind === 'completed' && msg.files_changed === undefined) msg.files_changed = [];
101
+ const { valid, warnings } = validateMessage(msg);
102
+ if (!valid) throw new Error(`invalid ${kind} message: ${warnings.join('; ')}`);
103
+ return msg;
104
+ }
105
+
106
+ module.exports = {
107
+ MESSAGE_KINDS,
108
+ PRIORITIES,
109
+ DEFAULT_PRIORITY,
110
+ validateMessage,
111
+ handoffPriority,
112
+ preview,
113
+ makeMessage,
114
+ };
@@ -0,0 +1,138 @@
1
+ #!/usr/bin/env bash
2
+ # session-guardian.sh — Autonomous-loop session guard (MindForge)
3
+ # Exit 0 = proceed. Exit 1 = skip this loop cycle.
4
+ # Called by any bin/autonomous loop (and the deferred background observer)
5
+ # BEFORE spawning a model session, so the loop cannot burn tokens overnight,
6
+ # AFK, or faster than its cooldown.
7
+ #
8
+ # Ported near-verbatim from ECC (continuous-learning-v2/agents/session-guardian.sh).
9
+ # Env defaults map to a new instincts.observer config block (default-off posture):
10
+ # OBSERVER_INTERVAL_SECONDS default: 300 (per-project cooldown)
11
+ # OBSERVER_LAST_RUN_LOG default: ~/.mindforge/observer-last-run.log
12
+ # OBSERVER_ACTIVE_HOURS_START default: 800 (8:00 AM local, 0 to disable)
13
+ # OBSERVER_ACTIVE_HOURS_END default: 2300 (11:00 PM local, 0 to disable)
14
+ # OBSERVER_MAX_IDLE_SECONDS default: 1800 (30 min; 0 to disable)
15
+ #
16
+ # Gate order (cheapest first):
17
+ # Gate 1: Time window check (~0ms)
18
+ # Gate 2: Project cooldown log (~1ms, mkdir lock)
19
+ # Gate 3: Idle detection (~5-50ms, OS syscall; fail open)
20
+
21
+ set -euo pipefail
22
+
23
+ INTERVAL="${OBSERVER_INTERVAL_SECONDS:-300}"
24
+ LOG_PATH="${OBSERVER_LAST_RUN_LOG:-$HOME/.mindforge/observer-last-run.log}"
25
+ ACTIVE_START="${OBSERVER_ACTIVE_HOURS_START:-800}"
26
+ ACTIVE_END="${OBSERVER_ACTIVE_HOURS_END:-2300}"
27
+ MAX_IDLE="${OBSERVER_MAX_IDLE_SECONDS:-1800}"
28
+
29
+ # ── Gate 1: Time Window ───────────────────────────────────────────────────────
30
+ if [ "$ACTIVE_START" -ne 0 ] || [ "$ACTIVE_END" -ne 0 ]; then
31
+ current_hhmm=$(date +%k%M | tr -d ' ')
32
+ current_hhmm_num=$(( 10#${current_hhmm:-0} ))
33
+ active_start_num=$(( 10#${ACTIVE_START:-800} ))
34
+ active_end_num=$(( 10#${ACTIVE_END:-2300} ))
35
+
36
+ within_active_hours=0
37
+ if [ "$active_start_num" -lt "$active_end_num" ]; then
38
+ if [ "$current_hhmm_num" -ge "$active_start_num" ] && [ "$current_hhmm_num" -lt "$active_end_num" ]; then
39
+ within_active_hours=1
40
+ fi
41
+ else
42
+ if [ "$current_hhmm_num" -ge "$active_start_num" ] || [ "$current_hhmm_num" -lt "$active_end_num" ]; then
43
+ within_active_hours=1
44
+ fi
45
+ fi
46
+
47
+ if [ "$within_active_hours" -ne 1 ]; then
48
+ echo "session-guardian: outside active hours (${current_hhmm}, window ${ACTIVE_START}-${ACTIVE_END})" >&2
49
+ exit 1
50
+ fi
51
+ fi
52
+
53
+ # ── Gate 2: Project Cooldown Log ─────────────────────────────────────────────
54
+ project_root="${PROJECT_DIR:-}"
55
+ if [ -z "$project_root" ] || [ ! -d "$project_root" ]; then
56
+ project_root="$(git rev-parse --show-toplevel 2>/dev/null || echo "$PWD")"
57
+ fi
58
+ project_name="$(basename "$project_root")"
59
+ now="$(date +%s)"
60
+
61
+ mkdir -p "$(dirname "$LOG_PATH")" || {
62
+ echo "session-guardian: cannot create log dir, proceeding" >&2
63
+ exit 0
64
+ }
65
+
66
+ _lock_dir="${LOG_PATH}.lock"
67
+ if ! mkdir "$_lock_dir" 2>/dev/null; then
68
+ echo "session-guardian: log locked by concurrent process, skipping cycle" >&2
69
+ exit 1
70
+ else
71
+ trap 'rm -rf "$_lock_dir"' EXIT INT TERM
72
+
73
+ last_spawn=0
74
+ last_spawn=$(awk -F '\t' -v key="$project_root" '$1 == key { value = $2 } END { if (value != "") print value }' "$LOG_PATH" 2>/dev/null) || true
75
+ last_spawn="${last_spawn:-0}"
76
+ [[ "$last_spawn" =~ ^[0-9]+$ ]] || last_spawn=0
77
+
78
+ elapsed=$(( now - last_spawn ))
79
+ if [ "$elapsed" -lt "$INTERVAL" ]; then
80
+ rm -rf "$_lock_dir"
81
+ trap - EXIT INT TERM
82
+ echo "session-guardian: cooldown active for '${project_name}' (last spawn ${elapsed}s ago, interval ${INTERVAL}s)" >&2
83
+ exit 1
84
+ fi
85
+
86
+ tmp_log="$(mktemp "$(dirname "$LOG_PATH")/observer-last-run.XXXXXX")"
87
+ awk -F '\t' -v key="$project_root" '$1 != key' "$LOG_PATH" > "$tmp_log" 2>/dev/null || true
88
+ printf '%s\t%s\n' "$project_root" "$now" >> "$tmp_log"
89
+ mv "$tmp_log" "$LOG_PATH"
90
+
91
+ rm -rf "$_lock_dir"
92
+ trap - EXIT INT TERM
93
+ fi
94
+
95
+ # ── Gate 3: Idle Detection ────────────────────────────────────────────────────
96
+ get_idle_seconds() {
97
+ local _raw
98
+ case "$(uname -s)" in
99
+ Darwin)
100
+ _raw=$( { /usr/sbin/ioreg -c IOHIDSystem \
101
+ | /usr/bin/awk '/HIDIdleTime/ {print int($NF/1000000000); exit}'; } \
102
+ 2>/dev/null ) || true
103
+ printf '%s\n' "${_raw:-0}" | head -n1
104
+ ;;
105
+ Linux)
106
+ if command -v xprintidle >/dev/null 2>&1; then
107
+ _raw=$(xprintidle 2>/dev/null) || true
108
+ echo $(( ${_raw:-0} / 1000 ))
109
+ else
110
+ echo 0 # fail open: xprintidle not installed
111
+ fi
112
+ ;;
113
+ *MINGW*|*MSYS*|*CYGWIN*)
114
+ _raw=$(powershell.exe -NoProfile -NonInteractive -Command \
115
+ "try { \
116
+ Add-Type -MemberDefinition '[DllImport(\"user32.dll\")] public static extern bool GetLastInputInfo(ref LASTINPUTINFO p); [StructLayout(LayoutKind.Sequential)] public struct LASTINPUTINFO { public uint cbSize; public int dwTime; }' -Name WinAPI -Namespace PInvoke; \
117
+ \$l = New-Object PInvoke.WinAPI+LASTINPUTINFO; \$l.cbSize = 8; \
118
+ [PInvoke.WinAPI]::GetLastInputInfo([ref]\$l) | Out-Null; \
119
+ [int][Math]::Max(0, [long]([Environment]::TickCount - [long]\$l.dwTime) / 1000) \
120
+ } catch { 0 }" \
121
+ 2>/dev/null | tr -d '\r') || true
122
+ printf '%s\n' "${_raw:-0}" | head -n1
123
+ ;;
124
+ *)
125
+ echo 0 # fail open: unknown platform
126
+ ;;
127
+ esac
128
+ }
129
+
130
+ if [ "$MAX_IDLE" -gt 0 ]; then
131
+ idle_seconds=$(get_idle_seconds)
132
+ if [ "$idle_seconds" -gt "$MAX_IDLE" ]; then
133
+ echo "session-guardian: user idle ${idle_seconds}s (threshold ${MAX_IDLE}s), skipping" >&2
134
+ exit 1
135
+ fi
136
+ fi
137
+
138
+ exit 0