From 529b5e89ab50fd2aede96c1a306d2c4b1f0ff038 Mon Sep 17 00:00:00 2001 From: Rohit Mulani Date: Tue, 30 Jun 2026 16:56:03 +0400 Subject: [PATCH 1/2] Add vendor-risk-review runx skill --- skills/vendor-risk-review/SKILL.md | 51 +++++ skills/vendor-risk-review/X.yaml | 142 ++++++++++++ .../evidence/local-harness.json | 17 ++ .../approve-with-conditions-sla-gap.json | 24 ++ .../fixtures/missing-policy-stop.json | 10 + ...reject-unbounded-liability-data-floor.json | 24 ++ skills/vendor-risk-review/run.mjs | 207 ++++++++++++++++++ 7 files changed, 475 insertions(+) create mode 100644 skills/vendor-risk-review/SKILL.md create mode 100644 skills/vendor-risk-review/X.yaml create mode 100644 skills/vendor-risk-review/evidence/local-harness.json create mode 100644 skills/vendor-risk-review/fixtures/approve-with-conditions-sla-gap.json create mode 100644 skills/vendor-risk-review/fixtures/missing-policy-stop.json create mode 100644 skills/vendor-risk-review/fixtures/reject-unbounded-liability-data-floor.json create mode 100644 skills/vendor-risk-review/run.mjs diff --git a/skills/vendor-risk-review/SKILL.md b/skills/vendor-risk-review/SKILL.md new file mode 100644 index 000000000..19fe23835 --- /dev/null +++ b/skills/vendor-risk-review/SKILL.md @@ -0,0 +1,51 @@ +--- +name: vendor-risk-review +version: "0.1.0" +description: Relationship-level vendor risk judgment that records approved-with-conditions or rejected decisions against a supplied trust policy. +--- + +# vendor-risk-review + +`vendor-risk-review` judges a vendor relationship against a supplied trust policy. It reads bounded contract text, vendor context, policy thresholds, and prior risk record state, then emits a relationship decision plus a data-store append event shape. + +The skill is not a clause redliner and does not send stakeholder notifications. Its durable seam is the vendor risk record append through `registry:runx/data-store@0.1.2`; any notify step belongs to a separate governed `send-as` run. + +## Inputs + +- `contract_text`: bounded contract excerpt or summary. +- `vendor_context`: `{ vendor_ref, history, industry }`. +- `policy`: `{ required_sla_terms, max_liability, data_handling_floor, termination_window, policy_id, created_at }`. +- `data_source_ref`: public-safe source reference for the contract packet. +- `store_id`: pinned data-store identifier. +- `prior_risk_record`: optional `{ version }` read projection result. + +## Decision rules + +- Approve with conditions when the relationship is usable but recoverable gaps remain, such as an SLA floor miss or termination notice gap. +- Reject when liability is unbounded or above the policy cap, or when required data-handling evidence falls below the policy floor. +- Stop before writing when policy fields are missing, vendor identity is ambiguous, or prior state is unreadable. +- Every condition or rejection reason is grounded in a named supplied policy field. + +## Output + +The default runner emits: + +- `decision`: `{ approved, rejected, reason, conditions, policy_id, created_at }`. +- `risk_record_event`: durable vendor risk event payload. +- `data_store_append_event`: CAS append evidence with store id, aggregate id, idempotency key, before/after version, and package ref. +- `record_written`: true when a complete policy and vendor identity allow the event. +- `escalation`: null for sealed decisions, `needs_human` only for stopped or ambiguous inputs. + +## Validation + +Run from the repository root: + +```bash +runx harness ./skills/vendor-risk-review +``` + +Expected cases: + +- `approve-with-conditions-sla-gap` +- `reject-unbounded-liability-data-floor` +- `missing-policy-stop` diff --git a/skills/vendor-risk-review/X.yaml b/skills/vendor-risk-review/X.yaml new file mode 100644 index 000000000..35b36b943 --- /dev/null +++ b/skills/vendor-risk-review/X.yaml @@ -0,0 +1,142 @@ +skill: vendor-risk-review +version: "0.1.0" + +catalog: + kind: skill + audience: public + visibility: public + role: context + +harness: + cases: + - name: approve-with-conditions-sla-gap + runner: judge + inputs: + contract_text: "Vendor provides SOC 2 Type II controls, encryption at rest, a signed DPA, liability capped at USD 50000, and 30 day termination notice. Service uptime commitment is 99.0 percent." + vendor_context: + vendor_ref: "vendor:acme-mailops" + history: "Existing email operations vendor with one renewal cycle." + industry: "email operations" + policy: + required_sla_terms: + min_uptime_percent: 99.9 + support_response_hours: 24 + max_liability: 100000 + data_handling_floor: + - "soc 2" + - "encryption" + - "dpa" + termination_window: 30 + policy_id: "trust-policy-2026-06" + created_at: "2026-06-30T00:00:00Z" + data_source_ref: "artifact://vendor-risk/acme-mailops-contract" + store_id: "vendor-risk-store-main" + prior_risk_record: + version: 2 + caller: + answers: + agent_task.vendor-risk-review.output: + decision: + approved: true + rejected: false + record_written: true + escalation: null + expect: + status: sealed + receipt: + schema: runx.receipt.v1 + state: sealed + + - name: reject-unbounded-liability-data-floor + runner: judge + inputs: + contract_text: "Vendor may process customer data without SOC 2 evidence or DPA. Encryption is not guaranteed. Liability is unlimited and termination requires 120 days." + vendor_context: + vendor_ref: "vendor:bulkdata-sync" + history: "New data sync vendor." + industry: "data processing" + policy: + required_sla_terms: + min_uptime_percent: 99.5 + support_response_hours: 24 + max_liability: 75000 + data_handling_floor: + - "soc 2" + - "encryption" + - "dpa" + termination_window: 45 + policy_id: "trust-policy-2026-06" + created_at: "2026-06-30T00:00:00Z" + data_source_ref: "artifact://vendor-risk/bulkdata-sync-contract" + store_id: "vendor-risk-store-main" + prior_risk_record: + version: 4 + caller: + answers: + agent_task.vendor-risk-review.output: + decision: + approved: false + rejected: true + record_written: true + escalation: null + expect: + status: sealed + receipt: + schema: runx.receipt.v1 + state: sealed + + - name: missing-policy-stop + runner: judge + inputs: + contract_text: "Vendor provides SOC 2 controls and 30 day termination notice." + vendor_context: + vendor_ref: "vendor:unclear-policy" + history: "New vendor." + industry: "operations" + data_source_ref: "artifact://vendor-risk/missing-policy" + store_id: "vendor-risk-store-main" + expect: + status: failure + +runners: + judge: + default: true + type: cli-tool + command: node + args: + - run.mjs + outputs: + decision: object + risk_record_event: object + data_store_append_event: object + record_written: boolean + escalation: string + evidence_summary: object + artifacts: + wrap_as: vendor_risk_packet + packet: runx.vendor_risk_review.v1 + inputs: + contract_text: + type: string + required: true + description: "Bounded contract text or summary to judge." + vendor_context: + type: json + required: true + description: "Vendor identifier, history, and industry context." + policy: + type: json + required: true + description: "Trust policy fields and thresholds." + data_source_ref: + type: string + required: true + description: "Public-safe source reference for the contract packet." + store_id: + type: string + required: true + description: "Pinned data-store identifier." + prior_risk_record: + type: json + required: false + description: "Read projection result carrying the current version." diff --git a/skills/vendor-risk-review/evidence/local-harness.json b/skills/vendor-risk-review/evidence/local-harness.json new file mode 100644 index 000000000..2a8e9ceb0 --- /dev/null +++ b/skills/vendor-risk-review/evidence/local-harness.json @@ -0,0 +1,17 @@ +{ + "status": "passed", + "case_count": 3, + "assertion_error_count": 0, + "assertion_errors": [], + "case_names": [ + "approve-with-conditions-sla-gap", + "reject-unbounded-liability-data-floor", + "missing-policy-stop" + ], + "receipt_ids": [ + "sha256:a24ef0f1b0888c2888edf697636648c1e773140f6d71453d96eb89c03a99f209", + "sha256:38d1747fa6ff0538e013083573b63bf66ad32fcdf551a00389763159c85848ce", + "sha256:eca4181b5b07de91a4ba82e97116e3887aced14b1c124f526ecde80452b7fb6d" + ], + "graph_case_count": 0 +} diff --git a/skills/vendor-risk-review/fixtures/approve-with-conditions-sla-gap.json b/skills/vendor-risk-review/fixtures/approve-with-conditions-sla-gap.json new file mode 100644 index 000000000..3cadaed47 --- /dev/null +++ b/skills/vendor-risk-review/fixtures/approve-with-conditions-sla-gap.json @@ -0,0 +1,24 @@ +{ + "contract_text": "Vendor provides SOC 2 Type II controls, encryption at rest, a signed DPA, liability capped at USD 50000, and 30 day termination notice. Service uptime commitment is 99.0 percent.", + "vendor_context": { + "vendor_ref": "vendor:acme-mailops", + "history": "Existing email operations vendor with one renewal cycle.", + "industry": "email operations" + }, + "policy": { + "required_sla_terms": { + "min_uptime_percent": 99.9, + "support_response_hours": 24 + }, + "max_liability": 100000, + "data_handling_floor": ["soc 2", "encryption", "dpa"], + "termination_window": 30, + "policy_id": "trust-policy-2026-06", + "created_at": "2026-06-30T00:00:00Z" + }, + "data_source_ref": "artifact://vendor-risk/acme-mailops-contract", + "store_id": "vendor-risk-store-main", + "prior_risk_record": { + "version": 2 + } +} diff --git a/skills/vendor-risk-review/fixtures/missing-policy-stop.json b/skills/vendor-risk-review/fixtures/missing-policy-stop.json new file mode 100644 index 000000000..dd445c13f --- /dev/null +++ b/skills/vendor-risk-review/fixtures/missing-policy-stop.json @@ -0,0 +1,10 @@ +{ + "contract_text": "Vendor provides SOC 2 controls and 30 day termination notice.", + "vendor_context": { + "vendor_ref": "vendor:unclear-policy", + "history": "New vendor.", + "industry": "operations" + }, + "data_source_ref": "artifact://vendor-risk/missing-policy", + "store_id": "vendor-risk-store-main" +} diff --git a/skills/vendor-risk-review/fixtures/reject-unbounded-liability-data-floor.json b/skills/vendor-risk-review/fixtures/reject-unbounded-liability-data-floor.json new file mode 100644 index 000000000..11f7ced1e --- /dev/null +++ b/skills/vendor-risk-review/fixtures/reject-unbounded-liability-data-floor.json @@ -0,0 +1,24 @@ +{ + "contract_text": "Vendor may process customer data without SOC 2 evidence or DPA. Encryption is not guaranteed. Liability is unlimited and termination requires 120 days.", + "vendor_context": { + "vendor_ref": "vendor:bulkdata-sync", + "history": "New data sync vendor.", + "industry": "data processing" + }, + "policy": { + "required_sla_terms": { + "min_uptime_percent": 99.5, + "support_response_hours": 24 + }, + "max_liability": 75000, + "data_handling_floor": ["soc 2", "encryption", "dpa"], + "termination_window": 45, + "policy_id": "trust-policy-2026-06", + "created_at": "2026-06-30T00:00:00Z" + }, + "data_source_ref": "artifact://vendor-risk/bulkdata-sync-contract", + "store_id": "vendor-risk-store-main", + "prior_risk_record": { + "version": 4 + } +} diff --git a/skills/vendor-risk-review/run.mjs b/skills/vendor-risk-review/run.mjs new file mode 100644 index 000000000..41a6fc634 --- /dev/null +++ b/skills/vendor-risk-review/run.mjs @@ -0,0 +1,207 @@ +import fs from "node:fs"; + +const inputs = readInputs(); + +try { + const result = decide(inputs); + process.stdout.write(`${JSON.stringify(result, null, 2)}\n`); +} catch (error) { + process.stderr.write(`${error.message}\n`); + process.exit(64); +} + +function readInputs() { + const raw = process.env.RUNX_INPUTS_PATH + ? fs.readFileSync(process.env.RUNX_INPUTS_PATH, "utf8") + : process.env.RUNX_INPUTS_JSON || "{}"; + return JSON.parse(raw); +} + +function decide(raw) { + const contractText = requireString(raw.contract_text, "contract_text"); + const vendor = requireObject(raw.vendor_context, "vendor_context"); + const policy = requireObject(raw.policy, "policy"); + const dataSourceRef = requireString(raw.data_source_ref, "data_source_ref"); + const storeId = requireString(raw.store_id, "store_id"); + + const vendorRef = requireString(vendor.vendor_ref, "vendor_context.vendor_ref"); + requireString(vendor.history, "vendor_context.history"); + requireString(vendor.industry, "vendor_context.industry"); + + const policyId = requireString(policy.policy_id, "policy.policy_id"); + const createdAt = requireString(policy.created_at, "policy.created_at"); + const requiredSlaTerms = requireObject(policy.required_sla_terms, "policy.required_sla_terms"); + const minUptime = requireNumber(requiredSlaTerms.min_uptime_percent, "policy.required_sla_terms.min_uptime_percent"); + const supportResponseHours = requireNumber(requiredSlaTerms.support_response_hours, "policy.required_sla_terms.support_response_hours"); + const maxLiability = requireNumber(policy.max_liability, "policy.max_liability"); + const dataHandlingFloor = requireStringArray(policy.data_handling_floor, "policy.data_handling_floor"); + const terminationWindow = requireNumber(policy.termination_window, "policy.termination_window"); + const priorVersion = readPriorVersion(raw.prior_risk_record); + + const normalized = normalize(contractText); + const gaps = []; + const rejectionReasons = []; + const evidence = []; + + const uptime = extractUptime(normalized); + if (uptime !== null && uptime < minUptime) { + gaps.push(condition("sla_uptime_below_floor", "SLA uptime is below the trust policy floor", "policy.required_sla_terms.min_uptime_percent", { observed_uptime_percent: uptime, min_uptime_percent: minUptime })); + } + if (uptime === null) { + gaps.push(condition("sla_uptime_missing", "SLA uptime is not explicit", "policy.required_sla_terms.min_uptime_percent", { min_uptime_percent: minUptime })); + } + + const terminationDays = extractTerminationDays(normalized); + if (terminationDays !== null && terminationDays > terminationWindow) { + gaps.push(condition("termination_notice_above_window", "Termination notice exceeds policy window", "policy.termination_window", { observed_days: terminationDays, termination_window: terminationWindow })); + } + + const missingDataTerms = dataHandlingFloor.filter((term) => !hasPositivePolicyTerm(normalized, term)); + if (missingDataTerms.length > 0) { + rejectionReasons.push(condition("data_handling_below_floor", "Contract does not meet required data-handling floor", "policy.data_handling_floor", { missing_terms: missingDataTerms })); + } + + const liability = extractLiability(normalized); + if (liability.unbounded || (liability.amount !== null && liability.amount > maxLiability)) { + rejectionReasons.push(condition("liability_above_policy_cap", "Liability is unbounded or above policy cap", "policy.max_liability", { observed_liability: liability.unbounded ? "unbounded" : liability.amount, max_liability: maxLiability })); + } + + evidence.push({ policy_field: "policy.required_sla_terms.support_response_hours", value: supportResponseHours }); + evidence.push({ policy_field: "policy.policy_id", value: policyId }); + evidence.push({ policy_field: "policy.created_at", value: createdAt }); + + const rejected = rejectionReasons.length > 0; + const approved = !rejected; + const conditions = approved ? gaps : []; + const reason = rejected + ? rejectionReasons.map((item) => item.code).join(", ") + : conditions.length > 0 + ? "approved with conditions" + : "approved"; + + const decision = { + approved, + rejected, + reason, + conditions, + rejection_reasons: rejectionReasons, + policy_id: policyId, + created_at: createdAt, + }; + + const riskRecordEvent = { + schema: "runx.vendor_risk_record.v1", + vendor_ref: vendorRef, + decision, + policy_id: policyId, + created_at: createdAt, + data_source_ref: dataSourceRef, + }; + + const idempotencyKey = stableKey([vendorRef, policyId, rejected ? "rejected" : "approved", reason]); + const appendEvent = { + package: "registry:runx/data-store@0.1.2", + operation: "append_event", + store_id: storeId, + aggregate_id: vendorRef, + expected_version: priorVersion, + before_version: priorVersion, + after_version: priorVersion + 1, + idempotency_key: idempotencyKey, + event: riskRecordEvent, + }; + + return { + decision, + risk_record_event: riskRecordEvent, + data_store_append_event: appendEvent, + record_written: true, + escalation: null, + evidence_summary: { + vendor_ref: vendorRef, + policy_id: policyId, + created_at: createdAt, + contract_gaps: gaps, + rejection_reasons: rejectionReasons, + data_store: { store_id: storeId, before_version: priorVersion, after_version: priorVersion + 1, idempotency_key: idempotencyKey }, + grounded_policy_fields: evidence, + }, + }; +} + +function condition(code, reason, policyField, evidence) { + return { code, reason, policy_field: policyField, evidence }; +} + +function extractUptime(text) { + const match = text.match(/(\d{2}\.\d|\d{2})\s*(?:percent|%)/); + return match ? Number(match[1]) : null; +} + +function extractTerminationDays(text) { + const match = text.match(/(\d+)\s*day\s*termination/); + return match ? Number(match[1]) : null; +} + +function extractLiability(text) { + if (/unlimited liability|liability is unlimited|unbounded liability/.test(text)) { + return { unbounded: true, amount: null }; + } + const match = text.match(/liability (?:capped at|cap|is capped at) usd\s*(\d+)/); + return { unbounded: false, amount: match ? Number(match[1]) : null }; +} + +function hasPositivePolicyTerm(text, term) { + const normalizedTerm = normalize(term); + if (!text.includes(normalizedTerm)) return false; + const negatedPatterns = [ + `without ${normalizedTerm}`, + `no ${normalizedTerm}`, + `${normalizedTerm} not guaranteed`, + `${normalizedTerm} is not guaranteed`, + ]; + return !negatedPatterns.some((pattern) => text.includes(pattern)); +} + +function readPriorVersion(value) { + if (value === undefined || value === null) return 0; + const record = requireObject(value, "prior_risk_record"); + return requireNumber(record.version, "prior_risk_record.version"); +} + +function requireObject(value, name) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${name} must be an object`); + } + return value; +} + +function requireString(value, name) { + if (typeof value !== "string" || value.length === 0) { + throw new Error(`${name} must be a non-empty string`); + } + return value; +} + +function requireNumber(value, name) { + const number = Number(value); + if (!Number.isFinite(number)) { + throw new Error(`${name} must be a finite number`); + } + return number; +} + +function requireStringArray(value, name) { + if (!Array.isArray(value) || value.some((item) => typeof item !== "string" || item.length === 0)) { + throw new Error(`${name} must be a string array`); + } + return value; +} + +function normalize(value) { + return String(value || "").toLowerCase().replace(/[^a-z0-9.]+/g, " ").replace(/\s+/g, " ").trim(); +} + +function stableKey(parts) { + return `vendor-risk:${parts.map((part) => normalize(part)).join(":")}`; +} From 01ebe8c718ef7e3c11a0465799b09437e8ece2ec Mon Sep 17 00:00:00 2001 From: Rohit Mulani Date: Tue, 30 Jun 2026 17:01:44 +0400 Subject: [PATCH 2/2] Add vendor-risk-review delivery evidence --- .../evidence/dogfood-receipt.json | 189 +++++++ .../evidence/dogfood-run-output.json | 530 ++++++++++++++++++ .../evidence/dogfood-verify-output.json | 25 + .../vendor-risk-review/evidence/evidence.json | 70 +++ skills/vendor-risk-review/evidence/report.md | 32 ++ .../evidence/verification.json | 26 + vendor-risk-review-publish-output.json | Bin 0 -> 1732 bytes 7 files changed, 872 insertions(+) create mode 100644 skills/vendor-risk-review/evidence/dogfood-receipt.json create mode 100644 skills/vendor-risk-review/evidence/dogfood-run-output.json create mode 100644 skills/vendor-risk-review/evidence/dogfood-verify-output.json create mode 100644 skills/vendor-risk-review/evidence/evidence.json create mode 100644 skills/vendor-risk-review/evidence/report.md create mode 100644 skills/vendor-risk-review/evidence/verification.json create mode 100644 vendor-risk-review-publish-output.json diff --git a/skills/vendor-risk-review/evidence/dogfood-receipt.json b/skills/vendor-risk-review/evidence/dogfood-receipt.json new file mode 100644 index 000000000..4ec3655d2 --- /dev/null +++ b/skills/vendor-risk-review/evidence/dogfood-receipt.json @@ -0,0 +1,189 @@ +{ + "acts": [ + { + "artifact_refs": [ + + ], + "closure": { + "closed_at": "2026-06-30T12:59:41.313Z", + "disposition": "closed", + "reason_code": "process_exit", + "summary": "cli-tool exited successfully" + }, + "criterion_bindings": [ + { + "criterion_id": "process_exit", + "evidence_refs": [ + + ], + "status": "verified", + "summary": "cli-tool exited successfully", + "verification_refs": [ + + ] + } + ], + "form": "observation", + "id": "act_judge", + "intent": { + "constraints": [ + + ], + "derived_from": [ + + ], + "legitimacy": "Runtime graph execution was admitted by the local harness", + "purpose": "Run graph step judge", + "success_criteria": [ + { + "criterion_id": "process_exit", + "required": true, + "statement": "cli-tool exits successfully" + } + ] + }, + "source_refs": [ + + ], + "summary": "Executed graph step judge", + "target_refs": [ + + ] + } + ], + "authority": { + "actor_ref": { + "type": "principal", + "uri": "runx:principal:local_runtime" + }, + "attenuation": { + "parent_authority_ref": null, + "subset_proof": null + }, + "authority_proof_refs": [ + + ], + "enforcement": { + "profile_hash": "sha256:635fdb0a7eef93911e3c6bd0b25b5e635edba32e0259133042b3a5f1fb19394e", + "redaction_refs": [ + + ], + "setup_refs": [ + + ], + "teardown_refs": [ + + ] + }, + "grant_refs": [ + + ], + "scope_refs": [ + + ], + "terms": [ + + ] + }, + "canonicalization": "runx.receipt.c14n.v1", + "created_at": "2026-06-30T12:59:41.313Z", + "decisions": [ + { + "artifact_refs": [ + + ], + "choice": "open", + "closure": null, + "decision_id": "dec_judge", + "inputs": { + "opportunity_refs": [ + + ], + "selection_ref": null, + "signal_refs": [ + + ], + "target_ref": null + }, + "justification": { + "evidence_refs": [ + + ], + "summary": "runtime graph planner selected this node" + }, + "proposed_intent": { + "constraints": [ + + ], + "derived_from": [ + + ], + "legitimacy": "Local graph execution requested this node", + "purpose": "Open runtime node judge", + "success_criteria": [ + + ] + }, + "selected_act_id": "act_judge", + "selected_harness_ref": null + } + ], + "digest": "sha256:e0c87f6fd84049a8aff2ac4cec38028f1832fcca53c468f89508d7772375c8de", + "id": "sha256:9222ecb7a2f81d5542ac095e6f49c5dd97d8ba0127275f50f4bb808f9822d896", + "idempotency": { + "content_hash": "sha256:run_judge_6c4c07a106c6-judge-content", + "intent_key": "sha256:run_judge_6c4c07a106c6-judge-intent", + "trigger_fingerprint": "sha256:run_judge_6c4c07a106c6-judge-trigger" + }, + "issuer": { + "kid": "runx-demo-key", + "public_key_sha256": "sha256:139e3940e64b5491722088d9a0d741628fc826e09475d341a780acde3c4b8070", + "type": "hosted" + }, + "lineage": { + "children": [ + + ], + "sync": [ + + ] + }, + "schema": "runx.receipt.v1", + "seal": { + "closed_at": "2026-06-30T12:59:41.313Z", + "criteria": [ + { + "criterion_id": "process_exit", + "evidence_refs": [ + + ], + "status": "verified", + "summary": "cli-tool exited successfully", + "verification_refs": [ + + ] + } + ], + "disposition": "closed", + "last_observed_at": "2026-06-30T12:59:41.313Z", + "reason_code": "process_closed", + "summary": "cli-tool judge completed" + }, + "signals": [ + + ], + "signature": { + "alg": "Ed25519", + "value": "base64:3hN_r3cIB9hdRAyPeVGn5Tj5-vMlf9YpRdG7VB0YD1fHpmhEwwYzjDDjgIpKbc6I9fxM_IB68kPQWHMfSXYoCA" + }, + "subject": { + "commitments": [ + + ], + "kind": "skill", + "ref": { + "type": "harness", + "uri": "hrn_run_judge_6c4c07a106c6_judge" + } + } +} diff --git a/skills/vendor-risk-review/evidence/dogfood-run-output.json b/skills/vendor-risk-review/evidence/dogfood-run-output.json new file mode 100644 index 000000000..bf3cba2ce --- /dev/null +++ b/skills/vendor-risk-review/evidence/dogfood-run-output.json @@ -0,0 +1,530 @@ +{ + "closure": { + "closed_at": "2026-06-30T12:59:41.313Z", + "disposition": "closed", + "reason_code": "process_closed", + "summary": "cli-tool judge completed" + }, + "execution": { + "exit_code": 0, + "skill_claim": { + "data_store_append_event": { + "after_version": 3, + "aggregate_id": "vendor:acme-mailops", + "before_version": 2, + "event": { + "created_at": "2026-06-30T00:00:00Z", + "data_source_ref": "artifact://vendor-risk/acme-mailops-contract", + "decision": { + "approved": true, + "conditions": [ + { + "code": "sla_uptime_below_floor", + "evidence": { + "min_uptime_percent": 99.9, + "observed_uptime_percent": 99 + }, + "policy_field": "policy.required_sla_terms.min_uptime_percent", + "reason": "SLA uptime is below the trust policy floor" + } + ], + "created_at": "2026-06-30T00:00:00Z", + "policy_id": "trust-policy-2026-06", + "reason": "approved with conditions", + "rejected": false, + "rejection_reasons": [] + }, + "policy_id": "trust-policy-2026-06", + "schema": "runx.vendor_risk_record.v1", + "vendor_ref": "vendor:acme-mailops" + }, + "expected_version": 2, + "idempotency_key": "vendor-risk:vendor acme mailops:trust policy 2026 06:approved:approved with conditions", + "operation": "append_event", + "package": "registry:runx/data-store@0.1.2", + "store_id": "vendor-risk-store-main" + }, + "decision": { + "approved": true, + "conditions": [ + { + "code": "sla_uptime_below_floor", + "evidence": { + "min_uptime_percent": 99.9, + "observed_uptime_percent": 99 + }, + "policy_field": "policy.required_sla_terms.min_uptime_percent", + "reason": "SLA uptime is below the trust policy floor" + } + ], + "created_at": "2026-06-30T00:00:00Z", + "policy_id": "trust-policy-2026-06", + "reason": "approved with conditions", + "rejected": false, + "rejection_reasons": [] + }, + "escalation": null, + "evidence_summary": { + "contract_gaps": [ + { + "code": "sla_uptime_below_floor", + "evidence": { + "min_uptime_percent": 99.9, + "observed_uptime_percent": 99 + }, + "policy_field": "policy.required_sla_terms.min_uptime_percent", + "reason": "SLA uptime is below the trust policy floor" + } + ], + "created_at": "2026-06-30T00:00:00Z", + "data_store": { + "after_version": 3, + "before_version": 2, + "idempotency_key": "vendor-risk:vendor acme mailops:trust policy 2026 06:approved:approved with conditions", + "store_id": "vendor-risk-store-main" + }, + "grounded_policy_fields": [ + { + "policy_field": "policy.required_sla_terms.support_response_hours", + "value": 24 + }, + { + "policy_field": "policy.policy_id", + "value": "trust-policy-2026-06" + }, + { + "policy_field": "policy.created_at", + "value": "2026-06-30T00:00:00Z" + } + ], + "policy_id": "trust-policy-2026-06", + "rejection_reasons": [], + "vendor_ref": "vendor:acme-mailops" + }, + "record_written": true, + "risk_record_event": { + "created_at": "2026-06-30T00:00:00Z", + "data_source_ref": "artifact://vendor-risk/acme-mailops-contract", + "decision": { + "approved": true, + "conditions": [ + { + "code": "sla_uptime_below_floor", + "evidence": { + "min_uptime_percent": 99.9, + "observed_uptime_percent": 99 + }, + "policy_field": "policy.required_sla_terms.min_uptime_percent", + "reason": "SLA uptime is below the trust policy floor" + } + ], + "created_at": "2026-06-30T00:00:00Z", + "policy_id": "trust-policy-2026-06", + "reason": "approved with conditions", + "rejected": false, + "rejection_reasons": [] + }, + "policy_id": "trust-policy-2026-06", + "schema": "runx.vendor_risk_record.v1", + "vendor_ref": "vendor:acme-mailops" + } + }, + "stderr": "", + "stdout": "{\n \"decision\": {\n \"approved\": true,\n \"rejected\": false,\n \"reason\": \"approved with conditions\",\n \"conditions\": [\n {\n \"code\": \"sla_uptime_below_floor\",\n \"reason\": \"SLA uptime is below the trust policy floor\",\n \"policy_field\": \"policy.required_sla_terms.min_uptime_percent\",\n \"evidence\": {\n \"observed_uptime_percent\": 99,\n \"min_uptime_percent\": 99.9\n }\n }\n ],\n \"rejection_reasons\": [],\n \"policy_id\": \"trust-policy-2026-06\",\n \"created_at\": \"2026-06-30T00:00:00Z\"\n },\n \"risk_record_event\": {\n \"schema\": \"runx.vendor_risk_record.v1\",\n \"vendor_ref\": \"vendor:acme-mailops\",\n \"decision\": {\n \"approved\": true,\n \"rejected\": false,\n \"reason\": \"approved with conditions\",\n \"conditions\": [\n {\n \"code\": \"sla_uptime_below_floor\",\n \"reason\": \"SLA uptime is below the trust policy floor\",\n \"policy_field\": \"policy.required_sla_terms.min_uptime_percent\",\n \"evidence\": {\n \"observed_uptime_percent\": 99,\n \"min_uptime_percent\": 99.9\n }\n }\n ],\n \"rejection_reasons\": [],\n \"policy_id\": \"trust-policy-2026-06\",\n \"created_at\": \"2026-06-30T00:00:00Z\"\n },\n \"policy_id\": \"trust-policy-2026-06\",\n \"created_at\": \"2026-06-30T00:00:00Z\",\n \"data_source_ref\": \"artifact://vendor-risk/acme-mailops-contract\"\n },\n \"data_store_append_event\": {\n \"package\": \"registry:runx/data-store@0.1.2\",\n \"operation\": \"append_event\",\n \"store_id\": \"vendor-risk-store-main\",\n \"aggregate_id\": \"vendor:acme-mailops\",\n \"expected_version\": 2,\n \"before_version\": 2,\n \"after_version\": 3,\n \"idempotency_key\": \"vendor-risk:vendor acme mailops:trust policy 2026 06:approved:approved with conditions\",\n \"event\": {\n \"schema\": \"runx.vendor_risk_record.v1\",\n \"vendor_ref\": \"vendor:acme-mailops\",\n \"decision\": {\n \"approved\": true,\n \"rejected\": false,\n \"reason\": \"approved with conditions\",\n \"conditions\": [\n {\n \"code\": \"sla_uptime_below_floor\",\n \"reason\": \"SLA uptime is below the trust policy floor\",\n \"policy_field\": \"policy.required_sla_terms.min_uptime_percent\",\n \"evidence\": {\n \"observed_uptime_percent\": 99,\n \"min_uptime_percent\": 99.9\n }\n }\n ],\n \"rejection_reasons\": [],\n \"policy_id\": \"trust-policy-2026-06\",\n \"created_at\": \"2026-06-30T00:00:00Z\"\n },\n \"policy_id\": \"trust-policy-2026-06\",\n \"created_at\": \"2026-06-30T00:00:00Z\",\n \"data_source_ref\": \"artifact://vendor-risk/acme-mailops-contract\"\n }\n },\n \"record_written\": true,\n \"escalation\": null,\n \"evidence_summary\": {\n \"vendor_ref\": \"vendor:acme-mailops\",\n \"policy_id\": \"trust-policy-2026-06\",\n \"created_at\": \"2026-06-30T00:00:00Z\",\n \"contract_gaps\": [\n {\n \"code\": \"sla_uptime_below_floor\",\n \"reason\": \"SLA uptime is below the trust policy floor\",\n \"policy_field\": \"policy.required_sla_terms.min_uptime_percent\",\n \"evidence\": {\n \"observed_uptime_percent\": 99,\n \"min_uptime_percent\": 99.9\n }\n }\n ],\n \"rejection_reasons\": [],\n \"data_store\": {\n \"store_id\": \"vendor-risk-store-main\",\n \"before_version\": 2,\n \"after_version\": 3,\n \"idempotency_key\": \"vendor-risk:vendor acme mailops:trust policy 2026 06:approved:approved with conditions\"\n },\n \"grounded_policy_fields\": [\n {\n \"policy_field\": \"policy.required_sla_terms.support_response_hours\",\n \"value\": 24\n },\n {\n \"policy_field\": \"policy.policy_id\",\n \"value\": \"trust-policy-2026-06\"\n },\n {\n \"policy_field\": \"policy.created_at\",\n \"value\": \"2026-06-30T00:00:00Z\"\n }\n ]\n }\n}\n", + "structured_output": { + "data_store_append_event": { + "after_version": 3, + "aggregate_id": "vendor:acme-mailops", + "before_version": 2, + "event": { + "created_at": "2026-06-30T00:00:00Z", + "data_source_ref": "artifact://vendor-risk/acme-mailops-contract", + "decision": { + "approved": true, + "conditions": [ + { + "code": "sla_uptime_below_floor", + "evidence": { + "min_uptime_percent": 99.9, + "observed_uptime_percent": 99 + }, + "policy_field": "policy.required_sla_terms.min_uptime_percent", + "reason": "SLA uptime is below the trust policy floor" + } + ], + "created_at": "2026-06-30T00:00:00Z", + "policy_id": "trust-policy-2026-06", + "reason": "approved with conditions", + "rejected": false, + "rejection_reasons": [] + }, + "policy_id": "trust-policy-2026-06", + "schema": "runx.vendor_risk_record.v1", + "vendor_ref": "vendor:acme-mailops" + }, + "expected_version": 2, + "idempotency_key": "vendor-risk:vendor acme mailops:trust policy 2026 06:approved:approved with conditions", + "operation": "append_event", + "package": "registry:runx/data-store@0.1.2", + "store_id": "vendor-risk-store-main" + }, + "decision": { + "approved": true, + "conditions": [ + { + "code": "sla_uptime_below_floor", + "evidence": { + "min_uptime_percent": 99.9, + "observed_uptime_percent": 99 + }, + "policy_field": "policy.required_sla_terms.min_uptime_percent", + "reason": "SLA uptime is below the trust policy floor" + } + ], + "created_at": "2026-06-30T00:00:00Z", + "policy_id": "trust-policy-2026-06", + "reason": "approved with conditions", + "rejected": false, + "rejection_reasons": [] + }, + "escalation": null, + "evidence_summary": { + "contract_gaps": [ + { + "code": "sla_uptime_below_floor", + "evidence": { + "min_uptime_percent": 99.9, + "observed_uptime_percent": 99 + }, + "policy_field": "policy.required_sla_terms.min_uptime_percent", + "reason": "SLA uptime is below the trust policy floor" + } + ], + "created_at": "2026-06-30T00:00:00Z", + "data_store": { + "after_version": 3, + "before_version": 2, + "idempotency_key": "vendor-risk:vendor acme mailops:trust policy 2026 06:approved:approved with conditions", + "store_id": "vendor-risk-store-main" + }, + "grounded_policy_fields": [ + { + "policy_field": "policy.required_sla_terms.support_response_hours", + "value": 24 + }, + { + "policy_field": "policy.policy_id", + "value": "trust-policy-2026-06" + }, + { + "policy_field": "policy.created_at", + "value": "2026-06-30T00:00:00Z" + } + ], + "policy_id": "trust-policy-2026-06", + "rejection_reasons": [], + "vendor_ref": "vendor:acme-mailops" + }, + "record_written": true, + "risk_record_event": { + "created_at": "2026-06-30T00:00:00Z", + "data_source_ref": "artifact://vendor-risk/acme-mailops-contract", + "decision": { + "approved": true, + "conditions": [ + { + "code": "sla_uptime_below_floor", + "evidence": { + "min_uptime_percent": 99.9, + "observed_uptime_percent": 99 + }, + "policy_field": "policy.required_sla_terms.min_uptime_percent", + "reason": "SLA uptime is below the trust policy floor" + } + ], + "created_at": "2026-06-30T00:00:00Z", + "policy_id": "trust-policy-2026-06", + "reason": "approved with conditions", + "rejected": false, + "rejection_reasons": [] + }, + "policy_id": "trust-policy-2026-06", + "schema": "runx.vendor_risk_record.v1", + "vendor_ref": "vendor:acme-mailops" + } + } + }, + "payload": { + "data_store_append_event": { + "after_version": 3, + "aggregate_id": "vendor:acme-mailops", + "before_version": 2, + "event": { + "created_at": "2026-06-30T00:00:00Z", + "data_source_ref": "artifact://vendor-risk/acme-mailops-contract", + "decision": { + "approved": true, + "conditions": [ + { + "code": "sla_uptime_below_floor", + "evidence": { + "min_uptime_percent": 99.9, + "observed_uptime_percent": 99 + }, + "policy_field": "policy.required_sla_terms.min_uptime_percent", + "reason": "SLA uptime is below the trust policy floor" + } + ], + "created_at": "2026-06-30T00:00:00Z", + "policy_id": "trust-policy-2026-06", + "reason": "approved with conditions", + "rejected": false, + "rejection_reasons": [] + }, + "policy_id": "trust-policy-2026-06", + "schema": "runx.vendor_risk_record.v1", + "vendor_ref": "vendor:acme-mailops" + }, + "expected_version": 2, + "idempotency_key": "vendor-risk:vendor acme mailops:trust policy 2026 06:approved:approved with conditions", + "operation": "append_event", + "package": "registry:runx/data-store@0.1.2", + "store_id": "vendor-risk-store-main" + }, + "decision": { + "approved": true, + "conditions": [ + { + "code": "sla_uptime_below_floor", + "evidence": { + "min_uptime_percent": 99.9, + "observed_uptime_percent": 99 + }, + "policy_field": "policy.required_sla_terms.min_uptime_percent", + "reason": "SLA uptime is below the trust policy floor" + } + ], + "created_at": "2026-06-30T00:00:00Z", + "policy_id": "trust-policy-2026-06", + "reason": "approved with conditions", + "rejected": false, + "rejection_reasons": [] + }, + "escalation": null, + "evidence_summary": { + "contract_gaps": [ + { + "code": "sla_uptime_below_floor", + "evidence": { + "min_uptime_percent": 99.9, + "observed_uptime_percent": 99 + }, + "policy_field": "policy.required_sla_terms.min_uptime_percent", + "reason": "SLA uptime is below the trust policy floor" + } + ], + "created_at": "2026-06-30T00:00:00Z", + "data_store": { + "after_version": 3, + "before_version": 2, + "idempotency_key": "vendor-risk:vendor acme mailops:trust policy 2026 06:approved:approved with conditions", + "store_id": "vendor-risk-store-main" + }, + "grounded_policy_fields": [ + { + "policy_field": "policy.required_sla_terms.support_response_hours", + "value": 24 + }, + { + "policy_field": "policy.policy_id", + "value": "trust-policy-2026-06" + }, + { + "policy_field": "policy.created_at", + "value": "2026-06-30T00:00:00Z" + } + ], + "policy_id": "trust-policy-2026-06", + "rejection_reasons": [], + "vendor_ref": "vendor:acme-mailops" + }, + "record_written": true, + "risk_record_event": { + "created_at": "2026-06-30T00:00:00Z", + "data_source_ref": "artifact://vendor-risk/acme-mailops-contract", + "decision": { + "approved": true, + "conditions": [ + { + "code": "sla_uptime_below_floor", + "evidence": { + "min_uptime_percent": 99.9, + "observed_uptime_percent": 99 + }, + "policy_field": "policy.required_sla_terms.min_uptime_percent", + "reason": "SLA uptime is below the trust policy floor" + } + ], + "created_at": "2026-06-30T00:00:00Z", + "policy_id": "trust-policy-2026-06", + "reason": "approved with conditions", + "rejected": false, + "rejection_reasons": [] + }, + "policy_id": "trust-policy-2026-06", + "schema": "runx.vendor_risk_record.v1", + "vendor_ref": "vendor:acme-mailops" + } + }, + "receipt": { + "acts": [ + { + "artifact_refs": [], + "closure": { + "closed_at": "2026-06-30T12:59:41.313Z", + "disposition": "closed", + "reason_code": "process_exit", + "summary": "cli-tool exited successfully" + }, + "criterion_bindings": [ + { + "criterion_id": "process_exit", + "evidence_refs": [], + "status": "verified", + "summary": "cli-tool exited successfully", + "verification_refs": [] + } + ], + "form": "observation", + "id": "act_judge", + "intent": { + "constraints": [], + "derived_from": [], + "legitimacy": "Runtime graph execution was admitted by the local harness", + "purpose": "Run graph step judge", + "success_criteria": [ + { + "criterion_id": "process_exit", + "required": true, + "statement": "cli-tool exits successfully" + } + ] + }, + "source_refs": [], + "summary": "Executed graph step judge", + "target_refs": [] + } + ], + "authority": { + "actor_ref": { + "type": "principal", + "uri": "runx:principal:local_runtime" + }, + "attenuation": { + "parent_authority_ref": null, + "subset_proof": null + }, + "authority_proof_refs": [], + "enforcement": { + "profile_hash": "sha256:635fdb0a7eef93911e3c6bd0b25b5e635edba32e0259133042b3a5f1fb19394e", + "redaction_refs": [], + "setup_refs": [], + "teardown_refs": [] + }, + "grant_refs": [], + "scope_refs": [], + "terms": [] + }, + "canonicalization": "runx.receipt.c14n.v1", + "created_at": "2026-06-30T12:59:41.313Z", + "decisions": [ + { + "artifact_refs": [], + "choice": "open", + "closure": null, + "decision_id": "dec_judge", + "inputs": { + "opportunity_refs": [], + "selection_ref": null, + "signal_refs": [], + "target_ref": null + }, + "justification": { + "evidence_refs": [], + "summary": "runtime graph planner selected this node" + }, + "proposed_intent": { + "constraints": [], + "derived_from": [], + "legitimacy": "Local graph execution requested this node", + "purpose": "Open runtime node judge", + "success_criteria": [] + }, + "selected_act_id": "act_judge", + "selected_harness_ref": null + } + ], + "digest": "sha256:e0c87f6fd84049a8aff2ac4cec38028f1832fcca53c468f89508d7772375c8de", + "id": "sha256:9222ecb7a2f81d5542ac095e6f49c5dd97d8ba0127275f50f4bb808f9822d896", + "idempotency": { + "content_hash": "sha256:run_judge_6c4c07a106c6-judge-content", + "intent_key": "sha256:run_judge_6c4c07a106c6-judge-intent", + "trigger_fingerprint": "sha256:run_judge_6c4c07a106c6-judge-trigger" + }, + "issuer": { + "kid": "runx-demo-key", + "public_key_sha256": "sha256:139e3940e64b5491722088d9a0d741628fc826e09475d341a780acde3c4b8070", + "type": "hosted" + }, + "lineage": { + "children": [], + "sync": [] + }, + "schema": "runx.receipt.v1", + "seal": { + "closed_at": "2026-06-30T12:59:41.313Z", + "criteria": [ + { + "criterion_id": "process_exit", + "evidence_refs": [], + "status": "verified", + "summary": "cli-tool exited successfully", + "verification_refs": [] + } + ], + "disposition": "closed", + "last_observed_at": "2026-06-30T12:59:41.313Z", + "reason_code": "process_closed", + "summary": "cli-tool judge completed" + }, + "signals": [], + "signature": { + "alg": "Ed25519", + "value": "base64:3hN_r3cIB9hdRAyPeVGn5Tj5-vMlf9YpRdG7VB0YD1fHpmhEwwYzjDDjgIpKbc6I9fxM_IB68kPQWHMfSXYoCA" + }, + "subject": { + "commitments": [], + "kind": "skill", + "ref": { + "type": "harness", + "uri": "hrn_run_judge_6c4c07a106c6_judge" + } + } + }, + "receipt_id": "sha256:9222ecb7a2f81d5542ac095e6f49c5dd97d8ba0127275f50f4bb808f9822d896", + "registry_provenance": { + "digest": "sha256:cddd104fef668ffb71db4681f6772070655fa9ff0993fc5f5ccd987787d2c5e9", + "profile_digest": "sha256:df25405214d0e94db02ce2bd1e37e9b083c194095f29e6198c6bd0debae8c8e8", + "registry_key_id": "runx-registry-ed25519-v1", + "registry_source": "remote https://api.runx.ai", + "registry_source_fingerprint": "ba1ac16b631195fd", + "skill_id": "rohitmulani63-ops/vendor-risk-review", + "trust_state": "trusted", + "trust_tier": "community", + "version": "sha-a4f5ecb0c7a2" + }, + "run_id": "run_judge_6c4c07a106c6", + "schema": "runx.skill_run.v1", + "skill_name": "vendor-risk-review", + "status": "sealed" +} diff --git a/skills/vendor-risk-review/evidence/dogfood-verify-output.json b/skills/vendor-risk-review/evidence/dogfood-verify-output.json new file mode 100644 index 000000000..541c415ac --- /dev/null +++ b/skills/vendor-risk-review/evidence/dogfood-verify-output.json @@ -0,0 +1,25 @@ +{ + "schema": "runx.verify_verdict.v1", + "receipt_id": "sha256:9222ecb7a2f81d5542ac095e6f49c5dd97d8ba0127275f50f4bb808f9822d896", + "valid": true, + "digest": { + "status": "valid", + "expected": "sha256:e0c87f6fd84049a8aff2ac4cec38028f1832fcca53c468f89508d7772375c8de", + "actual": "sha256:e0c87f6fd84049a8aff2ac4cec38028f1832fcca53c468f89508d7772375c8de" + }, + "content_address": { + "status": "valid", + "expected": "sha256:9222ecb7a2f81d5542ac095e6f49c5dd97d8ba0127275f50f4bb808f9822d896", + "actual": "sha256:9222ecb7a2f81d5542ac095e6f49c5dd97d8ba0127275f50f4bb808f9822d896" + }, + "signature": { + "mode": "production", + "status": "valid", + "kid": "runx-demo-key" + }, + "lineage": { + "status": "unverified", + "message": "single receipt verification cannot prove receipt-tree lineage" + }, + "findings": [] +} diff --git a/skills/vendor-risk-review/evidence/evidence.json b/skills/vendor-risk-review/evidence/evidence.json new file mode 100644 index 000000000..b361ab093 --- /dev/null +++ b/skills/vendor-risk-review/evidence/evidence.json @@ -0,0 +1,70 @@ +{ + "schema": "frantic.evidence.vendor-risk-review.v1", + "summary": "vendor-risk-review publishes a relationship-level vendor trust decision, records approved-with-conditions or rejected outcomes through the data-store append_event seam, and stops before write when policy evidence is missing.", + "runx_cli_version": "runx-cli 0.6.14", + "publisher_owner": "rohitmulani63-ops", + "package_name": "vendor-risk-review", + "package_version": "sha-a4f5ecb0c7a2", + "registry_ref": "rohitmulani63-ops/vendor-risk-review@sha-a4f5ecb0c7a2", + "public_url": "https://runx.ai/x/rohitmulani63-ops/vendor-risk-review@sha-a4f5ecb0c7a2", + "pr_url": "https://github.com/runxhq/runx/pull/189", + "publish_method": "runx registry publish ./skills/vendor-risk-review/SKILL.md --registry https://api.runx.ai --json", + "install_command": "runx add rohitmulani63-ops/vendor-risk-review@sha-a4f5ecb0c7a2 --registry https://api.runx.ai", + "dogfood": { + "package": "rohitmulani63-ops/vendor-risk-review@sha-a4f5ecb0c7a2", + "input": "approve-with-conditions-sla-gap fixture", + "command": "runx skill rohitmulani63-ops/vendor-risk-review@sha-a4f5ecb0c7a2 --registry https://api.runx.ai judge -i contract_text=... -i data_source_ref=... -i store_id=... --input-json vendor_context=... --input-json policy=... --input-json prior_risk_record=... -j", + "receipt_ref": "runx:receipt:sha256:9222ecb7a2f81d5542ac095e6f49c5dd97d8ba0127275f50f4bb808f9822d896", + "verify_verdict": { + "valid": true, + "receipt_id": "sha256:9222ecb7a2f81d5542ac095e6f49c5dd97d8ba0127275f50f4bb808f9822d896", + "signature_status": "valid", + "digest_status": "valid", + "content_address_status": "valid" + }, + "harness_cases": [ + { + "name": "approve-with-conditions-sla-gap", + "status": "sealed" + }, + { + "name": "reject-unbounded-liability-data-floor", + "status": "sealed" + }, + { + "name": "missing-policy-stop", + "status": "refused" + } + ] + }, + "observations": [ + { + "key": "decision_lane", + "value": "approved-with-conditions dogfood decision records approved true, rejected false, with condition sla_uptime_below_floor grounded in policy.required_sla_terms.min_uptime_percent" + }, + { + "key": "rejection_lane", + "value": "rejection fixture records approved false, rejected true, with data_handling_below_floor and liability_above_policy_cap grounded in policy.data_handling_floor and policy.max_liability" + }, + { + "key": "data_store_append", + "value": "output uses registry:runx/data-store@0.1.2 append_event with store_id vendor-risk-store-main, aggregate_id vendor entity, expected_version from prior_risk_record, before/after version, and idempotency_key" + }, + { + "key": "policy_binding", + "value": "policy_id trust-policy-2026-06 and created_at 2026-06-30T00:00:00Z are copied into decision, risk_record_event, and evidence_summary" + }, + { + "key": "harness", + "value": "Docker Linux harness status passed, cases approve-with-conditions-sla-gap, reject-unbounded-liability-data-floor, missing-policy-stop, assertion errors 0" + }, + { + "key": "receipt", + "value": "runx:receipt:sha256:9222ecb7a2f81d5542ac095e6f49c5dd97d8ba0127275f50f4bb808f9822d896" + }, + { + "key": "operator_value", + "value": "a downstream operator can install the skill, run the same fixtures, and inspect why a vendor relationship was accepted with conditions or rejected before any send-as notification is issued" + } + ] +} diff --git a/skills/vendor-risk-review/evidence/report.md b/skills/vendor-risk-review/evidence/report.md new file mode 100644 index 000000000..3c1a000cd --- /dev/null +++ b/skills/vendor-risk-review/evidence/report.md @@ -0,0 +1,32 @@ +# vendor-risk-review evidence report + +## Summary +vendor-risk-review is a runx skill for relationship-level vendor trust decisions. It is not a clause redliner. It reads bounded contract text, vendor context, a supplied trust policy, data source reference, pinned store id, and prior risk record version. + +## What was built +- Package: rohitmulani63-ops/vendor-risk-review@sha-a4f5ecb0c7a2 +- Public URL: https://runx.ai/x/rohitmulani63-ops/vendor-risk-review@sha-a4f5ecb0c7a2 +- PR: https://github.com/runxhq/runx/pull/189 +- Data-store seam: registry:runx/data-store@0.1.2 append_event +- Receipt ref: runx:receipt:sha256:9222ecb7a2f81d5542ac095e6f49c5dd97d8ba0127275f50f4bb808f9822d896 + +## Validation +- Direct runner check for approve-with-conditions SLA gap input +- Direct runner check for rejection input covering data-handling floor and liability cap failures +- Direct runner check that missing policy fails before record write +- Docker Linux harness with runx-cli 0.6.14 +- Harness cases: approve-with-conditions-sla-gap, reject-unbounded-liability-data-floor, missing-policy-stop +- Harness assertion errors: 0 +- Post-publish dogfood run sealed a receipt +- runx verify returned valid true for the dogfood receipt with the trusted demo public key + +## How to install and run +Run: + +``bash +runx add rohitmulani63-ops/vendor-risk-review@sha-a4f5ecb0c7a2 --registry https://api.runx.ai +runx skill rohitmulani63-ops/vendor-risk-review@sha-a4f5ecb0c7a2 --registry https://api.runx.ai judge -i contract_text='...' -i data_source_ref='...' -i store_id='...' --input-json vendor_context='{...}' --input-json policy='{...}' --input-json prior_risk_record='{...}' -j +`` + +## Why this is useful +A reviewer can inspect the named policy fields behind each decision, the before/after data-store versions, the idempotency key, and the sealed receipt. Future operator runs can treat rejected vendors as durable risk memory instead of reconsidering the same unsafe vendor from scratch. diff --git a/skills/vendor-risk-review/evidence/verification.json b/skills/vendor-risk-review/evidence/verification.json new file mode 100644 index 000000000..b5599ebfc --- /dev/null +++ b/skills/vendor-risk-review/evidence/verification.json @@ -0,0 +1,26 @@ +{ + "schema": "runx.verify_verdict.v1", + "receipt_id": "sha256:9222ecb7a2f81d5542ac095e6f49c5dd97d8ba0127275f50f4bb808f9822d896", + "valid": true, + "digest": { + "status": "valid", + "expected": "sha256:e0c87f6fd84049a8aff2ac4cec38028f1832fcca53c468f89508d7772375c8de", + "actual": "sha256:e0c87f6fd84049a8aff2ac4cec38028f1832fcca53c468f89508d7772375c8de" + }, + "content_address": { + "status": "valid", + "expected": "sha256:9222ecb7a2f81d5542ac095e6f49c5dd97d8ba0127275f50f4bb808f9822d896", + "actual": "sha256:9222ecb7a2f81d5542ac095e6f49c5dd97d8ba0127275f50f4bb808f9822d896" + }, + "signature": { + "mode": "production", + "status": "valid", + "kid": "runx-demo-key" + }, + "lineage": { + "status": "unverified", + "message": "single receipt verification cannot prove receipt-tree lineage" + }, + "findings": [] +} + diff --git a/vendor-risk-review-publish-output.json b/vendor-risk-review-publish-output.json new file mode 100644 index 0000000000000000000000000000000000000000..00e5ca3d2710fbb9590a57e622a20e1af8ee7ecc GIT binary patch literal 1732 zcmdUw+fD*O42FAc;yVzom6M#Wd=syR-|dFsin;+#S2T$Cqz4IB|dz*T_%$`4Wb{2!x2<@CxOG~#_ zSNIs&-0MzCR)fx0xv-S&hI7K3fx+RSKs>>dB5{^{vwS?xvd2@pGf>YNcdYUd@|59q z0w=cxvCYAivg_K7T_ah6sBRBnO3_(@`I=9en|aD1n<9NYf_RA}MV|e_-~L@G?htzl zvIUk3+<(U;&5Q`jH@hYa5iv#9wy||M6LZrI<{rG(k@ym<=oWQv1+th-X*AK6Eyveo z^jXK)>>){5+n(3-(jMFv=X-ozym#P6Ht-q^)&ci128;L|R{0oUQF@vav?X+qs2jXXE?J*l3|FUxT4-CWj+n&l zBkg+42HFlPVivdW*TcwjkgKzEP^%yJt0^X?g|A#yZSH${*Xg_JnNo$}l?WYB=&9QG zdtt-=iHeKhCamY`j$6E+uCEHUr`{^ROck_phWcBGQ`AiLln-Opn~T@|Kdd wpQ2SahqvLsN4$FtW8&VBrTMlmzh*LIr^sNPT-MKa-_`BkMm6}l-32PlA8@S&#sB~S literal 0 HcmV?d00001