kaloko 0.17.0 → 0.19.0

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.
Files changed (44) hide show
  1. package/README.md +7 -0
  2. package/package.json +1 -1
  3. package/schemas/config.v1.json +51 -1
  4. package/schemas/run.v1.json +64 -1
  5. package/schemas/scenario.v1.json +81 -14
  6. package/skills/kaloko/SKILL.md +4 -0
  7. package/skills/kaloko/reference/acceptance.md +18 -4
  8. package/skills/kaloko/reference/ci.md +83 -0
  9. package/skills/kaloko/reference/evaluation.md +44 -1
  10. package/skills/kaloko/reference/integrations.md +6 -0
  11. package/skills/kaloko/reference/mcp.md +2 -0
  12. package/skills/kaloko/reference/stacks.md +104 -14
  13. package/src/cli.mjs +186 -34
  14. package/src/lib/build.mjs +18 -7
  15. package/src/lib/capture.mjs +31 -9
  16. package/src/lib/changelog-core.mjs +19 -1
  17. package/src/lib/ci.mjs +4 -2
  18. package/src/lib/config.mjs +15 -1
  19. package/src/lib/crawler.mjs +242 -0
  20. package/src/lib/draft.mjs +2 -0
  21. package/src/lib/evaluate.mjs +124 -38
  22. package/src/lib/findings.mjs +73 -0
  23. package/src/lib/git.mjs +14 -1
  24. package/src/lib/health-core.mjs +28 -5
  25. package/src/lib/health-index.mjs +187 -0
  26. package/src/lib/health.mjs +4 -0
  27. package/src/lib/jev.mjs +34 -14
  28. package/src/lib/linkcheck.mjs +59 -22
  29. package/src/lib/local-evaluator.mjs +270 -0
  30. package/src/lib/manifest-core.mjs +38 -12
  31. package/src/lib/pack-help.mjs +24 -0
  32. package/src/lib/packs.mjs +41 -3
  33. package/src/lib/pii.mjs +2 -0
  34. package/src/lib/pr.mjs +5 -1
  35. package/src/lib/probe.mjs +62 -1
  36. package/src/lib/recheck.mjs +5 -1
  37. package/src/lib/scenario.mjs +11 -3
  38. package/src/lib/seo-core.mjs +372 -0
  39. package/src/lib/seo-init.mjs +272 -0
  40. package/src/lib/service.mjs +31 -2
  41. package/src/lib/site.mjs +362 -0
  42. package/src/lib/stack.mjs +230 -23
  43. package/src/lib/upload.mjs +3 -2
  44. package/viewer/index.html +40 -19
@@ -17,6 +17,15 @@ sorting, thumbnails and CSV export, and each page opens in the lightbox.
17
17
  per page, and export the failing ones for the content team.
18
18
  - A redesign or migration: walk the old URL list against the new site.
19
19
 
20
+ For a weekly SEO and answer-engine monitor of a public site, start with
21
+ `npx kaloko init --seo <site>`: it reads robots.txt and the sitemaps, finds
22
+ the languages and the biggest content families and writes
23
+ `qa/flows/seo-<site>.yml` with one rotating stack per family (`match` per
24
+ language, packs seo and aeo). Read the comment at the top of the file:
25
+ families it paired across languages by size are guesses to check. Then
26
+ `kaloko validate`, `kaloko walk --limit 20`, and
27
+ `kaloko schedule export github`. See help/how-to/seo-monitor.md.
28
+
20
29
  Not for a flow of different screens: those stay separate steps. A stack
21
30
  step does not click through anything. It opens each page by URL. Use a
22
31
  step script only to prepare each page, for example to dismiss a dialog
@@ -31,10 +40,13 @@ step script only to prepare each page, for example to dismiss a dialog
31
40
  purpose: Every article reads well and is findable.
32
41
  packs: [seo] # health checks of every page (advisory)
33
42
  items:
34
- sitemap: /sitemap.xml # or urls / file / crawl / script (exactly one)
43
+ sitemap: /sitemap.xml # or robots: true / urls / file / crawl / script (exactly one)
35
44
  match: '^/clanky/' # regex on the path with its query; exclude: '…' drops pages
45
+ # or a map by locale: { default: '^/blog/', fi: '^/fi/blogi/' }
36
46
  limit: 800 # default 500, at most 10000
37
- semantic_sample: 20 # semantic criteria on the first 20 pages only
47
+ # rotate: week # each ISO week the next `limit` pages (run: each run)
48
+ semantic_sample: 20 # semantic criteria on 20 pages only
49
+ # semantic_order: priority # failing and least recently checked pages first
38
50
  # viewports: [desktop] # default: the first viewport of the scenario
39
51
  fields: # index columns (max 12); default title, h1, words, http
40
52
  - { id: title, label: Title, from: title }
@@ -55,12 +67,43 @@ Item sources:
55
67
  - `file: qa/data/products.csv` is a list next to the scenario. Accepted
56
68
  formats are text with one path per line, CSV (first column), or JSON:
57
69
  `["/a"]` or `[{ path, title, key, purpose }]`, where `key` gives a stable id.
58
- - `sitemap: /sitemap.xml` also follows a sitemap index (up to 50 sitemaps).
70
+ - `sitemap: /sitemap.xml` also follows a sitemap index, two levels deep
71
+ (up to 50 sitemaps each). Gzipped sitemaps work: a `.gz` URL or gzip
72
+ bytes behind any URL are unpacked.
73
+ - `sitemap: robots` (or `robots: true`) reads the `Sitemap:` lines of
74
+ `/robots.txt` on the environment's origin. When their URLs name the
75
+ walked locale as a segment (`/sitemaps/de/sitemap.xml.gz`,
76
+ `sitemap-de.xml`; `en_US` finds `en-us`, `en` does not), only that
77
+ locale's sitemaps are read; otherwise all of them. Use it when a site
78
+ lists one sitemap per language, so the scenario does not hard-code paths.
59
79
  - `crawl: { from: /clanky, links: "a.card", next: "a.next", pages: 20 }`
60
80
  collects links from a listing and follows its pagination.
61
81
  - `script: qa/steps/items.mjs` uses the default export
62
- `({ page, env, locale, url }) => [paths | { path, title, key, purpose }]` for
63
- anything else, such as an API or a search.
82
+ `({ page, env, locale, url, node, params }) => [paths | { path, title, key, purpose }]`
83
+ for anything else, such as an API or a search. `node` is
84
+ `{ id, title, lane, lane_title, session }` of the step and `params` is
85
+ `items.params`, so one script can serve several stacks.
86
+
87
+ Fix handoff: a script or a JSON list may give each page `edit_url` (the
88
+ page in the CMS, http(s) only) and `content_ref` (its record id for a
89
+ content API). `resolve: steps/resolve.mjs` (default export
90
+ `async (item, { env, locale, node, params, url }) => ({ edit_url, content_ref })`)
91
+ looks them up for the walked pages only, 15 s per page; a page it fails on is walked
92
+ without them and the walk says how many. They show in the page detail
93
+ ("Edit in CMS"), the stack CSV, `get_stack_items` and
94
+ `kaloko export findings`.
95
+
96
+ `match` and `exclude` apply to every source before `limit` (and before
97
+ `rotate`). Either is a regex or a map by locale; the walked locale, then its
98
+ language (`en` for `en_US`), then `default` is used. A `match` map without
99
+ the locale and without `default` is an error.
100
+
101
+ `rotate: week` (or `rotate: run`) walks a big list in turns: each ISO week
102
+ (or each run) takes the next `limit` pages, wrapping around, so the list is
103
+ covered in `pages ÷ limit` rounds. `run` keeps its count and each page's last
104
+ check in `.cache/stack/` of the output folder, so use `week` on CI with a
105
+ fresh checkout. The stack index gets `rotation` per locale (window, covered,
106
+ never checked) and each page `last_checked` (its previous check, or null).
64
107
 
65
108
  An item's `purpose` (for example its title and meta description) is what the
66
109
  semantic evaluator reads as `screen_purpose` for that page instead of the
@@ -73,11 +116,44 @@ in the wrong language.
73
116
  Pages are deduplicated. External URLs are allowed (e.g. an old domain).
74
117
  `{locale}` in any source string is replaced by the walked locale. This is
75
118
  how one sitemap with every language is split:
76
- `match: '^/guides/.*lang={locale}'`, or `sitemap: /{locale}/sitemap.xml`.
119
+ `match: '^/guides/.*lang={locale}'`, or `sitemap: /{locale}/sitemap.xml`,
120
+ or `sitemap: /sitemaps/{locale}/sitemap.xml.gz`. One content family per
121
+ stack: the same per-locale sitemap with `match: '/eshop/'` in one step and
122
+ `match: '/(magazin|magazine|revista)/'` in another.
77
123
 
78
124
  Field sources: `title`, `h1`, `description`, `canonical`, `lang`, `url`,
79
125
  `path`, `words` (words in main/article/body), `http` (the navigation
80
- status), `count:<selector>`, `<selector>` (text) and `<selector>@<attr>`.
126
+ status), `jsonld_types` (the schema.org types of the page's JSON-LD, for
127
+ example `Product, Offer, BreadcrumbList`; `invalid×1` when a block does not
128
+ parse), a meta tag by name (`og:title`, `og:image`, `twitter:card`,
129
+ `meta:robots`), `count:<selector>`, `<selector>` (text) and
130
+ `<selector>@<attr>`.
131
+
132
+ Structured data is also summarised without a field: every capture records
133
+ `structured_data` (blocks, invalid blocks, types), and `items.json` carries
134
+ `structured_data` per page and, at the top, pages per type, pages without
135
+ structured data and pages with invalid JSON-LD. The `seo` pack checks it
136
+ (HC112 present, HC113 valid); `packs: [aeo]` adds the answer-engine signals
137
+ (HC901–HC905; HC905 `nojs_parity` fetches each page's raw HTML as GPTBot,
138
+ respecting robots.txt, and fails when the title, canonical, H1, JSON-LD or
139
+ most of the text appear only after JavaScript). See
140
+ help/reference/checks-and-packs.md.
141
+
142
+ With the `seo` pack a stack also compares its pages with each other after the
143
+ walk (HC121–HC126, advisory): shared titles, descriptions and H1s within a
144
+ language, canonicals pointing elsewhere or to error/noindex pages, hreflang
145
+ that the other walked version does not answer, and (sitemap or robots
146
+ sources) listed pages that answer non-200, redirect or say noindex. They are
147
+ health entries of the stack node (`cross_page: true`, `counts` in pages,
148
+ `urls`); `items.json` has them under `cross_page` with the groups, and each
149
+ affected page lists the ids in `cross_fails`. They cannot be adopted.
150
+ `packs: [locale]` (scenarios with `locales`) checks html lang, the own
151
+ hreflang, the text's language and price decimals against the walked locale
152
+ (HC1001–HC1004). `health: { site: true }` on the scenario adds site checks
153
+ once per run (robots.txt, sitemaps, llms.txt, http→https and www redirects;
154
+ HC1101–HC1107, `site.json` in the run, `site` in the manifest). Off the
155
+ `production` environment HC1102 is a warning and HC1103 is skipped;
156
+ `health.site.environments: [production]` runs them there only.
81
157
 
82
158
  A step `execution.script` still works. It is called with `item` in the
83
159
  context, opens `item.url` itself and may call `capture()`. Without a script,
@@ -86,19 +162,28 @@ the walk opens each page and captures it.
86
162
  ## Cost and size: choose deliberately
87
163
 
88
164
  - Deterministic criteria and packs run on every page. They are cheap.
89
- - Semantic criteria go to the evaluator. On a stack they run only on the
90
- first `semantic_sample` pages (default 20). The other pages show the
91
- criterion as not evaluated. Raise the sample only when the question
165
+ - Semantic criteria go to the evaluator. On a stack they run only on
166
+ `semantic_sample` pages (default 20), the first in walk order. With
167
+ `semantic_order: priority`, or an evaluator budget, pages failing a
168
+ deterministic acceptance criterion come first, then those never or least
169
+ recently checked. The other pages show the criterion as not evaluated. `evaluate.budget.calls`
170
+ caps the calls of a whole evaluation (evaluation.md). Raise the sample only when the question
92
171
  really differs between pages, and tell the person what it costs
93
172
  (one call per page and viewport).
94
173
  - Stacks capture the first screen of each page (fast, small). Set
95
174
  `capture: { full_page: true }` when the criteria are about the whole page.
96
175
  - Walking takes about 1–2 s per page. For a first look, use
97
176
  `kaloko walk --limit 50`. Re-walk chosen pages with
98
- `--items clanek-7,clanek-12` (matches the id or the path).
99
- - A shared run holds at most 20000 files and 300 MB. Each page stores about
100
- four files, so a stack of a few thousand first-screen pages fits. Split
101
- larger catalogues by section (`match`), one stack per section.
177
+ `--items clanek-7,clanek-12` (matches the id or the path; picked from
178
+ every matching page, also outside this week's rotation, and such a
179
+ partial walk leaves the rotation state alone).
180
+ - A shared run holds at most 20000 files and 300 MB as stored (HTML and JSON
181
+ travel gzip-compressed, about a tenth of their size on disk). Each page
182
+ stores about four files, so a stack of a few thousand first-screen pages
183
+ fits. For SEO crawls where the checks read the page, not the picture, set
184
+ `capture: { screenshot: thumb }`: only the 640 px thumbnail is kept. Split
185
+ larger catalogues by section (`match`) or walk a rotating sample
186
+ (`rotate: week`).
102
187
 
103
188
  ## Reading the results
104
189
 
@@ -118,6 +203,11 @@ the walk opens each page and captures it.
118
203
  `get_stack_items {run_id, step, status?, criterion?, query?, offset, limit}`
119
204
  lists pages with fields, failing criteria, evidence and screenshot URLs.
120
205
  This is the way to hand an agent "fix these 37 articles".
206
+ - `kaloko export findings [--format csv|json] [--locale l] [--step s] [--out file]`
207
+ writes the fix list of the local run: one row per failing criterion or
208
+ health check, page and locale, with URL, evidence, `edit_url`,
209
+ `content_ref` and `recheck` (`kaloko walk --steps s --items <page> --locales l`,
210
+ which re-walks just that page after the fix).
121
211
 
122
212
  ## Report
123
213