[importlinter]
root_package = previously
# Five of the six contracts forbid external packages (sqlalchemy, psycopg,
# alembic, anthropic, openai, pyrage, boto3, botocore). import-linter requires
# this setting for that, otherwise it aborts with a configuration error — it
# is not a loosening of the contracts themselves.
include_external_packages = True

[importlinter:contract:layers]
name = Layers: core beside migrations, both above storage, contract below all
type = layers
layers =
    previously.cli
    previously.core | previously.migrations
    previously.storage
    previously.contract

[importlinter:contract:core-is-clean]
name = core knows no foreign system and no model
type = forbidden
source_modules =
    previously.core
forbidden_modules =
    sqlalchemy
    psycopg
    alembic
# Until 2026-10-04 this contract carried two named exemptions,
# `core.append -> storage.postgres` and `core.verify -> storage.postgres`,
# because both modules typed their `storage` parameter as the concrete
# `PostgresStorage` under `TYPE_CHECKING`. They were granted by ruling T7-a
# and by ruling T8-c of the 2026-10-02 stage 1a plan, whose execution ledger
# was never shipped and is lost, so both labels are provenance and nothing
# more — the reasoning is the paragraph you are reading.
#
# There is no directory to follow them into, and until 2026-10-04 this
# comment named one. Measured that day against the only record under
# `docs/superpowers/sdd/`: `2026-10-03-dokumentation/progress.md` carries a
# `Ruling T7-a` about an unrelated decision and no `Ruling T8-c` at all, so
# the pointer sent a reader to the wrong decision — worse than a label with
# no pointer, because the reader stops with an answer.
#
# The exemptions were enumerated by name and not matched by a wildcard, so
# that a third module following the same pattern would break this contract
# until somebody granted it deliberately — measured with a throwaway module,
# a wildcard let the new edge through silently.
#
# Stage 1b typed `core` against `contract.store.LogStore[Conn]` instead, and
# the edge is gone. There is nothing to exempt. Should an exemption ever come
# back here, it is a named edge, never a pattern; {ref}`module-boundaries`
# carries the measurement that says why.

[importlinter:contract:core-and-contract-know-no-sql]
name = core and contract import no sqlalchemy
type = forbidden
source_modules =
    previously.core
    previously.contract
forbidden_modules =
    sqlalchemy
    psycopg
# Until 2026-10-04 this contract carried the same two named exemptions as
# `core-is-clean`: edges enumerated one by one, no wildcard across `core`. See
# there for the reasoning, and for what the two stage 1a ruling labels are
# worth. Stage 1b typed `core` against `contract.store.LogStore[Conn]`, the
# edge `core -> storage.postgres` is gone, and there is nothing left to exempt
# here either.
#
# The contract was named `Only storage imports sqlalchemy`, with the id
# `only-storage-knows-sql`, until 2026-10-05. That day the migrations moved into
# the package, and they import SQLAlchemy too, so the old name stated
# something false while the contract still checked the right thing: its
# sources were always `core` and `contract`, and the name now says so.

[importlinter:contract:no-vendor-sdk]
name = No vendor SDK in the package
type = forbidden
source_modules =
    previously
forbidden_modules =
    anthropic
    openai

[importlinter:contract:only-sealing-knows-age]
name = Only core.sealing imports pyrage
type = forbidden
source_modules =
    previously
forbidden_modules =
    pyrage
ignore_imports =
    previously.core.sealing -> pyrage
# Sealing happens where the rules are, in `core`, so that the store below
# gets and gives ciphertext only: a module in `storage` that could open what
# it stores is the mistake this contract exists to catch. The whole package
# is the source, and the one edge that may exist is named — never a pattern,
# for the reason `core-is-clean` gives. Should `core.sealing` stop importing
# `pyrage`, `lint-imports` fails with "No matches for ignored import"
# (measured on 2026-10-05), so the exemption cannot outlive its edge.

[importlinter:contract:only-s3-knows-boto3]
name = Only storage.s3 imports boto3
type = forbidden
source_modules =
    previously
forbidden_modules =
    boto3
    botocore
ignore_imports =
    previously.storage.s3 -> boto3
    previously.storage.s3 -> botocore
# Storing happens where the foreign systems are, in `storage`, the way SQL
# stays in `storage.postgres`: what reaches `core` is the protocol in
# `contract.blobs` and the errors in `storage.errors`, no `botocore` type.
# `botocore` is forbidden beside `boto3` because the exceptions and the
# client configuration come from there, and an import of either would carry
# the foreign system upwards. Two named edges, out of one module.
