lazycodex-ai 5.0.0-beta.63 → 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 (104) hide show
  1. package/dist/cli/index.js +21 -21
  2. package/dist/cli-node/index.js +21 -21
  3. package/package.json +1 -1
  4. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  5. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +2 -2
  6. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  7. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  8. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  9. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  10. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  11. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  12. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  13. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  14. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
  15. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  16. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  17. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  18. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  19. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  20. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  21. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  22. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  23. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  24. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  25. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
  26. package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
  27. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-codex-validation.d.ts +7 -5
  28. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-codex-validation.js +7 -2
  29. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint.js +2 -0
  30. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +364 -135
  31. package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-snapshot.d.ts +6 -0
  32. package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-snapshot.js +20 -12
  33. package/packages/omo-codex/plugin/components/ulw-loop/dist/domain-types.d.ts +3 -0
  34. package/packages/omo-codex/plugin/components/ulw-loop/dist/driver-objective-ack.d.ts +4 -0
  35. package/packages/omo-codex/plugin/components/ulw-loop/dist/driver-objective-ack.js +18 -0
  36. package/packages/omo-codex/plugin/components/ulw-loop/dist/evidence-artifacts.d.ts +1 -0
  37. package/packages/omo-codex/plugin/components/ulw-loop/dist/evidence-artifacts.js +37 -0
  38. package/packages/omo-codex/plugin/components/ulw-loop/dist/evidence.d.ts +1 -0
  39. package/packages/omo-codex/plugin/components/ulw-loop/dist/evidence.js +8 -0
  40. package/packages/omo-codex/plugin/components/ulw-loop/dist/paths.d.ts +1 -0
  41. package/packages/omo-codex/plugin/components/ulw-loop/dist/paths.js +6 -0
  42. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-crud.d.ts +4 -1
  43. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-crud.js +6 -3
  44. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-goal-factory.d.ts +10 -3
  45. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-goal-factory.js +38 -31
  46. package/packages/omo-codex/plugin/components/ulw-loop/dist/review-blockers.js +8 -4
  47. package/packages/omo-codex/plugin/components/ulw-loop/dist/sdk/factory.js +17 -8
  48. package/packages/omo-codex/plugin/components/ulw-loop/dist/sdk/manifest.d.ts +335 -0
  49. package/packages/omo-codex/plugin/components/ulw-loop/dist/sdk/manifest.js +109 -10
  50. package/packages/omo-codex/plugin/components/ulw-loop/dist/sdk/types.d.ts +3 -0
  51. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-mutations.d.ts +3 -2
  52. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-mutations.js +4 -4
  53. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-types.d.ts +9 -1
  54. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering.d.ts +3 -2
  55. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering.js +6 -6
  56. package/packages/omo-codex/plugin/components/ulw-loop/dist/success-criteria-input.d.ts +9 -0
  57. package/packages/omo-codex/plugin/components/ulw-loop/dist/success-criteria-input.js +41 -0
  58. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
  59. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  60. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint-codex-validation.ts +15 -3
  61. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint.ts +2 -0
  62. package/packages/omo-codex/plugin/components/ulw-loop/src/codex-goal-snapshot.ts +26 -14
  63. package/packages/omo-codex/plugin/components/ulw-loop/src/domain-types.ts +3 -0
  64. package/packages/omo-codex/plugin/components/ulw-loop/src/driver-objective-ack.ts +20 -0
  65. package/packages/omo-codex/plugin/components/ulw-loop/src/evidence-artifacts.ts +45 -0
  66. package/packages/omo-codex/plugin/components/ulw-loop/src/evidence.ts +7 -0
  67. package/packages/omo-codex/plugin/components/ulw-loop/src/paths.ts +7 -0
  68. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-crud.ts +9 -3
  69. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-goal-factory.ts +63 -31
  70. package/packages/omo-codex/plugin/components/ulw-loop/src/review-blockers.ts +8 -4
  71. package/packages/omo-codex/plugin/components/ulw-loop/src/sdk/factory.ts +17 -7
  72. package/packages/omo-codex/plugin/components/ulw-loop/src/sdk/manifest.ts +124 -10
  73. package/packages/omo-codex/plugin/components/ulw-loop/src/sdk/types.ts +3 -0
  74. package/packages/omo-codex/plugin/components/ulw-loop/src/steering-mutations.ts +5 -4
  75. package/packages/omo-codex/plugin/components/ulw-loop/src/steering-types.ts +13 -1
  76. package/packages/omo-codex/plugin/components/ulw-loop/src/steering.ts +7 -6
  77. package/packages/omo-codex/plugin/components/ulw-loop/src/success-criteria-input.ts +56 -0
  78. package/packages/omo-codex/plugin/components/ulw-loop/test/codex-goal-snapshot.test.ts +37 -1
  79. package/packages/omo-codex/plugin/components/ulw-loop/test/sdk-driver-states.test.ts +33 -4
  80. package/packages/omo-codex/plugin/components/ulw-loop/test/sdk-dx.test.ts +219 -0
  81. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  82. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  83. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  84. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  85. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  86. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  87. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  88. package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
  89. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  90. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  91. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  92. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  93. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  94. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  95. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  96. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  97. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  98. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  99. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  100. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  101. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  102. package/packages/omo-codex/plugin/package-lock.json +12 -12
  103. package/packages/omo-codex/plugin/package.json +1 -1
  104. package/packages/omo-codex/scripts/install-dist/install-local.mjs +4 -4
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // components/ulw-loop/src/checkpoint.ts
4
- import { existsSync as existsSync4, statSync as statSync2 } from "node:fs";
4
+ import { existsSync as existsSync5, statSync as statSync2 } from "node:fs";
5
5
  import { readFile as readFile3 } from "node:fs/promises";
6
- import { resolve as resolve3 } from "node:path";
6
+ import { resolve as resolve4 } from "node:path";
7
7
 
8
8
  // components/ulw-loop/src/codex-goal-snapshot.ts
9
9
  import { existsSync } from "node:fs";
@@ -80,33 +80,58 @@ function reconcileCodexGoalSnapshot(snapshot, options) {
80
80
  const effectiveSnapshot = snapshot ?? { available: false, raw: null };
81
81
  const errors = [];
82
82
  const warnings = [];
83
+ const nextActions = [];
83
84
  const expected = options.expectedObjective;
84
- const normalizedExpected = normalizeObjective(expected);
85
85
  if (!effectiveSnapshot.available) {
86
- warnings.push(`call get_goal; if none, create_goal with codexObjective "${expected}" verbatim`);
87
- return { ok: errors.length === 0, snapshot: effectiveSnapshot, warnings, errors };
86
+ nextActions.push(`call get_goal; if none, create_goal with codexObjective "${expected}" verbatim`);
87
+ return { ok: errors.length === 0, snapshot: effectiveSnapshot, warnings, nextActions, errors };
88
88
  }
89
- const accepted = new Set([
90
- normalizedExpected,
91
- ...(options.acceptedObjectives ?? []).map((objective) => normalizeObjective(objective))
92
- ].filter(Boolean));
89
+ const normalized = (objectives) => new Set(objectives.map(normalizeObjective).filter(Boolean));
90
+ const accepted = normalized([expected, ...options.acceptedObjectives ?? []]);
91
+ const acknowledged = normalized(options.acknowledgedObjectives ?? []);
93
92
  const actual = normalizeObjective(effectiveSnapshot.objective ?? "");
94
- if (actual && !accepted.has(normalizeObjective(actual))) {
93
+ let unacknowledgedObjective;
94
+ if (actual && !accepted.has(actual) && !acknowledged.has(actual)) {
95
95
  warnings.push(`driver_objective_differs: expected "${expected}", got "${actual}".`);
96
+ unacknowledgedObjective = actual;
96
97
  }
97
98
  const actualStatus = effectiveSnapshot.status ?? "unknown";
98
99
  if (actualStatus === "paused" || actualStatus === "usage_limited" || actualStatus === "budget_limited") {
99
- warnings.push("/goal resume or raise the budget");
100
+ nextActions.push("/goal resume or raise the budget");
100
101
  }
101
102
  if (actualStatus === "complete")
102
- warnings.push(`driver closed early: call create_goal with codexObjective "${expected}" verbatim`);
103
- return { ok: errors.length === 0, snapshot: effectiveSnapshot, warnings, errors };
103
+ nextActions.push(`driver closed early: call create_goal with codexObjective "${expected}" verbatim`);
104
+ return {
105
+ ok: errors.length === 0,
106
+ snapshot: effectiveSnapshot,
107
+ warnings,
108
+ nextActions,
109
+ errors,
110
+ ...unacknowledgedObjective === undefined ? {} : { unacknowledgedObjective }
111
+ };
104
112
  }
105
113
  function formatCodexGoalReconciliation(reconciliation) {
106
- const parts = [...reconciliation.errors, ...reconciliation.warnings];
114
+ const parts = [...reconciliation.errors, ...reconciliation.nextActions, ...reconciliation.warnings];
107
115
  return parts.join(" ");
108
116
  }
109
117
 
118
+ // components/ulw-loop/src/driver-objective-ack.ts
119
+ function normalizeDriverObjective(value) {
120
+ return value.replace(/\s+/g, " ").trim();
121
+ }
122
+ function acknowledgedDriverObjectives(plan) {
123
+ return plan.acknowledgedDriverObjectives ?? [];
124
+ }
125
+ function acknowledgeDriverObjective(plan, objective) {
126
+ if (objective === undefined)
127
+ return false;
128
+ const normalized = normalizeDriverObjective(objective);
129
+ if (!normalized || acknowledgedDriverObjectives(plan).includes(normalized))
130
+ return false;
131
+ plan.acknowledgedDriverObjectives = [...acknowledgedDriverObjectives(plan), normalized];
132
+ return true;
133
+ }
134
+
110
135
  // components/ulw-loop/src/paths.ts
111
136
  import { isAbsolute, join, relative, sep } from "node:path";
112
137
  // components/ulw-loop/src/constants.ts
@@ -193,6 +218,10 @@ function repoRelative(absolutePath, repoRoot) {
193
218
  return absolutePath.slice(backslashPrefix.length).split("\\").join("/");
194
219
  return absolutePath.split("\\").join("/");
195
220
  }
221
+ function ulwLoopEvidenceRoot(scope) {
222
+ const sessionId = normalizeUlwLoopSessionId(scope?.sessionId);
223
+ return sessionId === null ? ".omo/evidence" : `.omo/evidence/ulw/${sessionId}`;
224
+ }
196
225
  function ulwLoopAttemptEvidenceDir(goalId, attempt, scope) {
197
226
  const sessionId = normalizeUlwLoopSessionId(scope?.sessionId);
198
227
  if (sessionId === null) {
@@ -299,14 +328,16 @@ async function validateCheckpointCodexGoal(input) {
299
328
  const expected = expectedCodexObjective(input.plan, input.goal);
300
329
  const reconciliation = reconcileCodexGoalSnapshot(snapshot, {
301
330
  expectedObjective: expected,
331
+ acknowledgedObjectives: acknowledgedDriverObjectives(input.plan),
302
332
  ...codexGoalMode(input.plan) === "aggregate" ? { acceptedObjectives: compatibleCodexObjectives(input.plan) } : {}
303
333
  });
304
334
  if (!reconciliation.ok)
305
335
  throw new CodexGoalSnapshotError(formatCodexGoalReconciliation(reconciliation));
306
336
  return {
307
337
  raw: snapshot?.raw,
308
- nextActions: reconciliation.warnings,
309
- warnings: reconciliation.warnings.filter((warning) => warning.startsWith("driver_objective_differs"))
338
+ nextActions: reconciliation.nextActions,
339
+ warnings: reconciliation.warnings,
340
+ ...reconciliation.unacknowledgedObjective === undefined ? {} : { unacknowledgedObjective: reconciliation.unacknowledgedObjective }
310
341
  };
311
342
  }
312
343
  function combineCheckpointValidationErrors(codexError, gateError) {
@@ -316,6 +347,41 @@ ${gateError.message}`, "ULW_LOOP_QUALITY_GATE_INVALID", {
316
347
  });
317
348
  }
318
349
 
350
+ // components/ulw-loop/src/evidence-artifacts.ts
351
+ import { existsSync as existsSync2 } from "node:fs";
352
+ import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve2, sep as sep2 } from "node:path";
353
+ function fail(message, code, details) {
354
+ throw new UlwLoopError(message, code, { details });
355
+ }
356
+ function stored(repoRoot, absolute) {
357
+ const rel = relative2(repoRoot, absolute);
358
+ const inside = rel !== "" && !rel.startsWith("..") && !isAbsolute2(rel);
359
+ return inside ? rel.split(sep2).join("/") : absolute;
360
+ }
361
+ function resolveEvidenceArtifacts(repoRoot, artifacts) {
362
+ if (artifacts === undefined)
363
+ return;
364
+ const seen = new Set;
365
+ const resolved = [];
366
+ for (const [index, candidate] of artifacts.entries()) {
367
+ const path = typeof candidate === "string" ? candidate.trim() : "";
368
+ if (!path)
369
+ fail(`Artifact ${index + 1} must be a non-empty path.`, "ULW_LOOP_ARGUMENT_INVALID", { index });
370
+ const absolute = resolve2(repoRoot, path);
371
+ if (!existsSync2(absolute))
372
+ fail(`Evidence artifact does not exist: ${path} (resolved to ${absolute}).`, "ULW_LOOP_EVIDENCE_ARTIFACT_MISSING", {
373
+ path,
374
+ resolved: absolute
375
+ });
376
+ const value = stored(repoRoot, absolute);
377
+ if (!seen.has(value)) {
378
+ seen.add(value);
379
+ resolved.push(value);
380
+ }
381
+ }
382
+ return resolved;
383
+ }
384
+
319
385
  // components/ulw-loop/src/plan-commit.ts
320
386
  import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
321
387
  import { randomUUID as randomUUID2 } from "node:crypto";
@@ -327,7 +393,7 @@ import { join as join4 } from "node:path";
327
393
  import { join as join3 } from "node:path";
328
394
 
329
395
  // components/ulw-loop/src/plan-log.ts
330
- import { existsSync as existsSync2, readdirSync, readFileSync } from "node:fs";
396
+ import { existsSync as existsSync3, readdirSync, readFileSync } from "node:fs";
331
397
  import { join as join2 } from "node:path";
332
398
  function hasCode(error, code) {
333
399
  return error instanceof Error && "code" in error && error.code === code;
@@ -394,7 +460,7 @@ function reconcilePlan(dir) {
394
460
  }
395
461
  function planExists(repoRoot, scope) {
396
462
  const dir = ulwLoopDir(repoRoot, scope);
397
- return existsSync2(join2(dir, "goals.json")) || logNames(dir).length > 0;
463
+ return existsSync3(join2(dir, "goals.json")) || logNames(dir).length > 0;
398
464
  }
399
465
 
400
466
  // components/ulw-loop/src/ledger.ts
@@ -965,6 +1031,7 @@ async function recordEvidence(repoRoot, args, scope) {
965
1031
  const goal = findGoal(plan, args.goalId);
966
1032
  const criterion = findCriterion(goal, args.criterionId);
967
1033
  const evidence = nonEmptyEvidence(args.evidence);
1034
+ const artifacts = resolveEvidenceArtifacts(repoRoot, args.artifacts);
968
1035
  const kind = ledgerKind(args.status);
969
1036
  const prevStatus = criterion.status;
970
1037
  const capturedAt = iso();
@@ -973,6 +1040,10 @@ async function recordEvidence(repoRoot, args, scope) {
973
1040
  criterion.capturedAt = capturedAt;
974
1041
  if (args.notes !== undefined)
975
1042
  criterion.notes = args.notes;
1043
+ if (artifacts !== undefined)
1044
+ criterion.artifacts = artifacts;
1045
+ else
1046
+ delete criterion.artifacts;
976
1047
  goal.updatedAt = capturedAt;
977
1048
  plan.updatedAt = capturedAt;
978
1049
  const ledgerEntry = {
@@ -983,6 +1054,7 @@ async function recordEvidence(repoRoot, args, scope) {
983
1054
  criterionStatus: args.status,
984
1055
  evidence,
985
1056
  capturedEvidence: evidence,
1057
+ ...artifacts === undefined ? {} : { artifacts },
986
1058
  before: { status: prevStatus },
987
1059
  after: { goalId: goal.id, criterionId: criterion.id, status: args.status, evidence, capturedAt, prevStatus }
988
1060
  };
@@ -1034,7 +1106,7 @@ function requireEssentialCriteriaPass(goal) {
1034
1106
  }
1035
1107
 
1036
1108
  // components/ulw-loop/src/quality-gate-artifacts.ts
1037
- import { resolve as resolve2 } from "node:path";
1109
+ import { resolve as resolve3 } from "node:path";
1038
1110
 
1039
1111
  // components/ulw-loop/src/quality-gate-fields.ts
1040
1112
  var PLACEHOLDER_PATTERN = /^(?:<replace:[^>]+>|placeholder|todo|tbd|n\/a|stub)$/i;
@@ -1173,14 +1245,14 @@ function artifactCompatible(surface, kind) {
1173
1245
  function checkFile(path, field, opts) {
1174
1246
  if (opts?.repoRoot === undefined || opts.fs === undefined || isPoisoned(field))
1175
1247
  return;
1176
- const absolute = resolve2(opts.repoRoot, path);
1248
+ const absolute = resolve3(opts.repoRoot, path);
1177
1249
  if (!opts.fs.existsSync(absolute)) {
1178
1250
  invalid(`${field} must point to an existing artifact.`, field);
1179
1251
  return;
1180
1252
  }
1181
1253
  if (opts.fs.statSync(absolute).size <= 0)
1182
1254
  invalid(`${field} must point to a non-empty artifact.`, field);
1183
- if (opts.currentAttemptDir !== undefined && !isWithinAttemptDir(absolute, resolve2(opts.repoRoot, opts.currentAttemptDir)))
1255
+ if (opts.currentAttemptDir !== undefined && !isWithinAttemptDir(absolute, resolve3(opts.repoRoot, opts.currentAttemptDir)))
1184
1256
  invalid(`${field} (${path}) must point to an artifact from the current attempt (${opts.currentAttemptDir}).`, field);
1185
1257
  }
1186
1258
  function artifactMap(refs) {
@@ -1256,7 +1328,7 @@ function adversarialVerdict(row, field) {
1256
1328
  }
1257
1329
 
1258
1330
  // components/ulw-loop/src/surface.ts
1259
- import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
1331
+ import { existsSync as existsSync4, readFileSync as readFileSync3 } from "node:fs";
1260
1332
  import { dirname as dirname2, join as join5 } from "node:path";
1261
1333
  import { fileURLToPath } from "node:url";
1262
1334
  var REVIEWER_ROLES_BY_SURFACE = {
@@ -1316,7 +1388,7 @@ function resolveToolkitSurface(options) {
1316
1388
  }
1317
1389
  function readMarkerSurface(markerPath) {
1318
1390
  try {
1319
- if (!existsSync3(markerPath))
1391
+ if (!existsSync4(markerPath))
1320
1392
  return null;
1321
1393
  const parsed = JSON.parse(readFileSync3(markerPath, "utf8"));
1322
1394
  return parseSurface(parsed["surface"]);
@@ -1629,23 +1701,23 @@ async function parseValidationBatches(input, goals) {
1629
1701
  if (raw === undefined)
1630
1702
  return;
1631
1703
  if (!Array.isArray(raw))
1632
- fail("--validation-batch-json must be a JSON array.");
1704
+ fail2("--validation-batch-json must be a JSON array.");
1633
1705
  const batches = raw.map(batchFromObject);
1634
1706
  validateBatches(batches, goals);
1635
1707
  return batches;
1636
1708
  }
1637
1709
  function batchFromObject(value) {
1638
1710
  if (!isObject(value))
1639
- fail("validation batch entries must be objects.");
1711
+ fail2("validation batch entries must be objects.");
1640
1712
  const batchId = text(value, "batchId");
1641
1713
  const memberIds = strings(value, "memberIds");
1642
1714
  const finalGoalId = text(value, "finalGoalId");
1643
1715
  if (batchId === undefined)
1644
- fail("validation batch requires batchId.");
1716
+ fail2("validation batch requires batchId.");
1645
1717
  if (memberIds === undefined || memberIds.length < 2)
1646
- fail("validation batch requires at least two memberIds.");
1718
+ fail2("validation batch requires at least two memberIds.");
1647
1719
  if (finalGoalId === undefined)
1648
- fail("validation batch requires finalGoalId.");
1720
+ fail2("validation batch requires finalGoalId.");
1649
1721
  return { batchId, memberIds, finalGoalId };
1650
1722
  }
1651
1723
  function validateBatches(batches, goals) {
@@ -1654,17 +1726,17 @@ function validateBatches(batches, goals) {
1654
1726
  const members = new Set;
1655
1727
  for (const batch of batches) {
1656
1728
  if (batchIds.has(batch.batchId))
1657
- fail(`duplicate validation batch id: ${batch.batchId}.`);
1729
+ fail2(`duplicate validation batch id: ${batch.batchId}.`);
1658
1730
  batchIds.add(batch.batchId);
1659
1731
  if (new Set(batch.memberIds).size !== batch.memberIds.length)
1660
- fail(`validation batch ${batch.batchId} has duplicate memberIds.`);
1732
+ fail2(`validation batch ${batch.batchId} has duplicate memberIds.`);
1661
1733
  if (!batch.memberIds.includes(batch.finalGoalId))
1662
- fail(`validation batch ${batch.batchId} finalGoalId must be a member.`, "ULW_LOOP_VALIDATION_BATCH_FINAL_NOT_MEMBER");
1734
+ fail2(`validation batch ${batch.batchId} finalGoalId must be a member.`, "ULW_LOOP_VALIDATION_BATCH_FINAL_NOT_MEMBER");
1663
1735
  for (const memberId of batch.memberIds) {
1664
1736
  if (!goalIds.has(memberId))
1665
- fail(`validation batch ${batch.batchId} references unknown goal: ${memberId}.`, "ULW_LOOP_VALIDATION_BATCH_MEMBER_UNKNOWN");
1737
+ fail2(`validation batch ${batch.batchId} references unknown goal: ${memberId}.`, "ULW_LOOP_VALIDATION_BATCH_MEMBER_UNKNOWN");
1666
1738
  if (members.has(memberId))
1667
- fail(`goal appears in multiple validation batches: ${memberId}.`, "ULW_LOOP_VALIDATION_BATCH_OVERLAP");
1739
+ fail2(`goal appears in multiple validation batches: ${memberId}.`, "ULW_LOOP_VALIDATION_BATCH_OVERLAP");
1668
1740
  members.add(memberId);
1669
1741
  }
1670
1742
  }
@@ -1723,12 +1795,12 @@ function memberResolved(plan, goalId) {
1723
1795
  const goal = plan.goals.find((candidate) => candidate.id === goalId);
1724
1796
  return goal !== undefined && isMemberResolved(goal, plan);
1725
1797
  }
1726
- function fail(message, code = "ULW_LOOP_VALIDATION_BATCH_INVALID") {
1798
+ function fail2(message, code = "ULW_LOOP_VALIDATION_BATCH_INVALID") {
1727
1799
  throw new UlwLoopError(message, code);
1728
1800
  }
1729
1801
 
1730
1802
  // components/ulw-loop/src/checkpoint.ts
1731
- var QUALITY_GATE_FS = { existsSync: existsSync4, statSync: statSync2 };
1803
+ var QUALITY_GATE_FS = { existsSync: existsSync5, statSync: statSync2 };
1732
1804
  function ulwLoopFail2(message, code) {
1733
1805
  throw new UlwLoopError(message, code);
1734
1806
  }
@@ -1750,8 +1822,8 @@ async function readJsonInput2(raw, repoRoot) {
1750
1822
  if (!(error instanceof SyntaxError))
1751
1823
  throw error;
1752
1824
  }
1753
- const path = resolve3(repoRoot, trimmed);
1754
- if (!existsSync4(path))
1825
+ const path = resolve4(repoRoot, trimmed);
1826
+ if (!existsSync5(path))
1755
1827
  return ulwLoopFail2("Quality gate JSON is neither valid JSON nor a readable path.", "ulw_loop_json_input_invalid");
1756
1828
  try {
1757
1829
  return JSON.parse(await readFile3(path, "utf8"));
@@ -1850,6 +1922,7 @@ async function checkpointUlwLoop(repoRoot, args, scope, dependencies) {
1850
1922
  codexGoal = validation.raw;
1851
1923
  nextActions = validation.nextActions;
1852
1924
  warnings = validation.warnings;
1925
+ acknowledgeDriverObjective(plan, validation.unacknowledgedObjective);
1853
1926
  } catch (error) {
1854
1927
  if (!(error instanceof UlwLoopError))
1855
1928
  throw error;
@@ -2235,6 +2308,48 @@ function joinLines(lines) {
2235
2308
  `);
2236
2309
  }
2237
2310
 
2311
+ // components/ulw-loop/src/success-criteria-input.ts
2312
+ function invalid2(message, details) {
2313
+ throw new UlwLoopError(`Invalid successCriteria: ${message}`, "ULW_LOOP_ARGUMENT_INVALID", { details });
2314
+ }
2315
+ function isUserModel(value) {
2316
+ return typeof value === "string" && ULW_LOOP_SUCCESS_CRITERION_USER_MODELS.some((model) => model === value);
2317
+ }
2318
+ function requireText(value, field, index) {
2319
+ const trimmed = typeof value === "string" ? value.trim() : "";
2320
+ return trimmed || invalid2(`entry ${index + 1} needs a non-empty ${field}.`, { index, field });
2321
+ }
2322
+ function criterionId(index) {
2323
+ return `C${String(index + 1).padStart(3, "0")}`;
2324
+ }
2325
+ function criteriaFromInput(input) {
2326
+ if (input.length === 0)
2327
+ invalid2("provide at least one criterion or omit the field for placeholders.", { count: 0 });
2328
+ return input.map((entry, index) => {
2329
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry))
2330
+ return invalid2(`entry ${index + 1} must be an object.`, { index });
2331
+ const record = { ...entry };
2332
+ const userModel = record["userModel"] ?? "happy";
2333
+ if (!isUserModel(userModel))
2334
+ return invalid2(`entry ${index + 1} userModel must be one of ${ULW_LOOP_SUCCESS_CRITERION_USER_MODELS.join(", ")}.`, {
2335
+ index,
2336
+ userModel: String(userModel)
2337
+ });
2338
+ const essential = record["essential"];
2339
+ if (essential !== undefined && typeof essential !== "boolean")
2340
+ return invalid2(`entry ${index + 1} essential must be a boolean.`, { index });
2341
+ return {
2342
+ id: criterionId(index),
2343
+ scenario: requireText(record["scenario"], "scenario", index),
2344
+ userModel,
2345
+ expectedEvidence: requireText(record["expectedEvidence"], "expectedEvidence", index),
2346
+ essential: essential ?? true,
2347
+ capturedEvidence: null,
2348
+ status: "pending"
2349
+ };
2350
+ });
2351
+ }
2352
+
2238
2353
  // components/ulw-loop/src/plan-goal-factory.ts
2239
2354
  function cleanLine(line) {
2240
2355
  return line.replace(/^\s*(?:[-*+]\s+|\d+[.)]\s+)/, "").trim();
@@ -2259,40 +2374,35 @@ function assertNonEmpty(value, label) {
2259
2374
  function truncateObjective(objective) {
2260
2375
  return objective.length > 80 ? `${objective.slice(0, 77).trimEnd()}...` : objective;
2261
2376
  }
2262
- function seedDefaultSuccessCriteria(goalIndex, objective) {
2377
+ function replaceVia(goalId, id, surface) {
2378
+ return surface === "omo-senpi" ? `Replace via agentToolkit.steer({ kind: "revise_criterion", source: "finding", goalId: "${goalId}", criterionId: "${id}", scenario, expectedEvidence, evidence, rationale }) (or pass successCriteria to addGoal)` : `Replace via omo-agent-toolkit ulw-loop steer --kind revise_criterion --goal-id ${goalId} --criterion-id ${id} --scenario "<scenario>" --expected-evidence "<proof>" --evidence "<why>" --rationale "<why>"`;
2379
+ }
2380
+ function seedDefaultSuccessCriteria(goalIndex, objective, options = {}) {
2263
2381
  const subject = truncateObjective(normalizeObjective2(objective) || `Goal ${goalIndex + 1}`);
2382
+ const goalId = options.goalId ?? `G${String(goalIndex + 1).padStart(3, "0")}`;
2383
+ const surface = options.surface ?? "lazycodex";
2264
2384
  const rows = [
2385
+ ["happy", `happy path for: ${subject}`, `observable happy-path proof for goal ${goalIndex + 1}`, true],
2386
+ ["edge", "edge case (boundary/empty/malformed)", `boundary or malformed-input proof for: ${subject}`, true],
2265
2387
  [
2266
- "C001",
2267
- "happy",
2268
- `happy path for: ${subject}`,
2269
- `Replace via revise_criterion with observable happy-path proof for goal ${goalIndex + 1}.`,
2270
- true
2271
- ],
2272
- [
2273
- "C002",
2274
- "edge",
2275
- "edge case (boundary/empty/malformed)",
2276
- `Replace via revise_criterion with boundary or malformed-input proof for: ${subject}.`,
2277
- true
2278
- ],
2279
- [
2280
- "C003",
2281
2388
  "regression",
2282
2389
  "regression: adjacent surface still works",
2283
- `Replace via revise_criterion with regression proof for neighboring behavior after: ${subject}.`,
2390
+ `regression proof for neighboring behavior after: ${subject}`,
2284
2391
  false
2285
2392
  ]
2286
2393
  ];
2287
- return rows.map(([id, userModel, scenario, expectedEvidence, essential]) => ({
2288
- id,
2289
- scenario,
2290
- userModel,
2291
- expectedEvidence,
2292
- essential,
2293
- capturedEvidence: null,
2294
- status: "pending"
2295
- }));
2394
+ return rows.map(([userModel, scenario, proof, essential], index) => {
2395
+ const id = criterionId(index);
2396
+ return {
2397
+ id,
2398
+ scenario,
2399
+ userModel,
2400
+ expectedEvidence: `${replaceVia(goalId, id, surface)} with ${proof}.`,
2401
+ essential,
2402
+ capturedEvidence: null,
2403
+ status: "pending"
2404
+ };
2405
+ });
2296
2406
  }
2297
2407
  function deriveGoalCandidates(brief) {
2298
2408
  const bulletGoals = brief.split(/\r?\n/).map((line) => ({ original: line, cleaned: normalizeObjective2(cleanLine(line)) })).filter(({ cleaned }) => cleaned.length > 0 && cleaned.length <= 1200).filter(({ original, cleaned }, index, all) => /^\s*(?:[-*+]\s+|\d+[.)]\s+)/.test(original) && all.findIndex((candidate) => candidate.cleaned === cleaned) === index).map(({ cleaned }) => cleaned);
@@ -2303,22 +2413,27 @@ function deriveGoalCandidates(brief) {
2303
2413
  objective
2304
2414
  }));
2305
2415
  }
2306
- function makeGoal(title, objective, index, now) {
2416
+ function makeGoal(title, objective, index, now, options = {}) {
2307
2417
  const cleanTitle = assertNonEmpty(title, "title");
2308
2418
  const cleanObjective = assertNonEmpty(objective, "objective");
2419
+ const id = normalizeGoalId(cleanTitle, index);
2420
+ const successCriteria = options.successCriteria === undefined ? seedDefaultSuccessCriteria(index, cleanObjective, {
2421
+ goalId: id,
2422
+ ...options.surface === undefined ? {} : { surface: options.surface }
2423
+ }) : criteriaFromInput(options.successCriteria);
2309
2424
  return {
2310
- id: normalizeGoalId(cleanTitle, index),
2425
+ id,
2311
2426
  title: cleanTitle,
2312
2427
  objective: cleanObjective,
2313
2428
  status: "pending",
2314
- successCriteria: seedDefaultSuccessCriteria(index, cleanObjective),
2429
+ successCriteria,
2315
2430
  attempt: 0,
2316
2431
  createdAt: now,
2317
2432
  updatedAt: now
2318
2433
  };
2319
2434
  }
2320
- function appendGoalToPlan(plan, title, objective, now) {
2321
- const goal = makeGoal(title, objective, plan.goals.length, now);
2435
+ function appendGoalToPlan(plan, title, objective, now, options = {}) {
2436
+ const goal = makeGoal(title, objective, plan.goals.length, now, options);
2322
2437
  plan.goals.push(goal);
2323
2438
  plan.updatedAt = now;
2324
2439
  return goal;
@@ -2357,7 +2472,7 @@ async function createUlwLoopPlan(repoRoot, args, scope, surface = "lazycodex") {
2357
2472
  throw new UlwLoopError(`Refusing to overwrite existing ${ulwLoopGoalsRelativePath(scope)}; pass --force to recreate it.`, "ULW_LOOP_PLAN_EXISTS");
2358
2473
  }
2359
2474
  const now = iso();
2360
- const goals = deriveGoalCandidates(args.brief).map((goal, index) => makeGoal(goal.title, goal.objective, index, now));
2475
+ const goals = deriveGoalCandidates(args.brief).map((goal, index) => makeGoal(goal.title, goal.objective, index, now, { surface }));
2361
2476
  const plan = {
2362
2477
  version: 1,
2363
2478
  revision: existing?.revision ?? 0,
@@ -2398,11 +2513,14 @@ function completedPlanExistsError(scope, surface) {
2398
2513
  ]
2399
2514
  ].join(" "), "ULW_LOOP_PLAN_EXISTS_COMPLETE");
2400
2515
  }
2401
- async function addUlwLoopGoal(repoRoot, args, scope) {
2516
+ async function addUlwLoopGoal(repoRoot, args, scope, surface = "lazycodex") {
2402
2517
  return withUlwLoopMutationLock(repoRoot, scope, async () => {
2403
2518
  const plan = await readUlwLoopPlan(repoRoot, scope);
2404
2519
  const now = iso();
2405
- const goal = appendGoalToPlan(plan, args.title, args.objective, now);
2520
+ const goal = appendGoalToPlan(plan, args.title, args.objective, now, {
2521
+ surface,
2522
+ ...args.successCriteria === undefined ? {} : { successCriteria: args.successCriteria }
2523
+ });
2406
2524
  await commit(repoRoot, scope, {
2407
2525
  plan,
2408
2526
  entries: [{ at: now, kind: "goal_added", goalId: goal.id, status: goal.status, message: goal.title }]
@@ -2570,7 +2688,7 @@ function isSource(value) {
2570
2688
  function isModel(value) {
2571
2689
  return ULW_LOOP_SUCCESS_CRITERION_USER_MODELS.some((model) => model === value);
2572
2690
  }
2573
- function fail2(message, code, details) {
2691
+ function fail3(message, code, details) {
2574
2692
  throw new UlwLoopError(message, code, { details });
2575
2693
  }
2576
2694
  function kindMessage(prefix) {
@@ -2584,15 +2702,15 @@ function text2(value, field) {
2584
2702
  const trimmed = value.trim();
2585
2703
  if (trimmed.length > 0)
2586
2704
  return trimmed;
2587
- return fail2(`Empty ${field}.`, "ULW_LOOP_STEERING_FIELD_EMPTY", { field });
2705
+ return fail3(`Empty ${field}.`, "ULW_LOOP_STEERING_FIELD_EMPTY", { field });
2588
2706
  }
2589
2707
  function required3(argv, flag) {
2590
2708
  const value = text2(readValue(argv, flag), flag);
2591
- return value ?? fail2(`Missing ${flag}.`, "ULW_LOOP_STEERING_FIELD_REQUIRED", { flag });
2709
+ return value ?? fail3(`Missing ${flag}.`, "ULW_LOOP_STEERING_FIELD_REQUIRED", { flag });
2592
2710
  }
2593
2711
  function requiredGoal(argv) {
2594
2712
  const value = text2(parseGoalArg(argv), "--goal-id");
2595
- return value ?? fail2("Missing --goal-id.", "ULW_LOOP_GOAL_ID_REQUIRED", { flag: "--goal-id" });
2713
+ return value ?? fail3("Missing --goal-id.", "ULW_LOOP_GOAL_ID_REQUIRED", { flag: "--goal-id" });
2596
2714
  }
2597
2715
  function readObject(value, key) {
2598
2716
  return Object.entries(value).find(([name]) => name === key)?.[1];
@@ -2616,7 +2734,7 @@ function objectChildren(value, key) {
2616
2734
  for (const item of candidate) {
2617
2735
  const next = child(item);
2618
2736
  if (next === null)
2619
- return fail2(`${key} entries require title/objective.`, "ULW_LOOP_STEERING_CHILD_INVALID", { key });
2737
+ return fail3(`${key} entries require title/objective.`, "ULW_LOOP_STEERING_CHILD_INVALID", { key });
2620
2738
  parsed.push(next);
2621
2739
  }
2622
2740
  return parsed;
@@ -2631,13 +2749,13 @@ function parseSteeringKind(argv) {
2631
2749
  const value = readValue(argv, "--kind");
2632
2750
  if (isKind(value))
2633
2751
  return value;
2634
- return value === undefined ? fail2(kindMessage("Missing --kind."), "ULW_LOOP_STEERING_KIND_REQUIRED", { flag: "--kind", expected: ULW_LOOP_STEERING_MUTATION_KINDS, usage: STEERING_KIND_HELP }) : fail2(kindMessage(`Invalid --kind: ${value}.`), "ULW_LOOP_STEERING_KIND_INVALID", { value, expected: ULW_LOOP_STEERING_MUTATION_KINDS, usage: STEERING_KIND_HELP });
2752
+ return value === undefined ? fail3(kindMessage("Missing --kind."), "ULW_LOOP_STEERING_KIND_REQUIRED", { flag: "--kind", expected: ULW_LOOP_STEERING_MUTATION_KINDS, usage: STEERING_KIND_HELP }) : fail3(kindMessage(`Invalid --kind: ${value}.`), "ULW_LOOP_STEERING_KIND_INVALID", { value, expected: ULW_LOOP_STEERING_MUTATION_KINDS, usage: STEERING_KIND_HELP });
2635
2753
  }
2636
2754
  function parseSteeringSource(argv) {
2637
2755
  const value = readValue(argv, "--source");
2638
2756
  if (value === undefined)
2639
2757
  return "cli";
2640
- return isSource(value) ? value : fail2(`Invalid --source: ${value}.`, "ULW_LOOP_STEERING_SOURCE_INVALID", { value, expected: SOURCES });
2758
+ return isSource(value) ? value : fail3(`Invalid --source: ${value}.`, "ULW_LOOP_STEERING_SOURCE_INVALID", { value, expected: SOURCES });
2641
2759
  }
2642
2760
  function child(value) {
2643
2761
  if (!isPlain(value))
@@ -2654,12 +2772,12 @@ async function children(argv, flag, needed) {
2654
2772
  return [];
2655
2773
  const raw = await readJsonInput(input);
2656
2774
  if (!Array.isArray(raw))
2657
- return fail2(`${flag} must be a JSON array.`, "ULW_LOOP_STEERING_JSON_ARRAY_REQUIRED", { flag });
2775
+ return fail3(`${flag} must be a JSON array.`, "ULW_LOOP_STEERING_JSON_ARRAY_REQUIRED", { flag });
2658
2776
  const parsed = [];
2659
2777
  for (const item of raw) {
2660
2778
  const next = child(item);
2661
2779
  if (next === null)
2662
- return fail2(`${flag} entries require title/objective.`, "ULW_LOOP_STEERING_CHILD_INVALID", { flag });
2780
+ return fail3(`${flag} entries require title/objective.`, "ULW_LOOP_STEERING_CHILD_INVALID", { flag });
2663
2781
  parsed.push(next);
2664
2782
  }
2665
2783
  return parsed;
@@ -2667,11 +2785,11 @@ async function children(argv, flag, needed) {
2667
2785
  async function stringArray2(argv, flag) {
2668
2786
  const raw = await readJsonInput(required3(argv, flag));
2669
2787
  if (!Array.isArray(raw))
2670
- return fail2(`${flag} must be a JSON array.`, "ULW_LOOP_STEERING_JSON_ARRAY_REQUIRED", { flag });
2788
+ return fail3(`${flag} must be a JSON array.`, "ULW_LOOP_STEERING_JSON_ARRAY_REQUIRED", { flag });
2671
2789
  const values = [];
2672
2790
  for (const item of raw) {
2673
2791
  if (typeof item !== "string")
2674
- return fail2(`${flag} entries must be strings.`, "ULW_LOOP_STEERING_STRING_ARRAY_REQUIRED", { flag });
2792
+ return fail3(`${flag} entries must be strings.`, "ULW_LOOP_STEERING_STRING_ARRAY_REQUIRED", { flag });
2675
2793
  values.push(text2(item, flag) ?? "");
2676
2794
  }
2677
2795
  return values;
@@ -2680,10 +2798,10 @@ function model(value) {
2680
2798
  const trimmed = text2(value, "--user-model");
2681
2799
  if (trimmed === undefined)
2682
2800
  return;
2683
- return isModel(trimmed) ? trimmed : fail2(`Invalid --user-model: ${trimmed}.`, "ULW_LOOP_STEERING_USER_MODEL_INVALID", { value: trimmed, expected: ULW_LOOP_SUCCESS_CRITERION_USER_MODELS });
2801
+ return isModel(trimmed) ? trimmed : fail3(`Invalid --user-model: ${trimmed}.`, "ULW_LOOP_STEERING_USER_MODEL_INVALID", { value: trimmed, expected: ULW_LOOP_SUCCESS_CRITERION_USER_MODELS });
2684
2802
  }
2685
2803
  function neverKind(kind) {
2686
- return fail2(`Unsupported steering kind: ${String(kind)}.`, "ULW_LOOP_STEERING_KIND_UNSUPPORTED", { kind });
2804
+ return fail3(`Unsupported steering kind: ${String(kind)}.`, "ULW_LOOP_STEERING_KIND_UNSUPPORTED", { kind });
2687
2805
  }
2688
2806
  async function parseSteeringProposal(argv) {
2689
2807
  const kind = parseSteeringKind(argv);
@@ -2704,7 +2822,7 @@ async function parseSteeringProposal(argv) {
2704
2822
  const revisedTitle = readValue(argv, "--title");
2705
2823
  const revisedObjective = readValue(argv, "--objective");
2706
2824
  if (revisedTitle === undefined && revisedObjective === undefined)
2707
- return fail2("revise_pending_wording requires --title or --objective.", "ULW_LOOP_STEERING_UPDATE_REQUIRED", { kind });
2825
+ return fail3("revise_pending_wording requires --title or --objective.", "ULW_LOOP_STEERING_UPDATE_REQUIRED", { kind });
2708
2826
  return normalizeSteeringProposal({ ...base, goalId, targetGoalId: goalId, ...revisedTitle === undefined ? {} : { revisedTitle }, ...revisedObjective === undefined ? {} : { revisedObjective } });
2709
2827
  }
2710
2828
  case "revise_criterion": {
@@ -2714,7 +2832,7 @@ async function parseSteeringProposal(argv) {
2714
2832
  const expectedEvidence = readValue(argv, "--expected-evidence");
2715
2833
  const userModel = model(readValue(argv, "--user-model"));
2716
2834
  if (scenario === undefined && expectedEvidence === undefined && userModel === undefined)
2717
- return fail2("revise_criterion requires scenario, expected-evidence, or user-model.", "ULW_LOOP_STEERING_UPDATE_REQUIRED", { kind });
2835
+ return fail3("revise_criterion requires scenario, expected-evidence, or user-model.", "ULW_LOOP_STEERING_UPDATE_REQUIRED", { kind });
2718
2836
  return normalizeSteeringProposal({ ...base, goalId, targetGoalId: goalId, criterionId, ...scenario === undefined ? {} : { scenario }, ...expectedEvidence === undefined ? {} : { expectedEvidence }, ...userModel === undefined ? {} : { userModel } });
2719
2837
  }
2720
2838
  case "annotate_ledger":
@@ -2764,10 +2882,10 @@ async function parseSteeringProposals(argv) {
2764
2882
  if (input === undefined)
2765
2883
  return [await parseSteeringProposal(argv)];
2766
2884
  if (readValue(argv, "--kind") !== undefined)
2767
- return fail2("--kind and --proposals-json are mutually exclusive.", "ULW_LOOP_STEERING_BATCH_CONFLICT", { flags: ["--kind", "--proposals-json"] });
2885
+ return fail3("--kind and --proposals-json are mutually exclusive.", "ULW_LOOP_STEERING_BATCH_CONFLICT", { flags: ["--kind", "--proposals-json"] });
2768
2886
  const raw = await readJsonInput(input);
2769
2887
  if (!Array.isArray(raw) || raw.length === 0)
2770
- return fail2("--proposals-json must be a non-empty JSON array.", "ULW_LOOP_STEERING_BATCH_ARRAY_REQUIRED", { flag: "--proposals-json" });
2888
+ return fail3("--proposals-json must be a non-empty JSON array.", "ULW_LOOP_STEERING_BATCH_ARRAY_REQUIRED", { flag: "--proposals-json" });
2771
2889
  const proposals = [];
2772
2890
  for (const item of raw)
2773
2891
  proposals.push(normalizeSteeringProposal(proposalFromObject(item)));
@@ -2775,13 +2893,13 @@ async function parseSteeringProposals(argv) {
2775
2893
  }
2776
2894
  function proposalFromObject(value) {
2777
2895
  if (!isPlain(value))
2778
- return fail2("--proposals-json entries must be objects.", "ULW_LOOP_STEERING_BATCH_ITEM_INVALID", { flag: "--proposals-json" });
2896
+ return fail3("--proposals-json entries must be objects.", "ULW_LOOP_STEERING_BATCH_ITEM_INVALID", { flag: "--proposals-json" });
2779
2897
  const kind = readObject(value, "kind");
2780
2898
  const source = readObject(value, "source") ?? "cli";
2781
2899
  if (!isProposalKind(kind))
2782
- return fail2(`Invalid batch steering kind: ${String(kind)}.`, "ULW_LOOP_STEERING_KIND_INVALID", { value: kind });
2900
+ return fail3(`Invalid batch steering kind: ${String(kind)}.`, "ULW_LOOP_STEERING_KIND_INVALID", { value: kind });
2783
2901
  if (!isProposalSource(source))
2784
- return fail2(`Invalid batch steering source: ${String(source)}.`, "ULW_LOOP_STEERING_SOURCE_INVALID", { value: source });
2902
+ return fail3(`Invalid batch steering source: ${String(source)}.`, "ULW_LOOP_STEERING_SOURCE_INVALID", { value: source });
2785
2903
  let proposal = { kind, source, evidence: objectText(value, "evidence") ?? "", rationale: objectText(value, "rationale") ?? "" };
2786
2904
  const goalId = objectText(value, "goalId");
2787
2905
  const targetGoalId = objectText(value, "targetGoalId");
@@ -2867,12 +2985,13 @@ function nextGoalId(plan) {
2867
2985
  }
2868
2986
  function appendBlockerGoal(plan, args, now) {
2869
2987
  const index = plan.goals.length;
2988
+ const id = nextGoalId(plan);
2870
2989
  const goal = {
2871
- id: nextGoalId(plan),
2990
+ id,
2872
2991
  title: args.title,
2873
2992
  objective: args.objective,
2874
2993
  status: "pending",
2875
- successCriteria: seedDefaultSuccessCriteria(index, args.objective),
2994
+ successCriteria: seedDefaultSuccessCriteria(index, args.objective, { goalId: id }),
2876
2995
  attempt: 0,
2877
2996
  createdAt: now,
2878
2997
  updatedAt: now
@@ -2893,10 +3012,12 @@ async function recordFinalReviewBlockers(repoRoot, args, scope) {
2893
3012
  const snapshot = await readCodexGoalSnapshotInput(args.codexGoalJson, repoRoot);
2894
3013
  const reconciliation = reconcileCodexGoalSnapshot(snapshot, {
2895
3014
  expectedObjective: expectedCodexObjective(plan, goal),
2896
- acceptedObjectives: compatibleCodexObjectives(plan)
3015
+ acceptedObjectives: compatibleCodexObjectives(plan),
3016
+ acknowledgedObjectives: acknowledgedDriverObjectives(plan)
2897
3017
  });
2898
3018
  if (!reconciliation.ok)
2899
3019
  throw new CodexGoalSnapshotError(formatCodexGoalReconciliation(reconciliation));
3020
+ acknowledgeDriverObjective(plan, reconciliation.unacknowledgedObjective);
2900
3021
  const now = iso();
2901
3022
  for (const field of BLOCKER_FIELDS)
2902
3023
  Reflect.deleteProperty(goal, field);
@@ -2920,8 +3041,8 @@ async function recordFinalReviewBlockers(repoRoot, args, scope) {
2920
3041
  blockedGoal: goal,
2921
3042
  newGoal,
2922
3043
  ledgerEntries,
2923
- nextActions: reconciliation.warnings,
2924
- warnings: reconciliation.warnings.filter((warning) => warning.startsWith("driver_objective_differs"))
3044
+ nextActions: reconciliation.nextActions,
3045
+ warnings: reconciliation.warnings
2925
3046
  };
2926
3047
  });
2927
3048
  }
@@ -2992,11 +3113,11 @@ function nextId(plan, offset) {
2992
3113
  }, 0);
2993
3114
  return `G${String(max + offset).padStart(3, "0")}`;
2994
3115
  }
2995
- function makeGoal2(plan, childGoal, evidence, now, offset) {
3116
+ function makeGoal2(plan, childGoal, evidence, now, offset, surface = "lazycodex") {
2996
3117
  const id = nextId(plan, offset);
2997
3118
  const digits = /^G(\d+)/u.exec(id)?.[1];
2998
3119
  const goalIndex = digits === undefined ? plan.goals.length + offset - 1 : Number(digits) - 1;
2999
- return { id, title: childGoal.title, objective: childGoal.objective, status: "pending", successCriteria: seedDefaultSuccessCriteria(goalIndex, childGoal.objective), attempt: 0, createdAt: now, updatedAt: now, evidence };
3120
+ return { id, title: childGoal.title, objective: childGoal.objective, status: "pending", successCriteria: seedDefaultSuccessCriteria(goalIndex, childGoal.objective, { goalId: id, surface }), attempt: 0, createdAt: now, updatedAt: now, evidence };
3000
3121
  }
3001
3122
  function reviseWording(plan, proposal, now) {
3002
3123
  const target = goal(plan, targets(proposal)[0]);
@@ -3008,11 +3129,11 @@ function reviseWording(plan, proposal, now) {
3008
3129
  target.steeringRationale = proposal.rationale;
3009
3130
  target.updatedAt = now;
3010
3131
  }
3011
- function splitOrBlock(plan, proposal, now) {
3132
+ function splitOrBlock(plan, proposal, now, surface = "lazycodex") {
3012
3133
  const target = goal(plan, targets(proposal)[0]);
3013
3134
  if (target === undefined)
3014
3135
  return;
3015
- const replacements = children2(proposal).map((item, index) => makeGoal2(plan, item, proposal.evidence, now, index + 1));
3136
+ const replacements = children2(proposal).map((item, index) => makeGoal2(plan, item, proposal.evidence, now, index + 1, surface));
3016
3137
  target.steeringEvidence = proposal.evidence;
3017
3138
  target.steeringRationale = proposal.rationale;
3018
3139
  target.updatedAt = now;
@@ -3068,7 +3189,7 @@ function changedGoalIdsBetween(before, after) {
3068
3189
 
3069
3190
  // components/ulw-loop/src/steering.ts
3070
3191
  var SOURCES2 = ["user_prompt_submit", "finding", "cli"];
3071
- var PROTECTED = new Set(["aggregateCompletion", "codexObjective", "codexObjectiveAliases", "originalConstraints", "qualityGate", "status", "completedAt", "completionStatus"]);
3192
+ var PROTECTED = new Set(["aggregateCompletion", "codexObjective", "codexObjectiveAliases", "acknowledgedDriverObjectives", "originalConstraints", "qualityGate", "status", "completedAt", "completionStatus"]);
3072
3193
  var isObject2 = (value) => typeof value === "object" && value !== null;
3073
3194
  var isPlain2 = (value) => isObject2(value) && !Array.isArray(value);
3074
3195
  var read3 = (value, key) => Object.entries(value).find(([name]) => name === key)?.[1];
@@ -3226,13 +3347,13 @@ function validateCriterion(plan, proposal, reasons) {
3226
3347
  if (model !== undefined && !isModel3(model))
3227
3348
  reasons.push("invalid userModel");
3228
3349
  }
3229
- function applySteeringMutation(plan, proposal, audit) {
3350
+ function applySteeringMutation(plan, proposal, audit, surface = "lazycodex") {
3230
3351
  const next = structuredClone(plan);
3231
3352
  if (!audit.invariant.accepted)
3232
3353
  return next;
3233
3354
  const now = proposal.now?.toISOString() ?? iso();
3234
3355
  if (proposal.kind === "add_subgoal")
3235
- next.goals.push(makeGoal2(next, { title: proposal.title ?? "", objective: proposal.objective ?? "" }, proposal.evidence, now, 1));
3356
+ next.goals.push(makeGoal2(next, { title: proposal.title ?? "", objective: proposal.objective ?? "" }, proposal.evidence, now, 1, surface));
3236
3357
  if (proposal.kind === "reorder_pending") {
3237
3358
  const order = pendingOrder(proposal);
3238
3359
  next.goals = [...order.map((id) => goal2(next, id)).filter((item) => item !== undefined), ...next.goals.filter((item) => !order.includes(item.id))];
@@ -3240,7 +3361,7 @@ function applySteeringMutation(plan, proposal, audit) {
3240
3361
  if (proposal.kind === "revise_pending_wording")
3241
3362
  reviseWording(next, proposal, now);
3242
3363
  if (proposal.kind === "split_subgoal" || proposal.kind === "mark_blocked_superseded")
3243
- splitOrBlock(next, proposal, now);
3364
+ splitOrBlock(next, proposal, now, surface);
3244
3365
  if (proposal.kind === "revise_criterion")
3245
3366
  reviseCriterion(next, proposal, now);
3246
3367
  if (proposal.kind !== "annotate_ledger")
@@ -3263,7 +3384,7 @@ function parseUlwLoopSteeringDirective(text) {
3263
3384
  throw error;
3264
3385
  }
3265
3386
  }
3266
- async function steerUlwLoop(repoRoot, proposal, scope) {
3387
+ async function steerUlwLoop(repoRoot, proposal, scope, surface = "lazycodex") {
3267
3388
  return withUlwLoopMutationLock(repoRoot, scope, async () => {
3268
3389
  const plan = await readUlwLoopPlan(repoRoot, scope);
3269
3390
  const key = proposal.idempotencyKey ?? proposal.promptSignature;
@@ -3274,7 +3395,7 @@ async function steerUlwLoop(repoRoot, proposal, scope) {
3274
3395
  }
3275
3396
  const audit = validateUlwLoopSteeringProposal(plan, proposal);
3276
3397
  const accepted = audit.invariant.accepted;
3277
- const next = accepted ? applySteeringMutation(plan, proposal, audit) : plan;
3398
+ const next = accepted ? applySteeringMutation(plan, proposal, audit, surface) : plan;
3278
3399
  const finalAudit = { ...audit };
3279
3400
  if (accepted) {
3280
3401
  const changed = changedGoalIdsBetween(plan, next);
@@ -3303,20 +3424,119 @@ function ledgerEntry(proposal, audit, at) {
3303
3424
  }
3304
3425
 
3305
3426
  // components/ulw-loop/src/sdk/manifest.ts
3427
+ var CRITERION_INPUT = '{ scenario: string, expectedEvidence: string, userModel?: "happy" | "edge" | "regression" | "adversarial", essential?: boolean }';
3428
+ var STEER_KINDS = '"add_subgoal" | "split_subgoal" | "reorder_pending" | "revise_pending_wording" | "revise_criterion" | "annotate_ledger" | "mark_blocked_superseded"';
3306
3429
  var ULW_LOOP_MANIFEST = {
3307
3430
  version: 1,
3308
3431
  name: "ulw-loop",
3309
3432
  operations: [
3310
- { name: "help", mutating: false },
3311
- { name: "create-goals", mutating: true },
3312
- { name: "status", mutating: false },
3313
- { name: "complete-goals", mutating: true },
3314
- { name: "checkpoint", mutating: true },
3315
- { name: "steer", mutating: true },
3316
- { name: "add-goal", mutating: true },
3317
- { name: "criteria", mutating: false },
3318
- { name: "record-evidence", mutating: true },
3319
- { name: "record-review-blockers", mutating: true }
3433
+ {
3434
+ name: "help",
3435
+ method: "help",
3436
+ mutating: false,
3437
+ description: "Return this manifest: every operation with its method name, argument fields, and what it does.",
3438
+ args: {}
3439
+ },
3440
+ {
3441
+ name: "create-goals",
3442
+ method: "createGoals",
3443
+ mutating: true,
3444
+ description: "Create the session plan from a brief; one goal per bullet, each seeded with placeholder criteria until revised.",
3445
+ args: {
3446
+ brief: "string",
3447
+ codexGoalMode: '"aggregate" | "per_story"?',
3448
+ force: "boolean?",
3449
+ validationBatchesJson: "string?"
3450
+ }
3451
+ },
3452
+ {
3453
+ name: "status",
3454
+ method: "status",
3455
+ mutating: false,
3456
+ description: "Read the plan, its summary, structured nextActions, the stable evidenceRoot, and the active goal's currentAttemptDir.",
3457
+ args: {}
3458
+ },
3459
+ {
3460
+ name: "complete-goals",
3461
+ method: "completeGoals",
3462
+ mutating: true,
3463
+ description: "Acquire the next eligible goal or resume the in-progress one; returns { done: true } once the aggregate is complete.",
3464
+ args: { retryFailed: "boolean?" }
3465
+ },
3466
+ {
3467
+ name: "checkpoint",
3468
+ method: "checkpoint",
3469
+ mutating: true,
3470
+ description: "Close the goal as complete, failed, or blocked with evidence; the final goal also needs the quality gate. printTemplate returns that gate's template instead.",
3471
+ args: {
3472
+ goalId: "string",
3473
+ status: '"complete" | "failed" | "blocked"',
3474
+ evidence: "string",
3475
+ codexGoalJson: "string?",
3476
+ qualityGateJson: "string?",
3477
+ printTemplate: "true? (with goalId? only)"
3478
+ }
3479
+ },
3480
+ {
3481
+ name: "steer",
3482
+ method: "steer",
3483
+ mutating: true,
3484
+ description: "Propose an evidence-backed plan mutation; revise_criterion replaces a criterion's scenario, expectedEvidence, or userModel.",
3485
+ args: {
3486
+ kind: STEER_KINDS,
3487
+ source: '"finding" | "user_prompt_submit" | "cli"',
3488
+ evidence: "string",
3489
+ rationale: "string",
3490
+ goalId: "string?",
3491
+ criterionId: "string? (revise_criterion)",
3492
+ scenario: "string? (revise_criterion)",
3493
+ expectedEvidence: "string? (revise_criterion)",
3494
+ userModel: '"happy" | "edge" | "regression" | "adversarial"? (revise_criterion)',
3495
+ title: "string? (add_subgoal)",
3496
+ objective: "string? (add_subgoal)",
3497
+ childGoals: "{ title: string, objective: string }[]? (split_subgoal)",
3498
+ revisedTitle: "string? (revise_pending_wording)",
3499
+ revisedObjective: "string? (revise_pending_wording)",
3500
+ pendingOrder: "string[]? (reorder_pending)",
3501
+ blockedReason: "string? (mark_blocked_superseded)",
3502
+ idempotencyKey: "string?"
3503
+ }
3504
+ },
3505
+ {
3506
+ name: "add-goal",
3507
+ method: "addGoal",
3508
+ mutating: true,
3509
+ description: "Append a goal; pass successCriteria to define its criteria in the same call, otherwise placeholders name the revise call.",
3510
+ args: { title: "string", objective: "string", successCriteria: `${CRITERION_INPUT}[]?` }
3511
+ },
3512
+ {
3513
+ name: "criteria",
3514
+ method: "criteria",
3515
+ mutating: false,
3516
+ description: "List one goal's success criteria with their status and captured evidence.",
3517
+ args: { goalId: "string" }
3518
+ },
3519
+ {
3520
+ name: "record-evidence",
3521
+ method: "recordEvidence",
3522
+ mutating: true,
3523
+ description: "Record a criterion's pass, fail, or blocked evidence; artifacts must exist (resolved against the session cwd) and are stored with the criterion and the ledger entry.",
3524
+ args: {
3525
+ goalId: "string",
3526
+ criterionId: "string",
3527
+ status: '"pass" | "fail" | "blocked"',
3528
+ evidence: "string",
3529
+ notes: "string?",
3530
+ artifacts: "string[]?"
3531
+ }
3532
+ },
3533
+ {
3534
+ name: "record-review-blockers",
3535
+ method: "recordReviewBlockers",
3536
+ mutating: true,
3537
+ description: "Mark the final goal review_blocked and append the blocker goal the reviewer verdict names.",
3538
+ args: { goalId: "string", title: "string", objective: "string", evidence: "string", codexGoalJson: "string?" }
3539
+ }
3320
3540
  ]
3321
3541
  };
3322
3542
 
@@ -3358,10 +3578,11 @@ function isKnownRequest(request) {
3358
3578
  function unreachable(value) {
3359
3579
  throw new UlwLoopError(`Unhandled operation: ${String(value)}`, "ULW_LOOP_OPERATION_UNHANDLED");
3360
3580
  }
3361
- function nextActionsFrom(result) {
3362
- if (!("nextActions" in result) || !Array.isArray(result.nextActions))
3581
+ function stringsFrom(result, key) {
3582
+ if (!(key in result))
3363
3583
  return [];
3364
- return result.nextActions.filter((action) => typeof action === "string").slice(0, 8);
3584
+ const value = Object.entries(result).find(([name]) => name === key)?.[1];
3585
+ return Array.isArray(value) ? value.filter((item) => typeof item === "string").slice(0, 8) : [];
3365
3586
  }
3366
3587
  function checkpointWithValidatedSnapshot(context, scope, args) {
3367
3588
  validateCodexGoalJson(args.codexGoalJson);
@@ -3378,8 +3599,15 @@ function createAgentToolkit(context, deps = {}) {
3378
3599
  const invoke = async (operation, fn) => {
3379
3600
  try {
3380
3601
  const result = await fn();
3381
- const nextActions = typeof result === "object" && result !== null ? nextActionsFrom(result) : [];
3382
- return await notify(operation, { ok: true, operation, result, nextActions });
3602
+ const nextActions = typeof result === "object" && result !== null ? stringsFrom(result, "nextActions") : [];
3603
+ const warnings = typeof result === "object" && result !== null ? stringsFrom(result, "warnings") : [];
3604
+ return await notify(operation, {
3605
+ ok: true,
3606
+ operation,
3607
+ result,
3608
+ nextActions,
3609
+ ...warnings.length === 0 ? {} : { warnings }
3610
+ });
3383
3611
  } catch (error) {
3384
3612
  if (error instanceof UlwLoopError && error.code === "ULW_LOOP_PLAN_MISSING" && context.surface === "omo-senpi")
3385
3613
  return notify(operation, failure(operation, planMissingError(ulwLoopGoalsRelativePath(scope), listUlwLoopSessionIds(context.cwd), context.surface)));
@@ -3425,13 +3653,14 @@ function createAgentToolkit(context, deps = {}) {
3425
3653
  plan,
3426
3654
  summary: summarizeUlwLoopPlan(plan),
3427
3655
  nextActions: statusNextActions(plan, context.surface),
3656
+ evidenceRoot: ulwLoopEvidenceRoot(scope),
3428
3657
  ...active === undefined || plan.evidenceLayoutVersion !== 2 ? {} : { currentAttemptDir: ulwLoopAttemptEvidenceDir(active.id, active.attempt, scope) }
3429
3658
  };
3430
3659
  }),
3431
3660
  completeGoals: (args = {}) => invoke("complete-goals", () => startNextUlwLoop(context.cwd, args, scope)),
3432
3661
  checkpoint: (args) => invoke("checkpoint", () => args.printTemplate === true ? checkpointTemplate(context.cwd, scope, args.goalId, { surface: context.surface }) : checkpointWithValidatedSnapshot(context, scope, args)),
3433
- steer: (args) => invoke("steer", () => steerUlwLoop(context.cwd, args, scope)),
3434
- addGoal: (args) => invoke("add-goal", () => addUlwLoopGoal(context.cwd, args, scope)),
3662
+ steer: (args) => invoke("steer", () => steerUlwLoop(context.cwd, args, scope, context.surface)),
3663
+ addGoal: (args) => invoke("add-goal", () => addUlwLoopGoal(context.cwd, args, scope, context.surface)),
3435
3664
  criteria: (args) => invoke("criteria", async () => {
3436
3665
  const plan = await readUlwLoopPlan(context.cwd, scope);
3437
3666
  const goal = plan.goals.find((candidate) => candidate.id === args.goalId);
@@ -3798,7 +4027,7 @@ function commandScope(repoRoot, argv) {
3798
4027
  import { readFileSync as readFileSync5 } from "node:fs";
3799
4028
 
3800
4029
  // components/ulw-loop/src/ultrawork-skill-pointer.ts
3801
- import { existsSync as existsSync5, readFileSync as readFileSync4 } from "node:fs";
4030
+ import { existsSync as existsSync6, readFileSync as readFileSync4 } from "node:fs";
3802
4031
  import { fileURLToPath as fileURLToPath2 } from "node:url";
3803
4032
  var ULTRAWORK_SKILL_POINTER_TEMPLATE = `<ultrawork-mode>
3804
4033
  ULTRAWORK MODE IS ACTIVE FOR THIS TASK.
@@ -3839,7 +4068,7 @@ function buildUltraworkSkillPointer(skillFilePath) {
3839
4068
  }
3840
4069
  function buildUltraworkAdditionalContext(options = {}) {
3841
4070
  const skillFilePath = options.skillFilePath === undefined ? resolveUltraworkSkillFilePath() : options.skillFilePath;
3842
- if (skillFilePath !== null && existsSync5(skillFilePath)) {
4071
+ if (skillFilePath !== null && existsSync6(skillFilePath)) {
3843
4072
  return buildUltraworkSkillPointer(skillFilePath);
3844
4073
  }
3845
4074
  return ULTRAWORK_DIRECTIVE;
@@ -4091,7 +4320,7 @@ import { join as join7 } from "node:path";
4091
4320
 
4092
4321
  // components/ulw-loop/src/spawn-budget-io.ts
4093
4322
  import { randomBytes } from "node:crypto";
4094
- import { existsSync as existsSync6, readFileSync as readFileSync6, renameSync as renameSync2, statSync as statSync3, writeFileSync as writeFileSync2 } from "node:fs";
4323
+ import { existsSync as existsSync7, readFileSync as readFileSync6, renameSync as renameSync2, statSync as statSync3, writeFileSync as writeFileSync2 } from "node:fs";
4095
4324
  import { dirname as dirname3, join as join6 } from "node:path";
4096
4325
  function readAdmissionBreaker(sessionId) {
4097
4326
  const dataDir = process.env["PLUGIN_DATA"];
@@ -4111,7 +4340,7 @@ function atomicWriteJson(targetPath, data) {
4111
4340
  }
4112
4341
  function isNonEmptyFile(path) {
4113
4342
  try {
4114
- return existsSync6(path) && statSync3(path).size > 0;
4343
+ return existsSync7(path) && statSync3(path).size > 0;
4115
4344
  } catch (error) {
4116
4345
  if (error instanceof Error)
4117
4346
  return false;
@@ -4409,8 +4638,8 @@ function readPlan(repoRoot, sessionId) {
4409
4638
  }
4410
4639
 
4411
4640
  // components/ulw-loop/src/stop-resume-hook.ts
4412
- import { existsSync as existsSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync3 } from "node:fs";
4413
- import { isAbsolute as isAbsolute2, join as join8, resolve as resolve4, sep as sep2 } from "node:path";
4641
+ import { existsSync as existsSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync3 } from "node:fs";
4642
+ import { isAbsolute as isAbsolute3, join as join8, resolve as resolve5, sep as sep3 } from "node:path";
4414
4643
  var RESUME_CAP = 2;
4415
4644
  var CONTEXT_PRESSURE_MARKERS2 = [
4416
4645
  "context compacted",
@@ -4478,8 +4707,8 @@ function consumeResumeBudgetLocked(lockPath, stateDir, goalId) {
4478
4707
  }
4479
4708
  function consumeResumeBudget(stateDir, goalId) {
4480
4709
  const ledgerLineCount = readLedgerAt(stateDir).length;
4481
- const counterPath = resolve4(stateDir, `auto-resume-${goalId}.json`);
4482
- const stuckPath = resolve4(stateDir, `auto-resume-${goalId}.stuck`);
4710
+ const counterPath = resolve5(stateDir, `auto-resume-${goalId}.json`);
4711
+ const stuckPath = resolve5(stateDir, `auto-resume-${goalId}.stuck`);
4483
4712
  if (!isInsideDir(stateDir, counterPath) || !isInsideDir(stateDir, stuckPath))
4484
4713
  return false;
4485
4714
  const previous = readCounter(counterPath);
@@ -4493,7 +4722,7 @@ function consumeResumeBudget(stateDir, goalId) {
4493
4722
  return true;
4494
4723
  }
4495
4724
  function isInsideDir(dir, candidate) {
4496
- return candidate.startsWith(resolve4(dir) + sep2);
4725
+ return candidate.startsWith(resolve5(dir) + sep3);
4497
4726
  }
4498
4727
  function renderResumeDirective(plan, goal, sessionId) {
4499
4728
  const normalized = normalizeUlwLoopSessionId(sessionId);
@@ -4519,7 +4748,7 @@ function readPlan2(repoRoot, sessionId) {
4519
4748
  }
4520
4749
  function readCounter(counterPath) {
4521
4750
  try {
4522
- if (!existsSync7(counterPath))
4751
+ if (!existsSync8(counterPath))
4523
4752
  return null;
4524
4753
  const parsed = JSON.parse(readFileSync7(counterPath, "utf8"));
4525
4754
  if (typeof parsed["count"] !== "number" || typeof parsed["ledgerLineCount"] !== "number")
@@ -4564,9 +4793,9 @@ function boulderPlanHasChecklist(cwd, entry) {
4564
4793
  const activePlan = entry["active_plan"];
4565
4794
  if (typeof activePlan !== "string" || activePlan.trim().length === 0)
4566
4795
  return false;
4567
- const planPath = isAbsolute2(activePlan) ? activePlan : join8(cwd, activePlan);
4796
+ const planPath = isAbsolute3(activePlan) ? activePlan : join8(cwd, activePlan);
4568
4797
  const worktree = entry["worktree_path"];
4569
- const candidates = typeof worktree === "string" && worktree.trim().length > 0 && !isAbsolute2(activePlan) ? [join8(isAbsolute2(worktree) ? worktree : join8(cwd, worktree), activePlan), planPath] : [planPath];
4798
+ const candidates = typeof worktree === "string" && worktree.trim().length > 0 && !isAbsolute3(activePlan) ? [join8(isAbsolute3(worktree) ? worktree : join8(cwd, worktree), activePlan), planPath] : [planPath];
4570
4799
  for (const candidate of candidates) {
4571
4800
  try {
4572
4801
  return readFileSync7(candidate, "utf8").split(/\r?\n/).some((line) => line.startsWith("- [ ] ") || line.startsWith("- [x] ") || line.startsWith("- [X] "));