From 0724029270a054f27e6a02de20629a9207f77347 Mon Sep 17 00:00:00 2001 From: Aryam Goyal Date: Thu, 20 Aug 2026 16:58:32 +0530 Subject: [PATCH] Clarify FixMap website and evidence boundaries --- CHANGELOG.md | 4 +- .../app/_components/interactive-map-stage.tsx | 291 ++++-------------- apps/web/app/changelog/page.tsx | 4 +- apps/web/app/evidence/page.tsx | 12 + apps/web/app/globals.css | 76 ++++- apps/web/app/page.tsx | 73 +++-- apps/web/app/product/page.tsx | 4 +- apps/web/app/sitemap.ts | 12 +- benchmarks/agent-study/protocol.json | 19 +- docs/AGENT_STUDY.md | 23 +- scripts/evaluate-agent-study.mjs | 61 +++- 11 files changed, 285 insertions(+), 294 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9cb18a5..08f7c69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ which is generated from the recorded results rather than transcribed by hand. - `fixmap benchmark --repo . --last 50` backtests BM25-over-code, FixMap context, and FixMap with Impact Graph on identical historical parent-snapshot corpora. It reports all, path-mentioned, and unmentioned cohorts, Wilson intervals, raw cases, skip counts, and secondary-file recall without executing repository code or scoring generated twins as primary answers. - `fixmap plan --format agent` emits a compact, stable handoff organized as EDIT CANDIDATE, INSPECT, TEST, RISK, AVOID, and UNCERTAINTY. - A frozen four-arm agent-study protocol and validator are checked in for future controlled measurements. No agent-effectiveness or time-saved claim is made without completed, auditable runs. -- A 32-second motion-first agent comparison is available on the README and website in animated-preview and 1080p H.264/AAC formats, with original no-vocals music and no unsupported savings claim. +- A 32-second motion-first agent comparison is available on the README and website in animated-preview and 1080p H.264/AAC formats, with original no-vocals music and no claim of measured agent efficiency. ### Improved @@ -58,7 +58,7 @@ which is generated from the recorded results rather than transcribed by hand. ### Evidence - The release ledger maps all 126 open GitHub issues in #500-#627 (excluding already-closed #542 and #599) plus every item in the attached 30-finding review to a resolution and verification source. -- External, held-out, adversarial, BM25/lexical/path baselines, savings, rendered examples, package smoke tests, and the 1,000-file scan gate were regenerated and checked. The held-out evidence continues to state plainly where BM25 leads FixMap. +- External, held-out, adversarial, BM25/lexical/path baselines, context-size proxy, scan-performance, rendered examples, package smoke tests, and the 1,000-file scan gate were regenerated and checked. The held-out evidence continues to state plainly where BM25 leads FixMap. ### Installation diff --git a/apps/web/app/_components/interactive-map-stage.tsx b/apps/web/app/_components/interactive-map-stage.tsx index bfbf016..c04c07d 100644 --- a/apps/web/app/_components/interactive-map-stage.tsx +++ b/apps/web/app/_components/interactive-map-stage.tsx @@ -1,242 +1,87 @@ -"use client"; - -import { useEffect, useMemo, useRef, useState, type CSSProperties } from "react"; import { ArrowRight, - CaretDown, CheckCircle, FileText, + GitBranch, ShieldCheck, Warning -} from "@phosphor-icons/react"; - -type StageKey = "files" | "checks" | "risks"; - -type Example = { - label: string; - issue: string; - keywords: string[]; - files: [string, string, string]; - checks: [string, string, string]; - risks: [string, string, string]; -}; - -const examples: Example[] = [ - { - label: "Expiring reset links", - issue: "Password reset links expire too early", - keywords: ["auth", "email", "expire", "link", "password", "reset", "token"], - files: ["src/features/auth/reset/request.ts", "src/features/auth/reset/token-service.ts", "src/lib/email/templates/reset.ts"], - checks: ["Token expiration logic", "Reset token integration tests", "Email link TTL configuration"], - risks: ["Clock and timezone boundary", "Cached authentication config", "Email client link prefetch"] - }, - { - label: "Wrong invoice time zone", - issue: "Invoices show the wrong time after daylight saving changes", - keywords: ["date", "daylight", "dst", "invoice", "time", "timezone"], - files: ["src/timezone/resolve.ts", "src/invoices/summary.ts", "src/timezone/index.ts"], - checks: ["Timezone conversion tests", "DST boundary cases", "Invoice rendering path"], - risks: ["Cached timezone offsets", "External API assumptions", "Off-by-one date handling"] - }, - { - label: "Duplicate webhook event", - issue: "Payment webhooks sometimes create duplicate orders", - keywords: ["duplicate", "idempotency", "order", "payment", "retry", "webhook"], - files: ["src/payments/webhook.ts", "src/orders/create-order.ts", "src/payments/idempotency.ts"], - checks: ["Webhook replay test", "Order idempotency suite", "Concurrent insert handling"], - risks: ["Retry timing window", "Missing unique constraint", "Out-of-order delivery"] +} from "@phosphor-icons/react/ssr"; +import { buildReportFromRepo } from "@aryam/fixmap-core/browser"; +import { sampleRepo } from "../sample-repo"; + +const task = "TOKEN_TTL_MINUTES is ignored and reset links expire immediately."; +const command = `fixmap plan --issue "${task}" --format agent`; +const report = buildReportFromRepo(sampleRepo, { issueText: task, limit: 3 }); + +function homepageExample() { + const editCandidate = report.contextFiles[0]; + const impactFile = (report.impact?.files ?? []).find((file) => !file.path.includes("test/")); + const testRoute = report.testRoutes[0]; + const risk = report.risks[0]; + + if (!editCandidate || !impactFile || !testRoute || !risk) { + throw new Error("The homepage example no longer contains the file, test, impact, and risk evidence it is designed to explain."); } -]; - -const firstExample = examples[0]!; -const genericWords = new Set([ - "a", "an", "and", "are", "be", "because", "broken", "bug", "create", "does", "error", "failed", "fails", "failure", - "fix", "for", "from", "in", "is", "issue", "it", "of", "on", "or", "problem", "sometimes", "the", "to", "when", "with", "wrong" -]); - -function issueWords(issue: string): string[] { - return [...new Set(issue.toLowerCase().match(/[a-z0-9]+/g) ?? [])] - .filter((word) => word.length > 2 && !genericWords.has(word)); -} - -function customResult(issue: string): Example { - const words = issueWords(issue); - const area = words[0]?.slice(0, 32) ?? "feature"; - const behavior = words[1]?.slice(0, 32) ?? "behavior"; - const title = `${area} ${behavior}`; - return { - label: "Custom issue preview", - issue, - keywords: words, - files: [`src/${area}/${behavior}.ts`, `src/${area}/index.ts`, `test/${area}/${behavior}.test.ts`], - checks: [`${title} behavior`, `${area} integration path`, `Regression for the reported issue`], - risks: ["Existing behavior compatibility", "Uncovered input boundary", "Related integration assumptions"] - }; -} -function resultForIssue(issue: string): { example: Example; index: number } { - const words = new Set(issueWords(issue)); - const ranked = examples - .map((example, index) => ({ example, index, score: example.keywords.filter((keyword) => words.has(keyword)).length })) - .sort((left, right) => right.score - left.score); - const match = ranked[0]; - return match && match.score > 0 ? match : { example: customResult(issue), index: -1 }; + return { editCandidate, impactFile, testRoute, risk }; } -const stageMeta: Array<{ key: StageKey; label: string; hint: string }> = [ - { key: "files", label: "Files", hint: "Finding relevant code" }, - { key: "checks", label: "Tests", hint: "Finding checks" }, - { key: "risks", label: "Risks", hint: "Reviewing impact" } -]; +const { editCandidate, impactFile, testRoute, risk } = homepageExample(); -function OutputCard({ - stage, - active, - ready, - items, - onSelect -}: { - stage: StageKey; - active: boolean; - ready: boolean; - items: [string, string, string]; - onSelect: () => void; -}) { - const config = { - files: { icon: FileText, title: "Files to open first", count: "7 files", metric: "Match", value: "Strong" }, - checks: { icon: CheckCircle, title: "Tests and checks", count: "8 checks", metric: "Support", value: "Found" }, - risks: { icon: Warning, title: "Risks worth reviewing", count: "6 risks", metric: "Impact", value: "Medium" } - }[stage]; - const Icon = config.icon; - - return ( - - ); -} +const candidateReason = + editCandidate.reasons.find((reason) => reason.startsWith("defines task identifiers")) ?? + editCandidate.reasons[0] ?? "ranked repository evidence"; +const impactReason = impactFile.evidence[0]?.reason ?? "related repository evidence"; export function InteractiveMapStage() { - const [exampleIndex, setExampleIndex] = useState(0); - const [issue, setIssue] = useState(firstExample.issue); - const [result, setResult] = useState(firstExample); - const [activeStage, setActiveStage] = useState("files"); - const [readyCount, setReadyCount] = useState(3); - const [isRunning, setIsRunning] = useState(false); - const timers = useRef([]); - - const clearTimers = () => { - timers.current.forEach(window.clearTimeout); - timers.current = []; - }; - - useEffect(() => clearTimers, []); - - const stageItems = useMemo( - () => ({ files: result.files, checks: result.checks, risks: result.risks }), - [result] - ); - - const runMap = () => { - clearTimers(); - const next = resultForIssue(issue); - setExampleIndex(next.index); - setResult(next.example); - setIsRunning(true); - setReadyCount(0); - setActiveStage("files"); - timers.current = [ - window.setTimeout(() => setReadyCount(1), 420), - window.setTimeout(() => { setReadyCount(2); setActiveStage("checks"); }, 980), - window.setTimeout(() => { setReadyCount(3); setActiveStage("risks"); }, 1540), - window.setTimeout(() => setIsRunning(false), 1880) - ]; - }; - - const chooseExample = (index: number) => { - const nextExample = examples[index]; - if (!nextExample) return; - clearTimers(); - setExampleIndex(index); - setIssue(nextExample.issue); - setResult(nextExample); - setReadyCount(3); - setActiveStage("files"); - setIsRunning(false); - }; - return ( -
-
- FixMap - - Runs here -
- -
-
-
1Describe what needs fixing
-
- -