Sloth-ninja makes Clio matter search stop second-guessing itself

A pilot finding that missed most matching matters has become a more complete and safer chat search experience.

searchsecurity

A solicitor searching for open Kyckr matters saw only 10 of 26 records, leaving chat to keep trying narrower searches. Sloth-ninja has raised the results returned at once and added controls that make the search more useful without turning it into an uncontrolled browse.

  • Status filtering lets users ask for open, pending, closed, or a combination of matters.
  • Status-only searches work when a name or keyword is unavailable.
  • Continuation tokens remember the original search and status, so later pages cannot quietly broaden the request.
  • Pagination links from Clio, the legal practice-management platform, are checked before use, blocking malicious or malformed destinations.

The chat now reports what it returned, whether a total is available, and whether more records exist rather than overstating completion.

So what Firms using chat to find operational matter information should care because faster retrieval is valuable only when the results stay scoped and trustworthy.

View this fork on GitHub →

Spotted something wrong? Or know the PR text has fresher detail than the writeup above?

Commits in this thread

3 commits from Sloth-ninja/JessicaOSS, oldest first. Source extracted verbatim from the harvested git log.

SHA Subject Author Date
faaf5a6b Clio matter search: status filter, 100-result pages, honest pagination sloth-ninja 2026-08-04 ↗ GitHub
commit body
Pilot bug (04/08): "open Kyckr matters" made the model loop targeted
searches for minutes because clio_find_matter hardcoded limit 10 with no
filters or pagination. Now: page size 100 (Clio caps index actions at
200/request), validated open/pending/closed status filter passed through
as Clio's comma-separated status param, page_token continuation minted
from meta.paging.next and replayed against the API base, and a
{ matters, count, total_entries, has_more, next_page_token? } payload so
the model reports totals honestly instead of guessing. meta.records is
undocumented in reachable Clio docs, so it is parsed tolerantly (null
when absent). Schema + system prompt teach: status arg not query
keywords, report totals, fetch further pages only on request, never
brute-force loop.

Verified: tsc clean, 616/616 vitest (baseline 604 + 12 new), prettier
clean on changed files.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
11373e51 Review fixes: opaque page_token cursor, raw-next has_more, FAQ citation sloth-ninja 2026-08-04 ↗ GitHub
commit body
Round-1 review blocker: the path-shaped page_token with a
startsWith("/matters.json") guard was traversal-bypassable because
buildUrl's new URL() normalises ".." segments (DURABLE_LESSONS
2026-07-28 class). Redesigned to an opaque cursor:
matterPageTokenFromNext now parses meta.paging.next, requires the exact
Manage API origin and exact <apiBase>/matters.json pathname, and
extracts only the page_token query-param value; the continuation
request is rebuilt from named parts (hardcoded path, our fields+limit,
repeated query/status per the schema) so a model-supplied string never
becomes a request path. has_more now derives from the raw presence of
meta.paging.next, never from cursor extraction success. Status-filter
citation pinned to docs.developers.clio.com/faq/ (case sensitivity
undocumented; lowercase literals enforced); at-least-one-argument rule
stated in the function description. Tests: traversal/suffix-path/
off-host/protocol-relative next URLs rejected, traversal-shaped
page_token asserted to never reach the path, continuation rebuild
asserted to carry fields+limit+cursor. 620/620 tests, tsc + prettier
clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ea89d17e Final review wave: filter-bound page token, host-spoof tests, length bound sloth-ninja 2026-08-04 ↗ GitHub
commit body
Bind the continuation token: next_page_token is now base64url JSON
{ c: cursor, q?, s? } carrying the query/status the page was actually
fetched with. On continuation the token is length-bounded (<=1024),
decoded (failures -> the fixed friendly error before any fetch), its
status re-validated, and the decoded filters used - model-supplied
query/status are ignored under page_token, so a model dropping optional
args on a follow-up can never silently fetch the unfiltered matter list.
Schema/prompt now say "pass page_token alone; filters are remembered".
Removed the uncited "next URL = original params + page_token" comment
claim. Added the two named 2026-07-28 host-spoof regression shapes
(suffix-domain, userinfo) to the matterPageTokenFromNext tests.

627/627 tests (23 new vs baseline), tsc + prettier clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Capture this thread into my fork

Download a single Markdown prompt that tells Claude how to port every commit above into your working tree — adapting paths and structure to match your repo. Run it via claude -p < capture-thread-1065.md from inside the repo you want the changes in.

⬇ Download capture-thread-1065.md