From 6514c4ab0e73ba1fa9f656961ae40ef4a6be88ee Mon Sep 17 00:00:00 2001 From: Gergo Magyar Date: Sun, 21 Jun 2026 10:32:53 +0000 Subject: [PATCH] fix(analyze): gate up-to-date self-heal on --allow-duplicate-name (#2264) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The prior commit healed every up-to-date-but-unregistered repo by falling through to register it — which broke the #1169 guard: a plain `analyze` of an up-to-date repo whose registry entry is missing MUST fail loudly ("Analysis did not finalize") rather than silently register a possibly half-finalized index. Distinguish the two causes of "unregistered": - collision-rejected + user re-runs with --allow-duplicate-name → explicit intent to register, so fall through to the pipeline and register it (#829). - plain analyze, registry missing/wiped → keep the #1169 fail-loud behavior. So self-heal is gated on options.allowDuplicateName; isRepoRegistered is only read on that opt-in branch, so the common fast path keeps its single-stat cost. Both cli-e2e guards (#1169 fail-loud, #829 heal) now pass. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01JBJomjoTdBV2eveDVq4JMm --- gitnexus/src/core/run-analyze.ts | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/gitnexus/src/core/run-analyze.ts b/gitnexus/src/core/run-analyze.ts index cf8196acb..4043075d9 100644 --- a/gitnexus/src/core/run-analyze.ts +++ b/gitnexus/src/core/run-analyze.ts @@ -778,15 +778,21 @@ export async function runFullAnalysis( return true; // conservative on git failure } })(); - // Only short-circuit when this repo is actually REGISTERED. A prior run - // can write meta.json and then fail before registerRepo (e.g. a rejected - // --name collision), leaving the index up-to-date but UNREGISTERED. Taking - // the fast path there returns an unregistered repo that the CLI's - // assertAnalysisFinalized rejects — and `--allow-duplicate-name` could - // never heal it (it would keep hitting this early-return). Fall through to - // the pipeline so it gets registered (honoring allowDuplicateName); already - // registered repos keep the fast path unchanged (#2264). - if (!dirty && (await isRepoRegistered(repoPath))) { + // Registration wrinkle around the fast path (#2264). A prior + // `analyze --name X` that hit a name collision writes meta.json (meta-save + // runs before registerRepo) then fails before registering, leaving the + // index up-to-date but UNREGISTERED. When the user re-runs with + // --allow-duplicate-name they explicitly want it registered, so fall + // through to the pipeline (which registers it, honoring the flag) instead + // of early-returning an unregistered repo the flag could never heal. + // For a PLAIN analyze we deliberately do NOT self-heal: an up-to-date but + // unregistered repo early-returns here and the CLI's assertAnalysisFinalized + // surfaces it as a hard failure (#1169) rather than silently registering a + // possibly half-finalized index. `isRepoRegistered` is only read on the + // opt-in branch so the common fast path keeps its single-stat cost. + const healUnregistered = + options.allowDuplicateName === true && !(await isRepoRegistered(repoPath)); + if (!dirty && !healUnregistered) { await ensureGitNexusIgnored(repoPath); return { // `resolveRepoIdentityRoot` collapses worktree roots to the