[importlinter]
root_package = mcp_hangar
include_external_packages = False

# ---------------------------------------------------------------------------
# Hexagon layering
# ---------------------------------------------------------------------------
# Read bottom-up: a layer may import anything BELOW it and nothing above.
#
#   shared kernel   cross-cutting vocabulary every layer may speak: logging,
#                   the error taxonomy, protocol constants, the SDK shim, the
#                   vendored task wire (ADR-015). No I/O, no policy.
#   domain          entities, value objects, policies, ports.
#   application     use cases, command/query handlers, sagas, read models.
#   infrastructure  adapters. The root-level modules on this line are adapters
#                   that never moved into the package: metrics is a Prometheus
#                   registry, http_client and stdio_client are transports,
#                   retry/progress/gc are runtime machinery.
#   delivery        HTTP/MCP surfaces and the facade.
#
# The split of the root-level modules is the point of this contract rather than
# a detail. Folding all 14 into the shared kernel would have been a shorter
# file and would have legitimised `domain -> metrics` and
# `domain.contracts.launcher -> http_client` -- a port importing its own
# adapter. Those are the leaks the contract exists to make visible.
#
# Component packages (auth, approvals, compliance, integrations, bootstrap,
# observability) carry their own internal layering and are deliberately out of
# scope here; exhaustive = False keeps them unconstrained rather than
# pretending this contract governs them.

[importlinter:contract:hexagon]
name = Hexagon: shared kernel < domain < application < infrastructure < delivery
type = layers
layers =
    mcp_hangar.server : mcp_hangar.fastmcp_server : mcp_hangar.facade
    mcp_hangar.infrastructure : mcp_hangar.metrics : mcp_hangar.http_client : mcp_hangar.stdio_client : mcp_hangar.retry : mcp_hangar.progress : mcp_hangar.gc
    mcp_hangar.application
    mcp_hangar.domain
    mcp_hangar.logging_config : mcp_hangar.lock_hierarchy : mcp_hangar.redactor : mcp_hangar.errors : mcp_hangar.protocol : mcp_hangar.context : mcp_hangar._sdk_compat : mcp_hangar.tasks_wire : mcp_hangar.negotiation : mcp_hangar.observability : mcp_hangar.trusted_hosts
exhaustive = False

# The debt ledger. Every line is an edge that exists today and should not.
# It may shrink; tests/unit/test_import_contracts.py caps it so it cannot grow.
#
# It went 33 -> 9. What cleared did so because the edge turned out to be held
# up by code that could not run: a port that was never wired, a fallback beside
# an injected dependency, a deprecation shim with no callers. Measuring first
# was what found those -- three of the "cycle avoidance" imports avoided no
# cycle at all, and grimp said so in a second.
#
# The nine that remain are NOT of that kind, and the difference is worth
# stating so the next pass does not mistake churn for progress:
#
#   * application -> metrics (2) + observability.tracing -> metrics.
#     Services instrumenting themselves. A port per counter would turn
#     IMetricsPublisher into a god interface; `metrics` is an adapter (it has a
#     scrape endpoint behind it), so moving it into the shared kernel is
#     relabelling, not repair. cost_handler CLEARED: CostReportGenerated now
#     carries the mcp_server / tool / cost_model dimensions (schema v2), so the
#     metrics adapter can reconstruct the counter from the event and the
#     application layer no longer writes one directly. No upcaster was needed --
#     the change is additive and v1 rows genuinely lack the dimensions, so
#     passthrough plus empty defaults is the honest reading of old data.
#     The tracing one is a third case again: tracing.py is dual, half
#     ambient-span accessors that logging_config legitimately needs and half
#     OTLP exporter setup. Splitting those two apart is the honest fix and is a
#     673-line module's worth of work.
#
#   * context -> domain.value_objects.identity. The kernel's contextvar carrier
#     needing a domain type. Moving IdentityContext down would put a validated
#     domain concept (CallerIdentity enforces an invariant) in the kernel;
#     moving the contextvar up into domain puts a runtime mechanism in a
#     value-objects module and rewrites 41 import sites. Accepted for now
#     BECAUSE both available moves are worse than the edge, not because nobody
#     looked.
#
#   * domain.model.mcp_server -> infrastructure.launchers, and
#     domain.services -> application.read_models.tool_projection. The aggregate
#     constructing its own launchers despite IMcpServerLauncher existing, and a
#     domain service reaching into an application read model. These two look
#     like the ones that cleared -- worth measuring next.
#
#   * application.event_handlers.detection_handler -> server.api.sessions
#     CLEARED. It was the only application -> delivery edge, and it was three
#     problems in three lines: a function-local import, hiding a reach past the
#     underscore into another module's private state, in the wrong layer. The
#     suspended-session store was never route code -- it is a bounded TTL cache,
#     i.e. an adapter -- so it moved to infrastructure behind
#     ISessionSuspensionRegistry and the handler is handed the same instance the
#     routes use.
#
#   * infrastructure.truncation.manager -> server.tools.batch.models. A shared
#     DTO living in the delivery layer; likely wants to move down rather than
#     be inverted.
#
ignore_imports =
    mcp_hangar.application.commands.handlers -> mcp_hangar.metrics
    mcp_hangar.application.commands.load_handlers -> mcp_hangar.infrastructure.runtime_store
    mcp_hangar.application.discovery.discovery_orchestrator -> mcp_hangar.metrics
    mcp_hangar.application.mcp.tooling -> mcp_hangar.fastmcp_server.asgi
    mcp_hangar.context -> mcp_hangar.domain.value_objects.identity
    mcp_hangar.domain.model.mcp_server -> mcp_hangar.infrastructure.launchers
    mcp_hangar.domain.services -> mcp_hangar.application.read_models.tool_projection
    mcp_hangar.infrastructure.truncation.manager -> mcp_hangar.server.tools.batch.models
    mcp_hangar.observability.tracing -> mcp_hangar.metrics
