Contracts

distribution-profile.md

Contract: DistributionProfile

Shape

@dataclass(frozen=True)
class DistributionProfile:
    package_name: str
    package_aliases: tuple[str, ...] = ()
    upgrade_provider: object | None = None  # LatestVersionProvider instance or None
    index_url: str | None = None
    extra_index_url: str | None = None
    data_freshness_seconds: int | None = None
    disable_public_pypi_notifier: bool = False
    version_label: str | None = None

Consumers

ConsumerFields used
resolve_cli_package_name / version utilspackage_name, package_aliases
session-presence refresh / compat plannerprovider + package_name
has_fresh_data pathdata_freshness_seconds
remediation / upgrade_hintpackage_name, index_url, extra_index_url
maybe_emit_no_upgrade_noticedisable_public_pypi_notifier
--version bannerversion_label or package_name

Stock profile

package_name="spec-kitty-cli", empty aliases, PyPIProvider, no index URLs, default freshness, notifier enabled, version_label=None (banner uses package name).

entry-points.md

Contract: Packager entry-point groups

Groups

GroupCardinalityLoad shapeFallback
spec_kitty.cli_package0..n (prefer 1)str, zero-arg callable → str, or object with package_name/namepackages_distributions"spec-kitty-cli"
spec_kitty.upgrade_provider0..nzero-arg callable/type → object with get_latestPyPIProvider()
spec_kitty.distribution_profile0..1 preferredzero-arg callable/type → DistributionProfilesynthesize from above / stock

Multi-registration

  • Upgrade providers: SPEC_KITTY_UPGRADE_PROVIDER=<entry_point_name> selects; else first name alphabetically.
  • Unknown env name: ignore and use deterministic alphabetical pick (or stock if load fails) — never invent a class.
  • Load/construct errors: fall back to stock; never raise to callers.

Non-goals

  • No runtime env var for distribution package name.
  • No .kittify/config.yaml keys for these hooks.

simple-index-provider.md

Contract: SimpleIndexProvider

Behaviour

  • Implements LatestVersionProvider.get_latest(package) -> LatestVersionResult.
  • Never raises.
  • Fetches PEP 503 simple index HTML for the configured base URL + package.
  • Parses version candidates from anchor hrefs / wheel filenames; returns the highest stable sanitised version matching _VERSION_RE. Pre-releases are excluded (mirroring PyPIProvider's stable info.version); the highest pre-release is returned only when no stable version exists. Anchors carrying a PEP 592 data-yanked attribute are skipped.
  • TLS verification on; response size capped (same order as PyPIProvider 1 MiB); redirects not followed.
  • Successful lookups set source="simple_index".
  • Failures return version=None, source="none", error=<token> (timeout, http_error, parse_error, oversized).

Construction

  • Base class accepts index_url (+ optional package filename prefix) in __init__.
  • Fork subclasses provide zero-arg __init__ that calls super().__init__(...) with their URL — upstream defaults must not embed fork URLs.

Non-goals

  • Full PEP 503 JSON simple API (HTML only for this mission unless already trivial).
  • Authentication headers for private indexes (packager subclass may override fetch later; not required in upstream base).