lazycodex-ai 5.0.0-beta.63 → 5.0.0-beta.65
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +28 -23
- package/dist/cli-node/index.js +28 -23
- package/package.json +1 -1
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +8 -3
- package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
- package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
- package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
- package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/rules/package.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/agents/plan.toml +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/scripts/scaffold-plan.mjs +1 -0
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-codex-validation.d.ts +7 -5
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-codex-validation.js +7 -2
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint.js +2 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +364 -135
- package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-snapshot.d.ts +6 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-snapshot.js +20 -12
- package/packages/omo-codex/plugin/components/ulw-loop/dist/domain-types.d.ts +3 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/driver-objective-ack.d.ts +4 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/driver-objective-ack.js +18 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/evidence-artifacts.d.ts +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/evidence-artifacts.js +37 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/evidence.d.ts +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/evidence.js +8 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/paths.d.ts +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/paths.js +6 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-crud.d.ts +4 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-crud.js +6 -3
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-goal-factory.d.ts +10 -3
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-goal-factory.js +38 -31
- package/packages/omo-codex/plugin/components/ulw-loop/dist/review-blockers.js +8 -4
- package/packages/omo-codex/plugin/components/ulw-loop/dist/sdk/factory.js +17 -8
- package/packages/omo-codex/plugin/components/ulw-loop/dist/sdk/manifest.d.ts +335 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/sdk/manifest.js +109 -10
- package/packages/omo-codex/plugin/components/ulw-loop/dist/sdk/types.d.ts +3 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-mutations.d.ts +3 -2
- package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-mutations.js +4 -4
- package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-types.d.ts +9 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/steering.d.ts +3 -2
- package/packages/omo-codex/plugin/components/ulw-loop/dist/steering.js +6 -6
- package/packages/omo-codex/plugin/components/ulw-loop/dist/success-criteria-input.d.ts +9 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/success-criteria-input.js +41 -0
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint-codex-validation.ts +15 -3
- package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint.ts +2 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/codex-goal-snapshot.ts +26 -14
- package/packages/omo-codex/plugin/components/ulw-loop/src/domain-types.ts +3 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/driver-objective-ack.ts +20 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/evidence-artifacts.ts +45 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/evidence.ts +7 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/paths.ts +7 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/plan-crud.ts +9 -3
- package/packages/omo-codex/plugin/components/ulw-loop/src/plan-goal-factory.ts +63 -31
- package/packages/omo-codex/plugin/components/ulw-loop/src/review-blockers.ts +8 -4
- package/packages/omo-codex/plugin/components/ulw-loop/src/sdk/factory.ts +17 -7
- package/packages/omo-codex/plugin/components/ulw-loop/src/sdk/manifest.ts +124 -10
- package/packages/omo-codex/plugin/components/ulw-loop/src/sdk/types.ts +3 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/steering-mutations.ts +5 -4
- package/packages/omo-codex/plugin/components/ulw-loop/src/steering-types.ts +13 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/steering.ts +7 -6
- package/packages/omo-codex/plugin/components/ulw-loop/src/success-criteria-input.ts +56 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/codex-goal-snapshot.test.ts +37 -1
- package/packages/omo-codex/plugin/components/ulw-loop/test/sdk-driver-states.test.ts +33 -4
- package/packages/omo-codex/plugin/components/ulw-loop/test/sdk-dx.test.ts +219 -0
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/package-lock.json +12 -12
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/skills/ulw-plan/scripts/scaffold-plan.mjs +1 -0
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +10 -5
- package/packages/shared-skills/skills/ulw-plan/references/full-workflow.md +1 -0
- package/packages/shared-skills/skills/ulw-plan/scripts/scaffold-plan.mjs +1 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { ULW_LOOP_SUCCESS_CRITERION_USER_MODELS, UlwLoopError } from "./types.js";
|
|
2
|
+
function invalid(message, details) {
|
|
3
|
+
throw new UlwLoopError(`Invalid successCriteria: ${message}`, "ULW_LOOP_ARGUMENT_INVALID", { details });
|
|
4
|
+
}
|
|
5
|
+
function isUserModel(value) {
|
|
6
|
+
return typeof value === "string" && ULW_LOOP_SUCCESS_CRITERION_USER_MODELS.some((model) => model === value);
|
|
7
|
+
}
|
|
8
|
+
function requireText(value, field, index) {
|
|
9
|
+
const trimmed = typeof value === "string" ? value.trim() : "";
|
|
10
|
+
return trimmed || invalid(`entry ${index + 1} needs a non-empty ${field}.`, { index, field });
|
|
11
|
+
}
|
|
12
|
+
export function criterionId(index) {
|
|
13
|
+
return `C${String(index + 1).padStart(3, "0")}`;
|
|
14
|
+
}
|
|
15
|
+
export function criteriaFromInput(input) {
|
|
16
|
+
if (input.length === 0)
|
|
17
|
+
invalid("provide at least one criterion or omit the field for placeholders.", { count: 0 });
|
|
18
|
+
return input.map((entry, index) => {
|
|
19
|
+
if (typeof entry !== "object" || entry === null || Array.isArray(entry))
|
|
20
|
+
return invalid(`entry ${index + 1} must be an object.`, { index });
|
|
21
|
+
const record = { ...entry };
|
|
22
|
+
const userModel = record["userModel"] ?? "happy";
|
|
23
|
+
if (!isUserModel(userModel))
|
|
24
|
+
return invalid(`entry ${index + 1} userModel must be one of ${ULW_LOOP_SUCCESS_CRITERION_USER_MODELS.join(", ")}.`, {
|
|
25
|
+
index,
|
|
26
|
+
userModel: String(userModel),
|
|
27
|
+
});
|
|
28
|
+
const essential = record["essential"];
|
|
29
|
+
if (essential !== undefined && typeof essential !== "boolean")
|
|
30
|
+
return invalid(`entry ${index + 1} essential must be a boolean.`, { index });
|
|
31
|
+
return {
|
|
32
|
+
id: criterionId(index),
|
|
33
|
+
scenario: requireText(record["scenario"], "scenario", index),
|
|
34
|
+
userModel,
|
|
35
|
+
expectedEvidence: requireText(record["expectedEvidence"], "expectedEvidence", index),
|
|
36
|
+
essential: essential ?? true,
|
|
37
|
+
capturedEvidence: null,
|
|
38
|
+
status: "pending",
|
|
39
|
+
};
|
|
40
|
+
});
|
|
41
|
+
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"type": "command",
|
|
8
8
|
"command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook user-prompt-submit --with-ultrawork",
|
|
9
9
|
"timeout": 10,
|
|
10
|
-
"statusMessage": "(OmO 5.0.0-beta.
|
|
10
|
+
"statusMessage": "(OmO 5.0.0-beta.65) Checking Ulw-Loop Steering"
|
|
11
11
|
}
|
|
12
12
|
]
|
|
13
13
|
}
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"type": "command",
|
|
21
21
|
"command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook post-tool-use-spawn",
|
|
22
22
|
"timeout": 5,
|
|
23
|
-
"statusMessage": "(OmO 5.0.0-beta.
|
|
23
|
+
"statusMessage": "(OmO 5.0.0-beta.65) Recording Spawn Admission"
|
|
24
24
|
}
|
|
25
25
|
]
|
|
26
26
|
}
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"type": "command",
|
|
34
34
|
"command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook pre-tool-use",
|
|
35
35
|
"timeout": 5,
|
|
36
|
-
"statusMessage": "(OmO 5.0.0-beta.
|
|
36
|
+
"statusMessage": "(OmO 5.0.0-beta.65) Enforcing Unlimited Ulw-Loop Budget"
|
|
37
37
|
}
|
|
38
38
|
]
|
|
39
39
|
},
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"type": "command",
|
|
45
45
|
"command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook pre-tool-use-spawn",
|
|
46
46
|
"timeout": 5,
|
|
47
|
-
"statusMessage": "(OmO 5.0.0-beta.
|
|
47
|
+
"statusMessage": "(OmO 5.0.0-beta.65) Guarding Ulw-Loop Spawns"
|
|
48
48
|
}
|
|
49
49
|
]
|
|
50
50
|
}
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"type": "command",
|
|
57
57
|
"command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook stop",
|
|
58
58
|
"timeout": 10,
|
|
59
|
-
"statusMessage": "(OmO 5.0.0-beta.
|
|
59
|
+
"statusMessage": "(OmO 5.0.0-beta.65) Checking Ulw-Loop Resume"
|
|
60
60
|
}
|
|
61
61
|
]
|
|
62
62
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@code-yeongyu/codex-ulw-loop",
|
|
3
|
-
"version": "5.0.0-beta.
|
|
3
|
+
"version": "5.0.0-beta.65",
|
|
4
4
|
"description": "Codex plugin: durable repo-native multi-goal orchestration with embedded success criteria and observable evidence audit.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "npm@11.12.1",
|
|
@@ -4,11 +4,19 @@ import {
|
|
|
4
4
|
readCodexGoalSnapshotInput,
|
|
5
5
|
reconcileCodexGoalSnapshot,
|
|
6
6
|
} from "./codex-goal-snapshot.js";
|
|
7
|
+
import { acknowledgedDriverObjectives } from "./driver-objective-ack.js";
|
|
7
8
|
import { codexGoalMode, compatibleCodexObjectives, expectedCodexObjective } from "./goal-status.js";
|
|
8
9
|
import type { UlwLoopScope } from "./paths.js";
|
|
9
10
|
import type { UlwLoopItem, UlwLoopPlan } from "./types.js";
|
|
10
11
|
import { UlwLoopError } from "./types.js";
|
|
11
12
|
|
|
13
|
+
export interface CheckpointCodexGoalValidation {
|
|
14
|
+
readonly raw: unknown;
|
|
15
|
+
readonly nextActions: readonly string[];
|
|
16
|
+
readonly warnings: readonly string[];
|
|
17
|
+
readonly unacknowledgedObjective?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
12
20
|
export async function validateCheckpointCodexGoal(input: {
|
|
13
21
|
readonly repoRoot: string;
|
|
14
22
|
readonly plan: UlwLoopPlan;
|
|
@@ -16,11 +24,12 @@ export async function validateCheckpointCodexGoal(input: {
|
|
|
16
24
|
readonly raw: string | undefined;
|
|
17
25
|
readonly evidence: string;
|
|
18
26
|
readonly scope?: UlwLoopScope;
|
|
19
|
-
}): Promise<
|
|
27
|
+
}): Promise<CheckpointCodexGoalValidation> {
|
|
20
28
|
const snapshot = await readCodexGoalSnapshotInput(input.raw, input.repoRoot);
|
|
21
29
|
const expected = expectedCodexObjective(input.plan, input.goal);
|
|
22
30
|
const reconciliation = reconcileCodexGoalSnapshot(snapshot, {
|
|
23
31
|
expectedObjective: expected,
|
|
32
|
+
acknowledgedObjectives: acknowledgedDriverObjectives(input.plan),
|
|
24
33
|
...(codexGoalMode(input.plan) === "aggregate"
|
|
25
34
|
? { acceptedObjectives: compatibleCodexObjectives(input.plan) }
|
|
26
35
|
: {}),
|
|
@@ -28,8 +37,11 @@ export async function validateCheckpointCodexGoal(input: {
|
|
|
28
37
|
if (!reconciliation.ok) throw new CodexGoalSnapshotError(formatCodexGoalReconciliation(reconciliation));
|
|
29
38
|
return {
|
|
30
39
|
raw: snapshot?.raw,
|
|
31
|
-
nextActions: reconciliation.
|
|
32
|
-
warnings: reconciliation.warnings
|
|
40
|
+
nextActions: reconciliation.nextActions,
|
|
41
|
+
warnings: reconciliation.warnings,
|
|
42
|
+
...(reconciliation.unacknowledgedObjective === undefined
|
|
43
|
+
? {}
|
|
44
|
+
: { unacknowledgedObjective: reconciliation.unacknowledgedObjective }),
|
|
33
45
|
};
|
|
34
46
|
}
|
|
35
47
|
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
combineCheckpointValidationErrors,
|
|
7
7
|
validateCheckpointCodexGoal,
|
|
8
8
|
} from "./checkpoint-codex-validation.js";
|
|
9
|
+
import { acknowledgeDriverObjective } from "./driver-objective-ack.js";
|
|
9
10
|
import { requireAllCriteriaPass, requireAllPlanCriteriaPass, requireEssentialCriteriaPass } from "./evidence.js";
|
|
10
11
|
import { codexGoalMode, isFinalRunCompletionCandidate } from "./goal-status.js";
|
|
11
12
|
import type { UlwLoopScope } from "./paths.js";
|
|
@@ -190,6 +191,7 @@ export async function checkpointUlwLoop(
|
|
|
190
191
|
codexGoal = validation.raw;
|
|
191
192
|
nextActions = validation.nextActions;
|
|
192
193
|
warnings = validation.warnings;
|
|
194
|
+
acknowledgeDriverObjective(plan, validation.unacknowledgedObjective);
|
|
193
195
|
} catch (error) {
|
|
194
196
|
if (!(error instanceof UlwLoopError)) throw error;
|
|
195
197
|
codexValidationError = error;
|
|
@@ -22,13 +22,19 @@ export interface CodexGoalSnapshot {
|
|
|
22
22
|
export interface CodexGoalReconciliation {
|
|
23
23
|
ok: boolean;
|
|
24
24
|
snapshot: CodexGoalSnapshot;
|
|
25
|
+
/** Facts to know (a differing driver objective); never something to do. */
|
|
25
26
|
warnings: string[];
|
|
27
|
+
/** Things to do next (create, re-create, or resume the driver goal). */
|
|
28
|
+
nextActions: string[];
|
|
26
29
|
errors: string[];
|
|
30
|
+
/** The differing objective this reconciliation reported for the first time, for the caller to acknowledge. */
|
|
31
|
+
unacknowledgedObjective?: string;
|
|
27
32
|
}
|
|
28
33
|
|
|
29
34
|
export interface ReconcileCodexGoalOptions {
|
|
30
35
|
expectedObjective: string;
|
|
31
36
|
readonly acceptedObjectives?: readonly string[];
|
|
37
|
+
readonly acknowledgedObjectives?: readonly string[];
|
|
32
38
|
}
|
|
33
39
|
|
|
34
40
|
export class CodexGoalSnapshotError extends Error {}
|
|
@@ -111,35 +117,41 @@ export function reconcileCodexGoalSnapshot(
|
|
|
111
117
|
const effectiveSnapshot = snapshot ?? { available: false, raw: null };
|
|
112
118
|
const errors: string[] = [];
|
|
113
119
|
const warnings: string[] = [];
|
|
120
|
+
const nextActions: string[] = [];
|
|
114
121
|
|
|
115
122
|
const expected = options.expectedObjective;
|
|
116
|
-
const normalizedExpected = normalizeObjective(expected);
|
|
117
123
|
if (!effectiveSnapshot.available) {
|
|
118
|
-
|
|
119
|
-
return { ok: errors.length === 0, snapshot: effectiveSnapshot, warnings, errors };
|
|
124
|
+
nextActions.push(`call get_goal; if none, create_goal with codexObjective "${expected}" verbatim`);
|
|
125
|
+
return { ok: errors.length === 0, snapshot: effectiveSnapshot, warnings, nextActions, errors };
|
|
120
126
|
}
|
|
121
127
|
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
...(options.acceptedObjectives ?? []).map((objective) => normalizeObjective(objective)),
|
|
126
|
-
].filter(Boolean),
|
|
127
|
-
);
|
|
128
|
+
const normalized = (objectives: readonly string[]) => new Set(objectives.map(normalizeObjective).filter(Boolean));
|
|
129
|
+
const accepted = normalized([expected, ...(options.acceptedObjectives ?? [])]);
|
|
130
|
+
const acknowledged = normalized(options.acknowledgedObjectives ?? []);
|
|
128
131
|
const actual = normalizeObjective(effectiveSnapshot.objective ?? "");
|
|
129
|
-
|
|
132
|
+
let unacknowledgedObjective: string | undefined;
|
|
133
|
+
if (actual && !accepted.has(actual) && !acknowledged.has(actual)) {
|
|
130
134
|
warnings.push(`driver_objective_differs: expected "${expected}", got "${actual}".`);
|
|
135
|
+
unacknowledgedObjective = actual;
|
|
131
136
|
}
|
|
132
137
|
|
|
133
138
|
const actualStatus = effectiveSnapshot.status ?? "unknown";
|
|
134
139
|
if (actualStatus === "paused" || actualStatus === "usage_limited" || actualStatus === "budget_limited") {
|
|
135
|
-
|
|
140
|
+
nextActions.push("/goal resume or raise the budget");
|
|
136
141
|
}
|
|
137
142
|
if (actualStatus === "complete")
|
|
138
|
-
|
|
139
|
-
return {
|
|
143
|
+
nextActions.push(`driver closed early: call create_goal with codexObjective "${expected}" verbatim`);
|
|
144
|
+
return {
|
|
145
|
+
ok: errors.length === 0,
|
|
146
|
+
snapshot: effectiveSnapshot,
|
|
147
|
+
warnings,
|
|
148
|
+
nextActions,
|
|
149
|
+
errors,
|
|
150
|
+
...(unacknowledgedObjective === undefined ? {} : { unacknowledgedObjective }),
|
|
151
|
+
};
|
|
140
152
|
}
|
|
141
153
|
|
|
142
154
|
export function formatCodexGoalReconciliation(reconciliation: CodexGoalReconciliation): string {
|
|
143
|
-
const parts = [...reconciliation.errors, ...reconciliation.warnings];
|
|
155
|
+
const parts = [...reconciliation.errors, ...reconciliation.nextActions, ...reconciliation.warnings];
|
|
144
156
|
return parts.join(" ");
|
|
145
157
|
}
|
|
@@ -19,6 +19,7 @@ export interface UlwLoopSuccessCriterion {
|
|
|
19
19
|
status: UlwLoopCriterionStatus;
|
|
20
20
|
capturedAt?: string;
|
|
21
21
|
notes?: string;
|
|
22
|
+
artifacts?: string[];
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
export interface UlwLoopItem {
|
|
@@ -75,6 +76,7 @@ export interface UlwLoopPlan {
|
|
|
75
76
|
codexGoalMode?: UlwLoopCodexGoalMode;
|
|
76
77
|
codexObjective?: string;
|
|
77
78
|
codexObjectiveAliases?: string[];
|
|
79
|
+
acknowledgedDriverObjectives?: string[];
|
|
78
80
|
aggregateCompletion?: UlwLoopAggregateCompletion;
|
|
79
81
|
activeGoalId?: string;
|
|
80
82
|
validationBatches?: readonly UlwLoopValidationBatch[];
|
|
@@ -173,6 +175,7 @@ export interface UlwLoopLedgerEntry {
|
|
|
173
175
|
codexGoal?: unknown;
|
|
174
176
|
evidence?: string;
|
|
175
177
|
capturedEvidence?: string;
|
|
178
|
+
artifacts?: string[];
|
|
176
179
|
qualityGate?: unknown;
|
|
177
180
|
steering?: UlwLoopSteeringAudit;
|
|
178
181
|
before?: unknown;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { UlwLoopPlan } from "./types.js";
|
|
2
|
+
|
|
3
|
+
export function normalizeDriverObjective(value: string): string {
|
|
4
|
+
return value.replace(/\s+/g, " ").trim();
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function acknowledgedDriverObjectives(plan: UlwLoopPlan): readonly string[] {
|
|
8
|
+
return plan.acknowledgedDriverObjectives ?? [];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// A differing driver objective is reported once; the plan remembers the exact text so every later
|
|
12
|
+
// checkpoint under the same driver stays quiet, while a driver rewritten to a third objective is
|
|
13
|
+
// reported again.
|
|
14
|
+
export function acknowledgeDriverObjective(plan: UlwLoopPlan, objective: string | undefined): boolean {
|
|
15
|
+
if (objective === undefined) return false;
|
|
16
|
+
const normalized = normalizeDriverObjective(objective);
|
|
17
|
+
if (!normalized || acknowledgedDriverObjectives(plan).includes(normalized)) return false;
|
|
18
|
+
plan.acknowledgedDriverObjectives = [...acknowledgedDriverObjectives(plan), normalized];
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { isAbsolute, relative, resolve, sep } from "node:path";
|
|
3
|
+
import { UlwLoopError } from "./types.js";
|
|
4
|
+
|
|
5
|
+
function fail(message: string, code: string, details: Record<string, unknown>): never {
|
|
6
|
+
throw new UlwLoopError(message, code, { details });
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function stored(repoRoot: string, absolute: string): string {
|
|
10
|
+
const rel = relative(repoRoot, absolute);
|
|
11
|
+
const inside = rel !== "" && !rel.startsWith("..") && !isAbsolute(rel);
|
|
12
|
+
return inside ? rel.split(sep).join("/") : absolute;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Artifacts are bound at record time: a cited path that does not exist is a claim without a file,
|
|
16
|
+
// so the whole record is refused before any state changes. Paths inside the session cwd persist
|
|
17
|
+
// repo-relative (portable across machines); paths outside persist absolute.
|
|
18
|
+
export function resolveEvidenceArtifacts(
|
|
19
|
+
repoRoot: string,
|
|
20
|
+
artifacts: readonly unknown[] | undefined,
|
|
21
|
+
): string[] | undefined {
|
|
22
|
+
if (artifacts === undefined) return undefined;
|
|
23
|
+
const seen = new Set<string>();
|
|
24
|
+
const resolved: string[] = [];
|
|
25
|
+
for (const [index, candidate] of artifacts.entries()) {
|
|
26
|
+
const path = typeof candidate === "string" ? candidate.trim() : "";
|
|
27
|
+
if (!path) fail(`Artifact ${index + 1} must be a non-empty path.`, "ULW_LOOP_ARGUMENT_INVALID", { index });
|
|
28
|
+
const absolute = resolve(repoRoot, path);
|
|
29
|
+
if (!existsSync(absolute))
|
|
30
|
+
fail(
|
|
31
|
+
`Evidence artifact does not exist: ${path} (resolved to ${absolute}).`,
|
|
32
|
+
"ULW_LOOP_EVIDENCE_ARTIFACT_MISSING",
|
|
33
|
+
{
|
|
34
|
+
path,
|
|
35
|
+
resolved: absolute,
|
|
36
|
+
},
|
|
37
|
+
);
|
|
38
|
+
const value = stored(repoRoot, absolute);
|
|
39
|
+
if (!seen.has(value)) {
|
|
40
|
+
seen.add(value);
|
|
41
|
+
resolved.push(value);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return resolved;
|
|
45
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolveEvidenceArtifacts } from "./evidence-artifacts.js";
|
|
1
2
|
import { essentialCriteriaOf, hasAllCriteriaPass, hasEssentialCriteriaPass } from "./goal-status.js";
|
|
2
3
|
import type { UlwLoopScope } from "./paths.js";
|
|
3
4
|
import { commit } from "./plan-commit.js";
|
|
@@ -12,6 +13,7 @@ type RecordEvidenceArgs = {
|
|
|
12
13
|
readonly status: EvidenceStatus;
|
|
13
14
|
readonly evidence: string;
|
|
14
15
|
readonly notes?: string;
|
|
16
|
+
readonly artifacts?: readonly string[];
|
|
15
17
|
};
|
|
16
18
|
|
|
17
19
|
function ulwLoopFail(message: string, code: string, details: Record<string, unknown>): never {
|
|
@@ -67,6 +69,7 @@ export async function recordEvidence(
|
|
|
67
69
|
const goal = findGoal(plan, args.goalId);
|
|
68
70
|
const criterion = findCriterion(goal, args.criterionId);
|
|
69
71
|
const evidence = nonEmptyEvidence(args.evidence);
|
|
72
|
+
const artifacts = resolveEvidenceArtifacts(repoRoot, args.artifacts);
|
|
70
73
|
const kind = ledgerKind(args.status);
|
|
71
74
|
const prevStatus = criterion.status;
|
|
72
75
|
const capturedAt = iso();
|
|
@@ -74,6 +77,8 @@ export async function recordEvidence(
|
|
|
74
77
|
criterion.capturedEvidence = evidence;
|
|
75
78
|
criterion.capturedAt = capturedAt;
|
|
76
79
|
if (args.notes !== undefined) criterion.notes = args.notes;
|
|
80
|
+
if (artifacts !== undefined) criterion.artifacts = artifacts;
|
|
81
|
+
else delete criterion.artifacts;
|
|
77
82
|
goal.updatedAt = capturedAt;
|
|
78
83
|
plan.updatedAt = capturedAt;
|
|
79
84
|
const ledgerEntry: UlwLoopLedgerEntry = {
|
|
@@ -84,6 +89,7 @@ export async function recordEvidence(
|
|
|
84
89
|
criterionStatus: args.status,
|
|
85
90
|
evidence,
|
|
86
91
|
capturedEvidence: evidence,
|
|
92
|
+
...(artifacts === undefined ? {} : { artifacts }),
|
|
87
93
|
before: { status: prevStatus },
|
|
88
94
|
after: { goalId: goal.id, criterionId: criterion.id, status: args.status, evidence, capturedAt, prevStatus },
|
|
89
95
|
};
|
|
@@ -112,6 +118,7 @@ export async function markCriteriaPendingResetForGoal(
|
|
|
112
118
|
criterion.capturedEvidence = null;
|
|
113
119
|
delete criterion.capturedAt;
|
|
114
120
|
delete criterion.notes;
|
|
121
|
+
delete criterion.artifacts;
|
|
115
122
|
}
|
|
116
123
|
goal.updatedAt = now;
|
|
117
124
|
plan.updatedAt = now;
|
|
@@ -85,6 +85,13 @@ export function repoRelative(absolutePath: string, repoRoot: string): string {
|
|
|
85
85
|
return absolutePath.split("\\").join("/");
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
// The plan-level evidence root stays fixed for the whole run, unlike the per-goal attempt dir, so
|
|
89
|
+
// long lanes that span goals have one stable place to write artifacts.
|
|
90
|
+
export function ulwLoopEvidenceRoot(scope?: UlwLoopScope): string {
|
|
91
|
+
const sessionId = normalizeUlwLoopSessionId(scope?.sessionId);
|
|
92
|
+
return sessionId === null ? ".omo/evidence" : `.omo/evidence/ulw/${sessionId}`;
|
|
93
|
+
}
|
|
94
|
+
|
|
88
95
|
// Both the status --json emitter and the checkpoint enforcement resolve the attempt dir through
|
|
89
96
|
// this function from the scope alone; a second resolution path (env, a literal placeholder)
|
|
90
97
|
// would let the gate reject its own advertised directory.
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
import { beforePlanMutation, commit } from "./plan-commit.js";
|
|
9
9
|
import { appendGoalToPlan, deriveGoalCandidates, makeGoal } from "./plan-goal-factory.js";
|
|
10
10
|
import { planExists, readUlwLoopPlan, withUlwLoopMutationLock } from "./plan-io.js";
|
|
11
|
+
import type { SuccessCriterionInput } from "./success-criteria-input.js";
|
|
11
12
|
import type { UlwLoopToolkitSurface } from "./surface.js";
|
|
12
13
|
import type {
|
|
13
14
|
UlwLoopCodexGoalMode,
|
|
@@ -20,6 +21,7 @@ import { iso, UlwLoopError } from "./types.js";
|
|
|
20
21
|
import { parseValidationBatches } from "./validation-batch.js";
|
|
21
22
|
|
|
22
23
|
export { deriveGoalCandidates, seedDefaultSuccessCriteria } from "./plan-goal-factory.js";
|
|
24
|
+
export type { SuccessCriterionInput } from "./success-criteria-input.js";
|
|
23
25
|
|
|
24
26
|
export type UlwLoopPlanSummary = {
|
|
25
27
|
readonly total: number;
|
|
@@ -81,7 +83,7 @@ export async function createUlwLoopPlan(
|
|
|
81
83
|
}
|
|
82
84
|
const now = iso();
|
|
83
85
|
const goals = deriveGoalCandidates(args.brief).map((goal, index) =>
|
|
84
|
-
makeGoal(goal.title, goal.objective, index, now),
|
|
86
|
+
makeGoal(goal.title, goal.objective, index, now, { surface }),
|
|
85
87
|
);
|
|
86
88
|
const plan: UlwLoopPlan = {
|
|
87
89
|
version: 1,
|
|
@@ -128,13 +130,17 @@ function completedPlanExistsError(scope: UlwLoopScope | undefined, surface: UlwL
|
|
|
128
130
|
|
|
129
131
|
export async function addUlwLoopGoal(
|
|
130
132
|
repoRoot: string,
|
|
131
|
-
args: { title: string; objective: string },
|
|
133
|
+
args: { title: string; objective: string; successCriteria?: readonly SuccessCriterionInput[] },
|
|
132
134
|
scope?: UlwLoopScope,
|
|
135
|
+
surface: UlwLoopToolkitSurface = "lazycodex",
|
|
133
136
|
): Promise<{ plan: UlwLoopPlan; goal: UlwLoopItem }> {
|
|
134
137
|
return withUlwLoopMutationLock(repoRoot, scope, async () => {
|
|
135
138
|
const plan = await readUlwLoopPlan(repoRoot, scope);
|
|
136
139
|
const now = iso();
|
|
137
|
-
const goal = appendGoalToPlan(plan, args.title, args.objective, now
|
|
140
|
+
const goal = appendGoalToPlan(plan, args.title, args.objective, now, {
|
|
141
|
+
surface,
|
|
142
|
+
...(args.successCriteria === undefined ? {} : { successCriteria: args.successCriteria }),
|
|
143
|
+
});
|
|
138
144
|
await commit(repoRoot, scope, {
|
|
139
145
|
plan,
|
|
140
146
|
entries: [{ at: now, kind: "goal_added", goalId: goal.id, status: goal.status, message: goal.title }],
|
|
@@ -1,6 +1,14 @@
|
|
|
1
|
+
import { criteriaFromInput, criterionId, 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
4
|
import { UlwLoopError } from "./types.js";
|
|
3
5
|
|
|
6
|
+
export interface GoalSeedOptions {
|
|
7
|
+
readonly goalId?: string;
|
|
8
|
+
readonly surface?: UlwLoopToolkitSurface;
|
|
9
|
+
readonly successCriteria?: readonly SuccessCriterionInput[];
|
|
10
|
+
}
|
|
11
|
+
|
|
4
12
|
function cleanLine(line: string): string {
|
|
5
13
|
return line.replace(/^\s*(?:[-*+]\s+|\d+[.)]\s+)/, "").trim();
|
|
6
14
|
}
|
|
@@ -38,40 +46,44 @@ function truncateObjective(objective: string): string {
|
|
|
38
46
|
return objective.length > 80 ? `${objective.slice(0, 77).trimEnd()}...` : objective;
|
|
39
47
|
}
|
|
40
48
|
|
|
41
|
-
|
|
49
|
+
// The placeholder must name the exact call that replaces it on the surface the plan is driven from,
|
|
50
|
+
// so a goal seeded without criteria costs one documented call per criterion instead of a doc hunt.
|
|
51
|
+
function replaceVia(goalId: string, id: string, surface: UlwLoopToolkitSurface): string {
|
|
52
|
+
return surface === "omo-senpi"
|
|
53
|
+
? `Replace via agentToolkit.steer({ kind: "revise_criterion", source: "finding", goalId: "${goalId}", criterionId: "${id}", scenario, expectedEvidence, evidence, rationale }) (or pass successCriteria to addGoal)`
|
|
54
|
+
: `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>"`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function seedDefaultSuccessCriteria(
|
|
58
|
+
goalIndex: number,
|
|
59
|
+
objective: string,
|
|
60
|
+
options: Pick<GoalSeedOptions, "goalId" | "surface"> = {},
|
|
61
|
+
): UlwLoopSuccessCriterion[] {
|
|
42
62
|
const subject = truncateObjective(normalizeObjective(objective) || `Goal ${goalIndex + 1}`);
|
|
63
|
+
const goalId = options.goalId ?? `G${String(goalIndex + 1).padStart(3, "0")}`;
|
|
64
|
+
const surface = options.surface ?? "lazycodex";
|
|
43
65
|
const rows = [
|
|
66
|
+
["happy", `happy path for: ${subject}`, `observable happy-path proof for goal ${goalIndex + 1}`, true],
|
|
67
|
+
["edge", "edge case (boundary/empty/malformed)", `boundary or malformed-input proof for: ${subject}`, true],
|
|
44
68
|
[
|
|
45
|
-
"C001",
|
|
46
|
-
"happy",
|
|
47
|
-
`happy path for: ${subject}`,
|
|
48
|
-
`Replace via revise_criterion with observable happy-path proof for goal ${goalIndex + 1}.`,
|
|
49
|
-
true,
|
|
50
|
-
],
|
|
51
|
-
[
|
|
52
|
-
"C002",
|
|
53
|
-
"edge",
|
|
54
|
-
"edge case (boundary/empty/malformed)",
|
|
55
|
-
`Replace via revise_criterion with boundary or malformed-input proof for: ${subject}.`,
|
|
56
|
-
true,
|
|
57
|
-
],
|
|
58
|
-
[
|
|
59
|
-
"C003",
|
|
60
69
|
"regression",
|
|
61
70
|
"regression: adjacent surface still works",
|
|
62
|
-
`
|
|
71
|
+
`regression proof for neighboring behavior after: ${subject}`,
|
|
63
72
|
false,
|
|
64
73
|
],
|
|
65
74
|
] as const;
|
|
66
|
-
return rows.map(([
|
|
67
|
-
id
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
+
return rows.map(([userModel, scenario, proof, essential], index) => {
|
|
76
|
+
const id = criterionId(index);
|
|
77
|
+
return {
|
|
78
|
+
id,
|
|
79
|
+
scenario,
|
|
80
|
+
userModel,
|
|
81
|
+
expectedEvidence: `${replaceVia(goalId, id, surface)} with ${proof}.`,
|
|
82
|
+
essential,
|
|
83
|
+
capturedEvidence: null,
|
|
84
|
+
status: "pending",
|
|
85
|
+
};
|
|
86
|
+
});
|
|
75
87
|
}
|
|
76
88
|
|
|
77
89
|
export function deriveGoalCandidates(brief: string): Array<{ title: string; objective: string }> {
|
|
@@ -101,23 +113,43 @@ export function deriveGoalCandidates(brief: string): Array<{ title: string; obje
|
|
|
101
113
|
}));
|
|
102
114
|
}
|
|
103
115
|
|
|
104
|
-
export function makeGoal(
|
|
116
|
+
export function makeGoal(
|
|
117
|
+
title: string,
|
|
118
|
+
objective: string,
|
|
119
|
+
index: number,
|
|
120
|
+
now: string,
|
|
121
|
+
options: GoalSeedOptions = {},
|
|
122
|
+
): UlwLoopItem {
|
|
105
123
|
const cleanTitle = assertNonEmpty(title, "title");
|
|
106
124
|
const cleanObjective = assertNonEmpty(objective, "objective");
|
|
125
|
+
const id = normalizeGoalId(cleanTitle, index);
|
|
126
|
+
const successCriteria =
|
|
127
|
+
options.successCriteria === undefined
|
|
128
|
+
? seedDefaultSuccessCriteria(index, cleanObjective, {
|
|
129
|
+
goalId: id,
|
|
130
|
+
...(options.surface === undefined ? {} : { surface: options.surface }),
|
|
131
|
+
})
|
|
132
|
+
: criteriaFromInput(options.successCriteria);
|
|
107
133
|
return {
|
|
108
|
-
id
|
|
134
|
+
id,
|
|
109
135
|
title: cleanTitle,
|
|
110
136
|
objective: cleanObjective,
|
|
111
137
|
status: "pending",
|
|
112
|
-
successCriteria
|
|
138
|
+
successCriteria,
|
|
113
139
|
attempt: 0,
|
|
114
140
|
createdAt: now,
|
|
115
141
|
updatedAt: now,
|
|
116
142
|
};
|
|
117
143
|
}
|
|
118
144
|
|
|
119
|
-
export function appendGoalToPlan(
|
|
120
|
-
|
|
145
|
+
export function appendGoalToPlan(
|
|
146
|
+
plan: UlwLoopPlan,
|
|
147
|
+
title: string,
|
|
148
|
+
objective: string,
|
|
149
|
+
now: string,
|
|
150
|
+
options: GoalSeedOptions = {},
|
|
151
|
+
): UlwLoopItem {
|
|
152
|
+
const goal = makeGoal(title, objective, plan.goals.length, now, options);
|
|
121
153
|
plan.goals.push(goal);
|
|
122
154
|
plan.updatedAt = now;
|
|
123
155
|
return goal;
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
readCodexGoalSnapshotInput,
|
|
7
7
|
reconcileCodexGoalSnapshot,
|
|
8
8
|
} from "./codex-goal-snapshot.js";
|
|
9
|
+
import { acknowledgeDriverObjective, acknowledgedDriverObjectives } from "./driver-objective-ack.js";
|
|
9
10
|
import { compatibleCodexObjectives, expectedCodexObjective, isFinalRunCompletionCandidate } from "./goal-status.js";
|
|
10
11
|
import type { UlwLoopScope } from "./paths.js";
|
|
11
12
|
import { commit } from "./plan-commit.js";
|
|
@@ -40,12 +41,13 @@ function nextGoalId(plan: UlwLoopPlan): string {
|
|
|
40
41
|
|
|
41
42
|
function appendBlockerGoal(plan: UlwLoopPlan, args: RecordFinalReviewBlockersArgs, now: string): UlwLoopItem {
|
|
42
43
|
const index = plan.goals.length;
|
|
44
|
+
const id = nextGoalId(plan);
|
|
43
45
|
const goal: UlwLoopItem = {
|
|
44
|
-
id
|
|
46
|
+
id,
|
|
45
47
|
title: args.title,
|
|
46
48
|
objective: args.objective,
|
|
47
49
|
status: "pending",
|
|
48
|
-
successCriteria: seedDefaultSuccessCriteria(index, args.objective),
|
|
50
|
+
successCriteria: seedDefaultSuccessCriteria(index, args.objective, { goalId: id }),
|
|
49
51
|
attempt: 0,
|
|
50
52
|
createdAt: now,
|
|
51
53
|
updatedAt: now,
|
|
@@ -70,8 +72,10 @@ export async function recordFinalReviewBlockers(
|
|
|
70
72
|
const reconciliation = reconcileCodexGoalSnapshot(snapshot, {
|
|
71
73
|
expectedObjective: expectedCodexObjective(plan, goal),
|
|
72
74
|
acceptedObjectives: compatibleCodexObjectives(plan),
|
|
75
|
+
acknowledgedObjectives: acknowledgedDriverObjectives(plan),
|
|
73
76
|
});
|
|
74
77
|
if (!reconciliation.ok) throw new CodexGoalSnapshotError(formatCodexGoalReconciliation(reconciliation));
|
|
78
|
+
acknowledgeDriverObjective(plan, reconciliation.unacknowledgedObjective);
|
|
75
79
|
|
|
76
80
|
const now = iso();
|
|
77
81
|
for (const field of BLOCKER_FIELDS) Reflect.deleteProperty(goal, field);
|
|
@@ -95,8 +99,8 @@ export async function recordFinalReviewBlockers(
|
|
|
95
99
|
blockedGoal: goal,
|
|
96
100
|
newGoal,
|
|
97
101
|
ledgerEntries,
|
|
98
|
-
nextActions: reconciliation.
|
|
99
|
-
warnings: reconciliation.warnings
|
|
102
|
+
nextActions: reconciliation.nextActions,
|
|
103
|
+
warnings: reconciliation.warnings,
|
|
100
104
|
};
|
|
101
105
|
});
|
|
102
106
|
}
|