Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions .claude/hooks/require-scaffold-with-src.sh
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,11 @@ src_touched=$(printf '%s\n' "$staged" | grep -E '^packages/(core|server|cli)/src
if [ -z "$src_touched" ]; then exit 0; fi

# Any scaffold teaching surface staged in the same commit? Either the scaffold
# templates/generators (the gallery, runnable but disposable via gallery:clear)
# OR the agent skill at .agents/skills/webjs/ (the durable teacher bundled into
# every scaffold, the only surface that survives gallery:clear).
scaffold_staged=$(printf '%s\n' "$staged" | grep -E '^packages/cli/(templates|lib)/|^\.agents/skills/webjs/' || true)
# templates/generators, or the canonical repo-root gallery/ app (the gallery is
# runnable but disposable via gallery:clear, and prepack bundles it into the CLI
# tarball), OR the agent skill at .agents/skills/webjs/ (the durable teacher
# bundled into every scaffold, the only surface that survives gallery:clear).
scaffold_staged=$(printf '%s\n' "$staged" | grep -E '^packages/cli/(templates|lib)/|^gallery/|^\.agents/skills/webjs/' || true)
if [ -n "$scaffold_staged" ]; then exit 0; fi

cat >&2 <<'EOF'
Expand All @@ -100,7 +101,8 @@ capability, a config key, a CLI behaviour), invoke the `webjs-scaffold-sync`
skill, then `git add` the teaching surface(s) and commit again:
`.agents/skills/webjs/` the DURABLE teacher (SKILL.md + references/),
survives `gallery:clear` so it MUST teach the feature
packages/cli/templates/gallery/ a UI feature-gallery demo (runnable, disposable)
gallery/ a UI feature-gallery demo (runnable, disposable).
The canonical root app; prepack bundles it into the CLI
packages/cli/lib/api-gallery.js an api backend-showcase endpoint
packages/cli/lib/{create,saas-template}.js the generators (home, theme, schema, wiring)
packages/cli/templates/ (AGENTS/CONVENTIONS/.cursorrules/...) scaffold rules in lockstep
Expand Down
5 changes: 3 additions & 2 deletions .claude/skills/webjs-doc-sync/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,9 @@ applies, then update or consciously skip each.
change to how apps are AUTHORED lands in the skill. When the change is to what
`webjs create` GENERATES (a gallery/showcase demo, a template, the generated
layout/home/theme/schema, a scaffold convention), this surface has more parts
(the `packages/cli/lib/*` generators, the `packages/cli/templates/gallery/**`
demos, the scaffold tests, the framework template-matrix docs, the preview
(the `packages/cli/lib/*` generators, the repo-root `gallery/**` app whose
demos prepack bundles into the CLI, the scaffold tests, the framework
template-matrix docs, the preview
apps) and a mandatory `generate + boot + webjs check` step: use the dedicated
**`webjs-scaffold-sync`** skill for those, and treat this doc-sync entry as the
docs-only slice. The CLI help text in `packages/cli/` is part of this surface
Expand Down
16 changes: 12 additions & 4 deletions .claude/skills/webjs-scaffold-sync/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,13 @@ surfaces that must stay in sync with the framework:
so the skill is the ONLY teaching surface that SURVIVES, and after a clear it
is the agent's sole reference. A feature the skill does not teach is lost the
moment an agent clears the gallery.
2. **The gallery** (`packages/cli/templates/gallery/**`): runnable, densely
2. **The gallery** (the repo-root `gallery/**` app): runnable, densely
commented demos an agent learns the idioms from by reading and running, then
adapts. It is DISPOSABLE (removed by `gallery:clear` before real work begins).
It lives ONCE, at the repo root, as a live workspace app you can boot and
test (`npm run dev:gallery`, `npm test --workspace=@webjsdev/gallery`).
`packages/cli/prepack` bundles it into `packages/cli/templates/gallery/` for
the tarball and `postpack` deletes that copy, so never edit or commit there.

So a change to a WebJs FEATURE usually needs BOTH: the skill updated (the durable
pattern) AND a gallery demo (the runnable illustration). A change to what
Expand Down Expand Up @@ -112,7 +116,7 @@ it applies, then update or consciously skip each.
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,
is a full-stack GALLERY card now (`gallery/{app/features/auth,
modules/auth}`), pruned by `gallery:clear`, not a separate template.
- **The `gallery:clear` reset scripts (BOTH templates, MANDATORY parity).**
`packages/cli/templates/scripts/clear-gallery.mjs` (full-stack) strips the
Expand All @@ -131,8 +135,12 @@ it applies, then update or consciously skip each.
them after any gallery change.
- Any future `*-template.js` / `*-gallery.js` split out for escaping sanity.
3. **The verbatim template files** copied into every app:
- `packages/cli/templates/gallery/**` (the UI feature gallery + example app,
shipped in full-stack AND saas).
- The repo-root `gallery/**` app (the UI feature gallery + example app). Its
own shell (`app/layout.ts`, `app/page.ts`, `components/theme-toggle.ts`,
`lib/utils/cn.ts`) is NOT payload: the generator writes those itself, so
both the copy and the prepack bundle filter them via
`packages/cli/lib/gallery-shell-files.js`. Add a file there if the gallery
ever needs another app-only shell file.
- `packages/cli/templates/**` (everything else copied per app: `lib/utils/ui.ts`,
`public/`, `tsconfig.json`, `gitignore`, `.hooks/`, the metadata/route stubs).
4. **The per-agent rule files** (LOCKSTEP: all carry the SAME rules in each
Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/webjs-start-work/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ Doc drift is the #1 way a framework rots. Documentation MUST stay in sync with c
- `CLAUDE.md` (only if a Claude Code rule is specifically added; framework conventions go in AGENTS.md).
- `.github/*.md` (issue templates, PR templates, contributing) when a workflow rule shifts.
3. **User-facing docs site** under `website/app/docs/<topic>/page.ts` (these are `.ts` files, not markdown, so they're excluded by the markdown query but they're the canonical user-facing reference). If the change is visible to a user reading the docs site, update the matching topic page. Add a new page if the surface is new and there's no obvious home.
4. **Scaffold templates** under `packages/cli/templates/` and the generators `packages/cli/lib/{create,api-gallery}.js`. Update if the change affects what `webjs create` generates. The scaffold ships a gallery index home + layout + db wiring, a densely-commented feature gallery (`packages/cli/templates/gallery/**`, demos under `app/features/` plus `app/examples/todo`) and the api showcase (`api-gallery.js`), plus one cross-agent skill at `.agents/skills/webjs/` (SKILL.md + references) that the agent grows in place; there are no per-agent rule files. A feature change that agents should know about lands in the skill; a generated-code change lands in the generators, verified with `generate + boot + webjs check`.
4. **Scaffold templates** under `packages/cli/templates/` and the generators `packages/cli/lib/{create,api-gallery}.js`. Update if the change affects what `webjs create` generates. The scaffold ships a gallery index home + layout + db wiring, a densely-commented feature gallery (the repo-root `gallery/**` app, demos under `app/features/` plus `app/examples/todo`, bundled into the CLI at prepack) and the api showcase (`api-gallery.js`), plus one cross-agent skill at `.agents/skills/webjs/` (SKILL.md + references) that the agent grows in place; there are no per-agent rule files. A feature change that agents should know about lands in the skill; a generated-code change lands in the generators, verified with `generate + boot + webjs check`.
5. **The MCP server** (the standalone `@webjsdev/mcp` package, `packages/mcp/src/{mcp,mcp-docs,mcp-source}.js`, extracted from the CLI in #415; `webjs mcp` and `npx @webjsdev/mcp` both run it). The MCP is how AI agents learn and introspect webjs, so it must stay in lockstep with the surfaces it exposes. Update it whenever the change touches what it serves:
- **Introspection tools** (`list_routes` / `list_actions` / `list_components` / `list_elision` / `check`): if you change the route table shape, the action/RPC-hash scheme, component registration, or a `webjs check` rule, update the matching tool projection so the MCP reports reality.
- **Knowledge layer** (resources + `init` + `docs` + prompts): the resources are the skill at `.agents/skills/webjs/` (SKILL.md + references/) + `AGENTS.md`, so a docs change is picked up automatically (it is bundled at `prepack`). But if you add or rename a skill reference file, ADD A NEW INVARIANT, change the execution model, or add an authoring concept an agent should know, also: (a) confirm the `init` primer still pulls the right `AGENTS.md` sections (it sources the Execution-model + Invariants headings, so a heading rename breaks it), and (b) add a guided-workflow PROMPT for any new common recipe (a new page/route/action/component-shaped task). New recipes without a prompt are a silent gap.
Expand Down
28 changes: 22 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ jobs:
cache: npm
- run: npm ci
# Dogfood the framework's own correctness checks on the in-repo apps.
- name: webjs check (blog, website)
- name: webjs check (blog, gallery, website)
run: |
for app in examples/blog website; do
for app in examples/blog gallery website; do
echo "::group::webjs check $app"
( cd "$app" && node "$GITHUB_WORKSPACE/packages/cli/bin/webjs.js" check )
echo "::endgroup::"
Expand All @@ -48,9 +48,9 @@ jobs:
# and cannot red this job. Deliberately NOT --strict:
# the git-hook, env-drift, vendor-pin, and framework-resolve checks are
# environment-shaped and would fail a perfectly healthy runner.
- name: webjs doctor (blog, website)
- name: webjs doctor (blog, gallery, website)
run: |
for app in examples/blog website; do
for app in examples/blog gallery website; do
echo "::group::webjs doctor $app"
( cd "$app" && node "$GITHUB_WORKSPACE/packages/cli/bin/webjs.js" doctor )
echo "::endgroup::"
Expand Down Expand Up @@ -514,7 +514,7 @@ jobs:
- run: npm run build:dist --workspace=@webjsdev/core

apps:
name: In-repo app tests (website + blog)
name: In-repo app tests (website + blog + gallery)
runs-on: ubuntu-latest
# The framework jobs above cover packages/* and the root cross-package
# suite. This job runs each IN-REPO app's OWN test suite (its `webjs test`
Expand All @@ -535,14 +535,22 @@ jobs:
node-version: '24'
cache: npm
- run: npm ci
- name: Install Playwright Chromium (for the website browser tests)
- name: Install Playwright Chromium (for the website + gallery browser tests)
run: npx playwright install --with-deps chromium
- name: Prepare the blog example database
working-directory: examples/blog
run: |
cp .env.example .env
npm run db:migrate
npm run db:seed
# The gallery's auth card queries a real users table, and its test skips
# itself (rather than failing) when the table is missing, so an unmigrated
# database would turn the whole card green vacuously.
- name: Prepare the gallery database
working-directory: gallery
run: |
cp .env.example .env
npm run db:migrate
# Nothing in the pipeline typechecked the website before, so a type
# break landed on main and every branch cut from it inherited a red
# tsc (#1260). It runs before the tests so a type break names itself
Expand All @@ -556,10 +564,18 @@ jobs:
run: npm run typecheck --workspace=@webjsdev/website
- name: blog typecheck
run: npm run typecheck --workspace=@webjsdev/example-blog
- name: gallery typecheck
run: npm run typecheck --workspace=@webjsdev/gallery
- name: website tests (node + browser)
run: npm test --workspace=@webjsdev/website
- name: blog tests (node)
run: npm test --workspace=@webjsdev/example-blog
# The gallery is the canonical source of everything `webjs create` ships,
# so its suite is the one that catches a demo breaking against a framework
# change. Before #1370 those files sat un-executed under templates/, which
# is exactly the rot this job now closes. `webjs test` runs node + browser.
- name: gallery tests (node + browser)
run: npm test --workspace=@webjsdev/gallery
# Boot the website on Node and assert it serves real routes with no
# broken modulepreload, covering its /docs (#1098) and /ui (#1099)
# routes. Runs the app's `webjs.start.before` presteps first (the ui
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,11 @@ packages/mcp/resources/
# scripts/sync-scaffold-skill.mjs). Never commit this bundle.
packages/cli/templates/.agents/skills/webjs/

# Generated at prepack from the canonical repo-root gallery/ app (see
# scripts/sync-scaffold-gallery.mjs). Never commit this bundle: the gallery
# lives ONCE at gallery/, and a committed copy here would silently drift.
packages/cli/templates/gallery/

# A machine-local symlink into ~/.local/share (an omarchy skill). Kept on disk
# so the local setup keeps resolving it, untracked so it does not dangle in
# every other clone. See test/repo-health/no-committed-symlinks.test.mjs.
Expand Down
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,8 @@ Every code change MUST include, automatically:

1. **Tests, every applicable layer (not just unit).** Ship the tests that prove the change across EVERY layer it touches: **unit** (`packages/*/test/**`, `test/**`, including the counterfactual that fails when reverted), **browser** (`*/test/**/browser/*` via `npm run test:browser`, for hydration / DOM / slots / client router / custom-element upgrade), **e2e** (`test/e2e/*.test.mjs` via `WEBJS_E2E=1`, including network probes / navigation / streaming), and **smoke** (`test/examples/*/smoke/*`). A unit test is NECESSARY BUT NOT SUFFICIENT for any client-router / component / browser-facing change (the headline behaviour is a browser/e2e assertion). **Bun parity is part of the task, not an afterthought:** WebJs runs on Node 24+ AND Bun (#508), so a change to a runtime-sensitive surface (the serializer, the node:http vs `Bun.serve` listener + request path, SSR / action / CSRF dispatch, streams, `node:crypto`, the TS stripper, auth / session / cors) MUST be proven on Bun (`node scripts/run-bun-tests.js` + the touched `test/bun/*.mjs` under `bun`) AND ship an added/updated `test/bun/<feature>.mjs` cross-runtime assertion. `npm test` does NOT run browser, e2e, or Bun; run them yourself and report the result. Never report work done with failing or missing tests. See `references/testing.md`. Enforced by `.claude/hooks/require-tests-with-src.sh` (the scaffold variant WARNS unless `WEBJS_TEST_GATE=block`) and `.claude/hooks/require-bun-parity-with-runtime-src.sh` (BLOCKS a commit that stages runtime-sensitive source with no `test/bun/**` test; escape hatch `WEBJS_BUN_VERIFIED=1`).
2. **Documentation, part of the definition of done (not optional).** A task is NOT done until EVERY doc surface its change touches is in sync: `AGENTS.md` + the skill at `.agents/skills/webjs/` (SKILL.md + references/) for new API surface, `CONVENTIONS.md` (and per-package `AGENTS.md`) for new conventions, the docs site (`website/app/docs/<topic>`), the marketing `website/`, the scaffold templates (`packages/cli/templates/` per-agent rule files), and `README.md` for a headline capability. Updating `AGENTS.md` alone reproduces the #488 gap (docs site left stale). Invoke the `webjs-doc-sync` skill to sync every applicable surface. Enforced by `.claude/hooks/require-docs-with-src.sh`, which BLOCKS a commit that stages public `packages/*/src` source with no doc surface alongside it (a genuinely internal refactor / CI / release / perf change with no behaviour change bypasses with `WEBJS_NO_DOC_GATE=1`).
3. **Scaffold + skill sync (when a feature changes what apps should do).** The scaffold `webjs create` emits is a gallery index home + a root layout + db wiring, a densely-commented feature gallery (`packages/cli/templates/gallery/**`, single-concept demos under `app/features/` plus the `app/examples/todo` app, shipped in every UI template) and the api backend-features showcase (`packages/cli/lib/api-gallery.js`), plus the one cross-agent skill at `packages/cli/templates/.agents/skills/webjs/` (SKILL.md + references). So when a WebJs feature is added or changed, ask: does the generator (`packages/cli/lib/{create,api-gallery}.js`), a gallery demo (`packages/cli/templates/gallery/`), or the agent skill (`.agents/skills/webjs/SKILL.md` + its `references/`) need to move so a freshly scaffolded app and the skill teach the new reality? Verify by generating an app and running `generate + boot + webjs check` (the generators emit strings, so an escaping bug only shows in a freshly generated app). See `framework-dev.md`.
4. **Convention validation.** Run `webjs check` and fix violations. Run it from INSIDE an app, never from the repo root: the root is a workspace, not an app, so the command refuses there with exit 1 rather than reporting the cross-app collisions no single runtime ever sees (#1301). In this repo that means `( cd examples/blog && npx webjs check )` and `( cd website && npx webjs check )`. Run `webjs doctor` too when you touched an in-repo app (`examples/blog`, `website`): the required `conventions` CI job runs it over both, and it fails on a hard toolchain check or on whatever that app's `webjs.doctor.gate` marks `error` (today `UNMARKED_ASSET_LINKS` in `website` and `examples/blog`), so a clean `webjs check` alone is not enough to predict that job (#1257).
3. **Scaffold + skill sync (when a feature changes what apps should do).** The scaffold `webjs create` emits is a gallery index home + a root layout + db wiring, a densely-commented feature gallery (`gallery/**`, single-concept demos under `app/features/` plus the `app/examples/todo` app, shipped in every UI template) and the api backend-features showcase (`packages/cli/lib/api-gallery.js`), plus the one cross-agent skill at `packages/cli/templates/.agents/skills/webjs/` (SKILL.md + references). So when a WebJs feature is added or changed, ask: does the generator (`packages/cli/lib/{create,api-gallery}.js`), a gallery demo (`gallery/`), or the agent skill (`.agents/skills/webjs/SKILL.md` + its `references/`) need to move so a freshly scaffolded app and the skill teach the new reality? Verify by generating an app and running `generate + boot + webjs check` (the generators emit strings, so an escaping bug only shows in a freshly generated app). See `framework-dev.md`.
4. **Convention validation.** Run `webjs check` and fix violations. Run it from INSIDE an app, never from the repo root: the root is a workspace, not an app, so the command refuses there with exit 1 rather than reporting the cross-app collisions no single runtime ever sees (#1301). In this repo that means `( cd gallery && npx webjs check )`, `( cd examples/blog && npx webjs check )` and `( cd website && npx webjs check )`. Run `webjs doctor` too when you touched an in-repo app (`gallery`, `examples/blog`, `website`): the required `conventions` CI job runs it over all three, and it fails on a hard toolchain check or on whatever that app's `webjs.doctor.gate` marks `error` (today `UNMARKED_ASSET_LINKS` in `website` and `examples/blog`), so a clean `webjs check` alone is not enough to predict that job (#1257).

### Git workflow (mandatory)

Expand Down
Loading