@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 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.
@@ -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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@besales/ops-framework",
3
- "version": "0.1.27",
3
+ "version": "0.1.28",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "ops-agent": "bin/ops-agent.mjs"