+ Generated by the real browser-safe FixMap engine against the sample repository used in the live demo.
+
+
);
}
diff --git a/apps/web/app/changelog/page.tsx b/apps/web/app/changelog/page.tsx
index 50af597..6e735c0 100644
--- a/apps/web/app/changelog/page.tsx
+++ b/apps/web/app/changelog/page.tsx
@@ -58,7 +58,7 @@ const releases: Release[] = [
"fixmap benchmark compares BM25, FixMap, and Impact Graph on identical historical parent snapshots without executing repository code.",
"fixmap watch streams working-tree drift findings and recalculated impact as an agent edits, with Markdown or JSON Lines output.",
"Compact agent output provides a stable edit, inspect, test, risk, avoid, and uncertainty handoff.",
- "A 32-second motion-first comparison shows two agents handling the same issue, with an original no-vocals soundtrack and no unsupported savings claim."
+ "A 32-second motion-first comparison shows two agents handling the same issue, with an original no-vocals soundtrack and no claim of measured agent efficiency."
]
},
{
@@ -109,7 +109,7 @@ const releases: Release[] = [
label: "Evidence",
items: [
"The release ledger maps 126 GitHub issues plus the complete attached 30-finding review to fixes and verification.",
- "External, held-out, adversarial, baseline, savings, package, generated-output, and 1,000-file scan gates are recorded and reproducible."
+ "External, held-out, adversarial, baseline, context-size proxy, scan-performance, package, generated-output, and 1,000-file scan gates are recorded and reproducible."
]
}
]
diff --git a/apps/web/app/evidence/page.tsx b/apps/web/app/evidence/page.tsx
index 24c72c5..cd66ee4 100644
--- a/apps/web/app/evidence/page.tsx
+++ b/apps/web/app/evidence/page.tsx
@@ -45,6 +45,18 @@ export default function EvidencePage() {
Three different questions need three different answers.
+
Retrieval measurements can show whether FixMap surfaced a known fixing file. They cannot, by themselves, show whether a coding agent used fewer tokens or completed the task better.
+
+
+ Measured
Repository retrieval, calibration, adversarial behavior, and scan time
The checked-in suites below contain the cases, baselines, misses, and confidence intervals.
+ Mechanism
Focused context, test routes, impact candidates, and explicit uncertainty
These are product behaviors produced from repository evidence. They are not downstream outcome claims.
+ Not yet measured
Agent tokens, cost, time, tool calls, and task success
The controlled protocol and evaluator are ready for real runs; no result is published until a complete run set exists.
For developers and coding agents · FixMap v{siteStats.version}
-
Stop searching the repository blindly.
+
Repository context for coding agents · FixMap v{siteStats.version}
+
Give your coding agent a map before it edits.
- Give FixMap a task. It shows you and your coding agent the files to inspect, the tests
- to run, and the risks to review before anything is edited.
+ Give FixMap a coding task and a repository. It returns focused files to inspect,
+ relevant tests and checks, and nearby impact or risks—before the agent starts changing code.
Median time to scan and rank the sample repositories
{siteStats.medianSeconds}s
-
+
+
+ Measured
+ {count(heldoutUnmentioned.top3HitRate, heldoutUnmentioned.cases)}/{heldoutUnmentioned.cases} held-out tasks surfaced the fixing file in the top 3.
+
BM25 over the same code corpus tied Top-3 and reached {count(heldoutBaseline.bm25.top5HitRate, heldoutBaseline.bm25.cases)}/{heldoutBaseline.bm25.cases} at Top-5 versus {count(heldoutBaseline.fixmap.top5HitRate, heldoutBaseline.fixmap.cases)}/{heldoutBaseline.fixmap.cases} for FixMap.
Its evidence comes from repository paths, symbols, imports, related tests, and bounded Git co-change history.
+
+
+ Not yet measured
+ Token, cost, time, tool-call, and task-success improvements.
+
A controlled agent-study protocol exists, but there are no completed runs to support those outcome claims.
+
+
@@ -111,6 +115,21 @@ export default function HomePage() {
+
+
+
Optional overview
+
See FixMap in a full workflow.
+
A short walkthrough from task to focused repository context.
+
+
+ Watch the 32-second overview
+
+
+
+
Ready to give the next change a better start?Try it once or install it for your coding agent.
diff --git a/apps/web/app/product/page.tsx b/apps/web/app/product/page.tsx
index 86ef989..f7aa9b2 100644
--- a/apps/web/app/product/page.tsx
+++ b/apps/web/app/product/page.tsx
@@ -73,7 +73,7 @@ const stages = [
},
{
id: "verify",
- number: "07",
+ number: "06",
icon: GitDiff,
eyebrow: "After the edit",
title: "Verify: compare the plan with the real change.",
@@ -157,7 +157,7 @@ export default function ProductPage() {
The boundary matters
A starting map, not proof.
-
FixMap can help an agent search less and review more deliberately. It cannot prove a change is correct, complete, or safe.
+
FixMap gives an agent a focused, evidence-backed starting set for more deliberate review. It cannot prove a change is correct, complete, or safe.
That is why every score has reasons, Verify is mostly advisory, and the benchmark page publishes failures alongside hits.
See the evidence
diff --git a/apps/web/app/sitemap.ts b/apps/web/app/sitemap.ts
index 5505ac3..3c39cc0 100644
--- a/apps/web/app/sitemap.ts
+++ b/apps/web/app/sitemap.ts
@@ -3,17 +3,7 @@ import type { MetadataRoute } from "next";
const base = "https://usefixmap.vercel.app";
export default function sitemap(): MetadataRoute.Sitemap {
- const lastModifiedByPath: Record = {
- "": new Date("2026-08-09T00:00:00.000Z"),
- "/product": new Date("2026-08-09T00:00:00.000Z"),
- "/demo": new Date("2026-08-09T00:00:00.000Z"),
- "/evidence": new Date("2026-08-09T00:00:00.000Z"),
- "/changelog": new Date("2026-08-09T00:00:00.000Z"),
- "/get-started": new Date("2026-08-09T00:00:00.000Z"),
- "/docs": new Date("2026-08-09T00:00:00.000Z")
- };
return ["", "/product", "/demo", "/evidence", "/changelog", "/get-started", "/docs"].map((path) => ({
- url: `${base}${path}`,
- lastModified: lastModifiedByPath[path]
+ url: `${base}${path}`
}));
}
diff --git a/benchmarks/agent-study/protocol.json b/benchmarks/agent-study/protocol.json
index 988728a..8616463 100644
--- a/benchmarks/agent-study/protocol.json
+++ b/benchmarks/agent-study/protocol.json
@@ -1,5 +1,5 @@
{
- "protocolVersion": 1,
+ "protocolVersion": 2,
"status": "protocol-only",
"suite": "navigation-focused pinned tasks",
"arms": [
@@ -16,9 +16,15 @@
"randomizedArmOrder": true,
"fixedTimeoutAndBudget": true,
"noFixMapChangesMidStudy": true,
- "rawTranscriptsRequired": true
+ "rawTranscriptsRequired": true,
+ "taskSuccessRubricFrozen": true,
+ "tokenAccountingSourceRecorded": true,
+ "costRateCardRecorded": true,
+ "failuresRetained": true
},
"metrics": [
+ "runStatus",
+ "failureReason",
"taskResolved",
"correctFileInFirstThreeOpened",
"toolCallsToFirstRelevantFile",
@@ -26,7 +32,16 @@
"incorrectFilesEdited",
"totalToolCalls",
"inputTokens",
+ "cachedInputTokens",
"outputTokens",
+ "reasoningTokens",
+ "totalTokens",
+ "modelCostUsd",
+ "turns",
+ "repositorySearchCalls",
+ "filesRead",
+ "sourceBytesRead",
+ "wallClockMs",
"testsSelectedCorrectly",
"finalPatchAccepted",
"fixmapPlanUsed",
diff --git a/docs/AGENT_STUDY.md b/docs/AGENT_STUDY.md
index 1c8084c..2a86ef5 100644
--- a/docs/AGENT_STUDY.md
+++ b/docs/AGENT_STUDY.md
@@ -1,6 +1,6 @@
# FixMap differential agent study
-FixMap 0.9 includes a frozen, machine-checked four-arm protocol. It does **not** publish an
+FixMap includes a frozen, machine-checked four-arm protocol. It does **not** publish an
effectiveness percentage until complete raw runs exist for every task and arm.
The arms are:
@@ -12,8 +12,20 @@ The arms are:
Every paired task must use the same model and model version, task text, repository revision,
timeout, and budget. Each run starts with fresh context, arm order is randomized, FixMap is
-frozen during the study, and the raw transcript is retained. The primary navigation metric is
-tool calls to the first relevant file, not turns to the first edit.
+frozen during the study, the task-success rubric and price sheet are recorded before execution,
+and the raw transcript is retained. Failed and timed-out runs stay in the dataset. The primary
+navigation metric is tool calls to the first relevant file, not turns to the first edit.
+
+Each JSONL row records provider-reported input, cached-input, output, reasoning, and total tokens
+when the provider exposes them; model cost in USD under the frozen price sheet; turns; total tool
+calls; repository-search calls; files and source bytes read; wall-clock time; correctness; test
+selection; and failure status. Unsupported provider counters are `null`, never estimated from
+characters or repository size.
+
+The row also pins `taskId`, `taskTextSha256`, `arm`, randomized `runOrder`, model and version,
+repository and revision, `environmentId`, `timeoutMs`, `budgetId`, `priceSheetId`, and transcript
+reference plus SHA-256. Those controls let the evaluator reject a comparison where more than the
+experimental arm changed.
Validate the protocol without claiming a result:
@@ -28,6 +40,11 @@ per line outside the repository and evaluate it explicitly:
node scripts/evaluate-agent-study.mjs --input path/to/runs.jsonl
```
+The evaluator rejects incomplete task/arm sets, duplicate runs, invalid metric types, mismatched
+models or repository revisions, missing transcript hashes, inconsistent task controls, and
+unaccounted failures. Its summary reports quality and efficiency together; a cheaper run is not a
+win if task resolution or patch acceptance falls.
+
Run data is deliberately not checked in by default: transcripts can contain source, prompts,
and account metadata. Any public study must use consented, reviewed, redacted artifacts and link
the exact model/version and frozen task-selection record.
diff --git a/scripts/evaluate-agent-study.mjs b/scripts/evaluate-agent-study.mjs
index 60cd3cc..518d0ad 100644
--- a/scripts/evaluate-agent-study.mjs
+++ b/scripts/evaluate-agent-study.mjs
@@ -7,12 +7,31 @@ const protocolPath = join(root, "benchmarks", "agent-study", "protocol.json");
const protocol = JSON.parse(await readFile(protocolPath, "utf8"));
const requiredArms = ["baseline", "fixmap-available", "fixmap-instructed", "fixmap-impact"];
const requiredMetrics = [
+ "runStatus", "failureReason",
"taskResolved", "correctFileInFirstThreeOpened", "toolCallsToFirstRelevantFile",
"filesOpenedBeforeFirstEdit", "incorrectFilesEdited", "totalToolCalls", "inputTokens",
- "outputTokens", "testsSelectedCorrectly", "finalPatchAccepted", "fixmapPlanUsed", "verifyUsefulWarnings"
+ "cachedInputTokens", "outputTokens", "reasoningTokens", "totalTokens", "modelCostUsd",
+ "turns", "repositorySearchCalls", "filesRead", "sourceBytesRead", "wallClockMs",
+ "testsSelectedCorrectly", "finalPatchAccepted", "fixmapPlanUsed", "verifyUsefulWarnings"
];
+const identityFields = [
+ "taskId", "taskTextSha256", "arm", "runOrder", "model", "modelVersion", "repository",
+ "revision", "environmentId", "timeoutMs", "budgetId", "priceSheetId", "transcript",
+ "transcriptSha256"
+];
+const booleanMetrics = [
+ "taskResolved", "correctFileInFirstThreeOpened", "testsSelectedCorrectly",
+ "finalPatchAccepted", "fixmapPlanUsed", "verifyUsefulWarnings"
+];
+const numericMetrics = [
+ "toolCallsToFirstRelevantFile", "filesOpenedBeforeFirstEdit", "incorrectFilesEdited",
+ "totalToolCalls", "inputTokens", "cachedInputTokens", "outputTokens", "reasoningTokens",
+ "totalTokens", "modelCostUsd", "turns", "repositorySearchCalls", "filesRead",
+ "sourceBytesRead", "wallClockMs"
+];
+const runStatuses = ["completed", "failed", "timed-out"];
-if (protocol.protocolVersion !== 1 || protocol.status !== "protocol-only" ||
+if (protocol.protocolVersion !== 2 || protocol.status !== "protocol-only" ||
JSON.stringify(protocol.arms) !== JSON.stringify(requiredArms) ||
requiredMetrics.some((metric) => !protocol.metrics.includes(metric)) ||
Object.values(protocol.requirements).some((value) => value !== true)) {
@@ -34,11 +53,27 @@ if (rows.length === 0) throw new Error("Agent-study input contains no runs.");
const keys = new Set();
for (const [index, row] of rows.entries()) {
- for (const field of ["taskId", "arm", "model", "modelVersion", "repository", "revision", "transcript", ...requiredMetrics]) {
+ for (const field of [...identityFields, ...requiredMetrics]) {
if (!(field in row)) throw new Error(`Run ${index + 1} is missing ${field}.`);
}
if (!requiredArms.includes(row.arm)) throw new Error(`Run ${index + 1} has unknown arm ${JSON.stringify(row.arm)}.`);
if (typeof row.transcript !== "string" || !row.transcript.trim()) throw new Error(`Run ${index + 1} has no transcript reference.`);
+ if (!/^[a-f0-9]{64}$/i.test(row.taskTextSha256)) throw new Error(`Run ${index + 1} has an invalid taskTextSha256.`);
+ if (!/^[a-f0-9]{64}$/i.test(row.transcriptSha256)) throw new Error(`Run ${index + 1} has an invalid transcriptSha256.`);
+ if (!runStatuses.includes(row.runStatus)) throw new Error(`Run ${index + 1} has invalid runStatus ${JSON.stringify(row.runStatus)}.`);
+ if (!Number.isInteger(row.runOrder) || row.runOrder < 1 || row.runOrder > requiredArms.length) throw new Error(`Run ${index + 1} has an invalid runOrder.`);
+ if (!Number.isFinite(row.timeoutMs) || row.timeoutMs <= 0) throw new Error(`Run ${index + 1} has an invalid timeoutMs.`);
+ if (row.runStatus === "completed" && row.failureReason !== null) throw new Error(`Run ${index + 1} completed but has a failureReason.`);
+ if (row.runStatus !== "completed" && (typeof row.failureReason !== "string" || !row.failureReason.trim())) throw new Error(`Run ${index + 1} failed without a failureReason.`);
+ for (const metric of booleanMetrics) {
+ if (row[metric] !== true && row[metric] !== false && row[metric] !== null) throw new Error(`Run ${index + 1} has invalid ${metric}; expected boolean or null.`);
+ }
+ for (const metric of numericMetrics) {
+ if (row[metric] !== null && (!Number.isFinite(row[metric]) || row[metric] < 0)) throw new Error(`Run ${index + 1} has invalid ${metric}; expected a non-negative number or null.`);
+ }
+ if (row.totalTokens !== null && [row.inputTokens, row.outputTokens].every(Number.isFinite) && row.totalTokens < row.inputTokens + row.outputTokens) {
+ throw new Error(`Run ${index + 1} reports totalTokens below inputTokens + outputTokens.`);
+ }
const key = `${row.taskId}\0${row.arm}`;
if (keys.has(key)) throw new Error(`Duplicate task/arm run: ${row.taskId} / ${row.arm}.`);
keys.add(key);
@@ -51,7 +86,8 @@ for (const task of tasks) {
if (JSON.stringify(arms) !== JSON.stringify([...requiredArms].sort())) {
throw new Error(`Task ${task} does not contain exactly one run from every arm.`);
}
- for (const field of ["model", "modelVersion", "repository", "revision"]) {
+ if (new Set(taskRows.map((row) => row.runOrder)).size !== requiredArms.length) throw new Error(`Task ${task} does not contain a unique randomized runOrder for every arm.`);
+ for (const field of ["taskTextSha256", "model", "modelVersion", "repository", "revision", "environmentId", "timeoutMs", "budgetId", "priceSheetId"]) {
if (new Set(taskRows.map((row) => row[field])).size !== 1) throw new Error(`Task ${task} does not hold ${field} constant.`);
}
}
@@ -67,11 +103,24 @@ const aggregate = Object.fromEntries(requiredArms.map((arm) => {
};
return [arm, {
runs: armRows.length,
+ completedRuns: armRows.filter((row) => row.runStatus === "completed").length,
+ failedRuns: armRows.filter((row) => row.runStatus !== "completed").length,
taskResolutionRate: rate("taskResolved"),
acceptedPatchRate: rate("finalPatchAccepted"),
firstThreeFileRate: rate("correctFileInFirstThreeOpened"),
medianToolCallsToRelevantFile: median("toolCallsToFirstRelevantFile"),
- medianTotalToolCalls: median("totalToolCalls")
+ medianTotalToolCalls: median("totalToolCalls"),
+ medianRepositorySearchCalls: median("repositorySearchCalls"),
+ medianFilesRead: median("filesRead"),
+ medianSourceBytesRead: median("sourceBytesRead"),
+ medianInputTokens: median("inputTokens"),
+ medianCachedInputTokens: median("cachedInputTokens"),
+ medianOutputTokens: median("outputTokens"),
+ medianReasoningTokens: median("reasoningTokens"),
+ medianTotalTokens: median("totalTokens"),
+ medianModelCostUsd: median("modelCostUsd"),
+ medianTurns: median("turns"),
+ medianWallClockMs: median("wallClockMs")
}];
}));
-process.stdout.write(`${JSON.stringify({ protocolVersion: 1, tasks: tasks.length, aggregate }, null, 2)}\n`);
+process.stdout.write(`${JSON.stringify({ protocolVersion: 2, tasks: tasks.length, aggregate }, null, 2)}\n`);