lazycodex-ai 5.0.0-beta.62 → 5.0.0-beta.64

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 (215) hide show
  1. package/dist/cli/doctor/checks/browser-provider.d.ts +2 -0
  2. package/dist/cli/index.js +70 -41
  3. package/dist/cli-node/index.js +70 -41
  4. package/package.json +2 -6
  5. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  6. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +4 -2
  7. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  8. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  9. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  10. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  11. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  12. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  13. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  14. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  15. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
  16. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  17. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  18. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  19. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  20. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  21. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  22. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  23. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  24. package/packages/omo-codex/plugin/components/ultrawork/agents/plan.toml +2 -2
  25. package/packages/omo-codex/plugin/components/ultrawork/dist/cli.js +5 -4
  26. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  27. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  28. package/packages/omo-codex/plugin/components/ultrawork/src/directive-content.ts +1 -1
  29. package/packages/omo-codex/plugin/components/ulw-execute-continuation/directive.md +1 -1
  30. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
  31. package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
  32. package/packages/omo-codex/plugin/components/ulw-loop/CHANGELOG.md +13 -0
  33. package/packages/omo-codex/plugin/components/ulw-loop/directive.md +5 -4
  34. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-codex-validation.d.ts +7 -5
  35. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-codex-validation.js +7 -2
  36. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint.js +7 -4
  37. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +931 -442
  38. package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-snapshot.d.ts +6 -0
  39. package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-snapshot.js +20 -12
  40. package/packages/omo-codex/plugin/components/ulw-loop/dist/domain-types.d.ts +8 -0
  41. package/packages/omo-codex/plugin/components/ulw-loop/dist/driver-objective-ack.d.ts +4 -0
  42. package/packages/omo-codex/plugin/components/ulw-loop/dist/driver-objective-ack.js +18 -0
  43. package/packages/omo-codex/plugin/components/ulw-loop/dist/evidence-artifacts.d.ts +1 -0
  44. package/packages/omo-codex/plugin/components/ulw-loop/dist/evidence-artifacts.js +37 -0
  45. package/packages/omo-codex/plugin/components/ulw-loop/dist/evidence.d.ts +1 -0
  46. package/packages/omo-codex/plugin/components/ulw-loop/dist/evidence.js +24 -12
  47. package/packages/omo-codex/plugin/components/ulw-loop/dist/ledger.d.ts +4 -0
  48. package/packages/omo-codex/plugin/components/ulw-loop/dist/ledger.js +34 -0
  49. package/packages/omo-codex/plugin/components/ulw-loop/dist/paths.d.ts +1 -0
  50. package/packages/omo-codex/plugin/components/ulw-loop/dist/paths.js +6 -0
  51. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-commit.d.ts +19 -0
  52. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-commit.js +107 -0
  53. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-crud.d.ts +6 -2
  54. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-crud.js +54 -25
  55. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-goal-factory.d.ts +10 -3
  56. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-goal-factory.js +38 -31
  57. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-io.d.ts +9 -7
  58. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-io.js +105 -114
  59. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-log.d.ts +15 -0
  60. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-log.js +86 -0
  61. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-missing-recovery.d.ts +5 -1
  62. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-missing-recovery.js +15 -3
  63. package/packages/omo-codex/plugin/components/ulw-loop/dist/review-blockers.js +11 -8
  64. package/packages/omo-codex/plugin/components/ulw-loop/dist/sdk/factory.js +28 -12
  65. package/packages/omo-codex/plugin/components/ulw-loop/dist/sdk/manifest.d.ts +335 -0
  66. package/packages/omo-codex/plugin/components/ulw-loop/dist/sdk/manifest.js +109 -10
  67. package/packages/omo-codex/plugin/components/ulw-loop/dist/sdk/types.d.ts +3 -0
  68. package/packages/omo-codex/plugin/components/ulw-loop/dist/spawn-budget-io.d.ts +5 -0
  69. package/packages/omo-codex/plugin/components/ulw-loop/dist/spawn-budget-io.js +67 -0
  70. package/packages/omo-codex/plugin/components/ulw-loop/dist/spawn-guard.js +8 -62
  71. package/packages/omo-codex/plugin/components/ulw-loop/dist/state-lock.d.ts +11 -1
  72. package/packages/omo-codex/plugin/components/ulw-loop/dist/state-lock.js +120 -88
  73. package/packages/omo-codex/plugin/components/ulw-loop/dist/status-next-actions.d.ts +3 -2
  74. package/packages/omo-codex/plugin/components/ulw-loop/dist/status-next-actions.js +18 -10
  75. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-batch.js +4 -4
  76. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-mutations.d.ts +3 -2
  77. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-mutations.js +4 -4
  78. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-types.d.ts +9 -1
  79. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering.d.ts +3 -2
  80. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering.js +11 -11
  81. package/packages/omo-codex/plugin/components/ulw-loop/dist/stop-resume-hook.js +8 -17
  82. package/packages/omo-codex/plugin/components/ulw-loop/dist/success-criteria-input.d.ts +9 -0
  83. package/packages/omo-codex/plugin/components/ulw-loop/dist/success-criteria-input.js +41 -0
  84. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
  85. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  86. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +2 -2
  87. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint-codex-validation.ts +15 -3
  88. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint.ts +7 -4
  89. package/packages/omo-codex/plugin/components/ulw-loop/src/codex-goal-snapshot.ts +26 -14
  90. package/packages/omo-codex/plugin/components/ulw-loop/src/domain-types.ts +8 -0
  91. package/packages/omo-codex/plugin/components/ulw-loop/src/driver-objective-ack.ts +20 -0
  92. package/packages/omo-codex/plugin/components/ulw-loop/src/evidence-artifacts.ts +45 -0
  93. package/packages/omo-codex/plugin/components/ulw-loop/src/evidence.ts +23 -16
  94. package/packages/omo-codex/plugin/components/ulw-loop/src/ledger.ts +33 -0
  95. package/packages/omo-codex/plugin/components/ulw-loop/src/paths.ts +7 -0
  96. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-commit.ts +124 -0
  97. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-crud.ts +67 -54
  98. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-goal-factory.ts +63 -31
  99. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-io.ts +126 -140
  100. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-log.ts +84 -0
  101. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-missing-recovery.ts +26 -3
  102. package/packages/omo-codex/plugin/components/ulw-loop/src/review-blockers.ts +11 -7
  103. package/packages/omo-codex/plugin/components/ulw-loop/src/sdk/factory.ts +47 -12
  104. package/packages/omo-codex/plugin/components/ulw-loop/src/sdk/manifest.ts +124 -10
  105. package/packages/omo-codex/plugin/components/ulw-loop/src/sdk/types.ts +3 -0
  106. package/packages/omo-codex/plugin/components/ulw-loop/src/spawn-budget-io.ts +59 -0
  107. package/packages/omo-codex/plugin/components/ulw-loop/src/spawn-guard.ts +8 -59
  108. package/packages/omo-codex/plugin/components/ulw-loop/src/state-lock.ts +129 -111
  109. package/packages/omo-codex/plugin/components/ulw-loop/src/status-next-actions.ts +19 -10
  110. package/packages/omo-codex/plugin/components/ulw-loop/src/steering-batch.ts +4 -4
  111. package/packages/omo-codex/plugin/components/ulw-loop/src/steering-mutations.ts +5 -4
  112. package/packages/omo-codex/plugin/components/ulw-loop/src/steering-types.ts +13 -1
  113. package/packages/omo-codex/plugin/components/ulw-loop/src/steering.ts +12 -10
  114. package/packages/omo-codex/plugin/components/ulw-loop/src/stop-resume-hook.ts +8 -16
  115. package/packages/omo-codex/plugin/components/ulw-loop/src/success-criteria-input.ts +56 -0
  116. package/packages/omo-codex/plugin/components/ulw-loop/test/codex-goal-snapshot.test.ts +37 -1
  117. package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/barrier.ts +13 -0
  118. package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/commit-child.ts +65 -0
  119. package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/commit-cli-qa.mjs +76 -0
  120. package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/commit-crash.ts +23 -0
  121. package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/commit-mutations.mjs +58 -0
  122. package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/commit-writer.ts +65 -0
  123. package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/lease-clock.ts +23 -0
  124. package/packages/omo-codex/plugin/components/ulw-loop/test/plan-commit-log.test.ts +252 -0
  125. package/packages/omo-codex/plugin/components/ulw-loop/test/plan-commit-recovery.test.ts +190 -0
  126. package/packages/omo-codex/plugin/components/ulw-loop/test/plan-io.test.ts +50 -35
  127. package/packages/omo-codex/plugin/components/ulw-loop/test/plan-log-newest.test.ts +72 -0
  128. package/packages/omo-codex/plugin/components/ulw-loop/test/sdk-completed-plan.test.ts +43 -0
  129. package/packages/omo-codex/plugin/components/ulw-loop/test/sdk-driver-states.test.ts +33 -4
  130. package/packages/omo-codex/plugin/components/ulw-loop/test/sdk-dx.test.ts +219 -0
  131. package/packages/omo-codex/plugin/components/ulw-loop/test/sdk-session-context.test.ts +21 -0
  132. package/packages/omo-codex/plugin/components/ulw-loop/test/state-lock.test.ts +92 -0
  133. package/packages/omo-codex/plugin/components/ulw-loop/test/status-next-actions-surface.test.ts +133 -0
  134. package/packages/omo-codex/plugin/components/ulw-loop/test/steering-batch.test.ts +12 -12
  135. package/packages/omo-codex/plugin/components/ulw-loop/test/steering.test.ts +4 -1
  136. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  137. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  138. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  139. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  140. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  141. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  142. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  143. package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
  144. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  145. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  146. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  147. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  148. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  149. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  150. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  151. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  152. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  153. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  154. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  155. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  156. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  157. package/packages/omo-codex/plugin/package-lock.json +12 -12
  158. package/packages/omo-codex/plugin/package.json +1 -1
  159. package/packages/omo-codex/plugin/scripts/sync-skills.d.mts +4 -0
  160. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +15 -7
  161. package/packages/omo-codex/plugin/skills/ast-grep/SKILL.md +12 -0
  162. package/packages/omo-codex/plugin/skills/debugging/references/tools/playwright-cli.md +79 -161
  163. package/packages/omo-codex/plugin/skills/frontend/SKILL.md +1 -1
  164. package/packages/omo-codex/plugin/skills/frontend/references/design/aside.md +1 -1
  165. package/packages/omo-codex/plugin/skills/frontend/references/design/clone-from-url.md +1 -1
  166. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/lighthouse-audit.py +2 -2
  167. package/packages/omo-codex/plugin/skills/review-work/SKILL.md +1 -1
  168. package/packages/omo-codex/plugin/skills/ultimate-browsing/ATTRIBUTION.md +1 -82
  169. package/packages/omo-codex/plugin/skills/ultimate-browsing/SKILL.md +17 -29
  170. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/AGENTS.md +2 -1
  171. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/templates/package.json +2 -2
  172. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/templates/playwright_mobile_chrome.js +5 -2
  173. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/templates/playwright_real_chrome.js +8 -6
  174. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_playwright_stealth.py +86 -0
  175. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_playwright_templates.py +4 -7
  176. package/packages/omo-codex/plugin/skills/ultimate-browsing/references/chrome-stealth.md +86 -100
  177. package/packages/omo-codex/plugin/skills/ultimate-browsing/references/insane-search/README.md +9 -3
  178. package/packages/omo-codex/plugin/skills/ultimate-browsing/references/insane-search/playwright.md +16 -13
  179. package/packages/omo-codex/plugin/skills/ultimate-browsing/scripts/extract_cookies.py +3 -3
  180. package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +5 -4
  181. package/packages/omo-codex/plugin/skills/ulw-execute/SKILL.md +1 -1
  182. package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +2 -2
  183. package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +3 -3
  184. package/packages/omo-codex/plugin/skills/visual-qa/AGENTS.md +1 -1
  185. package/packages/omo-codex/plugin/skills/visual-qa/SKILL.md +1 -1
  186. package/packages/omo-codex/plugin/skills/visual-qa/references/browser-setup.md +75 -0
  187. package/packages/omo-codex/scripts/install-dist/install-local.mjs +6 -4
  188. package/packages/prompts-core/prompts/ultrawork/codex.md +5 -4
  189. package/packages/shared-skills/skills/ast-grep/SKILL.md +12 -0
  190. package/packages/shared-skills/skills/debugging/references/tools/playwright-cli.md +79 -161
  191. package/packages/shared-skills/skills/frontend/SKILL.md +1 -1
  192. package/packages/shared-skills/skills/frontend/references/design/aside.md +1 -1
  193. package/packages/shared-skills/skills/frontend/references/design/clone-from-url.md +1 -1
  194. package/packages/shared-skills/skills/frontend/scripts/perfection/lighthouse-audit.py +2 -2
  195. package/packages/shared-skills/skills/review-work/SKILL.md +1 -1
  196. package/packages/shared-skills/skills/ultimate-browsing/ATTRIBUTION.md +1 -82
  197. package/packages/shared-skills/skills/ultimate-browsing/SKILL.md +17 -29
  198. package/packages/shared-skills/skills/ultimate-browsing/engine/AGENTS.md +2 -1
  199. package/packages/shared-skills/skills/ultimate-browsing/engine/templates/package.json +2 -2
  200. package/packages/shared-skills/skills/ultimate-browsing/engine/templates/playwright_mobile_chrome.js +5 -2
  201. package/packages/shared-skills/skills/ultimate-browsing/engine/templates/playwright_real_chrome.js +8 -6
  202. package/packages/shared-skills/skills/ultimate-browsing/engine/tests/test_playwright_stealth.py +86 -0
  203. package/packages/shared-skills/skills/ultimate-browsing/engine/tests/test_playwright_templates.py +4 -7
  204. package/packages/shared-skills/skills/ultimate-browsing/references/chrome-stealth.md +86 -100
  205. package/packages/shared-skills/skills/ultimate-browsing/references/insane-search/README.md +9 -3
  206. package/packages/shared-skills/skills/ultimate-browsing/references/insane-search/playwright.md +16 -13
  207. package/packages/shared-skills/skills/ultimate-browsing/scripts/extract_cookies.py +3 -3
  208. package/packages/shared-skills/skills/ulw-execute/SKILL.md +1 -1
  209. package/packages/shared-skills/skills/ulw-plan/scripts/scaffold-plan.mjs +1 -1
  210. package/packages/shared-skills/skills/ulw-research/SKILL.md +3 -3
  211. package/packages/shared-skills/skills/visual-qa/AGENTS.md +1 -1
  212. package/packages/shared-skills/skills/visual-qa/SKILL.md +1 -1
  213. package/packages/shared-skills/skills/visual-qa/references/browser-setup.md +75 -0
  214. package/packages/omo-codex/plugin/skills/visual-qa/references/agent-browser-setup.md +0 -45
  215. package/packages/shared-skills/skills/visual-qa/references/agent-browser-setup.md +0 -45
@@ -8,12 +8,18 @@ export interface CodexGoalSnapshot {
8
8
  export interface CodexGoalReconciliation {
9
9
  ok: boolean;
10
10
  snapshot: CodexGoalSnapshot;
11
+ /** Facts to know (a differing driver objective); never something to do. */
11
12
  warnings: string[];
13
+ /** Things to do next (create, re-create, or resume the driver goal). */
14
+ nextActions: string[];
12
15
  errors: string[];
16
+ /** The differing objective this reconciliation reported for the first time, for the caller to acknowledge. */
17
+ unacknowledgedObjective?: string;
13
18
  }
14
19
  export interface ReconcileCodexGoalOptions {
15
20
  expectedObjective: string;
16
21
  readonly acceptedObjectives?: readonly string[];
22
+ readonly acknowledgedObjectives?: readonly string[];
17
23
  }
18
24
  export declare class CodexGoalSnapshotError extends Error {
19
25
  }
@@ -73,29 +73,37 @@ export function reconcileCodexGoalSnapshot(snapshot, options) {
73
73
  const effectiveSnapshot = snapshot ?? { available: false, raw: null };
74
74
  const errors = [];
75
75
  const warnings = [];
76
+ const nextActions = [];
76
77
  const expected = options.expectedObjective;
77
- const normalizedExpected = normalizeObjective(expected);
78
78
  if (!effectiveSnapshot.available) {
79
- warnings.push(`call get_goal; if none, create_goal with codexObjective "${expected}" verbatim`);
80
- return { ok: errors.length === 0, snapshot: effectiveSnapshot, warnings, errors };
79
+ nextActions.push(`call get_goal; if none, create_goal with codexObjective "${expected}" verbatim`);
80
+ return { ok: errors.length === 0, snapshot: effectiveSnapshot, warnings, nextActions, errors };
81
81
  }
82
- const accepted = new Set([
83
- normalizedExpected,
84
- ...(options.acceptedObjectives ?? []).map((objective) => normalizeObjective(objective)),
85
- ].filter(Boolean));
82
+ const normalized = (objectives) => new Set(objectives.map(normalizeObjective).filter(Boolean));
83
+ const accepted = normalized([expected, ...(options.acceptedObjectives ?? [])]);
84
+ const acknowledged = normalized(options.acknowledgedObjectives ?? []);
86
85
  const actual = normalizeObjective(effectiveSnapshot.objective ?? "");
87
- if (actual && !accepted.has(normalizeObjective(actual))) {
86
+ let unacknowledgedObjective;
87
+ if (actual && !accepted.has(actual) && !acknowledged.has(actual)) {
88
88
  warnings.push(`driver_objective_differs: expected "${expected}", got "${actual}".`);
89
+ unacknowledgedObjective = actual;
89
90
  }
90
91
  const actualStatus = effectiveSnapshot.status ?? "unknown";
91
92
  if (actualStatus === "paused" || actualStatus === "usage_limited" || actualStatus === "budget_limited") {
92
- warnings.push("/goal resume or raise the budget");
93
+ nextActions.push("/goal resume or raise the budget");
93
94
  }
94
95
  if (actualStatus === "complete")
95
- warnings.push(`driver closed early: call create_goal with codexObjective "${expected}" verbatim`);
96
- return { ok: errors.length === 0, snapshot: effectiveSnapshot, warnings, errors };
96
+ nextActions.push(`driver closed early: call create_goal with codexObjective "${expected}" verbatim`);
97
+ return {
98
+ ok: errors.length === 0,
99
+ snapshot: effectiveSnapshot,
100
+ warnings,
101
+ nextActions,
102
+ errors,
103
+ ...(unacknowledgedObjective === undefined ? {} : { unacknowledgedObjective }),
104
+ };
97
105
  }
98
106
  export function formatCodexGoalReconciliation(reconciliation) {
99
- const parts = [...reconciliation.errors, ...reconciliation.warnings];
107
+ const parts = [...reconciliation.errors, ...reconciliation.nextActions, ...reconciliation.warnings];
100
108
  return parts.join(" ");
101
109
  }
@@ -10,6 +10,7 @@ export interface UlwLoopSuccessCriterion {
10
10
  status: UlwLoopCriterionStatus;
11
11
  capturedAt?: string;
12
12
  notes?: string;
13
+ artifacts?: string[];
13
14
  }
14
15
  export interface UlwLoopItem {
15
16
  id: string;
@@ -50,6 +51,9 @@ export interface UlwLoopValidationBatch {
50
51
  }
51
52
  export interface UlwLoopPlan {
52
53
  version: 1;
54
+ revision?: number;
55
+ brief?: string;
56
+ ledgerResetRevision?: number;
53
57
  evidenceLayoutVersion?: 2;
54
58
  createdAt: string;
55
59
  updatedAt: string;
@@ -59,6 +63,7 @@ export interface UlwLoopPlan {
59
63
  codexGoalMode?: UlwLoopCodexGoalMode;
60
64
  codexObjective?: string;
61
65
  codexObjectiveAliases?: string[];
66
+ acknowledgedDriverObjectives?: string[];
62
67
  aggregateCompletion?: UlwLoopAggregateCompletion;
63
68
  activeGoalId?: string;
64
69
  validationBatches?: readonly UlwLoopValidationBatch[];
@@ -136,6 +141,8 @@ export interface UlwLoopQualityGateSenpi extends UlwLoopQualityGateCommon {
136
141
  }
137
142
  export type UlwLoopQualityGate = UlwLoopQualityGateLazycodex | UlwLoopQualityGateSenpi;
138
143
  export interface UlwLoopLedgerEntry {
144
+ revision?: number;
145
+ id?: string;
139
146
  at: string;
140
147
  kind: UlwLoopLedgerEventKind;
141
148
  goalId?: string;
@@ -146,6 +153,7 @@ export interface UlwLoopLedgerEntry {
146
153
  codexGoal?: unknown;
147
154
  evidence?: string;
148
155
  capturedEvidence?: string;
156
+ artifacts?: string[];
149
157
  qualityGate?: unknown;
150
158
  steering?: UlwLoopSteeringAudit;
151
159
  before?: unknown;
@@ -0,0 +1,4 @@
1
+ import type { UlwLoopPlan } from "./types.js";
2
+ export declare function normalizeDriverObjective(value: string): string;
3
+ export declare function acknowledgedDriverObjectives(plan: UlwLoopPlan): readonly string[];
4
+ export declare function acknowledgeDriverObjective(plan: UlwLoopPlan, objective: string | undefined): boolean;
@@ -0,0 +1,18 @@
1
+ export function normalizeDriverObjective(value) {
2
+ return value.replace(/\s+/g, " ").trim();
3
+ }
4
+ export function acknowledgedDriverObjectives(plan) {
5
+ return plan.acknowledgedDriverObjectives ?? [];
6
+ }
7
+ // A differing driver objective is reported once; the plan remembers the exact text so every later
8
+ // checkpoint under the same driver stays quiet, while a driver rewritten to a third objective is
9
+ // reported again.
10
+ export function acknowledgeDriverObjective(plan, objective) {
11
+ if (objective === undefined)
12
+ return false;
13
+ const normalized = normalizeDriverObjective(objective);
14
+ if (!normalized || acknowledgedDriverObjectives(plan).includes(normalized))
15
+ return false;
16
+ plan.acknowledgedDriverObjectives = [...acknowledgedDriverObjectives(plan), normalized];
17
+ return true;
18
+ }
@@ -0,0 +1 @@
1
+ export declare function resolveEvidenceArtifacts(repoRoot: string, artifacts: readonly unknown[] | undefined): string[] | undefined;
@@ -0,0 +1,37 @@
1
+ import { existsSync } from "node:fs";
2
+ import { isAbsolute, relative, resolve, sep } from "node:path";
3
+ import { UlwLoopError } from "./types.js";
4
+ function fail(message, code, details) {
5
+ throw new UlwLoopError(message, code, { details });
6
+ }
7
+ function stored(repoRoot, absolute) {
8
+ const rel = relative(repoRoot, absolute);
9
+ const inside = rel !== "" && !rel.startsWith("..") && !isAbsolute(rel);
10
+ return inside ? rel.split(sep).join("/") : absolute;
11
+ }
12
+ // Artifacts are bound at record time: a cited path that does not exist is a claim without a file,
13
+ // so the whole record is refused before any state changes. Paths inside the session cwd persist
14
+ // repo-relative (portable across machines); paths outside persist absolute.
15
+ export function resolveEvidenceArtifacts(repoRoot, artifacts) {
16
+ if (artifacts === undefined)
17
+ return undefined;
18
+ const seen = new Set();
19
+ const resolved = [];
20
+ for (const [index, candidate] of artifacts.entries()) {
21
+ const path = typeof candidate === "string" ? candidate.trim() : "";
22
+ if (!path)
23
+ fail(`Artifact ${index + 1} must be a non-empty path.`, "ULW_LOOP_ARGUMENT_INVALID", { index });
24
+ const absolute = resolve(repoRoot, path);
25
+ if (!existsSync(absolute))
26
+ fail(`Evidence artifact does not exist: ${path} (resolved to ${absolute}).`, "ULW_LOOP_EVIDENCE_ARTIFACT_MISSING", {
27
+ path,
28
+ resolved: absolute,
29
+ });
30
+ const value = stored(repoRoot, absolute);
31
+ if (!seen.has(value)) {
32
+ seen.add(value);
33
+ resolved.push(value);
34
+ }
35
+ }
36
+ return resolved;
37
+ }
@@ -7,6 +7,7 @@ type RecordEvidenceArgs = {
7
7
  readonly status: EvidenceStatus;
8
8
  readonly evidence: string;
9
9
  readonly notes?: string;
10
+ readonly artifacts?: readonly string[];
10
11
  };
11
12
  export declare function recordEvidence(repoRoot: string, args: RecordEvidenceArgs, scope?: UlwLoopScope): Promise<{
12
13
  plan: UlwLoopPlan;
@@ -1,5 +1,7 @@
1
+ import { resolveEvidenceArtifacts } from "./evidence-artifacts.js";
1
2
  import { essentialCriteriaOf, hasAllCriteriaPass, hasEssentialCriteriaPass } from "./goal-status.js";
2
- import { appendLedger, readUlwLoopPlan, withUlwLoopMutationLock, writePlan } from "./plan-io.js";
3
+ import { commit } from "./plan-commit.js";
4
+ import { readUlwLoopPlan, withUlwLoopMutationLock } from "./plan-io.js";
3
5
  import { iso, UlwLoopError } from "./types.js";
4
6
  function ulwLoopFail(message, code, details) {
5
7
  throw new UlwLoopError(message, code, { details });
@@ -38,6 +40,7 @@ export async function recordEvidence(repoRoot, args, scope) {
38
40
  const goal = findGoal(plan, args.goalId);
39
41
  const criterion = findCriterion(goal, args.criterionId);
40
42
  const evidence = nonEmptyEvidence(args.evidence);
43
+ const artifacts = resolveEvidenceArtifacts(repoRoot, args.artifacts);
41
44
  const kind = ledgerKind(args.status);
42
45
  const prevStatus = criterion.status;
43
46
  const capturedAt = iso();
@@ -46,9 +49,12 @@ export async function recordEvidence(repoRoot, args, scope) {
46
49
  criterion.capturedAt = capturedAt;
47
50
  if (args.notes !== undefined)
48
51
  criterion.notes = args.notes;
52
+ if (artifacts !== undefined)
53
+ criterion.artifacts = artifacts;
54
+ else
55
+ delete criterion.artifacts;
49
56
  goal.updatedAt = capturedAt;
50
57
  plan.updatedAt = capturedAt;
51
- await writePlan(repoRoot, plan, scope);
52
58
  const ledgerEntry = {
53
59
  at: capturedAt,
54
60
  kind,
@@ -57,10 +63,11 @@ export async function recordEvidence(repoRoot, args, scope) {
57
63
  criterionStatus: args.status,
58
64
  evidence,
59
65
  capturedEvidence: evidence,
66
+ ...(artifacts === undefined ? {} : { artifacts }),
60
67
  before: { status: prevStatus },
61
68
  after: { goalId: goal.id, criterionId: criterion.id, status: args.status, evidence, capturedAt, prevStatus },
62
69
  };
63
- await appendLedger(repoRoot, ledgerEntry, scope);
70
+ await commit(repoRoot, scope, { plan, entries: [ledgerEntry] });
64
71
  return { plan, goal, criterion, ledgerEntry };
65
72
  });
66
73
  }
@@ -80,18 +87,23 @@ export async function markCriteriaPendingResetForGoal(repoRoot, goalId, scope) {
80
87
  criterion.capturedEvidence = null;
81
88
  delete criterion.capturedAt;
82
89
  delete criterion.notes;
90
+ delete criterion.artifacts;
83
91
  }
84
92
  goal.updatedAt = now;
85
93
  plan.updatedAt = now;
86
- await writePlan(repoRoot, plan, scope);
87
- await appendLedger(repoRoot, {
88
- at: now,
89
- kind: "criteria_revised",
90
- goalId,
91
- message: `Reset ${goal.successCriteria.length} criteria to pending.`,
92
- before,
93
- after: { resetCount: goal.successCriteria.length },
94
- }, scope);
94
+ await commit(repoRoot, scope, {
95
+ plan,
96
+ entries: [
97
+ {
98
+ at: now,
99
+ kind: "criteria_revised",
100
+ goalId,
101
+ message: `Reset ${goal.successCriteria.length} criteria to pending.`,
102
+ before,
103
+ after: { resetCount: goal.successCriteria.length },
104
+ },
105
+ ],
106
+ });
95
107
  return { plan, resetCount: goal.successCriteria.length };
96
108
  });
97
109
  }
@@ -0,0 +1,4 @@
1
+ import { type UlwLoopScope } from "./paths.js";
2
+ import type { UlwLoopLedgerEntry } from "./types.js";
3
+ export declare function readLedgerAt(dir: string): UlwLoopLedgerEntry[];
4
+ export declare function readLedger(repoRoot: string, scope?: UlwLoopScope): UlwLoopLedgerEntry[];
@@ -0,0 +1,34 @@
1
+ import { join } from "node:path";
2
+ import { ulwLoopDir } from "./paths.js";
3
+ import { readOptional, readRecords, reconcilePlan } from "./plan-log.js";
4
+ export function readLedgerAt(dir) {
5
+ const entries = new Map();
6
+ const lines = (readOptional(join(dir, "ledger.jsonl")) ?? "").split(/\r?\n/);
7
+ for (const [index, line] of lines.entries()) {
8
+ if (line.trim().length === 0)
9
+ continue;
10
+ try {
11
+ const entry = JSON.parse(line);
12
+ entry.revision ??= 0;
13
+ entry.id ??= `legacy-${index + 1}`;
14
+ entries.set(entry.id, entry);
15
+ }
16
+ catch (error) {
17
+ if (!(error instanceof SyntaxError))
18
+ throw error;
19
+ }
20
+ }
21
+ for (const record of readRecords(dir))
22
+ for (const [seq, entry] of record.ledger.entries()) {
23
+ const id = entry.id ?? `${record.revision}-${seq}`;
24
+ entries.set(id, { ...entry, revision: record.revision, id });
25
+ }
26
+ const reset = reconcilePlan(dir)?.ledgerResetRevision ?? 0;
27
+ const sequence = (entry) => Number(entry.id?.split("-").at(-1) ?? 0);
28
+ return [...entries.values()]
29
+ .filter((entry) => (entry.revision ?? 0) >= reset)
30
+ .sort((a, b) => (a.revision ?? 0) - (b.revision ?? 0) || sequence(a) - sequence(b));
31
+ }
32
+ export function readLedger(repoRoot, scope) {
33
+ return readLedgerAt(ulwLoopDir(repoRoot, scope));
34
+ }
@@ -14,6 +14,7 @@ export declare function ulwLoopGoalsPath(repoRoot: string, scope?: UlwLoopScope)
14
14
  export declare function ulwLoopLedgerPath(repoRoot: string, scope?: UlwLoopScope): string;
15
15
  export declare function ulwLoopStateLockPath(repoRoot: string, scope?: UlwLoopScope): string;
16
16
  export declare function repoRelative(absolutePath: string, repoRoot: string): string;
17
+ export declare function ulwLoopEvidenceRoot(scope?: UlwLoopScope): string;
17
18
  export declare function ulwLoopAttemptEvidenceDir(goalId: string, attempt: number, scope?: UlwLoopScope): string;
18
19
  interface AttemptPathApi {
19
20
  relative(from: string, to: string): string;
@@ -62,6 +62,12 @@ export function repoRelative(absolutePath, repoRoot) {
62
62
  return absolutePath.slice(backslashPrefix.length).split("\\").join("/");
63
63
  return absolutePath.split("\\").join("/");
64
64
  }
65
+ // The plan-level evidence root stays fixed for the whole run, unlike the per-goal attempt dir, so
66
+ // long lanes that span goals have one stable place to write artifacts.
67
+ export function ulwLoopEvidenceRoot(scope) {
68
+ const sessionId = normalizeUlwLoopSessionId(scope?.sessionId);
69
+ return sessionId === null ? ".omo/evidence" : `.omo/evidence/ulw/${sessionId}`;
70
+ }
65
71
  // Both the status --json emitter and the checkpoint enforcement resolve the attempt dir through
66
72
  // this function from the scope alone; a second resolution path (env, a literal placeholder)
67
73
  // would let the gate reject its own advertised directory.
@@ -0,0 +1,19 @@
1
+ import { type UlwLoopScope } from "./paths.js";
2
+ import { type UlwLoopLedgerEntry, type UlwLoopPlan } from "./types.js";
3
+ interface CommitHooks {
4
+ readonly beforeMutation?: () => void | Promise<void>;
5
+ readonly beforeCommit?: () => void | Promise<void>;
6
+ readonly afterCommit?: () => void | Promise<void>;
7
+ readonly link?: (source: string, destination: string) => void;
8
+ readonly writeView?: (path: string, content: string) => Promise<void>;
9
+ }
10
+ export declare function withCommitHooks<T>(value: CommitHooks, fn: () => Promise<T>): Promise<T>;
11
+ export declare function beforePlanMutation(): Promise<void>;
12
+ export declare function writeViewFile(path: string, content: string): Promise<void>;
13
+ export declare function commit(repoRoot: string, scope: UlwLoopScope | undefined, mutation: {
14
+ readonly plan: UlwLoopPlan;
15
+ readonly entries: readonly UlwLoopLedgerEntry[];
16
+ }): Promise<void>;
17
+ export declare function materializeSync(dir: string): void;
18
+ export declare function materialize(dir: string): Promise<void>;
19
+ export {};
@@ -0,0 +1,107 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ import { randomUUID } from "node:crypto";
3
+ import { linkSync, mkdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
4
+ import { rename, writeFile } from "node:fs/promises";
5
+ import { join } from "node:path";
6
+ import { readLedgerAt } from "./ledger.js";
7
+ import { ulwLoopDir, ulwLoopStateLockPath } from "./paths.js";
8
+ import { assertStateLockOwned, migrationEntries } from "./plan-io.js";
9
+ import { hasCode, readOptional, readRecords, reconcilePlan } from "./plan-log.js";
10
+ import { UlwLoopError } from "./types.js";
11
+ const hooks = new AsyncLocalStorage();
12
+ export function withCommitHooks(value, fn) {
13
+ return hooks.run(value, fn);
14
+ }
15
+ export async function beforePlanMutation() {
16
+ await hooks.getStore()?.beforeMutation?.();
17
+ }
18
+ export async function writeViewFile(path, content) {
19
+ await (hooks.getStore()?.writeView ?? writeFile)(path, content);
20
+ }
21
+ // link is the sole commit point; no copy fallback (Node copyFile is not atomic).
22
+ // A resumed zombie can publish first, but never overwrite an existing revision.
23
+ // Logical audit order is (revision, sequence), not materialization completion time.
24
+ export async function commit(repoRoot, scope, mutation) {
25
+ const { plan } = mutation;
26
+ const dir = ulwLoopDir(repoRoot, scope);
27
+ assertStateLockOwned(ulwLoopStateLockPath(repoRoot, scope));
28
+ await hooks.getStore()?.beforeCommit?.();
29
+ const next = (plan.revision ?? 0) + 1;
30
+ const brief = plan.brief ?? readOptional(join(dir, "brief.md")) ?? "";
31
+ const entries = [...migrationEntries(plan), ...mutation.entries].map((entry, seq) => ({
32
+ ...entry,
33
+ revision: next,
34
+ id: `${next}-${seq}`,
35
+ }));
36
+ const record = {
37
+ version: 1,
38
+ revision: next,
39
+ plan: { ...plan, revision: next, brief },
40
+ ledger: entries,
41
+ };
42
+ mkdirSync(join(dir, "revisions"), { recursive: true });
43
+ mkdirSync(join(dir, "tmp"), { recursive: true });
44
+ const temp = join(dir, "tmp", `${process.pid}-${randomUUID()}.json`);
45
+ try {
46
+ writeFileSync(temp, `${JSON.stringify(record)}\n`, { flag: "wx" });
47
+ try {
48
+ (hooks.getStore()?.link ?? linkSync)(temp, join(dir, "revisions", `${String(next).padStart(8, "0")}.json`));
49
+ }
50
+ catch (error) {
51
+ if (hasCode(error, "EEXIST"))
52
+ throw new UlwLoopError("Another writer published the read revision.", "ULW_LOOP_PUBLISH_CONFLICT", {
53
+ cause: error,
54
+ });
55
+ if (["EPERM", "ENOTSUP", "EXDEV"].some((code) => hasCode(error, code)))
56
+ throw new UlwLoopError("Immutable ulw-loop publication requires atomic hard links.", "ULW_LOOP_PUBLISH_UNSUPPORTED_FS", { cause: error });
57
+ throw error;
58
+ }
59
+ }
60
+ finally {
61
+ rmSync(temp, { force: true });
62
+ }
63
+ plan.revision = next;
64
+ plan.brief = brief;
65
+ for (const [index, entry] of mutation.entries.entries())
66
+ Object.assign(entry, entries[index + entries.length - mutation.entries.length]);
67
+ await hooks.getStore()?.afterCommit?.();
68
+ await materialize(dir);
69
+ }
70
+ function viewContents(dir) {
71
+ const plan = reconcilePlan(dir);
72
+ if (plan === undefined || readRecords(dir).length === 0)
73
+ return [];
74
+ const ledger = readLedgerAt(dir);
75
+ const views = [
76
+ ["goals.json", `${JSON.stringify(plan, null, 2)}\n`],
77
+ ["ledger.jsonl", ledger.length === 0 ? "" : `${ledger.map((entry) => JSON.stringify(entry)).join("\n")}\n`],
78
+ ["brief.md", plan.brief ?? readOptional(join(dir, "brief.md")) ?? ""],
79
+ ];
80
+ return views.filter(([name, content]) => readOptional(join(dir, name)) !== content);
81
+ }
82
+ export function materializeSync(dir) {
83
+ for (const [name, content] of viewContents(dir)) {
84
+ mkdirSync(join(dir, "tmp"), { recursive: true });
85
+ const temp = join(dir, "tmp", `${process.pid}-${randomUUID()}-${name}`);
86
+ try {
87
+ writeFileSync(temp, content, { flag: "wx" });
88
+ renameSync(temp, join(dir, name));
89
+ }
90
+ finally {
91
+ rmSync(temp, { force: true });
92
+ }
93
+ }
94
+ }
95
+ export async function materialize(dir) {
96
+ for (const [name, content] of viewContents(dir)) {
97
+ mkdirSync(join(dir, "tmp"), { recursive: true });
98
+ const temp = join(dir, "tmp", `${process.pid}-${randomUUID()}-${name}`);
99
+ try {
100
+ await writeViewFile(temp, content);
101
+ await rename(temp, join(dir, name));
102
+ }
103
+ finally {
104
+ rmSync(temp, { force: true });
105
+ }
106
+ }
107
+ }
@@ -1,6 +1,9 @@
1
1
  import { type UlwLoopScope } from "./paths.js";
2
+ import type { SuccessCriterionInput } from "./success-criteria-input.js";
3
+ import type { UlwLoopToolkitSurface } from "./surface.js";
2
4
  import type { UlwLoopCodexGoalMode, UlwLoopItem, UlwLoopPlan } from "./types.js";
3
5
  export { deriveGoalCandidates, seedDefaultSuccessCriteria } from "./plan-goal-factory.js";
6
+ export type { SuccessCriterionInput } from "./success-criteria-input.js";
4
7
  export type UlwLoopPlanSummary = {
5
8
  readonly total: number;
6
9
  readonly pending: number;
@@ -24,11 +27,12 @@ export declare function createUlwLoopPlan(repoRoot: string, args: {
24
27
  codexGoalMode?: UlwLoopCodexGoalMode;
25
28
  force?: boolean;
26
29
  validationBatchesJson?: string;
27
- }, scope?: UlwLoopScope): Promise<UlwLoopPlan>;
30
+ }, scope?: UlwLoopScope, surface?: UlwLoopToolkitSurface): Promise<UlwLoopPlan>;
28
31
  export declare function addUlwLoopGoal(repoRoot: string, args: {
29
32
  title: string;
30
33
  objective: string;
31
- }, scope?: UlwLoopScope): Promise<{
34
+ successCriteria?: readonly SuccessCriterionInput[];
35
+ }, scope?: UlwLoopScope, surface?: UlwLoopToolkitSurface): Promise<{
32
36
  plan: UlwLoopPlan;
33
37
  goal: UlwLoopItem;
34
38
  }>;
@@ -1,9 +1,8 @@
1
- import { existsSync } from "node:fs";
2
- import { mkdir, writeFile } from "node:fs/promises";
3
1
  import { aggregateCodexObjectiveForScope, isUlwLoopDone } from "./goal-status.js";
4
- import { ulwLoopBriefPath, ulwLoopBriefRelativePath, ulwLoopDir, ulwLoopGoalsPath, ulwLoopGoalsRelativePath, ulwLoopLedgerPath, ulwLoopLedgerRelativePath, } from "./paths.js";
2
+ import { ulwLoopBriefRelativePath, ulwLoopGoalsRelativePath, ulwLoopLedgerRelativePath, } from "./paths.js";
3
+ import { beforePlanMutation, commit } from "./plan-commit.js";
5
4
  import { appendGoalToPlan, deriveGoalCandidates, makeGoal } from "./plan-goal-factory.js";
6
- import { appendLedger, readUlwLoopPlan, withUlwLoopMutationLock, writePlan } from "./plan-io.js";
5
+ import { planExists, readUlwLoopPlan, withUlwLoopMutationLock } from "./plan-io.js";
7
6
  import { iso, UlwLoopError } from "./types.js";
8
7
  import { parseValidationBatches } from "./validation-batch.js";
9
8
  export { deriveGoalCandidates, seedDefaultSuccessCriteria } from "./plan-goal-factory.js";
@@ -22,18 +21,30 @@ function clearGoalBlockerFields(goal) {
22
21
  ])
23
22
  delete goal[key];
24
23
  }
25
- export async function createUlwLoopPlan(repoRoot, args, scope) {
24
+ export async function createUlwLoopPlan(repoRoot, args, scope, surface = "lazycodex") {
26
25
  return withUlwLoopMutationLock(repoRoot, scope, async () => {
27
- if (!args.force && existsSync(ulwLoopGoalsPath(repoRoot, scope))) {
28
- const existing = await readUlwLoopPlan(repoRoot, scope);
26
+ let existing;
27
+ if (planExists(repoRoot, scope)) {
28
+ try {
29
+ existing = await readUlwLoopPlan(repoRoot, scope);
30
+ }
31
+ catch (error) {
32
+ if (!args.force)
33
+ throw error;
34
+ }
35
+ }
36
+ if (!args.force && existing !== undefined) {
29
37
  if (isUlwLoopDone(existing))
30
- throw completedPlanExistsError(scope);
38
+ throw completedPlanExistsError(scope, surface);
31
39
  throw new UlwLoopError(`Refusing to overwrite existing ${ulwLoopGoalsRelativePath(scope)}; pass --force to recreate it.`, "ULW_LOOP_PLAN_EXISTS");
32
40
  }
33
41
  const now = iso();
34
- const goals = deriveGoalCandidates(args.brief).map((goal, index) => makeGoal(goal.title, goal.objective, index, now));
42
+ const goals = deriveGoalCandidates(args.brief).map((goal, index) => makeGoal(goal.title, goal.objective, index, now, { surface }));
35
43
  const plan = {
36
44
  version: 1,
45
+ revision: existing?.revision ?? 0,
46
+ ledgerResetRevision: (existing?.revision ?? 0) + 1,
47
+ brief: args.brief.endsWith("\n") ? args.brief : `${args.brief}\n`,
37
48
  evidenceLayoutVersion: 2,
38
49
  createdAt: now,
39
50
  updatedAt: now,
@@ -48,28 +59,39 @@ export async function createUlwLoopPlan(repoRoot, args, scope) {
48
59
  plan.validationBatches = validationBatches;
49
60
  if (plan.codexGoalMode === "aggregate")
50
61
  plan.codexObjective = aggregateCodexObjectiveForScope(scope);
51
- await mkdir(ulwLoopDir(repoRoot, scope), { recursive: true });
52
- await writeFile(ulwLoopBriefPath(repoRoot, scope), args.brief.endsWith("\n") ? args.brief : `${args.brief}\n`, "utf8");
53
- await writePlan(repoRoot, plan, scope);
54
- await writeFile(ulwLoopLedgerPath(repoRoot, scope), "", "utf8");
55
- await appendLedger(repoRoot, { at: now, kind: "plan_created", message: `${goals.length} goal(s) created` }, scope);
62
+ await beforePlanMutation();
63
+ await commit(repoRoot, scope, {
64
+ plan,
65
+ entries: [{ at: now, kind: "plan_created", message: `${goals.length} goal(s) created` }],
66
+ });
56
67
  return plan;
57
68
  });
58
69
  }
59
- function completedPlanExistsError(scope) {
70
+ function completedPlanExistsError(scope, surface) {
60
71
  return new UlwLoopError([
61
72
  `Existing ulw-loop aggregate is already complete at ${ulwLoopGoalsRelativePath(scope)}.`,
62
- "Start a new run with `omo-agent-toolkit ulw-loop create-goals --session-id <new-id> ...` to isolate fresh state.",
63
- "Use --force only when you intentionally want to overwrite the completed evidence.",
73
+ ...(surface === "omo-senpi"
74
+ ? [
75
+ "Start a new run under a fresh session id (a new senpi session) or call agentToolkit.createGoals({ brief, force: true }) to recreate.",
76
+ ]
77
+ : [
78
+ "Start a new run with `omo-agent-toolkit ulw-loop create-goals --session-id <new-id> ...` to isolate fresh state.",
79
+ "Use --force only when you intentionally want to overwrite the completed evidence.",
80
+ ]),
64
81
  ].join(" "), "ULW_LOOP_PLAN_EXISTS_COMPLETE");
65
82
  }
66
- export async function addUlwLoopGoal(repoRoot, args, scope) {
83
+ export async function addUlwLoopGoal(repoRoot, args, scope, surface = "lazycodex") {
67
84
  return withUlwLoopMutationLock(repoRoot, scope, async () => {
68
85
  const plan = await readUlwLoopPlan(repoRoot, scope);
69
86
  const now = iso();
70
- const goal = appendGoalToPlan(plan, args.title, args.objective, now);
71
- await writePlan(repoRoot, plan, scope);
72
- await appendLedger(repoRoot, { at: now, kind: "goal_added", goalId: goal.id, status: goal.status, message: goal.title }, scope);
87
+ const goal = appendGoalToPlan(plan, args.title, args.objective, now, {
88
+ surface,
89
+ ...(args.successCriteria === undefined ? {} : { successCriteria: args.successCriteria }),
90
+ });
91
+ await commit(repoRoot, scope, {
92
+ plan,
93
+ entries: [{ at: now, kind: "goal_added", goalId: goal.id, status: goal.status, message: goal.title }],
94
+ });
73
95
  return { plan, goal };
74
96
  });
75
97
  }
@@ -82,17 +104,18 @@ export async function startNextUlwLoop(repoRoot, args = {}, scope) {
82
104
  const existing = plan.goals.find((goal) => goal.status === "in_progress" && isScheduleEligible(goal));
83
105
  if (existing)
84
106
  return { plan, goal: existing, resumed: true };
107
+ const entries = [];
85
108
  let next = plan.goals.find((goal) => goal.status === "pending" && isScheduleEligible(goal));
86
109
  if (!next && args.retryFailed) {
87
110
  next = plan.goals.find((goal) => goal.status === "failed" && !goal.nonRetriable && isScheduleEligible(goal));
88
111
  if (next)
89
- await appendLedger(repoRoot, {
112
+ entries.push({
90
113
  at: now,
91
114
  kind: "goal_retried",
92
115
  goalId: next.id,
93
116
  status: "pending",
94
117
  ...(next.failureReason ? { message: next.failureReason } : {}),
95
- }, scope);
118
+ });
96
119
  }
97
120
  if (!next)
98
121
  return { done: true, plan };
@@ -103,8 +126,14 @@ export async function startNextUlwLoop(repoRoot, args = {}, scope) {
103
126
  next.updatedAt = now;
104
127
  plan.activeGoalId = next.id;
105
128
  plan.updatedAt = now;
106
- await writePlan(repoRoot, plan, scope);
107
- await appendLedger(repoRoot, { at: now, kind: "goal_started", goalId: next.id, status: next.status, message: `Attempt ${next.attempt}` }, scope);
129
+ entries.push({
130
+ at: now,
131
+ kind: "goal_started",
132
+ goalId: next.id,
133
+ status: next.status,
134
+ message: `Attempt ${next.attempt}`,
135
+ });
136
+ await commit(repoRoot, scope, { plan, entries });
108
137
  return { plan, goal: next, resumed: false };
109
138
  });
110
139
  }
@@ -1,8 +1,15 @@
1
+ import { type SuccessCriterionInput } from "./success-criteria-input.js";
2
+ import type { UlwLoopToolkitSurface } from "./surface.js";
1
3
  import type { UlwLoopItem, UlwLoopPlan, UlwLoopSuccessCriterion } from "./types.js";
2
- export declare function seedDefaultSuccessCriteria(goalIndex: number, objective: string): UlwLoopSuccessCriterion[];
4
+ export interface GoalSeedOptions {
5
+ readonly goalId?: string;
6
+ readonly surface?: UlwLoopToolkitSurface;
7
+ readonly successCriteria?: readonly SuccessCriterionInput[];
8
+ }
9
+ export declare function seedDefaultSuccessCriteria(goalIndex: number, objective: string, options?: Pick<GoalSeedOptions, "goalId" | "surface">): UlwLoopSuccessCriterion[];
3
10
  export declare function deriveGoalCandidates(brief: string): Array<{
4
11
  title: string;
5
12
  objective: string;
6
13
  }>;
7
- export declare function makeGoal(title: string, objective: string, index: number, now: string): UlwLoopItem;
8
- export declare function appendGoalToPlan(plan: UlwLoopPlan, title: string, objective: string, now: string): UlwLoopItem;
14
+ export declare function makeGoal(title: string, objective: string, index: number, now: string, options?: GoalSeedOptions): UlwLoopItem;
15
+ export declare function appendGoalToPlan(plan: UlwLoopPlan, title: string, objective: string, now: string, options?: GoalSeedOptions): UlwLoopItem;