03/15/26 — 03/16/26 — Post-Submission Bug Fixes & New Features
03/15/26 — “Publishing failed” error with WordPress 6.9.4
- Root cause:
transition_post_statushook was making synchronous HTTP calls to the Inkwell API inside the Gutenberg REST API request, blocking the response and triggering a publish failure - Fix: detect
REST_REQUESTand defer crosspost execution usingwp_schedule_single_event+spawn_cron() - Follow-up: cron never fired on host due to blocked loopback HTTP requests — replaced with
shutdownaction hook ignore_user_abort(true)added to keep DB connections alive after response is sentfastcgi_finish_request()called when available so PHP-FPM flushes the response to the browser before the crosspost runs- Wrapped shutdown execution in
try/catch (\Throwable)to surface silent fatal errors - Fixed
add_transient()unavailable in shutdown context afterfastcgi_finish_request()— replaced transient lock with directget_post_meta()check andforce=truecall in shutdown path - Retained cron hook registration for any events already queued from previous version
03/15/26 — Debug Log page
- New admin page at
Settings → Crosspost to Inkwell → 🪲 Debug Log - Enable/disable toggle — off by default to avoid storing sensitive data unnecessarily
- Dark terminal-style log viewer, color-coded: green for success, red for errors, blue for outgoing requests, orange for API responses
- Refresh button (live AJAX reload) and Clear Log button with confirmation prompt
- Log stored in
wp_optionsunderinkwell_debug_log, capped at 200 entries - Debug Log link added to settings page header and plugin action links row
- 26 log points added throughout the crosspost flow:
- Cron/shutdown deferral and execution
- Crosspost start (post ID, force flag, post status)
- Transient lock and already-posted guard hits
- Every outgoing API request (method, URL, payload keys)
- Every API response (HTTP status code + first 600 chars of body)
- Image upload — file read, encode, upload, result
- Crosspost success with entry URL
- All error and skip paths
03/15/26 — Verify Key: reads live field value
inkwell_ajax_verify_keywas reading the API key only from savedwp_options, so clicking Verify Key before saving always returned “No API key entered”- Fix: JS now reads the current value of
#inkwell_api_keyand passes it in the AJAX request; PHP handler uses the submitted key if present, falls back to saved option
03/15/26 — API key field: show/hide eye toggle
- Added 👁 / 🔒 toggle button inline with the API key
<input type="password">field - Button switches field between
passwordandtexttype on click aria-labelupdates on toggle for screen reader accessibility- Typing in the key field clears the connected account status display so re-verification is obvious
03/16/26 — Persistent connected account display
- After a successful Verify Key, the connected account name and handle are now saved to
wp_optionsand displayed persistently on page load — no longer disappears on refresh connected_display_nameandconnected_usernameadded to plugin options defaults and sanitize callbackinkwell_ajax_verify_keyrefactored to make a single/api/mecall (previously called it twice — once ininkwell_verify_credentials()and once again to extract data), saving account info and building the response message from the same result- On verify failure, saved account info is cleared so stale data from a previous key is never shown
- Sanitize callback updated to pass through
connected_display_nameandconnected_usernamefrom$inputwhen present — previously always read from$existing(old DB value), silently overwriting the newly saved name on everyupdate_optioncall wp_kses()used instead ofsanitize_text_field()for display name to preserve emoji and Unicode characters (e.g.ΞVΞ🦋)- API key change (via settings save) clears connected account info so stale name from previous key is never displayed
Marginalia1
What I found funny was it took me several prompts working with Claude to find the cause of the publishing error. “He” kept guessing, going back and forth saying it was this or that and never got it right. It wasn’t until I told him to okay, create a debug log, I then used that debug log in WordPress that printed the silent errors that were going on. Only then was Claude able to figure out what the problem was to fix it. Goes to show that AI can’t proactively reason for itself (at least not yet). It still needs a human to guide it, so you still need some kind of a basic idea about how coding works , or it will just have you going around in circles like a dog chasing its tail.
Luckily, Wordpress hadn’t reviewed my plugin submission yet so I was able to make the corrections before it got published.