Repository navigation
fault gate: admit a record check only after it catches its faults - #1445
Merged
Merged
Conversation
A check built from the same place as the save proves little until it has
been seen to fail. The gate runs the check against a test copy of the
system of record, writes seven kinds of bad save to the copy's store
(wrong value per field, look-alike record, unrelated edit, partial,
duplicate, missing, delayed) and lets the check read them through its own
read, end to end through verify-run. A read whose scope can't see a fault
fails the gate.
- openadapt_flow/fault_gate.py: run_gate, check_digest (effects, read
shape and frame; not the store's location), CoverageRecord
(openadapt.check-coverage/v1), AdmissionPolicy, and two public test
copies (MemoryRestCopy, SqliteCopy).
- verify-run: admission: section; checked.coverage in the signed receipt
({killed, applicable, sha256, policy}); verified requires policy met.
- scaffold-verifier --fault-gate: the gate on a copy of --db. Without it,
the recorded-state test is written in the same format, incomplete on
purpose (it can't test a wrong value over the right record or a late
write).
- countersign shows the coverage; qualify ignores admission:.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…o verification-coverage/fault-gate
The fault gate showed that a --db check whose read is scoped to the patient can't see an edit to another patient's row in the same table. Synthesis now adds an unscoped read of the target table, so the frame sees every row the save didn't declare, and the gated check kills every fault in the suite. Co-Authored-By: Claude Opus 5.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.
Part 2 of #1434 (
docs/VERIFICATION_COVERAGE.md). Stacked on #1443; the base is its branch.Problem
A synthesized record check is built from observed values and available reads.
scaffold-verifiertests it by mutating the records the check already read. That tests the judge, not the read. A check whose read is scoped to one patient can't see an edit to another patient's chart, however the judge is written, and the recorded-state test never notices.Change
openadapt_flow/fault_gate.py:run_gateruns a check against a test copy of the system of record. Each case resets the copy, takes the check's baseline, performs the clean save and writes one bad save to the copy's store. The check then reads it through its own read, end to end throughverify-run. The clean save must come outverified; each fault must not.wrong_value(one per declared field),wrong_record(a look-alike),unrelated_edit(another record in the slice),partial,duplicate,missing,delayed(lands after the check's timeout).check_digest: the effects, the read's shape and the frame. It leaves out where the store is and how to sign in, so a copy runs the same check as production, and a narrower read is a different check.CoverageRecord(openadapt.check-coverage/v1,check_coverage.json) andAdmissionPolicy. The public default is a rule, not a tuned number: every applicable fault is caught, and coverage measured only on recorded states is refused. Deployment floors stay private.MemoryRestCopy(an in-memory store over HTTP) andSqliteCopy. Fixture copies for OpenEMR and openIMIS come with the benchmark PR.admission:: under a policy the signed receipt carrieschecked.coverage = {killed, applicable, sha256, policy}, andverifiedrequirespolicy: met. A missing, stale or other-check record blocks it.scaffold-verifier --fault-gateruns the gate on a copy of--db, and a surviving fault keeps the check a draft. Without the flag, the recorded-state test is written in the same format and is incomplete on purpose: it can't test a wrong value written over the right record, or a late write.records_drop_fields: [id]) is a view of the same read, so records declared in one are declared in both. Without it, the frame flagged ascaffold-verifiercheck's own saved row.What the gate found, and the fix
--dbsynthesis scopes the target table's read by the patient (WHERE patient_id = :p0). On a SQLite copy, an edit to another patient's row in the same table survived the gate; the recorded-state test had passed it (test_the_gate_finds_a_read_scoped_to_the_patient). Synthesis now also reads the target table whole, so the frame sees every undeclared row, and the gated check kills every fault (test_scaffold_verifier_on_the_gate_reads_the_target_table_whole). The frame-condition review fixes from #1443 are merged in.Safety review
This adds refusals only: a policy can block
verified, and nothing turns a refusal into a confirmation. Withoutadmission:nothing changes. Still, it touches whenverifiedis issued, so it waits for the coordinator's review with #1443.Tests
tests/test_fault_gate.py(8): a full read kills all 9 applicable faults; a patient-scoped SQLite read letsunrelated_editsurvive; the digest ignores the store location and sees a narrower read; admission problems; verify-run signs the coverage, and a missing or stale record blocksverified; scaffold on the gate.🤖 Generated with Claude Code