# Independent plan review — Remozio round-2 plan

Plan-only review. I did not inspect source, run tests, or browse; all assessment is against the text of `reviews/round-2-plan.md` as written. Scope assessed: internal consistency, security boundaries (malicious unprivileged caller in scope; root/phone-OS compromise out), platform feasibility assumptions vs. explicit gates, command binding, Android biometric credential release, UI race handling, recovery and remote-coding UX, and completeness of planned validation. All three adapters kept in scope.

The plan is generally strong: threat-model honesty (§2), anti-confused-deputy design (§3), canonical wire format and single-use signatures (§6), biometric binding and transition testing (§7), sudoers policy gate (§8), and race acknowledgment with residual-risk recording (§9) are coherent. Known hard problems (single-biometric credential release, descriptor-based exec, Secure Enclave in background services, 1Password secure-field targeting, push/relay choice) are correctly scheduled as feasibility gates with user-decision fallbacks, and I do not count those as defects.

## Finding 1 — Medium: undefined terminal state when the GUI adapter dies between dispatch-permit consumption and actuation

**Sections:** 3 ("obtains an atomic one-time dispatch permit from the root ledger immediately before acting. If it crashes after dispatch, the result is unknown and the action is not retried"), 6 (state machine; crash semantics defined only for the *service* after consumption).

**Failure scenario:** A 1Password unlock is approved. The adapter verifies the signature, obtains the one-time permit (approval now consumed in the root ledger), then crashes *before* injecting the credential — or crashes after acting but the plan's "crash after dispatch → unknown" only covers the post-dispatch case. The approval is single-use and consumed, so no new permit can be issued; no surviving actor owns the request; §6 defines no transition for "permit consumed, no dispatch report ever arrives." The request has no defined path to succeeded/failed/unknown, conflicting with §1's contract ("The phone reports success, failure, or an uncertain outcome") and §10's acceptance goal of "no unexplained silent stalls."

**Minimal correction:** In §6, add a root-service watchdog rule: a consumed dispatch permit with no outcome report within a bounded window transitions the request to **unknown**; because the action may never have happened, the still-present dialog may generate a *fresh* request requiring new user approval (this is resubmission, not an automatic retry of a consumed action, so it does not violate the no-retry rule). Add the corresponding acceptance case (adapter killed after permit consumption → unknown reported, no silent hang, no replay).

**UX impact of correction:** none — no extra confirmations or restrictions in the normal path.

## Finding 2 — Low: expiry/renewal behavior for persistent UI dialogs is unspecified

**Sections:** 5 ("permit explicit resubmission"), 6 ("expired [is a] terminal branch"), 9 ("Material changes cancel the old approval and replace it with a new request" — replacement defined only for material changes).

**Failure scenario:** A Little Snitch alert or 1Password unlock dialog remains open past the provisional five-minute expiry. For command requests, resubmission has a natural initiator (the caller re-runs `remozio sudo`). For UI requests there is no caller; the adapter still observes the dialog, but the plan never states whether the adapter re-issues a fresh request for a still-present dialog or how the phone initiates resubmission of an adapter-originated request. A user approving at minute 6 gets a stale result with no defined route back to an actionable request, leaving a genuinely pending dialog invisible to the phone.

**Minimal correction:** In §9 (or §6), state that when a request expires while its dialog is still present, the adapter either re-issues a fresh request subject to §5's per-Mac notification rate bounds, or the expired phone entry offers explicit one-tap resubmission. Either is consistent with the existing state machine.

**UX impact of correction:** none — resubmission is user-visible and optional; no added biometric or confirmation in the normal path.

## Finding 3 — Low: executable-identity TOCTOU has no named fallback if descriptor-based execution proves infeasible

**Section:** 8 ("File identity/hash checks narrow accidental substitution but are not atomic guarantees… Spike descriptor-based execution on macOS").

**Failure scenario:** A malicious unprivileged caller (in scope per §2) obtains approval for a caller-writable script, then substitutes content between the pre-execution identity check and `execve`. The plan acknowledges the race and gates descriptor-based execution — but macOS has no `fexecve` equivalent, so this spike may well fail, at which point the only stated recourse is the generic §10 rule "feasibility failures require a user-visible decision," leaving a security-relevant default undecided despite an obvious non-UX-worsening option.

**Minimal correction:** Name the fallback now: on spike failure, the privileged service copies the verified entry executable (or script) into root-controlled storage and executes the copy, with the already-disclaimed transitive-dependency caveat unchanged and the signed payload still displaying the original path. This preserves identical approval UX and converts an open decision into a documented default with a known limitation.

**UX impact of correction:** none — no command restrictions, allowlists, or added steps (an allowlist would require user consultation; this does not propose one).

## Explicitly assessed and found adequately planned (not findings)

- Single-biometric credential release uncertainty — correctly gated with a user-consultation fallback (§7 spike); flagging it as a defect would demand implementation evidence from a draft plan.
- sudoers semantic preservation — explicitly gated with two honest options and installation disclosure (§8).
- Accessibility non-atomicity and residual risk — acknowledged with deliberate race tests and a user-consultation clause rather than silent feature removal (§9).
- Flux licensing — correctly handled: design reference only, no source copying until license resolved (§4), matching the review constraint.
- Denial/cancellation unforgeability-as-approval, replay/expiry/consumption rejection, transport-key separation, relay metadata exposure, and recovery without the old phone — internally consistent across §§3, 5, 6.

## Conclusion

**SUBSTANTIVE FINDINGS REMAIN** — one medium (dispatch-permit crash-window reconciliation, §§3/6) and two lows (UI-request expiry renewal, §§5/6/9; TOCTOU fallback naming, §8). All proposed corrections preserve the normal notification → details → single biometric approval → result flow and add no confirmations, restrictions, or manual steps; none require user consultation under §1's rule. Remaining verification limits: this review cannot confirm macOS/Android API behavior, AX-tree feasibility, or Keystore/Secure Enclave semantics — those remain subject to the plan's own stage-1 gates and later implementation audit.
