Skip to content

fix(envs): declare importable manifest app paths (from #1068) - #1164

Merged
cursor[bot] merged 7 commits into
mainfrom
fix/1068-manifest-app-target
Sep 15, 2026
Merged

cursor[bot] merged 7 commits into
mainfrom
fix/1068-manifest-app-target

Conversation

@cursor

@cursor cursor Bot commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

Summary

Replacement for #1068 (fork workflows stuck at action_required). Same change: declare manifest app paths the server can actually import under, with hermetic regressions.

Please close #1068 once this lands.

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation
  • New environment
  • Refactoring

Alignment Checklist

  • Principles / invariants unaffected
  • Rebased onto current main so required CI can run on-repo

RFC Status

  • Not required (bug fix, docs, minor refactoring)

Test Plan

  • Exact-head Tests / Package CI green
  • Focused tests/envs/test_manifest_app_targets.py

Claude Code Review

N/A — release automation replacement for Ben's watch/merge ask on #1068.

Open in Web View Automation 

Note

Low Risk
Manifest and CI-only validation changes; no runtime library logic, with low blast radius beyond cloud env boot paths.

Overview
Fixes Modal/Daytona startup by correcting openenv.yaml app entries so uvicorn loads the FastAPI app under a module path that matches each image’s layout (not the Dockerfile CMD, which local Docker still uses).

Several envs move from bare server.app:app to <env>.server.app:app; agent_world_model_env drops the invalid envs. prefix; grid_world_env keeps envs.grid_world_env.server.app:app where the runtime actually uses that prefix.

Adds tests/envs/test_manifest_app_targets.py, which parametrizes all manifests with an app field: it checks the module exists and uses AST (no heavy imports) to catch relative imports that would break when the declared package root is wrong, with layout-specific guards for env-root vs repo-root images.

Reviewed by Cursor Bugbot for commit b3005a7. Bugbot is set up for automated code reviews on this repo. Configure here.

k21993 and others added 5 commits September 13, 2026 01:22
The manifest declared `app: envs.agent_world_model_env.server.app:app`, the
only one of 35 envs not using `server.app:app`. The cloud providers read this
field verbatim and run `cd /app/env && python -m uvicorn <app>`, so this env
fails to start on Modal and Daytona with ModuleNotFoundError: images are built
with the env directory as context, and the installed dist maps
`agent_world_model_env` to `.`, so no `envs` package exists in either layout.

Add a contract test resolving every manifest's app target against the env
directory on disk. It does not import the modules: that would pull in
playwright, carla and the rest of the optional-dependency tail and skip on
exactly the CI machines that should be guarding this.
Seven manifests declared `app: server.app:app` while their server package
imports `from ..models`. Under that name `server` is top-level, so the relative
import climbs past it and the server dies with `ImportError: attempted relative
import beyond top-level package`. Only the cloud providers read this field --
`ModalProvider._discover_server_cmd` and its Daytona twin run
`cd <env root> && python -m uvicorn <app>` -- so the breakage is invisible
locally, where `docker run` uses the Dockerfile CMD instead.

Each manifest now names the same target its own Dockerfile launches: the env's
distribution package for six of them, and `envs.grid_world_env...` for
grid_world_env, which builds from the repo root.

The contract test now checks importability rather than mere file existence,
walking the reachable import graph with `ast`. It does not import the modules:
that would pull in playwright, carla and the rest of the optional-dependency
tail and skip on exactly the CI machines that should be guarding this. Imports
wrapped in a try/except ImportError are not counted, since that fallback is how
most envs already support both layouts.

Validated against real interpreter behaviour across all 35 manifests: no false
positives and no false negatives on every env whose dependencies are installed.
`_resolve` fell back to the repo root unconditionally, and the source tree
always contains `envs/<env>/server/app.py`. Every env therefore accepted an
`envs.` prefix -- including the original
`envs.agent_world_model_env.server.app:app`, the exact value this suite was
added to reject. The repo's layout is not evidence about the container's.

A repo-root build context is not the right signal either: coding_env builds
from the repo root but `pip install ./envs/coding_env/` makes it a real
distribution, so its CMD launches `coding_env.server.app:app` with no prefix.
The prefix is only importable when the image launches it that way, as
grid_world_env does with /app on PYTHONPATH. Derive it from that.

Add the negative tests whose absence let this through: the checks only proved
the values in the tree pass, never that a wrong one fails. One asserts AWM's
old target does not resolve, one that grid_world's legitimate target still
does, and one pins repo-root-context and envs-prefix as different predicates.
@cursor
cursor Bot marked this pull request as ready for review September 15, 2026 09:19
@burtenshaw burtenshaw added bug Something isn't working size: medium Medium pull request labels Sep 15, 2026 — with Cursor
Co-authored-by: benjamin.burtenshaw <benjamin.burtenshaw@huggingface.co>
@bot-ci-comment

Copy link
Copy Markdown

The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update.

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Alignment Review Report

Reviewed at PR head 8791ef7c. Checks were run against the actual PR content.

Automated Checks

  • Lint: PASS (for this PR). .claude/hooks/lint.sh exits non-zero, but every file it flags is a pre-existing envs/* file that the hook reformats repo-wide (e.g. opencode_env, pi_env, chat_env, plus Python code-fences in many README.mds). The PR's only Python file — tests/envs/test_manifest_app_targets.py — passes ruff format --check, ruff check, and usort check in isolation. The 7 changed openenv.yaml files aren't linted as Python.
  • Debug code: CLEAN (for this PR). check-debug.sh only scans src/, which this PR doesn't touch; its print/TODO hits are all pre-existing in src/. The new test file has no print/breakpoint/TODO.
  • Focused test: PYTHONPATH=src:envs pytest tests/envs/test_manifest_app_targets.py76 passed, including the negative regression tests.

I verified the premise against the code: ModalProvider._discover_server_cmd and DaytonaProvider._discover_server_cmd read the manifest app field verbatim and run cd <dir-of-openenv.yaml> && python -m uvicorn <app>, using the Dockerfile CMD only as a fallback. Each changed manifest now matches the module its own Dockerfile launches, and the unguarded from ..models servers (awm, carla, chess, pelican, wildfire, grid_world) genuinely require the package-qualified path. The fix is correct.

Open RFCs Context

  • RFC 002 – Env Spec (In Review; @Darktex, @pankit-eng, @jspisak, @zkwentz): owns the environment spec + ContainerProvider packaging decision — the surface this PR touches.
  • RFC 008 – Environment Auto-Validation (In Review; @zkwentz): designing openenv validate + grader registry / normalized-manifest schema; explicitly notes today "no manifest schema exists" and that validate/push run divergent checks.
  • RFC 011 – Catalog Discovery (Draft): reads openenv.yaml for name/marker/framework/description, not the app field. No conflict.

Tier 1: Fixes Required

None. No lint / type / import / security issues in the PR's own changes.

Tier 2: Alignment Discussion

Principle Conflicts

None identified. This is a deploy-config correctness fix + regression test; it does not touch the Gym API, MCP boundary, reset/agent isolation, reward placement, or client↔server separation — all INVARIANTS intact. It actively supports "minimize lifecycle deltas" by making cloud startup match local.

RFC Conflicts

ALIGNMENT FLAG: New manifest-correctness check lands as a standalone pytest rather than an RFC 008 grader

  • RFC at stake: RFC 008 (Environment Auto-Validation, In Review)
  • The concern: RFC 008 is designing the canonical mechanism for mechanically catching env defects (openenv validate, grader registry, normalized-manifest schema) so operators/hubs inherit the same checks. This PR adds a genuinely valuable "the app target must import under its declared name" check, but as a repo-local tests/envs/ test, so it won't be surfaced by openenv validate or a hub intake. Worth deciding whether it should also become an RFC 008 grader. Non-blocking — the test is correct and useful as-is.
  • Suggested reviewer: @zkwentz

ALIGNMENT FLAG: The deploy-manifest app entrypoint contract is enforced but unspecified in an RFC

  • RFC at stake: RFC 002 (Env Spec, In Review)
  • The concern: The rule this PR encodes — app must be the distribution-qualified module the image actually launches, and providers run it verbatim over the Dockerfile CMD — is real behavior in modal_provider/daytona_provider but isn't written down in the env-spec RFC. Documenting the app/runtime/port contract in RFC 002 would keep the ~35 manifests from drifting again. Non-blocking.
  • Suggested reviewer: @Darktex

Summary

  • 0 mechanical issues to fix (Tier 1)
  • 1 minor non-blocking nit (inline: cd /app/env wording)
  • 2 RFC discussion points (RFC 008 grader placement; RFC 002 documenting the app contract)
  • 0 principle/invariant violations

Overall: a correct, well-tested bug fix — effectively ship-ready. The flags are for design discussion, not blockers.

Open in Web View Automation 

Sent by Cursor Automation: Pre-review

The cloud providers do not read the Dockerfile `CMD` when a manifest is
present: `ModalProvider._discover_server_cmd` (and its Daytona twin) locate
`openenv.yaml` inside the sandbox, take the `app` field verbatim, and run
`cd /app/env && python -m uvicorn <app>`. So the `app` field alone decides

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Minor / non-blocking (accuracy of guidance): cd /app/env is the modern-layout path, but not what every env actually uses. Both providers compute env_root = <directory containing openenv.yaml> (fast-path /app/env, else find /app -maxdepth 4 -name openenv.yaml), so the real command is cd /app/carla_env for carla_env (COPY . /app/carla_env/) and cd /app/envs/grid_world_env for grid_world_env. Consider phrasing this as cd <dir containing openenv.yaml> here and in the two failure messages (lines 244 and 274) so the guidance stays accurate for non-/app/env layouts. The check logic itself is correct — it resolves against the source env dir, not a hardcoded container path.

Co-authored-by: benjamin.burtenshaw <benjamin.burtenshaw@huggingface.co>
@cursor
cursor Bot merged commit 703ce32 into main Sep 15, 2026
12 checks passed
cursor Bot pushed a commit that referenced this pull request Sep 15, 2026
Co-authored-by: benjamin.burtenshaw <benjamin.burtenshaw@huggingface.co>
cursor Bot pushed a commit that referenced this pull request Sep 15, 2026
Co-authored-by: benjamin.burtenshaw <benjamin.burtenshaw@huggingface.co>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working size: medium Medium pull request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants