Imported from jezdez/conda-wasm (
AGENTS.md). Install upstream withnpx skills add jezdez/conda-wasm. Copyright stays with the author.
AGENTS.md - conda-wasm coding guidelines
Project Structure
-
conda-wasmis a browser/WebAssembly stack for running real conda in JupyterLite and other Emscripten-hosted Python environments. The repository contains the Rust WASM module, the Python runtime and conda plugin, the JupyterLite frontend extension, demo site assets, docs, and conda recipes. -
The Rust crate lives in
crates/conda-wasm/. Keep Rust code split by responsibility:bootstrap.rsfor startup/bootstrap helpers,solve.rsfor solver entry points,extract.rsfor package extraction,sharded.rsfor sharded repodata handling,gateway.rsfor fetch/decode plumbing, anderror.rsfor shared error conversion. Public WASM exports belong inlib.rs; substantial logic belongs in the focused modules. -
The Python package lives in
python/conda_wasm/. Keep the public API packages small:runtime/__init__.pyowns browser runtime setup, with runtime helpers inruntime/assets.py,runtime/loader.py,runtime/globals.py,runtime/prefetch.py, andruntime/state.py.magic/__init__.pyowns IPython magic registration, with command, prefix, and shared-library helpers inmagic/command.py,magic/prefix.py, andmagic/shared_libs.py.plugin/__init__.pyowns conda hook registration,plugin/solver.pyowns the conda solver backend,plugin/extractor.pyowns package extraction helpers, andplugin/patches.pyis the runtime patch facade. -
Do not introduce generic
_supportpackages or underscore module packages. Prefer concrete package names that are already part of the public import surface, such asruntime/,magic/, andplugin/compat/. Avoid "bridge",cx, andconda-expressterminology in new code. -
plugin/compat/contains focused compatibility patches for conda under Emscripten. Keep each patch in the module named for the thing being patched (download.py,extract.py,repodata.py,subprocess.py,urllib3.py, etc.).plugin/patches.pyshould stay a small coordinator. -
The JupyterLite extension lives in
jupyterlite/. TypeScript source belongs injupyterlite/src/, built output injupyterlite/lib/andjupyterlite/labextension/is generated and should not be hand-edited. -
The demo site lives in
demo/. Demo notebooks and JupyterLite config should demonstrate the real browser workflow, not duplicate runtime implementation logic. -
Recipes live in
recipes/.recipes/conda/is the patched conda recipe for Emscripten, andrecipes/conda-wasm/packages the Python runtime plus WASM assets copied fromcrates/conda-wasm/pkg/. -
Documentation lives in
docs/and uses Sphinx withconda-sphinx-theme,myst-parser,sphinx-design,sphinx-copybutton,sphinx-reredirects, andsphinx-sitemap.
Naming
-
Prefer direct, descriptive names over private-by-default names. A leading underscore is appropriate for Python protocol hooks, third-party API contracts, or a genuinely local implementation variable, but not as a general way to organize modules.
-
Keep public imports stable for users:
conda_wasm.runtime,conda_wasm.magic, andconda_wasm.plugin.patchesare public surfaces. Move complexity behind them without changing their import paths. -
Use
conda-wasmfor the project/package/crate andconda_wasmfor Python import paths. Do not reintroducecx-wasm,cx-jupyterlite,conda-emscripten-plugin, orconda-expressnames.
Imports
-
Use relative imports for intra-package Python references when practical (
from .loader import load_conda_wasm,from ..extractor import extract_wasm). Absoluteconda_wasm.*imports are acceptable from support code that is intentionally crossing package boundaries or avoiding circular imports. -
Inline imports are reserved for platform-specific or optional runtime dependencies. Acceptable cases include
js/pyjs, conda internals that may not be installed in native smoke tests, plugin hook bodies loaded by conda, and browser-only paths. Everywhere else, imports belong at the top of the module. -
All Python modules should use
from __future__ import annotations.
Dependencies
-
Minimize the dependency graph. Prefer stdlib, conda APIs, Rust crates already in use, or JupyterLite/JupyterLab packages already required by the extension over adding new dependencies.
-
Pin minimum supported versions in manifests and recipes, not exact versions, unless an exact pin is required for a known compatibility constraint.
-
After changing
pixi.toml, always runpixi lock --checkfirst. If the lockfile is out of date, runpixi lockand keep thepixi.lockupdate with the manifest change.
Python and Conda Plugin Code
-
Use modern type annotations (
str | None,list[str],dict[str, Any]). -
Use conda's own APIs where available (
conda.base.context.context,conda.plugins.types,conda.models.records,conda.gateways.*) instead of reimplementing conda behavior. -
Keep conda plugin import overhead low.
conda_wasm.pluginis discovered by conda through[project.entry-points.conda], so avoid importing heavy solver, runtime, browser, or extraction code at module import time. -
Runtime patches should be idempotent. A patch function should be safe to call from both the
%condamagic path and conda'sconda_pre_commandshook. -
Browser-only code must guard native execution. Use
sys.platform == "emscripten"checks where importing or calling JS/browser APIs would fail on native Python. -
Timing and diagnostic output should be opt-in unless it is essential user feedback. Use
CONDA_WASM_TIMING=1for timing details.
Rust Code
-
Keep WASM exports thin. Parse inputs, call focused helpers, and convert errors at the boundary.
-
Prefer typed request/response structs with
serdeover ad hoc JSON access. -
Use
wasm-bindgenandweb-sysAPIs deliberately. When a function must work in the browser worker context, avoid APIs that only exist on the main thread. -
Run
pixi run wasm-testafter changing Rust logic. Runpixi run -e web wasm-buildwhen changes affect exported WASM artifacts.
JupyterLite Extension
-
Keep TypeScript changes scoped to the extension behavior in
jupyterlite/src/. Do not edit generatedlib/orlabextension/output by hand. -
Run
pixi run -e demo demo-buildorpixi run -e demo demo-build-localafter changes that affect the demo or extension integration. Use the local build when validating locally builtconda-wasmpackages.
Testing and Verification
-
For Python-only changes, at minimum run:
python3 -m compileall -q python demo/build.py. -
For import-surface changes, run a native import smoke test with
sys.path.insert(0, "python")and importconda_wasm.runtime,conda_wasm.magic, and the relevant plugin modules. -
For package changes, build the recipe with a temporary output directory to avoid local
output/cleanup noise:pixi run -e recipes rattler-build build --recipe recipes/conda-wasm/recipe.yaml -c conda-forge --output-dir /private/tmp/conda-wasm-output. -
For docs changes, run
pixi run -e docs docs. -
For recipe changes, run the relevant recipe task from the
recipesenvironment. Usepixi run -e recipes build-conda-wasmfor the Python/WASM package andpixi run -e recipes build-condafor the patched conda recipe. -
Always run
git diff --checkbefore considering the work done.
Documentation
-
Follow Diataxis structure where the docs grow: tutorials for learning paths, how-to guides for task-oriented workflows, reference for exact behavior, and explanation for design tradeoffs.
-
Keep browser workflow docs concrete. Show how the Rust WASM module, Python runtime, conda plugin, patched conda recipe, and JupyterLite demo fit together.
-
Avoid excessive bold and italic in prose, list items, and headings. Let the text carry the emphasis.
-
Keep
sphinx-designtab labels short to avoid overflow on narrow viewports.
Generated Files and Cleanup
-
Do not hand-edit generated outputs: Rust
target/,crates/conda-wasm/pkg/, JupyterLitelib/, JupyterLitelabextension/, docs_build/, demo_output/, demo_env/, Python__pycache__/, and recipeoutput/. -
Remove
.DS_Store,__pycache__/, and temporary build output before final status checks.