Title: Semantic Journey
Author: hdfraser
Published: <strong>July 17, 2026</strong>
Last modified: July 17, 2026

---

Search plugins

![](https://ps.w.org/semantic-journey/assets/banner-772x250.png?rev=3611664)

![](https://ps.w.org/semantic-journey/assets/icon-256x256.png?rev=3611664)

# Semantic Journey

 By [hdfraser](https://profiles.wordpress.org/hdfraser/)

[Download](https://downloads.wordpress.org/plugin/semantic-journey.1.7.1.zip)

 * [Details](https://wordpress.org/plugins/semantic-journey/#description)
 * [Reviews](https://wordpress.org/plugins/semantic-journey/#reviews)
 *  [Installation](https://wordpress.org/plugins/semantic-journey/#installation)
 * [Development](https://wordpress.org/plugins/semantic-journey/#developers)

 [Support](https://wordpress.org/support/plugin/semantic-journey/)

## Description

Semantic Journey answers questions about your content that conventional analytics
answers badly or not at all: what does this content experience look like for real
readers, and did they reach the actions that matter to you? Not how many events 
fired, how long sessions lasted, or how you compare to a benchmark.

It is a plain-language, privacy-first alternative to conventional analytics for 
editorial teams, built around a fixed contract:

 1. **Semantic, not mechanical.** “Readers reached the end”, never “scroll depth exceeded
    80%”.
 2. **Understandable without training.** No analytics vocabulary, no dashboards that
    require interpretation skills.
 3. **Outcome-first language.** Progress, completion, confidence, clarity, friction.
    Never metrics, events, rates, or funnels.
 4. **Privacy-first by design.** No personal data, no identifiers, no cookies, no user
    tracking, nothing sent to third parties.
 5. **Aggregate-only signals.** No sessions, no individuals, no cross-page journeys,
    no behavioral profiling.
 6. **Liftable by default.** Every module is self-contained and works independently
    of any other plugin.

Semantic Journey works on any public post type, not only posts and pages: products,
portfolio items, or any custom post type your site uses.

#### Reader Progress (module 1)

The reference implementation of the contract above. Reader Progress reports four
fixed milestones for each piece of content:

 * **Started Reading.** The reader began engaging with the main content.
 * **Reached the Middle.** The reader progressed through roughly half of the content.
 * **Read Most.** The reader consumed the large majority of the content (more forgiving
   than strict completion).
 * **Reached the End.** The reader reached the conclusion.

Detection is content-relative, using IntersectionObserver against the primary content
container, never scroll events, scroll percentages, time-on-page, or mouse movement.
Milestones fire at most once per page view and are recorded as counts only: no per-
event timestamps, no session or user identity of any kind.

#### Decision Point (module 2)

A Decision Point marks the moment a reader reached a site-owner-defined action: 
a “Buy Now” button, a survey link, a phone number. Unlike Reader Progress’s four
fixed milestones, Decision Point labels are yours to define, and a Decision Point
can be reached more than once per page view since each click is a genuine, separate
signal.

Four ways to mark a Decision Point, in order of precedence:

 * A `data-sj-decision="Label"` attribute added to any element, for precise control
   from your theme or page builder.
 * Phone number links (`tel:`), email links (`mailto:`), and text message links (`
   sms:`), all detected automatically with no configuration.
 * An admin-defined CSS selector rule (Settings > Decision Point), matching any 
   element, not only links. This is the option for AJAX-submitted forms, Add to 
   Cart buttons, or anything a page builder generates without a real link underneath
   it.
 * An admin-defined URL pattern rule (Settings > Decision Point), for marking real
   links by address without touching a template.

Decision Point data appears as additional columns on the same Reports screen as 
Reader Progress, and in the same CSV export, built dynamically from whichever labels
actually have data in the selected time window.

#### Reports

A dedicated Reports screen shows, for any content item, how many readers started,
reached the middle, read most, or reached the end, over a 7/30/90-day window, plus
a column for each Decision Point label with data in that window, with search, column
sorting, and a CSV export. The four Reader Progress columns use plain-language milestone
names; there are no rates, percentages, or scores to interpret.

#### Copy prompt for your LLM

The Reports screen can build a plain-text summary of exactly what is currently displayed(
the full filtered set, not just the current page) for you to paste into an LLM of
your choice, if you want a plain-English interpretation of the counts. This plugin
does not send this text anywhere itself, there is no outbound link and no third-
party integration; it copies to your clipboard only, and where you paste it afterward
is entirely your own choice. The built-in instructions ask the LLM not to invent
rates or percentages and not to assert a definite cause for any pattern, matching
this plugin’s own approach to the same data.

#### On the post editor

A small sidebar box on the post/page edit screen (and any other trackable post type)
confirms whether Reader Progress and Decision Point are recording for that piece
of content, showing 30-day counts for each, with a link straight to the filtered
Reports screen. Read-only, no fields to fill in.

#### On the Posts and Pages list, and the Dashboard

A Semantic Journey column on the Posts/Pages list table (and any other trackable
post type) gives an at-a-glance summary without opening each item. A small Dashboard
widget shows the top 5 pieces of content by readers reaching the end, over the last
7 days.

#### Privacy and external services

This plugin does not send any data to third-party services. All data collected by
Reader Progress stays in your own WordPress database, in a dedicated table, as aggregate
daily counts. Nothing is shared externally, and no cookies or personal identifiers
are used at any point.

#### For developers

Other plugins can integrate with Semantic Journey through a small, stable public
API (see includes/functions.php):

 * `semantic_journey_is_module_enabled( string $module, $context = null ): bool`
 * `semantic_journey_reader_progress_get_summary( int $content_id, int $days = 30):
   array`
 * `semantic_journey_decision_point_get_summary( int $content_id, int $days = 30):
   array`
 * the `semantic_journey_module_enabled` filter, for applying your own licensing
   or per-post rules on top of the site-level toggle.

Semantic Journey itself has no concept of licensing, plans, or host-specific gating;
that interpretation belongs entirely to the integrating plugin.

## Screenshots

[⌊Reader Progress report: plain-language milestone counts per content item, with
a date-range filter, search, and CSV export.⌉⌊Reader Progress report: plain-language
milestone counts per content item, with a date-range filter, search, and CSV export
.⌉[

Reader Progress report: plain-language milestone counts per content item, with a
date-range filter, search, and CSV export.

[⌊Settings screen: module toggles, retention window, and uninstall data-removal 
preference.⌉⌊Settings screen: module toggles, retention window, and uninstall data-
removal preference.⌉[

Settings screen: module toggles, retention window, and uninstall data-removal preference.

## Installation

 1. Upload the `semantic-journey` folder to `/wp-content/plugins/`, or install it via
    Plugins > Add New > Upload Plugin.
 2. Activate Semantic Journey through the Plugins menu. Activation creates the `wp_reader_progress_daily`
    and `wp_decision_points_daily` tables and schedules their daily retention-purge
    events.
 3. Go to Semantic Journey > Settings to confirm Reader Progress and Decision Point
    are enabled, set your retention windows, and optionally add Decision Point URL 
    pattern rules.
 4. Visit Semantic Journey > Reports to see reader activity once readers have visited
    your content. No further configuration is required; Reader Progress and automatic
    tel:/mailto:/sms: link detection both work immediately after activation.

## FAQ

### Why did a reader’s milestone or click not get recorded?

The most common reason is that JavaScript or a required browser feature was unavailable,
described elsewhere in this FAQ. One other case: each submission carries a short-
lived security token, similar to how WordPress protects its own forms, which identifies
nothing about the visitor but expires roughly a day after the page loaded. If a 
browser tab is left open far longer than that before a reader reaches a milestone,
that one signal is silently not recorded rather than recorded against an expired
token. This is intentional: a missing signal in that rare case is preferable to 
weakening the same check for every other visitor.

### Does the Copy prompt for your LLM feature send my data anywhere?

No. It builds a plain-text summary and copies it to your clipboard only. There is
no outbound link, no API call, and no third-party integration built into this feature.
Whether you paste that text into an LLM afterward, and which one, is entirely your
own separate choice.

### Does this replace Google Analytics or a similar tool?

No. It answers a narrower, editorial question and is not a general-purpose analytics
platform. It has no sessions, no user identity, and no cross-page journeys by design.

### Does Reader Progress use cookies?

No. It stores no identifiers of any kind, and no data is shared with any third party.

### What happens if a reader has JavaScript or IntersectionObserver disabled?

The module silently disables itself for that page view. No partial or misleading
data is ever recorded.

### How long is data kept?

90 days by default, on a rolling basis, configurable in Semantic Journey > Settings.
Older rows are purged automatically once a day.

### What happens to my data if I uninstall the plugin?

By default, nothing is removed, in case you are reinstalling or upgrading. If you
want your data removed on uninstall, enable “Remove data on uninstall” in Settings
first.

### Can a Decision Point be reached more than once by the same visitor?

Yes. A Decision Point is a click on an action, not a reading milestone, so each 
click is counted, matching how a conversion or goal click is normally understood.
This is a deliberate difference from Reader Progress’s four milestones, which each
fire at most once per page view.

### Why isn’t my button being recorded as a Decision Point?

URL pattern rules only match real links (an element with an href). Many buttons,
especially ones generated by page builders or ones that submit a form via AJAX, 
are not links at all. For those, use a CSS selector rule instead (Settings > Decision
Point), which matches any element, or add a `data-sj-decision="Label"` attribute
directly to the button. If you configured a CSS selector rule and it still is not
firing, check your browser’s console: an invalid selector is reported there rather
than failing with no explanation at all. If the element is a phone number, email,
or text message link, no rule is needed at all, those three are detected automatically.

### Does this work on a WordPress multisite network?

Yes. Activating network-wide sets up every existing site, and any site created afterward
on a network-active install is set up automatically. Each site’s data stays separate;
there is no cross-site rollup in the free version.

### Does this affect my privacy policy?

Semantic Journey adds a short, accurate entry to Tools > Privacy > Policy Guide 
describing what it does: it does not collect personal data of any kind. You can 
include this text in your site’s privacy policy as-is or edit it to match your own
wording.

## Reviews

There are no reviews for this plugin.

## Contributors & Developers

“Semantic Journey” is open source software. The following people have contributed
to this plugin.

Contributors

 *   [ hdfraser ](https://profiles.wordpress.org/hdfraser/)

[Translate “Semantic Journey” into your language.](https://translate.wordpress.org/projects/wp-plugins/semantic-journey)

### Interested in development?

[Browse the code](https://plugins.trac.wordpress.org/browser/semantic-journey/),
check out the [SVN repository](https://plugins.svn.wordpress.org/semantic-journey/),
or subscribe to the [development log](https://plugins.trac.wordpress.org/log/semantic-journey/)
by [RSS](https://plugins.trac.wordpress.org/log/semantic-journey/?limit=100&mode=stop_on_copy&format=rss).

## Changelog

#### 1.7.1

 * Email links (mailto:) and text message links (sms:) now join phone number links
   at the automatic, zero-configuration Decision Point detection tier.

#### 1.7.0

 * Added CSS selector rules to Decision Point (Settings > Decision Point): a fourth
   way to mark a Decision Point that works on any element, not only links. Closes
   a real gap for AJAX-submitted forms, Add to Cart buttons, and anything a page
   builder generates without a real href underneath it.
 * Invalid CSS selector rules are now reported in the browser console rather than
   failing silently.

#### 1.6.4

 * Replaced an inline closure used to sanitize a settings checkbox with WordPress’s
   own rest_sanitize_boolean(), also fixing an edge case where the literal string“
   false” would have been treated as enabled.

#### 1.6.3

 * Fixed a CSV formula injection risk (CWE-1236) in the Reports CSV export: post
   titles and Decision Point labels are now protected against being interpreted 
   as spreadsheet formulas when the exported file is opened. Found proactively, 
   not from an external report.
 * Added a UTF-8 BOM to the CSV export so non-ASCII characters display correctly
   in Excel.

#### 1.6.2

 * Both REST endpoints (Reader Progress, Decision Point) now require a nonce, verified
   server-side, in addition to remaining reachable by anonymous visitors as intended.
   This closes a gap where either endpoint could previously be scripted against 
   directly with no prior reconnaissance at all. Identifies nothing about the visitor
   and requires no cookie or login state.
 * Fixed a sanitization gap: a setting’s array keys were only cast to strings rather
   than sanitized.
 * Corrected the Contributors field in this readme.

#### 1.6.1

 * Fixed an escaping issue in the Posts list column flagged by Plugin Check: output
   is now escaped once, correctly, at the actual point of output.
 * Documented one read-only display-filter nonce warning introduced by the Copy 
   prompt feature’s search handling.

#### 1.6.0

 * Added a branded admin page header shared across Reports and Settings.
 * Added “Copy prompt for your LLM” on Reports: builds a plain-text summary of the
   currently filtered report for you to paste into an LLM of your choice. Copy only;
   this plugin sends nothing anywhere itself.
 * Accessibility fixes found in a dedicated sweep: corrected two color combinations
   that measured below WCAG AA contrast thresholds, fixed a hidden field that was
   reachable by keyboard despite being visually and semantically hidden, added heading
   semantics and aria-current where state was previously conveyed by CSS class alone,
   and fixed a redundant image description that caused a double announcement.
 * Updated Plugin URI and Support URI to semanticjourney.com.

#### 1.5.1

 * Fixed a bug where any module toggle not already known to the Settings screen 
   was silently discarded on every write to the module-enabled setting, not just
   from the Settings form itself. This could have prevented a future module’s toggle
   from ever persisting.

#### 1.5.0

 * Fixed a scope mismatch: Reports and CSV export now cover every trackable post
   type (matching what the front-end script actually tracks), not just posts and
   pages. Sites with custom post types were having activity recorded but not shown.
 * The editor box now shows Decision Point alongside Reader Progress.
 * New Semantic Journey column on the Posts/Pages list table (and any other trackable
   post type).
 * New Dashboard widget: top 5 content items by readers reaching the end, last 7
   days.
 * Multisite: network-wide activation now sets up every site; a site created later
   on a network-active install is set up automatically.
 * Added a privacy policy content declaration under Tools > Privacy > Policy Guide.

#### 1.4.1

 * Addressed the first WordPress.org Plugin Check pass: documented justifications
   for read-only display-filter nonce warnings, the CSV export’s file-stream handling,
   and direct-query caching warnings, and removed an unused Domain Path header. 
   No functional change.

#### 1.4.0

 * Added Decision Point, module 2 of Semantic Journey: site-owner-defined action
   clicks such as a Buy Now button, a survey link, or a phone number, marked via
   a data-sj-decision attribute, automatic tel: link detection, or an admin-defined
   URL pattern rule.
 * Decision Point data appears as additional columns on the existing Reports screen
   and in CSV export, alongside the four Reader Progress milestones.
 * The Reports screen’s underlying query was restructured so a post with only Decision
   Point activity and no Reader Progress data still appears; it would previously
   have been hidden.

#### 1.3.0

 * Added an editor sidebar meta box (Reader Progress) on the post/page edit screen,
   showing whether Reader Progress is recording for the current post and its 30-
   day milestone counts, with a link to the filtered Reports screen. Read-only; 
   no fields, no derived score.

#### 1.2.0

 * Internal only, no user-facing change: retrofitted to the HDUK WordPress.org Plugin
   Development Standards v1.0. Classes renamed for prefix-length compliance, database
   layer consolidated into a single class with a private table getter, every direct
   database query documented with a phpcs:ignore justification, and the semantic_journey_module_enabled
   filter renamed from its earlier slash-namespaced form.
 * Table name (wp_reader_progress_daily) is unchanged; data and settings from 1.1.0
   carry over automatically.

#### 1.1.0

 * Added the Reports screen (Semantic Journey > Reports): a standard, sortable, 
   searchable admin table of Reader Progress milestone counts per content item, 
   with 7/30/90-day filters and CSV export.
 * Reader Progress write path rewritten to use fully static, per-branch SQL for 
   each milestone, so no column name is ever interpolated into a query.
 * Menu restructured: Reports and Settings now live under a single top-level Semantic
   Journey menu.

#### 1.0.0

 * Initial standalone release. Semantic Journey extracted from the CQIP plugin into
   its own framework, with no dependency on any other plugin.
 * Reader Progress rebuilt on a dedicated wp_reader_progress_daily table (previously
   post meta), with atomic increment writes and a configurable retention-purge cron.
 * New generic semantic_journey_module_enabled filter allows any host product to
   integrate without a hard dependency.
 * New Settings screen for module toggles, retention window, and uninstall data-
   removal preference.

## Meta

 *  Version **1.7.1**
 *  Last updated **20 hours ago**
 *  Active installations **Fewer than 10**
 *  WordPress version ** 6.0 or higher **
 *  Tested up to **7.0.2**
 *  PHP version ** 7.4 or higher **
 * Tags
 * [content quality](https://wordpress.org/plugins/tags/content-quality/)[privacy](https://wordpress.org/plugins/tags/privacy/)
 *  [Advanced View](https://wordpress.org/plugins/semantic-journey/advanced/)

## Ratings

No reviews have been submitted yet.

[Your review](https://wordpress.org/support/plugin/semantic-journey/reviews/#new-post)

[See all reviews](https://wordpress.org/support/plugin/semantic-journey/reviews/)

## Contributors

 *   [ hdfraser ](https://profiles.wordpress.org/hdfraser/)

## Support

Got something to say? Need help?

 [View support forum](https://wordpress.org/support/plugin/semantic-journey/)