Skip to content

spec: adversarial hardening of the Assay — measured trust, not asserted - #278

Merged
mdheller merged 1 commit into
mainfrom
spec/assay-adversarial-hardening
Aug 4, 2026
Merged

spec: adversarial hardening of the Assay — measured trust, not asserted#278
mdheller merged 1 commit into
mainfrom
spec/assay-adversarial-hardening

Conversation

@mdheller

@mdheller mdheller commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Self-review of the merged Assay (#198) — I red-teamed my own work and found four defects. This fixes all four and documents a fifth as intentional.

Findings & fixes

# Severity Defect Fix
F1 🔴 Critical The verifier's conclusion was never recorded — only its reliability. bad was reachable only via broken integrity, so a claim refuted by a calibrated verifier (clean integrity) could not project bad. Add verifier.judgment (supported/refuted/abstained). refuted + calibrated → bad; refuted + uncalibrated → sad (an uncalibrated judge cannot force a verdict).
F2 🔴 Critical (ironic) AssayStandard.calibrated was a self-asserted boolean — the exact assert-vs-measure anti-pattern the framework exists to kill, on the one flag that gates ok. Add explicit calibrationThreshold; the validator recomputes F1 from confusionMatrix and rejects any record whose calibrated disagrees with (derivedF1 >= threshold).
F3 🟠 Major Declared metrics (f1/precision/recall) were never checked against the confusion matrix. Validator recomputes and rejects contradictions (tolerance 0.02).
F4 🟠 Major agreement.effectiveVotes could exceed arms, defeating the decorrelation discount. Validator rejects effectiveVotes > arms.
F5 🟡 By design The projection doesn't consume the agreement axis for ok. Documented as intentional (a deterministic computed+inline result needs no vote) in schema + README, rather than left silent.

Verification

tools/validate_reasoning_examples.py is green, and every new gate was confirmed to bite via negative tests:

  • lying calibrated: true on an F1-0.26 standard → rejected
  • bogus f1: 0.95 contradicting the matrix → rejected
  • arms: 1, effectiveVotes: 99rejected
  • refuted + calibrated → bad; refuted + uncalibrated → sad

Adds a genuinely-uncalibrated AssayStandard fixture (deployed-nli, F1 0.26, κ "slight") so the sad-path references real recorded-but-weak reliability instead of an absent lookup.

Theme

The original framework measured a claim's trustworthiness but let the judge assert its own. This closes that loop: trust is measured and cross-checked at every level, never asserted — the principle applied to the judges themselves.

Note: sourceos-spec auto-merges on green CI, so this is expected to land automatically; flagging for post-merge visibility.

Self-review of the merged Assay found four defects; this fixes them.

F1 (critical): the verifier's *conclusion* was never recorded — only its
reliability. 'bad' was reachable solely via broken integrity, so a claim
refuted by a calibrated verifier (clean integrity) could not project bad.
Add verifier.judgment (supported|refuted|abstained); refuted+calibrated -> bad,
refuted+uncalibrated -> sad (an uncalibrated judge cannot force a verdict).

F2 (critical, ironic): AssayStandard.calibrated was a self-asserted boolean —
the exact assert-vs-measure anti-pattern the framework exists to kill, on the
one flag that gates 'ok'. Add explicit calibrationThreshold; the validator now
recomputes F1 from confusionMatrix and rejects any record whose calibrated flag
disagrees with (derivedF1 >= threshold).

F3: declared metrics (f1/precision/recall) were never checked against the
confusion matrix. Validator now recomputes and rejects contradictions.

F4: agreement.effectiveVotes could exceed arms, defeating the decorrelation
discount. Validator now rejects effectiveVotes > arms.

F5 (documented): the projection intentionally does not consume the agreement
axis for 'ok' — a deterministic computed+inline result needs no vote. Noted in
schema + README rather than left silent.

Adds a genuinely-uncalibrated AssayStandard fixture (deployed-nli, F1 0.26) so
the sad-path references real recorded-but-weak reliability. All new gates
verified biting via negative tests.
@mdheller
mdheller merged commit 1aa0b28 into main Aug 4, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant