repowise-dev / repowise-dev/repowise

[Bug] An incremental update reads no stored health data under a configured database

Open
#2,406 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
6.7k
Forks
711
Avg merge
1d 13h
Merged PRs (30d)
439

Description

Summary

Four store reads in packages/core/src/repowise/core/pipeline/incremental.py decide whether a store exists by looking for the repo-local wiki.db file, and then build their engine from resolve_db_url(), which prefers REPOWISE_DB_URL. Under a configured shared database that file is absent by design, so all four take their "no store" path on every incremental run.

The comment at :415-418 already says which guard this is: "Same guard, and the same reason, as the head-commit stamper."

The four sites

line function returns without a store what is lost
419 load_stored_git_meta None dead-code scope narrows, and the run reports it in yellow
469 load_stored_performance_callers None stale caller findings from deleted or renamed symbols can no longer be removed
533 load_stored_function_mod_p80 None the Function Hotspot gate re-scores without its p80
586 load_stored_coverage_map {} every re-analyzed file scores as if no coverage had evolved

Each is the same three lines:

if not (Path(repo_path) / ".repowise" / "wiki.db").is_file():
    return {}

and each is followed a few lines later by the engine it never reaches:

engine = create_engine(resolve_db_url(repo_path))

Why the guard is wrong

resolve_db_url resolves REPOWISE_DB_URL first and falls back to <repo>/.repowise/wiki.db only when nothing is configured. The guard asks a different question from the one the code below it answers.

The guard has a real purpose and it is written down at :415-418: never create the store as a side effect of reading it, because an empty database reads as "indexed" downstream. A configured database is not empty, and reading it does not create it, so that reason does not reach this case.

The fix

Ask whether a store exists instead of whether one particular file does. #2404 adds that predicate next to resolve_db_url:

def has_db_store(repo_path: str | Path | None = None) -> bool:
    if get_configured_db_url() is not None:
        return True
    if repo_path is None:
        return False
    return (Path(repo_path) / ".repowise" / "wiki.db").is_file()

Each site then becomes:

from repowise.core.persistence.database import has_db_store

if not has_db_store(repo_path):
    return {}

Keep the comment at :415-418 and extend it, because it is the only place the reasoning is recorded. If #2404 has not landed, either wait for it or write the same three lines locally. The outcome that matters is that all four sites and the head-commit stamper use one predicate rather than two.

Tests

tests/unit/pipeline/test_load_stored_git_meta.py and tests/unit/pipeline/test_load_stored_coverage_map.py already exist and are the pattern to copy.

The cheap assertion that catches this without seeding any rows: point REPOWISE_DB_URL at a sqlite file outside the repo, call the function, and check that the file now exists. Reaching the store means opening it, and opening it migrates first, so a guard that returns early leaves the file untouched. This fails on current main for all four functions.

Then one test for the guarantee the old guard was protecting: with no REPOWISE_DB_URL and no local wiki.db, each function must still return its empty value and must not create a database.

How to run it

uv sync --all-packages
uv run pytest tests/unit/pipeline/ -q
uv run ruff check packages/core/src/repowise/core/pipeline/incremental.py

Process

  • Comment on this issue saying you are taking it, before you open a PR. One line is enough.
  • Branch from current main. One PR for all four sites, since this is one bug rather than four.
  • In the PR body, show the before and after for one site and say the other three are identical. Four copies of the same diff is harder to review than one plus a sentence.
  • Include the "still no-ops when nothing is configured" test. A reviewer will ask, because that is the guarantee the current guard exists to provide.
  • If the git checkout of a source file back to main breaks an unrelated import while you are testing, your branch is stale. Sabotage against your merge base rather than against main.

Written to work as a good first issue if it reads like one: four mechanical sites, an existing test file to copy, and no new concepts.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in packages/core/src/repowise/core/pipeline/incremental.py at the four store-read guards and the head-commit stamper comment around lines 415-418. Review has_db_store and resolve_db_url, then use tests/unit/pipeline/test_load_stored_git_meta.py and test_load_stored_coverage_map.py as patterns. Run the pipeline tests and ruff; done means configured external databases are reached while an unconfigured repository still returns its empty value without creating a database.

Written by the indexing model from the issue text.

Assessment

Tech stack
python, sqlite
Domain
backend, databases
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
82/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.