Releases: Kntnt/kntnt-ai-visibility
Releases · Kntnt/kntnt-ai-visibility
Release list
v0.5.1
Fixed
- Resolved the WordPress 6.7+ "translation loading was triggered too early" notice that 0.5.0 reintroduced: the plugin logged one
_load_textdomain_just_in_timenotice on every request, whether or not any translations were installed. The Excluded paths field's label and help text are now translated lazily, when the settings page renders (afterinit), instead of while the plugin file is still being included.Settings\Fieldwas the one value object that never received the lazy-label treatmentSectionandCapability_Columngot in 0.2.2, so the two strings the 0.5.0 exclusion section introduced were resolved at bootstrap.Fieldnow accepts aClosure(): stringfor bothlabelanddescription, mirroringSection::title()andCapability_Column::label(), and unit tests guard all three against resolving a label at construction.
Full changelog: https://github.com/Kntnt/kntnt-ai-visibility/blob/v0.5.1/CHANGELOG.md
v0.5.0
Added
- Path exclusions: a new Excluded paths settings section (Settings → AI Visibility) curates individual entries out of every artifact — the per-page
.mdalternate,/llms.txtand/llms-full.txt— by listing path patterns, one regular expression per line, matched against each page's home-relative path (e.g./cookiepolicy/). Patterns are written without delimiters or flags; matching is Unicode-aware and case-insensitive, an invalid pattern is reported and dropped when you save, and changing the patterns turns the cache over so the exclusion takes effect on the next request. The sharedCore\Eligibilitygate enforces it for both the per-page.md(is_eligible()) and the aggregates (enumerate()). Two developer filters customise it in code:kntnt_ai_visibility_exclusion_patterns(amend the pattern list) andkntnt_ai_visibility_is_excluded(force a per-post verdict).
Changed
build-release-zip.shnow buildsdist/kntnt-ai-visibility.zip(creatingdist/when missing) when run with no arguments, instead of printing the usage text and exiting; an explicit destination (--output,--updateor--create) still overrides this, anddist/is git-ignored.
Full changelog: https://github.com/Kntnt/kntnt-ai-visibility/blob/v0.5.0/CHANGELOG.md
v0.4.0
Added
- The Link-headers module (Release 3): RFC 8288 HTTP
Linkheaders that advertise every registered artifact on HTML responses — each singular page's Markdown alternate (rel="alternate",type="text/markdown") and the site-wide/llms.txtand/llms-full.txtsingletons (rel="related",type="text/plain"). Headers are emitted on every HTML response throughsend_headersand skipped on admin, REST, feed,robots.txtand 404 responses; the early-router artifact path is deliberately left undecorated.Discovery_Contextgains a nullable post so a provider can answer both the per-page and the site-wide discovery call, and relations are de-duplicated by the full (href,rel,type) triple. - The Content Signals module (Release 4): a site-wide, tri-state
Content-Signaldeclaration spliced into the virtualrobots.txtunderUser-agent: *, using the Content Signals Policy vocabulary (search/ai-input/ai-train). Each signal is grant (yes), reserve (no) or defer (omitted); the zero-config defaults –search=defer,ai-input=grant,ai-train=defer – emit the single lineContent-Signal: ai-input=yes. An all-defer policy emits nothing and the block is suppressed entirely on a non-public site (blog_public = 0); a new "AI usage" settings section carries the three controls with inline warnings on visibility-reducing choices. A developer filter (kntnt_ai_visibility_content_signals) overrides the resolved policy in code. Specified indocs/spec/content-signals.mdand ADR-0012; theCONTEXT.mdglossary gains the term Content signal. - Developer-filter reference:
docs/EXTENSIBILITY.mddocuments the plugin'skntnt_ai_visibility_*filters – the customisation surface for the Markdown alternates,llms.txt/llms-full.txt, the content signals and caching – with worked examples, and the README gains an Extending section that points to it.
Changed
- The GitHub self-update check is now cached behind a filterable TTL (default six hours,
kntnt_ai_visibility_update_check_ttl) and pinned to the expected GitHub host, so the plugin polls less often and ignores a response served from an unexpected host. - Less filesystem work on the cache-hit serve path: the cache-base real path is memoised and a response's
ETagis computed only when it is needed.
Fixed
- Control characters in a post's title, author or term names are now escaped in the Markdown front matter, so a stray control byte cannot break the YAML block.
/llms.txtno longer emits a section header for a content type that has no eligible posts.- Conditional requests honour a comma-separated
If-None-Matchlist and weak ETags, so a matching validator returns304as it should. - The declared WordPress requirement is corrected to 6.7, from an erroneous 7.0, matching the actual minimum.
Security
- The canonical host used in
Linkheaders and advertised URLs is taken from the site configuration rather than the incomingHostheader, so a spoofedHostcannot influence what the plugin advertises. - Single-flight lock files are written to a namespaced, restricted directory, preventing cross-install collisions and tightening the lock-file location.
Full changelog: https://github.com/Kntnt/kntnt-ai-visibility/blob/v0.4.0/CHANGELOG.md
v0.2.2
Fixed
- Resolved the WordPress 6.7+ "translation loading was triggered too early" notice. The settings-matrix column headers and the section heading are now translated lazily, when the settings page renders (after
init), instead of when the plugin boots — so no translation is requested beforeinit.
Full changelog: https://github.com/Kntnt/kntnt-ai-visibility/blob/v0.2.2/CHANGELOG.md
v0.2.1
Changed
- Lowered the minimum PHP version from 8.5 to 8.4. The floor came entirely from the bundled
kntnt/html-to-markdownconverter's use of the PHP-8.5-onlyUri\Rfc3986\Uriclass for relative-URL resolution; the converter now hand-ports RFC 3986 §5.2 reference resolution instead (byte-for-byte-identical output, verified by its golden fixtures), leaving the nativeDom\HTMLDocumentHTML5 parser (PHP 8.4) as the only floor driver. The bundled converter is bumped to^0.1.3, and CI, PHPStan'sphpVersion, the PHPCStestVersionand the Playground e2e harness now target PHP 8.4.
Full changelog: https://github.com/Kntnt/kntnt-ai-visibility/blob/v0.2.1/CHANGELOG.md
v0.2.0
Added
- This changelog.
CONTRIBUTING.mdwith contribution and pull-request guidance.- README sections: Questions, bugs, and feature requests, How you can contribute, and Changelog.
- Modular architecture and design (a Core plus four feature modules) documented in
docs/architecture.md, theCONTEXT.mdglossary and architecture decision records underdocs/adr/. - The coding standard is now materialised in the repository under
agents.d/coding-standard/– one module per language axis (general, PHP, WordPress and Bash) plus a privatemanifest.jsonsnapshot – and loaded on demand rather than held in context every session. - On-demand agent guides under
agents.d/:writing-standard.md,releasing.mdandtesting.md, each linked from theAGENTS.mdReferences index. - Step 1.3 specification for Release 1 – the Markdown alternate – with concrete Core-slice and module contracts in
docs/spec/markdown-alternate.md. - Core foundation for Release 1: the
Moduleboot contract and theCoreservice facade; an artifact-provider registry with its identity, request, serve-pattern and link-relation value objects; a file-backed artifact cache and an early, hardened serve router that contains every request inside the cache directory (adversarially tested against path traversal, encoded traversal, null bytes and symlink escape); a zero-config settings registry over the singlekntnt_ai_visibilityoption; and a visitor-silent logger. - Markdown-alternate generation: the shared Page-Markdown service that renders a post through
the_content, converts the HTML to GitHub-Flavored Markdown (tables and strikethrough included) with relative URLs absolutised against the site, and assembles the YAML front-matter (title,canonical_url,date,author, and conditionalfeatured_image,categories,tags) followed by the page's visible H1 and body, with single-flight caching; plus the Markdown-alternate provider and its eligibility rule (single, public, published, front-end-viewable entries — including pages, posts and public custom post types — with the static-home/index.md), resolving the target viaurl_to_postid()with hierarchical-page and published-post-slug fallbacks so a.mdrequest still resolves when the rewrite has steered WordPress's main query to the front page. - Markdown-alternate serving and discovery: the request handler with strict content negotiation (
.mdURL >?format=markdown>Accept: text/markdown), the inline uncachedAcceptform carryingVary: Acceptand a steering alternate link,Content-Type/Content-Length/Last-Modified/ETagheaders with conditional304s, a403for password-protected content, a301for a trailing-slashed.mdURL, and canonical-redirect suppression; the per-page<link rel="alternate" type="text/markdown">discovery tag onwp_head; delete-on-change invalidation (per entry on save and status transition, whole-cache flush on theme switch and settings change) with a cache-version stamp; and a filterable TTL safety net on the serve router. - The Markdown-alternate module is now wired into the plugin: the
Pluginbootstrap builds the Core service graph and boots the module, and the early serve router runs from the main plugin file before WordPress routing — a cached.mdrequest is served straight from disk and the WordPress lifecycle skipped, while a miss falls through to lazy generation. - A content-type settings section on the plugin's settings page: a Core-owned capability matrix — one row per front-end-viewable post type with a checkbox column per artifact kind a module registers (the Markdown
.mdcolumn to begin with) — replacing the Markdown post-type text field, with the.mdselection mirrored by thekntnt_ai_visibility_eligible_post_typesfilter and defaulting to every viewable type, plus a Clear cache button beside it that flushes every cached file. - Release-2 specification for the llms.txt module – the singleton
llms.txtandllms-full.txtartifacts and the Core extensions they need (the content-type matrix, the markdown-alternate locator, the single-flight materialiser and the exact-path serve router) – indocs/spec/llms-txt.md. - A behavioural WordPress Playground end-to-end test (
tests/Integration/playground-e2e.sh) that boots a real Playground HTTP server with the plugin mounted and fixtures seeded, then drives the request lifecycle over HTTP — a real.md(200,text/markdown, front-matter, the converted and absolutised body),?format=markdown,Acceptnegotiation (Vary, the steering alternateLink),/index.md, a 404 for ineligible content, a 403 for password-protected content, a 301 for a trailing slash, and path-traversal payloads that never leak; and the llms singletons —/llms.txt(200,text/plain, the curated index with its sections and.mdlinks, no canonical orVary),/llms-full.txt(the concatenated Pages-only full text), the early-router cache hit with a stableETagand a conditional304,HEAD, the password-protected and draft content absent from both files, and traversal/evasion payloads against the singletons — wired intorun-tests.shand the CI e2e job alongside the boot smoke test. - The Playground e2e now also covers an invalidation round-trip — a mid-run content change (through a test-only mu-plugin endpoint) bumps the cache version, the rebuilt
/llms.txtreflects the change and the orphaned previous-version aggregate is pruned — and a second, separate boot under a/subsubdirectory (tests/Integration/playground-e2e-subdir.sh) that proves the per-page.mdand the llms singletons resolve home-relative and stay contained when WordPress lives under a path. - Subdirectory-install support: cache keys and request resolution are taken relative to the WordPress home (the serve router strips the same base path), so both per-page
.mdand the home/index.mdresolve and cache correctly when WordPress lives under a path such as/blog/, not only at the domain root. - The llms.txt module – two discoverable, machine-readable files for AI agents.
/llms.txtis a curated Markdown index: an H1 site name, the tagline, an intro line pointing at the full file, then one section per content type listing each page as a link to its.mdalternate followed by its excerpt (titles escaped so they cannot break the link, excerpts stripped and length-capped)./llms-full.txtis the site's selected pages concatenated as Markdown, assembled from the per-page.mdalternates and never re-rendered. Both are served astext/plain; charset=utf-8straight from the early cache router on a warm hit – which gains an exact-path match mode alongside the.mdsuffix match, with the same path-traversal hardening – and generated lazily on a miss; they are invalidated by a cache-version bump when a public post is published, edited, unpublished or trashed (so a page that leaves public view is dropped promptly), with the existing TTL as a safety net, and never include password-protected content. Which content types appear in each file is the In llms.txt and In llms-full.txt columns of the content-type matrix (every viewable type inllms.txt, Pages only inllms-full.txtby default), each mirrored by a developer filter (kntnt_ai_visibility_llms_post_types,…_llms_full_post_types), with per-entry, title, summary, intro, sections and whole-document filters for full customisation. - README section Serving cached Markdown directly documenting the optional web-server tier (nginx and Apache
try_filesexamples) that serves the cached.mdfiles straight from disk, bypassing PHP entirely, with a fall-through to WordPress on a miss; it also notes that/llms.txtand/llms-full.txtare early-served from the PHP cache but, because their cache filename carries a version stamp a static rule cannot resolve, are not eligible for that no-PHP static tier.
Changed
- The llms request handler now prunes the stale, version-stamped aggregate cache files that a cache-version bump orphans (for example
llms-txt/llms-v7.mdafter a bump to v8), instead of leaving them until the TTL safety net or a cache flush removes them; pruning is scoped to the one aggregate directory and contained within the cache base, so it never touches the per-page.mdcache. - Release tags are now
v-prefixed (vX.Y.Z) and the GitHub-release notes are taken from the matchingCHANGELOG.mdsection rather than GitHub's auto-generated digest (ADR-0011; ADR-0005 amended): the release workflow matchesv[0-9]+.[0-9]+.[0-9]+, strips thevfor the header-versus-tag check, andbuild-release-zip.sh --createpublishes the changelog section as the release body. - Activation now registers the
.mdand the/llms.txt//llms-full.txtrewrite rules and flushes them once; deactivation flushes the rewrite rules and clears the file cache while preserving the settings option; uninstall deletes thekntnt_ai_visibilityoption and the cache-version option and removes the file cache directory — replacing the scaffold's transient-based cleanup, since the cache is files under uploads rather than transients. - Documentation now follows British English via the
kntnt-text-skills:writing-rules en_GBstandard; the README uses spaced en-dashes ( – ) throughout. - Bumped
actions/checkoutandactions/setup-nodeto v5 (Node 24 runtime). AGENTS.mdslimmed to an always-loaded canon – authoritative ground rules, the non-obvious project facts and a References index – andCLAUDE.mdreduced to a single@AGENTS.mdbridge, cutting the always-loaded agent context by about 95 %.README.mdandCONTRIBUTING.mdnow point atagents.d/coding-standard/for the coding standard and describe the agent-context files accurately.
Removed
docs/coding-standards.md– the monolithic coding standard, superseded by the on-demand modules under `agents.d/coding-standar...
0.1.0
Full Changelog: https://github.com/Kntnt/kntnt-ai-visibility/commits/0.1.0