@se-studio/skills 1.4.0 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @se-studio/skills
2
2
 
3
+ ## 1.4.1
4
+
5
+ ### Patch Changes
6
+
7
+ - f2de9ba: Document CMS smoke integrity (`cmsIntegrity`, `runStaticSmokeTestWithIntegrity`), Vercel `VERCEL_FORCE_NO_BUILD_CACHE`, and update `se-marketing-sites-smoke-test-setup` skill plus `example-empty` reference.
8
+
3
9
  ## 1.4.0
4
10
 
5
11
  ### Minor Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@se-studio/skills",
3
- "version": "1.4.0",
3
+ "version": "1.4.1",
4
4
  "description": "SE Studio agent skills for marketing site development with Contentful CMS",
5
5
  "repository": {
6
6
  "type": "git",
@@ -121,16 +121,23 @@ If a route needs a different layout (e.g. no header/footer), create `layout.tsx`
121
121
 
122
122
  ## A/B Test Variant Route (`page-test`)
123
123
 
124
- When A/B testing is enabled, variant pages are served through a `/page-test/[...slugs]` route that the middleware rewrites to. This route **must** use `generatePageTestMetadata` (not `generatePageMetadata`) so that `robots`/`indexed` comes from the **canonical/control** page — variant entries are intentionally `indexed: false` in Contentful.
124
+ When A/B testing is enabled, variant pages are served through a `/page-test/[...slugs]` route that the middleware rewrites to. This route **must** use:
125
+
126
+ - **`generatePageTestMetadata`** (not `generatePageMetadata`) so that `robots`/`indexed` comes from the **canonical/control** page — variant entries are intentionally `indexed: false` in Contentful.
127
+ - **`generatePageTest`** (not `generatePage`) so breadcrumbs, `model.href`, and JSON-LD use the **control/canonical** path while variant **content** still renders.
128
+
129
+ `BasicLayout` should resolve breadcrumbs and structured data from `currentPath ?? model.href` (the factory passes `currentPath={canonicalPath}`).
125
130
 
126
131
  If the slug is not a known variant in `testsByPath`, **return 404** (`notFound()`): there is no control page to merge, and `/page-test/…` is only valid for configured tests.
127
132
 
133
+ Direct visits to a variant's own CMS URL (normal `[...slugs]` route) continue to use `generatePage` and show variant breadcrumbs.
134
+
128
135
  ```typescript
129
136
  import { findCanonicalPath } from '@se-studio/ab-testing';
130
137
  import type { ResolvingMetadata } from 'next';
131
138
  import { notFound } from 'next/navigation';
132
139
  import { testsByPath } from '@/generated/abTests';
133
- import { generatePage, generatePageTestMetadata } from '@/lib/route-handlers';
140
+ import { generatePageTest, generatePageTestMetadata } from '@/lib/route-handlers';
134
141
  import { getPageRouteConfig } from '@/lib/routeConfig';
135
142
 
136
143
  /** Variant slug must appear in testsByPath; otherwise this route is not valid. */
@@ -165,13 +172,15 @@ export async function generateMetadata(
165
172
  }
166
173
 
167
174
  export default async function (props: PageProps<'/page-test/[...slugs]'>) {
168
- const { slug, path } = await extractDetails(props);
169
- requireCanonicalPath(slug);
170
- return generatePage(slug, path, getPageRouteConfig(slug));
175
+ const { slug } = await extractDetails(props);
176
+ const canonicalPath = requireCanonicalPath(slug);
177
+ const canonicalSlug = canonicalPath.replace(/^\/|\/$/g, '') || slug;
178
+ // Use generatePageTest: variant contents with control identity (href, breadcrumbs, JSON-LD).
179
+ return generatePageTest(slug, canonicalSlug, canonicalPath, getPageRouteConfig(canonicalSlug));
171
180
  }
172
181
  ```
173
182
 
174
- Also export `generatePageTestMetadata` from the app's `src/lib/route-handlers.ts` destructuring alongside `generatePageMetadata`.
183
+ Also export `generatePageTestMetadata` and `generatePageTest` from the app's `src/lib/route-handlers.ts` destructuring alongside `generatePageMetadata`.
175
184
 
176
185
  ## See Also
177
186
 
@@ -5,17 +5,18 @@ description: "Set up or regenerate smoke.cases.json for local smoke tests from t
5
5
 
6
6
  # Smoke test setup (static `smoke.cases.json`)
7
7
 
8
- Local smoke tests are **HTTP-only**: no `cms-server`, Contentful, or preload. Each app commits **`smoke.cases.json`** with curated URLs. Regenerate this file when routes or indexed content change materially.
8
+ Local smoke tests use curated HTTP cases in **`smoke.cases.json`**. Optionally enable **`cmsIntegrity`** for a local-only Contentful article-link check (no `cms-server` import in smoke scripts).
9
9
 
10
- Requires `@se-studio/site-check` **2.1.2+** for cache log audit; **2.0.0+** for static smoke.
10
+ Requires `@se-studio/site-check` **^2.6.1** when using `cmsIntegrity` or `pnpm smoke-test` with integrity enabled; **2.1.2+** for cache log audit; **2.0.0+** for HTTP-only static smoke.
11
11
 
12
- Preview / `DRAFT_ONLY` Contentful access in local dev is expected and not a smoke failure.
12
+ Preview / `DRAFT_ONLY` Contentful access in local dev is expected and not a smoke failure. **Deployment / live smoke stays HTTP-only** — do not enable `cmsIntegrity` on Vercel deployment checks.
13
13
 
14
14
  ## Prerequisites
15
15
 
16
- - App has `scripts/smoke-test-run.ts` calling `runStaticSmokeTest('smoke.cases.json')`.
16
+ - App has `scripts/smoke-test-run.ts` (see [CMS article-link integrity](#cms-article-link-integrity-optional) below).
17
17
  - `package.json`: `"smoke-test:run": "tsx scripts/smoke-test-run.ts"`.
18
18
  - `SITEMAP_PROD_URL` in `.env.example` (production sitemap for URL curation).
19
+ - When `cmsIntegrity.enabled` is true: `@se-studio/contentful-rest-api@^1.10.0`, `@se-studio/core-data-types@^1.5.1`, Contentful tokens in `.env.local`.
19
20
 
20
21
  ## Script matrix
21
22
 
@@ -103,8 +104,24 @@ Or from the app directory without a per-app script: `node ../../scripts/smoke-te
103
104
  | `DEPLOYMENT_URL` | Live deployment URL for `smoke-test:live` (CI / `repository_dispatch` payload) |
104
105
  | `VERCEL_PROTECTION_BYPASS_TOKEN` | Deployment Protection bypass secret (aliases: `VERCEL_AUTOMATION_BYPASS_SECRET`, `VERCEL_BYPASS_TOKEN`) |
105
106
  | `PRODUCTION_SITE_URL` | Optional canonical production URL for audits and related tooling |
107
+ | `SMOKE_TEST_SKIP_INTEGRITY` | Set by `smoke-test-one` when integrity runs in parallel; HTTP-only child run |
108
+ | `VERCEL_FORCE_NO_BUILD_CACHE` | **Vercel project env only** (`1`) — skip remote build cache when webpack `WasmHash` failures occur on Vercel (not `vercel.json`) |
106
109
 
107
- Add these to `.env.example` under an SEO / site-check section. `runPreviewStaticSmokeTest` requires `PREVIEW_SITE_URL` and a bypass token in `.env.local`.
110
+ Add smoke-related vars to `.env.example` under an SEO / site-check section. `runPreviewStaticSmokeTest` requires `PREVIEW_SITE_URL` and a bypass token in `.env.local`.
111
+
112
+ ### Vercel build cache (`VERCEL_FORCE_NO_BUILD_CACHE`)
113
+
114
+ Intermittent Vercel `next build` failures (`WasmHash._updateWithBuffer` / `Cannot read properties of undefined (reading 'length')`) often correlate with a stale remote build cache. Mitigation:
115
+
116
+ ```bash
117
+ vercel env add VERCEL_FORCE_NO_BUILD_CACHE preview develop --value 1 --yes
118
+ vercel env add VERCEL_FORCE_NO_BUILD_CACHE production --value 1 --yes
119
+ ```
120
+
121
+ - Set on the **Vercel project** (Preview and/or Production). Not in app `.env.local`.
122
+ - Tradeoff: cold builds ~1m vs ~30s cached.
123
+ - Roll out to other marketing sites on the next Vercel config pass when builds fail intermittently locally pass.
124
+ - Document in the app `AGENTS.md` when applied.
108
125
 
109
126
  ## Workflow
110
127
 
@@ -175,16 +192,37 @@ curl -sI "http://localhost:<port>/some-path.md"
175
192
  }
176
193
  ```
177
194
 
195
+ Optional `cmsIntegrity` (local only — see section below):
196
+
197
+ ```json
198
+ {
199
+ "cmsIntegrity": {
200
+ "enabled": true,
201
+ "routing": {
202
+ "articleTypesBasePath": "/resources",
203
+ "tagsBasePath": "/topics",
204
+ "peopleBasePath": "/team",
205
+ "defaultTopic": "other",
206
+ "enablePrimaryTagPartOfSlug": true,
207
+ "topiclessArticleTypeSlugs": ["ebooks"]
208
+ }
209
+ }
210
+ }
211
+ ```
212
+
213
+ Map `routing` from `src/lib/constants.ts` (or the app’s URL rules). `enabled: false` omits the integrity fetch.
214
+
178
215
  - `path` — site-relative, trailing slash when the site uses trailing slashes.
179
216
  - `port` — default; override at run time with `SMOKE_TEST_PORT`.
180
217
 
181
218
  ### 7. Verify
182
219
 
183
220
  ```bash
184
- pnpm smoke-test:run
221
+ pnpm smoke-test:run # HTTP (+ integrity when cmsIntegrity.enabled)
222
+ pnpm smoke-test # starts dev server; integrity runs in parallel when enabled
185
223
  ```
186
224
 
187
- Expect `Summary: N cases, 0 failed`. Preview API / `DRAFT_ONLY` logs in dev are OK.
225
+ Expect `Summary: N cases, 0 failed`. With integrity: `article link integrity: ok`. Preview API / `DRAFT_ONLY` logs in dev are OK.
188
226
 
189
227
  Optional cache audit:
190
228
 
@@ -198,14 +236,42 @@ Expect `cache log audit: ok` with no 2MB errors. `cache skip` lines are warnings
198
236
 
199
237
  Commit `smoke.cases.json`. Remove legacy `smoke.config.ts` if present.
200
238
 
239
+ ## CMS article-link integrity (optional)
240
+
241
+ Local-only check: one Contentful fetch validates all article/person/tag link `href` values (broken links, conversion errors). Runs in parallel with server boot when using `pnpm smoke-test`.
242
+
243
+ **`scripts/smoke-test-run.ts`:**
244
+
245
+ ```ts
246
+ import {
247
+ formatCombinedSmokeReport,
248
+ getCombinedSmokeExitCode,
249
+ runStaticSmokeTestWithIntegrity,
250
+ } from '@se-studio/site-check/smoke-test';
251
+
252
+ async function main(): Promise<number> {
253
+ const skipIntegrity = (process.env.SMOKE_TEST_SKIP_INTEGRITY ?? '').toLowerCase() === 'true';
254
+ const { integrity, http } = await runStaticSmokeTestWithIntegrity('smoke.cases.json', {
255
+ skipIntegrity,
256
+ });
257
+ console.log(formatCombinedSmokeReport(integrity, http));
258
+ return getCombinedSmokeExitCode(integrity, http);
259
+ }
260
+ ```
261
+
262
+ Remove legacy `smoke-test:validate-links`, `smoke-test-preload.cjs`, and bespoke link-validation scripts when migrating.
263
+
264
+ **Per-site rollout:** bump `@se-studio/site-check@^2.6.1`, add `cmsIntegrity` to `smoke.cases.json`, update `smoke-test-run.ts`, run `pnpm smoke-test` locally. Reference consumer: `om1-website` (`develop`).
265
+
201
266
  ## Migrating from `smoke.config.ts`
202
267
 
203
268
  1. Delete `smoke.config.ts`.
204
269
  2. Simplify `smoke-test:run` script (no bash, no `NODE_OPTIONS`, no preload).
205
- 3. Update `@se-studio/site-check` to `^2.1.2`.
270
+ 3. Update `@se-studio/site-check` to `^2.6.1` when adopting integrity; otherwise `^2.1.2` minimum.
206
271
  4. Follow workflow above to create `smoke.cases.json`.
207
272
 
208
273
  ## Reference
209
274
 
210
- - Example: `apps/example-empty/smoke.cases.json` (monorepo).
211
- - Package API: `loadStaticSmokeConfig`, `runStaticSmokeTest`, `runPreviewStaticSmokeTest`, `auditCacheLogs` from `@se-studio/site-check/smoke-test`.
275
+ - HTTP + integrity example: `apps/example-empty/smoke.cases.json` and `scripts/smoke-test-run.ts` (monorepo).
276
+ - Package API: `runStaticSmokeTest`, `runStaticSmokeTestWithIntegrity`, `runPreviewStaticSmokeTest`, `auditCacheLogs` from `@se-studio/site-check/smoke-test`.
277
+ - CMS integrity types: `@se-studio/site-check/cms-integrity`.