Nobody has started this yet — be first.
Business impact
GET /v1/jobs today fetches and returns every single job in the system in one response, unbounded. That's fine for a demo with 3 seeded jobs; it's a real problem for any operator running chronos at scale with thousands of jobs -- every dashboard load, every polling script, every health check that touches this endpoint pays the cost of serializing and transferring the entire table, and that cost only grows. An unbounded list endpoint is a classic, easy-to-miss source of a production incident: it works fine for months and then one day the jobs table crosses a size threshold and the endpoint starts timing out or OOMing the process.
Problem
internal/api/jobs_handler.go's listJobs already parses ?limit= and ?offset= and calls s.jobs.ListPage(ctx, limit, offset) when either is present (defaulting to limit=50, offset=0 if only one is given). internal/store/jobs.go's ListPage is a placeholder that panics with "TODO(ticket-E3): ListPage not implemented".
Current behavior
GET /v1/jobs?limit=2&offset=1 returns HTTP 500 instead of 200.
Expected behavior
GET /v1/jobs?limit=20&offset=40 returns jobs 41-60 (ordered by id), so a client can page through the full set predictably. Omitting both limit and offset keeps today's "return everything" behavior.
Steps to reproduce
Why this matters
This is standard offset-based pagination -- the same ORDER BY ... LIMIT ... OFFSET ... pattern already used elsewhere in this codebase (e.g. pkg/queue.ListByJob's LIMIT $2), just with an added OFFSET.
Suggested approach
Look at List in internal/store/jobs.go for the base query and ordering (ORDER BY id) to build on; ListPage needs the same query with LIMIT $1 OFFSET $2 appended.
Acceptance criteria
Verification
TEST_DATABASE_URL="postgresql://chronos:chronos@localhost:5432/chronos?sslmode=disable" go test ./tickets/tests/... -run TestTicket09 -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 feat/list-jobs-paginationFix it and commit
Meet every acceptance criterion, and add a test that would have caught this.
Push the branch
$git push -u origin feat/list-jobs-paginationSubmit 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 inhttp://localhost:8081app-1http://localhost:8082app-2http://localhost:8090curl localhost:8081/healthzcurl localhost:8081/readyzWork the tickets in tickets/docs/ (ordered easy → hard); each names one Go test in tickets/tests/. Most tickets need a real Postgres (tickets 4, 11, and 18 are pure-function tests and do not):
export TEST_DATABASE_URL="postgresql://chronos:chronos@localhost:5432/chronos?sslmode=disable"
go test ./tickets/tests/... -run TestTicket08 -v # a single ticket
./tickets/run_tickets.sh # all 20, clean pass/fail summary
Use -race when working on tickets 15, 16, or 17 (the concurrency ones) -- run_tickets.sh already does this for every ticket. Tickets 15 and 16 are adjacent bugs in the same function (pkg/leader/leader.go's lock-release path); fix ticket 16 first, since ticket 15's test keeps failing for ticket 16's reason until that one is fixed too.
Level 2
Implement a feature
Extend the system within its own patterns.