Skip to content

feat: add githubProfileTags and onboardingProfileTags mutations#3775

Merged
capJavert merged 4 commits intomainfrom
feat/onboarding-profile-tags
Apr 7, 2026
Merged

feat: add githubProfileTags and onboardingProfileTags mutations#3775
capJavert merged 4 commits intomainfrom
feat/onboarding-profile-tags

Conversation

@capJavert
Copy link
Copy Markdown
Contributor

Add two GraphQL mutations for onboarding v2 tag extraction via bragi:

  • githubProfileTags: reads GitHub token from ba_account, sends to bragi with full allowed keyword vocabulary, saves results as ContentPreference
  • onboardingProfileTags: sends free-text prompt to bragi, cross-matches results against allowed keywords, saves as ContentPreference

Both mutations are idempotent — subsequent calls return saved tags without calling bragi. Rate limited to 3/hour. Tags persisted to both ContentPreferenceKeyword and FeedTag for backward compatibility.

Add two GraphQL mutations for onboarding v2 tag extraction via bragi:
- githubProfileTags: reads GitHub token from ba_account, sends to bragi
  with full allowed keyword vocabulary, saves results as ContentPreference
- onboardingProfileTags: sends free-text prompt to bragi, cross-matches
  results against allowed keywords, saves as ContentPreference

Both mutations are idempotent — subsequent calls return saved tags without
calling bragi. Rate limited to 3/hour. Tags persisted to both
ContentPreferenceKeyword and FeedTag for backward compatibility.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@pulumi
Copy link
Copy Markdown

pulumi bot commented Apr 4, 2026

🍹 The Update (preview) for dailydotdev/api/prod (at 6b3eb32) was successful.

✨ Neo Explanation

Routine deployment of a new application version (commit `78d7df71`) across all services and cronjobs, with accompanying database and ClickHouse migration jobs. ✅ Low Risk

This is a standard application deployment rolling out commit 78d7df71 to replace 1e518d2a. Every deployment and CronJob is updated solely by bumping the container image tag and version label/env var to the new git SHA. No configuration, resource sizing, or scheduling changes are included.

The two migration Jobs (Postgres via TypeORM and ClickHouse) follow the expected pattern: old jobs keyed to the previous SHA are deleted and new jobs keyed to the new SHA are created to run migrations against the new image. The migrations run with restartPolicy: Never, meaning a failure will not retry automatically — monitor job completion after deploy.

The GCP provider credential warning is cosmetic and does not affect the Kubernetes resources being deployed.

Resource Changes

    Name                                                    Type                           Operation
~   vpc-native-clean-gifted-plus-cron                       kubernetes:batch/v1:CronJob    update
~   vpc-native-personalized-digest-deployment               kubernetes:apps/v1:Deployment  update
-   vpc-native-api-clickhouse-migration-1e518d2a            kubernetes:batch/v1:Job        delete
~   vpc-native-update-source-public-threshold-cron          kubernetes:batch/v1:CronJob    update
~   vpc-native-update-views-cron                            kubernetes:batch/v1:CronJob    update
~   vpc-native-deployment                                   kubernetes:apps/v1:Deployment  update
~   vpc-native-expire-super-agent-trial-cron                kubernetes:batch/v1:CronJob    update
~   vpc-native-calculate-top-readers-cron                   kubernetes:batch/v1:CronJob    update
~   vpc-native-user-profile-updated-sync-cron               kubernetes:batch/v1:CronJob    update
~   vpc-native-user-posts-analytics-refresh-cron            kubernetes:batch/v1:CronJob    update
~   vpc-native-clean-zombie-user-companies-cron             kubernetes:batch/v1:CronJob    update
~   vpc-native-materialize-yearly-best-post-archives-cron   kubernetes:batch/v1:CronJob    update
~   vpc-native-rotate-weekly-quests-cron                    kubernetes:batch/v1:CronJob    update
+   vpc-native-api-clickhouse-migration-78d7df71            kubernetes:batch/v1:Job        create
~   vpc-native-personalized-digest-cron                     kubernetes:batch/v1:CronJob    update
~   vpc-native-sync-subscription-with-cio-cron              kubernetes:batch/v1:CronJob    update
~   vpc-native-validate-active-users-cron                   kubernetes:batch/v1:CronJob    update
~   vpc-native-channel-digests-cron                         kubernetes:batch/v1:CronJob    update
~   vpc-native-generic-referral-reminder-cron               kubernetes:batch/v1:CronJob    update
~   vpc-native-daily-digest-cron                            kubernetes:batch/v1:CronJob    update
-   vpc-native-api-db-migration-1e518d2a                    kubernetes:batch/v1:Job        delete
~   vpc-native-update-achievement-rarity-cron               kubernetes:batch/v1:CronJob    update
~   vpc-native-post-analytics-clickhouse-cron               kubernetes:batch/v1:CronJob    update
~   vpc-native-hourly-notification-cron                     kubernetes:batch/v1:CronJob    update
+   vpc-native-api-db-migration-78d7df71                    kubernetes:batch/v1:Job        create
~   vpc-native-update-tags-str-cron                         kubernetes:batch/v1:CronJob    update
~   vpc-native-squad-posts-analytics-refresh-cron           kubernetes:batch/v1:CronJob    update
~   vpc-native-temporal-deployment                          kubernetes:apps/v1:Deployment  update
~   vpc-native-user-profile-analytics-clickhouse-cron       kubernetes:batch/v1:CronJob    update
~   vpc-native-bg-deployment                                kubernetes:apps/v1:Deployment  update
~   vpc-native-post-analytics-history-day-clickhouse-cron   kubernetes:batch/v1:CronJob    update
~   vpc-native-update-trending-cron                         kubernetes:batch/v1:CronJob    update
~   vpc-native-clean-zombie-opportunities-cron              kubernetes:batch/v1:CronJob    update
~   vpc-native-update-highlighted-views-cron                kubernetes:batch/v1:CronJob    update
~   vpc-native-clean-zombie-users-cron                      kubernetes:batch/v1:CronJob    update
~   vpc-native-private-deployment                           kubernetes:apps/v1:Deployment  update
~   vpc-native-rotate-daily-quests-cron                     kubernetes:batch/v1:CronJob    update
~   vpc-native-channel-highlights-cron                      kubernetes:batch/v1:CronJob    update
~   vpc-native-generate-search-invites-cron                 kubernetes:batch/v1:CronJob    update
~   vpc-native-update-tag-materialized-views-cron           kubernetes:batch/v1:CronJob    update
~   vpc-native-materialize-monthly-best-post-archives-cron  kubernetes:batch/v1:CronJob    update
... and 11 other changes

@capJavert
Copy link
Copy Markdown
Contributor Author

@claude review

@claude
Copy link
Copy Markdown

claude bot commented Apr 4, 2026

Claude finished @capJavert's task in 2m 14s —— View job


Code Review

  • Gather context
  • Read changed files
  • Analyze code
  • Post review feedback

Overall the feature is well-structured with good test coverage and correct use of proto message classes. There are a few issues to address.

Critical

Raw SQL query violates codebase rules (src/schema/users.ts:4041–4048)

const result = await ctx.con.query(
  `SELECT "accessToken" FROM ba_account WHERE "userId" = $1 AND "providerId" = 'github' LIMIT 1`,
  [ctx.userId],
);

CLAUDE.md explicitly says: "Never use raw SQL queries (con.query()) — always use TypeORM repository methods or query builder." This should use a query builder even if ba_account doesn't have a TypeORM entity. Fix this →


Important

Idempotency check is too broad — conflates mutations and manual follows (src/schema/users.ts:4025–4036, 4113–4124)

Both mutations check for any ContentPreferenceKeyword with status: Follow for the user. This creates two problems:

  1. A user who manually followed keywords before running onboarding will always get the early-return — the mutation never calls bragi, and neither mutation is actually "run".
  2. Calling githubProfileTags first and then onboardingProfileTags returns the GitHub-derived tags from the onboarding mutation (wrong behavior — they share the same bucket).

There's no marker to distinguish "tags bootstrapped by this mutation" from "tags the user manually followed". Consider tracking this via a user flag (e.g., onboardingTagsDone, githubTagsDone) or a dedicated status/source column. Fix this →


Minor

Zod validation runs after idempotency check (src/schema/users.ts:4126)

// existing check first...
if (existing.length) {
  return { includeTags: existing.map((pref) => pref.keywordId) };
}
// THEN validation
const parsed = onboardingProfileTagsInputSchema.parse(args);

If a user has cached tags, an invalid prompt (e.g., empty string or 2000+ chars) would bypass validation silently. Validate first for consistent behavior. Fix this →


Keyword vocabulary fetch uses primary DB (src/schema/users.ts:4051–4060, 4146–4155)

Both ctx.getRepository(Keyword).createQueryBuilder()... calls hit the primary. Since eventual consistency is fine here, prefer queryReadReplica. Fix this →


take limit missing in onboardingProfileTags existing check (src/schema/users.ts:4113–4122)

githubProfileTags has take: 500 ("arbitrary limit for protection") but onboardingProfileTags omits it. Apply the same limit for consistency.


Code duplication in transaction block

The ContentPreferenceKeyword + FeedTag insert transaction is identical in both mutations. Extract it into a shared inline helper to reduce duplication and future drift. Fix this →


Tests

Missing test: onboardingProfileTags NotFound error — there is a test for githubProfileTags propagating a bragi NotFound, but no equivalent for onboardingProfileTags. Add one for symmetry.

Missing test: prompt max length — the test for empty prompt is good, but there is no test for a prompt exceeding the 2000-char limit.


Nits

  • The ctx.getRepository calls inside the resolvers go through the primary connection — use ctx.con.getRepository consistently (the ctx.getRepository shorthand may hide which connection is being used).
  • In __tests__/helpers.ts the mock gitHubProfileTags returns webdev, rust, golang but the fixture keywords used in tests may or may not include all of these — worth double-checking keyword fixture contents to avoid brittle tag matching.

capJavert and others added 3 commits April 4, 2026 17:44
- Move Zod validation before idempotency check in onboardingProfileTags
- Use queryReadReplica for keyword vocabulary fetches in both resolvers
- Add take: 500 limit to onboardingProfileTags existing check
- Remove unnecessary orderBy on vocabulary query
- Add GQL docs describing one-shot + idempotent return behavior
- Add test: onboardingProfileTags bragi NotFound error
- Add test: prompt exceeding 2000-char max length

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Bump @dailydotdev/schema to 0.3.3 (adds tagVocabulary to OnboardingProfileTagsRequest)
- Pass allowed keyword vocabulary to bragi onboardingProfileTags call
- Remove post-hoc cross-match — bragi now constrains output via vocabulary
- Update test: verify vocabulary is passed to bragi

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@capJavert capJavert merged commit c260e4d into main Apr 7, 2026
10 checks passed
@capJavert capJavert deleted the feat/onboarding-profile-tags branch April 7, 2026 18:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant