@besales/ops-framework 0.1.27 → 0.1.28
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 +6 -0
- package/bin/run-verify.mjs +107 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.28
|
|
4
|
+
|
|
5
|
+
- Added Verify reuse guard: a passing `verify.result.json` is reused when `plan.md` and `execution.md` hashes are unchanged, unless `--force` is passed.
|
|
6
|
+
- Updated status after external Verify pass/fail/return verdicts so Supervisor routing does not keep asking for stale Verify reruns.
|
|
7
|
+
- Routed passing Verify results to retrospective/learning closeout instead of repeated verifier loops.
|
|
8
|
+
|
|
3
9
|
## 0.1.27
|
|
4
10
|
|
|
5
11
|
- Updated internal Verify closeout behavior so `status.md` moves to retrospective/learning closeout instead of continuing to ask for external Verify.
|
package/bin/run-verify.mjs
CHANGED
|
@@ -46,6 +46,7 @@ async function runMain() {
|
|
|
46
46
|
const taskDir = resolveTaskDir(taskArg);
|
|
47
47
|
const taskId = path.basename(taskDir);
|
|
48
48
|
const verifierConfig = resolveVerifierConfig(args);
|
|
49
|
+
const force = getFlag(args, 'force', false) === true;
|
|
49
50
|
const runStartedAt = new Date();
|
|
50
51
|
appendVerifyTimeline(taskDir, {
|
|
51
52
|
event: 'verify_started',
|
|
@@ -85,6 +86,25 @@ async function runMain() {
|
|
|
85
86
|
return;
|
|
86
87
|
}
|
|
87
88
|
|
|
89
|
+
const reusableVerify = readReusableVerifyResult({ taskDir, planSha, executionSha });
|
|
90
|
+
if (reusableVerify && !force) {
|
|
91
|
+
updateStatusForVerifyResult(taskDir, reusableVerify, {
|
|
92
|
+
reused: true,
|
|
93
|
+
verifierMode: reusableVerify.verificationMode || verifierConfig.mode,
|
|
94
|
+
});
|
|
95
|
+
appendVerifyTimeline(taskDir, {
|
|
96
|
+
event: 'verify_reused',
|
|
97
|
+
verdict: reusableVerify.verdict,
|
|
98
|
+
verifierRunId: reusableVerify.verifierRunId || null,
|
|
99
|
+
timing: buildTiming(runStartedAt),
|
|
100
|
+
});
|
|
101
|
+
appendOrchestrationLog(taskDir, `verify result reused; verdict=${reusableVerify.verdict}; plan/execution unchanged; use --force to rerun`);
|
|
102
|
+
console.log(`Verifier result reused for ${taskId}: ${reusableVerify.verdict}`);
|
|
103
|
+
console.log('- reason: plan.md and execution.md hashes match existing passing verify.result.json');
|
|
104
|
+
console.log('- use --force to rerun Verify anyway');
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
88
108
|
if (verifierConfig.mode === 'internal_supervisor') {
|
|
89
109
|
writeInternalSupervisorVerify({
|
|
90
110
|
taskDir,
|
|
@@ -268,6 +288,10 @@ async function runMain() {
|
|
|
268
288
|
|
|
269
289
|
writeTaskFile(taskDir, 'verify.md', verifyMarkdown);
|
|
270
290
|
writeTaskFile(taskDir, 'verify.result.json', JSON.stringify(verifyResultJson, null, 2));
|
|
291
|
+
updateStatusForVerifyResult(taskDir, verifyResultJson, {
|
|
292
|
+
reused: false,
|
|
293
|
+
verifierMode: 'external_cli',
|
|
294
|
+
});
|
|
271
295
|
if (finalPack) {
|
|
272
296
|
recordLlmInputUsage({
|
|
273
297
|
taskDir,
|
|
@@ -303,6 +327,85 @@ function buildTiming(startedAt, completedAt = new Date()) {
|
|
|
303
327
|
};
|
|
304
328
|
}
|
|
305
329
|
|
|
330
|
+
function readReusableVerifyResult({ taskDir, planSha, executionSha }) {
|
|
331
|
+
const result = readOptionalJson(taskDir, 'verify.result.json');
|
|
332
|
+
if (!result || typeof result !== 'object' || Array.isArray(result)) {
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
if (!['pass', 'pass_with_notes'].includes(String(result.verdict || '').toLowerCase())) {
|
|
336
|
+
return null;
|
|
337
|
+
}
|
|
338
|
+
if (result.planSha !== planSha || result.executionSha !== executionSha) {
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
if (result.readyForRetrospective !== true) {
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
return result;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function updateStatusForVerifyResult(taskDir, result, { reused = false, verifierMode = null } = {}) {
|
|
348
|
+
const verdict = String(result?.verdict || 'unknown').toLowerCase();
|
|
349
|
+
const mode = verifierMode || result?.verificationMode || 'unknown';
|
|
350
|
+
const commonResult = [
|
|
351
|
+
`- \`verify.result.json\`: \`${verdict}\``,
|
|
352
|
+
`- Verification mode: \`${mode}\``,
|
|
353
|
+
`- Ready for retrospective: \`${Boolean(result?.readyForRetrospective)}\``,
|
|
354
|
+
reused ? '- Reused: `true` (plan/execution unchanged)' : null,
|
|
355
|
+
].filter(Boolean).join('\n');
|
|
356
|
+
|
|
357
|
+
if (['pass', 'pass_with_notes'].includes(verdict)) {
|
|
358
|
+
updateStatus(taskDir, {
|
|
359
|
+
stage: 'Verify complete',
|
|
360
|
+
verifyVerdict: `\`${verdict}\``,
|
|
361
|
+
verifyResult: commonResult,
|
|
362
|
+
supervisorAction: reused
|
|
363
|
+
? 'Existing passing Verify result reused because plan/execution hashes are unchanged.'
|
|
364
|
+
: 'Verify completed with sufficient evidence for this slice.',
|
|
365
|
+
expectedOutcome: 'Retrospective, learning closeout, then task closeout.',
|
|
366
|
+
humanApproval: 'no',
|
|
367
|
+
nextStep: 'Run learning closeout / retrospective. Do not rerun Verify unless plan/execution changes or explicit --force/human escalation applies.',
|
|
368
|
+
});
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (verdict === 'return_to_execute') {
|
|
373
|
+
updateStatus(taskDir, {
|
|
374
|
+
stage: 'Execute',
|
|
375
|
+
verifyVerdict: '`return_to_execute`',
|
|
376
|
+
verifyResult: commonResult,
|
|
377
|
+
supervisorAction: 'Verify returned blocking execution findings.',
|
|
378
|
+
expectedOutcome: 'Address blocking Verify findings, update execution evidence, then rerun Verify.',
|
|
379
|
+
humanApproval: 'no',
|
|
380
|
+
nextStep: 'Return to Execute and fix the blocking Verify findings before another Verify run.',
|
|
381
|
+
});
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
if (verdict === 'return_to_plan') {
|
|
386
|
+
updateStatus(taskDir, {
|
|
387
|
+
stage: 'Plan',
|
|
388
|
+
verifyVerdict: '`return_to_plan`',
|
|
389
|
+
verifyResult: commonResult,
|
|
390
|
+
supervisorAction: 'Verify found plan/scope mismatch.',
|
|
391
|
+
expectedOutcome: 'Revise plan/check artifacts before Execute continues.',
|
|
392
|
+
humanApproval: 'maybe',
|
|
393
|
+
nextStep: 'Return to Plan and resolve the Verify findings before Execute continues.',
|
|
394
|
+
});
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
updateStatus(taskDir, {
|
|
399
|
+
stage: 'Verify',
|
|
400
|
+
verifyVerdict: `\`${verdict}\``,
|
|
401
|
+
verifyResult: commonResult,
|
|
402
|
+
supervisorAction: 'Verify did not produce a passing result.',
|
|
403
|
+
expectedOutcome: 'Resolve verifier failure or request human decision.',
|
|
404
|
+
humanApproval: 'maybe',
|
|
405
|
+
nextStep: 'Resolve Verify result before continuing.',
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
|
|
306
409
|
function appendVerifyTimeline(taskDir, event) {
|
|
307
410
|
const timelinePath = path.join(taskDir, 'verify-timeline.json');
|
|
308
411
|
let existing = [];
|
|
@@ -703,6 +806,10 @@ function writeVerifierFailure({
|
|
|
703
806
|
};
|
|
704
807
|
writeTaskFile(taskDir, 'verify.md', verifyMarkdown);
|
|
705
808
|
writeTaskFile(taskDir, 'verify.result.json', JSON.stringify(result, null, 2));
|
|
809
|
+
updateStatusForVerifyResult(taskDir, result, {
|
|
810
|
+
reused: false,
|
|
811
|
+
verifierMode: 'external_cli',
|
|
812
|
+
});
|
|
706
813
|
appendOrchestrationLog(taskDir, `external CLI verifier failed via ${verifierConfig.provider}; failureReason=${failureReason}; runId=${verifierRunId}`);
|
|
707
814
|
}
|
|
708
815
|
|