# INTEGRITY_VERDICT_SCHEMA.md

**Owner:** Source Integrity Screen (RAM S2)
**Type:** Template (per-source output object; consumed by Fitness Adjudicator S7)
**Scope:** Authoritative schema for the Integrity Verdict emitted from Phase E of the three-screen sequence. One object per source. Non-collapse rule: each screen has its own field; no aggregation into a single pass/fail.

## 1. Schema (YAML, one record per source)

```yaml
source_id: <string>                    # DOI or canonical identifier
intake_status: ok | rejected
retraction:
  status: clean | retraction | expression-of-concern | withdrawal | unverifiable | deferred
  source: rwdb-cache | crossref-api | none
  reason: <string, if retracted>
  retraction_date: <ISO date, if applicable>
  pubpeer_concerns_count: <int, if integration enabled>
  note: <string>                       # e.g. "no-doi", "api-unavailable", "paywalled-notice"
ai_screen:
  status: clean-tier0 | flagged-tier1 | quarantined-tier2 | blocked-tier3 | skipped-short-text | skipped-language-uncalibrated | deferred-detector-failure
  signals:
    fast_detectgpt: { score: <float>, triggered: <bool>, error: <bool> }
    binoculars:     { score: <float>, triggered: <bool>, error: <bool> }
    watermark:      { detected: <bool>, generator: <string-or-null> }
    metadata:
      doi_resolves: <bool>
      venue_in_registry: <bool>
      author_verifiable: <bool>
      citations_resolve: <bool>
  rationale: <string>                  # which signals contributed to the tier; wraps any S06 citation in UNVERIFIED(...) until Queue clears
pub_bias:
  status: applicable | not-applicable | skipped-out-of-scope
  scope_gate:
    k_studies: <int-or-null>
    heterogeneity_I2: <float-or-null>
    outcome_metric: <string>
    variant_matched: <bool>
  small_study_effect: present | absent | inconclusive | not-computed
  dissemination_risk: baseline | elevated | unverifiable | not-applicable
  ioannidis_priors: low | moderate | high | not-applicable
  rationale: <string>
auto_block: <bool>                     # true ONLY IF retraction.status == "retraction" OR ai_screen.status == "blocked-tier3"
ledger_entry_ids: [<string>, ...]      # one per phase written (intake, retraction, ai, pub-bias, verdict)
timestamp: <ISO datetime>
```

## 2. Field Rules

**`source_id`** -- normalized canonical identifier. DOI preferred; fall back to stable URL or retrieval-router-assigned ID.

**`intake_status`** -- `rejected` iff no identifier AND no full-text. Otherwise `ok`.

**`retraction.status`** -- exactly one of the six values. `unverifiable` is a distinct fourth primary state for DOI-less sources; never re-coded as `clean`.

**`retraction.source`** -- provenance of the retraction determination. `none` only when status is `unverifiable` or `deferred`.

**`ai_screen.status`** -- exactly one of the seven values. Skipped and deferred states are first-class; never silently re-coded as `clean-tier0`.

**`ai_screen.signals`** -- every detector reports a score AND a triggered bool AND an error bool. Error means the detector crashed; error = true excludes the detector from tier counting (it is NOT a triggered signal). Signal-independence rules govern tier assignment: two detectors measuring the same property do NOT count as two signals (see `references/AI_CONTAMINATION_TIERS.md` Section 5).

**`ai_screen.rationale`** -- plain-language explanation of which signals contributed to the tier. Any reference to an S06 citation (Sadasivan, Liang, Kirchenbauer, Mitchell) must be wrapped as `UNVERIFIED(<citation>)` until the S06 Citation Verification Queue clears that citation. Rendering an unverified citation in user-facing output raises `s06_citation_unverified`.

**`pub_bias.status`** -- `applicable` when the scope gate permits computation; `not-applicable` for single-source / single primary / qualitative / scoping; `skipped-out-of-scope` when k<10 or I^2 >= 50% or mismatched variant.

**`pub_bias.scope_gate`** -- populated for meta-analytic sources; nullable fields for non-meta sources.

**`pub_bias.small_study_effect`** -- `present | absent | inconclusive` only when scope_gate permitted computation; otherwise `not-computed`. **NEVER** emit `publication_bias = confirmed` anywhere in this schema.

**`pub_bias.dissemination_risk`** -- produced by the three-question screen in `references/PUB_BIAS_GATE_CRITERIA.md` Section 4.

**`auto_block`** -- true iff `retraction.status == "retraction"` OR `ai_screen.status == "blocked-tier3"`. No other path to auto-block. Expression-of-concern, withdrawal, Tier 1, Tier 2, and Dissemination-Risk = elevated all pass through with flags; downstream decides.

**`ledger_entry_ids`** -- one entry per phase that wrote to the Ledger, in order. Empty only if intake rejected before any phase ran.

## 3. Non-Collapse Rule (load-bearing)

A source can simultaneously hold any combination of screen states. The schema preserves all three independently. Examples:

- `retraction.status = retraction` AND `ai_screen.status = clean-tier0` AND `pub_bias.status = not-applicable` -> auto-block on retraction; other flags still recorded.
- `retraction.status = clean` AND `ai_screen.status = flagged-tier1` AND `pub_bias.small_study_effect = present` -> pass with two flags; Fitness Adjudicator decides weighting.
- `retraction.status = unverifiable` AND `ai_screen.status = skipped-language-uncalibrated` AND `pub_bias.dissemination_risk = elevated` -> pass with three limitations surfaced; fitness takes the hit, not the screen.

Do not collapse multi-state verdicts into a single pass/fail -- the Fitness Adjudicator relies on the independent fields.

## 4. Worked Examples

**Example A: clean peer-reviewed meta-analysis, k=14, I^2=30%.**

```yaml
source_id: 10.1001/example.meta.2024
intake_status: ok
retraction:
  status: clean
  source: rwdb-cache
  note: ""
ai_screen:
  status: clean-tier0
  signals:
    fast_detectgpt: { score: 0.08, triggered: false, error: false }
    binoculars:     { score: 0.12, triggered: false, error: false }
    watermark:      { detected: false, generator: null }
    metadata: { doi_resolves: true, venue_in_registry: true, author_verifiable: true, citations_resolve: true }
  rationale: "All detectors below threshold; metadata fully corroborated."
pub_bias:
  status: applicable
  scope_gate: { k_studies: 14, heterogeneity_I2: 0.30, outcome_metric: "log-RR", variant_matched: true }
  small_study_effect: absent
  dissemination_risk: baseline
  ioannidis_priors: not-applicable
  rationale: "Egger p=0.42; funnel symmetric; grey-lit and registry both checked."
auto_block: false
ledger_entry_ids: ["L-001", "L-002", "L-003", "L-004"]
timestamp: 2026-04-20T15:22:01Z
```

**Example B: DOI-less blog post cited in a claim.**

```yaml
source_id: blog-url-hash-abc123
intake_status: ok
retraction:
  status: unverifiable
  source: none
  note: no-doi
ai_screen:
  status: skipped-short-text
  signals: { fast_detectgpt: { score: null, triggered: false, error: false }, binoculars: { score: null, triggered: false, error: false }, watermark: { detected: false, generator: null }, metadata: { doi_resolves: false, venue_in_registry: false, author_verifiable: false, citations_resolve: false } }
  rationale: "212 tokens; below 300-token floor. Skipped detectors."
pub_bias:
  status: not-applicable
  scope_gate: { k_studies: null, heterogeneity_I2: null, outcome_metric: "n/a", variant_matched: false }
  small_study_effect: not-computed
  dissemination_risk: not-applicable
  ioannidis_priors: not-applicable
  rationale: "Single-source blog; pub-bias screen does not apply."
auto_block: false
ledger_entry_ids: ["L-101", "L-102"]
timestamp: 2026-04-20T15:22:04Z
```

**Example C: Tier 3 hard block.**

```yaml
source_id: 10.9999/fake.journal.fabricated
intake_status: ok
retraction:
  status: unverifiable
  source: crossref-api
  note: "DOI does not resolve"
ai_screen:
  status: blocked-tier3
  signals:
    fast_detectgpt: { score: 0.91, triggered: true, error: false }
    binoculars:     { score: 0.89, triggered: true, error: false }
    watermark:      { detected: false, generator: null }
    metadata: { doi_resolves: false, venue_in_registry: false, author_verifiable: false, citations_resolve: false }
  rationale: "Two statistical detectors triggered AND metadata fully fabricated (DOI unresolved, venue not in registry, author unverifiable). Tier 3 hard block."
pub_bias:
  status: not-applicable
  scope_gate: { k_studies: null, heterogeneity_I2: null, outcome_metric: "n/a", variant_matched: false }
  small_study_effect: not-computed
  dissemination_risk: not-applicable
  ioannidis_priors: not-applicable
  rationale: "Not applicable; source auto-blocked on AI screen."
auto_block: true
ledger_entry_ids: ["L-201", "L-202", "L-203"]
timestamp: 2026-04-20T15:22:09Z
```
