fix(graph): reject edges targeting unknown nodes during validation #104

Merged
CoreRasurae merged 1 commit from bugfix/m1-graph-edge-target-validation into master 2026-08-04 19:36:48 +00:00
Member

Summary

Implements the fix for issue #89 (companion TDD-capture issue #92, PR !102): a graph route edge whose source or target named a node absent from nodes was accepted silently at load time instead of raising ConfigurationError, violating Actor Configuration Standard §11 preamble, §11.3.4, and §6.11.5.

create_executor()/Executor.execute() — the router-facing API exercised by the TDD regression test and by the Robot integration tests — never called into the existing cleveractors.validation package at all, and that package's actor-level path (_actor.py _validate_graph_actor) never checked edge endpoints either (the spec-level path already did, but that's a different, unexercised config shape). Rather than wiring the full validate_dict()/validate_actor_config() dispatcher into Executor.__init__ (which would newly start enforcing llm/tool/multi_actor structural checks never exercised via create_executor() before — an unrelated regression risk), the fix adds a narrowly-scoped _validate_graph_edge_endpoints() helper in runtime_dispatch.py, called from both _execute_graph() and _execute_graph_stream() right after pg_nodes/pg_edges are built and before the agent-creation loop, so a malformed graph never instantiates an agent or processes a message (§12.1 lifecycle ordering). start/end/START/END are always accepted regardless of explicit declaration, matching PureLangGraph's auto-injection/normalization (§6.2.1, §6.4).

The existing (unwired) spec-level edge check in cleveractors.validation is left as-is — out of scope for this actor-level runtime bug, preserved rather than removed.

Changes

  • src/cleveractors/runtime_dispatch.py: new _validate_graph_edge_endpoints(), wired into _execute_graph()/_execute_graph_stream().
  • features/pure_graph_edge_target_validation.feature / features/steps/pure_graph_edge_target_validation_steps.py: removed @tdd_expected_fail from the two issue #91 regression scenarios (now pass unconditionally; @tdd_issue/@tdd_issue_91 retained as permanent regression guards), added 3 new scenarios (END-target normalization, START-source normalization, fully valid graph).
  • robot/email_graph_negative_tests.robot: 3 new integration test cases reusing the existing EmailGraphLib.
  • CHANGELOG.md: new Fixed entry.

Test plan

  • nox -s lint green
  • nox -s format -- --check green
  • nox -s typecheck green
  • nox -s security_scan green
  • nox -s dead_code green
  • nox -s complexity green
  • nox -s unit_tests green (2910/2910 scenarios)
  • nox -s integration_tests green
  • nox -s coverage_report — 96.7% (>= 96.5% threshold)

Closes #89

## Summary Implements the fix for issue #89 (companion TDD-capture issue #92, PR !102): a graph route edge whose `source` or `target` named a node absent from `nodes` was accepted silently at load time instead of raising `ConfigurationError`, violating Actor Configuration Standard §11 preamble, §11.3.4, and §6.11.5. `create_executor()`/`Executor.execute()` — the router-facing API exercised by the TDD regression test and by the Robot integration tests — never called into the existing `cleveractors.validation` package at all, and that package's actor-level path (`_actor.py` `_validate_graph_actor`) never checked edge endpoints either (the spec-level path already did, but that's a different, unexercised config shape). Rather than wiring the full `validate_dict()`/`validate_actor_config()` dispatcher into `Executor.__init__` (which would newly start enforcing llm/tool/multi_actor structural checks never exercised via `create_executor()` before — an unrelated regression risk), the fix adds a narrowly-scoped `_validate_graph_edge_endpoints()` helper in `runtime_dispatch.py`, called from both `_execute_graph()` and `_execute_graph_stream()` right after `pg_nodes`/`pg_edges` are built and before the agent-creation loop, so a malformed graph never instantiates an agent or processes a message (§12.1 lifecycle ordering). `start`/`end`/`START`/`END` are always accepted regardless of explicit declaration, matching `PureLangGraph`'s auto-injection/normalization (§6.2.1, §6.4). The existing (unwired) spec-level edge check in `cleveractors.validation` is left as-is — out of scope for this actor-level runtime bug, preserved rather than removed. ## Changes - `src/cleveractors/runtime_dispatch.py`: new `_validate_graph_edge_endpoints()`, wired into `_execute_graph()`/`_execute_graph_stream()`. - `features/pure_graph_edge_target_validation.feature` / `features/steps/pure_graph_edge_target_validation_steps.py`: removed `@tdd_expected_fail` from the two issue #91 regression scenarios (now pass unconditionally; `@tdd_issue`/`@tdd_issue_91` retained as permanent regression guards), added 3 new scenarios (END-target normalization, START-source normalization, fully valid graph). - `robot/email_graph_negative_tests.robot`: 3 new integration test cases reusing the existing `EmailGraphLib`. - `CHANGELOG.md`: new `Fixed` entry. ## Test plan - [x] `nox -s lint` green - [x] `nox -s format -- --check` green - [x] `nox -s typecheck` green - [x] `nox -s security_scan` green - [x] `nox -s dead_code` green - [x] `nox -s complexity` green - [x] `nox -s unit_tests` green (2910/2910 scenarios) - [x] `nox -s integration_tests` green - [x] `nox -s coverage_report` — 96.7% (>= 96.5% threshold) Closes #89
CoreRasurae added this to the v2.1.0 milestone 2026-08-04 13:27:05 +00:00
hurui200320 left a comment

PR Review: !104 (Ticket #89)

Verdict: Approve

The implementation correctly addresses the core bug: graph routes with edges referencing undeclared nodes now raise ConfigurationError before any agent is instantiated or any message is processed. The fix is narrowly scoped to the runtime dispatch path, preserves the existing (unwired) spec-level validator, and includes Behave regression tests plus Robot integration tests. Code quality, type safety, and error messaging are all satisfactory.

Critical Issues

None.

Major Issues

None.

Minor Issues

  • Missing test coverage for the streaming error path
    • File: features/ and robot/
    • Line: N/A
    • Problem: _validate_graph_edge_endpoints() is added to both _execute_graph() and _execute_graph_stream(), but the new tests only exercise the non-streaming execute() path. The streaming path’s error branch is therefore not directly verified, even though it has distinct billing-integrity wrapping.
    • Recommendation: Add one Behave scenario or Robot case that calls executor.execute_stream() (or execute_email_graph_stream) with a dangling edge and asserts a ConfigurationError is raised.

Nits

None.

Summary

PR !104 is a focused, well-explained bugfix. The _validate_graph_edge_endpoints() helper is clean, correctly accepts the auto-injected/normalized start/end/START/END sentinels, and is placed after edge parsing but before agent creation in both graph execution paths. Tests remove the @tdd_expected_fail guard and add positive cases for valid graphs and sentinel normalization. The only notable gap is the lack of a streaming-path negative test; everything else meets the acceptance criteria without introducing regressions.

## PR Review: !104 (Ticket #89) ### Verdict: Approve The implementation correctly addresses the core bug: graph routes with edges referencing undeclared nodes now raise `ConfigurationError` before any agent is instantiated or any message is processed. The fix is narrowly scoped to the runtime dispatch path, preserves the existing (unwired) spec-level validator, and includes Behave regression tests plus Robot integration tests. Code quality, type safety, and error messaging are all satisfactory. ### Critical Issues None. ### Major Issues None. ### Minor Issues - **Missing test coverage for the streaming error path** - **File:** `features/` and `robot/` - **Line:** N/A - **Problem:** `_validate_graph_edge_endpoints()` is added to both `_execute_graph()` and `_execute_graph_stream()`, but the new tests only exercise the non-streaming `execute()` path. The streaming path’s error branch is therefore not directly verified, even though it has distinct billing-integrity wrapping. - **Recommendation:** Add one Behave scenario or Robot case that calls `executor.execute_stream()` (or `execute_email_graph_stream`) with a dangling edge and asserts a `ConfigurationError` is raised. ### Nits None. ### Summary PR !104 is a focused, well-explained bugfix. The `_validate_graph_edge_endpoints()` helper is clean, correctly accepts the auto-injected/normalized `start`/`end`/`START`/`END` sentinels, and is placed after edge parsing but before agent creation in both graph execution paths. Tests remove the `@tdd_expected_fail` guard and add positive cases for valid graphs and sentinel normalization. The only notable gap is the lack of a streaming-path negative test; everything else meets the acceptance criteria without introducing regressions.
CoreRasurae force-pushed bugfix/m1-graph-edge-target-validation from 25371bf89b
Some checks failed
CI / lint (pull_request) Successful in 54s
CI / typecheck (pull_request) Successful in 1m41s
CI / quality (pull_request) Successful in 1m40s
CI / build (pull_request) Successful in 1m37s
CI / security (pull_request) Successful in 2m25s
CI / integration_tests (pull_request) Successful in 3m2s
CI / unit_tests (pull_request) Successful in 5m52s
CI / status-check (pull_request) Failing after 6s
CI / benchmark (pull_request) Failing after 27m42s
CI / coverage (pull_request) Failing after 17m20s
to 8ecc4405dd
Some checks failed
CI / lint (pull_request) Successful in 1m10s
CI / typecheck (pull_request) Successful in 1m56s
CI / security (pull_request) Successful in 1m34s
CI / build (pull_request) Failing after 1m24s
CI / quality (pull_request) Successful in 1m43s
CI / integration_tests (pull_request) Successful in 4m8s
CI / unit_tests (pull_request) Successful in 5m50s
CI / benchmark (pull_request) Has been cancelled
CI / status-check (pull_request) Has been cancelled
CI / coverage (pull_request) Successful in 5m11s
2026-08-04 18:25:13 +00:00
Compare
Author
Member

Addressed the Minor finding from @hurui200320's review: added a Behave scenario (features/pure_graph_edge_target_validation.feature) that drives Executor.execute_stream() against the same dangling-edge-target config, asserting the ConfigurationError propagates before any token is yielded and that executor.last_result gets the billing-integrity placeholder. Confirmed via nox -s coverage_report (scoped to this feature file) that the previously-uncovered _execute_graph_stream() exception branch is now exercised. Full nox -s unit_tests is green at 2911/2911 scenarios (was 2910). Amended the existing commit (no new commit) and force-pushed to this branch. Re-requesting review.

Addressed the Minor finding from @hurui200320's review: added a Behave scenario (`features/pure_graph_edge_target_validation.feature`) that drives `Executor.execute_stream()` against the same dangling-edge-target config, asserting the `ConfigurationError` propagates before any token is yielded and that `executor.last_result` gets the billing-integrity placeholder. Confirmed via `nox -s coverage_report` (scoped to this feature file) that the previously-uncovered `_execute_graph_stream()` exception branch is now exercised. Full `nox -s unit_tests` is green at 2911/2911 scenarios (was 2910). Amended the existing commit (no new commit) and force-pushed to this branch. Re-requesting review.
fix(graph): reject edges targeting unknown nodes during validation
Some checks failed
CI / lint (pull_request) Successful in 52s
CI / typecheck (pull_request) Successful in 1m42s
CI / security (pull_request) Successful in 1m28s
CI / quality (pull_request) Successful in 1m59s
CI / build (pull_request) Successful in 1m35s
CI / integration_tests (pull_request) Successful in 4m42s
CI / unit_tests (pull_request) Successful in 6m27s
CI / coverage (pull_request) Successful in 5m0s
CI / status-check (pull_request) Successful in 12s
CI / lint (push) Successful in 1m55s
CI / typecheck (push) Successful in 2m30s
CI / quality (push) Successful in 1m52s
CI / build (push) Successful in 1m41s
CI / security (push) Successful in 2m21s
CI / integration_tests (push) Successful in 3m38s
CI / unit_tests (push) Successful in 6m1s
CI / benchmark (pull_request) Failing after 23m37s
CI / coverage (push) Successful in 5m51s
CI / status-check (push) Successful in 7s
CI / benchmark (push) Failing after 20m20s
67972f0dc4
A graph route edge whose source or target named a node absent from
nodes was accepted silently instead of raising ConfigurationError,
violating the load-time validation required by the Actor
Configuration Standard §11 preamble, §11.3.4 (graph edge source/
target must reference existing nodes), and §6.11.5 (edge validation).
The actor would execute every node up to a dangling target and exit
silently with partial output; a dangling source can never be
traversed at runtime, so it instead let the graph complete normally
while silently ignoring the structurally invalid edge.

The cleveractors.validation package already implements this check for
spec-level configs (agents + routes at the top level, _routes.py
_validate_graph_route), but create_executor()/Executor.execute() — the
router-facing API this project's TDD regression test and Robot
integration tests exercise — never calls into cleveractors.validation
at all. That package's actor-level path (_actor.py
_validate_graph_actor) also never checked edge endpoints. Rather than
wiring the full validate_dict()/validate_actor_config() dispatcher
into Executor.__init__ (which would additionally start enforcing
llm/tool/multi_actor structural checks never exercised via
create_executor() before, an unrelated behavior change/regression
risk), the fix adds a narrowly-scoped _validate_graph_edge_endpoints()
helper in runtime_dispatch.py, called from both _execute_graph() and
_execute_graph_stream() immediately after pg_nodes/pg_edges are built
from either the legacy route={} or v2.0 routes={main:{}} config shape
— and before the agent-creation loop, so a malformed graph never
instantiates an agent or processes a message (§12.1 lifecycle
ordering). start/end/START/END are always accepted as valid endpoints
regardless of whether they appear in the declared nodes mapping, since
PureLangGraph._initialize_nodes() auto-injects them and
_analyze_graph() normalizes the uppercase spellings (§6.2.1, §6.4).
The cleveractors.validation package's existing (unwired) spec-level
edge check is left as-is per its own scope — extending it further is
unrelated to this actor-level runtime bug and is preserved rather than
removed.

Removes @tdd_expected_fail from the two issue #91 regression scenarios
now that the fix makes them pass unconditionally (leaving @tdd_issue/
@tdd_issue_91 as permanent regression guards), and adds four new
scenarios: an edge targeting the auto-injected END node, an edge
sourced from the auto-injected START node, a fully valid graph, and
(per hurui200320's PR review) a streaming-path counterpart of the
dangling-target scenario — all per issue #89's acceptance criteria.
Adds three Robot integration test cases to
email_graph_negative_tests.robot (reusing the existing EmailGraphLib
rather than a new library) covering the same dangling-target,
dangling-source, and END-normalization behavior end-to-end through
create_executor().

Addresses the sole (Minor) finding from hurui200320's review of this
PR: _validate_graph_edge_endpoints() is called from both
_execute_graph() and _execute_graph_stream(), but the original tests
only drove the non-streaming execute() path, leaving
_execute_graph_stream()'s distinct billing-integrity wrapper (the
try/except around config normalization that populates
executor.last_result with a <no_llm> placeholder before re-raising
ConfigurationError) unverified. The new scenario drives
Executor.execute_stream() to exhaustion against the same
dangling-edge-target config, asserting the ConfigurationError
propagates before any token is yielded and that executor.last_result
is populated with the billing-integrity placeholder.

ISSUES CLOSED: #89
Refs: #91, #92
CoreRasurae force-pushed bugfix/m1-graph-edge-target-validation from 8ecc4405dd
Some checks failed
CI / lint (pull_request) Successful in 1m10s
CI / typecheck (pull_request) Successful in 1m56s
CI / security (pull_request) Successful in 1m34s
CI / build (pull_request) Failing after 1m24s
CI / quality (pull_request) Successful in 1m43s
CI / integration_tests (pull_request) Successful in 4m8s
CI / unit_tests (pull_request) Successful in 5m50s
CI / benchmark (pull_request) Has been cancelled
CI / status-check (pull_request) Has been cancelled
CI / coverage (pull_request) Successful in 5m11s
to 67972f0dc4
Some checks failed
CI / lint (pull_request) Successful in 52s
CI / typecheck (pull_request) Successful in 1m42s
CI / security (pull_request) Successful in 1m28s
CI / quality (pull_request) Successful in 1m59s
CI / build (pull_request) Successful in 1m35s
CI / integration_tests (pull_request) Successful in 4m42s
CI / unit_tests (pull_request) Successful in 6m27s
CI / coverage (pull_request) Successful in 5m0s
CI / status-check (pull_request) Successful in 12s
CI / lint (push) Successful in 1m55s
CI / typecheck (push) Successful in 2m30s
CI / quality (push) Successful in 1m52s
CI / build (push) Successful in 1m41s
CI / security (push) Successful in 2m21s
CI / integration_tests (push) Successful in 3m38s
CI / unit_tests (push) Successful in 6m1s
CI / benchmark (pull_request) Failing after 23m37s
CI / coverage (push) Successful in 5m51s
CI / status-check (push) Successful in 7s
CI / benchmark (push) Failing after 20m20s
2026-08-04 19:16:55 +00:00
Compare
CoreRasurae deleted branch bugfix/m1-graph-edge-target-validation 2026-08-04 19:36:55 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
cleveragents/cleveractors-core!104
No description provided.