feat(tempo): blue team Tempo time from pick-up, not queue entry
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Previously blue_started_at was set when the RED team submitted evidence
(= queue open time), so Tempo was getting total queue wait time instead
of actual work time.
Changes:
- DB: add blue_work_started_at column (migration b045), set when a blue
tech explicitly picks up the test (mirrors red_started_at for red team)
- Workflow: new start_blue_work() function + POST /tests/{id}/start-blue-work
endpoint (blue_tech / blue_lead roles). Cannot be called twice.
- submit_blue_evidence: uses blue_work_started_at (when available) as the
phase start for the Tempo worklog, falls back to blue_started_at
- reopen_test: clears blue_work_started_at alongside other timing fields
- Tempo: both red_team_execution and blue_team_evaluation now synced;
correct work_date and description per activity type
- Frontend: "Start Evaluation" button shown in blue_evaluating state when
blue_work_started_at is null; live timer shows from pick-up time
What each timestamp tracks:
blue_started_at = queue entry (SLA / internal tracking)
blue_work_started_at = pick-up by blue tech (Tempo start)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
||||
startExecution,
|
||||
submitRedEvidence,
|
||||
submitBlueEvidence,
|
||||
startBlueWork,
|
||||
validateAsRedLead,
|
||||
validateAsBlueLead,
|
||||
reopenTest,
|
||||
@@ -190,6 +191,15 @@ export default function TestDetailPage() {
|
||||
onError: (err: unknown) => showToast(extractError(err), "error"),
|
||||
});
|
||||
|
||||
const startBlueWorkMutation = useMutation({
|
||||
mutationFn: () => startBlueWork(testId!),
|
||||
onSuccess: () => {
|
||||
invalidateAll();
|
||||
showToast("Blue evaluation started", "success");
|
||||
},
|
||||
onError: (err: unknown) => showToast(extractError(err), "error"),
|
||||
});
|
||||
|
||||
const validateRedLeadMutation = useMutation({
|
||||
mutationFn: (payload: { red_validation_status: "approved" | "rejected"; red_validation_notes?: string }) =>
|
||||
validateAsRedLead(testId!, payload),
|
||||
@@ -302,6 +312,7 @@ export default function TestDetailPage() {
|
||||
startExecMutation.isPending ||
|
||||
submitRedMutation.isPending ||
|
||||
submitBlueMutation.isPending ||
|
||||
startBlueWorkMutation.isPending ||
|
||||
reopenMutation.isPending;
|
||||
|
||||
// ── Loading / Error states ─────────────────────────────────────
|
||||
@@ -370,6 +381,7 @@ export default function TestDetailPage() {
|
||||
onStartExecution={() => startExecMutation.mutate()}
|
||||
onSubmitRed={() => submitRedMutation.mutate()}
|
||||
onSubmitBlue={() => submitBlueMutation.mutate()}
|
||||
onStartBlueWork={() => startBlueWorkMutation.mutate()}
|
||||
onOpenValidateModal={(side) => setValidationModal({ open: true, side })}
|
||||
onReopen={() => setConfirmReopen(true)}
|
||||
onPauseTimer={() => pauseTimerMutation.mutate()}
|
||||
|
||||
Reference in New Issue
Block a user