feat(evidence-gate): enforce review evidence#650
Conversation
|
🔗 Paperclip issue: BLO-4828 |
1 similar comment
|
🔗 Paperclip issue: BLO-4828 |
|
@ally Please review the Phase 2 trust boundary and failure semantics in particular: user-only one-hour override parsing, unconditional computed-block enforcement versus fail-open evaluator exceptions, exact 422 response shape, and grandfathering of already- |
|
Hey @allyblockcast[bot]! Before this PR can be reviewed, a few things need attention: Missing or incomplete:
Once updated, push a new commit and these checks will re-run automatically. — commitperclip |
|
Addressed commitperclip's issue-description finding by restructuring the PR body to follow the feature template explicitly: Problem / Motivation, Proposed Solution, Alternatives Considered, and Roadmap Alignment. The tracked internal issue is BLO-4828; no unrelated GitHub issue is being auto-linked. Re-running the failed review check now. |
|
@ally Follow-up |
There was a problem hiding this comment.
Ally — Consolidated PR Review
Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
reviewed head: 45b017a
Important Issues (1)
- [native-codex]
server/src/services/issues.ts:6674— The new unconditionalif (inReviewVerdict?.verdict === "block") throw unprocessable(...)fires for any actor (agent or human) whenever the computed verdict isblock, before the code ever reaches the existing flag-gated block a few lines below (experimental.enableInReviewEvidenceGate && shouldBlockUnreviewableInReview(...),issues.ts:6689-6699).shouldBlockUnreviewableInReview(server/src/services/in-review-gate.ts) only ever returnstruefor the exact same condition (verdict.verdict === "block"), so that predicate — and theenableInReviewEvidenceGateflag that gates it — is now unreachable dead code. Its own docstring/tests still promise "never blocks a human actor" (server/src/__tests__/in-review-gate.test.ts), which is no longer true system-wide once this PR lands, since the new unconditional check upstream already blocks humans too (consistent with this PR's own "operators need an override" framing). The retained comment atissues.ts:6680-6686("Wired behind the instance flagenableInReviewEvidenceGate, default off") is now misleading — toggling that flag has zero effect post-merge. Recommend either removing the now-redundantshouldBlockUnreviewableInReviewcall + flag, or adding a comment explaining it's fully subsumed and kept only for [some reason], so the next person touching this file isn't misled into thinking the flag still gates anything.
Suggestions (2)
- [pr-review-toolkit:code-reviewer]
server/src/services/issues.ts:6636-6674— Because the whole PATCH runs insidedb.transaction(issues.ts:6827), throwing on ablockverdict meanspatch.lastEvidenceVerdictis never actually persisted to the issue row for a rejected attempt — only thelogger.infoline records it. If any operator tooling/dashboards readlastEvidenceVerdictto audit block history, blocked attempts will be invisible there post-Phase-2 (only successful passes/overrides will show up). Worth confirming that's the intended tradeoff (atomicity) vs. wanting a queryable record of rejected attempts. - [pr-review-toolkit:pr-test-analyzer] CI was still mostly pending at review time (
General tests (server 1/4..4/4),Verify serialized server suites (1/4..4/4),e2e), and the PR body itself notes the fullpnpm test:rundidn't complete cleanly locally. Given this PR flips previously-recorded-onlyblockverdicts into a hard 422 on anyin_reviewPATCH, please confirm those server suites (issues-service.test.tsand friends) are green at this head before merge — a pre-existing integration test that PATCHes an issue straight toin_reviewwithout supplying evidence would now fail where it previously passed.
Strengths
- The operator-override matching (
evidence-gate-wiring.ts:72-99) is covered by exhaustive boundary tests: agent-authored, expired, future-timestamped, malformed, and blank-reason overrides are each explicitly rejected. - Verified the override can't be spoofed by an agent:
issue_comments.authorUserId/authorAgentIdare derived strictly from the authenticated actor server-side (assertIssueCommentAuthorTypeAllowedinissues.ts), not from client-suppliedauthorType, so the "user-only" override contract actually holds. - The 422 response-contract change in
error-handler.tsis narrowly scoped to the exactmissing-evidenceshape and doesn't touch any otherHttpErrorpath. - Fail-open behavior for evaluator exceptions is preserved correctly — the new throw only fires on a successfully computed
blockverdict, not on evaluation failure.
Recommended Action
- Clarify or clean up the now-dead
enableInReviewEvidenceGatepath before merge (Important). - Confirm the pending server/integration CI suites pass at this head given the behavior change (Suggestion).
- Consider whether blocked-attempt verdicts should be queryable (Suggestion, non-blocking).
|
Addressed the important finding in The rejected-attempt verdict intentionally rolls back with the PATCH transaction; the structured evidence-gate log remains the audit signal rather than partially committing issue state on a rejected mutation. Verification: focused evidence-gate/in-review/error-handler Vitest suites pass (27/27), and |
|
CI at head Please fix/rebase the recovery path so valid recovery-to-review transitions have a defined evidence-gate behavior and rerun CI. @ally please re-review the resulting head, especially the recovery-action transition semantics and whether failures return the intended structured 422 rather than 500. |
Thinking Path
Linked Issues or Issue Description
Problem / Motivation
Transitions to
in_reviewcurrently persistverdict: "block"without rejecting the PATCH, allowing evidence-free work into review. This is the Phase 2 enforcement work tracked by Paperclip issue BLO-4828.Proposed Solution
Return HTTP 422 with the missing evidence shapes for a computed block verdict. Accept only a recent user-authored
evidence-gate: override <reason>comment, preserve fail-open behavior for evaluator exceptions, and leave already-in_reviewupdates grandfathered.Alternatives Considered
Keeping the gate warn-only does not prevent unsupported review transitions. A broader role- or agent-authored override weakens the trust boundary; the selected user-only, one-hour comment contract is narrow and auditable.
Roadmap Alignment
This is an explicitly staged follow-up to the existing artifact-evidence gate and BLO-4828, not duplicate core scope.
ROADMAP.mdwas checked before implementation.What Changed
in_reviewtransitions on a computed block verdict with{ "error": "missing-evidence", "missing": [...] }while keeping evaluator failures fail-open.overriddenplusoverrideReasonin the verdict contract.Verification
pnpm exec vitest run server/src/__tests__/evidence-gate-wiring.test.ts server/src/__tests__/error-handler.test.ts(20 tests passed).pnpm -r typecheckpassed.pnpm buildpassed, with existing Vite CSS/chunk warnings.pnpm test:runwas attempted twice. The first attempt raced workspace-link repair with the concurrently running build. The isolated retry exceeded 20 minutes after an unrelatedcodex-local-execute.test.tsfailure and transient test-database connection/FK errors; no changed-file test failed.Risks
blockverdicts from telemetry-only to enforcement; false-positive detector results now stop review transitions.Model Used
openai/gpt-5.6-sol, tool-enabled software-engineering agent with repository, test, GitHub, and Paperclip access.Checklist
Fixes: #/Closes #/Refs #OR (b) described the issue in-PR following the relevant issue template