Add base profile schema#68
Conversation
There was a problem hiding this comment.
Pull request overview
Adds the first Convex-backed profiles schema for VRDex’s shared person/community profile model and documents the schema boundaries for follow-on work.
Changes:
- Defines the
profilestable with profile type, identity, presentation, claim/publication state, provenance, timestamps, and initial indexes. - Adds profile schema documentation and links it from backend/repo docs.
- Updates planning/bootstrap docs to reflect that issue
#9now establishes the base profile table.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
convex/schema.ts |
Adds the durable profiles Convex table and indexes. |
docs/backend/profile-schema.md |
Documents the schema contract, state semantics, indexes, and deferred boundaries. |
docs/backend/convex-bootstrap.md |
Updates backend bootstrap documentation to include the profile schema slice. |
convex/README.md |
Points backend contributors to the new profile schema contract. |
README.md |
Updates repo overview and local bootstrap notes for the new profile table. |
docs/planning/architecture.md |
Records implementation status for the profile domain model. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Greptile SummaryThis PR introduces the first product-domain Convex table —
Confidence Score: 4/5Safe to merge — this is a schema-only change with no mutations yet, so no data can be written incorrectly today. The schema design is clean and deliberately narrow. The three concerns are all forward-looking: state-timestamp invariants that future mutation authors must remember to enforce, an updatedAt patch contract that Convex won't enforce on updates, and the absence of a claimState-leading index that moderation queries will likely need once writes land. None of these affect schema validity or any existing code path today. convex/schema.ts is the only non-documentation file changed and is worth a second look specifically around the optional timestamp fields and index coverage before the first claim or publish mutations are written. Important Files Changed
Entity Relationship Diagram%%{init: {'theme': 'neutral'}}%%
erDiagram
profiles {
id _id PK "Convex auto-generated"
number _creationTime "Convex auto-generated"
string profileType "person | community"
string displayName
string sortName
array aliases "string[]"
string headline "optional"
string bio "optional"
string region "optional"
string timezone "optional"
string claimState "unclaimed | claimed_unverified | claimed_verified"
string publicationState "draft_private | published"
string creationSource "self | community | concierge | import | moderator"
number claimedAt "optional"
number publishedAt "optional"
number updatedAt "app-maintained"
}
Prompt To Fix All With AIFix the following 3 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 3
convex/schema.ts:38-39
**State-timestamp invariants unenforced at schema level**
`claimedAt` and `publishedAt` are optional in the schema, but the doc contract treats them as conditionally required — `claimedAt` should be present when `claimState != "unclaimed"` and `publishedAt` should be set when `publicationState == "published"`. Convex doesn't support conditional field validation, so any mutation that transitions these state fields without setting the corresponding timestamp will silently create inconsistent records. The schema has no comment or co-located note flagging this mutation contract, making it easy for future developers writing `#13` (claim transitions) or publish flows to miss it.
### Issue 2 of 3
convex/schema.ts:42-45
**No single-column index on `claimState`**
All four indexes use `claimState` as a trailing column, not a leading one. A moderation query like "all unclaimed profiles regardless of publication state or creation source" can't use any of these indexes efficiently — it would require ranging across every `publicationState` value in `by_publicationState_claimState` or every `creationSource` value in `by_creationSource_claimState`, effectively scanning most of the table. If the moderation view described in the doc is a first-class query pattern, a dedicated `by_claimState` index (or at minimum `by_claimState_profileType`) is worth adding now before writes land.
### Issue 3 of 3
convex/schema.ts:40
**`updatedAt` is a silent mutation contract with no enforcement path**
`updatedAt: v.number()` is required by schema, so Convex will reject any insert that omits it — that part is safe. But on updates, Convex doesn't re-validate unchanged fields, so a mutation that patches `displayName` or `claimState` without including `updatedAt` in its patch will leave the timestamp stale without any runtime error. This is a common footgun in Convex. Consider noting in a schema comment that every mutation must include `updatedAt: Date.now()` in its patch, so `#13` and later mutation authors know the contract upfront.
Reviews (1): Last reviewed commit: "feat: add base profile schema" | Re-trigger Greptile |
Summary
profilestable for people and communitiesValidation
pnpm typecheck:backendpnpm verify:webpnpm check:backend:generatedgit diff --checkCloses #9