Skip to content

feat: consolidate pre-publish event validation gate at upsert_event#420

Merged
chubes4 merged 1 commit into
mainfrom
validation-gate
Jul 6, 2026
Merged

feat: consolidate pre-publish event validation gate at upsert_event#420
chubes4 merged 1 commit into
mainfrom
validation-gate

Conversation

@chubes4

@chubes4 chubes4 commented Jul 5, 2026

Copy link
Copy Markdown
Member

Summary

Adds a single consolidated pre-publish validation gate at the upsert_event boundary (EventUpsert::validateForPublish()) so that every fetch handler — web scraper, Ticketmaster, Dice, and any future feed — funnels through one quality bar before any write. Closes #417.

What already existed (and how this consolidates rather than duplicates)

Per the site rule "check what already exists before designing new infrastructure", I read the two just-merged fixes first and designed the gate around them:

No parallel reimplementation of any of these. JunkPayloadFilter is not modified; its contract (source-specific patterns via data_machine_events_junk_payload_patterns) is preserved.

Rules enforced (v1)

  1. Valid start date — non-empty, parseable startDate (folded in from Event import accepts empty startDate — publishes undated events that render/sort incorrectly #415; getDateTimeConfidence() === 'none' → reject).
  2. Real title — non-empty, not a junk-marker leak (Event bot leaks "Rejected:" events: pipeline prompts never tell the AI to use reject_source / give no rejection criteria #349/Dedup workflow leaks junk-titled PUBLISHED events — "(Duplicate)" / "Consolidate:" titles escape the #349 guard; agent marks duplicates via title-rename instead of a tool #367 via isJunkTitle()), and not a generic placeholder/noise title (new isPlaceholderTitle(): exact match against a filterable deny-list, plus a bare-punctuation/noise regex catching ?, ??, -, ).
  3. Junk/test payloadJunkPayloadFilter::is_junk() evaluated at the upsert boundary with the resolved source_type, applying each source's registered deny-list (CCPER-, Standalone Upsell, Upcoming Event no-artist, etc.).

Disposal policy — SKIP + LOG (decided)

Rejected items are skipped entirely and logged. They are neither published nor saved as draft. There is no human-review queue and no draft fallback. Each rejection routes through gateRejection(), which logs a datamachine_log warning with a machine-readable rule slug (missing_title, junk_marker_title, placeholder_title, invalid_start_date, junk_payload) and returns the standard success:false tool response so the item is dropped and the AI moves on.

Filters (all generic, data_machine_events_* prefix)

  • data_machine_events_junk_payload_patterns — existing; source-specific junk buckets (unchanged).
  • data_machine_events_placeholder_titlesnew; generic placeholder title deny-list (defaults: Test Event, Upcoming Event).

No vendor or venue is special-cased in the gate (layer purity). Patterns are config; matching is generic.

Files changed

  • inc/Steps/Upsert/Events/EventUpsert.php — added validateForPublish() (the gate), gateRejection() (uniform skip+log), isPlaceholderTitle() (generic placeholder check); refactored executeUpsert() to call the gate before acquiring the advisory lock; added $junk_filter property.
  • tests/Unit/EventUpsertTest.php — added gate-level tests: valid event passes, empty date skipped, placeholder Test Event title skipped, bare ? title skipped, junk payload skipped from a non-Ticketmaster source (the consolidation win), placeholder deny-list filterable, exact-match-only (non-substring).

Lint / test results

  • php -l — clean on both changed PHP files.
  • PHPStan — not configured for this plugin (phpstan.neon absent).
  • PHPUnit could not run — the WP Codebox test sandbox is unavailable on this host (no tests/bootstrap.php / vendor/bin/phpunit locally); tests will run in CI. Test logic was verified by reasoning against the gate's rule ordering and the existing getDateTimeConfidence() / JunkPayloadFilter contracts.

@homeboy-ci

homeboy-ci Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Homeboy Results — data-machine-events

Review audit

review audit — passed

  • audit — 13 finding(s)
  • Total: 13 finding(s)

Deep dive: homeboy review audit data-machine-events --changed-since 28859c9

Artifacts and drill-down
  • CI results artifact: homeboy-ci-results-data-machine-events-review-audit-homeboy-Linux-node24 contains immediate command JSON for this action invocation.
  • Observation artifact: homeboy-observations-data-machine-events-review-audit-homeboy-Linux-node24 contains exported Homeboy run history for deeper queries.
  • Drill-down: download the observation artifact, then run homeboy runs import <dir>, homeboy runs list, and homeboy runs findings <run-id>.
  • Artifacts are attached to the workflow run: https://github.com/Extra-Chill/data-machine-events/actions/runs/28764162004
Tooling versions
  • Homeboy CLI: homeboy 0.281.14+dde84b2f9d55+b5161f3
  • Extension: wordpress from https://github.com/Extra-Chill/homeboy-extensions
  • Extension revision: 68d84557
  • Action: Extra-Chill/homeboy-action@v2

…417)

Add a single validation boundary in EventUpsert::validateForPublish() that
every fetch handler (web scraper, Ticketmaster, Dice, …) passes through
before any write. Junk never reaches publish regardless of source.

Reuses rather than duplicates the prior scattered guards:
- #415 empty/unparseable startDate gate — folded in from the former inline
  check; getDateTimeConfidence() reused as-is.
- #349/#367 junk-marker title guard — isJunkTitle() reused.
- #416 junk/test-payload filter — JunkPayloadFilter reused, now evaluated
  at the upsert boundary for every source instead of only inside the
  Ticketmaster fetch handler.

Adds isPlaceholderTitle() for generic, source-agnostic placeholder/noise
title detection ("Test Event", bare "?", etc.), filterable via
data_machine_events_placeholder_titles.

Disposal policy: SKIP + LOG. Rejected items are neither published nor
drafted; each rejection is logged via datamachine_log with a
machine-readable rule slug. No human-review queue, no draft fallback.

All matching is generic and filterable; no vendor or venue is special-cased
in the gate (layer purity).
@chubes4 chubes4 force-pushed the validation-gate branch from 6087d36 to 8f1d0ac Compare July 6, 2026 02:36
@chubes4 chubes4 merged commit 60678eb into main Jul 6, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a pre-publish event validation gate at upsert_event (ties #415, #416)

1 participant