Yeachan-Heo/oh-my-codex · error · UltragoalError
omx ultragoal steer rejects broad natural-language mutation
Error message
omx ultragoal steer rejects broad natural-language mutation requests; pass structured fields or --directive-json.
What it means
ultragoal steer deliberately refuses freeform natural-language positional text as a mutation request. Because mutations are leader-owned and auditable, the CLI only accepts structured fields (--kind/--evidence/--rationale/etc.), an --after-json proposal, or a full --directive-json document.
Source
Thrown at src/cli/ultragoal.ts:274
targetGoalId: normalizeTargetGoalId(raw),
title: typeof raw.title === 'string' ? raw.title : undefined,
objective: typeof raw.objective === 'string' ? raw.objective : undefined,
after,
pendingOrder: Array.isArray(raw.pendingOrder) ? raw.pendingOrder.filter((id): id is string => typeof id === 'string') : undefined,
idempotencyKey: typeof raw.idempotencyKey === 'string' ? raw.idempotencyKey : undefined,
};
}
async function parseSteeringProposal(args: readonly string[]): Promise<UltragoalSteeringProposal> {
const directiveFile = readValue(args, '--directive-file');
const directiveRaw = readValue(args, '--directive-json') ?? (directiveFile ? await readFile(directiveFile, 'utf-8') : undefined);
if (directiveRaw) {
const directive = assertPlainObject(await readJsonInput(directiveRaw, '--directive-json'), '--directive-json');
return normalizeSteeringProposal(directive, directiveRaw.trim().startsWith('{') ? directiveRaw : undefined);
}
const freeform = positionalText(args);
if (freeform) throw new UltragoalError('omx ultragoal steer rejects broad natural-language mutation requests; pass structured fields or --directive-json.');
const after = await readJsonInput(readValue(args, '--after-json'), '--after-json');
return normalizeSteeringProposal({
kind: readValue(args, '--kind'),
evidence: readValue(args, '--evidence'),
rationale: readValue(args, '--rationale'),
targetGoalId: readValue(args, '--target-goal-id') ?? readValue(args, '--goal-id'),
title: readValue(args, '--title'),
objective: readValue(args, '--objective'),
after,
idempotencyKey: readValue(args, '--idempotency-key'),
});
}
function buildSteerDirectiveText(args: readonly string[]): string {
return args.join(' ');
}
View on GitHub (pinned to 3ad79a8a6f)
Solutions
- Convert the intent into structured flags: --kind, --evidence, --rationale plus kind-specific fields
- Use --after-json with a proposal object
- Use --directive-json with a complete directive document for complex multi-field mutations
Example fix
# before omx ultragoal steer "pause the api goal" # Error: omx ultragoal steer rejects broad natural-language mutation requests... # after omx ultragoal steer --kind update_goal --goal-id api-1 --status paused --evidence 'flaky ci' --rationale 'pause until green'
Defensive patterns
Strategy: validation
Validate before calling
const args = ['pause the api goal']; // freeform
if (args.length && !hasDirectiveJson) {
// convert intent to structured flags instead of passing prose
throw new Error('convert freeform text to --kind/--evidence/--rationale or --directive-json');
} Prevention
- Never pass prose to steer; author mutations as structured flags or JSON
- Keep a snippet library of well-formed steer commands
When it happens
Trigger: Running `omx ultragoal steer "please pause all goals"` — i.e. supplying positional freeform text without --directive-json; the code detects positionalText(args) and throws.
Common situations: Habit from other AI CLIs that accept prose commands; older workflows before structured steering was enforced; scripts written against a previous freeform interface.
Related errors
- Missing --kind for structured ultragoal steer.
- Missing --evidence.
- Missing --rationale.
- Invalid --codex-goal-mode; expected aggregate or per-story.
- Invalid ${label}: ${message}
AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27).
Data as JSON: /api/errors/00a0bb8f50dbf9ee.
Report an issue: GitHub.