@gajae-code/coding-agent 0.8.2 → 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 (179) hide show
  1. package/CHANGELOG.md +29 -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/fixture-report.d.ts +20 -0
  5. package/dist/types/cli/fixture-report.test.d.ts +1 -0
  6. package/dist/types/cli/nofile-limit.d.ts +11 -0
  7. package/dist/types/cli/update-cli.d.ts +22 -0
  8. package/dist/types/config/settings-schema.d.ts +105 -1
  9. package/dist/types/discovery/index.d.ts +1 -0
  10. package/dist/types/export/html/template.generated.d.ts +1 -1
  11. package/dist/types/export/ttsr.d.ts +14 -3
  12. package/dist/types/gjc-runtime/deep-interview-state.d.ts +5 -2
  13. package/dist/types/gjc-runtime/session-state-sidecar.d.ts +7 -0
  14. package/dist/types/gjc-runtime/state-writer.d.ts +3 -0
  15. package/dist/types/gjc-runtime/team-runtime.d.ts +26 -0
  16. package/dist/types/gjc-runtime/ultragoal-receipt-freshness.d.ts +42 -0
  17. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +119 -13
  18. package/dist/types/goals/runtime.d.ts +1 -0
  19. package/dist/types/hooks/native-skill-hook.d.ts +12 -0
  20. package/dist/types/modes/components/assistant-message.d.ts +5 -1
  21. package/dist/types/modes/components/status-line.d.ts +9 -0
  22. package/dist/types/modes/controllers/command-controller.d.ts +1 -0
  23. package/dist/types/modes/controllers/event-controller.d.ts +5 -0
  24. package/dist/types/modes/interactive-mode.d.ts +2 -0
  25. package/dist/types/modes/rpc/rpc-types.d.ts +14 -0
  26. package/dist/types/modes/shared/agent-wire/event-contract.d.ts +19 -0
  27. package/dist/types/modes/shared/agent-wire/event-envelope.d.ts +23 -1
  28. package/dist/types/modes/types.d.ts +1 -0
  29. package/dist/types/modes/utils/ui-helpers.d.ts +1 -0
  30. package/dist/types/notifications/config.d.ts +6 -0
  31. package/dist/types/notifications/telegram-daemon.d.ts +8 -0
  32. package/dist/types/notifications/threaded-render.d.ts +6 -0
  33. package/dist/types/sdk.d.ts +3 -0
  34. package/dist/types/session/agent-session.d.ts +43 -1
  35. package/dist/types/session/file-mention-pruning.d.ts +8 -0
  36. package/dist/types/session/messages.d.ts +4 -0
  37. package/dist/types/session/session-manager.d.ts +17 -1
  38. package/dist/types/skill-state/active-state.d.ts +18 -1
  39. package/dist/types/system-prompt.d.ts +18 -2
  40. package/dist/types/task/executor.d.ts +1 -0
  41. package/dist/types/task/fork-context-budget.d.ts +7 -0
  42. package/dist/types/task/token-log.d.ts +30 -0
  43. package/dist/types/task/token-log.test.d.ts +1 -0
  44. package/dist/types/tools/bisect.d.ts +81 -0
  45. package/dist/types/tools/index.d.ts +1 -0
  46. package/dist/types/tools/search.d.ts +2 -2
  47. package/dist/types/utils/file-mentions.d.ts +14 -0
  48. package/dist/types/utils/git.d.ts +32 -0
  49. package/dist/types/web/scrapers/youtube.d.ts +8 -0
  50. package/package.json +7 -7
  51. package/src/append-only-mode.ts +58 -0
  52. package/src/autoresearch/prompt.md +1 -1
  53. package/src/autoresearch/tools/log-experiment.ts +1 -1
  54. package/src/capability/rule.ts +9 -0
  55. package/src/cli/config-cli.ts +27 -3
  56. package/src/cli/fixture-report.test.ts +230 -0
  57. package/src/cli/fixture-report.ts +171 -0
  58. package/src/cli/nofile-limit.ts +48 -0
  59. package/src/cli/update-cli.ts +85 -9
  60. package/src/cli.ts +26 -0
  61. package/src/commands/contribution-prep.ts +1 -1
  62. package/src/config/settings-schema.ts +68 -1
  63. package/src/defaults/gjc/skills/deep-interview/SKILL.md +12 -13
  64. package/src/defaults/gjc/skills/ralplan/SKILL.md +30 -93
  65. package/src/defaults/gjc/skills/team/SKILL.md +16 -13
  66. package/src/defaults/gjc/skills/ultragoal/SKILL.md +59 -4
  67. package/src/defaults/gjc/skills/ultragoal/pipeline-validation-contracts.md +30 -0
  68. package/src/defaults/gjc-defaults.ts +9 -0
  69. package/src/discovery/helpers.ts +10 -0
  70. package/src/discovery/index.ts +1 -0
  71. package/src/export/html/template.generated.ts +1 -1
  72. package/src/export/html/template.js +60 -2
  73. package/src/export/ttsr.ts +96 -18
  74. package/src/extensibility/extensions/runner.ts +181 -219
  75. package/src/extensibility/gjc-plugins/runtime-adapters.ts +156 -44
  76. package/src/gjc-runtime/deep-interview-state.ts +31 -2
  77. package/src/gjc-runtime/session-state-sidecar.ts +208 -13
  78. package/src/gjc-runtime/state-writer.ts +15 -0
  79. package/src/gjc-runtime/team-runtime.ts +164 -15
  80. package/src/gjc-runtime/ultragoal-guard.ts +55 -66
  81. package/src/gjc-runtime/ultragoal-receipt-freshness.ts +315 -0
  82. package/src/gjc-runtime/ultragoal-runtime.ts +1593 -143
  83. package/src/gjc-runtime/workflow-manifest.generated.json +113 -2
  84. package/src/gjc-runtime/workflow-manifest.ts +74 -1
  85. package/src/goals/runtime.ts +10 -2
  86. package/src/hindsight/backend.ts +1 -3
  87. package/src/hooks/native-skill-hook.ts +95 -4
  88. package/src/internal-urls/docs-index.generated.ts +14 -5
  89. package/src/main.ts +43 -2
  90. package/src/memories/index.ts +12 -1
  91. package/src/modes/components/assistant-message.ts +241 -82
  92. package/src/modes/components/custom-editor.ts +1 -0
  93. package/src/modes/components/status-line.ts +167 -54
  94. package/src/modes/components/tool-execution.ts +44 -19
  95. package/src/modes/controllers/command-controller.ts +39 -2
  96. package/src/modes/controllers/event-controller.ts +66 -47
  97. package/src/modes/controllers/input-controller.ts +1 -1
  98. package/src/modes/interactive-mode.ts +9 -1
  99. package/src/modes/rpc/rpc-mode.ts +29 -3
  100. package/src/modes/rpc/rpc-types.ts +9 -0
  101. package/src/modes/shared/agent-wire/command-validation.ts +4 -0
  102. package/src/modes/shared/agent-wire/event-contract.ts +22 -0
  103. package/src/modes/shared/agent-wire/event-envelope.ts +82 -0
  104. package/src/modes/shared/agent-wire/scopes.ts +1 -0
  105. package/src/modes/types.ts +1 -0
  106. package/src/modes/utils/ui-helpers.ts +17 -4
  107. package/src/notifications/config.ts +16 -4
  108. package/src/notifications/index.ts +78 -5
  109. package/src/notifications/telegram-daemon-cli.ts +2 -2
  110. package/src/notifications/telegram-daemon-control.ts +2 -2
  111. package/src/notifications/telegram-daemon.ts +85 -8
  112. package/src/notifications/threaded-render.ts +17 -1
  113. package/src/prompts/agents/architect.md +19 -10
  114. package/src/prompts/agents/critic.md +16 -18
  115. package/src/prompts/agents/executor.md +14 -11
  116. package/src/prompts/agents/planner.md +18 -13
  117. package/src/prompts/ci-green-request.md +3 -8
  118. package/src/prompts/goals/goal-continuation.md +0 -5
  119. package/src/prompts/goals/goal-mode-active.md +0 -5
  120. package/src/prompts/system/agent-creation-architect.md +2 -2
  121. package/src/prompts/system/plan-mode-active.md +1 -1
  122. package/src/prompts/system/plan-mode-subagent.md +3 -3
  123. package/src/prompts/system/project-prompt.md +0 -13
  124. package/src/prompts/system/subagent-system-prompt.md +3 -2
  125. package/src/prompts/system/system-prompt.md +26 -19
  126. package/src/prompts/system/volatile-project-context.md +10 -0
  127. package/src/prompts/system/web-search.md +4 -7
  128. package/src/prompts/tools/apply-patch.md +6 -0
  129. package/src/prompts/tools/ast-edit.md +1 -0
  130. package/src/prompts/tools/bash.md +1 -1
  131. package/src/prompts/tools/bisect.md +22 -0
  132. package/src/prompts/tools/browser.md +1 -1
  133. package/src/prompts/tools/cron.md +0 -2
  134. package/src/prompts/tools/eval.md +2 -2
  135. package/src/prompts/tools/find.md +1 -1
  136. package/src/prompts/tools/github.md +6 -6
  137. package/src/prompts/tools/goal.md +1 -3
  138. package/src/prompts/tools/image-gen.md +12 -5
  139. package/src/prompts/tools/irc.md +5 -4
  140. package/src/prompts/tools/job.md +2 -2
  141. package/src/prompts/tools/lsp.md +1 -1
  142. package/src/prompts/tools/monitor.md +2 -2
  143. package/src/prompts/tools/patch.md +2 -2
  144. package/src/prompts/tools/replace.md +2 -21
  145. package/src/prompts/tools/rewind.md +1 -1
  146. package/src/prompts/tools/search-tool-bm25.md +1 -11
  147. package/src/prompts/tools/search.md +1 -1
  148. package/src/prompts/tools/skill.md +3 -2
  149. package/src/prompts/tools/ssh.md +2 -1
  150. package/src/prompts/tools/subagent.md +2 -2
  151. package/src/prompts/tools/task.md +4 -5
  152. package/src/prompts/tools/todo-write.md +1 -1
  153. package/src/prompts/tools/vim.md +1 -0
  154. package/src/prompts/tools/web-search.md +2 -0
  155. package/src/prompts/tools/write.md +8 -2
  156. package/src/sdk.ts +32 -22
  157. package/src/session/agent-session.ts +732 -107
  158. package/src/session/file-mention-pruning.ts +64 -0
  159. package/src/session/messages.ts +4 -0
  160. package/src/session/session-manager.ts +59 -2
  161. package/src/skill-state/active-state.ts +141 -5
  162. package/src/skill-state/deep-interview-mutation-guard.ts +67 -3
  163. package/src/slash-commands/builtin-registry.ts +65 -0
  164. package/src/system-prompt.ts +57 -16
  165. package/src/task/executor.ts +125 -20
  166. package/src/task/fork-context-advisory.ts +2 -7
  167. package/src/task/fork-context-budget.ts +9 -0
  168. package/src/task/index.ts +4 -3
  169. package/src/task/output-manager.ts +4 -2
  170. package/src/task/token-log.test.ts +188 -0
  171. package/src/task/token-log.ts +179 -0
  172. package/src/tools/bisect.ts +367 -0
  173. package/src/tools/index.ts +3 -0
  174. package/src/tools/output-meta.ts +116 -4
  175. package/src/tools/search.ts +8 -2
  176. package/src/tools/skill.ts +15 -0
  177. package/src/utils/file-mentions.ts +39 -7
  178. package/src/utils/git.ts +73 -0
  179. package/src/web/scrapers/youtube.ts +24 -12
@@ -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
+ }