2026-05-25 22:24:37 +09:00
import type {
2026-05-29 12:38:22 +09:00
UlwLoopCodexGoalMode ,
UlwLoopItem ,
UlwLoopPlan ,
UlwLoopStatus ,
UlwLoopSuccessCriterion ,
2026-05-25 22:24:37 +09:00
} from "./types.js" ;
2026-05-29 12:38:22 +09:00
export const ULW_LOOP_AGGREGATE_CODEX_OBJECTIVE : string =
"Complete the durable ulw-loop plan in .omo/ulw-loop/goals.json, including later accepted/appended stories, under the original brief constraints; use .omo/ulw-loop/ledger.jsonl as the audit trail." ;
2026-05-25 22:24:37 +09:00
2026-05-29 12:38:22 +09:00
export function codexGoalMode ( plan : UlwLoopPlan ) : UlwLoopCodexGoalMode {
2026-05-25 22:24:37 +09:00
return plan . codexGoalMode ? ? "per_story" ;
}
2026-05-29 12:38:22 +09:00
function isResolvedStatus ( status : UlwLoopStatus ) : boolean {
2026-05-25 22:24:37 +09:00
return status === "complete" ;
}
2026-05-29 12:38:22 +09:00
function isSupersededResolved ( goal : UlwLoopItem , plan : UlwLoopPlan ) : boolean {
2026-05-25 22:24:37 +09:00
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 ) ;
} ) ;
}
2026-05-29 12:38:22 +09:00
function isCompletionBlocking ( goal : UlwLoopItem , plan : UlwLoopPlan ) : boolean {
2026-05-25 22:24:37 +09:00
if ( goal . steeringStatus === "superseded" ) return ! isSupersededResolved ( goal , plan ) ;
if ( goal . steeringStatus === "blocked" ) return true ;
return ! isResolvedStatus ( goal . status ) ;
}
function isCompletionBlockingForFinalCandidate (
2026-05-29 12:38:22 +09:00
candidate : UlwLoopItem ,
finalCandidate : UlwLoopItem ,
plan : UlwLoopPlan ,
2026-05-25 22:24:37 +09:00
) : 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 ) ;
}
2026-05-29 12:38:22 +09:00
export function isUlwLoopDone ( plan : UlwLoopPlan ) : boolean {
2026-05-25 22:24:37 +09:00
if ( plan . aggregateCompletion ? . status === "complete" ) return true ;
return plan . goals . every ( ( goal ) = > ! isCompletionBlocking ( goal , plan ) ) ;
}
2026-05-29 12:38:22 +09:00
export function isFinalRunCompletionCandidate ( plan : UlwLoopPlan , goal : UlwLoopItem ) : boolean {
2026-05-25 22:24:37 +09:00
return (
isCompletionBlocking ( goal , plan ) &&
plan . goals . every ( ( candidate ) = > ! isCompletionBlockingForFinalCandidate ( candidate , goal , plan ) )
) ;
}
2026-05-29 12:38:22 +09:00
export function aggregateCodexObjective ( plan : UlwLoopPlan ) : string {
return plan . codexObjective ? ? ULW_LOOP_AGGREGATE_CODEX_OBJECTIVE ;
2026-05-25 22:24:37 +09:00
}
2026-05-29 12:38:22 +09:00
export function expectedCodexObjective ( plan : UlwLoopPlan , goal : UlwLoopItem ) : string {
2026-05-25 22:24:37 +09:00
return codexGoalMode ( plan ) === "aggregate" ? aggregateCodexObjective ( plan ) : goal . objective ;
}
2026-05-29 12:38:22 +09:00
export function compatibleCodexObjectives ( plan : UlwLoopPlan ) : readonly string [ ] {
2026-05-25 22:24:37 +09:00
return [ aggregateCodexObjective ( plan ) , . . . ( plan . codexObjectiveAliases ? ? [ ] ) ] ;
}
2026-05-29 12:38:22 +09:00
export function hasAllCriteriaPass ( goal : UlwLoopItem ) : boolean {
2026-05-25 22:24:37 +09:00
return goal . successCriteria . length > 0 && goal . successCriteria . every ( ( criterion ) = > criterion . status === "pass" ) ;
}
2026-05-29 12:38:22 +09:00
export function firstUnresolvedCriterion ( goal : UlwLoopItem ) : UlwLoopSuccessCriterion | undefined {
2026-05-25 22:24:37 +09:00
return goal . successCriteria . find ( ( criterion ) = > criterion . status !== "pass" ) ;
}