From 79648234f56668a8b68c1272142ae5a83d42e158 Mon Sep 17 00:00:00 2001 From: franzap <_@franzap.com> Date: Mon, 19 Jan 2026 22:13:40 -0300 Subject: [PATCH] - Move CONTEXT.md to root as AI entry point, symlink .cursorrules - Rename specs/ to spec/ (singular) - Consolidate templates into single _TEMPLATE.md files with examples - Add "When to Create a Feature Spec" and work lifecycle to QUALITY_BAR.md - Document package manager as FEAT-001 (first behavioral contract) - Remove old _template/ directories --- .cursorrules | 21 +--- CONTEXT.md | 70 ++++++++++++ spec/features/FEAT-001-package-manager.md | 122 +++++++++++++++++++++ spec/features/_TEMPLATE.md | 75 +++++++++++++ {specs => spec}/guidelines/ARCHITECTURE.md | 0 {specs => spec}/guidelines/INVARIANTS.md | 0 {specs => spec}/guidelines/QUALITY_BAR.md | 64 ++++++----- {specs => spec}/guidelines/VISION.md | 0 specs/features/_template/FEAT-XXX-name.md | 31 ------ specs/guidelines/CONTEXT.md | 85 -------------- work/_TEMPLATE.md | 91 +++++++++++++++ work/_template/decisions.md | 33 ------ work/_template/task_plan.md | 27 ----- work/_template/test_matrix.md | 35 ------ 14 files changed, 397 insertions(+), 257 deletions(-) mode change 100644 => 120000 .cursorrules create mode 100644 CONTEXT.md create mode 100644 spec/features/FEAT-001-package-manager.md create mode 100644 spec/features/_TEMPLATE.md rename {specs => spec}/guidelines/ARCHITECTURE.md (100%) rename {specs => spec}/guidelines/INVARIANTS.md (100%) rename {specs => spec}/guidelines/QUALITY_BAR.md (58%) rename {specs => spec}/guidelines/VISION.md (100%) delete mode 100644 specs/features/_template/FEAT-XXX-name.md delete mode 100644 specs/guidelines/CONTEXT.md create mode 100644 work/_TEMPLATE.md delete mode 100644 work/_template/decisions.md delete mode 100644 work/_template/task_plan.md delete mode 100644 work/_template/test_matrix.md diff --git a/.cursorrules b/.cursorrules deleted file mode 100644 index 13c892f..0000000 --- a/.cursorrules +++ /dev/null @@ -1,20 +0,0 @@ -# Zapstore — Cursor Rules - -Source of truth: -- specs/guidelines/CONTEXT.md (orientation only) -- specs/guidelines/VISION.md -- specs/guidelines/ARCHITECTURE.md -- specs/guidelines/INVARIANTS.md -- specs/guidelines/QUALITY_BAR.md - -Hard constraints: -- Do NOT edit files under specs/guidelines unless explicitly instructed by a human. -- If specs are unclear or contradictory, STOP and report a "Spec Issue". Do not guess. - -Working 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. -- Do not use polling or artificial Future.delayed; await Futures/Streams correctly. -- Keep lib/widgets/common generic and reusable. -- Assume Android as default target unless instructed otherwise. diff --git a/.cursorrules b/.cursorrules new file mode 120000 index 0000000..fa62d27 --- /dev/null +++ b/.cursorrules @@ -0,0 +1 @@ +CONTEXT.md \ No newline at end of file diff --git a/CONTEXT.md b/CONTEXT.md new file mode 100644 index 0000000..40225ad --- /dev/null +++ b/CONTEXT.md @@ -0,0 +1,70 @@ +# Zapstore — Project Context + +This document is the entry point for AI assistants. +All behavioral authority lives in the project spec, not here. + +If anything in this file conflicts with files under `spec/guidelines/`, +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. + +## Project Spec Structure + + 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 + +**Never modify** files in `spec/guidelines/`. +If a guideline seems wrong or incomplete, report it as a Spec Issue. + +| Path | Owner | AI May Modify | +|------|-------|---------------| +| `spec/guidelines/*` | Human | No | +| `spec/features/*` | Human | No (unless explicitly asked) | +| `work/*.md` | AI | Yes | +| `lib/**` | Shared | Yes | +| `test/**` | Shared | Yes | +| `CONTEXT.md` | Human | No | + +## Key Dependencies + +- **models** / **purplebase**: Nostr SDK (local-first storage, relay sync, domain models). + See package README in pub cache. Basic usage patterns in `spec/guidelines/ARCHITECTURE.md`. +- **amber_signer**: NIP-55 Android signer integration. +- **background_downloader**: Download management with pause/resume. + +## Working 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. +- Do not use polling or artificial `Future.delayed`; await Futures/Streams correctly. +- Keep `lib/widgets/common` generic and reusable. +- Assume Android as default target unless instructed otherwise. diff --git a/spec/features/FEAT-001-package-manager.md b/spec/features/FEAT-001-package-manager.md new file mode 100644 index 0000000..83b8b81 --- /dev/null +++ b/spec/features/FEAT-001-package-manager.md @@ -0,0 +1,122 @@ +# FEAT-001 — Package Manager + +## Goal + +Single source of truth for installed packages and active install operations. +Manages the complete lifecycle: download → verify → install, with pause/resume/cancel support. + +## Non-Goals + +- Managing non-APK file types +- Auto-updating without user awareness +- Installing from sources other than Nostr-published releases + +## User-Visible Behavior + +### Download Phase + +- User taps "Install" → download begins, progress shown +- User can pause/resume/cancel active downloads +- Multiple downloads queue automatically (max 3 concurrent) +- "Update All" queues all updates immediately with visual feedback + +### Verification Phase + +- After download completes, hash verification runs +- Verification state is visible (not hidden) +- Hash mismatch blocks install with clear error + +### Permission Phase + +- If "Install unknown apps" permission not granted, user is prompted +- Permission state is explicit in UI +- Once granted, all waiting installs advance automatically + +### Install Phase + +- Native Android install dialog shown +- One install dialog at a time (serialized) +- If user dismisses dialog, install shows "Tap to retry" state +- Success updates installed list immediately (no stale UI) + +### Failure States + +- Download failed → clear error, can retry +- Hash mismatch → error, cannot proceed +- Certificate mismatch → offer "Uninstall and reinstall" option +- Permission denied → guidance to enable in Settings + +## State Machine + +Operations follow this sealed class hierarchy (`install_operation.dart`): + +``` +DownloadQueued → Downloading ↔ DownloadPaused + ↓ + Verifying + ↓ + AwaitingPermission (if needed) + ↓ + ReadyToInstall + ↓ + Installing → AwaitingUserAction (if dismissed) + ↓ + [cleared] or OperationFailed +``` + +State transitions are unidirectional except Downloading ↔ DownloadPaused. + +## Edge Cases + +- Network drops mid-download → download pauses or fails gracefully, can retry +- App backgrounded during install → install completes, UI updates on return +- 404 from origin server → automatic CDN fallback before failing +- Stale operations (>7 days) → garbage collected on app restart +- Android package DB race condition → state updated from target metadata, not sync + +## Invariants + +These are non-negotiable. Violations mean the implementation is broken. + +1. **UI never blocks** — `install()` returns immediately; events drive state via EventChannel +2. **One install dialog at a time** — Android PackageInstaller limitation, enforced by serialization +3. **Hash verification before install** — Native side verifies before install session opens +4. **Permission flow is explicit** — `AwaitingPermission` state exists for UI feedback +5. **Downloaded files are cleaned up** — Deleted after success or dismissal +6. **No polling** — All state changes via callbacks/events, never periodic checks + +## Integration Boundaries + +``` +┌─────────────────────────────────────────────────────────────┐ +│ PackageManager (Dart) │ +│ - State machine owner │ +│ - Download management (background_downloader) │ +│ - Orchestrates flow │ +└─────────────────────────┬───────────────────────────────────┘ + │ MethodChannel / EventChannel +┌─────────────────────────▼───────────────────────────────────┐ +│ AndroidPackageManagerPlugin (Kotlin) │ +│ - Hash verification │ +│ - PackageInstaller session │ +│ - Permission checks │ +│ - Emits: verifying/started/success/failed/cancelled │ +└─────────────────────────────────────────────────────────────┘ +``` + +## Acceptance Criteria + +- [ ] User can download, pause, resume, cancel downloads +- [ ] User can install apps with proper verification +- [ ] Multiple downloads queue correctly (max 3 concurrent) +- [ ] Install failures show actionable error messages +- [ ] Certificate mismatch offers force-update option +- [ ] UI remains responsive throughout all operations +- [ ] No operations block the UI thread + +## Files + +- `lib/services/package_manager/package_manager.dart` — Base class, state machine +- `lib/services/package_manager/install_operation.dart` — State definitions +- `lib/services/package_manager/android_package_manager.dart` — Android implementation +- `android/.../AndroidPackageManagerPlugin.kt` — Native side diff --git a/spec/features/_TEMPLATE.md b/spec/features/_TEMPLATE.md new file mode 100644 index 0000000..2078342 --- /dev/null +++ b/spec/features/_TEMPLATE.md @@ -0,0 +1,75 @@ +# FEAT-XXX — Short Name + +## Goal + +1–2 sentences describing what this feature/bugfix achieves for the user. + +## Non-Goals + +- Explicitly list what is out of scope +- Prevents scope creep and AI drift + +## User-Visible Behavior + +- What the user sees or can do +- States: loading, success, error, empty (where relevant) +- Offline behavior + +## Edge Cases + +- Degraded or no network +- Cancellation / retry +- Invalid or partial data +- Permission denied +- Other relevant risks + +## Acceptance Criteria + +- [ ] Observable outcome 1 +- [ ] Observable outcome 2 +- [ ] Observable outcome 3 + +## Notes (optional) + +- Anything that needs human decision +- Open questions + +--- + +# Example: FEAT-002 — NWC Zaps + +## Goal + +Allow users to zap app developers directly from the app detail screen using Nostr Wallet Connect. + +## Non-Goals + +- In-app wallet management (just NWC connection) +- Zapping comments or reviews (only developers) +- Recurring zaps or subscriptions + +## User-Visible Behavior + +- Zap button visible on app detail screen when NWC connected +- Tapping opens amount selection dialog (21, 100, 500, 1000 sats, custom) +- Success: toast confirmation with amount +- Failure: error dialog with reason +- Button disabled with tooltip when developer has no lightning address + +## Edge Cases + +- Developer has no lightning address → button hidden or disabled with explanation +- NWC connection drops mid-zap → graceful error, suggest reconnect +- Insufficient wallet balance → clear error from wallet +- App backgrounded during zap → completes, toast on return + +## Acceptance Criteria + +- [ ] User can connect NWC from profile settings +- [ ] User can zap developer from app detail screen +- [ ] Zap fails gracefully with clear error message +- [ ] Zap button correctly disabled when NWC not connected + +## Notes + +- Consider whether to show cumulative zaps received by developer diff --git a/specs/guidelines/ARCHITECTURE.md b/spec/guidelines/ARCHITECTURE.md similarity index 100% rename from specs/guidelines/ARCHITECTURE.md rename to spec/guidelines/ARCHITECTURE.md diff --git a/specs/guidelines/INVARIANTS.md b/spec/guidelines/INVARIANTS.md similarity index 100% rename from specs/guidelines/INVARIANTS.md rename to spec/guidelines/INVARIANTS.md diff --git a/specs/guidelines/QUALITY_BAR.md b/spec/guidelines/QUALITY_BAR.md similarity index 58% rename from specs/guidelines/QUALITY_BAR.md rename to spec/guidelines/QUALITY_BAR.md index ca05269..f58471d 100644 --- a/specs/guidelines/QUALITY_BAR.md +++ b/spec/guidelines/QUALITY_BAR.md @@ -6,6 +6,33 @@ - Happy-path-only implementations are insufficient. - Failures must be explicit and observable. +## When to Create a Feature Spec + +Create a spec if the work: + +- Touches async/lifecycle code (risk of UI blocking or resource leaks) +- Modifies security-sensitive flows (verification, permissions, signing, secrets) +- Changes state machine behavior (package manager, auth, subscriptions) +- Affects multiple screens or services +- Could regress existing UX + +**Skip the spec** if: + +- Pure UI cosmetics (colors, spacing, copy changes) +- Adding a field to an existing model with no behavioral change +- Bug fix with obvious cause and obvious solution +- Dependency update with no API changes + +When in doubt, create a spec. The overhead is low. + +## Work Packet Lifecycle + +1. Create `WORK-XXX-*.md` when starting non-trivial work +2. Update tasks and decisions as you work +3. **Delete after PR merges** — the feature spec remains as the contract + +If multiple phases: `WORK-005-a.md`, `WORK-005-b.md` (same feature number). + ## Layer Expectations ### models @@ -46,54 +73,39 @@ - Artificial delays or polling - Large refactors unrelated to the task -## Working With AI (Human Guidelines) +## Working With AI This project uses a spec-first workflow to collaborate safely with AI. ### Documentation Discipline - Markdown files must remain small, focused, and human-readable. -- Prefer extending or refining existing documents over creating new ones. -- New markdown files should be introduced only when unavoidable. +- Prefer extending existing documents over creating new ones. - The goal is to do more with less, not to document everything. ### What Humans Own -- Foundation specs under `specs/guidelines/` -- Feature specs under `specs/features/` +- Guidelines under `spec/guidelines/` (never AI-modified) +- Feature specs under `spec/features/` - Decisions to change behavior or architecture ### What AI Owns -- Execution plans under `work/**/task_plan.md` -- Decision logs and test matrices under `work/**/` - -### When a Work Packet Is Required - -- New features -- UX changes -- Async, lifecycle, or background work -- Security or verification changes -- Any non-trivial or risky change +- Work packets under `work/` +- Refinement of task plans during implementation ### Spec-First Rule -- Behavior changes require updating the spec first. +- Behavior changes require a feature spec first. - During implementation, specs are read-only. - If a spec is unclear or incorrect, AI must stop and report a "Spec Issue". -### Task Plan Usage - -- Humans create the initial task_plan with a rough checklist. -- AI refines the plan, executes tasks, and marks progress. -- Every code change must map to an item in the task_plan. - ### Task Completeness -For non-trivial work, changes are not considered complete unless: +For non-trivial work, changes are not complete unless: -- task_plan.md reflects the actual work performed -- test_matrix.md demonstrates behavioral coverage -- no significant code exists outside the task plan +- Work packet reflects the actual work performed +- No significant code exists outside the task plan +- Edge cases and failure modes are addressed This workflow exists to prevent AI drift, accidental refactors, and UX regressions. diff --git a/specs/guidelines/VISION.md b/spec/guidelines/VISION.md similarity index 100% rename from specs/guidelines/VISION.md rename to spec/guidelines/VISION.md diff --git a/specs/features/_template/FEAT-XXX-name.md b/specs/features/_template/FEAT-XXX-name.md deleted file mode 100644 index b23432d..0000000 --- a/specs/features/_template/FEAT-XXX-name.md +++ /dev/null @@ -1,31 +0,0 @@ -# FEAT-XXX — - -## Goal - -<1–2 sentences describing what this feature/bugfix achieves for the user> - -## Non-goals - -- - -## User-visible Behavior - -- -- - -## Edge Cases - -- -- -- -- - -## Acceptance Criteria - -- [ ] -- [ ] -- [ ] - -## Notes / Open Questions (optional) - -- diff --git a/specs/guidelines/CONTEXT.md b/specs/guidelines/CONTEXT.md deleted file mode 100644 index 72414eb..0000000 --- a/specs/guidelines/CONTEXT.md +++ /dev/null @@ -1,85 +0,0 @@ -# Zapstore — Project Context - -This document provides **orientation only**. -It does **not** define rules, behavior, or constraints. - -If anything in this file conflicts with files under `specs/guidelines/`, -this file is wrong. - ---- - -## What This Repository Is - -Zapstore is a Flutter-based, local-first application store built on top of -the Nostr protocol and Bitcoin Lightning payments. - -It allows users to: - -- discover apps published via Nostr, -- verify and install applications safely, -- and directly support developers through zaps. - -Zapstore prioritizes trust, verification, and user sovereignty over scale, -growth, or engagement metrics. - ---- - -## Core Stack (High Level) - -- **Frontend**: Flutter (Android-first) -- **Protocols**: Nostr, Lightning Network -- **Payments**: Zaps, NWC (NIP-47) -- **Data Model**: Nostr event kinds (apps, releases, files, zaps) -- **Storage**: Local-first (SQLite), relay-backed sync -- **Execution Model**: Async + isolates (non-blocking UI) - ---- - -## Repository Structure (Conceptual) - -This repository is organized into three main packages: - -- **zapstore** - - - Flutter UI and application orchestration - - Navigation, presentation, and user interaction - -- **purplebase** - - - Local-first storage, indexing, and relay synchronization - - Background work and isolate execution - -- **models** - - Pure domain models and utilities - - Nostr kinds, parsing, signing, encryption, verification - -Details and dependency rules are defined in `ARCHITECTURE.md`. - ---- - -## How to Read the Specs (Important) - -The following files define the actual guardrails of the project: - -1. **VISION.md** - - What Zapstore is and is not -2. **ARCHITECTURE.md** - - Package responsibilities and dependency boundaries -3. **INVARIANTS.md** - - Non-negotiable behavioral guarantees -4. **QUALITY_BAR.md** - - Definition of acceptable work and anti-patterns - -These files are human-owned and change slowly. - ---- - -## What This File Is Not - -- This file does **not** define invariants or rules -- This file does **not** describe UI flows or behavior -- This file does **not** override any foundation spec -- This file should remain small and stable - -Its sole purpose is to provide initial context for humans and AI agents -before reading the foundation specifications. diff --git a/work/_TEMPLATE.md b/work/_TEMPLATE.md new file mode 100644 index 0000000..1c870b4 --- /dev/null +++ b/work/_TEMPLATE.md @@ -0,0 +1,91 @@ +# WORK-XXX — Short Name + +**Feature:** FEAT-XXX-short-name.md +**Status:** In Progress | Complete + +## Tasks + +- [ ] 1. Task description + - Files: `lib/path/to/file.dart` + - Notes: any relevant context +- [ ] 2. Task description +- [ ] 3. Handle edge cases per spec +- [ ] 4. Self-review against INVARIANTS.md + +## Test Coverage + +| Scenario | Expected | Status | +|----------|----------|--------| +| Happy path | Describe expected behavior | [ ] | +| Edge case: network failure | Graceful degradation | [ ] | +| Edge case: cancellation | Clean cleanup | [ ] | + +## Decisions + +### YYYY-MM-DD — Decision title + +**Context:** Why this decision came up. +**Options:** A, B, C considered. +**Decision:** Chosen option. +**Rationale:** Why. + +## Spec Issues + +Report blockers here instead of guessing. Format: + +- **Issue:** Description of unclear/incorrect spec +- **Question:** What clarification is needed + +## Progress Notes + +Brief updates as work proceeds. + +--- + +# Example: WORK-002 — NWC Zaps + +**Feature:** FEAT-002-nwc-zaps.md +**Status:** In Progress + +## Tasks + +- [x] 1. Add nwc_wallet package dependency +- [x] 2. Create NwcService in lib/services/ + - Files: `lib/services/nwc_service.dart` + - Stores connection string in SecureStorageService +- [x] 3. Add NWC settings UI in profile screen + - Files: `lib/screens/profile_screen.dart`, `lib/widgets/nwc_widgets.dart` +- [ ] 4. Create ZapButton widget + - Files: `lib/widgets/zap_button.dart` +- [ ] 5. Create ZapDialog for amount selection +- [ ] 6. Integrate into AppDetailScreen +- [ ] 7. Implement zap request flow (kind 9734) +- [ ] 8. Self-review against INVARIANTS.md + +## Test Coverage + +| Scenario | Expected | Status | +|----------|----------|--------| +| Connect valid NWC | Connection saved, status updated | [x] | +| Connect invalid NWC | Error shown, nothing saved | [x] | +| Zap with sufficient balance | Success toast | [ ] | +| Zap with insufficient balance | Wallet error shown | [ ] | +| Developer has no LN address | Button disabled with tooltip | [ ] | + +## Decisions + +### 2026-01-15 — NWC string storage + +**Context:** Need to persist NWC connection string securely. +**Options:** New encrypted file, SecureStorageService, Nostr event. +**Decision:** SecureStorageService. +**Rationale:** Already used for nsec, platform-native secure storage. + +## Spec Issues + +_None_ + +## Progress Notes + +**2026-01-15:** Completed NWC connection flow. Reused SecureStorageService. +**2026-01-17:** ZapButton done. Added edge case for missing LN address to test matrix. diff --git a/work/_template/decisions.md b/work/_template/decisions.md deleted file mode 100644 index a0b5e1b..0000000 --- a/work/_template/decisions.md +++ /dev/null @@ -1,33 +0,0 @@ -# Decisions - -This file records **non-obvious decisions** made during implementation. - -Only write here when: - -- a tradeoff was made -- multiple options existed -- a decision affects future work - -Do not log: - -- routine steps -- mechanical changes -- obvious refactors - ---- - -## Decision Log - -### YYYY-MM-DD — Short title - -**Context** -Brief description of the situation. - -**Decision** -What was chosen. - -**Rationale** -Why this option was selected. - -**Consequences** -Expected impact or follow-up considerations. diff --git a/work/_template/task_plan.md b/work/_template/task_plan.md deleted file mode 100644 index e52d351..0000000 --- a/work/_template/task_plan.md +++ /dev/null @@ -1,27 +0,0 @@ -# Task Plan — FEAT-XXX - -This file is the working memory for this change. - -Rules: - -- Every code change must map to a task below. -- Tasks must be checked when completed. -- If the spec is unclear or incorrect, stop and report a Spec Issue. - -## Goal - - - -## Constraints - -- Follow the feature spec -- Do not modify foundation specs -- Respect invariants and quality bar - -## Tasks - -- [ ] Understand current behavior -- [ ] Implement required changes -- [ ] Handle failure and edge cases -- [ ] Update tests -- [ ] Validate against test matrix diff --git a/work/_template/test_matrix.md b/work/_template/test_matrix.md deleted file mode 100644 index 022c273..0000000 --- a/work/_template/test_matrix.md +++ /dev/null @@ -1,35 +0,0 @@ -# Test Matrix - -This file describes **how behavior is validated** for this change. - -Focus on: - -- observable behavior -- edge cases -- failure modes - -Do not list: - -- trivial happy paths only -- tests that merely mirror the implementation - ---- - -## Scope - -Briefly describe what behavior this change introduces or modifies. - ---- - -## Behavioral Coverage - -| Scenario | Expected Behavior | Test Type | Location | -| -------- | ----------------- | --------- | -------- | -| | | | | - ---- - -## Notes - -- Prefer fewer, high-signal tests over broad but shallow coverage. -- If a scenario is intentionally untested, state why.