Skip to content

Enforce held-object geometry and explicit reconciliation - #47

Merged
Jepson2k merged 20 commits into
mainfrom
feat/held-object-geometry
Sep 19, 2026
Merged

Jepson2k merged 20 commits into
mainfrom
feat/held-object-geometry

Conversation

@Jepson2k

@Jepson2k Jepson2k commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

Track held geometry under the PAROL6 flange and exempt only declared contact pairs. Attach/detach requires confirmed whole-world application; invalid contact names preserve the previous geometry. Reference or controller-context changes invalidate declarations and gate arm motion until explicit reconciliation or removal. Live and dry-run clients expose the same readback and capability. These declarations neither operate the gripper nor confirm a grasp or estimate payload.

Completion waits now query the exact command result. A tool can finish independently while arm motion is paused; finishing that tool cannot falsely complete the paused or cancelled arm command. The controller retains 1024 successful indices, including every command consumed by a blend. Unknown, cancelled, and expired results remain unconfirmed, and a controller session change during a wait raises ConnectionError. Client and controller must both support the new query. Replies to timed-out queries are filtered by response type so they cannot satisfy a different query.

Depends on #46 and the companion waldoctl attachment contract.

Validation: real fake-serial regressions reproduced false completion of paused or cancelled arm work, late-response confusion, and missed controller restarts. Restart detection also watches independent status broadcasts when the command socket stops receiving after a peer reset. The latest change passes 16 controller/tool/lifecycle/error checks and 13 Commander pause, nested-skill, blended-stepping, recording, restart, and project-import workflows. Formatting and type checks pass. Query and acknowledgement receives preserve caller cancellation when a controller reply arrives concurrently, avoiding the Python 3.11 wait_for cancellation race observed in CI. The integration workflow cancels a query on arrival of a real controller reply and confirms subsequent queries still work. The latest CI run passes all twelve combinations of Linux, macOS, Windows and Python 3.11–3.14, plus lint. Each primary suite contains 314 cases (306 passed and eight separately executed examples skipped on Linux/macOS; 305 passed and the additional SIGKILL-only test skipped on Windows). All eight examples also pass in their separate job step on every combination.

Companion PRs: waldoctl, par6, Waldo-Commander.

Jepson2k and others added 17 commits September 8, 2026 04:55
…ams with ERROR

set_shapes checked all eight slots of the homed array, but the firmware
byte only carries six joints, so every attachment declaration was refused
on hardware (the fake serial path fills all eight, which is why tests were
green). While the attachment context is stale, every fire-and-forget jog
or servo datagram was answered with an ERROR nobody awaited; the client
dequeued those replies on its next unrelated request and raised for a
command the server never refused. Streamed datagrams are now dropped (and
logged once per epoch); acked commands are still refused explicitly.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…d-object-geometry

# Conflicts:
#	parol6/client/async_client.py
…d-object-geometry

# Conflicts:
#	parol6/client/async_client.py
#	parol6/commands/query_commands.py
#	parol6/server/controller.py
…eply

The attachment gate test set a part on the flange in the process-wide
collision world and left it there, so every later test's arm stood in
collision at home. The completion readback test handed the query's
struct to the message decoder; it now packs it as the controller does.
The formatter's and type checker's remaining findings on this branch go
with them.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N8zt66KjkzgZLtQuSfJd9r
…sults

The dry-run client keeps one chunk per submitted command at the control
tick — a planned trajectory, a delay's held pose, a tool action's jaw
ramp over its estimated duration, or nothing with the refusal on it —
and `plan()` assembles them into a waldoctl `TickIndex` at the record's
row rate, one block per command with its move type. `simulate()` is the
same record: this backend has no plant. Motion and queued methods
answer with their program index, as the live client does; `delay`,
`checkpoint`, `wait_command` and `flush` are real methods; and the
client carries the backend `Robot` it stands in for.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N8zt66KjkzgZLtQuSfJd9r
The skills contract on this branch still reads them off every client,
preview clients included.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N8zt66KjkzgZLtQuSfJd9r
…metry

# Conflicts:
#	parol6/client/async_client.py
#	parol6/client/dry_run_client.py
@Jepson2k
Jepson2k marked this pull request as ready for review September 18, 2026 03:20
Every arm-moving command, home included, is refused while the
attachment context is stale, from one ARM_MOTION_CMD_TYPES set shared by
the controller and the dry run. Attaching a part is refused by one gate
in the state that also sees a plan still in the planner: the controller
records the highest submitted command index and the player the highest
one a returned segment accounts for. A hardware E-stop keeps its own
error while pressed; the stale-attachment error latches on release.

wait_command is paced by the status stream, re-asking the completion
query when a frame reports the command done or an error standing, or
after 250 ms without one. Query replies are matched by request id
alone; the broadcaster reads the session id the state already owns;
the dry run's ack folds to one expression; the wire's Attachment import
is at module level; the fake serial's E-stop input is driven through
press_estop. The in-process controller fixture is shared from conftest,
and the blend test keeps only the pause half of a case the record test
already covers.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N8zt66KjkzgZLtQuSfJd9r
…metry

# Conflicts:
#	parol6/client/dry_run_client.py
@Jepson2k
Jepson2k changed the base branch from feat/demonstration-recording to main September 19, 2026 02:59
@Jepson2k
Jepson2k merged commit a65a466 into main Sep 19, 2026
13 checks passed
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.

2 participants