From 0838270fa255e63918b7847eb6d328e8b99a6820 Mon Sep 17 00:00:00 2001 From: lostmartian Date: Fri, 28 Aug 2026 13:09:00 +0530 Subject: [PATCH] =?UTF-8?q?feat(website):=20site-wide=20redesign=20?= =?UTF-8?q?=E2=80=94=20new=20pages,=20editorial=20features=20page,=20tabbe?= =?UTF-8?q?d=20surfaces?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- website/package.json | 1 + website/pnpm-lock.yaml | 36 + .../src/app/(site)/action/opengraph-image.tsx | 66 ++ website/src/app/(site)/action/page.tsx | 382 +++++++++ .../app/(site)/adapters/AdaptersClient.tsx | 561 +++++++++++++ .../app/(site)/adapters/opengraph-image.tsx | 66 ++ website/src/app/(site)/adapters/page.tsx | 43 + .../blog/[slug]/opengraph-image.tsx | 2 +- .../src/app/{ => (site)}/blog/[slug]/page.tsx | 14 +- .../app/{ => (site)}/blog/opengraph-image.tsx | 0 website/src/app/{ => (site)}/blog/page.tsx | 6 +- .../app/(site)/compare/opengraph-image.tsx | 66 ++ website/src/app/(site)/compare/page.tsx | 442 ++++++++++ .../app/(site)/features/opengraph-image.tsx | 66 ++ website/src/app/(site)/features/page.tsx | 778 ++++++++++++++++++ website/src/app/{blog => (site)}/layout.tsx | 18 +- .../app/(site)/quickstart/opengraph-image.tsx | 66 ++ website/src/app/(site)/quickstart/page.tsx | 343 ++++++++ website/src/app/globals.css | 65 +- website/src/app/layout.tsx | 12 +- website/src/app/page.tsx | 12 +- website/src/app/sitemap.ts | 14 + .../src/components/AnimatedTelemetryHub.tsx | 370 +++++++++ website/src/components/ClosingCTA.tsx | 74 +- website/src/components/CodeBlock.tsx | 96 +++ website/src/components/FeaturesGrid.tsx | 179 ++-- website/src/components/Footer.tsx | 11 +- website/src/components/Header.tsx | 44 +- website/src/components/Hero.tsx | 106 ++- .../src/components/IntegrationShowcase.tsx | 345 +++----- website/src/components/PRCommentShowcase.tsx | 241 +++--- website/src/components/ProblemSolution.tsx | 188 +++-- website/src/components/StackFitSection.tsx | 124 +++ website/src/components/SurfacesTabs.tsx | 74 ++ 34 files changed, 4272 insertions(+), 639 deletions(-) create mode 100644 website/src/app/(site)/action/opengraph-image.tsx create mode 100644 website/src/app/(site)/action/page.tsx create mode 100644 website/src/app/(site)/adapters/AdaptersClient.tsx create mode 100644 website/src/app/(site)/adapters/opengraph-image.tsx create mode 100644 website/src/app/(site)/adapters/page.tsx rename website/src/app/{ => (site)}/blog/[slug]/opengraph-image.tsx (98%) rename website/src/app/{ => (site)}/blog/[slug]/page.tsx (93%) rename website/src/app/{ => (site)}/blog/opengraph-image.tsx (100%) rename website/src/app/{ => (site)}/blog/page.tsx (86%) create mode 100644 website/src/app/(site)/compare/opengraph-image.tsx create mode 100644 website/src/app/(site)/compare/page.tsx create mode 100644 website/src/app/(site)/features/opengraph-image.tsx create mode 100644 website/src/app/(site)/features/page.tsx rename website/src/app/{blog => (site)}/layout.tsx (71%) create mode 100644 website/src/app/(site)/quickstart/opengraph-image.tsx create mode 100644 website/src/app/(site)/quickstart/page.tsx create mode 100644 website/src/components/AnimatedTelemetryHub.tsx create mode 100644 website/src/components/CodeBlock.tsx create mode 100644 website/src/components/StackFitSection.tsx create mode 100644 website/src/components/SurfacesTabs.tsx diff --git a/website/package.json b/website/package.json index 77ab4e910..e43dd38a1 100644 --- a/website/package.json +++ b/website/package.json @@ -11,6 +11,7 @@ "dependencies": { "@lobehub/icons": "^5.16.0", "@types/prismjs": "^1.26.6", + "framer-motion": "^13.1.1", "katex": "^0.18.4", "lucide-react": "^1.31.0", "marked": "^18.0.9", diff --git a/website/pnpm-lock.yaml b/website/pnpm-lock.yaml index 6bde7d2e6..a3ae7ee93 100644 --- a/website/pnpm-lock.yaml +++ b/website/pnpm-lock.yaml @@ -14,6 +14,9 @@ importers: '@types/prismjs': specifier: ^1.26.6 version: 1.26.6 + framer-motion: + specifier: ^13.1.1 + version: 13.1.1(react-dom@19.2.8(react@19.2.8))(react@19.2.8) katex: specifier: ^0.18.4 version: 0.18.4 @@ -2156,6 +2159,7 @@ packages: eslint@9.39.5: resolution: {integrity: sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true peerDependencies: jiti: '*' @@ -2299,6 +2303,17 @@ packages: react-dom: optional: true + framer-motion@13.1.1: + resolution: {integrity: sha512-B/xn2TPS4f61cEBLFjiYlQFnBZUW1YVj/LM+C+N4OP8Rs95VLEI2ot/RlfBg111la/EiyECFaJJi/A3FWA8MUA==} + peerDependencies: + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} @@ -3085,9 +3100,15 @@ packages: motion-dom@12.43.0: resolution: {integrity: sha512-azKON4d9S65PEoFUiQTMTgPheEmzf2QngdRc50AKfJp9Q9mmcBVw22c8eMq9k8kxOFHdL7+WZY7N/5F/lwiDag==} + motion-dom@13.1.1: + resolution: {integrity: sha512-XSf8VYWSB6G/0IY3rWVbyLcxWXtAVHkN1PQE2agTaCv3u8RGvbwu56TyyR/MNzBqqNavEBTZzErcxI1TxBrjcA==} + motion-utils@12.39.0: resolution: {integrity: sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ==} + motion-utils@13.0.0: + resolution: {integrity: sha512-7DnN7TmbLcYXcG4RVadXIihWlyuM9afoUww8Y5Agg431kGKiuL2/OMyP4mJ5wLz+pvN3t5ySClLOaVXJ+wekRQ==} + motion@12.43.0: resolution: {integrity: sha512-BQgQbSa9Hn3/mtbib0MK53y6JSANa+YKUKlaYnWzAVDH424RYQ5LVpV3pNiWH00BA2z4ojsSdMzqT7g2FQwjuQ==} peerDependencies: @@ -6564,6 +6585,15 @@ snapshots: react: 19.2.8 react-dom: 19.2.8(react@19.2.8) + framer-motion@13.1.1(react-dom@19.2.8(react@19.2.8))(react@19.2.8): + dependencies: + motion-dom: 13.1.1 + motion-utils: 13.0.0 + tslib: 2.8.1 + optionalDependencies: + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + function-bind@1.1.2: {} function.prototype.name@1.2.0: @@ -7714,8 +7744,14 @@ snapshots: dependencies: motion-utils: 12.39.0 + motion-dom@13.1.1: + dependencies: + motion-utils: 13.0.0 + motion-utils@12.39.0: {} + motion-utils@13.0.0: {} + motion@12.43.0(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8): dependencies: framer-motion: 12.43.0(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) diff --git a/website/src/app/(site)/action/opengraph-image.tsx b/website/src/app/(site)/action/opengraph-image.tsx new file mode 100644 index 000000000..fccf4e0bf --- /dev/null +++ b/website/src/app/(site)/action/opengraph-image.tsx @@ -0,0 +1,66 @@ +import { ImageResponse } from "next/og"; + +export const size = { width: 1200, height: 630 }; +export const dynamic = "force-static"; +export const contentType = "image/png"; + +export default function OgImage() { + return new ImageResponse( + ( +
+
+
+
+
+
+
+
+
+
+
+
+
agentdiff
+
action
+
+ +
+
+
+ A gate on every +
+
+ pull request. +
+
+ +
+ Trajectory regression testing for AI agents + agentdiff.lostmartian.in/action +
+
+ ), + size, + ); +} diff --git a/website/src/app/(site)/action/page.tsx b/website/src/app/(site)/action/page.tsx new file mode 100644 index 000000000..e0ae7bcc5 --- /dev/null +++ b/website/src/app/(site)/action/page.tsx @@ -0,0 +1,382 @@ +import type { Metadata } from "next"; +import Link from "next/link"; +import { ArrowRight, Check, GitPullRequest, ShieldCheck, ShieldAlert, Sparkles, Terminal, FileCode2, ExternalLink, RotateCcw, AlertTriangle } from "lucide-react"; +import Reveal from "@/components/Reveal"; +import CodeBlock from "@/components/CodeBlock"; + +const SITE_URL = "https://agentdiff.lostmartian.in"; + +export const metadata: Metadata = { + title: "GitHub Action — Automated Trajectory Regression Gate", + description: + "Gate AI agent regressions on every pull request with the official agentdiff-check GitHub Action. Real-time PR commentary, threshold governance, and automated baseline rotation.", + keywords: [ + "agentdiff-check GitHub Action", + "CI/CD agent testing", + "PR trajectory check", + "automated regression gate", + "GitHub Actions AI testing", + ], + authors: [{ name: "AgentDiff", url: SITE_URL }], + creator: "AgentDiff", + publisher: "AgentDiff", + alternates: { canonical: "/action" }, + robots: { index: true, follow: true }, + openGraph: { + title: "GitHub Action | AgentDiff", + description: + "Gate AI agent regressions on every pull request with agentdiff-check — automated PR commentary, threshold governance, and baseline rotation.", + url: `${SITE_URL}/action`, + siteName: "AgentDiff", + type: "website", + }, + twitter: { + card: "summary_large_image", + title: "GitHub Action | AgentDiff", + description: + "Gate AI agent regressions on every pull request with agentdiff-check — automated PR commentary, threshold governance, and baseline rotation.", + }, +}; + +const ACTION_INPUTS = [ + { + name: "baseline", + req: true, + desc: "Path to the committed golden baseline trace JSON. When update-baseline is active, establishing this on clean runs is automatic.", + }, + { + name: "candidate", + req: true, + desc: "Path to the candidate trace JSON generated by the agent during the current PR workflow run.", + }, + { + name: "pr", + req: false, + desc: "Pull request number (e.g. ${{ github.event.number }}) to post the formatted divergence report and culprit explanation to.", + }, + { + name: "baseline-config", + req: false, + desc: "Path to the baseline's agentdiff.toml. Enables the Goodhart guard to detect if PR authors silently loosened test thresholds.", + }, + { + name: "adapter", + req: false, + desc: "Telemetry parser adapter: auto (default), generic, openinference, langfuse, langsmith, openai_agents, langgraph, crewai.", + }, + { + name: "update-baseline", + req: false, + desc: "Set to true to automatically advance the golden baseline trace when the PR verdict is completely clean.", + }, + { + name: "max-divergence / max-cost-delta / max-loops", + req: false, + desc: "Explicit override thresholds for TDI divergence [0.0-1.0], max cost delta percentage, and loop count allowances.", + }, +]; + +export default function ActionPage() { + return ( +
+ {/* 1. MARKETING HERO */} +
+ +
+ + Continuous Integration + +

+ Gate agent regressions on every PR. +

+

+ The agentdiff-check GitHub Action diffs candidate agent trajectories against your committed golden baselines, posts an interactive diagnostic report directly to the PR, and blocks the merge if regressions occur. +

+
+ 1-Step Action Workflow + • + Automated PR Comment Verdicts + • + Goodhart Threshold Guard +
+
+
+
+ + {/* 2. THE PRODUCTION WORKFLOW SPECIFICATION */} +
+
+ {/* Left Column: Context */} +
+
+ + 01 + + + GITHUB ACTION WORKFLOW + +
+

+ Drop into Any Repository in 30 Seconds +

+

+ Execute your agent in CI, record a PR candidate trace, and invoke agentdiff-check. If divergence or loop thresholds are breached, the action exits with code 1 and blocks the merge button. +

+ +
+ + {/* Right Column: Workflow Yaml */} +
+ +
+
+
+ + {/* 3. THREE CORE ARTIFACTS ON EVERY PR */} +
+ +
+ + Automation Deliverables + +

+ What Happens on Every PR Commit +

+

+ Three deterministic safeguards are deployed automatically on every pull request: +

+
+
+ + +
+ {/* Feature 1 */} +
+
+ +
+
+
DIAGNOSTIC COMMENT
+

Rich PR Commentary

+
+

+ Posts a complete regression breakdown table, topological diff trees, cost percentage changes, and the exact culprit step directly into the PR review timeline. +

+
+ + {/* Feature 2 */} +
+
+ +
+
+
STRICT CI ENFORCEMENT
+

Exit Code 1 Merge Blocker

+
+

+ --fail-on-regression causes the GitHub Action step to exit with code 1 upon threshold violations, keeping GitHub's merge button locked. +

+
+ + {/* Feature 3 */} +
+
+ +
+
+
AUTOMATED ROTATION
+

Clean Baseline Rotation

+
+

+ Pass update-baseline: true to advance the stored golden baseline only when candidate runs pass all mathematical regression criteria. +

+
+
+
+
+ + {/* 4. THE GOODHART THRESHOLD GUARD */} +
+
+ {/* Left Column: Explanation */} +
+
+ + 02 + + + THRESHOLD GOVERNANCE + +
+

+ Did the PR Pass — or Did the Gate Move? +

+

+ When developers weaken test thresholds (e.g. raising max_divergence from 0.25 to 0.70 to force a broken PR to pass), AgentDiff compares the PR configuration against the main branch baseline config. +

+
+
+ + Goodhart's Law Protection +
+

+ Threshold alterations are surfaced as explicit security warnings in the PR verdict, making test weakening immediately visible to code reviewers. +

+
+
+ + {/* Right Column: Governance Snippet */} +
+ /tmp/baseline-agentdiff.toml + + - name: Gate Against Baseline with Governance Guard + uses: lostmartian/agentdiff/.github/actions/agentdiff-check@main + with: + baseline: baselines/golden_run.json + candidate: traces/pr_candidate.json + baseline-config: /tmp/baseline-agentdiff.toml # Flags loosened thresholds + pr: \${{ github.event.number }} + github-token: \${{ secrets.GITHUB_TOKEN }}`} + /> +
+
+
+ + {/* 5. COMPLETE ACTION INPUTS SPECIFICATION */} +
+ +
+ + Action Parameters + +

+ Inputs Specification +

+

+ All parameters supported by the agentdiff-check action: +

+
+
+ + +
+
+ + + + + + + + + + {ACTION_INPUTS.map((input) => ( + + + + + + ))} + +
ParameterRequirementDescription
+ {input.name} + + {input.req ? ( + + REQUIRED + + ) : ( + + OPTIONAL + + )} + + {input.desc} +
+
+
+
+
+ + {/* 6. CTA */} +
+ +

+ Record. Diff. Gate. Ship. +

+

+ Automate trajectory regression testing across your entire team in under five minutes. +

+
+ + Start the Quickstart → + + + Explore Full CLI Docs + +
+
+
+
+ ); +} diff --git a/website/src/app/(site)/adapters/AdaptersClient.tsx b/website/src/app/(site)/adapters/AdaptersClient.tsx new file mode 100644 index 000000000..9559cb6bd --- /dev/null +++ b/website/src/app/(site)/adapters/AdaptersClient.tsx @@ -0,0 +1,561 @@ +"use client"; + +import React, { useState } from "react"; +import Link from "next/link"; +import { ArrowRight, Check, Copy, FileCode2, Terminal } from "lucide-react"; +import { OpenAI, Langfuse, LangSmith, LangGraph, CrewAI } from "@lobehub/icons"; +import OpenTelemetry from "@/components/OpenTelemetry"; +import Reveal from "@/components/Reveal"; +import CodeBlock from "@/components/CodeBlock"; +import AnimatedTelemetryHub from "@/components/AnimatedTelemetryHub"; + +/* ========================================================================= + CUSTOM VECTOR ASSET: 50-Line Adapter Protocol Visual + ========================================================================= */ +function CustomProtocolAsset() { + return ( +
+ + + + + + + + {/* Step 1: Subclass BaseAdapter */} + + + 1. SUBCLASS BASEADAPTER + + class MyTracer(BaseAdapter): +   def from_dict(cls, data): ... + Single mandatory abstractmethod + Maps proprietary fields to AgentTrace + + + + + {/* Step 2: Auto-Detection Sniffer */} + + + 2. OPT-IN FORMAT SNIFFER + + @classmethod + def detect(cls, data) -> bool: ... + Enables automatic format discovery + Sniffs distinct keys & schemas + + + + + {/* Step 3: Zero-Config Entrypoint Discovery */} + + + 3. PYTHON ENTRY-POINTS + + [project.entry-points] + "agentdiff.adapters" + Discovered Lazily + No core codebase fork needed + + +
+ ); +} + +const ECOSYSTEMS = [ + { + id: "langgraph", + name: "LangGraph", + tag: "Native Direct", + icon: LangGraph, + badge: "Direct State Ingestion", + highlight: "Zero OTel exporter overhead", + desc: "Ingests native LangGraph checkpoint state dumps and node graphs. Automatically maps branch checkpoints and tool node executions into a linear causal DAG.", + format: "LangGraph State Snapshots", + cliCommand: "agentdiff baseline.json cand.json --adapter langgraph", + pythonSnippet: `from agentdiff import load_trace\ntrace = load_trace("checkpoint.json", adapter="langgraph")`, + }, + { + id: "openai", + name: "OpenAI Agents SDK", + tag: "Built-In", + icon: OpenAI, + badge: "Run Tree Parsing", + highlight: "Validated against official SDK outputs", + desc: "Direct support for OpenAI's official Agents SDK telemetry format. Ingests function-calling tool payloads, streaming steps, and token count structures seamlessly.", + format: "OpenAI Agents SDK Run Dump", + cliCommand: "agentdiff baseline.json cand.json --adapter openai_agents", + pythonSnippet: `from agentdiff import load_trace\ntrace = load_trace("run_tree.json", adapter="openai_agents")`, + }, + { + id: "crewai", + name: "CrewAI", + tag: "Multi-Agent", + icon: CrewAI, + badge: "Crew Output Dump", + highlight: "Inter-agent task delegation tracking", + desc: "Ingests CrewOutput dumps from multi-agent crew kickoffs. Maps sequential agent delegations, task outputs, and tool usage into standardized steps.", + format: "CrewOutput Dumps", + cliCommand: "agentdiff crew_v1.json crew_v2.json --adapter crewai", + pythonSnippet: `from agentdiff import load_trace\ntrace = load_trace("crew_output.json", adapter="crewai")`, + }, + { + id: "otel", + name: "OpenTelemetry / OpenInference", + tag: "Industry Standard", + icon: OpenTelemetry, + badge: "OTel Semantic Conventions", + highlight: "LlamaIndex, Phoenix, Traceloop", + desc: "Compatible with any agent runtime instrumented via OpenTelemetry OpenInference standard spans. Ingests span parent-child relationships and transforms them into execution DAGs.", + format: "OTel Span JSON / OTLP", + cliCommand: "agentdiff otel_base.json otel_cand.json --adapter openinference", + pythonSnippet: `from agentdiff import load_trace\ntrace = load_trace("spans.json", adapter="openinference")`, + }, + { + id: "langfuse", + name: "Langfuse", + tag: "Observability", + icon: Langfuse, + badge: "SDK Trace Export", + highlight: "Snake_case normalization & latency maps", + desc: "Ingest trace dumps directly from the Langfuse SDK or API. Normalizes observations, scores, and nested generation steps into strict deterministic nodes.", + format: "Langfuse Trace Dumps", + cliCommand: "agentdiff langfuse_base.json langfuse_cand.json --adapter langfuse", + pythonSnippet: `from agentdiff import load_trace\ntrace = load_trace("langfuse.json", adapter="langfuse")`, + }, + { + id: "langsmith", + name: "LangSmith", + tag: "Observability", + icon: LangSmith, + badge: "Run Tree Sync", + highlight: "Hierarchical run tree reconstruction", + desc: "Converts hierarchical run trees exported from the LangSmith platform into standardized AgentTrace schema ready for regression evaluation in CI.", + format: "LangSmith Run Trees", + cliCommand: "agentdiff smith_base.json smith_cand.json --adapter langsmith", + pythonSnippet: `from agentdiff import load_trace\ntrace = load_trace("smith_run.json", adapter="langsmith")`, + }, + { + id: "generic", + name: "Generic JSON (Canonical)", + tag: "Native Schema", + icon: null, + badge: "Reference Format", + highlight: "Produced via agentdiff record", + desc: "AgentDiff's versioned canonical format. When using custom internal runtimes or Python SDK instrumentation, emit this format for instant, zero-conversion diffing.", + format: "Canonical AgentTrace JSON (v1.0)", + cliCommand: "agentdiff record -- my_agent_script.py", + pythonSnippet: `from agentdiff import load_trace\ntrace = load_trace("trace.json") # auto-detected`, + }, +]; + +export default function AdaptersClient() { + const [selectedId, setSelectedId] = useState("langgraph"); + const [copiedId, setCopiedId] = useState(null); + + const activeEcosystem = ECOSYSTEMS.find((e) => e.id === selectedId) || ECOSYSTEMS[0]; + const ActiveIcon = activeEcosystem.icon; + + const copyCommand = (cmd: string, id: string) => { + navigator.clipboard.writeText(cmd); + setCopiedId(id); + setTimeout(() => setCopiedId(null), 1800); + }; + + return ( +
+ {/* 1. MARKETING HERO */} +
+ +
+ + Universal Ecosystem Adapters + +

+ Bring your own telemetry. We normalize the rest. +

+

+ No need to re-instrument your agent or swap out your logging stack. Seven built-in adapters parse traces from LangGraph, CrewAI, OpenAI Agents, OpenTelemetry, and observability platforms into canonical execution graphs in under 2ms. +

+
+ Zero instrumentation lock-in + • + Automatic format sniffing + • + ~50 LOC extensible protocol +
+
+
+ + {/* Universal Telemetry Hub Visual */} + + + +
+ + {/* 2. VALUE PROPOSITION STRIP (HIGH-HIERARCHY MARKETING PILLARS) */} +
+ +
+ + Architecture Guarantees + +

+ Zero Ingestion Friction, 100% Deterministic Diffing +

+
+
+ + +
+ {/* Pillar 1 */} +
+
+ + 01 + +

+ Zero Rewrites Required +

+
+

+ You do not need to install proprietary SDK wrappers or modify your agent's runtime logic. Feed existing raw JSON dumps directly into the CLI or Python library. +

+
+ No runtime decorator overhead +
+
+ + {/* Pillar 2 */} +
+
+ + 02 + +

+ Format Auto-Sniffing +

+
+

+ AgentDiff sniffs the JSON structure, identifies unique telemetry fingerprint keys, and picks the right adapter automatically with <1.8ms execution latency. +

+
+ <1.8ms automatic resolution +
+
+ + {/* Pillar 3 */} +
+
+ + 03 + +

+ Identical CI Guarantees +

+
+

+ Whether your traces originate from LangGraph, CrewAI, or raw OTel spans, all 5 mathematical metrics (TDI, WEI, Loops, RSR) run against the same canonical DAG. +

+
+ 100% metric mathematical parity +
+
+
+
+
+ + {/* 3. SUPPORTED FRAMEWORKS SHOWCASE (SPLIT-DECK BENTO SHOWCASE) */} +
+ +
+ + Supported Ecosystems + +

+ Built-in Telemetry Adapters +

+

+ Maintained with contract tests against real-world trace fixtures to guarantee schema fidelity across framework upgrades. +

+
+
+ + {/* Master-Detail Interactive Showcase */} + +
+ {/* Left Column: Quick-Select Ecosystem List (Full-width on mobile, 5 cols on desktop) */} +
+ {ECOSYSTEMS.map((eco) => { + const isSelected = eco.id === selectedId; + const EcoIcon = eco.icon; + return ( + + ); + })} + + {/* Custom Adapter Direct CTA */} +
+
+
+ +
+
+
Custom Runtime
+
~50 LOC Plugin API
+
+
+ + Build + + +
+
+ + {/* Right Column: Dynamic Deep-Dive Stage (Hidden on mobile, visible on lg screens) */} +
+
+ {/* Header */} +
+
+
+ {ActiveIcon ? ( + + ) : ( + + )} +
+
+

+ {activeEcosystem.name} +

+
+ Source: {activeEcosystem.format} +
+
+
+ + + {activeEcosystem.badge} + +
+ + {/* Highlight & Description */} +
+
+ {activeEcosystem.highlight} +
+

+ {activeEcosystem.desc} +

+
+ + {/* Code Examples */} +
+ {/* CLI Command */} +
+
+ CLI Command + +
+
+ {activeEcosystem.cliCommand} +
+
+ + {/* Python Snippet */} +
+
+ Python SDK Invocation +
+
+
{activeEcosystem.pythonSnippet}
+
+
+
+
+ + {/* Bottom Guarantee Note */} +
+ ⚡ <1.8ms Execution Latency + Deterministic Canonical DAG Output +
+
+
+
+
+ + {/* 4. EXTENSIBILITY PROTOCOL (TECHNICAL SPECIFICATION) */} +
+ +
+ + Developer Protocol & Extensibility + +

+ Writing a Custom Adapter in ~50 Lines +

+

+ AgentDiff uses a plugin architecture. Implementing an adapter requires only one class method to map your schema into canonical trace steps: +

+
+
+ + {/* Custom Protocol Visual */} + + + + + +
+
+

+ Any Python class inheriting from BaseAdapter can participate in auto-detection by declaring a detect(data) -> bool method. +

+
+
1. Standard Entry-Point Registration
+
# In your package pyproject.toml:
+
+ [project.entry-points."agentdiff.adapters"]
+ my_tracer = "my_package.adapter:MyAdapter" +
+
+

+ Once registered via entry-points or register_adapter(), the CLI and Python SDK automatically discover the new adapter with zero modifications to core AgentDiff. +

+
+ +
+ bool: + return "acme_run_id" in data and "execution_spans" in data + + @classmethod + def from_dict(cls, data: dict) -> AgentTrace: + steps = [] + for idx, span in enumerate(data.get("execution_spans", [])): + steps.append( + TraceStep( + step_id=span["id"], + parent_id=span.get("parent_id"), + step_index=idx, + step_type=span.get("kind", "tool"), + name=span["operation_name"], + input_payload=span.get("inputs", {}), + output_payload=span.get("outputs", {}), + latency_ms=span.get("duration_ms", 0.0), + ) + ) + return AgentTrace(steps=steps)`} + /> +
+
+
+
+ + {/* 5. CTA SECTION */} +
+ +

+ Start Diffing Your Agent Telemetry Today +

+

+ No telemetry changes needed. Pass your existing JSON traces and run your first regression check in seconds. +

+
+ + Get Started with Quickstart → + + + Try Interactive Web Comparator + +
+
+
+
+ ); +} diff --git a/website/src/app/(site)/adapters/opengraph-image.tsx b/website/src/app/(site)/adapters/opengraph-image.tsx new file mode 100644 index 000000000..8c0bfeb36 --- /dev/null +++ b/website/src/app/(site)/adapters/opengraph-image.tsx @@ -0,0 +1,66 @@ +import { ImageResponse } from "next/og"; + +export const size = { width: 1200, height: 630 }; +export const dynamic = "force-static"; +export const contentType = "image/png"; + +export default function OgImage() { + return new ImageResponse( + ( +
+
+
+
+
+
+
+
+
+
+
+
+
agentdiff
+
adapters
+
+ +
+
+
+ Bring your own +
+
+ telemetry. +
+
+ +
+ Trajectory regression testing for AI agents + agentdiff.lostmartian.in/adapters +
+
+ ), + size, + ); +} diff --git a/website/src/app/(site)/adapters/page.tsx b/website/src/app/(site)/adapters/page.tsx new file mode 100644 index 000000000..da9497c91 --- /dev/null +++ b/website/src/app/(site)/adapters/page.tsx @@ -0,0 +1,43 @@ +import type { Metadata } from "next"; +import AdaptersClient from "./AdaptersClient"; + +const SITE_URL = "https://agentdiff.lostmartian.in"; + +export const metadata: Metadata = { + title: "Universal Adapters — Ingest LangGraph, CrewAI, OpenAI, & OTel Traces", + description: + "Zero-rewrite trace ingestion for LangGraph, CrewAI, OpenAI Agents SDK, OpenTelemetry / OpenInference, Langfuse, and LangSmith. Auto-detects schemas and diffs DAGs in CI.", + keywords: [ + "LangGraph trace diff", + "CrewAI testing", + "OpenAI Agents SDK traces", + "OpenTelemetry OpenInference", + "Langfuse trace export", + "LangSmith run trees", + "agent telemetry adapter", + "DAG diffing", + ], + authors: [{ name: "AgentDiff", url: SITE_URL }], + creator: "AgentDiff", + publisher: "AgentDiff", + alternates: { canonical: "/adapters" }, + robots: { index: true, follow: true }, + openGraph: { + title: "Universal Ecosystem Adapters | AgentDiff", + description: + "Ingest traces from LangGraph, CrewAI, OpenAI Agents SDK, OpenTelemetry, Langfuse, and LangSmith with zero rewrite.", + url: `${SITE_URL}/adapters`, + siteName: "AgentDiff", + type: "website", + }, + twitter: { + card: "summary_large_image", + title: "Universal Ecosystem Adapters | AgentDiff", + description: + "Ingest traces from LangGraph, CrewAI, OpenAI Agents SDK, OpenTelemetry, Langfuse, and LangSmith with zero rewrite.", + }, +}; + +export default function AdaptersPage() { + return ; +} diff --git a/website/src/app/blog/[slug]/opengraph-image.tsx b/website/src/app/(site)/blog/[slug]/opengraph-image.tsx similarity index 98% rename from website/src/app/blog/[slug]/opengraph-image.tsx rename to website/src/app/(site)/blog/[slug]/opengraph-image.tsx index f2e1d0d32..6e034fff6 100644 --- a/website/src/app/blog/[slug]/opengraph-image.tsx +++ b/website/src/app/(site)/blog/[slug]/opengraph-image.tsx @@ -1,5 +1,5 @@ import { ImageResponse } from "next/og"; -import { getAllPosts, getPost, formatDate } from "../../../lib/blog"; +import { getAllPosts, getPost, formatDate } from "../../../../lib/blog"; /** * Branded social card prerendered per blog post at build time. Same brand diff --git a/website/src/app/blog/[slug]/page.tsx b/website/src/app/(site)/blog/[slug]/page.tsx similarity index 93% rename from website/src/app/blog/[slug]/page.tsx rename to website/src/app/(site)/blog/[slug]/page.tsx index 22335bd28..66018e111 100644 --- a/website/src/app/blog/[slug]/page.tsx +++ b/website/src/app/(site)/blog/[slug]/page.tsx @@ -1,11 +1,11 @@ import type { Metadata } from "next"; import Link from "next/link"; import { notFound } from "next/navigation"; -import DocContent from "../../docs/DocContent"; -import BlogGradient from "../../../components/BlogGradient"; -import BlogToc from "../../../components/BlogToc"; -import ScrollToTop from "../../../components/ScrollToTop"; -import { getAllPosts, getPost, formatDate, getHeadings } from "../../../lib/blog"; +import DocContent from "../../../docs/DocContent"; +import BlogGradient from "../../../../components/BlogGradient"; +import BlogToc from "../../../../components/BlogToc"; +import ScrollToTop from "../../../../components/ScrollToTop"; +import { getAllPosts, getPost, formatDate, getHeadings } from "../../../../lib/blog"; const SITE_URL = "https://agentdiff.lostmartian.in"; @@ -25,7 +25,6 @@ export async function generateMetadata({ if (!post) return {}; const url = `${SITE_URL}/blog/${post.slug}`; const keywords = post.keywords ? post.keywords.split(",").map((s) => s.trim()) : []; - const ogImage = `${SITE_URL}/blog/${post.slug}/opengraph-image`; return { title: post.title, description: post.description, @@ -44,13 +43,12 @@ export async function generateMetadata({ publishedTime: post.date, authors: ["AgentDiff"], tags: keywords, - images: [{ url: ogImage, width: 1200, height: 630, alt: post.title }], + // og:image auto-wired from the per-slug opengraph-image route }, twitter: { card: "summary_large_image", title: `${post.title} | AgentDiff Blog`, description: post.description, - images: [ogImage], creator: "@agentdiff", }, }; diff --git a/website/src/app/blog/opengraph-image.tsx b/website/src/app/(site)/blog/opengraph-image.tsx similarity index 100% rename from website/src/app/blog/opengraph-image.tsx rename to website/src/app/(site)/blog/opengraph-image.tsx diff --git a/website/src/app/blog/page.tsx b/website/src/app/(site)/blog/page.tsx similarity index 86% rename from website/src/app/blog/page.tsx rename to website/src/app/(site)/blog/page.tsx index 9a7ff18b2..2f2bc82a2 100644 --- a/website/src/app/blog/page.tsx +++ b/website/src/app/(site)/blog/page.tsx @@ -1,6 +1,6 @@ import type { Metadata } from "next"; -import { getAllPosts } from "../../lib/blog"; -import BlogGrid from "../../components/BlogGrid"; +import { getAllPosts } from "../../../lib/blog"; +import BlogGrid from "../../../components/BlogGrid"; export const metadata: Metadata = { title: "Blog", @@ -14,13 +14,11 @@ export const metadata: Metadata = { url: "https://agentdiff.lostmartian.in/blog", siteName: "AgentDiff", type: "website", - images: [{ url: "https://agentdiff.lostmartian.in/blog/opengraph-image", width: 1200, height: 630, alt: "AgentDiff Blog" }], }, twitter: { card: "summary_large_image", title: "Blog | AgentDiff", description: "Product news, engineering deep dives, and guides on agent reliability.", - images: ["https://agentdiff.lostmartian.in/blog/opengraph-image"], }, }; diff --git a/website/src/app/(site)/compare/opengraph-image.tsx b/website/src/app/(site)/compare/opengraph-image.tsx new file mode 100644 index 000000000..5529f689a --- /dev/null +++ b/website/src/app/(site)/compare/opengraph-image.tsx @@ -0,0 +1,66 @@ +import { ImageResponse } from "next/og"; + +export const size = { width: 1200, height: 630 }; +export const dynamic = "force-static"; +export const contentType = "image/png"; + +export default function OgImage() { + return new ImageResponse( + ( +
+
+
+
+
+
+
+
+
+
+
+
+
agentdiff
+
compare
+
+ +
+
+
+ Different tools, +
+
+ different jobs. +
+
+ +
+ Trajectory regression testing for AI agents + agentdiff.lostmartian.in/compare +
+
+ ), + size, + ); +} diff --git a/website/src/app/(site)/compare/page.tsx b/website/src/app/(site)/compare/page.tsx new file mode 100644 index 000000000..18f74f979 --- /dev/null +++ b/website/src/app/(site)/compare/page.tsx @@ -0,0 +1,442 @@ +import type { Metadata } from "next"; +import Link from "next/link"; +import { Check, Minus, X, GitPullRequest, Radio, Sparkles, Scale, Gauge, ShieldAlert, Cpu } from "lucide-react"; +import Reveal from "@/components/Reveal"; + +const SITE_URL = "https://agentdiff.lostmartian.in"; + +export const metadata: Metadata = { + title: "AgentDiff vs Alternatives — Objective Architectural Comparison", + description: + "Deterministic trajectory diffing in CI vs LLM-as-judge evals vs observability platforms. What each evaluation layer actually catches, honestly compared.", + keywords: [ + "AgentDiff vs LLM judge", + "LLM eval comparison", + "agent observability vs testing", + "trajectory regression testing", + "Goodharts law agent eval", + ], + authors: [{ name: "AgentDiff", url: SITE_URL }], + creator: "AgentDiff", + publisher: "AgentDiff", + alternates: { canonical: "/compare" }, + robots: { index: true, follow: true }, + openGraph: { + title: "AgentDiff vs Alternatives | AgentDiff", + description: + "Deterministic trajectory diffing in CI vs LLM judges vs observability platforms. An honest architectural breakdown.", + url: `${SITE_URL}/compare`, + siteName: "AgentDiff", + type: "website", + }, + twitter: { + card: "summary_large_image", + title: "AgentDiff vs Alternatives | AgentDiff", + description: + "Deterministic trajectory diffing in CI vs LLM judges vs observability platforms. An honest architectural breakdown.", + }, +}; + +interface RowItem { + capability: string; + category: string; + agentdiff: boolean | string; + judge: boolean | string; + observability: boolean | string; +} + +const COMPARISON_ROWS: RowItem[] = [ + { + category: "CI/CD & Determinism", + capability: "Deterministic CI gate (100% reproducible verdict)", + agentdiff: true, + judge: false, + observability: false, + }, + { + category: "CI/CD & Determinism", + capability: "Catches silent trajectory drift (wrong path, right answer)", + agentdiff: true, + judge: "partial", + observability: false, + }, + { + category: "CI/CD & Determinism", + capability: "Blocks PR merge in automated CI pipelines", + agentdiff: true, + judge: "partial", + observability: false, + }, + { + category: "CI/CD & Determinism", + capability: "Flags when test thresholds are silently loosened in a PR", + agentdiff: true, + judge: false, + observability: false, + }, + { + category: "Root-Cause Diagnostics", + capability: "Isolates the exact culprit step & tool iteration loop", + agentdiff: true, + judge: false, + observability: "partial", + }, + { + category: "Root-Cause Diagnostics", + capability: "Zero LLM judge latency (<10ms execution)", + agentdiff: true, + judge: false, + observability: true, + }, + { + category: "Security & Privacy", + capability: "Runs air-gapped with zero API keys or external calls", + agentdiff: true, + judge: false, + observability: "partial", + }, + { + category: "Security & Privacy", + capability: "Traces stay 100% local on your machine / runner", + agentdiff: true, + judge: "partial", + observability: false, + }, + { + category: "Complementary Domains", + capability: "Semantic answer quality & tone grading", + agentdiff: false, + judge: true, + observability: "partial", + }, + { + category: "Complementary Domains", + capability: "Production traffic telemetry & live sampling dashboards", + agentdiff: false, + judge: false, + observability: true, + }, +]; + +function StatusCell({ v }: { v: boolean | string }) { + if (v === true) { + return ( +
+ + + + Yes +
+ ); + } + if (v === false) { + return ( +
+ + + + No +
+ ); + } + return ( +
+ + + + {v} +
+ ); +} + +export default function ComparePage() { + return ( +
+ {/* 1. MARKETING HERO */} +
+ +
+ + Architectural Comparison + +

+ Different tools, different jobs. +

+

+ AgentDiff doesn't replace observability dashboards or LLM-as-judge evals — it covers the critical gap they don't: deterministic, sub-10ms trajectory regression gates in CI before code merges. +

+
+ 100% Deterministic CI + • + Zero LLM calls + • + Air-gapped execution +
+
+
+
+ + {/* 2. THE 3-PILLAR TRIAD ARCHITECTURE */} +
+ +
+ + The Production Agent Stack + +

+ Where Each Layer Fits in Your Lifecycle +

+
+
+ + +
+ {/* Layer 1: AgentDiff */} +
+
+ +
+
+
CI/CD Pull Request Gate
+

AgentDiff

+
+

+ Runs deterministically on every pull request. Diffing candidate traces against golden baselines to block tool loops, cost surges, and drift in under 10ms. +

+
+ Verdict: PASS / FAIL (Exit Code 0 / 1) +
+
+ + {/* Layer 2: Observability */} +
+
+ +
+
+
Production Telemetry
+

Observability (OTel/Langfuse)

+
+

+ Ingests live user traffic, distributed traces, and latency charts in production. Shows what happened in the wild, but does not block broken code from shipping. +

+
+ Verdict: Real-time Telemetry & Alerts +
+
+ + {/* Layer 3: LLM Judge Evals */} +
+
+ +
+
+
Offline Research & Tone
+

LLM-as-Judge Evals

+
+

+ Evaluates semantic output quality, tone, and subjective answers on offline datasets. Accepts non-deterministic scoring and API cost to grade language nuance. +

+
+ Verdict: Qualitative Semantic Scores +
+
+
+
+
+ + {/* 3. CAPABILITY MATRIX (GROUPED BY DOMAIN) */} +
+ +
+ + Feature-by-Feature Breakdown + +

+ Capability Matrix +

+

+ An objective evaluation of what each tool is architected to solve across testing determinism, diagnostics, and privacy. +

+
+
+ + +
+
+ + + + + + + + + + + {/* Category 1 */} + + + + {COMPARISON_ROWS.filter((r) => r.category === "CI/CD & Determinism").map((row) => ( + + + + + + + ))} + + {/* Category 2 */} + + + + {COMPARISON_ROWS.filter((r) => r.category === "Root-Cause Diagnostics").map((row) => ( + + + + + + + ))} + + {/* Category 3 */} + + + + {COMPARISON_ROWS.filter((r) => r.category === "Security & Privacy").map((row) => ( + + + + + + + ))} + + {/* Category 4 */} + + + + {COMPARISON_ROWS.filter((r) => r.category === "Complementary Domains").map((row) => ( + + + + + + + ))} + +
Evaluation Dimension +
+ + AgentDiff (CI Gate) +
+
LLM JudgesObservability
+ 01 · CI/CD & Determinism +
+ {row.capability} + + + + + + +
+ 02 · Root-Cause Diagnostics +
+ {row.capability} + + + + + + +
+ 03 · Security & Privacy +
+ {row.capability} + + + + + + +
+ 04 · Complementary Domains +
+ {row.capability} + + + + + + +
+
+
+
+
+ + {/* 4. GUIDANCE: WHEN TO USE WHAT */} +
+ +
+ + Stack Architecture + +

+ The Unified Engineering Stack +

+

+ Leading agent engineering teams don't choose between these tools — they combine them according to each layer's strength: +

+
+
+ + +
+
+ + Recommended Reference Topology: +
+

+ Use Observability (Langfuse / OTel) in production to capture real user traces. Ingest those traces into AgentDiff as committed golden baselines to deterministically block regressions on every PR in CI. Run LLM Judges offline when conducting prompt experimentation or evaluating natural language tone. +

+
+
+
+ + {/* 5. CTA */} +
+ +

+ Add Deterministic Regression Testing to Your CI +

+

+ No API keys or hosted subscriptions required. Diff your first two traces in seconds. +

+
+ + Get Started with Quickstart → + + + Explore Supported Adapters + +
+
+
+
+ ); +} diff --git a/website/src/app/(site)/features/opengraph-image.tsx b/website/src/app/(site)/features/opengraph-image.tsx new file mode 100644 index 000000000..33d72ca78 --- /dev/null +++ b/website/src/app/(site)/features/opengraph-image.tsx @@ -0,0 +1,66 @@ +import { ImageResponse } from "next/og"; + +export const size = { width: 1200, height: 630 }; +export const dynamic = "force-static"; +export const contentType = "image/png"; + +export default function OgImage() { + return new ImageResponse( + ( +
+
+
+
+
+
+
+
+
+
+
+
+
agentdiff
+
features
+
+ +
+
+
+ Everything you need +
+
+ to gate AI agents. +
+
+ +
+ Trajectory regression testing for AI agents + agentdiff.lostmartian.in/features +
+
+ ), + size, + ); +} diff --git a/website/src/app/(site)/features/page.tsx b/website/src/app/(site)/features/page.tsx new file mode 100644 index 000000000..d99c034df --- /dev/null +++ b/website/src/app/(site)/features/page.tsx @@ -0,0 +1,778 @@ +import type { Metadata } from "next"; +import Link from "next/link"; +import { ArrowUpRight, ArrowRight } from "lucide-react"; +import Reveal from "@/components/Reveal"; +import CodeBlock from "@/components/CodeBlock"; + +const SITE_URL = "https://agentdiff.lostmartian.in"; + +export const metadata: Metadata = { + title: "Under the Hood — The AgentDiff Engine Specification", + description: + "A deep technical breakdown of the AgentDiff engine: Topological DAG alignment, exact metric formulas, stagnant loop detection algorithms, config-as-code, and deterministic explanation trees.", + keywords: [ + "DAG alignment algorithm", + "trajectory drift metric", + "agent loop detection", + "recovery step ratio", + "AgentDiff engine specification", + ], + authors: [{ name: "AgentDiff", url: SITE_URL }], + creator: "AgentDiff", + publisher: "AgentDiff", + alternates: { canonical: "/features" }, + robots: { index: true, follow: true }, + openGraph: { + title: "Under the Hood | AgentDiff Engine Specification", + description: "Deep technical dive into topological DAG diffing, cycle detection, and metric mathematics.", + url: `${SITE_URL}/features`, + siteName: "AgentDiff", + type: "website", + }, + twitter: { + card: "summary_large_image", + title: "Under the Hood | AgentDiff Engine Specification", + description: "Deep technical dive into topological DAG diffing, cycle detection, and metric mathematics.", + }, +}; + +/* ========================================================================= + CUSTOM VECTOR ASSET 1: Ingestion & Equivalence Signature Hasher + ========================================================================= */ +function IngestionSignatureAsset() { + return ( +
+ + + + + + + + + + + + {/* Input Raw Telemetry Box */} + + + RAW TELEMETRY TRACE + step_type: "tool" + name: "vector_query" + timestamp: 1740723821 + uuid: "4a89-ef12..." + payload: { query: ... } + + + {/* Connector 1 */} + + + {/* Middle: Masking & Key Extraction Filter */} + + + SEMANTIC MASKING + − Drop UUIDs & Timestamps + + Sort Input Keys + → key_tuple: ("query", "top_k") + regex filter via agentdiff.toml + + + {/* Connector 2 */} + + + {/* Output: Canonical Signature Hash */} + + + EQUIVALENCE SIGNATURE + + ("tool", "vector_query", +  ("query", "top_k")) + Deterministic graph token + Ready for Topological LCS + + +
+ ); +} + +/* ========================================================================= + CUSTOM VECTOR ASSET 2: Dynamic 2D LCS Matrix Alignment Diagram + ========================================================================= */ +function TopologicalMatrixAsset() { + return ( +
+ + + + + + + + {/* Matrix Grid Canvas */} + + {/* Header Row (Candidate Steps) */} + CANDIDATE SEQUENCE → + planner + search + sql_gen + retry_1 + retry_2 + execute + + {/* Baseline Column */} + BASELINE ↓ + planner + search + sql_gen + execute + + {/* Matrix Grid Lines */} + + + {/* Matched Cell 1: planner/planner */} + + LCS = 1 + + {/* Matched Cell 2: search/search */} + + LCS = 2 + + {/* Matched Cell 3: sql_gen/sql_gen */} + + LCS = 3 + + {/* Diverged Fork Loop Path */} + + + 2 UNMATCHED RETRY STEPS + + {/* Matched Cell 4: execute/execute */} + + LCS = 4 + + +
+ ); +} + +/* ========================================================================= + CUSTOM VECTOR ASSET 3: Blame Attribution Decision Tree + ========================================================================= */ +function BlameAttributionTreeAsset() { + return ( +
+ + + + + + + + {/* Root Diff Report Input */} + + + DiffReport + Aligned DAG output + + + {/* Tree Branches */} + + + + + {/* Decision Nodes */} + {/* Branch 1: Loops */} + + + 1. Stagnant Loop Check (k-gram) + Match: loop repeats > 0 with static args + + + + CULPRIT: "retry_sql_query" + High Severity Blame Finding Emitted + + + {/* Branch 2: Divergence */} + + + 2. Fork Point Discovery + Locates first modified causal edge + + + + FORK: Node index 03 diverged + TDI Score Calculated & Attributed + + + {/* Branch 3: Resource Delta */} + + + 3. Resource Spike Attribution + Computes USD & token delta per node + + + + COST SPIKE: +170% on Step 04 + Formatted for CLI Tree & PR Comment + + +
+ ); +} + +/* ========================================================================= + CUSTOM VECTOR ASSET 4: Declarative Governance CI Lifecycle + ========================================================================= */ +function DeclarativeGovernanceAsset() { + return ( +
+ + + + + + + + {/* Step 1: Git Tracked Config */} + + + 1. VERSION CONTROLLED TOML + + max_divergence = 0.25 + allow_loops = false + Committed in repo next to code + Single source of truth + + + + + {/* Step 2: PR Threshold Guardian */} + + + 2. ANTI-GOODHART GUARDIAN + PR Diff Analysis: + ⚠ TDI raised: 0.25 → 0.45 + Flags Loosened Rigor in PR + Prevents artificial green checks + + + + + {/* Step 3: Deterministic Exit Gate */} + + + 3. UNIX CI GATE VERDICT + + EXIT CODE 1 (FAIL) + Threshold breached: TDI 0.40 > 0.25 + Provenance Self-Described + SHA · Baseline age · Flag options + + +
+ ); +} + +export default function FeaturesPage() { + return ( +
+ {/* 1. TECHNICAL SPEC HEADER */} +
+ +
+ + Engine Specification & Architecture + +

+ How the comparator actually works under the hood. +

+

+ This is the technical deep-dive into the deterministic graph algorithms powering AgentDiff: topological trace normalization, modified LCS alignment, k-gram loop detection, rule-based root cause isolation, and CI exit code specifications. +

+
+ Zero LLM calls + • + <10ms execution latency + • + 100% Deterministic CI verdicts +
+
+
+
+ + {/* 2. SECTION 01: TRACE NORMALIZATION & EQUIVALENCE SIGNATURES */} +
+ +
+ + 01 / Trace Ingestion & Equivalence + +

+ Step Signatures & Semantic Equivalence +

+

+ Before comparison, raw JSON telemetry traces from adapters (LangGraph, CrewAI, OpenAI Agents, OTel) are normalized into strongly typed DAGs. Every node computes a structural equivalence signature: +

+
+
+ + {/* Ingestion & Signature Hasher Vector Asset */} + + + + + +
+
+

+ Two steps A and B are considered equal by the comparator if and only if their signature matches: +

+
+ # Equivalence Signature Tuple
+ Signature(Node) = (
+   step.step_type,  # tool | llm | retrieval | reasoning
+   step.name,       # tool/function identifier
+   sorted_keys      # tuple(sorted(input_payload.keys()))
+ ) +
+

+ When strict_tool_signatures = true is set in agentdiff.toml, the comparator performs recursive value hashing on the input payload, while applying user-defined regex exclusion masks to discard non-deterministic timestamps, request UUIDs, and auth tokens. +

+
+ +
+ +
+
+
+
+ + {/* 3. SECTION 02: MODIFIED TOPOLOGICAL LCS ALIGNMENT */} +
+ +
+ + 02 / The Comparison Algorithm + +

+ Topological Longest Common Subsequence (LCS) +

+

+ Standard string diff algorithms fail on agent traces because tool executions contain dependencies and state mutations. AgentDiff uses a 3-phase graph alignment pipeline: +

+
+
+ + {/* Alignment Matrix Vector Asset */} + + + + + +
+ {/* 3 Phases */} +
+
+
PHASE 1: TOPO-SORT
+

Dependency DAG Sorting

+

+ Linearizes the execution graph via Kahn's algorithm while preserving strict causal dependency edges defined by parent_id pointers. +

+
+
+
PHASE 2: DP LCS TABLE
+

2D Matrix Reconstruction

+

+ Constructs an O(N · M) dynamic programming grid scoring sequence matches, insertions, and branch modifications. +

+
+
+
PHASE 3: BACKTRACKING
+

Diff Classification

+

+ Backtracks the optimal alignment path to mark each step as MATCHED, ADDED, REMOVED, or MODIFIED. +

+
+
+ + {/* LCS Matrix Visual Table */} +
+
Alignment Trace Matrix Table
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IdxBaseline StepCandidate StepStatusState Delta
01planner:intentplanner:intent· MATCHEDidentical
02search_db(query)search_db(query)· MATCHEDidentical
03synthesize_sqlsynthesize_sql~ MODIFIED{`output.syntax_version: 1 → 2`}
04— (absent)retry_sql_query+ ADDEDLoop entry point (500 syntax error)
05— (absent)retry_sql_query+ ADDEDStagnant parameter repetition (k=1)
06execute_db_poolexecute_db_pool· MATCHEDFell back to working query
+
+
+
+
+
+ + {/* 4. SECTION 03: MATHEMATICAL SPECIFICATION OF THE 5 METRICS */} +
+ +
+ + 03 / Mathematical Formulas + +

+ Formal Metric Definitions & Visual Logic +

+

+ Every verdict in AgentDiff is backed by closed-form equations and visual step breakdowns. +

+
+
+ +
+ {/* Formula 1: TDI */} + +
+
+ Metric 01 +

Trajectory Divergence Index

+
TDI ∈ [0.0, 1.0]
+
+
+
+ TDI(A, B) = 1 − [ 2 · |LCS(A, B)| ] / [ |A| + |B| ] +
+ {/* Visual TDI Comparison Strip */} +
+
+ Base: +
+ {["auth", "search", "filter", "export"].map((s, i) => ( + {s} + ))} +
+
+
+ Cand: +
+ {["auth", "search"].map((s, i) => ( + {s} + ))} + {["raw_sql", "scrape", "export"].map((s, i) => ( + {s} + ))} +
+
+
+

+ Where |A| and |B| are trace step counts, and |LCS(A, B)| is the longest common valid subsequence. +

+
+
+
+ + {/* Formula 2: WEI */} + +
+
+ Metric 02 +

Wasted Effort Index

+
WEI ∈ [0.0, 1.0]
+
+
+
+ WEI = ( N_failed + N_retry + N_abandoned ) / N_total +
+ {/* Visual Compute Allocation Bar */} +
+
+
+
+
+
+ 42% Productive Execution (3 steps) + 58% Ineffective Retries (4 iterations) +
+
+

+ Measures the fraction of total trace compute allocated to non-productive branches. A step is flagged as wasted if it emits an error status or is superseded by retries. +

+
+
+ + + {/* Formula 3: Stagnant Loop Detector */} + +
+
+ Metric 03 +

k-Gram Cycle Detector

+
Loops ∈ ℕ₀
+
+
+
+ # Stagnant Loop Condition:
+ is_loop = (Sequence[i : i+k] == Sequence[i+k : i+2k]) ∧ (Input_Payload[i] == Input_Payload[i+k]) +
+ {/* Visual Stagnant State Flow */} +
+
+ query_sql(id=104, limit=10) + 500 Syntax Error +
+
+ ↻ query_sql(id=104, limit=10) [stagnant args] + 500 Syntax Error +
+
+

+ Evaluates sliding window sub-sequences for pattern lengths k ∈ [1, N/2]. Checks state stagnation to allow progressive pagination while halting recursive loops. +

+
+
+
+ + {/* Formula 4: RSR */} + +
+
+ Metric 04 +

Recovery Step Ratio

+
RSR ∈ [0.0, ∞)
+
+
+
+ RSR = Steps_to_Resolution(Candidate) / Steps_to_Resolution(Baseline) +
+
+
+ Baseline: + Error + → + Fallback (1 step · 420ms) +
+
+ Candidate: + Error + → + Retry 1 + → + Retry 2 + → + Fallback (3 steps · 1,840ms) +
+
+

+ Isolates the sub-graph following the first non-fatal tool error. An RSR > 1.0 indicates degraded self-healing capability. +

+
+
+
+
+
+ + {/* 5. SECTION 04: DETERMINISTIC ROOT CAUSE EXPLANATIONS */} +
+ +
+ + 04 / Root Cause Synthesis + +

+ Rule-Based Blame Attribution Tree +

+

+ When a gate fails, developers do not need raw graph matrices — they need the culpable step and the exact failure mechanism. AgentDiff runs a deterministic rules engine (explanations.py) that converts diff reports into human-readable findings: +

+
+
+ + {/* Blame Attribution Vector Tree Asset */} + + + + + +
+
+
Rule Priority Cascade Execution
+
+
1. Loop Attribution (Highest Severity)
+

Identifies exact cycle length k, repeated tool names, and stagnant parameter signatures.

+
2. Divergence Fork Isolation
+

Pins the exact step index where candidate sequence forked away from baseline.

+
3. Cost & Latency Attribution
+

Computes delta percentages for tokens and milliseconds on the culprit node.

+
+
+ +
+
Rendered Terminal Tree Output (--tree)
+
+{`baseline [4 steps] vs candidate [6 steps]
+     1 · planner:task_intent
+     2 · search_vector_db
+     3 ~ synthesize_sql            (changed)
+     4 + retry_sql_query           (added — culprit loop)
+     5 + retry_sql_query           (added — stagnant cycle)
+     6 · execute_db_pool
+
+> Root Cause: 'retry_sql_query' [loop] — entered a loop
+  repeating 'retry_sql_query' (2 times) after SQL syntax error.`}
+              
+
+
+
+
+ + {/* 6. SECTION 05: CONFIG-AS-CODE SPECIFICATION */} +
+ +
+ + 05 / Configuration Specification + +

+ Declarative Governance (agentdiff.toml) +

+

+ Thresholds and masking rules are committed directly to your git repository, ensuring consistent CI gate evaluation across all developer machines and automated workflows. +

+
+
+ + {/* Declarative Governance Vector Flow Asset */} + + + + + +
+ 0.25 +max_wasted_effort = 0.10 # Fails CI if > 10% compute is wasted +allow_loops = false # Fails CI on any tool repetition +max_cost_increase_pct = 5.0 # Fails CI if cost delta > +5% +max_recovery_step_ratio = 1.5 # Fails CI if error recovery degrades > 1.5x + +[masking] +ignore_keys = ["timestamp", "session_id", "trace_id", "auth_token"] +regex_patterns = ["^uuid_[0-9a-f-]+$", "^Bearer\\\\s+.*$"] + +[governance] +warn_stale_baseline_days = 30 # Warns when golden baseline exceeds 30 days +flag_threshold_changes = true # Flags PRs that lower gate rigor`} + /> +
+
+
+ + {/* 7. CTA / DOCS LINK */} +
+ +

+ Ready to integrate into your CI pipeline? +

+

+ Install the Python package, record your first golden baseline trace, and gate your agent in 5 minutes. +

+
+ + Get Started with Quickstart → + + + Explore Full CLI & SDK Docs + +
+
+
+
+ ); +} diff --git a/website/src/app/blog/layout.tsx b/website/src/app/(site)/layout.tsx similarity index 71% rename from website/src/app/blog/layout.tsx rename to website/src/app/(site)/layout.tsx index db149b9ea..5a8387d72 100644 --- a/website/src/app/blog/layout.tsx +++ b/website/src/app/(site)/layout.tsx @@ -1,20 +1,18 @@ -import type { Metadata } from "next"; import fs from "fs"; import path from "path"; import Header from "../../components/Header"; import Footer from "../../components/Footer"; /** - * Shared shell for every /blog route: the same sticky Header, Footer, and - * max-w-7xl grid container as the landing page, so the blog reads as part of - * the site rather than a separate surface. + * Shared chrome for every marketing route in the (site) group — blog, + * features, action, quickstart, adapters, compare. Same sticky Header, + * Footer, and max-w-7xl grid container as the landing page, so every page + * reads as one site. + * + * Home (/) keeps its own wrapper (landing-specific layout classes); this + * group covers everything else. */ -export const metadata: Metadata = { - description: - "Product news, engineering deep dives, and guides on trajectory regression testing for AI agents.", -}; - /** Read the version string from the package `__init__.py` at build time. */ function readPackageVersion(): string { try { @@ -33,7 +31,7 @@ function readPackageVersion(): string { } } -export default function BlogLayout({ +export default function SiteLayout({ children, }: { children: React.ReactNode; diff --git a/website/src/app/(site)/quickstart/opengraph-image.tsx b/website/src/app/(site)/quickstart/opengraph-image.tsx new file mode 100644 index 000000000..1d0bfd5ee --- /dev/null +++ b/website/src/app/(site)/quickstart/opengraph-image.tsx @@ -0,0 +1,66 @@ +import { ImageResponse } from "next/og"; + +export const size = { width: 1200, height: 630 }; +export const dynamic = "force-static"; +export const contentType = "image/png"; + +export default function OgImage() { + return new ImageResponse( + ( +
+
+
+
+
+
+
+
+
+
+
+
+
agentdiff
+
quickstart
+
+ +
+
+
+ Install. Record. +
+
+ Diff. Gate. +
+
+ +
+ Trajectory regression testing for AI agents + agentdiff.lostmartian.in/quickstart +
+
+ ), + size, + ); +} diff --git a/website/src/app/(site)/quickstart/page.tsx b/website/src/app/(site)/quickstart/page.tsx new file mode 100644 index 000000000..51cca5ca8 --- /dev/null +++ b/website/src/app/(site)/quickstart/page.tsx @@ -0,0 +1,343 @@ +import type { Metadata } from "next"; +import Link from "next/link"; +import { ArrowRight, Check, Sparkles, Terminal, FileCode2, Play, GitPullRequest, ShieldCheck, Flame, ArrowDown } from "lucide-react"; +import Reveal from "@/components/Reveal"; +import CodeBlock from "@/components/CodeBlock"; + +const SITE_URL = "https://agentdiff.lostmartian.in"; + +export const metadata: Metadata = { + title: "Quickstart — Zero to Gated Agent in 5 Minutes", + description: + "Install, record, diff, and gate with AgentDiff. Step-by-step developer guide with CLI commands, pytest integration, and GitHub Action workflows.", + keywords: [ + "AgentDiff quickstart", + "agent trajectory testing tutorial", + "AI agent regression CI/CD", + "record agent trace", + "agentdiff CLI", + ], + authors: [{ name: "AgentDiff", url: SITE_URL }], + creator: "AgentDiff", + publisher: "AgentDiff", + alternates: { canonical: "/quickstart" }, + robots: { index: true, follow: true }, + openGraph: { + title: "Quickstart | AgentDiff", + description: "Install, record, diff, and gate — AgentDiff in five minutes.", + url: `${SITE_URL}/quickstart`, + siteName: "AgentDiff", + type: "website", + }, + twitter: { + card: "summary_large_image", + title: "Quickstart | AgentDiff", + description: "Install, record, diff, and gate — AgentDiff in five minutes.", + }, +}; + +export default function QuickstartPage() { + return ( +
+ {/* 1. HERO HEADER */} +
+ +
+ + Developer Onboarding + +

+ Zero to gated agent in five minutes. +

+

+ No API keys. No accounts. No telemetry daemons. One clean install, one recorded golden trace, and an automated regression gate in CI. +

+
+ <5 minute setup + • + Python 3.10+ + • + Air-gapped & Local +
+
+
+
+ + {/* 2. MAIN 4-STEP PIPELINE */} +
+
+ + {/* STEP 01 */} + +
+
+
+ + 01 + + + INSTALLATION + +
+

+ Install the CLI & Python SDK +

+

+ Install AgentDiff into your virtual environment or global toolchain. Compatible with Python 3.10+ and managed with uv or pip with zero background daemons. +

+
+ + Python 3.10+ + · macOS / Linux / Windows +
+
+ +
+ +
+
+
+ + {/* STEP 02 */} + +
+
+
+ + 02 + + + BASELINE CAPTURE + +
+

+ Record a Golden Baseline Trace +

+

+ Point AgentDiff directly at your agent entry callable. It executes once and emits a deterministic canonical JSON trace file. Commit this to git next to your tests. +

+
+ + Canonical AgentTrace DAG + · Schema v1.0 +
+
+ +
+ +
+
+
+ + {/* STEP 03 */} + +
+
+
+ + 03 + + + TOPOLOGICAL EVALUATION + +
+

+ Diff the Next Run & Inspect Divergence +

+

+ When modifying prompts, model versions, or tools, run the comparator to inspect structural divergence (TDI), loop cycles, and cost delta in <10ms. +

+
+ + <10ms execution + · Zero LLM judge calls +
+
+ +
+ +
+
+
+ + {/* STEP 04 */} + +
+
+
+ + 04 + + + CI/CD REGRESSION GATE + +
+

+ Gate Every PR in Your CI Pipeline +

+

+ Add a strict exit-code regression gate to your continuous integration workflow. Exits with code 1 if thresholds are breached and outputs a rich diagnostic PR comment. +

+
+ + Exit Code 0 / 1 + · Automated PR diagnostics +
+
+ +
+ +
+
+
+ +
+
+ + {/* 3. INTEGRATION PATTERNS: PYTEST & DECLARATIVE POLICY */} +
+ +
+ + Integration Frameworks + +

+ Two Ways to Run in Your Toolchain +

+

+ Whether you want standard unit test assertions in pytest or repository-wide threshold governance in agentdiff.toml: +

+
+
+ + +
+ {/* Pattern 1 */} +
+
+
+ + A + +

Native Pytest Plugin

+
+ + pytest-plugin + +
+

+ Run your golden test baselines directly through standard pytest runners. Automatically flag regressions with expressive assert_no_regressions() assertions. +

+
+ +
+
+ + {/* Pattern 2 */} +
+
+
+ + B + +

Declarative Governance Policy

+
+ + agentdiff.toml + +
+

+ Commit your regression thresholds once in agentdiff.toml. Audit threshold alterations directly on PRs to prevent silent test weakening. +

+
+ +
+
+
+
+
+ + {/* 4. CTA */} +
+ +

+ Ready to Automate on GitHub? +

+

+ Add the one-step AgentDiff GitHub Action to your workflows and get automatic PR comment verdicts on every commit. +

+
+ + Setup GitHub Action → + + + Explore Full CLI Docs + +
+
+
+
+ ); +} diff --git a/website/src/app/globals.css b/website/src/app/globals.css index 23806525b..cd9b32bbb 100644 --- a/website/src/app/globals.css +++ b/website/src/app/globals.css @@ -66,10 +66,11 @@ --font-mono: var(--font-jetbrains-mono), ui-monospace, monospace; } -/* Anchor navigation: glide instead of jump, and never hide a section - title behind the sticky bar. */ -html { - scroll-behavior: smooth; +/* Anchor navigation and scroll offsets */ +@media (prefers-reduced-motion: no-preference) { + :root:has(:target) { + scroll-behavior: smooth; + } } section[id] { @@ -83,7 +84,6 @@ body { position: relative; overflow-x: hidden; -webkit-font-smoothing: antialiased; - transition: background-color 0.2s ease, color 0.2s ease; } /* Custom scrollbars */ @@ -91,13 +91,16 @@ body { width: 8px; height: 8px; } + ::-webkit-scrollbar-track { background: transparent; } + ::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 9999px; } + ::-webkit-scrollbar-thumb:hover { background: var(--faint); } @@ -108,8 +111,15 @@ body { } @keyframes canvas-fade { - from { opacity: 0; transform: translateY(6px); } - to { opacity: 1; transform: translateY(0); } + from { + opacity: 0; + transform: translateY(6px); + } + + to { + opacity: 1; + transform: translateY(0); + } } .animate-canvas-fade { @@ -227,7 +237,7 @@ body { } /* Only style inline code - not code inside syntax highlighter blocks */ -.markdown-content :not(.relative) > code:not([class*="language-"]) { +.markdown-content :not(.relative)>code:not([class*="language-"]) { font-family: var(--font-mono); background-color: var(--surface-2); border: 1px solid var(--border); @@ -391,33 +401,54 @@ body { overflow-y: hidden; } -.markdown-content .katex-display > .katex { +.markdown-content .katex-display>.katex { display: block; text-align: center; } -.markdown-content .katex-display > .katex > .katex-html { +.markdown-content .katex-display>.katex>.katex-html { display: block; } .markdown-content .katex-display .katex-display { margin: 0; } + /* Mobile nav overlay entrance */ @keyframes menu-in { - from { opacity: 0; transform: translateY(-10px); } - to { opacity: 1; transform: translateY(0); } + from { + opacity: 0; + transform: translateY(-10px); + } + + to { + opacity: 1; + transform: translateY(0); + } +} + +.menu-in { + animation: menu-in 0.24s ease-out both; } -.menu-in { animation: menu-in 0.24s ease-out both; } /* Demo screen transition */ @keyframes fade-slide { - from { opacity: 0; transform: translateY(10px); } - to { opacity: 1; transform: translateY(0); } + from { + opacity: 0; + transform: translateY(10px); + } + + to { + opacity: 1; + transform: translateY(0); + } +} + +.fade-slide { + animation: fade-slide 0.4s ease-out both; } -.fade-slide { animation: fade-slide 0.4s ease-out both; } /* Keep anchored sections clear of the sticky nav */ section[id] { scroll-margin-top: 5.5rem; -} +} \ No newline at end of file diff --git a/website/src/app/layout.tsx b/website/src/app/layout.tsx index 98bee211e..9e0944797 100644 --- a/website/src/app/layout.tsx +++ b/website/src/app/layout.tsx @@ -155,15 +155,17 @@ export default function RootLayout({ children }: LayoutProps<"/">) { className={`${manrope.variable} ${jetBrainsMono.variable} h-full antialiased`} > - - - +