fix(generation): prioritize oldest stale pages when the budget is constrained - #1845
fix(generation): prioritize oldest stale pages when the budget is constrained#1845sloemo01 wants to merge 1 commit into
Conversation
…strained The cascade budget in ChangeDetector.get_affected_pages sliced its regenerate set by pagerank alone, so a constrained docs run could regenerate pages that were already current while leaving genuinely stale prose untouched (issues repowise-dev#847 / repowise-dev#851). Thread existing stale-page ages into the sort so an already-stale page outranks a fresh page no matter how central the fresh one is; among stale pages the oldest decays first. Adds get_stale_file_page_ages to the persistence CRUD, wires it into the CLI update path and the server's incremental page regen, and pins the behaviour with regression tests.
|
✅ Health of changed files: 2.6 → 2.9 (+0.3) 📋 At a glance Files & modules (2)
✅ Health gate: passed 📌 Before you merge
🎯 Blast radius (symbols whose signature this PR changed, and who calls them)
🔎 More signals (3)🗺️ Change map flowchart LR
subgraph PR ["Changed in this PR (6 with dependents)"]
f_packages_cli_src_repowise_cli_commands_update_cmd_command_py[".../update_cmd/command.py 🔥"]:::changed
f_packages_cli_src_repowise_cli_commands_update_cmd_deterministic_py[".../update_cmd/deterministic.py 🔥"]:::changed
f_packages_core_src_repowise_core_persistence___init___py[".../persistence/__init__.py 🔥"]:::changed
f_packages_server_src_repowise_server_job_executor_py[".../server/job_executor.py 🔥"]:::changed
f_packages_core_src_repowise_core_ingestion_change_detector_py[".../ingestion/change_detector.py"]:::changed
f_packages_core_src_repowise_core_persistence_crud_pages_py[".../crud/pages.py"]:::changed
end
f_packages_cli_src_repowise_cli_commands_generate_cmd_command_py[".../generate_cmd/command.py"]
f_packages_cli_src_repowise_cli_commands_update_cmd_command_py --> f_packages_cli_src_repowise_cli_commands_generate_cmd_command_py
f_packages_cli_src_repowise_cli_commands_init_cmd_command_py[".../init_cmd/command.py"]
f_packages_cli_src_repowise_cli_commands_update_cmd_command_py --> f_packages_cli_src_repowise_cli_commands_init_cmd_command_py
f_packages_cli_src_repowise_cli_commands_init_cmd_generation_py[".../init_cmd/generation.py"]
f_packages_cli_src_repowise_cli_commands_update_cmd_command_py --> f_packages_cli_src_repowise_cli_commands_init_cmd_generation_py
f_packages_cli_src_repowise_cli_commands_init_cmd_persistence_py[".../init_cmd/persistence.py"]
f_packages_cli_src_repowise_cli_commands_update_cmd_command_py --> f_packages_cli_src_repowise_cli_commands_init_cmd_persistence_py
f_packages_cli_src_repowise_cli_commands_update_cmd_deterministic_py --> f_packages_cli_src_repowise_cli_commands_init_cmd_command_py
f_packages_cli_src_repowise_cli_commands_restyle_cmd_py[".../commands/restyle_cmd.py"]
f_packages_cli_src_repowise_cli_commands_update_cmd_deterministic_py --> f_packages_cli_src_repowise_cli_commands_restyle_cmd_py
f_packages_cli_src_repowise_cli__repo_session_py[".../cli/_repo_session.py"]
f_packages_core_src_repowise_core_persistence___init___py --> f_packages_cli_src_repowise_cli__repo_session_py
f_packages_cli_src_repowise_cli_commands_augment_cmd_search_py[".../augment_cmd/search.py"]
f_packages_core_src_repowise_core_persistence___init___py --> f_packages_cli_src_repowise_cli_commands_augment_cmd_search_py
f_packages_cli_src_repowise_cli_commands_claude_md_cmd_py[".../commands/claude_md_cmd.py"]
f_packages_core_src_repowise_core_persistence___init___py --> f_packages_cli_src_repowise_cli_commands_claude_md_cmd_py
f_packages_cli_src_repowise_cli_commands_costs_cmd_py[".../commands/costs_cmd.py"]
f_packages_core_src_repowise_core_persistence___init___py --> f_packages_cli_src_repowise_cli_commands_costs_cmd_py
f_packages_server_src_repowise_server_job_executor_py --> f_packages_cli_src_repowise_cli_commands_init_cmd_command_py
f_packages_server_src_repowise_server_job_executor_py --> f_packages_cli_src_repowise_cli_commands_init_cmd_persistence_py
f_packages_cli_src_repowise_cli_commands_init_cmd_workspace_py[".../init_cmd/workspace.py"]
f_packages_server_src_repowise_server_job_executor_py --> f_packages_cli_src_repowise_cli_commands_init_cmd_workspace_py
f_packages_server_src_repowise_server_job_executor_py --> f_packages_cli_src_repowise_cli_commands_restyle_cmd_py
more(["+89 more dependents"])
PR --> more
w_packages_cli_src_repowise_cli_commands_update_cmd_persistence_py(["⚠️ .../update_cmd/persistence.py changed together 14×, not in PR"]):::warn
f_packages_cli_src_repowise_cli_commands_update_cmd_command_py -.- w_packages_cli_src_repowise_cli_commands_update_cmd_persistence_py
w_packages_core_src_repowise_core_workspace_update_py(["⚠️ .../workspace/update.py changed together 13×, not in PR"]):::warn
f_packages_cli_src_repowise_cli_commands_update_cmd_command_py -.- w_packages_core_src_repowise_core_workspace_update_py
t_tests_integration_test_cli_py(["✅ tests/integration/test_cli.py"]):::guard
t_tests_integration_test_cli_py -.-> f_packages_cli_src_repowise_cli_commands_update_cmd_command_py
t_tests_unit_cli_test_deterministic_update_py(["✅ .../cli/test_deterministic_update.py"]):::guard
t_tests_unit_cli_test_deterministic_update_py -.-> f_packages_cli_src_repowise_cli_commands_update_cmd_deterministic_py
t_tests_integration_persistence_test_graph_metrics_pg_py(["✅ .../persistence/test_graph_metrics_pg.py"]):::guard
t_tests_integration_persistence_test_graph_metrics_pg_py -.-> f_packages_core_src_repowise_core_persistence___init___py
classDef changed fill:#dbeafe,stroke:#1d4ed8,color:#1e3a5f
classDef warn fill:#fef3c7,stroke:#b45309,color:#78350f
classDef guard fill:#dcfce7,stroke:#15803d,color:#14532d
Solid arrows: code that imports the changed files (99 direct dependents, from the last indexed snapshot). Dashed: history/tests. 🔥 Hotspots touched (5)
2 more
🔗 Hidden coupling (1 file)
👀 Suggested reviewers @RaghavChamadiya 📊 See the full report for this PR |
Ayush7614
left a comment
There was a problem hiding this comment.
Verified: packages/core/src/repowise/core/ingestion/change_detector.py:1 now accepts stale_pages map and sorts staleness-first (oldest stale outranks fresh regardless of pagerank) for budget-constrained regen (#851). Plumbing via get_stale_file_page_ages and CLI/server call sites is best-effort with fallback to pure-importance. Tests in test_change_detector.py + test_stale_file_page_ages.py cover ordering. No fake code — LGTM.
Summary
Fixes #851 (spun out of #847).
When the cascade budget is constrained, the ordering that decides which pages to regenerate was driven purely by pagerank (importance). That meant a docs run could spend its LLM calls regenerating high-importance pages that were already current while leaving genuinely stale prose untouched — the exact staleness-vs-importance mis-prioritization reported.
This change makes staleness the primary sort axis for the budgeted slice:
ChangeDetector.get_affected_pagesnow accepts astale_pagesmap ({file_path: staleness_age_seconds}). When provided, an already-stale page outranks a fresh page no matter how central the fresh one is; among stale pages, the oldest stale page regenerates first. Importance (pagerank) breaks ties within each staleness class.stale_pageskeeps the historical pure-importance ordering, so no call site changes behaviour unless it opts in.Plumbing
get_stale_file_page_ages(session, repo_id)persistence CRUD helper: returns{file_path: age_seconds}for stale/expiredfile_pagerows, age measured fromupdated_at(the last regeneration) so the longest-lagging page carries the largest value.repowise update --docsloads stale ages and passes them into the cascade._incremental_page_regen) loads stale ages through its session/repo and passes them in (best-effort; missing session/repo falls back to the old ordering).repowise.core.persistence.Tests
tests/unit/ingestion/test_change_detector.py: a stale page regenerates ahead of a more important fresh page; oldest-stale-first within a budget; absentstale_pagespreserves importance ordering.tests/unit/persistence/test_stale_file_page_ages.py: which rows count, age semantics, and empty-return contract.Verification
uv run ruff check— clean on all changed files.uv run pytestover the changed modules — all pass (the onetest_repo_totals_churnfailure is a pre-existing git-environment issue, reproduced with the changes stashed).