{"slug": "openworkproof-protocol-specification", "title": "OpenWorkProof Protocol Specification", "summary": "A community-driven specification, OpenWorkProof, defines a protocol for verifying AI agent work through signed, auditable evidence and negative control arms. The protocol addresses the failure model where verification can be green yet structurally meaningless, citing production data showing a gate caught only 56% of known degradations. It introduces primitives such as guards, negative controls, and population manifests to test verifier capability and prevent silent decay.", "body_md": "**Status:** Draft v0.3 — community co-design\n\n**Date:** 2026-08-12\n\n**Repository:** [https://github.com/dengyier/OpenWorkProof](https://github.com/dengyier/OpenWorkProof)\n\n**License:** TBD (project convention: open, permissive)\n\nThis specification is the product of a public design conversation (2026-08-08 → 2026-08-12) across LinkedIn and Dev.to. Every primitive below was shaped by named community contributions; each section cites its origin. The protocol's governing principle, stated by the community and adopted here:\n\nVerification must itself be tested.A check that never fails has never been checked.\n\nOpenWorkProof is a protocol for **verifying AI agent work**: producing signed, auditable evidence that a piece of agent execution happened as claimed, and that the verifier producing that evidence is capable of detecting failure.\n\nTwo distinct claims are always separated in this protocol:\n\n| Claim | Mechanism | Meaning |\n|---|---|---|\nAuthenticity |\nSignature over a receipt | This work was executed and attested as stated |\nVerifier capability |\nNegative control arm in the receipt | The verifier would have caught a lie |\n\nSignatures alone prove the first. They say nothing about the second — the `ln.strip()`\n\nlesson (Section 4.1).\n\n**In scope:**\n\n**Non-goals (for now):**\n\n| Term | Definition |\n|---|---|\nGuard |\nA verification check: command + assertion + expected failure behavior |\nGuard inventory |\nThe set of guards an operator runs, classified proven / unproven / broken |\nNegative control |\nA deliberately broken input, run on every CI pass, asserting the guard goes red |\nProvocation contract |\nThe formal spec of what a negative control provokes (exit code, stderr pattern, schema scope) |\nReceipt |\nSigned evidence of one verification event |\nPositive arm |\nThe \"did it pass\" side of a receipt: test suite, result, population |\nNegative arm |\nThe \"would it catch a lie\" side: control fixture, control result, control target |\nPopulation manifest |\nThe honest enumeration of what a check was supposed to examine |\nEligible seen |\nPre-selection count: what reached the gate |\nSelection loss |\nThe auditable gap between `eligible_seen` and `population_size`\n|\nRot |\nSilent decay of a check's capability while output stays green (three kinds: guard, control, population) |\n\nThe protocol exists because verification can be **green and structurally meaningless**. The failure model is explicit.\n\n`ln.strip()`\n\n)\nA production gateway ran a signed, audited check that reported `verified: true`\n\non every run for months. The bug: a stray newline pushed an `assert`\n\nbelow a `return`\n\n, so the assertion never executed. Exit code 0. Verdict verified.\n\nMeasured impact (fintech engineer's post, 2026-08-09):\n\n| Status | Count |\n|---|---|\n| Guards total | 40 |\n| Proven (can detect failure) | 7 |\n| Broken | 0 |\nUnproven |\n33 |\n\nFive of eight caller-test shapes produced false passes. The agent had \"passed 2,283 tests\" and failed in production.\n\n**Lesson:** the verifier is part of the system being verified. It must be tested with inputs designed to make it fail.\n\nEven with negative controls in place, a check can go silently dead three ways (community taxonomy, 2026-08-12):\n\n| Rot | Failure | Countermeasure | Origin |\n|---|---|---|---|\nGuard rot |\nGuard stops catching real failures | Continuous negative control on every CI pass | Max Quimby |\nControl rot |\nControl stops testing the right failure (recall/precision of the test itself) | Digest pinning + `control_schema_version`\n|\nSkillselion |\nPopulation rot |\nGuard examines the wrong population, or none at all |\n`eligible_seen` in the population manifest |\nTom Jones |\n\nAll three can produce a green checkmark while being structurally meaningless. All three need different countermeasures.\n\nProduction data (Ethan Walker, 2026-08-12): a gate caught **23 of 41** known degradations — a 56% catch rate — across eleven green weeks in which nobody asked what fraction it catches.\n\n**Lesson:** every guard must be measurable against known-bad inputs. The negative control is the cheap, proactive version of the expensive forensic replay Ethan had to do retroactively.\n\nA guard is a check plus a control:\n\n```\nguard:\n  id: gw_check_response_shape\n  description: \"Every gateway response matches the documented schema\"\n  command: \"check_response.sh\"\n  assertion: \"schema_validate $INPUT\"\n  status: unproven        # proven | unproven | broken — set by control runs\n  controls: [gw_control_null_handling]\n```\n\nThe **guard inventory** is a published, versioned list — not a private detail. It is the unit of honesty:\n\n```\nguard_inventory:\n  schema_version: \"1.0\"\n  generated_at: 2026-08-12T00:00:00Z\n  totals:\n    guards: 40\n    proven: 7\n    unproven: 33\n    broken: 0\n  guards: [ ... ]\n```\n\n\"Proven\" is defined operationally: **a guard is proven only while its negative control fails as expected.** The moment the control passes (green on broken input), the guard is reclassified `unproven`\n\nor `broken`\n\n. Proven is a time-decaying label, not a permanent badge (Max Quimby: guard rot).\n\nA negative control pins the exact broken input and the exact expected failure, and scopes itself to schema versions (Skillselion: control rot / digest pinning):\n\n```\nnegative_control:\n  fixture_digest: sha256:abc123...            # The exact broken input\n  expected_failure_digest: sha256:def456...   # The exact failure signature\n  control_schema_version: 2                   # For schema migration tracking\n  control_spec:\n    target_schema_version: \">=1.0, <3.0\"      # Valid for these schema versions\n    provocation_type: null_handling           # What class of failure it tests\n    expected_exit_code: non-zero              # Minimum bar\n    expected_stderr_pattern: \"NullPointerException\"  # Specific signal\n```\n\nRules:\n\n`control_schema_version`\n\nand `expected_failure_digest`\n\nover time.`target_schema_version`\n\nrange.A guard's scope must be auditable. The manifest distinguishes *what reached the gate* from *what passed selection* (Tom Jones, third round):\n\n```\npopulation_manifest:\n  selection_rule: \"threads we have commented in\"   # What we HOLD\n  eligible_seen: 400                               # What reached the gate (pre-selection)\n  population_size: 12                              # What passed selection (post-selection)\n  population_digest: <merkle_root>                 # Tamper-evident enumeration\n  sampling_rate: 1.0                               # 100% = no sampling\n  effective_from: <timestamp>                      # When the rule was authoritative\n```\n\nSemantics:\n\n`eligible_seen`\n\n— `population_size`\n\n— Canonical decision table (Tom Jones):\n\n| Scenario | `eligible_seen` |\n`population_size` |\nMeaning |\n|---|---|---|---|\n| Healthy instrument | 0 | 0 | Nothing to do, nothing expected |\n| Broken collector | 400 | 0 | 400 things should have been checked, 0 were |\n\nWithout `eligible_seen`\n\n, both scenarios produce the same receipt. With it, the broken case is a **live, self-reporting rot signal on the day it happens** — not at the next review.\n\nOperational rule: **check the set you HOLD, not the set you FETCHED.** The `selection_rule`\n\nmust be defined over the population you intend to cover, and `eligible_seen`\n\nproves the collector reached it.\n\nReference scenarios (included in Appendix A with attribution): the thread monitor and the sampler.\n\nThe signed unit of verification. One payload, two arms (Cophy Origin: the receipt–content gap; Mikhail: dual-arm verification):\n\n```\ndual_arm_receipt:\n  schema_version: \"1.0\"\n  claim:\n    task_id: owp-task-20260812-001\n    description: \"Refund processed for order R-4491\"\n    result: done\n    output_digest: sha256:9f8e...               # What was actually produced\n  positive_arm:\n    test_suite_digest: sha256:77aa...\n    test_result: pass\n    population_manifest:                       # 5.3 — what the pass covered\n      selection_rule: \"refunds with amount > 0\"\n      eligible_seen: 113\n      population_size: 113\n      population_digest: sha256:31cd...\n      sampling_rate: 1.0\n      effective_from: 2026-08-12T00:00:00Z\n  negative_arm:                                # 5.2 — would the verifier catch a lie?\n    control_fixture_digest: sha256:abc123...\n    control_result: fail-as-expected\n    control_schema_version: 2\n    control_target: guard:gw_check_response_shape\n  signature:\n    algorithm: ed25519\n    key_id: owp-key-issuer-01\n    value: 0x...\n```\n\nRules:\n\n`output_digest`\n\n(what was produced), not merely record that a file exists.`control_target`\n\nties the control to the specific guardA receipt is a **bounded claim**: \"this was true under these conditions at this time\" (Suraj Suradkar). Obsolescence is tracked, not hidden.\n\n```\nretraction_receipt:\n  parent_receipt_id: owp-receipt-20260812-001\n  retraction_auth: <PolicyDecision>            # Independent trust boundary; co-signed\n  propagation_class: none | downstream_causal | same_predicate\n  semantic_cause:                              # Open, versioned enum\n    category: superseded_by | refuted_by | scope_changed\n    superseding_receipt_id: owp-receipt-20260812-009\n    valid_until: 2026-08-20T00:00:00Z\n```\n\nCategories (Suraj, refining Mikhail's v0.2):\n\n| Category | Meaning | Consequence |\n|---|---|---|\n`superseded_by` |\nDecision was correct then, no longer current | Old receipts: not invalid, bounded |\n`refuted_by` |\nOld evidence was wrong | Old receipts: retrospective scope loss |\n`scope_changed` |\nPopulation/policy shifted | Old receipts: incomplete relative to new scope |\n\nDesign constraints (Mikhail):\n\n`REVOKED / SUPERSEDED / EXPIRED`\n\nare `retraction_auth`\n\nis an `propagation_class`\n\nlives in the protocol layer; `semantic_cause`\n\nin the application layer.Policies and their checkpoints are versioned in a monotonic registry (Brian Jin, rounds 2–3):\n\n```\npolicy_checkpoint:\n  policy_id: pol-refund-eligibility\n  revision: 4\n  artifact_digest: sha256:e0b1...\n  effective_from: 2026-08-12T00:00:00Z\n  previous_checkpoint_digest: sha256:c2a9...    # Monotonic chain\n  signature:\n    key_id: owp-key-policy-01\n    value: 0x...\n┌─ Every CI pass ─────────────────────────────────────────────┐\n│ 1. Define  guard + negative_control (provocation contract)   │\n│ 2. Run positive arm:   suite → test_result, population_manifest\n│ 3. Run negative arm:   broken fixture → control_result        │\n│    — control must FAIL as expected, else guard reclassified   │\n│ 4. Package DualArmReceipt (claim + positive + negative)       │\n│ 5. Sign with issuer key; publish to registry                  │\n│ 6. On policy/scope change: emit RetractionReceipt             │\n└──────────────────────────────────────────────────────────────┘\n```\n\nOutputs:\n\nCausal order (community-corrected model; PolicyAnchor is Layer −1, not the top):\n\n| Layer | Concern | Primitive |\n|---|---|---|\n| Policy history / authority | What rules were in force when | Policy-State Registry, PolicyAnchor |\n| Judgment | What decision was authorized | JPS layer (external) |\n| Execution | What the agent did | claim + output_digest |\n| Evidence | That verification worked | DualArmReceipt (positive + negative arm) |\n\nEvidence is the *result* of the pipeline; authority is its *input*. Confusing the two was the original `ln.strip()`\n\nerror — evidence was signed while authority was dead.\n\n(Glen Allen: \"verification must itself be tested\".)\n\n| Level | State | Signal |\n|---|---|---|\n| 0 | Trust the agent's own report | No independent evidence |\n| 1 | Logs exist | \"It ran\" (unverifiable claim) |\n| 2 | Signed receipts | Authenticity proven |\n| 3 | Negative controls | Verifier capability proven for fixtures |\n| 4 | Continuous controls + guard inventory published | Capability continuously re-proven; rot visible |\n| 5 | Controls are themselves tested (control rot guarded) | The verifier's verifier is verified |\n\nTarget for production adoption: **Level 4**, with Level 5 as the differentiator.\n\n(Sri Ramya: layered adoption — execution evidence first, signed receipts second.)\n\n`DualArmReceipt`\n\ns, continuous controls, published inventory. What this specification defines.Teams adopt Tier 1 first; Tier 2 is the upgrade path, not the entry requirement.\n\n`eligible_seen`\n\n: is it total events or matched events? Do we need a third count, `total_seen`\n\n?`control_schema_version`\n\nmigration: what is the deprecation protocol for a control whose `target_schema_version`\n\nrange expires?`semantic_cause`\n\nenum: who curates versions, and how do downstream consumers handle unknown values?This specification was co-designed in public. Contributions by:\n\n`ln.strip()`\n\n`eligible_seen`\n\n, FETCHED vs HOLD, reference scenarios (5.3, Appendix A)`DualArmReceipt`\n\n(5.4)Attributed to Tom Jones (2026-08-12), canonical for the population manifest:\n\n**Scenario 1 — The thread monitor.** A sampler configured to measure, on 100% of eligible events, how often two models agree on correctness. Four days produced zero rows, while the box served 113–209 requests/day. Every part of the receipt was valid: selection rule correct, tool ran, exit 0, signature valid. The population was empty because the *eligible shape was too narrow*. `eligible_seen = 400, population_size = 0`\n\nexposes the broken collector the day it happens.\n\n**Scenario 2 — The sampler.** An invite sampler claims to have sampled 12 threads. Without a manifest, the claim is unfalsifiable. With `selection_rule + eligible_seen + population_size`\n\n:\n\n\"You sampled 12 of 400\" invites an argument. \"You sampled 12\" ends one.\n\n`schema_version`\n\n.`negative_control.control_schema_version`\n\ntracks migration of control fixtures; a control is only valid within `control_spec.target_schema_version`\n\n.", "url": "https://wpnews.pro/news/openworkproof-protocol-specification", "canonical_source": "https://dev.to/dengyier/openworkproof-protocol-specification-3k8e", "published_at": "2026-08-12 14:53:14+00:00", "updated_at": "2026-08-12 15:19:05.585718+00:00", "lang": "en", "topics": ["ai-agents", "ai-safety", "ai-infrastructure", "developer-tools"], "entities": ["OpenWorkProof", "Max Quimby", "Skillselion", "Tom Jones", "Ethan Walker"], "alternates": {"html": "https://wpnews.pro/news/openworkproof-protocol-specification", "markdown": "https://wpnews.pro/news/openworkproof-protocol-specification.md", "text": "https://wpnews.pro/news/openworkproof-protocol-specification.txt", "jsonld": "https://wpnews.pro/news/openworkproof-protocol-specification.jsonld"}}