From 6243d2caddf2b6c1a7af52188bc0131f3c572dcd Mon Sep 17 00:00:00 2001 From: Ariel Shulman Date: Fri, 28 Aug 2026 17:19:43 +0300 Subject: [PATCH] feat(fixtures): consolidate corpus around a flagship demo fork + mutation tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace 14 single-scenario fixtures with a flagship fixture (a fork of the ora-air demo enriched with a dynamic segment, hand-owned llms.txt, MDX page, sitemap, gated page, and two MCP servers behind a secret-free auth stub), a full Pages Router twin, and thin single-axis fixtures (webmcp, next-auth). 21 fixtures become 11; the golden/conformance pipeline needed no changes. Add scripts/mutation-tests.mjs: multi-build sequences over the flagship (idempotence, user-edit preservation, orphaned-twin cleanup, additive change, auth-method flip, manifest drift) — the build-N+1 bugs the one-shot golden layer can't see. Wired into CI after the dogfood step. Fix three product bugs the new corpus surfaced: - the router model now counts MDX pages as routes exactly when next.config pageExtensions serves them, so /guide is in the serving manifest instead of 404-wayfinding for agents - the stale-twin sweep no longer deletes ax's own 404.md every second build (phantom markdownTwins.deleted entry); twin deletions now log per-path - the raw-mode multiselect no longer garbles on arrow keys: the shared readline interface's keypress listeners are suspended during the selector (history recall was rewriting earlier answers over the rows), and a zero-column PTY no longer truncates every row to nothing Retarget dogfood-middleware.mjs to the flagship (adds a composed-middleware probe: withAx's second argument still bot-gates scripted UAs). Co-Authored-By: Claude Fable 5 --- .github/workflows/ci.yml | 9 +- .gitignore | 24 +- README.md | 2 +- fixtures/config-overrides/README.md | 21 - fixtures/config-overrides/app/layout.tsx | 14 - fixtures/config-overrides/app/page.tsx | 11 - fixtures/config-overrides/ax.config.ts | 57 --- fixtures/config-overrides/report.golden.json | 196 -------- fixtures/discovery/README.md | 16 - fixtures/discovery/app/layout.tsx | 31 -- fixtures/discovery/app/page.tsx | 12 - fixtures/discovery/app/sitemap.ts | 12 - fixtures/discovery/ax.config.ts | 11 - fixtures/discovery/package.json | 23 - fixtures/discovery/public/agents.md | 9 - fixtures/discovery/public/robots.txt | 9 - fixtures/discovery/report.golden.json | 187 -------- fixtures/discovery/twins.golden/404.md | 21 - fixtures/discovery/twins.golden/index.md | 11 - fixtures/flagship-pages/README.md | 28 ++ fixtures/flagship-pages/ax.config.ts | 21 + .../cards.golden/server-card.json | 28 ++ .../cards.golden/server-card/api-mcp.json | 37 ++ .../server-card/api-public-mcp.json | 28 ++ fixtures/flagship-pages/lib/data/airports.ts | 35 ++ fixtures/flagship-pages/lib/data/flights.ts | 166 +++++++ fixtures/flagship-pages/lib/data/seatmaps.ts | 128 +++++ .../flagship-pages/lib/services/booking.ts | 89 ++++ .../flagship-pages/lib/services/payment.ts | 32 ++ .../flagship-pages/lib/services/search.ts | 16 + fixtures/flagship-pages/middleware.ts | 12 + .../next-env.d.ts | 0 .../next.config.mjs | 0 .../package.json | 3 +- fixtures/flagship-pages/pages/404.tsx | 13 + .../pages/_app.tsx | 0 .../pages/_document.tsx | 4 +- fixtures/flagship-pages/pages/account.tsx | 19 + .../flagship-pages/pages/api/[transport].ts | 128 +++++ .../pages/api/public/[transport].ts | 47 ++ fixtures/flagship-pages/pages/checkout.tsx | 18 + .../flagship-pages/pages/confirmation.tsx | 18 + .../pages/destinations/[slug].tsx | 35 ++ .../pages/destinations/index.tsx | 51 ++ fixtures/flagship-pages/pages/index.tsx | 25 + fixtures/flagship-pages/pages/results.tsx | 18 + fixtures/flagship-pages/pages/seats.tsx | 18 + .../report.golden.json | 158 ++++--- .../tsconfig.json | 5 + .../twins.golden/404.md | 9 +- fixtures/flagship-pages/twins.golden/auth.md | 18 + .../twins.golden/destinations.md | 26 ++ fixtures/flagship-pages/twins.golden/index.md | 13 + .../types/webmcp-forms.d.ts | 0 fixtures/flagship/README.md | 42 ++ .../oauth-authorization-server/route.ts | 36 ++ .../oauth-protected-resource/route.ts | 32 ++ fixtures/flagship/app/account/page.tsx | 21 + .../flagship/app/api/[transport]/route.ts | 126 +++++ fixtures/flagship/app/api/book/route.ts | 35 ++ fixtures/flagship/app/api/pay/route.ts | 37 ++ .../app/api/public/[transport]/route.ts | 42 ++ fixtures/flagship/app/api/search/route.ts | 89 ++++ fixtures/flagship/app/api/seats/route.ts | 42 ++ fixtures/flagship/app/api/session/route.ts | 6 + .../flagship/app/checkout/checkout-client.tsx | 209 +++++++++ fixtures/flagship/app/checkout/page.tsx | 15 + .../flagship/app/components/AirportSelect.tsx | 96 ++++ .../flagship/app/components/DateSelect.tsx | 73 +++ .../app/components/FareHoldBanner.tsx | 46 ++ fixtures/flagship/app/components/Footer.tsx | 35 ++ fixtures/flagship/app/components/Header.tsx | 38 ++ .../flagship/app/components/PaxSelect.tsx | 41 ++ .../flagship/app/components/SearchWidget.tsx | 84 ++++ .../flagship/app/components/SliderCaptcha.tsx | 86 ++++ fixtures/flagship/app/components/StepBar.tsx | 33 ++ fixtures/flagship/app/components/format.ts | 16 + .../app/confirmation/confirmation-client.tsx | 107 +++++ fixtures/flagship/app/confirmation/page.tsx | 14 + .../flagship/app/destinations/[slug]/page.tsx | 54 +++ fixtures/flagship/app/destinations/page.tsx | 55 +++ fixtures/flagship/app/globals.css | 88 ++++ fixtures/flagship/app/guide/page.mdx | 27 ++ fixtures/flagship/app/layout.tsx | 25 + fixtures/flagship/app/llms.txt/route.ts | 41 ++ .../flagship/app/organization-json-ld.tsx | 30 ++ fixtures/flagship/app/page.tsx | 99 ++++ fixtures/flagship/app/results/page.tsx | 14 + .../flagship/app/results/results-client.tsx | 220 +++++++++ fixtures/flagship/app/seats/page.tsx | 15 + fixtures/flagship/app/seats/seats-client.tsx | 196 ++++++++ fixtures/flagship/app/sitemap.ts | 18 + .../flagship/app/state/BookingContext.tsx | 203 ++++++++ fixtures/flagship/ax.config.ts | 29 ++ .../flagship/cards.golden/server-card.json | 28 ++ .../cards.golden/server-card/api-mcp.json | 38 ++ .../server-card/api-public-mcp.json | 28 ++ fixtures/flagship/lib/data/airports.ts | 35 ++ fixtures/flagship/lib/data/flights.ts | 166 +++++++ fixtures/flagship/lib/data/seatmaps.ts | 128 +++++ fixtures/flagship/lib/services/api-guard.ts | 21 + fixtures/flagship/lib/services/booking.ts | 89 ++++ fixtures/flagship/lib/services/payment.ts | 32 ++ fixtures/flagship/lib/services/search.ts | 16 + fixtures/flagship/lib/services/session.ts | 31 ++ .../mdx-components.tsx | 0 fixtures/flagship/middleware.ts | 81 ++++ .../next-env.d.ts | 0 .../{mdx-content => flagship}/next.config.mjs | 0 .../{mdx-content => flagship}/package.json | 7 +- fixtures/flagship/public/agents.md | 27 ++ fixtures/flagship/public/destinations.md | 26 ++ fixtures/flagship/public/openapi.json | 344 ++++++++++++++ fixtures/flagship/public/robots.txt | 19 + fixtures/flagship/report.golden.json | 388 +++++++++++++++ fixtures/{llms-txt => flagship}/tsconfig.json | 5 + .../twins.golden/404.md | 14 +- fixtures/flagship/twins.golden/auth.md | 19 + fixtures/flagship/twins.golden/checkout.md | 13 + .../flagship/twins.golden/confirmation.md | 13 + fixtures/flagship/twins.golden/guide.md | 34 ++ fixtures/flagship/twins.golden/index.md | 92 ++++ fixtures/flagship/twins.golden/results.md | 13 + fixtures/flagship/twins.golden/seats.md | 13 + fixtures/llms-txt/README.md | 15 - fixtures/llms-txt/app/llms.txt/route.ts | 20 - fixtures/llms-txt/app/page.tsx | 11 - fixtures/llms-txt/ax.config.ts | 17 - fixtures/llms-txt/package.json | 23 - fixtures/llms-txt/report.golden.json | 183 -------- fixtures/llms-txt/twins.golden/404.md | 22 - fixtures/mcp-adapter-gated/README.md | 18 - .../app/[transport]/route.ts | 28 -- fixtures/mcp-adapter-gated/app/layout.tsx | 14 - fixtures/mcp-adapter-gated/app/page.tsx | 11 - fixtures/mcp-adapter-gated/ax.config.ts | 9 - .../cards.golden/server-card.json | 32 -- fixtures/mcp-adapter-gated/next-env.d.ts | 6 - fixtures/mcp-adapter-gated/next.config.mjs | 4 - fixtures/mcp-adapter-gated/package.json | 26 -- fixtures/mcp-adapter-gated/report.golden.json | 239 ---------- fixtures/mcp-adapter-gated/tsconfig.json | 5 - .../mcp-adapter-gated/twins.golden/auth.md | 19 - fixtures/mcp-adapter/README.md | 18 - fixtures/mcp-adapter/app/[transport]/route.ts | 19 - fixtures/mcp-adapter/app/layout.tsx | 14 - fixtures/mcp-adapter/app/page.tsx | 11 - fixtures/mcp-adapter/ax.config.ts | 17 - .../mcp-adapter/cards.golden/server-card.json | 28 -- fixtures/mcp-adapter/next-env.d.ts | 6 - fixtures/mcp-adapter/next.config.mjs | 4 - fixtures/mcp-adapter/twins.golden/404.md | 22 - fixtures/mcp-multi-server/README.md | 21 - .../mcp-multi-server/app/api/mcp/route.ts | 24 - .../app/api/public/mcp/route.ts | 17 - fixtures/mcp-multi-server/app/layout.tsx | 14 - fixtures/mcp-multi-server/app/page.tsx | 12 - fixtures/mcp-multi-server/ax.config.ts | 9 - .../cards.golden/server-card.json | 28 -- .../cards.golden/server-card/api-mcp.json | 32 -- .../server-card/api-public-mcp.json | 28 -- fixtures/mcp-multi-server/next-env.d.ts | 6 - fixtures/mcp-multi-server/next.config.mjs | 4 - fixtures/mcp-multi-server/package.json | 26 -- fixtures/mcp-multi-server/tsconfig.json | 5 - .../mcp-multi-server/twins.golden/auth.md | 19 - .../mcp-multi-server/twins.golden/index.md | 11 - fixtures/mdx-content/README.md | 15 - fixtures/mdx-content/app/guide/page.mdx | 24 - fixtures/mdx-content/app/layout.tsx | 14 - fixtures/mdx-content/app/page.tsx | 13 - fixtures/mdx-content/app/widget.tsx | 10 - fixtures/mdx-content/app/widgets/page.mdx | 7 - fixtures/mdx-content/next-env.d.ts | 6 - fixtures/mdx-content/report.golden.json | 203 -------- fixtures/mdx-content/tsconfig.json | 5 - fixtures/mdx-content/twins.golden/404.md | 21 - fixtures/mdx-content/twins.golden/guide.md | 29 -- fixtures/mdx-content/twins.golden/index.md | 11 - fixtures/middleware/README.md | 34 -- fixtures/middleware/app/blog/[slug]/page.tsx | 13 - fixtures/middleware/app/docs/page.tsx | 15 - fixtures/middleware/app/layout.tsx | 20 - fixtures/middleware/app/page.tsx | 18 - fixtures/middleware/app/private/page.tsx | 20 - fixtures/middleware/app/shell/page.tsx | 10 - fixtures/middleware/ax.config.ts | 13 - fixtures/middleware/middleware.ts | 12 - fixtures/middleware/next-env.d.ts | 6 - fixtures/middleware/next.config.mjs | 4 - fixtures/middleware/package.json | 24 - fixtures/middleware/public/docs.md | 9 - fixtures/middleware/report.golden.json | 199 -------- fixtures/middleware/tsconfig.json | 5 - fixtures/middleware/twins.golden/404.md | 23 - fixtures/middleware/twins.golden/index.md | 13 - fixtures/next-auth/README.md | 11 + .../app/api/auth/[...nextauth]/route.ts | 19 + .../{llms-txt => next-auth}/app/layout.tsx | 4 +- fixtures/next-auth/app/page.tsx | 11 + .../{mdx-content => next-auth}/ax.config.ts | 2 +- .../{discovery => next-auth}/next-env.d.ts | 0 .../{discovery => next-auth}/next.config.mjs | 0 .../package.json | 3 +- .../report.golden.json | 32 +- .../tsconfig.json | 0 .../twins.golden/404.md | 4 +- fixtures/openapi/README.md | 13 - fixtures/openapi/app/layout.tsx | 14 - fixtures/openapi/app/page.tsx | 11 - fixtures/openapi/ax.config.ts | 17 - fixtures/openapi/next-env.d.ts | 6 - fixtures/openapi/next.config.mjs | 4 - fixtures/openapi/public/openapi.json | 63 --- fixtures/openapi/report.golden.json | 224 --------- fixtures/openapi/tsconfig.json | 5 - fixtures/openapi/twins.golden/404.md | 23 - fixtures/openapi/twins.golden/auth.md | 21 - fixtures/pages-bare/README.md | 6 - fixtures/pages-bare/ax.config.ts | 11 - fixtures/pages-bare/next.config.mjs | 4 - fixtures/pages-bare/package.json | 23 - fixtures/pages-bare/pages/404.tsx | 5 - fixtures/pages-bare/pages/about.tsx | 8 - fixtures/pages-bare/pages/blog/[slug].tsx | 5 - fixtures/pages-bare/pages/index.tsx | 8 - fixtures/pages-bare/report.golden.json | 187 -------- fixtures/pages-bare/tsconfig.json | 5 - fixtures/pages-bare/twins.golden/404.md | 22 - fixtures/pages-mcp/README.md | 5 - fixtures/pages-mcp/ax.config.ts | 11 - .../pages-mcp/cards.golden/server-card.json | 28 -- fixtures/pages-mcp/next-env.d.ts | 6 - fixtures/pages-mcp/next.config.mjs | 4 - fixtures/pages-mcp/package.json | 26 -- fixtures/pages-mcp/pages/api/[transport].ts | 30 -- fixtures/pages-mcp/pages/index.tsx | 8 - fixtures/pages-mcp/report.golden.json | 205 -------- fixtures/pages-mcp/tsconfig.json | 5 - fixtures/pages-mcp/twins.golden/404.md | 22 - fixtures/pages-webmcp-declarative/README.md | 5 - .../pages-webmcp-declarative/ax.config.ts | 11 - .../pages-webmcp-declarative/next-env.d.ts | 6 - .../pages-webmcp-declarative/next.config.mjs | 4 - .../pages-webmcp-declarative/package.json | 23 - .../pages-webmcp-declarative/pages/index.tsx | 21 - .../report.golden.json | 183 -------- .../pages-webmcp-declarative/tsconfig.json | 5 - .../twins.golden/404.md | 21 - fixtures/webmcp-declarative/README.md | 7 - fixtures/webmcp-declarative/app/layout.tsx | 14 - fixtures/webmcp-declarative/app/page.tsx | 21 - fixtures/webmcp-declarative/next-env.d.ts | 6 - fixtures/webmcp-declarative/next.config.mjs | 4 - fixtures/webmcp-declarative/package.json | 23 - fixtures/webmcp-declarative/tsconfig.json | 5 - fixtures/webmcp-imperative/README.md | 7 - fixtures/webmcp-imperative/app/layout.tsx | 14 - fixtures/webmcp-imperative/app/page.tsx | 11 - fixtures/webmcp-imperative/next-env.d.ts | 6 - fixtures/webmcp-imperative/next.config.mjs | 4 - fixtures/webmcp-imperative/package.json | 21 - fixtures/webmcp-imperative/tsconfig.json | 5 - fixtures/webmcp/README.md | 18 + fixtures/webmcp/app/layout.tsx | 18 + fixtures/webmcp/app/page.tsx | 29 ++ .../app/register-tools.tsx | 0 .../ax.config.ts | 2 +- fixtures/{llms-txt => webmcp}/next-env.d.ts | 0 fixtures/{llms-txt => webmcp}/next.config.mjs | 0 fixtures/{openapi => webmcp}/package.json | 2 +- .../report.golden.json | 51 +- fixtures/{discovery => webmcp}/tsconfig.json | 0 .../twins.golden/404.md | 4 +- .../types/webmcp-forms.d.ts | 0 .../types/webmcp.d.ts | 0 package.json | 1 + packages/ax/src/app-dir.ts | 42 +- packages/ax/src/cli.ts | 7 +- packages/ax/src/generate.ts | 14 +- packages/ax/src/manifest.ts | 8 +- packages/ax/src/markdown-twins.ts | 13 +- packages/ax/src/pages-dir.ts | 46 +- packages/ax/src/prompt.ts | 30 +- packages/ax/src/router-model.ts | 33 +- packages/ax/test/detect-webmcp.test.ts | 21 +- packages/ax/test/fixtures-integration.test.ts | 345 +++++++------- packages/ax/test/markdown-twins.test.ts | 13 + packages/ax/test/router-model.test.ts | 24 + pnpm-lock.yaml | 442 +++++------------- scripts/dogfood-middleware.mjs | 70 +-- scripts/mutation-tests.mjs | 404 ++++++++++++++++ spec/ard/README.md | 2 +- 293 files changed, 6541 insertions(+), 4696 deletions(-) delete mode 100644 fixtures/config-overrides/README.md delete mode 100644 fixtures/config-overrides/app/layout.tsx delete mode 100644 fixtures/config-overrides/app/page.tsx delete mode 100644 fixtures/config-overrides/ax.config.ts delete mode 100644 fixtures/config-overrides/report.golden.json delete mode 100644 fixtures/discovery/README.md delete mode 100644 fixtures/discovery/app/layout.tsx delete mode 100644 fixtures/discovery/app/page.tsx delete mode 100644 fixtures/discovery/app/sitemap.ts delete mode 100644 fixtures/discovery/ax.config.ts delete mode 100644 fixtures/discovery/package.json delete mode 100644 fixtures/discovery/public/agents.md delete mode 100644 fixtures/discovery/public/robots.txt delete mode 100644 fixtures/discovery/report.golden.json delete mode 100644 fixtures/discovery/twins.golden/404.md delete mode 100644 fixtures/discovery/twins.golden/index.md create mode 100644 fixtures/flagship-pages/README.md create mode 100644 fixtures/flagship-pages/ax.config.ts create mode 100644 fixtures/flagship-pages/cards.golden/server-card.json create mode 100644 fixtures/flagship-pages/cards.golden/server-card/api-mcp.json create mode 100644 fixtures/flagship-pages/cards.golden/server-card/api-public-mcp.json create mode 100644 fixtures/flagship-pages/lib/data/airports.ts create mode 100644 fixtures/flagship-pages/lib/data/flights.ts create mode 100644 fixtures/flagship-pages/lib/data/seatmaps.ts create mode 100644 fixtures/flagship-pages/lib/services/booking.ts create mode 100644 fixtures/flagship-pages/lib/services/payment.ts create mode 100644 fixtures/flagship-pages/lib/services/search.ts create mode 100644 fixtures/flagship-pages/middleware.ts rename fixtures/{pages-bare => flagship-pages}/next-env.d.ts (100%) rename fixtures/{config-overrides => flagship-pages}/next.config.mjs (100%) rename fixtures/{mcp-adapter => flagship-pages}/package.json (88%) create mode 100644 fixtures/flagship-pages/pages/404.tsx rename fixtures/{pages-bare => flagship-pages}/pages/_app.tsx (100%) rename fixtures/{pages-bare => flagship-pages}/pages/_document.tsx (72%) create mode 100644 fixtures/flagship-pages/pages/account.tsx create mode 100644 fixtures/flagship-pages/pages/api/[transport].ts create mode 100644 fixtures/flagship-pages/pages/api/public/[transport].ts create mode 100644 fixtures/flagship-pages/pages/checkout.tsx create mode 100644 fixtures/flagship-pages/pages/confirmation.tsx create mode 100644 fixtures/flagship-pages/pages/destinations/[slug].tsx create mode 100644 fixtures/flagship-pages/pages/destinations/index.tsx create mode 100644 fixtures/flagship-pages/pages/index.tsx create mode 100644 fixtures/flagship-pages/pages/results.tsx create mode 100644 fixtures/flagship-pages/pages/seats.tsx rename fixtures/{mcp-multi-server => flagship-pages}/report.golden.json (59%) rename fixtures/{mcp-adapter => flagship-pages}/tsconfig.json (68%) rename fixtures/{mcp-multi-server => flagship-pages}/twins.golden/404.md (73%) create mode 100644 fixtures/flagship-pages/twins.golden/auth.md create mode 100644 fixtures/flagship-pages/twins.golden/destinations.md create mode 100644 fixtures/flagship-pages/twins.golden/index.md rename fixtures/{pages-webmcp-declarative => flagship-pages}/types/webmcp-forms.d.ts (100%) create mode 100644 fixtures/flagship/README.md create mode 100644 fixtures/flagship/app/.well-known/oauth-authorization-server/route.ts create mode 100644 fixtures/flagship/app/.well-known/oauth-protected-resource/route.ts create mode 100644 fixtures/flagship/app/account/page.tsx create mode 100644 fixtures/flagship/app/api/[transport]/route.ts create mode 100644 fixtures/flagship/app/api/book/route.ts create mode 100644 fixtures/flagship/app/api/pay/route.ts create mode 100644 fixtures/flagship/app/api/public/[transport]/route.ts create mode 100644 fixtures/flagship/app/api/search/route.ts create mode 100644 fixtures/flagship/app/api/seats/route.ts create mode 100644 fixtures/flagship/app/api/session/route.ts create mode 100644 fixtures/flagship/app/checkout/checkout-client.tsx create mode 100644 fixtures/flagship/app/checkout/page.tsx create mode 100644 fixtures/flagship/app/components/AirportSelect.tsx create mode 100644 fixtures/flagship/app/components/DateSelect.tsx create mode 100644 fixtures/flagship/app/components/FareHoldBanner.tsx create mode 100644 fixtures/flagship/app/components/Footer.tsx create mode 100644 fixtures/flagship/app/components/Header.tsx create mode 100644 fixtures/flagship/app/components/PaxSelect.tsx create mode 100644 fixtures/flagship/app/components/SearchWidget.tsx create mode 100644 fixtures/flagship/app/components/SliderCaptcha.tsx create mode 100644 fixtures/flagship/app/components/StepBar.tsx create mode 100644 fixtures/flagship/app/components/format.ts create mode 100644 fixtures/flagship/app/confirmation/confirmation-client.tsx create mode 100644 fixtures/flagship/app/confirmation/page.tsx create mode 100644 fixtures/flagship/app/destinations/[slug]/page.tsx create mode 100644 fixtures/flagship/app/destinations/page.tsx create mode 100644 fixtures/flagship/app/globals.css create mode 100644 fixtures/flagship/app/guide/page.mdx create mode 100644 fixtures/flagship/app/layout.tsx create mode 100644 fixtures/flagship/app/llms.txt/route.ts create mode 100644 fixtures/flagship/app/organization-json-ld.tsx create mode 100644 fixtures/flagship/app/page.tsx create mode 100644 fixtures/flagship/app/results/page.tsx create mode 100644 fixtures/flagship/app/results/results-client.tsx create mode 100644 fixtures/flagship/app/seats/page.tsx create mode 100644 fixtures/flagship/app/seats/seats-client.tsx create mode 100644 fixtures/flagship/app/sitemap.ts create mode 100644 fixtures/flagship/app/state/BookingContext.tsx create mode 100644 fixtures/flagship/ax.config.ts create mode 100644 fixtures/flagship/cards.golden/server-card.json create mode 100644 fixtures/flagship/cards.golden/server-card/api-mcp.json create mode 100644 fixtures/flagship/cards.golden/server-card/api-public-mcp.json create mode 100644 fixtures/flagship/lib/data/airports.ts create mode 100644 fixtures/flagship/lib/data/flights.ts create mode 100644 fixtures/flagship/lib/data/seatmaps.ts create mode 100644 fixtures/flagship/lib/services/api-guard.ts create mode 100644 fixtures/flagship/lib/services/booking.ts create mode 100644 fixtures/flagship/lib/services/payment.ts create mode 100644 fixtures/flagship/lib/services/search.ts create mode 100644 fixtures/flagship/lib/services/session.ts rename fixtures/{mdx-content => flagship}/mdx-components.tsx (100%) create mode 100644 fixtures/flagship/middleware.ts rename fixtures/{config-overrides => flagship}/next-env.d.ts (100%) rename fixtures/{mdx-content => flagship}/next.config.mjs (100%) rename fixtures/{mdx-content => flagship}/package.json (79%) create mode 100644 fixtures/flagship/public/agents.md create mode 100644 fixtures/flagship/public/destinations.md create mode 100644 fixtures/flagship/public/openapi.json create mode 100644 fixtures/flagship/public/robots.txt create mode 100644 fixtures/flagship/report.golden.json rename fixtures/{llms-txt => flagship}/tsconfig.json (68%) rename fixtures/{mcp-adapter-gated => flagship}/twins.golden/404.md (51%) create mode 100644 fixtures/flagship/twins.golden/auth.md create mode 100644 fixtures/flagship/twins.golden/checkout.md create mode 100644 fixtures/flagship/twins.golden/confirmation.md create mode 100644 fixtures/flagship/twins.golden/guide.md create mode 100644 fixtures/flagship/twins.golden/index.md create mode 100644 fixtures/flagship/twins.golden/results.md create mode 100644 fixtures/flagship/twins.golden/seats.md delete mode 100644 fixtures/llms-txt/README.md delete mode 100644 fixtures/llms-txt/app/llms.txt/route.ts delete mode 100644 fixtures/llms-txt/app/page.tsx delete mode 100644 fixtures/llms-txt/ax.config.ts delete mode 100644 fixtures/llms-txt/package.json delete mode 100644 fixtures/llms-txt/report.golden.json delete mode 100644 fixtures/llms-txt/twins.golden/404.md delete mode 100644 fixtures/mcp-adapter-gated/README.md delete mode 100644 fixtures/mcp-adapter-gated/app/[transport]/route.ts delete mode 100644 fixtures/mcp-adapter-gated/app/layout.tsx delete mode 100644 fixtures/mcp-adapter-gated/app/page.tsx delete mode 100644 fixtures/mcp-adapter-gated/ax.config.ts delete mode 100644 fixtures/mcp-adapter-gated/cards.golden/server-card.json delete mode 100644 fixtures/mcp-adapter-gated/next-env.d.ts delete mode 100644 fixtures/mcp-adapter-gated/next.config.mjs delete mode 100644 fixtures/mcp-adapter-gated/package.json delete mode 100644 fixtures/mcp-adapter-gated/report.golden.json delete mode 100644 fixtures/mcp-adapter-gated/tsconfig.json delete mode 100644 fixtures/mcp-adapter-gated/twins.golden/auth.md delete mode 100644 fixtures/mcp-adapter/README.md delete mode 100644 fixtures/mcp-adapter/app/[transport]/route.ts delete mode 100644 fixtures/mcp-adapter/app/layout.tsx delete mode 100644 fixtures/mcp-adapter/app/page.tsx delete mode 100644 fixtures/mcp-adapter/ax.config.ts delete mode 100644 fixtures/mcp-adapter/cards.golden/server-card.json delete mode 100644 fixtures/mcp-adapter/next-env.d.ts delete mode 100644 fixtures/mcp-adapter/next.config.mjs delete mode 100644 fixtures/mcp-adapter/twins.golden/404.md delete mode 100644 fixtures/mcp-multi-server/README.md delete mode 100644 fixtures/mcp-multi-server/app/api/mcp/route.ts delete mode 100644 fixtures/mcp-multi-server/app/api/public/mcp/route.ts delete mode 100644 fixtures/mcp-multi-server/app/layout.tsx delete mode 100644 fixtures/mcp-multi-server/app/page.tsx delete mode 100644 fixtures/mcp-multi-server/ax.config.ts delete mode 100644 fixtures/mcp-multi-server/cards.golden/server-card.json delete mode 100644 fixtures/mcp-multi-server/cards.golden/server-card/api-mcp.json delete mode 100644 fixtures/mcp-multi-server/cards.golden/server-card/api-public-mcp.json delete mode 100644 fixtures/mcp-multi-server/next-env.d.ts delete mode 100644 fixtures/mcp-multi-server/next.config.mjs delete mode 100644 fixtures/mcp-multi-server/package.json delete mode 100644 fixtures/mcp-multi-server/tsconfig.json delete mode 100644 fixtures/mcp-multi-server/twins.golden/auth.md delete mode 100644 fixtures/mcp-multi-server/twins.golden/index.md delete mode 100644 fixtures/mdx-content/README.md delete mode 100644 fixtures/mdx-content/app/guide/page.mdx delete mode 100644 fixtures/mdx-content/app/layout.tsx delete mode 100644 fixtures/mdx-content/app/page.tsx delete mode 100644 fixtures/mdx-content/app/widget.tsx delete mode 100644 fixtures/mdx-content/app/widgets/page.mdx delete mode 100644 fixtures/mdx-content/next-env.d.ts delete mode 100644 fixtures/mdx-content/report.golden.json delete mode 100644 fixtures/mdx-content/tsconfig.json delete mode 100644 fixtures/mdx-content/twins.golden/404.md delete mode 100644 fixtures/mdx-content/twins.golden/guide.md delete mode 100644 fixtures/mdx-content/twins.golden/index.md delete mode 100644 fixtures/middleware/README.md delete mode 100644 fixtures/middleware/app/blog/[slug]/page.tsx delete mode 100644 fixtures/middleware/app/docs/page.tsx delete mode 100644 fixtures/middleware/app/layout.tsx delete mode 100644 fixtures/middleware/app/page.tsx delete mode 100644 fixtures/middleware/app/private/page.tsx delete mode 100644 fixtures/middleware/app/shell/page.tsx delete mode 100644 fixtures/middleware/ax.config.ts delete mode 100644 fixtures/middleware/middleware.ts delete mode 100644 fixtures/middleware/next-env.d.ts delete mode 100644 fixtures/middleware/next.config.mjs delete mode 100644 fixtures/middleware/package.json delete mode 100644 fixtures/middleware/public/docs.md delete mode 100644 fixtures/middleware/report.golden.json delete mode 100644 fixtures/middleware/tsconfig.json delete mode 100644 fixtures/middleware/twins.golden/404.md delete mode 100644 fixtures/middleware/twins.golden/index.md create mode 100644 fixtures/next-auth/README.md create mode 100644 fixtures/next-auth/app/api/auth/[...nextauth]/route.ts rename fixtures/{llms-txt => next-auth}/app/layout.tsx (66%) create mode 100644 fixtures/next-auth/app/page.tsx rename fixtures/{mdx-content => next-auth}/ax.config.ts (77%) rename fixtures/{discovery => next-auth}/next-env.d.ts (100%) rename fixtures/{discovery => next-auth}/next.config.mjs (100%) rename fixtures/{config-overrides => next-auth}/package.json (87%) rename fixtures/{webmcp-declarative => next-auth}/report.golden.json (94%) rename fixtures/{config-overrides => next-auth}/tsconfig.json (100%) rename fixtures/{webmcp-declarative => next-auth}/twins.golden/404.md (83%) delete mode 100644 fixtures/openapi/README.md delete mode 100644 fixtures/openapi/app/layout.tsx delete mode 100644 fixtures/openapi/app/page.tsx delete mode 100644 fixtures/openapi/ax.config.ts delete mode 100644 fixtures/openapi/next-env.d.ts delete mode 100644 fixtures/openapi/next.config.mjs delete mode 100644 fixtures/openapi/public/openapi.json delete mode 100644 fixtures/openapi/report.golden.json delete mode 100644 fixtures/openapi/tsconfig.json delete mode 100644 fixtures/openapi/twins.golden/404.md delete mode 100644 fixtures/openapi/twins.golden/auth.md delete mode 100644 fixtures/pages-bare/README.md delete mode 100644 fixtures/pages-bare/ax.config.ts delete mode 100644 fixtures/pages-bare/next.config.mjs delete mode 100644 fixtures/pages-bare/package.json delete mode 100644 fixtures/pages-bare/pages/404.tsx delete mode 100644 fixtures/pages-bare/pages/about.tsx delete mode 100644 fixtures/pages-bare/pages/blog/[slug].tsx delete mode 100644 fixtures/pages-bare/pages/index.tsx delete mode 100644 fixtures/pages-bare/report.golden.json delete mode 100644 fixtures/pages-bare/tsconfig.json delete mode 100644 fixtures/pages-bare/twins.golden/404.md delete mode 100644 fixtures/pages-mcp/README.md delete mode 100644 fixtures/pages-mcp/ax.config.ts delete mode 100644 fixtures/pages-mcp/cards.golden/server-card.json delete mode 100644 fixtures/pages-mcp/next-env.d.ts delete mode 100644 fixtures/pages-mcp/next.config.mjs delete mode 100644 fixtures/pages-mcp/package.json delete mode 100644 fixtures/pages-mcp/pages/api/[transport].ts delete mode 100644 fixtures/pages-mcp/pages/index.tsx delete mode 100644 fixtures/pages-mcp/report.golden.json delete mode 100644 fixtures/pages-mcp/tsconfig.json delete mode 100644 fixtures/pages-mcp/twins.golden/404.md delete mode 100644 fixtures/pages-webmcp-declarative/README.md delete mode 100644 fixtures/pages-webmcp-declarative/ax.config.ts delete mode 100644 fixtures/pages-webmcp-declarative/next-env.d.ts delete mode 100644 fixtures/pages-webmcp-declarative/next.config.mjs delete mode 100644 fixtures/pages-webmcp-declarative/package.json delete mode 100644 fixtures/pages-webmcp-declarative/pages/index.tsx delete mode 100644 fixtures/pages-webmcp-declarative/report.golden.json delete mode 100644 fixtures/pages-webmcp-declarative/tsconfig.json delete mode 100644 fixtures/pages-webmcp-declarative/twins.golden/404.md delete mode 100644 fixtures/webmcp-declarative/README.md delete mode 100644 fixtures/webmcp-declarative/app/layout.tsx delete mode 100644 fixtures/webmcp-declarative/app/page.tsx delete mode 100644 fixtures/webmcp-declarative/next-env.d.ts delete mode 100644 fixtures/webmcp-declarative/next.config.mjs delete mode 100644 fixtures/webmcp-declarative/package.json delete mode 100644 fixtures/webmcp-declarative/tsconfig.json delete mode 100644 fixtures/webmcp-imperative/README.md delete mode 100644 fixtures/webmcp-imperative/app/layout.tsx delete mode 100644 fixtures/webmcp-imperative/app/page.tsx delete mode 100644 fixtures/webmcp-imperative/next-env.d.ts delete mode 100644 fixtures/webmcp-imperative/next.config.mjs delete mode 100644 fixtures/webmcp-imperative/package.json delete mode 100644 fixtures/webmcp-imperative/tsconfig.json create mode 100644 fixtures/webmcp/README.md create mode 100644 fixtures/webmcp/app/layout.tsx create mode 100644 fixtures/webmcp/app/page.tsx rename fixtures/{webmcp-imperative => webmcp}/app/register-tools.tsx (100%) rename fixtures/{webmcp-declarative => webmcp}/ax.config.ts (84%) rename fixtures/{llms-txt => webmcp}/next-env.d.ts (100%) rename fixtures/{llms-txt => webmcp}/next.config.mjs (100%) rename fixtures/{openapi => webmcp}/package.json (93%) rename fixtures/{mcp-adapter => webmcp}/report.golden.json (91%) rename fixtures/{discovery => webmcp}/tsconfig.json (100%) rename fixtures/{config-overrides => webmcp}/twins.golden/404.md (86%) rename fixtures/{webmcp-declarative => webmcp}/types/webmcp-forms.d.ts (100%) rename fixtures/{webmcp-imperative => webmcp}/types/webmcp.d.ts (100%) create mode 100644 scripts/mutation-tests.mjs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4768af1..9eb0cd0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -77,12 +77,19 @@ jobs: - name: Verify report snapshots run: pnpm reports:verify - # Dogfood the negotiation middleware over HTTP: `next start` on the built middleware fixture, + # Dogfood the negotiation middleware over HTTP: `next start` on the built flagship fixture, # then the dual-fetch probes (Ora's markdown-negotiation-vary semantics), the cloaking # guards, and the gated/dynamic/unknown-path branches — the Edge bundle serving for real. - name: Middleware dual-fetch dogfood run: pnpm dogfood:middleware + # Multi-build mutation tests over the flagship fixture: idempotence, user-edit preservation, + # orphaned-twin cleanup, additive change, and manifest drift — each is a copy → build → + # mutate → rebuild sequence with targeted delta assertions (the build-N+1 bugs the one-shot + # golden layer can't see). + - name: Flagship mutation tests + run: pnpm mutation:test + # Official ARD conformance tool (vendored from ards-project/ard-spec — spec/ard/README.md) # over every catalog the fixture builds just generated. jsonschema enables the tool's strict # JSON Schema layer on top of its semantic checks. diff --git a/.gitignore b/.gitignore index cbad2d2..29aa527 100644 --- a/.gitignore +++ b/.gitignore @@ -26,16 +26,11 @@ fixtures/**/public/.well-known/mcp/server-card/ # Markdown twins + the auth guide ax's postbuild step generates in fixtures — build output. The # tracked artifacts are the normalized snapshots under fixtures/*/twins.golden/. Scoped to the # exact generated paths so hand-authored fixture markdown (markdown-twins/public/hand.md, -# discovery/public/agents.md) stays committed; a newly generated file that isn't listed here shows -# up as untracked, which is the prompt to snapshot and list it. +# flagship/public/destinations.md) stays committed; a newly generated file that isn't listed here +# shows up as untracked, which is the prompt to snapshot and list it. fixtures/markdown-twins/public/index.md fixtures/markdown-twins/public/guides/ fixtures/markdown-twins/public/live.md -fixtures/mdx-content/public/index.md -fixtures/mdx-content/public/guide.md -fixtures/mcp-multi-server/public/index.md -fixtures/discovery/public/index.md -fixtures/middleware/public/index.md fixtures/*/public/auth.md fixtures/*/public/404.md @@ -52,3 +47,18 @@ fixtures/**/.ora/ # npm pack build artifact (not committed in this repo; the demo repo commits its own copy) *.tgz + +# Flagship fixture generated twins (its hand-authored twin, public/destinations.md, stays +# committed; generated per-slug twins under public/destinations/ are covered by the dir entry). +fixtures/flagship/public/index.md +fixtures/flagship/public/results.md +fixtures/flagship/public/checkout.md +fixtures/flagship/public/seats.md +fixtures/flagship/public/confirmation.md +fixtures/flagship/public/guide.md +fixtures/flagship/public/account.md +fixtures/flagship/public/destinations/ + +# flagship-pages fixture generated twins — build output. +fixtures/flagship-pages/public/index.md +fixtures/flagship-pages/public/destinations.md diff --git a/README.md b/README.md index 4dfb250..bdfc122 100644 --- a/README.md +++ b/README.md @@ -181,5 +181,5 @@ actionable message. ``` packages/ax the plugin / CLI (`@ora-ai/ax`) — the npm package (3 runtime deps: ajv, ajv-formats, jiti) spec/ vendored AI Catalog spec + hand-written JSON Schema + validator oracle -fixtures/* minimal-but-real Next.js apps — the test suite, docs examples, and eval corpus +fixtures/* real Next.js apps — the flagship (a full demo-app fork) + single-axis fixtures; the test suite, docs examples, and eval corpus ``` diff --git a/fixtures/config-overrides/README.md b/fixtures/config-overrides/README.md deleted file mode 100644 index 7475f3f..0000000 --- a/fixtures/config-overrides/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# fixture: config-overrides - -A Next.js app that declares catalog config in `ax.config.ts` — the config surface. - -**Exercises:** - -- **Config loading** — a TypeScript `ax.config.ts` (typed with `AxConfig` imported from the - plugin) is discovered and loaded by the postbuild CLI via `jiti`. -- **Config-declared entries** — `docs` (`text/html`) and `skills` (`application/ai-skill+md`) - pointers are emitted into the catalog. (Until zero-config detection applies, these are the only - source of entries; the same `identifier`-matching merge layers config over _inferred_ entries.) -- **`isGated` safety net + re-inclusion** — the config also declares two `/api/auth/**` entries, - which the built-in gating floor covers. The config's `isGated` composes `defaultIsGated` with a - re-inclusion: `urn:example:auth-status` is un-gated (the matcher returns `false` for it) and - survives; `urn:example:auth-internal` stays gated, and since a plain `text/html` pointer has no - derivable auth descriptor, ax drops it from the emitted catalog even though it's declared. - Demonstrates "precision over recall" and the `isGated` replacement for the old denylist/allowlist. - -So the emitted catalog contains exactly three entries: `urn:example:docs`, `urn:example:skills`, -and `urn:example:auth-status` — asserted in -`packages/ax/test/fixtures-integration.test.ts`. diff --git a/fixtures/config-overrides/app/layout.tsx b/fixtures/config-overrides/app/layout.tsx deleted file mode 100644 index 512749e..0000000 --- a/fixtures/config-overrides/app/layout.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import type { ReactNode } from 'react'; - -export const metadata = { - title: 'Config Overrides Fixture', - description: 'A Next.js app that declares catalog entries and an isGated policy via ax.config.', -}; - -export default function RootLayout({ children }: { children: ReactNode }) { - return ( - - {children} - - ); -} diff --git a/fixtures/config-overrides/app/page.tsx b/fixtures/config-overrides/app/page.tsx deleted file mode 100644 index e866382..0000000 --- a/fixtures/config-overrides/app/page.tsx +++ /dev/null @@ -1,11 +0,0 @@ -export default function Home() { - return ( -
-

Config overrides fixture

-

- Exercises ax.config.ts: config-declared entries plus isGated - filtering. -

-
- ); -} diff --git a/fixtures/config-overrides/ax.config.ts b/fixtures/config-overrides/ax.config.ts deleted file mode 100644 index 9345e63..0000000 --- a/fixtures/config-overrides/ax.config.ts +++ /dev/null @@ -1,57 +0,0 @@ -import type { AxConfig } from '@ora-ai/ax'; - -// This is the fixture that documents-and-tests the config surface. Until zero-config artifact -// detection lands there are no *inferred* entries to override, so here every entry is -// config-declared; the same `identifier`-matching merge layers these over inferred entries. -const config: AxConfig = { - // `isGated` supersedes the old denylist/allowlist pair. Supplying it replaces the built-in floor - // wholesale, so this restates the floor (the built-in `defaultIsGated` gates `/api/auth/**` and - // `/api/webhooks/**`) and then re-includes this app's one public auth endpoint by returning - // false for it — the job the old allowlist did. A gated entry ax can't describe (a plain - // text/html pointer has no derivable auth descriptor) is dropped rather than published. - // - // The floor is inlined here rather than composed from the exported `defaultIsGated`: this file is - // evaluated for real (via jiti) by the test suite, which runs against src without building the - // package's dist, so importing a *runtime* value from `@ora-ai/ax` would fail to resolve. The - // README shows the `defaultIsGated`-composition form for consumers of the published package. - isGated: ({ path }) => - (path.startsWith('/api/auth/') || path.startsWith('/api/webhooks/')) && - path !== '/api/auth/status', - entries: [ - // Ordinary docs/skills pointers — absolute URLs, so they're spec-valid on their own (the - // catalog schema requires `url` to be an absolute URI). Identifiers follow the ARD URN format - // (`urn:air::`, spec §4.2.1) the emission gate enforces, and every - // entry carries the `displayName` the ARD schema requires. - { - identifier: 'urn:air:example.com:docs', - type: 'text/html', - displayName: 'API documentation', - url: 'https://example.com/docs', - }, - { - identifier: 'urn:air:example.com:skills', - type: 'application/ai-skill+md', - displayName: 'Agent skills', - url: 'https://github.com/example/agent-skills', - }, - // Gated by the default floor but re-included by the `isGated` matcher above, so it survives - // into the catalog. - { - identifier: 'urn:air:example.com:auth-status', - type: 'text/html', - displayName: 'Auth status endpoint', - url: 'https://example.com/api/auth/status', - }, - // Gated by the default floor and NOT re-included — ax can't describe its auth, so the safety - // net drops it from the catalog even though it's declared here. Demonstrates "precision over - // recall". - { - identifier: 'urn:air:example.com:auth-internal', - type: 'text/html', - displayName: 'Internal auth endpoint', - url: 'https://example.com/api/auth/internal', - }, - ], -}; - -export default config; diff --git a/fixtures/config-overrides/report.golden.json b/fixtures/config-overrides/report.golden.json deleted file mode 100644 index 9ac8c8e..0000000 --- a/fixtures/config-overrides/report.golden.json +++ /dev/null @@ -1,196 +0,0 @@ -{ - "generatedAt": "", - "basePath": "", - "routers": [ - "app" - ], - "catalog": { - "entryCount": 3, - "entries": [ - { - "identifier": "urn:air:example.com:docs", - "type": "text/html", - "url": "https://example.com/docs", - "displayName": "API documentation" - }, - { - "identifier": "urn:air:example.com:skills", - "type": "application/ai-skill+md", - "url": "https://github.com/example/agent-skills", - "displayName": "Agent skills" - }, - { - "identifier": "urn:air:example.com:auth-status", - "type": "text/html", - "url": "https://example.com/api/auth/status", - "displayName": "Auth status endpoint" - } - ], - "path": "public/.well-known/ai-catalog.json", - "target": "static" - }, - "mcp": { - "mounts": [], - "unreviewedMounts": [] - }, - "agent404": { - "notFoundPresent": false, - "agentAware": false, - "pages": [], - "markdownGuide": "public/404.md" - }, - "middleware": { - "present": false, - "wiredToAx": false - }, - "artifacts": { - "robotsTxt": { - "found": false - }, - "sitemap": { - "found": false - }, - "agentsMd": { - "found": false - }, - "jsonLd": { - "found": false - }, - "llmsTxt": { - "found": false - }, - "openapi": { - "found": false - } - }, - "auth": { - "gatedSurfaces": [] - }, - "scaffolds": {}, - "markdownTwins": { - "enabled": true, - "written": [], - "userOwned": [], - "skipped": [ - { - "route": "/", - "reason": "too-little-text", - "detail": "The prerendered HTML carries under 200 characters of text — a JS-shell page, whose twin would be an empty page presented as content. If this page renders client-side, keep page.tsx a server component that exports its own `metadata` (title + description) and renders your client component — ax then derives a minimal metadata twin, with no pre-hydration placeholder DOM (which would flicker)." - } - ], - "dynamicRouteCount": 0, - "deleted": [] - }, - "sizes": [ - { - "artifact": "ai-catalog.json", - "path": "public/.well-known/ai-catalog.json", - "bytes": 658, - "chars": 658, - "tokens": 165 - }, - { - "artifact": "404.md", - "path": "public/404.md", - "bytes": 655, - "chars": 649, - "tokens": 162 - } - ], - "ora": { - "checks": [ - { - "id": "ard-catalog", - "artifact": "ai-catalog.json", - "status": "addressed" - }, - { - "id": "agent-discovery-file", - "artifact": "ai-catalog.json", - "status": "addressed" - }, - { - "id": "llms-txt-exists", - "artifact": "llms.txt", - "status": "actionable" - }, - { - "id": "llms-txt-formatting", - "artifact": "llms.txt", - "status": "actionable" - }, - { - "id": "markdown-url-fallback", - "artifact": "markdown-twins", - "status": "actionable", - "note": "No markdown twin could be derived for the homepage (too-little-text): The prerendered HTML carries under 200 characters of text — a JS-shell page, whose twin would be an empty page presented as content. If this page renders client-side, keep page.tsx a server component that exports its own `metadata` (title + description) and renders your client component — ax then derives a minimal metadata twin, with no pre-hydration placeholder DOM (which would flicker). The report’s markdownTwins.skipped section lists every twin-less route with its reason." - }, - { - "id": "markdown-frontmatter", - "artifact": "markdown-twins", - "status": "actionable", - "note": "No markdown twin could be derived for the homepage (too-little-text): The prerendered HTML carries under 200 characters of text — a JS-shell page, whose twin would be an empty page presented as content. If this page renders client-side, keep page.tsx a server component that exports its own `metadata` (title + description) and renders your client component — ax then derives a minimal metadata twin, with no pre-hydration placeholder DOM (which would flicker). The report’s markdownTwins.skipped section lists every twin-less route with its reason." - }, - { - "id": "robots-ai-policy-quality", - "artifact": "robots.txt", - "status": "actionable" - }, - { - "id": "sitemap", - "artifact": "sitemap", - "status": "actionable" - }, - { - "id": "agent-instruction", - "artifact": "agents.md", - "status": "actionable" - }, - { - "id": "json-ld", - "artifact": "json-ld", - "status": "actionable" - }, - { - "id": "org-schema-completeness", - "artifact": "json-ld", - "status": "actionable" - }, - { - "id": "openapi-spec", - "artifact": "openapi.json", - "status": "actionable" - }, - { - "id": "mcp-server", - "artifact": "mcp-server", - "status": "actionable" - }, - { - "id": "markdown-negotiation", - "artifact": "middleware", - "status": "actionable", - "note": "Serve agents the generated markdown automatically: First run `npx ax manifest` (and wire it as the \"prebuild\" script so it stays fresh — `middleware.ts` is compiled during `next build`, so the manifest must exist before the build starts). Then: add `import { withAx } from '@ora-ai/ax/middleware';` and `import { axManifest } from './ax-manifest';`, create middleware.ts with `export default withAx({ manifest: axManifest });`, and export the matcher literal `export const config = { matcher: ['/((?!_next|api|.*\\\\..*|favicon|robots|health|status).*)'] };`. The middleware rewrites a request to its markdown twin only when the manifest lists one, never touches gated paths, and answers unknown URLs from detected agents with a 200 markdown wayfinding body — it composes with (never replaces) your existing middleware, and keeps your own gating (UA filters, bot walls) from ever blocking the published discovery artifacts." - }, - { - "id": "markdown-negotiation-vary", - "artifact": "middleware", - "status": "actionable", - "note": "Serve agents the generated markdown automatically: First run `npx ax manifest` (and wire it as the \"prebuild\" script so it stays fresh — `middleware.ts` is compiled during `next build`, so the manifest must exist before the build starts). Then: add `import { withAx } from '@ora-ai/ax/middleware';` and `import { axManifest } from './ax-manifest';`, create middleware.ts with `export default withAx({ manifest: axManifest });`, and export the matcher literal `export const config = { matcher: ['/((?!_next|api|.*\\\\..*|favicon|robots|health|status).*)'] };`. The middleware rewrites a request to its markdown twin only when the manifest lists one, never touches gated paths, and answers unknown URLs from detected agents with a 200 markdown wayfinding body — it composes with (never replaces) your existing middleware, and keeps your own gating (UA filters, bot walls) from ever blocking the published discovery artifacts." - } - ] - }, - "warnings": [ - "isGated excluded entry \"urn:air:example.com:auth-internal\" (/api/auth/internal) — it is gated but ax can't derive an auth descriptor for it, so it is not published as an open surface. Declare it in ax.config \"entries\" with an \"auth\" descriptor to list it as gated instead." - ], - "recommendations": [ - "No OpenAPI doc found (public/openapi.json) — an OpenAPI doc lets agents understand and call your API without guessing at routes or schemas. ax never generates one; produce it however your API is defined (many schema/validation libraries can emit OpenAPI from your existing route definitions) and commit the result to public/openapi.json.", - "No llms.txt found — a short natural-language guide agents read to decide whether your site fits their task, and to find your key pages. Add one at app/llms.txt/route.ts (or public/llms.txt), or set scaffoldLlmsTxt: true in ax.config to have a starter written for you. Pair it with JSON-LD structured data (an Organization block with a sameAs array): llms.txt says what your site is for, JSON-LD identifies it as an entity registries can rank — the two reinforce each other, so add both rather than one alone.", - "No sitemap found — add one so agents can discover all public routes. Use the built-in Next.js path (app/sitemap.ts); ax never generates a sitemap itself. Give each entry a date (agents use it to judge freshness), then reference it from robots.txt with a \"Sitemap:\" line.", - "No robots.txt found — add one (app/robots.ts or public/robots.txt) that explicitly Allows the reputable AI agents you want to reach, scoped to specific User-agent groups (never \"User-agent: *\"), plus a \"Sitemap:\" line. Note: Next's MetadataRoute.Robots (app/robots.ts) has no field for the \"Agentmap:\" catalog pointer — put that line in a static public/robots.txt. Set scaffoldRobots: true in ax.config to have one written for you.", - "No agents.md found — consider adding one (public/agents.md) with when-to-use / when-NOT-to-use guidance for agents. ax won't write its content for you; the companion skill can help author it from your repo.", - "No JSON-LD structured data found in your layouts/pages — structured data is how registries and agents disambiguate and rank your site. Add an Organization block in your root layout (