Offer the Franka arm's error recovery on a channel a console asks on - #744
Offer the Franka arm's error recovery on a channel a console asks on#744v-positronic wants to merge 1 commit into
Conversation
The arm ran recover_from_errors only inside the driver's own loop, on no channel, and that loop fires only while state().error is set. A latched fault leaves the arm in mode Reflex with error reading 0, so the automatic path never runs and nothing outside the driver can reach it. Add a recover receiver and a recovery_result emitter on franka.Robot. The run loop reads the receiver each tick; an ask runs recover_from_errors and emits whether it cleared. This is the positronic half of internal#1255; the console binds console.recover_arm to recover and reports recovery_result. Ticket: none — positronic half of internal#1255; PR body links it.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4aa0372d01
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| self.recover = pimm.ControlSystemReceiver[bool](self) | ||
| self.recovery_result = pimm.ControlSystemEmitter[bool](self) |
There was a problem hiding this comment.
Couple recovery requests to their replies
Rule hidden-dependency violated:
recover and recovery_result implement one request/reply operation as two independent signals, so a result cannot be correlated with its request and the default size-one signal queues can discard back-to-back requests or replies. Expose a single ControlSystemHandler[None, bool] and answer each incoming call instead.
AGENTS.md reference: AGENTS.md:L7-L8
Useful? React with 👍 / 👎.
| # in libfranka mode Reflex while ``state().error`` reads 0, so nothing else fires recovery. The | ||
| # emitter reports whether the recovery cleared it. | ||
| self.recover = pimm.ControlSystemReceiver[bool](self) | ||
| self.recovery_result = pimm.ControlSystemEmitter[bool](self) |
There was a problem hiding this comment.
Name the result for the boolean verdict
Rule misleading-name violated:
recovery_result exposes a bool under an activity/result name, leaving consumers to discover what True asserts. Name the signal for its verdict, such as fault_cleared, so boolean uses read as the claim being tested.
AGENTS.md reference: AGENTS.md:L7-L8
Useful? React with 👍 / 👎.
The gap
franka.Robotrunsrecover_from_errors()only inside its own run loop, on no channel, and that branch fires only whilestate().error != 0. A latched fault leaves the arm in libfranka modeReflexwitherrorreading 0, so the automatic path never runs and nothing outside the driver can reach it. On 2026-09-10 an arm latched this way rejected every jog and every home for a whole session (Positronic-Robotics/internal#1255).What this ships
Two ports on
franka.Robot:recover— a receiver. The run loop reads it each tick; an ask runsrecover_from_errors()regardless ofstate().error, so it reaches a latchedReflexthe automatic branch never sees.recovery_result— an emitter carrying whether the recovery cleared the fault.This is the positronic half of
internal#1255. The console half already exists (platform#479, merged): the panel draws aClear robot faultcontrol that emits onEvalUI.recover_armand greys itself while the wire is bound to nothing. A later platform change bindsconsole.recover_armtorecoverand readsrecovery_result, at which point the control goes live.Scope
origin/main. P3 in the plan; independent of positronic#678 and P2, which touch nearby lines inrun()and will need a rebase against whichever lands first.Tests
Driver-level, no rig: a console ask runs the recovery and reports the result; a fault that does not clear reports
False; an untouched arm reports no result.uv run --locked pytest positronic/drivers/roboarm/tests/test_franka.pyis green (35 passed).pre-commit(ruff, ruff format, basedpyright) passed on commit.The
check-rulesfan-out is skipped this pass to keep the shared box free while other agents work; the rules check is due before merge.