Repository navigation
wp_options row keys silently lost after Studio site restart (partial options corruption) #52
Description
Summary
On a Studio site running markdown-database-integration, some recently-added keys inside a serialized array option vanished after a studio site stop + studio site start cycle. Not the whole option row — the row persists with its original keys, but a subset of keys added within the option's PHP-serialized array value silently disappear.
This is observably distinct from, but likely related to, #47 (cold-boot wp_posts corruption via ALTER TABLE CHANGE). That issue corrupts a whole table; this one drops specific keys from a serialized array inside a single option row.
Observed reproduction (not yet 100% deterministic)
Setup
- Studio site
intelligence-chubes4running markdown-database-integration (development checkout at/Users/chubes/Developer/markdown-database-integration/symlinked into the site) - Data Machine installed with
datamachine_settingsoption storing a PHP-serialized array of scalar + array settings - Studio dev build (combined fixes branch)
Sequence that triggered the loss
- Via
wp datamachine settings set, set these keys sequentially:default_model = gpt-5.4-minidefault_provider = openaidaily_memory_enabled = truecontext_models = { pipeline: {...}, chat: {...}, system: {...} }agent_models = { ... }(auto-mirrored from context_models by the DM settings ability)
- Ran
studio site stop - Ran
studio site start - After restart, inspected
get_option('datamachine_settings')
Result
Four of the nine keys were missing from the post-restart option value. The surviving keys were the ones present in the option before this batch of writes (disabled_tools, enabled_pages, site_context_enabled, cleanup_job_data_on_failure). Only default_model — the first of the new keys written — survived. default_provider, daily_memory_enabled, context_models, agent_models were all gone.
Second restart test (not reproducing on this attempt)
Re-added the missing keys. Stopped + started the site again. All 9 keys survived.
So this is intermittent or tied to a specific boot codepath, not every stop/start cycle. Suspected trigger: cold boot where SQLite index is rebuilt from markdown files, vs warm boot where the index is reused. The first restart (which triggered the loss) may have been a cold-boot case because the dev CLI had been freshly rebuilt and a marker file may have been invalidated.
Why this is probably markdown-database-integration
- The wp_options row itself persists — this isn't a Studio-injects-fresh-WP problem.
- The loss is at the serialized-array-key level — something is deserializing, dropping keys, and re-serializing. That matches the MDI markdown-to-SQLite reload pattern more than any other component on the stack.
- Related existing bug: issue Plugin table schema loading corrupts wp_posts data during cold boot #47 documents ALTER TABLE CHANGE (MySQL syntax) failing in SQLite, triggering
rebuild_information_schema()which corruptswp_posts. A similar fallback corruptingwp_optionsrow contents is a plausible mechanism. - If
datamachine_settingshas a corresponding markdown-backed source file and MDI reloads it on boot, stale content from before the recent writes could overwrite fresh in-DB content. Worth checking whether wp_options rows are tracked by MDI at all and how they round-trip.
Why this matters
Local Studio development of any plugin that uses serialized-array options (most of WordPress) is exposed to silent data loss on restart. Symptoms are hard to diagnose because:
- The option row is still present
- Older keys are still present
- Only the most-recently-added or most-recently-modified keys vanish
- Nothing fails loudly — the plugin reads the option, finds a partial config, and behaves unexpectedly
This hit Data Machine twice in a 30-minute session today (first time explained by a full cold boot + the SQLite index delete workaround for #47; second time inexplicable to me without reading MDI's internals).
Diagnostic information
Option state immediately before failure
9 keys inside datamachine_settings:
disabled_tools, enabled_pages, site_context_enabled, cleanup_job_data_on_failure,
default_model, default_provider, daily_memory_enabled, context_models, agent_models
Option state after the restart that triggered loss
disabled_tools, enabled_pages, site_context_enabled, cleanup_job_data_on_failure,
default_model
wp_options row count and related rows (after re-adding missing keys)
- Total
wp_options: 178 datamachine_settingslength: 236 bytes (matches all 9 keys serialized)chubes_ai_http_shared_api_keyslength: 192 bytes (survived in both cases)- Note:
datamachine_network_settingswas already missing — never set, so not a data loss.
Environment
- Studio dev build (1.7.8-beta4), combined-fixes branch
- Playground combined-fixes branch with PR #3494 merged
- SQLite via markdown-database-integration + bundled sqlite-database-integration
- No db.php drop-in customizations beyond what MDI installs
Requests
- Confirm whether wp_options rows are round-tripped through markdown files by MDI. If yes, describe where
datamachine_settings-equivalent option content would be written to disk. If no, investigate what other MDI mechanism could partially clobber an option value. - Check the cold-boot vs warm-boot path distinction for option rehydration. The first restart in my session may have been a cold-boot equivalent (SQLite index stale or loader forced rebuild) while the second was warm. If there's a branch where MDI reads pre-existing values from disk and the disk content is stale, that's the failure mode.
- Relationship to Plugin table schema loading corrupts wp_posts data during cold boot #47. Is this the same root cause manifesting in a different table? Or a separate fallback path?
Testing clue for whoever picks this up
If the "trigger is cold boot where index is rebuilt" hypothesis is right, the simplest deterministic reproduction is:
- Set the options
- Delete
wp-content/markdown-index.sqlite(forces cold boot on next start, per Plugin table schema loading corrupts wp_posts data during cold boot #47 workaround) - Start the site
- Check options — the vanished ones should match the pattern observed here (recently-added keys inside a serialized array option)
I haven't tried this yet — happy to when time allows, but it's the first experiment worth running.
Related
- Plugin table schema loading corrupts wp_posts data during cold boot #47 — Markdown DB loader corrupts wp_posts during cold boot (ALTER TABLE CHANGE / rebuild_information_schema fallback)
Activity
chubes4 commentedon Apr 17, 2026
Root cause identified — not an MDI bug
Spent a session reproducing this today. The filed hypothesis (
register_shutdown_functionnot firing) turned out to be wrong. The real cause is Studio issue #3095 — Studio overwriteswp-content/db.phpon site start, reverting it to stock SQLite integration. MDI silently stops loading.The sequence that produces the symptom
studio site start→ Studio replaces MDI'sdb.phpdrop-in with stock SQLite integrationwp option set/update_option()writes go towp-content/database/.ht.sqlite(stock SQLite), not to MDI's in-memory DBWP_Markdown_Write_Enginenever sees those writes, sooptions.jsonis never re-flushedoptions.jsonto rehydrate SQLite → recent option keys are missingWhat made it look like a shutdown-flush bug
The smoking-gun observation in the original report was "options.json mtime is hours old despite successful WP-CLI writes." That looked like the
register_shutdown_functionflush wasn't firing. What was actually happening:db.phphad been overwritten hours earlierVerification that shutdown hooks are fine
Tested both
register_shutdown_functionandadd_action('shutdown', ...)understudio wp eval— both fire correctly. The earlier "shutdown not firing" test was a false negative caused by writing probe logs to/tmp, which Studio doesn't mount into the Playground WASM filesystem (paths outside the site folder silently fail). Writing probes intowp-content/confirmed shutdown hooks run cleanly on both web requests and CLI invocations.Deterministic repro of the full lifecycle with MDI properly loaded
After restoring MDI's
db.phpand confirming it's the active drop-in:wp option patch insert datamachine_settings final_repro_check "value-$(date +%s)"→ immediately visible inoptions.json(write engine flushed on shutdown) ✅studio site stop→db.phppreserved (on Studio dev fork with #3095 merged)rm wp-content/markdown-index.sqlite→ force cold boot on next startstudio site start→ cold boot rehydrates fromoptions.jsonwp option get datamachine_settings→final_repro_checksurvives ✅No data loss. The write engine works as designed.
Fix / recommendation
This issue is resolved upstream by Studio #3095 (preserve custom db.php drop-ins). On the Studio dev fork where #3095 is merged, the symptom does not reproduce.
For users on vanilla Studio, the workaround until #3095 lands is to re-copy MDI's
db.phpintowp-content/db.phpafter everystudio site start. A defensive option in MDI would be to verify the drop-in is still MDI's on plugin load and re-install if not, but that feels like papering over a Studio bug that already has a fix in flight.Closing unless there's a scenario I missed where MDI alone reproduces the loss.
Related
chubes4 commentedon Apr 21, 2026
Closing — root-caused as Studio #3095 (Studio overwrites
wp-content/db.phpon site start, reverting MDI's drop-in to stock SQLite integration). Fixed upstream on the Studio dev fork where #3095 is merged; the symptom does not reproduce there.Not an MDI bug. The original hypothesis about
register_shutdown_functionwas a false lead — shutdown hooks fire correctly, the earlier probe was just writing logs to/tmpwhich Studio doesn't mount into the Playground WASM filesystem (silent fail).See the full repro + resolution in the comment above.