Skip to content

fix(live-e2e): the backend cold install writes overrides.vitest so npm 10 can install the published graph (objectui#12649) - #12674

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-12649-live-backend-cold-install
Oct 16, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-12649-live-backend-cold-install

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #12649

Clause-②: no

The Live E2E (informational) lane's backend now installs cold on the runner's npm. e2e/live/ci/start-backend.sh writes one root overrides entry, vitest: ~4.0.0, into the rewritten showcase manifest. The same value is the third component of the fixture stamp, so a fixture prepared under another install recipe is rebuilt, not reused. The CI/CD guide's paragraph that said "no transitive dependency is overridden" now names this one and says what the fixture cache still cannot see.

Reproduction

  • Runner npm, read from the failing job's actions/setup-node step (job 116927385035): Node 22.23.3, npm 10.9.9.
  • Every local run below ran the real script end to end, under npm 10.9.9 or 11.21.0 fetched into a temp prefix, with a fresh npm cache and a fresh LIVE_BACKEND_DIR.
  • Baseline, the origin/main script at f78efdd1e under npm 10.9.9: exit 1 with npm error Cannot read properties of null (reading 'edgesOut'). The stack is in #loadPeerSet under idealTree:node_modules/@objectstack/cli. That is the CI failure, reproduced on the runner's npm.
  • Why it crashes: the installed graph reaches vitest only as an optional peer, from better-auth@1.7.3 (nested under @objectstack/plugin-auth) and from @objectstack/metadata-core@18.0.0-next.2. That was read by scanning the installed tree. Upstream today, npm view vitest dist-tags gives V4: 4.1.12, and the 4.x line of @vitest/browser-webdriverio ends at 4.1.11.

Remedy chosen, and the alternatives' readings

route npm install boot: sign-in · /api/v1/health · /api/v1/ready allowlist installed tree
(a) overrides.vitest: ~4.0.0 (chosen) 10.9.9 exit 0, 528 packages ready · 200 · 200 5 passed same as (b); no vitest installed
(b) npm 11, no override 11.21.0 exit 0, 528 packages ready · 200 · 200 5 passed the reference
(c) --legacy-peer-deps 10.9.9 exit 0, 506 packages ready · 200 · 200 5 passed differs from (b)
  • Why (a). It is the simplest correct route. It is objectstack's documented remedy for this crash: the troubleshooting entry "Cannot read properties of null (reading 'edgesOut') from npm install", objectstack 7a6ba03109. objectstack's scaffolds emit the same override (fix(cli): os init / os create emit overrides.vitest so new projects install on npm 10 objectstack#23850).
  • (a) installs the tree (b) installs. The two lockfiles' packages maps were compared path for path and version for version: 554 entries each, and no difference beyond npm 10 and npm 11 writing different optional/peer flags. So the lane still tests the published pair.
  • (b) is equally correct, but it adds a runtime npm download to the script and installs no different tree.
  • (c) installs a tree nobody ships. It drops @objectstack/plugin-auth's better-sqlite3@12.11.1 peer with its 21-package build subtree, and hoists better-auth to the root. The claim allowed it only as the last resort.

Boot and allowlist proof: what was measured, and what was not

  • (a), cold, through the changed script: the install exits 0. The script's own readiness probe, the seeded admin's sign-in, answered. GET /api/v1/health gave 200 (status: ok, version: 18.0.0-next.2), and GET /api/v1/ready gave 200 (status: ready).
  • Allowlist: the console was built with VITE_BASE_PATH=/, as in the workflow's Build console step, and served by vite preview with DEV_PROXY_TARGET at the backend. The four specs test:e2e:live:ci names ran: 5 passed.
  • Two container-only deviations in that run: the browser was the preinstalled Chromium, through the launch executablePath, and video recording was off, because this container has no ffmpeg for this Playwright revision. Both came from a scratch config that spread playwright.live.config.ts and changed nothing else. It was deleted afterwards and is not in this diff.
  • The stamp, both directions:
    • Rerunning the changed script on its own prepared fixture printed prepare: pins unchanged (… 18.0.0-next.2 vitest@~4.0.0), reusing, in 13 s with no install.
    • Running it on a fixture the origin/main script had prepared printed prepare: showcase@… and installed cold again: exit 0, 528 packages. That fixture's stamp has no third component, which is what a restored cache would carry.
  • NOT MEASURED here: the GitHub runner itself (only its Node and npm versions were reproduced), and the nightly schedule path.

Is this PR's own Live E2E run cold?

  • The fixture cache key is live-backend- plus the runner OS plus hashFiles('e2e/live/ci/backend.env'). This PR does not touch backend.env, so the key stays 3e640c78135ae9fa….
    • That hash was recomputed locally as the sha256 of the file's sha256. It matches the key the failing job printed, which is the positive control.
  • No cache under that key is readable by this PR. A pull request run reads its own caches and main's, and main's are written only by the nightly schedule and manual dispatch. The workflow's run list since 11:00Z on 2026-10-16 shows only pull_request runs, so nothing on main has saved a fixture since f62b6ff moved backend.env.
  • Even on a hit, the stamp's new third component rejects any fixture prepared without it, so the script cold-installs.
  • This body is written once. The reading of the run itself, from its Start ObjectStack backend (published packages) step, goes in the os-dev report on Live E2E: a cold live-backend install crashes in npm (Cannot read properties of null (reading 'edgesOut')) on next.1 and next.2 alike; the lane is green only from its cache #12649.

What the cache still hides

  • The key hashes backend.env alone. So a cold install that breaks because the registry moved under unchanged pins stays hidden until OBJECTSTACK_VERSION moves. That is how this one broke: the card's 18.0.0-next.1 control fails the same way.
  • Closing that needs a periodic cold run or a committed backend lockfile. The first is a workflow edit, which is governed and outside this claim, so it is named here and in the report, not done. The docs page now states the limit.

Removal condition

  • Written at the override: remove VITEST_OVERRIDE, the overrides line in prepare(), and the stamp's third component once the published @objectstack/cli at OBJECTSTACK_VERSION installs on npm 10 with no override.
  • That is not yet true. objectstack's metadata-core change (fix(metadata-core): drop the optional vitest peer so npm 10 installs it again objectstack#23834) is not in a published next, and better-auth's optional vitest peer reaches the crash on its own.
  • The comment gives the re-measure recipe, and warns of a trap measured here. Delete the override in a prepared dir and run npm install again, and npm answers up to date with exit 0, because it installs from the lockfile. That rerun proves nothing; only a fresh dir does.

Gates (local, at eed841f45)

  • node scripts/check-governed-queue-guard.mjs --test e2e/live/ci/start-backend.sh content/docs/guide/ci-cd-pipeline.md: exit 0, NOT GOVERNED.
  • node scripts/check-changeset-presence.mjs: exit 0, no changeset owed. The two files are neither published source nor a release-covered manifest.
  • These all exit 0: check-bash32-floor, check-doc-links, check-control-bytes, check-new-cross-file-line-citations (0 new), check-doc-fence-languages and check-shell-escape-residue. bash -n on the script is clean.
  • check-doc-json-fences first refused with exit 2, a prerequisite: @object-ui/types was not built. After the build it exits 0.
  • Tests: pnpm exec vitest run over the 17 test files that name ci-cd-pipeline.md or start-backend.sh (found with git grep -l): 17 files and 737 tests passed.
  • Everything else is CI's, read from this PR's check-runs.

Acceptance notes

  • stop-backend.sh does not stop the backend. This is pre-existing and not touched here.
    • start() records $! of a backgrounded cd … && nohup … objectstack dev list. That is the forked subshell, not the server.
    • Measured with the origin/main scripts: the pid file held 30766, and the server was 30767, its child. stop-backend.sh printed stopped pid 30766 and exited 0, while /api/v1/health on the port still answered 200. The server was reparented to pid 1.
    • CI never calls it (ephemeral runner), so the lane is unaffected. The local recipe it exists for leaks the server and the port. It goes to the seat as a finding.
  • The cache's remaining blind spot is above.

Session: https://claude.ai/code/session_016PVjjuVPte5XEJvaQx1E4t


Generated by Claude Code

…npm 10 can install the published graph

The live lane's backend install (`e2e/live/ci/start-backend.sh`) died on npm 10
(the runner's npm, 10.9.9 on Node 22.23.3) with `Cannot read properties of null
(reading 'edgesOut')`: the published @objectstack/cli graph reaches vitest only
as an optional peer, and the newest vitest 4 has no coherent
@vitest/browser-webdriverio peer set for npm 10's peer-set walk. A cache hit on
the old pins hid it until backend.env moved.

The rewritten showcase manifest now carries a root `overrides.vitest: ~4.0.0`,
objectstack's documented remedy for this crash. The value is the stamp's third
component, so a fixture prepared under another recipe is rebuilt rather than
reused. The comment states why it exists and when it goes. The CI/CD guide's
paragraph that said no transitive dependency is overridden now says which one is,
and what the fixture cache still cannot see.

Claude-Session: https://claude.ai/code/session_016PVjjuVPte5XEJvaQx1E4t
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Oct 16, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · ACCEPT · objectui#12674 (draft, head eed841f45, Fixes #12649) · domain:devx @ objectui seat 1 · session_016PVjjuVPte5XEJvaQx1E4t · 2026-10-16T13:19Z

Checked on GitHub and against the sources, not on the report's say-so.

  • Shape:
    • 2 files, +61 / −15: e2e/live/ci/start-backend.sh and content/docs/guide/ci-cd-pipeline.md. Both are inside the claim's surface (6215807690).
    • No .github/workflows/**, no packages/**, and backend.env is unchanged. The dev's check-governed-queue-guard --test reads NOT GOVERNED.
    • check-changeset-presence says no changeset is owed, so none was added.
    • The body opens Fixes #12649, then Clause-②: no.
  • The script, read by this seat on the diff:
    • prepare() writes pkg.overrides = { ...pkg.overrides, vitest: VITEST_OVERRIDE }, with VITEST_OVERRIDE='~4.0.0', into the rewritten showcase manifest. That is remedy (a), the one objectstack documents.
    • The stamp becomes "$OBJECTSTACK_REF $OBJECTSTACK_VERSION vitest@$VITEST_OVERRIDE". A fixture that the Actions cache restores from the old recipe no longer matches, so it is rebuilt cold and never reused. This is the "pin the cold install" ask.
    • The note at VITEST_OVERRIDE states the cause, the remedy's source, the removal condition (the published @objectstack/cli at OBJECTSTACK_VERSION installs on npm 10 with no override), and how to re-measure: a fresh dir and a fresh cache, because a prepared dir answers "up to date".
  • Measured by the dev, on the runner's npm (10.9.9, read from the failing job):
route install health / ready allowlist
baseline (main script) exit 1, edgesOut — —
(a) override (chosen) exit 0, 528 packages 200 / 200 5 passed
(b) npm 11.21.0, no override exit 0, 528 packages 200 / 200 5 passed
(c) --legacy-peer-deps exit 0, 506 packages 200 / 200 5 passed, but better-sqlite3 and 21 build deps are missing
  • The (a) and (b) lockfile trees are identical path for path, so the override changes nothing the backend runs.
  • On the PR itself: Live E2E (informational) (job 116990202266) succeeded, and it was a cold install. The restore missed, the start step took 71 s, "Save live backend fixture" ran, and the allowlist read "5 passed".
  • Out of scope, routed by this seat:
    • stop-backend.sh records the subshell's PID, not the server's, so a local stop leaves the server running. CI is unaffected (ephemeral runner). The seat files it as its own card.
    • The cache key hashes backend.env alone, so registry drift under unchanged pins stays hidden until the next bump. That is stated in the PR body and on the docs page. Closing it would need a governed live-e2e.yml edit, and the seat does not widen this PR for it.
  • Landing: objectui's required contexts are Lint, Type Check, Build & E2E, Build Docs, Changeset Declaration, Test and Governed Surface Queue Guard. Once every check on this head reads green, the seat readies the PR, arms auto-merge and confirms the merge on objectui origin/main.

Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation

Projects

None yet

2 participants