mindforge-cc 11.9.2 → 11.9.3
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/CLAUDE.md +37 -13
- package/.agent/hooks/mindforge-block-no-verify.js +61 -13
- package/.agent/hooks/mindforge-config-protection.js +82 -3
- package/.agent/hooks/mindforge-context-monitor.js +1 -1
- package/.agent/hooks/mindforge-workflow-guard.js +2 -2
- package/.agent/hooks/run-with-flags.js +190 -20
- package/.agent/mindforge/browse.md +2 -2
- package/.agent/mindforge/checkpoint.md +1 -1
- package/.agent/mindforge/harness-audit.md +1 -1
- package/.agent/mindforge/orch-add-feature.md +1 -1
- package/.agent/mindforge/orch-build-mvp.md +1 -1
- package/.agent/mindforge/orch-change-feature.md +1 -1
- package/.agent/mindforge/orch-fix-defect.md +1 -1
- package/.agent/mindforge/orch-refine-code.md +1 -1
- package/.agent/mindforge/qa.md +2 -2
- package/.claude/CLAUDE.md +37 -13
- package/.claude/commands/mindforge/browse.md +2 -2
- package/.claude/commands/mindforge/checkpoint.md +1 -1
- package/.claude/commands/mindforge/harness-audit.md +1 -1
- package/.claude/commands/mindforge/orch-add-feature.md +1 -1
- package/.claude/commands/mindforge/orch-build-mvp.md +1 -1
- package/.claude/commands/mindforge/orch-change-feature.md +1 -1
- package/.claude/commands/mindforge/orch-fix-defect.md +1 -1
- package/.claude/commands/mindforge/orch-refine-code.md +1 -1
- package/.claude/commands/mindforge/qa.md +2 -2
- package/.mindforge/MINDFORGE-SCHEMA.json +1 -1
- package/.mindforge/config.json +3 -3
- package/.mindforge/engine/autonomous/headless-adapter.md +2 -2
- package/.mindforge/engine/temporal-protocol.md +2 -2
- package/.mindforge/governance/change-classifier.md +20 -4
- package/.mindforge/memory/sync-manifest.json +1 -1
- package/.mindforge/skills/agent-architecture-audit/SKILL.md +2 -2
- package/.mindforge/skills/orch-pipeline/SKILL.md +4 -4
- package/CHANGELOG.md +194 -0
- package/MINDFORGE.md +13 -6
- package/README.md +4 -3
- package/RELEASENOTES.md +2 -2
- package/SECURITY.md +22 -3
- package/bin/autonomous/auto-runner.js +65 -2
- package/bin/change-classifier.js +151 -16
- package/bin/dashboard/api-router.js +18 -38
- package/bin/dashboard/frontend/app.js +429 -0
- package/bin/dashboard/frontend/index.html +13 -406
- package/bin/dashboard/metrics-aggregator.js +46 -22
- package/bin/dashboard/server.js +160 -1
- package/bin/dashboard/sse-bridge.js +11 -8
- package/bin/engine/sre-manager.js +1 -1
- package/bin/engine/temporal-cli.js +56 -6
- package/bin/engine/verification-runner.js +134 -17
- package/bin/engine/verify-cli.js +25 -7
- package/bin/governance/approval-record.js +147 -0
- package/bin/governance/approve.js +12 -7
- package/bin/governance/policy-engine.js +33 -3
- package/bin/governance/policy-gate-hardened.js +36 -1
- package/bin/governance/verify-approvals.js +163 -0
- package/bin/harness-audit.js +224 -10
- package/bin/hooks/instinct-capture-hook.js +12 -4
- package/bin/install.js +63 -3
- package/bin/installer/harness-adapter-compliance.js +339 -28
- package/bin/installer/hook-registration.js +504 -0
- package/bin/installer-core.js +451 -63
- package/bin/learning/instinct-cli.js +7 -0
- package/bin/memory/vector-hub.js +196 -13
- package/bin/migrations/0.6.0-to-1.0.0.js +30 -25
- package/bin/migrations/1.0.0-to-2.0.0.js +22 -23
- package/bin/mindforge-cli.js +67 -6
- package/bin/models/cost-tracker.js +104 -6
- package/bin/models/model-client.js +6 -1
- package/bin/revops/debt-monitor.js +57 -13
- package/bin/security/trust-gate-hook.js +50 -6
- package/bin/skill-validator.js +6 -1
- package/bin/skills-builder/skill-scorer.js +46 -6
- package/bin/updater/self-update.js +6 -1
- package/bin/updater/version-comparator.js +21 -1
- package/bin/utils/mindforge-version.js +99 -0
- package/bin/utils/redact-secrets.js +106 -0
- package/bin/validate-config.js +42 -2
- package/bin/wizard/setup-wizard.js +4 -1
- package/bin/wizard/theme.js +9 -1
- package/changelogs/index.json +11 -9
- package/changelogs/v11.9.3.md +195 -0
- package/docs/References/config-reference.md +5 -2
- package/docs/References/sdk-api.md +1 -1
- package/docs/Templates/Codebase/architecture.md +1 -1
- package/docs/commands-reference.md +4 -5
- package/docs/faq.md +25 -5
- package/docs/getting-started.md +3 -3
- package/docs/sdk-reference.md +15 -7
- package/docs/troubleshooting.md +10 -6
- package/docs/user-guide.md +14 -14
- package/examples/sdk-integration/README.md +1 -1
- package/package.json +7 -3
- package/subagents/.claude-plugin/marketplace.json +1 -1
- package/bin/dashboard/approval-handler.js +0 -136
|
@@ -41,8 +41,21 @@ const REQUIRED_FIELDS = Object.freeze([
|
|
|
41
41
|
'last_verified_at',
|
|
42
42
|
'owner',
|
|
43
43
|
'source_docs',
|
|
44
|
+
// install_claims is the machine-checkable half of the record. Before it existed, every claim in
|
|
45
|
+
// this file was prose that only `--check` looked at, and `--check` compares the doc against these
|
|
46
|
+
// records — so a record that was false about the installer propagated INTO the documentation and
|
|
47
|
+
// nothing ever compared either side to a real install. See tests/harness-emitted-tree.test.js.
|
|
48
|
+
'install_claims',
|
|
44
49
|
]);
|
|
45
50
|
|
|
51
|
+
function deepFreeze(value) {
|
|
52
|
+
if (Array.isArray(value)) return Object.freeze(value.map(deepFreeze));
|
|
53
|
+
if (value && typeof value === 'object') {
|
|
54
|
+
return Object.freeze(Object.fromEntries(Object.entries(value).map(([k, v]) => [k, deepFreeze(v)])));
|
|
55
|
+
}
|
|
56
|
+
return value;
|
|
57
|
+
}
|
|
58
|
+
|
|
46
59
|
function freezeRecord(record) {
|
|
47
60
|
return Object.freeze({
|
|
48
61
|
...record,
|
|
@@ -52,100 +65,325 @@ function freezeRecord(record) {
|
|
|
52
65
|
verification_commands: Object.freeze(record.verification_commands.slice()),
|
|
53
66
|
risk_notes: Object.freeze(record.risk_notes.slice()),
|
|
54
67
|
source_docs: Object.freeze(record.source_docs.slice()),
|
|
68
|
+
install_claims: deepFreeze(record.install_claims),
|
|
55
69
|
});
|
|
56
70
|
}
|
|
57
71
|
|
|
72
|
+
/**
|
|
73
|
+
* The seven asset trees the installer copies into a harness dir, one per subdir key declared in that
|
|
74
|
+
* harness's RUNTIMES entry (bin/installer-core.js:604-627 — `if (subDir && fsu.exists(asset.src))`,
|
|
75
|
+
* so a harness that declares no key silently receives nothing).
|
|
76
|
+
*
|
|
77
|
+
* Every count below was MEASURED on `git clone --no-hardlinks` of 963902d followed by
|
|
78
|
+
* `node bin/install.js --<runtime> --local` in a fresh mktemp -d with HOME also confined to a fresh
|
|
79
|
+
* mktemp -d (the installer writes ~/.mindforge/registry.json — bin/installer-core.js:835).
|
|
80
|
+
* They are floors, not equalities, so adding a skill does not turn the suite red; a *missing* tree
|
|
81
|
+
* counts 0 and fails. `verbatim_subset_of` is the content half: every file under that repo-relative
|
|
82
|
+
* source dir must exist in the emitted dir with byte-identical content, so a stale or truncated copy
|
|
83
|
+
* fails even though its name and count are unchanged.
|
|
84
|
+
*/
|
|
85
|
+
function sharedAssetTrees(base, { hooks = 11 } = {}) {
|
|
86
|
+
return [
|
|
87
|
+
{ asset: 'skills', dir: `${base}/skills`, min_files: 282, verbatim_subset_of: '.agent/skills' },
|
|
88
|
+
{ asset: 'hooks', dir: `${base}/hooks`, min_files: hooks, verbatim_subset_of: '.agent/hooks' },
|
|
89
|
+
{ asset: 'personas', dir: `${base}/personas`, min_files: 218, verbatim_subset_of: '.mindforge/personas' },
|
|
90
|
+
{ asset: 'docs references', dir: `${base}/docs/references`, min_files: 20, verbatim_subset_of: 'docs/References' },
|
|
91
|
+
{ asset: 'docs templates', dir: `${base}/docs/templates`, min_files: 42, verbatim_subset_of: 'docs/Templates' },
|
|
92
|
+
{ asset: 'memory specs', dir: `${base}/memory`, min_files: 4, verbatim_subset_of: '.mindforge/memory' },
|
|
93
|
+
{ asset: 'plugin specs', dir: `${base}/plugins`, min_files: 4, verbatim_subset_of: '.mindforge/plugins' },
|
|
94
|
+
];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Commands come from two sources: `.agent/mindforge` (221 files) and `.agent/forge` (3 files).
|
|
99
|
+
*
|
|
100
|
+
* THIS RECORD USED TO DOCUMENT A DATA-LOSS BUG AS A SPECIFICATION. Both sources were written into the
|
|
101
|
+
* same directory under their bare filenames, forge second, and all three forge names collide with
|
|
102
|
+
* mindforge names — so forge won. The previous version of this comment said so plainly ("The three
|
|
103
|
+
* forge names collide with mindforge names and WIN, which is why the total is 221 and not 224") and
|
|
104
|
+
* then encoded it: `min_files: 221` and a probe asserting `help.md` was byte-identical to
|
|
105
|
+
* `.agent/forge/help.md`. Someone observed the overwrite, understood it, and pinned it as correct.
|
|
106
|
+
*
|
|
107
|
+
* What was actually being lost, measured against the sources:
|
|
108
|
+
* help.md 33 -> 11 lines, init-project.md 170 -> 36, plan-phase.md 131 -> 34. 253 lines, silently,
|
|
109
|
+
* on every install — and the three surviving forge commands pointed at `.claude/commands/forge/`,
|
|
110
|
+
* a directory the installer never created.
|
|
111
|
+
*
|
|
112
|
+
* The installer now gives each namespace its own destination (resolveCommandTarget in
|
|
113
|
+
* bin/installer-core.js), so nothing is overwritten. Measured after that change:
|
|
114
|
+
* claude / opencode / gemini / copilot commands/mindforge 221 files, sibling forge/ 3 files
|
|
115
|
+
* cursor (flat `rules/`) rules 224 files (forge prefixed `forge:`)
|
|
116
|
+
*
|
|
117
|
+
* So min_files is 221 for the namespaced harnesses — the probed directory holds mindforge's set — and
|
|
118
|
+
* 224 for cursor, where both land side by side. And `help.md` in the probed directory is now
|
|
119
|
+
* mindforge's, which is what the probe asserts.
|
|
120
|
+
*
|
|
121
|
+
* @param {string} asset
|
|
122
|
+
* @param {string} dir
|
|
123
|
+
* @param {number} [minFiles=221] 224 for harnesses whose commands directory is not per-namespace.
|
|
124
|
+
*/
|
|
125
|
+
function flatCommandTree(asset, dir, minFiles = 221) {
|
|
126
|
+
return {
|
|
127
|
+
asset,
|
|
128
|
+
dir,
|
|
129
|
+
min_files: minFiles,
|
|
130
|
+
probes: [
|
|
131
|
+
{ file: 'security-scan.md', identical_to: '.agent/mindforge/security-scan.md' },
|
|
132
|
+
{ file: 'help.md', identical_to: '.agent/mindforge/help.md' },
|
|
133
|
+
],
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
58
137
|
const ADAPTER_RECORDS = Object.freeze([
|
|
59
138
|
{
|
|
60
139
|
id: 'claude-code',
|
|
61
140
|
harness: 'Claude Code',
|
|
62
141
|
state: 'Native',
|
|
63
|
-
supported_assets: ['skills', 'commands (slash)', 'hooks', '
|
|
64
|
-
unsupported_surfaces: [
|
|
142
|
+
supported_assets: ['skills', 'commands (slash)', 'hooks (scripts + registered settings.json)', 'subagents', 'personas', 'docs references and templates', 'memory and plugin specs'],
|
|
143
|
+
unsupported_surfaces: [
|
|
144
|
+
'Claude-native hooks do not imply parity in other harnesses',
|
|
145
|
+
'No MCP config: the installer writes no .mcp.json and no mcpServers key for ANY harness — the only mcpServers strings in an emitted tree are inside two content files (.mindforge/schemas/plugin.schema.json and .claude/agents/scientific-literature-researcher.md)',
|
|
146
|
+
'Commands arrive as TWO namespaces, not one: .claude/commands/mindforge/ (221 files) and .claude/commands/forge/ (3). The three forge basenames collide with mindforge names, so they get their own directory — which is also where .agent/forge/help.md says forge commands live. Until this was fixed the forge copies overwrote the mindforge ones in place, destroying 253 lines across help.md, init-project.md and plan-phase.md on every install',
|
|
147
|
+
],
|
|
65
148
|
install_or_onramp: ['`npx mindforge-cc@latest --claude --local`', 'Claude plugin install'],
|
|
66
|
-
|
|
149
|
+
// `node bin/install.js --check` was listed here and is an npm UPDATE check (bin/install.js:114
|
|
150
|
+
// "Check for updates without installing"), not an install verification — it exited 1 against a
|
|
151
|
+
// freshly installed project. Replaced with the gate that actually inspects the emitted tree.
|
|
152
|
+
verification_commands: ['`node bin/harness-audit.js`', '`node tests/harness-emitted-tree.test.js`'],
|
|
67
153
|
risk_notes: ['Do not load every skill by default; keep hooks opt-in and inspectable.'],
|
|
68
|
-
last_verified_at: '2026-
|
|
154
|
+
last_verified_at: '2026-08-19',
|
|
69
155
|
owner: 'MindForge maintainers',
|
|
70
156
|
source_docs: ['.claude/settings.json', '.claude-plugin/', 'bin/installer-core.js'],
|
|
157
|
+
install_claims: {
|
|
158
|
+
runtime: 'claude',
|
|
159
|
+
base_dir: '.claude',
|
|
160
|
+
entry_files: ['.claude/CLAUDE.md', 'CLAUDE.md'],
|
|
161
|
+
emits: [
|
|
162
|
+
flatCommandTree('commands (slash)', '.claude/commands/mindforge'),
|
|
163
|
+
// 16, not 11: REG-01 adds instinct/, security/ and utils/file-lock.js on top of the 11
|
|
164
|
+
// copied from .agent/hooks (bin/installer/hook-registration.js).
|
|
165
|
+
...sharedAssetTrees('.claude', { hooks: 16 }),
|
|
166
|
+
{
|
|
167
|
+
asset: 'subagents',
|
|
168
|
+
dir: '.claude/agents',
|
|
169
|
+
min_files: 164,
|
|
170
|
+
probes: [{ file: 'backend-developer.md', identical_to: 'subagents/categories/01-core-development/backend-developer.md' }],
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
asset: 'settings.json (registered hooks)',
|
|
174
|
+
file: '.claude/settings.json',
|
|
175
|
+
contains: ['"PreToolUse"', '"PostToolUse"', '"SessionStart"', 'run-with-flags.js', 'mindforge-config-protection'],
|
|
176
|
+
},
|
|
177
|
+
],
|
|
178
|
+
absent_dirs: [],
|
|
179
|
+
no_harness_surface_for: [],
|
|
180
|
+
claude_mirror: 'own',
|
|
181
|
+
},
|
|
71
182
|
},
|
|
72
183
|
{
|
|
73
184
|
id: 'antigravity',
|
|
74
185
|
harness: 'Antigravity (Gemini)',
|
|
75
186
|
state: 'Adapter-backed',
|
|
76
|
-
|
|
77
|
-
|
|
187
|
+
// 'rules' and 'MCP reference config' were both false: a clean `--antigravity --local` emits no
|
|
188
|
+
// .agents/rules and no MCP config anywhere. What it DOES emit and the record omitted: hooks,
|
|
189
|
+
// personas, docs and the memory/plugin specs.
|
|
190
|
+
supported_assets: ['skills', 'commands (namespace:prefix, .agents/workflows/)', 'hooks (scripts only, unregistered)', 'personas', 'docs references and templates', 'memory and plugin specs', 'subagents via the shared .claude/ mirror'],
|
|
191
|
+
unsupported_surfaces: [
|
|
192
|
+
'Command naming uses mindforge: namespace prefix; hook parity differs from Claude',
|
|
193
|
+
'No .agents/settings.json is written, so the 11 copied hook scripts are inert — REG-01 registers claude + local only (bin/installer/hook-registration.js)',
|
|
194
|
+
'No .agents/rules directory and no MCP config are emitted',
|
|
195
|
+
],
|
|
78
196
|
install_or_onramp: ['`npx mindforge-cc@latest --antigravity --local`'],
|
|
79
|
-
verification_commands: ['`node bin/harness-audit.js`'],
|
|
197
|
+
verification_commands: ['`node bin/harness-audit.js`', '`node tests/harness-emitted-tree.test.js`'],
|
|
80
198
|
risk_notes: ['Keep the .agent settings mirror in sync with .claude (Gemini mirror is live, not dead).'],
|
|
81
|
-
last_verified_at: '2026-
|
|
199
|
+
last_verified_at: '2026-08-19',
|
|
82
200
|
owner: 'MindForge maintainers',
|
|
83
201
|
source_docs: ['.agent/settings.json', 'bin/installer-core.js'],
|
|
202
|
+
install_claims: {
|
|
203
|
+
runtime: 'antigravity',
|
|
204
|
+
base_dir: '.agents',
|
|
205
|
+
entry_files: ['.agents/CLAUDE.md', 'CLAUDE.md'],
|
|
206
|
+
emits: [
|
|
207
|
+
// 224, and the only transformed copy in the installer: frontmatter is re-emitted as a lone
|
|
208
|
+
// `description:` and the source frontmatter stripped (bin/installer-core.js:571-578), so this
|
|
209
|
+
// one cannot be checked byte-for-byte against its source.
|
|
210
|
+
{
|
|
211
|
+
asset: 'commands (namespace:prefix)',
|
|
212
|
+
dir: '.agents/workflows',
|
|
213
|
+
min_files: 224,
|
|
214
|
+
probes: [{ file: 'mindforge:security-scan.md', contains: ['description:', '# MindForge — Security Scan Command'] }],
|
|
215
|
+
},
|
|
216
|
+
...sharedAssetTrees('.agents'),
|
|
217
|
+
],
|
|
218
|
+
absent_dirs: [{ asset: 'subagents (harness dir)', dir: '.agents/agents' }],
|
|
219
|
+
no_harness_surface_for: ['rules', 'settings.json'],
|
|
220
|
+
claude_mirror: 'shared',
|
|
221
|
+
},
|
|
84
222
|
},
|
|
85
223
|
{
|
|
86
224
|
id: 'opencode',
|
|
87
225
|
harness: 'OpenCode',
|
|
88
226
|
state: 'Adapter-backed',
|
|
89
|
-
|
|
90
|
-
|
|
227
|
+
// 'MCP config' and 'event adapter patterns' were both false: no MCP config is emitted for any
|
|
228
|
+
// harness, and no OpenCode event-adapter file is emitted either — the 11 hook scripts under
|
|
229
|
+
// .opencode/hooks/ are byte-identical Claude-shaped scripts from .agent/hooks.
|
|
230
|
+
supported_assets: ['skills', 'commands (slash)', 'hooks (scripts only, unregistered)', 'personas', 'docs references and templates', 'memory and plugin specs', 'subagents via the shared .claude/ mirror'],
|
|
231
|
+
unsupported_surfaces: [
|
|
232
|
+
'Event names and command dispatch differ from Claude Code',
|
|
233
|
+
'No .opencode/settings.json is written, so the 11 copied hook scripts are inert — REG-01 registers claude + local only',
|
|
234
|
+
'No MCP config and no OpenCode-shaped event-adapter files are emitted; the copied hooks are the unmodified Claude scripts',
|
|
235
|
+
],
|
|
91
236
|
install_or_onramp: ['`npx mindforge-cc@latest --opencode --local`'],
|
|
92
|
-
verification_commands: ['`node bin/harness-audit.js`'],
|
|
237
|
+
verification_commands: ['`node bin/harness-audit.js`', '`node tests/harness-emitted-tree.test.js`'],
|
|
93
238
|
risk_notes: ['Keep hook logic in shared scripts; adapt only event shape at the edge.'],
|
|
94
|
-
last_verified_at: '2026-
|
|
239
|
+
last_verified_at: '2026-08-19',
|
|
95
240
|
owner: 'MindForge maintainers',
|
|
96
241
|
source_docs: ['bin/installer-core.js'],
|
|
242
|
+
install_claims: {
|
|
243
|
+
runtime: 'opencode',
|
|
244
|
+
base_dir: '.opencode',
|
|
245
|
+
entry_files: ['.opencode/CLAUDE.md', 'CLAUDE.md'],
|
|
246
|
+
emits: [
|
|
247
|
+
flatCommandTree('commands (slash)', '.opencode/commands/mindforge'),
|
|
248
|
+
...sharedAssetTrees('.opencode'),
|
|
249
|
+
],
|
|
250
|
+
absent_dirs: [{ asset: 'subagents (harness dir)', dir: '.opencode/agents' }],
|
|
251
|
+
no_harness_surface_for: ['settings.json'],
|
|
252
|
+
claude_mirror: 'shared',
|
|
253
|
+
},
|
|
97
254
|
},
|
|
98
255
|
{
|
|
99
256
|
id: 'gemini',
|
|
100
257
|
harness: 'Gemini CLI',
|
|
101
258
|
state: 'Instruction-backed',
|
|
102
|
-
|
|
103
|
-
|
|
259
|
+
// 'rules' was false (no .gemini/rules is emitted). The record also omitted the slash commands it
|
|
260
|
+
// DOES receive: 221 files under .gemini/commands/mindforge/ (supportsSlash:true in RUNTIMES).
|
|
261
|
+
supported_assets: ['project-local instructions (GEMINI.md)', 'skills', 'commands (slash)', 'hooks (scripts only, unregistered)', 'personas', 'docs references and templates', 'memory and plugin specs', 'subagents via the shared .claude/ mirror'],
|
|
262
|
+
unsupported_surfaces: [
|
|
263
|
+
'No full hook parity; ports must document drift',
|
|
264
|
+
'No .gemini/settings.json is written, so the 11 copied hook scripts are inert — REG-01 registers claude + local only',
|
|
265
|
+
'No .gemini/rules directory and no MCP config are emitted',
|
|
266
|
+
],
|
|
104
267
|
install_or_onramp: ['`npx mindforge-cc@latest --gemini --local`'],
|
|
105
|
-
verification_commands: ['`node bin/harness-audit.js`'],
|
|
268
|
+
verification_commands: ['`node bin/harness-audit.js`', '`node tests/harness-emitted-tree.test.js`'],
|
|
106
269
|
risk_notes: ['Treat Gemini ports as ecosystem adapters until validated end-to-end inside Gemini CLI.'],
|
|
107
|
-
last_verified_at: '2026-
|
|
270
|
+
last_verified_at: '2026-08-19',
|
|
108
271
|
owner: 'MindForge maintainers',
|
|
109
272
|
source_docs: ['bin/installer-core.js', '.agent/skills/mindforge-neural-orchestrator/references/gemini-tools.md'],
|
|
273
|
+
install_claims: {
|
|
274
|
+
runtime: 'gemini',
|
|
275
|
+
base_dir: '.gemini',
|
|
276
|
+
entry_files: ['.gemini/GEMINI.md', 'GEMINI.md', 'CLAUDE.md'],
|
|
277
|
+
emits: [
|
|
278
|
+
flatCommandTree('commands (slash)', '.gemini/commands/mindforge'),
|
|
279
|
+
...sharedAssetTrees('.gemini'),
|
|
280
|
+
],
|
|
281
|
+
absent_dirs: [{ asset: 'subagents (harness dir)', dir: '.gemini/agents' }],
|
|
282
|
+
no_harness_surface_for: ['rules', 'settings.json'],
|
|
283
|
+
claude_mirror: 'shared',
|
|
284
|
+
},
|
|
110
285
|
},
|
|
111
286
|
{
|
|
112
287
|
id: 'cursor',
|
|
113
288
|
harness: 'Cursor',
|
|
114
289
|
state: 'Instruction-backed',
|
|
115
|
-
|
|
116
|
-
|
|
290
|
+
// All three original claims were true; they were merely incomplete — the record named 3 of the
|
|
291
|
+
// 8 asset kinds a Cursor install actually receives.
|
|
292
|
+
supported_assets: ['Cursor rules (commands rendered into .cursor/rules/)', 'project-local skills', 'instruction entry file (.cursorrules)', 'hooks (scripts only, unregistered)', 'personas', 'docs references and templates', 'memory and plugin specs', 'subagents via the shared .claude/ mirror'],
|
|
293
|
+
unsupported_surfaces: [
|
|
294
|
+
'No slash-command surface (supportsSlash:false); hook events differ from Claude',
|
|
295
|
+
'No .cursor/settings.json is written, so the 11 copied hook scripts are inert — REG-01 registers claude + local only',
|
|
296
|
+
'No MCP config is emitted',
|
|
297
|
+
],
|
|
117
298
|
install_or_onramp: ['`npx mindforge-cc@latest --cursor --local`'],
|
|
118
|
-
verification_commands: ['`node bin/harness-audit.js`'],
|
|
299
|
+
verification_commands: ['`node bin/harness-audit.js`', '`node tests/harness-emitted-tree.test.js`'],
|
|
119
300
|
risk_notes: ['Cursor adapters must preserve existing project rules and avoid silent overwrite.'],
|
|
120
|
-
last_verified_at: '2026-
|
|
301
|
+
last_verified_at: '2026-08-19',
|
|
121
302
|
owner: 'MindForge maintainers',
|
|
122
303
|
source_docs: ['bin/installer-core.js'],
|
|
304
|
+
install_claims: {
|
|
305
|
+
runtime: 'cursor',
|
|
306
|
+
base_dir: '.cursor',
|
|
307
|
+
entry_files: ['.cursor/.cursorrules', '.cursorrules', 'CLAUDE.md'],
|
|
308
|
+
emits: [
|
|
309
|
+
flatCommandTree('Cursor rules', '.cursor/rules', 224),
|
|
310
|
+
...sharedAssetTrees('.cursor'),
|
|
311
|
+
],
|
|
312
|
+
absent_dirs: [{ asset: 'subagents (harness dir)', dir: '.cursor/agents' }],
|
|
313
|
+
no_harness_surface_for: ['settings.json'],
|
|
314
|
+
claude_mirror: 'shared',
|
|
315
|
+
},
|
|
123
316
|
},
|
|
124
317
|
{
|
|
125
318
|
id: 'copilot',
|
|
126
319
|
harness: 'GitHub Copilot',
|
|
127
320
|
state: 'Instruction-backed',
|
|
128
|
-
|
|
129
|
-
|
|
321
|
+
// 'rules' and 'project-local skills' were both FALSE, and the second is the dangerous one: a
|
|
322
|
+
// clean `--copilot --local` emits .github/copilot-instructions/mindforge/ (221 files) and
|
|
323
|
+
// .github/copilot-instructions.md, and NOTHING ELSE UNDER .github/ — 0 skills, 0 hooks, 0
|
|
324
|
+
// personas, 0 docs, 0 memory, 0 plugin specs ON ANY COPILOT-READABLE SURFACE.
|
|
325
|
+
//
|
|
326
|
+
// Scoped to .github/ deliberately. Read as an absolute this is false: the same install receives
|
|
327
|
+
// the shared .mindforge/ tree, measured at 232 SKILL.md files (322 files) under
|
|
328
|
+
// .mindforge/skills/ and 218 under .mindforge/personas/, identical for every harness and read by
|
|
329
|
+
// the MindForge runtime in bin/ rather than by Copilot. An earlier draft of this record said
|
|
330
|
+
// Copilot gets "zero skills in total"; that would point a reader at delivering assets that are
|
|
331
|
+
// already present instead of at the real gap, which is the unpopulated Copilot surface.
|
|
332
|
+
// The copilot RUNTIMES entry (bin/installer-core.js:89-96) declares
|
|
333
|
+
// none of the six subdir keys, and bin/installer-core.js:614-616 skips any asset whose key is
|
|
334
|
+
// undefined. Deliberately NOT fixed by inventing a Copilot file contract for skills: nothing in
|
|
335
|
+
// this repository documents a layout Copilot reads for them, and guessing one reproduces exactly
|
|
336
|
+
// the fail-open registration REG-01 existed to remove. The honest boundary is stated instead.
|
|
337
|
+
supported_assets: ['copilot-instructions.md entry (project root and .github/)', 'commands as instruction files (.github/copilot-instructions/mindforge/)', 'shared .mindforge/ framework tree (identical for every harness)', 'subagents and command copies via the shared .claude/ mirror'],
|
|
338
|
+
unsupported_surfaces: [
|
|
339
|
+
'No slash-command surface (supportsSlash:false); no native hook enforcement',
|
|
340
|
+
'Receives NO harness-surface skills, hooks, personas, docs, memory or plugin assets: the copilot RUNTIMES entry (bin/installer-core.js:89-96) declares no skillsSubdir/hooksSubdir/personasSubdir/docsSubdir/memorySubdir/pluginsSubdir, and bin/installer-core.js:614-616 copies an asset only when its subdir key is set',
|
|
341
|
+
'No .github/settings.json and no MCP config',
|
|
342
|
+
],
|
|
130
343
|
install_or_onramp: ['`npx mindforge-cc@latest --copilot --local`'],
|
|
131
|
-
verification_commands: ['`node bin/harness-audit.js`'],
|
|
132
|
-
|
|
133
|
-
|
|
344
|
+
verification_commands: ['`node bin/harness-audit.js`', '`node tests/harness-emitted-tree.test.js`'],
|
|
345
|
+
// The previous note ("Treat hooks as policy text") implied hook files arrive as text. None do.
|
|
346
|
+
risk_notes: ['Copilot has no runtime hook surface AND receives no hook files at all; do not add a Copilot contract for skills or hooks without evidence of a layout Copilot reads, or the install fails open again.'],
|
|
347
|
+
last_verified_at: '2026-08-19',
|
|
134
348
|
owner: 'MindForge maintainers',
|
|
135
349
|
source_docs: ['bin/installer-core.js'],
|
|
350
|
+
install_claims: {
|
|
351
|
+
runtime: 'copilot',
|
|
352
|
+
base_dir: '.github',
|
|
353
|
+
entry_files: ['.github/copilot-instructions.md', 'copilot-instructions.md', 'CLAUDE.md'],
|
|
354
|
+
emits: [flatCommandTree('commands as instruction files', '.github/copilot-instructions/mindforge')],
|
|
355
|
+
absent_dirs: [
|
|
356
|
+
{ asset: 'skills', dir: '.github/skills' },
|
|
357
|
+
{ asset: 'hooks', dir: '.github/hooks' },
|
|
358
|
+
{ asset: 'personas', dir: '.github/personas' },
|
|
359
|
+
{ asset: 'docs', dir: '.github/docs' },
|
|
360
|
+
{ asset: 'memory', dir: '.github/memory' },
|
|
361
|
+
{ asset: 'plugins', dir: '.github/plugins' },
|
|
362
|
+
{ asset: 'subagents (harness dir)', dir: '.github/agents' },
|
|
363
|
+
],
|
|
364
|
+
no_harness_surface_for: ['skills', 'hooks', 'personas', 'docs', 'memory', 'plugins', 'rules', 'settings.json'],
|
|
365
|
+
claude_mirror: 'shared',
|
|
366
|
+
},
|
|
136
367
|
},
|
|
137
368
|
{
|
|
138
369
|
id: 'terminal-only',
|
|
139
370
|
harness: 'Terminal-only',
|
|
140
371
|
state: 'Native',
|
|
141
|
-
supported_assets: ['skills', 'rules', 'commands', 'bin/ scripts', 'harness audit'
|
|
142
|
-
unsupported_surfaces: [
|
|
372
|
+
supported_assets: ['skills', 'rules', 'commands', 'bin/ scripts', 'harness audit'],
|
|
373
|
+
unsupported_surfaces: [
|
|
374
|
+
'No external UI; no automatic session control unless scripts run explicitly',
|
|
375
|
+
'.planning/AUDIT.jsonl is gitignored and absent from a fresh clone — it is created by the first audited run, not shipped',
|
|
376
|
+
],
|
|
143
377
|
install_or_onramp: ['Clone repo', 'run bin/ commands directly', 'use --local for project installs'],
|
|
144
378
|
verification_commands: ['`node bin/harness-audit.js`', '`node tests/run-all.js`'],
|
|
145
379
|
risk_notes: ['This is the fallback contract; every higher-level adapter should degrade to it.'],
|
|
146
|
-
last_verified_at: '2026-
|
|
380
|
+
last_verified_at: '2026-08-19',
|
|
147
381
|
owner: 'MindForge maintainers',
|
|
148
382
|
source_docs: ['bin/harness-audit.js', 'bin/mindforge-cli.js'],
|
|
383
|
+
// Not an installer target — there is no key for it in installer-core RUNTIMES, so there is no
|
|
384
|
+
// emitted tree to assert. null rather than omitted, and tests/harness-emitted-tree.test.js pins
|
|
385
|
+
// the set of nulls against Object.keys(RUNTIMES) so a harness cannot dodge the gate by nulling.
|
|
386
|
+
install_claims: null,
|
|
149
387
|
},
|
|
150
388
|
].map(freezeRecord));
|
|
151
389
|
|
|
@@ -242,6 +480,78 @@ function validateAdapterRecords(records = ADAPTER_RECORDS) {
|
|
|
242
480
|
if (typeof record.last_verified_at !== 'string' || !/^\d{4}-\d{2}-\d{2}$/.test(record.last_verified_at)) {
|
|
243
481
|
errors.push(`${label}: last_verified_at must be YYYY-MM-DD`);
|
|
244
482
|
}
|
|
483
|
+
|
|
484
|
+
errors.push(...validateInstallClaims(record, label));
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
return errors;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Shape-check install_claims so a malformed claim fails `npm run harness:compliance` rather than
|
|
492
|
+
* silently disabling an assertion in tests/harness-emitted-tree.test.js. A claim with no `emits` is
|
|
493
|
+
* the specific hazard: the tree loop would iterate zero times and the harness would look verified.
|
|
494
|
+
*/
|
|
495
|
+
function validateInstallClaims(record, label) {
|
|
496
|
+
const errors = [];
|
|
497
|
+
const claims = record.install_claims;
|
|
498
|
+
|
|
499
|
+
if (claims === null) return errors;
|
|
500
|
+
|
|
501
|
+
if (typeof claims !== 'object' || Array.isArray(claims)) {
|
|
502
|
+
errors.push(`${label}: install_claims must be an object or null`);
|
|
503
|
+
return errors;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
for (const field of ['runtime', 'base_dir', 'claude_mirror']) {
|
|
507
|
+
if (typeof claims[field] !== 'string' || !claims[field].trim()) {
|
|
508
|
+
errors.push(`${label}: install_claims.${field} must be a non-empty string`);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
if (!['own', 'shared'].includes(claims.claude_mirror)) {
|
|
513
|
+
errors.push(`${label}: install_claims.claude_mirror must be "own" or "shared"`);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
if (!Array.isArray(claims.entry_files) || claims.entry_files.length === 0) {
|
|
517
|
+
errors.push(`${label}: install_claims.entry_files must be a non-empty array`);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
for (const field of ['absent_dirs', 'no_harness_surface_for']) {
|
|
521
|
+
if (!Array.isArray(claims[field])) {
|
|
522
|
+
errors.push(`${label}: install_claims.${field} must be an array`);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
if (!Array.isArray(claims.emits) || claims.emits.length === 0) {
|
|
527
|
+
errors.push(`${label}: install_claims.emits must be a non-empty array`);
|
|
528
|
+
return errors;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
claims.emits.forEach((entry, index) => {
|
|
532
|
+
const at = `${label}: install_claims.emits[${index}]`;
|
|
533
|
+
if (typeof entry.asset !== 'string' || !entry.asset.trim()) errors.push(`${at}.asset must be a non-empty string`);
|
|
534
|
+
|
|
535
|
+
if (typeof entry.file === 'string') {
|
|
536
|
+
if (!Array.isArray(entry.contains) || entry.contains.length === 0) {
|
|
537
|
+
errors.push(`${at}.contains must be a non-empty array for a file claim`);
|
|
538
|
+
}
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
if (typeof entry.dir !== 'string' || !entry.dir.trim()) {
|
|
543
|
+
errors.push(`${at} must declare either a dir or a file`);
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
// A floor of 0 would make the count assertion unfalsifiable — an absent directory counts 0.
|
|
547
|
+
if (!Number.isInteger(entry.min_files) || entry.min_files < 1) {
|
|
548
|
+
errors.push(`${at}.min_files must be an integer >= 1`);
|
|
549
|
+
}
|
|
550
|
+
const hasContentCheck = typeof entry.verbatim_subset_of === 'string'
|
|
551
|
+
|| (Array.isArray(entry.probes) && entry.probes.length > 0);
|
|
552
|
+
if (!hasContentCheck) {
|
|
553
|
+
errors.push(`${at} must carry a content check (verbatim_subset_of or probes), not just a count`);
|
|
554
|
+
}
|
|
245
555
|
});
|
|
246
556
|
|
|
247
557
|
return errors;
|
|
@@ -295,6 +605,7 @@ module.exports = {
|
|
|
295
605
|
renderStateTable,
|
|
296
606
|
validateAdapterRecords,
|
|
297
607
|
validateDocumentation,
|
|
608
|
+
validateInstallClaims,
|
|
298
609
|
};
|
|
299
610
|
|
|
300
611
|
// CLI: `node bin/installer/harness-adapter-compliance.js [--check]`
|