@decantr/verifier 2.1.0 → 2.2.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.
package/README.md CHANGED
@@ -20,6 +20,7 @@ npm install @decantr/verifier
20
20
  - `createEvidenceBundle()` for privacy-redacted local evidence artifacts used by AI repair loops and CI
21
21
  - schema-backed report types for project audits, Project Health, Evidence Bundles, Workspace Health, file critiques, and showcase verification
22
22
  - `ProjectHealthReport`, `ProjectHealthFinding`, and `ProjectHealthRemediation` types for the CLI's end-user health surface
23
+ - interaction findings now include scanned file counts, file line ranges, and expected signal evidence where available, so CLI health/check output can point agents at source-grounded remediation
23
24
  - published verifier report schemas are exercised by AJV-backed round-trip tests against real audit, critique, and shortlist-report outputs
24
25
  - project audits include runtime evidence when a built `dist/` output is present:
25
26
  - root document
package/dist/index.d.ts CHANGED
@@ -97,6 +97,13 @@ declare const INTERACTION_SIGNALS: Record<string, InteractionRequirement>;
97
97
  interface InteractionMissingFinding {
98
98
  interaction: string;
99
99
  suggestion: string;
100
+ scannedFiles?: number;
101
+ scannedLocations?: Array<{
102
+ file: string;
103
+ startLine: number;
104
+ endLine: number;
105
+ }>;
106
+ expectedSignals?: string[];
100
107
  }
101
108
  /**
102
109
  * Scan a source-tree map (file path → file contents) for evidence of each
package/dist/index.js CHANGED
@@ -653,7 +653,7 @@ var INTERACTION_SIGNALS = {
653
653
  },
654
654
  "click-connect": {
655
655
  interaction: "click-connect",
656
- signals: [/onClick.*?port|connect/i, /connections?\s*[:\[=]/i],
656
+ signals: [/onClick.*?port|connect/i, /connections?\s*[:[=]/i],
657
657
  suggestion: "Implement a two-click state machine: first click selects a port, second click on another port creates a connection. Store connections in component state."
658
658
  },
659
659
  "inline-edit": {
@@ -697,9 +697,11 @@ var INTERACTION_SIGNALS = {
697
697
  suggestion: "Implement focus trap inside modal/dialog \u2014 listen for Tab key, cycle focus within the dialog, restore focus on close."
698
698
  }
699
699
  };
700
+ function sourceLineCount(source) {
701
+ return Math.max(1, source.replace(/\r?\n$/, "").split(/\r?\n/).length);
702
+ }
700
703
  function verifyInteractionsInSource(interactions, sources) {
701
704
  if (interactions.length === 0 || sources.size === 0) return [];
702
- const combined = Array.from(sources.values()).join("\n\n");
703
705
  const unique = Array.from(new Set(interactions));
704
706
  const missing = [];
705
707
  for (const interaction of unique) {
@@ -707,16 +709,30 @@ function verifyInteractionsInSource(interactions, sources) {
707
709
  if (!requirement) {
708
710
  continue;
709
711
  }
710
- const matched = requirement.signals.some((signal) => {
711
- if (typeof signal === "string") {
712
- return combined.includes(signal);
713
- }
714
- return signal.test(combined);
715
- });
712
+ let matched = false;
713
+ for (const source of sources.values()) {
714
+ matched = requirement.signals.some((signal) => {
715
+ if (typeof signal === "string") {
716
+ return source.includes(signal);
717
+ }
718
+ signal.lastIndex = 0;
719
+ return signal.test(source);
720
+ });
721
+ if (matched) break;
722
+ }
716
723
  if (!matched) {
717
724
  missing.push({
718
725
  interaction,
719
- suggestion: requirement.suggestion
726
+ suggestion: requirement.suggestion,
727
+ scannedFiles: sources.size,
728
+ scannedLocations: [...sources.entries()].slice(0, 8).map(([file, source]) => ({
729
+ file,
730
+ startLine: 1,
731
+ endLine: sourceLineCount(source)
732
+ })),
733
+ expectedSignals: requirement.signals.map(
734
+ (signal) => typeof signal === "string" ? signal : String(signal)
735
+ )
720
736
  });
721
737
  }
722
738
  }
@@ -1689,6 +1705,11 @@ function buildRegistryContext(projectRoot) {
1689
1705
  }
1690
1706
  } catch {
1691
1707
  }
1708
+ for (const id of ["content-section", "footer", "form-basic", "hero", "nav-header"]) {
1709
+ if (!patternRegistry.has(id)) {
1710
+ patternRegistry.set(id, { id, source: "bundled" });
1711
+ }
1712
+ }
1692
1713
  return { themeRegistry, patternRegistry };
1693
1714
  }
1694
1715
  function guardViolationToFinding(violation) {
@@ -2862,7 +2883,10 @@ function appendSourceAuditFindings(findings, sourceAudit, essence, reviewPack) {
2862
2883
  })
2863
2884
  );
2864
2885
  }
2865
- if (topology.hasAuthFeature && topology.primaryRoutes.length > 0 && sourceAudit.protectedSurfaceSignals.count > 0 && sourceAudit.authGuardSignals.count > 0 && !sourceAuditBucketsOverlap(sourceAudit.protectedSurfaceSignals, sourceAudit.authGuardSignals) && !sourceAuditBucketsOverlap(sourceAudit.protectedSurfaceSignals, sourceAudit.authSessionSignals) && !sourceAuditProtectedSurfacesCoveredByGuardedNextLayouts(
2886
+ if (topology.hasAuthFeature && topology.primaryRoutes.length > 0 && sourceAudit.protectedSurfaceSignals.count > 0 && sourceAudit.authGuardSignals.count > 0 && !sourceAuditBucketsOverlap(sourceAudit.protectedSurfaceSignals, sourceAudit.authGuardSignals) && !sourceAuditBucketsOverlap(
2887
+ sourceAudit.protectedSurfaceSignals,
2888
+ sourceAudit.authSessionSignals
2889
+ ) && !sourceAuditProtectedSurfacesCoveredByGuardedNextLayouts(
2866
2890
  sourceAudit.protectedSurfaceSignals,
2867
2891
  sourceAudit.authGuardSignals,
2868
2892
  topology