feat(tests): let managers escalate and directly resolve disputed tests
Aegis CI / lint-and-test (push) Has been cancelled
Snyk Security Scan / Python vulnerabilities (backend) (push) Has been cancelled
Snyk Security Scan / npm vulnerabilities (frontend) (push) Has been cancelled
Snyk Security Scan / Docker image vulnerabilities (backend) (push) Has been cancelled
Aegis CI / lint-and-test (push) Has been cancelled
Snyk Security Scan / Python vulnerabilities (backend) (push) Has been cancelled
Snyk Security Scan / npm vulnerabilities (frontend) (push) Has been cancelled
Snyk Security Scan / Docker image vulnerabilities (backend) (push) Has been cancelled
Request Discussion only ever nudged the peer lead, and the one-time
manager notification when a dispute starts had no real follow-up
action attached to it — a manager could be told a dispute existed but
had no way to actually do anything about it. Adds:
- POST /tests/{id}/escalate-to-manager — either lead can explicitly
ask managers to step in, distinct from the passive initial notice.
- POST /tests/{id}/manager-resolve-dispute — a manager decides the
final outcome (validated/rejected) directly, bypassing both leads'
votes entirely, reusing the same dual-validation event dispatch so
Jira/notifications behave like any other resolution.
Both leads are notified of the manager's final call, win or lose.
This commit is contained in:
@@ -672,6 +672,27 @@ class TestEntity:
|
||||
|
||||
self._events.append(DomainEvent("dispute_resolved_to_rework", {"target": target}))
|
||||
|
||||
def resolve_dispute_by_manager(self, outcome: str) -> None:
|
||||
"""A manager makes the final call on a disputed test, ending the standoff.
|
||||
|
||||
Unlike ``resolve_dispute_reject`` (a lead flipping their own vote),
|
||||
this bypasses both leads entirely — the manager's decision is final
|
||||
and terminal, going straight to ``validated`` or the normal
|
||||
``rejected`` dead-end (which any lead can later reopen via the
|
||||
standard reopen-to-draft flow, same as any other rejection).
|
||||
|
||||
Args:
|
||||
outcome (str): ``"validated"`` or ``"rejected"``.
|
||||
"""
|
||||
if outcome not in ("validated", "rejected"):
|
||||
raise InvalidOperationError("outcome must be 'validated' or 'rejected'")
|
||||
|
||||
target_state = TestState.validated if outcome == "validated" else TestState.rejected
|
||||
self._transition(target_state)
|
||||
self._events.append(
|
||||
DomainEvent("dual_validation_approved" if outcome == "validated" else "dual_validation_rejected")
|
||||
)
|
||||
|
||||
# -- Private -------------------------------------------------------
|
||||
|
||||
def _auto_resume(self) -> int:
|
||||
|
||||
Reference in New Issue
Block a user