@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.
- package/CHANGELOG.md +29 -0
- package/dist/types/append-only-mode.d.ts +31 -0
- package/dist/types/capability/rule.d.ts +8 -0
- package/dist/types/cli/fixture-report.d.ts +20 -0
- package/dist/types/cli/fixture-report.test.d.ts +1 -0
- package/dist/types/cli/nofile-limit.d.ts +11 -0
- package/dist/types/cli/update-cli.d.ts +22 -0
- package/dist/types/config/settings-schema.d.ts +105 -1
- package/dist/types/discovery/index.d.ts +1 -0
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/export/ttsr.d.ts +14 -3
- package/dist/types/gjc-runtime/deep-interview-state.d.ts +5 -2
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +7 -0
- package/dist/types/gjc-runtime/state-writer.d.ts +3 -0
- package/dist/types/gjc-runtime/team-runtime.d.ts +26 -0
- package/dist/types/gjc-runtime/ultragoal-receipt-freshness.d.ts +42 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +119 -13
- package/dist/types/goals/runtime.d.ts +1 -0
- package/dist/types/hooks/native-skill-hook.d.ts +12 -0
- package/dist/types/modes/components/assistant-message.d.ts +5 -1
- package/dist/types/modes/components/status-line.d.ts +9 -0
- package/dist/types/modes/controllers/command-controller.d.ts +1 -0
- package/dist/types/modes/controllers/event-controller.d.ts +5 -0
- package/dist/types/modes/interactive-mode.d.ts +2 -0
- package/dist/types/modes/rpc/rpc-types.d.ts +14 -0
- package/dist/types/modes/shared/agent-wire/event-contract.d.ts +19 -0
- package/dist/types/modes/shared/agent-wire/event-envelope.d.ts +23 -1
- package/dist/types/modes/types.d.ts +1 -0
- package/dist/types/modes/utils/ui-helpers.d.ts +1 -0
- package/dist/types/notifications/config.d.ts +6 -0
- package/dist/types/notifications/telegram-daemon.d.ts +8 -0
- package/dist/types/notifications/threaded-render.d.ts +6 -0
- package/dist/types/sdk.d.ts +3 -0
- package/dist/types/session/agent-session.d.ts +43 -1
- package/dist/types/session/file-mention-pruning.d.ts +8 -0
- package/dist/types/session/messages.d.ts +4 -0
- package/dist/types/session/session-manager.d.ts +17 -1
- package/dist/types/skill-state/active-state.d.ts +18 -1
- package/dist/types/system-prompt.d.ts +18 -2
- package/dist/types/task/executor.d.ts +1 -0
- package/dist/types/task/fork-context-budget.d.ts +7 -0
- package/dist/types/task/token-log.d.ts +30 -0
- package/dist/types/task/token-log.test.d.ts +1 -0
- package/dist/types/tools/bisect.d.ts +81 -0
- package/dist/types/tools/index.d.ts +1 -0
- package/dist/types/tools/search.d.ts +2 -2
- package/dist/types/utils/file-mentions.d.ts +14 -0
- package/dist/types/utils/git.d.ts +32 -0
- package/dist/types/web/scrapers/youtube.d.ts +8 -0
- package/package.json +7 -7
- package/src/append-only-mode.ts +58 -0
- package/src/autoresearch/prompt.md +1 -1
- package/src/autoresearch/tools/log-experiment.ts +1 -1
- package/src/capability/rule.ts +9 -0
- package/src/cli/config-cli.ts +27 -3
- package/src/cli/fixture-report.test.ts +230 -0
- package/src/cli/fixture-report.ts +171 -0
- package/src/cli/nofile-limit.ts +48 -0
- package/src/cli/update-cli.ts +85 -9
- package/src/cli.ts +26 -0
- package/src/commands/contribution-prep.ts +1 -1
- package/src/config/settings-schema.ts +68 -1
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +12 -13
- package/src/defaults/gjc/skills/ralplan/SKILL.md +30 -93
- package/src/defaults/gjc/skills/team/SKILL.md +16 -13
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +59 -4
- package/src/defaults/gjc/skills/ultragoal/pipeline-validation-contracts.md +30 -0
- package/src/defaults/gjc-defaults.ts +9 -0
- package/src/discovery/helpers.ts +10 -0
- package/src/discovery/index.ts +1 -0
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +60 -2
- package/src/export/ttsr.ts +96 -18
- package/src/extensibility/extensions/runner.ts +181 -219
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +156 -44
- package/src/gjc-runtime/deep-interview-state.ts +31 -2
- package/src/gjc-runtime/session-state-sidecar.ts +208 -13
- package/src/gjc-runtime/state-writer.ts +15 -0
- package/src/gjc-runtime/team-runtime.ts +164 -15
- package/src/gjc-runtime/ultragoal-guard.ts +55 -66
- package/src/gjc-runtime/ultragoal-receipt-freshness.ts +315 -0
- package/src/gjc-runtime/ultragoal-runtime.ts +1593 -143
- package/src/gjc-runtime/workflow-manifest.generated.json +113 -2
- package/src/gjc-runtime/workflow-manifest.ts +74 -1
- package/src/goals/runtime.ts +10 -2
- package/src/hindsight/backend.ts +1 -3
- package/src/hooks/native-skill-hook.ts +95 -4
- package/src/internal-urls/docs-index.generated.ts +14 -5
- package/src/main.ts +43 -2
- package/src/memories/index.ts +12 -1
- package/src/modes/components/assistant-message.ts +241 -82
- package/src/modes/components/custom-editor.ts +1 -0
- package/src/modes/components/status-line.ts +167 -54
- package/src/modes/components/tool-execution.ts +44 -19
- package/src/modes/controllers/command-controller.ts +39 -2
- package/src/modes/controllers/event-controller.ts +66 -47
- package/src/modes/controllers/input-controller.ts +1 -1
- package/src/modes/interactive-mode.ts +9 -1
- package/src/modes/rpc/rpc-mode.ts +29 -3
- package/src/modes/rpc/rpc-types.ts +9 -0
- package/src/modes/shared/agent-wire/command-validation.ts +4 -0
- package/src/modes/shared/agent-wire/event-contract.ts +22 -0
- package/src/modes/shared/agent-wire/event-envelope.ts +82 -0
- package/src/modes/shared/agent-wire/scopes.ts +1 -0
- package/src/modes/types.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +17 -4
- package/src/notifications/config.ts +16 -4
- package/src/notifications/index.ts +78 -5
- package/src/notifications/telegram-daemon-cli.ts +2 -2
- package/src/notifications/telegram-daemon-control.ts +2 -2
- package/src/notifications/telegram-daemon.ts +85 -8
- package/src/notifications/threaded-render.ts +17 -1
- package/src/prompts/agents/architect.md +19 -10
- package/src/prompts/agents/critic.md +16 -18
- package/src/prompts/agents/executor.md +14 -11
- package/src/prompts/agents/planner.md +18 -13
- package/src/prompts/ci-green-request.md +3 -8
- package/src/prompts/goals/goal-continuation.md +0 -5
- package/src/prompts/goals/goal-mode-active.md +0 -5
- package/src/prompts/system/agent-creation-architect.md +2 -2
- package/src/prompts/system/plan-mode-active.md +1 -1
- package/src/prompts/system/plan-mode-subagent.md +3 -3
- package/src/prompts/system/project-prompt.md +0 -13
- package/src/prompts/system/subagent-system-prompt.md +3 -2
- package/src/prompts/system/system-prompt.md +26 -19
- package/src/prompts/system/volatile-project-context.md +10 -0
- package/src/prompts/system/web-search.md +4 -7
- package/src/prompts/tools/apply-patch.md +6 -0
- package/src/prompts/tools/ast-edit.md +1 -0
- package/src/prompts/tools/bash.md +1 -1
- package/src/prompts/tools/bisect.md +22 -0
- package/src/prompts/tools/browser.md +1 -1
- package/src/prompts/tools/cron.md +0 -2
- package/src/prompts/tools/eval.md +2 -2
- package/src/prompts/tools/find.md +1 -1
- package/src/prompts/tools/github.md +6 -6
- package/src/prompts/tools/goal.md +1 -3
- package/src/prompts/tools/image-gen.md +12 -5
- package/src/prompts/tools/irc.md +5 -4
- package/src/prompts/tools/job.md +2 -2
- package/src/prompts/tools/lsp.md +1 -1
- package/src/prompts/tools/monitor.md +2 -2
- package/src/prompts/tools/patch.md +2 -2
- package/src/prompts/tools/replace.md +2 -21
- package/src/prompts/tools/rewind.md +1 -1
- package/src/prompts/tools/search-tool-bm25.md +1 -11
- package/src/prompts/tools/search.md +1 -1
- package/src/prompts/tools/skill.md +3 -2
- package/src/prompts/tools/ssh.md +2 -1
- package/src/prompts/tools/subagent.md +2 -2
- package/src/prompts/tools/task.md +4 -5
- package/src/prompts/tools/todo-write.md +1 -1
- package/src/prompts/tools/vim.md +1 -0
- package/src/prompts/tools/web-search.md +2 -0
- package/src/prompts/tools/write.md +8 -2
- package/src/sdk.ts +32 -22
- package/src/session/agent-session.ts +732 -107
- package/src/session/file-mention-pruning.ts +64 -0
- package/src/session/messages.ts +4 -0
- package/src/session/session-manager.ts +59 -2
- package/src/skill-state/active-state.ts +141 -5
- package/src/skill-state/deep-interview-mutation-guard.ts +67 -3
- package/src/slash-commands/builtin-registry.ts +65 -0
- package/src/system-prompt.ts +57 -16
- package/src/task/executor.ts +125 -20
- package/src/task/fork-context-advisory.ts +2 -7
- package/src/task/fork-context-budget.ts +9 -0
- package/src/task/index.ts +4 -3
- package/src/task/output-manager.ts +4 -2
- package/src/task/token-log.test.ts +188 -0
- package/src/task/token-log.ts +179 -0
- package/src/tools/bisect.ts +367 -0
- package/src/tools/index.ts +3 -0
- package/src/tools/output-meta.ts +116 -4
- package/src/tools/search.ts +8 -2
- package/src/tools/skill.ts +15 -0
- package/src/utils/file-mentions.ts +39 -7
- package/src/utils/git.ts +73 -0
- 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
|
+
}
|