Add Candidate Page and Claim Highlights Feature - #5371
Add Candidate Page and Claim Highlights Feature#5371rudransh-shrivastava wants to merge 21 commits into
Conversation
Add GraphQL node/query/mutation for the model. Also update existing claim mutations and queries, allow REJECTED claim/evidence to be visible to anyone. Update the owasp_sync_board_candidates command to sync the raw markdown text. Add missing make target for owasp_sync_board_candidates Signed-off-by: Rudransh Shrivastava <rudransh.shrivastava@owasp.org>
add AnnotatedProfile component to handle annotations. update single claim and evidence pages to show approved/rejected claims publicly. add source_url to queries and mutations but next commit will wire it up with create/update pages. Signed-off-by: Rudransh Shrivastava <rudransh.shrivastava@owasp.org>
show source text in create/update pages. show candidate page in breadcrumbs. Signed-off-by: Rudransh Shrivastava <rudransh.shrivastava@owasp.org>
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
Summary by CodeRabbit
WalkthroughAdds synchronized board candidate profiles, annotated Markdown rendering, claim source text, and access to rejected claims and evidence across the backend and frontend. ChangesBoard candidate transparency
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
🧪 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: 13
🤖 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 `@backend/make/apps/owasp.mk`:
- Line 5: Update the .PHONY target list in the OWASP make configuration so
owasp-sync-board-candidates appears before owasp-sync-posts, and wrap the
entries to match the surrounding line-width convention.
In `@backend/src/apps/owasp/management/commands/owasp_sync_board_candidates.py`:
- Around line 70-85: Update parse_candidate_profile so the YAML frontmatter
pattern accepts zero interior lines, allowing an empty `---\n---` block to be
removed and return an empty profile instead of the delimiters. Add a regression
test covering empty frontmatter and preserve existing behavior for non-empty
frontmatter and content without frontmatter.
In
`@backend/tests/unit/apps/owasp/api/internal/mutations/board_candidate_claim_test.py`:
- Line 713: Extend the test around the MagicMock input_data to cover source_text
with a non-empty value and with an empty string, in addition to None. For each
case, assert the resulting claim.source_text and the corresponding update_fields
values, preserving the existing omitted-field coverage.
- Around line 519-526: Add the required return type annotation to the
_make_input_data test helper, using the appropriate type for the MagicMock-based
object it returns, so the function satisfies Ruff’s ANN202 check without
changing its behavior.
In `@frontend/__tests__/unit/components/AnnotatedProfile.test.tsx`:
- Around line 15-17: Update the dompurify mock usage in AnnotatedProfile tests
so at least one test bypasses the identity mock and exercises the real
sanitizer; provide rawMarkdown containing a script tag or onerror attribute,
call renderMarkdown, and assert the unsafe content is removed from the result.
- Around line 81-83: Update the test title for the overlapsExistingClaim case in
AnnotatedProfile tests to state that it returns true when the selection equals a
claimed text, matching the existing assertion.
In `@frontend/__tests__/unit/pages/BoardCandidatesPage.test.tsx`:
- Around line 16-19: Update the next/navigation mock around useRouter to reuse a
hoisted, externally accessible push mock instead of creating one per call, then
add a BoardCandidatesPage test that clicks a candidate card with a login and
asserts navigation to /board/{year}/candidates/{login}.
In `@frontend/src/app/board/`[year]/candidates/[login]/claims/[claimKey]/page.tsx:
- Around line 73-79: Reorder the checks in the page component so the existing
request-error display and missing-claim 404 display run before the canView
access-denial branch. Keep the current AccessDeniedDisplay behavior for claims
that exist but the user cannot view, using the surrounding claim-loading symbols
to preserve each outcome.
In `@frontend/src/app/board/`[year]/candidates/[login]/page.tsx:
- Around line 17-24: Validate the parsed year route parameter before the
useQuery call, and render the existing not-found response when it is non-numeric
instead of passing NaN as the GraphQL Int variable. Keep valid-year query
behavior unchanged and reuse the page’s existing 404 handling path.
- Around line 41-49: Update the profile-loading logic in the page component to
check the query `error` before the `!profile` branch and render the existing
error state for query failures. Keep the 404 ErrorDisplay only for successful
responses where no profile is returned.
In `@frontend/src/components/AnnotatedProfile.tsx`:
- Around line 89-101: Update the selection-to-highlight flow involving onMouseUp
and toHighlightRanges so stored rendered-text selections are matched against
rendered HTML text nodes rather than raw Markdown. Preserve correct start/end
offsets for ranges spanning Markdown or HTML syntax and typographer-transformed
quotes, ensuring every stored claim can produce visible highlights.
- Around line 232-296: Update AnnotatedProfile so injected mark[data-claim-key]
elements are keyboard-focusable with tabindex="0" and open their claim tooltip
through focusin as well as mouseover. Add keydown handling for Enter, preserving
the existing tooltip data and clearing behavior, and register corresponding
listeners with cleanup in the useEffect. Extend the selection-popup flow around
onMouseUp to react to keyboard selection via selectionchange or keyup, while
retaining the existing validation and overlap checks.
- Around line 143-151: Update renderSegment to provide a fallback color when
STATUS_COLOR[claim.status] is unmapped, using the same bg-gray-400 fallback
already applied for the status dot at line 372. Preserve the existing mapped
colors and mark rendering.
🪄 Autofix
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: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: ada7f184-fa23-45e4-9e64-86bdb309bf21
⛔ Files ignored due to path filters (4)
frontend/src/types/__generated__/boardQueries.generated.tsis excluded by!**/*.generated.*,!**/__generated__/**frontend/src/types/__generated__/claimMutations.generated.tsis excluded by!**/*.generated.*,!**/__generated__/**frontend/src/types/__generated__/claimQueries.generated.tsis excluded by!**/*.generated.*,!**/__generated__/**frontend/src/types/__generated__/graphql.tsis excluded by!**/__generated__/**
📒 Files selected for processing (47)
backend/make/apps/owasp.mkbackend/src/apps/owasp/admin/__init__.pybackend/src/apps/owasp/admin/board_candidate_profile.pybackend/src/apps/owasp/api/internal/mutations/board_candidate_claim.pybackend/src/apps/owasp/api/internal/nodes/board_candidate_claim.pybackend/src/apps/owasp/api/internal/nodes/board_candidate_profile.pybackend/src/apps/owasp/api/internal/queries/__init__.pybackend/src/apps/owasp/api/internal/queries/board_candidate_claim.pybackend/src/apps/owasp/api/internal/queries/board_candidate_claim_evidence.pybackend/src/apps/owasp/api/internal/queries/board_candidate_profile.pybackend/src/apps/owasp/management/commands/owasp_sync_board_candidates.pybackend/src/apps/owasp/migrations/0081_boardcandidateclaim_source_text_and_more.pybackend/src/apps/owasp/models/__init__.pybackend/src/apps/owasp/models/board_candidate_claim.pybackend/src/apps/owasp/models/board_candidate_profile.pybackend/tests/unit/apps/owasp/admin/board_candidate_profile_test.pybackend/tests/unit/apps/owasp/api/internal/mutations/board_candidate_claim_test.pybackend/tests/unit/apps/owasp/api/internal/nodes/board_candidate_claim_test.pybackend/tests/unit/apps/owasp/api/internal/queries/board_candidate_claim_evidence_test.pybackend/tests/unit/apps/owasp/api/internal/queries/board_candidate_claim_test.pybackend/tests/unit/apps/owasp/management/commands/owasp_sync_board_candidates_test.pybackend/tests/unit/apps/owasp/models/board_candidate_claim_test.pybackend/tests/unit/apps/owasp/models/board_candidate_profile_test.pye2e/pages/BoardCandidateClaimDetails.spec.tse2e/pages/BoardCandidateClaims.spec.tsfrontend/__tests__/unit/components/AnnotatedProfile.test.tsxfrontend/__tests__/unit/components/forms/shared/FormTextarea.test.tsxfrontend/__tests__/unit/hooks/useBreadcrumbs.test.tsxfrontend/__tests__/unit/pages/BoardCandidatesPage.test.tsxfrontend/__tests__/unit/pages/ClaimDetailsPage.test.tsxfrontend/__tests__/unit/pages/CreateClaimPage.test.tsxfrontend/__tests__/unit/pages/EditClaimPage.test.tsxfrontend/__tests__/unit/pages/EvidenceDetailsPage.test.tsxfrontend/src/app/board/[year]/candidates/[login]/claims/[claimKey]/edit/page.tsxfrontend/src/app/board/[year]/candidates/[login]/claims/[claimKey]/evidences/[evidenceKey]/page.tsxfrontend/src/app/board/[year]/candidates/[login]/claims/[claimKey]/page.tsxfrontend/src/app/board/[year]/candidates/[login]/claims/create/page.tsxfrontend/src/app/board/[year]/candidates/[login]/layout.tsxfrontend/src/app/board/[year]/candidates/[login]/page.tsxfrontend/src/app/board/[year]/candidates/page.tsxfrontend/src/app/globals.cssfrontend/src/components/AnnotatedProfile.tsxfrontend/src/components/ClaimForm.tsxfrontend/src/components/forms/shared/FormTextarea.tsxfrontend/src/server/mutations/claimMutations.tsfrontend/src/server/queries/boardQueries.tsfrontend/src/server/queries/claimQueries.ts
💤 Files with no reviewable changes (1)
- frontend/src/app/board/[year]/candidates/[login]/layout.tsx
There was a problem hiding this comment.
All reported issues were addressed across 51 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
Signed-off-by: Rudransh Shrivastava <rudransh.shrivastava@owasp.org>
Signed-off-by: Rudransh Shrivastava <rudransh.shrivastava@owasp.org>
Signed-off-by: Rudransh Shrivastava <rudransh.shrivastava@owasp.org>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## feature/bod-candidate-transparency #5371 +/- ##
======================================================================
- Coverage 98.46% 98.41% -0.06%
======================================================================
Files 564 570 +6
Lines 18521 18677 +156
Branches 2645 2666 +21
======================================================================
+ Hits 18237 18381 +144
- Misses 163 173 +10
- Partials 121 123 +2
Flags with carried forward coverage won't be shown. Click here to find out more.
Continue to review full report in Codecov by Harness.
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
frontend/src/app/board/[year]/candidates/[login]/page.tsx (1)
17-26: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winAdd the invalid-year guard to
skip.
parsedYearisNaNfor a non-numeric route segment.skiponly checksisSyncing, so Apollo still sends the request withyear: NaN.NaNis not a valid GraphQLInt, so the request fails andhandleAppErrorreports a spurious error before the 404 renders. Add the check toskip.🐛 Proposed fix
const { data, error, loading } = useQuery(GetCandidateProfileDocument, { - skip: isSyncing, + skip: isSyncing || Number.isNaN(parsedYear), variables: {🤖 Prompt for 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. In `@frontend/src/app/board/`[year]/candidates/[login]/page.tsx around lines 17 - 26, Update the useQuery call for GetCandidateProfileDocument so skip is true when either isSyncing or parsedYear is NaN, preventing requests with an invalid year while preserving the existing query behavior for valid years.
🤖 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 `@frontend/src/app/board/`[year]/candidates/page.tsx:
- Around line 298-307: Replace the card’s outer button and its
handleCardClick/onKeyDown behavior with a non-interactive wrapper, preserving
the card styling and click behavior through an appropriate primary
candidate-name link or overlay link. Ensure the existing LinkedIn, GitHub,
chapter, and project links remain independently nested without
interactive-element violations, and remove the now-unnecessary keyboard handler.
In `@frontend/src/components/AnnotatedProfile.tsx`:
- Around line 239-263: Update the renderMark callback so the role="button" mark
activates consistently: handle both Enter and Space in onKeyDown, and add an
onClick that clears the tooltip and navigates to the same claim URL via
router.push. Preserve the existing event prevention and navigation behavior.
- Around line 265-281: Update the compiler options in the useMemo block to
disable parsing raw HTML from rawMarkdown by setting disableParsingRawHTML:
true. Preserve the existing img and source URL resolution overrides and the
highlight-range processing.
---
Duplicate comments:
In `@frontend/src/app/board/`[year]/candidates/[login]/page.tsx:
- Around line 17-26: Update the useQuery call for GetCandidateProfileDocument so
skip is true when either isSyncing or parsedYear is NaN, preventing requests
with an invalid year while preserving the existing query behavior for valid
years.
🪄 Autofix
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: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 6d91d92c-bd92-4a1b-97c7-f37d1ba1e62a
⛔ Files ignored due to path filters (1)
frontend/pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (18)
backend/make/apps/owasp.mkbackend/src/apps/owasp/admin/board_candidate_profile.pybackend/src/apps/owasp/api/internal/queries/board_candidate_profile.pybackend/src/apps/owasp/management/commands/owasp_sync_board_candidates.pybackend/src/apps/owasp/migrations/0081_boardcandidateclaim_source_text_and_more.pybackend/src/apps/owasp/models/board_candidate_profile.pybackend/tests/unit/apps/owasp/api/internal/mutations/board_candidate_claim_test.pybackend/tests/unit/apps/owasp/api/internal/queries/board_candidate_claim_evidence_test.pyfrontend/__tests__/unit/components/AnnotatedProfile.test.tsxfrontend/__tests__/unit/pages/BoardCandidatesPage.test.tsxfrontend/package.jsonfrontend/src/app/board/[year]/candidates/[login]/claims/[claimKey]/page.tsxfrontend/src/app/board/[year]/candidates/[login]/claims/create/page.tsxfrontend/src/app/board/[year]/candidates/[login]/page.tsxfrontend/src/app/board/[year]/candidates/page.tsxfrontend/src/components/AnnotatedProfile.tsxfrontend/src/components/ClaimForm.tsxfrontend/src/components/forms/shared/FormTextarea.tsx
💤 Files with no reviewable changes (1)
- frontend/src/components/ClaimForm.tsx
There was a problem hiding this comment.
All reported issues were addressed across 19 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
Signed-off-by: Rudransh Shrivastava <rudransh.shrivastava@owasp.org>
Signed-off-by: Rudransh Shrivastava <rudransh.shrivastava@owasp.org>
add useProfileSelection hook and ClaimHighlight component. Signed-off-by: Rudransh Shrivastava <rudransh.shrivastava@owasp.org>
Signed-off-by: Rudransh Shrivastava <rudransh.shrivastava@owasp.org>
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
frontend/__tests__/unit/pages/CreateClaimPage.test.tsx (1)
279-306: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winAlign the mocked mutation response with the new response shape.
The test now verifies
sourceTextin the mutation input, but the mocked claim response still omitssourceText. Add this field to the mock response and assert the returned or cached value if this flow depends on it.🤖 Prompt for 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. In `@frontend/__tests__/unit/pages/CreateClaimPage.test.tsx` around lines 279 - 306, The mocked claim response used by the CreateClaimPage tests must include sourceText to match the updated response shape. Update the relevant mock mutation response fixture and, if the flow exposes or caches the created claim, assert that the returned or cached sourceText is preserved.
♻️ Duplicate comments (1)
frontend/src/app/board/[year]/candidates/[login]/page.tsx (1)
17-24: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winReject partially numeric board routes.
Number.parseInt('2025invalid')returns2025. The page can load the 2025 board for a malformed route instead of showing the existing 404 response. Parse and validate the complete segment, then use the same validation result forskipand the 404 branch.Proposed fix
- const parsedYear = Number.parseInt(year) + const parsedYear = Number(year) + const hasValidYear = /^\d+$/.test(year) && Number.isSafeInteger(parsedYear) const { data, error, loading } = useQuery(GetCandidateProfileDocument, { - skip: isSyncing || Number.isNaN(parsedYear), + skip: isSyncing || !hasValidYear, ... - if (Number.isNaN(parsedYear)) { + if (!hasValidYear) {🤖 Prompt for 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. In `@frontend/src/app/board/`[year]/candidates/[login]/page.tsx around lines 17 - 24, Update the year parsing and validation in the candidate profile page to reject partially numeric route segments such as “2025invalid” while accepting only a complete valid year value. Reuse this single validation result for both the GetCandidateProfileDocument query’s skip option and the existing 404 branch, and pass the validated year to the query variables.
🤖 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.
Outside diff comments:
In `@frontend/__tests__/unit/pages/CreateClaimPage.test.tsx`:
- Around line 279-306: The mocked claim response used by the CreateClaimPage
tests must include sourceText to match the updated response shape. Update the
relevant mock mutation response fixture and, if the flow exposes or caches the
created claim, assert that the returned or cached sourceText is preserved.
---
Duplicate comments:
In `@frontend/src/app/board/`[year]/candidates/[login]/page.tsx:
- Around line 17-24: Update the year parsing and validation in the candidate
profile page to reject partially numeric route segments such as “2025invalid”
while accepting only a complete valid year value. Reuse this single validation
result for both the GetCandidateProfileDocument query’s skip option and the
existing 404 branch, and pass the validated year to the query variables.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 6a69d631-a382-4a43-9c52-8d3b9b0007c8
⛔ Files ignored due to path filters (1)
frontend/src/types/__generated__/claimMutations.generated.tsis excluded by!**/*.generated.*,!**/__generated__/**
📒 Files selected for processing (10)
frontend/__tests__/unit/components/AnnotatedProfile.test.tsxfrontend/__tests__/unit/components/ClaimHighlight.test.tsxfrontend/__tests__/unit/hooks/useProfileSelection.test.tsxfrontend/__tests__/unit/pages/ClaimDetailsPage.test.tsxfrontend/__tests__/unit/pages/CreateClaimPage.test.tsxfrontend/src/app/board/[year]/candidates/[login]/page.tsxfrontend/src/components/AnnotatedProfile.tsxfrontend/src/components/ClaimHighlight.tsxfrontend/src/hooks/useProfileSelection.tsfrontend/src/server/mutations/claimMutations.ts
There was a problem hiding this comment.
All reported issues were addressed across 11 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
Signed-off-by: Rudransh Shrivastava <rudransh.shrivastava@owasp.org>
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
frontend/src/components/AnnotatedProfile.tsx (1)
119-123: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy liftPersist a unique profile anchor with the selected text.
useProfileSelectionderivesselection.textfrom rendered text. Line 121 sends only that text.wrapClaimslater searchesrawMarkdownwithindexOfat Line 65.If a selection crosses Markdown syntax, the rendered text is not a contiguous raw Markdown substring. For example, selecting rendered
Board memberfrom**Board** membercauses the claim to be stored but skipped at Line 66.Persist a profile revision plus a stable range or equivalent anchor. Resolve highlights against the same rendered representation. Add coverage for selections that cross inline Markdown syntax.
🤖 Prompt for 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. In `@frontend/src/components/AnnotatedProfile.tsx` around lines 119 - 123, Update handleCreateFromSelection and the related useProfileSelection/wrapClaims flow to persist a profile revision plus a stable selection range or equivalent anchor instead of relying only on selection.text. Resolve claim highlights against the same rendered representation used to derive the selection, so selections spanning Markdown syntax such as bold markers are matched and wrapped correctly. Add coverage for a selection crossing inline Markdown syntax.
🤖 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.
Outside diff comments:
In `@frontend/src/components/AnnotatedProfile.tsx`:
- Around line 119-123: Update handleCreateFromSelection and the related
useProfileSelection/wrapClaims flow to persist a profile revision plus a stable
selection range or equivalent anchor instead of relying only on selection.text.
Resolve claim highlights against the same rendered representation used to derive
the selection, so selections spanning Markdown syntax such as bold markers are
matched and wrapped correctly. Add coverage for a selection crossing inline
Markdown syntax.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 5f6e6979-4d67-4e27-9766-a90f56b8d10b
📒 Files selected for processing (6)
frontend/__tests__/unit/components/AnnotatedProfile.test.tsxfrontend/__tests__/unit/components/ClaimHighlight.test.tsxfrontend/__tests__/unit/hooks/useProfileSelection.test.tsxfrontend/src/components/AnnotatedProfile.tsxfrontend/src/components/ClaimHighlight.tsxfrontend/src/hooks/useProfileSelection.ts
There was a problem hiding this comment.
All reported issues were addressed across 6 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
…rkdown Signed-off-by: Rudransh Shrivastava <rudransh.shrivastava@owasp.org>
Signed-off-by: Rudransh Shrivastava <rudransh.shrivastava@owasp.org>
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
backend/src/apps/owasp/management/commands/owasp_generate_board_candidates_claims.py (1)
165-181: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winRequire a source text that matches the raw profile Markdown.
source_textcan be empty or can differ from the profile Markdown. The frontend only highlights an exact raw-Markdown match. These claims persist successfully but never render as highlights.Keep the original Markdown before
strip_tags(). Skip generated claims whensource_textis empty or absent from that original content. Update the tests that currently accept an emptysource_text.Proposed fix
- markdown_content = strip_tags(markdown_content) + raw_markdown = markdown_content + markdown_content = strip_tags(raw_markdown) ... - if name: + if name and source_text and source_text in raw_markdown: claims.append(🤖 Prompt for 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. In `@backend/src/apps/owasp/management/commands/owasp_generate_board_candidates_claims.py` around lines 165 - 181, Preserve the original profile Markdown before the content is passed through strip_tags(), then validate source_text in the claim-generation flow before appending BoardCandidateClaim instances. Skip claims when source_text is empty or is not an exact substring of the preserved raw Markdown, and update tests to reject empty source_text cases.
🤖 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.
Outside diff comments:
In
`@backend/src/apps/owasp/management/commands/owasp_generate_board_candidates_claims.py`:
- Around line 165-181: Preserve the original profile Markdown before the content
is passed through strip_tags(), then validate source_text in the
claim-generation flow before appending BoardCandidateClaim instances. Skip
claims when source_text is empty or is not an exact substring of the preserved
raw Markdown, and update tests to reject empty source_text cases.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: ab3f7270-76fe-4118-adb8-f70154889668
📒 Files selected for processing (5)
backend/make/apps/owasp.mkbackend/src/apps/owasp/management/commands/owasp_generate_board_candidates_claims.pybackend/tests/unit/apps/owasp/management/commands/owasp_generate_board_candidates_claims_test.pyfrontend/__tests__/unit/components/AnnotatedProfile.test.tsxfrontend/src/components/AnnotatedProfile.tsx
There was a problem hiding this comment.
All reported issues were addressed across 2 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
Signed-off-by: Rudransh Shrivastava <rudransh.shrivastava@owasp.org>
Signed-off-by: Rudransh Shrivastava <rudransh.shrivastava@owasp.org>
There was a problem hiding this comment.
All reported issues were addressed across 9 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
|
Contribution validation failed:
|
…ndidate-improvements Signed-off-by: Rudransh Shrivastava <rudransh.shrivastava@owasp.org>
kasya
left a comment
There was a problem hiding this comment.
@rudransh-shrivastava this looks pretty good 🔥 Left some comments, mostly about styling and one logic request. The parsing worked great 👍🏼
| const STATUS_STYLES: Record<string, StatusStyle> = { | ||
| [ClaimStatusEnum.Approved]: { | ||
| label: 'Approved', | ||
| mark: 'bg-green-200/70 text-green-900', | ||
| badge: 'bg-green-100 text-green-800 dark:bg-green-800/50 dark:text-green-100', | ||
| }, | ||
| [ClaimStatusEnum.Submitted]: { | ||
| label: 'Submitted', | ||
| mark: 'bg-yellow-200/70 text-yellow-900', | ||
| badge: 'bg-yellow-100 text-yellow-800 dark:bg-yellow-800/50 dark:text-yellow-100', | ||
| }, | ||
| [ClaimStatusEnum.Draft]: { | ||
| label: 'Draft', | ||
| mark: 'bg-gray-200/70 text-gray-800', | ||
| badge: 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-100', | ||
| }, | ||
| [ClaimStatusEnum.Rejected]: { | ||
| label: 'Rejected', | ||
| mark: 'bg-red-200/70 text-red-900', | ||
| badge: 'bg-red-100 text-red-800 dark:bg-red-800/50 dark:text-red-100', | ||
| }, | ||
| [ClaimStatusEnum.Withdrawn]: { | ||
| label: 'Withdrawn', | ||
| mark: 'bg-gray-200/70 text-gray-800', | ||
| badge: 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-100', | ||
| }, | ||
| [ClaimStatusEnum.Discarded]: { | ||
| label: 'Discarded', | ||
| mark: 'bg-gray-200/70 text-gray-800', | ||
| badge: 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-100', | ||
| }, | ||
| } | ||
|
|
There was a problem hiding this comment.
These are cool, but I'm wondering if that will be too colorful on the page..
What if we only leave 2 main colors?
Show rejected in red, as you do now and show submitted in something neutral... maybe light grey?
The rest would just show as a plain text, meaning that everything else is correct and does not need verification.
by the way, when testing current implementation - I did not see a yellow highlight for submitted claim. I could see green and red for approved and rejected, but nothing for submitted 🤔
There was a problem hiding this comment.
Yes, I think we shouldn't display discarded or withdrawn claims at all. Draft can stay, it helps user see the highlight before they submit.
Sure, I can make draft + submitted light gray or some variation of gray. Btw, draft claims are visible only to the candidate and submitted to candidate + reviewer for that year, they're not publicly visible.
I think you didn't see submitted claims because they're only visible to the candidate and reviewers for that year.
I usually just set the member of any candidate to me. That way, when i visit my page, it shows their profile content but I'm able to view all claims including submitted.
For above example, when I visit /board/2025/candidates/rudransh-shrivastava it renders Sam's profile but since its linked to my user, I can see all claims.

There was a problem hiding this comment.
@rudransh-shrivastava I see, thanks for clarifying. So drafts and submitted are not visible publicly and the rest are, is that correct?
I think the point is for these to be visible publicly to all not just the candidate itself. This way we could show people what claims were made and what was never confirmed. 🤔 What I mean is that if we don't show drafts/submitted claims to all - how would people know that these are not confirmed yet? The styling will match - no highlighting at all, which would imply they were verified.
I can see how we might not need to show drafts, yes! But in my opinion submitted should be shown publicly 👍🏼
What do you think?
There was a problem hiding this comment.
And thanks for suggesting that workaround with attaching to your own member - that should work great for testing 👍🏼
There was a problem hiding this comment.
Yes, I can discuss this in the huddle this Friday.
There was a problem hiding this comment.
Partially addressed by #5384
Submitted claims are now publicly visible.
Will update frontend here soon.
There was a problem hiding this comment.
Addressed, submitted claims are now gray.
I display "UNDER REVIEW" for public but also include a "SUBMITTED" for the candidates own profile (self).
There was a problem hiding this comment.
@rudransh-shrivastava do I need to re-run any commands? I am still not seeing submitted claim highlighted publicly 🤔 The claim is in the first point for Open Source Leadership:

However, I can see it when I'm logged in as that user. Just not publicly:

Also, I believe the approved one should not be highlighted with any color, as discussed above and during our huddle last Friday. I thought we agreed on highlighting submitted and rejected, meaning that everything else was either verified and approved or is not a verifiable claim.
Am I misunderstanding something? 👀
There was a problem hiding this comment.
Hi @kasya
The part to make submitted claims public is handled in this PR: #5384
I mentioned this in above comment. Why? because it's easier to resolve merge conflicts that way. Changes to claim visibly affect claims, attached evidences, and also the reviews on that claim, a lot of change in multiple files.
You may merge that PR locally, but I think you can test it separately as its easier.
For the highlights, yes I think there's a misunderstanding on my part.
In the huddle, I think I misunderstood that we should not highlight verified claims. I asked if submitted claims should be visible publicly and agreed to making them public and highlight in gray.
The huddle notes also only show this:

I will update the code to not display approved claims but I think that it breaks the entire point of my project for these reasons:
- Currently, we don't reject claims at all. There is no function or feature to reject a claim. The red highlights will never be shown unless we manually reject a claim using Django admin. I think it's something I can improve, but currently the code does not reject a claim.
- If we never show any highlights for approved claims, there is no distinction between a profile with a lot of verified claims and one with no verified claims. The highlights just disappear as soon as they're verified/approved.
There was a problem hiding this comment.
I also couldn't see any data first here or on the card.
I could see claims being parsed and created just fine in the admin view, but candidate page returned claims as an empty array.
To fix this I had to go into Entity member and link that candidate as a member (it was empty):
I got to this from the Board Candidate link.
Is there a way to set this when we create candidate profiles? 🤔 Otherwise we'd have to manually go and link all candidates.
There was a problem hiding this comment.
I think you mean something like this, right?

This is an existing issue, it happens because it's difficult to correlate GitHub login/user of candidate when syncing them. The sync command can only read the name and email ("" if not found) from the markdown files in www-board-candidates repository.
I can probably search the db and set member in the command itself, but it will be fragile and not guaranteed to work for all candidates. What do you think @arkid15r @kasya ?
There was a problem hiding this comment.
I see.. I guess we could manually update candidates before each election, but that would be tedious still and we'll need to remember to do it 😅 Otherwise just an empty card and page will show up.
There was a problem hiding this comment.
Oh, I did add it earlier. Then, I ran into a problem with pages like these: https://owasp.org/www-board-candidates/2025/aruneesh_salhotra.html
These pages don't seem to have a dark theme. So it looked really bad with a dark background. That's why it's a forced light theme.
There was a problem hiding this comment.
@rudransh-shrivastava Yep, they are designed strictly for light theme, but we need to make user our users have pleasant experience on our website. Being switched to such a light page after many dark ones - could be pretty harsh!
I played around with it and I believe you should be able to achieve overriding the colors on the page. I'll leave an inline comment for more details to cover this.
Signed-off-by: Rudransh Shrivastava <rudransh.shrivastava@owasp.org>
There was a problem hiding this comment.
All reported issues were addressed across 4 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
Signed-off-by: Rudransh Shrivastava <rudransh.shrivastava@owasp.org>
|
kasya
left a comment
There was a problem hiding this comment.
Hey @rudransh-shrivastava ! replied to a couple of your comments with questions/suggestions. Also attaching my suggestion on how to fix the light background for the page ⬇️
| <div className="relative"> | ||
| <div | ||
| ref={containerRef} | ||
| className="md-wrapper rounded-xl bg-white p-6 text-gray-700 shadow-sm" |
There was a problem hiding this comment.
We can add a custom style here that will override any inline styling colors. It will inherit the colors we pass in.
| className="md-wrapper rounded-xl bg-white p-6 text-gray-700 shadow-sm" | |
| className="md-wrapper md-neutralized rounded-xl bg-white p-6 text-gray-700 shadow-sm dark:bg-slate-800 dark:text-gray-300" | |
Then in styles it would be something like this:
/*
* Candidate profiles are synced verbatim from www-board-candidates, which is
* authored for a light-only site and ships arbitrary inline colors. Drop them
* so the content always inherits whichever color the active theme sets on the
* wrapper. Code and claim highlights keep their own themed backgrounds, and
* links additionally keep their own themed color.
*/
.md-neutralized *:not(code, [data-claim-highlight]) {
background-color: transparent !important;
background-image: none !important;
text-shadow: none !important;
}
.md-neutralized *:not(a, code, [data-claim-highlight]) {
color: inherit !important;
}
The colors I added were pretty much random, so feel free to change them if you feel like something else will look better.
| const STATUS_STYLES: Record<string, StatusStyle> = { | ||
| [ClaimStatusEnum.Approved]: { | ||
| label: 'Approved', | ||
| mark: 'bg-green-200/70 text-green-900', | ||
| badge: 'bg-green-100 text-green-800 dark:bg-green-800/50 dark:text-green-100', | ||
| }, | ||
| [ClaimStatusEnum.Submitted]: { | ||
| label: 'Submitted', | ||
| mark: 'bg-yellow-200/70 text-yellow-900', | ||
| badge: 'bg-yellow-100 text-yellow-800 dark:bg-yellow-800/50 dark:text-yellow-100', | ||
| }, | ||
| [ClaimStatusEnum.Draft]: { | ||
| label: 'Draft', | ||
| mark: 'bg-gray-200/70 text-gray-800', | ||
| badge: 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-100', | ||
| }, | ||
| [ClaimStatusEnum.Rejected]: { | ||
| label: 'Rejected', | ||
| mark: 'bg-red-200/70 text-red-900', | ||
| badge: 'bg-red-100 text-red-800 dark:bg-red-800/50 dark:text-red-100', | ||
| }, | ||
| [ClaimStatusEnum.Withdrawn]: { | ||
| label: 'Withdrawn', | ||
| mark: 'bg-gray-200/70 text-gray-800', | ||
| badge: 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-100', | ||
| }, | ||
| [ClaimStatusEnum.Discarded]: { | ||
| label: 'Discarded', | ||
| mark: 'bg-gray-200/70 text-gray-800', | ||
| badge: 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-100', | ||
| }, | ||
| } | ||
|
|
There was a problem hiding this comment.
@rudransh-shrivastava do I need to re-run any commands? I am still not seeing submitted claim highlighted publicly 🤔 The claim is in the first point for Open Source Leadership:

However, I can see it when I'm logged in as that user. Just not publicly:

Also, I believe the approved one should not be highlighted with any color, as discussed above and during our huddle last Friday. I thought we agreed on highlighting submitted and rejected, meaning that everything else was either verified and approved or is not a verifiable claim.
Am I misunderstanding something? 👀





Proposed change
Part 1: Partially Resolves #4628
backend:
frontend:
Checklist