Skip to content

Trac Notifications: ticket-read DAO methods + Trac_API caching layer#655

Draft
obenland wants to merge 7 commits into
WordPress:trunkfrom
obenland:add/trac-json-api
Draft

Trac Notifications: ticket-read DAO methods + Trac_API caching layer#655
obenland wants to merge 7 commits into
WordPress:trunkfrom
obenland:add/trac-json-api

Conversation

@obenland
Copy link
Copy Markdown
Member

Summary

First of a planned multi-PR feature that surfaces WordPress.org Trac data as JSON over HTTP. This PR lays the data and caching plumbing inside the trac-notifications plugin without yet exposing any new HTTP surface — that lands in follow-up work.

Architecture context

[ api.wordpress.org/dotorg/trac/...  (FUTURE PR) ]
[ wporg-abilities MCP                (FUTURE PR) ]
                  │
                  ▼
[ Trac_API ]  ←── this PR (wp.org side)
  • memcached fresh + stale + circuit breaker
                  │
                  │ Trac_Notifications_HTTP_Client (existing)
                  ▼
[ <trac>.trac.wordpress.org/wpapi ]
                  │
                  ▼
[ Trac_Notifications_DB ]  ←── new methods in this PR (Trac-box side)
                  │
                  ▼
              Trac MySQL DB

What's in this PR

Trac_Notifications_DB gains five read methods for data the existing class did not expose:

  • get_trac_ticket_full( $id, $opts ) — composite payload (one Trac round-trip per ticket)
  • get_trac_ticket_comments( $id, $limit, $offset ) — ASC-ordered window of comment bodies
  • get_trac_ticket_comment_count( $id ) — total, for pagination + most-recent-N math
  • get_trac_ticket_changelog( $id ) — every non-comment field change (status, owner, keywords, milestone, …)
  • get_trac_ticket_attachments( $id ) — filename, size, time, description, author
  • get_trac_ticket_custom_fields( $id ) — full name => value map (generalises the focuses-only accessor)

The composite returns the most recent N comments in chronological order by computing offset = max(0, total - limit), so large tickets don't dump hundreds of KB by default. All time columns are returned raw (microseconds since epoch) — Trac_API converts them at the output boundary.

Trac_API (new) is the wp.org-side caching wrapper around Trac_Notifications_HTTP_Client:

  • Memcached cache with separate fresh (5min) and stale (24h) TTLs
  • Stale fallback when Trac is unreachable
  • Per-trac circuit breaker (5min TTL): a core.trac outage cannot block meta.trac calls
  • Negative caching for missing tickets (60s sentinel) to absorb 404 floods, written to both fresh and stale keys so a deletion doesn't resurface old data
  • Dependency-injectable cache backend + client factory, so tests don't need memcached
  • Missing TRAC_NOTIFICATIONS_API_KEY is logged loudly (operational config error ≠ transient outage)

Unit tests: new PHPUnit setup at wordpress.org/public_html/wp-content/plugins/trac-notifications/, 36 tests, 90 assertions, no WordPress dependency. Follows the existing core/serve-happy/1.0/tests and common/includes/tests/slack/trac patterns. Added a matrix entry to .github/workflows/unit-tests.yml.

What's NOT in this PR

  • The HTTP endpoint at api.wordpress.org/dotorg/trac/... — separate PR
  • MCP abilities in wporg-abilities — separate PR
  • Decommissioning the disabled /core/trac-search/1.0/ endpoint — separate task
  • Sibling endpoints (contributors/, keywords/, releases/, components/) — these mostly read Make/Handbook content rather than Trac DB, so each becomes its own small PR

Deployment notes

The DAO additions need to be deployed to the Trac boxes (core.trac and meta.trac) before the follow-up HTTP gateway PR can be tested end-to-end. The wp.org-side caching layer can be exercised against the existing wpapi endpoint as long as only existing methods are called.

Test plan

  • All 36 PHPUnit tests pass locally
  • BASE_REF=trunk php .github/bin/phpcs-branch.php reports no violations
  • CI: Trac Notifications matrix entry passes
  • CI: PHP Coding Standards passes

🤖 Generated with Claude Code

obenland and others added 2 commits May 26, 2026 14:05
…layer.

Adds the data and caching plumbing the upcoming JSON API endpoint and MCP
abilities will share. Lays the foundation without yet exposing any new HTTP
surface — that lands in a follow-up PR.

Trac_Notifications_DB gains five read methods covering data the existing
class did not expose: comment bodies, comment count, the non-comment
changelog, attachments, and the full custom-fields map. The composite
get_trac_ticket_full() assembles a single ticket payload and returns the most
recent N comments in chronological order using count + offset.

class-trac-api.php is a new wp.org-side wrapper around
Trac_Notifications_HTTP_Client. It memoises calls in memcached with separate
fresh and stale TTLs, falls back to stale data when Trac is unreachable, and
trips a per-trac circuit breaker so a degraded Trac is not hammered by
retries. Missing tickets are negatively cached (sentinel) to absorb 404
floods. A missing TRAC_NOTIFICATIONS_API_KEY is logged loudly so a
permanent misconfiguration is distinguishable from a transient outage.

Includes a standalone PHPUnit suite (36 tests, 90 assertions) following the
pattern used by other no-WP-dependency tests in the repo, plus a CI matrix
entry to run them on every PR.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Declares the `$db` property explicitly on Trac_Notifications_DB so PHP 8.2+
no longer flags dynamic-property creation. Switches the two new test
classes from `@group` docblock metadata to `#[Group]` attributes, which
PHPUnit 12 will require.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@obenland obenland force-pushed the add/trac-json-api branch from aab7b1b to 31f45be Compare May 26, 2026 19:09
obenland and others added 5 commits May 26, 2026 14:24
Brings the new files in line with --standard=WordPress (stricter than the
project's tuned ruleset that CI enforces). Adds the short descriptions WPCS
expects on every docblock, capitalises a couple of sentences, and fully-stops
the trailing inline comments inside the composite fixtures.

Moves the three test-fake classes from tests/ into tests/includes/ to mirror
the WordPress core test-scaffold convention and keep the tests/ directory
focused on actual TestCase classes. bootstrap.php now requires them from the
new path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Lets external callers (HTTP API endpoint, future MCP abilities) discover
tickets by an allowlisted set of equality filters plus optional focuses /
keywords substring matches and a changed_since date range. Returns a richer
column set than the existing get_tickets_by(): id, summary, status,
resolution, type, component, priority, milestone, owner, reporter, changetime.

A safety guard refuses unscoped focuses / keywords LIKE queries — the worst
performance shape, since neither field is indexed. Callers must combine those
filters with at least one equality scope or changed_since to limit blast
radius. The existing equality filters all narrow the row set the LIKE
evaluates against.

get_trac_ticket_focuses() now delegates to get_trac_ticket_custom_fields() to
drop the duplicate ticket_custom query. Return contract preserved.

Both new closures use the `static` keyword so they don't bind `$this`.

54 unit tests, 123 assertions, all passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Trac_API now calls wp_cache_get/wp_cache_set directly with the 'trac_api'
group instead of going through an injectable cache backend. Drops the
class-trac-api-wpcache.php adapter — it existed only to make tests inject a
stateful in-memory cache, which we now achieve with a thin wp_cache_* polyfill
in tests/bootstrap.php backed by a static Test_Cache class.

Net result is the same testability with one less production class.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Trust callers — the HTTP gateway and MCP ability will each validate their
own trac slug input (URL allowlist for the gateway, enum schema for MCP)
before reaching this class. A bad slug now flows through to a failing
HTTP call and the breaker, which is the same path that a flaky Trac box
takes. One less premature validation point.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The earlier commit added the entry again on top of the one upstream already
shipped (3622a12). Keep the new .phpunit.cache/ directory entry, drop the
duplicate.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

1 participant