WStack integration contract

The narrow, reproducible interface between the KOPR host, WStack and WCCD measurement workflows.

Source-verified The contract was consolidated from the maintained merge plan and the host callbacks present in the audited source baseline.

Owned files and dependency boundary

WStack is implemented by three root-level files:

  • wstack.py — tab GUI, dialogs, workers and host adapter;
  • wstack_functions.py — calibration, QC, astrometry, reprojection and stacking algorithms;
  • wstack_common.py — frame loading, normalized metadata, FITS/WCS helpers and session state.

It uses the production host's koprfig.ImageDialog and host settings adapter. A WStack delivery must not overwrite the production koprfig.py with a private copy.

Host construction and lifecycle API

The current host constructs WStack as:

wstack.ScrollWStack(
    parent,
    settings_provider=host._settingsSnapshot,
    locations_provider=host._locationChoices,
    measure_stacked_callback=host._measureStackGroup,
)
SurfaceDirectionContract
refresh_host_settings()Host → existing WStack widgetRefresh displayed active location/telescope/camera without creating a new tab.
shutdown_workers(timeout_ms)Host → WStackCancel and join owned workers; return false if safe shutdown is incomplete.
settings_provider()WStack → host callbackReturn the current historical KOPR settings structure.
locations_provider()WStack → host callbackReturn all configured observing locations.
manual measurement callbackWStack → hostPass one validated stack-group mapping to WCCD.

ScrollWStack(parent) remains usable with standalone fallbacks, but the integrated host should supply providers/callbacks rather than allowing WStack to infer main-window internals.

Host settings mapping

settings[2] = active location
settings[3] = active telescope
settings[4] = active eyepiece
settings[5] = active camera

Expected rows: location: name, longitude, latitude, altitude, GMT; telescope: name, type, aperture, focal length, optional fixed zoom/FOV; camera: name, camera code, chip name/code, X/Y resolution, X/Y pixel size, optional spectral peak.

R56 requires these four active rows to survive restart. Do not restore the old behaviour that forced menu index zero after Settings or startup. Persist by flushed atomic replacement of settings.dat. If a future KOPR version changes the schema, modify WStack's host-context adapter only; do not distribute new raw indices through astrometry, stacking or photometry functions.

R57 keeps WStack operation preferences separate from host configuration. Save validated method/sigma values in wstack/comet and wstack/stars; save comet output mode with the Comet namespace. Cancelled or invalid dialogs must not commit preferences.

Matched-stack measurement contracts

Measure route

The host callback requires a mapping with a valid comet stack path, an optional stars stack path, a positive frame count and pair metadata. The host creates/reuses WCCD, switches to its tab and calls start_stacked_group_measurement(group). The ordinary New CCD/DSLR observation dialog opens with transferred values as editable defaults. R29 preserves one named Browse handler per path, click/Enter activation, empty-field-only Tab activation, modal/re-entry guards, Cancel path preservation and the existing Stars Clear action.

Stack-method dialog

Mean and Sigma-clipped Mean must remain one compatibility-group family: changing between them only toggles the sigma control. Entering or leaving Sum must rebuild groups because the exposure compatibility contract changes.

ACF and Review integration boundary

The ACF/Centroid/GUI merge adds production acfcore.py plus coordinated changes in wccd.py, autophot.py and photom.py. A later WStack rebase must not overwrite these newer files with an older integration baseline.

Preserve pa-adaptive-upper-v1, immutable ACFMeasurementContext, context/run invalidation, Review button placement, compatible PhotometryACF() return values and kopr-acf-provenance-v1. Resolve conflicts semantically rather than restoring the former symmetric ACF implementation.

Operational priorities to preserve

Topocentric position

  1. manual MPC observatory code when explicitly supplied;
  2. active KOPR longitude, latitude and altitude;
  3. geocentric fallback only when no observing site is available.

FITS acquisition times are UTC. The configured GMT field must not be added to DATE-OBS.

R48 astrometry input contract

  • Offline position: complete FITS WCS; FITS pointing; internal per-frame comet ephemeris only when FITS has no usable position.
  • Offline scale: FITS WCS/pixel-scale metadata; active KOPR optics with FITS binning; standalone fallback only when integrated geometry is unavailable.
  • Manual override: both fields in the RA/DEC or scale pair must be edited; accepting displayed automatic values does not freeze them. Clearing both scale fields restores automatic scale.
  • Online: blind FITS upload with an empty constraint dictionary; no offline-parameter dialog; calibration requested only after server success.
scale_x = 206.265 × pixel_x_um × XBINNING / focal_length_mm
scale_y = 206.265 × pixel_y_um × YBINNING / focal_length_mm
scale   = sqrt(scale_x × scale_y)

The local solver interval remains ±15% around this estimate.

Solver backend and index ordering

  1. native solve-field on PATH;
  2. on native Windows, the default or explicitly selected WSL distribution;
  3. fail once with an actionable error when no backend is usable;
  4. check/download indexes only after solver preflight succeeds.

Preserve KOPR_WSL_DISTRIBUTION, wslpath-based path translation, solve-field/WSL provenance and process-group cancellation. Do not replace argument-token passing with shell-interpolated paths.

R45 solver retry and group guard

  • preserve the three-attempt ceiling and attempt ordering;
  • create robust copies only in isolated work directories and never mutate calibrated inputs;
  • return from a failed adaptive seed to the frame's independent seed on attempt 2;
  • keep Stop and skip remaining frames as the default required-anchor policy;
  • retain the explicit independent-seed override for heterogeneous groups;
  • discover 4100 and 4200 siblings while keeping 4100 optional and automatic download at 4206–4214.

R47 WCCD source and saturation fallback contract

  • Preserve exact case-sensitive COBS identifiers. APASS Sloan g′/r′/i′ are AG/AR/AI; calculated R/I share AR/AI with the corresponding direct Sloan sources; Gaia identifiers retain their defined mixed case.
  • Do not use the observed image maximum as a saturation threshold.
  • After complete NSATVAL and explicit detector metadata, detector bit-depth metadata must outrank the wider integer storage ceiling.
  • Floating 0–1 data resolve to 1.0; other floating data without metadata resolve to editable non-authoritative 65535.
  • Keep Comet and Stars threshold state independent and retain the R40 manual-override semantics.

Repeatable production rebase

  1. Archive the production baseline. Record its checksum and create a clean working copy.
  2. Compare host surfaces. Inspect current kopr.py, koprhand.py, koprfig.py and WCCD construction before applying old edits.
  3. Start from the latest stabilized WStack trio. Do not reintroduce an older test ZIP after later QC/photometry fixes.
  4. Apply host changes by semantic anchor. Import WStack, add/reposition its persistent tab constants, extend the lazy factory, supply callbacks/providers, refresh existing context and include worker shutdown.
  5. Verify the settings adapter. Confirm row shape and units; adapt only the boundary when needed.
  6. Keep production shared modules. Do not overwrite settings.py, koprfig.py, datavar.py or unrelated tabs merely to make an old patch apply.
  7. Run syntax and automated tests. Use python -m pytest -q tests and verify_autophot_build.py.
  8. Run the live regression checklist. Exercise tabs, settings, workers, calibration, astrometry, stacking and the Measure hand-off into WCCD.
  9. Write a merge report. Document every intentional divergence, test result and final archive checksum.

Integration regression checklist

Host

  • all six original positions remain stable and open lazily;
  • current selection and status bar remain correct;
  • Configuration menus change active settings normally;
  • active Location/Telescope/Eyepiece/Camera survive restart and are not forced to index zero;
  • settings.dat writes are flushed and atomically replaced;
  • no duplicate menu or toolbar item is introduced;
  • application shutdown stops WStack workers safely.

WStack GUI

  • opens as the integrated tab, not a second top-level main window;
  • Raw/Calib/Astro/Stack pages work;
  • preview uses production koprfig.ImageDialog;
  • WStack settings retain API key/fallbacks;
  • Comet and Stars method/sigma preferences remain independent, comet output mode persists, and Cancel/invalid input does not save;
  • active host context refreshes after configuration change;
  • all user-facing text remains English.

Astrometry backend

  • native solver remains preferred;
  • native Windows falls back to the selected WSL distribution;
  • solver preflight precedes index checking/downloading;
  • Windows paths, including spaces, translate through wslpath;
  • generated WSL config uses WSL-visible paths and LF endings;
  • provenance records solve-field/WSL;
  • Cancel terminates the Linux solver process group without shutting down the entire WSL distribution;
  • missing launcher/package failures do not retry every frame.
  • each frame performs no more than the documented source/robust/robust-broad three-stage sequence;
  • the robust temporary input removes isolated impulses without changing the calibrated source or multi-pixel stellar profiles;
  • required-anchor failure produces one FAILED result and deterministic SKIPPED_ANCHOR_FAILED group results under the default policy;
  • the independent-seed continuation policy is explicit and per group;
  • 4100 and 4200 sibling families are discovered, missing 4100 remains non-fatal and automatic download remains 4206–4214;

Pipeline and hand-off

  • DSLR discovery occurs before session mutation and automatically sends pending/stale candidates to materialization;
  • no confirmation or RAW-bypass branch remains;
  • RawMaterializationWorker and progress dialog run outside the GUI thread with cooperative between-file cancellation;
  • post-resolution rediscovery produces a FITS-only _session_files index and migrates historical RAW state paths to verified TG FITS;
  • conflicting targets are preserved and incomplete final FITS is never committed;
  • master dark/flat, LIGHT calibration and astrometry;
  • AXY diagnostics and relative-WCS OFF persistence;
  • star stack, manual comet stack and automatic topocentric comet stack creation;
  • instant Mean ↔ Sigma-clipped Mean switching without a group rebuild, while transitions involving Sum do rebuild;
  • mean, sum and single-pass sigma-clipped mean;
  • deterministic names and overwrite behavior;
  • NREQUEST, WCSREJ, NCOMBINE, NVALID, saturation maps and provenance;
  • Measure hand-off opens the editable WCCD observation dialog and preserves transactional helper start;
  • Comet/Stars path fields share their named Browse handlers, suppress modal re-entry/duplicate requests and preserve paths on Cancel;
  • the WCCD direct comet-measure button follows positive-aperture and finite-centre state and uses the existing measurement path.

Scientific/Compact and FITS contract

  • old settings without index 14 default to Scientific;
  • settings access uses central getters and preserves unknown trailing fields;
  • new masters, calibrated LIGHTs and all stack methods use the selected writer;
  • unsafe Compact products fall back per file without clipping;
  • actual and requested storage/policy keywords are consistent;
  • auxiliary HDUs remain lossless and compatible with both modes;
  • WCS updates preserve pixel storage and extensions without requantization;
  • weighted stack time and method-specific exposure contracts replace inherited reference-frame cards;
  • manual WCCD accepts supported external integer/float FITS;
  • validate_scientific_compact.py runs on synthetic, paired and source-writer cases.

Performance release contract

  • validate_performance_release.py passes without importing PyQt, NumPy or Astropy;
  • numerical scheduling remains thread-based and does not use Python process pools;
  • no thread-count or memory-limit control appears in the user interface;
  • master median is bounded to four threads; LIGHT calibration, row reprojection and sigma clipping are bounded to two;
  • the prepared-master cache and shared memory allowances remain bounded;
  • astrometry runs no more than two external solver processes with isolated working directories;
  • serial fallback remains available for small jobs, one-CPU systems, insufficient RAM and scheduler initialization failure;
  • Windows/WSL commits remain deterministic and cancellation removes active processes and .solve_* work directories.

WCCD Automatic Helpers integration boundary

A later WStack or WCCD rebase must preserve ephemeris_position.py, cometfinder.py, cometcentroid.py, starmeasure.py, named measurement-mode IDs, observation-local state machines, stale-worker guards, atomic star commits, Slice overlays and the persistent ACF Review ownership rule.

  • Keep interactive candidate detection at 120 arcsec and unattended complete automatic photometry at 30 arcsec. Preserve Primary/Extended classification, post-filter C1..Cn display IDs, zero-candidate review and pan/zoom click suppression.
  • Manual WCCD clicks, automatic candidates and M1 must all call the canonical bounded 2D Gaussian CometCentroid from cometcentroid.py. Do not create a second production centroid definition.
  • Confirmation commits only the accepted centre; it must not start photometry. The accepted centre remains authoritative for state, marker, Slice, aperture measurement and ACF.
  • Preserve the R50 shape tiers, standard-first common-aperture ordering, stable-increment plateau rule, star-count warning/failure levels and scatter decision.
  • Preserve the R55 one-pipeline contract: lightweight projection guard; mask-aware stellar centroid without NaN zero-fill; separate compact-profile FWHM/elongation; robust isolated/lower-distribution FWHM; 3.5× robust-FWHM fallback capped at 4.0×; post-aperture blend gate; strict invalid-aperture policy; checked partial-annulus and balanced_boxes background paths; precise rejection diagnostics.
  • Do not route the R55 stellar profile into Comet Finder or stellar-trail contamination scoring without a separate validation batch.
  • Keep basic WCS matching, catalogue/colour routing, saturation provenance, S/N sequence and individual-zero-point clipping algorithm unchanged unless a separate scientific migration explicitly replaces them.
  • Do not re-add automatic Saturation: text to Results, ICQ or COBS comments. Keep saturation policy/provenance in diagnostics.

Do not decode physical DSLR RAW inside WStack orchestration or reintroduce physical RAW paths into the working index. Keep rawpy optional when a directory contains only usable existing FITS, but require it whenever pending or stale DSLR representations must be materialized automatically.

Do not make astroquery a hard dependency. Do not restore Review invalidation on Stars-image, catalogue or photometric-option changes. Do not introduce the postponed successive-frame astrometry-seed optimisation as part of this documentation/rebase contract. Resolve conflicts by separating numerical backend, GUI controller and commit layer.

R74 automatic DSLR materialization contract

  • Use metadata-only discovery and discovery.extraction_candidates; do not decode RAW in wstack.py.
  • Start _run_raw_materialization_batch(discovery) immediately when pending/stale candidates exist and rawpy/LibRaw is available.
  • Retain RawMaterializationWorker, RawExtractionProgressDialog, cooperative cancellation and atomic temporary-product validation.
  • After resolution, run discovery again and build the session only from fits_only_root_inputs(discovery).
  • Prefer the first newly created/refreshed TG FITS; otherwise select the first verified record.representation_path.
  • Missing rawpy/LibRaw must produce the critical extraction-unavailable path, never direct RAW loading or a bypass choice.
  • Conflicting targets remain separate warnings and must not be overwritten.
  • Resolve all RAW conditions before mutating _base_dir or replacing the open session.

R79 cumulative integration contract

  • Preserve the R75 master-flat correction guard and the one-pass Stack Parameters preflight without changing calibration/stack pixels.
  • Keep camera identity/native geometry separate from validated observation binning.
  • Use one shared physical-scale priority: solved WCS/online calibration, explicit manual override, FITS metadata, then active optics with validated binning.
  • Preserve internal DE440s down-leg light-time and treat Guide/Tycho/Horizons residuals as diagnostics without a universal arcsecond gate.
  • Keep one lossless canonical reference-star collection for AutoStars and Manual Stars.
  • Apply extinction to q_i before a global strict pairwise solution; exactly 0.150 mag fails.
  • Keep user_enabled separate from pairwise_accepted.
  • Reference stars, Process obs. and Af-rho must consume one accepted-ID set and one final solution; do not add a parallel post-success scatter gate.
  • Do not add internal pairwise audit text to the ICQ comment.

R80–R81 hotfix integration contract

  • Do not map the Reference stars Use checkbox directly to user_enabled. It must represent effective inclusion: user_enabled AND quality_eligible AND pairwise_accepted is not False.
  • Keep Reason as the single visible exclusion diagnostic and do not restore the redundant Pairwise status column. Preserve the lossless internal states separately.
  • A direct materialized TG FITS carries RAWREP=T; calibrated and astrometric derivatives carry RAWREP=F.
  • Final stacks must not retain the single-source cards RAWREP, RAWFILE, RAWEXT, RAWMODE, RAWVERS, RAWSIZE or RAWMTIME. Preserve aggregate ancestry in HISTORY, REFIMAGE, NCOMBINE and PROVENANCE.
  • Discovery must treat derived-product markers and safe legacy names as evidence that historical RAW cards are ancestry, not direct-representation identity. Continue supporting legacy valid _TG.fits without RAWREP; continue blocking two genuine direct representations.
  • When a repeated measurement already has the required image role active, update the selector/mode without image activation or redraw. A role mismatch still requires the normal switch and redraw.

Conflict policy

  • Prefer the newer production host architecture and adapt the narrow WStack adapter.
  • Do not overwrite newer KOPR fixes merely to make an older patch apply.
  • Preserve stabilized WStack processing APIs, relative-WCS behavior and the R40 role-specific saturation contract: complete NSATVAL authority in automatic policy, per-image manual overrides, slot-local reset and exact-path threshold hand-off.
  • Preserve the validated R27 photometry flow and R22 automatic resource limits.
  • When two branches disagree on numerical meaning, require reference-data evidence; do not choose the cleaner implementation by style alone.
  • Document every intentional divergence from this contract in the merge report.

R73 saturation and camera integration contract

  • Keep one shared saturation resolver in saturationpolicy.py; do not reintroduce divergent manual, WCCD-helper or Automatic Star Measure calculations.
  • For scalar fallback, preserve automatic/manual base parity, integer image-domain scaling and effective_limit >= finite_image_maximum without copying the observed maximum.
  • Do not turn SATCOMP=NONE or a missing source-frame peak provider into blanket comparison-star rejection. Preserve flat-top and bleeding morphology vetoes.
  • Evaluate camera compatibility before opening the selection dialog. Exact native geometry and a unique supported active-area match must suppress the dialog.
  • Preserve source-geometry × binning reconstruction and the VERIFIED_EXACT/VERIFIED_COMPATIBLE distinction.