COM Logo COM Argentina

Detailed tasks

Per-item roadmap breakdown. Subtasks with status and spec references.

Updated: 2026-05-12 · View roadmap

✓ Done ~ In progress ○ Planned ! Blocked

v0.1 Alpha MVP (online-first) Shipped

astro-skeleton Astro site skeleton + Tailwind + i18n
6/6

subtasks

✓
Astro 5 project with output: 'static'
✓
Tailwind 3 + @astrojs/tailwind integration
✓
EN/ES locales via astro/i18n + prefixDefaultLocale
✓
BaseLayout + DocLayout shared shells
✓
Header + Footer with theme toggle, lang switcher, mobile menu
✓
Sitemap (@astrojs/sitemap)
supabase-schema Supabase schema with PostGIS + RLS

02-observation.md · 05-map.md · 06-darwin-core.md · 07-licensing.md

8/8

subtasks

✓
6 core tables (users, taxa, taxon_usage_history, observations, identifications, media_files)
✓
PostGIS geography columns + GIST indexes
✓
RLS owner + public-read + credentialed-read
✓
3 triggers (auth-user-created, obs-count, sync-primary-id)
✓
obscure_point() helper + obscure_level enum
✓
Idempotent — replayable via make db-apply
✓
Storage bucket 'media' + policies
✓
Role-level grants (anon SELECT, authenticated CRUD)
auth-magic-link Magic-link auth + guest mode

04-auth.md

6/7

subtasks

✓
supabase.ts singleton client
✓
auth.ts helpers (sendMagicLink, exchangeCode, signOut)
✓
Sign-in pages in EN + ES
✓
Callback handles PKCE and implicit flows
✓
Header avatar dropdown via onAuthStateChange
✓
ObserverRef discriminated union
○
Hard-cap UI nudge after 3rd guest observation
auth-multi Google + GitHub OAuth, OTP code, passkey, sign-out-everywhere

04-auth.md

6/7

subtasks

✓
signInWithGoogle / signInWithGitHub (with user:email scope)
✓
Email OTP code path (verifyOtp)
✓
Passkey enrol + verify (b64url ↔ ArrayBuffer)
✓
WebAuthn feature detection
✓
signOutEverywhere() global revocation
✓
SignInForm shared component used by both locales
!
Operator: enable WebAuthn MFA toggle in dashboard

Blocked by: Manual dashboard step

ci-cd CI/CD via GitHub Actions

infra/testing.md · infra/github-actions.yml

5/5

subtasks

✓
ci.yml — typecheck + test + build on PRs
✓
deploy.yml — typecheck + test + build + deploy
✓
deploy-functions.yml — manual Edge Function deploys
✓
PUBLIC_* secrets pushed via gh secret set
✓
Vitest job runs in CI
profile-basics Profile page + edit + avatar dropdown

08-profile-activity-gamification.md

7/7

subtasks

✓
ALTER TABLE users with 11 profile/credential columns
✓
ProfileView + ProfileEditForm shared components
✓
Pages in EN /profile/* and ES /perfil/*
✓
Initials-SVG avatar fallback
✓
Header avatar dropdown (View / Edit / Sign out)
✓
Activity feed section (renders v0.3+ events)
✓
Badges section (renders when gamification opted in)
gps-observation GPS observation form with EXIF auto-fill

02-observation.md

9/9

subtasks

✓
Multi-image capture/upload
✓
Live GPS via getCurrentPosition
✓
EXIF GPS fallback via exifr
✓
Manual coords input
✓
Habitat / weather / evidence_type dropdowns
✓
Notes textarea (≤2000 chars)
✓
NOM-059 / CITES privacy notice
✓
Audio capture (MediaRecorder ≤30s)
✓
Saves to Dexie outbox + immediate sync attempt
plantnet-id PlantNet photo ID integration

01-photo-id.md · 13-identifier-registry.md

7/7

subtasks

✓
Edge Function with PlantNet → Claude waterfall
✓
force_provider field for cascade routing
✓
client_keys.plantnet BYO key support
✓
0.7 confidence threshold
✓
Plugin wrapper in identifiers/plantnet.ts
✓
testConnection() probe
✓
Operator: deploy + set secret
claude-haiku-id Claude Haiku 4.5 vision cascade

01-photo-id.md

4/5

subtasks

✓
Cached system prompt (90% token savings)
✓
JSON response parsing with code-fence stripping
✓
client_anthropic_key BYO support
✓
Cost-tracking probe in testConnection
○
Operator: optional ANTHROPIC_API_KEY secret
map-view MapLibre map with clustered observation pins

05-map.md

6/6

subtasks

✓
OpenFreeMap Liberty style (free, IPv4)
✓
GeoJSON source from observations table
✓
Clustering at low zoom + pins at high zoom
✓
Kingdom-coloured pins
✓
Click pin → popup with thumbnail + link
✓
pmtiles offline layer (handed off to v0.3 offline-maps)

→ tracked under offline-maps

darwin-core-csv Darwin Core CSV export

06-darwin-core.md

5/5

subtasks

✓
darwin-core.ts mapping + CSV generation
✓
Three column presets: DwC / SNIB / CONANP
✓
Obscuration tier handling
✓
ExportView component with format selector
✓
9 Vitest unit tests
pwa-manifest PWA manifest + service worker shell cache

03-offline.md

4/4

subtasks

✓
manifest.webmanifest with display:standalone
✓
sw.js cache-first for same-origin GETs
✓
Production-only registration (skip localhost)
✓
Apple-mobile-web-app meta tags
offline-queue Dexie IndexedDB outbox + sync engine + identify trigger

03-offline.md · 10-media-storage.md

6/6

subtasks

✓
RastrumDB with observations / mediaBlobs / idQueue
✓
syncOutbox + registerSyncTriggers
✓
R2 + Supabase Storage dual upload paths
✓
Client-side image resize before upload
✓
Cascade engine integration
✓
Audio blob support
unit-tests Vitest unit-test scaffold

infra/testing.md

4/6

subtasks

✓
Vitest 4.1 + happy-dom env
✓
darwin-core.test.ts (9 tests)
✓
byo-keys.test.ts (10 tests)
✓
Make targets + CI integration
○
pgTAP RLS test suite
○
Playwright E2E suite

v0.3 Offline intelligence + activity Shipped

activity-feed Activity feed + server-side triggers

08-profile-activity-gamification.md

6/6

subtasks

✓
activity_events table with kind enum (12 values)
✓
RLS self/public + visibility tiers
✓
fire_observation_created trigger
✓
fire_research_grade trigger (auto-public)
✓
Profile-page rendering with i18n labels
✓
Auto-mark-as-read on view
unread-badge Unread-count badge on avatar dropdown

08-profile-activity-gamification.md

4/6

subtasks

✓
Red dot with count when unread > 0
✓
Refresh on every onAuthStateChange
✓
99+ overflow text
✓
Supabase RPC or client query for unread activity_events count
○
CSS pulse animation on new unread arrival
○
Accessible aria-label announcing unread count to screen readers
sensitive-privacy NOM-059 / CITES obscuration warning

02-observation.md

4/5

subtasks

✓
Amber notice always visible in form
✓
Schema obscure_level enum
✓
sync_primary_identification trigger applies obscuration
✓
location_obscured separate column for public reads
○
Per-species look-up at form-submit time
exif-extraction EXIF/XMP/ID3 metadata auto-extraction

02-observation.md

3/7

subtasks

✓
exifr library wired in observation form
✓
GPS extracted with EXIF source flag
✓
DateTimeOriginal → observation timestamp
○
XMP sidecar parsing for drone/camera-trap metadata
○
ID3 tag extraction from audio recordings (MediaRecorder output)
○
Camera make/model stored on media_files for device analytics
○
Orientation tag auto-rotation before upload
webllm-text WebLLM Llama-3.2-1B for translation + field notes

11-in-browser-ai.md

5/6

subtasks

✓
@mlc-ai/web-llm installed
✓
loadTextEngine + translateNote + generateFieldNote
✓
Profile/edit AI download card with consent modal
✓
Cache management (probe / delete / re-download)
✓
requestPersistentStorage() to prevent iOS eviction
○
Translate + Auto-narrative buttons in observation form
onnx-base EfficientNet-Lite0 ONNX base fallback (~18 MB INT8 hosted on R2)

13-identifier-registry.md

6/6

subtasks

✓
Convert model to ONNX (TF Model Garden EfficientNet-Lite0)
✓
Bundle ImageNet labels JSON
✓
src/lib/identifiers/onnx-base.ts implementation
✓
Image preprocessing (224x224 cover crop + ImageNet mean/std)
✓
On-device inference via onnxruntime-web (cached in IndexedDB)
✓
Profile/edit AI download card with consent + cache management
offline-maps pmtiles offline map download (Mexico zoom 0–10, ~48 MB)

05-map.md

5/6

subtasks

✓
Mexico-bounded pmtiles archive hosted on R2
✓
PUBLIC_PMTILES_MX_URL env var wired into the build
✓
src/lib/offline-map.ts download + Cache API persistence
✓
MapLibre pmtiles:// protocol registered for offline reads
✓
Profile/edit download card with cell-conn warning
○
Per-region zoom 11–14 chunks on-demand
byo-anthropic-key BYO Anthropic key (client-set, never persisted)

01-photo-id.md · 13-identifier-registry.md

5/5

subtasks

✓
byo-keys.ts central store backed by localStorage
✓
client_keys.anthropic forwarded per call to Edge Function
✓
Edge Function never logs or persists the key
✓
Profile/edit Configure UI with Save/Test/Clear
✓
testConnection() probe verifies a live key
webllm-default WebLLM as default AI fallback (download warning on first use)

11-in-browser-ai.md · 13-identifier-registry.md

5/5

subtasks

✓
Phi-3.5-vision plugin registered in bootstrapIdentifiers()
✓
Cascade automatically falls back to WebLLM when network plugins fail
✓
First-use download dialog with size + persistence consent
✓
Cache management (probe/delete/re-download) in profile
✓
0.35 confidence cap to prevent low-quality auto-promote
identification-block Visible identification block in observation form

02-observation.md · 01-photo-id.md

5/5

subtasks

✓
Inline ID block under photo grid (spinner + result chip)
✓
Auto-fires on first photo + manual re-run button
✓
Manual scientific-name input overrides cascade
✓
Confidence + source surfaced (PlantNet, Claude, …)
✓
Low-confidence amber notice when conf < 0.4
gps-two-pass Two-pass GPS: fast coarse fix then high-accuracy refinement

02-observation.md

5/5

subtasks

✓
First call: enableHighAccuracy=false, ~1 s timeout
✓
Second call: enableHighAccuracy=true, ~10 s timeout
✓
Form preview updates live as fix improves
✓
Cancel logic on form submit / reset
✓
Falls back to EXIF GPS on timeout

v0.5 Beta

byo-keys-platform Per-plugin BYO API keys with guided setup

13-identifier-registry.md

6/6

subtasks

✓
KeySpec + SetupStep + testConnection contract
✓
byo-keys.ts central store
✓
One-time legacy migration
✓
Per-plugin Configure disclosure UI
✓
Live save + Test + Clear buttons
✓
10 unit tests
webllm-vision WebLLM Phi-3.5-vision fallback ID

11-in-browser-ai.md

5/5

subtasks

✓
Phi-3.5-vision plugin in identifiers/phi-vision.ts
✓
Confidence hard-cap 0.35 (DB trigger blocks <0.4)
✓
Disclaimer in profile/edit + plugin description
✓
Same consent + cache + delete flow as Llama
✓
Cascade integration as last-resort opt-in
discovery-badges 39 seed badges + nightly evaluator

08-profile-activity-gamification.md

6/7

subtasks

✓
badges + user_badges tables with RLS
✓
39 badge seed (5 categories × 4 tiers)
✓
Badge eligibility SQL functions
✓
award-badges nightly Edge Function
✓
Profile-page tier-coloured pills
✓
Cron schedule 30 7 * * *
○
Activity-feed badge_earned event integration
quality-gates Confidence ≥ 0.4 enforcement on research-grade

08-profile-activity-gamification.md

2/6

subtasks

✓
enforce_research_grade_quality trigger
✓
Plugin-level confidence_ceiling cap
○
UI warning banner when identification confidence < 0.4
○
Admin dashboard widget showing research-grade rejection rate
○
Vitest coverage for trigger edge cases (null confidence, exact 0.4 boundary)
○
Documentation in module spec for quality-gate thresholds and rationale
consensus-workflow Research-grade 2/3 consensus + anti-sybil + expert weight

08-profile-activity-gamification.md

2/4

subtasks

✓
prevent_self_validation trigger
✓
recompute_consensus with 3× expert weight
○
Validator UI for community input
○
AFTER INSERT/UPDATE auto-recompute trigger
multi-image Multi-image observations

02-observation.md

4/4

subtasks

✓
Gallery <input multiple>
✓
Photo grid with primary indicator + remove
✓
All blobs saved to Dexie + uploaded to R2
✓
media_files inserted with sort_order + is_primary
eco-evidence Ecological evidence fields

02-observation.md

3/6

subtasks

✓
evidence_type enum on observations
✓
Form dropdown with 9 values
✓
i18n labels for all 9 evidence_type enum values in EN + ES
○
Darwin Core mapping of evidence_type to DwC basisOfRecord
○
Filter by evidence_type on /explore/recent grid
○
Icon set for each evidence type in observation detail view
birdnet-audio BirdNET-Lite audio ID (Cornell Lab CC BY-NC-SA 4.0, ONNX hosted on R2)

12-birdnet-audio.md

9/9

subtasks

✓
Audio capture in observation form (≤30s)
✓
Sync engine routes audio to R2 + cascade
✓
Module 12 spec written
✓
BirdNET-Lite v2.4 ONNX hosted on R2 + PUBLIC_BIRDNET_WEIGHTS_URL
✓
Audio decode + 48 kHz resample + 3 s window pre-processing
✓
onnxruntime-web inference in plugin (top-K species)
✓
Bundle species labels JSON (~6,000 species)
✓
Profile/edit download card with consent + cache management
✓
Cornell Lab CC BY-NC-SA 4.0 attribution in UI + DwC export
scout-v0 Rastrum Scout v0 (conversational ID, pgvector RAG)
0/6

subtasks

!
Enable pgvector extension

Blocked by: Deferred per future-migrations.md

!
Embedding pipeline (Voyage-3 over ~50K Mexican taxa)

Blocked by: Embedding budget (~$50)

○
taxon_embeddings table + HNSW index
○
scout Edge Function (retrieval + Claude composition)
○
Chat UI in app
○
chat_sessions table + history
onnx-regional Regional ONNX packs (Oaxaca, Yucatán)
0/5

subtasks

!
Training pipeline (TFLite Model Maker / PyTorch+Optimum)

Blocked by: ML training infra

○
Curated training set per region (~1K species each)
○
Convert to ONNX int8
○
Download cards in profile/edit
○
Region-aware cascade routing (lat/lng → pack)
gbif-ipt GBIF IPT pilot publish (DwC-A ZIP)

06-darwin-core.md

3/6

subtasks

!
GBIF publisher account application

Blocked by: ~2-week ed-org review

✓
DwC-A ZIP generator (meta.xml + eml.xml + csvs)
✓
export-dwca Edge Function (on-demand export)
✓
publish-to-ipt.sh operator script
○
gbif-publish Edge Function (monthly cron)
○
DOI tracking in dataset_versions table
local-contexts Local Contexts BC/TK Notice integration
0/4

subtasks

!
Community consent (Zapoteco partnership)

Blocked by: Multi-month governance work

○
Local Contexts Hub API v2 integration
○
observation_bc_notices link table
○
BC/TK label rendering on observations + DwC
user-api-tokens User API tokens (rst_*, scoped, SHA-256 hashed)

14-user-api-tokens.md

5/5

subtasks

✓
user_api_tokens table with hashed token + scopes[]
✓
Crypto-strong rst_<base32> generation client-side
✓
RLS owner-only on user_api_tokens
✓
Plaintext shown to user exactly once on creation
✓
last_used_at fire-and-forget update on each call
token-rest-api Token-authenticated REST API

14-user-api-tokens.md

7/7

subtasks

✓
supabase/functions/api Edge Function deployed
✓
/api/observe POST creates a row in observations
✓
/api/identify POST runs the cascade with the user's BYO keys
✓
/api/observations GET (paginated, RLS-scoped to caller)
✓
/api/export GET returns Darwin Core CSV for caller
✓
Scope enforcement (observe/identify/export) per route
✓
Deployed --no-verify-jwt; the function validates rst_* itself
token-ui Token management UI at /profile/tokens (EN) and /perfil/tokens (ES)

14-user-api-tokens.md

6/6

subtasks

✓
Locale-paired routes /profile/tokens and /perfil/tokens
✓
Create form (label + scopes checkboxes) + plaintext modal
✓
List existing tokens (label, scopes, last used, created)
✓
Revoke action with confirm
✓
i18n strings under tr.profile.tokens.*
✓
Copy-to-clipboard button on creation

v1.0 Public Launch

streaks Opt-in streaks + grace window

08-profile-activity-gamification.md

7/8

subtasks

✓
user_streaks table (current_days, longest_days, last_qualifying_day, grace_used_at) + RLS self-read + public-when-opted-in
✓
recompute_streak(p_user_id) PL/pgSQL function with single-grace-per-30-days window logic
✓
Quality gate: only identifications with confidence ≥ 0.4 count toward a qualifying day
✓
supabase/functions/recompute-streaks Edge Function — iterates gamification_opt_in users, RPCs recompute_streak per user
✓
pg_cron schedule (nightly 07:05 UTC) firing the Edge Function via net.http_post
✓
StreakCard.astro on profile page — opt-in CTA, current/longest/last-active display, flame state (active/at_risk/broken/none)
✓
summarizeStreak() helper in src/lib/social.ts derives state from row + today's date
○
Inbox-digest email for milestone streaks (7/30/100 days)
shareable-cards Observation share OG cards
7/7

subtasks

✓
supabase/functions/share-card Edge Function — accepts ?obs_id=, format=html|svg, joins observations + identifications + users
✓
1200×630 SVG renderer with brand stripe, gradient bg, italic species name, date + region + habitat meta
✓
obscure_level='full' returns 404 — fully-withheld species cannot be share-card scraped
✓
Sensitive-species visual badge when obscure_level > 'none' (still surfaces region, hides precise locale)
✓
Public Astro page src/pages/share/obs/[id].astro renders observation with OG/Twitter meta tags pointing to share-card endpoint
✓
Anonymous fallback when profile_public is false — observer rendered as 'Anonymous observer'
✓
XML-escape all user-provided strings to prevent SVG injection
social-features Follows + comments + watchlists schema

08-profile-activity-gamification.md

8/8

subtasks

✓
public.follows (follower_id, followee_id) PRIMARY KEY composite + RLS follows_self_manage + follows_public_read
✓
Anti-self-follow CHECK constraint (follower_id ≠ followee_id) on follows
✓
public.observation_comments with parent_id self-FK (single-level threading) + soft-delete via deleted_at + idx_comments_obs
✓
comments RLS triplet: comments_authenticated_insert + comments_self_update + comments_public_read
✓
public.watchlists (user_id, taxon_id, radius_km, region_geojson) + watchlists_self RLS
✓
Comments.astro component — list + composer + reply (one-level) + edit/delete owned, 2000-char limit
✓
FollowButton.astro — optimistic toggle, hidden when self-target, sign-in CTA on RLS-rejected writes
✓
WatchlistView.astro — surfaces activity_events feed via RLS visibility=followers/public, mark-all-read
expert-system Expert taxonomic 3× weight

08-profile-activity-gamification.md

6/7

subtasks

✓
users.is_expert (boolean) + users.expert_taxa (text[] e.g. ARRAY['Aves','Plantae']) columns on the users table
✓
recompute_consensus(p_observation_id) PL/pgSQL — SUM weight 3.0 when u.is_expert AND t.kingdom = ANY(u.expert_taxa) else 1.0
✓
public.expert_applications table (user_id, taxa[], credentials, institution, orcid, status enum) + indices for pending queue
✓
expert_applications RLS — user reads/inserts own, UPDATE admin-only (withdraw via fresh row keeps audit trail)
✓
ExpertApplyView.astro — taxa multi-select (11 kingdoms), credentials textarea, ORCID validation, pending/approved state UI
✓
Already-expert short-circuit: ExpertApplyView reads users.is_expert and shows thank-you instead of form
○
Expert badges surfaced on profile page (insignia visual when is_expert + per-kingdom chips)
bioblitz-events-schema Events table + RLS (schema)

08-profile-activity-gamification.md

7/7

subtasks

✓
public.events table — id, slug UNIQUE, name, description_md, organiser_id FK→users, starts_at, ends_at, kind
✓
region_geojson geography(Polygon, 4326) NOT NULL — defines the spatial bounds for in-event observations
✓
Three event kinds via CHECK (kind IN ('bioblitz','survey','challenge'))
✓
Composite/temporal index idx_events_time on (starts_at, ends_at)
✓
Spatial GIST index idx_events_region on region_geojson for fast obs-within-region queries
✓
RLS enabled + events_public_read policy (anyone can SELECT — events are publicly browsable)
✓
Idempotent schema (CREATE TABLE IF NOT EXISTS, DROP POLICY IF EXISTS … ; CREATE POLICY) — replayable via make db-apply
institutional-export DwC + SNIB + CONANP CSV presets

06-darwin-core.md

7/7

subtasks

✓
DWC_COLUMNS — full Darwin Core column set in src/lib/darwin-core.ts (toCSV() default)
✓
SNIB_COLUMNS subset — 14 columns aligned with CONABIO SNIB ingest schema (toCsvSnib())
✓
CONANP_COLUMNS subset — 9 columns for ANP report format including informationWithheld (toCsvConanp())
✓
ExportView.astro <select id="format"> with options dwc / snib / conanp
✓
Switch in download handler picks the right serializer per fmt value
✓
Filename suffixes per format — rastrum-{dwc|snib|conanp}-YYYY-MM-DD.csv via downloadCSV()
✓
Unit tests for column subsets and CSV header order in src/lib/darwin-core.test.ts
credentialed-access credentialed_researcher RLS gate

07-licensing.md

5/6

subtasks

✓
users.credentialed_researcher (boolean DEFAULT false) — admin-set after ID verification, no self-serve
✓
users.credentialed_at (timestamptz) + users.credentialed_by (uuid FK→users) for audit trail
✓
obs_credentialed_read RLS policy — SELECT bypasses obscure_level when caller's credentialed_researcher=true
✓
Policy still respects BC/TK Notice withholding (community-level overrides remain in effect)
✓
Idempotent ALTER TABLE … ADD COLUMN IF NOT EXISTS for the three columns
○
Application flow page (UI for self-serve credential request — admin reviews via dashboard)
env-enrichment Lunar phase + OpenMeteo weather Edge Function
7/7

subtasks

✓
supabase/functions/enrich-environment Edge Function (Deno) — POST { observation_id }, service-role client
✓
Conway lunar-phase algorithm — computes phase ('new'..'waning_crescent') + illumination 0..1 from observed_at UTC
✓
OpenMeteo historical archive fetch (CC BY 4.0) — daily precipitation_sum + temperature_2m_mean for the obs lat/lng/date
✓
UPDATE observations SET moon_phase, moon_illumination, precipitation_24h_mm, temp_celsius, post_rain_flag (>5mm) — 5 columns
✓
Auto-fires from src/lib/sync.ts after observation sync — fire-and-forget (failures don't block the sync flow)
✓
Idempotent — safe to invoke repeatedly on the same observation_id (UPSERT semantics via update + observation PK)
✓
Partial-enrichment tolerance — OpenMeteo failure still writes lunar phase; lunar failure leaves columns NULL but doesn't 500
video-support Video support ≤30s (H.265/AV1)

02-observation.md

3/6

subtasks

✓
Video capture in observation form (MediaRecorder ≤30s)
✓
media_files row with media_type='video' + R2 upload
✓
Video preview tile in form grid (autoplay muted)
○
Server-side ffmpeg transcoding pipeline
○
Frame extraction for ID + audio split for BirdNET
○
Video player on share/obs page
camera-trap-ingest Camera trap ingestion (on-device MegaDetector v5a YOLOv5 ONNX)

09-camera-trap.md · 19-batch-photo-importer.md

4/7

subtasks

✓
Bulk-upload UI at /profile/import/camera-trap (drag-drop)
✓
Shared trap location (single GPS) + per-photo EXIF timestamp
✓
Plugin runtime:'client' running MegaDetector v5a via onnxruntime-web (megadetector-yolo.ts letterbox+NMS, megadetector-cache.ts download/cache)
✓
FilteredFrameError short-circuits the cascade for empty/human/vehicle frames (cascade.ts + errors.ts)
!
Operator hosts the ~134 MB ONNX behind PUBLIC_MEGADETECTOR_WEIGHTS_URL

Blocked by: Operator action: convert v5a checkpoint and upload to R2

○
camera_trap_deployments + processing_queue tables (deferred)
○
Edge Function: motion → ID → research-grade pipeline
capacitor-ios Capacitor iOS App Store wrapper (v1.2)
0/5

subtasks

!
Apple Developer Program ($99/yr)

Blocked by: Subscription required

○
Capacitor scaffolding + platform/ios
○
Native plugins (Camera, Geo, FS, Network)
○
App Store metadata + screenshots
○
First TestFlight build
follows-comments-ui UI for follows + threaded comments + watchlists

08-profile-activity-gamification.md

3/5

subtasks

✓
Follow button on public profile pages (FollowButton.astro)
✓
Threaded comments component on share/obs page (Comments.astro)
✓
Watchlist add/edit view in profile (WatchlistView.astro)
○
Followers' research-grade events in activity feed
○
Watchlist alerts (inbox digest, daily cron)
map-location-picker Interactive map picker for observation location

15-map-location-picker.md · 02-observation.md

5/5

subtasks

✓
Drag-pin + tap-to-place on MapLibre
✓
Locality search (geocoder) with EN/ES results
✓
Reverse-geocode to populate locality string
✓
Mobile bottom-sheet UX for the picker
✓
Round-trip integration with ObservationForm fields
my-observations Personal observation list/history page

16-my-observations.md

6/6

subtasks

✓
Route /profile/observations (EN) / /perfil/observaciones (ES)
✓
MyObservationsView component (shared between locales)
✓
Thumbnail grid with status pill (synced/pending/failed)
✓
Click row → /share/obs/<id> for detail
✓
Includes outbox-pending rows (unsynced)
✓
Pagination + newest-first sort
camera-getUserMedia In-app camera via getUserMedia API with file-input fallback

17-in-app-camera.md · 02-observation.md

5/5

subtasks

✓
navigator.mediaDevices.getUserMedia({video:{facingMode:'environment'}})
✓
Live <video> preview + capture-to-canvas → Blob
✓
Permission denied → fallback to <input type=file capture=environment>
✓
Stream cleanup on close to avoid camera-on indicator
✓
Identify + Observe surfaces both wire to it
batch-exif-importer Batch photo importer with EXIF GPS/datetime extraction

19-batch-photo-importer.md

6/6

subtasks

✓
Drag-drop / file-input multi-photo intake
✓
exifr extraction of GPS + DateTimeOriginal per file
✓
Per-row review table (include/exclude, edit fields)
✓
Bulk insert into Dexie outbox + queued sync
✓
EN /profile/import/, ES /perfil/importar/ pages
✓
Camera-trap deep-link (subroute) for staged uploads
oauth-custom-domain Custom auth domain on Supabase OAuth (auth.rastrum.org)

04-auth.md

0/4

subtasks

!
Supabase Pro plan ($25/mo) — required by Supabase for custom auth domain

Blocked by: Deferred for the zero-cost target; default Supabase callback host is fine for v1.0

○
DNS CNAME for auth.rastrum.org
○
Update Site URL + redirect allow-list in Supabase Auth
○
Re-test Google + GitHub OAuth flows on custom domain
mcp-server MCP server for AI agents (JSON-RPC over HTTP at /functions/v1/mcp)

15-mcp-server.md · 14-user-api-tokens.md

6/6

subtasks

✓
supabase/functions/mcp Edge Function with JSON-RPC 2.0 transport
✓
tools/list and tools/call backed by user_api_tokens scopes
✓
5 tools: identify_species, submit_observation, list_observations, get_observation, export_darwin_core
✓
initialize + ping unauthenticated for capability probing
✓
Deployed --no-verify-jwt; the function validates rst_* itself
✓
Documented Claude Desktop, Cursor, VS Code, Copilot Coding Agent integrations
rastrum-org-domain Migrate canonical domain to rastrum.org
6/6

subtasks

✓
Register rastrum.org + DNS via Cloudflare
✓
Update Astro site config + sitemap host
✓
Migrate media bucket hostname to media.rastrum.org
✓
Service worker pass-through host list updated
✓
Old domain (rastrum.artemiop.com) 301-redirects
✓
Documentation updated to reference rastrum.org
ux-revamp-pr1-ia-chrome UX revamp PR 1: IA + chrome rebuild

00-index.md

8/8

subtasks

✓
chrome-mode helper + chrome-helpers module + routeTree (TDD)
✓
i18n strings for tagline, explore dropdown, docs groups, drawer, bottom bar
✓
Explore subroute placeholder pages (/explore/recent, /explore/watchlist, /explore/species)
✓
301 redirect for /profile/watchlist to /explore/watchlist
✓
MegaMenu, MobileBottomBar, MobileDrawer components
✓
Header.astro rewrite with verb-first copy, mobile-friendly layout, accent-color classnames
✓
BaseLayout pb-20 sm:pb-0 for mobile bottom-bar clearance
✓
e2e coverage: active rail highlight, mega-menu renders, FAB targets /observe, drawer toggle, watchlist 301
bioblitz-events-ui Bioblitz events

08-profile-activity-gamification.md

0/6

subtasks

○
Bioblitz events — UI (event detail page, live aggregates, participation badges)
○
Event detail page with title, description, date range, and region polygon on map
○
Live observation count and species tally aggregated from event polygon
○
Participation badge awarded on bioblitz completion via award-badges function
○
Event listing page at /explore/events with upcoming/active/past filters
○
Join event button with auth check and participant count display

v1.1 UX polish (post-launch brainstorm) Planned

chat-improvements-v1-1 Chat v1.1: Gemma 4 text + entity context + 5 typed tools

20-chat

15/15

subtasks

✓
loadGemmaTextEngine: reuse Gemma 4 E2B vision weights via transformers.js for text-only generation (no second download)
✓
chat-engine: streaming + 1-round tool-call loop, Llama fallback on Gemma load failure
✓
Generic EntitySpec registry mirroring src/lib/identifiers/ — 6 built-ins (observation/species/project/camera_station/observer/self_profile)
✓
5 SECURITY INVOKER chat_find_* RPCs + chat_entity_card dispatcher + 6 per-kind card builders
✓
chat-tools.ts: typed JSON tool layer with hand-rolled validators (no Zod), {ok|unknown_tool|invalid_args|network|offline} discriminated union
✓
Two-button consent gate: side-by-side Gemma 4 (recommended) vs Llama 3.2 1B (lighter); refreshState checks both caches
✓
AskRastrumButton + ?attach=kind:id deep-link parser; mounted on share-obs, public profile, project detail, species profile
✓
ChatEntityPicker popover with 6 tabs and search; opens from composer's Context button
✓
Empty-state suggestion chips (Near me, Review observation, How it works, NOM-059) seed input + auto-submit
✓
Header model badge resolved client-side from cache status (Gemma 4 / Llama 1B / On-device)
✓
Chat link added to MobileDrawer (was missing on <sm viewports)
✓
tests/sql/chat.sql regression suite (10 assertions) + db-validate workflow wiring
✓
Privacy invariants: obscure-coords on observation card, self-only on self_profile_card, no-centroid on observer_card
✓
Telemetry events on rastrum:onboarding-event bus (chat.entity.attached, chat.tool.called, chat.tool.failed, chat.engine.fallback)
✓
Runbook: docs/runbooks/chat-improvements.md + index link
community-validation Community validation queue (Module 22)

22-community-validation

7/7

subtasks

✓
Spec v1.0 → v1.3 (Copilot review × 2 + schema audit)
✓
SQL: validation_queue view + 3 RLS policies + UNIQUE index + tie-handling patch on recompute_consensus
✓
Components: ValidationQueueView, SuggestIdModal, ValidationDashboardView
✓
4 routes: /{en,es}/{explore,explorar}/{validate,validar}/ + profile counterparts
✓
Research-grade chip on MyObservationsView + ExploreRecentView
✓
Suggest CTA on every /share/obs/?id= page
✓
Validate card on /explorar/ index
owner-observation-crud Owner CRUD on synced observations

02-observation · 07-licensing

4/4

subtasks

✓
Manage panel on share/obs (visible only to owner)
✓
Edit notes + obscure_level + scientific name override
✓
delete-observation Edge Function — atomic R2 + DB delete
✓
Client wired to delete-observation instead of direct DELETE (no orphan blobs)
og-pipeline Open Graph pipeline (zero per-request compute)

10-media-storage

5/5

subtasks

✓
satori + resvg-js for build-time static-page PNGs
✓
Client-side renderer in src/lib/og-card.ts (lazy import)
✓
Hook into syncOne — per-observation OG card uploaded next to photo
✓
Hook into ProfileEditForm — per-profile OG card on save
✓
BaseLayout meta-tag mapping (path → /og/<slug>.png with override)
karma-phase-1-foundation Karma engine — schema, computation, basic UI (Phase 1)

23-karma-expertise-rarity

11/11

subtasks

✓
user_expertise + taxon_rarity + karma_events tables
✓
taxa.parent_id + ancestor_path trigger + name-rank backfill
✓
is_expert → user_expertise migration shim
✓
refresh_taxon_rarity() + nightly pg_cron
✓
award_karma() helper
✓
recompute_consensus calls award_karma + uses user_expertise weights
✓
src/lib/karma.ts pure helpers + Vitest
✓
Suggest modal pre-vote rarity + magnitudes microcopy
✓
Profile karma section + top-5 expertise
✓
Pokédex page (/profile/dex, /perfil/dex)
✓
Apply schema via make db-apply (operator)
ai-sponsorships AI sponsorships — operator/community-sponsored Anthropic budget
14/15

subtasks

✓
Schema + RLS (sponsorships table, monthly USD budget, owner/public read policies)
✓
_shared/sponsorship.ts helper + unit tests (budget check, charge accounting)
✓
sponsorships Edge Function (CRUD + budget reads, JWT-gated)
✓
identify Edge Function modified to use sponsored key when within budget, BYOK fallback otherwise
✓
UI + i18n + routes (sponsor settings page, profile widget, EN/ES paired)
✓
Smoke tests + lint in CI (Edge Function smoke, type/lint gates)
✓
Rollout + remove server-side ANTHROPIC_API_KEY (operator action)
✓
M27 polish — 9 UX gaps (PR #84)
✓
M27 complete coverage (PR #94)
✓
Resend SMTP integration (active)
✓
Sponsorship request flow
✓
Public docs page /docs/sponsorships
✓
Onboarding tour + replay
✓
AI mode selector in ObserveView2 (Sponsored / Own key / Local toggle with localStorage persistence)

→ Shipped in ObserveView2; closes #328

○
Optional: delete ANTHROPIC_API_KEY env
admin-console-foundation Admin/Moderator/Expert console — PR1 foundation

24-admin-console.md

7/7

subtasks

✓
Schema migration: user_roles + admin_audit + has_role + sync trigger + RLS
✓
Console chrome mode + ConsoleLayout + sidebar + i18n
✓
admin Edge Function dispatcher + 3 handlers (role.grant/revoke + user_audit)
✓
Three tabs ship: Overview, Experts (moved), Audit log
✓
Old /profile/admin/experts/ → 308 redirect
✓
Module spec 24, runbooks (bootstrap, role-model, audit, ops), CLAUDE.md update
✓
Vitest (chrome-mode, console-tabs, admin-client) + Playwright smoke
admin-console-pr2-users-credentials Admin/Moderator/Expert console — PR2 (Users + Credentials tabs, role grants UI)

24-admin-console.md

5/5

subtasks

✓
ConsoleUsersView: search, role chips, grant/revoke slide-over
✓
ConsoleCredentialsView: researcher role grants with notes field
✓
Expert console tabs declared (5 tabs, all stub initially)
✓
Role pill + sidebar routing for moderator + expert
✓
E2E smoke for users + credentials not-auth banners
admin-console-pr3-ops-tabs Admin console — PR3 (Sync + API + Cron read-only ops tabs)

24-admin-console.md

5/5

subtasks

✓
ConsoleSyncFailuresView: error groups table with 7-day filter
✓
ConsoleApiQuotasView: daily quota burn-down + 30-day chart
✓
ConsoleCronRunsView: pg_cron job status table
✓
Moderator console foundation: ConsoleModOverviewView + 4 tabs
✓
E2E smoke for sync + api + cron not-auth banners
admin-console-pr4-observations Admin console — PR4 (Observations admin tab + 4 obs handlers)

24-admin-console.md

5/5

subtasks

✓
ConsoleObservationsView: global obs table with hide/obscure/license chips
✓
Handler: observation.hide + observation.unhide
✓
Handler: observation.obscure + observation.license_override
✓
obs_public_read RLS excludes hidden observations
✓
E2E smoke for observations not-auth banner
admin-console-pr5-moderator Console — PR5 (Moderator console: Flag queue, Comments, Soft-bans + 9 handlers)

24-admin-console.md

7/7

subtasks

✓
ConsoleFlagQueueView: report triage/resolve/dismiss with slide-over
✓
ConsoleCommentsView: hide/unhide/lock/unlock comments
✓
ConsoleBansView: issue + lift soft-bans
✓
Handlers: report.triage/resolve/dismiss (3)
✓
Handlers: comment.hide/unhide/lock/unlock (4)
✓
Handlers: user.ban + user.unban (2)
✓
E2E smoke for flag-queue + comments + bans not-auth banners
admin-console-pr6-expert Console — PR6 (Expert console: Overview, Validation queue, Your expertise)

24-admin-console.md

7/7

subtasks

✓
ConsoleExpertOverviewView: KPI counters + action items + recent contributions
✓
ConsoleExpertValidationView: queue table filtered to expert_taxa + all-taxa toggle
✓
ConsoleExpertExpertiseView: per-taxon scores + search/filter + SVG kingdom chart
✓
Stub: ConsoleExpertOverridesView + ConsoleExpertTaxonNotesView
✓
8 new EN/ES route pages + expert dispatch on /console/ root
✓
Remove stub flag from 3 functional expert tabs in console-tabs.ts
✓
E2E smoke for validation + expertise not-auth banners
admin-overview-real-kpis Admin overview — real platform-state KPIs + alerts + recent activity

24-admin-console.md

3/5

subtasks

✓
Real KPI cards (user count, obs count, sync failures, cron health)
✓
Alert panel (elevated sync failure rate, stale cron run)
✓
Recent activity feed from admin_audit
○
7-day trend sparklines on each KPI card
○
Export KPI snapshot as CSV for monthly reporting
admin-console-pr7-remaining-admin Admin console PR7 — Badges + Taxa + Karma + Features + Bioblitz tabs

24-admin-console.md

6/6

subtasks

✓
ConsoleBadgesView: list + detail + tier chart (stub write buttons)
✓
ConsoleTaxaView: list + detail + rarity map (stub write buttons)
✓
ConsoleKarmaView: config table + rarity multipliers + recent events
✓
ConsoleFeatureFlagsView: flag grid grouped by category (stub toggles)
✓
ConsoleBioblitzView: stub with upcoming feature notice
✓
karma_events admin RLS fix (PR #88)
admin-console-pr8-hardening Admin console PR8 — CORS tighten + rate limit + pgTAP RLS suite + 5 write handlers + DB-backed config

24-admin-console.md

18/18

subtasks

✓
CORS: restrict Access-Control-Allow-Origin to rastrum.org + localhost dev/preview ports
✓
Token-bucket rate limiter (30 req/min/actor, cost 3 for writes) — in-memory per isolate
✓
tests/pgtap/rls.sql — 24 TAP assertions covering has_role + 6 table RLS policies
✓
db-validate.yml: install pgTAP + run RLS suite step
✓
badge.award_manual handler — upsert user_badges + audit row
✓
badge.revoke handler — delete user_badges + audit row
✓
taxon.recompute_rarity handler — call refresh_taxon_rarity() RPC
✓
taxon.toggle_conservation handler — update nom059_status/cites_appendix/iucn_category with taxon_conservation_set audit op
✓
feature_flag.toggle handler — update app_feature_flags.value + updated_by
✓
app_feature_flags table + RLS policies + seed from FEATURE_FLAGS
✓
karma_config + karma_rarity_multipliers tables + RLS policies + seed from TS modules
✓
ConsoleFeatureFlagsView: DB-backed reads + live toggle wired to adminClient.featureFlag.toggle
✓
ConsoleBadgesView: Award + Revoke buttons live with slide-over forms
✓
ConsoleTaxaView: Recompute Rarity + Toggle Conservation buttons live
✓
ConsoleKarmaView: DB-backed reads (karma_config + karma_rarity_multipliers)
✓
adminClient SDK: badge.{awardManual,revoke} + taxon.{recomputeRarity,toggleConservation} + featureFlag.toggle
✓
4 new unit tests for badge + featureFlag SDK methods
✓
Doc sync: progress.json + tasks.json + module 24 + admin-ops.md + CLAUDE.md
admin-console-pr9-quick-actions Admin console PR9 — admin/mod UX (reason templates + URL filters + g-prefix keybindings + mobile sticky bottom-sheet)

24-admin-console.md

5/5

subtasks

✓
ConsoleSlideOver: pre-translated quick reason pills via data attributes
✓
console-filter-state.ts: read/write/applyFilterState helpers (URLSearchParams + history.replaceState)
✓
console-keybindings.ts: g-prefix Vim chord (g a / g u / g r…) + ? help overlay + Escape close
✓
ConsoleSlideOver: sticky bottom-sheet on mobile (env(safe-area-inset-bottom))
✓
i18n parity for all reason templates (EN/ES)
admin-console-pr10-subject-ux Admin console PR10 — subject UX (ban banner + appeals workflow + comment lock + hidden-obs owner badge)

24-admin-console.md

6/6

subtasks

✓
BanBanner.astro mounted in BaseLayout for active bans
✓
ban_appeals table + RLS + appeal.accept/reject handlers
✓
AppealView.astro + /profile/appeal/ pages (EN/ES)
✓
ConsoleAppealsView moderator queue + appeal lifecycle UI
✓
Comments.astro: locked-thread banner + read-only mode
✓
MyObservationsView: hidden-obs owner badge
admin-console-pr11-engineering Admin console PR11 — engineering hygiene (Postgres rate-limit RPC + flash polish + handler-registry test)

24-admin-console.md

4/4

subtasks

✓
rate_limit_buckets table + consume_rate_limit_token() SECURITY DEFINER RPC
✓
_shared/rate-limit.ts: replace in-memory Map with async RPC call (fail-open)
✓
/console/* flash polish: hidden gate div + 1500ms fallback (static-only mode)
✓
tests/unit/admin-handlers-registry.test.ts: regex-based consistency check
admin-console-pr12-observability Admin console PR12 — observability (anomaly detection + weekly health digest + forensics CSV + function_errors sink)

24-admin-console.md

7/7

subtasks

✓
admin_anomalies + admin_health_digests + function_errors tables + RLS
✓
detect_admin_anomalies() — high_rate / bulk_delete / off_hours rules + hourly cron
✓
compute_admin_health_digest() weekly Mondays 09:00 UTC
✓
anomaly.acknowledge + audit.export Edge Function handlers
✓
_shared/csv.ts pure helper + 17 unit tests for CSV escape edge cases
✓
_shared/error-reporter.ts wired into admin dispatcher catch
✓
ConsoleAnomaliesView + ConsoleForensicsView admin tabs (EN/ES)
admin-console-pr13-future-proofing Admin console PR13 — future-proofing (time-bounded roles + two-person rule + webhooks + trust score primitive)

24-admin-console.md

9/9

subtasks

✓
user_roles.expires_at + auto_revoke_expired_roles() daily 08:15 UTC cron
✓
admin_action_proposals state machine + expire_stale_proposals() hourly cron
✓
proposal.create / approve / reject handlers + IRREVERSIBLE_OPS registry
✓
Self-approval guard in _shared/proposal-guards.ts (testable)
✓
admin_webhooks + admin_webhook_deliveries tables + dispatch_admin_webhooks() trigger
✓
HMAC-SHA256 signing helper + webhook.create/update/delete/test handlers
✓
compute_moderator_trust_score() v1 placeholder + moderator_trust_scores view
✓
ConsoleProposalsView + ConsoleWebhooksView admin tabs (EN/ES)
✓
4 new runbooks: admin-time-bounded-roles, admin-two-person-rule, admin-webhooks, admin-trust-scores
admin-console-pr14-deferred-cleanup Admin console PR14 — close v1.1 deferred items (per-admin tz + webhook _meta replay + reconcile cron + real trust formula + irreversible enforcement gate + durable observability dry-run)

24-admin-console.md

9/9

subtasks

✓
users.timezone column + per-admin off_hours rule (LEFT JOIN + COALESCE → UTC)
✓
Profile → Edit timezone picker (9 IANA zones, canonical America/Argentina/Buenos_Aires)
✓
admin_webhook_deliveries.nonce + .request_id columns + signed _meta envelope
✓
reconcile_webhook_deliveries() SECURITY DEFINER + 2-min cron (writes back async pg_net status)
✓
compute_moderator_trust_score() v1.1 formula (anomaly + overturn + active_days + recency, clamped 0-100)
✓
checkIrreversibleEnforcement() pure function + enforce_two_person_irreversible feature flag (default off)
✓
ConsoleSlideOver irreversible prop + 'Require approval' toggle on Users + Observations slide-overs
✓
.github/workflows/admin-observability-dryrun.yml — one-shot 2026-05-06 + weekly Mondays + red badge if reconcile stuck
✓
Doc sync: 4 runbooks updated, CLAUDE.md status line bumped, sentinel asserts +4
admin-console-pr15-observability-ui Admin console PR15 — observability UI (Health digest cards + sparklines + Errors browser with bulk ack + per-webhook deliveries drilldown + replay)

24-admin-console.md

10/10

subtasks

✓
function_errors.acknowledged_at + acknowledged_by + ack_notes columns + partial unack index
✓
audit_op enum +4: health_recompute, error_acknowledge, error_acknowledge_bulk, webhook_replay
✓
Edge Function handlers: health.recompute + error.acknowledge[_bulk] + webhook.replay_delivery
✓
ConsoleHealthView — hero card + 12-week sparklines + manual recompute (EN/ES)
✓
ConsoleErrorsView — function_errors browser with URL filters + severity pills + auto-refresh + single + bulk ack (EN/ES)
✓
ConsoleWebhooksView — per-webhook deliveries drilldown + filter chips + replay last + nonce copy
✓
Pure helpers: src/lib/health-delta.ts + src/lib/error-severity.ts (10 + 8 unit assertions)
✓
Console keybindings: g h → health, g e → errors (g x reclaimed for experts), help overlay updated
✓
rls.sql 32nd assertion + sentinel +3 columns; admin-handlers-registry test bumps to 36 actions
✓
Runbooks: admin-function-errors.md (new) + admin-health-digest UI section + admin-webhooks PR15 section + admin-ops 4 new entries
admin-console-pr16-entity-browsers Admin console PR16 — read-only entity browsers (Identifications, Notifications, Media, Follows, Watchlists, Projects, Taxon changes) on shared template + runtime

24-admin-console.md

13/13

subtasks

✓
Hot-path indexes (filter_field, created_at DESC) on identifications, notifications, media_files, follows, projects, watchlists + admin-SELECT policies on notifications + watchlists
✓
ConsoleEntityBrowser.astro shared template + entity-browser.ts runtime (server-side pagination, URL-driven filters, FK lookups, auto-populated dropdowns, drilldown expansion, rowIdFromRow for composite-PK tables)
✓
ConsoleIdentificationsView — filter by identifier/taxon/source/validated/research-grade; drill-down full row + raw_response + obs deep-link
✓
ConsoleNotificationsView — filter by recipient/kind (auto-populated)/read state/from-date; drill-down full payload jsonb
✓
ConsoleMediaView — filter by type/state/observation; thumbnail with loading=lazy; drill-down full preview + R2 URL + EXIF
✓
ConsoleFollowsView — filter by follower/followee (autocomplete)/status/tier; rowIdFromRow synthesises stable id from (follower_id, followee_id)
✓
ConsoleWatchlistsView — filter by user/taxon/scientific_name (ilike); drill-down full row JSON. Schema note: no `region` column in v1; uses radius_km + digest_only as visible facets
✓
ConsoleProjectsView — backed by projects_with_geojson view (security_invoker); filter by owner/visibility/name; drill-down GeoJSON + species_list + lazy obs count via head:true count(*)
✓
ConsoleTaxonChangesView — backed by admin_audit filtered to taxon_* ops via baseline-filter sentinel; filter by taxon/action/actor/date; drill-down before/after diff JSON
✓
EN+ES page wrappers for all 7 entity browsers (14 pages); console-tabs.ts registers taxon-changes; routes.consoleTaxonChanges + routeTree label pair
✓
i18n: console.{identifications,notifications,media,follows,watchlists,projects,taxonChanges}View namespaces EN+ES + tab labels + entityBrowser shared strings
✓
Tests: console-tabs.test.ts bumped to 39 tabs (32 + 7 PR16); entity-browser.test.ts (212 LOC: filter predicates + applyFilters + render helpers); 681 tests pass
✓
Runbook: docs/runbooks/admin-entity-browsers.md (template pattern, performance characteristics, recipe for adding a new entity browser, composite-PK handling, lazy aggregate facets)
community-observers-pr17-ux-fixes Community observers UX fixes — privacy explainer + country CTA + GPS-based Nearby

28-community-discovery.md

10/10

subtasks

✓
Privacy-default explainer banner — viewer-state-aware copy (private vs public profile); renders on 0 rows + signed in
✓
Set-country inline CTA — only when viewer is signed in AND country_code IS NULL; links to /profile/edit/
✓
community_observers_nearby_at(lat, lng, radius_m, …) SECURITY INVOKER RPC; granted to authenticated only; reuses GiST index on centroid_geog
✓
📍 Use my location pill via navigator.geolocation; coords stored in sessionStorage (key rastrum.community.gps), NEVER URL — Referer-leak prevention
✓
Fallback to centroid Nearby on geolocation deny; friendly toast
✓
loadGps / saveGps / clearGps helpers in community-url.ts (testable, dependency-injected)
✓
Regression-guard test: 'NEVER serializes GPS coords into the URL' + 9 sessionStorage round-trip / malformed / out-of-range cases
✓
loadViewerCommunityMeta() — single query for {signedIn, profilePublic, countryCode}; no N+1
✓
Sentinel verify entries for both nearby RPCs
✓
CLAUDE.md M28 4th load-bearing rule + docs/runbooks/community-discovery.md UX clarifications section
console-chrome-rendering-fix Console chrome rendering fix — restore sidebar + role pills (70 console pages were using BaseLayout, ConsoleLayout was dead code)

24-admin-console.md

10/10

subtasks

✓
Diagnose: ConsoleLayout existed but unused; all 70 /console + /consola pages imported BaseLayout, leaving the chrome unrendered
✓
Refactor ConsoleLayout to be SSG-safe: props (allRoles, activeRole, currentPath, identity) made optional with sensible defaults
✓
Server-render all 3 role tab lists with data-role attributes, all initially hidden; client script reveals based on getUserRoles()
✓
Same pattern for role pills: all 3 ship hidden, JS reveals only those held by the viewer; active role styled with role accent
✓
Active tab highlighting via currentPath = Astro.url.pathname (works at build time since each route has its own static HTML)
✓
Swap BaseLayout → ConsoleLayout in all 35 EN console pages + 35 ES consola pages (70 total)
✓
BaseLayout: console.warn at build time if a /console/* or /consola/* path slips through using BaseLayout (regression guard)
✓
Verification: grep dist/ confirms <aside + data-console-pill + data-role admin/moderator/expert present on all sampled pages
✓
New runbook: docs/runbooks/admin-chrome-rendering.md (the ConsoleLayout-only invariant)
✓
CLAUDE.md Console section: 4th load-bearing rule documenting the ConsoleLayout-only contract + chrome-rendering runbook cross-link
ux-confidence-ring Confidence ring (SVG arc graded emerald→amber→red) instead of percentage pill

13-identifier-registry.md

6/6

subtasks

✓
Confidence ring (SVG arc graded emerald→amber→red) instead of percentage pill — faster comprehension for non-technical users
✓
SVG arc component with graded color stops (emerald > 0.7, amber 0.4-0.7, red < 0.4)
✓
Animated arc fill on mount for visual engagement
✓
Accessible aria-label with numeric confidence for screen readers
✓
Replace percentage pill on observation detail and share card views
✓
Vitest snapshot test for ring rendering at boundary values (0, 0.4, 0.7, 1.0)
ux-quick-taxon-chips Quick-taxon icons under photo upload (🌿 plant · 🐦 bird · 🐾 mammal · 🐛 insect · 🍄 fungus)

02-observation-form.md

6/6

subtasks

✓
Quick-taxon icons under photo upload (🌿 plant · 🐦 bird · 🐾 mammal · 🐛 insect · 🍄 fungus) — primes the cascade and sets honest user expectations
✓
Chip row component with 5 kingdom icons below photo upload area
✓
Tap chip sets taxon_hint field on observation for cascade priming
✓
Visual feedback: selected chip gets ring highlight + deselect on second tap
✓
i18n labels for all 5 chip tooltips in EN + ES
✓
Mobile-friendly touch targets (min 44px) with horizontal scroll on narrow screens
ux-photo-compression Auto-compress photos > 4 MP to 4 MP via canvas before upload

02-observation-form.md

6/6

subtasks

✓
Auto-compress photos > 4 MP to 4 MP via canvas before upload — cuts R2 footprint 4×, speeds sync on slow networks, no ID quality loss
✓
Canvas-based resize to 4 MP max before upload (preserves aspect ratio)
✓
JPEG quality parameter tuned for ID accuracy vs file size tradeoff
✓
Skip compression for images already under 4 MP threshold
✓
EXIF orientation preserved through compression pipeline
✓
File size reduction logged for sync performance analytics
ux-share-button Share button on result card → /share/obs/<id> with existing OG card; critical for viral pi

03-observation-detail.md

6/6

subtasks

✓
Share button on result card → /share/obs/<id> with existing OG card; critical for viral pickup during family launch
✓
Web Share API integration with fallback to clipboard copy
✓
Share URL points to /share/obs/<id> with pre-rendered OG card
✓
Share button positioned on observation result card after identification
✓
Toast confirmation after successful share or clipboard copy
✓
Analytics event tracking for share button taps
ux-save-as-draft Save observation as draft without GPS

02-observation-form.md

6/6

subtasks

✓
Save observation as draft without GPS — current form blocks submit on missing location, breaking the flow for users in cell-dead zones
✓
Draft status on Dexie outbox entries when GPS is missing
✓
Resume draft flow: list drafts on observe page with edit/delete actions
✓
Visual indicator distinguishing drafts from pending-sync observations
✓
Auto-save to draft on page navigation away from incomplete form
✓
Draft count badge on observe nav item when drafts exist
ux-onboarding-tour First-signup onboarding
6/6

subtasks

✓
First-signup onboarding — 3 dismissible cards (install PWA → take a photo → see your profile); skippable; never repeats
✓
3 dismissible onboarding cards (install PWA, take a photo, see your profile)
✓
Cards shown only on first sign-up via localStorage flag
✓
Skip button to dismiss all remaining cards at once
✓
Never repeats after dismissal (localStorage persistence)
✓
Bilingual card content in EN + ES matching user locale
ux-chat-suggestions Chat follow-up suggestion chips below each AI reply (¿Es venenosa?, ¿Cómo distingo de X?,

13-identifier-registry.md

6/6

subtasks

✓
Chat follow-up suggestion chips below each AI reply (¿Es venenosa?, ¿Cómo distingo de X?, Hábitat) — reduce cold-start friction
✓
Suggestion chip row rendered below each AI reply in chat
✓
Context-aware chips based on identified species (toxicity, habitat, lookalikes)
✓
Tap chip auto-fills chat input and sends follow-up question
✓
Bilingual chip labels in EN + ES
✓
Chips hidden after one is selected to avoid clutter
ux-chat-persistence Persist chat conversation history per device in Dexie
6/6

subtasks

✓
Persist chat conversation history per device in Dexie — currently lost on reload
✓
Dexie table for chat conversation history (per-device, per-user)
✓
Restore previous conversation on /chat page mount
✓
Clear conversation button with confirmation dialog
✓
Conversation auto-pruned after 50 messages to limit IndexedDB size
✓
Scroll-to-bottom on page load when restoring conversation
ux-explore-time-slider Time-slider on /explore map

12-explore-map.md

6/6

subtasks

✓
Time-slider on /explore map — drag months to see phenological patterns; visually striking for first-time visitors
✓
Range slider component for month selection on /explore map
✓
GeoJSON source filtered by observation date range on slider change
✓
Animated transition when filtering observations by time window
✓
Month labels displayed below slider with locale-aware formatting
✓
Reset button to clear time filter and show all observations
ux-skeleton-screens Replace bare spinners with skeleton screens on /observations, /explore, profile
6/6

subtasks

✓
Replace bare spinners with skeleton screens on /observations, /explore, profile — cumulatively makes the platform feel snappier
✓
Skeleton placeholder components for observation cards, profile sections, explore grid
✓
CSS shimmer animation on skeleton elements for loading perception
✓
Skeleton dimensions match real content to prevent layout shift (CLS)
✓
Graceful transition from skeleton to real content on data load
✓
Dark mode compatible skeleton colors via CSS custom properties
ux-voice-chat-input SpeechRecognition voice input in /chat

13-identifier-registry.md

6/6

subtasks

✓
SpeechRecognition voice input in /chat — accessibility win, especially for indigenous-language native speakers
✓
SpeechRecognition API integration with microphone permission handling
✓
Voice input button in /chat with recording indicator animation
✓
Transcript auto-fills chat input field on speech end
✓
Language detection matching current locale (EN or ES) for recognition
✓
Graceful fallback message when SpeechRecognition is not supported
ux-first-observation-celebration First-observation confetti + 'Bienvenido a Rastrum 🌱' banner on the user's literal first s

08-profile-activity-gamification.md

6/6

subtasks

✓
First-observation confetti + 'Bienvenido a Rastrum 🌱' banner on the user's literal first synced observation
✓
Confetti animation triggered on first synced observation completion
✓
Bilingual welcome banner with species illustration
✓
First-observation flag stored in user profile to prevent repeat
✓
Banner auto-dismisses after 5 seconds with manual close button
✓
Activity feed event logged for first observation milestone
ux-indigenous-taxa-search Indigenous-language taxon search (Zapoteco / Náhuatl / Maya / Mixteco / Tseltal → scientif

07-licensing.md

0/6

subtasks

○
Indigenous-language taxon search (Zapoteco / Náhuatl / Maya / Mixteco / Tseltal → scientific name); requires corpus + governance per local-contexts
○
Corpus partnership with CONABIO and community Co-PIs for indigenous name data
○
Indigenous name lookup table: Zapoteco, Nahuatl, Maya, Mixteco, Tseltal to scientific name
○
Search autocomplete supporting indigenous language input alongside Spanish/Latin
○
Governance review and community consent protocol before deployment
○
Local Contexts BC/TK notice integration for culturally sensitive taxa
ux-photo-dedupe Image deduplication on submit

02-observation-form.md

6/6

subtasks

✓
Image deduplication on submit — perceptual hash warns when the same photo is being re-uploaded
✓
Perceptual hash computation (pHash) on image upload in observation form
✓
Duplicate detection warning when hash matches an existing upload in session
✓
User can override warning and submit duplicate intentionally
✓
Hash comparison runs client-side with no server round-trip
✓
Performance budget: hash computation under 100ms per image on mid-range mobile
ux-streak-push Web Push notification at 8 PM local when a streak is 1 day from breaking

08-profile-activity-gamification.md

6/6

subtasks

✓
Web Push notification at 8 PM local when a streak is 1 day from breaking — opt-in only, single nightly notification
✓
Web Push subscription opt-in UI in profile notification preferences
✓
Edge Function to check streaks at risk (1 day from breaking) nightly
✓
Push notification payload with streak count and motivational message (EN + ES)
✓
Respect user timezone for 8 PM local delivery via timezone column on users table
✓
Rate limit: maximum one push notification per user per day
delete-observation-atomic Atomic delete-observation Edge Function

03-observation-detail.md

6/6

subtasks

✓
Atomic delete-observation Edge Function — wipes R2 photos + OG card alongside DB rows; no orphan blobs
✓
Edge Function that deletes observation + all media_files + R2 blobs in one transaction
✓
R2 object deletion via presigned DELETE or service binding
✓
OG card cleanup alongside media deletion (no orphan share cards)
✓
RLS check ensuring only observation owner or admin can trigger delete
✓
Confirmation dialog in UI before triggering irreversible delete
suggest-from-share-page Suggest identification from any /share/obs page

22-community-validation.md

6/6

subtasks

✓
Suggest identification from any /share/obs page — community-suggestions list visible to all viewers
✓
Suggest identification modal on /share/obs page for community input
✓
Community suggestions list visible to all viewers below primary ID
✓
Auth check: only signed-in users can submit suggestions
✓
Suggestion includes taxon name, confidence, and optional notes
✓
Consensus recomputation triggered when new suggestion is added
karma-phase-2-engagement Karma engagement layers

23-karma.md

0/6

subtasks

○
Karma engagement layers — toast, digest, leaderboards (Phase 2)
○
Toast notification on karma milestone achievements (100, 500, 1000 points)
○
Weekly karma digest email summarizing points earned and rank change
○
Community leaderboard page at /explore/leaderboard with opt-in visibility
○
Karma breakdown tooltip on profile showing points by category
○
Seasonal karma challenges with time-limited bonus multipliers
karma-phase-3-conservation-bonuses Karma conservation bonuses

23-karma.md

0/6

subtasks

○
Karma conservation bonuses — IUCN/NOM-059 multipliers (Phase 3)
○
IUCN Red List status multiplier (Vulnerable 1.5x, Endangered 2x, CR 3x)
○
NOM-059 category multiplier for Mexican endemic threatened species
○
First-observation-of-species bonus for new taxa records in a region
○
Protected area observation bonus when GPS falls within ANP polygon (M29)
○
Conservation karma leaderboard filtered by region and time period

v1.2 Profile privacy & public profile

profile-privacy-matrix Profile privacy matrix (module 25)

25-profile-privacy

15/15

subtasks

✓
Schema: users.profile_privacy JSONB (19-key matrix) + dismissed_privacy_intro_at + GIN index
✓
can_see_facet(target, facet, viewer) STABLE SQL function
✓
can_see_facets() batch RPC (jsonb result)
✓
Migration backfill from module 08's profile_public boolean
✓
users_update_self_privacy RLS policy
✓
Add Privacy as 5th tab in SettingsShell.astro (/profile/settings/privacy/) with 3 presets + 19-row matrix + live preview
✓
Widen SettingsShell activeTab union: 'profile'|'preferences'|'data'|'developer'|'privacy' + add settings.tabs.privacy i18n keys (EN+ES)
✓
Add 4 entries to PR-4 command-palette index: Privacy settings, Pokédex, View my public profile, Make profile private (action)
✓
Register routeTree entries: profileSettingsPrivacy, publicProfile, publicProfileDex (PR-3 breadcrumbs/footer pickup)
✓
Insert 6th onboarding step (PR-5 spotlight tour): Pick your privacy preset
✓
Module 23 hand-off: replace user_expertise_public_read with user_expertise_facet_read
✓
Onboarding step (module 18): Pick your privacy preset
✓
Module 22 voter-link gating (anonymous expert when profile=private)
✓
Intro banner + dismissed_privacy_intro_at
✓
pgTAP test: 9-cell matrix coverage of can_see_facet()
public-profile-route Public profile at /u/<username>/

25-profile-privacy

14/14

subtasks

✓
Astro routes: /en/u/[username]/index.astro + /es/u/[username]/index.astro
✓
PublicProfileView orchestrator with batched can_see_facets() call
✓
ProfileHero + ProfileObservationMap + ProfileTopSpecies + ProfileValidationReputation components
✓
ProfileEmptyState (404-equivalent for profile=private)
✓
OG card gating (public/signed_in/private renders)
✓
Sitemap filter: exclude profile_privacy.profile <> 'public'
✓
noindex + nofollow meta on non-public profiles
✓
Person JSON-LD entity on /u/<username>/ when profile=public (PR-C/E SEO bar)
✓
Per-page metadata for /u/<username>/: canonical, hreflang en↔es pair, OG/Twitter (PR-B parity)
✓
Sitemap enumeration of public profiles (filter profile_privacy.profile = 'public', emit hreflang pairs)
✓
301 redirect from legacy /{lang}/profile/u/?username=… to /u/<username>/
✓
Update internal links: MyObservationsView, ProfileView, ExploreRecentView, ValidationQueueView, /share/obs
✓
Rewrite PublicProfileView to use can_see_facets() instead of profile_public
✓
Remove profile_public checkbox from ProfileEditForm; StreakCard sets profile_privacy.profile instead
profile-widgets-richer Richer profile widgets (heatmap, donut, streak, top species, mini-map)

25-profile-privacy · 08-profile-activity-gamification

5/5

subtasks

✓
ProfileCalendarHeatmap (12-month SVG grid, click → filtered list)
✓
ProfileTaxonomicDonut (kingdom/phylum/family breakdown)
✓
ProfileStreakRing (current arc + longest faded outer arc)
✓
Facet-gated views: profile_calendar_buckets, profile_taxonomic_donut, profile_top_species, profile_karma, profile_pokedex
✓
ProfilePokedexLink + visitor route /u/<username>/dex/
social-graph-m26 Social graph + reactions (Module 26)

26-social-graph.md

13/13

subtasks

✓
follows table + counters trigger + privacy helpers (social_visible_to, is_collaborator_of) + collaborator coord-precision unlock
✓
per-target reaction tables: observation_reactions, photo_reactions, identification_reactions (+ RLS, kind enums, unique constraints)
✓
blocks (read-symmetric) + reports (queue with reason enum) + notifications (kind enum + 90-day prune cron)
✓
fan-out triggers: follow → notification, observation_reactions → notification (skip blocked recipients)
✓
Edge Functions: follow (request/accept/reject + rate-limit), react (idempotent toggle), report (with operator email)
✓
TypeScript types (types.social.ts) + social client (followUser, react, reportTarget, blockUser) + 5 unit tests
✓
i18n: socialgraph.* namespace EN+ES (sidesteps existing flat social.* namespace) + new routes (inbox, profileFollowers, profileFollowing)
✓
UI: ReactionStrip, FollowersView, FollowingView, InboxView, BlockedUsersList + EN/ES paired pages
✓
tests: tests/sql/social-rls.sql regression + tests/e2e/social.spec.ts smoke
✓
schema applied to prod (auto-fired by db-apply.yml on merge of #43); MIN(uuid) view bug fixed in #62
✓
Edge Functions deployed (follow/react/report) — auto-deployed by deploy-functions.yml after CI/CD revamp in #62
✓
UI integration (#63 + #64): BellIcon→/inbox + unread badge, rich notification cards (avatars + thumbnails + type-coded icons + skeletons + empty state), profile follower/following pills, profile overflow ⋮ menu (Block + Report), shared ReportDialog modal in BaseLayout (focus trap + Esc + backdrop close), FollowButton swapped to follow Edge Function with Requested state, ReactionStrip wired interactive on /share/obs/
✓
v1.1 follow-ups (DELIVERED in PR #97 + #100 + #101): see social-graph-m26-v11, visitor-pokedex-route, deploy-functions-resilience entries below
social-graph-m26-v11 M26 v1.1 polish — reactions on feed + Block/Report on cards/comments + ARIA

26-social-graph.md

6/6

subtasks

✓
observation_reaction_summary view (security_invoker = true) for batched reaction counts without N+1
✓
❤ N reaction count chip on ExploreRecentView + MyObservationsView feed cards
✓
Overflow ⋮ menu on observation cards (Block + Report) — same pattern as PublicProfileViewV2; hidden on owner cards
✓
Block/Report per-comment in Comments.astro (lazy import of blockUser, ReportDialog with target='comment')
✓
ARIA: aria-live region on ReactionStrip toggle, role=menu/menuitem on overflows, aria-label per InboxView row
✓
i18n EN/ES under socialgraph.cards.* + socialgraph.reactions.* (12 new keys)
visitor-pokedex-route Public visitor Pokédex route /u/dex/?username=

25-profile-privacy.md

6/6

subtasks

✓
Parametrize PokedexView.astro with visitor mode prop (owner self-view fallback when prop absent)
✓
Privacy gate via can_see_facet(target, 'pokedex', viewer) RPC; private fallback when false
✓
EN + ES paired pages at /u/dex/ with noindex,nofollow
✓
PublicProfileViewV2.astro — un-hide Pokédex link for visitors (owner → /profile/dex/, visitor → /u/dex/?username=)
✓
OG card: profile-dex-visitor slug in generate-og.ts + ogSlugForPath() in BaseLayout (EN + ES)
✓
i18n: pokedex.visitor_* keys (title, subtitle, empty, private, not_found, not_found_cta, back_to_profile)
deploy-functions-resilience Pin esm.sh imports across Edge Functions for deploy resilience

13-identifier-registry.md

3/4

subtasks

✓
Pin @supabase/supabase-js@2 → @2.39.7 across 24 Edge Function files
✓
Verify all other esm.sh imports already pinned (aws-sdk@3.658.1, jszip@3.10.1, zod@3.23.8)
✓
Inline pin during merge cascade for new file recompute-user-stats/index.ts that landed mid-PR
○
Follow-up: pin imports in PR #99's 5 new admin handlers once #99 merges (~5 LOC patch)
m26-v1-1-review-followups M26 v1.1 review follow-ups — minor polish from PR #100/#101 reviewer notes

26-social-graph.md

6/6

subtasks

✓
?u= alias on readUsername() — removed (audit showed zero references outside docs across PublicProfileViewV2, FollowingView, FollowersView, PokedexView)
✓
document.title in JS → use i18n key for consistency (PublicProfileViewV2, ExploreSpeciesView, PokedexView)
✓
runVisitor catch-all → distinguish error vs not-found state (Pokedex visitor mode now shows visitor_load_error for network/RPC errors, visitor_not_found only for 0-row PGRST116)
✓
ConfirmDialog component — replaces confirm()/alert() in Block flow + delete-photo flow (matches ReportDialog pattern; closes notes from PR #101 + #125 reviews)
✓
Extract overflow-menu logic into lib/overflow-menu.ts (replaces 3× duplication across PublicProfileViewV2, ExploreRecentView, Comments — wireOverflowMenu(wrap, trigger, menu) returns teardown)
✓
Standardize fave_count_one vs fave_aria_one data-attr naming — both consumers now use data-label-fave-aria-one/other
community-discovery-m28 Community discovery (Module 28)

28-community-discovery.md

5/5

subtasks

✓
PR1 — schema deltas: 6 new users columns + 7 partial indexes scoped to (NOT hide_from_leaderboards AND profile_public) + iso_countries reference table + 28-country bilingual seed + normalize_country_code() + dual views (community_observers anon-safe / community_observers_with_centroid auth-only)
✓
PR2 — recompute-user-stats Edge Function + nightly cron at 08:00 UTC + SECURITY DEFINER recompute_user_stats() wrapper
✓
PR3 — backfill recompute-user-stats once after deploy to seed counters/centroids/country_code for existing users. Automated as a workflow_dispatch button at .github/workflows/community-backfill.yml (POSTs the EF, surfaces rows_updated/elapsed_ms in the Actions UI, idempotent)
✓
PR4 — Profile → Edit: country picker + 'Show me in community discovery' toggle (UI-true → DB-false hide_from_leaderboards) + country_code_source 'auto'/'user' for the 'inferred from your region' badge + buildCommunityDiscoveryPayload() pure helper + 9 unit tests
✓
PR5+PR6 — atomic landing: /community/observers/ CSR page + composable filter chips (sort, country, taxon, experts-only, nearby) + sign-in gate on Nearby + URL-state serializer + community_observers_nearby RPC + MegaMenu split (Biodiversity / Community columns) + MobileDrawer subheading + i18n rewrite of the two shipped 'no leaderboards' strings + OG card + roadmap + module spec status → 'shipped'
obs-detail-redesign Observation detail page redesign — viewer + owner edit
6/7

subtasks

✓
PR1 — extract reusable MapPicker.astro from ObservationForm. Per-instance HTML IDs (-${pickerId} suffix). Native modal a11y preserved. Playwright regression test for the observe-form flow
✓
PR2 — schema: observations.last_material_edit_at + media_files.deleted_at + idx_media_files_active + observations_material_edit_check_trg trigger (location > 1 km, observed_at > 24 h, primary_taxon_id change). Extract observation-enums.ts + obs_detail.* i18n namespace EN+ES
✓
PR3 — PhotoGallery.astro (native lightbox + keyboard + swipe + canShare probe + dynamic 'Photo N of M' aria-labels) + ShareObsView.astro extraction to two-column desktop / stacked mobile + share/obs/index.astro slimmed to ~15 LOC + coords readout + 'Edited after IDs' badge + Playwright e2e (5 cases) + ShareObsView ternary strings migrated to obs_detail.view.*
✓
PR4 — ObsManagePanel.astro Details tab: editable date/time + habitat + weather + establishment_means + scientific-name override + notes + obscure-level. Material-edit trigger fires automatically on save
✓
PR5 — ObsManagePanel.astro Location tab: read-only view picker (pickerId='obs-detail-loc-view') + edit-modal picker (pickerId='obs-detail-edit') wired by wireManagePanelLocation; pointGeographyLiteral helper + 5 vitest cases + 5 Playwright e2e cases
✓
PR6 — Photos tab + delete-photo Edge Function (atomic transaction via delete_photo_atomic SECURITY DEFINER RPC: media_files.deleted_at + identifications demote clearing validated_by/validated_at/is_research_grade + observations.last_material_edit_at). Photo add via R2 upload flow. Cascade-photo confirm dialog driven by willDemote() helper (covered by vitest)
○
v1.1 follow-ups: gc-orphan-media cron (R2 GC of soft-deleted blobs); native vs PhotoSwipe lightbox decision (was native ≤150 lines per spec)
projects-anp-m29 Projects (Module 29) — ANP polygon protocols

29-projects-anp.md

3/4

subtasks

✓
Schema: projects (slug UNIQUE, polygon geography(MultiPolygon, 4326), visibility public/private) + project_members + RLS + GIST index
✓
BEFORE INSERT/UPDATE OF location trigger on observations: ST_Covers(polygon, NEW.location) → auto-tag NEW.project_id (manual override respected, ORDER BY created_at ASC tiebreaker)
✓
upsert_project SECURITY DEFINER RPC + projects_with_geojson view WITH (security_invoker = true) + UI (List/New/Detail) at /{en,es}/{projects,proyectos}/
○
v1.1 follow-up: surface Projects link from chrome (Header MegaMenu Biodiversity column) — currently only reachable via direct URL
cli-batch-import-m30 CLI batch import for camera-trap memory cards (Module 30)

30-cli-batch-import.md

4/4

subtasks

✓
cli/ Node 20+ TypeScript package (rastrum-import) — walker + EXIF reader + orchestrator
✓
POST /api/upload-url Edge Function endpoint — issues presigned R2 PUT URLs scoped by rst_* token
✓
--project-slug flag auto-tags every observation into the M29 polygon. State file makes the import resumable across SD recoveries
✓
Built for the CONANP-Oaxaca / DRFSIPS / PROREST 2026 workflow (500–2000 images per camera deployment)
camera-stations-m31 Camera stations + sampling-effort tracking (Module 31)

31-camera-stations.md

3/4

subtasks

✓
Schema: camera_stations (project_id FK + station_key + coords + camera_model)
✓
Schema: camera_station_active_periods (start/end) — multiple per station for redeployments across a year
✓
Foundation for downstream wildlife indices: Relative Abundance Index (RAI), detection rate per 100 trap-nights, species richness — all blocked on knowing how long the camera was sampling
○
v1.1 follow-up: UI to capture active periods (CRUD form + station picker on observations + station map layer)
multi-provider-vision-m32 Multi-provider vision + per-sponsor model + platform pool (Module 32)

32-multi-provider-vision.md

4/4

subtasks

✓
_shared/vision-provider.ts — VisionProvider interface implemented by 6 providers (Anthropic api_key + oauth_token, Bedrock with AWS Sig V4, OpenAI, Azure OpenAI, Gemini)
✓
Per-sponsor preferred_model column on ai_credentials — beneficiaries can request Haiku/Sonnet/Opus or Bedrock equivalents (closes #116)
✓
OpenAI / Azure OpenAI / Google Gemini / Vertex AI providers (closes #118)
✓
Platform-wide call pool: sponsor_pools table + consume_pool_slot RPC (closes #115)
social-graph-m26-ui M26 UI integration

26-social-graph.md

7/7

subtasks

✓
M26 UI integration — bell→inbox + unread badge, rich notification cards, profile follower/following pills + Block/Report overflow, shared ReportDialog with focus trap, ReactionStrip on /share/obs/, FollowButton on the new follow Edge Function (Requested state for private profiles)
✓
Bell icon repointed to /inbox with unread badge from notifications table
✓
Rich notification cards with avatars, thumbnails, type-coded icons, skeletons
✓
Profile follower/following pills + overflow menu (Block + Report)
✓
Shared ReportDialog modal with focus trap, Esc close, reason radio + optional note
✓
FollowButton calling m26 follow Edge Function with Requested state for private profiles
✓
ReactionStrip rewritten to self-hydrate and wired interactive on /share/obs/
ci-cd-edge-auto-deploy CI/CD
5/6

subtasks

✓
CI/CD — auto-deploy Edge Functions on path-filtered push (mirrors db-apply.yml). Filesystem-derived function list eliminates drift; manual workflow_dispatch preserved for surgical rollback
✓
Path-filtered GitHub Actions workflow triggering on supabase/functions/** changes
✓
Filesystem-derived function list eliminates drift between UI enum and deploy loop
✓
Manual workflow_dispatch preserved for surgical rollback of individual functions
✓
Deploy job uses pinned supabase CLI version for reproducibility
○
Slack/email notification on deploy failure with function name and error log
ci-rls-presence-check CI gate
6/6

subtasks

✓
CI gate — db-validate.yml fails if any public.* table lacks ENABLE ROW LEVEL SECURITY. Closes the loop on the Supabase rls_disabled_in_public lint by catching missing RLS at PR review time instead of via the after-the-fact security alert email
✓
db-validate.yml workflow checking all public.* tables have ENABLE ROW LEVEL SECURITY
✓
SQL query against pg_catalog to detect tables missing RLS
✓
CI job fails PR if any public table lacks RLS (non-zero exit code)
✓
Clear error message listing specific tables missing RLS in CI output
✓
Runs on every PR that touches supabase/ or docs/specs/infra/ paths

v1.5 Territory Layer Planned

biodiversity-trails Biodiversity Trails with GPS waypoints + diversity metrics
0/5

subtasks

○
trails table (start/end, route LineString, observer)
○
Trail recording UI (start/stop, waypoints)
○
Per-trail observation linking (FK trail_id)
○
Diversity metrics per trail (S, H', D)
○
Trail detail page + map render
pits-qr PITs + QR/NFC anchors
0/5

subtasks

○
pits table (slug, polygon, install_location)
○
/{lang}/pit/<slug>/ page (obs in polygon)
○
QR generator for printing
○
NFC tag write protocol docs
○
Stats: visitors + species recorded at PIT
spatial-analysis Spatial analysis: ANP/INEGI/INAH GeoJSON layers
0/5

subtasks

○
Static GeoJSON for ANP boundaries (CONANP)
○
INEGI municipal boundaries
○
INAH archaeological-zone polygons
○
Layer toggle in MapLibre
○
PostGIS ST_Within queries (obs within ANP X)
diversity-indices Diversity indices: S, H', D, Chao1, Pielou J
0/4

subtasks

○
diversity-stats Edge Function (bbox + window → JSON)
○
Math: Shannon-Wiener, Simpson, Chao1, Pielou
○
Rarefaction curves
○
Render in trail / event / region pages
trail-pdf-export Trail PDF export (field guide style)
0/10

subtasks

○
Server-side PDF rendering (Puppeteer or react-pdf)
○
Cover map, species list, diversity stats
○
Bilingual templates
○
PDF layout engine selection (Puppeteer headless vs react-pdf/pdfkit)
○
Cover page with trail map snapshot (MapLibre static image API)
○
Species checklist table with thumbnails, scientific + common names, confidence
○
Diversity statistics section (species richness, Shannon index, observation count)
○
Bilingual EN/ES PDF templates with locale-aware date formatting
○
Edge Function endpoint for on-demand PDF generation with auth check
○
Download button in ExportView component alongside CSV options

v2.0 Institutional Planned

camera-trap-advanced Camera trap: occupancy modelling, activity histograms
0/9

subtasks

○
R or Python statistical pipeline
○
Activity histograms by species + time-of-day
○
Multi-camera grid analysis
○
Occupancy modelling pipeline (R or Python) with detection/non-detection matrices
○
Activity histogram visualization by species and time-of-day (24h polar chart)
○
Multi-camera grid analysis aggregating detections across camera_stations
○
Trap-night effort calculation from camera_station_active_periods (M31 dependency)
○
RAI (Relative Abundance Index) per species per station with confidence intervals
○
Export occupancy + activity results as CSV and integration with DwC-A
gbif-publisher GBIF dataset publisher + DOI generation
0/10

subtasks

○
Continuous DwC-A export
○
Versioned dataset releases with DOIs (Zenodo/DataCite)
○
Citation generator
○
Automated DwC-A ZIP generation via export-dwca Edge Function on schedule
○
Dataset metadata EML file with contact, citation, and license fields
○
Versioned dataset releases with semantic versioning and changelog
○
DOI minting via Zenodo or DataCite API integration
○
Citation generator producing BibTeX + APA + RIS formats
○
GBIF IPT registration and endpoint configuration
○
Operator runbook for initial GBIF publisher account setup
regional-ml Regional ML training pipeline

07-licensing.md

0/4

subtasks

○
Continuous fine-tuning on validated observations
○
Federated learning across institutional partners
○
Model card publication per release
○
License gate: only CC BY / CC0 enter training
b2g-dashboard B2G SaaS dashboard for CONANP / state agencies
0/5

subtasks

○
Separate Astro+React app at b2g.rastrum.app
○
Per-agency report templates
○
Stripe subscriptions
○
Audit logs + SLA monitoring
!
Cornell BirdNET commercial license signed

Blocked by: Cornell licensing process

inat-bridge iNaturalist import/export bridge
0/4

subtasks

○
iNat OAuth flow
○
Import: pull user's iNat observations
○
Export: push verified data back with attribution
○
Rate-limit compatibility

v2.5 AI + AR Planned

scout-full Rastrum Scout — full conversational field AI
0/4

subtasks

○
Multi-turn conversation refinement
○
Region-aware retrieval (lat/lng filtering)
○
Citation rendering for sources
○
Builds on scout-v0
ar-overlay AR species overlay in camera viewfinder
0/9

subtasks

○
WebXR or Capacitor ARKit/ARCore
○
Real-time camera frame → ID → 3D label
○
Performance budget: 30fps mobile
○
WebXR Device API feature detection with Capacitor ARKit/ARCore fallback path
○
Real-time camera frame capture and resize to model input dimensions
○
On-frame species identification via lightweight ONNX model or server cascade
○
3D label overlay anchored to detected organism bounding box
○
Performance budget enforcement: 30fps on mid-range mobile (Snapdragon 7xx)
○
Tap-to-capture flow: freeze frame + save observation from AR view
voice-indigenous Indigenous language voice I/O
0/9

subtasks

○
Whisper-tiny via transformers.js for STT
!
Custom-trained TTS for Zapoteco/Mixteco

Blocked by: No off-the-shelf TTS exists

○
Conversation flow in Zapoteco for Sierra Norte pilot
○
Whisper-tiny ONNX via transformers.js for client-side STT
○
Language detection and routing for Zapoteco, Mixteco, Nahuatl, Maya, Tseltal
!
Custom-trained TTS model for Zapoteco (Sierra Norte pilot)

Blocked by: Requires community partnership and voice corpus

○
Conversational flow in Zapoteco for observation form field-by-field input
○
Fallback to Spanish STT when indigenous language model confidence is low
○
Community governance review and consent protocol before deployment
conabio-api Formal CONABIO/CONANP/INAH partnership APIs
0/9

subtasks

○
MOUs with each agency
○
Whitelisted API endpoints with audit logs
○
Bidirectional sync agreements
○
Draft MOUs with CONABIO, CONANP, and INAH legal teams
○
Whitelisted REST API endpoints with rst_* token authentication
○
Structured audit log for all agency API calls (request/response metadata)
○
Bidirectional sync protocol: push observations to SNIB, pull taxa updates
○
Rate limiting and quota management per agency partner
○
Data format adapters: Rastrum JSON to SNIB CSV and CONANP XML schemas

Edit docs/tasks.json to update. The page rebuilds on every push.

Report an issue

Show diagnostics

Environment

 

Console errors

 

Failed requests