Architecture Upgrade Plan#
This document tracks the design issues identified during the September 2026 architecture review. Keep the status and notes current as each item is fixed.
Status values:
Open: not started.In progress: actively being changed.Done: implemented and verified.Deferred: intentionally postponed with a reason.
Items#
DoneRuntime API trust boundaryThe runtime API currently accepts local filesystem paths for pipeline YAML and custom source/sink class import paths from client payloads. This is acceptable only for a trusted local helper. A network-facing service needs path allowlists, disabled or registry-backed custom imports, and an explicit auth model.
Outcome:
RuntimePolicynow centralizes API trust-boundary settings. The defaulttrustedpolicy preserves local-helper behavior, while therestrictedpolicy disablespipeline.yaml_path, disables process-step filesystem discovery outside the curated/explicit registry, and disables arbitrary custom source/sink imports throughkwargs.class_path. Optional read/write roots constrain file-backed source/sink paths andwrite_hdfoutput paths; restricted mode requires those roots for file-backed IO. Lightweight limits are available for session count, pipeline YAML payload size, and buffer upload size. Hard CPU/memory isolation and authentication remain deployment/container responsibilities.DonePartial rerun invalidation contractDirty-step selection should be based on an explicit per-step dependency contract instead of server-owned config heuristics. Steps should declare which source refs they read and which
ProcessingDatakeys they read or write.Outcome:
ProcessStep.dependency_contract()now provides the partial-rerun dependency contract. Boundary steps declare exact contracts for source loading, source registration, sink registration, and sink exports. Runtime dirty-step detection now consumes the contract and expands matching seeds to downstream descendants.DonePipeline scheduler ownershipPipelinecurrently inherits fromgraphlib.TopologicalSorter, which mixes graph data with one-shot scheduler state. Prefer keepingPipelineas the graph/spec holder and creating a fresh scheduler per execution.Outcome:
Pipelineno longer subclassesTopologicalSorter. It owns the graph and exposescreate_scheduler()for fresh per-use schedulers. Existing manual scheduling helpers remain as compatibility wrappers, and the shared runner now uses a local scheduler per job.DoneProcess-step configuration schemaStep configuration is split between
CONFIG_KEYSandProcessStepDescriber.arguments. Constructor-provided configuration is overwritten during initialization, and documented argument types are not centrally validated.Outcome:
ProcessStepnow builds a unified effective configuration schema from sharedCONFIG_KEYSand module-specificProcessStepDescriber.arguments. Constructor-provided configuration, pipeline YAML/spec configuration, and manual config updates all preserve user values and validate unknown keys plus top-level value types through the same path. YAML sequences are normalized to Python tuples for configuration keys that explicitly declaretupleas their runtime type.CONFIG_KEYSremains the compatibility location for shared/base options.DoneExplicit in-place step executioncalculate()is documented as returning a mapping thatexecute()merges, but several modules mutate sharedProcessingDatabefore that merge. Partial rerun rollback and trace semantics would be clearer with an explicit commit boundary.Outcome: MoDaCor now treats in-place
ProcessingDatamutation as the authoritative process-step contract.execute()no longer merges returned mappings back intoProcessingData; it stores them only as optionalproduced_outputsbookkeeping. Built-in steps that previously relied on the merge path now updateself.processing_datadirectly. Runtime rollback stays at session scope through partial snapshots and full-rerun fallback, avoiding per-step full data copies in the normal execution path.DoneCore data metadata contractsDataBundleis currently a permissivedict, andBaseDataarithmetic inherits axes/rank/weights from the left operand. Mixed-axis operations need an explicit metadata policy.Outcome:
DataBundlenow accepts only non-empty string keys mapped toBaseDatavalues while preserving existing construction styles such asDataBundle(signal=bd).BaseDatabinary arithmetic now uses a cheap structural metadata policy: Pint still handles unit compatibility and unit algebra, while MoDaCor rejects rank and axes conflicts that can be checked without scanning full axis arrays. Metadata-neutral scalar factors and correction maps keep the primary data metadata intact, and simultaneous array-valued weights retain the existing left-operand inheritance behavior.
Update Log#
2026-09-01: Created tracker and started item 2.
2026-09-01: Completed item 1 with restricted runtime policy, locked registry support, IO path roots, custom IO import controls, and lightweight API limits.
2026-09-01: Completed item 2. Verified with targeted
.venv-devpytest runs.2026-09-01: Started item 3.
2026-09-01: Completed item 3. Verified with full
.venv-devpytest run.2026-09-01: Started item 4.
2026-09-01: Completed item 4. Verified with full
.venv-devpytest run.2026-09-01: Fixed item 4 YAML tuple compatibility regression.
2026-09-01: Completed item 5. Verified with full
.venv-devpytest run.2026-09-01: Completed item 6. Added cheap BaseData metadata compatibility checks and DataBundle entry validation.