Skip to content

feat: 18-reading sequence#22

Merged
c5ln merged 8 commits into
mainfrom
feat/18-reading-sequence
May 18, 2026
Merged

feat: 18-reading sequence#22
c5ln merged 8 commits into
mainfrom
feat/18-reading-sequence

Conversation

@c5ln

@c5ln c5ln commented May 17, 2026

Copy link
Copy Markdown
Owner

Summary

읽는 순서 추천 알고리즘 구현. PageRank와 Betweeness Connectivity 알고리즘을 활용하였다.

Changes

+42 −1 CMakeLists.txt
+12 −0 imgui.ini
+33 −0 src/algo/AlgoConfig.h
+240 −0 src/algo/AlgoDbWriter.cpp
+23 −0 src/algo/AlgoDbWriter.h
+150 −0 src/algo/AlgoRunner.cpp
+77 −0 src/algo/AlgoRunner.h
+127 −0 src/algo/BetweennessCentrality.cpp
+31 −0 src/algo/BetweennessCentrality.h
+79 −0 src/algo/Graph.cpp
+25 −0 src/algo/Graph.h
+220 −0 src/algo/GraphBuilder.cpp
+32 −0 src/algo/GraphBuilder.h
+93 −0 src/algo/ReadingSequencer.cpp
+18 −0 src/algo/ReadingSequencer.h
+104 −0 src/algo/Scoring.cpp
+25 −0 src/algo/Scoring.h
+20 −0 src/algo_main.cpp
+23 −1 src/db/db.cpp

Related Issue

Closes #18

Checklist

  • Code compiles without errors
  • Self-reviewed the diff
  • No unintended files included

@c5ln c5ln self-assigned this May 17, 2026
@c5ln c5ln added the feature New feature or request label May 17, 2026
@c5ln c5ln linked an issue May 17, 2026 that may be closed by this pull request
@coderabbitai

coderabbitai Bot commented May 17, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c4aae305-dd28-4627-bcd0-d82f2ade378e

📥 Commits

Reviewing files that changed from the base of the PR and between f096cb5 and e24778a.

📒 Files selected for processing (5)
  • src/algo/AlgoDbWriter.cpp
  • src/algo/BetweennessCentrality.cpp
  • src/algo/Graph.h
  • src/algo/GraphBuilder.cpp
  • src/algo_main.cpp
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/algo_main.cpp
  • src/algo/AlgoDbWriter.cpp
  • src/algo/GraphBuilder.cpp
  • src/algo/BetweennessCentrality.cpp

📝 Walkthrough

Walkthrough

Adds a complete graph-based reading-sequence system: DB schema, graph construction from SQLite, PageRank + configurable betweenness centrality, SCC-condensed prioritized topological sequencing, two-pass file/function merging into ranked reading entries, persistence, CLI entry, and test/build wiring.

Changes

Reading Sequence Recommendation

Layer / File(s) Summary
Graph data structures and SCC computation
src/algo/Graph.h, src/algo/Graph.cpp
NodeId alias, bidirectional string↔ID maps, Graph::get_or_add, and Tarjan SCC finder with large‑SCC diagnostic.
Scoring: PageRank and BC strategies
src/algo/Scoring.h, src/algo/Scoring.cpp, src/algo/BetweennessCentrality.h, src/algo/BetweennessCentrality.cpp
PageRank power iteration with dangling handling and convergence; min‑max normalization; BC strategies: Exact Brandes, deterministic sampling (LCG seed), Zero; make_bc_strategy selects strategy by pass/size/config.
Database schema and GraphBuilder
src/db/db.cpp, src/algo/GraphBuilder.h, src/algo/GraphBuilder.cpp
Extends kInitSQL with reading_sequence and reading_sequence_config. GraphBuilder builds file- and function-level graphs, resolves imports to files, and emits entity→file/type/start-line and file LOC maps.
Reading sequence generation
src/algo/ReadingSequencer.h, src/algo/ReadingSequencer.cpp
Condense SCCs, compute per‑SCC super_score, build reversed condensation DAG, run prioritized Kahn topological traversal; SCC expansion orders nodes by combined score, LOC hint, then name.
Two-pass algorithm and merging
src/algo/AlgoConfig.h, src/algo/AlgoRunner.h, src/algo/AlgoRunner.cpp, src/algo_main.cpp
Adds AlgoConfig, AlgoPass with FilePass/FunctionPass. Runs two passes (file/function), merges file/global and function/local ranks into ReadingEntry outputs, and adds a CLI entrypoint.
Configuration loading and persistence
src/algo/AlgoDbWriter.h, src/algo/AlgoDbWriter.cpp
loadConfig reads parameters with defaults; write performs transactional delete+insert+config upserts (including last_computed_at) and commits or rolls back on error.
Build configuration and tests
CMakeLists.txt, imgui.ini
Adds googletest via FetchContent, creates algo static library, TelescodeAlgo executable, AlgoTests test target with GTest::gtest_main and add_test; imgui.ini persists ImGui window state.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

  • c5ln/Telescode#9: Extends the same src/db/db.cpp schema initialization by adding reading_sequence and reading_sequence_config table definitions to the existing kInitSQL setup.
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Feat/18 reading sequence' is partially related to the changeset; it mentions the feature number and general topic but lacks descriptive detail about the main change. Consider using a more descriptive title like 'Implement reading sequence recommendation using PageRank and Betweenness Centrality' to clearly convey the main feature being added.
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The description covers the summary and changes sections adequately and mentions the related issue #18, though the 'Changes' section is primarily file listings without detailed explanations.
Linked Issues check ✅ Passed The PR implements all objectives from issue #18: PageRank computation, Betweenness Centrality with sampling strategies for large graphs, graph construction, and reading sequence generation with proper tie-breaking and SCC handling.
Out of Scope Changes check ✅ Passed All changes are in scope: new algo module files implement the reading sequence feature, CMakeLists.txt adds build targets and GoogleTest dependency, db.cpp extends schema, and imgui.ini is a configuration file update.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/18-reading-sequence

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (3)
CMakeLists.txt (1)

48-55: ⚡ Quick win

Gate GoogleTest and AlgoTests behind BUILD_TESTING.

Lines 48–55 and 207–218 unconditionally fetch and build test dependencies and targets. Production builds should not pull or compile test-only artifacts.

♻️ Suggested CMake adjustment
-FetchContent_Declare(
-    googletest
-    GIT_REPOSITORY https://github.com/google/googletest.git
-    GIT_TAG        v1.15.2
-)
-
-FetchContent_MakeAvailable(tree-sitter SDL3 imgui imnodes googletest)
+FetchContent_MakeAvailable(tree-sitter SDL3 imgui imnodes)
+
+if(BUILD_TESTING)
+    FetchContent_Declare(
+        googletest
+        GIT_REPOSITORY https://github.com/google/googletest.git
+        GIT_TAG        v1.15.2
+    )
+    FetchContent_MakeAvailable(googletest)
+endif()
@@
-enable_testing()
-
-add_executable(AlgoTests
-    tests/algo/test_reading_sequencer.cpp
-    tests/algo/test_pagerank.cpp
-    tests/algo/test_score_combiner.cpp
-    tests/algo/test_betweenness_centrality.cpp
-    tests/algo/test_algo_merge.cpp
-)
-target_include_directories(AlgoTests PRIVATE src)
-target_link_libraries(AlgoTests PRIVATE algo GTest::gtest_main)
-add_test(NAME AlgoTests COMMAND AlgoTests)
+if(BUILD_TESTING)
+    enable_testing()
+
+    add_executable(AlgoTests
+        tests/algo/test_reading_sequencer.cpp
+        tests/algo/test_pagerank.cpp
+        tests/algo/test_score_combiner.cpp
+        tests/algo/test_betweenness_centrality.cpp
+        tests/algo/test_algo_merge.cpp
+    )
+    target_include_directories(AlgoTests PRIVATE src)
+    target_link_libraries(AlgoTests PRIVATE algo GTest::gtest_main)
+    add_test(NAME AlgoTests COMMAND AlgoTests)
+endif()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@CMakeLists.txt` around lines 48 - 55, The CMake fetch and build of googletest
and the test target AlgoTests are unconditional; gate these behind CMake's
BUILD_TESTING option by wrapping the
FetchContent_Declare(FetchContent_MakeAvailable(googletest)) and the AlgoTests
target/add_subdirectory logic in an if(BUILD_TESTING) ... endif() block, ensure
you call include(CTest) or enable_testing() earlier so BUILD_TESTING is defined,
and only add_link_libraries or add_test entries for AlgoTests when BUILD_TESTING
is ON.
src/algo/AlgoRunner.cpp (1)

148-148: 💤 Low value

Return value from AlgoDbWriter::write() is discarded.

If persistence fails, the caller receives valid in-memory results without knowing the DB write failed. Consider propagating the error or logging a warning.

♻️ Suggested improvement
-    AlgoDbWriter::write(dbPath, merged, cfg);
+    int write_rc = AlgoDbWriter::write(dbPath, merged, cfg);
+    if (write_rc != SQLITE_OK) {
+        std::fprintf(stderr, "AlgoRunner: DB write failed with code %d\n", write_rc);
+    }
     return merged;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/algo/AlgoRunner.cpp` at line 148, The call to AlgoDbWriter::write(dbPath,
merged, cfg) in AlgoRunner.cpp currently drops its return value; update the
caller to check the returned status/result from AlgoDbWriter::write and handle
failures by either returning an error up the call chain or at minimum logging a
warning/error via the existing logging mechanism; specifically, detect a
non-success return from AlgoDbWriter::write and then propagate that error from
the surrounding function (or convert it into an appropriate exception/Status) or
call the logger to record the DB write failure along with context (dbPath, cfg
or merged identifier) so callers are aware the persistence step failed.
src/algo/ReadingSequencer.cpp (1)

56-63: ⚡ Quick win

Add a deterministic tie-breaker for equal SCC scores.

When super_score[a] == super_score[b], pop order is left to heap internals. Adding a secondary key (e.g., SCC index) keeps output stable across environments.

Suggested fix
     auto cmp_super = [&](int a, int b) {
-        return super_score[a] < super_score[b];  // max-heap
+        if (super_score[a] != super_score[b]) return super_score[a] < super_score[b];
+        return a > b; // smaller SCC index first for deterministic ordering
     };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/algo/ReadingSequencer.cpp` around lines 56 - 63, The comparator cmp_super
and priority_queue pq use only super_score, which leaves ties non-deterministic;
change cmp_super to break ties deterministically by comparing the SCC index
(e.g., if super_score[a] != super_score[b] return super_score[a] <
super_score[b]; else return a > b to keep the highest-index popped last) so that
pq ordering is stable; update the lambda used to construct pq (cmp_super) and
ensure the same tie-break logic is used anywhere pq.push/pop is relied on
(references: cmp_super, super_score, pq, S, indegree).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/algo_main.cpp`:
- Around line 14-19: Wrap the calls to AlgoDbWriter::loadConfig(dbPath) and
AlgoRunner::run(dbPath, cfg) in a try/catch block that catches std::exception
(and a catch-all) to prevent abrupt termination; on error print a clear
diagnostic to stderr (including the exception.what() or a descriptive message)
via std::fprintf and return a non-zero exit code instead of 0. Ensure you still
print the successful "computed ... entries" message using result.entries.size()
when no exception occurs, and map different failure types to distinct non-zero
codes if useful (e.g., config load failure vs. run failure) while keeping the
main function returning controlled error codes.

In `@src/algo/AlgoDbWriter.cpp`:
- Around line 63-74: The upsert function ignores return codes from
sqlite3_prepare_v2 and sqlite3_step so DB errors are lost; change upsert to
return an int status, check and return the result of sqlite3_prepare_v2 and
sqlite3_step (and handle bind failures), call sqlite3_finalize in all paths, and
include sqlite3_errmsg(db) in any logged error; then update updateConfig to
check upsert's return value and propagate or log errors instead of always
returning SQLITE_OK.

In `@src/algo/BetweennessCentrality.cpp`:
- Around line 79-87: The code can divide by zero when samples == 0 (k <= 0);
modify the scaling block in BetweennessCentrality.cpp to guard against samples
== 0 before computing scale and multiplying bc. Specifically, using the existing
symbols (samples, N, k, bc, scale, order, brandes_source), add a check like "if
(samples == 0) return;" or skip the scaling loop when samples == 0 so you never
compute N / samples; ensure other logic (the brandes_source loop) remains
unchanged because it already won't run when samples == 0.

In `@src/algo/Graph.h`:
- Around line 3-7: The header defines NodeId as uint32_t but doesn't include
<cstdint>, relying on transitive includes; update src/algo/Graph.h to add an
explicit `#include` <cstdint> so uint32_t is defined in this header (i.e., ensure
the file that declares NodeId includes <cstdint> before using uint32_t).

In `@src/algo/GraphBuilder.cpp`:
- Around line 103-106: The code reads CALLS/INHERITS rows via
sqlite3_prepare_v2/stmt and inserts every returned (source_id, target_id) row,
causing parallel function-level edges; fix by deduplicating before insertion:
while iterating sqlite3_step results (after extracting source_id and target_id),
maintain a local unordered_set (or std::set) of seen pairs (e.g.,
pair<uint64_t,uint64_t> or a combined key string/uint64) and only perform the
existing insertion logic if the pair is not already in the set, skipping
duplicates so each function-level dependency is inserted once.

In `@src/algo/ReadingSequencer.cpp`:
- Around line 27-31: The code assumes combined_score has at least one element
per graph node when computing super_score and later uses combined_score[u]; add
an explicit guard before indexing: verify combined_score.size() >= g.size() (or
check u < combined_score.size() inside the loops over sccs) and handle the
failure path (e.g., throw a descriptive exception or return an error) to avoid
out-of-bounds access; update the loops that reference combined_score (the loop
computing super_score with symbols super_score, sccs, NodeId, S and the later
uses around lines 73–76) to perform this check before any combined_score[u]
read.

In `@src/algo/Scoring.cpp`:
- Around line 89-101: The code computes combined scores from pr and bc without
validating inputs: ensure pr.size() == bc.size() before proceeding and
return/throw if they differ, and check that total = alpha + beta is not zero
before dividing (handle zero by returning empty, using equal weights, or
signaling an error). Validate sizes at start using pr and bc, compute total =
alpha + beta and if total == 0 handle it, then compute w_pr = alpha/total and
w_bc = beta/total and call minmax_normalize(pr)/minmax_normalize(bc) and
populate score vector as before (referencing symbols pr, bc, alpha, beta, total,
w_pr, w_bc, minmax_normalize, score).

---

Nitpick comments:
In `@CMakeLists.txt`:
- Around line 48-55: The CMake fetch and build of googletest and the test target
AlgoTests are unconditional; gate these behind CMake's BUILD_TESTING option by
wrapping the FetchContent_Declare(FetchContent_MakeAvailable(googletest)) and
the AlgoTests target/add_subdirectory logic in an if(BUILD_TESTING) ... endif()
block, ensure you call include(CTest) or enable_testing() earlier so
BUILD_TESTING is defined, and only add_link_libraries or add_test entries for
AlgoTests when BUILD_TESTING is ON.

In `@src/algo/AlgoRunner.cpp`:
- Line 148: The call to AlgoDbWriter::write(dbPath, merged, cfg) in
AlgoRunner.cpp currently drops its return value; update the caller to check the
returned status/result from AlgoDbWriter::write and handle failures by either
returning an error up the call chain or at minimum logging a warning/error via
the existing logging mechanism; specifically, detect a non-success return from
AlgoDbWriter::write and then propagate that error from the surrounding function
(or convert it into an appropriate exception/Status) or call the logger to
record the DB write failure along with context (dbPath, cfg or merged
identifier) so callers are aware the persistence step failed.

In `@src/algo/ReadingSequencer.cpp`:
- Around line 56-63: The comparator cmp_super and priority_queue pq use only
super_score, which leaves ties non-deterministic; change cmp_super to break ties
deterministically by comparing the SCC index (e.g., if super_score[a] !=
super_score[b] return super_score[a] < super_score[b]; else return a > b to keep
the highest-index popped last) so that pq ordering is stable; update the lambda
used to construct pq (cmp_super) and ensure the same tie-break logic is used
anywhere pq.push/pop is relied on (references: cmp_super, super_score, pq, S,
indegree).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0a3cfaaa-b629-4fe5-b213-e432d44ebcae

📥 Commits

Reviewing files that changed from the base of the PR and between 6d6a985 and 0bf8ac5.

📒 Files selected for processing (19)
  • CMakeLists.txt
  • imgui.ini
  • src/algo/AlgoConfig.h
  • src/algo/AlgoDbWriter.cpp
  • src/algo/AlgoDbWriter.h
  • src/algo/AlgoRunner.cpp
  • src/algo/AlgoRunner.h
  • src/algo/BetweennessCentrality.cpp
  • src/algo/BetweennessCentrality.h
  • src/algo/Graph.cpp
  • src/algo/Graph.h
  • src/algo/GraphBuilder.cpp
  • src/algo/GraphBuilder.h
  • src/algo/ReadingSequencer.cpp
  • src/algo/ReadingSequencer.h
  • src/algo/Scoring.cpp
  • src/algo/Scoring.h
  • src/algo_main.cpp
  • src/db/db.cpp

Comment thread src/algo_main.cpp Outdated
Comment thread src/algo/AlgoDbWriter.cpp Outdated
Comment thread src/algo/BetweennessCentrality.cpp
Comment thread src/algo/Graph.h
Comment thread src/algo/GraphBuilder.cpp
Comment thread src/algo/ReadingSequencer.cpp
Comment thread src/algo/Scoring.cpp
@c5ln
c5ln merged commit 731ffc7 into main May 18, 2026
1 check passed
@c5ln
c5ln deleted the feat/18-reading-sequence branch May 18, 2026 12:37
@c5ln c5ln changed the title Feat/18 reading sequence feat: 18-reading sequence Jun 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement functions to recommend the reading sequence

1 participant