repowise-dev / repowise-dev/repowise
[Bug] An incremental update reads no stored health data under a configured database
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 checkoutof a source file back tomainbreaks an unrelated import while you are testing, your branch is stale. Sabotage against your merge base rather than againstmain.
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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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