docs: record check-in goals
The product, storage and design records now say what a goal is, why it never marks an event done, and why an unreadable goal is kept rather than stripped. That last rule is the one a future cleanup would most likely undo by mistake. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
3c668ab5c1
commit
d5ffad38c5
@@ -952,6 +952,10 @@ event's length. It adds **no schema field**, so the feed contract is untouched.
|
||||
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.
|
||||
- **A check-in goal makes an event a daily, and meeting it never makes the event done.** Some events
|
||||
want five days of seven (`progress.goal`, `src/shared/goal.ts`, PRD F12). A met week stays in the
|
||||
strip as done this week without a tick being invented; only the reader marks the event done. A
|
||||
goal this build cannot read is kept in storage and treated as none — never stripped on load.
|
||||
|
||||
## Events the reader entered themselves
|
||||
|
||||
|
||||
+21
-4
@@ -214,7 +214,7 @@ malformed row surfaces at the boundary rather than deep in the UI.
|
||||
Namespaced, versioned, and small. Nothing here ever goes to the server.
|
||||
|
||||
```ts
|
||||
"gacha-tracker:v1:progress" // { [eventId]: { status?, effort?, note?, at } }
|
||||
"gacha-tracker:v1:progress" // { [eventId]: { status?, effort?, daily?, note?, goal?, at } }
|
||||
"gacha-tracker:v1:daily" // { [id]: { days: ["2026-08-15", ...], at } }
|
||||
"gacha-tracker:v1:ignored" // { [eventId]: { at } } — "stop showing me this"
|
||||
"gacha-tracker:v1:prefs" // { region, hiddenGames[], knownGames[]?, gameOrder[]?,
|
||||
@@ -280,9 +280,19 @@ Namespaced, versioned, and small. Nothing here ever goes to the server.
|
||||
| `effort` | `"quick"` \| `"short"` \| `"long"` \| `"grind"` \| absent | How much work they reckon it is |
|
||||
| `note` | free text | Anything worth remembering |
|
||||
| `daily` | `true` \| `false` \| absent | Whether it repeats daily, overruling detection |
|
||||
| `goal` | `{ period: "week", measure: "days", required: 1–7, weekStartsOn: 1–7 }` \| `{ period: "event", measure: "days", required }` \| absent | A check-in goal: N distinct game-days per week or across the event (PRD F12) |
|
||||
|
||||
An entry with none of the three set is deleted rather than kept, so the store stays a set of things
|
||||
the reader actually said something about.
|
||||
An entry with none of these set is deleted rather than kept, so the store stays a set of things the
|
||||
reader actually said something about. `isEmpty` in `useProgress.ts` must name every field: one left
|
||||
out is a record marked only with that field, silently dropped on the next write.
|
||||
|
||||
**`goal` is read where it is used and never stripped.** It is stored typed `unknown` and read through
|
||||
`readGoal` (`src/shared/goal.ts`), which returns undefined for anything that does not parse. Nothing
|
||||
removes an unreadable goal from the record — the likeliest one is a newer shape (weekly tasks add
|
||||
`measure: "amount"`) met by an older cached bundle, and dropping it on load would delete it on the
|
||||
next write. A goal makes the event a daily regardless of `daily` and detection; setting one clears a
|
||||
stored `daily: false`, and any `daily` answer clears the goal, so the two never contradict in
|
||||
storage.
|
||||
|
||||
**`effort` is load-bearing, not decorative.** Combined with the time remaining it answers "can I
|
||||
still finish this?" — the same two days is comfortable for a `quick` event and hopeless for a
|
||||
@@ -324,6 +334,12 @@ counting toward `streakOf` and `dailySummary`, and is simply not drawn — the s
|
||||
outside the window the feed now claims. Nothing prunes the log against either window, because a
|
||||
fortnight's streak exists on that device and nowhere else.
|
||||
|
||||
**A check-in goal counts days in this same log; it stores no days of its own.** `goalStatus`
|
||||
recomputes from `daily` on every read, so a backfill, a removed tick or a week rolling over needs no
|
||||
bookkeeping and clears nothing. Weeks are cut in day-key space by `periodOf`
|
||||
(`src/shared/period.ts`): the seven keys from the chosen weekday, so a week rolls exactly where the
|
||||
game's day does, per region and per reset override. A day key later than today never counts.
|
||||
|
||||
**Not every game has a server per region.** `GameMeta.resetOffsets` records the regions where a
|
||||
game's server clock differs from `REGION_RESET_UTC_OFFSET`. Endfield is the case this exists for: it
|
||||
has two server groups rather than three, and Europe is served off the Americas machine on a fixed
|
||||
@@ -596,7 +612,8 @@ the earlier copy there discards every edit made after it, in both directions an
|
||||
restoring a backup taken before an evening's work would roll that evening back, and importing an old
|
||||
file onto a device with newer progress would roll the device back. Taking the maximum of the two
|
||||
timestamps keeps the merge order-independent and idempotent either way, and removes nothing.
|
||||
`mergeProgress` is pure and `test/progress.test.ts` pins it. An export written before daily checklists existed
|
||||
`mergeProgress` is pure and `test/progress.test.ts` pins it. Check-in goals ride `progress`, so they need no export key of their own
|
||||
and merge with the record they belong to. An export written before daily checklists existed
|
||||
simply has no `daily` key, which is not an error. Losing a user's marks to a bad import is unrecoverable, so the merge is
|
||||
deliberately one-directional. A file whose `format` is unrecognised is refused outright rather than
|
||||
partly applied.
|
||||
|
||||
@@ -404,6 +404,13 @@ selected tab fills with `raised` and turns ink; unselected tabs are faint and go
|
||||
- **State:** `aria-pressed` carries the state. On hover a chip lifts 1px and gains a 1px hue ring,
|
||||
and an unpressed chip's label goes to ink. On press it scales to 0.96. A chip with nothing waiting
|
||||
sits at 50% opacity: still reachable, but not competing for attention.
|
||||
- **Goal met, not ticked today** (a dailies chip whose event has a check-in goal): full-strength hue
|
||||
border, label and check — the check drawn dashed — on the *outstanding* ground, with no wash. The
|
||||
wash belongs to a real tick only, so "done this week" never reads as "done today". A goal chip's
|
||||
count is the goal's (`3/5 wk`) and replaces the streak; one counter per pill.
|
||||
- **Check-in goal control** (detail sheet, under the daily checklist): the status pills' style —
|
||||
Off / Each week / Across the event — then a small number field and, for weeks, a weekday select,
|
||||
both on `ground` with a hairline border like the notes field.
|
||||
|
||||
### Event row (signature)
|
||||
The unit of the checklist. It is one full-bleed target that opens the event and does nothing else.
|
||||
|
||||
+14
@@ -297,6 +297,20 @@ Three rules hold it honest:
|
||||
logged, keeps counting toward the streak and the totals, and is simply off-screen. Nothing removes a
|
||||
day the reader did not remove themselves.
|
||||
|
||||
**Some events want a number of days, not every day.** Genshin's “To Temper Thyself and Journey
|
||||
Far” asks for five check-ins out of seven each week. Ticking it as a daily calls two unticked days a
|
||||
failure and keeps it outstanding when the week is already done. So a repeating event can carry a
|
||||
**check-in goal**: N days each week (weeks starting on a weekday the reader picks, at the game's own
|
||||
reset), or N days across the whole event. It is always the reader's input — nothing is read off a
|
||||
title — and a goal makes an event a daily even with detection off.
|
||||
|
||||
- **A met week stays in the strip** as done this week, counted complete in the header, and comes
|
||||
back when the next week opens. No tick is invented for it, and only a real tick celebrates.
|
||||
- **Meeting a goal never marks the event done.** A count may not have claimed the rewards. An
|
||||
event-wide goal reached offers *Mark event done*; the reader decides.
|
||||
- **The target is never lowered.** A short first or last week keeps it and says when too few days
|
||||
are left; an unknown end leaves the days left unknown.
|
||||
|
||||
**F13 — Your own games and your own events.**
|
||||
No feasible adapter set covers everyone. Fourteen games were named in the first release thread and
|
||||
the reader with the largest collection asked for exactly one thing — *"can you add a custom game
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
**Date:** 2026-10-03, revised and approved 2026-10-13
|
||||
|
||||
**Status:** approved; spec 1 of 2. Spec 2 is
|
||||
**Status:** implemented 2026-10-13; spec 1 of 2. Spec 2 is
|
||||
`2026-10-13-periodic-tasks-design.md`, which reuses the period engine defined here.
|
||||
|
||||
## Request and intended outcome
|
||||
|
||||
Reference in New Issue
Block a user