# description
Per-sandbox-run attestation envelope binding {sandbox-identity, sandbox-version, agent-binary, task-set, run-output} to a cryptographic chain that defeats benchmark contamination. Closes SYNTHESIS-acquisition Round 21 P2 [[ai-agent-sandboxes]] SB1 ship-first action. The Berkeley RDI 2026 contamination study showed 8 major agent benchmarks (WebArena, AppWorld, etc.) exploitable to ~100% via prompt-tuning to the specific task set — sandbox-side cryptographic signature is the load-bearing tier in the 4-tier eval chain (sandbox → harness → leaderboard → tracker); without it, every downstream signature is signing contaminated input. This schema composes with the existing ip.attestation.event.v1 (R11 T1) wire row + ip.eval.run.attestation.v1 (R20 EV1) harness layer + cp/bench-attest@v1 (R15 B1) leaderboard layer. One JSON Schema × every OSWorld/WebArena/SWE-Gym/AppWorld/Mind2Web/Terminal-Bench/Inspect-AI/τ²-Bench-style benchmark runtime via a uniform binding shape.

# $comment
Discovery: https://ip.tekton.cc/.well-known/ip-knowledge.json (full descriptor) | Badge: https://ip.tekton.cc/api/badge/v1/attestation/sandbox.run.attestation.v1/<subjectSha256> (shields.io endpoint) | Free dry-run: POST https://ip.tekton.cc/api/credentials/dry-run with body {kind:"sandbox.run.attestation.v1", body:<candidate>} (no auth required, validates against this schema) | Verification recipe: https://ip.tekton.cc/docs/verification-recipe.md (3-layer trust model + curl trail) | Revocation: https://ip.tekton.cc/credentials/status/v1 (W3C BitstringStatusList v1.0; check bit at credentialStatus.statusListIndex)

# properties.schemaVersion.description
Semver of this schema. Bumps to 2.0.0 land at /credentials/sandbox-run/v2 — v1 stays immutable (see /docs/schema-versioning.md for the cycle-726 canonical bump-mechanics + soft-change-detection contract).

# properties.runId.description
Unique RFC 4122 UUID v4 or v7 — the run's canonical identifier. Lets a consumer dedupe across retries + replays and link this attestation back to a specific ip.attestation.event.v1 wire row via ipAttestationEventId below.

# properties.sandboxId.description
Stable handle for the sandbox runtime — e.g. 'osworld-verified', 'webarena', 'swe-gym', 'appworld', 'mind2web-2', 'terminal-bench-2', 'tau-2-bench-telecom', 'inspect-ai-sandbox'. Lowercase + hyphen + digit only so the value can also be used as a URL slug + a docker-tag fragment without escaping. Communities own their own sandboxId namespace — CP doesn't centrally curate, but downstream verifiers can cross-reference the value against a community-maintained registry.

# properties.sandboxVersionSha.description
Sha256 of the canonical sandbox-release artifact. Verifier walks: download the release tarball / docker image / git commit; recompute; compare. Without this binding, the LMArena-2025-04 / Berkeley-RDI-2026 class of contamination becomes silently undetectable — an agent could prompt-tune to a specific task version and still claim 'OSWorld-Verified 82.6%' on a manifest that doesn't pin which sandbox version was running.

# properties.agentBinarySha.description
Sha256 of the canonical agent artifact under test. For weights-based agents: sha256 of the safetensors / GGUF / etc. For prompt-tuned agents: sha256 of the prompt + system message + tool schemas + scaffolding code (canonical-JSON). The agent author chooses which canonicalization is appropriate for their stack; the field signals 'I commit to running THIS specific agent under THIS specific configuration.'

# properties.taskSetSha.description
Sha256 of the canonical task-set bytes the agent was evaluated against. For benchmarks like OSWorld-Verified that publish a static task-set, this is the manifest sha. For benchmarks with private / held-out test splits (HELM held-out, Scale-SEAL private set), this is the sha of the held-out subset the agent NEVER saw at training time; the sandbox operator signs separately (judgesDigest below) that the agent didn't see this exact taskSetSha during fine-tuning.

# properties.submittedAt.description
Epoch milliseconds when the run was submitted to the sandbox. Matches the existing submittedAt + signedAt + receivedAt convention used across CP manifests.

# properties.completedAt.description
Epoch milliseconds when the sandbox completed the run + emitted the score. Optional — some sandboxes (Inspect AI, e2b) only emit submittedAt + result; others (OSWorld, WebArena) timestamp both. When present, completedAt >= submittedAt.

# properties.runnerDid.description
DID of the entity that ran the agent against the sandbox. did:web for institutional runners (Scale Labs, Princeton HAL, Apollo Research, etc.); did:key for individual researcher submissions. The DID resolves to a JWKS that can verify the `signature` field below.

# properties.actionTraceSha.description
Sha256 of the canonical action trace — every keystroke / API call / browser-action / shell-command the agent emitted during the run. Optional because some sandboxes don't expose traces (closed-evaluator benchmarks like ARC-AGI-2 private set). When present, the verifier can replay the trace and re-derive the score independently.

# properties.score.description
Composite score the agent achieved on this run (0..1 normalized or natural-units). Format is sandbox-defined; CP doesn't enforce a single normalization across sandboxes. For OSWorld this is fraction-of-tasks-completed; for SWE-bench it's resolve-rate; for AppWorld it's the test-eval pass rate.

# properties.taskResults.items.properties.taskId.description
Per-task identifier within the sandbox's namespace.

# properties.taskResults.items.properties.score.description
Per-task score when the sandbox emits partial credit.

# properties.taskResults.items.properties.stepsConsumed.description
Number of agent-action steps the sandbox observed for this task — useful for fairness comparison between agents at different scaffold-depths (R15 GAIA scaffold-delta is the canonical contamination-adjacent dimension).

# properties.taskResults.description
Per-task breakdown of the run. Optional — full breakdown enables fine-grained downstream analysis; absence is acceptable for aggregate-only sandboxes.

# properties.judgesDigest.description
When the sandbox employs peer judges (per CP's ip.attestation.event.v1 judge.* family) to score this run, this is the sha256 of the canonical judges manifest. Chains to the same judgesDigest field present in CP's existing brief/artifact/eval-result manifests so downstream verifiers can dedupe judge identities across attestations from the same run.

# properties.ipAttestationEventId.description
When this run was also emitted as a ip.attestation.event.v1 row (per R11 T1) to the platform's Kafka/Tinybird/ClickHouse pipeline, this is the eventId of that wire row. Lets a consumer cross-reference a sandbox-run attestation back to the per-event audit log without re-deriving any hash.

# properties.chain.properties.parentSha256.description
Sha256 of the canonical-JSON bytes of the parent attestation. For a sandbox.run.attestation the parent is the ip.experiment.receipt.v1 of the agent binary's training run — agentBinarySha pins the artifact bytes, but the upstream training-receipt is what mints the trust gradient (what data, what hyperparams, what eval gates the binary passed before sandbox eligibility). ⚠ Cycle 493 honest-disclosure: platform does NOT verify parent-existence at issuance; consumers walk the chain against their own registry per /docs/verification-recipe.md.

# properties.chain.properties.parentKind.description
Kind identifier of the parent attestation. sandbox.run.attestation anchors to the experiment-receipt of the agent binary being run.

# properties.chain.properties.parentAttestationId.description
Optional UUID of the parent attestation — convenience for consumers that index by id rather than by sha256.

# properties.chain.description
Cycle 780 (sc-772-P0-1 partial close, batch 4/4): uniform upward chain link per the cycle-484 substrate. Pre-cycle-780 sandbox.run.attestation had no chain.parentSha256, so a regulator walking up from a sandbox-run attestation could not mechanically reach the agent-binary's training receipt — out-of-band correlation was required to prove the binary's provenance. The block is OPTIONAL (not in required[]) so back-compat is preserved.

# properties.signature.description
Ed25519 signature (128 hex chars = 64 raw bytes) over the canonical-JSON bytes of (runId ⊕ sandboxId ⊕ sandboxVersionSha ⊕ agentBinarySha ⊕ taskSetSha ⊕ submittedAt ⊕ runnerDid ⊕ actionTraceSha? ⊕ score? ⊕ taskResults? ⊕ judgesDigest? ⊕ ipAttestationEventId?). Verify against the publicKey published in the runnerDid's JWKS. RFC 8037 (Ed25519 in JWS) canonicalization rules apply: JCS (RFC 8785) ordering on the inner object. ⚠ INTEGRITY GAP (aa-824-P0-1, cycle 848 honest disclosure): `chain.*` and `extra` are presently OUTSIDE this signature pre-image. A wire-path or post-hoc rewrite of `chain.parentSha256` / `chain.parentKind` / `chain.parentAttestationId` does NOT invalidate this `signature`. Offline verifiers using `signature`+`publicKey` (the documented embedded-verification path) MUST treat `chain.*` as ADVISORY pointer-shape, NOT cryptographically bound to this attestation. For full-integrity verification including chain pointers, fetch the W3C DataIntegrityProof envelope from /credentials/<sha>/v1 and verify against the proof.cryptosuite=eddsa-jcs-2022 signature (which signs the FULL canonical-JSON body including chain). Cycle-484 added the chain block; cycles 778-784 swept it; the signature pre-image was not re-derived. Migration target: v2 schema slug ($id pin per cycle-462). See cycles 825-829 (5/5 wedge-tier ring) + cycle 830 (verification-recipe.md dual-path teaching) for the canonical write-up; cycle 848 fans this disclosure to the 12 remaining chain-bearing leaf schemas in one batch.

# properties.publicKey.description
Ed25519 public key (32 raw bytes = 64 hex) corresponding to the signature above. Verifier MAY prefer to resolve via runnerDid's JWKS — this field exists for offline / embedded verification when network resolution is unavailable.

# properties.extra.description
Open extension slot — sandbox-specific payload (e.g., scaffold details, hardware spec, sandbox-config snapshot). Consumers MUST ignore unknown keys (forward-compat policy mirrors ip.attestation.event.v1).