dsh-doublecheck 0.9.30 → 0.9.31

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.
@@ -348,6 +348,7 @@ export function apply(ctx, config) {
348
348
  ],
349
349
  required: true,
350
350
  },
351
+ verificationNote: { oneOf: [{ type: 'string' }, { type: 'null' }], required: true },
351
352
  path: { oneOf: [{ type: 'string' }, { type: 'null' }], required: true },
352
353
  written: { type: 'boolean', required: true },
353
354
  },
@@ -365,13 +366,14 @@ export function apply(ctx, config) {
365
366
  ? { spec: null, testRuns: { failed: 0, passed: 0 }, timeline: [], edits: 0, review: null }
366
367
  : foldReportFacts(sessionEvents(session), reportDetection);
367
368
  const verify = args.verify ?? config.reportVerify;
368
- const verification = verify
369
+ const attempt = verify
369
370
  ? await runVerifyWorkflow(ctx, config, facts.spec, exec)
370
- : null;
371
+ : { verification: null, note: null };
371
372
  const report = {
372
373
  ...facts,
373
- verdict: deriveReportVerdict(facts, verification),
374
- verification,
374
+ verdict: deriveReportVerdict(facts, attempt.verification),
375
+ verification: attempt.verification,
376
+ verificationNote: attempt.note,
375
377
  path: null,
376
378
  written: false,
377
379
  };
@@ -379,7 +381,7 @@ export function apply(ctx, config) {
379
381
  report.path = outcome.path;
380
382
  report.written = outcome.written;
381
383
  if (session !== undefined) {
382
- ctx.emit('doublecheck/report', { session, verdict: report.verdict, verification, path: report.path, written: report.written });
384
+ ctx.emit('doublecheck/report', { session, verdict: report.verdict, verification: report.verification, verificationNote: report.verificationNote, path: report.path, written: report.written });
383
385
  }
384
386
  return report;
385
387
  },
@@ -394,19 +396,65 @@ export function apply(ctx, config) {
394
396
  }));
395
397
  }
396
398
  /**
397
- * Run the per-dimension verification workflow through the workflow seam,
398
- * when one is mounted. A missing seam or a non-completed run settles as
399
- * `null` (no verification) — the report states it did not run.
400
- * @returns the settled checks with the completeness flag, or null when
401
- * verification did not run at all.
399
+ * Resolve the workflow engine for this call.
400
+ *
401
+ * A profile-level bundle row lives in the Host realm, but the web profile
402
+ * mounts the engine inside the calling preset's isolated `delegation` group,
403
+ * so `ctx.get('workflowEngine')` from the plugin context returns undefined
404
+ * even though the service is registered. `agentPresets.serviceFor()` is the
405
+ * host's explicit cross-realm accessor (it inspects the agent's retained
406
+ * composition), so it is tried first; the plain lookup stays as the fallback
407
+ * for profiles that mount the engine on the host plane.
408
+ * @param ctx - the plugin context (Host/profile realm).
409
+ * @param agent - the calling agent, when the tool call has one.
410
+ * @returns the engine, or a human-readable reason it is unreachable.
411
+ */
412
+ function resolveWorkflowEngine(ctx, agent) {
413
+ const presets = ctx.get('agentPresets');
414
+ if (agent?.ctx !== undefined && typeof presets?.serviceFor === 'function') {
415
+ try {
416
+ const scoped = presets.serviceFor(agent, 'workflowEngine');
417
+ if (scoped !== undefined)
418
+ return { engine: scoped };
419
+ }
420
+ catch (error) {
421
+ ctx.logger.debug(`dsh-doublecheck: preset workflowEngine lookup failed: ${String(error)}`);
422
+ }
423
+ }
424
+ const host = ctx.get('workflowEngine');
425
+ if (host !== undefined)
426
+ return { engine: host };
427
+ return {
428
+ why: presets === undefined
429
+ ? 'the agent-preset registry is not mounted and the host plane has no workflowEngine'
430
+ : typeof presets.serviceFor !== 'function'
431
+ ? 'the mounted agentPresets service has no serviceFor() and the host plane has no workflowEngine'
432
+ : 'the calling agent preset does not mount a workflow engine (standard/cordis enable workflow-ptc; ptc/minimal do not) and the host plane has none',
433
+ };
434
+ }
435
+ /**
436
+ * Run the per-dimension verification workflow through the workflow seam.
437
+ *
438
+ * Every way this can fail — no spec, no agent, an engine that is unreachable
439
+ * from this realm, a rejected start, an early stop, a malformed value, a
440
+ * rejected result — settles as `verification: null` plus a note that the
441
+ * report renders. A missing seam must not fail the report call itself, but it
442
+ * must not look like "the checks all passed and there was nothing to say"
443
+ * either.
444
+ * @returns the settled checks, or null verification with the reason.
402
445
  */
403
446
  async function runVerifyWorkflow(ctx, config, spec, exec) {
404
- if (spec === null || exec.agent === undefined)
405
- return null;
406
- const engine = ctx.get('workflowEngine');
447
+ if (spec === null) {
448
+ return { verification: null, note: 'no committed spec is on record; record one with doublecheck_spec before verification' };
449
+ }
450
+ if (exec.agent === undefined) {
451
+ return { verification: null, note: 'the call has no owning agent, so no agent preset can supply a workflow engine' };
452
+ }
453
+ const { engine, why } = resolveWorkflowEngine(ctx, exec.agent);
407
454
  if (engine === undefined) {
408
- ctx.logger.warn('dsh-doublecheck: verification skipped: the workflowEngine seam is not mounted');
409
- return null;
455
+ const note = `verification did not run: ${why}`;
456
+ ctx.logger.warn(`dsh-doublecheck: ${note}`);
457
+ return { verification: null, note };
410
458
  }
411
459
  let run;
412
460
  try {
@@ -423,16 +471,20 @@ async function runVerifyWorkflow(ctx, config, spec, exec) {
423
471
  catch (error) {
424
472
  // Invalid script/meta or an absent verify provider: verification did not
425
473
  // run, and the report states so instead of failing the report itself.
426
- ctx.logger.warn(`dsh-doublecheck: verification workflow rejected: ${String(error)}`);
427
- return null;
474
+ const note = `verification did not run: the workflow engine rejected the verify run: ${String(error)}`;
475
+ ctx.logger.warn(`dsh-doublecheck: ${note}`);
476
+ return { verification: null, note };
428
477
  }
429
478
  try {
430
479
  const result = await run.result;
431
- if (result.stopReason !== 'completed')
432
- return null;
480
+ if (result.stopReason !== 'completed') {
481
+ const detail = result.error === undefined ? '' : `: ${result.error}`;
482
+ return { verification: null, note: `verification did not run: the verify workflow stopped with "${result.stopReason}" before completing${detail}` };
483
+ }
433
484
  const value = result.value;
434
- if (value === null || value === undefined || !Array.isArray(value.checks))
435
- return null;
485
+ if (value === null || value === undefined || !Array.isArray(value.checks)) {
486
+ return { verification: null, note: 'verification did not run: the verify workflow completed without a structured checks array' };
487
+ }
436
488
  const checks = [];
437
489
  for (const check of value.checks) {
438
490
  if (!VERIFY_DIMENSIONS.includes(check.dimension))
@@ -443,17 +495,23 @@ async function runVerifyWorkflow(ctx, config, spec, exec) {
443
495
  // judged here so a checker that silently dropped a dimension cannot
444
496
  // upgrade the delivery.
445
497
  const complete = VERIFY_DIMENSIONS.every(dimension => checks.some(check => check.dimension === dimension));
446
- return { checks, complete };
498
+ return { verification: { checks, complete }, note: null };
447
499
  }
448
500
  catch (error) {
449
501
  // A broken workflow engine must not fail the report call itself: the
450
502
  // delivery settles as unverified and the markdown says verification did
451
- // not run.
452
- ctx.logger.warn(`dsh-doublecheck: verification workflow failed: ${String(error)}`);
453
- return null;
503
+ // not run, with the reason.
504
+ const note = `verification did not run: the verify workflow failed: ${String(error)}`;
505
+ ctx.logger.warn(`dsh-doublecheck: ${note}`);
506
+ return { verification: null, note };
454
507
  }
455
508
  finally {
456
- await run.dispose();
509
+ try {
510
+ await run.dispose();
511
+ }
512
+ catch (error) {
513
+ ctx.logger.warn(`dsh-doublecheck: verify run disposal failed: ${String(error)}`);
514
+ }
457
515
  }
458
516
  }
459
517
  /** Write the report markdown through the filesystem seam, when one exists. */
@@ -113,6 +113,7 @@ export function doublecheckHandler(deps) {
113
113
  ...facts,
114
114
  verdict: deriveReportVerdict(facts, null),
115
115
  verification: null,
116
+ verificationNote: null,
116
117
  path: null,
117
118
  written: false,
118
119
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-doublecheck",
3
- "version": "0.9.30",
3
+ "version": "0.9.31",
4
4
  "description": "Delivery quality gate for DeepSeek Harness: grill the requirements, test the implementation, prove the delivery, and gate the handoff with a deliverable/rework decision.",
5
5
  "packageManager": "pnpm@11.7.0",
6
6
  "repository": {