feat(planner,coder,moderator): integrate CAS for phase tracking

Phase 2 of #23:
- Planner schema compact: {hash, title} only, details stored via CAS CLI
- Planner prompt instructs agent to shell out `cas put` for each phase
- Coder prompt instructs agent to `cas get` for phase details, report hash
- Moderator compares hashes instead of names
- Removed COMPLETED_PHASE_SENTINELS — hash matching eliminates ambiguity

Refs #23
This commit is contained in:
2026-05-07 04:54:25 +00:00
parent 4b44665c7e
commit 341ff656dc
4 changed files with 49 additions and 99 deletions
@@ -3,28 +3,23 @@ import { END } from "@uncaged/workflow";
import type { SolveIssueMeta } from "./roles.js";
const COMPLETED_PHASE_SENTINELS = new Set(["all-done", "all_done", "complete"]);
function coderFinishedAllPlannedPhases(
phases: ReadonlyArray<{ name: string }>,
phases: ReadonlyArray<{ hash: string }>,
coderCompletedPhases: ReadonlyArray<string>,
): boolean {
if (phases.length === 0) {
return true;
}
const plannedNames = new Set(phases.map((p) => p.name));
const lastName = phases[phases.length - 1].name;
const explicit = new Set(coderCompletedPhases.filter((name) => plannedNames.has(name)));
if (phases.every((p) => explicit.has(p.name))) {
const plannedHashes = new Set(phases.map((p) => p.hash));
const lastHash = phases[phases.length - 1].hash;
const explicit = new Set(coderCompletedPhases.filter((h) => plannedHashes.has(h)));
if (phases.every((p) => explicit.has(p.hash))) {
return true;
}
// One-shot runs often report only the final phase; treat that as the full plan done.
if (coderCompletedPhases.some((name) => name === lastName)) {
if (coderCompletedPhases.some((h) => h === lastHash)) {
return true;
}
return coderCompletedPhases.some(
(name) => !plannedNames.has(name) && COMPLETED_PHASE_SENTINELS.has(name),
);
return false;
}
function nextAfterCoder(