@gajae-code/coding-agent 0.11.5 → 0.11.7

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 (138) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/types/capability/index.d.ts +10 -20
  3. package/dist/types/capability/types.d.ts +3 -0
  4. package/dist/types/commands/daemon.d.ts +2 -2
  5. package/dist/types/commands/sdk.d.ts +20 -2
  6. package/dist/types/config/keybindings.d.ts +1 -1
  7. package/dist/types/config/settings-schema.d.ts +67 -7
  8. package/dist/types/eval/py/runner-artifact.d.ts +8 -0
  9. package/dist/types/gjc-runtime/ultragoal-guard.d.ts +5 -5
  10. package/dist/types/gjc-runtime/ultragoal-receipt-freshness.d.ts +20 -0
  11. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +19 -5
  12. package/dist/types/main.d.ts +24 -1
  13. package/dist/types/modes/components/btw-panel.d.ts +1 -0
  14. package/dist/types/modes/components/model-selector.d.ts +2 -0
  15. package/dist/types/modes/components/tool-status-header.d.ts +1 -0
  16. package/dist/types/modes/controllers/btw-controller.d.ts +5 -0
  17. package/dist/types/modes/interactive-mode.d.ts +2 -0
  18. package/dist/types/modes/types.d.ts +1 -0
  19. package/dist/types/runtime-mcp/content-limits.d.ts +7 -0
  20. package/dist/types/sdk/bus/chat-daemon-control.d.ts +4 -0
  21. package/dist/types/sdk/bus/index.d.ts +1 -1
  22. package/dist/types/sdk/bus/lifecycle-commands.d.ts +5 -4
  23. package/dist/types/sdk/bus/notification-service.d.ts +34 -0
  24. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +13 -3
  25. package/dist/types/sdk/bus/telegram-daemon.d.ts +61 -3
  26. package/dist/types/sdk/bus/topic-registry.d.ts +88 -2
  27. package/dist/types/sdk/client/discovery.d.ts +2 -0
  28. package/dist/types/sdk/client/liveness.d.ts +7 -0
  29. package/dist/types/sdk/transport/auth-preface.d.ts +9 -0
  30. package/dist/types/sdk/transport/index.d.ts +12 -0
  31. package/dist/types/sdk/transport/relay.d.ts +43 -0
  32. package/dist/types/sdk/transport/serve-cli.d.ts +4 -0
  33. package/dist/types/sdk/transport/socket.d.ts +7 -0
  34. package/dist/types/sdk/transport/stdio.d.ts +3 -0
  35. package/dist/types/session/agent-session.d.ts +28 -5
  36. package/dist/types/session/btw-contract.d.ts +16 -0
  37. package/dist/types/session/internal/managed-session-scope.d.ts +21 -7
  38. package/dist/types/session/internal/managed-session-storage.d.ts +10 -8
  39. package/dist/types/session/session-manager.d.ts +35 -4
  40. package/dist/types/session/yield-queue.d.ts +8 -0
  41. package/dist/types/setup/credential-import.d.ts +5 -1
  42. package/dist/types/setup/provider-onboarding.d.ts +2 -0
  43. package/dist/types/ssh/utils.d.ts +1 -0
  44. package/dist/types/task/discovery.d.ts +3 -1
  45. package/dist/types/tools/fetch.d.ts +5 -8
  46. package/dist/types/tools/image-gen.d.ts +16 -0
  47. package/dist/types/tools/skill-discovery.d.ts +7 -0
  48. package/dist/types/tools/sqlite-reader.d.ts +7 -0
  49. package/dist/types/utils/shell-snapshot.d.ts +12 -0
  50. package/dist/types/web/insane/bridge.d.ts +1 -0
  51. package/dist/types/web/insane/url-guard.d.ts +17 -0
  52. package/package.json +8 -8
  53. package/scripts/generate-sdk-operation-inventory.ts +3 -0
  54. package/src/capability/index.ts +50 -42
  55. package/src/capability/types.ts +3 -0
  56. package/src/cli/setup-cli.ts +10 -4
  57. package/src/cli.ts +34 -9
  58. package/src/commands/daemon.ts +22 -5
  59. package/src/commands/sdk.ts +42 -9
  60. package/src/config/keybindings.ts +1 -1
  61. package/src/config/model-profiles.ts +18 -0
  62. package/src/config/settings-schema.ts +71 -7
  63. package/src/defaults/gjc/skills/ultragoal/SKILL.md +42 -1
  64. package/src/discovery/ssh.ts +12 -1
  65. package/src/eval/py/kernel.ts +1 -21
  66. package/src/eval/py/runner-artifact.ts +84 -0
  67. package/src/extensibility/plugins/marketplace/fetcher.ts +191 -7
  68. package/src/gjc-runtime/ultragoal-guard.ts +88 -10
  69. package/src/gjc-runtime/ultragoal-receipt-freshness.ts +103 -0
  70. package/src/gjc-runtime/ultragoal-runtime.ts +275 -12
  71. package/src/gjc-runtime/workflow-manifest.generated.json +58 -0
  72. package/src/gjc-runtime/workflow-manifest.ts +23 -0
  73. package/src/internal-urls/docs-index.generated.ts +9 -8
  74. package/src/internal-urls/local-protocol.ts +32 -6
  75. package/src/main.ts +87 -28
  76. package/src/modes/action-registry.ts +1 -1
  77. package/src/modes/components/btw-panel.ts +82 -16
  78. package/src/modes/components/model-selector.ts +31 -3
  79. package/src/modes/components/tool-status-header.ts +6 -1
  80. package/src/modes/controllers/btw-controller.ts +119 -36
  81. package/src/modes/controllers/command-controller.ts +15 -0
  82. package/src/modes/controllers/extension-ui-controller.ts +14 -2
  83. package/src/modes/controllers/input-controller.ts +31 -0
  84. package/src/modes/controllers/selector-controller.ts +106 -12
  85. package/src/modes/interactive-mode.ts +16 -7
  86. package/src/modes/runtime-init.ts +16 -2
  87. package/src/modes/types.ts +1 -0
  88. package/src/prompts/system/btw-user.md +3 -8
  89. package/src/prompts/tools/skill-discovery.md +1 -0
  90. package/src/runtime-mcp/client.ts +41 -55
  91. package/src/runtime-mcp/content-limits.ts +71 -0
  92. package/src/runtime-mcp/json-rpc.ts +23 -9
  93. package/src/runtime-mcp/transports/http.ts +50 -16
  94. package/src/sdk/bus/chat-daemon-control.ts +20 -7
  95. package/src/sdk/bus/index.ts +8 -7
  96. package/src/sdk/bus/lifecycle-commands.ts +16 -7
  97. package/src/sdk/bus/notification-service.ts +41 -18
  98. package/src/sdk/bus/telegram-daemon-contract.ts +13 -3
  99. package/src/sdk/bus/telegram-daemon-control.ts +26 -8
  100. package/src/sdk/bus/telegram-daemon.ts +1736 -338
  101. package/src/sdk/bus/topic-registry.ts +451 -12
  102. package/src/sdk/client/discovery.ts +19 -3
  103. package/src/sdk/client/liveness.ts +49 -0
  104. package/src/sdk/host/control/dispatch.ts +14 -5
  105. package/src/sdk/protocol/operation-inventory.generated.json +29 -0
  106. package/src/sdk/session.ts +29 -5
  107. package/src/sdk/transport/auth-preface.ts +82 -0
  108. package/src/sdk/transport/index.ts +18 -0
  109. package/src/sdk/transport/relay.ts +252 -0
  110. package/src/sdk/transport/serve-cli.ts +105 -0
  111. package/src/sdk/transport/socket.ts +197 -0
  112. package/src/sdk/transport/stdio.ts +17 -0
  113. package/src/session/agent-session.ts +1212 -768
  114. package/src/session/blob-store.ts +19 -6
  115. package/src/session/btw-contract.ts +59 -0
  116. package/src/session/internal/managed-session-scope.ts +313 -57
  117. package/src/session/internal/managed-session-storage.ts +146 -35
  118. package/src/session/session-manager.ts +385 -85
  119. package/src/session/yield-queue.ts +20 -0
  120. package/src/setup/credential-import.ts +28 -3
  121. package/src/setup/provider-onboarding.ts +30 -1
  122. package/src/setup/provider-presets.json +2 -1
  123. package/src/slash-commands/builtin-registry.ts +37 -1
  124. package/src/ssh/utils.ts +14 -0
  125. package/src/task/discovery.ts +8 -2
  126. package/src/tools/ask-answer-registry.ts +15 -1
  127. package/src/tools/ask.ts +1 -1
  128. package/src/tools/fetch.ts +13 -131
  129. package/src/tools/image-gen.ts +301 -20
  130. package/src/tools/read.ts +2 -0
  131. package/src/tools/skill-discovery.ts +39 -3
  132. package/src/tools/sqlite-reader.ts +63 -5
  133. package/src/utils/pasted-image-path.ts +15 -6
  134. package/src/utils/shell-snapshot.ts +173 -63
  135. package/src/web/insane/bridge.ts +5 -1
  136. package/src/web/insane/url-guard.ts +138 -27
  137. package/src/web/scrapers/types.ts +19 -49
  138. package/src/web/scrapers/utils.ts +12 -26
@@ -6,15 +6,37 @@ 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
+ CRITIC_GATE_HARD_STOP_EVENT,
10
+ CRITIC_GATE_OVERRIDE_EVENT,
11
+ CRITIC_VERDICT_EVENT,
12
+ type CriticVerdict,
13
+ computeCriticVerdictPlanGeneration,
9
14
  computeUltragoalPlanGeneration,
15
+ countNonOkayTerminalCriticVerdicts,
10
16
  findFreshBatchCloseReceipt,
11
17
  findLedgerReceiptEvent,
18
+ isCleanPauseCriticVerdictShape,
12
19
  requiredUltragoalGoals,
20
+ TERMINAL_CRITIC_CEILING,
21
+ terminalCriticCeilingReached,
22
+ terminalCriticGateOverridden,
23
+ terminalCriticHardStopReached,
13
24
  validateDeferredMemberReceiptFresh,
14
25
  validateReceiptFreshBase,
15
26
  } from "./ultragoal-receipt-freshness";
16
27
 
17
- export { computeUltragoalPlanGeneration, receiptRelevantGoals } from "./ultragoal-receipt-freshness";
28
+ export {
29
+ CRITIC_GATE_HARD_STOP_EVENT,
30
+ CRITIC_GATE_OVERRIDE_EVENT,
31
+ CRITIC_VERDICT_EVENT,
32
+ type CriticVerdict,
33
+ computeUltragoalPlanGeneration,
34
+ countTerminalCriticVerdicts,
35
+ receiptRelevantGoals,
36
+ TERMINAL_CRITIC_CEILING,
37
+ terminalCriticCeilingReached,
38
+ terminalCriticGateOverridden,
39
+ } from "./ultragoal-receipt-freshness";
18
40
 
19
41
  import { gjcRoot, sessionUltragoalDir } from "./session-layout";
20
42
  import {
@@ -427,7 +449,6 @@ export const DEFAULT_ULTRAGOAL_NUDGE_BUDGET = 10;
427
449
  export function countUltragoalNudges(ledger: readonly UltragoalLedgerEvent[], goalId: string): number {
428
450
  return ledger.filter(event => event.event === "nudge" && event.goalId === goalId).length;
429
451
  }
430
-
431
452
  function parseNudgeBudgetValue(value: unknown): number | null {
432
453
  return typeof value === "number" && Number.isFinite(value) && Number.isInteger(value) && value >= 0 ? value : null;
433
454
  }
@@ -2514,12 +2535,23 @@ async function validateCompletionQualityGate(
2514
2535
  } = {},
2515
2536
  ): Promise<void> {
2516
2537
  const batchMode = options.goal?.validationBatch;
2538
+ const receiptKind =
2539
+ options.plan && options.goal && options.ledger
2540
+ ? chooseReceiptKind(options.plan, options.ledger, options.goal, "complete")
2541
+ : undefined;
2542
+ const isFinalAggregate = receiptKind === "final-aggregate";
2517
2543
  if (batchMode && options.goal && options.goal.id !== batchMode.finalGoalId) {
2518
2544
  validateDeferredCompletionQualityGate(gate, options.goal, batchMode, options.changeSet);
2519
2545
  return;
2520
2546
  }
2521
2547
  if (batchMode && options.goal && options.goal.id === batchMode.finalGoalId) {
2522
- const allowedKeys = new Set(["architectReview", "executorQa", "iteration", "validationBatchClose"]);
2548
+ const allowedKeys = new Set([
2549
+ "architectReview",
2550
+ "executorQa",
2551
+ "iteration",
2552
+ "validationBatchClose",
2553
+ "criticReview",
2554
+ ]);
2523
2555
  const unsupportedKeys = Object.keys(gate).filter(key => !allowedKeys.has(key));
2524
2556
  if (unsupportedKeys.length > 0)
2525
2557
  throw new Error(`qualityGate contains unsupported keys: ${unsupportedKeys.join(", ")}`);
@@ -2534,8 +2566,8 @@ async function validateCompletionQualityGate(
2534
2566
  }
2535
2567
  const allowedKeys = new Set(
2536
2568
  batchMode
2537
- ? ["architectReview", "executorQa", "iteration", "validationBatchClose"]
2538
- : ["architectReview", "executorQa", "iteration"],
2569
+ ? ["architectReview", "executorQa", "iteration", "validationBatchClose", "criticReview"]
2570
+ : ["architectReview", "executorQa", "iteration", "criticReview"],
2539
2571
  );
2540
2572
  const unsupportedKeys = Object.keys(gate).filter(key => !allowedKeys.has(key));
2541
2573
  if (unsupportedKeys.length > 0) {
@@ -2547,6 +2579,25 @@ async function validateCompletionQualityGate(
2547
2579
  if (!architectReview || !executorQa || !iteration) {
2548
2580
  throw new Error("qualityGate requires architectReview, executorQa, and iteration objects");
2549
2581
  }
2582
+ if (isFinalAggregate) {
2583
+ if (
2584
+ options.ledger &&
2585
+ terminalCriticCeilingReached(options.ledger) &&
2586
+ !terminalCriticGateOverridden(options.ledger)
2587
+ ) {
2588
+ throw new Error(
2589
+ "checkpoint --status complete blocked: terminal-critic ceiling reached; requires human/leader gjc ultragoal record-critic-gate-override before completion",
2590
+ );
2591
+ }
2592
+ const criticReview = qualityGateObject(gate.criticReview);
2593
+ if (criticReview?.verdict !== "OKAY") {
2594
+ throw new Error(
2595
+ "checkpoint --status complete (final aggregate) requires criticReview with verdict OKAY, non-empty evidence, and empty blockers",
2596
+ );
2597
+ }
2598
+ requireNonEmptyString(criticReview.evidence, "criticReview.evidence");
2599
+ requireEmptyBlockers(criticReview.blockers, "criticReview.blockers");
2600
+ }
2550
2601
  if (
2551
2602
  architectReview.architectureStatus !== CLEAN_ARCHITECT_STATUS ||
2552
2603
  architectReview.productStatus !== CLEAN_ARCHITECT_STATUS ||
@@ -3550,10 +3601,10 @@ export async function recordUltragoalReviewBlockers(input: {
3550
3601
  export type UltragoalBlockerClassification = "human_blocked" | "resolvable";
3551
3602
 
3552
3603
  /**
3553
- * Record an audited blocker triage classification in the durable ledger. A
3554
- * `human_blocked` classification is the only thing that authorizes
3555
- * `goal({"op":"pause"})` while an Ultragoal run is active; `resolvable` is an
3556
- * audit note and never unblocks pause.
3604
+ * Record an audited blocker triage classification in the durable ledger. Pause
3605
+ * requires the latest `blocker_classified` event to be `human_blocked` and a
3606
+ * later clean pause terminal critic verdict bound to that classification; `resolvable`
3607
+ * is an audit note and never unblocks pause.
3557
3608
  */
3558
3609
  export async function recordUltragoalBlockerClassification(input: {
3559
3610
  cwd: string;
@@ -3574,6 +3625,130 @@ export async function recordUltragoalBlockerClassification(input: {
3574
3625
  });
3575
3626
  }
3576
3627
 
3628
+ export async function recordUltragoalCriticVerdict(input: {
3629
+ cwd: string;
3630
+ terminus: "completion" | "pause";
3631
+ verdict: CriticVerdict;
3632
+ evidence: string;
3633
+ blockers?: string[];
3634
+ goalId?: string;
3635
+ classificationEventId?: string;
3636
+ }): Promise<UltragoalLedgerEvent> {
3637
+ const evidence = input.evidence.trim();
3638
+ if (!evidence) throw new Error("record-critic-verdict --evidence is required");
3639
+ if (input.terminus !== "completion" && input.terminus !== "pause") {
3640
+ throw new Error('record-critic-verdict --terminus must be "completion" or "pause"');
3641
+ }
3642
+ if (input.verdict !== "OKAY" && input.verdict !== "ITERATE" && input.verdict !== "REJECT") {
3643
+ throw new Error("record-critic-verdict --verdict must be OKAY, ITERATE, or REJECT");
3644
+ }
3645
+ const blockers = stringArray(input.blockers ?? []);
3646
+ if (!blockers) throw new Error("record-critic-verdict --blockers-json must be a JSON string array");
3647
+ if (input.terminus === "completion" && input.verdict === "OKAY" && blockers.length > 0) {
3648
+ throw new Error("OKAY critic verdict must have empty blockers");
3649
+ }
3650
+ const classificationEventId = input.classificationEventId?.trim();
3651
+ if (input.terminus === "pause" && !classificationEventId) {
3652
+ throw new Error("record-critic-verdict --classification-event-id is required for pause verdicts");
3653
+ }
3654
+ const resolvedSessionId = resolveGjcSessionForWrite(input.cwd, {
3655
+ envSessionId: process.env.GJC_SESSION_ID,
3656
+ }).gjcSessionId;
3657
+ const paths = getUltragoalPaths(input.cwd, resolvedSessionId);
3658
+ return withWorkflowStateLock(
3659
+ paths.ledgerPath,
3660
+ async () => {
3661
+ const plan = await readUltragoalPlan(input.cwd, resolvedSessionId);
3662
+ if (!plan) throw new Error("record-critic-verdict requires an active ultragoal plan");
3663
+ const ledger = await readUltragoalLedger(input.cwd, resolvedSessionId);
3664
+ if (input.terminus === "pause") {
3665
+ const latestClassification = [...ledger].reverse().find(event => event.event === "blocker_classified");
3666
+ if (
3667
+ latestClassification?.classification !== "human_blocked" ||
3668
+ latestClassification.eventId !== classificationEventId
3669
+ ) {
3670
+ throw new Error(
3671
+ "record-critic-verdict pause requires --classification-event-id to name the latest human_blocked classification",
3672
+ );
3673
+ }
3674
+ }
3675
+ const planGeneration = computeCriticVerdictPlanGeneration(plan);
3676
+ if (
3677
+ input.terminus === "pause" &&
3678
+ input.verdict === "OKAY" &&
3679
+ !isCleanPauseCriticVerdictShape(
3680
+ {
3681
+ event: CRITIC_VERDICT_EVENT,
3682
+ terminus: input.terminus,
3683
+ verdict: input.verdict,
3684
+ evidence,
3685
+ blockers,
3686
+ planGeneration,
3687
+ classificationEventId,
3688
+ },
3689
+ planGeneration,
3690
+ classificationEventId!,
3691
+ )
3692
+ ) {
3693
+ throw new Error("OKAY critic verdict must have empty blockers");
3694
+ }
3695
+ const criticVerdict = await appendLedger(
3696
+ input.cwd,
3697
+ {
3698
+ event: CRITIC_VERDICT_EVENT,
3699
+ terminus: input.terminus,
3700
+ verdict: input.verdict,
3701
+ evidence,
3702
+ blockers,
3703
+ planGeneration,
3704
+ ...(classificationEventId ? { classificationEventId } : {}),
3705
+ ...(input.goalId?.trim() ? { goalId: input.goalId.trim() } : {}),
3706
+ },
3707
+ resolvedSessionId,
3708
+ );
3709
+ const updatedLedger = [...ledger, criticVerdict];
3710
+ const count = countNonOkayTerminalCriticVerdicts(updatedLedger);
3711
+ if (count >= TERMINAL_CRITIC_CEILING && !terminalCriticHardStopReached(updatedLedger)) {
3712
+ await appendLedger(
3713
+ input.cwd,
3714
+ {
3715
+ event: CRITIC_GATE_HARD_STOP_EVENT,
3716
+ planGeneration,
3717
+ reason: "Terminal critic verdict ceiling reached.",
3718
+ count,
3719
+ },
3720
+ resolvedSessionId,
3721
+ );
3722
+ }
3723
+ return criticVerdict;
3724
+ },
3725
+ { cwd: input.cwd },
3726
+ );
3727
+ }
3728
+
3729
+ export async function recordUltragoalCriticGateOverride(input: {
3730
+ cwd: string;
3731
+ evidence: string;
3732
+ }): Promise<UltragoalLedgerEvent> {
3733
+ const evidence = input.evidence.trim();
3734
+ if (!evidence) throw new Error("record-critic-gate-override --evidence is required");
3735
+ const resolvedSessionId = resolveGjcSessionForWrite(input.cwd, {
3736
+ envSessionId: process.env.GJC_SESSION_ID,
3737
+ }).gjcSessionId;
3738
+ const paths = getUltragoalPaths(input.cwd, resolvedSessionId);
3739
+ return withWorkflowStateLock(
3740
+ paths.ledgerPath,
3741
+ async () => {
3742
+ const ledger = await readUltragoalLedger(input.cwd, resolvedSessionId);
3743
+ if (!terminalCriticHardStopReached(ledger)) {
3744
+ throw new Error("record-critic-gate-override requires a durably recorded terminal critic hard stop");
3745
+ }
3746
+ return appendLedger(input.cwd, { event: CRITIC_GATE_OVERRIDE_EVENT, evidence }, resolvedSessionId);
3747
+ },
3748
+ { cwd: input.cwd },
3749
+ );
3750
+ }
3751
+
3577
3752
  type UltragoalReviewMode = "review-only" | "review-start";
3578
3753
  type UltragoalReviewContractStrength = "strong" | "thin-derived";
3579
3754
 
@@ -3972,7 +4147,7 @@ function renderUltragoalHelp(args: readonly string[]): string | null {
3972
4147
  " $ gjc ultragoal classify-blocker --classification <human_blocked|resolvable> --evidence <text> [FLAGS]",
3973
4148
  "",
3974
4149
  "FLAGS",
3975
- " --classification=<value> Required. human_blocked authorizes pause only as the latest ledger event; resolvable never authorizes pause",
4150
+ " --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
4151
  " --evidence=<value> Required. Specific blocker evidence; must name the human-only dependency for human_blocked",
3977
4152
  " --goal-id=<value> Optional durable .gjc/ultragoal goal id, e.g. G001",
3978
4153
  " --json Output a machine-readable receipt",
@@ -3983,6 +4158,44 @@ function renderUltragoalHelp(args: readonly string[]): string | null {
3983
4158
  "",
3984
4159
  ].join("\n");
3985
4160
  }
4161
+ if (subject === "record-critic-verdict") {
4162
+ return [
4163
+ "Run native GJC Ultragoal workflow commands",
4164
+ "",
4165
+ "USAGE",
4166
+ " $ gjc ultragoal record-critic-verdict --terminus <completion|pause> --verdict <OKAY|ITERATE|REJECT> --evidence <text> [--blockers-json <json>] [--goal-id <id>] [--classification-event-id <id>]",
4167
+ "",
4168
+ "FLAGS",
4169
+ " --terminus=<value> Required. completion or pause",
4170
+ " --verdict=<value> Required. OKAY, ITERATE, or REJECT",
4171
+ " --evidence=<value> Required. Specific evidence supporting the verdict",
4172
+ " --blockers-json=<value> Optional JSON string array of blockers",
4173
+ " --goal-id=<value> Optional durable .gjc/ultragoal goal id, e.g. G001",
4174
+ " --classification-event-id=<id> Required for pause verdicts; binds the human_blocked classification",
4175
+ " --json Output a machine-readable receipt",
4176
+ "",
4177
+ "EXAMPLES",
4178
+ ' $ gjc ultragoal record-critic-verdict --terminus completion --verdict OKAY --evidence "all final-aggregate checkpoint evidence is current"',
4179
+ "",
4180
+ ].join("\n");
4181
+ }
4182
+ if (subject === "record-critic-gate-override") {
4183
+ return [
4184
+ "Run native GJC Ultragoal workflow commands",
4185
+ "",
4186
+ "USAGE",
4187
+ " $ gjc ultragoal record-critic-gate-override --evidence <text> [--json]",
4188
+ "",
4189
+ "FLAGS",
4190
+ " --evidence=<value> Required. Human/leader authorization evidence for the terminal-critic ceiling override",
4191
+ " --json Output a machine-readable receipt",
4192
+ "",
4193
+ "EXAMPLES",
4194
+ ' $ gjc ultragoal record-critic-gate-override --evidence "leader approved another terminal attempt after reviewing all five findings"',
4195
+ "",
4196
+ ].join("\n");
4197
+ }
4198
+
3986
4199
  return [
3987
4200
  "Run native GJC Ultragoal workflow commands",
3988
4201
  "",
@@ -3998,11 +4211,14 @@ function renderUltragoalHelp(args: readonly string[]): string | null {
3998
4211
  " steer",
3999
4212
  " record-review-blockers",
4000
4213
  " classify-blocker",
4214
+ " record-critic-verdict",
4215
+ " record-critic-gate-override",
4216
+
4001
4217
  " start-pipeline-overlap",
4002
4218
  " join-pipeline-overlap",
4003
4219
  " rebaseline-pipeline-overlap",
4004
4220
  "",
4005
- "Run `gjc ultragoal checkpoint --help`, `gjc ultragoal review --help`, or `gjc ultragoal classify-blocker --help` for command-specific requirements.",
4221
+ "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
4222
  "",
4007
4223
  ].join("\n");
4008
4224
  }
@@ -4354,8 +4570,53 @@ async function dispatchUltragoalCommand(args: string[], cwd: string): Promise<Ul
4354
4570
  ok: true,
4355
4571
  event: "blocker_classified",
4356
4572
  classification: event.classification,
4573
+ event_id: event.eventId,
4574
+ })
4575
+ : `Recorded blocker classification: ${String(event.classification)} event-id=${String(event.eventId)}.\n`,
4576
+ };
4577
+ }
4578
+ case "record-critic-verdict": {
4579
+ const blockersJson = flagValue(args, "--blockers-json");
4580
+ const blockers =
4581
+ blockersJson === undefined ? undefined : stringArray(await readStructuredValue(cwd, blockersJson));
4582
+ if (blockersJson !== undefined && !blockers) {
4583
+ throw new Error("record-critic-verdict --blockers-json must be a JSON string array");
4584
+ }
4585
+ const event = await recordUltragoalCriticVerdict({
4586
+ cwd,
4587
+ terminus: (flagValue(args, "--terminus") ?? "") as "completion" | "pause",
4588
+ verdict: (flagValue(args, "--verdict") ?? "") as CriticVerdict,
4589
+ evidence: flagValue(args, "--evidence") ?? "",
4590
+ blockers: blockers ?? undefined,
4591
+ goalId: flagValue(args, "--goal-id"),
4592
+ classificationEventId: flagValue(args, "--classification-event-id"),
4593
+ });
4594
+ return {
4595
+ status: 0,
4596
+ stdout: json
4597
+ ? renderCliWriteReceipt({
4598
+ ok: true,
4599
+ event: CRITIC_VERDICT_EVENT,
4600
+ terminus: event.terminus,
4601
+ verdict: event.verdict,
4602
+ })
4603
+ : `Recorded critic verdict: ${String(event.verdict)} (${String(event.terminus)}).\n`,
4604
+ };
4605
+ }
4606
+ case "record-critic-gate-override": {
4607
+ const event = await recordUltragoalCriticGateOverride({
4608
+ cwd,
4609
+ evidence: flagValue(args, "--evidence") ?? "",
4610
+ });
4611
+ return {
4612
+ status: 0,
4613
+ stdout: json
4614
+ ? renderCliWriteReceipt({
4615
+ ok: true,
4616
+ event: CRITIC_GATE_OVERRIDE_EVENT,
4617
+ event_id: event.eventId,
4357
4618
  })
4358
- : `Recorded blocker classification: ${String(event.classification)}.\n`,
4619
+ : `Recorded terminal critic gate override event-id=${String(event.eventId)}.\n`,
4359
4620
  };
4360
4621
  }
4361
4622
  case "start-pipeline-overlap": {
@@ -4435,6 +4696,8 @@ const RECONCILE_COMMANDS = new Set([
4435
4696
  "record-review-blockers",
4436
4697
  "review",
4437
4698
  "classify-blocker",
4699
+ "record-critic-verdict",
4700
+ "record-critic-gate-override",
4438
4701
  "start-pipeline-overlap",
4439
4702
  "join-pipeline-overlap",
4440
4703
  "rebaseline-pipeline-overlap",
@@ -1515,12 +1515,60 @@
1515
1515
  "record-review-blockers",
1516
1516
  "steer",
1517
1517
  "classify-blocker",
1518
+ "record-critic-verdict",
1519
+ "record-critic-gate-override",
1518
1520
  "rebaseline-pipeline-overlap"
1519
1521
  ],
1520
1522
  "name": "evidence",
1521
1523
  "required": true,
1522
1524
  "type": "string"
1523
1525
  },
1526
+ {
1527
+ "appliesToVerbs": [
1528
+ "record-critic-verdict"
1529
+ ],
1530
+ "enumValues": [
1531
+ "completion",
1532
+ "pause"
1533
+ ],
1534
+ "name": "terminus",
1535
+ "required": true,
1536
+ "type": "enum"
1537
+ },
1538
+ {
1539
+ "appliesToVerbs": [
1540
+ "record-critic-verdict"
1541
+ ],
1542
+ "enumValues": [
1543
+ "OKAY",
1544
+ "ITERATE",
1545
+ "REJECT"
1546
+ ],
1547
+ "name": "verdict",
1548
+ "required": true,
1549
+ "type": "enum"
1550
+ },
1551
+ {
1552
+ "appliesToVerbs": [
1553
+ "record-critic-verdict"
1554
+ ],
1555
+ "name": "blockers-json",
1556
+ "type": "string"
1557
+ },
1558
+ {
1559
+ "appliesToVerbs": [
1560
+ "record-critic-verdict"
1561
+ ],
1562
+ "name": "goal-id",
1563
+ "type": "string"
1564
+ },
1565
+ {
1566
+ "appliesToVerbs": [
1567
+ "record-critic-verdict"
1568
+ ],
1569
+ "name": "classification-event-id",
1570
+ "type": "string"
1571
+ },
1524
1572
  {
1525
1573
  "appliesToVerbs": [
1526
1574
  "create-goals"
@@ -1743,6 +1791,8 @@
1743
1791
  "record-review-blockers",
1744
1792
  "steer",
1745
1793
  "classify-blocker",
1794
+ "record-critic-verdict",
1795
+ "record-critic-gate-override",
1746
1796
  "start-pipeline-overlap",
1747
1797
  "join-pipeline-overlap",
1748
1798
  "rebaseline-pipeline-overlap"
@@ -1830,6 +1880,14 @@
1830
1880
  "name": "classify-blocker",
1831
1881
  "surface": "command-positional"
1832
1882
  },
1883
+ {
1884
+ "name": "record-critic-verdict",
1885
+ "surface": "command-positional"
1886
+ },
1887
+ {
1888
+ "name": "record-critic-gate-override",
1889
+ "surface": "command-positional"
1890
+ },
1833
1891
  {
1834
1892
  "name": "start-pipeline-overlap",
1835
1893
  "surface": "command-positional"
@@ -255,6 +255,8 @@ export const WORKFLOW_MANIFEST: Record<CanonicalGjcWorkflowSkill, SkillManifest>
255
255
  "record-review-blockers",
256
256
  "steer",
257
257
  "classify-blocker",
258
+ "record-critic-verdict",
259
+ "record-critic-gate-override",
258
260
  "start-pipeline-overlap",
259
261
  "join-pipeline-overlap",
260
262
  "rebaseline-pipeline-overlap",
@@ -290,9 +292,28 @@ export const WORKFLOW_MANIFEST: Record<CanonicalGjcWorkflowSkill, SkillManifest>
290
292
  "record-review-blockers",
291
293
  "steer",
292
294
  "classify-blocker",
295
+ "record-critic-verdict",
296
+ "record-critic-gate-override",
293
297
  "rebaseline-pipeline-overlap",
294
298
  ],
295
299
  },
300
+ {
301
+ name: "terminus",
302
+ type: "enum",
303
+ enumValues: ["completion", "pause"],
304
+ required: true,
305
+ appliesToVerbs: ["record-critic-verdict"],
306
+ },
307
+ {
308
+ name: "verdict",
309
+ type: "enum",
310
+ enumValues: ["OKAY", "ITERATE", "REJECT"],
311
+ required: true,
312
+ appliesToVerbs: ["record-critic-verdict"],
313
+ },
314
+ { name: "blockers-json", type: "string", appliesToVerbs: ["record-critic-verdict"] },
315
+ { name: "goal-id", type: "string", appliesToVerbs: ["record-critic-verdict"] },
316
+ { name: "classification-event-id", type: "string", appliesToVerbs: ["record-critic-verdict"] },
296
317
  {
297
318
  name: "goal-metadata-json",
298
319
  type: "string",
@@ -403,6 +424,8 @@ export const WORKFLOW_MANIFEST: Record<CanonicalGjcWorkflowSkill, SkillManifest>
403
424
  "record-review-blockers",
404
425
  "steer",
405
426
  "classify-blocker",
427
+ "record-critic-verdict",
428
+ "record-critic-gate-override",
406
429
  "start-pipeline-overlap",
407
430
  "join-pipeline-overlap",
408
431
  "rebaseline-pipeline-overlap",