avorelo 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +56 -0
  3. package/bin/avorelo +9 -0
  4. package/package.json +135 -0
  5. package/scripts/README.md +40 -0
  6. package/scripts/cco-dashboard.js +252 -0
  7. package/scripts/cco-status.js +430 -0
  8. package/scripts/lib/activation/account-state.js +37 -0
  9. package/scripts/lib/activation/activation-runner.js +546 -0
  10. package/scripts/lib/activation/activation-self-healing.js +480 -0
  11. package/scripts/lib/activation/activation-state.js +83 -0
  12. package/scripts/lib/activation/activation-summary.js +191 -0
  13. package/scripts/lib/activation/adapters/claude-code.js +77 -0
  14. package/scripts/lib/activation/adapters/codex-cli.js +52 -0
  15. package/scripts/lib/activation/adapters/cursor.js +37 -0
  16. package/scripts/lib/activation/adapters/github-agent.js +39 -0
  17. package/scripts/lib/activation/adapters/terminal.js +42 -0
  18. package/scripts/lib/activation/adapters/vscode.js +39 -0
  19. package/scripts/lib/activation/adapters/windsurf.js +37 -0
  20. package/scripts/lib/activation/ai-surface-detector.js +151 -0
  21. package/scripts/lib/activation/connect-account.js +145 -0
  22. package/scripts/lib/activation/detect-environment.js +75 -0
  23. package/scripts/lib/activation/detect-hosts.js +62 -0
  24. package/scripts/lib/activation/format-activation-output.js +109 -0
  25. package/scripts/lib/activation/next-action.js +43 -0
  26. package/scripts/lib/activation/repair-engine.js +219 -0
  27. package/scripts/lib/activation-distribution-readiness.js +507 -0
  28. package/scripts/lib/adapter-conformance.js +176 -0
  29. package/scripts/lib/adapter-readiness.js +417 -0
  30. package/scripts/lib/adapter-safety-boundaries.js +335 -0
  31. package/scripts/lib/adapter-technical-readiness-gate.js +205 -0
  32. package/scripts/lib/agent-access-governance.js +455 -0
  33. package/scripts/lib/agent-enforcement.js +765 -0
  34. package/scripts/lib/agent-policy-profile.js +210 -0
  35. package/scripts/lib/agent-security/action-evaluator.js +507 -0
  36. package/scripts/lib/agent-security/adapter-registry.js +98 -0
  37. package/scripts/lib/agent-security/auto-policy.js +139 -0
  38. package/scripts/lib/agent-security/bounded-scan.js +93 -0
  39. package/scripts/lib/agent-security/enforcement-adapter.js +174 -0
  40. package/scripts/lib/agent-security/enforcement-engine.js +1129 -0
  41. package/scripts/lib/agent-security/file-write-adapter.js +183 -0
  42. package/scripts/lib/agent-security/file-write-rules.js +178 -0
  43. package/scripts/lib/agent-security/index.js +3342 -0
  44. package/scripts/lib/agent-security/instruction-risk.js +181 -0
  45. package/scripts/lib/agent-security/mcp-action-adapter.js +185 -0
  46. package/scripts/lib/agent-security/mcp-action-rules.js +184 -0
  47. package/scripts/lib/agent-security/package-action-adapter.js +175 -0
  48. package/scripts/lib/agent-security/package-action-rules.js +233 -0
  49. package/scripts/lib/agent-security/performance.js +148 -0
  50. package/scripts/lib/agent-security/permission-minimizer.js +403 -0
  51. package/scripts/lib/agent-security/scan-cache.js +74 -0
  52. package/scripts/lib/agent-security/source-trust.js +146 -0
  53. package/scripts/lib/ai-install-prompt.js +288 -0
  54. package/scripts/lib/ai-workspace-hygiene.js +1499 -0
  55. package/scripts/lib/alpha-activation.js +520 -0
  56. package/scripts/lib/alpha-feedback.js +263 -0
  57. package/scripts/lib/alpha-readiness-gate.js +332 -0
  58. package/scripts/lib/anti-gaming.js +169 -0
  59. package/scripts/lib/artifact-health.js +431 -0
  60. package/scripts/lib/attribution.js +180 -0
  61. package/scripts/lib/audit.js +289 -0
  62. package/scripts/lib/avorelo-skill-registry.js +810 -0
  63. package/scripts/lib/batch-jobs.js +71 -0
  64. package/scripts/lib/brain-pack.js +578 -0
  65. package/scripts/lib/brand-boundary.js +424 -0
  66. package/scripts/lib/brand.js +74 -0
  67. package/scripts/lib/browser-capability.js +1048 -0
  68. package/scripts/lib/browser-proof-preflight.js +321 -0
  69. package/scripts/lib/cache-readiness.js +187 -0
  70. package/scripts/lib/canonical-reentry.js +162 -0
  71. package/scripts/lib/capability-packs.js +314 -0
  72. package/scripts/lib/capability-recommender.js +512 -0
  73. package/scripts/lib/capability-registry.js +1059 -0
  74. package/scripts/lib/carry-forward-surfacing.js +194 -0
  75. package/scripts/lib/ccusage-adapter.js +188 -0
  76. package/scripts/lib/company-loop.js +1149 -0
  77. package/scripts/lib/config.js +637 -0
  78. package/scripts/lib/context-acquisition-plan.js +287 -0
  79. package/scripts/lib/context-budget-guard.js +170 -0
  80. package/scripts/lib/context-budget-scanner.js +257 -0
  81. package/scripts/lib/context-optimizer.js +715 -0
  82. package/scripts/lib/context-reduction-plan.js +178 -0
  83. package/scripts/lib/context-safety.js +88 -0
  84. package/scripts/lib/context-savings-engine.js +158 -0
  85. package/scripts/lib/cost-evidence.js +254 -0
  86. package/scripts/lib/cross-host-install-plan.js +308 -0
  87. package/scripts/lib/cross-host-install-readiness.js +237 -0
  88. package/scripts/lib/cross-host-value-flow.js +268 -0
  89. package/scripts/lib/dashboard.js +900 -0
  90. package/scripts/lib/design-partner-feedback.js +346 -0
  91. package/scripts/lib/entitlements.js +100 -0
  92. package/scripts/lib/execution-packet.js +559 -0
  93. package/scripts/lib/experimentation-events.js +547 -0
  94. package/scripts/lib/external-capability-compliance.js +107 -0
  95. package/scripts/lib/external-user-simulation.js +166 -0
  96. package/scripts/lib/failure-recovery-readiness.js +81 -0
  97. package/scripts/lib/failure-recovery.js +419 -0
  98. package/scripts/lib/feedback-intelligence.js +537 -0
  99. package/scripts/lib/feedback-signals.js +205 -0
  100. package/scripts/lib/file-integrity.js +68 -0
  101. package/scripts/lib/fsx.js +127 -0
  102. package/scripts/lib/full-readiness-gate.js +451 -0
  103. package/scripts/lib/guidance-builder.js +174 -0
  104. package/scripts/lib/hook-apply.js +1019 -0
  105. package/scripts/lib/hook-baseline.js +310 -0
  106. package/scripts/lib/hook-config-preview.js +275 -0
  107. package/scripts/lib/hook-contracts.js +290 -0
  108. package/scripts/lib/hook-safety-boundary-readiness.js +80 -0
  109. package/scripts/lib/host-capability-matrix.js +351 -0
  110. package/scripts/lib/host-support-context.js +254 -0
  111. package/scripts/lib/http-hook-action.js +538 -0
  112. package/scripts/lib/install-ai-readiness.js +84 -0
  113. package/scripts/lib/install-intake-risk.js +1037 -0
  114. package/scripts/lib/install-journey-intelligence.js +329 -0
  115. package/scripts/lib/intervention-guidance.js +57 -0
  116. package/scripts/lib/known-limitations.js +115 -0
  117. package/scripts/lib/l8-path-truth.js +146 -0
  118. package/scripts/lib/launch-hardening-gate.js +436 -0
  119. package/scripts/lib/launch-readiness.js +628 -0
  120. package/scripts/lib/learning-memory.js +686 -0
  121. package/scripts/lib/lifecycle-hooks.js +802 -0
  122. package/scripts/lib/local-package-smoke.js +423 -0
  123. package/scripts/lib/local-pricing.js +299 -0
  124. package/scripts/lib/mcp-enforcement.js +311 -0
  125. package/scripts/lib/mcp-least-privilege-policy.js +303 -0
  126. package/scripts/lib/mcp-tool-inventory.js +388 -0
  127. package/scripts/lib/mcp-tool-risk.js +0 -0
  128. package/scripts/lib/memory.js +335 -0
  129. package/scripts/lib/metrics.js +699 -0
  130. package/scripts/lib/micro-proof.js +133 -0
  131. package/scripts/lib/next-run-context.js +436 -0
  132. package/scripts/lib/operating-value.js +1648 -0
  133. package/scripts/lib/optimization-v3.js +122 -0
  134. package/scripts/lib/orchestration/adapters/_shared.js +49 -0
  135. package/scripts/lib/orchestration/adapters/aider.js +18 -0
  136. package/scripts/lib/orchestration/adapters/claude-code.js +35 -0
  137. package/scripts/lib/orchestration/adapters/codex.js +35 -0
  138. package/scripts/lib/orchestration/adapters/gemini-cli.js +18 -0
  139. package/scripts/lib/orchestration/adapters/git.js +25 -0
  140. package/scripts/lib/orchestration/adapters/index.js +31 -0
  141. package/scripts/lib/orchestration/adapters/lm-studio.js +18 -0
  142. package/scripts/lib/orchestration/adapters/ollama.js +18 -0
  143. package/scripts/lib/orchestration/adapters/opencode.js +18 -0
  144. package/scripts/lib/orchestration/adapters/openrouter.js +18 -0
  145. package/scripts/lib/orchestration/adapters/test-runner.js +25 -0
  146. package/scripts/lib/orchestration/cli.js +438 -0
  147. package/scripts/lib/orchestration/execution-manager.js +279 -0
  148. package/scripts/lib/orchestration/handoff.js +314 -0
  149. package/scripts/lib/orchestration/index.js +456 -0
  150. package/scripts/lib/orchestration/inventory.js +47 -0
  151. package/scripts/lib/orchestration/model-discovery.js +498 -0
  152. package/scripts/lib/orchestration/model-profiler.js +170 -0
  153. package/scripts/lib/orchestration/model-profiles.js +252 -0
  154. package/scripts/lib/orchestration/model-refresh-policy.js +72 -0
  155. package/scripts/lib/orchestration/proof-writer.js +349 -0
  156. package/scripts/lib/orchestration/provider-discovery/aider.js +49 -0
  157. package/scripts/lib/orchestration/provider-discovery/claude-code.js +56 -0
  158. package/scripts/lib/orchestration/provider-discovery/codex.js +49 -0
  159. package/scripts/lib/orchestration/provider-discovery/common.js +186 -0
  160. package/scripts/lib/orchestration/provider-discovery/gemini.js +106 -0
  161. package/scripts/lib/orchestration/provider-discovery/lm-studio.js +118 -0
  162. package/scripts/lib/orchestration/provider-discovery/models-dev.js +12 -0
  163. package/scripts/lib/orchestration/provider-discovery/ollama.js +100 -0
  164. package/scripts/lib/orchestration/provider-discovery/opencode.js +47 -0
  165. package/scripts/lib/orchestration/provider-discovery/openrouter.js +44 -0
  166. package/scripts/lib/orchestration/risk-classifier.js +130 -0
  167. package/scripts/lib/orchestration/routing-policy.js +486 -0
  168. package/scripts/lib/orchestration/settings.js +112 -0
  169. package/scripts/lib/orchestration/state.js +165 -0
  170. package/scripts/lib/orchestration/verification-manager.js +138 -0
  171. package/scripts/lib/output-profiles.js +146 -0
  172. package/scripts/lib/package-content-audit.js +368 -0
  173. package/scripts/lib/package-runtime.js +278 -0
  174. package/scripts/lib/plan-surface.js +53 -0
  175. package/scripts/lib/plans.js +2318 -0
  176. package/scripts/lib/policy-provider.js +27 -0
  177. package/scripts/lib/prelaunch-activation-readiness.js +409 -0
  178. package/scripts/lib/prelaunch-evidence-store.js +816 -0
  179. package/scripts/lib/prelaunch-intelligence.js +869 -0
  180. package/scripts/lib/pricing-experiment.js +118 -0
  181. package/scripts/lib/pro-moment-events.js +77 -0
  182. package/scripts/lib/pro-moment-state.js +227 -0
  183. package/scripts/lib/pro-moments.js +1216 -0
  184. package/scripts/lib/product-learning-events.js +629 -0
  185. package/scripts/lib/project-profile.js +555 -0
  186. package/scripts/lib/prompt-compiler.js +280 -0
  187. package/scripts/lib/prompt-lint.js +32 -0
  188. package/scripts/lib/prompt-suggestions.js +52 -0
  189. package/scripts/lib/proof-canonical.js +398 -0
  190. package/scripts/lib/proof-drilldown.js +383 -0
  191. package/scripts/lib/proof-events.js +342 -0
  192. package/scripts/lib/proof-history.js +243 -0
  193. package/scripts/lib/proof-metrics.js +296 -0
  194. package/scripts/lib/proof-outcome-evidence.js +134 -0
  195. package/scripts/lib/proof-receipt.js +335 -0
  196. package/scripts/lib/proof-record.js +461 -0
  197. package/scripts/lib/public-activation-distribution-gate.js +258 -0
  198. package/scripts/lib/public-cli.js +3891 -0
  199. package/scripts/lib/public-distribution-truth.js +211 -0
  200. package/scripts/lib/public-install-claim-checker.js +294 -0
  201. package/scripts/lib/publish-provenance-readiness.js +283 -0
  202. package/scripts/lib/readiness-delta.js +218 -0
  203. package/scripts/lib/readiness-evidence-closure.js +196 -0
  204. package/scripts/lib/reentry-memory-capture.js +241 -0
  205. package/scripts/lib/reentry-memory-retrieval.js +302 -0
  206. package/scripts/lib/reentry-memory-status.js +146 -0
  207. package/scripts/lib/reentry-memory-store.js +178 -0
  208. package/scripts/lib/reentry-state.js +66 -0
  209. package/scripts/lib/release-candidate-bundle.js +166 -0
  210. package/scripts/lib/remediation.js +81 -0
  211. package/scripts/lib/repo-map.js +391 -0
  212. package/scripts/lib/run-improvements-lifecycle.js +330 -0
  213. package/scripts/lib/run-improvements.js +789 -0
  214. package/scripts/lib/runtime-decision-policy.js +387 -0
  215. package/scripts/lib/safe-path-engine.js +705 -0
  216. package/scripts/lib/safe-run-controller.js +887 -0
  217. package/scripts/lib/score.js +262 -0
  218. package/scripts/lib/seamless-enforcement.js +329 -0
  219. package/scripts/lib/seamless-outcome.js +689 -0
  220. package/scripts/lib/seamless-reality-gate.js +5043 -0
  221. package/scripts/lib/security-risk-classifier.js +511 -0
  222. package/scripts/lib/security-scan.js +384 -0
  223. package/scripts/lib/session-context-optimizer.js +1211 -0
  224. package/scripts/lib/session-timing.js +315 -0
  225. package/scripts/lib/skill-hygiene.js +805 -0
  226. package/scripts/lib/skill-packs.js +161 -0
  227. package/scripts/lib/skills-operating-layer.js +580 -0
  228. package/scripts/lib/smart-work-routing.js +768 -0
  229. package/scripts/lib/source-catalog.js +700 -0
  230. package/scripts/lib/status-value-summary.js +32 -0
  231. package/scripts/lib/support-bundle.js +578 -0
  232. package/scripts/lib/task-continuation.js +440 -0
  233. package/scripts/lib/test-helpers.js +15 -0
  234. package/scripts/lib/tier.js +38 -0
  235. package/scripts/lib/token-context-quality-gate.js +370 -0
  236. package/scripts/lib/token-cost-capture.js +187 -0
  237. package/scripts/lib/token-cost-intelligence.js +358 -0
  238. package/scripts/lib/token-efficiency-evidence.js +213 -0
  239. package/scripts/lib/token-evidence.js +699 -0
  240. package/scripts/lib/tokenish.js +17 -0
  241. package/scripts/lib/tool-output-sandbox.js +304 -0
  242. package/scripts/lib/trust-audit.js +136 -0
  243. package/scripts/lib/unified-events.js +396 -0
  244. package/scripts/lib/upgrade-interruption-recovery.js +407 -0
  245. package/scripts/lib/usage-ledger.js +201 -0
  246. package/scripts/lib/value-ledger.js +130 -0
  247. package/scripts/lib/value-proof-calibration.js +531 -0
  248. package/scripts/lib/visual-qa.js +231 -0
  249. package/scripts/lib/voice-alpha.js +29 -0
  250. package/scripts/lib/work-aware-orchestration.js +976 -0
  251. package/scripts/lib/work-control-receipts.js +577 -0
  252. package/scripts/lib/work-ledger.js +1123 -0
  253. package/scripts/lib/work-panel-preview.js +352 -0
  254. package/scripts/lib/workflow-discipline.js +280 -0
  255. package/scripts/lib/workflow-signals.js +419 -0
  256. package/scripts/lib/workspace-map.js +281 -0
  257. package/scripts/lib/workspace-registry.js +1367 -0
  258. package/scripts/lib/workspace-resolver.js +480 -0
@@ -0,0 +1,262 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * lib/score.js — Shared verification-gap score logic.
5
+ *
6
+ * Used by:
7
+ * scripts/cco-score.js (CLI)
8
+ * scripts/cco-sessionend.js (SessionEnd hook)
9
+ *
10
+ * Exposes pure functions only. No process.exit, no console output.
11
+ */
12
+
13
+ const fs = require("fs");
14
+ const path = require("path");
15
+
16
+ const METRICS_REL = ".claude/cco/metrics/metrics.jsonl";
17
+ const RELEVANT_EVENTS = new Set(["file.change", "validation.start", "validation.result"]);
18
+
19
+ // ── Metrics reader ───────────────────────────────────────────────────────────
20
+
21
+ /**
22
+ * Read metrics.jsonl safely.
23
+ * @param {string} metricsPath Absolute path to metrics.jsonl
24
+ * @returns {{ events: object[], warnings: string[], missing: boolean }}
25
+ */
26
+ function readMetrics(metricsPath) {
27
+ if (!fs.existsSync(metricsPath)) {
28
+ return { events: [], warnings: [], missing: true };
29
+ }
30
+ let raw;
31
+ try {
32
+ raw = fs.readFileSync(metricsPath, "utf8");
33
+ } catch {
34
+ return { events: [], warnings: ["Could not read metrics.jsonl."], missing: false };
35
+ }
36
+ const lines = raw.split("\n").filter((l) => l.trim().length > 0);
37
+ const events = [];
38
+ const warnings = [];
39
+ let corruptSeen = false;
40
+ for (const line of lines) {
41
+ try {
42
+ const obj = JSON.parse(line);
43
+ if (obj && typeof obj === "object") events.push(obj);
44
+ } catch {
45
+ corruptSeen = true;
46
+ }
47
+ }
48
+ if (corruptSeen) {
49
+ warnings.push("Some lines in metrics.jsonl could not be parsed and were skipped.");
50
+ }
51
+ return { events, warnings, missing: false };
52
+ }
53
+
54
+ // ── Run ID selection ─────────────────────────────────────────────────────────
55
+
56
+ /**
57
+ * Select the target runId.
58
+ * Preferred order: explicit arg > latest file.change event's runId > null.
59
+ *
60
+ * @param {object[]} events Already-filtered RELEVANT_EVENTS subset
61
+ * @param {string|null} explicitRunId
62
+ * @returns {string|null}
63
+ */
64
+ function selectTargetRunId(events, explicitRunId) {
65
+ if (explicitRunId) return explicitRunId;
66
+ const fileChanges = events.filter((e) => e.event === "file.change");
67
+ if (fileChanges.length === 0) return null;
68
+ fileChanges.sort((a, b) => {
69
+ const ta = a.ts ? new Date(a.ts).getTime() : 0;
70
+ const tb = b.ts ? new Date(b.ts).getTime() : 0;
71
+ return tb - ta; // descending — latest first
72
+ });
73
+ const latest = fileChanges[0];
74
+ const id = latest.meta?.runId ?? latest.sessionId ?? null;
75
+ return id != null ? String(id) : null;
76
+ }
77
+
78
+ // ── Core analysis ────────────────────────────────────────────────────────────
79
+
80
+ /**
81
+ * Analyze a single run. Always filters events to targetRunId.
82
+ *
83
+ * @param {string} targetRunId
84
+ * @param {object[]} events Already-filtered RELEVANT_EVENTS subset
85
+ * @returns {object|null} Score result, or null if targetRunId is falsy
86
+ */
87
+ function analyzeRun(targetRunId, events) {
88
+ if (!targetRunId) return null;
89
+
90
+ // Strict filter — never mix runs
91
+ const filtered = events.filter(
92
+ (e) => String(e.meta?.runId ?? e.sessionId ?? "") === targetRunId
93
+ );
94
+
95
+ const fileChanges = filtered.filter((e) => e.event === "file.change");
96
+ const validationStarts = filtered.filter((e) => e.event === "validation.start");
97
+ const validationResults = filtered.filter((e) => e.event === "validation.result");
98
+
99
+ const sessionIds = [...new Set(filtered.map((e) => e.sessionId).filter(Boolean))];
100
+ const fileChangePaths = [
101
+ ...new Set(fileChanges.map((f) => String(f.meta?.filePath ?? f.meta?.path ?? "unknown"))),
102
+ ];
103
+
104
+ const hadFileChange = fileChanges.length > 0;
105
+ const hadValidationStart = validationStarts.length > 0;
106
+ const hadValidationResult = validationResults.length > 0;
107
+ const verificationGap = hadFileChange && !hadValidationResult;
108
+
109
+ let status, reason, nextAction;
110
+
111
+ if (!hadFileChange) {
112
+ status = "NO DATA";
113
+ reason = "No file changes recorded for this run.";
114
+ nextAction = "Make a code change and re-run wuz score.";
115
+ } else if (hadValidationResult) {
116
+ status = "PASS";
117
+ const outcome =
118
+ validationResults[0]?.meta?.verdict ??
119
+ validationResults[0]?.meta?.policyOutcome ??
120
+ "unknown";
121
+ reason = `Validation completed with outcome: ${outcome}.`;
122
+ nextAction = "No action needed — verification is closed.";
123
+ } else if (hadValidationStart) {
124
+ status = "INSUFFICIENT";
125
+ reason = "Validation started but no result recorded yet.";
126
+ nextAction = "Wait for validation to complete, then re-run wuz score.";
127
+ } else {
128
+ status = "GAP";
129
+ reason = "File changes were made but no validation was started or completed.";
130
+ nextAction =
131
+ "Run validation (e.g. npm test or your check command) before completing this task.";
132
+ }
133
+
134
+ return {
135
+ status,
136
+ runId: targetRunId,
137
+ sessionIds,
138
+ hadFileChange,
139
+ hadValidationStart,
140
+ hadValidationResult,
141
+ verificationGap,
142
+ fileChangeCount: fileChanges.length,
143
+ fileChangePaths,
144
+ validationStartCount: validationStarts.length,
145
+ validationResultCount: validationResults.length,
146
+ reason,
147
+ nextAction,
148
+ };
149
+ }
150
+
151
+ // ── No-data result ───────────────────────────────────────────────────────────
152
+
153
+ /**
154
+ * Standardized NO DATA result shape.
155
+ * @param {string} reason
156
+ * @returns {object}
157
+ */
158
+ function noDataResult(reason) {
159
+ return {
160
+ status: "NO DATA",
161
+ runId: null,
162
+ sessionIds: [],
163
+ hadFileChange: false,
164
+ hadValidationStart: false,
165
+ hadValidationResult: false,
166
+ verificationGap: false,
167
+ fileChangeCount: 0,
168
+ fileChangePaths: [],
169
+ validationStartCount: 0,
170
+ validationResultCount: 0,
171
+ reason,
172
+ nextAction: "Make a code change and run validation, then re-run wuz score.",
173
+ };
174
+ }
175
+
176
+ // ── Text formatter ───────────────────────────────────────────────────────────
177
+
178
+ /**
179
+ * Compact human-readable score line.
180
+ * @param {object} result
181
+ * @returns {string}
182
+ */
183
+ function formatScoreText(result) {
184
+ const lines = [`Wuz score: ${result.status}`];
185
+ if (result.runId) lines.push(`Run: ${result.runId}`);
186
+ lines.push(`Reason: ${result.reason}`);
187
+ lines.push(`Next: ${result.nextAction}`);
188
+ return lines.join("\n");
189
+ }
190
+
191
+ // ── Compact single-line for hook output ──────────────────────────────────────
192
+
193
+ /**
194
+ * One-liner for session-end hook stderr — avoids multi-line clutter.
195
+ * Returns null for NO DATA (stay quiet).
196
+ * @param {object} result
197
+ * @returns {string|null}
198
+ */
199
+ function formatScoreOneLiner(result) {
200
+ switch (result.status) {
201
+ case "GAP":
202
+ return `Wuz score: GAP — file changes detected, no validation.result completed. Next: ${result.nextAction}`;
203
+ case "PASS":
204
+ return `Wuz score: PASS — validation evidence exists for this run.`;
205
+ case "INSUFFICIENT":
206
+ return `Wuz score: INSUFFICIENT — validation started but did not complete. Next: ${result.nextAction}`;
207
+ case "NO DATA":
208
+ return null; // stay quiet
209
+ default:
210
+ return null;
211
+ }
212
+ }
213
+
214
+ // ── Orchestration ────────────────────────────────────────────────────────────
215
+
216
+ /**
217
+ * Full score pipeline: read metrics, select run, analyze.
218
+ *
219
+ * @param {string} metricsPath Absolute path to metrics.jsonl
220
+ * @param {string|null} explicitRunId
221
+ * @returns {{ result: object, warnings: string[] }}
222
+ */
223
+ function runScore(metricsPath, explicitRunId) {
224
+ const { events, warnings, missing } = readMetrics(metricsPath);
225
+
226
+ if (missing) {
227
+ return {
228
+ result: noDataResult(
229
+ `Metrics file not found at ${path.basename(metricsPath)}. Has the CCO harness run yet?`
230
+ ),
231
+ warnings,
232
+ };
233
+ }
234
+
235
+ const relevantEvents = events.filter((e) => RELEVANT_EVENTS.has(e.event));
236
+ const targetRunId = selectTargetRunId(relevantEvents, explicitRunId);
237
+
238
+ if (!targetRunId) {
239
+ return {
240
+ result: noDataResult("No file.change events found in metrics. No run to score."),
241
+ warnings,
242
+ };
243
+ }
244
+
245
+ const result =
246
+ analyzeRun(targetRunId, relevantEvents) ||
247
+ noDataResult(`Could not analyze run: ${targetRunId}`);
248
+
249
+ return { result, warnings };
250
+ }
251
+
252
+ module.exports = {
253
+ METRICS_REL,
254
+ RELEVANT_EVENTS,
255
+ readMetrics,
256
+ selectTargetRunId,
257
+ analyzeRun,
258
+ noDataResult,
259
+ formatScoreText,
260
+ formatScoreOneLiner,
261
+ runScore,
262
+ };
@@ -0,0 +1,329 @@
1
+ "use strict";
2
+
3
+ // ── Seamless Automation Enforcement ──────────────────────────────────────────
4
+ //
5
+ // Wires shouldInterruptForDecision() and shouldApplySilently() into actual
6
+ // CLI command output and post-run value summary writes. This is not a new
7
+ // orchestrator — it's an enforcement layer over existing foundations.
8
+ //
9
+ // Principles:
10
+ // - Automatic mode: work silently when safe; show compact outcome at end
11
+ // - Assisted/manual_debug: expose decision detail when appropriate
12
+ // - Every block must include taskStillExecutable + safeNextAction
13
+ // - Every run writes a value summary (best-effort, never blocks the command)
14
+ // - No LLM calls, no network calls, fully deterministic
15
+
16
+ const {
17
+ getSeamlessMode,
18
+ shouldInterruptForDecision,
19
+ shouldApplySilently,
20
+ buildOutcomePreservationCheck,
21
+ buildValueSummaryFromReceipts,
22
+ emitSeamlessEvent,
23
+ } = require("./seamless-outcome");
24
+ const { buildTaskContinuation } = require("./task-continuation");
25
+
26
+ const CONTRACT = "avorelo.seamlessEnforcement.v1";
27
+ const SCHEMA_VERSION = 1;
28
+
29
+ // ── Decision visibility ───────────────────────────────────────────────────────
30
+
31
+ // Returns whether full decision detail should be shown vs. compact outcome line.
32
+ // Used by guard and route text output to enforce seamless mode behavior.
33
+ function getDecisionVisibility(input = {}) {
34
+ const mode = getSeamlessMode({});
35
+ const interruptResult = shouldInterruptForDecision({
36
+ actionType: input.actionType,
37
+ riskLevel: input.riskLevel,
38
+ isDestructive: input.isDestructive,
39
+ isIrreversible: input.isIrreversible,
40
+ hasSecretsRisk: input.hasSecretsRisk,
41
+ requiresExplicitApproval: input.requiresExplicitApproval,
42
+ });
43
+
44
+ // Always show full detail when user action is required
45
+ if (interruptResult.interrupt) {
46
+ return {
47
+ showDetail: true,
48
+ interruptRequired: true,
49
+ interruptReason: interruptResult.reason,
50
+ mode,
51
+ };
52
+ }
53
+
54
+ // manual_debug always shows detail
55
+ if (mode === "manual_debug") {
56
+ return {
57
+ showDetail: true,
58
+ interruptRequired: false,
59
+ interruptReason: "manual_debug_exposes_all_decisions",
60
+ mode,
61
+ };
62
+ }
63
+
64
+ const silentResult = shouldApplySilently({
65
+ actionType: input.actionType,
66
+ riskLevel: input.riskLevel,
67
+ isDestructive: input.isDestructive,
68
+ isIrreversible: input.isIrreversible,
69
+ hasSecretsRisk: input.hasSecretsRisk,
70
+ });
71
+
72
+ if (silentResult.silent) {
73
+ return {
74
+ showDetail: false,
75
+ interruptRequired: false,
76
+ interruptReason: null,
77
+ mode,
78
+ compactSuffix: "Applied silently in automatic mode. Run with --debug for details.",
79
+ };
80
+ }
81
+
82
+ // Assisted or high-risk: show detail without requiring interrupt
83
+ return {
84
+ showDetail: true,
85
+ interruptRequired: false,
86
+ interruptReason: silentResult.reason,
87
+ mode,
88
+ };
89
+ }
90
+
91
+ // ── Compact outcome lines ─────────────────────────────────────────────────────
92
+
93
+ // Returns a compact, customer-outcome-oriented summary line for automatic mode.
94
+ function formatCompactRouteLine(route) {
95
+ const worker = route.chosenTool || "none";
96
+ const profile = route.chosenModelProfile || "unknown";
97
+ const risk = route.task?.risk || "unknown";
98
+ const premium = route.premiumUseInvolved ? "premium" : "local/free";
99
+ const state = route.state || "partial";
100
+ return [
101
+ `Routed: ${worker} / ${profile} (${premium} · risk: ${risk} · state: ${state}).`,
102
+ `Receipt written. Run \`avorelo proof --debug\` for routing details.`,
103
+ ].join(" ");
104
+ }
105
+
106
+ function formatCompactContextLine(pack) {
107
+ const tokens = pack.tokenEstimate?.estimatedTokens ?? 0;
108
+ const stage = pack.stageId || "implement";
109
+ const warning = pack.tokenEstimate?.warning || "within_budget";
110
+ const compacted = pack.reductionSummary?.compactionUsed ? " (compacted)" : "";
111
+ return `Context: ~${tokens} tokens / ${stage} stage / ${warning}${compacted}. Receipt written.`;
112
+ }
113
+
114
+ function formatCompactGuardLine(decision, riskLevel) {
115
+ if (decision === "allow") {
116
+ return `Guard: allow. Safe routine — applied silently. Risk: ${riskLevel || "low"}.`;
117
+ }
118
+ if (decision === "approval_required") {
119
+ return `Guard: approval required. Task can proceed with narrower scope — see saferAlternative.`;
120
+ }
121
+ return `Guard: ${decision}. Risk: ${riskLevel || "unknown"}. See saferAlternative for next step.`;
122
+ }
123
+
124
+ // ── Outcome-preserving guard block check ──────────────────────────────────────
125
+
126
+ // Every guard decision must include taskStillExecutable and safeNextAction.
127
+ // This enforces the "reduced access must still allow the task to finish,
128
+ // or provide fallback/escalation" principle.
129
+ // Handles block, approval_required, and warn decisions.
130
+ function buildGuardBlockOutcome(decision, guardInput = {}) {
131
+ if (!["block", "approval_required", "warn"].includes(decision)) return null;
132
+
133
+ const riskLevel = guardInput.riskLevel || "unknown";
134
+ const isHighRisk = ["high", "critical"].includes(String(riskLevel).toLowerCase());
135
+
136
+ const continuation = buildTaskContinuation({
137
+ actionType: guardInput.actionType,
138
+ target: guardInput.target || guardInput.command || guardInput.url || guardInput.toolName || "",
139
+ task: guardInput.userIntent || guardInput.taskType || "",
140
+ riskLevel,
141
+ decision,
142
+ reasonCodes: guardInput.reasonCodes || [],
143
+ });
144
+
145
+ const taskStillExecutable = continuation.taskStillExecutable;
146
+ const fallbackAvailable = continuation.fallbackAvailable;
147
+ const escalationRecommended = continuation.escalationRecommended;
148
+ const derivedSafeNextAction = continuation.safeNextAction;
149
+
150
+ return buildOutcomePreservationCheck({
151
+ originalAction: guardInput.actionType,
152
+ reductionApplied: decision === "approval_required" || decision === "warn",
153
+ riskLevel,
154
+ actionType: guardInput.actionType,
155
+ target: guardInput.target,
156
+ taskDescription: guardInput.userIntent || guardInput.taskType || "",
157
+ taskStillExecutable,
158
+ qualityRiskIntroduced: continuation.qualityRiskIntroduced,
159
+ fallbackAvailable,
160
+ escalationRecommended,
161
+ safeNextAction: derivedSafeNextAction,
162
+ interruptRequired: decision === "block",
163
+ reasonCodes: guardInput.reasonCodes || [],
164
+ continuationType: continuation.continuationType,
165
+ suggestedScope: continuation.suggestedScope,
166
+ suggestedCommand: continuation.suggestedCommand,
167
+ verificationNextStep: continuation.verificationNextStep,
168
+ });
169
+ }
170
+
171
+ // ── Auto-write value summary ──────────────────────────────────────────────────
172
+
173
+ // Called after any automated run to write a value summary and emit events.
174
+ // Best-effort: never throws, never blocks the calling command.
175
+ function writeSeamlessRunReceipt(cwd, opts = {}) {
176
+ try {
177
+ const mode = getSeamlessMode({});
178
+ const { summary, path: summaryPath } = buildValueSummaryFromReceipts(cwd, {
179
+ mode,
180
+ manualDecisionsAvoided: opts.manualDecisionsAvoided || 0,
181
+ safePathReductionsApplied: opts.safePathReductionsApplied || 0,
182
+ unsafeActionsBlocked: opts.unsafeActionsBlocked || 0,
183
+ secretsExposurePrevented: opts.secretsExposurePrevented || 0,
184
+ qualityPreserved: opts.qualityPreserved !== false,
185
+ qualityRiskIntroduced: Boolean(opts.qualityRiskIntroduced),
186
+ escalationRecommended: Boolean(opts.escalationRecommended),
187
+ strongWorkerReservedForCriticalStage: Boolean(opts.strongWorkerReservedForCriticalStage),
188
+ });
189
+
190
+ emitSeamlessEvent(cwd, "automatic_optimization_applied", {
191
+ triggerCommand: opts.command || "unknown",
192
+ mode,
193
+ manualDecisionsAvoided: opts.manualDecisionsAvoided || 0,
194
+ unsafeActionsBlocked: opts.unsafeActionsBlocked || 0,
195
+ });
196
+
197
+ if (opts.manualDecisionsAvoided > 0) {
198
+ emitSeamlessEvent(cwd, "manual_decision_avoided", {
199
+ triggerCommand: opts.command || "unknown",
200
+ count: opts.manualDecisionsAvoided,
201
+ });
202
+ }
203
+
204
+ if (opts.userInterruptAvoided) {
205
+ emitSeamlessEvent(cwd, "user_interrupt_avoided", {
206
+ triggerCommand: opts.command || "unknown",
207
+ });
208
+ }
209
+
210
+ if (opts.interruptionRequired) {
211
+ emitSeamlessEvent(cwd, "interruption_required", {
212
+ triggerCommand: opts.command || "unknown",
213
+ reason: opts.interruptReason || "unknown",
214
+ });
215
+ }
216
+
217
+ return { summary, summaryPath, mode };
218
+ } catch {
219
+ return null;
220
+ }
221
+ }
222
+
223
+ // ── Enforcement contract surface ──────────────────────────────────────────────
224
+
225
+ function buildEnforcementSurface(cwd) {
226
+ const mode = getSeamlessMode({});
227
+ try {
228
+ const { readLatestValueSummary } = require("./seamless-outcome");
229
+ const summary = readLatestValueSummary(cwd);
230
+ return {
231
+ contract: CONTRACT,
232
+ schemaVersion: SCHEMA_VERSION,
233
+ mode,
234
+ enforced: true,
235
+ manualDecisionsAvoided: summary?.saved?.manualDecisionsAvoided || 0,
236
+ unsafeActionsBlocked: summary?.protected?.unsafeActionsBlocked || 0,
237
+ qualityPreserved: summary?.quality?.qualityPreserved !== false,
238
+ escalationRecommended: Boolean(summary?.quality?.escalationRecommended),
239
+ };
240
+ } catch {
241
+ return {
242
+ contract: CONTRACT,
243
+ schemaVersion: SCHEMA_VERSION,
244
+ mode,
245
+ enforced: true,
246
+ manualDecisionsAvoided: 0,
247
+ unsafeActionsBlocked: 0,
248
+ qualityPreserved: true,
249
+ escalationRecommended: false,
250
+ };
251
+ }
252
+ }
253
+
254
+ // ── Task continuation event emission ─────────────────────────────────────────
255
+ //
256
+ // Emits product-learning events for task continuation. Called after the guard
257
+ // payload is built — best-effort, never throws.
258
+
259
+ function emitTaskContinuationEvents(cwd, opts = {}) {
260
+ try {
261
+ const { appendProductLearningEvent } = require("./product-learning-events");
262
+ const continuationType = opts.continuationType || "unknown";
263
+ const safeNextAction = opts.safeNextAction || "";
264
+
265
+ // Detect generic fallback text
266
+ const isGeneric =
267
+ safeNextAction.startsWith("Reduce scope around the highest-risk") ||
268
+ safeNextAction.startsWith("Keep the action narrowed to the smallest") ||
269
+ safeNextAction === "" ||
270
+ safeNextAction == null;
271
+
272
+ appendProductLearningEvent(cwd, {
273
+ eventName: "task_continuation_generated",
274
+ category: "task_continuation",
275
+ surface: "local",
276
+ status: "observed",
277
+ payload: {
278
+ continuationType,
279
+ decision: opts.decision || "unknown",
280
+ riskLevel: opts.riskLevel || "unknown",
281
+ actionType: opts.actionType || "unknown",
282
+ taskStillExecutable: opts.taskStillExecutable,
283
+ fallbackAvailable: Boolean(opts.fallbackAvailable),
284
+ escalationRecommended: Boolean(opts.escalationRecommended),
285
+ },
286
+ });
287
+
288
+ if (isGeneric) {
289
+ appendProductLearningEvent(cwd, {
290
+ eventName: "generic_safe_next_action_detected",
291
+ category: "task_continuation",
292
+ surface: "local",
293
+ status: "observed",
294
+ payload: {
295
+ continuationType,
296
+ decision: opts.decision || "unknown",
297
+ actionType: opts.actionType || "unknown",
298
+ riskLevel: opts.riskLevel || "unknown",
299
+ },
300
+ });
301
+ } else {
302
+ appendProductLearningEvent(cwd, {
303
+ eventName: "task_specific_safe_next_action_generated",
304
+ category: "task_continuation",
305
+ surface: "local",
306
+ status: "observed",
307
+ payload: {
308
+ continuationType,
309
+ decision: opts.decision || "unknown",
310
+ actionType: opts.actionType || "unknown",
311
+ riskLevel: opts.riskLevel || "unknown",
312
+ },
313
+ });
314
+ }
315
+ } catch {}
316
+ }
317
+
318
+ module.exports = {
319
+ CONTRACT,
320
+ SCHEMA_VERSION,
321
+ getDecisionVisibility,
322
+ buildGuardBlockOutcome,
323
+ writeSeamlessRunReceipt,
324
+ emitTaskContinuationEvents,
325
+ formatCompactRouteLine,
326
+ formatCompactContextLine,
327
+ formatCompactGuardLine,
328
+ buildEnforcementSurface,
329
+ };