Skip to content

feat: complete qualification and attended phone workflow - #93

Open
abrichr wants to merge 10 commits into
mainfrom
feat/desktop-reconcile-attended
Open

feat: complete qualification and attended phone workflow#93
abrichr wants to merge 10 commits into
mainfrom
feat/desktop-reconcile-attended

Conversation

@abrichr

@abrichr abrichr commented Jul 29, 2026

Copy link
Copy Markdown
Member

Summary

  • connect Desktop qualification, deployment, run history, and correction paths
  • add the signed self-hosted phone decision portal with pairing and full retained evidence
  • support reconcile without retry for uncertain delivery
  • show one focused request, one evidence view, and a distinct result while preserving all six request types
  • generate deterministic mobile presentation fixtures outside the package

Verification

  • 136 portal Python tests pass
  • 77 UI tests pass
  • TypeScript and Vite production build pass
  • Ruff and source syntax checks pass
  • six request fixtures and all action-result fixtures were rendered with the public OpenEMR reference frame

abrichr and others added 10 commits July 28, 2026 13:27
The runner-local decision portal serves the whole local `presentation` --
screenshot crops, the gated control label, the full halt detail -- and reaching
it from a phone requires an HTTPS origin the customer terminates themselves
(`engine/portal/ingress.py`, `customer_ingress`). That is right for an
organisation with an IT department and unavailable to a dental practice.

Flow's hosted lane needs none of it: the runner dials OUT, so there is no
inbound port, no port forward, no certificate, no reverse proxy and no static
address, and it works behind NAT on ordinary practice broadband. Desktop just
has to turn it on.

`--remote-decisions` is now passed to the attended console when, and only when,
the operator's deployment sets `human_decisions.remote.enabled: true`.
`PreparedPrivateYaml` carries that answer from the SAME snapshot it serializes,
rather than re-reading the operator's file: a second read would reintroduce the
exact TOCTOU gap that class exists to close, and could launch Flow with the
outbound lane on while it executes a config that never enabled it.

`enabled` must be literally `true`. A truthy string or a 1 means no. An outbound
lane that carries decision context is never inferred.

The runner credential goes to the child process in its environment, never as an
argument, where it would sit in the process table for every user on the machine.

Both preconditions are checked BEFORE the spawn, because the failure mode
afterwards is indistinguishable and useless: an unknown flag makes argparse exit
before the capability banner, and Desktop reports "the local decision service
did not start" -- naming neither the missing registration nor the old runtime.
So a deployment that enabled phone decisions on a computer that was never
registered with the control plane, or on a Flow older than the flag, stops with
a sentence that names the fix. It does not start a console whose phone lane is
silently absent, which is worse than the gap because nothing on either surface
says the phone will never ring.

`MIN_FLOW_FOR_REMOTE_DECISIONS` is 1.26.0. The installer currently pins 1.25.0,
so the refusal is the whole behaviour until that pin moves; a deployment that
did not enable remote decisions is untouched either way.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NyCHrzA1psrKMFfroYbzaM
…y one

The page opened by saying this portal is how a halt reaches a phone. That was
true when it was written and is now the narrower half of the answer: it serves
protected screen crops, which is exactly why it needs an HTTPS origin the
customer terminates themselves -- and why it is unavailable to the practice
this product is for.

Names the hosted lane, what Desktop does to turn it on, and the two
before-the-spawn refusals, so an operator reading this page is not left
believing an ingress is the only route to a phone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NyCHrzA1psrKMFfroYbzaM
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