Description
Verified by read-only audit (seal/gpt-5.6-sol subagent, verdict: CONFIRMED).
persistWorkflowJson writes artifacts as a multi-step sequence of individually-atomic writes:
extensions/workflows/artifacts.ts:154-165 — transcripts.json and optionally journal.json
artifacts.ts:167-172 — atomic replace of result.json
artifacts.ts:174-185 — compact manifest with resultArtifact: "result.json" written separately to workflow.json
writeFileAtomic (serialization.ts:147-161) is a temp-write + renameSync per file — no directory-level transaction.
Impact
Crash window: after the result.json rename succeeds and before the workflow.json rename succeeds, readers retain the prior manifest (typically running, without resultArtifact). Consequences:
hydrateRunArtifacts (dashboard.ts:102-114) reads result.json only when the manifest references it.
- Restart recovery (
index.ts:972-990) and status recovery (index.ts:2212-2226) are manifest-driven and never probe an unreferenced result.json.
- Stale-run recovery (
dashboard.ts:483-514) flips persisted running to uncertain without inspecting result.json.
- No GC, cleanup, or orphan-detection path exists — the terminal result stays orphaned indefinitely and the run is reported as
running/uncertain instead of terminal.
Test coverage gap
execute.e2e.test.ts:297-302 covers successful persistence and individual atomic replacement, but not failure/crash between the result.json and workflow.json writes.
Suggested fix
Commit a single manifest only after all dependent artifacts are durably prepared (e.g. transaction-like temp run directory + rename, or a manifest generation/commit marker). Additionally, recovery should detect and reconcile orphan result.json artifacts instead of trusting only the old manifest.
Related: #107, #108, #109 (same delivery/persistence subsystem).
Found by automated workflow audit (run wf_56028a5b018b).
Description
Verified by read-only audit (
seal/gpt-5.6-solsubagent, verdict: CONFIRMED).persistWorkflowJsonwrites artifacts as a multi-step sequence of individually-atomic writes:extensions/workflows/artifacts.ts:154-165—transcripts.jsonand optionallyjournal.jsonartifacts.ts:167-172— atomic replace ofresult.jsonartifacts.ts:174-185— compact manifest withresultArtifact: "result.json"written separately toworkflow.jsonwriteFileAtomic(serialization.ts:147-161) is a temp-write +renameSyncper file — no directory-level transaction.Impact
Crash window: after the
result.jsonrename succeeds and before theworkflow.jsonrename succeeds, readers retain the prior manifest (typicallyrunning, withoutresultArtifact). Consequences:hydrateRunArtifacts(dashboard.ts:102-114) readsresult.jsononly when the manifest references it.index.ts:972-990) and status recovery (index.ts:2212-2226) are manifest-driven and never probe an unreferencedresult.json.dashboard.ts:483-514) flips persistedrunningtouncertainwithout inspectingresult.json.running/uncertaininstead of terminal.Test coverage gap
execute.e2e.test.ts:297-302covers successful persistence and individual atomic replacement, but not failure/crash between theresult.jsonandworkflow.jsonwrites.Suggested fix
Commit a single manifest only after all dependent artifacts are durably prepared (e.g. transaction-like temp run directory + rename, or a manifest generation/commit marker). Additionally, recovery should detect and reconcile orphan
result.jsonartifacts instead of trusting only the old manifest.Related: #107, #108, #109 (same delivery/persistence subsystem).
Found by automated workflow audit (run
wf_56028a5b018b).