feat(courts): expose verdict fields on the court-case API, whitelisted - #438
Conversation
The case-progress stepper needs to know whether a docket has been decided
and how, but `CourtCaseSerializer` stopped at the 12 fields it shipped with:
`verdict_type` and the verdict dates were added to the ORM projection in
migration 0003 — explicitly to make verdict "first-class, queryable, and
indexable" — and then never crossed the API boundary. So the frontend can
read `case_status` and hearings but not the promoted outcome.
Exposing the column raw would have been wrong. `verdict_type` is a plain
CharField with no DB constraint, and historic Supreme enrichment wrote raw
portal text into it. Measured on prod 2026-08-06: of 104,327 populated
`supreme` rows, 1,323 (1.27%, 37 distinct values) are not enum members —
242 bench referrals, 46 interlocutory orders, 312 unsplit compounds, 718
unmapped dispositions and 5 rows of punctuation. `special`, `patanhc` and
`kathmandudc` are clean, so this is Supreme-specific.
The referrals are the dangerous class, not the garbage: `पूर्ण इजलासमा पेस
हुने` ("to be presented to the full bench") reads like a disposition but
means the case is still live. Publishing it would tell a reader a pending
appeal was decided.
So `verdict_type` goes out through `cs.VERDICT_TYPES`; anything else
serialises as null, which is the honest claim — we hold no classified
verdict for that docket. The raw value stays in the database for the DQ
backfill to repair. Verified against all 53 distinct prod values: 103,004
publish, 1,323 null, no Devanagari leaks.
The dates need no guard — `verdict_date_bs` is shape-clean corpus-wide
(95,366 well-formed, 44,382 null, zero malformed), and every dirty
`verdict_type` row carries a NULL date, so a date can't assert a decision
the type guard just suppressed.
No migration: the columns already exist in Django's state.
Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Code review is billed via overage credits. To resume reviews, an organization admin can raise the monthly limit at claude.ai/admin-settings/claude-code.
Once credits are available, push a new commit or reopen this pull request to trigger a review.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThe change defines supported verdict types, exposes validated values through ChangesVerdict exposure
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 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 |
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
🛠️ Relevant configurations:
[config] enable_ai_metadata: False
custom_model_max_tokens: 200000
git_provider: github
output_relevant_configurations: True
model: openai/cx/gpt-5.5
ENABLE_AUTO_APPROVAL: True
custom_reasoning_model: False
fallback_models: ['openai/cx/gpt-5.4-mini']
is_auto_command: True
publish_output: True
publish_output_progress: True
progress_gif_url:
progress_gif_width: 48
verbosity_level: 0
use_extra_bad_extensions: False
log_level: DEBUG
use_wiki_settings_file: True
use_repo_settings_file: True
use_global_settings_file: True
extra_config_url:
disable_auto_feedback: False
ai_timeout: 120
response_language: en-US
repo_context_files: ['AGENTS.md']
repo_context_from_default_branch: True
repo_context_max_lines: 500
max_description_tokens: 500
max_commits_tokens: 500
max_model_tokens: 32000
model_token_count_estimate_factor: 0.3
patch_extension_skip_types: ['.md', '.txt']
allow_dynamic_context: True
max_extra_lines_before_dynamic_context: 10
patch_extra_lines_before: 5
patch_extra_lines_after: 1
cli_mode: False
large_patch_policy: clip
duplicate_prompt_examples: False
seed: -1
temperature: 0.2
ignore_pr_title: ['^\\[Auto\\]', '^Auto', '^Bump ', '^chore\\(deps\\)']
ignore_pr_target_branches: []
ignore_pr_source_branches: []
ignore_pr_labels: []
ignore_pr_authors: []
ignore_repositories: []
ignore_language_framework: []
restricted_mode: False
reasoning_effort: medium
enable_claude_extended_thinking: False
extended_thinking_budget_tokens: 2048
extended_thinking_max_output_tokens: 4096
claude_extended_thinking_models_override: []
extract_issue_from_branch: True
branch_issue_regex:
enable_custom_labels: False
[pr_reviewer] require_ticket_analysis_review: False
require_score_review: False
require_tests_review: True
require_estimate_effort_to_review: True
require_can_be_split_review: False
require_security_review: True
require_estimate_contribution_time_cost: False
require_todo_scan: False
publish_output_no_suggestions: True
persistent_comment: True
extra_instructions: Focus on: logic errors and edge cases; security/authz regressions; missing error handling;
Django/DRF correctness (migrations, N+1 queries, transaction/atomicity, serializer & permission gaps).
Do NOT comment on formatting, import order, or naming — ruff handles those in CI.
num_max_findings: 3
final_update_message: True
enable_review_labels_security: True
enable_review_labels_effort: True
require_all_thresholds_for_incremental_review: False
minimal_commits_for_incremental_review: 0
minimal_minutes_for_incremental_review: 0
enable_intro_text: True
enable_help_text: False
|
PR Code Suggestions ✨Explore these optional code suggestions:
🛠️ Relevant configurations:
[config] enable_ai_metadata: False
custom_model_max_tokens: 200000
git_provider: github
output_relevant_configurations: True
model: openai/cx/gpt-5.5
ENABLE_AUTO_APPROVAL: True
custom_reasoning_model: False
fallback_models: ['openai/cx/gpt-5.4-mini']
is_auto_command: True
publish_output: True
publish_output_progress: True
progress_gif_url:
progress_gif_width: 48
verbosity_level: 0
use_extra_bad_extensions: False
log_level: DEBUG
use_wiki_settings_file: True
use_repo_settings_file: True
use_global_settings_file: True
extra_config_url:
disable_auto_feedback: False
ai_timeout: 120
response_language: en-US
repo_context_files: ['AGENTS.md']
repo_context_from_default_branch: True
repo_context_max_lines: 500
max_description_tokens: 500
max_commits_tokens: 500
max_model_tokens: 32000
model_token_count_estimate_factor: 0.3
patch_extension_skip_types: ['.md', '.txt']
allow_dynamic_context: True
max_extra_lines_before_dynamic_context: 10
patch_extra_lines_before: 5
patch_extra_lines_after: 1
cli_mode: False
large_patch_policy: clip
duplicate_prompt_examples: False
seed: -1
temperature: 0.2
ignore_pr_title: ['^\\[Auto\\]', '^Auto', '^Bump ', '^chore\\(deps\\)']
ignore_pr_target_branches: []
ignore_pr_source_branches: []
ignore_pr_labels: []
ignore_pr_authors: []
ignore_repositories: []
ignore_language_framework: []
restricted_mode: False
reasoning_effort: medium
enable_claude_extended_thinking: False
extended_thinking_budget_tokens: 2048
extended_thinking_max_output_tokens: 4096
claude_extended_thinking_models_override: []
extract_issue_from_branch: True
branch_issue_regex:
enable_custom_labels: False
[pr_code_suggestions] commitable_code_suggestions: False
dual_publishing_score_threshold: -1
focus_only_on_problems: True
extra_instructions: Prefer a few high-impact, project-specific suggestions over many generic ones.
Skip style/formatting (ruff-enforced) and changes under cases/migrations/.
enable_help_text: False
enable_chat_text: False
persistent_comment: True
max_history_len: 4
publish_output_no_suggestions: True
suggestions_score_threshold: 0
new_score_mechanism: True
new_score_mechanism_th_high: 9
new_score_mechanism_th_medium: 7
auto_extended_mode: True
num_code_suggestions_per_chunk: 3
max_number_of_calls: 3
parallel_calls: True
final_clip_factor: 0.8
decouple_hunks: False
demand_code_suggestions_self_review: False
code_suggestions_self_review_text: **Author self-review**: I have reviewed the PR code suggestions, and addressed the relevant ones.
approve_pr_on_self_review: False
fold_suggestions_on_self_review: True
num_code_suggestions: 4
|
PR Agent Walkthrough 🤖Welcome to the PR Agent, an AI-powered tool for automated pull request analysis, feedback, suggestions and more. Here is a list of tools you can use to interact with the PR Agent:
(1) Note that each tool can be triggered automatically when a new PR is opened, or called manually by commenting on a PR. (2) Tools marked with [*] require additional parameters to be passed. For example, to invoke the |
|
Auto-approved PR |
User description
Why
Groundwork for the case-progress stepper (design doc): the public case page needs to show where a CIAA case sits on the CIAA charge → Special Court → appeal ladder, and that needs the docket's verdict.
CourtCaseSerializerexposed 12 fields and stopped there.verdict_type,verdict_date_bsandverdict_date_adwere added to the ORM projection in migration0003— the comment atcourts/models.py:84-90says the point was to make verdict "first-class, queryable, and indexable" — but they never crossed the API boundary. Today the frontend can readcase_statusand hearings, but not the promoted outcome.The catch:
verdict_typeis not a clean enumIt's a plain
CharFieldwith no DB constraint, and historic Supreme enrichment wrote raw portal text into it. Measured on prod 2026-08-06 — of 104,327 populatedsupremerows, 1,323 (1.27%) across 37 distinct values are not enum members:जारी(writ issued)बदर, उच्च अदालतमा पठाउनेपूर्ण इजलासमा पेस हुनेकैफियत प्रतिवेदन माग्ने।।।।।।।special,patanhcandkathmandudcare 100% clean, so this is Supreme-specific.The referrals are the dangerous class, not the garbage.
पूर्ण इजलासमा पेस हुने("to be presented to the full bench") reads like a disposition but means the case is still live. Publishing it verbatim would tell a reader that a pending appeal had been decided — on the exact cases where being wrong matters most.What this does
verdict_typegoes out through a newcs.VERDICT_TYPESfrozenset. Anything else serialises asnull, which is the honest claim: we hold no classified verdict for that docket. The raw value stays in the database for the DQ backfill to repair.The dates need no guard.
verdict_date_bsis shape-clean corpus-wide (95,366 well-formed / 44,382 null / zero malformed), and every dirtyverdict_typerow carries a NULL date — so a date can't assert a decision the type guard just suppressed.No migration. The columns are already in Django's state.
Verification
courts/tests+cases/tests— 594 passedtests/api/test_openapi_documentation.py— 8 passedruff checkandty checkcleanreturn value or Nonefails exactlytest_bench_referral_is_not_published_as_a_verdictandtest_scrape_garbage_is_not_publishedsupremevalues: 103,004 publish, 1,323 null, no Devanagari leaks, and every enum member seen on other courts is coveredtest_whitelist_covers_every_declared_constantguards the failure mode where someone adds a verdict constant and it's silently dropped on the wire.Reviewer notes
backfill_case_statusrun.verdict_judge(500-char free-text bench string) orcase_subject(untruncated TextField) — not needed for the stepper, and both widen the public surface for no gain.🤖 Generated with Claude Code
PR Type
Enhancement, Tests
Description
Expose court-case verdict fields
Whitelist public verdict types
Suppress raw Supreme scrape values
Add API coverage
Diagram Walkthrough
File Walkthrough
case_status.py
Add verdict type whitelistcourts/case_status.py
VERDICT_TYPESwhitelist.serializers.py
Expose filtered verdict fieldscourts/serializers.py
verdict_typeserializer method.None.test_api.py
Cover verdict API exposurecourts/tests/test_api.py
🛠️ Relevant configurations:
These are the relevant configurations for this tool:
[config]
[pr_description]
Summary by CodeRabbit
New Features
Bug Fixes