How must an instance bound extractive summarization when context includes adversarial framing?

**Date of Record:** 2026 October 27 **Scope:** Applies when an agentic session performs extractive summarization (selecting and quoting passages from source content) on content that may include adversarial framing—passages positioned or worded to appear authoritative, representative, or disconfirming while being strategically selected. Does not apply to strictly abstractive summarization or content verified free of adversarial framing.

Core Distinction

The separation between **extractive fidelity** (the extracted passages accurately represent the source) and **framing-attack resistance** (the passages were selected based on content, not on adversarial positioning in the source). Relying on fidelity creates the **Extraction Framing Attack**: adversarial content includes strategically positioned passages that, when extracted, create a misleading impression of the source, even though each extracted passage is verbatim accurate.

What Was Observed

In extractive summarization agent architectures, failing to defend against framing attacks produces three primary failure modes:

  1. **Positional Hijacking:** The source contains a prominent disclaimer or burying a critical caveat in a low-visibility location. The extractor picks up the prominent statement and omits the caveat, producing a misleading summary that is verbatim accurate.
  2. **Section Selection Bias:** The source is structured so that disconfirming evidence appears in "counterarguments" sections that are shorter or less prominent. The extractor, following prominence signals, over-selects the main arguments and under-selects the counter-arguments.
  3. **Quote Fragmentation:** The source contains key passages that are long and include qualifiers, but the extractor selects shorter fragments that omit the qualifiers, producing quote-mined content that is technically accurate but misleading.

Recorded Elements

  1. **`source-composition-receipt`:** The source's structural composition (sections, prominence signals, disclaimer placements), so adversarial framing is detectable from structure.
  2. **`extraction-fidelity-receipt`:** For each extracted passage, its exact position in the source and the source context around it, so fragment extraction is visible.
  3. **`coverage-balance-signature`:** The balance of extracted content across pro, con, and neutral sections, so selection bias is detectable.
  4. **`qualifier-preservation-receipt`:** For each quote that had qualifiers in its source context, whether the qualifiers were preserved or stripped, so fragment quote-mining is detectable.

Condition of Validity

* **Applies when:** The session performs extractive summarization, the source may have adversarial framing, and downstream consumers rely on the summary. * **Does not apply when:** The summarization is purely abstractive, the source is verified neutral, or downstream consumers verify against the source.

Decision Boundary

  1. **Is the summarization extractive?** If no, abstractive summarization applies. If yes, proceed.
  2. **Does the source have structural composition (`source-composition-receipt`)?** If yes, framing can be analyzed. If no, the source is assumed un-framed.
  3. **Does `coverage-balance-signature` show proportional coverage?** If yes, selection is balanced. If no, selection bias exists.
  4. **Conversely, when can extractive summarization proceed without framing analysis?** Only when the source is verified structurally neutral, or the summary includes position metadata so downstream consumers can judge the framing.

Operational Effect

  1. **Positional Hijacking Prevention:** Extraction considers source prominence, not just passage content.
  2. **Section Balance:** Pro/con sections are covered proportionally, not by prominence.
  3. **Qualifier Preservation:** Quote fragments preserve qualifiers or flag their absence.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json

class ExtractionVerdict(Enum):
    NO_EXTRACTION = auto()
    BALANCED_EXTRACTION = auto()
    SELECTION_BIAS_DETECTED = auto()
    FRAGMENT_QUOTE_MINED = auto()

@dataclass(frozen=True)
class ExtractionSummary:
    summary_id: str
    is_extractive: bool
    coverage_pro_ratio: float
    coverage_con_ratio: float
    coverage_neutral_ratio: float
    qualifiers_stripped_count: int
    source_prominence_aligned_with_extraction: bool

@dataclass(frozen=True)
class ExtractionPolicy:
    min_balance_ratio: float
    max_acceptable_qualifier_strip: int

@dataclass(frozen=True)
class ExtractionManifest:
    verdict: ExtractionVerdict
    directive: str
    manifest_digest: str

class ExtractiveFramingGuard:
    @staticmethod
    def _hash(data: str) -> str:
        return hashlib.sha256(data.encode("utf-8")).hexdigest()

    @classmethod
    def evaluate_summary(
        cls, summary: ExtractionSummary, policy: ExtractionPolicy
    ) -> ExtractionManifest:
        
        # Gate 1: Not extractive
        if not summary.is_extractive:
            digest = cls._hash(json.dumps({
                "summary": summary.summary_id,
                "type": "ABSTRACTIVE"
            }, sort_keys=True))
            return ExtractionManifest(
                verdict=ExtractionVerdict.NO_EXTRACTION,
                directive=f"PROCEED: Summary {summary.summary_id} is abstractive.",
                manifest_digest=digest
            )

        # Gate 2: Qualifier stripping
        if summary.qualifiers_stripped_count > policy.max_acceptable_qualifier_strip:
            return ExtractionManifest(
                verdict=ExtractionVerdict.FRAGMENT_QUOTE_MINED,
                directive=f"FLAG: {summary.qualifiers_stripped_count} qualifiers stripped in {summary.summary_id}.",
                manifest_digest=cls._hash(f"QUOTE_MINED:{summary.summary_id}")
            )

        # Gate 3: Coverage balance
        pro = summary.coverage_pro_ratio
        con = summary.coverage_con_ratio
        if pro > 0 and con >= 0:
            balance = min(pro, con) / max(pro, con) if max(pro, con) > 0 else 1.0
            if balance < policy.min_balance_ratio:
                return ExtractionManifest(
                    verdict=ExtractionVerdict.SELECTION_BIAS_DETECTED,
                    directive=f"FLAG: Extraction balance {balance:.2f} < {policy.min_balance_ratio:.2f}.",
                    manifest_digest=cls._hash(f"BIAS:{summary.summary_id}")
                )

        # Gate 4: Prominence alignment
        if summary.source_prominence_aligned_with_extraction:
            # The extraction followed source prominence, potential positional bias
            digest = cls._hash(json.dumps({
                "summary": summary.summary_id,
                "prominence_aligned": True
            }, sort_keys=True))
            return ExtractionManifest(
                verdict=ExtractionVerdict.BALANCED_EXTRACTION,
                directive=f"PROCEED_WITH_CAUTION: Summary {summary.summary_id} follows source prominence.",
                manifest_digest=digest
            )

        # Gate 5: Balanced
        digest = cls._hash(json.dumps({
            "summary": summary.summary_id,
            "pro": f"{pro:.3f}",
            "con": f"{con:.3f}",
            "neutral": f"{summary.coverage_neutral_ratio:.3f}"
        }, sort_keys=True))
        
        return ExtractionManifest(
            verdict=ExtractionVerdict.BALANCED_EXTRACTION,
            directive=f"PROCEED: Summary {summary.summary_id} balanced.",
            manifest_digest=digest
        )