PF research library · 7 min read

ISAES method, adapter and execution SDK

r34 documentation · isaes-sdk.md

On this page
  1. Methods and adapters
  2. Executable dependency plans
  3. Released transformation adapters
  4. Prespecified analysis
  5. Isolation, resources and reproducibility

The reporting SDK is 1.0.0. Executable analysis plans use schema 2; retained schema-1 plans and reports remain historical records. These interfaces organize declared scientific calculations. They do not automatically determine whether an arbitrary program implements valid physics or establish a confirmatory study from a research question alone.

Methods and adapters

An analyzer supplies name, kinds and analyze(dataset, value, context), returning one finding and a list of declarative figures. Native extensions also supply a capability declaration. Adapters supply adapt(dataset, value, context) returning {value, metadata, transformations} and register through registry.register_adapter(adapter). They convert representations explicitly; their output can feed another adapter or scientific method.

from pf_reporting.engine import finding
from pf_reporting.sdk import capability

class DeclaredSum:
    name, kinds = "declared_sum", ("scalar",)
    capability = capability(
        name, kinds, version="1.0.0",
        input_schema={
            "type": "object",
            "properties": {"left": {"type": "number"}, "right": {"type": "number"}},
            "required": ["left", "right"], "additionalProperties": False,
        },
        required_metadata=["units"],
        applicability="Two supplied real numbers under one explicitly matched quantity/unit convention.",
        assumptions=["Operands describe the same quantity and compatible units."],
    )

    def analyze(self, dataset, value, context):
        return finding(dataset, self.name, "Sum of the declared operands.",
                       {"sum": value["left"] + value["right"]}), []

def configure(registry, context):
    registry.register(DeclaredSum())

Every capability has an SDK/schema version, method/adapter ID and semantic implementation version, accepted input kinds, input/output schemas, required metadata, applicability description, assumptions, known failure modes and input/output/figure budgets. The contract digest and version are retained per execution. Method findings also pass the platform finding validator, regardless of the extension's output schema. Native private methods and adapters with absent or null required metadata cannot execute. Both SDK conformance helpers enforce the same prerequisite check before invoking an extension. A rejected stage blocks its dependents while valid independent stages remain available. Shared built-in methods retain their documented incomplete-result behavior, and explicit legacy compatibility remains distinct from native conformance. Optional applicable(dataset, value, context) must return an actual boolean eligible and a string reason; an ineligible result is retained as an unavailable decision.

The deliberately small schema dialect supports type, properties, required, items, enum and boolean additionalProperties. Types are object, array, string, number, integer, boolean, null and any. Booleans are not numbers. Unknown schema directives are rejected rather than silently ignored. any is intentional for legacy/polymorphic value encodings: the declared kind's numerical implementation remains responsible for its complete scientific shape and prerequisite checks. Shared method catalogs describe those requirements and limits.

Private registrations always retain private-extension authority, including replacement of a shared analyzer. In addition, whenever any private extension or private scientific layer runs, every calculation in that report is conservatively attributed to the private execution environment, including calls to shared methods. In-process private Python can influence the entire interpreter; mutable registry labels cannot certify independent shared-core execution. A declared capability cannot request stronger authority. conformance(analyzer, dataset, value, context) and adapter_conformance(adapter, dataset, value, context) execute fixtures and returns hashed structural-conformance evidence. It is not independent scientific validation. Extension numerical reference tests and difficult-case tests remain necessary.

Existing extensions without declarations remain supported through the explicit legacy_analyzer_v1 compatibility adapter. Their kind dispatch, finding, evidence, figure and resource checks still run. The capability record discloses that a complete scientific input schema was not supplied; no legacy declaration is silently represented as native conformance.

Executable dependency plans

Without analysis.steps, the engine preserves compatible default dispatch and established finding IDs. With steps, the engine validates the whole graph before execution, derives input dependencies, rejects cycles/unresolved identities and executes a stable topological ordering. Steps are limited to 512 and named inputs to 64 per step. Existing method and figure budgets also apply; work is not silently cropped.

This example converts a retained distance from meters to kilometers and passes the converted value into the scalar method:

{
  "datasets": [
    {"name": "distance", "kind": "scalar", "source": "/data/distance", "units": "m"}
  ],
  "analysis": {
    "question": "What is the retained distance in kilometers?",
    "steps": [
      {"id": "convert", "adapter": "unit_conversion", "dataset": "distance",
       "parameters": {"source_units": "m", "target_units": "km"}},
      {"id": "inspect", "method": "scalar",
       "inputs": {"value": {"step": "convert", "path": "/value"}}}
    ]
  }
}

A step names exactly one method or adapter. A dataset alone is shorthand for its single value input. Explicit inputs maps meaningful names to {dataset: name, path: JSON_pointer} or {step: ID, path: JSON_pointer}. One input named value is passed directly; multiple names form an object. An adapter output exposes /value; a method output exposes its finding, including /values/.... A downstream method therefore consumes an actual retained calculation, not another independent dispatch over the original data.

Optional depends_on adds prerequisites even where no value is consumed. An unavailable, suppressed, insufficient or numerically failed prerequisite blocks its consumers. Successful independent branches remain useful. Explicit-step finding IDs are stage:<step ID>, allowing the same method to run more than once without identity collisions.

metadata supplies explicit derived scientific conventions and cannot replace source/name/authority. parameters reaches adapters/private analyzers as context['step_parameters']; scientific built-ins continue using their documented dataset metadata. Upstream findings are also available under context['upstream_findings'], keyed by prerequisite ID. Private methods still need to validate any cross-input unit/frame/sampling compatibility: the engine does not infer this from similar array shapes.

Every stage retains resolved input references and hashes, implementation contract/version, applicability outcome, effective metadata, output identity and transformations. Adapter outputs are retained as JSON. Finding edges connect predecessor calculations to the consuming stage in the evidence graph. Original source snapshots stay unchanged, including values converted for analysis.

Released transformation adapters

select_inputs accepts either parameters.path for one JSON pointer or parameters.fields, a mapping from output names to source pointers. An optional output_kind declares the resulting representation. Selection records exact source/result hashes and never invents observations, interpolates samples or supplies missing physical semantics.

unit_conversion uses Pint with explicit matching source_units and compatible target_units. A scalar/array converts directly. For a supported series/value envelope, value_path selects /y or /values. Coordinates are not implicitly converted. Uncertainty-bearing or custom structures require their own dimensional adapter; converting a mean while relabeling unchanged error bars is rejected. Frame, basis, sampling identity and originals are preserved.

Typed missingness is processed after original-input retention and before method execution. Only the separately documented explicit missingness policy can exclude rows. Its transformation and selected identities accompany the finding. Row-axis coordinates in derived finding/stage metadata are selected by those same retained row indices; the original input coordinates remain unchanged. Unqualified missingness assumptions mark the result insufficient/descriptive and block downstream inference. Absence of a mask is not permission to impute or discard nonfinite values.

Prespecified analysis

mode: "confirmatory" requests a controlled prespecification check. The definition requires a question, explicit DAG and analysis.confirmatory with nonempty hypotheses, named primary_outcomes, decision_rule, sampling_plan and multiplicity. Primary outcome identities must resolve to locked steps.

The service must supply a verified retained preregistration receipt made before the scientific run, bound to its owner/workflow/version, policy definition and run timing. A receipt identifier can be selected through analysis.pre_registration; a user-controlled verified flag is not accepted as policy. Missing or mismatched prerequisites leave the report incomplete with effective_mode: "exploratory". The useful calculations remain available with their existing scientific scopes.

planning.definition_digest(policy) hashes the scientific policy, excluding presentation/order/language wording and the preregistration receipt selector itself. Changing scientific datasets, assumptions, methods, budgets, seeds, requirements or reference mappings invalidates the binding. The service and engine use this same function on the same effective merged policy. A successful gate records locked_pre_run_plan and effective_mode: "prespecified"; it does not claim independent verification of power, hypotheses, sampling or arbitrary user-defined decision rules. Numerical, statistical and empirical support statuses remain separate.

Isolation, resources and reproducibility

Methods/adapters remain inside the existing isolated reporting-worker boundary. SDK checks do not sandbox Python themselves or grant the worker platform-administration rights. Each capability declares at most 16 MiB input, 8 MiB output and 256 figures; smaller declared bounds are enforced. Overall report/worker memory, time, artifact and retention limits still apply. They are computation budgets, not adjustments to physical models.

New plans and transformations are carried by retained report/analysis artifacts and therefore inherit the report's owner isolation, source dependencies, archive/delete guards and recovery protocol. No independent executable pickle, external callback or remote provider is required. Reanalysis creates a new report; old report bytes and source identities are not rewritten.

The focused tests/test_isaes_planner_sdk.py cases cover topological composition, prior finding consumption, graph rejection, conversion and selection provenance, failure/suppression/resource propagation, native and legacy private methods, authority enforcement, missingness, prespecification refusal and scientific-policy binding. They complement numerical method validation and real-server report persistence tests.