Skip to content

A2A chat read API: thread list, cursor pagination, unread counts (unified-chat slice 4) - #275

Merged
jaylfc merged 1 commit into
masterfrom
exec/tsk-qthbox
Aug 14, 2026
Merged

A2A chat read API: thread list, cursor pagination, unread counts (unified-chat slice 4)#275
jaylfc merged 1 commit into
masterfrom
exec/tsk-qthbox

Conversation

@jaylfc

@jaylfc jaylfc commented Aug 14, 2026

Copy link
Copy Markdown
Owner

CARD TITLE (intent, not commit subject): A2A chat read API: thread list, cursor pagination, unread counts (unified-chat slice 4)

Autonomous build of board card tsk-qthbox.

CONSUMER-VISIBLE CONTRACT CHANGE — read before merging

GET /a2a/threads changes its sort order from alphabetical by thread name to
most-recently-active first. This is a shipped endpoint. Any consumer that relies on
the old ordering — including anything that reads the first element, or diffs the list
positionally — changes behaviour silently the moment this merges; nothing errors.

Measured before merge (2026-08-14), so the risk is stated with a number rather than as a
caution: zero consumers today. The taOSmd dashboard calls /a2a/channels,
/a2a/members, /a2a/messages and /a2a/stream, not /a2a/threads; a filesystem sweep
across ~/Development and the fleet scripts (control: the same sweep for /a2a/messages
returns 8 files) finds no caller of /a2a/threads outside this repo. The endpoint appears
in http_server.py, remote.py, service.py and its tests, and nowhere else — it is not
mentioned in docs/, README.md or CHANGELOG.md.

So this is safe to land now, and the reason it is safe is that the endpoint has no
audience yet — not that the change is compatible. Flagged to @taOS-dev on the bus.


  • fix a2a_threads sort order: was alphabetical by thread name, now ordered
    by latest activity timestamp descending
  • strip internal _last_ts from returned thread objects (not part of API shape)
  • export a2a_threads and a2a_thread_messages in service.all
  • add tests: threads ordered by latest activity, before/after cursors,
    limit clamped at 200, stable pagination across concurrent insert,
    message id passed to since= rejected with clear 400 error

Files:
taosmd/service.py | 8 +++--
tests/test_http_server.py | 91 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 97 insertions(+), 2 deletions(-)

…tion tests

- fix a2a_threads sort order: was alphabetical by thread name, now ordered
  by latest activity timestamp descending
- strip internal _last_ts from returned thread objects (not part of API shape)
- export a2a_threads and a2a_thread_messages in service.__all__
- add tests: threads ordered by latest activity, before/after cursors,
  limit clamped at 200, stable pagination across concurrent insert,
  message id passed to since= rejected with clear 400 error
@qodo-code-review

Copy link
Copy Markdown

ⓘ Qodo reviews are paused because your trial has ended. Ask your workspace admin to add credits to resume reviews. Manage billing

@coderabbitai

coderabbitai Bot commented Aug 14, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@jaylfc, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 8 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ca778b3d-bd32-4b20-9a5c-39acdb204c5f

📥 Commits

Reviewing files that changed from the base of the PR and between 1019775 and 5e6331c.

📒 Files selected for processing (2)
  • taosmd/service.py
  • tests/test_http_server.py

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gitar-bot

gitar-bot Bot commented Aug 14, 2026

Copy link
Copy Markdown

Important

You are using the Gitar free plan. Upgrade to unlock code review, CI analysis, auto-apply, custom automations, and more.

Gitar

@kilo-code-bot

kilo-code-bot Bot commented Aug 14, 2026

Copy link
Copy Markdown

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Files Reviewed (2 files)
  • taosmd/service.py - a2a_threads sort fix, _last_ts stripped from output, all exports added
  • tests/test_http_server.py - 6 new tests covering ordering, cursor pagination, limit clamp, stability, and since= rejection

Reviewed by step-3.7-flash · Input: 66K · Output: 20.3K · Cached: 1.1M

@jaylfc

jaylfc commented Aug 14, 2026

Copy link
Copy Markdown
Owner Author

APPROVED with one required change. The behaviour change is correct and I proved it in both directions rather than reading the diff. One documentation gap has to close before this lands, because after this PR the ordering is a contract with no written record anywhere.

The change is real and its test is not vacuous

Red control — this branch's tests against master's service.py, everything else identical:

FAILED test_a2a_threads_ordered_by_latest_activity_desc
E   AssertionError: assert ['alpha', 'beta'] == ['beta', 'alpha']
1 failed, 5 passed

Green — the same tests against this branch: 6 passed.

So the ordering test fails on master by its own assertion, naming the exact defect, and passes here. That is the bar #272/#273 established and this clears it.

Worth stating plainly: the other five tests pass on master too. They are regression coverage for the cursor/limit behaviour that already worked, not evidence for this change. One test defends the change; five defend what was already there. Both are worth having, but the PR reads as six-tests-for-a-fix and it is one.

_last_ts really is stripped — checked on the wire, not in the diff

The return builds fresh dicts, but the internal key has to be absent from the actual response, so I asked the server:

GET /a2a/threads  ->  PAYLOAD KEYS: ['kind', 'last_message', 'participants', 'thread']

Exactly the four documented keys. No leak.

Sort stability

archive.py:56 declares timestamp REAL, so _last_ts is a float epoch and the test's time.sleep(0.02) is comfortably above resolution — no flake there. Ties are still possible in principle, and list.sort is stable, so two threads sharing a _last_ts fall back to archive scan order rather than anything a caller can predict. A key=lambda x: (-x["_last_ts"], x["thread"]) would make the tie deterministic. Nit, not a blocker.

Required change: the ordering is now undocumented behaviour

The a2a_threads docstring specifies the item shape and says nothing about order — that was fine when the order was alphabetical and obvious from the code, and it is not fine now. I checked where else a consumer could find out:

git grep -l "a2a/threads\|a2a_threads" origin/master
  taosmd/http_server.py  taosmd/remote.py  taosmd/service.py  tests/test_http_server.py

Nothing in docs/, README.md or CHANGELOG.md. So after this merges, the only statement of the endpoint's ordering guarantee is one sort line and one test. Add a sentence to the a2a_threads docstring — "Threads are ordered by most recent activity first" — and this is done. That is the whole required change.

The contract change, and why it is safe today

I have added a section to the PR body rather than leaving this in review notes, since it is the thing a future reader needs at merge time. Summarised here: /a2a/threads is a shipped endpoint whose sort order changes from alphabetical to most-recently-active, and nothing errors if a consumer depended on the old order.

Measured rather than assumed: zero consumers today. The dashboard uses /a2a/channels, /a2a/members, /a2a/messages and /a2a/stream (dashboard/src/api.ts, views/A2AView.tsx); a sweep across ~/Development and the fleet scripts finds no external caller of /a2a/threads, with the same sweep for /a2a/messages returning 8 files as the control that the sweep works. Flagged to @taOS-dev.

Separate finding, pre-existing, not this PR's to fix

a2a_threads(*, principal=None, ...) accepts principal, http_server.py:1697 reads it from the query string and passes it in, and the local path never uses it — the only reference in the function body is the forward to RemoteClient. A caller asking for a principal-scoped thread list gets every thread, with a 200 and no indication the filter was dropped. That arrived long before this PR and I am carding it separately rather than growing this one.

Mechanics

@jaylfc
jaylfc merged commit 1e79f08 into master Aug 14, 2026
6 checks passed
@jaylfc
jaylfc deleted the exec/tsk-qthbox branch August 14, 2026 16:08
jaylfc added a commit that referenced this pull request Aug 14, 2026
…led as cards

The bounded-probe question is ruled (bus 2679): executing a pre-specified
acceptance bar is orchestration, inventing one is hands-on, and a probe run
under the usage band must print a positive control or a denominator. The
freshness predicate is now a checked-in script that prints its own controls
rather than something retyped each session.

Also records that the automated 5h usage notice still contradicts the ruled
band, that assignee "@any" is not a stored field (37 of 37 claimable cards
carry null), and that a heredoc cannot read a pipe.
jaylfc added a commit that referenced this pull request Aug 14, 2026
…281)

The GET /a2a/threads endpoint was changed in PR #275 to return threads
sorted most-recently-active first (replacing the previous alphabetical
ordering), but that contract change was unrecorded apart from the sort
line and one test assertion. Add a sentence to the a2a_threads docstring
stating the ordering and noting the prior behaviour, so callers relying
on alphabetical order know to sort client-side.

Closes tsk-4f7hsa.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant