-
Notifications
You must be signed in to change notification settings - Fork 24
Description
Context
Beads currently have two overlapping mechanisms for storing type-specific data:
-
beads.metadata— a freeform JSON text column (text default '{}') on thebeadstable. Any bead type can stash arbitrary key/value pairs here (e.g.source_bead_id,pr_url,source_agent_id). -
Satellite metadata tables — typed tables like
review_metadata,agent_metadata,escalation_metadata,convoy_metadatathat are joined viabead_idforeign key. Each has its own schema with named, typed columns.
Both patterns are actively used. For example, pr_url exists in both review_metadata.pr_url (typed column) AND beads.metadata JSON (written by setReviewPrUrl). The source_bead_id field only exists in beads.metadata but is read by completeReviewWithResult via untyped JSON access.
Problems
- Dual writes: Some fields (e.g.
pr_url) must be written to both locations to be visible from both query paths (bead list endpoint readsbeads.metadata, review queue reads the JOIN withreview_metadata). - No schema enforcement on metadata JSON: The
metadatacolumn isRecord<string, unknown>— no compile-time guarantees on what's stored or retrieved. - Inconsistent access patterns: Some code reads from the satellite table, other code reads from the JSON column, leading to subtle bugs when one is updated but not the other.
Proposal
Decide on one canonical pattern and migrate to it:
- Option A: Lean into satellite tables — remove
beads.metadataor restrict it to truly ad-hoc data. Move all typed fields to their satellite tables. - Option B: Lean into the JSON column — use Zod schemas per bead type to validate the JSON, remove satellite tables, use SQLite generated columns or views for indexed access.
- Option C: Keep both but establish clear rules — satellite tables for indexed/queryable fields, metadata JSON for display-only context. Document the boundary.
References
cloudflare-gastown/src/db/tables/beads.table.ts— metadata column definitioncloudflare-gastown/src/db/tables/review-metadata.table.ts— satellite table examplecloudflare-gastown/src/dos/town/review-queue.ts—setReviewPrUrlwrites to bothcloudflare-gastown/src/dos/town/beads.ts—logBeadEvent,listBeadEvents
Part of #204 (Phase 4: Hardening)