Skip to content

[Bug] index-only update advances last_sync_commit, making stale prose unreachable by update --docs #847

Description

@RaghavChamadiya

Describe the Bug

repowise update --index-only advances last_sync_commit to HEAD without regenerating any wiki pages. Because repowise update --docs uses last_sync_commit as its diff base, a later docs run sees an empty diff and regenerates nothing. It prints "No changed files detected" and exits 0.

The result is that page prose silently falls behind the code, permanently, and the command that exists to fix it reports success without doing anything. Since page prose is the entire semantic and full-text retrieval surface, get_answer and search_codebase keep serving descriptions of code that has since changed, with no staleness warning.

This affects any repo that runs index-only updates and later wants docs:

  • indexed with repowise init --index-only
  • indexed after declining the cost gate (that path persists docs_enabled=False, see fix(release): state migration safety + cost-gate UX #156)
  • any repo with docs_enabled=false in .repowise/state.json, where the post-commit hook runs index-only updates on every commit

Each of those commits walks last_sync_commit forward past code whose pages were never regenerated. The staleness is then unreachable: the pointer has already moved past it.

Steps to Reproduce

  1. repowise init --index-only on a repo at commit C0. State is written with docs_enabled=false and last_sync_commit=C0.
  2. Make and commit changes to a few source files that already have pages.
  3. repowise update (defaults to index-only here, per docs_enabled=false). It refreshes the graph, symbols, git and health data, regenerates no pages, and writes last_sync_commit=C1.
  4. repowise update --docs to generate the prose.

Expected Behavior

Step 4 regenerates pages for every file that changed since the pages were last generated (C0).

Actual Behavior

Step 4 prints "No changed files detected" and exits 0. No pages are regenerated. base_ref resolves to last_sync_commit (C1), which already equals HEAD, so the diff is empty. The pages from C0 remain, describing code that no longer exists, and nothing reports this.

The only workaround is to know the problem exists and pass an explicit ref reaching back to when the pages were actually generated:

repowise update --docs --since <ref-before-pages-were-generated>

That ref is not recorded anywhere, so a user has to infer it from page timestamps in wiki.db.

Root Cause

last_sync_commit is doing two different jobs: "how far has the index been walked" and "how far has the prose been generated". Index-only updates advance it for the first meaning, which silently destroys the second.

  • packages/cli/src/repowise/cli/commands/update_cmd/command.py:461 sets the docs diff base:
    base_ref = since or state.get("last_sync_commit")
  • packages/cli/src/repowise/cli/commands/update_cmd/persistence.py:130-184, _persist_index_only_update, whose docstring states "No LLM regeneration", writes at line 172:
    new_state = {
        **state,
        "last_sync_commit": head,
        ...
    }
  • command.py:582-587 then treats the resulting empty diff as success and re-stamps the pointer again.

Suggested Fix

Track the two meanings separately. Persist a last_docs_commit (the commit at which pages were last regenerated), write it only on runs that actually generate pages, and have the docs path diff from it:

base_ref = since or (state.get("last_docs_commit") if not index_only
                     else state.get("last_sync_commit"))

Backfill for existing state files: fall back to last_sync_commit when last_docs_commit is absent, so current index-only users are no worse off than today.

Related Issues Worth Fixing Alongside

These are separate defects, but they compound the one above and could reasonably land in the same change. Happy to split them into their own issues if preferred.

  1. The cascade budget silently caps regeneration at 50 pages. packages/core/src/repowise/core/ingestion/change_detector.py:80-87 hard-caps the auto-scaled budget at 50. A user who does reach a genuine backlog of stale pages regenerates at most 50 of them and gets a success report with no indication that the rest were skipped. Worth surfacing the number of affected pages that were not regenerated.

  2. Page selection is by importance, not staleness. With an explicit --cascade-budget above the cap, the selected pages are chosen by importance over the affected set, so runs can regenerate pages that are already current while leaving genuinely stale ones untouched. Staleness is arguably the more useful tiebreak for an update, as distinct from an init.

  3. Nothing warns. No stale marker is attached to search results or answers while lagging prose is being served as the whole retrieval surface. A repo can sit in this state indefinitely with no signal in the CLI or in MCP responses.

Environment

  • OS: Windows 11
  • Python version: 3.13.5
  • Repowise version: 0.31.0
  • Installation method: pip (editable)

Confirmed by reading the code paths above; the reproduction is the sequence a docs_enabled=false repo follows automatically via the post-commit hook.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions