feat(daily): ship daily detection off, and say it's experimental

Dailiness is guessed from the source's wording, which is a weak signal
and wrong in both directions. Guessing on a reader's behalf and hoping
they notice is the wrong default for a checklist they may build a streak
on, so `detectDaily` now starts false and the control carries an
Experimental tag saying what it does and doesn't know.

The default applies to new readers only — a stored `prefs` keeps its
value, because switching it off under someone who has been ticking
auto-detected checklists would pull those chips out of the strip with no
explanation. Nothing is deleted either way.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
Lucas Winther
2026-08-16 20:57:26 +02:00
co-authored by Claude Opus 5
parent 3a70672d22
commit 3434ac02cf
4 changed files with 28 additions and 10 deletions
+6 -3
View File
@@ -211,9 +211,12 @@ event's length. It adds **no schema field**, so the feed contract is untouched.
- **A repeating event the reader marked done leaves the strip.** They have said there is nothing
left to do; keeping a tickable chip for it is the app arguing with them. Their logged days are
untouched, so unmarking it brings the chip and the streak straight back.
- **Detection is a default, not a verdict.** The reader can mark any event as repeating, or unmark
one detection got wrong (`progress.daily`, resolved by `resolveDaily`), and `prefs.detectDaily`
switches the guessing off entirely. Store an override only when it *disagrees* with detection —
- **Detection is a guess, not a verdict — and it ships off.** `prefs.detectDaily` defaults to
`false` and the control is labelled experimental: wording is a weak signal and gets it wrong in
both directions, so a new reader opts in rather than out. The default moves nothing for an
existing reader, whose stored `prefs` wins. The reader can mark any event as repeating, or unmark
one detection got wrong (`progress.daily`, resolved by `resolveDaily`), whether the guessing is
on or off. Store an override only when it *disagrees* with detection —
recording agreement would freeze today's guess and stop a better parser from ever reaching that
event. Neither control ever deletes a mark or a logged day, so both are reversible.
+8 -1
View File
@@ -293,7 +293,14 @@ It adds no schema field, so nothing about the feed contract or the event ID chan
for one event and wins outright (`resolveDaily`); absent means they have not said, so detection
stands. `prefs.detectDaily` switches the guessing off altogether, leaving only events they marked
themselves — it silences detection rather than deleting anything, so every mark and every logged day
survives and switching it back on restores exactly what was there. An override that merely
survives and switching it back on restores exactly what was there.
**Detection is off by default and labelled experimental in the UI.** Wording is a weak signal and it
is wrong in both directions, so a new reader starts with only the standing `dailies:<game>` chores
and whatever they mark themselves; opting in is one checkbox. The default applies to new readers
only — a stored `prefs` keeps whatever value it has, because turning it off under a reader who has
been ticking auto-detected checklists would pull those chips out of the strip with no explanation.
An override that merely
agrees with detection is **not stored** (`dailyOverride`) — freezing today's guess into their data
would stop a later parser improvement from ever reaching that event. This is the only field in
`progress` that changes what the app *shows* rather than recording what the reader did, which is
+9 -5
View File
@@ -87,9 +87,9 @@ export function Controls({
</label>
{/* Detection reads the source's wording and is wrong in both
directions. Off leaves only the events the reader marked, and
discards nothing every mark and logged day survives, so it can
be switched back on. */}
directions, so it ships off and says so. Off leaves only the
events the reader marked, and discards nothing every mark and
logged day survives, so it can be switched back on. */}
<label className="flex cursor-pointer select-none items-start gap-2 text-xs text-muted">
<input
type="checkbox"
@@ -99,9 +99,13 @@ export function Controls({
/>
<span>
Spot daily events automatically
<span className="ml-1.5 rounded-full border border-hairline px-1.5 py-0.5 align-[1px] text-[0.5625rem] font-medium uppercase tracking-wider text-faint">
Experimental
</span>
<span className="mt-0.5 block max-w-xs leading-relaxed text-faint">
Off, only events you mark yourself get a checklist. Your ticks
and streaks are kept either way.
Guessed from what the source wrote, so it misses some and
invents others. Off, only events you mark yourself get a
checklist. Your ticks and streaks are kept either way.
</span>
</span>
</label>
+5 -1
View File
@@ -23,6 +23,10 @@ export interface Prefs {
* Whether to guess which events repeat daily from what the source printed.
* Off leaves only the ones the reader marked themselves; it never discards a
* mark or a logged day, so it is reversible.
*
* Off by default: the guess reads source wording and is wrong in both
* directions, so a reader starts with only the dailies they chose. Readers
* who already switched it on keep it — stored prefs win over this default.
*/
detectDaily: boolean;
showCompleted: boolean;
@@ -40,7 +44,7 @@ function defaults(): Prefs {
hiddenGames: [],
focusGame: null,
sort: "ending",
detectDaily: true,
detectDaily: false,
showCompleted: true,
showIgnored: false,
regionConfirmed: false,