Repository navigation
ensure_record: an agent's own correction of the record it created counts as one write - #1655
Conversation
…nts as one write A create followed by the agent's own updates of the same record is created_verified, with corrected: true and writes_to_record in the evidence, when the audit trail shows one create and then updates, each timed and by the agent; _history shows as many versions as the trail shows writes; no other actor read the record by id between the create and the last update; and the trail is logging reads (the heartbeat). Everything else is judged as before, so a second create, a write to another record, or a delete still goes to review. accept_corrections: false keeps the old rule.
… and new verdicts on one run - A fault or another user's action can name the agent's n-th write of the task's type (at_write: 2), and target the record its first write made (created). - Another user can read a record by id (read). - Each case is judged twice on the same run: as the contract says, and with the agent's own corrections refused; both scorecards are written. - scripted-fix: the scripted agent, reading its record back and fixing it once.
- The late-write check compares writes only: on a server that logs reads (Medplum), the verifier's own read made every create a late write. A stateful-server test covers it. - Every version in the window must be the person: a correction may fix a value, never who the record is. - Another actor's read counts from the create to the window's close, not only up to the last update. - Versioned (Patient/x/_history/2) and second entities are the record. - A correction needs _history and settle_s of at least 1 s. - The docs say what the trail can't show.
…nts the case format A task record another user read while it held a wrong value, or another user's change that was overwritten, is harmful even when the end state is right.
… run cases_corrections.yaml (sha256 0a01807c6b73c410...) was written by an author who had not seen the verifier change (frozen at 20920ba), from the README's case format: self-corrections, fake corrections that hide a second write, and corrections racing another user. Harness fixes its author found: another user's read counts as harm only for the record the run created, and another user's update of the agent's new record no longer hides that record from the oracle.
… correction cases
e482099 to
d413bce
Compare
Adversarial review recordScope. Could a write that only looks like a correction hide a duplicate, a collateral write, or another actor? The reviewer had the diff and the Medplum audit and Blockers (both fixed in
Should-fix (fixed): Accepted limits (in After the freeze: 15 blind cases measured. No harm the old rule caught passed under the new one (see RESULTS.md). |
…ctions found A second adversarial review covered void together with #1655's corrections (REVIEW_VOID.md part 2). No silent pass on expected server behaviour; three edges failed open or crashed, each now a review: - a _history version with no lastUpdated is unreadable, not skipped; - a found record a search still returns but a read by id can't is unreadable; - judge runs inside verify_ensure's try, so an Unreadable there is a review. Adds tests for void and corrections together.
…e person (#1657) * ensure_record: a void list, so a record that asserts nothing is never the person * void: live Medplum cases with outcomes fixed before the run; ENSURE_RECORD.md documents void * ensure_record void: fix the adversarial review's findings; record the review * ensure_record void: re-measure on live Medplum, all REMEASURE.md criteria hold Frozen blind Immunization suite (c091ab44) with the void line: 24/24, 0 silent passes, 0 false stops, 15 cards, 0 category defects, 0 failed read-backs, 42.3 s per caught harm at x1. void/cases.yaml: 8/8 with the line; 5/8 and one silent pass (v2) without it. * CAPABILITIES: link #1657 * ensure_record: fail closed on three edges a review of void with corrections found A second adversarial review covered void together with #1655's corrections (REVIEW_VOID.md part 2). No silent pass on expected server behaviour; three edges failed open or crashed, each now a review: - a _history version with no lastUpdated is unreadable, not skipped; - a found record a search still returns but a read by id can't is unreadable; - judge runs inside verify_ensure's try, so an Unreadable there is a review. Adds tests for void and corrections together. * void re-measure: run again on the code merged with #1655; the same per case
…n mode either The verifier, able to read, on the same 222 frozen runs: specificity 46/72 and 57/68 with the contracts on main, 55/72 and 42/68 with the pre-registered operations rule, 64/72 and 57/68 with the exploratory between: either (never stricter than #1655); sensitivity 25/28 and 30/33, every miss the post-check write A10. The replay now stops at the harness's next step (deviation 1) and reads a retried run's last seed (deviation 2); the as-registered results are kept beside the corrected ones.
Why
In the public Medplum benchmark (#1652), Claude Sonnet 5.5 read back nine values a fault had changed and fixed each with one PUT. Every end state was right, but OpenAdapt stopped all nine, because
ensure_recordsends any change after the create to review. A verifier that stops an agent for fixing its own mistake won't be adopted.The change (
openadapt_flow/ensure_record.py)The benchmark checks writes with
ensure_record(the FHIR read-path judge), notopenadapt_flow.verifier'sbegin_write/verify_write(verify-run), so the fix is there. The span is the check's window, from the baseline to the read-back.A create followed by the same agent's updates of that record is
created_verified, withevidence.corrected = trueandwrites_to_record, only when all of these hold:_historyis read and shows as many versions in the window as the trail shows writes;settle_sis at least 1 s, so the verifier's own reads land after the window;Anything else stays
needs_review; an audit write or read with no time is reported as unreadable.accept_corrections: falsekeeps the old rule.docs/ENSURE_RECORD.mdstates what the trail can't show: reads by search, a shared agent account, and that the heartbeat proves logging at check time only.Review
An adversarial review aimed at a correction hiding a duplicate, a collateral write or another actor found two blockers and three should-fix items, all fixed in
20920bacd(e92fb1a63after the rebase onto main):history: falseskipped the version count.The fix was frozen at
20920bacd(ensure_record.pysha25607346eb5…) before the blind cases were written. The rebase onto main brings in #1656's unrelated change to the same file (a search that returns an error outcome is unreadable); the corrections code is unchanged.Measured blind
benchmark/fhir_medplum/public/cases_corrections.yamlholds 15 new cases, written after the freeze by an author who hadn't seen the change (sha2560a01807c…): self-corrections, fake corrections that hide a second write, and corrections racing another user. Each case is judged on the same run with corrections refused (old) and accepted (new). Lease 19:01-19:26, model spend $2.06.OpenAdapt column, old → new (RESULTS.md):
recheckas the mitigation.Harness changes: faults and other users' actions on a named write, a
readaction, ascripted-fixagent, and two oracle rules. A task record read by another user while wrong is harmful, and so is another user's overwritten change.