Skip to content

Add blueprint validation reports#380

Merged
txmed82 merged 1 commit into
masterfrom
codex/blueprint-validation
Jun 4, 2026
Merged

Add blueprint validation reports#380
txmed82 merged 1 commit into
masterfrom
codex/blueprint-validation

Conversation

@txmed82

@txmed82 txmed82 commented Jun 4, 2026

Copy link
Copy Markdown
Owner

Summary

  • Add deterministic BlueprintValidator for schema/plausibility/grounding/judge readiness tiers
  • Add casecrawler validate-blueprints to validate persisted blueprint artifacts and store BlueprintValidationReport rows
  • Add tests for grounded, ungrounded/unsupported, judge-validated, and CLI persisted validation paths

Tests

  • .venv/bin/python -m pytest tests/test_blueprint_validator.py tests/test_cli_synthetic.py::test_validate_blueprints_command_persists_validation_reports -q
  • .venv/bin/python -m ruff check src/casecrawler/validation/blueprints.py src/casecrawler/cli.py tests/test_blueprint_validator.py tests/test_cli_synthetic.py
  • .venv/bin/python -m pytest tests/test_blueprint_validator.py tests/test_blueprint_models.py tests/test_blueprint_storage.py tests/test_cli_synthetic.py -q
  • .venv/bin/python -m pytest -q -m "not optional_backend and not network and not slow"

Summary by CodeRabbit

  • New Features

    • Added validate-blueprints CLI command to validate clinical blueprint artifacts
    • Filter validation results by dataset ID and cohort plan ID
    • --no-grounding flag to make grounding citations optional
    • Validation reports generated with clinical plausibility assessments
  • Tests

    • Added comprehensive test coverage for blueprint validation

@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR introduces a blueprint validation system: a BlueprintValidator class that validates ClinicalBlueprint objects against clinical and schema requirements, computes validation issues and release readiness tiers, and a new validate-blueprints CLI command that applies validation to persisted blueprints and saves reports to storage.

Changes

Blueprint validation and CLI

Layer / File(s) Summary
BlueprintValidator implementation
src/casecrawler/validation/blueprints.py
BlueprintValidator accepts an optional require_grounding flag and validates blueprints by computing issues (missing patient age/sex, clinical reasoning targets, unsupported claims, missing citations), determining schema validity, clinical plausibility, grounding status, and judge validation, then deriving a ReleaseReadinessTier and returning a BlueprintValidationReport with all computed fields, issues, and judge reports.
BlueprintValidator unit tests
tests/test_blueprint_validator.py
Test helper _blueprint() constructs ClinicalBlueprint payloads with overrides; three unit tests verify grounded/clinically plausible validation, unsupported claims rejection, and judge report integration advancing the tier to RESEARCH_RELEASE_READY.
CLI command and integration test
src/casecrawler/cli.py, tests/test_cli_synthetic.py
validate-blueprints CLI command filters blueprints by dataset/cohort, validates each with BlueprintValidator, persists reports to DatasetStore, and prints summaries; integration test creates a dataset with a saved blueprint, runs the command, and verifies the validation report is persisted with expected fields. Synthetic test fixture _blueprint_cli_result updated to include citations.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • txmed82/case-crawler#371: The BlueprintValidator and validation report depend on the blueprint model types (ClinicalBlueprint, JudgeReport, BlueprintValidationReport, ReleaseReadinessTier) introduced in this PR.
  • txmed82/case-crawler#372: The validate-blueprints CLI command and validator integration depend on DatasetStore persistence methods for querying and saving blueprint validation reports.
  • txmed82/case-crawler#378: The synthetic test helper _blueprint_cli_result used in CLI tests was introduced in this PR; this PR updates it to include citations for validation.

🐰 Blueprints now pass the validator test,
Citations grounding claims with zest,
Tiers climb from schema to release-ready glow,
Clinical plausibility steals the show!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add blueprint validation reports' accurately captures the main change: introducing a BlueprintValidator class, a CLI validate-blueprints command, and supporting validation report functionality across the codebase.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/blueprint-validation

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

@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

🤖 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 `@src/casecrawler/validation/blueprints.py`:
- Around line 67-74: The check that adds a "clinical_reasoning_targets" issue
currently sets severity "warning" so a blueprint can still be marked
RESEARCH_RELEASE_READY despite this outstanding issue; update the validation in
blueprints.py to treat a missing clinical_reasoning_targets as a blocking issue
(set severity to "error" or otherwise add it to self.issues as an error) so
BlueprintValidationReport.research_release_ready (the property on
BlueprintValidationReport) will correctly block promotion; apply the same change
to the duplicate check around the other occurrence (the 98-109 block) so both
places create an error-level issue for blueprint.clinical_reasoning_targets.
- Around line 51-58: The check currently treats age==0 as missing because it
uses a falsy test on blueprint.patient.get("age"); update the conditional in the
validation (the block using blueprint.patient.get("age") that appends to issues)
to explicitly test for absence or None (e.g., check if "age" not in
blueprint.patient or blueprint.patient["age"] is None) so that age 0 is
considered present while still flagging missing/None values.
- Around line 93-96: The _grounded method currently returns True whenever
self._require_grounding is False, making ungrounded ClinicalBlueprints appear
grounded; change _grounded(self, blueprint: ClinicalBlueprint) to always compute
and return the factual grounding state based solely on
blueprint.evidence.citations (e.g., bool(blueprint.evidence.citations)) and stop
using self._require_grounding to override that result; use require_grounding
only to gate validation behavior elsewhere (e.g., raise or skip validation in
the validator flow), so persisted reports and tier calculations rely on the
factual _grounded value rather than a policy flag.
- Around line 22-37: The code currently hard-codes schema_valid=True; change it
to derive schema_valid from the collected issues: set schema_valid = not
any(issue["severity"] == "error" for issue in issues) so any structural/schema
error (e.g., missing required patient fields) flips schema_valid to False.
Update the block that computes schema_valid alongside issues, grounded,
clinically_plausible and tier (references: variables schema_valid, issues,
grounded, clinically_plausible, judge_validated and the _issues/_grounded/_tier
helpers) to use this computed value.
🪄 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 Plus

Run ID: c40e576f-1436-4648-a9d1-d01314465cc8

📥 Commits

Reviewing files that changed from the base of the PR and between 7eb4d95 and e023523.

📒 Files selected for processing (4)
  • src/casecrawler/cli.py
  • src/casecrawler/validation/blueprints.py
  • tests/test_blueprint_validator.py
  • tests/test_cli_synthetic.py

Comment on lines +22 to +37
issues = self._issues(blueprint)
schema_valid = True
grounded = self._grounded(blueprint)
clinically_plausible = not any(
issue["severity"] == "error" and issue["field"] != "evidence.citations"
for issue in issues
)
if self._require_grounding and not grounded:
clinically_plausible = False
judge_validated = bool(judges) and all(report.passed for report in judges)
tier = self._tier(
schema_valid=schema_valid,
clinically_plausible=clinically_plausible,
grounded=grounded,
judge_validated=judge_validated,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Compute schema_valid from the collected issues instead of hard-coding it.

Right now any blueprint with missing required patient fields still comes back with schema_valid=True, so ReleaseReadinessTier.DRAFT is unreachable and a structurally incomplete blueprint can still be labeled SCHEMA_VALID.

Suggested direction
         issues = self._issues(blueprint)
-        schema_valid = True
+        schema_valid = not any(
+            issue["severity"] == "error"
+            and issue["field"] in {"patient.age", "patient.sex"}
+            for issue in issues
+        )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
issues = self._issues(blueprint)
schema_valid = True
grounded = self._grounded(blueprint)
clinically_plausible = not any(
issue["severity"] == "error" and issue["field"] != "evidence.citations"
for issue in issues
)
if self._require_grounding and not grounded:
clinically_plausible = False
judge_validated = bool(judges) and all(report.passed for report in judges)
tier = self._tier(
schema_valid=schema_valid,
clinically_plausible=clinically_plausible,
grounded=grounded,
judge_validated=judge_validated,
)
issues = self._issues(blueprint)
schema_valid = not any(
issue["severity"] == "error"
and issue["field"] in {"patient.age", "patient.sex"}
for issue in issues
)
grounded = self._grounded(blueprint)
clinically_plausible = not any(
issue["severity"] == "error" and issue["field"] != "evidence.citations"
for issue in issues
)
if self._require_grounding and not grounded:
clinically_plausible = False
judge_validated = bool(judges) and all(report.passed for report in judges)
tier = self._tier(
schema_valid=schema_valid,
clinically_plausible=clinically_plausible,
grounded=grounded,
judge_validated=judge_validated,
)
🤖 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 `@src/casecrawler/validation/blueprints.py` around lines 22 - 37, The code
currently hard-codes schema_valid=True; change it to derive schema_valid from
the collected issues: set schema_valid = not any(issue["severity"] == "error"
for issue in issues) so any structural/schema error (e.g., missing required
patient fields) flips schema_valid to False. Update the block that computes
schema_valid alongside issues, grounded, clinically_plausible and tier
(references: variables schema_valid, issues, grounded, clinically_plausible,
judge_validated and the _issues/_grounded/_tier helpers) to use this computed
value.

Comment on lines +51 to +58
if not blueprint.patient.get("age"):
issues.append(
{
"severity": "error",
"field": "patient.age",
"message": "Blueprint patient must include age.",
}
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Treat age 0 as present.

Line 51 flags newborn blueprints as missing patient.age because 0 is falsy.

Suggested fix
-        if not blueprint.patient.get("age"):
+        if "age" not in blueprint.patient or blueprint.patient.get("age") is None:
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if not blueprint.patient.get("age"):
issues.append(
{
"severity": "error",
"field": "patient.age",
"message": "Blueprint patient must include age.",
}
)
if "age" not in blueprint.patient or blueprint.patient.get("age") is None:
issues.append(
{
"severity": "error",
"field": "patient.age",
"message": "Blueprint patient must include age.",
}
)
🤖 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 `@src/casecrawler/validation/blueprints.py` around lines 51 - 58, The check
currently treats age==0 as missing because it uses a falsy test on
blueprint.patient.get("age"); update the conditional in the validation (the
block using blueprint.patient.get("age") that appends to issues) to explicitly
test for absence or None (e.g., check if "age" not in blueprint.patient or
blueprint.patient["age"] is None) so that age 0 is considered present while
still flagging missing/None values.

Comment on lines +67 to +74
if not blueprint.clinical_reasoning_targets:
issues.append(
{
"severity": "warning",
"field": "clinical_reasoning_targets",
"message": "Blueprint has no explicit reasoning target.",
}
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't promote reports with outstanding issues to RESEARCH_RELEASE_READY.

A blueprint with only the clinical_reasoning_targets warning can still reach RESEARCH_RELEASE_READY after judge approval, but BlueprintValidationReport.research_release_ready in src/casecrawler/models/blueprint.py:214-225 requires not self.issues. That makes tier and the report's own readiness property disagree.

Also applies to: 98-109

🤖 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 `@src/casecrawler/validation/blueprints.py` around lines 67 - 74, The check
that adds a "clinical_reasoning_targets" issue currently sets severity "warning"
so a blueprint can still be marked RESEARCH_RELEASE_READY despite this
outstanding issue; update the validation in blueprints.py to treat a missing
clinical_reasoning_targets as a blocking issue (set severity to "error" or
otherwise add it to self.issues as an error) so
BlueprintValidationReport.research_release_ready (the property on
BlueprintValidationReport) will correctly block promotion; apply the same change
to the duplicate check around the other occurrence (the 98-109 block) so both
places create an error-level issue for blueprint.clinical_reasoning_targets.

Comment on lines +93 to +96
def _grounded(self, blueprint: ClinicalBlueprint) -> bool:
if not self._require_grounding:
return True
return bool(blueprint.evidence.citations)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep grounded as an observed fact, not a policy override.

When require_grounding=False, _grounded() returns True even if evidence.citations is empty. That makes ungrounded blueprints indistinguishable from genuinely grounded ones in persisted reports and can incorrectly inflate the tier.

Suggested fix
     def _grounded(self, blueprint: ClinicalBlueprint) -> bool:
-        if not self._require_grounding:
-            return True
         return bool(blueprint.evidence.citations)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def _grounded(self, blueprint: ClinicalBlueprint) -> bool:
if not self._require_grounding:
return True
return bool(blueprint.evidence.citations)
def _grounded(self, blueprint: ClinicalBlueprint) -> bool:
return bool(blueprint.evidence.citations)
🤖 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 `@src/casecrawler/validation/blueprints.py` around lines 93 - 96, The _grounded
method currently returns True whenever self._require_grounding is False, making
ungrounded ClinicalBlueprints appear grounded; change _grounded(self, blueprint:
ClinicalBlueprint) to always compute and return the factual grounding state
based solely on blueprint.evidence.citations (e.g.,
bool(blueprint.evidence.citations)) and stop using self._require_grounding to
override that result; use require_grounding only to gate validation behavior
elsewhere (e.g., raise or skip validation in the validator flow), so persisted
reports and tier calculations rely on the factual _grounded value rather than a
policy flag.

@txmed82 txmed82 merged commit 53b5ef3 into master Jun 4, 2026
4 checks passed
@txmed82 txmed82 deleted the codex/blueprint-validation branch June 4, 2026 01:20
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