From 2b0aa6bd0fe3c4bf21eead0dfc326a243a6aab48 Mon Sep 17 00:00:00 2001 From: Lucas Winther Date: Mon, 17 Aug 2026 17:20:14 +0200 Subject: [PATCH] docs(feedback): record the first public release thread MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Analysis of the 26-comment r/gachagaming thread from two days after the first Pages deploy, with every claim checked against the tree at 6fc3c0b. Two criticisms recur and only two: the game list is too short to be anyone's tracker, and the design reads as unfinished. Nobody questioned a single date, which is the thing most of this codebase's rules exist to protect. Recording it because the priority list it ends on is what the next commits act on — P1c, more games, best-source-first, one commit each. The saved thread it quotes is left untracked. Co-Authored-By: Claude Opus 5 (1M context) --- docs/FEEDBACK.md | 289 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 289 insertions(+) create mode 100644 docs/FEEDBACK.md diff --git a/docs/FEEDBACK.md b/docs/FEEDBACK.md new file mode 100644 index 0000000..a0c77c9 --- /dev/null +++ b/docs/FEEDBACK.md @@ -0,0 +1,289 @@ +# Community feedback — first public release + +Source: `FEEDBACK.HTML`, a saved copy of the r/gachagaming thread +*"[PROMO] I made an event tracker for those playing multiple gacha games at the same time, to know +which events to prioritize."* — 26 comments from 15 accounts (20 comments from 14 readers, 6 replies +from OP). Read 2026-08-17, two days after the first Pages deploy. + +Every quote below is from that thread. Every "what the code does today" note was checked against the +tree at `6fc3c0b`. + +--- + +## The one-paragraph read + +The idea landed and the execution did not get in the way: multiple people called it useful +unprompted, one bookmarked it, and **nobody questioned a single date** — which is the thing this +codebase spends most of its rules protecting. Two criticisms recur, and only two. First and loudest: +**the game list is too short to be someone's tracker.** Every reader who did not already play one of +the six covered games bounced off, politely. Second: **the visual design reads as unfinished**, and +one reader judged it from the screenshot alone and never opened the site. A third, narrower note is +already coming true — with two games selected the list is long enough to lose the point of the app. + +Nobody asked for accounts, sync, or a login. Nobody complained about mobile. Nobody mentioned the +daily-checklist feature at all, in either direction. + +--- + +## What works + +**The premise is the product.** Four separate readers described the problem in the app's own terms +before saying anything about the app: + +> "hmm this is actually what I need right now playing 4 games (3 hoyo + wuwa)." + +> "It's helpful for me at least since I play so many games and looks like I have to catch up with +> some of them." + +> "AI slop or not, this makes prioritizing across games much easier." + +The cross-game-prioritisation framing needs no explaining to this audience. Keep it as the headline. + +**Date accuracy drew zero complaints.** In 26 comments nobody said an end date was wrong, nobody +said an event was missing from a covered game, and nobody said a countdown looked off. For a +two-day-old scraper over six games that is the single best signal in the thread. The skip-never-guess +rule and the `endsAt: null` discipline are earning their keep — and this is also the thing that will +silently stop being true if the refresh pipeline stalls (see P0). + +**No one asked for an account.** Not one reader asked to log in, sync across devices, or save to a +profile — including the reader juggling ten games, who asked for *customisation*, not sync. The +no-accounts constraint costs nothing with this audience. + +**The AI-slop self-deprecation was forgiven, not shared.** Two readers went out of their way to say +it did not matter: + +> "I don't typically like the way AI is used these days, but I've always thought it had the potential +> to actually help us out. This is one of those cases, thanks for this man." + +That framing is a net negative to keep using, though — see P3. + +**The reader who wanted a specific interface wanted the one that already exists.** Asked what would +improve the UI, the one reader who criticised the design answered "i really like paimon.moe timeline +for GI." That is a horizontal Gantt per game with today pinned — which is exactly +`src/client/components/Timeline.tsx`, already built, already the shape the PRD specifies as F1. The +feature is done. Its discoverability is not. + +--- + +## What needs work, in priority order + +### P0 — Confirm the live site is serving live data + +**Why first:** the entire value proposition is that the dates are right *today*. Everything else in +this document is worthless if this is broken. + +**What I can see:** `snapshots/` contains exactly one page — `endfield-wikigg-events.html` — against +seven registered sources in `src/ingest/adapters/index.ts`. Snapshots are tracked in git on purpose +(`snapshots/README.md`), and `scripts/build-feed.ts` falls back to `fixtures/` when a source has no +snapshot. So the deployed feed is being built from **pinned fixture bytes captured 2026-08-14** for +Genshin, HSR, ZZZ, WuWa, NTE and the Game8 Endfield source — five of six games. Those fixtures were +correct when captured; a patch cycle is six weeks, so they will start being wrong within days, and +they will be wrong *confidently*, which is the exact failure mode the PRD calls the one to prevent. + +I cannot tell from a local checkout whether the six Game8 fetches are failing (robots check failing +closed, a 403, a template-mismatch throw) or simply have not committed a change yet. That is the +first thing to find out. + +**Steps:** + +1. Read the last few **Refresh sources** runs in Actions. The runner does not retry and fails closed + on an unreadable `robots.txt`, so a systematic Game8 failure would be visible per source there. +2. `bun run refresh --dry-run` locally to see the plan and the robots verdict per source without + making a request. +3. If Game8 is being refused, that is a scraping-conduct question before it is a code question — + re-read `CLAUDE.md` § Scraping conduct and decide, rather than working around it. +4. Regardless of cause: surface it in the UI. `Colophon.tsx` already receives `staleCount`, and + `App.tsx` computes `staleSources` at a two-day threshold. Verify a reader actually sees that + banner today, because if five of six games are on four-day-old fixtures, the app is currently + claiming more freshness than it has. +5. Add a CI assertion that fails the build when a source has neither a snapshot nor a fixture newer + than N days. A silent fallback to stale bytes should not be able to deploy. + +### P1 — Games: the only thing standing between this and daily use + +**The signal.** This is not one comment, it is the shape of the whole thread. + +> "Sadly I don't play any of those gacha games (despite juggling 10 games atm), will wait till more +> are added or we are able to customise it." + +> "This is awesome, bookmarked it. Hope it gets some more games added, like Reverse: 1999." + +> "Would there be option to add more games?" + +Named across the thread, excluding the six already covered: + +| Game | Mentions | Notes | +|---|---|---| +| Arknights | 2 | **`GameId`, hue and `dailyTasks` already exist in `games.ts` with no source registered.** Cheapest possible win. | +| Reverse: 1999 | 1 | Named as the one thing between a bookmark and a habit. | +| Azur Lane | 1 | OP judged doable | +| Blue Archive | 1 | OP judged doable | +| Umamusume | 1 | OP judged doable | +| Persona 5X | 1 | OP judged doable | +| Nikke | 1 | | +| Stella Sora | 1 | | +| Aether Gazer | 1 | | +| Chaos Zero Nightmare | 1 | | +| Girls' Frontline 2 | 1 | | +| Punishing: Gray Raven | 1 | | +| Guardian Tales | 1 | | +| Silver Palace | 1 | Unreleased — no schedule to scrape yet | + +**Steps:** + +1. **Arknights first.** Half the work is already committed: the `GameId` exists, so this is a + `SOURCES` entry, a fixture, and a test — no schema change, which is the PRD's own test of whether + the data model is right. It is also the most-named game in the thread. +2. Then work down the list **by source quality, not by mention count.** Prefer a page that publishes + machine-readable timestamps: `wikigg.ts` exists and is the only reason `regionEnds` carries real + data anywhere, so a wiki.gg or MediaWiki event page for a requested game is a `SOURCES` entry + against an existing parser. A Game8 page is a second choice; anything image-only is not a source. +3. Use the **add-game-source** skill per game — it covers the ToS/robots check, fixture capture, + registration and the test. One source per pass, verified against the live page, then commit. +4. Do not batch six games into one commit. Each adapter is a fixture and a test that has to prove + itself; a bad one publishes wrong dates for a game nobody was asking to be wrong about. +5. Say the roadmap out loud in the app. A reader who does not see their game currently sees no reason + to come back. A line in the colophon listing what is being worked on converts "will wait till more + are added" into a return visit. + +### P1 — The list gets long fast, and the default view should not be the long one + +**The signal.** The most specific, most actionable comment in the thread: + +> "I just selected Genshin and ZZZ and the list became really long. Once more games are in it's going +> to be a problem to try and focus in one specific game even with the filters. I feel it would be +> easier in the eyes if the current interface became a sort of 'Detailed' list view while the default +> interface focused on the three next closest events to their deadlines and it's on the user to check +> out these so they get filtered out as the player finish them." + +**He is measurably right.** Genshin's fixture holds 9 events and ZZZ's 12 — 21 rows for the +two-game case he tried, out of ~50 across six games. Doubling the game count doubles that. + +**What the code does today.** More of this is built than the comment assumes, which is good news: + +- `NextUp.tsx` already singles out the next event to expire — but exactly **one**, where he asked + for three. +- `GameFocus.tsx` (shipped 2026-08-16, after the thread) is precisely the "focus one specific game" + control he predicted needing. +- `outstanding()` in `state/lens.ts` already drops done and ignored events from the instruction + surfaces, which is the "they get filtered out as the player finish them" half. + +So this is a **default and framing problem**, not a missing-feature problem. + +**Steps:** + +1. Widen `NextUp` from one row to the next **three** outstanding deadlines. Keep it fed by + `outstanding` + `firstToExpire`'s ordering so it never points at finished work, and keep reading + the minimum end date rather than the head of the list — under "doing first" those differ. +2. Cap the "Running now" section at a handful of rows with an explicit "show all N" expander, rather + than rendering 21. Grouping stays as-is; this is truncation of the view, not a re-sort, so the + deadline-order guarantee in `CLAUDE.md` § Conventions is untouched. +3. **Persist the view.** `const [view, setView] = useState("soon")` in `App.tsx:68` is + component state, so every reload throws the reader back to the list even if they chose the + timeline last time. It belongs in `prefs` next to `sort` and `focusGame`. +4. Adopt his vocabulary. "Detailed" is a better name for the full list than the current split + implies, and it tells the reader the short view is the intended one. + +### P2 — The visual design is the only thing anyone criticised + +**The signal.** + +> "but from the screenshot the UI looks very bad :(" + +> "probably the colour or design style. It's true that I wasn't able to check out the whole site and +> only judged from the screenshot (as I said in the beginning)." + +Two things are true at once here: it is one person's opinion, and it was formed **entirely from the +promo screenshot** — the reader said so twice. That makes it partly a first-impression problem you +control completely, and it cost a visit from someone who was otherwise the exact target user. + +**Steps:** + +1. **Rename the "Calendar" tab to "Timeline."** The one reader who told you what good looks like + named paimon.moe's timeline, and OP had to *ask* him whether he had found the view — "Have you + checked out the calender view? :)" A reader hunting for a timeline does not read "Calendar" as + one, and the tabs are 12px text in the top-right corner. +2. **Consider making the timeline the default view.** `docs/PRD.md` F1 specifies the calendar view as + the default; the shipped app defaults to `"soon"`. The timeline is also the better screenshot and + the thing that reads as "designed" rather than "a list." If P1's three-deadline view lands, that + becomes a genuine three-way call — decide it deliberately rather than by inheritance. +3. **Reshoot the promo screenshot** before the next post: timeline view, three or four games with + overlapping bars, today's rule visible. The current one apparently showed the dense list. +4. Take one focused pass on colour. The palette carries two orthogonal axes on purpose — game hue + (`games.ts`) and urgency (`time.ts` / `URGENCY_COLOR`) — and seven saturated hues next to three + urgency colours is where "AI-generated default" reads from. The **frontend-design** skill is the + right tool; the constraint to hand it is that the two axes must stay separable at a glance, + because that separation is what lets one look answer both "whose event is this?" and "how long + have I got?" +5. Do not rebuild the interface. One person disliked the colours from a screenshot; several others + used the app and said it was helpful. This is a polish pass, not a rewrite. + +### P2 — Custom events and custom games: the tail no adapter list will ever cover + +**The signal.** Asked twice, independently, and it is the *only* feature request from the reader with +the largest collection of games: + +> "Or can you add a custom game option, we can input out own event description and time frames?" + +> "will wait till more are added **or we are able to customise it**" + +Fourteen games were named in one thread. No feasible adapter set covers a ten-game juggler, so this +is the feature that makes the app usable for readers you will never scrape for — and it needs no +scraping, no ToS question, and no server. + +**Steps, and the constraints they must respect:** + +1. Store user-authored events in `localStorage` alongside `progress` / `daily` / `ignored`, and add + them to `exportProgress` — a backup that silently omits hand-entered events is a lossy backup, the + same argument the code already makes for streaks. +2. **Never mint a user event's ID the standard way.** `${game}:${slug}:${date}` and `dailies:` + are live key spaces and a collision corrupts real marks. Give user events their own prefix, and + run the **schema-guardian** agent on the change — this is exactly the class of change + `CLAUDE.md` § Event IDs flags. +3. **Mark provenance in the UI.** A hand-entered date must be visibly the reader's own, never + attributed to a source, and must not flow into merge or sanitisation. The trust boundary at + `src/ingest/sanitize.ts` is for pages we do not control; this is a different path entirely. +4. A custom *game* needs a name and a colour but no adapter, and must not appear as a scraped game + with an empty feed. Note that `games` in `App.tsx:154` is derived from feed rows, so a custom game + needs threading through deliberately rather than added to `GAMES`. +5. This is currently **out of scope in `docs/PRD.md`** ("user-submitted events"). The thread is an + argument to change that decision — make the change in the PRD first, with the reasoning, rather + than letting the code drift from the spec. + +### P3 — Smaller notes worth acting on + +- **Drop the "AI-SLOP" framing from the next post.** It was volunteered, not extracted — and the one + criticism the thread produced was about visual polish, which is precisely what that framing invites + a reader to look for. The one reader who criticised the design judged the screenshot harshly + having been told in advance it was slop. The work stands on its own. +- **A non-English reader turned up on day one.** "Por fin algo que de verdad me sirve" — *finally + something that's actually useful to me*. Localisation is explicitly out of scope in the PRD and + should stay there for now; note only that dates and countdowns already localise through `Intl`, so + the copy is the whole cost when it becomes worth paying. +- **The "catching up" reader is a real second use case, but do not build for it yet.** Three readers + described being behind rather than being busy: "I have to catch up with some of them," + "I haven't touched Wuwa, zzz, HSR and arknights in a long time," and a third reader's plan to play + one gacha in concentrated bursts. The `status` ("partway through") and `effort` fields plus the + "doing first" sort already serve this. Resist adding a "you haven't played X in N days" nudge: + `CLAUDE.md` draws a hard line between what the app *shows* and what it *tells you to do*, and + nagging someone about a game they consciously dropped is the app arguing with them. +- **The daily-checklist feature got no signal at all** — not one comment, positive or negative, in 26. + It shipped detection-off and experimental two days ago, which is the right posture. Do not invest + further until someone mentions it; one reader's alternative was a notepad + ("while i simply put mine in a notepad. i'm playing 6 games"). + +--- + +## Suggested order of work + +1. **P0** — diagnose the refresh pipeline; make stale data loud and un-deployable. *Hours.* +2. **P1a** — Arknights adapter (`GameId` already exists). *One pass of `add-game-source`.* +3. **P1b** — three deadlines in `NextUp`, cap the long list, persist `view`, rename Calendar → + Timeline. *Small, all in the client, all directly requested.* +4. **P1c** — two or three more games, best-source-first, one commit each. Publish the roadmap in the + colophon. +5. **P2** — colour and screenshot pass; then decide the default view deliberately. +6. **P2** — custom events, starting with the PRD decision and a schema-guardian review. + +Everything in P0 and P1b is a change to code that already exists. That is the cheapest half of this +list and it addresses the two loudest complaints in the thread.