@gajae-code/coding-agent 0.11.6 → 0.11.8
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 +92 -0
- package/dist/types/capability/index.d.ts +10 -20
- package/dist/types/capability/types.d.ts +3 -0
- package/dist/types/cli/args.d.ts +0 -2
- package/dist/types/commands/daemon.d.ts +2 -2
- package/dist/types/commands/sdk.d.ts +20 -2
- package/dist/types/config/keybindings.d.ts +34 -4
- package/dist/types/config/model-profile-activation.d.ts +4 -3
- package/dist/types/config/model-profile-contract.d.ts +38 -0
- package/dist/types/config/settings-schema.d.ts +70 -7
- package/dist/types/edit/modes/patch.d.ts +13 -0
- package/dist/types/edit/path-mutation-lock.d.ts +15 -0
- package/dist/types/eval/py/executor.d.ts +1 -1
- package/dist/types/eval/py/runner-artifact.d.ts +8 -0
- package/dist/types/eval/py/tool-bridge.d.ts +1 -2
- package/dist/types/gjc-runtime/repository-binding.d.ts +63 -0
- package/dist/types/gjc-runtime/ultragoal-guard.d.ts +6 -5
- package/dist/types/gjc-runtime/ultragoal-receipt-freshness.d.ts +20 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +43 -5
- package/dist/types/main.d.ts +32 -1
- package/dist/types/modes/components/btw-panel.d.ts +1 -0
- package/dist/types/modes/components/model-selector.d.ts +2 -0
- package/dist/types/modes/components/runtime-mcp-add-wizard.d.ts +2 -1
- package/dist/types/modes/components/tool-status-header.d.ts +4 -2
- package/dist/types/modes/components/welcome.d.ts +2 -0
- package/dist/types/modes/controllers/btw-controller.d.ts +5 -0
- package/dist/types/modes/controllers/command-controller.d.ts +2 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +2 -1
- package/dist/types/modes/interactive-mode.d.ts +7 -2
- package/dist/types/modes/prompt-action-autocomplete.d.ts +6 -1
- package/dist/types/modes/prompt-suggestion-controller.d.ts +35 -0
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +2 -2
- package/dist/types/modes/types.d.ts +4 -0
- package/dist/types/modes/utils/hotkeys-markdown.d.ts +2 -1
- package/dist/types/runtime-mcp/content-limits.d.ts +7 -0
- package/dist/types/runtime-mcp/manager.d.ts +1 -1
- package/dist/types/runtime-mcp/oauth-flow.d.ts +0 -5
- package/dist/types/runtime-mcp/plugin-network-boundary.d.ts +14 -0
- package/dist/types/sdk/broker/broker.d.ts +2 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +6 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +6 -1
- package/dist/types/sdk/bus/config-commands.d.ts +2 -2
- package/dist/types/sdk/bus/config.d.ts +2 -0
- package/dist/types/sdk/bus/conversation-store.d.ts +1 -0
- package/dist/types/sdk/bus/index.d.ts +26 -1
- package/dist/types/sdk/bus/lifecycle-commands.d.ts +5 -4
- package/dist/types/sdk/bus/notification-orchestration.d.ts +1 -0
- package/dist/types/sdk/bus/notification-service.d.ts +34 -0
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +105 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +18 -3
- package/dist/types/sdk/bus/telegram-daemon-control.d.ts +5 -1
- package/dist/types/sdk/bus/telegram-daemon.d.ts +189 -4
- package/dist/types/sdk/bus/telegram-reference.d.ts +2 -0
- package/dist/types/sdk/bus/topic-registry.d.ts +88 -2
- package/dist/types/sdk/client/discovery.d.ts +2 -0
- package/dist/types/sdk/client/liveness.d.ts +7 -0
- package/dist/types/sdk/host/control/operations.d.ts +1 -1
- package/dist/types/sdk/host/host.d.ts +1 -1
- package/dist/types/sdk/host/query/handlers.d.ts +9 -0
- package/dist/types/sdk/index.d.ts +2 -0
- package/dist/types/sdk/prompt-status.d.ts +88 -0
- package/dist/types/sdk/session-directory.d.ts +1 -1
- package/dist/types/sdk/startup-capability.d.ts +3 -0
- package/dist/types/sdk/transport/auth-preface.d.ts +9 -0
- package/dist/types/sdk/transport/index.d.ts +12 -0
- package/dist/types/sdk/transport/relay.d.ts +43 -0
- package/dist/types/sdk/transport/serve-cli.d.ts +4 -0
- package/dist/types/sdk/transport/socket.d.ts +7 -0
- package/dist/types/sdk/transport/stdio.d.ts +3 -0
- package/dist/types/session/agent-session.d.ts +30 -5
- package/dist/types/session/btw-contract.d.ts +16 -0
- package/dist/types/session/internal/managed-session-scope.d.ts +32 -9
- package/dist/types/session/internal/managed-session-storage.d.ts +27 -10
- package/dist/types/session/internal/native-publish-outcome.d.ts +48 -0
- package/dist/types/session/session-manager.d.ts +25 -4
- package/dist/types/session/session-storage.d.ts +18 -0
- package/dist/types/setup/credential-import.d.ts +5 -1
- package/dist/types/setup/provider-onboarding.d.ts +2 -0
- package/dist/types/ssh/utils.d.ts +1 -0
- package/dist/types/task/discovery.d.ts +3 -1
- package/dist/types/task/index.d.ts +3 -1
- package/dist/types/task/receipt.d.ts +2 -0
- package/dist/types/task/types.d.ts +94 -0
- package/dist/types/tools/ask.d.ts +454 -174
- package/dist/types/tools/fetch.d.ts +5 -8
- package/dist/types/tools/image-gen.d.ts +63 -1
- package/dist/types/tools/index.d.ts +4 -0
- package/dist/types/tools/skill-discovery.d.ts +7 -0
- package/dist/types/tools/sqlite-reader.d.ts +7 -0
- package/dist/types/utils/prompt-suggestion.d.ts +33 -0
- package/dist/types/utils/shell-snapshot.d.ts +12 -0
- package/dist/types/web/insane/bridge.d.ts +1 -0
- package/dist/types/web/insane/url-guard.d.ts +17 -0
- package/package.json +8 -8
- package/scripts/capture-platform-shortcut-labels-showcase.ts +342 -0
- package/scripts/dogfood-repository-binding.ts +209 -0
- package/scripts/generate-sdk-operation-inventory.ts +4 -0
- package/scripts/run-test-manifest.ts +4 -4
- package/src/capability/index.ts +50 -42
- package/src/capability/types.ts +3 -0
- package/src/cli/args.ts +2 -92
- package/src/cli/notify-cli.ts +5 -38
- package/src/cli/setup-cli.ts +10 -4
- package/src/cli.ts +52 -9
- package/src/commands/daemon.ts +22 -5
- package/src/commands/sdk.ts +44 -11
- package/src/config/keybindings.ts +158 -25
- package/src/config/model-profile-activation.ts +7 -18
- package/src/config/model-profile-contract.ts +161 -0
- package/src/config/model-profiles.ts +18 -0
- package/src/config/model-registry.ts +11 -1
- package/src/config/settings-schema.ts +73 -7
- package/src/coordinator-mcp/model-preset.ts +21 -74
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +2 -2
- package/src/defaults/gjc/skills/ralplan/SKILL.md +3 -1
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +42 -1
- package/src/discovery/ssh.ts +12 -1
- package/src/edit/index.ts +3 -6
- package/src/edit/modes/patch.ts +38 -1
- package/src/edit/modes/replace.ts +42 -0
- package/src/edit/path-mutation-lock.ts +112 -0
- package/src/eval/py/executor.ts +30 -11
- package/src/eval/py/kernel.ts +1 -21
- package/src/eval/py/prelude.py +2 -2
- package/src/eval/py/runner-artifact.ts +84 -0
- package/src/eval/py/tool-bridge.ts +34 -18
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +2 -1
- package/src/extensibility/plugins/marketplace/fetcher.ts +191 -7
- package/src/gjc-runtime/launch-tmux.ts +1 -1
- package/src/gjc-runtime/managed-owner-supervisor.ts +83 -4
- package/src/gjc-runtime/ralplan-runtime.ts +75 -3
- package/src/gjc-runtime/repository-binding.ts +267 -0
- package/src/gjc-runtime/session-state-sidecar.ts +25 -9
- package/src/gjc-runtime/tmux-owner-isolation.ts +2 -1
- package/src/gjc-runtime/tmux-sessions.ts +45 -6
- package/src/gjc-runtime/ultragoal-guard.ts +93 -15
- package/src/gjc-runtime/ultragoal-receipt-freshness.ts +103 -0
- package/src/gjc-runtime/ultragoal-runtime.ts +456 -31
- package/src/gjc-runtime/workflow-manifest.generated.json +58 -0
- package/src/gjc-runtime/workflow-manifest.ts +23 -0
- package/src/internal-urls/docs-index.generated.ts +12 -10
- package/src/internal-urls/local-protocol.ts +64 -21
- package/src/internal-urls/mcp-protocol.ts +73 -11
- package/src/main.ts +111 -37
- package/src/modes/DESIGN.md +5 -0
- package/src/modes/acp/acp-agent.ts +50 -16
- package/src/modes/action-registry.ts +1 -1
- package/src/modes/components/btw-panel.ts +82 -16
- package/src/modes/components/model-selector.ts +73 -11
- package/src/modes/components/plan-preview-overlay.ts +1 -1
- package/src/modes/components/runtime-mcp-add-wizard.ts +5 -0
- package/src/modes/components/tool-status-header.ts +25 -4
- package/src/modes/components/welcome.ts +22 -18
- package/src/modes/controllers/btw-controller.ts +119 -36
- package/src/modes/controllers/command-controller.ts +97 -54
- package/src/modes/controllers/event-controller.ts +10 -0
- package/src/modes/controllers/input-controller.ts +38 -2
- package/src/modes/controllers/runtime-mcp-command-controller.ts +29 -6
- package/src/modes/controllers/selector-controller.ts +149 -63
- package/src/modes/interactive-mode.ts +57 -34
- package/src/modes/prompt-action-autocomplete.ts +32 -13
- package/src/modes/prompt-suggestion-controller.ts +96 -0
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +2 -2
- package/src/modes/types.ts +4 -0
- package/src/modes/utils/hotkeys-markdown.ts +64 -36
- package/src/modes/utils/ui-helpers.ts +5 -3
- package/src/prompts/agents/architect.md +0 -1
- package/src/prompts/agents/critic.md +0 -1
- package/src/prompts/system/btw-user.md +3 -8
- package/src/prompts/system/prompt-suggestion-system.md +32 -0
- package/src/prompts/system/system-prompt.md +6 -4
- package/src/prompts/tools/cron.md +3 -1
- package/src/prompts/tools/skill-discovery.md +1 -0
- package/src/runtime-mcp/client.ts +41 -55
- package/src/runtime-mcp/content-limits.ts +71 -0
- package/src/runtime-mcp/json-rpc.ts +23 -9
- package/src/runtime-mcp/manager.ts +51 -53
- package/src/runtime-mcp/oauth-flow.ts +0 -72
- package/src/runtime-mcp/plugin-network-boundary.ts +101 -0
- package/src/runtime-mcp/transports/http.ts +63 -22
- package/src/sdk/broker/broker.ts +34 -20
- package/src/sdk/broker/lifecycle.ts +185 -36
- package/src/sdk/bus/chat-daemon-control.ts +19 -5
- package/src/sdk/bus/config-commands.ts +2 -2
- package/src/sdk/bus/config.ts +9 -1
- package/src/sdk/bus/conversation-store.ts +40 -6
- package/src/sdk/bus/index.ts +244 -70
- package/src/sdk/bus/lifecycle-commands.ts +63 -10
- package/src/sdk/bus/lifecycle-control-runtime.ts +11 -2
- package/src/sdk/bus/lifecycle-orchestrator.ts +32 -1
- package/src/sdk/bus/notification-orchestration.ts +12 -0
- package/src/sdk/bus/notification-service.ts +68 -20
- package/src/sdk/bus/prompt-reconciliation.ts +239 -0
- package/src/sdk/bus/recent-activity.ts +90 -26
- package/src/sdk/bus/telegram-daemon-contract.ts +18 -3
- package/src/sdk/bus/telegram-daemon-control.ts +147 -35
- package/src/sdk/bus/telegram-daemon.ts +2463 -450
- package/src/sdk/bus/telegram-reference.ts +20 -2
- package/src/sdk/bus/topic-registry.ts +451 -12
- package/src/sdk/cli/session-cli.ts +15 -2
- package/src/sdk/client/discovery.ts +19 -3
- package/src/sdk/client/liveness.ts +49 -0
- package/src/sdk/host/control/dispatch.ts +1 -1
- package/src/sdk/host/control/operations.ts +1 -1
- package/src/sdk/host/host.ts +2 -6
- package/src/sdk/host/query/handlers.ts +70 -5
- package/src/sdk/index.ts +12 -0
- package/src/sdk/prompt-status.ts +97 -0
- package/src/sdk/protocol/operation-inventory.generated.json +58 -0
- package/src/sdk/protocol/operation-registry.ts +18 -4
- package/src/sdk/session-directory.ts +5 -1
- package/src/sdk/session.ts +31 -5
- package/src/sdk/startup-capability.ts +9 -1
- package/src/sdk/transport/auth-preface.ts +82 -0
- package/src/sdk/transport/index.ts +18 -0
- package/src/sdk/transport/relay.ts +252 -0
- package/src/sdk/transport/serve-cli.ts +105 -0
- package/src/sdk/transport/socket.ts +197 -0
- package/src/sdk/transport/stdio.ts +17 -0
- package/src/session/agent-session.ts +292 -154
- package/src/session/blob-store.ts +25 -7
- package/src/session/btw-contract.ts +59 -0
- package/src/session/internal/managed-session-scope.ts +1163 -137
- package/src/session/internal/managed-session-storage.ts +397 -234
- package/src/session/internal/native-publish-outcome.ts +266 -0
- package/src/session/session-manager.ts +620 -403
- package/src/session/session-storage.ts +128 -11
- package/src/setup/credential-import.ts +28 -3
- package/src/setup/provider-onboarding.ts +30 -1
- package/src/setup/provider-presets.json +2 -1
- package/src/slash-commands/builtin-registry.ts +1 -1
- package/src/ssh/utils.ts +14 -0
- package/src/task/discovery.ts +8 -2
- package/src/task/gjc-command.ts +2 -2
- package/src/task/index.ts +121 -15
- package/src/task/receipt.ts +3 -0
- package/src/task/types.ts +34 -3
- package/src/tools/ask.ts +199 -89
- package/src/tools/ast-edit.ts +2 -2
- package/src/tools/bash-pty-selection.ts +2 -2
- package/src/tools/browser/screenshot-format.ts +6 -1
- package/src/tools/cron.ts +1 -1
- package/src/tools/fetch.ts +13 -131
- package/src/tools/image-gen.ts +476 -23
- package/src/tools/index.ts +4 -0
- package/src/tools/read.ts +2 -0
- package/src/tools/render-utils.ts +7 -1
- package/src/tools/skill-discovery.ts +39 -3
- package/src/tools/sqlite-reader.ts +63 -5
- package/src/utils/edit-mode.ts +2 -2
- package/src/utils/pasted-image-path.ts +15 -6
- package/src/utils/prompt-suggestion.ts +265 -0
- package/src/utils/shell-snapshot.ts +173 -63
- package/src/utils/sixel.ts +3 -3
- package/src/web/insane/bridge.ts +5 -1
- package/src/web/insane/url-guard.ts +140 -27
- package/src/web/scrapers/docs-rs.ts +30 -5
- package/src/web/scrapers/types.ts +19 -49
- package/src/web/scrapers/utils.ts +12 -26
|
@@ -6,15 +6,43 @@ import { buildUltragoalHudSummary as buildWorkflowUltragoalHudSummary } from "..
|
|
|
6
6
|
import { renderCliWriteReceipt } from "./cli-write-receipt";
|
|
7
7
|
import { DEFAULT_ULTRAGOAL_OBJECTIVE } from "./goal-mode-request";
|
|
8
8
|
import {
|
|
9
|
+
assertCwdMatchesRepositoryBinding,
|
|
10
|
+
captureRepositoryBinding,
|
|
11
|
+
parseRepositoryBinding,
|
|
12
|
+
type RepositoryBinding,
|
|
13
|
+
} from "./repository-binding";
|
|
14
|
+
import {
|
|
15
|
+
CRITIC_GATE_HARD_STOP_EVENT,
|
|
16
|
+
CRITIC_GATE_OVERRIDE_EVENT,
|
|
17
|
+
CRITIC_VERDICT_EVENT,
|
|
18
|
+
type CriticVerdict,
|
|
19
|
+
computeCriticVerdictPlanGeneration,
|
|
9
20
|
computeUltragoalPlanGeneration,
|
|
21
|
+
countNonOkayTerminalCriticVerdicts,
|
|
10
22
|
findFreshBatchCloseReceipt,
|
|
11
23
|
findLedgerReceiptEvent,
|
|
24
|
+
isCleanPauseCriticVerdictShape,
|
|
12
25
|
requiredUltragoalGoals,
|
|
26
|
+
TERMINAL_CRITIC_CEILING,
|
|
27
|
+
terminalCriticCeilingReached,
|
|
28
|
+
terminalCriticGateOverridden,
|
|
29
|
+
terminalCriticHardStopReached,
|
|
13
30
|
validateDeferredMemberReceiptFresh,
|
|
14
31
|
validateReceiptFreshBase,
|
|
15
32
|
} from "./ultragoal-receipt-freshness";
|
|
16
33
|
|
|
17
|
-
export {
|
|
34
|
+
export {
|
|
35
|
+
CRITIC_GATE_HARD_STOP_EVENT,
|
|
36
|
+
CRITIC_GATE_OVERRIDE_EVENT,
|
|
37
|
+
CRITIC_VERDICT_EVENT,
|
|
38
|
+
type CriticVerdict,
|
|
39
|
+
computeUltragoalPlanGeneration,
|
|
40
|
+
countTerminalCriticVerdicts,
|
|
41
|
+
receiptRelevantGoals,
|
|
42
|
+
TERMINAL_CRITIC_CEILING,
|
|
43
|
+
terminalCriticCeilingReached,
|
|
44
|
+
terminalCriticGateOverridden,
|
|
45
|
+
} from "./ultragoal-receipt-freshness";
|
|
18
46
|
|
|
19
47
|
import { gjcRoot, sessionUltragoalDir } from "./session-layout";
|
|
20
48
|
import {
|
|
@@ -170,6 +198,8 @@ export interface UltragoalPlan {
|
|
|
170
198
|
gjcObjective: string;
|
|
171
199
|
gjcObjectiveAliases?: string[];
|
|
172
200
|
goals: UltragoalGoal[];
|
|
201
|
+
/** Authoritative repository identity for multi-repo fail-closed spawn (#2901). */
|
|
202
|
+
repositoryBinding?: RepositoryBinding;
|
|
173
203
|
createdAt: string;
|
|
174
204
|
updatedAt: string;
|
|
175
205
|
[key: string]: unknown;
|
|
@@ -427,7 +457,6 @@ export const DEFAULT_ULTRAGOAL_NUDGE_BUDGET = 10;
|
|
|
427
457
|
export function countUltragoalNudges(ledger: readonly UltragoalLedgerEvent[], goalId: string): number {
|
|
428
458
|
return ledger.filter(event => event.event === "nudge" && event.goalId === goalId).length;
|
|
429
459
|
}
|
|
430
|
-
|
|
431
460
|
function parseNudgeBudgetValue(value: unknown): number | null {
|
|
432
461
|
return typeof value === "number" && Number.isFinite(value) && Number.isInteger(value) && value >= 0 ? value : null;
|
|
433
462
|
}
|
|
@@ -1328,6 +1357,10 @@ function normalizePlan(raw: unknown): UltragoalPlan {
|
|
|
1328
1357
|
(value): value is string => typeof value === "string" && value.trim().length > 0,
|
|
1329
1358
|
)
|
|
1330
1359
|
: undefined;
|
|
1360
|
+
let repositoryBinding: RepositoryBinding | undefined;
|
|
1361
|
+
if (record.repositoryBinding !== undefined) {
|
|
1362
|
+
repositoryBinding = parseRepositoryBinding(record.repositoryBinding);
|
|
1363
|
+
}
|
|
1331
1364
|
return {
|
|
1332
1365
|
version: 1,
|
|
1333
1366
|
brief,
|
|
@@ -1337,6 +1370,7 @@ function normalizePlan(raw: unknown): UltragoalPlan {
|
|
|
1337
1370
|
goals,
|
|
1338
1371
|
createdAt,
|
|
1339
1372
|
updatedAt,
|
|
1373
|
+
...(repositoryBinding ? { repositoryBinding } : {}),
|
|
1340
1374
|
...(typeof record.state_revision === "number" && Number.isFinite(record.state_revision)
|
|
1341
1375
|
? { state_revision: record.state_revision }
|
|
1342
1376
|
: {}),
|
|
@@ -1537,12 +1571,16 @@ export async function createUltragoalPlan(input: {
|
|
|
1537
1571
|
goal.validationBatch = validationBatchByGoalId.get(goal.id);
|
|
1538
1572
|
validateValidationBatchPipelineExclusion(goal);
|
|
1539
1573
|
}
|
|
1574
|
+
const repositoryBinding = await captureRepositoryBinding(input.cwd, {
|
|
1575
|
+
displayPath: input.cwd,
|
|
1576
|
+
});
|
|
1540
1577
|
const plan: UltragoalPlan = {
|
|
1541
1578
|
version: 1,
|
|
1542
1579
|
brief,
|
|
1543
1580
|
gjcGoalMode: input.gjcGoalMode ?? "aggregate",
|
|
1544
1581
|
gjcObjective: DEFAULT_ULTRAGOAL_OBJECTIVE,
|
|
1545
1582
|
goals,
|
|
1583
|
+
repositoryBinding,
|
|
1546
1584
|
createdAt: now,
|
|
1547
1585
|
updatedAt: now,
|
|
1548
1586
|
};
|
|
@@ -1618,6 +1656,50 @@ export function getUltragoalRunCompletionState(
|
|
|
1618
1656
|
};
|
|
1619
1657
|
}
|
|
1620
1658
|
|
|
1659
|
+
/**
|
|
1660
|
+
* Discriminated next-action for `complete-goals` handoff (#2903).
|
|
1661
|
+
* `none` is reserved for genuine completion; `execute-goal` always carries a goal.
|
|
1662
|
+
*/
|
|
1663
|
+
export type UltragoalCompleteNextActionKind =
|
|
1664
|
+
| "none"
|
|
1665
|
+
| "execute-goal"
|
|
1666
|
+
| "retry-failed"
|
|
1667
|
+
| "resolve-blockers"
|
|
1668
|
+
| "final-aggregate-receipt";
|
|
1669
|
+
|
|
1670
|
+
export type UltragoalCompleteNextAction = {
|
|
1671
|
+
kind: UltragoalCompleteNextActionKind;
|
|
1672
|
+
goal?: UltragoalGoal;
|
|
1673
|
+
blockedGoals?: UltragoalGoal[];
|
|
1674
|
+
failedGoals?: UltragoalGoal[];
|
|
1675
|
+
};
|
|
1676
|
+
|
|
1677
|
+
/**
|
|
1678
|
+
* Resolve the actionable next step after scheduling / complete-goals.
|
|
1679
|
+
* Blocked and review_blocked goals remain unschedulable; they surface as
|
|
1680
|
+
* `resolve-blockers` instead of a contradictory `execute-goal` without goal_id.
|
|
1681
|
+
*/
|
|
1682
|
+
export function resolveUltragoalCompleteNextAction(
|
|
1683
|
+
plan: UltragoalPlan,
|
|
1684
|
+
options: { retryFailed?: boolean; selectedGoal?: UltragoalGoal } = {},
|
|
1685
|
+
): UltragoalCompleteNextAction {
|
|
1686
|
+
const state = getUltragoalRunCompletionState(plan, { retryFailed: options.retryFailed });
|
|
1687
|
+
// Genuine completion keeps next_action=`none` (historical complete-goals contract).
|
|
1688
|
+
// final-aggregate-receipt is reserved for a future dedicated handoff; do not remap
|
|
1689
|
+
// allComplete here so aggregate runs still finish with `none` / complete text.
|
|
1690
|
+
if (state.allComplete) return { kind: "none" };
|
|
1691
|
+
const goal = options.selectedGoal ?? state.nextGoal;
|
|
1692
|
+
if (goal) return { kind: "execute-goal", goal };
|
|
1693
|
+
const blockedGoals = state.incompleteGoals.filter(
|
|
1694
|
+
item => item.status === "blocked" || item.status === "review_blocked",
|
|
1695
|
+
);
|
|
1696
|
+
if (blockedGoals.length > 0) return { kind: "resolve-blockers", blockedGoals };
|
|
1697
|
+
const failedGoals = state.incompleteGoals.filter(item => item.status === "failed");
|
|
1698
|
+
if (failedGoals.length > 0) return { kind: "retry-failed", failedGoals };
|
|
1699
|
+
// Incomplete but not schedulable (unexpected statuses): still actionable, not "none".
|
|
1700
|
+
return { kind: "resolve-blockers", blockedGoals: state.incompleteGoals };
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1621
1703
|
export async function startNextUltragoalGoal(input: {
|
|
1622
1704
|
cwd: string;
|
|
1623
1705
|
retryFailed?: boolean;
|
|
@@ -1626,11 +1708,27 @@ export async function startNextUltragoalGoal(input: {
|
|
|
1626
1708
|
plan: UltragoalPlan;
|
|
1627
1709
|
goal?: UltragoalGoal;
|
|
1628
1710
|
allComplete: boolean;
|
|
1711
|
+
nextAction: UltragoalCompleteNextAction;
|
|
1629
1712
|
}> {
|
|
1630
1713
|
const plan = await readUltragoalPlan(input.cwd, input.sessionId);
|
|
1631
1714
|
if (!plan) throw new Error("No ultragoal plan found. Run `gjc ultragoal create-goals --brief ...` first.");
|
|
1632
|
-
|
|
1633
|
-
if (!
|
|
1715
|
+
// Fail closed: delegated execution requires stamped repository authority (#2901).
|
|
1716
|
+
if (!plan.repositoryBinding) {
|
|
1717
|
+
throw new Error(
|
|
1718
|
+
"Ultragoal plan is missing repositoryBinding; recreate goals so the plan is bound to an authoritative repository identity.",
|
|
1719
|
+
);
|
|
1720
|
+
}
|
|
1721
|
+
await assertCwdMatchesRepositoryBinding(input.cwd, plan.repositoryBinding);
|
|
1722
|
+
const retryFailed = input.retryFailed === true;
|
|
1723
|
+
const goal = chooseNextGoal(plan, retryFailed);
|
|
1724
|
+
if (!goal) {
|
|
1725
|
+
const state = getUltragoalRunCompletionState(plan, { retryFailed });
|
|
1726
|
+
return {
|
|
1727
|
+
plan,
|
|
1728
|
+
allComplete: state.allComplete,
|
|
1729
|
+
nextAction: resolveUltragoalCompleteNextAction(plan, { retryFailed }),
|
|
1730
|
+
};
|
|
1731
|
+
}
|
|
1634
1732
|
if (goal.status !== "active") {
|
|
1635
1733
|
const now = new Date().toISOString();
|
|
1636
1734
|
goal.status = "active";
|
|
@@ -1640,7 +1738,12 @@ export async function startNextUltragoalGoal(input: {
|
|
|
1640
1738
|
await writePlan(input.cwd, plan, input.sessionId);
|
|
1641
1739
|
await appendLedger(input.cwd, { event: "goal_started", goalId: goal.id }, input.sessionId);
|
|
1642
1740
|
}
|
|
1643
|
-
return {
|
|
1741
|
+
return {
|
|
1742
|
+
plan,
|
|
1743
|
+
goal,
|
|
1744
|
+
allComplete: false,
|
|
1745
|
+
nextAction: { kind: "execute-goal", goal },
|
|
1746
|
+
};
|
|
1644
1747
|
}
|
|
1645
1748
|
|
|
1646
1749
|
async function readStructuredValue(cwd: string, value: string): Promise<unknown> {
|
|
@@ -2514,12 +2617,23 @@ async function validateCompletionQualityGate(
|
|
|
2514
2617
|
} = {},
|
|
2515
2618
|
): Promise<void> {
|
|
2516
2619
|
const batchMode = options.goal?.validationBatch;
|
|
2620
|
+
const receiptKind =
|
|
2621
|
+
options.plan && options.goal && options.ledger
|
|
2622
|
+
? chooseReceiptKind(options.plan, options.ledger, options.goal, "complete")
|
|
2623
|
+
: undefined;
|
|
2624
|
+
const isFinalAggregate = receiptKind === "final-aggregate";
|
|
2517
2625
|
if (batchMode && options.goal && options.goal.id !== batchMode.finalGoalId) {
|
|
2518
2626
|
validateDeferredCompletionQualityGate(gate, options.goal, batchMode, options.changeSet);
|
|
2519
2627
|
return;
|
|
2520
2628
|
}
|
|
2521
2629
|
if (batchMode && options.goal && options.goal.id === batchMode.finalGoalId) {
|
|
2522
|
-
const allowedKeys = new Set([
|
|
2630
|
+
const allowedKeys = new Set([
|
|
2631
|
+
"architectReview",
|
|
2632
|
+
"executorQa",
|
|
2633
|
+
"iteration",
|
|
2634
|
+
"validationBatchClose",
|
|
2635
|
+
"criticReview",
|
|
2636
|
+
]);
|
|
2523
2637
|
const unsupportedKeys = Object.keys(gate).filter(key => !allowedKeys.has(key));
|
|
2524
2638
|
if (unsupportedKeys.length > 0)
|
|
2525
2639
|
throw new Error(`qualityGate contains unsupported keys: ${unsupportedKeys.join(", ")}`);
|
|
@@ -2534,8 +2648,8 @@ async function validateCompletionQualityGate(
|
|
|
2534
2648
|
}
|
|
2535
2649
|
const allowedKeys = new Set(
|
|
2536
2650
|
batchMode
|
|
2537
|
-
? ["architectReview", "executorQa", "iteration", "validationBatchClose"]
|
|
2538
|
-
: ["architectReview", "executorQa", "iteration"],
|
|
2651
|
+
? ["architectReview", "executorQa", "iteration", "validationBatchClose", "criticReview"]
|
|
2652
|
+
: ["architectReview", "executorQa", "iteration", "criticReview"],
|
|
2539
2653
|
);
|
|
2540
2654
|
const unsupportedKeys = Object.keys(gate).filter(key => !allowedKeys.has(key));
|
|
2541
2655
|
if (unsupportedKeys.length > 0) {
|
|
@@ -2547,6 +2661,25 @@ async function validateCompletionQualityGate(
|
|
|
2547
2661
|
if (!architectReview || !executorQa || !iteration) {
|
|
2548
2662
|
throw new Error("qualityGate requires architectReview, executorQa, and iteration objects");
|
|
2549
2663
|
}
|
|
2664
|
+
if (isFinalAggregate) {
|
|
2665
|
+
if (
|
|
2666
|
+
options.ledger &&
|
|
2667
|
+
terminalCriticCeilingReached(options.ledger) &&
|
|
2668
|
+
!terminalCriticGateOverridden(options.ledger)
|
|
2669
|
+
) {
|
|
2670
|
+
throw new Error(
|
|
2671
|
+
"checkpoint --status complete blocked: terminal-critic ceiling reached; requires human/leader gjc ultragoal record-critic-gate-override before completion",
|
|
2672
|
+
);
|
|
2673
|
+
}
|
|
2674
|
+
const criticReview = qualityGateObject(gate.criticReview);
|
|
2675
|
+
if (criticReview?.verdict !== "OKAY") {
|
|
2676
|
+
throw new Error(
|
|
2677
|
+
"checkpoint --status complete (final aggregate) requires criticReview with verdict OKAY, non-empty evidence, and empty blockers",
|
|
2678
|
+
);
|
|
2679
|
+
}
|
|
2680
|
+
requireNonEmptyString(criticReview.evidence, "criticReview.evidence");
|
|
2681
|
+
requireEmptyBlockers(criticReview.blockers, "criticReview.blockers");
|
|
2682
|
+
}
|
|
2550
2683
|
if (
|
|
2551
2684
|
architectReview.architectureStatus !== CLEAN_ARCHITECT_STATUS ||
|
|
2552
2685
|
architectReview.productStatus !== CLEAN_ARCHITECT_STATUS ||
|
|
@@ -3550,10 +3683,10 @@ export async function recordUltragoalReviewBlockers(input: {
|
|
|
3550
3683
|
export type UltragoalBlockerClassification = "human_blocked" | "resolvable";
|
|
3551
3684
|
|
|
3552
3685
|
/**
|
|
3553
|
-
* Record an audited blocker triage classification in the durable ledger.
|
|
3554
|
-
* `
|
|
3555
|
-
*
|
|
3556
|
-
* audit note and never unblocks pause.
|
|
3686
|
+
* Record an audited blocker triage classification in the durable ledger. Pause
|
|
3687
|
+
* requires the latest `blocker_classified` event to be `human_blocked` and a
|
|
3688
|
+
* later clean pause terminal critic verdict bound to that classification; `resolvable`
|
|
3689
|
+
* is an audit note and never unblocks pause.
|
|
3557
3690
|
*/
|
|
3558
3691
|
export async function recordUltragoalBlockerClassification(input: {
|
|
3559
3692
|
cwd: string;
|
|
@@ -3574,6 +3707,130 @@ export async function recordUltragoalBlockerClassification(input: {
|
|
|
3574
3707
|
});
|
|
3575
3708
|
}
|
|
3576
3709
|
|
|
3710
|
+
export async function recordUltragoalCriticVerdict(input: {
|
|
3711
|
+
cwd: string;
|
|
3712
|
+
terminus: "completion" | "pause";
|
|
3713
|
+
verdict: CriticVerdict;
|
|
3714
|
+
evidence: string;
|
|
3715
|
+
blockers?: string[];
|
|
3716
|
+
goalId?: string;
|
|
3717
|
+
classificationEventId?: string;
|
|
3718
|
+
}): Promise<UltragoalLedgerEvent> {
|
|
3719
|
+
const evidence = input.evidence.trim();
|
|
3720
|
+
if (!evidence) throw new Error("record-critic-verdict --evidence is required");
|
|
3721
|
+
if (input.terminus !== "completion" && input.terminus !== "pause") {
|
|
3722
|
+
throw new Error('record-critic-verdict --terminus must be "completion" or "pause"');
|
|
3723
|
+
}
|
|
3724
|
+
if (input.verdict !== "OKAY" && input.verdict !== "ITERATE" && input.verdict !== "REJECT") {
|
|
3725
|
+
throw new Error("record-critic-verdict --verdict must be OKAY, ITERATE, or REJECT");
|
|
3726
|
+
}
|
|
3727
|
+
const blockers = stringArray(input.blockers ?? []);
|
|
3728
|
+
if (!blockers) throw new Error("record-critic-verdict --blockers-json must be a JSON string array");
|
|
3729
|
+
if (input.terminus === "completion" && input.verdict === "OKAY" && blockers.length > 0) {
|
|
3730
|
+
throw new Error("OKAY critic verdict must have empty blockers");
|
|
3731
|
+
}
|
|
3732
|
+
const classificationEventId = input.classificationEventId?.trim();
|
|
3733
|
+
if (input.terminus === "pause" && !classificationEventId) {
|
|
3734
|
+
throw new Error("record-critic-verdict --classification-event-id is required for pause verdicts");
|
|
3735
|
+
}
|
|
3736
|
+
const resolvedSessionId = resolveGjcSessionForWrite(input.cwd, {
|
|
3737
|
+
envSessionId: process.env.GJC_SESSION_ID,
|
|
3738
|
+
}).gjcSessionId;
|
|
3739
|
+
const paths = getUltragoalPaths(input.cwd, resolvedSessionId);
|
|
3740
|
+
return withWorkflowStateLock(
|
|
3741
|
+
paths.ledgerPath,
|
|
3742
|
+
async () => {
|
|
3743
|
+
const plan = await readUltragoalPlan(input.cwd, resolvedSessionId);
|
|
3744
|
+
if (!plan) throw new Error("record-critic-verdict requires an active ultragoal plan");
|
|
3745
|
+
const ledger = await readUltragoalLedger(input.cwd, resolvedSessionId);
|
|
3746
|
+
if (input.terminus === "pause") {
|
|
3747
|
+
const latestClassification = [...ledger].reverse().find(event => event.event === "blocker_classified");
|
|
3748
|
+
if (
|
|
3749
|
+
latestClassification?.classification !== "human_blocked" ||
|
|
3750
|
+
latestClassification.eventId !== classificationEventId
|
|
3751
|
+
) {
|
|
3752
|
+
throw new Error(
|
|
3753
|
+
"record-critic-verdict pause requires --classification-event-id to name the latest human_blocked classification",
|
|
3754
|
+
);
|
|
3755
|
+
}
|
|
3756
|
+
}
|
|
3757
|
+
const planGeneration = computeCriticVerdictPlanGeneration(plan);
|
|
3758
|
+
if (
|
|
3759
|
+
input.terminus === "pause" &&
|
|
3760
|
+
input.verdict === "OKAY" &&
|
|
3761
|
+
!isCleanPauseCriticVerdictShape(
|
|
3762
|
+
{
|
|
3763
|
+
event: CRITIC_VERDICT_EVENT,
|
|
3764
|
+
terminus: input.terminus,
|
|
3765
|
+
verdict: input.verdict,
|
|
3766
|
+
evidence,
|
|
3767
|
+
blockers,
|
|
3768
|
+
planGeneration,
|
|
3769
|
+
classificationEventId,
|
|
3770
|
+
},
|
|
3771
|
+
planGeneration,
|
|
3772
|
+
classificationEventId!,
|
|
3773
|
+
)
|
|
3774
|
+
) {
|
|
3775
|
+
throw new Error("OKAY critic verdict must have empty blockers");
|
|
3776
|
+
}
|
|
3777
|
+
const criticVerdict = await appendLedger(
|
|
3778
|
+
input.cwd,
|
|
3779
|
+
{
|
|
3780
|
+
event: CRITIC_VERDICT_EVENT,
|
|
3781
|
+
terminus: input.terminus,
|
|
3782
|
+
verdict: input.verdict,
|
|
3783
|
+
evidence,
|
|
3784
|
+
blockers,
|
|
3785
|
+
planGeneration,
|
|
3786
|
+
...(classificationEventId ? { classificationEventId } : {}),
|
|
3787
|
+
...(input.goalId?.trim() ? { goalId: input.goalId.trim() } : {}),
|
|
3788
|
+
},
|
|
3789
|
+
resolvedSessionId,
|
|
3790
|
+
);
|
|
3791
|
+
const updatedLedger = [...ledger, criticVerdict];
|
|
3792
|
+
const count = countNonOkayTerminalCriticVerdicts(updatedLedger);
|
|
3793
|
+
if (count >= TERMINAL_CRITIC_CEILING && !terminalCriticHardStopReached(updatedLedger)) {
|
|
3794
|
+
await appendLedger(
|
|
3795
|
+
input.cwd,
|
|
3796
|
+
{
|
|
3797
|
+
event: CRITIC_GATE_HARD_STOP_EVENT,
|
|
3798
|
+
planGeneration,
|
|
3799
|
+
reason: "Terminal critic verdict ceiling reached.",
|
|
3800
|
+
count,
|
|
3801
|
+
},
|
|
3802
|
+
resolvedSessionId,
|
|
3803
|
+
);
|
|
3804
|
+
}
|
|
3805
|
+
return criticVerdict;
|
|
3806
|
+
},
|
|
3807
|
+
{ cwd: input.cwd },
|
|
3808
|
+
);
|
|
3809
|
+
}
|
|
3810
|
+
|
|
3811
|
+
export async function recordUltragoalCriticGateOverride(input: {
|
|
3812
|
+
cwd: string;
|
|
3813
|
+
evidence: string;
|
|
3814
|
+
}): Promise<UltragoalLedgerEvent> {
|
|
3815
|
+
const evidence = input.evidence.trim();
|
|
3816
|
+
if (!evidence) throw new Error("record-critic-gate-override --evidence is required");
|
|
3817
|
+
const resolvedSessionId = resolveGjcSessionForWrite(input.cwd, {
|
|
3818
|
+
envSessionId: process.env.GJC_SESSION_ID,
|
|
3819
|
+
}).gjcSessionId;
|
|
3820
|
+
const paths = getUltragoalPaths(input.cwd, resolvedSessionId);
|
|
3821
|
+
return withWorkflowStateLock(
|
|
3822
|
+
paths.ledgerPath,
|
|
3823
|
+
async () => {
|
|
3824
|
+
const ledger = await readUltragoalLedger(input.cwd, resolvedSessionId);
|
|
3825
|
+
if (!terminalCriticHardStopReached(ledger)) {
|
|
3826
|
+
throw new Error("record-critic-gate-override requires a durably recorded terminal critic hard stop");
|
|
3827
|
+
}
|
|
3828
|
+
return appendLedger(input.cwd, { event: CRITIC_GATE_OVERRIDE_EVENT, evidence }, resolvedSessionId);
|
|
3829
|
+
},
|
|
3830
|
+
{ cwd: input.cwd },
|
|
3831
|
+
);
|
|
3832
|
+
}
|
|
3833
|
+
|
|
3577
3834
|
type UltragoalReviewMode = "review-only" | "review-start";
|
|
3578
3835
|
type UltragoalReviewContractStrength = "strong" | "thin-derived";
|
|
3579
3836
|
|
|
@@ -3972,7 +4229,7 @@ function renderUltragoalHelp(args: readonly string[]): string | null {
|
|
|
3972
4229
|
" $ gjc ultragoal classify-blocker --classification <human_blocked|resolvable> --evidence <text> [FLAGS]",
|
|
3973
4230
|
"",
|
|
3974
4231
|
"FLAGS",
|
|
3975
|
-
" --classification=<value> Required. human_blocked
|
|
4232
|
+
" --classification=<value> Required. human_blocked must be the latest blocker_classified event; pause also requires a later bound clean pause terminal critic OKAY verdict; resolvable never authorizes pause",
|
|
3976
4233
|
" --evidence=<value> Required. Specific blocker evidence; must name the human-only dependency for human_blocked",
|
|
3977
4234
|
" --goal-id=<value> Optional durable .gjc/ultragoal goal id, e.g. G001",
|
|
3978
4235
|
" --json Output a machine-readable receipt",
|
|
@@ -3983,6 +4240,44 @@ function renderUltragoalHelp(args: readonly string[]): string | null {
|
|
|
3983
4240
|
"",
|
|
3984
4241
|
].join("\n");
|
|
3985
4242
|
}
|
|
4243
|
+
if (subject === "record-critic-verdict") {
|
|
4244
|
+
return [
|
|
4245
|
+
"Run native GJC Ultragoal workflow commands",
|
|
4246
|
+
"",
|
|
4247
|
+
"USAGE",
|
|
4248
|
+
" $ gjc ultragoal record-critic-verdict --terminus <completion|pause> --verdict <OKAY|ITERATE|REJECT> --evidence <text> [--blockers-json <json>] [--goal-id <id>] [--classification-event-id <id>]",
|
|
4249
|
+
"",
|
|
4250
|
+
"FLAGS",
|
|
4251
|
+
" --terminus=<value> Required. completion or pause",
|
|
4252
|
+
" --verdict=<value> Required. OKAY, ITERATE, or REJECT",
|
|
4253
|
+
" --evidence=<value> Required. Specific evidence supporting the verdict",
|
|
4254
|
+
" --blockers-json=<value> Optional JSON string array of blockers",
|
|
4255
|
+
" --goal-id=<value> Optional durable .gjc/ultragoal goal id, e.g. G001",
|
|
4256
|
+
" --classification-event-id=<id> Required for pause verdicts; binds the human_blocked classification",
|
|
4257
|
+
" --json Output a machine-readable receipt",
|
|
4258
|
+
"",
|
|
4259
|
+
"EXAMPLES",
|
|
4260
|
+
' $ gjc ultragoal record-critic-verdict --terminus completion --verdict OKAY --evidence "all final-aggregate checkpoint evidence is current"',
|
|
4261
|
+
"",
|
|
4262
|
+
].join("\n");
|
|
4263
|
+
}
|
|
4264
|
+
if (subject === "record-critic-gate-override") {
|
|
4265
|
+
return [
|
|
4266
|
+
"Run native GJC Ultragoal workflow commands",
|
|
4267
|
+
"",
|
|
4268
|
+
"USAGE",
|
|
4269
|
+
" $ gjc ultragoal record-critic-gate-override --evidence <text> [--json]",
|
|
4270
|
+
"",
|
|
4271
|
+
"FLAGS",
|
|
4272
|
+
" --evidence=<value> Required. Human/leader authorization evidence for the terminal-critic ceiling override",
|
|
4273
|
+
" --json Output a machine-readable receipt",
|
|
4274
|
+
"",
|
|
4275
|
+
"EXAMPLES",
|
|
4276
|
+
' $ gjc ultragoal record-critic-gate-override --evidence "leader approved another terminal attempt after reviewing all five findings"',
|
|
4277
|
+
"",
|
|
4278
|
+
].join("\n");
|
|
4279
|
+
}
|
|
4280
|
+
|
|
3986
4281
|
return [
|
|
3987
4282
|
"Run native GJC Ultragoal workflow commands",
|
|
3988
4283
|
"",
|
|
@@ -3998,11 +4293,14 @@ function renderUltragoalHelp(args: readonly string[]): string | null {
|
|
|
3998
4293
|
" steer",
|
|
3999
4294
|
" record-review-blockers",
|
|
4000
4295
|
" classify-blocker",
|
|
4296
|
+
" record-critic-verdict",
|
|
4297
|
+
" record-critic-gate-override",
|
|
4298
|
+
|
|
4001
4299
|
" start-pipeline-overlap",
|
|
4002
4300
|
" join-pipeline-overlap",
|
|
4003
4301
|
" rebaseline-pipeline-overlap",
|
|
4004
4302
|
"",
|
|
4005
|
-
"Run `gjc ultragoal checkpoint --help`, `gjc ultragoal review --help`,
|
|
4303
|
+
"Run `gjc ultragoal checkpoint --help`, `gjc ultragoal review --help`, `gjc ultragoal classify-blocker --help`, `gjc ultragoal record-critic-verdict --help`, or `gjc ultragoal record-critic-gate-override --help` for command-specific requirements.",
|
|
4006
4304
|
"",
|
|
4007
4305
|
].join("\n");
|
|
4008
4306
|
}
|
|
@@ -4021,31 +4319,111 @@ function renderStatus(summary: UltragoalStatusSummary, json: boolean): string {
|
|
|
4021
4319
|
return renderUltragoalStatusMarkdown(summary);
|
|
4022
4320
|
}
|
|
4023
4321
|
|
|
4322
|
+
function summarizeBlockedGoalForHandoff(goal: UltragoalGoal): {
|
|
4323
|
+
id: string;
|
|
4324
|
+
status: UltragoalGoalStatus;
|
|
4325
|
+
evidence?: string;
|
|
4326
|
+
} {
|
|
4327
|
+
const evidence = typeof goal.evidence === "string" && goal.evidence.trim() ? goal.evidence.trim() : undefined;
|
|
4328
|
+
return {
|
|
4329
|
+
id: goal.id,
|
|
4330
|
+
status: goal.status,
|
|
4331
|
+
...(evidence ? { evidence } : {}),
|
|
4332
|
+
};
|
|
4333
|
+
}
|
|
4334
|
+
|
|
4024
4335
|
function renderCompleteHandoff(
|
|
4025
|
-
result: {
|
|
4336
|
+
result: {
|
|
4337
|
+
plan: UltragoalPlan;
|
|
4338
|
+
goal?: UltragoalGoal;
|
|
4339
|
+
allComplete: boolean;
|
|
4340
|
+
nextAction?: UltragoalCompleteNextAction;
|
|
4341
|
+
},
|
|
4026
4342
|
json: boolean,
|
|
4027
4343
|
cwd: string,
|
|
4028
4344
|
): string {
|
|
4345
|
+
const nextAction =
|
|
4346
|
+
result.nextAction ??
|
|
4347
|
+
resolveUltragoalCompleteNextAction(result.plan, {
|
|
4348
|
+
selectedGoal: result.goal,
|
|
4349
|
+
});
|
|
4350
|
+
const goalsPath = getUltragoalPaths(cwd, currentUltragoalSessionId(cwd)).goalsPath;
|
|
4351
|
+
|
|
4029
4352
|
if (json) {
|
|
4030
|
-
|
|
4353
|
+
const receipt: Record<string, unknown> = {
|
|
4031
4354
|
ok: true,
|
|
4032
4355
|
all_complete: result.allComplete,
|
|
4033
|
-
next_action:
|
|
4034
|
-
goal_id: result.goal?.id,
|
|
4035
|
-
goal_status: result.goal?.status,
|
|
4356
|
+
next_action: nextAction.kind,
|
|
4036
4357
|
gjc_objective: result.plan.gjcObjective,
|
|
4037
|
-
goals_path:
|
|
4038
|
-
}
|
|
4358
|
+
goals_path: goalsPath,
|
|
4359
|
+
};
|
|
4360
|
+
if (nextAction.kind === "execute-goal" && nextAction.goal) {
|
|
4361
|
+
receipt.goal_id = nextAction.goal.id;
|
|
4362
|
+
receipt.goal_status = nextAction.goal.status;
|
|
4363
|
+
}
|
|
4364
|
+
if (nextAction.kind === "resolve-blockers" && nextAction.blockedGoals) {
|
|
4365
|
+
receipt.blocked_goals = nextAction.blockedGoals.map(summarizeBlockedGoalForHandoff);
|
|
4366
|
+
receipt.blocked_goal_ids = nextAction.blockedGoals.map(goal => goal.id);
|
|
4367
|
+
receipt.recovery_hints = [
|
|
4368
|
+
"gjc ultragoal classify-blocker --help",
|
|
4369
|
+
"gjc ultragoal record-review-blockers --help",
|
|
4370
|
+
"gjc ultragoal steer --kind add_subgoal --help",
|
|
4371
|
+
"gjc ultragoal steer --kind mark_blocked_superseded --help",
|
|
4372
|
+
];
|
|
4373
|
+
}
|
|
4374
|
+
if (nextAction.kind === "retry-failed" && nextAction.failedGoals) {
|
|
4375
|
+
receipt.failed_goal_ids = nextAction.failedGoals.map(goal => goal.id);
|
|
4376
|
+
receipt.recovery_hints = ["gjc ultragoal complete-goals --retry-failed"];
|
|
4377
|
+
}
|
|
4378
|
+
if (nextAction.kind === "final-aggregate-receipt") {
|
|
4379
|
+
receipt.recovery_hints = [
|
|
4380
|
+
"Finalize the aggregate completion receipt before treating the ultragoal run as closed.",
|
|
4381
|
+
];
|
|
4382
|
+
}
|
|
4383
|
+
return renderCliWriteReceipt(receipt);
|
|
4039
4384
|
}
|
|
4040
|
-
|
|
4041
|
-
if (
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4385
|
+
|
|
4386
|
+
if (nextAction.kind === "none" || (result.allComplete && nextAction.kind !== "final-aggregate-receipt")) {
|
|
4387
|
+
return "ultragoal complete all=true\n";
|
|
4388
|
+
}
|
|
4389
|
+
if (nextAction.kind === "final-aggregate-receipt") {
|
|
4390
|
+
return [
|
|
4391
|
+
"ultragoal next-action=final-aggregate-receipt",
|
|
4392
|
+
"hint=finalize the aggregate completion receipt before treating the run as closed",
|
|
4393
|
+
"",
|
|
4394
|
+
].join("\n");
|
|
4395
|
+
}
|
|
4396
|
+
if (nextAction.kind === "execute-goal" && nextAction.goal) {
|
|
4397
|
+
return [
|
|
4398
|
+
`ultragoal next-action=execute-goal goal-id=${nextAction.goal.id}`,
|
|
4399
|
+
`objective=${nextAction.goal.objective}`,
|
|
4400
|
+
`gjc-objective=${result.plan.gjcObjective}`,
|
|
4401
|
+
"checkpoint requires=architectReview:CLEAR+APPROVE,executorQa:passed",
|
|
4402
|
+
"",
|
|
4403
|
+
].join("\n");
|
|
4404
|
+
}
|
|
4405
|
+
if (nextAction.kind === "resolve-blockers" && nextAction.blockedGoals && nextAction.blockedGoals.length > 0) {
|
|
4406
|
+
const ids = nextAction.blockedGoals.map(goal => goal.id).join(",");
|
|
4407
|
+
const statuses = nextAction.blockedGoals.map(goal => `${goal.id}:${goal.status}`).join(",");
|
|
4408
|
+
return [
|
|
4409
|
+
"ultragoal next-action=resolve-blockers",
|
|
4410
|
+
`blocked-goal-ids=${ids}`,
|
|
4411
|
+
`blocked-statuses=${statuses}`,
|
|
4412
|
+
"hint=resolve blockers via classify-blocker / record-review-blockers / steer --kind add_subgoal (or audited mark_blocked_superseded); blocked goals stay unschedulable",
|
|
4413
|
+
"",
|
|
4414
|
+
].join("\n");
|
|
4415
|
+
}
|
|
4416
|
+
if (nextAction.kind === "retry-failed" && nextAction.failedGoals && nextAction.failedGoals.length > 0) {
|
|
4417
|
+
const ids = nextAction.failedGoals.map(goal => goal.id).join(",");
|
|
4418
|
+
return [
|
|
4419
|
+
"ultragoal next-action=retry-failed",
|
|
4420
|
+
`failed-goal-ids=${ids}`,
|
|
4421
|
+
"hint=run `gjc ultragoal complete-goals --retry-failed` after the failure is addressed",
|
|
4422
|
+
"",
|
|
4423
|
+
].join("\n");
|
|
4424
|
+
}
|
|
4425
|
+
// Fail closed: never claim complete or execute-goal without a goal id.
|
|
4426
|
+
return "ultragoal next-action=resolve-blockers\nhint=no schedulable goal; inspect goals.json and ledger\n";
|
|
4049
4427
|
}
|
|
4050
4428
|
function renderCheckpointContinuation(
|
|
4051
4429
|
result: UltragoalCheckpointContinuation,
|
|
@@ -4354,8 +4732,53 @@ async function dispatchUltragoalCommand(args: string[], cwd: string): Promise<Ul
|
|
|
4354
4732
|
ok: true,
|
|
4355
4733
|
event: "blocker_classified",
|
|
4356
4734
|
classification: event.classification,
|
|
4735
|
+
event_id: event.eventId,
|
|
4736
|
+
})
|
|
4737
|
+
: `Recorded blocker classification: ${String(event.classification)} event-id=${String(event.eventId)}.\n`,
|
|
4738
|
+
};
|
|
4739
|
+
}
|
|
4740
|
+
case "record-critic-verdict": {
|
|
4741
|
+
const blockersJson = flagValue(args, "--blockers-json");
|
|
4742
|
+
const blockers =
|
|
4743
|
+
blockersJson === undefined ? undefined : stringArray(await readStructuredValue(cwd, blockersJson));
|
|
4744
|
+
if (blockersJson !== undefined && !blockers) {
|
|
4745
|
+
throw new Error("record-critic-verdict --blockers-json must be a JSON string array");
|
|
4746
|
+
}
|
|
4747
|
+
const event = await recordUltragoalCriticVerdict({
|
|
4748
|
+
cwd,
|
|
4749
|
+
terminus: (flagValue(args, "--terminus") ?? "") as "completion" | "pause",
|
|
4750
|
+
verdict: (flagValue(args, "--verdict") ?? "") as CriticVerdict,
|
|
4751
|
+
evidence: flagValue(args, "--evidence") ?? "",
|
|
4752
|
+
blockers: blockers ?? undefined,
|
|
4753
|
+
goalId: flagValue(args, "--goal-id"),
|
|
4754
|
+
classificationEventId: flagValue(args, "--classification-event-id"),
|
|
4755
|
+
});
|
|
4756
|
+
return {
|
|
4757
|
+
status: 0,
|
|
4758
|
+
stdout: json
|
|
4759
|
+
? renderCliWriteReceipt({
|
|
4760
|
+
ok: true,
|
|
4761
|
+
event: CRITIC_VERDICT_EVENT,
|
|
4762
|
+
terminus: event.terminus,
|
|
4763
|
+
verdict: event.verdict,
|
|
4764
|
+
})
|
|
4765
|
+
: `Recorded critic verdict: ${String(event.verdict)} (${String(event.terminus)}).\n`,
|
|
4766
|
+
};
|
|
4767
|
+
}
|
|
4768
|
+
case "record-critic-gate-override": {
|
|
4769
|
+
const event = await recordUltragoalCriticGateOverride({
|
|
4770
|
+
cwd,
|
|
4771
|
+
evidence: flagValue(args, "--evidence") ?? "",
|
|
4772
|
+
});
|
|
4773
|
+
return {
|
|
4774
|
+
status: 0,
|
|
4775
|
+
stdout: json
|
|
4776
|
+
? renderCliWriteReceipt({
|
|
4777
|
+
ok: true,
|
|
4778
|
+
event: CRITIC_GATE_OVERRIDE_EVENT,
|
|
4779
|
+
event_id: event.eventId,
|
|
4357
4780
|
})
|
|
4358
|
-
: `Recorded
|
|
4781
|
+
: `Recorded terminal critic gate override event-id=${String(event.eventId)}.\n`,
|
|
4359
4782
|
};
|
|
4360
4783
|
}
|
|
4361
4784
|
case "start-pipeline-overlap": {
|
|
@@ -4435,6 +4858,8 @@ const RECONCILE_COMMANDS = new Set([
|
|
|
4435
4858
|
"record-review-blockers",
|
|
4436
4859
|
"review",
|
|
4437
4860
|
"classify-blocker",
|
|
4861
|
+
"record-critic-verdict",
|
|
4862
|
+
"record-critic-gate-override",
|
|
4438
4863
|
"start-pipeline-overlap",
|
|
4439
4864
|
"join-pipeline-overlap",
|
|
4440
4865
|
"rebaseline-pipeline-overlap",
|