# SCHED-MODELS-20-001 — Policy Engine Run DTOs > Status: 2025-10-26 — **Complete** Defines the scheduler contracts that Policy Engine (Epic 2) relies on for orchestration, simulation, and explainability. DTOs serialize with `CanonicalJsonSerializer` to guarantee deterministic ordering, enabling replay and signed artefacts. ## PolicyRunRequest — `scheduler.policy-run-request@1` Posted by CLI/UI or the orchestrator to enqueue a run. Canonical sample lives at `samples/api/scheduler/policy-run-request.json`. ```jsonc { "schemaVersion": "scheduler.policy-run-request@1", "tenantId": "default", "policyId": "P-7", "policyVersion": 4, "mode": "incremental", // full | incremental | simulate "priority": "normal", // normal | high | emergency "runId": "run:P-7:2025-10-26:auto", // optional idempotency key "queuedAt": "2025-10-26T14:05:00+00:00", "requestedBy": "user:cli", "correlationId": "req-...", "metadata": {"source": "stella policy run", "trigger": "cli"}, "inputs": { "sbomSet": ["sbom:S-318", "sbom:S-42"], // sorted uniques "advisoryCursor": "2025-10-26T13:59:00+00:00", "vexCursor": "2025-10-26T13:58:30+00:00", "environment": {"exposure": "internet", "sealed": false}, "captureExplain": true } } ``` * Environment values accept any JSON primitive/object; keys normalise to lowercase for deterministic hashing. * `metadata` is optional contextual breadcrumbs (lowercased keys). Use it for orchestrator provenance or offline bundle identifiers. ## PolicyRunStatus — `scheduler.policy-run-status@1` Captured in `policy_runs` collection and returned by run status APIs. Sample: `samples/api/scheduler/policy-run-status.json`. ```jsonc { "schemaVersion": "scheduler.policy-run-status@1", "runId": "run:P-7:2025-10-26:auto", "tenantId": "default", "policyId": "P-7", "policyVersion": 4, "mode": "incremental", "status": "succeeded", // queued|running|succeeded|failed|canceled|replay_pending "priority": "normal", "queuedAt": "2025-10-26T14:05:00+00:00", "startedAt": "2025-10-26T14:05:11+00:00", "finishedAt": "2025-10-26T14:06:01+00:00", "determinismHash": "sha256:...", // optional until run completes "traceId": "01HE0BJX5S4T9YCN6ZT0", "metadata": {"orchestrator": "scheduler", "sbombatchhash": "sha256:..."}, "stats": { "components": 1742, "rulesFired": 68023, "findingsWritten": 4321, "vexOverrides": 210, "quieted": 12, "durationSeconds": 50.8 }, "inputs": { ... } // same schema as request } ``` * `determinismHash` must be a `sha256:` digest combining ordered input digests + policy digest. * `attempts` (not shown) increases per retry. * Error responses populate `errorCode` (`ERR_POL_00x`) and `error` message; omitted when successful. ## PolicyDiffSummary — `scheduler.policy-diff-summary@1` Returned by simulation APIs; referenced by CLI/UI diff visualisations. Sample: `samples/api/scheduler/policy-diff-summary.json`. ```jsonc { "schemaVersion": "scheduler.policy-diff-summary@1", "added": 12, "removed": 8, "unchanged": 657, "bySeverity": { "critical": {"up": 1}, "high": {"up": 3, "down": 4}, "medium": {"up": 2, "down": 1} }, "ruleHits": [ {"ruleId": "rule-block-critical", "ruleName": "Block Critical Findings", "up": 1}, {"ruleId": "rule-quiet-low", "ruleName": "Quiet Low Risk", "down": 2} ] } ``` * Severity bucket keys normalise to camelCase for JSON determinism across CLI/UI consumers. * Zero-valued counts (`down`/`up`) are omitted to keep payloads compact. * `ruleHits` sorts by `ruleId` to keep diff heatmaps deterministic. ## PolicyExplainTrace — `scheduler.policy-explain-trace@1` Canonical explain tree embedded in findings explainers and exported bundles. Sample: `samples/api/scheduler/policy-explain-trace.json`. ```jsonc { "schemaVersion": "scheduler.policy-explain-trace@1", "findingId": "finding:sbom:S-42/pkg:npm/lodash@4.17.21", "policyId": "P-7", "policyVersion": 4, "tenantId": "default", "runId": "run:P-7:2025-10-26:auto", "evaluatedAt": "2025-10-26T14:06:01+00:00", "verdict": {"status": "blocked", "severity": "critical", "score": 19.5}, "ruleChain": [ {"ruleId": "rule-allow-known", "action": "allow", "decision": "skipped"}, {"ruleId": "rule-block-critical", "action": "block", "decision": "matched", "score": 19.5} ], "evidence": [ {"type": "advisory", "reference": "CVE-2025-12345", "source": "nvd", "status": "affected", "weight": 1, "metadata": {}}, {"type": "vex", "reference": "vex:ghsa-2025-0001", "source": "vendor", "status": "not_affected", "weight": 0.5} ], "vexImpacts": [ {"statementId": "vex:ghsa-2025-0001", "provider": "vendor", "status": "not_affected", "accepted": true} ], "history": [ {"status": "blocked", "occurredAt": "2025-10-26T14:06:01+00:00", "actor": "policy-engine"} ], "metadata": {"componentpurl": "pkg:npm/lodash@4.17.21", "sbomid": "sbom:S-42", "traceid": "01HE0BJX5S4T9YCN6ZT0"} } ``` * Rule chain preserves execution order; evidence & VEX arrays sort for deterministic outputs. * Evidence metadata is always emitted (empty object when no attributes) so clients can merge annotations deterministically. * Metadata keys lower-case for consistent lookups (`componentpurl`, `traceid`, etc.). * `verdict.status` uses `passed|warned|blocked|quieted|ignored` reflecting final policy decision. ## Compliance Checklist | Item | Owner | Status | Notes | | --- | --- | --- | --- | | Canonical samples committed (`policy-run-request|status|diff-summary|explain-trace`) | Scheduler Models Guild | ☑ 2025-10-26 | Round-trip tests enforce schema stability. | | DTOs documented here and linked from `/docs/policy/runs.md` checklist | Scheduler Models Guild | ☑ 2025-10-26 | Added Run DTO schema section. | | Serializer ensures deterministic ordering for new types | Scheduler Models Guild | ☑ 2025-10-26 | `CanonicalJsonSerializer` updated with property order + converters. | | Tests cover DTO validation and sample fixtures | Scheduler Models Guild | ☑ 2025-10-26 | `PolicyRunModelsTests` + extended `SamplePayloadTests`. | | Scheduler guilds notified (Models, Worker, WebService) | Scheduler Models Guild | ☑ 2025-10-26 | Posted in `#scheduler-guild` with sample links. | --- *Last updated: 2025-10-26.*