Skip to content

feat(gen-sim): add TaskSpec v0.1 and guarded E2 acceptance - #613

Open
yuecideng wants to merge 5 commits into
ljd/action_engine_refactorfrom
codex/taskspec-v01
Open

feat(gen-sim): add TaskSpec v0.1 and guarded E2 acceptance#613
yuecideng wants to merge 5 commits into
ljd/action_engine_refactorfrom
codex/taskspec-v01

Conversation

@yuecideng

Copy link
Copy Markdown
Contributor

Description

First implementation increment of the TaskSpec/GenSim consolidation design,
targeting ljd/action_engine_refactor (#531), not main. Baseline verified:
c414b8d158615bbe52e448bf67f96206ae7e7a2b. Builds on #531; no issue auto-close.

Delivered

  • Pure standard-library embodichain.task_spec v0.1: five strict records,
    bounded predicate AST, canonical role IDs, exact SI decimal normalization,
    restricted semantic equivalence/hashing, content references and fail-closed
    certificate record validation. Legacy candidate step hashes keep their meaning.
  • Explicit template/instance planning provenance in graph/v2; full v2 execution
    remains gated until measured instance/witness qualification exists.
  • Public typed registered phase protections and empty-plan recovery. Compiler
    binds existing acquire/release gates and held guards; GenSim does not create
    another executor. New held-move protection is versioned opt-in so legacy
    lowerer configurations remain usable.
  • Opt-in --task-template E2 observed-goal acceptance. A strict sidecar and
    fingerprint/v3 link template to graph/v1; old no-TaskSpec bundles remain v2.
    Upright segment thresholds derive from the template. Only single-object
    local-+Z upright templates/assets are admitted; process/unsupported predicates
    fail early. CLI defaults to supported dual_franka and rejects unsupported
    profiles before generation.
  • Shared robust angle measurement, actual initial/final pose evidence, and a
    reject-only Gym final-acceptance hook before metadata finalization/save/reset.
    Initial invalid/unavailable/trivial batches abort without saving. Final rows
    are evaluated independently. Exceptional rollouts freeze failed/unavailable
    final evidence before abort reset. Reused evidence directories are rejected.
  • Existing FeasibilityBroker now participates in preparation. Unknown or
    runtime-probe checks do not masquerade as successful static feasibility;
    IK, collision-path and physical checks explicitly remain not_run.

Deliberate qualification boundary / follow-up

This is not completion of all P0–P5 and not a physical success certificate.
The E2 report says qualification=observed_goal_only and
certificate_status=unavailable. Full asset-content closure, measured
SceneInstance identity, successful ActionWitness/certificate assembly,
TaskAgent seed migration/template-driven scene requests, other task-family
evaluators, process monitoring, decoder consolidation, Gradio migration and
motion GenerationSession hosting remain dependent increments. No simulator
step/reset/retry/commit owner was added. See embodichain/task_spec/README.md
and IMPLEMENTATION.md for input limits and remaining exits.

Validation

  • 590 focused CPU tests passed; one confirmed pre-existing packaged-default
    assertion deselected. Coverage includes identity/strict records, semantic
    compilation, runtime/atomic failure injection, old/new bundle composition,
    actual host metadata/reset ordering, feasibility/workflow, context and docs.
  • Black 26.3.1 black ., git diff --check, context check/routing and API
    coverage passed (2202/2202 exports).
  • Sphinx dummy build exited 0 with unrelated existing warnings; no warning
    matched the new TaskSpec/evaluation pages or modules.
  • Independent review reproduced and closed float32 small-angle false passes,
    failed-rollout/stale evidence, unresolved guard-only motion and misspelled
    registered protected phases, with regression tests.

CPU commands use Python 3.11, PYTEST_DISABLE_PLUGIN_AUTOLOAD=1,
-q -c /dev/null --noconftest -p no:cacheprovider, and explicit
PYTHONPATH=<worktree>:/root/sources/dexsim/build_Release/lib/python_package.
The compiled DexSim package is used for imports only; no simulator is launched.

Known unrelated checks: packaged workflow test expects 8000 steps while this
baseline already configures 10000; that assertion is excluded from the final
focused run. A broader configured open_drawer test attempted an external
Drawer.zip download and failed checksum verification (81 other tests passed);
the existing asset loader removed its invalid cache. No GPU rollout,
multi-seed robustness or remote generation service was run.

Type of change

  • Bug fix
  • Enhancement / new feature
  • Documentation update

Screenshots

Not applicable: protocol, compiler/runtime contracts and CPU evidence paths.

Checklist

  • Ran Black 26.3.1 black . before commits.
  • Updated documentation and affected project context.
  • Public API coverage checked.
  • Added behavior, failure-injection and compatibility tests.
  • No new dependencies required.

@yuecideng yuecideng added enhancement New feature or request gym robot learning env and its related features atomic action atomic action related functionality labels Sep 11, 2026
@greptile-apps

greptile-apps Bot commented Sep 11, 2026

Copy link
Copy Markdown

RetriggerConfidence Score: 3/5

The PR is not yet safe to merge because guard timeouts can fail already-verified peer rows and post-acceptance exceptions can overwrite the authoritative final evidence.

Fix All in CodexFindings

  1. P1 Guard timeout fails peer rows
  2. P1 Failure overwrites frozen evidence
  3. P2 Invalid axes yield zero tilt
Fix with agent prompt
### Issue 1
embodichain/lab/task_program/runtime/executor.py:1303-1305
In a vector rollout, one row can satisfy its held-object guard while another remains unresolved. Because the command cursor is shared, both rows remain in `request.env_mask`; when the deadline is reached, this branch marks the entire mask as failed. It therefore invalidates the already-verified row as well as the unresolved row, breaking row-local execution outcomes.

### Issue 2
embodichain/gen_sim/task_engine/_task_spec.py:195-203
If final acceptance has already written `task_evaluation.json` and a later metadata hook or dataset-saving reset raises, the outer exception handler calls this function. It re-observes the environment and overwrites both the goal-state artifact and the evaluation report. This loses the original program and task result and can replace the pre-reset observation with partially reset state, so the retained evidence no longer represents the acceptance boundary it was meant to freeze.

### Issue 3
embodichain/compute/task_predicates.py:36-39
This public API documents `local_axis` as a unit vector but validates only its shape. A caller can pass a zero axis, for which `atan2(0, 0)` produces a plausible zero tilt that downstream threshold checks may treat as upright. Validating or normalizing finite, nonzero axes here would prevent misleading orientation measurements.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

  • Adds strict standard-library TaskSpec validation, canonicalization, hashing, instance/witness/certificate records, and public documentation.
  • Carries explicit TaskSpec provenance through semantic planning while retaining graph/v1 for the currently supported E2 execution path.
  • Adds initial/final pose evidence and a pre-metadata final-acceptance hook to prevent goal-failed demonstrations from being committed.
  • Extends Atomic Action and Task Program execution with registered phase gates, held-object guards, pending evidence, and empty-plan recovery.
  • Integrates static feasibility reporting into preparation without treating unknown runtime capability checks as proven.
  • Two blocking lifecycle/row-isolation defects remain in guard timeout handling and frozen evidence preservation.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    T[TaskTemplate v0.1] --> V[Strict validation and canonical hash]
    V --> P[Semantic planner]
    P --> G2[Graph v2 provenance]
    G2 --> Q[Qualification gate]
    Q -->|Measured instance/witness unavailable| X[Execution rejected]

    P --> G1[Legacy graph v1 E2 recipe]
    G1 --> B[TaskSpec binding sidecar and fingerprint v3]
    B --> I[Observe settled initial pose]
    I -->|Invalid or already satisfied| A[Abort without saving]
    I -->|Eligible| E[Existing Task Program / Atomic Action executor]
    E --> H[Registered phase gates and held guards]
    H --> F[Observe final pose before metadata/reset]
    F --> R[Per-row final acceptance]
    R -->|Accepted| S[Commit successful episode]
    R -->|Rejected| D[Discard episode and retain failure evidence]
Loading

Reviews (1) · Last reviewed commit: "docs(gen-sim): clarify measured witness ..."

Comment on lines +1303 to +1305
if guard_only and context.robot.timestamp >= request.deadline:
failure_mask = request.env_mask.clone()
observed_mask.zero_()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Guard timeout fails peer rows

In a vector rollout, one row can satisfy its held-object guard while another remains unresolved. Because the command cursor is shared, both rows remain in request.env_mask; when the deadline is reached, this branch marks the entire mask as failed. It therefore invalidates the already-verified row as well as the unresolved row, breaking row-local execution outcomes.

Prompt To Fix With AI
This is a comment left during a code review.
Path: embodichain/lab/task_program/runtime/executor.py
Line: 1303-1305

Comment:
**Guard timeout fails peer rows**

In a vector rollout, one row can satisfy its held-object guard while another remains unresolved. Because the command cursor is shared, both rows remain in `request.env_mask`; when the deadline is reached, this branch marks the entire mask as failed. It therefore invalidates the already-verified row as well as the unresolved row, breaking row-local execution outcomes.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Codex Fix in Claude Code

Comment on lines +195 to +203
try:
final = observe(env, binding, scope="task_goal", output=output)
except Exception as exc:
final = {
"status": ["unavailable"] * num_envs,
"error": {"type": type(exc).__name__, "message": str(exc)},
}
write_evidence(
output / "task_evaluation.json",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Failure overwrites frozen evidence

If final acceptance has already written task_evaluation.json and a later metadata hook or dataset-saving reset raises, the outer exception handler calls this function. It re-observes the environment and overwrites both the goal-state artifact and the evaluation report. This loses the original program and task result and can replace the pre-reset observation with partially reset state, so the retained evidence no longer represents the acceptance boundary it was meant to freeze.

Prompt To Fix With AI
This is a comment left during a code review.
Path: embodichain/gen_sim/task_engine/_task_spec.py
Line: 195-203

Comment:
**Failure overwrites frozen evidence**

If final acceptance has already written `task_evaluation.json` and a later metadata hook or dataset-saving reset raises, the outer exception handler calls this function. It re-observes the environment and overwrites both the goal-state artifact and the evaluation report. This loses the original program and task result and can replace the pre-reset observation with partially reset state, so the retained evidence no longer represents the acceptance boundary it was meant to freeze.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Codex Fix in Claude Code

Comment on lines +36 to +39
if pose.ndim != 3 or pose.shape[-2:] != (4, 4) or local_axis.shape != (3,):
raise ValueError("Expected poses (N, 4, 4) and one local axis (3,).")
axis = pose[:, :3, :3].double() @ local_axis.double()
return torch.atan2(torch.linalg.vector_norm(axis[:, :2], dim=-1), axis[:, 2])

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 Invalid axes yield zero tilt

This public API documents local_axis as a unit vector but validates only its shape. A caller can pass a zero axis, for which atan2(0, 0) produces a plausible zero tilt that downstream threshold checks may treat as upright. Validating or normalizing finite, nonzero axes here would prevent misleading orientation measurements.

Prompt To Fix With AI
This is a comment left during a code review.
Path: embodichain/compute/task_predicates.py
Line: 36-39

Comment:
**Invalid axes yield zero tilt**

This public API documents `local_axis` as a unit vector but validates only its shape. A caller can pass a zero axis, for which `atan2(0, 0)` produces a plausible zero tilt that downstream threshold checks may treat as upright. Validating or normalizing finite, nonzero axes here would prevent misleading orientation measurements.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Codex Fix in Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

atomic action atomic action related functionality enhancement New feature or request gym robot learning env and its related features

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant