Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
30f3fa2
refactor: remove duplicated POD scientific modules
VPRamon Oct 4, 2026
bd031fc
fix: complete service architecture consolidation
VPRamon Oct 4, 2026
e4880f7
fix: reject unused real-data inputs
VPRamon Oct 4, 2026
a0868d9
refactor: remove scientific service reexports
VPRamon Oct 4, 2026
bf580ac
refactor: import scientific types from siderust
VPRamon Oct 4, 2026
c851b61
test: cover synthetic input provenance
VPRamon Oct 4, 2026
b76c287
refactor: remove legacy spice reexports
VPRamon Oct 4, 2026
a20d4ab
docs: align lambert module naming
VPRamon Oct 4, 2026
db18e7f
docs: rewrite unreleased changelog
VPRamon Oct 4, 2026
21fcf55
docs: refresh examples and doctests
VPRamon Oct 4, 2026
703390b
docs: align DAF SPK ownership
VPRamon Oct 4, 2026
560848f
docs: update SPK coverage ADR
VPRamon Oct 4, 2026
b08ce44
docs: update error handling ADR
VPRamon Oct 4, 2026
c2757b7
docs: align LISA ADR with siderust POD
VPRamon Oct 4, 2026
f0cf87c
docs: align module naming with spod
VPRamon Oct 4, 2026
8b71068
docs: align module naming with spod
VPRamon Oct 4, 2026
0657bbc
docs: align module naming with spod
VPRamon Oct 4, 2026
18c2247
docs: align module naming with spod
VPRamon Oct 4, 2026
0daca5f
docs: align module naming with spod
VPRamon Oct 4, 2026
71717cc
ci: tighten legacy namespace guard
VPRamon Oct 4, 2026
62085eb
ci: reject all legacy siderust-pod namespaces
VPRamon Oct 4, 2026
11b6609
style: apply rustfmt output
VPRamon Oct 4, 2026
0767d08
style: apply rustfmt import order
VPRamon Oct 4, 2026
b914633
style: format spice docs
VPRamon Oct 4, 2026
a46c7f7
docs: migrate SPICE doctest to siderust parser API
VPRamon Oct 4, 2026
0fd2dd8
fix: persist REST request provenance
VPRamon Oct 4, 2026
045ab39
fix: reject unsupported third-body force
VPRamon Oct 4, 2026
ca20946
test: reject unsupported third-body force
VPRamon Oct 4, 2026
db13590
docs: align package metadata with service architecture
VPRamon Oct 4, 2026
a30bf94
docs: clarify scientific ownership
VPRamon Oct 4, 2026
9d3ab54
docs: record provenance and force validation fixes
VPRamon Oct 4, 2026
c2a8051
docs: mark third-body force as unsupported
VPRamon Oct 4, 2026
3f11db3
ci: verify persisted REST request provenance
VPRamon Oct 4, 2026
2484278
style: apply rustfmt to force validation
VPRamon Oct 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 64 additions & 16 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -127,24 +127,82 @@ jobs:
- uses: Swatinem/rust-cache@v2
- run: cargo build --bins
- run: cargo run --quiet --bin spod -- --help
- name: Start REST server and query health
- name: Start REST server and verify job provenance
shell: bash
run: |
set -euo pipefail
port=18080
cargo run --quiet --bin spod-rest &
server_pid=$!
trap 'kill "$server_pid" 2>/dev/null || true' EXIT

ready=false
for attempt in {1..30}; do
if curl --fail --silent "http://127.0.0.1:${port}/healthz"; then
exit 0
if curl --fail --silent "http://127.0.0.1:${port}/healthz" >/dev/null; then
ready=true
break
fi
sleep 1
done
if [[ "$ready" != true ]]; then
echo "REST server did not become ready" >&2
exit 1
fi

job_id="$(
curl --fail --silent \
-H 'content-type: application/json' \
-d '{"enable_j2":false}' \
"http://127.0.0.1:${port}/jobs" \
| python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])'
)"

status_json=
for attempt in {1..60}; do
status_json="$(curl --fail --silent "http://127.0.0.1:${port}/jobs/${job_id}")"
state="$(printf '%s' "$status_json" | python3 -c 'import json,sys; print(json.load(sys.stdin)["state"])')"
if [[ "$state" == "finished" ]]; then
break
fi
if [[ "$state" == "failed" ]]; then
echo "$status_json" >&2
exit 1
fi
sleep 1
done
echo "REST server did not become ready" >&2
exit 1

state="$(printf '%s' "$status_json" | python3 -c 'import json,sys; print(json.load(sys.stdin)["state"])')"
[[ "$state" == "finished" ]]

run_dir="$SPOD_REST_OUT/$job_id"
request_path="$run_dir/request.json"
manifest_path="$run_dir/run.manifest.json"
test -f "$request_path"
test -f "$manifest_path"

python3 - "$manifest_path" "$request_path" <<'PY'
import hashlib
import json
import pathlib
import sys

manifest_path = pathlib.Path(sys.argv[1])
request_path = pathlib.Path(sys.argv[2])
manifest = json.loads(manifest_path.read_text())
payload = request_path.read_bytes()

assert len(manifest["inputs"]) == 1
input_ref = manifest["inputs"][0]
assert pathlib.Path(input_ref["path"]) == request_path
assert input_ref["kind"] == "configuration"
assert input_ref["bytes"] == len(payload)
digest = hashlib.sha256(payload).hexdigest()
assert input_ref["sha256"] == digest
assert manifest["config_sha256"] == digest
PY
env:
SPOD_REST_BIND: 127.0.0.1:18080
SPOD_REST_OUT: ${{ runner.temp }}/spod-rest-smoke

docker:
name: Docker image and compatibility smoke tests
Expand All @@ -160,7 +218,7 @@ jobs:
run: |
set -euo pipefail
cleanup() {
docker rm -f spod-ci-default spod-ci-new spod-ci-legacy >/dev/null 2>&1 || true
docker rm -f spod-ci-default spod-ci-new >/dev/null 2>&1 || true
}
trap cleanup EXIT

Expand All @@ -183,16 +241,6 @@ jobs:
sleep 1
done

docker run -d --name spod-ci-legacy -p 18081:8080 \
-e SIDERUST_POD_REST_BIND=0.0.0.0:8080 spod:ci >/dev/null
for attempt in {1..30}; do
if curl --fail --silent "http://127.0.0.1:18081/healthz"; then
exit 0
fi
[[ "$attempt" -eq 30 ]] && exit 1
sleep 1
done

coverage:
name: Coverage
runs-on: ubuntu-latest
Expand Down
201 changes: 52 additions & 149 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,162 +1,65 @@
# Changelog

All notable changes to this workspace are documented here. Format follows
All notable changes to this repository are documented here. Format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project
follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html) per
crate (independent versioning).
follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Migration

* Renamed the project to `spod`, including the Cargo package/library, the
`spod` and `spod-rest` binaries, repository URLs, and Docker image examples.
* Renamed REST configuration variables to `SPOD_REST_BIND` and
`SPOD_REST_OUT`. The former `SIDERUST_POD_REST_BIND` and
`SIDERUST_POD_REST_OUT` names remain supported as deprecated fallbacks.
Update scripts and deployments to use the `SPOD_*` namespace.
* Raised the MSRV to Rust 1.89 because the released `siderust` dependency
graph requires it. The Docker builder and CI MSRV job use the same version.
* Split CI into blocking format, build, lint, feature-test, doctest,
documentation, service-smoke, Docker, coverage, guard, MSRV, and
supply-chain jobs.

### Workspace
### Changed

* Consolidated the former multi-crate implementation into the current
`spod` package and migrated its Siderust ecosystem dependencies to
released crates.io versions.
* Added a `[workspace.lints]` table (`unsafe_code = forbid`,
`missing_docs = deny`, `clippy::all = deny`,
`clippy::{todo,unimplemented,dbg_macro} = deny`,
`rustdoc::broken_intra_doc_links = deny`) and wired every workspace
member to inherit it via `[lints] workspace = true`. The full
workspace now builds clean under `cargo clippy --all-targets -- -D
warnings` and `cargo doc -- -D rustdoc::broken-intra-doc-links`.
* CI scripts under `scripts/`:
* `check_no_todos.sh` (gates `todo!`/`unimplemented!`/TODO/FIXME/XXX
comments — currently passes with zero hits in the source tree).
* `snapshot_public_api.sh` (commits `crates/<crate>/api.snapshot`
baselines for all 14 library crates via `cargo public-api`; runs
in `--update` mode locally and as a diff in CI).
* `.github/workflows/ci.yml`: extended the `build-test` job with
default / no-default-features / all-features test matrices and the
new `todo-sweep` step, and added an advisory `public-api` job that
diffs against the committed snapshots.
* Per-crate library-hygiene baseline: every flagged `missing_docs`
warning across `siderust-{tle,dynamics,lambert,sgp4,spice}` and
`siderust-pod-{core,dynamics}` was resolved (120 warnings → 0)
with informative rustdoc covering items, fields, variants, and
units/encoding.
* `siderust-pod-estimation::WlsSolverError`: new `Other(String)` variant
+ `WlsSolverError::other()` constructor so callers can wrap upstream
propagation/STM failures during normal-equation assembly.
* `siderust-pod-service::pipeline`: scoped `#[allow(deprecated)]`
around the legacy `finite_diff_stm_series` call site (upstream notes
explicitly preserve the series API for batch-LS use, since
`propagate_stm` only returns Φ at the terminal epoch).
- Renamed the project and Cargo package to `spod`, including the `spod` and
`spod-rest` binaries, repository metadata, Docker examples, and REST
configuration variables.
- Consolidated `spod` into a service/application built on the canonical
`siderust 0.12` POD APIs instead of maintaining a second POD scientific
implementation.
- The synthetic POD pipeline now uses
`siderust::pod::propagation::VariationalPropagator` for aligned per-epoch
states and state-transition matrices.
- CLI runs hash the executed YAML configuration with Siderust `DatasetRef`;
REST jobs hash a deterministic serialized request representation.
- The service now rejects every real-data input while real-data ingestion is
not implemented, preventing unused files from being recorded as scientific
provenance.
- The synthetic-only configuration rejects unsupported third-body gravity
instead of silently ignoring the requested force model.
- REST jobs persist their canonical request document inside the run directory
before hashing it, so every manifest input path names a real auditable file.
- Reusable scientific types and low-level SPICE parser APIs are imported
directly from Siderust rather than re-exported through compatibility
façades.
- Raised the MSRV to Rust 1.89 to match the released Siderust dependency graph.

### Fixed

* `siderust-pod-observations::gnss::sagnac_km`: convert the typed
`OMEGA_EARTH_RAD_S` (`InverseSeconds`) to its scalar value before the
km/(km·s⁻¹) division, restoring `cargo check` after the upstream
`siderust` typed-constant migration.
* `siderust-pod-service`: pipeline & synthetic-arc generation updated
to the new five-argument `rk4_propagate_series`/`finite_diff_stm_series`
signatures (which now take a `&DynamicsContext` and return
`Result<_, DynamicsError>`); errors are wrapped through
`WlsSolverError::Other`. EKF replay test migrated to the
`EncodedTime`-based `OrbitState::new` and to
`StateCovariance::diagonal_from_sigmas` with typed
`Kilometers`/`KmPerSecond` arguments.
- Run manifests now contain a real configuration SHA-256, canonical hashed
dataset references, and RFC3339 start/finish timestamps.
- Removed unsupported synthetic carrier-ambiguity injection from the MVP,
whose estimated parameters are the six state components plus receiver
clock.
- Synthetic state/STM indexing explicitly maps epoch zero to the identity STM
and subsequent epochs to the corresponding cumulative variational STM.
- Runtime diagnostics no longer reference deleted implementation-plan
documents.

### Added

* `siderust_pod_io::oem::{read_oem, OemFile, OemSegment}` — CCSDS OEM
KVN reader with full round-trip support (`write_oem` ↔ `read_oem`),
multi-segment parsing, tolerant skipping of `COVARIANCE_*` and
`MAN_*` sub-blocks, structured `PodIoError::Format` diagnostics with
line numbers, and a documented inverse `iso8601_to_jd`. Closes the
long-standing `todo!()` in `tests/functional/oem.rs` (replaced by a
proper round-trip functional test).

* `siderust-pod-core::manifest`: shipped JSON-Schema (draft-07) for the
`RunManifest` and QC report formats as `schema/run_manifest.v1.json` and
`schema/qc.v1.json`, embedded at compile time as `RUN_MANIFEST_SCHEMA_V1`
and `QC_SCHEMA_V1` constants. Two new tests assert the embedded manifest
schema matches the actual serialised field set and that the QC schema
declares `schema = "qc.v1"`. Total `siderust-pod-core` test count: 8.
* `siderust-pod-qc::orbit_compare`: RTN comparison adapter migrated to the
`siderust 0.7` API (`OrbitState::epoch_jd()`, `LocalOrbitalFrame::try_from_state`).
Existing tests still pass.

### Changed

* `siderust-pod-{io,observations,estimation,qc,products}`: migrated to the
upstream `siderust` API rename (`OrbitState.epoch_tt → epoch`,
`OrbitState::new(JulianDate, …) → OrbitState::new_at_jd(JulianDate, …)`,
`StateCovariance::from_stddevs([f64;3], [f64;3]) →
StateCovariance::diagonal_from_sigmas([Kilometers;3], [KmPerSeconds;3])`,
`LocalOrbitalFrame::from_state → try_from_state`). Public APIs of pod
crates unchanged.

* Reserved sibling reusable crates under `rust/`:
`siderust-dynamics` (variational/STM, force-model composition,
thrust-arc physics), `siderust-sgp4` (SGP4 propagator producing typed
TEME states), `siderust-tle` (TLE/3LE/OMM parser),
`siderust-spice` (DAF/SPK reader extending `siderust::data::{daf,spk}`
to Type 3/9/13), and `siderust-lambert` (0/N-revolution Lambert
solver). Each crate ships with a README defining ownership boundaries
and a `CHANGELOG.md`. Implementation lands in plan Phases 3–5; the
scaffolds today only declare crate identity, dependencies on `qtty` /
`tempoch` / `affn[astro]` / `cheby` / `siderust`, an `AGPL-3.0-or-later`
license, and `#![forbid(unsafe_code)]`. None of these crates depend on
`siderust-pod-*`.
* `siderust-pod-core`: new crate scaffolding the POD domain primitives
referenced by the existing design plan (`error::PodError`,
`dataset::DatasetRef`, `manifest::RunManifest` with deterministic JSON,
`parameter::{ParameterKind, Parameter, ParameterOrdering}`,
`covariance::ParameterCovariance` with symmetry/diagonal validators,
`providers::{EphemerisProvider, EarthOrientationProvider,
FrameTransformProvider}` traits). Closes the long-standing gap where
the workspace plan and changelog referenced this crate but it did not
exist on disk.
* `siderust-pod-dynamics`: new crate scaffolding the POD-specific
dynamics composition layer (`force_config::ForceModelConfig`,
`thrust_arc::ThrustArcConfig` emitting estimable parameters,
`process_noise::ProcessNoiseConfig`). Numerical propagation and
analytic STM remain in `siderust` (today) and the future reusable
`siderust-dynamics` crate; this layer only owns POD-specific
composition.
* `siderust-pod-dynamics`: cannonball solar-radiation-pressure force
(`forces::CannonballSrp`) and exponential-density atmospheric drag
(`forces::ExponentialDrag`). (audit fixes A-09, A-10)
* `siderust-pod-core`: `PodError::NotImplemented` variant for
capabilities that are scoped to a future milestone.
* `siderust-pod-service`: integration test verifying the runner refuses
real-input runs with a structured `Unsupported` error rather than
silently propagating-and-ignoring the inputs (audit fix A-03).
* Top-level `SECURITY.md`, `SUPPORT.md`, `CHANGELOG.md`, and
`deny.toml` for cargo-deny gating in CI.
* `crates/siderust-pod-dynamics/benches/propagation.rs`: criterion
bench skeleton for two-body and two-body+J2 RK4 propagation
(audit fix A-13 starter).

### Changed

* `siderust-pod-service::runner::run`: when real inputs are supplied
the runner now returns
`std::io::ErrorKind::Unsupported` carrying the
`PodError::NotImplemented` message instead of silently invoking the
legacy propagation-only stub. This closes audit finding C-01.

### Documentation

* `spod/docs/architecture/dependency-rules.md`: extended forbidden
edges to include reusable foundational crates (`qtty`, `tempoch`,
`affn`, `cheby`, `siderust`) which must not depend on any
`spod-*` crate. Enforced by `scripts/check_dep_graph.sh`.
* `plan.md` §13 records the post-M7 technical audit (six 🔴 critical,
nine 🟠 high, ten 🟡 medium, five 🟢 low findings) and the M8–M12
remediation roadmap.
- End-to-end synthetic service coverage from `RunConfig` through Siderust
propagation/estimation to orbit, residual, QC, and run-manifest artifacts.
- Regression coverage ensuring every unsupported real-data input is rejected
rather than hashed and ignored.
- CI guards for formatting, Clippy, feature combinations, documentation,
dependency direction, legacy project namespaces, MSRV, coverage, Docker,
REST/CLI smoke tests, and supply-chain policy.

### Removed

- Duplicated local POD scientific modules for dynamics, observations,
estimation, products, quality control, parameters, covariance, and run
metadata.
- Deprecated finite-difference STM compatibility code and its deprecation
allowance.
- Deprecated pre-rename REST environment aliases.
- Obsolete compatibility reexports, stale architecture documents, and
dependencies used only by the removed scientific implementation.
Loading
Loading