@agwab/pi-workflow 0.3.0 → 0.4.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.
Files changed (90) hide show
  1. package/README.md +3 -1
  2. package/dist/artifact-graph-runtime.d.ts +1 -1
  3. package/dist/artifact-graph-runtime.js +10 -5
  4. package/dist/artifact-graph-schema.js +127 -5
  5. package/dist/compiler.js +46 -11
  6. package/dist/dynamic-decision.d.ts +1 -0
  7. package/dist/dynamic-decision.js +7 -0
  8. package/dist/dynamic-generated-task-runtime.js +3 -1
  9. package/dist/dynamic-profiles.d.ts +1 -0
  10. package/dist/dynamic-profiles.js +3 -0
  11. package/dist/engine-run-graph.d.ts +2 -0
  12. package/dist/engine-run-graph.js +55 -5
  13. package/dist/engine.js +278 -15
  14. package/dist/extension.js +3 -2
  15. package/dist/index.d.ts +8 -0
  16. package/dist/index.js +4 -0
  17. package/dist/prompt-json.d.ts +7 -0
  18. package/dist/prompt-json.js +13 -0
  19. package/dist/roles.d.ts +1 -1
  20. package/dist/roles.js +5 -8
  21. package/dist/store.d.ts +20 -1
  22. package/dist/store.js +89 -29
  23. package/dist/strings.d.ts +11 -0
  24. package/dist/strings.js +24 -0
  25. package/dist/subagent-backend.js +557 -13
  26. package/dist/types.d.ts +101 -1
  27. package/dist/verification-ontology.d.ts +31 -0
  28. package/dist/verification-ontology.js +66 -0
  29. package/dist/workflow-artifact-tool.js +5 -6
  30. package/dist/workflow-artifacts.d.ts +7 -0
  31. package/dist/workflow-artifacts.js +55 -4
  32. package/dist/workflow-fetch-cache-extension.d.ts +1 -0
  33. package/dist/workflow-fetch-cache-extension.js +57 -9
  34. package/dist/workflow-metrics.d.ts +113 -0
  35. package/dist/workflow-metrics.js +272 -0
  36. package/dist/workflow-output-artifacts.js +5 -3
  37. package/dist/workflow-partial-output.d.ts +45 -0
  38. package/dist/workflow-partial-output.js +205 -0
  39. package/dist/workflow-progress-health.js +42 -10
  40. package/dist/workflow-web-source-extension.js +27 -4
  41. package/dist/workflow-web-source.js +26 -12
  42. package/docs/usage.md +76 -29
  43. package/node_modules/@agwab/pi-subagent/package.json +1 -1
  44. package/node_modules/@agwab/pi-subagent/src/index.ts +53 -5
  45. package/node_modules/@agwab/pi-subagent/src/panel.ts +7 -3
  46. package/package.json +2 -2
  47. package/skills/workflow-guide/SKILL.md +1 -0
  48. package/src/artifact-graph-runtime.ts +19 -13
  49. package/src/artifact-graph-schema.ts +143 -3
  50. package/src/cli.mjs +52 -0
  51. package/src/compiler.ts +49 -9
  52. package/src/dynamic-decision.ts +11 -0
  53. package/src/dynamic-generated-task-runtime.ts +3 -1
  54. package/src/dynamic-profiles.ts +4 -0
  55. package/src/engine-run-graph.ts +63 -4
  56. package/src/engine.ts +400 -14
  57. package/src/extension.ts +3 -2
  58. package/src/index.ts +49 -0
  59. package/src/prompt-json.ts +13 -0
  60. package/src/roles.ts +6 -9
  61. package/src/store.ts +123 -34
  62. package/src/strings.ts +38 -0
  63. package/src/subagent-backend.ts +727 -41
  64. package/src/types.ts +110 -2
  65. package/src/verification-ontology.ts +88 -0
  66. package/src/workflow-artifact-tool.ts +5 -7
  67. package/src/workflow-artifacts.ts +83 -3
  68. package/src/workflow-fetch-cache-extension.ts +78 -13
  69. package/src/workflow-metrics.ts +478 -0
  70. package/src/workflow-output-artifacts.ts +5 -3
  71. package/src/workflow-partial-output.ts +299 -0
  72. package/src/workflow-progress-health.ts +47 -15
  73. package/src/workflow-web-source-extension.ts +33 -4
  74. package/src/workflow-web-source.ts +36 -12
  75. package/workflows/README.md +7 -25
  76. package/workflows/deep-research/batched-verification.spec.json +253 -0
  77. package/workflows/deep-research/helpers/batch-verification-candidates.mjs +136 -0
  78. package/workflows/deep-research/helpers/claim-evidence-gate.mjs +173 -20
  79. package/workflows/deep-research/helpers/normalize-input-packet.mjs +80 -1
  80. package/workflows/deep-research/helpers/render-executive.mjs +32 -5
  81. package/workflows/deep-research/helpers/shadow-select-verification.mjs +229 -0
  82. package/workflows/deep-research/helpers/verification-ontology.mjs +77 -0
  83. package/workflows/deep-research/schemas/deep-research-executive-render-control.schema.json +3 -2
  84. package/workflows/deep-research/schemas/deep-research-research-questions-control.schema.json +38 -0
  85. package/workflows/deep-research/schemas/deep-research-sanitize-claims-control.schema.json +63 -0
  86. package/workflows/deep-research/schemas/deep-research-verify-claims-batch-control.schema.json +47 -0
  87. package/workflows/deep-research/schemas/deep-research-verify-claims-control.schema.json +10 -3
  88. package/workflows/deep-research/spec.json +32 -12
  89. package/skills/workflow-guide/scaffolds/dag-required-reads/spec.json.validate.stderr +0 -0
  90. package/skills/workflow-guide/scaffolds/dag-required-reads/spec.json.validate.stdout +0 -13
@@ -1,3 +1,9 @@
1
+ import {
2
+ VERIFICATION_STATUS,
3
+ VERIFICATION_STATUS_BUCKETS,
4
+ canonicalVerificationStatus,
5
+ } from "./verification-ontology.mjs";
6
+
1
7
  // Deterministic claim audit for deep-research.
2
8
  //
3
9
  // Sources: plan (optional), normalize-claims (optional), verify-claims foreach
@@ -16,10 +22,6 @@ function asArray(value) {
16
22
  if (Array.isArray(value)) return value;
17
23
  if (value && typeof value === "object") {
18
24
  if (Array.isArray(value.auditedClaims)) return value.auditedClaims;
19
- if (Array.isArray(value.claims)) return value.claims;
20
- if (Array.isArray(value.claimVerdicts)) return value.claimVerdicts;
21
- if (Array.isArray(value.verdicts)) return value.verdicts;
22
- if (Array.isArray(value.items)) return value.items;
23
25
  if (
24
26
  "status" in value ||
25
27
  "verdict" in value ||
@@ -28,6 +30,11 @@ function asArray(value) {
28
30
  "id" in value
29
31
  )
30
32
  return [value];
33
+ if (Array.isArray(value.results)) return value.results;
34
+ if (Array.isArray(value.claims)) return value.claims;
35
+ if (Array.isArray(value.claimVerdicts)) return value.claimVerdicts;
36
+ if (Array.isArray(value.verdicts)) return value.verdicts;
37
+ if (Array.isArray(value.items)) return value.items;
31
38
  return Object.values(value).flatMap(asArray);
32
39
  }
33
40
  return [];
@@ -345,40 +352,133 @@ function compactStrings(values) {
345
352
  }
346
353
 
347
354
  function canonicalVerifierStatus(status) {
348
- return status === "partiallySupported" ? "partially_supported" : status;
355
+ return canonicalVerificationStatus(status);
349
356
  }
350
357
 
351
358
  function conservativeVerifierStatus(statuses) {
352
359
  const normalized = statuses.map(canonicalVerifierStatus);
353
360
  for (const status of [
354
- "conflicting",
355
- "unsupported",
356
- "partially_supported",
357
- "unverified",
361
+ VERIFICATION_STATUS.CONFLICTING,
362
+ VERIFICATION_STATUS.UNSUPPORTED,
363
+ VERIFICATION_STATUS.VERIFICATION_BLOCKED,
364
+ VERIFICATION_STATUS.PARTIALLY_SUPPORTED,
365
+ VERIFICATION_STATUS.UNVERIFIED,
358
366
  ]) {
359
367
  if (normalized.includes(status)) return status;
360
368
  }
361
- if (normalized.every((status) => status === "verified")) return "verified";
369
+ if (normalized.every((status) => status === VERIFICATION_STATUS.VERIFIED))
370
+ return VERIFICATION_STATUS.VERIFIED;
362
371
  return (
363
372
  normalized.find((status) => typeof status === "string" && status) ??
364
- "unverified"
373
+ VERIFICATION_STATUS.UNVERIFIED
365
374
  );
366
375
  }
367
376
 
368
- function issueForVerifierRow({ sourceId, claim, reason, claimId, index }) {
377
+ function issueForVerifierRow({
378
+ sourceId,
379
+ claim,
380
+ reason,
381
+ claimId,
382
+ index,
383
+ ...details
384
+ }) {
369
385
  return {
370
386
  sourceId,
371
387
  ...(Number.isInteger(index) ? { index } : {}),
372
388
  ...(claimId ? { claimId } : {}),
389
+ ...details,
373
390
  reason,
374
391
  status: verdictOf(claim),
375
392
  nextStep:
376
393
  reason === "unknown_claim_id"
377
394
  ? "Verify-claims output did not match any normalized verification candidate; quarantine it from claim counts."
378
- : "Verifier output is missing a usable string id/claimId; rerun or repair the verifier row before counting it.",
395
+ : reason === "batch_result_id_not_in_source_batch"
396
+ ? "Verifier batch output included a claim id outside the source batch; rerun or repair the batch before counting any row."
397
+ : reason === "unknown_verification_batch_id"
398
+ ? "Verifier batch output came from an unknown batch id; rerun or repair the batch before counting any row."
399
+ : "Verifier output is missing a usable string id/claimId; rerun or repair the verifier row before counting it.",
379
400
  };
380
401
  }
381
402
 
403
+ function asBatchArray(value) {
404
+ if (Array.isArray(value?.batches)) return value.batches;
405
+ if (Array.isArray(value)) return value;
406
+ return [];
407
+ }
408
+
409
+ function buildBatchMembershipById(verificationBatches) {
410
+ const batches = new Map();
411
+ for (const batch of asBatchArray(verificationBatches)) {
412
+ const id = typeof batch?.id === "string" ? batch.id.trim() : "";
413
+ if (!id) continue;
414
+ const claimIds = Array.isArray(batch.claimIds)
415
+ ? batch.claimIds
416
+ : Array.isArray(batch.claims)
417
+ ? batch.claims.map(
418
+ (claim, index) =>
419
+ claimIdOf(claim).id ??
420
+ `candidate-${String(index + 1).padStart(3, "0")}`,
421
+ )
422
+ : [];
423
+ batches.set(
424
+ id,
425
+ new Set(
426
+ claimIds
427
+ .filter((claimId) => typeof claimId === "string")
428
+ .map((claimId) => claimId.trim())
429
+ .filter(Boolean),
430
+ ),
431
+ );
432
+ }
433
+ return batches;
434
+ }
435
+
436
+ function verifierBatchId(sourceId) {
437
+ const prefix = "verify-claims.";
438
+ if (typeof sourceId !== "string" || !sourceId.startsWith(prefix)) return null;
439
+ const id = sourceId.slice(prefix.length).trim();
440
+ return id || null;
441
+ }
442
+
443
+ function buildBatchIdBySourceName(sourceStatuses) {
444
+ const bySource = new Map();
445
+ for (const status of Array.isArray(sourceStatuses) ? sourceStatuses : []) {
446
+ const source = typeof status?.source === "string" ? status.source : "";
447
+ const batchId = verifierBatchId(status?.specId);
448
+ if (source && batchId) bySource.set(source, batchId);
449
+ }
450
+ return bySource;
451
+ }
452
+
453
+ function batchMembershipIssue({
454
+ sourceId,
455
+ claimId,
456
+ batchMembershipById,
457
+ batchIdBySourceName,
458
+ }) {
459
+ if (!(batchMembershipById instanceof Map) || batchMembershipById.size === 0)
460
+ return null;
461
+ const batchId =
462
+ verifierBatchId(sourceId) ?? batchIdBySourceName?.get(sourceId);
463
+ if (!batchId) return null;
464
+ const expectedClaimIds = batchMembershipById.get(batchId);
465
+ if (!expectedClaimIds) {
466
+ return {
467
+ reason: "unknown_verification_batch_id",
468
+ batchId,
469
+ expectedBatchIds: [...batchMembershipById.keys()],
470
+ };
471
+ }
472
+ if (!expectedClaimIds.has(claimId)) {
473
+ return {
474
+ reason: "batch_result_id_not_in_source_batch",
475
+ batchId,
476
+ expectedClaimIds: [...expectedClaimIds],
477
+ };
478
+ }
479
+ return null;
480
+ }
481
+
382
482
  function gapForVerifierIssue(issue) {
383
483
  return {
384
484
  ...(issue.claimId ? { claimId: issue.claimId } : {}),
@@ -437,12 +537,33 @@ function mergeVerifierRows(rows) {
437
537
  };
438
538
  }
439
539
 
440
- const STATUS_BUCKETS = {
441
- verified: "verified",
442
- partially_supported: "partiallySupported",
443
- unsupported: "unsupported",
444
- conflicting: "conflicting",
445
- };
540
+ function buildBatchAdoptionReadiness({ gateSummary, candidateCount }) {
541
+ const checks = [
542
+ ["invalid_verifier_rows", gateSummary.invalidVerifierRows],
543
+ ["missing_verifier_results", gateSummary.missingVerifierResults],
544
+ ["duplicate_verifier_rows", gateSummary.duplicateVerifierRows],
545
+ ["duplicate_status_conflicts", gateSummary.duplicateStatusConflicts],
546
+ ["invalid_normalized_candidates", gateSummary.invalidNormalizedCandidates],
547
+ ["source_ref_join_failures", gateSummary.sourceRefJoinFailures],
548
+ ];
549
+ const blockers = checks
550
+ .filter(([, count]) => Number(count ?? 0) > 0)
551
+ .map(([reason, count]) => ({ reason, count }));
552
+ if (candidateCount === 0)
553
+ blockers.push({ reason: "no_verification_candidates", count: 0 });
554
+ return {
555
+ status: blockers.length === 0 ? "eligible_for_canary" : "blocked",
556
+ adopted: false,
557
+ canaryRequired: true,
558
+ reason:
559
+ blockers.length === 0
560
+ ? "Verifier identity/sourceRef integrity is clean; batch adoption still requires a non-holdout canary before use."
561
+ : "Batch adoption is blocked until verifier identity/sourceRef integrity issues are resolved.",
562
+ blockers,
563
+ };
564
+ }
565
+
566
+ const STATUS_BUCKETS = VERIFICATION_STATUS_BUCKETS;
446
567
 
447
568
  function findSource(sources, stageId) {
448
569
  for (const [specId, source] of Object.entries(sources ?? {})) {
@@ -451,11 +572,18 @@ function findSource(sources, stageId) {
451
572
  return null;
452
573
  }
453
574
 
454
- export default async function claimEvidenceGate({ sources, options = {} }) {
575
+ export default async function claimEvidenceGate({
576
+ sources,
577
+ options = {},
578
+ context = {},
579
+ }) {
455
580
  const plan = findSource(sources, "plan");
456
581
  const normalizeClaims = findSource(sources, "normalize-claims");
457
582
  const sanitizedCandidates = findSource(sources, "sanitize-claims");
458
583
  const normalized = sanitizedCandidates ?? normalizeClaims;
584
+ const verificationBatches = findSource(sources, "verification-batches");
585
+ const batchMembershipById = buildBatchMembershipById(verificationBatches);
586
+ const batchIdBySourceName = buildBatchIdBySourceName(context.sourceStatuses);
459
587
  const normalizeInputPacket = findSource(sources, "normalize-input-packet");
460
588
  const urlToSourceRef = buildUrlSourceRefLookup(normalizeInputPacket);
461
589
  const candidateRecords = [];
@@ -568,6 +696,25 @@ export default async function claimEvidenceGate({ sources, options = {} }) {
568
696
  gateSummary.invalidVerifierRows += 1;
569
697
  continue;
570
698
  }
699
+ const batchIssue = batchMembershipIssue({
700
+ sourceId,
701
+ claimId: idCheck.id,
702
+ batchMembershipById,
703
+ batchIdBySourceName,
704
+ });
705
+ if (batchIssue) {
706
+ const issue = issueForVerifierRow({
707
+ sourceId,
708
+ claim,
709
+ index,
710
+ claimId: idCheck.id,
711
+ ...batchIssue,
712
+ });
713
+ invalidVerifierRows.push(issue);
714
+ remainingGaps.push(gapForVerifierIssue(issue));
715
+ gateSummary.invalidVerifierRows += 1;
716
+ continue;
717
+ }
571
718
  const row = {
572
719
  sourceId,
573
720
  claimId: idCheck.id,
@@ -806,6 +953,7 @@ export default async function claimEvidenceGate({ sources, options = {} }) {
806
953
  partiallySupported: [],
807
954
  unsupported: [],
808
955
  conflicting: [],
956
+ verificationBlocked: [],
809
957
  other: [],
810
958
  };
811
959
  for (const claim of auditedClaims) {
@@ -859,11 +1007,16 @@ export default async function claimEvidenceGate({ sources, options = {} }) {
859
1007
  verdictDigest: claim.verdictDigest,
860
1008
  correctionOrCounterclaim: claim.correctionOrCounterclaim,
861
1009
  }));
1010
+ const batchAdoptionReadiness = buildBatchAdoptionReadiness({
1011
+ gateSummary,
1012
+ candidateCount: candidateRecords.length,
1013
+ });
862
1014
 
863
1015
  return {
864
1016
  auditedClaims,
865
1017
  claimDigests,
866
1018
  gateSummary,
1019
+ batchAdoptionReadiness,
867
1020
  remainingGaps,
868
1021
  sourceRefJoinFailures,
869
1022
  invalidVerifierRows,
@@ -157,6 +157,60 @@ function compactGap(gap, sourceId, index) {
157
157
  };
158
158
  }
159
159
 
160
+ function compactBudgetLedger(source, sourceId) {
161
+ const ledger = asObject(source.budgetLedger);
162
+ if (Object.keys(ledger).length === 0) return undefined;
163
+ const searchBudget = Number(ledger.searchBudget);
164
+ const searchCallsUsed = Number(ledger.searchCallsUsed);
165
+ return {
166
+ sourceId,
167
+ question: stringOf(source.question)?.slice(0, 300),
168
+ ...(Number.isFinite(searchBudget) ? { searchBudget } : {}),
169
+ ...(Number.isFinite(searchCallsUsed) ? { searchCallsUsed } : {}),
170
+ searchQueriesAttempted: compactStrings(ledger.searchQueriesAttempted, 12),
171
+ omittedSearchQueries: compactStrings(ledger.omittedSearchQueries, 12),
172
+ budgetExhausted: ledger.budgetExhausted === true,
173
+ gapRecorded: ledger.gapRecorded === true,
174
+ };
175
+ }
176
+
177
+ function sourceStatusesOf(context) {
178
+ return asArray(context?.sourceStatuses).map(asObject);
179
+ }
180
+
181
+ function isResearchQuestionStatus(status) {
182
+ return [status.source, status.specId, status.displayName, status.stageId]
183
+ .map((value) => String(value ?? ""))
184
+ .some(
185
+ (value) =>
186
+ value === "research-questions" ||
187
+ value.startsWith("research-questions."),
188
+ );
189
+ }
190
+
191
+ function compactSourceStatusGap(status, index) {
192
+ const sourceId =
193
+ stringOf(status.specId) ??
194
+ stringOf(status.source) ??
195
+ stringOf(status.displayName) ??
196
+ `research-questions.status-${String(index + 1).padStart(3, "0")}`;
197
+ const detail = compactStrings(
198
+ [status.statusDetail, status.errorType, status.lastMessage],
199
+ 3,
200
+ ).join("; ");
201
+ return {
202
+ originLocator: `${sourceId}.status-gap`,
203
+ sourceId,
204
+ lead: `Research question source ${sourceId} ended with status ${String(status.status ?? "unknown")}${detail ? ` (${detail.slice(0, 300)})` : ""}.`,
205
+ sourceUrls: [],
206
+ sourceRefs: [],
207
+ factSlotIds: [],
208
+ reason: "research_question_non_completed",
209
+ status: String(status.status ?? "unknown"),
210
+ ...(stringOf(status.taskId) ? { taskId: stringOf(status.taskId) } : {}),
211
+ };
212
+ }
213
+
160
214
  function pushBounded(target, overflow, items, limit, overflowKind) {
161
215
  for (const item of items) {
162
216
  if (target.length < limit) target.push(item);
@@ -428,19 +482,21 @@ function buildPrecisionGuard({ claims, planSlots }) {
428
482
  };
429
483
  }
430
484
 
431
- export default async function normalizeInputPacket({ sources }) {
485
+ export default async function normalizeInputPacket({ sources, context } = {}) {
432
486
  const plan = asObject(findSource(sources, "plan"));
433
487
  const research = researchSources(sources);
434
488
  const extractedFacts = [];
435
489
  const claims = [];
436
490
  const sourceCards = [];
437
491
  const evidenceGaps = [];
492
+ const questionBudgetLedger = [];
438
493
  const overflow = {};
439
494
  const limits = {
440
495
  extractedFacts: 240,
441
496
  claims: 240,
442
497
  sources: 160,
443
498
  evidenceGaps: 120,
499
+ questionBudgetLedger: 80,
444
500
  };
445
501
 
446
502
  for (const { sourceId, source } of research) {
@@ -480,7 +536,29 @@ export default async function normalizeInputPacket({ sources }) {
480
536
  limits.evidenceGaps,
481
537
  "omittedEvidenceGaps",
482
538
  );
539
+ const budgetLedger = compactBudgetLedger(source, sourceId);
540
+ if (budgetLedger) {
541
+ pushBounded(
542
+ questionBudgetLedger,
543
+ overflow,
544
+ [budgetLedger],
545
+ limits.questionBudgetLedger,
546
+ "omittedQuestionBudgetLedgers",
547
+ );
548
+ }
483
549
  }
550
+ pushBounded(
551
+ evidenceGaps,
552
+ overflow,
553
+ sourceStatusesOf(context)
554
+ .filter(
555
+ (status) =>
556
+ isResearchQuestionStatus(status) && status.status !== "completed",
557
+ )
558
+ .map(compactSourceStatusGap),
559
+ limits.evidenceGaps,
560
+ "omittedEvidenceGaps",
561
+ );
484
562
 
485
563
  const planSlots = asArray(plan.factSlots).map(compactPlanSlot);
486
564
  const precisionGuard = buildPrecisionGuard({ claims, planSlots });
@@ -506,6 +584,7 @@ export default async function normalizeInputPacket({ sources }) {
506
584
  claims,
507
585
  sources: sourceCards,
508
586
  evidenceGaps,
587
+ questionBudgetLedger,
509
588
  },
510
589
  slotPreservation,
511
590
  precisionGuard,
@@ -8,6 +8,7 @@
8
8
 
9
9
  import { mkdir, writeFile } from "node:fs/promises";
10
10
  import { join } from "node:path";
11
+ import { canonicalVerificationStatus } from "./verification-ontology.mjs";
11
12
 
12
13
  function findSource(sources, stageId) {
13
14
  const entries = Object.entries(sources ?? {});
@@ -298,11 +299,24 @@ function finiteNumber(value) {
298
299
  function normalizeClaimStatus(status) {
299
300
  const text = cleanText(status).toLowerCase();
300
301
  if (!text) return "";
302
+ const canonical = canonicalVerificationStatus(text);
303
+ if (canonical !== "unverified") return canonical;
304
+ if (
305
+ text === "unverified" ||
306
+ text.includes("not verified") ||
307
+ text.includes("not_verified")
308
+ )
309
+ return "unverified";
310
+ if (
311
+ text.includes("verification_blocked") ||
312
+ text.includes("verification blocked")
313
+ )
314
+ return "verification_blocked";
301
315
  if (text.includes("conflict")) return "conflicting";
302
316
  if (text.includes("unsupported")) return "unsupported";
303
317
  if (text.includes("partial")) return "partially_supported";
304
- if (text.includes("verified")) return "verified";
305
- return text;
318
+ if (/\bverified\b/.test(text)) return "verified";
319
+ return canonical;
306
320
  }
307
321
 
308
322
  function coverageCounts(coverage, fallback) {
@@ -316,13 +330,18 @@ function coverageCounts(coverage, fallback) {
316
330
  fallback.partially_supported,
317
331
  unsupported: finiteNumber(coverage.unsupported) ?? fallback.unsupported,
318
332
  conflicting: finiteNumber(coverage.conflicting) ?? fallback.conflicting,
333
+ verification_blocked:
334
+ finiteNumber(coverage.verificationBlocked) ??
335
+ finiteNumber(coverage.verification_blocked) ??
336
+ fallback.verification_blocked,
319
337
  };
320
338
  if (counts.total == null) {
321
339
  counts.total =
322
340
  counts.verified +
323
341
  counts.partially_supported +
324
342
  counts.unsupported +
325
- counts.conflicting;
343
+ counts.conflicting +
344
+ counts.verification_blocked;
326
345
  }
327
346
  return counts;
328
347
  }
@@ -338,7 +357,8 @@ function packetVerdictCounts(packet, fallback) {
338
357
  counts.verified +
339
358
  counts.partially_supported +
340
359
  counts.unsupported +
341
- counts.conflicting;
360
+ counts.conflicting +
361
+ counts.verification_blocked;
342
362
  return counts;
343
363
  }
344
364
 
@@ -350,6 +370,7 @@ function claimCounts(control, packet) {
350
370
  partially_supported: 0,
351
371
  unsupported: 0,
352
372
  conflicting: 0,
373
+ verification_blocked: 0,
353
374
  };
354
375
  for (const claim of claims) {
355
376
  const status = normalizeClaimStatus(claim?.status);
@@ -372,6 +393,7 @@ function claimCounts(control, packet) {
372
393
  "partially_supported",
373
394
  "unsupported",
374
395
  "conflicting",
396
+ "verification_blocked",
375
397
  ]) {
376
398
  if (coverage[key] !== counts[key]) {
377
399
  mismatches.push({
@@ -520,6 +542,8 @@ function evidenceStrength(status) {
520
542
  case "unsupported":
521
543
  return 1;
522
544
  case "conflicting":
545
+ case "verification_blocked":
546
+ case "unverified":
523
547
  return 0;
524
548
  default:
525
549
  return -1;
@@ -587,6 +611,7 @@ function coverageSummaryFromPacket(packet, fallback = {}) {
587
611
  partially_supported: 0,
588
612
  unsupported: 0,
589
613
  conflicting: 0,
614
+ verification_blocked: 0,
590
615
  });
591
616
  if (!counts) return fallback;
592
617
  return {
@@ -595,6 +620,7 @@ function coverageSummaryFromPacket(packet, fallback = {}) {
595
620
  partiallySupported: counts.partially_supported,
596
621
  unsupported: counts.unsupported,
597
622
  conflicting: counts.conflicting,
623
+ verificationBlocked: counts.verification_blocked,
598
624
  verificationCandidates: counts.total,
599
625
  depth: packet?.researchMetadataSeed?.depth ?? fallback.depth,
600
626
  researchQuestions:
@@ -975,7 +1001,7 @@ function renderAuditSummary(report, claimSummary, slots) {
975
1001
  return [
976
1002
  "## Audit summary",
977
1003
  "",
978
- `- Claims: ${claimSummary.verified} verified, ${claimSummary.partially_supported} partially supported, ${claimSummary.unsupported} unsupported, ${claimSummary.conflicting} conflicting.`,
1004
+ `- Claims: ${claimSummary.verified} verified, ${claimSummary.partially_supported} partially supported, ${claimSummary.unsupported} unsupported, ${claimSummary.conflicting} conflicting, ${claimSummary.verification_blocked} verification blocked.`,
979
1005
  `- Fact slots: ${slots.filled} filled, ${slots.partial} partial, ${slots.missingOrConflicting} missing/conflicting, ${slots.total} total.`,
980
1006
  ...(mismatches.length > 0
981
1007
  ? [
@@ -1240,6 +1266,7 @@ export default async function renderExecutive({
1240
1266
  partially_supported: 0,
1241
1267
  unsupported: 0,
1242
1268
  conflicting: 0,
1269
+ verification_blocked: 0,
1243
1270
  },
1244
1271
  factSlotSummary: {
1245
1272
  total: 0,