2b597921f4
A Poll finding a higher number now clears series.latest_raised_by: the value it stores is its own, so the next Poll must not credit that Reader with an agreement they did not earn, nor charge them for a retraction of a number they never reported. A PUT carrying no chapter is no Sighting and defers nothing. The ceiling now counts the Site's own rest rather than defaultRest. The userscripts PUT an unchanged read too - the case the whole mechanism exists for was the one they never sent. ADR-0011 carries its citations and states honestly when a Reader's marks take effect.
140 lines
7.9 KiB
Markdown
140 lines
7.9 KiB
Markdown
# ADR-0011: Sightings — a Reader report defers a Poll where being wrong hurts only them
|
|
|
|
Date: 2026-08-16
|
|
Status: accepted
|
|
|
|
## Decision
|
|
|
|
A **Sighting** is the Latest Chapter the Reader's own browser read off the
|
|
Series page and PUT to the backend. It is now allowed to stand in for a Poll,
|
|
under one restriction and one ceiling:
|
|
|
|
- **Solitary Series only.** A Sighting defers the Poll of a Series exactly one
|
|
Bookmark points at. A Series two Readers share is Polled on schedule no matter
|
|
how recently it was sighted.
|
|
- **One rest of standing.** A Sighting postpones Polls for one Rest
|
|
(`defaultRest`, an hour), not forever: a Series nobody visits again returns to
|
|
the normal schedule by itself.
|
|
- **Six-rest ceiling.** `sightingCeilingRests = 6`, counted in the Site's own
|
|
Rest — six hours everywhere today. However many Sightings arrive, a Series
|
|
unpolled that long is Polled.
|
|
|
|
Both live in the due query's HAVING clause (`store.DueForLatestCheck`), beside
|
|
the Rest cutoff — the same place the schedule has always been decided, so no
|
|
timer and no second code path can disagree with it.
|
|
|
|
Attribution and judgement:
|
|
|
|
- `Store.RecordSighting` runs *before* the Upsert that stores the reported
|
|
value, because the raise test needs the row as it stands. A report that raises
|
|
the stored Latest Chapter names its Reader in `series.latest_raised_by`.
|
|
- The Poll is the oracle. `Poller.checkOne` already compares what the Site
|
|
publishes against what is stored, so judgement costs no extra request: a lower
|
|
number contradicts the Sighting (`sighting_disagreements + 1`, both numbers and
|
|
the Reader logged), the same number confirms it (`sighting_agreements + 1`), a
|
|
**higher** number is the Site publishing and means nothing either way — but it
|
|
does clear the attribution (`Store.ClearSightingAttribution`), because the
|
|
value stored afterwards is the Poll's own and nobody must answer for it.
|
|
- At `SightingDisagreementLimit` (3) that Reader's Sightings stop deferring
|
|
anything. They still write the Latest Chapter — the penalty removes a
|
|
privilege, it does not silence anyone.
|
|
- `SightingAgreementsToClear` (20) consecutive confirmations forgive the
|
|
disagreements. A disagreement resets the run to zero.
|
|
- The owner clears marks from the administration page (issue #102, shipped
|
|
first precisely so a false mark has a remedy the day the mechanism lands).
|
|
|
|
One client change was required, and only one. Both userscripts stopped short of
|
|
PUTting a read whose number had not moved (`applyLatestChapterIfChanged`), so
|
|
the case this whole mechanism exists for — visiting a Series with nothing new —
|
|
never reached the backend. `reportLatestChapter` now sends it, skipping only the
|
|
local write and the re-render. A numberless PUT (favourite toggle, progress from
|
|
a chapter page) is not a Sighting and defers nothing: nobody read the Series
|
|
page, so there would be nothing to judge later.
|
|
|
|
## Why
|
|
|
|
Most of the backend's work was redundant. The userscript reads the Latest
|
|
Chapter on every Series page visit; minutes later the Poll Lane fetches the same
|
|
page for the same number. Deferring on a report converts a visit into a Poll
|
|
saved, which is Lane capacity handed back to Series nobody is
|
|
reading.
|
|
|
|
The restriction is the whole safety argument, and it is about **blast radius**,
|
|
not about trust arithmetic:
|
|
|
|
- On a solitary Series, a wrong report can only mislead the Reader who made it.
|
|
There is nobody else's ember to falsify.
|
|
- On a shared Series it could mislead someone else, so a report never postpones
|
|
anything there.
|
|
|
|
The ceiling bounds the damage in time: a false value dies within six hours
|
|
whatever happens, because the Poll that finds it is guaranteed. That is also
|
|
what makes lying pointless — the six-hour audit is certain, not sampled, so a
|
|
determined attacker buys at most three ceilings' worth of a wrong number on
|
|
their own Series and then loses deferral entirely.
|
|
|
|
The cost of recovery is deliberate. An agreement is only recorded when a later
|
|
Poll confirms a Sighting, so twenty agreements are twenty Polls of Series that
|
|
Reader bookmarks — hours to days of real time, not twenty page views. Waiting is
|
|
therefore not a strategy, and credit cannot be banked in advance.
|
|
|
|
## Tradeoffs and rejections
|
|
|
|
- **Trusting a Sighting on a shared Series** rejected: it is the only case where
|
|
one Reader's mistake reaches another Reader's list, and no amount of
|
|
reputation makes that recoverable within the six-hour window.
|
|
- **Cross-Reader agreement, voting, weighting, consensus scoring** rejected on
|
|
evidence: every truth-discovery method estimates source reliability by
|
|
comparing sources on the same object, and the standard survey states outright
|
|
that an object provided by very few sources cannot have its confidence
|
|
evaluated — Li, Gao, Meng, Li, Su, Zhao, Fan, Han, *A Survey on Truth
|
|
Discovery*, SIGMOD Record 45(1), 2016 (arXiv:1505.02463), §"Challenges" on
|
|
sparse sources. With the two Readers this backend actually has, a
|
|
disagreement is a coin flip. The Poll is an authoritative oracle, so it is
|
|
the only judge.
|
|
- **A randomised audit** (Poll a fraction of deferred Series) rejected in favour
|
|
of the fixed ceiling. Sampling an oracle against untrusted reports is the
|
|
gold-question technique from crowdsourcing quality control — Le, Edmonds,
|
|
Hester, Biewald, *Ensuring quality in crowdsourced search relevance
|
|
evaluation: the effects of training question distribution*, SIGIR 2010
|
|
Workshop on Crowdsourcing for Search Evaluation, which inserts known answers
|
|
sporadically and adjusts each worker's trust from them. The ceiling is the
|
|
same idea made deterministic: sampling prices an attack in expectation, a
|
|
guaranteed six-hour audit prices it as a certainty, which is what makes the
|
|
solitary-Series rule defensible in one sentence.
|
|
- **A trust *ratio*** (agreements over judgements, as that same gold-question
|
|
scheme uses) rejected for two thresholds: a ratio lets an attacker bank
|
|
credit first and spend it on lies later, and it needs the owner watching a
|
|
score to act. Three-and-twenty is a threshold both ways — a disagreement
|
|
resets the run to zero, so credit cannot be pre-bought, and recovery happens
|
|
without the owner in the loop.
|
|
- **Blocking a marked Reader's writes** rejected: the Latest Chapter they report
|
|
is still the best available value, and their Sightings must keep being judged
|
|
or they could never earn the privilege back.
|
|
- **Per-Series flagging** rejected in favour of per-Reader marks: a Series is
|
|
not the thing that can be wrong. Naming the Reader and logging both numbers is
|
|
also what distinguishes a broken Site adapter (every Reader of that Site
|
|
contradicted at once) from one bad actor.
|
|
- **Timers or a background reputation job** rejected: deferral is recomputed
|
|
from live facts every round — Bookmark count and sighting timestamp — so a
|
|
Series that gains a second Bookmark stops deferring at once, with nothing to
|
|
invalidate. The Reader's marks are the one input read earlier, when the
|
|
Sighting is recorded rather than when the round runs: a Reader who crosses
|
|
the threshold, or has their marks cleared, changes behaviour from their next
|
|
Sighting on, and the standing they already bought lasts out its rest. That is
|
|
bounded by one rest and costs one subselect instead of joining `readers` into
|
|
the due query on every round.
|
|
|
|
## Constraints preserved
|
|
|
|
- A Sighting is not Progress: it may move the Latest Chapter and nothing else.
|
|
`updated_at` never moves, so a report cannot reorder the list (ADR-0004).
|
|
- The Latest Chapter is a Series-level fact (ADR-0003): a Sighting writes the
|
|
shared row, so every Reader of a shared Series sees it immediately — deferral
|
|
is the only thing the solitary rule withholds.
|
|
- Ember means new chapter only (`docs/design-system.md`): a marked Reader
|
|
renders no differently in their own list, and nothing about the trust model
|
|
reaches the Series list's colour.
|
|
- The Poll remains authoritative. Where a Sighting and a Poll disagree, the
|
|
Poll's value is what gets stored.
|