godpowers 3.0.2 → 3.13.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/CHANGELOG.md +283 -0
- package/README.md +24 -10
- package/RELEASE.md +23 -35
- package/agents/god-debt-assessor.md +179 -99
- package/bin/install.js +34 -0
- package/fixtures/gate/harden-pass/.godpowers/state.json +26 -0
- package/lib/artifact-map.js +2 -1
- package/lib/cli-dispatch.js +409 -2
- package/lib/evidence/.provenance.json +45 -0
- package/lib/evidence-import.js +147 -0
- package/lib/evidence.js +908 -0
- package/lib/gate.js +26 -15
- package/lib/installer-args.js +219 -1
- package/lib/quarterback.js +183 -0
- package/lib/work-report.js +137 -0
- package/package.json +1 -1
- package/references/orchestration/GOD-ORCHESTRATOR-RUNBOOK.md +49 -4
- package/routing/recipes/audit-remediate.yaml +30 -0
- package/skills/god-harden.md +5 -2
- package/skills/god-version.md +1 -1
- package/workflows/full-arc.yaml +17 -3
package/lib/cli-dispatch.js
CHANGED
|
@@ -149,6 +149,395 @@ function runExtensionScaffoldCommand(opts) {
|
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
+
function verifyFailure(opts, id, reason) {
|
|
153
|
+
return {
|
|
154
|
+
command: 'verify',
|
|
155
|
+
verdict: 'fail',
|
|
156
|
+
project: opts.project,
|
|
157
|
+
kind: null,
|
|
158
|
+
verified: false,
|
|
159
|
+
checks: [{ id, status: 'fail', artifact: '.godpowers/ledger/verifications.jsonl', reason }],
|
|
160
|
+
findings: [{ id, severity: 'error', artifact: '.godpowers/ledger/verifications.jsonl', reason }]
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function relLedger(opts, absPath) {
|
|
165
|
+
const path = require('path');
|
|
166
|
+
return path.relative(opts.project, absPath);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function renderVerify(result) {
|
|
170
|
+
const lines = [];
|
|
171
|
+
lines.push('Godpowers Verify');
|
|
172
|
+
lines.push('');
|
|
173
|
+
if (result.verdict === 'fail' && result.findings && result.findings.length > 0 && !result.record) {
|
|
174
|
+
lines.push('Verdict: fail');
|
|
175
|
+
for (const finding of result.findings) {
|
|
176
|
+
lines.push(`Error: ${finding.reason}`);
|
|
177
|
+
}
|
|
178
|
+
return lines.join('\n');
|
|
179
|
+
}
|
|
180
|
+
const verdictLabel = result.kind === 'attested'
|
|
181
|
+
? 'attested (self-reported, not machine-checked)'
|
|
182
|
+
: result.verdict;
|
|
183
|
+
lines.push(`Verdict: ${verdictLabel}`);
|
|
184
|
+
lines.push(`Command: ${result.commandText ? '`' + result.commandText + '`' : '(none)'}`);
|
|
185
|
+
if (result.substep) lines.push(`Substep: ${result.substep}`);
|
|
186
|
+
if (result.kind === 'executed') {
|
|
187
|
+
lines.push(`Exit: ${result.exitCode} (${result.durationMs} ms)`);
|
|
188
|
+
}
|
|
189
|
+
if (result.claim) lines.push(`Claim: ${result.claim}`);
|
|
190
|
+
lines.push(`Ledger: ${result.ledger}`);
|
|
191
|
+
if (result.rollup) {
|
|
192
|
+
lines.push(result.rollup.applied
|
|
193
|
+
? `Rollup: applied ${result.rollup.substep} verification.commands (${result.rollup.status})`
|
|
194
|
+
: `Rollup: skipped (${result.rollup.reason})`);
|
|
195
|
+
}
|
|
196
|
+
if (result.event) {
|
|
197
|
+
lines.push(result.event.emitted
|
|
198
|
+
? `Event: ${result.event.name} -> runs/${result.event.runId}/events.jsonl`
|
|
199
|
+
: `Event: not emitted (${result.event.error})`);
|
|
200
|
+
}
|
|
201
|
+
if (result.kind === 'executed' && !result.verified && result.diagnostics) {
|
|
202
|
+
lines.push('');
|
|
203
|
+
lines.push('Diagnostics:');
|
|
204
|
+
lines.push(result.diagnostics);
|
|
205
|
+
}
|
|
206
|
+
return lines.join('\n');
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function runVerifyCommand(opts) {
|
|
210
|
+
const evidence = require('./evidence');
|
|
211
|
+
|
|
212
|
+
if (opts.attest) {
|
|
213
|
+
if (!opts.claim) {
|
|
214
|
+
const result = verifyFailure(opts, 'claim-required', 'verify --attest requires --claim="<claim>"');
|
|
215
|
+
if (opts.json) console.log(JSON.stringify(result, null, 2));
|
|
216
|
+
else console.log(renderVerify(result));
|
|
217
|
+
process.exitCode = 1;
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const outcome = evidence.verifyClaim(opts.claim, opts.evidence, {
|
|
221
|
+
substep: opts.substep || undefined,
|
|
222
|
+
projectRoot: opts.project
|
|
223
|
+
});
|
|
224
|
+
const result = {
|
|
225
|
+
command: 'verify',
|
|
226
|
+
kind: 'attested',
|
|
227
|
+
verdict: 'attested',
|
|
228
|
+
verified: null,
|
|
229
|
+
project: opts.project,
|
|
230
|
+
commandText: null,
|
|
231
|
+
substep: outcome.record.substep,
|
|
232
|
+
claim: outcome.record.claim,
|
|
233
|
+
ledger: relLedger(opts, outcome.ledger),
|
|
234
|
+
rollup: null,
|
|
235
|
+
event: null,
|
|
236
|
+
record: outcome.record
|
|
237
|
+
};
|
|
238
|
+
if (opts.json) console.log(JSON.stringify(result, null, 2));
|
|
239
|
+
else console.log(renderVerify(result));
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (!opts.verifyCommand) {
|
|
244
|
+
const result = verifyFailure(opts, 'command-required', 'verify requires a command, for example: npx godpowers verify "npm test" --substep tier-2.build');
|
|
245
|
+
if (opts.json) console.log(JSON.stringify(result, null, 2));
|
|
246
|
+
else console.log(renderVerify(result));
|
|
247
|
+
process.exitCode = 1;
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
if (!opts.substep) {
|
|
251
|
+
const result = verifyFailure(opts, 'substep-required', 'verify requires --substep=<id>, for example --substep=tier-2.build');
|
|
252
|
+
if (opts.json) console.log(JSON.stringify(result, null, 2));
|
|
253
|
+
else console.log(renderVerify(result));
|
|
254
|
+
process.exitCode = 1;
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const outcome = evidence.verify(opts.verifyCommand, {
|
|
259
|
+
substep: opts.substep,
|
|
260
|
+
claim: opts.claim || undefined,
|
|
261
|
+
timeout: opts.timeout || undefined,
|
|
262
|
+
projectRoot: opts.project
|
|
263
|
+
});
|
|
264
|
+
const result = {
|
|
265
|
+
command: 'verify',
|
|
266
|
+
kind: 'executed',
|
|
267
|
+
verdict: outcome.verified ? 'pass' : 'fail',
|
|
268
|
+
verified: outcome.verified,
|
|
269
|
+
project: opts.project,
|
|
270
|
+
commandText: outcome.record.command,
|
|
271
|
+
substep: outcome.record.substep,
|
|
272
|
+
claim: outcome.record.claim,
|
|
273
|
+
exitCode: outcome.record.exit_code,
|
|
274
|
+
durationMs: Math.max(0, Math.round(outcome.record.duration_seconds * 1000)),
|
|
275
|
+
diagnostics: outcome.rollup && outcome.rollup.applied
|
|
276
|
+
? undefined
|
|
277
|
+
: (outcome.record.stderr_tail || outcome.record.stdout_tail || ''),
|
|
278
|
+
ledger: relLedger(opts, outcome.ledger),
|
|
279
|
+
rollup: outcome.rollup,
|
|
280
|
+
event: outcome.event,
|
|
281
|
+
record: outcome.record
|
|
282
|
+
};
|
|
283
|
+
// Surface diagnostics on failure regardless of rollup state.
|
|
284
|
+
if (!outcome.verified) {
|
|
285
|
+
result.diagnostics = outcome.record.stderr_tail || outcome.record.stdout_tail || `exit ${outcome.record.exit_code}`;
|
|
286
|
+
}
|
|
287
|
+
if (opts.json) console.log(JSON.stringify(result, null, 2));
|
|
288
|
+
else console.log(renderVerify(result));
|
|
289
|
+
if (!outcome.verified) process.exitCode = 1;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function renderCanClose(result) {
|
|
293
|
+
const lines = [];
|
|
294
|
+
lines.push('Godpowers Can-Close');
|
|
295
|
+
lines.push('');
|
|
296
|
+
lines.push(`Substep: ${result.substep || '(none)'}`);
|
|
297
|
+
lines.push(`Can close: ${result.canClose ? 'yes' : 'no'}`);
|
|
298
|
+
lines.push(`Reason: ${result.reason}`);
|
|
299
|
+
if (result.strategy) lines.push(`Strategy: ${result.strategy}`);
|
|
300
|
+
if (result.wentInFlightAt) lines.push(`In-flight since: ${result.wentInFlightAt}`);
|
|
301
|
+
return lines.join('\n');
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function runCanCloseCommand(opts) {
|
|
305
|
+
const evidence = require('./evidence');
|
|
306
|
+
if (!opts.substep) {
|
|
307
|
+
const result = {
|
|
308
|
+
command: 'can-close',
|
|
309
|
+
project: opts.project,
|
|
310
|
+
canClose: false,
|
|
311
|
+
reason: 'substep-required',
|
|
312
|
+
strategy: null,
|
|
313
|
+
substep: null
|
|
314
|
+
};
|
|
315
|
+
if (opts.json) console.log(JSON.stringify(result, null, 2));
|
|
316
|
+
else console.log(`${renderCanClose(result)}\n\nError: can-close requires --substep=<id>, for example --substep=tier-2.build`);
|
|
317
|
+
process.exitCode = 1;
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
const verdict = evidence.canClose(opts.substep, { projectRoot: opts.project });
|
|
321
|
+
const result = { command: 'can-close', project: opts.project, ...verdict };
|
|
322
|
+
if (opts.json) console.log(JSON.stringify(result, null, 2));
|
|
323
|
+
else console.log(renderCanClose(result));
|
|
324
|
+
if (!verdict.canClose) process.exitCode = 1;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function renderRoute(result) {
|
|
328
|
+
const lines = [];
|
|
329
|
+
lines.push('Godpowers Route');
|
|
330
|
+
lines.push('');
|
|
331
|
+
lines.push(`Route: ${result.route}`);
|
|
332
|
+
lines.push(`Reason: ${result.reason}`);
|
|
333
|
+
lines.push(`Next command: ${result.nextCommand || '(answer inline)'}`);
|
|
334
|
+
lines.push(`Ceremony: ${result.ceremony}`);
|
|
335
|
+
lines.push(`Verification: ${result.verificationStrategy}`);
|
|
336
|
+
lines.push(`Chat policy: ${result.chatPolicy}`);
|
|
337
|
+
const ev = result.evidence || {};
|
|
338
|
+
lines.push(`Evidence: classification=${ev.classification}, latest verdict=${ev.latestVerdict}, open findings=${ev.openFindings}`);
|
|
339
|
+
return lines.join('\n');
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function runRouteCommand(opts) {
|
|
343
|
+
const quarterback = require('./quarterback');
|
|
344
|
+
const result = quarterback.route(opts.routePrompt || '', { projectRoot: opts.project });
|
|
345
|
+
if (opts.json) console.log(JSON.stringify(result, null, 2));
|
|
346
|
+
else console.log(renderRoute(result));
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function runReportCommand(opts) {
|
|
350
|
+
const workReport = require('./work-report');
|
|
351
|
+
const result = workReport.report({
|
|
352
|
+
since: opts.since || 'last',
|
|
353
|
+
peek: opts.peek,
|
|
354
|
+
projectRoot: opts.project
|
|
355
|
+
});
|
|
356
|
+
if (opts.json) console.log(JSON.stringify(result, null, 2));
|
|
357
|
+
else console.log(workReport.render(result));
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function runReflectCommand(opts) {
|
|
361
|
+
const evidence = require('./evidence');
|
|
362
|
+
if (!opts.reflectAction) {
|
|
363
|
+
const result = {
|
|
364
|
+
command: 'reflect',
|
|
365
|
+
verdict: 'fail',
|
|
366
|
+
project: opts.project,
|
|
367
|
+
findings: [{ id: 'action-required', severity: 'error', artifact: '.godpowers/ledger/reflections.jsonl', reason: 'reflect requires --action="<what was attempted>"' }]
|
|
368
|
+
};
|
|
369
|
+
if (opts.json) console.log(JSON.stringify(result, null, 2));
|
|
370
|
+
else console.log('Godpowers Reflect\n\nError: reflect requires --action="<what was attempted>"');
|
|
371
|
+
process.exitCode = 1;
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
const outcome = evidence.reflect({
|
|
375
|
+
action: opts.reflectAction,
|
|
376
|
+
outcome: opts.outcome || undefined,
|
|
377
|
+
observation: opts.observation || undefined,
|
|
378
|
+
rootCause: opts.rootCause || undefined,
|
|
379
|
+
next: opts.nextAction || undefined,
|
|
380
|
+
lesson: opts.lesson || undefined
|
|
381
|
+
}, { substep: opts.substep || undefined, projectRoot: opts.project });
|
|
382
|
+
if (opts.json) {
|
|
383
|
+
console.log(JSON.stringify(outcome, null, 2));
|
|
384
|
+
} else {
|
|
385
|
+
const r = outcome.record;
|
|
386
|
+
const lines = ['Godpowers Reflect', '', `Outcome: ${r.outcome}`, `Action: ${r.action}`];
|
|
387
|
+
if (r.substep) lines.push(`Substep: ${r.substep}`);
|
|
388
|
+
if (r.next) lines.push(`Next: ${r.next}`);
|
|
389
|
+
if (r.lesson) lines.push(`Lesson: ${r.lesson}`);
|
|
390
|
+
lines.push('Recorded to .godpowers/ledger/reflections.jsonl');
|
|
391
|
+
console.log(lines.join('\n'));
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function memoryError(opts, reason) {
|
|
396
|
+
if (opts.json) {
|
|
397
|
+
console.log(JSON.stringify({ command: 'memory', verdict: 'fail', project: opts.project, reason }, null, 2));
|
|
398
|
+
} else {
|
|
399
|
+
console.log(`Godpowers Memory\n\nError: ${reason}`);
|
|
400
|
+
}
|
|
401
|
+
process.exitCode = 1;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function runMemoryCommand(opts) {
|
|
405
|
+
const evidence = require('./evidence');
|
|
406
|
+
const action = opts.memoryAction;
|
|
407
|
+
if (!action || !['set', 'get', 'list', 'clear'].includes(action)) {
|
|
408
|
+
return memoryError(opts, 'memory requires an action: set, get, list, or clear');
|
|
409
|
+
}
|
|
410
|
+
const projectRoot = opts.project;
|
|
411
|
+
let result;
|
|
412
|
+
if (action === 'set') {
|
|
413
|
+
if (!opts.memoryKey || opts.memoryValue === null) {
|
|
414
|
+
return memoryError(opts, 'memory set requires a key and value, for example: memory set decision "use postgres"');
|
|
415
|
+
}
|
|
416
|
+
result = evidence.memory.set(opts.memoryKey, opts.memoryValue, { category: opts.category || undefined, projectRoot });
|
|
417
|
+
} else if (action === 'get') {
|
|
418
|
+
if (!opts.memoryKey) return memoryError(opts, 'memory get requires a key');
|
|
419
|
+
result = evidence.memory.get(opts.memoryKey, { projectRoot });
|
|
420
|
+
} else if (action === 'list') {
|
|
421
|
+
result = evidence.memory.list({ category: opts.category || undefined, projectRoot });
|
|
422
|
+
} else {
|
|
423
|
+
result = evidence.memory.clear(opts.memoryKey || undefined, { projectRoot });
|
|
424
|
+
}
|
|
425
|
+
if (opts.json) {
|
|
426
|
+
console.log(JSON.stringify({ command: 'memory', action, result }, null, 2));
|
|
427
|
+
} else {
|
|
428
|
+
const lines = ['Godpowers Memory', '', `Action: ${action}`];
|
|
429
|
+
if (action === 'list') {
|
|
430
|
+
const entries = result || [];
|
|
431
|
+
lines.push(`Entries: ${entries.length}`);
|
|
432
|
+
for (const e of entries) lines.push(` [${e.category}] ${e.key} = ${e.value}`);
|
|
433
|
+
} else if (action === 'get') {
|
|
434
|
+
lines.push(result ? `[${result.category}] ${result.key} = ${result.value}` : '(not found)');
|
|
435
|
+
} else if (action === 'set') {
|
|
436
|
+
lines.push(`Set [${result.category}] ${result.key} = ${result.value}`);
|
|
437
|
+
} else {
|
|
438
|
+
lines.push(`Removed ${result.removed} entr${result.removed === 1 ? 'y' : 'ies'}`);
|
|
439
|
+
}
|
|
440
|
+
console.log(lines.join('\n'));
|
|
441
|
+
}
|
|
442
|
+
if (action === 'get' && !result) process.exitCode = 1;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function runLessonCommand(opts) {
|
|
446
|
+
const evidence = require('./evidence');
|
|
447
|
+
const action = opts.lessonAction;
|
|
448
|
+
if (!action || !['add', 'list'].includes(action)) {
|
|
449
|
+
if (opts.json) console.log(JSON.stringify({ command: 'lesson', verdict: 'fail', reason: 'lesson requires an action: add or list' }, null, 2));
|
|
450
|
+
else console.log('Godpowers Lesson\n\nError: lesson requires an action: add or list');
|
|
451
|
+
process.exitCode = 1;
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
const projectRoot = opts.project;
|
|
455
|
+
if (action === 'add') {
|
|
456
|
+
if (!opts.lessonText) {
|
|
457
|
+
if (opts.json) console.log(JSON.stringify({ command: 'lesson', verdict: 'fail', reason: 'lesson add requires the lesson text' }, null, 2));
|
|
458
|
+
else console.log('Godpowers Lesson\n\nError: lesson add requires the lesson text, for example: lesson add "guard inputs before parsing"');
|
|
459
|
+
process.exitCode = 1;
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
const tags = opts.tags ? String(opts.tags).split(',').map((t) => t.trim()).filter(Boolean) : [];
|
|
463
|
+
const record = evidence.lesson.add(opts.lessonText, { tags, scope: opts.scope || undefined, projectRoot });
|
|
464
|
+
if (opts.json) console.log(JSON.stringify({ command: 'lesson', action, result: record }, null, 2));
|
|
465
|
+
else console.log(`Godpowers Lesson\n\nAdded [${record.scope}]${record.tags.length ? ` (${record.tags.join(',')})` : ''}: ${record.lesson}`);
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
const records = evidence.lesson.list({ scope: opts.scope || undefined, projectRoot });
|
|
469
|
+
if (opts.json) {
|
|
470
|
+
console.log(JSON.stringify({ command: 'lesson', action, result: records }, null, 2));
|
|
471
|
+
} else {
|
|
472
|
+
const lines = ['Godpowers Lesson', '', `Lessons: ${records.length}`];
|
|
473
|
+
for (const r of records) lines.push(` [${r.scope}]${r.tags && r.tags.length ? ` (${r.tags.join(',')})` : ''} ${r.lesson}`);
|
|
474
|
+
console.log(lines.join('\n'));
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function outcomeError(opts, reason) {
|
|
479
|
+
if (opts.json) console.log(JSON.stringify({ command: 'outcome', verdict: 'fail', reason }, null, 2));
|
|
480
|
+
else console.log(`Godpowers Outcome\n\nError: ${reason}`);
|
|
481
|
+
process.exitCode = 1;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function runOutcomeCommand(opts) {
|
|
485
|
+
const evidence = require('./evidence');
|
|
486
|
+
const action = opts.outcomeAction;
|
|
487
|
+
if (!action || !['start', 'check', 'stop', 'status'].includes(action)) {
|
|
488
|
+
return outcomeError(opts, 'outcome requires an action: start, check, stop, or status');
|
|
489
|
+
}
|
|
490
|
+
if (!opts.outcomeSlug) {
|
|
491
|
+
return outcomeError(opts, `outcome ${action} requires a name, for example: outcome ${action} green-build`);
|
|
492
|
+
}
|
|
493
|
+
const projectRoot = opts.project;
|
|
494
|
+
let payload;
|
|
495
|
+
if (action === 'start') {
|
|
496
|
+
payload = evidence.outcome.start(opts.outcomeSlug, {
|
|
497
|
+
goal: opts.outcomeGoal || undefined,
|
|
498
|
+
verifier: opts.outcomeVerify || undefined,
|
|
499
|
+
budget: opts.budget || undefined,
|
|
500
|
+
substep: opts.substep || undefined,
|
|
501
|
+
projectRoot
|
|
502
|
+
});
|
|
503
|
+
} else if (action === 'check') {
|
|
504
|
+
payload = evidence.outcome.check(opts.outcomeSlug, { projectRoot });
|
|
505
|
+
} else if (action === 'stop') {
|
|
506
|
+
payload = evidence.outcome.stop(opts.outcomeSlug, opts.reason || undefined, { projectRoot });
|
|
507
|
+
} else {
|
|
508
|
+
payload = evidence.outcome.status(opts.outcomeSlug, { projectRoot });
|
|
509
|
+
}
|
|
510
|
+
if (opts.json) {
|
|
511
|
+
console.log(JSON.stringify({ command: 'outcome', action, result: payload }, null, 2));
|
|
512
|
+
} else {
|
|
513
|
+
const lines = ['Godpowers Outcome', '', `Action: ${action}`];
|
|
514
|
+
const goal = payload && (payload.goal || (payload.slug ? payload : null));
|
|
515
|
+
if (goal && goal.slug) {
|
|
516
|
+
lines.push(`Outcome: ${goal.slug}`);
|
|
517
|
+
lines.push(`Status: ${goal.status}`);
|
|
518
|
+
lines.push(`Iterations: ${goal.iterations}/${goal.budget}`);
|
|
519
|
+
}
|
|
520
|
+
if (payload && payload.verified !== undefined) lines.push(`This check: ${payload.verified ? 'verified' : 'unverified'}`);
|
|
521
|
+
if (payload && payload.reason) lines.push(`Note: ${payload.reason}`);
|
|
522
|
+
if (!payload) lines.push('(outcome not found)');
|
|
523
|
+
console.log(lines.join('\n'));
|
|
524
|
+
}
|
|
525
|
+
// A check that did not verify, or a missing outcome, exits non-zero.
|
|
526
|
+
if (action === 'check' && payload && payload.verified === false) process.exitCode = 1;
|
|
527
|
+
if ((action === 'status') && !payload) process.exitCode = 1;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
function runImportLedgerCommand(opts) {
|
|
531
|
+
const importer = require('./evidence-import');
|
|
532
|
+
const result = importer.importMythify({
|
|
533
|
+
source: opts.importFrom || undefined,
|
|
534
|
+
projectRoot: opts.project
|
|
535
|
+
});
|
|
536
|
+
if (opts.json) console.log(JSON.stringify(result, null, 2));
|
|
537
|
+
else console.log(importer.render(result));
|
|
538
|
+
if (!result.found) process.exitCode = 1;
|
|
539
|
+
}
|
|
540
|
+
|
|
152
541
|
function runGateCommand(opts) {
|
|
153
542
|
if (!opts.tier) {
|
|
154
543
|
const result = {
|
|
@@ -223,7 +612,16 @@ const COMMAND_RUNNERS = {
|
|
|
223
612
|
demo: runDemoCommand,
|
|
224
613
|
surface: runSurfaceCommand,
|
|
225
614
|
'extension-scaffold': runExtensionScaffoldCommand,
|
|
226
|
-
gate: runGateCommand
|
|
615
|
+
gate: runGateCommand,
|
|
616
|
+
verify: runVerifyCommand,
|
|
617
|
+
'can-close': runCanCloseCommand,
|
|
618
|
+
route: runRouteCommand,
|
|
619
|
+
report: runReportCommand,
|
|
620
|
+
reflect: runReflectCommand,
|
|
621
|
+
memory: runMemoryCommand,
|
|
622
|
+
lesson: runLessonCommand,
|
|
623
|
+
outcome: runOutcomeCommand,
|
|
624
|
+
'import-ledger': runImportLedgerCommand
|
|
227
625
|
};
|
|
228
626
|
|
|
229
627
|
function runCommand(opts) {
|
|
@@ -247,5 +645,14 @@ module.exports = {
|
|
|
247
645
|
runMcpInfoCommand,
|
|
248
646
|
runExtensionScaffoldCommand,
|
|
249
647
|
runGateCommand,
|
|
250
|
-
runStateCommand
|
|
648
|
+
runStateCommand,
|
|
649
|
+
runVerifyCommand,
|
|
650
|
+
runCanCloseCommand,
|
|
651
|
+
runRouteCommand,
|
|
652
|
+
runReportCommand,
|
|
653
|
+
runReflectCommand,
|
|
654
|
+
runMemoryCommand,
|
|
655
|
+
runLessonCommand,
|
|
656
|
+
runOutcomeCommand,
|
|
657
|
+
runImportLedgerCommand
|
|
251
658
|
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"source": "mythify-mcp",
|
|
3
|
+
"upstreamRepo": "mythify",
|
|
4
|
+
"upstreamPath": "mcp-server/src/index.js",
|
|
5
|
+
"engine": "verify_run + verify_claim (verification tools)",
|
|
6
|
+
"version": "3.6.3",
|
|
7
|
+
"commit": "7cbd601f9c4d5328a54921a051d450703e44c514",
|
|
8
|
+
"syncedAt": "2026-06-15",
|
|
9
|
+
"adaptations": [
|
|
10
|
+
"verificationStepContext (plan/step_id/step_title/step_status) rebound to substepContext (arc/substep/substep_status)",
|
|
11
|
+
"state dir .mythify/ rebound to .godpowers/ledger/",
|
|
12
|
+
"appendJsonl (fs.appendFileSync) rebound to an atomic read-modify-write append via lib/atomic-write.js",
|
|
13
|
+
"executed verdict additionally rolled up into state.json verification.commands[] via lib/state.js in the Godpowers gate shape",
|
|
14
|
+
"executed verdict additionally emitted as gate.pass / gate.fail to .godpowers/runs/<id>/events.jsonl via lib/events.js",
|
|
15
|
+
"verify_claim exposed as evidence.verifyClaim: attested, verified:null, never rolled up, never emitted"
|
|
16
|
+
],
|
|
17
|
+
"upstreamRecordShape": {
|
|
18
|
+
"executed": [
|
|
19
|
+
"kind",
|
|
20
|
+
"claim",
|
|
21
|
+
"command",
|
|
22
|
+
"exit_code",
|
|
23
|
+
"duration_seconds",
|
|
24
|
+
"stdout_tail",
|
|
25
|
+
"stderr_tail",
|
|
26
|
+
"verified",
|
|
27
|
+
"timestamp",
|
|
28
|
+
"plan",
|
|
29
|
+
"step_id",
|
|
30
|
+
"step_title",
|
|
31
|
+
"step_status"
|
|
32
|
+
],
|
|
33
|
+
"attested": [
|
|
34
|
+
"kind",
|
|
35
|
+
"claim",
|
|
36
|
+
"evidence",
|
|
37
|
+
"verified",
|
|
38
|
+
"timestamp",
|
|
39
|
+
"plan",
|
|
40
|
+
"step_id",
|
|
41
|
+
"step_title",
|
|
42
|
+
"step_status"
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One-time importer for existing Mythify .mythify/ ledgers.
|
|
3
|
+
*
|
|
4
|
+
* Optional, best-effort, additive: it copies an existing Mythify state dir into
|
|
5
|
+
* the Godpowers ledger, rebinding the Mythify plan/step context to Godpowers'
|
|
6
|
+
* arc/substep on each record. It appends to the ledger and does NOT roll up into
|
|
7
|
+
* state.json or emit gate events; this is a historical import, not a re-run.
|
|
8
|
+
*
|
|
9
|
+
* See docs/FUSION-ARCHITECTURE.md (Phase 3, optional importer).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
|
|
15
|
+
const atomic = require('./atomic-write');
|
|
16
|
+
const evidence = require('./evidence');
|
|
17
|
+
|
|
18
|
+
function readJson(file) {
|
|
19
|
+
try {
|
|
20
|
+
return JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
21
|
+
} catch (_) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Rebind a Mythify record's plan/step context to Godpowers' arc/substep.
|
|
28
|
+
* Records that already carry arc/substep pass through unchanged.
|
|
29
|
+
*/
|
|
30
|
+
function rebindRecord(record) {
|
|
31
|
+
if (!record || typeof record !== 'object') return record;
|
|
32
|
+
const hasMythifyContext = 'plan' in record || 'step_id' in record
|
|
33
|
+
|| 'step_title' in record || 'step_status' in record;
|
|
34
|
+
if (!hasMythifyContext) return { ...record };
|
|
35
|
+
const out = { ...record };
|
|
36
|
+
out.arc = record.arc !== undefined ? record.arc : (record.plan !== undefined ? record.plan : null);
|
|
37
|
+
out.substep = record.substep !== undefined ? record.substep : (record.step_id !== undefined ? record.step_id : null);
|
|
38
|
+
out.substep_status = record.substep_status !== undefined
|
|
39
|
+
? record.substep_status
|
|
40
|
+
: (record.step_status !== undefined ? record.step_status : null);
|
|
41
|
+
delete out.plan;
|
|
42
|
+
delete out.step_id;
|
|
43
|
+
delete out.step_title;
|
|
44
|
+
delete out.step_status;
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Import an existing .mythify/ ledger into the Godpowers ledger.
|
|
50
|
+
*
|
|
51
|
+
* @param {{ source?: string, projectRoot?: string }} [opts]
|
|
52
|
+
* @returns {{ source: string, projectRoot: string, found: boolean,
|
|
53
|
+
* imported: { verifications: number, reflections: number, memory: number, lessons: number, outcomes: number },
|
|
54
|
+
* error?: string }}
|
|
55
|
+
*/
|
|
56
|
+
function importMythify(opts = {}) {
|
|
57
|
+
const projectRoot = path.resolve(opts.projectRoot || process.cwd());
|
|
58
|
+
const source = path.resolve(opts.source || path.join(projectRoot, '.mythify'));
|
|
59
|
+
const result = {
|
|
60
|
+
source,
|
|
61
|
+
projectRoot,
|
|
62
|
+
found: false,
|
|
63
|
+
imported: { verifications: 0, reflections: 0, memory: 0, lessons: 0, outcomes: 0 }
|
|
64
|
+
};
|
|
65
|
+
if (!fs.existsSync(source)) {
|
|
66
|
+
result.error = 'source-not-found';
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
result.found = true;
|
|
70
|
+
|
|
71
|
+
// Verifications (rebind plan/step -> arc/substep).
|
|
72
|
+
for (const record of evidence.readJsonl(path.join(source, 'verifications.jsonl'))) {
|
|
73
|
+
evidence.appendJsonlAtomic(evidence.verificationsPath(projectRoot), rebindRecord(record));
|
|
74
|
+
result.imported.verifications += 1;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Reflections (rebind for any bound context; mostly pass-through).
|
|
78
|
+
for (const record of evidence.readJsonl(path.join(source, 'reflections.jsonl'))) {
|
|
79
|
+
evidence.appendJsonlAtomic(evidence.reflectionsPath(projectRoot), rebindRecord(record));
|
|
80
|
+
result.imported.reflections += 1;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Memory (merge entries by key).
|
|
84
|
+
const sourceMemory = readJson(path.join(source, 'memory.json'));
|
|
85
|
+
if (sourceMemory && Array.isArray(sourceMemory.entries) && sourceMemory.entries.length > 0) {
|
|
86
|
+
const dest = readJson(evidence.memoryPath(projectRoot)) || { entries: [], metadata: {} };
|
|
87
|
+
if (!Array.isArray(dest.entries)) dest.entries = [];
|
|
88
|
+
for (const entry of sourceMemory.entries) {
|
|
89
|
+
if (!entry || typeof entry.key !== 'string') continue;
|
|
90
|
+
const idx = dest.entries.findIndex((existing) => existing && existing.key === entry.key);
|
|
91
|
+
if (idx >= 0) dest.entries[idx] = entry;
|
|
92
|
+
else dest.entries.push(entry);
|
|
93
|
+
result.imported.memory += 1;
|
|
94
|
+
}
|
|
95
|
+
dest.metadata = dest.metadata || {};
|
|
96
|
+
dest.metadata.total_entries = dest.entries.length;
|
|
97
|
+
fs.mkdirSync(evidence.ledgerDir(projectRoot), { recursive: true });
|
|
98
|
+
atomic.writeJsonAtomic(evidence.memoryPath(projectRoot), dest);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Lessons (Mythify lessons/*.json -> our lessons.jsonl).
|
|
102
|
+
const lessonsDir = path.join(source, 'lessons');
|
|
103
|
+
if (fs.existsSync(lessonsDir)) {
|
|
104
|
+
for (const name of fs.readdirSync(lessonsDir).filter((n) => n.endsWith('.json'))) {
|
|
105
|
+
const lesson = readJson(path.join(lessonsDir, name));
|
|
106
|
+
if (lesson) {
|
|
107
|
+
evidence.appendJsonlAtomic(evidence.lessonsPath(projectRoot, 'project'), lesson);
|
|
108
|
+
result.imported.lessons += 1;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Outcomes (copy goal.json + iterations.jsonl per slug).
|
|
114
|
+
const outcomesDir = path.join(source, 'outcomes');
|
|
115
|
+
if (fs.existsSync(outcomesDir)) {
|
|
116
|
+
for (const slug of fs.readdirSync(outcomesDir)) {
|
|
117
|
+
const srcGoal = path.join(outcomesDir, slug, 'goal.json');
|
|
118
|
+
if (!fs.existsSync(srcGoal)) continue;
|
|
119
|
+
const destDir = path.join(evidence.ledgerDir(projectRoot), 'outcomes', slug);
|
|
120
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
121
|
+
atomic.writeFileAtomic(path.join(destDir, 'goal.json'), fs.readFileSync(srcGoal, 'utf8'));
|
|
122
|
+
const srcIters = path.join(outcomesDir, slug, 'iterations.jsonl');
|
|
123
|
+
if (fs.existsSync(srcIters)) {
|
|
124
|
+
atomic.writeFileAtomic(path.join(destDir, 'iterations.jsonl'), fs.readFileSync(srcIters, 'utf8'));
|
|
125
|
+
}
|
|
126
|
+
result.imported.outcomes += 1;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return result;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function render(result) {
|
|
134
|
+
const lines = ['Godpowers Ledger Import', ''];
|
|
135
|
+
lines.push(`Source: ${result.source}`);
|
|
136
|
+
if (!result.found) {
|
|
137
|
+
lines.push('');
|
|
138
|
+
lines.push('No .mythify/ ledger found at the source. Pass --from <path>.');
|
|
139
|
+
return lines.join('\n');
|
|
140
|
+
}
|
|
141
|
+
const i = result.imported;
|
|
142
|
+
lines.push(`Imported: ${i.verifications} verification(s), ${i.reflections} reflection(s), ${i.memory} memory entr(ies), ${i.lessons} lesson(s), ${i.outcomes} outcome(s)`);
|
|
143
|
+
lines.push('Records were appended to the Godpowers ledger (no state rollup, no gate events).');
|
|
144
|
+
return lines.join('\n');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
module.exports = { importMythify, rebindRecord, render };
|