Status: design (2026-06-08)
Issue: #262 (Kong et al. 2026 META #255, Tier B)
Target agent: deep-research/agents/synthesis_agent.md
Paper anchor: Kong et al. 2026 (arXiv:2605.18661, L. Kong, "Roadmap & User Guide") §7.4.2 — "systems increasingly retrieve and summarize individual papers well, but still struggle with multi-paper relational reasoning, methodological lineage, and cross-paper contradictions."
Sibling precedent: #261 (figure/table fidelity, prose-layer) and #214 (synthesis sub-claim inventory, prose-layer) — both shipped prose-layer-only because producer and consumer are LLM agents, not a deterministic parser.
synthesis_agent (Phase 3, Analysis) already has prose-level contradiction handling:
What is not yet present, per Kong §7.4.2:
(A calibration reference is discussed later as an optional, non-shipping sanity-check — it is not a deliverable of this change.)
This is the cross-paper-relational sibling of #214 (which decomposed within-review sub-claims) and #261 (figure fidelity). All three enumerate before asserting "addressed."
The issue's acceptance text overclaims, and following it literally would manufacture the exact "false rigor" the repo rejected in #261. This section records why we depart from the issue wording and ship the lighter form.
The issue acceptance says "Synthesis output schema adds contradiction_pairs[] block" and the rationale invokes "downstream agents (formatter, integrity_verification) cannot programmatically check." Read literally that points to a JSON Schema + lint invariant + CI gold gate. But:
formatter_agent, integrity_verification_agent — are themselves LLM agents reading a markdown report. There is no deterministic non-LLM parser that consumes a contradiction block.paper_a is a non-empty string; it would not validate that Paper A actually contradicts Paper B. That is field-presence rigor masquerading as fidelity rigor.This is the same scope call #261 made — its rationale was that machine-validating the YAML shape proves field presence, not fidelity, and adds an unrequested maintenance surface, whereas #213 earned schema+lint only because it has a deterministic machine consumer. #262 has no machine consumer either.
Decision: prose-layer-only. Extend the existing synthesis_agent contradiction section with a YAML-shaped markdown inventory block + a scoping subsection + a worked examples/ file. No JSON Schema, no lint invariant, no CI gold fixture. Vocabulary is aligned with the issue (contradiction, conditional_difference) to avoid drift, but no schema architecture is imported.
The departures from the issue's literal acceptance, to be stated plainly in the PR body:
| Issue acceptance (literal) | What ships | Why |
|---|---|---|
"Synthesis output schema adds contradiction_pairs[] block" |
YAML-shaped markdown block in the existing Contradictions section | No deterministic consumer; schema = false rigor (#261 precedent) |
| "Calibration gold set … accuracy ≥ 0.75" as acceptance | No calibration artifact ships; any future/manual calibration is recorded out-of-band (model/date/prompt + confusion matrix + per-class recall) and stays non-blocking | 20 LLM-judged pairs is too small and too nondeterministic across runs to be a hard CI gate; wiring it as one would be false precision |
The issue's draft field model has four defects, all carried into this corrected model:
conflict_type mixed two orthogonal axes. The issue enum {contradictory, conditional_difference, resolved_in_synthesis} folds a conflict nature (contradictory, conditional_difference) together with a resolution status (resolved_in_synthesis). These are separated into two fields.insufficient_overlap was referenced but not in the enum (the resolution_in_manuscript required-when clause names it). It is now a first-class pair_assessment value.a_finding / b_finding as free text invites the agent to state claims the papers do not make. Each side gains an evidence pointer grounded in the corpus context.candidate_basis was implicit. Recording why a pair was checked makes the scan's coverage inspectable (and exposes same-camp bias — see scoping).The shipped block (markdown, inside the existing Contradictions & Resolutions section):
cross_paper_tensions:
- pair_id: CP-001 # synthesizer-assigned, stable within the synthesis
paper_a: "<citation_key or ref slug>"
paper_b: "<citation_key or ref slug>"
candidate_basis: "shared RQ subtopic | shared construct/outcome/measure | opposite finding direction | bibliographic coupling | scholar flag | agent-noted cross-cluster"
overlap_topic: "..."
a_finding: "..."
a_evidence_pointer: "..." # where in the corpus context Paper A's finding is grounded
b_finding: "..."
b_evidence_pointer: "..."
pair_assessment: "contradiction | conditional_difference | no_material_conflict | insufficient_overlap"
resolution_status: "resolved_in_synthesis | flagged_unresolved | not_applicable"
resolution_pointer: "Synthesis Report > Contradictions & Resolutions, ¶N" # required when resolution_status == resolved_in_synthesis
scholar_confirmation: "pending | confirmed | disputed" # scholar-set; agent emits 'pending'
Field rules:
pair_assessment and resolution_status are orthogonal. A pair can be conditional_difference + resolved_in_synthesis, or contradiction + flagged_unresolved. The two axes never collapse into one enum.resolution_pointer is required iff resolution_status == resolved_in_synthesis — a claimed resolution must point at where in the report it was resolved, so the scholar can confirm it. For flagged_unresolved / not_applicable the pointer is omitted.no_material_conflict and insufficient_overlap pairs MAY be listed but are not obligations to resolve. Listing a checked-but-clear pair documents coverage; it does not manufacture a contradiction. (This mirrors #214's not-mentioned is NOT opposition discipline — a checked pair that is clear is not a tension.)scholar_confirmation is scholar-set. The agent always emits pending; it never self-confirms (narrative-side / audit-side separation, below).The issue framed scoping as "cluster-then-pairwise, O(K²) per cluster." That framing overstates what an LLM agent does: it does not execute an O(K²) enumeration; it is given a candidate-selection heuristic in prose. Calling it an algorithm with complexity guarantees would be dishonest unless a deterministic script built the clusters — and none does. So the design states it as bounded, recall-limited candidate generation.
Two failure modes are real and are disclosed in the output, not hidden:
The heuristic, stated as guidance:
(paper_a, paper_b).synthesis_agent already runs under a strict narrative-side contract: it emits, it does not audit; it does not simulate audit steps; it does not read entry frontmatter (synthesis_agent.md § PATTERN PROTECTION + the citation-emission narrative-side rules). The contradiction inventory inherits all of it:
cross_paper_tensions[] and pending scholar confirmations. It does not decide whether the manuscript adequately addressed a tension — the scholar makes the final call (advisory-only).synthesis_agent prose contradiction handling — this extends it (makes the existing obligation inspectable), it does not create a parallel artifact. The Step 3 prose procedure and the Contradictions table stay; the inventory block sits alongside them.devils_advocate_agent — DA stress-tests a single argument adversarially; it does not enumerate pairwise paper conflicts. Different scope, untouched (#262 non-goal).resolution_pointer points at a report location, not a claim id — claim ids are not the primary key. (A future related_manifest_claim_id could be added as an optional link if a resolution becomes a tracked manuscript claim, but that is out of scope here.)A calibration reference may be recorded to sanity-check contradiction-judgment quality: a small set of paper pairs (e.g. contradictory / conditional / compatible) judged by the agent, reported as a confusion matrix + per-class recall, with the model id, date, and prompt recorded. It is not wired into CI and is not a pass/fail acceptance gate — 20 LLM-judged pairs are too few and too nondeterministic across runs for a hard threshold. Recorded as a behavioral observation, not a guarantee.
devils_advocate_agent or the claim-audit chain.#111 dependency (that is a single boolean, not lineage clusters — the issue's own correction).synthesis_agent.md Step 3 / Output Format extended to emit a cross_paper_tensions[] markdown block in addition to the existing prose (no schema).pair_assessment + resolution_status, evidence pointers, and candidate_basis (codex-corrected, not the issue's non-MECE draft).examples/contradiction_pairs_example.md shows contradiction / conditional_difference / insufficient_overlap rows + a Coverage Note with a cross-cluster recall caveat.