@typeroll/mcp-server 0.45.96 → 0.45.97

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.
@@ -12,7 +12,7 @@ export const deployTools = [
12
12
  },
13
13
  {
14
14
  name: 'trigger_deploy',
15
- description: "Enqueue a deploy (static-page build → hosting) of the currently active version. Returns a job_id immediately; poll get_deploy_status until the status leaves queued/running. NOT for previewing iterative design/content edits — that's what get_preview_link is for (it renders live from the DB with no build). Reserve trigger_deploy for publishing, a stakeholder link to the compiled site, or pre-merge validation. Deploys build SAVED content only — unsaved drafts (working copies) are excluded, so commit_working_copy (or save:true on your writes) before deploying, or your changes won't ship. Customer publishing freezes a consistent saved source, then resumes source/Git/build-dispatch checkpoints without recapturing later edits. GitHub or Cloudflare performs rendering and media work. With dry_run:true, customer publishing validates frozen source without a Git push or external build; it does not prove an Astro build succeeds. Other self-hosted adapters may build locally without upload. Check the reported phase.",
15
+ description: "Enqueue a deploy (static-page build → hosting) of the currently active version. Returns a job_id immediately; poll get_deploy_status until the status leaves queued/running. NOT for previewing iterative design/content edits — that's what get_preview_link is for (it renders live from the DB with no build). Reserve trigger_deploy for publishing, a stakeholder link to the compiled site, or pre-merge validation. Deploys build SAVED content only — unsaved drafts (working copies) are excluded, so commit_working_copy (or save:true on your writes) before deploying, or your changes won't ship. Customer publishing freezes a consistent saved source, then resumes source/Git/build-dispatch checkpoints without recapturing later edits. GitHub or Cloudflare performs rendering and media work. With dry_run:true, customer publishing validates frozen source without a Git push or external build; it does not prove an Astro build succeeds. Other self-hosted adapters may build locally without upload. Check the reported phase. All sites in an organization share one build engine: publish one site at a time, starting the next only when the previous job has finished and read_organization_build_engine shows no active_jobs.",
16
16
  inputSchema: {
17
17
  environment: z.enum(['production', 'staging']).optional(),
18
18
  allow_unsaved: z.boolean().optional().describe('Explicitly publish saved content while leaving the reported unsaved Pages/partials untouched. Only after owner confirmation.'),
@@ -44,7 +44,7 @@ export const mediaTools = [
44
44
  },
45
45
  {
46
46
  name: 'get_media_preparation',
47
- description: 'Read background image preparation progress. Originals and prepared draft variants remain private. Requires site access.',
47
+ description: 'Read background image preparation progress, including the build_job_id of a running preparation job on the organization build engine. Originals and prepared draft variants remain private. Requires site access.',
48
48
  inputSchema: {},
49
49
  handler: withErrorBoundary(async (_args, { client, siteId }) => ok(await client.get(siteId, 'media/preparation'))),
50
50
  },
@@ -79,7 +79,7 @@ export const mediaTools = [
79
79
  },
80
80
  {
81
81
  name: 'upload_media_from_url',
82
- description: 'Fetch an image (or PDF) from any public URL and push it to the site\'s media library in one call. The MCP server downloads the source from verified public HTTP(S) destinations; private addresses and unsafe redirects are refused. Integrity-safe: the bytes are streamed, never transcribed as base64 through the model, so (unlike a large `upload_media_inline` payload) they can\'t be silently corrupted in transit. Returns { media_id, cdn_url, filename }.',
82
+ description: 'Fetch an image (or PDF) from any public URL and push it to the site\'s media library in one call. The MCP server downloads the source from verified public HTTP(S) destinations; private addresses and unsafe redirects are refused. Integrity-safe: the bytes are streamed, never transcribed as base64 through the model, so (unlike a large `upload_media_inline` payload) they can\'t be silently corrupted in transit. Returns { media_id, cdn_url, filename, finalize }. Each upload queues image preparation (finalize.preparation): one job per site on the organization\'s shared build engine, started shortly after the upload. get_media_preparation returns its build_job_id; let it finish, like any other active build, before publishing another site of the organization.',
83
83
  inputSchema: {
84
84
  source_url: z.string().url().describe('Public URL to download the image from.'),
85
85
  filename: z.string().optional().describe('Override the filename used on R2. Defaults to the last path segment of source_url. When its extension does not match the content type, the type\'s extension is appended; the response returns the stored filename.'),
@@ -95,7 +95,7 @@ export const mediaTools = [
95
95
  },
96
96
  {
97
97
  name: 'upload_media_batch_from_urls',
98
- description: 'Upload 1–50 public images/PDFs (max 25 MiB each) in one MCP call. Uses the same integrity-safe download → signed PUT → finalize pipeline as upload_media_from_url, with four concurrent workers. Returns one result per source URL; individual failures do not abort the batch.',
98
+ description: 'Upload 1–50 public images/PDFs (max 25 MiB each) in one MCP call. Uses the same integrity-safe download → signed PUT → finalize pipeline as upload_media_from_url, with four concurrent workers. Returns one result per source URL; individual failures do not abort the batch. Uploads queue image preparation as upload_media_from_url describes.',
99
99
  inputSchema: {
100
100
  items: z.array(z.object({
101
101
  source_url: z.string().url(),
@@ -16,6 +16,7 @@ export const redirectTools = [
16
16
  {
17
17
  name: 'create_redirect',
18
18
  description: 'Create a redirect rule, or update the rule that already has this from_path in place (same id, no gap). ' +
19
+ 'The response action says which happened; every distinct from_path gets its own rule, however long. ' +
19
20
  'Defaults to 301; pass status_code=302 for a temporary redirect. ' +
20
21
  'WILDCARDS: a trailing "*" captures everything under a prefix and ":splat" replays it into the ' +
21
22
  'target — `from_path="/category/*", to_path="/blogg/:splat"` retires an entire WordPress taxonomy ' +
@@ -124,7 +124,7 @@ export const settingsTools = [
124
124
  accept_all: z.string().optional(),
125
125
  privacy_link: z.string().optional(),
126
126
  manage: z.string().optional(),
127
- }).optional().describe('Plain-text replacements for the banner heading, buttons and links (max 120 characters each). Omitted or blank labels follow the page language: Swedish, German, otherwise English. Replaces the whole labels object.'),
127
+ }).optional().describe('Plain-text replacements for the banner heading, buttons and links (max 120 characters each). Omitted or blank labels follow the page language: Swedish, German, Dutch, otherwise English. Replaces the whole labels object.'),
128
128
  }).optional().describe('Native consent banner configuration. Partial updates preserve omitted fields. Script fields execute in visitor browsers and are accepted under the API key authority.'),
129
129
  },
130
130
  handler: withErrorBoundary(async (args, { client, siteId }) => {
package/dist/version.js CHANGED
@@ -8,4 +8,4 @@
8
8
  //
9
9
  // Keep it in lockstep with package.json: tests/version.test.ts asserts
10
10
  // VERSION === package.json.version, so a bump that forgets this line fails CI.
11
- export const VERSION = '0.45.96';
11
+ export const VERSION = '0.45.97';
package/dist/workspace.js CHANGED
@@ -69,6 +69,11 @@ Public documentation: https://typeroll.com/docs/llms.txt
69
69
  upload/finalization. Never put image bytes or customer secrets in Git.
70
70
  - Inspect desktop/mobile layouts and interaction states. Read changes back.
71
71
  Record exact version, tested pages, evidence and unresolved issues in qa/.
72
+ - The signed preview is for content and layout review. Verify embeds, consent,
73
+ analytics, fonts and measured design checks on the published test address.
74
+ - Publish one Site at a time per Organization: all Sites share one build engine.
75
+ - Record Typeroll defects in qa/typeroll-platform-feedback.md. Do not resume
76
+ affected publishing until the fix is confirmed live.
72
77
  - Never edit generated publishing source to change CMS content. Do not overwrite
73
78
  current CMS or owner answers from an old local snapshot.
74
79
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@typeroll/mcp-server",
3
- "version": "0.45.96",
3
+ "version": "0.45.97",
4
4
  "description": "Typeroll CMS MCP server \u2013 connect AI agents to the REST API to manage content and publish static websites.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -143,6 +143,18 @@ Pick from high-quality Google Fonts pairings:
143
143
  Same font for heading and body is fine if it has enough weight variation
144
144
  (Inter at 700 + 400 works well).
145
145
 
146
+ Use the exact Google Fonts family name ("Plus Jakarta Sans", not "plus
147
+ jakarta"). Fonts are self-hosted by default (`fonts.delivery: "self_hosted"`,
148
+ Core 0.2.130+): when a publication is frozen, Typeroll copies the Google Fonts
149
+ CSS and woff2 files (weights 400–700) into the build. The site serves
150
+ `/_typeroll/fonts/fonts.css` and the files under `/_typeroll/fonts/`; visitors
151
+ never contact `fonts.googleapis.com` or `fonts.gstatic.com`, before or after
152
+ consent. A family Google does not serve is skipped without an error and the
153
+ browser falls back to the next font in the stack. `system`, or only local
154
+ fonts such as Arial, captures nothing. `fonts.delivery: "google"` links the
155
+ Google CDN directly and sends every visitor's IP address to Google: that is a
156
+ privacy/consent decision for the owner, never your default.
157
+
146
158
  `size_base` should be 16 for most sites; 17–18 for text-heavy editorial
147
159
  sites; 15 for dense dashboards.
148
160
 
@@ -324,7 +336,7 @@ Better default:
324
336
 
325
337
  Reserve a third column only when there's a genuinely different surface (locations, languages, partner pages). Don't pad the footer with content-type columns — navigation to those happens via top nav + topic pages.
326
338
 
327
- ## Step 7 — Preview
339
+ ## Step 7 — Preview, then verify fonts on the published site
328
340
 
329
341
  ```
330
342
  get_preview_link
@@ -332,10 +344,19 @@ get_preview_link
332
344
 
333
345
  Open in browser. Check:
334
346
  - Colors render as intended (not "undefined" or missing)
335
- - Fonts load (Google Fonts link is in `<head>`)
336
347
  - Nav text is readable against header background
337
348
  - Body text has sufficient contrast
338
349
 
350
+ The preview loads fonts from Google inside a sandbox, so it proves nothing
351
+ about font delivery. After an authorized publish, check the published
352
+ temporary/test address:
353
+ - `/_typeroll/fonts/fonts.css` has `@font-face` rules for every chosen family.
354
+ A comment instead of rules means Google did not serve that name: fix it.
355
+ - Every font file `url(...)` in it returns 200.
356
+ - No request goes to `fonts.googleapis.com` or `fonts.gstatic.com`.
357
+ - After `await document.fonts.ready`, `[...document.fonts]` has a `loaded`
358
+ face for each family, and headings/body compute to that family.
359
+
339
360
  ## Pitfalls
340
361
 
341
362
  - **Don't set colors without checking the header contrast.** If `primary`
@@ -344,9 +365,9 @@ Open in browser. Check:
344
365
  - **Custom_css is global.** Rules here apply to every page. Keep it to
345
366
  `:root {}` token additions and truly global utilities. Page-specific
346
367
  styles go in the page's HTML `<style>` block.
347
- - **Google Fonts load time.** Two different font families is fine; three
348
- adds measurable LCP impact. Stick to two families with variable-font
349
- versions when possible.
368
+ - **Font weight on every page.** Two families is fine; a third adds
369
+ measurable LCP impact even when self-hosted. A misspelled family name
370
+ fails silently to the fallback font — verify it as in Step 7.
350
371
  - **Dark themes need dark surface too.** Setting `background: #0f0f0f`
351
372
  but leaving `surface: #f8fafc` (white) breaks every card/input. Always
352
373
  update all 7 tokens as a set.
@@ -37,14 +37,21 @@ So, always:
37
37
  1. Get a URL for the version under review. While iterating, use the DB-live
38
38
  `get_preview_link` (mint once, reuse — defaults to a 24h TTL) — it renders
39
39
  from the DB with no build, so fixes show on reload without re-deploying, and
40
- it's the loop for the review-fix-recheck cycle. For a FINAL bit-for-bit check
41
- of the compiled output before merge, deploy once (`trigger_deploy
42
- version="<branch>"` → poll `get_deploy_status` → use the immutable
43
- `deploy_url`, a Cloudflare Pages hash URL). Review the SAME url end to end.
40
+ it's the loop for content and layout review (screenshots, reading). It is
41
+ NOT a measurement surface: it serves the page from the portal origin as a
42
+ nested frame in an opaque-origin sandbox, so evaluate/DOM/computed-style
43
+ reads from your browser tool do not reach it reliably, the same-origin
44
+ iframe snippets below fail, third-party iframes usually stay empty, cookies
45
+ and storage do not persist, and fonts load from Google. `?consent=all|necessary`
46
+ on the link only pre-answers the cookie banner for that session.
47
+ Run the measured dimensions, third-party embeds, consent, analytics and
48
+ font checks on the published temporary/test address (an authorized
49
+ `trigger_deploy version="<branch>"` → poll `get_deploy_status` → its
50
+ `deploy_url`). Review the SAME url end to end.
44
51
  2. The snippets below run in a browser tool's "evaluate JavaScript" (Playwright /
45
- chrome-devtools / puppeteer MCP). **One origin per eval:** the iframe trick
46
- needs same-origin, so run each variant's snippet on its own page (different
47
- `*.pages.dev` hashes are cross-origin → `contentDocument` is null).
52
+ chrome-devtools / puppeteer MCP) on that published page. **One origin per
53
+ eval:** the iframe trick needs same-origin, so run each variant's snippet on
54
+ its own page (different hosts are cross-origin → `contentDocument` is null).
48
55
  3. If you run several variants with one shared browser profile, do them
49
56
  SEQUENTIALLY — parallel browser agents on one profile contaminate each other's
50
57
  tabs/screenshots.
@@ -293,9 +300,9 @@ then a one-line verdict. Rules:
293
300
  unqualified "approved" off a partial pass is not.
294
301
  - Brand FIT (palette/voice matching `brand.md`) is a direction judgment, not a
295
302
  pass/fail defect — call it out separately so the user decides direction.
296
- - If you fixed anything mid-review, just reload the DB-live preview and re-run
297
- the affected dimension before signing off — no re-deploy needed (deploy only
298
- for the final compiled-output check, if any).
303
+ - If you fixed anything mid-review, check it on the DB-live preview, then
304
+ re-run the affected measured dimension on the next authorized publish of the
305
+ test address before signing off.
299
306
 
300
307
  See `tr-redesign-branch` for the surrounding branch → preview → approve → merge
301
308
  flow; this skill is its measured approval round.
@@ -68,6 +68,18 @@ for the content type default). `discard_working_copy` throws an unsaved draft
68
68
  away. Publishing means saving the draft, `update_page` `status: "published"`
69
69
  (applies immediately) and, when the user wants it live now, `trigger_deploy`.
70
70
 
71
+ **Publishing and failed deploys.** Poll `get_deploy_status` until the job is
72
+ `succeeded` or `failed`. Every site in an organization shares one build
73
+ engine: publish one site at a time, and start another site's publish only when
74
+ the previous job has finished and `read_organization_build_engine` shows no
75
+ `active_jobs`. `build_result_missing` means the engine stopped without
76
+ reporting a result — a runner never ran the task, not a content problem;
77
+ publish again once the engine is idle. `retry_deploy_verification` only
78
+ retries public verification of an already-served publication
79
+ (`publication_observation_timeout`, `publication_cache_refresh_*`); a build or
80
+ engine-side static verification failure needs a new publish after fixing the
81
+ reported cause.
82
+
71
83
  **Site-wide changes are composition, not a series of page edits.** "Change
72
84
  every hero", "a CTA on every product page", "our phone number in every
73
85
  footer":
@@ -121,6 +121,9 @@ desktop 1280, wide 1536); do not independently hide all links at the menu bounda
121
121
  Verify 1023/1024 px, Tab/Shift-Tab, Enter/Space, Escape returning focus,
122
122
  visible focus, long labels, no-JavaScript links and no horizontal overflow.
123
123
 
124
+ Each entry in `links` (`core/navigation_links` and `core/navigation`) may set
125
+ `link_rel`, for example `nofollow sponsored` on an affiliate or partner link.
126
+
124
127
  ## Footer tree and consent
125
128
 
126
129
  Use an accessible footer navigation landmark and `core/navigation_links`, not
@@ -145,9 +148,10 @@ Cookie consent is a site feature, not a footer widget. Configure it separately:
145
148
  ```
146
149
 
147
150
  The heading, buttons and links follow the page language: Swedish for `sv`,
148
- German for `de`, otherwise English. For any other language, write `text` and
149
- set `labels` (`title`, `reject`, `necessary`, `accept_all`, `privacy_link`,
150
- `manage`) in that language.
151
+ German for `de`, Dutch for `nl`, otherwise English. For any other language,
152
+ write `text` and set `labels` (`title`, `reject`, `necessary`, `accept_all`,
153
+ `privacy_link`, `manage`) in that language. The publication's job `warnings`
154
+ list any banner wording and built-in control labels that fall back to English.
151
155
 
152
156
  Use `update_site_settings`, then read settings back. `scripts_necessary` and
153
157
  `scripts_optional` execute in visitors' browsers and must be reviewed like
@@ -228,7 +228,24 @@ A cluster of one is meaningless markup.
228
228
 
229
229
  ## Phase 7 — Verify BEFORE touching DNS
230
230
 
231
- Deploy each site (`trigger_deploy`), then check what it actually serves:
231
+ Deploy each site (`trigger_deploy`), then check what it actually serves.
232
+
233
+ **Publish one site at a time.** All sites in an organization share one
234
+ organization build engine, and its runners claim the oldest task in the
235
+ organization. Start the next site's `trigger_deploy` only when the previous
236
+ job is `succeeded` or `failed` and `read_organization_build_engine` shows no
237
+ `active_jobs`. Concurrent publications at best queue; on Core ≤0.2.132 they
238
+ could fail each other with `build_result_missing`.
239
+
240
+ - `build_result_missing` ("the build engine stopped without reporting a
241
+ result") means a runner never ran the task. Content did not cause it.
242
+ Publish that site again once the engine is idle.
243
+ - `retry_deploy_verification` only retries public verification of an
244
+ already-served publication (`publication_observation_timeout`,
245
+ `publication_cache_refresh_*`). Build failures and the engine's static
246
+ verification failures need a new publish after fixing the reported cause.
247
+
248
+ When a site's job has succeeded, check what it serves:
232
249
 
233
250
  ```
234
251
  verify_migration_urls site_id=<de-site> source_origin="https://example.de" check_source=true
@@ -296,6 +313,8 @@ new canonical build and is not an inventory-only operation):
296
313
  path; two markets both have `/kontakt`. One site, one inventory.
297
314
  - **Don't build all N sites in parallel from scratch.** Build one, approve,
298
315
  replicate. Parallel building multiplies every design mistake by N.
316
+ - **Don't publish the markets in parallel.** One shared build engine serves
317
+ the organization; publish site by site (Phase 7).
299
318
  - **Don't skip the parity check because coverage says 100%.** Coverage is a
300
319
  claim about the data; parity is a measurement of the server. They disagree
301
320
  exactly when it matters — an unpublished target page, a typo'd path, a
@@ -160,7 +160,9 @@ One `update_site_settings` call with every field you know:
160
160
  ```
161
161
 
162
162
  Read it back with `read_site_settings`. Google Fonts names are
163
- case-sensitive display names ("Plus Jakarta Sans", not "plus jakarta").
163
+ case-sensitive display names ("Plus Jakarta Sans", not "plus jakarta"). Fonts
164
+ are self-hosted on the site by default; a name Google does not serve falls
165
+ back silently, so verify fonts on the published address (`tr-brand` Step 7).
164
166
 
165
167
  **Site icons are part of brand setup** — upload favicon (32–64px), a
166
168
  180×180 apple touch icon, and a 192×192 app icon; set `favicon`,
@@ -161,9 +161,15 @@ bulk_replace_text pattern="OldCo" replacement="NewCo" dry_run=false version="<br
161
161
  **Self-review is a multi-DIMENSION pass, not a glance — and most of it you
162
162
  MEASURE, not eyeball.** Structural checks (copy present, images return 200) are
163
163
  NOT a design review; never report "approved" off them. Don't just list the bugs
164
- you happened to notice — walk every dimension below on the DB-live preview
165
- (a reused `get_preview_link`; browser tool + DOM reads), fix what you find,
166
- reload, re-check. No re-deploy between fixes — the preview renders from the DB.
164
+ you happened to notice — walk every dimension below. Use the DB-live preview
165
+ (a reused `get_preview_link`) for content and layout review: fix, reload,
166
+ re-check, no re-deploy between fixes. It is a sandboxed, opaque-origin frame
167
+ served from the portal, so DOM/computed-style reads from your browser tool are
168
+ limited, third-party iframes usually stay empty, and consent, analytics,
169
+ cookies and font loading cannot be verified there (`?consent=all|necessary`
170
+ only pre-answers the banner). Run the measured checks, embeds, consent,
171
+ analytics and fonts on the published temporary/test address of the branch
172
+ after an authorized publish.
167
173
 
168
174
  **Use `tr-design-review` (`read_skill tr-design-review`) for the HOW** — it has
169
175
  the per-dimension measurement snippets (overflow ladder, computed contrast, touch
@@ -241,7 +247,7 @@ link for your own verification:
241
247
  details is for your own one-off checks (a new hash each deploy).
242
248
 
243
249
  Default: review/iterate on the reused DB-live preview; deploy only for the
244
- compiled output or merge. Wait for an explicit "looks good, ship it."
250
+ compiled output, the published-address checks in step 6, or merge. Wait for an explicit "looks good, ship it."
245
251
 
246
252
  ### 7. Merge + deploy
247
253
 
@@ -201,6 +201,17 @@ Menus use matching SVG symbols; `close_size_px` overrides the close symbol size
201
201
  without shrinking the44px hit area. Compare exact threshold boundaries in real
202
202
  preview and static output before deleting corrective CSS.
203
203
 
204
+ ## Constrained containers in a column (render version 12)
205
+
206
+ From render version 12 (Core 0.2.132), a `core/container` with width narrow,
207
+ normal or wide inside a container whose direction is column at every
208
+ breakpoint fills the column up to its maximum width, centered. Below 12, auto
209
+ margins shrink it to its content, so setting width `wide` does NOT make a card
210
+ fill a column. Read `read_site_settings` → `render.version` before advising;
211
+ when it is below 12, raise it with `tr-upgrade-rendering` instead of working
212
+ around it in content. Rows, and containers that become rows at some
213
+ breakpoint, are unchanged.
214
+
204
215
  ## Native image framing and body heading scales (Core 0.2.35+)
205
216
 
206
217
  Use `core/image.scale_percent` (100–200, responsive) for deliberate horizontal
@@ -60,6 +60,14 @@ comparison first.
60
60
  `version` to do it on a branch first). The preview changes at once; the
61
61
  live site changes at the next deploy. To undo, write the previous number.
62
62
 
63
+ ## Platform fixes behind a newer version
64
+
65
+ When a layout defect is fixed in a newer render version (for example version
66
+ 12: constrained containers fill column layouts), the fix is to raise
67
+ `render_version` through steps 3–8 — compare, report, upgrade with approval,
68
+ then publish. Do not work around it in content (extra wrappers, widths,
69
+ paddings or custom CSS); those workarounds outlive the fix and fight it.
70
+
63
71
  ## Pitfalls
64
72
 
65
73
  - Comparing a draft against saved content shows content edits, not
@@ -3,10 +3,16 @@
3
3
  - Confirm Organization, Site, Version and deployed/source identity.
4
4
  - Run `python3 scripts/typeroll/check_unsaved.py` before trigger_deploy. Review
5
5
  and explicitly save authorized Pages and partials; templates save immediately.
6
+ - Publish one Site at a time per Organization (shared build engine): start the
7
+ next only when the previous job finished and read_organization_build_engine
8
+ shows no active_jobs.
6
9
  - Compare preserved text with capture_text.cjs + compare_text.py and an explicit
7
10
  selector. Check intentional additions, source comments and images separately.
8
11
  - Inspect desktop/mobile previews and actual hosted output around breakpoints.
9
12
  Use shootframe.cjs / shoot_hosted.cjs and inspect full screenshots, not only tiles.
13
+ - Use the signed preview for content/layout review only: it is a sandboxed,
14
+ opaque-origin frame. Verify third-party embeds, consent, analytics, cookies,
15
+ fonts and measured design checks on the published test address.
10
16
  - Exercise menus, keyboard focus, current links, forms and enabled app states.
11
17
  Use card-stacking-check.cjs for whole-card excerpt/media/whitespace targets.
12
18
  - Check actual contrast on light/dark/photo backgrounds, image subjects and
@@ -46,6 +52,9 @@ indexing have distinct effects but do not require repeated approval if covered.
46
52
  print files, private archive manifest, verified copies and a restore test.
47
53
  - [ ] Review ICC-converted derivatives visually; preserve originals separately.
48
54
  - [ ] Verify Analytics configuration and actual beacon/cookie behavior after build.
55
+ - [ ] Fonts on the published address: `/_typeroll/fonts/fonts.css` has `@font-face`
56
+ rules for each chosen family, every font file returns 200, no request goes to
57
+ fonts.googleapis.com/fonts.gstatic.com, and `document.fonts` shows each family loaded.
49
58
  - [ ] Read domain publication intent before deployment; distinguish test-host
50
59
  updates, future-host candidates and explicit traffic approval.
51
60
  - [ ] Inspect the frozen candidate's noindex/robots output, not only current CMS settings.
@@ -1,11 +1,35 @@
1
1
  # Platform feedback
2
2
 
3
- Record site-specific observations here, without credentials or private exports.
3
+ Record Typeroll product defects and gaps here, without credentials or private
4
+ exports. Distinguish bugs, missing capabilities, unclear instructions and
5
+ project choices; project choices belong in decisions.md.
4
6
 
5
- | ID | Priority | Surface / version | Observation | Evidence | Workaround | Status |
6
- | --- | --- | --- | --- | --- | --- | --- |
7
+ Index:
7
8
 
8
- For each finding record expected versus actual behavior, minimal reproduction,
9
- Site/Version, page/block locator, relevant schema, tested widths, source revision
10
- and whether it blocks launch. Distinguish bugs, capabilities, instructions and
11
- project choices. Link upstream fixes; verify deployed output before closing them.
9
+ | ID | Site / Organization | Surface | Summary | Blocks launch | Status |
10
+ | --- | --- | --- | --- | --- | --- |
11
+
12
+ One entry per finding:
13
+
14
+ ```markdown
15
+ ## DE-03 — <short title>
16
+
17
+ - Site / Organization / Version: <names and IDs>
18
+ - Core / MCP version: <from /api/version or get_site_capabilities>
19
+ - Tool or surface: <e.g. trigger_deploy, get_preview_link, core/container>
20
+ - Observed: <what happened, with exact error codes, job IDs, URLs, widths and
21
+ timestamps; minimal reproduction; expected versus actual>
22
+ - Impact: <what is blocked or wrong, and for whom>
23
+ - Ask: <the product change requested>
24
+ - Acceptance criteria: <checks that prove it is fixed>
25
+ - Workaround: <none, or what was done and how to remove it later>
26
+ - Status: open | fixed in <version> | verified <date, version, result>
27
+ ```
28
+
29
+ Prefix IDs per site or market (for example DE-03) and never reuse one.
30
+
31
+ After reporting a defect that affects publishing, do not resume the affected
32
+ publishing until the fix is confirmed live: check `/api/version` or the release
33
+ note for the fixed version. Then retest against the acceptance criteria and
34
+ record the result and version under Status. Verify deployed output before
35
+ closing a finding.
@@ -12,6 +12,10 @@ starting points, not permission to overwrite an approved design.
12
12
  - Sections own gutters. Do not erase side padding with `padding: 3rem 0` when
13
13
  only vertical spacing is intended. Wrap column-slot contents in a flow
14
14
  container with article rhythm when they need vertical block spacing.
15
+ - A narrow/normal/wide container inside an always-column container fills the
16
+ column only from render version 12; below it, it shrinks to its content
17
+ (width `wide` does not help). Raise render_version with tr-upgrade-rendering
18
+ instead of working around it in content.
15
19
  - Give cards one clear destination. Use whole_card_link without action_label
16
20
  or download actions. Check excerpt, image, whitespace and keyboard activation.
17
21
  - Do not fade body text with opacity. Verify actual text/background contrast,
@@ -55,6 +59,12 @@ schema: counts, widths and breakpoints are not spacing steps. Pixel values are
55
59
  explicit exceptions, not the default authoring method. Configure scales in
56
60
  `design_scales`, then reuse them in role styles and blocks.
57
61
 
62
+ Use exact Google Fonts family names. Published sites self-host them by default
63
+ (`fonts.delivery: self_hosted`); a name Google does not serve falls back
64
+ silently, and the preview loads fonts from Google, so check fonts on the
65
+ published address (qa/checklist.md). `delivery: google` sends each visitor's IP
66
+ address to Google: a privacy decision for the owner.
67
+
58
68
  Record deliberate exceptions in `decisions.md`, with source/block, reason and
59
69
  review criteria (for example white text on a photograph). Run `lint_design_tokens`
60
70
  before launch and after major design changes. It warns, never blocks launch.
@@ -75,6 +75,15 @@ A deploy with working copies returns `unsaved_content`; review the targets.
75
75
  Save explicitly or obtain approval to publish saved content only using
76
76
  `allow_unsaved: true`. Never silently save somebody else's work.
77
77
 
78
+ All Sites in an Organization share one build engine. Run trigger_deploy for one
79
+ Site at a time: start the next only when the previous job is succeeded/failed
80
+ and read_organization_build_engine shows no active_jobs. `build_result_missing`
81
+ means a runner never ran the task (not a content fault): publish again once the
82
+ engine is idle. retry_deploy_verification only retries public verification of an
83
+ already-served publication (`publication_observation_timeout`,
84
+ `publication_cache_refresh_*`); build and engine-side static verification
85
+ failures need a new publish.
86
+
78
87
  Keep the bound Organization/Site/Version explicit. A site API key cannot switch
79
88
  to another Organization: use an authorized OAuth connection or a separate key.
80
89
  Read the schema for target envelopes and pagination; do not guess aliases.