Catching up: the wiki's recipe still omits after_id, and GET /captures does not take it #439
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
performance
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Postulo/postulo#439
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?
Found on 2026-09-29 in a read-only review of postulo at
95348908d, checking every list call againstapi/paging.pyand the wiki's The capture API.Checked again on 2026-10-01 against
mainat0a5cad587: still present. The code is now atapi/api.py:633-654(list_captures); the wiki page has not changed.#245 added
after_idbecause a cursor made ofupdated_atalone cannot get past a run of rows that share one timestamp, and it said the handbook's recipe would change to "remember the lastupdated_atandid". The recipe did not change, so a client written from the wiki still has the loop #245 fixed. And the one list the wiki names as readable with thecapturesscope alone, the capture queue, never gained the parameter.postulo.wiki/The-capture-API.md:277-284(Catching up): "Every row carries anupdated_at, and that is the cursor: read a page, remember theupdated_atof the last row on it, and ask again from there." The example URL carries onlyupdated_since.after_idis named once on the page, for/listings/choices(line 330).postulo.wiki/The-capture-API.md:60-62and:273-275say every list takesupdated_since, and say nothing ofafter_id.api/api.py:631-652,list_captures: takesupdated_sinceonly and returnschanged_since(captures, updated_since). Every other queryset list (applications, companies, CVs, letters, interviews, listings, listing choices, offers, reminders) takesafter_idand passes it on;/documentsis the one deliberate exception, and its docstring explains why.updated_atisauto_now). A client following the wiki asksGET /applications?updated_since=…&limit=50, remembers theupdated_atof the fiftieth row and asks again from it.changed_sincewithoutafter_idfiltersupdated_at__gte=since, so the client is handed the same fifty rows every time: it loops, or stops and never reads the other fifty.after_idwould fix this, but nothing on the page tells the client it exists. A client catching up on/captureshas noafter_idto send at all.tests/test_api_lists.pywalks with both halves), but not in the documentation a client is written from, and not on one of the lists. The mirror a client keeps comes up short with no error.updated_sinceandafter_idfrom the same last row, with an example URL carrying both, and say what goes wrong withoutafter_id. Mentionafter_idwherever the page says every list takesupdated_since, and note that/documentsdoes not take it.after_id: int | None = Query(None, description=AFTER_ID)tolist_capturesand pass it tochanged_since.Related to #245 (closed), which promised the recipe change and added
after_idto the other lists.Done when
tests/test_api_lists.pyhas a test that walks a run of pending captures sharing oneupdated_atthroughGET /api/v1/captures?updated_since=…&after_id=…&limit=…and reaches every one of them; it fails today becauseafter_idis ignored.updated_sincetogether withafter_id.