Skip to content

Load executor skills without host path conversion - #29626

Merged
jif-oai merged 5 commits into
mainfrom
jif/uri-native-executor-skills
Jun 23, 2026
Merged

Load executor skills without host path conversion#29626
jif-oai merged 5 commits into
mainfrom
jif/uri-native-executor-skills

Conversation

@jif-oai

@jif-oai jif-oai commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Why

After #28918, selected skill roots are PathUri, but the executor skill provider still converts them to the app-server host's AbsolutePathBuf. A foreign Windows root therefore cannot be discovered by a Unix host, and the inverse has the same problem.

This PR keeps executor skill discovery and reads on the filesystem that owns the selected root while reusing the existing skill rules.

What changed

  • Generalize the existing skill traversal to operate on PathUri through ExecutorFileSystem, preserving its depth, directory, symlink, and sibling-metadata concurrency behavior.
  • Add a small environment skill loader that reuses the shared discovery, frontmatter validation, dependency parsing, product policy, and prompt-visibility rules.
  • Keep the environment id and entrypoint PathUri in the skill catalog, then route skills.read back through the same environment filesystem.
  • Preserve the executor's path convention when deriving catalog handles, including literal backslashes in POSIX filenames.
  • Resolve plugin namespaces from nearby manifests through URI-native filesystem reads.
  • Cover foreign Windows roots, executor-owned reads, namespaces, metadata, policy, and path identity.
selected root (PathUri)
        |
        v
shared discovery over ExecutorFileSystem
        |
        v
environment-bound catalog entry --skills.read--> same ExecutorFileSystem

No second filesystem abstraction or duplicate traversal implementation is introduced.

Stack

  1. path-uri: add lexical containment #29614 — add lexical PathUri containment.
  2. Decouple plugin manifest path resolution #29620 — share URI-native manifest path resolution.
  3. Make selected plugin roots URI-native #28918 — keep selected plugin roots and resources URI-native.
  4. This PR — load executor skills without host path conversion.
  5. Keep executor plugin MCP paths URI-native #29628 — resolve executor MCP working directories without host path conversion.

Comment thread codex-rs/core-skills/src/loader/environment.rs
Comment thread codex-rs/core-skills/src/loader/environment.rs Outdated
Comment thread codex-rs/core-skills/src/loader/environment.rs Outdated
jif-oai added a commit that referenced this pull request Jun 23, 2026
## Why

Plugin manifests use the same schema whether the package lives on the
host or in an executor. Only the path representation differs: host
callers need native `Path` inputs and `AbsolutePathBuf` outputs, while
executor callers need `PathUri` throughout.

Maintaining separate parsing or resolver implementations would duplicate
the manifest rules and allow them to drift. This PR instead makes
URI-native resolution the single parsing path and keeps host conversion
at the boundary.

## What changed

- Make `parse_plugin_manifest_uri` the shared manifest parser and
resolve every path-bearing field as `PathUri`.
- Keep the existing host entrypoint as a thin adapter: convert its
native root and manifest path to `PathUri`, run the shared parser, then
map resources back to `AbsolutePathBuf`.
- Expose `PluginManifest::try_map_resources` so callers can convert the
generic resource type without duplicating manifest construction.
- Resolve relative manifest paths using the root URI's convention:
backslashes are separators for Windows roots and ordinary filename
characters for POSIX roots.
- Apply lexical containment after URI resolution, rejecting absolute
paths and parent traversal outside the plugin root.
- Make encoded backslashes fail containment only for Windows URIs;
encoded `/` remains unsafe for every convention.
- Use a host-native synthetic root for marketplace fallback manifests so
the host adapter also works on Windows.

```text
host Path --------> PathUri --\
                              +--> one manifest parser --> PluginManifest<PathUri>
executor PathUri -------------/

host result: PluginManifest<PathUri> --> PluginManifest<AbsolutePathBuf>
```

Existing host manifest behavior is preserved; #28918 is the first
executor consumer.

## Verification

- `just test -p codex-utils-path-uri`
- `just test -p codex-plugin`
- `just test -p codex-core-plugins`

## Stack

1. #29614 — add lexical `PathUri` containment.
2. **This PR** — share URI-native manifest path resolution.
3. #28918 — keep selected plugin roots and resources URI-native.
4. #29626 — load executor skills without host path conversion.
5. #29628 — resolve executor MCP working directories without host path
conversion.
jif-oai added a commit that referenced this pull request Jun 23, 2026
## Why

Selected capability roots belong to the executor filesystem, not the
app-server host. Converting their path strings into the host's native
`Path` breaks whenever the two machines use different path conventions,
such as a Windows executor behind a Unix app-server.

This PR establishes `PathUri` as the selected-plugin boundary so the
executor remains authoritative for its paths.

## What changed

- Require `selectedCapabilityRoots[].location.path` to be a canonical
`file:` URI and deserialize it directly as `PathUri`; native path
strings are rejected.
- Update the app-server schema, generated TypeScript, examples, and
request coverage for the URI contract.
- Keep selected roots, resolved plugin locations, manifest paths, and
manifest resources as `PathUri`.
- Inspect and read plugin roots and manifests only through the selected
environment's `ExecutorFileSystem`.
- Parse executor manifests with the shared URI-native parser from #29620
instead of projecting them onto the host filesystem.
- Enforce resource containment lexically and preserve the root URI's
POSIX or Windows path convention.
- Cover foreign Windows plugin roots and URI-native manifest resources.

```text
thread/start
  selectedCapabilityRoots[].location.path = "file:///C:/plugins/demo"
                              | PathUri
                              v
                    ExecutorFileSystem
                              |
                              +--> plugin.json
                              +--> manifest resources
```

This PR stops at the shared selected-plugin representation. The next two
PRs remove the remaining host-path projections in the skill and MCP
consumers.

## Stack

1. #29614 — add lexical `PathUri` containment.
2. #29620 — share URI-native manifest path resolution.
3. **This PR** — keep selected plugin roots and resources URI-native.
4. #29626 — load executor skills without host path conversion.
5. #29628 — resolve executor MCP working directories without host path
conversion.
Base automatically changed from jif/uri-native-selected-plugins to main June 23, 2026 21:51
@jif-oai
jif-oai requested a review from a team as a code owner June 23, 2026 21:51
@jif-oai
jif-oai force-pushed the jif/uri-native-executor-skills branch from 4f503c4 to 4ed0594 Compare June 23, 2026 21:55
@jif-oai
jif-oai merged commit 220f5b7 into main Jun 23, 2026
31 checks passed
@jif-oai
jif-oai deleted the jif/uri-native-executor-skills branch June 23, 2026 22:26
@github-actions github-actions Bot locked and limited conversation to collaborators Jun 23, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants