Run the complete gate:
pnpm checkThis includes:
- TypeScript type checking.
- Biome formatting and linting.
- Vitest parser, pagination, timeline coverage, validation, and MCP transport tests.
- Cloudflare Worker dry-run builds for both the default and production environments.
Tests mock Trac responses. They should not depend on Trac availability or mutable ticket content.
Mocked tests cannot see the one failure that matters most for multiple instances: *.trac.wordpress.org redirects unknown subdomains to Core, so a server that follows redirects answers for the wrong Trac while every mocked test passes. The automated suite pins the guard against that, and step 10 of the smoke test confirms it against the live domain.
Start the Worker by following local-development.md, then use harmless public ticket and changeset IDs. See smoke-test.md for the same list as runnable commands with expected results.
- Confirm
/healthreturns200 OKand/renders the landing page. - Send
OPTIONS /mcp; expect204and CORS headers. - Initialize
/mcp, sendping, and list tools. Then runmcp-explorer doctoragainst/mcpand expect both the stateless 2026-07-28 mode and the legacy mode to report ok. - Exercise each standard tool:
- Search a keyword and a structured filter.
- Read ticket
65739with and without comments. - Read ticket
65808and confirm its linked pull request data is present. - Read ticket
65793; confirm linked pull request data remains available with no reviews and attachments are separate from comments. - Read ticket
62358; confirm linked pull request data remains available with no checks and changesets are separate from comments. - Read ticket
59446with comments; confirm the description and comments keep<a href>links with absolute URLs. - Read ticket
51407and changeset62723; confirm the literal text<script>survives in both. Trac escapes it once for HTML and again for RSS, and decoding a level too many turns it into a tag the stripper removes. - Read changeset
58504with and without its diff. - Read seven days of timeline activity. Confirm this call keeps the original
totalEventsanddaysBackresponse contract and that the strict event limit is applied. A short window can legitimately be empty, so judge this on the request succeeding rather than on the count. - Read a historical timeline range filtered to one author, using a range from January 2005 so the result cannot come from recent activity, and ask for less than the window holds so the first response cannot be complete. Every event should carry that author with a January 2005 date,
coveredshould be a whole-day window insiderequested,completeshould befalse, andcontinueWithshould resume on the day beforecoveredbegins. - Walk that range to its end by resending exactly the
continueWiththe server returned, until a response reportscompleteastrue. The walked windows should tile the requested range with no gap and no overlap, and the walk should terminate. This is the check the page-based smoke test never reached: it promised a final page and never ran one. smoke-test.md records the event total for that fixture window, which the walk should add up to. - List components and milestones.
- Check search page 1 and a page beyond the final result; the latter should return an empty page.
- Initialize
/mcp/chatgpt, then search a keyword, ticket65739, and changesetr58504. - Send invalid arguments and confirm each returns a tool result with
isError: truethat names the failing field, before any upstream request. An unknown method or tool is a JSON-RPC error. - Request ticket
99999999and changeset99999999; confirm each returns a tool error whose payload carries"code": "not_found"with the resource and ID named. Search with an unsupported filter field and confirm"code": "invalid_argument". - Exercise a non-Core instance at
/mcp/metaand/mcp/meta/chatgpt. Every URL in a result must point atmeta.trac.wordpress.org; acore.trac.wordpress.orgURL means the instance was not threaded through. - Ask an instance for a field it does not configure, such as severities on
/mcp/meta. Expect an "are not available" answer rather than a tool error, and confirm Core still returns its populated list. - Call a tool on a slug with no Trac behind it, such as
/mcp/xyzzy-nope. ExpectUnknown or unavailable Trac instance. Core data here is a security regression: the domain redirects unknown subdomains to Core, so a server that follows redirects fails this check while looking healthy. - Filter a search on a field the instance does not configure, such as a component on
/mcp/themes. Expect a tool error naming the fields that instance does have, and confirm the same filter still succeeds where the field exists. A passing-looking result is the failure here: Trac answers an unconfigured filter with the whole ticket set and a matching count. - Send a malformed instance path, such as
/mcp/,/mcp/Meta,/mcp/meta/,/mcp/meta/tools, or/mcp/chatgpt/chatgpt. Each returns404. Slugs are lowercase, andchatgptis a route keyword rather than an instance.
Do not paste private ticket data or credentials into fixtures. If live checks fail, distinguish a Trac response change from Worker behavior before changing a parser.
Run pnpm check, complete the manual smoke test, review the Worker dry-run output, and confirm the intended Cloudflare environment. Deployment is a separate, maintainer-approved action.