Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .claude/skills/cleardb/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,17 @@ launch — deleting the file is equivalent to clearing all tables.)
`pgrep -af "task_roulette|dev.sh"`. Kill the matched PIDs
(`kill <pids>`). This ends the `./dev.sh` hot-reload session too — that's
expected; a full restart is needed for the empty DB to take effect anyway.

**Pre-flight: read the binary's PATH before you kill anything.** The DB path
is keyed to the app ID, so **every** checkout — including
`~/projects/personal/app-worktrees/<name>/` worktrees — shares this ONE
database. `pgrep -af` prints the full path; check which checkout each match
actually came from. If a running app (or any `dev.sh`) belongs to a
**different worktree/branch** than the one you're working in, or if you find
**more than one** `dev.sh`, **stop and tell the user before killing or
deleting** — name the worktree, and confirm they're fine with that session's
app being killed and its data wiped. Another session may be mid-test against
that data, and neither the kill nor the clear is scoped to your checkout.
3. **Back up first (safety, always).** This is a dev machine — the local DB is
often the only copy. Copy before deleting:
`cp -v "$DB" "$DBDIR/task_roulette.db.preclear_$(date +%Y%m%d_%H%M%S)"`.
Expand All @@ -42,5 +53,9 @@ launch — deleting the file is equivalent to clearing all tables.)
or `shared_preferences.json` (app settings, not task data).
- Don't run this while a release/manual test that depends on existing data is
mid-flight without confirming — clearing is only for a deliberate clean slate.
- **One DB, all checkouts.** There is a single DB per app ID, so a clear hits
every worktree, not just the current one. Never assume the running app is
yours — verify via the process path (see step 2) and confirm with the user
when it isn't.
- To restore: `cp "$DBDIR/task_roulette.db.preclear_<ts>" "$DBDIR/task_roulette.db"`
(with the app stopped), then relaunch.
9 changes: 7 additions & 2 deletions .claude/skills/manual-test/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ Generate a checklist of manual tests the user should run for recent changes.
- **Always state the Inbox toggle state per task.** For EVERY task the user creates where the Inbox toggle is shown — at root level on All Tasks, via the Starred FAB, the **create-from-empty-search** flow (All Tasks search *and* Today's 5 "Pick existing task"), or any future surface that shows it — explicitly say whether Inbox should be **ON** or **OFF** for that specific task (e.g. "tap + FAB, **turn Inbox OFF**, type 'Container'" or "tap + FAB, **keep Inbox ON (default)**"). The toggle defaults ON, so a created-at-root/Starred task with no Inbox instruction is genuinely ambiguous — it changes whether the task lands in the Inbox section or the main list. State it per task in the setup, not just once. Tasks created *inside a parent* (drilled in) don't show the Inbox toggle, so no Inbox mention is needed there.
- **Inbox-ON tasks are root-level — so never assert "at root" alone.** Inbox tasks *are* at the root, so an expected outcome of "the task is at root" is ambiguous and will read as a bug when the task correctly lands in the Inbox. When a test's expected result is where a created task ends up, distinguish the two root buckets explicitly: an Inbox-ON task lands "**in the Inbox section**"; an Inbox-OFF root task lands "**in the main root list (not Inbox)**". This is the outcome-side twin of the rule above: state the Inbox toggle in the action *and* make the expected result name the correct bucket. (Orthogonality note: pinning to Today's 5 is independent of Inbox membership — a task can be both in the Inbox and pinned, so "in Inbox + pinned" is a valid, non-contradictory outcome.)
- **Bulk fixture creation — use "Add multiple" (brain dump), not N separate adds.** When a setup needs several tasks at the same level, tell the user to use the **"Add multiple"** flow (**+ FAB → "Add multiple" → one task name per line → Add**) — do NOT ask them to repeat the single-add flow N times. It's available on the **All Tasks root** add and the **Starred FAB** (the Today's 5 "Create new task" flow hides it). State the **Inbox** toggle **once for the whole batch** (it applies to every line). Fall back to individual single adds only when tasks need **different per-task settings** the batch can't express (mixed Inbox states, per-task pin/deadline, or nesting under different parents).
- **State the expected starting state** before the first test — and by default that is a **clean slate via `/cleardb`** (see the `/cleardb` rule below). Tell the user to run `/cleardb` and relaunch `./dev.sh` before Section 1 unless there is an explicit reason to keep existing data (in which case say so, and explain why the existing data is safe — e.g. "Your existing tasks/pins won't interfere — these tests create new tasks.").
- **State the expected starting state** before the first test — and by default that is a **clean slate via `/cleardb`** (see the `/cleardb` rule below). **Never present a `/cleardb` as a done deal — ASK first** (see the rule below for how); the user may have data they need. If there is an explicit reason to keep existing data, say so and explain why it's safe — e.g. "Your existing tasks/pins won't interfere — these tests create new tasks."
- **"Go to root" is the breadcrumb, NOT the back arrow.** On All Tasks these are two different controls (see the All Tasks section of `docs/UI_VIEWS.md`): the app bar's **back arrow pops exactly one level**, while the **breadcrumb strip below the app bar** jumps straight to any ancestor — tapping its **"Task Roulette"** crumb is the one-tap way to reach root. So never write "back arrow to root": it is wrong from two-plus levels deep, and it only appears to work one level down. Write **"tap 'Task Roulette' in the breadcrumb to return to root"**, or, when the point is genuinely to go up a single level, "tap the back arrow once (up one level)". Name the exact control whenever a step involves changing level.
- **Never open a step with a bare imperative that summarizes it — every instruction must be actionable on the spot.** A step that begins "Fill Today's 5, then check the Pin chip disappears" reads as a command the user is expected to carry out, but names no method — they stop and ask "how do I fill Today's 5?". Anything phrased as an order IS an order. If you want to state the step's purpose up front, mark it unmistakably as a label — `*Goal:* the pin toggle must vanish once there's no free slot` — or, better, drop the preamble and open with the first concrete tap, letting the expected result carry the intent. Never let a summary and an instruction share the same imperative voice. Same rule for mid-step conditions: "with Today's 5 full, …" must be preceded by the taps that fill it.
- **A negative or elsewhere outcome MUST come with the navigation to go check it.** Never assert "no duplicate was created", "no task named X exists", "the count is unchanged", or "it did NOT appear in Y" without telling the user *where to look and how to get there* — after the final action they are usually parked on some other screen (a leaf detail view, a drilled-in parent, another tab), where the thing to verify simply isn't visible. Spell out the return trip and what to count. Bad: "no duplicate (still exactly one 'Bravo leaf' at root) and no task named 'qqq'". Good: "tap the back arrow until you're at the All Tasks root list → scan it: exactly one 'Bravo leaf' row, and no 'qqq' row anywhere (check the Inbox section too)". The same applies to absence-on-another-surface claims — say "switch to the Starred tab → no card for it" rather than just "not starred". Absence is only verifiable somewhere specific; name that place.
- **Tests that verify a refresh or side-effect MUST spell out the mutation steps.** If a test asks the user to "switch to tab X and confirm Y reflects the latest state", you must include the exact mutation that produces that new state — never assume the user knows what "recently completed", "recently edited", or "the latest" means in context. Bad: "switch to Today → Today's 5 reflects latest state". Good: "On All Tasks, tap any Today's 5 task → tap 'Done today' → switch to Today tab → that task now appears under the Done section". This rule applies to all "X refreshes on entry", "Y reloads on tab switch", "Z reflects external changes" tests.

5. Prioritize tests by risk:
Expand Down Expand Up @@ -93,7 +96,9 @@ D2. Reach [populated / overflowing state] → [what to look at] → does it look
- **Never include sync/auth tests when running on test data.** The user uses test data on their dev machine — don't suggest signing in, triggering sync, or testing sync status. Only include sync tests if the change is sync-specific AND the user explicitly says they want to test sync.
- If a change is purely algorithmic with no UI impact, say so and focus tests on observable outcomes — and skip the Design checks section (there's nothing new to look at).
- **Design checks are their own section and follow the batching rule** — present them one at a time like any other section, after (or alongside) the functional sections, and don't advance until the user has judged every visual case in the batch. They belong ONLY when a screen's look actually changed; a pure logic/data change gets none. When the user is unsure about a state, proactively offer to look via `/check-screenshot` and compare it to what the design was meant to be — do not vouch for a screen you haven't seen.
- **Prefer a clean-slate `/cleardb` before the round starts — ONCE, not per-section.** Unless there is an explicit reason to keep the existing data, recommend the user run **`/cleardb`** (then relaunch `./dev.sh`) **before Section 1** so the whole round runs against an empty, predictable DB — existing pins/tasks won't interfere with setup or outcomes. Do this **once per round, before the first section only** — do NOT ask for a `/cleardb` before Section 2, Section 3, etc.; later sections build on the same fresh DB plus whatever earlier sections created. `/cleardb` is `disable-model-invocation` and destructive (it backs up first, stops `./dev.sh`, and needs a relaunch), so **you cannot run it yourself** — instruct the user to run it. Reasons to skip/defer it (call them out explicitly): the change is specifically about **existing-data / migration / sync-merge** behavior, the test needs pre-existing fixtures that only live in the current DB, or the user says to keep their data.
- **Prefer a clean-slate `/cleardb` before the round starts — ONCE, not per-section.** Unless there is an explicit reason to keep the existing data, recommend the user run **`/cleardb`** (then relaunch `./dev.sh`) **before Section 1** so the whole round runs against an empty, predictable DB — existing pins/tasks won't interfere with setup or outcomes. Do this **once per round, before the first section only** — do NOT ask for a `/cleardb` before Section 2, Section 3, etc.; later sections build on the same fresh DB plus whatever earlier sections created. `/cleardb` is `disable-model-invocation` and destructive (it backs up first, stops `./dev.sh`, and needs a relaunch), so **you cannot run it yourself** — the user runs it.

**ASK before any `/cleardb` — never just instruct.** Whenever you're about to recommend one, put the question to the user with **`AskUserQuestion`** (not plain text) and let them decide, offering at minimum: **clear it** (clean slate, backed up first), **keep the data** (you adapt the setup steps to coexist with what's already there), and where useful **back up only**. Their local DB is often the only copy of real data, and only they know whether it matters right now — so this is their call, not a default you apply on their behalf. Say plainly what a clear costs them (it stops the running app, needs a `./dev.sh` relaunch, and is restorable from the timestamped `.preclear_*` copy) so the choice is informed. If they decline, do NOT re-ask in later sections — adapt the checklist and move on. Reasons to skip/defer it (call them out explicitly): the change is specifically about **existing-data / migration / sync-merge** behavior, the test needs pre-existing fixtures that only live in the current DB, or the user says to keep their data.
- Mention which platform to test on (Linux via `./dev.sh` unless the change is mobile-specific).
- **Present tests in batches by section.** Don't dump all test cases at once — show one section at a time (e.g. "Today's 5" tests first, then "All Tasks" after the user reports results). This prevents the list from feeling overwhelming and lets the user focus. **This applies to the caller too** — when presenting the agent's results, you MUST show only the first section and hold back the rest until the user reports results. Never flatten all sections into one list or present them all at once, even as a "summary". **Do NOT move to the next section until the user has reported results for ALL test cases in the current section.** If the user reports on only some tests (e.g. "1. works" but there are 10 tests), re-display the remaining tests from that section — do NOT advance to the next section.
- **Snackbar undo tests are time-sensitive.** The undo snackbar only lasts 5 seconds. Rules for undo tests:
Expand Down
Loading