Skip to content

Add profile identity foundation#69

Merged
BASIC-BIT merged 2 commits into
mainfrom
issue-10-profile-identity-foundation
May 18, 2026
Merged

Add profile identity foundation#69
BASIC-BIT merged 2 commits into
mainfrom
issue-10-profile-identity-foundation

Conversation

@BASIC-BIT
Copy link
Copy Markdown
Owner

Summary

  • add global profile slugs with strict validation/generation helpers and a by_slug lookup index
  • convert profiles to a discriminated person/community schema with flexible type-aware fields
  • add permission, claim-state, and trust-label helper contracts without public write mutations
  • document slug, type-aware, permission, and claim-state behavior for follow-on work

Validation

  • pnpm verify

Closes #10
Closes #11
Closes #12
Closes #13

Copilot AI review requested due to automatic review settings May 17, 2026 22:07
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ace6371659

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread convex/schema.ts
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 17, 2026

Greptile Summary

This PR lands the profile identity foundation: globally unique slugs with strict validation and generation helpers, a discriminated person/community schema union with type-specific nested fields, and pure helper contracts for permissions, claim-state transitions, and trust labels — all without adding public write mutations.

  • convex/schema.ts converts profiles to a v.union discriminated by profileType, adding slug, tags, nested person/community objects, and a by_slug index.
  • convex/_profileSlugs.ts adds normalization, validation, DB-availability checks, and a retry loop for auto-generating unique slugs.
  • convex/_profilePermissions.ts / _profileStates.ts introduce pure read/edit permission helpers and claim-state / trust-label derivation helpers, deliberately deferring mutations and auth wiring.

Confidence Score: 4/5

Safe to merge as a foundations-only slice; no mutations are introduced and the schema change is additive.

The schema conversion and slug helpers are well-constructed. The main concern is in _profilePermissions.ts: canEditProfileField does not check publicationState, so future mutation authors must remember to compose it with canReadProfile — the API doesn’t enforce this. Combined with the absence of unit tests for these pure permission and state-transition functions, the permission contract layer needs a closer look before mutations build on top of it.

convex/_profilePermissions.ts — the permission API surface should be hardened before mutations consume it.

Important Files Changed

Filename Overview
convex/_profilePermissions.ts New file. Pure permission helpers for read/edit access. Two design gaps: canEditProfileField doesn't check publicationState, so callers must compose it with canReadProfile manually; no unit tests for these pure functions despite the project's stated preference for test coverage on permission logic.
convex/_profileSlugs.ts New file. Adds slug normalization, validation, availability check, and DB-lookup helpers. Overall solid logic; minor fragility in createProfileSlugBase ordering when padding a short slug that could later match a newly reserved word.
convex/_profileStates.ts New file. Adds trust-label derivation and claim-state transition helpers. The from === to no-op allowance in canTransitionProfileClaimState is a subtle trap for future mutation authors who must additionally check state change before writing claimedAt.
convex/schema.ts Schema migrated to a discriminated union for person / community profiles; adds slug, tags, type-specific nested objects, and the by_slug index. Changes look correct; slug uniqueness enforcement is explicitly delegated to future mutations.
convex/_generated/api.d.ts Generated type declarations updated to reference the three new helper modules. No hand-edited logic; safe.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Input string] --> B[normalizeProfileSlugInput]
    B --> C{createProfileSlugBase}
    C -- too short --> D[append -profile]
    C -- reserved --> E[append -profile]
    C -- too long --> F[truncate to 64 chars]
    D & E & F --> G[validateProfileSlug]
    G -- invalid --> H[fallback: profile-page]
    G -- valid --> I[base slug]
    I --> J[findAvailableProfileSlug loop]
    J --> K[createProfileSlugCandidate attempt N]
    K --> L[checkProfileSlugAvailability]
    L --> M{available?}
    M -- yes --> N[return slug]
    M -- no, attempt lt maxAttempts --> K
    M -- exhausted --> O[throw Error]
    subgraph Permission Gate
        P[subject + profile] --> Q[canReadProfile]
        P --> R[canEditProfileField]
        R --> S{isFieldCompatibleWithProfileType}
        S -- incompatible --> T[false]
        S -- compatible --> U{subject role?}
        U -- moderator --> V[true]
        U -- claimed_owner --> W{claimState != unclaimed}
        U -- community_submitter --> X{unclaimed AND allowed field?}
        U -- public --> Y[false]
    end
    subgraph Claim State Machine
        Z[unclaimed] --> AA[claimed_unverified]
        Z --> AB[claimed_verified]
        AA --> AB
    end
Loading
Prompt To Fix All With AI
Fix the following 4 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 4
convex/_profilePermissions.ts:55-80
**`canEditProfileField` doesn't check `publicationState`**

`community_submitter` can pass this check for a `draft_private` unclaimed profile — even though `canReadProfile` would deny them access. The two helpers are designed to be used together, but nothing in the API enforces it. A future mutation that calls only `canEditProfileField` without first gating on `canReadProfile` would let a community submitter edit a profile they cannot read. Adding either a guard inside this function or a combined `canAccessAndEdit` helper would make the contract harder to misuse.

### Issue 2 of 4
convex/_profileStates.ts:37-42
**No-op transitions silently succeed in `canTransitionProfileClaimState`**

`from === to` is treated as valid, so `requireProfileClaimStateTransition("unclaimed", "unclaimed")` never throws. Per the mutation contract doc, future mutations must set `claimedAt` when `claimState` leaves `"unclaimed"`. Any mutation that uses `requireProfileClaimStateTransition` to gate that assignment also needs an explicit `from !== to` guard; otherwise a no-op call could skip — or incorrectly trigger — the `claimedAt` write. This is a subtle trap for the first mutation author.

### Issue 3 of 4
convex/_profilePermissions.ts:1-80
**No unit tests for pure permission and state-transition helpers**

`_profilePermissions.ts` and `_profileStates.ts` are entirely pure functions with no I/O or Convex runtime dependencies — ideal candidates for straightforward unit tests. The project's testing rule calls for stronger automated coverage when permission or verification logic changes. The edge cases (e.g., `community_submitter` on an incompatible profile type, the claim-state transition table, the reserved field gate) are the kind of things that regress silently without a test suite.

### Issue 4 of 4
convex/_profileSlugs.ts:102-119
**`createProfileSlugBase` doesn't re-check reservation after the too-short padding**

The length-pad (`< PROFILE_SLUG_MIN_LENGTH → append "-profile"`) runs before the reservation check. If a two-character slug like `"ab"` is padded to `"ab-profile"` and that result happened to match a reserved slug added to the list later, the subsequent reserved guard would append `-profile` again, and the trailing truncation and `validateProfileSlug` fallback would catch it. More fragile, if after both appends the result is still reserved, `validateProfileSlug` falls back silently to `"profile-page"` without any signal to the caller. A second reserved re-check before `validateProfileSlug` would make this function's logic easier to verify and extend.

Reviews (1): Last reviewed commit: "feat: add profile identity foundation" | Re-trigger Greptile

Comment thread convex/_profilePermissions.ts
Comment thread convex/_profileStates.ts
Comment thread convex/_profilePermissions.ts
Comment thread convex/_profileSlugs.ts
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 16 changed files in this pull request and generated 1 comment.

Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

Comment on lines +64 to +67
if (!canReadProfile(subject, profile)) {
return false;
}

@BASIC-BIT BASIC-BIT merged commit 3d7dc07 into main May 18, 2026
12 of 13 checks passed
@BASIC-BIT BASIC-BIT deleted the issue-10-profile-identity-foundation branch May 18, 2026 05:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants