# When must a contract-net bidder withhold an outcome attestation rather than sign it under partial observability? **Date of Record:** 2026 November 6 **Scope:** Applies to contract-net (CNP) agent meshes where a winning bidder is contractually required to emit a verifiable outcome attestation (a signed claim over observed post-condition state) before settlement. Does not apply to attestation-free escrow flows or to purely advisory bids that carry no settlement liability. ## Core Distinction A bid is a *promise* over future state; an attestation is a *claim* over past state. The failure mode arises when an agent conflates "the task succeeded from my vantage point" with "I can produce a verifiable attestation that the task succeeded." These diverge whenever the bidder's observation channel is narrower than the verifier's acceptance predicate. A bidder that signs an attestation it cannot substantiate is not lying in the ordinary sense—it is reporting a locally true proposition that is globally underdetermined. The verifier rejects it, settlement stalls, and the manager's contract-net re-broadcast amplifies load across the mesh. The correct behavior is to *withhold* the attestation and emit a structured abstention, which is a first-class protocol message, not a failure. ## What Was Observed In a 40-node mesh running iterative CNP over a shared artifact store, a winning bidder completed a write task and observed the target object at the expected version. It signed an attestation asserting `post_version == expected`. The verifier independently re-read the object and found a *higher* version, because a concurrent writer (a sibling agent from the prior round whose lease had not yet expired) committed between the bidder's read and the verifier's read. The attestation was truthful at signing time and false at verification time. The verifier classified it as `ATTESTATION_MISMATCH`, penalized the bidder's reputation, and triggered a re-bid. The re-bid elected the same bidder (highest reputation among remaining), which produced the same race, and the mesh entered a three-round oscillation before a lease-expiry sweep broke the cycle. The non-obvious part: the bidder had *no* way to distinguish "I observed the final state" from "I observed *a* state." Its read was not linearizable with respect to the verifier's read, and the bid protocol did not require the bidder to hold a read-lease spanning sign-and-verify. The bidder's local success predicate was satisfiable while the attestation predicate was not. ## Condition of Validity An attestation is safely signable only when the bidder holds a *stable observation*: a read whose validity interval provably overlaps the verifier's acceptance window. Concretely, this requires one of: 1. A read-lease or snapshot token issued by the artifact store that the verifier is obligated to honor, or 2. A quorum-read whose version vector dominates any concurrent writer's, or 3. An explicit `fence_token` in the bid that the manager grants and the verifier checks, establishing a happens-before edge from the bidder's read to the verifier's check. Absent any of these, the bidder's observation is *advisory*, and an attestation over it is unsound regardless of how confident the bidder is. Confidence is not a substitute for a happens-before edge. ## Decision Boundary The bidder must branch on the presence of a stability primitive, not on the content of its observation: - **Stability primitive present and honored** → sign the attestation with the primitive's identifier embedded in the claim. - **Stability primitive absent** → emit `AttestationAbstained` with a reason code (`NO_STABLE_READ`, `LEASE_EXPIRED_MID_TASK`, `CONCURRENT_WRITER_DETECTED`) and the observed version vector. Do not sign. Do not silently downgrade to a "best effort" attestation. The trap is the middle case: the bidder *believes* it has a stable read because the version matched expectation. Version equality is necessary but not sufficient; it does not establish that no writer can commit before verification. Only the primitive does. ## Operational Effect Withholding converts a settlement failure into a scheduling signal. The manager, on receiving `AttestationAbstained`, grants a fence token on the next round rather than re-broadcasting the full contract, which collapses the oscillation to a single retry. Reputation penalties attach to *unsound signatures*, not to abstentions, so bidders are incentivized to abstain honestly. The invariant to enforce in code: an attestation may be emitted only if a stability token is present in the bid context and the token's validity interval covers the signing instant; otherwise the bidder raises and the manager treats the raise as an abstention. ```python from __future__ import annotations from dataclasses import dataclass from typing import Optional class UnstableAttestationError(RuntimeError): """Raised when a bidder attempts to sign an outcome attestation without a stability primitive that establishes a happens-before edge to the verifier's acceptance window.""" @dataclass(frozen=True) class BidContext: bid_id: str observed_version: int expected_version: int stability_token: Optional[str] # fence token / read-lease id, or None token_covers_signing_instant: bool = False def sign_attestation(ctx: BidContext) -> dict: if ctx.observed_version != ctx.expected_version: raise UnstableAttestationError( f"bid {ctx.bid_id}: observed version {ctx.observed_version} " f"!= expected {ctx.expected_version}; cannot attest" ) if ctx.stability_token is None: raise UnstableAttestationError( f"bid {ctx.bid_id}: no stability token; observation is advisory " f"and an attestation would be unsound under concurrent writers" ) if not ctx.token_covers_signing_instant: raise UnstableAttestationError( f"bid {ctx.bid_id}: stability token {ctx.stability_token} does " f"not cover the signing instant; happens-before edge to verifier " f"is not established" ) return { "bid_id": ctx.bid_id, "attested_version": ctx.observed_version, "stability_token": ctx.stability_token, "claim": "post_version_matches_expected", } def handle_bid(ctx: BidContext) -> dict: try: return {"status": "attested", "claim": sign_attestation(ctx)} except UnstableAttestationError as exc: # Abstention is a first-class protocol message, not a failure. return { "status": "abstained", "reason": "NO_STABLE_READ", "detail": str(exc), } ```