feat(field-guide): album state module + recordOnce primitive (FG-2) #94
No reviewers
Labels
No labels
area: arcade
area: art-studio
area: calendar
area: chapel
area: content
area: design
area: field-guide
area: infra
area: library
area: os
area: portal
area: sync
area: typing
blocked
breaking
bug
content: approved
content: changes-requested
content: drafting
content: needs-doctrinal-review
content: needs-review
dependencies
documentation
duplicate
enhancement
epic
github_actions
good first issue
help wanted
invalid
javascript
needs-triage
priority: high
priority: low
priority: medium
question
type: enhancement
type: feature
type: fix
type: perf
type: security
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Bosco/bosco!94
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/field-guide-album-state"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
FG-2 of v0.4.0 — the album's storage layer. Part of #92. Independent of FG-1. Architecture:
docs/architecture/field-guide.md(§ The album).What's here
BoscoStore.recordOnce(collection, id, data)— a generic insert-if-absent primitive. A live record with that id returns unchanged: noput(), no freshupdatedAt. So calling it repeatedly (the album's "record on read") is idempotent — it never re-marks the record pending and never re-bumps its timestamp. A tombstoned id (deleted: true, e.g. synced in from another device) counts as absent, so a later read can revive it. Reusable later by mastery / high-score / saved-art collections.src/lib/state/album.ts— a thin wrapper:recordCard/hasCard/listCards. A card stores only{ v: 1 }keyed by the creature slug; title/art/summary are joined from live gated frontmatter at view time, so a later-unapproved creature resolves to an inert frame and can never leak a title. Neutral records-not-rewards vocabulary (noearn/claim), and every function is profileless-safe — a reader with no profile browses without an error.Pure state: no wiring, no UI. Record-on-read wiring is FG-4; the album view is FG-5.
The new
albumcollection is picked up for free bypendingChanges/sync(union-merge) /export/import/deleteProfile— all enumerate collections dynamically. No migration, noSCHEMA_VERSIONbump.Verification (local gauntlet, all green)
check(0 errors) ·lint·guard:colour·build·guard:external·guard:content·guard:provenance·guard:offline· 159 unit tests (+9). New tests via the deterministicmakeStore()fakes: double-record freezesupdatedAt+ stays out ofpendingChangesafter a sync; tombstoned-id revives; profileless returns[]/false/no-op; profile-scoped albums; export/import round-trip.deleteProfileclearing the album is already covered by the existing store test (it uses thealbumcollection). No rendered surface — e2e n/a.