Skip to content

Signals: Refine results (sort, min relevance, tone, networks), Load more, CSV export - #110

Merged
ralyodio merged 1 commit into
mainfrom
feat/signals-refine
Oct 6, 2026
Merged

ralyodio merged 1 commit into
mainfrom
feat/signals-refine

Conversation

@ralyodio

@ralyodio ralyodio commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

What

A Needle-style Refine results panel on /signals, the screen where leads from listening arrive. It filters the posts already loaded in the browser. Load more widens the list, then Export CSV downloads what's shown.

Control Options
Sort Most relevant (default) · Newest first · By network
Min relevance Any match · Possible+ (≥0.4) · Relevant+ (≥0.6) · Very relevant (≥0.8, the feed's existing "High intent")
Tone Any · Positive · Neutral · Negative (signals.sentiment, which listening sets from the post type)
Networks one checkbox per network present, with counts. All networks are on by default
  • API: GET /signals takes before=<timestamp|id> and returns next_cursor, which is null on the last page. The id breaks ties, so posts stamped with the same second are never skipped or repeated.
  • Export: the CSV columns are network, name, summary, relevance, tone, type, posted_at, url and person_id. Cells starting with =, +, - or @ get a leading quote so a spreadsheet can't run them as formulas.
  • Today page: fetchSignals() keeps its shape for Today, and the Signals page uses a new fetchSignalPage().

Verified

  • New tests:
    • apps/web/lib/signal-filters.test.ts, 6 tests: sorts, tier floors, tone, networks and CSV.
    • apps/api/src/signals-feed.test.ts, 2 tests: the cursor walks 7 seeded signals (3 sharing one timestamp) 3 per page, each seen exactly once, and the last page has no cursor. A malformed cursor is ignored.
  • Root tsc and apps/web tsc --noEmit pass, and prettier was applied.
  • Not checked visually: the page needs a signed-in session against the API.

🤖 Generated with Claude Code

…ore, CSV export

The signal feed is how leads arrive, and it was a flat newest-first list of 50.
Now it has a Refine results panel that filters what is loaded in the browser:

- Sort: Most relevant (default), Newest first, By network
- Min relevance: Any match, Possible+ (0.4), Relevant+ (0.6), Very relevant
  (0.8, the threshold the feed already labels "High intent")
- Tone: Any, Positive, Neutral, Negative (signals.sentiment, set by listening
  from the post type)
- Networks: a checkbox per network present, with counts
- Load more widens the list (GET /signals?before=<cursor>, timestamp|id so
  posts sharing a second are neither skipped nor repeated; next_cursor null at
  the end); Export CSV downloads exactly what the filters show, formulas defused

fetchSignals() keeps its shape for Today; the Signals page uses fetchSignalPage().
Tests: 6 for the filters/CSV, 2 for paging through the API.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ralyodio
ralyodio merged commit d636551 into main Oct 6, 2026
4 checks passed
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