Skip to content

lunartrace.science.study

STUDY plane: run a MethodContract against SOURCE_FROZEN inputs and emit a receipt.

The DIRECT plane is lunartrace.science.slope_quality.evaluate over in-memory inputs. The STUDY plane adds custody: inputs are resolved from a SourceWitness manifest into the content-addressed store by sha256, each byte stream is re-verified before use, and the run writes a deterministic canonical result plus a non-deterministic ExecutionReceipt describing how the result was produced.

Authority: the method is defined by the lunartrace.slope_quality/v1 MethodContract; this module only executes it and records custody. Its output is a computation record. Emitting a receipt or a projection does not by itself grant any scientific standing; the standing label is supplied by the caller (see build_projection) and is validated, not assigned, here. Sample validity and the missing-is-never-zero rule belong to lunartrace.science.slope_quality, which this module calls.

Spec: docs/r2/SCIENCE-API-DECISION.md. Usage:

python -I -m lunartrace.science.study run \
--manifest research/sources/pgda78-site01.manifest.json \
--store <store>/lt-source-store --out <dir>

class

FrozenInput

One SOURCE_FROZEN input resolved from the content-addressed store.

NameTypeDescription
rolestrRole name from the manifest entry (for example "slope" or "roi_shp").
namestrOriginal file name recorded in the manifest.
sha256strHex SHA-256 digest that the blob was verified against.
pathpathlib.PathLocation of the verified blob inside the store (store / cas).

function

def load_manifest(path: Path) -> dict[str, object]

Read and check a lunartrace.source_witness/v0 manifest.

NameTypeDescription
pathpathlib.PathManifest JSON file, decoded as UTF-8.
  • dict[str, object] — The parsed manifest object, unmodified.
  • lunartrace.science.slope_quality.MethodInputError — If the file is not a JSON object with schema lunartrace.source_witness/v0, or if its status is not SOURCE_FROZEN.

Only the schema and status are checked here. Blob integrity is checked later by resolve_inputs.

function

def resolve_inputs(manifest: Mapping[str, object], store: Path) -> dict[str, FrozenInput]

Map manifest roles to verified content-addressed blobs.

NameTypeDescription
manifestMapping[str, object]A checked manifest as returned by load_manifest. Its entries list is read.
storepathlib.PathRoot of the content-addressed store; each entry’s cas path is taken relative to it.
  • dict[str, FrozenInput] — One entry per role, keyed by role name.
  • lunartrace.science.slope_quality.MethodInputError — If entries is not a list; if a dict entry lacks a string role, cas, sha256 or name; if a blob is missing; if a blob’s SHA-256 differs from the manifest; or if the roles slope, slope_rms, roi_shp, roi_shx, roi_dbf or roi_prj are not all present.

Entries that are not JSON objects are skipped silently. Every blob is re-hashed in full before use, so a result built from this mapping cannot silently use altered bytes.

function

def run_study(
manifest_path: Path,
store: Path,
out_dir: Path,
taus: Sequence[float] = sq.DEFAULT_TAUS,
repo: Path | None = None,
) -> dict[str, object]

Execute lunartrace.slope_quality/v1 from the CAS and write its outputs.

NameTypeDescription
manifest_pathpathlib.PathA SOURCE_FROZEN source-witness manifest.
storepathlib.PathContent-addressed store holding the manifest’s blobs.
out_dirpathlib.PathOutput directory, created if missing. Existing files with the same names are overwritten.
tausSequence[float]RMS thresholds, in degrees, passed to the method. Defaults to lunartrace.science.slope_quality.DEFAULT_TAUS. Default sq.DEFAULT_TAUS.
repopathlib.Path or NoneRepository root used for the source and environment records. If None, it is inferred from this file’s location. The records use the root only if it is this package’s own checkout; otherwise source.commit, source.dirty and environment.uv_lock_sha256 are recorded as None (UNKNOWN). Default None.
  • dict[str, object] — The receipt written to receipt.json.
  • lunartrace.science.slope_quality.MethodInputError — Propagated from manifest checks, blob verification or the method’s own input checks.

Two files are written to out_dir. result.json is canonical JSON: the same inputs and taus give the same bytes. receipt.json is not deterministic; it holds timestamps, git state and environment versions. The receipt status is METHOD_EXECUTED, which is an execution record and is not a scientific standing. Inputs are read only from the local store and the network is not used.

function

def projection_provenance(manifest: Mapping[str, object]) -> tuple[str, str]

Return the citation and CRS that every cross-client projection carries.

NameTypeDescription
manifestMapping[str, object]A checked source-witness manifest.
  • tuple[str, str] — (citation, crs_proj4). The citation is the first citations entry formatted as "<text> doi:<doi>". The CRS is the manifest’s native_semantics.crs with any text from the first " (" onward removed; it is passed through as a string and not parsed here.
  • lunartrace.science.slope_quality.MethodInputError — If there is no first citation dict, or no string native CRS.

function

def check_reproduces(result_sha256: str, witness: object, reference: Path) -> None

Check that a result hash reproduces a committed slope_quality/v0 projection.

NameTypeDescription
result_sha256strHex SHA-256 of the canonical result bytes being labelled.
witnessobjectThe id of the manifest the result came from.
referencepathlib.PathA committed lunartrace.projection.slope_quality/v0 document.
  • lunartrace.science.slope_quality.MethodInputError — If the reference names another witness, has no result_sha256, or records a different hash.

This is the comparison that earns NUMERIC_REPRODUCED. It is not a scientific admission.

function

def build_projection(
result_path: Path,
manifest_path: Path,
standing: str,
reproduces: Path | None = None,
) -> dict[str, object]

Wrap canonical result bytes in the typed cross-client projection.

NameTypeDescription
result_pathpathlib.PathA result.json written by run_study. Its bytes must already be canonical.
manifest_pathpathlib.PathThe source-witness manifest the result came from. Its citation, CRS and id are copied into the projection.
standingstrStanding label recorded on the projection. It is validated by the projection model (lunartrace.projection.Standing). "NUMERIC_REPRODUCED" is recorded only when the result reproduces reproduces, and "SCIENCE_VERTICAL_QUALIFIED" is refused.
reproducespathlib.Path or NoneA committed slope_quality/v0 projection whose result_sha256 the result must match. Required for "NUMERIC_REPRODUCED". See check_reproduces. Default None.
  • dict[str, object] — The projection as JSON-compatible data, using the model’s aliased field names.
  • lunartrace.science.slope_quality.MethodInputError — If the result bytes are not canonical JSON, the manifest lacks a citation or native CRS, or the requested standing is not earned by a matching reference.
  • pydantic.ValidationError — If the projection model rejects the assembled document, for example an unknown standing label.

The result’s SHA-256 is computed over the exact bytes read from result_path.

function

def main(argv: Sequence[str] | None = None) -> int

Command-line entry point with the run and project subcommands.

NameTypeDescription
argvSequence[str] or NoneArguments without the program name. If None, sys.argv is used. Default None.
  • int — 0 on success. Errors are not converted to exit codes here; they propagate.

run takes --manifest, --store, --out and optional repeated --tau. project takes --result, --manifest, --standing, --out and --reproduces, which NUMERIC_REPRODUCED requires. Method and input errors propagate as exceptions; argparse exits on usage errors.