Changelog
What we shipped.
A running log of every task completed on webgrow24.com. Bug fixes, migrations, SEO improvements, content additions and more. Updated after every deploy.
Tasks logged
991+
across all rounds
Since
Jan 2026
founding month
Latest
2026-10-30
most recent entry
2026-10-30
| # | Status | Priority | Difficulty | Time | Value | Completed Task |
|---|---|---|---|---|---|---|
| 2697 | done | P2 | easy | 10m | high | #2697 chore(reviews): refresh reviews.json from the live GBP listing. 40 reviews at 4.9, 1/39 split, extractedAt 2026-10-30. |
| 2700 | done | P2 | easy | 15m | high | #2700 chore(gbp): submit GBP hours Mon-Sat 10:00-19:00, Sunday closed. Google shows the edit pending review. |
| 2470 | done | P1 | med | 20m | high | #2470 docs: repoint #1717 references at the open #2568 DOM harness. |
| 2497 | done | P1 | med | 20m | high | #2497 chore(audit): ci runs audit:brand-tokens --strict. |
| 2514 | done | P1 | med | 20m | high | #2514 fix(schema): blog Person blocks carry a profile url and @id. |
| 2518 | done | P1 | med | 20m | high | #2518 fix(content): posts credit the founder, who has a profile page. |
| 2523 | done | P1 | med | 20m | high | #2523 fix(content): the tools-library post says 49 tools. |
| 2549 | done | P1 | med | 20m | high | #2549 a11y(cards): the tools card CTA no longer repeats the tool name. |
| 2575 | done | P1 | med | 20m | high | #2575 fix(audit): audit-page-meta-seo resolves the repo root. |
| 2590 | done | P1 | med | 20m | high | #2590 chore(scripts): the fragments auditor defaults to production like the link auditor. |
| 2591 | done | P1 | med | 20m | high | #2591 chore(scripts): both link auditors share linkAuditorUserAgent(). |
| 2610 | done | P1 | med | 20m | high | #2610 fix(prompt): the assistant prompt reads brand, tagline and currency from SITE. |
| 2622 | done | P1 | med | 20m | high | #2622 chore(gate): the palette fixer and auditor both exempt globals.css. |
| 2624 | done | P1 | med | 20m | high | #2624 fix(audit): audit-lib-exports matches export { A, B } lists. |
| 2625 | done | P1 | med | 20m | high | #2625 fix(audit): list-lib-exports walks .astro and .svelte. |
| 2626 | done | P1 | med | 20m | high | #2626 chore(audit): audit-lib-exports drops the deleted src/app root. |
| 2637 | done | P1 | med | 20m | high | #2637 chore(audit): audit-rendered-copy scans .astro and .svelte. |
| 2638 | done | P1 | med | 20m | high | #2638 chore(audit): audit-pricing-copy walks .astro, .svelte and .mdx; fenced code is exempt. |
| 2639 | done | P1 | med | 20m | high | #2639 chore(audit): rule-check walks .astro, .svelte and .mdx. |
| 2640 | done | P1 | med | 20m | high | #2640 fix(price): the price gate skips code comments. |
| 2692 | done | P1 | med | 20m | high | #2692 chore(audit): console-errors-audit, test-active-widgets and audit-site skip the Umami origin. |
| 2696 | done | P1 | med | 20m | high | #2696 docs(blocked): the GA4 data caveats are historical only. |
| 2703 | done | P1 | med | 20m | high | #2703 docs(blocked): the resolved #1528 row is retired. |
| 2711 | done | P1 | med | 20m | high | #2711 chore(home): remove the unused featuredCase const. |
| 2712 | done | P1 | med | 20m | high | #2712 fix(header): megaColumns() guards a null servicesMega. |
| 2713 | done | P1 | med | 20m | high | #2713 fix(audit): audit-page-jsonld fails when src/pages is missing. |
| 2714 | done | P1 | med | 20m | high | #2714 chore(repo): untrack and ignore .openchamber audit snapshots. |
| 2717 | done | P1 | med | 20m | high | #2717 fix(scripts): rule-check writes under reports/. |
| 2718 | done | P1 | med | 20m | high | #2718 docs(e2e): the tools-routes docblock names Umami. |
| 2719 | done | P1 | med | 20m | high | #2719 fix(copy): tools pages say twelve server tools. |
| 2721 | done | P1 | med | 20m | high | #2721 fix(origin): broken-link-checker and twitter-card-validator call botUserAgent(). |
| 2722 | done | P1 | med | 20m | high | #2722 docs(seo): the home title docblock says 51 chars. |
| 2710 | done | P0 | easy | 20m | high | #2710 fix(test): `npm run test` no longer hangs on the two Playwright visual audits. The vitest shim (`tests/unit/audit-scripts.test.ts`) exports `WG24_SKIP_PLAYWRIGHT_AUDITS=1` for runs without Chromium or a live dev server, but `scripts/audit-visual-2105.test.cjs` and `scripts/audit-visual-screen.test.cjs` ignored it and spawned `execFileSync` against `http://127.0.0.1:3737`, which no server listens on. The audit then waited on Playwright navigation until the shim's 400s timeout, leaving orphaned Chromium children and a stuck vitest worker - the goal session's "test suite still running" turns were this hang. Both sibling tests now honour the flag, print `audit-visual-2105: skipped (WG24_SKIP_PLAYWRIGHT_AUDITS=1)` / `audit-visual-screen: skipped (...)` and exit 0 immediately; the registry `successLine` patterns in `tests/unit/audit-scripts.registry.ts` accept either the pass line or the skip line. Verified: both scripts exit 0 in under a second with the flag, and `npx vitest run tests/unit/audit-scripts.test.ts -t "audit-visual"` passes 4/4 in 371ms. |
| 2424 | done | P1 | med | 20m | high | #2424 perf(schema): the home Organization node drops address, contactPoint and award. |
| 2434 | done | P2 | easy | 10m | high | #2434 fix(seo): the home title sits in the 50-60 char band with `| WebGrow24`. |
| 2446 | done | P1 | med | 30m | high | #2446 perf(seo): the home page emits one JSON-LD `@graph` instead of six blocks. |
| 2500 | done | P2 | med | 20m | high | #2500 a11y(islands): tech-grid and industry-grid scope their tab and panel ids. |
| 2502 | done | P2 | med | 20m | high | #2502 a11y(islands): faq-search and site-search scope their fixed ids. |
| 2529 | done | P2 | easy | 10m | med | #2529 chore(origin): the indexnow key-not-found message reads SITE_URL. |
| 2541 | done | P2 | easy | 10m | med | #2541 docs(schema): organizationSchema() carries a docblock describing what it publishes. |
| 2553 | done | P2 | easy | 10m | high | #2553 fix(content): the tools-library post says nine months, not two years. |
| 2586 | done | P1 | easy | 15m | high | #2586 a11y(search): stale search rows use text-fg/70 instead of opacity-60. |
| 2595 | done | P2 | easy | 10m | high | #2595 fix(content): src/data/blog.ts says 49 tools, not 50. |
| 2601 | done | P2 | med | 20m | high | #2601 chore(scripts): check-brand-classes.cjs walks .astro and .svelte files. |
| 2611 | done | P2 | med | 20m | high | #2611 a11y(ids): services-tabs scopes its document-wide tab and panel ids. |
| 2623 | done | P1 | easy | 10m | high | #2623 fix(lib): hrefsIn is module-private; check:lib-exports passes. |
| 2635 | done | P1 | med | 20m | high | #2635 fix(tools): the Core Web Vitals endpoint uses the shared metrics module. |
| 2642 | done | P1 | med | 20m | high | #2642 fix(origin): the tool API routes call botUserAgent() instead of hand-typing it. |
| 2643 | done | P1 | med | 20m | high | #2643 fix(origin): schema.ts, metadata.ts and the schema island read normalizeSiteOrigin(). |
| 2644 | done | P1 | med | 20m | high | #2644 fix(cards): the service card carries one anchor, not a stretched overlay. |
| 2683 | done | P1 | med | 20m | high | #2683 feat(analytics): Base.astro carries the Umami tracker with the hardcoded id. |
| 2684 | done | P2 | easy | 10m | high | #2684 feat(analytics): the Umami tag carries data-do-not-track="true". |
| 2685 | done | P1 | easy | 15m | high | #2685 chore(analytics): the GA4 gtag stub, scroll-depth events and gtag.js are gone. |
| 2686 | done | P1 | med | 20m | high | #2686 fix(csp): analytics.webgrow24.com is allowlisted for script-src and connect-src. |
| 2687 | done | P1 | easy | 10m | high | #2687 fix(csp): build-csp.mjs no longer lists GA4 or Google Fonts origins. |
| 2688 | done | P2 | easy | 5m | med | #2688 chore(types): window.gtag and window.dataLayer are gone from globals.d.ts. |
| 2690 | done | P2 | med | 20m | high | #2690 fix(copy): cookies, privacy and infrastructure pages name Umami, not GA4. |
| 2693 | done | P2 | med | 20m | high | #2693 test(analytics): no GA4 id in src/ and one Umami tag per page. |
| 2695 | done | P2 | easy | 10m | high | #2695 fix(data): technologies and company copy name Umami, not GA4. |
| 2705 | done | P3 | easy | 5m | med | #2705 chore(todo): course tasks 2650-2669 are retired into changelog rows. |
| 2604 | done | P2 | med | 20m | high | #2604 test(audit): the source-paths test cleans its fixture when killed. |
| 2618 | done | P2 | easy | 10m | med | #2618 fix(copy): the tool TL;DR keeps "We do not store it." capitalised. |
| 2629 | done | P1 | med | 20m | high | #2629 fix(schema): schema.ts restores founderPersonId, founderPersonUrl, teamMemberId and homeSpeakableSchema. |
| 2631 | done | P1 | easy | 10m | high | #2631 fix(reviews): reviews.ts restores reviewPublishedDate. |
| 2632 | done | P1 | med | 20m | high | #2632 fix(tools): tools.ts restores the processing field for eleven server tools. |
| 2633 | done | P1 | med | 20m | high | #2633 fix(tools): the tool page privacy note branches on processing. |
| 2634 | done | P2 | easy | 10m | high | #2634 fix(tools): the tools hub FAQ says eleven tools call an API. |
| 2645 | done | P1 | med | 20m | high | #2645 fix(cards): the tools-grid tile carries one anchor, not an article wrapper. |
| 2646 | done | P1 | easy | 10m | high | #2646 a11y(cards): the service card ordinal clears 4.5:1. |
| 2648 | done | P1 | med | 20m | high | #2648 feat(schema): the home page wires homeSpeakableSchema() back in. |
| 2689 | done | P2 | med | 20m | high | #2689 feat(analytics): scroll_depth events go through umami.track. |
| 2691 | done | P2 | med | 20m | high | #2691 test(build): the CSP and tools-routes e2e no longer pin google-analytics origins. |
| 2707 | done | P1 | easy | 25m | high | #2707 fix(hero): the strip layout stops crushing its cards on a phone. `highlightsLayout="strip"` pinned `grid-template-columns: repeat(4, minmax(0, 1fr))` outside any media query, so on a 390-412px viewport each of the four highlight cards was about 76px wide: the value wrapped one character per line ("24 / m / o / n / t / h / s"), the label ran past the card and was clipped by its `overflow: hidden`, and the row read as broken rather than compact. Four pages render the strip - `/ai-coding-course`, `/company`, `/industries` and `/technologies` - so the defect was site-wide. `src/styles/globals.css` now keeps the strip on the base two-track grid and starts the four-track row at `min-width: 1024px`, which is the first width where a card clears the 32px icon, the 12px gap, the 16px deco-dot clearance and the 32px padding and still holds a three-word label on one line. The highlight card also took `min-width: 0` so a long value cannot push an `fr` track wider than the container (the same fix task #2119 applied to page grids). A second rule stacks the icon above the text below 640px, because `/technologies` carries single words at 20px bold ("Frontend", "Senior-led") that do not fit 80px of text column and broke mid-word; stacking gives them the full card width. Verified in Chrome at 390px, 768px and 1440px on `/ai-coding-course` and `/technologies`: 2x2 cards with the icon on top at 390px, 2x2 side-by-side at 768px, the four-across strip at 1440px, and no horizontal overflow. `tests/unit/page-hero-contract.test.ts` gained a five-case responsive block that fails if the four-track rule returns to the base rule, if the 1024px breakpoint moves, if the card stacking rule is dropped, or if the card loses `min-width: 0`. |
| 2650 | done | P2 | med | 20m | high | #2650 feat(data): add src/data/courses.ts as the only home for course figures. |
| 2651 | done | P2 | med | 20m | high | #2651 fix(data): unit-test that year-one total equals the year-two stipend payout. |
| 2652 | done | P2 | med | 20m | high | #2652 chore(gate): confirm both price gates pass with no exemption and no literal. |
| 2653 | done | P2 | med | 20m | high | #2653 feat(course): scaffold /ai-coding-course with metadata, canonical and breadcrumb. |
| 2654 | done | P2 | med | 20m | high | #2654 feat(course): build the hero with keyword H1 and two CTAs. |
| 2655 | done | P2 | med | 20m | high | #2655 feat(course): add the at-a-glance duration, fee, stipend and seats table. |
| 2656 | done | P2 | med | 20m | high | #2656 feat(course): add the three-step zero-fee timeline including its conditions. |
| 2657 | done | P2 | med | 20m | high | #2657 feat(course): add the six-track curriculum with the brochure tool names. |
| 2658 | done | P2 | med | 20m | high | #2658 feat(course): add the months 1-24 arc from fundamentals to paid client work. |
| 2659 | done | P2 | med | 20m | high | #2659 feat(course): add the live-projects and portfolio proof section. |
| 2660 | done | P2 | med | 20m | high | #2660 feat(course): add who-it-is-for and who-it-is-not-for sections. |
| 2661 | done | P2 | med | 20m | high | #2661 feat(course): add the AI-assisted development section for Cursor and Minimax. |
| 2662 | done | P2 | med | 20m | high | #2662 feat(course): add the fee and payment table reading courses.ts. |
| 2663 | done | P2 | med | 20m | high | #2663 feat(course): add month-24 outcomes with no placement guarantee. |
| 2664 | done | P2 | med | 20m | high | #2664 feat(course): add the four-step admission process. |
| 2665 | done | P2 | med | 20m | high | #2665 feat(course): add the FAQ with FAQPage schema. |
| 2666 | done | P2 | med | 20m | high | #2666 feat(schema): add courseSchema() plus a unit test. |
| 2667 | done | P2 | med | 20m | high | #2667 feat(course): add the WhatsApp apply CTA with a pre-filled message. |
| 2668 | done | P2 | med | 20m | high | #2668 chore(sitemap): list /ai-coding-course in the misc sitemap section. |
| 2669 | done | P2 | med | 20m | high | #2669 chore(nav): decide and wire the course page into site navigation. |
This log reflects completed tasks only. Pending work is tracked in todo.md at the root of this repository.