feat: add tier-2 scaffold teaching-coverage gate for core exports#861
Merged
Conversation
The require-scaffold-with-src commit hook is only a floor: it proves a feature commit touched SOME scaffold file, but cannot tell a real demo from a doc bullet, which is how #848 shipped forbidden()/unauthorized() documented but undemoed. Add the missing tier-2, mirroring how tests are enforced (a commit floor plus an un-skippable CI gate). gallery-coverage.test.js reconciles the live @webjsdev/core export surface against a hand-curated gallery-coverage.json manifest and FAILS when a new export is neither demoed in the gallery nor consciously exempted (internal plumbing, or a deferred agent-facing API). It runs under npm test, so a local --no-verify cannot skip it: a new export turns CI red until classified. The reconcile() core is pure; its failure modes (new export, stale key, missing demo file, empty reason) are proven with synthetic inputs beside the real-surface assertion. Seed the manifest across all 100 current exports (12 demoed, 59 internal, 29 deferred, tracked in #859), and make notFound genuinely demoed by having routing/[id] throw it for a reserved id (verified: /features/routing/missing renders 404).
…urfaces Wire the new gate into the surfaces that describe scaffold enforcement: the webjs-scaffold-sync skill (a two-tier enforcement section plus a change-type row requiring a manifest entry for every new core export), the require-scaffold-with-src hook header (cross-reference the CI gate as the real teeth), and agent-docs/framework-dev.md (a section explaining both tiers and the manifest workflow).
6 tasks
Close the two enforcement gaps the core-only gate left open, in the same
tier-2 model. The gate now reconciles THREE live surfaces, not one:
- @webjsdev/core exports (unchanged): a { demo } gallery-file pointer.
- @webjsdev/server exports (new, 129): { demoed: true } verified by a
generated full-stack/api/saas app importing it, else an { exempt }
reason. 21 demoed, 72 internal, 36 deferred.
- routing convention files (new, 18): the stems DERIVED from
packages/server/src/router.js source (so a new stem === '...' branch
auto-appears and forces classification), each demonstrated by a file
in a generated app. 6 demonstrated, 12 deferred (error/loading/
not-found boundaries + metadata routes the scaffold does not yet ship).
A pure reconcileSet() handles the demoed/exempt model; its failure modes
(unclassified, stale, demoed-but-not-demonstrated, empty reason) are
proven with synthetic inputs beside the real-surface assertions. Docs
(skill two-tier section + change-type row, hook header, framework-dev)
updated to the broadened scope. Full scaffold suite 44/44.
Collaborator
Author
|
Extended to close the two enforcement gaps flagged in review, in the same tier-2 model (commit 308b2e1). The gate now reconciles THREE live surfaces:
A pure The remaining gap (writing the deferred demos) is content work the gate is designed to SURFACE, tracked in #859 (now expanded to server + convention deferred items). |
vivek7405
added a commit
that referenced
this pull request
Jul 9, 2026
The coverage-gate extension gated routing convention files and server exports, but this framework-dev paragraph still claimed conventions stay tier-1-only. Correct it to the shipped three-surface scope.
vivek7405
added a commit
that referenced
this pull request
Jul 9, 2026
…ting Review round 3: the #861 heuristic wrongly marked documented app-facing exports internal. Move render/renderStream/renderToString (public API), readBody/stampRemoteIp (api-routes/rate-limiting docs), attachWebSocket + the buildRouteTable/matchPage/matchApi/rawActionRequest test helpers (testing docs) to deferred. A proactive doc-import sweep of every remaining internal-marked core+server export confirms none others are mis-classed (parse/stringify appear only in a 'you never see this file' generated-stub snippet, so they stay internal).
vivek7405
added a commit
that referenced
this pull request
Jul 9, 2026
The coverage-gate extension gated routing convention files and server exports, but this framework-dev paragraph still claimed conventions stay tier-1-only. Correct it to the shipped three-surface scope.
vivek7405
added a commit
that referenced
this pull request
Jul 9, 2026
…ting Review round 3: the #861 heuristic wrongly marked documented app-facing exports internal. Move render/renderStream/renderToString (public API), readBody/stampRemoteIp (api-routes/rate-limiting docs), attachWebSocket + the buildRouteTable/matchPage/matchApi/rawActionRequest test helpers (testing docs) to deferred. A proactive doc-import sweep of every remaining internal-marked core+server export confirms none others are mis-classed (parse/stringify appear only in a 'you never see this file' generated-stub snippet, so they stay internal).
vivek7405
added a commit
that referenced
this pull request
Jul 9, 2026
* docs: fix stale tier-1-only convention note left by #861 The coverage-gate extension gated routing convention files and server exports, but this framework-dev paragraph still claimed conventions stay tier-1-only. Correct it to the shipped three-surface scope. * feat: ship sitemap/robots/manifest metadata-route demos in the scaffold Close 4 coverage-gate deferred gaps: the scaffold now ships example app/sitemap.ts (uses sitemap() from @webjsdev/server), app/robots.ts, and app/manifest.ts, flipping the sitemap/robots/manifest routing conventions and the server sitemap export from exempt to demonstrated. Gate: conventions 6->9 demonstrated, server 21->22 demoed. * feat: demo the common lit directives in the scaffold (live/ref/until/keyed/unsafeHTML) Extend the directives gallery demo with a second card covering live (a controlled input), ref + createRef (a DOM-node handle), until (a pending fallback), unsafeHTML (trusted raw HTML), and keyed (fresh subtree on key change). Flips 6 core exports from deferred to demoed (core 12->18). The niche completeness directives (guard, templateContent, asyncAppend/ asyncReplace) stay documented-deferred rather than ship contrived demos. * feat: demo error/loading/not-found boundaries in the scaffold Round out the boundaries feature with the three remaining nested boundary conventions: error.ts (with a crash/ sub-route that throws so it is reachable), loading.ts (the Suspense fallback), and not-found.ts (nearest 404, served for an unmatched subtree URL). Index links the new routes. Flips error/loading/not-found conventions to demonstrated (conventions 9->12). Verified: /crash renders 500, an unmatched URL 404. * feat: demo shadow-DOM css + computed/effect/batch in the scaffold Add a <reactive-meter> shadow-DOM component to the components feature: static shadow=true with static styles=css`` (scoped CSS, the one place the lit reflex is right), plus computed (derived signal), effect (a browser-side reaction), and batch (coalesced writes). Flips css/computed/ effect/batch from deferred to demoed (core 18->22). Shadow DSD verified in SSR output. * feat: demo navigate()/revalidate() in the client-router gallery Add a <router-controls> component to the client-router feature that drives programmatic soft navigation (navigate()) and browser snapshot eviction (revalidate()) from JS handlers. Flips navigate/revalidate to demoed (core 22->24). * fix: manifest literal placeholder + until() re-render flicker in scaffold demos Two review findings: (1) gallery files are copied verbatim with no {{APP_NAME}} substitution, so manifest.ts served a literal '{{APP_NAME}}' in /manifest.json; hardcode a neutral 'webjs app' the user adapts. (2) the directives demo called later() inside render(), so until()'s new promise identity each render flashed back to the fallback on any re-render; create the promise once in a field. Also rephrase the inline comments to satisfy the prose-punctuation invariant. * fix: reclassify session/store exports back to deferred (over-reached internal) Review round 2: cookieSessionStorage, storeSessionStorage (+ its alias storeSession), and getStore are app-facing and documented (docs/sessions, docs/cache, agent-docs/built-ins), so internal was wrong. Move them back to deferred so they stay in the demo backlog. cookieSessionStorage now matches its alias cookieSession. Six genuinely-internal reclassifications (attachWebSocket, runInstrumentation, low-level cookie serializers, runWithActionSignal) stand. * fix: correct more internal/deferred mis-classes from #861 regex bucketing Review round 3: the #861 heuristic wrongly marked documented app-facing exports internal. Move render/renderStream/renderToString (public API), readBody/stampRemoteIp (api-routes/rate-limiting docs), attachWebSocket + the buildRouteTable/matchPage/matchApi/rawActionRequest test helpers (testing docs) to deferred. A proactive doc-import sweep of every remaining internal-marked core+server export confirms none others are mis-classed (parse/stringify appear only in a 'you never see this file' generated-stub snippet, so they stay internal).
This was referenced Jul 9, 2026
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.
Why
Follow-up to #854. That PR hardened the
webjs-scaffold-syncskill (guidance) so the #848-class gap (a new control-flow throw shipped documented but undemoed) would be caught by an agent reading the skill. But guidance is not enforcement. As discussed, tests are enforced two ways: a commit gate ("include a test") AND CI actually running it ("it must exist and pass"). Scaffold-sync only had tier 1, therequire-scaffold-with-src.shcommit floor, which proves you touched a scaffold file but cannot tell a real demo from a doc bullet. That floor was green when #848 shipped no demo.What: the missing tier 2
test/scaffolds/gallery-coverage.test.jsreconciles the live@webjsdev/coreexport surface against a hand-curatedtest/scaffolds/gallery-coverage.jsonmanifest and FAILS when an export is neither:{ demo }, a runnable gallery file that actually references the symbol, or{ exempt }, with a reason (internal:plumbing, ordeferred:agent-facing but not yet demoed).It runs under
npm test(walked byscripts/run-node-tests.js) and in CI, so a local--no-verifycannot skip it. A new core export turns CI red until it is classified, the exact analogue of "a test must exist and pass." Had this existed, addingforbidden/unauthorizedwould have failed CI until they were demoed (which #854 did) or consciously exempted.Design details:
reconcile()core is a pure function. Its failure modes (new export, stale key, missing demo file, demo that does not reference the symbol, empty exemption) are proven with synthetic inputs alongside the real-surface assertion, so the gate's teeth are themselves tested.Suspense,css/ shadow styles,navigate, the remaining lit directives) is the honest audit output. It is printed every run and tracked in scaffold: close the coverage-gate deferred gaps (core + server + convention demos) #859 so it stays visible and gets converted to real demos over time.notFoundgenuinely demoed:routing/[id]now throws it for a reserved id (/features/routing/missingrenders 404, verified on a booted app), so the gate drives a demo rather than an exemption.Scope / limitation
The gate covers the export surface (where the #848 throwers live). A new routing convention file type (a
*.tsboundary) is not an export, so it stays tier-1-only for now, noted in the skill and the hook header.Verification
test/scaffolds/gallery-coverage.test.js: 6/6 (real surface green plus 5 counterfactuals proving the gate fails correctly).test/scaffolds/*: 38/38./features/routing/42returns 200,/features/routing/missingreturns 404.webjs check: onlyno-scaffold-placeholder.Definition of done
webjs-scaffold-syncskill (two-tier enforcement section plus change-type row),require-scaffold-with-src.shheader,agent-docs/framework-dev.md. Updated.notFounddemo added and verified.The #859 backlog (demoing the deferred exports) stays open and is out of scope here; this PR only adds the gate.
Closes #865