Release: daily post verification check #3

Merged
hannah-vernon merged 4 commits from dev into main 2026-08-21 13:37:35 -05:00

Summary

Promotes dev to main. One feature: the daily_post check, merged via #2.

Contents

A check that confirms a post actually published today, rather than that the publishing mechanism responded.

The wp_cron check reports success whenever wp-cron.php returns HTTP 200. On 2026-08-21 a scheduled post on sqlserverscience.com stayed in future status past its 09:28 slot while this tool triggered wp-cron.php every minute and got HTTP 200 every time. Roughly 200 successful triggers, no post, and every check green. The cause was the publish_future_post event being absent from WordPress's cron array, which no check that measures the mechanism can see.

daily_post asks the site what it last published. Before a configured local cutoff it passes, because the day's post is not due yet. From the cutoff onwards it fails until something has been published on the current date in a configured time zone.

Verified against the live site, passing:

[+] daily_post  OK  1129ms - today's post is out: post 4913 "Forwarded Records: the Back-Pointer and the Trip Home" at 2026-08-21 09:28

and failing, by pointing the same check at the site's oldest post:

[!] daily_post  FAIL  1107ms - no post published today, and it is past 11:00; newest is post 7 "Use DBCC PAGE to identify rows involved in blocking operations in SQL Server" at 2015-03-03 00:15, 4189 days ago

Change scope

11 files, 692 insertions, 19 deletions. New service and its tests, wiring in SiteChecker and Program, a config model, and documentation. The 19 deletions are the placeholder UnitTest1.cs and lines replaced in the docs.

Verification

  • dotnet build clean, 0 warnings.
  • dotnet test 22 passed, 0 failed, run against dev as it now stands.
  • Both the passing and failing paths exercised end to end against the live site.
  • No new NuGet packages; the zero-dependency constraint holds and THIRD-PARTY-NOTICES.md is unchanged.
  • NuGetAudit settings in Directory.Build.props are untouched and still enforce NU1901-NU1904 as errors.

Backward compatibility

Existing site configs have no contentFreshness block. The model default sets Enabled = true but leaves Url empty, and the check returns success with a "no url configured" message when the URL is empty. Existing deployments therefore behave exactly as they do today until a URL is added.

Branch divergence

main is one commit ahead of dev, the merge commit from #1. Every dev to main PR leaves a merge commit on main that never travels back, so Forgejo may report this branch as out of date with its base. The content is unaffected: git diff origin/dev origin/main shows no difference beyond this PR's own changes.

I attempted a back-merge of main into dev to reconcile the history, and the push was declined by a pre-receive hook, which is correct since dev is protected and requires a PR. Worth deciding separately whether to reconcile through a routine back-merge PR after each release, or to switch this repository's dev to main merges to fast-forward or rebase so no merge commit lands on main.

Assumptions

  • Merging dev into main is the intended promotion path for this repository.
  • The daily_post check is appropriate only for sites that publish every day. A weekday-only site would alert every weekend, so the check should be left disabled there. This is documented in README.md.
  • Alerting was not exercised from the development machine: the SMTP relay is reachable only from the host running the scheduled task, so smtp.host was blanked for local runs. The alert path is unchanged by this PR and uses the existing AlertService.
  • The default cutoff of 11:00 suits a morning posting schedule and is configurable per site.

Deployment

Rebuild and redeploy the executable to the host running the scheduled task, then add a contentFreshness block to that host's site config. Until the block is added, behaviour is unchanged.

## Summary Promotes `dev` to `main`. One feature: the `daily_post` check, merged via #2. ## Contents A check that confirms a post actually published today, rather than that the publishing mechanism responded. The `wp_cron` check reports success whenever `wp-cron.php` returns HTTP 200. On 2026-08-21 a scheduled post on sqlserverscience.com stayed in `future` status past its 09:28 slot while this tool triggered `wp-cron.php` every minute and got HTTP 200 every time. Roughly 200 successful triggers, no post, and every check green. The cause was the `publish_future_post` event being absent from WordPress's cron array, which no check that measures the mechanism can see. `daily_post` asks the site what it last published. Before a configured local cutoff it passes, because the day's post is not due yet. From the cutoff onwards it fails until something has been published on the current date in a configured time zone. Verified against the live site, passing: ``` [+] daily_post OK 1129ms - today's post is out: post 4913 "Forwarded Records: the Back-Pointer and the Trip Home" at 2026-08-21 09:28 ``` and failing, by pointing the same check at the site's oldest post: ``` [!] daily_post FAIL 1107ms - no post published today, and it is past 11:00; newest is post 7 "Use DBCC PAGE to identify rows involved in blocking operations in SQL Server" at 2015-03-03 00:15, 4189 days ago ``` ## Change scope 11 files, 692 insertions, 19 deletions. New service and its tests, wiring in `SiteChecker` and `Program`, a config model, and documentation. The 19 deletions are the placeholder `UnitTest1.cs` and lines replaced in the docs. ## Verification - `dotnet build` clean, 0 warnings. - `dotnet test` 22 passed, 0 failed, run against `dev` as it now stands. - Both the passing and failing paths exercised end to end against the live site. - No new NuGet packages; the zero-dependency constraint holds and `THIRD-PARTY-NOTICES.md` is unchanged. - `NuGetAudit` settings in `Directory.Build.props` are untouched and still enforce `NU1901-NU1904` as errors. ## Backward compatibility Existing site configs have no `contentFreshness` block. The model default sets `Enabled = true` but leaves `Url` empty, and the check returns success with a "no url configured" message when the URL is empty. Existing deployments therefore behave exactly as they do today until a URL is added. ## Branch divergence `main` is one commit ahead of `dev`, the merge commit from #1. Every `dev` to `main` PR leaves a merge commit on `main` that never travels back, so Forgejo may report this branch as out of date with its base. The content is unaffected: `git diff origin/dev origin/main` shows no difference beyond this PR's own changes. I attempted a back-merge of `main` into `dev` to reconcile the history, and the push was declined by a pre-receive hook, which is correct since `dev` is protected and requires a PR. Worth deciding separately whether to reconcile through a routine back-merge PR after each release, or to switch this repository's `dev` to `main` merges to fast-forward or rebase so no merge commit lands on `main`. ## Assumptions - Merging `dev` into `main` is the intended promotion path for this repository. - The `daily_post` check is appropriate only for sites that publish every day. A weekday-only site would alert every weekend, so the check should be left disabled there. This is documented in `README.md`. - Alerting was not exercised from the development machine: the SMTP relay is reachable only from the host running the scheduled task, so `smtp.host` was blanked for local runs. The alert path is unchanged by this PR and uses the existing `AlertService`. - The default cutoff of 11:00 suits a morning posting schedule and is configurable per site. ## Deployment Rebuild and redeploy the executable to the host running the scheduled task, then add a `contentFreshness` block to that host's site config. Until the block is added, behaviour is unchanged.
Add daily_post check: confirm today's post actually published
All checks were successful
Build and Test / build (pull_request) Successful in 1m55s
2f7dd329ed
The wp_cron check reports success whenever wp-cron.php returns HTTP 200.  That proves the endpoint responds, not that anything was published.  On 2026-08-21 a scheduled post on sqlserverscience.com stayed in future status past its slot while wp-cron.php was being triggered every minute and answering 200 each time, so the site missed a day with every check green.  The cause was the publish_future_post event being absent from WordPress's cron array, which no existing check could see.

The new check asks the site what it last published instead of inspecting the mechanism.  It is calendar-day based rather than a rolling age window: before expectPostByLocalTime it always passes, and from that time onwards it fails until something has been published on the current date in the configured time zone.  A rolling window would drift with the previous day's publish time and could stay quiet through a whole missed morning.  The day boundary and the cutoff both use the configured zone because publishing schedules are set in site-local time.

No credentials are required.  The WordPress REST API exposes published posts anonymously and refuses anonymous requests for scheduled ones, so the check reads only what a visitor could read.  Detecting an overdue future post directly would need an application password, which would give a monitoring tool write access to the site.

date_gmt is used rather than date because WordPress emits both without a timezone designator; date is site-local and would be read as the monitoring machine's local time.

Adds 22 unit tests covering response parsing, time zone resolution, the cutoff boundary including the exact-cutoff case, and a day boundary that resolves differently in UTC than in the site's zone.  No new NuGet packages, so THIRD-PARTY-NOTICES.md is unchanged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Merge pull request 'Add daily_post check: confirm today's post actually published' (#2) from feature/daily-post-check into dev
All checks were successful
Build and Test / build (push) Successful in 57s
Build and Test / build (pull_request) Successful in 58s
d1eb4473d4
Reviewed-on: #2
Merge main into dev to reconcile the release merge commit
All checks were successful
Build and Test / build (pull_request) Successful in 1m3s
694019ad3b
Merge pull request 'Merge main into dev to reconcile release history' (#4) from chore/reconcile-main-into-dev into dev
All checks were successful
Build and Test / build (push) Successful in 58s
Build and Test / build (pull_request) Successful in 1m2s
8ea15dd5c0
Reviewed-on: #4
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
hannah-vernon/vigilance-sentinel!3
No description provided.