Batch 2026-07-28-gate-hygiene-t2: 1 issue (Batch_2026-07-28_134922) - #189
Merged
Conversation
…x stages (#185) * feat(gate-findings): add rebuttal primitives for evaluator-fed fix stages (#167) Task 1 of #167: behaviour-neutral primitives that let a fixer treat a reviewer's finding as a hypothesis to verify rather than a command to obey. - FIX_SCHEMA.rebutted ({finding_id, evidence}), not in required — mirrors REVIEW_SCHEMA.issues staying out of its own required list. - FINDING_HYPOTHESIS_ASK beside HANDOFF_ASK/COMMIT_SHA_ASK: verify before acting, record disproof in `rebutted` if wrong, rebut only what you didn't fix, only bracketed-id findings are rebuttable, and a gate-agnostic consequence clause (no immediate-exit claim, since pr-review continues into another review round). - normalizeRebuttals(raw, findings): pure, fails toward today's behavior on every drop (non-array, blank finding_id/evidence, unrendered id); carries the matched finding's summary through. - contestedBlock(ctx): pure, renders rebutted-but-not-adjudicated findings back to the next reviewer with an inverted (vs settledBlock) closing contract, and states the iteration-2+ do-not-re-flag instruction doesn't apply to a contested entry. Never calls settleDecision(). Rendered immediately after settledBlock() at the quality/spec/code review prompts; emits '' until a later task populates ctx.contested. - retypeGateDisposition(ctx, gate, from, to): pure, moves one disposition count between buckets without touching count/severity; from === to is a documented count-preserving no-op, reachable at MAX_QUALITY_ITERATIONS. - ctx.metrics.rebuttal_only_rounds, plus tests/harness.js freshMetrics() parity. None of these are wired into the fix stages' control flow yet (task 2/3); FINDING_HYPOTHESIS_ASK and retypeGateDisposition are unused in production until then. 45 new unit tests in tests/gate-findings.test.js. node scripts/lint-engine.js --fix kept .claude/workflows/ticketmill.js in lockstep. node --test: 763/763 passing. * feat(gate-findings): wire rebuttal-only exits into the quality and test loops (#167) Renders FINDING_HYPOTHESIS_ASK in the quality-fix and test-quality-fix prompts only when the reviewer/validator named structured findings, and wires normalizeRebuttals()/retypeGateDisposition()/contestedBlock() (from task 1) into both evaluator-fed fix loops so a fix that rebuts every finding and applies none can never approve its own gate: - runQualityLoop: a third loop-exit flag `rebutted`, kept separate from `degraded` so the rolling degrade window is untouched, retypes the disposition already booked for that iteration to 'carried-unresolved', records the rebuttal as contested (not settled), and rolls up to exactly one VERIFY_SKIPS line per issue (ctx.quality_rebuttals / quality_rebuttal_skip_index), mirroring the existing cap roll-up. - runTestLoop: same predicate at test-quality-fix, exits through the loop's own { ok: true } path (never ok:false) so a rebuttal-only round can never route through the 'test-loop' merge-block stage key. No gate key to retype here (test-quality books none today). The two ORACLE-fed fix stages (test-fix, browser-fix) are untouched and keep their anti-rebuttal guards — a failing test is ground truth, not a hypothesis to verify. Extends tests/quality-loop.test.js and tests/test-loop.test.js with the rebuttal-only exit, partial-rebuttal/omitted-field/blank-evidence non-triggers, the rolled-up VERIFY_SKIPS line, and scope-pinning assertions proving FINDING_HYPOTHESIS_ASK renders only at the three evaluator-fed sites and never at test-fix. * feat(gate-findings): wire the pr-review merge gate's rebuttal-only continue Renders FINDING_HYPOTHESIS_ASK in the pr-fix prompt when either reviewer returned structured findings, and evaluates the rebuttalOnly predicate over the union of both reviewers' rendered finding sets. Unlike the quality and test loops (which exit their gate on a rebuttal-only round), pr-review CONTINUES: it retypes the iteration's disposition to carried-unresolved, carries the dispute into ctx.contested so the next iteration's reviewers see it, records one un-rolled Verification Gaps line, and moves on to the next review iteration instead of ending the gate. A per-call counter permits exactly one such round per issue; a second sets haltReason and falls through to the existing needs_human path, leaving the PR open. The `continue` precedes runQualityLoop so a rebuttal-only round (empty files_changed) never burns a quality gate against an untouched tree. Issue #167 * docs(gate-hygiene): document the rebuttal framing for evaluator-fed fix stages (#167) Add a "Rebuttal: a finding is a hypothesis, not a command" section to gate-hygiene.md covering what a rebuttal is and its evidence bar, why the framing is scoped to quality-fix/test-quality-fix/pr-fix and not test-fix/browser-fix, FIX_SCHEMA.rebutted being schema-wide but read at only three sites, normalizeRebuttals' fail-toward-today drops, the three per-gate exits and why only pr-review can block a merge, contestedBlock vs settledBlock's inverted contract, and rebuttal_only_rounds (a continuation, not an exit, on its first pr-review increment). Extend the gate-hygiene.md row in index.md's file map, and correct three now-stale passages in engine-internals.md that #167 falsifies: the carried-unresolved disposition enumeration (missing the rebuttal-only route), the "only findings_empty_exits distinguishes them" claim at pr-review, and findingsBlock's description of a rendered finding's job list. * fix(gate-findings): trim standing engine prose, close a disposition-tally gap - Move the #167 rebuttal-framing comment blocks (contestedBlock, FINDING_HYPOTHESIS_ASK, retypeGateDisposition, normalizeRebuttals, and the three in-loop rebuttalOnly explainers) out of workflows/ticketmill.js into one-sentence pointers at docs/architecture/gate-hygiene.md, where the same rationale already lives — clears lint-engine's 92% size warning. - reviewAndMerge()'s second (halting) rebuttal-only pr-fix round now calls retypeGateDisposition before breaking, so gate_findings['pr-review'] never leaves a leftover 're-litigated' count on the run a human is sent to inspect. - Reword gate-hygiene.md's normalizeRebuttals claim: it enforces only non-blankness, not the prompt's concrete-evidence bar. - Make contestedBlock's do-not-re-flag override site-agnostic so it also covers spec review's "stay consistent with prior reviews" instruction. - Correct the contestedBlock comment and gate-hygiene.md to state plainly that nothing closes a ctx.contested entry today — a known gap, not the "only a reviewer closes it" claim that didn't match the code. Fixes code review findings code-i1-1 through code-i1-5 on PR #185. * docs(gate-hygiene): correct rebuttal_only_rounds halt-round description [code-i2-1] The halting second pr-fix rebuttal-only round retypes the disposition and breaks without incrementing the counter (by design, pinned at tests/pr-review-gate.test.js:568-570), but the prose said the counter "bounds an issue to at most two rebuttal-only pr-fix rounds" without noting the halt itself is never counted. Clarify that the counter tracks continuations, not rounds, and that the halt is already carried by haltReason and the needs_human status.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Batch integration branch for the gate-hygiene tier 2 run. A human reviews and merges this; the engine never merges to the base branch itself.
Closes #167
What landed
#167 — A finding is a hypothesis at the evaluator-fed fix stages (PR #185)
The verify-then-adopt-or-rebut framing that the two contrarian revision stages already carried now reaches the three fix stages fed by a judge's opinion: quality-fix, test-quality-fix, and pr-fix. The two fed by a real failure, test-fix and browser-fix, are deliberately untouched, since telling a fixer that a failing test is a hypothesis inverts a correct guard.
FIX_SCHEMAgains an optionalrebutted: [{finding_id, evidence}], so a fixer who thinks a finding is wrong has somewhere to put that other than an error return.The enforcement is deterministic JS, not another prompt rule: a round that rebuts findings while changing no files and applying no fixes is a rebuttal-only round. Quality and test loops exit on one; the merge gate allows one and halts to
needs_humanon a second consecutive round, recordingcarried-unresolvedwith a Verification Gaps line rather than approving.Run note
This batch was launched for #167 and #168 and stopped by request partway through #168, which had reached its second plan iteration with no implementation commit. #168 is not in this PR. Its claim and worktree are intact and it resumes against this same batch branch.
Verification
Release commit
chore(release): v0.2.2was added by hand, since the run was stopped before the Report phase that normally writes it.