avorelo 0.3.8 → 0.4.0-rc.2

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 (394) hide show
  1. package/LICENSE +16 -23
  2. package/README.md +62 -72
  3. package/bin/avorelo +9 -0
  4. package/package.json +147 -139
  5. package/scripts/README.md +40 -0
  6. package/scripts/avorelo-session-collision-guard.js +540 -0
  7. package/scripts/avorelo-workstream-preflight.js +918 -0
  8. package/scripts/cco-dashboard.js +346 -0
  9. package/scripts/cco-posttooluse.js +504 -0
  10. package/scripts/cco-pretooluse.js +381 -0
  11. package/scripts/cco-status.js +540 -0
  12. package/scripts/cco-stop.js +77 -0
  13. package/scripts/cco-userprompt-submit.js +83 -0
  14. package/scripts/lib/activation/account-state.js +37 -0
  15. package/scripts/lib/activation/activate-command.js +114 -0
  16. package/scripts/lib/activation/activation-runner.js +607 -0
  17. package/scripts/lib/activation/activation-self-healing.js +481 -0
  18. package/scripts/lib/activation/activation-state.js +96 -0
  19. package/scripts/lib/activation/activation-summary.js +241 -0
  20. package/scripts/lib/activation/adapters/claude-code.js +77 -0
  21. package/scripts/lib/activation/adapters/codex-cli.js +52 -0
  22. package/scripts/lib/activation/adapters/cursor.js +37 -0
  23. package/scripts/lib/activation/adapters/github-agent.js +39 -0
  24. package/scripts/lib/activation/adapters/terminal.js +42 -0
  25. package/scripts/lib/activation/adapters/vscode.js +39 -0
  26. package/scripts/lib/activation/adapters/windsurf.js +37 -0
  27. package/scripts/lib/activation/ai-surface-detector.js +151 -0
  28. package/scripts/lib/activation/connect-account.js +145 -0
  29. package/scripts/lib/activation/detect-environment.js +75 -0
  30. package/scripts/lib/activation/detect-hosts.js +62 -0
  31. package/scripts/lib/activation/format-activation-output.js +109 -0
  32. package/scripts/lib/activation/next-action.js +43 -0
  33. package/scripts/lib/activation/repair-engine.js +219 -0
  34. package/scripts/lib/activation/run-entry.js +346 -0
  35. package/scripts/lib/activation-distribution-readiness.js +507 -0
  36. package/scripts/lib/adapter-conformance.js +176 -0
  37. package/scripts/lib/adapter-readiness.js +428 -0
  38. package/scripts/lib/adapter-safety-boundaries.js +335 -0
  39. package/scripts/lib/adapter-technical-readiness-gate.js +205 -0
  40. package/scripts/lib/agent-access-governance.js +455 -0
  41. package/scripts/lib/agent-activation-readiness.js +878 -0
  42. package/scripts/lib/agent-enforcement.js +765 -0
  43. package/scripts/lib/agent-evolution-loop.js +479 -0
  44. package/scripts/lib/agent-policy-profile.js +210 -0
  45. package/scripts/lib/agent-security/action-evaluator.js +507 -0
  46. package/scripts/lib/agent-security/adapter-registry.js +98 -0
  47. package/scripts/lib/agent-security/auto-policy.js +139 -0
  48. package/scripts/lib/agent-security/bounded-scan.js +93 -0
  49. package/scripts/lib/agent-security/enforcement-adapter.js +174 -0
  50. package/scripts/lib/agent-security/enforcement-engine.js +1129 -0
  51. package/scripts/lib/agent-security/file-write-adapter.js +183 -0
  52. package/scripts/lib/agent-security/file-write-rules.js +178 -0
  53. package/scripts/lib/agent-security/index.js +3342 -0
  54. package/scripts/lib/agent-security/instruction-risk.js +181 -0
  55. package/scripts/lib/agent-security/mcp-action-adapter.js +185 -0
  56. package/scripts/lib/agent-security/mcp-action-rules.js +184 -0
  57. package/scripts/lib/agent-security/package-action-adapter.js +175 -0
  58. package/scripts/lib/agent-security/package-action-rules.js +233 -0
  59. package/scripts/lib/agent-security/performance.js +148 -0
  60. package/scripts/lib/agent-security/permission-minimizer.js +403 -0
  61. package/scripts/lib/agent-security/scan-cache.js +74 -0
  62. package/scripts/lib/agent-security/source-trust.js +146 -0
  63. package/scripts/lib/ai-efficiency-dogfood-pack.js +437 -0
  64. package/scripts/lib/ai-efficiency-eval.js +207 -0
  65. package/scripts/lib/ai-efficiency-guard-contract.js +209 -0
  66. package/scripts/lib/ai-efficiency-measured-value.js +106 -0
  67. package/scripts/lib/ai-install-prompt.js +293 -0
  68. package/scripts/lib/ai-task-inference.js +155 -0
  69. package/scripts/lib/ai-value-ledger.js +121 -0
  70. package/scripts/lib/ai-value-proof-table.js +151 -0
  71. package/scripts/lib/ai-work-efficiency-routing.js +623 -0
  72. package/scripts/lib/ai-workspace-hygiene.js +1499 -0
  73. package/scripts/lib/alpha-activation.js +520 -0
  74. package/scripts/lib/alpha-feedback.js +263 -0
  75. package/scripts/lib/alpha-readiness-gate.js +332 -0
  76. package/scripts/lib/anti-gaming.js +169 -0
  77. package/scripts/lib/artifact-health.js +431 -0
  78. package/scripts/lib/artifact-receipt-hygiene.js +619 -0
  79. package/scripts/lib/attribution.js +180 -0
  80. package/scripts/lib/audit.js +289 -0
  81. package/scripts/lib/avorelo-skill-registry.js +810 -0
  82. package/scripts/lib/batch-jobs.js +71 -0
  83. package/scripts/lib/brain-pack.js +578 -0
  84. package/scripts/lib/brand-boundary.js +424 -0
  85. package/scripts/lib/brand.js +74 -0
  86. package/scripts/lib/browser-capability.js +1048 -0
  87. package/scripts/lib/browser-proof-governed-workflow.js +1020 -0
  88. package/scripts/lib/browser-proof-preflight.js +321 -0
  89. package/scripts/lib/cache-readiness.js +296 -0
  90. package/scripts/lib/canonical-reentry.js +162 -0
  91. package/scripts/lib/capability-contract-validator.js +177 -0
  92. package/scripts/lib/capability-hygiene.js +504 -0
  93. package/scripts/lib/capability-intake-quarantine.js +789 -0
  94. package/scripts/lib/capability-kernel-registry.js +566 -0
  95. package/scripts/lib/capability-kernel.js +202 -0
  96. package/scripts/lib/capability-manifest.js +255 -0
  97. package/scripts/lib/capability-outcome-normalizer.js +104 -0
  98. package/scripts/lib/capability-packs.js +314 -0
  99. package/scripts/lib/capability-recommender.js +513 -0
  100. package/scripts/lib/capability-registry.js +1062 -0
  101. package/scripts/lib/carry-forward-surfacing.js +194 -0
  102. package/scripts/lib/ccusage-adapter.js +188 -0
  103. package/scripts/lib/claude-session-evidence/index.js +1027 -0
  104. package/scripts/lib/cli-output-sanitizer.js +118 -0
  105. package/scripts/lib/company-loop.js +1149 -0
  106. package/scripts/lib/config.js +641 -0
  107. package/scripts/lib/context-acquisition-plan.js +359 -0
  108. package/scripts/lib/context-authority-rules.js +138 -0
  109. package/scripts/lib/context-budget-guard.js +170 -0
  110. package/scripts/lib/context-budget-scanner.js +257 -0
  111. package/scripts/lib/context-budget.js +167 -0
  112. package/scripts/lib/context-claim-safety.js +116 -0
  113. package/scripts/lib/context-compiler.js +236 -0
  114. package/scripts/lib/context-optimizer.js +715 -0
  115. package/scripts/lib/context-packet.js +121 -0
  116. package/scripts/lib/context-receipt.js +57 -0
  117. package/scripts/lib/context-reduction-plan.js +179 -0
  118. package/scripts/lib/context-safety.js +88 -0
  119. package/scripts/lib/context-savings-engine.js +158 -0
  120. package/scripts/lib/context-source-registry.js +393 -0
  121. package/scripts/lib/context-task-profiles.js +147 -0
  122. package/scripts/lib/cost-abuse-guard.js +975 -0
  123. package/scripts/lib/cost-evidence.js +287 -0
  124. package/scripts/lib/cost-to-outcome-signals.js +639 -0
  125. package/scripts/lib/cross-host-install-plan.js +308 -0
  126. package/scripts/lib/cross-host-install-readiness.js +242 -0
  127. package/scripts/lib/cross-host-value-flow.js +268 -0
  128. package/scripts/lib/dashboard.js +1056 -0
  129. package/scripts/lib/data-permissions-guard.js +1187 -0
  130. package/scripts/lib/deployment-environment-guard.js +1236 -0
  131. package/scripts/lib/design-partner-feedback.js +346 -0
  132. package/scripts/lib/dogfood-evidence-freshness.js +567 -0
  133. package/scripts/lib/efficiency-receipt.js +354 -0
  134. package/scripts/lib/entitlements.js +100 -0
  135. package/scripts/lib/execution-packet.js +559 -0
  136. package/scripts/lib/execution-route-governor.js +179 -0
  137. package/scripts/lib/experimentation-events.js +547 -0
  138. package/scripts/lib/external-capability-compliance.js +107 -0
  139. package/scripts/lib/external-readiness/ai-visibility/ai-visibility-runner.js +322 -0
  140. package/scripts/lib/external-readiness/calibration/benchmark-site-catalog.js +121 -0
  141. package/scripts/lib/external-readiness/calibration/completion-gate.js +112 -0
  142. package/scripts/lib/external-readiness/calibration/real-site-benchmark-runner.js +284 -0
  143. package/scripts/lib/external-readiness/drift-detector.js +100 -0
  144. package/scripts/lib/external-readiness/endpoint-prober.js +332 -0
  145. package/scripts/lib/external-readiness/external-readiness-runner.js +112 -0
  146. package/scripts/lib/external-readiness/full-agent-readiness-scorer.js +220 -0
  147. package/scripts/lib/external-readiness/golden-path-runner.js +333 -0
  148. package/scripts/lib/external-readiness/inside-out-runner.js +118 -0
  149. package/scripts/lib/external-readiness/proof-receipt-writer.js +112 -0
  150. package/scripts/lib/external-readiness/safe-fix-executor.js +117 -0
  151. package/scripts/lib/external-readiness/skills/category-boundary-guard.js +30 -0
  152. package/scripts/lib/external-readiness/skills/pmm-positioning-guard.js +69 -0
  153. package/scripts/lib/external-readiness/skills/safe-fix-policy.js +35 -0
  154. package/scripts/lib/external-user-simulation.js +166 -0
  155. package/scripts/lib/failure-recovery-readiness.js +81 -0
  156. package/scripts/lib/failure-recovery.js +419 -0
  157. package/scripts/lib/feedback-intelligence.js +537 -0
  158. package/scripts/lib/feedback-signals.js +205 -0
  159. package/scripts/lib/file-integrity.js +68 -0
  160. package/scripts/lib/fsx.js +129 -0
  161. package/scripts/lib/full-readiness-gate.js +451 -0
  162. package/scripts/lib/governed-action-exposure-primitives.js +1514 -0
  163. package/scripts/lib/governed-exposure-planner.js +186 -0
  164. package/scripts/lib/governed-workspace-hygiene.js +946 -0
  165. package/scripts/lib/guard-suite-common.js +412 -0
  166. package/scripts/lib/guidance-builder.js +174 -0
  167. package/scripts/lib/hook-apply.js +1075 -0
  168. package/scripts/lib/hook-baseline.js +310 -0
  169. package/scripts/lib/hook-config-preview.js +275 -0
  170. package/scripts/lib/hook-contracts.js +290 -0
  171. package/scripts/lib/hook-safety-boundary-readiness.js +80 -0
  172. package/scripts/lib/host-capability-matrix.js +351 -0
  173. package/scripts/lib/host-support-context.js +254 -0
  174. package/scripts/lib/http-hook-action.js +538 -0
  175. package/scripts/lib/install-ai-readiness.js +84 -0
  176. package/scripts/lib/install-intake-risk.js +1051 -0
  177. package/scripts/lib/install-journey-intelligence.js +329 -0
  178. package/scripts/lib/intervention-guidance.js +57 -0
  179. package/scripts/lib/known-limitations.js +115 -0
  180. package/scripts/lib/l8-path-truth.js +146 -0
  181. package/scripts/lib/launch-hardening-gate.js +436 -0
  182. package/scripts/lib/launch-readiness.js +635 -0
  183. package/scripts/lib/learning-experimentation-foundation.js +1484 -0
  184. package/scripts/lib/learning-memory.js +686 -0
  185. package/scripts/lib/lifecycle-hooks.js +982 -0
  186. package/scripts/lib/local-package-smoke.js +436 -0
  187. package/scripts/lib/local-pricing.js +299 -0
  188. package/scripts/lib/mcp-enforcement.js +311 -0
  189. package/scripts/lib/mcp-least-privilege-policy.js +303 -0
  190. package/scripts/lib/mcp-tool-inventory.js +388 -0
  191. package/scripts/lib/mcp-tool-risk.js +0 -0
  192. package/scripts/lib/memory.js +335 -0
  193. package/scripts/lib/metrics.js +699 -0
  194. package/scripts/lib/micro-proof.js +133 -0
  195. package/scripts/lib/model-work-class-routing.js +79 -0
  196. package/scripts/lib/next-run-context.js +436 -0
  197. package/scripts/lib/next-run-continuity.js +1207 -0
  198. package/scripts/lib/observability-recovery-guard.js +1030 -0
  199. package/scripts/lib/operating-value.js +1662 -0
  200. package/scripts/lib/optimization-v3.js +122 -0
  201. package/scripts/lib/orchestration/adapters/_shared.js +49 -0
  202. package/scripts/lib/orchestration/adapters/aider.js +18 -0
  203. package/scripts/lib/orchestration/adapters/claude-code.js +35 -0
  204. package/scripts/lib/orchestration/adapters/codex.js +35 -0
  205. package/scripts/lib/orchestration/adapters/gemini-cli.js +18 -0
  206. package/scripts/lib/orchestration/adapters/git.js +25 -0
  207. package/scripts/lib/orchestration/adapters/index.js +31 -0
  208. package/scripts/lib/orchestration/adapters/lm-studio.js +18 -0
  209. package/scripts/lib/orchestration/adapters/ollama.js +18 -0
  210. package/scripts/lib/orchestration/adapters/opencode.js +18 -0
  211. package/scripts/lib/orchestration/adapters/openrouter.js +18 -0
  212. package/scripts/lib/orchestration/adapters/test-runner.js +25 -0
  213. package/scripts/lib/orchestration/cli.js +438 -0
  214. package/scripts/lib/orchestration/execution-manager.js +279 -0
  215. package/scripts/lib/orchestration/handoff.js +314 -0
  216. package/scripts/lib/orchestration/index.js +456 -0
  217. package/scripts/lib/orchestration/inventory.js +47 -0
  218. package/scripts/lib/orchestration/model-discovery.js +498 -0
  219. package/scripts/lib/orchestration/model-profiler.js +170 -0
  220. package/scripts/lib/orchestration/model-profiles.js +252 -0
  221. package/scripts/lib/orchestration/model-refresh-policy.js +72 -0
  222. package/scripts/lib/orchestration/proof-writer.js +349 -0
  223. package/scripts/lib/orchestration/provider-discovery/aider.js +49 -0
  224. package/scripts/lib/orchestration/provider-discovery/claude-code.js +56 -0
  225. package/scripts/lib/orchestration/provider-discovery/codex.js +49 -0
  226. package/scripts/lib/orchestration/provider-discovery/common.js +186 -0
  227. package/scripts/lib/orchestration/provider-discovery/gemini.js +106 -0
  228. package/scripts/lib/orchestration/provider-discovery/lm-studio.js +118 -0
  229. package/scripts/lib/orchestration/provider-discovery/models-dev.js +12 -0
  230. package/scripts/lib/orchestration/provider-discovery/ollama.js +100 -0
  231. package/scripts/lib/orchestration/provider-discovery/opencode.js +47 -0
  232. package/scripts/lib/orchestration/provider-discovery/openrouter.js +44 -0
  233. package/scripts/lib/orchestration/risk-classifier.js +130 -0
  234. package/scripts/lib/orchestration/routing-policy.js +486 -0
  235. package/scripts/lib/orchestration/settings.js +95 -0
  236. package/scripts/lib/orchestration/state.js +165 -0
  237. package/scripts/lib/orchestration/verification-manager.js +138 -0
  238. package/scripts/lib/outcome-evidence-stack.js +1083 -0
  239. package/scripts/lib/output-profiles.js +146 -0
  240. package/scripts/lib/package-content-audit.js +368 -0
  241. package/scripts/lib/package-runtime.js +278 -0
  242. package/scripts/lib/plan-surface.js +53 -0
  243. package/scripts/lib/plans.js +2327 -0
  244. package/scripts/lib/policy-provider.js +27 -0
  245. package/scripts/lib/prelaunch-activation-readiness.js +409 -0
  246. package/scripts/lib/prelaunch-evidence-store.js +816 -0
  247. package/scripts/lib/prelaunch-intelligence.js +869 -0
  248. package/scripts/lib/pricing-experiment.js +118 -0
  249. package/scripts/lib/pro-moment-events.js +77 -0
  250. package/scripts/lib/pro-moment-state.js +227 -0
  251. package/scripts/lib/pro-moments.js +1216 -0
  252. package/scripts/lib/product-home-receipt.js +1790 -0
  253. package/scripts/lib/product-learning-events.js +696 -0
  254. package/scripts/lib/production-confidence-control-tower.js +1209 -0
  255. package/scripts/lib/production-confidence.js +1858 -0
  256. package/scripts/lib/project-profile.js +555 -0
  257. package/scripts/lib/prompt-compiler-plan.js +94 -0
  258. package/scripts/lib/prompt-compiler.js +280 -0
  259. package/scripts/lib/prompt-lint.js +32 -0
  260. package/scripts/lib/prompt-suggestions.js +52 -0
  261. package/scripts/lib/proof-canonical.js +398 -0
  262. package/scripts/lib/proof-drilldown.js +383 -0
  263. package/scripts/lib/proof-e2e/promise-completion.js +141 -0
  264. package/scripts/lib/proof-e2e/promise-dashboard-claims.js +135 -0
  265. package/scripts/lib/proof-e2e/promise-local-proof.js +156 -0
  266. package/scripts/lib/proof-e2e/promise-provider-neutral.js +136 -0
  267. package/scripts/lib/proof-e2e/promise-registry.js +219 -0
  268. package/scripts/lib/proof-e2e/promise-security-trust.js +141 -0
  269. package/scripts/lib/proof-e2e/promise-skills-risk.js +163 -0
  270. package/scripts/lib/proof-e2e/promise-token-savings.js +112 -0
  271. package/scripts/lib/proof-e2e/promise-work-management.js +126 -0
  272. package/scripts/lib/proof-events.js +342 -0
  273. package/scripts/lib/proof-history.js +243 -0
  274. package/scripts/lib/proof-metrics.js +296 -0
  275. package/scripts/lib/proof-outcome-evidence.js +134 -0
  276. package/scripts/lib/proof-receipt.js +335 -0
  277. package/scripts/lib/proof-record.js +461 -0
  278. package/scripts/lib/proof-savings-report.js +1549 -0
  279. package/scripts/lib/public-activation-distribution-gate.js +266 -0
  280. package/scripts/lib/public-cli.js +5631 -0
  281. package/scripts/lib/public-distribution-truth.js +211 -0
  282. package/scripts/lib/public-distribution.js +73 -0
  283. package/scripts/lib/public-install-claim-checker.js +300 -0
  284. package/scripts/lib/publish-provenance-readiness.js +287 -0
  285. package/scripts/lib/readiness-delta.js +218 -0
  286. package/scripts/lib/readiness-evidence-closure.js +196 -0
  287. package/scripts/lib/reentry-memory-capture.js +241 -0
  288. package/scripts/lib/reentry-memory-retrieval.js +302 -0
  289. package/scripts/lib/reentry-memory-status.js +147 -0
  290. package/scripts/lib/reentry-memory-store.js +183 -0
  291. package/scripts/lib/reentry-state.js +66 -0
  292. package/scripts/lib/release-candidate-bundle.js +166 -0
  293. package/scripts/lib/remediation.js +81 -0
  294. package/scripts/lib/repo-map.js +391 -0
  295. package/scripts/lib/run-action-policy.js +104 -0
  296. package/scripts/lib/run-contract-receipt.js +48 -0
  297. package/scripts/lib/run-contract.js +288 -0
  298. package/scripts/lib/run-cost-to-verified-outcome.js +87 -0
  299. package/scripts/lib/run-improvements-lifecycle.js +330 -0
  300. package/scripts/lib/run-improvements.js +789 -0
  301. package/scripts/lib/run-proof-contract.js +116 -0
  302. package/scripts/lib/run-risk-classifier.js +131 -0
  303. package/scripts/lib/run-task-classifier.js +216 -0
  304. package/scripts/lib/runtime-decision-policy.js +387 -0
  305. package/scripts/lib/safe-path-engine.js +705 -0
  306. package/scripts/lib/safe-repair.js +877 -0
  307. package/scripts/lib/safe-run-controller.js +887 -0
  308. package/scripts/lib/scope-repair-before-approval.js +865 -0
  309. package/scripts/lib/score.js +262 -0
  310. package/scripts/lib/seamless-enforcement.js +329 -0
  311. package/scripts/lib/seamless-outcome.js +689 -0
  312. package/scripts/lib/seamless-reality-gate.js +5053 -0
  313. package/scripts/lib/security-confidence/activation-safety.js +78 -0
  314. package/scripts/lib/security-confidence/adapters/adapter-contract.js +125 -0
  315. package/scripts/lib/security-confidence/adapters/mock-claude-bughunter-adapter.js +36 -0
  316. package/scripts/lib/security-confidence/adapters/mock-decepticon-adapter.js +34 -0
  317. package/scripts/lib/security-confidence/adapters/mock-mcp-scan-adapter.js +35 -0
  318. package/scripts/lib/security-confidence/adapters/mock-snyk-agent-scan-adapter.js +35 -0
  319. package/scripts/lib/security-confidence/adapters/mock-tank-adapter.js +34 -0
  320. package/scripts/lib/security-confidence/adapters/reference-registry.js +63 -0
  321. package/scripts/lib/security-confidence/auth-rbac-plan.js +39 -0
  322. package/scripts/lib/security-confidence/component-inventory.js +151 -0
  323. package/scripts/lib/security-confidence/constants.js +191 -0
  324. package/scripts/lib/security-confidence/dependency-script-risk.js +95 -0
  325. package/scripts/lib/security-confidence/evidence-hygiene.js +127 -0
  326. package/scripts/lib/security-confidence/feature-impact-classifier.js +80 -0
  327. package/scripts/lib/security-confidence/file-utils.js +161 -0
  328. package/scripts/lib/security-confidence/finding-triage.js +90 -0
  329. package/scripts/lib/security-confidence/findings.js +115 -0
  330. package/scripts/lib/security-confidence/index.js +29 -0
  331. package/scripts/lib/security-confidence/permission-budget.js +93 -0
  332. package/scripts/lib/security-confidence/redaction.js +82 -0
  333. package/scripts/lib/security-confidence/redteam-fixtures.js +53 -0
  334. package/scripts/lib/security-confidence/reference-adoption.js +229 -0
  335. package/scripts/lib/security-confidence/reference-taxonomy.js +155 -0
  336. package/scripts/lib/security-confidence/report-quality.js +99 -0
  337. package/scripts/lib/security-confidence/report-writer.js +79 -0
  338. package/scripts/lib/security-confidence/risk-classifier.js +187 -0
  339. package/scripts/lib/security-confidence/runner.js +179 -0
  340. package/scripts/lib/security-confidence/schema.js +188 -0
  341. package/scripts/lib/security-confidence/secret-safety.js +74 -0
  342. package/scripts/lib/security-confidence/security-capability-router.js +98 -0
  343. package/scripts/lib/security-confidence/web-security-smoke-plan.js +46 -0
  344. package/scripts/lib/security-risk-classifier.js +511 -0
  345. package/scripts/lib/security-scan.js +396 -0
  346. package/scripts/lib/session-context-optimizer.js +1211 -0
  347. package/scripts/lib/session-timing.js +322 -0
  348. package/scripts/lib/skill-hygiene.js +805 -0
  349. package/scripts/lib/skill-packs.js +161 -0
  350. package/scripts/lib/skills-operating-layer-v1.js +1023 -0
  351. package/scripts/lib/skills-operating-layer.js +629 -0
  352. package/scripts/lib/smallest-safe-plan.js +214 -0
  353. package/scripts/lib/smart-routing-plan.js +115 -0
  354. package/scripts/lib/smart-work-routing.js +768 -0
  355. package/scripts/lib/source-catalog.js +1022 -0
  356. package/scripts/lib/status-value-summary.js +32 -0
  357. package/scripts/lib/supply-chain-dependency-guard.js +1333 -0
  358. package/scripts/lib/supply-chain-guard.js +219 -0
  359. package/scripts/lib/support-bundle.js +561 -0
  360. package/scripts/lib/support-redaction.js +66 -0
  361. package/scripts/lib/task-continuation.js +440 -0
  362. package/scripts/lib/test-helpers.js +15 -0
  363. package/scripts/lib/tier.js +38 -0
  364. package/scripts/lib/token-context-quality-gate.js +370 -0
  365. package/scripts/lib/token-cost-capture.js +187 -0
  366. package/scripts/lib/token-cost-evidence.js +862 -0
  367. package/scripts/lib/token-cost-intelligence.js +358 -0
  368. package/scripts/lib/token-efficiency-evidence.js +213 -0
  369. package/scripts/lib/token-efficiency-map.js +110 -0
  370. package/scripts/lib/token-evidence.js +703 -0
  371. package/scripts/lib/tokenish.js +17 -0
  372. package/scripts/lib/tool-output-sandbox.js +559 -0
  373. package/scripts/lib/trust-audit.js +136 -0
  374. package/scripts/lib/unified-events.js +396 -0
  375. package/scripts/lib/upgrade-interruption-recovery.js +407 -0
  376. package/scripts/lib/usage-ledger.js +201 -0
  377. package/scripts/lib/value-ledger.js +130 -0
  378. package/scripts/lib/value-proof-calibration.js +531 -0
  379. package/scripts/lib/visual-qa.js +231 -0
  380. package/scripts/lib/voice-alpha.js +29 -0
  381. package/scripts/lib/webhook-idempotency-guard.js +990 -0
  382. package/scripts/lib/work-aware-orchestration.js +976 -0
  383. package/scripts/lib/work-control-receipts.js +577 -0
  384. package/scripts/lib/work-ledger.js +1123 -0
  385. package/scripts/lib/work-panel-preview.js +352 -0
  386. package/scripts/lib/workflow-discipline.js +280 -0
  387. package/scripts/lib/workflow-intelligence-radar.js +804 -0
  388. package/scripts/lib/workflow-signals.js +419 -0
  389. package/scripts/lib/workspace-map.js +281 -0
  390. package/scripts/lib/workspace-registry.js +1367 -0
  391. package/scripts/lib/workspace-resolver.js +542 -0
  392. package/scripts/wuz-doctor.js +315 -0
  393. package/bin/avorelo.mjs +0 -7
  394. package/dist/avorelo.mjs +0 -24678
@@ -0,0 +1,1236 @@
1
+ "use strict";
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+
6
+ const { nowIso, safeReadJson, safeWriteJson } = require("./fsx");
7
+
8
+ const CONTRACT = "avorelo.deploymentEnvironmentGuard.v1";
9
+ const REPORT_DIR_REL = ".claude/cco/reports/deployment-environment-guard";
10
+ const REPORT_JSON_REL = `${REPORT_DIR_REL}/latest-report.json`;
11
+
12
+ const TEXT_FILE_RE = /\.(cjs|cts|env|ini|js|json|jsx|md|mjs|mts|prisma|sql|toml|ts|tsx|txt|yaml|yml)$/i;
13
+ const ENV_FILE_RE = /(^|\/)(\.env(?:\.[^/]+)?|env\.example)$/i;
14
+ const ENV_EXAMPLE_RE = /(^|\/)(\.env\.(?:example|sample|local\.example|production\.example|development\.example)|env\.example)$/i;
15
+ const WORKFLOW_RE = /^\.github\/workflows\/.+\.(yml|yaml)$/i;
16
+ const SKIP_DIRS = new Set([
17
+ ".claude",
18
+ ".git",
19
+ ".next",
20
+ ".nuxt",
21
+ ".turbo",
22
+ ".wasp",
23
+ "build",
24
+ "coverage",
25
+ "dist",
26
+ "node_modules",
27
+ ]);
28
+
29
+ const PUBLIC_ENV_PREFIX_RE = /^(NEXT_PUBLIC_|VITE_|PUBLIC_|REACT_APP_|EXPO_PUBLIC_)/i;
30
+ const SECRET_LIKE_ENV_RE = /(SERVICE_ROLE|SECRET|TOKEN|API_KEY|DATABASE_URL|DB_URL|ADMIN|PRIVATE|PASSWORD|CREDENTIAL|SESSION_SECRET|JWT_SECRET|STRIPE_SECRET|SUPABASE_SERVICE_ROLE)/i;
31
+ const DEPLOY_COMMAND_RE = /\b(deploy|release|publish|vercel|netlify|railway|fly\s+deploy|docker\s+push|gh\s+release)\b/i;
32
+ const DEPLOY_PROVIDER_RE = /\b(vercel|netlify|railway|render|fly|docker|cloudbuild|procfile)\b/i;
33
+ const PRODUCTION_DEPLOY_RE = /(?:--prod\b|\bproduction\b|\bdeploy:prod\b|\bdeploy:production\b|\bvercel\s+--prod\b|\bnetlify\s+deploy\s+--prod\b|\bfly\s+deploy\b|\brailway\s+up\b)/i;
34
+ const PREVIEW_SIGNAL_RE = /\b(preview|staging|deploy-preview|branch-deploy|nonprod|non-prod)\b/i;
35
+ const MIGRATION_COMMAND_RE = /\b(prisma\s+migrate\s+deploy|prisma\s+db\s+push|drizzle-kit\s+(push|migrate)|supabase\s+(db\s+push|migration\s+up)|db:migrate|migrate(?::deploy)?|migration)\b/i;
36
+ const SEED_COMMAND_RE = /\b(seed|db:seed|seed:prod|supabase\s+db\s+seed)\b/i;
37
+ const DESTRUCTIVE_COMMAND_RE = /\b(db\s+reset|drop\s+database|drop\s+table|truncate\b|delete\s+from\s+[a-z0-9_]+\s*;?$|supabase\s+db\s+reset|prisma\s+db\s+push|force\s+migration|reset:db)\b/i;
38
+ const TEST_COMMAND_RE = /\b((npm|pnpm|yarn)\s+test|node\s+--test|vitest|jest|playwright|cypress|pytest|go\s+test|cargo\s+test)\b/i;
39
+ const PRODUCTION_URL_RE = /\bhttps?:\/\/[A-Za-z0-9.-]+\.[A-Za-z]{2,}(?::\d+)?(?:\/[^\s"'`)]*)?/g;
40
+ const LOCALHOST_RE = /\bhttps?:\/\/(?:localhost|127\.0\.0\.1)(?::\d+)?\b/i;
41
+ const PUSH_MAIN_RE = /\bon\s*:\s*[\s\S]{0,600}\bpush\s*:\s*[\s\S]{0,600}\bbranches\s*:\s*[\s\S]{0,300}\bmain\b/i;
42
+ const WORKFLOW_DISPATCH_RE = /\bworkflow_dispatch\b/i;
43
+ const PRODUCTION_ENV_RE = /\benvironment\s*:\s*production\b/i;
44
+ const SECRET_LITERAL_RE = /["']?(?:api[_-]?key|token|password|secret|authorization|database_url)["']?\s*[:=]\s*["']?(?!\$\{\{|\$[A-Z_]+|process\.env|import\.meta\.env|<redacted>|redacted|example|sample|placeholder|changeme|replace-me|your-|test-|xxxx|dummy\b)([^\s"',}]{12,})/i;
45
+ const URL_WITH_CREDENTIAL_RE = /\b(?:postgres(?:ql)?|mysql|https?):\/\/[^/\s:@]+:[^@\s]+@/i;
46
+ const ROLLBACK_SIGNAL_RE = /\b(rollback|roll back|recovery|restore|revert|backup|runbook|incident|migration down)\b/i;
47
+
48
+ function unique(values) {
49
+ return [...new Set((values || []).filter(Boolean))];
50
+ }
51
+
52
+ function toPosix(value) {
53
+ return String(value || "").replace(/\\/g, "/");
54
+ }
55
+
56
+ function sanitizeText(value, max = 220) {
57
+ const text = String(value || "")
58
+ .replace(/\s+/g, " ")
59
+ .replace(/(?:sk-[A-Za-z0-9_-]{16,}|gh[ps]_[A-Za-z0-9]{12,}|npm_[A-Za-z0-9]{12,}|AKIA[0-9A-Z]{16})/g, "[redacted-token]")
60
+ .replace(/(bearer\s+)[A-Za-z0-9._-]{8,}/ig, "$1[redacted]")
61
+ .replace(/((?:api[_ -]?key|token|password|secret|authorization|database_url)\s*[:=]\s*)([^\s'"]+)/ig, "$1[redacted]")
62
+ .replace(/\bhttps?:\/\/[^/\s:@]+:[^@\s]+@/ig, "https://[redacted]@[redacted]")
63
+ .trim();
64
+ if (!text) return null;
65
+ return text.length > max ? `${text.slice(0, max - 3).trimEnd()}...` : text;
66
+ }
67
+
68
+ function sanitizeRef(ref) {
69
+ const text = String(ref || "").trim().replace(/\\/g, "/");
70
+ if (!text || text.startsWith("..")) return null;
71
+ return text;
72
+ }
73
+
74
+ function makeEvidenceRef(relPath, lineNumber, label) {
75
+ const base = sanitizeRef(relPath);
76
+ if (!base) return null;
77
+ const suffix = Number.isFinite(lineNumber) && lineNumber > 0 ? `:${lineNumber}` : "";
78
+ const note = label ? ` (${sanitizeText(label, 80)})` : "";
79
+ return `${base}${suffix}${note}`;
80
+ }
81
+
82
+ function severityRank(severity) {
83
+ if (severity === "block") return 4;
84
+ if (severity === "warn") return 3;
85
+ if (severity === "info") return 2;
86
+ return 1;
87
+ }
88
+
89
+ function riskRank(risk) {
90
+ return {
91
+ none: 0,
92
+ low: 1,
93
+ medium: 2,
94
+ high: 3,
95
+ blocked: 4,
96
+ unknown: 1,
97
+ }[risk] ?? 0;
98
+ }
99
+
100
+ function severityToRisk(severity) {
101
+ if (severity === "block") return "blocked";
102
+ if (severity === "warn") return "medium";
103
+ if (severity === "info") return "low";
104
+ return "none";
105
+ }
106
+
107
+ function pickHigherRisk(current, next) {
108
+ return riskRank(next) > riskRank(current) ? next : current;
109
+ }
110
+
111
+ function walkWorkspace(cwd) {
112
+ const files = [];
113
+
114
+ function visit(current, depth) {
115
+ if (depth > 8 || files.length >= 2400) return;
116
+ let entries = [];
117
+ try {
118
+ entries = fs.readdirSync(current, { withFileTypes: true });
119
+ } catch {
120
+ return;
121
+ }
122
+
123
+ for (const entry of entries) {
124
+ if (files.length >= 2400) return;
125
+ const absPath = path.join(current, entry.name);
126
+ const relPath = toPosix(path.relative(cwd, absPath));
127
+ if (entry.isDirectory()) {
128
+ if (SKIP_DIRS.has(entry.name)) continue;
129
+ visit(absPath, depth + 1);
130
+ continue;
131
+ }
132
+ if (!entry.isFile()) continue;
133
+ files.push({
134
+ absPath,
135
+ relPath,
136
+ text: undefined,
137
+ });
138
+ }
139
+ }
140
+
141
+ visit(cwd, 0);
142
+ return files;
143
+ }
144
+
145
+ function readText(file) {
146
+ if (file.text !== undefined) return file.text;
147
+ if (!(
148
+ TEXT_FILE_RE.test(file.relPath)
149
+ || ENV_FILE_RE.test(file.relPath)
150
+ || /(^|\/)(Dockerfile|Procfile|\.dockerignore)$/i.test(file.relPath)
151
+ )) {
152
+ file.text = null;
153
+ return file.text;
154
+ }
155
+ try {
156
+ const stat = fs.statSync(file.absPath);
157
+ if (stat.size > 256 * 1024) {
158
+ file.text = null;
159
+ return file.text;
160
+ }
161
+ file.text = fs.readFileSync(file.absPath, "utf8");
162
+ return file.text;
163
+ } catch {
164
+ file.text = null;
165
+ return file.text;
166
+ }
167
+ }
168
+
169
+ function findLineNumber(text, index) {
170
+ return text.slice(0, index).split(/\r?\n/).length;
171
+ }
172
+
173
+ function findLineRef(file, predicate, labelFactory) {
174
+ const text = readText(file);
175
+ if (!text) return null;
176
+ const lines = text.split(/\r?\n/);
177
+ for (let i = 0; i < lines.length; i += 1) {
178
+ if (!predicate(lines[i], i + 1)) continue;
179
+ return makeEvidenceRef(file.relPath, i + 1, labelFactory ? labelFactory(lines[i], i + 1) : null);
180
+ }
181
+ return makeEvidenceRef(file.relPath, null, null);
182
+ }
183
+
184
+ function packageScriptRef(file, scriptName) {
185
+ const text = readText(file);
186
+ if (!text) return sanitizeRef(file.relPath);
187
+ const index = text.indexOf(`"${scriptName}"`);
188
+ if (index < 0) return makeEvidenceRef(file.relPath, null, scriptName);
189
+ return makeEvidenceRef(file.relPath, findLineNumber(text, index), scriptName);
190
+ }
191
+
192
+ function addSurfaceRef(list, ref) {
193
+ if (ref) list.push(ref);
194
+ }
195
+
196
+ function makeBaseSurface() {
197
+ return { risk: "none" };
198
+ }
199
+
200
+ function buildEmptyReport() {
201
+ return {
202
+ schemaVersion: CONTRACT,
203
+ generatedAt: nowIso(),
204
+ status: "no_data",
205
+ redacted: true,
206
+ summary: {
207
+ surfacesInspected: 0,
208
+ risksFound: 0,
209
+ blockers: 0,
210
+ needsReview: 0,
211
+ safeNextAction: "No local deployment or environment surface was detected. Re-run after adding deploy, CI, or environment configuration.",
212
+ },
213
+ surfaces: {
214
+ packageScripts: {
215
+ detected: false,
216
+ scriptRefs: [],
217
+ deployScripts: [],
218
+ migrationScripts: [],
219
+ risk: "none",
220
+ },
221
+ envFiles: {
222
+ detected: false,
223
+ envNameRefs: [],
224
+ publicSecretNameSignals: [],
225
+ missingExampleSignals: [],
226
+ risk: "none",
227
+ },
228
+ deploymentProviders: {
229
+ detected: false,
230
+ vercel: [],
231
+ netlify: [],
232
+ render: [],
233
+ railway: [],
234
+ fly: [],
235
+ docker: [],
236
+ risk: "none",
237
+ },
238
+ ciWorkflows: {
239
+ detected: false,
240
+ workflowRefs: [],
241
+ testBeforeDeployEvidence: [],
242
+ deployWithoutTestSignals: [],
243
+ risk: "none",
244
+ },
245
+ databaseDeployPath: {
246
+ detected: false,
247
+ migrationRefs: [],
248
+ seedRefs: [],
249
+ resetOrDestructiveSignals: [],
250
+ risk: "none",
251
+ },
252
+ productionRouting: {
253
+ detected: false,
254
+ productionUrlRefs: [],
255
+ localhostInProductionSignals: [],
256
+ branchOrEnvSeparationEvidence: [],
257
+ risk: "none",
258
+ },
259
+ rollbackRecovery: {
260
+ detected: false,
261
+ rollbackRefs: [],
262
+ recoveryRefs: [],
263
+ missingRecoverySignals: [],
264
+ risk: "none",
265
+ },
266
+ },
267
+ findings: [],
268
+ rollup: {
269
+ readySignals: [],
270
+ needsReview: [],
271
+ blockedItems: [],
272
+ notEnoughEvidence: [],
273
+ },
274
+ integrationRefs: {
275
+ productionConfidence: [REPORT_JSON_REL],
276
+ productHome: [REPORT_JSON_REL],
277
+ radar: [REPORT_JSON_REL],
278
+ status: [REPORT_JSON_REL],
279
+ dashboard: [REPORT_JSON_REL],
280
+ support: [REPORT_JSON_REL],
281
+ },
282
+ privacy: {
283
+ localOnly: true,
284
+ remoteFetch: false,
285
+ providerConnection: false,
286
+ deployExecuted: false,
287
+ buildExecuted: false,
288
+ migrationExecuted: false,
289
+ rawSecretValuesStored: false,
290
+ rawPromptStored: false,
291
+ rawCodeStored: false,
292
+ fullLogsStored: false,
293
+ privatePathsStored: false,
294
+ redacted: true,
295
+ },
296
+ claimSafety: {
297
+ deploymentVerified: false,
298
+ launchReadyClaim: false,
299
+ productionCertified: false,
300
+ cloudProviderScanned: false,
301
+ envValuesVerified: false,
302
+ rollbackGuaranteed: false,
303
+ autoFixed: false,
304
+ },
305
+ };
306
+ }
307
+
308
+ function createState() {
309
+ return {
310
+ seenFindingIds: new Set(),
311
+ deploymentSurfaceDetected: false,
312
+ previewEvidence: [],
313
+ productionEvidence: [],
314
+ rollbackEvidence: [],
315
+ workflowManualDispatch: [],
316
+ envUsageRefs: [],
317
+ };
318
+ }
319
+
320
+ function updateSurfaceRisk(surface, severity, overrideRisk) {
321
+ surface.risk = pickHigherRisk(surface.risk, overrideRisk || severityToRisk(severity));
322
+ }
323
+
324
+ function addFinding(report, state, surface, finding, riskOverride) {
325
+ const evidenceRef = sanitizeRef(finding.evidenceRef);
326
+ const key = `${finding.id}:${evidenceRef || "none"}`;
327
+ if (state.seenFindingIds.has(key)) return;
328
+ state.seenFindingIds.add(key);
329
+
330
+ const normalized = {
331
+ id: finding.id,
332
+ severity: finding.severity,
333
+ category: finding.category,
334
+ title: sanitizeText(finding.title, 120),
335
+ evidenceRef,
336
+ reasonCodes: unique(finding.reasonCodes || []),
337
+ safeNextAction: sanitizeText(finding.safeNextAction, 220) || "Review the local deployment path before shipping.",
338
+ };
339
+ report.findings.push(normalized);
340
+ if (surface) updateSurfaceRisk(surface, normalized.severity, riskOverride);
341
+ }
342
+
343
+ function addReadySignal(report, text) {
344
+ if (!text) return;
345
+ report.rollup.readySignals.push(sanitizeText(text, 220));
346
+ }
347
+
348
+ function providerSlotFor(relPath) {
349
+ const name = toPosix(relPath).toLowerCase();
350
+ if (/vercel\.json$/.test(name)) return "vercel";
351
+ if (/netlify\.toml$/.test(name)) return "netlify";
352
+ if (/render\.ya?ml$/.test(name)) return "render";
353
+ if (/railway\.json$/.test(name)) return "railway";
354
+ if (/fly\.toml$/.test(name)) return "fly";
355
+ if (/(^|\/)(dockerfile|docker-compose\.ya?ml|\.dockerignore)$/.test(name)) return "docker";
356
+ return null;
357
+ }
358
+
359
+ function configFileCandidates(files) {
360
+ return files.filter((file) => {
361
+ const rel = file.relPath;
362
+ return providerSlotFor(rel)
363
+ || WORKFLOW_RE.test(rel)
364
+ || /(^|\/)(procfile|app\.json|cloudbuild\.ya?ml)$/i.test(rel)
365
+ || /(^|\/)(scripts|deploy|infra|ops)\/.+\.(cjs|js|mjs|ps1|sh|ts|yaml|yml)$/i.test(rel)
366
+ || /(^|\/)package\.json$/i.test(rel);
367
+ });
368
+ }
369
+
370
+ function isExampleEnvFile(relPath) {
371
+ return ENV_EXAMPLE_RE.test(relPath);
372
+ }
373
+
374
+ function isDeployScript(name, command) {
375
+ return /\bdeploy\b/i.test(name) || DEPLOY_COMMAND_RE.test(command);
376
+ }
377
+
378
+ function isPreviewScript(name, command) {
379
+ return /\bpreview\b/i.test(name) || PREVIEW_SIGNAL_RE.test(command);
380
+ }
381
+
382
+ function isBuildScript(name, command) {
383
+ return /\bbuild\b/i.test(name) || /\bbuild\b/i.test(command);
384
+ }
385
+
386
+ function hasProductionDeploy(command, name) {
387
+ return PRODUCTION_DEPLOY_RE.test(command) || /\bdeploy:(prod|production)\b/i.test(name);
388
+ }
389
+
390
+ function isMigrationCommand(command) {
391
+ return MIGRATION_COMMAND_RE.test(command);
392
+ }
393
+
394
+ function isSeedCommand(command) {
395
+ return SEED_COMMAND_RE.test(command);
396
+ }
397
+
398
+ function isDestructiveCommand(command) {
399
+ return DESTRUCTIVE_COMMAND_RE.test(command);
400
+ }
401
+
402
+ function hasPublishSignal(command) {
403
+ return /\bnpm\s+publish\b|\bgh\s+release\b/i.test(command);
404
+ }
405
+
406
+ function isWorkflowDeployActionLine(line) {
407
+ if (PRODUCTION_ENV_RE.test(line)) return true;
408
+ return /\b(run|command)\s*:\s*.*\b(deploy|release|publish|vercel|netlify|railway|fly\s+deploy|docker\s+push|gh\s+release)\b/i.test(line)
409
+ || /\buses\s*:\s*.*\b(vercel|netlify|railway|fly|docker)\b/i.test(line);
410
+ }
411
+
412
+ function extractEnvUsageRefs(files) {
413
+ const refs = [];
414
+ for (const file of files) {
415
+ const text = readText(file);
416
+ if (!text || /\.md$/i.test(file.relPath)) continue;
417
+ const matcher = /\b(?:process\.env|import\.meta\.env)\.([A-Z0-9_]+)\b/g;
418
+ let match;
419
+ while ((match = matcher.exec(text)) && refs.length < 24) {
420
+ refs.push({
421
+ name: match[1],
422
+ ref: makeEvidenceRef(file.relPath, findLineNumber(text, match.index), match[1]),
423
+ });
424
+ if (match.index === matcher.lastIndex) matcher.lastIndex += 1;
425
+ }
426
+ }
427
+ return refs;
428
+ }
429
+
430
+ function detectPackageScripts(report, state, files) {
431
+ const surface = report.surfaces.packageScripts;
432
+ const dbSurface = report.surfaces.databaseDeployPath;
433
+ const routingSurface = report.surfaces.productionRouting;
434
+
435
+ for (const file of files.filter((item) => /(^|\/)package\.json$/i.test(item.relPath))) {
436
+ const text = readText(file);
437
+ if (!text) continue;
438
+ let manifest;
439
+ try {
440
+ manifest = JSON.parse(text);
441
+ } catch {
442
+ continue;
443
+ }
444
+
445
+ const scripts = manifest && typeof manifest.scripts === "object" ? manifest.scripts : null;
446
+ if (!scripts || Object.keys(scripts).length === 0) continue;
447
+ surface.detected = true;
448
+
449
+ for (const [name, rawValue] of Object.entries(scripts)) {
450
+ if (typeof rawValue !== "string") continue;
451
+ const value = rawValue.trim();
452
+ const lowerName = String(name || "").toLowerCase();
453
+ const ref = packageScriptRef(file, name);
454
+ addSurfaceRef(surface.scriptRefs, ref);
455
+ if (isBuildScript(lowerName, value)) {
456
+ state.deploymentSurfaceDetected = true;
457
+ }
458
+
459
+ const deployScript = isDeployScript(lowerName, value);
460
+ const previewScript = isPreviewScript(lowerName, value);
461
+ const productionDeploy = hasProductionDeploy(value, lowerName);
462
+ const migrationScript = isMigrationCommand(value);
463
+ const seedScript = isSeedCommand(value);
464
+ const destructive = isDestructiveCommand(value);
465
+ const publishSignal = hasPublishSignal(value);
466
+
467
+ if (deployScript) {
468
+ state.deploymentSurfaceDetected = true;
469
+ surface.detected = true;
470
+ addSurfaceRef(surface.deployScripts, ref);
471
+ }
472
+ if (previewScript) {
473
+ state.previewEvidence.push(ref);
474
+ addReadySignal(report, "Preview or staging script evidence is present.");
475
+ }
476
+ if (productionDeploy) {
477
+ state.productionEvidence.push(ref);
478
+ }
479
+ if (migrationScript) {
480
+ dbSurface.detected = true;
481
+ addSurfaceRef(surface.migrationScripts, ref);
482
+ addSurfaceRef(dbSurface.migrationRefs, ref);
483
+ }
484
+ if (seedScript) {
485
+ dbSurface.detected = true;
486
+ addSurfaceRef(dbSurface.seedRefs, ref);
487
+ }
488
+ if (destructive) {
489
+ dbSurface.detected = true;
490
+ addSurfaceRef(dbSurface.resetOrDestructiveSignals, ref);
491
+ }
492
+ if (PRODUCTION_URL_RE.test(value)) {
493
+ routingSurface.detected = true;
494
+ addSurfaceRef(routingSurface.productionUrlRefs, ref);
495
+ }
496
+ PRODUCTION_URL_RE.lastIndex = 0;
497
+
498
+ if (deployScript && lowerName === "deploy" && productionDeploy) {
499
+ addFinding(report, state, surface, {
500
+ id: "default_production_deploy",
501
+ severity: "block",
502
+ category: "package_script",
503
+ title: "Default deploy script targets production",
504
+ evidenceRef: ref,
505
+ reasonCodes: [
506
+ "package_scripts_detected",
507
+ "deploy_script_detected",
508
+ "production_deploy_script_detected",
509
+ "blocked_deployment_risk",
510
+ "safe_next_action_available",
511
+ ],
512
+ safeNextAction: "Make preview or staging the default deploy path and keep production deploy explicit and reviewed.",
513
+ });
514
+ } else if (deployScript && productionDeploy) {
515
+ addFinding(report, state, surface, {
516
+ id: "explicit_production_deploy",
517
+ severity: "warn",
518
+ category: "package_script",
519
+ title: "Production deploy script is present locally",
520
+ evidenceRef: ref,
521
+ reasonCodes: [
522
+ "package_scripts_detected",
523
+ "deploy_script_detected",
524
+ "production_deploy_script_detected",
525
+ "safe_next_action_available",
526
+ ],
527
+ safeNextAction: "Keep production deploy explicit and add preview, test, and rollback evidence before shipping.",
528
+ });
529
+ } else if (deployScript) {
530
+ addFinding(report, state, surface, {
531
+ id: "deploy_target_unclear",
532
+ severity: "warn",
533
+ category: "package_script",
534
+ title: "Deploy script target is not clearly separated",
535
+ evidenceRef: ref,
536
+ reasonCodes: [
537
+ "package_scripts_detected",
538
+ "deploy_script_detected",
539
+ "preview_production_separation_unclear",
540
+ "not_enough_evidence",
541
+ ],
542
+ safeNextAction: "Make preview versus production targets explicit in local deploy scripts before shipping.",
543
+ });
544
+ }
545
+
546
+ if (deployScript && migrationScript) {
547
+ addFinding(report, state, dbSurface, {
548
+ id: productionDeploy ? "deploy_migration_coupling_prod" : "deploy_migration_coupling",
549
+ severity: destructive ? "block" : "warn",
550
+ category: "migration",
551
+ title: destructive
552
+ ? "Production deploy path includes risky database mutation commands"
553
+ : "Deploy path is coupled to database migration commands",
554
+ evidenceRef: ref,
555
+ reasonCodes: [
556
+ "migration_script_detected",
557
+ "deploy_migration_coupling_detected",
558
+ ...(destructive ? ["destructive_db_command_detected", "blocked_deployment_risk"] : []),
559
+ ],
560
+ safeNextAction: destructive
561
+ ? "Remove destructive database commands from deploy-time scripts and keep database review separate from deploy."
562
+ : "Split migration steps from deploy commands and review them separately before shipping.",
563
+ }, destructive ? "blocked" : productionDeploy ? "high" : "medium");
564
+ }
565
+
566
+ if (deployScript && seedScript) {
567
+ addFinding(report, state, dbSurface, {
568
+ id: productionDeploy ? "deploy_seed_coupling_prod" : "deploy_seed_coupling",
569
+ severity: productionDeploy ? "block" : "warn",
570
+ category: "seed",
571
+ title: productionDeploy
572
+ ? "Production deploy path includes seeding"
573
+ : "Deploy path is coupled to a seed command",
574
+ evidenceRef: ref,
575
+ reasonCodes: [
576
+ "seed_script_detected",
577
+ "deploy_seed_coupling_detected",
578
+ ...(productionDeploy ? ["blocked_deployment_risk"] : []),
579
+ ],
580
+ safeNextAction: "Keep seed commands out of production deploy paths and run them through a separately reviewed workflow.",
581
+ }, productionDeploy ? "blocked" : "medium");
582
+ }
583
+
584
+ if (deployScript && destructive) {
585
+ addFinding(report, state, dbSurface, {
586
+ id: "destructive_db_in_deploy_script",
587
+ severity: "block",
588
+ category: "migration",
589
+ title: "Deploy path includes destructive database commands",
590
+ evidenceRef: ref,
591
+ reasonCodes: [
592
+ "destructive_db_command_detected",
593
+ "blocked_deployment_risk",
594
+ "deploy_script_detected",
595
+ ],
596
+ safeNextAction: "Remove reset, drop, truncate, or unsafe db push commands from deploy-time scripts before shipping.",
597
+ }, "blocked");
598
+ }
599
+
600
+ if (deployScript && publishSignal) {
601
+ addFinding(report, state, surface, {
602
+ id: "publish_in_deploy_path",
603
+ severity: "block",
604
+ category: "package_script",
605
+ title: "Deploy path mixes app deployment with package publishing",
606
+ evidenceRef: ref,
607
+ reasonCodes: [
608
+ "deploy_script_detected",
609
+ "npm_publish_script_detected",
610
+ "blocked_deployment_risk",
611
+ ],
612
+ safeNextAction: "Separate package publishing or release commands from the app deploy path before shipping.",
613
+ }, "blocked");
614
+ }
615
+ }
616
+ }
617
+
618
+ surface.scriptRefs = unique(surface.scriptRefs);
619
+ surface.deployScripts = unique(surface.deployScripts);
620
+ surface.migrationScripts = unique(surface.migrationScripts);
621
+ dbSurface.migrationRefs = unique(dbSurface.migrationRefs);
622
+ dbSurface.seedRefs = unique(dbSurface.seedRefs);
623
+ dbSurface.resetOrDestructiveSignals = unique(dbSurface.resetOrDestructiveSignals);
624
+ }
625
+
626
+ function detectEnvFiles(report, state, files) {
627
+ const surface = report.surfaces.envFiles;
628
+ const envUsageRefs = extractEnvUsageRefs(files);
629
+ state.envUsageRefs = envUsageRefs;
630
+ const envFiles = files.filter((file) => ENV_FILE_RE.test(file.relPath));
631
+ const exampleFiles = envFiles.filter((file) => isExampleEnvFile(file.relPath));
632
+
633
+ if (envFiles.length === 0 && envUsageRefs.length === 0) {
634
+ return;
635
+ }
636
+
637
+ surface.detected = true;
638
+ const nameRefs = [];
639
+ const publicSecretRefs = [];
640
+
641
+ for (const file of envFiles) {
642
+ const text = readText(file);
643
+ if (!text) continue;
644
+ const lines = text.split(/\r?\n/);
645
+ for (let i = 0; i < lines.length; i += 1) {
646
+ const match = lines[i].match(/^\s*([A-Z0-9_]+)\s*=/);
647
+ if (!match) continue;
648
+ const name = match[1];
649
+ const ref = makeEvidenceRef(file.relPath, i + 1, name);
650
+ nameRefs.push(ref);
651
+ if (PUBLIC_ENV_PREFIX_RE.test(name) && SECRET_LIKE_ENV_RE.test(name)) {
652
+ publicSecretRefs.push(ref);
653
+ }
654
+ }
655
+ }
656
+
657
+ for (const item of envUsageRefs) {
658
+ if (!item || !item.name || !item.ref) continue;
659
+ if (PUBLIC_ENV_PREFIX_RE.test(item.name) && SECRET_LIKE_ENV_RE.test(item.name)) {
660
+ publicSecretRefs.push(item.ref);
661
+ }
662
+ }
663
+
664
+ surface.envNameRefs = unique(nameRefs).slice(0, 12);
665
+ surface.publicSecretNameSignals = unique(publicSecretRefs).slice(0, 8);
666
+
667
+ if (surface.publicSecretNameSignals.length > 0) {
668
+ addFinding(report, state, surface, {
669
+ id: "public_secret_like_env_name",
670
+ severity: "block",
671
+ category: "env",
672
+ title: "Public or client env names expose secret-like variables",
673
+ evidenceRef: surface.publicSecretNameSignals[0],
674
+ reasonCodes: [
675
+ "env_file_detected",
676
+ "public_env_prefix_detected",
677
+ "public_secret_like_name_detected",
678
+ "blocked_deployment_risk",
679
+ ],
680
+ safeNextAction: "Move secret-like variables out of public or client env prefixes before shipping.",
681
+ }, "blocked");
682
+ }
683
+
684
+ if (envUsageRefs.length > 0 && exampleFiles.length === 0) {
685
+ surface.missingExampleSignals = unique(envUsageRefs.map((item) => item.ref)).slice(0, 6);
686
+ addFinding(report, state, surface, {
687
+ id: "env_example_missing",
688
+ severity: "warn",
689
+ category: "env",
690
+ title: "Environment variables are used without a clear example file",
691
+ evidenceRef: surface.missingExampleSignals[0],
692
+ reasonCodes: [
693
+ "env_example_missing_or_unclear",
694
+ "not_enough_evidence",
695
+ ],
696
+ safeNextAction: "Add a redacted example env file that documents safe variable names before shipping.",
697
+ }, "medium");
698
+ } else if (exampleFiles.length > 0) {
699
+ addReadySignal(report, "Example env files are present for local deployment setup.");
700
+ }
701
+
702
+ surface.missingExampleSignals = unique(surface.missingExampleSignals);
703
+ }
704
+
705
+ function detectProviderAndRoutingSignals(report, state, files) {
706
+ const providerSurface = report.surfaces.deploymentProviders;
707
+ const routingSurface = report.surfaces.productionRouting;
708
+
709
+ for (const file of configFileCandidates(files)) {
710
+ const slot = providerSlotFor(file.relPath);
711
+ if (slot) {
712
+ providerSurface.detected = true;
713
+ providerSurface[slot].push(sanitizeRef(file.relPath));
714
+ state.deploymentSurfaceDetected = true;
715
+ }
716
+
717
+ const text = readText(file);
718
+ if (!text) continue;
719
+ const lines = text.split(/\r?\n/);
720
+
721
+ let providerHadSeparation = false;
722
+ for (let i = 0; i < lines.length; i += 1) {
723
+ const line = lines[i];
724
+
725
+ if (PUBLIC_ENV_PREFIX_RE.test(line) && SECRET_LIKE_ENV_RE.test(line)) {
726
+ const ref = makeEvidenceRef(file.relPath, i + 1, "public-secret-like-env");
727
+ addFinding(report, state, providerSurface, {
728
+ id: "provider_public_secret_env_name",
729
+ severity: "block",
730
+ category: "provider_config",
731
+ title: "Deployment config references public secret-like env names",
732
+ evidenceRef: ref,
733
+ reasonCodes: [
734
+ "provider_config_detected",
735
+ "public_env_prefix_detected",
736
+ "public_secret_like_name_detected",
737
+ "blocked_deployment_risk",
738
+ ],
739
+ safeNextAction: "Remove secret-like public env names from deploy or provider config before shipping.",
740
+ }, "blocked");
741
+ }
742
+
743
+ if (SECRET_LITERAL_RE.test(line) || URL_WITH_CREDENTIAL_RE.test(line)) {
744
+ const ref = makeEvidenceRef(file.relPath, i + 1, "secret-literal");
745
+ addFinding(report, state, providerSurface, {
746
+ id: "provider_secret_literal",
747
+ severity: "block",
748
+ category: slot === "docker" ? "docker" : "provider_config",
749
+ title: "Provider or deploy config appears to contain a committed secret-like literal",
750
+ evidenceRef: ref,
751
+ reasonCodes: [
752
+ ...(slot ? [`${slot}_config_detected`] : ["provider_config_detected"]),
753
+ "secret_value_literal_detected",
754
+ "blocked_deployment_risk",
755
+ ],
756
+ safeNextAction: "Replace committed secret-like literals with environment references before shipping.",
757
+ }, "blocked");
758
+ }
759
+ SECRET_LITERAL_RE.lastIndex = 0;
760
+ URL_WITH_CREDENTIAL_RE.lastIndex = 0;
761
+
762
+ const urlMatches = [...line.matchAll(PRODUCTION_URL_RE)];
763
+ if (urlMatches.length > 0 && !LOCALHOST_RE.test(line)) {
764
+ const ref = makeEvidenceRef(file.relPath, i + 1, "production-url");
765
+ routingSurface.detected = true;
766
+ routingSurface.productionUrlRefs.push(ref);
767
+ addFinding(report, state, routingSurface, {
768
+ id: "production_url_detected_local",
769
+ severity: "warn",
770
+ category: "production_route",
771
+ title: "Production URL is hardcoded in local deploy or config files",
772
+ evidenceRef: ref,
773
+ reasonCodes: [
774
+ "production_url_detected",
775
+ "safe_next_action_available",
776
+ ],
777
+ safeNextAction: "Move production URLs behind explicit environment separation and review the default target before shipping.",
778
+ }, "medium");
779
+ }
780
+
781
+ if (LOCALHOST_RE.test(line) && /\bproduction\b/i.test(text)) {
782
+ const ref = makeEvidenceRef(file.relPath, i + 1, "localhost-in-production");
783
+ routingSurface.detected = true;
784
+ routingSurface.localhostInProductionSignals.push(ref);
785
+ addFinding(report, state, routingSurface, {
786
+ id: "localhost_in_production",
787
+ severity: "warn",
788
+ category: "production_route",
789
+ title: "Production config appears to reference localhost",
790
+ evidenceRef: ref,
791
+ reasonCodes: [
792
+ "localhost_in_production_config",
793
+ "safe_next_action_available",
794
+ ],
795
+ safeNextAction: "Remove localhost targets from production-oriented config and keep preview and production routing separate.",
796
+ }, "medium");
797
+ }
798
+
799
+ if (PREVIEW_SIGNAL_RE.test(line) && /\bproduction\b/i.test(text)) {
800
+ const ref = makeEvidenceRef(file.relPath, i + 1, "preview-production-separation");
801
+ providerHadSeparation = true;
802
+ routingSurface.detected = true;
803
+ routingSurface.branchOrEnvSeparationEvidence.push(ref);
804
+ state.previewEvidence.push(ref);
805
+ }
806
+ }
807
+
808
+ if (providerHadSeparation) {
809
+ addReadySignal(report, "Provider or deploy config shows preview versus production separation evidence.");
810
+ }
811
+ }
812
+
813
+ providerSurface.vercel = unique(providerSurface.vercel);
814
+ providerSurface.netlify = unique(providerSurface.netlify);
815
+ providerSurface.render = unique(providerSurface.render);
816
+ providerSurface.railway = unique(providerSurface.railway);
817
+ providerSurface.fly = unique(providerSurface.fly);
818
+ providerSurface.docker = unique(providerSurface.docker);
819
+ routingSurface.productionUrlRefs = unique(routingSurface.productionUrlRefs);
820
+ routingSurface.localhostInProductionSignals = unique(routingSurface.localhostInProductionSignals);
821
+ routingSurface.branchOrEnvSeparationEvidence = unique(routingSurface.branchOrEnvSeparationEvidence);
822
+
823
+ if (providerSurface.detected && routingSurface.branchOrEnvSeparationEvidence.length === 0) {
824
+ addFinding(report, state, providerSurface, {
825
+ id: "provider_separation_unclear",
826
+ severity: "warn",
827
+ category: "provider_config",
828
+ title: "Deployment provider config lacks clear preview or production separation evidence",
829
+ evidenceRef: providerSurface.vercel[0]
830
+ || providerSurface.netlify[0]
831
+ || providerSurface.render[0]
832
+ || providerSurface.railway[0]
833
+ || providerSurface.fly[0]
834
+ || providerSurface.docker[0]
835
+ || null,
836
+ reasonCodes: [
837
+ "provider_config_detected",
838
+ "preview_production_separation_unclear",
839
+ "not_enough_evidence",
840
+ ],
841
+ safeNextAction: "Document or encode preview versus production separation in local provider config before shipping.",
842
+ }, "medium");
843
+ }
844
+ }
845
+
846
+ function detectCiWorkflows(report, state, files) {
847
+ const surface = report.surfaces.ciWorkflows;
848
+
849
+ for (const file of files.filter((item) => WORKFLOW_RE.test(item.relPath))) {
850
+ surface.detected = true;
851
+ state.deploymentSurfaceDetected = true;
852
+ addSurfaceRef(surface.workflowRefs, sanitizeRef(file.relPath));
853
+
854
+ const text = readText(file);
855
+ if (!text) continue;
856
+ const lines = text.split(/\r?\n/);
857
+
858
+ let firstDeployLine = null;
859
+ let firstTestLine = null;
860
+ let hasDeploy = false;
861
+ let hasManualDispatch = WORKFLOW_DISPATCH_RE.test(text);
862
+
863
+ for (let i = 0; i < lines.length; i += 1) {
864
+ const line = lines[i];
865
+ if (firstTestLine === null && TEST_COMMAND_RE.test(line)) {
866
+ firstTestLine = i + 1;
867
+ }
868
+ if (firstDeployLine === null && isWorkflowDeployActionLine(line)) {
869
+ firstDeployLine = i + 1;
870
+ }
871
+ if (isWorkflowDeployActionLine(line)) {
872
+ hasDeploy = true;
873
+ }
874
+ }
875
+
876
+ if (!hasDeploy) continue;
877
+
878
+ const deployRef = makeEvidenceRef(file.relPath, firstDeployLine, "deploy-workflow");
879
+ const testBeforeDeploy = firstTestLine !== null && firstDeployLine !== null && firstTestLine < firstDeployLine;
880
+
881
+ if (testBeforeDeploy) {
882
+ surface.testBeforeDeployEvidence.push(makeEvidenceRef(file.relPath, firstTestLine, "test-before-deploy"));
883
+ addReadySignal(report, "CI workflow shows test-before-deploy evidence.");
884
+ } else {
885
+ surface.deployWithoutTestSignals.push(deployRef);
886
+ }
887
+
888
+ if (hasManualDispatch) {
889
+ state.workflowManualDispatch.push(sanitizeRef(file.relPath));
890
+ addReadySignal(report, "CI workflow includes a manual dispatch path for bounded review.");
891
+ }
892
+
893
+ const pushMainProduction = PUSH_MAIN_RE.test(text) && (PRODUCTION_ENV_RE.test(text) || PRODUCTION_DEPLOY_RE.test(text));
894
+ if (pushMainProduction && !testBeforeDeploy) {
895
+ addFinding(report, state, surface, {
896
+ id: "push_main_production_without_tests",
897
+ severity: "block",
898
+ category: "ci",
899
+ title: "CI appears to deploy to production on push to main without test-before-deploy evidence",
900
+ evidenceRef: deployRef,
901
+ reasonCodes: [
902
+ "ci_workflow_detected",
903
+ "deploy_workflow_detected",
904
+ "push_main_production_deploy_detected",
905
+ "deploy_without_test_evidence",
906
+ "blocked_deployment_risk",
907
+ ],
908
+ safeNextAction: "Require tests before deploy and avoid direct push-to-main production deploys without bounded review evidence.",
909
+ }, "blocked");
910
+ } else if (!testBeforeDeploy) {
911
+ addFinding(report, state, surface, {
912
+ id: "deploy_without_tests",
913
+ severity: "warn",
914
+ category: "ci",
915
+ title: "Deploy workflow does not show test-before-deploy evidence",
916
+ evidenceRef: deployRef,
917
+ reasonCodes: [
918
+ "ci_workflow_detected",
919
+ "deploy_workflow_detected",
920
+ "deploy_without_test_evidence",
921
+ "not_enough_evidence",
922
+ ],
923
+ safeNextAction: "Add explicit test steps before deploy in CI before treating the deploy path as safer to ship.",
924
+ }, "medium");
925
+ }
926
+ }
927
+
928
+ surface.workflowRefs = unique(surface.workflowRefs);
929
+ surface.testBeforeDeployEvidence = unique(surface.testBeforeDeployEvidence);
930
+ surface.deployWithoutTestSignals = unique(surface.deployWithoutTestSignals);
931
+ }
932
+
933
+ function detectDatabaseAndRollback(report, state, files) {
934
+ const dbSurface = report.surfaces.databaseDeployPath;
935
+ const rollbackSurface = report.surfaces.rollbackRecovery;
936
+
937
+ for (const file of files) {
938
+ const rel = file.relPath;
939
+ const text = readText(file);
940
+ if (!text) continue;
941
+ const lowerRel = rel.toLowerCase();
942
+
943
+ if (/(\brollback\b|\brecovery\b|\brunbook\b|\brestore\b|\brevert\b)/i.test(rel) || ROLLBACK_SIGNAL_RE.test(text)) {
944
+ rollbackSurface.detected = true;
945
+ const ref = findLineRef(file, (line) => ROLLBACK_SIGNAL_RE.test(line), () => "rollback-recovery") || sanitizeRef(rel);
946
+ if (/\brollback|roll back|revert|restore\b/i.test(text) || /\brollback|revert\b/i.test(rel)) {
947
+ rollbackSurface.rollbackRefs.push(ref);
948
+ }
949
+ if (/\brecovery|runbook|incident|backup\b/i.test(text) || /\brecovery|runbook|incident\b/i.test(rel)) {
950
+ rollbackSurface.recoveryRefs.push(ref);
951
+ }
952
+ state.rollbackEvidence.push(ref);
953
+ }
954
+
955
+ if (/^(.+\/)?(scripts|deploy|infra|ops)\//i.test(lowerRel) || WORKFLOW_RE.test(rel) || /(^|\/)package\.json$/i.test(rel)) {
956
+ const lines = text.split(/\r?\n/);
957
+ for (let i = 0; i < lines.length; i += 1) {
958
+ const line = lines[i];
959
+ if (MIGRATION_COMMAND_RE.test(line)) {
960
+ dbSurface.detected = true;
961
+ dbSurface.migrationRefs.push(makeEvidenceRef(rel, i + 1, "migration-command"));
962
+ }
963
+ if (SEED_COMMAND_RE.test(line)) {
964
+ dbSurface.detected = true;
965
+ dbSurface.seedRefs.push(makeEvidenceRef(rel, i + 1, "seed-command"));
966
+ }
967
+ if (DESTRUCTIVE_COMMAND_RE.test(line)) {
968
+ dbSurface.detected = true;
969
+ dbSurface.resetOrDestructiveSignals.push(makeEvidenceRef(rel, i + 1, "destructive-db-command"));
970
+ }
971
+ }
972
+ }
973
+ }
974
+
975
+ rollbackSurface.rollbackRefs = unique(rollbackSurface.rollbackRefs);
976
+ rollbackSurface.recoveryRefs = unique(rollbackSurface.recoveryRefs);
977
+ dbSurface.migrationRefs = unique(dbSurface.migrationRefs);
978
+ dbSurface.seedRefs = unique(dbSurface.seedRefs);
979
+ dbSurface.resetOrDestructiveSignals = unique(dbSurface.resetOrDestructiveSignals);
980
+
981
+ if (dbSurface.resetOrDestructiveSignals.length > 0) {
982
+ addFinding(report, state, dbSurface, {
983
+ id: "destructive_db_signal_detected",
984
+ severity: "block",
985
+ category: "migration",
986
+ title: "Destructive database commands are present near the deploy path",
987
+ evidenceRef: dbSurface.resetOrDestructiveSignals[0],
988
+ reasonCodes: [
989
+ "destructive_db_command_detected",
990
+ "blocked_deployment_risk",
991
+ ],
992
+ safeNextAction: "Remove destructive database commands from deploy-adjacent scripts or workflows before shipping.",
993
+ }, "blocked");
994
+ } else if (dbSurface.detected && (dbSurface.migrationRefs.length > 0 || dbSurface.seedRefs.length > 0)) {
995
+ const evidenceRef = dbSurface.migrationRefs[0] || dbSurface.seedRefs[0] || null;
996
+ addFinding(report, state, dbSurface, {
997
+ id: "database_deploy_path_review",
998
+ severity: "warn",
999
+ category: dbSurface.seedRefs.length > 0 ? "seed" : "migration",
1000
+ title: "Database mutation commands appear near the deploy path",
1001
+ evidenceRef,
1002
+ reasonCodes: [
1003
+ ...(dbSurface.migrationRefs.length > 0 ? ["migration_script_detected"] : []),
1004
+ ...(dbSurface.seedRefs.length > 0 ? ["seed_script_detected"] : []),
1005
+ "not_enough_evidence",
1006
+ ],
1007
+ safeNextAction: "Review database migration and seed steps separately from deploy commands before shipping.",
1008
+ }, "medium");
1009
+ }
1010
+
1011
+ if (state.deploymentSurfaceDetected && state.rollbackEvidence.length === 0) {
1012
+ rollbackSurface.detected = true;
1013
+ rollbackSurface.missingRecoverySignals.push(
1014
+ report.surfaces.packageScripts.deployScripts[0]
1015
+ || report.surfaces.ciWorkflows.workflowRefs[0]
1016
+ || report.surfaces.deploymentProviders.vercel[0]
1017
+ || report.surfaces.deploymentProviders.netlify[0]
1018
+ || report.surfaces.deploymentProviders.render[0]
1019
+ || report.surfaces.deploymentProviders.railway[0]
1020
+ || report.surfaces.deploymentProviders.fly[0]
1021
+ || report.surfaces.deploymentProviders.docker[0]
1022
+ || null
1023
+ );
1024
+ addFinding(report, state, rollbackSurface, {
1025
+ id: "rollback_evidence_missing",
1026
+ severity: "warn",
1027
+ category: "rollback",
1028
+ title: "Deployment surfaces exist without local rollback or recovery evidence",
1029
+ evidenceRef: rollbackSurface.missingRecoverySignals[0],
1030
+ reasonCodes: [
1031
+ "rollback_evidence_missing",
1032
+ "not_enough_evidence",
1033
+ ],
1034
+ safeNextAction: "Add a compact rollback or recovery note before treating the deploy path as safer to ship.",
1035
+ }, "medium");
1036
+ } else if (state.rollbackEvidence.length > 0) {
1037
+ addReadySignal(report, "Rollback or recovery evidence is present locally.");
1038
+ }
1039
+
1040
+ rollbackSurface.missingRecoverySignals = unique(rollbackSurface.missingRecoverySignals);
1041
+ }
1042
+
1043
+ function finalizeReport(report, state) {
1044
+ const findings = report.findings.sort((a, b) => severityRank(b.severity) - severityRank(a.severity));
1045
+ report.findings = findings;
1046
+
1047
+ report.rollup.readySignals = unique(report.rollup.readySignals).filter(Boolean).slice(0, 8);
1048
+ report.rollup.needsReview = unique(findings.filter((item) => item.severity === "warn").map((item) => item.title)).filter(Boolean).slice(0, 8);
1049
+ report.rollup.blockedItems = unique(findings.filter((item) => item.severity === "block").map((item) => item.title)).filter(Boolean).slice(0, 8);
1050
+ report.rollup.notEnoughEvidence = unique(findings
1051
+ .filter((item) => item.reasonCodes.includes("not_enough_evidence") || item.reasonCodes.includes("preview_production_separation_unclear") || item.reasonCodes.includes("rollback_evidence_missing") || item.reasonCodes.includes("env_example_missing_or_unclear"))
1052
+ .map((item) => item.title))
1053
+ .filter(Boolean)
1054
+ .slice(0, 8);
1055
+
1056
+ report.surfaces.packageScripts.scriptRefs = unique(report.surfaces.packageScripts.scriptRefs).slice(0, 12);
1057
+ report.surfaces.packageScripts.deployScripts = unique(report.surfaces.packageScripts.deployScripts).slice(0, 8);
1058
+ report.surfaces.packageScripts.migrationScripts = unique(report.surfaces.packageScripts.migrationScripts).slice(0, 8);
1059
+ report.surfaces.envFiles.envNameRefs = unique(report.surfaces.envFiles.envNameRefs).slice(0, 12);
1060
+ report.surfaces.envFiles.publicSecretNameSignals = unique(report.surfaces.envFiles.publicSecretNameSignals).slice(0, 8);
1061
+ report.surfaces.envFiles.missingExampleSignals = unique(report.surfaces.envFiles.missingExampleSignals).slice(0, 8);
1062
+
1063
+ const detectedSurfaces = Object.values(report.surfaces).filter((surface) => surface.detected).length;
1064
+ const blockers = findings.filter((item) => item.severity === "block").length;
1065
+ const needsReview = findings.filter((item) => item.severity === "warn").length;
1066
+
1067
+ report.summary.surfacesInspected = detectedSurfaces;
1068
+ report.summary.risksFound = findings.length;
1069
+ report.summary.blockers = blockers;
1070
+ report.summary.needsReview = needsReview;
1071
+
1072
+ if (detectedSurfaces === 0) {
1073
+ report.status = "no_data";
1074
+ report.summary.safeNextAction = "No local deployment or environment surface was detected. Re-run after adding deploy, CI, or environment configuration.";
1075
+ return report;
1076
+ }
1077
+
1078
+ if (blockers > 0) {
1079
+ report.status = "blocked";
1080
+ } else if (needsReview > 0) {
1081
+ report.status = "warn";
1082
+ } else {
1083
+ report.status = "pass";
1084
+ }
1085
+
1086
+ const nextFinding = findings.find((item) => item.severity === "block")
1087
+ || findings.find((item) => item.severity === "warn");
1088
+ report.summary.safeNextAction = nextFinding
1089
+ ? nextFinding.safeNextAction
1090
+ : "Keep preview or production separation, test-before-deploy evidence, and rollback guidance current before shipping.";
1091
+ return report;
1092
+ }
1093
+
1094
+ function buildDeploymentEnvironmentGuard(cwd = process.cwd(), options = {}) {
1095
+ const files = walkWorkspace(cwd);
1096
+ const report = buildEmptyReport();
1097
+ const state = createState();
1098
+
1099
+ detectPackageScripts(report, state, files);
1100
+ detectEnvFiles(report, state, files);
1101
+ detectProviderAndRoutingSignals(report, state, files);
1102
+ detectCiWorkflows(report, state, files);
1103
+ detectDatabaseAndRollback(report, state, files);
1104
+
1105
+ if (report.surfaces.productionRouting.branchOrEnvSeparationEvidence.length > 0) {
1106
+ report.surfaces.productionRouting.detected = true;
1107
+ updateSurfaceRisk(report.surfaces.productionRouting, "info", "low");
1108
+ }
1109
+ if (report.surfaces.ciWorkflows.testBeforeDeployEvidence.length > 0 && report.surfaces.ciWorkflows.deployWithoutTestSignals.length === 0) {
1110
+ updateSurfaceRisk(report.surfaces.ciWorkflows, "info", "low");
1111
+ }
1112
+ if (report.surfaces.rollbackRecovery.rollbackRefs.length > 0 || report.surfaces.rollbackRecovery.recoveryRefs.length > 0) {
1113
+ updateSurfaceRisk(report.surfaces.rollbackRecovery, "info", "low");
1114
+ }
1115
+ if (report.surfaces.envFiles.detected && report.surfaces.envFiles.publicSecretNameSignals.length === 0 && report.surfaces.envFiles.missingExampleSignals.length === 0) {
1116
+ updateSurfaceRisk(report.surfaces.envFiles, "info", "low");
1117
+ }
1118
+ if (report.surfaces.deploymentProviders.detected && report.surfaces.productionRouting.branchOrEnvSeparationEvidence.length > 0) {
1119
+ updateSurfaceRisk(report.surfaces.deploymentProviders, "info", "low");
1120
+ }
1121
+
1122
+ finalizeReport(report, state);
1123
+
1124
+ if (options.write !== false) {
1125
+ safeWriteJson(cwd, REPORT_JSON_REL, report);
1126
+ }
1127
+ return report;
1128
+ }
1129
+
1130
+ function generateDeploymentEnvironmentGuard(cwd = process.cwd(), options = {}) {
1131
+ const report = buildDeploymentEnvironmentGuard(cwd, options);
1132
+ return {
1133
+ report,
1134
+ reportPath: path.join(cwd, REPORT_JSON_REL),
1135
+ };
1136
+ }
1137
+
1138
+ function loadDeploymentEnvironmentGuard(cwd = process.cwd()) {
1139
+ return safeReadJson(cwd, REPORT_JSON_REL, null);
1140
+ }
1141
+
1142
+ function formatDeploymentEnvironmentGuardText(report) {
1143
+ if (!report || report.schemaVersion !== CONTRACT) {
1144
+ return [
1145
+ "Avorelo Deployment & Environment Guard",
1146
+ "Status: NO DATA",
1147
+ "No local deployment or environment surface was detected yet.",
1148
+ "Safe next action: Re-run after adding deploy, CI, or environment configuration.",
1149
+ ].join("\n");
1150
+ }
1151
+
1152
+ const lines = [
1153
+ "Avorelo Deployment & Environment Guard",
1154
+ `Status: ${String(report.status || "no_data").replace(/_/g, " ").toUpperCase()} · redacted=${report.redacted === true ? "yes" : "no"}`,
1155
+ `Surfaces: ${report.summary?.surfacesInspected || 0} · risks=${report.summary?.risksFound || 0} · blockers=${report.summary?.blockers || 0} · review=${report.summary?.needsReview || 0}`,
1156
+ "Notes: local static scan only · no provider connection · no deploy/build/migration execution",
1157
+ ];
1158
+
1159
+ const findings = Array.isArray(report.findings) ? report.findings.slice(0, 5) : [];
1160
+ if (findings.length > 0) {
1161
+ lines.push("");
1162
+ lines.push("Findings:");
1163
+ for (const finding of findings) {
1164
+ lines.push(`- [${finding.severity}] ${finding.title}${finding.evidenceRef ? ` · ${finding.evidenceRef}` : ""}`);
1165
+ }
1166
+ }
1167
+
1168
+ if (Array.isArray(report.rollup?.readySignals) && report.rollup.readySignals.length > 0) {
1169
+ lines.push("");
1170
+ lines.push("Evidence:");
1171
+ for (const item of report.rollup.readySignals.slice(0, 3)) {
1172
+ lines.push(`- ${item}`);
1173
+ }
1174
+ }
1175
+
1176
+ lines.push("");
1177
+ lines.push(`Safe next action: ${report.summary?.safeNextAction || "Review the local deployment path before shipping."}`);
1178
+ return lines.join("\n");
1179
+ }
1180
+
1181
+ function formatDeploymentEnvironmentGuardStatusLineFromReport(report) {
1182
+ if (!report || report.schemaVersion !== CONTRACT) {
1183
+ return "Deployment guard: NOT RUN · run `avorelo deployment-guard --text`";
1184
+ }
1185
+ return `Deployment guard: ${String(report.status || "no_data").replace(/_/g, " ").toUpperCase()}`
1186
+ + ` · blockers=${report.summary?.blockers || 0}`
1187
+ + ` · review=${report.summary?.needsReview || 0}`
1188
+ + ` · next: ${sanitizeText(report.summary?.safeNextAction, 140) || "Review the local deployment path before shipping."}`;
1189
+ }
1190
+
1191
+ function buildDeploymentEnvironmentGuardSurface(cwd = process.cwd(), options = {}) {
1192
+ const report = loadDeploymentEnvironmentGuard(cwd) || (options.buildIfMissing ? buildDeploymentEnvironmentGuard(cwd, { write: false }) : null);
1193
+ if (!report) {
1194
+ return {
1195
+ status: "not_run",
1196
+ blockerCount: 0,
1197
+ needsReviewCount: 0,
1198
+ surfacesInspected: 0,
1199
+ nextBestAction: "Run `avorelo deployment-guard --text` before treating a local deploy path as safe to ship.",
1200
+ statusLine: "Deployment guard: NOT RUN · run `avorelo deployment-guard --text`",
1201
+ showInStatus: true,
1202
+ showInDashboard: true,
1203
+ evidenceRef: null,
1204
+ limitations: [
1205
+ "Local static scan only. No live deploy or provider connection.",
1206
+ ],
1207
+ };
1208
+ }
1209
+
1210
+ return {
1211
+ status: report.status,
1212
+ blockerCount: report.summary?.blockers || 0,
1213
+ needsReviewCount: report.summary?.needsReview || 0,
1214
+ surfacesInspected: report.summary?.surfacesInspected || 0,
1215
+ nextBestAction: report.summary?.safeNextAction,
1216
+ statusLine: formatDeploymentEnvironmentGuardStatusLineFromReport(report),
1217
+ showInStatus: true,
1218
+ showInDashboard: true,
1219
+ evidenceRef: REPORT_JSON_REL,
1220
+ limitations: [
1221
+ "Local static scan only. No live deploy or provider connection.",
1222
+ "No deploy, build, migration, or provider API execution happens in this guard.",
1223
+ ],
1224
+ };
1225
+ }
1226
+
1227
+ module.exports = {
1228
+ CONTRACT,
1229
+ REPORT_JSON_REL,
1230
+ buildDeploymentEnvironmentGuard,
1231
+ generateDeploymentEnvironmentGuard,
1232
+ loadDeploymentEnvironmentGuard,
1233
+ formatDeploymentEnvironmentGuardText,
1234
+ formatDeploymentEnvironmentGuardStatusLineFromReport,
1235
+ buildDeploymentEnvironmentGuardSurface,
1236
+ };