Skip to content

Lock the one-verdict contract: one canonical verdict enum + projection guards#145

Merged
pengfei-threemoonslab merged 1 commit into
mainfrom
feat/verdict-contract-lock
May 30, 2026
Merged

Lock the one-verdict contract: one canonical verdict enum + projection guards#145
pengfei-threemoonslab merged 1 commit into
mainfrom
feat/verdict-contract-lock

Conversation

@pengfei-threemoonslab
Copy link
Copy Markdown
Contributor

What

Locks the "one decision engine" discipline structurally. The verify cycle (M1–M3, #139#143) already computes a single release verdict in build_release_decision() and projects it onto the report summaries and the agent-facing merge_verdict — but nothing enforced that the projections couldn't drift. This converts the documented discipline into types + tests, with zero behavior or wire-format change.

Why

verifier.json's merge_verdict and release_decision.decision are two vocabularies bridged by a hand-maintained mapping, and the verdict enum was hand-respelled in four places. A drift between them means two parts of the system disagree about whether a PR can merge — the exact failure this product exists to prevent.

Changes

  • One canonical enum. ReleaseDecisionStatus now lives once in schemas/common.py; AgentSummary.verdict, ReviewerSummary.verdict, VerifierVerdict, and ReleaseConsequence.decision reuse it (were four inline Literal respellings). Generated schemas are byte-identicalgenerate_schemas.py --check is clean, no report_schema_version bump.
  • Total, drift-proof projection. _DECISION_TO_VERDICT is typed dict[ReleaseDecisionStatus, MergeVerdict]; a totality test fails CI if a release status lacks an explicit mapping (no silent human_review_required fallback for a known status).
  • Structural consistency lock. A VerifierArtifact model_validator makes it impossible to construct an artifact whose merge_verdict / decision disagree with release_decision.decision.
  • One no-decision authority. merge_verdict_for() centralizes the skipped/failed fallback; the divergent inline _merge_verdict in the orchestrator is removed (summaries defaulted to passed, verify defaulted to mergeable/unknown — now one rule).
  • Contract tests in tests/test_verdict_contract.py.

Verification

  • Full suite: 2302 passed, 4 skipped, 0 failed
  • python scripts/generate_schemas.py --check: clean (no docs/ schema changes)
  • ruff check: clean
  • Net diff removes duplication: +97 / −42 across 5 files + 1 new test.

🤖 Generated with Claude Code

…n guards

The verify cycle (M1-M3) already computes one release verdict in
build_release_decision() and projects it onto the report summaries and the
agent-facing merge_verdict, but the discipline was enforced only by
convention and docstrings. This makes it structural.

- Define ReleaseDecisionStatus once in schemas/common.py and reuse it for
  AgentSummary.verdict, ReviewerSummary.verdict, VerifierVerdict, and
  ReleaseConsequence.decision (previously four hand-respelled Literals of
  the same vocabulary). Generated JSON schemas are byte-identical
  (generate_schemas.py --check clean) - no wire change, no schema bump.
- Type _DECISION_TO_VERDICT as dict[ReleaseDecisionStatus, MergeVerdict]
  and add a totality test, so a new release status without a mapping fails
  CI instead of silently falling back to human_review_required.
- Add a VerifierArtifact model_validator: when a head release_decision is
  present, merge_verdict and the decision copy MUST be exact projections of
  it - an inconsistent artifact is impossible to construct.
- Centralize the no-decision verdict rule in merge_verdict_for(); delete the
  divergent inline _merge_verdict in the orchestrator (summaries defaulted
  to "passed", verify defaulted to "mergeable"/"unknown" - now one rule).
- Add tests/test_verdict_contract.py pinning canonical-enum reuse across all
  verdict surfaces, projection totality + the exact table, the fail-safe
  (unknown status never auto-passes), and the validator.

Full suite: 2302 passed, 4 skipped. Behavior unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@pengfei-threemoonslab pengfei-threemoonslab merged commit c26a207 into main May 30, 2026
1 check passed
@pengfei-threemoonslab pengfei-threemoonslab deleted the feat/verdict-contract-lock branch May 30, 2026 06:32
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