Skip to content

fix(docker): expand ~ and $VAR in extra_mounts destinations - #128

Draft
bai-uipath wants to merge 1 commit into
mainfrom
bai/mount-dest-expandvars
Draft

fix(docker): expand ~ and $VAR in extra_mounts destinations#128
bai-uipath wants to merge 1 commit into
mainfrom
bai/mount-dest-expandvars

Conversation

@bai-uipath

@bai-uipath bai-uipath commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

What

extra_mounts expands ~ and $VAR on the source side of a spec but not on the destination, which was only checked for a leading /. This expands the destination the same way, before the absolute-path check.

Why: the Maestro flow v2 preview evals need it

The concrete driver is evaluating the preview/uipath-maestro-{flow,case,bpmn} builder-SDK skills (flow v2, the @uipath/flow-sdk TypeScript authoring path) under the docker driver. Those runs need two things the shipped v1 experiments never had to mount by hand.

The uip CLI login state is the first one. Almost every Maestro task ends in a tenant call, so the container needs the saved credential at ~/.uipath. CI sidesteps this with UIPATH_CLI_ENABLE_ENV_AUTH and a token in the environment, but the eval VM and any dev box authenticate from the file, so the overlay has to mount it. The destination cannot be /root/.uipath: the docker runner forwards --env HOME with the host value by design (models/sandbox.py, "HOME is intentional in default"), which overrides the image's /root, and uip then looks under that forwarded host home. So the destination has to be the host's $HOME/.uipath, and until this change the only way to write that was to hardcode one machine's home directory.

The skills repo root is the second one. A v1 experiment gets it mounted for free, because its plugins.path is the repo root and plugin paths are auto-mounted. A preview experiment narrows the catalog to preview/, which drops the mount, and criteria across the flow and case suites shell out to $SKILLS_REPO_PATH/tests/tasks/**/_shared/*.py. Adding it back also wants $VAR on both sides.

Why the failure mode is worth a code change rather than a comment

A login state the CLI cannot see fails tasks on their tenant calls, which scores as a capability problem, not a configuration one, so the run completes and reports a plausible-looking number. A manual docker run does not reproduce it either: without --env HOME the container falls back to the image's /root and authenticates fine. The 2026-08-20 preview run only came out clean (auth-failure signatures at zero across all 257 rows) because the destination was pointed at the forwarded host home first.

The hardcode is already in the tree, which is the other reason to fix it upstream rather than work around it again. skills/tests/experiments/same-ground-headtohead.yaml mounts to /home/tmatup/.uipath, with a comment saying the destination "must match that path until #100 makes HOME portable". This is that change; #100 is the issue it closes. Two lines in that overlay become $HOME/... once this merges, and it stops carrying one person's home directory.

Downstream

UiPath/skills#2728 adds the preview docker overlay and currently ships the auth mount commented out, with a literal-home fallback documented, because a $HOME destination fails validation outright on 0.10.2. That comment can be deleted and the mount enabled once this is released.

Notes

Two details are deliberate rather than incidental:

  • expandvars leaves an unset variable verbatim, so a typo'd name still fails the absolute-path check. The message now shows the raw and the expanded form, otherwise it reads as a puzzle.
  • the framework-owned-mount check runs on the expanded destination, since a variable could itself expand to /work or / and the raw form would sail past the gate.

Four tests cover destination $VAR, destination ~, the unset-variable rejection, and a variable expanding to a reserved destination.

🤖 Generated with Claude Code

The source side of an extra_mounts spec is normalized with
expandvars(expanduser(...)) so authors can write portable specs; the
destination was only checked for a leading "/" and never expanded.

That asymmetry makes one common mount impossible to write portably.
env_passthrough forwards HOME with the HOST value on purpose, so any
container-side path that must line up with $HOME -- $HOME/.uipath for the
uip CLI's saved login state, for instance -- has a different literal value
on every host. The only way to express it was to hardcode one host's home
directory, which then mounts to the wrong place everywhere else. A login
state the CLI cannot see fails tasks as a capability problem rather than a
config one, so the misconfiguration is close to invisible: it cost 26% of
the rows in an ad-hoc Maestro run before it was spotted.

Expand the destination the same way, before the absolute-path check, so
`~/.uipath:$HOME/.uipath:rw` resolves. Two details worth keeping:

- expandvars leaves an unset variable verbatim, so a typo'd name still
  fails the absolute-path check. The message now shows the raw and the
  expanded form, otherwise it reads as a puzzle.
- the framework-owned-mount check runs on the expanded destination, since
  a variable could itself expand to /work or / and the raw form would sail
  past the gate.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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