You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
⚠️ 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-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:
release-please-config.json → packages keys are packages/loopover-mcp, packages/loopover-engine, packages/loopover-miner, packages/loopover-ui-kit only.
.release-please-manifest.json → the same four.
.github/workflows/ → publish-engine.yml, publish-mcp.yml, publish-miner.yml, publish-ui-kit.yml; no publish-contract.yml.
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
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).
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.
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.
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.
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
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.
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.
Context
#9517 created
packages/loopover-contractand #9518/#9536/#9537 made it a runtime dependency ofboth published CLIs:
packages/loopover-mcp/package.json—"dependencies": { "@loopover/contract": "^0.1.0", ... }packages/loopover-miner/package.json— the same entry@loopover/contractis authored to be published: it has"publishConfig": { "access": "public" }, afilesallowlist, a fullexportsmap (.,./enums,./tools,./agent-specs) and abuildscript. But it is not published:
GET https://registry.npmjs.org/@loopover%2fcontractreturns{"error":"Not found"}(404). It is also absent from every piece of this repo's release machinery:release-please-config.json→packageskeys arepackages/loopover-mcp,packages/loopover-engine,packages/loopover-miner,packages/loopover-ui-kitonly..release-please-manifest.json→ the same four..github/workflows/→publish-engine.yml,publish-mcp.yml,publish-miner.yml,publish-ui-kit.yml; nopublish-contract.yml.RELEASE_AUTOMATION_WATCHED_PATHS(scripts/lib/validate-mcp/overrides.ts:40-50) — the anti-rotguard added by mcp: validate:mcp contract validator — Ajv output enforcement, per-tool smoke gate, version tri-lock across all three servers #9520 — watches the other three publish workflows and never mentions the contract
package.
The currently published
@loopover/mcp@3.14.0predates 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.jsonis3.15.2with the contract dependency, andpublish-mcp.yml:158-159smoke-installs the packed tarball into a temp prefix(
npm --prefix "$TMP" install "$TARBALL_PATH") — which resolves@loopover/contract@^0.1.0from thepublic 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:cisays so:scripts/check-mcp-package.tsonly inspectsnpm pack --dry-run's file list(
loadMcpPackFromNpm(),validateMcpPackFileList()) and never resolves a dependency.Requirements
packages/loopover-contractbecomes a first-class released workspace: registered inrelease-please-config.jsonand.release-please-manifest.jsonalongside the other fourpackages, with a
packages/loopover-contract/CHANGELOG.md(thefilesarray already lists onethat does not exist).
.github/workflows/publish-contract.ymlthat mirrors.github/workflows/publish-engine.ymlexactly in structure:
workflow_dispatchonly with thereleased_by_release_pleaseinput, theunprivileged
validatejob (verify release commit is on main → resolve version → resolve therelease SHA by
git log -Son the version string → pack → tarball file allowlist + secret scan →temp-prefix install smoke test importing
listToolDefinitionsfrom the installed tarball →upload artifact), the privileged
publishjob (environment: release, tag creation at theresolved SHA, OIDC trusted publish of the uploaded tarball), and the
github-releasejob. Allthird-party actions SHA-pinned to the same SHAs
publish-engine.ymlalready uses.packages/loopover-contractis added toRELEASE_AUTOMATION_WATCHED_PATHS(
scripts/lib/validate-mcp/overrides.ts) — at minimum itspackage.json, itsCHANGELOG.mdandthe new workflow file — so
checkWatchedPathsExistfails if any of them is moved or deleted.npm run test:ci, that fails when any workspace package listed inrelease-please-config.jsondeclares adependenciesentry on another workspace package that isitself not listed in
release-please-config.json. This is the check that would have caught thedefect on the PR that introduced it.
packages/loopover-contractpack check mirroringscripts/check-mcp-package.ts: the packedfile list matches an allowlist (
dist/**,package.json,README.md,CHANGELOG.md) and nopacked file matches
FORBIDDEN_CONTENT.Deliverables
packages/loopover-contractpresent inrelease-please-config.jsonand.release-please-manifest.json, withpackages/loopover-contract/CHANGELOG.mdcreated.github/workflows/publish-contract.ymlmirroringpublish-engine.yml's two-jobprivilege-split shape, SHA-pinned,
environment: release, OIDC publishRELEASE_AUTOMATION_WATCHED_PATHSinscripts/lib/validate-mcp/overrides.tsincludes thecontract package's
package.json,CHANGELOG.mdandpublish-contract.yml, asserted by theexisting
checkWatchedPathsExistcase intest/contract/validate-mcp.test.tstest:cistep that fails when a release-tracked workspace package depends on anon-release-tracked workspace package, with a unit test covering both the passing and failing
arm over a fixture manifest (not the live repo files)
scripts/check-contract-package.ts+ allowlist module pair mirroring the MCP pair, wiredinto
test:ci, with unit tests for the forbidden-path, unexpected-file and forbidden-contentrejections
All Deliverables above are required in a single PR. A PR that satisfies only some of them — for
example adding
publish-contract.ymlwithout the release-please registration and without theworkspace-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.tsand the workspace-dependency guard live underscripts/**,which is not in
coverage.include(vitest.config.ts:55-100) — but real unit tests are stillrequired and are the only mechanical proof the guards work: follow
test/unit/*mcp-package*anddrive 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/**orpackages/*/{src,lib,bin}/**while doing this is Codecov-graded at 99% branch-counted, so botharms of every changed conditional there need a test.
Expected Outcome
@loopover/contractis a published, release-tracked package; the next@loopover/mcpand@loopover/minerreleases install cleanly for end users instead of failing atpublish-*.yml'starball smoke install; and CI refuses any future PR that adds a runtime dependency from a released
package onto an unreleased workspace package.
Links & Resources
packages/loopover-contract/package.json,packages/loopover-mcp/package.json,packages/loopover-miner/package.json.github/workflows/publish-engine.yml(the pattern),publish-mcp.yml:141-172,publish-miner.yml:139-164(the smoke-install steps that will fail)release-please-config.json,.release-please-manifest.jsonscripts/check-mcp-package.ts,scripts/mcp-package-allowlist.ts,scripts/lib/validate-mcp/overrides.ts:40-50Maintainer note (out of scope for this PR): as
publish-engine.yml's own header records, npm'sOIDC trusted publishing cannot create a brand-new package — the first
@loopover/contractpublishand the npmjs.com Trusted Publisher configuration are a one-time maintainer step performed after this
PR lands.