Sightings: a Reader report defers a Poll of a solitary Series (#103) #108

Merged
sulthan merged 2 commits from feat/103-sightings into main 2026-08-16 20:11:00 +07:00
Owner

Closes #103.

A userscript PUT already carries the Latest Chapter the Reader's own browser read off the Series page. It may now stand in for a Poll, under one restriction and one ceiling:

  • Solitary Series only — a Series two Readers share is Polled on schedule however recently it was sighted, so one Reader's mistake can never reach another's list.
  • One rest of standing, and a six-rest ceiling (sightingCeilingRests, counted in the Site's own Rest): however many Sightings arrive, an unpolled Series is Polled.

Both live in the due query's HAVING clause (Store.DueForLatestCheck) — the same place the schedule has always been decided, so no timer and no second code path can disagree with it. No new query per scheduler round.

Judgement costs no extra request. Poller.checkOne already compares what the Site publishes against what is stored: a lower number contradicts the Sighting (Reader and both numbers logged), the same number confirms it, a higher number is the Site publishing and clears the attribution instead. Three contradictions stop that Reader deferring — their reports still write the Latest Chapter — and twenty consecutive confirmations forgive them, as does the owner's clear-marks control from #102.

One client change was required: both userscripts skipped the PUT when the number had not moved, so the case the whole mechanism exists for — visiting a Series with nothing new — never reached the backend. reportLatestChapter sends it, skipping only the local write and the re-render. A numberless PUT (favourite toggle, progress from a chapter page) is no Sighting and defers nothing.

Schema: migration 0011_series_sightings.sql adds series.latest_sighted_at and series.latest_raised_by. Trust model, thresholds, and rejected alternatives with their citations: docs/adr/0011-sighting-deferral-trust-model.md.

Reviewed on both axes (spec against #103, standards against the repo's rules); the blocker — attribution surviving a Poll that overtook the report — is fixed and has a test that fails without the fix.

Verification: go test ./... green (needs Docker), node --test userscript/test/*.test.js 66 pass.

Closes #103. A userscript PUT already carries the Latest Chapter the Reader's own browser read off the Series page. It may now stand in for a Poll, under one restriction and one ceiling: - **Solitary Series only** — a Series two Readers share is Polled on schedule however recently it was sighted, so one Reader's mistake can never reach another's list. - **One rest of standing**, and a **six-rest ceiling** (`sightingCeilingRests`, counted in the Site's own Rest): however many Sightings arrive, an unpolled Series is Polled. Both live in the due query's HAVING clause (`Store.DueForLatestCheck`) — the same place the schedule has always been decided, so no timer and no second code path can disagree with it. No new query per scheduler round. Judgement costs no extra request. `Poller.checkOne` already compares what the Site publishes against what is stored: a lower number contradicts the Sighting (Reader and both numbers logged), the same number confirms it, a higher number is the Site publishing and clears the attribution instead. Three contradictions stop that Reader deferring — their reports still write the Latest Chapter — and twenty consecutive confirmations forgive them, as does the owner's clear-marks control from #102. One client change was required: both userscripts skipped the PUT when the number had not moved, so the case the whole mechanism exists for — visiting a Series with nothing new — never reached the backend. `reportLatestChapter` sends it, skipping only the local write and the re-render. A numberless PUT (favourite toggle, progress from a chapter page) is no Sighting and defers nothing. Schema: migration `0011_series_sightings.sql` adds `series.latest_sighted_at` and `series.latest_raised_by`. Trust model, thresholds, and rejected alternatives with their citations: `docs/adr/0011-sighting-deferral-trust-model.md`. Reviewed on both axes (spec against #103, standards against the repo's rules); the blocker — attribution surviving a Poll that overtook the report — is fixed and has a test that fails without the fix. Verification: `go test ./...` green (needs Docker), `node --test userscript/test/*.test.js` 66 pass.
sulthan added 2 commits 2026-08-16 16:36:54 +07:00
A userscript PUT already carries the Latest Chapter the Reader's own browser
read; it now stands in for a Poll where being wrong can hurt nobody else.
Deferral lives in the due query beside the rest cutoff: one Bookmark, sighted
within one rest, under the six-rest ceiling. checkOne judges the Reader whose
report raised the value off the comparison it already makes - three
contradictions stop them deferring, twenty confirmations forgive.

ADR-0011 records the trust model and the rejected alternatives.
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.
sulthan merged commit ba679223b2 into main 2026-08-16 20:11:00 +07:00
Sign in to join this conversation.