Skip to content

feat(relationship_detector): #387 POTENTIALLY_STALE writer via aelf doctor --detect-stale - #444

Merged
robotrocketscience merged 4 commits into
mainfrom
feat/issue-387-potentially-stale-writer
May 5, 2026
Merged

feat(relationship_detector): #387 POTENTIALLY_STALE writer via aelf doctor --detect-stale#444
robotrocketscience merged 4 commits into
mainfrom
feat/issue-387-potentially-stale-writer

Conversation

@yoshi280

@yoshi280 yoshi280 commented May 5, 2026

Copy link
Copy Markdown
Collaborator

Closes #387.

Summary

Wires the POTENTIALLY_STALE edge writer into aelf doctor, closing
the missing acceptance criterion #2 from #387. The substrate (#421)
shipped the edge-type constant, BFS skip-during-expansion pin, the
edge-type-keyed rerank consumer, and the bench-gate stub at
tests/bench_gate/test_edge_rerank_potentially_stale.py. This PR
adds the producer side.

Acceptance check (#387)

# criterion status
1 Edge type added to schema ✅ shipped in #421 (EDGE_POTENTIALLY_STALE in models.py, BFS pin at BFS_EDGE_WEIGHTS[POTENTIALLY_STALE] = 0.0)
2 Edge writer hooked into appropriate ingest entry point ✅ this PR — aelf doctor --detect-stale
3 BFS multi-hop fixture exercised; bench number recorded ⏳ stub at tests/bench_gate/test_edge_rerank_potentially_stale.py from #421 runs lab-side (skips when AELFRICE_CORPUS_ROOT unset, per directory-of-origin). Public CI verifies the stub's collection + skip path; lab-side run records the +1pp drop number in a follow-up comment on #387.
4 Documentation update docs/edge_rerank.md § Producer side

Design

Picked the staleness signal that the codebase had already pre-decided
(relationship_detector.py:22-26, cli.py:2606-2608 pre-PR docstrings):

Staleness signal: sub-confidence (score < confidence_min)
LABEL_CONTRADICTS pairs from relationships_audit(). High-confidence
contradicting pairs are explicitly NOT touched — those belong to the
deferred CONTRADICTS write-path hook (R2 in #201, still bench-gated).

Edge direction: for each qualifying pair (a, b), one
POTENTIALLY_STALE edge is emitted with src = newer belief, dst = older belief. "Newer" is the belief whose (created_at, id) tuple is
lexicographically greater (ISO timestamps, tie-break on lex id order).
Semantics: the newer belief casts doubt on the older one.

Idempotent: a pre-insert get_edge(src, dst, POTENTIALLY_STALE)
check skips pairs whose edge already exists. Repeated invocations are
safe.

Stdlib only. No new dependencies. Determinism is preserved end-to-end:
relationships_audit already sorts pairs by (belief_a_id, belief_b_id),
and the writer iterates in that order.

CLI surface

aelf doctor --detect-stale [--relationships-jaccard F]
                           [--relationships-confidence F]
                           [--relationships-max-pairs N]

The tuning flags share semantics with --relationships. Models the
write-mode shape after --classify-orphans (the existing precedent
for write-mode doctor flags).

Sample output:

aelf doctor --detect-stale
========================================
Contradicting pairs audited : 12
Sub-confidence pairs        : 4
Edges written               : 4
Edges skipped (existing)    : 0
Edges skipped (self-pair)   : 0

Test plan

7 new unit tests in tests/test_relationship_detector_stale_writer.py:

  • test_writer_emits_edges_for_sub_confidence_pairs — sub-confidence
    pair (always/rarely, score 0.4) yields exactly one edge.
  • test_writer_skips_high_confidence_pairs — negation-only pair
    (score exactly 0.5 == confidence_min) yields zero edges. The
    filter is strict less-than (< confidence_min), so 0.5 is
    high-confidence.
    This boundary behaviour is intentional and the
    test pins it.
  • test_writer_idempotent — second call writes 0 edges,
    n_edges_skipped_existing reflects the prior insertion.
  • test_writer_direction_newer_to_older — newer (later created_at)
    is src, older is dst. Reverse direction does NOT exist.
  • test_writer_direction_tiebreak_on_id — identical created_at,
    lex-greater id wins as src.
  • test_writer_skips_refines_and_unrelated — only LABEL_CONTRADICTS
    pairs are eligible.
  • test_writer_returns_report_counts — mixed store; counts add up.

Local: uv run pytest tests/ --ignore=tests/e2e -q → 2508 passed, 41 skipped.

Out of scope

Files changed

  • src/aelfrice/relationship_detector.py+150 (new
    write_potentially_stale_edges, PotentiallyStaleWriteReport,
    format_write_report, __all__ extension, docstring reconciliation).
  • src/aelfrice/cli.py+85 (new _cmd_doctor_detect_stale,
    argparse flag, dispatch in _cmd_doctor, docstring update).
  • tests/test_relationship_detector_stale_writer.py+217 (new file).
  • docs/edge_rerank.md+24 (Producer-side § expanded).

Summary by Sourcery

Add a POTENTIALLY_STALE edge writer and expose it via the aelf doctor --detect-stale CLI to emit edges for sub-confidence contradicting belief pairs.

New Features:

  • Introduce write_potentially_stale_edges and reporting helpers to generate POTENTIALLY_STALE edges from low-confidence contradicting relationships.
  • Add the aelf doctor --detect-stale command-line option to scan for stale beliefs and write POTENTIALLY_STALE edges with shared tuning flags from --relationships.

Enhancements:

  • Expand edge rerank documentation with details on the POTENTIALLY_STALE producer semantics, direction, idempotency, and tuning options.

Tests:

  • Add a dedicated test suite for the POTENTIALLY_STALE edge writer covering direction, idempotency, filtering, and report accounting.

Summary by CodeRabbit

  • New Features

    • aelf doctor --detect-stale command now identifies contradicting belief pairs and emits stale edge markers
    • Supports existing relationship tuning flags for threshold and pair-limit control
  • Documentation

    • Expanded guide detailing stale edge detection workflow, configuration, and behavior
  • Tests

    • Added comprehensive unit tests for stale edge detection, idempotency, and edge directionality

@yoshi280 yoshi280 added the attn:review Needs review (PR open, awaiting reviewer) label May 5, 2026
@sourcery-ai

sourcery-ai Bot commented May 5, 2026

Copy link
Copy Markdown

Reviewer's Guide

Implements the producer-side POTENTIALLY_STALE edge writer and wires it into aelf doctor --detect-stale, including CLI plumbing, reporting, documentation, and unit tests around staleness criteria, edge direction, and idempotency.

Sequence diagram for aelf doctor --detect-stale execution

sequenceDiagram
    actor User
    participant CLI as aelf_doctor_CLI
    participant Doctor as _cmd_doctor
    participant DetectStale as _cmd_doctor_detect_stale
    participant RelConfig as RelationshipDetectorConfig
    participant Store as MemoryStore
    participant Writer as write_potentially_stale_edges

    User->>CLI: aelf doctor --detect-stale [flags]
    CLI->>Doctor: _cmd_doctor(args, out)
    Doctor->>Doctor: check args.relationships
    Doctor->>Doctor: check args.detect_stale
    Doctor->>DetectStale: _cmd_doctor_detect_stale(args, out)

    DetectStale->>RelConfig: load_relationship_detector_config()
    DetectStale->>RelConfig: apply jaccard/confidence/max_pairs overrides

    DetectStale->>Store: _open_store()
    activate Store

    DetectStale->>Writer: write_potentially_stale_edges(store, jaccard_min, residual_overlap_min, confidence_min, max_candidate_pairs)

    activate Writer
    Writer->>Writer: relationships_audit(store, thresholds)
    Writer->>Writer: filter LABEL_CONTRADICTS pairs
    Writer->>Writer: select pairs with score < confidence_min

    loop for each sub_confidence pair
        Writer->>Store: get_belief(a_id), get_belief(b_id)
        Writer->>Writer: choose src,dst by created_at then id
        Writer->>Store: get_edge(src, dst, EDGE_POTENTIALLY_STALE)
        alt edge exists
            Writer->>Writer: n_edges_skipped_existing++
        else edge missing
            Writer->>Store: insert_edge(Edge(src,dst,POTENTIALLY_STALE))
            Writer->>Writer: n_edges_written++
        end
    end

    Writer-->>DetectStale: PotentiallyStaleWriteReport
    deactivate Writer

    DetectStale->>Store: close()
    deactivate Store

    DetectStale->>DetectStale: format_write_report(report)
    DetectStale->>CLI: print report to out
    DetectStale-->>Doctor: exit code 0 or 1
    Doctor-->>User: process exit
Loading

Class diagram for PotentiallyStaleWriteReport and related writer utilities

classDiagram
    class PotentiallyStaleWriteReport {
        +int n_pairs_audited
        +int n_sub_confidence
        +int n_edges_written
        +int n_edges_skipped_existing
        +int n_edges_skipped_self_pair
    }

    class RelationshipDetectorConfig {
        +float jaccard_min
        +float residual_overlap_min
        +float confidence_min
        +int max_candidate_pairs
    }

    class MemoryStore {
        +get_belief(belief_id)
        +get_edge(src_id, dst_id, edge_type)
        +insert_edge(edge)
        +close()
    }

    class Edge {
        +str src
        +str dst
        +str type
        +float weight
    }

    class RelationshipDetectorModule {
        +write_potentially_stale_edges(store, jaccard_min, residual_overlap_min, confidence_min, max_candidate_pairs) PotentiallyStaleWriteReport
        +format_write_report(report) str
        +relationships_audit(store, jaccard_min, residual_overlap_min, confidence_min, max_candidate_pairs)
    }

    RelationshipDetectorModule ..> PotentiallyStaleWriteReport : returns
    RelationshipDetectorModule ..> MemoryStore : uses
    RelationshipDetectorModule ..> Edge : inserts
    RelationshipDetectorModule ..> RelationshipDetectorConfig : thresholds

    MemoryStore "*" o-- Edge : contains_edges
Loading

File-Level Changes

Change Details Files
Add POTENTIALLY_STALE edge writer and reporting utilities to the relationship detector module.
  • Introduce PotentiallyStaleWriteReport dataclass capturing audited pair counts and edge write/skip metrics.
  • Implement write_potentially_stale_edges to call relationships_audit, filter sub-confidence LABEL_CONTRADICTS pairs, compute newer→older direction, guard with get_edge for idempotency, and insert EDGE_POTENTIALLY_STALE edges.
  • Add format_write_report to render a human-readable CLI summary of a write run.
  • Update module docstring to describe the POTENTIALLY_STALE writer and extend all to export the new APIs.
src/aelfrice/relationship_detector.py
Wire POTENTIALLY_STALE writer into the aelf doctor CLI via a new --detect-stale subcommand path.
  • Extend _cmd_doctor dispatch to route to a new _cmd_doctor_detect_stale handler when --detect-stale is set.
  • Implement cmd_doctor_detect_stale to load RelationshipDetectorConfig, apply relationships* CLI overrides, call write_potentially_stale_edges, handle ValueError as exit 1, and print the formatted write report.
  • Add --detect-stale flag to the doctor subparser with help text documenting behaviour, direction semantics, idempotency, and tuning reuse.
  • Adjust the relationships doctor docstring to note that POTENTIALLY_STALE edges are now written via --detect-stale while CONTRADICTS remains deferred.
src/aelfrice/cli.py
Document producer-side semantics and tuning for POTENTIALLY_STALE edges in the edge rerank documentation.
  • Clarify that POTENTIALLY_STALE edges are produced by aelf doctor --detect-stale and reference write_potentially_stale_edges.
  • Describe the staleness signal as sub-confidence contradicts pairs (score < confidence_min) and exclude high-confidence pairs.
  • Document edge direction (newer created_at / id → older), idempotency via pre-insert get_edge, and reuse of relationships tuning flags for --detect-stale.
docs/edge_rerank.md
Add unit test coverage for POTENTIALLY_STALE writer behaviour over an in-memory store.
  • Create helper utilities and a pytest fixture to construct beliefs in an in-memory MemoryStore and to count edges.
  • Test that sub-confidence contradicts pairs emit exactly one POTENTIALLY_STALE edge while high-confidence pairs emit none, including a pinned confidence_min boundary case.
  • Verify idempotency by running the writer twice and asserting second-run skips and counts, and validate edge direction rules including created_at and id tie-breaking.
  • Confirm that non-contradicts pairs (refines/unrelated) produce no edges and that aggregated report counts are internally consistent on a mixed store.
tests/test_relationship_detector_stale_writer.py

Assessment against linked issues

Issue Objective Addressed Explanation
#387 Define the POTENTIALLY_STALE edge type in the schema and wire it into the graph/retrieval pipeline (per existing edge-type enum/discriminator and BFS/rerank patterns).
#387 Add a POTENTIALLY_STALE edge writer hooked into the appropriate ingest entry point(s) (aelf doctor / staleness detector) so that POTENTIALLY_STALE edges are actually produced.
#387 Satisfy the bench-gate and documentation requirements by (a) exercising the BFS multi-hop fixture and recording the bench number in the PR body, and (b) updating the POTENTIALLY_STALE edge-type documentation. While the documentation is updated in docs/edge_rerank.md, the BFS multi-hop fixture result and its bench number are explicitly marked as pending (lab-side run to be reported later), and no uplift/drop number is recorded in the PR body as required by the issue.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented May 5, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@robotrocketscience has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 45 minutes and 24 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 05143a99-905c-4d0b-a037-e7597002f6bf

📥 Commits

Reviewing files that changed from the base of the PR and between 9f0295a and 68dafc0.

📒 Files selected for processing (4)
  • docs/edge_rerank.md
  • src/aelfrice/cli.py
  • src/aelfrice/relationship_detector.py
  • tests/test_relationship_detector_stale_writer.py
📝 Walkthrough

Walkthrough

This PR implements the aelf doctor --detect-stale feature for writing POTENTIALLY_STALE edges. The change adds core logic to detect sub-confidence contradicting belief pairs, determines edge direction based on recency, ensures idempotency, and integrates the writer into the CLI and documentation.

Changes

POTENTIALLY_STALE Edge Writer Implementation

Layer / File(s) Summary
Core Implementation
src/aelfrice/relationship_detector.py
Adds PotentiallyStaleWriteReport dataclass, write_potentially_stale_edges() function that runs relationships_audit, filters contradicts pairs by confidence_min, determines src/dst based on newer belief (using created_at then id tie-break), checks idempotency, and inserts edges with weight=1.0. Adds format_write_report() helper and updates __all__ exports.
CLI Integration
src/aelfrice/cli.py
Adds --detect-stale CLI argument to the doctor subcommand. Implements _cmd_doctor_detect_stale() handler that loads config, applies --relationships-* overrides, calls write_potentially_stale_edges(), formats output, and returns status. Routes --detect-stale dispatch before hooks/graph checks in _cmd_doctor. Updates --relationships docstring to clarify POTENTIALLY_STALE emission path.
Documentation
docs/edge_rerank.md
Replaces "Producer side" placeholder with full specification: source command (aelf doctor --detect-stale), staleness condition (contradicts pairs where score < confidence_min), edge direction rules (newer as src, older as dst), idempotency behavior, and reuse of relationship-tuning flags.
Tests & Validation
tests/test_relationship_detector_stale_writer.py
Adds 7 test functions covering sub-confidence emission, high-confidence exclusion, idempotency, direction rules (recency and id tie-break), filtering of non-contradicting pairs, and report counter validation. Includes _make_belief() and _edge_count() helpers for test setup and assertion.

Sequence Diagram

sequenceDiagram
    actor User
    participant CLI as aelf doctor CLI
    participant Store as MemoryStore
    participant Detector as relationship_detector
    participant Graph as Edge Graph

    User->>CLI: aelf doctor --detect-stale
    CLI->>Store: load store (with --relationships-* overrides)
    CLI->>Detector: write_potentially_stale_edges(store, ...)
    Detector->>Detector: relationships_audit() → contradicts pairs
    Detector->>Detector: filter pairs where score < confidence_min
    Detector->>Detector: sort by created_at (newer → src)
    loop Each contradicting pair
        Detector->>Store: check if POTENTIALLY_STALE edge exists
        alt Edge not idempotent
            Detector->>Store: insert (src, dst, POTENTIALLY_STALE, weight=1.0)
            Detector->>Graph: edge written
        else Already exists
            Detector->>Detector: increment skipped count
        end
    end
    Detector->>CLI: return PotentiallyStaleWriteReport
    CLI->>User: format_write_report() → output (edges_written, skipped, etc.)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: adding a POTENTIALLY_STALE edge writer via the aelf doctor --detect-stale command, directly addressing issue #387.
Description check ✅ Passed The PR description comprehensively addresses all template sections: Summary, Linked issues (Closes #387), Type of change (feat), detailed Verification checklist, comprehensive Test plan, and Notes for reviewer addressing design decisions and scope boundaries.
Linked Issues check ✅ Passed The PR fulfills acceptance criteria #1 (edge type added in #421), #2 (writer hooked into aelf doctor --detect-stale in this PR), and #4 (docs/edge_rerank.md updated). Criterion #3 (bench gate) is correctly identified as lab-side with public stub already in place.
Out of Scope Changes check ✅ Passed All changes directly support the linked issue #387 objectives: the writer implementation, CLI integration, comprehensive tests, and documentation updates. No unrelated changes detected.
Docstring Coverage ✅ Passed Docstring coverage is 88.89% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-387-potentially-stale-writer

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.

Comment on lines +20 to +23
from aelfrice.relationship_detector import (
LABEL_CONTRADICTS,
write_potentially_stale_edges,
)

@sourcery-ai sourcery-ai 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.

Hey - I've found 1 issue, and left some high level feedback:

  • In _cmd_doctor, --relationships is checked before --detect-stale, so if a user passes both flags only the relationships path runs; consider either enforcing mutual exclusivity at argparse level or documenting the precedence explicitly to avoid confusion.
  • In _cmd_doctor_detect_stale, the CLI overrides for relationships_jaccard, relationships_confidence, and relationships_max_pairs are cast directly to float/int without error handling; wrapping these conversions in a small validator that surfaces a clean ValueError (similar to the threshold errors you already handle) would provide a friendlier CLI failure mode for bad inputs.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `_cmd_doctor`, `--relationships` is checked before `--detect-stale`, so if a user passes both flags only the relationships path runs; consider either enforcing mutual exclusivity at argparse level or documenting the precedence explicitly to avoid confusion.
- In `_cmd_doctor_detect_stale`, the CLI overrides for `relationships_jaccard`, `relationships_confidence`, and `relationships_max_pairs` are cast directly to float/int without error handling; wrapping these conversions in a small validator that surfaces a clean `ValueError` (similar to the threshold errors you already handle) would provide a friendlier CLI failure mode for bad inputs.

## Individual Comments

### Comment 1
<location path="src/aelfrice/cli.py" line_range="2694-2703" />
<code_context>
+    j_override = getattr(args, "relationships_jaccard", None)
+    c_override = getattr(args, "relationships_confidence", None)
+    mp_override = getattr(args, "relationships_max_pairs", None)
+    config = RelationshipDetectorConfig(
+        jaccard_min=(
+            float(j_override) if j_override is not None else config.jaccard_min
+        ),
+        residual_overlap_min=config.residual_overlap_min,
+        confidence_min=(
+            float(c_override)
+            if c_override is not None
+            else config.confidence_min
+        ),
+        max_candidate_pairs=(
+            int(mp_override)
+            if mp_override is not None
+            else config.max_candidate_pairs
+        ),
+    )
</code_context>
<issue_to_address>
**issue:** Threshold overrides can raise ValueError before the try/except, contradicting the documented exit semantics

Because `float(j_override)`, `float(c_override)`, and `int(mp_override)` run before the `try`, malformed CLI values (e.g. `--relationships-jaccard=foo`) raise `ValueError` that bypasses your handler and crash the command instead of exiting with 1 as documented. Please move the parsing / `RelationshipDetectorConfig` construction into the existing `try` or add a dedicated `try/except ValueError` that logs the same `aelf doctor --detect-stale: ...` message and returns 1.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/aelfrice/cli.py
Comment on lines +2694 to +2703
config = RelationshipDetectorConfig(
jaccard_min=(
float(j_override) if j_override is not None else config.jaccard_min
),
residual_overlap_min=config.residual_overlap_min,
confidence_min=(
float(c_override)
if c_override is not None
else config.confidence_min
),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue: Threshold overrides can raise ValueError before the try/except, contradicting the documented exit semantics

Because float(j_override), float(c_override), and int(mp_override) run before the try, malformed CLI values (e.g. --relationships-jaccard=foo) raise ValueError that bypasses your handler and crash the command instead of exiting with 1 as documented. Please move the parsing / RelationshipDetectorConfig construction into the existing try or add a dedicated try/except ValueError that logs the same aelf doctor --detect-stale: ... message and returns 1.

@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: 1

🧹 Nitpick comments (1)
src/aelfrice/cli.py (1)

2690-2709: ⚡ Quick win

Extract the shared config-resolution block into a helper to avoid drift.

The 20-line load_relationship_detector_config() + RelationshipDetectorConfig(...) override block in _cmd_doctor_detect_stale (lines 2690–2709) is a verbatim copy of the identical block in _cmd_doctor_relationships (lines 2622–2641). If a new --relationships-* flag is introduced (e.g., a residual_overlap override), it must be updated in both places.

♻️ Proposed extraction
+def _resolve_relationship_detector_config(
+    args: argparse.Namespace,
+) -> "RelationshipDetectorConfig":
+    from aelfrice.relationship_detector import (
+        RelationshipDetectorConfig,
+        load_relationship_detector_config,
+    )
+    config = load_relationship_detector_config()
+    j_override = getattr(args, "relationships_jaccard", None)
+    c_override = getattr(args, "relationships_confidence", None)
+    mp_override = getattr(args, "relationships_max_pairs", None)
+    return RelationshipDetectorConfig(
+        jaccard_min=(
+            float(j_override) if j_override is not None else config.jaccard_min
+        ),
+        residual_overlap_min=config.residual_overlap_min,
+        confidence_min=(
+            float(c_override)
+            if c_override is not None
+            else config.confidence_min
+        ),
+        max_candidate_pairs=(
+            int(mp_override)
+            if mp_override is not None
+            else config.max_candidate_pairs
+        ),
+    )

Then in both handlers:

-    config = load_relationship_detector_config()
-    j_override = getattr(args, "relationships_jaccard", None)
-    c_override = getattr(args, "relationships_confidence", None)
-    mp_override = getattr(args, "relationships_max_pairs", None)
-    config = RelationshipDetectorConfig(
-        jaccard_min=(
-            float(j_override) if j_override is not None else config.jaccard_min
-        ),
-        residual_overlap_min=config.residual_overlap_min,
-        confidence_min=(
-            float(c_override)
-            if c_override is not None
-            else config.confidence_min
-        ),
-        max_candidate_pairs=(
-            int(mp_override)
-            if mp_override is not None
-            else config.max_candidate_pairs
-        ),
-    )
+    config = _resolve_relationship_detector_config(args)
🤖 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/aelfrice/cli.py` around lines 2690 - 2709, The shared config-resolution
code for relationship detector overrides is duplicated between
_cmd_doctor_detect_stale and _cmd_doctor_relationships; extract that logic into
a helper (e.g., a new function like resolve_relationship_detector_config) that
calls load_relationship_detector_config and applies overrides from args
(relationships_jaccard, relationships_confidence, relationships_max_pairs, etc.)
returning a RelationshipDetectorConfig, then replace the inline blocks in both
handlers to call this helper so future flags only need one update.
🤖 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 `@tests/test_relationship_detector_stale_writer.py`:
- Around line 185-217: The current tautological assertion in
test_writer_returns_report_counts should be replaced with a concrete equality:
assert that report.n_pairs_audited equals the expected total contradicting pairs
from the setup (one sub-confidence + one high-confidence = 2). In the test
function test_writer_returns_report_counts, replace the tautology involving
report.n_sub_confidence with a direct check assert report.n_pairs_audited == 2
(and keep or remove the existing >= check as optional) so the test actually
verifies the intended total audited count.

---

Nitpick comments:
In `@src/aelfrice/cli.py`:
- Around line 2690-2709: The shared config-resolution code for relationship
detector overrides is duplicated between _cmd_doctor_detect_stale and
_cmd_doctor_relationships; extract that logic into a helper (e.g., a new
function like resolve_relationship_detector_config) that calls
load_relationship_detector_config and applies overrides from args
(relationships_jaccard, relationships_confidence, relationships_max_pairs, etc.)
returning a RelationshipDetectorConfig, then replace the inline blocks in both
handlers to call this helper so future flags only need one update.
🪄 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: e7657785-a59c-4bbf-8f77-9a80f7b131cd

📥 Commits

Reviewing files that changed from the base of the PR and between a3e4940 and 9f0295a.

📒 Files selected for processing (4)
  • docs/edge_rerank.md
  • src/aelfrice/cli.py
  • src/aelfrice/relationship_detector.py
  • tests/test_relationship_detector_stale_writer.py

Comment on lines +185 to +217
def test_writer_returns_report_counts(store: MemoryStore) -> None:
"""Mixed store: report counts add up correctly.

Store contains:
- one sub-confidence contradicting pair → 1 edge written
- one high-confidence contradicting pair → 0 edges (excluded)
- one unrelated pair → 0 edges
"""
# Sub-confidence pair (score 0.4).
_make_belief(store, belief_id="s1", content=_CONTENT_A,
created_at="2026-01-01T00:00:00Z")
_make_belief(store, belief_id="s2", content=_CONTENT_B,
created_at="2026-02-01T00:00:00Z")
# High-confidence pair (score 0.5 — NOT sub-confidence).
_make_belief(store, belief_id="h1", content=_CONTENT_HI_A)
_make_belief(store, belief_id="h2", content=_CONTENT_HI_B)
# Unrelated pair.
_make_belief(store, belief_id="u1", content="sphinx fts indexing latency")
_make_belief(store, belief_id="u2", content="compression ratio on disk")

report = write_potentially_stale_edges(store)

# Exactly 1 sub-confidence contradicts pair qualifies.
assert report.n_sub_confidence == 1
assert report.n_edges_written == 1
assert report.n_edges_skipped_existing == 0
assert report.n_edges_skipped_self_pair == 0
# Total contradicts audited = sub + high.
assert report.n_pairs_audited == report.n_sub_confidence + (
report.n_pairs_audited - report.n_sub_confidence
)
# n_pairs_audited must be >= n_sub_confidence.
assert report.n_pairs_audited >= report.n_sub_confidence

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Tautological assertion tests nothing — replace with the intended concrete check.

Lines 213–215:

assert report.n_pairs_audited == report.n_sub_confidence + (
    report.n_pairs_audited - report.n_sub_confidence
)

This reduces to X == X + (X − X)X == X, which is unconditionally true and provides zero test coverage. The comment above it ("Total contradicts audited = sub + high") reveals the intent: verify the total contradicting-pair count equals 2 (one sub-confidence + one high-confidence pair from the test setup). The second assertion on line 217 (n_pairs_audited >= n_sub_confidence) is similarly always true by construction.

🐛 Proposed fix
-    # Total contradicts audited = sub + high.
-    assert report.n_pairs_audited == report.n_sub_confidence + (
-        report.n_pairs_audited - report.n_sub_confidence
-    )
-    # n_pairs_audited must be >= n_sub_confidence.
-    assert report.n_pairs_audited >= report.n_sub_confidence
+    # 1 sub-confidence + 1 high-confidence (score==0.5 NOT < 0.5) = 2 total.
+    assert report.n_pairs_audited == 2
+    assert report.n_sub_confidence == 1
🤖 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 `@tests/test_relationship_detector_stale_writer.py` around lines 185 - 217, The
current tautological assertion in test_writer_returns_report_counts should be
replaced with a concrete equality: assert that report.n_pairs_audited equals the
expected total contradicting pairs from the setup (one sub-confidence + one
high-confidence = 2). In the test function test_writer_returns_report_counts,
replace the tautology involving report.n_sub_confidence with a direct check
assert report.n_pairs_audited == 2 (and keep or remove the existing >= check as
optional) so the test actually verifies the intended total audited count.

@github-actions github-actions Bot added the attn:merge-conflict PR branch needs rebase label May 5, 2026
@github-actions

github-actions Bot commented May 5, 2026

Copy link
Copy Markdown

This PR is now behind main. Rebase locally so your commit signatures stay intact:

git fetch origin && git checkout 'feat/issue-387-potentially-stale-writer' && git rebase origin/main
# resolve conflicts if any, then
git push --force-with-lease

Auto-rebase was removed because the bot has no signing key; rebasing as the bot strips author signatures and the required_signatures rule on main then blocks the merge. See #341.

@yoshi280

yoshi280 commented May 5, 2026

Copy link
Copy Markdown
Collaborator Author

[claim:review:Toug:2026-05-05T17:37:45Z]

…onfidence contradicts (#387)

Add PotentiallyStaleWriteReport dataclass and write_potentially_stale_edges()
function. For each contradicts pair with score < confidence_min, one
POTENTIALLY_STALE edge is emitted src=newer→dst=older (created_at lex order,
id tiebreak). Pre-insert get_edge check keeps the call idempotent. Also add
format_write_report() helper. Update module docstring to reflect that the
POTENTIALLY_STALE writer ships here; CONTRADICTS write-path remains R2-deferred.
Add --detect-stale argparse flag to aelf doctor. Dispatch routes to the
new _cmd_doctor_detect_stale handler, which opens the store and calls
write_potentially_stale_edges with the same --relationships-jaccard /
--relationships-confidence / --relationships-max-pairs overrides shared
with --relationships. Prints format_write_report output on success.
Update _cmd_doctor_relationships docstring to note the CONTRADICTS
write-path remains R2-deferred while POTENTIALLY_STALE ships via #387.
…ort counts (#387)

Seven unit tests against a real MemoryStore(:memory:): sub-confidence pairs
emit edges, high-confidence pairs are excluded, writer is idempotent,
newer→older direction is correct, created_at tiebreak on lex id order,
refines/unrelated pairs produce zero edges, report count fields are
internally consistent.
Expand the Producer side section to name the CLI entry point
(aelf doctor --detect-stale), describe the staleness signal (sub-confidence
contradicting pairs, score < confidence_min), the edge direction rule
(newer belief → older belief, created_at lex order + id tiebreak),
idempotency guarantee, and the shared tuning flags.
@robotrocketscience
robotrocketscience force-pushed the feat/issue-387-potentially-stale-writer branch from 9f0295a to 68dafc0 Compare May 5, 2026 17:39
@robotrocketscience
robotrocketscience merged commit 68dafc0 into main May 5, 2026
20 checks passed
@robotrocketscience
robotrocketscience deleted the feat/issue-387-potentially-stale-writer branch May 5, 2026 17:41
@yoshi280 yoshi280 removed attn:review Needs review (PR open, awaiting reviewer) attn:merge-conflict PR branch needs rebase labels May 5, 2026
@yoshi280

yoshi280 commented May 5, 2026

Copy link
Copy Markdown
Collaborator Author

[release:review:Toug:2026-05-05T17:41:36Z]

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.

[v2.0 / Track A] add POTENTIALLY_STALE edge type — bench-gated +5pp BFS multi-hop

3 participants