domain:* / type / priority are triage's.
Found while building a real app on the published scaffold (objectstack-ai/ats) — the duplication reached that repository's initial commit before anyone noticed it.
Measured (origin/main 3e270d4e)
packages/create-objectstack/src/index.ts:533:
execSync('npx -y skills add objectstack-ai/objectstack/skills --all', { … })
--all is documented by the skills CLI as shorthand for --skill '*' --agent '*' -y. The --agent '*' half fans the catalog out to every agent runtime the CLI knows, and src/index.ts:524 already records this as an assumption ("assumed: skills add --all fans the catalog out to every agent").
packages/create-objectstack/src/created-summary.ts:17 measured the result on published create-objectstack@17.1.0:
.agents/ 49 agent/ 49 .claude/ 11
.claude/skills/* are symlinks into .agents/skills/; agent/skills/ is a full second copy for a different runtime.
packages/create-objectstack/src/templates/blank/_gitignore — complete contents:
node_modules
dist
.objectstack/
*.log
.DS_Store
.env
.env.*
Neither .agents/ nor agent/ is excluded.
Reproduced on a real project: the initial commit of objectstack-ai/ats carried 133 files, of which 46 were .agents/skills/**, 46 a byte-identical agent/skills/**, and 11 symlinks — about 804 KB of duplication. Removing the duplicate copy and reinstalling with --skill '*' --agent claude-code -y took the repository to 76 files and produced a clean 13,361-line deletion; skills-lock.json was unchanged, because it records source and hash, not paths.
What is and is not the defect
Installing for several runtimes is defensible — a project may be opened in Claude Code, Cursor and Codex. The defect is that all copies are committed. Three byte-identical trees in git review as noise forever: they bloat the first diff anyone reads, they re-appear in every skills add update as a three-way churn, and nothing tells the author which copy is canonical. The scaffolder's own summary comment already calls the phase-3 output "two ~968 KB trees of agent instructions … landed on the user's disk unnamed".
The ask
Pick one canonical committed location and keep the rest out of git. Either route is acceptable; state the choice in the changeset.
- Route A (minimal, recommended). Keep
--all, and add to the template _gitignore:
.claude/skills/ symlinks resolve into .agents/, so if .agents/ is ignored the symlinks dangle for a fresh cloner — therefore Route A must also make .claude/skills/ the committed real copy, or ignore all three and treat skills-lock.json as the source of truth with a one-line README instruction to run skills add. Decide and encode it; do not leave dangling symlinks in a fresh clone.
- Route B. Install for one agent by default (
--skill '*' --agent claude-code -y), print the one-line command for other runtimes in the closing summary. Smallest checkout; costs the multi-runtime default.
Whichever route: a fresh create-objectstack run followed by git add -A && git status must show the skills bundle once, and a fresh clone of that commit must have working skill files (no dangling symlink).
Acceptance
- A scaffolded project's first
git add -A stages the bundle exactly once.
- Cloning that commit into a clean directory yields readable
SKILL.md files at whatever path the project committed.
create-objectstack's scaffold test asserts the staged-once property (count the staged SKILL.md paths).
- Changeset:
create-objectstack patch for Route A, minor for Route B (behaviour change in what gets installed).
Not in this card
The missing CI workflow (separate card). The accuracy of the printed "Created files" summary — created-summary.ts already documents that problem and its reasoning; this card only changes what lands in git.
domain:*/ type / priority are triage's.Found while building a real app on the published scaffold (
objectstack-ai/ats) — the duplication reached that repository's initial commit before anyone noticed it.Measured (
origin/main3e270d4e)packages/create-objectstack/src/index.ts:533:--allis documented by the skills CLI as shorthand for--skill '*' --agent '*' -y. The--agent '*'half fans the catalog out to every agent runtime the CLI knows, andsrc/index.ts:524already records this as an assumption ("assumed:skills add --allfans the catalog out to every agent").packages/create-objectstack/src/created-summary.ts:17measured the result on publishedcreate-objectstack@17.1.0:.claude/skills/*are symlinks into.agents/skills/;agent/skills/is a full second copy for a different runtime.packages/create-objectstack/src/templates/blank/_gitignore— complete contents:.agents/noragent/is excluded.Reproduced on a real project: the initial commit of
objectstack-ai/atscarried 133 files, of which 46 were.agents/skills/**, 46 a byte-identicalagent/skills/**, and 11 symlinks — about 804 KB of duplication. Removing the duplicate copy and reinstalling with--skill '*' --agent claude-code -ytook the repository to 76 files and produced a clean 13,361-line deletion;skills-lock.jsonwas unchanged, because it records source and hash, not paths.What is and is not the defect
Installing for several runtimes is defensible — a project may be opened in Claude Code, Cursor and Codex. The defect is that all copies are committed. Three byte-identical trees in git review as noise forever: they bloat the first diff anyone reads, they re-appear in every
skills addupdate as a three-way churn, and nothing tells the author which copy is canonical. The scaffolder's own summary comment already calls the phase-3 output "two ~968 KB trees of agent instructions … landed on the user's disk unnamed".The ask
Pick one canonical committed location and keep the rest out of git. Either route is acceptable; state the choice in the changeset.
--all, and add to the template_gitignore:.claude/skills/symlinks resolve into.agents/, so if.agents/is ignored the symlinks dangle for a fresh cloner — therefore Route A must also make.claude/skills/the committed real copy, or ignore all three and treatskills-lock.jsonas the source of truth with a one-line README instruction to runskills add. Decide and encode it; do not leave dangling symlinks in a fresh clone.--skill '*' --agent claude-code -y), print the one-line command for other runtimes in the closing summary. Smallest checkout; costs the multi-runtime default.Whichever route: a fresh
create-objectstackrun followed bygit add -A && git statusmust show the skills bundle once, and a fresh clone of that commit must have working skill files (no dangling symlink).Acceptance
git add -Astages the bundle exactly once.SKILL.mdfiles at whatever path the project committed.create-objectstack's scaffold test asserts the staged-once property (count the stagedSKILL.mdpaths).create-objectstackpatch for Route A, minor for Route B (behaviour change in what gets installed).Not in this card
The missing CI workflow (separate card). The accuracy of the printed "Created files" summary —
created-summary.tsalready documents that problem and its reasoning; this card only changes what lands in git.