ProtectedapplyPersist predictions back onto the scored records per the request's write-back
OutputMapping, returning a WriteBackOutcome the summary folds in.
No-ops (and reports wroteBack: false) when:
request.dryRun is true (compute-only — predictions are surfaced but never
persisted, matching ProcessRun.DryRun in the substrate).Otherwise, for each Succeeded record (paired 1:1 with records in order),
it resolves the target BaseEntity, validates + applies the mapped
fields, and Save()s. A record whose Save fails is recorded in
failedIndexes so summarize can move it from scored -> failed.
ProtectedassertValidate every mapped target field name against the entity's real fields,
throwing on the first unknown name. The mapping is caller-supplied, so an
unknown field would otherwise silently no-op (or be swallowed by Set); we
reject loudly — the same defensive posture as primaryKeyFilter.
ProtectedbuildBuild the per-record execution context for the processor. A contextUser is
required (server-side scoring is user-scoped); the Action validates it before
delegating, but we also assert here so this runner can't be driven without one
(no silent force-cast of an absent user).
ProtectedentityConvert a loaded entity object into a record ref carrying the preloaded row.
OptionalentityId: stringProtectedfirstFirst string-able value of an object (used to key a record ref from a pk map).
ProtectedmissingA placeholder IArtifactLoader that fails loudly. The Engine package
does not import MJStorage drivers, so the real artifact loader is supplied by
the layer that owns the storage binding (see scoring/seams.ts). Callers that
run this action in production wire deps (with a real artifact loader) into
the runner; tests inject the whole runner.
ProtectedprimaryBuild a Field='value'-style filter from a primary-key object. Each KEY is
validated against the entity's real fields BEFORE interpolation — an unknown
field name is rejected (SQL-injection defense for the untrusted key names,
mirroring how the write-entity-fields action validates field names). Values
are still single-quote-escaped.
Optionalprovider: IMetadataProviderProtectedresolveResolve the deps bundle, building production model-loader + sidecar when absent.
ProtectedresolveResolve an entity id from its name (best-effort; empty when unavailable).
Optionalprovider: IMetadataProviderProtectedresolveResolve an entity + filter into entity-object record refs.
OptionalcontextUser: UserInfoOptionalprovider: IMetadataProviderProtectedresolveResolve a MJ: Lists id into its member records as entity objects. Mirrors
the substrate's ListSource membership contract — a List's members live in
MJ: List Details (which store a composite-key-safe RecordID), NOT in a
backing User View. We (1) read the list's target EntityID/name, (2) read its
member RecordIDs from MJ: List Details, then (3) load those target rows as
entity_objects so the inference processor gets the same preloaded-row shape
as the other scopes.
OptionalcontextUser: UserInfoOptionalprovider: IMetadataProviderProtectedresolveResolve the explicit field-mapping out of the write-back directive. Returns
undefined for the boolean form or a malformed/empty mapping (treated as
"no write-back to apply here").
ProtectedresolveResolve the scope into a set of record refs. Exactly one selector is honored.
OptionalcontextUser: UserInfoOptionalprovider: IMetadataProviderProtectedresolveResolve the strongly-typed target BaseEntity for a record. Most scopes
carry the already-loaded entity on .Record; reuse it (no extra DB round-trip)
when it is a real BaseEntity. Otherwise (id-only records/single refs)
load it from the provider via the record's EntityID + single-column PK using
the request user, so write-back works regardless of how the scope was resolved.
ProtectedresolveResolve a saved-view id into entity-object record refs.
OptionalcontextUser: UserInfoOptionalprovider: IMetadataProviderScore the requested scope and return the run summary.
ProtectedsummarizeFold per-record results into the run summary (counts + predictions /
write-back). The MLModelInferenceProcessor.ProcessBatch contract returns
results 1:1 with the input records in the SAME order, so we pair each
result with its record by walking both together and key each prediction off
the PAIRED record's RecordID (record-correlated, not a bare positional
lookup that would silently misalign if the lengths ever diverged). All three
outcome buckets — Succeeded / Failed / Skipped — are tallied.
wroteBack reflects what was ACTUALLY persisted: it is true only when at
least one record's prediction was Saved. A record that scored but whose
write-back Save failed (its index is in writeBack.failedIndexes) is moved
from the scored bucket into the failed bucket — its prediction did not land.
Predictions are surfaced ephemerally only when nothing was written back.
ProtectedtoConvert a scope records entry (id string or pk object) into a record ref.
ProtectedwriteApply the mapping to ONE record's prediction and Save it. Resolves the target
BaseEntity (reusing the preloaded .Record when present, else loading by id
via the provider), validates every mapped field name against the entity's real
fields (rejecting unknown names — mirroring the PK-name validation in
primaryKeyFilter), sets the resolved values via the config-driven
Set accessor, and Saves. Returns true on a successful Save, false on any
failure (logged) so the caller can reclassify the record as failed.
Resolves a ScoringScope into records and drives the MLModelInferenceProcessor batch path.