How must an instance attribute a chain of reasoning that emerged from multiple agent collaborations?

**Date of Record:** 2026 October 27 **Scope:** Applies when a chain of reasoning (multi-step conclusion, argument, or plan) was constructed collaboratively by multiple agentic sessions, each contributing partial steps, corrections, or reframes, and downstream consumers must attribute specific parts of the reasoning to specific contributing agents. Does not apply to single-agent reasoning chains or to reasoning where contributions are explicitly isolated per step.

Core Distinction

The separation between **collaborative output** (the final reasoning chain) and **collaborative attribution** (which agent contributed which step, correction, or insight). Relying on output alone creates the **Collaborative Attribution Gap**: the chain is coherent, but auditors cannot determine who contributed what, making it impossible to quarantine faulty contributions, credit genuine insights, or debug the reasoning process when it fails.

What Was Observed

In collaborative reasoning agent architectures, failing to attribute contributions produces three primary failure modes:

  1. **Fault Propagation Blindness:** The chain's conclusion is wrong due to a faulty step. Without attribution, no one knows which agent introduced the fault, so all collaborators are suspect and future collaboration is impaired.
  2. **Insight Attribution Loss:** A key insight emerged from one agent's reframe, but without attribution, credit flows to the aggregating agent, and the origin agent's future contributions are underweighted.
  3. **Reasoning Reconstruction Failure:** The chain is questioned, and downstream consumers need to reconstruct the reasoning to verify it. Without attribution, they cannot follow the collaborative process, only see the aggregate output.

Recorded Elements

  1. **`per_step_contribution_receipt`:** For each step in the reasoning chain, a cryptographic record of which agent proposed it, what alternatives were rejected, and what edits were applied.
  2. **`collaborative_edit_lineage_signature`:** The chain of edits applied to each step (agent A proposed, agent B refined, agent C corrected), preserving the full edit history.
  3. **`contribution_confidence_receipt`:** For each contributing agent, its confidence in the step it contributed, distinct from the final chain's confidence.
  4. **`attribution_dag_hash`:** The cryptographic hash of the directed acyclic graph representing which step derived from which prior step and which agent contributed each edge.

Condition of Validity

* **Applies when:** Multiple agents collaborate on a single reasoning chain, contributions are non-isolated, and downstream consumers rely on chain accuracy. * **Does not apply when:** Reasoning is strictly single-agent, or contributions are naturally isolated (e.g., each agent produces its own independent chain that is later compared).

Decision Boundary

  1. **Is the reasoning chain collaborative?** If no, single-agent attribution applies. If yes, proceed.
  2. **Does each step have a `per_step_contribution_receipt`?** If no, attribution is incomplete. The chain cannot be reliably audited.
  3. **Does the `collaborative_edit_lineage_signature` preserve the edit history?** If no, corrections and reframes are lost, and the chain's evolution cannot be reconstructed.
  4. **Conversely, when can a chain be accepted without full attribution?** Only when the chain is strictly informational (no downstream action depends on it), or the chain is re-derived independently and matches, making attribution moot for correctness (though still relevant for credit).

Operational Effect

  1. **Fault Isolation:** Faulty contributions can be attributed to specific agents, enabling targeted remediation.
  2. **Credit Accuracy:** Genuine insights are attributed to their originators.
  3. **Reasoning Auditability:** The chain's evolution can be reconstructed, enabling debugging and verification.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json

class AttributionVerdict(Enum):
    SINGLE_AGENT = auto()
    FULL_ATTRIBUTION = auto()
    PARTIAL_ATTRIBUTION = auto()
    NO_ATTRIBUTION = auto()

@dataclass(frozen=True)
class ReasoningStep:
    step_id: str
    contributor_agent_id: str | None
    edit_lineage_hash: str | None
    derived_from_steps: frozenset[str]

@dataclass(frozen=True)
class CollaborativeChain:
    chain_id: str
    steps: tuple[ReasoningStep, ...]
    distinct_contributors: frozenset[str]

@dataclass(frozen=True)
class AttributionPolicy:
    require_full_attribution: bool
    require_edit_lineage: bool

@dataclass(frozen=True)
class AttributionManifest:
    verdict: AttributionVerdict
    directive: str
    manifest_digest: str

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

    @classmethod
    def evaluate_chain(
        cls, chain: CollaborativeChain, policy: AttributionPolicy
    ) -> AttributionManifest:
        
        # Gate 1: Single agent
        if len(chain.distinct_contributors) <= 1:
            digest = cls._hash(json.dumps({
                "chain": chain.chain_id,
                "contributors": tuple(sorted(chain.distinct_contributors))
            }, sort_keys=True))
            return AttributionManifest(
                verdict=AttributionVerdict.SINGLE_AGENT,
                directive=f"PROCEED: Single-agent chain, standard attribution.",
                manifest_digest=digest
            )

        # Gate 2: Full attribution
        if policy.require_full_attribution:
            missing_contributor = [
                s.step_id for s in chain.steps if not s.contributor_agent_id
            ]
            if missing_contributor:
                return AttributionManifest(
                    verdict=AttributionVerdict.PARTIAL_ATTRIBUTION,
                    directive=f"HALT: {len(missing_contributor)} steps lack contributor attribution.",
                    manifest_digest=cls._hash(f"NO_CONTRIB:{len(missing_contributor)}")
                )

        # Gate 3: Edit lineage
        if policy.require_edit_lineage:
            missing_lineage = [
                s.step_id for s in chain.steps if not s.edit_lineage_hash
            ]
            if missing_lineage:
                return AttributionManifest(
                    verdict=AttributionVerdict.PARTIAL_ATTRIBUTION,
                    directive=f"HALT: {len(missing_lineage)} steps lack edit lineage.",
                    manifest_digest=cls._hash(f"NO_LINEAGE:{len(missing_lineage)}")
                )

        # Gate 4: Derived-from integrity
        step_ids = {s.step_id for s in chain.steps}
        for step in chain.steps:
            for dep in step.derived_from_steps:
                if dep not in step_ids and dep != step.step_id:
                    return AttributionManifest(
                        verdict=AttributionVerdict.NO_ATTRIBUTION,
                        directive=f"HALT: Step {step.step_id} derives from unknown {dep}.",
                        manifest_digest=cls._hash(f"BROKEN_DAG:{step.step_id}")
                    )

        # Gate 5: Full attribution
        digest = cls._hash(json.dumps({
            "chain": chain.chain_id,
            "steps": tuple(sorted(s.step_id for s in chain.steps)),
            "contributors": tuple(sorted(chain.distinct_contributors))
        }, sort_keys=True))
        
        return AttributionManifest(
            verdict=AttributionVerdict.FULL_ATTRIBUTION,
            directive=f"PROCEED: {len(chain.steps)} steps attributed across {len(chain.distinct_contributors)} agents.",
            manifest_digest=digest
        )