@decantr/verifier 3.4.1 → 3.5.3

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/dist/index.js CHANGED
@@ -344,6 +344,300 @@ function auditComponentReuse(projectRoot, sourceFiles) {
344
344
  };
345
345
  }
346
346
 
347
+ // src/contracts-v2.ts
348
+ var VERIFICATION_COMMON_V2_SCHEMA_URL = "https://decantr.ai/schemas/verification-report.common.v2.json";
349
+ var PROJECT_HEALTH_REPORT_V2_SCHEMA_URL = "https://decantr.ai/schemas/project-health-report.v2.json";
350
+ var DECANTR_CI_REPORT_V2_SCHEMA_URL = "https://decantr.ai/schemas/decantr-ci-report.v2.json";
351
+ var WORKSPACE_HEALTH_REPORT_V2_SCHEMA_URL = "https://decantr.ai/schemas/workspace-health-report.v2.json";
352
+ var EVIDENCE_BUNDLE_V2_SCHEMA_URL = "https://decantr.ai/schemas/evidence-bundle.v2.json";
353
+ var RUNTIME_PROBE_PAYLOAD_V2_SCHEMA_URL = "https://decantr.ai/schemas/runtime-probe-payload.v2.json";
354
+ var LOOP_READINESS_V2_SCHEMA_URL = "https://decantr.ai/schemas/loop-readiness.v2.json";
355
+ var AUTHORITY_RESOLUTION_V2_SCHEMA_URL = "https://decantr.ai/schemas/authority-resolution.v2.json";
356
+ var PROOF_FIELD_REPORT_V2_SCHEMA_URL = "https://decantr.ai/schemas/proof-field-report.v2.json";
357
+ function unique(values) {
358
+ return [...new Set(values)];
359
+ }
360
+ function rounded(value) {
361
+ return Number(Math.max(0, Math.min(1, value)).toFixed(2));
362
+ }
363
+ function createEvidenceTier(report, options = {}) {
364
+ const runtimeProbeCount = options.runtimeProbeCount ?? (report.summary.runtimeAuditChecked ? 1 : 0);
365
+ const visualArtifactCount = options.visualArtifactCount ?? 0;
366
+ const findingsAnchored = report.findings.filter((finding) => finding.graph).length;
367
+ const findingsWithRepairPlan = report.findings.filter((finding) => finding.repairPlan).length;
368
+ const capabilities = ["static-audit", "project-health"];
369
+ if (report.graph.ready) capabilities.push("typed-graph");
370
+ if (runtimeProbeCount > 0) capabilities.push("runtime-probe");
371
+ if (report.summary.runtimeAuditChecked || visualArtifactCount > 0)
372
+ capabilities.push("browser-evidence");
373
+ if (visualArtifactCount > 0) capabilities.push("visual-baseline");
374
+ if (findingsWithRepairPlan > 0) capabilities.push("repair-plan");
375
+ if (options.benchmarkReplay) capabilities.push("benchmark-replay");
376
+ let stage = "static";
377
+ if (options.benchmarkReplay) stage = "proof";
378
+ else if (findingsWithRepairPlan > 0) stage = "repair";
379
+ else if (visualArtifactCount > 0) stage = "visual";
380
+ else if (runtimeProbeCount > 0 || report.summary.runtimeAuditChecked) stage = "runtime";
381
+ else if (report.graph.ready) stage = "graph";
382
+ const routeCoverage = report.routes.declared.length > 0 ? Math.min(1, report.routes.runtimeChecked.length / report.routes.declared.length) : report.summary.runtimeAuditChecked ? 1 : 0;
383
+ const graphCoverage = report.findings.length > 0 ? findingsAnchored / report.findings.length : report.graph.ready ? 1 : 0;
384
+ const repairCoverage = report.findings.length > 0 ? findingsWithRepairPlan / report.findings.length : 1;
385
+ const runtimeCoverage = runtimeProbeCount > 0 || report.summary.runtimeAuditChecked ? 1 : 0;
386
+ const score = rounded(
387
+ 0.22 + (report.graph.ready ? 0.22 : 0) + routeCoverage * 0.16 + graphCoverage * 0.16 + repairCoverage * 0.14 + runtimeCoverage * 0.1
388
+ );
389
+ const level = score >= 0.78 ? "high" : score >= 0.48 ? "moderate" : "low";
390
+ const reasons = [];
391
+ reasons.push(report.graph.ready ? "typed graph is ready" : "typed graph is missing or stale");
392
+ reasons.push(
393
+ runtimeProbeCount > 0 || report.summary.runtimeAuditChecked ? "runtime evidence is present" : "runtime evidence has not run"
394
+ );
395
+ reasons.push(
396
+ report.findings.length === 0 ? "no open findings" : `${findingsAnchored}/${report.findings.length} finding(s) have graph anchors`
397
+ );
398
+ reasons.push(
399
+ report.findings.length === 0 ? "repair context is not needed" : `${findingsWithRepairPlan}/${report.findings.length} finding(s) include repair plans`
400
+ );
401
+ return {
402
+ schemaVersion: 2,
403
+ stage,
404
+ status: report.status === "healthy" ? "healthy" : report.status,
405
+ capabilities: unique(capabilities),
406
+ coverage: {
407
+ declaredRoutes: report.routes.declared.length,
408
+ runtimeRoutesChecked: report.routes.runtimeChecked.length,
409
+ findingsAnchored,
410
+ findingsWithRepairPlan,
411
+ runtimeProbeCount,
412
+ visualArtifactCount
413
+ },
414
+ confidence: {
415
+ level,
416
+ score,
417
+ reasons
418
+ }
419
+ };
420
+ }
421
+ function createAuthorityOrder() {
422
+ return [
423
+ {
424
+ id: "production-source",
425
+ label: "Production source",
426
+ role: "Existing runtime/source wins first in Brownfield unless a human explicitly accepts a contract change.",
427
+ rank: 1
428
+ },
429
+ {
430
+ id: "local-law",
431
+ label: "Accepted local law",
432
+ role: "Project-owned rules and codified patterns constrain edits after they are accepted locally.",
433
+ rank: 2
434
+ },
435
+ {
436
+ id: "style-bridge",
437
+ label: "Accepted style bridge",
438
+ role: "Mapped native style tokens/components govern Hybrid styling once accepted locally.",
439
+ rank: 3
440
+ },
441
+ {
442
+ id: "essence-contract",
443
+ label: "Essence V4 contract",
444
+ role: "Structural route, section, page, guard, and DNA contract for Decantr context.",
445
+ rank: 4
446
+ },
447
+ {
448
+ id: "registry-guidance",
449
+ label: "Hosted packs and registry",
450
+ role: "Advisory guidance unless mapped into accepted local law or the local style bridge.",
451
+ rank: 5
452
+ }
453
+ ];
454
+ }
455
+ function laneForFinding(finding) {
456
+ if (finding.source === "style-bridge") return "style-bridge";
457
+ if (finding.source === "brownfield" || finding.category.toLowerCase().includes("drift")) {
458
+ return "production-source";
459
+ }
460
+ if (finding.source === "check" || finding.source === "assertion") return "essence-contract";
461
+ if (finding.source === "pack") return "registry-guidance";
462
+ return "local-law";
463
+ }
464
+ function actionsForFinding(finding) {
465
+ const commands = finding.repairPlan?.commands ?? finding.remediation?.commands ?? [];
466
+ const repairCommand = commands[0] ?? null;
467
+ const source = finding.source;
468
+ const actions = [];
469
+ if (source === "graph") {
470
+ actions.push({
471
+ kind: "regenerate_graph",
472
+ label: "Regenerate graph/context evidence",
473
+ command: "decantr graph --check || decantr graph",
474
+ writes: true,
475
+ rationale: "Graph drift should be resolved by regenerating derived graph artifacts from current source."
476
+ });
477
+ } else if (source === "style-bridge") {
478
+ actions.push({
479
+ kind: "update_style_bridge",
480
+ label: "Update accepted style bridge",
481
+ command: "decantr codify --style-bridge",
482
+ writes: true,
483
+ rationale: "Style conflicts should be accepted through the explicit Hybrid style bridge workflow."
484
+ });
485
+ } else if (source === "brownfield") {
486
+ actions.push({
487
+ kind: "accept_observed_source",
488
+ label: "Accept observed source into contract",
489
+ command: "decantr init --existing --merge-proposal",
490
+ writes: true,
491
+ rationale: "Brownfield source is first authority; accept it explicitly before treating contract drift as repaired."
492
+ });
493
+ } else if (source === "pack") {
494
+ actions.push({
495
+ kind: "regenerate_context",
496
+ label: "Regenerate Decantr context",
497
+ command: "decantr refresh",
498
+ writes: true,
499
+ rationale: "Generated context and pack artifacts are derived from the local contract."
500
+ });
501
+ } else {
502
+ actions.push({
503
+ kind: "repair_source",
504
+ label: "Repair source to satisfy accepted authority",
505
+ command: repairCommand,
506
+ writes: false,
507
+ rationale: "The finding can be repaired by editing the relevant application source under human/agent control."
508
+ });
509
+ }
510
+ actions.push({
511
+ kind: "defer_to_drift_log",
512
+ label: "Defer to drift log",
513
+ command: `decantr resolve --defer ${finding.id}`,
514
+ writes: true,
515
+ rationale: "Record an explicit human deferral when this conflict should not block the current task."
516
+ });
517
+ if (finding.severity !== "error") {
518
+ actions.push({
519
+ kind: "mark_advisory",
520
+ label: "Mark advisory",
521
+ command: `decantr resolve --mark-advisory ${finding.id}`,
522
+ writes: true,
523
+ rationale: "Warnings can be documented as advisory when the observed source is intentionally different."
524
+ });
525
+ }
526
+ return actions;
527
+ }
528
+ function createAuthorityResolution(report) {
529
+ const conflicts = report.findings.filter((finding) => finding.severity !== "info").map((finding) => ({
530
+ id: finding.id,
531
+ source: finding.source,
532
+ category: finding.category,
533
+ severity: finding.severity,
534
+ message: finding.message,
535
+ ...finding.graph ? { graphAnchor: finding.graph } : {},
536
+ lane: laneForFinding(finding),
537
+ status: finding.severity === "error" ? "blocking" : finding.source === "brownfield" || finding.source === "style-bridge" ? "repairable" : "advisory",
538
+ recommendedActions: actionsForFinding(finding)
539
+ }));
540
+ const activeLane = report.summary.workflowMode === "brownfield-attach" ? "production-source" : "essence-contract";
541
+ return {
542
+ schemaVersion: 2,
543
+ order: createAuthorityOrder(),
544
+ activeLane,
545
+ summary: activeLane === "production-source" ? "Brownfield authority: preserve observed production source first, then accepted local law/style bridge, then Essence V4 structure, with hosted packs advisory." : "Contract authority: Essence V4 and accepted local project law guide source changes; hosted packs remain advisory until accepted locally.",
546
+ conflicts,
547
+ stopRule: "If runtime source and Decantr context disagree, stop and report drift instead of guessing which truth to overwrite."
548
+ };
549
+ }
550
+ function readTargetsForFindings(report) {
551
+ const targets = /* @__PURE__ */ new Set(["DECANTR.md", "decantr.essence.json"]);
552
+ for (const finding of report.findings) {
553
+ for (const target of finding.repairPlan?.readTargets ?? []) targets.add(target);
554
+ if (finding.file) targets.add(finding.file);
555
+ if (finding.source === "graph") targets.add(".decantr/graph/graph.manifest.json");
556
+ if (finding.source === "style-bridge") targets.add(".decantr/style-bridge.json");
557
+ }
558
+ return [...targets].sort();
559
+ }
560
+ function createLoopReadiness(report, authority = createAuthorityResolution(report), evidenceTier = createEvidenceTier(report)) {
561
+ const hasEssence = Boolean(report.summary.essenceVersion);
562
+ const graphMissing = !report.graph.ready;
563
+ const blockingConflicts = authority.conflicts.filter(
564
+ (conflict) => conflict.status === "blocking"
565
+ );
566
+ const repairableConflicts = authority.conflicts.filter(
567
+ (conflict) => conflict.status === "repairable"
568
+ );
569
+ let state = "verified";
570
+ const blockingReasons = [];
571
+ if (!hasEssence) {
572
+ state = "blocked_missing_context";
573
+ blockingReasons.push("Essence V4 context is missing.");
574
+ } else if (graphMissing) {
575
+ state = "blocked_missing_graph";
576
+ blockingReasons.push("Typed Contract graph is missing, stale, or incomplete.");
577
+ } else if (blockingConflicts.length > 0 && repairableConflicts.length > 0) {
578
+ state = "human_resolution_required";
579
+ blockingReasons.push("Blocking findings conflict with Brownfield or Hybrid authority.");
580
+ } else if (blockingConflicts.length > 0 || report.summary.errorCount > 0) {
581
+ state = "repair_required";
582
+ blockingReasons.push("Project Health has blocking errors.");
583
+ } else if (repairableConflicts.length > 0 || report.summary.warnCount > 0) {
584
+ state = "repair_required";
585
+ blockingReasons.push("Project Health has repairable warnings or drift.");
586
+ } else if (report.status === "healthy") {
587
+ state = "verified";
588
+ } else {
589
+ state = "verify_required";
590
+ blockingReasons.push("Verification needs to be rerun before the loop is complete.");
591
+ }
592
+ const nextActions = state === "verified" ? ["Continue normal route/task workflow."] : state === "blocked_missing_context" ? ["Run `decantr scan` for a read-only preview, then `decantr adopt` when ready."] : state === "blocked_missing_graph" ? ["Run `decantr graph` to regenerate typed graph artifacts, then rerun verification."] : state === "human_resolution_required" ? ["Run `decantr resolve` and choose an explicit resolution action before editing."] : state === "repair_required" ? ["Repair the highest-severity finding, then rerun the verify command."] : ["Run the verify command before treating the task as complete."];
593
+ const status = state.startsWith("blocked") || state === "human_resolution_required" ? "blocked" : report.status;
594
+ return {
595
+ schemaVersion: 2,
596
+ state,
597
+ status,
598
+ verdict: state === "verified" ? "Loop verified." : "Loop needs attention before it should be considered complete.",
599
+ summary: `${report.status} Project Health, ${evidenceTier.confidence.level} evidence confidence, ${authority.conflicts.length} authority conflict(s).`,
600
+ authority: {
601
+ activeLane: authority.activeLane,
602
+ summary: authority.summary,
603
+ stopRule: authority.stopRule
604
+ },
605
+ evidenceTier,
606
+ blockingReasons,
607
+ nextActions,
608
+ maker: {
609
+ title: "Maker instructions",
610
+ instructions: [
611
+ "Read the route/task context before editing source.",
612
+ "Preserve production source behavior and accepted local law unless a human chooses a resolution action.",
613
+ "Stop and report drift when runtime source and Decantr context disagree."
614
+ ]
615
+ },
616
+ checker: {
617
+ title: "Checker instructions",
618
+ instructions: [
619
+ "Rerun the verify command after edits.",
620
+ "Check graph anchors and repair plans before accepting a fix.",
621
+ "Treat critique-only or advisory evidence as warning-level unless runtime evidence proves a failure."
622
+ ]
623
+ },
624
+ readTargets: readTargetsForFindings(report),
625
+ graphImpact: {
626
+ status: !report.graph.present ? "missing" : report.graph.ready && report.graph.current !== false ? "ready" : "stale",
627
+ snapshotId: report.graph.snapshotId,
628
+ sourceHash: report.graph.sourceHash,
629
+ sourceArtifactCount: report.graph.sourceArtifactCount,
630
+ staleArtifacts: report.graph.staleArtifacts
631
+ },
632
+ stopConditions: [
633
+ "Runtime source and Decantr context disagree.",
634
+ "Typed graph is missing for the route or changed file.",
635
+ "A finding requires contract/source/local-law mutation outside the current explicit workflow."
636
+ ],
637
+ verifyCommand: report.ci.recommendedCommand
638
+ };
639
+ }
640
+
347
641
  // src/runtime.ts
348
642
  import { existsSync, readdirSync, readFileSync as readFileSync2, statSync } from "fs";
349
643
  import { createServer } from "http";
@@ -2010,9 +2304,9 @@ function sourceLineCount(source) {
2010
2304
  }
2011
2305
  function verifyInteractionsInSource(interactions, sources) {
2012
2306
  if (interactions.length === 0 || sources.size === 0) return [];
2013
- const unique = Array.from(new Set(interactions));
2307
+ const unique2 = Array.from(new Set(interactions));
2014
2308
  const missing = [];
2015
- for (const interaction of unique) {
2309
+ for (const interaction of unique2) {
2016
2310
  const requirement = INTERACTION_SIGNALS[interaction];
2017
2311
  if (!requirement) {
2018
2312
  continue;
@@ -2361,6 +2655,9 @@ function scanReactRouter(projectRoot) {
2361
2655
  for (const route of detectPathnameBranchRoutes(content)) {
2362
2656
  routes.set(route, { path: route, file, hasLayout: false });
2363
2657
  }
2658
+ for (const route of detectDeclarativeRouteSpecRoutes(content)) {
2659
+ routes.set(route, { path: route, file, hasLayout: false });
2660
+ }
2364
2661
  }
2365
2662
  return { routes: [...routes.values()], hashRouting };
2366
2663
  }
@@ -2401,6 +2698,17 @@ function detectPathnameBranchRoutes(content) {
2401
2698
  }
2402
2699
  return [...routes];
2403
2700
  }
2701
+ function detectDeclarativeRouteSpecRoutes(content) {
2702
+ const routes = /* @__PURE__ */ new Set();
2703
+ const hasRouteSpecSignal = content.includes("@wasp.sh/spec") || /\b(?:const|export\s+const)\s+\w*Spec\b/.test(content) || /\bapp\s*\(\s*\{[\s\S]*\bspec\s*:/.test(content);
2704
+ if (!hasRouteSpecSignal) return [];
2705
+ collectRouteLiterals(
2706
+ /\broute\s*\(\s*["'`][^"'`]*["'`]\s*,\s*["'`](\/[^"'`]*)["'`]\s*,\s*page\s*\(/g,
2707
+ content,
2708
+ routes
2709
+ );
2710
+ return [...routes];
2711
+ }
2404
2712
  function scanRoutes(projectRoot, detection) {
2405
2713
  const appRoutes = ["src/app", "app"].flatMap(
2406
2714
  (dir) => existsSync3(join3(projectRoot, dir)) ? walkNextAppRoutes(join3(projectRoot, dir), projectRoot, []) : []
@@ -3734,17 +4042,17 @@ function resolveSourceSymbolOrigin(context, pathOrSourceFile, localName, options
3734
4042
 
3735
4043
  // src/index.ts
3736
4044
  var VERIFICATION_SCHEMA_URLS = {
3737
- common: "https://decantr.ai/schemas/verification-report.common.v1.json",
4045
+ common: VERIFICATION_COMMON_V2_SCHEMA_URL,
3738
4046
  projectAudit: "https://decantr.ai/schemas/project-audit-report.v1.json",
3739
- projectHealth: "https://decantr.ai/schemas/project-health-report.v1.json",
3740
- decantrCi: "https://decantr.ai/schemas/decantr-ci-report.v1.json",
3741
- evidenceBundle: "https://decantr.ai/schemas/evidence-bundle.v1.json",
4047
+ projectHealth: PROJECT_HEALTH_REPORT_V2_SCHEMA_URL,
4048
+ decantrCi: DECANTR_CI_REPORT_V2_SCHEMA_URL,
4049
+ evidenceBundle: EVIDENCE_BUNDLE_V2_SCHEMA_URL,
3742
4050
  scanReport: "https://decantr.ai/schemas/scan-report.v1.json",
3743
- workspaceHealth: "https://decantr.ai/schemas/workspace-health-report.v1.json",
4051
+ workspaceHealth: WORKSPACE_HEALTH_REPORT_V2_SCHEMA_URL,
3744
4052
  fileCritique: "https://decantr.ai/schemas/file-critique-report.v1.json",
3745
4053
  showcaseShortlist: "https://decantr.ai/schemas/showcase-shortlist-report.v1.json"
3746
4054
  };
3747
- var EVIDENCE_BUNDLE_SCHEMA_URL = "https://decantr.ai/schemas/evidence-bundle.v1.json";
4055
+ var EVIDENCE_BUNDLE_SCHEMA_URL = EVIDENCE_BUNDLE_V2_SCHEMA_URL;
3748
4056
  function hashString(value) {
3749
4057
  return createHash("sha256").update(value).digest("hex").slice(0, 16);
3750
4058
  }
@@ -4033,6 +4341,8 @@ function createContractAssertions(projectRoot, audit) {
4033
4341
  }
4034
4342
  function createEvidenceBundle(input) {
4035
4343
  const assertions = input.assertions ?? createContractAssertions(input.projectRoot, input.audit);
4344
+ const evidenceTier = input.report.evidenceTier ?? createEvidenceTier(input.report);
4345
+ const authority = input.report.authority ?? createAuthorityResolution(input.report);
4036
4346
  let resolvedProjectRoot = input.projectRoot;
4037
4347
  try {
4038
4348
  resolvedProjectRoot = realpathSync2(input.projectRoot);
@@ -4045,13 +4355,18 @@ function createEvidenceBundle(input) {
4045
4355
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
4046
4356
  project: {
4047
4357
  id: projectId,
4048
- rootLabel: basename2(input.projectRoot) || "project"
4358
+ rootLabel: basename2(input.projectRoot) || "project",
4359
+ workflowMode: input.report.summary.workflowMode,
4360
+ adoptionMode: input.report.summary.adoptionMode
4049
4361
  },
4050
4362
  toolchain: {
4051
- verifierVersion: input.verifierVersion ?? null
4363
+ verifierVersion: input.verifierVersion ?? null,
4364
+ cliVersion: input.cliVersion ?? null,
4365
+ mcpServerVersion: input.mcpServerVersion ?? null
4052
4366
  },
4053
4367
  privacy: {
4054
4368
  localOnly: true,
4369
+ sourceIncluded: false,
4055
4370
  redactedFields: [
4056
4371
  "source",
4057
4372
  "prompt",
@@ -4060,8 +4375,10 @@ function createEvidenceBundle(input) {
4060
4375
  "absolute_path",
4061
4376
  "repository_name"
4062
4377
  ],
4063
- screenshotsLocalOnly: true
4378
+ screenshotsLocalOnly: true,
4379
+ hostedUploadAllowed: false
4064
4380
  },
4381
+ evidenceTier,
4065
4382
  health: {
4066
4383
  status: input.report.status,
4067
4384
  score: input.report.score,
@@ -4079,7 +4396,9 @@ function createEvidenceBundle(input) {
4079
4396
  graphDiff: provenanceEntry(input.projectRoot, ".decantr/graph/graph.diff.json"),
4080
4397
  contractCapsule: provenanceEntry(input.projectRoot, ".decantr/graph/contract-capsule.json"),
4081
4398
  ...input.workspaceConfigPath ? { workspaceConfig: provenanceForPath(input.projectRoot, input.workspaceConfigPath) } : {},
4082
- ...input.designTokensPath ? { designTokens: provenanceForPath(input.projectRoot, input.designTokensPath) } : {}
4399
+ ...input.designTokensPath ? { designTokens: provenanceForPath(input.projectRoot, input.designTokensPath) } : {},
4400
+ ...input.runtimeProbePath ? { runtimeProbe: provenanceForPath(input.projectRoot, input.runtimeProbePath) } : {},
4401
+ ...input.visualManifestPath ? { visualManifest: provenanceForPath(input.projectRoot, input.visualManifestPath) } : {}
4083
4402
  },
4084
4403
  assertions,
4085
4404
  findings: input.report.findings.map((finding) => ({
@@ -4096,10 +4415,37 @@ function createEvidenceBundle(input) {
4096
4415
  graph: finding.graph,
4097
4416
  repair: redactRepairAction(input.projectRoot, finding.repair),
4098
4417
  repairPlan: buildProjectHealthRepairPlan(input.projectRoot, finding),
4418
+ evidenceTier: finding.evidenceTier ?? evidenceTier,
4419
+ authorityLane: finding.authorityLane ?? authority.conflicts.find((conflict) => conflict.id === finding.id)?.lane ?? authority.activeLane,
4420
+ resolutionActions: finding.resolutionActions ?? authority.conflicts.find((conflict) => conflict.id === finding.id)?.recommendedActions,
4421
+ privacy: finding.privacy ?? {
4422
+ sourceIncluded: false,
4423
+ redacted: true,
4424
+ localOnly: true
4425
+ },
4426
+ loopVerdict: finding.loopVerdict ?? input.report.loop?.state,
4099
4427
  remediationSummary: finding.remediation.summary,
4100
4428
  commands: finding.remediation.commands,
4101
4429
  promptCommand: `decantr health --prompt ${finding.id}`
4102
4430
  })),
4431
+ runtimeProbe: input.runtimeProbe ?? null,
4432
+ artifacts: input.artifacts ?? [
4433
+ {
4434
+ id: "artifact:project-health",
4435
+ kind: "project-health",
4436
+ path: "decantr-health.json",
4437
+ hash: null,
4438
+ localOnly: true,
4439
+ redacted: true
4440
+ }
4441
+ ],
4442
+ dashboard: {
4443
+ safeToUpload: false,
4444
+ retentionClass: "local-only",
4445
+ redactionNotes: [
4446
+ "Evidence Bundle v2 does not include raw source, prompts, secrets, absolute paths, or uploaded screenshots by default."
4447
+ ]
4448
+ },
4103
4449
  ...input.browser ? { browser: input.browser } : {},
4104
4450
  ...input.designTokens ? { designTokens: input.designTokens } : {}
4105
4451
  };
@@ -4854,6 +5200,10 @@ function buildRegistryContext(projectRoot) {
4854
5200
  const patternRegistry = /* @__PURE__ */ new Map();
4855
5201
  const cacheDir = join6(projectRoot, ".decantr", "cache");
4856
5202
  const customDir = join6(projectRoot, ".decantr", "custom");
5203
+ const addPattern = (id, source, data = null) => {
5204
+ if (typeof id !== "string" || !id.trim() || patternRegistry.has(id)) return;
5205
+ patternRegistry.set(id, data ?? { id, source });
5206
+ };
4857
5207
  const cachedThemesDir = join6(cacheDir, "@official", "themes");
4858
5208
  try {
4859
5209
  if (existsSync6(cachedThemesDir)) {
@@ -4887,17 +5237,27 @@ function buildRegistryContext(projectRoot) {
4887
5237
  (name) => name.endsWith(".json") && name !== "index.json"
4888
5238
  )) {
4889
5239
  const data = JSON.parse(readFileSync6(join6(cachedPatternsDir, file), "utf-8"));
4890
- if (data.id && !patternRegistry.has(data.id)) {
4891
- patternRegistry.set(data.id, data);
4892
- }
5240
+ addPattern(data.id, "cache", data);
4893
5241
  }
4894
5242
  }
4895
5243
  } catch {
4896
5244
  }
4897
- for (const id of ["content-section", "footer", "form-basic", "hero", "nav-header"]) {
4898
- if (!patternRegistry.has(id)) {
4899
- patternRegistry.set(id, { id, source: "bundled" });
5245
+ const customPatternsDir = join6(customDir, "patterns");
5246
+ try {
5247
+ if (existsSync6(customPatternsDir)) {
5248
+ for (const file of readdirSync5(customPatternsDir).filter((name) => name.endsWith(".json"))) {
5249
+ const data = JSON.parse(readFileSync6(join6(customPatternsDir, file), "utf-8"));
5250
+ addPattern(data.id, "custom", data);
5251
+ }
4900
5252
  }
5253
+ } catch {
5254
+ }
5255
+ const localPatterns = readJsonIfExists(join6(projectRoot, ".decantr", "local-patterns.json"));
5256
+ for (const pattern of localPatterns?.patterns ?? []) {
5257
+ addPattern(pattern.id, "local-law", pattern);
5258
+ }
5259
+ for (const id of ["content-section", "footer", "form-basic", "hero", "nav-header"]) {
5260
+ addPattern(id, "bundled");
4901
5261
  }
4902
5262
  return { themeRegistry, patternRegistry };
4903
5263
  }
@@ -15678,14 +16038,23 @@ async function critiqueFile(filePath, projectRoot) {
15678
16038
  });
15679
16039
  }
15680
16040
  export {
16041
+ AUTHORITY_RESOLUTION_V2_SCHEMA_URL,
15681
16042
  COMPONENT_REUSE_RULE_ID,
16043
+ DECANTR_CI_REPORT_V2_SCHEMA_URL,
15682
16044
  EVIDENCE_BUNDLE_SCHEMA_URL,
16045
+ EVIDENCE_BUNDLE_V2_SCHEMA_URL,
15683
16046
  INTERACTION_SIGNALS,
15684
16047
  KNOWN_VERIFICATION_DIAGNOSTICS,
16048
+ LOOP_READINESS_V2_SCHEMA_URL,
16049
+ PROJECT_HEALTH_REPORT_V2_SCHEMA_URL,
16050
+ PROOF_FIELD_REPORT_V2_SCHEMA_URL,
15685
16051
  RAW_CONTROL_REUSE_RULE_ID,
16052
+ RUNTIME_PROBE_PAYLOAD_V2_SCHEMA_URL,
15686
16053
  SCAN_REPORT_SCHEMA_URL,
15687
16054
  STYLE_BRIDGE_ARBITRARY_VALUE_RULE_ID,
16055
+ VERIFICATION_COMMON_V2_SCHEMA_URL,
15688
16056
  VERIFICATION_SCHEMA_URLS,
16057
+ WORKSPACE_HEALTH_REPORT_V2_SCHEMA_URL,
15689
16058
  anchorFindingsToGraph,
15690
16059
  auditBuiltDist,
15691
16060
  auditComponentReuse,
@@ -15695,8 +16064,12 @@ export {
15695
16064
  collectMissingPackManifestFiles,
15696
16065
  collectProjectSourceFiles,
15697
16066
  collectSourceImports,
16067
+ createAuthorityOrder,
16068
+ createAuthorityResolution,
15698
16069
  createContractAssertions,
15699
16070
  createEvidenceBundle,
16071
+ createEvidenceTier,
16072
+ createLoopReadiness,
15700
16073
  createProjectSourceProgram,
15701
16074
  createSourceInventory,
15702
16075
  createUnavailableScanReport,