fix(cascade): fuse heading-nav and TF-IDF signals instead of skipping TF-IDF on a zero-hit - #137
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Team Run ID: 📒 Files selected for processing (3)
Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthroughTier 1 now always runs heading navigation and TF-IDF scoring. The cascade adds seven routing outcomes, distinguishes zero-score TF-IDF results from real signals, updates Tier 2 escalation documentation, and expands regression coverage. ChangesTier 1 cascade routing
Estimated code review effort: 4 (Complex) | ~45 minutes Merge Risk: ⚪ Minimal · up to Tier 1 now evaluates heading-navigation and TF-IDF together, resolving clear TF-IDF-only matches and escalating ambiguous or absent signals without fabricating zero-score disagreements. The documented routing behavior and regression coverage support merge readiness. Sequence Diagram(s)sequenceDiagram
participant route_tier1
participant heading_nav
participant score_sections
participant route_tier2
participant OKF
route_tier1->>heading_nav: obtain heading-navigation result
route_tier1->>score_sections: score sections with TF-IDF
score_sections-->>route_tier1: ranked results and signal status
route_tier1->>route_tier1: resolve or create escalation result
route_tier1->>route_tier2: pass escalating result
route_tier2->>OKF: validate candidate or all sections
OKF-->>route_tier2: return trust status
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 27 functions across 2 files. (1 skipped: 1 unsupported.)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
code-rankerBuilt on a fork. View full report ↗ python
|
… TF-IDF on a zero-hit Problem 2 of constructorfabric#134: route_tier1 escalated immediately whenever heading-nav found zero hits, without ever running TF-IDF -- discarding a genuinely different signal. Heading-nav requires the query's exact literal substring somewhere in a section's raw text; TF-IDF tokenizes on individual words, so a query differing from the source only in punctuation/spacing/hyphenation can score unambiguously on TF-IDF even when heading-nav's exact-phrase match fails outright. Both methods now always run. Two new Tier 1 rows cover heading-nav's zero-hit case: TF-IDF alone resolves at Tier 1 when unambiguous, and otherwise escalates with TF-IDF's own top pick as the Tier-2 candidate (rather than none at all) unless TF-IDF also has no positive score anywhere. Existing rows (agree/disagree/diffuse-margin when heading-nav does hit) are unchanged. Deliberately still out of scope: folding OKF summaries into Tier 1 as a fourth signal -- Tier 1's whole value is being free and deterministic without ever touching the OKF bundle. Signed-off-by: TECK KEAT WILSON <yeow.teck.keat@constructor.tech>
7b9a838 to
af084c5
Compare
…signal Review findings on PR constructorfabric#137 (ainetx): - Major: route_tier1 unconditionally compared tfidf_ranked[0] against heading-nav's pick once heading-nav found a hit, even when every section scored exactly 0 (no real TF-IDF signal at all). tfidf_ranked[0] in that case is just an arbitrary document-order tie-break, not a genuine top pick -- comparing it anyway fabricated a "resolved_multi" / "heading_nav_tfidf_disagree" verdict (or, by coincidence of order, a fabricated agreement) out of a signal that was never there. Added an explicit row 4 (heading-nav has a hit, TF-IDF has no signal anywhere) that escalates on heading-nav's single, unconfirmed signal instead, symmetric to how rows 2/3 already handle TF-IDF's own no-signal case. Splits route_tier1 into two functions along the table's own two-part structure (heading-nav miss vs. hit) rather than disabling pylint's too-many-return-statements once the new row pushed past it. - Minor: five hand-built TestRouteTier2 fixtures still used the retired "heading_nav_no_hits" reason string (route_tier1 was renamed to "no_signal_from_either_method" for that case in the prior commit, without these being updated). Consolidated into one named constant, since route_tier2 never reads the reason field -- these fixtures only ever needed the row-1 *shape*, not a specific string -- so a future rename can't silently drift out of sync with these tests again. - Renamed the TestRouteTier1 test methods to match the routing table's real row numbers post-insertion (row 4 new; disagree/agree/diffuse-margin shifted from 3/2/4 to 5/6/7) and added a regression test reproducing the review's exact repro case for the new row 4. Signed-off-by: TECK KEAT WILSON <yeow.teck.keat@constructor.tech>
…ote TF-IDF's unconditional cost Review findings on PR constructorfabric#137 (ainetx, second pass against 6645603): - route_tier1's docstring enumerated candidate-count rows as "2/4/6/7" but its own parenthetical immediately after ("rows 3/4/7 despite escalating") named row 3 as one of the escalating-but-one-candidate rows -- contradicting the main list, which omitted it. Row 3 (heading- nav miss, TF-IDF diffuse) does return exactly one candidate, same as rows 2/4/6/7; the list was just wrong. Fixed to "2/3/4/6/7", so the list and its own parenthetical agree with each other and with the actual implementation. - Fusing heading-nav and TF-IDF (this function's whole point) means score_sections() now runs on every call, even one that a heading-nav hit could otherwise have resolved without it -- tfidf.py's own module docstring documents this scoring method as validated against a single ~166-page/~9-section document with deliberately no cap on section count or file size. That's an accepted, explicit tradeoff of fusing the signals, not an oversight; documented at both the call site and in route_tier1's own docstring rather than left implicit. Signed-off-by: TECK KEAT WILSON <yeow.teck.keat@constructor.tech>
…ce, and OKF independence Review findings on PR constructorfabric#137 (ainetx, second pass against 6645603): - No test exercised score_sections' single-section `unambiguous` code path: with only one ranked section, `_confidence` returns `unambiguous=True` purely because there's no second candidate to compare against (`len(ranked) == 1`), never by beating a real rival's score. Every existing row-2 fixture used two sections and only ever exercised the latter path. Added test_row2_tfidf_only_unambiguous_with_single_section_document with a genuinely single-section fixture, verified against tfidf.py's actual `_confidence` logic rather than asserted blindly. - No regression test guarded that the row-2 (tfidf_only_unambiguous) branch never falls through to route_tier2/OKF. Added test_row2_tfidf_only_unambiguous_never_calls_route_tier2_or_okf, spying on both with unittest.mock.patch and asserting neither is called. - route_tier1's OKF-independence guarantee (module docstring: Tier 1 must stay free and deterministic, never touching the OKF bundle) had no automated backing. Added test_route_tier1_never_touches_okf, which patches get_okf_status to raise if called at all and exercises every routing-table row (1 through 7, plus the margin_threshold opt-in) against it. - route_tier2 had no test for the new row-3 (heading-nav miss, TF-IDF diffuse) escalation, whose candidate is TF-IDF-sourced -- a provenance never exercised before (every prior route_tier2 test used a heading-nav-sourced candidate or none at all). Added test_row3_tfidf_sourced_candidate_recommends_okf_when_current, building the row-3 result via a real route_tier1 call so it breaks if row 3's actual shape ever changes. Signed-off-by: TECK KEAT WILSON <yeow.teck.keat@constructor.tech>
…er2, tighten row-3 assertion, enforce row-number docstring Third ainetx review pass on constructorfabric#137 found four more gaps: - test_row3_tfidf_sourced_candidate_recommends_okf_when_current wrote every section's concept file current, so a route_tier2 that (bug) validated the whole bundle instead of narrowing to just the named candidate would still pass. Added test_row3_tfidf_sourced_candidate_narrows_staleness_to_only_that_section, which only makes the candidate's own section current and leaves the other section missing -- verified it actually fails under that bug by injecting it and running the new test before reverting. - route_tier2's docstring claims it's "only called for the escalating rows: row 1 ... row 3 ... row 4, and row 7" but nothing enforced that cross-reference against route_tier1's real behavior. Added test_docstrings_row_number_cross_reference_matches_actual_escalating_reasons, which runs route_tier1 against a fixture for all seven table rows and asserts the escalating rows/reasons that actually come back are exactly what the docstring claims. - test_row3_tfidf_only_diffuse_escalates_with_tfidf_pick_as_candidate asserted tier/reason/candidates as three separate assertions instead of one whole-dict equality, unlike every other row test in this file -- switched it to match that pattern. - Row 4's own route_tier1 output was tested, but nothing fed a real row-4 result into route_tier2, unlike row 3. Added test_row4_heading_nav_sourced_candidate_recommends_okf_when_current, modeled on the existing row-3 route_tier2 test.
…er2, tighten row-3 assertion, enforce row-number docstring Third ainetx review pass on constructorfabric#137 found four more gaps: - test_row3_tfidf_sourced_candidate_recommends_okf_when_current wrote every section's concept file current, so a route_tier2 that (bug) validated the whole bundle instead of narrowing to just the named candidate would still pass. Added test_row3_tfidf_sourced_candidate_narrows_staleness_to_only_that_section, which only makes the candidate's own section current and leaves the other section missing -- verified it actually fails under that bug by injecting it and running the new test before reverting. - route_tier2's docstring claims it's "only called for the escalating rows: row 1 ... row 3 ... row 4, and row 7" but nothing enforced that cross-reference against route_tier1's real behavior. Added test_docstrings_row_number_cross_reference_matches_actual_escalating_reasons, which runs route_tier1 against a fixture for all seven table rows and asserts the escalating rows/reasons that actually come back are exactly what the docstring claims. - test_row3_tfidf_only_diffuse_escalates_with_tfidf_pick_as_candidate asserted tier/reason/candidates as three separate assertions instead of one whole-dict equality, unlike every other row test in this file -- switched it to match that pattern. - Row 4's own route_tier1 output was tested, but nothing fed a real row-4 result into route_tier2, unlike row 3. Added test_row4_heading_nav_sourced_candidate_recommends_okf_when_current, modeled on the existing row-3 route_tier2 test. Signed-off-by: TECK KEAT WILSON <yeow.teck.keat@constructor.tech>
d0d43a3 to
ad30b6f
Compare
|
Note: the round-3 fix commit referenced in the four replies above as |
|
ainetx
left a comment
There was a problem hiding this comment.
Automated review complete -- no blocking findings remain and CI is passing.



Summary
route_tier1escalated immediately whenever heading-nav found zero hits, without ever running TF-IDF — discarding a genuinely different signal. Heading-nav (find_sections) requires the query's exact literal substring to appear somewhere in a section's raw text; TF-IDF (score_sections) tokenizes on individual alphanumeric words, so a query differing from the source only in punctuation/spacing/hyphenation can still score unambiguously on TF-IDF even when heading-nav's exact-phrase match fails outright.tfidf_only_unambiguous).heading_nav_no_hits_diffuse_tfidf) rather than none at all — unless TF-IDF also has no positive score anywhere, in which case it escalates with nothing (no_signal_from_either_method), same as the old behavior for a genuinely signal-less query.Update: review round (ainetx) — 1 Major, 1 Minor, both fixed
route_tier1still compared TF-IDF's document-order tie-break pick against heading-nav's real pick as if it were a genuine competing answer — fabricating aresolved_multi/heading_nav_tfidf_disagreeverdict (or, by coincidence of order, a fabricated agreement) out of a signal that was never there. Fix: added an explicit new row (heading-nav has a hit, TF-IDF has no signal anywhere) that escalates on heading-nav's single, unconfirmed signal instead — symmetric to how the zero-hit rows already handle TF-IDF's own no-signal case.route_tier1was split into two functions along the routing table's own two-part structure (heading-nav miss vs. hit) rather than disabling pylint's too-many-return-statements once the new row pushed past it. Added a regression test reproducing the review's exact repro case.TestRouteTier2fixtures still used the retired"heading_nav_no_hits"reason string (renamed to"no_signal_from_either_method"earlier in this same PR, without these being updated) —route_tier2never reads the reason field, so nothing failed, but the drift was real and misleading. Fix: consolidated into one named constant instead of five separate literal copies, so a future rename can't silently skip these fixtures again. Test method names were also renumbered to match the routing table's real rows post-insertion (new row 4; disagree/agree/diffuse-margin shifted from 3/2/4 to 5/6/7).Test plan
_TFIDF_ONLY_UNAMBIGUOUS_SAMPLE,_TFIDF_ONLY_DIFFUSE_SAMPLE) use a real hyphenation gap (source:"KAPING-framework"/ query:"KAPING framework") to reproduce heading-nav's exact-phrase miss while TF-IDF's tokenizer still resolves it — hand-verified againsttokenize()'s and_confidence()'s actual behavior, not just asserted against.pytest tests/test_cascade.py -q— 30 passed (the only test file this PR's diff touches — an earlier version of this description incorrectly claimed a 5-file, 182-passed command pulled in from a broader sanity check, not this PR's own scope)pylintoncascade.py— cleancfs validate --artifact architecture/features/traceability-validation.md— passes (pre-existing, unrelatedtoc-missing-descriptionwarning only)cfs spec-coverage --system studio --min-coverage 90 --min-file-coverage 60 --min-granularity 0.46— all thresholds metmain, not stacked on that PRSummary by CodeRabbit