Phase 05 · Week 20 · 105 minutes

Day 135: Robot skills as typed tools with preconditions

Agentic task execution · Combine reasoning with deterministic robot skills.

Chapter 20 · Build a bounded language-to-ROS 2 task executor

Today in the field story

One problem, then the next

Each Courier-17 task node now becomes a typed tool. place_card names entity identity, target identity, units, frames, scene version, preconditions, timeout, cancellation, feedback, result codes, and a postcondition that another observation can verify. Invalid or missing fields fail before dispatch. You keep the Week 19 action decoder behind the skill implementation, not inside the planner prompt. The courier can therefore explain what it intends while a deterministic adapter owns what request is legal.

Why now

Typed skills turn a prose plan into inspectable execution boundaries.

Ignore today

Do not expose raw controller topics or accept inferred defaults.

Unlocks next

A planner-callable interface whose failures have stable meanings.

Understand

Build the physical picture first

A robot skill is a narrow typed socket: the planner may propose a plug, but the executor decides whether it fits current physical reality.

Give every skill one stable name and a closed input schema. move_to_pose needs more than three coordinates: it needs a pose type, unit convention, frame ID, target timestamp or scene version, speed profile, request identity, and declared optional fields. Object skills receive stable entity IDs rather than descriptions such as “that cup.” Reject unknown fields, missing units, unsupported frames, non-finite values, and values outside allowed ranges before any ROS request is created.

A contract also defines eligibility. Preconditions are evaluated against a fresh authoritative snapshot, not copied from the planner's text. A navigation skill might require localized state, an active map, sufficient battery reserve, a reachable goal, a current transform, no stop state, and an unexpired authority lease. A manipulation skill may additionally require collision-scene freshness, a known tool, payload limits, and a reserved workspace. A valid JSON shape with a failed precondition is still an invalid physical call.

ROS 2 actions provide the transport lifecycle for long-running skills: a typed goal is accepted or rejected, feedback reports progress, and a result ends in succeeded, aborted, or canceled status. Accepted is not completed, a cancel request is not proof of cancellation, and a succeeded result is not yet the physical postcondition. Retain the goal UUID, watch status and feedback, bound silence and total duration, and wait for or reconcile the terminal state before another conflicting skill begins.

Return structured results that help the orchestrator choose only preapproved branches. Include skill version, request and goal IDs, terminal status, machine-readable error code, last verified state version, observed effect summary, and whether compensation or human review is required. Keep free-form diagnostics for people, not branch logic. The executor maps known result codes to re-observe, alternate skill, replan, handoff, or abort; an unknown code fails closed.

Words you need

Name each idea precisely

Skill contract

The versioned definition of a skill's typed inputs, eligibility rules, lifecycle, outputs, errors, side effects, and completion evidence.

Physical example:

The move_to_pose/v2 contract requires metres, a map frame, scene version, speed limit, request ID, and a postcondition tolerance.

Goal

The typed request sent to a ROS 2 action server describing the bounded outcome that one skill should attempt.

Physical example:

A navigation goal contains a stamped pose in map; it does not contain the natural-language sentence that caused the planner to select it.

Feedback

Intermediate progress information emitted while an accepted action remains active, useful for monitoring but not a terminal outcome.

Physical example:

Remaining distance decreases from 2.0 to 0.4 metres while the navigation goal is executing.

Terminal status

The final ROS action state indicating that execution succeeded, aborted, or completed cancellation.

Physical example:

A goal moves from executing to canceling and only later to canceled after the action server finishes its stop and cleanup behavior.

Structured error

A finite machine-readable failure category with documented meaning and allowed recovery, separate from a human diagnostic message.

Physical example:

STALE_SCENE sends the orchestrator to re-observe, while LIMIT_REJECTED forbids automatic retry and records the rejected value.

Math, one line at a time

Work through today’s relationship

Prerequisite rescue · optionalTask graphs, timeouts, and retries

An autonomous task is a state machine with measurable guards, not one long prompt.

t_deadline
latest allowed completion timeUnit: seconds (s)
N_retry
maximum retry countUnit: attempts
P(success)
observed success frequencyUnit: probability
  1. A grasp skill gets a 5 s timeout and at most 2 retries.

  2. The maximum planned attempt time is 3×5 = 15 s, excluding recovery.

  3. After each failure, re-observe and check a typed precondition before retrying; do not replay a stale command.

Programmer analogy

It resembles a workflow engine with typed APIs, except retries require fresh perception of a changed world.

One initial try plus three retries, each capped at 4 s, permits how much attempt time?

4 attempts × 4 s = 16 s.

A call is eligible only when every required guard is true:

allowed=preconditionslimitsfresh state.\mathrm{allowed}=\mathrm{preconditions}\land\mathrm{limits}\land\mathrm{fresh\ state}.

For truefalsetrue\mathrm{true}\land\mathrm{false}\land\mathrm{true}, the result is false, so the executor must reject the call.

Define and evaluate the `move_to_pose/v2` skill

A simulated mobile base exposes a ROS 2 navigation action. The orchestrator must move to a staging marker without letting free-form planner output reach the action server.

  1. Define required inputs: request_id as a unique string, target_pose as finite x-y-yaw values, frame_id from an allowlist, scene_version as a nonnegative integer, max_speed_mps within 0.05..0.40, and deadline_ms within 1000..60000. Reject additional physical-control fields the contract does not own.

  2. Define preconditions from executor-owned state: localization is valid, the map transform is younger than 200 ms, scene version equals the current authoritative version, battery is above reserve, the goal is in the allowed zone, no protective or application stop is active, and this executor holds the navigation lease.

  3. Map the validated request to one ROS 2 action goal, store the returned goal UUID beside request_id, and record whether the server accepted or rejected it. Rejection ends this attempt; it is not an executing goal.

  4. While executing, collect timestamped feedback and enforce two clocks: feedback silence may not exceed two seconds, and total goal time may not exceed the declared deadline. Progress is informative, but arrival is not declared from remaining-distance feedback alone.

  5. On timeout, request cancellation for the exact goal UUID and enter CANCELING. Do not dispatch another navigation goal until status reaches canceled, aborted, or succeeded, or until a separately defined reconciliation path proves which goal can still command the robot.

  6. After a succeeded result, obtain fresh measured pose and require position error at most 0.10 m, yaw error at most 0.15 rad, near-zero commanded motion, and the same intended staging entity. If these fail, return POSTCONDITION_FAILED rather than success.

  7. Return one structured result containing request ID, goal UUID, contract version, terminal ROS status, postcondition verdict, final state version, duration, last feedback age, and error code; map every non-success code to a declared next state.

Result

A well-typed call becomes one correlated ROS goal, malformed or ineligible calls fail before dispatch, active work remains cancelable and observable, and only a fresh measured arrival closes the skill successfully.

What this proves

Types prevent malformed requests, preconditions prevent physically ineligible requests, action status bounds execution, and postcondition observation decides whether the world actually changed as required.

Physical examples

Where this appears in real life

Stamped navigation call

A planner proposes move_to_pose for a simulated cart using target (2.2, 1.1), frame map, speed 0.35 m/s, scene version 84, and request ID R-901.

Look for:

Schema validation succeeds, but dispatch still waits for localization, transform freshness, battery, authority, stop-state, reachability, and scene-version guards.

Wrong-frame pick request

A pick call contains a plausible object pose measured in the camera frame while the skill contract requires a current transform into robot_base; that transform is missing.

Look for:

The executor returns FRAME_UNAVAILABLE before motion, logs the rejected frame and snapshot, and does not ask the language planner to invent a transform.

Hands-on exercise

Make the idea observable

Use a simulated ROS 2 action or a paper action-server state machine. Do not connect an experimental skill contract directly to powered motion.

  1. Choose one existing long-running skill and write its versioned input schema, units, allowed frames, range limits, request identity, optional fields, and unknown-field policy.

  2. Write executor-owned preconditions, expected postconditions, maximum feedback silence, total timeout, cancel behavior, and the finite result-code table.

  3. Evaluate at least five calls: nominal, missing frame, NaN or non-finite value, stale scene version, and target outside the approved workspace. Record that only the nominal call reaches dispatch.

  4. Run the nominal action and capture goal acceptance, goal UUID, at least one feedback sample, terminal status, and a fresh postcondition observation under one request ID.

  5. Run a slow case, request cancellation by exact goal UUID, and verify the state passes through canceling before canceled or another documented terminal result.

  6. Force a controller success while the final observed pose or object state remains outside tolerance; verify the wrapper returns POSTCONDITION_FAILED and blocks the next dependent skill.

Observe

Schema checks catch malformed proposals, state guards catch valid-looking but ineligible calls, ROS status explains the transport lifecycle, and postcondition checks catch physical mismatch after apparent success.

Done when

Every test produces one documented result code, no invalid call reaches the action server, canceling is distinguished from canceled, and no dependent skill runs before a fresh postcondition passes.

Build today

Build an agent that converts a natural-language goal into inspectable ROS 2 actions and recovers from one failure.

Evidence to save

DONE when “Robot skills as typed tools with preconditions” runs from one documented command and the nominal plus boundary outputs are attached.

Common mistakes

Catch the wrong mental model

Wrong

Calling a tool safe because its JSON parsed and every required field was present.

Better

Validate types, finite values, units, frames, ranges, scene version, authority, and current physical preconditions before constructing the ROS goal.

Wrong

Treating an accepted goal, a cancel acknowledgment, or a controller success result as the final task outcome.

Better

Track the exact goal through terminal status and then verify the declared physical postcondition from fresh authoritative state.

Wrong

Branching on arbitrary diagnostic text returned by a skill.

Better

Use a closed versioned error-code set with documented recovery mappings; retain diagnostic text only for operators and debugging.

Job connection

How this becomes employable evidence

Wrap navigation, manipulation, docking, inspection, or device actions in versioned contracts that validate model-proposed arguments, preserve ROS goal identity and cancellation, expose bounded errors, and verify the real or simulated postcondition before workflow continuation.

Relevant target roles

  • Robot Learning Deployment / Physical AI Integration Engineer
  • Robotics Application / ROS 2 Integration Engineer
  • Robotics Software Engineer — ROS 2 / AMR
  • Robotics Deployment, Integration & Validation Engineer

Chapter 20 interview drill

Interview questions: Robot skills as typed tools with preconditions

Practise a 60–90 second answer: define the idea, connect it to a physical robot, state assumptions, frames, and units when relevant, then finish with the failure signal or evidence you would inspect.

Primary interview scenario

Design a typed pick_object tool over a ROS 2 action. Explain its schema, frames and units, preconditions, feedback, timeout, cancel lifecycle, result codes, and the observation that must pass after the action reports success.

Answer shape: clarify the situation → trace the physical and software path → test the most likely boundaries → name the evidence that would confirm the result.

Technical follow-up questions

Q1Why can a schema-valid `move_to_pose` call still be rejected?
Model interview answer

Current localization, transform freshness, scene version, workspace, battery, stop state, reachability, or authority may violate the skill's preconditions.

Q2What is the difference between canceling and canceled?
Model interview answer

Canceling means a cancel request was accepted and cleanup or stopping is in progress; canceled is the terminal state reached after that work completes.

Q3What must happen after a ROS action reports succeeded?
Model interview answer

The wrapper must obtain fresh authoritative state and verify the skill's physical postcondition within its declared tolerances before dependent work proceeds.