Pipeline 2648 failed build-gateway with ERR_PNPM_OUTDATED_LOCKFILE
although publish-next-npm completed green and restored the workspace
byte-exact ('git diff clean' in its own log). Root cause: build-gateway
and publish-next-npm both depend on [build, verify] and run
concurrently; kaniko's COPY of apps/gateway/package.json raced inside
publish's in-place mutation window (transform .. publish .. restore).
PR 1405's snapshot/restore governs the END state of its own step only —
no mutual exclusion against a concurrent reader.
The snapshot/restore set was already a superset of the transform's
write set (both derive from apps/packages/plugins with the same
exclusions — the tasking's apps-coverage hypothesis is refuted by the
committed find). The defect is ordering, so the fix is a DAG edge:
build-gateway, build-appservice, and build-web each gain
publish-next-npm in depends_on, serializing every kaniko workspace
consumer after the restore. publish-npm (main-only, transform is
next-only) is unchanged. The invariant is stated inline at each edge:
any new workspace-consuming step must depend on publish-next-npm.
Red-first evidence (clean clone of next; scripts from the committed
YAML; gateway manifest as the demonstration case): pre-fix DAG shows
build-gateway deps == publish deps (concurrent); post-fix adds the
edge. Reading apps/gateway/package.json mid-window shows
0.0.3-next.2648 pins (the 2648 dump shape) and pnpm install
--frozen-lockfile fails ERR_PNPM_OUTDATED_LOCKFILE; the same read
after restore shows workspace:^ and the identical clean tree installs
rc=0. Closes#1411 (blocks PR 1401's merge — USC chain).
PR #1400's transform rewrites package.json files in the shared pipeline
workspace (pnpm publish reads the workspace manifests, so in-place is
required) but did not restore them. build-gateway's kaniko build then
COPYs the mutated manifests and its pnpm install --frozen-lockfile
refuses: pnpm-lock.yaml still records workspace:^ while the manifest
says 0.0.x-next.NNNN — ERR_PNPM_OUTDATED_LOCKFILE, next tip red
(pipeline 2646).
Fix (restore, chosen over tarball-scoped pack because pnpm publish
must read the workspace tree anyway — a temp-copy pack would need to
reconstruct the workspace linkage per package; restore is one cp per
manifest and byte-exact): snapshot every publishable manifest to a
step-local mktemp dir BEFORE the transform; after publish + the #1389
guard, restore byte-exact (cp -p preserves mtime), then a pristine
guard runs git diff --exit-code over '**/package.json' and fails THIS
step if any residual mutation remains — the #1404 class now fails in
publish-next-npm with a named cause instead of surfacing as a frozen-
lockfile error in build-gateway.
Red-first control (scratch clone of next, real scripts extracted from
the committed YAML): transform without restore -> git diff dirty
(guard red) AND pnpm install --frozen-lockfile reproduces
ERR_PNPM_OUTDATED_LOCKFILE verbatim (the 2646 failure class); with
snapshot+restore -> git diff clean, frozen install rc=0. Also fixed a
trailing space after the NODE heredoc opener introduced with #1400's
edit that would have broken the heredoc invocation in real CI.
Squash-merged by topher (jarvis principal) via break-glass API path (wrapper main-only gap, documented). Gates: CI 2492 green at head e19013ed, review 181 APPROVED (fred) at pinned head. Diagnostic merge: the push pipeline now exercises kaniko with the REGISTRY_* secrets - valid creds yield the first fully green gated publish; invalid yield an explicit 401.
Co-authored-by: Jarvis <[email protected]>
Squash-merged by topher (jarvis principal) via break-glass: pr-merge.sh hard-codes main-only merge targets and cannot express this repo's next trunk. Gates: CI 2476 green at head 46784c8d, review 177 APPROVED (fred) at pinned head. First gated publish: every publish step now depends on verify-release at the exact commit.
Durable @next integration-line publish: on next pushes, compute <patch+1>-next.<pipeline#> prerelease versions (in-CI, uncommitted) and publish @mosaicstack/* under the next dist-tag; gateway image sha-only on next. Strict guardrails: next-only, never writes latest, never tags from next; main path unchanged. PR-event CI 1631 fully green + review-of-record APPROVE (head b1a887a2). Guardrails independently verified.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Publish pipeline:
- Add publish-npm step to .woodpecker/publish.yml — publishes all
@mosaic/* packages to Gitea npm registry on main push/tag
- Requires gitea_npm_token Woodpecker secret (package:write scope)
- publish-npm runs after build, parallel with Docker image builds
- pnpm publish resolves workspace:* to concrete versions automatically
Package configuration:
- All 20 packages versioned at 0.0.1-alpha.1
- publishConfig added to all packages (Gitea registry, public access)
- files field added to all packages (ship only dist/)
- @mosaic/forge includes pipeline/ assets in published package
Meta package (@mosaic/mosaic):
- Now depends on @mosaic/forge, @mosaic/macp, @mosaic/prdy,
@mosaic/quality-rails, @mosaic/types
- npm install @mosaic/mosaic pulls in the standalone framework
Build fixes:
- Fix forge and macp tsconfig rootDir: '.' -> 'src' so dist/index.js
resolves correctly (was dist/src/index.js)
- Exclude __tests__ and vitest.config from build includes
- Clean stale build artifacts from old rootDir config
Required Woodpecker secret:
woodpecker secret add mosaic/mosaic-stack \
--name gitea_npm_token --value '<token>' \
--event push,manual,tag