# Import Linter configuration for Adapt
#
# Purpose:
# Enforce architectural boundaries so the codebase stays modular,
# testable, and free of accidental coupling.
#
# Run:
#   lint-imports
#
# Philosophy:
# - The layer stack below is the architecture. A package may import only
#   packages in layers BELOW its own; siblings on one layer (joined by |)
#   are independent and never import each other.
# - `exhaustive = true` means every top-level package under adapt MUST be
#   placed in a layer. Adding a new package fails this check until it is
#   deliberately assigned a home — that is the point.
# - This file is mirrored in ARCHITECTURE.md; tests/test_architecture.py
#   asserts the two stay identical and match the real source tree.
# - Scientific-module independence (adapt.modules.X never imports
#   adapt.modules.Y) is enforced by tests/test_architecture.py, which
#   auto-discovers module subpackages at runtime.

[importlinter]
root_packages =
    adapt

include_external_packages = False

# ==========================================================
# 1. The layer stack
# ==========================================================
#
# cli            outermost shell; may import anything
# consumers      dashboards/TSE — read ONLY via adapt.api (see contract 2)
# visualization  plotting; core never imports it
# runtime        composition root: orchestrates everything below
# configuration  pydantic schemas + resolution; builds module configs
# api            StoreClient read facade over the store
# execution      graph + nodes: the one sanctioned wiring layer over modules
# modules        science; deterministic; no I/O to the store
# persistence    SQLite/NetCDF store; no science, no orchestration
# contracts      types + check_* validators; zero adapt imports
# downloaders    boto3/S3 isolation; zero adapt imports
# utils          shared pure functions; zero adapt imports

[importlinter:contract:layers]
name = Adapt layer stack
type = layers
containers =
    adapt
exhaustive = true
layers =
    cli
    consumers | visualization
    runtime
    configuration
    api | execution
    modules | persistence
    contracts | downloaders | utils

# ==========================================================
# 2. Consumers speak only through the public API
# ==========================================================
#
# The layer stack alone would let consumers import any lower layer.
# Consumers (dashboard, target selection) must not reach persistence,
# runtime, or execution directly — reaching them THROUGH adapt.api is
# the sanctioned path, hence allow_indirect_imports. StoreClient is the
# only read surface (I/O side doors are closed by
# tests/test_architecture.py::test_consumers_read_only_through_the_store_api).

[importlinter:contract:consumers_use_public_api_only]
name = Consumers do not import persistence, runtime, or execution
type = forbidden
source_modules =
    adapt.consumers
forbidden_modules =
    adapt.persistence
    adapt.runtime
    adapt.execution
allow_indirect_imports = True
