Sign In

@gitwand/core

Package Overview
Dependencies
Maintainers
1
Versions
50
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@gitwand/core - npm Package Compare versions

Comparing version
2.8.2
to
2.8.4
+37
dist/__tests__/bench/congra-mini.test.d.ts
/**
* v2.5 — ConGra-mini regression bench.
*
* Loads ~15 hand-crafted `complex` conflicts under
* `__tests__/fixtures/congra-mini/`, feeds each to `resolveAsync()` with the
* LLM fallback enabled and a deterministic mock endpoint, then tallies the
* resolution outcome.
*
* ## Done criterion (CORE-V2-ROADMAP v2.5)
*
* > "résout au moins 80 % des hunks `complex` du ConGra-mini sans régression
* > sur le reste"
*
* The test fails if `successRate < 0.80`. We do NOT lower the threshold when
* a fixture regresses — that's the whole point of locking the bench. If a
* fixture becomes unrealistic for the deterministic+LLM pipeline, either
* fix the pipeline or replace the fixture; do not move the goalposts.
*
* ## Why `validationLevel: "off"`
*
* Tree-sitter grammars are not loaded in unit tests (the bench runs without
* `web-tree-sitter` peer or grammar WASMs). Parse-tree validation would
* therefore always return `null` and is irrelevant to what we're measuring
* here — namely, the LLM fallback's hit rate.
*
* ## Why a normal `describe` and not Vitest's `bench`
*
* We are measuring a hit rate, not throughput. A real bench (`bench(...)`)
* would re-run the same workload many times and report ops/s — useless here.
* The file is named `*.bench.ts` to keep it co-located with the other
* regression-style benches, but it lives in the standard test suite so CI
* runs it on every push.
*
* Use `SKIP_BENCH=true pnpm test` to skip the suite in fast iterations.
*/
export {};
//# sourceMappingURL=congra-mini.test.d.ts.map
{"version":3,"file":"congra-mini.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/bench/congra-mini.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG"}
/**
* v2.5 — ConGra-mini regression bench.
*
* Loads ~15 hand-crafted `complex` conflicts under
* `__tests__/fixtures/congra-mini/`, feeds each to `resolveAsync()` with the
* LLM fallback enabled and a deterministic mock endpoint, then tallies the
* resolution outcome.
*
* ## Done criterion (CORE-V2-ROADMAP v2.5)
*
* > "résout au moins 80 % des hunks `complex` du ConGra-mini sans régression
* > sur le reste"
*
* The test fails if `successRate < 0.80`. We do NOT lower the threshold when
* a fixture regresses — that's the whole point of locking the bench. If a
* fixture becomes unrealistic for the deterministic+LLM pipeline, either
* fix the pipeline or replace the fixture; do not move the goalposts.
*
* ## Why `validationLevel: "off"`
*
* Tree-sitter grammars are not loaded in unit tests (the bench runs without
* `web-tree-sitter` peer or grammar WASMs). Parse-tree validation would
* therefore always return `null` and is irrelevant to what we're measuring
* here — namely, the LLM fallback's hit rate.
*
* ## Why a normal `describe` and not Vitest's `bench`
*
* We are measuring a hit rate, not throughput. A real bench (`bench(...)`)
* would re-run the same workload many times and report ops/s — useless here.
* The file is named `*.bench.ts` to keep it co-located with the other
* regression-style benches, but it lives in the standard test suite so CI
* runs it on every push.
*
* Use `SKIP_BENCH=true pnpm test` to skip the suite in fast iterations.
*/
import { describe, it, expect } from "vitest";
import { readFileSync, readdirSync, statSync } from "node:fs";
import { join, dirname } from "node:path";
import { fileURLToPath } from "node:url";
import { resolveAsync } from "../../resolver/index.js";
import { buildMockEndpoint, fenced } from "../utils/mock-llm-endpoint.js";
const __dirname = dirname(fileURLToPath(import.meta.url));
const FIXTURES_ROOT = join(__dirname, "..", "fixtures", "congra-mini");
/**
* Extract a unique substring of the first conflict's `ours` block. We grab
* the first non-empty line after `<<<<<<< ours` — that's enough to
* disambiguate fixtures whose conflict markers are unique per fixture.
*/
function extractOursSnippet(conflict) {
const lines = conflict.split("\n");
const start = lines.findIndex((l) => l.startsWith("<<<<<<<"));
if (start === -1)
return "";
const end = lines.findIndex((l, i) => i > start && (l.startsWith("|||||||") || l.startsWith("=======")));
const ours = lines.slice(start + 1, end === -1 ? lines.length : end);
return ours.find((l) => l.trim().length > 0) ?? ours.join("\n");
}
function loadFixtures() {
const entries = readdirSync(FIXTURES_ROOT).filter((n) => {
const p = join(FIXTURES_ROOT, n);
return statSync(p).isDirectory();
});
return entries.sort().map((name) => {
const dir = join(FIXTURES_ROOT, name);
const conflict = readFileSync(join(dir, "conflict.txt"), "utf-8");
const expectedResolution = readFileSync(join(dir, "expected-llm-resolution.txt"), "utf-8");
const meta = JSON.parse(readFileSync(join(dir, "meta.json"), "utf-8"));
return {
name,
conflict,
expectedResolution,
meta,
oursSnippet: extractOursSnippet(conflict),
};
});
}
function classifyOutcome(fixture, result) {
const firstHunk = result.hunks[0];
const firstResolution = result.resolutions[0];
if (!firstHunk || !firstResolution) {
return {
fixture,
outcome: "unresolved",
decisionType: "no-hunk",
reason: "no conflict parsed",
};
}
// Resolved by the LLM fallback → success.
if (firstHunk.type === "llm_proposed" && firstResolution.autoResolved) {
return {
fixture,
outcome: "llm-resolved",
decisionType: firstHunk.type,
validationScore: firstHunk.trace.llmTrace?.validationScore,
reason: firstResolution.resolutionReason,
};
}
// Resolved by a deterministic pattern before the LLM ran → does NOT count
// toward LLM success (the fixture was supposed to be `complex`).
if (firstResolution.autoResolved) {
return {
fixture,
outcome: "deterministic-resolved",
decisionType: firstHunk.type,
reason: firstResolution.resolutionReason,
};
}
return {
fixture,
outcome: "unresolved",
decisionType: firstHunk.type,
validationScore: firstHunk.trace.llmTrace?.validationScore,
reason: firstResolution.resolutionReason,
};
}
// ─── Suite ───────────────────────────────────────────────
const SKIP = process.env.SKIP_BENCH === "true";
describe.skipIf(SKIP)("ConGra-mini — LLM fallback regression bench", () => {
const fixtures = loadFixtures();
it("loads exactly 15 fixtures", () => {
expect(fixtures.length).toBe(15);
});
it("each fixture parses one or more conflict hunks", () => {
for (const f of fixtures) {
expect(f.conflict).toContain("<<<<<<<");
expect(f.conflict).toContain("=======");
expect(f.conflict).toContain(">>>>>>>");
expect(f.oursSnippet.length).toBeGreaterThan(0);
}
});
it("resolves ≥ 80 % of complex hunks via the LLM fallback", async () => {
// Build a single mock endpoint that knows every fixture's expected
// resolution. The endpoint matches by ours-snippet substring (see
// mock-llm-endpoint.ts).
const responses = new Map();
for (const f of fixtures) {
responses.set(f.oursSnippet, fenced(f.expectedResolution));
}
const endpoint = buildMockEndpoint(responses);
const results = [];
for (const f of fixtures) {
const merge = await resolveAsync(f.conflict, f.meta.filePath, {
llmFallback: {
enabled: true,
endpoint,
minPostMergeScore: 80,
contextLines: 50,
},
// Tree-sitter is unavailable in unit-test env — disable parse-tree
// validation. The LLM resolver still runs `validateMergedContent`
// (residual markers + JSON/YAML/TOML syntax).
validationLevel: "off",
});
results.push(classifyOutcome(f, merge));
}
// Per-fixture log (one line each) + summary.
const lines = [];
for (const r of results) {
const symbol = r.outcome === "llm-resolved" ? "✓"
: r.outcome === "deterministic-resolved" ? "·"
: "✗";
const score = r.validationScore !== undefined ? ` score=${r.validationScore}` : "";
lines.push(` ${symbol} [${r.fixture.meta.category}/${r.fixture.meta.difficulty}] ${r.fixture.name} → ${r.decisionType}${score}`);
}
const llmResolved = results.filter((r) => r.outcome === "llm-resolved").length;
const deterministic = results.filter((r) => r.outcome === "deterministic-resolved").length;
const unresolved = results.filter((r) => r.outcome === "unresolved").length;
const total = results.length;
const successRate = llmResolved / total;
const summary = [
"",
"ConGra-mini bench summary:",
...lines,
"",
` Total fixtures : ${total}`,
` LLM-resolved : ${llmResolved} (${(successRate * 100).toFixed(1)} %)`,
` Deterministic-resolved: ${deterministic} (regressions — expected to be complex)`,
` Unresolved : ${unresolved}`,
` Target : ≥ 80 % LLM-resolved`,
"",
].join("\n");
// eslint-disable-next-line no-console
console.log(summary);
expect(successRate, `LLM fallback resolved ${llmResolved}/${total} = ${(successRate * 100).toFixed(1)} % (target ≥ 80 %).\n${summary}`).toBeGreaterThanOrEqual(0.8);
});
});
//# sourceMappingURL=congra-mini.test.js.map
{"version":3,"file":"congra-mini.test.js","sourceRoot":"","sources":["../../../src/__tests__/bench/congra-mini.test.ts"],"names":[],"mappings":"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"}
/**
* v2.5 — Mock LLM endpoint for deterministic bench / integration tests.
*
* `@gitwand/core` never makes a network call: it consumes an injected
* `LlmEndpoint` whose `call(prompt)` returns the model output as a string.
* For tests, we want a reproducible endpoint that returns a pre-recorded
* response for each fixture without any randomness.
*
* ## Lookup strategy
*
* The pipeline serialises the conflict hunk (ours / base / theirs) verbatim
* inside the prompt — so we can match a fixture by searching the prompt for
* a unique substring. Two keys are supported, in order:
*
* 1. A literal `[FIXTURE: <name>]` marker injected by the caller (used by
* future prompt builders that want explicit tagging).
* 2. The `ours` snippet of the fixture — the prompt always contains the
* ours block verbatim between `<<<<<<< ours` and `||||||| base` (or
* `=======` for diff2). A unique substring of `oursLines` is therefore
* enough to disambiguate.
*
* If neither match yields a hit, the endpoint returns the empty string —
* which the pipeline treats as `CANNOT_RESOLVE` (lines: null, rejected).
* This is the expected behaviour for "the LLM has nothing useful to say".
*/
import type { LlmEndpoint } from "../../types.js";
/**
* Build a deterministic mock endpoint that returns pre-recorded responses
* keyed by a unique substring of the prompt.
*
* @param responses - Map of `lookupKey → rawResponseBody`. The raw response
* is returned as-is to the pipeline, which then parses
* fenced blocks / detects `CANNOT_RESOLVE` / etc.
* Wrap the resolution in triple backticks like a real
* LLM would.
*/
export declare function buildMockEndpoint(responses: Map<string, string>): LlmEndpoint;
/**
* Wrap a raw resolution body in a fenced code block, matching what a real
* LLM would output. The pipeline's `parseResponse()` extracts the first
* fenced block and uses it as the resolved lines.
*/
export declare function fenced(body: string): string;
//# sourceMappingURL=mock-llm-endpoint.d.ts.map
{"version":3,"file":"mock-llm-endpoint.d.ts","sourceRoot":"","sources":["../../../src/__tests__/utils/mock-llm-endpoint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAC7B,WAAW,CAsBb;AAED;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE3C"}
/**
* v2.5 — Mock LLM endpoint for deterministic bench / integration tests.
*
* `@gitwand/core` never makes a network call: it consumes an injected
* `LlmEndpoint` whose `call(prompt)` returns the model output as a string.
* For tests, we want a reproducible endpoint that returns a pre-recorded
* response for each fixture without any randomness.
*
* ## Lookup strategy
*
* The pipeline serialises the conflict hunk (ours / base / theirs) verbatim
* inside the prompt — so we can match a fixture by searching the prompt for
* a unique substring. Two keys are supported, in order:
*
* 1. A literal `[FIXTURE: <name>]` marker injected by the caller (used by
* future prompt builders that want explicit tagging).
* 2. The `ours` snippet of the fixture — the prompt always contains the
* ours block verbatim between `<<<<<<< ours` and `||||||| base` (or
* `=======` for diff2). A unique substring of `oursLines` is therefore
* enough to disambiguate.
*
* If neither match yields a hit, the endpoint returns the empty string —
* which the pipeline treats as `CANNOT_RESOLVE` (lines: null, rejected).
* This is the expected behaviour for "the LLM has nothing useful to say".
*/
/**
* Build a deterministic mock endpoint that returns pre-recorded responses
* keyed by a unique substring of the prompt.
*
* @param responses - Map of `lookupKey → rawResponseBody`. The raw response
* is returned as-is to the pipeline, which then parses
* fenced blocks / detects `CANNOT_RESOLVE` / etc.
* Wrap the resolution in triple backticks like a real
* LLM would.
*/
export function buildMockEndpoint(responses) {
return {
async call(prompt) {
// First pass — explicit fixture tag (future-proof).
const tagMatch = prompt.match(/\[FIXTURE: ([^\]]+)\]/);
if (tagMatch) {
const direct = responses.get(tagMatch[1]);
if (direct !== undefined)
return direct;
}
// Second pass — first key whose substring appears in the prompt wins.
// The fixture loader stores the ours-snippet as the key, which the
// pipeline serialises verbatim into the conflict block.
for (const [key, response] of responses) {
if (key && prompt.includes(key))
return response;
}
// No match — return empty string. The pipeline treats this as
// CANNOT_RESOLVE, leaving the hunk unresolved (audit trail kept).
return "";
},
};
}
/**
* Wrap a raw resolution body in a fenced code block, matching what a real
* LLM would output. The pipeline's `parseResponse()` extracts the first
* fenced block and uses it as the resolved lines.
*/
export function fenced(body) {
return "```\n" + body + "\n```";
}
//# sourceMappingURL=mock-llm-endpoint.js.map
{"version":3,"file":"mock-llm-endpoint.js","sourceRoot":"","sources":["../../../src/__tests__/utils/mock-llm-endpoint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAIH;;;;;;;;;GASG;AACH,MAAM,UAAU,iBAAiB,CAC/B,SAA8B;IAE9B,OAAO;QACL,KAAK,CAAC,IAAI,CAAC,MAAc;YACvB,oDAAoD;YACpD,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC;YACvD,IAAI,QAAQ,EAAE,CAAC;gBACb,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC1C,IAAI,MAAM,KAAK,SAAS;oBAAE,OAAO,MAAM,CAAC;YAC1C,CAAC;YAED,sEAAsE;YACtE,mEAAmE;YACnE,wDAAwD;YACxD,KAAK,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,IAAI,SAAS,EAAE,CAAC;gBACxC,IAAI,GAAG,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC;oBAAE,OAAO,QAAQ,CAAC;YACnD,CAAC;YAED,8DAA8D;YAC9D,kEAAkE;YAClE,OAAO,EAAE,CAAC;QACZ,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,MAAM,CAAC,IAAY;IACjC,OAAO,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC;AAClC,CAAC"}
+1
-1
{
"name": "@gitwand/core",
"version": "2.8.2",
"version": "2.8.4",
"description": "GitWand core — automatic Git conflict resolution engine (powers @gitwand/cli, @gitwand/mcp, and the GitWand desktop app)",

@@ -5,0 +5,0 @@ "type": "module",