11 KiB
Version routines Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Built-in routines can reset with a game's version, from a hand-kept, cited table of version timers; WuWa's three shop-pull lines are the first.
Architecture: A new src/shared/versions.ts holds VERSIONS, VERSION_DAYS and a pure
versionWindow(game, now). Built-in routines gain a { unit: "version" } period (never in the stored
PeriodSchema); taskStatus resolves it through versionWindow and reports it as scope cycle. The
About tab of a game in Your games shows the version row.
Tech Stack: Bun, TypeScript, zod, React 19, bun test.
Spec: docs/superpowers/specs/2026-10-22-version-routines-design.md
Global Constraints
- The stored
PeriodSchema,TaskSchema,gacha-tracker:v1:tasksand the export format do not change. - A routine resets only at a recorded
end; an estimate is displayed, never acted on (decision 3). - Version
start/endare ISO UTC instants;endis when the next version's stock appears. - WuWa 3.7:
end=2026-11-11T10:30:00Z,next="3.8";VERSION_DAYS.wuwa= 42. - Version routines are amount measures only; scope reported as
"cycle". - The period key is the start instant's UTC date, bare
YYYY-MM-DD(AmountLogkeys are free strings, butPinnedRoutineand the catch-up code compareperiodStartwith day keys, so a date stays comparable). - Captions (exact):
resets with 3.8 · 11 Nov;resets with next version (3.9) · expected ~23 Dec;resets with next version · expected ~23 Dec; past the estimate append, not yet confirmed. - Every figure cites an https page; an in-game figure's note says
Confirmed in game, YYYY-MM-DD.
Review Focus
- The moment of the switch: one minute before
end, the old count is shown; atendexactly, 0/7. Pinned in Task 1 and Task 2. - Past the estimate with no recorded end: the count holds and the key does not move. Task 1 tests the same key on 2027-02-01.
- A routine row with
daysLeftnull: the chip showsn/7with no· 0d. Task 2goalCounttest. - Catch-up "Last version:": without a
periodOffor version, the Dailies catch-up would throw. Task 2 skips amount catch-up for version items, with a test. - The timeline band for an open-ended version:
routinePeriodmust not throw or draw a zero-width band. Task 2 test: it ends at the estimate.
Task 1: Version table and window
Files:
- Create:
src/shared/versions.ts - Modify:
src/shared/period.ts(addRoutinePeriodSchema,RoutinePeriod) - Test:
test/versions.test.ts
Interfaces:
-
Produces:
period.ts:export const RoutinePeriodSchema = PeriodSchema.or(z.object({ unit: z.literal("version") }));export type RoutinePeriod.versions.ts:interface GameVersion(fields as the spec § Shape),VERSIONS: readonly GameVersion[],VERSION_DAYS: Partial<Record<GameId, number>>,interface VersionWindow { number?: string; key: string; start: number; end?: number; estimate?: number; nextNumber?: string },versionWindow(game: string, now: number, versions = VERSIONS, days = VERSION_DAYS): VersionWindow | undefined,versionCaption(w: VersionWindow, now: number): string(the exact caption strings; dates asasOfLabelminus the year, e.g.11 Nov).
-
Step 1: Write failing tests in
test/versions.test.ts:- table invariants over
VERSIONS: per game sorted, non-overlapping;end > start; recordedendequals the next entry'sstart; https source; both instants parse;checkedis a date. versionWindow("wuwa", Date.parse("2026-10-22T14:30:00Z"))→{ number: "3.7", key: "2026-09-30", end: Date.parse("2026-11-11T10:30:00Z"), nextNumber: "3.8", estimate: undefined }.- at
2026-11-11T10:29:00Z→ number"3.7"; at2026-11-11T10:30:00Z→{ number: "3.8", key: "2026-11-11", end: undefined, estimate: Date.parse("2026-12-23T10:30:00Z"), nextNumber: undefined }. - at
2027-02-01T00:00:00Z→ stillnumber: "3.8",key: "2026-11-11". - a fixture table with one entry and no
end→ window is that entry,estimate = start + 42d. versionWindow("genshin", now)→undefined; withdayslacking the game →estimateundefined.versionCaption:"resets with 3.8 · 11 Nov";"resets with next version (3.9) · expected ~23 Dec"(fixture withnext: "3.9");"resets with next version · expected ~23 Dec"; past the estimate →"…expected ~23 Dec, not yet confirmed"; no estimate →"resets with next version".
- table invariants over
-
Step 2: Run
bun test test/versions.test.ts— expect FAIL (module missing). -
Step 3: Implement. First entry per the spec § First entry. Before writing it, check a page for 3.7's start (Game8 / Fandom / Prydwen). If one states it, cite it; otherwise use
2026-09-30T10:30:00Zand say innotethat the start is 42 days before the in-game end. Comment onVERSION_DAYS.wuwaciting 3.7's length and AGENTS.md's six weeks. Window rules exactly as the spec § Shape. -
Step 4: Run
bun test test/versions.test.ts— PASS. -
Step 5: Commit
feat(versions): a cited table of version timers, and the window holding now.
Task 2: Routines can follow a version
Files:
- Modify:
src/shared/routines.ts(schema period →RoutinePeriodSchema),src/shared/task.ts(TaskShape.period: RoutinePeriod;taskStatus;taskCaption),src/shared/goal.ts(goalCount),src/client/state/reader/routines.ts,src/client/components/Dailies.tsx,src/client/components/redesign/TimelinePage.tsx,src/client/components/redesign/RoutinesPage.tsx,src/client/components/redesign/RoutineDrawer.tsx(period line), plus whatever elsebun run typechecknames. - Test:
test/task.test.ts,test/routines.test.ts,test/reader-routines.test.ts,test/redesign-routines.test.tsx
Interfaces:
-
Consumes: Task 1's
RoutinePeriod,versionWindow,versionCaption. -
Produces:
taskStatusacceptstask.period: RoutinePeriod; forversionit returnsscope: "cycle",periodStart: window.key,daysLeft/open=Math.ceil((end - now) / DAY)whenendis recorded, elsenull.taskCaption(task, now?)usesversionCaptionfor version. AroutineVersioned(r): booleanhelper inroutines.ts. -
Step 1: Write failing tests:
task.test.ts: a wuwa version routine (amount 7) withamounts = { "2026-09-30": { value: 3, at } }at 2026-10-22T14:30Z →count 3, required 7, scope "cycle", periodStart "2026-09-30", daysLeft 20; at2026-11-11T10:30Z→count 0, periodStart "2026-11-11", daysLeft null, open null.goalCount({ scope: "cycle", count: 3, required: 7, daysLeft: null, open: null, ... })→"3/7"; withdaysLeft: 20→"3/7 · 20d".routines.test.ts: every routine withperiod.unit === "version"hasmeasure.kind === "amount";RoutineSchemaaccepts{ unit: "version" };TaskSchemastill refuses it.reader-routines.test.ts: a version routine row hascadence "cycle".- Dailies catch-up renders no "Last version" editor for a version item (render test in the existing Dailies/edit tests' style).
routinePeriodfor an open-ended version row ends at the estimate; with a recorded end, atend.redesign-routines.test.tsx: Make it mine is absent for a version routine.
-
Step 2: Run those files — FAIL.
-
Step 3: Implement. In
taskStatus, branch onversionbeforedayKey.goalCount's cycle form drops· Ndwhen bothdaysLeftandopenare null. Cadence:version→"cycle". Dailies: amount catch-up returnsnullfor a version period.routinePeriod: version →[window.start, end ?? estimate ?? now + DAY]. RoutinesPage: hide Make it mine whenroutineVersioned. RoutineDrawer's read-only period line: the caption. Runbun run typecheckand fix every site it lists the same way (versionnever reachesperiodOf). -
Step 4: Run
bun testandbun run typecheck— all PASS. -
Step 5: Commit
feat(routines): a routine can reset with its game's version.
Task 3: WuWa's shop pulls
Files:
-
Modify:
src/shared/routines.ts(three entries after Tower of Adversity),test/routines.test.ts(the "each game's, as sourced" wuwa expectation) -
Step 1: Find the source. One page describing the shop's Tide stock (prefer one naming the shop and its currency). Record its date as
asOf,checked: "2026-10-22". TitleShop: <Tide>, or<Shop name>: <Tide>if the page names the shop. -
Step 2: Update the test expecting, after Tower of Adversity, the three rows with
{ unit: "version" }and{ kind: "amount", target: 7, unit: "<Tide name>" }; Radiant and Forgingpulls: "premium", Lustrous"standard". Run — FAIL. -
Step 3: Add the entries (ids
routine:wuwa:shop-radiant-tide,-forging-tide,-lustrous-tide; note endsConfirmed in game, 2026-10-22., ≤140 chars). Runbun test— PASS. -
Step 4: Commit
feat(routines): Wuthering Waves' shop pulls, each version.
Task 4: The version row in Your games
Files:
-
Modify:
src/client/components/redesign/GameSettings.tsx(About group, after the reset rows) -
Test:
test/game-settings.test.tsx -
Step 1: Failing test: WuWa's About tab at 2026-10-22T14:30Z shows a row
Version 3.7withends 11 Nov 2026(formatted withformatInZonein the reader's zone),(in 19d), a link to the source, and the note. At 2026-11-12 it showsVersion 3.8andnext version expected ~23 Dec 2026 (estimate). Genshin's About has no version row. -
Step 2: Run — FAIL. Step 3: Implement with
versionWindowand the existingtrowmarkup. Step 4: Run — PASS. -
Step 5: Commit
feat(settings): show a game's version timer on its About tab.
Task 5: Docs, browser check, changelog
- Step 1:
docs/DATA-MODEL.md(version period built-in only; amounts under the version's start date),docs/PRD.mdF17, AGENTS.md bullet (versions hand-kept and cited; estimates shown, never acted on; add each version's end when known), TODO.md (Oscillated Coral partly addressed; follow-ups from spec § Not in this change; "record WuWa 3.8's end"), spec status → shipped with commit range. Commitdocs: version routines. - Step 2:
bun run buildand drive the app (run skill): WuWa routines show0/7 · 20dand the caption; About shows the version row. Fix anything found. - Step 3: Changelog entry in
src/shared/changelog.tsin a follow-up commit naming the feature commits, matching recentchore(changelog): …commits;bun test test/changelog*.ts*.