Repository navigation
problems_only switch policy: input problems leave it on, a person re-arms with one signed tap - #1517
Merged
Merged
Conversation
…program problems turn it off, a person re-arms with one signed tap - post.problem_class sorts every problem. Item-input problems caught before anything was written (a missing or invalid value refused before the first step, an input item check, a real duplicate candidate) send the item to a person and leave the switch on. Program and environment problems, and any unnamed cause, turn it off at once. - A switched-off switch never re-arms itself. After 10 consecutive verified items, a person's approver key signs a re-arm over the off event, its cause and those items; the log records it, and a gate honours it only with the key pinned. A forged, tampered or early re-arm is ignored. - The gate's count of unapproved delivered saves now uses the certifying policy, as its own save check does. A step the certified qualification classified as not saving made every approved, verified item a problem. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…arm_to_sign, rearm_evidence) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
abrichr
added a commit
that referenced
this pull request
Oct 25, 2026
- The re-arm card shows Flow's re-arm request: why the switch went off, when, and the 10 items saved and checked in a row since, named. Its id covers the request, so a re-arm signs only what the person saw. - Answering it is one Touch ID over Flow's own bytes (rearm_to_sign); Flow verifies against the approvers registry, never a key Desktop supplies. Desktop's switch view honours a re-arm by a registered approver. - Feature-detected: the card appears only with a Flow that has signed re-arm (#1517). Checked against #1517's real switch log. - A locked Mac no longer reads as having no Secure Enclave: the check isn't remembered, and the refusal says to unlock the Mac. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s by default The run gate reads the approver keys registered in the JIT key directory's principal registry (hex, as jit register --role approver writes them), so a re-arm signed with Desktop's approver key counts at run time with no extra configuration. pinned_approver_keys still names keys explicitly. An unreadable registry, or one with no approvers, gives no keys, and a switched-off switch stays off. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- An explicit switch-off from the gate, R2 or the engine is never classed down to an input problem. - A duplicate stop counts as an input problem only from the duplicate check's structured outcome (StepResult.duplicate_check == 'candidate', set by the record-channel check, #1516), never from the error text. An unreadable list or channel turns the switch off (record_check_lost); a duplicate stop that doesn't say is a program problem. - A re-arm's approver must be its key's registered name. - Only a re-arm that verifies ends an off period, so a forged event can't hide a switch-off or reset the count. - Trusting the registered approvers by default is documented as deliberate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…uplicate candidates Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
abrichr
added a commit
that referenced
this pull request
Oct 25, 2026
StepResult.duplicate_check is set whenever no_duplicate_candidate fails: exact or close (a record matched) and candidate (a listed row matched) are about the item; unreadable (the record channel or the screen list could not answer, or a record-pinned check had no channel) is about the program. None otherwise and then omitted, so existing reports serialize unchanged. #1517 classes the problems_only switch from it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… the pinned root #1486 moved approvers out of principals.json into a signed roster. The gate's default approver keys are now the roster entries the run's pinned root vouches for (approver_keys_b64, pinned_root), imported by name so this works before and after #1486 merges; with no roster, no pinned root, or a roster that doesn't verify, no re-arm counts. 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.
This PR implements the coordinator's decision on the
problems_onlyswitch policy: classify every problem, and let a person re-arm only with a signed tap. It is stacked on OpenAdaptAI/OpenAdapt#1514.Why: in the measured soak (OpenAdaptAI/OpenAdapt#1513), one caught problem turned the switch off for the rest of the run. Every later save then needed an approval: 25 of 125.5 operator minutes.
1. Which problems turn it off (
post.problem_class,classify_decision)Item-input problems leave the switch on. The item goes to a person and doesn't count toward
too_many_problems. These are problems caught before anything was written:inputs_supplied,inputs_strict,channel);StepResult.duplicate_check == "candidate", which the record-channel check in Duplicate check asks the system of record (FHIR/REST, SQL fallback) instead of the screen list #1516 sets. It is never read from the error text. An unreadable list or channel ("unreadable") turns the switch off withrecord_check_lost. Until Duplicate check asks the system of record (FHIR/REST, SQL fallback) instead of the screen list #1516 sets the field, a duplicate stop is a program problem.Program and environment problems turn it off at once. For example:
Any outcome that doesn't consistently read "nothing was written" is a program problem (fail closed). A switch-off the gate, R2 or the engine already named is never classed down to an input problem. Gate pre-check failures map to closed off reasons:
signed_in_user→environment_changed,right_recordandsame_screens→screen_changed,record_check_ready→record_check_lost.too_many_problemsstays as a backstop.2. A person re-arms with one signed tap (
switch.rearm_request,sign_rearm,rearm)no_personorapproveditems; any other finished item resets the count.rearmedevent with who signed, the key, and what they saw. The field is left out of every other event, so existing log lines and hashes are unchanged.jit register --role approver, fix: a person's approval needs a registered approver key's signature #1486), and the registry refuses agent and model names.ApprovalGate(pinned_approver_keys=...)names keys explicitly instead. Keys may be hex (the registry's form) or base64.approver_idmust equal itskey_id(the key's registered name).rearm_to_signreturns the exact bytes, andrearm_evidencewraps a signature made elsewhere. Desktop's Touch ID card (feat(desktop): 0.24.0, the Touch ID approver key and one-tap stop review #1519) uses these.approvers, so no re-arm counts.3. A bug the soak exposed
_unapproved_delivered_savesclassified saves without the certifying policy, as the gate's own save check did before #1455. On OpenEMR, "Create New Patient" (certified as not saving) counted as an unapproved delivered save. So after the switch turned off, every approved, verified item was logged as a problem (save_not_approved), and it could never have counted toward a re-arm.Tests
test_approval_mode_switch_policy.py(23). Covers:uncertain_result);rearmedevent appended to the log is ignored.🤖 Generated with Claude Code