blun-king-cli 9.1.567 → 9.1.569

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 (262) hide show
  1. package/agent-spine-plugin/.claude-plugin/marketplace.json +1 -1
  2. package/agent-spine-plugin/.claude-plugin/plugin.json +1 -1
  3. package/agent-spine-plugin/.codex-plugin/plugin.json +2 -1
  4. package/agent-spine-plugin/CHANGELOG.md +1581 -0
  5. package/agent-spine-plugin/README.md +30 -4
  6. package/agent-spine-plugin/blun.plugin.json +3 -3
  7. package/agent-spine-plugin/docs/acceptance.md +61 -0
  8. package/agent-spine-plugin/docs/assignment-continuation.md +48 -0
  9. package/agent-spine-plugin/docs/host-integration.md +178 -0
  10. package/agent-spine-plugin/docs/preflight-recall.md +69 -0
  11. package/agent-spine-plugin/docs/preservation-contract.md +53 -0
  12. package/agent-spine-plugin/docs/quality-gates.md +50 -0
  13. package/agent-spine-plugin/docs/releasing.md +85 -0
  14. package/agent-spine-plugin/docs/session-timeline.md +251 -0
  15. package/agent-spine-plugin/docs/source-roots.md +113 -0
  16. package/agent-spine-plugin/docs/structured-completion.md +67 -0
  17. package/agent-spine-plugin/docs/world-model.md +94 -0
  18. package/agent-spine-plugin/hooks/codex.json +2 -2
  19. package/agent-spine-plugin/hooks/hooks.json +1 -1
  20. package/agent-spine-plugin/hooks/version.json +1 -1
  21. package/agent-spine-plugin/package.json +13 -3
  22. package/agent-spine-plugin/scripts/check-codex-install.js +226 -0
  23. package/agent-spine-plugin/scripts/check-hosts.js +14 -5
  24. package/agent-spine-plugin/scripts/check-install-hook.js +226 -0
  25. package/agent-spine-plugin/scripts/check-install-selfstarter.js +154 -0
  26. package/agent-spine-plugin/scripts/check-install.js +478 -0
  27. package/agent-spine-plugin/scripts/check-line-budget.js +58 -0
  28. package/agent-spine-plugin/scripts/check-syntax.js +29 -0
  29. package/agent-spine-plugin/scripts/github-actions.js +11 -0
  30. package/agent-spine-plugin/scripts/hermetic-process.js +183 -0
  31. package/agent-spine-plugin/scripts/release-check.js +145 -0
  32. package/agent-spine-plugin/scripts/run-acceptance.js +19 -0
  33. package/agent-spine-plugin/scripts/run-checks.js +47 -0
  34. package/agent-spine-plugin/scripts/run-tests-hermetic.js +89 -0
  35. package/agent-spine-plugin/skills/agent-spine/SKILL.md +16 -2
  36. package/agent-spine-plugin/spine-example/1-identity.md +12 -0
  37. package/agent-spine-plugin/spine-example/2-voice.md +6 -0
  38. package/agent-spine-plugin/spine-example/3-conduct.md +8 -0
  39. package/agent-spine-plugin/spine-example/4-history.md +4 -0
  40. package/agent-spine-plugin/src/cli-agent.js +296 -0
  41. package/agent-spine-plugin/src/cli-attention.js +95 -0
  42. package/agent-spine-plugin/src/cli-autonomy.js +36 -0
  43. package/agent-spine-plugin/src/cli-common.js +71 -0
  44. package/agent-spine-plugin/src/cli-continuity.js +116 -0
  45. package/agent-spine-plugin/src/cli-core.js +128 -0
  46. package/agent-spine-plugin/src/cli-diagnostics.js +291 -0
  47. package/agent-spine-plugin/src/cli-host.js +21 -0
  48. package/agent-spine-plugin/src/cli-learning.js +305 -0
  49. package/agent-spine-plugin/src/cli-premortem.js +16 -0
  50. package/agent-spine-plugin/src/cli-sharing.js +230 -0
  51. package/agent-spine-plugin/src/cli.js +40 -1350
  52. package/agent-spine-plugin/src/codex-reader-launcher.js +182 -0
  53. package/agent-spine-plugin/src/hook.js +185 -567
  54. package/agent-spine-plugin/src/index.js +16 -2
  55. package/agent-spine-plugin/src/lib/acceptance.js +113 -11
  56. package/agent-spine-plugin/src/lib/action-lesson-recall.js +53 -0
  57. package/agent-spine-plugin/src/lib/attention-context.js +167 -0
  58. package/agent-spine-plugin/src/lib/attention-events.js +113 -0
  59. package/agent-spine-plugin/src/lib/attention-privacy.js +72 -0
  60. package/agent-spine-plugin/src/lib/attention-schema.js +164 -0
  61. package/agent-spine-plugin/src/lib/attention-storage.js +93 -0
  62. package/agent-spine-plugin/src/lib/attention.js +9 -600
  63. package/agent-spine-plugin/src/lib/audit-premortem.js +223 -0
  64. package/agent-spine-plugin/src/lib/audit.js +56 -6
  65. package/agent-spine-plugin/src/lib/autonomy-policy.js +110 -0
  66. package/agent-spine-plugin/src/lib/autonomy-store.js +202 -0
  67. package/agent-spine-plugin/src/lib/autonomy.js +8 -0
  68. package/agent-spine-plugin/src/lib/briefing.js +67 -4
  69. package/agent-spine-plugin/src/lib/catalog-document-read.js +51 -0
  70. package/agent-spine-plugin/src/lib/catalog.js +1 -1
  71. package/agent-spine-plugin/src/lib/codex-installation.js +231 -0
  72. package/agent-spine-plugin/src/lib/codex-skill-installation.js +211 -0
  73. package/agent-spine-plugin/src/lib/context.js +3 -1
  74. package/agent-spine-plugin/src/lib/delivery-agent-usage.js +224 -0
  75. package/agent-spine-plugin/src/lib/delivery-assignment.js +220 -0
  76. package/agent-spine-plugin/src/lib/delivery-command-actions.js +453 -0
  77. package/agent-spine-plugin/src/lib/delivery-knowledge.js +78 -0
  78. package/agent-spine-plugin/src/lib/delivery-premortem-binding.js +107 -0
  79. package/agent-spine-plugin/src/lib/delivery-premortem-closure.js +171 -0
  80. package/agent-spine-plugin/src/lib/delivery-premortem-codec.js +45 -0
  81. package/agent-spine-plugin/src/lib/delivery-premortem-correction.js +101 -0
  82. package/agent-spine-plugin/src/lib/delivery-premortem-file.js +21 -0
  83. package/agent-spine-plugin/src/lib/delivery-premortem-index.js +493 -0
  84. package/agent-spine-plugin/src/lib/delivery-premortem-inspection.js +37 -0
  85. package/agent-spine-plugin/src/lib/delivery-premortem-recovery.js +120 -0
  86. package/agent-spine-plugin/src/lib/delivery-premortem-rejection.js +65 -0
  87. package/agent-spine-plugin/src/lib/delivery-premortem-results.js +28 -0
  88. package/agent-spine-plugin/src/lib/delivery-premortem-session-guard.js +46 -0
  89. package/agent-spine-plugin/src/lib/delivery-premortem-write-ledger.js +285 -0
  90. package/agent-spine-plugin/src/lib/delivery-premortem.js +499 -0
  91. package/agent-spine-plugin/src/lib/delivery-shell-heredoc.js +115 -0
  92. package/agent-spine-plugin/src/lib/delivery-shell-substitutions.js +111 -0
  93. package/agent-spine-plugin/src/lib/delivery-shell-wrapper.js +64 -0
  94. package/agent-spine-plugin/src/lib/delivery-target.js +73 -0
  95. package/agent-spine-plugin/src/lib/delivery-verification.js +445 -0
  96. package/agent-spine-plugin/src/lib/documents.js +27 -5
  97. package/agent-spine-plugin/src/lib/filesystem-retry.js +2 -0
  98. package/agent-spine-plugin/src/lib/gateway-common.js +68 -0
  99. package/agent-spine-plugin/src/lib/gateway-control.js +302 -0
  100. package/agent-spine-plugin/src/lib/gateway-delivery.js +103 -0
  101. package/agent-spine-plugin/src/lib/gateway-execution.js +350 -0
  102. package/agent-spine-plugin/src/lib/gateway-host-lifecycle.js +185 -0
  103. package/agent-spine-plugin/src/lib/gateway-inspection.js +85 -0
  104. package/agent-spine-plugin/src/lib/gateway-knowledge.js +129 -0
  105. package/agent-spine-plugin/src/lib/gateway-policy-provenance.js +197 -0
  106. package/agent-spine-plugin/src/lib/gateway-premortem-disposition.js +82 -0
  107. package/agent-spine-plugin/src/lib/gateway-premortem.js +363 -0
  108. package/agent-spine-plugin/src/lib/gateway-runs.js +300 -0
  109. package/agent-spine-plugin/src/lib/gateway-runtime-identity.js +31 -0
  110. package/agent-spine-plugin/src/lib/gateway-runtime-records.js +21 -0
  111. package/agent-spine-plugin/src/lib/gateway-runtime.js +18 -1623
  112. package/agent-spine-plugin/src/lib/gateway-state-transaction.js +356 -0
  113. package/agent-spine-plugin/src/lib/gateway-state.js +342 -0
  114. package/agent-spine-plugin/src/lib/hook-artifact-guards.js +356 -0
  115. package/agent-spine-plugin/src/lib/hook-audit.js +16 -2
  116. package/agent-spine-plugin/src/lib/hook-briefing-use.js +68 -0
  117. package/agent-spine-plugin/src/lib/hook-context.js +424 -0
  118. package/agent-spine-plugin/src/lib/hook-final-message.js +26 -0
  119. package/agent-spine-plugin/src/lib/hook-input.js +27 -0
  120. package/agent-spine-plugin/src/lib/hook-output.js +140 -0
  121. package/agent-spine-plugin/src/lib/hook-premortem.js +257 -0
  122. package/agent-spine-plugin/src/lib/hook-process-advisory.js +37 -0
  123. package/agent-spine-plugin/src/lib/hook-protection.js +95 -0
  124. package/agent-spine-plugin/src/lib/hook-stop-verification.js +84 -0
  125. package/agent-spine-plugin/src/lib/hook-timeline.js +91 -0
  126. package/agent-spine-plugin/src/lib/identifier-analysis.js +446 -0
  127. package/agent-spine-plugin/src/lib/indexed-memory.js +23 -6
  128. package/agent-spine-plugin/src/lib/knowledge-evidence.js +431 -0
  129. package/agent-spine-plugin/src/lib/learning-applications.js +441 -0
  130. package/agent-spine-plugin/src/lib/learning-candidates.js +274 -0
  131. package/agent-spine-plugin/src/lib/learning-context.js +130 -0
  132. package/agent-spine-plugin/src/lib/learning-delivery-contracts.js +310 -0
  133. package/agent-spine-plugin/src/lib/learning-evaluation-contracts.js +350 -0
  134. package/agent-spine-plugin/src/lib/learning-evaluation-registration.js +372 -0
  135. package/agent-spine-plugin/src/lib/learning-evaluation-revocation.js +292 -0
  136. package/agent-spine-plugin/src/lib/learning-evidence-contracts.js +321 -0
  137. package/agent-spine-plugin/src/lib/learning-findings.js +458 -0
  138. package/agent-spine-plugin/src/lib/learning-measurement-contracts.js +285 -0
  139. package/agent-spine-plugin/src/lib/learning-measurements.js +265 -0
  140. package/agent-spine-plugin/src/lib/learning-outcome-contracts.js +165 -0
  141. package/agent-spine-plugin/src/lib/learning-outcomes.js +343 -0
  142. package/agent-spine-plugin/src/lib/learning-reconciliation.js +290 -0
  143. package/agent-spine-plugin/src/lib/learning-retry-contracts.js +188 -0
  144. package/agent-spine-plugin/src/lib/learning-schema.js +229 -0
  145. package/agent-spine-plugin/src/lib/learning-scope-targets.js +424 -0
  146. package/agent-spine-plugin/src/lib/learning-state-upgrade.js +477 -0
  147. package/agent-spine-plugin/src/lib/learning-status-configuration.js +476 -0
  148. package/agent-spine-plugin/src/lib/learning-storage.js +203 -0
  149. package/agent-spine-plugin/src/lib/learning-trial-recovery.js +218 -0
  150. package/agent-spine-plugin/src/lib/learning-validation-contracts.js +375 -0
  151. package/agent-spine-plugin/src/lib/learning-validation-renewal.js +298 -0
  152. package/agent-spine-plugin/src/lib/learning-validation-runtime.js +234 -0
  153. package/agent-spine-plugin/src/lib/learning.js +36 -6923
  154. package/agent-spine-plugin/src/lib/lesson-recall-session.js +172 -0
  155. package/agent-spine-plugin/src/lib/mcp-autonomy-tools.js +37 -0
  156. package/agent-spine-plugin/src/lib/mcp-delivery-completion.js +124 -0
  157. package/agent-spine-plugin/src/lib/mcp-delivery-tools.js +45 -0
  158. package/agent-spine-plugin/src/lib/mcp-premortem.js +68 -0
  159. package/agent-spine-plugin/src/lib/mcp-runtime.js +269 -0
  160. package/agent-spine-plugin/src/lib/mcp-source-context.js +51 -0
  161. package/agent-spine-plugin/src/lib/mcp-timeline-tools.js +164 -0
  162. package/agent-spine-plugin/src/lib/mcp-world-tools.js +52 -0
  163. package/agent-spine-plugin/src/lib/owned-file-lock.js +30 -10
  164. package/agent-spine-plugin/src/lib/project-portfolio.js +176 -0
  165. package/agent-spine-plugin/src/lib/selfstarter-core.js +382 -0
  166. package/agent-spine-plugin/src/lib/selfstarter-jobs.js +149 -0
  167. package/agent-spine-plugin/src/lib/selfstarter-lease.js +204 -0
  168. package/agent-spine-plugin/src/lib/selfstarter-policy.js +90 -0
  169. package/agent-spine-plugin/src/lib/selfstarter-workspace.js +111 -0
  170. package/agent-spine-plugin/src/lib/selfstarter.js +11 -911
  171. package/agent-spine-plugin/src/lib/session-timeline-auth.js +316 -0
  172. package/agent-spine-plugin/src/lib/session-timeline-codex.js +58 -0
  173. package/agent-spine-plugin/src/lib/session-timeline-contract.js +48 -0
  174. package/agent-spine-plugin/src/lib/session-timeline-enrollment-source.js +41 -0
  175. package/agent-spine-plugin/src/lib/session-timeline-enrollment-storage.js +132 -0
  176. package/agent-spine-plugin/src/lib/session-timeline-enrollment-transport.js +17 -0
  177. package/agent-spine-plugin/src/lib/session-timeline-enrollment.js +500 -0
  178. package/agent-spine-plugin/src/lib/session-timeline-event-extract.js +157 -0
  179. package/agent-spine-plugin/src/lib/session-timeline-host-origin.js +74 -0
  180. package/agent-spine-plugin/src/lib/session-timeline-host-receipt.js +117 -0
  181. package/agent-spine-plugin/src/lib/session-timeline-invocation.js +201 -0
  182. package/agent-spine-plugin/src/lib/session-timeline-king.js +79 -0
  183. package/agent-spine-plugin/src/lib/session-timeline-prior.js +59 -0
  184. package/agent-spine-plugin/src/lib/session-timeline-provider.js +34 -0
  185. package/agent-spine-plugin/src/lib/session-timeline-query.js +55 -0
  186. package/agent-spine-plugin/src/lib/session-timeline-results.js +31 -0
  187. package/agent-spine-plugin/src/lib/session-timeline-root.js +11 -0
  188. package/agent-spine-plugin/src/lib/session-timeline-search.js +82 -0
  189. package/agent-spine-plugin/src/lib/session-timeline-sid-acl.js +217 -0
  190. package/agent-spine-plugin/src/lib/session-timeline-source.js +83 -0
  191. package/agent-spine-plugin/src/lib/session-timeline-state.js +45 -0
  192. package/agent-spine-plugin/src/lib/session-timeline-transport.js +50 -0
  193. package/agent-spine-plugin/src/lib/session-timeline-windows-acl.js +148 -0
  194. package/agent-spine-plugin/src/lib/session-timeline.js +453 -0
  195. package/agent-spine-plugin/src/lib/source-roots.js +69 -136
  196. package/agent-spine-plugin/src/lib/source-tree-scan.js +178 -0
  197. package/agent-spine-plugin/src/lib/task-knowledge-context.js +78 -0
  198. package/agent-spine-plugin/src/lib/timeline-tool-guard.js +202 -0
  199. package/agent-spine-plugin/src/lib/world-knowledge.js +249 -0
  200. package/agent-spine-plugin/src/lib/world-model.js +278 -0
  201. package/agent-spine-plugin/src/mcp.js +20 -161
  202. package/agent-spine-plugin/src/version.js +1 -1
  203. package/agent-spine-plugin/src/worker.js +22 -5
  204. package/bin/agent-resume-snapshot.cjs +2 -2
  205. package/bin/agentspine-king-goal-inbox.mjs +111 -0
  206. package/bin/agentspine-king-goal-intake.mjs +106 -0
  207. package/bin/baseline-skill-performance-policy.cjs +1 -16
  208. package/bin/core-bootstrap.js +2 -0
  209. package/bin/curiosity-scout-policy.cjs +5 -1
  210. package/bin/input-draft-persistence.cjs +2 -2
  211. package/bin/king-tui-function-contract.json +33 -0
  212. package/bin/launcher-restart-policy.cjs +150 -0
  213. package/bin/launcher-runtime.js +56 -44
  214. package/bin/managed-context-startup-policy.cjs +27 -0
  215. package/bin/managed-plugin-selection.cjs +116 -0
  216. package/bin/mistake-relevance-policy.cjs +1 -1
  217. package/bin/observer-hooks.cjs +14 -0
  218. package/bin/oversized-context-offload-policy.cjs +86 -0
  219. package/bin/plugin-bootstrap.js +7 -40
  220. package/bin/proactive-compaction-policy.cjs +1 -1
  221. package/bin/provider-model-refresh-deadline.cjs +53 -0
  222. package/bin/provider-model-refresh-policy.cjs +107 -0
  223. package/bin/release-artifact-freeze-policy.cjs +30 -0
  224. package/bin/repeated-user-message-projection.cjs +3 -126
  225. package/bin/research-page-result.cjs +74 -0
  226. package/bin/runtime-exit-ledger.cjs +1 -0
  227. package/bin/session-compaction-policy.cjs +84 -0
  228. package/bin/skill-listing-performance-policy.cjs +2 -2
  229. package/bin/standard-tools-bootstrap.js +0 -37
  230. package/bin/subagent-skill-policy.cjs +3 -1
  231. package/bin/telegram-approval-relay.cjs +12 -7
  232. package/bin/telegram-private-conversation-policy.cjs +3 -2
  233. package/bin/telegram-queue-handoff-policy.cjs +24 -0
  234. package/bin/thinking-activity-status-policy.cjs +1 -1
  235. package/bin/thinking-only-guard.cjs +16 -12
  236. package/bin/tool-call-loop-policy.cjs +0 -2
  237. package/bin/tool-result-offload-policy.cjs +11 -2
  238. package/bin/tui-functional-contract.cjs +55 -0
  239. package/bin/update-notice.js +18 -14
  240. package/bin/user-message-offload-policy.cjs +1 -1
  241. package/bin/user-prompt-hook-origin-policy.cjs +34 -0
  242. package/bin/windows-node-crash-dump.cjs +110 -0
  243. package/blun.mjs +2517 -1048
  244. package/codebase-index/codebase_index.py +4 -3
  245. package/package.json +3 -17
  246. package/standard-skills/research-evidence/SKILL.md +39 -0
  247. package/standard-skills/research-evidence/references/evidence-format.md +82 -0
  248. package/standard-skills/research-evidence/scripts/evidence-collection.cjs +254 -0
  249. package/standard-skills/research-evidence/scripts/score-report.cjs +112 -0
  250. package/standard-skills/web-lesen/SKILL.md +37 -22
  251. package/standard-skills/web-lesen/scripts/crawl_public.py +376 -0
  252. package/telegram-plugin/DELIVERY.md +36 -0
  253. package/telegram-plugin/bin/telegram-approval-relay.cjs +13 -7
  254. package/telegram-plugin/bin/telegram-launcher-status-queue.cjs +122 -0
  255. package/telegram-plugin/bin/telegram-private-conversation-policy.cjs +3 -2
  256. package/telegram-plugin/bin/telegram-reply-parts.cjs +149 -0
  257. package/telegram-plugin/dist/bridge.mjs +7 -56
  258. package/telegram-plugin/dist/mcp-server.mjs +33 -4
  259. package/agent-spine-plugin/skill/SKILL.md +0 -76
  260. package/bin/mnemo-connect-heartbeat.cjs +0 -204
  261. package/bin/mnemo-tool-agent-policy.cjs +0 -22
  262. package/telegram-plugin/bin/telegram-mnemo-capture.cjs +0 -297
@@ -0,0 +1,298 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import {
3
+ ID_RE, REGISTRY_BOUND_EVALUATIONS
4
+ } from "./learning-schema.js";
5
+ import {
6
+ exactScope, digest, revokedMeasurement
7
+ } from "./learning-scope-targets.js";
8
+ import {
9
+ revokedEvaluationForCandidate, revalidationTrialDigest, storedRevalidationWindowStructure, validationLeasePayload
10
+ } from "./learning-validation-contracts.js";
11
+ import {
12
+ validationLeaseState
13
+ } from "./learning-validation-runtime.js";
14
+ import {
15
+ evaluationStalenessPolicy, revokedEvidenceSourceAttestationForCandidate
16
+ } from "./learning-measurement-contracts.js";
17
+ import {
18
+ revokedApplication, revokedApplicationForCandidate, revokedDelivery, revokedOutcomeForCandidate
19
+ } from "./learning-delivery-contracts.js";
20
+ import {
21
+ date, mutation, preserve
22
+ } from "./learning-storage.js";
23
+ import {
24
+ improvement, rollbackCandidate
25
+ } from "./learning-reconciliation.js";
26
+
27
+ export function validationEvidencePreviouslyUsed(state, measurementId, applicationId, deliveryId) {
28
+ const leases = [
29
+ ...state.validationLeases,
30
+ ...state.history.filter((entry) => entry.kind === "learning-validation").map((entry) => entry.value)
31
+ ];
32
+ return leases.some((lease) => ["agentspine.learning-validation/v2", "agentspine.learning-validation/v3",
33
+ "agentspine.learning-validation/v4", "agentspine.learning-validation/v5"].includes(lease?.schema)
34
+ && lease.renewalEvidence?.some((entry) => entry.measurementId === measurementId
35
+ || entry.applicationId === applicationId || entry.deliveryId === deliveryId));
36
+ }
37
+
38
+ export async function renewLearningValidation({
39
+ root = process.cwd(), learningId, evidence, confirmLocalValidation = false, now = new Date()
40
+ }) {
41
+ if (!confirmLocalValidation) throw new Error("validation renewal requires explicit local confirmation");
42
+ if (!ID_RE.test(learningId || "") || !Array.isArray(evidence) || !evidence.length) {
43
+ throw new Error("validation renewal requires a learningId and evidence bindings");
44
+ }
45
+ const timestamp = date(now, "now");
46
+ return mutation(root, (state, _catalog, learningPath) => {
47
+ const candidate = state.candidates.find((entry) => entry.id === learningId);
48
+ if (revokedEvaluationForCandidate(state, candidate)) {
49
+ throw new Error("validation renewal baseline contains an explicitly revoked evaluation contract");
50
+ }
51
+ if (revokedEvidenceSourceAttestationForCandidate(state, candidate)) {
52
+ throw new Error("validation renewal baseline contains an explicitly revoked evidence source attestation");
53
+ }
54
+ if (revokedApplicationForCandidate(state, candidate)) {
55
+ throw new Error("validation renewal baseline contains an explicitly revoked application");
56
+ }
57
+ if (revokedOutcomeForCandidate(state, candidate)) {
58
+ throw new Error("validation renewal baseline contains an explicitly revoked outcome");
59
+ }
60
+ const validation = validationLeaseState(state, candidate, timestamp);
61
+ const revalidation = candidate?.promotion?.canary?.revalidation;
62
+ if (validation.status !== "active"
63
+ || !REGISTRY_BOUND_EVALUATIONS.has(validation.evaluation?.schema)
64
+ || revalidation?.status !== "active" || revalidation.predecessorValidationId !== validation.lease.id
65
+ || revalidation.predecessorValidationDigest !== validation.lease.digest
66
+ || !storedRevalidationWindowStructure(revalidation)
67
+ || new Date(revalidation.expiresAt).getTime() < new Date(timestamp).getTime()) {
68
+ throw new Error("validation renewal requires one current matching revalidation window");
69
+ }
70
+ const contract = validation.evaluation;
71
+ const baselineReferences = validation.lease.schema === "agentspine.learning-validation/v1"
72
+ ? validation.lease.beforeOutcomes : validation.lease.baselineOutcomes;
73
+ const baselines = baselineReferences.map((reference) => state.outcomes.find((outcome) =>
74
+ outcome.id === reference.id && outcome.digest === reference.digest));
75
+ if (baselines.some((item) => !item)) throw new Error("validation renewal baseline evidence is missing");
76
+ const baselineByRoot = new Map(baselines.map((item) => [item.measurement.evaluatorRootDigest, item]));
77
+ let normalized = evidence.map((entry) => {
78
+ const measurement = state.measurements.find((item) => item.id === entry?.measurementId);
79
+ const application = state.applications.find((item) => item.id === entry?.applicationId);
80
+ const delivery = state.deliveries.find((item) => item.id === entry?.deliveryId);
81
+ if (!measurement || !application || !delivery) throw new Error("validation renewal evidence binding is missing");
82
+ if (revokedApplication(state, application.id)) {
83
+ throw new Error("validation renewal application was explicitly revoked");
84
+ }
85
+ if (revokedMeasurement(state, measurement.id)) {
86
+ throw new Error("validation renewal measurement was explicitly revoked");
87
+ }
88
+ if (revokedDelivery(state, delivery.id)) {
89
+ throw new Error("validation renewal delivery was explicitly revoked");
90
+ }
91
+ const baseline = baselineByRoot.get(measurement.measurement?.evaluatorRootDigest);
92
+ if (measurement.phase !== "after" || measurement.learningId !== learningId
93
+ || measurement.evaluationId !== contract.id || !exactScope(measurement.scope, contract.scope)
94
+ || !baseline || measurement.measurement.kind === "model-suggestion"
95
+ || measurement.measurement.kind !== baseline.measurement.kind
96
+ || measurement.coverage.caseCount !== baseline.coverage.caseCount
97
+ || new Date(measurement.measuredAt).getTime() < new Date(revalidation.startedAt).getTime()) {
98
+ throw new Error("validation renewal measurement does not match the frozen baseline cohort");
99
+ }
100
+ if (application.learningId !== learningId || !exactScope(application.scope, contract.scope)
101
+ || new Date(application.projectedAt).getTime() < new Date(revalidation.startedAt).getTime()
102
+ || delivery.applicationId !== application.id || delivery.learningId !== learningId
103
+ || !exactScope(delivery.scope, contract.scope)
104
+ || new Date(measurement.measuredAt).getTime() < new Date(delivery.completedAt).getTime()
105
+ || new Date(measurement.measuredAt).getTime() > new Date(application.expiresAt).getTime()) {
106
+ throw new Error("validation renewal requires a distinct completed matching model turn");
107
+ }
108
+ if (validationEvidencePreviouslyUsed(state, measurement.id, application.id, delivery.id)) {
109
+ throw new Error("validation renewal evidence cannot be replayed");
110
+ }
111
+ return { measurement, application, delivery, baseline };
112
+ });
113
+ let selectionProof = null;
114
+ if (revalidation.schema === "agentspine.learning-revalidation-window/v2") {
115
+ const required = revalidation.selection.requiredDeliveries;
116
+ if (normalized.length !== required) {
117
+ throw new Error("validation renewal must measure the complete precommitted delivery cohort");
118
+ }
119
+ const completed = state.deliveries.filter((delivery) => delivery.learningId === learningId
120
+ && exactScope(delivery.scope, contract.scope)
121
+ && new Date(delivery.completedAt).getTime() >= new Date(revalidation.startedAt).getTime()
122
+ && new Date(delivery.completedAt).getTime() <= new Date(revalidation.expiresAt).getTime()
123
+ && state.applications.some((application) => application.id === delivery.applicationId
124
+ && application.learningId === learningId && exactScope(application.scope, contract.scope)
125
+ && new Date(application.projectedAt).getTime() >= new Date(revalidation.startedAt).getTime()))
126
+ .sort((a, b) => a.completedAt.localeCompare(b.completedAt) || a.id.localeCompare(b.id))
127
+ .slice(0, required);
128
+ if (completed.length !== required) {
129
+ throw new Error("validation renewal first-completed delivery cohort is not complete");
130
+ }
131
+ normalized = completed.map((delivery, index) => {
132
+ const selected = normalized.find((item) => item.delivery.id === delivery.id);
133
+ const frozenRoot = revalidation.selection.evaluatorRoots[index];
134
+ if (!selected) {
135
+ throw new Error("validation renewal cannot omit or replace a precommitted completed turn");
136
+ }
137
+ if (selected.measurement.measurement.evaluatorRootDigest !== frozenRoot.evaluatorRootDigest) {
138
+ throw new Error("validation renewal evaluator root does not match its precommitted turn slot");
139
+ }
140
+ return selected;
141
+ });
142
+ selectionProof = {
143
+ revalidationWindowId: revalidation.id,
144
+ revalidationWindowDigest: revalidation.digest,
145
+ mode: revalidation.selection.mode,
146
+ requiredDeliveries: required,
147
+ deliveries: normalized.map((item, index) => ({
148
+ slot: index + 1,
149
+ deliveryId: item.delivery.id,
150
+ deliveryDigest: item.delivery.digest,
151
+ evaluatorRootDigest: item.measurement.measurement.evaluatorRootDigest,
152
+ authority: "context-only"
153
+ })),
154
+ authority: "context-only"
155
+ };
156
+ } else if (["agentspine.learning-revalidation-window/v3",
157
+ "agentspine.learning-revalidation-window/v4"].includes(revalidation.schema)) {
158
+ const required = revalidation.selection.requiredDeliveries;
159
+ if (normalized.length !== required) {
160
+ throw new Error("validation renewal must measure the complete precommitted admission cohort");
161
+ }
162
+ const trialBound = revalidation.schema === "agentspine.learning-revalidation-window/v4";
163
+ const admitted = state.applications.filter((application) =>
164
+ application.schema === (trialBound ? "agentspine.learning-application/v4" : "agentspine.learning-application/v3")
165
+ && application.learningId === learningId && exactScope(application.scope, contract.scope)
166
+ && application.revalidationAdmission.revalidationWindowId === revalidation.id
167
+ && application.revalidationAdmission.revalidationWindowDigest === revalidation.digest)
168
+ .sort((a, b) => a.revalidationAdmission.slot - b.revalidationAdmission.slot);
169
+ if (admitted.length !== required) {
170
+ throw new Error("validation renewal first-admitted turn cohort is not complete");
171
+ }
172
+ normalized = admitted.map((application, index) => {
173
+ const selected = normalized.find((item) => item.application.id === application.id);
174
+ const admission = application.revalidationAdmission;
175
+ const frozenRoot = revalidation.selection.evaluatorRoots[index];
176
+ if (!selected) {
177
+ throw new Error("validation renewal cannot omit or replace a precommitted admitted turn");
178
+ }
179
+ if (admission.slot !== index + 1 || admission.evaluatorRootDigest !== frozenRoot.evaluatorRootDigest
180
+ || selected.measurement.measurement.evaluatorRootDigest !== frozenRoot.evaluatorRootDigest
181
+ || (trialBound && (admission.evaluatorId !== frozenRoot.evaluatorId
182
+ || admission.runId !== frozenRoot.runId || admission.trialDigest !== frozenRoot.trialDigest
183
+ || selected.measurement.measurement.evaluatorId !== frozenRoot.evaluatorId
184
+ || selected.measurement.measurement.runId !== frozenRoot.runId
185
+ || selected.measurement.coverage.caseCount !== frozenRoot.caseCount
186
+ || frozenRoot.trialDigest !== revalidationTrialDigest({
187
+ evaluationId: contract.id, evaluationDigest: contract.digest,
188
+ predecessorValidationId: validation.lease.id,
189
+ predecessorValidationDigest: validation.lease.digest,
190
+ slot: frozenRoot.slot, evaluatorId: frozenRoot.evaluatorId,
191
+ evaluatorRootDigest: frozenRoot.evaluatorRootDigest, runId: frozenRoot.runId,
192
+ benchmark: contract.benchmark, caseCount: frozenRoot.caseCount
193
+ })))) {
194
+ throw new Error("validation renewal evaluator root does not match its precommitted admission slot");
195
+ }
196
+ return selected;
197
+ });
198
+ selectionProof = {
199
+ revalidationWindowId: revalidation.id,
200
+ revalidationWindowDigest: revalidation.digest,
201
+ mode: revalidation.selection.mode,
202
+ requiredDeliveries: required,
203
+ applications: normalized.map((item, index) => ({
204
+ slot: index + 1,
205
+ applicationId: item.application.id,
206
+ applicationDigest: item.application.digest,
207
+ deliveryId: item.delivery.id,
208
+ deliveryDigest: item.delivery.digest,
209
+ evaluatorRootDigest: item.measurement.measurement.evaluatorRootDigest,
210
+ ...(trialBound ? {
211
+ evaluatorId: item.measurement.measurement.evaluatorId,
212
+ runId: item.measurement.measurement.runId,
213
+ trialDigest: revalidation.selection.evaluatorRoots[index].trialDigest
214
+ } : {}),
215
+ authority: "context-only"
216
+ })),
217
+ authority: "context-only"
218
+ };
219
+ }
220
+ const roots = new Set(normalized.map((item) => item.measurement.measurement.evaluatorRootDigest));
221
+ const applications = new Set(normalized.map((item) => item.application.id));
222
+ if (roots.size < contract.thresholds.afterReceipts || applications.size < contract.thresholds.afterReceipts
223
+ || !normalized.some((item) => item.measurement.measurement.kind === "objective")) {
224
+ throw new Error("validation renewal requires the frozen independent evidence threshold");
225
+ }
226
+ if (normalized.some((item) => item.measurement.metric.blockingDefects > 0)) {
227
+ const rolledBack = rollbackCandidate(state, candidate, "validation renewal recorded a blocking defect",
228
+ timestamp, "automatic-revalidation-regression");
229
+ return { ...rolledBack, decision: "rolled-back", learningPath };
230
+ }
231
+ const deltas = normalized.map((item) => improvement(contract.metric.direction,
232
+ item.baseline.metric.value, item.measurement.metric.value));
233
+ if (deltas.some((value) => value < -contract.thresholds.regressionTolerance)) {
234
+ const rolledBack = rollbackCandidate(state, candidate, "validation renewal regressed against its frozen baseline",
235
+ timestamp, "automatic-revalidation-regression");
236
+ return { ...rolledBack, decision: "rolled-back", learningPath };
237
+ }
238
+ const average = deltas.reduce((sum, value) => sum + value, 0) / deltas.length;
239
+ if (average < contract.thresholds.minImprovement) {
240
+ const rolledBack = rollbackCandidate(state, candidate, "validation renewal did not meet the frozen minimum improvement",
241
+ timestamp, "automatic-revalidation-no-improvement");
242
+ return { ...rolledBack, decision: "rolled-back", learningPath };
243
+ }
244
+ const expiresAt = new Date(Math.min(new Date(contract.expiresAt).getTime(),
245
+ new Date(timestamp).getTime()
246
+ + evaluationStalenessPolicy(contract, state.config).canaryTtlDays * 86400000)).toISOString();
247
+ if (new Date(expiresAt).getTime() <= new Date(validation.lease.expiresAt).getTime()) {
248
+ throw new Error("validation renewal cannot extend the current evidence lease");
249
+ }
250
+ const payload = validationLeasePayload({
251
+ schema: selectionProof?.mode === "first-admitted-trials" ? "agentspine.learning-validation/v5"
252
+ : selectionProof?.mode === "first-admitted-turns" ? "agentspine.learning-validation/v4"
253
+ : selectionProof ? "agentspine.learning-validation/v3" : "agentspine.learning-validation/v2",
254
+ id: `validation:${randomUUID()}`,
255
+ learningId,
256
+ evaluationId: contract.id,
257
+ evaluationDigest: contract.digest,
258
+ evaluatorRegistryBindingDigest: validation.lease.evaluatorRegistryBindingDigest,
259
+ scope: contract.scope,
260
+ metric: contract.metric,
261
+ baselineOutcomes: baselineReferences,
262
+ predecessorValidation: { id: validation.lease.id, digest: validation.lease.digest, authority: "context-only" },
263
+ renewalEvidence: normalized.map(({ measurement, application, delivery }) => ({
264
+ measurementId: measurement.id, measurementDigest: measurement.digest,
265
+ applicationId: application.id, applicationDigest: application.digest,
266
+ deliveryId: delivery.id, deliveryDigest: delivery.digest,
267
+ evaluatorRootDigest: measurement.measurement.evaluatorRootDigest,
268
+ authority: "context-only"
269
+ })).sort((a, b) => a.evaluatorRootDigest.localeCompare(b.evaluatorRootDigest)),
270
+ selectionProof,
271
+ improvement: average,
272
+ validatedAt: timestamp,
273
+ expiresAt
274
+ });
275
+ const lease = { ...payload, digest: digest(payload) };
276
+ preserve(state, "learning-validation", validation.lease, timestamp);
277
+ state.validationLeases = state.validationLeases.filter((entry) => entry.learningId !== learningId);
278
+ state.validationLeases.push(lease);
279
+ state.validationLeases.sort((a, b) => a.id.localeCompare(b.id));
280
+ preserve(state, "learning-candidate", candidate, timestamp);
281
+ const renewed = {
282
+ ...candidate,
283
+ promotion: { ...candidate.promotion, canary: {
284
+ ...candidate.promotion.canary,
285
+ validatedAt: timestamp,
286
+ expiresAt,
287
+ improvement: average,
288
+ validationLeaseId: lease.id,
289
+ validationLeaseDigest: lease.digest,
290
+ revalidation: null
291
+ } },
292
+ updatedAt: timestamp,
293
+ authority: "context-only"
294
+ };
295
+ state.candidates = state.candidates.map((entry) => entry.id === learningId ? renewed : entry);
296
+ return { candidate: renewed, lease, decision: "renewed", learningPath };
297
+ });
298
+ }
@@ -0,0 +1,234 @@
1
+ import {
2
+ ID_RE, DIGEST_RE, REGISTRY_BOUND_EVALUATIONS, DEADLINE_BOUND_EVALUATIONS, VALIDATION_REVOCATION_REASONS
3
+ } from "./learning-schema.js";
4
+ import {
5
+ exactScope, digest, learningTargetForCandidate, revokedEvidence, revokedMeasurementForCandidate, activeEvaluationBinding,
6
+ revokedEvaluation
7
+ } from "./learning-scope-targets.js";
8
+ import {
9
+ revokedEvaluationForCandidate, storedValidationLeaseStructure
10
+ } from "./learning-validation-contracts.js";
11
+ import {
12
+ revokedEvidenceSourceAttestation, revokedEvidenceSourceAttestationForCandidate
13
+ } from "./learning-measurement-contracts.js";
14
+ import {
15
+ revokedApplicationForCandidate, revokedDeliveryForCandidate, revokedOutcomeForCandidate
16
+ } from "./learning-delivery-contracts.js";
17
+
18
+ export function validationLeaseRecords(state) {
19
+ return [...state.validationLeases, ...state.history
20
+ .filter((entry) => entry.kind === "learning-validation")
21
+ .map((entry) => entry.value)
22
+ .filter(Boolean)];
23
+ }
24
+
25
+ export function validationLeaseRecord(state, id, leaseDigest = null) {
26
+ return validationLeaseRecords(state).find((lease) => lease.id === id
27
+ && (leaseDigest === null || lease.digest === leaseDigest)) || null;
28
+ }
29
+
30
+ export function validationLeaseChain(state, candidate) {
31
+ const canary = candidate?.promotion?.mode === "outcome-canary" ? candidate.promotion.canary : null;
32
+ if (!canary?.validationLeaseId || !canary.validationLeaseDigest) return [];
33
+ const chain = [];
34
+ const seen = new Set();
35
+ let lease = validationLeaseRecord(state, canary.validationLeaseId, canary.validationLeaseDigest);
36
+ while (lease && !seen.has(lease.id)) {
37
+ seen.add(lease.id);
38
+ chain.push(lease);
39
+ lease = lease.schema === "agentspine.learning-validation/v1" ? null
40
+ : validationLeaseRecord(state, lease.predecessorValidation.id, lease.predecessorValidation.digest);
41
+ }
42
+ return chain;
43
+ }
44
+
45
+ export function validationRevocationPayload({ id, learningId, evaluationId, evaluationDigest,
46
+ evaluatorBindingDigest, validationLeaseId, validationLeaseDigest, targetDigest, scopeDigest,
47
+ reasonCode, reasonDigest, revokedAt }) {
48
+ return {
49
+ schema: "agentspine.learning-validation-revocation/v1",
50
+ id,
51
+ learningId,
52
+ evaluationId,
53
+ evaluationDigest,
54
+ evaluatorBindingDigest,
55
+ validationLeaseId,
56
+ validationLeaseDigest,
57
+ targetDigest,
58
+ scopeDigest,
59
+ reasonCode,
60
+ reasonDigest,
61
+ revokedAt,
62
+ authority: "context-only"
63
+ };
64
+ }
65
+
66
+ export function storedValidationRevocationStructure(receipt) {
67
+ if (!receipt || typeof receipt !== "object" || Array.isArray(receipt)) return false;
68
+ const payload = validationRevocationPayload(receipt);
69
+ return receipt.schema === "agentspine.learning-validation-revocation/v1"
70
+ && Object.keys(receipt).length === 15
71
+ && Object.keys(receipt).every((field) => ["schema", "id", "learningId", "evaluationId",
72
+ "evaluationDigest", "evaluatorBindingDigest", "validationLeaseId", "validationLeaseDigest",
73
+ "targetDigest", "scopeDigest", "reasonCode", "reasonDigest", "revokedAt", "authority", "digest"].includes(field))
74
+ && ID_RE.test(receipt.id || "") && ID_RE.test(receipt.learningId || "")
75
+ && ID_RE.test(receipt.evaluationId || "") && DIGEST_RE.test(receipt.evaluationDigest || "")
76
+ && DIGEST_RE.test(receipt.evaluatorBindingDigest || "")
77
+ && ID_RE.test(receipt.validationLeaseId || "") && DIGEST_RE.test(receipt.validationLeaseDigest || "")
78
+ && DIGEST_RE.test(receipt.targetDigest || "") && DIGEST_RE.test(receipt.scopeDigest || "")
79
+ && VALIDATION_REVOCATION_REASONS.has(receipt.reasonCode)
80
+ && DIGEST_RE.test(receipt.reasonDigest || "") && Number.isFinite(new Date(receipt.revokedAt).getTime())
81
+ && receipt.authority === "context-only" && receipt.digest === digest(payload);
82
+ }
83
+
84
+ export function validationRevocationMatchesState(state, receipt) {
85
+ const candidate = state.candidates.find((entry) => entry.id === receipt.learningId);
86
+ const evaluation = state.evaluations.find((entry) => entry.id === receipt.evaluationId
87
+ && entry.digest === receipt.evaluationDigest);
88
+ const binding = state.evaluationBindings.find((entry) => entry.evaluationId === receipt.evaluationId
89
+ && entry.evaluationDigest === receipt.evaluationDigest);
90
+ const lease = validationLeaseRecord(state, receipt.validationLeaseId, receipt.validationLeaseDigest);
91
+ return Boolean(candidate && evaluation && binding && lease && storedValidationLeaseStructure(lease)
92
+ && evaluation.learningId === candidate.id && lease.learningId === candidate.id
93
+ && lease.evaluationId === evaluation.id && lease.evaluationDigest === evaluation.digest
94
+ && lease.evaluatorRegistryBindingDigest === binding.digest
95
+ && receipt.evaluatorBindingDigest === binding.digest
96
+ && learningTargetForCandidate(candidate).digest === receipt.targetDigest
97
+ && digest(lease.scope) === receipt.scopeDigest && exactScope(lease.scope, evaluation.scope));
98
+ }
99
+
100
+ export function revokedValidation(state, validationLeaseId) {
101
+ return state.validationRevocations.find((receipt) => receipt.validationLeaseId === validationLeaseId) || null;
102
+ }
103
+
104
+ export function revokedValidationForCandidate(state, candidate) {
105
+ const chain = validationLeaseChain(state, candidate);
106
+ if (!chain.length) return null;
107
+ return state.validationRevocations.find((receipt) => receipt.learningId === candidate.id
108
+ && chain.some((lease) => lease.id === receipt.validationLeaseId
109
+ && lease.digest === receipt.validationLeaseDigest)) || null;
110
+ }
111
+
112
+ export function validationLeaseState(state, candidate, timestamp) {
113
+ const canary = candidate?.promotion?.mode === "outcome-canary" ? candidate.promotion.canary : null;
114
+ if (candidate?.status !== "accepted" || canary?.status !== "validated") {
115
+ return { status: "not-applicable", lease: null, evaluation: null };
116
+ }
117
+ const evaluation = state.evaluations.find((entry) => entry.id === canary.evaluationId
118
+ && entry.learningId === candidate.id && entry.digest === canary.evaluationDigest) || null;
119
+ if (!evaluation) return { status: "missing-evaluation", lease: null, evaluation: null };
120
+ if (revokedEvaluation(state, evaluation.id)) {
121
+ return { status: "revoked-evaluation", lease: null, evaluation };
122
+ }
123
+ if (revokedEvidenceSourceAttestation(state, evaluation.id)) {
124
+ return { status: "revoked-evidence-source-attestation", lease: null, evaluation };
125
+ }
126
+ if (!REGISTRY_BOUND_EVALUATIONS.has(evaluation.schema)) {
127
+ return new Date(canary.expiresAt).getTime() < new Date(timestamp).getTime()
128
+ ? { status: "expired", lease: null, evaluation }
129
+ : { status: "legacy", lease: null, evaluation };
130
+ }
131
+ const lease = state.validationLeases.find((entry) => entry.id === canary.validationLeaseId
132
+ && entry.digest === canary.validationLeaseDigest && entry.learningId === candidate.id
133
+ && entry.evaluationId === evaluation.id && entry.evaluationDigest === evaluation.digest) || null;
134
+ if (!lease) return { status: "missing", lease: null, evaluation };
135
+ if (revokedValidationForCandidate(state, candidate)) {
136
+ return { status: "revoked-validation", lease, evaluation };
137
+ }
138
+ const binding = activeEvaluationBinding(state, evaluation);
139
+ if (!binding || binding.digest !== lease.evaluatorRegistryBindingDigest) {
140
+ return { status: "revoked", lease, evaluation };
141
+ }
142
+ if (new Date(lease.expiresAt).getTime() <= new Date(timestamp).getTime()
143
+ || lease.expiresAt !== canary.expiresAt) return { status: "expired", lease, evaluation };
144
+ return { status: "active", lease, evaluation };
145
+ }
146
+
147
+ export function initialTrialTimeout(state, candidate, timestamp) {
148
+ const canary = candidate?.promotion?.mode === "outcome-canary" ? candidate.promotion.canary : null;
149
+ const evaluation = state.evaluations.find((entry) => entry.id === canary?.evaluationId
150
+ && entry.digest === canary?.evaluationDigest && entry.learningId === candidate?.id);
151
+ if (!DEADLINE_BOUND_EVALUATIONS.has(evaluation?.schema)) return null;
152
+ const applications = state.applications.filter((entry) => entry.schema === "agentspine.learning-application/v7"
153
+ && entry.learningId === candidate.id && entry.initialAdmission.evaluationId === evaluation.id
154
+ && entry.initialAdmission.evaluationDigest === evaluation.digest)
155
+ .sort((a, b) => a.initialAdmission.slot - b.initialAdmission.slot);
156
+ for (const application of applications) {
157
+ const delivery = state.deliveries.find((entry) => entry.applicationId === application.id);
158
+ if (!delivery && new Date(timestamp).getTime() > new Date(application.deliveryExpiresAt).getTime()) {
159
+ return { evaluation, application, failure: "delivery-timeout", deadline: application.deliveryExpiresAt };
160
+ }
161
+ const outcome = state.outcomes.find((entry) => entry.applicationId === application.id
162
+ && entry.evaluationId === evaluation.id && entry.phase === "after");
163
+ if (delivery && !outcome && new Date(timestamp).getTime() > new Date(application.outcomeExpiresAt).getTime()) {
164
+ return { evaluation, application, failure: "outcome-timeout", deadline: application.outcomeExpiresAt };
165
+ }
166
+ }
167
+ return null;
168
+ }
169
+
170
+ export function canaryValidity(state, candidate, timestamp) {
171
+ const canary = candidate?.promotion?.mode === "outcome-canary" ? candidate.promotion.canary : null;
172
+ const evidenceRevocation = revokedEvidence(state, candidate);
173
+ if (candidate?.status === "accepted" && evidenceRevocation) {
174
+ return { status: "revoked-evidence", canary, evaluation: null, lease: null, evidenceRevocation };
175
+ }
176
+ const evaluationRevocation = revokedEvaluationForCandidate(state, candidate);
177
+ if (candidate?.status === "accepted" && evaluationRevocation) {
178
+ return { status: "revoked-evaluation", canary, evaluation: null, lease: null, evaluationRevocation };
179
+ }
180
+ const evidenceSourceAttestationRevocation = revokedEvidenceSourceAttestationForCandidate(state, candidate);
181
+ if (candidate?.status === "accepted" && evidenceSourceAttestationRevocation) {
182
+ return { status: "revoked-evidence-source-attestation", canary, evaluation: null, lease: null,
183
+ evidenceSourceAttestationRevocation };
184
+ }
185
+ const validationRevocation = revokedValidationForCandidate(state, candidate);
186
+ if (candidate?.status === "accepted" && validationRevocation) {
187
+ return { status: "revoked-validation", canary, evaluation: null, lease: null, validationRevocation };
188
+ }
189
+ const measurementRevocation = revokedMeasurementForCandidate(state, candidate);
190
+ if (candidate?.status === "accepted" && measurementRevocation) {
191
+ return { status: "revoked-measurement", canary, evaluation: null, lease: null, measurementRevocation };
192
+ }
193
+ const applicationRevocation = revokedApplicationForCandidate(state, candidate);
194
+ if (candidate?.status === "accepted" && applicationRevocation) {
195
+ return { status: "revoked-application", canary, evaluation: null, lease: null, applicationRevocation };
196
+ }
197
+ const deliveryRevocation = revokedDeliveryForCandidate(state, candidate);
198
+ if (candidate?.status === "accepted" && deliveryRevocation) {
199
+ return { status: "revoked-delivery", canary, evaluation: null, lease: null, deliveryRevocation };
200
+ }
201
+ const outcomeRevocation = revokedOutcomeForCandidate(state, candidate);
202
+ if (candidate?.status === "accepted" && outcomeRevocation) {
203
+ return { status: "revoked-outcome", canary, evaluation: null, lease: null, outcomeRevocation };
204
+ }
205
+ if (candidate?.status !== "accepted" || !["active", "validated"].includes(canary?.status)) {
206
+ return { status: "not-applicable", canary, evaluation: null, lease: null };
207
+ }
208
+ if (new Date(canary.expiresAt).getTime() <= new Date(timestamp).getTime()) {
209
+ return { status: canary.status === "validated" ? "stale-validated" : "stale-active",
210
+ canary, evaluation: null, lease: null };
211
+ }
212
+ const evaluation = state.evaluations.find((entry) => entry.id === canary.evaluationId
213
+ && entry.learningId === candidate.id && entry.digest === canary.evaluationDigest) || null;
214
+ if (!evaluation || new Date(evaluation.expiresAt).getTime() <= new Date(timestamp).getTime()) {
215
+ return { status: canary.status === "validated" ? "stale-validated" : "stale-active",
216
+ canary, evaluation, lease: null };
217
+ }
218
+ if (REGISTRY_BOUND_EVALUATIONS.has(evaluation.schema)
219
+ && !activeEvaluationBinding(state, evaluation)) {
220
+ return { status: canary.status === "validated" ? "revoked-validated" : "revoked-active",
221
+ canary, evaluation, lease: null };
222
+ }
223
+ const failedTrial = initialTrialTimeout(state, candidate, timestamp);
224
+ if (failedTrial) return { status: "failed-initial-trial", canary, evaluation, lease: null, failedTrial };
225
+ if (canary.status === "validated"
226
+ && REGISTRY_BOUND_EVALUATIONS.has(evaluation.schema)) {
227
+ const validation = validationLeaseState(state, candidate, timestamp);
228
+ if (validation.status !== "active") return { ...validation, status: validation.status === "missing"
229
+ ? "unproven-validated" : `${validation.status}-validated`, canary };
230
+ return { status: "current-validated", canary, evaluation, lease: validation.lease };
231
+ }
232
+ return { status: canary.status === "validated" ? "legacy-validated" : "current-active",
233
+ canary, evaluation, lease: null };
234
+ }