work
This commit is contained in:
114
docs/modules/policy/contracts/29-002-streaming-simulation.md
Normal file
114
docs/modules/policy/contracts/29-002-streaming-simulation.md
Normal file
@@ -0,0 +1,114 @@
|
||||
# POLICY-ENGINE-29-002 — Streaming Simulation (Path/Scope) Contract
|
||||
|
||||
Status: Final · 2025-11-23
|
||||
Owners: Policy Guild · SBOM Service Guild · Findings Ledger Guild
|
||||
Working directory: `src/Policy/StellaOps.Policy.Engine`
|
||||
|
||||
## Purpose
|
||||
Define the canonical request/response contract for streaming comparisons between two policy versions with path/scope awareness. Output is explainable per-finding deltas without writes, suitable for Console simulation, Ledger projections, and Advisory AI consumers.
|
||||
|
||||
## Request Schema (JSON)
|
||||
```json
|
||||
{
|
||||
"schemaVersion": "1.0.0",
|
||||
"tenant": "acme", // required
|
||||
"basePolicyRef": "policy://acme/main@sha256:abcd", // required
|
||||
"candidatePolicyRef": "policy://acme/feature@sha256:ef01", // required
|
||||
"subject": {
|
||||
"purl": "pkg:npm/lodash@4.17.21", // or cpe; at least one
|
||||
"packagePath": "lib/isEqual.js", // optional, POSIX
|
||||
"osImage": "ghcr.io/acme/app@sha256:..." // optional
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"filePath": "src/lib/isEqual.js", // required, POSIX
|
||||
"digest": "c1ab...", // optional hex sha256
|
||||
"treeDigest": "7ff0...", // optional Merkle root
|
||||
"pathMatch": "prefix", // exact|prefix|glob
|
||||
"pattern": "src/lib/", // required with pathMatch
|
||||
"confidence": 0.92, // 0..1
|
||||
"depthLimit": 3, // optional int
|
||||
"evidenceHash": "4f9b...", // optional hex (stable over sorted JSON)
|
||||
"ingestedAt": "2025-11-20T00:00:00Z", // optional ISO-8601 UTC
|
||||
"connectorId": "excititor-ghsa" // optional string
|
||||
}
|
||||
],
|
||||
"options": {
|
||||
"sort": "path,finding,verdict", // required enum; default shown
|
||||
"maxFindings": 1000, // optional int
|
||||
"includeTrace": true, // include rule trace fragments
|
||||
"deterministic": true // must be true; rejects false
|
||||
}
|
||||
}
|
||||
```
|
||||
- `schemaVersion` pinned to semantic version; breaking changes require bump.
|
||||
- All string comparisons are case-sensitive except `policy://` refs, which normalise to lowercase host/path.
|
||||
- At least one `target` required. Each target binds evidence to scope (derived from PREP docs `2025-11-20-policy-engine-29-002-prep.md`).
|
||||
|
||||
### Derived Scope Normalisation
|
||||
For each target:
|
||||
- `pathMatch` resolution order: `exact` > `prefix` > `glob`; tie-breaker by higher `confidence`, then lexical `filePath`.
|
||||
- `evidenceHash` = SHA-256 of canonical JSON object with properties sorted ASCII and nulls removed.
|
||||
|
||||
## Response Schema (streamed NDJSON)
|
||||
Each line is a JSON object with stable ordering:
|
||||
```json
|
||||
{
|
||||
"tenant": "acme",
|
||||
"subject": { "purl": "pkg:npm/lodash@4.17.21", "packagePath": "lib/isEqual.js" },
|
||||
"target": {
|
||||
"filePath": "src/lib/isEqual.js",
|
||||
"pattern": "src/lib/",
|
||||
"pathMatch": "prefix",
|
||||
"confidence": 0.92,
|
||||
"evidenceHash": "4f9b..."
|
||||
},
|
||||
"finding": {
|
||||
"id": "GHSA-35jh-r3h4-6jhm",
|
||||
"ruleId": "policy.rules.javascript.unsafe-merge",
|
||||
"severity": "high",
|
||||
"verdict": {
|
||||
"base": "deny", // allow|deny|warn|info|not-applicable
|
||||
"candidate": "warn",
|
||||
"delta": "softened" // added|removed|hardened|softened|unchanged
|
||||
},
|
||||
"evidence": {
|
||||
"locator": { "filePath": "src/lib/isEqual.js", "digest": "c1ab..." },
|
||||
"provenance": { "ingestedAt": "2025-11-20T00:00:00Z", "connectorId": "excititor-ghsa" }
|
||||
}
|
||||
},
|
||||
"trace": [
|
||||
{ "step": "match", "rule": "unsafe-merge", "path": "src/lib/isEqual.js" },
|
||||
{ "step": "decision", "effect": "warn" }
|
||||
],
|
||||
"metrics": {
|
||||
"evalTicks": 128, // deterministic instruction counter
|
||||
"rulesEvaluated": 12,
|
||||
"bindings": 3
|
||||
}
|
||||
}
|
||||
```
|
||||
- Lines are sorted by `target.filePath`, then `finding.id`, then `finding.ruleId`. No other ordering allowed.
|
||||
- Absent optional fields must be omitted (not null).
|
||||
|
||||
## Error Envelope
|
||||
Errors surface as NDJSON line with `type: "error"`:
|
||||
```json
|
||||
{ "type": "error", "code": "POLICY_29_002_SCHEMA", "message": "target[0].pattern required" }
|
||||
```
|
||||
Codes: `POLICY_29_002_SCHEMA`, `POLICY_29_002_UNSUPPORTED_VERSION`, `POLICY_29_002_SCOPE_MISMATCH`, `POLICY_29_002_TOO_MANY_FINDINGS`.
|
||||
|
||||
## Determinism Rules
|
||||
- No wall-clock, RNG, or network access during evaluation.
|
||||
- All hashes use SHA-256 over canonical JSON (sorted properties, UTF-8, no insignificant whitespace).
|
||||
- Stable ordering: request-specified sort or default `path,finding,verdict` defined above.
|
||||
- Trace fragments trimmed to deterministic steps only (no timestamps or hostnames).
|
||||
|
||||
## Validation & Conformance
|
||||
- JSON Schema published at `schemas/policy/29-002-streaming-simulation.schema.json` (to be mirrored in Offline Kit); engines must validate requests before execution.
|
||||
- CI fixtures: `tests/policy/fixtures/29-002-sample-request.json` and `...-response.ndjson` (to be added when engine implements).
|
||||
|
||||
## Compatibility Notes
|
||||
- Extends PREP docs `2025-11-20-policy-engine-29-002-prep.md` and `2025-11-21-policy-path-scope-29-002-prep.md`; supersedes their draft status.
|
||||
- Downstream tasks 29-003..40-002 must consume `target` shape above; any change requires bumping `schemaVersion` and updating sprint risks.
|
||||
|
||||
Reference in New Issue
Block a user