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
11 changes: 6 additions & 5 deletions .claude/agents/trellis-check.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ Look for the `<!-- trellis-hook-injected -->` marker in your input above.
## Context

Before checking, read:
- `.trellis/spec/` - Development guidelines
- Task `task.json`'s `package` field (`legacy` or `app`) - picks the spec tree and the `AGENTS.md` below
- `.trellis/spec/<package>/` - Development guidelines
- Task `prd.md` - Requirements document
- Task `design.md` - Technical design (if exists)
- Task `implement.md` - Execution plan (if exists)
Expand Down Expand Up @@ -59,7 +60,7 @@ git diff # View specific changes

### Step 2: Check Against Specs and Task Artifacts

Read the task's prd.md, design.md if present, and implement.md if present, then read relevant specs in `.trellis/spec/` to check code:
Read the task's prd.md, design.md if present, and implement.md if present, then read relevant specs in `.trellis/spec/<package>/` to check code:

- Does it satisfy the task requirements
- Does it follow the technical design and implementation plan when present
Expand All @@ -79,15 +80,15 @@ After finding issues:

### Step 4: Run Verification

FMP is a Flutter app: "lint and typecheck" is `flutter analyze`. Run, in order:
FMP is a Flutter app: "lint and typecheck" is `flutter analyze`. Read the task's `package` from `task.json` first: `legacy` verifies against `lib/AGENTS.md`, `app` against `app/AGENTS.md`. Run, in order:

1. Codegen when a model or `*.i18n.json` changed, or `*.g.dart` is missing: `dart run build_runner build`, `dart run slang`. Stale codegen fails as a missing getter that looks like a source bug.
2. `dart format lib test tool`, then `flutter analyze`.
3. The tests for every changed area: the matching rows of AGENTS.md § Verification, plus the Quality Check section of each touched `.trellis/spec/<layer>/index.md`.
3. The tests for every changed area: the matching rows of the package's `AGENTS.md` § Verification / § 驗證, plus the Quality Check section of each touched `.trellis/spec/<package>/<layer>/index.md`.

If anything fails, fix it and re-run.

On-device verification is mandatory for user-visible changes (AGENTS.md), but it needs the emulator and the `verify-on-device` skill, which you cannot run. Never mark it passed: report it as required, with what to observe, so the main session runs it.
On-device verification is mandatory for user-visible changes (the package's `AGENTS.md`), but it needs the emulator and the `verify-legacy-on-device` skill (`legacy`) or `verify-on-device` skill (`app`), which you cannot run. Never mark it passed: report it as required, with what to observe, so the main session runs it.

---

Expand Down
11 changes: 6 additions & 5 deletions .claude/agents/trellis-implement.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,15 @@ Look for the `<!-- trellis-hook-injected -->` marker in your input above.

Before implementing, read:
- `.trellis/workflow.md` - Project workflow
- `.trellis/spec/` - Development guidelines
- Task `task.json`'s `package` field (`legacy` or `app`) - picks the spec tree and the `AGENTS.md` below
- `.trellis/spec/<package>/` - Development guidelines
- Task `prd.md` - Requirements document
- Task `design.md` - Technical design (if exists)
- Task `implement.md` - Execution plan (if exists)

## Core Responsibilities

1. **Understand specs** - Read relevant spec files in `.trellis/spec/`
1. **Understand specs** - Read relevant spec files in `.trellis/spec/<package>/`
2. **Understand task artifacts** - Read prd.md, design.md if present, and implement.md if present
3. **Implement features** - Write code following specs and task artifacts
4. **Self-check** - Ensure code quality
Expand Down Expand Up @@ -75,13 +76,13 @@ Read the task's prd.md, design.md if present, and implement.md if present:

### 4. Verify

FMP is a Flutter app: "lint and typecheck" is `flutter analyze`.
FMP is a Flutter app: "lint and typecheck" is `flutter analyze`. Read the task's `package` from `task.json` first: `legacy` verifies against `lib/AGENTS.md` § Verification, `app` against `app/AGENTS.md` § 驗證.

1. Codegen when a model or `*.i18n.json` changed, or `*.g.dart` is missing: `dart run build_runner build`, `dart run slang`.
2. `dart format lib test tool`, then `flutter analyze`.
3. The tests named by the matching rows of AGENTS.md § Verification, plus the tests you wrote.
3. The tests named by the matching rows of the package's § Verification / § 驗證, plus the tests you wrote.

If the change is user-visible, say so in the report: on-device verification is the main session's job (`verify-on-device` skill).
If the change is user-visible, say so in the report: on-device verification is the main session's job (`verify-legacy-on-device` skill for `legacy`, `verify-on-device` for `app`).

---

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
---
name: verify-on-device
name: verify-legacy-on-device
description: >-
Run FMP on the Android emulator or the Windows desktop build and verify a
Legacy-app hotfixes only (the app under `app/` uses the `verify-on-device`
skill). Run FMP on the Android emulator or the Windows desktop build and verify a
change against the live app: boot the emulator, install and launch, drive the
UI, read Dart logs, hot reload, and inspect runtime state. Use after every
change to UI pages or widgets, playback controls, source result rendering, or
layout-affecting strings — root AGENTS.md requires an on-device check for those
layout-affecting strings — `lib/AGENTS.md` requires an on-device check for those
before reporting — and whenever asked to run, screenshot, tap, type into, or
observe FMP on a device or emulator.
---
Expand Down Expand Up @@ -94,7 +95,7 @@ line in its output means the Windows accessibility tree has frozen (see
node with pixel and normalized centers:

```bash
PYTHONIOENCODING=utf-8 python .claude/skills/verify-on-device/scripts/ax_flatten.py --limit 30
PYTHONIOENCODING=utf-8 python .claude/skills/verify-legacy-on-device/scripts/ax_flatten.py --limit 30
```

Flutter's semantics surface through uiautomator, so labels, list rows and nav
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Narrator running. The engine answers through MSAA only (checked in
`flutter_windows.dll`, Flutter 3.47.1):

```bash
S=.claude/skills/verify-on-device/scripts
S=.claude/skills/verify-legacy-on-device/scripts
powershell.exe -NoProfile -ExecutionPolicy Bypass -File $S/msaa_tree.ps1 -Filter button
# [push button] '查看佇列' @(3156,1228 121x49) screen rect, physical px
powershell.exe -NoProfile -ExecutionPolicy Bypass -File $S/msaa_tree.ps1 -Click '查看佇列'
Expand Down Expand Up @@ -108,7 +108,7 @@ Read SMTC through WinRT, not the media flyout (the flyout dismisses on focus
change). FMP does not need to be visible:

```bash
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .claude/skills/verify-on-device/scripts/smtc_probe.ps1 -AppFilter fmp
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .claude/skills/verify-legacy-on-device/scripts/smtc_probe.ps1 -AppFilter fmp
```

It prints `IsNextEnabled` / `IsPreviousEnabled` / `IsPlaybackPositionEnabled` /
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
# Dump, with each element's screen rectangle in physical pixels:
#
# powershell.exe -NoProfile -ExecutionPolicy Bypass `
# -File .claude/skills/verify-on-device/scripts/msaa_tree.ps1 [-Proc fmp] [-Filter button]
# -File .claude/skills/verify-legacy-on-device/scripts/msaa_tree.ps1 [-Proc fmp] [-Filter button]
#
# Click an element by its exact accessible name (after raising the window):
#
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# System.Runtime.WindowsRuntime` fails there.
#
# powershell.exe -NoProfile -ExecutionPolicy Bypass `
# -File .claude/skills/verify-on-device/scripts/smtc_probe.ps1 [-AppFilter fmp]
# -File .claude/skills/verify-legacy-on-device/scripts/smtc_probe.ps1 [-AppFilter fmp]
#
# Exit codes: 0 = at least one session found, 1 = no sessions, 2 = WinRT failed.

Expand Down
12 changes: 12 additions & 0 deletions .trellis/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,18 @@ session_auto_commit: false
# Default package used when --package is not specified.
# default_package: frontend

packages:
legacy:
path: .
app:
path: app
default_package: app

# FMP: inject only the active task's package spec (default_package when no
# task is active). Without it SessionStart lists every package's spec.
session:
spec_scope: active_task

#-------------------------------------------------------------------------------
# Channel worker OOM guard
#-------------------------------------------------------------------------------
Expand Down
4 changes: 2 additions & 2 deletions .trellis/spec/guides/code-reuse-thinking-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ static rules. Writing a second copy is usually what the rule catches.
| Backend decisions both players share | `playback_end_reason_rules.dart`, `live_edge_seek_policy.dart`, `next_media_plan.dart` |
| Playlist provider refresh after a mutation | `libraryInvalidationCoordinatorProvider` |
| Duration text | `DurationFormatter` |
| Widgets: images, sliders, dialogs, menus, toasts, errors | the table in `../ui/widgets.md` |
| Test doubles and waits | `test/support/`, `test/support/fakes/` (`../testing/test-conventions.md`) |
| Widgets: images, sliders, dialogs, menus, toasts, errors | the table in `../legacy/ui/widgets.md` |
| Test doubles and waits | `test/support/`, `test/support/fakes/` (`../legacy/testing/test-conventions.md`) |

## When there are two near-copies

Expand Down
6 changes: 3 additions & 3 deletions .trellis/spec/guides/cross-layer-thinking-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ media bytes: StreamResolutionService ─► MediaHandoff ─► audio backend /
```

Each arrow is a contract. An error usually becomes a user sentence once, at the
edge (`userMessageFor` — see `../shared/errors-and-logging.md`); the import path
edge (`userMessageFor` — see `../legacy/shared/errors-and-logging.md`); the import path
translates earlier (`ImportService`, the playlist import sources).

## Changes that always fan out

### A new persisted setting

1. Field on `Settings` with a default; decide whether Isar's type default for old
rows is acceptable → migration step or not (`../data/persistence.md`).
rows is acceptable → migration step or not (`../legacy/data/persistence.md`).
2. `dart run build_runner build`.
3. Backup export + import, or an entry in `_deliberatelyExcludedSettingsFields`
with a reason.
Expand Down Expand Up @@ -48,6 +48,6 @@ relinked in the same transaction.

### Anything about credentials

Name which of the auth vocabulary terms (`services/download-and-auth.md`) applies: Stream Resolution Auth
Name which of the auth vocabulary terms (`../legacy/services/download-and-auth.md`) applies: Stream Resolution Auth
(adapter request) and Media Request Credentials (byte request, empty by
construction) are different arrows. Changing either needs the user's approval.
2 changes: 1 addition & 1 deletion .trellis/spec/guides/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ say *what else a change touches*.

## Quality Check

- A new error path stays typed until the user-facing edge and becomes a sentence there through `userMessageFor` / `failureMessage`. The existing exceptions (the import path translating early, `e.toString()` in some `state.error`) are listed in `../shared/errors-and-logging.md`; do not extend them.
- A new error path stays typed until the user-facing edge and becomes a sentence there through `userMessageFor` / `failureMessage`. The existing exceptions (the import path translating early, `e.toString()` in some `state.error`) are listed in `../legacy/shared/errors-and-logging.md`; do not extend them.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ and the shared foundation in `lib/core/` (errors, logger, constants, utils).

Neither directory imports `lib/services/` or `lib/providers/` (one recorded
exception, `lib/core/extensions/track_extensions.dart`, in
`test/support/layer_boundary_static_rule_test.dart`) — see AGENTS.md § Boundaries. Vocabulary for auth and media handoff is in `services/download-and-auth.md` § Auth vocabulary.
`test/support/layer_boundary_static_rule_test.dart`) — see lib/AGENTS.md § Boundaries. Vocabulary for auth and media handoff is in `services/download-and-auth.md` § Auth vocabulary.

## Guidelines

Expand All @@ -21,12 +21,12 @@ Cross-layer error and logging rules: [../shared/errors-and-logging.md](../shared

- [ ] Changing a source adapter → read `sources.md` and ADR 0001 (string source ids).
- [ ] Touching `Track` identity, `cid` or lyrics matching → read ADR 0005 and the identity section of `persistence.md`.
- [ ] Adding or changing a persisted field → read the `kFmpSchemaVersion` dartdoc in `lib/data/database/database_migration.dart` **before** editing the model. Changing persisted schema semantics needs the user's approval first (AGENTS.md § Conventions).
- [ ] Adding or changing a persisted field → read the `kFmpSchemaVersion` dartdoc in `lib/data/database/database_migration.dart` **before** editing the model. Changing persisted schema semantics needs the user's approval first (lib/AGENTS.md § Conventions).
- [ ] Anything that reaches Isar → it goes in `lib/data/repositories/` (ADR 0002).

## Quality Check

- Run the AGENTS.md § Verification rows that match the change: *Source adapters / HTTP policy*, *Isar models / migrations*.
- Run the lib/AGENTS.md § Verification rows that match the change: *Source adapters / HTTP policy*, *Isar models / migrations*.
- Codegen is gitignored: after a model change run `dart run build_runner build` before tests, or a missing getter looks like a source bug.
- New outbound host, `Timer.periodic`, header literal or cross-feature import → the static-rule test for it turns red; add the entry with a reason rather than working around the detector.
- No `isar.` outside repositories, no concrete adapter outside `SourceManager`, no credentials on media requests (see `sources.md`).
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ Points that bite:
- A new `Settings` field must also be exported and imported by backup, or listed
in `_deliberatelyExcludedSettingsFields` with a reason
(`test/services/static_rules/settings_backup_coverage_static_rule_test.dart`).
- Verify with the *Isar models / migrations* row of AGENTS.md § Verification; for
- Verify with the *Isar models / migrations* row of lib/AGENTS.md § Verification; for
risky schema work also run `test/manual/real_db_probe.dart` against a **copy**
of a real database.

Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@ Error and logging rules shared with other layers: [../shared/errors-and-logging.

## Pre-Development Checklist

- [ ] Audio change → read `audio.md` and ADR 0003 (two backends). UI calls `AudioController`, never `FmpAudioService`; radio is the one exception (AGENTS.md § Boundaries).
- [ ] Audio change → read `audio.md` and ADR 0003 (two backends). UI calls `AudioController`, never `FmpAudioService`; radio is the one exception (lib/AGENTS.md § Boundaries).
- [ ] Download / storage path change → read `download-and-auth.md` and ADR 0004.
- [ ] Anything that decides which credentials go where → read `download-and-auth.md` § Auth vocabulary first. Changing the auth boundary needs the user's approval (AGENTS.md § Conventions).
- [ ] Anything that decides which credentials go where → read `download-and-auth.md` § Auth vocabulary first. Changing the auth boundary needs the user's approval (lib/AGENTS.md § Conventions).
- [ ] A new repeating timer, outbound host or cross-feature import → plan the static-rule entry with its reason.

## Quality Check

- Run the AGENTS.md § Verification row that matches: *Audio playback/controller/queue*, *Source adapters / HTTP policy*, or *Download pipeline*.
- Playback controls or anything the user hears/sees → on-device check with the `verify-on-device` skill.
- Run the lib/AGENTS.md § Verification row that matches: *Audio playback/controller/queue*, *Source adapters / HTTP policy*, or *Download pipeline*.
- Playback controls or anything the user hears/sees → on-device check with the `verify-legacy-on-device` skill.
- New async code in a disposable service or notifier checks disposed / superseded / `ref.mounted` after an `await` that is followed by a state write. Existing code is uneven (the audio and download paths check; `ImportService` and many settings notifiers do not), so do not take an unchecked file as the pattern.
- Not gated — check by hand: the provider of a new service calls its `dispose` (see `service-conventions.md` § Disposal for the existing exceptions); no cookie or token in a new log line, and a stream URL goes through `logLabel` / `redactStreamUrl`; `Platform.is*` placement; a fire-and-forget future that can fail logs its error (see `service-conventions.md` § Async guards).
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

## Comments and dartdoc

- New and edited comments are Traditional Chinese (AGENTS.md § Conventions).
- New and edited comments are Traditional Chinese (lib/AGENTS.md § Conventions).
The tree still holds Simplified lines; convert only lines you are editing.
Log messages and identifiers are English, as are most exception messages and
test names. Exceptions: the playlist import sources throw translated messages on
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Testing (`test/`, `tool/`)

Applies to every test and to static-rule tests. Which tests to run for which
change is AGENTS.md § Verification; the full CI run is
change is lib/AGENTS.md § Verification; the full CI run is
`flutter test --exclude-tags live`.

## Guidelines
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
A static-rule test reads `lib/` source to hold a boundary that behaviour tests
cannot see. List them with
`find test -name '*_static_rule_test.dart'`; each file's top dartdoc is the
rule's rationale. AGENTS.md names only the ones an agent is most likely to trip.
rule's rationale. lib/AGENTS.md names only the ones an agent is most likely to trip.

Prefer a behavioural test when one can observe the rule (`20a96dc9`, `175e5d2a`
replaced static rules with behaviour tests).
Expand Down
10 changes: 5 additions & 5 deletions .trellis/spec/ui/index.md → .trellis/spec/legacy/ui/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ Stack: `flutter_riverpod` 3 written by hand (no codegen, no freezed, no hooks),

## Pre-Development Checklist

- [ ] Playback controls call `AudioController` (`ref.read(audioControllerProvider.notifier)`), never `FmpAudioService` — AGENTS.md § Boundaries.
- [ ] The search page's source chips are the only source selector — AGENTS.md § Boundaries.
- [ ] Playback controls call `AudioController` (`ref.read(audioControllerProvider.notifier)`), never `FmpAudioService` — lib/AGENTS.md § Boundaries.
- [ ] The search page's source chips are the only source selector — lib/AGENTS.md § Boundaries.
- [ ] Before writing a widget, check the shared-widget table in `widgets.md`; reuse wins over a new variant.
- [ ] A string that can change layout, or any user-visible change, needs on-device verification — plan for the `verify-on-device` skill.
- [ ] A string that can change layout, or any user-visible change, needs on-device verification — plan for the `verify-legacy-on-device` skill.

## Quality Check

- Run the AGENTS.md § Verification row *UI widgets/pages* (targeted `test/ui` tests + `flutter analyze`); for i18n JSON also `dart run slang`.
- On-device verification with the `verify-on-device` skill is mandatory for user-visible changes; report what was observed, or name the blocker.
- Run the lib/AGENTS.md § Verification row *UI widgets/pages* (targeted `test/ui` tests + `flutter analyze`); for i18n JSON also `dart run slang`.
- On-device verification with the `verify-legacy-on-device` skill is mandatory for user-visible changes; report what was observed, or name the blocker.
- Static rules that commonly fire here (all under `test/ui/static_rules/` or `test/providers/static_rules/`): image widgets, `ScopedSlider`, watch scope, error presentation, anchored providers, Equatable `props`. When one fires, follow its failure `reason:`.
- Not gated — check by hand: `ref.mounted` after awaits, `IconButton` tooltips, `AppRadius` / `AnimationDurations` instead of literals. (Using a `BuildContext` after an await without a `mounted` check is caught by `use_build_context_synchronously`, part of `flutter_lints`.)
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion .trellis/tasks/09-26-fmp-rewrite/task.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"status": "planning",
"dev_type": null,
"scope": null,
"package": null,
"package": "app",
"priority": "P2",
"creator": "1morr",
"assignee": "1morr",
Expand Down
Loading
Loading