@gajae-code/coding-agent 0.1.1 → 0.1.2
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 +4 -0
- package/dist/types/config/model-registry.d.ts +7 -0
- package/dist/types/gjc-runtime/ultragoal-guard.d.ts +26 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +44 -0
- package/dist/types/goals/tools/goal-tool.d.ts +4 -4
- package/dist/types/hooks/skill-state.d.ts +3 -0
- package/dist/types/modes/components/model-selector.d.ts +4 -4
- package/dist/types/skill-state/deep-interview-mutation-guard.d.ts +28 -0
- package/package.json +11 -7
- package/src/config/model-registry.ts +41 -0
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +30 -30
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +51 -21
- package/src/gjc-runtime/ultragoal-guard.ts +239 -0
- package/src/gjc-runtime/ultragoal-runtime.ts +318 -4
- package/src/goals/tools/goal-tool.ts +10 -4
- package/src/hooks/native-skill-hook.ts +26 -0
- package/src/hooks/skill-state.ts +59 -0
- package/src/main.ts +1 -17
- package/src/modes/components/model-selector.ts +120 -28
- package/src/modes/controllers/selector-controller.ts +16 -3
- package/src/modes/prompt-action-autocomplete.ts +40 -15
- package/src/session/agent-session.ts +31 -1
- package/src/setup/model-onboarding-guidance.ts +5 -3
- package/src/skill-state/deep-interview-mutation-guard.ts +303 -0
- package/src/slash-commands/builtin-registry.ts +130 -11
- package/src/tools/ask.ts +55 -17
- package/src/tools/ast-edit.ts +7 -0
- package/src/tools/bash.ts +2 -1
- package/src/tools/gh.ts +37 -9
- package/src/tools/path-utils.ts +1 -0
|
@@ -65,14 +65,14 @@ Loop until `gjc ultragoal status` reports all goals complete:
|
|
|
65
65
|
4. If no active GJC goal exists, call `create_goal({"objective":"<printed payload objective>"})` with the printed payload. In aggregate mode, if the same aggregate objective is already active, continue the current GJC story without creating a new GJC goal.
|
|
66
66
|
5. Complete the current GJC story only.
|
|
67
67
|
6. Run a completion audit against the story objective and real artifacts/tests.
|
|
68
|
-
7. In aggregate mode, do **not** call `update_goal` for intermediate stories; checkpoint with a fresh `get_goal({})` snapshot whose aggregate objective is still `active`. On the final story only,
|
|
69
|
-
8. Checkpoint the durable ledger with that snapshot.
|
|
70
|
-
`gjc ultragoal checkpoint --goal-id <id> --status complete --evidence "<evidence>" --gjc-goal-json <get_goal-json-or-path>
|
|
68
|
+
7. Before any `--status complete` checkpoint, run the mandatory final cleanup/review gate below. In aggregate mode, do **not** call `update_goal` for intermediate stories; checkpoint with a fresh `get_goal({})` snapshot whose aggregate objective is still `active`. On the final story only, call `update_goal({"status":"complete"})` after the gate is clean, then call `get_goal({})` again for a fresh `complete` snapshot.
|
|
69
|
+
8. Checkpoint the durable ledger with that snapshot. Complete checkpoints require `--quality-gate-json`; the runtime hook rejects closure without a clean architect review:
|
|
70
|
+
`gjc ultragoal checkpoint --goal-id <id> --status complete --evidence "<evidence>" --gjc-goal-json <get_goal-json-or-path> --quality-gate-json <quality-gate-json-or-path>`
|
|
71
71
|
9. If blocked or failed, checkpoint failure:
|
|
72
72
|
`gjc ultragoal checkpoint --goal-id <id> --status failed --evidence "<blocker/evidence>"`
|
|
73
|
-
|
|
73
|
+
11. For legacy per-story completed-goal blockers, preserve the non-terminal blocker with:
|
|
74
74
|
`gjc ultragoal checkpoint --goal-id <id> --status blocked --evidence "<completed legacy GJC goal blocks create_goal in this thread>" --gjc-goal-json <get_goal-json-or-path>`
|
|
75
|
-
|
|
75
|
+
12. Resume failed goals with `gjc ultragoal complete-goals --retry-failed`.
|
|
76
76
|
|
|
77
77
|
## Dynamic steering
|
|
78
78
|
|
|
@@ -120,6 +120,8 @@ If an Ultragoal request has no approved plan or consensus artifact, run `ralplan
|
|
|
120
120
|
|
|
121
121
|
The Ultragoal leader owns `.gjc/ultragoal/goals.json` and `.gjc/ultragoal/ledger.jsonl`. Role agents return implementation/review evidence; they do not checkpoint Ultragoal or mutate goal state.
|
|
122
122
|
|
|
123
|
+
For large subgoals with independent slices, the Ultragoal leader must spawn parallel `executor` subagents instead of doing serial solo work. Split only cleanly separable files/surfaces, give each executor bounded targets and acceptance criteria, and keep checkpoint ownership in the leader. Use `architect` / `critic` review lanes after integration; do not let worker agents mutate `.gjc/ultragoal` or call goal tools.
|
|
124
|
+
|
|
123
125
|
## Use Ultragoal and Team together
|
|
124
126
|
|
|
125
127
|
Use ultragoal and team together for a durable Ultragoal story that benefits from one visible tmux worker session. Ultragoal remains leader-owned: `.gjc/ultragoal/goals.json` stores the story plan and `.gjc/ultragoal/ledger.jsonl` stores checkpoints. Team is the single-worker tmux execution engine and returns task/evidence status to the leader.
|
|
@@ -132,9 +134,9 @@ gjc ultragoal checkpoint --goal-id <id> --status complete --evidence "<team evid
|
|
|
132
134
|
|
|
133
135
|
Workers do not own ultragoal goal state, do not create worker ultragoal ledgers, and do not checkpoint Ultragoal. Team launch remains explicit; Ultragoal does not auto-launch Team and performs no hidden goal mutation.
|
|
134
136
|
|
|
135
|
-
## Mandatory
|
|
137
|
+
## Mandatory completion cleanup and review gate
|
|
136
138
|
|
|
137
|
-
|
|
139
|
+
An ultragoal story cannot be checkpointed `complete` until the active agent has run the quality gate:
|
|
138
140
|
|
|
139
141
|
1. Run targeted verification for the story.
|
|
140
142
|
2. Run a cleanup/refactor review pass on changed files only; if there are no relevant edits, the cleaner still runs and records a passed/no-op report.
|
|
@@ -142,28 +144,56 @@ The final ultragoal story is not complete until the active agent has run the fin
|
|
|
142
144
|
4. Run a final code review pass. Clean means `codeReview.recommendation: "APPROVE"` and `codeReview.architectStatus: "CLEAR"`; `COMMENT`, `WATCH`, `REQUEST CHANGES`, and `BLOCK` are non-clean.
|
|
143
145
|
5. If review is non-clean, do **not** call `update_goal`. Record durable blocker work instead:
|
|
144
146
|
|
|
147
|
+
1. Run targeted implementation verification for the story.
|
|
148
|
+
2. Delegate an `architect` review covering all three lanes:
|
|
149
|
+
- architecture-side: system boundaries, layering, data/control flow, operational risks.
|
|
150
|
+
- product-side: user-visible behavior, acceptance criteria, edge cases, regressions.
|
|
151
|
+
- code-side: maintainability, tests, integration points, and unsafe shortcuts.
|
|
152
|
+
3. Delegate an `executor` QA/red-team lane to build and run the e2e/read-teaming QA suite appropriate for the story. This lane must try to break the change, not just confirm the happy path.
|
|
153
|
+
4. If any lane finds an issue, do **not** checkpoint `complete` and do **not** call `update_goal`. Record durable blocker work instead:
|
|
145
154
|
```sh
|
|
146
|
-
gjc ultragoal record-review-blockers --goal-id <id> --title "Resolve
|
|
155
|
+
gjc ultragoal record-review-blockers --goal-id <id> --title "Resolve verification blockers" --objective "<blocker-resolution objective>" --evidence "<architect/executor findings>" --gjc-goal-json <active-get-goal-json-or-path>
|
|
147
156
|
```
|
|
157
|
+
5. Complete or steer through the blocker story, then rerun the full blocking verification loop. Repeat until all verifier lanes are clean.
|
|
158
|
+
6. Only after the loop is clean, checkpoint the story as complete with a structured quality gate. The checkpoint creates a receipt; `goals.json.status` alone is not proof. Aggregate direct completion requires a fresh final aggregate receipt covering the full required-goal set before `update_goal({"status":"complete"})` is allowed.
|
|
148
159
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
6. If review is clean, call `update_goal({"status":"complete"})`, call `get_goal({})`, and checkpoint with a structured final gate:
|
|
152
|
-
|
|
153
|
-
```sh
|
|
154
|
-
gjc ultragoal checkpoint --goal-id <id> --status complete --evidence "<tests/files/review evidence>" --gjc-goal-json <fresh-complete-get-goal-json-or-path> --quality-gate-json <quality-gate-json-or-path>
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
`--quality-gate-json` must include:
|
|
160
|
+
The native `checkpoint --status complete` command rejects missing or shallow gates. `--quality-gate-json` must include:
|
|
158
161
|
|
|
159
162
|
```json
|
|
160
163
|
{
|
|
161
|
-
"
|
|
162
|
-
|
|
163
|
-
|
|
164
|
+
"architectReview": {
|
|
165
|
+
"architectureStatus": "CLEAR",
|
|
166
|
+
"productStatus": "CLEAR",
|
|
167
|
+
"codeStatus": "CLEAR",
|
|
168
|
+
"recommendation": "APPROVE",
|
|
169
|
+
"evidence": "architect review synthesis with architecture/product/code coverage",
|
|
170
|
+
"commands": ["architect review command or agent evidence id"],
|
|
171
|
+
"blockers": []
|
|
172
|
+
},
|
|
173
|
+
"executorQa": {
|
|
174
|
+
"status": "passed",
|
|
175
|
+
"e2eStatus": "passed",
|
|
176
|
+
"redTeamStatus": "passed",
|
|
177
|
+
"evidence": "executor-built e2e and red-team QA commands/results",
|
|
178
|
+
"e2eCommands": ["bun test:e2e"],
|
|
179
|
+
"redTeamCommands": ["bun test:red-team"],
|
|
180
|
+
"blockers": []
|
|
181
|
+
},
|
|
182
|
+
"iteration": {
|
|
183
|
+
"status": "passed",
|
|
184
|
+
"evidence": "blockers were absent or resolved and the full verification loop was rerun cleanly",
|
|
185
|
+
"fullRerun": true,
|
|
186
|
+
"rerunCommands": ["bun test:e2e", "bun test:red-team"],
|
|
187
|
+
"blockers": []
|
|
188
|
+
}
|
|
164
189
|
}
|
|
165
190
|
```
|
|
166
191
|
|
|
192
|
+
Receipts are freshness-scoped:
|
|
193
|
+
- Per-goal receipts remain fresh for their target goal unless that goal, its blocker metadata, or its supersession metadata changes.
|
|
194
|
+
- Normal later `goal_started` or clean receipt-backed `goal_checkpointed` events for other goals do not stale older per-goal receipts.
|
|
195
|
+
- Appending required goals or changing final required-goal state stales final aggregate receipts. Final aggregate completion requires a fresh final aggregate receipt proving no incomplete, blocked, or `review_blocked` required goals remain.
|
|
196
|
+
|
|
167
197
|
## Constraints
|
|
168
198
|
|
|
169
199
|
- The shell command cannot directly invoke interactive `/goal`; it emits a model-facing handoff for the active GJC agent.
|
|
@@ -171,6 +201,6 @@ The final ultragoal story is not complete until the active agent has run the fin
|
|
|
171
201
|
- After a completed aggregate ultragoal run, clear the goal manually with `/goal clear` before starting another ultragoal run in the same session/thread.
|
|
172
202
|
- Never call `create_goal` when `get_goal` reports a different active goal.
|
|
173
203
|
- Never call `update_goal` unless the aggregate run or legacy per-story goal is actually complete.
|
|
174
|
-
- In aggregate mode, intermediate story checkpoints require a matching `active` GJC goal snapshot; final story
|
|
204
|
+
- In aggregate mode, intermediate story checkpoints require a matching `active` GJC goal snapshot; final story checkpoint also uses the active snapshot and creates the final aggregate receipt. Only after that receipt exists may `update_goal({"status":"complete"})` reconcile the inline goal state.
|
|
175
205
|
- Completion checkpoints require read-only goal snapshot reconciliation: pass fresh `get_goal` JSON/path with `--gjc-goal-json`; shell commands and hooks must not mutate goal state.
|
|
176
206
|
- Treat `ledger.jsonl` as the durable audit trail; checkpoint after every success or failure.
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { DEFAULT_ULTRAGOAL_OBJECTIVE } from "./goal-mode-request";
|
|
2
|
+
import {
|
|
3
|
+
computeUltragoalPlanGeneration,
|
|
4
|
+
hashStructuredValue,
|
|
5
|
+
readUltragoalLedger,
|
|
6
|
+
readUltragoalPlan,
|
|
7
|
+
type UltragoalCompletionVerification,
|
|
8
|
+
type UltragoalGoal,
|
|
9
|
+
type UltragoalLedgerEvent,
|
|
10
|
+
type UltragoalPlan,
|
|
11
|
+
type UltragoalReceiptKind,
|
|
12
|
+
} from "./ultragoal-runtime";
|
|
13
|
+
|
|
14
|
+
export type UltragoalGuardState =
|
|
15
|
+
| "inactive"
|
|
16
|
+
| "unrelated_goal"
|
|
17
|
+
| "active_verified_complete"
|
|
18
|
+
| "active_missing_receipt"
|
|
19
|
+
| "active_stale_receipt"
|
|
20
|
+
| "active_missing_final_receipt"
|
|
21
|
+
| "active_dirty_quality_gate"
|
|
22
|
+
| "active_review_blocked_unrecorded"
|
|
23
|
+
| "active_review_blocked_recorded"
|
|
24
|
+
| "unreadable_fail_closed";
|
|
25
|
+
|
|
26
|
+
export interface UltragoalGuardDiagnostic {
|
|
27
|
+
state: UltragoalGuardState;
|
|
28
|
+
message: string;
|
|
29
|
+
goalId?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface CurrentGoalLike {
|
|
33
|
+
objective: string;
|
|
34
|
+
status?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function objectiveMatches(currentObjective: string, plan: UltragoalPlan): boolean {
|
|
38
|
+
const normalized = currentObjective.trim();
|
|
39
|
+
if (!normalized) return false;
|
|
40
|
+
if (normalized === plan.gjcObjective || normalized === DEFAULT_ULTRAGOAL_OBJECTIVE) return true;
|
|
41
|
+
if (plan.gjcObjectiveAliases?.some(alias => alias === normalized)) return true;
|
|
42
|
+
return plan.goals.some(goal => goal.objective === normalized);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function requiredGoals(plan: UltragoalPlan): UltragoalGoal[] {
|
|
46
|
+
return plan.goals.filter(goal => goal.status !== "superseded");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function findReceiptGoal(
|
|
50
|
+
plan: UltragoalPlan,
|
|
51
|
+
currentObjective: string,
|
|
52
|
+
): { goal: UltragoalGoal; receiptKind: UltragoalReceiptKind } | null {
|
|
53
|
+
if (
|
|
54
|
+
currentObjective === plan.gjcObjective ||
|
|
55
|
+
currentObjective === DEFAULT_ULTRAGOAL_OBJECTIVE ||
|
|
56
|
+
plan.gjcObjectiveAliases?.some(alias => alias === currentObjective)
|
|
57
|
+
) {
|
|
58
|
+
const finalGoal = [...requiredGoals(plan)]
|
|
59
|
+
.reverse()
|
|
60
|
+
.find(goal => goal.completionVerification?.receiptKind === "final-aggregate");
|
|
61
|
+
return finalGoal ? { goal: finalGoal, receiptKind: "final-aggregate" } : null;
|
|
62
|
+
}
|
|
63
|
+
const storyGoal = plan.goals.find(goal => goal.objective === currentObjective);
|
|
64
|
+
return storyGoal ? { goal: storyGoal, receiptKind: "per-goal" } : null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function findLedgerReceiptEvent(
|
|
68
|
+
ledger: readonly UltragoalLedgerEvent[],
|
|
69
|
+
receipt: UltragoalCompletionVerification,
|
|
70
|
+
): UltragoalLedgerEvent | null {
|
|
71
|
+
return (
|
|
72
|
+
ledger.find(event => {
|
|
73
|
+
if (event.eventId !== receipt.checkpointLedgerEventId) return false;
|
|
74
|
+
if (event.event !== "goal_checkpointed") return false;
|
|
75
|
+
if (event.goalId !== receipt.goalId) return false;
|
|
76
|
+
const eventReceipt = event.completionVerification as UltragoalCompletionVerification | undefined;
|
|
77
|
+
return (
|
|
78
|
+
event.status === "complete" &&
|
|
79
|
+
eventReceipt?.receiptId === receipt.receiptId &&
|
|
80
|
+
eventReceipt.receiptKind === receipt.receiptKind &&
|
|
81
|
+
eventReceipt.planGeneration === receipt.planGeneration
|
|
82
|
+
);
|
|
83
|
+
}) ?? null
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function validateCompletionReceipt(input: {
|
|
88
|
+
plan: UltragoalPlan;
|
|
89
|
+
ledger: readonly UltragoalLedgerEvent[];
|
|
90
|
+
goal: UltragoalGoal;
|
|
91
|
+
receiptKind: UltragoalReceiptKind;
|
|
92
|
+
}): UltragoalGuardDiagnostic {
|
|
93
|
+
const receipt = input.goal.completionVerification;
|
|
94
|
+
if (!receipt) {
|
|
95
|
+
return {
|
|
96
|
+
state: input.receiptKind === "final-aggregate" ? "active_missing_final_receipt" : "active_missing_receipt",
|
|
97
|
+
message: `Ultragoal ${input.goal.id} has no ${input.receiptKind} completion verification receipt.`,
|
|
98
|
+
goalId: input.goal.id,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
if (
|
|
102
|
+
receipt.schemaVersion !== 1 ||
|
|
103
|
+
receipt.goalId !== input.goal.id ||
|
|
104
|
+
receipt.receiptKind !== input.receiptKind ||
|
|
105
|
+
!receipt.planGeneration ||
|
|
106
|
+
!receipt.checkpointLedgerEventId
|
|
107
|
+
) {
|
|
108
|
+
return {
|
|
109
|
+
state: "active_stale_receipt",
|
|
110
|
+
message: `Ultragoal ${input.goal.id} receipt is malformed or stale.`,
|
|
111
|
+
goalId: input.goal.id,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
const event = findLedgerReceiptEvent(input.ledger, receipt);
|
|
115
|
+
if (!event) {
|
|
116
|
+
return {
|
|
117
|
+
state: "active_stale_receipt",
|
|
118
|
+
message: `Ultragoal ${input.goal.id} receipt ledger event is missing.`,
|
|
119
|
+
goalId: input.goal.id,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
const generation = computeUltragoalPlanGeneration({
|
|
123
|
+
plan: input.plan,
|
|
124
|
+
ledger: input.ledger,
|
|
125
|
+
goal: input.goal,
|
|
126
|
+
receiptKind: input.receiptKind,
|
|
127
|
+
beforeStatus: receipt.goalStatusBeforeCheckpoint,
|
|
128
|
+
excludeEventId: receipt.checkpointLedgerEventId,
|
|
129
|
+
});
|
|
130
|
+
if (generation.planGeneration !== receipt.planGeneration) {
|
|
131
|
+
return {
|
|
132
|
+
state: "active_stale_receipt",
|
|
133
|
+
message: `Ultragoal ${input.goal.id} receipt generation is stale.`,
|
|
134
|
+
goalId: input.goal.id,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
if (hashStructuredValue(event.qualityGateJson) !== receipt.qualityGateHash) {
|
|
138
|
+
return {
|
|
139
|
+
state: "active_dirty_quality_gate",
|
|
140
|
+
message: `Ultragoal ${input.goal.id} receipt quality-gate hash does not match ledger.`,
|
|
141
|
+
goalId: input.goal.id,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
if (input.goal.updatedAt !== receipt.verifiedAt) {
|
|
145
|
+
return {
|
|
146
|
+
state: "active_stale_receipt",
|
|
147
|
+
message: `Ultragoal ${input.goal.id} receipt target changed after verification.`,
|
|
148
|
+
goalId: input.goal.id,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
if (input.receiptKind === "final-aggregate") {
|
|
152
|
+
const incomplete = requiredGoals(input.plan).filter(goal => goal.status !== "complete");
|
|
153
|
+
if (incomplete.length > 0) {
|
|
154
|
+
return {
|
|
155
|
+
state: "active_missing_final_receipt",
|
|
156
|
+
message: `Ultragoal final receipt is not valid while required goals remain incomplete: ${incomplete.map(goal => goal.id).join(", ")}.`,
|
|
157
|
+
goalId: input.goal.id,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
const missingReceipts = requiredGoals(input.plan).filter(
|
|
161
|
+
goal => goal.id !== input.goal.id && !goal.completionVerification,
|
|
162
|
+
);
|
|
163
|
+
if (missingReceipts.length > 0) {
|
|
164
|
+
return {
|
|
165
|
+
state: "active_missing_receipt",
|
|
166
|
+
message: `Ultragoal final receipt is missing per-goal evidence for: ${missingReceipts.map(goal => goal.id).join(", ")}.`,
|
|
167
|
+
goalId: input.goal.id,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
state: "active_verified_complete",
|
|
173
|
+
message: `Ultragoal ${input.goal.id} has a fresh ${input.receiptKind} receipt.`,
|
|
174
|
+
goalId: input.goal.id,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export async function readUltragoalVerificationState(input: {
|
|
179
|
+
cwd: string;
|
|
180
|
+
currentGoal?: CurrentGoalLike | null;
|
|
181
|
+
}): Promise<UltragoalGuardDiagnostic> {
|
|
182
|
+
const currentObjective = input.currentGoal?.objective?.trim() ?? "";
|
|
183
|
+
if (!currentObjective) return { state: "inactive", message: "No current goal objective is active." };
|
|
184
|
+
let plan: UltragoalPlan | null;
|
|
185
|
+
let ledger: UltragoalLedgerEvent[];
|
|
186
|
+
try {
|
|
187
|
+
plan = await readUltragoalPlan(input.cwd);
|
|
188
|
+
ledger = await readUltragoalLedger(input.cwd);
|
|
189
|
+
} catch (error) {
|
|
190
|
+
if (currentObjective === DEFAULT_ULTRAGOAL_OBJECTIVE) {
|
|
191
|
+
return {
|
|
192
|
+
state: "unreadable_fail_closed",
|
|
193
|
+
message: `Unable to read Ultragoal verification state: ${error instanceof Error ? error.message : String(error)}`,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
return { state: "unrelated_goal", message: "Current goal is not an active Ultragoal objective." };
|
|
197
|
+
}
|
|
198
|
+
if (!plan) return { state: "inactive", message: "No Ultragoal plan exists." };
|
|
199
|
+
if (!objectiveMatches(currentObjective, plan))
|
|
200
|
+
return { state: "unrelated_goal", message: "Current goal is not an active Ultragoal objective." };
|
|
201
|
+
if (plan.goals.some(goal => goal.status === "review_blocked")) {
|
|
202
|
+
return {
|
|
203
|
+
state: "active_review_blocked_recorded",
|
|
204
|
+
message: "Ultragoal has recorded review blockers; complete blocker work and rerun verification.",
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
if (plan.goals.some(goal => goal.status === "blocked" || goal.status === "failed")) {
|
|
208
|
+
return {
|
|
209
|
+
state: "active_dirty_quality_gate",
|
|
210
|
+
message: "Ultragoal has blocked or failed goals; record blockers or rerun verification.",
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
const receiptTarget = findReceiptGoal(plan, currentObjective);
|
|
214
|
+
if (!receiptTarget) {
|
|
215
|
+
return {
|
|
216
|
+
state: "active_missing_final_receipt",
|
|
217
|
+
message: "Ultragoal aggregate completion requires a fresh final aggregate receipt.",
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
return validateCompletionReceipt({ plan, ledger, goal: receiptTarget.goal, receiptKind: receiptTarget.receiptKind });
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export async function assertCanCompleteCurrentGoal(input: {
|
|
224
|
+
cwd: string;
|
|
225
|
+
currentGoal?: CurrentGoalLike | null;
|
|
226
|
+
}): Promise<void> {
|
|
227
|
+
if (!input.cwd) return;
|
|
228
|
+
const diagnostic = await readUltragoalVerificationState(input);
|
|
229
|
+
if (["inactive", "unrelated_goal", "active_verified_complete"].includes(diagnostic.state)) return;
|
|
230
|
+
throw new Error(
|
|
231
|
+
`${diagnostic.message} Run strict \`gjc ultragoal checkpoint --status complete --quality-gate-json <file> --gjc-goal-json <file>\` first, or record review blockers and rerun verification.`,
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export function isUltragoalBypassPrompt(prompt: string): boolean {
|
|
236
|
+
return /update_goal\s*\(|goal\s+complete|checkpoint[^\n]+--status\s+complete|skip\s+verification|weaken\s+verification|mark\s+.*complete/i.test(
|
|
237
|
+
prompt,
|
|
238
|
+
);
|
|
239
|
+
}
|