@neurcode-ai/cli 0.9.66 → 0.10.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 (180) hide show
  1. package/dist/commands/governance.d.ts +3 -0
  2. package/dist/commands/governance.d.ts.map +1 -0
  3. package/dist/commands/governance.js +390 -0
  4. package/dist/commands/governance.js.map +1 -0
  5. package/dist/commands/quickstart.js +3 -3
  6. package/dist/commands/quickstart.js.map +1 -1
  7. package/dist/commands/remediate-export.d.ts +5 -0
  8. package/dist/commands/remediate-export.d.ts.map +1 -1
  9. package/dist/commands/remediate-export.js +803 -14
  10. package/dist/commands/remediate-export.js.map +1 -1
  11. package/dist/commands/replay.d.ts.map +1 -1
  12. package/dist/commands/replay.js +14 -0
  13. package/dist/commands/replay.js.map +1 -1
  14. package/dist/commands/session.d.ts +7 -0
  15. package/dist/commands/session.d.ts.map +1 -1
  16. package/dist/commands/session.js +156 -0
  17. package/dist/commands/session.js.map +1 -1
  18. package/dist/commands/start-intent.d.ts.map +1 -1
  19. package/dist/commands/start-intent.js +61 -11
  20. package/dist/commands/start-intent.js.map +1 -1
  21. package/dist/commands/verify-guidance.d.ts +5 -0
  22. package/dist/commands/verify-guidance.d.ts.map +1 -0
  23. package/dist/commands/verify-guidance.js +49 -0
  24. package/dist/commands/verify-guidance.js.map +1 -0
  25. package/dist/commands/verify-output.d.ts +37 -0
  26. package/dist/commands/verify-output.d.ts.map +1 -0
  27. package/dist/commands/verify-output.js +572 -0
  28. package/dist/commands/verify-output.js.map +1 -0
  29. package/dist/commands/verify-render.d.ts +41 -0
  30. package/dist/commands/verify-render.d.ts.map +1 -0
  31. package/dist/commands/verify-render.js +457 -0
  32. package/dist/commands/verify-render.js.map +1 -0
  33. package/dist/commands/verify.d.ts.map +1 -1
  34. package/dist/commands/verify.js +278 -1081
  35. package/dist/commands/verify.js.map +1 -1
  36. package/dist/commands/workspace.d.ts.map +1 -1
  37. package/dist/commands/workspace.js +3 -14
  38. package/dist/commands/workspace.js.map +1 -1
  39. package/dist/context-engine/graph.d.ts.map +1 -1
  40. package/dist/context-engine/graph.js +69 -7
  41. package/dist/context-engine/graph.js.map +1 -1
  42. package/dist/context-engine/scanner.d.ts.map +1 -1
  43. package/dist/context-engine/scanner.js +9 -2
  44. package/dist/context-engine/scanner.js.map +1 -1
  45. package/dist/daemon/compatibility/execution.d.ts +42 -0
  46. package/dist/daemon/compatibility/execution.d.ts.map +1 -0
  47. package/dist/daemon/compatibility/execution.js +183 -0
  48. package/dist/daemon/compatibility/execution.js.map +1 -0
  49. package/dist/daemon/compatibility/mutation.d.ts +24 -0
  50. package/dist/daemon/compatibility/mutation.d.ts.map +1 -0
  51. package/dist/daemon/compatibility/mutation.js +724 -0
  52. package/dist/daemon/compatibility/mutation.js.map +1 -0
  53. package/dist/daemon/routes.d.ts +19 -0
  54. package/dist/daemon/routes.d.ts.map +1 -0
  55. package/dist/daemon/routes.js +123 -0
  56. package/dist/daemon/routes.js.map +1 -0
  57. package/dist/daemon/runtime/execution-bus.d.ts +217 -0
  58. package/dist/daemon/runtime/execution-bus.d.ts.map +1 -0
  59. package/dist/daemon/runtime/execution-bus.js +1420 -0
  60. package/dist/daemon/runtime/execution-bus.js.map +1 -0
  61. package/dist/daemon/runtime/workspace-runtime.d.ts +280 -0
  62. package/dist/daemon/runtime/workspace-runtime.d.ts.map +1 -0
  63. package/dist/daemon/runtime/workspace-runtime.js +1473 -0
  64. package/dist/daemon/runtime/workspace-runtime.js.map +1 -0
  65. package/dist/daemon/server.d.ts.map +1 -1
  66. package/dist/daemon/server.js +171 -874
  67. package/dist/daemon/server.js.map +1 -1
  68. package/dist/daemon/shaping.d.ts +11 -0
  69. package/dist/daemon/shaping.d.ts.map +1 -0
  70. package/dist/daemon/shaping.js +240 -0
  71. package/dist/daemon/shaping.js.map +1 -0
  72. package/dist/governance/canonical-pipeline.d.ts +2 -1
  73. package/dist/governance/canonical-pipeline.d.ts.map +1 -1
  74. package/dist/governance/canonical-pipeline.js +259 -84
  75. package/dist/governance/canonical-pipeline.js.map +1 -1
  76. package/dist/governance/structural-cache.d.ts.map +1 -1
  77. package/dist/governance/structural-cache.js +2 -7
  78. package/dist/governance/structural-cache.js.map +1 -1
  79. package/dist/index.js +230 -66
  80. package/dist/index.js.map +1 -1
  81. package/dist/utils/active-engineering-context.d.ts +12 -0
  82. package/dist/utils/active-engineering-context.d.ts.map +1 -0
  83. package/dist/utils/active-engineering-context.js +67 -0
  84. package/dist/utils/active-engineering-context.js.map +1 -0
  85. package/dist/utils/artifact-io.d.ts +33 -0
  86. package/dist/utils/artifact-io.d.ts.map +1 -0
  87. package/dist/utils/artifact-io.js +183 -0
  88. package/dist/utils/artifact-io.js.map +1 -0
  89. package/dist/utils/change-contract.d.ts +6 -2
  90. package/dist/utils/change-contract.d.ts.map +1 -1
  91. package/dist/utils/change-contract.js +175 -0
  92. package/dist/utils/change-contract.js.map +1 -1
  93. package/dist/utils/context-pack.d.ts +12 -0
  94. package/dist/utils/context-pack.d.ts.map +1 -0
  95. package/dist/utils/context-pack.js +147 -0
  96. package/dist/utils/context-pack.js.map +1 -0
  97. package/dist/utils/control-plane.d.ts +18 -0
  98. package/dist/utils/control-plane.d.ts.map +1 -1
  99. package/dist/utils/control-plane.js +31 -4
  100. package/dist/utils/control-plane.js.map +1 -1
  101. package/dist/utils/drift-intelligence.d.ts +47 -0
  102. package/dist/utils/drift-intelligence.d.ts.map +1 -0
  103. package/dist/utils/drift-intelligence.js +2099 -0
  104. package/dist/utils/drift-intelligence.js.map +1 -0
  105. package/dist/utils/execution-actions.d.ts +22 -0
  106. package/dist/utils/execution-actions.d.ts.map +1 -0
  107. package/dist/utils/execution-actions.js +103 -0
  108. package/dist/utils/execution-actions.js.map +1 -0
  109. package/dist/utils/execution-bus.d.ts +1 -214
  110. package/dist/utils/execution-bus.d.ts.map +1 -1
  111. package/dist/utils/execution-bus.js +15 -1359
  112. package/dist/utils/execution-bus.js.map +1 -1
  113. package/dist/utils/git.d.ts +1 -0
  114. package/dist/utils/git.d.ts.map +1 -1
  115. package/dist/utils/git.js +13 -3
  116. package/dist/utils/git.js.map +1 -1
  117. package/dist/utils/governance-decisions.d.ts +75 -0
  118. package/dist/utils/governance-decisions.d.ts.map +1 -0
  119. package/dist/utils/governance-decisions.js +412 -0
  120. package/dist/utils/governance-decisions.js.map +1 -0
  121. package/dist/utils/governance-provenance.d.ts +1 -1
  122. package/dist/utils/governance-provenance.d.ts.map +1 -1
  123. package/dist/utils/governance-provenance.js +5 -7
  124. package/dist/utils/governance-provenance.js.map +1 -1
  125. package/dist/utils/governance.d.ts +108 -0
  126. package/dist/utils/governance.d.ts.map +1 -1
  127. package/dist/utils/governance.js +209 -7
  128. package/dist/utils/governance.js.map +1 -1
  129. package/dist/utils/intelligence-runtime-common.d.ts +30 -0
  130. package/dist/utils/intelligence-runtime-common.d.ts.map +1 -0
  131. package/dist/utils/intelligence-runtime-common.js +156 -0
  132. package/dist/utils/intelligence-runtime-common.js.map +1 -0
  133. package/dist/utils/intent-contract-diagnostics.d.ts +9 -0
  134. package/dist/utils/intent-contract-diagnostics.d.ts.map +1 -0
  135. package/dist/utils/intent-contract-diagnostics.js +322 -0
  136. package/dist/utils/intent-contract-diagnostics.js.map +1 -0
  137. package/dist/utils/intent-pack.d.ts +15 -0
  138. package/dist/utils/intent-pack.d.ts.map +1 -0
  139. package/dist/utils/intent-pack.js +196 -0
  140. package/dist/utils/intent-pack.js.map +1 -0
  141. package/dist/utils/plan-sync.d.ts +1 -0
  142. package/dist/utils/plan-sync.d.ts.map +1 -1
  143. package/dist/utils/plan-sync.js +23 -0
  144. package/dist/utils/plan-sync.js.map +1 -1
  145. package/dist/utils/policy-decision.d.ts +5 -0
  146. package/dist/utils/policy-decision.d.ts.map +1 -0
  147. package/dist/utils/policy-decision.js +17 -0
  148. package/dist/utils/policy-decision.js.map +1 -0
  149. package/dist/utils/replay-custody.d.ts +43 -0
  150. package/dist/utils/replay-custody.d.ts.map +1 -0
  151. package/dist/utils/replay-custody.js +168 -0
  152. package/dist/utils/replay-custody.js.map +1 -0
  153. package/dist/utils/replay-runtime.d.ts +13 -0
  154. package/dist/utils/replay-runtime.d.ts.map +1 -1
  155. package/dist/utils/replay-runtime.js +96 -9
  156. package/dist/utils/replay-runtime.js.map +1 -1
  157. package/dist/utils/repository-intelligence.d.ts +9 -0
  158. package/dist/utils/repository-intelligence.d.ts.map +1 -0
  159. package/dist/utils/repository-intelligence.js +372 -0
  160. package/dist/utils/repository-intelligence.js.map +1 -0
  161. package/dist/utils/runtime-events.d.ts.map +1 -1
  162. package/dist/utils/runtime-events.js +25 -6
  163. package/dist/utils/runtime-events.js.map +1 -1
  164. package/dist/utils/semantic-contract-intelligence.d.ts +20 -0
  165. package/dist/utils/semantic-contract-intelligence.d.ts.map +1 -0
  166. package/dist/utils/semantic-contract-intelligence.js +825 -0
  167. package/dist/utils/semantic-contract-intelligence.js.map +1 -0
  168. package/dist/utils/session-continuity.d.ts +56 -0
  169. package/dist/utils/session-continuity.d.ts.map +1 -0
  170. package/dist/utils/session-continuity.js +318 -0
  171. package/dist/utils/session-continuity.js.map +1 -0
  172. package/dist/utils/verification-evidence.d.ts.map +1 -1
  173. package/dist/utils/verification-evidence.js +4 -1
  174. package/dist/utils/verification-evidence.js.map +1 -1
  175. package/dist/utils/workspace-runtime.d.ts +1 -266
  176. package/dist/utils/workspace-runtime.d.ts.map +1 -1
  177. package/dist/utils/workspace-runtime.js +15 -1412
  178. package/dist/utils/workspace-runtime.js.map +1 -1
  179. package/package.json +11 -10
  180. package/LICENSE +0 -201
@@ -67,6 +67,9 @@ const policy_compiler_1 = require("../utils/policy-compiler");
67
67
  const change_contract_1 = require("../utils/change-contract");
68
68
  const diff_symbols_1 = require("../utils/diff-symbols");
69
69
  const advisory_signals_1 = require("../utils/advisory-signals");
70
+ const verify_guidance_1 = require("./verify-guidance");
71
+ const verify_output_1 = require("./verify-output");
72
+ const verify_render_1 = require("./verify-render");
70
73
  const structural_rules_1 = require("../structural-rules");
71
74
  const canonical_pipeline_1 = require("../governance/canonical-pipeline");
72
75
  const canonical_invariants_1 = require("../governance/canonical-invariants");
@@ -76,15 +79,16 @@ const structural_on_diff_1 = require("../governance/structural-on-diff");
76
79
  // into the canonical pipeline. Merging them into policyViolations caused
77
80
  // duplicate GovernanceFinding objects (fixed in Phase 1 canonical graph hardening).
78
81
  const telemetry_1 = require("@neurcode-ai/telemetry");
79
- const governance_provenance_1 = require("../utils/governance-provenance");
80
82
  const pilot_metrics_1 = require("../utils/pilot-metrics");
81
- const explainability_1 = require("../explainability");
83
+ const replay_custody_1 = require("../utils/replay-custody");
82
84
  const runtime_guard_1 = require("../utils/runtime-guard");
83
85
  const artifact_signature_1 = require("../utils/artifact-signature");
84
86
  const policy_1 = require("@neurcode-ai/policy");
87
+ const active_engineering_context_1 = require("../utils/active-engineering-context");
85
88
  const ai_debt_budget_1 = require("../utils/ai-debt-budget");
86
89
  const verification_evidence_1 = require("../utils/verification-evidence");
87
90
  const verify_runtime_stability_1 = require("../utils/verify-runtime-stability");
91
+ const policy_decision_1 = require("../utils/policy-decision");
88
92
  // Import chalk with fallback
89
93
  let chalk;
90
94
  try {
@@ -137,6 +141,42 @@ function logCiPolicyOnlyOutcomeExplainability(params) {
137
141
  }
138
142
  console.log(chalk.dim(' Next: resolve BLOCKING first → `neurcode remediate-export` (optional) → re-run `neurcode verify --ci`.\n'));
139
143
  }
144
+ function driftSeverityToPolicySeverity(severity) {
145
+ return severity === 'critical' || severity === 'high' ? 'block' : 'warn';
146
+ }
147
+ function driftGateToPolicySeverity(gate, fallbackSeverity) {
148
+ if (gate === 'policy-blocker' || gate === 'rollout-blocker' || gate === 'architecture-blocker') {
149
+ return 'block';
150
+ }
151
+ if (gate === 'review-blocker' || gate === 'advisory') {
152
+ return 'warn';
153
+ }
154
+ return driftSeverityToPolicySeverity(fallbackSeverity);
155
+ }
156
+ function driftFindingsToVerificationViolations(drift) {
157
+ if (!drift || !Array.isArray(drift.findings)) {
158
+ return [];
159
+ }
160
+ if (Array.isArray(drift.narratives) && drift.narratives.length > 0) {
161
+ return drift.narratives.map((narrative) => ({
162
+ file: narrative.affectedFiles[0]
163
+ || narrative.affectedModules[0]
164
+ || narrative.affectedServices[0]
165
+ || '.neurcode/intent-pack.json',
166
+ rule: `drift_narrative:${narrative.category}`,
167
+ severity: driftGateToPolicySeverity(drift.riskSynthesis?.governanceGate, narrative.severity),
168
+ message: narrative.summary,
169
+ }));
170
+ }
171
+ return drift.findings.map((finding) => ({
172
+ file: finding.file || finding.module || finding.service || '.neurcode/intent-pack.json',
173
+ rule: `drift_intelligence:${finding.category}`,
174
+ severity: driftGateToPolicySeverity(finding.governanceGate, finding.severity),
175
+ message: finding.priority
176
+ ? `${finding.message} (${finding.priority}; ${finding.governanceGate || 'review-blocker'})`
177
+ : finding.message,
178
+ }));
179
+ }
140
180
  ;
141
181
  function toArtifactSignatureSummary(status) {
142
182
  return {
@@ -697,569 +737,6 @@ function runtimeGuardViolationsToReport(summary) {
697
737
  message: item.message,
698
738
  }));
699
739
  }
700
- function asObjectRecord(value) {
701
- if (!value || typeof value !== 'object' || Array.isArray(value)) {
702
- return null;
703
- }
704
- return value;
705
- }
706
- function asObjectArray(value) {
707
- if (!Array.isArray(value)) {
708
- return [];
709
- }
710
- return value
711
- .map((item) => asObjectRecord(item))
712
- .filter((item) => item !== null);
713
- }
714
- function asBooleanFlag(value) {
715
- return typeof value === 'boolean' ? value : null;
716
- }
717
- function asNumberValue(value) {
718
- return typeof value === 'number' && Number.isFinite(value) ? value : null;
719
- }
720
- function asStringValue(value) {
721
- return typeof value === 'string' && value.trim().length > 0 ? value : null;
722
- }
723
- const EXPEDITE_FOLLOW_UP_CHECKLIST = [
724
- 'Add validation back',
725
- 'Move logic to proper layer',
726
- 'Remove temporary code',
727
- ];
728
- function containsAnyToken(value, tokens) {
729
- const normalized = value.toLowerCase();
730
- return tokens.some((token) => normalized.includes(token));
731
- }
732
- function isSecurityOrAuthViolation(fileRaw, policyRaw, messageRaw) {
733
- const combined = `${fileRaw} ${policyRaw} ${messageRaw}`.toLowerCase();
734
- return containsAnyToken(combined, [
735
- 'auth',
736
- 'authentication',
737
- 'authorization',
738
- 'security',
739
- 'permission',
740
- 'access control',
741
- 'access_control',
742
- 'token',
743
- 'secret',
744
- 'credential',
745
- 'encryption',
746
- 'encrypt',
747
- 'decrypt',
748
- 'csrf',
749
- 'xss',
750
- 'sql injection',
751
- 'sqli',
752
- 'insecure',
753
- 'vulnerability',
754
- ]);
755
- }
756
- function isCriticalScopeBreach(fileRaw, messageRaw) {
757
- const combined = `${fileRaw} ${messageRaw}`.toLowerCase();
758
- return containsAnyToken(combined, [
759
- 'auth',
760
- 'security',
761
- 'secret',
762
- 'token',
763
- 'credential',
764
- 'permission',
765
- 'infra/terraform',
766
- 'terraform',
767
- 'k8s',
768
- 'helm',
769
- 'migration',
770
- 'database/migration',
771
- 'policy',
772
- 'contract',
773
- ]);
774
- }
775
- function resolveExpediteModeFromPayload(payload) {
776
- const explicit = asBooleanFlag(payload.expediteMode);
777
- if (explicit !== null) {
778
- return explicit;
779
- }
780
- const message = asStringValue(payload.message) || '';
781
- return containsAnyToken(message, ['hotfix', 'urgent', 'prod down', 'incident', 'expedite']);
782
- }
783
- function toVerifySeverity(value) {
784
- const normalized = typeof value === 'string' ? value.trim().toLowerCase() : '';
785
- if (normalized === 'critical' || normalized === 'block')
786
- return 'critical';
787
- if (normalized === 'high')
788
- return 'high';
789
- if (normalized === 'warn'
790
- || normalized === 'warning'
791
- || normalized === 'medium'
792
- || normalized === 'low') {
793
- return 'warning';
794
- }
795
- return 'info';
796
- }
797
- function toVerifyVerdict(value) {
798
- const normalized = typeof value === 'string' ? value.trim().toUpperCase() : '';
799
- if (normalized === 'PASS' || normalized === 'WARN' || normalized === 'FAIL') {
800
- return normalized;
801
- }
802
- return 'FAIL';
803
- }
804
- function normalizeScopeIssueMessage(rawMessage) {
805
- const message = asStringValue(rawMessage);
806
- return message || 'File modified outside intended scope';
807
- }
808
- function pushVerifyIssue(target, seen, key, value) {
809
- if (seen.has(key))
810
- return;
811
- seen.add(key);
812
- target.push(value);
813
- }
814
- function dedupeTriageItems(items) {
815
- const seen = new Set();
816
- const output = [];
817
- for (const item of items) {
818
- const key = `${item.source}|${item.file.toLowerCase()}|${item.policy.toLowerCase()}|${item.message.toLowerCase()}`;
819
- if (seen.has(key))
820
- continue;
821
- seen.add(key);
822
- output.push(item);
823
- }
824
- return output;
825
- }
826
- function toCanonicalVerifyOutput(payload) {
827
- const verdict = toVerifyVerdict(payload.verdict);
828
- const violations = [];
829
- const warnings = [];
830
- const scopeIssues = [];
831
- const seenViolations = new Set();
832
- const seenWarnings = new Set();
833
- const seenScopeIssues = new Set();
834
- const addScopeIssue = (fileRaw, messageRaw) => {
835
- const file = asStringValue(fileRaw) || 'unknown';
836
- const message = normalizeScopeIssueMessage(messageRaw);
837
- const key = file.toLowerCase();
838
- pushVerifyIssue(scopeIssues, seenScopeIssues, key, { file, message });
839
- };
840
- const addWarning = (fileRaw, messageRaw, policyRaw) => {
841
- const file = asStringValue(fileRaw) || 'unknown';
842
- const message = asStringValue(messageRaw) || 'Warning detected';
843
- const policy = asStringValue(policyRaw) || 'warning';
844
- const key = `${file.toLowerCase()}|${message.toLowerCase()}|${policy.toLowerCase()}`;
845
- pushVerifyIssue(warnings, seenWarnings, key, { file, message, policy });
846
- };
847
- const addViolation = (fileRaw, messageRaw, policyRaw, severityRaw) => {
848
- const file = asStringValue(fileRaw) || 'unknown';
849
- const message = asStringValue(messageRaw) || 'Policy violation detected';
850
- const policy = asStringValue(policyRaw) || 'unknown_policy';
851
- const severity = toVerifySeverity(severityRaw);
852
- const key = `${file.toLowerCase()}|${message.toLowerCase()}|${policy.toLowerCase()}|${severity}`;
853
- pushVerifyIssue(violations, seenViolations, key, { file, message, policy, severity });
854
- };
855
- const rawScopeIssues = Array.isArray(payload.scopeIssues) ? payload.scopeIssues : [];
856
- for (const item of rawScopeIssues) {
857
- const record = asObjectRecord(item);
858
- if (record) {
859
- addScopeIssue(record.file, record.message);
860
- }
861
- else {
862
- addScopeIssue(item, null);
863
- }
864
- }
865
- const rawBloatFiles = Array.isArray(payload.bloatFiles) ? payload.bloatFiles : [];
866
- for (const item of rawBloatFiles) {
867
- addScopeIssue(item, null);
868
- }
869
- const rawWarnings = Array.isArray(payload.warnings) ? payload.warnings : [];
870
- for (const item of rawWarnings) {
871
- const record = asObjectRecord(item);
872
- if (record) {
873
- addWarning(record.file, record.message, record.policy ?? record.rule);
874
- }
875
- else if (typeof item === 'string') {
876
- addWarning('unknown', item, 'warning');
877
- }
878
- }
879
- const rawViolations = Array.isArray(payload.violations) ? payload.violations : [];
880
- for (const item of rawViolations) {
881
- const record = asObjectRecord(item);
882
- if (!record)
883
- continue;
884
- const file = record.file;
885
- const message = record.message;
886
- const policy = record.policy ?? record.rule;
887
- const severity = toVerifySeverity(record.severity);
888
- const combined = `${String(policy || '').toLowerCase()} ${String(message || '').toLowerCase()}`;
889
- const isScopeIssue = combined.includes('scope_guard')
890
- || combined.includes('scope')
891
- || combined.includes('outside the plan')
892
- || combined.includes('out of scope');
893
- if (isScopeIssue) {
894
- addScopeIssue(file, message);
895
- continue;
896
- }
897
- // Artifact presence/signature checks are advisory — they must never block a PR.
898
- // Real governance signal (policy violations, scope drift) should not be obscured
899
- // by infrastructure setup state.
900
- const policyStr = String(policy || '').toLowerCase();
901
- const isArtifactCheck = policyStr === 'deterministic_artifacts_required'
902
- || policyStr === 'signed_artifacts_required';
903
- if (isArtifactCheck) {
904
- addWarning(file, message, policy);
905
- continue;
906
- }
907
- if (severity === 'warning' || severity === 'info') {
908
- addWarning(file, message, policy);
909
- continue;
910
- }
911
- addViolation(file, message, policy, severity);
912
- }
913
- const payloadMessage = asStringValue(payload.message);
914
- if (payloadMessage
915
- && violations.length === 0
916
- && warnings.length === 0
917
- && scopeIssues.length === 0) {
918
- addWarning('unknown', payloadMessage, 'verify_result');
919
- }
920
- const summaryRecord = asObjectRecord(payload.summary);
921
- const fileSet = new Set();
922
- for (const violation of violations)
923
- fileSet.add(violation.file);
924
- for (const warning of warnings)
925
- fileSet.add(warning.file);
926
- for (const scopeIssue of scopeIssues)
927
- fileSet.add(scopeIssue.file);
928
- const totalFilesChanged = (() => {
929
- const fromSummary = summaryRecord ? asNumberValue(summaryRecord.totalFilesChanged) : null;
930
- if (fromSummary !== null)
931
- return Math.max(0, Math.floor(fromSummary));
932
- const blastRadius = asObjectRecord(payload.blastRadius);
933
- const fromBlastRadius = blastRadius ? asNumberValue(blastRadius.filesChanged) : null;
934
- if (fromBlastRadius !== null)
935
- return Math.max(0, Math.floor(fromBlastRadius));
936
- return fileSet.size;
937
- })();
938
- const driftScoreRaw = asNumberValue(payload.driftScore);
939
- const driftScore = driftScoreRaw === null
940
- ? undefined
941
- : Math.max(0, Math.min(100, Math.round(driftScoreRaw)));
942
- const expediteModeUsed = resolveExpediteModeFromPayload(payload);
943
- const scopeTriageItems = scopeIssues.map((item) => ({
944
- file: item.file,
945
- message: item.message,
946
- policy: 'scope_guard',
947
- severity: 'block',
948
- source: 'scope',
949
- }));
950
- const violationTriageItems = violations.map((item) => ({
951
- file: item.file,
952
- message: item.message,
953
- policy: item.policy,
954
- severity: item.severity,
955
- source: 'violation',
956
- }));
957
- const warningTriageItems = warnings.map((item) => ({
958
- file: item.file,
959
- message: item.message,
960
- policy: item.policy,
961
- severity: 'warning',
962
- source: 'warning',
963
- }));
964
- const defaultBlockingItems = dedupeTriageItems([
965
- ...scopeTriageItems,
966
- ...violationTriageItems.filter((item) => item.severity === 'critical' || item.severity === 'high'),
967
- ]);
968
- const defaultAdvisoryItems = dedupeTriageItems([
969
- ...warningTriageItems,
970
- ...violationTriageItems.filter((item) => item.severity === 'warning' || item.severity === 'info'),
971
- ]);
972
- const expediteBlockingItems = dedupeTriageItems([
973
- ...scopeTriageItems.filter((item) => isCriticalScopeBreach(item.file, item.message)),
974
- ...violationTriageItems.filter((item) => isSecurityOrAuthViolation(item.file, item.policy, item.message)),
975
- ...warningTriageItems
976
- .filter((item) => isSecurityOrAuthViolation(item.file, item.policy, item.message))
977
- .map((item) => ({
978
- ...item,
979
- source: 'violation',
980
- })),
981
- ]);
982
- const expediteItems = dedupeTriageItems([
983
- ...scopeTriageItems
984
- .filter((item) => !isCriticalScopeBreach(item.file, item.message))
985
- .map((item) => ({
986
- ...item,
987
- source: 'expedite',
988
- })),
989
- ...violationTriageItems
990
- .filter((item) => !isSecurityOrAuthViolation(item.file, item.policy, item.message))
991
- .map((item) => ({
992
- ...item,
993
- source: 'expedite',
994
- })),
995
- ...warningTriageItems
996
- .filter((item) => !isSecurityOrAuthViolation(item.file, item.policy, item.message))
997
- .map((item) => ({
998
- ...item,
999
- source: 'expedite',
1000
- })),
1001
- ]);
1002
- // ── Intent issues and summary from engine ───────────────────────────────
1003
- const rawIntentIssues = Array.isArray(payload.intentIssues) ? payload.intentIssues : [];
1004
- const intentDomains = Array.isArray(payload.intentDomains) ? payload.intentDomains : [];
1005
- const intentSummary = (payload.intentSummary ?? null);
1006
- const rawFlowIssues = Array.isArray(payload.flowIssues) ? payload.flowIssues : [];
1007
- const rawRegressions = Array.isArray(payload.regressions) ? payload.regressions : [];
1008
- // High-severity intent issues become blocking; medium become advisory.
1009
- const intentBlockingTriageItems = rawIntentIssues
1010
- .filter((i) => i.severity === 'high')
1011
- .map((i) => ({
1012
- file: (i.files?.[0]) ?? 'intent-analysis',
1013
- message: i.message,
1014
- policy: i.rule,
1015
- severity: 'high',
1016
- source: 'violation',
1017
- }));
1018
- const intentAdvisoryTriageItems = rawIntentIssues
1019
- .filter((i) => i.severity === 'medium')
1020
- .map((i) => ({
1021
- file: (i.files?.[0]) ?? 'intent-analysis',
1022
- message: i.message,
1023
- policy: i.rule,
1024
- severity: 'warning',
1025
- source: 'warning',
1026
- }));
1027
- // V5: flow issues — high → blocking, medium → advisory
1028
- const flowBlockingTriageItems = rawFlowIssues
1029
- .filter((i) => i.severity === 'high')
1030
- .map((i) => ({
1031
- file: (i.files?.[0]) ?? 'flow-analysis',
1032
- message: i.message,
1033
- policy: i.rule,
1034
- severity: 'high',
1035
- source: 'violation',
1036
- }));
1037
- const flowAdvisoryTriageItems = rawFlowIssues
1038
- .filter((i) => i.severity === 'medium')
1039
- .map((i) => ({
1040
- file: (i.files?.[0]) ?? 'flow-analysis',
1041
- message: i.message,
1042
- policy: i.rule,
1043
- severity: 'warning',
1044
- source: 'warning',
1045
- }));
1046
- let blockingItems = expediteModeUsed ? expediteBlockingItems : defaultBlockingItems;
1047
- let advisoryItems = expediteModeUsed ? expediteItems : defaultAdvisoryItems;
1048
- if (intentBlockingTriageItems.length > 0) {
1049
- blockingItems = dedupeTriageItems([...blockingItems, ...intentBlockingTriageItems]);
1050
- }
1051
- if (intentAdvisoryTriageItems.length > 0) {
1052
- advisoryItems = dedupeTriageItems([...advisoryItems, ...intentAdvisoryTriageItems]);
1053
- }
1054
- if (flowBlockingTriageItems.length > 0) {
1055
- blockingItems = dedupeTriageItems([...blockingItems, ...flowBlockingTriageItems]);
1056
- }
1057
- if (flowAdvisoryTriageItems.length > 0) {
1058
- advisoryItems = dedupeTriageItems([...advisoryItems, ...flowAdvisoryTriageItems]);
1059
- }
1060
- // V6: regressions — always blocking
1061
- const regressionBlockingTriageItems = rawRegressions.map((r) => ({
1062
- file: 'regression-analysis',
1063
- message: r.message,
1064
- policy: r.rule,
1065
- severity: 'high',
1066
- source: 'violation',
1067
- }));
1068
- if (regressionBlockingTriageItems.length > 0) {
1069
- blockingItems = dedupeTriageItems([...regressionBlockingTriageItems, ...blockingItems]);
1070
- }
1071
- const grade = verdict === 'PASS' ? 'A' : verdict === 'WARN' ? 'C' : 'F';
1072
- const canonical = {
1073
- grade,
1074
- score: violations.length === 0 && warnings.length === 0 && scopeIssues.length === 0 ? 100 : 0,
1075
- verdict,
1076
- summary: {
1077
- totalFilesChanged,
1078
- totalViolations: violations.length,
1079
- totalWarnings: warnings.length,
1080
- totalScopeIssues: scopeIssues.length,
1081
- },
1082
- violations,
1083
- warnings,
1084
- scopeIssues,
1085
- blockingCount: blockingItems.length,
1086
- advisoryCount: advisoryItems.length,
1087
- blockingItems,
1088
- advisoryItems,
1089
- intentIssues: rawIntentIssues,
1090
- intentDomains,
1091
- intentSummary,
1092
- flowIssues: rawFlowIssues,
1093
- regressions: rawRegressions,
1094
- expediteModeUsed,
1095
- expediteCount: expediteModeUsed ? expediteItems.length : 0,
1096
- expediteItems: expediteModeUsed ? expediteItems : [],
1097
- expediteFollowUpChecklist: expediteModeUsed ? [...EXPEDITE_FOLLOW_UP_CHECKLIST] : [],
1098
- ...(expediteModeUsed ? { expediteNote: 'Expedite Mode used' } : {}),
1099
- ...(typeof driftScore === 'number' ? { driftScore } : {}),
1100
- };
1101
- // Preserve actionable metadata from rich verify payloads so downstream
1102
- // consumers (Action, CI contracts, dashboards) keep structured context.
1103
- const passthroughKeys = [
1104
- 'message',
1105
- 'mode',
1106
- 'ciMode',
1107
- 'policyOnly',
1108
- 'policyOnlySource',
1109
- 'policySources',
1110
- 'scopeGuardPassed',
1111
- 'policyLock',
1112
- 'policyCompilation',
1113
- 'policyExceptions',
1114
- 'policyGovernance',
1115
- 'changeContract',
1116
- 'runtimeGuard',
1117
- 'governanceDecision',
1118
- 'orgGovernance',
1119
- 'aiChangeLog',
1120
- 'verificationSource',
1121
- 'tier',
1122
- 'aiDebt',
1123
- 'blastRadius',
1124
- 'suspiciousChange',
1125
- 'policyDecision',
1126
- 'policyPack',
1127
- 'changeContractViolations',
1128
- 'governanceVerification',
1129
- 'governanceFindings',
1130
- 'structuralViolations',
1131
- 'structuralRulesApplied',
1132
- 'structuralSuppressedCount',
1133
- ];
1134
- const canonicalMutable = canonical;
1135
- for (const key of passthroughKeys) {
1136
- const v = payload[key];
1137
- if (Object.prototype.hasOwnProperty.call(payload, key) && v !== undefined && v !== null) {
1138
- canonicalMutable[key] = v;
1139
- }
1140
- }
1141
- // Backward-compatibility alias: older integrations and tests expect `rule`
1142
- // while canonical contract uses `policy`.
1143
- canonical.violations = canonical.violations.map((item) => ({
1144
- ...item,
1145
- rule: item.policy,
1146
- }));
1147
- canonical.warnings = canonical.warnings.map((item) => ({
1148
- ...item,
1149
- rule: item.policy,
1150
- }));
1151
- return canonical;
1152
- }
1153
- function emitCanonicalVerifyJson(payload, onEmit) {
1154
- const canonical = toCanonicalVerifyOutput((0, canonical_pipeline_1.attachCanonicalGovernance)(payload));
1155
- onEmit?.(canonical);
1156
- // Use sync stdout write so immediate process.exit paths do not truncate JSON.
1157
- const serialized = Buffer.from(`${JSON.stringify(canonical, null, 2)}\n`, 'utf-8');
1158
- try {
1159
- let offset = 0;
1160
- while (offset < serialized.length) {
1161
- try {
1162
- const written = (0, fs_1.writeSync)(1, serialized, offset, serialized.length - offset);
1163
- if (written <= 0)
1164
- break;
1165
- offset += written;
1166
- }
1167
- catch (error) {
1168
- const code = error.code;
1169
- if (code === 'EAGAIN' || code === 'EWOULDBLOCK') {
1170
- continue;
1171
- }
1172
- throw error;
1173
- }
1174
- }
1175
- }
1176
- catch {
1177
- process.stdout.write(serialized.toString('utf-8'));
1178
- }
1179
- }
1180
- function buildDeterministicLayerSummary(payload) {
1181
- const verdict = asStringValue(payload.verdict) || 'UNKNOWN';
1182
- const mode = asStringValue(payload.mode) || 'unknown';
1183
- const policyOnly = payload.policyOnly === true;
1184
- const scopeGuardPassed = asBooleanFlag(payload.scopeGuardPassed);
1185
- const violations = asObjectArray(payload.violations);
1186
- const policyViolations = violations.filter((entry) => {
1187
- const rule = String(entry.rule || '').toLowerCase();
1188
- return (!rule.includes('scope_guard')
1189
- && !rule.includes('change_contract')
1190
- && !rule.includes('runtime_guard')
1191
- && !rule.includes('deterministic_artifacts_required')
1192
- && !rule.includes('signed_artifacts_required'));
1193
- });
1194
- const policyBlocking = policyViolations.filter((entry) => String(entry.severity || '').toLowerCase() === 'block');
1195
- const policyWarnings = policyViolations.filter((entry) => String(entry.severity || '').toLowerCase() === 'warn');
1196
- const changeContract = asObjectRecord(payload.changeContract);
1197
- const changeContractValid = asBooleanFlag(changeContract?.valid);
1198
- const changeContractEnforced = changeContract?.enforced === true;
1199
- const changeContractViolations = Array.isArray(changeContract?.violations)
1200
- ? (changeContract?.violations).length
1201
- : 0;
1202
- const explicitContractViolations = violations.filter((entry) => {
1203
- const rule = String(entry.rule || '').toLowerCase();
1204
- return rule.includes('scope_guard') || rule.includes('change_contract');
1205
- }).length;
1206
- const runtimeGuard = asObjectRecord(payload.runtimeGuard);
1207
- const runtimeGuardRequired = runtimeGuard?.required === true;
1208
- const runtimeGuardPass = asBooleanFlag(runtimeGuard?.pass);
1209
- const runtimeGuardViolations = Array.isArray(runtimeGuard?.violations)
1210
- ? (runtimeGuard?.violations).length
1211
- : violations.filter((entry) => String(entry.rule || '').toLowerCase().includes('runtime_guard')).length;
1212
- const policyCompilation = asObjectRecord(payload.policyCompilation);
1213
- const deterministicRuleCount = asNumberValue(policyCompilation?.deterministicRuleCount);
1214
- const unmatchedStatements = asNumberValue(policyCompilation?.unmatchedStatements);
1215
- let policyGateStatus = 'pass';
1216
- if (policyBlocking.length > 0) {
1217
- policyGateStatus = 'fail';
1218
- }
1219
- else if (policyWarnings.length > 0 || verdict === 'WARN') {
1220
- policyGateStatus = 'warn';
1221
- }
1222
- let contractGateStatus = 'not_applicable';
1223
- if (!policyOnly) {
1224
- contractGateStatus = 'pass';
1225
- if (changeContractEnforced
1226
- && (changeContractValid === false || changeContractViolations > 0 || explicitContractViolations > 0 || scopeGuardPassed === false)) {
1227
- contractGateStatus = 'fail';
1228
- }
1229
- else if (!changeContractEnforced && (changeContractViolations > 0 || explicitContractViolations > 0)) {
1230
- contractGateStatus = 'warn';
1231
- }
1232
- }
1233
- let runtimeGuardStatus = 'not_applicable';
1234
- if (runtimeGuardRequired) {
1235
- runtimeGuardStatus = runtimeGuardPass === false || runtimeGuardViolations > 0 ? 'fail' : 'pass';
1236
- }
1237
- else if (runtimeGuardViolations > 0) {
1238
- runtimeGuardStatus = 'fail';
1239
- }
1240
- return {
1241
- policyGate: {
1242
- status: policyGateStatus,
1243
- blockingViolations: policyBlocking.length,
1244
- warningViolations: policyWarnings.length,
1245
- deterministicRuleCount: deterministicRuleCount ?? null,
1246
- unmatchedStatements: unmatchedStatements ?? null,
1247
- },
1248
- contractGate: {
1249
- status: contractGateStatus,
1250
- enforced: changeContractEnforced,
1251
- valid: changeContractValid,
1252
- violationCount: changeContractViolations + explicitContractViolations,
1253
- mode,
1254
- },
1255
- runtimeGuardGate: {
1256
- status: runtimeGuardStatus,
1257
- required: runtimeGuardRequired,
1258
- pass: runtimeGuardPass,
1259
- violationCount: runtimeGuardViolations,
1260
- },
1261
- };
1262
- }
1263
740
  function toAiDebtSummary(evaluation) {
1264
741
  return {
1265
742
  mode: evaluation.mode,
@@ -1363,7 +840,7 @@ function resolveVerifyExpediteMode(projectRoot) {
1363
840
  return true;
1364
841
  }
1365
842
  const branchName = (0, git_1.detectCurrentGitBranch)(projectRoot) || process.env.GITHUB_REF_NAME || '';
1366
- return containsAnyToken(branchName, ['hotfix', 'urgent', 'prod-down', 'prod_down', 'prod down', 'incident', 'expedite']);
843
+ return (0, verify_output_1.containsAnyToken)(branchName, ['hotfix', 'urgent', 'prod-down', 'prod_down', 'prod down', 'incident', 'expedite']);
1367
844
  }
1368
845
  function isSignedAiLogsRequired(orgGovernanceSettings) {
1369
846
  const explicitRequirement = isEnabledFlag(process.env.NEURCODE_GOVERNANCE_REQUIRE_SIGNED_LOGS) ||
@@ -1388,19 +865,6 @@ function toPolicyLockViolations(mismatches) {
1388
865
  message: item.message,
1389
866
  }));
1390
867
  }
1391
- function resolvePolicyDecisionFromViolations(violations) {
1392
- let hasWarn = false;
1393
- for (const violation of violations) {
1394
- const severity = String(violation.severity || '').toLowerCase();
1395
- if (severity === 'block') {
1396
- return 'block';
1397
- }
1398
- if (severity === 'warn') {
1399
- hasWarn = true;
1400
- }
1401
- }
1402
- return hasWarn ? 'warn' : 'allow';
1403
- }
1404
868
  function explainExceptionEligibilityReason(reason) {
1405
869
  switch (reason) {
1406
870
  case 'reason_required':
@@ -1537,12 +1001,15 @@ async function recordVerificationIfRequested(options, config, payload) {
1537
1001
  * Returns the exit code to use
1538
1002
  */
1539
1003
  async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRoot, config, client, source, ciModeEnabled, scopeTelemetry, projectId, orgGovernanceSettings, aiLogSigningKey, aiLogSigningKeyId, aiLogSigningKeys, aiLogSigner, expediteModeEnabled, compiledPolicyArtifact, compiledPolicyMetadata, changeContractSummary, onCanonicalEmit) {
1540
- const emitPolicyOnlyJson = (payload) => {
1541
- emitCanonicalVerifyJson({
1004
+ const emitPolicyOnlyJson = (payload, onEmit) => {
1005
+ (0, verify_output_1.emitCanonicalVerifyJson)({
1542
1006
  ...payload,
1543
1007
  ciMode: payload.ciMode ?? ciModeEnabled,
1544
1008
  expediteMode: expediteModeEnabled,
1545
- }, onCanonicalEmit);
1009
+ }, (canonical) => {
1010
+ onEmit?.(canonical);
1011
+ onCanonicalEmit?.(canonical);
1012
+ });
1546
1013
  };
1547
1014
  const policyOnlyVerificationSource = 'policy_only';
1548
1015
  const recordPolicyOnlyVerification = async (payload) => recordVerificationIfRequested(options, config, {
@@ -1559,18 +1026,22 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
1559
1026
  const diffFilesForPolicy = diffFiles.filter((f) => !ignoreFilter(f.path));
1560
1027
  const expectedPolicyOnlyFiles = diffFilesForPolicy.map((file) => file.path);
1561
1028
  const signedLogsRequired = isSignedAiLogsRequired(orgGovernanceSettings);
1029
+ const activeEngineeringContext = (0, active_engineering_context_1.loadActiveEngineeringContext)(projectRoot);
1562
1030
  const governanceAnalysis = (0, governance_1.evaluateGovernance)({
1563
1031
  projectRoot,
1564
1032
  task: 'Policy-only verification',
1565
1033
  expectedFiles: expectedPolicyOnlyFiles,
1566
1034
  diffFiles: diffFilesForPolicy,
1567
- contextCandidates: expectedPolicyOnlyFiles,
1035
+ contextCandidates: activeEngineeringContext
1036
+ ? activeEngineeringContext.contextPack.selectedFiles.map((item) => item.path)
1037
+ : expectedPolicyOnlyFiles,
1568
1038
  orgGovernance: orgGovernanceSettings,
1569
1039
  requireSignedAiLogs: signedLogsRequired,
1570
1040
  signingKey: aiLogSigningKey,
1571
1041
  signingKeyId: aiLogSigningKeyId,
1572
1042
  signingKeys: aiLogSigningKeys,
1573
1043
  signer: aiLogSigner,
1044
+ activeEngineeringContext,
1574
1045
  });
1575
1046
  const governancePayload = buildGovernancePayload(governanceAnalysis, orgGovernanceSettings, {
1576
1047
  compiledPolicy: compiledPolicyMetadata,
@@ -1636,19 +1107,23 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
1636
1107
  const message = governanceAnalysis.governanceDecision.summary
1637
1108
  || 'Governance decision matrix returned BLOCK.';
1638
1109
  const reasonCodes = governanceAnalysis.governanceDecision.reasonCodes || [];
1110
+ const driftViolations = driftFindingsToVerificationViolations(governanceAnalysis.driftIntelligence)
1111
+ .filter((item) => !ignoreFilter(item.file));
1112
+ const governanceBlockViolations = [
1113
+ {
1114
+ file: '.neurcode/ai-change-log.json',
1115
+ rule: 'governance_decision_block',
1116
+ severity: 'block',
1117
+ message,
1118
+ },
1119
+ ...driftViolations,
1120
+ ];
1639
1121
  if (options.json) {
1640
1122
  emitPolicyOnlyJson({
1641
1123
  grade: 'F',
1642
1124
  score: 0,
1643
1125
  verdict: 'FAIL',
1644
- violations: [
1645
- {
1646
- file: '.neurcode/ai-change-log.json',
1647
- rule: 'governance_decision_block',
1648
- severity: 'block',
1649
- message,
1650
- },
1651
- ],
1126
+ violations: governanceBlockViolations,
1652
1127
  message,
1653
1128
  scopeGuardPassed: false,
1654
1129
  bloatCount: 0,
@@ -1671,14 +1146,7 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
1671
1146
  }
1672
1147
  await recordPolicyOnlyVerification({
1673
1148
  grade: 'F',
1674
- violations: [
1675
- {
1676
- file: '.neurcode/ai-change-log.json',
1677
- rule: 'governance_decision_block',
1678
- severity: 'block',
1679
- message,
1680
- },
1681
- ],
1149
+ violations: governanceBlockViolations,
1682
1150
  verifyResult: {
1683
1151
  adherenceScore: 0,
1684
1152
  verdict: 'FAIL',
@@ -1968,11 +1436,15 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
1968
1436
  message: `Policy audit chain is invalid: ${auditIntegrityStatus.issues.join('; ') || 'unknown issue'}`,
1969
1437
  });
1970
1438
  }
1439
+ if (governanceAnalysis.driftIntelligence) {
1440
+ const driftViolations = driftFindingsToVerificationViolations(governanceAnalysis.driftIntelligence);
1441
+ policyViolations.push(...driftViolations.filter((item) => !ignoreFilter(item.file)));
1442
+ }
1971
1443
  const policyOnlyStructural = (0, structural_on_diff_1.runStructuralOnDiffFiles)(projectRoot, diffFilesForPolicy);
1972
1444
  // Structural violations are passed to the canonical pipeline via payload.structuralViolations
1973
1445
  // (see line ~2584). Do NOT merge them into policyViolations — that would create structural:*
1974
1446
  // duplicates that contaminate the canonical finding graph.
1975
- policyDecision = resolvePolicyDecisionFromViolations(policyViolations);
1447
+ policyDecision = (0, policy_decision_1.resolvePolicyDecisionFromViolations)(policyViolations);
1976
1448
  const effectiveVerdict = policyDecision === 'block' ? 'FAIL' : policyDecision === 'warn' ? 'WARN' : 'PASS';
1977
1449
  const grade = effectiveVerdict === 'PASS' ? 'A' : effectiveVerdict === 'WARN' ? 'C' : 'F';
1978
1450
  const score = effectiveVerdict === 'PASS' ? 100 : effectiveVerdict === 'WARN' ? 50 : 0;
@@ -2073,15 +1545,46 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
2073
1545
  }
2074
1546
  : {}),
2075
1547
  };
1548
+ const policyOnlyReplayCustody = (0, replay_custody_1.captureVerifyReplayCustody)({
1549
+ projectRoot,
1550
+ diffContext: `${options.base || 'HEAD'} vs working tree`,
1551
+ filesAnalyzed: diffFiles.length,
1552
+ planId: null,
1553
+ verificationSource: policyOnlyVerificationSource,
1554
+ policyLockFingerprint: (0, policy_packs_1.readPolicyLockFile)(projectRoot).lock?.effective.fingerprint || null,
1555
+ compiledPolicyFingerprint: compiledPolicyArtifact?.fingerprint || null,
1556
+ ruleIds: policyOnlyStructural.rulesApplied,
1557
+ blockingCount: policyViolations.filter((v) => v.severity === 'block').length
1558
+ + policyOnlyStructural.violations.filter((v) => v.severity === 'BLOCKING').length,
1559
+ advisoryCount: policyViolations.filter((v) => v.severity !== 'block').length
1560
+ + policyOnlyStructural.violations.filter((v) => v.severity !== 'BLOCKING').length,
1561
+ suppressedCount: policyOnlyStructural.suppressedCount,
1562
+ structuralBlockingCount: policyOnlyStructural.violations.filter((v) => v.severity === 'BLOCKING').length,
1563
+ structuralAdvisoryCount: policyOnlyStructural.violations.filter((v) => v.severity !== 'BLOCKING').length,
1564
+ deterministicSignals: policyOnlyStructural.violations.filter((v) => v.determinism === 'deterministic-structural').length,
1565
+ heuristicSignals: policyOnlyStructural.violations.filter((v) => v.determinism === 'heuristic-advisory').length,
1566
+ overallTrustScore: policyOnlyStructural.violations.length > 0
1567
+ ? Math.round((policyOnlyStructural.violations.filter((v) => v.determinism === 'deterministic-structural').length / policyOnlyStructural.violations.length) * 100)
1568
+ : 100,
1569
+ verdict: effectiveVerdict,
1570
+ governanceDecision: governanceAnalysis.governanceDecision.summary || 'policy-only',
1571
+ actor: ciModeEnabled ? 'ci-runner' : 'local-user',
1572
+ source: ciModeEnabled ? 'ci' : 'cli',
1573
+ replayChecksum: policyOnlyReplayChecksum,
1574
+ });
2076
1575
  if (options.json) {
2077
- emitPolicyOnlyJson(policyOnlyPayload);
1576
+ emitPolicyOnlyJson(policyOnlyPayload, (canonical) => {
1577
+ (0, replay_custody_1.applyReplayCustodyToCanonicalOutput)(canonical, policyOnlyReplayCustody);
1578
+ });
2078
1579
  }
2079
1580
  else {
2080
- onCanonicalEmit?.(toCanonicalVerifyOutput({
1581
+ const policyOnlyCanonical = (0, verify_output_1.toCanonicalVerifyOutput)((0, canonical_pipeline_1.attachCanonicalGovernance)({
2081
1582
  ...policyOnlyPayload,
2082
1583
  ciMode: ciModeEnabled,
2083
1584
  expediteMode: expediteModeEnabled,
2084
1585
  }));
1586
+ (0, replay_custody_1.applyReplayCustodyToCanonicalOutput)(policyOnlyCanonical, policyOnlyReplayCustody);
1587
+ onCanonicalEmit?.(policyOnlyCanonical);
2085
1588
  if (effectiveVerdict === 'PASS') {
2086
1589
  console.log(chalk.green('✅ Policy check passed'));
2087
1590
  }
@@ -2101,7 +1604,7 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
2101
1604
  if (governance.audit.requireIntegrity && !auditIntegrityStatus.valid) {
2102
1605
  console.log(chalk.red(' Policy audit integrity check failed'));
2103
1606
  }
2104
- displayGovernanceInsights(governanceAnalysis, { explain: options.explain });
1607
+ (0, verify_render_1.displayGovernanceInsights)(chalk, governanceAnalysis, { explain: options.explain });
2105
1608
  console.log(chalk.dim(`\n${message}`));
2106
1609
  logCiPolicyOnlyOutcomeExplainability({
2107
1610
  ciModeEnabled,
@@ -2148,6 +1651,8 @@ async function verifyCommand(options) {
2148
1651
  let lastCanonicalOutput = null;
2149
1652
  let lastEvidenceFallbackOutput = null;
2150
1653
  let evidenceFinalizeAttempted = false;
1654
+ const custodySource = ciModeEnabled ? 'ci' : 'cli';
1655
+ const custodyActor = ciModeEnabled ? 'ci-runner' : 'local-user';
2151
1656
  // ── Phase 1 Runtime Stability: create context early so all subsystems share it ──
2152
1657
  // Structural governance is NEVER gated by this context — it always runs.
2153
1658
  const runtimeCtx = (0, verify_runtime_stability_1.createVerifyRuntimeContext)(ciModeEnabled);
@@ -2162,8 +1667,16 @@ async function verifyCommand(options) {
2162
1667
  options.asyncMode = false;
2163
1668
  }
2164
1669
  const captureEvidencePayload = (payload) => {
2165
- lastEvidenceFallbackOutput = payload;
2166
- lastCanonicalOutput = toCanonicalVerifyOutput(payload);
1670
+ const enrichedPayload = (0, canonical_pipeline_1.attachCanonicalGovernance)(payload);
1671
+ lastEvidenceFallbackOutput = enrichedPayload;
1672
+ lastCanonicalOutput = (0, verify_output_1.toCanonicalVerifyOutput)(enrichedPayload);
1673
+ };
1674
+ const applyCapturedReplayCustody = (canonical, custody) => {
1675
+ if (!canonical || !custody) {
1676
+ return;
1677
+ }
1678
+ (0, replay_custody_1.applyReplayCustodyToCanonicalOutput)(canonical, custody);
1679
+ lastProvenanceRunId = custody.provenanceRecord?.runId ?? lastProvenanceRunId;
2167
1680
  };
2168
1681
  const finalizeEvidence = (exitCode) => {
2169
1682
  if (!evidenceEnabled || evidenceFinalizeAttempted) {
@@ -2225,7 +1738,7 @@ async function verifyCommand(options) {
2225
1738
  let structuralViolations = [];
2226
1739
  let structuralRulesApplied = [];
2227
1740
  let structuralSuppressedCount = 0;
2228
- const emitVerifyJson = (payload) => {
1741
+ const emitVerifyJson = (payload, onEmit) => {
2229
1742
  // Check memory pressure immediately before emission (may have changed during long verify)
2230
1743
  (0, verify_runtime_stability_1.applyMemoryPressureDegradation)(runtimeCtx);
2231
1744
  const runtimeStabilityReport = (0, verify_runtime_stability_1.buildVerifyRuntimeReport)(runtimeCtx);
@@ -2248,8 +1761,9 @@ async function verifyCommand(options) {
2248
1761
  runtimeStabilityReport,
2249
1762
  };
2250
1763
  lastEvidenceFallbackOutput = enrichedPayload;
2251
- emitCanonicalVerifyJson(enrichedPayload, (canonical) => {
1764
+ (0, verify_output_1.emitCanonicalVerifyJson)(enrichedPayload, (canonical) => {
2252
1765
  lastCanonicalOutput = canonical;
1766
+ onEmit?.(lastCanonicalOutput);
2253
1767
  });
2254
1768
  };
2255
1769
  if (!isGitRepository(projectRoot)) {
@@ -2854,34 +2368,70 @@ async function verifyCommand(options) {
2854
2368
  console.log(chalk.cyan('\n🔍 Local-only mode: deterministic structural verification (no API required)...'));
2855
2369
  }
2856
2370
  const localStructural = (0, structural_on_diff_1.runStructuralOnDiffFiles)(projectRoot, diffFiles);
2371
+ const localStructuralFindings = localStructural.violations.map(canonical_pipeline_1.findingFromStructural);
2372
+ const localReplayChecksum = (0, canonical_invariants_1.computeCanonicalFindingChecksum)(localStructuralFindings);
2857
2373
  const blockingViolations = localStructural.violations.filter((v) => v.severity === 'BLOCKING');
2374
+ const advisoryViolations = localStructural.violations.filter((v) => v.severity !== 'BLOCKING');
2858
2375
  const localVerdict = blockingViolations.length > 0 ? 'FAIL' : 'PASS';
2859
2376
  const localGrade = blockingViolations.length > 0 ? 'F' : 'B';
2860
2377
  const localScore = blockingViolations.length > 0 ? 0 : 70;
2378
+ const localPayload = {
2379
+ grade: localGrade,
2380
+ score: localScore,
2381
+ verdict: localVerdict,
2382
+ violations: localStructural.violations.map((v) => ({
2383
+ file: v.filePath,
2384
+ rule: v.ruleId,
2385
+ severity: v.severity === 'BLOCKING' ? 'block' : 'warn',
2386
+ message: `${v.ruleName}: ${v.evidence.slice(0, 120)}`,
2387
+ })),
2388
+ adherenceScore: localScore,
2389
+ bloatCount: 0,
2390
+ bloatFiles: [],
2391
+ plannedFilesModified: 0,
2392
+ totalPlannedFiles: 0,
2393
+ message: `Local-only structural verification: ${localStructural.violations.length} finding(s), ${blockingViolations.length} blocking.`,
2394
+ scopeGuardPassed: true,
2395
+ mode: 'local_only_structural',
2396
+ policyOnly: true,
2397
+ replayChecksum: localReplayChecksum,
2398
+ replayMode: 'local-structural',
2399
+ structuralViolations: localStructural.violations,
2400
+ structuralBlockingCount: blockingViolations.length,
2401
+ structuralRulesApplied: localStructural.rulesApplied,
2402
+ changeContract: changeContractSummary,
2403
+ };
2404
+ captureEvidencePayload((0, canonical_pipeline_1.attachCanonicalGovernance)(localPayload));
2405
+ const localReplayCustody = (0, replay_custody_1.captureVerifyReplayCustody)({
2406
+ projectRoot,
2407
+ diffContext: `${options.base || 'HEAD'} vs working tree`,
2408
+ filesAnalyzed: diffFiles.length,
2409
+ planId: null,
2410
+ verificationSource: 'local_only_structural',
2411
+ policyLockFingerprint: null,
2412
+ compiledPolicyFingerprint: null,
2413
+ ruleIds: localStructural.rulesApplied,
2414
+ blockingCount: blockingViolations.length,
2415
+ advisoryCount: advisoryViolations.length,
2416
+ suppressedCount: localStructural.suppressedCount,
2417
+ structuralBlockingCount: blockingViolations.length,
2418
+ structuralAdvisoryCount: advisoryViolations.length,
2419
+ deterministicSignals: localStructural.violations.filter((v) => v.determinism === 'deterministic-structural').length,
2420
+ heuristicSignals: localStructural.violations.filter((v) => v.determinism === 'heuristic-advisory').length,
2421
+ overallTrustScore: localStructural.violations.length > 0
2422
+ ? Math.round((localStructural.violations.filter((v) => v.determinism === 'deterministic-structural').length / localStructural.violations.length) * 100)
2423
+ : 100,
2424
+ verdict: localVerdict,
2425
+ governanceDecision: 'local deterministic structural verification',
2426
+ actor: custodyActor,
2427
+ source: custodySource,
2428
+ replayChecksum: localReplayChecksum,
2429
+ });
2430
+ applyCapturedReplayCustody(lastCanonicalOutput, localReplayCustody);
2861
2431
  if (options.json) {
2862
- emitVerifyJson({
2863
- grade: localGrade,
2864
- score: localScore,
2865
- verdict: localVerdict,
2866
- violations: localStructural.violations.map((v) => ({
2867
- file: v.filePath,
2868
- rule: v.ruleId,
2869
- severity: v.severity === 'BLOCKING' ? 'block' : 'warn',
2870
- message: `${v.ruleName}: ${v.evidence.slice(0, 120)}`,
2871
- })),
2872
- adherenceScore: localScore,
2873
- bloatCount: 0,
2874
- bloatFiles: [],
2875
- plannedFilesModified: 0,
2876
- totalPlannedFiles: 0,
2877
- message: `Local-only structural verification: ${localStructural.violations.length} finding(s), ${blockingViolations.length} blocking.`,
2878
- scopeGuardPassed: true,
2879
- mode: 'local_only_structural',
2880
- policyOnly: true,
2881
- structuralViolations: localStructural.violations,
2882
- structuralBlockingCount: blockingViolations.length,
2883
- structuralRulesApplied: localStructural.rulesApplied,
2884
- changeContract: changeContractSummary,
2432
+ (0, verify_output_1.emitCanonicalVerifyJson)(localPayload, (canonical) => {
2433
+ applyCapturedReplayCustody(canonical, localReplayCustody);
2434
+ lastCanonicalOutput = canonical;
2885
2435
  });
2886
2436
  }
2887
2437
  else {
@@ -3278,6 +2828,10 @@ async function verifyCommand(options) {
3278
2828
  missingExpectedFiles: 0,
3279
2829
  blockedFilesTouched: 0,
3280
2830
  actionMismatches: 0,
2831
+ serviceBoundaryBreaches: 0,
2832
+ infraBoundaryBreaches: 0,
2833
+ dependencyBoundaryBreaches: 0,
2834
+ sensitiveBoundaryBreaches: 0,
3281
2835
  expectedSymbols: advisoryContract.expectedSymbols?.length || 0,
3282
2836
  changedSymbols: 0,
3283
2837
  missingExpectedSymbols: 0,
@@ -3387,8 +2941,8 @@ async function verifyCommand(options) {
3387
2941
  });
3388
2942
  }
3389
2943
  else {
3390
- printFirstRunAdvisoryMessage(options.demo === true);
3391
- printAdvisorySignals(advisorySignals, options.demo === true);
2944
+ (0, verify_guidance_1.printFirstRunAdvisoryMessage)(chalk, options.demo === true);
2945
+ (0, verify_guidance_1.printAdvisorySignals)(chalk, advisorySignals, options.demo === true);
3392
2946
  if (autoContractPath) {
3393
2947
  console.log(chalk.green(`✅ Auto-generated minimal advisory contract: ${autoContractPath}`));
3394
2948
  }
@@ -3426,6 +2980,7 @@ async function verifyCommand(options) {
3426
2980
  let planFiles = [];
3427
2981
  let planDependencies = [];
3428
2982
  let remotePlanSessionId = null;
2983
+ const activeEngineeringContext = (0, active_engineering_context_1.loadActiveEngineeringContext)(projectRoot);
3429
2984
  if (useLocalPlanSync) {
3430
2985
  const localIntent = (localPlanSync.intent || '').trim();
3431
2986
  const localConstraintText = localPlanSync.constraints.length > 0
@@ -3460,6 +3015,27 @@ async function verifyCommand(options) {
3460
3015
  }
3461
3016
  planFilesForVerification = [...new Set([...planFiles, ...localPlanExpectedFiles])];
3462
3017
  intentConstraintsForVerification = originalIntent || undefined;
3018
+ if (activeEngineeringContext) {
3019
+ if (activeEngineeringContext.intentPack.approvedScope.files.length > 0) {
3020
+ planFilesForVerification = [...activeEngineeringContext.intentPack.approvedScope.files];
3021
+ }
3022
+ if (activeEngineeringContext.intentPack.intent.normalized) {
3023
+ intentConstraintsForVerification = activeEngineeringContext.intentPack.intent.normalized;
3024
+ governanceTask = activeEngineeringContext.intentPack.intent.normalized;
3025
+ }
3026
+ planDependencies = [...new Set([
3027
+ ...planDependencies,
3028
+ ...activeEngineeringContext.intentPack.expectedDependencies,
3029
+ ])];
3030
+ if (!options.json) {
3031
+ console.log(chalk.dim(` Intent runtime loaded: ${activeEngineeringContext.sessionRuntime.sessionId} ` +
3032
+ `(${planFilesForVerification.length} approved file(s), ` +
3033
+ `${activeEngineeringContext.contextPack.selectedFiles.length} context file(s))`));
3034
+ activeEngineeringContext.warnings.slice(0, 3).forEach((warning) => {
3035
+ console.log(chalk.yellow(` Context warning: ${warning}`));
3036
+ });
3037
+ }
3038
+ }
3463
3039
  // ── Intent-Aware Engine ─────────────────────────────────────────────
3464
3040
  // Run once we have both diffFiles and the resolved intent text.
3465
3041
  // Stored in outer scope so all emitCanonicalVerifyJson call sites can
@@ -3513,13 +3089,16 @@ async function verifyCommand(options) {
3513
3089
  expectedFiles: planFilesForVerification,
3514
3090
  expectedDependencies: planDependencies,
3515
3091
  diffFiles,
3516
- contextCandidates: planFilesForVerification,
3092
+ contextCandidates: activeEngineeringContext
3093
+ ? activeEngineeringContext.contextPack.selectedFiles.map((item) => item.path)
3094
+ : planFilesForVerification,
3517
3095
  orgGovernance: orgGovernanceSettings,
3518
3096
  requireSignedAiLogs: signedLogsRequired,
3519
3097
  signingKey: aiLogSigningKey,
3520
3098
  signingKeyId: aiLogSigningKeyId,
3521
3099
  signingKeys: aiLogSigningKeys,
3522
3100
  signer: aiLogSigner,
3101
+ activeEngineeringContext,
3523
3102
  });
3524
3103
  // Get sessionId from state file (.neurcode/state.json) first, then fallback to config
3525
3104
  // Fallback to sessionId from plan if not in state/config
@@ -3561,13 +3140,17 @@ async function verifyCommand(options) {
3561
3140
  }
3562
3141
  }
3563
3142
  // Step C: The Intersection Logic
3564
- const approvedSet = new Set([...planFilesForVerification, ...allowedFiles]);
3143
+ const approvedSet = new Set([
3144
+ ...planFilesForVerification,
3145
+ ...allowedFiles,
3146
+ ...(governanceResult.engineeringContext?.approvedScope?.files || []),
3147
+ ]);
3565
3148
  const violations = modifiedFiles.filter(f => !approvedSet.has(f));
3566
3149
  const filteredViolations = violations.filter((p) => !shouldIgnore(p));
3567
3150
  // Step D: The Block (only report scope violations for non-ignored files)
3568
3151
  if (filteredViolations.length > 0) {
3569
3152
  const criticalScopeViolations = expediteModeEnabled
3570
- ? filteredViolations.filter((file) => isCriticalScopeBreach(file, 'File modified outside the plan'))
3153
+ ? filteredViolations.filter((file) => (0, verify_output_1.isCriticalScopeBreach)(file, 'File modified outside the plan'))
3571
3154
  : filteredViolations;
3572
3155
  const expediteScopeViolations = expediteModeEnabled
3573
3156
  ? filteredViolations.filter((file) => !criticalScopeViolations.includes(file))
@@ -3686,7 +3269,7 @@ async function verifyCommand(options) {
3686
3269
  }
3687
3270
  }
3688
3271
  if (governanceResult) {
3689
- displayGovernanceInsights(governanceResult, { explain: options.explain });
3272
+ (0, verify_render_1.displayGovernanceInsights)(chalk, governanceResult, { explain: options.explain });
3690
3273
  }
3691
3274
  // ── Intent Status in scope-violation path ──────────────────────
3692
3275
  if (intentEngineSummary) {
@@ -3755,7 +3338,7 @@ async function verifyCommand(options) {
3755
3338
  console.log(chalk.yellow(` • ${file}`));
3756
3339
  });
3757
3340
  console.log(chalk.dim(' Follow-up checklist:'));
3758
- EXPEDITE_FOLLOW_UP_CHECKLIST.forEach((item) => {
3341
+ verify_output_1.EXPEDITE_FOLLOW_UP_CHECKLIST.forEach((item) => {
3759
3342
  console.log(chalk.dim(` - ${item}`));
3760
3343
  });
3761
3344
  console.log(chalk.dim(' Note: Expedite Mode used\n'));
@@ -4056,10 +3639,14 @@ async function verifyCommand(options) {
4056
3639
  message: `Policy audit chain is invalid: ${auditIntegrityStatus.issues.join('; ') || 'unknown issue'}`,
4057
3640
  });
4058
3641
  }
3642
+ if (governanceResult?.driftIntelligence) {
3643
+ const driftViolations = driftFindingsToVerificationViolations(governanceResult.driftIntelligence);
3644
+ policyViolations.push(...driftViolations.filter((item) => !shouldIgnore(item.file)));
3645
+ }
4059
3646
  // Structural violations are passed to the canonical pipeline via payload.structuralViolations
4060
3647
  // (see line ~5281). Do NOT merge them into policyViolations — that would create structural:*
4061
3648
  // duplicates that contaminate the canonical finding graph.
4062
- policyDecision = resolvePolicyDecisionFromViolations(policyViolations);
3649
+ policyDecision = (0, policy_decision_1.resolvePolicyDecisionFromViolations)(policyViolations);
4063
3650
  const policyExceptionsSummary = {
4064
3651
  sourceMode: policyExceptionResolution.mode,
4065
3652
  sourceWarning: policyExceptionResolution.warning,
@@ -4201,7 +3788,7 @@ async function verifyCommand(options) {
4201
3788
  message: violation,
4202
3789
  }));
4203
3790
  policyViolations.push(...intentProofPolicyViolations);
4204
- policyDecision = resolvePolicyDecisionFromViolations(policyViolations);
3791
+ policyDecision = (0, policy_decision_1.resolvePolicyDecisionFromViolations)(policyViolations);
4205
3792
  }
4206
3793
  if (!options.json) {
4207
3794
  if (intentProofSummary.pass) {
@@ -4280,7 +3867,7 @@ async function verifyCommand(options) {
4280
3867
  });
4281
3868
  }
4282
3869
  else {
4283
- displayChangeContractDrift(changeContractSummary, { advisory: false });
3870
+ (0, verify_render_1.displayChangeContractDrift)(chalk, changeContractSummary, { advisory: false });
4284
3871
  }
4285
3872
  await recordVerificationIfRequested(options, config, {
4286
3873
  grade: 'F',
@@ -4304,7 +3891,7 @@ async function verifyCommand(options) {
4304
3891
  exitWithEvidence(2);
4305
3892
  }
4306
3893
  else if (!changeContractEvaluation.valid && !options.json) {
4307
- displayChangeContractDrift(changeContractSummary, { advisory: true });
3894
+ (0, verify_render_1.displayChangeContractDrift)(chalk, changeContractSummary, { advisory: true });
4308
3895
  }
4309
3896
  }
4310
3897
  // Call verify API (or deterministic local evaluation for Plan Sync scope mode)
@@ -4538,9 +4125,50 @@ async function verifyCommand(options) {
4538
4125
  : {}),
4539
4126
  };
4540
4127
  captureEvidencePayload(verifyEvidencePayload);
4128
+ const canonicalReplayChecksum = (() => {
4129
+ const direct = lastCanonicalOutput?.['replayChecksum'];
4130
+ if (typeof direct === 'string') {
4131
+ return direct;
4132
+ }
4133
+ const envelope = lastCanonicalOutput?.['governanceVerification'] ?? undefined;
4134
+ return typeof envelope?.replayChecksum === 'string' ? envelope.replayChecksum : null;
4135
+ })();
4136
+ const structuralBlocking = structuralViolations.filter(v => v.severity === 'BLOCKING').length;
4137
+ const structuralAdvisory = structuralViolations.filter(v => v.severity === 'ADVISORY').length;
4138
+ const deterministicSigs = structuralViolations.filter(v => v.determinism === 'deterministic-structural').length;
4139
+ const heuristicSigs = structuralViolations.filter(v => v.determinism === 'heuristic-advisory').length;
4140
+ const trustScore = structuralViolations.length > 0
4141
+ ? Math.round((deterministicSigs / structuralViolations.length) * 100)
4142
+ : 100;
4143
+ const mainReplayCustody = (0, replay_custody_1.captureVerifyReplayCustody)({
4144
+ projectRoot,
4145
+ diffContext: `${options.base || 'HEAD'} vs working tree`,
4146
+ filesAnalyzed: diffFiles.length,
4147
+ planId: finalPlanId || null,
4148
+ verificationSource: verifySource,
4149
+ policyLockFingerprint: (0, policy_packs_1.readPolicyLockFile)(projectRoot).lock?.effective.fingerprint || null,
4150
+ compiledPolicyFingerprint: effectiveCompiledPolicy?.fingerprint || null,
4151
+ ruleIds: structuralRulesApplied,
4152
+ blockingCount: policyViolations.filter((v) => v.severity === 'block').length + structuralBlocking,
4153
+ advisoryCount: policyViolations.filter((v) => v.severity !== 'block').length + structuralAdvisory,
4154
+ suppressedCount: structuralSuppressedCount,
4155
+ structuralBlockingCount: structuralBlocking,
4156
+ structuralAdvisoryCount: structuralAdvisory,
4157
+ deterministicSignals: deterministicSigs,
4158
+ heuristicSignals: heuristicSigs,
4159
+ overallTrustScore: trustScore,
4160
+ verdict: effectiveVerdict,
4161
+ governanceDecision: governanceResult?.governanceDecision?.summary || 'automatic',
4162
+ actor: custodyActor,
4163
+ source: custodySource,
4164
+ replayChecksum: canonicalReplayChecksum,
4165
+ });
4166
+ applyCapturedReplayCustody(lastCanonicalOutput, mainReplayCustody);
4541
4167
  // If JSON output requested, output JSON and exit
4542
4168
  if (options.json) {
4543
- emitVerifyJson(verifyEvidencePayload);
4169
+ emitVerifyJson(verifyEvidencePayload, (canonical) => {
4170
+ applyCapturedReplayCustody(canonical, mainReplayCustody);
4171
+ });
4544
4172
  await recordVerificationIfRequested(options, config, {
4545
4173
  grade,
4546
4174
  violations: violations,
@@ -4574,7 +4202,7 @@ async function verifyCommand(options) {
4574
4202
  // Display results (only if not in json mode; exclude ignored paths from bloat)
4575
4203
  if (!options.json) {
4576
4204
  const displayBloatFiles = (verifyResult.bloatFiles || []).filter((f) => !shouldIgnore(f));
4577
- displayVerifyResults({
4205
+ (0, verify_render_1.displayVerifyResults)(chalk, {
4578
4206
  ...verifyResult,
4579
4207
  verdict: effectiveVerdict,
4580
4208
  message: effectiveMessage,
@@ -4582,7 +4210,7 @@ async function verifyCommand(options) {
4582
4210
  bloatCount: displayBloatFiles.length,
4583
4211
  }, policyViolations, expediteModeEnabled, intentEngineIssues, intentEngineSummary, intentEngineFlowIssues, intentEngineRegressions, structuralViolations);
4584
4212
  if (governanceResult) {
4585
- displayGovernanceInsights(governanceResult, { explain: options.explain });
4213
+ (0, verify_render_1.displayGovernanceInsights)(chalk, governanceResult, { explain: options.explain });
4586
4214
  }
4587
4215
  if (aiDebtSummary.mode !== 'off') {
4588
4216
  const header = aiDebtSummary.mode === 'enforce'
@@ -4656,34 +4284,7 @@ async function verifyCommand(options) {
4656
4284
  // ── Governance Provenance Chain + Pilot Metrics ───────────────────────
4657
4285
  // Best-effort: never throws, never changes the verification outcome.
4658
4286
  try {
4659
- const structuralBlocking = structuralViolations.filter(v => v.severity === 'BLOCKING').length;
4660
- const structuralAdvisory = structuralViolations.filter(v => v.severity === 'ADVISORY').length;
4661
- const deterministicSigs = structuralViolations.filter(v => v.determinism === 'deterministic-structural').length;
4662
- const heuristicSigs = structuralViolations.filter(v => v.determinism === 'heuristic-advisory').length;
4663
- const trustScore = structuralViolations.length > 0
4664
- ? Math.round((deterministicSigs / structuralViolations.length) * 100)
4665
- : 100;
4666
- const prov = (0, governance_provenance_1.buildProvenanceRecord)({
4667
- repoRoot: projectRoot,
4668
- filesAnalyzed: diffFiles.length,
4669
- diffContext: `${options.base || 'HEAD'} vs working tree`,
4670
- planId: finalPlanId || null,
4671
- intentHash: null,
4672
- policyHash: null,
4673
- ruleIds: structuralRulesApplied,
4674
- blockingCount: policyViolations.filter((v) => v.severity === 'block').length + structuralBlocking,
4675
- advisoryCount: policyViolations.filter((v) => v.severity !== 'block').length + structuralAdvisory,
4676
- suppressedCount: structuralSuppressedCount,
4677
- structuralBlockingCount: structuralBlocking,
4678
- structuralAdvisoryCount: structuralAdvisory,
4679
- deterministicSignals: deterministicSigs,
4680
- heuristicSignals: heuristicSigs,
4681
- overallTrustScore: trustScore,
4682
- verdict: effectiveVerdict,
4683
- governanceDecision: governanceResult?.governanceDecision?.summary || 'automatic',
4684
- });
4685
- (0, governance_provenance_1.saveProvenanceRecord)(projectRoot, prov);
4686
- lastProvenanceRunId = prov.runId;
4287
+ lastProvenanceRunId = mainReplayCustody.provenanceRecord?.runId ?? lastProvenanceRunId;
4687
4288
  // Tally per-rule counts for pilot metrics
4688
4289
  const ruleCounts = {};
4689
4290
  for (const v of structuralViolations) {
@@ -4694,8 +4295,8 @@ async function verifyCommand(options) {
4694
4295
  verifyCount: 1,
4695
4296
  passCount: effectiveVerdict === 'PASS' ? 1 : 0,
4696
4297
  failCount: effectiveVerdict === 'FAIL' ? 1 : 0,
4697
- blockingCaught: prov.blockingCount,
4698
- advisoryCaught: prov.advisoryCount,
4298
+ blockingCaught: mainReplayCustody.provenanceRecord?.blockingCount ?? (policyViolations.filter((v) => v.severity === 'block').length + structuralBlocking),
4299
+ advisoryCaught: mainReplayCustody.provenanceRecord?.advisoryCount ?? (policyViolations.filter((v) => v.severity !== 'block').length + structuralAdvisory),
4699
4300
  suppressions: structuralSuppressedCount,
4700
4301
  structuralCaught: structuralViolations.length,
4701
4302
  aiDebtDelta: aiDebtSummary?.score ?? 0,
@@ -4817,7 +4418,7 @@ async function verifyCommand(options) {
4817
4418
  catch (error) {
4818
4419
  if (options.json) {
4819
4420
  const errorMessage = error instanceof Error ? error.message : 'Unknown error';
4820
- emitCanonicalVerifyJson({
4421
+ (0, verify_output_1.emitCanonicalVerifyJson)({
4821
4422
  verdict: 'FAIL',
4822
4423
  summary: {
4823
4424
  totalFilesChanged: 0,
@@ -5068,6 +4669,8 @@ function buildGovernancePayload(governance, orgGovernanceSettings, options) {
5068
4669
  suspiciousChange: governance.suspiciousChange,
5069
4670
  changeJustification: governance.changeJustification,
5070
4671
  governanceDecision: governance.governanceDecision,
4672
+ engineeringContext: governance.engineeringContext,
4673
+ driftIntelligence: governance.driftIntelligence,
5071
4674
  aiChangeLog: {
5072
4675
  path: governance.aiChangeLogPath,
5073
4676
  auditPath: governance.aiChangeLogAuditPath,
@@ -5090,412 +4693,6 @@ function buildGovernancePayload(governance, orgGovernanceSettings, options) {
5090
4693
  ...(options?.aiDebt ? { aiDebt: options.aiDebt } : {}),
5091
4694
  };
5092
4695
  }
5093
- function displayGovernanceInsights(governance, options = {}) {
5094
- const maxUnexpectedFiles = options.maxUnexpectedFiles ?? 20;
5095
- const decision = governance.governanceDecision;
5096
- console.log(chalk.bold.white('\nBlast Radius:'));
5097
- console.log(chalk.dim(` Files touched: ${governance.blastRadius.filesChanged}`));
5098
- console.log(chalk.dim(` Functions impacted: ${governance.blastRadius.functionsAffected}`));
5099
- console.log(chalk.dim(` Modules impacted: ${governance.blastRadius.modulesAffected.join(', ') || 'none'}`));
5100
- if (governance.blastRadius.dependenciesAdded.length > 0) {
5101
- console.log(chalk.dim(` Dependencies added: ${governance.blastRadius.dependenciesAdded.join(', ')}`));
5102
- }
5103
- console.log(chalk.dim(` Risk level: ${governance.blastRadius.riskScore.toUpperCase()}`));
5104
- console.log(chalk.dim(` Governance decision: ${decision.decision.toUpperCase().replace('_', ' ')} | Avg relevance: ${decision.averageRelevanceScore}`));
5105
- console.log(chalk.dim(` Policy source: ${governance.policySources.mode}${governance.policySources.orgPolicy ? ' (org + local)' : ' (local)'}`));
5106
- console.log(governance.aiChangeLogIntegrity.valid
5107
- ? chalk.dim(` AI change-log integrity: valid (${governance.aiChangeLogIntegrity.signed ? 'signed' : 'unsigned'})`)
5108
- : chalk.red(` AI change-log integrity: invalid (${governance.aiChangeLogIntegrity.issues.join('; ') || 'unknown'})`));
5109
- if (governance.aiChangeLogIntegrity.signed) {
5110
- const keyId = typeof governance.aiChangeLogIntegrity.keyId === 'string'
5111
- ? governance.aiChangeLogIntegrity.keyId
5112
- : null;
5113
- const verifiedWithKeyId = typeof governance.aiChangeLogIntegrity.verifiedWithKeyId === 'string'
5114
- ? governance.aiChangeLogIntegrity.verifiedWithKeyId
5115
- : null;
5116
- if (keyId || verifiedWithKeyId) {
5117
- console.log(chalk.dim(` Signing key: ${keyId || 'n/a'}${verifiedWithKeyId ? ` (verified via ${verifiedWithKeyId})` : ''}`));
5118
- }
5119
- }
5120
- if (governance.suspiciousChange.flagged) {
5121
- console.log(chalk.red('\nSuspicious Change Detected'));
5122
- console.log(chalk.red(` Plan expected files: ${governance.suspiciousChange.expectedFiles} | AI modified files: ${governance.suspiciousChange.actualFiles}`));
5123
- governance.suspiciousChange.unexpectedFiles.slice(0, maxUnexpectedFiles).forEach((filePath) => {
5124
- console.log(chalk.red(` • ${filePath}`));
5125
- });
5126
- console.log(chalk.red(` Confidence: ${governance.suspiciousChange.confidence}`));
5127
- }
5128
- if (decision.lowRelevanceFiles.length > 0) {
5129
- console.log(chalk.yellow('\nLow Relevance Files'));
5130
- decision.lowRelevanceFiles.slice(0, 10).forEach((item) => {
5131
- console.log(chalk.yellow(` • ${item.file} (score ${item.relevanceScore}, ${item.planLink.replace('_', ' ')})`));
5132
- });
5133
- }
5134
- if (options.explain) {
5135
- console.log(chalk.bold.white('\nAI Change Justification:'));
5136
- console.log(chalk.dim(` Task: ${governance.changeJustification.task}`));
5137
- governance.changeJustification.changes.forEach((item) => {
5138
- const relevance = typeof item.relevanceScore === 'number' ? ` [score ${item.relevanceScore}]` : '';
5139
- console.log(chalk.dim(` • ${item.file} — ${item.reason}${relevance}`));
5140
- });
5141
- }
5142
- }
5143
- function displayChangeContractDrift(summary, options = { advisory: false }) {
5144
- const groups = (0, change_contract_1.groupChangeContractViolations)(summary.violations.map((item) => ({
5145
- code: item.code,
5146
- message: item.message,
5147
- ...(item.file ? { file: item.file } : {}),
5148
- ...(item.symbol ? { symbol: item.symbol } : {}),
5149
- ...(item.symbolType ? { symbolType: item.symbolType } : {}),
5150
- ...(item.expected ? { expected: item.expected } : {}),
5151
- ...(item.actual ? { actual: item.actual } : {}),
5152
- })));
5153
- if (groups.length === 0)
5154
- return;
5155
- const maxItemsPerGroup = options.maxItemsPerGroup ?? 12;
5156
- const header = options.advisory
5157
- ? chalk.yellow('\nWARN ⚠️ Change contract drift detected')
5158
- : chalk.red('\nFAIL ❌ Change contract enforcement failed');
5159
- console.log(header);
5160
- for (const group of groups) {
5161
- console.log(chalk.white(`\n${group.title}:`));
5162
- group.items.slice(0, maxItemsPerGroup).forEach((entry) => {
5163
- console.log(` - ${entry}`);
5164
- });
5165
- if (group.items.length > maxItemsPerGroup) {
5166
- console.log(chalk.dim(` - ... ${group.items.length - maxItemsPerGroup} more`));
5167
- }
5168
- console.log(chalk.dim(` Why it matters: ${group.impact}`));
5169
- }
5170
- console.log(chalk.dim('\nSummary:'));
5171
- console.log(chalk.dim('Implementation deviates from intended contract.'));
5172
- console.log(chalk.dim(`Contract path: ${summary.path}`));
5173
- }
5174
- /**
5175
- * Display verification results in a formatted report card
5176
- */
5177
- function displayVerifyResults(result, policyViolations, expediteModeUsed = false, intentIssuesForDisplay = [], intentSummaryForDisplay = null, flowIssuesForDisplay = [], regressionsForDisplay = [], structuralViolationsForDisplay = []) {
5178
- // ── Header ────────────────────────────────────────────────────────────────
5179
- const headerLabel = result.verdict === 'PASS'
5180
- ? chalk.bold.green('\n✅ VERIFICATION PASSED')
5181
- : result.verdict === 'WARN'
5182
- ? chalk.bold.yellow('\n⚠️ VERIFICATION PASSED WITH WARNINGS')
5183
- : chalk.bold.red('\n❌ VERIFICATION FAILED');
5184
- console.log(headerLabel);
5185
- // ── Intent Status block ──────────────────────────────────────────────────
5186
- if (intentSummaryForDisplay) {
5187
- const s = intentSummaryForDisplay;
5188
- const domainLabel = s.domain.charAt(0).toUpperCase() + s.domain.slice(1);
5189
- const confColor = s.confidence === 'HIGH'
5190
- ? chalk.green
5191
- : s.confidence === 'MEDIUM'
5192
- ? chalk.yellow
5193
- : chalk.red;
5194
- // V4: weighted coverage bar
5195
- const wCovPct = s.weightedCoverage != null
5196
- ? Math.round(s.weightedCoverage * 100)
5197
- : s.coveragePct;
5198
- const barWidth = 20;
5199
- const filled = Math.round((wCovPct / 100) * barWidth);
5200
- const bar = chalk.cyan('█'.repeat(filled)) + chalk.dim('░'.repeat(barWidth - filled));
5201
- // V4: system status label
5202
- const sysStatus = s.status;
5203
- const statusLabel = sysStatus === 'CRITICAL'
5204
- ? chalk.bold.red('[CRITICAL]')
5205
- : sysStatus === 'AT RISK'
5206
- ? chalk.bold.yellow('[AT RISK]')
5207
- : chalk.bold.green('[SECURE]');
5208
- console.log(chalk.bold('\n━━━ INTENT STATUS ━━━━━━━━━━━━━━━━━━━━━━'));
5209
- console.log(` ${statusLabel} ${chalk.bold(`${domainLabel} Implementation:`)} ${bar} ${chalk.bold(`${wCovPct}%`)} (weighted)`);
5210
- console.log(` Confidence: ${confColor(s.confidence)}`);
5211
- if (s.foundList.length > 0) {
5212
- const foundLabels = s.foundList
5213
- .map((k) => k.split('-').map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(' '))
5214
- .slice(0, 4);
5215
- console.log(` Found: ${chalk.green(foundLabels.join(', '))}${s.foundList.length > 4 ? chalk.dim(` +${s.foundList.length - 4} more`) : ''}`);
5216
- }
5217
- // V4: show critical missing and non-critical missing separately
5218
- const critMissing = s.criticalMissing ?? [];
5219
- const otherMissing = s.missing.filter((k) => !critMissing.includes(k));
5220
- if (critMissing.length > 0) {
5221
- console.log(` ${chalk.bold.red('Critical missing:')}`);
5222
- critMissing.forEach((k) => {
5223
- const label = k.split('-').map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(' ');
5224
- console.log(chalk.red(` ✗ ${label}`));
5225
- });
5226
- }
5227
- if (otherMissing.length > 0) {
5228
- console.log(` ${chalk.bold.yellow('Missing:')}`);
5229
- otherMissing.forEach((k) => {
5230
- const label = k.split('-').map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(' ');
5231
- console.log(chalk.yellow(` • ${label}`));
5232
- });
5233
- }
5234
- if (critMissing.length === 0 && otherMissing.length === 0) {
5235
- console.log(` Missing: ${chalk.green('none — all components detected')}`);
5236
- }
5237
- console.log(chalk.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
5238
- }
5239
- // ── Triage items ──────────────────────────────────────────────────────────
5240
- const maxBlockingItems = 20;
5241
- const maxAdvisoryItems = 8;
5242
- const maxExpediteItems = 12;
5243
- const policyItems = policyViolations || [];
5244
- const isBlockingSeverity = (severityRaw) => {
5245
- const normalized = String(severityRaw || '').toLowerCase();
5246
- return normalized === 'block' || normalized === 'critical' || normalized === 'high';
5247
- };
5248
- const scopeItems = result.bloatFiles.map((file) => ({
5249
- file,
5250
- message: 'File modified outside intended scope',
5251
- policy: 'scope_guard',
5252
- }));
5253
- const policyTriageItems = policyItems.map((item) => ({
5254
- file: item.file,
5255
- message: item.message || item.rule,
5256
- policy: item.rule || 'policy_violation',
5257
- severity: item.severity,
5258
- }));
5259
- // Structural rule violations — split by severity
5260
- const structuralBlocking = structuralViolationsForDisplay
5261
- .filter((v) => v.severity === 'BLOCKING')
5262
- .map((v) => ({
5263
- file: v.filePath,
5264
- message: `${v.ruleId} · ${v.ruleName} (line ${v.line}) — ${v.operationalRisk}`,
5265
- }));
5266
- const structuralAdvisory = structuralViolationsForDisplay
5267
- .filter((v) => v.severity === 'ADVISORY')
5268
- .map((v) => ({
5269
- file: v.filePath,
5270
- message: `${v.ruleId} · ${v.ruleName} (line ${v.line}) — ${v.operationalRisk}`,
5271
- }));
5272
- let blockingItems = [
5273
- ...scopeItems.map((item) => ({
5274
- file: item.file,
5275
- message: item.message,
5276
- })),
5277
- ...policyTriageItems
5278
- .filter((item) => isBlockingSeverity(item.severity))
5279
- .map((item) => ({
5280
- file: item.file,
5281
- message: item.message,
5282
- })),
5283
- ...structuralBlocking,
5284
- ];
5285
- let advisoryItems = [
5286
- ...policyTriageItems
5287
- .filter((item) => !isBlockingSeverity(item.severity))
5288
- .map((item) => ({
5289
- file: item.file,
5290
- message: item.message,
5291
- })),
5292
- ...structuralAdvisory,
5293
- ];
5294
- let expediteItems = [];
5295
- if (expediteModeUsed) {
5296
- blockingItems = [
5297
- ...scopeItems
5298
- .filter((item) => isCriticalScopeBreach(item.file, item.message))
5299
- .map((item) => ({ file: item.file, message: item.message })),
5300
- ...policyTriageItems
5301
- .filter((item) => isSecurityOrAuthViolation(item.file, item.policy, item.message))
5302
- .map((item) => ({ file: item.file, message: item.message })),
5303
- ];
5304
- expediteItems = [
5305
- ...scopeItems
5306
- .filter((item) => !isCriticalScopeBreach(item.file, item.message))
5307
- .map((item) => ({ file: item.file, message: item.message })),
5308
- ...policyTriageItems
5309
- .filter((item) => !isSecurityOrAuthViolation(item.file, item.policy, item.message))
5310
- .map((item) => ({ file: item.file, message: item.message })),
5311
- ];
5312
- advisoryItems = [];
5313
- }
5314
- // ── Counts ────────────────────────────────────────────────────────────────
5315
- console.log(blockingItems.length > 0
5316
- ? chalk.red(`Blocking Issues: ${blockingItems.length}`)
5317
- : chalk.dim('Blocking Issues: 0'));
5318
- if (expediteModeUsed) {
5319
- console.log(chalk.yellow(`Expedite Issues: ${expediteItems.length}`));
5320
- }
5321
- else {
5322
- console.log(advisoryItems.length > 0
5323
- ? chalk.yellow(`Advisory Issues: ${advisoryItems.length}`)
5324
- : chalk.dim('Advisory Issues: 0'));
5325
- }
5326
- console.log(chalk.dim(`Plan adherence: ${result.plannedFilesModified}/${result.totalPlannedFiles} files (${result.adherenceScore}%)`));
5327
- // ── Top issues ────────────────────────────────────────────────────────────
5328
- const topIssues = [
5329
- ...blockingItems,
5330
- ...(expediteModeUsed ? expediteItems : advisoryItems),
5331
- ].slice(0, 2);
5332
- if (topIssues.length > 0) {
5333
- console.log(chalk.bold('\nTop Issues:'));
5334
- topIssues.forEach((item, i) => {
5335
- console.log(` ${i + 1}. ${item.message} → ${chalk.cyan(item.file)}`);
5336
- });
5337
- }
5338
- // ── Detailed lists ────────────────────────────────────────────────────────
5339
- if (blockingItems.length > 0) {
5340
- console.log(chalk.red(`\nBLOCKING (${blockingItems.length})`));
5341
- blockingItems.slice(0, maxBlockingItems).forEach((item) => {
5342
- console.log(` - ${item.file}: ${item.message}`);
5343
- });
5344
- if (blockingItems.length > maxBlockingItems) {
5345
- console.log(chalk.dim(` - ... ${blockingItems.length - maxBlockingItems} more`));
5346
- }
5347
- }
5348
- if (advisoryItems.length > 0) {
5349
- console.log(chalk.yellow(`\nADVISORY (${advisoryItems.length})`));
5350
- advisoryItems.slice(0, maxAdvisoryItems).forEach((item) => {
5351
- console.log(` - ${item.file}: ${item.message}`);
5352
- });
5353
- if (advisoryItems.length > maxAdvisoryItems) {
5354
- console.log(chalk.dim(` - ... ${advisoryItems.length - maxAdvisoryItems} more (summarized)`));
5355
- }
5356
- }
5357
- if (expediteModeUsed && expediteItems.length > 0) {
5358
- console.log(chalk.yellow(`\nEXPEDITE (requires follow-up) (${expediteItems.length})`));
5359
- expediteItems.slice(0, maxExpediteItems).forEach((item) => {
5360
- console.log(` - ${item.file}: ${item.message}`);
5361
- });
5362
- if (expediteItems.length > maxExpediteItems) {
5363
- console.log(chalk.dim(` - ... ${expediteItems.length - maxExpediteItems} more (summarized)`));
5364
- }
5365
- console.log(chalk.dim(' Follow-up checklist:'));
5366
- EXPEDITE_FOLLOW_UP_CHECKLIST.forEach((checkItem) => {
5367
- console.log(chalk.dim(` - ${checkItem}`));
5368
- });
5369
- console.log(chalk.dim(' Note: Expedite Mode used'));
5370
- }
5371
- // ── Intent issues ─────────────────────────────────────────────────────────
5372
- if (intentIssuesForDisplay.length > 0) {
5373
- console.log(chalk.magenta(`\nINTENT ISSUES (${intentIssuesForDisplay.length})`));
5374
- intentIssuesForDisplay.forEach((issue) => {
5375
- const label = issue.severity === 'high' ? chalk.red('[HIGH]') : chalk.yellow('[MEDIUM]');
5376
- const typeLabel = issue.type === 'missing' ? 'Missing' : issue.type === 'misplaced' ? 'Misplaced' : 'Partial';
5377
- console.log(` ${label} ${typeLabel}: ${issue.message}`);
5378
- });
5379
- }
5380
- // ── Flow Validation ───────────────────────────────────────────────────────
5381
- if (flowIssuesForDisplay.length > 0) {
5382
- console.log(chalk.bold('\n━━━ FLOW VALIDATION ━━━━━━━━━━━━━━━━━━━━━'));
5383
- flowIssuesForDisplay.forEach((issue) => {
5384
- const label = issue.severity === 'high' ? chalk.red('[HIGH]') : chalk.yellow('[MEDIUM]');
5385
- const typeIcon = issue.type === 'missing-flow' ? '⛓' : issue.type === 'misplaced-flow' ? '⚠' : '⊘';
5386
- console.log(` ${label} ${typeIcon} ${issue.message}`);
5387
- if (issue.files && issue.files.length > 0) {
5388
- const display = issue.files.slice(0, 3);
5389
- console.log(chalk.dim(` → ${display.join(', ')}${issue.files.length > 3 ? ` +${issue.files.length - 3} more` : ''}`));
5390
- }
5391
- });
5392
- console.log(chalk.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
5393
- }
5394
- // ── Regression Analysis ───────────────────────────────────────────────────
5395
- if (regressionsForDisplay.length > 0) {
5396
- console.log(chalk.bold.red('\n━━━ REGRESSION ANALYSIS ━━━━━━━━━━━━━━━━━'));
5397
- regressionsForDisplay.forEach((reg) => {
5398
- const icon = reg.type === 'coverage-regression' ? '📉' :
5399
- reg.type === 'critical-regression' ? '🔴' :
5400
- reg.type === 'flow-regression' ? '⛓' : '⚠';
5401
- console.log(` ${chalk.red('[REGRESSION]')} ${icon} ${reg.message}`);
5402
- });
5403
- console.log(chalk.bold.red('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
5404
- }
5405
- // ── Structural Rule Engine ─────────────────────────────────────────────────
5406
- // Display detailed structural violations with AST evidence and determinism labels.
5407
- if (structuralViolationsForDisplay.length > 0) {
5408
- try {
5409
- const report = (0, explainability_1.buildViolationReport)(structuralViolationsForDisplay, '');
5410
- const formatter = new explainability_1.ViolationFormatter();
5411
- const blocking = report.blocking;
5412
- const advisory = report.advisory;
5413
- if (blocking.length > 0 || advisory.length > 0) {
5414
- console.log(chalk.bold('\n━━━ STRUCTURAL ANALYSIS ━━━━━━━━━━━━━━━━━'));
5415
- blocking.slice(0, 5).forEach((v) => {
5416
- const detLabel = v.determinism === 'deterministic-structural'
5417
- ? chalk.cyan('⚙ AST-verified')
5418
- : chalk.yellow('⚡ heuristic');
5419
- console.log(chalk.red(`\n ● ${v.ruleId} [BLOCKING] ${detLabel} · confidence ${Math.round(v.confidence * 100)}%`));
5420
- console.log(chalk.bold(` ${v.filePath}:${v.line}`));
5421
- console.log(chalk.dim(` Pattern: ${v.evidence.matchReason}`));
5422
- if (v.evidence.codeSnippet) {
5423
- console.log(chalk.dim(` Code: ${v.evidence.codeSnippet.slice(0, 100)}`));
5424
- }
5425
- console.log(chalk.yellow(` Risk: ${v.operationalRisk}`));
5426
- console.log(chalk.green(` Fix: ${v.remediation}`));
5427
- });
5428
- if (blocking.length > 5) {
5429
- console.log(chalk.dim(`\n ... ${blocking.length - 5} more blocking structural violations`));
5430
- }
5431
- advisory.slice(0, 3).forEach((v) => {
5432
- console.log(chalk.yellow(`\n ○ ${v.ruleId} [ADVISORY] ⚡ heuristic · confidence ${Math.round(v.confidence * 100)}%`));
5433
- console.log(chalk.dim(` ${v.filePath}:${v.line} — ${v.operationalRisk}`));
5434
- });
5435
- if (advisory.length > 3) {
5436
- console.log(chalk.dim(` ... ${advisory.length - 3} more advisory structural violations`));
5437
- }
5438
- const deterministicCount = report.deterministicCount;
5439
- const heuristicCount = report.heuristicCount;
5440
- console.log(chalk.dim(`\n Determinism: ${deterministicCount} AST-verified · ${heuristicCount} heuristic`));
5441
- console.log(chalk.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
5442
- }
5443
- }
5444
- catch {
5445
- // Non-fatal: explainability rendering must never break verification display
5446
- }
5447
- }
5448
- const hasAnyIssue = blockingItems.length > 0 ||
5449
- advisoryItems.length > 0 ||
5450
- expediteItems.length > 0 ||
5451
- intentIssuesForDisplay.length > 0 ||
5452
- flowIssuesForDisplay.length > 0 ||
5453
- regressionsForDisplay.length > 0;
5454
- if (!hasAnyIssue) {
5455
- console.log(chalk.green('\nNo issues detected.'));
5456
- }
5457
- // ── Next step ─────────────────────────────────────────────────────────────
5458
- if (hasAnyIssue) {
5459
- console.log(chalk.bold('\nNext step:'));
5460
- console.log(` ${chalk.cyan('neurcode fix')}`);
5461
- console.log(chalk.dim(' or: neurcode fix --apply-safe (auto-apply high-confidence patches)'));
5462
- }
5463
- console.log(chalk.dim(`\nDetails: ${result.message}\n`));
5464
- }
5465
- function printFirstRunAdvisoryMessage(demoMode) {
5466
- console.log(chalk.cyan('\nNeurcode first-run advisory mode'));
5467
- console.log(chalk.dim('Neurcode checks if your AI-generated code matches your intended plan.'));
5468
- console.log(chalk.dim('To get full enforcement:'));
5469
- console.log(chalk.dim('1. Define a plan'));
5470
- console.log(chalk.dim('2. Generate a contract'));
5471
- console.log(chalk.dim('Running in advisory mode for now.\n'));
5472
- if (demoMode) {
5473
- console.log(chalk.dim('Demo mode: this run is intentionally non-blocking to make evaluation easy.'));
5474
- }
5475
- }
5476
- function printAdvisorySignals(signals, demoMode) {
5477
- if (signals.length === 0) {
5478
- if (demoMode) {
5479
- console.log(chalk.dim('No high-signal advisory findings detected for this diff.'));
5480
- }
5481
- return;
5482
- }
5483
- console.log(chalk.yellow('\nAdvisory findings (non-blocking):'));
5484
- for (const signal of signals) {
5485
- const severityLabel = signal.severity === 'warn' ? chalk.yellow('[warn]') : chalk.dim('[info]');
5486
- console.log(`${severityLabel} ${signal.title}`);
5487
- console.log(chalk.dim(` ${signal.detail}`));
5488
- console.log(chalk.dim(` Confidence: ${signal.confidence.toUpperCase()} (advisory-only)`));
5489
- if (signal.evidence.length > 0) {
5490
- console.log(chalk.dim(` Evidence: ${signal.evidence.join(', ')}`));
5491
- }
5492
- console.log(chalk.dim(` Structural gap: ${signal.structuralCoverageGap}`));
5493
- console.log(chalk.dim(` Uncertainty: ${signal.uncertainty}`));
5494
- signal.files.forEach((file) => {
5495
- console.log(chalk.dim(` - ${file}`));
5496
- });
5497
- }
5498
- }
5499
4696
  function buildMinimalAdvisoryContractFromDiff(diffFiles, fallbackPlanId) {
5500
4697
  const expectedFiles = [...new Set(diffFiles.map((file) => toUnixPath(file.path)).filter(Boolean))];
5501
4698
  const planFiles = expectedFiles.map((path) => {