feat(omo-claude): port ultragoal to per-session file-based goals

Goal content under ./.omo/ultragoal/sessions/claude-<id>/ keyed by Claude Code
session_id (claude: prefix); UltragoalScope struct threaded; plan version 2 +
index.json registry with read-only v1 forward-migration (never deletes v1);
create_goal/get_goal/update_goal dependency dropped (file/steering-based); the
PreToolUse create_goal budget guard kept compiled+unit-tested but its hooks.json
registration removed (inert in CC). 26 tests pass. ultragoal removed from
sync-components HANDLED set — it is a hand-fork, not patch-synced.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
YeonGyu-Kim
2026-05-29 13:27:22 +09:00
parent 6123fd8a43
commit 71e4e2a6d0
33 changed files with 3282 additions and 1 deletions
@@ -0,0 +1,16 @@
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "node \"${CLAUDE_PLUGIN_ROOT}/dist/cli.js\" hook user-prompt-submit",
"timeout": 10,
"statusMessage": "checking ultragoal steering"
}
]
}
]
}
}
@@ -0,0 +1,55 @@
{
"name": "@code-yeongyu/codex-ultragoal",
"version": "0.1.0",
"description": "Codex plugin: durable repo-native multi-goal orchestration with embedded success criteria and observable evidence audit.",
"type": "module",
"packageManager": "npm@11.12.1",
"license": "MIT",
"homepage": "https://github.com/code-yeongyu/codex-ultragoal",
"repository": {
"type": "git",
"url": "git+https://github.com/code-yeongyu/codex-ultragoal.git"
},
"bugs": {
"url": "https://github.com/code-yeongyu/codex-ultragoal/issues"
},
"keywords": [
"codex",
"codex-plugin",
"ultragoal",
"goal-mode",
"orchestration",
"evidence",
"typescript"
],
"bin": {
"omo": "./dist/cli.js"
},
"files": [
"dist",
"hooks",
"skills",
"LICENSE",
"NOTICE",
"README.md",
"CHANGELOG.md"
],
"scripts": {
"build": "tsc -p tsconfig.build.json",
"test": "vitest --run",
"test:watch": "vitest",
"typecheck": "tsc --noEmit",
"lint": "biome check .",
"lint:fix": "biome check --write .",
"check": "tsc --noEmit && biome check . && npm run build"
},
"devDependencies": {
"@biomejs/biome": "2.4.15",
"@types/node": "^25.7.0",
"typescript": "^6.0.3",
"vitest": "^4.1.5"
},
"engines": {
"node": ">=20.0.0"
}
}
@@ -0,0 +1,198 @@
---
name: ulw-loop
description: Goal-like loop that uses ultrawork mode to decompose work into systematic, evidence-bound steps.
metadata:
short-description: Goal-like ultrawork loop for systematic decomposition
---
## Role
Expert goal orchestration agent. Plan multi-goal work that survives across turns and sessions.
Use GPT-5.x style: outcome-first, evidence-bound, atomic decisions, no nested branching prose.
## Goal
Deliver every goal in `.omo/ultragoal/goals.json` end-to-end.
Prove EVERY success criterion with captured observable evidence from a real-usage scenario you actually ran (HTTP call / tmux / browser use / computer use — see the Manual-QA channels below).
TESTS ALONE NEVER PROVE DONE. A green test suite is supporting evidence, not completion proof.
Audit each pass, fail, block, steering change, and checkpoint in `.omo/ultragoal/ledger.jsonl`.
## Manual-QA channels (PICK ONE PER CRITERION — ACTUALLY RUN IT)
For every criterion, build a real-usage scenario through ONE of these four channels and run it yourself before recording PASS. The full test suite being green is NEVER verification on its own.
1. **HTTP call** — hit the live endpoint with `curl -i` (or a Playwright APIRequestContext); capture status line + headers + body.
2. **tmux**`tmux new-session -d -s ulw-qa-<criterion>`, drive with `send-keys`, dump via `tmux capture-pane -pS -E -`; transcript is the artifact.
3. **Browser use** — drive the real page via Playwright / puppeteer / Chromium; capture action log + screenshot path.
4. **Computer use** — OS-level GUI automation (computer-use agent, AppleScript, xdotool, etc.) against the running app; capture action log + screenshot.
Auxiliary surfaces (pure CLI stdout / DB state diff / parsed config dump) satisfy CLI- or data-shaped criteria but NEVER replace a channel scenario for user-facing behavior. `--dry-run`, printing the command, "should respond", and "looks correct" never count.
## Artifacts
- `.omo/ultragoal/brief.md`: original brief and durable constraints.
- `.omo/ultragoal/goals.json`: goals with embedded `successCriteria` per goal.
- `.omo/ultragoal/ledger.jsonl`: append-only audit trail.
- Read artifacts before resuming, steering, or checkpointing.
- Never invent state outside `.omo/ultragoal` artifacts or `omo ultragoal status --json`.
## Bootstrap
Do all three steps before execution. No edits, goal tools, or checkpointing before bootstrap completes.
### 1. Create goals from the brief
Resolve the CLI before the first command. If `omo` is absent from PATH, use the stable local installer bin or cached Codex component CLI. This is the same ultragoal CLI, so PATH absence is not a blocker. If PATH is empty, the fallback uses shell builtins and absolute Node locations before reporting guidance, and records the failure in `.omo/ultragoal/bootstrap-notepad.md`.
```sh
if command -v omo >/dev/null 2>&1; then
ULTRAGOAL_CLI=omo
else
CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"
ULTRAGOAL_CLI=
if [ -f "$CODEX_HOME/bin/omo" ] || [ -x "$CODEX_HOME/bin/omo" ]; then
ULTRAGOAL_CLI="$CODEX_HOME/bin/omo"
else
for candidate in "$CODEX_HOME"/plugins/cache/sisyphuslabs/omo/*/components/ultragoal/dist/cli.js; do
[ -f "$candidate" ] || continue
ULTRAGOAL_CLI="$candidate"
done
fi
ULTRAGOAL_NODE="$(command -v node 2>/dev/null || true)"
if [ -z "$ULTRAGOAL_NODE" ]; then
for candidate in /opt/homebrew/bin/node /usr/local/bin/node /usr/bin/node; do
[ -x "$candidate" ] || continue
ULTRAGOAL_NODE="$candidate"
break
done
fi
if [ -n "$ULTRAGOAL_CLI" ] && [ -n "$ULTRAGOAL_NODE" ]; then
omo() { "$ULTRAGOAL_NODE" "$ULTRAGOAL_CLI" "$@"; }
fi
fi
if [ -z "${ULTRAGOAL_CLI:-}" ]; then
/bin/mkdir -p .omo/ultragoal 2>/dev/null || mkdir -p .omo/ultragoal 2>/dev/null || true
NOTE="${NOTE:-.omo/ultragoal/bootstrap-notepad.md}"
printf '%s\n' "omo executable missing from PATH; cached ultragoal CLI not found under ${CODEX_HOME:-$HOME/.codex}." >> "$NOTE" 2>/dev/null || true
printf '%s\n' "Install with bunx omo install --platform=codex or set CODEX_LOCAL_BIN_DIR to a PATH directory." >&2
fi
```
If `ULTRAGOAL_CLI` is empty, open the durable notepad first, record the missing CLI evidence, then surface the installer issue.
Run one form:
```sh
omo ultragoal create-goals --brief "<brief>" --json
omo ultragoal create-goals --brief-file <path> --json
cat <brief> | omo ultragoal create-goals --from-stdin --json
```
Write state through the CLI path. Do not hand-edit state files.
### 2. Refine success criteria per goal
Define pass/fail acceptance criteria before launching execution lanes. Include the command, artifact, or manual check that will prove success.
Each goal MUST carry 3+ `successCriteria` covering happy path, edge, regression, and adversarial risk.
For each criterion set: `id`, `scenario`, `expectedEvidence`, adversarial classes, stop condition, and the Manual-QA channel (HTTP call / tmux / browser use / computer use) that will exercise it.
Apply ultraqa classes where relevant: malformed input, repeated interruptions, prompt injection, cancel/resume, stale state, dirty worktree, hung or long commands, flaky tests, misleading success output.
Use evidence verbs from the channel table (tmux transcript, curl status+body, browser screenshot, computer-use action log, CLI stdout, DB diff, parsed config dump) — not vibes.
"Tests pass" is supporting signal, NEVER completion proof. Every criterion needs its own channel scenario, built fresh and exercised every time.
Record manual QA notes when behavior is user-visible.
Revise any criterion that lacks observable `expectedEvidence` or a named channel before execution.
### 3. Inspect state
Run `omo ultragoal status --json`.
Read pending goals, criteria IDs, current ledger head, blockers, and aggregate Codex objective.
## Execution Loop
Loop per goal. Cap at 5 cycles per goal. Cap identical same-criterion failures at 3.
### Acquire Next Goal
1. Run `omo ultragoal complete-goals --json` and read the handoff, including criteria.
2. Call `get_goal` and inspect active Codex state.
3. Apply this table exactly:
| get_goal result | action |
|-----------------|--------|
| no active goal | Call `create_goal` with the handoff payload. |
| same aggregate objective active | Continue the current ultragoal story. |
| different goal active | STOP. Checkpoint blocked and surface the conflict. |
4. If retrying failed work, run `omo ultragoal complete-goals --retry-failed --json`.
5. Never create a second Codex goal for the same aggregate objective.
### Per-Criterion Cycle
1. PLAN: read `criterion.scenario`, `criterion.expectedEvidence`, prior ledger entries, and safety bounds.
2. Register atomic todos: `path: <action> for <criterion> - verify by <check>`.
3. EXECUTE-AS-SCENARIO: do one bounded change, then ACTUALLY run the Manual-QA channel scenario the criterion named (HTTP call / tmux / browser use / computer use — see the channel table above). The unit suite being green is NEVER substitute for running the channel scenario.
4. CAPTURE: collect the observable artifact path: transcript, stdout, screenshot, assertion, status+body, diff, or parsed dump.
5. CLEAN (PAIRED, NEVER SKIP): tear down every runtime artifact step 3 spawned BEFORE recording — server PIDs (`kill`, verify `kill -0` fails), `tmux` sessions (`tmux kill-session -t ulw-qa-<criterion>`; confirm `tmux ls`), browser / Playwright contexts (`.close()`), containers (`docker rm -f`), bound ports (`lsof -i :<port>` empty), temp sockets / files / dirs (`rm -rf` the `mktemp` paths), QA-only env vars. Embed a one-line cleanup receipt in the evidence string, e.g. `cleanup: killed 12345; tmux kill-session ulw-qa-foo; rm -rf /tmp/ulw.aB12cD`. Missing receipt → record BLOCKED, not PASS.
6. RECORD exactly one result:
- PASS: `omo ultragoal record-evidence --goal-id <id> --criterion-id <id> --status pass --evidence "<observable> | <cleanup receipt>" --json`
- FAIL: `omo ultragoal record-evidence --goal-id <id> --criterion-id <id> --status fail --evidence "<observable> | <cleanup receipt>" --notes "<diagnosis>" --json`
- BLOCKED: `omo ultragoal record-evidence --goal-id <id> --criterion-id <id> --status blocked --evidence "<observable>" --notes "<safety/blocker/leftover-state>" --json`
7. If actual does not match expected, diagnose, fix minimally, and rerun the SAME criterion (including a fresh cleanup).
8. After 3 same-criterion failures, exit the goal with diagnosis.
9. After 5 cycles on one goal without all criteria passing, checkpoint failed.
10. Continue only when the next pending criterion has a concrete `expectedEvidence` target.
### Goal Completion
1. Confirm every criterion is `pass` with `omo ultragoal criteria --goal-id <id> --json`.
2. Call `get_goal` for a fresh snapshot.
3. Run `omo ultragoal checkpoint --goal-id <id> --status complete --evidence "<criteria evidence summary>" --codex-goal-json <snapshot> --json`.
4. If blocked or failed, checkpoint with `--status blocked` or `--status failed` and include diagnosis evidence.
5. If this is the final goal, run the final quality gate first and pass `--quality-gate-json`.
## Final Quality Gate
Trigger only when one goal remains and all its criteria are passing.
1. Run targeted verification for changed behavior.
2. Run `ai-slop-cleaner` on changed files. If no relevant edits exist, record a passed no-op cleaner report.
3. Rerun verification after cleanup.
4. Run `$code-review`.
5. Clean review means `codeReview.recommendation == "APPROVE"` and `codeReview.architectStatus == "CLEAR"`.
6. If review is non-clean, run `omo ultragoal record-review-blockers --goal-id <id> --title "<...>" --objective "<...>" --evidence "<review findings>" --codex-goal-json <snapshot> --json`.
7. If clean, checkpoint final completion:
```sh
omo ultragoal checkpoint --goal-id <id> --status complete --evidence "<e2e evidence + manual QA notes>" --codex-goal-json <snapshot> --quality-gate-json <json-or-path> --json
```
`--quality-gate-json` shape:
```json
{
"aiSlopCleaner": { "status": "passed", "evidence": "cleaner report" },
"verification": { "status": "passed", "commands": ["npm test"], "evidence": "post-cleaner verification" },
"codeReview": { "recommendation": "APPROVE", "architectStatus": "CLEAR", "evidence": "review synthesis" },
"criteriaCoverage": { "totalCriteria": N, "passCount": N, "adversarialClassesCovered": ["malformed_input", "..."] }
}
```
## Dynamic Steering
Use steering only for structured evidence-backed mutation. Reject natural-language steering requests.
| Kind | When to use | Required fields |
|------|-------------|-----------------|
| add_subgoal | Real blocker found; new story required | `--title`, `--objective`, `--evidence`, `--rationale` |
| split_subgoal | Story too large; needs decomposition | `--goal-id`, `--children` JSON, `--evidence`, `--rationale` |
| reorder_pending | Discovered dependency order | `--order` JSON array of ids, `--evidence`, `--rationale` |
| revise_pending_wording | Title/objective ambiguous | `--goal-id`, `--title?`, `--objective?`, `--evidence`, `--rationale` |
| revise_criterion | Criterion lacks observable PASS evidence | `--goal-id`, `--criterion-id`, `--scenario?`, `--expected-evidence?`, `--evidence`, `--rationale` |
| annotate_ledger | Audit-only note | `--evidence`, `--rationale` |
| mark_blocked_superseded | Old story replaced by new evidence | `--goal-id`, `--replacements?`, `--evidence`, `--rationale` |
Command form: `omo ultragoal steer --kind <kind> [<kind-specific-fields>] --evidence "<...>" --rationale "<...>" --json`.
Structured prompt directives accepted: `OMO_ULTRAGOAL_STEER: { ... }`, `omo.ultragoal.steer: {...}`, `omo ultragoal steer: {...}`.
## Constraints
1. NEVER call `update_goal` mid-aggregate; only on final story after the quality gate passes.
2. NEVER call `create_goal` when `get_goal` shows a different active goal.
3. NEVER mark `criterion.status == "pass"` without captured observable evidence in `record-evidence`.
4. NEVER bypass the criteria gate at checkpoint; all criteria must be `pass` before `--status complete`.
5. Baseline build/lint/typecheck/test commands are necessary evidence, NOT SUFFICIENT completion proof. Criteria coverage with observable evidence is the gate.
6. Treat `.omo/ultragoal/ledger.jsonl` as the durable audit trail; checkpoint after every success or failure.
7. Per-story Codex goal mode is opt-in only with `--codex-goal-mode per-story`; default is aggregate.
8. Structured steering directives mutate state through validation; normal prose does not.
9. Evidence MUST be observable from the real surface: tmux transcript, curl status+body, browser/Playwright assertion, CLI stdout, DB state diff, parsed config dump.
10. Apply ultraqa's 9 adversarial classes where relevant per goal: malformed input, prompt injection, cancel/resume, stale state, dirty worktree, hung commands, flaky tests, misleading success output, repeated interruptions.
11. After completing an aggregate ultragoal run, clear the Codex goal manually with `/goal clear` before starting another in the same session.
12. The shell command emits a model-facing handoff; only the Codex agent calls `get_goal`, `create_goal`, or `update_goal` tools.
13. NEVER record `--status pass` while a QA-spawned process, `tmux` session, browser context, bound port, container, or temp file / dir is still alive. The evidence string MUST include the cleanup receipt. Leftover runtime state = BLOCKED, not PASS.
## Stop Rules
- All goals complete plus all criteria `pass` plus final quality gate clean: DONE.
- 3x same criterion failure: checkpoint failed, surface diagnosis.
- 5 cycles on one goal without all-pass: checkpoint failed, surface.
- Safety boundary such as destructive command, secret exfiltration, or production write: block and surface a safe substitute.
- Codex `get_goal` reports a different active goal: checkpoint blocker, stop, surface.
- Leftover state from QA (live process, `tmux` session, browser context, bound port, temp dir): NOT pass. Clean up, append the receipt, then continue.
- User issues `/cancel`: release in-progress state cleanly and do not auto-resume.
@@ -0,0 +1,6 @@
interface:
display_name: "ulw loop"
short_description: "Goal-like ultrawork loop for systematic decomposition"
search_terms:
- "ultragoal"
default_prompt: "Use $ulw-loop to break this work into a systematic ultrawork loop with evidence-backed checkpoints."
@@ -0,0 +1,161 @@
// biome-ignore-all format: keep checkpoint orchestration below the pure LOC budget.
import { existsSync } from "node:fs";
import { readFile } from "node:fs/promises";
import { resolve } from "node:path";
import { formatGoalReconciliation, readGoalSnapshotInput, reconcileGoalSnapshot } from "./goal-snapshot.js";
import { requireAllCriteriaPass } from "./evidence.js";
import { compatibleObjectives, expectedObjective, goalMode, isFinalRunCompletionCandidate } from "./goal-status.js";
import { ultragoalBriefPath } from "./paths.js";
import { appendLedger, readUltragoalPlan, withUltragoalMutationLock, writePlan } from "./plan-io.js";
import { classifyExternalAuthorizationBlocker, clearGoalBlockerFields, sameBlockerOccurrences, validateQualityGate } from "./quality-gate.js";
import type { UltragoalScope } from "./session-scope.js";
import type { UltragoalAggregateCompletion, UltragoalItem, UltragoalLedgerEntry, UltragoalPlan, UltragoalQualityGate } from "./types.js";
import { iso, ULTRAGOAL_DIR, ULTRAGOAL_GOALS, ULTRAGOAL_LEDGER, UltragoalError } from "./types.js";
export interface CheckpointUltragoalArgs { readonly goalId: string; readonly status: "complete" | "failed" | "blocked"; readonly evidence: string; readonly goalSnapshotJson?: string; readonly qualityGateJson?: string }
export interface CheckpointUltragoalResult { readonly plan: UltragoalPlan; readonly goal: UltragoalItem; readonly ledgerEntry: UltragoalLedgerEntry; readonly aggregateCompletion?: UltragoalAggregateCompletion }
function ultragoalFail(message: string, code: string): never { throw new UltragoalError(message, code); }
function normalizeObjective(value: string): string { return value.replace(/\s+/g, " ").trim(); }
function nonEmptyEvidence(value: string): string { const trimmed = value.trim(); return trimmed || ultragoalFail("Evidence must be a non-empty string.", "ultragoal_evidence_required"); }
function findGoal(plan: UltragoalPlan, goalId: string): UltragoalItem { const goal = plan.goals.find((candidate) => candidate.id === goalId); return goal ?? ultragoalFail(`Unknown ultragoal id: ${goalId}.`, "ultragoal_goal_not_found"); }
function textMentionsUltragoalPlanArtifact(value: string | undefined): boolean {
const normalized = (value ?? "").toLowerCase();
return normalized.includes(ULTRAGOAL_DIR.toLowerCase()) || normalized.includes(ULTRAGOAL_GOALS.toLowerCase()) || normalized.includes(ULTRAGOAL_LEDGER.toLowerCase());
}
function textMentionsGoalId(value: string | undefined, goalId: string): boolean { return (value ?? "").toLowerCase().includes(goalId.toLowerCase()); }
function textHasCompletionValidationEvidence(value: string | undefined): boolean {
const normalized = (value ?? "").toLowerCase();
const done = /\b(?:planned work|implementation|deliverables?|scope|task|work)\b/.test(normalized) && /\b(?:done|complete|completed|finished|shipped)\b/.test(normalized);
const verified = /\b(?:validation|verification|tests?|build|lint|review|quality gate|code-review)\b/.test(normalized) && /\b(?:passed|complete|completed|clean|green|approve|approved|clear)\b/.test(normalized);
return done && verified;
}
async function snapshotObjectiveMapsToUltragoalPlan(scope: UltragoalScope, snapshotObjective: string): Promise<boolean> {
const actual = normalizeObjective(snapshotObjective).toLowerCase();
if (textMentionsUltragoalPlanArtifact(actual)) return true;
if (actual.length < 24 || !existsSync(ultragoalBriefPath(scope))) return false;
try {
const brief = normalizeObjective(await readFile(ultragoalBriefPath(scope), "utf8")).toLowerCase();
return brief.length >= 24 && (brief.includes(actual) || actual.includes(brief));
} catch (error) {
if (error instanceof Error) return false;
throw error;
}
}
async function canReconcileCompletedTaskScopedAggregateSnapshot(scope: UltragoalScope, plan: UltragoalPlan, goal: UltragoalItem, snapshotObjective: string, evidence: string): Promise<boolean> {
if (goalMode(plan) !== "aggregate") return false;
if (goal.status !== "in_progress" || plan.activeGoalId !== goal.id) return false;
if (isFinalRunCompletionCandidate(plan, goal)) return snapshotObjectiveMapsToUltragoalPlan(scope, snapshotObjective);
if (!textMentionsUltragoalPlanArtifact(evidence) || !textMentionsGoalId(evidence, goal.id)) return false;
if (!textHasCompletionValidationEvidence(evidence)) return false;
return snapshotObjectiveMapsToUltragoalPlan(scope, snapshotObjective);
}
function buildCompletedLegacyGoalRemediation(goal: UltragoalItem): string {
return [
"If a provided goal snapshot reports a different completed legacy/thread objective, do not repeat --status complete in this thread.",
`Record a non-terminal blocker with: omo ultragoal checkpoint --goal-id ${goal.id} --status blocked --evidence "<completed legacy goal blocks completion in this thread>".`,
"Then continue only from a context with no active/completed conflicting goal, in the same repo/worktree.",
].join(" ");
}
function buildTaskScopedAggregateReconciliationHint(goal: UltragoalItem, final: boolean): string {
if (final) {
return ` Final task-scoped aggregate reconciliation requires the checkpoint goal to be the active in-progress final OMO goal and the completed snapshot objective to map to the ultragoal brief or artifact. ${buildCompletedLegacyGoalRemediation(goal)}`;
}
return ` Completed task-scoped aggregate reconciliation requires the checkpoint goal to be the active in-progress OMO goal, evidence that names that active OMO goal id, names .omo/ultragoal/goals.json or ledger.jsonl, includes completed implementation plus validation/review evidence, and a snapshot objective that maps to the ultragoal brief/artifact. ${buildCompletedLegacyGoalRemediation(goal)}`;
}
async function readJsonInput(raw: string | undefined, repoRoot: string): Promise<unknown> {
if (raw === undefined || raw.trim() === "") return undefined;
const trimmed = raw.trim();
try { return JSON.parse(trimmed); } catch (error) { if (!(error instanceof SyntaxError)) throw error; }
const path = resolve(repoRoot, trimmed);
if (!existsSync(path)) return ultragoalFail("Quality gate JSON is neither valid JSON nor a readable path.", "ultragoal_json_input_invalid");
try { return JSON.parse(await readFile(path, "utf8")); } catch (error) { return ultragoalFail(`Quality gate path does not contain valid JSON${error instanceof Error ? `: ${error.message}` : "."}`, "ultragoal_json_input_invalid"); }
}
function makeAggregateCompletion(now: string, evidence: string, goalSnapshot: unknown): UltragoalAggregateCompletion {
return { status: "complete", completedAt: now, evidence, ...(goalSnapshot === undefined ? {} : { goalSnapshot }) };
}
function applyBlockedOrFailed(goal: UltragoalItem, plan: UltragoalPlan, status: "failed" | "blocked", evidence: string, now: string): void {
const signature = classifyExternalAuthorizationBlocker(evidence);
const occurrences = signature === null ? 0 : sameBlockerOccurrences(plan, signature) + 1;
const needsDecision = signature !== null && occurrences >= 3;
goal.status = needsDecision ? "needs_user_decision" : status;
goal.updatedAt = now;
if (status === "failed" || needsDecision) { goal.failedAt = now; goal.failureReason = evidence; }
if (status === "blocked" || needsDecision) goal.blockedReason = evidence;
if (signature !== null) { goal.blockerSignature = signature; goal.blockerOccurrenceCount = occurrences; goal.requiredExternalDecision = `Resolve external authorization: ${signature}`; }
if (needsDecision) goal.nonRetriable = true;
if (plan.activeGoalId === goal.id) delete plan.activeGoalId;
}
function ledgerKind(status: CheckpointUltragoalArgs["status"], goal: UltragoalItem, aggregateCompletion: UltragoalAggregateCompletion | undefined): UltragoalLedgerEntry["kind"] {
if (aggregateCompletion !== undefined) return "aggregate_completed";
if (status === "complete") return "goal_completed";
if (goal.status === "needs_user_decision") return "goal_needs_user_decision";
return status === "blocked" ? "goal_blocked" : "goal_failed";
}
function buildLedger(now: string, args: CheckpointUltragoalArgs, goal: UltragoalItem, qualityGate: UltragoalQualityGate | undefined, goalSnapshot: unknown, aggregateCompletion: UltragoalAggregateCompletion | undefined): UltragoalLedgerEntry {
const entry: UltragoalLedgerEntry = { at: now, kind: ledgerKind(args.status, goal, aggregateCompletion), goalId: goal.id, status: goal.status, evidence: args.evidence };
if (goalSnapshot !== undefined) entry.goalSnapshot = goalSnapshot;
if (qualityGate !== undefined) entry.qualityGate = qualityGate;
if (goal.blockerSignature !== undefined) entry.blockerSignature = goal.blockerSignature;
if (goal.blockerOccurrenceCount !== undefined) entry.blockerOccurrenceCount = goal.blockerOccurrenceCount;
if (goal.requiredExternalDecision !== undefined) entry.requiredExternalDecision = goal.requiredExternalDecision;
return entry;
}
export async function checkpointUltragoal(scope: UltragoalScope, args: CheckpointUltragoalArgs): Promise<CheckpointUltragoalResult> {
return withUltragoalMutationLock(scope, async () => {
const repoRoot = scope.repoRoot;
const plan = await readUltragoalPlan(scope);
const goal = findGoal(plan, args.goalId);
if (args.status === "complete") requireAllCriteriaPass(goal);
const evidence = nonEmptyEvidence(args.evidence);
const now = iso();
let aggregateCompletion: UltragoalAggregateCompletion | undefined;
let qualityGate: UltragoalQualityGate | undefined;
let goalSnapshot: unknown;
if (args.status === "complete") {
const aggregate = goalMode(plan) === "aggregate";
const final = isFinalRunCompletionCandidate(plan, goal);
// Goal snapshot is OPTIONAL under the file/steering model (no create_goal/get_goal dependency).
// When a snapshot is supplied it is reconciled as before; when absent, reconciliation is skipped.
if (args.goalSnapshotJson !== undefined) {
const snapshot = await readGoalSnapshotInput(args.goalSnapshotJson, repoRoot);
const reconciliation = reconcileGoalSnapshot(snapshot, { expectedObjective: expectedObjective(plan, goal), ...(aggregate ? { acceptedObjectives: compatibleObjectives(plan) } : {}), allowedStatuses: aggregate ? (final ? ["complete"] : ["active"]) : ["complete"], requireSnapshot: true, requireComplete: !aggregate || final });
goalSnapshot = reconciliation.snapshot.raw;
if (!reconciliation.ok) {
const objective = snapshot?.objective;
const taskScoped = snapshot?.available === true && snapshot.status === "complete" && objective !== undefined && normalizeObjective(objective) !== normalizeObjective(expectedObjective(plan, goal)) && await canReconcileCompletedTaskScopedAggregateSnapshot(scope, plan, goal, objective, evidence);
if (!taskScoped) throw new UltragoalError(`${formatGoalReconciliation(reconciliation)}${aggregate && snapshot?.status === "complete" && objective !== undefined ? buildTaskScopedAggregateReconciliationHint(goal, final) : ""}`, "ultragoal_goal_snapshot_mismatch");
aggregateCompletion = makeAggregateCompletion(now, evidence, goalSnapshot);
}
}
if (final) aggregateCompletion = makeAggregateCompletion(now, evidence, goalSnapshot);
if (final || aggregateCompletion !== undefined) qualityGate = validateQualityGate(await readJsonInput(args.qualityGateJson, repoRoot));
goal.status = "complete";
goal.completedAt = now;
goal.evidence = evidence;
delete goal.failedAt;
delete goal.failureReason;
clearGoalBlockerFields(goal);
if (plan.activeGoalId === goal.id) delete plan.activeGoalId;
} else applyBlockedOrFailed(goal, plan, args.status, evidence, now);
goal.updatedAt = now;
if (aggregateCompletion !== undefined) plan.aggregateCompletion = aggregateCompletion;
plan.updatedAt = now;
await writePlan(scope, plan);
const ledgerEntry = buildLedger(now, args, goal, qualityGate, goalSnapshot, aggregateCompletion);
await appendLedger(scope, ledgerEntry);
return aggregateCompletion === undefined ? { plan, goal, ledgerEntry } : { plan, goal, ledgerEntry, aggregateCompletion };
});
}
@@ -0,0 +1,186 @@
import { makeUltragoalScope } from "./session-scope.js";
import { parseUltragoalSteeringDirective, steerUltragoal } from "./steering.js";
export interface UserPromptSubmitPayload {
readonly cwd: string;
readonly hook_event_name: "UserPromptSubmit";
readonly model?: string;
readonly permission_mode?: string;
readonly prompt: string;
readonly session_id: string;
readonly transcript_path?: string;
readonly turn_id?: string;
}
export interface PreToolUsePayload {
readonly cwd: string;
readonly hook_event_name: "PreToolUse";
readonly model?: string;
readonly permission_mode?: string;
readonly session_id: string;
readonly tool_input: unknown;
readonly tool_name: string;
readonly tool_use_id: string;
readonly transcript_path?: string | null;
readonly turn_id?: string;
}
interface PreToolUseHookOutput {
readonly hookSpecificOutput: {
readonly hookEventName: "PreToolUse";
readonly permissionDecision: "deny";
readonly permissionDecisionReason: string;
readonly additionalContext: string;
};
}
const CREATE_GOAL_TOOL_NAME = "create_goal";
const GOAL_BUDGET_WARNING =
"Do not set a budget on create_goal. Omit the budget field so the goal stays unlimited; ultrawork and ultragoal runs must always use unlimited goals.";
export function parseUserPromptSubmitPayload(raw: string): UserPromptSubmitPayload | null {
if (raw.trim().length === 0) return null;
try {
const parsed: unknown = JSON.parse(raw);
return isUserPromptSubmitPayload(parsed) ? parsed : null;
} catch (error) {
if (error instanceof SyntaxError) return null;
return null;
}
}
export function parsePreToolUsePayload(raw: string): PreToolUsePayload | null {
if (raw.trim().length === 0) return null;
try {
const parsed: unknown = JSON.parse(raw);
return isPreToolUsePayload(parsed) ? parsed : null;
} catch (error) {
if (error instanceof SyntaxError) return null;
return null;
}
}
export async function applyUserPromptUltragoalSteering(payload: UserPromptSubmitPayload): Promise<string> {
try {
if (payload.hook_event_name !== "UserPromptSubmit") return "";
const proposal = parseUltragoalSteeringDirective(payload.prompt);
if (proposal === null) return "";
const scope = makeUltragoalScope(payload.cwd, payload.session_id);
const result = await steerUltragoal(scope, proposal);
if (!result.accepted) return "";
return JSON.stringify({
status: "accepted",
kind: result.audit.kind,
source: result.audit.source,
deduped: result.deduped,
});
} catch (error) {
if (error instanceof Error) return "";
return "";
}
}
/**
* Inert under Claude Code: the `create_goal` PreToolUse block is intentionally
* NOT registered in hooks.json (D4) because Claude Code never emits a
* `create_goal` tool call. The guard CODE is retained and unit-testable so the
* behavior is documented and exercised; it short-circuits to "" for every tool
* Claude Code actually emits.
*/
export function applyPreToolUseGoalBudgetGuard(payload: PreToolUsePayload): string {
if (payload.hook_event_name !== "PreToolUse") return "";
if (payload.tool_name !== CREATE_GOAL_TOOL_NAME) return "";
if (!hasGoalBudgetInput(payload.tool_input)) return "";
const output: PreToolUseHookOutput = {
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: GOAL_BUDGET_WARNING,
additionalContext: GOAL_BUDGET_WARNING,
},
};
return `${JSON.stringify(output)}\n`;
}
export async function runUltragoalHookCli(stdin: NodeJS.ReadableStream, stdout: NodeJS.WritableStream): Promise<void> {
try {
const payload = parseUserPromptSubmitPayload(await readAll(stdin));
if (payload === null) return;
const output = await applyUserPromptUltragoalSteering(payload);
if (output.length > 0) stdout.write(output);
} catch (error) {
if (error instanceof Error) return;
return;
}
}
export async function runPreToolUseGoalBudgetGuardCli(
stdin: NodeJS.ReadableStream,
stdout: NodeJS.WritableStream,
): Promise<void> {
try {
const payload = parsePreToolUsePayload(await readAll(stdin));
if (payload === null) return;
const output = applyPreToolUseGoalBudgetGuard(payload);
if (output.length > 0) stdout.write(output);
} catch (error) {
if (error instanceof Error) return;
return;
}
}
function isUserPromptSubmitPayload(value: unknown): value is UserPromptSubmitPayload {
if (!isRecord(value)) return false;
return (
value["hook_event_name"] === "UserPromptSubmit" &&
typeof value["cwd"] === "string" &&
typeof value["prompt"] === "string" &&
typeof value["session_id"] === "string" &&
["model", "permission_mode", "transcript_path", "turn_id"].every((key) => optionalString(value[key]))
);
}
/**
* Relaxed PreToolUse validator (D4): `model` and `turn_id` are optional because
* Claude Code never sends `turn_id` and omits `model` on non-SessionStart
* events. Requiring them would make the guard untestable against CC payloads.
*/
function isPreToolUsePayload(value: unknown): value is PreToolUsePayload {
if (!isRecord(value)) return false;
return (
value["hook_event_name"] === "PreToolUse" &&
typeof value["cwd"] === "string" &&
typeof value["session_id"] === "string" &&
typeof value["tool_name"] === "string" &&
typeof value["tool_use_id"] === "string" &&
(value["transcript_path"] === null || optionalString(value["transcript_path"])) &&
optionalString(value["model"]) &&
optionalString(value["permission_mode"]) &&
optionalString(value["turn_id"]) &&
Object.hasOwn(value, "tool_input")
);
}
function hasGoalBudgetInput(value: unknown): boolean {
return isRecord(value) && (Object.hasOwn(value, "token_budget") || Object.hasOwn(value, "tokenBudget"));
}
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
function optionalString(value: unknown): boolean {
return value === undefined || typeof value === "string";
}
function readAll(stdin: NodeJS.ReadableStream): Promise<string> {
return new Promise((resolve, reject) => {
let data = "";
stdin.setEncoding("utf8");
stdin.on("data", (chunk: unknown) => {
data += chunk instanceof Buffer ? chunk.toString() : String(chunk);
});
stdin.once("error", reject);
stdin.once("end", () => resolve(data));
});
}
@@ -0,0 +1,95 @@
// biome-ignore-all format: keep this module under the mandated pure LOC budget.
import { readFile } from "node:fs/promises";
import { UltragoalError } from "./types.js";
type RecordEvidenceCliArgs = { readonly goalId: string; readonly criterionId: string; readonly status: "pass" | "fail" | "blocked"; readonly evidence: string; readonly notes?: string };
const VALUE_FLAGS = new Set("--session-id --brief --brief-file --goal-mode --codex-goal-mode --goal --goal-id --criterion-id --status --evidence --notes --goal-snapshot-json --codex-goal-json --quality-gate-json --kind --rationale --title --objective --target-goal-id --source --after-json --directive-json --directive-file --idempotency-key".split(" "));
const SUBCOMMANDS = new Set("create-goals status complete-goals criteria record-evidence checkpoint steer add-goal record-review-blockers".split(" "));
export function hasFlag(argv: readonly string[], flag: string): boolean { return argv.includes(flag); }
export function readValue(argv: readonly string[], flag: string): string | undefined {
const index = argv.indexOf(flag);
if (index >= 0) {
const next = argv[index + 1];
return next === undefined || next.startsWith("--") ? undefined : next;
}
const prefix = `${flag}=`;
return argv.find((arg) => arg.startsWith(prefix))?.slice(prefix.length);
}
export function readRepeated(argv: readonly string[], flag: string): string[] {
const values: string[] = [];
const prefix = `${flag}=`;
for (let index = 0; index < argv.length; index += 1) {
const arg = argv[index];
const next = argv[index + 1];
if (arg === flag && next !== undefined && !next.startsWith("--")) { values.push(next); index += 1; }
else if (arg?.startsWith(prefix)) values.push(arg.slice(prefix.length));
}
return values;
}
export function parseGoalArg(argv: readonly string[]): string | undefined { return readValue(argv, "--goal-id") ?? readValue(argv, "--goal"); }
export async function readStdin(): Promise<string> {
const chunks: Buffer[] = [];
for await (const chunk of process.stdin) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
return Buffer.concat(chunks).toString("utf8");
}
export function positionalText(argv: readonly string[]): string {
const words: string[] = [];
for (let index = SUBCOMMANDS.has(argv[0] ?? "") ? 1 : 0; index < argv.length; index += 1) {
const arg = argv[index];
if (arg === undefined) continue;
if (VALUE_FLAGS.has(arg)) { index += 1; continue; }
if (arg.startsWith("--")) continue;
words.push(arg);
}
return words.join(" ").trim();
}
function looksLikeJson(value: string): boolean { const trimmed = value.trim(); return trimmed.startsWith("{") || trimmed.startsWith("["); }
export async function readJsonInput(value: string | undefined): Promise<unknown | undefined> {
if (value === undefined) return undefined;
try { return JSON.parse(looksLikeJson(value) ? value : await readFile(value, "utf8")); }
catch (error) {
const message = error instanceof Error ? error.message : "unknown error";
throw new UltragoalError(`Invalid JSON input: ${message}`, "ULTRAGOAL_JSON_INPUT_INVALID", { cause: error });
}
}
export async function parseGoalSnapshotJson(value: string | undefined): Promise<string | undefined> {
if (value === undefined) return undefined;
const raw = looksLikeJson(value) ? value : await readFile(value, "utf8");
try { JSON.parse(raw); return raw; }
catch (error) {
const message = error instanceof Error ? error.message : "unknown error";
throw new UltragoalError(`Invalid --goal-snapshot-json: ${message}`, "ULTRAGOAL_GOAL_SNAPSHOT_JSON_INVALID", { cause: error });
}
}
function required(argv: readonly string[], flag: string, code: string): string {
const value = readValue(argv, flag)?.trim();
if (value) return value;
throw new UltragoalError(`Missing ${flag}.`, code, { details: { flag } });
}
function evidenceStatus(value: string): RecordEvidenceCliArgs["status"] {
switch (value) {
case "pass": return "pass";
case "fail": return "fail";
case "blocked": return "blocked";
default: throw new UltragoalError("Invalid --status; expected pass, fail, or blocked.", "ULTRAGOAL_EVIDENCE_STATUS_INVALID", { details: { status: value } });
}
}
export function parseRecordEvidenceArgs(argv: readonly string[]): RecordEvidenceCliArgs {
const result = { goalId: required(argv, "--goal-id", "ULTRAGOAL_GOAL_ID_REQUIRED"), criterionId: required(argv, "--criterion-id", "ULTRAGOAL_CRITERION_ID_REQUIRED"), status: evidenceStatus(required(argv, "--status", "ULTRAGOAL_EVIDENCE_STATUS_REQUIRED")), evidence: required(argv, "--evidence", "ULTRAGOAL_EVIDENCE_REQUIRED") };
const notes = readValue(argv, "--notes")?.trim();
return notes ? { ...result, notes } : result;
}
@@ -0,0 +1,177 @@
// biome-ignore-all format: keep cli-commands dispatcher under the 200 pure LOC budget.
import { readFile } from "node:fs/promises";
import { checkpointUltragoal } from "./checkpoint.js";
import { hasFlag, parseGoalSnapshotJson, parseRecordEvidenceArgs, positionalText, readStdin, readValue } from "./cli-arg-parser.js";
import { blockedDecisionHandoff, normalizeGoalMode, printJson, printStatus, ULTRAGOAL_HELP } from "./cli-output.js";
import { parseSteeringProposal, printSteerResult } from "./cli-steering.js";
import { buildGoalInstruction } from "./goal-instruction.js";
import { recordEvidence } from "./evidence.js";
import { addUltragoalGoal, createUltragoalPlan, startNextUltragoal, summarizeUltragoalPlan } from "./plan-crud.js";
import { readUltragoalIndex, readUltragoalPlan } from "./plan-io.js";
import { recordFinalReviewBlockers } from "./review-blockers.js";
import { makeUltragoalScope, type UltragoalScope } from "./session-scope.js";
import { steerUltragoal } from "./steering.js";
import type { UltragoalItem } from "./types.js";
import { UltragoalError } from "./types.js";
type CheckpointStatus = "complete" | "failed" | "blocked";
/**
* Resolve the active session scope for a CLI subcommand. CLI subcommands run
* WITHOUT a hook payload, so the session is resolved in precedence order:
* 1. `--session-id <id>` flag
* 2. `$CLAUDE_SESSION_ID`
* 3. newest-active session in `./.omo/ultragoal/index.json`
* 4. otherwise error `ULTRAGOAL_SESSION_REQUIRED`
*/
export async function resolveUltragoalScope(repoRoot: string, argv: readonly string[]): Promise<UltragoalScope> {
const flag = readValue(argv, "--session-id")?.trim();
if (flag) return makeUltragoalScope(repoRoot, flag);
const env = process.env["CLAUDE_SESSION_ID"]?.trim();
if (env) return makeUltragoalScope(repoRoot, env);
const index = await readUltragoalIndex(repoRoot);
const newest = [...index.sessions].sort((a, b) => Date.parse(b.updatedAt) - Date.parse(a.updatedAt))[0];
if (newest !== undefined) return makeUltragoalScope(repoRoot, newest.sessionId);
throw new UltragoalError("No ultragoal session resolved. Pass --session-id, set $CLAUDE_SESSION_ID, or run from a Claude Code session.", "ULTRAGOAL_SESSION_REQUIRED");
}
export async function ultragoalCommand(argv: readonly string[]): Promise<number> {
const command = argv[0] ?? "help";
const rest = argv.slice(1);
const repoRoot = process.cwd();
const json = hasFlag(rest, "--json");
try {
switch (command) {
case "help": case "--help": case "-h": process.stdout.write(`${ULTRAGOAL_HELP}\n`); return 0;
case "create-goals": return await createGoals(await resolveScopeForCreate(repoRoot, rest), rest, json);
case "status": return await status(await resolveUltragoalScope(repoRoot, rest), json);
case "complete-goals": return await completeGoals(await resolveUltragoalScope(repoRoot, rest), rest, json);
case "checkpoint": return await checkpoint(await resolveUltragoalScope(repoRoot, rest), rest, json);
case "steer": return await steer(await resolveUltragoalScope(repoRoot, rest), rest, json);
case "add-goal": return await addGoal(await resolveUltragoalScope(repoRoot, rest), rest, json);
case "criteria": return await criteria(await resolveUltragoalScope(repoRoot, rest), rest, json);
case "record-evidence": return await captureEvidence(await resolveUltragoalScope(repoRoot, rest), rest, json);
case "record-review-blockers": return await reviewBlockers(await resolveUltragoalScope(repoRoot, rest), rest, json);
default: process.stdout.write(`${ULTRAGOAL_HELP}\n`); return 1;
}
} catch (error) {
if (error instanceof UltragoalError) process.stderr.write(`[ultragoal] ${error.message}\n`);
else if (error instanceof Error) process.stderr.write(`[ultragoal] unexpected: ${error.message}\n`);
else process.stderr.write("[ultragoal] unknown error\n");
return 1;
}
}
/**
* create-goals can bootstrap a brand-new session: if --session-id / env are
* absent and no session exists in the index, it still needs a scope. We require
* an explicit session for create so two parallel sessions never collide; fall
* back to the index only when one already exists.
*/
async function resolveScopeForCreate(repoRoot: string, argv: readonly string[]): Promise<UltragoalScope> {
const flag = readValue(argv, "--session-id")?.trim();
if (flag) return makeUltragoalScope(repoRoot, flag);
const env = process.env["CLAUDE_SESSION_ID"]?.trim();
if (env) return makeUltragoalScope(repoRoot, env);
throw new UltragoalError("create-goals requires a session. Pass --session-id or set $CLAUDE_SESSION_ID.", "ULTRAGOAL_SESSION_REQUIRED");
}
async function createGoals(scope: UltragoalScope, argv: readonly string[], json: boolean): Promise<number> {
const briefFile = readValue(argv, "--brief-file");
const brief = readValue(argv, "--brief") ?? (briefFile === undefined ? undefined : await readFile(briefFile, "utf8")) ?? (hasFlag(argv, "--from-stdin") ? await readStdin() : undefined) ?? positionalText(argv);
if (!brief.trim()) throw new UltragoalError("Missing brief text. Pass --brief, --brief-file, --from-stdin, or positional text.", "ULTRAGOAL_BRIEF_REQUIRED");
const plan = await createUltragoalPlan(scope, { brief, goalMode: normalizeGoalMode(readValue(argv, "--goal-mode") ?? readValue(argv, "--codex-goal-mode")), force: hasFlag(argv, "--force") });
if (json) printJson({ ok: true, plan, summary: summarizeUltragoalPlan(plan) });
else process.stdout.write(`ultragoal plan created: ${plan.goals.length} goal(s)\nsession: ${plan.sessionId}\nbrief: ${plan.briefPath}\ngoals: ${plan.goalsPath}\nledger: ${plan.ledgerPath}\n`);
return 0;
}
async function status(scope: UltragoalScope, json: boolean): Promise<number> {
const plan = await readUltragoalPlan(scope);
if (json) printJson({ ok: true, plan, summary: summarizeUltragoalPlan(plan) });
else printStatus(plan);
return 0;
}
async function completeGoals(scope: UltragoalScope, argv: readonly string[], json: boolean): Promise<number> {
const result = await startNextUltragoal(scope, { retryFailed: hasFlag(argv, "--retry-failed") });
if ("done" in result) {
const handoff = blockedDecisionHandoff(result.plan);
if (json) printJson({ ok: true, done: true, blocked: handoff.length > 0, handoff, summary: summarizeUltragoalPlan(result.plan), plan: result.plan });
else process.stdout.write(`${handoff || "ultragoal: all goals complete"}\n`);
return 0;
}
const instruction = buildGoalInstruction({ plan: result.plan, goal: result.goal });
if (json) printJson({ ok: true, resumed: result.resumed, goal: result.goal, instruction, plan: result.plan });
else process.stdout.write(`${instruction.text}\n`);
return 0;
}
async function checkpoint(scope: UltragoalScope, argv: readonly string[], json: boolean): Promise<number> {
const goalId = required(argv, "--goal-id");
const statusValue = checkpointStatus(required(argv, "--status"));
const evidence = required(argv, "--evidence");
const goalSnapshotJson = await parseGoalSnapshotJson(readValue(argv, "--goal-snapshot-json") ?? readValue(argv, "--codex-goal-json"));
const qualityGateJson = readValue(argv, "--quality-gate-json");
const base = { goalId, status: statusValue, evidence };
const withSnapshot = goalSnapshotJson === undefined ? base : { ...base, goalSnapshotJson };
const result = await checkpointUltragoal(scope, qualityGateJson === undefined ? withSnapshot : { ...withSnapshot, qualityGateJson });
if (json) printJson({ ok: true, ...result, summary: summarizeUltragoalPlan(result.plan) });
else process.stdout.write(`ultragoal checkpoint: ${result.goal.id} -> ${result.goal.status}\n`);
return 0;
}
async function steer(scope: UltragoalScope, argv: readonly string[], json: boolean): Promise<number> {
const proposal = await parseSteeringProposal(argv);
const result = await steerUltragoal(scope, proposal);
printSteerResult(result, json);
return result.accepted ? 0 : 1;
}
async function addGoal(scope: UltragoalScope, argv: readonly string[], json: boolean): Promise<number> {
const result = await addUltragoalGoal(scope, { title: required(argv, "--title"), objective: required(argv, "--objective") });
if (json) printJson({ ok: true, plan: result.plan, goal: result.goal, summary: summarizeUltragoalPlan(result.plan) });
else { process.stdout.write(`ultragoal added goal: ${result.goal.id}\n`); printStatus(result.plan); }
return 0;
}
async function criteria(scope: UltragoalScope, argv: readonly string[], json: boolean): Promise<number> {
const goalId = required(argv, "--goal-id");
const goal = findGoal(await readUltragoalPlan(scope), goalId);
if (json) printJson({ ok: true, goalId: goal.id, criteria: goal.successCriteria });
else process.stdout.write(`criteria for ${goal.id}:\n${goal.successCriteria.map((c) => `- ${c.id} [${c.status}] (${c.userModel}) ${c.scenario} evidence: ${c.capturedEvidence ?? "pending"}`).join("\n")}\n`);
return 0;
}
async function captureEvidence(scope: UltragoalScope, argv: readonly string[], json: boolean): Promise<number> {
const result = await recordEvidence(scope, parseRecordEvidenceArgs(argv));
if (json) printJson({ ok: true, ...result, summary: summarizeUltragoalPlan(result.plan) });
else process.stdout.write(`ultragoal evidence recorded: ${result.goal.id}/${result.criterion.id} -> ${result.criterion.status}\n`);
return 0;
}
async function reviewBlockers(scope: UltragoalScope, argv: readonly string[], json: boolean): Promise<number> {
const goalSnapshotJson = await parseGoalSnapshotJson(readValue(argv, "--goal-snapshot-json") ?? readValue(argv, "--codex-goal-json"));
const base = { goalId: required(argv, "--goal-id"), title: required(argv, "--title"), objective: required(argv, "--objective"), evidence: required(argv, "--evidence") };
const result = await recordFinalReviewBlockers(scope, goalSnapshotJson === undefined ? base : { ...base, goalSnapshotJson });
if (json) printJson({ ok: true, plan: result.plan, blockedGoal: result.blockedGoal, goal: result.newGoal, ledgerEntries: result.ledgerEntries, summary: summarizeUltragoalPlan(result.plan) });
else process.stdout.write(`ultragoal final review blockers recorded: ${result.blockedGoal.id} -> review_blocked; added ${result.newGoal.id}\n`);
return 0;
}
function required(argv: readonly string[], flag: string): string {
const value = readValue(argv, flag)?.trim();
if (value) return value;
throw new UltragoalError(`Missing ${flag}.`, "ULTRAGOAL_ARGUMENT_MISSING", { details: { flag } });
}
function checkpointStatus(value: string): CheckpointStatus {
if (value === "complete" || value === "failed" || value === "blocked") return value;
throw new UltragoalError("Missing or invalid --status; expected complete, failed, or blocked.", "ULTRAGOAL_STATUS_INVALID", { details: { status: value } });
}
function findGoal(plan: { readonly goals: readonly UltragoalItem[] }, goalId: string): UltragoalItem {
const goal = plan.goals.find((candidate) => candidate.id === goalId);
if (goal !== undefined) return goal;
throw new UltragoalError(`Unknown ultragoal id: ${goalId}.`, "ULTRAGOAL_GOAL_NOT_FOUND", { details: { goalId } });
}
@@ -0,0 +1,61 @@
import type { UltragoalGoalMode, UltragoalItem, UltragoalPlan } from "./types.js";
import { UltragoalError } from "./types.js";
export const ULTRAGOAL_HELP = `Usage (all subcommands resolve a session via --session-id, $CLAUDE_SESSION_ID, or the newest active session):
omo ultragoal create-goals --brief "..." [--session-id <id>] [--brief-file <path>] [--from-stdin] [--goal-mode aggregate|per_story] [--force] [--json]
omo ultragoal status [--session-id <id>] [--json]
omo ultragoal complete-goals [--session-id <id>] [--retry-failed] [--json]
omo ultragoal criteria --goal-id <id> [--session-id <id>] [--json]
omo ultragoal record-evidence --goal-id <id> --criterion-id <id> --status pass|fail|blocked --evidence "..." [--notes "..."] [--session-id <id>] [--json]
omo ultragoal checkpoint --goal-id <id> --status complete|failed|blocked --evidence "..." [--goal-snapshot-json <...>] [--quality-gate-json <...>] [--session-id <id>] [--json]
omo ultragoal steer --kind <kind> ... --evidence "..." --rationale "..." [--session-id <id>] [--json]
omo ultragoal add-goal --title "..." --objective "..." [--session-id <id>] [--json]
omo ultragoal record-review-blockers --goal-id <id> --title "..." --objective "..." --evidence "..." [--goal-snapshot-json <...>] [--session-id <id>] [--json]`;
type CriteriaCounts = { readonly pass: number; readonly total: number };
export function printJson(value: unknown): void {
process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
}
function criteriaCounts(goal: UltragoalItem): CriteriaCounts {
let pass = 0;
for (const criterion of goal.successCriteria) if (criterion.status === "pass") pass += 1;
return { pass, total: goal.successCriteria.length };
}
export function printStatus(plan: UltragoalPlan): void {
let totalCriteria = 0;
let passCriteria = 0;
const lines = ["ultragoal status", "", "goals:"];
for (const goal of plan.goals) {
const counts = criteriaCounts(goal);
totalCriteria += counts.total;
passCriteria += counts.pass;
const marker = goal.id === plan.activeGoalId ? "*" : "-";
lines.push(`${marker} ${goal.id} [${goal.status}] ${goal.title} (criteria: ${counts.pass}/${counts.total})`);
}
lines.push("", "summary:", `total goals: ${plan.goals.length}`, `criteria: ${passCriteria}/${totalCriteria} pass`);
process.stdout.write(`${lines.join("\n")}\n`);
}
export function blockedDecisionHandoff(plan: UltragoalPlan): string {
const blocked = plan.goals.find((goal) => goal.status === "needs_user_decision" && goal.nonRetriable);
if (blocked === undefined) return "";
return [
"ultragoal: blocked on repeated external authorization; no retryable failed goals remain.",
`Goal: ${blocked.id} - ${blocked.title}`,
`Required external decision: ${blocked.requiredExternalDecision ?? "provide the missing authorization or choose a different unblock path"}.`,
"Do not run complete-goals --retry-failed again until external state changes or the user authorizes an unblock path.",
].join("\n");
}
export function normalizeGoalMode(value: string | undefined): UltragoalGoalMode {
if (value === undefined) return "aggregate";
if (value === "aggregate" || value === "per_story") return value;
throw new UltragoalError(
"Invalid --goal-mode; expected aggregate or per_story.",
"ULTRAGOAL_GOAL_MODE_INVALID",
{ details: { value } },
);
}
@@ -0,0 +1,94 @@
// biome-ignore-all format: keep this module under the mandated pure LOC budget.
import { parseGoalArg, readJsonInput, readValue } from "./cli-arg-parser.js";
import { printJson, printStatus } from "./cli-output.js";
import type { SteerUltragoalResult, UltragoalSteeringChildGoal, UltragoalSteeringMutationKind, UltragoalSteeringProposal, UltragoalSteeringSource, UltragoalSuccessCriterionUserModel } from "./types.js";
import { ULTRAGOAL_STEERING_MUTATION_KINDS, ULTRAGOAL_SUCCESS_CRITERION_USER_MODELS, UltragoalError } from "./types.js";
const SOURCES = ["user_prompt_submit", "finding", "cli"] as const satisfies readonly UltragoalSteeringSource[];
export type CliSteeringProposal = UltragoalSteeringProposal & { readonly goalId?: string; readonly scenario?: string; readonly expectedEvidence?: string; readonly userModel?: UltragoalSuccessCriterionUserModel };
function isKind(value: string | undefined): value is UltragoalSteeringMutationKind { return value !== undefined && ULTRAGOAL_STEERING_MUTATION_KINDS.some((kind) => kind === value); }
function isSource(value: string | undefined): value is UltragoalSteeringSource { return value !== undefined && SOURCES.some((source) => source === value); }
function isModel(value: string): value is UltragoalSuccessCriterionUserModel { return ULTRAGOAL_SUCCESS_CRITERION_USER_MODELS.some((model) => model === value); }
function fail(message: string, code: string, details: Record<string, unknown>): never { throw new UltragoalError(message, code, { details }); }
function text(value: string | undefined, field: string): string | undefined { if (value === undefined) return undefined; const trimmed = value.trim(); if (trimmed.length > 0) return trimmed; return fail(`Empty ${field}.`, "ULTRAGOAL_STEERING_FIELD_EMPTY", { field }); }
function required(argv: readonly string[], flag: string): string { const value = text(readValue(argv, flag), flag); return value ?? fail(`Missing ${flag}.`, "ULTRAGOAL_STEERING_FIELD_REQUIRED", { flag }); }
function requiredGoal(argv: readonly string[]): string { const value = text(parseGoalArg(argv), "--goal-id"); return value ?? fail("Missing --goal-id.", "ULTRAGOAL_GOAL_ID_REQUIRED", { flag: "--goal-id" }); }
function readObject(value: object, key: string): unknown { return Object.entries(value).find(([name]) => name === key)?.[1]; }
function isPlain(value: unknown): value is object { return typeof value === "object" && value !== null && !Array.isArray(value); }
function objectText(value: object, key: string): string | undefined { const candidate = readObject(value, key); return typeof candidate === "string" ? candidate : undefined; }
export function parseSteeringKind(argv: readonly string[]): UltragoalSteeringMutationKind {
const value = readValue(argv, "--kind");
if (isKind(value)) return value;
return value === undefined ? fail("Missing --kind.", "ULTRAGOAL_STEERING_KIND_REQUIRED", { flag: "--kind" }) : fail(`Invalid --kind: ${value}.`, "ULTRAGOAL_STEERING_KIND_INVALID", { value, expected: ULTRAGOAL_STEERING_MUTATION_KINDS });
}
export function parseSteeringSource(argv: readonly string[]): UltragoalSteeringSource {
const value = readValue(argv, "--source");
if (value === undefined) return "cli";
return isSource(value) ? value : fail(`Invalid --source: ${value}.`, "ULTRAGOAL_STEERING_SOURCE_INVALID", { value, expected: SOURCES });
}
function child(value: unknown): UltragoalSteeringChildGoal | null {
if (!isPlain(value)) return null;
const title = text(objectText(value, "title"), "title"); const objective = text(objectText(value, "objective"), "objective");
if (title === undefined || objective === undefined) return null;
return { title, objective };
}
async function children(argv: readonly string[], flag: string, needed: boolean): Promise<UltragoalSteeringChildGoal[]> {
const input = needed ? required(argv, flag) : text(readValue(argv, flag), flag);
if (input === undefined) return [];
const raw = await readJsonInput(input);
if (!Array.isArray(raw)) return fail(`${flag} must be a JSON array.`, "ULTRAGOAL_STEERING_JSON_ARRAY_REQUIRED", { flag });
const parsed: UltragoalSteeringChildGoal[] = [];
for (const item of raw) { const next = child(item); if (next === null) return fail(`${flag} entries require title/objective.`, "ULTRAGOAL_STEERING_CHILD_INVALID", { flag }); parsed.push(next); }
return parsed;
}
async function stringArray(argv: readonly string[], flag: string): Promise<string[]> {
const raw = await readJsonInput(required(argv, flag));
if (!Array.isArray(raw)) return fail(`${flag} must be a JSON array.`, "ULTRAGOAL_STEERING_JSON_ARRAY_REQUIRED", { flag });
const values: string[] = [];
for (const item of raw) { if (typeof item !== "string") return fail(`${flag} entries must be strings.`, "ULTRAGOAL_STEERING_STRING_ARRAY_REQUIRED", { flag }); values.push(text(item, flag) ?? ""); }
return values;
}
function model(value: string | undefined): UltragoalSuccessCriterionUserModel | undefined { const trimmed = text(value, "--user-model"); if (trimmed === undefined) return undefined; return isModel(trimmed) ? trimmed : fail(`Invalid --user-model: ${trimmed}.`, "ULTRAGOAL_STEERING_USER_MODEL_INVALID", { value: trimmed, expected: ULTRAGOAL_SUCCESS_CRITERION_USER_MODELS }); }
function neverKind(kind: never): never { return fail(`Unsupported steering kind: ${String(kind)}.`, "ULTRAGOAL_STEERING_KIND_UNSUPPORTED", { kind }); }
export async function parseSteeringProposal(argv: readonly string[]): Promise<CliSteeringProposal> {
const kind = parseSteeringKind(argv); const source = parseSteeringSource(argv); const base = { kind, source, evidence: required(argv, "--evidence"), rationale: required(argv, "--rationale") };
switch (kind) {
case "add_subgoal": return normalizeSteeringProposal({ ...base, title: required(argv, "--title"), objective: required(argv, "--objective") });
case "split_subgoal": { const goalId = requiredGoal(argv); return normalizeSteeringProposal({ ...base, goalId, targetGoalId: goalId, childGoals: await children(argv, "--children", true) }); }
case "reorder_pending": return normalizeSteeringProposal({ ...base, pendingOrder: await stringArray(argv, "--order") });
case "revise_pending_wording": { const goalId = requiredGoal(argv); const revisedTitle = readValue(argv, "--title"); const revisedObjective = readValue(argv, "--objective"); if (revisedTitle === undefined && revisedObjective === undefined) return fail("revise_pending_wording requires --title or --objective.", "ULTRAGOAL_STEERING_UPDATE_REQUIRED", { kind }); return normalizeSteeringProposal({ ...base, goalId, targetGoalId: goalId, ...(revisedTitle === undefined ? {} : { revisedTitle }), ...(revisedObjective === undefined ? {} : { revisedObjective }) }); }
case "revise_criterion": { const goalId = requiredGoal(argv); const criterionId = required(argv, "--criterion-id"); const scenario = readValue(argv, "--scenario"); const expectedEvidence = readValue(argv, "--expected-evidence"); const userModel = model(readValue(argv, "--user-model")); if (scenario === undefined && expectedEvidence === undefined && userModel === undefined) return fail("revise_criterion requires scenario, expected-evidence, or user-model.", "ULTRAGOAL_STEERING_UPDATE_REQUIRED", { kind }); return normalizeSteeringProposal({ ...base, goalId, targetGoalId: goalId, criterionId, ...(scenario === undefined ? {} : { scenario }), ...(expectedEvidence === undefined ? {} : { expectedEvidence }), ...(userModel === undefined ? {} : { userModel }) }); }
case "annotate_ledger": return normalizeSteeringProposal(base);
case "mark_blocked_superseded": { const goalId = requiredGoal(argv); const childGoals = await children(argv, "--replacements", false); return normalizeSteeringProposal({ ...base, goalId, targetGoalId: goalId, ...(childGoals.length === 0 ? {} : { childGoals }) }); }
default: return neverKind(kind);
}
}
function normalizedChildren(values: readonly UltragoalSteeringChildGoal[] | undefined): UltragoalSteeringChildGoal[] | undefined { if (values === undefined) return undefined; return values.map((item) => ({ title: text(item.title, "child.title") ?? "", objective: text(item.objective, "child.objective") ?? "" })); }
function normalizedStrings(values: readonly string[] | undefined, field: string): string[] | undefined { if (values === undefined) return undefined; return values.map((value) => text(value, field) ?? ""); }
export function normalizeSteeringProposal(proposal: CliSteeringProposal): CliSteeringProposal {
const evidence = text(proposal.evidence, "evidence") ?? ""; const rationale = text(proposal.rationale, "rationale") ?? ""; const goalId = text(proposal.goalId, "goalId"); const targetGoalId = text(proposal.targetGoalId, "targetGoalId"); const targetGoalIds = normalizedStrings(proposal.targetGoalIds, "targetGoalIds");
const criterionId = text(proposal.criterionId, "criterionId"); const title = text(proposal.title, "title"); const objective = text(proposal.objective, "objective"); const revisedTitle = text(proposal.revisedTitle, "revisedTitle"); const revisedObjective = text(proposal.revisedObjective, "revisedObjective");
const blockedReason = text(proposal.blockedReason, "blockedReason"); const directiveText = text(proposal.directiveText, "directiveText"); const promptSignature = text(proposal.promptSignature, "promptSignature"); const idempotencyKey = text(proposal.idempotencyKey, "idempotencyKey");
const scenario = text(proposal.scenario, "scenario"); const expectedEvidence = text(proposal.expectedEvidence, "expectedEvidence"); const childGoals = normalizedChildren(proposal.childGoals); const pendingOrder = normalizedStrings(proposal.pendingOrder, "pendingOrder");
return { kind: proposal.kind, source: proposal.source, evidence, rationale, ...(goalId === undefined ? {} : { goalId }), ...(targetGoalId === undefined ? {} : { targetGoalId }), ...(targetGoalIds === undefined ? {} : { targetGoalIds }), ...(criterionId === undefined ? {} : { criterionId }), ...(title === undefined ? {} : { title }), ...(objective === undefined ? {} : { objective }), ...(childGoals === undefined ? {} : { childGoals }), ...(revisedTitle === undefined ? {} : { revisedTitle }), ...(revisedObjective === undefined ? {} : { revisedObjective }), ...(pendingOrder === undefined ? {} : { pendingOrder }), ...(blockedReason === undefined ? {} : { blockedReason }), ...(proposal.after === undefined ? {} : { after: proposal.after }), ...(directiveText === undefined ? {} : { directiveText }), ...(promptSignature === undefined ? {} : { promptSignature }), ...(idempotencyKey === undefined ? {} : { idempotencyKey }), ...(proposal.now === undefined ? {} : { now: proposal.now }), ...(scenario === undefined ? {} : { scenario }), ...(expectedEvidence === undefined ? {} : { expectedEvidence }), ...(proposal.userModel === undefined ? {} : { userModel: proposal.userModel }) };
}
export function printSteerResult(result: SteerUltragoalResult, json: boolean): void {
if (json) { printJson({ ok: result.accepted, accepted: result.accepted, rejectedReasons: result.rejectedReasons, deduped: result.deduped, audit: result.audit, plan: result.plan }); return; }
const outcome = result.deduped ? "deduped" : result.accepted ? "accepted" : "rejected";
process.stdout.write(`ultragoal steer: ${outcome} ${result.audit.kind}\n`);
if (result.rejectedReasons.length > 0) process.stdout.write(`rejected: ${result.rejectedReasons.join("; ")}\n`);
if (result.audit.idempotencyKey !== undefined) process.stdout.write(`idempotency-key: ${result.audit.idempotencyKey}\n`);
printStatus(result.plan);
}
@@ -0,0 +1,40 @@
#!/usr/bin/env node
import { ultragoalCommand } from "./cli-commands.js";
import { runPreToolUseGoalBudgetGuardCli, runUltragoalHookCli } from "./claude-hook.js";
const TOP_LEVEL_HELP =
"Usage:\n omo ultragoal <subcommand> [args]\n omo hook user-prompt-submit (Claude Code UserPromptSubmit hook)\n omo help | --help | -h (this message)\n\nRun `omo ultragoal help` for ultragoal subcommands.\n";
async function main(): Promise<number> {
const argv = process.argv.slice(2);
const command = argv[0];
if (command === undefined || command === "help" || command === "--help" || command === "-h") {
process.stdout.write(TOP_LEVEL_HELP);
return 0;
}
if (command === "ultragoal") return ultragoalCommand(argv.slice(1));
if (command === "hook") {
const sub = argv[1];
if (sub === "user-prompt-submit") {
await runUltragoalHookCli(process.stdin, process.stdout);
return 0;
}
if (sub === "pre-tool-use") {
await runPreToolUseGoalBudgetGuardCli(process.stdin, process.stdout);
return 0;
}
process.stderr.write(`[omo] unknown hook subcommand: ${sub ?? "(none)"}\n`);
return 1;
}
process.stderr.write(`[omo] unknown command: ${command}\n${TOP_LEVEL_HELP}`);
return 1;
}
main()
.then((code) => {
process.exit(code);
})
.catch((error: unknown) => {
process.stderr.write(`[omo] ${error instanceof Error ? error.message : String(error)}\n`);
process.exit(1);
});
@@ -0,0 +1,122 @@
// biome-ignore-all format: keep this module under the mandated pure LOC budget.
import { hasAllCriteriaPass } from "./goal-status.js";
import { appendLedger, readUltragoalPlan, withUltragoalMutationLock, writePlan } from "./plan-io.js";
import type { UltragoalScope } from "./session-scope.js";
import type { UltragoalItem, UltragoalLedgerEntry, UltragoalPlan, UltragoalSuccessCriterion } from "./types.js";
import { iso, UltragoalError } from "./types.js";
type EvidenceStatus = "pass" | "fail" | "blocked";
type RecordEvidenceArgs = { readonly goalId: string; readonly criterionId: string; readonly status: EvidenceStatus; readonly evidence: string; readonly notes?: string };
function ultragoalFail(message: string, code: string, details: Record<string, unknown>): never { throw new UltragoalError(message, code, { details }); }
function ledgerKind(status: EvidenceStatus): UltragoalLedgerEntry["kind"] {
switch (status) {
case "pass":
return "evidence_captured";
case "fail":
return "criterion_failed";
case "blocked":
return "criterion_blocked";
default:
return ultragoalFail("Invalid criterion status.", "ULTRAGOAL_CRITERION_STATUS_INVALID", { status });
}
}
function findGoal(plan: UltragoalPlan, goalId: string): UltragoalItem {
const goal = plan.goals.find((candidate) => candidate.id === goalId);
return goal ?? ultragoalFail(`Ultragoal goal not found: ${goalId}.`, "ULTRAGOAL_GOAL_NOT_FOUND", { goalId });
}
function findCriterion(goal: UltragoalItem, criterionId: string): UltragoalSuccessCriterion {
const criterion = goal.successCriteria.find((candidate) => candidate.id === criterionId);
return criterion ?? ultragoalFail(`Success criterion not found: ${criterionId}.`, "ULTRAGOAL_CRITERION_NOT_FOUND", { goalId: goal.id, criterionId });
}
function nonEmptyEvidence(evidence: string): string { const trimmed = evidence.trim(); return trimmed || ultragoalFail("Evidence must be a non-empty string.", "ULTRAGOAL_EVIDENCE_REQUIRED", {}); }
export async function recordEvidence(scope: UltragoalScope, args: RecordEvidenceArgs): Promise<{ plan: UltragoalPlan; goal: UltragoalItem; criterion: UltragoalSuccessCriterion; ledgerEntry: UltragoalLedgerEntry }> {
return withUltragoalMutationLock(scope, async () => {
const plan = await readUltragoalPlan(scope);
const goal = findGoal(plan, args.goalId);
const criterion = findCriterion(goal, args.criterionId);
const evidence = nonEmptyEvidence(args.evidence);
const kind = ledgerKind(args.status);
const prevStatus = criterion.status;
const capturedAt = iso();
criterion.status = args.status;
criterion.capturedEvidence = evidence;
criterion.capturedAt = capturedAt;
if (args.notes !== undefined) criterion.notes = args.notes;
goal.updatedAt = capturedAt;
plan.updatedAt = capturedAt;
await writePlan(scope, plan);
const ledgerEntry: UltragoalLedgerEntry = {
at: capturedAt,
kind,
goalId: goal.id,
criterionId: criterion.id,
criterionStatus: args.status,
evidence,
capturedEvidence: evidence,
before: { status: prevStatus },
after: { goalId: goal.id, criterionId: criterion.id, status: args.status, evidence, capturedAt, prevStatus },
};
await appendLedger(scope, ledgerEntry);
return { plan, goal, criterion, ledgerEntry };
});
}
export async function markCriteriaPendingResetForGoal(scope: UltragoalScope, goalId: string): Promise<{ plan: UltragoalPlan; resetCount: number }> {
return withUltragoalMutationLock(scope, async () => {
const plan = await readUltragoalPlan(scope);
const goal = findGoal(plan, goalId);
const now = iso();
const before = goal.successCriteria.map((criterion) => ({ id: criterion.id, status: criterion.status, capturedEvidence: criterion.capturedEvidence, capturedAt: criterion.capturedAt ?? null }));
for (const criterion of goal.successCriteria) {
criterion.status = "pending";
criterion.capturedEvidence = null;
delete criterion.capturedAt;
delete criterion.notes;
}
goal.updatedAt = now;
plan.updatedAt = now;
await writePlan(scope, plan);
await appendLedger(scope, { at: now, kind: "criteria_revised", goalId, message: `Reset ${goal.successCriteria.length} criteria to pending.`, before, after: { resetCount: goal.successCriteria.length } });
return { plan, resetCount: goal.successCriteria.length };
});
}
export function criteriaSummary(plan: UltragoalPlan): { totalCriteria: number; passCount: number; pendingCount: number; failCount: number; blockedCount: number; goalsWithUnresolvedCriteria: string[] } {
let totalCriteria = 0;
let passCount = 0;
let pendingCount = 0;
let failCount = 0;
let blockedCount = 0;
const goalsWithUnresolvedCriteria: string[] = [];
for (const goal of plan.goals) {
let unresolved = false;
for (const criterion of goal.successCriteria) {
totalCriteria += 1;
if (criterion.status !== "pass") unresolved = true;
switch (criterion.status) {
case "pass": passCount += 1; break;
case "pending": pendingCount += 1; break;
case "fail": failCount += 1; break;
case "blocked": blockedCount += 1; break;
default: ultragoalFail("Invalid criterion status.", "ULTRAGOAL_CRITERION_STATUS_INVALID", { status: criterion.status });
}
}
if (unresolved) goalsWithUnresolvedCriteria.push(goal.id);
}
return { totalCriteria, passCount, pendingCount, failCount, blockedCount, goalsWithUnresolvedCriteria };
}
export function unresolvedCriteriaOf(goal: UltragoalItem): UltragoalSuccessCriterion[] { return goal.successCriteria.filter((criterion) => criterion.status !== "pass"); }
export function requireAllCriteriaPass(goal: UltragoalItem): void {
if (hasAllCriteriaPass(goal)) return;
throw new UltragoalError(`Goal ${goal.id} has unresolved success criteria.`, "ultragoal_criteria_not_all_pass", {
details: { goalId: goal.id, unresolved: unresolvedCriteriaOf(goal).map((criterion) => ({ id: criterion.id, status: criterion.status })) },
});
}
@@ -0,0 +1,110 @@
import { expectedObjective, goalMode, isFinalRunCompletionCandidate } from "./goal-status.js";
import type { UltragoalGoalMode, UltragoalItem, UltragoalPlan, UltragoalSuccessCriterion } from "./types.js";
export interface UltragoalGoalInstruction {
readonly text: string;
readonly objective: string;
}
export function buildGoalInstruction(args: {
readonly plan: UltragoalPlan;
readonly goal: UltragoalItem;
readonly isFinal?: boolean;
}): UltragoalGoalInstruction {
const mode = goalMode(args.plan);
const objective = expectedObjective(args.plan, args.goal);
const isFinal = args.isFinal ?? isFinalRunCompletionCandidate(args.plan, args.goal);
return { text: buildText(mode, args.plan, args.goal, objective, isFinal), objective };
}
function buildText(
mode: UltragoalGoalMode,
plan: UltragoalPlan,
goal: UltragoalItem,
objective: string,
isFinal: boolean,
): string {
return joinLines([
mode === "aggregate" ? "Ultragoal aggregate-goal handoff" : "Ultragoal active-goal handoff",
`Mode: ${mode}`,
`Plan: ${plan.goalsPath}`,
`Ledger: ${plan.ledgerPath}`,
`Session: ${plan.sessionId}`,
`Goal: ${goal.id}${goal.title}`,
"",
...activeGoalLines(goal),
"",
...successCriteriaLines(goal.successCriteria),
"",
"Ultragoal tracking constraints (file/steering based — no goal tool required):",
`- The durable objective is tracked in ${plan.goalsPath}; treat it as the source of truth.`,
"- Goals are unlimited. Do not impose numeric token/work limits.",
...modeConstraintLines(mode, isFinal),
finalSection(goal, isFinal, mode === "aggregate"),
...checkpointLines(mode),
"",
"Active objective:",
objective,
]);
}
function modeConstraintLines(mode: UltragoalGoalMode, isFinal: boolean): readonly string[] {
if (mode === "per_story") {
return [
"- Work only this goal until its completion audit passes, then checkpoint it.",
"- Record success-criteria evidence with `omo ultragoal record-evidence` as you go.",
];
}
return [
"- The aggregate objective spans the whole ultragoal run; OMO G001/G002/etc. are ledger stories.",
"- Continue the current OMO story; do not start a competing objective.",
isFinal
? "- This is the final story; complete it only after the mandatory quality gate passes."
: "- This is not the final story: keep the aggregate objective active while later OMO stories remain.",
];
}
function checkpointLines(mode: UltragoalGoalMode): readonly string[] {
const failureLine =
"- If blocked or failed, checkpoint with --status failed and the failure evidence; rerun complete-goals --retry-failed to resume.";
if (mode === "per_story") return [failureLine];
return [
"- Checkpoint this OMO story once its success criteria pass under the aggregate objective.",
failureLine,
];
}
function activeGoalLines(goal: UltragoalItem): readonly string[] {
return ["Active goal:", `- id: ${goal.id}`, `- title: ${goal.title}`, `- objective: ${goal.objective}`];
}
function successCriteriaLines(criteria: readonly UltragoalSuccessCriterion[]): readonly string[] {
if (criteria.length === 0) return ["Success criteria:", "- No success criteria recorded for this goal."];
return ["Success criteria:", ...criteria.map(formatCriterionLine)];
}
function formatCriterionLine(criterion: UltragoalSuccessCriterion): string {
const remainingWork = criterion.status === "pending" ? " remaining work:" : "";
return `-${remainingWork} [${criterion.id}] (${criterion.userModel}) ${criterion.scenario} — expect: ${criterion.expectedEvidence} — status: ${criterion.status}`;
}
function finalSection(goal: UltragoalItem, isFinal: boolean, aggregate: boolean): string {
if (!isFinal)
return "- This is not the final ultragoal story; do not run the final ai-slop-cleaner/$code-review gate yet.";
const blockerCommand = `omo ultragoal record-review-blockers --goal-id ${goal.id} --title "Resolve final code-review blockers" --objective "<blocker-resolution objective>" --evidence "<review findings>"`;
const checkpointCommand = `omo ultragoal checkpoint --goal-id ${goal.id} --status complete --evidence "<tests/files/PR evidence>" --quality-gate-json "<quality gate JSON or path>"`;
return joinLines([
"Final story — run the mandatory quality gate before completing:",
"- Run ai-slop-cleaner on changed files even when it is a no-op, rerun verification, then run $code-review.",
"- If final $code-review is not APPROVE with architect status CLEAR, do not complete. Record blocker work first:",
` ${blockerCommand}`,
aggregate
? "- If final $code-review is clean, checkpoint the aggregate story:"
: "- If final $code-review is clean, checkpoint this story:",
` ${checkpointCommand}`,
]);
}
function joinLines(lines: readonly string[]): string {
return lines.join("\n");
}
@@ -0,0 +1,134 @@
import { existsSync } from "node:fs";
import { readFile } from "node:fs/promises";
import { resolve } from "node:path";
export type GoalSnapshotStatus = "active" | "complete" | "cancelled" | "failed" | "unknown";
export interface GoalSnapshot {
available: boolean;
objective?: string;
status?: GoalSnapshotStatus;
raw: unknown;
}
export interface GoalReconciliation {
ok: boolean;
snapshot: GoalSnapshot;
warnings: string[];
errors: string[];
}
export interface ReconcileGoalOptions {
expectedObjective: string;
acceptedObjectives?: readonly string[];
allowedStatuses?: readonly GoalSnapshotStatus[];
requireSnapshot?: boolean;
requireComplete?: boolean;
}
export class GoalSnapshotError extends Error {}
function safeObject(value: unknown): Record<string, unknown> {
return value && typeof value === "object" && !Array.isArray(value) ? (value as Record<string, unknown>) : {};
}
function safeString(value: unknown): string {
return typeof value === "string" ? value.trim() : "";
}
function normalizeStatus(value: unknown): GoalSnapshotStatus {
const status = safeString(value).toLowerCase();
if (status === "complete" || status === "completed" || status === "done") return "complete";
if (status === "cancelled" || status === "canceled") return "cancelled";
if (status === "failed" || status === "failure") return "failed";
if (status === "active" || status === "in_progress" || status === "pending" || status === "running") return "active";
return "unknown";
}
function normalizeObjective(value: string): string {
return value.replace(/\s+/g, " ").trim();
}
export function parseGoalSnapshot(value: unknown): GoalSnapshot {
const root = safeObject(value);
const goalValue = Object.hasOwn(root, "goal") ? root["goal"] : value;
if (goalValue === null || goalValue === undefined || goalValue === false) {
return { available: false, raw: value };
}
const goal = safeObject(goalValue);
const objective = safeString(goal["objective"] ?? goal["goal"] ?? goal["description"] ?? root["objective"]);
const status = normalizeStatus(goal["status"] ?? root["status"]);
return {
available: Boolean(objective || status !== "unknown"),
...(objective ? { objective } : {}),
status,
raw: value,
};
}
export async function readGoalSnapshotInput(raw: string | undefined, cwd = process.cwd()): Promise<GoalSnapshot | null> {
if (!raw?.trim()) return null;
const trimmed = raw.trim();
try {
return parseGoalSnapshot(JSON.parse(trimmed));
} catch {
const path = resolve(cwd, trimmed);
if (!existsSync(path)) {
throw new GoalSnapshotError(`Goal snapshot is neither valid JSON nor a readable path: ${trimmed}`);
}
try {
return parseGoalSnapshot(JSON.parse(await readFile(path, "utf-8")));
} catch (error) {
throw new GoalSnapshotError(
`Goal snapshot path does not contain valid JSON: ${trimmed}${error instanceof Error ? ` (${error.message})` : ""}`,
);
}
}
}
export function reconcileGoalSnapshot(
snapshot: GoalSnapshot | null | undefined,
options: ReconcileGoalOptions,
): GoalReconciliation {
const effectiveSnapshot = snapshot ?? { available: false, raw: null };
const errors: string[] = [];
const warnings: string[] = [];
if (!effectiveSnapshot.available) {
const message = "Goal snapshot is absent or reports no active goal.";
if (options.requireSnapshot) errors.push(message);
else warnings.push(message);
return { ok: errors.length === 0, snapshot: effectiveSnapshot, warnings, errors };
}
const expected = normalizeObjective(options.expectedObjective);
const accepted = new Set(
[expected, ...(options.acceptedObjectives ?? []).map((objective) => normalizeObjective(objective))].filter(
Boolean,
),
);
const actual = normalizeObjective(effectiveSnapshot.objective ?? "");
if (!actual) {
errors.push("Goal snapshot is missing objective text.");
} else if (!accepted.has(actual)) {
errors.push(`Goal objective mismatch: expected "${expected}", got "${actual}".`);
}
const allowed = options.allowedStatuses ?? (options.requireComplete ? ["complete"] : ["active", "complete"]);
const actualStatus = effectiveSnapshot.status ?? "unknown";
if (!allowed.includes(actualStatus)) {
errors.push(`Goal status mismatch: expected ${allowed.join(" or ")}, got ${actualStatus}.`);
}
if (options.requireComplete && actualStatus !== "complete") {
errors.push("Goal is not complete; finish the work and pass a complete snapshot.");
}
return { ok: errors.length === 0, snapshot: effectiveSnapshot, warnings, errors };
}
export function formatGoalReconciliation(reconciliation: GoalReconciliation): string {
const parts = [...reconciliation.errors, ...reconciliation.warnings];
return parts.join(" ");
}
@@ -0,0 +1,84 @@
import type {
UltragoalGoalMode,
UltragoalItem,
UltragoalPlan,
UltragoalStatus,
UltragoalSuccessCriterion,
} from "./types.js";
export const ULTRAGOAL_AGGREGATE_OBJECTIVE: string =
"Complete the durable ultragoal plan in .omo/ultragoal/goals.json, including later accepted/appended stories, under the original brief constraints; use .omo/ultragoal/ledger.jsonl as the audit trail.";
export function goalMode(plan: UltragoalPlan): UltragoalGoalMode {
return plan.goalMode ?? "per_story";
}
function isResolvedStatus(status: UltragoalStatus): boolean {
return status === "complete";
}
function isSupersededResolved(goal: UltragoalItem, plan: UltragoalPlan): boolean {
if (goal.steeringStatus !== "superseded") return false;
const replacements = goal.supersededBy ?? [];
if (replacements.length === 0) return false;
return replacements.every((id) => {
const replacement = plan.goals.find((candidate) => candidate.id === id);
return replacement !== undefined && isResolvedStatus(replacement.status);
});
}
function isCompletionBlocking(goal: UltragoalItem, plan: UltragoalPlan): boolean {
if (goal.steeringStatus === "superseded") return !isSupersededResolved(goal, plan);
if (goal.steeringStatus === "blocked") return true;
return !isResolvedStatus(goal.status);
}
function isCompletionBlockingForFinalCandidate(
candidate: UltragoalItem,
finalCandidate: UltragoalItem,
plan: UltragoalPlan,
): boolean {
if (candidate.id === finalCandidate.id) return false;
if (candidate.steeringStatus === "superseded") {
const replacements = candidate.supersededBy ?? [];
if (replacements.length === 0) return true;
return !replacements.every((id) => {
if (id === finalCandidate.id) return true;
const replacement = plan.goals.find((goal) => goal.id === id);
return replacement !== undefined && isResolvedStatus(replacement.status);
});
}
return isCompletionBlocking(candidate, plan);
}
export function isUltragoalDone(plan: UltragoalPlan): boolean {
if (plan.aggregateCompletion?.status === "complete") return true;
return plan.goals.every((goal) => !isCompletionBlocking(goal, plan));
}
export function isFinalRunCompletionCandidate(plan: UltragoalPlan, goal: UltragoalItem): boolean {
return (
isCompletionBlocking(goal, plan) &&
plan.goals.every((candidate) => !isCompletionBlockingForFinalCandidate(candidate, goal, plan))
);
}
export function aggregateObjective(plan: UltragoalPlan): string {
return plan.objective ?? ULTRAGOAL_AGGREGATE_OBJECTIVE;
}
export function expectedObjective(plan: UltragoalPlan, goal: UltragoalItem): string {
return goalMode(plan) === "aggregate" ? aggregateObjective(plan) : goal.objective;
}
export function compatibleObjectives(plan: UltragoalPlan): readonly string[] {
return [aggregateObjective(plan), ...(plan.objectiveAliases ?? [])];
}
export function hasAllCriteriaPass(goal: UltragoalItem): boolean {
return goal.successCriteria.length > 0 && goal.successCriteria.every((criterion) => criterion.status === "pass");
}
export function firstUnresolvedCriterion(goal: UltragoalItem): UltragoalSuccessCriterion | undefined {
return goal.successCriteria.find((criterion) => criterion.status !== "pass");
}
@@ -0,0 +1,74 @@
import { join } from "node:path";
import type { UltragoalScope } from "./session-scope.js";
import {
ULTRAGOAL_BRIEF,
ULTRAGOAL_DIR,
ULTRAGOAL_GOALS,
ULTRAGOAL_INDEX,
ULTRAGOAL_LEDGER,
ULTRAGOAL_SESSIONS,
} from "./types.js";
/** Root `./.omo/ultragoal` directory for a repo. */
export function ultragoalRootDir(repoRoot: string): string {
return join(repoRoot, ULTRAGOAL_DIR);
}
/** `./.omo/ultragoal/sessions` directory holding all per-session scopes. */
export function ultragoalSessionsRoot(repoRoot: string): string {
return join(ultragoalRootDir(repoRoot), ULTRAGOAL_SESSIONS);
}
/** Per-repo session registry path `./.omo/ultragoal/index.json`. */
export function ultragoalIndexPath(repoRoot: string): string {
return join(ultragoalRootDir(repoRoot), ULTRAGOAL_INDEX);
}
/** Per-session content directory `./.omo/ultragoal/sessions/<scope>`. */
export function ultragoalSessionDir(scope: UltragoalScope): string {
return join(ultragoalSessionsRoot(scope.repoRoot), scope.sessionScope);
}
/** Backwards-compatible alias used by the rest of the codebase. */
export function ultragoalDir(scope: UltragoalScope): string {
return ultragoalSessionDir(scope);
}
export function ultragoalBriefPath(scope: UltragoalScope): string {
return join(ultragoalSessionDir(scope), ULTRAGOAL_BRIEF);
}
export function ultragoalGoalsPath(scope: UltragoalScope): string {
return join(ultragoalSessionDir(scope), ULTRAGOAL_GOALS);
}
export function ultragoalLedgerPath(scope: UltragoalScope): string {
return join(ultragoalSessionDir(scope), ULTRAGOAL_LEDGER);
}
// --- Legacy repo-level getters (read-only, for v1 -> v2 migration) ---
/** Legacy v1 goals path `./.omo/ultragoal/goals.json` (repo-level, no session). */
export function legacyUltragoalGoalsPath(repoRoot: string): string {
return join(ultragoalRootDir(repoRoot), ULTRAGOAL_GOALS);
}
/** Legacy v1 brief path `./.omo/ultragoal/brief.md`. */
export function legacyUltragoalBriefPath(repoRoot: string): string {
return join(ultragoalRootDir(repoRoot), ULTRAGOAL_BRIEF);
}
/** Legacy v1 ledger path `./.omo/ultragoal/ledger.jsonl`. */
export function legacyUltragoalLedgerPath(repoRoot: string): string {
return join(ultragoalRootDir(repoRoot), ULTRAGOAL_LEDGER);
}
export function repoRelative(absolutePath: string, repoRoot: string): string {
const slashPrefix = `${repoRoot}/`;
const backslashPrefix = `${repoRoot}\\`;
if (absolutePath.startsWith(slashPrefix)) return absolutePath.slice(slashPrefix.length).split("\\").join("/");
if (absolutePath.startsWith(backslashPrefix))
return absolutePath.slice(backslashPrefix.length).split("\\").join("/");
return absolutePath.split("\\").join("/");
}
@@ -0,0 +1,115 @@
// biome-ignore-all format: keep this port under the mandated pure LOC budget.
import { existsSync } from "node:fs";
import { mkdir, writeFile } from "node:fs/promises";
import { ULTRAGOAL_AGGREGATE_OBJECTIVE } from "./goal-status.js";
import { ultragoalBriefPath, ultragoalGoalsPath, ultragoalLedgerPath, ultragoalSessionDir } from "./paths.js";
import { appendLedger, readUltragoalPlan, withUltragoalMutationLock, writePlan } from "./plan-io.js";
import type { UltragoalScope } from "./session-scope.js";
import type { UltragoalGoalMode, UltragoalItem, UltragoalPlan, UltragoalSuccessCriterion } from "./types.js";
import { iso, ULTRAGOAL_DIR, ULTRAGOAL_GOALS, ULTRAGOAL_LEDGER, ULTRAGOAL_PLATFORM, UltragoalError } from "./types.js";
export type UltragoalPlanSummary = { readonly total: number; readonly pending: number; readonly in_progress: number; readonly complete: number; readonly failed: number; readonly blocked: number; readonly review_blocked: number; readonly needs_user_decision: number; readonly superseded: number; readonly criteria: { readonly total: number; readonly pass: number; readonly pending: number; readonly fail: number; readonly blocked: number } };
function cleanLine(line: string): string { return line.replace(/^\s*(?:[-*+]\s+|\d+[.)]\s+)/, "").trim(); }
function normalizeObjective(value: string): string { return value.replace(/\s+/g, " ").trim(); }
function titleFromObjective(objective: string, fallback: string): string { const firstLine = objective.split(/\r?\n/).map((line) => line.trim()).find(Boolean) ?? fallback; return firstLine.length > 72 ? `${firstLine.slice(0, 69).trimEnd()}...` : firstLine; }
function normalizeGoalId(title: string, index: number): string { const slug = title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 36).replace(/-+$/g, ""); return `G${String(index + 1).padStart(3, "0")}${slug ? `-${slug}` : ""}`; }
function assertNonEmpty(value: string | undefined, label: string): string { const trimmed = value?.trim(); if (!trimmed) throw new UltragoalError(`Missing ${label}.`, "ULTRAGOAL_ARGUMENT_MISSING"); return trimmed; }
function truncateObjective(objective: string): string { return objective.length > 80 ? `${objective.slice(0, 77).trimEnd()}...` : objective; }
export function seedDefaultSuccessCriteria(goalIndex: number, objective: string): UltragoalSuccessCriterion[] {
const subject = truncateObjective(normalizeObjective(objective) || `Goal ${goalIndex + 1}`);
const rows = [
["C001", "happy", `happy path for: ${subject}`, `Replace via revise_criterion with observable happy-path proof for goal ${goalIndex + 1}.`],
["C002", "edge", "edge case (boundary/empty/malformed)", `Replace via revise_criterion with boundary or malformed-input proof for: ${subject}.`],
["C003", "regression", "regression: adjacent surface still works", `Replace via revise_criterion with regression proof for neighboring behavior after: ${subject}.`],
] as const;
return rows.map(([id, userModel, scenario, expectedEvidence]) => ({ id, scenario, userModel, expectedEvidence, capturedEvidence: null, status: "pending" }));
}
export function deriveGoalCandidates(brief: string): Array<{ title: string; objective: string }> {
const bulletGoals = brief.split(/\r?\n/).map((line) => ({ original: line, cleaned: normalizeObjective(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);
const paragraphs = brief.split(/\n\s*\n/).map(normalizeObjective).filter((paragraph) => paragraph.length > 0 && !paragraph.startsWith("#"));
const selected = (bulletGoals.length > 0 ? bulletGoals : paragraphs).length > 0 ? (bulletGoals.length > 0 ? bulletGoals : paragraphs) : ["Complete the requested project objective."];
return selected.map((objective, index) => ({ title: titleFromObjective(objective, `Goal ${index + 1}`), objective }));
}
function makeGoal(title: string, objective: string, index: number, now: string): UltragoalItem {
const cleanTitle = assertNonEmpty(title, "title");
const cleanObjective = assertNonEmpty(objective, "objective");
return { id: normalizeGoalId(cleanTitle, index), title: cleanTitle, objective: cleanObjective, status: "pending", successCriteria: seedDefaultSuccessCriteria(index, cleanObjective), attempt: 0, createdAt: now, updatedAt: now };
}
function appendGoalToPlan(plan: UltragoalPlan, title: string, objective: string, now: string): UltragoalItem {
const goal = makeGoal(title, objective, plan.goals.length, now);
plan.goals.push(goal);
plan.updatedAt = now;
return goal;
}
function isScheduleEligible(goal: UltragoalItem): boolean { return goal.steeringStatus !== "superseded" && goal.steeringStatus !== "blocked"; }
function clearGoalBlockerFields(goal: UltragoalItem): void {
for (const key of ["blockedReason", "blockerSignature", "blockerOccurrenceCount", "requiredExternalDecision", "nonRetriable", "failedAt", "failureReason"] as const) delete goal[key];
}
export async function createUltragoalPlan(scope: UltragoalScope, args: { brief: string; goalMode?: UltragoalGoalMode; force?: boolean }): Promise<UltragoalPlan> {
return withUltragoalMutationLock(scope, async () => {
if (!args.force && existsSync(ultragoalGoalsPath(scope))) throw new UltragoalError(`Refusing to overwrite existing ${ULTRAGOAL_DIR}/sessions/${scope.sessionScope}/${ULTRAGOAL_GOALS}; pass --force to recreate it.`, "ULTRAGOAL_PLAN_EXISTS");
const now = iso();
const goals = deriveGoalCandidates(args.brief).map((goal, index) => makeGoal(goal.title, goal.objective, index, now));
const sessionPrefix = `${ULTRAGOAL_DIR}/sessions/${scope.sessionScope}`;
const plan: UltragoalPlan = { version: 2, platform: ULTRAGOAL_PLATFORM, sessionId: scope.sessionId, sessionScope: scope.sessionScope, createdAt: now, updatedAt: now, briefPath: `${sessionPrefix}/brief.md`, goalsPath: `${sessionPrefix}/${ULTRAGOAL_GOALS}`, ledgerPath: `${sessionPrefix}/${ULTRAGOAL_LEDGER}`, goalMode: args.goalMode ?? "aggregate", goals };
if (plan.goalMode === "aggregate") plan.objective = ULTRAGOAL_AGGREGATE_OBJECTIVE;
await mkdir(ultragoalSessionDir(scope), { recursive: true });
await writeFile(ultragoalBriefPath(scope), args.brief.endsWith("\n") ? args.brief : `${args.brief}\n`, "utf8");
await writePlan(scope, plan);
await writeFile(ultragoalLedgerPath(scope), "", "utf8");
await appendLedger(scope, { at: now, kind: "plan_created", message: `${goals.length} goal(s) created` });
return plan;
});
}
export async function addUltragoalGoal(scope: UltragoalScope, args: { title: string; objective: string }): Promise<{ plan: UltragoalPlan; goal: UltragoalItem }> {
return withUltragoalMutationLock(scope, async () => {
const plan = await readUltragoalPlan(scope);
const now = iso();
const goal = appendGoalToPlan(plan, args.title, args.objective, now);
await writePlan(scope, plan);
await appendLedger(scope, { at: now, kind: "goal_added", goalId: goal.id, status: goal.status, message: goal.title });
return { plan, goal };
});
}
export async function startNextUltragoal(scope: UltragoalScope, args: { retryFailed?: boolean } = {}): Promise<{ plan: UltragoalPlan; goal: UltragoalItem; resumed: boolean } | { done: true; plan: UltragoalPlan }> {
return withUltragoalMutationLock(scope, async () => {
const plan = await readUltragoalPlan(scope);
const now = iso();
if (plan.aggregateCompletion?.status === "complete") return { done: true, plan };
const existing = plan.goals.find((goal) => goal.status === "in_progress" && isScheduleEligible(goal));
if (existing) { await appendLedger(scope, { at: now, kind: "goal_resumed", goalId: existing.id, status: existing.status, message: "Resuming active ultragoal" }); return { plan, goal: existing, resumed: true }; }
let next = plan.goals.find((goal) => goal.status === "pending" && isScheduleEligible(goal));
if (!next && args.retryFailed) {
next = plan.goals.find((goal) => goal.status === "failed" && !goal.nonRetriable && isScheduleEligible(goal));
if (next) await appendLedger(scope, { at: now, kind: "goal_retried", goalId: next.id, status: "pending", ...(next.failureReason ? { message: next.failureReason } : {}) });
}
if (!next) return { done: true, plan };
next.status = "in_progress";
next.attempt += 1;
next.startedAt = now;
clearGoalBlockerFields(next);
next.updatedAt = now;
plan.activeGoalId = next.id;
plan.updatedAt = now;
await writePlan(scope, plan);
await appendLedger(scope, { at: now, kind: "goal_started", goalId: next.id, status: next.status, message: `Attempt ${next.attempt}` });
return { plan, goal: next, resumed: false };
});
}
export function summarizeUltragoalPlan(plan: UltragoalPlan): UltragoalPlanSummary {
const countStatus = (status: UltragoalItem["status"]): number => plan.goals.filter((goal) => goal.status === status).length;
const countCriteria = (status: UltragoalSuccessCriterion["status"]): number => plan.goals.reduce((sum, goal) => sum + goal.successCriteria.filter((criterion) => criterion.status === status).length, 0);
return { total: plan.goals.length, pending: countStatus("pending"), in_progress: countStatus("in_progress"), complete: countStatus("complete"), failed: countStatus("failed"), blocked: countStatus("blocked"), review_blocked: countStatus("review_blocked"), needs_user_decision: countStatus("needs_user_decision"), superseded: plan.goals.filter((goal) => goal.steeringStatus === "superseded").length, criteria: { total: plan.goals.reduce((sum, goal) => sum + goal.successCriteria.length, 0), pass: countCriteria("pass"), pending: countCriteria("pending"), fail: countCriteria("fail"), blocked: countCriteria("blocked") } };
}
@@ -0,0 +1,240 @@
import { appendFile, mkdir, readFile, rename, writeFile } from "node:fs/promises";
import {
legacyUltragoalGoalsPath,
repoRelative,
ultragoalGoalsPath,
ultragoalIndexPath,
ultragoalLedgerPath,
ultragoalRootDir,
ultragoalSessionDir,
} from "./paths.js";
import type { UltragoalScope } from "./session-scope.js";
import type {
UltragoalIndex,
UltragoalIndexEntry,
UltragoalLedgerEntry,
UltragoalLegacyPlanV1,
UltragoalPlan,
} from "./types.js";
import {
iso,
ULTRAGOAL_DIR,
ULTRAGOAL_GOALS,
ULTRAGOAL_LEDGER,
ULTRAGOAL_PLATFORM,
UltragoalError,
} from "./types.js";
const AGGREGATE_OBJECTIVE = `Complete the durable ultragoal plan in ${ULTRAGOAL_DIR}/${ULTRAGOAL_GOALS}, including later accepted/appended stories, under the original brief constraints; use ${ULTRAGOAL_DIR}/${ULTRAGOAL_LEDGER} as the audit trail.`;
const LEGACY_OBJECTIVE_PREFIX = `Complete all ultragoal stories in ${ULTRAGOAL_DIR}/${ULTRAGOAL_GOALS}: `;
const LEGACY_OBJECTIVE = `Complete all ultragoal stories listed in ${ULTRAGOAL_DIR}/${ULTRAGOAL_GOALS}. Use ${ULTRAGOAL_DIR}/${ULTRAGOAL_LEDGER} as the durable audit trail.`;
const locks = new Map<string, Promise<unknown>>();
function lockKey(scope: UltragoalScope): string {
return `${scope.repoRoot}::${scope.sessionScope}`;
}
function hasCode(error: unknown, code: string): boolean {
return error instanceof Error && "code" in error && error.code === code;
}
function isLegacyEnumeratedAggregateObjective(objective: string | undefined): objective is string {
return objective === LEGACY_OBJECTIVE || Boolean(objective?.startsWith(LEGACY_OBJECTIVE_PREFIX));
}
function isSteeringKind(value: unknown): value is UltragoalLedgerEntry["kind"] {
return value === "steering_accepted" || value === "steering_rejected" || value === "criteria_revised";
}
export async function withUltragoalMutationLock<T>(scope: UltragoalScope, fn: () => Promise<T>): Promise<T> {
const key = lockKey(scope);
const prior = locks.get(key) ?? Promise.resolve();
const run = prior.then(fn, fn);
locks.set(
key,
run.catch(() => undefined),
);
return run;
}
function assertValidV2Plan(plan: UltragoalPlan, scope: UltragoalScope, path: string): void {
if (plan.version !== 2 || !Array.isArray(plan.goals)) {
throw new UltragoalError(
`Invalid ultragoal plan at ${repoRelative(path, scope.repoRoot)}.`,
"ULTRAGOAL_PLAN_INVALID",
);
}
}
/**
* Read the plan for a session scope. If no session-scoped plan exists but a
* legacy v1 repo-level plan is present, the v1 plan is migrated forward into
* this session scope (D3) and written there; the original v1 file is left in
* place (never deleted).
*/
export async function readUltragoalPlan(scope: UltragoalScope): Promise<UltragoalPlan> {
const path = ultragoalGoalsPath(scope);
let raw: string;
try {
raw = await readFile(path, "utf8");
} catch (error) {
if (!hasCode(error, "ENOENT")) throw error;
const migrated = await tryMigrateLegacyV1(scope);
if (migrated !== null) return migrated;
throw new UltragoalError(
`No ultragoal plan found at ${repoRelative(path, scope.repoRoot)}. Run \`omo ultragoal create-goals ...\` first.`,
"ULTRAGOAL_PLAN_MISSING",
{ cause: error },
);
}
const parsed: UltragoalPlan = JSON.parse(raw);
assertValidV2Plan(parsed, scope, path);
return await maybeMigrateAggregateObjective(scope, parsed);
}
async function maybeMigrateAggregateObjective(scope: UltragoalScope, plan: UltragoalPlan): Promise<UltragoalPlan> {
const previousObjective = plan.objective;
if ((plan.goalMode ?? "per_story") === "aggregate" && isLegacyEnumeratedAggregateObjective(previousObjective)) {
const now = iso();
plan.objective = AGGREGATE_OBJECTIVE;
plan.objectiveAliases = [...new Set([...(plan.objectiveAliases ?? []), previousObjective])];
plan.updatedAt = now;
await writePlan(scope, plan);
await appendLedger(scope, {
at: now,
kind: "aggregate_objective_migrated",
message: "Migrated legacy enumerated aggregate objective to the stable pointer objective.",
before: { objective: previousObjective },
after: { objective: plan.objective },
});
}
return plan;
}
/**
* Read a legacy repo-level v1 plan (if present) and migrate it into the given
* session scope. Returns the migrated v2 plan, or null when no v1 file exists.
* The legacy v1 file is read-only and never deleted.
*/
async function tryMigrateLegacyV1(scope: UltragoalScope): Promise<UltragoalPlan | null> {
const legacyPath = legacyUltragoalGoalsPath(scope.repoRoot);
let legacyRaw: string;
try {
legacyRaw = await readFile(legacyPath, "utf8");
} catch (error) {
if (hasCode(error, "ENOENT")) return null;
throw error;
}
const legacy: UltragoalLegacyPlanV1 = JSON.parse(legacyRaw);
if (legacy.version !== 1 || !Array.isArray(legacy.goals)) return null;
const now = iso();
const plan = migrateV1ToV2(legacy, scope);
await writePlan(scope, plan);
await appendLedger(scope, {
at: now,
kind: "plan_migrated_to_session",
message: `Migrated legacy v1 plan into session ${scope.sessionId} (original v1 file left intact).`,
before: { version: 1, goalsPath: legacy.goalsPath },
after: { version: 2, sessionScope: scope.sessionScope, goalsPath: plan.goalsPath },
});
return plan;
}
function migrateV1ToV2(legacy: UltragoalLegacyPlanV1, scope: UltragoalScope): UltragoalPlan {
const sessionPrefix = `${ULTRAGOAL_DIR}/sessions/${scope.sessionScope}`;
const plan: UltragoalPlan = {
version: 2,
platform: ULTRAGOAL_PLATFORM,
sessionId: scope.sessionId,
sessionScope: scope.sessionScope,
createdAt: legacy.createdAt,
updatedAt: legacy.updatedAt,
briefPath: `${sessionPrefix}/brief.md`,
goalsPath: `${sessionPrefix}/${ULTRAGOAL_GOALS}`,
ledgerPath: `${sessionPrefix}/${ULTRAGOAL_LEDGER}`,
goals: legacy.goals,
};
if (legacy.codexGoalMode !== undefined) plan.goalMode = legacy.codexGoalMode;
if (legacy.codexObjective !== undefined) plan.objective = legacy.codexObjective;
if (legacy.codexObjectiveAliases !== undefined) plan.objectiveAliases = legacy.codexObjectiveAliases;
if (legacy.activeGoalId !== undefined) plan.activeGoalId = legacy.activeGoalId;
if (legacy.aggregateCompletion !== undefined) {
const completion = legacy.aggregateCompletion;
plan.aggregateCompletion = {
status: completion.status,
completedAt: completion.completedAt,
evidence: completion.evidence,
...(completion.codexGoal === undefined ? {} : { goalSnapshot: completion.codexGoal }),
};
}
return plan;
}
export async function writePlan(scope: UltragoalScope, plan: UltragoalPlan): Promise<void> {
await mkdir(ultragoalSessionDir(scope), { recursive: true });
const path = ultragoalGoalsPath(scope);
const tmpPath = `${path}.${process.pid}.${Date.now()}.tmp`;
await writeFile(tmpPath, `${JSON.stringify(plan, null, 2)}\n`, "utf8");
await rename(tmpPath, path);
await upsertIndexEntry(scope, plan);
}
export async function appendLedger(scope: UltragoalScope, entry: UltragoalLedgerEntry): Promise<void> {
await mkdir(ultragoalSessionDir(scope), { recursive: true });
await appendFile(ultragoalLedgerPath(scope), `${JSON.stringify(entry)}\n`, "utf8");
}
export async function readSteeringLedgerEntries(scope: UltragoalScope): Promise<UltragoalLedgerEntry[]> {
let raw: string;
try {
raw = await readFile(ultragoalLedgerPath(scope), "utf8");
} catch (error) {
if (hasCode(error, "ENOENT")) return [];
throw error;
}
const entries: UltragoalLedgerEntry[] = [];
for (const line of raw.split(/\r?\n/).filter(Boolean)) {
const entry: UltragoalLedgerEntry = JSON.parse(line);
if (isSteeringKind(entry.kind)) entries.push(entry);
}
return entries;
}
// --- index.json registry ---
export async function readUltragoalIndex(repoRoot: string): Promise<UltragoalIndex> {
try {
const raw = await readFile(ultragoalIndexPath(repoRoot), "utf8");
const parsed: UltragoalIndex = JSON.parse(raw);
if (parsed.version === 2 && Array.isArray(parsed.sessions)) return parsed;
return { version: 2, sessions: [] };
} catch (error) {
if (hasCode(error, "ENOENT")) return { version: 2, sessions: [] };
throw error;
}
}
async function writeUltragoalIndex(repoRoot: string, index: UltragoalIndex): Promise<void> {
await mkdir(ultragoalRootDir(repoRoot), { recursive: true });
const path = ultragoalIndexPath(repoRoot);
const tmpPath = `${path}.${process.pid}.${Date.now()}.tmp`;
await writeFile(tmpPath, `${JSON.stringify(index, null, 2)}\n`, "utf8");
await rename(tmpPath, path);
}
/** Atomically upsert this session's entry in the per-repo registry. */
async function upsertIndexEntry(scope: UltragoalScope, plan: UltragoalPlan): Promise<void> {
const index = await readUltragoalIndex(scope.repoRoot);
const entry: UltragoalIndexEntry = {
sessionId: scope.sessionId,
sessionScope: scope.sessionScope,
platform: ULTRAGOAL_PLATFORM,
createdAt: plan.createdAt,
updatedAt: plan.updatedAt,
goalsPath: plan.goalsPath,
};
const next = index.sessions.filter((session) => session.sessionScope !== scope.sessionScope);
next.push(entry);
await writeUltragoalIndex(scope.repoRoot, { version: 2, sessions: next });
}
@@ -0,0 +1,102 @@
import type { UltragoalItem, UltragoalPlan, UltragoalQualityGate } from "./types.js";
import { UltragoalError } from "./types.js";
const BLOCKER_FIELD_KEYS = "blocker blockerSignature blockerEvidence blockerOccurrences blockedAt".split(" ");
const URL_PATTERN = /https?:\/\/\S+/g;
const PUNCTUATION_PATTERN = /[`"'()[\]{}:,;]/g;
const WHITESPACE_PATTERN = /\s+/g;
const AUTH_PATTERN = /\b(auth\w*|credential\w*|token|permission\w*|scope\w*|access|unauthorized|forbidden|401|403)\b/;
const MISSING_PATTERN =
/\b(unset|missing|required|requires|without|omit\w*|not set|not available|no read packages|read packages)\b/;
const GHCR_PATTERN =
/\b(ghcr|github container registry|read packages|imagepullsecret|package api|anonymous|container image)\b/;
const GHCR_401_PATTERN = /\b(401|unauthorized|anonymous pull|authentication required)\b/;
const GHCR_403_PATTERN = /\b(403|forbidden|read packages|package api)\b/;
function invalid(message: string, field: string): never {
throw new UltragoalError(message, "ULTRAGOAL_QUALITY_GATE_INVALID", { details: { field } });
}
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
function section(value: unknown, field: string): Record<string, unknown> {
return isRecord(value) ? value : invalid(`Final quality gate is missing ${field} evidence.`, field);
}
function nonEmptyString(value: unknown, field: string): string {
return typeof value === "string" && value.trim() !== ""
? value
: invalid(`Final quality gate requires non-empty ${field}.`, field);
}
function numberField(value: unknown, field: string): number {
return typeof value === "number" && Number.isFinite(value)
? value
: invalid(`Final quality gate requires numeric ${field}.`, field);
}
function stringArray(value: unknown, field: string): string[] {
if (!Array.isArray(value) || value.length === 0) return invalid(`Final quality gate requires ${field}.`, field);
return value.map((item) => nonEmptyString(item, field));
}
export function validateQualityGate(input: unknown): UltragoalQualityGate {
const gate = section(input, "qualityGate");
const cleaner = section(gate["aiSlopCleaner"], "aiSlopCleaner");
const verification = section(gate["verification"], "verification");
const review = section(gate["codeReview"], "codeReview");
const coverage = section(gate["criteriaCoverage"], "criteriaCoverage");
if (cleaner["status"] !== "passed") invalid("aiSlopCleaner.status must be passed.", "aiSlopCleaner.status");
if (verification["status"] !== "passed") invalid("verification.status must be passed.", "verification.status");
if (review["recommendation"] !== "APPROVE") invalid("recommendation must be APPROVE.", "codeReview.recommendation");
if (review["architectStatus"] !== "CLEAR") invalid("architectStatus must be CLEAR.", "codeReview.architectStatus");
const totalCriteria = numberField(coverage["totalCriteria"], "criteriaCoverage.totalCriteria");
const passCount = numberField(coverage["passCount"], "criteriaCoverage.passCount");
if (passCount < totalCriteria)
invalid("criteriaCoverage.passCount must cover totalCriteria.", "criteriaCoverage.passCount");
const commands = stringArray(verification["commands"], "verification.commands");
const covered = stringArray(coverage["adversarialClassesCovered"], "criteriaCoverage.adversarialClassesCovered");
const cleanerEvidence = nonEmptyString(cleaner["evidence"], "aiSlopCleaner.evidence");
const verificationEvidence = nonEmptyString(verification["evidence"], "verification.evidence");
const reviewEvidence = nonEmptyString(review["evidence"], "codeReview.evidence");
const result: UltragoalQualityGate = {
aiSlopCleaner: { status: "passed", evidence: cleanerEvidence },
verification: { status: "passed", commands, evidence: verificationEvidence },
codeReview: { recommendation: "APPROVE", architectStatus: "CLEAR", evidence: reviewEvidence },
};
Object.assign(result, { criteriaCoverage: { totalCriteria, passCount, adversarialClassesCovered: covered } });
return result;
}
export function normalizeBlockerEvidence(evidence: string): string {
const withoutUrls = evidence.toLowerCase().replace(URL_PATTERN, " ");
const withoutPunctuation = withoutUrls.replace(PUNCTUATION_PATTERN, " ");
return withoutPunctuation.replace(WHITESPACE_PATTERN, " ").trim();
}
export function classifyExternalAuthorizationBlocker(evidence: string): string | null {
const normalized = normalizeBlockerEvidence(evidence);
if (!normalized || !AUTH_PATTERN.test(normalized) || !MISSING_PATTERN.test(normalized)) return null;
if (!GHCR_PATTERN.test(normalized)) return "EXTERNAL_AUTHORIZATION_REQUIRED";
const status401 = GHCR_401_PATTERN.test(normalized) ? "HTTP_401_ANONYMOUS" : null;
const status403 = GHCR_403_PATTERN.test(normalized) ? "HTTP_403_NO_READ_PACKAGES" : null;
const status = [status401, status403].filter((part): part is string => part !== null).join("+");
return `GHCR_PULL_ACCESS:${status || "AUTHORIZATION_REQUIRED"}:GHCR_VISIBILITY_OR_CREDENTIAL_REQUIRED`;
}
function nestedBlockerSignature(goal: UltragoalItem): string | null {
const blocker = Reflect.get(goal, "blocker");
const signature = isRecord(blocker) ? blocker["signature"] : null;
return typeof signature === "string" ? signature : null;
}
export function sameBlockerOccurrences(plan: UltragoalPlan, signature: string): number {
return plan.goals.filter((goal) => goal.blockerSignature === signature || nestedBlockerSignature(goal) === signature)
.length;
}
export function clearGoalBlockerFields(goal: UltragoalItem): void {
for (const key of BLOCKER_FIELD_KEYS) Reflect.deleteProperty(goal, key);
}
@@ -0,0 +1,83 @@
// biome-ignore-all format: compact port must stay within the requested pure LOC budget.
import { readGoalSnapshotInput, reconcileGoalSnapshot } from "./goal-snapshot.js";
import { compatibleObjectives, expectedObjective, goalMode, isFinalRunCompletionCandidate } from "./goal-status.js";
import { seedDefaultSuccessCriteria } from "./plan-crud.js";
import { appendLedger, readUltragoalPlan, withUltragoalMutationLock, writePlan } from "./plan-io.js";
import type { UltragoalScope } from "./session-scope.js";
import type { UltragoalItem, UltragoalLedgerEntry, UltragoalPlan } from "./types.js";
import { iso, UltragoalError } from "./types.js";
export interface RecordFinalReviewBlockersArgs { readonly goalId: string; readonly title: string; readonly objective: string; readonly evidence: string; readonly goalSnapshotJson?: string }
export interface RecordFinalReviewBlockersResult { readonly plan: UltragoalPlan; readonly blockedGoal: UltragoalItem; readonly newGoal: UltragoalItem; readonly ledgerEntries: UltragoalLedgerEntry[] }
const BLOCKER_FIELDS = "blockedReason blockerSignature blockerOccurrenceCount requiredExternalDecision nonRetriable failedAt failureReason completedAt blocker blockerEvidence blockerOccurrences blockedAt".split(" ");
function ultragoalError(message: string, code: string): never {
throw new UltragoalError(message, code);
}
function nextGoalId(plan: UltragoalPlan): string {
const max = plan.goals.reduce((current, goal) => {
const digits = /^G(\d+)/u.exec(goal.id)?.[1];
return digits === undefined ? current : Math.max(current, Number(digits));
}, 0);
return `G${String(max + 1).padStart(3, "0")}`;
}
function appendBlockerGoal(plan: UltragoalPlan, args: RecordFinalReviewBlockersArgs, now: string): UltragoalItem {
const index = plan.goals.length;
const goal: UltragoalItem = {
id: nextGoalId(plan),
title: args.title,
objective: args.objective,
status: "pending",
successCriteria: seedDefaultSuccessCriteria(index, args.objective),
attempt: 0,
createdAt: now,
updatedAt: now,
};
plan.goals.push(goal);
return goal;
}
export async function recordFinalReviewBlockers(
scope: UltragoalScope,
args: RecordFinalReviewBlockersArgs,
): Promise<RecordFinalReviewBlockersResult> {
return withUltragoalMutationLock(scope, async () => {
const plan = await readUltragoalPlan(scope);
const goal = plan.goals.find((candidate) => candidate.id === args.goalId);
if (goal === undefined) ultragoalError(`Unknown ultragoal id: ${args.goalId}`, "ultragoal_goal_not_found");
if (goal.status !== "in_progress") ultragoalError(`${goal.id} is ${goal.status}.`, "ultragoal_goal_not_in_progress");
if (!isFinalRunCompletionCandidate(plan, goal)) ultragoalError(`${goal.id} is not final.`, "ultragoal_not_final_story");
// Goal snapshot is optional under the file/steering model (no create_goal/get_goal dependency).
const snapshot = await readGoalSnapshotInput(args.goalSnapshotJson, scope.repoRoot);
const aggregate = goalMode(plan) === "aggregate";
if (args.goalSnapshotJson !== undefined) {
const reconciliation = reconcileGoalSnapshot(snapshot, { expectedObjective: expectedObjective(plan, goal), ...(aggregate ? { acceptedObjectives: compatibleObjectives(plan) } : {}), allowedStatuses: ["active"], requireSnapshot: true, requireComplete: false });
if (!reconciliation.ok) ultragoalError(reconciliation.errors.join(" "), "ultragoal_goal_snapshot_mismatch");
}
const now = iso();
for (const field of BLOCKER_FIELDS) Reflect.deleteProperty(goal, field);
goal.status = "review_blocked";
goal.reviewBlockedAt = now;
goal.evidence = args.evidence;
goal.updatedAt = now;
if (plan.activeGoalId === goal.id) delete plan.activeGoalId;
const newGoal = appendBlockerGoal(plan, args, now);
plan.updatedAt = now;
const goalSnapshot = snapshot?.raw;
const blockedEntry: UltragoalLedgerEntry = { at: now, kind: "goal_review_blocked", goalId: goal.id, status: goal.status, evidence: args.evidence, ...(goalSnapshot === undefined ? {} : { goalSnapshot }) };
const addedEntry: UltragoalLedgerEntry = { at: now, kind: "goal_added", goalId: newGoal.id, status: newGoal.status, evidence: args.evidence, message: newGoal.title };
const summaryEntry: UltragoalLedgerEntry = { at: now, kind: "goal_review_blocked", goalId: goal.id, status: goal.status, evidence: args.evidence, ...(goalSnapshot === undefined ? {} : { goalSnapshot }), message: `Review blockers recorded; appended ${newGoal.id}.` };
Reflect.set(summaryEntry, "kind", "blocker_recorded");
const ledgerEntries = [blockedEntry, addedEntry, summaryEntry];
await writePlan(scope, plan);
for (const entry of ledgerEntries) await appendLedger(scope, entry);
return { plan, blockedGoal: goal, newGoal, ledgerEntries };
});
}
@@ -0,0 +1,65 @@
/**
* Per-session scope resolution for ultragoal goal content.
*
* Goal content always lives under `./.omo/ultragoal/` and is tracked per
* Claude Code session keyed by `session_id` with the `claude:` prefix. The
* `UltragoalScope` struct carries everything callers need to resolve a plan;
* it is threaded everywhere instead of a bare session string (D2) so future
* fields can be added without another N-file refactor.
*/
/**
* Recognized session-id prefixes. `claude` is the native Claude Code platform
* prefix; `codex`/`opencode` are accepted so a session id that already carries
* a sibling-platform prefix is left untouched (mirrors
* `packages/boulder-state/src/storage/shared.ts` `normalizeSessionId`).
*/
export const PREFIX_RE = /^(claude|codex|opencode):/;
export const CLAUDE_SESSION_PREFIX = "claude:";
export interface UltragoalScope {
/** Absolute repo root that anchors `./.omo/ultragoal/`. */
readonly repoRoot: string;
/** Prefixed session key, e.g. `claude:abc-123`. */
readonly sessionId: string;
/** Filesystem-safe directory segment derived from `sessionId`. */
readonly sessionScope: string;
}
/**
* Normalize a raw Claude Code `session_id` into a prefixed session key. If the
* value already carries a recognized platform prefix it is returned verbatim;
* otherwise the `claude:` prefix is applied.
*/
export function normalizeClaudeSessionId(sessionId: string): string {
const trimmed = sessionId.trim();
if (trimmed.length === 0) {
throw new Error("session_id must be a non-empty string");
}
if (PREFIX_RE.test(trimmed)) return trimmed;
return `${CLAUDE_SESSION_PREFIX}${trimmed}`;
}
/**
* Derive the filesystem directory segment for a (possibly prefixed) session id.
* The colon separating prefix and id is replaced with a dash and any remaining
* path-hostile characters are sanitized so the segment is always a single safe
* directory name (e.g. `claude:abc/def` -> `claude-abc-def`).
*/
export function sessionScopeDir(sessionId: string): string {
const normalized = normalizeClaudeSessionId(sessionId);
const sanitized = normalized.replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
if (sanitized.length === 0) {
throw new Error(`session_id "${sessionId}" sanitizes to an empty scope directory`);
}
return sanitized;
}
/**
* Build an `UltragoalScope` from a repo root and a raw or prefixed session id.
*/
export function makeUltragoalScope(repoRoot: string, sessionId: string): UltragoalScope {
const normalized = normalizeClaudeSessionId(sessionId);
return { repoRoot, sessionId: normalized, sessionScope: sessionScopeDir(normalized) };
}
@@ -0,0 +1,271 @@
// biome-ignore-all format: compact steering module must stay below the 240 pure-LOC budget
import { isUltragoalDone } from "./goal-status.js";
import { appendLedger, readSteeringLedgerEntries, readUltragoalPlan, withUltragoalMutationLock, writePlan } from "./plan-io.js";
import type { UltragoalScope } from "./session-scope.js";
import type {
SteerUltragoalResult,
UltragoalItem,
UltragoalLedgerEntry,
UltragoalPlan,
UltragoalSteeringAudit,
UltragoalSteeringChildGoal,
UltragoalSteeringMutationKind,
UltragoalSteeringProposal,
UltragoalSteeringSource,
UltragoalSuccessCriterionUserModel,
} from "./types.js";
import { iso, ULTRAGOAL_STEERING_MUTATION_KINDS, ULTRAGOAL_SUCCESS_CRITERION_USER_MODELS } from "./types.js";
const SOURCES = ["user_prompt_submit", "finding", "cli"] as const satisfies readonly UltragoalSteeringSource[];
// NOTE: the plan's aggregate objective field was renamed `codexObjective` -> `objective`.
// We intentionally do NOT list bare `objective`/`objectiveAliases` here because steering
// proposals legitimately carry an `objective` field (add_subgoal/split_subgoal children).
// The aggregate objective remains protected via `aggregateCompletion`, the `status`/
// `complete*` guards, and the `weakens()` invariant.
const PROTECTED = new Set(["aggregateCompletion", "originalConstraints", "qualityGate", "status", "completedAt", "completionStatus"]);
const isObject = (value: unknown): value is object => typeof value === "object" && value !== null; const isPlain = (value: unknown): value is object => isObject(value) && !Array.isArray(value);
const read = (value: object, key: string): unknown => Object.entries(value).find(([name]) => name === key)?.[1];
const isText = (value: unknown): value is string => typeof value === "string" && value.trim().length > 0;
const text = (value: object, key: string): string | undefined => {
const candidate = read(value, key);
return isText(candidate) ? candidate.trim() : undefined;
};
const isKind = (value: unknown): value is UltragoalSteeringMutationKind => typeof value === "string" && ULTRAGOAL_STEERING_MUTATION_KINDS.some((kind) => kind === value);
const isSource = (value: unknown): value is UltragoalSteeringSource => typeof value === "string" && SOURCES.some((source) => source === value);
const isModel = (value: unknown): value is UltragoalSuccessCriterionUserModel => typeof value === "string" && ULTRAGOAL_SUCCESS_CRITERION_USER_MODELS.some((model) => model === value);
const texts = (value: object, key: string): string[] => {
const candidate = read(value, key);
return Array.isArray(candidate) && candidate.every((item) => typeof item === "string") ? candidate : [];
};
function targets(proposal: object): string[] {
const many = texts(proposal, "targetGoalIds");
const one = text(proposal, "targetGoalId") ?? text(proposal, "goalId");
return many.length > 0 ? many : one === undefined ? [] : [one];
}
const after = (proposal: object): object | undefined => {
const candidate = read(proposal, "after");
return isPlain(candidate) ? candidate : undefined;
};
const revised = (proposal: object, direct: string, nested: string): string | undefined => text(proposal, direct) ?? text(after(proposal) ?? proposal, nested);
function child(value: unknown): UltragoalSteeringChildGoal | null {
if (!isPlain(value)) return null;
const title = text(value, "title");
const objective = text(value, "objective");
if (title === undefined || objective === undefined) return null;
return { title, objective };
}
function childValues(proposal: object): unknown[] {
const direct = read(proposal, "childGoals");
if (Array.isArray(direct) && direct.length > 0) return direct;
const nested = after(proposal);
const fromAfter = nested === undefined ? undefined : read(nested, "children");
return Array.isArray(fromAfter) ? fromAfter : [];
}
const children = (proposal: object): UltragoalSteeringChildGoal[] => childValues(proposal).map(child).filter((item): item is UltragoalSteeringChildGoal => item !== null);
const pendingOrder = (proposal: object): string[] => {
const direct = texts(proposal, "pendingOrder");
return direct.length > 0 ? direct : texts(after(proposal) ?? proposal, "pendingGoalIds");
};
function hasProtected(value: unknown): boolean {
if (!isObject(value)) return false;
for (const [key, childValue] of Object.entries(value)) if (PROTECTED.has(key) || key.toLowerCase().includes("complete") || hasProtected(childValue)) return true;
return false;
}
function allText(value: unknown): string {
if (typeof value === "string") return value;
return isObject(value) ? Object.values(value).map(allText).filter(Boolean).join("\n") : "";
}
function weakens(value: unknown): boolean {
const valueText = allText(value).toLowerCase();
return /\b(skip|bypass|weaken|remove|omit|auto[-\s]?complete|mark complete|complete faster)\b/.test(valueText) && /\b(test|tests|verification|review|quality gate|complete|completion)\b/.test(valueText);
}
function auditFor(proposal: unknown, reasons: string[]): UltragoalSteeringAudit {
const object = isPlain(proposal) ? proposal : undefined;
const kindRaw = object === undefined ? undefined : read(object, "kind");
const sourceRaw = object === undefined ? undefined : read(object, "source");
const evidence = object === undefined ? "" : (text(object, "evidence") ?? "");
const rationale = object === undefined ? "" : (text(object, "rationale") ?? "");
const audit: UltragoalSteeringAudit = { kind: isKind(kindRaw) ? kindRaw : "annotate_ledger", source: isSource(sourceRaw) ? sourceRaw : "cli", targetGoalIds: object === undefined ? [] : targets(object), evidence, rationale, invariant: { accepted: reasons.length === 0, structuralInvariantAccepted: reasons.length === 0, evidenceBackedNecessity: evidence.length > 0 && rationale.length > 0, noEasierCompletion: !weakens(proposal), rejectedReasons: reasons, reasons } };
if (object === undefined) return audit;
const criterionId = text(object, "criterionId");
const directiveText = text(object, "directiveText");
const promptSignature = text(object, "promptSignature");
const idempotencyKey = text(object, "idempotencyKey");
if (criterionId !== undefined) audit.criterionId = criterionId;
if (directiveText !== undefined) audit.directiveText = directiveText;
if (promptSignature !== undefined) audit.promptSignature = promptSignature;
if (idempotencyKey !== undefined) audit.idempotencyKey = idempotencyKey;
return audit;
}
export function validateUltragoalSteeringProposal(plan: UltragoalPlan, proposal: unknown): UltragoalSteeringAudit {
const reasons: string[] = [];
if (!isPlain(proposal)) reasons.push("proposal must be an object");
const object = isPlain(proposal) ? proposal : {};
const kind = read(object, "kind");
if (!isKind(kind)) reasons.push(`invalid kind: ${String(kind)}`);
if (!isSource(read(object, "source"))) reasons.push(`invalid source: ${String(read(object, "source"))}`);
if (text(object, "evidence") === undefined) reasons.push("missing evidence");
if (text(object, "rationale") === undefined) reasons.push("missing rationale");
if (hasProtected(proposal)) reasons.push("protected payload");
if (weakens(proposal)) reasons.push("weakened completion");
if (isUltragoalDone(plan)) reasons.push("plan already complete");
if (isKind(kind)) validateKind(plan, object, kind, reasons);
return auditFor(proposal, reasons);
}
function goal(plan: UltragoalPlan, id: string | undefined): UltragoalItem | undefined {
return id === undefined ? undefined : plan.goals.find((item) => item.id === id);
}
function validateKind(plan: UltragoalPlan, proposal: object, kind: UltragoalSteeringMutationKind, reasons: string[]): void {
const target = goal(plan, targets(proposal)[0]);
if (kind === "add_subgoal" && (text(proposal, "title") === undefined || text(proposal, "objective") === undefined)) reasons.push("add_subgoal requires title/objective");
if ((kind === "split_subgoal" || kind === "revise_pending_wording" || kind === "mark_blocked_superseded") && target === undefined) reasons.push(`${kind} requires target`);
if ((kind === "split_subgoal" || kind === "revise_pending_wording") && target !== undefined && target.status !== "pending") reasons.push(`${kind} requires pending target`);
const rawChildren = childValues(proposal);
if (kind === "split_subgoal" && rawChildren.length === 0) reasons.push("split_subgoal requires children");
if ((kind === "split_subgoal" || kind === "mark_blocked_superseded") && rawChildren.some((item) => child(item) === null)) reasons.push(`${kind} children require title/objective`);
if (kind === "reorder_pending") validateOrder(plan, proposal, reasons);
if (kind === "revise_pending_wording" && revised(proposal, "revisedTitle", "title") === undefined && revised(proposal, "revisedObjective", "objective") === undefined) reasons.push("revise_pending_wording requires update");
if (kind === "revise_criterion") validateCriterion(plan, proposal, reasons);
}
function validateOrder(plan: UltragoalPlan, proposal: object, reasons: string[]): void {
const requested = pendingOrder(proposal);
const pending = plan.goals.filter((item) => item.status === "pending" && item.steeringStatus === undefined).map((item) => item.id);
if (requested.length === 0) reasons.push("reorder_pending requires ids");
if (new Set(requested).size !== requested.length) reasons.push("duplicate pending id");
if (requested.some((id) => !pending.includes(id))) reasons.push("unknown pending id");
}
function validateCriterion(plan: UltragoalPlan, proposal: object, reasons: string[]): void {
const target = goal(plan, targets(proposal)[0]);
const criterionId = text(proposal, "criterionId");
if (target === undefined) reasons.push("revise_criterion requires goalId");
else if (criterionId === undefined || target.successCriteria.every((item) => item.id !== criterionId)) reasons.push("revise_criterion requires criterionId");
const model = read(proposal, "userModel");
if (read(proposal, "scenario") === undefined && read(proposal, "expectedEvidence") === undefined && model === undefined) reasons.push("revise_criterion requires update");
if (model !== undefined && !isModel(model)) reasons.push("invalid userModel");
}
function nextId(plan: UltragoalPlan, offset: number): string {
const max = plan.goals.reduce((current, item) => {
const digits = /^G(\d+)$/u.exec(item.id)?.[1];
return digits === undefined ? current : Math.max(current, Number(digits));
}, 0);
return `G${String(max + offset).padStart(3, "0")}`;
}
function makeGoal(plan: UltragoalPlan, childGoal: UltragoalSteeringChildGoal, evidence: string, now: string, offset: number): UltragoalItem {
return { id: nextId(plan, offset), title: childGoal.title, objective: childGoal.objective, status: "pending", successCriteria: [], attempt: 0, createdAt: now, updatedAt: now, evidence };
}
export function applySteeringMutation(plan: UltragoalPlan, proposal: UltragoalSteeringProposal, audit: UltragoalSteeringAudit): UltragoalPlan {
const next = structuredClone(plan);
if (!audit.invariant.accepted) return next;
const now = proposal.now?.toISOString() ?? iso();
if (proposal.kind === "add_subgoal") next.goals.push(makeGoal(next, { title: proposal.title ?? "", objective: proposal.objective ?? "" }, proposal.evidence, now, 1));
if (proposal.kind === "reorder_pending") {
const order = pendingOrder(proposal);
next.goals = [...order.map((id) => goal(next, id)).filter((item): item is UltragoalItem => item !== undefined), ...next.goals.filter((item) => !order.includes(item.id))];
}
if (proposal.kind === "revise_pending_wording") reviseWording(next, proposal, now);
if (proposal.kind === "split_subgoal" || proposal.kind === "mark_blocked_superseded") splitOrBlock(next, proposal, now);
if (proposal.kind === "revise_criterion") reviseCriterion(next, proposal, now);
if (proposal.kind !== "annotate_ledger") next.updatedAt = now;
return next;
}
function reviseWording(plan: UltragoalPlan, proposal: UltragoalSteeringProposal, now: string): void {
const target = goal(plan, targets(proposal)[0]);
if (target === undefined) return;
target.title = revised(proposal, "revisedTitle", "title") ?? target.title;
target.objective = revised(proposal, "revisedObjective", "objective") ?? target.objective;
target.steeringEvidence = proposal.evidence;
target.steeringRationale = proposal.rationale;
target.updatedAt = now;
}
function splitOrBlock(plan: UltragoalPlan, proposal: UltragoalSteeringProposal, now: string): void {
const target = goal(plan, targets(proposal)[0]);
if (target === undefined) return;
const replacements = children(proposal).map((item, index) => makeGoal(plan, item, proposal.evidence, now, index + 1));
target.steeringEvidence = proposal.evidence;
target.steeringRationale = proposal.rationale;
target.updatedAt = now;
if (replacements.length === 0) {
target.status = "blocked";
target.steeringStatus = "blocked";
target.blockedReason = proposal.blockedReason ?? proposal.rationale;
} else {
target.steeringStatus = "superseded";
target.supersededBy = replacements.map((item) => item.id);
for (const item of replacements) item.supersedes = [target.id];
plan.goals.splice(plan.goals.indexOf(target) + 1, 0, ...replacements);
}
if (plan.activeGoalId === target.id) delete plan.activeGoalId;
}
function reviseCriterion(plan: UltragoalPlan, proposal: UltragoalSteeringProposal, now: string): void {
const target = goal(plan, targets(proposal)[0]);
const index = target?.successCriteria.findIndex((item) => item.id === proposal.criterionId) ?? -1;
const current = target?.successCriteria[index];
if (target === undefined || current === undefined) return;
const model = read(proposal, "userModel");
target.successCriteria[index] = { ...current, scenario: text(proposal, "scenario") ?? current.scenario, expectedEvidence: text(proposal, "expectedEvidence") ?? current.expectedEvidence, userModel: isModel(model) ? model : current.userModel };
target.updatedAt = now;
}
function isProposal(value: unknown): value is UltragoalSteeringProposal {
return isPlain(value) && isKind(read(value, "kind")) && isSource(read(value, "source")) && isText(read(value, "evidence")) && isText(read(value, "rationale"));
}
export function parseUltragoalSteeringDirective(text: string): UltragoalSteeringProposal | null {
const match = /(?:^|\s)(?:OMO_ULTRAGOAL_STEER|omo\.ultragoal\.steer|omo ultragoal steer):\s*([\s\S]+)$/u.exec(text);
if (match?.[1] === undefined) return null;
try {
const parsed: unknown = JSON.parse(match[1].trim());
return isProposal(parsed) ? parsed : null;
} catch (error) {
if (error instanceof SyntaxError) return null;
throw error;
}
}
export async function steerUltragoal(scope: UltragoalScope, proposal: UltragoalSteeringProposal): Promise<SteerUltragoalResult> {
return withUltragoalMutationLock(scope, async () => {
const plan = await readUltragoalPlan(scope);
const key = proposal.idempotencyKey ?? proposal.promptSignature;
const prior = key === undefined ? undefined : (await readSteeringLedgerEntries(scope)).find((entry) => entry.steering?.invariant.accepted === true && (entry.idempotencyKey === key || entry.steering.idempotencyKey === key || entry.steering.promptSignature === key));
if (prior?.steering !== undefined) return { plan, accepted: true, audit: { ...prior.steering, deduped: true }, rejectedReasons: [], deduped: true };
const audit = validateUltragoalSteeringProposal(plan, proposal);
const accepted = audit.invariant.accepted;
const next = accepted ? applySteeringMutation(plan, proposal, audit) : plan;
const finalAudit: UltragoalSteeringAudit = { ...audit, before: plan };
if (accepted) finalAudit.after = next;
if (accepted) await writePlan(scope, next);
await appendLedger(scope, ledgerEntry(proposal, finalAudit, proposal.now?.toISOString() ?? iso()));
return { plan: next, accepted, audit: finalAudit, rejectedReasons: audit.invariant.rejectedReasons, deduped: false };
});
}
function ledgerEntry(proposal: UltragoalSteeringProposal, audit: UltragoalSteeringAudit, at: string): UltragoalLedgerEntry {
const entry: UltragoalLedgerEntry = { at, kind: audit.invariant.accepted ? (proposal.kind === "revise_criterion" ? "criteria_revised" : "steering_accepted") : "steering_rejected", evidence: proposal.evidence, message: proposal.rationale, steering: audit, mutationKind: proposal.kind };
const goalId = audit.targetGoalIds[0];
if (goalId !== undefined) entry.goalId = goalId;
if (proposal.criterionId !== undefined) entry.criterionId = proposal.criterionId;
if (proposal.idempotencyKey !== undefined) entry.idempotencyKey = proposal.idempotencyKey;
if (audit.before !== undefined) entry.before = audit.before;
if (audit.after !== undefined) entry.after = audit.after;
return entry;
}
@@ -0,0 +1,327 @@
export const ULTRAGOAL_DIR = ".omo/ultragoal";
export const ULTRAGOAL_SESSIONS = "sessions";
export const ULTRAGOAL_INDEX = "index.json";
export const ULTRAGOAL_BRIEF = "brief.md";
export const ULTRAGOAL_GOALS = "goals.json";
export const ULTRAGOAL_LEDGER = "ledger.jsonl";
export const ULTRAGOAL_PLATFORM = "claude" as const;
export type UltragoalPlatform = typeof ULTRAGOAL_PLATFORM;
export type UltragoalStatus =
| "pending"
| "in_progress"
| "complete"
| "failed"
| "blocked"
| "review_blocked"
| "needs_user_decision";
export type UltragoalGoalMode = "aggregate" | "per_story";
/** @deprecated retained as an alias for back-compat; use {@link UltragoalGoalMode}. */
export type UltragoalCodexGoalMode = UltragoalGoalMode;
export type UltragoalSteeringStatus = "superseded" | "blocked";
export const ULTRAGOAL_STEERING_MUTATION_KINDS = [
"add_subgoal",
"split_subgoal",
"reorder_pending",
"revise_pending_wording",
"revise_criterion",
"annotate_ledger",
"mark_blocked_superseded",
] as const satisfies readonly string[];
export type UltragoalSteeringMutationKind = (typeof ULTRAGOAL_STEERING_MUTATION_KINDS)[number];
export type UltragoalSteeringSource = "user_prompt_submit" | "finding" | "cli";
export const ULTRAGOAL_SUCCESS_CRITERION_USER_MODELS = [
"happy",
"edge",
"regression",
"adversarial",
] as const satisfies readonly string[];
export type UltragoalSuccessCriterionUserModel = (typeof ULTRAGOAL_SUCCESS_CRITERION_USER_MODELS)[number];
export const ULTRAGOAL_CRITERION_STATUSES = ["pending", "pass", "fail", "blocked"] as const satisfies readonly string[];
export type UltragoalCriterionStatus = (typeof ULTRAGOAL_CRITERION_STATUSES)[number];
export const ULTRAGOAL_LEDGER_EVENT_KINDS = [
"plan_created",
"goal_started",
"goal_resumed",
"goal_completed",
"goal_blocked",
"goal_failed",
"goal_needs_user_decision",
"goal_retried",
"aggregate_completed",
"aggregate_objective_migrated",
"plan_migrated_to_session",
"goal_added",
"steering_accepted",
"steering_rejected",
"final_review_failed",
"goal_review_blocked",
"evidence_captured",
"criterion_failed",
"criterion_blocked",
"criteria_revised",
] as const satisfies readonly string[];
export type UltragoalLedgerEventKind = (typeof ULTRAGOAL_LEDGER_EVENT_KINDS)[number];
export interface UltragoalSuccessCriterion {
readonly id: string;
readonly scenario: string;
readonly userModel: UltragoalSuccessCriterionUserModel;
readonly expectedEvidence: string;
capturedEvidence: string | null;
status: UltragoalCriterionStatus;
capturedAt?: string;
notes?: string;
}
export interface UltragoalSteeringInvariantResult {
accepted: boolean;
structuralInvariantAccepted: boolean;
evidenceBackedNecessity: boolean;
noEasierCompletion: boolean;
rejectedReasons: string[];
reasons?: string[];
}
export interface UltragoalSteeringChildGoal {
title: string;
objective: string;
}
export interface UltragoalSteeringAfterPayload {
title?: string;
objective?: string;
pendingGoalIds?: string[];
children?: UltragoalSteeringChildGoal[];
}
export interface UltragoalSteeringProposal {
kind: UltragoalSteeringMutationKind;
source: UltragoalSteeringSource;
targetGoalId?: string;
targetGoalIds?: string[];
criterionId?: string;
evidence: string;
rationale: string;
title?: string;
objective?: string;
childGoals?: UltragoalSteeringChildGoal[];
revisedTitle?: string;
revisedObjective?: string;
pendingOrder?: string[];
blockedReason?: string;
after?: UltragoalSteeringAfterPayload;
directiveText?: string;
promptSignature?: string;
idempotencyKey?: string;
now?: Date;
}
export interface UltragoalSteeringAudit {
kind: UltragoalSteeringMutationKind;
source: UltragoalSteeringSource;
targetGoalIds: string[];
criterionId?: string;
before?: unknown;
after?: unknown;
evidence: string;
rationale: string;
invariant: UltragoalSteeringInvariantResult;
directiveText?: string;
promptSignature?: string;
idempotencyKey?: string;
deduped?: boolean;
}
export interface SteerUltragoalResult {
plan: UltragoalPlan;
accepted: boolean;
audit: UltragoalSteeringAudit;
rejectedReasons: string[];
deduped: boolean;
}
export interface UltragoalItem {
id: string;
title: string;
objective: string;
status: UltragoalStatus;
successCriteria: UltragoalSuccessCriterion[];
attempt: number;
createdAt: string;
updatedAt: string;
startedAt?: string;
completedAt?: string;
failedAt?: string;
reviewBlockedAt?: string;
evidence?: string;
failureReason?: string;
steeringStatus?: UltragoalSteeringStatus;
supersededBy?: string[];
supersedes?: string[];
blockedReason?: string;
blockerSignature?: string;
blockerOccurrenceCount?: number;
requiredExternalDecision?: string;
nonRetriable?: boolean;
steeringEvidence?: string;
steeringRationale?: string;
}
export interface UltragoalAggregateCompletion {
status: "complete";
completedAt: string;
evidence: string;
goalSnapshot?: unknown;
}
/** Current durable plan schema version. */
export const ULTRAGOAL_PLAN_VERSION = 2 as const;
export interface UltragoalPlan {
version: 2;
platform: UltragoalPlatform;
sessionId: string;
sessionScope: string;
createdAt: string;
updatedAt: string;
briefPath: string;
goalsPath: string;
ledgerPath: string;
goalMode?: UltragoalGoalMode;
objective?: string;
objectiveAliases?: string[];
aggregateCompletion?: UltragoalAggregateCompletion;
activeGoalId?: string;
goals: UltragoalItem[];
}
/** A single registered session inside the per-repo {@link UltragoalIndex}. */
export interface UltragoalIndexEntry {
sessionId: string;
sessionScope: string;
platform: UltragoalPlatform;
createdAt: string;
updatedAt: string;
goalsPath: string;
}
/** Per-repo registry of every session that owns ultragoal content. */
export interface UltragoalIndex {
version: 2;
sessions: UltragoalIndexEntry[];
}
/**
* Legacy repo-level v1 plan, read-only for forward migration (D3). The v1 file
* is never written or deleted; it is read once and migrated into a session
* scope.
*/
export interface UltragoalLegacyPlanV1 {
version: 1;
createdAt: string;
updatedAt: string;
briefPath: string;
goalsPath: string;
ledgerPath: string;
codexGoalMode?: UltragoalGoalMode;
codexObjective?: string;
codexObjectiveAliases?: string[];
aggregateCompletion?: { status: "complete"; completedAt: string; evidence: string; codexGoal?: unknown };
activeGoalId?: string;
goals: UltragoalItem[];
}
export interface UltragoalLedgerEntry {
at: string;
kind: UltragoalLedgerEventKind;
goalId?: string;
criterionId?: string;
status?: UltragoalStatus;
criterionStatus?: UltragoalCriterionStatus;
message?: string;
goalSnapshot?: unknown;
evidence?: string;
capturedEvidence?: string;
qualityGate?: UltragoalQualityGate;
steering?: UltragoalSteeringAudit;
before?: unknown;
after?: unknown;
mutationKind?: UltragoalSteeringMutationKind;
idempotencyKey?: string;
blockerSignature?: string;
blockerOccurrenceCount?: number;
requiredExternalDecision?: string;
}
export interface CreateUltragoalOptions {
brief: string;
goals?: Array<{ title?: string; objective: string }>;
goalMode?: UltragoalGoalMode;
now?: Date;
force?: boolean;
}
export interface StartNextOptions {
now?: Date;
retryFailed?: boolean;
}
export interface CheckpointOptions {
goalId: string;
status: Extract<UltragoalStatus, "complete" | "failed"> | "blocked";
evidence?: string;
goalSnapshot?: unknown;
qualityGate?: unknown;
allowActiveFinalGoalSnapshot?: boolean;
now?: Date;
}
export interface AddUltragoalGoalOptions {
title: string;
objective: string;
evidence?: string;
now?: Date;
}
export interface RecordFinalReviewBlockersOptions extends AddUltragoalGoalOptions {
goalId: string;
goalSnapshot?: unknown;
}
export interface UltragoalQualityGate {
aiSlopCleaner: { status: "passed"; evidence: string };
verification: { status: "passed"; commands: string[]; evidence: string };
codeReview: { recommendation: "APPROVE"; architectStatus: "CLEAR"; evidence: string };
}
export interface UltragoalErrorOptions {
readonly cause?: unknown;
readonly details?: Record<string, unknown>;
}
export class UltragoalError extends Error {
readonly code: string;
readonly details?: Record<string, unknown>;
constructor(message: string, code: string, opts?: UltragoalErrorOptions) {
super(message, opts?.cause === undefined ? undefined : { cause: opts.cause });
this.name = "UltragoalError";
this.code = code;
if (opts?.details !== undefined) {
this.details = opts.details;
}
}
}
export function iso(): string {
return new Date().toISOString();
}
@@ -0,0 +1,109 @@
import { mkdtemp, readFile, readdir } from "node:fs/promises";
import { existsSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { Readable, Writable } from "node:stream";
import { describe, expect, it } from "bun:test";
import {
applyPreToolUseGoalBudgetGuard,
type PreToolUsePayload,
parsePreToolUsePayload,
runUltragoalHookCli,
type UserPromptSubmitPayload,
} from "../src/claude-hook.js";
import { makeUltragoalScope } from "../src/session-scope.js";
import { createUltragoalPlan } from "../src/plan-crud.js";
function captureStdout(): { readonly stdout: Writable; readonly read: () => string } {
let captured = "";
const stdout = new Writable({
write(chunk: unknown, _encoding: BufferEncoding, callback: (error?: Error | null) => void): void {
captured += chunk instanceof Buffer ? chunk.toString() : String(chunk);
callback();
},
});
return { stdout, read: () => captured };
}
function upsPayload(prompt: string, cwd: string, sessionId: string): UserPromptSubmitPayload {
return { cwd, hook_event_name: "UserPromptSubmit", prompt, session_id: sessionId };
}
const STEER =
'OMO_ULTRAGOAL_STEER: {"kind":"annotate_ledger","source":"user_prompt_submit","evidence":"x","rationale":"y"}';
describe("UserPromptSubmit hook derives scope from session_id", () => {
it("two UserPromptSubmit payloads with distinct session_ids create two sessions/claude-* dirs", async () => {
const repoRoot = await mkdtemp(join(tmpdir(), "ug-hookscope-"));
// Seed plans for two distinct sessions so steering has something to mutate.
await createUltragoalPlan(makeUltragoalScope(repoRoot, "hook-a"), {
brief: "- objective alpha for hook test\n",
});
await createUltragoalPlan(makeUltragoalScope(repoRoot, "hook-b"), {
brief: "- objective beta for hook test\n",
});
for (const sid of ["hook-a", "hook-b"]) {
const stdin = Readable.from([JSON.stringify(upsPayload(STEER, repoRoot, sid))]);
const cap = captureStdout();
await runUltragoalHookCli(stdin, cap.stdout);
}
const sessionsRoot = join(repoRoot, ".omo", "ultragoal", "sessions");
expect(existsSync(sessionsRoot)).toBe(true);
const dirs = (await readdir(sessionsRoot)).filter((d) => d.startsWith("claude-"));
expect(dirs.length).toBe(2);
});
it("steering is a no-op (returns empty) when no plan exists for the session", async () => {
const repoRoot = await mkdtemp(join(tmpdir(), "ug-hooknoplan-"));
const stdin = Readable.from([JSON.stringify(upsPayload(STEER, repoRoot, "no-plan-session"))]);
const cap = captureStdout();
await runUltragoalHookCli(stdin, cap.stdout);
expect(cap.read()).toBe("");
});
});
function preToolPayload(toolName: string, toolInput: unknown): PreToolUsePayload {
return {
cwd: "/repo",
hook_event_name: "PreToolUse",
session_id: "s1",
tool_input: toolInput,
tool_name: toolName,
tool_use_id: "call-1",
transcript_path: null,
};
}
describe("inert create_goal guard (D4: code kept, registration removed)", () => {
it("parses a PreToolUse payload WITHOUT model/turn_id (relaxed validator)", () => {
const raw = JSON.stringify(preToolPayload("create_goal", { objective: "Ship", token_budget: 5 }));
const parsed = parsePreToolUsePayload(raw);
expect(parsed).not.toBeNull();
expect(parsed?.tool_name).toBe("create_goal");
});
it("guard still blocks a budgeted create_goal when invoked directly (code remains unit-testable)", () => {
const out = applyPreToolUseGoalBudgetGuard(
preToolPayload("create_goal", { objective: "Ship", token_budget: 5 }),
);
const parsed = JSON.parse(out);
expect(parsed.hookSpecificOutput.permissionDecision).toBe("deny");
});
it("guard returns empty for create_goal without a budget", () => {
expect(applyPreToolUseGoalBudgetGuard(preToolPayload("create_goal", { objective: "Ship" }))).toBe("");
});
it("hooks.json does NOT register a PreToolUse create_goal block", async () => {
const hooks = JSON.parse(await readFile(join(import.meta.dir, "..", "hooks", "hooks.json"), "utf8"));
expect(hooks.hooks.PreToolUse).toBeUndefined();
// Only UserPromptSubmit is registered.
expect(Object.keys(hooks.hooks)).toEqual(["UserPromptSubmit"]);
const raw = await readFile(join(import.meta.dir, "..", "hooks", "hooks.json"), "utf8");
expect(raw).not.toContain("create_goal");
expect(raw).not.toContain("pre-tool-use");
});
});
@@ -0,0 +1,60 @@
import { mkdtemp } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, beforeEach, describe, expect, it } from "bun:test";
import { resolveUltragoalScope } from "../src/cli-commands.js";
import { makeUltragoalScope } from "../src/session-scope.js";
import { createUltragoalPlan } from "../src/plan-crud.js";
import { UltragoalError } from "../src/types.js";
const BRIEF = "- a goal objective for scope resolution\n";
let savedEnv: string | undefined;
beforeEach(() => {
savedEnv = process.env["CLAUDE_SESSION_ID"];
delete process.env["CLAUDE_SESSION_ID"];
});
afterEach(() => {
if (savedEnv === undefined) delete process.env["CLAUDE_SESSION_ID"];
else process.env["CLAUDE_SESSION_ID"] = savedEnv;
});
describe("resolveUltragoalScope precedence", () => {
it("1. --session-id flag wins", async () => {
const repoRoot = await mkdtemp(join(tmpdir(), "ug-cli-flag-"));
process.env["CLAUDE_SESSION_ID"] = "env-session";
const scope = await resolveUltragoalScope(repoRoot, ["--session-id", "flag-session"]);
expect(scope.sessionId).toBe("claude:flag-session");
});
it("2. $CLAUDE_SESSION_ID is used when no flag", async () => {
const repoRoot = await mkdtemp(join(tmpdir(), "ug-cli-env-"));
process.env["CLAUDE_SESSION_ID"] = "env-session";
const scope = await resolveUltragoalScope(repoRoot, []);
expect(scope.sessionId).toBe("claude:env-session");
});
it("3. newest-active session in index.json when no flag/env", async () => {
const repoRoot = await mkdtemp(join(tmpdir(), "ug-cli-idx-"));
await createUltragoalPlan(makeUltragoalScope(repoRoot, "first"), { brief: BRIEF });
await createUltragoalPlan(makeUltragoalScope(repoRoot, "second"), { brief: BRIEF });
const scope = await resolveUltragoalScope(repoRoot, []);
// "second" was created last -> newest active.
expect(scope.sessionId).toBe("claude:second");
});
it("4. errors with ULTRAGOAL_SESSION_REQUIRED when nothing resolves", async () => {
const repoRoot = await mkdtemp(join(tmpdir(), "ug-cli-none-"));
let caught: unknown;
try {
await resolveUltragoalScope(repoRoot, []);
} catch (error) {
caught = error;
}
expect(caught).toBeInstanceOf(UltragoalError);
expect((caught as UltragoalError).code).toBe("ULTRAGOAL_SESSION_REQUIRED");
});
});
@@ -0,0 +1,87 @@
import { mkdtemp, readFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { describe, expect, it } from "bun:test";
import { makeUltragoalScope } from "../src/session-scope.js";
import { createUltragoalPlan, startNextUltragoal } from "../src/plan-crud.js";
import { checkpointUltragoal } from "../src/checkpoint.js";
import { recordEvidence } from "../src/evidence.js";
import { steerUltragoal } from "../src/steering.js";
import { buildGoalInstruction } from "../src/goal-instruction.js";
import { readUltragoalPlan } from "../src/plan-io.js";
import { ultragoalLedgerPath } from "../src/paths.js";
const BRIEF = "- Implement the alpha feature\n- Implement the beta feature\n";
async function tmpRepo(): Promise<string> {
return mkdtemp(join(tmpdir(), "ug-life-"));
}
describe("scope-threaded lifecycle still works end-to-end", () => {
it("create -> complete-goals handoff -> record-evidence -> checkpoint (per_story)", async () => {
const repoRoot = await tmpRepo();
const scope = makeUltragoalScope(repoRoot, "life-1");
const plan = await createUltragoalPlan(scope, { brief: BRIEF, goalMode: "per_story" });
expect(plan.goals.length).toBe(2);
const started = await startNextUltragoal(scope, {});
expect("goal" in started).toBe(true);
if (!("goal" in started)) throw new Error("expected a goal");
const goal = started.goal;
// handoff text is file/steering based, no create_goal/get_goal language.
const instruction = buildGoalInstruction({ plan: started.plan, goal });
expect(instruction.text).not.toContain("create_goal");
expect(instruction.text).not.toContain("get_goal");
expect(instruction.text).toContain(started.plan.goalsPath);
// pass all seeded criteria
for (const c of goal.successCriteria) {
await recordEvidence(scope, { goalId: goal.id, criterionId: c.id, status: "pass", evidence: "verified ok" });
}
// checkpoint complete WITHOUT a goal snapshot (snapshot is optional now)
const result = await checkpointUltragoal(scope, {
goalId: goal.id,
status: "complete",
evidence: "alpha done; tests pass; review clean",
});
expect(result.goal.status).toBe("complete");
const reread = await readUltragoalPlan(scope);
expect(reread.goals.find((g) => g.id === goal.id)?.status).toBe("complete");
});
it("steering adds a subgoal and is written under the session scope", async () => {
const repoRoot = await tmpRepo();
const scope = makeUltragoalScope(repoRoot, "life-2");
await createUltragoalPlan(scope, { brief: BRIEF });
const result = await steerUltragoal(scope, {
kind: "add_subgoal",
source: "cli",
title: "Gamma feature",
objective: "Implement the gamma feature with care",
evidence: "user asked for gamma",
rationale: "newly discovered requirement",
});
expect(result.accepted).toBe(true);
const reread = await readUltragoalPlan(scope);
expect(reread.goals.some((g) => g.title === "Gamma feature")).toBe(true);
// ledger lives in the session scope
const ledger = await readFile(ultragoalLedgerPath(scope), "utf8");
expect(ledger).toContain("steering_accepted");
});
it("steering is a no-op when the plan is missing for the scope", async () => {
const repoRoot = await tmpRepo();
const scope = makeUltragoalScope(repoRoot, "life-noplan");
await expect(
steerUltragoal(scope, {
kind: "annotate_ledger",
source: "cli",
evidence: "x",
rationale: "y",
}),
).rejects.toThrow();
});
});
@@ -0,0 +1,108 @@
import { mkdtemp, readFile, writeFile, mkdir } from "node:fs/promises";
import { existsSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { describe, expect, it } from "bun:test";
import { makeUltragoalScope } from "../src/session-scope.js";
import { createUltragoalPlan } from "../src/plan-crud.js";
import { readUltragoalPlan, writePlan, readUltragoalIndex } from "../src/plan-io.js";
import { ultragoalGoalsPath, ultragoalSessionDir, ultragoalIndexPath, legacyUltragoalGoalsPath } from "../src/paths.js";
const BRIEF = "- First goal objective for testing\n- Second goal objective for testing\n";
async function tmpRepo(): Promise<string> {
return mkdtemp(join(tmpdir(), "ug-session-"));
}
describe("per-session isolation", () => {
it("two distinct session ids produce two isolated plans under sessions/claude-*/", async () => {
const repoRoot = await tmpRepo();
const scopeA = makeUltragoalScope(repoRoot, "session-aaa");
const scopeB = makeUltragoalScope(repoRoot, "session-bbb");
await createUltragoalPlan(scopeA, { brief: BRIEF });
await createUltragoalPlan(scopeB, { brief: BRIEF });
expect(existsSync(ultragoalGoalsPath(scopeA))).toBe(true);
expect(existsSync(ultragoalGoalsPath(scopeB))).toBe(true);
expect(ultragoalSessionDir(scopeA)).toContain("claude-session-aaa");
expect(ultragoalSessionDir(scopeB)).toContain("claude-session-bbb");
expect(ultragoalGoalsPath(scopeA)).not.toBe(ultragoalGoalsPath(scopeB));
// goal content lives under ./.omo/ultragoal/sessions/
expect(ultragoalSessionDir(scopeA)).toContain(join(".omo", "ultragoal", "sessions"));
});
it("registers both sessions in the index.json registry", async () => {
const repoRoot = await tmpRepo();
const scopeA = makeUltragoalScope(repoRoot, "idx-aaa");
const scopeB = makeUltragoalScope(repoRoot, "idx-bbb");
await createUltragoalPlan(scopeA, { brief: BRIEF });
await createUltragoalPlan(scopeB, { brief: BRIEF });
expect(existsSync(ultragoalIndexPath(repoRoot))).toBe(true);
const index = await readUltragoalIndex(repoRoot);
const ids = index.sessions.map((s) => s.sessionId);
expect(ids).toContain("claude:idx-aaa");
expect(ids).toContain("claude:idx-bbb");
});
it("writes version:2 plans with platform/sessionId/sessionScope", async () => {
const repoRoot = await tmpRepo();
const scope = makeUltragoalScope(repoRoot, "v2-check");
const plan = await createUltragoalPlan(scope, { brief: BRIEF });
expect(plan.version).toBe(2);
expect(plan.platform).toBe("claude");
expect(plan.sessionId).toBe("claude:v2-check");
const reread = await readUltragoalPlan(scope);
expect(reread.version).toBe(2);
expect(reread.goals.length).toBe(2);
});
});
describe("v1 -> v2 migration", () => {
it("reads a legacy v1 plan, migrates it forward, and does NOT delete the v1 file", async () => {
const repoRoot = await tmpRepo();
const scope = makeUltragoalScope(repoRoot, "legacy-1");
// Author a legacy v1 plan at the OLD repo-level path.
const legacyPath = legacyUltragoalGoalsPath(repoRoot);
await mkdir(join(repoRoot, ".omo", "ultragoal"), { recursive: true });
const v1Plan = {
version: 1,
createdAt: "2026-01-01T00:00:00.000Z",
updatedAt: "2026-01-01T00:00:00.000Z",
briefPath: ".omo/ultragoal/brief.md",
goalsPath: ".omo/ultragoal/goals.json",
ledgerPath: ".omo/ultragoal/ledger.jsonl",
codexGoalMode: "aggregate",
goals: [
{
id: "G001",
title: "Legacy goal",
objective: "Do the legacy thing.",
status: "pending",
successCriteria: [],
attempt: 0,
createdAt: "2026-01-01T00:00:00.000Z",
updatedAt: "2026-01-01T00:00:00.000Z",
},
],
};
await writeFile(legacyPath, `${JSON.stringify(v1Plan, null, 2)}\n`, "utf8");
const migrated = await readUltragoalPlan(scope);
expect(migrated.version).toBe(2);
expect(migrated.goals[0]?.id).toBe("G001");
expect(migrated.goalMode).toBe("aggregate");
// The legacy v1 file must still exist (read-only migration).
expect(existsSync(legacyPath)).toBe(true);
const stillV1 = JSON.parse(await readFile(legacyPath, "utf8"));
expect(stillV1.version).toBe(1);
// And the migrated copy lives in the session scope dir.
expect(existsSync(ultragoalGoalsPath(scope))).toBe(true);
});
});
@@ -0,0 +1,59 @@
import { describe, expect, it } from "bun:test";
import {
CLAUDE_SESSION_PREFIX,
makeUltragoalScope,
normalizeClaudeSessionId,
PREFIX_RE,
sessionScopeDir,
} from "../src/session-scope.js";
describe("normalizeClaudeSessionId", () => {
it("applies the claude: prefix to a bare session id", () => {
expect(normalizeClaudeSessionId("abc-123")).toBe("claude:abc-123");
});
it("leaves an already-claude-prefixed id untouched", () => {
expect(normalizeClaudeSessionId("claude:abc-123")).toBe("claude:abc-123");
});
it("leaves a sibling-platform prefix untouched", () => {
expect(normalizeClaudeSessionId("codex:xyz")).toBe("codex:xyz");
expect(normalizeClaudeSessionId("opencode:xyz")).toBe("opencode:xyz");
});
it("throws on an empty session id", () => {
expect(() => normalizeClaudeSessionId(" ")).toThrow();
});
});
describe("sessionScopeDir", () => {
it("converts the prefix colon to a dash", () => {
expect(sessionScopeDir("abc-123")).toBe("claude-abc-123");
});
it("sanitizes path-hostile characters", () => {
expect(sessionScopeDir("claude:abc/def")).toBe("claude-abc-def");
});
it("is deterministic for the same id", () => {
expect(sessionScopeDir("s1")).toBe(sessionScopeDir("claude:s1"));
});
});
describe("makeUltragoalScope", () => {
it("builds a struct with repoRoot, prefixed sessionId, and scope dir", () => {
const scope = makeUltragoalScope("/repo", "s1");
expect(scope.repoRoot).toBe("/repo");
expect(scope.sessionId).toBe("claude:s1");
expect(scope.sessionScope).toBe("claude-s1");
});
});
describe("PREFIX_RE / constants", () => {
it("recognizes the claude prefix", () => {
expect(PREFIX_RE.test("claude:x")).toBe(true);
expect(PREFIX_RE.test("nope:x")).toBe(false);
expect(CLAUDE_SESSION_PREFIX).toBe("claude:");
});
});
@@ -0,0 +1,12 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"allowImportingTsExtensions": false,
"declaration": true,
"outDir": "dist",
"rootDir": "src",
"noEmit": false
},
"include": ["src/**/*"],
"exclude": ["test/**/*"]
}
@@ -0,0 +1,27 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"lib": ["ES2022"],
"strict": true,
"exactOptionalPropertyTypes": true,
"noUncheckedIndexedAccess": true,
"noPropertyAccessFromIndexSignature": true,
"verbatimModuleSyntax": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"esModuleInterop": true,
"allowImportingTsExtensions": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"useDefineForClassFields": false,
"types": ["node"],
"noEmit": true
},
"include": ["src/**/*", "test/**/*", "vitest.config.ts"]
}
@@ -39,9 +39,12 @@ export const HANDLED_COMPONENTS = [
"comment-checker",
"lsp",
"ultrawork",
"ultragoal",
"start-work-continuation",
];
// NOTE: `ultragoal` is intentionally NOT sync-managed. It is a deep hand-fork
// (per-session `.omo/ultragoal` refactor, `claude:` session scoping, inert
// create_goal guard) that diverges from the omo-codex source, so it is
// maintained directly in this tree rather than regenerated from a patch manifest.
// Per-component copy surface. Directories are copied recursively; files are
// copied verbatim. Entries that do not exist in the source are skipped.