Skip to content

[#531] Add configurable project scoring and judge deliberation - #532

Merged
DVidal1205 merged 5 commits into
mainfrom
blade/project-scoring
Sep 6, 2026
Merged

[#531] Add configurable project scoring and judge deliberation#532
DVidal1205 merged 5 commits into
mainfrom
blade/project-scoring

Conversation

@DVidal1205

Copy link
Copy Markdown
Contributor

Why

Blade can import Devpost projects and grant room-based judging access, but the actual judging work still lives outside the product. Judges need a clear queue, an editable rubric submission, and a private way to prepare for deliberation. Officers need to configure that workflow without a yearly code change and audit what was submitted.

This slice turns the project table into the judging core while preserving the guest isolation introduced in #529. Sponsor guests stay locked to their room challenge. Authenticated judges keep their broader role-based access. Written feedback visibility now explicitly controls delivery to hackers, while authenticated judges and officers can review every response.

What

Closes: #531

Configurable rubric and lifecycle

  • Officers can configure any number of required 1 through 5 rating questions and written-response questions per hackathon.
  • Written responses support Never share with hackers, Guest chooses hacker visibility, and Always share with hackers policies. Authenticated judge feedback is always shared with hackers.
  • An evaluation score is the average of its quantitative answers. Challenge ratings average evaluations in the selected challenge; overall ratings average evaluations across every challenge.
  • Judging moves through Draft, Open, and Closed states. Closed judging remains readable and may be reopened.
  • The first evaluation locks rubric editing and destructive inventory replacement. Normal imports remain add-only and deduplicate projects by normalized Devpost URL.
  • Officers may reveal challenge results early to authenticated judges with Display all results. Guest score visibility never expands.

Projects queue

  • Adds Projects, Submissions, and Deliberation tabs to the judge workspace.
  • The project table shows Challenge rating for the active scope and Rating across all scopes to authenticated members. Score sorting happens on the server and preserves pagination.
  • Projects disappear from the active queue after the current judge evaluates them in that challenge. See previously judged is off by default and restores those rows.
  • Authenticated members see challenge badges turn green after the first evaluation in that challenge. General uses the darker green treatment.
  • Guest rows omit the Challenges column and every other challenge assignment. Their room challenge remains fixed and server-enforced.
  • The evaluation dialog supports creating and editing one evaluation per judge, project, and challenge while judging is Open.

Submissions and feedback

  • Judges can review their complete personal submission history and reopen any entry for editing.
  • Written feedback is available on click instead of expanding the project table.
  • Member judges see a prominent notice that their responses are shared with hackers.
  • Optional guest responses default to private and expose a per-response control with immediate Shared with hackers or Not shared with hackers feedback.
  • Officers receive a revision audit view with every saved version of an evaluation.

Private deliberation

  • Each judge can create private named sections and add projects they have already evaluated.
  • Project selection uses a searchable combobox.
  • Sections and projects support drag-and-drop ordering plus keyboard-friendly move controls.
  • Reordering or renaming a deliberation section never changes an evaluation score.

Project command center

  • Combines judging setup, project inventory, room and QR controls, live rosters, and evaluation history in one officer workspace.
  • Renames the sidebar entry to Command Center while retaining the descriptive page heading.
  • Keeps /admin/projects compatible through a server redirect into the command center Projects tab.

Data and access controls

  • Adds normalized rubric, evaluation, answer, revision, deliberation-section, and ranked-project tables through migration 0046_hot_zarda.sql.
  • Uses database constraints and transactions for one-evaluation-per-scope behavior, lifecycle transitions, revisions, and ordering.
  • Preserves hash-only guest credentials, room revocation, and server-side challenge scoping from [#528] Add room-based guest judging access #529.
  • Adds the new judging data to sanitized development-backup removal.
  • Adds audit coverage for officer configuration and evaluation revision events.

Screenshots

No screenshot or Devpost CSV is committed to Forge. All review media below is hosted separately from the repository.

1. Authenticated judge project queue

Authenticated judge project queue with score columns, judged toggle, and green challenge completion badges

Authenticated judges retain the normal Blade shell, room selection, unrestricted challenge filters, both score columns, and challenge completion badges. The screenshot enables See previously judged to show the completed AdaptLearn AI row beside the remaining queue.

2. Authenticated judge rubric

Authenticated member evaluation dialog with configured rating questions

The active challenge is called out at the top. Every quantitative question uses the configured 1 through 5 scale and touch-friendly controls.

3. Forced hacker feedback delivery

Authenticated member feedback field marked as shared with hackers

Authenticated judges do not receive a misleading privacy choice. The dialog states that the project team will receive the response.

4. Personal submissions

Judge submission history with editable evaluations

The Submissions tab gives each judge a single place to review and edit prior work.

5. Private deliberation workspace

Private deliberation sections with ranked projects

Judges can build personal shortlists without changing any recorded score.

6. Searchable deliberation project picker

Searchable combobox for adding judged projects to deliberation

Only projects the current judge has evaluated are eligible, and the combobox supports text search.

7. Officer setup

Officer judging setup with lifecycle, result visibility, and configurable rubric

The setup tab centralizes judging state, authenticated result visibility, and the data-driven rubric. This KH VIII fixture is already Open, so the rubric is visibly locked.

8. Protected project inventory

Officer project inventory with add-only and destructive replacement controls

Ordinary imports stay available for unseen projects. Replacement and full deletion remain separate destructive actions after judging begins.

9. Rooms and live roster

Officer room controls and live judge roster

The existing room model remains the source of guest challenge scope and now sits beside the rest of the judging controls. Officers can see account and QR guests together.

10. Evaluation audit history

Officer evaluation history with project, challenge, judge, and revision count

Officers can inspect the current evaluation and every saved revision from one audit table.

11. Unskippable guest introduction

Guest judge name dialog before project access

The projects shell stays blocked until the guest introduces themselves. The dialog explains that the name supports deliberation and identity verification and that responses are not shared from this introduction step.

12. Guest project queue

Room-scoped guest project queue without challenge metadata

Guests see only the room challenge, its remaining projects, Challenge rating, and the judged-project toggle. The Challenges and overall Rating columns are absent.

13. Guest rubric context

Guest evaluation dialog with hacker-delivery explanation

The guest dialog explains that judges and officers can review every response and that the field-level policy controls what hackers receive.

14. Optional guest feedback

Guest feedback field defaulting to not shared with hackers

Optional guest feedback defaults to private. The toggle and status text name the recipient directly, so the effect is clear before submission.

15. Mobile authenticated queue

Mobile authenticated judge project queue

The server-rendered mobile shell keeps room and hackathon controls, all three workspace tabs, the judged-project toggle, and a card layout with usable touch targets.

Test Plan

Automated verification:

  • pnpm verify:precommit
  • pnpm --filter @forge/db with-env drizzle-kit check
  • pnpm --filter @forge/blade build
  • git diff --check
  • Full package tests passed: 146 database, 768 API, 774 Blade, and 267 validator tests. Total: 1,955.
  • Disposable-database integration coverage verifies rubric lifecycle, evaluation creation and editing, revisions, guest and member scoping, score aggregation, score sorting, judged-project filtering, feedback delivery policy, deliberation ordering, import locking, and account deletion behavior.

Manual verification used KH VIII with 188 imported projects, 19 challenge records, and four judging rooms:

  • Authenticated judges can change challenge and room, sort by available ratings, submit and edit evaluations, review Submissions, and maintain private deliberation sections.
  • Completed projects disappear from Projects by default and return through See previously judged.
  • Authenticated challenge badges turn green after the first evaluation in that scope; General remains visually distinct.
  • Guest judges complete the required name gate, receive only their room challenge, never receive other challenge metadata, and cannot widen score visibility.
  • Hacker feedback delivery is explicit in member and guest dialogs, including the optional guest toggle.
  • Officers can manage lifecycle, result visibility, rubric, add-only imports, destructive replacement, rooms, QR access, live rosters, and evaluation revisions.
  • Loading, empty, closed, desktop, 390px, and 320px states were reviewed for the member and guest shells.
  • A depth-5 Forge review covered authorization and privacy, persistence and migration safety, API contracts, UI and accessibility, and test and product behavior. Every identified blocker was fixed and reverified.

Checklist

  • Database: No schema changes, OR I ran pnpm db:generate and committed the generated files in packages/db/drizzle/
  • Environment Variables: No environment variables changed, OR I have contacted the Development Lead to modify them on Coolify BEFORE merging.

Deployment note: this slice adds no environment variable. It continues to use the JUDGING_ACCESS_SECRET introduced and provisioned with #529.

@DVidal1205 DVidal1205 added Feature New Feature or Request Major Big change - 2+ reviewers required Blade Change modifies code in Blade app Database Change modifies code in the DB package API Change modifies code in the global API/tRPC package labels Sep 5, 2026
@DVidal1205 DVidal1205 self-assigned this Sep 5, 2026
@coderabbitai

coderabbitai Bot commented Sep 5, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

Next included review available in 28 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: QUIET

Plan: Team

Run ID: 42926537-336f-4722-8f73-15f66874dae0

📥 Commits

Reviewing files that changed from the base of the PR and between 6bee54b and 759c307.

📒 Files selected for processing (20)
  • .forge/features/judging-scores-and-deliberation/spec.md
  • .forge/features/judging-scores-and-deliberation/srd.md
  • .forge/features/judging-scores-and-deliberation/status.md
  • .forge/features/judging-scores-and-deliberation/test-cases.md
  • apps/blade/src/app/_components/judging/evaluation-dialog.tsx
  • apps/blade/src/app/_components/judging/judge-deliberation.tsx
  • apps/blade/src/app/_components/projects/judge-project-workspace.tsx
  • apps/blade/src/app/_components/projects/judge-projects-loading.tsx
  • apps/blade/src/app/judge/projects/loading.tsx
  • apps/blade/src/tests/projects/judge-deliberation.test.tsx
  • apps/blade/src/tests/projects/project-judge-privacy.test.tsx
  • packages/api/src/tests/integration/judging-access.test.ts
  • packages/db/drizzle/0047_fluffy_alex_power.sql
  • packages/db/drizzle/meta/0047_snapshot.json
  • packages/db/drizzle/meta/_journal.json
  • packages/db/src/schemas/knight-hacks.ts
  • packages/db/src/tests/judging-schema.test.ts
  • packages/db/src/tests/migration-lineage.test.ts
  • packages/validators/src/judging.ts
  • packages/validators/src/tests/judging.test.ts
📝 Walkthrough

Walkthrough

Adds configurable judging with rubric validation, evaluations, score aggregation, visibility controls, revision history, private deliberation, and lifecycle state management. Adds database schema, API procedures, project filtering, judge tabs, officer command center views, audit coverage, import locking, and tests.

Merge Risk: 🟡 Moderate · up to 6bee5

Judges can receive misleading feedback-sharing promises and stale deliberation views, while malformed evaluation data and invalid project–challenge relationships remain insufficiently constrained. These issues should be resolved before merge.

🚥 Pre-merge checks | ✅ 7 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 67 functions across 44 files. (6 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (7 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The reviewable changes address issue #531: configurable rubrics, judging states, score aggregation, editable evaluations, guest scoping, deliberation, officer command center, migration coverage, audit…
Out of Scope Changes check ✅ Passed The implementation, tests, migrations, specifications, and backup updates are related to the judging and deliberation objectives in issue #531. No unrelated code changes are evident.
No Hardcoded Secrets ✅ Passed No hardcoded API key, password, token, or secret was introduced. The PR diff contains no known provider-key or private-key patterns. The only secret-related test setup uses `vi.stubEnv("JUDGING_ACCESS…
Validated Env Access ✅ Passed No pull-request change adds direct process.env usage. The diff from base f4436df1 through HEAD contains no added process.env lines, and none of the changed files contains that reference. Exist…
No Typescript Escape Hatches ✅ Passed No TypeScript escape hatch was introduced in the feature diff. Added-line searches found no any type, @ts-ignore, @ts-expect-error, or non-null assertion. The only new any match is prose: “Add…
Title check ✅ Passed The title starts with the required issue number, describes the judging feature, and is 62 characters long.
Description check ✅ Passed The description clearly explains the configurable scoring, judge deliberation, access controls, data changes, and verification for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 67 functions across 44 files. (6 skipped: 6 unsupported.)

✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch blade/project-scoring

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@DVidal1205

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 5, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Note

Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.

🟡 Other comments (2)
apps/blade/src/app/_components/projects/judge-project-workspace.tsx-374-375 (1)

374-375: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Replace the (?) rating placeholder with readable text.

Screen readers announce (?) as punctuation. Use explicit text for the missing-rating state in both rating columns.

♻️ Proposed fix
                         const score = scoreByProject.get(project.id)?.scoped;
                         return score?.value === null || score === undefined ? (
-                          "(?)"
+                          <span className="text-muted-foreground">
+                            Not rated
+                          </span>
                         ) : (
                               const score = scoreByProject.get(
                                 project.id,
                               )?.overall;
                               return score?.value === null ||
                                 score === undefined
-                                ? "(?)"
+                                ? "Not rated"
                                 : `${score.value.toFixed(2)} (${score.count})`;
As per path instructions for `apps/blade/**`: "Accessibility (alt text, ARIA, semantic HTML)".

Also applies to: 400-403

Source: Path instructions

apps/blade/src/app/judge/projects/loading.tsx-1-4 (1)

1-4: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Add a nested Suspense boundary with a static skeleton fallback around JudgeProjectsLoadingView; its useSearchParams() call is not covered by the route loading fallback.

🧹 Nitpick comments (2)
packages/api/src/tests/integration/judging-access.test.ts (1)

750-757: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Sensitive Data Exposure (CWE-359)

Reachability: Internal · Exploitability: Theoretical

Assert isPublic: false for the private responses.

This block checks only response values. It does not verify that the "private" guest policy overrides the guest-provided isPublic: true.

💚 Proposed addition
+    expect(
+      memberDetails.feedback.find(
+        (item) => item.value === "Always private response",
+      ),
+    ).toMatchObject({ isPublic: false });
+    expect(
+      memberDetails.feedback.find(
+        (item) => item.value === "Private optional response",
+      ),
+    ).toMatchObject({ isPublic: false });

Source: Path instructions

apps/blade/src/tests/projects/project-judge-privacy.test.tsx (1)

295-297: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Assert the completion state, not the Tailwind token. ProjectBadges uses evaluationCount > 0 and title="1 evaluation" to expose completion. Replace the exact color assertion with expect(screen.getByTitle("1 evaluation")).toBeInTheDocument().


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: QUIET

Plan: Team

Run ID: 8b422e8b-5968-4768-8b3a-ee8b4bea12f3

📥 Commits

Reviewing files that changed from the base of the PR and between f4436df and 6bee54b.

⛔ Files ignored due to path filters (1)
  • packages/api/src/tests/root/__snapshots__/api-surface.test.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (51)
  • .forge/features/judging-scores-and-deliberation/spec.md
  • .forge/features/judging-scores-and-deliberation/srd.md
  • .forge/features/judging-scores-and-deliberation/status.md
  • .forge/features/judging-scores-and-deliberation/test-cases.md
  • apps/blade/src/app/_components/judging/evaluation-audit-panel.tsx
  • apps/blade/src/app/_components/judging/evaluation-dialog.tsx
  • apps/blade/src/app/_components/judging/judge-deliberation.tsx
  • apps/blade/src/app/_components/judging/judge-submissions.tsx
  • apps/blade/src/app/_components/judging/judging-configuration-panel.tsx
  • apps/blade/src/app/_components/judging/judging-control-panel.tsx
  • apps/blade/src/app/_components/judging/project-command-center.tsx
  • apps/blade/src/app/_components/judging/project-score-dialog.tsx
  • apps/blade/src/app/_components/projects/admin-project-workspace.tsx
  • apps/blade/src/app/_components/projects/judge-project-workspace.tsx
  • apps/blade/src/app/_components/projects/judge-projects-loading.tsx
  • apps/blade/src/app/_components/projects/params.ts
  • apps/blade/src/app/_components/projects/project-directory.tsx
  • apps/blade/src/app/_components/projects/project-workspace-skeleton.tsx
  • apps/blade/src/app/_components/shared/admin-navigation.ts
  • apps/blade/src/app/admin/judging/loading.tsx
  • apps/blade/src/app/admin/judging/page.tsx
  • apps/blade/src/app/admin/projects/page.tsx
  • apps/blade/src/app/judge/projects/loading.tsx
  • apps/blade/src/app/judge/projects/page.tsx
  • apps/blade/src/tests/admin/authenticated-shell.test.tsx
  • apps/blade/src/tests/admin/hackathon-admin-navigation.test.tsx
  • apps/blade/src/tests/projects/admin-projects-redirect.test.ts
  • apps/blade/src/tests/projects/evaluation-audit-panel.test.tsx
  • apps/blade/src/tests/projects/judge-deliberation.test.tsx
  • apps/blade/src/tests/projects/project-judge-privacy.test.tsx
  • packages/api/src/projects-import.server.ts
  • packages/api/src/routers/judging-scores.ts
  • packages/api/src/routers/judging.ts
  • packages/api/src/routers/projects.ts
  • packages/api/src/tests/integration/judging-access.test.ts
  • packages/api/src/utils/audit/coverage.ts
  • packages/api/src/utils/judging/scoring.test.ts
  • packages/api/src/utils/judging/scoring.ts
  • packages/db/drizzle/0046_hot_zarda.sql
  • packages/db/drizzle/meta/0046_snapshot.json
  • packages/db/drizzle/meta/_journal.json
  • packages/db/scripts/dev-db-backup-sanitizer.ts
  • packages/db/src/schemas/knight-hacks.ts
  • packages/db/src/tests/dev-db-backup-sanitizer.test.ts
  • packages/db/src/tests/judging-schema.test.ts
  • packages/db/src/tests/migration-lineage.test.ts
  • packages/validators/src/audit.ts
  • packages/validators/src/judging.ts
  • packages/validators/src/projects.ts
  • packages/validators/src/tests/audit.test.ts
  • packages/validators/src/tests/judging.test.ts

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.

Comment thread .forge/features/judging-scores-and-deliberation/spec.md Outdated
Comment thread apps/blade/src/app/_components/judging/judge-deliberation.tsx
Comment thread packages/db/drizzle/0046_hot_zarda.sql
Comment thread packages/validators/src/judging.ts Outdated
@DVidal1205

DVidal1205 commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

Addressed every finding from the first review in 712c74e.

  • Deliberation now adopts refreshed server sections after every CRUD mutation.
  • Evaluations require an actual project-to-challenge membership through a composite foreign key.
  • Duplicate rating and response item IDs fail validation.
  • The visible (?) placeholder now has an accessible Not rated label.
  • The route loading state has a static skeleton fallback around the search-param reader.
  • Privacy tests assert behavior instead of Tailwind implementation details, and integration coverage now checks private response flags.
  • The approved hacker-sharing behavior remains intact. The docs and interface now distinguish stored sharing eligibility from the later hacker-facing delivery view.

All four inline threads have replies and are resolved. Focused tests, pnpm verify:precommit, Drizzle schema checks, and the production Blade build pass.

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 5, 2026

Copy link
Copy Markdown

@DVidal1205 Review the changes in commit 712c74e0.


🧠 Learnings used
Learnt from: DVidal1205
Repo: KnightHacks/forge

Timestamp: 2026-09-05T23:32:16.861Z
Learning: For the Blade judging scores and deliberation feature, short-response visibility persists canonical hacker-sharing eligibility. A later hacker-facing view and delivery process consumes the eligibility setting; the judging slice does not directly display or deliver responses to project teams.
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@DVidal1205

Copy link
Copy Markdown
Contributor Author

@coderabbitai resolve

@coderabbitai

coderabbitai Bot commented Sep 5, 2026

Copy link
Copy Markdown
✅ Action performed

Comments resolved and changes approved.

@DVidal1205
DVidal1205 added this pull request to the merge queue Sep 6, 2026
Merged via the queue into main with commit ec5e26e Sep 6, 2026
13 checks passed
@DVidal1205
DVidal1205 deleted the blade/project-scoring branch September 6, 2026 02:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

API Change modifies code in the global API/tRPC package Blade Change modifies code in Blade app Database Change modifies code in the DB package Feature New Feature or Request Major Big change - 2+ reviewers required

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add configurable project scoring and judge deliberation

1 participant