fix(contrarian): stop requiring every finding to carry a recommendation - #174
Conversation
…164) CHALLENGE_SCHEMA.findings.items.required drops 'recommendation', keeping it as a declared string property: a required fix proposal is prompt elaboration of the finding shape, not a control-flow need, and a published measurement tied that elaboration to rejecting correct code 26.2% -> 73.2% of the time. A required proposal also makes a finding cheaper to emit than to withhold, fighting these same prompts' stated acceptance condition that zero critical/major findings is the expected, unremarkable case. Widened from the issue body's two named prompt lines to three: all three "findings ARRAY" contrarian-prompt occurrences (consolidation gate, approach gate, plan gate) feed CHALLENGE_SCHEMA, so relaxing the schema without the consolidation line would leave that gate contradicting itself. Also: rewrites the now-false "one field looser" REVIEW_SCHEMA comment (the two item schemas require the same two fields as of this change, while `issues` staying out of REVIEW_SCHEMA.required and `id` never being in either schema remain true and load-bearing); cross-references #164 from the issue #162 NOTE block whose severity/summary-required, recommendation- optional description of parity only becomes literally true now; and updates all three persona anchors in templates/agents/contrarian.md (Core Principle, the Recommendation bullet, the anti-pattern list) to drop the unconditional fix-proposal demand while keeping the substantive-reasoning and concrete- failure-mode bar. templates/agents/contrarian.md and its engine-owned twin .claude/agents/contrarian.md are byte-identical before this change and deliberately drift after it: the twin is out of scope for this issue and is left untouched, deferred as a separate note. No render-site edits: all six 'f.recommendation || ''' sites already render undefined and '' identically, which is exactly the acceptance criterion. Tests: gate-findings.test.js gains a structural assertion (via harness.readGlobal) that CHALLENGE_SCHEMA.findings.items.required is exactly [severity, summary] and recommendation stays a declared property — needed because tests/harness.js stubs agent() and never validates opts.schema, so no harness test can otherwise exercise the real validator. contrarian- cap.test.js gains one recommendation-omitted case per gate (approach, plan) asserting byte-identical rendering at both the cap-out unresolved line and the prior iteration's re-evaluate/re-plan prompt, plus a findings-free case that locks in pre-existing behavior (findings was already outside CHALLENGE_SCHEMA's top-level required) rather than proving anything new.
…EMA parity claim Issue #164 made CHALLENGE_SCHEMA.findings.items and REVIEW_SCHEMA.issues.items require the same two fields (severity, summary), with recommendation optional on both — but gate-hygiene.md still documented the old asymmetry as deliberate design ("one field looser... a finding without a recommendation is an unfinished argument"). Rewrote "The typed shape, and why it's one field looser than CHALLENGE_SCHEMA" (heading included) to state the new parity, why the contrarian gate gave up its extra required field (a mandatory fix proposal is the prompt shape a published measurement tied to inflating rejection of correct work, and it fought these same prompts' stated acceptance condition that zero critical/major findings is expected), and that the reviewer-side half of the original reasoning survives unchanged. Notes the parity is schema-level only (ISSUES_ASK's prose still asks for a recommendation), that all three CHALLENGE_SCHEMA prompt lines were reworded (not just the two the issue body named), and that the absent-vs-empty-string distinction is invisible at all five challenge render sites — distinct from findingsBlock() on the REVIEW_SCHEMA side, which also prefixes the engine-assigned id. Also touched the :17 one-line reference so it no longer reads as reviewer-only now that the split is shared. Did not touch pipeline.md/metrics.md/failure-semantics.md (hash-frozen), AGENTS.md/CLAUDE.md (byte-identical freeze pair, neither row mentions this asymmetry), or index.md (gate-hygiene.md's row already exists). Refs #164
Spec Review (Iteration 1)Verdict: approved Verified PR #174 against issue #164 by diffing the PR branch against its actual base ( Acceptance criteria — all met
Scope note (flagged, not a defect)The issue body named two prompt lines ("approach-gate and plan-gate"); the PR reworded three, adding the consolidation-gate's identical "findings ARRAY" line. This was explicitly evaluated at the approach-contrarian gate (sound_with_caveats, iteration 2) and adopted as correct — the consolidation gate feeds the same Out-of-scope items correctly deferred, not scope creep
No scope creep found. No missing acceptance criteria. Recommend merge. |
Code Review (Iteration 1)Verdict: approved — no blocking, major, or minor findings. What I verified
Non-blocking observations (no action requested)
|
Tech Docsdocs_needed: yes — already done.
The section "The typed shape, and why it's one field looser than No other doc needed a change: No commit from this stage — the doc work and its commit predate this |
Implementation CompleteBranch
Deferred Suggestions for Follow-up
|
Closes #164
Summary
CHALLENGE_SCHEMA.findings.items.requiredpreviously forced every contrarian finding to includerecommendation— prompt elaboration that a published measurement tied to inflated rejection of correct work. This PR makesrecommendationoptional (still a declared property) while keepingseverityandsummaryrequired.Changes
workflows/ticketmill.js):CHALLENGE_SCHEMA.findings.items.requiredchanged from['severity','summary','recommendation']to['severity','summary']; source comment extended with the measurement rationale.templates/agents/contrarian.mdat all three relevant anchors (Core Principle, Recommendation bullet, anti-pattern list); the engine-owned twin.claude/agents/contrarian.mddeliberately left untouched (deferred, out of scope).f.recommendation || '', so omitted and empty already render identically — the acceptance criterion holds without touching them.tests/gate-findings.test.js(structural schema assertion) andtests/contrarian-cap.test.js(recommendation-omitted findings render byte-identically at both gates; plus a findings-free challenge case).docs/architecture/gate-hygiene.md's section documenting the retired asymmetry; added CHANGELOG.md entry.Key decisions
TASK_REVIEW_SCHEMA.suggested_improvements(a correctly-required yes/no flag) is untouched, as excluded by the issue.pipeline.md,metrics.md,failure-semantics.mduntouched (hash-frozen moved prose) per issue instructions..claude/agents/contrarian.mdleft as-is; drift fromtemplates/agents/contrarian.mdis deliberate and deferred.Token usage (approximate, this issue only): 256195 output tokens