mirror of
https://github.com/zapstore/zapstore.git
synced 2026-10-05 12:38:24 +00:00
Improve AI guidelines
This commit is contained in:
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
../../spec/guidelines/ARCHITECTURE.md
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
---
|
||||||
|
description: Flutter/Dart conventions for Zapstore projects
|
||||||
|
globs: ["**/*.dart", "**/pubspec.yaml"]
|
||||||
|
---
|
||||||
|
|
||||||
|
# Flutter/Dart Conventions
|
||||||
|
|
||||||
|
## State Management
|
||||||
|
|
||||||
|
- Use Flutter Hooks (`HookWidget`, `HookConsumerWidget`) for local widget state — not `StatefulWidget`.
|
||||||
|
- Use `useState`, `useAnimationController`, `useEffect` over manual `State` classes.
|
||||||
|
- Use Riverpod providers for shared/global state. Keep providers focused and composable.
|
||||||
|
- Prefer `ref.watch` for reactive UI, `ref.read` in callbacks only.
|
||||||
|
|
||||||
|
## Data & Async
|
||||||
|
|
||||||
|
- All async work must be cancellable. Pass `CancellationToken` or use `ref.onDispose`.
|
||||||
|
- Never `await` inside `build()`. Move async work to providers or `useEffect`.
|
||||||
|
- Use `switch` on sealed classes / union types for exhaustive state handling:
|
||||||
|
```dart
|
||||||
|
return switch (state) {
|
||||||
|
StorageLoading() => const CircularProgressIndicator(),
|
||||||
|
StorageError(:final exception) => Text('Error: $exception'),
|
||||||
|
StorageData(:final models) => MyWidget(models),
|
||||||
|
};
|
||||||
|
```
|
||||||
|
- All async operations must have explicit loading, success, and error states in the UI.
|
||||||
|
|
||||||
|
## Widget Structure
|
||||||
|
|
||||||
|
- One widget per file. File name matches widget name in snake_case.
|
||||||
|
- Keep `build()` methods short — extract sub-widgets or use helper methods.
|
||||||
|
- Widgets must not manage relay connections, storage, or background jobs.
|
||||||
|
- Use `const` constructors wherever possible.
|
||||||
|
|
||||||
|
## Naming
|
||||||
|
|
||||||
|
- Files: `snake_case.dart`. Classes: `PascalCase`. Variables/methods: `camelCase`.
|
||||||
|
- Providers: `<noun>Provider` or `<noun>NotifierProvider`.
|
||||||
|
- Services: `<Noun>Service`. Notifiers: `<Noun>Notifier`.
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
- Never swallow exceptions silently. Surface errors to the UI or log them.
|
||||||
|
- Use typed exceptions where the caller needs to distinguish error types.
|
||||||
|
- `try/catch` at the boundary (provider/service), not deep in domain logic.
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
- Test providers and services, not widget internals.
|
||||||
|
- Use `ProviderContainer` for unit-testing Riverpod providers.
|
||||||
|
- Mock external dependencies (storage, network) — no real I/O in unit tests.
|
||||||
|
- Widget tests for critical UI states: loading, error, empty, success.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- Run `fvm flutter pub get` after any `pubspec.yaml` change.
|
||||||
|
- Pin Flutter SDK version via FVM (`fvm use <version>`).
|
||||||
|
- Prefer packages already in use over adding new ones.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
- Assume Android as default target unless instructed otherwise.
|
||||||
|
- Release builds must be reproducible (see INVARIANTS.md).
|
||||||
|
- Run `flutter analyze` — fix all issues before committing.
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
../../spec/guidelines/INVARIANTS.md
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
../../spec/guidelines/QUALITY_BAR.md
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
../../spec/guidelines/VISION.md
|
||||||
@@ -1 +0,0 @@
|
|||||||
CONTEXT.md
|
|
||||||
@@ -1,17 +1,8 @@
|
|||||||
# AI Agents
|
# Zapstore — Agent Instructions
|
||||||
|
|
||||||
This document is the entry point for AI assistants.
|
Local-first, Nostr-native app store for Android.
|
||||||
All behavioral authority lives in the project spec, not here.
|
|
||||||
|
|
||||||
If anything in this file conflicts with files under `spec/guidelines/`,
|
All behavioral authority lives in `spec/guidelines/`. If this file conflicts, guidelines win.
|
||||||
this file is wrong.
|
|
||||||
|
|
||||||
## What This Repository Is
|
|
||||||
|
|
||||||
Zapstore is a local-first, Nostr-native app store for Android.
|
|
||||||
It discovers, downloads, verifies, and installs APKs signed by developers the user trusts.
|
|
||||||
|
|
||||||
Users can support developers directly via Lightning zaps.
|
|
||||||
|
|
||||||
## Quick Reference
|
## Quick Reference
|
||||||
|
|
||||||
@@ -22,51 +13,29 @@ Users can support developers directly via Lightning zaps.
|
|||||||
| Quality standards | `spec/guidelines/QUALITY_BAR.md` |
|
| Quality standards | `spec/guidelines/QUALITY_BAR.md` |
|
||||||
| Product vision | `spec/guidelines/VISION.md` |
|
| Product vision | `spec/guidelines/VISION.md` |
|
||||||
| Feature specs | `spec/features/` |
|
| Feature specs | `spec/features/` |
|
||||||
| Active work | `work/` |
|
| Active work | `spec/work/` |
|
||||||
|
| Decisions & learnings | `spec/knowledge/` |
|
||||||
|
|
||||||
## Project Spec Structure
|
Guidelines are symlinked into `.cursor/rules/` and auto-load.
|
||||||
|
|
||||||
spec/
|
|
||||||
guidelines/ # Permanent rules (human-owned, never AI-modified)
|
|
||||||
ARCHITECTURE.md # Package boundaries, dependencies, key patterns
|
|
||||||
INVARIANTS.md # Non-negotiable behavioral guarantees
|
|
||||||
QUALITY_BAR.md # Standards, when to create specs
|
|
||||||
VISION.md # Product goals and non-goals
|
|
||||||
|
|
||||||
features/ # Feature specs (behavioral contracts)
|
|
||||||
_TEMPLATE.md # Template with example
|
|
||||||
FEAT-001-*.md # Actual feature specs
|
|
||||||
|
|
||||||
work/ # Active work packets (temporary, delete after merge)
|
|
||||||
_TEMPLATE.md # Template with example
|
|
||||||
WORK-001-*.md # Actual work packets
|
|
||||||
|
|
||||||
## How to Work
|
|
||||||
|
|
||||||
1. Before implementing, check `spec/features/` for a relevant feature spec
|
|
||||||
2. For non-trivial work, create a work packet in `work/`
|
|
||||||
3. Every code change must trace to a task in the work packet
|
|
||||||
4. If a spec is unclear or incorrect, report a Spec Issue—do not guess
|
|
||||||
|
|
||||||
See `spec/guidelines/QUALITY_BAR.md` for what qualifies as "non-trivial."
|
|
||||||
|
|
||||||
## File Ownership
|
## File Ownership
|
||||||
|
|
||||||
**Never modify** files in `spec/guidelines/`.
|
|
||||||
If a guideline seems wrong or incomplete, report it as a Spec Issue.
|
|
||||||
|
|
||||||
| Path | Owner | AI May Modify |
|
| Path | Owner | AI May Modify |
|
||||||
|------|-------|---------------|
|
|------|-------|---------------|
|
||||||
| `spec/guidelines/*` | Human | No |
|
| `spec/guidelines/*` | Human | No |
|
||||||
| `spec/features/*` | Human | No (unless explicitly asked) |
|
| `spec/features/*` | Human | No (unless asked) |
|
||||||
| `work/*.md` | AI | Yes |
|
| `spec/work/*.md` | AI | Yes |
|
||||||
| `lib/**` | Shared | Yes |
|
| `spec/knowledge/*.md` | AI | Yes |
|
||||||
| `test/**` | Shared | Yes |
|
| `lib/**`, `test/**` | Shared | Yes |
|
||||||
| `AGENTS.md` | Human | No |
|
|
||||||
|
|
||||||
## Working Rules
|
## Key Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
fvm flutter pub get # Dependencies
|
||||||
|
fvm flutter analyze # Lint
|
||||||
|
fvm flutter test # Tests
|
||||||
|
```
|
||||||
|
|
||||||
|
## Project Rules
|
||||||
|
|
||||||
- Prefer small, localized changes. Avoid unrelated refactors.
|
|
||||||
- After dependency changes, run: `fvm flutter pub get`
|
|
||||||
- Fix any analyze/lint errors introduced by your changes.
|
|
||||||
- Assume Android as default target unless instructed otherwise.
|
- Assume Android as default target unless instructed otherwise.
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
---
|
||||||
|
description: Architecture — layers, dependency rules, ownership, common Dart/Flutter patterns
|
||||||
|
alwaysApply: true
|
||||||
|
---
|
||||||
|
|
||||||
# Zapstore — Architecture
|
# Zapstore — Architecture
|
||||||
|
|
||||||
## Core Principle
|
## Core Principle
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
---
|
||||||
|
description: Non-negotiable invariants — UI safety, async discipline, local-first, security, lifecycle
|
||||||
|
alwaysApply: true
|
||||||
|
---
|
||||||
|
|
||||||
# Zapstore — Invariants
|
# Zapstore — Invariants
|
||||||
|
|
||||||
The following guarantees are non-negotiable.
|
The following guarantees are non-negotiable.
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
---
|
||||||
|
description: Quality expectations — when to spec, layer standards, testing, anti-patterns, AI workflow
|
||||||
|
alwaysApply: true
|
||||||
|
---
|
||||||
|
|
||||||
# Zapstore — Quality Bar
|
# Zapstore — Quality Bar
|
||||||
|
|
||||||
## General Expectations
|
## General Expectations
|
||||||
@@ -66,6 +71,20 @@ If multiple phases: `WORK-005-a.md`, `WORK-005-b.md` (same feature number).
|
|||||||
- Failure, cancellation, and degraded-network paths must be covered.
|
- Failure, cancellation, and degraded-network paths must be covered.
|
||||||
- Tests that only assert the happy path are insufficient.
|
- Tests that only assert the happy path are insufficient.
|
||||||
|
|
||||||
|
### Test layers
|
||||||
|
|
||||||
|
- **Widget tests** (`flutter test`): preferred for UI state machines, all states
|
||||||
|
(loading, empty, error, retry). Fully headless, no device needed.
|
||||||
|
- **Integration tests** (`flutter test integration_test/`): used for flows that
|
||||||
|
cross platform boundaries — downloads, installs, storage, notifications.
|
||||||
|
Run against a connected Android emulator or device via ADB.
|
||||||
|
- **Patrol** is the integration test framework. Use `patrolTest` and `$.native`
|
||||||
|
for interacting with Android system UI (e.g. PackageInstaller dialogs).
|
||||||
|
Do not use `testWidgets` for integration tests.
|
||||||
|
- Pre-grant `REQUEST_INSTALL_PACKAGES` via
|
||||||
|
`adb shell appops set <package> REQUEST_INSTALL_PACKAGES allow`
|
||||||
|
before running install tests, to avoid a permissions setup screen.
|
||||||
|
|
||||||
## Anti-Patterns
|
## Anti-Patterns
|
||||||
|
|
||||||
- Silent failures
|
- Silent failures
|
||||||
@@ -91,7 +110,7 @@ This project uses a spec-first workflow to collaborate safely with AI.
|
|||||||
|
|
||||||
### What AI Owns
|
### What AI Owns
|
||||||
|
|
||||||
- Work packets under `work/`
|
- Work packets under `spec/work/`
|
||||||
- Refinement of task plans during implementation
|
- Refinement of task plans during implementation
|
||||||
|
|
||||||
### Spec-First Rule
|
### Spec-First Rule
|
||||||
@@ -109,3 +128,7 @@ For non-trivial work, changes are not complete unless:
|
|||||||
- Edge cases and failure modes are addressed
|
- Edge cases and failure modes are addressed
|
||||||
|
|
||||||
This workflow exists to prevent AI drift, accidental refactors, and UX regressions.
|
This workflow exists to prevent AI drift, accidental refactors, and UX regressions.
|
||||||
|
|
||||||
|
## Knowledge Entries
|
||||||
|
|
||||||
|
After a work packet merges, promote non-obvious decisions to `spec/knowledge/DEC-XXX-*.md`. See `spec/knowledge/_TEMPLATE.md` for format and criteria.
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
---
|
||||||
|
description: Product vision — what Zapstore is, who it serves, what success means
|
||||||
|
alwaysApply: true
|
||||||
|
---
|
||||||
|
|
||||||
# Zapstore — Vision
|
# Zapstore — Vision
|
||||||
|
|
||||||
## What Zapstore Is
|
## What Zapstore Is
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
---
|
||||||
|
date: YYYY-MM-DD
|
||||||
|
tags: [tag1, tag2, tag3]
|
||||||
|
problem: One-line description of the problem encountered
|
||||||
|
---
|
||||||
|
|
||||||
|
# DEC-XXX — Short Title
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
|
||||||
|
What went wrong, or what was unclear. Concrete and specific.
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
Why this came up. What was being built. What made it non-obvious.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
What was chosen. One clear statement.
|
||||||
|
|
||||||
|
## Options Considered
|
||||||
|
|
||||||
|
- **Option A** — description, why rejected
|
||||||
|
- **Option B** — description, why rejected
|
||||||
|
- **Option C (chosen)** — description, why selected
|
||||||
|
|
||||||
|
## Rationale
|
||||||
|
|
||||||
|
Why this option fits this codebase, this team, this context.
|
||||||
|
|
||||||
|
## How to Avoid This Problem Next Time
|
||||||
|
|
||||||
|
Concrete rule or pattern an agent can follow automatically:
|
||||||
|
- Do X instead of Y
|
||||||
|
- When you see Z, always do W
|
||||||
|
- Reference: `path/to/example.go` shows the correct pattern
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Example: DEC-001 — Separate polling from local watching
|
||||||
|
|
||||||
|
---
|
||||||
|
date: 2026-02-04
|
||||||
|
tags: [state-management, providers, riverpod, skeleton]
|
||||||
|
problem: Mixed local/remote data in one provider caused skeleton showing during refreshes
|
||||||
|
---
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
|
||||||
|
The updates screen showed a skeleton loading state during every pull-to-refresh, even when data was already loaded locally.
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
Building the updates screen. A single `CategorizedUpdatesNotifier` was both watching local DB and performing remote fetches. Timer-based invalidation triggered network requests on every rebuild.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
Split into two providers: one owns remote polling, one watches local data only.
|
||||||
|
|
||||||
|
## Options Considered
|
||||||
|
|
||||||
|
- **Single provider** — simpler, but mixing concerns caused skeleton/loading state confusion
|
||||||
|
- **Split providers (chosen)** — `UpdatePollerNotifier` (remote) + `CategorizedUpdatesNotifier` (local only)
|
||||||
|
|
||||||
|
## Rationale
|
||||||
|
|
||||||
|
Local provider is purely reactive — it never triggers network. Remote provider handles network + throttling. Clear separation means skeleton logic is simple: show only when no local data matches.
|
||||||
|
|
||||||
|
## How to Avoid This Problem Next Time
|
||||||
|
|
||||||
|
- Never mix `RemoteSource` and `LocalSource` queries in the same provider
|
||||||
|
- If a provider needs both local reactivity and remote fetching, split it
|
||||||
|
- Skeleton rule: `showSkeleton = installedIds.isNotEmpty && !hasAnyMatch` — once any match exists, never show skeleton again
|
||||||
|
- See `lib/services/updates_service.dart` for the correct pattern
|
||||||
@@ -40,6 +40,10 @@ Report blockers here instead of guessing. Format:
|
|||||||
|
|
||||||
Brief updates as work proceeds.
|
Brief updates as work proceeds.
|
||||||
|
|
||||||
|
## On Merge
|
||||||
|
|
||||||
|
Delete this work packet. If any decision here is non-obvious and worth remembering, promote it to `spec/knowledge/DEC-XXX-short-title.md` first.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Example: WORK-002 — NWC Zaps
|
# Example: WORK-002 — NWC Zaps
|
||||||
Reference in New Issue
Block a user