Feature: Consolidated Langgraph
  Combined scenarios from: langgraph_bridge_coverage, langgraph_dynamic_router_coverage, langgraph_graph_coverage, langgraph_nodes_additional_coverage, langgraph_nodes_coverage, langgraph_nodes_uncovered_lines, langgraph_state_additional_coverage, langgraph_state_branch_coverage, pure_graph_coverage, agent_langgraph_port

  # ============================================================
  # Originally from: langgraph_bridge_coverage.feature
  # Feature: LangGraph bridge coverage
  # ============================================================

  Scenario: Cover bridge lifecycle and operators
    Given I set up a langgraph bridge test harness
    When I create a bridge with mocked graph configuration
    And I exercise bridge graph and stream creation
    And I exercise graph executor branches
    And I exercise state utilities and node operator
    And I exercise conditional routing helpers
    And I exercise router node helpers and cleanup
    Then bridge coverage steps should complete successfully


  # ============================================================
  # Originally from: langgraph_dynamic_router_coverage.feature
  # Feature: Dynamic Router Coverage
  # ============================================================

  Scenario: Default route when no rules are provided
    Given a dynamic router with no rules
    When I route the message
      """
      {"text": "hello"}
      """
    Then the router should return the default end edge


  Scenario: Route to target when no condition is required
    Given a dynamic router with a rule targeting "next" and no condition
    When I route the message
      """
      {"text": "any"}
      """
    Then the router should return a single edge to "next"


  Scenario: Route to target when condition matches
    Given a dynamic router with a rule targeting "match_target" when message type is "match"
    When I route the message
      """
      {"type": "match", "text": "payload"}
      """
    Then the router should return a single edge to "match_target"


  Scenario: Fall back to end when condition fails
    Given a dynamic router with a rule targeting "conditional" when message type is "expected"
    When I route the message
      """
      {"type": "other", "text": "payload"}
      """
    Then the router should return the default end edge


  # ============================================================
  # Originally from: langgraph_graph_coverage.feature
  # Feature: LangGraph uncovered branches
  # ============================================================

  Scenario: Scheduler falls back when no running loop exists
    Given a langgraph config with only start and end nodes
    And asyncio has no running loop
    When I construct a LangGraph without providing a scheduler
    Then it should create an AsyncIOScheduler and initialize streams


  Scenario: Execute dispatches to start stream and returns state
    Given a langgraph instance with a fresh state manager
    When I execute the graph with input data
    Then the start stream should receive the state and execution returns a GraphState


  Scenario: Start raises when start stream is missing
    Given a langgraph instance with a removed start stream
    When I call start on the graph
    Then a start stream error should be raised


  Scenario: Parallel groups disabled and cycles detected
    Given a langgraph config with parallel execution disabled and a cycle
    When I construct the LangGraph
    Then the graph should report cycles and empty parallel groups


  Scenario: Node executor updates state and history
    Given a langgraph instance with a patched state manager
    When I invoke the builtin node executor
    Then it should update state and record execution history


  Scenario: Topological levels enumerate layered nodes
    Given a langgraph config with layered nodes
    When I compute the topological levels
    Then the levels should list each layer


  Scenario: Execution history getter returns copy
    Given a langgraph instance with recorded history
    When I request execution history
    Then it should return a copy of the recorded history


  Scenario: Initialization respects explicit guard nodes
    Given a langgraph config with explicit start and end nodes
    When I construct the LangGraph with that config
    Then initialization should reuse the provided start and end nodes


  Scenario: Node subscriptions invoke handlers on stream events
    Given a langgraph instance prepared to observe node streams
    When the node stream emits next error and completion events
    Then the subscription handlers should be exercised


  Scenario: Node stream messages are processed by the node executor
    Given a langgraph instance with a mocked node executor
    When a message is emitted on the worker node stream
    Then the registered executor should be called with the message


  Scenario: Raw stream values are wrapped before node processing
    Given a langgraph instance with a mocked node executor
    When a raw value is emitted on the worker node stream
    Then the executor should receive a StreamMessage wrapping the raw value


  Scenario: Missing executor triggers a diagnostic warning
    Given a langgraph instance with a removed node executor
    When a message is emitted on the executorless worker stream
    Then a warning about missing executor should be logged


  Scenario: Each node stream dispatches to its own executor
    Given a langgraph instance with multiple mocked node executors
    When messages are emitted on both alpha and beta node streams
    Then each executor should receive its own message independently


  Scenario: Node stream on_next handles executor exceptions gracefully
    Given a langgraph instance with a failing node executor
    When a message is emitted on the failing worker stream
    Then the exception should be caught and logged without propagating


  Scenario: Subscription is skipped when observable is missing
    Given a langgraph instance with a removed observable
    When node stream subscriptions are set up
    Then no executor should be called and no error should occur


  Scenario: Execute raises when start stream is missing
    Given a langgraph instance with a removed start stream for execute
    When I execute the graph expecting a start stream error
    Then a start stream missing error should be raised from execute


  Scenario: Parallel groups computed for converging branches
    Given a langgraph config with converging branches
    When I construct the LangGraph for parallel groups
    Then parallel groups should reflect staged execution order


  Scenario: Validation fails when guard nodes missing
    Given a langgraph instance missing required guard nodes
    When I validate the graph explicitly
    Then a guard validation error should be raised


  Scenario: Start uses default state and stop resets running flag
    Given a langgraph instance ready to start
    When I start without input then stop and fetch state
    Then it should use default state and update running flags


  Scenario: Executor completes via run_coroutine_threadsafe on running loop
    Given a langgraph instance with a scheduler whose loop is running in a background thread
    When a message is emitted on the worker stream via the running loop path
    Then the executor should complete successfully via run_coroutine_threadsafe


  Scenario: Execution history is capped at maximum size
    Given a langgraph instance with execution history at capacity
    When one more executor invocation is recorded
    Then the history length should remain at the maximum and the oldest entry should be dropped


  Scenario: Concurrent state updates are serialized by the lock
    Given a state manager configured for concurrent access
    When multiple threads perform state updates simultaneously
    Then the final execution count should equal the total number of updates


  Scenario: Executor times out via run_coroutine_threadsafe path
    Given a langgraph instance with a slow executor on a background loop and a short timeout
    When the slow executor is invoked via the running loop path
    Then a TimeoutError should be raised with the node name and timeout duration


  Scenario: Executor times out via thread pool path
    Given a langgraph instance with a slow executor and a short timeout
    When the slow executor is invoked via the thread pool path
    Then a TimeoutError should be raised from the thread pool path with the node name


  Scenario: Graph can be restarted after stop
    Given a langgraph instance that has been started and stopped
    When I restart the graph and invoke an executor
    Then the executor should complete successfully after restart


  Scenario: Executor raises when graph is not running
    Given a langgraph instance that is not running
    When I invoke the node executor directly
    Then a RuntimeError should be raised indicating the graph is not running


  Scenario: StateManager replace_state atomically replaces and emits deep copy
    Given a state manager with an initial state
    When I call replace_state with a new state
    Then get_state should return the new state
    And the state stream should have received a notification
    And the emitted state should be a deep copy not the same object
    And the emitted state should be distinct from the internal state


  Scenario: CancelledError in thread pool path raises descriptive RuntimeError
    Given a langgraph instance with a cancellable executor task
    When the executor future is cancelled before completion
    Then a RuntimeError should be raised indicating graph stopping


  Scenario: LangGraph __del__ shuts down executor pool without error
    Given a langgraph instance that was never stopped
    When __del__ is called on the graph
    Then no exception should propagate from __del__


  Scenario: Execute raises when graph is not running via execute method
    Given a langgraph instance that is not running for execute
    When I call execute on the non-running graph
    Then a RuntimeError should be raised indicating the graph is not running for execute


  Scenario: TimeoutError through stream path logs warning not exception
    Given a langgraph instance with an executor that raises TimeoutError
    When a message is emitted on the timeout worker stream
    Then the timeout should be logged at warning level
    And logger exception should not be called for timeout


  Scenario: CancelledError in run_coroutine_threadsafe path raises descriptive RuntimeError
    Given a langgraph instance with a scheduler loop and a cancellable coroutine future
    When the coroutine future is cancelled before completion
    Then a RuntimeError should be raised indicating graph stopping from coroutine path


  # ============================================================
  # Originally from: langgraph_nodes_additional_coverage.feature
  # Feature: Additional LangGraph nodes coverage
  # ============================================================

  Scenario: Conversation history handles empty messages
    Given a node with default history limits
    When I prepare the conversation history for empty input
    Then it should return empty history without truncation


  Scenario: Conversation history falls back on invalid limits
    Given a node with invalid history metadata and two messages
    When I prepare the conversation history with invalid limits
    Then it should return full history without truncation


  Scenario: Agent node without configured agent returns failure metadata
    Given an agent node without an agent configured
    When I execute the node expecting an agent configuration error
    Then the result should include failed_node and error text



  Scenario: Agent receives empty input when no messages exist
    Given an agent node with no prior messages
    When I execute the agent node with empty history
    Then the agent should receive empty input and track node metadata


  Scenario: Agent merges nested metadata context and flags truncation
    Given an agent node with nested metadata context and long history
    When I execute the agent node with nested context
    Then the agent context should merge nested values and mark truncation


  Scenario: Agent error path captures processing failures
    Given an agent node whose handler raises an exception
    When I execute the failing agent node
    Then the agent response should contain the error string


  Scenario: Function node retries and reports missing function
    Given a function node missing a target with retry policy
    When I execute the function node with retries
    Then it should report failure after retries


  # ============================================================
  # Originally from: langgraph_nodes_coverage.feature
  # Feature: LangGraph nodes coverage
  # ============================================================

  Scenario: Conversation history truncates by message count
    Given a node with history limit 2 messages
    And a conversation history of 3 short messages
    When I prepare the conversation history
    Then it should return 2 messages and indicate truncation


  Scenario: Agent node falls back when agent instance is missing
    Given an agent node referencing a missing agent
    When I execute the agent node
    Then it should return a not found assistant message


  Scenario: Tool agent uses current_message metadata for input
    Given a tool agent node with a current_message "override" and long history
    When I execute the tool agent node
    Then the tool agent should receive input "override"
    And the response metadata should include last_agent_node


  Scenario: Function node runs async function
    Given a function node with an async function
    When I execute the function node
    Then the function result message should be "async-result"


  Scenario: Function node awaits coroutine returned from sync function
    Given a function node with a sync function that returns a coroutine
    When I execute the function node
    Then the function result message should be "sync-inner"


  Scenario: Conditional node detects assistant field match
    Given a conditional node looking for field "status" equals "OK"
    When I execute the conditional node with matching assistant message
    Then the condition_result should be true and metadata flagged


  Scenario: Message router selects rule based on field condition
    Given a message router node with a rule matching field "topic" value "urgent"
    When I execute the message router with current_message containing topic "urgent"
    Then it should route to "urgent_node"


  Scenario: Edge condition checks equals against latest message
    Given an edge with equals condition on field "content" value "done"
    When I evaluate the edge condition with matching last message
    Then the edge condition should be true
    And evaluating with no messages should also default to true


  Scenario: Eval condition supports equals and field comparisons
    Given I evaluate equals condition on message "abc"
    Then the equals evaluation should return true
    When I evaluate field condition on message dict with foo value "bar"
    Then the field evaluation should return true
    When I evaluate field condition on non-dict message
    Then it should return false


  Scenario: Node parallel execution flag reflects configuration
    Given a node configured for parallel execution
    Then can_execute_parallel should return true


  # ============================================================
  # Originally from: langgraph_nodes_uncovered_lines.feature
  # Feature: LangGraph nodes uncovered lines
  # ============================================================

  Scenario: Tool node marks execution
    Given a tool node configured
    When I execute the tool node
    Then it should mark tool executed


  Scenario: ToolAgent branch uses current_message for input
    Given a tool agent node using ToolAgent with current_message "override"
    When I execute the tool agent node with ToolAgent branch
    Then the ToolAgent should receive input "override"


  Scenario: Agent falls back to last assistant message when no user entries exist
    Given an agent node with only assistant history
    When I execute the agent node for assistant fallback
    Then the agent should receive the assistant fallback content


  Scenario: Agent clearing context skips metadata merge
    Given an agent that clears context during processing
    When I execute the clearing agent node
    Then only last_agent_node metadata should remain


  Scenario: Agent mutation adds metadata updates
    Given an agent that mutates context to add metadata
    When I execute the mutating agent node
    Then the metadata should include the mutated key


  Scenario: Subgraph node reports invocation
    Given a subgraph node named "child_subgraph"
    When I execute the subgraph node
    Then it should report the invoked subgraph "child_subgraph"


  Scenario: Start node indicates start
    Given a start node
    When I execute the start node
    Then it should indicate graph start


  Scenario: End node indicates completion
    Given an end node
    When I execute the end node
    Then it should indicate graph completion


  Scenario: Non-dict tool result leaves base state updates
    Given a tool node patched to return non dict
    When I execute the patched tool node
    Then the execute result should only include current_node


  # ============================================================
  # Originally from: langgraph_state_additional_coverage.feature
  # Feature: LangGraph state additional coverage
  # ============================================================

  Scenario: Merge mode trims messages beyond retention limit
    Given the state management system is available
    Given I have a GraphState starting with 49 messages
    And I have merge updates containing 5 new messages
    When I merge the updates into the state
    Then the message list should be trimmed to 50 messages
    And the newest message should be retained after trimming


  Scenario: Append mode trims messages beyond retention limit
    Given the state management system is available
    Given the state management system is available
    Given I have a GraphState starting with 50 messages
    And I have append updates containing 5 new messages
    When I append the updates into the state
    Then the message list should be trimmed to 50 messages after append
    And the newest appended message should be retained after trimming



  Scenario: Time travel history is recorded and trimmed
    Given the state management system is available
    Given I have a StateManager with time travel enabled and history size 2
    When I perform 4 sequential state updates with time travel
    Then only the two most recent history snapshots should remain
    And the earliest snapshots should be discarded


  Scenario: Automatic checkpoint is saved at interval
    Given the state management system is available
    Given I have a StateManager with checkpointing enabled and interval 1
    When I perform 2 sequential state updates with checkpointing
    Then a checkpoint file should exist in the directory
    And checkpoint saving should have been triggered


  Scenario: Time travel clamps when steps exceed history
    Given the state management system is available
    Given I have a StateManager with time travel history of 2 snapshots
    When I request time travel 5 steps back
    Then time travel should return the earliest available snapshot
    And the returned state should reflect the earliest snapshot


  # ============================================================
  # Originally from: langgraph_state_branch_coverage.feature
  # Feature: LangGraph state branch coverage
  # ============================================================

  Scenario: Replace mode overwrites existing fields
    Given the state management system is available
    Given I have a GraphState with metadata {"original": 1} and current node "old"
    When I replace the state with updates containing metadata {"new": 2} and current node "new"
    Then the state's metadata should equal {"new": 2}
    And the state's current node should equal "new"


  Scenario: Merge mode merges metadata dictionaries
    Given the state management system is available
    Given I have a GraphState with metadata {"alpha": 1}
    And I have merge metadata updates {"beta": 2}
    When I merge metadata updates into the state
    Then the state's metadata should contain keys "alpha" and "beta"


  Scenario: Append mode appends a single message item
    Given the state management system is available
    Given I have a GraphState with 1 message
    When I append a single message with id 99 into the state
    Then the state's messages should end with id 99
    And the message list length should be 2


  Scenario: Save checkpoint without directory is a no-op
    Given the state management system is available
    Given I have a StateManager without checkpointing
    When I invoke checkpoint saving manually
    Then the update count should remain at 0


  Scenario: Load checkpoint restores state and counters
    Given the state management system is available
    Given I have a checkpoint file with execution count 7 and metadata {"restored": true}
    When I load the checkpoint into a StateManager
    Then the manager state should include metadata {"restored": true}
    And the manager update count should be 7


  Scenario: Latest checkpoint returns None when missing
    Given the state management system is available
    Given I have a StateManager without checkpointing
    When I request the latest checkpoint
    Then no checkpoint path should be returned


  Scenario: Latest checkpoint selects the most recent file
    Given the state management system is available
    Given I have a StateManager with two checkpoints written at different times
    When I request the latest checkpoint
    Then the newest checkpoint path should be returned


  Scenario: Time travel without history returns None
    Given the state management system is available
    Given I have a StateManager with time travel disabled
    When I request time travel
    Then the returned time travel state should be None


  Scenario: Clear history and reset restore initial state
    Given the state management system is available
    Given I have a StateManager with time travel history entries
    When I clear the history and then reset with metadata {"reset": true}
    Then the history should be empty after reset
    And the execution count should be zero after reset
    And the state metadata should include {"reset": true}


  # ============================================================
  # Originally from: pure_graph_coverage.feature
  # Feature: PureGraph coverage
  # ============================================================

  Scenario: Topological order keeps start and end boundaries
    Given a pure graph with nodes "alpha" and "beta"
    When I list its topological order
    Then the order should be start alpha beta end


  Scenario: Execute applies registered node functions sequentially
    Given a pure graph with function nodes "double" and "increment"
    And registered functions that double then increment
    When I execute the pure graph starting with 1
    Then the final result should be 3
    And the functions should run in declaration order


  Scenario: Execute skips unregistered functions
    Given a pure graph with a missing function node
    When I execute the pure graph starting with 5
    Then the result should remain 5


  Scenario: Nodes without functions do not change value
    Given a pure graph with non-functional nodes "alpha" and "beta"
    When I execute the pure graph starting with 7
    Then the result should remain 7


  # ============================================================
  # Originally from: agent_langgraph_port.feature
  # Feature: Actor-first port of v2 agent and LangGraph suites
  # ============================================================
