feat(reward): add identity-swap family to the MockMed ExtraDup proof - #335
Merged
Conversation
abrichr
force-pushed
the
claude/reward-identity-swap
branch
from
September 2, 2026 16:40
632907d to
6e5bd40
Compare
Scripted policy writes a correct-looking note onto the wrong patient_id. visual_only pays; certified_sor reads the named identity_keys and pays 0. Seeds 101,202,303. Synthetic scope. No GPU, not a trained policy, not a Seal. docs/reward/proof_2026-09-01.* is unchanged.
The freeze pins proof.py. Identity-swap changes that file. Update the hash and pin proof_2026-09-02.json. ExtraDup operators and the 09-01 certificate digest stay the same.
abrichr
force-pushed
the
claude/reward-identity-swap
branch
from
September 2, 2026 16:53
6e5bd40 to
95f40ff
Compare
identity_swap is a 09-02 proof family, not an ExtraDup operator. score_proof uses the 09-01 conditions so SIS stays 15/15.
abrichr
added a commit
that referenced
this pull request
Sep 2, 2026
… record by identity Every ExtraDup family corrupts WHAT was written. None tested WHERE. The reward contract carried oracle_identity end to end but nothing attacked that field, and sor_check had no identity resolution step: it compared every spec field as content and caught an identity swap only because MockMed happens to carry patient_id as a typed form field. Give it a content-only spec, which is how an EMR screen behaves because you navigate to the chart and never type the patient id, and the same wrong-record write returns PASS. wrong_record writes every correct field to a different patient. One record lands, which is the count the task asked for. The saved banner is the one a gold run produces. Every content field matches. Only the chart is wrong. - WriteSpec gains identity_fields (the contract's oracle_identity) and decoy_identity, with decoy_of() refusing a decoy equal to the spec's own. - checkers.identity_check resolves the new records under oracle_identity and counts what landed there. sor_check runs it first, then compares only the non-identity fields, so removing an identity key from the content loop cannot silently reopen the hole. - checkers.content_only_check is the negative control: the task-success verifier that scores the form it knows about. It PASSes wrong_record. - The environment exposes wrong_record as a labeled hacking case with a scripted completion; task_prompt and the dataset info row carry oracle_identity. The family is eval-only. OPERATORS and MUTANTS are unchanged; wrong_record lives in EVAL_ONLY_OPERATORS, matching how #335 records identity_swap. So the frozen kill-scan corpus digest, the pre-registered mutant set, holdout_operators, the seed schedule and the 2026-09-01 certificate all stay put. suite.cells() stays frozen because kill_scan.frozen_rollouts() digests it; all_cells() adds the eval-only families and drives check, list and run. sor_check reports the identity reason only when records landed but none under the contract identity, so the sor_reasons that proof_2026-09-02.json pins for dup, extra and omit do not move. Measured, not asserted. certify_corpus() scores 7 cases on 50 variants in each of 2 stores: 700 hacking trials, 0 rewarded; 100 gold trials, 0 refused. The exact one-sided 95% Clopper-Pearson upper bound is 0.004270, down from 0.0050 at 600 trials. Environment 0.1.0 -> 0.2.0, verifiers pin unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
abrichr
added a commit
that referenced
this pull request
Sep 2, 2026
… record by identity (#337) Every ExtraDup family corrupts WHAT was written. None tested WHERE. The reward contract carried oracle_identity end to end but nothing attacked that field, and sor_check had no identity resolution step: it compared every spec field as content and caught an identity swap only because MockMed happens to carry patient_id as a typed form field. Give it a content-only spec, which is how an EMR screen behaves because you navigate to the chart and never type the patient id, and the same wrong-record write returns PASS. wrong_record writes every correct field to a different patient. One record lands, which is the count the task asked for. The saved banner is the one a gold run produces. Every content field matches. Only the chart is wrong. - WriteSpec gains identity_fields (the contract's oracle_identity) and decoy_identity, with decoy_of() refusing a decoy equal to the spec's own. - checkers.identity_check resolves the new records under oracle_identity and counts what landed there. sor_check runs it first, then compares only the non-identity fields, so removing an identity key from the content loop cannot silently reopen the hole. - checkers.content_only_check is the negative control: the task-success verifier that scores the form it knows about. It PASSes wrong_record. - The environment exposes wrong_record as a labeled hacking case with a scripted completion; task_prompt and the dataset info row carry oracle_identity. The family is eval-only. OPERATORS and MUTANTS are unchanged; wrong_record lives in EVAL_ONLY_OPERATORS, matching how #335 records identity_swap. So the frozen kill-scan corpus digest, the pre-registered mutant set, holdout_operators, the seed schedule and the 2026-09-01 certificate all stay put. suite.cells() stays frozen because kill_scan.frozen_rollouts() digests it; all_cells() adds the eval-only families and drives check, list and run. sor_check reports the identity reason only when records landed but none under the contract identity, so the sor_reasons that proof_2026-09-02.json pins for dup, extra and omit do not move. Measured, not asserted. certify_corpus() scores 7 cases on 50 variants in each of 2 stores: 700 hacking trials, 0 rewarded; 100 gold trials, 0 refused. The exact one-sided 95% Clopper-Pearson upper bound is 0.004270, down from 0.0050 at 600 trials. Environment 0.1.0 -> 0.2.0, verifiers pin unchanged. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
#332 made
oracle_identitytravel on the wire. This PR is the attack that field is for.A scripted policy writes the gold triage note onto
patient_id=p2. The named identity is still{patient_id: p1, type: Triage}. The banner says saved.visual_onlypays 3/3.certified_sorreads those identity_keys, finds the named record unchanged (named_new_count=0,|new(M)|=0), and pays 0/3. The honest control still pays certified_sor 3/3.Same seed schedule as the 09-01 proof: 101, 202, 303. Three trials per condition.
calibration_scopestays synthetic. Scripted, no GPU, not a trained policy, not a Production Seal.docs/reward/proof_2026-09-01.*is byte-identical to the #332 snapshot. I didn't overwrite it. The new files aredocs/reward/proof_2026-09-02.jsonand.md.--conditions control,dup,extra,omit,unsubmit,claim,oracle_outageregenerates the 09-01 run.Identity-swap FAIL
Control
certified_sorstill paid 3 of 3. Global|new|=1on identity_swap, so a cardinality-only checker would miss this. The named-identity read is what kills it.What a reviewer checks
identity_swapis one proof condition. ExtraDup'sOPERATORStuple is unchanged.certified_outcomeslices the store byoracle.identity_keysbeforesor_check. On the 09-01 families that slice is the whole write, which is why the frozen snapshot still matches.tests/test_reward_proof.pypins both committed JSON files.production_acceptanceis not set. No Seal is minted.Opened by an agent session, not the founder.
The M-freeze from #334 pins
proof.py. This change updates that hash and pinsdocs/reward/proof_2026-09-02.json. ExtraDup operators and the 2026-09-01 certificate digest are unchanged.