Skip to content

@loopover/contract is unpublished but is a runtime dependency of @loopover/mcp and @loopover/miner — the next release of either ships uninstallable #9749

Description

@JSONbored

The problem

@loopover/contract is not on npm:

$ curl -s -o /dev/null -w "%{http_code}" https://registry.npmjs.org/@loopover%2Fcontract
404
$ npm install "@loopover/contract@^0.1.0" --dry-run
npm error 404  '@loopover/contract@^0.1.0' is not in this registry.

But both published CLI packages now declare it as a runtime dependency, not a dev one:

  • packages/loopover-mcp/package.json"@loopover/contract": "^0.1.0" in dependencies
  • packages/loopover-miner/package.json → same

And both have real runtime imports in code that ships, built with plain tsc (no bundler, so the specifiers survive into the emitted JS):

  • packages/loopover-mcp/lib/telemetry.ts@loopover/contract
  • packages/loopover-mcp/bin/loopover-mcp.ts@loopover/contract, /api-schemas, /tools
  • packages/loopover-miner/lib/tenant-credential-resolution.ts@loopover/contract/orb-broker
  • packages/loopover-miner/lib/github-token-resolution.ts@loopover/contract/cli-config

Why it hasn't broken yet

The currently-published versions predate the dependency. @loopover/mcp@3.14.0 declares only @loopover/engine, @modelcontextprotocol/sdk, posthog-node, zod; @loopover/miner@3.14.1 likewise. Both still install cleanly today — verified.

It fires on the next release of either package. That publish will carry "@loopover/contract": "^0.1.0", and npm install -g @loopover/mcp@latest will fail with E404 for every external user.

Worth noting this also breaks the documented contributor workflow: .claude/skills/contributing-to-loopover/SKILL.md instructs npm install -g @loopover/mcp@latest as the pre-submit gate oracle.

Supporting evidence that publishing was intended

packages/loopover-contract/package.json is already shaped for it — no private: true, "publishConfig": { "access": "public" }, and a files: ["dist", "CHANGELOG.md"] allowlist. It is the only workspace package with that shape and no publish-*.yml workflow (engine, mcp, miner, ui-kit all have one). It looks like the publish step was simply never added when the package was split out.

Options

A. Publish it (add publish-contract.yml mirroring publish-engine.yml). Matches the package's existing shape and the other four packages' pattern. Contract must publish before any mcp/miner release, so release ordering becomes a real constraint.

B. Bundle it into the consumers — switch mcp/miner from plain tsc to a bundler that inlines @loopover/contract, and demote it to a devDependency. Keeps one less public package, but changes both build pipelines and their dist shape.

C. Vendor the shared types into each consumer. Cheapest to ship, worst for drift — it re-creates exactly the duplication #9521 consolidated into this package.

Recommend A: it is the smallest change, matches how every sibling package already ships, and preserves the single-source-of-truth the contract package exists to provide.

Guard worth adding either way

Nothing currently catches this. test:mcp-pack / test:miner-pack pack the tarball but never install it from a clean registry-only context, so an unresolvable dependency passes. A check that installs the packed tarball with --omit=dev against the public registry would have caught it before the dependency ever landed.

Metadata

Metadata

Assignees

Labels

maintainer-onlyOwner-only work — yields no Gittensor points.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions