fix(models): reject empty text/name/title on Claim/Entity/Page#300
fix(models): reject empty text/name/title on Claim/Entity/Page#300minion1227 wants to merge 2 commits into
Conversation
the non-empty contract for `Claim.text`, `Entity.name`, and `Page.title` lived only in the `propose_*` helpers, so `store.put_*`, `store.update_*`, and bundle/sync import (via `_validate_content`) all silently accepted empty or whitespace-only values and landed artifacts carrying zero of the field's semantic content. add `@field_validator`s on each field, mirroring the existing `Claim.evidence` min-citation validator (vouchdev#81/vouchdev#82): they reject blank input at construction time, so every write path — direct construction, put/update, and import — inherits the check at once. the validators gate on emptiness only and preserve surrounding whitespace of non-blank values. `Source.locator` is deliberately left out of scope — its format question is bigger and deserves its own issue, as the report notes. closes vouchdev#155
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
✅ Files skipped from review due to trivial changes (1)
📝 WalkthroughWalkthroughAdds model-level validators that reject empty or whitespace-only ChangesModel-layer non-empty validation
Estimated code review effort: 2 (Simple) | ~15 minutes Possibly related issues
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 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: 1
🧹 Nitpick comments (1)
src/vouch/models.py (1)
235-245: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider extracting shared non-empty validator helper.
_text_non_empty,_name_non_empty, and_title_non_emptyare structurally identical (strip-check + raiseValueError). A small shared helper (e.g._require_non_empty(v: str, field_label: str) -> str) called from each@field_validatorwould remove the duplication while keeping the per-field error messages.♻️ Proposed refactor
+def _require_non_empty(v: str, label: str) -> str: + if not v.strip(): + raise ValueError(f"{label} must not be empty") + return v + + class Claim(BaseModel): ... `@field_validator`("text") `@classmethod` def _text_non_empty(cls, v: str) -> str: - if not v.strip(): - raise ValueError("claim text must not be empty") - return v + return _require_non_empty(v, "claim text")Apply the same pattern to
Entity._name_non_emptyandPage._title_non_empty.Also applies to: 281-288, 338-345
🤖 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/vouch/models.py` around lines 235 - 245, Extract the duplicated strip-and-empty validation logic in the model validators into a shared helper such as _require_non_empty(v, field_label), then have _text_non_empty, Entity._name_non_empty, and Page._title_non_empty call it so each field keeps its own error message without repeating the same check. Keep the existing field validators in place and replace the repeated if not v.strip(): raise ValueError(...) blocks with the shared helper to reduce duplication across the affected validators.
🤖 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 `@CHANGELOG.md`:
- Line 99: Capitalize the sentence start in the changelog entry so the text
after the period begins with “The” instead of “the”. Update the affected
CHANGELOG.md entry only, keeping the rest of the release note unchanged.
---
Nitpick comments:
In `@src/vouch/models.py`:
- Around line 235-245: Extract the duplicated strip-and-empty validation logic
in the model validators into a shared helper such as _require_non_empty(v,
field_label), then have _text_non_empty, Entity._name_non_empty, and
Page._title_non_empty call it so each field keeps its own error message without
repeating the same check. Keep the existing field validators in place and
replace the repeated if not v.strip(): raise ValueError(...) blocks with the
shared helper to reduce duplication across the affected validators.
🪄 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: dac51ccc-7071-47be-a22f-7902ad39d843
📒 Files selected for processing (3)
CHANGELOG.mdsrc/vouch/models.pytests/test_storage.py
address review on vouchdev#300: the three vouchdev#155 field validators (Claim.text / Entity.name / Page.title) were structurally identical strip-checks. extract a module-level `_require_non_empty(v, label)` helper they all delegate to, keeping the per-field error messages. also fix a mid-entry sentence casing in the CHANGELOG. no behaviour change.
|
thanks — addressed both in 89815c0:
no behaviour change; tests and assertions unchanged and green. |
What changed
Claim.text,Entity.name, andPage.titlenow reject empty /whitespace-only values at the model layer, via
@field_validators thatmirror the existing
Claim.evidencemin-citation validator (#81/#82).Why
the non-empty contract for these fields lived only in the
propose_*helpers, so three other write paths slipped through:
Claim(text=""),Entity(name=""),Page(title="")were all accepted, andstore.put_*landed theYAML / markdown on disk.
bundle._validate_contentcalls<Model>.model_validate(...), which had no text-content validator, so amanifest-consistent bundle with blank fields imported cleanly.
claim.text = ""; store.update_claim(claim)re-validates via
model_validate, but with no validator the blankpersisted.
enforcing on the model closes all three at once — exactly the
contract-vs-enforcement asymmetry that #81/#82 fixed for
Claim.evidence.closes #155.
What might break
nothing on disk or on the wire. this only rejects artifacts that were never
semantically valid (blank text / name / title). existing well-formed KBs are
unaffected. the validators gate on emptiness only and preserve the
surrounding whitespace of non-blank values (no silent stripping). a KB that
somehow already holds a blank-field artifact will surface it as a
validation error on next load — which is the intended signal, matching how
the #81 evidence validator behaves.
Source.locatoris intentionally out of scope (the issue flags itsformat question as bigger and deserving its own issue).
VEP
not a surface change — a model-layer validation tightening in the same shape
already merged for
Claim.evidence. no VEP needed.Tests
make check— ruff clean; mypy clean on touched files; pytest green(the only local failures are pre-existing and environment-only:
fastapi/[web]not installed and embeddings installed, both ofwhich also fail on
main)tests/test_storage.py(alongside thebug: Claim model has no min-evidence validator — uncited claims land via bundle import, put_claim, update_claim #81 evidence-validator tests): model-construction rejection
(empty + whitespace, parametrised),
put_*rejection withno-file-landed assertions,
update_claimmutate-then-persistrejection, and a whitespace-preservation guard for non-blank values
CHANGELOG.mdupdated under## [Unreleased]Summary by CodeRabbit