Skip to content

release(contract): publish `@loopover/contract #9654

Description

@JSONbored

⚠️ Definition of Done: this issue must be completed in full, in a single PR. Do not split this
work across multiple PRs, and do not defer any Deliverable below to a follow-up issue. A PR that
satisfies only some of the Deliverables, stubs a required test, or leaves a checkbox
partially-done does NOT resolve this issue and will be closed.

Context

#9517 created packages/loopover-contract and #9518/#9536/#9537 made it a runtime dependency of
both published CLIs:

  • packages/loopover-mcp/package.json"dependencies": { "@loopover/contract": "^0.1.0", ... }
  • packages/loopover-miner/package.json — the same entry

@loopover/contract is authored to be published: it has "publishConfig": { "access": "public" }, a
files allowlist, a full exports map (., ./enums, ./tools, ./agent-specs) and a build
script. But it is not published: GET https://registry.npmjs.org/@loopover%2fcontract returns
{"error":"Not found"} (404). It is also absent from every piece of this repo's release machinery:

The currently published @loopover/mcp@3.14.0 predates the dependency, so nothing is broken today.
The next release is not fine: #8638 ("MCP release due: 3.15.2") is open, HEAD's
packages/loopover-mcp/package.json is 3.15.2 with the contract dependency, and
publish-mcp.yml:158-159 smoke-installs the packed tarball into a temp prefix
(npm --prefix "$TMP" install "$TARBALL_PATH") — which resolves @loopover/contract@^0.1.0 from the
public registry and will fail with E404. The identical step exists at publish-miner.yml:150-151.
So both publish pipelines are dead on arrival, and nothing in npm run test:ci says so:
scripts/check-mcp-package.ts only inspects npm pack --dry-run's file list
(loadMcpPackFromNpm(), validateMcpPackFileList()) and never resolves a dependency.

Requirements

  1. packages/loopover-contract becomes a first-class released workspace: registered in
    release-please-config.json and .release-please-manifest.json alongside the other four
    packages, with a packages/loopover-contract/CHANGELOG.md (the files array already lists one
    that does not exist).
  2. A .github/workflows/publish-contract.yml that mirrors .github/workflows/publish-engine.yml
    exactly in structure: workflow_dispatch only with the released_by_release_please input, the
    unprivileged validate job (verify release commit is on main → resolve version → resolve the
    release SHA by git log -S on the version string → pack → tarball file allowlist + secret scan →
    temp-prefix install smoke test importing listToolDefinitions from the installed tarball →
    upload artifact), the privileged publish job (environment: release, tag creation at the
    resolved SHA, OIDC trusted publish of the uploaded tarball), and the github-release job. All
    third-party actions SHA-pinned to the same SHAs publish-engine.yml already uses.
  3. packages/loopover-contract is added to RELEASE_AUTOMATION_WATCHED_PATHS
    (scripts/lib/validate-mcp/overrides.ts) — at minimum its package.json, its CHANGELOG.md and
    the new workflow file — so checkWatchedPathsExist fails if any of them is moved or deleted.
  4. A new CI guard, run from npm run test:ci, that fails when any workspace package listed in
    release-please-config.json declares a dependencies entry on another workspace package that is
    itself not listed in release-please-config.json. This is the check that would have caught the
    defect on the PR that introduced it.
  5. A packages/loopover-contract pack check mirroring scripts/check-mcp-package.ts: the packed
    file list matches an allowlist (dist/**, package.json, README.md, CHANGELOG.md) and no
    packed file matches FORBIDDEN_CONTENT.

⚠️ Required pattern: mirror .github/workflows/publish-engine.yml (job split, permissions,
environment: release, git log -S release-SHA resolution, tarball allowlist + secret scan +
temp-prefix install smoke) and scripts/check-mcp-package.ts + scripts/mcp-package-allowlist.ts
for the pack check. A push-triggered or tag-triggered workflow, a single-job workflow that packs
and publishes in the same job, a workflow that publishes with an NPM_TOKEN secret instead of
OIDC trusted publishing, unpinned uses: references, or a pack check that reuses
MCP_PACKAGE_ALLOWED_FILE_PATTERNS instead of declaring the contract package's own allowlist do
NOT satisfy this issue.

Deliverables

  • packages/loopover-contract present in release-please-config.json and
    .release-please-manifest.json, with packages/loopover-contract/CHANGELOG.md created
  • .github/workflows/publish-contract.yml mirroring publish-engine.yml's two-job
    privilege-split shape, SHA-pinned, environment: release, OIDC publish
  • RELEASE_AUTOMATION_WATCHED_PATHS in scripts/lib/validate-mcp/overrides.ts includes the
    contract package's package.json, CHANGELOG.md and publish-contract.yml, asserted by the
    existing checkWatchedPathsExist case in test/contract/validate-mcp.test.ts
  • A new script + test:ci step that fails when a release-tracked workspace package depends on a
    non-release-tracked workspace package, with a unit test covering both the passing and failing
    arm over a fixture manifest (not the live repo files)
  • A scripts/check-contract-package.ts + allowlist module pair mirroring the MCP pair, wired
    into test:ci, with unit tests for the forbidden-path, unexpected-file and forbidden-content
    rejections

All Deliverables above are required in a single PR. A PR that satisfies only some of them — for
example adding publish-contract.yml without the release-please registration and without the
workspace-dependency guard, so the same class of defect can recur — does not resolve this issue.

Test Coverage Requirements

This repo enforces 99%+ Codecov patch coverage, branch-counted. The new
scripts/check-contract-package.ts and the workspace-dependency guard live under scripts/**,
which is not in coverage.include (vitest.config.ts:55-100) — but real unit tests are still
required and are the only mechanical proof the guards work: follow test/unit/*mcp-package* and
drive the pure exported functions with fixture inputs, asserting both the accept and the reject arm
of every conditional you add. Any change you make under src/** or
packages/*/{src,lib,bin}/** while doing this is Codecov-graded at 99% branch-counted, so both
arms of every changed conditional there need a test.

Expected Outcome

@loopover/contract is a published, release-tracked package; the next @loopover/mcp and
@loopover/miner releases install cleanly for end users instead of failing at publish-*.yml's
tarball smoke install; and CI refuses any future PR that adds a runtime dependency from a released
package onto an unreleased workspace package.

Links & Resources

Maintainer note (out of scope for this PR): as publish-engine.yml's own header records, npm's
OIDC trusted publishing cannot create a brand-new package — the first @loopover/contract publish
and the npmjs.com Trusted Publisher configuration are a one-time maintainer step performed after this
PR lands.

Metadata

Metadata

Assignees

Labels

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

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions