Add candidate blurb library and drafting workflows#391
Conversation
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_009a78f2-3c42-40ad-a6ab-9e7f446a0a5f) |
📝 WalkthroughWalkthroughChangesCandidate blurbs now have versioned shared storage, bounded AI drafting, dashboard APIs and panels, Discord save/draft workflows, permission checks, gig enrichment, and CRM profile-summary synchronization. Candidate blurb management
Estimated code review effort: 5 (Critical) | ~120 minutes Sequence Diagram(s)sequenceDiagram
participant SteeringMember
participant Dashboard
participant BackendAPI
participant SharedBlurbRuntime
participant CandidateBlurbStore
SteeringMember->>Dashboard: Open candidate or gig details
Dashboard->>BackendAPI: Load candidate blurbs
BackendAPI->>SharedBlurbRuntime: List or enrich blurbs
SharedBlurbRuntime->>CandidateBlurbStore: Query current and historical versions
CandidateBlurbStore-->>BackendAPI: Blurb records
BackendAPI-->>Dashboard: Enriched gig or person data
SteeringMember->>Dashboard: Draft or save blurb
Dashboard->>BackendAPI: Submit draft or save request
BackendAPI->>SharedBlurbRuntime: Draft or persist blurb
SharedBlurbRuntime-->>BackendAPI: Draft or versioned record
BackendAPI-->>Dashboard: Result and metadata
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 9
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/admin_dashboard/src/main.tsx`:
- Around line 216-233: Update CandidateBlurb to include lineage_id, version, and
supersedes_id, then revise the current/history grouping and rendering logic to
partition blurbs by lineage_id. Treat each independent lineage’s current blurb
as current, and only classify superseded versions within that same lineage as
history; update the related logic around the alternate occurrence as well.
- Around line 6365-6373: Update the candidate blurb panel rendering around
candidate-selection state and the existing unattachedBlurbGroups flow so it also
renders an empty creation panel when a candidate is selected, even when no
unattached groups exist. Preserve listing and editing of existing groups, and
use the existing candidate/API callbacks to draft the first gig-specific blurb
without requiring an application.
In `@apps/api/src/five08/backend/api.py`:
- Around line 4264-4276: Update the application enrichment loop around
list_candidate_blurbs to eliminate sequential per-application database calls:
fetch all valid application and general blurb histories through a bounded batch
query, then group the results by application ID in memory and assign each
application’s blurbs. Preserve empty blurbs for invalid IDs and the existing
current_only=False/include_general=True behavior.
- Around line 4367-4377: Update the non-write listing branch around
list_candidate_blurbs to catch CandidateBlurbNotFoundError and validation
errors, mapping them to the same stable HTTP responses used by the POST path
instead of allowing them to escape as 500s. Preserve the existing successful
JSONResponse behavior and reuse the POST path’s established exception handling
and response construction.
In `@apps/discord_bot/src/five08/discord_bot/cogs/jobs.py`:
- Around line 517-518: Update the Save and Cancel callbacks to use the shared
_save_lock for their entire terminal transition, and add cancellation tracking
alongside _saved. Under the lock, prevent either callback from reopening or
persisting an already-cancelled/saved draft, mark the appropriate terminal state
atomically, and preserve the existing “not saved” behavior only when no terminal
transition has occurred.
- Around line 921-940: Update the early role authorization in the relevant
command authorization method before the configured-guild lookup so it cannot
approve roles from an arbitrary guild; only evaluate “Steering Committee” roles
after resolving and validating the configured Discord server and its member.
Preserve the existing member-fetch fallback and denial behavior, and add a
regression test proving an identically named role from another guild is
rejected.
In
`@apps/worker/src/five08/worker/migrations/versions/20260715_0100_create_candidate_blurbs.py`:
- Around line 156-185: Replace the partial current-only indexes in the
migration’s upgrade path with non-partial indexes on each target column plus
created_at, covering person_id, crm_contact_id, discord_user_id, engagement_id,
and application_id. Update the downgrade path to drop these corresponding
historical-lookup indexes, preserving consistent index names and migration
reversibility.
- Around line 133-142: Update the foreign key constraints in the candidate blurb
migration so deleting an engagement or application cannot cascade-delete blurb
history. Replace CASCADE behavior on both engagement_id and the composite
application_id/engagement_id constraint with RESTRICT or NO ACTION, preserving
blurb rows and their provenance.
In `@packages/shared/src/five08/candidate_blurbs.py`:
- Around line 857-858: Update the target-only branch in the blurb query, where
_candidate_match_sql(target, params) is appended, to also constrain results to
general blurbs by filtering scope appropriately. Preserve scoped matching for
branches that provide an explicit scope, while ensuring person-only requests
return only general-library records.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: cdaeab03-8c3c-4410-a8eb-47793f32c782
📒 Files selected for processing (15)
apps/admin_dashboard/src/main.tsxapps/api/src/five08/backend/api.pyapps/api/src/five08/backend/routes.pyapps/api/src/five08/backend/schemas.pyapps/discord_bot/src/five08/discord_bot/cogs/jobs.pyapps/worker/src/five08/worker/crm/people_sync.pyapps/worker/src/five08/worker/migrations/versions/20260715_0100_create_candidate_blurbs.pydocs/discord-gig-dashboard.mdpackages/shared/src/five08/audit.pypackages/shared/src/five08/candidate_blurbs.pytests/unit/test_backend_api.pytests/unit/test_candidate_blurbs.pytests/unit/test_jobs.pytests/unit/test_shared_audit.pytests/unit/test_worker_people_sync.py
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e50a6c2144
ℹ️ 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".
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_54c41a3f-9ad3-4e39-bd61-8b42a117950d) |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: adac78a44b
ℹ️ 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".
| if application is None: | ||
| application = applications_by_person.get(str(blurb.get("person_id") or "")) |
There was a problem hiding this comment.
Keep unattached gig blurbs off application rows
When a gig-scoped blurb has application_id null but shares a CRM/Discord/person identity with an application (for example, it was captured from Discord before the candidate was added), these identity fallbacks attach it to application.blurbs instead of leaving it in candidate_blurbs. The application panel then edits via /applications/{application_id}/blurbs, but save_candidate_blurb rejects replacing the original because its stored application_id is still None, so the blurb is shown in a panel that cannot update it. Limit the identity fallback to general blurbs or keep unattached gig blurbs at the gig level.
Useful? React with 👍 / 👎.
Summary
Adds a durable, versioned candidate-blurb library so candidate-written introductions can be reused generally or tailored to a gig without creating a false application record.
/save-blurb,/draft-blurb, and Apps → Save blurb on candidate messages, including DMs.Why
Candidate blurbs often arrive informally and retain the candidate's own voice. Previously they had to be requested and recreated for every gig. This creates a reusable, auditable library while preserving the distinction between a general profile and candidate-authored pitch.
Validation
./scripts/test.sh— 1,942 passed, 20 skipped./scripts/pyrefly.sh— 0 errorsbun run lint && bun run typecheck && bun run test(admin dashboard) — passeduv run --package worker alembic -c pyproject.toml heads—20260715_0100 (head)Note
Medium Risk
Large new write surface for candidate-facing text with Steering-gated but multi-channel entry (dashboard, Discord, AI drafts); correctness of scoping and “no false application” behavior matters for recruiting workflows.
Overview
Introduces a versioned candidate blurb library separate from CRM profile descriptions: immutable lineages with provenance (
candidate,candidate_attributed,team,ai), general vs gig scope, and status/history. Persistence lands via migration (candidate_blurbs,people.profile_summary) and sharedfive08.candidate_blurbs(save, list, batch enrich, review-only LLM draft grounded on synced facts and prior samples).Dashboard (Steering/admin-gated): new APIs under people, gig applications, and unattached gig identities; gig detail enriches applications plus gig-level blurbs that never imply an application. People and Gigs get
CandidateBlurbPanel(load, paste, AI draft, edit/replace version, copy, history). Sessiongroups/is_admindrive UI gating aligned with API steering checks.Discord:
/save-blurb,/draft-blurb, and Save blurb message action—private modals/views, explicit confirm before persist, scope from registered gig thread or general; DM access resolves roles against the configured server.Worker: CRM
descriptionsyncs intoprofile_summaryfor draft context.Reviewed by Cursor Bugbot for commit adac78a. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by CodeRabbit
New Features
Documentation
Tests