Files
xarmian ca184cff96 feat(needs): sort=importance|simplicity with a score-keyed cursor (TASK-3959, part 2) (#2274)
* feat(needs): sort=importance|simplicity with a score-keyed cursor (TASK-3959, part 2)

GET /me/needs takes sort=importance and sort=simplicity: the primary score
then the secondary, both highest first, then the item id; a row a scorer left
unscored sorts last. The cursor carries the key as score/score2 (absent under
age, so an age cursor is unchanged), and a score cursor missing either, or
out of -1..100, is invalid_cursor. docs/api.md documents the sorts.

Claude-Session: https://claude.ai/code/session_01UghmjfBZb6A8UbRgi4rPaw

* fix(needs): r1 — differing-order sort test, degraded-page and missing-score docs, comments (TASK-3959, part 2 r1)

Claude-Session: https://claude.ai/code/session_01DQDip8AC96dA71t2UCPLTD

* fix(needs): r1 — range-check every cursor field, pin the as_of clamp (TASK-3959, part 2 r1)

Claude-Session: https://claude.ai/code/session_01DQDip8AC96dA71t2UCPLTD

* fix(needs): a cursor the server minted is always followed; key times clamp to int64 nanoseconds (TASK-3959, part 2 r2)

r2 found that r1's key/created_at range check (> 0, before 2200) refused
cursors the server itself mints: a pre-1970 date, or a zero time from an
unparseable stored created_at, gives a non-positive UnixNano, and paging
dead-ended at that row under every sort. A check may refuse only what the
server could never mint, so key and created_at are no longer checked.

A time outside int64 nanoseconds (a zero time, or past 2262) also did not
round-trip: UnixNano wraps, so an age cursor named a different instant from
the row's key. KeyFor now clamps both key times to that range, so the sort
and the cursor see the same instant.

The table drops the key/created_at 400 rows, keeps negative values as
followed, and adds the age-cursor-with-a-score rows (r2 L2). A new test pages
pre-1970, zero, unparseable and year-3000 boundary rows through every sort
at limits 1 and 2.

Claude-Session: https://claude.ai/code/session_01DQDip8AC96dA71t2UCPLTD

* fix(needs): the cursor docs say only what the server could never mint is refused; the odd-time test runs on Postgres (TASK-3959, part 2 r3)

r3 (both reviewers): docs/api.md still listed the key/created_at range
check removed in r2. The sentence now lists exactly what is refused, says the
key times are positions that any value of follows, and says a key time
outside about 1677 to 2262 is clamped, so such rows tie on it (hunter L3).

The odd-boundary test wrote created_at through the raw *sql.DB with a ?
placeholder, which is not rewritten for Postgres; it now writes literal SQL,
as setCreated does, and passes on both backends. It reports each sort with
Errorf, and its comment says what each sort proves: every sort that the
cursor's key times are followed, and only age that the clamp holds (r3 M2).

Claude-Session: https://claude.ai/code/session_01DQDip8AC96dA71t2UCPLTD

* fix(needs): the cursor docs list the version check; the real-scorer sort test runs on both backends (TASK-3959, part 2 r4)

r4 hunter L1: the sentence listing exactly what a cursor check refuses left
out a cursor of another version (v). r4 hunter L2: the test that sorts by the
real scorers through the router ran on SQLite only; it now runs under
needsBackends, and passes on Postgres.

Claude-Session: https://claude.ai/code/session_01DQDip8AC96dA71t2UCPLTD
2026-10-21 12:38:23 -04:00
..