Skip to content

feat(auth): provision $member + identities on signup and install#359

Merged
buremba merged 1 commit into
mainfrom
feat/install-identity-provisioning
Apr 26, 2026
Merged

feat(auth): provision $member + identities on signup and install#359
buremba merged 1 commit into
mainfrom
feat/install-identity-provisioning

Conversation

@buremba
Copy link
Copy Markdown
Member

@buremba buremba commented Apr 25, 2026

Summary

Closes the identity-graph gap that was blocking gateway routing — the gateway can now resolve "which user owns this WhatsApp JID?" via a single `entity_identities` lookup once these rows are populated.

New file: `packages/owletto-backend/src/auth/subject-identities.ts`

  • `provisionMemberAndCoreIdentities(orgId, subject)` — creates the user's `$member` entity in the org and writes `entity_identities` rows for `auth_user_id` and `email`. Reuses the existing `ensureMemberEntity` helper.
  • `linkWhatsAppToMember({orgId, email, rawPhone})` — normalizes the user's phone to E.164, derives the WhatsApp JID (`@s.whatsapp.net`), and writes `phone` + `wa_jid` identity rows. Idempotent.
  • `normalizePhoneE164` + `phoneToWhatsAppJid` pure helpers (covered by 7 unit tests — UK +44 national, leading 0, full E.164, the `+44 (0) ...` trunk-prefix form, non-UK numbers).

Modified: `personal-org-provisioning.ts` (signup hook)

  • After creating the org, call `provisionMemberAndCoreIdentities` so every signed-up user lands with a `$member` entity in their own org and discoverable identities. Failure is logged, doesn't roll back the org.

Modified: `install-routes.ts` (POST /api/install)

  • Accepts an optional `whatsapp_phone` in the body. On valid phone: writes `wa_jid` + `phone` identities to the user's `$member`. Reports `whatsappError="invalid_phone"` or `"no_member"` without failing the install (agent is in; user can re-link later).

Integration tests

  • Install with `whatsapp_phone` writes both identity rows correctly.
  • Install with unparseable phone surfaces `whatsappError="invalid_phone"` and still completes.

Stacked on

Targets `feat/install-endpoint` (#357). Pulls in `feat/personal-org-on-signup` (#352) via merge so both `personal-org-provisioning.ts` and `install-routes.ts` are in scope.

⚠️ Both `feat/personal-org-on-signup` and `feat/schema-mirror-install-flow` have seen upstream refactors after this PR was opened (advisory locks for concurrency; classifier/watcher mirroring). When this branch rebases at merge time it'll need to integrate with the new tips — my additions are confined to a new helper file plus one new call site, so the conflicts should be small and mechanical.

Test plan

  • 7 unit tests for phone normalization + JID derivation.
  • 5 integration tests in `install-routes.test.ts` (including 2 new ones for the WhatsApp identity path).
  • Pre-commit Biome + tsc pass.
  • Manual: `POST /api/install -d '{"templateAgentId":"...","whatsapp_phone":"+447123456789"}'` while signed in; confirm `entity_identities` rows appear with namespaces `phone` and `wa_jid`.
  • Manual: re-run the same POST; confirm rows aren't duplicated (idempotent).

Why this matters

This is the missing link. Before: WhatsApp routing (#7) had nothing to query because no code wrote the JID anywhere. After: every install writes `wa_jid` to the right `$member` in the right org, and the gateway routing PR can be a clean `SELECT organization_id FROM entity_identities WHERE namespace='wa_jid' AND identifier=$senderJid`.

@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Closes the identity-graph gap that was blocking gateway routing — the
gateway can now resolve "which user owns this WhatsApp JID?" via a
single entity_identities lookup once these rows are populated.

New:

- packages/owletto-backend/src/auth/subject-identities.ts
  - provisionMemberAndCoreIdentities(orgId, subject) — creates the
    user's $member entity in the given org and writes
    entity_identities rows for auth_user_id and email.
  - linkWhatsAppToMember({orgId, email, rawPhone}) — normalizes the
    user's phone to E.164, derives the WhatsApp JID, and writes
    phone + wa_jid identity rows. Idempotent.
  - normalizePhoneE164 + phoneToWhatsAppJid pure helpers (unit tested).

Modified:

- packages/owletto-backend/src/auth/personal-org-provisioning.ts
  - After creating a user's personal org, call
    provisionMemberAndCoreIdentities so every signed-up user lands with
    a $member entity in their own org and discoverable identities.
    Failure is logged but doesn't roll back the org creation —
    identities are recoverable via a re-link.

- packages/owletto-backend/src/agents/install-routes.ts
  - POST /api/install accepts an optional whatsapp_phone in the body.
    On a valid phone, writes the wa_jid + phone identities to the
    user's $member. Reports whatsappError="invalid_phone" or
    "no_member" without failing the install (agent is already in;
    user can re-link later).

Tests:

- 7 unit tests for normalizePhoneE164 + phoneToWhatsAppJid (UK +44
  national, leading 0, E.164, +44 (0) trunk-prefix form, non-UK).
- Integration tests: install with whatsapp_phone writes both rows;
  install with unparseable phone returns whatsappError="invalid_phone"
  but still completes the install.

Note on stacking: this PR is based on a snapshot of feat/personal-org-
on-signup (#352) and feat/install-endpoint (#357). Both have since seen
upstream refactors (advisory locks, classifier/watcher mirroring). At
merge time, this branch will need to rebase onto their new tips —
my additions are confined to a new helper file plus one new call
site, so the conflicts should be small.
@buremba buremba force-pushed the feat/install-identity-provisioning branch from a190663 to e5b755e Compare April 26, 2026 16:39
@buremba buremba merged commit db1e199 into main Apr 26, 2026
10 checks passed
@buremba buremba deleted the feat/install-identity-provisioning branch April 26, 2026 16:39
@github-actions github-actions Bot added the triage:needs-human Triage agent escalated for human review label Apr 26, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Triage decision: needs-human

Reasons:

  • PR size exceeds auto-merge threshold (360 lines, 6 files; limits 300 lines, 10 files)

Next: Manual merge required after final review — all CI checks are passing, but the change volume requires human eyes per auto-merge policy

buremba added a commit that referenced this pull request Apr 26, 2026
, #359 install-half) (#372)

The install flow as built — schema-mirror clones a template's entity types /
relationship types / classifiers / watchers into each user's personal org —
was the wrong abstraction. Cross-org vocabulary (an entity in tenant org A
referencing a type defined in a public-catalog org B by FK) is the planned
direction; the mirror pipeline duplicated rows per user and added re-sync
complexity for no working installs (verified 0 rows used the mirror columns
in prod).

Removed:
- packages/owletto-backend/src/agents/install.ts (installAgentFromTemplate, resyncInstalledAgent)
- packages/owletto-backend/src/agents/install-routes.ts (POST /api/install)
- packages/owletto-backend/src/agents/install-manifest-routes.ts (GET /api/install/manifest/:slug)
- All associated integration tests
- subject-identities WhatsApp helpers (normalizePhoneE164, phoneToWhatsAppJid, linkWhatsAppToMember) + their unit tests
- db/migrations/20260425120000_add_template_mirror_tracking.sql (rolled back on prod first)
- Route registrations from src/index.ts

Kept:
- subject-identities.ts provisionMemberAndCoreIdentities — used by the signup
  hook in personal-org-provisioning.ts, orthogonal to install flow.
- #352 personal-org-on-signup, #350/#354/#355/#356 personal-finance content —
  no install dependencies.

DB state: prod migrated down via dbmate (mirror columns dropped), then
20260426120000_entities_entity_type_fk re-applied. 0 user-visible data lost.
@buremba buremba restored the feat/install-identity-provisioning branch May 12, 2026 00:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

triage:needs-human Triage agent escalated for human review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant