Status: Draft v0.3 β community co-design
Date: 2026-08-12
Repository: https://github.com/dengyier/OpenWorkProof
License: TBD (project convention: open, permissive)
This 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:
Verification must itself be tested.A check that never fails has never been checked.
OpenWorkProof 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.
Two distinct claims are always separated in this protocol:
| Claim | Mechanism | Meaning |
|---|---|---|
| Authenticity | ||
| Signature over a receipt | This work was executed and attested as stated | |
| Verifier capability | ||
| Negative control arm in the receipt | The verifier would have caught a lie |
Signatures alone prove the first. They say nothing about the second β the ln.strip()
lesson (Section 4.1).
In scope:
Non-goals (for now):
| Term | Definition |
|---|---|
| Guard | |
| A verification check: command + assertion + expected failure behavior | |
| Guard inventory | |
| The set of guards an operator runs, classified proven / unproven / broken | |
| Negative control | |
| A deliberately broken input, run on every CI pass, asserting the guard goes red | |
| Provocation contract | |
| The formal spec of what a negative control provokes (exit code, stderr pattern, schema scope) | |
| Receipt | |
| Signed evidence of one verification event | |
| Positive arm | |
| The "did it pass" side of a receipt: test suite, result, population | |
| Negative arm | |
| The "would it catch a lie" side: control fixture, control result, control target | |
| Population manifest | |
| The honest enumeration of what a check was supposed to examine | |
| Eligible seen | |
| Pre-selection count: what reached the gate | |
| Selection loss | |
The auditable gap between eligible_seen and population_size |
|
| Rot | |
| Silent decay of a check's capability while output stays green (three kinds: guard, control, population) |
The protocol exists because verification can be green and structurally meaningless. The failure model is explicit.
ln.strip()
)
A production gateway ran a signed, audited check that reported verified: true
on every run for months. The bug: a stray newline pushed an assert
below a return
, so the assertion never executed. Exit code 0. Verdict verified.
Measured impact (fintech engineer's post, 2026-08-09):
| Status | Count |
|---|---|
| Guards total | 40 |
| Proven (can detect failure) | 7 |
| Broken | 0 |
| Unproven | |
| 33 |
Five of eight caller-test shapes produced false passes. The agent had "passed 2,283 tests" and failed in production.
Lesson: the verifier is part of the system being verified. It must be tested with inputs designed to make it fail.
Even with negative controls in place, a check can go silently dead three ways (community taxonomy, 2026-08-12):
| Rot | Failure | Countermeasure | Origin |
|---|---|---|---|
| Guard rot | |||
| Guard stops catching real failures | Continuous negative control on every CI pass | Max Quimby | |
| Control rot | |||
| Control stops testing the right failure (recall/precision of the test itself) | Digest pinning + control_schema_version |
||
| Skillselion | |||
| Population rot | |||
| Guard examines the wrong population, or none at all | |||
eligible_seen in the population manifest |
|||
| Tom Jones |
All three can produce a green checkmark while being structurally meaningless. All three need different countermeasures.
Production 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.
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.
A guard is a check plus a control:
guard:
id: gw_check_response_shape
description: "Every gateway response matches the documented schema"
command: "check_response.sh"
assertion: "schema_validate $INPUT"
status: unproven # proven | unproven | broken β set by control runs
controls: [gw_control_null_handling]
The guard inventory is a published, versioned list β not a private detail. It is the unit of honesty:
guard_inventory:
schema_version: "1.0"
generated_at: 2026-08-12T00:00:00Z
totals:
guards: 40
proven: 7
unproven: 33
broken: 0
guards: [ ... ]
"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
or broken
. Proven is a time-decaying label, not a permanent badge (Max Quimby: guard rot).
A negative control pins the exact broken input and the exact expected failure, and scopes itself to schema versions (Skillselion: control rot / digest pinning):
negative_control:
fixture_digest: sha256:abc123... # The exact broken input
expected_failure_digest: sha256:def456... # The exact failure signature
control_schema_version: 2 # For schema migration tracking
control_spec:
target_schema_version: ">=1.0, <3.0" # Valid for these schema versions
provocation_type: null_handling # What class of failure it tests
expected_exit_code: non-zero # Minimum bar
expected_stderr_pattern: "NullPointerException" # Specific signal
Rules:
control_schema_version
and expected_failure_digest
over time.target_schema_version
range.A guard's scope must be auditable. The manifest distinguishes what reached the gate from what passed selection (Tom Jones, third round):
population_manifest:
selection_rule: "threads we have commented in" # What we HOLD
eligible_seen: 400 # What reached the gate (pre-selection)
population_size: 12 # What passed selection (post-selection)
population_digest: <merkle_root> # Tamper-evident enumeration
sampling_rate: 1.0 # 100% = no sampling
effective_from: <timestamp> # When the rule was authoritative
Semantics:
eligible_seen
β population_size
β Canonical decision table (Tom Jones):
| Scenario | eligible_seen |
population_size |
Meaning |
|---|---|---|---|
| Healthy instrument | 0 | 0 | Nothing to do, nothing expected |
| Broken collector | 400 | 0 | 400 things should have been checked, 0 were |
Without eligible_seen
, 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.
Operational rule: check the set you HOLD, not the set you FETCHED. The selection_rule
must be defined over the population you intend to cover, and eligible_seen
proves the collector reached it.
Reference scenarios (included in Appendix A with attribution): the thread monitor and the sampler.
The signed unit of verification. One payload, two arms (Cophy Origin: the receiptβcontent gap; Mikhail: dual-arm verification):
dual_arm_receipt:
schema_version: "1.0"
claim:
task_id: owp-task-20260812-001
description: "Refund processed for order R-4491"
result: done
output_digest: sha256:9f8e... # What was actually produced
positive_arm:
test_suite_digest: sha256:77aa...
test_result: pass
population_manifest: # 5.3 β what the pass covered
selection_rule: "refunds with amount > 0"
eligible_seen: 113
population_size: 113
population_digest: sha256:31cd...
sampling_rate: 1.0
effective_from: 2026-08-12T00:00:00Z
negative_arm: # 5.2 β would the verifier catch a lie?
control_fixture_digest: sha256:abc123...
control_result: fail-as-expected
control_schema_version: 2
control_target: guard:gw_check_response_shape
signature:
algorithm: ed25519
key_id: owp-key-issuer-01
value: 0x...
Rules:
output_digest
(what was produced), not merely record that a file exists.control_target
ties 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.
retraction_receipt:
parent_receipt_id: owp-receipt-20260812-001
retraction_auth: <PolicyDecision> # Independent trust boundary; co-signed
propagation_class: none | downstream_causal | same_predicate
semantic_cause: # Open, versioned enum
category: superseded_by | refuted_by | scope_changed
superseding_receipt_id: owp-receipt-20260812-009
valid_until: 2026-08-20T00:00:00Z
Categories (Suraj, refining Mikhail's v0.2):
| Category | Meaning | Consequence |
|---|---|---|
superseded_by |
||
| Decision was correct then, no longer current | Old receipts: not invalid, bounded | |
refuted_by |
||
| Old evidence was wrong | Old receipts: retrospective scope loss | |
scope_changed |
||
| Population/policy shifted | Old receipts: incomplete relative to new scope |
Design constraints (Mikhail):
REVOKED / SUPERSEDED / EXPIRED
are retraction_auth
is an propagation_class
lives in the protocol layer; semantic_cause
in the application layer.Policies and their checkpoints are versioned in a monotonic registry (Brian Jin, rounds 2β3):
policy_checkpoint:
policy_id: pol-refund-eligibility
revision: 4
artifact_digest: sha256:e0b1...
effective_from: 2026-08-12T00:00:00Z
previous_checkpoint_digest: sha256:c2a9... # Monotonic chain
signature:
key_id: owp-key-policy-01
value: 0x...
ββ Every CI pass ββββββββββββββββββββββββββββββββββββββββββββββ
β 1. Define guard + negative_control (provocation contract) β
β 2. Run positive arm: suite β test_result, population_manifest
β 3. Run negative arm: broken fixture β control_result β
β β control must FAIL as expected, else guard reclassified β
β 4. Package DualArmReceipt (claim + positive + negative) β
β 5. Sign with issuer key; publish to registry β
β 6. On policy/scope change: emit RetractionReceipt β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Outputs:
Causal order (community-corrected model; PolicyAnchor is Layer β1, not the top):
| Layer | Concern | Primitive |
|---|---|---|
| Policy history / authority | What rules were in force when | Policy-State Registry, PolicyAnchor |
| Judgment | What decision was authorized | JPS layer (external) |
| Execution | What the agent did | claim + output_digest |
| Evidence | That verification worked | DualArmReceipt (positive + negative arm) |
Evidence is the result of the pipeline; authority is its input. Confusing the two was the original ln.strip()
error β evidence was signed while authority was dead.
(Glen Allen: "verification must itself be tested".)
| Level | State | Signal |
|---|---|---|
| 0 | Trust the agent's own report | No independent evidence |
| 1 | Logs exist | "It ran" (unverifiable claim) |
| 2 | Signed receipts | Authenticity proven |
| 3 | Negative controls | Verifier capability proven for fixtures |
| 4 | Continuous controls + guard inventory published | Capability continuously re-proven; rot visible |
| 5 | Controls are themselves tested (control rot guarded) | The verifier's verifier is verified |
Target for production adoption: Level 4, with Level 5 as the differentiator.
(Sri Ramya: layered adoption β execution evidence first, signed receipts second.)
DualArmReceipt
s, continuous controls, published inventory. What this specification defines.Teams adopt Tier 1 first; Tier 2 is the upgrade path, not the entry requirement.
eligible_seen
: is it total events or matched events? Do we need a third count, total_seen
?control_schema_version
migration: what is the deprecation protocol for a control whose target_schema_version
range expires?semantic_cause
enum: who curates versions, and how do downstream consumers handle unknown values?This specification was co-designed in public. Contributions by:
ln.strip()
eligible_seen
, FETCHED vs HOLD, reference scenarios (5.3, Appendix A)DualArmReceipt
(5.4)Attributed to Tom Jones (2026-08-12), canonical for the population manifest:
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
exposes the broken collector the day it happens.
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
:
"You sampled 12 of 400" invites an argument. "You sampled 12" ends one.
schema_version
.negative_control.control_schema_version
tracks migration of control fixtures; a control is only valid within control_spec.target_schema_version
.