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: 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


  # ============================================================
  # 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
  # ============================================================
