Nobody has started this yet — be first.
Business impact
jobs_finished_total{status=...} is the metric anyone would reach for to answer "what fraction of transcodes are failing right now" -- the kind of number that gates a deploy, drives an alert threshold, or shows up on the first panel of an on-call dashboard. Right now, every single job that completes successfully increments the "failed" bucket instead of "completed". In a healthy system processing thousands of successful jobs a day, this metric would show a 100% failure rate. Best case, whoever's watching that dashboard loses trust in it and starts ignoring it -- so the day this service actually starts failing for real, nobody notices. Worst case, it pages someone at 2am for a "failure spike" that is, in fact, every job succeeding normally.
Problem
This line runs immediately after the job's success _transition(..., status="completed", ...) call -- exactly when the job succeeded -- but increments the counter under the "failed" label instead of "completed".
Current behavior
run_job()'s success branch calls jobs_finished_total.labels(status="failed").inc() immediately after transitioning the job to status="completed" -- the wrong label literal for the branch it's in.
Expected behavior
A job that finishes with status="completed" (no error) must increment jobs_finished_total{status="completed"}. jobs_finished_total{status="failed"} must only increment for jobs that actually raised an exception.
Steps to reproduce
pytest practice_tickets/tests/test_ticket_01_completed_jobs_counted_as_failed.py -vPOST /jobs, poll GET /jobs/{id}?wait=1 until status == "completed", then read .Why this matters
This is a copy-paste-style mistake: the success branch's metrics call almost certainly started as a copy of the except branch's (or vice versa), and the label literal never got updated to match which branch it's actually in. Nothing in the type system or a linter catches a wrong string literal passed to .labels(status=...) -- it's valid Python, valid Prometheus, and only wrong in meaning. run_job() has three near-identical .labels(status=...).inc() call sites (success/cancelled/failed) -- one of them is wrong, and eyeballing them side by side is the fastest way to spot it.
Suggested approach
Look at all three jobs_finished_total.labels(status=...) call sites in app/worker.py's run_job() (success, cancelled, failed branches) side by side. One of them doesn't match the branch it's in.
Acceptance criteria
Verification
pytest practice_tickets/tests/test_ticket_01_completed_jobs_counted_as_failed.py -v
Hints (0/2)
Try it without hints first — the reading is the exercise.
Working on this ticket
Work on a branch named for the ticket — that's what you'll submit.
Branch off your fork
Sign in to create it automatically, or do it yourself:
$git checkout -b fix/completed-jobs-counted-as-failedFix it and commit
Meet every acceptance criterion, and add a test that would have caught this.
Push the branch
$git push -u origin fix/completed-jobs-counted-as-failedSubmit it below
Paste your fork URL and the branch name, with a short write-up of the root cause.
Questions
Ask about anything unclear in the ticket — the maintainer and anyone who has solved it can answer. Please don't post full solutions.
Sign in to ask a question or reply.
Sign inGET /metricsjobs_finished_total{status="failed"} increments to 1.0, for a job that completed with no error at all.http://localhost:8000localhost:6379curl localhost:8000/healthz{"status": "ok", "redis": true}Work the tickets in practice_tickets/tickets/ (ordered easy → hard); each names one dedicated pytest file in practice_tickets/tests/:
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements-dev.txt
pytest practice_tickets/tests/test_ticket_01_completed_jobs_counted_as_failed.py -v # a single ticket
./practice_tickets/run_tickets.sh # all 15, clean pass/fail summary
Each ticket test runs in its own pytest process with a 60s timeout, so one hung/crashed ticket can't take down the whole run or hide another ticket's result. Run pytest tests/ (the project's own suite, against fakeredis, hermetic and fast) after each fix to check for regressions -- the README documents exactly which fixes are expected to change that suite's results.
Level 1
Fix a bug
Read existing behaviour, correct it.