Skip to content

feat(cli): install shared-model dependencies from npm and Python packages in meta deps sync - #501

Merged
dmealing merged 6 commits into
mainfrom
fm/mo-deps-pkg-transports
Oct 28, 2026
Merged

dmealing merged 6 commits into
mainfrom
fm/mo-deps-pkg-transports

Conversation

@dmealing

Copy link
Copy Markdown
Member

What Changed

  • meta deps sync, meta deps check and verify --deps now resolve npm and python dependency transports, where before sync refused them as "not supported by this toolchain yet". The new dependency-transports.ts finds the package the way Node resolves it from the directory holding .metaobjects/, or through importlib.util.find_spec in the chosen Python interpreter. The interpreter is the first of $METAOBJECTS_PYTHON, the active virtualenv, a .venv beside .metaobjects/, and python3, and sync prints the one it used. The resolved dir is then snapshotted and locked exactly as a path dependency, and the lock format is unchanged. Failures are ERR_DEPENDENCY_UNRESOLVED (package not installed, no interpreter, or missing dir) and ERR_DEPENDENCY_MANIFEST_INVALID. sync also warns when the installed package version differs from its manifest version.
  • sharedModelFile() (and its ejectable reference copy) now falls back to a static pyproject.toml [project] version when no package.json version is found. A Python package can therefore stamp the version its consumers install.
  • Added a 14-case transport-cases.json conformance arm (Node only) with a runner test, two new dependency-conformance cases in cases.json, and deps-sync-packages.test.ts. Updated CHANGELOG.md, docs/features/metadata-dependencies.md (two-step npm and Python publish path, plus a private-registry recipe), the README, the CLI and conformance docs, and the regenerated agent-context skill references and their fixtures.
Validation

Risk Assessment

✅ Low: The change is bounded to Node deps sync/check/verify --deps transports plus docs and fixtures. I traced the npm walk, the python interpreter ladder and refusals, the PEP 440 version compare, and the pyproject version fallback; all behave as the intent requires. No private names or absolute paths were added, the case counts reconcile (37 resolution+check cases, 14 transport cases), and the tests assert behavior against independent oracles.

Testing

Baseline targeted tests pass (67). Drove the built CLI (dist/bin/meta.js) against disposable npm and python consumer projects under /tmp, since removed. npm: sync records the lock with resolvedFrom npm, deps check reports current, and an uninstalled package is refused naming npm install. Python: sync via METAOBJECTS_PYTHON succeeds, a missing VIRTUAL_ENV interpreter is refused even with a working .venv beside the config, a missing module and a dir that escapes the package are refused with the fix named, a parent-import failure says to fix that environment, and a real dist-info version mismatch warns and exits 0. Regression: full CLI package bun test (1819 tests, 0 fail), SDK dependency-conformance.test.ts (27 pass), Python tests/conformance/test_dependency_conformance.py (27 pass). Note: the harness sed initially rewrote a config name, a harness error, fixed and re-run.

  • Live validation: ✅ go - 9 of 10 scenarios driven live against the product
Scenario Result Live Evidence
npm: sync a published model from node_modules into the lock ✅ pass live meta deps sync exit 0; deps.lock.json records version 1.0.0, integrity sha256-10fbf886..., resolvedFrom npm @acme/common-model
npm: deps check reports installed package current ✅ pass live meta deps check: acme-common current 1.0.0, exit 0
npm: uninstalled package refused with install hint ✅ pass live 'the npm package "@acme/common-model" is not installed where ... Install it (npm install @acme/common-model...)' exit 1
python: sync a model from an installed package via METAOBJECTS_PYTHON ✅ pass live meta deps sync exit 0; lock acme-model 1.0.0; output shows interpreter /usr/bin/python3 (from $METAOBJECTS_PYTHON); interpreter path not in lock
python: set VIRTUAL_ENV with no interpreter is refused, not skipped (even with working .venv beside config) ✅ pass live '$VIRTUAL_ENV is ..., but it has no python interpreter' exit 1
python: missing module refused naming pip install ✅ pass live 'the Python package "acme.nothere" is not installed in /usr/bin/python3 ... (for example pip install acme)' exit 1
python: dir escaping the package refused ✅ pass live '"dir": "../../x" must be a path inside the python package' exit 1
python: parent-package import failure says fix the environment, not pip install ✅ pass live 'importing the parent package of "acme.model" ... raised ModuleNotFoundError ... Fix that environment' exit 1
python: installed version differing from manifest warns and does not fail ✅ pass live warning 'installed Python package "acme.model" is 1.0.0, but its shared-model manifest records 9.9.9'; exit 0; lock records 9.9.9
yarn Plug'n'Play and maven/nuget transports ⏸️ untested no Yarn PnP is documented unsupported, not a code path to drive. maven/nuget are out of scope for this change.

Pipeline

Updates from git push no-mistakes

✅ **Intent** - passed

✅ No issues found.

🔧 **Rebase** - 6 issues found → auto-fixed ✅
  • ⚠️ CHANGELOG.md - merge conflict rebasing onto origin/main
  • ⚠️ docs/CONFORMANCE.md - merge conflict rebasing onto origin/main
  • ⚠️ docs/features/cli.md - merge conflict rebasing onto origin/main
  • ⚠️ docs/features/metadata-dependencies.md - merge conflict rebasing onto origin/main
  • ⚠️ fixtures/dependency-conformance/README.md - merge conflict rebasing onto origin/main
  • ⚠️ fixtures/dependency-conformance/cases.json - merge conflict rebasing onto origin/main

🔧 Fix applied.
✅ Re-checked - no issues remain.

✅ **Review** - passed

✅ No issues found.

✅ **Test** - passed

✅ No issues found.

  • Live validation: ✅ go - 9 of 10 scenarios driven live against the product
Scenario Result Live Evidence
npm: sync a published model from node_modules into the lock ✅ pass live meta deps sync exit 0; deps.lock.json records version 1.0.0, integrity sha256-10fbf886..., resolvedFrom npm @acme/common-model
npm: deps check reports installed package current ✅ pass live meta deps check: acme-common current 1.0.0, exit 0
npm: uninstalled package refused with install hint ✅ pass live 'the npm package "@acme/common-model" is not installed where ... Install it (npm install @acme/common-model...)' exit 1
python: sync a model from an installed package via METAOBJECTS_PYTHON ✅ pass live meta deps sync exit 0; lock acme-model 1.0.0; output shows interpreter /usr/bin/python3 (from $METAOBJECTS_PYTHON); interpreter path not in lock
python: set VIRTUAL_ENV with no interpreter is refused, not skipped (even with working .venv beside config) ✅ pass live '$VIRTUAL_ENV is ..., but it has no python interpreter' exit 1
python: missing module refused naming pip install ✅ pass live 'the Python package "acme.nothere" is not installed in /usr/bin/python3 ... (for example pip install acme)' exit 1
python: dir escaping the package refused ✅ pass live '"dir": "../../x" must be a path inside the python package' exit 1
python: parent-package import failure says fix the environment, not pip install ✅ pass live 'importing the parent package of "acme.model" ... raised ModuleNotFoundError ... Fix that environment' exit 1
python: installed version differing from manifest warns and does not fail ✅ pass live warning 'installed Python package "acme.model" is 1.0.0, but its shared-model manifest records 9.9.9'; exit 0; lock records 9.9.9
yarn Plug'n'Play and maven/nuget transports ⏸️ untested no Yarn PnP is documented unsupported, not a code path to drive. maven/nuget are out of scope for this change.
  • scripts/ci-local.sh --only ts-fast --only ts-unit --strict-toolchains
  • bun test test/deps-sync.test.ts test/deps-sync-packages.test.ts test/dependency-transport-conformance.test.ts (server/typescript/packages/cli)
  • bun run build (server/typescript/packages/cli)
  • node dist/bin/meta.js deps sync — npm consumer, installed @acme/common-model
  • node dist/bin/meta.js deps check — npm consumer after sync
  • node dist/bin/meta.js deps sync — npm consumer with package removed (adversarial)
  • node dist/bin/meta.js deps sync — python consumer, METAOBJECTS_PYTHON=/usr/bin/python3
  • node dist/bin/meta.js deps sync — VIRTUAL_ENV set to empty dir with working .venv beside config (adversarial refusal)
  • node dist/bin/meta.js deps sync — python missing module, dir escaping package, parent-import failure (adversarial refusals)
  • node dist/bin/meta.js deps sync — python dist-info version 1.0.0 vs manifest 9.9.9 (version-mismatch warning)
  • bun test (full server/typescript/packages/cli package)
  • bun test test/dependency-conformance.test.ts (server/typescript/packages/sdk)
  • uv run pytest tests/conformance/test_dependency_conformance.py (server/python)
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

A shared model can now travel inside a published package instead of only
over a folder path. A dependency declared as { "npm": "@acme/model",
"dir": "metaobjects" } or { "python": "acme_model", "dir": "metaobjects" }
is found where the consumer's package manager installed it: node_modules
up the tree from the directory holding .metaobjects/, or
importlib.util.find_spec in the project's interpreter ($METAOBJECTS_PYTHON,
then $VIRTUAL_ENV, then a .venv beside .metaobjects/, then python3). sync
snapshots and locks it exactly as a path dependency; check and verify
--deps resolve the same way. A package that is not installed, a missing
interpreter or a dir the package does not ship is ERR_DEPENDENCY_UNRESOLVED
naming the npm install / pip install fix, and sync warns when the installed
package's version differs from its manifest. The lock format is unchanged
and records no host path.

On the publisher side, sharedModelFile()'s default version also reads a
pyproject.toml [project] version. The feature doc gains the two-step
publish path for npm and Python packages and a private-registry recipe.
The dependency corpus gains npm and python snapshot-load cases (run by
TypeScript and Python) and a transport-cases.json arm (14 cases, Node
only) that lays packages out under a temp node_modules and import path.
…not installed

find_spec raises ModuleNotFoundError when a parent of a dotted name is
missing. Treat that as not installed, with the pip install hint, and keep
the import-error wording for a parent whose own import fails on some
other module.
# Conflicts:
#	README.md
#	agent-context/skills/metaobjects-codegen/references/dependencies-and-shared-models.md
#	docs/CONFORMANCE.md
#	docs/features/cli.md
#	docs/features/metadata-dependencies.md
#	fixtures/agent-context-conformance/java-kotlin-react-tanstack/expected/.claude/skills/metaobjects-codegen/references/dependencies-and-shared-models.md
#	fixtures/agent-context-conformance/java-react/expected/.claude/skills/metaobjects-codegen/references/dependencies-and-shared-models.md
#	fixtures/agent-context-conformance/python/expected/.claude/skills/metaobjects-codegen/references/dependencies-and-shared-models.md
#	fixtures/agent-context-conformance/ts-react-tanstack/expected/.claude/skills/metaobjects-codegen/references/dependencies-and-shared-models.md
#	fixtures/agent-context-conformance/ts-requirements/expected/.claude/skills/metaobjects-codegen/references/dependencies-and-shared-models.md
#	fixtures/dependency-conformance/README.md
@dmealing
dmealing merged commit dc0a692 into main Oct 28, 2026
1 check passed
@dmealing
dmealing deleted the fm/mo-deps-pkg-transports branch October 28, 2026 16:29
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