Signals judge one subject, with a sentence of your own, not any one word (#259)
Jev was asked "Is this item about any of these: ai, development, programming?", so any one broad word was enough and the Signal's name, which says what is meant, was never sent. AI Development held 54 items: Sega pulling a game remaster (0.90), a Surface Laptop review (0.95), a C++ tutorial (0.99). It is now asked about one subject, called by the Signal's name, described by its words and, when those are not enough, its owner's sentence. Scored against TypeSafe over those 54 with "AI tools for writing software: coding agents, AI dev tools, vibe coding, AI in code review.": 5 stay, Linux kernel AI code review highest at 0.94; the remaster 0.06, the C++ tutorial 0.02. signals.description is new, and its first start deletes every verdict, which answered the old question, so everything in the window is asked again. Any change to a Signal's name, words or sentence drops its verdicts, since each is part of the question. The queue query names the Signal's sentence apart from the item's description, which share a row; the test caught the item's winning. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
# Signals
|
||||
|
||||
A Signal is a feed someone makes from their own subscriptions (#254). They give it a name and the
|
||||
words to listen for (`formula 1`, `f1`). TypeSafe's Jev reads each item of the feeds they subscribe
|
||||
to and picks out the ones about those words. Those items show in the Signal as well as in their
|
||||
A Signal is a feed someone makes from their own subscriptions (#254). They give it a name, the
|
||||
words to listen for (`formula 1`, `f1`) and, when those are not enough, a sentence saying what they
|
||||
mean (#259). TypeSafe's Jev reads each item of the feeds they subscribe to and picks out the ones
|
||||
about that subject: the name, the words and the sentence together, not any one word. Those items show in the Signal as well as in their
|
||||
own feed. The rest stays noise, where it was.
|
||||
|
||||
Each person brings their own TypeSafe key, entered in Settings. Nothing is judged for someone
|
||||
@@ -17,8 +18,9 @@ their Signals that has not heard about the item yet.
|
||||
words under its name and its unread count.
|
||||
- **The folder's page** lists the Signals, as an OPML's page lists its feeds, with a + of its own.
|
||||
- **A Signal's page** is an ordinary item list: All, Unread, Downloaded and Pinned, search and sort.
|
||||
Its header says what it listens for, and that nothing is picked out without a key. Edit changes
|
||||
the name or the words; Delete removes the Signal and leaves the items in their feeds.
|
||||
Its header says what it listens for (the sentence, or the words), and that nothing is picked
|
||||
out without a key. Edit changes the name, the words or the sentence; Delete removes the Signal
|
||||
and leaves the items in their feeds.
|
||||
- **Read state is the item's.** Reading an item in a Signal reads it in its feed, and the other way
|
||||
round. The counts follow without a reload (#258).
|
||||
- **Settings, TypeSafe key.** A password field. A key saves on leaving the field, and the bin
|
||||
@@ -73,7 +75,9 @@ judge sleeps until woken or for 5 minutes.
|
||||
|
||||
`signal_verdicts` records each answer, fitting or not. An item with no row has not been asked yet;
|
||||
an item with a row is never asked again. So the cost is one call per item per person, once.
|
||||
Changing a Signal's words deletes its verdicts, and everything in the window is asked again.
|
||||
Changing a Signal's name, words or sentence deletes its verdicts, and everything in the window is
|
||||
asked again. The first start with sentences (#259) deleted every verdict, since they answered the
|
||||
old question.
|
||||
|
||||
## TypeSafe
|
||||
|
||||
@@ -93,18 +97,18 @@ Sent with `Authorization: Bearer <the person's key>`, for an item and two of the
|
||||
"questions": {
|
||||
"signal_3": {
|
||||
"type": "noul",
|
||||
"instructions": "Is this item about any of these: formula 1, f1?",
|
||||
"instructions": "The reader follows a subject they call \"F1\", described by these words: formula 1, f1. Is this item about that subject?",
|
||||
"criteria": {
|
||||
"true": "The item's subject is one of them, or plainly part of one.",
|
||||
"false": "None of them is its subject: one is only mentioned in passing, or none is."
|
||||
"true": "The item's main subject is that subject, as the name, the words and the reader's description together describe it.",
|
||||
"false": "It is about something else, or touches the subject only in passing."
|
||||
}
|
||||
},
|
||||
"signal_4": {
|
||||
"type": "noul",
|
||||
"instructions": "Is this item about any of these: baking?",
|
||||
"instructions": "The reader follows a subject they call \"Food\", described by these words: baking. In their own words: Bread and cakes, not restaurants. Is this item about that subject?",
|
||||
"criteria": {
|
||||
"true": "The item's subject is one of them, or plainly part of one.",
|
||||
"false": "None of them is its subject: one is only mentioned in passing, or none is."
|
||||
"true": "The item's main subject is that subject, as the name, the words and the reader's description together describe it.",
|
||||
"false": "It is about something else, or touches the subject only in passing."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -116,10 +120,16 @@ Sent with `Authorization: Bearer <the person's key>`, for an item and two of the
|
||||
- Each question is keyed `signal_<id>`, and its answer comes back under the same key.
|
||||
- `criteria` is an object saying what true and false mean. A sentence there is refused with a 422
|
||||
(#257).
|
||||
- **One subject, not any word.** The first question was "Is this item about any of these: ai,
|
||||
development, programming?", so one broad word was enough: AI Development took Sega pulling a game
|
||||
remaster (0.90), a Surface Laptop review (0.95) and a C++ tutorial (0.99), 54 items in all. Asked
|
||||
about the subject, with the sentence "AI tools for writing software: coding agents, AI dev tools,
|
||||
vibe coding, AI in code review.", those scored 0.06, 0.16 and 0.02, and 5 of the 54 stayed in,
|
||||
AI code review in the Linux kernel highest at 0.94 (#259).
|
||||
|
||||
### Its answer
|
||||
|
||||
What TypeSafe answered to the request above, on 2026-10-16:
|
||||
What TypeSafe answered to a request of this shape, on 2026-10-16:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -158,8 +168,9 @@ does not exist.
|
||||
|
||||
```json
|
||||
[
|
||||
{ "id": 1, "name": "AI Development", "keywords": ["ai", "development", "programming"], "unread": 5 },
|
||||
{ "id": 2, "name": "Apple", "keywords": ["apple", "mac", "iphone"], "unread": 0 }
|
||||
{ "id": 1, "name": "AI Development", "keywords": ["ai", "development", "programming"],
|
||||
"description": "AI tools for writing software: coding agents, AI dev tools, vibe coding, AI in code review.", "unread": 5 },
|
||||
{ "id": 2, "name": "Apple", "keywords": ["apple", "mac", "iphone"], "description": null, "unread": 0 }
|
||||
]
|
||||
```
|
||||
|
||||
@@ -169,17 +180,18 @@ not read.
|
||||
### `POST /api/signals`
|
||||
|
||||
```json
|
||||
{ "name": "Formula 1", "keywords": ["formula 1", "f1"] }
|
||||
{ "name": "Formula 1", "keywords": ["formula 1", "f1"], "description": "Grand prix racing, not road cars." }
|
||||
```
|
||||
|
||||
Answers `{ "id": 3 }` and wakes the judge. A name is 1 to 100 characters. Between 1 and 50 words
|
||||
are allowed, each up to 200 characters, kept once whatever their case. Anything else is a `400`
|
||||
saying so.
|
||||
are allowed, each up to 200 characters, kept once whatever their case. `description` is optional,
|
||||
up to 500 characters. Anything else is a `400` saying so.
|
||||
|
||||
### `PATCH /api/signals/{id}`
|
||||
|
||||
Either field or both: `{ "name": "F1" }`, `{ "keywords": ["f1", "grand prix"] }`. New keywords drop
|
||||
the Signal's verdicts and wake the judge. Answers `204`, or `404` for a Signal that is not yours.
|
||||
Any of the three: `{ "name": "F1" }`, `{ "keywords": ["f1", "grand prix"] }`,
|
||||
`{ "description": null }` (null or empty clears it). Any change drops the Signal's verdicts and
|
||||
wakes the judge, since each is part of the question. Answers `204`, or `404` for a Signal that is not yours.
|
||||
|
||||
### `DELETE /api/signals/{id}`
|
||||
|
||||
@@ -248,6 +260,7 @@ erDiagram
|
||||
bigint user_id
|
||||
text name
|
||||
text keywords "JSON array"
|
||||
text description "NULL: none"
|
||||
bigint created
|
||||
}
|
||||
signal_verdicts {
|
||||
@@ -259,8 +272,8 @@ erDiagram
|
||||
```
|
||||
|
||||
- `signals` and `signal_verdicts` are created from their entities like every table
|
||||
(`crates/ipx/src/entity.rs`). `users.typesafe_key` was added to an existing table, so
|
||||
`create_missing` adds the column when it is missing.
|
||||
(`crates/ipx/src/entity.rs`). `users.typesafe_key` and `signals.description` were added to
|
||||
existing tables, so `create_missing` adds each column when it is missing.
|
||||
- A Signal's list is `entries_where` with
|
||||
`EXISTS (SELECT 1 FROM signal_verdicts v WHERE v.signal_id = $n AND v.feed_id = e.feed_id AND v.guid = e.guid AND v.hit)`
|
||||
on top of the subscribed-feeds scope. So filters, search, sort, block lists and read state all
|
||||
|
||||
Reference in New Issue
Block a user