test(scaffold-e2e): cover the pinned Dockerfile shape a real scaffold ships - #9142
Merged
os-project-manager merged 1 commit intoAug 16, 2026
Merged
Conversation
… ships The docker leg scaffolds with --skip-install and installs the project in a separate step, so the scaffolder's own post-install pin (#9017) never runs and the shape built and booted is the unpinned one, while every real npx create-objectstack user ships the pinned one. Adds a second, throwaway project scaffolded WITHOUT --skip-install that asserts the emitted Dockerfile names the @objectstack/cli version the project resolved. No docker build: the two shapes differ by exactly one FROM tag. Also narrows the docker leg's comment, which claimed to exercise the exact path a real user ships — true only of the --skip-install variant. Part of #9117 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011RB4waLuNbdruCo6X9oobm
os-project-manager
marked this pull request as ready for review
August 16, 2026 14:39
os-project-manager
deleted the
claude/issue-9117-scaffold-e2e-pinned-shape
branch
August 16, 2026 14:57
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #9117
The docker leg of
scaffold-e2escaffolds with--skip-installand installs the generated project in a separate step, so the scaffolder's own post-install work never runs — and the Dockerfile runtime-image pin landed by PR #9115 (for #9017) lives exactly there. The shape the job built and booted was therefore the unpinned one (FROM ghcr.io/objectstack-ai/objectstack:latest), while every realnpx create-objectstackuser ships the pinned one. The pin silently ceasing to happen was invisible to this job.This lands the card's first shape: a second, throwaway project scaffolded the way a real user does — no
--skip-install— asserting only that the emitted Dockerfile names the@objectstack/cliversion the project actually resolved. No docker build: the two shapes differ by exactly oneFROMtag, and the existing leg already proves the image builds and boots. Option 2 (relocating the install into the scaffolder) was not taken and no impracticality is claimed — see the residual at the bottom.What only this leg can catch
runtime-image.test.tscovers the rewrite on fabricated input. What needs an end-to-end run is the pin never running: a scaffolder that stops calling it, or an install path that no longer leaves a resolvable CLI behind. Both are proven discriminating below.The three branches, all measured
The step distinguishes two failure classes rather than skipping on any install trouble. When the scaffolder's install leaves no CLI behind, the registry is asked whether the emitted range was satisfiable at all:
FROMtag == resolved CLIVerification — union run at
d1ae6ce7e, the final commitCI cannot be run locally, so every run below executes the literal
run:block extracted out ofscaffold-e2e.ymlby a small YAML harness, withRUNNER_TEMP/GITHUB_WORKSPACEset — the bytes CI will run, not a re-typed copy. The workflow invokesbin/create-objectstack.js, which resolvesdist/, so every ablation was rebuilt and its arrival in the artifact proven withscripts/ablation-dist-preflight.mjsbefore the run's colour was allowed to mean anything.Green, unmodified scaffolder:
Reverse verification (direction predicted before running: red in the first two, exit 0 in the third, red in the fourth):
pinRuntimeImagereturns success without writingindex.tswiring99.0.0::warning::— the window branchdetectPackageManagerreturns a broken pmThe red message names the pin, which is what the card asked for:
Worth recording: under ablation 1 the scaffolder still printed its own
✓ Dockerfile runtime image pinned to 17.0.0while the file was untouched — the exact "silently not happening" mode, and the reason a leg that reads the emitted file is the only thing that catches it.Restore legs:
index.ts's call-site marker was proven present again after ablation 2, noABLATION_9117string survives insrc/ordist/, the working tree is clean atd1ae6ce7e, andpnpm --filter create-objectstack testis 58 passed (4 files) from the restored tree — this PR changes no package source.Gates re-derived against the actual changed path (
.github/workflows/scaffold-e2e.yml), which added nothing beyond the dispatched list — all green atd1ae6ce7e:check:node-version,check:required-contexts,check:shard-attestation,check:workflow-status-functions,check:nul-bytes,node scripts/check-shard-attestation.mjs. Control-byte self-scan of the edited file: clean.The comment that had gone stale
The docker leg claimed to exercise "the exact Docker path a real
npm create objectstackuser ships" — true only of the--skip-installvariant since PR #9115. It now states its scope precisely and points at the new step for the pinned half. The file header gained the same split.Residual, stated rather than hidden
In the unpublished-version window the new leg warns and skips instead of asserting. That is not a design preference: the fallback which rewrites unresolvable
@objectstack/*ranges tolatestlives in the workflow's install step, and relocating it into the scaffolder is the separate design the card records. The degradation is evidenced (the registry is asked) rather than blanket, so a broken install path still fails loudly. That fork needs a card of its own; out of scope here, and no other card is addressed by this PR.Changeset
None —
.github/workflows/**only, releasing nothing user-visible.skip-changesetapplies.Generated by Claude Code