| name | webjs-scaffold-sync |
| description | Use this skill whenever a change affects what `webjs create` GENERATES (a new or changed gallery/showcase demo, a new template, a changed generated file like the layout/home/theme/schema, a new convention that belongs in generated apps, a new scaffold-shipped config/hook) OR when the user asks to sync the scaffold, "update all three templates", check the scaffold is consistent, or teach agents how to use webjs through the scaffold. The scaffold is webjs's PRIMARY teaching surface for AI agents, so a change to it must propagate in lockstep across the generators, the per-agent rule files, the scaffold tests, the framework docs that describe the scaffold, and the preview/example apps. This skill is the authoritative map of every scaffold surface plus the change-type to surface mapping, and the mandatory "generate + boot + check" verification, so no surface is silently skipped. Complements webjs-doc-sync (which owns framework API/behaviour docs); this skill owns what the scaffold emits. |
| when_to_use | Examples that should trigger this skill:
"add a new demo to the feature gallery"
"the gallery should also ship in the saas template"
"update all three scaffold templates with this"
"add a backend-features showcase to the api template"
"make sure the scaffold teaches agents how to do X"
"did we keep the scaffold rule files in sync?"
"the saas home / theme / schema changed, sync everything"
finishing any change to what `webjs create` generates
Do NOT trigger for: a framework API/behaviour change with no scaffold
impact (use webjs-doc-sync), or a pure-internal refactor of the CLI
that does not change generated output.
|
Keep every scaffold surface in sync with what webjs create generates
The scaffold is the primary way AI agents learn webjs: an agent reads the
generated gallery/showcase and its comments to learn the idioms, then builds the
real app by adapting them. So a change to what webjs create emits is a
first-class change with MANY surfaces, and the recurring failure is updating one
(usually a template file) while the per-agent rule files, the scaffold tests, the
framework docs, and the preview apps drift behind. This skill closes that gap.
It is the sibling of webjs-doc-sync. Division of labour:
- webjs-doc-sync owns the framework's API/behaviour docs (the root
AGENTS.md
API sections, agent-docs/*.md, docs-site topic pages, the marketing website).
- webjs-scaffold-sync (this skill) owns what the scaffold GENERATES and the
surfaces that DESCRIBE the scaffold.
They overlap on two surfaces (the scaffold's per-agent rule files, and the
template matrix in the framework docs/README). Whichever skill reaches that
surface must update it; when in doubt run both.
Enforcement is TWO tiers, deliberately mirroring how tests are enforced (a
commit-time floor plus an un-skippable CI gate):
-
Tier 1, the commit floor. .claude/hooks/require-scaffold-with-src.sh
BLOCKS a commit that stages framework-feature source (packages/(core|server|cli)/src)
with no scaffold surface (packages/cli/templates or packages/cli/lib) in the
same commit (escape hatch WEBJS_NO_SCAFFOLD_GATE=1). Like the test commit
gate, it only proves you touched a scaffold file; it cannot tell a real demo
from a doc bullet, which is exactly how #848 slipped (forbidden()/unauthorized()
staged doc bullets, shipped no gallery demo).
-
Tier 2, the CI coverage gate. test/scaffolds/gallery-coverage.test.js
reconciles the LIVE framework surface against the hand-curated
test/scaffolds/gallery-coverage.json manifest and FAILS when a new surface is
neither demoed nor exempted. It gates THREE surfaces: @webjsdev/core
exports (a { demo } pointing at a gallery file that references it),
@webjsdev/server exports ({ demoed: true }, verified by a generated app
importing it), and routing convention files (the stems the router parses,
DERIVED from packages/server/src/router.js so a new stem === '...' branch
auto-appears, each demonstrated by a file in a generated app). Every entry is
demo/demoed or { exempt } with a reason (internal: ... for plumbing,
deferred: ... for an agent-facing surface not yet demoed). It runs on every
npm test and in CI, so it cannot be skipped with a local --no-verify, the
analogue of "a test must exist AND pass": a new export or convention turns CI red
until it is classified. When you add or rename a core/server export, or add a
routing convention file the router parses, update the manifest (a demo, or an
honest exemption), the same reflex as writing a test.
THIS skill does the substantive per-surface judgment and the generate-boot-check
verification that neither tier can automate.
The complete scaffold surface map
Treat this as the universe. For any scaffold change, decide per surface whether
it applies, then update or consciously skip each.
- The generators (the code that writes the app):
packages/cli/lib/create.js (the main generator: layout, home page, the
theme block, db/schema, the full-stack gallery wiring, the per-template
gates like isApi / isFullStack / !isApi).
packages/cli/lib/api-gallery.js (the api backend-features showcase). Auth
is a full-stack GALLERY card now (templates/gallery/{app/features/auth, modules/auth}), pruned by gallery:clear, not a separate template.
- Any future
*-template.js / *-gallery.js split out for escaping sanity.
- The verbatim template files copied into every app:
packages/cli/templates/gallery/** (the UI feature gallery + example app,
shipped in full-stack AND saas).
packages/cli/templates/** (everything else copied per app: lib/utils/ui.ts,
public/, tsconfig.json, gitignore, .hooks/, the metadata/route stubs).
- The per-agent rule files (LOCKSTEP: all carry the SAME rules in each
agent's format, the #134/#136 divergence lesson). A convention/workflow change
for generated apps must land in ALL of them together:
packages/cli/templates/AGENTS.md
packages/cli/templates/CLAUDE.md
packages/cli/templates/CONVENTIONS.md
packages/cli/templates/.cursorrules
packages/cli/templates/.github/copilot-instructions.md
packages/cli/templates/.agents/rules/workflow.md
packages/cli/templates/.gemini/**, .opencode/**, .claude/** (whatever
per-agent rule files the scaffold currently ships; enumerate, do not assume)
- The scaffold tests:
test/scaffolds/*.test.js (e.g. scaffold-gallery,
scaffold-ui-integration). A new demo/template/generated-file assertion goes
here, including the counterfactual (a per-template exclusion test).
- The framework docs that DESCRIBE the scaffold (shared with doc-sync):
- Root
AGENTS.md "Scaffolding" section.
- The docs site:
docs/app/docs/getting-started/page.ts (+ backend-only,
ai-first, conventions where they describe generated structure).
README.md (the template matrix + the "scaffold is the tutorial" note).
- The CLI surface:
packages/cli/ --template validation + --help/usage
text when a template or flag is added or renamed.
- The preview / example / dogfood apps:
examples/blog/ and any in-repo
apps, plus the local preview apps the user tests, when a convention they
demonstrate changes.
Change-type to surface mapping
| Change | Surfaces that MUST be checked |
|---|
| New / changed gallery or showcase demo | the template file(s) or generator strings for the demo + the home-page features/index array + the scaffold AGENTS.md gallery list + test/scaffolds/* FEATURES/assertions + generate + boot the affected template |
| New / removed template | the create.js template branch (+ a *-template.js if large) + the "only N templates exist" list in EVERY per-agent rule file + the framework AGENTS.md/getting-started/README template matrix + the CLI --template validation + --help + test/scaffolds/* |
New control-flow throw or routing boundary file (notFound / redirect / forbidden / unauthorized and their not-found / forbidden / unauthorized / error / loading / global-error / global-not-found boundary files) | a runnable gallery demo that exercises it (a route that throws it plus the nearest boundary file), NOT just an app-tree bullet in the rule files + the home-page features array + test/scaffolds/* FEATURES/boundary-file asserts + generate + boot + hit the route. A doc bullet in AGENTS.md / CONVENTIONS.md is necessary but NOT sufficient: the gallery is the primary teaching surface, so an undemoed thrower is invisible to a scaffolding agent (the #848 gap). Carve-out: a root-only boundary (global-error / global-not-found) cannot mount under app/features/ without clashing with the generated app root, so teach those in the demo's PROSE rather than as a live route. |
New / renamed public @webjsdev/core or @webjsdev/server export, or a new routing convention file the router parses | test/scaffolds/gallery-coverage.json MUST classify it (a { demo } / { demoed: true }, or { exempt } with an internal: / deferred: reason) or the tier-2 CI gate (gallery-coverage.test.js) FAILS. Prefer a real demo; deferred: is a conscious, reviewer-visible exemption tracked for later. This is the coverage-gate teeth described above. |
| New convention/rule for generated apps | ALL per-agent rule files in lockstep (surface 3) + repo CONVENTIONS.md if the repo demonstrates it + agent-docs only if it also changes a framework API |
| Changed generated file (layout, theme, home, schema, middleware) | the generator (create.js/*-template.js) + any scaffold test asserting it + any doc/preview describing it + regenerate + boot |
New scaffold-shipped config/hook (.hooks/, webjs.* in the generated package.json, a check rule) | templates/** + webjs doctor/check that reads it + the per-agent rule files if agents must know it |
| Which templates ship the gallery (scoping) | the copyGallery / gallery gate in create.js + the "full-stack only / full-stack and saas" wording in the per-agent rule files AND the framework docs (grep the old scoping phrase everywhere) |
| Pure-internal CLI refactor (no change to generated output) | NONE. Record that no scaffold surface applies. |
Per-change sync procedure
- Identify the change's IDENTIFYING TOKENS: the demo route (
app/features/<x>,
app/api/features/<x>), the template name, the generated file path, the
convention phrase, or the scoping phrase (e.g. "full-stack only").
- Grep those tokens across every scaffold surface:
git grep -n -iE '<token1>|<token2>' -- \
'packages/cli/lib/**' 'packages/cli/templates/**' 'test/scaffolds/**' \
AGENTS.md README.md 'docs/app/docs/**'
- Update every surface the mapping says applies. For a SCOPING or wording change
(e.g. "gallery is full-stack only" becoming "full-stack and saas"), the grep
MUST surface and fix every copy, in the rule files AND the framework docs.
- VERIFY BY GENERATING (mandatory, non-negotiable). The generators emit
strings, so a template-literal / escaping / interpolation bug is invisible in
the generator's own syntax and only appears in the GENERATED app. For each
affected template, generate an app and prove it:
node -e "import('packages/cli/lib/create.js').then(m => m.scaffoldApp('probe', '/tmp/x', { template: 'full-stack', install: false }))"
A scaffold change is NOT done until a freshly generated app of each affected
template BOOTS, serves the new/changed route, passes webjs check (only the
intended no-scaffold-placeholder markers), and webjs typecheck is clean.
- Run the scaffold tests (
node --test 'test/scaffolds/*.test.js') and add/adjust
assertions (a new demo in the FEATURES list, a per-template inclusion/exclusion
test, the counterfactual).
- Respect the prose-punctuation invariant (#11) in every comment and doc, and
keep each demo densely commented (a header stating the webjs concept + the
why, inline comments on the non-obvious idiom, a
webjs-scaffold-placeholder
marker). The scaffold teaches by its comments; a thin demo is a bug.
Audit-mode procedure (sweep the scaffold for drift)
- List the shipped scaffold changes (new demos, new template, scoping changes,
changed generated files).
- For each, pull its tokens and run the surface grep above.
- A surface is a GAP when the mapping says it applies but the token is absent or
describes stale behaviour there (a classic gap: a demo added to the full-stack
home but missing from the saas home, or a scoping phrase updated in one rule
file but not the other five).
- For a bulk audit, file a grounded follow-up via webjs-file-issue per gap
(title
scaffold: <surface> missing <thing>); for a single in-flight change,
just fix all surfaces on the same PR.
What this skill does NOT do
- It does not regenerate
llms.txt / llms-full.txt (live-generated) or the
website changelog (auto from PR titles).
- It does not own framework API/behaviour docs (that is
webjs-doc-sync); when a
change touches both a framework API and the scaffold, run both skills.
- It does not decide whether a CLI change alters generated output; that judgement
is step 1, and a pure-internal refactor correctly updates no scaffold surface.