fix(themes): stop archived theme pages from blocking their entity set - #710
Conversation
`_existing_theme_entity_sets` walked every page with no lifecycle filter, so an archived theme page kept contributing its entity frozenset and `propose_theme` treated that cluster as already taken after archive. archive is how an operator retires a wrong theme, and this made the retirement permanent for that entity cluster — the only way to re-synthesize it was hand-editing storage. same one-way trap compile's TAKEN TOPICS had (vouchdev#700). claims were already excluded through `_EXCLUDED_STATUSES`, which includes ARCHIVED; theme pages were not. skip archived pages when building the existing sets. pending proposals still count. awaiting review is not retirement, and two proposals for one cluster is the duplicate this guard exists to prevent.
|
read this alongside #709, #708, #698 and #697 — five PRs, one bug class, and the cluster is worth a word on its own. the fix here is right and the test set is the best of the five: the end-to-end case (propose → approve → confirm dedupe → archive → confirm the cluster resurfaces) is the one that actually proves the user-visible behaviour rather than just the predicate. holding pending proposals in the guard is also the correct call, and it is good that a test pins it. one cross-cutting note.
nothing blocking in this diff. |
|
diff coverage: n/a — this PR changes no python under |
closes #704
what
themes._existing_theme_entity_setswalkedstore.list_pages()with nolifecycle filter, so an archived theme page kept contributing its entity
frozenset and
propose_themetreated that cluster as already taken.Archive is how an operator retires a wrong theme — and this made the
retirement permanent for that entity cluster. The only way to re-synthesize it
was hand-editing storage. Same one-way trap compile's
TAKEN TOPICShad(#700).
Claims were already excluded here through
_EXCLUDED_STATUSES, which includesARCHIVED. Theme pages were not.the fix
Skip
PageStatus.ARCHIVEDwhen building the existing entity sets. Thetype == "theme"filter the issue also suggests was already in place, so thediff is the one lifecycle check.
Pending proposals deliberately still count: awaiting review is not retirement,
and two proposals for one cluster is exactly the duplicate this guard exists to
prevent.
tests
Four in
tests/test_themes.py:repro)
detect_themesdedupes thecluster away, archive the page, and confirm the cluster resurfaces
Full gate green locally:
pytest tests/ --ignore=tests/embeddings,mypy src,ruff check src tests, plusdiff-cover --fail-under 100on the changed lines.Sibling of #700 (compile) and #701 (provenance graph) — same archive leak,
three different surfaces, one PR each and no file overlap between them.