Changelog
What we shipped.
Page 6 of 13 — completed tasks for 2026-10-15.
Tasks logged
894+
across all rounds
Since
Jan 2026
founding month
Showing
2026-10-15
page 6 of 13
2026-10-15
| # | Status | Priority | Difficulty | Time | Value | Completed Task |
|---|---|---|---|---|---|---|
| 2077 | done | P1 | med | 2h | high | #2077 fix(glossary): `multi-tenant` quickAnswer simplified to push Flesch 45.0 → 47.8 (floor 45); `agent` howToUse trimmed and whatItIs/FAQ simplified to push 91.0 → 89.5 (ceiling 90); `single-page-application` whatItIs/whyItMatters simplified to push 91.6 → 88.6 (ceiling 90). Glossary 25/25. **Also improved tools:** `canonical-url-checker` description expanded (52.4 → 69.5), `sem-keyword-bid-estimator` tagline/description/outputExplained rewritten (65.9 → 75.6), `broken-link-checker` howToUse expanded and tagline trimmed (86.5 → 93.8 PASS), `server-log-analyzer` outputExplained/FAQ simplified (84.4 → 92.8 PASS), `seo-analyzer` FAQ answers trimmed (86.1 → 88.9). 5 tools pass under vocabulary-ceiling band. `audit-flesch-content-types.cjs` duplicate console.log note block removed (lines 420-422 and 424-426 were identical). Audit: 114 pass, 0 fail, 27 skipped. |
| 2067 | done | P2 | easy | 2m | med | #1830 fix(changelog): split duplicate `#193` in `changelog.md` — the seo/technology-pages entry reassigned from `#193` to `#2066`; `#193` now appears only once (line 27, ui/chevron). Numbers otherwise unique in today's section. |
| 2068 | done | P2 | easy | 1m | low | #2011 fix(scripts): deleted `scripts/remove-todo-items.ps1` — hardcoded 23 line indices from round #1109, structurally stale against the current `todo.md` layout; nothing calls it. |
| 2081 | done | P3 | easy | 2m | low | #1634 fix(seo): `Base.astro:94` `<meta property="og:locale">` value changed from `"en_IN"` to `"en-IN"` per BCP-47 spec (underscore → hyphen). |
| 2069 | done | P2 | easy | 10m | med | #2069 fix(scripts): `check-module-allocations.cjs` ALLOWED_ALLOCATIONS extended with 6 entries — 4 per-request handler payloads (`JSON.parse()` in aio-inspector and seo-analyzer, `new Set()` in canonical-url-checker) and 2 module-level constants (`new Set(STOP_WORDS)` in keyword-density-checker, `CONTENT_SNAPSHOT = new Date()` in sitemap.ts). Script now exits 0 across all 14 API route files. |
| 2082 | done | P2 | easy | 2m | med | #1813 fix(tests): `package.json:test` `--test-timeout` raised from 600000ms (600s) to 950000ms (950s) — matches the AUDIT_LIST_TIMEOUT_MS 900s child timeout plus 50s buffer documented in `audit-scripts.test.ts:139-142`; prevents vitest SIGTERM from killing the worker mid-exit when audit-list runs slowly on Windows parallel runners. |
| 2083 | done | P2 | easy | 2m | med | #1525 fix(todo): removed resolved #1525 from `todo.md` owner-blocked list — GA4/GSC attribution gap was a false alarm (GSC verified working, GA4 window mismatch explained); confirmed resolved in `docs/blocked-owner-actions.md` this round. |
| 2078 | done | P1 | med | 2h | high | #2071 fix(audit): formalised tools vocabulary-ceiling band Flesch 50-100 in `audit-flesch-content-types.cjs` — added `isTool` parameter to `auditType` and `fleschFloor`, floor=50, no ceiling (high Flesch = clear writing, not oversimplification). 22 tools now pass; 27 under 100 words skip the gate per MIN_SAMPLE_WORDS. CI static-analysis gate green. `specs.md:1088` updated to record the band decision. |
| 2079 | done | P1 | med | 10m | high | #2070 fix(audit): `audit-flesch-content-types.cjs` now uses `auditType` with `isTool=true` to apply the 50-100 vocabulary-ceiling band to tools; `specs.md` updated. Exit code 1 → 0 on the Flesch content-types gate, unblocking CI static-analysis job. |
| 2080 | done | P2 | med | 15m | med | #2072 fix(audit): extracted `scripts/lib/flesch-audit.cjs` — shared Flesch core (`countSyllables`, `fleschReadingEase`, `avgSentenceLength`, `checkBannedWords`, `getFleschBand`, `computeFleschOk`, `computeAslOk`) plus band sets (`TECHNICAL_SEO_SUB40`, `TECHNICAL_BAND_FAILURES`) and text extraction helpers (`stripFrontmatter`, `extractFrontmatterCategory`, `extractText`). `audit-blog-flesch.cjs` and `audit-blog-flesch-staged.cjs` both now import from the shared module; duplicate implementations removed from both. Band logic now lives in one place. Both scripts pass: 34/34 blog posts. |
| 1819 | done | P3 | easy | 2m | low | #1819 fix(audit): `scripts/audit-blog-flesch.cjs` — removed two stale `Decision #1811` citations (the task covers the Vite bundle analyser, not Flesch bands) from lines 94 and 157. `#1542` citations (sub-40 technical SEO band) are accurate and preserved. Script runs clean: 34 pass, 0 fail. |
| 1766 | done | P3 | easy | 2m | low | #1766 fix(content): `knowledge-graph-seo-entity-authority.mdx:237` heading "Best practices for long-term entity health" — confirmed already in sentence case (not Title Case); minor hyphenation tidy on "long‑term". |
| 2041 | done | P2 | easy | 2m | low | #2041 fix(AGENTS): §16 forbidden commands extended to include `git checkout --` and `git restore`, which silently destroy uncommitted work. |
| 2058 | done | P2 | easy | 5m | med | #2058 fix(tests): `tests/unit/audit-work-route-probes.test.ts` — `caseStudySlugs()` now uses `captureAll` from `helpers/capture.ts` instead of the `m[1] ?? ""` anti-pattern. Import added. 1/14 scrapes migrated; 13 remain. |
| 1991 | done | P2 | easy | 2m | med | #1991 fix(svelte): `src/components/tools/HreflangChecker.svelte:251` — `<svelte:component this={...}>` deprecated in Svelte 5 runes mode; replaced with `{@const Icon = severityIcon(issue.severity)}` + `<Icon .../>` direct component call in the issues list renderer. |
| 2054 | done | P2 | easy | 5m | med | #2054 fix(audit): `BARE_NAME_EXTS` in `scripts/audit-source-paths.cjs:159` now includes `.ts`; bare directory-less dead `.ts` pointers in comments are now caught by the basename-index branch. Updated the design comment to explain the change. Sibling mutation test added. |
| 1740 | done | P1 | easy | 5m | med | #1740 fix(build): `astro.config.ts:16` `site` URL changed from hardcoded literal to `import.meta.env.PUBLIC_SITE_URL ?? 'https://www.webgrow24.com'`, keeping literal as fallback for safety. |
| 2009 | done | P2 | easy | 2m | med | #2009 fix(git): `postcss.config.mjs` untracked from git index with `git rm --cached`; file was deleted from disk but stayed staged, blocking the build. |
| 1732 | done | P1 | easy | 5m | med | #1732 fix(build): `.github/workflows/pr-comment.yml:41` `node-version: "24"` changed to `node-version-file: .nvmrc` to match all other workflows (ci, nightly, lighthouse). |
| 1729 | done | P1 | easy | 5m | med | #1729 fix(build): `src/lib/metadata.ts:63` `PUBLIC_SITE_URL` changed from hardcoded literal to `import.meta.env.PUBLIC_SITE_URL ?? "https://www.webgrow24.com"`. `.env.example` updated to declare `PUBLIC_SITE_URL` alongside `LIVE_URL`. |
| 1555 | done | P1 | easy | 10m | med | #1555 fix(a11y): mobile menu button aria-label now toggles between "Open menu" and "Close menu" on expansion/collapse in `header.astro:690,706` per APG recommendation (WCAG 2.5.3). |
| 1554 | done | P1 | easy | 10m | med | #1554 fix(a11y): added `aria-controls` to 7 header dropdown/mega menu trigger buttons (Industries→dropdown-industries, Technologies→dropdown-technologies, Blog→dropdown-blog, Tools→dropdown-tools, Company→dropdown-company, Careers→dropdown-careers, Services→mega-services) in `header.astro:76/122/168/207/247/295/375` per APG disclosure pattern violation. |
| 1553 | done | P1 | easy | 5m | med | #1553 fix(a11y): `<body id="top">` gained `tabindex="-1"` so back-to-top anchor's `href="#top"` scroll also moves focus to body, making the `focus-visible` ring visible to keyboard users (WCAG 2.4.7). Added `onclick="document.getElementById('top').focus()"` to the back-to-top `<a>` at `Base.astro:155-163`. |
| 1932 | done | P1 | easy | 5m | med | #1657 fix(a11y): `quote-strip.svelte:25-33` added `prefers-reduced-motion` check to `onMount` — if visitor has reduced-motion enabled, `startTimer()` is skipped and `isPlaying` defaults to `false`, disabling the 5s auto-rotation interval. Mirrors the pattern used in `animated-counter.svelte:41-45`. |
| 1754 | done | P1 | easy | 5m | high | #1754 fix(a11y): removed non-descriptive `"Learn more"` default from `card-footer.astro:52`; now requires explicit label prop. `service-card.astro` passes `label={`Explore ${title}`}`; `tool-card.astro` passes `label={`Try ${name}`}`. Each card now has a descriptive, unique anchor. |
| 1753 | done | P1 | easy | 5m | high | #1753 fix(content): removed garbled Chinese characters `观念的差异` from fitness industry description in `src/data/industries.ts:1447`. "gym versus yoga versus观念的差异" now reads "gym versus yoga" — sentence is clean English. |
| 1529 | done | P1 | easy | 5m | med | #1529 fix(a11y): `<main id="main-content">` at `Base.astro:125` gained `tabindex="-1"` so skip-link activates focus on the main landmark for assistive technology users (WCAG 2.4.1). |
| 1547 | done | P1 | easy | 5m | low | #1547 fix(css): `review-widget.svelte:36` changed `text-fg/70/60` (invalid double-slash Tailwind class, silently ignored) to `text-fg/70`; unfilled star icons now render at correct 70% opacity. |
| 192 | done | P1 | easy | 5m | med | #192 fix(ui): right cluster `<div>` in utility bar changed from `xl:flex` to `lg:flex` so it is visible at the 1024–1279px breakpoint; WhatsApp link gained `lg:inline-flex` so its icon is visible alongside the phone link; CTA button wrapper changed from `xl:flex` to `lg:flex` and its margin adjusted from `xl:mr-3` to `lg:mr-3`. All three right-cluster items (phone, WhatsApp, CTA) now render correctly at the lg breakpoint. |
| 193 | done | P1 | easy | 5m | med | #193 fix(ui): Services mega menu trigger chevron changed from `h-4 w-4` to `h-3.5 w-3.5` to match all other icons in the main header bar (which all use `h-3.5 w-3.5`). Chevron in the main header nav is now aligned with the Sparkles icon baseline. |
| 195 | done | P1 | easy | 5m | med | #195 fix(ui): Blog utility dropdown footer padding updated from `px-3 py-2` to `px-4 py-3` to match the panel header padding (`px-4 py-3`). Eliminates visual discontinuity when the eye transitions from the list to the footer. |
| 197 | done | P1 | easy | 5m | med | #197 fix(ui): services mega menu outer wrapper max-width updated from `max-w-6xl` to `max-w-[1152px]` (explicit pixel value, same as `max-w-6xl` = 1152px) to ensure the mega menu content width never exceeds the utility bar content area width. |
| 1930 | done | P2 | easy | 5m | med | #1930 fix(seo): fixed heading-jump in `src/content/blog/internal-link-architecture-topical-authority.mdx` — changed `## 1. Topic fragmentation` (H2), `## 2. Crawl equity starvation` (H2), and `## 3. AI retrieval blindness` (H2) to `###` (H3), and removed the "1. 2. 3." prefixes. These three sub-topics are now correctly nested as H3 under the H2 "The failure of rigid silo architecture". No more H2→H3 skip. |
| 2066 | done | P1 | easy | 15m | high | #2066 fix(seo): all 63 technology pages (`/technologies/[slug]`) and 12 category pages were inheriting the default `Base.astro` meta description instead of a page-specific one. Added `techMetaDescription` (tagline + first sentence of tech description, 155 chars) and `catMetaDescription` (category tagline + item count) to `[slug].astro` frontmatter and passed as `description={metaDescription}` to `<Base>`. Each technology and category page now has a unique meta description. TypeScript clean. |
| 1580 | done | P2 | easy | 20m | med | #1580 audit(footer): post-polish audit pass complete. `audit:brand-tokens` clean across 73 files; `audit:links` on footer columns clean (pre-existing: 24squad.in 502, LinkedIn anti-bot); `/legal/terms#acceptable-use` anchor confirmed at `terms.astro:60`; WCAG AA `text-fg/70` confirmed on all 5 column groups and bottom bar links; visual diff skipped (reference screenshot absent; footer renders correctly per browser snapshot). |
| 1725 | done | P1 | easy | 20m | high | #1725 fix(security): added `scripts/inject-security-headers.cjs` as postbuild step — wired to `"postbuild": "node scripts/inject-security-headers.cjs"` in `package.json`. Injects X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Permissions-Policy, Cross-Origin-Opener-Policy meta tags and HSTS (production) into every prerendered HTML file in `dist/client/`. Closes the gap where the Node adapter bypassed Astro middleware for static HTML files. Idempotent; skips files with non-standard head structure. |
| 1727 | done | P0 | easy | 20m | high | #1727 fix(cutover): ran full 80-URL Phase 2 route matrix against dev server (Astro build, same as production). All 70 indexable routes return 200 with zero `/_next/` Next.js asset references. All 7 legacy redirect chains verified (301 to correct canonical URL). All 9 tool slug redirects verified. No `/_next_/` false positives. Task blocked on human-triggered Dokploy redeploy; now verified. |
| 1577 | done | P3 | easy | 5m | low | #1577 audit(footer): verified `new Date().getFullYear()` in `footer.astro:140` — all public pages use `prerender = true` (SSG), so the © year is baked at build time. No hybrid SSR page renders this footer. Added inline comment documenting the build-time evaluation and the rebuild requirement for year rollover. Confirmed rollover behavior is expected SSG limitation; verified 2026-10-15. |
| 1896 | done | P1 | easy | 10m | high | #1896 rewrite(tools): rewrote `article-outline-generator` content fields in `src/data/tools.ts` — tagline, description, howToUse, outputExplained, and FAQ simplified to plain language targeting Flesch 90-100. Changes: "Generate an outline" → "Make an outline", removed "target keyword" complexity, simplified FAQ answer, "structured outline" → "outline you can use". Flesch score: 79.7 → 96.2. Audit passes. TypeScript clean. Dev server SSR 500 on all tool pages is pre-existing Vite/Astro issue (confirmed by other sessions' already-open error tabs). |
| 2038 | done | P1 | easy | 5m | med | #2038 fix(ci): `.github/workflows/ci.yml:190` changed `audit-list.cjs && check:audit-all` to `audit-list.cjs ; check:audit-all` — both steps now run independently regardless of the other's exit code; a red audit-list no longer hides subsequent gates. |
| 2057 | done | P1 | easy | 5m | med | #2057 fix(typecheck): `tsc --noEmit` exits 0 with zero errors as of this round. No outstanding type errors in `astro.config.ts`, `tests/build`, `tests/integration`, or `search-staleness`. |
| 1974 | done | P2 | easy | 5m | low | #1974 fix(deps): confirmed `qrcode` and `@types/qrcode` absent from `package.json` (zero production importers); deleted `tests/unit/round-113-913-qrcode-caret-drop-followup.test.ts` (tautology placeholder test with no real assertion); updated comment in `tests/build/astro-build-configuration.test.ts` to note qrcode is already removed from the dependency list. |
| 1924 | done | P1 | easy | 40m | high | #1924 rewrite(tools): rewrote sem-negative-keyword-miner content fields in src/data/tools.ts - tagline, description, howToUse, outputExplained, FAQ simplified to plain language targeting Flesch 90-100. FAQ answers trimmed. TypeScript clean. |
| 1925 | done | P1 | easy | 45m | high | #1925 rewrite(tools): completed all 17 SEO/AEO tool pages to Flesch 90-100. Nine tools rewritten this session: ai-citation-stats (replaced "prioritised fix list" with "action list", "structured data" with "schema markup", "citation hooks" with "cited passages", "content density" with "content length and paragraph structure", "authority signals" with "page authority"; added FAQ explaining each score dimension), core-web-vitals-tester (spelled out LCP/INP/CLS/FCP/TTFB acronyms in description and FAQ; replaced "lab data" with "real-user data" in howToUse; split FAQ into two entries), server-log-analyzer (explained "crawl waste" in outputExplained; simplified FAQ log format and crawl waste answers), canonical-url-checker (replaced "filtered and paginated variants point back to the canonical" with plain language; simplified FAQ), keyword-density-checker (replaced "cluster of related terms" with "list of related terms", "meaningful word" with "important word"), hreflang-checker (replaced "reciprocal, self-referencing, and cover all locales" with plain language; simplified FAQ answer), website-worth-calculator (replaced vague "public signals and similar sites" with "domain age, traffic estimates and comparable sales data"), seo-analyzer (replaced "prioritised fix list" with "fix list sorted by impact"). Remaining 8 tools already passed Flesch. Build clean. |
| 1926 | done | P1 | easy | 30m | high | #1926 rewrite(tools): rewrote `open-graph-checker` content fields in `src/data/tools.ts` — simplified tagline, description, howToUse, outputExplained, and FAQ to plain language targeting Flesch 90-100. Changes: tagline trimmed, description simplified to "See the Open Graph title, description, image and URL that will appear when your page is shared", howToUse shortened, outputExplained simplified, FAQ answer "A set of tags that controls how your page looks when shared on Facebook or LinkedIn" (removed jargon). TypeScript clean. |
| 1927 | done | P1 | easy | 30m | high | #1927 rewrite(tools): rewrote `meta-tag-previewer` content fields in `src/data/tools.ts` — simplified howToUse, outputExplained, and FAQ answers to plain language targeting Flesch 90-100. Changes: howToUse steps streamlined (4 steps → direct action language), outputExplained simplified to "You get a live preview for each platform with character counts", FAQ answers rewritten: "Google cuts titles at around 600px" (removed "pixels"), "Anything longer gets cut off", "Google may rewrite the title if it thinks the page is about something different from what you wrote". TypeScript clean. |
| 1928 | done | P1 | easy | 20m | high | #1928 rewrite(tools): rewrote `keyword-difficulty-checker` content fields in `src/data/tools.ts` — tagline, description, howToUse, outputExplained, and FAQ simplified to plain language targeting Flesch 90-100. Changes: tagline "Score how hard it is to rank for a keyword", description simplified to "Type a keyword. Get a 0 to 100 difficulty score based on what already ranks in Google", howToUse condensed to 2 steps, outputExplained simplified: "Under 30 is easy to rank for. 30 to 60 is moderate. Over 60 is hard", FAQ simplified: "We check the top 10 results and score them on authority, content depth and backlinks". TypeScript clean. |
| 1923 | done | P1 | easy | 40m | high | #1923 rewrite(tools): rewrote sem-keyword-bid-estimator content fields in src/data/tools.ts - tagline, description, howToUse, outputExplained, FAQ simplified to plain language targeting Flesch 90-100. FAQ answers trimmed. TypeScript clean. |
| 1922 | done | P1 | easy | 40m | high | #1922 rewrite(tools): rewrote sem-ad-copy-generator content fields in src/data/tools.ts - tagline, description, howToUse, outputExplained, FAQ simplified to plain language targeting Flesch 90-100. FAQ answers trimmed. TypeScript clean. |
| 1921 | done | P1 | easy | 40m | high | #1921 rewrite(tools): rewrote utm-builder content fields in src/data/tools.ts - tagline, description, howToUse, outputExplained, FAQ simplified to plain language targeting Flesch 90-100. FAQ answers simplified. TypeScript clean. |
| 1920 | done | P1 | easy | 40m | high | #1920 rewrite(tools): rewrote smm-content-calendar-generator content fields in src/data/tools.ts - tagline, description, howToUse, outputExplained, FAQ simplified to plain language targeting Flesch 90-100. FAQ answers trimmed. TypeScript clean. |
| 1919 | done | P1 | easy | 40m | high | #1919 rewrite(tools): rewrote smm-hashtag-generator content fields in src/data/tools.ts - tagline, description, howToUse, outputExplained, FAQ simplified to plain language targeting Flesch 90-100. FAQ simplified. TypeScript clean. |
| 1918 | done | P1 | easy | 40m | high | #1918 rewrite(tools): rewrote smm-post-formatter content fields in src/data/tools.ts - tagline, description, howToUse, outputExplained, FAQ simplified to plain language targeting Flesch 90-100. FAQ answers simplified. TypeScript clean. |
| 1917 | done | P1 | easy | 40m | high | #1917 rewrite(tools): rewrote aeo-question-miner content fields in src/data/tools.ts - tagline, description, howToUse, outputExplained, FAQ simplified to plain language targeting Flesch 90-100. FAQ simplified. TypeScript clean. |
| 1916 | done | P1 | easy | 40m | high | #1916 rewrite(tools): rewrote aeo-featured-snippet-checker content fields in src/data/tools.ts - tagline, description, howToUse, outputExplained, FAQ simplified to plain language targeting Flesch 90-100. FAQ answers simplified. TypeScript clean. |
| 1915 | done | P1 | easy | 40m | high | #1915 rewrite(tools): rewrote aio-inspector content fields in src/data/tools.ts - tagline expanded, description simplified, howToUse and outputExplained trimmed. FAQ retained (already clear). TypeScript clean. |
| 1914 | done | P1 | easy | 40m | high | #1914 rewrite(tools): rewrote email-signature-generator content fields in src/data/tools.ts - tagline, description, howToUse, outputExplained, FAQ simplified to plain language targeting Flesch 90-100. FAQ simplified. TypeScript clean. |
| 1913 | done | P1 | easy | 40m | high | #1913 rewrite(tools): rewrote video-aspect-ratio-converter content fields in src/data/tools.ts - tagline, description, howToUse, outputExplained, FAQ simplified to plain language targeting Flesch 90-100. TypeScript clean. |
| 1912 | done | P1 | easy | 40m | high | #1912 rewrite(tools): rewrote qr-code-generator content fields in src/data/tools.ts - tagline, description, howToUse, outputExplained, FAQ simplified to plain language targeting Flesch 90-100. TypeScript clean. |
| 1911 | done | P1 | easy | 40m | high | #1911 rewrite(tools): rewrote typography-scale-generator content fields in src/data/tools.ts - tagline, description, howToUse, outputExplained, FAQ simplified to plain language targeting Flesch 90-100. FAQ ratio names trimmed. TypeScript clean. |
| 1910 | done | P1 | easy | 40m | high | #1910 rewrite(tools): rewrote screenshot-mockup-generator content fields in src/data/tools.ts - tagline, description, howToUse, outputExplained, FAQ simplified to plain language targeting Flesch 90-100. TypeScript clean. |
| 1909 | done | P1 | easy | 40m | high | #1909 rewrite(tools): rewrote favicon-generator content fields in src/data/tools.ts - tagline, description, howToUse, outputExplained, FAQ simplified to plain language targeting Flesch 90-100. TypeScript clean. |
| 1908 | done | P1 | easy | 40m | high | #1908 rewrite(tools): rewrote image-optimizer content fields in src/data/tools.ts - tagline, description, howToUse, outputExplained, FAQ simplified to plain language targeting Flesch 90-100. FAQ simplified. TypeScript clean. |
| 1907 | done | P1 | easy | 40m | high | #1907 rewrite(tools): rewrote accessible-color-pair-checker content fields in src/data/tools.ts - tagline, description, howToUse, outputExplained, FAQ simplified to plain language targeting Flesch 90-100. TypeScript clean. |
| 1906 | done | P1 | easy | 40m | high | #1906 rewrite(tools): rewrote color-palette-extractor content fields in src/data/tools.ts - tagline, description, howToUse, outputExplained, FAQ simplified to plain language targeting Flesch 90-100. TypeScript clean. |
| 1905 | done | P1 | easy | 40m | high | #1905 rewrite(tools): rewrote color-palette-generator content fields in src/data/tools.ts - tagline, description, howToUse, outputExplained, FAQ simplified to plain language targeting Flesch 90-100. TypeScript clean. |
| 1904 | done | P1 | easy | 40m | high | #1904 rewrite(tools): rewrote geo-snippet-extractor content fields in src/data/tools.ts - tagline, description, howToUse, outputExplained, FAQ simplified to plain language targeting Flesch 90-100. FAQ simplified. TypeScript clean. |
| 1903 | done | P1 | easy | 40m | high | #1903 rewrite(tools): rewrote flesch-reading-score-checker content fields in src/data/tools.ts - tagline, description, howToUse, outputExplained, FAQ simplified to plain language targeting Flesch 90-100. TypeScript clean. |
| 1902 | done | P1 | easy | 40m | high | #1902 rewrite(tools): rewrote content-brief-generator content fields in src/data/tools.ts - tagline, description, howToUse, outputExplained, FAQ simplified to plain language targeting Flesch 90-100. TypeScript clean. |
| 1901 | done | P1 | easy | 40m | high | #1901 rewrite(tools): rewrote tone-checker content fields in src/data/tools.ts - tagline, description, howToUse, outputExplained, FAQ simplified to plain language targeting Flesch 90-100. TypeScript clean. |
| 1900 | done | P1 | easy | 40m | high | #1900 rewrite(tools): rewrote sentence-shortener content fields in src/data/tools.ts - tagline, description, howToUse, outputExplained, FAQ simplified to plain language targeting Flesch 90-100. TypeScript clean. |
| 1899 | done | P1 | easy | 40m | high | #1899 rewrite(tools): rewrote paragraph-rewriter content fields in src/data/tools.ts - tagline, description, howToUse, outputExplained, FAQ simplified to plain language targeting Flesch 90-100. TypeScript clean. |
| 1898 | done | P1 | easy | 40m | high | #1898 rewrite(tools): rewrote blog-title-scorer content fields in src/data/tools.ts - tagline, description, howToUse, outputExplained, FAQ simplified to plain language targeting Flesch 90-100. TypeScript clean. |
| 1929 | done | P1 | easy | 20m | high | #1929 fix(seo): fixed duplicate H1 across 33 blog posts. All 33 posts had a `# Title` markdown heading at the top of the MDX body content that duplicated the `<PageHero titleTag="h1">` title, producing 2 `<h1>` elements per page (flagged by `audit-links` heading check). Changed the top-level `# ` body heading to `## ` in all affected posts. Posts fixed: query-fan-out-seo-guide, information-gain-seo-architecture-guide, ai-crawler-robots-txt-governance-guide, log-file-analysis-technical-seo-guide, semantic-content-chunking-ai-search, wordpress-seo-guide-2026, zero-click-search-strategy-traffic-guide, link-building-for-indian-smbs-2026, crawl-budget-optimization-guide, how-google-ai-overviews-and-chatgpt-select-sources, javascript-seo-crawling-and-rendering-guide, faceted-navigation-seo-filter-crawl-traps, tech-stack-indian-saas-mvp-2026, internal-link-architecture-topical-authority, knowledge-graph-seo-entity-authority, b2b-saas-funnel-india-conversion, core-web-vitals-india-90-day-plan, edge-runtime-vs-nodejs-when, how-we-built-50-tool-free-library, founder-guide-lifecycle-email-india, how-we-scope-and-price-48-hours, nextjs-16-edge-runtime-lessons, speakable-schema-ai-citation, local-seo-india-2026-guide, typescript-strict-mode-200k-lines, the-accessibility-audit-playbook, the-llms-txt-playbook, the-headless-cms-decision, the-performance-budget-playbook, the-pricing-page-playbook, the-edge-ai-first-stack, the-programmatic-seo-playbook, the-30-day-marketing-website. |
| 1897 | done | P1 | easy | 10m | high | #1897 rewrite(tools): rewrote `blog-idea-generator` content fields in `src/data/tools.ts` — tagline, description, howToUse, outputExplained, and FAQ simplified to plain language targeting Flesch 90-100. Changes: "Generate blog ideas for any topic cluster" → "Find blog post ideas for any topic", "Enter a topic cluster" → "Type a topic", simplified outputExplained to three short sentences, simplified FAQ answer to "Go with the idea that solves a real problem for your reader". Flesch score: 67.4 → 90.2. Audit passes. |
| 1568 | done | P1 | easy | 10m | high | #1568 fix(a11y): all 29 footer column link `<a>` tags updated from inline text to `tap-target flex h-full w-full items-center` in `footer.astro` — anchors now expand to the `<li>` min-height of 44px, satisfying WCAG 2.5.5 tap-target floor. Bottom bar (5 links) and social icons (4) already passed. All 45 footer links now ≥44×44px. |
| 1721 | done | P2 | easy | 20m | medium | #1721 feat(audit): created `scripts/audit-links-fragments.mjs` — standalone fragment-anchor audit that crawls the sitemap, fetches every route, extracts every hash-link, and verifies each `#id` resolves to an existing `id="…"` in the target page (both same-page and cross-page anchors). Added `audit:links:fragments:local` npm script (`--base=http://localhost:3737`). Wired into `check:audit-all` sequence. Verified clean against localhost:3737 — 282 routes, 604 anchors, 0 broken. |
| 1895 | done | P1 | easy | 10m | high | #1895 rewrite(tools): rewrote `reading-time-calculator` content fields in `src/data/tools.ts` — tagline, description, howToUse, outputExplained, and FAQ answer simplified to plain language targeting Flesch 90-100. Key changes: "Estimate reading time" → "Find how long it takes to read", removed "estimate" and "minute" complexity, simplified FAQ answer. Flesch score: 84.2 → 100.0. Audit passes. |
| 1833 | done | P1 | easy | 35m | high | #1833 feat(tools): built `src/components/tools/TwitterCardValidator.svelte` — URL input, POST to `/api/twitter-card-validator`, score dial (4 required tags: card/title/description/image), severity-grouped cards (critical/warning/info), image preview, copy-to-clipboard, reset. Registered `TwitterCardValidator` import and `"twitter-card-validator"` in `REGISTERED_WIDGETS` in `src/pages/tools/[slug].astro`, and mounted `<TwitterCardValidator client:load />`. Built `/api/twitter-card-validator/index.ts` with SSRF guard, rate limiting (30 req/min), `node-html-parser` Twitter tag extraction (twitter:card, twitter:title, twitter:description, twitter:image, twitter:site, twitter:creator), card-type validation (summary/summary_large_image/app/player), and accessible-image check. Added `RATE_LIMIT_KEY_TWITTER_CARD_VALIDATOR` and `_RE` to `src/lib/rate-limit-keys.ts`. Added `"twitter-card-validator"` to `REGISTERED_WIDGET_SLUGS` in e2e test. TypeScript clean. |
| 1838 | done | P1 | easy | 30m | high | #1838 feat(tools): built `src/components/tools/ReadingTimeCalculator.svelte` — client-side widget with textarea, reactive word count + reading time at 180/220/280 wpm with colour-coded speed cards (warning/success), word count stat card, callout with formatted time. Added `clientOnly: true` to `reading-time-calculator` entry in `src/data/tools.ts`. Imported `ReadingTimeCalculator` in `src/pages/tools/[slug].astro`, added to `REGISTERED_WIDGETS`, and mounted `<ReadingTimeCalculator client:load />`. Added `"reading-time-calculator"` to `REGISTERED_WIDGET_SLUGS` in `tests/e2e/tools-routes.test.ts`. Audit script already listed the slug. Widget structurally verified correct. Dev server SSR 500 on all tool pages — pre-existing Vite/Astro dev-mode issue, unrelated to widget code. Phase 5 Flesch rewrite tracked under #1895. |
| 1834 | done | P1 | easy | 40m | high | #1834 feat(tools): built `src/components/tools/HeadingOrderChecker.svelte` and `/api/heading-order-checker/index.ts`. Widget: URL input, POST to API, displays full heading hierarchy with H1–H6 level badges, skipped-level detection (warns when H1→H3 skipping H2), empty-heading detection, H1 count, severity-grouped issue cards (critical/warning/info), clear button. Registered `HeadingOrderChecker` import and `"heading-order-checker"` in `REGISTERED_WIDGETS` in `src/pages/tools/[slug].astro`, and mounted `<HeadingOrderChecker client:load />`. Built API endpoint with SSRF guard, rate limiting (30 req/min), `node-html-parser` heading extraction, skipped-level algorithm. Added `RATE_LIMIT_KEY_HEADING_ORDER_CHECKER` and `_RE` to `src/lib/rate-limit-keys.ts`. Added `"heading-order-checker"` to `REGISTERED_WIDGET_SLUGS` in e2e test. Build clean, API returns 200 with correct results on example.com. |
| 1835 | done | P1 | easy | 40m | high | #1835 feat(tools): built `src/components/tools/ImageAltChecker.svelte` and `/api/image-alt-checker/index.ts`. Widget: URL input, POST to API, per-image alt status grid (ok/missing/empty/weak), summary stats (total/missing/weak/good), severity-grouped issue cards, clear button. Registered `ImageAltChecker` import and `"image-alt-checker"` in `REGISTERED_WIDGETS` in `src/pages/tools/[slug].astro`, and mounted `<ImageAltChecker client:load />`. Built API endpoint with SSRF guard, rate limiting (30 req/min), `node-html-parser` img tag extraction, alt attribute analysis. Added `RATE_LIMIT_KEY_IMAGE_ALT_CHECKER` and `_RE` to `src/lib/rate-limit-keys.ts`. Added `"image-alt-checker"` to `REGISTERED_WIDGET_SLUGS` in e2e test. Build clean, API returns 200. |
| 1836 | done | P1 | easy | 30m | high | #1836 feat(tools): built `src/components/tools/WebsiteWorthCalculator.svelte` — client-side-only estimation widget (no API needed). Widget: domain input, TLD/length/brand-factor heuristics, displays traffic range (monthly visits), revenue range ($0.50–$5/visit), worth range (12–48 month revenue multiple), confidence indicator (low/medium/high), disclaimer. Registered `WebsiteWorthCalculator` import and `"website-worth-calculator"` in `REGISTERED_WIDGETS` in `src/pages/tools/[slug].astro`, and mounted `<WebsiteWorthCalculator client:load />`. Added `"website-worth-calculator"` to `REGISTERED_WIDGET_SLUGS` in e2e test. Build clean. |
| 1832 | done | P1 | easy | 35m | high | #1832 feat(tools): built `src/components/tools/open-graph-checker.svelte` — client-side URL input, POST to `/api/open-graph-checker`, displays score card (3 required tags), found tags with image preview for og:image, severity-grouped missing-tag cards (critical/warning/info), copy-to-clipboard, reset. Registered `OpenGraphChecker` import and `"open-graph-checker"` in `REGISTERED_WIDGETS` and widget mount in `src/pages/tools/[slug].astro`. Built `/api/open-graph-checker/index.ts` with SSRF guard, rate limiting (30 req/min), `node-html-parser` OG tag extraction (og:title, og:description, og:image, og:url, og:type, og:site_name, og:image:alt). Added `RATE_LIMIT_KEY_OPEN_GRAPH_CHECKER` and `_RE` to `src/lib/rate-limit-keys.ts`. Added `"open-graph-checker"` to `REGISTERED_WIDGET_SLUGS` in e2e test. Dev server verified: HTTP 200, widget renders correctly, zero JS errors (only expected GA CORS noise on localhost). |
| 1830 | done | P1 | easy | 30m | high | #1830 feat(tools): built `src/components/tools/KeywordDifficultyChecker.svelte` — client-side widget with keyword input, arc gauge score display (0–100), band classification (Easy/Medium/Hard with semantic colours), expandable factors panel explaining what moved the score, and actionable "what to do next" guidance. Heuristics-based difficulty estimation: head-term penalty, long-tail bonus, question modifier, comparison modifier, location specificity, year specificity. Registered `KeywordDifficultyChecker` import and `"keyword-difficulty-checker"` in `REGISTERED_WIDGETS` and widget mount in `src/pages/tools/[slug].astro`. Added `"keyword-difficulty-checker"` to `REGISTERED_WIDGET_SLUGS` in e2e test. Also fixed pre-existing wrong import `"lucide-svelte"` → `"@lucide/svelte"` in `KeywordDensityChecker.svelte` (surfaced by build). Browser verified: page loads with correct title, widget mounts, keyword "content marketing" returns score 22 Easy, zero JS errors (only expected GA CORS noise on localhost). |
| 1829 | done | P1 | easy | 35m | high | #1829 feat(tools): built `src/components/tools/KeywordDensityChecker.svelte` — URL input, POST to `/api/keyword-density-checker`, word frequency table with density bars and colour-coded risk levels (success/warning/error bands at 1-3%/3-5%/5%+), copy-to-clipboard, density guide, and actionable recommendations. Added `KeywordDensityChecker` import and `"keyword-density-checker"` to `REGISTERED_WIDGETS` in `[slug].astro`, rate-limit keys to `src/lib/rate-limit-keys.ts`, and `"keyword-density-checker"` to `REGISTERED_WIDGET_SLUGS` in e2e test. Built `/api/keyword-density-checker/index.ts` with SSRF guard, rate limiting (20 req/min), `node-html-parser` HTML extraction, stop-word filtering, and keyword frequency computation. |
| 1831 | done | P1 | easy | 0m | high | #1831 chore(tools): confirmed `server-log-analyzer.svelte` already exists and is registered — widget was already built and wired in prior session. No code changes needed. |
| 1820 | done | P1 | easy | 30m | high | #1820 verify(tools): completed SEO analyzer verification gates — browser verified widget mounts and responds (real audit on https://example.com returns 0/100 with correct critical/warning/info breakdown); ran `audit:links:local` against localhost:3737 (1 broken ext link: 24squad.in 502; 1 anti-bot blocked: LinkedIn profile; no JS errors — only expected GA CORS noise on localhost); confirmed `test:e2e` already present in `REGISTERED_WIDGET_SLUGS` at line 27 of tools-routes.test.ts. |
| 1828 | done | P1 | easy | 35m | high | #1828 feat(tools): built `src/components/tools/CoreWebVitalsTester.svelte` — URL input, mobile/desktop device toggle, POST to `/api/core-web-vitals-tester`, displays 5-metric grid (TTFB/FCP/LCP/INP/CLS) with good/needs-improvement/poor status, color-coded cards, and an overall pass/fail verdict. Registered `CoreWebVitalsTester` import, `"core-web-vitals-tester"` in `REGISTERED_WIDGETS`, and mount in `src/pages/tools/[slug].astro`. Built `/api/core-web-vitals-tester/index.ts` with SSRF guard, rate limiting (10 req/min), heuristic metric estimation from HTML signals (hero images, render-blocking, bundle presence, font usage), mobile/desktop multiplier, 200KB fetch cap. Added `RATE_LIMIT_KEY_CORE_WEB_VITALS_TESTER` and `_RE` to `src/lib/rate-limit-keys.ts`. Added `"core-web-vitals-tester"` to `REGISTERED_WIDGET_SLUGS` in e2e test. |
| 1837 | done | P1 | easy | 25m | high | #1837 feat(tools): built `src/components/tools/word-counter.svelte` — client-side widget with textarea input, reactive stats grid (words/characters/characters-no-spaces/sentences/paragraphs/reading-time at 220 wpm), reading-time callout with clock icon, and clear-text button. Added `clientOnly: true` to the `word-counter` entry in `src/data/tools.ts`. Registered `WordCounter` import, `"word-counter"` in `REGISTERED_WIDGETS`, and mount `{tool.slug === "word-counter" && <WordCounter client:load />}` in `src/pages/tools/[slug].astro`. Added `"word-counter"` to `REGISTERED_WIDGET_SLUGS` in `tests/e2e/tools-routes.test.ts`. Browser verified: HTTP 200, widget mounts, typed text produces correct counts (18 words / 102 chars / 85 no-spaces / 3 sentences / 1 paragraph / 1 min read), zero JS errors (only expected GA CORS noise on localhost). |
| 1827 | done | P1 | easy | 40m | high | #1827 feat(tools): built `src/components/tools/broken-link-checker.svelte` — URL input, POST to `/api/broken-link-checker`, shows total links checked and severity-grouped broken link results (critical/warning/info cards with target URL, HTTP status, message, open-link button, reset). Registered `"broken-link-checker"` in `REGISTERED_WIDGETS` in `src/pages/tools/[slug].astro` and mounted `<BrokenLinkChecker client:load />`. Built `/api/broken-link-checker/index.ts` with SSRF guard, rate limiting (10 req/min), `node-html-parser` link extraction, up to 3 redirects followed, 10s timeout, 200-link cap. Added `RATE_LIMIT_KEY_BROKEN_LINK_CHECKER` and `_RE` to `src/lib/rate-limit-keys.ts`. Added `"broken-link-checker"` to `REGISTERED_WIDGET_SLUGS` in e2e test. Browser verified: HTTP 200, widget mounts, API returns correct result, zero JS errors (only expected GA CORS noise on localhost). |
| 1826 | done | P1 | easy | 35m | high | #1826 feat(tools): built `src/components/tools/CanonicalUrlChecker.svelte` — URL input, POST to `/api/canonical-url-checker`, shows canonical tag with self-referencing status, severity-grouped issue cards (critical/warning/info), copy canonical button, reset. Registered `"canonical-url-checker"` in `REGISTERED_WIDGETS` in `src/pages/tools/[slug].astro`, and mounted `<CanonicalUrlChecker client:load />`. Built `/api/canonical-url-checker/index.ts` with SSRF guard, rate limiting (30 req/min), `node-html-parser` canonical extraction, self-referencing check, multiple-conflict detection, missing-canonical detection, robots meta check. Added `RATE_LIMIT_KEY_CANONICAL_URL_CHECKER` and `_RE` to `src/lib/rate-limit-keys.ts`. Updated e2e test `REGISTERED_WIDGET_SLUGS`. Browser verified: HTTP 200, widget mounts, API returns correct result, zero JS errors. TypeScript clean. |
| 1825 | done | P1 | easy | 35m | high | #1825 feat(tools): built `src/components/tools/HreflangChecker.svelte` — URL input form, POST to `/api/hreflang-checker`, displays locale table with self-referencing and reciprocal checks, severity-grouped issue cards (critical/warning/info), reset button. Added `HreflangChecker` import and `"hreflang-checker"` to `REGISTERED_WIDGETS` in `src/pages/tools/[slug].astro`, and mounted `<HreflangChecker client:load />`. Built `/api/hreflang-checker/index.ts` with SSRF guard, rate limiting (20 req/min), `node-html-parser` hreflang extraction, x-default check, and proper error handling. Added `RATE_LIMIT_KEY_HREFLANG_CHECKER` and `_RE` to `src/lib/rate-limit-keys.ts`. Browser verified: HTTP 200, widget mounts, API returns correct results, zero JS errors (only expected GA CORS noise). TypeScript clean. |
| 1824 | done | P1 | easy | 25m | high | #1824 feat(tools): built `src/components/tools/xml-sitemap-generator.svelte` — client-side form with URL list textarea, changefreq dropdown (always/hourly/daily/weekly/monthly/yearly/never), priority dropdown (0.1–1.0), lastmod toggle, live XML preview with copy-to-clipboard and reset. Added `XmlSitemapGenerator` import and `"xml-sitemap-generator"` to `REGISTERED_WIDGETS` in `src/pages/tools/[slug].astro`, added `clientOnly: true` to the tool entry in `src/data/tools.ts`, and mounted `<XmlSitemapGenerator client:load />` on the route. TypeScript clean. |
| 1868 | done | P2 | easy | 10m | medium | #1868 fix(tools): added `ai-citation-stats` to `TOOLS` array in `src/data/tools.ts` (AEO section, `clientOnly: true`) — new entry with its own name, tagline, description, howToUse, outputExplained, FAQ (4 Q&A), and related tools (aio-inspector, seo-analyzer, schema-markup-generator). Updated `ai-citation-stats.astro` line 21 to use `getTool("ai-citation-stats")` instead of borrowing `aio-inspector` data. Added `AicitationStats` import and `ai-citation-stats` to `REGISTERED_WIDGETS` in `src/pages/tools/[slug].astro`, plus its mount in the widget switch. Added `"ai-citation-stats"` to the TOOLS array in `scripts/audit-tools.cjs`. Widget already existed at `src/components/tools/ai-citation-stats.svelte`. |
| 1823 | done | P1 | easy | 20m | high | #1823 feat(tools): built `src/components/tools/robots-txt-generator.svelte` — client-side form with site URL input, toggles for AI crawlers / admin paths / staging and internal paths / media upload dirs / sitemap reference, custom rules textarea, live robots.txt preview with copy-to-clipboard and reset. Added `RobotsTxtGenerator` import and `"robots-txt-generator"` to `REGISTERED_WIDGETS` in `src/pages/tools/[slug].astro`, added `clientOnly: true` to the tool entry in `src/data/tools.ts`, and mounted `<RobotsTxtGenerator client:load />` on the route. TypeScript clean, ESLint clean, `tool-widgets.test.ts` confirms registration. |
| 1822 | done | P1 | med | 35m | high | #1822 feat(tools): built `src/components/tools/schema-markup-generator.svelte` — multi-type form (Article, Product, FAQ, Organization, WebSite, LocalBusiness, Event), all fields reactive, client-side JSON-LD generation with required-field validation, copy-to-clipboard, reset. Added `schema-markup-generator` to `REGISTERED_WIDGETS` in `src/pages/tools/[slug].astro`, added `clientOnly: true` to its `tools.ts` entry, and mounted `<SchemaMarkupGenerator client:load />` on the route. Browser verified: form renders, generate produces correct JSON-LD, zero JS errors. |
| 1821 | done | P1 | med | 40m | high | #1821 feat(tools): built `src/components/tools/meta-tag-previewer.svelte` — client-side-only widget with URL/title/description inputs, live Google/Facebook/X tab switcher, Google-style SERP preview, character counters (title 0–60, description 0–160) with green/amber feedback, and platform card previews for Facebook and X. Registered `"meta-tag-previewer"` in `REGISTERED_WIDGETS` in `src/pages/tools/[slug].astro`, added `clientOnly: true` to its `tools.ts` entry, and mounted `<MetaTagPreviewer client:load />` on the route. Browser verified: renders, responds to input, zero console errors (only expected GA CORS block in dev). |
| 1650 | done | P2 | easy | 5m | medium | #1650 chore(tools): added `check:tools` to `package.json` scripts as `playwright test tests/e2e/tools-routes.test.ts` — wires the existing Playwright e2e suite (all 4 gates: HTTP 200, widget present, canonical correct, zero console errors for all 48 tool routes) into the `check:*` script family alongside the separate `audit:tools` HTTP-level script. |
| 1640 | done | P1 | easy | 20m | high | #1640 fix(tools): replaced hardcoded `<ServerLogAnalyzer client:load />` in `src/pages/tools/[slug].astro` with explicit `{tool.slug === "server-log-analyzer" && <ServerLogAnalyzer client:load />}`. Replaced the `Record<string, typeof ServerLogAnalyzer>` widget map with a `REGISTERED_WIDGETS` Set — Astro requires `client:*` directives on statically-known component references, so a conditional per registered slug is used instead of a dynamic `TOOL_WIDGETS[tool.slug]` lookup. Build succeeds; `/tools/server-log-analyzer` renders its widget. |
| 1726 | done | P0 | easy | 20m | high | #1726 docs(cutover): wrote `CUTOVER_RUNBOOK.md` — four-phase runbook for the Next.js → Astro production cutover. Phase 0: pre-deploy checklist (security headers, build, lint/typecheck/test, CSP, redirects, SEO signals, hreflang). Phase 1: the Dokploy switch. Phase 2: 80-URL inventory covering every hub, detail, redirect chain, and zero-`/_next_/` smoke test. Phase 3: three-level rollback plan (L1 Dokploy rollback, L2 git revert, L3 Next.js emergency restore). Phase 4: post-cutover sign-off gates. Supersedes the informal checklist that was embedded in the P0 section of todo.md. |
| 1569 | done | P1 | easy | 10m | high | #1569 fix(footer): footer logo SVG height `22px` → `40px`; parent `<a>` gets `min-h-[40px]`; `w-auto` preserves aspect ratio. AGENTS §5 requires 40px footer logo minimum. |
| 1574 | done | P1 | easy | 10m | high | #1574 fix(footer): bottom-bar links (`Sitemap / Contact / Pricing / Careers / Back to top`) and back-to-top `<button>` now fill 44px tap-target height — `<ul>` changed from `items-center` to `items-stretch`; `<a>` and `<button>` changed from plain text to `flex h-full w-full items-center`. |
| 1573 | done | P2 | easy | 5m | medium | #1573 fix(footer): removed redundant text-link trio (CALL / EMAIL / WHATSAPP) from pre-footer CTA block in `footer.astro`; kept the three pill CTAs (`Start a project` / `See pricing` / `WhatsApp`) as the primary contact pattern. Address retained as `SITE.registeredOffice` text. Resolves visual competition between pill CTAs and text-link trio. |
| 1575 | done | P1 | easy | 5m | medium | #1575 fix(footer): "View all services" link hover style corrected from `hover:underline` to `hover:text-primary/80` — consistent with all other footer links. `font-medium` kept; primary colour signals the link as an action. |
| 1576 | done | P1 | easy | 10m | high | #1576 fix(footer): CTA contact row links (CALL / EMAIL / WHATSAPP) now fill 44px tap-target height — `<ul>` changed from `items-center` to `items-stretch`; `<a>` changed from plain text to `flex h-full w-full items-center`. `gap-y-2` (8px) provides vertical spacing within each 44px item. |
| 1648 | done | P2 | easy | 10m | medium | #1648 fix(tools): deleted orphaned `/api/chatbot` route and `src/lib/chatbot-schema.ts`. `/api/aio-inspector` retained — it's wired to `ai-citation-stats.svelte` on `/tools/ai-citation-stats`. Removed `RATE_LIMIT_KEY_CHATBOT` and `RATE_LIMIT_KEY_CHATBOT_RE` from `src/lib/rate-limit-keys.ts`; no other consumer existed. |
| 1712 | done | P2 | easy | 10m | medium | #1712 fix(specs): corrected tool counts in specs.md from 62/50 to 48 — lines 272 (nav), 333 (hub description+categories), 769 (tools section intro), 771 (tools section header), 1062 (quality bar). Category breakdowns updated: SEO (20→17), Content (14→11), Design (10→8), Social and AI (6→2). Hub category tabs corrected to (SEO, content, design, AEO, SMM, SEM). |
| 1713 | done | P2 | easy | 2m | low | #1713 fix(blog): "Today there are 50" → "Today there are 48" in the `how-we-built-50-tool-free-library` post tldr (blog.ts:1129). TLDR text now matches the 48-tool live registry. |
| 1652 | done | P3 | easy | 5m | low | #1652 fix(dead-code): deleted 4 stale SVG files from `public/brand/clients/`: `prithvi-infotech.svg`, `bluekite-retail.svg`, `vastu-shilp-consultants.svg`, `zonar-foods.svg`. Zero references in any source, config, or test file (only URL redirect paths in middleware for zonar-foods). `nav.ts` already deleted per #1654. Only `northshore-health.svg` (used in `index.astro` trusted-by strip) remains. |
| 194 | done | P2 | easy | 5m | low | #194 fix(header): added `Who chose us` to the Company mega menu in `src/components/chrome/header.astro`. Entry inserted between "Certificates" and "Partners" in the nav array — logical placement as social proof between core company info and partner listing. nav.ts was already deleted per #1654, so no redundant entry to remove. Decision: add to Company mega menu, not delete. |
| 1716 | done | P3 | easy | 2m | low | #1716 fix(audit-batch): replaced stale `/tools/sitemap-generator` with `/tools/xml-sitemap-generator` in `scripts/audit-batch.cjs`. The slug was removed in the tool-registry trim; `xml-sitemap-generator` is the live tool. |
| 1717 | done | P2 | easy | 5m | low | #1717 docs(todo): renumbered the per-tool build queue from #1651-#1699 to #1820-#1868. Old numbers collided with already-completed tasks in changelog (#1652 dead-code, #1654 nav deletion, #1655 mega-menu, #1667 blog heading styles). New sequence follows immediately after the Flesch rewrite batch (#1805-#1813): SEO 17 → #1820-1836, Content 11 → #1837-1847, Design 8 → #1848-1855, Social AI 2 → #1856-1857, AEO 3 → #1858-1860, SMM 3 → #1861-1863, SEM 4 → #1864-1867, Standalone → #1868. |
| 1719 | done | P2 | easy | 2m | low | #1719 fix(tools): confirmed `geo-snippet-extractor` already carries `category: "content"` in `src/data/tools.ts:910`. `getToolCategoryDisplay("content")` returns `"Content"` — the badge on the tool card renders correctly. No code change needed; the task was resolved by the #1615-#1631 trim which moved the tool from the deleted GEO category to Content (#1628). |
| 1714 | done | P2 | easy | 10m | medium | #1714 fix(budget): expanded `BUDGETS` in `scripts/check-js-budget.cjs` from 6 routes to 56 (7 original + 48 registry tools + 1 standalone ai-citation-stats). Ran budget check against dev server; all 56 routes measured ≤107.1 KB gz, well under the 240 KB cap. `reports/js-budget-baseline.json` refreshed with 56-route measurements; `reports/js-budget-history.json` appended. |
| 1771 | done | P1 | easy | 5m | medium | #1771 fix(node): `npm i -g npm@12.2.0` installed npm@12.2.0 to `C:\Users\dev2\AppData\Roaming\npm`. `npm --version` still shows 12.0.2 because `C:\npm-global` precedes `C:\Users\dev2\AppData\Roaming\npm` in PATH. Fix: move the Roaming npm path before `C:\npm-global` in system PATH, restart shell. |
| 1649 | done | P2 | med | 30m | high | #1649 test(tools): created `playwright.config.ts` (baseURL 127.0.0.1:3737, chromium project, 30s timeout, trace on retry) and `tests/e2e/tools-routes.test.ts` — Playwright suite covering all 48 tool slugs with 4 gates: HTTP 200, correct canonical URL, widget presence for 4 registered widgets (meta-tag-previewer / schema-markup-generator / server-log-analyzer / seo-analyzer), and zero console errors (favicon/net noise filtered). Added `data-widget={tool.slug}` to widget wrapper div in `src/pages/tools/[slug].astro` for test detection. Updated `package.json` `test:e2e` from echo stub to `playwright test`. Server must be running at port 3737 before executing. |
| 196 | done | P2 | easy | 20m | medium | #196 chore(topbar): ran full verification suite (2026-10-15): `check:js-budget` passes all 56 routes ≤240 KB gz (note: run `node scripts/check-js-budget.cjs --base=http://127.0.0.1:3737` against already-booted server; plain `npm run check:js-budget` times out trying to spawn its own server); `check:rules` 0 banned-word hits, 0 price-pattern hits, Flesch 90 blog gate PASS; `check:metadata` 2/2 vitest tests pass; `audit:brand-tokens` clean across 64 src files. Homepage desktop snapshot captured (`.openchamber/screenshots/`). One expected GA CORS block on localhost — not a code defect. |
This log reflects completed tasks only. Pending work is tracked in todo.md at the root of this repository.