Releases: bitcryptic-gw/mdf-analytics-wp
Releases · bitcryptic-gw/mdf-analytics-wp
Release list
v0.1.9
Fixed
- Backfill recovery when the scheduled
mdf_backfill_batchsingle cron event
is lost to a race with another scheduler (observed in production with Action
Scheduler).mdf_start_backfill()now verifies the event persisted after
scheduling and retries once, and a self-heal check re-schedules the batch
whenever the queue still has work but no batch event is pending (runs on the
existing purge cron and on admin page loads — no new cron schedule).
Previously the backfill could stall indefinitely with a populated queue and
no scheduled event, requiring a manual Settings-toggle re-trigger. - Per-post markdown rebuilds (
mdf_markdown_rebuild, queued onsave_post
when edited content changes) get the same protection: scheduling now verifies
the single event persisted and retries once, records a lightweight per-post
pending marker, and the self-heal check re-schedules any stale pending
rebuild whose event was lost to the same cron write race.
Added
- Persistent markdown-coverage status line on the Settings page, shown whenever
"Offer markdown to agents" is enabled: while a backfill runs it keeps the
existing in-progress counter; once complete it shows "N of M published
posts/pages have a cached markdown version." (live count of cached.md
files vs. published content eligible for conversion), or "No published
content yet to convert." on an empty site. Hidden when the toggle is off.
v0.1.6
[0.1.6] - 2026-07-16
Fixed
- Backfill batch processor (
mdf_cron_backfill_batch()) no longer counts a post as
processed when its conversion/write actually failed —mdf_backfill_processednow
reflects successful conversions only, not attempts. Previously the counter incremented
unconditionally regardless of the return value ofmdf_convert_post(), which could
make the admin UI report a completed backfill with zero cache files actually written. - Added a writability check for the markdown cache directory (
wp-content/uploads/mdf-cache/posts/)
before attempting any write, in both the batch processor andmdf_convert_post()directly.
If the directory is not writable by the web server user, the failure is now recorded and
surfaced as a visible admin notice on the Settings page (mdf_cache_writable_erroroption),
instead of failing silently. The notice self-clears once the directory becomes writable again. mdf_create_cache_dirs()now also attemptschmod 0775on the cache base andposts/
directories at creation time, as a best-effort mitigation for installs where the directory
ends up owned by a different user than the one serving requests.
Changed
- Settings page copy: "posts" replaced with "content" in the markdown-offering toggle
description and backfill progress status line, since the pipeline covers pages and
custom post types as well as posts. - Settings page "Phase 3 (roadmap)" description updated to "Phase 3 — shipped" since
the CommonMark auto-generation pipeline is now live. Phase 2 (wallet/earning) remains
the only future roadmap item.
Known limitation
- If plugin activation runs as a different user than the web server process (e.g. root during
a Docker build step, vs.www-dataserving real requests), thechmodadded above may not
be sufficient to make the directory writable, since it changes permission mode but not
ownership. The runtime writability check and admin notice are the actual safety net in that
case. A cleaner fix (explicit ownership correction, or moving directory creation to first-request
time under the serving user) may be considered for a future release if this recurs.
v0.1.5
Supersedes v0.1.4, which was cut one commit before the MDF_VERSION constant fix landed. v0.1.4 should not be used. This release includes that fix (header and constant both correctly read 0.1.4 internally — the git tag is 0.1.5 as the release identifier).
v0.1.4
Added
- Vendored
league/html-to-markdown(v5.1.1) with namespace scoping (MdfAnalytics\Vendor\League\HTMLToMarkdown) to eliminate class-redeclaration collisions if another plugin also vendors the same upstream library. No Composer required at runtime — the library is source-committed. Scoped viahumbug/php-scoper, built bybuild-vendor.sh. - Pre-build markdown cache pipeline: all published posts are converted to CommonMark on save (via WP-Cron background job) and served to clients that send
Accept: text/markdown. Markdown is never served for a URL until a pre-built.mdfile actually exists for it — no live-conversion fallback. - Cache directory at
wp-content/uploads/mdf-cache/with flatposts/layout, per-post{post_id}.md+{post_id}.meta.jsonsidecars, and amanifest.jsonaggregate. - Settings toggle "Offer markdown to agents" — enabling it auto-queues a full backfill of all published posts. Disabling stops markdown serving immediately.
save_posthook computes content hash (SHA-256 ofthe_content()post-filter output) and skips rebuilds when unchanged. Atomic temp-file + rename writes keep the old.mdservable throughout a rebuild.template_redirectnegotiation gating: checksfile_exists()against the cached.md(single filesystem stat, no sidecar JSON read at request time), setsVary: AcceptandContent-Type: text/markdownonly when cached content exists.- Backfill uses batched WP-Cron (
mdf_backfill_batch) to avoid flooding the cron queue; admin notice shows progress during backfill.
Fixed
- Manifest read-modify-write race:
manifest.jsonupdates are now wrapped withflock(LOCK_EX), preventing concurrent WP-Cron-driven writes (e.g. overlappingmdf_markdown_rebuildandmdf_backfill_batchevents) from silently clobbering each other. The lock provides advisory exclusion aroundmdf_manifest_record_result()and the backfill-completion path; the existing atomic temp-file + rename is retained for reader safety.
v0.1.3
Added
- Plugin now serves a curated
llms.txtat the site root (/llms.txt), sourced from the file shipped in the plugin directory. Supports GET/HEAD,Last-Modified/If-Modified-Sinceconditional requests, and 1-hour public caching. Requests are logged through the existing analytics classifier.
v0.1.2
Fixed
- Type-2 (known agent) UA snippet now stores the matched agent fragment rather than the first token of the raw UA string. Bots that send
Mozilla/5.0 (compatible; Googlebot/2.1; ...)style UAs now display asGooglebotin the dashboard rather than the misleadingMozillaprefix. Affects all standard search and AI crawler UAs. Historical rows are unaffected and will age out within the 90-day retention window.
Changed
mdf_classify_ua()andmdf_ua_snippet()consolidated into a singlemdf_classify_ua_with_snippet()function that performs classification and snippet extraction in one pass, eliminating redundant UA string traversal.
v0.1.1
Fixed
- WordPress core self-calls (
WordPress/), Jetpack, and uptime monitors (UptimeRobot, Pingdom, etc.) now correctly classified as type-3 (internal/monitor) and excluded from agent counts and estimated earnings. Sites running v0.1.0 will have these requests miscategorised as type-1 or type-2 in historical data; rows clear automatically within 90 days, or truncatewp_mdf_requestsmanually to reset immediately.
v0.1.0
Added
- Initial release.
- Passive request logging on WordPress shutdown hook — no content modification, no payment processing.
- Visitor classification: type-2 known agent (40+ UA fragments covering AI assistants, agentic frameworks, search crawlers, and generic HTTP clients), type-1 likely automated (heuristic — no browser engine markers), type-3 internal/monitor (WordPress platform calls, uptime monitors, CDN health probes), type-0 human (not logged unless
Accept: text/markdownpresent). wants_markdownflag logged per request whenAccept: text/markdownis present in the request headers.- 90-day retention via daily WP-Cron purge.
- Admin dashboard with 7/30/90-day window selector, seven stat cards (total requests, known agents, likely automated, internal/monitors, wanted markdown, estimated earned, estimated missed), daily bar chart, top agents table, internal/monitors table, top markdown-requested paths table.
- Settings page: currency selector (sats / USDC), configurable per-request rate for earnings estimates.
- Phase 2 callout CTA in dashboard footer.
- No IP addresses stored. No external HTTP requests. No dependencies beyond WordPress core.