Feature: Invariant CLI commands coverage
  As a developer
  I want full test coverage for the invariant CLI commands module
  So that all commands and helper functions are verified

  # === _resolve_scope helper ===

  Scenario: Resolve scope with --global flag returns GLOBAL scope
    When I resolve invariant scope with global flag set
    Then the resolved invariant scope should be "global"
    And the resolved invariant source name should be "system"

  Scenario: Resolve scope with --project flag returns PROJECT scope
    When I resolve invariant scope with project "myapp"
    Then the resolved invariant scope should be "project"
    And the resolved invariant source name should be "myapp"

  Scenario: Resolve scope with --plan flag returns PLAN scope
    When I resolve invariant scope with plan "PLAN_01HX"
    Then the resolved invariant scope should be "plan"
    And the resolved invariant source name should be "PLAN_01HX"

  Scenario: Resolve scope with --action flag returns ACTION scope
    When I resolve invariant scope with action "deploy-service"
    Then the resolved invariant scope should be "action"
    And the resolved invariant source name should be "deploy-service"

  Scenario: Resolve scope with no flags defaults to GLOBAL
    When I resolve invariant scope with no flags
    Then the resolved invariant scope should be "global"
    And the resolved invariant source name should be "system"

  Scenario: Resolve scope with conflicting flags raises BadParameter
    When I resolve invariant scope with global and project flags
    Then a BadParameter error should be raised for invariant scope

  # === _invariant_dict helper ===

  Scenario: Invariant dict serializes an invariant correctly
    Given a sample invariant with known fields for dict test
    When I call invariant_dict on the sample invariant
    Then the invariant dict should contain the correct id and text
    And the invariant dict should contain scope and source_name
    And the invariant dict should contain active and created_at ISO string

  # === _get_service singleton ===

  Scenario: Get service creates InvariantService lazily
    When I call get_service with invariant module service reset to None
    Then an InvariantService instance should be returned from get_service
    And calling get_service again returns the same InvariantService instance

  # === invariant add command ===

  Scenario: Add invariant with --global flag via CLI
    Given a mocked InvariantService for invariant CLI add
    When I invoke invariant add with "--global" and text "Never delete prod data"
    Then the invariant CLI exit code should be 0
    And the invariant CLI output should contain "Invariant added"
    And the invariant CLI output should contain "Never delete prod data"

  Scenario: Add invariant with --project flag via CLI
    Given a mocked InvariantService for invariant CLI add
    When I invoke invariant add with "--project myapp" and text "All APIs need tests"
    Then the invariant CLI exit code should be 0
    And the invariant CLI output should contain "Invariant added"

  Scenario: Add invariant with --format json via CLI
    Given a mocked InvariantService for invariant CLI add
    When I invoke invariant add with "--global --format json" and text "JSON constraint"
    Then the invariant CLI exit code should be 0
    And the invariant CLI output should contain "JSON constraint"

  Scenario: Add invariant handles CleverAgentsError
    Given a mocked InvariantService that raises CleverAgentsError on add
    When I invoke invariant add with "--global" and text "will fail"
    Then the invariant CLI exit code should be non-zero
    And the invariant CLI output should contain "Error"

  # === invariant list command ===

  Scenario: List invariants with no results
    Given a mocked InvariantService that returns empty invariant list
    When I invoke invariant list with no filters
    Then the invariant CLI exit code should be 0
    And the invariant CLI output should contain "No invariants found"

  Scenario: List invariants with results in rich format
    Given a mocked InvariantService that returns two invariants for list
    When I invoke invariant list with no filters
    Then the invariant CLI exit code should be 0
    And the invariant CLI output should contain "Invariants"
    And the invariant CLI output should contain "Never delete prod"

  Scenario: List invariants with --global filter
    Given a mocked InvariantService that returns two invariants for list
    When I invoke invariant list with flags "--global"
    Then the invariant CLI exit code should be 0
    And the invariant service list was called with global scope

  Scenario: List invariants with --project filter
    Given a mocked InvariantService that returns two invariants for list
    When I invoke invariant list with flags "--project myapp"
    Then the invariant CLI exit code should be 0
    And the invariant service list was called with project scope and source "myapp"

  Scenario: List invariants with --plan filter
    Given a mocked InvariantService that returns two invariants for list
    When I invoke invariant list with flags "--plan PLAN_01HX"
    Then the invariant CLI exit code should be 0
    And the invariant service list was called with plan scope and source "PLAN_01HX"

  Scenario: List invariants with --action filter
    Given a mocked InvariantService that returns two invariants for list
    When I invoke invariant list with flags "--action deploy"
    Then the invariant CLI exit code should be 0
    And the invariant service list was called with action scope and source "deploy"

  Scenario: List invariants with --effective flag
    Given a mocked InvariantService that returns two invariants for list
    When I invoke invariant list with flags "--effective"
    Then the invariant CLI exit code should be 0
    And the invariant service list was called with effective true

  Scenario: List invariants with regex filter matching
    Given a mocked InvariantService that returns two invariants for list
    When I invoke invariant list with regex "prod"
    Then the invariant CLI exit code should be 0
    And the invariant CLI output should contain "Never delete prod"

  Scenario: List invariants with invalid regex
    Given a mocked InvariantService that returns two invariants for list
    When I invoke invariant list with regex "[invalid"
    Then the invariant CLI exit code should be non-zero
    And the invariant CLI output should contain "Invalid regex"

  Scenario: List invariants with --format json
    Given a mocked InvariantService that returns two invariants for list
    When I invoke invariant list with flags "--format json"
    Then the invariant CLI exit code should be 0
    And the invariant CLI output should contain "Never delete prod"

  # === invariant remove command ===

  Scenario: Remove invariant with --yes flag
    Given a mocked InvariantService for invariant CLI remove
    When I invoke invariant remove with "--yes" for id "INV_01HX"
    Then the invariant CLI exit code should be 0
    And the invariant CLI output should contain "Invariant removed"

  Scenario: Remove invariant without --yes cancelled by user
    Given a mocked InvariantService for invariant CLI remove
    When I invoke invariant remove without --yes for id "INV_01HX" and answer no
    Then the invariant CLI exit code should be non-zero
    And the invariant CLI output should contain "Cancelled"

  Scenario: Remove invariant raises NotFoundError
    Given a mocked InvariantService that raises NotFoundError on remove
    When I invoke invariant remove with "--yes" for id "MISSING_ID"
    Then the invariant CLI exit code should be non-zero
    And the invariant CLI output should contain "not found"
