From 1deb0cb4cba0b48d461c70fd3f005a214d1de28f Mon Sep 17 00:00:00 2001 From: Lucas Winther Date: Tue, 13 Oct 2026 12:57:37 +0200 Subject: [PATCH] docs: settle check-in goals and specify weekly and monthly tasks The goals draft stopped at three open questions; the owner answered them, and widened the scope to weekly and monthly chores with numeric targets. Both share one period engine, so they are specified together and built goals-first. Co-Authored-By: Claude Opus 5.5 --- .../specs/2026-10-03-check-in-goals-design.md | 308 ++++++++++-------- .../specs/2026-10-13-periodic-tasks-design.md | 152 +++++++++ 2 files changed, 332 insertions(+), 128 deletions(-) create mode 100644 docs/superpowers/specs/2026-10-13-periodic-tasks-design.md diff --git a/docs/superpowers/specs/2026-10-03-check-in-goals-design.md b/docs/superpowers/specs/2026-10-03-check-in-goals-design.md index 0a4fd0d..946f349 100644 --- a/docs/superpowers/specs/2026-10-03-check-in-goals-design.md +++ b/docs/superpowers/specs/2026-10-03-check-in-goals-design.md @@ -1,152 +1,204 @@ # Event check-in goals -**Date:** 2026-10-03 +**Date:** 2026-10-03, revised and approved 2026-10-13 -**Status:** proposed; not implemented +**Status:** approved; 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 Some events require a fixed number of check-ins rather than participation every day. The owner's example is Genshin's “To Temper Thyself and Journey Far”: five completions -out of seven days in a week. This example is the requested use case, not a verified -source schedule or a rule to hardcode by title. +out of seven days in a week. The event must stay in Dailies while unfinished, show how +many check-ins the week needs, and distinguish meeting this week's requirement from +finishing the event. -An unfinished event must remain available in Dailies. The tracker should show how many -check-ins are required, count the reader's recorded days, and distinguish meeting this -week's requirement from completing the whole event. +Checked against the feed: the event runs Monday 2026-08-10 to Monday 2026-11-02, twelve +whole weeks, and Game8 describes it as “Daily and Weekly Training Goals”. Genshin's week +rolls on Monday at the server's daily reset. So weeks that start on a chosen weekday at +the game's reset fit it. The required count is not published in our sources, which is +one reason a goal is reader input rather than parsed. -## Current behavior and the gap +## Decisions taken -`src/shared/daily.ts` detects whether an event repeats and counts recorded game-days, -remaining opportunities, missed days and streaks. It has no required-count or weekly -goal. `DailyChecklist.tsx` reports recorded days against the event's whole window, -which cannot express “five of seven this week.” - -`progress.daily` already lets the reader put an event in Dailies when source wording -does not identify it. `App.tsx` supplies live, outstanding repeating events to -`Dailies.tsx`; explicitly done or ignored events are excluded. A weekly goal must fit -this flow without treating a satisfied week as a permanent event completion. - -## Proposed scope - -Let the reader configure an optional check-in goal in the event detail sheet: - -- **Across this event:** a required number of distinct game-days in the event window. -- **Each week:** a required number of distinct game-days in each seven-day period, - with an explicitly chosen week-start day. - -The weekly mode covers the reported example. The event-wide mode covers login campaigns -that require a smaller number of days than the full time available. Neither mode -requires consecutive days; streaks remain a separate fact. - -Setting a goal makes the event eligible for Dailies even with experimental detection -off. Removing the goal returns to the existing detection and override behavior. The -existing “this isn't a daily event” action must offer to remove the goal too, rather -than leave a conflicting configuration hidden in storage. - -Goals are reader input in this first version. No parser changes, inferred counts, -title-specific exceptions, or changes to `GachaEvent` are proposed. - -## Counting and period boundaries - -Count at most one check-in per game-day, using the existing recorded day keys and the -event's game and selected server region. All calculations take `now` explicitly. -Future days never count toward a goal. - -A weekly period starts on the chosen weekday at the game's server reset, not browser -midnight or UTC midnight. Periods are seven game-days, intersected with the event's -actual start and end. An event-wide goal uses the event's resolved window, including -the existing handling of day-precision dates and region-specific ends. - -Ticks outside the applicable window do not satisfy its goal, but remain stored and -editable through the existing history controls. Catch-up and corrections recalculate -the affected period; no week transition clears, rewrites or prunes recorded days. - -A shortened first or last week retains the stated target. If fewer opportunities -remain than check-ins needed, show that fact without lowering the target or inventing -extra days. An unknown event end remains unknown; a configured weekly rule can still -define the current week's boundary without claiming an event end. - -## Dailies and completion behavior - -| State | Dailies behavior | +| Question | Answer | |---|---| -| Weekly goal unfinished | Keep the event visible; show, for example, `3 / 5 this week` and today's tick | -| Weekly goal met | Keep the unfinished event visible as `5 / 5 · Done this week`; stop presenting further check-ins as required | -| Next week opens | Recalculate against the new period and offer today's check-in again | -| Event-wide goal met | Show `5 / 5 · Goal reached`, with an explicit action to mark the event done | -| Event marked done or ignored | Exclude it from Dailies under the existing outstanding rule | -| Event expired | Follow the existing live-event rule; retain its goal and recorded history | +| Who sets a goal? | The reader, per event, in the detail sheet. Nothing is inferred from a title or source | +| Modes | **Each week**: N distinct game-days per week. **Across the event**: N distinct game-days in the whole window | +| Week boundary | A chosen ISO weekday, at the game's server reset for the reader's region. Defaults to the weekday of the event's first game-day | +| Numeric targets (`1900 / 2100`) | The goal carries `measure` from day one. This spec ships `measure: "days"` only; spec 2 adds `"amount"` and its store | +| Does meeting a goal finish the event? | Never. Only the reader marks an event done | +| Does a met week stay in Dailies? | Yes, as done-this-week, until the reader marks the event done | +| `GachaEvent`, IDs, day-log keys, storage keys | Unchanged | -A goal never writes `status: "done"` automatically. Reaching a check-in count might -not finish other objectives or claim the rewards. Only the reader's explicit event -completion removes a live event from Dailies. Undoing completion brings it back with -its goal and ticks intact. +## The period engine — `src/shared/period.ts` -The detail checklist keeps past days editable, including after a target is reached. -Undoing a tick can make a goal unfinished again. For goals, describe remaining -opportunities and check-ins needed; do not label every unticked past day as a failure -when participation on every day was never required. +Pure, takes `now` explicitly, and works in **day-key space** like `streakOf` and +`catchUpDays`: every day is a `YYYY-MM-DD` key already cut on the game's reset clock by +`dayKey`, so period boundaries inherit each game's per-region reset with no new clock. -Dailies' daily completion count and celebration must treat a satisfied goal as having -no further work required today. This must not fabricate a tick for today. A real tick -and a satisfied period remain separate states in accessible labels and rendering. +```ts +type Period = + | { unit: "week"; startsOn: 1 | 2 | 3 | 4 | 5 | 6 | 7 } // ISO weekday + | { unit: "month"; startsOn: number }; // 1–31, used by spec 2 -## Storage and implementation boundaries +/** The period containing `today`, as its first key and its day keys, oldest first. */ +periodOf(period: Period, today: string): { start: string; days: string[] }; +``` -Store an optional goal alongside the reader's per-event progress. The proposed shape -is a discriminated Zod schema with a positive integer `required` and either -`period: "event"` or `period: "week"` plus `weekStartsOn` (ISO weekday 1–7). Weekly -targets are limited to 1–7; event-wide targets must not exceed a known opportunity -count at configuration time. Existing goals remain readable if source dates later -change, with any resulting shortfall disclosed rather than silently discarded. +- **Week:** the ISO weekday of a key is its UTC weekday (keys are dates, not instants). + Step back `(weekday − startsOn + 7) % 7` days from `today`; take seven keys. +- **Month** (spec 2's consumer, built and tested here because it is the same function): + the most recent key whose day-of-month is `startsOn`, clamped to the month's last day + when the month is shorter (31 → 30 Sep, 28/29 Feb). The period runs to the day before + the next start. +- Callers get `today` from `dayKey(now, region, game)`, so “immediately before reset” + and “at reset” fall in different periods exactly where the game's day rolls. -Missing goal means today's behavior. No existing event ID, daily-log key, game-day -format, reset mapping or localStorage key changes. Preserve a progress record that -contains only a goal; include the new field in emptiness checks and export/import. -Import follows the existing last-edited progress merge and daily-log union rules. -Validate the goal without deleting unrelated status, notes or recorded ticks when -an imported goal is invalid. +## Goals — `src/shared/goal.ts` -Keep goal calculations pure in shared code. Wire the result into `App.tsx`, -`Dailies.tsx`, `DailyChecklist.tsx`, and `EventDetail.tsx`; extend the progress store -and backup validation. Reuse the shipped visual tokens and existing detail controls. -Custom event occurrence IDs keep their existing meaning; a goal applies to the -selected occurrence unless applying it to an entire recurring rule is separately -specified. +Zod is the source of truth for the type: + +```ts +// A plain union, not discriminatedUnion("period"): spec 2 adds amount variants that +// share a period with these, so no single field discriminates. +const GoalSchema = z.union([ + z.object({ period: z.literal("week"), measure: z.literal("days"), + required: z.number().int().min(1).max(7), + weekStartsOn: z.number().int().min(1).max(7) }), + z.object({ period: z.literal("event"), measure: z.literal("days"), + required: z.number().int().min(1).max(366) }), +]); +type Goal = z.infer; + +/** A stored value as a Goal, or undefined when absent or unreadable. Never throws. */ +readGoal(stored: unknown): Goal | undefined; + +goalStatus(input: { + goal: Goal; startsMs: number; endsMs: number | null; + region: Region; game: LaneId; now: number; logged: readonly string[]; +}): GoalStatus; + +interface GoalStatus { + scope: "week" | "event"; + required: number; + count: number; // distinct ticked keys in scope, never later than today + met: boolean; // count >= required + needed: number; // max(0, required - count) + open: number | null; // unticked days in scope from today on; null when unknowable + shortfall: boolean; // open !== null && needed > open +} +``` + +Counting rules: + +- **Scope.** Week: `periodOf` for today, intersected with the event's days — `dailyDays` + when the end is known, otherwise every key from the event's first game-day. Event: + `dailyDays`; with an unknown end, every key from the first game-day to today, and + `open` is null rather than a guess. +- **One per game-day**, from the existing day log. A key later than today never counts. + A tick outside scope never counts and is never removed. +- **A short first or last week keeps its target.** If too few days remain, `shortfall` + says so; the target is not lowered and no day is invented. +- Catch-up and corrections need no special handling: status is recomputed from the log on + every render, so backfilling a previous week cannot move this week's count. + +**Validated where used, never stripped from storage.** `readGoal` returns undefined for a +missing or unreadable goal and the record is left exactly as stored. Dropping an invalid +field on load would delete it from the device on the next write — the hazard AGENTS.md +§ Events the reader entered themselves describes — and the likeliest unreadable goal is a +newer shape (spec 2's `amount`) read by an older cached bundle. Setting a goal in the UI +overwrites whatever was there. + +## Storage + +`Progress` gains `goal?: unknown` in storage, read through `readGoal`. Concretely: + +- `isEmpty` treats a present `goal` as recorded, so a goal-only record survives. +- Export and import need no new key: goals ride `progress`, and `mergeProgress` already + keeps the later-edited record whole. Old backups lack the field and remain valid. +- No ID, day-log key, game-day format, reset mapping or localStorage key changes. + +## Dailies eligibility + +- An event whose `readGoal` is defined is a daily, whatever detection or `progress.daily` + says. App computes `daily = goal !== undefined || resolveDaily(...)`. +- **Setting a goal** clears a stored `daily: false` (the two would contradict) and leaves + any other `daily` value alone, so removing the goal later restores exactly the earlier + detection and override behaviour. +- **“This isn't a daily event”** clears the goal in the same patch, and its confirmation + copy says so when a goal is set. No conflicting state is left hidden in storage. +- `outstanding` is unchanged: done or ignored events leave the strip regardless of display + preferences; undoing either restores the event with its goal and ticks. + +## What the reader sees + +**Detail sheet — “Check-in goal”** under the daily checklist, and offered on non-daily +events too (setting one makes the event a daily). Three choices in the style of the +existing status and effort controls: **Off** · **Each week** `[5] days, weeks start +[Monday]` · **Across the event** `[5] days`. Weekly `required` is 1–7; event-wide is +capped at the known number of days when the end is known. + +**Checklist caption** when a goal is set replaces the “missed” wording, because an +unticked past day is not a failure when every day was never required: + +- Weekly: `3 of 5 this week · 2 needed · 4 days left this week` +- Event-wide: `3 of 5 · 2 needed · 9 days left`, or `3 of 5 · 2 needed` with an unknown end +- Shortfall: `2 needed, 1 day left this week` +- Met: `5 of 5 · Done this week` / `5 of 5 · Goal reached`. An event-wide goal reached + shows **Mark event done** beside it, which uses the existing status control. + +Past days stay editable after a target is reached; removing a tick can unmeet it. + +**Dailies chip.** Gains a short count: `3/5 wk` or `3/5`. Tapping still ticks today. +Three visually distinct states: + +| State | Look | Accessible label suffix | +|---|---|---| +| Not done today, goal unmet | Outstanding chip as now, plus count | `, not done today, 3 of 5 this week` | +| Goal met, not ticked today | Full-strength hue border and check, outstanding background | `, done this week` / `, goal reached` | +| Ticked today | Done chip as now, plus count | `, done today` | + +**Header count and celebration.** In `Today's dailies · n/total`, an item is complete when +ticked today **or** its goal is met — no tick is fabricated. The burst fires only when the +number of items **ticked today** rises and everything is complete, so meeting a week by +backfilling an earlier day completes the strip quietly, as backfilling does today. + +## Testing + +- `period.ts`: week starts for every `startsOn`, across month and year ends; month starts + with clamping; Genshin keys immediately before and at reset for Asia, America and + Europe, and a game with a reset override (`resetHourLocal`). +- `goal.ts`: acceptance cases 1–7 below as pure tests, including future ticks, ticks + outside scope, partial first and last weeks, shortfall and unknown ends; `readGoal` on + missing, malformed and future-shaped values. +- Progress: goal-only records survive `patch`; `mergeProgress` keeps the later record's + goal; export → import round trip keeps goals; an old export imports unchanged. +- Dailies: `dailyGroups`/item completion with goals; the burst rule; labels. +- Full offline suite and typecheck; inspect the built page in headless Chrome. ## Acceptance checks -1. A configured five-per-week event appears in Dailies with detection off and no - explicit completion. At three distinct ticks it reads `3 / 5 this week`. -2. Five nonconsecutive days satisfy the week. The event remains visible, labelled - done this week, without being marked done or requesting a sixth check-in. -3. The next weekly boundary makes the goal available again; previous ticks remain - unchanged. Test immediately before and at reset for each supported region. -4. Backfilling a previous week never advances this week's count. Removing a current - tick below the target restores the unfinished state. -5. A five-check-in event-wide goal in a seven-day window reaches its target on the - fifth recorded day and waits for explicit event completion. -6. Done and ignored events remain excluded regardless of list-display preferences; - undoing either restores the recorded goal and days while the event is live. -7. Partial weeks, unknown ends, future ticks, region-specific boundaries and games - with reset overrides count correctly without invented dates or deleted history. -8. Goal-only progress survives reload and export/import. Old backups remain valid; - invalid goals do not erase otherwise valid reader data. -9. Events with no goal retain current daily behavior, grouping, order and catch-up. -10. Accessible labels distinguish done today, done this week, and event completion. +1. A five-per-week event with detection off appears in Dailies; three distinct ticks read + `3/5 wk`. +2. Five non-consecutive days meet the week: still visible, labelled done this week, not + marked done, counted complete in the header. +3. The next week boundary offers the event again; earlier ticks are unchanged. +4. Backfilling a previous week never moves this week's count; removing a current tick + below target restores the unmet state. +5. A five-day event-wide goal in a seven-day window is reached on the fifth day and waits + for an explicit Mark event done. +6. Done and ignored events stay excluded; undoing either restores goal and ticks. +7. Partial weeks, unknown ends, future ticks, regional resets and reset overrides count + correctly with no invented date and no deleted history. +8. Goal-only progress survives reload and export/import; old backups import; an + unreadable goal leaves the record intact and behaves as no goal. +9. Events with no goal behave exactly as before. +10. Accessible labels distinguish done today, done this week and goal reached. -Before shipping, update PRD F12 and the data-model sections for progress, daily -checklists and export/import. Update the design record for any new visible controls. -Run relevant unit tests, the full offline suite and typecheck, and inspect the built -interface in a browser. +## Docs that change with it -## Decisions to confirm before implementation - -- Verify the example's actual weekly reset and whether its requirement is per week - or per another in-game training period. Do not infer this from the event title. -- Confirm that choosing a week-start weekday covers the desired games. If a source - resets on another clock or uses event-anchored seven-day periods, specify that - boundary explicitly before supporting it. -- Confirm the proposed event-wide mode and the choice to retain satisfied goals in - Dailies until the reader marks the whole event done. +PRD F12 (check-in goals paragraph); DATA-MODEL § Client-side storage `progress` table and +key comment, § Daily checklists, § Export format; AGENTS.md § Events that repeat daily (a +goal makes an event a daily; meeting one never marks it done); DESIGN.md for the +goal-met chip state and the goal control. diff --git a/docs/superpowers/specs/2026-10-13-periodic-tasks-design.md b/docs/superpowers/specs/2026-10-13-periodic-tasks-design.md new file mode 100644 index 0000000..f809978 --- /dev/null +++ b/docs/superpowers/specs/2026-10-13-periodic-tasks-design.md @@ -0,0 +1,152 @@ +# Weekly and monthly tasks, with numeric targets + +**Date:** 2026-10-13 + +**Status:** approved; spec 2 of 2. Builds on `2026-10-03-check-in-goals-design.md` (the +period engine and goals) and ships after it, in two phases. + +## Request and intended outcome + +Games have chores that roll weekly or monthly rather than daily — earning ZZZ's weekly +activity points, completing Endfield's weekly routine. The reader wants to set these up +per game, see them beside the dailies, and track either a plain “done this period” or a +number against a target: `1900 / 2100`. + +The owner's direction on where they come from: **reader-authored as the base**; the app +can also ship known tasks per game; when the reader turns a game on, the app asks whether +to use that game's automatic weekly and monthly tasks, and a toggle governs it; and a +built-in task can be copied into the reader's own tasks to edit. + +The recurring-events spec (2026-08-27) deferred “weekly task lists” to its Phase C. This +is that phase for the reader's side; it adds no `GachaEvent` field and nothing to ingest. + +## Decisions taken + +| Question | Answer | +|---|---| +| What is a task? | A per-game chore with a period (week or month) and a measure (days or amount). Not an event: no window, no end, no status | +| Measures | `days`: N distinct game-days ticked in the period (N = 1 is “done this period”). `amount`: a number the reader enters against a `target` | +| Where amounts live | A new store, one value per item per period | +| Where ticks live | The existing day log, keyed by the task id — union merge and export already work | +| Reader-authored ids | `mytask:` | +| Built-in ids | `routine::` | +| Built-ins on by default? | No. Per-game opt-in, asked once when the game is turned on, and a toggle in settings | +| Editing a built-in | Not in place. “Make it mine” copies it to a reader task and hides the original | +| Amount goals on events | Yes: spec 1's goal gains `measure: "amount"` variants, using the same store and editor | +| Phasing | **2a** reader tasks, amounts, amount goals. **2b** built-in routines, opt-in prompt and toggle, make-it-mine | + +## Model + +`src/shared/task.ts`, Zod as source of truth: + +```ts +const MeasureSchema = z.union([ + z.object({ kind: z.literal("days"), required: z.number().int().min(1).max(31) }), + z.object({ kind: z.literal("amount"), target: z.number().positive(), + unit: z.string().max(24).optional() }), // "points", "activity" +]); +const TaskSchema = z.object({ + id: z.string(), // mytask: + game: z.string(), // LaneId; z.string() for the retirement rule + title: z.string().min(1).max(80), + period: PeriodSchema, // from period.ts: week+startsOn | month+startsOn + measure: MeasureSchema, + from: z.string().optional(), // routine id this was copied from (2b) + at: z.string(), updatedAt: z.string(), +}); +``` + +`game` is `z.string()`, not `GameId`, for the reason `CustomEvent.game` is: narrowing it +arms every future game removal to delete reader rows on load. `days.required` is capped by +the period's length when edited (7 for a week). + +`taskStatus({ task, region, now, logged, amounts })` returns `{ periodStart, count?, +value?, met, needed }` from `periodOf` and the stores. Pure; `now` explicit. + +Spec 1's `GoalSchema` gains two variants: `{ period: "week", measure: "amount", target, +weekStartsOn }` and `{ period: "event", measure: "amount", target }`. An event-scoped +amount uses the event's first game-day as its period key. + +## Storage + +| Key | Shape | Merge on import | +|---|---|---| +| `gacha-tracker:v1:tasks` | `{ [mytask id]: Task }` | By id, later `updatedAt` wins; never removes | +| `gacha-tracker:v1:amounts` | `{ [itemKey]: { [periodStartKey]: { value, at } } }` | Per item **and** period, later `at` wins; never removes | +| `daily` (existing) | task ticks under the task id | Existing union of days | +| `prefs.routineGames` (2b) | `string[]` — games whose built-ins are on | Prefs rules as today | +| `prefs.routinesAsked` (2b) | `string[]` — games already asked about | Prefs rules as today | + +- `itemKey` is a task id or an event id. A new period starts at no value; old periods are + never pruned. Deleting a task removes the task record only — its ticks and amounts stay, + as every log in this app does. +- Loading reads through a `validRecords`-style filter **for display only**; a task that + fails its schema is not shown but is not dropped from storage. +- `tasks` and `amounts` are additive export keys: older exports lack them and import + unchanged; `parseImportData` passes them through for validation by the stores. +- `mytask` and `routine` become reserved first segments alongside `dailies`, `mygame`, + `myevent`; the existing test that no reserved segment is a `GameId` covers them. + +## What the reader sees + +**Dailies strip.** Weekly and monthly items sit in their game's group, after that game's +dailies, keeping the rule that a game's items stay together and that grouping never +re-sorts. + +- `days` chip: tap ticks today, like a daily. Label `Weekly boss · 1/1 wk`, or `3/5 wk`. +- `amount` chip: `ZZZ activity · 1900/2100`, with a thin progress underline in the hue. + A chip is one target, so tapping it **opens an inline editor under the strip** — + `Activity points this week: [1900] / 2100 · Save` — rather than ticking. Enter saves, + Escape cancels; the value is where the reader is now, not an increment. +- Met: the spec 1 met state, `2100/2100 · Done this week` / `· Done this month`. +- Header: `Today's dailies · 3/7` keeps counting dailies only; weekly and monthly items get + their own count beside it (`Weekly 1/2 · Monthly 0/1`, each shown only when present). + The celebration stays about today's dailies. +- Catch up: a `days` task shows its current and previous period's days as pips; an + `amount` task shows last period's value as an editable field. +- Accessible labels: `ZZZ activity points, 1900 of 2100 this week`, `…, done this week`. + +**Amount goals on events.** The detail sheet's goal control gains “Points” beside days: +`[2100] points each week` or `in total`; the checklist area shows the value and the same +editor. + +**Settings — “Your own”.** A tasks list beside your games and events: add, edit, delete. +Fields: game, title, every week (start weekday) or every month (start day), and either +“tick N days” or “reach a number” (target, optional unit). Weekly start defaults to +Monday; monthly to the 1st. + +### Phase 2b — built-in routines + +- `src/shared/routines.ts`: per-game templates with the same fields plus `source` (URL) + and `checked` (date). **Only a task a cited source documents ships**, the bar + `dailyTasks` in `games.ts` is held to; the owner's examples (ZZZ weekly activity, 2100; + Endfield weekly routine, 10) are the first candidates and are verified, not assumed. +- **Asking.** When a game with built-ins becomes visible — first-run picker, or switched on + in settings — and is not in `routinesAsked`, a one-line card at the top of the dailies + strip asks: `Track Zenless Zone Zero's weekly and monthly tasks? Yes · No thanks`. Either + answer records the game in `routinesAsked`; Yes also adds it to `routineGames`. Never a + modal, and never asked twice. +- **Toggle.** The games settings group shows a “Weekly tasks” switch on each game that has + built-ins, writing `routineGames`. Off hides the chips; their ticks and amounts stay. +- **Make it mine.** From a built-in chip's editor or the settings list: copies the template + into a `mytask:` record with `from` set, and copies the built-in's ticks and amounts to + the new id (additive; the originals stay). A built-in whose id is some task's `from` is + hidden; deleting that copy brings it back. + +## Testing + +- `task.ts`: status for days and amounts, week and month, at period boundaries per region. +- Stores: merge rules per table above; tasks failing schema are hidden, not dropped; + export/import round trip including old exports. +- Dailies: placement after a game's dailies, separate weekly/monthly count, editor + save/cancel, no celebration from weekly items. +- 2b: prompt asked once per game, toggle, make-it-mine copying and hiding, routine data has + a source on every entry (a test). +- Full suite, typecheck, browser inspection. + +## Docs that change with it + +PRD: a new feature entry for weekly and monthly tasks; DATA-MODEL: the two keys, the prefs +fields, reserved segments and export keys; AGENTS.md § Events the reader entered +themselves (tasks join the reader-authored spaces) and the dailies grouping rule; DESIGN.md +for the amount chip and editor; the 2026-08-27 spec's Phase C note pointing here.