oh-my-opencode 5.0.0-beta.33 → 5.0.0-beta.35

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 (221) hide show
  1. package/.agents/skills/codex-qa/scripts/lsp-e2e.sh +1 -3
  2. package/.agents/skills/opencode-qa/scripts/lsp-e2e.sh +1 -1
  3. package/.agents/skills/tech-debt-audit/SKILL.md +6 -76
  4. package/README.ja.md +1 -1
  5. package/README.ko.md +1 -1
  6. package/README.md +1 -1
  7. package/README.ru.md +1 -1
  8. package/README.zh-cn.md +1 -1
  9. package/THIRD-PARTY-NOTICES.md +27 -99
  10. package/dist/cli/index.js +48350 -48559
  11. package/dist/cli-node/index.js +48480 -48689
  12. package/dist/config/index.d.ts +1 -1
  13. package/dist/config/schema/hooks.d.ts +0 -1
  14. package/dist/config/schema/oh-my-opencode-config.d.ts +0 -10
  15. package/dist/config/schema.d.ts +0 -1
  16. package/dist/create-hooks.d.ts +0 -1
  17. package/dist/hooks/index.d.ts +0 -1
  18. package/dist/index.js +11675 -13742
  19. package/dist/mcp/index.d.ts +0 -4
  20. package/dist/mcp/types.d.ts +0 -1
  21. package/dist/oh-my-opencode.schema.json +1 -45
  22. package/dist/plugin/hooks/create-core-hooks.d.ts +0 -1
  23. package/dist/plugin/hooks/create-session-hooks.d.ts +1 -2
  24. package/dist/shared/omo-process-sweep.d.ts +2 -4
  25. package/dist/skills/init-deep/SKILL.md +13 -13
  26. package/dist/skills/ulw-plan/SKILL.md +0 -1
  27. package/dist/tui.js +44809 -44910
  28. package/package.json +16 -18
  29. package/packages/omo-codex/THIRD-PARTY-NOTICES.md +0 -17
  30. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -3
  31. package/packages/omo-codex/plugin/.mcp.json +0 -6
  32. package/packages/omo-codex/plugin/AGENTS.md +5 -5
  33. package/packages/omo-codex/plugin/README.md +1 -1
  34. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +1 -15
  35. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  36. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  37. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  38. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  39. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  40. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  41. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  42. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  43. package/packages/omo-codex/plugin/components/lcx/skills/lcx-doctor/SKILL.md +1 -1
  44. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
  45. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  46. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  47. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  48. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  49. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  50. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  51. package/packages/omo-codex/plugin/components/teammode/test/thread-title-hook.test.ts +1 -1
  52. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  53. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  54. package/packages/omo-codex/plugin/components/ultrawork/dist/cli.js +2 -2
  55. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  56. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  57. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +0 -1
  58. package/packages/omo-codex/plugin/components/ultrawork/src/directive-content.ts +1 -1
  59. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +2 -2
  60. package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
  61. package/packages/omo-codex/plugin/components/ulw-loop/README.md +11 -1
  62. package/packages/omo-codex/plugin/components/ulw-loop/directive.md +2 -2
  63. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-codex-validation.d.ts +12 -0
  64. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-codex-validation.js +45 -0
  65. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-reconciliation.d.ts +1 -0
  66. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-reconciliation.js +5 -0
  67. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-template.js +10 -3
  68. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint.js +35 -38
  69. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +278 -193
  70. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-aggregate.d.ts +5 -0
  71. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-aggregate.js +7 -51
  72. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-artifacts.js +39 -22
  73. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-fields.d.ts +13 -1
  74. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-fields.js +64 -8
  75. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-verdicts.js +5 -1
  76. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate.js +37 -18
  77. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
  78. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  79. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +2 -1
  80. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint-codex-validation.ts +81 -0
  81. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint-reconciliation.ts +6 -0
  82. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint-template.ts +10 -3
  83. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint.ts +32 -63
  84. package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate-aggregate.ts +7 -53
  85. package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate-artifacts.ts +50 -31
  86. package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate-fields.ts +67 -14
  87. package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate-verdicts.ts +6 -1
  88. package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate.ts +36 -17
  89. package/packages/omo-codex/plugin/components/ulw-loop/test/checkpoint-final.test.ts +26 -0
  90. package/packages/omo-codex/plugin/components/ulw-loop/test/checkpoint-template.test.ts +47 -2
  91. package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate-aggregate-basics.test.ts +258 -0
  92. package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate-cap-and-dedupe.test.ts +22 -0
  93. package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate-poisoning-cascades.test.ts +97 -0
  94. package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate-single-report.test.ts +241 -0
  95. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  96. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  97. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  98. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  99. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  100. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  101. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  102. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  103. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  104. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  105. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  106. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  107. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  108. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  109. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  110. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  111. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-ulw-execute-continuation.json +1 -1
  112. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  113. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  114. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  115. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  116. package/packages/omo-codex/plugin/package-lock.json +12 -146
  117. package/packages/omo-codex/plugin/package.json +1 -2
  118. package/packages/omo-codex/plugin/scripts/AGENTS.md +0 -1
  119. package/packages/omo-codex/plugin/scripts/auto-update-plan.mjs +0 -1
  120. package/packages/omo-codex/plugin/scripts/auto-update.mjs +0 -9
  121. package/packages/omo-codex/plugin/scripts/canonical-ultrawork-directive.mjs +17 -0
  122. package/packages/omo-codex/plugin/scripts/hook-status-message.mjs +0 -1
  123. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +2 -8
  124. package/packages/omo-codex/plugin/shared/src/config-loader.ts +2 -102
  125. package/packages/omo-codex/plugin/shared/src/config-migration.ts +0 -1
  126. package/packages/omo-codex/plugin/shared/test/config-loader.test.ts +41 -130
  127. package/packages/omo-codex/plugin/skills/init-deep/SKILL.md +13 -13
  128. package/packages/omo-codex/plugin/skills/lcx-doctor/SKILL.md +1 -1
  129. package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +2 -2
  130. package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +2 -1
  131. package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +0 -1
  132. package/packages/omo-codex/plugin/test/AGENTS.md +2 -2
  133. package/packages/omo-codex/plugin/test/aggregate-hooks.test.mjs +2 -21
  134. package/packages/omo-codex/plugin/test/aggregate-manifest.test.mjs +1 -2
  135. package/packages/omo-codex/plugin/test/aggregate-mcp.test.mjs +2 -7
  136. package/packages/omo-codex/plugin/test/auto-update.test.mjs +0 -25
  137. package/packages/omo-codex/plugin/test/bootstrap-setup.test.mjs +0 -31
  138. package/packages/omo-codex/plugin/test/canonical-ultrawork-directive.test.mjs +53 -0
  139. package/packages/omo-codex/plugin/test/component-bundled-cli.test.mjs +0 -50
  140. package/packages/omo-codex/plugin/test/hook-status-message.test.mjs +0 -1
  141. package/packages/omo-codex/plugin/test/mcp-research-servers.test.mjs +1 -3
  142. package/packages/omo-codex/scripts/install-cache-copy.test.mjs +27 -1
  143. package/packages/omo-codex/scripts/install-config-git-bash.test.mjs +0 -23
  144. package/packages/omo-codex/scripts/install-dist/install-local.mjs +238 -431
  145. package/packages/omo-codex/scripts/install-git-bash-mcp-env.test.mjs +1 -25
  146. package/packages/omo-codex/scripts/install-mcp-runtime.test.mjs +0 -36
  147. package/packages/shared-skills/skills/init-deep/SKILL.md +13 -13
  148. package/packages/shared-skills/skills/ulw-plan/SKILL.md +0 -1
  149. package/dist/config/schema/codegraph.d.ts +0 -12
  150. package/dist/hooks/codegraph-bootstrap/command-runner.d.ts +0 -17
  151. package/dist/hooks/codegraph-bootstrap/hook.d.ts +0 -35
  152. package/dist/hooks/codegraph-bootstrap/index.d.ts +0 -1
  153. package/dist/hooks/codegraph-bootstrap/project-root.d.ts +0 -1
  154. package/dist/hooks/codegraph-bootstrap/status.d.ts +0 -11
  155. package/dist/mcp/codegraph.d.ts +0 -16
  156. package/packages/omo-codex/plugin/components/codegraph/AGENTS.md +0 -58
  157. package/packages/omo-codex/plugin/components/codegraph/LICENSE +0 -21
  158. package/packages/omo-codex/plugin/components/codegraph/NODE-RUNTIME-LICENSES.md +0 -2951
  159. package/packages/omo-codex/plugin/components/codegraph/NOTICE +0 -16
  160. package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +0 -12326
  161. package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +0 -9908
  162. package/packages/omo-codex/plugin/components/codegraph/package.json +0 -33
  163. package/packages/omo-codex/plugin/components/codegraph/src/cache-gc.ts +0 -29
  164. package/packages/omo-codex/plugin/components/codegraph/src/cli.ts +0 -79
  165. package/packages/omo-codex/plugin/components/codegraph/src/hook-input.ts +0 -33
  166. package/packages/omo-codex/plugin/components/codegraph/src/hook-sweep.ts +0 -25
  167. package/packages/omo-codex/plugin/components/codegraph/src/hook-types.ts +0 -131
  168. package/packages/omo-codex/plugin/components/codegraph/src/hook.ts +0 -258
  169. package/packages/omo-codex/plugin/components/codegraph/src/mcp-bridge.ts +0 -310
  170. package/packages/omo-codex/plugin/components/codegraph/src/mcp-unavailable.ts +0 -92
  171. package/packages/omo-codex/plugin/components/codegraph/src/post-tool-use-hook.ts +0 -34
  172. package/packages/omo-codex/plugin/components/codegraph/src/serve-invocation.ts +0 -29
  173. package/packages/omo-codex/plugin/components/codegraph/src/serve.ts +0 -263
  174. package/packages/omo-codex/plugin/components/codegraph/src/session-start-command.ts +0 -106
  175. package/packages/omo-codex/plugin/components/codegraph/src/session-start-cooldown.ts +0 -145
  176. package/packages/omo-codex/plugin/components/codegraph/src/session-start-hook-runtime.ts +0 -22
  177. package/packages/omo-codex/plugin/components/codegraph/src/session-start-lock.ts +0 -139
  178. package/packages/omo-codex/plugin/components/codegraph/src/session-start-outcome.ts +0 -15
  179. package/packages/omo-codex/plugin/components/codegraph/src/session-start-paths.ts +0 -32
  180. package/packages/omo-codex/plugin/components/codegraph/src/session-start-project.ts +0 -109
  181. package/packages/omo-codex/plugin/components/codegraph/src/session-start-worker-result.ts +0 -148
  182. package/packages/omo-codex/plugin/components/codegraph/src/session-start-worker.ts +0 -175
  183. package/packages/omo-codex/plugin/components/codegraph/src/sweep-cli.ts +0 -81
  184. package/packages/omo-codex/plugin/components/codegraph/test/cache-gc.test.ts +0 -34
  185. package/packages/omo-codex/plugin/components/codegraph/test/hook-exclusion.test.ts +0 -112
  186. package/packages/omo-codex/plugin/components/codegraph/test/hook-registration.test.ts +0 -26
  187. package/packages/omo-codex/plugin/components/codegraph/test/hook-session-start-guard.test.ts +0 -160
  188. package/packages/omo-codex/plugin/components/codegraph/test/hook-store-upgrade.test.ts +0 -50
  189. package/packages/omo-codex/plugin/components/codegraph/test/hook-sweep.test.ts +0 -29
  190. package/packages/omo-codex/plugin/components/codegraph/test/hook.test.ts +0 -320
  191. package/packages/omo-codex/plugin/components/codegraph/test/mcp-bridge-fixtures.ts +0 -172
  192. package/packages/omo-codex/plugin/components/codegraph/test/package-runtime.test.ts +0 -91
  193. package/packages/omo-codex/plugin/components/codegraph/test/provisioned-node-guard.test.ts +0 -100
  194. package/packages/omo-codex/plugin/components/codegraph/test/serve-built-wrapper.test.ts +0 -76
  195. package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge-lifecycle.test.ts +0 -133
  196. package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge.test.ts +0 -245
  197. package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-facade.test.ts +0 -69
  198. package/packages/omo-codex/plugin/components/codegraph/test/serve-node-support.test.ts +0 -43
  199. package/packages/omo-codex/plugin/components/codegraph/test/serve-provision.test.ts +0 -147
  200. package/packages/omo-codex/plugin/components/codegraph/test/serve-unavailable-idle.test.ts +0 -47
  201. package/packages/omo-codex/plugin/components/codegraph/test/serve-unavailable.test.ts +0 -175
  202. package/packages/omo-codex/plugin/components/codegraph/test/serve.test.ts +0 -385
  203. package/packages/omo-codex/plugin/components/codegraph/test/session-start-node-support.test.ts +0 -206
  204. package/packages/omo-codex/plugin/components/codegraph/test/session-start-project.test.ts +0 -58
  205. package/packages/omo-codex/plugin/components/codegraph/test/session-start-state.test.ts +0 -104
  206. package/packages/omo-codex/plugin/components/codegraph/test/session-start-trust-boundary.test.ts +0 -65
  207. package/packages/omo-codex/plugin/components/codegraph/test/session-start-worker-availability.test.ts +0 -106
  208. package/packages/omo-codex/plugin/components/codegraph/test/session-start-worker-cooldown.test.ts +0 -116
  209. package/packages/omo-codex/plugin/components/codegraph/test/session-start-worker-flow.test.ts +0 -258
  210. package/packages/omo-codex/plugin/components/codegraph/test/sweep-cli.test.ts +0 -56
  211. package/packages/omo-codex/plugin/components/codegraph/tsconfig.build.json +0 -13
  212. package/packages/omo-codex/plugin/components/codegraph/tsconfig.json +0 -25
  213. package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate-aggregate.test.ts +0 -101
  214. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +0 -18
  215. package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +0 -17
  216. package/packages/omo-codex/plugin/scripts/migrate-omo-sot/editor.mjs +0 -125
  217. package/packages/omo-codex/plugin/scripts/migrate-omo-sot/jsonc.mjs +0 -43
  218. package/packages/omo-codex/plugin/scripts/migrate-omo-sot/scaffold.mjs +0 -29
  219. package/packages/omo-codex/plugin/scripts/migrate-omo-sot.mjs +0 -121
  220. package/packages/omo-codex/plugin/test/component-codegraph-mcp-smoke.test.mjs +0 -76
  221. package/packages/omo-codex/plugin/test/migrate-omo-sot.test.mjs +0 -179
@@ -3,7 +3,7 @@
3
3
  // components/ulw-loop/src/checkpoint.ts
4
4
  import { existsSync as existsSync4, statSync } from "node:fs";
5
5
  import { readFile as readFile5 } from "node:fs/promises";
6
- import { resolve as resolve4 } from "node:path";
6
+ import { resolve as resolve3 } from "node:path";
7
7
 
8
8
  // components/ulw-loop/src/checkpoint-reconciliation.ts
9
9
  import { existsSync as existsSync2 } from "node:fs";
@@ -379,6 +379,44 @@ function buildTaskScopedAggregateReconciliationHint(goal, final) {
379
379
  return ` Completed task-scoped aggregate reconciliation requires the checkpoint goal to be the active in-progress OMO goal, evidence that names that active OMO goal id, names .omo/ulw-loop/goals.json or ledger.jsonl, includes completed implementation plus validation/review evidence, and a get_goal objective that maps to the ulw-loop brief/artifact. ${buildCompletedLegacyGoalRemediation(goal)}`;
380
380
  }
381
381
 
382
+ // components/ulw-loop/src/checkpoint-codex-validation.ts
383
+ function normalizeObjective3(value) {
384
+ return value.replace(/\s+/g, " ").trim();
385
+ }
386
+ async function validateCheckpointCodexGoal(input) {
387
+ const aggregate = codexGoalMode(input.plan) === "aggregate";
388
+ const final = isFinalRunCompletionCandidate(input.plan, input.goal);
389
+ const snapshot = await readCodexGoalSnapshotInput(input.raw, input.repoRoot);
390
+ const expectedObjective = expectedCodexObjective(input.plan, input.goal);
391
+ const reconciliation = reconcileCodexGoalSnapshot(snapshot, {
392
+ expectedObjective,
393
+ ...aggregate ? { acceptedObjectives: compatibleCodexObjectives(input.plan) } : {},
394
+ allowedStatuses: aggregate ? final ? ["complete"] : ["active"] : ["complete"],
395
+ requireSnapshot: true,
396
+ requireComplete: !aggregate || final
397
+ });
398
+ if (reconciliation.ok)
399
+ return reconciliation.snapshot.raw;
400
+ const objective = snapshot?.objective;
401
+ const mismatchedTaskObjective = snapshot?.available === true && objective !== undefined && normalizeObjective3(objective) !== normalizeObjective3(expectedObjective);
402
+ const completedTaskScoped = mismatchedTaskObjective && snapshot.status === "complete" && await canReconcileCompletedTaskScopedAggregateSnapshot(input.repoRoot, input.plan, input.goal, objective, input.evidence, input.scope);
403
+ const activeFinalTaskScoped = mismatchedTaskObjective && snapshot.status === "active" && await canReconcileActiveFinalTaskScopedAggregateSnapshot(input.repoRoot, input.plan, input.goal, objective, input.evidence, input.scope);
404
+ if (completedTaskScoped || activeFinalTaskScoped)
405
+ return reconciliation.snapshot.raw;
406
+ throw codexSnapshotMismatchError({
407
+ reconciliation,
408
+ snapshot,
409
+ expectedObjective,
410
+ taskScopedHint: { goal: input.goal, aggregate, final }
411
+ });
412
+ }
413
+ function combineCheckpointValidationErrors(codexError, gateError) {
414
+ return new UlwLoopError(`${codexError.message}
415
+ ${gateError.message}`, "ULW_LOOP_QUALITY_GATE_INVALID", {
416
+ details: { ...codexError.details ?? {}, ...gateError.details ?? {} }
417
+ });
418
+ }
419
+
382
420
  // components/ulw-loop/src/plan-io.ts
383
421
  import { createReadStream, readdirSync } from "node:fs";
384
422
  import { appendFile, mkdir, readFile as readFile3, rename, writeFile } from "node:fs/promises";
@@ -634,111 +672,113 @@ function requireEssentialCriteriaPass(goal) {
634
672
  });
635
673
  }
636
674
 
637
- // components/ulw-loop/src/quality-gate-aggregate.ts
675
+ // components/ulw-loop/src/quality-gate-artifacts.ts
638
676
  import { resolve as resolve2 } from "node:path";
639
677
 
640
678
  // components/ulw-loop/src/quality-gate-fields.ts
641
679
  var PLACEHOLDER_PATTERN = /^(?:<replace:[^>]+>|placeholder|todo|tbd|n\/a|stub)$/i;
680
+ var activeCollector;
681
+ function withQualityGateCollector(operation) {
682
+ const previous = activeCollector;
683
+ const collector = { defects: [], poisonedFields: new Set, poisonedArtifactKinds: new Set };
684
+ activeCollector = collector;
685
+ try {
686
+ const result = operation();
687
+ if (collector.defects.length === 0)
688
+ return result;
689
+ throwQualityGateDefects(collector.defects);
690
+ } finally {
691
+ activeCollector = previous;
692
+ }
693
+ }
694
+ function throwQualityGateDefects(defects) {
695
+ const uniqueDefects = [
696
+ ...new Map(defects.map((defect) => [`${defect.field}\x00${defect.message}`, defect])).values()
697
+ ];
698
+ const fields = uniqueDefects.slice(0, 25);
699
+ const truncated = uniqueDefects.length > fields.length;
700
+ const message = [
701
+ `Final quality gate has ${fields.length}${truncated ? "+" : ""} validation defects:`,
702
+ ...fields.map((item) => `- ${item.field}: ${item.message}`)
703
+ ].join(`
704
+ `);
705
+ throw new UlwLoopError(message, "ULW_LOOP_QUALITY_GATE_INVALID", {
706
+ details: { field: fields[0]?.field, fields, ...truncated ? { truncated: true } : {} }
707
+ });
708
+ }
642
709
  function invalid(message, field) {
643
- throw new UlwLoopError(message, "ULW_LOOP_QUALITY_GATE_INVALID", { details: { field } });
710
+ if (activeCollector === undefined)
711
+ throw new UlwLoopError(message, "ULW_LOOP_QUALITY_GATE_INVALID", { details: { field } });
712
+ activeCollector.defects.push({ field, message });
713
+ activeCollector.poisonedFields.add(field);
714
+ return;
715
+ }
716
+ function isPoisoned(field) {
717
+ return activeCollector?.poisonedFields.has(field) ?? false;
718
+ }
719
+ function markPoisonedArtifactKind(id) {
720
+ activeCollector?.poisonedArtifactKinds.add(id);
721
+ }
722
+ function isPoisonedArtifactKind(id) {
723
+ return activeCollector?.poisonedArtifactKinds.has(id) ?? false;
644
724
  }
645
725
  function isRecord(value) {
646
726
  return typeof value === "object" && value !== null && !Array.isArray(value);
647
727
  }
648
728
  function section(value, field) {
649
- return isRecord(value) ? value : invalid(`Final quality gate is missing ${field} evidence.`, field);
729
+ if (isRecord(value))
730
+ return value;
731
+ invalid(`Final quality gate is missing ${field} evidence.`, field);
732
+ return {};
650
733
  }
651
734
  function textField(value, field) {
652
- if (typeof value !== "string" || value.trim() === "")
735
+ if (typeof value !== "string" || value.trim() === "") {
653
736
  invalid(`Final quality gate requires non-empty ${field}.`, field);
737
+ return "";
738
+ }
654
739
  const trimmed = value.trim();
655
740
  if (PLACEHOLDER_PATTERN.test(trimmed))
656
741
  invalid(`Final quality gate rejects placeholder ${field}.`, field);
657
742
  return trimmed;
658
743
  }
659
744
  function numberField(value, field) {
660
- return typeof value === "number" && Number.isFinite(value) ? value : invalid(`Final quality gate requires numeric ${field}.`, field);
745
+ if (typeof value === "number" && Number.isFinite(value))
746
+ return value;
747
+ invalid(`Final quality gate requires numeric ${field}.`, field);
748
+ return 0;
661
749
  }
662
750
  function stringArray(value, field) {
663
- if (!Array.isArray(value) || value.length === 0)
664
- return invalid(`Final quality gate requires ${field}.`, field);
751
+ if (!Array.isArray(value) || value.length === 0) {
752
+ invalid(`Final quality gate requires ${field}.`, field);
753
+ return [];
754
+ }
665
755
  return value.map((item) => textField(item, field));
666
756
  }
667
757
  function emptyBlockers(value, field) {
668
758
  if (Array.isArray(value) && value.length === 0)
669
759
  return [];
670
760
  invalid(`${field} must be empty.`, field);
761
+ return [];
671
762
  }
672
763
  function literal(value, expected, field) {
673
764
  if (value === expected)
674
765
  return expected;
675
766
  invalid(`${field} must be ${String(expected)}.`, field);
676
- }
677
-
678
- // components/ulw-loop/src/quality-gate-aggregate.ts
679
- var PLACEHOLDER = /^(?:<replace:[^>]+>|placeholder|todo|tbd|n\/a|stub)$/i;
680
- function add(defects, field, message) {
681
- defects.push({ field, message });
682
- }
683
- function text(value, field, defects) {
684
- if (typeof value !== "string" || value.trim() === "")
685
- add(defects, field, `Final quality gate requires non-empty ${field}.`);
686
- else if (PLACEHOLDER.test(value.trim()))
687
- add(defects, field, `Final quality gate rejects placeholder ${field}.`);
688
- }
689
- function section2(value, field, defects) {
690
- if (!isRecord(value)) {
691
- add(defects, field, `Final quality gate is missing ${field} evidence.`);
692
- return {};
693
- }
694
- return value;
695
- }
696
- function aggregateQualityGateDefects(input, opts) {
697
- if (!isRecord(input))
698
- return;
699
- const defects = [];
700
- const gate = input;
701
- const manual = section2(gate["manualQa"], "manualQa", defects);
702
- const review = section2(gate["gateReview"], "gateReview", defects);
703
- text(manual["evidence"], "manualQa.evidence", defects);
704
- text(review["evidence"], "gateReview.evidence", defects);
705
- if (review["recommendation"] !== "APPROVE")
706
- add(defects, "gateReview.recommendation", "gateReview.recommendation must be APPROVE.");
707
- const artifacts = Array.isArray(manual["artifactRefs"]) ? manual["artifactRefs"] : [];
708
- for (const [index, item] of artifacts.entries()) {
709
- if (!isRecord(item))
710
- continue;
711
- const path = item["path"];
712
- if (typeof path !== "string" || path.trim() === "")
713
- continue;
714
- if (opts?.repoRoot !== undefined && opts.fs !== undefined && !opts.fs.existsSync(resolve2(opts.repoRoot, path)))
715
- add(defects, `manualQa.artifactRefs[${index}].path`, `manualQa.artifactRefs[${index}].path must point to an existing artifact.`);
716
- }
717
- if (defects.length === 0)
718
- return;
719
- const truncated = defects.length > 25;
720
- const fields = defects.slice(0, 25).map(({ field, message: message2 }) => ({ field, message: message2 }));
721
- const message = [
722
- `Final quality gate has ${fields.length}${truncated ? "+" : ""} validation defects:`,
723
- ...fields.map((item) => `- ${item.field}: ${item.message}`)
724
- ].join(`
725
- `);
726
- throw new UlwLoopError(message, "ULW_LOOP_QUALITY_GATE_INVALID", {
727
- details: { field: fields[0]?.field, fields, ...truncated ? { truncated: true } : {} }
728
- });
767
+ return expected;
729
768
  }
730
769
 
731
770
  // components/ulw-loop/src/quality-gate-artifacts.ts
732
- import { resolve as resolve3 } from "node:path";
733
771
  function surfaceField(value, field) {
734
772
  if (value === "cli" || value === "http" || value === "tmux" || value === "browser" || value === "gui" || value === "data")
735
773
  return value;
736
774
  invalid(`${field} must be a supported manual QA surface.`, field);
775
+ return "cli";
737
776
  }
738
777
  function kindField(value, field) {
739
778
  if (value === "cli-transcript" || value === "log" || value === "screenshot" || value === "image" || value === "http-dump" || value === "data-diff")
740
779
  return value;
741
780
  invalid(`${field} must be a supported artifact kind.`, field);
781
+ return "log";
742
782
  }
743
783
  function artifactCompatible(surface, kind) {
744
784
  switch (surface) {
@@ -754,21 +794,21 @@ function artifactCompatible(surface, kind) {
754
794
  return kind === "data-diff";
755
795
  default:
756
796
  invalid("manualQa.surfaceEvidence has an unsupported surface.", "manualQa.surfaceEvidence.surface");
797
+ return false;
757
798
  }
758
799
  }
759
800
  function checkFile(path, field, opts) {
760
- if (opts?.repoRoot === undefined || opts.fs === undefined)
801
+ if (opts?.repoRoot === undefined || opts.fs === undefined || isPoisoned(field))
761
802
  return;
762
- const absolute = resolve3(opts.repoRoot, path);
763
- if (!opts.fs.existsSync(absolute))
803
+ const absolute = resolve2(opts.repoRoot, path);
804
+ if (!opts.fs.existsSync(absolute)) {
764
805
  invalid(`${field} must point to an existing artifact.`, field);
806
+ return;
807
+ }
765
808
  if (opts.fs.statSync(absolute).size <= 0)
766
809
  invalid(`${field} must point to a non-empty artifact.`, field);
767
- if (opts.currentAttemptDir !== undefined && opts.repoRoot !== undefined) {
768
- const attemptRoot = resolve3(opts.repoRoot, opts.currentAttemptDir);
769
- if (!isWithinAttemptDir(absolute, attemptRoot))
770
- invalid(`${field} (${path}) must point to an artifact from the current attempt (${opts.currentAttemptDir}).`, field);
771
- }
810
+ if (opts.currentAttemptDir !== undefined && !isWithinAttemptDir(absolute, resolve2(opts.repoRoot, opts.currentAttemptDir)))
811
+ invalid(`${field} (${path}) must point to an artifact from the current attempt (${opts.currentAttemptDir}).`, field);
772
812
  }
773
813
  function artifactMap(refs) {
774
814
  const byId = new Map;
@@ -780,39 +820,56 @@ function artifactMap(refs) {
780
820
  return byId;
781
821
  }
782
822
  function parseArtifactRefs(value, opts) {
783
- if (!Array.isArray(value) || value.length === 0)
823
+ if (!Array.isArray(value) || value.length === 0) {
784
824
  invalid("manualQa.artifactRefs must not be empty.", "manualQa.artifactRefs");
785
- return value.map((item, index) => {
786
- const ref = section(item, `manualQa.artifactRefs[${index}]`);
787
- const path = textField(ref["path"], `manualQa.artifactRefs[${index}].path`);
788
- checkFile(path, `manualQa.artifactRefs[${index}].path`, opts);
789
- return {
790
- id: textField(ref["id"], `manualQa.artifactRefs[${index}].id`),
791
- kind: kindField(ref["kind"], `manualQa.artifactRefs[${index}].kind`),
792
- description: textField(ref["description"], `manualQa.artifactRefs[${index}].description`),
793
- path
794
- };
825
+ return [];
826
+ }
827
+ return value.flatMap((item, index) => {
828
+ const prefix = `manualQa.artifactRefs[${index}]`;
829
+ const ref = section(item, prefix);
830
+ if (isPoisoned(prefix))
831
+ return [];
832
+ const pathField = `${prefix}.path`;
833
+ const idField = `${prefix}.id`;
834
+ const kindFieldName = `${prefix}.kind`;
835
+ const descriptionField = `${prefix}.description`;
836
+ const path = textField(ref["path"], pathField);
837
+ const id = textField(ref["id"], idField);
838
+ const kind = kindField(ref["kind"], kindFieldName);
839
+ if (isPoisoned(kindFieldName))
840
+ markPoisonedArtifactKind(id);
841
+ const description = textField(ref["description"], descriptionField);
842
+ checkFile(path, pathField, opts);
843
+ return [{ id, kind, description, path }];
795
844
  });
796
845
  }
797
846
  function referencedArtifacts(value, field, byId) {
798
- return stringArray(value, field).map((id) => {
847
+ const ids = stringArray(value, field);
848
+ if (isPoisoned(field))
849
+ return [];
850
+ return ids.flatMap((id) => {
799
851
  const artifact = byId.get(id);
800
- if (artifact === undefined)
852
+ if (artifact === undefined) {
801
853
  invalid(`${field} references unknown artifact ${id}.`, field);
802
- return artifact;
854
+ return [];
855
+ }
856
+ return [artifact];
803
857
  });
804
858
  }
805
859
 
806
860
  // components/ulw-loop/src/quality-gate-verdicts.ts
807
861
  function passedVerdict(value, field) {
808
- if (value === "not_applicable")
862
+ if (value === "not_applicable") {
809
863
  invalid(`${field} must not be not_applicable.`, field);
864
+ return "passed";
865
+ }
810
866
  return literal(value, "passed", field);
811
867
  }
812
868
  function codeQualityStatusField(value, field) {
813
869
  if (value === "CLEAR" || value === "WATCH")
814
870
  return value;
815
871
  invalid(`${field} must be CLEAR or WATCH.`, field);
872
+ return "CLEAR";
816
873
  }
817
874
  function adversarialVerdict(row, field) {
818
875
  const value = row["verdict"];
@@ -822,6 +879,7 @@ function adversarialVerdict(row, field) {
822
879
  return { verdict: "not_applicable", reason: textField(row["reason"], `${field}.reason`) };
823
880
  }
824
881
  invalid(`${field} must be passed or not_applicable with a reason.`, field);
882
+ return { verdict: "passed" };
825
883
  }
826
884
 
827
885
  // components/ulw-loop/src/surface.ts
@@ -841,10 +899,6 @@ var REVIEWER_ROLES_BY_SURFACE = {
841
899
  }
842
900
  };
843
901
  var GATE_REVIEWER_AGENT_NAMES = new Set(Object.values(REVIEWER_ROLES_BY_SURFACE).map((roles) => roles.gateReview));
844
- var REQUIRED_GATE_SECTIONS_BY_SURFACE = {
845
- lazycodex: ["codeReview", "manualQa", "gateReview", "iteration", "criteriaCoverage"],
846
- "omo-senpi": ["manualQa", "gateReview", "iteration", "criteriaCoverage"]
847
- };
848
902
  var GATE_SECTION_BY_ACCEPTOR = {
849
903
  lazycodex: {
850
904
  codeReview: [REVIEWER_ROLES_BY_SURFACE.lazycodex.codeReview],
@@ -930,6 +984,10 @@ function clearGoalBlockerFields(goal) {
930
984
 
931
985
  // components/ulw-loop/src/quality-gate.ts
932
986
  function reviewerRoleField(value, expected, field) {
987
+ if (typeof value !== "string" || value.trim() === "") {
988
+ textField(value, field);
989
+ return expected;
990
+ }
933
991
  const actual = textField(value, field);
934
992
  if (actual !== expected)
935
993
  invalid(`${field} must be ${expected}.`, field);
@@ -937,44 +995,51 @@ function reviewerRoleField(value, expected, field) {
937
995
  }
938
996
  function reviewerAcceptorField(value, surface, sectionName) {
939
997
  const field = `${sectionName}.by`;
940
- const actual = textField(value, field);
941
998
  const accepted = GATE_SECTION_BY_ACCEPTOR[surface][sectionName];
999
+ if (typeof value !== "string" || value.trim() === "") {
1000
+ textField(value, field);
1001
+ return accepted?.[0] ?? "";
1002
+ }
1003
+ const actual = textField(value, field);
942
1004
  if (accepted === undefined || !accepted.includes(actual))
943
1005
  invalid(`${field} must be one of ${accepted?.join(", ") ?? "the configured reviewers"}.`, field);
944
1006
  return actual;
945
1007
  }
946
1008
  function validateQualityGate(input, opts) {
1009
+ return withQualityGateCollector(() => validateQualityGateUncollected(input, opts));
1010
+ }
1011
+ function validateQualityGateUncollected(input, opts) {
947
1012
  const surface = opts?.reviewerSurface ?? "lazycodex";
948
- aggregateQualityGateDefects(input, opts);
949
1013
  const gate = section(input, "qualityGate");
950
- const requiredSections = REQUIRED_GATE_SECTIONS_BY_SURFACE[surface];
951
- for (const sectionName of requiredSections)
952
- section(gate[sectionName], sectionName);
953
1014
  if (surface === "omo-senpi" && gate["codeReview"] !== undefined)
954
1015
  invalid("omo-senpi gate has no codeReview lane.", "codeReview");
955
1016
  const manualQa = section(gate["manualQa"], "manualQa");
956
1017
  const gateReview = section(gate["gateReview"], "gateReview");
957
1018
  const iteration = section(gate["iteration"], "iteration");
958
1019
  const coverage = section(gate["criteriaCoverage"], "criteriaCoverage");
1020
+ const codeReview = surface === "lazycodex" ? section(gate["codeReview"], "codeReview") : {};
959
1021
  const manualQaBy = reviewerAcceptorField(manualQa["by"], surface, "manualQa");
960
1022
  const gateReviewBy = reviewerAcceptorField(gateReview["by"], surface, "gateReview");
1023
+ const manualQaEvidence = textField(manualQa["evidence"], "manualQa.evidence");
1024
+ const gateReviewEvidence = textField(gateReview["evidence"], "gateReview.evidence");
961
1025
  if (surface === "lazycodex")
962
- reviewerRoleField(section(gate["codeReview"], "codeReview")["by"], "lazycodex-code-reviewer", "codeReview.by");
1026
+ reviewerRoleField(codeReview?.["by"], "lazycodex-code-reviewer", "codeReview.by");
963
1027
  const totalCriteria = numberField(coverage["totalCriteria"], "criteriaCoverage.totalCriteria");
964
1028
  const passCount = numberField(coverage["passCount"], "criteriaCoverage.passCount");
965
- if (passCount < totalCriteria)
1029
+ if (!isPoisoned("criteriaCoverage.passCount") && passCount < totalCriteria)
966
1030
  invalid("criteriaCoverage.passCount must cover totalCriteria.", "criteriaCoverage.passCount");
967
1031
  const artifactRefs = parseArtifactRefs(manualQa["artifactRefs"], opts);
968
1032
  const byId = artifactMap(artifactRefs);
969
1033
  const surfaceEvidence = parseSurfaceEvidence(manualQa["surfaceEvidence"], byId);
970
1034
  const adversarialCases = parseAdversarialCases(manualQa["adversarialCases"], byId);
971
1035
  const gateReportPath = textField(gateReview["reportPath"], "gateReview.reportPath");
972
- checkFile(gateReportPath, "gateReview.reportPath", opts);
1036
+ if (!isPoisoned("gateReview.reportPath"))
1037
+ checkFile(gateReportPath, "gateReview.reportPath", opts);
973
1038
  const common = {
974
1039
  manualQa: {
975
1040
  by: manualQaBy,
976
1041
  status: literal(manualQa["status"], "passed", "manualQa.status"),
977
- evidence: textField(manualQa["evidence"], "manualQa.evidence"),
1042
+ evidence: manualQaEvidence,
978
1043
  surfaceEvidence,
979
1044
  adversarialCases,
980
1045
  artifactRefs
@@ -983,7 +1048,7 @@ function validateQualityGate(input, opts) {
983
1048
  by: gateReviewBy,
984
1049
  recommendation: literal(gateReview["recommendation"], "APPROVE", "gateReview.recommendation"),
985
1050
  reportPath: gateReportPath,
986
- evidence: textField(gateReview["evidence"], "gateReview.evidence"),
1051
+ evidence: gateReviewEvidence,
987
1052
  blockers: emptyBlockers(gateReview["blockers"], "gateReview.blockers")
988
1053
  },
989
1054
  iteration: {
@@ -1003,7 +1068,6 @@ function validateQualityGate(input, opts) {
1003
1068
  };
1004
1069
  if (surface === "omo-senpi")
1005
1070
  return { surface, ...common };
1006
- const codeReview = section(gate["codeReview"], "codeReview");
1007
1071
  const codeReportPath = textField(codeReview["reportPath"], "codeReview.reportPath");
1008
1072
  checkFile(codeReportPath, "codeReview.reportPath", opts);
1009
1073
  return {
@@ -1020,13 +1084,19 @@ function validateQualityGate(input, opts) {
1020
1084
  };
1021
1085
  }
1022
1086
  function parseSurfaceEvidence(value, byId) {
1023
- if (!Array.isArray(value) || value.length === 0)
1087
+ if (!Array.isArray(value) || value.length === 0) {
1024
1088
  invalid("manualQa.surfaceEvidence must not be empty.", "manualQa.surfaceEvidence");
1025
- return value.map((item, index) => {
1089
+ return [];
1090
+ }
1091
+ return value.flatMap((item, index) => {
1026
1092
  const row = section(item, `manualQa.surfaceEvidence[${index}]`);
1093
+ if (isPoisoned(`manualQa.surfaceEvidence[${index}]`))
1094
+ return [];
1027
1095
  const surface = surfaceField(row["surface"], `manualQa.surfaceEvidence[${index}].surface`);
1028
1096
  const artifacts = referencedArtifacts(row["artifactRefs"], `manualQa.surfaceEvidence[${index}].artifactRefs`, byId);
1029
1097
  for (const artifact of artifacts) {
1098
+ if (isPoisoned(`manualQa.surfaceEvidence[${index}].surface`) || isPoisonedArtifactKind(artifact.id))
1099
+ continue;
1030
1100
  if (!artifactCompatible(surface, artifact.kind)) {
1031
1101
  invalid(`manualQa.surfaceEvidence ${surface} artifact ${artifact.kind} is incompatible.`, "manualQa.surfaceEvidence");
1032
1102
  }
@@ -1042,10 +1112,14 @@ function parseSurfaceEvidence(value, byId) {
1042
1112
  });
1043
1113
  }
1044
1114
  function parseAdversarialCases(value, byId) {
1045
- if (!Array.isArray(value) || value.length === 0)
1115
+ if (!Array.isArray(value) || value.length === 0) {
1046
1116
  invalid("manualQa.adversarialCases must not be empty.", "manualQa.adversarialCases");
1047
- return value.map((item, index) => {
1117
+ return [];
1118
+ }
1119
+ return value.flatMap((item, index) => {
1048
1120
  const row = section(item, `manualQa.adversarialCases[${index}]`);
1121
+ if (isPoisoned(`manualQa.adversarialCases[${index}]`))
1122
+ return [];
1049
1123
  const artifacts = referencedArtifacts(row["artifactRefs"], `manualQa.adversarialCases[${index}].artifactRefs`, byId);
1050
1124
  const verdictInfo = adversarialVerdict(row, `manualQa.adversarialCases[${index}]`);
1051
1125
  return {
@@ -1158,7 +1232,7 @@ function isObject(value) {
1158
1232
  function read(value, key) {
1159
1233
  return Object.entries(value).find(([name]) => name === key)?.[1];
1160
1234
  }
1161
- function text2(value, key) {
1235
+ function text(value, key) {
1162
1236
  const candidate = read(value, key);
1163
1237
  return typeof candidate === "string" && candidate.trim().length > 0 ? candidate.trim() : undefined;
1164
1238
  }
@@ -1179,9 +1253,9 @@ async function parseValidationBatches(input, goals) {
1179
1253
  function batchFromObject(value) {
1180
1254
  if (!isObject(value))
1181
1255
  fail("validation batch entries must be objects.");
1182
- const batchId = text2(value, "batchId");
1256
+ const batchId = text(value, "batchId");
1183
1257
  const memberIds = strings(value, "memberIds");
1184
- const finalGoalId = text2(value, "finalGoalId");
1258
+ const finalGoalId = text(value, "finalGoalId");
1185
1259
  if (batchId === undefined)
1186
1260
  fail("validation batch requires batchId.");
1187
1261
  if (memberIds === undefined || memberIds.length < 2)
@@ -1274,9 +1348,6 @@ var QUALITY_GATE_FS = { existsSync: existsSync4, statSync };
1274
1348
  function ulwLoopFail2(message, code) {
1275
1349
  throw new UlwLoopError(message, code);
1276
1350
  }
1277
- function normalizeObjective3(value) {
1278
- return value.replace(/\s+/g, " ").trim();
1279
- }
1280
1351
  function nonEmptyEvidence2(value) {
1281
1352
  const trimmed = value.trim();
1282
1353
  return trimmed || ulwLoopFail2("Evidence must be a non-empty string.", "ulw_loop_evidence_required");
@@ -1295,7 +1366,7 @@ async function readJsonInput2(raw, repoRoot) {
1295
1366
  if (!(error instanceof SyntaxError))
1296
1367
  throw error;
1297
1368
  }
1298
- const path = resolve4(repoRoot, trimmed);
1369
+ const path = resolve3(repoRoot, trimmed);
1299
1370
  if (!existsSync4(path))
1300
1371
  return ulwLoopFail2("Quality gate JSON is neither valid JSON nor a readable path.", "ulw_loop_json_input_invalid");
1301
1372
  try {
@@ -1380,23 +1451,20 @@ async function checkpointUlwLoop(repoRoot, args, scope) {
1380
1451
  requireEssentialCriteriaPass(goal);
1381
1452
  else
1382
1453
  requireAllCriteriaPass(goal);
1383
- const snapshot = await readCodexGoalSnapshotInput(args.codexGoalJson, repoRoot);
1384
- const reconciliation = reconcileCodexGoalSnapshot(snapshot, {
1385
- expectedObjective: expectedCodexObjective(plan, goal),
1386
- ...aggregate ? { acceptedObjectives: compatibleCodexObjectives(plan) } : {},
1387
- allowedStatuses: aggregate ? final ? ["complete"] : ["active"] : ["complete"],
1388
- requireSnapshot: true,
1389
- requireComplete: !aggregate || final
1390
- });
1391
- codexGoal = reconciliation.snapshot.raw;
1392
- if (!reconciliation.ok) {
1393
- const objective = snapshot?.objective;
1394
- const mismatchedTaskObjective = snapshot?.available === true && objective !== undefined && normalizeObjective3(objective) !== normalizeObjective3(expectedCodexObjective(plan, goal));
1395
- const completedTaskScoped = mismatchedTaskObjective && snapshot.status === "complete" && await canReconcileCompletedTaskScopedAggregateSnapshot(repoRoot, plan, goal, objective, evidence, scope);
1396
- const activeFinalTaskScoped = mismatchedTaskObjective && snapshot.status === "active" && await canReconcileActiveFinalTaskScopedAggregateSnapshot(repoRoot, plan, goal, objective, evidence, scope);
1397
- const taskScoped = completedTaskScoped || activeFinalTaskScoped;
1398
- if (!taskScoped)
1399
- throw codexSnapshotMismatchError({ reconciliation, snapshot, expectedObjective: expectedCodexObjective(plan, goal), taskScopedHint: { goal, aggregate, final } });
1454
+ let codexValidationError;
1455
+ try {
1456
+ codexGoal = await validateCheckpointCodexGoal({
1457
+ repoRoot,
1458
+ plan,
1459
+ goal,
1460
+ raw: args.codexGoalJson,
1461
+ evidence,
1462
+ ...scope === undefined ? {} : { scope }
1463
+ });
1464
+ } catch (error) {
1465
+ if (!(error instanceof UlwLoopError))
1466
+ throw error;
1467
+ codexValidationError = error;
1400
1468
  }
1401
1469
  if (closesBatch)
1402
1470
  requireBatchFinalReady(plan, goal);
@@ -1405,14 +1473,22 @@ async function checkpointUlwLoop(repoRoot, args, scope) {
1405
1473
  if (final)
1406
1474
  aggregateCompletion = makeAggregateCompletion(now, evidence, codexGoal);
1407
1475
  if (final || aggregateCompletion !== undefined || closesBatch) {
1408
- qualityGate = validateQualityGate(await readJsonInput2(args.qualityGateJson, repoRoot), {
1409
- repoRoot,
1410
- fs: QUALITY_GATE_FS,
1411
- reviewerSurface: resolveToolkitSurface(),
1412
- ...plan.evidenceLayoutVersion === 2 ? { currentAttemptDir: ulwLoopAttemptEvidenceDir(goal.id, goal.attempt, scope) } : {}
1413
- });
1414
- requireBatchGate(plan, goal, qualityGate);
1476
+ try {
1477
+ qualityGate = validateQualityGate(await readJsonInput2(args.qualityGateJson, repoRoot), {
1478
+ repoRoot,
1479
+ fs: QUALITY_GATE_FS,
1480
+ reviewerSurface: resolveToolkitSurface(),
1481
+ ...plan.evidenceLayoutVersion === 2 ? { currentAttemptDir: ulwLoopAttemptEvidenceDir(goal.id, goal.attempt, scope) } : {}
1482
+ });
1483
+ requireBatchGate(plan, goal, qualityGate);
1484
+ } catch (error) {
1485
+ if (!(error instanceof UlwLoopError) || codexValidationError === undefined)
1486
+ throw error;
1487
+ throw combineCheckpointValidationErrors(codexValidationError, error);
1488
+ }
1415
1489
  }
1490
+ if (codexValidationError !== undefined)
1491
+ throw codexValidationError;
1416
1492
  goal.status = "complete";
1417
1493
  goal.completedAt = now;
1418
1494
  goal.evidence = evidence;
@@ -1498,7 +1574,8 @@ function gateTemplate(surface, base) {
1498
1574
  recommendation: "APPROVE",
1499
1575
  reportPath: artifactPath(base, "gate-review.md"),
1500
1576
  evidence: "<replace:gate review evidence>",
1501
- blockers: []
1577
+ blockers: [],
1578
+ notes: []
1502
1579
  },
1503
1580
  iteration: {
1504
1581
  fullRerun: true,
@@ -1537,12 +1614,20 @@ async function checkpointTemplate(repoRoot, scope, goalId) {
1537
1614
  throw new UlwLoopError(`Unknown ulw-loop id: ${goalId}.`, "ULW_LOOP_GOAL_NOT_FOUND", { details: { goalId } });
1538
1615
  const hasAttempt = plan.evidenceLayoutVersion === 2 && active !== undefined;
1539
1616
  const attemptDir = hasAttempt ? ulwLoopAttemptEvidenceDir(active.id, active.attempt, scope) : ".omo/evidence";
1617
+ const guidance = [
1618
+ "codex-goal-json requires goal.objective to equal the plan's codexObjective verbatim; do not paraphrase it.",
1619
+ "Fill every <replace:...> value with plausible non-empty evidence and use real, non-empty artifact files.",
1620
+ 'Passing codex-goal-json example: {"goal":{"objective":"<plan codexObjective verbatim>","status":"complete"}}.',
1621
+ 'Passing quality-gate-json example requires gateReview {"by":"category:deep","recommendation":"APPROVE","evidence":"review passed","reportPath":"<attemptDir>/gate-review.md","blockers":[],"notes":[]}, manualQa.artifactRefs objects, iteration, and criteriaCoverage.',
1622
+ ...hasAttempt ? [] : ["This plan is evidence-layout v1; artifacts go under .omo/evidence/."]
1623
+ ].join(" ");
1540
1624
  return {
1541
1625
  qualityGateTemplate: gateTemplate(resolveToolkitSurface(), attemptDir),
1542
1626
  codexGoalTemplate: {
1543
1627
  goal: { objective: plan.codexObjective ?? "<replace:codex objective>", status: "complete" }
1544
1628
  },
1545
- ...hasAttempt ? { attemptDir } : { guidance: "This plan is evidence-layout v1; artifacts go under .omo/evidence/." }
1629
+ guidance,
1630
+ ...hasAttempt ? { attemptDir } : {}
1546
1631
  };
1547
1632
  }
1548
1633
 
@@ -2094,7 +2179,7 @@ function kindMessage(prefix) {
2094
2179
 
2095
2180
  ${STEERING_KIND_HELP}`;
2096
2181
  }
2097
- function text3(value, field) {
2182
+ function text2(value, field) {
2098
2183
  if (value === undefined)
2099
2184
  return;
2100
2185
  const trimmed = value.trim();
@@ -2103,11 +2188,11 @@ function text3(value, field) {
2103
2188
  return fail2(`Empty ${field}.`, "ULW_LOOP_STEERING_FIELD_EMPTY", { field });
2104
2189
  }
2105
2190
  function required3(argv, flag) {
2106
- const value = text3(readValue(argv, flag), flag);
2191
+ const value = text2(readValue(argv, flag), flag);
2107
2192
  return value ?? fail2(`Missing ${flag}.`, "ULW_LOOP_STEERING_FIELD_REQUIRED", { flag });
2108
2193
  }
2109
2194
  function requiredGoal(argv) {
2110
- const value = text3(parseGoalArg(argv), "--goal-id");
2195
+ const value = text2(parseGoalArg(argv), "--goal-id");
2111
2196
  return value ?? fail2("Missing --goal-id.", "ULW_LOOP_GOAL_ID_REQUIRED", { flag: "--goal-id" });
2112
2197
  }
2113
2198
  function readObject(value, key) {
@@ -2158,14 +2243,14 @@ function parseSteeringSource(argv) {
2158
2243
  function child(value) {
2159
2244
  if (!isPlain(value))
2160
2245
  return null;
2161
- const title = text3(objectText(value, "title"), "title");
2162
- const objective = text3(objectText(value, "objective"), "objective");
2246
+ const title = text2(objectText(value, "title"), "title");
2247
+ const objective = text2(objectText(value, "objective"), "objective");
2163
2248
  if (title === undefined || objective === undefined)
2164
2249
  return null;
2165
2250
  return { title, objective };
2166
2251
  }
2167
2252
  async function children(argv, flag, needed) {
2168
- const input = needed ? required3(argv, flag) : text3(readValue(argv, flag), flag);
2253
+ const input = needed ? required3(argv, flag) : text2(readValue(argv, flag), flag);
2169
2254
  if (input === undefined)
2170
2255
  return [];
2171
2256
  const raw = await readJsonInput(input);
@@ -2188,12 +2273,12 @@ async function stringArray2(argv, flag) {
2188
2273
  for (const item of raw) {
2189
2274
  if (typeof item !== "string")
2190
2275
  return fail2(`${flag} entries must be strings.`, "ULW_LOOP_STEERING_STRING_ARRAY_REQUIRED", { flag });
2191
- values.push(text3(item, flag) ?? "");
2276
+ values.push(text2(item, flag) ?? "");
2192
2277
  }
2193
2278
  return values;
2194
2279
  }
2195
2280
  function model(value) {
2196
- const trimmed = text3(value, "--user-model");
2281
+ const trimmed = text2(value, "--user-model");
2197
2282
  if (trimmed === undefined)
2198
2283
  return;
2199
2284
  return isModel(trimmed) ? trimmed : fail2(`Invalid --user-model: ${trimmed}.`, "ULW_LOOP_STEERING_USER_MODEL_INVALID", { value: trimmed, expected: ULW_LOOP_SUCCESS_CRITERION_USER_MODELS });
@@ -2204,7 +2289,7 @@ function neverKind(kind) {
2204
2289
  async function parseSteeringProposal(argv) {
2205
2290
  const kind = parseSteeringKind(argv);
2206
2291
  const source = parseSteeringSource(argv);
2207
- const idempotencyKey = text3(readValue(argv, "--idempotency-key"), "--idempotency-key");
2292
+ const idempotencyKey = text2(readValue(argv, "--idempotency-key"), "--idempotency-key");
2208
2293
  const base = { kind, source, evidence: required3(argv, "--evidence"), rationale: required3(argv, "--rationale"), ...idempotencyKey === undefined ? {} : { idempotencyKey } };
2209
2294
  switch (kind) {
2210
2295
  case "add_subgoal":
@@ -2247,36 +2332,36 @@ async function parseSteeringProposal(argv) {
2247
2332
  function normalizedChildren(values) {
2248
2333
  if (values === undefined)
2249
2334
  return;
2250
- return values.map((item) => ({ title: text3(item.title, "child.title") ?? "", objective: text3(item.objective, "child.objective") ?? "" }));
2335
+ return values.map((item) => ({ title: text2(item.title, "child.title") ?? "", objective: text2(item.objective, "child.objective") ?? "" }));
2251
2336
  }
2252
2337
  function normalizedStrings(values, field) {
2253
2338
  if (values === undefined)
2254
2339
  return;
2255
- return values.map((value) => text3(value, field) ?? "");
2340
+ return values.map((value) => text2(value, field) ?? "");
2256
2341
  }
2257
2342
  function normalizeSteeringProposal(proposal) {
2258
- const evidence = text3(proposal.evidence, "evidence") ?? "";
2259
- const rationale = text3(proposal.rationale, "rationale") ?? "";
2260
- const goalId = text3(proposal.goalId, "goalId");
2261
- const targetGoalId = text3(proposal.targetGoalId, "targetGoalId");
2343
+ const evidence = text2(proposal.evidence, "evidence") ?? "";
2344
+ const rationale = text2(proposal.rationale, "rationale") ?? "";
2345
+ const goalId = text2(proposal.goalId, "goalId");
2346
+ const targetGoalId = text2(proposal.targetGoalId, "targetGoalId");
2262
2347
  const targetGoalIds = normalizedStrings(proposal.targetGoalIds, "targetGoalIds");
2263
- const criterionId = text3(proposal.criterionId, "criterionId");
2264
- const title = text3(proposal.title, "title");
2265
- const objective = text3(proposal.objective, "objective");
2266
- const revisedTitle = text3(proposal.revisedTitle, "revisedTitle");
2267
- const revisedObjective = text3(proposal.revisedObjective, "revisedObjective");
2268
- const blockedReason = text3(proposal.blockedReason, "blockedReason");
2269
- const directiveText = text3(proposal.directiveText, "directiveText");
2270
- const promptSignature = text3(proposal.promptSignature, "promptSignature");
2271
- const idempotencyKey = text3(proposal.idempotencyKey, "idempotencyKey");
2272
- const scenario = text3(proposal.scenario, "scenario");
2273
- const expectedEvidence = text3(proposal.expectedEvidence, "expectedEvidence");
2348
+ const criterionId = text2(proposal.criterionId, "criterionId");
2349
+ const title = text2(proposal.title, "title");
2350
+ const objective = text2(proposal.objective, "objective");
2351
+ const revisedTitle = text2(proposal.revisedTitle, "revisedTitle");
2352
+ const revisedObjective = text2(proposal.revisedObjective, "revisedObjective");
2353
+ const blockedReason = text2(proposal.blockedReason, "blockedReason");
2354
+ const directiveText = text2(proposal.directiveText, "directiveText");
2355
+ const promptSignature = text2(proposal.promptSignature, "promptSignature");
2356
+ const idempotencyKey = text2(proposal.idempotencyKey, "idempotencyKey");
2357
+ const scenario = text2(proposal.scenario, "scenario");
2358
+ const expectedEvidence = text2(proposal.expectedEvidence, "expectedEvidence");
2274
2359
  const childGoals = normalizedChildren(proposal.childGoals);
2275
2360
  const pendingOrder = normalizedStrings(proposal.pendingOrder, "pendingOrder");
2276
2361
  return { kind: proposal.kind, source: proposal.source, evidence, rationale, ...goalId === undefined ? {} : { goalId }, ...targetGoalId === undefined ? {} : { targetGoalId }, ...targetGoalIds === undefined ? {} : { targetGoalIds }, ...criterionId === undefined ? {} : { criterionId }, ...title === undefined ? {} : { title }, ...objective === undefined ? {} : { objective }, ...childGoals === undefined ? {} : { childGoals }, ...revisedTitle === undefined ? {} : { revisedTitle }, ...revisedObjective === undefined ? {} : { revisedObjective }, ...pendingOrder === undefined ? {} : { pendingOrder }, ...blockedReason === undefined ? {} : { blockedReason }, ...proposal.after === undefined ? {} : { after: proposal.after }, ...directiveText === undefined ? {} : { directiveText }, ...promptSignature === undefined ? {} : { promptSignature }, ...idempotencyKey === undefined ? {} : { idempotencyKey }, ...proposal.now === undefined ? {} : { now: proposal.now }, ...scenario === undefined ? {} : { scenario }, ...expectedEvidence === undefined ? {} : { expectedEvidence }, ...proposal.userModel === undefined ? {} : { userModel: proposal.userModel } };
2277
2362
  }
2278
2363
  async function parseSteeringProposals(argv) {
2279
- const input = text3(readValue(argv, "--proposals-json"), "--proposals-json");
2364
+ const input = text2(readValue(argv, "--proposals-json"), "--proposals-json");
2280
2365
  if (input === undefined)
2281
2366
  return [await parseSteeringProposal(argv)];
2282
2367
  if (readValue(argv, "--kind") !== undefined)
@@ -2468,7 +2553,7 @@ function activeGoalActions(plan, active) {
2468
2553
  // components/ulw-loop/src/steering-mutations.ts
2469
2554
  var read2 = (value, key) => Object.entries(value).find(([name]) => name === key)?.[1];
2470
2555
  var isText = (value) => typeof value === "string" && value.trim().length > 0;
2471
- var text4 = (value, key) => {
2556
+ var text3 = (value, key) => {
2472
2557
  const candidate = read2(value, key);
2473
2558
  return isText(candidate) ? candidate.trim() : undefined;
2474
2559
  };
@@ -2477,13 +2562,13 @@ var after = (proposal) => {
2477
2562
  const candidate = read2(proposal, "after");
2478
2563
  return typeof candidate === "object" && candidate !== null && !Array.isArray(candidate) ? candidate : undefined;
2479
2564
  };
2480
- var revised = (proposal, direct, nested) => text4(proposal, direct) ?? text4(after(proposal) ?? proposal, nested);
2481
- var targets = (proposal) => proposal.targetGoalIds ?? [proposal.targetGoalId ?? text4(proposal, "goalId") ?? ""].filter(Boolean);
2565
+ var revised = (proposal, direct, nested) => text3(proposal, direct) ?? text3(after(proposal) ?? proposal, nested);
2566
+ var targets = (proposal) => proposal.targetGoalIds ?? [proposal.targetGoalId ?? text3(proposal, "goalId") ?? ""].filter(Boolean);
2482
2567
  var child2 = (value) => {
2483
2568
  if (typeof value !== "object" || value === null || Array.isArray(value))
2484
2569
  return null;
2485
- const title = text4(value, "title");
2486
- const objective = text4(value, "objective");
2570
+ const title = text3(value, "title");
2571
+ const objective = text3(value, "objective");
2487
2572
  return title === undefined || objective === undefined ? null : { title, objective };
2488
2573
  };
2489
2574
  var children2 = (proposal) => {
@@ -2548,7 +2633,7 @@ function reviseCriterion(plan, proposal, now) {
2548
2633
  if (target === undefined || current === undefined)
2549
2634
  return;
2550
2635
  const model2 = read2(proposal, "userModel");
2551
- target.successCriteria[index] = { ...current, scenario: text4(proposal, "scenario") ?? current.scenario, expectedEvidence: text4(proposal, "expectedEvidence") ?? current.expectedEvidence, userModel: isModel2(model2) ? model2 : current.userModel };
2636
+ target.successCriteria[index] = { ...current, scenario: text3(proposal, "scenario") ?? current.scenario, expectedEvidence: text3(proposal, "expectedEvidence") ?? current.expectedEvidence, userModel: isModel2(model2) ? model2 : current.userModel };
2552
2637
  target.updatedAt = now;
2553
2638
  }
2554
2639
 
@@ -2583,7 +2668,7 @@ var isObject2 = (value) => typeof value === "object" && value !== null;
2583
2668
  var isPlain2 = (value) => isObject2(value) && !Array.isArray(value);
2584
2669
  var read3 = (value, key) => Object.entries(value).find(([name]) => name === key)?.[1];
2585
2670
  var isText2 = (value) => typeof value === "string" && value.trim().length > 0;
2586
- var text5 = (value, key) => {
2671
+ var text4 = (value, key) => {
2587
2672
  const candidate = read3(value, key);
2588
2673
  return isText2(candidate) ? candidate.trim() : undefined;
2589
2674
  };
@@ -2596,19 +2681,19 @@ var texts = (value, key) => {
2596
2681
  };
2597
2682
  function targets2(proposal) {
2598
2683
  const many = texts(proposal, "targetGoalIds");
2599
- const one = text5(proposal, "targetGoalId") ?? text5(proposal, "goalId");
2684
+ const one = text4(proposal, "targetGoalId") ?? text4(proposal, "goalId");
2600
2685
  return many.length > 0 ? many : one === undefined ? [] : [one];
2601
2686
  }
2602
2687
  var after2 = (proposal) => {
2603
2688
  const candidate = read3(proposal, "after");
2604
2689
  return isPlain2(candidate) ? candidate : undefined;
2605
2690
  };
2606
- var revised2 = (proposal, direct, nested) => text5(proposal, direct) ?? text5(after2(proposal) ?? proposal, nested);
2691
+ var revised2 = (proposal, direct, nested) => text4(proposal, direct) ?? text4(after2(proposal) ?? proposal, nested);
2607
2692
  function child3(value) {
2608
2693
  if (!isPlain2(value))
2609
2694
  return null;
2610
- const title = text5(value, "title");
2611
- const objective = text5(value, "objective");
2695
+ const title = text4(value, "title");
2696
+ const objective = text4(value, "objective");
2612
2697
  if (title === undefined || objective === undefined)
2613
2698
  return null;
2614
2699
  return { title, objective };
@@ -2647,15 +2732,15 @@ function auditFor(proposal, reasons) {
2647
2732
  const object = isPlain2(proposal) ? proposal : undefined;
2648
2733
  const kindRaw = object === undefined ? undefined : read3(object, "kind");
2649
2734
  const sourceRaw = object === undefined ? undefined : read3(object, "source");
2650
- const evidence = object === undefined ? "" : text5(object, "evidence") ?? "";
2651
- const rationale = object === undefined ? "" : text5(object, "rationale") ?? "";
2735
+ const evidence = object === undefined ? "" : text4(object, "evidence") ?? "";
2736
+ const rationale = object === undefined ? "" : text4(object, "rationale") ?? "";
2652
2737
  const audit = { kind: isKind2(kindRaw) ? kindRaw : "annotate_ledger", source: isSource2(sourceRaw) ? sourceRaw : "cli", targetGoalIds: object === undefined ? [] : targets2(object), evidence, rationale, invariant: { accepted: reasons.length === 0, structuralInvariantAccepted: reasons.length === 0, evidenceBackedNecessity: evidence.length > 0 && rationale.length > 0, noEasierCompletion: !weakens(proposal), rejectedReasons: reasons, reasons } };
2653
2738
  if (object === undefined)
2654
2739
  return audit;
2655
- const criterionId = text5(object, "criterionId");
2656
- const directiveText = text5(object, "directiveText");
2657
- const promptSignature = text5(object, "promptSignature");
2658
- const idempotencyKey = text5(object, "idempotencyKey");
2740
+ const criterionId = text4(object, "criterionId");
2741
+ const directiveText = text4(object, "directiveText");
2742
+ const promptSignature = text4(object, "promptSignature");
2743
+ const idempotencyKey = text4(object, "idempotencyKey");
2659
2744
  if (criterionId !== undefined)
2660
2745
  audit.criterionId = criterionId;
2661
2746
  if (directiveText !== undefined)
@@ -2676,9 +2761,9 @@ function validateUlwLoopSteeringProposal(plan, proposal) {
2676
2761
  reasons.push(`invalid kind: ${String(kind)}`);
2677
2762
  if (!isSource2(read3(object, "source")))
2678
2763
  reasons.push(`invalid source: ${String(read3(object, "source"))}`);
2679
- if (text5(object, "evidence") === undefined)
2764
+ if (text4(object, "evidence") === undefined)
2680
2765
  reasons.push("missing evidence");
2681
- if (text5(object, "rationale") === undefined)
2766
+ if (text4(object, "rationale") === undefined)
2682
2767
  reasons.push("missing rationale");
2683
2768
  if (hasProtected(proposal))
2684
2769
  reasons.push("protected payload");
@@ -2695,7 +2780,7 @@ function goal2(plan, id) {
2695
2780
  }
2696
2781
  function validateKind(plan, proposal, kind, reasons) {
2697
2782
  const target = goal2(plan, targets2(proposal)[0]);
2698
- if (kind === "add_subgoal" && (text5(proposal, "title") === undefined || text5(proposal, "objective") === undefined))
2783
+ if (kind === "add_subgoal" && (text4(proposal, "title") === undefined || text4(proposal, "objective") === undefined))
2699
2784
  reasons.push("add_subgoal requires title/objective");
2700
2785
  if ((kind === "split_subgoal" || kind === "revise_pending_wording" || kind === "mark_blocked_superseded") && target === undefined)
2701
2786
  reasons.push(`${kind} requires target`);
@@ -2725,7 +2810,7 @@ function validateOrder(plan, proposal, reasons) {
2725
2810
  }
2726
2811
  function validateCriterion(plan, proposal, reasons) {
2727
2812
  const target = goal2(plan, targets2(proposal)[0]);
2728
- const criterionId = text5(proposal, "criterionId");
2813
+ const criterionId = text4(proposal, "criterionId");
2729
2814
  if (target === undefined)
2730
2815
  reasons.push("revise_criterion requires goalId");
2731
2816
  else if (criterionId === undefined || target.successCriteria.every((item) => item.id !== criterionId))
@@ -2760,8 +2845,8 @@ function applySteeringMutation(plan, proposal, audit) {
2760
2845
  function isProposal(value) {
2761
2846
  return isPlain2(value) && isKind2(read3(value, "kind")) && isSource2(read3(value, "source")) && isText2(read3(value, "evidence")) && isText2(read3(value, "rationale"));
2762
2847
  }
2763
- function parseUlwLoopSteeringDirective(text6) {
2764
- const match = /(?:^|\s)(?:OMO_ULW_LOOP_STEER|omo\.ulw-loop\.steer|omo ulw-loop steer|omo-agent-toolkit ulw-loop steer):\s*([\s\S]+)$/u.exec(text6);
2848
+ function parseUlwLoopSteeringDirective(text5) {
2849
+ const match = /(?:^|\s)(?:OMO_ULW_LOOP_STEER|omo\.ulw-loop\.steer|omo ulw-loop steer|omo-agent-toolkit ulw-loop steer):\s*([\s\S]+)$/u.exec(text5);
2765
2850
  if (match?.[1] === undefined)
2766
2851
  return null;
2767
2852
  try {
@@ -3435,14 +3520,14 @@ function optionalString(value) {
3435
3520
  return value === undefined || typeof value === "string";
3436
3521
  }
3437
3522
  function readAll(stdin) {
3438
- return new Promise((resolve5, reject) => {
3523
+ return new Promise((resolve4, reject) => {
3439
3524
  let data = "";
3440
3525
  stdin.setEncoding("utf8");
3441
3526
  stdin.on("data", (chunk) => {
3442
3527
  data += chunk instanceof Buffer ? chunk.toString() : String(chunk);
3443
3528
  });
3444
3529
  stdin.once("error", reject);
3445
- stdin.once("end", () => resolve5(data));
3530
+ stdin.once("end", () => resolve4(data));
3446
3531
  });
3447
3532
  }
3448
3533
 
@@ -3740,7 +3825,7 @@ function readPlan(goalsPath) {
3740
3825
 
3741
3826
  // components/ulw-loop/src/stop-resume-hook.ts
3742
3827
  import { existsSync as existsSync8, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "node:fs";
3743
- import { isAbsolute as isAbsolute2, join as join4, resolve as resolve5, sep as sep2 } from "node:path";
3828
+ import { isAbsolute as isAbsolute2, join as join4, resolve as resolve4, sep as sep2 } from "node:path";
3744
3829
  var RESUME_CAP = 2;
3745
3830
  var CONTEXT_PRESSURE_MARKERS2 = [
3746
3831
  "context compacted",
@@ -3798,8 +3883,8 @@ function isResumableStatus(status2) {
3798
3883
  }
3799
3884
  function consumeResumeBudget(stateDir, goalId) {
3800
3885
  const ledgerLineCount = countLedgerLines(join4(stateDir, "ledger.jsonl"));
3801
- const counterPath = resolve5(stateDir, `auto-resume-${goalId}.json`);
3802
- const stuckPath = resolve5(stateDir, `auto-resume-${goalId}.stuck`);
3886
+ const counterPath = resolve4(stateDir, `auto-resume-${goalId}.json`);
3887
+ const stuckPath = resolve4(stateDir, `auto-resume-${goalId}.stuck`);
3803
3888
  if (!isInsideDir(stateDir, counterPath) || !isInsideDir(stateDir, stuckPath))
3804
3889
  return false;
3805
3890
  const previous = readCounter(counterPath);
@@ -3813,7 +3898,7 @@ function consumeResumeBudget(stateDir, goalId) {
3813
3898
  return true;
3814
3899
  }
3815
3900
  function isInsideDir(dir, candidate) {
3816
- return candidate.startsWith(resolve5(dir) + sep2);
3901
+ return candidate.startsWith(resolve4(dir) + sep2);
3817
3902
  }
3818
3903
  function renderResumeDirective(plan, goal3, sessionId) {
3819
3904
  const normalized = normalizeUlwLoopSessionId(sessionId);