Skip to content

Commit 026f941

Browse files
committed
wip: scaffold e2e asserts the package catalog; changeset
Claude-Session: https://claude.ai/code/session_01VoSxBQujKLZKPwK2u5ehQ6 Co-authored-by: Claude <noreply@anthropic.com>
1 parent c4a62fa commit 026f941

2 files changed

Lines changed: 78 additions & 8 deletions

File tree

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
"create-objectstack": minor
3+
---
4+
5+
feat(create-objectstack): a scaffolded project installs its AI skills from the `@objectstack/skills` package at its own `@objectstack/spec` version, never from the repository's `main` branch
6+
7+
Clause-②: no
8+
9+
- **The project depends on the catalog.** The blank template's `devDependencies` gain `@objectstack/skills`, at the same `^<version>` as every other `@objectstack/*` dependency, so a new project's dependency set changes: the skills are the ones written for the `@objectstack/spec` it installs.
10+
- **The install reads `node_modules`.** After `<pm> install`, the scaffolder runs `npx -y skills@1.7.2 experimental_sync --agent claude-code --copy -y`, which copies the installed package's catalog into `.claude/skills/` as real files, once, beside a `skills-lock.json` that names the package as every skill's source. The GitHub `npx skills add objectstack-ai/objectstack/skills …` command is gone from the run, its summary and the `AGENTS.md` and `README.md` it writes into the project.
11+
- **The summary says what happened.** It names the package version installed and prints the same command for re-installing after an upgrade of the `@objectstack/*` packages, and for any other agent runtime. When the install leaves no catalog to read, the run says so instead of reporting the skills installed.
12+
- **The flags keep their meaning.** `--skip-skills` skips the skills step. `--skip-install` installs no skills, because nothing is in `node_modules` yet; the summary names `<pm> install` and then the sync command.
13+
- **To re-install skills in a project created earlier:** add `@objectstack/skills` to `devDependencies` at your `@objectstack/spec` range, run your package manager's install, then `npx skills@1.7.2 experimental_sync --agent claude-code --copy -y` (name your own runtime after `--agent`).

‎.github/workflows/scaffold-e2e.yml‎

Lines changed: 65 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -235,11 +235,14 @@ jobs:
235235
236236
- name: 'Skills boundary: only the curated skills/ catalog installs'
237237
# 15.1 third-party eval: the repo-internal dogfood-verification skill
238-
# (.claude/skills/) leaked into scaffolded projects. Two leak paths,
239-
# two assertions against THIS checkout:
240-
# 1. The scaffolder/docs command is `skills add …/objectstack/skills
241-
# --all` — the /skills subpath is the hard boundary, because the
242-
# skills CLI's --all implies --skill '*' which INCLUDES
238+
# (.claude/skills/) leaked into scaffolded projects. The scaffolder no
239+
# longer reads GitHub — it syncs the installed @objectstack/skills
240+
# package, asserted by the step after the pinned shape below — so
241+
# these two assertions now guard the GitHub path the docs keep as the
242+
# `next` channel, against THIS checkout:
243+
# 1. The documented command is `skills add …/objectstack/skills …` —
244+
# the /skills subpath is the hard boundary, because the skills
245+
# CLI's --all implies --skill '*' which INCLUDES
243246
# metadata.internal skills. Install from the subpath and assert
244247
# set-equality with the curated catalog.
245248
# 2. Interactive `skills add …/framework` (no --all) relies on
@@ -293,8 +296,10 @@ jobs:
293296
# never RUNNING — a scaffolder that stops calling it, or an install path
294297
# that no longer leaves a resolvable CLI behind.
295298
#
296-
# `--skip-skills` is kept: skills install after the pin, so dropping it
297-
# would only add a network fetch, never coverage.
299+
# No `--skip-skills` either: the scaffolder's skills step reads the
300+
# catalog out of the node_modules this same install wrote, so this is
301+
# the one project in the job whose skills a real user would get. The
302+
# step after this one asserts them.
298303
#
299304
# The unpublished-version window is the one degradation, and it is
300305
# evidenced rather than blanket. The install step above carries the
@@ -337,7 +342,7 @@ jobs:
337342
# still catches a genuinely broken scaffolder install.
338343
run: |
339344
cd "$RUNNER_TEMP"
340-
node "$GITHUB_WORKSPACE/packages/create-objectstack/bin/create-objectstack.js" pinned-probe --skip-skills
345+
node "$GITHUB_WORKSPACE/packages/create-objectstack/bin/create-objectstack.js" pinned-probe
341346
cd pinned-probe
342347
343348
if [ ! -f node_modules/@objectstack/cli/package.json ]; then
@@ -377,6 +382,58 @@ jobs:
377382
fi
378383
echo "pinned as expected: FROM tag == resolved CLI ($RESOLVED)"
379384
385+
- name: 'Skills: the scaffolder installed exactly the @objectstack/skills catalog'
386+
# The pinned-shape project above was scaffolded the way a real user
387+
# does it, skills included. The scaffolder syncs the catalog out of the
388+
# @objectstack/skills package its own install put in node_modules — at
389+
# the range it gives every @objectstack/* dependency, so the version of
390+
# @objectstack/spec — into .claude/skills/ as real files (the measured
391+
# shapes live in packages/create-objectstack/src/skills-install.ts).
392+
# So the catalog this step compares against is the package's, read off
393+
# the registry-installed node_modules, never this checkout's skills/:
394+
# the same skill names, the same count, copied rather than linked.
395+
#
396+
# The unpublished-version window degrades the way the pinned-shape step
397+
# does, and only that window: with nothing installed at all, that step
398+
# has already classified the registry answer and failed on any other
399+
# cause, so this step asserts the degraded shape — no skills from
400+
# anywhere — instead of nothing. An install that resolved the CLI but
401+
# no @objectstack/skills is a template that lost its dependency: red.
402+
run: |
403+
cd "$RUNNER_TEMP/pinned-probe"
404+
PKG=node_modules/@objectstack/skills
405+
if [ ! -f "$PKG/package.json" ]; then
406+
if [ -f node_modules/@objectstack/cli/package.json ]; then
407+
echo "::error::the scaffolder's install resolved @objectstack/cli but no @objectstack/skills, so this project got no skills catalog at its own version — the template no longer depends on the package"
408+
exit 1
409+
fi
410+
if [ -e .claude/skills ]; then
411+
echo "::error::nothing was installed, yet .claude/skills exists — the scaffolder installed skills from a source other than the @objectstack/skills package"
412+
exit 1
413+
fi
414+
echo "::warning::no @objectstack/skills is installed — the unpublished-version window the pinned-shape step classified. Catalog EQUALITY is SKIPPED for this run; the degraded shape (no skills from any source) was asserted."
415+
exit 0
416+
fi
417+
418+
skills_in() { for d in "$1"/*/; do [ -f "${d}SKILL.md" ] && [ ! -L "${d%/}" ] && basename "$d"; done | sort; }
419+
expected=$(skills_in "$PKG/dist/skills")
420+
installed=$(skills_in .claude/skills)
421+
echo "@objectstack/skills@$(node -p "require('./$PKG/package.json').version") catalog ($(echo "$expected" | grep -c .) skills):"; echo "$expected"
422+
echo "installed in .claude/skills ($(echo "$installed" | grep -c .) skills):"; echo "$installed"
423+
if [ -z "$expected" ]; then
424+
echo "::error::the installed @objectstack/skills holds no dist/skills/<skill>/SKILL.md — the layout the scaffolder reads has moved"
425+
exit 1
426+
fi
427+
if [ "$installed" != "$expected" ]; then
428+
echo "::error::the skills the scaffolder installed are not the installed package's catalog (names and counts above)"
429+
exit 1
430+
fi
431+
if [ -e .agents ] || [ -n "$(find .claude -type l)" ]; then
432+
echo "::error::the scaffolder linked the catalog instead of copying it — a clone of this project would hold dangling links into node_modules"
433+
exit 1
434+
fi
435+
echo "skills as expected: .claude/skills == the installed @objectstack/skills catalog"
436+
380437
- name: Build official runtime image from this checkout
381438
# The scaffolded app's Dockerfile builds FROM
382439
# ghcr.io/objectstack-ai/objectstack. Build that base HERE from

0 commit comments

Comments
 (0)