Skip to content

feat(site): add a curated /pricing.md markdown twin for agents - #4069

Draft
revmag wants to merge 1 commit into
mainfrom
feat/pricing-markdown-twin
Draft

feat(site): add a curated /pricing.md markdown twin for agents#4069
revmag wants to merge 1 commit into
mainfrom
feat/pricing-markdown-twin

Conversation

@revmag

@revmag revmag commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

Staging preview


📄 Summary

An agent-experience audit flagged /pricing.md as thin: it existed, but only because the generic HTML-to-markdown pipeline converts the pricing page. That page is highly interactive (retention sliders, tabs, a bill calculator), so the conversion emits UI fragments instead of a price list — slider ranges render as 0GB100TB, icon URLs repeat, the plan cards fragment into loose headings, and nothing states the rates for retention tiers other than the default. An agent can't reliably answer "what does 500 GB of logs at 90-day retention cost, and which plan do I need?" from it.

This PR serves a hand-authored markdown twin at /pricing.md instead.

File What it does
constants/pricing.ts Canonical rate data: per-GB logs/traces and per-million-sample metrics prices for every retention tier, retention minimums, and plan-level amounts. The pricing calculator (PricingCalculator/constants.ts) now re-exports from it, so the page and the markdown can't drift.
utils/pricingMarkdown.ts Renders the document: plans at a glance, per-signal rates, retention price tables generated from those constants, what the Teams minimum includes, a bill formula with worked examples, Enterprise deployment options, startup program, Community/licensing, a plan feature breakdown, and billing + cost-control pointers.
app/(site)/pricing.md/route.ts Serves it via the shared agentResponse headers, mirroring /skill.md.
proxy.ts, utils/agentMarkdownRouting.ts New "curated markdown twin" concept (below).
app/(site)/llms.txt/route.ts Lists it under a new ## Pricing section.

Curated markdown twins. A small map (CURATED_MARKDOWN_TWINS, currently just /pricing) marks pages whose HTML converts poorly and that ship a hand-authored twin. /pricing.md is excluded from the generic page pipeline, and Accept: text/markdown on /pricing rewrites to the twin instead of /api/page-markdown/pricing, so both entry points return the same document. Vary: Accept on /pricing is preserved.

Content provenance. Plan tiers, feature matrix, startup terms, and Enterprise offerings come from the live pricing page components. The retention→price tables, retention minimums, what counts as ingested volume vs. a metric sample, included-usage/overage mechanics, AWS Marketplace activation, trial/cancellation behavior, and dual-licensing are drawn from the Billing docs in #3642. Retention prices were cross-checked against PricingCalculator/constants.ts in this repo and match — which also resolves the "please confirm the retention pairing" reviewer note on #3642.


✅ Change Type

  • ✨ Feature (new agent-facing surface)

🧪 Testing Strategy

  • New tests/pricing-md.test.js (6 tests): response shape and Vary: Accept, all three plan tiers with their prices, every retention tier asserted against constants/pricing.ts (so a rate change that isn't reflected in the doc fails), what usage is billed on, the feature breakdown, and that every link is absolute.
  • Updated tests/agent-markdown-routing.test.js — new curated markdown twins bypass the generic page pipeline case; existing generic-pipeline assertions repointed from /pricing to /alerts-management.
  • Updated tests/proxy.test.js — new serves the curated markdown twin for /pricing case; generic-pipeline assertions repointed likewise.
  • Updated tests/llms-txt.test.js for the new section.
  • node --test on pricing-md / agent-markdown-routing / proxy: 61 pass, 0 fail.
  • yarn check:stale-urls: pass. yarn lint: 0 errors. npx tsc --noEmit: no errors in changed files. yarn build: succeeds with /pricing.md registered as a route.

Known pre-existing failure, not from this PR: tests/llms-txt.test.js → "llms.txt follows the llms.txt structure: H1 then summary blockquote" fails on clean main too (verified by stashing this branch's changes). The summary line lacks its > prefix. The fix is queued in a separate in-flight branch covering /api-reference.md and the MCP discovery surfaces, so it's left alone here to avoid a conflict.


⚠️ Risk & Impact Assessment

  • Blast radius: agent-facing markdown only. The rendered /pricing HTML page is unchanged — the calculator now imports the same numbers from constants/pricing.ts rather than declaring them locally.
  • Potential regressions: (1) the calculator's rate import — covered by the build and by rate assertions in the new test; (2) proxy routing precedence for /pricing — covered by the new proxy test, which also asserts it no longer reaches /api/page-markdown/pricing.
  • Content accuracy: prices are code-sourced; plan/feature copy is transcribed from the pricing page and should get a product read (see below).
  • Rollback plan: revert the PR. /pricing.md falls back to the generic pipeline output it serves today.

📝 Changelog

Field Value
Deployment Type Cloud / OSS
Change Type Feature (agent-facing surface)
Description /pricing.md now serves a structured, hand-authored pricing document — plan tiers, per-signal rates at every retention tier, worked bill examples, and a plan feature breakdown — instead of a conversion of the interactive pricing page.

📋 Checklist

  • Tests added
  • Manually verified the rendered markdown output
  • Breaking changes documented (none)
  • Backward compatibility considered (/pricing.md already resolved; the response body changes, the URL does not)

👀 Notes for Reviewers

Two things worth a product read before merge:

  1. The $199 reference. The page shows Teams as $199 struck through $49. The doc phrases this as "a $49/month minimum (listed on the pricing page as a discount from $199/month)". Say the word if that framing should be dropped entirely.
  2. The plan feature breakdown. To stay readable, all-plan features are grouped as a bulleted list by category and only the 23 plan-differentiated rows are a table. Please confirm the Coming soon / Beta / Enterprise self-managed markers, which were transcribed from ExploreAllFeatures.tsx.

Also flagging follow-up debt: the same rate tables are still duplicated in SigNozCloudPricingOverview.tsx, MetricsCostEstimation.tsx, and components/Monthly-estimate/*. That's pre-existing and out of scope here, but they should all fold into constants/pricing.ts.

🤖 Generated with Claude Code

/pricing.md was produced by the generic HTML-to-markdown pipeline, which
turns the pricing page's sliders, tabs, and calculator into UI fragments
("0GB100TB", repeated icon URLs) rather than a price list. Agents could
not reliably read plan tiers, rates, or limits from it.

Serve a hand-authored markdown twin instead:

- constants/pricing.ts holds the canonical rate data (per-GB logs/traces
  and per-million-sample metrics prices for every retention tier, the
  retention minimums, and plan-level amounts). The pricing calculator now
  re-exports from it, so the page and the markdown cannot drift.
- utils/pricingMarkdown.ts renders plans at a glance, per-signal rates,
  retention price tables generated from those constants, what the Teams
  minimum includes, a bill formula with worked examples, Enterprise
  options, the startup program, licensing, a plan feature breakdown, and
  billing/cost-control pointers.
- app/(site)/pricing.md/route.ts serves it with the shared agent headers.
- Routing gains a "curated markdown twin" concept: /pricing.md is a real
  route excluded from the page pipeline, and Accept: text/markdown on
  /pricing rewrites to it. Vary: Accept on /pricing is preserved.
- llms.txt lists it under a new Pricing section.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@revmag
revmag requested a review from a team as a code owner August 27, 2026 12:44
@vercel

vercel Bot commented Aug 27, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
signoz-web Ready Ready Preview Aug 27, 2026 12:47pm

Request Review

@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Claude finished @revmag's task in 2m 49s —— View job


PR Review — curated /pricing.md markdown twin

Reviewed with the signoz-website-frontend-pr-review skill against contributing/site-code.md. All changed files are frontend/site code (no data/docs/**), so only the code rubric applies.

This is a clean, well-tested change. The routing model is sound: CURATED_MARKDOWN_TWINS is checked before the generic page pipeline, /pricing.md is added to MARKDOWN_ROUTE_PATHS so it passes through to its own route, servesMarkdownAlternate was extended so /pricing keeps Vary: Accept, and the calculator now re-exports from constants/pricing.ts (verified the consumers — usePricingCalculator.ts, PricingCalculatorDesktop*, Mobile* — still import MIN_LOG_VALUE/MAX_VALUE/prices from ./constants, so nothing breaks). Precedence, self-fetch guard, and query-string preservation all hold up under the new/updated proxy tests.

Code findings

Medium

  • Worked-example figures are hardcoded (utils/pricingMarkdown.ts:315) — the $31 / $200 / $1,400 bill rows and their ingestion volumes are literal strings while the retention tables are generated from constants. A base-rate change would update the tables but silently leave these stale — the drift this PR set out to eliminate — and the new test doesn't cover them. They're all at minimum retention, so they can be derived from the same constants and asserted. See inline comment for a suggested helper.

Low

  • varyAccept: true on /pricing.md (app/(site)/pricing.md/route.ts:10) — the route never content-negotiates, so Vary: Accept only fragments its CDN cache. The negotiated URL is /pricing (already handled by the proxy); the mirrored /skill.md route omits it. Optional cleanup. See inline comment.

Notes on your reviewer questions

  • $199 framing — this is a product/copy call, not a code issue; the phrasing reads accurately as written. Deferring to product.
  • Feature-matrix markers (Coming soon / Beta / Enterprise self-managed) — transcribed content I can't verify against source here; needs the product read you flagged.
  • Follow-up debt (rate tables still duplicated in SigNozCloudPricingOverview.tsx, MetricsCostEstimation.tsx, components/Monthly-estimate/*) — agreed it's out of scope; folding them into constants/pricing.ts later is the right direction and this PR sets up the pattern well.

Neither finding is blocking. The pre-existing llms-txt structure failure you documented is unrelated to this PR.


  • Gather context (read changed files)
  • Apply signoz-website-frontend-pr-review skill + contributing/site-code.md
  • Review routing / proxy logic
  • Review pricing constants & markdown rendering
  • Review tests
  • Post inline findings + summary
    · branch feat/pricing-markdown-twin

Comment thread utils/pricingMarkdown.ts
Comment on lines +315 to +319
| Monthly ingestion | Usage cost | Bill |
| --- | --- | --- |
| 50 GB logs, 20 GB traces, 100 mn metric samples | $31 | ${teamsMinimum} (within included usage) |
| 300 GB logs, 200 GB traces, 500 mn metric samples | $200 | $200 |
| 2,000 GB logs, 1,000 GB traces, 5,000 mn metric samples | $1,400 | $1,400 |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worked-example figures are hardcoded — drift risk · Medium

The ingestion volumes and usage costs in this table ($31, $200, $1,400) are literal strings, whereas the retention tables above are generated from constants/pricing.ts. If the base rates change there, the summary line and retention tables update but these three rows silently go stale — the exact "page and markdown can't drift" failure mode this PR set out to eliminate. The new pricing-md.test.js asserts the retention tiers but not these numbers, so a rate change wouldn't fail CI here.

Since every example uses minimum-retention rates, the usage costs can be derived from the same constants, e.g.:

const example = (logsGb: number, tracesGb: number, metricMillions: number) => {
  const usage =
    logsGb * BASE_TRACES_AND_LOGS_PRICE_PER_GB +
    tracesGb * BASE_TRACES_AND_LOGS_PRICE_PER_GB +
    metricMillions * BASE_METRICS_PRICE_PER_MILLION_SAMPLES
  const bill = Math.max(PLAN_PRICING.TEAMS_MONTHLY_MINIMUM, usage)
  const note = usage < PLAN_PRICING.TEAMS_MONTHLY_MINIMUM ? ' (within included usage)' : ''
  return `| ${logsGb.toLocaleString()} GB logs, ${tracesGb.toLocaleString()} GB traces, ${metricMillions.toLocaleString()} mn metric samples | ${money(usage)} | ${money(bill)}${note} |`
}

That keeps the examples locked to the same source and lets you assert them in the test.

* plan/rate/feature document instead.
*/
export async function GET() {
return agentResponse(buildPricingMarkdown(), { varyAccept: true })

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

varyAccept: true on a route that never negotiates · Low

agentResponse documents varyAccept as "Set on negotiated URLs that also serve HTML." /pricing.md always returns markdown regardless of Accept, so Vary: Accept here just fragments the CDN cache by request Accept without buying anything. The URL that actually negotiates is /pricing, and the proxy already appends Vary: Accept there (servesMarkdownAlternate). The route this mirrors — /skill.md — calls agentResponse(SKILL_MD) with no varyAccept. Consider dropping it (and the corresponding assertion in pricing-md.test.js). Harmless either way, just inconsistent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant