fix(seo): Wave 1 — fix critical SEO gaps

- Fix duplicate faq/ruFaq keys in dice.json, list.json, colors.json
- Add <main> landmarks to about/privacy pages (EN + RU)
- Move YandexRTB after <main> on homepage
- Add x-default hreflang to BaseLayout
- Configure sitemap i18n with hreflang alternates
- Add Open Graph + Twitter Card meta tags
- Create default OG image (1200x630)
This commit is contained in:
emil
2026-05-14 02:39:56 +03:00
parent 75038780d5
commit 43ea9bc8f3
23 changed files with 2087 additions and 146 deletions
+41 -134
View File
@@ -1,6 +1,6 @@
{
"schema_version": 2,
"active_work_id": "critical-refactor-f063daf0",
"active_work_id": "seo-blog-categories-9c7476b9",
"works": {
"critical-refactor-f063daf0": {
"work_id": "critical-refactor-f063daf0",
@@ -150,152 +150,59 @@
},
"ended_at": "2026-05-13T18:03:08.059Z",
"elapsed_ms": 5868793
},
"seo-blog-categories-9c7476b9": {
"work_id": "seo-blog-categories-9c7476b9",
"active_plan": "/home/emil/Desktop/Coding/AI/Randify.pro/.sisyphus/plans/seo-blog-categories.md",
"plan_name": "seo-blog-categories",
"status": "active",
"started_at": "2026-05-13T23:32:06.345Z",
"updated_at": "2026-05-13T23:36:41.704Z",
"session_ids": [
"ses_1de6ede59ffexUwt5C0sddEWwR"
],
"session_origins": {
"ses_1de6ede59ffexUwt5C0sddEWwR": "direct"
},
"agent": "atlas",
"task_sessions": {
"todo:1": {
"task_key": "todo:1",
"task_label": "1",
"task_title": "Fix duplicate JSON keys in 3 generator files",
"session_id": "ses_1dc4ed5ddffe54aNbdrrVGPkpI",
"agent": "Sisyphus-Junior",
"category": "quick",
"updated_at": "2026-05-13T23:36:41.704Z",
"started_at": "2026-05-13T23:36:41.704Z",
"status": "running"
}
}
}
},
"active_plan": "/home/emil/Desktop/Coding/AI/Randify.pro/.sisyphus/plans/critical-refactor.md",
"started_at": "2026-05-13T16:25:19.266Z",
"status": "completed",
"updated_at": "2026-05-13T18:03:08.059Z",
"active_plan": "/home/emil/Desktop/Coding/AI/Randify.pro/.sisyphus/plans/seo-blog-categories.md",
"started_at": "2026-05-13T23:32:06.345Z",
"status": "active",
"updated_at": "2026-05-13T23:36:41.704Z",
"session_ids": [
"ses_1de6ede59ffexUwt5C0sddEWwR"
],
"session_origins": {
"ses_1de6ede59ffexUwt5C0sddEWwR": "direct"
},
"plan_name": "critical-refactor",
"plan_name": "seo-blog-categories",
"task_sessions": {
"todo:1": {
"task_key": "todo:1",
"task_label": "1",
"task_title": "Add missing dependencies to package.json",
"session_id": "ses_1ddd57501ffeO4NlJDKb2pwLz2",
"task_title": "Fix duplicate JSON keys in 3 generator files",
"session_id": "ses_1dc4ed5ddffe54aNbdrrVGPkpI",
"agent": "Sisyphus-Junior",
"category": "writing",
"started_at": "2026-05-13T16:27:48.876Z",
"status": "completed",
"updated_at": "2026-05-13T16:33:52.668Z",
"ended_at": "2026-05-13T16:33:52.668Z",
"elapsed_ms": 363792
},
"todo:4": {
"task_key": "todo:4",
"task_label": "4",
"task_title": "Create ResultBox.astro shared component",
"session_id": "ses_1ddce8961ffeoaXaB2Rxgt684X",
"agent": "Sisyphus-Junior",
"category": "visual-engineering",
"updated_at": "2026-05-13T16:42:05.595Z",
"started_at": "2026-05-13T16:40:57.448Z",
"status": "completed",
"ended_at": "2026-05-13T16:42:05.595Z",
"elapsed_ms": 68147
},
"todo:6": {
"task_key": "todo:6",
"task_label": "6",
"task_title": "Refactor CardGenerator.astro",
"session_id": "ses_1ddc76889ffe240ehScl9taAza",
"agent": "Sisyphus-Junior",
"category": "unspecified-high",
"updated_at": "2026-05-13T16:47:10.433Z",
"started_at": "2026-05-13T16:44:57.812Z",
"status": "completed",
"ended_at": "2026-05-13T16:47:10.433Z",
"elapsed_ms": 132621
},
"todo:10": {
"task_key": "todo:10",
"task_label": "10",
"task_title": "Refactor DateGenerator.astro",
"session_id": "ses_1ddc380cbffe0YA1da3ew6QlNf",
"agent": "Sisyphus-Junior",
"category": "unspecified-high",
"updated_at": "2026-05-13T17:16:10.363Z",
"started_at": "2026-05-13T16:49:43.859Z",
"status": "completed",
"ended_at": "2026-05-13T17:16:10.363Z",
"elapsed_ms": 1586504
},
"todo:13": {
"task_key": "todo:13",
"task_label": "13",
"task_title": "Refactor GradientGenerator.astro",
"session_id": "ses_1dda85f21ffeEGeOWrbqmWAk02",
"agent": "Sisyphus-Junior",
"category": "unspecified-high",
"updated_at": "2026-05-13T17:22:16.123Z",
"started_at": "2026-05-13T17:19:45.020Z",
"status": "completed",
"ended_at": "2026-05-13T17:22:16.123Z",
"elapsed_ms": 151103
},
"todo:17": {
"task_key": "todo:17",
"task_label": "17",
"task_title": "Refactor LoremGenerator.astro",
"session_id": "ses_1dda37bf2ffexATCZ3W4Z6EDu1",
"agent": "Sisyphus-Junior",
"category": "unspecified-high",
"updated_at": "2026-05-13T17:25:32.173Z",
"started_at": "2026-05-13T17:25:11.269Z",
"status": "completed",
"ended_at": "2026-05-13T17:25:32.173Z",
"elapsed_ms": 20904
},
"todo:20": {
"task_key": "todo:20",
"task_label": "20",
"task_title": "Refactor MealGenerator.astro",
"session_id": "ses_1dda01e38ffeVc6AUwAlt7lQXl",
"agent": "Sisyphus-Junior",
"category": "unspecified-high",
"updated_at": "2026-05-13T17:31:02.031Z",
"started_at": "2026-05-13T17:30:26.005Z",
"status": "completed",
"ended_at": "2026-05-13T17:31:02.031Z",
"elapsed_ms": 36026
},
"todo:24": {
"task_key": "todo:24",
"task_label": "24",
"task_title": "Refactor PasswordGenerator.astro",
"session_id": "ses_1dd9b7bb1ffez3Q0aayg7PZUIF",
"agent": "Sisyphus-Junior",
"category": "unspecified-high",
"updated_at": "2026-05-13T17:34:08.166Z",
"started_at": "2026-05-13T17:32:34.652Z",
"status": "completed",
"ended_at": "2026-05-13T17:34:08.166Z",
"elapsed_ms": 93514
},
"todo:27": {
"task_key": "todo:27",
"task_label": "27",
"task_title": "Refactor TeamsGenerator.astro",
"session_id": "ses_1dd94b493ffemX2z2dEFdh8W7R",
"agent": "Sisyphus-Junior",
"category": "unspecified-high",
"started_at": "2026-05-13T17:37:23.881Z",
"status": "completed",
"updated_at": "2026-05-13T17:41:13.262Z",
"ended_at": "2026-05-13T17:41:13.262Z",
"elapsed_ms": 229381
},
"final-wave:f1": {
"task_key": "final-wave:f1",
"task_label": "F1",
"task_title": "**Plan Compliance Audit** — `oracle`",
"session_id": "ses_1dd81d9e1ffeclA0rP510AXfNB",
"agent": "Sisyphus-Junior",
"category": "deep",
"started_at": "2026-05-13T17:47:58.486Z",
"status": "completed",
"updated_at": "2026-05-13T18:02:13.341Z",
"ended_at": "2026-05-13T18:02:13.341Z",
"elapsed_ms": 854855
"category": "quick",
"updated_at": "2026-05-13T23:36:41.704Z",
"started_at": "2026-05-13T23:36:41.704Z",
"status": "running"
}
},
"agent": "atlas",
"ended_at": "2026-05-13T18:03:08.059Z",
"elapsed_ms": 5868793
"agent": "atlas"
}
+185
View File
@@ -0,0 +1,185 @@
> randify@0.0.1 build
> astro build
02:36:15 [types] Generated 342ms
02:36:15 [build] output: "static"
02:36:15 [build] directory: /home/emil/Desktop/Coding/AI/Randify.pro/dist/
02:36:15 [build] Collecting build info...
02:36:15 [build] ✓ Completed in 368ms.
02:36:15 [build] Building static entrypoints...
02:36:17 [vite] ✓ built in 2.10s
02:36:17 [build] ✓ Completed in 2.15s.
building client (vite)
02:36:17 [vite] transforming...
02:36:17 [vite] ✓ 71 modules transformed.
02:36:17 [vite] rendering chunks...
02:36:17 [vite] computing gzip size...
02:36:17 [vite] dist/_astro/hoisted.BKKrxAht.js 0.16 kB │ gzip: 0.15 kB
02:36:17 [vite] dist/_astro/animations.JKmio82t.js 0.19 kB │ gzip: 0.13 kB
02:36:17 [vite] dist/_astro/validation.plcuGzaj.js 0.20 kB │ gzip: 0.16 kB
02:36:17 [vite] dist/_astro/clipboard.v9BZgIeL.js 0.43 kB │ gzip: 0.22 kB
02:36:17 [vite] dist/_astro/random.PhfdigkC.js 0.50 kB │ gzip: 0.31 kB
02:36:17 [vite] dist/_astro/hoisted.B5m2ZYh6.js 0.90 kB │ gzip: 0.56 kB
02:36:17 [vite] dist/_astro/hoisted.CdG8uTdI.js 1.23 kB │ gzip: 0.61 kB
02:36:17 [vite] dist/_astro/hoisted.CT9HX5_Y.js 1.38 kB │ gzip: 0.72 kB
02:36:17 [vite] dist/_astro/hoisted.Cb7Tv2oN.js 1.48 kB │ gzip: 0.83 kB
02:36:17 [vite] dist/_astro/hoisted.D82rtlIG.js 1.54 kB │ gzip: 0.84 kB
02:36:17 [vite] dist/_astro/hoisted.uArMeWBD.js 1.63 kB │ gzip: 0.81 kB
02:36:17 [vite] dist/_astro/hoisted.CwDo-_-Z.js 1.64 kB │ gzip: 0.73 kB
02:36:17 [vite] dist/_astro/hoisted.j6ibv0wb.js 1.65 kB │ gzip: 0.81 kB
02:36:17 [vite] dist/_astro/hoisted.BEwWV8JC.js 1.73 kB │ gzip: 0.90 kB
02:36:17 [vite] dist/_astro/hoisted.6rLeMLDm.js 1.74 kB │ gzip: 0.86 kB
02:36:17 [vite] dist/_astro/hoisted.DOtqogeQ.js 1.76 kB │ gzip: 0.99 kB
02:36:17 [vite] dist/_astro/hoisted.CHG7AXVP.js 1.89 kB │ gzip: 1.05 kB
02:36:17 [vite] dist/_astro/hoisted.DnGtBYme.js 1.99 kB │ gzip: 0.95 kB
02:36:17 [vite] dist/_astro/hoisted.hrxZZFEF.js 2.19 kB │ gzip: 1.07 kB
02:36:17 [vite] dist/_astro/hoisted.CjZD7f2g.js 2.33 kB │ gzip: 1.15 kB
02:36:17 [vite] dist/_astro/hoisted.qLZXl1Do.js 2.88 kB │ gzip: 1.70 kB
02:36:17 [vite] dist/_astro/hoisted.BKosNXxl.js 2.91 kB │ gzip: 1.41 kB
02:36:17 [vite] dist/_astro/hoisted.CpSL7HYx.js 3.94 kB │ gzip: 1.95 kB
02:36:17 [vite] dist/_astro/hoisted.DmqdUAAB.js 4.10 kB │ gzip: 1.70 kB
02:36:17 [vite] dist/_astro/hoisted.COsV1Syq.js 4.30 kB │ gzip: 1.96 kB
02:36:17 [vite] dist/_astro/hoisted.Ftg8tUcf.js 4.53 kB │ gzip: 1.97 kB
02:36:17 [vite] dist/_astro/hoisted.3XDY-7Px.js 4.79 kB │ gzip: 1.97 kB
02:36:17 [vite] dist/_astro/hoisted.mRfAHLMS.js 5.27 kB │ gzip: 2.20 kB
02:36:17 [vite] dist/_astro/hoisted.B7vaoHN9.js 7.19 kB │ gzip: 2.54 kB
02:36:17 [vite] dist/_astro/hoisted.DU8tsC8l.js 9.71 kB │ gzip: 3.84 kB
02:36:17 [vite] dist/_astro/i18n.DYCg5-VL.js 10.76 kB │ gzip: 5.53 kB
02:36:17 [vite] dist/_astro/hoisted.ChvQAxmv.js 10.92 kB │ gzip: 3.89 kB
02:36:17 [vite] dist/_astro/hoisted.DdCnpAKh.js 11.83 kB │ gzip: 2.76 kB
02:36:17 [vite] dist/_astro/hoisted.CvjQ9xHL.js 35.55 kB │ gzip: 17.46 kB
02:36:17 [vite] dist/_astro/hoisted.Bdx021zL.js 45.26 kB │ gzip: 14.58 kB
02:36:17 [vite] ✓ built in 438ms
generating static routes
02:36:18 ▶ src/pages/about.astro
02:36:18 └─ /about/index.html (+11ms)
02:36:18 ▶ src/pages/generators/cards.astro
02:36:18 └─ /generators/cards/index.html (+6ms)
02:36:18 ▶ src/pages/generators/coin.astro
02:36:18 └─ /generators/coin/index.html (+4ms)
02:36:18 ▶ src/pages/generators/colors.astro
02:36:18 └─ /generators/colors/index.html (+3ms)
02:36:18 ▶ src/pages/generators/country.astro
02:36:18 └─ /generators/country/index.html (+4ms)
02:36:18 ▶ src/pages/generators/date.astro
02:36:18 └─ /generators/date/index.html (+2ms)
02:36:18 ▶ src/pages/generators/dice.astro
02:36:18 └─ /generators/dice/index.html (+3ms)
02:36:18 ▶ src/pages/generators/emoji.astro
02:36:18 └─ /generators/emoji/index.html (+3ms)
02:36:18 ▶ src/pages/generators/fontpair.astro
02:36:18 └─ /generators/fontpair/index.html (+3ms)
02:36:18 ▶ src/pages/generators/gradient.astro
02:36:18 └─ /generators/gradient/index.html (+2ms)
02:36:18 ▶ src/pages/generators/hash.astro
02:36:18 └─ /generators/hash/index.html (+2ms)
02:36:18 ▶ src/pages/generators/letter.astro
02:36:18 └─ /generators/letter/index.html (+2ms)
02:36:18 ▶ src/pages/generators/list.astro
02:36:18 └─ /generators/list/index.html (+2ms)
02:36:18 ▶ src/pages/generators/lorem.astro
02:36:18 └─ /generators/lorem/index.html (+2ms)
02:36:18 ▶ src/pages/generators/lottery.astro
02:36:18 └─ /generators/lottery/index.html (+2ms)
02:36:18 ▶ src/pages/generators/magic8ball.astro
02:36:18 └─ /generators/magic8ball/index.html (+2ms)
02:36:18 ▶ src/pages/generators/meal.astro
02:36:18 └─ /generators/meal/index.html (+2ms)
02:36:18 ▶ src/pages/generators/names.astro
02:36:18 └─ /generators/names/index.html (+3ms)
02:36:18 ▶ src/pages/generators/numbers.astro
02:36:18 └─ /generators/numbers/index.html (+2ms)
02:36:18 ▶ src/pages/generators/palette.astro
02:36:18 └─ /generators/palette/index.html (+2ms)
02:36:18 ▶ src/pages/generators/password.astro
02:36:18 └─ /generators/password/index.html (+2ms)
02:36:18 ▶ src/pages/generators/rps.astro
02:36:18 └─ /generators/rps/index.html (+2ms)
02:36:18 ▶ src/pages/generators/shuffler.astro
02:36:18 └─ /generators/shuffler/index.html (+2ms)
02:36:18 ▶ src/pages/generators/teams.astro
02:36:18 └─ /generators/teams/index.html (+2ms)
02:36:18 ▶ src/pages/generators/time.astro
02:36:18 └─ /generators/time/index.html (+2ms)
02:36:18 ▶ src/pages/generators/uuid.astro
02:36:18 └─ /generators/uuid/index.html (+2ms)
02:36:18 ▶ src/pages/generators/weighted.astro
02:36:18 └─ /generators/weighted/index.html (+2ms)
02:36:18 ▶ src/pages/generators/wheel.astro
02:36:18 └─ /generators/wheel/index.html (+2ms)
02:36:18 ▶ src/pages/generators/yesno.astro
02:36:18 └─ /generators/yesno/index.html (+2ms)
02:36:18 ▶ src/pages/privacy.astro
02:36:18 └─ /privacy/index.html (+2ms)
02:36:18 ▶ src/pages/ru/about.astro
02:36:18 └─ /ru/about/index.html (+3ms)
02:36:18 ▶ src/pages/ru/generators/cards.astro
02:36:18 └─ /ru/generators/cards/index.html (+3ms)
02:36:18 ▶ src/pages/ru/generators/coin.astro
02:36:18 └─ /ru/generators/coin/index.html (+2ms)
02:36:18 ▶ src/pages/ru/generators/colors.astro
02:36:18 └─ /ru/generators/colors/index.html (+3ms)
02:36:18 ▶ src/pages/ru/generators/country.astro
02:36:18 └─ /ru/generators/country/index.html (+2ms)
02:36:18 ▶ src/pages/ru/generators/date.astro
02:36:18 └─ /ru/generators/date/index.html (+2ms)
02:36:18 ▶ src/pages/ru/generators/dice.astro
02:36:18 └─ /ru/generators/dice/index.html (+3ms)
02:36:18 ▶ src/pages/ru/generators/emoji.astro
02:36:18 └─ /ru/generators/emoji/index.html (+2ms)
02:36:18 ▶ src/pages/ru/generators/fontpair.astro
02:36:18 └─ /ru/generators/fontpair/index.html (+2ms)
02:36:18 ▶ src/pages/ru/generators/gradient.astro
02:36:18 └─ /ru/generators/gradient/index.html (+2ms)
02:36:18 ▶ src/pages/ru/generators/hash.astro
02:36:18 └─ /ru/generators/hash/index.html (+2ms)
02:36:18 ▶ src/pages/ru/generators/letter.astro
02:36:18 └─ /ru/generators/letter/index.html (+2ms)
02:36:18 ▶ src/pages/ru/generators/list.astro
02:36:18 └─ /ru/generators/list/index.html (+2ms)
02:36:18 ▶ src/pages/ru/generators/lorem.astro
02:36:18 └─ /ru/generators/lorem/index.html (+2ms)
02:36:18 ▶ src/pages/ru/generators/lottery.astro
02:36:18 └─ /ru/generators/lottery/index.html (+2ms)
02:36:18 ▶ src/pages/ru/generators/magic8ball.astro
02:36:18 └─ /ru/generators/magic8ball/index.html (+2ms)
02:36:18 ▶ src/pages/ru/generators/meal.astro
02:36:18 └─ /ru/generators/meal/index.html (+3ms)
02:36:18 ▶ src/pages/ru/generators/names.astro
02:36:18 └─ /ru/generators/names/index.html (+2ms)
02:36:18 ▶ src/pages/ru/generators/numbers.astro
02:36:18 └─ /ru/generators/numbers/index.html (+2ms)
02:36:18 ▶ src/pages/ru/generators/palette.astro
02:36:18 └─ /ru/generators/palette/index.html (+2ms)
02:36:18 ▶ src/pages/ru/generators/password.astro
02:36:18 └─ /ru/generators/password/index.html (+2ms)
02:36:18 ▶ src/pages/ru/generators/rps.astro
02:36:18 └─ /ru/generators/rps/index.html (+2ms)
02:36:18 ▶ src/pages/ru/generators/shuffler.astro
02:36:18 └─ /ru/generators/shuffler/index.html (+2ms)
02:36:18 ▶ src/pages/ru/generators/teams.astro
02:36:18 └─ /ru/generators/teams/index.html (+2ms)
02:36:18 ▶ src/pages/ru/generators/time.astro
02:36:18 └─ /ru/generators/time/index.html (+2ms)
02:36:18 ▶ src/pages/ru/generators/uuid.astro
02:36:18 └─ /ru/generators/uuid/index.html (+2ms)
02:36:18 ▶ src/pages/ru/generators/weighted.astro
02:36:18 └─ /ru/generators/weighted/index.html (+2ms)
02:36:18 ▶ src/pages/ru/generators/wheel.astro
02:36:18 └─ /ru/generators/wheel/index.html (+2ms)
02:36:18 ▶ src/pages/ru/generators/yesno.astro
02:36:18 └─ /ru/generators/yesno/index.html (+2ms)
02:36:18 ▶ src/pages/ru/privacy.astro
02:36:18 └─ /ru/privacy/index.html (+2ms)
02:36:18 ▶ src/pages/ru/index.astro
02:36:18 └─ /ru/index.html (+4ms)
02:36:18 ▶ src/pages/index.astro
02:36:18 └─ /index.html (+3ms)
02:36:18 ✓ Completed in 381ms.
02:36:18 [@astrojs/sitemap] `sitemap-index.xml` created at `dist`
02:36:18 [build] 62 page(s) built in 3.40s
02:36:18 [build] Complete!
+16
View File
@@ -0,0 +1,16 @@
Task: Fix <main> landmarks on about/privacy pages
Files modified:
- src/pages/about.astro
- src/pages/ru/about.astro
- src/pages/privacy.astro
- src/pages/ru/privacy.astro
Verification:
- npm run lint: 0 errors
- npm run build: success
- grep dist for <main> on about/privacy pages: all 4 files contain <main> tag
Pattern applied:
- Breadcrumb nav and page header remain outside <main>
- Content sections wrapped in <main> following GeneratorLayout.astro pattern
+8
View File
@@ -0,0 +1,8 @@
TASK: Fix ad placement on homepage
FILE: src/pages/index.astro
CHANGE: Moved <YandexRTB /> from before <main> to after </main>
BUILD: PASS (npm run build exited 0)
VERIFICATION:
- dist/index.html: </main> at byte 43284
- dist/index.html: yandex_rtb at byte 43335
- Order correct: True (main content before ad block)
+11
View File
@@ -0,0 +1,11 @@
Task: Add x-default hreflang to BaseLayout
Date: 2026-05-14
Verification:
- npm run build: PASS (62 pages built)
- grep dist/index.html for x-default: FOUND
<link rel="alternate" hreflang="x-default" href="https://randify.pro/">
- grep dist/generators/dice/index.html for x-default: FOUND
- grep all dist/*.html for x-default: 62 matches across 62 files
All pages now include x-default hreflang fallback pointing to the English version.
+13
View File
@@ -0,0 +1,13 @@
# Task 5 Evidence: Sitemap i18n Config
## Build Status
PASS
## Verification
File: dist/sitemap-0.xml
Contains <xhtml:link rel="alternate" hreflang="en-US"> and <xhtml:link rel="alternate" hreflang="ru-RU"> entries for all bilingual pages.
## Sample Entries
- https://randify.pro/ -> en-US + ru-RU
- https://randify.pro/about/ -> en-US + ru-RU
- https://randify.pro/generators/coin/ -> en-US + ru-RU
+31
View File
@@ -0,0 +1,31 @@
Evidence: Open Graph and Twitter Card meta tags added successfully.
Build status: PASS (npm run build completed with exit 0)
--- dist/index.html ---
<meta property="og:site_name" content="Randify">
<meta property="og:locale" content="en_US">
<meta property="og:type" content="website">
<meta property="og:title" content="Randify — Random Value Generators">
<meta property="og:description" content="Simple tools for raffles, games, and everything that needs randomness.">
<meta property="og:url" content="https://randify.pro/">
<meta property="og:image" content="https://randify.pro/og-default.jpg">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Randify — Random Value Generators">
<meta name="twitter:description" content="Simple tools for raffles, games, and everything that needs randomness.">
<meta name="twitter:image" content="https://randify.pro/og-default.jpg">
--- dist/generators/dice/index.html ---
<meta property="og:site_name" content="Randify">
<meta property="og:locale" content="en_US">
<meta property="og:type" content="website">
<meta property="og:title" content="Dice Roller — d4 d6 d20 + Full Notation | D&D Dice | Randify">
<meta property="og:description" content="Free online dice roller with full XdY+Z notation, keep/drop (4d6dl1), exploding dice (3d6!), reroll, advantage/disadvantage, and roll history. Perfect for D&D, Pathfinder, and Savage Worlds.">
<meta property="og:url" content="https://randify.pro/generators/dice/">
<meta property="og:image" content="https://randify.pro/og-default.jpg">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Dice Roller — d4 d6 d20 + Full Notation | D&D Dice | Randify">
<meta name="twitter:description" content="Free online dice roller with full XdY+Z notation, keep/drop (4d6dl1), exploding dice (3d6!), reroll, advantage/disadvantage, and roll history. Perfect for D&D, Pathfinder, and Savage Worlds.">
<meta name="twitter:image" content="https://randify.pro/og-default.jpg">
All required tags present on both pages.
@@ -0,0 +1,10 @@
## Task 6: Open Graph + Twitter Card Meta Tags
- Added `ogImage?: string` prop to `BaseLayout.astro` Props interface.
- Implemented full OG tag set: `og:site_name`, `og:locale`, `og:type`, `og:title`, `og:description`, `og:url`, `og:image`.
- Implemented full Twitter Card tag set: `twitter:card` (summary_large_image), `twitter:title`, `twitter:description`, `twitter:image`.
- Default OG image (`/og-default.jpg`) is used when `ogImage` prop is omitted.
- `ogImageUrl` helper ensures absolute URLs by prefixing `https://randify.pro` to relative paths.
- Default OG image generated via Node.js + sharp (1200x630, dark background with accent radial gradients and "Randify" branding).
- Build passes; tags verified in `dist/index.html` and `dist/generators/dice/index.html`.
+200
View File
@@ -0,0 +1,200 @@
# Create AGENTS.md
## TL;DR
> Create `AGENTS.md` at repo root with compact, high-signal instructions for future OpenCode sessions. Replace stale `CLAUDE.md` claims. Verify all facts from executable sources.
>
> **Deliverables**: `AGENTS.md` (new file), committed and pushed to `main`
>
> **Estimated Effort**: Quick
> **Parallel Execution**: NO — single task
> **Critical Path**: Task 1 only
---
## Context
### Original Request
User: "обнови Agents.md" (Create or update `AGENTS.md` for this repository).
### Key Facts Discovered
- **Project**: Astro 4 + Tailwind CSS 4 static site, 28 random-value generators (bilingual EN/RU)
- **Deploy**: GitHub Actions + rsync to reg.ru on push to `main`
- **CI gate order**: `npm ci` → `npm run lint` → `npm run test` → `npm run build` → `rsync`
- **i18n**: Astro built-in routing, `defaultLocale: "en"`, `prefixDefaultLocale: false`. EN at `src/pages/`, RU at `src/pages/ru/`. Locale derived from `Astro.currentLocale`.
- **CRITICAL quirk**: Never use shared dynamic `[slug].astro` for RU pages — Astro bundles all imported component scripts, causing every RU page to run all 28 generator scripts.
- **Generators**: 28 live. Each requires 4 files: content JSON (EN+RU fields), component, EN page, RU page.
- **Content schema**: Zod `.strict()` in `src/lib/generator-schema.ts` — unknown fields fail build.
- **Shared utilities**: `src/lib/client/random.ts` (crypto `getRandomValues`), `src/lib/client/i18n.ts` (client locale helpers). All 28 generators refactored to use these (zero `Math.random`).
- **Dice engine**: deduplicated into `src/lib/dice-engine.ts`, imported by `DiceGenerator.astro`.
- **Test runner**: Vitest v4, `environment: "node"`, `include: ["src/**/*.test.ts"]`.
- **Lint**: ESLint via `eslint.config.mjs`, ignores `dist/`, `.astro/`, `node_modules/`, `src/env.d.ts`.
- **CLAUDE.md is stale**: claims "10 generators" and "No test runner or linter is configured" — both false.
- **No existing agent instruction files**: no `.cursorrules`, `.cursor/rules/`, `.github/copilot-instructions.md`, `opencode.json`.
### Metis Review
- No gaps identified — repo answers all important questions.
---
## Work Objectives
### Core Objective
Write a compact `AGENTS.md` at repo root containing only high-signal, repo-specific guidance that future OpenCode agents would likely miss.
### Concrete Deliverables
- `AGENTS.md` file at repo root
### Definition of Done
- [ ] `AGENTS.md` exists at repo root
- [ ] `npm run lint` passes (0 errors)
- [ ] `npm run build` passes (62 pages)
### Must Have
- Exact developer commands
- CI gate order
- i18n routing quirks and the `[slug].astro` prohibition
- Generator addition workflow (4 steps)
- Zod strict schema warning
- Shared utilities architecture (random.ts, i18n.ts, dice-engine.ts)
- Stale CLAUDE.md warnings
### Must NOT Have
- Generic software advice
- Long tutorials or exhaustive file trees
- Obvious language conventions
- Speculative claims
---
## Verification Strategy
### Test Decision
- **Infrastructure exists**: YES
- **Automated tests**: NO (not needed for doc file)
- **Agent-Executed QA**: YES — verify lint and build pass after file creation
### QA Policy
Every task MUST include agent-executed QA scenarios.
---
## Execution Strategy
### Wave 1 (Single Task)
- **Task 1**: Write `AGENTS.md`
---
## TODOs
- [ ] 1. Write AGENTS.md
**What to do**:
- Create `/home/emil/Desktop/Coding/AI/Randify.pro/AGENTS.md` with the following verified content:
- Project overview (28 generators, Astro 4 + Tailwind 4)
- Developer commands (exact npm scripts)
- CI gate order
- i18n routing architecture and `[slug].astro` prohibition
- Path alias `@/* → src/*`
- Shared utilities table (random.ts, i18n.ts, dice-engine.ts)
- Shared components (ResultBox.astro, CopyButton.astro)
- Adding a generator: 4-step workflow
- Key conventions (accent color, icons, analytics IDs)
- Stale CLAUDE.md warnings
- Testing, lint & format notes
- Build artifacts and operational gotchas
- Use compact bullets, not prose paragraphs.
- Verify no generic advice included.
**Must NOT do**:
- Do NOT include generic software advice
- Do NOT write long tutorials
- Do NOT include obvious conventions
- Do NOT make speculative claims
- Do NOT touch `CLAUDE.md` (leave it as-is; AGENTS.md is the new source of truth)
**Recommended Agent Profile**:
- **Category**: `quick`
- **Skills**: []
**Parallelization**:
- **Can Run In Parallel**: NO
- **Parallel Group**: Sequential
- **Blocks**: None
- **Blocked By**: None
**References**:
- `CLAUDE.md` — existing guidance (partially stale, use for comparison only)
- `package.json` — exact npm scripts
- `astro.config.mjs` — i18n routing config
- `vitest.config.ts` — test config
- `eslint.config.mjs` — lint rules and ignores
- `.github/workflows/deploy.yml` — CI gate order
- `src/lib/generator-schema.ts` — Zod strict schema
- `src/lib/client/random.ts` — crypto random utility
- `src/lib/client/i18n.ts` — client i18n helper
- `src/lib/dice-engine.ts` — canonical dice engine
**Acceptance Criteria**:
- [ ] `AGENTS.md` exists at `/home/emil/Desktop/Coding/AI/Randify.pro/AGENTS.md`
- [ ] `npm run lint` → PASS (0 errors)
- [ ] `npm run build` → PASS (62 pages generated)
**QA Scenarios**:
```
Scenario: File created and lint passes
Tool: Bash
Preconditions: None
Steps:
1. Read AGENTS.md and verify it contains: "28 random-value generators", "CI gate order", "[slug].astro" warning, "Zod .strict()" warning, shared utilities table
2. Run: npm run lint
3. Assert: 0 errors
Expected Result: lint exits 0, no errors
Evidence: .sisyphus/evidence/agents-lint.txt
Scenario: Build passes after file creation
Tool: Bash
Preconditions: None
Steps:
1. Run: npm run build
2. Assert: "62 pages" in output, exit code 0
Expected Result: 62 pages generated, exit code 0
Evidence: .sisyphus/evidence/agents-build.txt
```
**Evidence to Capture**:
- [ ] `.sisyphus/evidence/agents-lint.txt`
- [ ] `.sisyphus/evidence/agents-build.txt`
**Commit**: YES
- Message: `docs: add AGENTS.md with high-signal repo guidance`
- Files: `AGENTS.md`
- Pre-commit: `npm run lint && npm run build`
---
## Commit Strategy
- **1**: `docs: add AGENTS.md with high-signal repo guidance` — AGENTS.md, npm run lint && npm run build
---
## Success Criteria
### Verification Commands
```bash
npm run lint # Expected: 0 errors
npm run build # Expected: 62 pages
```
### Final Checklist
- [ ] AGENTS.md exists at repo root
- [ ] Contains 28 generators count (not 10)
- [ ] Contains exact CI gate order
- [ ] Contains [slug].astro prohibition
- [ ] Contains Zod strict warning
- [ ] Lint passes
- [ ] Build passes
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,10 @@
{
"sessionID": "ses_1dca376a3ffewdLYKY0T5iMPdZ",
"updatedAt": "2026-05-13T22:04:09.510Z",
"sources": {
"background-task": {
"state": "idle",
"updatedAt": "2026-05-13T22:04:09.510Z"
}
}
}
@@ -1,10 +1,10 @@
{
"sessionID": "ses_1de6ede59ffexUwt5C0sddEWwR",
"updatedAt": "2026-05-13T13:53:55.077Z",
"updatedAt": "2026-05-13T22:03:12.991Z",
"sources": {
"background-task": {
"state": "idle",
"updatedAt": "2026-05-13T13:53:55.077Z"
"updatedAt": "2026-05-13T22:03:12.991Z"
}
}
}
+9 -1
View File
@@ -4,7 +4,15 @@ import sitemap from "@astrojs/sitemap";
export default defineConfig({
site: "https://randify.pro",
integrations: [sitemap()],
integrations: [sitemap({
i18n: {
defaultLocale: "en",
locales: {
en: "en-US",
ru: "ru-RU",
},
},
})],
vite: {
plugins: [tailwindcss()],
},
Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

+8 -2
View File
@@ -26,12 +26,18 @@
"faq": [
{ "q": "What color formats are supported?", "a": "HEX, RGB, and HSL. All three are shown at once, so you can copy the one you need." },
{ "q": "Can I use the generated color in CSS?", "a": "Yes. Copy the HEX or RGB value and paste it directly into your stylesheet or design tool." },
{ "q": "Are the colors truly random?", "a": "Yes. Each color is generated using a cryptographically secure random number generator, covering the entire color space." }
{ "q": "Are the colors truly random?", "a": "Yes. Each color is generated using a cryptographically secure random number generator, covering the entire color space." },
{ "q": "What color formats are supported?", "a": "HEX, RGB, and HSL. All three are shown simultaneously for every generated color." },
{ "q": "Can I copy just one format?", "a": "Yes. Click the copy icon next to any format to copy only that value." },
{ "q": "Are the colors truly random?", "a": "Yes. Each color is generated using random values for hue, saturation, and lightness." }
],
"ruFaq": [
{ "q": "Какие форматы цветов поддерживаются?", "a": "HEX, RGB и HSL. Все три формата отображаются одновременно — скопируйте тот, который нужен." },
{ "q": "Можно ли использовать сгенерированный цвет в CSS?", "a": "Да. Скопируйте значение HEX или RGB и вставьте его прямо в стили или графический редактор." },
{ "q": "Цвета действительно случайные?", "a": "Да. Каждый цвет генерируется с помощью криптографически стойкого генератора случайных чисел, покрывая всё цветовое пространство." }
{ "q": "Цвета действительно случайные?", "a": "Да. Каждый цвет генерируется с помощью криптографически стойкого генератора случайных чисел, покрывая всё цветовое пространство." },
{ "q": "Какие форматы цветов поддерживаются?", "a": "HEX, RGB и HSL. Все три отображаются одновременно для каждого сгенерированного цвета." },
{ "q": "Можно скопировать только один формат?", "a": "Да. Нажмите иконку копирования рядом с нужным форматом." },
{ "q": "Цвета действительно случайные?", "a": "Да. Каждый цвет генерируется из случайных значений оттенка, насыщенности и освещённости." }
],
"ruHowTo": [
"Нажмите «Сгенерировать», чтобы получить случайный цвет.",
+8 -2
View File
@@ -29,12 +29,18 @@
"faq": [
{ "q": "What dice notation can I use?", "a": "You can use standard XdY+Z notation, plus modifiers like advantage, disadvantage, keep/drop (kh/kl/dl/dh), and exploding dice (!)." },
{ "q": "Is this fair for D&D and other RPGs?", "a": "Yes. Randify uses the browser's cryptographically secure random number generator, so every roll is unbiased." },
{ "q": "Can I roll multiple dice at once?", "a": "Absolutely. Enter something like 5d6 or click the die buttons and set the count to roll up to 20 dice in a single throw." }
{ "q": "Can I roll multiple dice at once?", "a": "Absolutely. Enter something like 5d6 or click the die buttons and set the count to roll up to 20 dice in a single throw." },
{ "q": "What dice notation is supported?", "a": "Standard XdY+Z, advantage/disadvantage, keep/drop (kh/kl/dh/dl), reroll (r), and exploding dice (!)." },
{ "q": "Can I roll more than one type of die at once?", "a": "Yes. Enter multiple notations separated by spaces, like 2d6 1d20." },
{ "q": "Is the roll history saved?", "a": "Yes. Your last rolls are shown below the results until you refresh the page." }
],
"ruFaq": [
{ "q": "Какую нотацию кубиков поддерживает генератор?", "a": "Стандартную нотацию XdY+Z, а также модификаторы: преимущество, помеха, keep/drop (kh/kl/dl/dh) и взрывающиеся кубики (!)." },
{ "q": "Подходит ли генератор для D&D и других НРИ?", "a": "Да. Randify использует криптографически стойкий генератор случайных чисел браузера, поэтому каждый бросок честный и непредвзятый." },
{ "q": "Можно ли бросать несколько кубиков одновременно?", "a": "Конечно. Введите что-то вроде 5d6 или настройте кубики вручную — можно бросить до 20 кубиков за раз." }
{ "q": "Можно ли бросать несколько кубиков одновременно?", "a": "Конечно. Введите что-то вроде 5d6 или настройте кубики вручную — можно бросить до 20 кубиков за раз." },
{ "q": "Какая нотация кубиков поддерживается?", "a": "Стандартная XdY+Z, преимущество/помеха, keep/drop (kh/kl/dh/dl), переброс (r) и взрывающиеся кубики (!)." },
{ "q": "Можно ли бросить несколько типов кубиков сразу?", "a": "Да. Введите несколько нотаций через пробел, например 2d6 1d20." },
{ "q": "История бросков сохраняется?", "a": "Да. Последние броски отображаются под результатами до обновления страницы." }
],
"ruHowTo": [
"Введите нотацию, например 2d6+3 или 1d20 — или настройте кубики вручную.",
+8 -2
View File
@@ -27,12 +27,18 @@
"faq": [
{ "q": "Can the same item be picked more than once?", "a": "Only if you enable the 'Allow duplicates' toggle. By default, each item can be chosen only once per draw." },
{ "q": "Is there a limit to how many items I can add?", "a": "You can add up to 1,000 items. For most use cases — giveaways, raffles, or team assignments — this is more than enough." },
{ "q": "Can I pick multiple winners at once?", "a": "Yes. Set the 'Pick' field to any number up to the total count of items, and Randify will select that many winners in one go." }
{ "q": "Can I pick multiple winners at once?", "a": "Yes. Set the 'Pick' field to any number up to the total count of items, and Randify will select that many winners in one go." },
{ "q": "Can the same item be picked more than once?", "a": "Only if you toggle Allow duplicates. By default, each item is picked at most once." },
{ "q": "Is there a limit to how many items I can add?", "a": "You can add up to 500 items, and pick up to the total number of unique items." },
{ "q": "Does the order of items matter?", "a": "No. The selection is completely random regardless of the order you enter items." }
],
"ruFaq": [
{ "q": "Может ли один и тот же элемент выпасть несколько раз?", "a": "Только если включён переключатель «Разрешить повторения». По умолчанию каждый элемент выбирается не более одного раза." },
{ "q": "Есть ли ограничение на количество элементов в списке?", "a": "Можно добавить до 1 000 элементов. Для розыгрышей, жеребьёвок и распределения задач этого более чем достаточно." },
{ "q": "Можно ли выбрать несколько победителей сразу?", "a": "Да. Укажите в поле «Выбрать» любое число до общего количества элементов — Randify выберет нужное количество победителей за один раз." }
{ "q": "Можно ли выбрать несколько победителей сразу?", "a": "Да. Укажите в поле «Выбрать» любое число до общего количества элементов — Randify выберет нужное количество победителей за один раз." },
{ "q": "Может ли один элемент выпасть несколько раз?", "a": "Только если включена опция «Разрешить повторения». По умолчанию каждый элемент выбирается не более одного раза." },
{ "q": "Есть ли ограничение на количество элементов?", "a": "Можно добавить до 500 элементов и выбрать до общего числа уникальных элементов." },
{ "q": "Имеет ли значение порядок элементов?", "a": "Нет. Выбор полностью случайный независимо от порядка ввода." }
],
"ruHowTo": [
"Введите список элементов — по одному на строку.",
+16 -1
View File
@@ -9,12 +9,15 @@ import type { Lang } from "../i18n/translations";
interface Props {
title?: string;
description?: string;
ogImage?: string;
}
const lang = (Astro.currentLocale as Lang) || "en";
const T = useT(lang);
const { title = T.defaultTitle, description = T.defaultDesc } = Astro.props;
const { title = T.defaultTitle, description = T.defaultDesc, ogImage = "/og-default.jpg" } = Astro.props;
const ogImageUrl = ogImage.startsWith("http") ? ogImage : `https://randify.pro${ogImage}`;
const pathWithoutLocale = Astro.url.pathname.replace(/^\/ru/, "") || "/";
const enUrl = `https://randify.pro${getRelativeLocaleUrl("en", pathWithoutLocale)}`;
@@ -48,6 +51,7 @@ const webPageSchema = {
<link rel="canonical" href={canonicalUrl} />
<link rel="alternate" hreflang="en" href={enUrl} />
<link rel="alternate" hreflang="ru" href={ruUrl} />
<link rel="alternate" hreflang="x-default" href={enUrl} />
<meta name="verification" content="er9ndnv9ih7agmh8" />
<script
type="application/ld+json"
@@ -61,6 +65,17 @@ const webPageSchema = {
<script src="https://yandex.ru/ads/system/context.js" async></script>
<slot name="head" />
<title>{title}</title>
<meta property="og:site_name" content="Randify" />
<meta property="og:locale" content={lang === "ru" ? "ru_RU" : "en_US"} />
<meta property="og:type" content="website" />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:url" content={canonicalUrl} />
<meta property="og:image" content={ogImageUrl} />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={title} />
<meta name="twitter:description" content={description} />
<meta name="twitter:image" content={ogImageUrl} />
<Analytics />
<script>
if ("serviceWorker" in navigator) {
+2
View File
@@ -59,6 +59,7 @@ const aboutSchema = {
<div class="w-16 h-0.5 bg-accent/60 rounded-full mt-4" aria-hidden="true"></div>
</header>
<main>
<div class="border-t border-zinc-800/60 pt-8 mt-8 space-y-8">
<section>
<h2 class="text-lg font-semibold text-zinc-100 mb-2">Mission</h2>
@@ -91,5 +92,6 @@ const aboutSchema = {
</a>
</section>
</div>
</main>
</div>
</BaseLayout>
+2 -2
View File
@@ -47,8 +47,6 @@ const webSiteSchema = {
</p>
</header>
<YandexRTB />
<main class="mt-8">
<div
class="grid gap-3"
@@ -65,5 +63,7 @@ const webSiteSchema = {
}
</div>
</main>
<YandexRTB />
</div>
</BaseLayout>
+2
View File
@@ -49,6 +49,7 @@ const T = useT(lang);
<div class="w-16 h-0.5 bg-accent/60 rounded-full mt-4" aria-hidden="true"></div>
</header>
<main>
<div class="border-t border-zinc-800/60 pt-8 mt-8 space-y-8">
<section>
<p class="text-zinc-400 leading-relaxed">{T.privacyIntro}</p>
@@ -81,5 +82,6 @@ const T = useT(lang);
<p class="text-zinc-400 leading-relaxed">{T.privacyChanges}</p>
</section>
</div>
</main>
</div>
</BaseLayout>
+2
View File
@@ -59,6 +59,7 @@ const aboutSchema = {
<div class="w-16 h-0.5 bg-accent/60 rounded-full mt-4" aria-hidden="true"></div>
</header>
<main>
<div class="border-t border-zinc-800/60 pt-8 mt-8 space-y-8">
<section>
<h2 class="text-lg font-semibold text-zinc-100 mb-2">Миссия</h2>
@@ -91,5 +92,6 @@ const aboutSchema = {
</a>
</section>
</div>
</main>
</div>
</BaseLayout>
+2
View File
@@ -49,6 +49,7 @@ const T = useT(lang);
<div class="w-16 h-0.5 bg-accent/60 rounded-full mt-4" aria-hidden="true"></div>
</header>
<main>
<div class="border-t border-zinc-800/60 pt-8 mt-8 space-y-8">
<section>
<p class="text-zinc-400 leading-relaxed">{T.privacyIntro}</p>
@@ -81,5 +82,6 @@ const T = useT(lang);
<p class="text-zinc-400 leading-relaxed">{T.privacyChanges}</p>
</section>
</div>
</main>
</div>
</BaseLayout>