Skip to content

fix(embodiments): un-hardcode verify script, finish RTC horizon integer migration - #308

Open
gaolebaigao wants to merge 3 commits into
FastCrest:mainfrom
gaolebaigao:fix/embodiment-verify-and-horizon-migration
Open

fix(embodiments): un-hardcode verify script, finish RTC horizon integer migration#308
gaolebaigao wants to merge 3 commits into
FastCrest:mainfrom
gaolebaigao:fix/embodiment-verify-and-horizon-migration

Conversation

@gaolebaigao

Copy link
Copy Markdown

What

Consistency fixes in the embodiment config layer, prepping the ground for the Unitree Z1 arm preset (#69):

  • scripts/verify_embodiment_structure.sh no longer hard-codes the expected preset list. It has been failing since quadcopter shipped (expects ['franka', 'so100', 'ur5'], but list_presets() returns 4). It now validates whatever the package actually ships.
  • Finish the rtc_execution_horizon fractional → integer migration (ADR 2026-04-25 decision [Orchestration] Enhance reflex doctor for Optional Dependencies #8) in the copies that were left behind: scripts/emit_embodiment_presets.py and configs/embodiments/{franka,so100,ur5}.json (franka/ur5: 25, so100: 12 — matching the already-migrated in-package presets). Re-running the emitter would previously have regressed the package presets back to fractional values, and every from-source load emitted a deprecation warning.
  • docs/embodiment_schema.md: fix shipped preset count (4, quadcopter included), the canonical preset location (in-package presets dir; configs/embodiments/ copies are dev fallbacks), and the rtc_execution_horizon field semantics + cross-field rules to match schema.json/validate.py.

Why

The Z1 preset (#69) will be the 5th preset and needs these gates green: the verify script must pass with a new preset present, and the emitter must be re-runnable without regressing the integer migration.

Testing

  • bash scripts/verify_embodiment_structure.sh — fails on main (hard-coded 3 presets), passes here (4/4 presets validated)
  • pytest tests/test_embodiments.py tests/test_guard.py — 90 passed
  • python scripts/emit_embodiment_presets.py — 3/3 validated; re-emit is content-identical (modulo pre-existing to_dict() key ordering of the optional gripper block)
  • ruff check scripts/emit_embodiment_presets.py — no new findings vs main (I001 etc. are pre-existing)

Prep for #69 — no hardware values included in this PR; the Z1 preset itself will follow once the hardware contract is confirmed there.

…er migration

- verify_embodiment_structure.sh hard-coded the expected preset list and
  has been failing since quadcopter shipped; validate whatever
  list_presets() returns instead.
- emit_embodiment_presets.py and configs/embodiments/ still stored the
  legacy fractional rtc_execution_horizon; re-running the emitter would
  regress the migrated integer values in the package presets, and every
  from-source load emitted a deprecation warning. Sync all copies to the
  migrated integer counts (franka/ur5: 25, so100: 12).
- docs/embodiment_schema.md: fix shipped preset count (4, quadcopter
  included), canonical preset location, and the rtc_execution_horizon
  field semantics + cross-field rules to match schema.json/validate.py
  (ADR 2026-04-25 decision FastCrest#8).

No behavior change beyond the verify script now passing. Prep for FastCrest#69
(the Unitree Z1 arm preset will be the 5th preset).

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bc3c0b0915

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/verify_embodiment_structure.sh Outdated
Comment on lines +44 to +48
presets = list_presets()
expected = ["franka", "so100", "ur5"]
if presets != expected:
print(f" ✗ list_presets() returned {presets}, expected {expected}")
# Don't hard-code the expected preset list — it already drifted once when
# quadcopter shipped, and hard-coding it here would break again on the
# next preset. Validate whatever the package actually ships.
if not presets:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep an independent inventory of required package presets

When any single bundled preset is accidentally omitted from src/tether/embodiments/presets/, list_presets() simply returns the surviving files, this nonempty check succeeds, and the loop reports that the package structure is valid. The earlier expected-list comparison caught such omissions; deriving both the subject and expected inventory from the same directory makes this preflight unable to detect incomplete packaging, including a missing quadcopter.json or one of the package copies whose dev copy was checked above.

Useful? React with 👍 / 👎.

Comment thread docs/embodiment_schema.md Outdated
| `frequency_hz` | float | 0–1000 (exclusive 0) | Robot control loop rate. |
| `chunk_size` | int | 1–200 | Actions in a single inference chunk. |
| `rtc_execution_horizon` | float | 0–5.0 (exclusive 0) | Seconds of chunk to execute before requesting next inference. |
| `rtc_execution_horizon` | int | 1–`chunk_size` | Integer count of actions to lock during RTC replan. Legacy fractional values (0 < v < 1) auto-migrate to `int(v × chunk_size)` at load with a one-time deprecation warning; schema v2 will reject them. |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Document the migration's actual rounding behavior

For legacy fractions whose product with chunk_size is not integral, this formula gives a different result from the loader: EmbodimentConfig.from_dict() uses int(round(horizon * chunk_size)), whereas the new documentation promises truncation via int(v × chunk_size). For example, v=0.25 and chunk_size=30 is documented as 7 actions but loads as 8, so operators tuning custom legacy configs cannot predict the resulting lock horizon from this schema guide.

Useful? React with 👍 / 👎.

…t migration formula in docs

Address Codex review feedback on FastCrest#308:

- verify_embodiment_structure.sh validated whatever list_presets()
  returned, so an accidentally dropped preset file (e.g. quadcopter,
  which has no configs/ dev copy) passed the preflight silently. Keep a
  required-minimum set checked as a SUBSET of what ships: omissions fail
  the check, while new presets still validate automatically without
  editing the script.
- schema.json description and docs/embodiment_schema.md documented the
  legacy fractional rtc_execution_horizon migration as int(v x
  chunk_size) (truncation), but EmbodimentConfig.from_dict() rounds:
  round(v x chunk_size), minimum 1. Document the actual behavior
  (v=0.25, chunk_size=30 migrates to 8, not 7). No behavior change; the
  shipped presets' migrated values are unaffected.
@gaolebaigao

Copy link
Copy Markdown
Author

Both suggestions addressed in 7fd2768:

  1. Omission detection restored (scripts/verify_embodiment_structure.sh): the script now keeps a required-minimum set {"franka", "so100", "ur5", "quadcopter"} checked as a subset of what list_presets() returns — an accidentally dropped preset file fails the preflight, while new presets still validate automatically without editing the script (the old equality check was what broke when quadcopter shipped). Negative-tested: hiding quadcopter.json now exits with required package presets missing: ['quadcopter'].

  2. Migration formula corrected (schema.json description + docs/embodiment_schema.md): now documented as round(v × chunk_size) (minimum 1) to match what EmbodimentConfig.from_dict() actually does — int(round(...)) — instead of the truncating int(v × chunk_size) both previously claimed (v=0.25, chunk_size=30 migrates to 8, not 7). Description-only change; the shipped presets' migrated values (25/12) are unaffected since their products are integral.

Verification: verify_embodiment_structure.sh passes 4/4 (and correctly fails with a preset removed); pytest tests/test_embodiments.py tests/test_guard.py — 90 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.

1 participant