Canonical application architecture

KOPR is a fully canonical src/kopr package application. All active product implementation is owned inside the package; retained top-level Python files form a non-owning compatibility/source-launcher perimeter.

R515 / C8 canonical baseline R515, not R410, is the final canonical-convergence baseline. The current public release line is owned by kopr.config.release_identity and is displayed dynamically on the project home page.

Canonical baseline and current architecture evidence

R515/C8 remains the canonical architecture baseline: all active implementation belongs under src/kopr/, and flat-root Python is a non-owning compatibility/launcher perimeter.

The current Theta-110 WCCD qualification reports 322 canonical src/kopr Python files, 802 canonical import edges, 0 canonical SCCs and 0 architecture violations. These are current release evidence, while the R515 inventory remains the historical convergence snapshot. Hard-zero ownership/cycle/violation invariants remain the governing contract as the canonical package legitimately grows.

Entry points and packaging

source checkout:  python3 kopr.py
installed CLI:    kopr
canonical target: kopr.application.main:main

The root kopr.py is a minimal historical source-checkout launcher. It does not own application behavior. Application composition and MainWindow are canonically owned by kopr.application.main. An installed wheel/package contains the complete active application and does not require root-only implementation modules.

The retired _kopr_src_bootstrap.py mechanism is not part of the current architecture and must not be recreated.

Canonical package layers

LayerResponsibilityImportant boundary
kopr.coreScientific/domain logic, numerical algorithms, astronomy/ephemerides, photometry, observation and Planner core contracts.Qt-free; no dependency on features, application or UI.
kopr.infrastructurePersistence, files, FITS/RAW, networking, MPC/COBS/VizieR, catalogues, PDF and other external adapters.No dependency on features, application or UI.
kopr.featuresFeature orchestration/state for Analyzer, Planner, WCCD, WStack, Visual and ephemeris workflows.Cross-feature use only through intentional public APIs.
kopr.uiShared presentation components. kopr.ui.common is the cross-feature shared UI boundary.Must not own scientific/domain decisions.
kopr.applicationTop-level composition, lifecycle, wiring and application entry.Reusable science does not belong here.
kopr.compatExplicit compatibility contracts/adapters.Never a destination for new product behavior.
kopr.configConfiguration definitions and config-facing contracts.Not a global mutable-state hub.
kopr.legacyQuarantined historical behavior.Not a target for new features.
kopr.tools / repository tools/Diagnostics, architecture, QA and maintenance.Production runtime must not depend on QA/migration tooling.

Hard architectural invariants

active_root_implementation_modules = 0
partial_legacy_facades_with_implementation = 0
root_to_root_import_edges = 0
src_kopr_to_flat_root_imports = 0
src_kopr_SCC = 0
architecture_violations = 0
  • All new implementation belongs under src/kopr/.
  • One capability has one canonical owner.
  • Canonical code never imports a KOPR flat-root compatibility module.
  • The canonical package dependency graph remains acyclic.
  • Architecture budgets/allowlists are not weakened merely to make a change pass.
flat-root compatibility / launcher
            ↓
       canonical src/kopr

The dependency arrow is one-way. The reverse direction is forbidden.

Root compatibility perimeter

R515 retained 86 top-level Python surfaces, all explicitly classified and non-owning. They can preserve historical import paths, module/class/function identity, pickle GLOBAL resolution, monkeypatch/rebinding semantics, historical CLI behavior or source-checkout compatibility.

They are not a second implementation layer. New scientific, persistence, networking, orchestration or GUI behavior must not be added to them. Compatibility removal or semantic retargeting requires a separate compatibility-breaking plan and qualification.

Canonical ownership and public APIs

Documentation should describe capabilities and canonical package ownership rather than old flat-root filenames. When a historical release page names modules such as wplanner.py, wlightcurves.py or koprhand.py, interpret those names as historical implementation evidence. Current code ownership is defined by src/kopr/ and the machine-readable package/API policies.

Examples of intentional current boundaries include kopr.features.analyzer.api, kopr.features.ephemeris.api, kopr.core.lightcurves.observations and kopr.core.lightcurves.projection. Internal modules are not public merely because Python can import them.

Current architecture and QA tooling

tools/architecture/architecture_guard.py
tools/architecture/architecture_policy.json
tools/architecture/compatibility_perimeter.json
tools/architecture/legacy_compatibility_identity_contract.json
tools/architecture/package_api_policy.json

tools/qa/

Run python3 tools/architecture/architecture_guard.py before and after production or structural work. Active tools/migration/ is intentionally absent. Historical migration reports, per-release migration gates and source-layout budgets are audit evidence only, not current development instructions.

Public release identity

Source snapshot note: the supplied current architecture-policy Markdown still contains a literal Theta-1 release-line example. Current product evidence is Theta-110, while the live public identity remains owned by kopr.config.release_identity. Treat the policy's literal revision as stale metadata, not as a rollback of the product release.
canonical owner: src/kopr/config/release_identity.py
website display: refreshed from the downloaded Python 3 repository ZIP

The current user-facing release is not copied into the documentation as a fixed value. koprgit.py reads the canonical release source from the newly downloaded repository archive without importing or executing it, and publishes the resulting KOPR Live <Series>-<Revision> label on the project home page. When present, the technical package version is read separately from pyproject.toml.

R515/C8 remains the structural baseline; internal Rxxx identifiers are engineering evidence only. Theta-specific release values in historical source reports remain provenance, not strings to copy forward. Documentation-only revisions do not advance the KOPR product release. The homepage release/download label continues to be derived from the downloaded canonical repository archive.

Why R515, not R410, is the structural baseline

R410 completed an earlier seven-phase behavior-neutral plan but left a hybrid architecture. A later runtime audit proved that 67/67 actual root implementation modules remained launcher-reachable. The end-state contract was therefore tightened to zero root implementation ownership.

R515/C8 is the first baseline that proves the full canonical end state. Any older wording such as “R410 completed migration”, “root implementation owners are acceptable” or “use tools/migration/ as the current source of truth” is historical and must not guide new development.

WCCD scientific and presentation ownership

Current WCCD aperture-check science is canonically owned by kopr.core.photometry.comet_aperture_check. WCCD runtime orchestration is owned by kopr.application.wccd_runtime; feature presentation/state contracts remain under kopr.features.wccd. Compatibility files must not acquire a second implementation.

Reference-star ensemble ownership and overlay ownership are deliberately different: the scientific ensemble may span the observation, while measured-star artists remain bound to their exact source image. See WCCD aperture check and reference provenance.

Rules for extending KOPR

  1. Start from the latest qualified canonical build.
  2. Read the current development policy and current machine-readable architecture policy.
  3. Identify the canonical owner under src/kopr/.
  4. Run the architecture guard before editing.
  5. Implement one coherent capability in the correct layer; do not create flat-root helpers or duplicate implementations.
  6. If unexpected path/global-state/Qt/multiprocessing/serialization/scientific coupling appears and needs a complex solution, re-plan the next bounded batch rather than adding an architecture exception.
  7. Run risk-appropriate focused/regression/package/GUI/scientific qualification and the architecture guard again.

See the exact normative rules in Development policy.

Immutable operation capture and freshness

For asynchronous scientific work, WStack now provides a concrete current pattern: capture the operation once, pass detached immutable metadata across thread/process boundaries, and bind reused filesystem metadata to explicit identity such as canonical path, file size and mtime_ns. If identity no longer matches, fall back to authoritative disk reconstruction rather than trusting cached mutable objects.

Application/UI layers orchestrate lifecycle and commit. Scientific ownership remains in canonical lower layers; for current Relative-WCS that owner is kopr.core.stacking.relative_wcs.