Skip to content

feat(api): codify pydantic strict-mode policy as pytest invariant #120

@w7-mgfcode

Description

@w7-mgfcode

Context

PR #115 (closing #109) and PR #119 (closing #117) shipped the same bug class twice in 14 days: a Pydantic v2 `ConfigDict(strict=True)` request model with a JSON-non-native field type (`date`/`datetime`/`time`/`UUID`/`Decimal`) and no `Field(strict=False, ...)` override → every HTTP caller returns 422 with a `*_type` error.

The fix is mechanical (one-line `Field` override), but until now the policy was only enforced by a human reviewer reading `docs/_base/SECURITY.md` → "Pydantic v2 strict mode on FastAPI request bodies".

Goal

Codify the policy as a load-bearing pytest invariant (mirrors the `app/features/featuresets/tests/test_leakage.py` precedent) so the third instance of this regression class fails CI before it leaves a contributor's machine.

Scope

Per `PRPs/PRP-14-strict-mode-policy-linter.md`:

  • NEW `app/core/tests/test_strict_mode_policy.py` — single ~80-LOC AST-walker module (zero new deps; stdlib `ast` only)
  • M `docs/_base/SECURITY.md` — one-line "Enforced by:" cross-reference

Out of scope: `app/shared/seeder/config.py` (not request bodies — no `ConfigDict(strict=True)` anywhere), `app/features/agents/tools/*` (PydanticAI `@agent.tool` decorators, not `BaseModel`), response models, auto-fix mode, pre-commit hook variant.

Acceptance

  • The 4 existing strict-mode request models pass on commit one (no schema churn in the landing PR diff)
    • `featuresets/schemas.py::ComputeFeaturesRequest::cutoff_date`
    • `featuresets/schemas.py::PreviewFeaturesRequest::cutoff_date`
    • `forecasting/schemas.py::TrainRequest::train_start_date`
    • `forecasting/schemas.py::TrainRequest::train_end_date`
  • Baseline-guard test asserts the walker discovered all 4 expected models (defends against silent-pass on collection-zero)
  • Negative-fixture self-test reports a synthetic offending model (proves the walker)
  • `docs/_base/SECURITY.md` "Pydantic v2 strict mode on FastAPI request bodies" subsection ends with `Enforced by: \`app/core/tests/test_strict_mode_policy.py\``
  • All 5 blocking CI jobs green (Lint & Format, Type Check, Test, Migration Check, Schema Validation if applicable)

Precedent

Effort

4–6 focused hours per PRP §12 — one file, ~80 LOC, two test functions + one baseline guard.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions