@neurcode-ai/cli 0.9.65 → 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 (260) hide show
  1. package/dist/commands/bootstrap-policy.d.ts +29 -0
  2. package/dist/commands/bootstrap-policy.d.ts.map +1 -0
  3. package/dist/commands/bootstrap-policy.js +334 -0
  4. package/dist/commands/bootstrap-policy.js.map +1 -0
  5. package/dist/commands/doctor.d.ts.map +1 -1
  6. package/dist/commands/doctor.js +82 -0
  7. package/dist/commands/doctor.js.map +1 -1
  8. package/dist/commands/governance.d.ts +3 -0
  9. package/dist/commands/governance.d.ts.map +1 -0
  10. package/dist/commands/governance.js +390 -0
  11. package/dist/commands/governance.js.map +1 -0
  12. package/dist/commands/quickstart.d.ts +21 -0
  13. package/dist/commands/quickstart.d.ts.map +1 -0
  14. package/dist/commands/quickstart.js +178 -0
  15. package/dist/commands/quickstart.js.map +1 -0
  16. package/dist/commands/remediate-export.d.ts +36 -0
  17. package/dist/commands/remediate-export.d.ts.map +1 -0
  18. package/dist/commands/remediate-export.js +1072 -0
  19. package/dist/commands/remediate-export.js.map +1 -0
  20. package/dist/commands/replay.d.ts.map +1 -1
  21. package/dist/commands/replay.js +14 -0
  22. package/dist/commands/replay.js.map +1 -1
  23. package/dist/commands/session.d.ts +7 -0
  24. package/dist/commands/session.d.ts.map +1 -1
  25. package/dist/commands/session.js +156 -0
  26. package/dist/commands/session.js.map +1 -1
  27. package/dist/commands/start-intent.d.ts.map +1 -1
  28. package/dist/commands/start-intent.js +61 -11
  29. package/dist/commands/start-intent.js.map +1 -1
  30. package/dist/commands/verify-guidance.d.ts +5 -0
  31. package/dist/commands/verify-guidance.d.ts.map +1 -0
  32. package/dist/commands/verify-guidance.js +49 -0
  33. package/dist/commands/verify-guidance.js.map +1 -0
  34. package/dist/commands/verify-output.d.ts +37 -0
  35. package/dist/commands/verify-output.d.ts.map +1 -0
  36. package/dist/commands/verify-output.js +572 -0
  37. package/dist/commands/verify-output.js.map +1 -0
  38. package/dist/commands/verify-render.d.ts +41 -0
  39. package/dist/commands/verify-render.d.ts.map +1 -0
  40. package/dist/commands/verify-render.js +457 -0
  41. package/dist/commands/verify-render.js.map +1 -0
  42. package/dist/commands/verify.d.ts.map +1 -1
  43. package/dist/commands/verify.js +384 -1091
  44. package/dist/commands/verify.js.map +1 -1
  45. package/dist/commands/workspace.d.ts.map +1 -1
  46. package/dist/commands/workspace.js +3 -14
  47. package/dist/commands/workspace.js.map +1 -1
  48. package/dist/context-engine/graph.d.ts.map +1 -1
  49. package/dist/context-engine/graph.js +69 -7
  50. package/dist/context-engine/graph.js.map +1 -1
  51. package/dist/context-engine/scanner.d.ts.map +1 -1
  52. package/dist/context-engine/scanner.js +9 -2
  53. package/dist/context-engine/scanner.js.map +1 -1
  54. package/dist/daemon/compatibility/execution.d.ts +42 -0
  55. package/dist/daemon/compatibility/execution.d.ts.map +1 -0
  56. package/dist/daemon/compatibility/execution.js +183 -0
  57. package/dist/daemon/compatibility/execution.js.map +1 -0
  58. package/dist/daemon/compatibility/mutation.d.ts +24 -0
  59. package/dist/daemon/compatibility/mutation.d.ts.map +1 -0
  60. package/dist/daemon/compatibility/mutation.js +724 -0
  61. package/dist/daemon/compatibility/mutation.js.map +1 -0
  62. package/dist/daemon/routes.d.ts +19 -0
  63. package/dist/daemon/routes.d.ts.map +1 -0
  64. package/dist/daemon/routes.js +123 -0
  65. package/dist/daemon/routes.js.map +1 -0
  66. package/dist/daemon/runtime/execution-bus.d.ts +217 -0
  67. package/dist/daemon/runtime/execution-bus.d.ts.map +1 -0
  68. package/dist/daemon/runtime/execution-bus.js +1420 -0
  69. package/dist/daemon/runtime/execution-bus.js.map +1 -0
  70. package/dist/daemon/runtime/workspace-runtime.d.ts +280 -0
  71. package/dist/daemon/runtime/workspace-runtime.d.ts.map +1 -0
  72. package/dist/daemon/runtime/workspace-runtime.js +1473 -0
  73. package/dist/daemon/runtime/workspace-runtime.js.map +1 -0
  74. package/dist/daemon/server.d.ts.map +1 -1
  75. package/dist/daemon/server.js +171 -874
  76. package/dist/daemon/server.js.map +1 -1
  77. package/dist/daemon/shaping.d.ts +11 -0
  78. package/dist/daemon/shaping.d.ts.map +1 -0
  79. package/dist/daemon/shaping.js +240 -0
  80. package/dist/daemon/shaping.js.map +1 -0
  81. package/dist/governance/canonical-invariants.d.ts +88 -0
  82. package/dist/governance/canonical-invariants.d.ts.map +1 -0
  83. package/dist/governance/canonical-invariants.js +197 -0
  84. package/dist/governance/canonical-invariants.js.map +1 -0
  85. package/dist/governance/canonical-ordering.d.ts +76 -0
  86. package/dist/governance/canonical-ordering.d.ts.map +1 -0
  87. package/dist/governance/canonical-ordering.js +189 -0
  88. package/dist/governance/canonical-ordering.js.map +1 -0
  89. package/dist/governance/canonical-pipeline.d.ts +9 -1
  90. package/dist/governance/canonical-pipeline.d.ts.map +1 -1
  91. package/dist/governance/canonical-pipeline.js +367 -24
  92. package/dist/governance/canonical-pipeline.js.map +1 -1
  93. package/dist/governance/diff-line-provenance.d.ts +59 -0
  94. package/dist/governance/diff-line-provenance.d.ts.map +1 -0
  95. package/dist/governance/diff-line-provenance.js +118 -0
  96. package/dist/governance/diff-line-provenance.js.map +1 -0
  97. package/dist/governance/pilot-readiness.d.ts +34 -0
  98. package/dist/governance/pilot-readiness.d.ts.map +1 -0
  99. package/dist/governance/pilot-readiness.js +226 -0
  100. package/dist/governance/pilot-readiness.js.map +1 -0
  101. package/dist/governance/policy-parity-validator.d.ts +62 -0
  102. package/dist/governance/policy-parity-validator.d.ts.map +1 -0
  103. package/dist/governance/policy-parity-validator.js +137 -0
  104. package/dist/governance/policy-parity-validator.js.map +1 -0
  105. package/dist/governance/remediation-boundary.d.ts +55 -0
  106. package/dist/governance/remediation-boundary.d.ts.map +1 -0
  107. package/dist/governance/remediation-boundary.js +120 -0
  108. package/dist/governance/remediation-boundary.js.map +1 -0
  109. package/dist/governance/structural-cache.d.ts +103 -0
  110. package/dist/governance/structural-cache.d.ts.map +1 -0
  111. package/dist/governance/structural-cache.js +235 -0
  112. package/dist/governance/structural-cache.js.map +1 -0
  113. package/dist/governance/structural-on-diff.d.ts +22 -2
  114. package/dist/governance/structural-on-diff.d.ts.map +1 -1
  115. package/dist/governance/structural-on-diff.js +36 -4
  116. package/dist/governance/structural-on-diff.js.map +1 -1
  117. package/dist/governance/structural-policy-merge.d.ts +8 -0
  118. package/dist/governance/structural-policy-merge.d.ts.map +1 -1
  119. package/dist/governance/structural-policy-merge.js +7 -0
  120. package/dist/governance/structural-policy-merge.js.map +1 -1
  121. package/dist/governance/verify-runtime-guard.d.ts +99 -0
  122. package/dist/governance/verify-runtime-guard.d.ts.map +1 -0
  123. package/dist/governance/verify-runtime-guard.js +129 -0
  124. package/dist/governance/verify-runtime-guard.js.map +1 -0
  125. package/dist/index.js +277 -77
  126. package/dist/index.js.map +1 -1
  127. package/dist/intent-engine/repo-classifier.d.ts +64 -0
  128. package/dist/intent-engine/repo-classifier.d.ts.map +1 -0
  129. package/dist/intent-engine/repo-classifier.js +178 -0
  130. package/dist/intent-engine/repo-classifier.js.map +1 -0
  131. package/dist/structural-rules/index.d.ts +4 -0
  132. package/dist/structural-rules/index.d.ts.map +1 -1
  133. package/dist/structural-rules/index.js +18 -1
  134. package/dist/structural-rules/index.js.map +1 -1
  135. package/dist/structural-rules/python/PY003-broad-except-clause.d.ts +21 -0
  136. package/dist/structural-rules/python/PY003-broad-except-clause.d.ts.map +1 -1
  137. package/dist/structural-rules/python/PY003-broad-except-clause.js +212 -21
  138. package/dist/structural-rules/python/PY003-broad-except-clause.js.map +1 -1
  139. package/dist/structural-rules/python/PY011-thread-lifecycle.d.ts +11 -0
  140. package/dist/structural-rules/python/PY011-thread-lifecycle.d.ts.map +1 -0
  141. package/dist/structural-rules/python/PY011-thread-lifecycle.js +97 -0
  142. package/dist/structural-rules/python/PY011-thread-lifecycle.js.map +1 -0
  143. package/dist/structural-rules/python/PY012-asyncio-run-misuse.d.ts +11 -0
  144. package/dist/structural-rules/python/PY012-asyncio-run-misuse.d.ts.map +1 -0
  145. package/dist/structural-rules/python/PY012-asyncio-run-misuse.js +83 -0
  146. package/dist/structural-rules/python/PY012-asyncio-run-misuse.js.map +1 -0
  147. package/dist/structural-rules/python/PY013-mutable-default-arg.d.ts +11 -0
  148. package/dist/structural-rules/python/PY013-mutable-default-arg.d.ts.map +1 -0
  149. package/dist/structural-rules/python/PY013-mutable-default-arg.js +73 -0
  150. package/dist/structural-rules/python/PY013-mutable-default-arg.js.map +1 -0
  151. package/dist/structural-rules/python/PY014-fixed-sleep-retry.d.ts +11 -0
  152. package/dist/structural-rules/python/PY014-fixed-sleep-retry.d.ts.map +1 -0
  153. package/dist/structural-rules/python/PY014-fixed-sleep-retry.js +115 -0
  154. package/dist/structural-rules/python/PY014-fixed-sleep-retry.js.map +1 -0
  155. package/dist/structural-rules/types.d.ts +12 -0
  156. package/dist/structural-rules/types.d.ts.map +1 -1
  157. package/dist/utils/active-engineering-context.d.ts +12 -0
  158. package/dist/utils/active-engineering-context.d.ts.map +1 -0
  159. package/dist/utils/active-engineering-context.js +67 -0
  160. package/dist/utils/active-engineering-context.js.map +1 -0
  161. package/dist/utils/artifact-io.d.ts +33 -0
  162. package/dist/utils/artifact-io.d.ts.map +1 -0
  163. package/dist/utils/artifact-io.js +183 -0
  164. package/dist/utils/artifact-io.js.map +1 -0
  165. package/dist/utils/change-contract.d.ts +6 -2
  166. package/dist/utils/change-contract.d.ts.map +1 -1
  167. package/dist/utils/change-contract.js +175 -0
  168. package/dist/utils/change-contract.js.map +1 -1
  169. package/dist/utils/context-pack.d.ts +12 -0
  170. package/dist/utils/context-pack.d.ts.map +1 -0
  171. package/dist/utils/context-pack.js +147 -0
  172. package/dist/utils/context-pack.js.map +1 -0
  173. package/dist/utils/control-plane.d.ts +18 -0
  174. package/dist/utils/control-plane.d.ts.map +1 -1
  175. package/dist/utils/control-plane.js +31 -4
  176. package/dist/utils/control-plane.js.map +1 -1
  177. package/dist/utils/drift-intelligence.d.ts +47 -0
  178. package/dist/utils/drift-intelligence.d.ts.map +1 -0
  179. package/dist/utils/drift-intelligence.js +2099 -0
  180. package/dist/utils/drift-intelligence.js.map +1 -0
  181. package/dist/utils/execution-actions.d.ts +22 -0
  182. package/dist/utils/execution-actions.d.ts.map +1 -0
  183. package/dist/utils/execution-actions.js +103 -0
  184. package/dist/utils/execution-actions.js.map +1 -0
  185. package/dist/utils/execution-bus.d.ts +1 -214
  186. package/dist/utils/execution-bus.d.ts.map +1 -1
  187. package/dist/utils/execution-bus.js +15 -1359
  188. package/dist/utils/execution-bus.js.map +1 -1
  189. package/dist/utils/git.d.ts +1 -0
  190. package/dist/utils/git.d.ts.map +1 -1
  191. package/dist/utils/git.js +13 -3
  192. package/dist/utils/git.js.map +1 -1
  193. package/dist/utils/governance-decisions.d.ts +75 -0
  194. package/dist/utils/governance-decisions.d.ts.map +1 -0
  195. package/dist/utils/governance-decisions.js +412 -0
  196. package/dist/utils/governance-decisions.js.map +1 -0
  197. package/dist/utils/governance-provenance.d.ts +1 -1
  198. package/dist/utils/governance-provenance.d.ts.map +1 -1
  199. package/dist/utils/governance-provenance.js +5 -7
  200. package/dist/utils/governance-provenance.js.map +1 -1
  201. package/dist/utils/governance.d.ts +108 -0
  202. package/dist/utils/governance.d.ts.map +1 -1
  203. package/dist/utils/governance.js +209 -7
  204. package/dist/utils/governance.js.map +1 -1
  205. package/dist/utils/intelligence-runtime-common.d.ts +30 -0
  206. package/dist/utils/intelligence-runtime-common.d.ts.map +1 -0
  207. package/dist/utils/intelligence-runtime-common.js +156 -0
  208. package/dist/utils/intelligence-runtime-common.js.map +1 -0
  209. package/dist/utils/intent-contract-diagnostics.d.ts +9 -0
  210. package/dist/utils/intent-contract-diagnostics.d.ts.map +1 -0
  211. package/dist/utils/intent-contract-diagnostics.js +322 -0
  212. package/dist/utils/intent-contract-diagnostics.js.map +1 -0
  213. package/dist/utils/intent-pack.d.ts +15 -0
  214. package/dist/utils/intent-pack.d.ts.map +1 -0
  215. package/dist/utils/intent-pack.js +196 -0
  216. package/dist/utils/intent-pack.js.map +1 -0
  217. package/dist/utils/plan-sync.d.ts +1 -0
  218. package/dist/utils/plan-sync.d.ts.map +1 -1
  219. package/dist/utils/plan-sync.js +23 -0
  220. package/dist/utils/plan-sync.js.map +1 -1
  221. package/dist/utils/policy-decision.d.ts +5 -0
  222. package/dist/utils/policy-decision.d.ts.map +1 -0
  223. package/dist/utils/policy-decision.js +17 -0
  224. package/dist/utils/policy-decision.js.map +1 -0
  225. package/dist/utils/replay-custody.d.ts +43 -0
  226. package/dist/utils/replay-custody.d.ts.map +1 -0
  227. package/dist/utils/replay-custody.js +168 -0
  228. package/dist/utils/replay-custody.js.map +1 -0
  229. package/dist/utils/replay-runtime.d.ts +13 -0
  230. package/dist/utils/replay-runtime.d.ts.map +1 -1
  231. package/dist/utils/replay-runtime.js +96 -9
  232. package/dist/utils/replay-runtime.js.map +1 -1
  233. package/dist/utils/repository-intelligence.d.ts +9 -0
  234. package/dist/utils/repository-intelligence.d.ts.map +1 -0
  235. package/dist/utils/repository-intelligence.js +372 -0
  236. package/dist/utils/repository-intelligence.js.map +1 -0
  237. package/dist/utils/runtime-events.d.ts.map +1 -1
  238. package/dist/utils/runtime-events.js +25 -6
  239. package/dist/utils/runtime-events.js.map +1 -1
  240. package/dist/utils/semantic-contract-intelligence.d.ts +20 -0
  241. package/dist/utils/semantic-contract-intelligence.d.ts.map +1 -0
  242. package/dist/utils/semantic-contract-intelligence.js +825 -0
  243. package/dist/utils/semantic-contract-intelligence.js.map +1 -0
  244. package/dist/utils/session-continuity.d.ts +56 -0
  245. package/dist/utils/session-continuity.d.ts.map +1 -0
  246. package/dist/utils/session-continuity.js +318 -0
  247. package/dist/utils/session-continuity.js.map +1 -0
  248. package/dist/utils/verification-evidence.d.ts.map +1 -1
  249. package/dist/utils/verification-evidence.js +4 -1
  250. package/dist/utils/verification-evidence.js.map +1 -1
  251. package/dist/utils/verify-runtime-stability.d.ts +142 -0
  252. package/dist/utils/verify-runtime-stability.d.ts.map +1 -0
  253. package/dist/utils/verify-runtime-stability.js +230 -0
  254. package/dist/utils/verify-runtime-stability.js.map +1 -0
  255. package/dist/utils/workspace-runtime.d.ts +1 -266
  256. package/dist/utils/workspace-runtime.d.ts.map +1 -1
  257. package/dist/utils/workspace-runtime.js +15 -1412
  258. package/dist/utils/workspace-runtime.js.map +1 -1
  259. package/package.json +11 -10
  260. package/LICENSE +0 -201
@@ -67,19 +67,28 @@ 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");
75
+ const canonical_invariants_1 = require("../governance/canonical-invariants");
72
76
  const structural_on_diff_1 = require("../governance/structural-on-diff");
73
- const structural_policy_merge_1 = require("../governance/structural-policy-merge");
77
+ // NOTE: mergeStructuralIntoPolicyViolations is intentionally NOT imported.
78
+ // Structural violations flow exclusively through payload.structuralViolations
79
+ // into the canonical pipeline. Merging them into policyViolations caused
80
+ // duplicate GovernanceFinding objects (fixed in Phase 1 canonical graph hardening).
74
81
  const telemetry_1 = require("@neurcode-ai/telemetry");
75
- const governance_provenance_1 = require("../utils/governance-provenance");
76
82
  const pilot_metrics_1 = require("../utils/pilot-metrics");
77
- const explainability_1 = require("../explainability");
83
+ const replay_custody_1 = require("../utils/replay-custody");
78
84
  const runtime_guard_1 = require("../utils/runtime-guard");
79
85
  const artifact_signature_1 = require("../utils/artifact-signature");
80
86
  const policy_1 = require("@neurcode-ai/policy");
87
+ const active_engineering_context_1 = require("../utils/active-engineering-context");
81
88
  const ai_debt_budget_1 = require("../utils/ai-debt-budget");
82
89
  const verification_evidence_1 = require("../utils/verification-evidence");
90
+ const verify_runtime_stability_1 = require("../utils/verify-runtime-stability");
91
+ const policy_decision_1 = require("../utils/policy-decision");
83
92
  // Import chalk with fallback
84
93
  let chalk;
85
94
  try {
@@ -100,6 +109,74 @@ catch {
100
109
  white: (str) => str,
101
110
  };
102
111
  }
112
+ /**
113
+ * Structured CI explainability for `neurcode verify --ci` / `--policy-only` human output.
114
+ * Keeps logs short — no JSON dumps — and separates merge-blocking vs advisory signals.
115
+ */
116
+ function logCiPolicyOnlyOutcomeExplainability(params) {
117
+ if (!params.ciModeEnabled || params.json) {
118
+ return;
119
+ }
120
+ const modeLine = params.source === 'ci'
121
+ ? '`verify --ci` uses deterministic local governance (compiled/custom policy + structural rules). Remote plan-verify API is not used.'
122
+ : '`--policy-only` — local policy + structural governance without plan adherence.';
123
+ const sev = (s) => String(s || '').toLowerCase();
124
+ const sBlock = params.structuralViolations.filter((v) => v.severity === 'BLOCKING').length;
125
+ const sAdv = params.structuralViolations.length - sBlock;
126
+ const pBlock = params.policyViolations.filter((v) => sev(v.severity) === 'block').length;
127
+ const pWarn = params.policyViolations.filter((v) => sev(v.severity) === 'warn').length;
128
+ if (params.verdict === 'PASS') {
129
+ console.log(chalk.dim('\n── CI verify contract ──'));
130
+ console.log(chalk.dim(` ${modeLine}`));
131
+ console.log(chalk.dim(' Exit 0: no blocking severities. Replay checksum (JSON) anchors structural findings for audit parity.'));
132
+ return;
133
+ }
134
+ console.log(chalk.bold.red('\n── CI failure explainability ──'));
135
+ console.log(chalk.dim(` ${modeLine}`));
136
+ console.log(chalk.red(` Merge-blocking rows: structural BLOCKING ${sBlock}; policy/custom severity=block ${pBlock}`));
137
+ console.log(chalk.yellow(` Non-blocking (warn/advisory-class): structural advisory ${sAdv}; policy warn ${pWarn} — does not fail CI unless your gate maps warns to failure`));
138
+ console.log(chalk.dim(' Offline / structural-only: set NEURCODE_VERIFY_LOCAL_ONLY=1 or `--local-only` to skip API compatibility probes (AST gates still run).'));
139
+ if (params.replayChecksum) {
140
+ console.log(chalk.dim(` Structural replay checksum: ${params.replayChecksum.slice(0, 16)}… · mode ${params.replayMode ?? 'local-structural'}`));
141
+ }
142
+ console.log(chalk.dim(' Next: resolve BLOCKING first → `neurcode remediate-export` (optional) → re-run `neurcode verify --ci`.\n'));
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
+ }
103
180
  ;
104
181
  function toArtifactSignatureSummary(status) {
105
182
  return {
@@ -660,569 +737,6 @@ function runtimeGuardViolationsToReport(summary) {
660
737
  message: item.message,
661
738
  }));
662
739
  }
663
- function asObjectRecord(value) {
664
- if (!value || typeof value !== 'object' || Array.isArray(value)) {
665
- return null;
666
- }
667
- return value;
668
- }
669
- function asObjectArray(value) {
670
- if (!Array.isArray(value)) {
671
- return [];
672
- }
673
- return value
674
- .map((item) => asObjectRecord(item))
675
- .filter((item) => item !== null);
676
- }
677
- function asBooleanFlag(value) {
678
- return typeof value === 'boolean' ? value : null;
679
- }
680
- function asNumberValue(value) {
681
- return typeof value === 'number' && Number.isFinite(value) ? value : null;
682
- }
683
- function asStringValue(value) {
684
- return typeof value === 'string' && value.trim().length > 0 ? value : null;
685
- }
686
- const EXPEDITE_FOLLOW_UP_CHECKLIST = [
687
- 'Add validation back',
688
- 'Move logic to proper layer',
689
- 'Remove temporary code',
690
- ];
691
- function containsAnyToken(value, tokens) {
692
- const normalized = value.toLowerCase();
693
- return tokens.some((token) => normalized.includes(token));
694
- }
695
- function isSecurityOrAuthViolation(fileRaw, policyRaw, messageRaw) {
696
- const combined = `${fileRaw} ${policyRaw} ${messageRaw}`.toLowerCase();
697
- return containsAnyToken(combined, [
698
- 'auth',
699
- 'authentication',
700
- 'authorization',
701
- 'security',
702
- 'permission',
703
- 'access control',
704
- 'access_control',
705
- 'token',
706
- 'secret',
707
- 'credential',
708
- 'encryption',
709
- 'encrypt',
710
- 'decrypt',
711
- 'csrf',
712
- 'xss',
713
- 'sql injection',
714
- 'sqli',
715
- 'insecure',
716
- 'vulnerability',
717
- ]);
718
- }
719
- function isCriticalScopeBreach(fileRaw, messageRaw) {
720
- const combined = `${fileRaw} ${messageRaw}`.toLowerCase();
721
- return containsAnyToken(combined, [
722
- 'auth',
723
- 'security',
724
- 'secret',
725
- 'token',
726
- 'credential',
727
- 'permission',
728
- 'infra/terraform',
729
- 'terraform',
730
- 'k8s',
731
- 'helm',
732
- 'migration',
733
- 'database/migration',
734
- 'policy',
735
- 'contract',
736
- ]);
737
- }
738
- function resolveExpediteModeFromPayload(payload) {
739
- const explicit = asBooleanFlag(payload.expediteMode);
740
- if (explicit !== null) {
741
- return explicit;
742
- }
743
- const message = asStringValue(payload.message) || '';
744
- return containsAnyToken(message, ['hotfix', 'urgent', 'prod down', 'incident', 'expedite']);
745
- }
746
- function toVerifySeverity(value) {
747
- const normalized = typeof value === 'string' ? value.trim().toLowerCase() : '';
748
- if (normalized === 'critical' || normalized === 'block')
749
- return 'critical';
750
- if (normalized === 'high')
751
- return 'high';
752
- if (normalized === 'warn'
753
- || normalized === 'warning'
754
- || normalized === 'medium'
755
- || normalized === 'low') {
756
- return 'warning';
757
- }
758
- return 'info';
759
- }
760
- function toVerifyVerdict(value) {
761
- const normalized = typeof value === 'string' ? value.trim().toUpperCase() : '';
762
- if (normalized === 'PASS' || normalized === 'WARN' || normalized === 'FAIL') {
763
- return normalized;
764
- }
765
- return 'FAIL';
766
- }
767
- function normalizeScopeIssueMessage(rawMessage) {
768
- const message = asStringValue(rawMessage);
769
- return message || 'File modified outside intended scope';
770
- }
771
- function pushVerifyIssue(target, seen, key, value) {
772
- if (seen.has(key))
773
- return;
774
- seen.add(key);
775
- target.push(value);
776
- }
777
- function dedupeTriageItems(items) {
778
- const seen = new Set();
779
- const output = [];
780
- for (const item of items) {
781
- const key = `${item.source}|${item.file.toLowerCase()}|${item.policy.toLowerCase()}|${item.message.toLowerCase()}`;
782
- if (seen.has(key))
783
- continue;
784
- seen.add(key);
785
- output.push(item);
786
- }
787
- return output;
788
- }
789
- function toCanonicalVerifyOutput(payload) {
790
- const verdict = toVerifyVerdict(payload.verdict);
791
- const violations = [];
792
- const warnings = [];
793
- const scopeIssues = [];
794
- const seenViolations = new Set();
795
- const seenWarnings = new Set();
796
- const seenScopeIssues = new Set();
797
- const addScopeIssue = (fileRaw, messageRaw) => {
798
- const file = asStringValue(fileRaw) || 'unknown';
799
- const message = normalizeScopeIssueMessage(messageRaw);
800
- const key = file.toLowerCase();
801
- pushVerifyIssue(scopeIssues, seenScopeIssues, key, { file, message });
802
- };
803
- const addWarning = (fileRaw, messageRaw, policyRaw) => {
804
- const file = asStringValue(fileRaw) || 'unknown';
805
- const message = asStringValue(messageRaw) || 'Warning detected';
806
- const policy = asStringValue(policyRaw) || 'warning';
807
- const key = `${file.toLowerCase()}|${message.toLowerCase()}|${policy.toLowerCase()}`;
808
- pushVerifyIssue(warnings, seenWarnings, key, { file, message, policy });
809
- };
810
- const addViolation = (fileRaw, messageRaw, policyRaw, severityRaw) => {
811
- const file = asStringValue(fileRaw) || 'unknown';
812
- const message = asStringValue(messageRaw) || 'Policy violation detected';
813
- const policy = asStringValue(policyRaw) || 'unknown_policy';
814
- const severity = toVerifySeverity(severityRaw);
815
- const key = `${file.toLowerCase()}|${message.toLowerCase()}|${policy.toLowerCase()}|${severity}`;
816
- pushVerifyIssue(violations, seenViolations, key, { file, message, policy, severity });
817
- };
818
- const rawScopeIssues = Array.isArray(payload.scopeIssues) ? payload.scopeIssues : [];
819
- for (const item of rawScopeIssues) {
820
- const record = asObjectRecord(item);
821
- if (record) {
822
- addScopeIssue(record.file, record.message);
823
- }
824
- else {
825
- addScopeIssue(item, null);
826
- }
827
- }
828
- const rawBloatFiles = Array.isArray(payload.bloatFiles) ? payload.bloatFiles : [];
829
- for (const item of rawBloatFiles) {
830
- addScopeIssue(item, null);
831
- }
832
- const rawWarnings = Array.isArray(payload.warnings) ? payload.warnings : [];
833
- for (const item of rawWarnings) {
834
- const record = asObjectRecord(item);
835
- if (record) {
836
- addWarning(record.file, record.message, record.policy ?? record.rule);
837
- }
838
- else if (typeof item === 'string') {
839
- addWarning('unknown', item, 'warning');
840
- }
841
- }
842
- const rawViolations = Array.isArray(payload.violations) ? payload.violations : [];
843
- for (const item of rawViolations) {
844
- const record = asObjectRecord(item);
845
- if (!record)
846
- continue;
847
- const file = record.file;
848
- const message = record.message;
849
- const policy = record.policy ?? record.rule;
850
- const severity = toVerifySeverity(record.severity);
851
- const combined = `${String(policy || '').toLowerCase()} ${String(message || '').toLowerCase()}`;
852
- const isScopeIssue = combined.includes('scope_guard')
853
- || combined.includes('scope')
854
- || combined.includes('outside the plan')
855
- || combined.includes('out of scope');
856
- if (isScopeIssue) {
857
- addScopeIssue(file, message);
858
- continue;
859
- }
860
- // Artifact presence/signature checks are advisory — they must never block a PR.
861
- // Real governance signal (policy violations, scope drift) should not be obscured
862
- // by infrastructure setup state.
863
- const policyStr = String(policy || '').toLowerCase();
864
- const isArtifactCheck = policyStr === 'deterministic_artifacts_required'
865
- || policyStr === 'signed_artifacts_required';
866
- if (isArtifactCheck) {
867
- addWarning(file, message, policy);
868
- continue;
869
- }
870
- if (severity === 'warning' || severity === 'info') {
871
- addWarning(file, message, policy);
872
- continue;
873
- }
874
- addViolation(file, message, policy, severity);
875
- }
876
- const payloadMessage = asStringValue(payload.message);
877
- if (payloadMessage
878
- && violations.length === 0
879
- && warnings.length === 0
880
- && scopeIssues.length === 0) {
881
- addWarning('unknown', payloadMessage, 'verify_result');
882
- }
883
- const summaryRecord = asObjectRecord(payload.summary);
884
- const fileSet = new Set();
885
- for (const violation of violations)
886
- fileSet.add(violation.file);
887
- for (const warning of warnings)
888
- fileSet.add(warning.file);
889
- for (const scopeIssue of scopeIssues)
890
- fileSet.add(scopeIssue.file);
891
- const totalFilesChanged = (() => {
892
- const fromSummary = summaryRecord ? asNumberValue(summaryRecord.totalFilesChanged) : null;
893
- if (fromSummary !== null)
894
- return Math.max(0, Math.floor(fromSummary));
895
- const blastRadius = asObjectRecord(payload.blastRadius);
896
- const fromBlastRadius = blastRadius ? asNumberValue(blastRadius.filesChanged) : null;
897
- if (fromBlastRadius !== null)
898
- return Math.max(0, Math.floor(fromBlastRadius));
899
- return fileSet.size;
900
- })();
901
- const driftScoreRaw = asNumberValue(payload.driftScore);
902
- const driftScore = driftScoreRaw === null
903
- ? undefined
904
- : Math.max(0, Math.min(100, Math.round(driftScoreRaw)));
905
- const expediteModeUsed = resolveExpediteModeFromPayload(payload);
906
- const scopeTriageItems = scopeIssues.map((item) => ({
907
- file: item.file,
908
- message: item.message,
909
- policy: 'scope_guard',
910
- severity: 'block',
911
- source: 'scope',
912
- }));
913
- const violationTriageItems = violations.map((item) => ({
914
- file: item.file,
915
- message: item.message,
916
- policy: item.policy,
917
- severity: item.severity,
918
- source: 'violation',
919
- }));
920
- const warningTriageItems = warnings.map((item) => ({
921
- file: item.file,
922
- message: item.message,
923
- policy: item.policy,
924
- severity: 'warning',
925
- source: 'warning',
926
- }));
927
- const defaultBlockingItems = dedupeTriageItems([
928
- ...scopeTriageItems,
929
- ...violationTriageItems.filter((item) => item.severity === 'critical' || item.severity === 'high'),
930
- ]);
931
- const defaultAdvisoryItems = dedupeTriageItems([
932
- ...warningTriageItems,
933
- ...violationTriageItems.filter((item) => item.severity === 'warning' || item.severity === 'info'),
934
- ]);
935
- const expediteBlockingItems = dedupeTriageItems([
936
- ...scopeTriageItems.filter((item) => isCriticalScopeBreach(item.file, item.message)),
937
- ...violationTriageItems.filter((item) => isSecurityOrAuthViolation(item.file, item.policy, item.message)),
938
- ...warningTriageItems
939
- .filter((item) => isSecurityOrAuthViolation(item.file, item.policy, item.message))
940
- .map((item) => ({
941
- ...item,
942
- source: 'violation',
943
- })),
944
- ]);
945
- const expediteItems = dedupeTriageItems([
946
- ...scopeTriageItems
947
- .filter((item) => !isCriticalScopeBreach(item.file, item.message))
948
- .map((item) => ({
949
- ...item,
950
- source: 'expedite',
951
- })),
952
- ...violationTriageItems
953
- .filter((item) => !isSecurityOrAuthViolation(item.file, item.policy, item.message))
954
- .map((item) => ({
955
- ...item,
956
- source: 'expedite',
957
- })),
958
- ...warningTriageItems
959
- .filter((item) => !isSecurityOrAuthViolation(item.file, item.policy, item.message))
960
- .map((item) => ({
961
- ...item,
962
- source: 'expedite',
963
- })),
964
- ]);
965
- // ── Intent issues and summary from engine ───────────────────────────────
966
- const rawIntentIssues = Array.isArray(payload.intentIssues) ? payload.intentIssues : [];
967
- const intentDomains = Array.isArray(payload.intentDomains) ? payload.intentDomains : [];
968
- const intentSummary = (payload.intentSummary ?? null);
969
- const rawFlowIssues = Array.isArray(payload.flowIssues) ? payload.flowIssues : [];
970
- const rawRegressions = Array.isArray(payload.regressions) ? payload.regressions : [];
971
- // High-severity intent issues become blocking; medium become advisory.
972
- const intentBlockingTriageItems = rawIntentIssues
973
- .filter((i) => i.severity === 'high')
974
- .map((i) => ({
975
- file: (i.files?.[0]) ?? 'intent-analysis',
976
- message: i.message,
977
- policy: i.rule,
978
- severity: 'high',
979
- source: 'violation',
980
- }));
981
- const intentAdvisoryTriageItems = rawIntentIssues
982
- .filter((i) => i.severity === 'medium')
983
- .map((i) => ({
984
- file: (i.files?.[0]) ?? 'intent-analysis',
985
- message: i.message,
986
- policy: i.rule,
987
- severity: 'warning',
988
- source: 'warning',
989
- }));
990
- // V5: flow issues — high → blocking, medium → advisory
991
- const flowBlockingTriageItems = rawFlowIssues
992
- .filter((i) => i.severity === 'high')
993
- .map((i) => ({
994
- file: (i.files?.[0]) ?? 'flow-analysis',
995
- message: i.message,
996
- policy: i.rule,
997
- severity: 'high',
998
- source: 'violation',
999
- }));
1000
- const flowAdvisoryTriageItems = rawFlowIssues
1001
- .filter((i) => i.severity === 'medium')
1002
- .map((i) => ({
1003
- file: (i.files?.[0]) ?? 'flow-analysis',
1004
- message: i.message,
1005
- policy: i.rule,
1006
- severity: 'warning',
1007
- source: 'warning',
1008
- }));
1009
- let blockingItems = expediteModeUsed ? expediteBlockingItems : defaultBlockingItems;
1010
- let advisoryItems = expediteModeUsed ? expediteItems : defaultAdvisoryItems;
1011
- if (intentBlockingTriageItems.length > 0) {
1012
- blockingItems = dedupeTriageItems([...blockingItems, ...intentBlockingTriageItems]);
1013
- }
1014
- if (intentAdvisoryTriageItems.length > 0) {
1015
- advisoryItems = dedupeTriageItems([...advisoryItems, ...intentAdvisoryTriageItems]);
1016
- }
1017
- if (flowBlockingTriageItems.length > 0) {
1018
- blockingItems = dedupeTriageItems([...blockingItems, ...flowBlockingTriageItems]);
1019
- }
1020
- if (flowAdvisoryTriageItems.length > 0) {
1021
- advisoryItems = dedupeTriageItems([...advisoryItems, ...flowAdvisoryTriageItems]);
1022
- }
1023
- // V6: regressions — always blocking
1024
- const regressionBlockingTriageItems = rawRegressions.map((r) => ({
1025
- file: 'regression-analysis',
1026
- message: r.message,
1027
- policy: r.rule,
1028
- severity: 'high',
1029
- source: 'violation',
1030
- }));
1031
- if (regressionBlockingTriageItems.length > 0) {
1032
- blockingItems = dedupeTriageItems([...regressionBlockingTriageItems, ...blockingItems]);
1033
- }
1034
- const grade = verdict === 'PASS' ? 'A' : verdict === 'WARN' ? 'C' : 'F';
1035
- const canonical = {
1036
- grade,
1037
- score: violations.length === 0 && warnings.length === 0 && scopeIssues.length === 0 ? 100 : 0,
1038
- verdict,
1039
- summary: {
1040
- totalFilesChanged,
1041
- totalViolations: violations.length,
1042
- totalWarnings: warnings.length,
1043
- totalScopeIssues: scopeIssues.length,
1044
- },
1045
- violations,
1046
- warnings,
1047
- scopeIssues,
1048
- blockingCount: blockingItems.length,
1049
- advisoryCount: advisoryItems.length,
1050
- blockingItems,
1051
- advisoryItems,
1052
- intentIssues: rawIntentIssues,
1053
- intentDomains,
1054
- intentSummary,
1055
- flowIssues: rawFlowIssues,
1056
- regressions: rawRegressions,
1057
- expediteModeUsed,
1058
- expediteCount: expediteModeUsed ? expediteItems.length : 0,
1059
- expediteItems: expediteModeUsed ? expediteItems : [],
1060
- expediteFollowUpChecklist: expediteModeUsed ? [...EXPEDITE_FOLLOW_UP_CHECKLIST] : [],
1061
- ...(expediteModeUsed ? { expediteNote: 'Expedite Mode used' } : {}),
1062
- ...(typeof driftScore === 'number' ? { driftScore } : {}),
1063
- };
1064
- // Preserve actionable metadata from rich verify payloads so downstream
1065
- // consumers (Action, CI contracts, dashboards) keep structured context.
1066
- const passthroughKeys = [
1067
- 'message',
1068
- 'mode',
1069
- 'ciMode',
1070
- 'policyOnly',
1071
- 'policyOnlySource',
1072
- 'policySources',
1073
- 'scopeGuardPassed',
1074
- 'policyLock',
1075
- 'policyCompilation',
1076
- 'policyExceptions',
1077
- 'policyGovernance',
1078
- 'changeContract',
1079
- 'runtimeGuard',
1080
- 'governanceDecision',
1081
- 'orgGovernance',
1082
- 'aiChangeLog',
1083
- 'verificationSource',
1084
- 'tier',
1085
- 'aiDebt',
1086
- 'blastRadius',
1087
- 'suspiciousChange',
1088
- 'policyDecision',
1089
- 'policyPack',
1090
- 'changeContractViolations',
1091
- 'governanceVerification',
1092
- 'governanceFindings',
1093
- 'structuralViolations',
1094
- 'structuralRulesApplied',
1095
- 'structuralSuppressedCount',
1096
- ];
1097
- const canonicalMutable = canonical;
1098
- for (const key of passthroughKeys) {
1099
- const v = payload[key];
1100
- if (Object.prototype.hasOwnProperty.call(payload, key) && v !== undefined && v !== null) {
1101
- canonicalMutable[key] = v;
1102
- }
1103
- }
1104
- // Backward-compatibility alias: older integrations and tests expect `rule`
1105
- // while canonical contract uses `policy`.
1106
- canonical.violations = canonical.violations.map((item) => ({
1107
- ...item,
1108
- rule: item.policy,
1109
- }));
1110
- canonical.warnings = canonical.warnings.map((item) => ({
1111
- ...item,
1112
- rule: item.policy,
1113
- }));
1114
- return canonical;
1115
- }
1116
- function emitCanonicalVerifyJson(payload, onEmit) {
1117
- const canonical = toCanonicalVerifyOutput((0, canonical_pipeline_1.attachCanonicalGovernance)(payload));
1118
- onEmit?.(canonical);
1119
- // Use sync stdout write so immediate process.exit paths do not truncate JSON.
1120
- const serialized = Buffer.from(`${JSON.stringify(canonical, null, 2)}\n`, 'utf-8');
1121
- try {
1122
- let offset = 0;
1123
- while (offset < serialized.length) {
1124
- try {
1125
- const written = (0, fs_1.writeSync)(1, serialized, offset, serialized.length - offset);
1126
- if (written <= 0)
1127
- break;
1128
- offset += written;
1129
- }
1130
- catch (error) {
1131
- const code = error.code;
1132
- if (code === 'EAGAIN' || code === 'EWOULDBLOCK') {
1133
- continue;
1134
- }
1135
- throw error;
1136
- }
1137
- }
1138
- }
1139
- catch {
1140
- process.stdout.write(serialized.toString('utf-8'));
1141
- }
1142
- }
1143
- function buildDeterministicLayerSummary(payload) {
1144
- const verdict = asStringValue(payload.verdict) || 'UNKNOWN';
1145
- const mode = asStringValue(payload.mode) || 'unknown';
1146
- const policyOnly = payload.policyOnly === true;
1147
- const scopeGuardPassed = asBooleanFlag(payload.scopeGuardPassed);
1148
- const violations = asObjectArray(payload.violations);
1149
- const policyViolations = violations.filter((entry) => {
1150
- const rule = String(entry.rule || '').toLowerCase();
1151
- return (!rule.includes('scope_guard')
1152
- && !rule.includes('change_contract')
1153
- && !rule.includes('runtime_guard')
1154
- && !rule.includes('deterministic_artifacts_required')
1155
- && !rule.includes('signed_artifacts_required'));
1156
- });
1157
- const policyBlocking = policyViolations.filter((entry) => String(entry.severity || '').toLowerCase() === 'block');
1158
- const policyWarnings = policyViolations.filter((entry) => String(entry.severity || '').toLowerCase() === 'warn');
1159
- const changeContract = asObjectRecord(payload.changeContract);
1160
- const changeContractValid = asBooleanFlag(changeContract?.valid);
1161
- const changeContractEnforced = changeContract?.enforced === true;
1162
- const changeContractViolations = Array.isArray(changeContract?.violations)
1163
- ? (changeContract?.violations).length
1164
- : 0;
1165
- const explicitContractViolations = violations.filter((entry) => {
1166
- const rule = String(entry.rule || '').toLowerCase();
1167
- return rule.includes('scope_guard') || rule.includes('change_contract');
1168
- }).length;
1169
- const runtimeGuard = asObjectRecord(payload.runtimeGuard);
1170
- const runtimeGuardRequired = runtimeGuard?.required === true;
1171
- const runtimeGuardPass = asBooleanFlag(runtimeGuard?.pass);
1172
- const runtimeGuardViolations = Array.isArray(runtimeGuard?.violations)
1173
- ? (runtimeGuard?.violations).length
1174
- : violations.filter((entry) => String(entry.rule || '').toLowerCase().includes('runtime_guard')).length;
1175
- const policyCompilation = asObjectRecord(payload.policyCompilation);
1176
- const deterministicRuleCount = asNumberValue(policyCompilation?.deterministicRuleCount);
1177
- const unmatchedStatements = asNumberValue(policyCompilation?.unmatchedStatements);
1178
- let policyGateStatus = 'pass';
1179
- if (policyBlocking.length > 0) {
1180
- policyGateStatus = 'fail';
1181
- }
1182
- else if (policyWarnings.length > 0 || verdict === 'WARN') {
1183
- policyGateStatus = 'warn';
1184
- }
1185
- let contractGateStatus = 'not_applicable';
1186
- if (!policyOnly) {
1187
- contractGateStatus = 'pass';
1188
- if (changeContractEnforced
1189
- && (changeContractValid === false || changeContractViolations > 0 || explicitContractViolations > 0 || scopeGuardPassed === false)) {
1190
- contractGateStatus = 'fail';
1191
- }
1192
- else if (!changeContractEnforced && (changeContractViolations > 0 || explicitContractViolations > 0)) {
1193
- contractGateStatus = 'warn';
1194
- }
1195
- }
1196
- let runtimeGuardStatus = 'not_applicable';
1197
- if (runtimeGuardRequired) {
1198
- runtimeGuardStatus = runtimeGuardPass === false || runtimeGuardViolations > 0 ? 'fail' : 'pass';
1199
- }
1200
- else if (runtimeGuardViolations > 0) {
1201
- runtimeGuardStatus = 'fail';
1202
- }
1203
- return {
1204
- policyGate: {
1205
- status: policyGateStatus,
1206
- blockingViolations: policyBlocking.length,
1207
- warningViolations: policyWarnings.length,
1208
- deterministicRuleCount: deterministicRuleCount ?? null,
1209
- unmatchedStatements: unmatchedStatements ?? null,
1210
- },
1211
- contractGate: {
1212
- status: contractGateStatus,
1213
- enforced: changeContractEnforced,
1214
- valid: changeContractValid,
1215
- violationCount: changeContractViolations + explicitContractViolations,
1216
- mode,
1217
- },
1218
- runtimeGuardGate: {
1219
- status: runtimeGuardStatus,
1220
- required: runtimeGuardRequired,
1221
- pass: runtimeGuardPass,
1222
- violationCount: runtimeGuardViolations,
1223
- },
1224
- };
1225
- }
1226
740
  function toAiDebtSummary(evaluation) {
1227
741
  return {
1228
742
  mode: evaluation.mode,
@@ -1326,7 +840,7 @@ function resolveVerifyExpediteMode(projectRoot) {
1326
840
  return true;
1327
841
  }
1328
842
  const branchName = (0, git_1.detectCurrentGitBranch)(projectRoot) || process.env.GITHUB_REF_NAME || '';
1329
- 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']);
1330
844
  }
1331
845
  function isSignedAiLogsRequired(orgGovernanceSettings) {
1332
846
  const explicitRequirement = isEnabledFlag(process.env.NEURCODE_GOVERNANCE_REQUIRE_SIGNED_LOGS) ||
@@ -1351,19 +865,6 @@ function toPolicyLockViolations(mismatches) {
1351
865
  message: item.message,
1352
866
  }));
1353
867
  }
1354
- function resolvePolicyDecisionFromViolations(violations) {
1355
- let hasWarn = false;
1356
- for (const violation of violations) {
1357
- const severity = String(violation.severity || '').toLowerCase();
1358
- if (severity === 'block') {
1359
- return 'block';
1360
- }
1361
- if (severity === 'warn') {
1362
- hasWarn = true;
1363
- }
1364
- }
1365
- return hasWarn ? 'warn' : 'allow';
1366
- }
1367
868
  function explainExceptionEligibilityReason(reason) {
1368
869
  switch (reason) {
1369
870
  case 'reason_required':
@@ -1500,12 +1001,15 @@ async function recordVerificationIfRequested(options, config, payload) {
1500
1001
  * Returns the exit code to use
1501
1002
  */
1502
1003
  async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRoot, config, client, source, ciModeEnabled, scopeTelemetry, projectId, orgGovernanceSettings, aiLogSigningKey, aiLogSigningKeyId, aiLogSigningKeys, aiLogSigner, expediteModeEnabled, compiledPolicyArtifact, compiledPolicyMetadata, changeContractSummary, onCanonicalEmit) {
1503
- const emitPolicyOnlyJson = (payload) => {
1504
- emitCanonicalVerifyJson({
1004
+ const emitPolicyOnlyJson = (payload, onEmit) => {
1005
+ (0, verify_output_1.emitCanonicalVerifyJson)({
1505
1006
  ...payload,
1506
1007
  ciMode: payload.ciMode ?? ciModeEnabled,
1507
1008
  expediteMode: expediteModeEnabled,
1508
- }, onCanonicalEmit);
1009
+ }, (canonical) => {
1010
+ onEmit?.(canonical);
1011
+ onCanonicalEmit?.(canonical);
1012
+ });
1509
1013
  };
1510
1014
  const policyOnlyVerificationSource = 'policy_only';
1511
1015
  const recordPolicyOnlyVerification = async (payload) => recordVerificationIfRequested(options, config, {
@@ -1522,18 +1026,22 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
1522
1026
  const diffFilesForPolicy = diffFiles.filter((f) => !ignoreFilter(f.path));
1523
1027
  const expectedPolicyOnlyFiles = diffFilesForPolicy.map((file) => file.path);
1524
1028
  const signedLogsRequired = isSignedAiLogsRequired(orgGovernanceSettings);
1029
+ const activeEngineeringContext = (0, active_engineering_context_1.loadActiveEngineeringContext)(projectRoot);
1525
1030
  const governanceAnalysis = (0, governance_1.evaluateGovernance)({
1526
1031
  projectRoot,
1527
1032
  task: 'Policy-only verification',
1528
1033
  expectedFiles: expectedPolicyOnlyFiles,
1529
1034
  diffFiles: diffFilesForPolicy,
1530
- contextCandidates: expectedPolicyOnlyFiles,
1035
+ contextCandidates: activeEngineeringContext
1036
+ ? activeEngineeringContext.contextPack.selectedFiles.map((item) => item.path)
1037
+ : expectedPolicyOnlyFiles,
1531
1038
  orgGovernance: orgGovernanceSettings,
1532
1039
  requireSignedAiLogs: signedLogsRequired,
1533
1040
  signingKey: aiLogSigningKey,
1534
1041
  signingKeyId: aiLogSigningKeyId,
1535
1042
  signingKeys: aiLogSigningKeys,
1536
1043
  signer: aiLogSigner,
1044
+ activeEngineeringContext,
1537
1045
  });
1538
1046
  const governancePayload = buildGovernancePayload(governanceAnalysis, orgGovernanceSettings, {
1539
1047
  compiledPolicy: compiledPolicyMetadata,
@@ -1599,19 +1107,23 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
1599
1107
  const message = governanceAnalysis.governanceDecision.summary
1600
1108
  || 'Governance decision matrix returned BLOCK.';
1601
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
+ ];
1602
1121
  if (options.json) {
1603
1122
  emitPolicyOnlyJson({
1604
1123
  grade: 'F',
1605
1124
  score: 0,
1606
1125
  verdict: 'FAIL',
1607
- violations: [
1608
- {
1609
- file: '.neurcode/ai-change-log.json',
1610
- rule: 'governance_decision_block',
1611
- severity: 'block',
1612
- message,
1613
- },
1614
- ],
1126
+ violations: governanceBlockViolations,
1615
1127
  message,
1616
1128
  scopeGuardPassed: false,
1617
1129
  bloatCount: 0,
@@ -1634,14 +1146,7 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
1634
1146
  }
1635
1147
  await recordPolicyOnlyVerification({
1636
1148
  grade: 'F',
1637
- violations: [
1638
- {
1639
- file: '.neurcode/ai-change-log.json',
1640
- rule: 'governance_decision_block',
1641
- severity: 'block',
1642
- message,
1643
- },
1644
- ],
1149
+ violations: governanceBlockViolations,
1645
1150
  verifyResult: {
1646
1151
  adherenceScore: 0,
1647
1152
  verdict: 'FAIL',
@@ -1931,9 +1436,15 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
1931
1436
  message: `Policy audit chain is invalid: ${auditIntegrityStatus.issues.join('; ') || 'unknown issue'}`,
1932
1437
  });
1933
1438
  }
1439
+ if (governanceAnalysis.driftIntelligence) {
1440
+ const driftViolations = driftFindingsToVerificationViolations(governanceAnalysis.driftIntelligence);
1441
+ policyViolations.push(...driftViolations.filter((item) => !ignoreFilter(item.file)));
1442
+ }
1934
1443
  const policyOnlyStructural = (0, structural_on_diff_1.runStructuralOnDiffFiles)(projectRoot, diffFilesForPolicy);
1935
- (0, structural_policy_merge_1.mergeStructuralIntoPolicyViolations)(policyViolations, policyOnlyStructural.violations);
1936
- policyDecision = resolvePolicyDecisionFromViolations(policyViolations);
1444
+ // Structural violations are passed to the canonical pipeline via payload.structuralViolations
1445
+ // (see line ~2584). Do NOT merge them into policyViolations — that would create structural:*
1446
+ // duplicates that contaminate the canonical finding graph.
1447
+ policyDecision = (0, policy_decision_1.resolvePolicyDecisionFromViolations)(policyViolations);
1937
1448
  const effectiveVerdict = policyDecision === 'block' ? 'FAIL' : policyDecision === 'warn' ? 'WARN' : 'PASS';
1938
1449
  const grade = effectiveVerdict === 'PASS' ? 'A' : effectiveVerdict === 'WARN' ? 'C' : 'F';
1939
1450
  const score = effectiveVerdict === 'PASS' ? 100 : effectiveVerdict === 'WARN' ? 50 : 0;
@@ -1989,6 +1500,11 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
1989
1500
  eventCount: auditIntegrity.count,
1990
1501
  },
1991
1502
  };
1503
+ // Phase 4: Compute replay checksum from structural findings so replayChecksum
1504
+ // is populated in --policy-only and --local-only CI/daemonless modes.
1505
+ // This closes the N/A gap identified in the Apache Airflow benchmark.
1506
+ const policyOnlyStructuralFindings = policyOnlyStructural.violations.map(canonical_pipeline_1.findingFromStructural);
1507
+ const policyOnlyReplayChecksum = (0, canonical_invariants_1.computeCanonicalFindingChecksum)(policyOnlyStructuralFindings);
1992
1508
  const policyOnlyPayload = {
1993
1509
  grade,
1994
1510
  score,
@@ -2007,6 +1523,8 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
2007
1523
  mode: 'policy_only',
2008
1524
  policyOnly: true,
2009
1525
  policyOnlySource: source,
1526
+ replayChecksum: policyOnlyReplayChecksum,
1527
+ replayMode: 'local-structural',
2010
1528
  ...governancePayload,
2011
1529
  policyLock: {
2012
1530
  enforced: policyLockEvaluation.enforced,
@@ -2027,15 +1545,46 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
2027
1545
  }
2028
1546
  : {}),
2029
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
+ });
2030
1575
  if (options.json) {
2031
- emitPolicyOnlyJson(policyOnlyPayload);
1576
+ emitPolicyOnlyJson(policyOnlyPayload, (canonical) => {
1577
+ (0, replay_custody_1.applyReplayCustodyToCanonicalOutput)(canonical, policyOnlyReplayCustody);
1578
+ });
2032
1579
  }
2033
1580
  else {
2034
- onCanonicalEmit?.(toCanonicalVerifyOutput({
1581
+ const policyOnlyCanonical = (0, verify_output_1.toCanonicalVerifyOutput)((0, canonical_pipeline_1.attachCanonicalGovernance)({
2035
1582
  ...policyOnlyPayload,
2036
1583
  ciMode: ciModeEnabled,
2037
1584
  expediteMode: expediteModeEnabled,
2038
1585
  }));
1586
+ (0, replay_custody_1.applyReplayCustodyToCanonicalOutput)(policyOnlyCanonical, policyOnlyReplayCustody);
1587
+ onCanonicalEmit?.(policyOnlyCanonical);
2039
1588
  if (effectiveVerdict === 'PASS') {
2040
1589
  console.log(chalk.green('✅ Policy check passed'));
2041
1590
  }
@@ -2055,8 +1604,18 @@ async function executePolicyOnlyMode(options, diffFiles, ignoreFilter, projectRo
2055
1604
  if (governance.audit.requireIntegrity && !auditIntegrityStatus.valid) {
2056
1605
  console.log(chalk.red(' Policy audit integrity check failed'));
2057
1606
  }
2058
- displayGovernanceInsights(governanceAnalysis, { explain: options.explain });
1607
+ (0, verify_render_1.displayGovernanceInsights)(chalk, governanceAnalysis, { explain: options.explain });
2059
1608
  console.log(chalk.dim(`\n${message}`));
1609
+ logCiPolicyOnlyOutcomeExplainability({
1610
+ ciModeEnabled,
1611
+ json: Boolean(options.json),
1612
+ verdict: effectiveVerdict,
1613
+ source,
1614
+ structuralViolations: policyOnlyStructural.violations,
1615
+ policyViolations,
1616
+ replayChecksum: policyOnlyReplayChecksum,
1617
+ replayMode: 'local-structural',
1618
+ });
2060
1619
  }
2061
1620
  await recordPolicyOnlyVerification({
2062
1621
  grade,
@@ -2092,6 +1651,15 @@ async function verifyCommand(options) {
2092
1651
  let lastCanonicalOutput = null;
2093
1652
  let lastEvidenceFallbackOutput = null;
2094
1653
  let evidenceFinalizeAttempted = false;
1654
+ const custodySource = ciModeEnabled ? 'ci' : 'cli';
1655
+ const custodyActor = ciModeEnabled ? 'ci-runner' : 'local-user';
1656
+ // ── Phase 1 Runtime Stability: create context early so all subsystems share it ──
1657
+ // Structural governance is NEVER gated by this context — it always runs.
1658
+ const runtimeCtx = (0, verify_runtime_stability_1.createVerifyRuntimeContext)(ciModeEnabled);
1659
+ // Large repo detection: sets largeRepoMode and emits cache-build recommendation.
1660
+ (0, verify_runtime_stability_1.applyLargeRepoProtection)(runtimeCtx, projectRoot);
1661
+ // Initial memory pressure check at entry.
1662
+ (0, verify_runtime_stability_1.applyMemoryPressureDegradation)(runtimeCtx);
2095
1663
  if (ciModeEnabled) {
2096
1664
  options.policyOnly = true;
2097
1665
  options.requirePlan = false;
@@ -2099,8 +1667,16 @@ async function verifyCommand(options) {
2099
1667
  options.asyncMode = false;
2100
1668
  }
2101
1669
  const captureEvidencePayload = (payload) => {
2102
- lastEvidenceFallbackOutput = payload;
2103
- 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;
2104
1680
  };
2105
1681
  const finalizeEvidence = (exitCode) => {
2106
1682
  if (!evidenceEnabled || evidenceFinalizeAttempted) {
@@ -2162,7 +1738,10 @@ async function verifyCommand(options) {
2162
1738
  let structuralViolations = [];
2163
1739
  let structuralRulesApplied = [];
2164
1740
  let structuralSuppressedCount = 0;
2165
- const emitVerifyJson = (payload) => {
1741
+ const emitVerifyJson = (payload, onEmit) => {
1742
+ // Check memory pressure immediately before emission (may have changed during long verify)
1743
+ (0, verify_runtime_stability_1.applyMemoryPressureDegradation)(runtimeCtx);
1744
+ const runtimeStabilityReport = (0, verify_runtime_stability_1.buildVerifyRuntimeReport)(runtimeCtx);
2166
1745
  const enrichedPayload = {
2167
1746
  ...payload,
2168
1747
  ciMode: payload.ciMode ?? ciModeEnabled,
@@ -2178,10 +1757,13 @@ async function verifyCommand(options) {
2178
1757
  structuralViolations: payload.structuralViolations ?? structuralViolations,
2179
1758
  structuralRulesApplied: payload.structuralRulesApplied ?? structuralRulesApplied,
2180
1759
  structuralSuppressedCount: payload.structuralSuppressedCount ?? structuralSuppressedCount,
1760
+ // Runtime stability transparency — always present
1761
+ runtimeStabilityReport,
2181
1762
  };
2182
1763
  lastEvidenceFallbackOutput = enrichedPayload;
2183
- emitCanonicalVerifyJson(enrichedPayload, (canonical) => {
1764
+ (0, verify_output_1.emitCanonicalVerifyJson)(enrichedPayload, (canonical) => {
2184
1765
  lastCanonicalOutput = canonical;
1766
+ onEmit?.(lastCanonicalOutput);
2185
1767
  });
2186
1768
  };
2187
1769
  if (!isGitRepository(projectRoot)) {
@@ -2622,8 +2204,19 @@ async function verifyCommand(options) {
2622
2204
  try {
2623
2205
  let contextNote = note;
2624
2206
  if (Array.isArray(changedFiles) && changedFiles.length > 0) {
2625
- const refreshed = (0, brain_context_1.refreshBrainContextForFiles)(projectRoot, brainScope, changedFiles);
2626
- contextNote = `${contextNote};indexed=${refreshed.indexed};removed=${refreshed.removed};skipped=${refreshed.skipped}`;
2207
+ // Runtime stability: skip brain context refresh if semantic layer is degraded
2208
+ // (memory pressure or time pressure). Structural verification is unaffected.
2209
+ if ((0, verify_runtime_stability_1.shouldSkipSemanticLayer)(runtimeCtx)) {
2210
+ contextNote = `${contextNote};semantic_skipped=runtime_pressure`;
2211
+ if (!ciModeEnabled && runtimeCtx.degradationReasons.length > 0) {
2212
+ console.log(chalk.yellow(`\n⚠️ Brain context refresh skipped: ${runtimeCtx.degradationReasons[runtimeCtx.degradationReasons.length - 1]}`));
2213
+ console.log(chalk.dim(' Deterministic structural governance continues unaffected.\n'));
2214
+ }
2215
+ }
2216
+ else {
2217
+ const refreshed = (0, brain_context_1.refreshBrainContextForFiles)(projectRoot, brainScope, changedFiles);
2218
+ contextNote = `${contextNote};indexed=${refreshed.indexed};removed=${refreshed.removed};skipped=${refreshed.skipped}`;
2219
+ }
2627
2220
  }
2628
2221
  (0, brain_context_1.recordBrainProgressEvent)(projectRoot, brainScope, {
2629
2222
  type: 'verify',
@@ -2775,34 +2368,70 @@ async function verifyCommand(options) {
2775
2368
  console.log(chalk.cyan('\n🔍 Local-only mode: deterministic structural verification (no API required)...'));
2776
2369
  }
2777
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);
2778
2373
  const blockingViolations = localStructural.violations.filter((v) => v.severity === 'BLOCKING');
2374
+ const advisoryViolations = localStructural.violations.filter((v) => v.severity !== 'BLOCKING');
2779
2375
  const localVerdict = blockingViolations.length > 0 ? 'FAIL' : 'PASS';
2780
2376
  const localGrade = blockingViolations.length > 0 ? 'F' : 'B';
2781
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);
2782
2431
  if (options.json) {
2783
- emitVerifyJson({
2784
- grade: localGrade,
2785
- score: localScore,
2786
- verdict: localVerdict,
2787
- violations: localStructural.violations.map((v) => ({
2788
- file: v.filePath,
2789
- rule: v.ruleId,
2790
- severity: v.severity === 'BLOCKING' ? 'block' : 'warn',
2791
- message: `${v.ruleName}: ${v.evidence.slice(0, 120)}`,
2792
- })),
2793
- adherenceScore: localScore,
2794
- bloatCount: 0,
2795
- bloatFiles: [],
2796
- plannedFilesModified: 0,
2797
- totalPlannedFiles: 0,
2798
- message: `Local-only structural verification: ${localStructural.violations.length} finding(s), ${blockingViolations.length} blocking.`,
2799
- scopeGuardPassed: true,
2800
- mode: 'local_only_structural',
2801
- policyOnly: true,
2802
- structuralViolations: localStructural.violations,
2803
- structuralBlockingCount: blockingViolations.length,
2804
- structuralRulesApplied: localStructural.rulesApplied,
2805
- changeContract: changeContractSummary,
2432
+ (0, verify_output_1.emitCanonicalVerifyJson)(localPayload, (canonical) => {
2433
+ applyCapturedReplayCustody(canonical, localReplayCustody);
2434
+ lastCanonicalOutput = canonical;
2806
2435
  });
2807
2436
  }
2808
2437
  else {
@@ -2818,7 +2447,9 @@ async function verifyCommand(options) {
2818
2447
  });
2819
2448
  }
2820
2449
  console.log(chalk.dim(`\n[Local-only] ${localStructural.violations.length} finding(s), ${blockingViolations.length} blocking. ` +
2821
- `Remove --local-only for full governance verification.\n`));
2450
+ `Structural analysis ran on this checkout only (no verify API).\n`));
2451
+ console.log(chalk.dim(' Replay: same commit + same flags → same structural findings.\n' +
2452
+ ' Full plan/adherence + remote verify: drop --local-only when policy allows network/API.\n'));
2822
2453
  }
2823
2454
  recordVerifyEvent(localVerdict, `local_only;structural=${localStructural.violations.length}`, diffFiles.map((f) => f.path));
2824
2455
  exitWithEvidence(blockingViolations.length > 0 ? 2 : 0);
@@ -3197,6 +2828,10 @@ async function verifyCommand(options) {
3197
2828
  missingExpectedFiles: 0,
3198
2829
  blockedFilesTouched: 0,
3199
2830
  actionMismatches: 0,
2831
+ serviceBoundaryBreaches: 0,
2832
+ infraBoundaryBreaches: 0,
2833
+ dependencyBoundaryBreaches: 0,
2834
+ sensitiveBoundaryBreaches: 0,
3200
2835
  expectedSymbols: advisoryContract.expectedSymbols?.length || 0,
3201
2836
  changedSymbols: 0,
3202
2837
  missingExpectedSymbols: 0,
@@ -3216,10 +2851,14 @@ async function verifyCommand(options) {
3216
2851
  }
3217
2852
  const message = 'No plan linked yet. Ran advisory verification for quick first-run experience. ' +
3218
2853
  'Use `neurcode plan` and `neurcode contract import --auto-detect --write-change-contract` for full enforcement.';
3219
- const advisorySignals = (0, advisory_signals_1.evaluateAdvisorySignals)({
3220
- diffFiles,
3221
- summary,
3222
- });
2854
+ // Runtime stability: skip advisory signals if advisory layer is degraded.
2855
+ // Structural rules always run regardless.
2856
+ const advisorySignals = (0, verify_runtime_stability_1.shouldSkipAdvisoryLayer)(runtimeCtx)
2857
+ ? []
2858
+ : (0, advisory_signals_1.evaluateAdvisorySignals)({ diffFiles, summary });
2859
+ if ((0, verify_runtime_stability_1.shouldSkipAdvisoryLayer)(runtimeCtx) && !options.json) {
2860
+ console.log(chalk.dim(' Advisory signals skipped (runtime pressure — structural governance unaffected).'));
2861
+ }
3223
2862
  const advisoryWarnCount = advisorySignals.filter((item) => item.severity === 'warn').length;
3224
2863
  // ── Advisory-first: always run structural rules, downgrade scope issues to advisory ──
3225
2864
  // Structural rules are deterministic and local — they MUST always run regardless of plan.
@@ -3302,8 +2941,8 @@ async function verifyCommand(options) {
3302
2941
  });
3303
2942
  }
3304
2943
  else {
3305
- printFirstRunAdvisoryMessage(options.demo === true);
3306
- 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);
3307
2946
  if (autoContractPath) {
3308
2947
  console.log(chalk.green(`✅ Auto-generated minimal advisory contract: ${autoContractPath}`));
3309
2948
  }
@@ -3341,6 +2980,7 @@ async function verifyCommand(options) {
3341
2980
  let planFiles = [];
3342
2981
  let planDependencies = [];
3343
2982
  let remotePlanSessionId = null;
2983
+ const activeEngineeringContext = (0, active_engineering_context_1.loadActiveEngineeringContext)(projectRoot);
3344
2984
  if (useLocalPlanSync) {
3345
2985
  const localIntent = (localPlanSync.intent || '').trim();
3346
2986
  const localConstraintText = localPlanSync.constraints.length > 0
@@ -3375,6 +3015,27 @@ async function verifyCommand(options) {
3375
3015
  }
3376
3016
  planFilesForVerification = [...new Set([...planFiles, ...localPlanExpectedFiles])];
3377
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
+ }
3378
3039
  // ── Intent-Aware Engine ─────────────────────────────────────────────
3379
3040
  // Run once we have both diffFiles and the resolved intent text.
3380
3041
  // Stored in outer scope so all emitCanonicalVerifyJson call sites can
@@ -3428,13 +3089,16 @@ async function verifyCommand(options) {
3428
3089
  expectedFiles: planFilesForVerification,
3429
3090
  expectedDependencies: planDependencies,
3430
3091
  diffFiles,
3431
- contextCandidates: planFilesForVerification,
3092
+ contextCandidates: activeEngineeringContext
3093
+ ? activeEngineeringContext.contextPack.selectedFiles.map((item) => item.path)
3094
+ : planFilesForVerification,
3432
3095
  orgGovernance: orgGovernanceSettings,
3433
3096
  requireSignedAiLogs: signedLogsRequired,
3434
3097
  signingKey: aiLogSigningKey,
3435
3098
  signingKeyId: aiLogSigningKeyId,
3436
3099
  signingKeys: aiLogSigningKeys,
3437
3100
  signer: aiLogSigner,
3101
+ activeEngineeringContext,
3438
3102
  });
3439
3103
  // Get sessionId from state file (.neurcode/state.json) first, then fallback to config
3440
3104
  // Fallback to sessionId from plan if not in state/config
@@ -3476,13 +3140,17 @@ async function verifyCommand(options) {
3476
3140
  }
3477
3141
  }
3478
3142
  // Step C: The Intersection Logic
3479
- const approvedSet = new Set([...planFilesForVerification, ...allowedFiles]);
3143
+ const approvedSet = new Set([
3144
+ ...planFilesForVerification,
3145
+ ...allowedFiles,
3146
+ ...(governanceResult.engineeringContext?.approvedScope?.files || []),
3147
+ ]);
3480
3148
  const violations = modifiedFiles.filter(f => !approvedSet.has(f));
3481
3149
  const filteredViolations = violations.filter((p) => !shouldIgnore(p));
3482
3150
  // Step D: The Block (only report scope violations for non-ignored files)
3483
3151
  if (filteredViolations.length > 0) {
3484
3152
  const criticalScopeViolations = expediteModeEnabled
3485
- ? 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'))
3486
3154
  : filteredViolations;
3487
3155
  const expediteScopeViolations = expediteModeEnabled
3488
3156
  ? filteredViolations.filter((file) => !criticalScopeViolations.includes(file))
@@ -3601,7 +3269,7 @@ async function verifyCommand(options) {
3601
3269
  }
3602
3270
  }
3603
3271
  if (governanceResult) {
3604
- displayGovernanceInsights(governanceResult, { explain: options.explain });
3272
+ (0, verify_render_1.displayGovernanceInsights)(chalk, governanceResult, { explain: options.explain });
3605
3273
  }
3606
3274
  // ── Intent Status in scope-violation path ──────────────────────
3607
3275
  if (intentEngineSummary) {
@@ -3670,7 +3338,7 @@ async function verifyCommand(options) {
3670
3338
  console.log(chalk.yellow(` • ${file}`));
3671
3339
  });
3672
3340
  console.log(chalk.dim(' Follow-up checklist:'));
3673
- EXPEDITE_FOLLOW_UP_CHECKLIST.forEach((item) => {
3341
+ verify_output_1.EXPEDITE_FOLLOW_UP_CHECKLIST.forEach((item) => {
3674
3342
  console.log(chalk.dim(` - ${item}`));
3675
3343
  });
3676
3344
  console.log(chalk.dim(' Note: Expedite Mode used\n'));
@@ -3971,8 +3639,14 @@ async function verifyCommand(options) {
3971
3639
  message: `Policy audit chain is invalid: ${auditIntegrityStatus.issues.join('; ') || 'unknown issue'}`,
3972
3640
  });
3973
3641
  }
3974
- (0, structural_policy_merge_1.mergeStructuralIntoPolicyViolations)(policyViolations, structuralViolations);
3975
- policyDecision = resolvePolicyDecisionFromViolations(policyViolations);
3642
+ if (governanceResult?.driftIntelligence) {
3643
+ const driftViolations = driftFindingsToVerificationViolations(governanceResult.driftIntelligence);
3644
+ policyViolations.push(...driftViolations.filter((item) => !shouldIgnore(item.file)));
3645
+ }
3646
+ // Structural violations are passed to the canonical pipeline via payload.structuralViolations
3647
+ // (see line ~5281). Do NOT merge them into policyViolations — that would create structural:*
3648
+ // duplicates that contaminate the canonical finding graph.
3649
+ policyDecision = (0, policy_decision_1.resolvePolicyDecisionFromViolations)(policyViolations);
3976
3650
  const policyExceptionsSummary = {
3977
3651
  sourceMode: policyExceptionResolution.mode,
3978
3652
  sourceWarning: policyExceptionResolution.warning,
@@ -4114,7 +3788,7 @@ async function verifyCommand(options) {
4114
3788
  message: violation,
4115
3789
  }));
4116
3790
  policyViolations.push(...intentProofPolicyViolations);
4117
- policyDecision = resolvePolicyDecisionFromViolations(policyViolations);
3791
+ policyDecision = (0, policy_decision_1.resolvePolicyDecisionFromViolations)(policyViolations);
4118
3792
  }
4119
3793
  if (!options.json) {
4120
3794
  if (intentProofSummary.pass) {
@@ -4193,7 +3867,7 @@ async function verifyCommand(options) {
4193
3867
  });
4194
3868
  }
4195
3869
  else {
4196
- displayChangeContractDrift(changeContractSummary, { advisory: false });
3870
+ (0, verify_render_1.displayChangeContractDrift)(chalk, changeContractSummary, { advisory: false });
4197
3871
  }
4198
3872
  await recordVerificationIfRequested(options, config, {
4199
3873
  grade: 'F',
@@ -4217,7 +3891,7 @@ async function verifyCommand(options) {
4217
3891
  exitWithEvidence(2);
4218
3892
  }
4219
3893
  else if (!changeContractEvaluation.valid && !options.json) {
4220
- displayChangeContractDrift(changeContractSummary, { advisory: true });
3894
+ (0, verify_render_1.displayChangeContractDrift)(chalk, changeContractSummary, { advisory: true });
4221
3895
  }
4222
3896
  }
4223
3897
  // Call verify API (or deterministic local evaluation for Plan Sync scope mode)
@@ -4451,9 +4125,50 @@ async function verifyCommand(options) {
4451
4125
  : {}),
4452
4126
  };
4453
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);
4454
4167
  // If JSON output requested, output JSON and exit
4455
4168
  if (options.json) {
4456
- emitVerifyJson(verifyEvidencePayload);
4169
+ emitVerifyJson(verifyEvidencePayload, (canonical) => {
4170
+ applyCapturedReplayCustody(canonical, mainReplayCustody);
4171
+ });
4457
4172
  await recordVerificationIfRequested(options, config, {
4458
4173
  grade,
4459
4174
  violations: violations,
@@ -4487,7 +4202,7 @@ async function verifyCommand(options) {
4487
4202
  // Display results (only if not in json mode; exclude ignored paths from bloat)
4488
4203
  if (!options.json) {
4489
4204
  const displayBloatFiles = (verifyResult.bloatFiles || []).filter((f) => !shouldIgnore(f));
4490
- displayVerifyResults({
4205
+ (0, verify_render_1.displayVerifyResults)(chalk, {
4491
4206
  ...verifyResult,
4492
4207
  verdict: effectiveVerdict,
4493
4208
  message: effectiveMessage,
@@ -4495,7 +4210,7 @@ async function verifyCommand(options) {
4495
4210
  bloatCount: displayBloatFiles.length,
4496
4211
  }, policyViolations, expediteModeEnabled, intentEngineIssues, intentEngineSummary, intentEngineFlowIssues, intentEngineRegressions, structuralViolations);
4497
4212
  if (governanceResult) {
4498
- displayGovernanceInsights(governanceResult, { explain: options.explain });
4213
+ (0, verify_render_1.displayGovernanceInsights)(chalk, governanceResult, { explain: options.explain });
4499
4214
  }
4500
4215
  if (aiDebtSummary.mode !== 'off') {
4501
4216
  const header = aiDebtSummary.mode === 'enforce'
@@ -4556,38 +4271,20 @@ async function verifyCommand(options) {
4556
4271
  console.log(chalk.dim(' Intent proof repo scan reached configured file/byte limit (truncated).'));
4557
4272
  }
4558
4273
  }
4274
+ console.log(chalk.dim('\n── Verification contract (this run) ──'));
4275
+ console.log(chalk.dim(` Verify source: ${verifySource === 'local_fallback'
4276
+ ? 'local deterministic fallback (verify API unavailable)'
4277
+ : 'verify API'}`));
4278
+ console.log(chalk.dim(` Structural findings: ${structuralViolations.length} ` +
4279
+ `(${structuralViolations.filter((v) => v.severity === 'BLOCKING').length} blocking, ` +
4280
+ `${structuralViolations.filter((v) => v.severity !== 'BLOCKING').length} advisory)`));
4281
+ console.log(chalk.dim(' Merge gates follow rule severity + policy: blocking structural findings are reproducible on this tree.'));
4282
+ console.log(chalk.dim(' Evidence trail: see `.neurcode/` on success (provenance, telemetry) — use `neurcode replay` / dashboard for audit parity.'));
4559
4283
  }
4560
4284
  // ── Governance Provenance Chain + Pilot Metrics ───────────────────────
4561
4285
  // Best-effort: never throws, never changes the verification outcome.
4562
4286
  try {
4563
- const structuralBlocking = structuralViolations.filter(v => v.severity === 'BLOCKING').length;
4564
- const structuralAdvisory = structuralViolations.filter(v => v.severity === 'ADVISORY').length;
4565
- const deterministicSigs = structuralViolations.filter(v => v.determinism === 'deterministic-structural').length;
4566
- const heuristicSigs = structuralViolations.filter(v => v.determinism === 'heuristic-advisory').length;
4567
- const trustScore = structuralViolations.length > 0
4568
- ? Math.round((deterministicSigs / structuralViolations.length) * 100)
4569
- : 100;
4570
- const prov = (0, governance_provenance_1.buildProvenanceRecord)({
4571
- repoRoot: projectRoot,
4572
- filesAnalyzed: diffFiles.length,
4573
- diffContext: `${options.base || 'HEAD'} vs working tree`,
4574
- planId: finalPlanId || null,
4575
- intentHash: null,
4576
- policyHash: null,
4577
- ruleIds: structuralRulesApplied,
4578
- blockingCount: policyViolations.filter((v) => v.severity === 'block').length + structuralBlocking,
4579
- advisoryCount: policyViolations.filter((v) => v.severity !== 'block').length + structuralAdvisory,
4580
- suppressedCount: structuralSuppressedCount,
4581
- structuralBlockingCount: structuralBlocking,
4582
- structuralAdvisoryCount: structuralAdvisory,
4583
- deterministicSignals: deterministicSigs,
4584
- heuristicSignals: heuristicSigs,
4585
- overallTrustScore: trustScore,
4586
- verdict: effectiveVerdict,
4587
- governanceDecision: governanceResult?.governanceDecision?.summary || 'automatic',
4588
- });
4589
- (0, governance_provenance_1.saveProvenanceRecord)(projectRoot, prov);
4590
- lastProvenanceRunId = prov.runId;
4287
+ lastProvenanceRunId = mainReplayCustody.provenanceRecord?.runId ?? lastProvenanceRunId;
4591
4288
  // Tally per-rule counts for pilot metrics
4592
4289
  const ruleCounts = {};
4593
4290
  for (const v of structuralViolations) {
@@ -4598,8 +4295,8 @@ async function verifyCommand(options) {
4598
4295
  verifyCount: 1,
4599
4296
  passCount: effectiveVerdict === 'PASS' ? 1 : 0,
4600
4297
  failCount: effectiveVerdict === 'FAIL' ? 1 : 0,
4601
- blockingCaught: prov.blockingCount,
4602
- 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),
4603
4300
  suppressions: structuralSuppressedCount,
4604
4301
  structuralCaught: structuralViolations.length,
4605
4302
  aiDebtDelta: aiDebtSummary?.score ?? 0,
@@ -4721,7 +4418,7 @@ async function verifyCommand(options) {
4721
4418
  catch (error) {
4722
4419
  if (options.json) {
4723
4420
  const errorMessage = error instanceof Error ? error.message : 'Unknown error';
4724
- emitCanonicalVerifyJson({
4421
+ (0, verify_output_1.emitCanonicalVerifyJson)({
4725
4422
  verdict: 'FAIL',
4726
4423
  summary: {
4727
4424
  totalFilesChanged: 0,
@@ -4972,6 +4669,8 @@ function buildGovernancePayload(governance, orgGovernanceSettings, options) {
4972
4669
  suspiciousChange: governance.suspiciousChange,
4973
4670
  changeJustification: governance.changeJustification,
4974
4671
  governanceDecision: governance.governanceDecision,
4672
+ engineeringContext: governance.engineeringContext,
4673
+ driftIntelligence: governance.driftIntelligence,
4975
4674
  aiChangeLog: {
4976
4675
  path: governance.aiChangeLogPath,
4977
4676
  auditPath: governance.aiChangeLogAuditPath,
@@ -4994,412 +4693,6 @@ function buildGovernancePayload(governance, orgGovernanceSettings, options) {
4994
4693
  ...(options?.aiDebt ? { aiDebt: options.aiDebt } : {}),
4995
4694
  };
4996
4695
  }
4997
- function displayGovernanceInsights(governance, options = {}) {
4998
- const maxUnexpectedFiles = options.maxUnexpectedFiles ?? 20;
4999
- const decision = governance.governanceDecision;
5000
- console.log(chalk.bold.white('\nBlast Radius:'));
5001
- console.log(chalk.dim(` Files touched: ${governance.blastRadius.filesChanged}`));
5002
- console.log(chalk.dim(` Functions impacted: ${governance.blastRadius.functionsAffected}`));
5003
- console.log(chalk.dim(` Modules impacted: ${governance.blastRadius.modulesAffected.join(', ') || 'none'}`));
5004
- if (governance.blastRadius.dependenciesAdded.length > 0) {
5005
- console.log(chalk.dim(` Dependencies added: ${governance.blastRadius.dependenciesAdded.join(', ')}`));
5006
- }
5007
- console.log(chalk.dim(` Risk level: ${governance.blastRadius.riskScore.toUpperCase()}`));
5008
- console.log(chalk.dim(` Governance decision: ${decision.decision.toUpperCase().replace('_', ' ')} | Avg relevance: ${decision.averageRelevanceScore}`));
5009
- console.log(chalk.dim(` Policy source: ${governance.policySources.mode}${governance.policySources.orgPolicy ? ' (org + local)' : ' (local)'}`));
5010
- console.log(governance.aiChangeLogIntegrity.valid
5011
- ? chalk.dim(` AI change-log integrity: valid (${governance.aiChangeLogIntegrity.signed ? 'signed' : 'unsigned'})`)
5012
- : chalk.red(` AI change-log integrity: invalid (${governance.aiChangeLogIntegrity.issues.join('; ') || 'unknown'})`));
5013
- if (governance.aiChangeLogIntegrity.signed) {
5014
- const keyId = typeof governance.aiChangeLogIntegrity.keyId === 'string'
5015
- ? governance.aiChangeLogIntegrity.keyId
5016
- : null;
5017
- const verifiedWithKeyId = typeof governance.aiChangeLogIntegrity.verifiedWithKeyId === 'string'
5018
- ? governance.aiChangeLogIntegrity.verifiedWithKeyId
5019
- : null;
5020
- if (keyId || verifiedWithKeyId) {
5021
- console.log(chalk.dim(` Signing key: ${keyId || 'n/a'}${verifiedWithKeyId ? ` (verified via ${verifiedWithKeyId})` : ''}`));
5022
- }
5023
- }
5024
- if (governance.suspiciousChange.flagged) {
5025
- console.log(chalk.red('\nSuspicious Change Detected'));
5026
- console.log(chalk.red(` Plan expected files: ${governance.suspiciousChange.expectedFiles} | AI modified files: ${governance.suspiciousChange.actualFiles}`));
5027
- governance.suspiciousChange.unexpectedFiles.slice(0, maxUnexpectedFiles).forEach((filePath) => {
5028
- console.log(chalk.red(` • ${filePath}`));
5029
- });
5030
- console.log(chalk.red(` Confidence: ${governance.suspiciousChange.confidence}`));
5031
- }
5032
- if (decision.lowRelevanceFiles.length > 0) {
5033
- console.log(chalk.yellow('\nLow Relevance Files'));
5034
- decision.lowRelevanceFiles.slice(0, 10).forEach((item) => {
5035
- console.log(chalk.yellow(` • ${item.file} (score ${item.relevanceScore}, ${item.planLink.replace('_', ' ')})`));
5036
- });
5037
- }
5038
- if (options.explain) {
5039
- console.log(chalk.bold.white('\nAI Change Justification:'));
5040
- console.log(chalk.dim(` Task: ${governance.changeJustification.task}`));
5041
- governance.changeJustification.changes.forEach((item) => {
5042
- const relevance = typeof item.relevanceScore === 'number' ? ` [score ${item.relevanceScore}]` : '';
5043
- console.log(chalk.dim(` • ${item.file} — ${item.reason}${relevance}`));
5044
- });
5045
- }
5046
- }
5047
- function displayChangeContractDrift(summary, options = { advisory: false }) {
5048
- const groups = (0, change_contract_1.groupChangeContractViolations)(summary.violations.map((item) => ({
5049
- code: item.code,
5050
- message: item.message,
5051
- ...(item.file ? { file: item.file } : {}),
5052
- ...(item.symbol ? { symbol: item.symbol } : {}),
5053
- ...(item.symbolType ? { symbolType: item.symbolType } : {}),
5054
- ...(item.expected ? { expected: item.expected } : {}),
5055
- ...(item.actual ? { actual: item.actual } : {}),
5056
- })));
5057
- if (groups.length === 0)
5058
- return;
5059
- const maxItemsPerGroup = options.maxItemsPerGroup ?? 12;
5060
- const header = options.advisory
5061
- ? chalk.yellow('\nWARN ⚠️ Change contract drift detected')
5062
- : chalk.red('\nFAIL ❌ Change contract enforcement failed');
5063
- console.log(header);
5064
- for (const group of groups) {
5065
- console.log(chalk.white(`\n${group.title}:`));
5066
- group.items.slice(0, maxItemsPerGroup).forEach((entry) => {
5067
- console.log(` - ${entry}`);
5068
- });
5069
- if (group.items.length > maxItemsPerGroup) {
5070
- console.log(chalk.dim(` - ... ${group.items.length - maxItemsPerGroup} more`));
5071
- }
5072
- console.log(chalk.dim(` Why it matters: ${group.impact}`));
5073
- }
5074
- console.log(chalk.dim('\nSummary:'));
5075
- console.log(chalk.dim('Implementation deviates from intended contract.'));
5076
- console.log(chalk.dim(`Contract path: ${summary.path}`));
5077
- }
5078
- /**
5079
- * Display verification results in a formatted report card
5080
- */
5081
- function displayVerifyResults(result, policyViolations, expediteModeUsed = false, intentIssuesForDisplay = [], intentSummaryForDisplay = null, flowIssuesForDisplay = [], regressionsForDisplay = [], structuralViolationsForDisplay = []) {
5082
- // ── Header ────────────────────────────────────────────────────────────────
5083
- const headerLabel = result.verdict === 'PASS'
5084
- ? chalk.bold.green('\n✅ VERIFICATION PASSED')
5085
- : result.verdict === 'WARN'
5086
- ? chalk.bold.yellow('\n⚠️ VERIFICATION PASSED WITH WARNINGS')
5087
- : chalk.bold.red('\n❌ VERIFICATION FAILED');
5088
- console.log(headerLabel);
5089
- // ── Intent Status block ──────────────────────────────────────────────────
5090
- if (intentSummaryForDisplay) {
5091
- const s = intentSummaryForDisplay;
5092
- const domainLabel = s.domain.charAt(0).toUpperCase() + s.domain.slice(1);
5093
- const confColor = s.confidence === 'HIGH'
5094
- ? chalk.green
5095
- : s.confidence === 'MEDIUM'
5096
- ? chalk.yellow
5097
- : chalk.red;
5098
- // V4: weighted coverage bar
5099
- const wCovPct = s.weightedCoverage != null
5100
- ? Math.round(s.weightedCoverage * 100)
5101
- : s.coveragePct;
5102
- const barWidth = 20;
5103
- const filled = Math.round((wCovPct / 100) * barWidth);
5104
- const bar = chalk.cyan('█'.repeat(filled)) + chalk.dim('░'.repeat(barWidth - filled));
5105
- // V4: system status label
5106
- const sysStatus = s.status;
5107
- const statusLabel = sysStatus === 'CRITICAL'
5108
- ? chalk.bold.red('[CRITICAL]')
5109
- : sysStatus === 'AT RISK'
5110
- ? chalk.bold.yellow('[AT RISK]')
5111
- : chalk.bold.green('[SECURE]');
5112
- console.log(chalk.bold('\n━━━ INTENT STATUS ━━━━━━━━━━━━━━━━━━━━━━'));
5113
- console.log(` ${statusLabel} ${chalk.bold(`${domainLabel} Implementation:`)} ${bar} ${chalk.bold(`${wCovPct}%`)} (weighted)`);
5114
- console.log(` Confidence: ${confColor(s.confidence)}`);
5115
- if (s.foundList.length > 0) {
5116
- const foundLabels = s.foundList
5117
- .map((k) => k.split('-').map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(' '))
5118
- .slice(0, 4);
5119
- console.log(` Found: ${chalk.green(foundLabels.join(', '))}${s.foundList.length > 4 ? chalk.dim(` +${s.foundList.length - 4} more`) : ''}`);
5120
- }
5121
- // V4: show critical missing and non-critical missing separately
5122
- const critMissing = s.criticalMissing ?? [];
5123
- const otherMissing = s.missing.filter((k) => !critMissing.includes(k));
5124
- if (critMissing.length > 0) {
5125
- console.log(` ${chalk.bold.red('Critical missing:')}`);
5126
- critMissing.forEach((k) => {
5127
- const label = k.split('-').map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(' ');
5128
- console.log(chalk.red(` ✗ ${label}`));
5129
- });
5130
- }
5131
- if (otherMissing.length > 0) {
5132
- console.log(` ${chalk.bold.yellow('Missing:')}`);
5133
- otherMissing.forEach((k) => {
5134
- const label = k.split('-').map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(' ');
5135
- console.log(chalk.yellow(` • ${label}`));
5136
- });
5137
- }
5138
- if (critMissing.length === 0 && otherMissing.length === 0) {
5139
- console.log(` Missing: ${chalk.green('none — all components detected')}`);
5140
- }
5141
- console.log(chalk.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
5142
- }
5143
- // ── Triage items ──────────────────────────────────────────────────────────
5144
- const maxBlockingItems = 20;
5145
- const maxAdvisoryItems = 8;
5146
- const maxExpediteItems = 12;
5147
- const policyItems = policyViolations || [];
5148
- const isBlockingSeverity = (severityRaw) => {
5149
- const normalized = String(severityRaw || '').toLowerCase();
5150
- return normalized === 'block' || normalized === 'critical' || normalized === 'high';
5151
- };
5152
- const scopeItems = result.bloatFiles.map((file) => ({
5153
- file,
5154
- message: 'File modified outside intended scope',
5155
- policy: 'scope_guard',
5156
- }));
5157
- const policyTriageItems = policyItems.map((item) => ({
5158
- file: item.file,
5159
- message: item.message || item.rule,
5160
- policy: item.rule || 'policy_violation',
5161
- severity: item.severity,
5162
- }));
5163
- // Structural rule violations — split by severity
5164
- const structuralBlocking = structuralViolationsForDisplay
5165
- .filter((v) => v.severity === 'BLOCKING')
5166
- .map((v) => ({
5167
- file: v.filePath,
5168
- message: `${v.ruleId} · ${v.ruleName} (line ${v.line}) — ${v.operationalRisk}`,
5169
- }));
5170
- const structuralAdvisory = structuralViolationsForDisplay
5171
- .filter((v) => v.severity === 'ADVISORY')
5172
- .map((v) => ({
5173
- file: v.filePath,
5174
- message: `${v.ruleId} · ${v.ruleName} (line ${v.line}) — ${v.operationalRisk}`,
5175
- }));
5176
- let blockingItems = [
5177
- ...scopeItems.map((item) => ({
5178
- file: item.file,
5179
- message: item.message,
5180
- })),
5181
- ...policyTriageItems
5182
- .filter((item) => isBlockingSeverity(item.severity))
5183
- .map((item) => ({
5184
- file: item.file,
5185
- message: item.message,
5186
- })),
5187
- ...structuralBlocking,
5188
- ];
5189
- let advisoryItems = [
5190
- ...policyTriageItems
5191
- .filter((item) => !isBlockingSeverity(item.severity))
5192
- .map((item) => ({
5193
- file: item.file,
5194
- message: item.message,
5195
- })),
5196
- ...structuralAdvisory,
5197
- ];
5198
- let expediteItems = [];
5199
- if (expediteModeUsed) {
5200
- blockingItems = [
5201
- ...scopeItems
5202
- .filter((item) => isCriticalScopeBreach(item.file, item.message))
5203
- .map((item) => ({ file: item.file, message: item.message })),
5204
- ...policyTriageItems
5205
- .filter((item) => isSecurityOrAuthViolation(item.file, item.policy, item.message))
5206
- .map((item) => ({ file: item.file, message: item.message })),
5207
- ];
5208
- expediteItems = [
5209
- ...scopeItems
5210
- .filter((item) => !isCriticalScopeBreach(item.file, item.message))
5211
- .map((item) => ({ file: item.file, message: item.message })),
5212
- ...policyTriageItems
5213
- .filter((item) => !isSecurityOrAuthViolation(item.file, item.policy, item.message))
5214
- .map((item) => ({ file: item.file, message: item.message })),
5215
- ];
5216
- advisoryItems = [];
5217
- }
5218
- // ── Counts ────────────────────────────────────────────────────────────────
5219
- console.log(blockingItems.length > 0
5220
- ? chalk.red(`Blocking Issues: ${blockingItems.length}`)
5221
- : chalk.dim('Blocking Issues: 0'));
5222
- if (expediteModeUsed) {
5223
- console.log(chalk.yellow(`Expedite Issues: ${expediteItems.length}`));
5224
- }
5225
- else {
5226
- console.log(advisoryItems.length > 0
5227
- ? chalk.yellow(`Advisory Issues: ${advisoryItems.length}`)
5228
- : chalk.dim('Advisory Issues: 0'));
5229
- }
5230
- console.log(chalk.dim(`Plan adherence: ${result.plannedFilesModified}/${result.totalPlannedFiles} files (${result.adherenceScore}%)`));
5231
- // ── Top issues ────────────────────────────────────────────────────────────
5232
- const topIssues = [
5233
- ...blockingItems,
5234
- ...(expediteModeUsed ? expediteItems : advisoryItems),
5235
- ].slice(0, 2);
5236
- if (topIssues.length > 0) {
5237
- console.log(chalk.bold('\nTop Issues:'));
5238
- topIssues.forEach((item, i) => {
5239
- console.log(` ${i + 1}. ${item.message} → ${chalk.cyan(item.file)}`);
5240
- });
5241
- }
5242
- // ── Detailed lists ────────────────────────────────────────────────────────
5243
- if (blockingItems.length > 0) {
5244
- console.log(chalk.red(`\nBLOCKING (${blockingItems.length})`));
5245
- blockingItems.slice(0, maxBlockingItems).forEach((item) => {
5246
- console.log(` - ${item.file}: ${item.message}`);
5247
- });
5248
- if (blockingItems.length > maxBlockingItems) {
5249
- console.log(chalk.dim(` - ... ${blockingItems.length - maxBlockingItems} more`));
5250
- }
5251
- }
5252
- if (advisoryItems.length > 0) {
5253
- console.log(chalk.yellow(`\nADVISORY (${advisoryItems.length})`));
5254
- advisoryItems.slice(0, maxAdvisoryItems).forEach((item) => {
5255
- console.log(` - ${item.file}: ${item.message}`);
5256
- });
5257
- if (advisoryItems.length > maxAdvisoryItems) {
5258
- console.log(chalk.dim(` - ... ${advisoryItems.length - maxAdvisoryItems} more (summarized)`));
5259
- }
5260
- }
5261
- if (expediteModeUsed && expediteItems.length > 0) {
5262
- console.log(chalk.yellow(`\nEXPEDITE (requires follow-up) (${expediteItems.length})`));
5263
- expediteItems.slice(0, maxExpediteItems).forEach((item) => {
5264
- console.log(` - ${item.file}: ${item.message}`);
5265
- });
5266
- if (expediteItems.length > maxExpediteItems) {
5267
- console.log(chalk.dim(` - ... ${expediteItems.length - maxExpediteItems} more (summarized)`));
5268
- }
5269
- console.log(chalk.dim(' Follow-up checklist:'));
5270
- EXPEDITE_FOLLOW_UP_CHECKLIST.forEach((checkItem) => {
5271
- console.log(chalk.dim(` - ${checkItem}`));
5272
- });
5273
- console.log(chalk.dim(' Note: Expedite Mode used'));
5274
- }
5275
- // ── Intent issues ─────────────────────────────────────────────────────────
5276
- if (intentIssuesForDisplay.length > 0) {
5277
- console.log(chalk.magenta(`\nINTENT ISSUES (${intentIssuesForDisplay.length})`));
5278
- intentIssuesForDisplay.forEach((issue) => {
5279
- const label = issue.severity === 'high' ? chalk.red('[HIGH]') : chalk.yellow('[MEDIUM]');
5280
- const typeLabel = issue.type === 'missing' ? 'Missing' : issue.type === 'misplaced' ? 'Misplaced' : 'Partial';
5281
- console.log(` ${label} ${typeLabel}: ${issue.message}`);
5282
- });
5283
- }
5284
- // ── Flow Validation ───────────────────────────────────────────────────────
5285
- if (flowIssuesForDisplay.length > 0) {
5286
- console.log(chalk.bold('\n━━━ FLOW VALIDATION ━━━━━━━━━━━━━━━━━━━━━'));
5287
- flowIssuesForDisplay.forEach((issue) => {
5288
- const label = issue.severity === 'high' ? chalk.red('[HIGH]') : chalk.yellow('[MEDIUM]');
5289
- const typeIcon = issue.type === 'missing-flow' ? '⛓' : issue.type === 'misplaced-flow' ? '⚠' : '⊘';
5290
- console.log(` ${label} ${typeIcon} ${issue.message}`);
5291
- if (issue.files && issue.files.length > 0) {
5292
- const display = issue.files.slice(0, 3);
5293
- console.log(chalk.dim(` → ${display.join(', ')}${issue.files.length > 3 ? ` +${issue.files.length - 3} more` : ''}`));
5294
- }
5295
- });
5296
- console.log(chalk.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
5297
- }
5298
- // ── Regression Analysis ───────────────────────────────────────────────────
5299
- if (regressionsForDisplay.length > 0) {
5300
- console.log(chalk.bold.red('\n━━━ REGRESSION ANALYSIS ━━━━━━━━━━━━━━━━━'));
5301
- regressionsForDisplay.forEach((reg) => {
5302
- const icon = reg.type === 'coverage-regression' ? '📉' :
5303
- reg.type === 'critical-regression' ? '🔴' :
5304
- reg.type === 'flow-regression' ? '⛓' : '⚠';
5305
- console.log(` ${chalk.red('[REGRESSION]')} ${icon} ${reg.message}`);
5306
- });
5307
- console.log(chalk.bold.red('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
5308
- }
5309
- // ── Structural Rule Engine ─────────────────────────────────────────────────
5310
- // Display detailed structural violations with AST evidence and determinism labels.
5311
- if (structuralViolationsForDisplay.length > 0) {
5312
- try {
5313
- const report = (0, explainability_1.buildViolationReport)(structuralViolationsForDisplay, '');
5314
- const formatter = new explainability_1.ViolationFormatter();
5315
- const blocking = report.blocking;
5316
- const advisory = report.advisory;
5317
- if (blocking.length > 0 || advisory.length > 0) {
5318
- console.log(chalk.bold('\n━━━ STRUCTURAL ANALYSIS ━━━━━━━━━━━━━━━━━'));
5319
- blocking.slice(0, 5).forEach((v) => {
5320
- const detLabel = v.determinism === 'deterministic-structural'
5321
- ? chalk.cyan('⚙ AST-verified')
5322
- : chalk.yellow('⚡ heuristic');
5323
- console.log(chalk.red(`\n ● ${v.ruleId} [BLOCKING] ${detLabel} · confidence ${Math.round(v.confidence * 100)}%`));
5324
- console.log(chalk.bold(` ${v.filePath}:${v.line}`));
5325
- console.log(chalk.dim(` Pattern: ${v.evidence.matchReason}`));
5326
- if (v.evidence.codeSnippet) {
5327
- console.log(chalk.dim(` Code: ${v.evidence.codeSnippet.slice(0, 100)}`));
5328
- }
5329
- console.log(chalk.yellow(` Risk: ${v.operationalRisk}`));
5330
- console.log(chalk.green(` Fix: ${v.remediation}`));
5331
- });
5332
- if (blocking.length > 5) {
5333
- console.log(chalk.dim(`\n ... ${blocking.length - 5} more blocking structural violations`));
5334
- }
5335
- advisory.slice(0, 3).forEach((v) => {
5336
- console.log(chalk.yellow(`\n ○ ${v.ruleId} [ADVISORY] ⚡ heuristic · confidence ${Math.round(v.confidence * 100)}%`));
5337
- console.log(chalk.dim(` ${v.filePath}:${v.line} — ${v.operationalRisk}`));
5338
- });
5339
- if (advisory.length > 3) {
5340
- console.log(chalk.dim(` ... ${advisory.length - 3} more advisory structural violations`));
5341
- }
5342
- const deterministicCount = report.deterministicCount;
5343
- const heuristicCount = report.heuristicCount;
5344
- console.log(chalk.dim(`\n Determinism: ${deterministicCount} AST-verified · ${heuristicCount} heuristic`));
5345
- console.log(chalk.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
5346
- }
5347
- }
5348
- catch {
5349
- // Non-fatal: explainability rendering must never break verification display
5350
- }
5351
- }
5352
- const hasAnyIssue = blockingItems.length > 0 ||
5353
- advisoryItems.length > 0 ||
5354
- expediteItems.length > 0 ||
5355
- intentIssuesForDisplay.length > 0 ||
5356
- flowIssuesForDisplay.length > 0 ||
5357
- regressionsForDisplay.length > 0;
5358
- if (!hasAnyIssue) {
5359
- console.log(chalk.green('\nNo issues detected.'));
5360
- }
5361
- // ── Next step ─────────────────────────────────────────────────────────────
5362
- if (hasAnyIssue) {
5363
- console.log(chalk.bold('\nNext step:'));
5364
- console.log(` ${chalk.cyan('neurcode fix')}`);
5365
- console.log(chalk.dim(' or: neurcode fix --apply-safe (auto-apply high-confidence patches)'));
5366
- }
5367
- console.log(chalk.dim(`\nDetails: ${result.message}\n`));
5368
- }
5369
- function printFirstRunAdvisoryMessage(demoMode) {
5370
- console.log(chalk.cyan('\nNeurcode first-run advisory mode'));
5371
- console.log(chalk.dim('Neurcode checks if your AI-generated code matches your intended plan.'));
5372
- console.log(chalk.dim('To get full enforcement:'));
5373
- console.log(chalk.dim('1. Define a plan'));
5374
- console.log(chalk.dim('2. Generate a contract'));
5375
- console.log(chalk.dim('Running in advisory mode for now.\n'));
5376
- if (demoMode) {
5377
- console.log(chalk.dim('Demo mode: this run is intentionally non-blocking to make evaluation easy.'));
5378
- }
5379
- }
5380
- function printAdvisorySignals(signals, demoMode) {
5381
- if (signals.length === 0) {
5382
- if (demoMode) {
5383
- console.log(chalk.dim('No high-signal advisory findings detected for this diff.'));
5384
- }
5385
- return;
5386
- }
5387
- console.log(chalk.yellow('\nAdvisory findings (non-blocking):'));
5388
- for (const signal of signals) {
5389
- const severityLabel = signal.severity === 'warn' ? chalk.yellow('[warn]') : chalk.dim('[info]');
5390
- console.log(`${severityLabel} ${signal.title}`);
5391
- console.log(chalk.dim(` ${signal.detail}`));
5392
- console.log(chalk.dim(` Confidence: ${signal.confidence.toUpperCase()} (advisory-only)`));
5393
- if (signal.evidence.length > 0) {
5394
- console.log(chalk.dim(` Evidence: ${signal.evidence.join(', ')}`));
5395
- }
5396
- console.log(chalk.dim(` Structural gap: ${signal.structuralCoverageGap}`));
5397
- console.log(chalk.dim(` Uncertainty: ${signal.uncertainty}`));
5398
- signal.files.forEach((file) => {
5399
- console.log(chalk.dim(` - ${file}`));
5400
- });
5401
- }
5402
- }
5403
4696
  function buildMinimalAdvisoryContractFromDiff(diffFiles, fallbackPlanId) {
5404
4697
  const expectedFiles = [...new Set(diffFiles.map((file) => toUnixPath(file.path)).filter(Boolean))];
5405
4698
  const planFiles = expectedFiles.map((path) => {