Skip to content

reason_code vocabulary leaks a 13th value through an untyped public field #428

Description

@guangyu-reflexio

Found during review of #426. Pre-existing.

review_service.py emits reason_code="evidence_unavailable" into ReviewUserPlaybookResult.reason_code, which is declared as a plain str in models/api_schema/domain/entities.py — not the Literal used by CandidateReviewDecision.

So the public vocabulary is effectively Literal ∪ {evidence_unavailable}, enforced nowhere and documented nowhere.

Backward-compatible in the strict sense (the field was never an enum), but:

  • API consumers branching on the value silently gain unhandled cases whenever the reviewer vocabulary grows. feat(reviewer): reject playbook entries about systems the agent does not drive #426 added not_agent_decision with no OpenAPI signal.
  • The taxonomy itself is not MECE — unsupported_evidence is a genus and absence_inference, unsupported_causality, unseen_artifact, speculative, internal_status are all species of it. Measured consequence: six near-identical candidates asserting the same defect drew four different reject codes, plus a revise, plus an accept.
  • Three codes (speculative, redundant, internal_status) have no numbered check defining when they apply, so the model picks among overlapping labels with no tie-breaker.

Worth deciding whether the public field should be typed, and separately whether the codes need a strict precedence order (most specific species first, unsupported_evidence as the catch-all genus last). The prompt already does this for checks 7–8 — "Checks 7-8 only" — and that is the one place labelling is consistent.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions