Skip to content

Fix undeclared concept removal and prune unreachable specs#654

Open
lchoquel wants to merge 3 commits intofeature/Build-PipeComposefrom
feature/Build-PipeCompose-2
Open

Fix undeclared concept removal and prune unreachable specs#654
lchoquel wants to merge 3 commits intofeature/Build-PipeComposefrom
feature/Build-PipeCompose-2

Conversation

@lchoquel
Copy link
Member

@lchoquel lchoquel commented Feb 8, 2026

Summary

  • Remove the flawed fixed_pipe_parallel_concepts optimization in _fix_undeclared_concept_references that prematurely removed concepts from the undeclared set even when referenced elsewhere, wasting a fix-loop iteration
  • Add _prune_unreachable_specs method that walks the call graph from main_pipe to remove unreachable pipes and transitively unused concepts after all fixes are applied
  • New method handles all controller pipe types: PipeSequence, PipeParallel, PipeBatch, and PipeCondition (filtering SpecialOutcome values)

Test plan

  • make agent-check — all linting, formatting, pyright, and mypy pass
  • make agent-test — full test suite passes

🤖 Generated with Claude Code


Note

Medium Risk
Prunes pipes/concepts in-place based on reachability and reference scanning, which could remove specs that were previously retained if graph traversal or domain detection misses an edge case.

Overview
Improves the builder fix loop by recomputing the undeclared concept set after mutating PipeParallel specs, avoiding prematurely dropping concepts that may still be referenced elsewhere.

Adds a post-fix pruning pass (_prune_unreachable_specs) that walks from main_pipe to delete unreachable pipes and then removes unused concepts (including transitive refines/structure references), with domain-aware handling so external concept refs don’t keep local concepts alive; includes new unit tests covering pruning, domain filtering, and missing-pipe warnings.

Written by Cursor Bugbot for commit 596c761. This will update automatically on new commits. Configure here.

Remove the flawed fixed_pipe_parallel_concepts optimization that
prematurely removed concepts from the undeclared set even when
referenced elsewhere (e.g., another pipe's input), wasting a fix-loop
iteration.

Add _prune_unreachable_specs method that walks the call graph from
main_pipe to remove unreachable pipes and transitively unused concepts,
cleaning up dead weight after all fixes are applied.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d28946ce9d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

After Step 3 nulls out combined_output and sets output to "Anything"
on PipeParallel specs, the undeclared set was never reduced, causing
Step 4 to trigger the LLM pipeline unnecessarily for concepts whose
only references were just eliminated.

Add _collect_local_bare_concept_codes helper that scans all pipe specs
and concept definitions for locally-referenced bare concept codes, and
use it after Step 3 to remove concepts that are no longer referenced
from the undeclared set before the LLM call.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@lchoquel
Copy link
Member Author

lchoquel commented Feb 8, 2026

Code review

Found 1 issue:

  1. _prune_unreachable_specs does not filter concept references by domain, unlike _collect_local_bare_concept_codes in the same PR. In Steps C and D, bare concept codes are extracted unconditionally using concept.split(".")[-1], while _collect_local_bare_concept_codes correctly checks if "." not in concept_ref_or_code or concept_ref_or_code.split(".", maxsplit=1)[0] == domain before adding. If a pipe references an external concept like external_lib.Document, the bare code Document is added to referenced_concepts, which could prevent a local unused concept with the same name from being pruned.

Step C (no domain check):

# Output
output_parse = parse_concept_with_multiplicity(pipe_spec.output)
output_concept = output_parse.concept_ref_or_code
bare_output = output_concept.split(".")[-1] if "." in output_concept else output_concept
referenced_concepts.add(bare_output)
# Inputs
if pipe_spec.inputs:
for input_concept_str in pipe_spec.inputs.values():
input_parse = parse_concept_with_multiplicity(input_concept_str)
input_concept = input_parse.concept_ref_or_code
bare_input = input_concept.split(".")[-1] if "." in input_concept else input_concept
referenced_concepts.add(bare_input)
# PipeParallel combined_output
if isinstance(pipe_spec, PipeParallelSpec) and pipe_spec.combined_output:
combined_parse = parse_concept_with_multiplicity(pipe_spec.combined_output)
combined_concept = combined_parse.concept_ref_or_code
bare_combined = combined_concept.split(".")[-1] if "." in combined_concept else combined_concept
referenced_concepts.add(bare_combined)

_collect_local_bare_concept_codes (correct domain check via _add_if_local):

def _add_if_local(concept_ref_or_code: str) -> None:
if "." not in concept_ref_or_code or concept_ref_or_code.split(".", maxsplit=1)[0] == domain:
bare_code = concept_ref_or_code.rsplit(".", maxsplit=1)[-1] if "." in concept_ref_or_code else concept_ref_or_code
referenced.add(bare_code)
# Scan pipe specs
if pipelex_bundle_spec.pipe:
for pipe_spec in pipelex_bundle_spec.pipe.values():
_add_if_local(parse_concept_with_multiplicity(pipe_spec.output).concept_ref_or_code)
if pipe_spec.inputs:
for input_concept_str in pipe_spec.inputs.values():
_add_if_local(parse_concept_with_multiplicity(input_concept_str).concept_ref_or_code)
if isinstance(pipe_spec, PipeParallelSpec) and pipe_spec.combined_output:
_add_if_local(parse_concept_with_multiplicity(pipe_spec.combined_output).concept_ref_or_code)

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

External domain references (e.g., "external.Document") were incorrectly
marking local concepts with the same bare name as referenced, preventing
them from being pruned. Extract a shared _extract_local_bare_code helper
that filters by domain and apply it in Steps C and D. Also move
reachable_pipes.add() after the existence check in Step A so non-existent
pipes don't silently pollute the reachable set, and log a warning instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

bare_ref = self._extract_local_bare_code(concept_ref_or_code=field_spec.item_concept_ref, domain=domain)
if bare_ref is not None and bare_ref not in referenced_concepts and bare_ref in pipelex_bundle_spec.concept:
referenced_concepts.add(bare_ref)
changed = True
Copy link

Choose a reason for hiding this comment

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

Duplicated concept-reference scanning across new methods

Low Severity

The concept-reference scanning logic (pipe outputs, inputs, PipeParallelSpec.combined_output, concept refines, structure concept_ref, and item_concept_ref) is duplicated nearly verbatim between _prune_unreachable_specs Steps C+D and _collect_local_bare_concept_codes. Both iterate the same fields and call _extract_local_bare_code identically. If a new concept-bearing field is added to any spec type, both locations need to be updated in lockstep, risking silent divergence.

Additional Locations (1)

Fix in Cursor Fix in Web

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.

1 participant