block columns 3 space down<["<b>layers</b><br>imports point down"]>(down) space anchored["<b>methods.anchored</b><br>consensus-anchored estimate"] apart<["<b>independence</b><br>no imports between"]>(x) bayesian["<b>methods.bayesian</b><br>consensus as prior"] history["<b>history</b><br>previous actuals, growth, revisions"]:3 anchor["<b>anchor</b><br>the panel as it stood at one moment"]:3 method["<b>method</b><br>compatibility import for core"]:3 core["<b>core</b><br>fit/apply protocol and registry"]:3 subjects["<b>subjects</b><br>compatibility import for validation"]:3 validation["<b>validation</b><br>subject and reading-policy contracts"]:3 panel["<b>panel</b><br>input panel and its Pandera schema"]:3 util["<b>util</b><br>dtype predicates, series primitives"]:3 defaults["<b>defaults</b><br>every constant, imports nothing"]:3 forbidden["<b>forbidden</b><br>no module in postforecast imports these"]:3 files["pathlib · os · io<br>tempfile · shutil · sys"] processes["subprocess · threading<br>multiprocessing · time · random"] network["socket · urllib · http · requests<br>httpx · pickle · json"] classDef family fill:#e3f1e6,stroke:#2e7d4f,color:#12301d classDef layer fill:#e6eef9,stroke:#3b6db3,color:#132a4a classDef compat fill:#f3f4f6,stroke:#9aa1ab,stroke-dasharray:4 3,color:#555 classDef contract fill:#fff4d6,stroke:#b58400,color:#4a3700 classDef forbid fill:#fdecec,stroke:#c0392b,color:#5a1a14 classDef module fill:#fff,stroke:#c0392b,color:#5a1a14 class anchored,bayesian family class history,anchor,core,validation,panel,util,defaults layer class method,subjects compat class down,apart contract class forbidden forbid class files,processes,network module
Architecture
The layers of the package and the direction imports run
postforecast is one pure package, postforecast, arranged in layers. A module may import from the layers below it and never from the layers above. uv run lint-imports enforces the order through .importlinter, so a wrong import fails just check rather than waiting for review.
Layers
From top to bottom:
methods: The method families. Each family is a subpackage that owns its parameters, its preparation and its publication boundary.history: Realised history per entity: previous actuals, growth and consensus revisions. It sits aboveanchorbecause a revision is one quantity read at two moments, and both reads have to be anchored.anchor: Reading the panel as it stood at one moment: strictly before the reading time, and no older than a maximum age.method: Compatibility imports for the method protocol and registry, kept so older import paths still work.core: The fit/apply protocol, declared requirements and the registry that finds a method by name.subjects: Compatibility import for subject-frame validation.validation: Contracts shared by preparation and anchoring: subject columns, reading-policy scalars and caller-supplied publication schedules.panel: The input panel and its Pandera schema, the one input format every method reads.util: Dtype predicates, point-in-time series primitives and the helper that keeps a renamed constant importable.defaults: Every default and numerical constant in the package. It imports nothing, so any layer can read it.
Rules the contracts enforce
- One direction: The layer order above is an import-linter
layerscontract. A lower layer never imports a higher one. - Families are independent:
methods.anchoredandmethods.bayesiannever import each other. Code that two families need moves down a layer, never sideways. - Pure: No module imports file, network, process, clock or randomness modules. Serialising an audit record is the caller’s job, at the caller’s edge.
Package layout
Modules are listed from the bottom layer up.
src/postforecast/
├── __init__.py # public API, re-exported from the layers below
├── defaults.py # every default and research constant
├── util/
│ ├── dtypes.py # dtype predicates for dataframe contracts
│ ├── renames.py # renamed constants, importable with a warning
│ └── series.py # point-in-time series primitives
├── panel.py # the input panel and its Pandera schema
├── validation/
│ ├── publication.py # caller-supplied publication schedules
│ ├── subjects.py # numeric and age columns methods consume
│ └── temporal.py # reading-policy scalar contracts
├── subjects.py # compatibility import for validation.subjects
├── core/
│ └── method.py # fit/apply protocol, requirements, registry
├── method.py # compatibility import for core.method
├── anchor.py # read the panel strictly before one moment
├── history.py # previous actuals, growth, revisions
└── methods/
├── __init__.py # imports each family so it registers
├── anchored/ # deterministic consensus-anchored estimate
│ ├── parameters.py # shipped, versioned coefficient sets
│ ├── gap.py # forecaster distance from consensus
│ ├── eligibility.py # publishable or not, with the reason
│ ├── estimate.py # the published number and its parts
│ ├── preparation.py # long panel to prepared subjects
│ └── publication.py # dataframe publication boundary
└── bayesian/ # consensus as prior, forecaster as evidence
├── settings.py # posterior arguments at shipped defaults
├── presets.py # named, documented settings bundles
├── series_math.py # growth, bias and moments
├── posterior.py # precision-weighted posterior
├── intervals.py # interval shape and directional confidence
├── empirical.py # intervals from pooled residuals
├── eligibility.py # publishable or not, with the reason
├── subjects.py # subject frame for BayesianPosterior
├── preparation.py # point-in-time preparation
├── prediction_settings.py # full-workflow configuration and names
├── prediction_math.py # independent and correlation-adjusted math
└── prediction.py # full Bayesian workflow over a tidy panel