Skip to content

perf(ingest): incremental per-turn edge detection (#1000) - #1003

Merged
github-actions[bot] merged 6 commits into
mainfrom
fix/issue-1000-incremental-edge-detection
Jun 23, 2026
Merged

perf(ingest): incremental per-turn edge detection (#1000)#1003
github-actions[bot] merged 6 commits into
mainfrom
fix/issue-1000-incremental-edge-detection

Conversation

@robotrocketscience

@robotrocketscience robotrocketscience commented Jun 23, 2026

Copy link
Copy Markdown
Owner

Summary

Makes the #988 semantic-edge build incremental so per-turn auto-relationship ingest no longer re-audits the whole store on every turn.

Closes #1000.

Problem

write_semantic_edges(store) audits the entire store via an O(n²) candidate-pair scan capped at max_candidate_pairs (5000). Wired into ingest per-turn, this produced two unsatisfactory build paths (both measured on LoCoMo10):

  • Per-turn full audit every turn → 25–40 min/conv (the O(n²) whole-store re-scan).
  • Single post-ingest pass → hits the 5000-pair cap and undercounts ~57% (conv-0: 46 edges vs the per-turn 72).

Same root cause: candidate-pair generation rescans the whole store instead of scoping to what changed.

Fix

Scope candidate-pair generation to pairs touching at least one belief newly inserted that turn (the delta inserted already computed in ingest._ingest_turn_ids).

Why this is equivalent to the full audit: every old-old high-confidence pair (both endpoints from prior turns) was already generated and processed in the turn when the later of its two endpoints was first inserted. So the only new edges a full audit writes each turn are exactly the delta-involving pairs — restricting to them discovers the same edges without the whole-store rescan. Because each turn adds a bounded number of pairs, the per-turn build never hits the 5000-pair cap, recovering the ~57% the single pass lost.

Write-gate preserved across turns

max_edges_per_belief (default 8) caps how many CONTRADICTS edges a belief accretes. The full audit enforced this by re-encountering a belief's prior edges each call. In incremental mode those old-old pairs aren't generated, so each endpoint's write-gate budget is seeded from its existing CONTRADICTS edge count in the store before the write loop — making the cap a true hard per-belief bound across turns. Full-store mode (new_belief_ids=None) is byte-identical to before.

Changes (atomic commits)

  1. refactor(dedup)restrict_to_ids filter on _jaccard_prefiltered_pairs (skip before Jaccard; None ⇒ unchanged).
  2. feat(relationship_detector) — thread restrict_to_ids through relationships_audit.
  3. feat(relationship_detector)new_belief_ids incremental mode in write_semantic_edges with store-seeded write-gate budget.
  4. feat(ingest) — pass the per-turn delta (inserted) to write_semantic_edges.
  5. test(relationship_detector) — 7 tests: incremental==full equivalence, determinism, full-store/idempotent backward-compat, hard-cap across turns, and restrict_to_ids unit coverage.

Determinism / scope

Stdlib-only, no random/embeddings/LLM — #605 posture holds. Sort and canonicalization unchanged, so edge tables stay byte-stable. Default-off flag (AELFRICE_AUTO_RELATIONSHIPS) unchanged; a fresh install still writes no edges and full-store mode is byte-identical.

Acceptance

  • Incremental per-turn build reproduces the full-audit edge set (unit test).
  • No cap-truncation undercount: per-turn deltas never hit max_candidate_pairs.
  • Determinism preserved; no regression with the flag off.
  • Per-belief cap holds as a hard bound across turns (unit test, cap=3).

LoCoMo10 wall-clock and exact-count reproduction against the per-turn baseline are a lab-side verification (the corpus lives lab-side); the equivalence and hard-cap properties are proven here by unit tests on synthetic stores.

Summary by Sourcery

Make semantic edge detection incremental by scoping per-turn candidate generation to newly inserted beliefs, preserving determinism and full-store behavior while avoiding whole-store rescans.

Enhancements:

  • Add optional ID-based restriction to Jaccard prefiltered pair generation and thread it through the relationship audit pipeline to support delta-scoped edge detection.
  • Introduce incremental mode in semantic edge writing that uses new belief IDs and existing edges to enforce a hard per-belief cap across turns while keeping full-store mode behavior unchanged.
  • Update ingest to pass each turn’s inserted belief IDs into semantic edge writing so per-turn auto-relationship detection runs incrementally instead of re-auditing the full store.

Tests:

  • Add incremental vs full-audit equivalence, determinism, backward-compatibility, cross-turn cap enforcement, and restrict_to_ids coverage tests for semantic edge detection.

@robotrocketscience robotrocketscience added the author-Toug PR coordination mutex label Jun 23, 2026
@sourcery-ai

sourcery-ai Bot commented Jun 23, 2026

Copy link
Copy Markdown

Reviewer's Guide

Implements incremental, per-turn semantic edge detection by scoping candidate-pair generation and write-gating to newly inserted beliefs while preserving full-store behavior, determinism, and per-belief caps, and updates ingest to use the new incremental path with comprehensive tests.

Sequence diagram for incremental per-turn semantic edge detection

sequenceDiagram
    participant Ingest as ingest__ingest_turn_ids
    participant Store
    participant RelDet as write_semantic_edges
    participant Audit as relationships_audit
    participant Dedup as _jaccard_prefiltered_pairs

    Ingest->>RelDet: write_semantic_edges(Store, new_belief_ids=inserted)
    activate RelDet
    RelDet->>RelDet: restrict = frozenset(new_belief_ids)
    RelDet->>Audit: relationships_audit(Store, restrict_to_ids=restrict)
    activate Audit
    Audit->>Dedup: _jaccard_prefiltered_pairs(beliefs, max_pairs, restrict_to_ids=restrict)
    activate Dedup
    Dedup-->>Audit: candidates, raw_count, truncated
    deactivate Dedup
    Audit-->>RelDet: RelationshipsAuditReport(pairs)
    deactivate Audit

    RelDet->>RelDet: high_pairs = [p for p in report.pairs if p.relationship == EDGE_CONTRADICTS]
    RelDet->>Store: edges_for_beliefs(endpoint_ids)
    Store-->>RelDet: existing_edges

    loop for each high_pair
        RelDet->>Store: get_edge(src_id, dst_id, EDGE_CONTRADICTS)
        alt edge_missing
            RelDet->>Store: insert_edge(Edge(src, dst, EDGE_CONTRADICTS))
        else edge_exists
            RelDet->>RelDet: [increment budget only if full-store mode]
        end
    end

    RelDet-->>Ingest: SemanticEdgeWriteReport
    deactivate RelDet
Loading

File-Level Changes

Change Details Files
Add optional ID-restriction filter to pair-prefiltering and thread it through relationship auditing to support incremental candidate generation.
  • Extend _jaccard_prefiltered_pairs to accept an optional restrict_to_ids frozenset and skip Jaccard scoring for pairs where both endpoints are outside the set before computing similarity.
  • Update raw candidate counting semantics in _jaccard_prefiltered_pairs so the count reflects only scored pairs, while preserving previous behavior when restrict_to_ids is None.
  • Add a restrict_to_ids parameter to relationships_audit, including validation and docstring updates, and forward it directly into _jaccard_prefiltered_pairs after normalizing to a frozenset.
src/aelfrice/dedup.py
src/aelfrice/relationship_detector.py
Make write_semantic_edges support incremental mode scoped to new beliefs while preserving full-store behavior and enforcing a hard per-belief edge cap across turns.
  • Introduce a new_belief_ids parameter to write_semantic_edges, treating None as legacy full-store mode and a non-None sequence as incremental mode.
  • Derive a restrict frozenset from new_belief_ids and pass it into relationships_audit to limit candidate pairs to those touching at least one new belief, avoiding O(n²) full-store scans per turn.
  • In incremental mode, precompute the CONTRADICTS edge count per endpoint via store.edges_for_beliefs and seed edges_per_belief with those counts before the write loop, instead of counting them during the loop.
  • Add a count_existing_in_loop flag so that in full-store mode existing edges still increment per-belief budgets inside the loop (preserving original behavior), while incremental mode relies solely on the preseeded counts.
  • Document incremental semantics, equivalence to full audits, and write-gate behavior in the write_semantic_edges docstring.
src/aelfrice/relationship_detector.py
Wire ingest to call write_semantic_edges incrementally using the per-turn inserted delta. src/aelfrice/ingest.py
Add tests validating incremental equivalence, determinism, backward compatibility, hard per-belief caps, and restrict_to_ids behavior.
  • Introduce a new test module test_relationship_detector_incremental.py using a real in-memory MemoryStore and factual Belief instances to exercise edge-writing behavior.
  • Add tests comparing incremental per-turn edge construction against a single full-store audit to ensure the final edge set matches and is non-trivial.
  • Add determinism tests asserting that repeated incremental runs on identical input produce byte-identical edge tables.
  • Add full-store backward-compatibility and idempotency tests for write_semantic_edges when new_belief_ids is None, ensuring edge counts and skip counts align with legacy behavior.
  • Add tests that a per-belief cap (e.g., 3 edges) is respected across multiple incremental turns for a hub node, and tests that restrict_to_ids in _jaccard_prefiltered_pairs properly filters pairs and is a no-op when None or omitted.
tests/test_relationship_detector_incremental.py

Assessment against linked issues

Issue Objective Addressed Explanation
#1000 Implement incremental per-turn semantic edge detection so that candidate pairs are generated only between newly inserted beliefs in the current turn and existing beliefs, instead of performing an O(n²) full-store scan each turn.
#1000 Ensure the incremental build reproduces the full-audit edge set (no max_candidate_pairs cap undercount) and enforces the per-belief max_edges_per_belief cap as a hard bound across turns, with appropriate tests.
#1000 Preserve determinism and backward compatibility when auto-relationships are disabled or when running in full-store mode (new_belief_ids=None), including not changing behavior when the feature flag is off.

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 Jun 23, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@robotrocketscience, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 38 minutes and 13 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more credits in the billing tab to continue.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses rolling per-developer review limits. Reviews become available again as older review attempts age out of the rolling limit window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1cbff01d-623c-4e00-a923-b971d7ae0a18

📥 Commits

Reviewing files that changed from the base of the PR and between 6839b3c and 5fec7eb.

📒 Files selected for processing (4)
  • src/aelfrice/dedup.py
  • src/aelfrice/ingest.py
  • src/aelfrice/relationship_detector.py
  • tests/test_relationship_detector_incremental.py
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/issue-1000-incremental-edge-detection

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.

@github-actions

github-actions Bot commented Jun 23, 2026

Copy link
Copy Markdown

PR-size soft cap

This PR is over the advisory size threshold:

  • 390 changed lines (limit: 200)
  • 4 changed files (limit: 3)

Bigger PRs collide with more open work, which under the parallel-session workflow tends to produce repeated attn:merge-conflict cycles (see #602). When practical, split into smaller PRs that each touch a focused surface.

This is advisory only — nothing is blocked. If the size is intentional (large refactor, module removal, generated code), apply the size:override label and this comment will be removed on the next push.

@robotrocketscience robotrocketscience added the attn:review Needs review (PR open, awaiting reviewer) label Jun 23, 2026

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

  • The meaning of raw_count in _jaccard_prefiltered_pairs has changed from "all O(n²) pairs visited" to "pairs actually scored", and now depends on restrict_to_ids; if any callers rely on the previous interpretation, consider explicitly documenting this behavioral change at the public call sites (e.g., relationships_audit) or renaming the field to avoid ambiguity.
  • The tests in test_relationship_detector_incremental.py reach into store._conn directly to inspect edges; consider adding a small public helper on MemoryStore (or using existing APIs) to expose this information so the tests don’t depend on private attributes.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The meaning of `raw_count` in `_jaccard_prefiltered_pairs` has changed from "all O(n²) pairs visited" to "pairs actually scored", and now depends on `restrict_to_ids`; if any callers rely on the previous interpretation, consider explicitly documenting this behavioral change at the public call sites (e.g., `relationships_audit`) or renaming the field to avoid ambiguity.
- The tests in `test_relationship_detector_incremental.py` reach into `store._conn` directly to inspect edges; consider adding a small public helper on `MemoryStore` (or using existing APIs) to expose this information so the tests don’t depend on private attributes.

## Individual Comments

### Comment 1
<location path="tests/test_relationship_detector_incremental.py" line_range="78-84" />
<code_context>
+    return b
+
+
+def _contradicts_edges(store: MemoryStore) -> list[tuple[str, str, str]]:
+    """Return all CONTRADICTS edges as sorted (src, dst, type) tuples."""
+    rows = store._conn.execute(  # type: ignore[attr-defined]
+        "SELECT src, dst, type FROM edges WHERE type = ? ORDER BY src, dst",
+        (EDGE_CONTRADICTS,),
+    ).fetchall()
+    return [(r[0], r[1], r[2]) for r in rows]
+
+
</code_context>
<issue_to_address>
**nitpick:** Avoid reaching into the store’s private connection in tests if there’s a public helper available, or add a small wrapper for clarity.

`_contradicts_edges` is reaching into `store._conn` directly. Prefer using a public API for fetching edges (or add a small test-only wrapper that calls `store.edges_for_beliefs`) so the tests don’t depend on the store’s internal schema/attributes. If no such API exists, at least add a TODO noting that changes to `_conn` or the edges schema will require updating these tests.
</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 tests/test_relationship_detector_incremental.py
Comment thread tests/test_relationship_detector_incremental.py Fixed
Comment thread tests/test_relationship_detector_incremental.py Fixed
@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:idnn:2026-06-23T18:13:47Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

Needs rebase — conflicts with just-merged #1002.

#1002 (issue #999, SUPPORTS/SUPERSEDES edge writers) landed on main a minute ago via FF push (6839b3c0). It modified the same three files this PR touches — src/aelfrice/relationship_detector.py, src/aelfrice/dedup.py, src/aelfrice/ingest.py — so this branch is now behind main and git merge-tree reports 2 real conflict markers (CI checks cancelled as a result).

The overlap is concentrated at the ingest auto-detect gate in ingest.py (where #1002 added the write_supports_edges / write_supersedes_edges calls next to write_semantic_edges) and the new writer/report scaffolding in relationship_detector.py + dedup.py.

Please rebase onto current github/main and re-push; the incremental per-turn detection (#1000) and the new edge-type writers (#999) should compose cleanly once the gate block is reconciled. Flagging attn:merge-conflict and releasing the review claim so it routes back to you.

@robotrocketscience robotrocketscience added attn:merge-conflict PR branch needs rebase and removed attn:review Needs review (PR open, awaiting reviewer) labels Jun 23, 2026
@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:idnn:2026-06-23T18:14:07Z]

@robotrocketscience robotrocketscience added ready-to-merge Trigger merge-train: FF main to this PR's head and removed ready-to-merge Trigger merge-train: FF main to this PR's head labels Jun 23, 2026
@robotrocketscience

Copy link
Copy Markdown
Owner Author

⚠️ Conflicts with main after #1002 mergedmergeable: CONFLICTING. Both PRs edit the same surface:

Needs a rebase on current main. While rebasing, please also resolve the design point the #1000 coordination note flagged: the two new writers from #1002 (write_supports_edges, write_supersedes_edges) do not take new_belief_ids, so they still run a full-store audit every turn — the 25–40 min/conv cost this PR fixes for CONTRADICTS would persist for SUPPORTS/SUPERSEDES. Either thread the same incremental delta through both new writers, or note explicitly why it's deferred.

Pulled ready-to-merge until the rebase lands; re-add it (or ping) and I'll put it back on the train.

…airs

When restrict_to_ids is provided (a frozenset of belief IDs), skip any
pair where neither endpoint appears in the set before scoring. raw_count
counts only pairs actually scored (not skipped). Default None preserves
byte-identical behaviour.
…hips_audit

Adds keyword-only restrict_to_ids param (set/frozenset/None). When
provided, passes it as a frozenset to _jaccard_prefiltered_pairs so
candidate-pair generation is scoped to pairs touching the given IDs.
Default None preserves byte-identical full-store audit behaviour.
…_semantic_edges with store-seeded write-gate budget

Adds keyword-only new_belief_ids param (Sequence[str] | None). When
provided, scopes the audit to pairs touching at least one new belief
(delta from the current turn), which is provably equivalent to the
full-store audit for discovering new edges. In incremental mode the
write-gate budget per endpoint is seeded from existing CONTRADICTS
edges in the store before the write loop so the hard per-belief cap
is enforced across turns, not just within one call. The existing-skip
branch only increments edges_per_belief in full-store mode to avoid
double-counting the seeded values. Default new_belief_ids=None
preserves byte-identical full-store behaviour.
…ntal edge detection

Changes write_semantic_edges(store) to write_semantic_edges(store,
new_belief_ids=inserted) so the edge audit is scoped to pairs
touching the beliefs newly inserted that turn rather than re-scanning
the whole store O(n²) every turn.
…lence, determinism, hard-cap, restrict)

Five test groups:
1. Incremental per-turn calls produce the same final CONTRADICTS edge set
   as a single full-store audit (equivalence proof).
2. Two incremental builds on identical input are byte-equal (determinism).
3. Full-store mode (new_belief_ids=None) is byte-identical to the original
   path (backward-compat / no-op parity, idempotency).
4. Hub belief never exceeds max_edges_per_belief across turns (hard-cap
   enforcement via store-seeded write-gate budget).
5. _jaccard_prefiltered_pairs restrict_to_ids unit test: every returned
   pair has at least one endpoint in the set; None returns the superset.
@robotrocketscience
robotrocketscience force-pushed the fix/issue-1000-incremental-edge-detection branch from 4d44668 to 46a6c86 Compare June 23, 2026 18:18
@robotrocketscience robotrocketscience added attn:review Needs review (PR open, awaiting reviewer) and removed attn:merge-conflict PR branch needs rebase labels Jun 23, 2026
@robotrocketscience

Copy link
Copy Markdown
Owner Author

Rebased onto main after #1002 merged. The only conflict was the ingest call site: resolved so write_semantic_edges takes the incremental per-turn delta (new_belief_ids=inserted) while the new SUPPORTS/SUPERSEDES writers stay full-store passes. Those two still re-audit the whole store each turn — extending the delta-scoping to them is a follow-up (filing separately). Suite green after rebase (322 passed / 5 skipped on the ingest+relationship+dedup+edge-writer sweep); discretion grep clean. Now MERGEABLE.

@github-actions

Copy link
Copy Markdown

merge-train: blocked

1 review thread(s) are unresolved on these files: tests/test_relationship_detector_incremental.py. Resolve them on the PR (click 'Resolve conversation' on each) and re-add the label.

The ready-to-merge label has been removed. Address the issue above and re-add the label when you're ready for another attempt.

@github-actions github-actions Bot removed the ready-to-merge Trigger merge-train: FF main to this PR's head label Jun 23, 2026
@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:gylf:2026-06-23T18:24:38Z]

Remove unused 'import pytest' and 'DEFAULT_MAX_EDGES_PER_BELIEF' from the
incremental edge-detection test module. Both were imported but never
referenced; CodeQL flagged them as code-scanning alerts, blocking the
merge-train on unresolved review threads. The store._conn access the
Sourcery nitpick flagged is consistent with established convention across
the test suite (test_auditor, test_clamp_ghosts, etc.) and is left as-is.
@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:Setr:2026-06-23T18:28:04Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:Setr:2026-06-23T18:28:08Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

Review complete. The incremental delta-scoping is sound: scoping candidate-pair generation to pairs touching at least one new_belief_ids endpoint is provably edge-equivalent to a full-store audit (every old-old pair was already evaluated when the later of its endpoints was first inserted), and the store-seeded write-gate budget makes max_edges_per_belief a true cross-turn hard bound. Equivalence/determinism/cap/restrict are all covered by tests/test_relationship_detector_incremental.py (7 tests, all green).

One semantics note for the record (not blocking): incremental == full-store edge set holds without cap pressure; under cap pressure the two modes may select different surviving edges (incremental writes turn-by-turn, full-store sees all pairs in one global sort), though both honor the ≤cap bound. Test 1 correctly scopes its equivalence assertion to the no-cap case.

Cleared the blockers: removed two CodeQL-flagged unused imports (pytest, DEFAULT_MAX_EDGES_PER_BELIEF) in a follow-up commit; the Sourcery store._conn nitpick is resolved as consistent with suite convention. CI green on 5fec7ebd, FF on main, all commits signed. Adding ready-to-merge.

@robotrocketscience robotrocketscience added the ready-to-merge Trigger merge-train: FF main to this PR's head label Jun 23, 2026
@github-actions
github-actions Bot merged commit 5fec7eb into main Jun 23, 2026
29 checks passed
@github-actions github-actions Bot removed the ready-to-merge Trigger merge-train: FF main to this PR's head label Jun 23, 2026
@github-actions

Copy link
Copy Markdown

merge-train: merged 5fec7ebmain via FF push.

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:gylf:2026-06-23T18:31:44Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

Review: APPROVE (with one scoping note for the operator)

Verified against post-#1002 main (rebased clean, FF-OK, 6 signed commits, CI green):

  • Incremental equivalence is correct for edge discovery. restrict_to_ids keeps only candidate pairs touching ≥1 delta belief; every old–old pair was already evaluated when the later endpoint was inserted, so delta-scoping discovers the same new CONTRADICTS edges as a full-store audit. The _jaccard_prefiltered_pairs filter (already on main) is right, and raw_count increments after the skip so raw_full ≥ raw_restricted holds.
  • The store-seeded write-gate budget is a faithful realization of the original "hard per-belief bound across re-runs" intent, not a regression. Incremental seeds each endpoint's existing CONTRADICTS count upfront, then doesn't double-count in the loop (count_existing_in_loop = not incremental). When the max_edges_per_belief cap does not bind, incremental == full-store (covered by test_incremental_equals_full_audit_edge_set). When it does bind, incremental enforces a stricter cross-turn cap — the tests are honest about this: the hard-cap case is verified separately (test_incremental_hard_cap_across_turns, asserts ≤ cap) rather than folded into the equivalence claim. Good test discipline.
  • Full-store mode is byte-identical (new_belief_ids=None), protecting the feat(ingest): extend edge substrate with SUPPORTS + SUPERSEDES (#999) #1002 SUPPORTS/SUPERSEDES writers and the non-incremental CONTRADICTS callers (test_full_store_mode_backward_compat + idempotency parity).
  • Composition in ingest.py is clean and documented: incremental CONTRADICTS + full-store SUPPORTS/SUPERSEDES.

Scoping note (operator call, not a blocker): this PR makes CONTRADICTS only incremental. SUPPORTS/SUPERSEDES still run a full-store audit every turn, tracked as the follow-up #1004. Per a fresh #1001 LoCoMo10 measurement on the post-#1002 substrate, SUPPORTS dominates the edge set 18,707 to 1,659 CONTRADICTS (866 SUPERSEDES) — so the bulk of the per-turn O(n²) cost #1000 cites is in the writers this PR does not yet scope. The latency win here is real but partial; #1000's 25–40 min/conv goal isn't met until #1004 lands. Flagging so the #1000#1004 issue linkage is a conscious choice (close #1000 now with #1004 as the tracked remainder, vs. keep #1000 open until #1004).

Code is correct regardless of that linkage choice. Adding ready-to-merge.

@robotrocketscience robotrocketscience added the ready-to-merge Trigger merge-train: FF main to this PR's head label Jun 23, 2026
@github-actions github-actions Bot removed the ready-to-merge Trigger merge-train: FF main to this PR's head label Jun 23, 2026
@github-actions

Copy link
Copy Markdown

merge-train: merged 5fec7ebmain via FF push.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

attn:review Needs review (PR open, awaiting reviewer) author-Toug PR coordination mutex

Projects

None yet

Development

Successfully merging this pull request may close these issues.

perf(ingest): incremental per-turn edge detection (fix 25-40min/conv latency + max_candidate_pairs ~57% undercount)

2 participants