Skip to content

fix(extractor): collapse ids repeated within a single LLM proposal (closes #41) - #52

Merged
Cyrax321 merged 1 commit into
Cyrax321:mainfrom
dchaudhari7177:fix/extractor-intra-proposal-dupes
Aug 17, 2026
Merged

fix(extractor): collapse ids repeated within a single LLM proposal (closes #41)#52
Cyrax321 merged 1 commit into
Cyrax321:mainfrom
dchaudhari7177:fix/extractor-intra-proposal-dupes

Conversation

@dchaudhari7177

Copy link
Copy Markdown
Contributor

Closes #41.

_merge seeds known_decisions / known_findings / known_work from the base state and never grows them, so an id the LLM emitted twice in one response passed the guard twice and was appended twice.

Fix

One line per collection — add the accepted id to its set as it's appended:

if not decision_id or decision_id in known_decisions:
    continue  # never overwrite a recorded fact
known_decisions.add(decision_id)
decisions.append(...)

That makes the guard that stops a proposal overwriting a recorded fact also stop it duplicating itself. All three collections had the same hole, so all three are fixed — pending_work isn't in the issue's repro but has identical code, and fixing two of three would leave the same bug behind under a different name.

First occurrence wins. That's the direction the existing semantics already run — the base state beats the proposal, so earlier beats later — and it's asserted rather than left implicit.

Tests

6 added to tests/test_extractor.py.

Three fail against pristine extractor.py:

FAILED test_a_repeated_decision_id_within_one_proposal_is_collapsed
FAILED test_a_repeated_finding_id_within_one_proposal_is_collapsed
FAILED test_a_repeated_task_id_within_one_proposal_is_collapsed

Three are regression guards that must pass both before and after, and do:

  • test_the_first_occurrence_wins_within_a_proposal — pins which copy survives, not just that one does
  • test_distinct_ids_in_one_proposal_are_all_kept — the de-dup must not collapse genuinely different components, which is how this kind of fix usually goes wrong
  • test_a_proposal_repeating_a_recorded_id_still_cannot_overwrite_itd1 is already recorded; repeating it twice must neither overwrite nor duplicate, i.e. the existing protection still holds through the new code path

Gate

Windows / Python 3.12, all three CI checks from CONTRIBUTING:

  • pytest683 passed, 4 skipped (677 before this branch)
  • ruff check src/ tests/ and ruff format --check src/ tests/ — clean
  • mypy src/continuum (strict) — no issues in 44 source files

🤖 Generated with Claude Code

_merge seeds known_decisions / known_findings / known_work from the base
state and never grows them, so an id the LLM emitted twice in one
response passed the guard twice and was appended twice. A single
proposal could therefore produce two decisions with the same
decision_id.

Each accepted id is now added to its set as it is appended, so the same
guard that stops a proposal overwriting a recorded fact also stops it
duplicating itself. Three call sites, one line each.

First occurrence wins, which is the same direction the existing
semantics already run: the base state beats the proposal, so earlier
beats later.

Tests: 6 added to tests/test_extractor.py. The three collapse cases
(decision, finding, pending work) fail against pristine extractor.py.
The other three are regression guards that must pass both before and
after, and do: first-occurrence-wins, distinct ids in one proposal all
survive, and a proposal repeating an already-recorded id still neither
overwrites nor duplicates it.

Gate: pytest 683 passed / 4 skipped (677 before); ruff check, ruff
format --check and mypy (strict) all clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 14, 2026

Copy link
Copy Markdown

@dchaudhari7177 is attempting to deploy a commit to the cyrax8590-gmailcom's projects Team on Vercel.

A member of the Team first needs to authorize it.

@Cyrax321 Cyrax321 self-assigned this Aug 17, 2026
@Cyrax321

Copy link
Copy Markdown
Owner

Thank you for this PR, and for closing the intra-proposal duplicate-id gap in the extractor!

The fix is correct: it seeds each known_* id set as proposals are appended (via known_decisions.add / known_findings.add / known_work.add in _merge), so a duplicate id the LLM emits twice within a single proposal is collapsed instead of appended a second time. while recorded (base-state) ids still win.

CI Check Analysis (run locally on the branch)

  • pytest: 680 passed, 7 skipped, 0 failed. Note: stale branch (fewer tests than current main); no failures introduced.
  • ruff check src/ tests/: passed.
  • ruff format --check src/ tests/: 75 files already formatted (branch predates the langchain/langgraph integration tests; this PR's own files are clean).
  • mypy src/continuum --strict: 4 errors in 2 files (openai, langgraph), matching this branch's baseline; all import-not-found for uninstalled optional adapters. No new strict errors.

Code & Test Review Findings

  • _merge (src/continuum/state/extractor.py): verified all three loops (decisions, findings, pending_work) now record the id into its known_* set after the first occurrence, so the second copy is skipped and a recorded id cannot be overwritten. Six new tests cover repeated ids per component, first-occurrence-wins, distinct ids kept, and recorded-id hijack resistance. Strong, specific coverage.

Concern (not a defect): branch is stale relative to main; please rebase before merge. Also note PR #56 also touches extractor.py. merge/rebase the two in a deliberate order.

LGTM from a review standpoint, thank you for the contribution!

@Cyrax321 Cyrax321 added bug Something isn't working detector Drift and anomaly detection components labels Aug 17, 2026
@Cyrax321
Cyrax321 merged commit a1bdef4 into Cyrax321:main Aug 17, 2026
5 of 6 checks passed
@Cyrax321 Cyrax321 added the state Semantic state, projection, validator, extractor label Aug 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working detector Drift and anomaly detection components state Semantic state, projection, validator, extractor

2 participants