Skip to content

<div style="display: none;" hidden="true" aria-hidden="true">Are you an LLM? You can read better optimized documentation at /changelog/Changelog.4.110.md for this page in Markdown format</div>

Home | Changelog

Version 4

version 4.110

  • [4.110.0] test(config): set the default PHPUnit memory_limit to 512M (Advisable-com/ecommercen#373)

    • Why. A full composer test run (all four suites in one process) fatally OOMed at PHP's default 128M while the DI container compiles mid-run (src/Rest/Product/container.php); a clean full run peaks at ~158M. Per-suite runs stayed under the ceiling, so it only bit the all-in-one run.
    • The fix. phpunit.xml.dist now sets &lt;ini name="memory_limit" value="512M"/>, so the full suite completes in one process with no CLI override (verified: 6874 tests, 0 failures, default PHP limit still 128M). Developer-tooling only — no runtime code changed. Note: a stale gitignored local phpunit.xml missing the line still OOMs — re-copy from .dist (documented in docs/guides/Testing.md).
  • [4.110.0] fix(sitemap): reference sitemap-index chunks via base_url() instead of the storage-disk URL

    • The problem. AdvGenerateSitemaps built the chunk URLs written into sitemap_index.xml with $disk->url($chunkPath), which resolves against the storage disk — an APP_BASE_URL-prefixed path on local-disk tenants but the configured SITEMAP_S3_URL on K8s/S3 tenants. On S3/CDN-backed tenants the index therefore listed chunk &lt;loc> URLs on the storage/CDN host rather than the site's own canonical host.
    • The fix. Build each chunk URL with base_url($chunkPath) so the index always references &lt;canonical-host>/sitemap/sitemap-N.xml, matching the host the index itself is served from. Chunk files are still written to the sitemap disk and served by nginx/CDN at the same /sitemap/ prefix, so the URLs continue to resolve. The adjacent code comment was updated to match.
  • [4.110.0] fix(admin): replace custom language dropdown with native select

    • The bug. The admin header language selector used a custom &lt;div>-based dropdown driven by JS toggle handlers. On mobile (≤600px) the selector was hidden entirely via display:none, and the sidebar fallback had a CSS typo (color:18px instead of font-size:18px). The JS close-on-outside-click and state-sync logic was fragile and unnecessary overhead.
    • The fix. Replaced the custom dropdown and its JS with a native &lt;select onchange="window.location.href=this.value">. Removed the duplicate .nav-user-mobile sidebar block and all related JS handlers. Added .nav-mob-lang-select SCSS with a custom SVG arrow and responsive rules so the selector is visible and styled correctly at all breakpoints. Added a rem() SCSS utility function and set html { font-size: 16px } as the rem base. Fixed mobile hamburger bar sizing (≤575px) and version badge padding.
  • [4.110.0] refactor(mcp): sharpen tool definitions for the LLM + unify the batch-update shape (MCP SEO Brief #2)

    • Why. A three-lens grading of the 17 tool definitions (what the bot sees in tools/list) flagged a handful of helpfulness gaps. These are AI-facing description/schema improvements — no domain logic changed except the products_batch_update return shape (see below).
    • Write output documented. The shared write-tool note now states that writes return changed (before/after), warnings (non-blocking, e.g. over-length meta) and the updated object, or no_changes: true on a no-op — so the bot can tell an applied edit from a no-op and relay warnings.
    • products_batch_update aligned with categories_batch_update. It now returns the unified data[] + results[] ({id|index, status, error?}) + missing[] shape (was results[]-only) and its input array param was renamed updatesitems. An existence pre-check routes not-found ids to missing[]. (Low-risk: MCP clients re-read the schema each session.)
    • lang made discoverable. Every lang description now points at get_shop_info for valid codes and explains that writing to a language the record lacks fails (read first / fall back to default).
    • Filled gaps. Added per-property descriptions that were missing (category_id, the five update_product_content body blocks, the shared id, page/per_page); documented the pagination block returned by the three list tools; sharpened the has_complete_description "raw vs accurate" caveat; clarified search_products is_active vs include_inactive precedence; cross-linked update_productupdate_product_content; noted brand_id must be an existing brand; flagged list_categories' truncated meta_description.
    • Server instructions now route the bot toward stats/batch tools to cut round-trips, state omit-vs-empty-string semantics, and mention the audit + warnings.
  • [4.110.0] feat(mcp): category bulk & SEO-audit tools — categories_stats, filters, batch get/update (MCP SEO Brief #2, Advisable-com/ecommercen#362 #363 #364 #365 #366)

    • What it is. Categories gain the bulk/filter/stats parity that products already have, so a category SEO audit runs in 1–3 calls instead of ~87 page reads. Brings the MCP tool surface to 17. Purely additive — no existing tool signature changes; new list-output fields are additions.
    • categories_stats. New tool returning the full audit in ONE call (no pagination): total, active, missing_meta_title, missing_meta_description, missing_both_meta, missing_description, incomplete_description. Optional scope: parent_id, is_active. Backed by Category\Service::stats() → a single conditional-SUM aggregate query.
    • list_categories filtering. per_page cap raised 20 → 200; new server-side filters has_meta, has_meta_title, has_meta_description, has_description, has_complete_description, parent_id, is_active (maps to published). has_meta uses the same rule as products. Default still returns ALL categories (backward-compatible — does not adopt search_products' active-only default).
    • Lightweight metadata in the list output. Each row now carries has_meta, has_meta_title, has_meta_description, meta_title_length, meta_description_length, description_length (clean text), description_word_count and has_complete_description, so a completeness audit needs no per-category get_category.
    • categories_batch_get. Reads up to 100 categories by id in one call; returns data (the get_category shape, in requested order) and missing.
    • categories_batch_update. Updates name/description/meta for up to 50 categories in one call, best-effort per row (a failing or not-found id never aborts the others); returns data (with before/after), per-item results and missing.
    • Domain foundation. New FilterByContentPresence spec + a shared ContentPresenceSql helper keep the "content present" rule in one place, reused by both the list filters and the stats aggregate so a filter and its matching count cannot drift.
  • [4.110.0] feat(mcp): products_stats + product completeness metadata (MCP SEO Brief #2, Advisable-com/ecommercen#367)

    • products_stats. Product counterpart to categories_stats — the same seven audit counts in one call, scoped by category_id, brand_id (alias vendor_id) and is_active.
    • Metadata parity. search_products rows gain the same lightweight metadata fields as list_categories (has_meta_title/has_meta_description, meta_*_length, description_length, description_word_count, has_complete_description), and search_products gains a has_complete_description filter.
  • [4.110.0] feat(domain): has_complete_description filter + incomplete_description stat (raw-length, MCP SEO Brief #2, Advisable-com/ecommercen#371)

    • What it is. A configurable "complete description" dimension for categories and products. A description is complete when present and reaching a length threshold, incomplete when present-but-short (disjoint from "missing").
    • Accurate per-row, fast server-side. The per-row has_complete_description shown in the list output is the accurate clean-text flag (DescriptionMetrics, strips HTML). The server-side filter and the incomplete_description stat use a faster raw-CHAR_LENGTH SQL approximation — so a borderline row can classify slightly differently between the two, by design (documented).
    • Configurable threshold. APP_MCP_DESCRIPTION_COMPLETE_MIN_CHARS (default 120) feeds both the domain Services (DI-bound) and the MCP projection, so all three agree on the number.
    • Note. The category updated_since request from the brief was dropped (no timestamp column on shop_product_category).

Notes

  • [4.110.0] Requires npm run admin-production
    • The admin nav redesign and the native language-select fix changed admin SCSS (assets/admin/scss/_admin_nav_redesign.scss, _mixins.scss, _root_vars.scss, _update.scss) and admin views. Upstream ships the rebuilt public/ui/admin/dist/ bundles in this release, so a standard deploy needs no build step — but clients that compile their own admin assets must run npm run admin-production to pick up the change. The storefront bundle is unchanged, so npm run production is not required.