Skip to content

fix: keep leftover recipe history envelope after a mid-put crash - #1310

Merged
xiaofei-zheng merged 3 commits into
mainfrom
bugfix/siliang/recipe-skip-stale-history-overwrite
Aug 31, 2026
Merged

fix: keep leftover recipe history envelope after a mid-put crash#1310
xiaofei-zheng merged 3 commits into
mainfrom
bugfix/siliang/recipe-skip-stale-history-overwrite

Conversation

@siliangchen-amd

@siliangchen-amd siliangchen-amd commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Description

put_recipe first writes an archive copy of the current live row at version N, then replaces the live row with N+1: two independent atomic writes. A crash between those two renames leaves the live row at N while the archive for N already exists. The next successful put used to overwrite that archive — the same snapshot, but a fresh replaced_by / archived_at envelope, discarding the provenance recorded by the interrupted write.
If the archive for the current live version already holds that same snapshot, it is now left alone and only the live row advances. This is not a two-phase journal: a crash still drops the in-flight N+1 payload.

Linked issue(s)

None.

Tests

  • An archive envelope left behind for the current live version survives the next successful put, and the live row still advances
    50 passed on the targeted tests. ruff check / ruff format --check on the changed files passed.

Breaking changes

No. Archive envelopes that already match the live row are no longer rewritten.

If history/v{N}.json already snapshots the current live row, the
previous put died between the two renames. Do not overwrite that
archive on the next successful write; only advance live.

Co-authored-by: Cursor <cursoragent@cursor.com>
@siliangchen-amd
siliangchen-amd requested a review from a team as a code owner August 27, 2026 09:25
@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown

CI E2E report — ❌ Failed

item value
result ❌ Failed
model Qwen/Qwen3-0.6B (dense)
resources 1× GPU, TP=1
PR branch bugfix/siliang/recipe-skip-stale-history-overwrite
commit e08329933d50db11388373b6f125021766ce88fc
session_id 8c1b40d6-0bda-4951-be5a-d13f63db29dc
queue → dispatch 399m 46s
run time 135m 0s
total 534m 46s

details

siliangchen-amd and others added 2 commits August 31, 2026 06:25
The skip-overwrite guard must prove the leftover archive snapshots
current live. An unreadable file cannot prove that, so fall through
to the overwrite path instead of raising LocalRecipeStoreError,
which would leave the canonical_id permanently unwritable.

Co-authored-by: Cursor <cursoragent@cursor.com>
@siliangchen-amd

Copy link
Copy Markdown
Contributor Author

Follow-up on review: two notes, one code change.

  1. Agreed — that was a regression this PR introduced. The skip-overwrite guard is only valid if we can prove the leftover archive is a snapshot of current live. An unreadable file cannot prove that, so it should rewrite, not abort. _read_json raising LocalRecipeStoreError left live at N, and the next put hit the same bad file again, so that canonical_id could never be written. main never read this file, so a corrupt archive was overwritten and self-healed. The read is now in try/except LocalRecipeStoreError: log a warning, set existing_archive = None, and fall through to the original overwrite path. Truncated JSON and a non-dict archive no longer block a later put.

  2. replaced_by is a provenance dict (source / generator / generated_at / details), not a version pointer, so it does not claim replacement by a version that never landed. The completing write’s provenance is on the live row. The docstring now says the kept envelope records the write that created that archive (which may not have finished). I am not turning replaced_by into an array or adding superseded_by: that would change the on-disk shape every audit consumer reads, for a distinction that only appears in the crash-residue case, and it belongs with the deferred two-phase journal.

@xiaofei-zheng
xiaofei-zheng merged commit a73d262 into main Aug 31, 2026
28 of 30 checks passed
@xiaofei-zheng
xiaofei-zheng deleted the bugfix/siliang/recipe-skip-stale-history-overwrite branch August 31, 2026 07:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants