From ff151bf5baa3d2e67dbc159fbd65655528843d6c Mon Sep 17 00:00:00 2001 From: sriramveeraghanta Date: Mon, 20 Apr 2026 20:35:20 +0530 Subject: [PATCH 1/8] docs(spec): add agent discovery design MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Brainstormed spec for three agent-discovery fixes: Content-Signal in robots.txt, Link response headers, and markdown content negotiation. Ephemeral — to be removed after implementation. --- .../2026-04-20-agent-discovery-design.md | 137 ++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 superpowers/specs/2026-04-20-agent-discovery-design.md diff --git a/superpowers/specs/2026-04-20-agent-discovery-design.md b/superpowers/specs/2026-04-20-agent-discovery-design.md new file mode 100644 index 00000000..16be9f0d --- /dev/null +++ b/superpowers/specs/2026-04-20-agent-discovery-design.md @@ -0,0 +1,137 @@ +# Agent Discovery Improvements — Design + +**Date:** 2026-04-20 +**Status:** Draft — pending user review +**Site:** developers.plane.so (VitePress static site on Vercel) + +## Goal + +Improve agent/AI-crawler discovery on the Plane developer docs by addressing three gaps flagged by an external audit: + +1. No `Link` response headers advertising useful resources (RFC 8288). +2. No `Content-Type: text/markdown` negotiation for agents that prefer markdown. +3. No `Content-Signal` directives in `robots.txt` declaring AI usage preferences. + +## Non-Goals + +- Creating an OpenAPI spec or `/.well-known/api-catalog` (none exists today). +- Flattening or cleaning VitePress custom components (``, ``, etc.) in the markdown output. Agents receive source markdown with those tags present. +- Changes to sidebar, navigation, or published content. +- Edge-runtime logic. The design stays fully static. + +## Current State + +- VitePress static site with source in `docs/`, build output in `docs/.vitepress/dist/`. +- Deployed on Vercel; config in `vercel.json` (already has `cleanUrls: true` and a `redirects` block). +- `docs/public/robots.txt` exists and declares a sitemap and a (non-standard) `LLMs-txt:` line. +- `docs/public/llms.txt` and `docs/public/llms-full.txt` already exist. +- No `/.well-known/` directory. No OpenAPI file. + +## Design + +### Fix 1 — Content Signals in `robots.txt` + +Append a single directive to `docs/public/robots.txt`: + +``` +Content-Signal: search=yes, ai-train=yes, ai-input=yes +``` + +**Policy rationale:** Plane's existing posture (publishing `llms.txt` and `llms-full.txt`) is maximally open. The Content Signal matches that — search indexing, model training, and AI-answer grounding are all permitted. + +Placement: directly below the `User-agent: *` / `Allow:` block, before the `Sitemap:` line. One line, no other changes. + +### Fix 2 — `Link` response headers (RFC 8288) + +Add a `headers` block to `vercel.json` applied to all paths. A single `Link` header carries three relations, comma-separated per RFC 8288 §3.5: + +``` +Link: ; rel="describedby"; type="text/plain", + ; rel="service-doc"; type="text/html", + ; rel="sitemap"; type="application/xml" +``` + +**Why each relation:** +- `describedby` → `/llms.txt` — points agents to the LLM-friendly site map (canonical discovery). +- `service-doc` → `/api-reference/introduction` — registered IANA relation for human-readable API docs. +- `sitemap` → `/sitemap.xml` — widely supported, de facto standard. + +Applied to `source: "/(.*)"` so the headers land on every page, not just the homepage. This lets any deep-link entry point still expose discovery metadata. + +### Fix 3 — Markdown content negotiation (static approach) + +Two pieces, both in files already owned by this repo: + +**3a. Build hook in `docs/.vitepress/config.mts`** + +Add a `buildEnd(siteConfig)` function that copies every source `.md` file from `siteConfig.srcDir` to the corresponding path under `siteConfig.outDir`, preserving directory structure. The hook: + +- Walks `srcDir` recursively. +- Skips anything under `.vitepress/` (config/theme/cache/dist). +- For each `.md` file, computes the relative path and writes it into `outDir` at the same relative location, creating intermediate directories as needed. +- Preserves file contents byte-for-byte. Frontmatter and VitePress custom components (``, ``, etc.) are included verbatim — acknowledged limitation. + +Result: after `pnpm build`, `dist/api-reference/introduction.md` exists alongside `dist/api-reference/introduction.html`. + +**3b. Vercel rewrite in `vercel.json`** + +Add a `rewrites` block that matches on the `Accept` header: + +```json +{ + "rewrites": [ + { + "source": "/:path*", + "has": [{ "type": "header", "key": "accept", "value": ".*text/markdown.*" }], + "destination": "/:path*.md" + } + ] +} +``` + +Vercel's default MIME mapping serves `.md` files as `text/markdown; charset=utf-8`. If preview-deployment testing shows a different `Content-Type` (e.g., `text/plain`), add an explicit header override to `vercel.json` for `source: "/(.*).md"` setting `Content-Type: text/markdown; charset=utf-8`. This is the only identified contingency. + +Interaction with existing `cleanUrls: true`: `cleanUrls` strips `.html` from URLs; the `.md` rewrite only fires when the `Accept` header matches, so browsers still get HTML via the default behavior. The two directives don't conflict — `rewrites` run before `cleanUrls` resolution. + +### Known Limitations (accepted) + +- **Raw frontmatter in markdown responses.** Agents will see the YAML frontmatter block at the top of each page. Most agent parsers tolerate this. +- **VitePress components in markdown responses.** Pages using ``, ``, ``, ``, `` will return those tags literally. Prose content is still machine-readable; component-rendered detail (e.g., parameter type badges) is not flattened. +- **No content-type validation at build time.** If a page has no source `.md` (unlikely in VitePress), the rewrite returns a 404; the HTML version remains available for normal requests. + +## File Changes (summary) + +| File | Change | +| ---------------------------------------- | ------------------------------------------------------------------ | +| `docs/public/robots.txt` | Add one `Content-Signal:` line | +| `vercel.json` | Add `headers` block (Fix 2) and `rewrites` block (Fix 3b) | +| `docs/.vitepress/config.mts` | Add `buildEnd` hook that copies `.md` files into `dist` (Fix 3a) | + +No new dependencies. No new directories. + +## Testing + +**Local (before merge):** + +- `pnpm build` — verify no errors, then check that `docs/.vitepress/dist/` contains `.md` copies (spot-check `dist/api-reference/introduction.md`). +- `pnpm check:format` — Prettier must pass. + +**On Vercel preview deployment:** + +- `curl -I https://.vercel.app/` — verify the `Link:` response header is present and contains all three relations. +- `curl -H "Accept: text/markdown" https://.vercel.app/api-reference/introduction` — verify the body is markdown and `Content-Type: text/markdown; charset=utf-8`. +- `curl https://.vercel.app/` (no custom Accept) — verify HTML is still returned (regression check). +- `curl https://.vercel.app/robots.txt | grep Content-Signal` — verify the directive is present. + +**Rationale for preview-only testing:** `pnpm preview` serves VitePress's built output directly and does not run Vercel's `rewrites` / `headers` engine. The header negotiation can only be validated on a Vercel deployment. + +## Rollout + +1. Implement all three fixes in a single PR branch (`feat/agent-discovery`). +2. Push, let Vercel preview build, run the `curl` checks above against the preview URL. +3. Merge to `master`; Vercel promotes to production. +4. Re-run `isitagentready.com` (or the source audit) against production to confirm the three issues are resolved. + +## Post-Implementation + +Once the implementation PR is merged and verified in production, **delete the `superpowers/` directory** from the repo — the spec is ephemeral and does not belong in the long-lived tree. From 2bf43d2a37c099afdafd4c8f7dc0a3c71157eb2b Mon Sep 17 00:00:00 2001 From: sriramveeraghanta Date: Mon, 20 Apr 2026 20:38:49 +0530 Subject: [PATCH 2/8] docs(plan): add agent discovery implementation plan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-by-task implementation plan for the three fixes in the companion spec. Ephemeral — removed as the final task. --- .../plans/2026-04-20-agent-discovery.md | 404 ++++++++++++++++++ 1 file changed, 404 insertions(+) create mode 100644 superpowers/plans/2026-04-20-agent-discovery.md diff --git a/superpowers/plans/2026-04-20-agent-discovery.md b/superpowers/plans/2026-04-20-agent-discovery.md new file mode 100644 index 00000000..be05e8c0 --- /dev/null +++ b/superpowers/plans/2026-04-20-agent-discovery.md @@ -0,0 +1,404 @@ +# Agent Discovery Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Add RFC 8288 `Link` response headers, markdown content negotiation, and `Content-Signal` directives to the Plane developer docs to improve agent/AI-crawler discovery. + +**Architecture:** Three orthogonal changes on a VitePress-on-Vercel static site: (1) a single line added to `robots.txt`, (2) a `headers` block in `vercel.json`, (3) a build-time hook that copies source `.md` into `dist/` plus a Vercel `rewrites` block that routes `Accept: text/markdown` requests to the `.md` counterpart. No runtime logic; all deployment config. + +**Tech Stack:** VitePress 1.6, Vercel static hosting, Node 20 `fs`/`path` APIs (already available via VitePress). + +**Spec:** `superpowers/specs/2026-04-20-agent-discovery-design.md` + +**Branch:** `feat/agent-discovery` off `master`. + +**TDD note:** This repo has no JS test runner — tests for this work are *shell-level verifications*. Each task uses a "verify-first" pattern: run a check that fails, make the change, run the check again and verify it passes. + +--- + +## File Structure + +| File | Action | Responsibility | +| ---------------------------------------- | -------- | ----------------------------------------------------------------- | +| `docs/public/robots.txt` | Modify | Declare Content Signal policy | +| `vercel.json` | Modify | Apply `Link` headers and `Accept`-conditional `rewrites` | +| `docs/.vitepress/config.mts` | Modify | `buildEnd` hook that copies `.md` sources into `dist/` | +| `superpowers/` | Delete | Cleanup (last task) — spec and plan are ephemeral | + +--- + +### Task 1: Create feature branch + +**Files:** (none) + +- [ ] **Step 1: Verify clean tree on `master`** + +Run: `git status` +Expected: `nothing to commit, working tree clean` on branch `master`. + +- [ ] **Step 2: Create and switch to feature branch** + +Run: `git checkout -b feat/agent-discovery` +Expected: `Switched to a new branch 'feat/agent-discovery'` + +--- + +### Task 2: Content Signals in `robots.txt` + +**Files:** +- Modify: `docs/public/robots.txt` + +- [ ] **Step 1: Verify directive absent (baseline)** + +Run: `grep "Content-Signal" docs/public/robots.txt; echo "exit=$?"` +Expected: no match, `exit=1` + +- [ ] **Step 2: Add the directive** + +Edit `docs/public/robots.txt`. Find the block: + +``` +# Allow all search engines to crawl all content +User-agent: * +Allow: / + +# Disallow crawling of search results (if any) +Disallow: /search +``` + +Insert a new block immediately after `Disallow: /search` and before the next comment: + +``` +# Content Signals — AI content usage preferences +# https://contentsignals.org/ +Content-Signal: search=yes, ai-train=yes, ai-input=yes +``` + +- [ ] **Step 3: Verify directive present** + +Run: `grep "Content-Signal: search=yes, ai-train=yes, ai-input=yes" docs/public/robots.txt` +Expected: one matching line printed. + +- [ ] **Step 4: Commit** + +```bash +git add docs/public/robots.txt +git commit -m "feat(robots): declare Content-Signal AI usage preferences" +``` + +--- + +### Task 3: Link response headers in `vercel.json` + +**Files:** +- Modify: `vercel.json` + +- [ ] **Step 1: Verify no `headers` key today (baseline)** + +Run: `grep -c '"headers"' vercel.json; echo "exit=$?"` +Expected: `0`, `exit=1` + +- [ ] **Step 2: Add `headers` block** + +Edit `vercel.json`. It currently has this shape: + +```json +{ + "cleanUrls": true, + "redirects": [ ... ] +} +``` + +Add a new top-level `"headers"` key directly after `"cleanUrls": true,` (before `"redirects"`): + +```json + "headers": [ + { + "source": "/(.*)", + "headers": [ + { + "key": "Link", + "value": "; rel=\"describedby\"; type=\"text/plain\", ; rel=\"service-doc\"; type=\"text/html\", ; rel=\"sitemap\"; type=\"application/xml\"" + } + ] + } + ], +``` + +The `Link` value is a **single string** with three link-value entries separated by `", "` (comma-space). Escaped quotes are required per JSON. + +- [ ] **Step 3: Verify JSON is still valid** + +Run: `node -e "JSON.parse(require('fs').readFileSync('vercel.json','utf8'))" && echo OK` +Expected: `OK` + +- [ ] **Step 4: Verify shape** + +Run: `node -e "const j=JSON.parse(require('fs').readFileSync('vercel.json','utf8')); console.log(j.headers[0].headers[0].value)"` +Expected: the exact `Link` header value, one line starting with `; rel="describedby"...`. + +- [ ] **Step 5: Commit** + +```bash +git add vercel.json +git commit -m "feat(vercel): add Link response headers for agent discovery" +``` + +--- + +### Task 4: `buildEnd` hook to copy `.md` sources into `dist/` + +**Files:** +- Modify: `docs/.vitepress/config.mts` + +**Context on the target file:** `docs/.vitepress/config.mts` is 972 lines. The root shape is `export default withMermaid(defineConfig({ ... }))`. We add one new top-level property to the config object. + +- [ ] **Step 1: Verify no `.md` files reach `dist/` today (baseline)** + +Run: `pnpm build 2>&1 | tail -5 && find docs/.vitepress/dist -name '*.md' | head -5` +Expected: build succeeds; the `find` prints **nothing** (no `.md` files in `dist`). + +- [ ] **Step 2: Add Node imports to top of `config.mts`** + +Edit `docs/.vitepress/config.mts`. Find the existing import block at the top: + +```ts +import { defineConfig, type HeadConfig } from "vitepress"; +import { tabsMarkdownPlugin } from "vitepress-plugin-tabs"; +import { withMermaid } from "vitepress-plugin-mermaid"; +import { readFileSync } from "node:fs"; +import { resolve } from "node:path"; +``` + +Change the `node:fs` import line to add the functions we need, and add `node:path` helpers: + +```ts +import { readFileSync, readdirSync, statSync, mkdirSync, copyFileSync } from "node:fs"; +import { resolve, join, relative, dirname } from "node:path"; +``` + +- [ ] **Step 3: Add the `buildEnd` hook inside the config object** + +Still editing `docs/.vitepress/config.mts`. Find `vite:` block near lines 61-77: + +```ts + vite: { + optimizeDeps: { + include: [ + "lucide-vue-next", + ... + ], + }, + }, + title: "Plane developer documentation", +``` + +Insert a new `buildEnd` function between `vite: { ... },` and `title: "..."`: + +```ts + buildEnd(siteConfig) { + // Copy source .md files into dist/ for Accept: text/markdown negotiation. + const srcDir = siteConfig.srcDir; + const outDir = siteConfig.outDir; + + function walk(dir: string): void { + for (const entry of readdirSync(dir)) { + if (entry === ".vitepress" || entry === "public" || entry === "node_modules") continue; + const abs = join(dir, entry); + const stat = statSync(abs); + if (stat.isDirectory()) { + walk(abs); + } else if (stat.isFile() && abs.endsWith(".md")) { + const rel = relative(srcDir, abs); + const dest = join(outDir, rel); + mkdirSync(dirname(dest), { recursive: true }); + copyFileSync(abs, dest); + } + } + } + + walk(srcDir); + }, +``` + +**Why the skip list:** `.vitepress/` contains theme and config; `public/` files are already copied verbatim to `dist/` root by VitePress (so `llms.txt`, etc., are already there); `node_modules/` is a safety skip in case srcDir ever resolves to the repo root. + +- [ ] **Step 4: Run build and verify `.md` files appear in `dist/`** + +Run: `pnpm build 2>&1 | tail -5` +Expected: build succeeds, no errors. + +Run: `find docs/.vitepress/dist -name '*.md' | wc -l` +Expected: a number `> 100` (Plane docs has 180+ endpoint pages plus self-hosting/dev-tools). + +- [ ] **Step 5: Spot-check a specific page copied correctly** + +Run: `test -f docs/.vitepress/dist/api-reference/introduction.md && head -5 docs/.vitepress/dist/api-reference/introduction.md` +Expected: file exists; first lines show frontmatter (e.g., `---`, `title:`). + +Run: `diff docs/api-reference/introduction.md docs/.vitepress/dist/api-reference/introduction.md && echo "byte-identical"` +Expected: `byte-identical` + +- [ ] **Step 6: Commit** + +```bash +git add docs/.vitepress/config.mts +git commit -m "feat(build): copy source .md files to dist for agent markdown negotiation" +``` + +--- + +### Task 5: Vercel rewrite for `Accept: text/markdown` + +**Files:** +- Modify: `vercel.json` + +- [ ] **Step 1: Verify no `rewrites` key today (baseline)** + +Run: `node -e "const j=JSON.parse(require('fs').readFileSync('vercel.json','utf8')); console.log('rewrites' in j ? 'PRESENT' : 'ABSENT')"` +Expected: `ABSENT` + +- [ ] **Step 2: Add `rewrites` block** + +Edit `vercel.json`. Add a new top-level `"rewrites"` key directly after `"redirects": [ ... ]` (at the end of the object, before the closing `}`). + +The shape: + +```json + "rewrites": [ + { + "source": "/:path*", + "has": [ + { "type": "header", "key": "accept", "value": ".*text/markdown.*" } + ], + "destination": "/:path*.md" + } + ] +``` + +Make sure the `"redirects"` array ends with a `,` before `"rewrites"`. + +- [ ] **Step 3: Verify JSON validity** + +Run: `node -e "JSON.parse(require('fs').readFileSync('vercel.json','utf8'))" && echo OK` +Expected: `OK` + +- [ ] **Step 4: Verify final shape of `vercel.json`** + +Run: `node -e "const j=JSON.parse(require('fs').readFileSync('vercel.json','utf8')); console.log(JSON.stringify({cleanUrls:j.cleanUrls, headers:!!j.headers, redirects:j.redirects.length, rewrites:j.rewrites.length}))"` +Expected: `{"cleanUrls":true,"headers":true,"redirects":24,"rewrites":1}` (redirect count may vary — the key point is all four keys present). + +- [ ] **Step 5: Commit** + +```bash +git add vercel.json +git commit -m "feat(vercel): rewrite to .md when Accept: text/markdown" +``` + +--- + +### Task 6: Format check + full build verification + +**Files:** (none) + +- [ ] **Step 1: Run Prettier check** + +Run: `pnpm check:format` +Expected: exits 0 with "All matched files use Prettier code style!" (or similar success message). + +If it fails, run `pnpm fix:format` and re-run `pnpm check:format`. Then stage and commit any Prettier fixes as a separate commit: `style: format with prettier`. + +- [ ] **Step 2: Full production build** + +Run: `pnpm build` +Expected: build succeeds. Look for "build complete" / no error output in the tail. + +- [ ] **Step 3: Confirm all three artifacts in `dist/`** + +Run: +```bash +grep "Content-Signal" docs/.vitepress/dist/robots.txt && \ +test -f docs/.vitepress/dist/api-reference/introduction.md && \ +test -f docs/.vitepress/dist/api-reference/introduction.html && \ +echo "ALL PRESENT" +``` +Expected: `ALL PRESENT` + +*Note on `Link` header: headers are a Vercel runtime behavior and cannot be validated locally — they require a preview deploy. The implementer should confirm on Vercel preview before merge, not here.* + +--- + +### Task 7: Remove the ephemeral `superpowers/` folder + +**Files:** +- Delete: `superpowers/` (entire directory — spec and plan) + +- [ ] **Step 1: Verify folder exists** + +Run: `ls superpowers/ && find superpowers -type f` +Expected: prints `specs/` and `plans/` subdirs plus two `.md` files. + +- [ ] **Step 2: Remove the folder** + +Run: `git rm -r superpowers/` +Expected: two files deleted from index. + +- [ ] **Step 3: Verify removed** + +Run: `test ! -d superpowers && echo "GONE"` +Expected: `GONE` + +- [ ] **Step 4: Commit** + +```bash +git commit -m "chore: remove ephemeral agent-discovery spec and plan" +``` + +--- + +### Task 8: Push branch and open PR + +**Files:** (none) + +- [ ] **Step 1: Push branch** + +Run: `git push -u origin feat/agent-discovery` +Expected: branch published, prints PR creation URL. + +- [ ] **Step 2: Open PR against `preview`** + +Per `CLAUDE.md`, PRs target `preview`, not `master`. + +```bash +gh pr create --base preview --title "feat: agent discovery (Link headers, markdown negotiation, Content-Signal)" --body "$(cat <<'EOF' +## Summary + +- Add `Content-Signal: search=yes, ai-train=yes, ai-input=yes` to `robots.txt` +- Add `Link` response headers on all paths advertising `llms.txt` (describedby), `/api-reference/introduction` (service-doc), and `/sitemap.xml` (sitemap) +- Serve source markdown when `Accept: text/markdown` is sent — static `.md` files copied into `dist` by a VitePress `buildEnd` hook, routed via a Vercel `rewrites` header matcher + +## Test plan + +Local: +- [ ] `pnpm check:format` passes +- [ ] `pnpm build` succeeds and produces `.md` files alongside `.html` in `dist/` +- [ ] `docs/.vitepress/dist/robots.txt` contains the `Content-Signal` line + +Preview deploy: +- [ ] `curl -I https://.vercel.app/` shows a `Link:` header with all three relations +- [ ] `curl -H "Accept: text/markdown" https://.vercel.app/api-reference/introduction` returns markdown body and `Content-Type: text/markdown; charset=utf-8` (if plain text, add explicit header override — see spec contingency) +- [ ] Default `curl https://.vercel.app/` still returns HTML (regression check) +EOF +)" +``` + +Expected: PR URL printed. + +--- + +## Post-Merge (out of scope for this plan) + +After merge to `preview` and promotion to production: + +1. Run the external audit (`isitagentready.com` or whichever tool flagged the original issues) against `https://developers.plane.so` to confirm all three findings are resolved. +2. If `Content-Type: text/markdown` is not returned (contingency in spec §Fix 3), add an explicit header override in a follow-up PR. From 9060762da94de6c9cf14fa7befb691f4efd559b8 Mon Sep 17 00:00:00 2001 From: sriramveeraghanta Date: Mon, 20 Apr 2026 21:00:39 +0530 Subject: [PATCH 3/8] feat(robots): declare Content-Signal AI usage preferences --- docs/public/robots.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/public/robots.txt b/docs/public/robots.txt index 22032e68..6d7bcdd4 100644 --- a/docs/public/robots.txt +++ b/docs/public/robots.txt @@ -8,6 +8,10 @@ Allow: / # Disallow crawling of search results (if any) Disallow: /search +# Content Signals — AI content usage preferences +# https://contentsignals.org/ +Content-Signal: search=yes, ai-train=yes, ai-input=yes + # Disallow crawling of any internal/private paths (add as needed) # Disallow: /private/ # Disallow: /admin/ From 9df7666d3867a894fa71db726a84b292b55d53ca Mon Sep 17 00:00:00 2001 From: sriramveeraghanta Date: Mon, 20 Apr 2026 21:02:07 +0530 Subject: [PATCH 4/8] feat(vercel): add Link response headers for agent discovery --- vercel.json | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/vercel.json b/vercel.json index 0cb878f4..5596fc1c 100644 --- a/vercel.json +++ b/vercel.json @@ -1,5 +1,16 @@ { "cleanUrls": true, + "headers": [ + { + "source": "/(.*)", + "headers": [ + { + "key": "Link", + "value": "; rel=\"describedby\"; type=\"text/plain\", ; rel=\"service-doc\"; type=\"text/html\", ; rel=\"sitemap\"; type=\"application/xml\"" + } + ] + } + ], "redirects": [ { "source": "/api-reference", From ee5d04e0af420d0a4c163767fb3431947998ebdf Mon Sep 17 00:00:00 2001 From: sriramveeraghanta Date: Mon, 20 Apr 2026 21:03:30 +0530 Subject: [PATCH 5/8] feat(build): copy source .md files to dist for agent markdown negotiation --- docs/.vitepress/config.mts | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 67b68194..b5652b70 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -1,8 +1,8 @@ import { defineConfig, type HeadConfig } from "vitepress"; import { tabsMarkdownPlugin } from "vitepress-plugin-tabs"; import { withMermaid } from "vitepress-plugin-mermaid"; -import { readFileSync } from "node:fs"; -import { resolve } from "node:path"; +import { readFileSync, readdirSync, statSync, mkdirSync, copyFileSync } from "node:fs"; +import { resolve, join, relative, dirname } from "node:path"; function loadEnvVar(key: string): string | undefined { // process.env takes precedence (CI/hosting platforms set vars here) @@ -75,6 +75,29 @@ export default withMermaid( ], }, }, + buildEnd(siteConfig) { + // Copy source .md files into dist/ for Accept: text/markdown negotiation. + const srcDir = siteConfig.srcDir; + const outDir = siteConfig.outDir; + + function walk(dir: string): void { + for (const entry of readdirSync(dir)) { + if (entry === ".vitepress" || entry === "public" || entry === "node_modules") continue; + const abs = join(dir, entry); + const stat = statSync(abs); + if (stat.isDirectory()) { + walk(abs); + } else if (stat.isFile() && abs.endsWith(".md")) { + const rel = relative(srcDir, abs); + const dest = join(outDir, rel); + mkdirSync(dirname(dest), { recursive: true }); + copyFileSync(abs, dest); + } + } + } + + walk(srcDir); + }, title: "Plane developer documentation", description: "Self-host Plane, integrate with our API, configure webhooks, and extend your project management platform. Complete guides for developers building on Plane.", From 96663093d54dbcc707e1df1afe2d2173ab7639fa Mon Sep 17 00:00:00 2001 From: sriramveeraghanta Date: Mon, 20 Apr 2026 21:03:54 +0530 Subject: [PATCH 6/8] feat(vercel): rewrite to .md when Accept: text/markdown --- vercel.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/vercel.json b/vercel.json index 5596fc1c..e0d10319 100644 --- a/vercel.json +++ b/vercel.json @@ -104,5 +104,14 @@ "source": "/self-hosting", "destination": "/self-hosting/overview" } + ], + "rewrites": [ + { + "source": "/:path*", + "has": [ + { "type": "header", "key": "accept", "value": ".*text/markdown.*" } + ], + "destination": "/:path*.md" + } ] } From c4158a3c7c5faa5938ff95d54056846fd1c740d0 Mon Sep 17 00:00:00 2001 From: sriramveeraghanta Date: Mon, 20 Apr 2026 21:05:30 +0530 Subject: [PATCH 7/8] style: format with prettier --- vercel.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/vercel.json b/vercel.json index e0d10319..bad473fb 100644 --- a/vercel.json +++ b/vercel.json @@ -108,9 +108,7 @@ "rewrites": [ { "source": "/:path*", - "has": [ - { "type": "header", "key": "accept", "value": ".*text/markdown.*" } - ], + "has": [{ "type": "header", "key": "accept", "value": ".*text/markdown.*" }], "destination": "/:path*.md" } ] From 2c5fbf733c57d2578346f5af9028b8dc46b9af96 Mon Sep 17 00:00:00 2001 From: sriramveeraghanta Date: Mon, 20 Apr 2026 21:05:59 +0530 Subject: [PATCH 8/8] chore: remove ephemeral agent-discovery spec and plan --- .../plans/2026-04-20-agent-discovery.md | 404 ------------------ .../2026-04-20-agent-discovery-design.md | 137 ------ 2 files changed, 541 deletions(-) delete mode 100644 superpowers/plans/2026-04-20-agent-discovery.md delete mode 100644 superpowers/specs/2026-04-20-agent-discovery-design.md diff --git a/superpowers/plans/2026-04-20-agent-discovery.md b/superpowers/plans/2026-04-20-agent-discovery.md deleted file mode 100644 index be05e8c0..00000000 --- a/superpowers/plans/2026-04-20-agent-discovery.md +++ /dev/null @@ -1,404 +0,0 @@ -# Agent Discovery Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** Add RFC 8288 `Link` response headers, markdown content negotiation, and `Content-Signal` directives to the Plane developer docs to improve agent/AI-crawler discovery. - -**Architecture:** Three orthogonal changes on a VitePress-on-Vercel static site: (1) a single line added to `robots.txt`, (2) a `headers` block in `vercel.json`, (3) a build-time hook that copies source `.md` into `dist/` plus a Vercel `rewrites` block that routes `Accept: text/markdown` requests to the `.md` counterpart. No runtime logic; all deployment config. - -**Tech Stack:** VitePress 1.6, Vercel static hosting, Node 20 `fs`/`path` APIs (already available via VitePress). - -**Spec:** `superpowers/specs/2026-04-20-agent-discovery-design.md` - -**Branch:** `feat/agent-discovery` off `master`. - -**TDD note:** This repo has no JS test runner — tests for this work are *shell-level verifications*. Each task uses a "verify-first" pattern: run a check that fails, make the change, run the check again and verify it passes. - ---- - -## File Structure - -| File | Action | Responsibility | -| ---------------------------------------- | -------- | ----------------------------------------------------------------- | -| `docs/public/robots.txt` | Modify | Declare Content Signal policy | -| `vercel.json` | Modify | Apply `Link` headers and `Accept`-conditional `rewrites` | -| `docs/.vitepress/config.mts` | Modify | `buildEnd` hook that copies `.md` sources into `dist/` | -| `superpowers/` | Delete | Cleanup (last task) — spec and plan are ephemeral | - ---- - -### Task 1: Create feature branch - -**Files:** (none) - -- [ ] **Step 1: Verify clean tree on `master`** - -Run: `git status` -Expected: `nothing to commit, working tree clean` on branch `master`. - -- [ ] **Step 2: Create and switch to feature branch** - -Run: `git checkout -b feat/agent-discovery` -Expected: `Switched to a new branch 'feat/agent-discovery'` - ---- - -### Task 2: Content Signals in `robots.txt` - -**Files:** -- Modify: `docs/public/robots.txt` - -- [ ] **Step 1: Verify directive absent (baseline)** - -Run: `grep "Content-Signal" docs/public/robots.txt; echo "exit=$?"` -Expected: no match, `exit=1` - -- [ ] **Step 2: Add the directive** - -Edit `docs/public/robots.txt`. Find the block: - -``` -# Allow all search engines to crawl all content -User-agent: * -Allow: / - -# Disallow crawling of search results (if any) -Disallow: /search -``` - -Insert a new block immediately after `Disallow: /search` and before the next comment: - -``` -# Content Signals — AI content usage preferences -# https://contentsignals.org/ -Content-Signal: search=yes, ai-train=yes, ai-input=yes -``` - -- [ ] **Step 3: Verify directive present** - -Run: `grep "Content-Signal: search=yes, ai-train=yes, ai-input=yes" docs/public/robots.txt` -Expected: one matching line printed. - -- [ ] **Step 4: Commit** - -```bash -git add docs/public/robots.txt -git commit -m "feat(robots): declare Content-Signal AI usage preferences" -``` - ---- - -### Task 3: Link response headers in `vercel.json` - -**Files:** -- Modify: `vercel.json` - -- [ ] **Step 1: Verify no `headers` key today (baseline)** - -Run: `grep -c '"headers"' vercel.json; echo "exit=$?"` -Expected: `0`, `exit=1` - -- [ ] **Step 2: Add `headers` block** - -Edit `vercel.json`. It currently has this shape: - -```json -{ - "cleanUrls": true, - "redirects": [ ... ] -} -``` - -Add a new top-level `"headers"` key directly after `"cleanUrls": true,` (before `"redirects"`): - -```json - "headers": [ - { - "source": "/(.*)", - "headers": [ - { - "key": "Link", - "value": "; rel=\"describedby\"; type=\"text/plain\", ; rel=\"service-doc\"; type=\"text/html\", ; rel=\"sitemap\"; type=\"application/xml\"" - } - ] - } - ], -``` - -The `Link` value is a **single string** with three link-value entries separated by `", "` (comma-space). Escaped quotes are required per JSON. - -- [ ] **Step 3: Verify JSON is still valid** - -Run: `node -e "JSON.parse(require('fs').readFileSync('vercel.json','utf8'))" && echo OK` -Expected: `OK` - -- [ ] **Step 4: Verify shape** - -Run: `node -e "const j=JSON.parse(require('fs').readFileSync('vercel.json','utf8')); console.log(j.headers[0].headers[0].value)"` -Expected: the exact `Link` header value, one line starting with `; rel="describedby"...`. - -- [ ] **Step 5: Commit** - -```bash -git add vercel.json -git commit -m "feat(vercel): add Link response headers for agent discovery" -``` - ---- - -### Task 4: `buildEnd` hook to copy `.md` sources into `dist/` - -**Files:** -- Modify: `docs/.vitepress/config.mts` - -**Context on the target file:** `docs/.vitepress/config.mts` is 972 lines. The root shape is `export default withMermaid(defineConfig({ ... }))`. We add one new top-level property to the config object. - -- [ ] **Step 1: Verify no `.md` files reach `dist/` today (baseline)** - -Run: `pnpm build 2>&1 | tail -5 && find docs/.vitepress/dist -name '*.md' | head -5` -Expected: build succeeds; the `find` prints **nothing** (no `.md` files in `dist`). - -- [ ] **Step 2: Add Node imports to top of `config.mts`** - -Edit `docs/.vitepress/config.mts`. Find the existing import block at the top: - -```ts -import { defineConfig, type HeadConfig } from "vitepress"; -import { tabsMarkdownPlugin } from "vitepress-plugin-tabs"; -import { withMermaid } from "vitepress-plugin-mermaid"; -import { readFileSync } from "node:fs"; -import { resolve } from "node:path"; -``` - -Change the `node:fs` import line to add the functions we need, and add `node:path` helpers: - -```ts -import { readFileSync, readdirSync, statSync, mkdirSync, copyFileSync } from "node:fs"; -import { resolve, join, relative, dirname } from "node:path"; -``` - -- [ ] **Step 3: Add the `buildEnd` hook inside the config object** - -Still editing `docs/.vitepress/config.mts`. Find `vite:` block near lines 61-77: - -```ts - vite: { - optimizeDeps: { - include: [ - "lucide-vue-next", - ... - ], - }, - }, - title: "Plane developer documentation", -``` - -Insert a new `buildEnd` function between `vite: { ... },` and `title: "..."`: - -```ts - buildEnd(siteConfig) { - // Copy source .md files into dist/ for Accept: text/markdown negotiation. - const srcDir = siteConfig.srcDir; - const outDir = siteConfig.outDir; - - function walk(dir: string): void { - for (const entry of readdirSync(dir)) { - if (entry === ".vitepress" || entry === "public" || entry === "node_modules") continue; - const abs = join(dir, entry); - const stat = statSync(abs); - if (stat.isDirectory()) { - walk(abs); - } else if (stat.isFile() && abs.endsWith(".md")) { - const rel = relative(srcDir, abs); - const dest = join(outDir, rel); - mkdirSync(dirname(dest), { recursive: true }); - copyFileSync(abs, dest); - } - } - } - - walk(srcDir); - }, -``` - -**Why the skip list:** `.vitepress/` contains theme and config; `public/` files are already copied verbatim to `dist/` root by VitePress (so `llms.txt`, etc., are already there); `node_modules/` is a safety skip in case srcDir ever resolves to the repo root. - -- [ ] **Step 4: Run build and verify `.md` files appear in `dist/`** - -Run: `pnpm build 2>&1 | tail -5` -Expected: build succeeds, no errors. - -Run: `find docs/.vitepress/dist -name '*.md' | wc -l` -Expected: a number `> 100` (Plane docs has 180+ endpoint pages plus self-hosting/dev-tools). - -- [ ] **Step 5: Spot-check a specific page copied correctly** - -Run: `test -f docs/.vitepress/dist/api-reference/introduction.md && head -5 docs/.vitepress/dist/api-reference/introduction.md` -Expected: file exists; first lines show frontmatter (e.g., `---`, `title:`). - -Run: `diff docs/api-reference/introduction.md docs/.vitepress/dist/api-reference/introduction.md && echo "byte-identical"` -Expected: `byte-identical` - -- [ ] **Step 6: Commit** - -```bash -git add docs/.vitepress/config.mts -git commit -m "feat(build): copy source .md files to dist for agent markdown negotiation" -``` - ---- - -### Task 5: Vercel rewrite for `Accept: text/markdown` - -**Files:** -- Modify: `vercel.json` - -- [ ] **Step 1: Verify no `rewrites` key today (baseline)** - -Run: `node -e "const j=JSON.parse(require('fs').readFileSync('vercel.json','utf8')); console.log('rewrites' in j ? 'PRESENT' : 'ABSENT')"` -Expected: `ABSENT` - -- [ ] **Step 2: Add `rewrites` block** - -Edit `vercel.json`. Add a new top-level `"rewrites"` key directly after `"redirects": [ ... ]` (at the end of the object, before the closing `}`). - -The shape: - -```json - "rewrites": [ - { - "source": "/:path*", - "has": [ - { "type": "header", "key": "accept", "value": ".*text/markdown.*" } - ], - "destination": "/:path*.md" - } - ] -``` - -Make sure the `"redirects"` array ends with a `,` before `"rewrites"`. - -- [ ] **Step 3: Verify JSON validity** - -Run: `node -e "JSON.parse(require('fs').readFileSync('vercel.json','utf8'))" && echo OK` -Expected: `OK` - -- [ ] **Step 4: Verify final shape of `vercel.json`** - -Run: `node -e "const j=JSON.parse(require('fs').readFileSync('vercel.json','utf8')); console.log(JSON.stringify({cleanUrls:j.cleanUrls, headers:!!j.headers, redirects:j.redirects.length, rewrites:j.rewrites.length}))"` -Expected: `{"cleanUrls":true,"headers":true,"redirects":24,"rewrites":1}` (redirect count may vary — the key point is all four keys present). - -- [ ] **Step 5: Commit** - -```bash -git add vercel.json -git commit -m "feat(vercel): rewrite to .md when Accept: text/markdown" -``` - ---- - -### Task 6: Format check + full build verification - -**Files:** (none) - -- [ ] **Step 1: Run Prettier check** - -Run: `pnpm check:format` -Expected: exits 0 with "All matched files use Prettier code style!" (or similar success message). - -If it fails, run `pnpm fix:format` and re-run `pnpm check:format`. Then stage and commit any Prettier fixes as a separate commit: `style: format with prettier`. - -- [ ] **Step 2: Full production build** - -Run: `pnpm build` -Expected: build succeeds. Look for "build complete" / no error output in the tail. - -- [ ] **Step 3: Confirm all three artifacts in `dist/`** - -Run: -```bash -grep "Content-Signal" docs/.vitepress/dist/robots.txt && \ -test -f docs/.vitepress/dist/api-reference/introduction.md && \ -test -f docs/.vitepress/dist/api-reference/introduction.html && \ -echo "ALL PRESENT" -``` -Expected: `ALL PRESENT` - -*Note on `Link` header: headers are a Vercel runtime behavior and cannot be validated locally — they require a preview deploy. The implementer should confirm on Vercel preview before merge, not here.* - ---- - -### Task 7: Remove the ephemeral `superpowers/` folder - -**Files:** -- Delete: `superpowers/` (entire directory — spec and plan) - -- [ ] **Step 1: Verify folder exists** - -Run: `ls superpowers/ && find superpowers -type f` -Expected: prints `specs/` and `plans/` subdirs plus two `.md` files. - -- [ ] **Step 2: Remove the folder** - -Run: `git rm -r superpowers/` -Expected: two files deleted from index. - -- [ ] **Step 3: Verify removed** - -Run: `test ! -d superpowers && echo "GONE"` -Expected: `GONE` - -- [ ] **Step 4: Commit** - -```bash -git commit -m "chore: remove ephemeral agent-discovery spec and plan" -``` - ---- - -### Task 8: Push branch and open PR - -**Files:** (none) - -- [ ] **Step 1: Push branch** - -Run: `git push -u origin feat/agent-discovery` -Expected: branch published, prints PR creation URL. - -- [ ] **Step 2: Open PR against `preview`** - -Per `CLAUDE.md`, PRs target `preview`, not `master`. - -```bash -gh pr create --base preview --title "feat: agent discovery (Link headers, markdown negotiation, Content-Signal)" --body "$(cat <<'EOF' -## Summary - -- Add `Content-Signal: search=yes, ai-train=yes, ai-input=yes` to `robots.txt` -- Add `Link` response headers on all paths advertising `llms.txt` (describedby), `/api-reference/introduction` (service-doc), and `/sitemap.xml` (sitemap) -- Serve source markdown when `Accept: text/markdown` is sent — static `.md` files copied into `dist` by a VitePress `buildEnd` hook, routed via a Vercel `rewrites` header matcher - -## Test plan - -Local: -- [ ] `pnpm check:format` passes -- [ ] `pnpm build` succeeds and produces `.md` files alongside `.html` in `dist/` -- [ ] `docs/.vitepress/dist/robots.txt` contains the `Content-Signal` line - -Preview deploy: -- [ ] `curl -I https://.vercel.app/` shows a `Link:` header with all three relations -- [ ] `curl -H "Accept: text/markdown" https://.vercel.app/api-reference/introduction` returns markdown body and `Content-Type: text/markdown; charset=utf-8` (if plain text, add explicit header override — see spec contingency) -- [ ] Default `curl https://.vercel.app/` still returns HTML (regression check) -EOF -)" -``` - -Expected: PR URL printed. - ---- - -## Post-Merge (out of scope for this plan) - -After merge to `preview` and promotion to production: - -1. Run the external audit (`isitagentready.com` or whichever tool flagged the original issues) against `https://developers.plane.so` to confirm all three findings are resolved. -2. If `Content-Type: text/markdown` is not returned (contingency in spec §Fix 3), add an explicit header override in a follow-up PR. diff --git a/superpowers/specs/2026-04-20-agent-discovery-design.md b/superpowers/specs/2026-04-20-agent-discovery-design.md deleted file mode 100644 index 16be9f0d..00000000 --- a/superpowers/specs/2026-04-20-agent-discovery-design.md +++ /dev/null @@ -1,137 +0,0 @@ -# Agent Discovery Improvements — Design - -**Date:** 2026-04-20 -**Status:** Draft — pending user review -**Site:** developers.plane.so (VitePress static site on Vercel) - -## Goal - -Improve agent/AI-crawler discovery on the Plane developer docs by addressing three gaps flagged by an external audit: - -1. No `Link` response headers advertising useful resources (RFC 8288). -2. No `Content-Type: text/markdown` negotiation for agents that prefer markdown. -3. No `Content-Signal` directives in `robots.txt` declaring AI usage preferences. - -## Non-Goals - -- Creating an OpenAPI spec or `/.well-known/api-catalog` (none exists today). -- Flattening or cleaning VitePress custom components (``, ``, etc.) in the markdown output. Agents receive source markdown with those tags present. -- Changes to sidebar, navigation, or published content. -- Edge-runtime logic. The design stays fully static. - -## Current State - -- VitePress static site with source in `docs/`, build output in `docs/.vitepress/dist/`. -- Deployed on Vercel; config in `vercel.json` (already has `cleanUrls: true` and a `redirects` block). -- `docs/public/robots.txt` exists and declares a sitemap and a (non-standard) `LLMs-txt:` line. -- `docs/public/llms.txt` and `docs/public/llms-full.txt` already exist. -- No `/.well-known/` directory. No OpenAPI file. - -## Design - -### Fix 1 — Content Signals in `robots.txt` - -Append a single directive to `docs/public/robots.txt`: - -``` -Content-Signal: search=yes, ai-train=yes, ai-input=yes -``` - -**Policy rationale:** Plane's existing posture (publishing `llms.txt` and `llms-full.txt`) is maximally open. The Content Signal matches that — search indexing, model training, and AI-answer grounding are all permitted. - -Placement: directly below the `User-agent: *` / `Allow:` block, before the `Sitemap:` line. One line, no other changes. - -### Fix 2 — `Link` response headers (RFC 8288) - -Add a `headers` block to `vercel.json` applied to all paths. A single `Link` header carries three relations, comma-separated per RFC 8288 §3.5: - -``` -Link: ; rel="describedby"; type="text/plain", - ; rel="service-doc"; type="text/html", - ; rel="sitemap"; type="application/xml" -``` - -**Why each relation:** -- `describedby` → `/llms.txt` — points agents to the LLM-friendly site map (canonical discovery). -- `service-doc` → `/api-reference/introduction` — registered IANA relation for human-readable API docs. -- `sitemap` → `/sitemap.xml` — widely supported, de facto standard. - -Applied to `source: "/(.*)"` so the headers land on every page, not just the homepage. This lets any deep-link entry point still expose discovery metadata. - -### Fix 3 — Markdown content negotiation (static approach) - -Two pieces, both in files already owned by this repo: - -**3a. Build hook in `docs/.vitepress/config.mts`** - -Add a `buildEnd(siteConfig)` function that copies every source `.md` file from `siteConfig.srcDir` to the corresponding path under `siteConfig.outDir`, preserving directory structure. The hook: - -- Walks `srcDir` recursively. -- Skips anything under `.vitepress/` (config/theme/cache/dist). -- For each `.md` file, computes the relative path and writes it into `outDir` at the same relative location, creating intermediate directories as needed. -- Preserves file contents byte-for-byte. Frontmatter and VitePress custom components (``, ``, etc.) are included verbatim — acknowledged limitation. - -Result: after `pnpm build`, `dist/api-reference/introduction.md` exists alongside `dist/api-reference/introduction.html`. - -**3b. Vercel rewrite in `vercel.json`** - -Add a `rewrites` block that matches on the `Accept` header: - -```json -{ - "rewrites": [ - { - "source": "/:path*", - "has": [{ "type": "header", "key": "accept", "value": ".*text/markdown.*" }], - "destination": "/:path*.md" - } - ] -} -``` - -Vercel's default MIME mapping serves `.md` files as `text/markdown; charset=utf-8`. If preview-deployment testing shows a different `Content-Type` (e.g., `text/plain`), add an explicit header override to `vercel.json` for `source: "/(.*).md"` setting `Content-Type: text/markdown; charset=utf-8`. This is the only identified contingency. - -Interaction with existing `cleanUrls: true`: `cleanUrls` strips `.html` from URLs; the `.md` rewrite only fires when the `Accept` header matches, so browsers still get HTML via the default behavior. The two directives don't conflict — `rewrites` run before `cleanUrls` resolution. - -### Known Limitations (accepted) - -- **Raw frontmatter in markdown responses.** Agents will see the YAML frontmatter block at the top of each page. Most agent parsers tolerate this. -- **VitePress components in markdown responses.** Pages using ``, ``, ``, ``, `` will return those tags literally. Prose content is still machine-readable; component-rendered detail (e.g., parameter type badges) is not flattened. -- **No content-type validation at build time.** If a page has no source `.md` (unlikely in VitePress), the rewrite returns a 404; the HTML version remains available for normal requests. - -## File Changes (summary) - -| File | Change | -| ---------------------------------------- | ------------------------------------------------------------------ | -| `docs/public/robots.txt` | Add one `Content-Signal:` line | -| `vercel.json` | Add `headers` block (Fix 2) and `rewrites` block (Fix 3b) | -| `docs/.vitepress/config.mts` | Add `buildEnd` hook that copies `.md` files into `dist` (Fix 3a) | - -No new dependencies. No new directories. - -## Testing - -**Local (before merge):** - -- `pnpm build` — verify no errors, then check that `docs/.vitepress/dist/` contains `.md` copies (spot-check `dist/api-reference/introduction.md`). -- `pnpm check:format` — Prettier must pass. - -**On Vercel preview deployment:** - -- `curl -I https://.vercel.app/` — verify the `Link:` response header is present and contains all three relations. -- `curl -H "Accept: text/markdown" https://.vercel.app/api-reference/introduction` — verify the body is markdown and `Content-Type: text/markdown; charset=utf-8`. -- `curl https://.vercel.app/` (no custom Accept) — verify HTML is still returned (regression check). -- `curl https://.vercel.app/robots.txt | grep Content-Signal` — verify the directive is present. - -**Rationale for preview-only testing:** `pnpm preview` serves VitePress's built output directly and does not run Vercel's `rewrites` / `headers` engine. The header negotiation can only be validated on a Vercel deployment. - -## Rollout - -1. Implement all three fixes in a single PR branch (`feat/agent-discovery`). -2. Push, let Vercel preview build, run the `curl` checks above against the preview URL. -3. Merge to `master`; Vercel promotes to production. -4. Re-run `isitagentready.com` (or the source audit) against production to confirm the three issues are resolved. - -## Post-Implementation - -Once the implementation PR is merged and verified in production, **delete the `superpowers/` directory** from the repo — the spec is ephemeral and does not belong in the long-lived tree.