test(langgraph): capture content_not_contains edge condition regression (#95) #99
Merged
CoreRasurae
merged 1 commits from 2026-08-04 10:07:51 +00:00
tdd/m1-pure-graph-content-not-contains into master
1 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
46bff0d451 |
test(langgraph): capture content_not_contains edge condition regression (#95)
CI / lint (pull_request) Successful in 54s
CI / typecheck (pull_request) Successful in 1m48s
CI / security (pull_request) Successful in 1m20s
CI / quality (pull_request) Successful in 1m17s
CI / build (pull_request) Successful in 1m53s
CI / integration_tests (pull_request) Successful in 4m31s
CI / unit_tests (pull_request) Successful in 5m45s
CI / coverage (pull_request) Successful in 5m3s
CI / status-check (pull_request) Successful in 6s
CI / security (push) Successful in 1m22s
CI / quality (push) Successful in 1m55s
CI / build (push) Successful in 2m9s
CI / benchmark (pull_request) Failing after 21m27s
CI / integration_tests (push) Successful in 4m15s
CI / unit_tests (push) Successful in 5m59s
CI / typecheck (push) Failing after 15m5s
CI / lint (push) Failing after 15m13s
CI / coverage (push) Has been skipped
CI / status-check (push) Failing after 13s
CI / benchmark (push) Successful in 22m57s
Adds a failing-first Behave regression test proving issue #95: PureLangGraph._evaluate_edge_condition() has no branch for content_not_contains (Actor Configuration Standard §5.4), so it falls into the "unknown condition type" catch-all and always returns True. When a classifier node has sibling content_contains/content_not_contains edges (the classic if/else idiom), both fire whenever the configured text is present, so _get_next_nodes() returns two targets instead of one. The scenario is tagged @tdd_issue, @tdd_issue_95, and @tdd_expected_fail per the project's TDD issue-capture workflow. No runtime hook previously existed to make @tdd_expected_fail meaningful, so this commit also adds TddExpectedFailPolicy (features/tdd_expected_fail.py) and wires it into features/environment.py's after_step/after_scenario hooks: it flips an @tdd_expected_fail scenario's AssertionError failures to passing (proving the bug is still present), and fails the scenario if it ever passes outright without an inversion (catching a stale tag left after the bug was actually fixed). Non-AssertionError failures are never inverted. The actual fix lands separately on bugfix/m1-pure-graph-content-not-contains per issue #95. Addresses PR #99 reviews (rui.hu): - Major: the scenario's premise conflicted with §5.4's Honored-By table, which listed content_not_contains as honored only by "Stream router, conditional nodes, bridge router" - excluding pure-graph edges. A full read of the spec confirmed this was a real, consistent restriction (every content_not_contains example was a stream-router split; the spec's only graph-edge branching example used two content_contains conditions, not a contains/not-contains pair), not an obvious omission. Per maintainer decision, the original intent was always symmetric with content_contains ("All subsystems"), so this commit adds docs/adr/ADR-2034-content-not-contains-all-subsystems.md and revises docs/index.md to v1.1.0: §5.4 Honored By extended to "All subsystems" for content_not_contains, plus a new §21.1 Revision History section. - Minor: TddExpectedFailPolicy now enforces the CONTRIBUTING.md tag-combination rules (@tdd_issue_<N> requires @tdd_issue; @tdd_expected_fail requires both @tdd_issue and a @tdd_issue_<N>), raising AssertionError on a malformed combination instead of silently under-validating the scenario. - Nits: removed the unused context.results assignment in the background step; reordered environment.py so the recursion-limit comment/call no longer sits beside the new first-party import as if commenting on it. - Minor: TddExpectedFailPolicy's guard-rail branches - stale-tag detection, malformed tag-combination validation, and non-AssertionError failures never being inverted - were only exercised indirectly through the single happy-path @tdd_issue_95 scenario. Adds features/unit_tdd_expected_fail_policy.feature (7 scenarios) and its step definitions, which construct behave.model.Scenario/Step objects directly and invoke TddExpectedFailPolicy's classmethods against a throwaway context, so each guard-rail branch is exercised in isolation without depending on a real scenario's outcome (a scenario tagged to deliberately go stale or malformed would otherwise fail the real suite). Kept in this same commit rather than deferred to a fast-follow PR, since none of this has merged to master yet. All fixes are folded into this same commit rather than split across PRs, since none of this has merged to master yet - it is one feature still being iterated on. Refs: #95, #96 |