# Known Issues & Working Patterns Critical bugs discovered, diagnosed, and fixed during development. Read before touching Elementor or page builds. --- ## 1. ElementorData WP Object Cache Bug (FIXED) **File:** `includes/Elementor/Data/ElementorData.php` **Problem:** In long-running PHP CLI scripts, WP object cache caused stale reads. After `wp_insert_post()`, WP caches `_elementor_data` as empty. Subsequent `update_post_meta()` calls write correctly to the DB but `get_post_meta()` within the same process returns the cached empty value. Each `elementor_add_container()` call then read the stale empty cache, added one element to an empty array, and overwrote the DB — only ever saving a single-element array instead of accumulating. **Symptom:** Page shows 0 root sections after a full build. DB row for `_elementor_data` is `[]` or contains only the last element added. **Fix:** Both `save_page_data()` and `get_page_data()` use raw `$wpdb` queries — bypassing WP object cache entirely — when `PHP_SAPI === 'cli'` OR `defined('DOING_CRON') && DOING_CRON`: ```php // save_page_data (CLI branch) $exists = $wpdb->get_var($wpdb->prepare( "SELECT meta_id FROM {$table} WHERE post_id=%d AND meta_key='_elementor_data' LIMIT 1", $post_id )); if ($exists) { $wpdb->query($wpdb->prepare("UPDATE {$table} SET meta_value=%s WHERE meta_id=%d", $json, (int)$exists)); } else { $wpdb->query($wpdb->prepare("INSERT INTO {$table} (post_id, meta_key, meta_value) VALUES (%d, '_elementor_data', %s)", $post_id, $json)); } wp_cache_delete($post_id, 'post_meta'); // get_page_data (CLI branch) $raw = $wpdb->get_var($wpdb->prepare( "SELECT meta_value FROM {$wpdb->postmeta} WHERE post_id=%d AND meta_key='_elementor_data' LIMIT 1", $post_id )); ``` **Rule:** Always include `define('DOING_CRON', true)` at the top of any CLI script that builds Elementor pages. --- ## 2. Page Template Validation in CLI (FIXED) **File:** `includes/Tools/PageTools.php` **Problem:** Passing `page_template` to `wp_update_post()` in CLI context throws `WP_Error: Invalid page template`. WordPress validates the template slug against registered templates, which only exist during HTTP requests (themes register them on the `init` hook, which doesn't fully fire in CLI). **Symptom:** ``` Publish error: Invalid page template. ``` Page status reverts or publish fails when template is set via `wp_update_post()`. **Fix:** Never pass `page_template` to `wp_update_post()`. Set it via `update_post_meta()` after the `wp_update_post()` call: ```php // WRONG wp_update_post(['ID' => $pid, 'post_status' => 'publish', 'page_template' => 'elementor-full-width']); // CORRECT wp_update_post(['ID' => $pid, 'post_status' => 'publish']); update_post_meta($pid, '_wp_page_template', 'elementor-full-width'); ``` `PageTools.php` has this fix applied. The template is set via meta in both `createPage()` and `updatePage()`. --- ## 3. wp_mail / sendmail Not Available in Container (FIXED) **Problem:** The `mainwp` Docker container has no `sendmail` binary. Any `wp_mail()` call in CLI context throws a PHP fatal (sendmail process fails). **Fix applied:** PHP ini override at `/usr/local/etc/php/conf.d/mcp-mail.ini`: ```ini sendmail_path = /bin/true ``` This routes all mail to `/dev/null` in CLI context. Web requests are unaffected (Apache/PHP-FPM reads the ini on startup). Also deployed as a mu-plugin: `/var/www/html/wp-content/mu-plugins/null_sendmail.php` ```php if (PHP_SAPI === 'cli') { ini_set('sendmail_path', '/bin/true'); } ``` --- ## 4. Standard CLI Script Header Every PHP script that bootstraps WordPress for CLI use must include: ```php getHandler('tool_name'), ['arg' => 'value']); ``` Errors thrown by tools are PHP exceptions. Wrap in try/catch if needed: ```php try { $r = call_user_func($reg->getHandler('elementor_add_container'), [ 'post_id' => $pid, 'settings' => ['background_color' => '#1e293b'], ]); $container_id = $r['element_id']; } catch (\Throwable $e) { echo "Error: " . $e->getMessage() . "\n"; } ``` --- ## 6. Gitea Commits via Script The `gitea_create_or_update_file` MCP tool sends `content` as a literal string — it does NOT read files from the VPS. Always provide the full file content directly in the `content` parameter. For multi-file commits, use a Node.js script that reads files from `/tmp/` and calls the Gitea API via HTTPS. --- ## 7. Elementor Build — Complete Working Pattern Full pattern for building a page with Elementor tools from a CLI script: ```php get_error_message() : strip_tags((string)$msg); throw new \RuntimeException("wp_die: $m"); }; }, 1); $reg = new \AskTamMCP\MCP\Registry(); // 1. Create page (draft, no template yet) $pid = wp_insert_post([ 'post_title' => 'My Page', 'post_status' => 'draft', 'post_type' => 'page', 'meta_input' => ['_elementor_edit_mode' => 'builder'], ]); // 2. Build with Elementor tools $hero = call_user_func($reg->getHandler('elementor_add_container'), [ 'post_id' => $pid, 'settings' => ['background_color' => '#1e293b', 'min_height' => ['size' => 100, 'unit' => 'vh']], ]); $hid = $hero['element_id']; call_user_func($reg->getHandler('elementor_add_heading'), [ 'post_id' => $pid, 'container_id' => $hid, 'title' => 'Hello World', 'tag' => 'h1', 'settings' => ['title_color' => '#ffffff'], ]); // 3. Publish — NO page_template in wp_update_post wp_update_post(['ID' => $pid, 'post_status' => 'publish']); // 4. Set template via meta AFTER publish update_post_meta($pid, '_wp_page_template', 'elementor-full-width'); // 5. Regenerate Elementor CSS if (class_exists('\Elementor\Core\Files\CSS\Post')) { (new \Elementor\Core\Files\CSS\Post($pid))->update(); } echo "Done: " . get_permalink($pid) . "\n"; ``` --- ## 8. Test Suite Two test scripts on VPS at `/tmp/`: | Script | Coverage | Expected | |---|---|---| | `test_93.php` | Original 93 tools | 74 OK / 2 test-data errors / 16 safe skips | | `test_new_tools.php` | 30 new tools | 24 OK / 3 test-data errors / 2 safe skips | Run: ```bash docker cp /tmp/test_93.php mainwp:/tmp/test_93.php docker exec mainwp php /tmp/test_93.php ``` "test-data errors" = tools that need real data (an existing order, a real product ID) to test. Not bugs. "safe skips" = destructive tools (delete page content, etc.) skipped intentionally. --- ## 9. Key Files to Check Before Making Changes | File | Why | |---|---| | `includes/Tools/ToolHandlers.php` | All 123 tools with schemas — check before adding a new tool | | `includes/MCP/Registry.php` | How tools are dispatched — check before changing routing | | `includes/Core/Auth.php` | Full auth flow — check before changing security | | `includes/Elementor/Data/ElementorData.php` | CLI cache fix — do not revert without understanding issue #1 | | `includes/Tools/PageTools.php` | Template fix — do not pass page_template to wp_update_post | | `includes/Elementor/Abilities/ElementorTools.php` | All 30 Elementor tool implementations |