fix(retry): lineage tracking + relative mission path resolution (#28)
- retryRun rewrites a snapshot's relative mission path to the run's own recorded mission.json (absolute) before execution — retries stay faithful to what originally ran - runTask accepts options.retriedFrom; retry records lineage in result.json (additive optional field, no schema break) - task suite +4 cases: retry succeeds, lineage recorded, mission section present after retry (36 total), missing-run retry exits 4 Closes #28
This commit is contained in:
+21
-2
@@ -234,7 +234,7 @@ function writeOnce(file, content) {
|
||||
}
|
||||
}
|
||||
|
||||
function runTask(taskFile) {
|
||||
function runTask(taskFile, options = {}) {
|
||||
const resolved = JSON.parse(
|
||||
spawnSync(process.execPath, [path.join(PROJECT_ROOT, "scripts", "mosaic-config.mjs"), "validate"], {
|
||||
cwd: PROJECT_ROOT,
|
||||
@@ -345,6 +345,7 @@ function runTask(taskFile) {
|
||||
request: task.prompt,
|
||||
response,
|
||||
expectedExact: expected,
|
||||
...(options.retriedFrom ? { retriedFrom: options.retriedFrom } : {}),
|
||||
workspace: task.workspace,
|
||||
tools: task.tools,
|
||||
session: task.session,
|
||||
@@ -455,6 +456,24 @@ function retryRun(runId) {
|
||||
fail(4, `run task snapshot unreadable: ${runId}`);
|
||||
}
|
||||
|
||||
// Relative mission paths in a snapshot resolve against the ORIGINAL task
|
||||
// location, which no longer exists here — rewrite them to the run's own
|
||||
// recorded mission.json so retries stay faithful.
|
||||
let snapshotDoc;
|
||||
try {
|
||||
snapshotDoc = JSON.parse(snapshot);
|
||||
} catch {
|
||||
fail(4, `run task snapshot is not valid JSON: ${runId}`);
|
||||
}
|
||||
if (snapshotDoc.mission && !path.isAbsolute(snapshotDoc.mission)) {
|
||||
const recordedMission = path.join(dir, "mission.json");
|
||||
if (!fs.existsSync(recordedMission)) {
|
||||
fail(4, `cannot retry ${runId}: relative mission path but no mission.json snapshot in run dir`);
|
||||
}
|
||||
snapshotDoc.mission = recordedMission;
|
||||
snapshot = `${JSON.stringify(snapshotDoc, null, 2)}\n`;
|
||||
}
|
||||
|
||||
// A retry is a brand-new run: replay the recorded task snapshot through
|
||||
// the ordinary run path; existing run records stay untouched.
|
||||
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "mosaic-retry-"));
|
||||
@@ -467,7 +486,7 @@ function retryRun(runId) {
|
||||
// Best-effort cleanup only.
|
||||
}
|
||||
});
|
||||
runTask(tempTaskFile);
|
||||
runTask(tempTaskFile, { retriedFrom: runId });
|
||||
}
|
||||
|
||||
const operation = process.argv[2];
|
||||
|
||||
@@ -142,6 +142,21 @@ EOF
|
||||
&& grep -q 'Seam directive A.' "$SANDBOX/data/system-prompt.md" \
|
||||
&& check "mission section injected into generated prompt" 0 \
|
||||
|| check "mission section injected into generated prompt" 1
|
||||
|
||||
# retry lineage + relative mission path resolution
|
||||
expect_exit "retry of mission run succeeds" 0 -- \
|
||||
env MOSAIC_CONFIG="$SANDBOX/mock-adapters.json" MOSAIC_MOCK_RESPONSE=MOCKED \
|
||||
node scripts/mosaic-task.mjs retry "$(ls -dt "$SANDBOX/data/runs"/r-* | head -1 | xargs basename)"
|
||||
RT="$(ls -dt "$SANDBOX/data/runs"/r-* | head -1 | xargs basename)"
|
||||
ORIG="$(ls -dt "$SANDBOX/data/runs"/r-* | sed -n 2p | xargs basename)"
|
||||
node -e 'const r=JSON.parse(require("fs").readFileSync(process.argv[1],"utf8"));process.exit(r.retriedFrom===process.argv[2]?0:1)' \
|
||||
"$SANDBOX/data/runs/$RT/result.json" "$ORIG" \
|
||||
&& check "retriedFrom lineage recorded" 0 || check "retriedFrom lineage recorded" 1
|
||||
grep -q 'MISSION (runtime)' "$SANDBOX/data/system-prompt.md" \
|
||||
&& check "mission section present after retry (relative path resolved)" 0 \
|
||||
|| check "mission section present after retry (relative path resolved)" 1
|
||||
expect_exit "retry of missing run exits 4" 4 -- \
|
||||
env MOSAIC_CONFIG="$SANDBOX/mock-adapters.json" node scripts/mosaic-task.mjs retry r-missing
|
||||
else
|
||||
echo "skip adapter seam cases (docker daemon unavailable)"
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user