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,1514 @@
1
+ "use strict";
2
+
3
+ const crypto = require("crypto");
4
+ const path = require("path");
5
+
6
+ const { nowIso, safeReadJson, safeWriteJson } = require("./fsx");
7
+ const { readPackageJson } = require("./package-runtime");
8
+ const { validateInstallClaims } = require("./public-install-claim-checker");
9
+ const {
10
+ buildProductionConfidence,
11
+ loadProductionConfidence,
12
+ REPORT_JSON_REL: PRODUCTION_CONFIDENCE_REPORT_REL,
13
+ } = require("./production-confidence");
14
+ const {
15
+ buildGovernedWorkspaceHygieneReport,
16
+ readLatestGovernedWorkspaceHygieneReport,
17
+ LATEST_REPORT_REL: WORKSPACE_HYGIENE_REPORT_REL,
18
+ } = require("./governed-workspace-hygiene");
19
+ const {
20
+ buildLearningExperimentationReport,
21
+ LATEST_REPORT_REL: LEARNING_REPORT_REL,
22
+ } = require("./learning-experimentation-foundation");
23
+ const {
24
+ buildOutcomeEvidenceStack,
25
+ LEARNING_REPORT_REL: OUTCOME_LEARNING_REPORT_REL,
26
+ } = require("./outcome-evidence-stack");
27
+ const {
28
+ buildSkillsOperatingLayerReport,
29
+ readLatestSkillsOperatingLayerReport,
30
+ LATEST_V1_REPORT_REL: SKILLS_REPORT_REL,
31
+ } = require("./skills-operating-layer");
32
+ const {
33
+ LATEST_REPORT_REL: SCOPE_REPAIR_REPORT_REL,
34
+ readLatestScopeRepairReport,
35
+ } = require("./scope-repair-before-approval");
36
+ const {
37
+ classifyFilesystemWriteTarget,
38
+ isConfigFileTarget,
39
+ isDocsFileTarget,
40
+ isSensitiveWriteTarget,
41
+ isSourceFileTarget,
42
+ isTestFileTarget,
43
+ } = require("./agent-security/file-write-rules");
44
+ const { classifyPackageAction } = require("./agent-security/package-action-rules");
45
+ const { classifyMcpAction } = require("./agent-security/mcp-action-rules");
46
+
47
+ const CONTRACT = "avorelo.governedActionExposure.v1";
48
+ const SCHEMA_VERSION = 1;
49
+ const REPORT_DIR_REL = ".claude/cco/security/governed-action-exposure";
50
+ const LATEST_REPORT_REL = `${REPORT_DIR_REL}/latest-report.json`;
51
+
52
+ const ACTION_TYPES = new Set([
53
+ "file_read",
54
+ "file_write",
55
+ "command_run",
56
+ "package_install",
57
+ "package_publish",
58
+ "mcp_tool_call",
59
+ "browser_action",
60
+ "network_request",
61
+ "secret_access",
62
+ "deploy_action",
63
+ "config_change",
64
+ "public_content_change",
65
+ "release_action",
66
+ "unknown",
67
+ ]);
68
+
69
+ const EXPOSURE_TYPES = new Set([
70
+ "local_only",
71
+ "repo_internal",
72
+ "docs_public",
73
+ "website_public",
74
+ "npm_package_public",
75
+ "external_network",
76
+ "browser_external",
77
+ "production_deploy",
78
+ "credential_or_config",
79
+ "customer_data_adjacent",
80
+ "public_claim",
81
+ "unknown_exposure",
82
+ ]);
83
+
84
+ const TOOL_TYPES = new Set([
85
+ "local_read",
86
+ "local_write",
87
+ "command",
88
+ "package_manager",
89
+ "mcp_read",
90
+ "mcp_write",
91
+ "browser",
92
+ "network",
93
+ "deploy",
94
+ "secret",
95
+ "unknown",
96
+ ]);
97
+
98
+ const DECISIONS = new Set(["allow", "warn", "approval_required", "auto_limit", "block"]);
99
+ const CONFIDENCE_LEVELS = new Set(["none", "low", "medium", "high"]);
100
+
101
+ const DOGFOOD_COMMANDS = Object.freeze([
102
+ "node bin/avorelo support --json",
103
+ "node bin/avorelo support --text",
104
+ "node bin/avorelo outcome-evidence --json",
105
+ "node bin/avorelo outcome-evidence --text",
106
+ "node bin/avorelo workspace-hygiene --json",
107
+ "node bin/avorelo workspace-hygiene --text",
108
+ "node bin/avorelo learning --json",
109
+ "node bin/avorelo learning --text",
110
+ "node bin/avorelo production-confidence --json",
111
+ "node bin/avorelo production-confidence --text",
112
+ "node bin/avorelo skills --json",
113
+ "node bin/avorelo skills --text",
114
+ "node bin/avorelo boundary --json",
115
+ "node bin/avorelo boundary --text",
116
+ "node bin/avorelo status --text",
117
+ "node bin/avorelo open --json",
118
+ "node bin/avorelo package-audit --json",
119
+ "node bin/avorelo package-smoke --json",
120
+ "node bin/avorelo install-claims --json",
121
+ "node bin/avorelo public-distribution --json",
122
+ ]);
123
+
124
+ function stableStringify(value) {
125
+ if (Array.isArray(value)) {
126
+ return `[${value.map((item) => stableStringify(item)).join(",")}]`;
127
+ }
128
+ if (value && typeof value === "object") {
129
+ return `{${Object.keys(value)
130
+ .sort((left, right) => left.localeCompare(right))
131
+ .map((key) => `${JSON.stringify(key)}:${stableStringify(value[key])}`)
132
+ .join(",")}}`;
133
+ }
134
+ return JSON.stringify(value);
135
+ }
136
+
137
+ function sha256(value) {
138
+ return crypto.createHash("sha256").update(String(value || "")).digest("hex");
139
+ }
140
+
141
+ function hashId(prefix, payload) {
142
+ return `${prefix}-${sha256(stableStringify(payload)).slice(0, 12)}`;
143
+ }
144
+
145
+ function unique(values) {
146
+ return [...new Set((values || []).filter(Boolean))];
147
+ }
148
+
149
+ function compactText(value, maxChars = 180) {
150
+ const text = String(value || "").replace(/\s+/g, " ").trim();
151
+ if (!text) return null;
152
+ if (text.length <= maxChars) return text;
153
+ return `${text.slice(0, maxChars - 3).trimEnd()}...`;
154
+ }
155
+
156
+ function sanitizeText(value, maxChars = 180) {
157
+ return compactText(
158
+ String(value || "")
159
+ .replace(/sk-[A-Za-z0-9_-]{16,}/g, "[redacted-openai-key]")
160
+ .replace(/ghp_[A-Za-z0-9]{20,}/g, "[redacted-github-token]")
161
+ .replace(/AKIA[0-9A-Z]{16}/g, "[redacted-aws-key]")
162
+ .replace(/(bearer\s+)[A-Za-z0-9._-]{8,}/ig, "$1[redacted]")
163
+ .replace(/((?:api[_ -]?key|token|password|secret|authorization)\s*[:=]\s*)([^\s'"]+)/ig, "$1[redacted]"),
164
+ maxChars,
165
+ );
166
+ }
167
+
168
+ function normalizeEnum(value, allowed, fallback) {
169
+ const normalized = String(value || "").trim().toLowerCase();
170
+ return allowed.has(normalized) ? normalized : fallback;
171
+ }
172
+
173
+ function toRelativeRedactedPath(cwd, value) {
174
+ const raw = String(value || "").trim();
175
+ if (!raw) return null;
176
+ const normalized = raw.replace(/\\/g, "/");
177
+ if (/^https?:\/\//i.test(normalized)) {
178
+ try {
179
+ const url = new URL(normalized);
180
+ return url.hostname.toLowerCase();
181
+ } catch {
182
+ return "[redacted-url]";
183
+ }
184
+ }
185
+ const absolute = path.isAbsolute(raw) ? raw : path.resolve(cwd, raw);
186
+ const relative = path.relative(cwd, absolute).replace(/\\/g, "/");
187
+ if (!relative || relative === "") return ".";
188
+ if (relative.startsWith("..") || path.isAbsolute(relative)) return "<outside-repo>";
189
+ return relative;
190
+ }
191
+
192
+ function normalizeList(value) {
193
+ const list = Array.isArray(value) ? value : value ? [value] : [];
194
+ return list.map((item) => String(item || "").trim()).filter(Boolean);
195
+ }
196
+
197
+ function normalizeBrowserTargetType(value, normalizedScope = {}) {
198
+ const explicit = String(value || "").trim().toLowerCase();
199
+ if (["localhost", "local_file", "external_url", "production_url", "unknown"].includes(explicit)) {
200
+ return explicit;
201
+ }
202
+ const firstDomain = String((normalizedScope.domains || [])[0] || "").toLowerCase();
203
+ if (/^(localhost|127\.0\.0\.1|0\.0\.0\.0|\[::1\]|::1)$/.test(firstDomain)) return "localhost";
204
+ if ((normalizedScope.paths || []).length > 0 && normalizedScope.visibility === "repo_internal") return "local_file";
205
+ if (normalizedScope.visibility === "production_deploy") return "production_url";
206
+ if ((normalizedScope.domains || []).length > 0) return "external_url";
207
+ return "unknown";
208
+ }
209
+
210
+ function normalizeBrowserActionCategory(value) {
211
+ const normalized = String(value || "").trim().toLowerCase();
212
+ return normalized || "browser_action";
213
+ }
214
+
215
+ function buildRiskDimensions(flags = {}) {
216
+ return {
217
+ read: flags.read === true,
218
+ write: flags.write === true,
219
+ exec: flags.exec === true,
220
+ network: flags.network === true,
221
+ secret: flags.secret === true,
222
+ browser: flags.browser === true,
223
+ package: flags.package === true,
224
+ deploy: flags.deploy === true,
225
+ production: flags.production === true,
226
+ public_exposure: flags.public_exposure === true,
227
+ config: flags.config === true,
228
+ path: flags.path === true,
229
+ external_service: flags.external_service === true,
230
+ unknown_scope: flags.unknown_scope === true,
231
+ };
232
+ }
233
+
234
+ function countActiveRiskDimensions(dimensions) {
235
+ return Object.values(dimensions || {}).filter(Boolean).length;
236
+ }
237
+
238
+ function computeSideEffectLevel(dimensions) {
239
+ if (dimensions.secret || dimensions.deploy) return "critical";
240
+ if (dimensions.write || dimensions.exec || dimensions.network || dimensions.browser || dimensions.public_exposure || dimensions.production) {
241
+ return "high";
242
+ }
243
+ if (dimensions.package || dimensions.config || dimensions.path || dimensions.external_service || dimensions.unknown_scope) {
244
+ return "medium";
245
+ }
246
+ if (dimensions.read) return "low";
247
+ return "none";
248
+ }
249
+
250
+ function buildFallbackProductionReport() {
251
+ return {
252
+ status: "warn",
253
+ readiness: {
254
+ state: "not_enough_evidence",
255
+ confidence: "none",
256
+ safeNextAction: "Run `avorelo production-confidence --text` before using boundary output for ship decisions.",
257
+ },
258
+ summary: {
259
+ blocked: 0,
260
+ needsReview: 0,
261
+ notEnoughEvidence: 1,
262
+ },
263
+ categories: {
264
+ rollbackRecovery: {
265
+ id: "rollbackRecovery",
266
+ state: "warn",
267
+ safeNextAction: "Run `avorelo production-confidence --text` before deploy-adjacent decisions.",
268
+ },
269
+ },
270
+ };
271
+ }
272
+
273
+ function buildFallbackWorkspaceReport() {
274
+ return {
275
+ status: "warn",
276
+ summary: {
277
+ unknownSources: 1,
278
+ approvalRequired: 0,
279
+ blockedSources: 0,
280
+ receiptCount: 0,
281
+ safeNextAction: "Run `avorelo workspace-hygiene --json` before widening tool or source scope.",
282
+ },
283
+ inventory: {
284
+ capabilities: [],
285
+ tools: [],
286
+ skills: [],
287
+ mcpServers: [],
288
+ sources: [],
289
+ },
290
+ governance: {
291
+ receipts: [],
292
+ },
293
+ };
294
+ }
295
+
296
+ function buildFallbackOutcomeReport() {
297
+ return {
298
+ status: "warn",
299
+ summary: {
300
+ found: [],
301
+ fixed: [],
302
+ savedOrProved: [],
303
+ needsAttention: ["Capture outcome evidence before escalating risky public or deploy-adjacent decisions."],
304
+ safeNextAction: "Capture or import redacted outcome evidence first.",
305
+ },
306
+ tokenCost: {
307
+ measurementMode: "none",
308
+ },
309
+ evidenceContract: {
310
+ weakFindings: [],
311
+ rejectedFindings: [],
312
+ acceptedFindings: [],
313
+ },
314
+ proofLinks: [],
315
+ };
316
+ }
317
+
318
+ function buildFallbackLearningReport() {
319
+ return {
320
+ status: "warn",
321
+ summary: {
322
+ localEventsReviewed: 0,
323
+ experimentsReviewed: 0,
324
+ activeExperiments: 0,
325
+ weakSignalsFiltered: 0,
326
+ safeNextAction: "Collect local learning signals only after decisions are redacted and bounded.",
327
+ },
328
+ learning: {
329
+ findings: [],
330
+ capabilityImprovementCandidates: [],
331
+ },
332
+ outcomeReadiness: {
333
+ found: [],
334
+ fixed: [],
335
+ savedOrProved: [],
336
+ needsAttention: [],
337
+ },
338
+ };
339
+ }
340
+
341
+ function buildFallbackSkillsReport() {
342
+ return {
343
+ status: "warn",
344
+ summary: {
345
+ safeNextAction: "Run `avorelo skills --json` to review skill trust and verification gates.",
346
+ },
347
+ routing: {
348
+ selected: [],
349
+ deferred: [],
350
+ skipped: [],
351
+ deterministic: true,
352
+ llmDecisionMaker: false,
353
+ },
354
+ skills: [],
355
+ sources: [],
356
+ };
357
+ }
358
+
359
+ function buildFallbackPublicDistributionReport(cwd) {
360
+ const pkg = readPackageJson(cwd) || {};
361
+ return {
362
+ status: "warn",
363
+ distributionState: typeof pkg.avoreloDistributionState === "string" ? pkg.avoreloDistributionState : "private_local_only",
364
+ installClaims: {
365
+ blockedCount: 0,
366
+ status: "warn",
367
+ },
368
+ safeNextAction: "Run `avorelo public-distribution --json` for package/public boundary evidence.",
369
+ };
370
+ }
371
+
372
+ function buildLightweightPublicDistributionReport(cwd) {
373
+ const pkg = readPackageJson(cwd) || {};
374
+ let installClaims;
375
+ try {
376
+ installClaims = validateInstallClaims(cwd);
377
+ } catch {
378
+ installClaims = { status: "warn", blockedCount: 0 };
379
+ }
380
+ const distributionState = typeof pkg.avoreloDistributionState === "string"
381
+ ? pkg.avoreloDistributionState
382
+ : (pkg.private === false ? "ready_to_publish" : "private_local_only");
383
+
384
+ return {
385
+ status: distributionState === "published_verified" && (installClaims.blockedCount || 0) === 0 ? "pass" : "warn",
386
+ distributionState,
387
+ installClaims: {
388
+ status: installClaims.status || ((installClaims.blockedCount || 0) === 0 ? "pass" : "warn"),
389
+ blockedCount: installClaims.blockedCount || 0,
390
+ },
391
+ safeNextAction: distributionState === "published_verified"
392
+ ? "Public npm distribution is verified."
393
+ : "Review local package/public signals before widening public exposure.",
394
+ };
395
+ }
396
+
397
+ function buildIntegrationContext(cwd, options = {}) {
398
+ const overrides = options.integrationOverrides || {};
399
+ const allowBuildIfMissing = options.buildIfMissing === true;
400
+ let workspaceReport;
401
+ let outcomeReport;
402
+ let learningReport;
403
+ let productionReport;
404
+ let skillsReport;
405
+ let publicDistributionReport;
406
+
407
+ try {
408
+ workspaceReport = overrides.workspaceReport
409
+ || readLatestGovernedWorkspaceHygieneReport(cwd)
410
+ || (allowBuildIfMissing ? buildGovernedWorkspaceHygieneReport(cwd, { writeReport: false }).report : buildFallbackWorkspaceReport());
411
+ } catch {
412
+ workspaceReport = buildFallbackWorkspaceReport();
413
+ }
414
+
415
+ try {
416
+ outcomeReport = overrides.outcomeReport || (allowBuildIfMissing ? buildOutcomeEvidenceStack(cwd) : buildFallbackOutcomeReport());
417
+ } catch {
418
+ outcomeReport = buildFallbackOutcomeReport();
419
+ }
420
+
421
+ try {
422
+ learningReport = overrides.learningReport || (allowBuildIfMissing ? buildLearningExperimentationReport(cwd, { writeReport: false }) : buildFallbackLearningReport());
423
+ } catch {
424
+ learningReport = buildFallbackLearningReport();
425
+ }
426
+
427
+ try {
428
+ productionReport = overrides.productionReport || loadProductionConfidence(cwd) || (allowBuildIfMissing ? buildProductionConfidence(cwd) : buildFallbackProductionReport());
429
+ } catch {
430
+ productionReport = buildFallbackProductionReport();
431
+ }
432
+
433
+ try {
434
+ skillsReport = overrides.skillsReport
435
+ || readLatestSkillsOperatingLayerReport(cwd)
436
+ || (allowBuildIfMissing
437
+ ? buildSkillsOperatingLayerReport(cwd, {
438
+ task: "Governed action and exposure boundary review",
439
+ commandContext: "boundary",
440
+ writeReport: false,
441
+ emitEvents: false,
442
+ })
443
+ : buildFallbackSkillsReport());
444
+ } catch {
445
+ skillsReport = buildFallbackSkillsReport();
446
+ }
447
+
448
+ try {
449
+ publicDistributionReport = overrides.publicDistributionReport || buildLightweightPublicDistributionReport(cwd);
450
+ } catch {
451
+ publicDistributionReport = buildFallbackPublicDistributionReport(cwd);
452
+ }
453
+
454
+ return {
455
+ workspaceReport,
456
+ outcomeReport,
457
+ learningReport,
458
+ productionReport,
459
+ skillsReport,
460
+ publicDistributionReport,
461
+ };
462
+ }
463
+
464
+ function buildWorkspaceRisk(context) {
465
+ const summary = context.workspaceReport?.summary || {};
466
+ const riskyCapabilitySignals = (summary.blockedSources || 0) + (summary.approvalRequired || 0);
467
+ let level = "low";
468
+ if ((summary.blockedSources || 0) > 0 || riskyCapabilitySignals > 2) {
469
+ level = "high";
470
+ } else if ((summary.unknownSources || 0) > 0 || riskyCapabilitySignals > 0) {
471
+ level = "medium";
472
+ }
473
+ return {
474
+ level,
475
+ unknownSources: summary.unknownSources || 0,
476
+ approvalRequired: summary.approvalRequired || 0,
477
+ blockedSources: summary.blockedSources || 0,
478
+ riskyCapabilitySignals,
479
+ };
480
+ }
481
+
482
+ function hasWeakOutcomeEvidence(context) {
483
+ const report = context.outcomeReport || {};
484
+ const measurementMode = report.tokenCost?.measurementMode || "none";
485
+ return measurementMode === "none"
486
+ || measurementMode === "estimated"
487
+ || (report.evidenceContract?.weakFindings || []).length > 0
488
+ || (report.evidenceContract?.rejectedFindings || []).length > 0
489
+ || (report.summary?.needsAttention || []).length > 0;
490
+ }
491
+
492
+ function buildProductionConfidenceState(context) {
493
+ const report = context.productionReport || buildFallbackProductionReport();
494
+ return {
495
+ state: report.readiness?.state || "not_enough_evidence",
496
+ confidence: report.readiness?.confidence || "none",
497
+ nextAction: report.readiness?.safeNextAction || "Run `avorelo production-confidence --text` before shipping.",
498
+ };
499
+ }
500
+
501
+ function buildSkillBinding(context, candidate) {
502
+ const explicit = candidate.skillBinding;
503
+ if (explicit && typeof explicit === "object") {
504
+ return {
505
+ id: explicit.id || null,
506
+ sourceId: explicit.sourceId || null,
507
+ sourceTrust: explicit.sourceTrust || candidate.sourceTrust || "unknown",
508
+ verificationGates: Array.isArray(explicit.verificationGates) ? explicit.verificationGates.slice(0, 4) : [],
509
+ evidenceRequirements: Array.isArray(explicit.evidenceRequirements) ? explicit.evidenceRequirements.slice(0, 4) : [],
510
+ contextBudgetImpact: explicit.contextBudgetImpact || "unknown",
511
+ sideEffectLevel: explicit.sideEffectLevel || "unknown",
512
+ };
513
+ }
514
+
515
+ const selected = Array.isArray(context.skillsReport?.routing?.selected)
516
+ ? context.skillsReport.routing.selected[0]
517
+ : null;
518
+ if (!selected) return null;
519
+
520
+ const skillDetail = (context.skillsReport.skills || []).find((item) => item.id === selected.id) || null;
521
+ return {
522
+ id: selected.id || null,
523
+ sourceId: skillDetail?.sourceId || null,
524
+ sourceTrust: skillDetail?.trustLevel || "unknown",
525
+ verificationGates: Array.isArray(selected.verificationGates) ? selected.verificationGates.slice(0, 4) : [],
526
+ evidenceRequirements: Array.isArray(selected.evidenceRequirements) ? selected.evidenceRequirements.slice(0, 4) : [],
527
+ contextBudgetImpact: selected.contextBudgetImpact || skillDetail?.contextBudgetImpact || "unknown",
528
+ sideEffectLevel: selected.sideEffectLevel || skillDetail?.sideEffectLevel || "unknown",
529
+ };
530
+ }
531
+
532
+ function buildCapabilityBinding(context, candidate, skillBinding) {
533
+ const explicit = candidate.capabilityBinding;
534
+ if (explicit && typeof explicit === "object") {
535
+ return {
536
+ capabilityId: explicit.capabilityId || null,
537
+ capabilityLabel: explicit.capabilityLabel || explicit.capabilityId || null,
538
+ bindingDimension: explicit.bindingDimension || "unknown",
539
+ confidence: explicit.confidence || "medium",
540
+ };
541
+ }
542
+
543
+ if (!skillBinding?.id) return null;
544
+ const selected = Array.isArray(context.skillsReport?.routing?.selected)
545
+ ? context.skillsReport.routing.selected.find((item) => item.id === skillBinding.id)
546
+ : null;
547
+ const binding = Array.isArray(selected?.capabilityBindings) ? selected.capabilityBindings[0] : null;
548
+ if (!binding) return null;
549
+ return {
550
+ capabilityId: binding.capabilityId || null,
551
+ capabilityLabel: binding.capabilityLabel || binding.capabilityId || null,
552
+ bindingDimension: binding.bindingDimension || "unknown",
553
+ confidence: binding.confidence || "medium",
554
+ };
555
+ }
556
+
557
+ function buildEvidenceRefs(context, extra = []) {
558
+ return unique([
559
+ context.workspaceReport ? WORKSPACE_HYGIENE_REPORT_REL : null,
560
+ context.outcomeReport ? OUTCOME_LEARNING_REPORT_REL : null,
561
+ context.learningReport ? LEARNING_REPORT_REL : null,
562
+ context.productionReport ? PRODUCTION_CONFIDENCE_REPORT_REL : null,
563
+ context.skillsReport ? SKILLS_REPORT_REL : null,
564
+ "package.json",
565
+ ...(context.outcomeReport?.proofLinks || []).slice(0, 3),
566
+ ...(Array.isArray(extra) ? extra : []),
567
+ ]).slice(0, 8);
568
+ }
569
+
570
+ function normalizeScope(cwd, scope = {}) {
571
+ const normalized = {
572
+ paths: unique(normalizeList(scope.paths).map((item) => toRelativeRedactedPath(cwd, item))).filter(Boolean),
573
+ domains: unique(normalizeList(scope.domains).map((item) => sanitizeText(item, 120))).filter(Boolean),
574
+ toolNames: unique(normalizeList(scope.toolNames).map((item) => sanitizeText(item, 120))).filter(Boolean),
575
+ commandClass: sanitizeText(scope.commandClass, 80),
576
+ visibility: sanitizeText(scope.visibility, 80),
577
+ notes: sanitizeText(scope.notes, 140),
578
+ repoWide: scope.repoWide === true,
579
+ redacted: true,
580
+ };
581
+ return normalized;
582
+ }
583
+
584
+ function hasUnknownScope(normalizedScope) {
585
+ return normalizedScope.repoWide === true
586
+ || ((normalizedScope.paths || []).length === 0
587
+ && (normalizedScope.domains || []).length === 0
588
+ && (normalizedScope.toolNames || []).length === 0
589
+ && !normalizedScope.commandClass
590
+ && !normalizedScope.visibility);
591
+ }
592
+
593
+ function buildBaseDecisionRecord(cwd, candidate, context, type) {
594
+ const skillBinding = buildSkillBinding(context, candidate);
595
+ const capabilityBinding = buildCapabilityBinding(context, candidate, skillBinding);
596
+ const productionConfidenceState = buildProductionConfidenceState(context);
597
+ const workspaceRisk = buildWorkspaceRisk(context);
598
+ const requestedScope = normalizeScope(cwd, candidate.requestedScope || {});
599
+ const normalizedScope = normalizeScope(cwd, candidate.normalizedScope || candidate.requestedScope || {});
600
+ const sourceTrust = normalizeEnum(candidate.sourceTrust, new Set(["trusted", "known", "unknown", "untrusted"]), "unknown");
601
+ const id = candidate.id || hashId("boundary", {
602
+ type,
603
+ actionType: candidate.actionType,
604
+ exposureType: candidate.exposureType,
605
+ toolType: candidate.toolType,
606
+ intent: candidate.intent,
607
+ requestedScope,
608
+ sourceTrust,
609
+ });
610
+
611
+ return {
612
+ id,
613
+ type,
614
+ actionType: type === "action" ? normalizeEnum(candidate.actionType, ACTION_TYPES, "unknown") : null,
615
+ exposureType: type === "exposure" ? normalizeEnum(candidate.exposureType, EXPOSURE_TYPES, "unknown_exposure") : null,
616
+ toolType: type === "tool_scope" ? normalizeEnum(candidate.toolType, TOOL_TYPES, "unknown") : null,
617
+ browserTargetType: type === "action" && normalizeEnum(candidate.actionType, ACTION_TYPES, "unknown") === "browser_action"
618
+ ? normalizeBrowserTargetType(candidate.browserTargetType, normalizedScope)
619
+ : null,
620
+ browserActionCategory: type === "action" && normalizeEnum(candidate.actionType, ACTION_TYPES, "unknown") === "browser_action"
621
+ ? normalizeBrowserActionCategory(candidate.browserActionCategory)
622
+ : null,
623
+ intent: sanitizeText(candidate.intent || "", 180) || "No explicit intent provided.",
624
+ requestedScope,
625
+ normalizedScope,
626
+ minimalSafeScope: null,
627
+ sourceTrust,
628
+ capabilityBinding,
629
+ skillBinding,
630
+ productionConfidenceState,
631
+ workspaceRisk,
632
+ riskDimensions: buildRiskDimensions(),
633
+ sideEffectLevel: "none",
634
+ decision: "warn",
635
+ confidence: "low",
636
+ reasonCodes: [],
637
+ evidenceRefs: buildEvidenceRefs(context, candidate.evidenceRefs),
638
+ receiptRef: null,
639
+ safeNextAction: "Keep the action inside the smallest reviewed local scope.",
640
+ redacted: true,
641
+ };
642
+ }
643
+
644
+ function pushReason(reasons, condition, code) {
645
+ if (condition) reasons.add(code);
646
+ }
647
+
648
+ function updateDecisionIfStronger(record, nextDecision) {
649
+ const order = {
650
+ allow: 0,
651
+ warn: 1,
652
+ auto_limit: 2,
653
+ approval_required: 3,
654
+ block: 4,
655
+ };
656
+ if (order[nextDecision] > order[record.decision]) {
657
+ record.decision = nextDecision;
658
+ }
659
+ }
660
+
661
+ function applyCommonDecisionFloors(record, context) {
662
+ const reasons = new Set(record.reasonCodes || []);
663
+ const dimensions = record.riskDimensions || buildRiskDimensions();
664
+ const productionState = record.productionConfidenceState?.state || "not_enough_evidence";
665
+ const browserUnknownTarget = record.actionType === "browser_action" && record.browserTargetType === "unknown";
666
+
667
+ pushReason(reasons, record.sourceTrust === "unknown", "source_unknown");
668
+ pushReason(reasons, record.sourceTrust === "untrusted", "source_untrusted");
669
+ pushReason(reasons, hasUnknownScope(record.normalizedScope), "unknown_scope");
670
+ pushReason(reasons, record.ownerMissing === true, "missing_owner");
671
+ pushReason(reasons, record.minimalSafeScope, "safe_scope_available");
672
+ pushReason(reasons, !record.minimalSafeScope, "safe_scope_not_available");
673
+
674
+ if (dimensions.exec) reasons.add("requires_exec");
675
+ if (dimensions.network) reasons.add("requires_network");
676
+ if (dimensions.browser) reasons.add("requires_browser");
677
+ if (dimensions.secret) reasons.add("requires_secret_access");
678
+
679
+ if (record.type === "action" && record.actionType === "package_install") reasons.add("package_install_requested");
680
+ if (record.type === "action" && record.actionType === "package_publish") reasons.add("package_publish_requested");
681
+ if (record.type === "action" && record.actionType === "deploy_action") reasons.add("deploy_requested");
682
+ if (record.type === "exposure" && ["docs_public", "website_public", "npm_package_public", "public_claim"].includes(record.exposureType)) {
683
+ reasons.add("public_exposure_requested");
684
+ }
685
+
686
+ if (record.type === "action" && record.actionType === "file_write") {
687
+ if ((record.normalizedScope.paths || []).some((item) => item === "<outside-repo>")) reasons.add("writes_outside_repo");
688
+ if ((record.normalizedScope.paths || []).some((item) => isSourceFileTarget(item))) reasons.add("writes_source");
689
+ if ((record.normalizedScope.paths || []).some((item) => isConfigFileTarget(item))) reasons.add("writes_config");
690
+ }
691
+
692
+ if (record.type === "tool_scope" && record.toolType === "unknown" && context.workspaceReport?.summary?.unknownSources > 0) {
693
+ reasons.add("workspace_hygiene_unknown_tool");
694
+ }
695
+ if ((record.workspaceRisk?.riskyCapabilitySignals || 0) > 0 && (dimensions.write || dimensions.exec || dimensions.network || dimensions.browser)) {
696
+ reasons.add("workspace_hygiene_risky_capability");
697
+ }
698
+
699
+ if (record.skillBinding?.sourceTrust === "unknown" || record.skillBinding?.sourceTrust === "untrusted") {
700
+ reasons.add("skill_source_unknown");
701
+ }
702
+ if ((record.skillBinding?.verificationGates || []).length > 0 && countActiveRiskDimensions(dimensions) >= 2) {
703
+ reasons.add("skill_requires_verification_gate");
704
+ }
705
+
706
+ if (record.type !== "action" || record.actionType !== "file_read") {
707
+ if ((context.outcomeReport?.proofLinks || []).length === 0) {
708
+ reasons.add("outcome_evidence_missing");
709
+ } else if (hasWeakOutcomeEvidence(context)) {
710
+ reasons.add("outcome_evidence_weak");
711
+ }
712
+ }
713
+
714
+ if (productionState === "blocked" && (dimensions.production || dimensions.deploy || dimensions.public_exposure)) {
715
+ reasons.add("production_confidence_blocked");
716
+ } else if (productionState === "needs_review" && (dimensions.production || dimensions.deploy || dimensions.public_exposure)) {
717
+ reasons.add("production_confidence_needs_review");
718
+ }
719
+
720
+ const distributionState = context.publicDistributionReport?.distributionState || "private_local_only";
721
+ const blockedCount = context.publicDistributionReport?.installClaims?.blockedCount || 0;
722
+ if (record.type === "exposure" && record.exposureType === "npm_package_public" && distributionState !== "published_verified") {
723
+ reasons.add("public_distribution_not_verified");
724
+ }
725
+ if ((record.type === "exposure" && record.exposureType === "npm_package_public") || record.actionType === "package_publish") {
726
+ if (blockedCount > 0) reasons.add("install_claims_blocked");
727
+ }
728
+
729
+ if (record.sourceTrust === "untrusted" && (dimensions.write || dimensions.exec || dimensions.network || dimensions.browser || dimensions.public_exposure || dimensions.deploy)) {
730
+ updateDecisionIfStronger(record, "block");
731
+ } else if (
732
+ record.sourceTrust === "unknown"
733
+ && (dimensions.write || dimensions.exec || dimensions.network || dimensions.browser || dimensions.public_exposure || dimensions.deploy)
734
+ && !browserUnknownTarget
735
+ ) {
736
+ updateDecisionIfStronger(record, "approval_required");
737
+ }
738
+
739
+ if (hasUnknownScope(record.normalizedScope) && record.decision === "allow") {
740
+ updateDecisionIfStronger(record, countActiveRiskDimensions(dimensions) > 1 ? "approval_required" : "warn");
741
+ }
742
+ if (record.ownerMissing === true && record.decision === "allow") {
743
+ updateDecisionIfStronger(record, "warn");
744
+ }
745
+ if (productionState === "blocked" && (dimensions.production || dimensions.deploy || dimensions.public_exposure)) {
746
+ updateDecisionIfStronger(record, "block");
747
+ } else if (productionState === "needs_review" && (dimensions.production || dimensions.deploy || dimensions.public_exposure)) {
748
+ updateDecisionIfStronger(record, "approval_required");
749
+ }
750
+
751
+ record.reasonCodes = Array.from(reasons);
752
+ record.sideEffectLevel = computeSideEffectLevel(dimensions);
753
+ record.confidence = inferConfidence(record, context);
754
+ record.safeNextAction = buildSafeNextAction(record, context);
755
+ return record;
756
+ }
757
+
758
+ function inferConfidence(record, context) {
759
+ if (record.decision === "block" && record.reasonCodes.length > 0) return "high";
760
+ if (record.sourceTrust === "trusted" && record.decision === "allow" && !hasUnknownScope(record.normalizedScope)) return "high";
761
+ if ((record.sourceTrust === "unknown" && !(record.actionType === "browser_action" && record.browserTargetType === "unknown")) || hasUnknownScope(record.normalizedScope)) return "low";
762
+ if (hasWeakOutcomeEvidence(context) && (record.riskDimensions.public_exposure || record.riskDimensions.deploy || record.riskDimensions.production)) {
763
+ return "low";
764
+ }
765
+ if (record.decision === "allow" || record.decision === "warn") return "medium";
766
+ return "medium";
767
+ }
768
+
769
+ function buildSafeNextAction(record, context) {
770
+ if (record.decision === "block") {
771
+ if (record.minimalSafeScope) return `Do not proceed as requested. Use the reduced scope instead: ${record.minimalSafeScope.notes || "review the bounded alternative first."}`;
772
+ return "Do not proceed with the blocked request. Keep the work local, redacted, and non-executing.";
773
+ }
774
+ if (record.decision === "approval_required") {
775
+ if (record.minimalSafeScope) return `Approve only the reduced scope: ${record.minimalSafeScope.notes || "use the bounded alternative first."}`;
776
+ return "Request explicit approval before moving beyond the current bounded local review.";
777
+ }
778
+ if (record.decision === "auto_limit") {
779
+ return record.minimalSafeScope?.notes || "Use the deterministic reduced scope instead of the broader request.";
780
+ }
781
+ if (record.decision === "warn") {
782
+ return record.minimalSafeScope?.notes
783
+ || context.productionReport?.readiness?.safeNextAction
784
+ || context.workspaceReport?.summary?.safeNextAction
785
+ || "Keep the work reviewed, bounded, and redacted before expanding scope.";
786
+ }
787
+ return "Proceed inside the reviewed local scope and keep outputs redacted.";
788
+ }
789
+
790
+ function evaluateActionCandidate(cwd, candidate, context) {
791
+ const record = buildBaseDecisionRecord(cwd, candidate, context, "action");
792
+ const reasons = new Set(record.reasonCodes);
793
+ const scopePaths = record.normalizedScope.paths || [];
794
+ const firstPath = scopePaths[0] || "";
795
+ const writeProfile = scopePaths.length > 0 ? classifyFilesystemWriteTarget(cwd, firstPath === "<outside-repo>" ? "../outside" : firstPath) : null;
796
+ const packageProfile = candidate.actionType === "package_install" || candidate.actionType === "package_publish"
797
+ ? classifyPackageAction({
798
+ actionType: candidate.actionType === "package_install" ? "package.install" : "package.run",
799
+ command: candidate.command || (candidate.actionType === "package_install" ? "npm install example-package" : "npm publish --access public"),
800
+ target: scopePaths[0] || "",
801
+ packageManager: candidate.packageManager || "npm",
802
+ packageName: candidate.packageName || "example-package",
803
+ })
804
+ : null;
805
+ const mcpProfile = candidate.actionType === "mcp_tool_call"
806
+ ? classifyMcpAction({
807
+ actionType: candidate.mcpActionType || "mcp.write",
808
+ mcpServer: candidate.mcpServer || "unknown",
809
+ toolName: candidate.toolName || "unknown",
810
+ target: candidate.target || `${candidate.mcpServer || "unknown"}:${candidate.toolName || "unknown"}`,
811
+ resource: candidate.resource || {},
812
+ args: candidate.args || {},
813
+ })
814
+ : null;
815
+ const browserTargetType = record.actionType === "browser_action"
816
+ ? normalizeBrowserTargetType(candidate.browserTargetType, record.normalizedScope)
817
+ : "unknown";
818
+ const browserActionCategory = record.actionType === "browser_action"
819
+ ? normalizeBrowserActionCategory(candidate.browserActionCategory)
820
+ : null;
821
+ const localBrowserTarget = browserTargetType === "localhost" || browserTargetType === "local_file";
822
+
823
+ let dimensions = buildRiskDimensions({
824
+ read: record.actionType === "file_read",
825
+ write: ["file_write", "config_change", "public_content_change"].includes(record.actionType),
826
+ exec: ["command_run", "package_install", "package_publish", "mcp_tool_call", "release_action"].includes(record.actionType),
827
+ network: record.actionType === "network_request"
828
+ || packageProfile?.installFromUrlOrPath === true
829
+ || (record.actionType === "browser_action" && !localBrowserTarget && browserTargetType !== "unknown"),
830
+ secret: record.actionType === "secret_access" || isSensitiveWriteTarget(firstPath),
831
+ browser: record.actionType === "browser_action",
832
+ package: ["package_install", "package_publish"].includes(record.actionType),
833
+ deploy: ["deploy_action", "release_action"].includes(record.actionType) || /\bdeploy\b/i.test(String(candidate.command || "")),
834
+ production: ["deploy_action", "release_action"].includes(record.actionType) || browserTargetType === "production_url",
835
+ public_exposure: record.actionType === "public_content_change",
836
+ config: record.actionType === "config_change" || isConfigFileTarget(firstPath),
837
+ path: scopePaths.length > 0,
838
+ external_service: record.actionType === "network_request" || (record.actionType === "browser_action" && !localBrowserTarget),
839
+ unknown_scope: hasUnknownScope(record.normalizedScope),
840
+ });
841
+
842
+ if (record.actionType === "file_read" && record.sourceTrust === "trusted" && !hasUnknownScope(record.normalizedScope)) {
843
+ record.decision = "allow";
844
+ reasons.add("trusted_read_only");
845
+ } else if (record.actionType === "file_write") {
846
+ if (firstPath === "<outside-repo>") {
847
+ record.decision = "block";
848
+ } else if (isSensitiveWriteTarget(firstPath) || /\.npmrc$/i.test(firstPath) || /\.env(?:\..+)?$/i.test(firstPath)) {
849
+ record.decision = "block";
850
+ } else if (isSourceFileTarget(firstPath) || isConfigFileTarget(firstPath)) {
851
+ record.decision = "approval_required";
852
+ } else if (isDocsFileTarget(firstPath) || isTestFileTarget(firstPath)) {
853
+ record.decision = "warn";
854
+ } else {
855
+ record.decision = "approval_required";
856
+ }
857
+ } else if (record.actionType === "command_run") {
858
+ record.decision = /\b(rm\s+-rf|del\s+\/[qs]|remove-item\b|git\s+reset\b|git\s+push\b.*--force)\b/i.test(String(candidate.command || ""))
859
+ ? "block"
860
+ : "approval_required";
861
+ } else if (record.actionType === "package_install") {
862
+ record.decision = packageProfile?.recommendedDecision === "deny" ? "block" : "approval_required";
863
+ record.minimalSafeScope = normalizeScope(cwd, {
864
+ paths: ["package.json", "package-lock.json"],
865
+ notes: "Review package metadata and lockfile impact locally before any install request is approved.",
866
+ });
867
+ } else if (record.actionType === "package_publish") {
868
+ record.decision = "block";
869
+ } else if (record.actionType === "mcp_tool_call") {
870
+ record.decision = mcpProfile?.readOnly === true && record.sourceTrust === "trusted"
871
+ ? "warn"
872
+ : (mcpProfile?.recommendedDecision === "deny" ? "block" : "approval_required");
873
+ if (mcpProfile?.readOnly !== true) {
874
+ record.minimalSafeScope = normalizeScope(cwd, {
875
+ toolNames: [candidate.toolName || "mcp-read-only-preview"],
876
+ notes: "Reduce the MCP request to read-only metadata inspection first.",
877
+ });
878
+ }
879
+ } else if (record.actionType === "browser_action") {
880
+ if (browserActionCategory === "production_browse" || browserTargetType === "production_url") {
881
+ record.decision = "block";
882
+ record.minimalSafeScope = normalizeScope(cwd, {
883
+ visibility: "local_plan_only",
884
+ notes: "Production browsing stays blocked. Keep proof scope local-only in v1.",
885
+ });
886
+ } else if (browserActionCategory === "form_submit") {
887
+ record.decision = "block";
888
+ record.minimalSafeScope = normalizeScope(cwd, {
889
+ visibility: "local_plan_only",
890
+ notes: "Do not submit forms. Capture bounded local proof without form actions.",
891
+ });
892
+ } else if (browserActionCategory === "credential_flow") {
893
+ record.decision = localBrowserTarget ? "approval_required" : "block";
894
+ record.minimalSafeScope = normalizeScope(cwd, {
895
+ visibility: "local_plan_only",
896
+ notes: "Do not use credentials by default. Keep browser proof to anonymous local evidence.",
897
+ });
898
+ } else if (browserActionCategory === "external_browse_request" || browserTargetType === "external_url") {
899
+ record.decision = "approval_required";
900
+ record.minimalSafeScope = normalizeScope(cwd, {
901
+ domains: record.normalizedScope.domains,
902
+ visibility: "local_plan_only",
903
+ notes: "Keep browser work at plan-only scope until a reviewed external target is explicitly approved.",
904
+ });
905
+ } else if (browserActionCategory === "screenshot_reference") {
906
+ if (localBrowserTarget) {
907
+ record.decision = "allow";
908
+ reasons.add("trusted_read_only");
909
+ } else if (browserTargetType === "unknown") {
910
+ record.decision = "warn";
911
+ } else {
912
+ record.decision = "approval_required";
913
+ record.minimalSafeScope = normalizeScope(cwd, {
914
+ visibility: "local_plan_only",
915
+ notes: "Keep screenshot and visual-hint scope local and redacted only.",
916
+ });
917
+ }
918
+ } else if (browserActionCategory === "visual_hint") {
919
+ if (localBrowserTarget || browserTargetType === "unknown") {
920
+ record.decision = "warn";
921
+ } else {
922
+ record.decision = "approval_required";
923
+ record.minimalSafeScope = normalizeScope(cwd, {
924
+ visibility: "local_plan_only",
925
+ notes: "Keep screenshot and visual-hint scope local and redacted only.",
926
+ });
927
+ }
928
+ } else if (["page_reachability_check", "dom_presence_check", "console_summary_check", "network_summary_check"].includes(browserActionCategory)) {
929
+ if (localBrowserTarget) {
930
+ record.decision = "allow";
931
+ reasons.add("trusted_read_only");
932
+ } else if (browserTargetType === "unknown") {
933
+ record.decision = "warn";
934
+ } else {
935
+ record.decision = "approval_required";
936
+ record.minimalSafeScope = normalizeScope(cwd, {
937
+ domains: record.normalizedScope.domains,
938
+ visibility: "local_plan_only",
939
+ notes: "Keep browser proof read-only and local-first until a reviewed external target is explicitly approved.",
940
+ });
941
+ }
942
+ } else {
943
+ record.decision = localBrowserTarget ? "warn" : "approval_required";
944
+ if (!localBrowserTarget) {
945
+ record.minimalSafeScope = normalizeScope(cwd, {
946
+ domains: record.normalizedScope.domains,
947
+ visibility: "local_plan_only",
948
+ notes: "Keep browser work at plan-only scope until the target is explicitly reviewed.",
949
+ });
950
+ }
951
+ }
952
+ } else if (record.actionType === "network_request") {
953
+ record.decision = "approval_required";
954
+ record.minimalSafeScope = normalizeScope(cwd, {
955
+ visibility: "no_network_summary_only",
956
+ notes: "Prepare a no-network local summary first instead of making the request.",
957
+ });
958
+ } else if (record.actionType === "secret_access") {
959
+ record.decision = "block";
960
+ } else if (record.actionType === "deploy_action") {
961
+ record.decision = record.productionConfidenceState.state === "blocked" ? "block" : "approval_required";
962
+ } else if (record.actionType === "config_change") {
963
+ record.decision = isSensitiveWriteTarget(firstPath) || /\.npmrc$/i.test(firstPath) ? "block" : "approval_required";
964
+ } else if (record.actionType === "public_content_change") {
965
+ record.decision = "auto_limit";
966
+ record.minimalSafeScope = normalizeScope(cwd, {
967
+ paths: ["docs/internal-draft.md"],
968
+ visibility: "repo_internal",
969
+ notes: "Keep public-facing copy changes as an internal draft until claims and proof are reviewed.",
970
+ });
971
+ } else if (record.actionType === "release_action") {
972
+ record.decision = "approval_required";
973
+ } else {
974
+ record.decision = hasUnknownScope(record.normalizedScope) ? "approval_required" : "warn";
975
+ }
976
+
977
+ if (writeProfile?.sourceFile) reasons.add("writes_source");
978
+ if (writeProfile?.configFile) reasons.add("writes_config");
979
+ if (!writeProfile?.insideProject && scopePaths.length > 0) reasons.add("writes_outside_repo");
980
+
981
+ record.riskDimensions = dimensions;
982
+ record.reasonCodes = Array.from(reasons);
983
+ return applyCommonDecisionFloors(record, context);
984
+ }
985
+
986
+ function evaluateExposureCandidate(cwd, candidate, context) {
987
+ const record = buildBaseDecisionRecord(cwd, candidate, context, "exposure");
988
+ const reasons = new Set(record.reasonCodes);
989
+ const claimRisk = normalizeEnum(candidate.claimRisk, new Set(["none", "bounded", "unsupported"]), "none");
990
+ const distributionState = context.publicDistributionReport?.distributionState || "private_local_only";
991
+ const installClaimsBlocked = context.publicDistributionReport?.installClaims?.blockedCount || 0;
992
+ const productionState = record.productionConfidenceState.state;
993
+
994
+ record.riskDimensions = buildRiskDimensions({
995
+ network: ["external_network", "browser_external"].includes(record.exposureType),
996
+ browser: record.exposureType === "browser_external",
997
+ deploy: record.exposureType === "production_deploy",
998
+ production: record.exposureType === "production_deploy",
999
+ public_exposure: ["docs_public", "website_public", "npm_package_public", "public_claim"].includes(record.exposureType),
1000
+ config: record.exposureType === "credential_or_config",
1001
+ secret: record.exposureType === "credential_or_config",
1002
+ external_service: ["external_network", "browser_external", "production_deploy", "npm_package_public", "website_public"].includes(record.exposureType),
1003
+ unknown_scope: hasUnknownScope(record.normalizedScope) || record.exposureType === "unknown_exposure",
1004
+ });
1005
+
1006
+ if (record.exposureType === "local_only") {
1007
+ record.decision = "allow";
1008
+ reasons.add("trusted_read_only");
1009
+ } else if (record.exposureType === "repo_internal") {
1010
+ record.decision = "allow";
1011
+ } else if (record.exposureType === "docs_public") {
1012
+ record.decision = claimRisk === "unsupported" ? "approval_required" : "warn";
1013
+ } else if (record.exposureType === "website_public") {
1014
+ record.decision = claimRisk === "unsupported" ? "approval_required" : "warn";
1015
+ } else if (record.exposureType === "npm_package_public") {
1016
+ if (distributionState !== "published_verified" || installClaimsBlocked > 0) {
1017
+ record.decision = "block";
1018
+ } else {
1019
+ record.decision = hasWeakOutcomeEvidence(context) ? "warn" : "allow";
1020
+ }
1021
+ } else if (record.exposureType === "external_network" || record.exposureType === "browser_external") {
1022
+ record.decision = "approval_required";
1023
+ } else if (record.exposureType === "production_deploy") {
1024
+ record.decision = productionState === "blocked" ? "block" : "approval_required";
1025
+ } else if (record.exposureType === "credential_or_config") {
1026
+ record.decision = "block";
1027
+ } else if (record.exposureType === "customer_data_adjacent") {
1028
+ record.decision = "approval_required";
1029
+ } else if (record.exposureType === "public_claim") {
1030
+ record.decision = claimRisk === "unsupported" ? "block" : "warn";
1031
+ } else {
1032
+ record.decision = "approval_required";
1033
+ }
1034
+
1035
+ if (claimRisk === "unsupported") reasons.add("public_claim_risk");
1036
+ record.reasonCodes = Array.from(reasons);
1037
+ return applyCommonDecisionFloors(record, context);
1038
+ }
1039
+
1040
+ function evaluateToolCandidate(cwd, candidate, context) {
1041
+ const record = buildBaseDecisionRecord(cwd, candidate, context, "tool_scope");
1042
+ const reasons = new Set(record.reasonCodes);
1043
+
1044
+ record.riskDimensions = buildRiskDimensions({
1045
+ read: record.toolType === "local_read" || record.toolType === "mcp_read",
1046
+ write: record.toolType === "local_write" || record.toolType === "mcp_write",
1047
+ exec: ["command", "package_manager", "mcp_write", "unknown"].includes(record.toolType),
1048
+ network: ["network", "browser", "package_manager", "unknown"].includes(record.toolType),
1049
+ browser: record.toolType === "browser",
1050
+ package: record.toolType === "package_manager",
1051
+ deploy: record.toolType === "deploy",
1052
+ production: record.toolType === "deploy",
1053
+ secret: record.toolType === "secret",
1054
+ public_exposure: false,
1055
+ config: record.toolType === "local_write" && (record.normalizedScope.paths || []).some((item) => isConfigFileTarget(item)),
1056
+ path: (record.normalizedScope.paths || []).length > 0,
1057
+ external_service: ["network", "browser", "deploy", "mcp_write", "unknown"].includes(record.toolType),
1058
+ unknown_scope: hasUnknownScope(record.normalizedScope) || record.toolType === "unknown",
1059
+ });
1060
+
1061
+ if (record.toolType === "local_read" && record.sourceTrust === "trusted" && !hasUnknownScope(record.normalizedScope)) {
1062
+ record.decision = "allow";
1063
+ reasons.add("trusted_read_only");
1064
+ } else if (record.toolType === "local_write") {
1065
+ record.decision = "auto_limit";
1066
+ record.minimalSafeScope = normalizeScope(cwd, {
1067
+ paths: ["docs/**", "tests/**"],
1068
+ visibility: "repo_internal",
1069
+ notes: "Reduce write scope to docs/tests-only until a narrower reviewed source write is approved.",
1070
+ });
1071
+ } else if (record.toolType === "command") {
1072
+ record.decision = "approval_required";
1073
+ } else if (record.toolType === "package_manager") {
1074
+ record.decision = "approval_required";
1075
+ record.minimalSafeScope = normalizeScope(cwd, {
1076
+ paths: ["package.json", "package-lock.json"],
1077
+ visibility: "repo_internal",
1078
+ notes: "Review dependency metadata only before using a package manager with install capability.",
1079
+ });
1080
+ } else if (record.toolType === "mcp_read") {
1081
+ record.decision = record.sourceTrust === "trusted" ? "allow" : "warn";
1082
+ } else if (record.toolType === "mcp_write") {
1083
+ record.decision = record.sourceTrust === "untrusted" ? "block" : "approval_required";
1084
+ record.minimalSafeScope = normalizeScope(cwd, {
1085
+ toolNames: ["read_only_metadata"],
1086
+ visibility: "repo_internal",
1087
+ notes: "Start with read-only MCP inspection instead of write-capable access.",
1088
+ });
1089
+ } else if (record.toolType === "browser") {
1090
+ record.decision = "approval_required";
1091
+ record.minimalSafeScope = normalizeScope(cwd, {
1092
+ visibility: "local_plan_only",
1093
+ notes: "Keep browser scope to local planning until the external target is explicitly reviewed.",
1094
+ });
1095
+ } else if (record.toolType === "network") {
1096
+ record.decision = "approval_required";
1097
+ record.minimalSafeScope = normalizeScope(cwd, {
1098
+ visibility: "no_network_summary_only",
1099
+ notes: "Use a no-network local summary before requesting network scope.",
1100
+ });
1101
+ } else if (record.toolType === "deploy" || record.toolType === "secret") {
1102
+ record.decision = "block";
1103
+ } else {
1104
+ record.decision = (record.riskDimensions.write || record.riskDimensions.network) ? "approval_required" : "warn";
1105
+ record.minimalSafeScope = normalizeScope(cwd, {
1106
+ toolNames: ["local_read_only"],
1107
+ visibility: "repo_internal",
1108
+ notes: "Reduce unknown tool scope to local read-only inspection first.",
1109
+ });
1110
+ }
1111
+
1112
+ record.reasonCodes = Array.from(reasons);
1113
+ return applyCommonDecisionFloors(record, context);
1114
+ }
1115
+
1116
+ function buildReceiptFromDecision(record) {
1117
+ return {
1118
+ receiptId: hashId("boundary-receipt", {
1119
+ id: record.id,
1120
+ decision: record.decision,
1121
+ reasonCodes: record.reasonCodes,
1122
+ }),
1123
+ generatedAt: nowIso(),
1124
+ type: record.type,
1125
+ decision: record.decision,
1126
+ actionType: record.actionType,
1127
+ exposureType: record.exposureType,
1128
+ toolType: record.toolType,
1129
+ normalizedScope: record.normalizedScope,
1130
+ minimalSafeScope: record.minimalSafeScope,
1131
+ reasonCodes: record.reasonCodes,
1132
+ evidenceRefs: record.evidenceRefs,
1133
+ redacted: true,
1134
+ safeNextAction: record.safeNextAction,
1135
+ };
1136
+ }
1137
+
1138
+ function buildDefaultCandidates(cwd) {
1139
+ const hasDocs = toRelativeRedactedPath(cwd, "docs") !== "<outside-repo>";
1140
+ return {
1141
+ actions: [
1142
+ {
1143
+ id: "boundary-action-file-read",
1144
+ actionType: "file_read",
1145
+ intent: "Inspect local package metadata.",
1146
+ sourceTrust: "trusted",
1147
+ requestedScope: { paths: ["package.json"], visibility: "repo_internal" },
1148
+ },
1149
+ {
1150
+ id: "boundary-action-file-write-source",
1151
+ actionType: "file_write",
1152
+ intent: "Change existing source code.",
1153
+ sourceTrust: "trusted",
1154
+ requestedScope: { paths: ["scripts/lib/public-cli.js"], visibility: "repo_internal" },
1155
+ },
1156
+ {
1157
+ id: "boundary-action-command-run",
1158
+ actionType: "command_run",
1159
+ intent: "Run a local command that may change repo state.",
1160
+ sourceTrust: "known",
1161
+ command: "git push origin HEAD",
1162
+ requestedScope: { commandClass: "local_command", visibility: "repo_internal" },
1163
+ },
1164
+ {
1165
+ id: "boundary-action-public-content",
1166
+ actionType: "public_content_change",
1167
+ intent: "Draft public-facing copy updates without publishing them.",
1168
+ sourceTrust: "trusted",
1169
+ requestedScope: { paths: [hasDocs ? "docs/articles" : "docs"], visibility: "public_draft" },
1170
+ },
1171
+ ],
1172
+ exposures: [
1173
+ {
1174
+ id: "boundary-exposure-local",
1175
+ exposureType: "local_only",
1176
+ intent: "Keep analysis local to the repo.",
1177
+ sourceTrust: "trusted",
1178
+ requestedScope: { visibility: "local_only" },
1179
+ },
1180
+ {
1181
+ id: "boundary-exposure-repo",
1182
+ exposureType: "repo_internal",
1183
+ intent: "Share a redacted draft inside the repo only.",
1184
+ sourceTrust: "trusted",
1185
+ requestedScope: { visibility: "repo_internal" },
1186
+ },
1187
+ {
1188
+ id: "boundary-exposure-docs",
1189
+ exposureType: "docs_public",
1190
+ intent: "Prepare customer-facing docs with bounded claims.",
1191
+ sourceTrust: "known",
1192
+ claimRisk: "bounded",
1193
+ requestedScope: { visibility: "docs_public" },
1194
+ },
1195
+ {
1196
+ id: "boundary-exposure-package",
1197
+ exposureType: "npm_package_public",
1198
+ intent: "Describe the public npm package without changing distribution state.",
1199
+ sourceTrust: "known",
1200
+ claimRisk: "bounded",
1201
+ requestedScope: { visibility: "npm_package_public" },
1202
+ },
1203
+ ],
1204
+ tools: [
1205
+ {
1206
+ id: "boundary-tool-local-read",
1207
+ toolType: "local_read",
1208
+ intent: "Read local files only.",
1209
+ sourceTrust: "trusted",
1210
+ requestedScope: { paths: ["package.json"], visibility: "repo_internal" },
1211
+ },
1212
+ {
1213
+ id: "boundary-tool-local-write",
1214
+ toolType: "local_write",
1215
+ intent: "Request broad write access to the repo.",
1216
+ sourceTrust: "known",
1217
+ requestedScope: { paths: ["src/**", "docs/**"], repoWide: true, visibility: "repo_internal" },
1218
+ },
1219
+ {
1220
+ id: "boundary-tool-package-manager",
1221
+ toolType: "package_manager",
1222
+ intent: "Use a package manager with install capability.",
1223
+ sourceTrust: "known",
1224
+ requestedScope: { paths: ["package.json", "package-lock.json"], visibility: "repo_internal" },
1225
+ },
1226
+ {
1227
+ id: "boundary-tool-browser",
1228
+ toolType: "browser",
1229
+ intent: "Use an external browser-capable tool.",
1230
+ sourceTrust: "unknown",
1231
+ requestedScope: { domains: ["example.com"], visibility: "browser_external" },
1232
+ },
1233
+ ],
1234
+ };
1235
+ }
1236
+
1237
+ function buildOutcomeReadiness(report, context) {
1238
+ const found = [];
1239
+ const fixed = [];
1240
+ const savedOrProved = [];
1241
+ const needsAttention = [];
1242
+
1243
+ if (report.summary.blocked > 0) {
1244
+ found.push(`${report.summary.blocked} boundary pattern(s) are blocked by deterministic policy.`);
1245
+ }
1246
+ if (report.summary.approvalRequired > 0) {
1247
+ found.push(`${report.summary.approvalRequired} pattern(s) still require explicit approval.`);
1248
+ }
1249
+ if (report.summary.autoLimited > 0) {
1250
+ fixed.push(`${report.summary.autoLimited} broad request(s) were reduced to a smaller safe scope.`);
1251
+ }
1252
+
1253
+ savedOrProved.push(`${report.summary.receiptsCreated} redacted local boundary receipt(s) were prepared.`);
1254
+ savedOrProved.push("LLM decisions remain disabled for boundary outcomes.");
1255
+ if (context.skillsReport) savedOrProved.push("Skills routing stays thin, local, and verification-gated.");
1256
+ if (context.publicDistributionReport?.distributionState === "published_verified") {
1257
+ savedOrProved.push("Public distribution remains published_verified.");
1258
+ }
1259
+
1260
+ if (hasWeakOutcomeEvidence(context)) {
1261
+ needsAttention.push(context.outcomeReport?.summary?.safeNextAction || "Strengthen outcome evidence before widening public or deploy-adjacent scope.");
1262
+ }
1263
+ if ((context.workspaceReport?.summary?.unknownSources || 0) > 0) {
1264
+ needsAttention.push(context.workspaceReport.summary.safeNextAction);
1265
+ }
1266
+ if ((context.productionReport?.readiness?.state || "") === "needs_review") {
1267
+ needsAttention.push(context.productionReport.readiness.safeNextAction);
1268
+ }
1269
+ if (report.summary.safeNextAction) {
1270
+ needsAttention.push(report.summary.safeNextAction);
1271
+ }
1272
+
1273
+ return {
1274
+ found: unique(found),
1275
+ fixed: unique(fixed),
1276
+ savedOrProved: unique(savedOrProved),
1277
+ needsAttention: unique(needsAttention).slice(0, 4),
1278
+ };
1279
+ }
1280
+
1281
+ function summarizeDecisions(decisions) {
1282
+ const summary = {
1283
+ actionsReviewed: 0,
1284
+ exposuresReviewed: 0,
1285
+ toolsReviewed: 0,
1286
+ allow: 0,
1287
+ warn: 0,
1288
+ approvalRequired: 0,
1289
+ autoLimited: 0,
1290
+ blocked: 0,
1291
+ unknownScope: 0,
1292
+ receiptsCreated: 0,
1293
+ safeNextAction: "No decision data available.",
1294
+ };
1295
+
1296
+ decisions.forEach((decision) => {
1297
+ if (decision.type === "action") summary.actionsReviewed += 1;
1298
+ if (decision.type === "exposure") summary.exposuresReviewed += 1;
1299
+ if (decision.type === "tool_scope") summary.toolsReviewed += 1;
1300
+ if (decision.decision === "allow") summary.allow += 1;
1301
+ if (decision.decision === "warn") summary.warn += 1;
1302
+ if (decision.decision === "approval_required") summary.approvalRequired += 1;
1303
+ if (decision.decision === "auto_limit") summary.autoLimited += 1;
1304
+ if (decision.decision === "block") summary.blocked += 1;
1305
+ if (decision.reasonCodes.includes("unknown_scope")) summary.unknownScope += 1;
1306
+ });
1307
+
1308
+ const next = decisions.find((item) => item.decision === "block")
1309
+ || decisions.find((item) => item.decision === "approval_required")
1310
+ || decisions.find((item) => item.decision === "auto_limit")
1311
+ || decisions.find((item) => item.decision === "warn")
1312
+ || decisions[0];
1313
+ summary.safeNextAction = next?.safeNextAction || summary.safeNextAction;
1314
+ return summary;
1315
+ }
1316
+
1317
+ function buildClaimSafety() {
1318
+ return {
1319
+ autonomousExecution: false,
1320
+ approvalBypassed: false,
1321
+ enterpriseIamReplacement: false,
1322
+ securityGuaranteed: false,
1323
+ productionCertified: false,
1324
+ liveEnforcementEverywhere: false,
1325
+ externalScanPerformed: false,
1326
+ };
1327
+ }
1328
+
1329
+ function buildPolicy() {
1330
+ return {
1331
+ deterministic: true,
1332
+ llmDecisionMaker: false,
1333
+ localOnly: true,
1334
+ upload: false,
1335
+ providerApis: false,
1336
+ toolExecution: false,
1337
+ browserExecution: false,
1338
+ deployExecution: false,
1339
+ };
1340
+ }
1341
+
1342
+ function buildDogfoodSection(cwd) {
1343
+ const pkg = readPackageJson(cwd) || {};
1344
+ const packageName = String(pkg.name || "").trim().toLowerCase();
1345
+ return {
1346
+ ranOnAvorelo: packageName === "avorelo" || /claudecode-optimizer/i.test(String(cwd || "")),
1347
+ commands: DOGFOOD_COMMANDS,
1348
+ summary: "Avorelo can classify risky action intent, public exposure, and tool scope on itself without executing the action.",
1349
+ };
1350
+ }
1351
+
1352
+ function buildGovernedActionExposureReport(cwd = process.cwd(), options = {}) {
1353
+ const context = buildIntegrationContext(cwd, {
1354
+ ...options,
1355
+ buildIfMissing: options.buildIfMissing === true,
1356
+ });
1357
+ const defaults = buildDefaultCandidates(cwd);
1358
+ const actionCandidates = Array.isArray(options.actions) ? options.actions : defaults.actions;
1359
+ const exposureCandidates = Array.isArray(options.exposures) ? options.exposures : defaults.exposures;
1360
+ const toolCandidates = Array.isArray(options.tools) ? options.tools : defaults.tools;
1361
+
1362
+ const decisions = [
1363
+ ...actionCandidates.map((candidate) => evaluateActionCandidate(cwd, candidate, context)),
1364
+ ...exposureCandidates.map((candidate) => evaluateExposureCandidate(cwd, candidate, context)),
1365
+ ...toolCandidates.map((candidate) => evaluateToolCandidate(cwd, candidate, context)),
1366
+ ];
1367
+
1368
+ const receipts = decisions.map((decision) => {
1369
+ const receipt = buildReceiptFromDecision(decision);
1370
+ decision.receiptRef = receipt.receiptId;
1371
+ return receipt;
1372
+ });
1373
+
1374
+ const summary = summarizeDecisions(decisions);
1375
+ summary.receiptsCreated = receipts.length;
1376
+ const outcomeReadiness = buildOutcomeReadiness({
1377
+ summary,
1378
+ }, context);
1379
+
1380
+ const report = {
1381
+ contract: CONTRACT,
1382
+ schemaVersion: SCHEMA_VERSION,
1383
+ generatedAt: nowIso(),
1384
+ status: summary.blocked > 0 ? "fail" : (summary.warn > 0 || summary.approvalRequired > 0 || summary.autoLimited > 0 || summary.unknownScope > 0 ? "warn" : "pass"),
1385
+ redacted: true,
1386
+ summary,
1387
+ decisions,
1388
+ policy: buildPolicy(),
1389
+ receipts,
1390
+ outcomeReadiness,
1391
+ scopeRepair: buildScopeRepairIntegration(cwd, options.integrationOverrides || {}),
1392
+ claimSafety: buildClaimSafety(),
1393
+ dogfood: buildDogfoodSection(cwd),
1394
+ };
1395
+
1396
+ return report;
1397
+ }
1398
+
1399
+ function writeGovernedActionExposureReport(cwd, report) {
1400
+ safeWriteJson(cwd, LATEST_REPORT_REL, report);
1401
+ return {
1402
+ jsonPath: LATEST_REPORT_REL,
1403
+ };
1404
+ }
1405
+
1406
+ function generateGovernedActionExposureReport(cwd = process.cwd(), options = {}) {
1407
+ const report = buildGovernedActionExposureReport(cwd, options);
1408
+ const written = writeGovernedActionExposureReport(cwd, report);
1409
+ return {
1410
+ report,
1411
+ written: true,
1412
+ jsonPath: written.jsonPath,
1413
+ };
1414
+ }
1415
+
1416
+ function readLatestGovernedActionExposureReport(cwd) {
1417
+ return safeReadJson(cwd, LATEST_REPORT_REL, null);
1418
+ }
1419
+
1420
+ function buildScopeRepairIntegration(cwd, options = {}) {
1421
+ const report = options.scopeRepairReport || readLatestScopeRepairReport(cwd);
1422
+ if (!report) return null;
1423
+ return {
1424
+ status: report.status,
1425
+ mismatchesDetected: Number(report.summary?.mismatchesDetected || 0),
1426
+ repairsApplied: Number(report.summary?.scopeRepairsApplied || 0),
1427
+ approvalsAvoided: Number(report.summary?.approvalsAvoided || 0),
1428
+ approvalsStillRequired: Number(report.summary?.approvalsStillRequired || 0),
1429
+ safeNextAction: sanitizeText(report.summary?.safeNextAction, 180),
1430
+ evidenceRef: SCOPE_REPAIR_REPORT_REL,
1431
+ };
1432
+ }
1433
+
1434
+ function formatGovernedActionExposureText(report) {
1435
+ const foundLine = report.outcomeReadiness.found.length > 0
1436
+ ? report.outcomeReadiness.found.slice(0, 2).join(" ")
1437
+ : "No high-risk boundary gaps detected.";
1438
+ const autoLimitedLine = report.decisions
1439
+ .filter((item) => item.decision === "auto_limit")
1440
+ .slice(0, 2)
1441
+ .map((item) => item.intent)
1442
+ .join(" ");
1443
+ const needsAttentionLine = report.outcomeReadiness.needsAttention.length > 0
1444
+ ? report.outcomeReadiness.needsAttention.slice(0, 2).join(" ")
1445
+ : "None.";
1446
+
1447
+ return [
1448
+ "Avorelo Boundary",
1449
+ `Status: ${String(report.status || "warn").toUpperCase()}`,
1450
+ `Reviewed: ${report.summary.actionsReviewed} actions, ${report.summary.exposuresReviewed} exposures, ${report.summary.toolsReviewed} tools`,
1451
+ `Decisions: ${report.summary.allow} allow, ${report.summary.warn} warn, ${report.summary.approvalRequired} approval required, ${report.summary.autoLimited} auto-limited, ${report.summary.blocked} blocked`,
1452
+ `Found: ${foundLine}`,
1453
+ `Auto-limited: ${autoLimitedLine || "None."}`,
1454
+ ...(report.scopeRepair ? [`Scope repair: ${report.scopeRepair.repairsApplied || 0} applied, ${report.scopeRepair.approvalsAvoided || 0} approvals avoided, ${report.scopeRepair.approvalsStillRequired || 0} still require review`] : []),
1455
+ `Needs attention: ${needsAttentionLine}`,
1456
+ `Safe next action: ${report.summary.safeNextAction}`,
1457
+ ].join("\n");
1458
+ }
1459
+
1460
+ function formatGovernedActionExposureStatusLineFromReport(report) {
1461
+ const reviewed = (report.summary.actionsReviewed || 0) + (report.summary.exposuresReviewed || 0) + (report.summary.toolsReviewed || 0);
1462
+ const scopeRepair = report.scopeRepair
1463
+ ? ` · scope repair ${report.scopeRepair.repairsApplied || 0}/${report.scopeRepair.approvalsAvoided || 0}`
1464
+ : "";
1465
+ return `Boundary: ${String(report.status || "warn").toUpperCase()} · ${reviewed} patterns reviewed · ${report.summary.approvalRequired || 0} approval required · ${report.summary.autoLimited || 0} auto-limited${scopeRepair} · next: ${report.summary.safeNextAction}`;
1466
+ }
1467
+
1468
+ function buildGovernedActionExposureSurface(cwd = process.cwd(), options = {}) {
1469
+ const report = readLatestGovernedActionExposureReport(cwd)
1470
+ || (options.buildIfMissing === true ? buildGovernedActionExposureReport(cwd, options) : null);
1471
+ if (!report) {
1472
+ return {
1473
+ status: "not_run",
1474
+ showInStatus: true,
1475
+ showInDashboard: true,
1476
+ reviewedPatterns: 0,
1477
+ approvalRequired: 0,
1478
+ autoLimited: 0,
1479
+ blocked: 0,
1480
+ nextAction: "Run `avorelo boundary --text` to generate the first governed boundary report.",
1481
+ latestReportPath: null,
1482
+ statusLine: "Boundary: NOT RUN · run `avorelo boundary --text`",
1483
+ };
1484
+ }
1485
+
1486
+ const reviewedPatterns = (report.summary.actionsReviewed || 0) + (report.summary.exposuresReviewed || 0) + (report.summary.toolsReviewed || 0);
1487
+ return {
1488
+ status: report.status,
1489
+ showInStatus: true,
1490
+ showInDashboard: true,
1491
+ reviewedPatterns,
1492
+ approvalRequired: report.summary.approvalRequired || 0,
1493
+ autoLimited: report.summary.autoLimited || 0,
1494
+ blocked: report.summary.blocked || 0,
1495
+ scopeRepair: report.scopeRepair || null,
1496
+ nextAction: report.summary.safeNextAction,
1497
+ latestReportPath: LATEST_REPORT_REL,
1498
+ statusLine: formatGovernedActionExposureStatusLineFromReport(report),
1499
+ };
1500
+ }
1501
+
1502
+ module.exports = {
1503
+ CONTRACT,
1504
+ SCHEMA_VERSION,
1505
+ LATEST_REPORT_REL,
1506
+ DOGFOOD_COMMANDS,
1507
+ buildGovernedActionExposureReport,
1508
+ writeGovernedActionExposureReport,
1509
+ generateGovernedActionExposureReport,
1510
+ readLatestGovernedActionExposureReport,
1511
+ buildGovernedActionExposureSurface,
1512
+ formatGovernedActionExposureText,
1513
+ formatGovernedActionExposureStatusLineFromReport,
1514
+ };