skip decision overwrites the projection it should leave alone #81
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Severity: MEDIUM
The bug
PhotoRepository.apply_decision_projection(backend/app/repositories/photos.py:80-101)unconditionally sets
current_decision_idand overwrites all threecurrent_effective_date_*fields for every decision type. Only
statusis spared for a skip.So a skip becomes the photo's "current decision," and if the reviewer's form fields were empty or
cleared, the photo's effective date is nulled by the act of declining to decide.
This is currently harmless only because the frontend pre-fills the form from the current effective
values, making the overwrite a no-op — correctness by coincidence.
Decision
Skip writes a history row and touches nothing else.
The alternatives considered were: a pure client-side advance with no record at all (fast, but
loses "someone looked at this and moved on," which matters once several people share a queue), and
the current behaviour. The chosen option keeps attribution without side effects.
Scope
apply_decision_projectionbefore the projection writes when the decision is askip.
DateDecisionrow is still created, with actor and timestamp, so the audit trail records it.current_decision_idis not updated by a skip.keep the history row for now.
Done when
References
backend/app/repositories/photos.py:80-101backend/app/models/models.py(DecisionType.skip)Related: #9 (concurrency tests), and the projection rebuild issue.
Done in
03dbc33, alongside #77 since both live in the decision/projection path.apply_decision_projectionnow returns early for a skip. TheDateDecisionrow is still written by the caller, so "someone looked at this and moved on" survives with its actor and timestamp — which is the property that matters once several people share a review queue.Skip also does not advance
review_version. The issue didn't specify this and it's worth flagging: no reviewable state changed, so invalidating another reviewer's in-flight edit would be gratuitous. It also makes two consecutive skips both succeed, which is how a queue actually gets used. There are tests for both.Your note that the current behaviour was "harmless only because the frontend pre-fills the form" was exactly right, and it's why this was worth fixing before real data exists rather than after — the day someone builds a script or a keyboard shortcut that posts a skip without pre-filling, it silently nulls settled dates.
Tests assert a skip leaves the effective dates,
current_decision_id, status and version untouched, that it still appears in the decision history attributed to the reviewer, and — separately — thatapprove/dispute/needs_reviewstill project correctly, since an early return is the kind of change that can quietly disable its neighbours.The rebuild fold (#79) will need to agree with this; I'll make it explicit when I build it, as this issue asks.
(Edited: this comment originally pointed at #6 for the rebuild fold. The correct issue is #79 — #6 is the projection-service test suite that consumes #79's oracle.)