import type { RuleSet } from '../../RuleSet';

export const iosRules: RuleSet = [
  {
    id: 'heuristics.ios.force-unwrap.ast',
    description: 'Detects Swift force unwrap usage in production code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.force-unwrap.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected force unwrap usage.',
      code: 'HEURISTICS_IOS_FORCE_UNWRAP_AST',
    },
  },
  {
    id: 'heuristics.ios.anyview.ast',
    description: 'Detects Swift AnyView type-erasure usage in production code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.anyview.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected AnyView usage.',
      code: 'HEURISTICS_IOS_ANYVIEW_AST',
    },
  },
  {
    id: 'heuristics.ios.type-erasure.any.ast',
    description: 'Detects Swift Any/AnyObject/AnyHashable type erasure in production code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.type-erasure.any.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected Swift Any/AnyObject/AnyHashable type erasure in production code.',
      code: 'HEURISTICS_IOS_TYPE_ERASURE_ANY_AST',
    },
  },
  {
    id: 'heuristics.ios.force-try.ast',
    description: 'Detects Swift force try usage in production code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.force-try.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected force try usage.',
      code: 'HEURISTICS_IOS_FORCE_TRY_AST',
    },
  },
  {
    id: 'heuristics.ios.force-cast.ast',
    description: 'Detects Swift force cast usage in production code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.force-cast.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected force cast usage.',
      code: 'HEURISTICS_IOS_FORCE_CAST_AST',
    },
  },
  {
    id: 'heuristics.ios.callback-style.ast',
    description: 'Detects callback-style signatures outside approved iOS bridge layers.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.callback-style.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected callback-style API signature outside bridge layers.',
      code: 'HEURISTICS_IOS_CALLBACK_STYLE_AST',
    },
  },
  {
    id: 'heuristics.ios.combine.sink-without-store.ast',
    description: 'Detects Combine sink/assign subscriptions that are not retained with store(in:).',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.combine.sink-without-store.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected Combine sink/assign without store(in:); keep cancellables retained explicitly.',
      code: 'HEURISTICS_IOS_COMBINE_SINK_WITHOUT_STORE_AST',
    },
  },
  {
    id: 'heuristics.ios.dispatchqueue.ast',
    description: 'Detects DispatchQueue usage in iOS production code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.dispatchqueue.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected DispatchQueue usage.',
      code: 'HEURISTICS_IOS_DISPATCHQUEUE_AST',
    },
  },
  {
    id: 'heuristics.ios.dispatchgroup.ast',
    description: 'Detects DispatchGroup usage in iOS production code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.dispatchgroup.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected DispatchGroup usage.',
      code: 'HEURISTICS_IOS_DISPATCHGROUP_AST',
    },
  },
  {
    id: 'heuristics.ios.dispatchsemaphore.ast',
    description: 'Detects DispatchSemaphore usage in iOS production code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.dispatchsemaphore.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected DispatchSemaphore usage.',
      code: 'HEURISTICS_IOS_DISPATCHSEMAPHORE_AST',
    },
  },
  {
    id: 'heuristics.ios.operation-queue.ast',
    description: 'Detects OperationQueue usage in iOS production code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.operation-queue.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected OperationQueue usage.',
      code: 'HEURISTICS_IOS_OPERATION_QUEUE_AST',
    },
  },
  {
    id: 'heuristics.ios.task-detached.ast',
    description: 'Detects Task.detached usage in iOS production code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.task-detached.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected Task.detached usage.',
      code: 'HEURISTICS_IOS_TASK_DETACHED_AST',
    },
  },
  {
    id: 'heuristics.ios.concurrency.long-task-without-cancellation-check.ast',
    description: 'Detects long-running async Swift loops without Task cancellation checks.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.concurrency.long-task-without-cancellation-check.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected a long async operation without cooperative Task cancellation checks.',
      code: 'HEURISTICS_IOS_CONCURRENCY_LONG_TASK_WITHOUT_CANCELLATION_CHECK_AST',
    },
  },
  {
    id: 'heuristics.ios.concurrency.async-without-await.ast',
    description: 'Detects private async functions that do not await in iOS production code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.concurrency.async-without-await.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected a private async function without await; remove async unless a protocol/override boundary requires it.',
      code: 'HEURISTICS_IOS_CONCURRENCY_ASYNC_WITHOUT_AWAIT_AST',
    },
  },
  {
    id: 'heuristics.ios.concurrency.dummy-await.ast',
    description: 'Detects dummy awaits inserted to silence async_without_await instead of fixing the async boundary.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.concurrency.dummy-await.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected a dummy await used as a Swift concurrency lint workaround.',
      code: 'HEURISTICS_IOS_CONCURRENCY_DUMMY_AWAIT_AST',
    },
  },
  {
    id: 'heuristics.ios.error.empty-catch.ast',
    description: 'Detects Swift catch blocks that silently swallow errors.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.error.empty-catch.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected an empty Swift catch block.',
      code: 'HEURISTICS_IOS_ERROR_EMPTY_CATCH_AST',
    },
  },
  {
    id: 'heuristics.ios.error.nserror-throw.ast',
    description: 'Detects direct NSError throws where typed Swift Error enums should be used.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.error.nserror-throw.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected throw NSError(...) in iOS production code.',
      code: 'HEURISTICS_IOS_ERROR_NSERROR_THROW_AST',
    },
  },
  {
    id: 'heuristics.ios.networking.endpoint-enum-ocp.ast',
    description: 'Detects API endpoint catalogs modeled as Swift enums instead of data-driven endpoint values.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.networking.endpoint-enum-ocp.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected API endpoint modeled as enum; use a data-driven APIEndpoint struct to preserve OCP.',
      code: 'HEURISTICS_IOS_NETWORKING_ENDPOINT_ENUM_OCP_AST',
    },
  },
  {
    id: 'heuristics.ios.swiftui.onappear-task.ast',
    description: 'Detects Task launches from SwiftUI onAppear where .task can provide lifecycle cancellation.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.swiftui.onappear-task.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected Task launched from SwiftUI onAppear; .task/.task(id:) provides lifecycle-aware cancellation.',
      code: 'HEURISTICS_IOS_SWIFTUI_ONAPPEAR_TASK_AST',
    },
  },
  {
    id: 'heuristics.ios.swiftui.onchange-task.ast',
    description:
      'Detects Task launches from SwiftUI onChange where .task(id:) should own value-dependent cancellation.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.swiftui.onchange-task.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected Task launched from SwiftUI onChange; .task(id:) should own value-dependent cancellation.',
      code: 'HEURISTICS_IOS_SWIFTUI_ONCHANGE_TASK_AST',
    },
  },
  {
    id: 'heuristics.ios.swiftui.onchange-readonly-var.ast',
    description: 'Detects local var declarations inside SwiftUI onChange closures where let should be used for read-only derived values.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.swiftui.onchange-readonly-var.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected local var inside SwiftUI onChange; prefer let for read-only derived values.',
      code: 'HEURISTICS_IOS_SWIFTUI_ONCHANGE_READONLY_VAR_AST',
    },
  },
  {
    id: 'heuristics.ios.memory.strong-delegate.ast',
    description: 'Detects strong delegate/dataSource references in iOS production code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.memory.strong-delegate.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected a strong delegate/dataSource reference; weak delegates remain the preferred baseline to avoid retain cycles.',
      code: 'HEURISTICS_IOS_MEMORY_STRONG_DELEGATE_AST',
    },
  },
  {
    id: 'heuristics.ios.architecture.cross-feature-import.ast',
    description: 'Detects Swift feature modules importing sibling feature modules directly.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.architecture.cross-feature-import.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected a Swift feature importing another feature module directly.',
      code: 'HEURISTICS_IOS_ARCHITECTURE_CROSS_FEATURE_IMPORT_AST',
    },
  },
  {
    id: 'heuristics.ios.architecture.layer-direction-violation.ast',
    description: 'Detects Swift imports that violate iOS Clean Architecture layer direction.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.architecture.layer-direction-violation.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected an import that violates Clean Architecture layer direction in iOS code.',
      code: 'HEURISTICS_IOS_ARCHITECTURE_LAYER_DIRECTION_VIOLATION_AST',
    },
  },
  {
    id: 'heuristics.ios.architecture.excessive-public-api.ast',
    description: 'Detects excessive public/open API surface in iOS app implementation files.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.architecture.excessive-public-api.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected excessive public/open Swift API surface in iOS app code.',
      code: 'HEURISTICS_IOS_ARCHITECTURE_EXCESSIVE_PUBLIC_API_AST',
    },
  },
  {
    id: 'heuristics.ios.memory.strong-self-escaping-closure.ast',
    description: 'Detects strong self captures in escaping iOS closures.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.memory.strong-self-escaping-closure.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected strong self capture in an escaping iOS closure; weak or unowned captures remain the preferred baseline when ownership is not explicit.',
      code: 'HEURISTICS_IOS_MEMORY_STRONG_SELF_ESCAPING_CLOSURE_AST',
    },
  },
  {
    id: 'heuristics.ios.memory.unowned-self-capture.ast',
    description: 'Detects unowned captures in iOS closures.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.memory.unowned-self-capture.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected unowned capture in an iOS closure; use weak capture unless lifetime is explicitly guaranteed.',
      code: 'HEURISTICS_IOS_MEMORY_UNOWNED_SELF_CAPTURE_AST',
    },
  },
  {
    id: 'heuristics.ios.memory.manual-management.ast',
    description: 'Detects manual memory management that bypasses Swift ARC.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.memory.manual-management.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected manual memory management that bypasses Swift ARC.',
      code: 'HEURISTICS_IOS_MEMORY_MANUAL_MANAGEMENT_AST',
    },
  },
  {
    id: 'heuristics.ios.testing.makesut-without-memory-tracking.ast',
    description: 'Detects iOS test makeSUT helpers that do not register SUTs for memory-leak tracking.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.testing.makesut-without-memory-tracking.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected makeSUT() in an iOS test without trackForMemoryLeaks(sut).',
      code: 'HEURISTICS_IOS_TESTING_MAKESUT_WITHOUT_MEMORY_TRACKING_AST',
    },
  },
  {
    id: 'heuristics.ios.testing.direct-sut-instantiation-without-makesut.ast',
    description: 'Detects direct SUT construction in iOS tests without a makeSUT factory.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.testing.direct-sut-instantiation-without-makesut.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected direct SUT instantiation in an iOS test without makeSUT().',
      code: 'HEURISTICS_IOS_TESTING_DIRECT_SUT_INSTANTIATION_WITHOUT_MAKESUT_AST',
    },
  },
  {
    id: 'heuristics.ios.maintainability.nested-if-pyramid.ast',
    description: 'Detects deeply nested if pyramids in iOS production code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.maintainability.nested-if-pyramid.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected nested if pyramid in iOS code; prefer guard clauses and early returns.',
      code: 'HEURISTICS_IOS_MAINTAINABILITY_NESTED_IF_PYRAMID_AST',
    },
  },
  {
    id: 'heuristics.ios.maintainability.comment-trivia.ast',
    description: 'Detects source comments in iOS production Swift code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.maintainability.comment-trivia.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected source comments in iOS production code; prefer self-documenting names and extracted concepts.',
      code: 'HEURISTICS_IOS_MAINTAINABILITY_COMMENT_TRIVIA_AST',
    },
  },
  {
    id: 'heuristics.ios.maintainability.warning-suppression.ast',
    description: 'Detects local warning or lint suppression directives in iOS production Swift code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.maintainability.warning-suppression.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected local warning/lint suppression in iOS production code; fix the underlying issue instead of hiding diagnostics.',
      code: 'HEURISTICS_IOS_MAINTAINABILITY_WARNING_SUPPRESSION_AST',
    },
  },
  {
    id: 'heuristics.ios.architecture.custom-singleton.ast',
    description: 'Detects custom static shared singletons in iOS production code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.architecture.custom-singleton.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected a custom static shared singleton in iOS production code; dependency injection remains the preferred baseline for app-owned services.',
      code: 'HEURISTICS_IOS_ARCHITECTURE_CUSTOM_SINGLETON_AST',
    },
  },
  {
    id: 'heuristics.ios.architecture.swinject.ast',
    description: 'Detects Swinject usage in iOS production code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.architecture.swinject.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected Swinject usage; manual dependency injection or SwiftUI Environment remain the preferred native baseline.',
      code: 'HEURISTICS_IOS_ARCHITECTURE_SWINJECT_AST',
    },
  },
  {
    id: 'heuristics.ios.architecture.tca-composable-architecture.ast',
    description: 'Detects The Composable Architecture usage in iOS production code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.architecture.tca-composable-architecture.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected The Composable Architecture usage; native SwiftUI state, use cases, actors and explicit environment boundaries remain the preferred baseline.',
      code: 'HEURISTICS_IOS_ARCHITECTURE_TCA_COMPOSABLE_ARCHITECTURE_AST',
    },
  },
  {
    id: 'heuristics.ios.architecture.massive-view-controller.ast',
    description: 'Detects UIViewController classes with direct infrastructure/data access.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.architecture.massive-view-controller.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected a UIViewController with direct infrastructure/data access; move data access behind application/domain boundaries.',
      code: 'HEURISTICS_IOS_ARCHITECTURE_MASSIVE_VIEW_CONTROLLER_AST',
    },
  },
  {
    id: 'heuristics.ios.maintainability.magic-number-layout.ast',
    description: 'Detects SwiftUI layout magic numbers in presentation code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.maintainability.magic-number-layout.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected SwiftUI layout magic numbers; named constants or design tokens remain the preferred baseline.',
      code: 'HEURISTICS_IOS_MAINTAINABILITY_MAGIC_NUMBER_LAYOUT_AST',
    },
  },
  {
    id: 'heuristics.ios.uikit.manual-frame-layout.ast',
    description: 'Detects UIKit manual frame CGRect layout in iOS presentation code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.uikit.manual-frame-layout.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected UIKit manual frame layout; use Auto Layout constraints or SwiftUI relative layout.',
      code: 'HEURISTICS_IOS_UIKIT_MANUAL_FRAME_LAYOUT_AST',
    },
  },
  {
    id: 'heuristics.ios.safety.non-iboutlet-iuo.ast',
    description: 'Detects implicitly unwrapped optionals outside IBOutlet wiring.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.safety.non-iboutlet-iuo.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected an implicitly unwrapped optional outside IBOutlet wiring; explicit optionals or initialization guarantees remain the preferred baseline.',
      code: 'HEURISTICS_IOS_SAFETY_NON_IBOUTLET_IUO_AST',
    },
  },
  {
    id: 'heuristics.ios.logging.adhoc-print.ast',
    description: 'Detects print/debugPrint/dump/NSLog/os_log usage in iOS production code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.logging.adhoc-print.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected print/debugPrint/dump/NSLog/os_log usage in iOS production code.',
      code: 'HEURISTICS_IOS_LOGGING_ADHOC_PRINT_AST',
    },
  },
  {
    id: 'heuristics.ios.logging.sensitive-data.ast',
    description: 'Detects sensitive data in iOS logging calls.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.logging.sensitive-data.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected sensitive data in an iOS logging call.',
      code: 'HEURISTICS_IOS_LOGGING_SENSITIVE_DATA_AST',
    },
  },
  {
    id: 'heuristics.ios.security.hardcoded-sensitive-string.ast',
    description: 'Detects hardcoded sensitive Swift string values in iOS production code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.security.hardcoded-sensitive-string.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected hardcoded sensitive Swift string; Keychain, secure config or environment-specific secrets remain the preferred baseline.',
      code: 'HEURISTICS_IOS_SECURITY_HARDCODED_SENSITIVE_STRING_AST',
    },
  },
  {
    id: 'heuristics.ios.localization.unlocalized-dateformatter.ast',
    description: 'Detects DateFormatter fixed date formats without an explicit locale in iOS production code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.localization.unlocalized-dateformatter.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected DateFormatter dateFormat usage without an explicit locale; localized date formatting remains the preferred baseline.',
      code: 'HEURISTICS_IOS_LOCALIZATION_UNLOCALIZED_DATEFORMATTER_AST',
    },
  },
  {
    id: 'heuristics.ios.networking.alamofire.ast',
    description: 'Detects Alamofire usage in iOS production code; URLSession is the preferred baseline for new code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.networking.alamofire.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected Alamofire usage in iOS production code; URLSession remains the preferred baseline for new code.',
      code: 'HEURISTICS_IOS_NETWORKING_ALAMOFIRE_AST',
    },
  },
  {
    id: 'heuristics.ios.json.jsonserialization.ast',
    description: 'Detects JSONSerialization usage in iOS production code; Codable is the preferred baseline for new code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.json.jsonserialization.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected JSONSerialization usage in iOS production code; Codable remains the preferred baseline for new code.',
      code: 'HEURISTICS_IOS_JSON_JSONSERIALIZATION_AST',
    },
  },
  {
    id: 'heuristics.ios.dependencies.cocoapods.ast',
    description: 'Detects CocoaPods dependency files in iOS projects; Swift Package Manager is the preferred baseline for new code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.dependencies.cocoapods.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected CocoaPods dependency files in an iOS project; Swift Package Manager remains the preferred baseline for new code.',
      code: 'HEURISTICS_IOS_DEPENDENCIES_COCOAPODS_AST',
    },
  },
  {
    id: 'heuristics.ios.dependencies.carthage.ast',
    description: 'Detects Carthage dependency files in iOS projects; Swift Package Manager is the preferred baseline for new code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.dependencies.carthage.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected Carthage dependency files in an iOS project; Swift Package Manager remains the preferred baseline for new code.',
      code: 'HEURISTICS_IOS_DEPENDENCIES_CARTHAGE_AST',
    },
  },
  {
    id: 'heuristics.ios.dependencies.swiftpm-branch-dependency.ast',
    description: 'Detects branch-based SwiftPM dependencies in iOS Package.swift.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.dependencies.swiftpm-branch-dependency.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected a branch-based SwiftPM dependency.',
      code: 'HEURISTICS_IOS_DEPENDENCIES_SWIFTPM_BRANCH_DEPENDENCY_AST',
    },
  },
  {
    id: 'heuristics.ios.dependencies.swift-tools-version-below-6-2.ast',
    description: 'Detects iOS Package.swift manifests below Swift tools 6.2.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.dependencies.swift-tools-version-below-6-2.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected Package.swift using swift-tools-version below 6.2.',
      code: 'HEURISTICS_IOS_DEPENDENCIES_SWIFT_TOOLS_VERSION_BELOW_6_2_AST',
    },
  },
  {
    id: 'heuristics.ios.concurrency.swiftpm-default-isolation-not-mainactor.ast',
    description: 'Detects SwiftPM iOS manifests whose default isolation is not MainActor.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.concurrency.swiftpm-default-isolation-not-mainactor.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected Package.swift .defaultIsolation not set to MainActor.',
      code: 'HEURISTICS_IOS_CONCURRENCY_SWIFTPM_DEFAULT_ISOLATION_NOT_MAINACTOR_AST',
    },
  },
  {
    id: 'heuristics.ios.concurrency.swiftpm-strict-concurrency-below-complete.ast',
    description: 'Detects SwiftPM iOS manifests using targeted/minimal StrictConcurrency.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.concurrency.swiftpm-strict-concurrency-below-complete.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected Package.swift StrictConcurrency below complete.',
      code: 'HEURISTICS_IOS_CONCURRENCY_SWIFTPM_STRICT_CONCURRENCY_BELOW_COMPLETE_AST',
    },
  },
  {
    id: 'heuristics.ios.concurrency.strict-concurrency-below-complete.ast',
    description: 'Detects Xcode iOS targets with strict concurrency checking below complete.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.concurrency.strict-concurrency-below-complete.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected SWIFT_STRICT_CONCURRENCY below complete in an iOS Xcode project.',
      code: 'HEURISTICS_IOS_CONCURRENCY_STRICT_CONCURRENCY_BELOW_COMPLETE_AST',
    },
  },
  {
    id: 'heuristics.ios.concurrency.default-actor-isolation-not-mainactor.ast',
    description: 'Detects Xcode iOS targets whose default actor isolation is not MainActor.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.concurrency.default-actor-isolation-not-mainactor.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected SWIFT_DEFAULT_ACTOR_ISOLATION not set to MainActor in an iOS Xcode project.',
      code: 'HEURISTICS_IOS_CONCURRENCY_DEFAULT_ACTOR_ISOLATION_NOT_MAINACTOR_AST',
    },
  },
  {
    id: 'heuristics.ios.concurrency.upcoming-feature-disabled.ast',
    description: 'Detects Xcode iOS targets with required Swift upcoming features disabled.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.concurrency.upcoming-feature-disabled.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected disabled Swift upcoming feature settings in an iOS Xcode project.',
      code: 'HEURISTICS_IOS_CONCURRENCY_UPCOMING_FEATURE_DISABLED_AST',
    },
  },
  {
    id: 'heuristics.ios.concurrency.ui-state-without-mainactor.ast',
    description: 'Detects observable iOS UI state owners without @MainActor isolation.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.concurrency.ui-state-without-mainactor.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected observable iOS UI state without @MainActor isolation.',
      code: 'HEURISTICS_IOS_CONCURRENCY_UI_STATE_WITHOUT_MAINACTOR_AST',
    },
  },
  {
    id: 'heuristics.ios.concurrency.shared-mutable-state-without-actor.ast',
    description: 'Detects shared mutable iOS state owners without actor isolation.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.concurrency.shared-mutable-state-without-actor.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected shared mutable iOS state without actor isolation.',
      code: 'HEURISTICS_IOS_CONCURRENCY_SHARED_MUTABLE_STATE_WITHOUT_ACTOR_AST',
    },
  },
  {
    id: 'heuristics.ios.concurrency.mainactor-run-patch.ast',
    description: 'Detects MainActor.run used as an isolation patch inside non-isolated iOS state owners.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.concurrency.mainactor-run-patch.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected MainActor.run used as an isolation patch in iOS production code.',
      code: 'HEURISTICS_IOS_CONCURRENCY_MAINACTOR_RUN_PATCH_AST',
    },
  },
  {
    id: 'heuristics.ios.naming.non-pascal-case-type.ast',
    description: 'Detects Swift type declarations that are not PascalCase.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.naming.non-pascal-case-type.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected Swift type declaration without PascalCase.',
      code: 'HEURISTICS_IOS_NAMING_NON_PASCAL_CASE_TYPE_AST',
    },
  },
  {
    id: 'heuristics.ios.uikit.cell-without-reuse.ast',
    description: 'Detects UITableView/UICollectionView cell providers that create cells without dequeue reuse.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.uikit.cell-without-reuse.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected UIKit cell provider creating cells without dequeueReusableCell.',
      code: 'HEURISTICS_IOS_UIKIT_CELL_WITHOUT_REUSE_AST',
    },
  },
  {
    id: 'heuristics.ios.security.userdefaults-sensitive-data.ast',
    description: 'Detects sensitive data stored in UserDefaults/AppStorage; Keychain is the preferred baseline for secrets.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.security.userdefaults-sensitive-data.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected sensitive data stored in UserDefaults/AppStorage; native Keychain remains the preferred baseline for secrets.',
      code: 'HEURISTICS_IOS_SECURITY_USERDEFAULTS_SENSITIVE_DATA_AST',
    },
  },
  {
    id: 'heuristics.ios.security.insecure-transport.ast',
    description: 'Detects insecure HTTP transport or permissive ATS configuration in iOS projects.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.security.insecure-transport.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected insecure iOS transport configuration; HTTPS and ATS remain the preferred baseline.',
      code: 'HEURISTICS_IOS_SECURITY_INSECURE_TRANSPORT_AST',
    },
  },
  {
    id: 'heuristics.ios.security.urlsession-trust-bypass.ast',
    description: 'Detects URLSessionDelegate trust challenge handlers that accept server trust without validation.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.security.urlsession-trust-bypass.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected URLSession trust challenge bypass in iOS production code.',
      code: 'HEURISTICS_IOS_SECURITY_URLSESSION_TRUST_BYPASS_AST',
    },
  },
  {
    id: 'heuristics.ios.localization.localizable-strings.ast',
    description: 'Detects legacy Localizable.strings files where String Catalogs are the preferred iOS baseline.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.localization.localizable-strings.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected Localizable.strings usage; String Catalogs (.xcstrings) remain the preferred baseline for new localization work.',
      code: 'HEURISTICS_IOS_LOCALIZATION_LOCALIZABLE_STRINGS_AST',
    },
  },
  {
    id: 'heuristics.ios.interface-builder.storyboard-xib.ast',
    description: 'Detects Storyboard/XIB usage where programmatic UI should be used.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.interface-builder.storyboard-xib.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected Storyboard/XIB usage; programmatic SwiftUI/UIKit UI remains the preferred baseline.',
      code: 'HEURISTICS_IOS_INTERFACE_BUILDER_STORYBOARD_XIB_AST',
    },
  },
  {
    id: 'heuristics.ios.localization.hardcoded-ui-string.ast',
    description: 'Detects hardcoded user-facing SwiftUI text where String(localized:) and String Catalogs are preferred.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.localization.hardcoded-ui-string.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected hardcoded user-facing SwiftUI text; String(localized:) and String Catalogs remain the preferred baseline.',
      code: 'HEURISTICS_IOS_LOCALIZATION_HARDCODED_UI_STRING_AST',
    },
  },
  {
    id: 'heuristics.ios.assets.loose-resource.ast',
    description: 'Detects loose image resource loading where Asset Catalogs are the preferred iOS baseline.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.assets.loose-resource.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected loose image resource loading in iOS production code; Asset Catalogs remain the preferred baseline.',
      code: 'HEURISTICS_IOS_ASSETS_LOOSE_RESOURCE_AST',
    },
  },
  {
    id: 'heuristics.ios.accessibility.fixed-font-size.ast',
    description: 'Detects fixed font sizes where Dynamic Type semantic styles are the preferred iOS baseline.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.accessibility.fixed-font-size.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected fixed font sizing in iOS production code; Dynamic Type semantic text styles remain the preferred baseline.',
      code: 'HEURISTICS_IOS_ACCESSIBILITY_FIXED_FONT_SIZE_AST',
    },
  },
  {
    id: 'heuristics.ios.localization.physical-text-alignment.ast',
    description: 'Detects physical left/right text alignment where leading/trailing are the preferred RTL-safe iOS baseline.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.localization.physical-text-alignment.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected physical left/right text alignment in iOS production code; leading/trailing remain the preferred RTL-safe baseline.',
      code: 'HEURISTICS_IOS_LOCALIZATION_PHYSICAL_TEXT_ALIGNMENT_AST',
    },
  },
  {
    id: 'heuristics.ios.performance.blocking-sleep.ast',
    description: 'Detects blocking sleep calls where cancellable async scheduling is the preferred iOS baseline.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.performance.blocking-sleep.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected blocking sleep usage in iOS production code; async clocks, suspension or cancellable scheduling remain the preferred baseline.',
      code: 'HEURISTICS_IOS_PERFORMANCE_BLOCKING_SLEEP_AST',
    },
  },
  {
    id: 'heuristics.ios.concurrency.thread-centric-debugging.ast',
    description: 'Detects Thread.current, Thread.isMainThread and pthread thread identity checks in iOS production code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.concurrency.thread-centric-debugging.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected thread-centric debugging in Swift Concurrency code.',
      code: 'HEURISTICS_IOS_CONCURRENCY_THREAD_CENTRIC_DEBUGGING_AST',
    },
  },
  {
    id: 'heuristics.ios.accessibility.icon-only-control-label.ast',
    description: 'Detects icon-only SwiftUI controls without explicit accessibility labels.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.accessibility.icon-only-control-label.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected an icon-only SwiftUI control without accessibilityLabel; explicit accessible labels remain the preferred baseline.',
      code: 'HEURISTICS_IOS_ACCESSIBILITY_ICON_ONLY_CONTROL_LABEL_AST',
    },
  },
  {
    id: 'heuristics.ios.accessibility.missing-accessibility-identifier.ast',
    description: 'Detects interactive SwiftUI controls without accessibilityIdentifier.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.accessibility.missing-accessibility-identifier.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected an interactive SwiftUI control without accessibilityIdentifier; stable identifiers are required for UI automation and traceability.',
      code: 'HEURISTICS_IOS_ACCESSIBILITY_MISSING_ACCESSIBILITY_IDENTIFIER_AST',
    },
  },
  {
    id: 'heuristics.ios.swiftui.missing-bindable-observable-binding.ast',
    description: 'Detects injected @Observable values used as bindings without @Bindable.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.swiftui.missing-bindable-observable-binding.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected an injected @Observable used as a binding without @Bindable.',
      code: 'HEURISTICS_IOS_SWIFTUI_MISSING_BINDABLE_OBSERVABLE_BINDING_AST',
    },
  },
  {
    id: 'heuristics.ios.unchecked-sendable.ast',
    description: 'Detects @unchecked Sendable usage in iOS production code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.unchecked-sendable.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected @unchecked Sendable usage.',
      code: 'HEURISTICS_IOS_UNCHECKED_SENDABLE_AST',
    },
  },
  {
    id: 'heuristics.ios.preconcurrency.ast',
    description: 'Detects @preconcurrency usage in iOS production code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.preconcurrency.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected @preconcurrency usage.',
      code: 'HEURISTICS_IOS_PRECONCURRENCY_AST',
    },
  },
  {
    id: 'heuristics.ios.nonisolated-unsafe.ast',
    description: 'Detects nonisolated(unsafe) usage in iOS production code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.nonisolated-unsafe.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected nonisolated(unsafe) usage.',
      code: 'HEURISTICS_IOS_NONISOLATED_UNSAFE_AST',
    },
  },
  {
    id: 'heuristics.ios.assume-isolated.ast',
    description: 'Detects assumeIsolated usage in iOS production code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.assume-isolated.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected assumeIsolated usage.',
      code: 'HEURISTICS_IOS_ASSUME_ISOLATED_AST',
    },
  },
  {
    id: 'heuristics.ios.observable-object.ast',
    description: 'Detects ObservableObject usage in modern iOS production code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.observable-object.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected ObservableObject usage.',
      code: 'HEURISTICS_IOS_OBSERVABLE_OBJECT_AST',
    },
  },
  {
    id: 'heuristics.ios.swiftui.legacy-preview-provider.ast',
    description: 'Detects PreviewProvider usage where modern SwiftUI #Preview macros should be used.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.swiftui.legacy-preview-provider.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected PreviewProvider usage; use #Preview macros for modern SwiftUI previews.',
      code: 'HEURISTICS_IOS_SWIFTUI_LEGACY_PREVIEW_PROVIDER_AST',
    },
  },
  {
    id: 'heuristics.ios.legacy-swiftui-observable-wrapper.ast',
    description: 'Detects @StateObject and @ObservedObject usage in modern SwiftUI production code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.legacy-swiftui-observable-wrapper.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected @StateObject/@ObservedObject usage in a modern SwiftUI path.',
      code: 'HEURISTICS_IOS_LEGACY_SWIFTUI_OBSERVABLE_WRAPPER_AST',
    },
  },
  {
    id: 'heuristics.ios.swiftui.environment-object.ast',
    description: 'Detects @EnvironmentObject usage in SwiftUI presentation code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.swiftui.environment-object.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected @EnvironmentObject in SwiftUI presentation code; prefer explicit dependencies or narrowly scoped Environment values.',
      code: 'HEURISTICS_IOS_SWIFTUI_ENVIRONMENT_OBJECT_AST',
    },
  },
  {
    id: 'heuristics.ios.testing.test-double-without-protocol.ast',
    description: 'Detects Swift Mock/Fake/Spy/Stub classes without protocol conformance.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.testing.test-double-without-protocol.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected a Swift test double class without protocol conformance.',
      code: 'HEURISTICS_IOS_TESTING_TEST_DOUBLE_WITHOUT_PROTOCOL_AST',
    },
  },
  {
    id: 'heuristics.ios.testing.production-test-double.ast',
    description: 'Detects Mock/Fake/Spy/Stub usage in iOS production code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.testing.production-test-double.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected Mock/Fake/Spy/Stub usage in iOS production code.',
      code: 'HEURISTICS_IOS_TESTING_PRODUCTION_TEST_DOUBLE_AST',
    },
  },
  {
    id: 'heuristics.ios.accessibility.low-contrast-static-color-pair.ast',
    description: 'Detects static SwiftUI foreground/background color pairs with insufficient contrast.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.accessibility.low-contrast-static-color-pair.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected a static SwiftUI foreground/background color pair with insufficient contrast.',
      code: 'HEURISTICS_IOS_ACCESSIBILITY_LOW_CONTRAST_STATIC_COLOR_PAIR_AST',
    },
  },
  {
    id: 'heuristics.ios.swiftui.non-private-state-ownership.ast',
    description: 'Detects @State/@StateObject declarations without private visibility in SwiftUI presentation code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.swiftui.non-private-state-ownership.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected @State/@StateObject without private visibility; SwiftUI owned state should be private.',
      code: 'HEURISTICS_IOS_SWIFTUI_NON_PRIVATE_STATE_OWNERSHIP_AST',
    },
  },
  {
    id: 'heuristics.ios.passed-value-state-wrapper.ast',
    description: 'Detects passed values stored as @State or @StateObject through init ownership in SwiftUI production code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.passed-value-state-wrapper.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected a passed value stored as @State/@StateObject via init wrapper ownership.',
      code: 'HEURISTICS_IOS_PASSED_VALUE_STATE_WRAPPER_AST',
    },
  },
  {
    id: 'heuristics.ios.foreach-indices.ast',
    description: 'Detects ForEach(...indices...) usage in SwiftUI presentation code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.foreach-indices.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected ForEach(...indices...) usage where stable element identity may be preferred.',
      code: 'HEURISTICS_IOS_FOREACH_INDICES_AST',
    },
  },
  {
    id: 'heuristics.ios.swiftui.foreach-self-identity.ast',
    description: 'Detects SwiftUI ForEach usage with id: \.self instead of stable domain identity.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.swiftui.foreach-self-identity.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected ForEach(..., id: \.self) usage; prefer a stable domain identity such as id: \.id or Identifiable models.',
      code: 'HEURISTICS_IOS_SWIFTUI_FOREACH_SELF_IDENTITY_AST',
    },
  },
  {
    id: 'heuristics.ios.swiftui.inline-foreach-transform.ast',
    description: 'Detects inline filter/map/sort transformations inside SwiftUI ForEach calls.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.swiftui.inline-foreach-transform.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected inline filter/map/sort work inside ForEach; prefiltered or cached collections remain the preferred baseline.',
      code: 'HEURISTICS_IOS_SWIFTUI_INLINE_FOREACH_TRANSFORM_AST',
    },
  },
  {
    id: 'heuristics.ios.swiftui.self-print-changes.ast',
    description: 'Detects SwiftUI Self._printChanges() debugging helpers in production views.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.swiftui.self-print-changes.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected Self._printChanges() in SwiftUI presentation code; remove debug-only render diagnostics from production views.',
      code: 'HEURISTICS_IOS_SWIFTUI_SELF_PRINT_CHANGES_AST',
    },
  },
  {
    id: 'heuristics.ios.swiftui.foreach-conditional-view-count.ast',
    description: 'Detects conditional view counts inside SwiftUI ForEach rows.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.swiftui.foreach-conditional-view-count.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected conditional view count inside ForEach; keep a constant number of views per element by moving branching into row views or modifiers.',
      code: 'HEURISTICS_IOS_SWIFTUI_FOREACH_CONDITIONAL_VIEW_COUNT_AST',
    },
  },
  {
    id: 'heuristics.ios.contains-user-filter.ast',
    description: 'Detects contains() usage in user-facing filter flows where localizedStandardContains() may be preferred.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.contains-user-filter.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected contains() in a user-facing filter where localizedStandardContains() may be preferred.',
      code: 'HEURISTICS_IOS_CONTAINS_USER_FILTER_AST',
    },
  },
  {
    id: 'heuristics.ios.geometryreader.ast',
    description: 'Detects GeometryReader usage in SwiftUI presentation code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.geometryreader.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected GeometryReader usage that may be replaceable with modern layout APIs.',
      code: 'HEURISTICS_IOS_GEOMETRYREADER_AST',
    },
  },
  {
    id: 'heuristics.ios.font-weight-bold.ast',
    description: 'Detects fontWeight(.bold) usage in SwiftUI presentation code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.font-weight-bold.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected fontWeight(.bold) usage where bold() may be preferred.',
      code: 'HEURISTICS_IOS_FONT_WEIGHT_BOLD_AST',
    },
  },
  {
    id: 'heuristics.ios.swiftui.explicit-color-static-member.ast',
    description: 'Detects Color.* static member usage where SwiftUI static member lookup is available.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.swiftui.explicit-color-static-member.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected Color.* static member usage where SwiftUI static member lookup may be preferred.',
      code: 'HEURISTICS_IOS_SWIFTUI_EXPLICIT_COLOR_STATIC_MEMBER_AST',
    },
  },
  {
    id: 'heuristics.ios.swiftui.closure-based-viewbuilder-content.ast',
    description: 'Detects closure-based SwiftUI content properties where @ViewBuilder let content: Content is preferred.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.swiftui.closure-based-viewbuilder-content.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected closure-based content storage; @ViewBuilder let content: Content remains the preferred SwiftUI container baseline.',
      code: 'HEURISTICS_IOS_SWIFTUI_CLOSURE_BASED_VIEWBUILDER_CONTENT_AST',
    },
  },
  {
    id: 'heuristics.ios.swiftui.large-config-context-prop.ast',
    description: 'Detects broad Config/Context properties stored directly on SwiftUI Views.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.swiftui.large-config-context-prop.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected a SwiftUI View storing a broad config/context object; pass only needed values to reduce update fan-out.',
      code: 'HEURISTICS_IOS_SWIFTUI_LARGE_CONFIG_CONTEXT_PROP_AST',
    },
  },
  {
    id: 'heuristics.ios.swiftui.conditional-same-view-identity.ast',
    description:
      'Detects SwiftUI if/else branches that rebuild the same View type for state-only visual changes.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.swiftui.conditional-same-view-identity.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected conditional branches rebuilding the same SwiftUI View type; prefer conditional modifiers or values to preserve view identity.',
      code: 'HEURISTICS_IOS_SWIFTUI_CONDITIONAL_SAME_VIEW_IDENTITY_AST',
    },
  },
  {
    id: 'heuristics.ios.swiftui.parent-owned-sheet-action.ast',
    description:
      'Detects SwiftUI sheets that receive save/cancel/dismiss callbacks from the parent view.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.swiftui.parent-owned-sheet-action.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected a SwiftUI sheet receiving parent-owned action callbacks; sheets should own save/cancel actions and call dismiss() internally.',
      code: 'HEURISTICS_IOS_SWIFTUI_PARENT_OWNED_SHEET_ACTION_AST',
    },
  },
  {
    id: 'heuristics.ios.swiftui.redundant-reactive-state-assignment.ast',
    description: 'Detects onChange/onReceive state assignments without a value-change guard.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.swiftui.redundant-reactive-state-assignment.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected reactive state assignment without a value-change guard; check for value changes before assigning state in hot paths.',
      code: 'HEURISTICS_IOS_SWIFTUI_REDUNDANT_REACTIVE_STATE_ASSIGNMENT_AST',
    },
  },
  {
    id: 'heuristics.ios.swiftui.non-lazy-scroll-foreach.ast',
    description: 'Detects ScrollView content backed by non-lazy stacks and ForEach.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.swiftui.non-lazy-scroll-foreach.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected ScrollView with a non-lazy stack feeding ForEach; LazyVStack/LazyHStack remain the preferred baseline for large scrollable collections.',
      code: 'HEURISTICS_IOS_SWIFTUI_NON_LAZY_SCROLL_FOREACH_AST',
    },
  },
  {
    id: 'heuristics.ios.swiftui.body-object-creation.ast',
    description: 'Detects expensive formatter object creation inside SwiftUI body.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.swiftui.body-object-creation.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected formatter object creation inside SwiftUI body; keep body simple and move expensive objects out of render paths.',
      code: 'HEURISTICS_IOS_SWIFTUI_BODY_OBJECT_CREATION_AST',
    },
  },
  {
    id: 'heuristics.ios.swiftui.image-data-decoding.ast',
    description: 'Detects UIImage(data:) decoding in SwiftUI presentation paths.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.swiftui.image-data-decoding.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected UIImage(data:) in SwiftUI presentation; downsample image data before rendering large images.',
      code: 'HEURISTICS_IOS_SWIFTUI_IMAGE_DATA_DECODING_AST',
    },
  },
  {
    id: 'heuristics.ios.swiftui.manual-rendering-without-imagerenderer.ast',
    description: 'Detects manual SwiftUI view rendering where ImageRenderer should be used.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.swiftui.manual-rendering-without-imagerenderer.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected manual SwiftUI view rendering without ImageRenderer.',
      code: 'HEURISTICS_IOS_SWIFTUI_MANUAL_RENDERING_WITHOUT_IMAGERENDERER_AST',
    },
  },
  {
    id: 'heuristics.ios.swiftui.large-viewbuilder-function.ast',
    description: 'Detects large @ViewBuilder helper functions in SwiftUI presentation code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.swiftui.large-viewbuilder-function.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected a large @ViewBuilder helper function; keep @ViewBuilder functions small and extract complex sections into subviews.',
      code: 'HEURISTICS_IOS_SWIFTUI_LARGE_VIEWBUILDER_FUNCTION_AST',
    },
  },
  {
    id: 'heuristics.ios.accessibility.animation-without-reduce-motion.ast',
    description: 'Detects SwiftUI animations that do not account for reduce motion.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.accessibility.animation-without-reduce-motion.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected SwiftUI animation without reduce motion handling.',
      code: 'HEURISTICS_IOS_ACCESSIBILITY_ANIMATION_WITHOUT_REDUCE_MOTION_AST',
    },
  },
  {
    id: 'heuristics.ios.swiftui.inline-action-logic.ast',
    description: 'Detects inline control-flow logic inside SwiftUI action handlers.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.swiftui.inline-action-logic.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected inline logic inside a SwiftUI action handler; action handlers should reference methods and keep view declarations focused.',
      code: 'HEURISTICS_IOS_SWIFTUI_INLINE_ACTION_LOGIC_AST',
    },
  },
  {
    id: 'heuristics.ios.navigation-view.ast',
    description: 'Detects NavigationView usage in iOS production code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.navigation-view.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected NavigationView usage.',
      code: 'HEURISTICS_IOS_NAVIGATION_VIEW_AST',
    },
  },
  {
    id: 'heuristics.ios.navigation.path-without-restoration.ast',
    description: 'Detects SwiftUI NavigationPath state without an explicit restoration contract.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.navigation.path-without-restoration.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected NavigationPath without an explicit restoration contract.',
      code: 'HEURISTICS_IOS_NAVIGATION_PATH_WITHOUT_RESTORATION_AST',
    },
  },
  {
    id: 'heuristics.ios.swiftui.untyped-navigation-link-destination.ast',
    description: 'Detects untyped SwiftUI NavigationLink destination usage.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.swiftui.untyped-navigation-link-destination.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected untyped NavigationLink destination usage; prefer NavigationLink(value:) with navigationDestination(for:) for type-safe navigation.',
      code: 'HEURISTICS_IOS_SWIFTUI_UNTYPED_NAVIGATION_LINK_DESTINATION_AST',
    },
  },
  {
    id: 'heuristics.ios.foreground-color.ast',
    description: 'Detects foregroundColor usage in modern SwiftUI code paths.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.foreground-color.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected foregroundColor usage.',
      code: 'HEURISTICS_IOS_FOREGROUND_COLOR_AST',
    },
  },
  {
    id: 'heuristics.ios.corner-radius.ast',
    description: 'Detects cornerRadius usage in modern SwiftUI code paths.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.corner-radius.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected cornerRadius usage.',
      code: 'HEURISTICS_IOS_CORNER_RADIUS_AST',
    },
  },
  {
    id: 'heuristics.ios.tab-item.ast',
    description: 'Detects tabItem usage where the modern Tab API may be preferred.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.tab-item.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected tabItem usage.',
      code: 'HEURISTICS_IOS_TAB_ITEM_AST',
    },
  },
  {
    id: 'heuristics.ios.on-tap-gesture.ast',
    description: 'Detects onTapGesture usage in iOS production code where Button is preferred.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.on-tap-gesture.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected onTapGesture usage where Button may be preferred.',
      code: 'HEURISTICS_IOS_ON_TAP_GESTURE_AST',
    },
  },
  {
    id: 'heuristics.ios.accessibility.on-tap-without-button-trait.ast',
    description: 'Detects tappable SwiftUI elements without button accessibility traits.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.accessibility.on-tap-without-button-trait.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected onTapGesture without button accessibility trait.',
      code: 'HEURISTICS_IOS_ACCESSIBILITY_ON_TAP_WITHOUT_BUTTON_TRAIT_AST',
    },
  },
  {
    id: 'heuristics.ios.swiftui.glass-interactive-static-element.ast',
    description: 'Detects Liquid Glass interactive styling on static SwiftUI elements.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.swiftui.glass-interactive-static-element.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected Liquid Glass .interactive() on a static SwiftUI element.',
      code: 'HEURISTICS_IOS_SWIFTUI_GLASS_INTERACTIVE_STATIC_ELEMENT_AST',
    },
  },
  {
    id: 'heuristics.ios.swiftui.glasseffectid-without-namespace.ast',
    description: 'Detects SwiftUI glassEffectID usage without @Namespace or Namespace.ID ownership.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.swiftui.glasseffectid-without-namespace.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected glassEffectID without @Namespace or Namespace.ID.',
      code: 'HEURISTICS_IOS_SWIFTUI_GLASSEFFECTID_WITHOUT_NAMESPACE_AST',
    },
  },
  {
    id: 'heuristics.ios.string-format.ast',
    description: 'Detects String(format:) usage in iOS production code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.string-format.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected String(format:) usage.',
      code: 'HEURISTICS_IOS_STRING_FORMAT_AST',
    },
  },
  {
    id: 'heuristics.ios.scrollview-shows-indicators.ast',
    description: 'Detects ScrollView initializer usage with showsIndicators: false in modern SwiftUI code paths.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.scrollview-shows-indicators.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected ScrollView(showsIndicators: false) usage.',
      code: 'HEURISTICS_IOS_SCROLLVIEW_SHOWS_INDICATORS_AST',
    },
  },
  {
    id: 'heuristics.ios.sheet-is-presented.ast',
    description: 'Detects .sheet(isPresented:) usage where .sheet(item:) may be preferred in SwiftUI flows.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.sheet-is-presented.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected .sheet(isPresented:) usage where .sheet(item:) may be preferred.',
      code: 'HEURISTICS_IOS_SHEET_IS_PRESENTED_AST',
    },
  },
  {
    id: 'heuristics.ios.legacy-onchange.ast',
    description: 'Detects legacy single-parameter onChange usage in modern SwiftUI code paths.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.legacy-onchange.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected legacy onChange usage where modern overloads may be preferred.',
      code: 'HEURISTICS_IOS_LEGACY_ONCHANGE_AST',
    },
  },
  {
    id: 'heuristics.ios.uiscreen-main-bounds.ast',
    description: 'Detects UIScreen.main.bounds usage in iOS production code.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.uiscreen-main-bounds.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected UIScreen.main.bounds usage.',
      code: 'HEURISTICS_IOS_UISCREEN_MAIN_BOUNDS_AST',
    },
  },
  {
    id: 'heuristics.ios.testing.xctest-import.ast',
    description: 'Detects legacy XCTest import usage in iOS unit and integration tests.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.testing.xctest-import.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected XCTest-only test usage where Swift Testing may be preferred.',
      code: 'HEURISTICS_IOS_TESTING_XCTEST_IMPORT_AST',
    },
  },
  {
    id: 'heuristics.ios.testing.xctest-suite-modernizable.ast',
    description:
      'Detects XCTestCase/test... suites that may be modernizable to Swift Testing in iOS unit and integration tests.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.testing.xctest-suite-modernizable.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected XCTestCase/test... suite that may be modernizable to Swift Testing with import Testing and @Test.',
      code: 'HEURISTICS_IOS_TESTING_XCTEST_SUITE_MODERNIZABLE_AST',
    },
  },
  {
    id: 'heuristics.ios.testing.xctassert.ast',
    description: 'Detects XCTest assertion macros in modern iOS tests.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.testing.xctassert.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected XCTest assertion usage where #expect may be preferred.',
      code: 'HEURISTICS_IOS_TESTING_XCTASSERT_AST',
    },
  },
  {
    id: 'heuristics.ios.testing.xctunwrap.ast',
    description: 'Detects XCTUnwrap usage in modern iOS tests.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.testing.xctunwrap.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected XCTUnwrap usage where #require may be preferred.',
      code: 'HEURISTICS_IOS_TESTING_XCTUNWRAP_AST',
    },
  },
  {
    id: 'heuristics.ios.testing.wait-for-expectations.ast',
    description: 'Detects wait(for:), waitForExpectations(timeout:) and waitForExistence(timeout:) usage in async iOS tests.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.testing.wait-for-expectations.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected wait(for:)/waitForExpectations/waitForExistence usage where an explicit async wait contract may be preferred.',
      code: 'HEURISTICS_IOS_TESTING_WAIT_FOR_EXPECTATIONS_AST',
    },
  },
  {
    id: 'heuristics.ios.testing.legacy-expectation-description.ast',
    description: 'Detects expectation(description:) scaffolding without modern fulfillment or confirmation flow in iOS tests.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.testing.legacy-expectation-description.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected expectation(description:) usage without modern fulfillment/confirmation flow.',
      code: 'HEURISTICS_IOS_TESTING_LEGACY_EXPECTATION_DESCRIPTION_AST',
    },
  },
  {
    id: 'heuristics.ios.testing.mixed-frameworks.ast',
    description:
      'Detects XCTestCase suites mixed with Swift Testing markers in the same iOS test file.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.testing.mixed-frameworks.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected XCTestCase and Swift Testing markers mixed in the same test file without explicit compatibility reason.',
      code: 'HEURISTICS_IOS_TESTING_MIXED_FRAMEWORKS_AST',
    },
  },
  {
    id: 'heuristics.ios.testing.quick-nimble.ast',
    description: 'Detects Quick/Nimble usage in iOS Swift tests.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.testing.quick-nimble.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected Quick/Nimble usage where native Swift Testing remains the preferred baseline.',
      code: 'HEURISTICS_IOS_TESTING_QUICK_NIMBLE_AST',
    },
  },
  {
    id: 'heuristics.ios.testing.third-party-ui-test-framework.ast',
    description: 'Detects third-party iOS UI test frameworks where native XCUITest is required.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.testing.third-party-ui-test-framework.ast',
      },
    },
    then: {
      kind: 'Finding',
      message:
        'AST heuristic detected third-party iOS UI test framework usage; use native XCUITest.',
      code: 'HEURISTICS_IOS_TESTING_THIRD_PARTY_UI_TEST_FRAMEWORK_AST',
    },
  },
  {
    id: 'heuristics.ios.core-data.nsmanagedobject-boundary.ast',
    description: 'Detects NSManagedObject usage in shared iOS boundaries.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.core-data.nsmanagedobject-boundary.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected NSManagedObject in a shared boundary.',
      code: 'HEURISTICS_IOS_CORE_DATA_NSMANAGEDOBJECT_BOUNDARY_AST',
    },
  },
  {
    id: 'heuristics.ios.core-data.nsmanagedobject-async-boundary.ast',
    description: 'Detects async iOS APIs that expose NSManagedObject directly.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.core-data.nsmanagedobject-async-boundary.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected NSManagedObject in an async boundary.',
      code: 'HEURISTICS_IOS_CORE_DATA_NSMANAGEDOBJECT_ASYNC_BOUNDARY_AST',
    },
  },
  {
    id: 'heuristics.ios.core-data.layer-leak.ast',
    description: 'Detects Core Data APIs leaking into iOS application or presentation layers.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.core-data.layer-leak.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected Core Data APIs leaking into application/presentation code.',
      code: 'HEURISTICS_IOS_CORE_DATA_LAYER_LEAK_AST',
    },
  },
  {
    id: 'heuristics.ios.swiftdata.layer-leak.ast',
    description: 'Detects SwiftData APIs leaking into iOS application or presentation layers.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.swiftdata.layer-leak.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected SwiftData APIs leaking into application/presentation code.',
      code: 'HEURISTICS_IOS_SWIFTDATA_LAYER_LEAK_AST',
    },
  },
  {
    id: 'heuristics.ios.core-data.nsmanagedobject-state-leak.ast',
    description: 'Detects NSManagedObject leaking into SwiftUI state or ViewModel state.',
    severity: 'WARN',
    platform: 'ios',
    locked: true,
    when: {
      kind: 'Heuristic',
      where: {
        ruleId: 'heuristics.ios.core-data.nsmanagedobject-state-leak.ast',
      },
    },
    then: {
      kind: 'Finding',
      message: 'AST heuristic detected NSManagedObject leaking into SwiftUI state or a ViewModel.',
      code: 'HEURISTICS_IOS_CORE_DATA_NSMANAGEDOBJECT_STATE_LEAK_AST',
    },
  },
];
