tane/CLAUDE.md
vjrj ee2fdc1e93 docs(trust): record the ego-centric pivot and ratings v1
network-trust.md §2 rewritten (ego-centric model, misuse resistance,
honest losses, unburnt Duniter bridge) + new §2.1 for ratings.
open-decisions.md: 2026-07-10 WoT-parameters decision superseded;
two new dated entries (ego-centric trust, ratings v1 with its soft
conversation anchor and the strong-anchor question kept open).
CLAUDE.md Block 2 paragraph updated to match.
2026-07-11 13:17:32 +02:00

75 lines
9.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# CLAUDE.md — Tanemaki
Context for AI agents (Claude Code) working in this repo. Read this first, then [`docs/design/open-decisions.md`](docs/design/open-decisions.md) (the **live decision log**).
## What this is
**Tanemaki** (種まき, "sow/scatter seeds"; short: **Tane**) — a **local-first, decentralized** app to manage and share traditional seeds. First app on a generic commons engine. Web: `tanemaki.app`. Package id: `org.comunes.tane`. See [`VISION.md`](VISION.md) for the human explanation and [`PLAN.md`](PLAN.md) for the origin analysis (with a "Nota de vigencia" reconciling evolved decisions).
## Golden rules (do not violate)
- **English for ALL code**: identifiers, class/field/column names, comments, commit messages, branch names, and technical specs. No exceptions.
- **User-facing strings are NEVER hardcoded** — always via i18n. The app is multilingual by design.
- **International by design, not Spain-first.** Tane targets the whole world, not one country or region. This is not just UI strings: it constrains **bundled data and assets** too.
- **Locale-keyed data, never mono-locale**: any catalog/reference data with human-readable names (species common names, vernacular names, units, categories) MUST be a locale-keyed map (`{ "es": [...], "en": [...], "ar": [...] }`), extensible to any language — never a single hardcoded language. Starter datasets may ship a small locale set, but the *schema*, code paths, and copy must never assume es/en only, and comments/notes must not brand the data as "Iberian/Spanish".
- **RTL is a first-class requirement**: layouts, widgets, and tests must work under RTL (Arabic, Hebrew, Persian). Use directional (`start`/`end`) not physical (`left`/`right`) insets/alignment; verify with `Directionality`.
- **Scripts & fonts**: bundled fonts must cover the scripts we claim to support (Latin + at least RTL/Arabic and CJK as they land); don't hardcode a Latin-only font stack.
- **OCR / language models**: bundled OCR traineddata and any language resource are pluggable per locale, not fixed to `eng`/`spa`. Match the data pack to the user's locale(s); the current es/en starter set is a seed, not the ceiling.
- **No locale-specific assumptions** in formatting, sorting, dates, numbers, name order — go through `intl`/ICU, never ad-hoc.
- **No plaintext at rest, ever** — the inventory DB is encrypted (SQLCipher). No plaintext logs/temp/caches. See [`docs/design/security-privacy.md`](docs/design/security-privacy.md).
- **Local-first**: everything works offline, no account, no central server. Online only enriches (degrades gracefully).
- **Progressive disclosure**: only `Variety.label` is mandatory; every other field optional. Audience is everyone 1080; simple by default, depth on demand. Never build "two modes".
- **Human words in the UI, never tech jargon.** User-facing copy speaks by *intent* (what it does for the person), not by *mechanism*. File formats and internals (CSV, JSON, HLC, CRDT, LWW, UUID…) are implementation details and MUST NOT appear in labels, subtitles, dialogs or messages. E.g. "Save a backup" / "Restore a backup" (not "Export JSON"); "Export to a spreadsheet" (a spreadsheet is a human concept; "CSV" is not). Format-named i18n *keys* (`exportJson`) are fine — they're code; only their *values* must be human. When a feature is genuinely for power users, reframe it or bury it under progressive disclosure — don't leak the jargon.
- **License: AGPL-3.0.** Keep new deps compatible.
- **Tests, near-TDD.** Every behavior is covered by automated tests (unit / widget / integration); **do NOT rely on manual testing.** Write tests first for domain logic (`commons_core` is pure Dart — ideal for TDD). Nothing merges without tests covering the new behavior; CI gates it. See [`docs/design/testing.md`](docs/design/testing.md).
- Discussion/design docs may be in Spanish; code and specs in English.
## Stack & structure
- **Flutter/Dart**, monorepo via **pub workspaces**. Target Android/iOS/desktop.
- **Drift** (SQLite) + **SQLCipher** for the encrypted local DB. Versioned step-by-step migrations, schemas exported to `drift_schemas/`, migration tests in CI. See [`docs/design/data-model.md`](docs/design/data-model.md) §5.
- i18n from day one (e.g. `flutter_localizations` + `intl`/`slang`).
```
tane/
pubspec.yaml # pub workspace root
packages/
commons_core/ # identity, Party/Group, TrustEdge, Offer, Pledge, transport (Nostr)+CRDT+sync, geohash discovery. NO seed specifics.
apps/
app_seeds/ # Tanemaki: Variety/Lot/Movement/Species, germination, plant-family units, UI, i18n, commons_ui (embedded for now)
```
Dependency direction: **`app_seeds``commons_core`, never the reverse.** Boundary rules in [`docs/design/core-domain-boundary.md`](docs/design/core-domain-boundary.md).
**Prefer workspace-level commands from the repo root** — one lockfile, one `.dart_tool`, both members at once. Verified working:
- `dart pub get` (root) — resolves the whole workspace; don't `pub get` per package.
- `dart analyze` (root) — analyzes `commons_core` **and** `app_seeds` together. The default gate.
**Testing — split by layer; NEVER run the whole `flutter test` blind (it's what "hangs").** Diagnosed 2026-07-11: logic tests are fast and reliable; only *widget* tests hang, because `pumpAndSettle()` on a screen with a never-settling source (live Drift/Nostr stream, periodic `Timer`, connectivity/plugin) waits at the 10-min default. The everyday gate:
- `dart test packages/commons_core` — pure Dart, ~1s. The TDD workhorse. (`dart test` can't run `app_seeds` — it needs `flutter`.)
- `flutter test test/services` — app logic + in-memory DB, no widgets, ~10s, reliable. Put new app logic in plain `test()` here.
- Widget tests: run targeted (`flutter test test/ui/<file>`), always with a timeout so a hang FAILS fast — a global `apps/app_seeds/dart_test.yaml` sets `timeout: 90s`; add `--timeout 30s` when in doubt. Never `pumpAndSettle` a live screen; use bounded `pump(Duration)`. See the testing-gotchas memory + [testing.md](docs/design/testing.md).
## Data model (decided, `schemaVersion = 1`)
Three levels: **`Variety`** (identity/accession) → **`Lot`** (a batch you hold: `harvest_year`, quantity, `offer_status`, germination) → **`Movement`** (append-only event log = history + provenance DAG + Plantare). Plus `Species` (bundled Wikidata/GBIF catalog), `VernacularName`, `Attachment`, `ExternalLink`, `GerminationTest`, `Party`, `SeedBank`, `Offer`, `Plantare`. Every mutable row: client `UUIDv7`, `created_at`, HLC `updated_at`, `last_author`, `is_deleted` (soft delete/tombstone), `schema_row_version`. CRDT: LWW scalars, OR-Set collections, grow-only `Movement`. Full spec: [`docs/design/data-model.md`](docs/design/data-model.md).
Decided schema details: `Quantity` is a shared value type (Lot + Movement); `offer_status` only on Lot; `category` free text prefilled from `Species.family`. Quantity units are **plant-aware** (cob/pod/ear/head…), not just kitchen units (§2.3.1). A Variety can hold multiple Lots with different years AND units at once.
## Identity & crypto (decided)
- **One identity** = a **Duniter/Ğ1-style root seed** (does profile, certifications, Ğ1). From it, **deterministically derive** a **secp256k1** subkey for **Nostr** (messaging/offers) — because Duniter has no messaging. User backs up ONE thing (the seed, as a printable recovery **QR**).
- At-rest DB key = a separate internal symmetric key in the OS keystore (unlocked by device biometric/PIN); NOT a user password. Backups also encrypted; key travels via the QR. See [`docs/design/backup-and-recovery.md`](docs/design/backup-and-recovery.md).
## Phasing — build order
**Block 1: Inventory.** Shippable alone, useful with zero network. Delivered (beta).
**Block 2 (STARTED — the social leap):** offers + messaging + relays + trust — big and indivisible. The de-risking spike ([`docs/design/spike-block2-findings.md`](docs/design/spike-block2-findings.md)) validated the whole happy path, so the **social round is now open**. Build order within Block 2: **(1) the transport foundation in `commons_core`** — one `NostrConnection` + N interfaces (`OfferTransport`/`MessageTransport`/`TrustTransport`/`RatingTransport`) + the pure `WebOfTrust` engine, on vetted libraries — then (2) offers UI, (3) messaging hardening, (4) trust & reputation polish. It is still large: scope honestly, keep it behind the transport interfaces, and don't let it regress Block 1.
Social layer uses: Nostr (offers NIP-99, DMs NIP-17, via the pure-Dart `nostr` package), **ego-centric trust** (you vouch for people you've met — kind 30777, one live cert per pair, expiring; your circle = friends-of-friends from YOUR key; **no global membership, no bootstrap referents, no user-facing parameters** — the global Duniter rule was dropped 2026-07-11 as not internationally viable, see open-decisions.md), **Wallapop-style ratings** (kind 30778, one live rating per pair, circle-weighted display), optional **Ğ1** currency (levels: price → wallet deep-link → optional WoT import remains a possible future enrichment). Identity: a **secp256k1 Nostr key derived (HKDF, domain-separated, one-way) from the Ğ1 root seed** — user backs up ONE seed. Cold-start via real Ğ1 seed groups + fairs. See [`docs/design/network-trust.md`](docs/design/network-trust.md), [`docs/design/g1-integration.md`](docs/design/g1-integration.md), and the spike findings above.
## Where things live
- Live decision log: [`docs/design/open-decisions.md`](docs/design/open-decisions.md) — check before deciding anything.
- First code steps: [`docs/design/first-sprint.md`](docs/design/first-sprint.md).
- Prior art / valuable mockups: [`docs/mockups/`](docs/mockups/) (inventory, item, search, profile, chat — the UI spec).
- Git: bare at `~/repos/tane.git`; working clone here. Commit messages in English.