Skip to content

fix(validation): stale_artifacts --delete removed fresh rebuilds along with the stale ones - #876

Open
WesIngwersen wants to merge 1 commit into
mainfrom
stale-artifacts-delete-exact-version
Open

fix(validation): stale_artifacts --delete removed fresh rebuilds along with the stale ones#876
WesIngwersen wants to merge 1 commit into
mainfrom
stale-artifacts-delete-exact-version

Conversation

@WesIngwersen

Copy link
Copy Markdown
Member

Follow-up to #867 / #874. Found by using the dry run that review asked for.

The bug

delete_stale matched f"{name}_v" against the bare name_data, so it deleted every version of an artifact. That is wrong exactly when it matters most: a rebuild sits beside the stale artifact it replaces and shares its name_data. The rebuild is not in problems — it is current, which is the point of having rebuilt it — but its filename starts with the same name, so the sweep removed the fix and kept nothing.

Net effect of a sweep in that state: you lose the good build and keep none, and the next run silently rebuilds from whatever is left.

Caught on a real sweep, not hypothetically

Regenerating UMD_GHGIA for 2022 and 2024 left 60 superseded artifacts flagged. The dry run listed 60 bare names — not one carrying the stale dcf7077 hash:

would delete UMD_GHGIA_T_2_S1_2022
would delete UMD_GHGIA_T_2_S1_2024

--delete --yes at that point would have taken the 60 fresh 99655e9 builds with them — the FBAs the Cornerstone GHG FBS series was mid-build on.

⚠️ Worth noting the dry run is the only reason this was visible. Without it the sweep would have run, and the damage would have surfaced later as a rebuild producing different numbers for no apparent reason.

The fix

find_stale had the versioned stem and threw it away by iterating .values(). It now iterates .items() and carries stem on every problem row; delete_stale matches that stem exactly.

A row without a stem deletes nothing — "I cannot tell which build" must not mean "all of them".

After the fix, the same sweep listed 60 stems all carrying the stale hash, ran, and left all 124 fresh files in place:

DRY RUN -- would delete 60 artifact(s) in scope 'extract'
  would delete UMD_GHGIA_T_2_S1_2022_v0.3.0_dcf7077
...
fresh 99655e9 UMD parquets before: 124
Deleted 120 file(s)
fresh after: 124   old dcf7077 remaining: 0

Testing

Four tests in a new test_stale_artifacts.py, since the failure mode is silent and expensive:

  • the flagged version is deleted and the rebuild beside it survives — the failure above
  • parquet and sidecar go together (leaving the parquet means the rebuild silently reloads it)
  • a row with no stem deletes nothing
  • every find_stale row carries a stem, so the contract holds at the producing end

Also stops the progress line raising ValueError on a scope outside the repo, which is what made the function untestable at all.

uv run pytest bedrock/utils/validation/__tests__/test_stale_artifacts.py -q  # 4 passed
uv run black --check . ; uv run ruff check . ; uv run mypy bedrock           # clean

🤖 Generated with Claude Code

…d alike

`--delete` matched `f"{name}_v"` against the bare `name_data`, so it took
every version of an artifact. That is wrong exactly when it matters most:
a rebuild sits beside the stale artifact it replaces and shares its name.
The rebuild is not in `problems` -- it is current, which is the point of
having rebuilt it -- but its filename starts with the same name, so the
sweep removed the fix and kept nothing.

Caught on a real sweep. 60 superseded UMD_GHGIA artifacts were flagged
after regenerating 2022 and 2024; the dry run added last week listed 60
bare names, none carrying the stale git hash, and `--delete --yes` would
have taken the 60 fresh builds with them -- the FBAs the GHG FBS series
was mid-build on.

find_stale now carries the versioned stem on every row (it had it and
threw it away by iterating .values()), and delete_stale matches that stem
exactly. A row without a stem deletes nothing: "I cannot tell which
build" must not mean "all of them". After the fix the same sweep listed
60 stems all carrying the stale hash, ran, and left all 124 fresh files
in place.

Four tests, since the failure mode is silent and expensive. Also stops
the progress line raising on a scope outside the repo, which is what
made the function untestable.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.

1 participant