Yeachan-Heo/oh-my-codex · error · UltragoalError

Cannot add a goal to an already completed aggregate ultragoa

Error message

Cannot add a goal to an already completed aggregate ultragoal plan; start a new plan for post-terminal work.

What it means

addUltragoalGoal rejects additions to a plan whose aggregateCompletion.status is 'complete'. Once the aggregate ultragoal is terminal, the plan is considered closed and post-terminal work must go into a new plan.

Source

Thrown at src/ultragoal/artifacts.ts:1177

    title,
    objective,
    status: 'pending',
    attempt: 0,
    createdAt: now,
    updatedAt: now,
    evidence: options.evidence,
    resolvesReviewBlockedGoalId: options.resolvesReviewBlockedGoalId,
  };
  plan.goals.push(goal);
  plan.updatedAt = now;
  return goal;
}

export async function addUltragoalGoal(cwd: string, options: AddUltragoalGoalOptions): Promise<{ plan: UltragoalPlan; goal: UltragoalItem }> {
  return withUltragoalMutationLock(cwd, async () => {
  const plan = await readUltragoalPlanUnderLock(cwd);
  if (plan.aggregateCompletion?.status === 'complete') {
    throw new UltragoalError('Cannot add a goal to an already completed aggregate ultragoal plan; start a new plan for post-terminal work.');
  }
  const now = iso(options.now);
  const goal = appendGoalToPlan(plan, options);
  await writePlan(cwd, plan);
  await appendLedger(cwd, {
    ts: now,
    event: 'goal_added',
    goalId: goal.id,
    status: goal.status,
    evidence: options.evidence,
    message: goal.title,
  });
  return { plan, goal };
  });
}


function proposalTargetIds(proposal: UltragoalSteeringProposal): string[] {

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Create a fresh plan for the new work: `omx ultragoal create-goals --force` (or in a new plan location) instead of appending
  2. If the completion was premature/mistaken, edit or re-run the finalize flow to reopen the plan before adding goals
  3. Add a pre-check in automation: read the plan and skip add when aggregateCompletion.status === 'complete'

Example fix

// before
await addUltragoalGoal(cwd, { objective: 'post-launch tweak' }); // throws 1174

// after
const plan = await readUltragoalPlan(cwd);
if (plan.aggregateCompletion?.status === 'complete') {
  await createUltragoalPlan(cwd, { brief: 'post-launch', force: true });
} else {
  await addUltragoalGoal(cwd, { objective: 'post-launch tweak' });
}
Defensive patterns

Strategy: type-guard

Validate before calling

const plan = await readUltragoalPlan(cwd);
const completed = plan.aggregateCompletion?.status === 'complete';
if (!completed) await addUltragoalGoal(cwd, opts);

Type guard

function isPlanOpen(plan: UltragoalPlan): boolean { return plan.aggregateCompletion?.status !== 'complete'; }

Try / catch

try { await addUltragoalGoal(cwd, opts); } catch (e) { if (e instanceof UltragoalError && e.message.includes('already completed')) { await createUltragoalPlan(cwd, { brief: 'post-terminal', force: true }); } else throw e; }

Prevention

When it happens

Trigger: Calling addUltragoalGoal on a plan that was previously finalized with aggregate completion marked complete — e.g. continuing to append goals after a release/milestone was closed out.

Common situations: Reopening work after a milestone was marked complete; automation that appends goals on a schedule hitting an already-finalized repo; two teams sharing a repo where one finalized the plan.

Related errors


AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27). Data as JSON: /api/errors/baeacb3feec963c0. Report an issue: GitHub.