Skip to content

feat(core): add accumulator-based fold routing and edge-case hardening - #540

Merged
knoepfel merged 5 commits into
Framework-R-D:mainfrom
knoepfel:fix-fold-caching
Aug 6, 2026
Merged

feat(core): add accumulator-based fold routing and edge-case hardening#540
knoepfel merged 5 commits into
Framework-R-D:mainfrom
knoepfel:fix-fold-caching

Conversation

@knoepfel

@knoepfel knoepfel commented Apr 22, 2026

Copy link
Copy Markdown
Member

This PR replaces the caching mechanism for fold results with a similar mechanism as used in #118.


Resolves #359

  • Core

    • Replace fold-result cache and store-counter handling with accumulator-based processing.
    • Add accumulator_node and fold_join_node to aggregate partition results and synchronize fold inputs.
    • Route fold partition messages through multilayer_join_node-style layer and counting metadata.
    • Update index_router finalization and end-token routing for explicit fold, unfold, provider, and layer-path metadata.
    • Add partition-layer support to declared_fold and replace flush_port() with partition_port().
    • Remove obsolete flush_message, flusher_t, store_counters, and related APIs.
    • Add sendable_type traits and update the sendable concept.
    • Add layer_path::depth() and correct the unfold child counter.
  • Build

    • Stop compiling and installing the removed store-counter sources.
  • Tests

    • Add Catch2/TBB coverage for accumulator emission, partition accumulation, queued index messages, zero-cell flushes, and unflushed-cache warnings.
    • Register the new accumulator CTest target.
  • Maintenance

    • Update repeater cleanup to emit cached products only when valid data and completion conditions exist.

@codecov

codecov Bot commented Apr 22, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.20455% with 38 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
phlex/core/index_router.cpp 90.24% 9 Missing and 3 partials ⚠️
phlex/core/fold_join_node.hpp 80.39% 8 Missing and 2 partials ⚠️
phlex/core/detail/accumulator_node.hpp 91.83% 1 Missing and 7 partials ⚠️
phlex/core/framework_graph.cpp 86.48% 1 Missing and 4 partials ⚠️
phlex/core/declared_fold.hpp 94.28% 2 Missing ⚠️
phlex/core/detail/repeater_node.cpp 0.00% 0 Missing and 1 partial ⚠️
@@            Coverage Diff             @@
##             main     #540      +/-   ##
==========================================
+ Coverage   83.89%   84.03%   +0.13%     
==========================================
  Files         171      172       +1     
  Lines        7234     7415     +181     
  Branches      850      872      +22     
==========================================
+ Hits         6069     6231     +162     
- Misses        881      896      +15     
- Partials      284      288       +4     
Flag Coverage Δ
scripts 80.09% <ø> (ø)
unittests 85.92% <89.20%> (+0.13%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
phlex/core/declared_fold.cpp 100.00% <100.00%> (ø)
phlex/core/declared_observer.hpp 100.00% <ø> (ø)
phlex/core/declared_unfold.cpp 100.00% <100.00%> (ø)
phlex/core/declared_unfold.hpp 96.66% <100.00%> (+0.23%) ⬆️
phlex/core/fold/send.hpp 100.00% <ø> (ø)
phlex/core/framework_graph.hpp 100.00% <100.00%> (ø)
phlex/core/index_router.hpp 100.00% <ø> (ø)
phlex/core/message.hpp 100.00% <ø> (ø)
phlex/core/multilayer_join_node.hpp 88.00% <100.00%> (ø)
phlex/model/layer_path.cpp 97.56% <100.00%> (+0.06%) ⬆️
... and 7 more

... and 5 files with indirect coverage changes


Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 240a461...cab459e. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@knoepfel
knoepfel force-pushed the fix-fold-caching branch 2 times, most recently from 7a865dc to 3d19c63 Compare April 23, 2026 15:12
@knoepfel
knoepfel marked this pull request as draft April 23, 2026 15:12
@knoepfel
knoepfel force-pushed the fix-fold-caching branch from 3d19c63 to 530eb15 Compare May 26, 2026 17:34
@knoepfel
knoepfel marked this pull request as ready for review May 26, 2026 17:39
@knoepfel
knoepfel force-pushed the fix-fold-caching branch from 530eb15 to c3caf2f Compare May 26, 2026 20:48
@Framework-R-D Framework-R-D deleted a comment from github-actions Bot May 26, 2026
@knoepfel
knoepfel force-pushed the fix-fold-caching branch from c3caf2f to 197de5e Compare May 26, 2026 21:43
@Framework-R-D Framework-R-D deleted a comment from github-actions Bot May 26, 2026
@knoepfel
knoepfel force-pushed the fix-fold-caching branch from 197de5e to 10aac8e Compare May 27, 2026 14:41
@Framework-R-D Framework-R-D deleted a comment from github-actions Bot May 27, 2026
@knoepfel
knoepfel force-pushed the fix-fold-caching branch 3 times, most recently from 322927f to decf3ae Compare June 2, 2026 20:58
@Framework-R-D Framework-R-D deleted a comment from github-actions Bot Jun 3, 2026
@Framework-R-D Framework-R-D deleted a comment from github-actions Bot Jun 3, 2026
@Framework-R-D Framework-R-D deleted a comment from github-actions Bot Jun 3, 2026
@Framework-R-D Framework-R-D deleted a comment from github-actions Bot Jun 3, 2026
@knoepfel
knoepfel force-pushed the fix-fold-caching branch from decf3ae to e7eb445 Compare June 4, 2026 18:27
@Framework-R-D Framework-R-D deleted a comment from github-actions Bot Jun 4, 2026
@knoepfel
knoepfel force-pushed the fix-fold-caching branch 2 times, most recently from e564fb3 to 5471faa Compare June 8, 2026 18:24
@Framework-R-D Framework-R-D deleted a comment from github-actions Bot Jun 8, 2026
@knoepfel
knoepfel force-pushed the fix-fold-caching branch 2 times, most recently from 34e1b5e to e106e73 Compare June 15, 2026 17:09
@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5f2db25a-d407-4bb8-aead-23a4fd3340fc

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR replaces store-counter fold caching with accumulator_node and fold_join_node. It updates fold, index-router, and framework-graph integration to use partition and unfold metadata, removes obsolete flush interfaces, and adds accumulator tests.

Changes

Fold pipeline refactor

Layer / File(s) Summary
Sendable type selection
phlex/core/fold/send.hpp, phlex/core/concepts.hpp
Adds sendable type selection and updates the sendable concept.
Accumulator and fold joining
phlex/core/detail/accumulator_node.hpp, phlex/core/fold_join_node.hpp, phlex/core/detail/repeater_node.cpp
Adds partition-aware accumulation, deferred index handling, flush completion, result emission, and fold/data joining.
Declared fold integration
phlex/core/declared_fold.*, phlex/core/CMakeLists.txt, phlex/core/declared_observer.hpp
Updates fold construction and ports, removes store-counter state, and removes store-counter build and installation entries.
Message and routing contracts
phlex/core/message.hpp, phlex/core/fwd.hpp, phlex/core/multilayer_join_node.hpp, phlex/core/index_router.hpp, phlex/model/layer_path.*
Removes obsolete flush types, adds counting-layer metadata, and exposes layer-path depth.
Index-router and graph wiring
phlex/core/index_router.*, phlex/core/framework_graph.*, phlex/core/declared_unfold.*
Reworks hierarchy finalization, fold-partition routing, end-token generation, and unfold metadata handling.
Accumulator validation
test/CMakeLists.txt, test/accumulator_test.cpp
Adds tests for flush ordering, multiple partitions, queued indexes, destructor warnings, and zero-cell results.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Suggested reviewers: greenc-fnal, beojan

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR replaces fold-result caching, but it introduces fold_join_node instead of changing caching to use multilayer_join_node as required by issue #359. Use multilayer_join_node for fold-result caching, or update the linked issue if fold_join_node is the intended replacement.
Docstring Coverage ⚠️ Warning Docstring coverage is 6.93% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the accumulator-based fold routing and related edge-case fixes.
Out of Scope Changes check ✅ Passed The changes support accumulator-based fold routing, cache removal, related metadata, edge cases, and tests; no clearly unrelated code changes are present.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@Framework-R-D Framework-R-D deleted a comment from github-actions Bot Jun 15, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🤖 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 `@phlex/core/concepts.hpp`:
- Around line 20-21: The `sendable` concept's constraint `sendable_type<T>{t}`
is ill-formed for `std::atomic<T>` types because direct brace-initialization
from an atomic reference is not allowed (e.g., `int{atomic_const_ref}` fails).
Fix this by reverting to the previous constraint approach that used `{ send(t) }
-> std::move_constructible`, which properly handles type conversion through the
`send()` function, or alternatively verify that `sendable_type<T>` exists
without attempting direct initialization.

In `@phlex/core/declared_fold.hpp`:
- Line 149: Remove the unused member variable `initializer_` from the class
definition in declared_fold.hpp. This member variable is default-constructed and
never referenced in the class implementation, making it leftover code. The
`initializer` constructor parameter is already being passed directly to
`make_initializer` and `join_`, so the member variable is not needed. Simply
delete the `InitTuple initializer_;` declaration line.
- Line 157: The member variable `product_count_` in the class is declared with
an accessor but never incremented, unlike the similar implementations in
declared_unfold.hpp and declared_transform.hpp. Either remove the
`product_count_` member variable and its corresponding accessor method if this
metric is not applicable to the fold pipeline design, or implement the logic to
increment `product_count_` at the appropriate point in the fold operation
processing to ensure it actually tracks the product count as intended,
maintaining consistency with the other declared pipeline classes.

In `@phlex/core/detail/accumulator_node.hpp`:
- Around line 295-304: The cleanup_cache_entry method dereferences
entry->accumulator_msg without checking if it is null. When flush_received is
true and counter is zero, but the partition message has not yet arrived,
accumulator_msg remains null, causing a null pointer dereference. Add a null
pointer check for entry->accumulator_msg before calling release_as_message on
it, and only proceed with the try_put and cached_results_.erase operations if
accumulator_msg is not null.

In `@phlex/core/fold_join_node.hpp`:
- Line 67: Fix the typo in the comment on line 67 of the fold_join_node.hpp
file. Change "qacross" to "across" in the comment that describes message
matching behavior. The corrected comment should read "Messages with the same ID
across" instead of "Messages with the same ID qacross".

In `@phlex/core/fold/send.hpp`:
- Around line 27-38: The two specializations of sendable_type_impl can become
ambiguous if a type is both std::move_constructible and has a valid send()
overload. To make these specializations mutually exclusive, add a requires
clause to the first sendable_type_impl specialization (the one that matches
std::move_constructible T) that explicitly excludes types satisfying the send()
requirement. This requires clause should check that the type does not satisfy
the condition in the second specialization, ensuring only one specialization can
match for any given type.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 26a2d1c9-a41c-4558-90d8-009f33711ab4

📥 Commits

Reviewing files that changed from the base of the PR and between 6ab0734 and e106e73.

📒 Files selected for processing (25)
  • .amazonq/rules/memory-bank/structure.md
  • phlex/core/CMakeLists.txt
  • phlex/core/concepts.hpp
  • phlex/core/declared_fold.cpp
  • phlex/core/declared_fold.hpp
  • phlex/core/declared_observer.hpp
  • phlex/core/declared_unfold.cpp
  • phlex/core/declared_unfold.hpp
  • phlex/core/detail/accumulator_node.hpp
  • phlex/core/fold/send.hpp
  • phlex/core/fold_join_node.hpp
  • phlex/core/framework_graph.cpp
  • phlex/core/framework_graph.hpp
  • phlex/core/fwd.hpp
  • phlex/core/index_router.cpp
  • phlex/core/index_router.hpp
  • phlex/core/message.hpp
  • phlex/core/multilayer_join_node.hpp
  • phlex/core/store_counters.cpp
  • phlex/core/store_counters.hpp
  • phlex/model/flush_gate.cpp
  • phlex/model/flush_gate.hpp
  • test/CMakeLists.txt
  • test/accumulator_test.cpp
  • test/flush_gate_test.cpp
💤 Files with no reviewable changes (6)
  • phlex/core/declared_observer.hpp
  • phlex/core/store_counters.hpp
  • phlex/core/CMakeLists.txt
  • phlex/core/store_counters.cpp
  • phlex/core/framework_graph.hpp
  • phlex/core/fwd.hpp
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: build (gcc, none)
  • GitHub Check: Analyze cpp with CodeQL
  • GitHub Check: clang-tidy-check
  • GitHub Check: coverage
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{cpp,cc,cxx,h,hpp}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{cpp,cc,cxx,h,hpp}: Use clang-format tool for all C++ code formatting (VS Code auto-formats on save); configuration defined in .clang-format with 100-character line limit and 2-space indentation
Follow clang-tidy recommendations defined in .clang-tidy

Files:

  • phlex/core/fold/send.hpp
  • phlex/core/declared_unfold.cpp
  • phlex/core/declared_fold.cpp
  • phlex/core/concepts.hpp
  • phlex/core/multilayer_join_node.hpp
  • phlex/model/flush_gate.hpp
  • phlex/core/declared_unfold.hpp
  • test/flush_gate_test.cpp
  • phlex/core/fold_join_node.hpp
  • phlex/core/detail/accumulator_node.hpp
  • test/accumulator_test.cpp
  • phlex/model/flush_gate.cpp
  • phlex/core/framework_graph.cpp
  • phlex/core/message.hpp
  • phlex/core/index_router.hpp
  • phlex/core/declared_fold.hpp
  • phlex/core/index_router.cpp
**/*.{hpp,cpp}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{hpp,cpp}: Use .hpp for C++ header files and .cpp for implementation files; test files should be named *_test.cpp
All C++ identifiers (namespaces, classes, structs, enums, functions, variables, parameters, members, constants, type aliases) must use lower_case naming
Template parameters in C++ must use CamelCase naming
C++ macros must use UPPER_CASE naming
Private, protected, and constant C++ members must have a trailing underscore (_)
Use east-const style in C++: int const x not const int x
Prefer enum class over plain enum in C++
Avoid boolean parameters in C++ function interfaces; prefer enumerations instead
Use std::shared_ptr for shared ownership, std::unique_ptr for exclusive ownership, and raw pointers only for non-owning references in C++
C++ clang-format enforces 100-character line limit and 2-space indentation with QualifierAlignment: Right and PointerAlignment: Left
Use namespaces phlex:: for core code and phlex::experimental:: for experimental features in C++
Use functors with agent noun naming (e.g., ModelEvaluator evaluate_model(...)) in C++

Files:

  • phlex/core/fold/send.hpp
  • phlex/core/declared_unfold.cpp
  • phlex/core/declared_fold.cpp
  • phlex/core/concepts.hpp
  • phlex/core/multilayer_join_node.hpp
  • phlex/model/flush_gate.hpp
  • phlex/core/declared_unfold.hpp
  • test/flush_gate_test.cpp
  • phlex/core/fold_join_node.hpp
  • phlex/core/detail/accumulator_node.hpp
  • test/accumulator_test.cpp
  • phlex/model/flush_gate.cpp
  • phlex/core/framework_graph.cpp
  • phlex/core/message.hpp
  • phlex/core/index_router.hpp
  • phlex/core/declared_fold.hpp
  • phlex/core/index_router.cpp
🪛 Cppcheck (2.20.0)
test/accumulator_test.cpp

[style] 24-24: The function 'convert' is never used.

(unusedFunction)

🔇 Additional comments (29)
phlex/core/detail/accumulator_node.hpp (1)

1-186: LGTM!

Also applies to: 206-237, 239-293

phlex/core/fold_join_node.hpp (1)

1-66: LGTM!

Also applies to: 68-171, 173-233

phlex/core/declared_fold.hpp (1)

38-54: LGTM!

Also applies to: 59-70, 74-148, 150-156, 158-158

phlex/core/declared_fold.cpp (1)

1-15: LGTM!

phlex/core/message.hpp (1)

52-77: LGTM!

phlex/core/multilayer_join_node.hpp (1)

114-117: LGTM!

phlex/core/index_router.hpp (1)

27-65: LGTM!

Also applies to: 85-98, 103-125, 148-184, 190-226

phlex/core/index_router.cpp (8)

45-106: LGTM!


108-126: LGTM!


128-143: LGTM!


199-258: LGTM!


362-450: LGTM!


452-513: LGTM!


577-611: LGTM!


175-175: This is not a concern—the project explicitly targets C++23 as its baseline standard (per AGENTS.md and CLANG_TIDY_CONFIGURATION.md), and std::ranges::contains is already used elsewhere in the codebase without issue (e.g., phlex/model/fixed_hierarchy.cpp:24). No action needed.

phlex/core/framework_graph.cpp (4)

17-41: LGTM!


43-75: LGTM!


218-223: LGTM!


122-128: LGTM!

phlex/core/declared_unfold.hpp (1)

45-55: LGTM!

test/accumulator_test.cpp (3)

20-80: LGTM!


81-117: LGTM!


120-268: LGTM!

phlex/core/declared_unfold.cpp (1)

23-23: LGTM!

test/CMakeLists.txt (1)

14-14: LGTM!

test/flush_gate_test.cpp (1)

29-29: LGTM!

Also applies to: 225-231

.amazonq/rules/memory-bank/structure.md (1)

19-19: LGTM!

phlex/model/flush_gate.hpp (1)

43-43: LGTM!

Also applies to: 61-61, 70-70, 88-88

phlex/model/flush_gate.cpp (1)

11-11: LGTM!

Also applies to: 22-22, 28-28, 38-42, 86-86

Comment thread phlex/core/concepts.hpp Outdated
Comment thread phlex/core/declared_fold.hpp Outdated
Comment thread phlex/core/declared_fold.hpp Outdated
Comment thread phlex/core/detail/accumulator_node.hpp
Comment thread phlex/core/fold_join_node.hpp Outdated
Comment thread phlex/core/fold/send.hpp Outdated
@knoepfel
knoepfel force-pushed the fix-fold-caching branch 5 times, most recently from 1f1e0f2 to a5c2559 Compare August 5, 2026 22:10
@knoepfel knoepfel changed the title Express fold nodes using accumulator nodes feat(core): add accumulator-based fold routing and edge-case hardening Aug 5, 2026
@knoepfel

knoepfel commented Aug 5, 2026

Copy link
Copy Markdown
Member Author

@CodeRabbit review

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 13

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
phlex/core/index_router.cpp (1)

371-405: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Avoid sending flush tokens from join nodes that do not route this index.

end_token_entries are populated in the per-slot loop, but message_slots only commits inside the has_exact_match and matched_count == slots.size() gate. A join node where one slot is neither exact nor a parent therefore skips the index message while still sending indexed_end_token entries at flush time. These tokens create cache entries with negative counters; both repeater_node and accumulator_node erase entries only when counter == 0, so an early unmatched flush can keep the cache entry alive and mask later decrements.

Buffer end-token entries in the node-local matching_slots loop and append them only inside the same gate that appends message_slots.

🤖 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 `@phlex/core/index_router.cpp` around lines 371 - 405, Buffer the flush-derived
end-token entries alongside the matched slots in the per-slot loop, and only
append them to the node’s committed end-token collection inside the existing
has_exact_match && matched_count == slots.size() gate. Ensure slots that are
neither exact matches nor parents produce no committed tokens, while preserving
the current counting-layer hash generation for valid matches.
🤖 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 `@phlex/core/declared_fold.hpp`:
- Around line 106-118: Update the fold_ node declaration and its lambda to use a
TBB function_node returning continue_msg, removing the unused output-port
parameter and the dead message_tuple<1> output declaration. Preserve the
existing apply_fold, calls_, and notify_result_repeater_port behavior; add a
brief comment only if the output port is intentionally retained for future use.
- Around line 62-68: Wrap the long explanatory comment above the constexpr
branch to keep each line within 100 columns. In the non-empty branch of the
lambda returned by the surrounding fold-building function, move-capture the
by-value args tuple instead of copy-capturing it, while preserving the existing
std::get<Is>(args)... expansion behavior.
- Around line 136-147: The apply_fold path currently allows concurrent calls on
the same shared partial_result; update fold_node/fold_ handling to prevent this.
Add per-partition synchronization around partial_result->call (covering the
shared accumulator state), or reject configurations where the requested
concurrency is unsafe for the fold type, while preserving parallelism across
independent partitions.

In `@phlex/core/detail/accumulator_node.hpp`:
- Around line 39-42: Update accumulator<Result>::release_as_product() to make
phlex::experimental::send visible before the compile-time branch and gate the
branch with phlex::experimental::has_send<Result>. Preserve unqualified
send(*result) invocation so custom sendable overloads participate, while atomic
results use the load-based send path instead of moving the value.

In `@phlex/core/fold_join_node.hpp`:
- Around line 141-150: Update fold_join_node::index_ports so it never selects
layers_[0] as the counting layer for multi-input folds; resolve and pass the
actual deepest input layer into fold_join_node before wiring the partition slot,
or explicitly reject multi-input folds until resolution is available. Preserve
partition_layer for empty layers and ensure the router counter uses the resolved
deepest layer.

In `@phlex/core/framework_graph.cpp`:
- Around line 64-74: Update the unfold-processing loop over n->input() so each
unique input_layer is counted and added to result.layer_pairs only once per
unfold, even when multiple selectors share that layer. Deduplicate within each
unfold before incrementing result.count_per_input_layer or appending the pair,
and add coverage for an unfold with two selectors from the same layer.

In `@phlex/core/index_router.cpp`:
- Around line 257-263: Update indexed_end_token::count and all corresponding
flush-token handling, including repeater_node::handle_flush_token, to use
std::ptrdiff_t so the committed count from committed_count_for_layer is not
narrowed. Remove the static_cast<int> in the set_flush_callback lambda and
preserve the existing count propagation behavior.
- Around line 180-189: Clean up wire_fold_partition_index_sets by iterating
fold_partition_ports values without binding the unused fold_node_name, and use
partition_port.layer directly when emplacing into index_set_nodes_ instead of
the redundant static_cast. Preserve the existing node creation and make_edge
behavior.
- Around line 126-152: Bound expansion in establish_layer_hierarchy using a
maximum depth derived from the initial deepest layer path plus
layer_pairs.size(), and reject candidates exceeding that bound. Throw a clear
diagnostic naming the offending unfold pair(s) instead of continuing the
fixed-point loop; preserve deduplication and normal hierarchy expansion within
the bound.

In `@phlex/core/index_router.hpp`:
- Around line 120-125: Change finalize’s layer_paths_from_driver parameter to
pass by value, matching establish_layer_hierarchy’s ownership-transfer usage and
allowing the existing std::move call to move the vector instead of copying it.

In `@phlex/core/message.hpp`:
- Around line 72-77: Update named_index_port::counting_layer in
phlex/core/message.hpp:72-77 to std::optional<phlex::experimental::identifier>,
documenting that an empty optional selects the node’s deepest layer and a
populated value selects the specified layer. In
phlex/core/multilayer_join_node.hpp:116-119, pass an empty optional instead of
layer and revise the comment to describe balancing against the node’s deepest
layer.

In `@phlex/model/layer_path.cpp`:
- Line 104: Update the declaration of layer_path::depth() in layer_path.hpp with
a one-sentence comment documenting that it counts stored path segments only and
does not include the implicit job root, unlike hash() and hashes(); leave the
implementation unchanged.

In `@test/accumulator_test.cpp`:
- Around line 62-67: Update use_ostream_logger to save spdlog::default_logger()
before installing the ostream logger, then add scope-bound cleanup that restores
the saved logger before the local ostringstream is destroyed.

---

Outside diff comments:
In `@phlex/core/index_router.cpp`:
- Around line 371-405: Buffer the flush-derived end-token entries alongside the
matched slots in the per-slot loop, and only append them to the node’s committed
end-token collection inside the existing has_exact_match && matched_count ==
slots.size() gate. Ensure slots that are neither exact matches nor parents
produce no committed tokens, while preserving the current counting-layer hash
generation for valid matches.
🪄 Autofix

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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: d9207be0-30aa-482b-bc06-418cc7f17952

📥 Commits

Reviewing files that changed from the base of the PR and between 240a461 and a5c2559.

📒 Files selected for processing (24)
  • phlex/core/CMakeLists.txt
  • phlex/core/concepts.hpp
  • phlex/core/declared_fold.cpp
  • phlex/core/declared_fold.hpp
  • phlex/core/declared_observer.hpp
  • phlex/core/declared_unfold.cpp
  • phlex/core/declared_unfold.hpp
  • phlex/core/detail/accumulator_node.hpp
  • phlex/core/detail/repeater_node.cpp
  • phlex/core/fold/send.hpp
  • phlex/core/fold_join_node.hpp
  • phlex/core/framework_graph.cpp
  • phlex/core/framework_graph.hpp
  • phlex/core/fwd.hpp
  • phlex/core/index_router.cpp
  • phlex/core/index_router.hpp
  • phlex/core/message.hpp
  • phlex/core/multilayer_join_node.hpp
  • phlex/core/store_counters.cpp
  • phlex/core/store_counters.hpp
  • phlex/model/layer_path.cpp
  • phlex/model/layer_path.hpp
  • test/CMakeLists.txt
  • test/accumulator_test.cpp
💤 Files with no reviewable changes (5)
  • phlex/core/CMakeLists.txt
  • phlex/core/declared_observer.hpp
  • phlex/core/store_counters.hpp
  • phlex/core/store_counters.cpp
  • phlex/core/fwd.hpp
📜 Review details
⏰ Context from checks skipped due to timeout. (4)
  • GitHub Check: Analyze cpp with CodeQL
  • GitHub Check: build (gcc, none)
  • GitHub Check: clang-tidy-check
  • GitHub Check: coverage
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{cpp,cc,cxx,h,hpp}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{cpp,cc,cxx,h,hpp}: Use clang-format tool for all C++ code formatting (VS Code auto-formats on save); configuration defined in .clang-format with 100-character line limit and 2-space indentation
Follow clang-tidy recommendations defined in .clang-tidy

Files:

  • phlex/model/layer_path.hpp
  • phlex/core/detail/repeater_node.cpp
  • phlex/core/declared_unfold.cpp
  • phlex/core/declared_fold.cpp
  • phlex/core/multilayer_join_node.hpp
  • phlex/core/concepts.hpp
  • phlex/model/layer_path.cpp
  • phlex/core/declared_unfold.hpp
  • phlex/core/framework_graph.cpp
  • phlex/core/message.hpp
  • test/accumulator_test.cpp
  • phlex/core/fold/send.hpp
  • phlex/core/framework_graph.hpp
  • phlex/core/detail/accumulator_node.hpp
  • phlex/core/fold_join_node.hpp
  • phlex/core/declared_fold.hpp
  • phlex/core/index_router.hpp
  • phlex/core/index_router.cpp
**/*.{hpp,cpp}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{hpp,cpp}: Use .hpp for header files, .cpp for implementation, and *_test.cpp for test files in C++
Enforce 100-character line limit and 2-space indentation in C++ code via .clang-format
Use QualifierAlignment: Right (east-const) style: int const x not const int x in C++
Use PointerAlignment: Left in C++ (pointer * attached to type, not variable name)
All C++ identifiers must use lower_case naming: namespaces, classes, structs, enums, functions, variables, parameters, members, and constants
Exception to C++ naming: template parameters use CamelCase
Exception to C++ naming: macros use UPPER_CASE
Private, protected, and constant members in C++ must have a trailing underscore (_), no trailing underscore on anything else
Use enum class preferred over plain enum in C++
Use std::shared_ptr for shared ownership, std::unique_ptr for exclusive ownership, raw pointers for non-owning references only in C++
Use functors with agent-noun pattern: ModelEvaluator evaluate_model(...) in C++
Apply .clang-tidy checks for bugprone, cert, clang-analyzer, concurrency, cppcoreguidelines, misc, modernize, performance, portability, and readability as defined in the .clang-tidy configuration file
Use phlex:: namespace for core code, phlex::experimental:: for experimental features in C++

Files:

  • phlex/model/layer_path.hpp
  • phlex/core/detail/repeater_node.cpp
  • phlex/core/declared_unfold.cpp
  • phlex/core/declared_fold.cpp
  • phlex/core/multilayer_join_node.hpp
  • phlex/core/concepts.hpp
  • phlex/model/layer_path.cpp
  • phlex/core/declared_unfold.hpp
  • phlex/core/framework_graph.cpp
  • phlex/core/message.hpp
  • test/accumulator_test.cpp
  • phlex/core/fold/send.hpp
  • phlex/core/framework_graph.hpp
  • phlex/core/detail/accumulator_node.hpp
  • phlex/core/fold_join_node.hpp
  • phlex/core/declared_fold.hpp
  • phlex/core/index_router.hpp
  • phlex/core/index_router.cpp
**/*.hpp

📄 CodeRabbit inference engine (AGENTS.md)

Avoid boolean parameters in C++ interfaces; prefer enumerations instead

Files:

  • phlex/model/layer_path.hpp
  • phlex/core/multilayer_join_node.hpp
  • phlex/core/concepts.hpp
  • phlex/core/declared_unfold.hpp
  • phlex/core/message.hpp
  • phlex/core/fold/send.hpp
  • phlex/core/framework_graph.hpp
  • phlex/core/detail/accumulator_node.hpp
  • phlex/core/fold_join_node.hpp
  • phlex/core/declared_fold.hpp
  • phlex/core/index_router.hpp
🪛 Cppcheck (2.21.0)
phlex/model/layer_path.cpp

[style] 104-104: The function 'depth' is never used.

(unusedFunction)

test/accumulator_test.cpp

[style] 24-24: The function 'convert' is never used.

(unusedFunction)

🔇 Additional comments (22)
phlex/core/fold/send.hpp (1)

12-51: LGTM!

phlex/core/concepts.hpp (1)

21-21: LGTM!

phlex/core/detail/accumulator_node.hpp (1)

50-337: LGTM!

phlex/core/detail/repeater_node.cpp (1)

155-170: LGTM!

phlex/core/fold_join_node.hpp (1)

18-140: LGTM!

Also applies to: 162-234

phlex/core/framework_graph.cpp (1)

18-51: LGTM!

Also applies to: 145-145, 225-252

phlex/core/framework_graph.hpp (1)

79-81: LGTM!

Also applies to: 184-186

phlex/core/declared_unfold.cpp (1)

25-25: LGTM!

phlex/core/declared_unfold.hpp (1)

45-55: LGTM!

test/CMakeLists.txt (1)

14-14: LGTM!

test/accumulator_test.cpp (1)

1-60: LGTM!

Also applies to: 69-209, 254-293

phlex/core/declared_fold.hpp (3)

42-57: LGTM!


121-134: LGTM!


149-155: LGTM!

phlex/core/declared_fold.cpp (1)

6-9: LGTM!

phlex/core/message.hpp (1)

52-71: LGTM!

phlex/model/layer_path.hpp (1)

47-49: LGTM!

phlex/core/index_router.hpp (1)

28-65: LGTM!

Also applies to: 85-99, 148-160, 169-195, 210-229

phlex/core/index_router.cpp (4)

154-167: LGTM!


195-228: LGTM!

Also applies to: 433-463


92-100: LGTM!

Also applies to: 272-285, 294-310, 415-431, 481-490, 516-518, 533-542


246-257: 🩺 Stability & Availability

No change needed.

all_children_accounted() returns false while flush_callback_ is absent, and there is no code path that calls send_flush() without a callback at this point.

Comment thread phlex/core/declared_fold.hpp Outdated
Comment thread phlex/core/declared_fold.hpp
Comment thread phlex/core/declared_fold.hpp
Comment thread phlex/core/detail/accumulator_node.hpp Outdated
Comment thread phlex/core/fold_join_node.hpp
Comment thread phlex/core/index_router.cpp
Comment thread phlex/core/index_router.hpp
Comment thread phlex/core/message.hpp
Comment thread phlex/model/layer_path.cpp
Comment thread test/accumulator_test.cpp
@knoepfel

knoepfel commented Aug 6, 2026

Copy link
Copy Markdown
Member Author

@CodeRabbit, I have addressed multiple issues in the latest (force) push. Please analyze your comments and resolve the comments that are already addressed with that push.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="Framework-R-D/phlex"
pr="540"

echo "== PR metadata =="
gh pr view "$pr" --repo "$repo" --json number,headRefOid,baseRefOid,url

echo "== CodeRabbit review comments =="
gh api --paginate "repos/$repo/pulls/$pr/comments?per_page=100" \
  --jq '.[] | select(.user.login == "coderabbitai[bot]" or .user.login == "coderabbitai") | {id,node_id,path,line,original_line,side,body,html_url,created_at,updated_at}'

echo "== Review threads =="
gh api graphql \
  -F owner="Framework-R-D" \
  -F name="phlex" \
  -F number="$pr" \
  -f query='
query($owner: String!, $name: String!, $number: Int!) {
  repository(owner: $owner, name: $name) {
    pullRequest(number: $number) {
      reviewThreads(first: 100) {
        nodes {
          id
          isResolved
          path
          line
          originalLine
          comments(first: 20) {
            nodes {
              id
              databaseId
              author { login }
              body
              createdAt
              url
            }
          }
        }
      }
    }
  }
}' \
  --jq '.data.repository.pullRequest.reviewThreads.nodes[] | select(any(.comments.nodes[]; .author.login == "coderabbitai" or .author.login == "coderabbitai[bot]"))'

Length of output: 180681


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Current HEAD and parent =="
git rev-parse HEAD
git rev-parse HEAD^

echo "== Relevant source outlines =="
for file in \
  phlex/core/declared_fold.hpp \
  phlex/core/detail/accumulator_node.hpp \
  phlex/core/framework_graph.cpp \
  phlex/core/index_router.cpp \
  phlex/core/index_router.hpp \
  phlex/core/message.hpp \
  phlex/model/layer_path.hpp \
  phlex/model/layer_path.cpp
do
  echo "=== $file ==="
  ast-grep outline "$file" --view compact || true
done

echo "== Current relevant source sections =="
sed -n '45,175p' phlex/core/declared_fold.hpp
sed -n '1,85p' phlex/core/detail/accumulator_node.hpp
sed -n '45,100p' phlex/core/framework_graph.cpp
sed -n '95,310p' phlex/core/index_router.cpp
sed -n '105,145p' phlex/core/index_router.hpp
sed -n '55,90p' phlex/core/message.hpp
sed -n '85,115p' phlex/model/layer_path.hpp
sed -n '90,112p' phlex/model/layer_path.cpp

echo "== Changes in the latest commit for affected files =="
git diff --unified=40 HEAD^ HEAD -- \
  phlex/core/declared_fold.hpp \
  phlex/core/detail/accumulator_node.hpp \
  phlex/core/framework_graph.cpp \
  phlex/core/index_router.cpp \
  phlex/core/index_router.hpp \
  phlex/core/message.hpp \
  phlex/model/layer_path.hpp \
  phlex/model/layer_path.cpp

Length of output: 28393


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== layer_path::depth declaration and documentation =="
rg -n -C 4 '\bdepth\s*\(\s*\)\s*const\s*noexcept' \
  phlex/model/layer_path.hpp phlex/model/layer_path.cpp

echo "== indexed_end_token count declaration and uses =="
rg -n -C 4 '\bindexed_end_token\b|\.count\b|static_cast<int>\(count\)' \
  phlex/core/message.hpp \
  phlex/core/index_router.cpp \
  phlex/core/detail/repeater_node.hpp \
  phlex/core/detail/accumulator_node.hpp

echo "== Resolve verified addressed review threads =="
for thread_id in \
  PRRT_kwDOOauAKM6Wzwxf \
  PRRT_kwDOOauAKM6Wzwxo \
  PRRT_kwDOOauAKM6Wzwxv \
  PRRT_kwDOOauAKM6Wzwx1 \
  PRRT_kwDOOauAKM6Wzwx2 \
  PRRT_kwDOOauAKM6Wzwx5 \
  PRRT_kwDOOauAKM6Wzwx8 \
  PRRT_kwDOOauAKM6WzwyC
do
  gh api graphql \
    -F threadId="$thread_id" \
    -f query='
mutation($threadId: ID!) {
  resolveReviewThread(input: {threadId: $threadId}) {
    thread { id isResolved }
  }
}' \
    --jq '.data.resolveReviewThread.thread'
done

Length of output: 13863


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Resolve addressed depth-documentation thread =="
gh api graphql \
  -F threadId="PRRT_kwDOOauAKM6WzwyF" \
  -f query='
mutation($threadId: ID!) {
  resolveReviewThread(input: {threadId: $threadId}) {
    thread { id isResolved path originalLine }
  }
}' \
  --jq '.data.resolveReviewThread.thread'

echo "== Remaining unresolved CodeRabbit threads =="
gh api graphql \
  -F owner="Framework-R-D" \
  -F name="phlex" \
  -F number="540" \
  -f query='
query($owner: String!, $name: String!, $number: Int!) {
  repository(owner: $owner, name: $name) {
    pullRequest(number: $number) {
      reviewThreads(first: 100) {
        nodes {
          id
          isResolved
          path
          originalLine
          comments(first: 1) {
            nodes { author { login } body url }
          }
        }
      }
    }
  }
}' \
  --jq '.data.repository.pullRequest.reviewThreads.nodes[]
    | select(.isResolved == false)
    | select(.comments.nodes[0].author.login == "coderabbitai" or .comments.nodes[0].author.login == "coderabbitai[bot]")
    | {id,path,originalLine,url: .comments.nodes[0].url,summary: (.comments.nodes[0].body | split("\n") | map(select(length > 0))[1])}'

Length of output: 2020


@knoepfel I verified the latest force push and resolved nine addressed CodeRabbit threads.

Two findings remain open:

  1. phlex/core/declared_fold.hpp: concurrent fold calls can still access one partition’s shared partial_result without synchronization.
  2. phlex/core/index_router.cpp: static_cast<int>(count) still narrows the committed std::size_t count.

I left these two threads open because the current code does not yet address them.

You are interacting with an AI system.

Comment thread test/accumulator_test.cpp
Comment thread phlex/core/fold/send.hpp

@marcpaterno marcpaterno left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

None of the comments in this review should block merging this PR.

@knoepfel
knoepfel merged commit 448de24 into Framework-R-D:main Aug 6, 2026
50 checks passed
@knoepfel
knoepfel deleted the fix-fold-caching branch August 6, 2026 16:27
knoepfel added a commit that referenced this pull request Aug 17, 2026
Replaces storing a committed-counts data member as a shared pointer by storing it by value instead.
Before PR #540 was merged, the committed-counts data member needed to be sent to downstream
nodes.  The type of the member was not copyable, so a shared pointer to that type was used instead.
This has been fixed with this PR.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Change fold-result caching to use multilayer_join_node

2 participants