Moteur de conflits : accord mesuré avec le merge humain (lots 1/C/E/F/G/D) - #170
Merged
Conversation
…ecline on generated files Lot 1 of docs/superpowers/specs/2026-08-26-conflict-engine-accuracy.md — the three changes that stop the engine being confidently wrong, measured against what teams actually committed (benchmark/). A — classifier contract. A complex hunk resolved by a format-aware resolver is reclassified `format_semantic` (new ConflictType), keeping the classifier's dimensions and boosters and adding the resolver's, then submitted to the same confidence threshold and policy gates as every pattern (semantic merges combine both sides — policies that exclude non_overlapping exclude them too, like the imports resolver already did). A non-complex hunk applied via a format resolver keeps its type but its confidence now records the semantic validation instead of silently bypassing the threshold. No file can come back fully resolved while reporting `complex` hunks — on laravel that was 116 of 325 files, applied with no confidence score and no trace. B — format invariants. validateMergedContent now checks what syntax cannot see: a changelog with two Unreleased sections or duplicated version headings, a JSON object with duplicate keys (scanner is string-safe and per-object; YAML dups were already caught by the yaml parser). Violations retract every auto-applied hunk in the file, same mechanism as the v2.4 parse-tree retraction. D (interim) — generated files decline by default. Their committed version is a tool's output, not a merge of two texts: "accept theirs" and semantic lockfile merges both diverged from what teams shipped in ~100% of measured cases. The engine now declines with an actionable reason (resolve the source, re-run the installer/build); only patterns that fabricate nothing (same_change, one_side_change, delete_no_change, whitespace_only) still apply. The historical behaviour sits behind a new option, resolveGeneratedFiles (default false), and generated_file moves from the "trivial" tier to "unresolved". Measured before → after on the pinned corpus (files resolved end-to-end, byte-identical to the human merge): laravel/framework 325 files 24.3% → 216 files 36.6% prettier/prettier 179 files 25.3% → 100 files 45.0% vuejs/core 226 files 92.5% → 220 files 95.0% expressjs/express 47 files 59.6% → 49 files 59.2% Coverage drops by design — those resolutions were ones the product's own contract said it should not have made. The remaining laravel disagreements are now Application.php version scalars and composer.json — exactly the merge- context problem (direction-aware value_only_change), which is lot C. Tests: 1078 passing. Lockfile/value_only suites exercise their resolvers behind the opt-in; corpus fixtures F11/F13/F14 now expect decline; the golden-funnel snapshot was regenerated on purpose (complex 20 → 14, format_semantic 6, autoResolved 31 → 28 on the fixture corpus). Not in this lot: desktop/CLI surfacing of resolveGeneratedFiles (settings + 5 locales), and website/reference/config.md documentation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LQoTe6RE4JpoQknCASS3hu
The engine-level default (generated files decline) needs a switch at the right scope. Whether a team regenerates or merges its lockfiles is a repository convention, not an app preference — so the opt-in lives in .gitwandrc (resolveGeneratedFiles: true, parsed strictly as a boolean), flows to the desktop through the existing rc loading in useGitWand, and gets a CLI flag (--resolve-generated) for hooks and CI. No SettingsPanel toggle on purpose. reference/config.md gains a Generated Files section: what is detected, why the default declines (with the benchmark link), which fabricate-nothing patterns still apply, and both opt-in forms. cli: 34 tests green; core tsc + desktop vue-tsc clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LQoTe6RE4JpoQknCASS3hu
The largest measured source of disagreement left after lot 1 is value_only_change guessing 'newer semver wins' on back-merges (laravel: ~112 wrong resolutions; the human answer is always the target branch's value). The engine can't know that without knowing what merge it is in — this plan adds an optional MergeContext to GitWandOptions, detection helpers on the callers' side (CLI/MCP read .git state, the desktop already knows its operation), the target-wins rule for version-like scalars, and demotes the context-less version guess from auto-apply to propose. Task 5 is the gate: the benchmark re-run must show agreement improving on at least two repos before the desktop wiring ships. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LQoTe6RE4JpoQknCASS3hu
…what merge it is in (lot C)
New optional GitWandOptions.mergeContext ({ operation, targetSide, oursRef?,
theirsRef? }), plain data, detected by the callers and echoed in traces — the
core stays a pure function.
The rule, refined once by the benchmark itself:
- An UNORDERABLE version pair ('13.x-dev' vs '12.54.1' — the file's version
identity) resolves to the target side when context is present, and is
proposed instead of applied when it is not. The old path fell back to
prefer-theirs: a coin flip measured wrong ~3 times out of 4.
- ORDERABLE semver pairs keep "newest wins" even with context. The first
version of this rule sent them to the target too, and agreement regressed on
prettier (45.0 → 39.0), vue and express — teams do take the newer dependency
brought by the source branch. The gate in the plan (task 5) caught it before
anything shipped.
Measured, same pinned corpus, files byte-identical to the human merge:
laravel/framework 36.6% → 81.9% (the Application.php class of failures)
prettier/prettier 45.0% → 45.0%
expressjs/express 59.2% → 59.2%
vuejs/core 95.0% → 90.0%*
* denominator artefact, not a regression: a per-file flip scan found ZERO
files where the previous engine agreed and this one doesn't. Fixing the
version hunk pulls previously-excluded files into the comparable set,
where other hunks disagree — all 11 in one merge, dominated by a
workspace:* migration done by hand during the merge.
Detection: detectMergeContext() reads .git state (MERGE_HEAD, rebase-merge/
rebase-apply + head-name, CHERRY_PICK_HEAD, REVERT_HEAD), covers linked
worktrees, returns null when nothing is in progress. Implemented in the CLI
(tested on real temp repos: merge, rebase, cherry-pick, worktree, clean, non-
repo) and duplicated in the MCP on purpose — mcp must not depend on cli, and
core stays Node-free. targetSide is declared by the caller in all cases, so
the engine never re-derives the rebase ours/theirs inversion; rebase-merge/
head-name is the branch being REBASED (theirs), which the desktop mapping now
gets right against its own misleading field name.
Wired: CLI resolve (flag-free, plus a verbose context line), the three MCP
resolve/preview sites, and the desktop batch loader via the existing
git_repo_state command. Tests: core 1084, cli 40, mcp 12, desktop 1058 across
4 shards — all green.
benchmark/README gains the three-state impact table and the refinement story;
plan tasks 1–5 checked, task 6 (corpus fixtures, golden funnel, site docs,
changelog) remains.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LQoTe6RE4JpoQknCASS3hu
Corpus gains two context-dependent fixtures — F47, the laravel back-merge shape resolved to the target when context is present, and F48, the same conflict without context, pinned as proposed-never-applied. Golden funnel regenerated for the two new fixtures (46 → 48, autoResolved 28 → 29). reference/config.md documents mergeContext (auto-detected by all three frontends; API consumers can pass it), and the CHANGELOG's Unreleased section tells the whole accuracy arc: format_semantic, invariants, generated files declining, merge context, and the benchmark that drove — and once corrected — those rules. Core suite: 1086 green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LQoTe6RE4JpoQknCASS3hu
Real package.json / composer.json conflicts are fragments — a few
'"key": value,' lines mid-object — so the full-document JSON resolver never
fired on them and the textual engine merged them line by line: exactly the
wrong granularity, measured at 48-67% agreement.
New resolvers/json-fragment.ts, hooked as the fallback of the dispatcher's
JSON branch: parse each side as simple entries (anything else — nested
multi-line values, blank lines, duplicate keys — declines rather than
guesses), then three-way merge by key. One-sided changes and deletions
resolve; both-sides-different gets a single bounded arbitration: two version
constraints on the SAME operator ('^7.23.0' vs '^7.23.3') resolve to the
newer — what teams ship, per the corpus. Operator changes and workspace:*
migrations are human decisions and fall through. Output preserves each
winning line's original formatting, keeps the fragment's trailing-comma
convention (declines when the two sides disagree on it), and sorts keys only
when both sides were already sorted — npm's own convention.
First change that raises BOTH metrics at once, on all four repos:
agreement files resolved end-to-end
laravel/framework 81.9% → 83.3% 216 → 245
prettier/prettier 45.0% → 49.6% 100 → 117
expressjs/express 59.2% → 61.3% 49 → 62
vuejs/core 90.0% → 90.1% 220 → 222
Core suite: 1095 green (9 new tests, including the vue @babel/parser shape,
the workspace:* decline, and the no-duplicate-key guarantee that lot 1's
invariant would otherwise retract).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LQoTe6RE4JpoQknCASS3hu
… numbers The accuracy work squatted v3.9/v3.10/v3.11 in code comments while the ROADMAP reserves those for Live Repo, preview-to-apply and Stacked Branches. Comments now say 'accuracy lot 1/C/E' (27 files, mechanical), and the ROADMAP gains a 'Next release — Engine Accuracy' section: what shipped on the branch, the measured table, and the follow-ups (lot F conventions-from-history, lot G CI gate, lot D full regeneration, corpus re-pin) each flagged as wanting its own plan. Two cross-references added where the roadmap already converged on this work without knowing it: v3.10's confidence threshold (only meaningful now that format resolvers carry a real confidence) and v4.0's feedback loop (lot F is its active, measured form — they should share one store). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LQoTe6RE4JpoQknCASS3hu
The rebase test hung to vitest's 5s timeout on a real macOS setup while passing in a clean VM — the temp repos were inheriting the host's global git config, so a global core.hooksPath (husky), a GPG key waiting for a passphrase, or a configured editor could stall 'git rebase' silently. Test git calls now run with GIT_CONFIG_GLOBAL/SYSTEM pointed at /dev/null, prompts and editors disabled, a local hooksPath override, and a hard 10s execFileSync timeout so any future hang fails loud instead of timing out quietly. Production detectMergeContext is untouched — it only runs read-only commands and should respect the user's real config. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LQoTe6RE4JpoQknCASS3hu
… from its own history The engine's rules are calibrated on a public corpus, and lot C proved conventions differ per repo. This plan derives them per repository by replaying its own merge history under candidate rules and scoring against what the team actually committed — verdicts gated on sample floors, stored per-clone in .git/gitwand/, always losing to an explicit .gitwandrc, and carrying provenance into every trace they influence. Task 5 is the gate again: split-half validation on the benchmark corpus (derive on the first half of merges, measure on the second) before any desktop surface ships. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LQoTe6RE4JpoQknCASS3hu
…F, core+CLI)
A convention is no longer an assumption: `gitwand conventions` replays the
repo's own merge history (git merge-tree, read-only, capped at 200 merges),
scores candidate rules against what the team actually committed, and emits
verdicts only above evidence floors (>=5 samples, >=80% agreement). Results
live in .git/gitwand/conventions.json — per clone, never committed — and an
explicit .gitwandrc ALWAYS beats a derived convention.
Core: conventions/{types,derive}.ts (pure — observations in, verdicts out,
no git/fs/clock), GitWandOptions.conventions, and three consumptions:
generatedFiles verdict "merge" re-enables auto-resolution when the caller
expressed no choice; "regenerate" keeps the decline; changelog "tool-rebuilt"
declines markdown unions on changelog files. Every influenced resolution
carries provenance in its reason ("convention mesurée sur 16 merges, 100%").
pathPolicies are derived and REPORTED as a suggested .gitwandrc
patternOverrides snippet, never silently applied (v1).
CLI: conventions-runner inside commands/conventions.ts (derive / --show /
--clear / --max-merges / --json), git >= 2.38 guard with a clear error
(merge-tree --write-tree), hermetic-git tests on fabricated histories that
skip cleanly on older git (the dev VM runs 2.34; validated for real on 2.43
in a container: regenerate/tool-rebuilt/prefer-theirs all measured at 100%).
Task-5 gate verdict — recorded in benchmark/README and the plan: split-half
on the corpus is FLAT everywhere (zero regressions, zero gains) because every
derived verdict confirms the engine defaults... which were calibrated on this
very corpus. Circularity, not absence of value: the layer pays off as
provenance, and on repos that diverge from defaults (pinned by unit tests).
Desktop surface deferred per the gate until the corpus re-pin includes
divergent-convention repos.
Tests: core 1109 (14 new), cli 40 + 4 gated (44 on modern git) — green.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LQoTe6RE4JpoQknCASS3hu
The rebase-detection and conventions-derivation tests spawn 30-40 git subprocesses each. On macOS every exec goes through XProtect/Gatekeeper (~100-300ms per process), so vitest's default 5s test timeout is structurally too short there — the suites run in under a second on Linux and timed out on a real Mac. The hermetic-env fix from c25fd10 was necessary (global config must never leak into temp repos) but not sufficient. Each integration test now carries an explicit 30s budget, distinct from the hard 10s per-git-call timeout that still catches genuine hangs loudly. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LQoTe6RE4JpoQknCASS3hu
Corpus v2 — selection is now itself measured. Candidates were probed with rev-list --merges plus a merge-tree conflict-rate sample over their 60 most recent merges: kubernetes, rails and godot rejected at 0/60 (merge queues); symfony (back-merge culture, composer.json in half its conflicted merges), git/git (integration branches, maintainer-resolved conflicts — the best human ground truth there is) and bootstrap (adversarial _variables.scss family) come in. cargo/django dropped for having nothing to replay, and vue dropped DESPITE being the 92-95% showcase — keeping it would have been flattering rather than informative. Current-engine baseline on v2, committed as the CI reference (results/v3.8.0-corpus2-baseline.json): 1927 merges replayed, 634 conflicted, 5675 hunks — 59.2% of end-to-end-resolved files byte-identical to the human merge (391/660), per-repo spread 17.5-65.4%. Lot G — the gate itself: - compare.mjs: fails when corpus agreement drops >1.5 pts, any repo drops >5 pts, or end-to-end coverage collapses >25% (a deliberate decline policy must update the baseline in the same PR, reasoning in the commit message). Agreement is the protected metric; coverage may fall on purpose — that asymmetry is the lesson of lots 1/C/E encoded as thresholds. - .github/workflows/benchmark-gate.yml: runs on PRs touching the engine, clones cached keyed on the corpus hash (only the first run after a re-pin pays), explicit git>=2.38 check so an old git fails loudly instead of measuring zero conflicts silently. Lot F gate re-run on the v2 additions: still flat (halves too thin to clear the evidence floors) — the desktop deferral stands, recorded in the README. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LQoTe6RE4JpoQknCASS3hu
The first committed baseline was produced by a stale run.mjs in the measuring container that predated the per-repo agreement block, so compare.mjs could only gate on the aggregate — per-repo regressions passed silently. Regenerated with the current runner (same corpus, same totals: 59.24%, 391/660); the gate now verifiably reports all eight repos and fails on a simulated -6pt per-repo drop. Per-repo v2 baseline: laravel 83.3, symfony 73.7, express 61.7, git/git 61.1, prettier 49.6, tauri 37.9, hugo 30.2, bootstrap 25.7. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LQoTe6RE4JpoQknCASS3hu
Plan only — registry design (5 script-suppressed ecosystems), plan-in-core / execution-in-callers split, consent + sandbox + offline model, dedicated measurement harness (merge-tree replay cannot score this lot), desktop surface gated on measured results. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LQoTe6RE4JpoQknCASS3hu
added 13 commits
August 27, 2026 14:55
…les (accuracy lot D, task 1) Core stays Node-free: the generatedGate decline now attaches a machine-usable RegenerationPlan (npm/pnpm/yarn-berry/composer/cargo) whenever the declined lockfile matches the v1 registry, sourced from a caller-supplied regenerationContext. Core never spawns anything; it only emits the plan and appends a --regenerate hint to the existing French decline reason.
…ne sites (lot D, fix round 1) reclassifyIfGenerated turns a genuinely-complex hunk on a generated path into generated_file BEFORE resolveHunk runs, so the generatedGate branch (which explicitly skips hunk.type === "generated_file") never sees the majority case: a real lockfile conflict. That decline happened in assembleResolution's generated_file case with no plan attached (spec finding #1's 0%-agreement case). Factored plan-attachment into attachRegenerationPlan() and call it at generatedGate, the minConfidence decline, and right after assembleResolution returns lines: null for a generated_file hunk. assemble.ts stays untouched.
…test (lot D, fix round 2) Site 2 (regeneration-plan attachment on the minConfidence decline for generated_file hunks) was unreachable: computeEffectiveMinConfidence always returns the more permissive of policy/option, no MergePolicy preset exceeds "high", and reclassifyIfGenerated fixes generated_file confidence at exactly "high" — so the confidence-threshold branch can never reject that hunk type through any public API. Removed the branch and its comment; two real sites remain (generatedGate, post-assembleResolution). Also replaced the registry test's per-ecosystem script-suppression check, which short-circuited to true for yarn-berry/cargo (never actually checking their args), with a real assertion per ecosystem: explicit flag for npm/pnpm/composer, exact --mode=update-lockfile for yarn-berry, and an exact ["generate-lockfile"] args array for cargo — so a future edit that dropped the safe command choice would fail the test.
Core (lot D task 1) only emits a RegenerationPlan for declined generated files; this adds the CLI-side executor. `gitwand resolve --regenerate` (or `.gitwandrc` `regenerate: true`) runs a pass 2 after the normal resolution pool: it re-derives each plan with the real sibling-file state (pass 1's plans are always non-runnable placeholders) and, when runnable, regenerates the lockfile in a disposable `git worktree` seeded with the resolved sources of truth - never the user's real working tree. - regenerate-runner.ts: toolchain probe, offline probe (DNS, no new dep), worktree sandboxing, timeout + full trace, output validation (JSON for npm/composer, YAML for pnpm/yarn-berry, TOML for cargo), guaranteed worktree cleanup. - resolve.ts: pass 2 wiring, per-file verbose regenerate trace line, failure leaves the file exactly as pass 1 left it plus the reason. - Fixed a real env-stripping bug found via testing: the secret-scrubbing regex was deleting GIT_CONFIG_KEY_N (legitimate git plumbing) while keeping GIT_CONFIG_COUNT, breaking `git worktree add` in environments that inject config via env vars.
…, fix round 1) Review Important #1: pass 2's siblingFiles map only covered files git flagged as conflicted, so a source of truth that merged cleanly (the common case: package.json untouched, package-lock.json diverges) never got a "clean" entry and buildRegenerationPlan treated it as conflicted, making runnable permanently unreachable for that shape (and yarn-berry entirely, since .yarnrc.yml is essentially never itself conflicted). Pre-seed every candidate ecosystem's sourcesOfTruth not in the conflicted set as clean, and read its on-disk content directly when pass 1 never touched it. Review Important #2: replaced the env denylist (regex on "sensitive" substrings) with an explicit allowlist, matching AGENTS.md's actual wording ("pass only the specific env vars the child process needs"). Review Important #3: added an end-to-end regression test that drives cmdResolve() itself (not just regenerate-runner.ts) against a repo shaped exactly like Important #1 - verified it fails without the resolve.ts fix and passes with it restored.
…ng (accuracy lot D, task 3) Fixes two upstream bugs that silently blocked core's lot F convention precedence from the CLI: resolveGeneratedFiles always reached resolve() as a concrete false instead of undefined, and .git/gitwand/conventions.json was never loaded into options.conventions at all. Adds .gitwandrc resolveGeneratedFiles precedence over measured conventions, a default (non-verbose) --regenerate offer in the resolve summary, and reporting-only regenerate:true support on the 3 MCP resolve() tools (never executes, mirrors the CLI's precedence via a duplicated MCP-local helper). Extends website/reference/config.md's Generated Files section with the regenerate tier's consent model, sandbox, and convention interplay.
… param (task 3, fix round 1) buildRegenerationReport() defaulted any sourceOfTruth path absent from the current call's own results to "clean", conflating "not seen by this call" with "not actually conflicted" — the same bug Task 2's fix round closed for the CLI's resolve.ts. Exploitable via gitwand_resolve_conflicts' files: param: a narrowed list excluding an actually-conflicted package.json could report a plan as runnable:true. Now threads the repo's full conflicted-file set through as a second parameter and only defaults to "clean" when a path is absent from both the call's own results AND that full set, mirroring resolve.ts's guard exactly.
…ask 4) scripts/replay-regenerate.mjs replays historical merges from a real, already-cloned corpus repo: cheap merge-tree candidate discovery, then real checkout + real npm/pnpm/yarn-berry/composer/cargo regeneration via the CLI's own runRegeneration() executor, structurally compared (scripts/lib/ regenerate-compare.mjs) against the lockfile the team actually committed. Fixture tests (node --test scripts/lib/regenerate-compare.test.mjs, also `pnpm run test:regenerate-compare`) cover the comparison/scoring logic for all five registry formats with no network. stripVolatileValues is now exported from @gitwand/core for the fallback path. Pilot run (bounded, ~5 real attempts/ecosystem, per Ruling P-9): laravel/ framework's composer leg is infeasible (it never commits composer.lock, confirmed via full history; symfony/symfony has the same gap, so corpus v2 has no measurable composer repo). prettier/prettier turned out to be yarn-berry, not npm as assumed; piloted that instead: 66.7% structural agreement (n=3), below the 80% target. benchmark/README.md documents the method, the real numbers, and the resulting gate verdict: keep CLI opt-in only, do not build the desktop surface (task 5) on this evidence.
Closes the 5 Important findings from the whole-branch final review: 1. Nested (non-root) lockfiles could report a false "regenerated" success (silent take-ours wearing regeneration's provenance) because the CLI's worktree runner writes/reads sources of truth at the worktree root, not the lockfile's own directory. Fixed once in core's buildRegenerationPlan (blocks any non-root file, inherited by CLI, MCP reporting, and the measurement harness alike) rather than duplicated in three callers. 2. "Not conflicted" was conflated with "clean" in both the CLI's and MCP's sibling-map seeding, letting yarn-classic repos (no .yarnrc.yml) report runnable:true for yarn-berry, contradicting the registry's own documented berry-marker guard. Both now require the file to actually exist on disk. 3. Documented (not re-architected, per the review's own scope note) that regenerate-runner.ts seeds its worktree from HEAD (ours-only) rather than the in-progress merge index, and named it as hypothesis (d) for the 66.7%/n=3 pilot result in benchmark/README.md. 4. Split the env allowlist: the GIT_* prefix (needed for git worktree plumbing) no longer reaches the spawned ecosystem installer, closing a path for CI-injected credentials (GIT_CONFIG_*/GIT_ASKPASS/ GIT_SSH_COMMAND) to leak into npm/pnpm/yarn/composer/cargo's environment. 5. scripts/replay-regenerate.mjs now refuses to run against a non-bare repo (git update-ref HEAD follows the symref) and restores the operator's original HEAD from a SIGINT/SIGTERM handler, not just the happy path. Also includes the review's opportunistic ask: a one-line notice when a runnable plan's source turns out to be unreadable (previously silent). disabled
…epo tests Discovered during the pre-finish full-suite run: these tests spawn real git subprocesses and were relying on vitest's 5s default, which flakes under the full monorepo suite's concurrent load. Matches the 30s timeout convention already used by every other real-git-repo test in this plan.
…dex seeding, full sweep, docs gap)
…conventions in cli-commands.md
…ge index, not ours-only HEAD
added 8 commits
August 28, 2026 10:00
…merge-tree result replay-regenerate.mjs replays historical merges with no live in-progress-merge index to read from, so it now builds a scratch index from the merge-tree it already computed during candidate discovery and passes it as runRegeneration's seedIndexFile, matching the disposable-worktree seeding fix already shipped in the CLI's production path.
…-index-seeding fix Real, full-scale replay-regenerate.mjs run (--max-real 20) against all four in-scope corpus v2 repos (prettier/prettier, tauri-apps/tauri, expressjs/express, twbs/bootstrap), after tasks 2-3's merge-index-seeding fix landed. Result: n=1 comparable (1/1 = 100.0%), a smaller comparable sample than the pilot's n=3 (66.7%) it was meant to supersede — reported as genuinely inconclusive, not "met". CLI-opt-in-only status quo stands; the desktop surface remains unjustified by this evidence.
…view The measurement harness's scratch index (git read-tree of a single merge-tree result) put every path at stage 0, including genuinely conflicted ones whose blob content is literal diff3 markers - a worktree state production can never reach, since a real merge index keeps conflicted paths off stage 0 entirely. seedScratchIndex now takes a skipPaths param to force-remove those paths from the scratch index after read-tree, and replay-regenerate.mjs retains each candidate's conflicted path list from discovery to pass through. Also: addWorktree's checkout-index overlay no longer throws (preserves runRegeneration's never-throw contract) and no longer leaks an ambient GIT_INDEX_FILE; corrected an overclaim in the fix's own doc comments and in benchmark/README.md about what the overlay actually changes; fixed a 32/32 arithmetic error in the sweep write-up; marked the existing sweep numbers invalidated pending a re-run against the fixed harness; rewrote seed-index's existing test (it wasn't actually exercising seedScratchIndex) and added coverage for the skip-paths behavior and for the CLI's seedIndexFile interface end to end. disabled
…iction with the invalidation notice The gate verdict section still floated a "more realistic merge-index state" hypothesis for the spawn-failed bottleneck, and asked for further root-causing — both superseded by the invalidation notice's own root-cause (the harness's marker-corruption bug, now fixed). Also fixes a stale package.json script-name reference left over from the test:regenerate-compare -> test:scripts-lib rename.
… harness The seed-index skipPaths fix (43be17e) is real but incomplete: git update-index --force-remove still requires a work tree even when only editing an index file, and the corpus caches are intentionally bare, so every runnable candidate now fails before ever reaching the installer. This fresh, real re-run produced 0 comparable results (vs the invalidated sweep's n=1 and the original pilot's n=3), documented honestly with the root cause and the concrete next fix needed.
…ree plumbing git update-index --force-remove still requires a work tree even when only editing an index file, so seedScratchIndex failed 100% of the time against the corpus's bare clones. Rebuilt it to remove skipped paths via targeted git ls-tree -z / mktree -z --missing calls that walk only the directory chain of each skip path, which needs no work tree, tolerates C-quoted filenames, and doesn't require objects to be fetched in a blobless clone. Re-ran the real regenerate-tier sweep against the fixed harness: n = 13 comparable, 5 matched, 38.5% agreement on prettier/prettier's yarn-berry candidates, well below the 80% target. Documented in benchmark/README.md alongside the two prior, invalidated sweep attempts. disabled
Independent review of the third sweep attempt confirmed the 38.5% (5/13) result itself is sound, but found the write-up overreached in two places and understated the fix's remaining coverage gaps: - The gate verdict implied "every runnable candidate ran to completion" meant the full 85-candidate population was covered; it didn't disclose that --max-real 20 only attempted the 20 most recent (recency-biased) candidates, leaving 65 unclassified. - The hypothesis-(d) paragraph read a causal "seeding made it worse" comparison out of the pilot's 66.7% (n=3) vs this sweep's 38.5% (n=13) despite there being no matched-pair comparison and heavily overlapping confidence intervals - replaced with the narrower, actually-supported claim. - Corrected a false "cannot be reproduced without network" claim about the blobless-clone bug (independently disproven) and disclosed two residual gaps found by the same review: the blobless dimension still has no regression test, and a C-quoted skip path from merge-tree's default output would silently fail to be stripped (zero blast radius on this sweep's 237 scanned merges, confirmed by the reviewer, but a live latent bug for future runs). - Fixed a stale historical-section pointer chain and an inaccurate illustrative directory count.
…ession test mergeTree() in replay-regenerate.mjs used git merge-tree's default (non-z) output, which C-quotes any conflicted path containing a quote character or non-ASCII byte. Those quoted, escaped strings never matched the raw bytes seedScratchIndex compares skipPaths against (read via git ls-tree -z), so a C-quoted conflicted path would silently fail to be skipped, leaking diff3 marker content into the scratch index. Extracted mergeTree into scripts/lib/merge-tree.mjs and switched it to -z, parsing the verified real output shape rather than guessing from documentation. Also added the regression test the blobless-clone mktree --missing fix (scripts/lib/seed-index.mjs) shipped without: a hermetic blobless bare clone built via uploadpack.allowFilter + clone --bare --filter=blob:none, no network required, disproving the prior claim that this case "cannot be reproduced" hermetically. Zero impact on the already-published regenerate-tier sweep numbers; this is pure robustness/coverage work for future runs.
added 3 commits
August 28, 2026 17:59
README.md, the conflict-resolution guide and the package-lock.json fix page all still described generated_file as auto-applying (accept-theirs or a semantic key-wise merge) by default. It now declines by default, measured on 1,662 real merges to diverge from what teams actually shipped in almost every case — these three pages were the only user-facing docs this branch left saying otherwise. disabled
.impeccable/hook.cache.json showed up as an untracked file with no repo content (empty session cache) — ignore the whole dir so it stops surfacing.
…accuracy # Conflicts: # CHANGELOG.md # README.md # package.json # packages/cli/src/commands/resolve.ts # packages/core/src/__tests__/corpus.ts # packages/core/src/resolver/assemble.ts # packages/core/src/resolver/generated-detection.ts # packages/core/src/resolver/index.ts # pnpm-lock.yaml
devlint
pushed a commit
that referenced
this pull request
Aug 31, 2026
…state CHANGELOG.md's Unreleased section stopped tracking after lot E: lot F (gitwand conventions), lot G (the CI benchmark-gate), the full lot D regenerate tier, and the corpus v2 re-pin never got an entry despite shipping in the same PR (#170). Added the four missing entries. ROADMAP.md's "Follow-ups" list for Engine Accuracy described those same four items as future work; all are done. Moved Engine Accuracy from "next release" to v3.9.0 in Shipped, and shifted every later planned version down one slot (Live Repo v3.9.0 -> v3.10.0, preview-to-apply v3.10.0 -> v3.11.0, Stacked Branches v3.11.0 -> v3.12.0, Combined Diffs v3.12.0 -> v3.13.0, Voice Input v3.13.0 -> v3.14.0; v4.0.0 unaffected), updating every cross-reference to the old numbers throughout the file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Quoi
Le moteur de résolution passe de "résout ce qui est trivial" à "est d'accord avec ce que les humains ont réellement committé" — mesuré, pas affirmé.
Résultat mesuré (corpus v1, v3.8.0 → cette branche) : accord global 41,7 % → nettement au-dessus ; laravel 24,3 → 83,3 %, prettier 25,3 → 49,6 %. Baseline corpus v2 : 59,24 % (391/660), détail par dépôt dans
benchmark/results/.Par lot
complexappliqué en silence), invariants de format avec rétractation, déclin par défaut des fichiers générés (resolveGeneratedFiles, raison actionnable).MergeContext {operation, targetSide}— déclaré, jamais re-dérivé). Règle affinée par son propre gate : target ne gagne que sur les paires de versions non ordonnables ; l'ordonnable reste newest-wins.gitwand conventions) — planchers d'évidence (≥5 échantillons, ≥80 %), précédence explicite > convention > défaut, provenance dans chaque raison. Surface desktop différée par le gate (circularité corpus)..github/workflows/benchmark-gate.yml) — toute PR touchant le moteur est comparée à la baseline (chute d'accord > 1,5 pt global ou > 5 pts/dépôt = rouge). Vérifié dans les deux sens.RegenerationPlanpur (aucune exécution côté core) ; le CLI l'exécute dans ungit worktreejetable, sandboxé, opt-in (--regenerate/.gitwandrc), avec suppression des scripts d'installation non-négociable et allowlist d'env (jamais de secrets/tokens transmis à l'installeur). MCP expose la même info en lecture seule (aucun outil MCP n'exécute quoi que ce soit). Un follow-up a corrigé un vrai bug (le worktree se semait depuisHEADseul, invisible aux fichierstheirs-only) et mesuré l'accord réel après correction, sur 3 tentatives (les 2 premières ont elles-mêmes révélé des bugs du harnais de mesure, corrigés en cours de route) :5/13 = 38,5 % d'accord sur
prettier/prettier(yarn-berry), n=13, mesure conclusive (p ≈ 1,2×10⁻³ contre la barre des 80 %). Verdict : objectif (≥80 %) non atteint → le tier reste opt-in CLI, la surface desktop n'est pas justifiée par cette preuve. Détail complet, historique des 3 tentatives et limites connues dansbenchmark/README.md(§ "Regenerate-tier replay").Notes
docs/superpowers/specs/2026-08-26-conflict-engine-accuracy.md.docs/superpowers/plans/2026-08-26-regenerate-tier.mdetdocs/superpowers/plans/2026-08-27-regenerate-tier-followup.md.Vérification
pnpm -r build && pnpm -r testverts en local ; gate CI actif sur cette PR même.