@gajae-code/coding-agent 0.8.1 → 0.9.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 (253) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/dist/types/append-only-mode.d.ts +31 -0
  3. package/dist/types/capability/rule.d.ts +8 -0
  4. package/dist/types/cli/completion-cli.d.ts +41 -0
  5. package/dist/types/cli/config-cli.d.ts +1 -0
  6. package/dist/types/cli/fixture-report.d.ts +20 -0
  7. package/dist/types/cli/fixture-report.test.d.ts +1 -0
  8. package/dist/types/cli/nofile-limit.d.ts +11 -0
  9. package/dist/types/cli/notify-cli.d.ts +7 -0
  10. package/dist/types/cli/update-cli.d.ts +22 -0
  11. package/dist/types/cli.d.ts +124 -1
  12. package/dist/types/commands/completion.d.ts +29 -0
  13. package/dist/types/commands/config.d.ts +3 -0
  14. package/dist/types/commands/deep-interview.d.ts +3 -0
  15. package/dist/types/config/file-lock.d.ts +7 -6
  16. package/dist/types/config/keybindings.d.ts +2 -2
  17. package/dist/types/config/model-profile-activation.d.ts +6 -0
  18. package/dist/types/config/settings-schema.d.ts +105 -1
  19. package/dist/types/config/settings.d.ts +8 -0
  20. package/dist/types/discovery/index.d.ts +1 -0
  21. package/dist/types/export/html/template.generated.d.ts +1 -1
  22. package/dist/types/export/ttsr.d.ts +14 -3
  23. package/dist/types/extensibility/extensions/runner.d.ts +2 -1
  24. package/dist/types/extensibility/extensions/types.d.ts +8 -0
  25. package/dist/types/gjc-runtime/deep-interview-ambiguity.d.ts +39 -0
  26. package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +10 -1
  27. package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +1 -1
  28. package/dist/types/gjc-runtime/deep-interview-state.d.ts +17 -2
  29. package/dist/types/gjc-runtime/session-state-sidecar.d.ts +7 -0
  30. package/dist/types/gjc-runtime/state-writer.d.ts +3 -0
  31. package/dist/types/gjc-runtime/team-runtime.d.ts +28 -1
  32. package/dist/types/gjc-runtime/ultragoal-receipt-freshness.d.ts +42 -0
  33. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +120 -13
  34. package/dist/types/goals/runtime.d.ts +1 -0
  35. package/dist/types/hooks/native-skill-hook.d.ts +12 -0
  36. package/dist/types/main.d.ts +4 -1
  37. package/dist/types/modes/components/assistant-message.d.ts +5 -1
  38. package/dist/types/modes/components/model-selector.d.ts +4 -0
  39. package/dist/types/modes/components/queued-message-selector.d.ts +12 -0
  40. package/dist/types/modes/components/settings-selector.d.ts +2 -0
  41. package/dist/types/modes/components/status-line.d.ts +9 -0
  42. package/dist/types/modes/controllers/command-controller.d.ts +1 -0
  43. package/dist/types/modes/controllers/event-controller.d.ts +5 -0
  44. package/dist/types/modes/controllers/input-controller.d.ts +1 -3
  45. package/dist/types/modes/interactive-mode.d.ts +2 -0
  46. package/dist/types/modes/print-mode.d.ts +13 -1
  47. package/dist/types/modes/prompt-action-autocomplete.d.ts +1 -1
  48. package/dist/types/modes/rpc/rpc-types.d.ts +14 -0
  49. package/dist/types/modes/shared/agent-wire/event-contract.d.ts +19 -0
  50. package/dist/types/modes/shared/agent-wire/event-envelope.d.ts +23 -1
  51. package/dist/types/modes/types.d.ts +1 -0
  52. package/dist/types/modes/utils/ui-helpers.d.ts +1 -0
  53. package/dist/types/notifications/config.d.ts +14 -1
  54. package/dist/types/notifications/daemon-paths.d.ts +1 -0
  55. package/dist/types/notifications/index.d.ts +6 -3
  56. package/dist/types/notifications/lifecycle-commands.d.ts +11 -3
  57. package/dist/types/notifications/telegram-daemon.d.ts +23 -3
  58. package/dist/types/notifications/telegram-reference.d.ts +0 -5
  59. package/dist/types/notifications/threaded-render.d.ts +6 -0
  60. package/dist/types/runtime-api-key.d.ts +7 -0
  61. package/dist/types/sdk.d.ts +3 -0
  62. package/dist/types/session/agent-session.d.ts +53 -1
  63. package/dist/types/session/agent-storage.d.ts +1 -0
  64. package/dist/types/session/file-mention-pruning.d.ts +8 -0
  65. package/dist/types/session/messages.d.ts +4 -0
  66. package/dist/types/session/session-manager.d.ts +17 -1
  67. package/dist/types/skill-state/active-state.d.ts +18 -1
  68. package/dist/types/system-prompt.d.ts +18 -2
  69. package/dist/types/task/executor.d.ts +3 -0
  70. package/dist/types/task/fork-context-budget.d.ts +7 -0
  71. package/dist/types/task/token-log.d.ts +30 -0
  72. package/dist/types/task/token-log.test.d.ts +1 -0
  73. package/dist/types/tools/bisect.d.ts +81 -0
  74. package/dist/types/tools/computer.d.ts +2 -2
  75. package/dist/types/tools/index.d.ts +1 -0
  76. package/dist/types/tools/search.d.ts +2 -2
  77. package/dist/types/utils/file-mentions.d.ts +14 -0
  78. package/dist/types/utils/git.d.ts +32 -0
  79. package/dist/types/web/scrapers/youtube.d.ts +8 -0
  80. package/dist/types/workflow/workflow-intent-diff.d.ts +8 -2
  81. package/dist/types/workflow/workflow-intent-report-consumer.d.ts +50 -0
  82. package/dist/types/workflow/workflow-intent-report-schema.d.ts +2 -0
  83. package/dist/types/workflow/workflow-intent-report.d.ts +48 -0
  84. package/package.json +7 -7
  85. package/src/append-only-mode.ts +58 -0
  86. package/src/autoresearch/prompt.md +1 -1
  87. package/src/autoresearch/tools/log-experiment.ts +1 -1
  88. package/src/capability/rule.ts +9 -0
  89. package/src/cli/completion-cli.ts +234 -0
  90. package/src/cli/config-cli.ts +61 -9
  91. package/src/cli/fast-help.ts +1 -0
  92. package/src/cli/fixture-report.test.ts +230 -0
  93. package/src/cli/fixture-report.ts +171 -0
  94. package/src/cli/nofile-limit.ts +48 -0
  95. package/src/cli/notify-cli.ts +91 -7
  96. package/src/cli/update-cli.ts +85 -9
  97. package/src/cli.ts +29 -1
  98. package/src/commands/completion.ts +84 -0
  99. package/src/commands/config.ts +2 -0
  100. package/src/commands/contribution-prep.ts +1 -1
  101. package/src/commands/deep-interview.ts +2 -1
  102. package/src/config/file-lock.ts +73 -34
  103. package/src/config/keybindings.ts +2 -2
  104. package/src/config/model-profile-activation.ts +65 -0
  105. package/src/config/model-registry.ts +14 -2
  106. package/src/config/settings-schema.ts +68 -1
  107. package/src/config/settings.ts +22 -0
  108. package/src/coordinator-mcp/server.ts +34 -8
  109. package/src/defaults/gjc/skills/deep-interview/SKILL.md +50 -18
  110. package/src/defaults/gjc/skills/ralplan/SKILL.md +40 -99
  111. package/src/defaults/gjc/skills/team/SKILL.md +17 -14
  112. package/src/defaults/gjc/skills/ultragoal/SKILL.md +59 -4
  113. package/src/defaults/gjc/skills/ultragoal/pipeline-validation-contracts.md +30 -0
  114. package/src/defaults/gjc-defaults.ts +9 -0
  115. package/src/discovery/helpers.ts +10 -0
  116. package/src/discovery/index.ts +1 -0
  117. package/src/export/html/template.generated.ts +1 -1
  118. package/src/export/html/template.js +60 -2
  119. package/src/export/ttsr.ts +96 -18
  120. package/src/extensibility/extensions/runner.ts +183 -219
  121. package/src/extensibility/extensions/types.ts +9 -0
  122. package/src/extensibility/gjc-plugins/runtime-adapters.ts +156 -44
  123. package/src/gjc-runtime/deep-interview-ambiguity.ts +206 -0
  124. package/src/gjc-runtime/deep-interview-recorder.ts +62 -8
  125. package/src/gjc-runtime/deep-interview-runtime.ts +210 -2
  126. package/src/gjc-runtime/deep-interview-state.ts +44 -2
  127. package/src/gjc-runtime/session-state-sidecar.ts +263 -20
  128. package/src/gjc-runtime/state-runtime.ts +12 -2
  129. package/src/gjc-runtime/state-writer.ts +15 -0
  130. package/src/gjc-runtime/team-runtime.ts +187 -15
  131. package/src/gjc-runtime/ultragoal-guard.ts +55 -66
  132. package/src/gjc-runtime/ultragoal-receipt-freshness.ts +315 -0
  133. package/src/gjc-runtime/ultragoal-runtime.ts +1663 -159
  134. package/src/gjc-runtime/workflow-manifest.generated.json +113 -2
  135. package/src/gjc-runtime/workflow-manifest.ts +74 -1
  136. package/src/goals/runtime.ts +10 -2
  137. package/src/hindsight/backend.ts +1 -3
  138. package/src/hooks/native-skill-hook.ts +124 -4
  139. package/src/internal-urls/docs-index.generated.ts +16 -7
  140. package/src/main.ts +95 -43
  141. package/src/memories/index.ts +12 -1
  142. package/src/modes/components/assistant-message.ts +241 -82
  143. package/src/modes/components/custom-editor.ts +1 -0
  144. package/src/modes/components/model-selector.ts +54 -11
  145. package/src/modes/components/queued-message-selector.ts +92 -0
  146. package/src/modes/components/settings-selector.ts +4 -0
  147. package/src/modes/components/status-line.ts +167 -54
  148. package/src/modes/components/tool-execution.ts +44 -19
  149. package/src/modes/components/welcome.ts +160 -44
  150. package/src/modes/controllers/command-controller.ts +40 -4
  151. package/src/modes/controllers/event-controller.ts +66 -47
  152. package/src/modes/controllers/extension-ui-controller.ts +4 -10
  153. package/src/modes/controllers/input-controller.ts +180 -18
  154. package/src/modes/controllers/selector-controller.ts +122 -27
  155. package/src/modes/interactive-mode.ts +51 -16
  156. package/src/modes/print-mode.ts +43 -4
  157. package/src/modes/prompt-action-autocomplete.ts +21 -20
  158. package/src/modes/rpc/rpc-mode.ts +29 -3
  159. package/src/modes/rpc/rpc-types.ts +9 -0
  160. package/src/modes/shared/agent-wire/command-validation.ts +4 -0
  161. package/src/modes/shared/agent-wire/event-contract.ts +22 -0
  162. package/src/modes/shared/agent-wire/event-envelope.ts +82 -0
  163. package/src/modes/shared/agent-wire/scopes.ts +1 -0
  164. package/src/modes/types.ts +1 -0
  165. package/src/modes/utils/hotkeys-markdown.ts +1 -0
  166. package/src/modes/utils/ui-helpers.ts +19 -6
  167. package/src/notifications/config.ts +25 -5
  168. package/src/notifications/daemon-paths.ts +2 -0
  169. package/src/notifications/index.ts +117 -14
  170. package/src/notifications/lifecycle-commands.ts +37 -7
  171. package/src/notifications/lifecycle-orchestrator.ts +19 -6
  172. package/src/notifications/telegram-daemon-cli.ts +2 -2
  173. package/src/notifications/telegram-daemon-control.ts +5 -3
  174. package/src/notifications/telegram-daemon.ts +302 -51
  175. package/src/notifications/telegram-reference.ts +0 -16
  176. package/src/notifications/threaded-render.ts +17 -1
  177. package/src/prompts/agents/architect.md +19 -10
  178. package/src/prompts/agents/critic.md +16 -18
  179. package/src/prompts/agents/executor.md +14 -11
  180. package/src/prompts/agents/planner.md +18 -13
  181. package/src/prompts/ci-green-request.md +3 -8
  182. package/src/prompts/goals/goal-continuation.md +0 -5
  183. package/src/prompts/goals/goal-mode-active.md +0 -5
  184. package/src/prompts/system/agent-creation-architect.md +2 -2
  185. package/src/prompts/system/plan-mode-active.md +1 -1
  186. package/src/prompts/system/plan-mode-subagent.md +3 -3
  187. package/src/prompts/system/project-prompt.md +0 -13
  188. package/src/prompts/system/subagent-system-prompt.md +3 -2
  189. package/src/prompts/system/system-prompt.md +29 -19
  190. package/src/prompts/system/volatile-project-context.md +10 -0
  191. package/src/prompts/system/web-search.md +4 -7
  192. package/src/prompts/tools/apply-patch.md +6 -0
  193. package/src/prompts/tools/ast-edit.md +1 -0
  194. package/src/prompts/tools/bash.md +1 -1
  195. package/src/prompts/tools/bisect.md +22 -0
  196. package/src/prompts/tools/browser.md +1 -1
  197. package/src/prompts/tools/computer.md +2 -2
  198. package/src/prompts/tools/cron.md +0 -2
  199. package/src/prompts/tools/eval.md +2 -2
  200. package/src/prompts/tools/find.md +1 -1
  201. package/src/prompts/tools/github.md +6 -6
  202. package/src/prompts/tools/goal.md +1 -3
  203. package/src/prompts/tools/image-gen.md +12 -5
  204. package/src/prompts/tools/irc.md +5 -4
  205. package/src/prompts/tools/job.md +2 -2
  206. package/src/prompts/tools/lsp.md +1 -1
  207. package/src/prompts/tools/monitor.md +2 -2
  208. package/src/prompts/tools/patch.md +2 -2
  209. package/src/prompts/tools/replace.md +2 -21
  210. package/src/prompts/tools/rewind.md +1 -1
  211. package/src/prompts/tools/search-tool-bm25.md +1 -11
  212. package/src/prompts/tools/search.md +1 -1
  213. package/src/prompts/tools/skill.md +3 -2
  214. package/src/prompts/tools/ssh.md +2 -1
  215. package/src/prompts/tools/subagent.md +2 -2
  216. package/src/prompts/tools/task.md +4 -5
  217. package/src/prompts/tools/todo-write.md +1 -1
  218. package/src/prompts/tools/vim.md +1 -0
  219. package/src/prompts/tools/web-search.md +2 -0
  220. package/src/prompts/tools/write.md +8 -2
  221. package/src/runtime-api-key.ts +16 -0
  222. package/src/sdk.ts +52 -28
  223. package/src/session/agent-session.ts +951 -132
  224. package/src/session/agent-storage.ts +4 -0
  225. package/src/session/file-mention-pruning.ts +64 -0
  226. package/src/session/messages.ts +4 -0
  227. package/src/session/session-manager.ts +103 -4
  228. package/src/skill-state/active-state.ts +141 -5
  229. package/src/skill-state/deep-interview-mutation-guard.ts +67 -3
  230. package/src/slash-commands/builtin-registry.ts +218 -71
  231. package/src/system-prompt.ts +57 -16
  232. package/src/task/executor.ts +132 -24
  233. package/src/task/fork-context-advisory.ts +2 -7
  234. package/src/task/fork-context-budget.ts +9 -0
  235. package/src/task/index.ts +13 -6
  236. package/src/task/output-manager.ts +20 -8
  237. package/src/task/receipt.ts +26 -7
  238. package/src/task/token-log.test.ts +188 -0
  239. package/src/task/token-log.ts +179 -0
  240. package/src/tools/bisect.ts +367 -0
  241. package/src/tools/computer.ts +246 -43
  242. package/src/tools/index.ts +3 -0
  243. package/src/tools/output-meta.ts +116 -4
  244. package/src/tools/search.ts +8 -2
  245. package/src/tools/skill.ts +15 -0
  246. package/src/tools/telegram-send.ts +6 -0
  247. package/src/utils/file-mentions.ts +39 -7
  248. package/src/utils/git.ts +73 -0
  249. package/src/web/scrapers/youtube.ts +24 -12
  250. package/src/workflow/workflow-intent-diff.ts +29 -6
  251. package/src/workflow/workflow-intent-report-consumer.ts +221 -0
  252. package/src/workflow/workflow-intent-report-schema.ts +129 -0
  253. package/src/workflow/workflow-intent-report.ts +135 -0
@@ -0,0 +1,315 @@
1
+ import * as crypto from "node:crypto";
2
+ import type {
3
+ UltragoalCompletionVerification,
4
+ UltragoalGoal,
5
+ UltragoalGoalStatus,
6
+ UltragoalLedgerEvent,
7
+ UltragoalPlan,
8
+ UltragoalReceiptKind,
9
+ } from "./ultragoal-runtime";
10
+
11
+ export type UltragoalReceiptFreshnessDiagnostic = {
12
+ state:
13
+ | "inactive"
14
+ | "unrelated_goal"
15
+ | "active_verified_complete"
16
+ | "active_missing_receipt"
17
+ | "active_stale_receipt"
18
+ | "active_missing_final_receipt"
19
+ | "active_dirty_quality_gate"
20
+ | "active_review_blocked_unrecorded"
21
+ | "active_review_blocked_recorded"
22
+ | "unreadable_fail_closed";
23
+ message: string;
24
+ goalId?: string;
25
+ };
26
+ function stableStructuredValue(value: unknown): unknown {
27
+ if (Array.isArray(value)) return value.map(item => stableStructuredValue(item));
28
+ if (typeof value !== "object" || value === null) return value;
29
+ const record = value as Record<string, unknown>;
30
+ const sorted: Record<string, unknown> = {};
31
+ for (const key of Object.keys(record).sort()) {
32
+ const item = record[key];
33
+ if (item !== undefined) sorted[key] = stableStructuredValue(item);
34
+ }
35
+ return sorted;
36
+ }
37
+
38
+ function hashStructuredValue(value: unknown): string {
39
+ return crypto
40
+ .createHash("sha256")
41
+ .update(JSON.stringify(stableStructuredValue(value)))
42
+ .digest("hex");
43
+ }
44
+
45
+ export function requiredUltragoalGoals(plan: UltragoalPlan): UltragoalGoal[] {
46
+ return plan.goals.filter(goal => goal.status !== "superseded");
47
+ }
48
+
49
+ export function receiptRelevantGoals(
50
+ plan: UltragoalPlan,
51
+ goal: UltragoalGoal,
52
+ receiptKind: UltragoalReceiptKind,
53
+ ): UltragoalGoal[] {
54
+ if (goal.validationBatch?.finalGoalId === goal.id) {
55
+ return goal.validationBatch.memberIds.map(memberId => {
56
+ const member = plan.goals.find(item => item.id === memberId);
57
+ if (!member)
58
+ throw new Error(`validation batch ${goal.validationBatch?.batchId} references missing goal ${memberId}`);
59
+ return member;
60
+ });
61
+ }
62
+ return receiptKind === "final-aggregate" ? requiredUltragoalGoals(plan) : [goal];
63
+ }
64
+
65
+ function ledgerEventId(event: UltragoalLedgerEvent): string | null {
66
+ return typeof event.eventId === "string" && event.eventId.trim().length > 0 ? event.eventId : null;
67
+ }
68
+
69
+ function isReceiptFreshnessBookkeepingEvent(event: UltragoalLedgerEvent): boolean {
70
+ return event.event === "nudge";
71
+ }
72
+
73
+ function latestRelevantLedgerEventId(
74
+ ledger: readonly UltragoalLedgerEvent[],
75
+ relevantGoalIds: readonly string[],
76
+ excludeEventId?: string,
77
+ ): string | null {
78
+ const relevant = new Set(relevantGoalIds);
79
+ for (const event of [...ledger].reverse()) {
80
+ const eventId = ledgerEventId(event);
81
+ if (eventId && eventId === excludeEventId) continue;
82
+ if (isReceiptFreshnessBookkeepingEvent(event)) continue;
83
+ const goalId = typeof event.goalId === "string" ? event.goalId.trim() : "";
84
+ if (goalId && relevant.has(goalId)) return eventId;
85
+ }
86
+ return null;
87
+ }
88
+
89
+ function planSnapshotForReceipt(input: {
90
+ plan: UltragoalPlan;
91
+ goal: UltragoalGoal;
92
+ beforeStatus: UltragoalGoalStatus;
93
+ targetGoalUpdatedAt: string;
94
+ receiptKind: UltragoalReceiptKind;
95
+ }): unknown {
96
+ const targetGoalSnapshot = {
97
+ ...input.goal,
98
+ status: input.beforeStatus,
99
+ updatedAt: input.targetGoalUpdatedAt,
100
+ evidence: undefined,
101
+ completedAt: undefined,
102
+ completionVerification: undefined,
103
+ };
104
+ const goals =
105
+ input.receiptKind === "final-aggregate"
106
+ ? input.plan.goals.map(goal => ({
107
+ ...goal,
108
+ status: goal.id === input.goal.id ? input.beforeStatus : goal.status,
109
+ updatedAt: goal.id === input.goal.id ? input.targetGoalUpdatedAt : goal.updatedAt,
110
+ evidence: goal.id === input.goal.id ? undefined : goal.evidence,
111
+ completedAt: goal.id === input.goal.id ? undefined : goal.completedAt,
112
+ completionVerification: undefined,
113
+ }))
114
+ : [targetGoalSnapshot];
115
+ return {
116
+ version: input.plan.version,
117
+ brief: input.plan.brief,
118
+ gjcGoalMode: input.plan.gjcGoalMode,
119
+ gjcObjective: input.plan.gjcObjective,
120
+ gjcObjectiveAliases: input.plan.gjcObjectiveAliases,
121
+ createdAt: input.plan.createdAt,
122
+ goals,
123
+ };
124
+ }
125
+
126
+ export function computeUltragoalPlanGeneration(input: {
127
+ plan: UltragoalPlan;
128
+ ledger: readonly UltragoalLedgerEvent[];
129
+ goal: UltragoalGoal;
130
+ receiptKind: UltragoalReceiptKind;
131
+ beforeStatus: UltragoalGoalStatus;
132
+ excludeEventId?: string;
133
+ targetGoalUpdatedAt?: string;
134
+ }): {
135
+ planGeneration: string;
136
+ basis: UltragoalCompletionVerification["basis"];
137
+ } {
138
+ const relevantGoals = receiptRelevantGoals(input.plan, input.goal, input.receiptKind);
139
+ const relevantGoalIds = relevantGoals.map(goal => goal.id);
140
+ const targetGoalUpdatedAt = input.targetGoalUpdatedAt ?? input.goal.updatedAt;
141
+ const planHashBeforeCheckpoint = hashStructuredValue(
142
+ planSnapshotForReceipt({
143
+ plan: input.plan,
144
+ goal: input.goal,
145
+ beforeStatus: input.beforeStatus,
146
+ targetGoalUpdatedAt,
147
+ receiptKind: input.receiptKind,
148
+ }),
149
+ );
150
+ const requiredGoalSetHashBeforeCheckpoint = hashStructuredValue(
151
+ relevantGoals.map(goal => ({
152
+ id: goal.id,
153
+ status: goal.id === input.goal.id ? input.beforeStatus : goal.status,
154
+ updatedAt: goal.id === input.goal.id ? targetGoalUpdatedAt : goal.updatedAt,
155
+ })),
156
+ );
157
+ const basis: UltragoalCompletionVerification["basis"] = {
158
+ planHashBeforeCheckpoint,
159
+ latestRelevantLedgerEventIdBeforeCheckpoint: latestRelevantLedgerEventId(
160
+ input.ledger,
161
+ relevantGoalIds,
162
+ input.excludeEventId,
163
+ ),
164
+ goalUpdatedAtBeforeCheckpoint: targetGoalUpdatedAt,
165
+ relevantGoalIdsBeforeCheckpoint: relevantGoalIds,
166
+ requiredGoalSetHashBeforeCheckpoint,
167
+ };
168
+ return { planGeneration: hashStructuredValue(basis), basis };
169
+ }
170
+
171
+ export function findLedgerReceiptEvent(
172
+ ledger: readonly UltragoalLedgerEvent[],
173
+ receipt: UltragoalCompletionVerification,
174
+ ): UltragoalLedgerEvent | null {
175
+ return (
176
+ ledger.find(event => {
177
+ if (event.eventId !== receipt.checkpointLedgerEventId) return false;
178
+ if (event.event !== "goal_checkpointed") return false;
179
+ if (event.goalId !== receipt.goalId) return false;
180
+ const eventReceipt = event.completionVerification as UltragoalCompletionVerification | undefined;
181
+ return (
182
+ event.status === "complete" &&
183
+ eventReceipt?.receiptId === receipt.receiptId &&
184
+ eventReceipt.receiptKind === receipt.receiptKind &&
185
+ eventReceipt.planGeneration === receipt.planGeneration
186
+ );
187
+ }) ?? null
188
+ );
189
+ }
190
+
191
+ export function validateReceiptFreshBase(input: {
192
+ plan: UltragoalPlan;
193
+ ledger: readonly UltragoalLedgerEvent[];
194
+ goal: UltragoalGoal;
195
+ receipt: UltragoalCompletionVerification;
196
+ receiptKind: UltragoalReceiptKind;
197
+ }): UltragoalReceiptFreshnessDiagnostic | null {
198
+ if (
199
+ input.receipt.schemaVersion !== 1 ||
200
+ input.receipt.goalId !== input.goal.id ||
201
+ input.receipt.receiptKind !== input.receiptKind ||
202
+ !input.receipt.planGeneration ||
203
+ !input.receipt.checkpointLedgerEventId
204
+ ) {
205
+ return {
206
+ state: "active_stale_receipt",
207
+ message: `Ultragoal ${input.goal.id} receipt is malformed or stale.`,
208
+ goalId: input.goal.id,
209
+ };
210
+ }
211
+ const event = findLedgerReceiptEvent(input.ledger, input.receipt);
212
+ if (!event)
213
+ return {
214
+ state: "active_stale_receipt",
215
+ message: `Ultragoal ${input.goal.id} receipt ledger event is missing.`,
216
+ goalId: input.goal.id,
217
+ };
218
+ const generation = computeUltragoalPlanGeneration({
219
+ plan: input.plan,
220
+ ledger: input.ledger,
221
+ goal: input.goal,
222
+ receiptKind: input.receiptKind,
223
+ beforeStatus: input.receipt.goalStatusBeforeCheckpoint,
224
+ excludeEventId: input.receipt.checkpointLedgerEventId,
225
+ });
226
+ if (generation.planGeneration !== input.receipt.planGeneration)
227
+ return {
228
+ state: "active_stale_receipt",
229
+ message: `Ultragoal ${input.goal.id} receipt generation is stale.`,
230
+ goalId: input.goal.id,
231
+ };
232
+ if (hashStructuredValue(event.qualityGateJson) !== input.receipt.qualityGateHash)
233
+ return {
234
+ state: "active_dirty_quality_gate",
235
+ message: `Ultragoal ${input.goal.id} receipt quality-gate hash does not match ledger.`,
236
+ goalId: input.goal.id,
237
+ };
238
+ if (input.goal.updatedAt !== input.receipt.verifiedAt)
239
+ return {
240
+ state: "active_stale_receipt",
241
+ message: `Ultragoal ${input.goal.id} receipt target changed after verification.`,
242
+ goalId: input.goal.id,
243
+ };
244
+ return null;
245
+ }
246
+
247
+ export function findFreshBatchCloseReceipt(input: {
248
+ plan: UltragoalPlan;
249
+ ledger: readonly UltragoalLedgerEvent[];
250
+ deferredGoal: UltragoalGoal;
251
+ deferredReceipt: UltragoalCompletionVerification;
252
+ }): UltragoalCompletionVerification | null {
253
+ const batch = input.deferredReceipt.validationBatch;
254
+ if (batch?.role !== "deferred-member") return null;
255
+ const finalGoal = input.plan.goals.find(goal => goal.id === batch.finalGoalId);
256
+ const finalReceipt = finalGoal?.completionVerification;
257
+ if (!finalGoal || finalReceipt?.validationBatch?.role !== "batch-close") return null;
258
+ if (
259
+ finalReceipt.validationBatch.batchId !== batch.batchId ||
260
+ finalReceipt.validationBatch.memberReceiptIds[input.deferredGoal.id] !== input.deferredReceipt.receiptId
261
+ )
262
+ return null;
263
+ const diagnostic = validateReceiptFreshBase({
264
+ plan: input.plan,
265
+ ledger: input.ledger,
266
+ goal: finalGoal,
267
+ receipt: finalReceipt,
268
+ receiptKind: finalReceipt.receiptKind,
269
+ });
270
+ return diagnostic ? null : finalReceipt;
271
+ }
272
+
273
+ export function validateDeferredMemberReceiptFresh(input: {
274
+ plan: UltragoalPlan;
275
+ ledger: readonly UltragoalLedgerEvent[];
276
+ goal: UltragoalGoal;
277
+ receipt: UltragoalCompletionVerification;
278
+ receiptKind: UltragoalReceiptKind;
279
+ requireClose: boolean;
280
+ }): UltragoalReceiptFreshnessDiagnostic {
281
+ const batch = input.receipt.validationBatch;
282
+ if (
283
+ batch?.role !== "deferred-member" ||
284
+ input.goal.validationBatch?.metadataHash !== batch.metadataHash ||
285
+ input.goal.validationBatch.batchId !== batch.batchId
286
+ ) {
287
+ return {
288
+ state: "active_stale_receipt",
289
+ message: `Ultragoal ${input.goal.id} deferred receipt is malformed or stale.`,
290
+ goalId: input.goal.id,
291
+ };
292
+ }
293
+ const base = validateReceiptFreshBase(input);
294
+ if (base) return base;
295
+ if (
296
+ input.requireClose &&
297
+ !findFreshBatchCloseReceipt({
298
+ plan: input.plan,
299
+ ledger: input.ledger,
300
+ deferredGoal: input.goal,
301
+ deferredReceipt: input.receipt,
302
+ })
303
+ ) {
304
+ return {
305
+ state: "active_missing_final_receipt",
306
+ message: `Ultragoal ${input.goal.id} is deferred to validation batch ${batch.batchId} until final goal ${batch.finalGoalId} closes the batch`,
307
+ goalId: input.goal.id,
308
+ };
309
+ }
310
+ return {
311
+ state: "active_verified_complete",
312
+ message: `Ultragoal ${input.goal.id} has a fresh deferred validation batch receipt.`,
313
+ goalId: input.goal.id,
314
+ };
315
+ }