diff --git a/CHANGELOG.md b/CHANGELOG.md index 08897be5..39e8bcf1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). Thi ## [Unreleased] ### Added +- MPCC event contract v0.1 (conversation + trading event family): `ConversationEvent` (the canonical 26-field conversation-fabric event — causal parents, authority context with delegation chain, visibility scope, requested/approved/actual effect references, modality, speech act), `EffectRequest` / `EffectDecision` / `EffectRecord` (the requested → approved → actual → compensated effect lifecycle with idempotency keys, referencing `PolicyDecision` / `ExecutionDecision` rather than duplicating them), `NullAbsenceRecord` (12-kind null/absence taxonomy), and the trading families `MarketDataEvent`, `OrderIntent`, `ExecutionReport`, `PositionChange`, `ReconciliationRecord` as profiles of the ConversationEvent envelope (one shared envelope vocabulary; parity machine-enforced). Includes canonical examples telling one end-to-end governed trade story, negative conformance vectors (`fixtures/mpcc-event-contract/conformance.json`), a `validate-mpcc-event-examples` target, and a normative spec note (`specs/mpcc-event-contract.md`). Provenance: SocioProphet/profit-mpcc, hardened to the policy-integrity tranche-0001 strictness bar. - Epistemic Assay contracts: `ReasoningAssay` (a typed verdict on a claim over five orthogonal axes — method, binding, verifier, agreement, authority — whose `ok`/`sad`/`bad` `projectedState` is a render-time projection, not a stored scalar) and `AssayStandard` (a verifier's measured, versioned reliability — the calibration reference every assay must point at). Includes canonical `ok`/`sad`/`bad` examples, ADR (`docs/adr/ADR-epistemic-assay-verdict-v0-1.md`), and a `validate-reasoning-examples` target that also enforces projection soundness (recomputes `assay()` from the stored axes and fails on drift) and retroactively brings the existing reasoning family under `make validate`. - `ReasoningReceipt.assay` (optional) — the reserved receipt landing spot for the Assay: a render-time, run-level epistemic summary that references its run's `ReasoningAssay` records by URN (`assayRefs`) and surfaces the projected `overallState`, weakest-link `binding`, verifier `calibrationRef`, per-state `counts`, and `projectedAt`. A cache of the referenced verdicts, not authoritative over them — re-projectable when an `AssayStandard` improves. Backward-compatible (optional field). The assay's `authority` axis continues to mirror `EventEnvelope.actor`/`integrity`, so the envelope needs no structural change. - Onboarding control-plane contract family: `WorkspaceScope`, `TrustMode`, `CapabilityPack`, `ConnectorActionScope`, `AutomationTemplate`, and `OnboardingReceipt`, with canonical first-run examples, semantic vocabulary seed, ADR, and `validate-onboarding-examples` validation target. diff --git a/Makefile b/Makefile index ca530608..d07b4617 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,12 @@ -.PHONY: validate validate-control-plane-examples validate-nlboot-examples validate-lattice-data-governai-examples validate-ops-history-examples validate-runtime-observability-examples validate-interpretability-examples validate-lifecycle-boundary-examples validate-svf-contracts validate-sync-cycle-receipts validate-onboarding-examples validate-runtime-causality-examples validate-agentic-os-examples validate-triparty-examples validate-labor-market-examples validate-supply-chain-risk-examples validate-reasoning-examples +.PHONY: validate validate-control-plane-examples validate-nlboot-examples validate-lattice-data-governai-examples validate-ops-history-examples validate-runtime-observability-examples validate-interpretability-examples validate-lifecycle-boundary-examples validate-svf-contracts validate-sync-cycle-receipts validate-onboarding-examples validate-runtime-causality-examples validate-agentic-os-examples validate-triparty-examples validate-labor-market-examples validate-supply-chain-risk-examples validate-reasoning-examples validate-mpcc-event-examples -validate: validate-control-plane-examples validate-nlboot-examples validate-lattice-data-governai-examples validate-ops-history-examples validate-runtime-observability-examples validate-interpretability-examples validate-lifecycle-boundary-examples validate-svf-contracts validate-sync-cycle-receipts validate-onboarding-examples validate-runtime-causality-examples validate-agentic-os-examples validate-triparty-examples validate-labor-market-examples validate-supply-chain-risk-examples validate-reasoning-examples +validate: validate-control-plane-examples validate-nlboot-examples validate-lattice-data-governai-examples validate-ops-history-examples validate-runtime-observability-examples validate-interpretability-examples validate-lifecycle-boundary-examples validate-svf-contracts validate-sync-cycle-receipts validate-onboarding-examples validate-runtime-causality-examples validate-agentic-os-examples validate-triparty-examples validate-labor-market-examples validate-supply-chain-risk-examples validate-reasoning-examples validate-mpcc-event-examples @echo "OK: validate" +validate-mpcc-event-examples: + python3 -m pip install --user jsonschema >/dev/null + python3 tools/validate_mpcc_event_examples.py + validate-reasoning-examples: python3 -m pip install --user jsonschema >/dev/null python3 tools/validate_reasoning_examples.py diff --git a/examples/conversation_event.json b/examples/conversation_event.json new file mode 100644 index 00000000..d2efc0ec --- /dev/null +++ b/examples/conversation_event.json @@ -0,0 +1,70 @@ +{ + "id": "urn:srcos:conversation-event:desk_alpha_msg_000117", + "type": "ConversationEvent", + "specVersion": "0.1.0", + "actorRef": "urn:srcos:agent:strategy_momentum_7", + "authorityContext": { + "principal": "agent", + "delegationChain": ["urn:srcos:subject:desk_operator_1"], + "capabilities": ["trading.order.propose"], + "approvalState": "pending", + "policyContext": ["urn:srcos:policy:desk_alpha_limits"] + }, + "workspaceRef": "urn:srcos:workspace:trading_demo", + "branchRef": "urn:srcos:branch:desk_alpha_main", + "visibilityScope": ["shared"], + "wallTime": "2026-07-28T13:30:00.250Z", + "logicalTime": 117, + "causalParents": ["urn:srcos:market-data-event:xnas_acme_quote_000042"], + "traceContext": { + "traceId": "trace_desk_alpha_0001", + "spanId": "span_proposal_0117", + "baggage": { + "desk": "alpha" + } + }, + "modality": "tool_call", + "codec": "utf-8", + "rawPayload": { + "tool": "propose_order", + "arguments": { + "instrument": "ACME.XNAS", + "side": "buy", + "quantity": "100", + "limitPrice": "184.20" + } + }, + "canonicalPayload": { + "normalizationRegime": "mpcc-canonical-json-v0", + "proposal": { + "instrument": "ACME.XNAS", + "side": "buy", + "quantity": "100", + "limitPrice": "184.20", + "rationale": "momentum breakout above vwap" + } + }, + "signalStats": { + "tokenCount": 42, + "proposalConfidence": 0.82 + }, + "speechAct": "request", + "claims": [], + "entities": ["ACME.XNAS"], + "topics": ["urn:srcos:topic:acme_momentum"], + "requestedEffects": ["urn:srcos:effect:place_order_acme_000117"], + "approvedEffects": ["urn:srcos:effect-decision:approve_place_order_acme_000117"], + "actualEffects": ["urn:srcos:effect-record:place_order_acme_000117_r1"], + "provenanceLinks": [ + { + "rel": "source_feed", + "ref": "urn:srcos:market-data-event:xnas_acme_quote_000042" + } + ], + "policyLabels": ["desk:alpha"], + "riskLabels": [], + "evaluationMetrics": { + "latencyMs": 12 + }, + "interactionEventRefs": ["urn:srcos:interaction-event:noetica_msg_9f3a"] +} diff --git a/examples/effect_decision.json b/examples/effect_decision.json new file mode 100644 index 00000000..7698d909 --- /dev/null +++ b/examples/effect_decision.json @@ -0,0 +1,43 @@ +{ + "id": "urn:srcos:effect-decision:approve_place_order_acme_000117", + "type": "EffectDecision", + "specVersion": "0.1.0", + "effectRequestRef": "urn:srcos:effect:place_order_acme_000117", + "subjectEventRef": "urn:srcos:conversation-event:desk_alpha_msg_000117", + "decision": "approved", + "decidedByActorRef": "urn:srcos:subject:desk_operator_1", + "authorityContext": { + "principal": "user", + "delegationChain": [], + "capabilities": ["trading.order.approve"], + "approvalState": "approved", + "policyContext": ["urn:srcos:policy:desk_alpha_limits"] + }, + "rationale": "Within desk limits; quantity narrowed from 100 to 50 under the open-notional exposure cap.", + "ruleRefs": ["rule:desk_alpha:max_open_notional"], + "policyDecisionRefs": ["urn:srcos:decision:pf_desk_alpha_7d21"], + "executionDecisionRef": null, + "approvedEffect": { + "effectKind": "execute", + "capability": "trading.order.place", + "target": { + "kind": "venue_order_gateway", + "identifier": "XNAS", + "location": "us-east" + }, + "parameters": { + "instrument": "ACME.XNAS", + "side": "buy", + "quantity": "50", + "orderType": "limit", + "limitPrice": "184.20", + "timeInForce": "day" + } + }, + "evidenceRefs": ["urn:srcos:market-data-event:xnas_acme_quote_000042"], + "policyLabels": ["desk:alpha"], + "riskLabels": ["notional:small"], + "decidedAt": "2026-07-28T13:30:02.000Z", + "expiresAt": "2026-07-28T13:35:00.000Z", + "decisionHash": "sha256:9f2b4a8c1d3e5f7a9b1c3d5e7f9a1b3c5d7e9f1a3b5c7d9e1f3a5b7c9d1e3f5a" +} diff --git a/examples/effect_record.json b/examples/effect_record.json new file mode 100644 index 00000000..5f87e8d8 --- /dev/null +++ b/examples/effect_record.json @@ -0,0 +1,34 @@ +{ + "id": "urn:srcos:effect-record:place_order_acme_000117_r1", + "type": "EffectRecord", + "specVersion": "0.1.0", + "effectRequestRef": "urn:srcos:effect:place_order_acme_000117", + "effectDecisionRef": "urn:srcos:effect-decision:approve_place_order_acme_000117", + "autonomousPolicyRef": null, + "status": "executed", + "executorRef": "urn:srcos:agent:execution_gateway_1", + "executionContext": { + "gateway": "fix44", + "session": "XNAS_A" + }, + "actualEffect": { + "effectKind": "execute", + "capability": "trading.order.place", + "venueOrderRef": "XNAS-99120031", + "filledQuantity": "50", + "averagePrice": "184.18" + }, + "idempotencyKey": "place_order_acme_000117_v1", + "startedAt": "2026-07-28T13:30:02.150Z", + "completedAt": "2026-07-28T13:30:02.500Z", + "resultRefs": ["urn:srcos:execution-report:xnas_fill_88231"], + "compensationRefs": [], + "provenanceLinks": [ + { + "rel": "executes", + "ref": "urn:srcos:effect:place_order_acme_000117" + } + ], + "policyLabels": ["desk:alpha"], + "riskLabels": [] +} diff --git a/examples/effect_request.json b/examples/effect_request.json new file mode 100644 index 00000000..4fc5a32c --- /dev/null +++ b/examples/effect_request.json @@ -0,0 +1,30 @@ +{ + "id": "urn:srcos:effect:place_order_acme_000117", + "type": "EffectRequest", + "specVersion": "0.1.0", + "requestedByEventRef": "urn:srcos:conversation-event:desk_alpha_msg_000117", + "requestedByActorRef": "urn:srcos:agent:strategy_momentum_7", + "workspaceRef": "urn:srcos:workspace:trading_demo", + "branchRef": "urn:srcos:branch:desk_alpha_main", + "effectKind": "execute", + "capability": "trading.order.place", + "target": { + "kind": "venue_order_gateway", + "identifier": "XNAS", + "location": "us-east" + }, + "parameters": { + "instrument": "ACME.XNAS", + "side": "buy", + "quantity": "100", + "orderType": "limit", + "limitPrice": "184.20", + "timeInForce": "day" + }, + "idempotencyKey": "place_order_acme_000117_v1", + "requiresHumanApproval": true, + "policyLabels": ["desk:alpha"], + "riskLabels": ["notional:small"], + "requestedAt": "2026-07-28T13:30:00.300Z", + "notes": "Momentum entry proposed by strategy_momentum_7; desk approval required by policy." +} diff --git a/examples/execution_report.json b/examples/execution_report.json new file mode 100644 index 00000000..6511d303 --- /dev/null +++ b/examples/execution_report.json @@ -0,0 +1,32 @@ +{ + "id": "urn:srcos:execution-report:xnas_fill_88231", + "type": "ExecutionReport", + "specVersion": "0.1.0", + "wallTime": "2026-07-28T13:30:02.480Z", + "logicalTime": 119, + "orderIntentRef": "urn:srcos:order-intent:acme_buy_000117", + "venueRef": "XNAS", + "reportKind": "fill", + "orderRef": "XNAS-99120031", + "fillQuantity": "50", + "fillPrice": "184.18", + "remainingQuantity": "0", + "sequenceRef": 4711187, + "causalParents": ["urn:srcos:order-intent:acme_buy_000117"], + "traceContext": { + "traceId": "trace_desk_alpha_0001", + "spanId": "span_report_0119" + }, + "visibilityScope": ["shared"], + "provenanceLinks": [ + { + "rel": "venue_report", + "ref": "drop_copy:xnas:session_a:88231" + } + ], + "policyLabels": ["desk:alpha"], + "riskLabels": [], + "details": { + "liquidityFlag": "removed" + } +} diff --git a/examples/market_data_event.json b/examples/market_data_event.json new file mode 100644 index 00000000..42c69b41 --- /dev/null +++ b/examples/market_data_event.json @@ -0,0 +1,41 @@ +{ + "id": "urn:srcos:market-data-event:xnas_acme_quote_000042", + "type": "MarketDataEvent", + "specVersion": "0.1.0", + "wallTime": "2026-07-28T13:30:00.125Z", + "logicalTime": 42, + "instrumentRef": "ACME.XNAS", + "venueRef": "XNAS", + "eventKind": "quote", + "sequenceRef": 4711042, + "feedRef": "feed_xnas_l1_a", + "rawPayload": { + "b": "184.15", + "a": "184.20", + "bs": 300, + "as": 500 + }, + "canonicalPayload": { + "normalizationRegime": "mpcc-canonical-quote-v0", + "bidPrice": "184.15", + "askPrice": "184.20", + "bidSize": "300", + "askSize": "500" + }, + "nullAbsenceRef": null, + "qualityFlags": [], + "causalParents": [], + "traceContext": { + "traceId": "trace_desk_alpha_0001", + "spanId": "span_feed_0042" + }, + "visibilityScope": ["shared"], + "provenanceLinks": [ + { + "rel": "source_feed", + "ref": "feed_xnas_l1_a" + } + ], + "policyLabels": ["market-data:realtime"], + "riskLabels": [] +} diff --git a/examples/null_absence_record.json b/examples/null_absence_record.json new file mode 100644 index 00000000..687cbd0f --- /dev/null +++ b/examples/null_absence_record.json @@ -0,0 +1,22 @@ +{ + "id": "urn:srcos:null-absence:xnas_feed_gap_000043", + "type": "NullAbsenceRecord", + "specVersion": "0.1.0", + "kind": "no_event_observed", + "observedAt": "2026-07-28T13:31:15.000Z", + "relatedEventRef": "urn:srcos:market-data-event:xnas_acme_quote_000042", + "relatedBranchRef": "urn:srcos:branch:desk_alpha_main", + "relatedWorkspaceRef": "urn:srcos:workspace:trading_demo", + "causalNotes": "Feed sequence gap after 4711042; venue heartbeat missed for 75 seconds. Recorded as no_event_observed, not transport_failure, until the feed handler attributes a cause.", + "policyLabels": ["feed:gap"], + "provenanceLinks": [ + { + "rel": "detected_by", + "ref": "monitor:xnas_l1_a" + } + ], + "details": { + "expectedNextSequence": 4711043, + "gapSeconds": 75 + } +} diff --git a/examples/order_intent.json b/examples/order_intent.json new file mode 100644 index 00000000..41ac39dc --- /dev/null +++ b/examples/order_intent.json @@ -0,0 +1,46 @@ +{ + "id": "urn:srcos:order-intent:acme_buy_000117", + "type": "OrderIntent", + "specVersion": "0.1.0", + "actorRef": "urn:srcos:agent:strategy_momentum_7", + "authorityContext": { + "principal": "agent", + "delegationChain": ["urn:srcos:subject:desk_operator_1"], + "capabilities": ["trading.order.propose"], + "approvalState": "approved", + "policyContext": ["urn:srcos:policy:desk_alpha_limits"] + }, + "workspaceRef": "urn:srcos:workspace:trading_demo", + "branchRef": "urn:srcos:branch:desk_alpha_main", + "visibilityScope": ["shared"], + "wallTime": "2026-07-28T13:30:02.100Z", + "logicalTime": 118, + "causalParents": [ + "urn:srcos:conversation-event:desk_alpha_msg_000117", + "urn:srcos:market-data-event:xnas_acme_quote_000042" + ], + "traceContext": { + "traceId": "trace_desk_alpha_0001", + "spanId": "span_intent_0118" + }, + "strategyRef": "strat_momentum_7", + "instrumentRef": "ACME.XNAS", + "venueRef": "XNAS", + "intentKind": "new", + "side": "buy", + "orderType": "limit", + "quantity": "50", + "price": "184.20", + "timeInForce": "day", + "requestedEffectRef": "urn:srcos:effect:place_order_acme_000117", + "signalRefs": ["urn:srcos:market-data-event:xnas_acme_quote_000042"], + "provenanceLinks": [ + { + "rel": "approved_by", + "ref": "urn:srcos:effect-decision:approve_place_order_acme_000117" + } + ], + "policyLabels": ["desk:alpha"], + "riskLabels": ["notional:small"], + "notes": "Quantity reflects the approved (narrowed) effect shape, not the original proposal." +} diff --git a/examples/position_change.json b/examples/position_change.json new file mode 100644 index 00000000..e11c3347 --- /dev/null +++ b/examples/position_change.json @@ -0,0 +1,29 @@ +{ + "id": "urn:srcos:position-change:acme_desk_alpha_000120", + "type": "PositionChange", + "specVersion": "0.1.0", + "wallTime": "2026-07-28T13:30:02.600Z", + "logicalTime": 120, + "instrumentRef": "ACME.XNAS", + "accountRef": "acct_desk_alpha", + "positionDelta": "50", + "cashDelta": "-9209.00", + "feeDelta": "-1.25", + "realizedPnlDelta": null, + "unrealizedPnlSnapshot": "12.50", + "sourceExecutionRefs": ["urn:srcos:execution-report:xnas_fill_88231"], + "causalParents": ["urn:srcos:execution-report:xnas_fill_88231"], + "traceContext": { + "traceId": "trace_desk_alpha_0001", + "spanId": "span_position_0120" + }, + "visibilityScope": ["shared"], + "provenanceLinks": [ + { + "rel": "derived_from", + "ref": "urn:srcos:execution-report:xnas_fill_88231" + } + ], + "policyLabels": ["desk:alpha"], + "riskLabels": [] +} diff --git a/examples/reconciliation_record.json b/examples/reconciliation_record.json new file mode 100644 index 00000000..d966a9a3 --- /dev/null +++ b/examples/reconciliation_record.json @@ -0,0 +1,35 @@ +{ + "id": "urn:srcos:reconciliation-record:eod_orders_2026-07-28_x1", + "type": "ReconciliationRecord", + "specVersion": "0.1.0", + "wallTime": "2026-07-28T20:10:00.000Z", + "logicalTime": 512, + "scopeKind": "order", + "scopeRef": "urn:srcos:order-intent:acme_buy_000117", + "status": "matched", + "expectedValue": { + "filledQuantity": "50", + "averagePrice": "184.18" + }, + "observedValue": { + "filledQuantity": "50", + "averagePrice": "184.18" + }, + "compensationRefs": [], + "causalParents": [ + "urn:srcos:execution-report:xnas_fill_88231", + "urn:srcos:position-change:acme_desk_alpha_000120" + ], + "traceContext": { + "traceId": "trace_desk_alpha_eod_0001" + }, + "visibilityScope": ["shared"], + "provenanceLinks": [ + { + "rel": "reconciles", + "ref": "urn:srcos:order-intent:acme_buy_000117" + } + ], + "policyLabels": ["reconciliation:eod"], + "riskLabels": [] +} diff --git a/fixtures/mpcc-event-contract/conformance.json b/fixtures/mpcc-event-contract/conformance.json new file mode 100644 index 00000000..01186101 --- /dev/null +++ b/fixtures/mpcc-event-contract/conformance.json @@ -0,0 +1,106 @@ +{ + "description": "Negative conformance vectors for the MPCC event contract v0.1. Each document MUST FAIL validation against its schema for the stated reason; tools/validate_mpcc_event_examples.py enforces this. These vectors keep the tranche-0001 strictness bar executable: closed objects, pinned contract version, mandatory idempotency keys, a closed absence taxonomy, and the effect-record authority invariant.", + "cases": [ + { + "schema": "ConversationEvent.json", + "reason": "Unknown top-level property must be rejected (additionalProperties: false).", + "document": { + "id": "urn:srcos:conversation-event:neg_unknown_prop", + "type": "ConversationEvent", + "specVersion": "0.1.0", + "actorRef": "urn:srcos:agent:strategy_momentum_7", + "workspaceRef": "urn:srcos:workspace:trading_demo", + "branchRef": "urn:srcos:branch:desk_alpha_main", + "wallTime": "2026-07-28T13:30:00.250Z", + "logicalTime": 117, + "modality": "text", + "rawPayload": "hello", + "canonicalPayload": "hello", + "surprise": true + } + }, + { + "schema": "EffectRecord.json", + "reason": "Authority invariant: a record with neither effectDecisionRef nor autonomousPolicyRef must fail the anyOf.", + "document": { + "id": "urn:srcos:effect-record:neg_no_authority_basis", + "type": "EffectRecord", + "specVersion": "0.1.0", + "effectRequestRef": "urn:srcos:effect:place_order_acme_000117", + "effectDecisionRef": null, + "autonomousPolicyRef": null, + "status": "executed", + "idempotencyKey": "place_order_acme_000117_v1", + "resultRefs": [], + "policyLabels": [], + "riskLabels": [] + } + }, + { + "schema": "MarketDataEvent.json", + "reason": "specVersion is pinned: any value other than the 0.1.0 const must fail.", + "document": { + "id": "urn:srcos:market-data-event:neg_wrong_spec_version", + "type": "MarketDataEvent", + "specVersion": "0.2.0", + "wallTime": "2026-07-28T13:30:00.125Z", + "instrumentRef": "ACME.XNAS", + "venueRef": "XNAS", + "eventKind": "quote", + "rawPayload": {}, + "canonicalPayload": {} + } + }, + { + "schema": "EffectRequest.json", + "reason": "idempotencyKey is a required replay guard on every side-effecting request.", + "document": { + "id": "urn:srcos:effect:neg_missing_idempotency_key", + "type": "EffectRequest", + "specVersion": "0.1.0", + "requestedByEventRef": "urn:srcos:conversation-event:desk_alpha_msg_000117", + "effectKind": "execute", + "capability": "trading.order.place", + "target": { + "kind": "venue_order_gateway", + "identifier": "XNAS" + }, + "parameters": {}, + "requiresHumanApproval": true, + "policyLabels": [], + "riskLabels": [], + "requestedAt": "2026-07-28T13:30:00.300Z" + } + }, + { + "schema": "NullAbsenceRecord.json", + "reason": "The 12-kind absence taxonomy is closed: kinds outside the enum must fail.", + "document": { + "id": "urn:srcos:null-absence:neg_unknown_kind", + "type": "NullAbsenceRecord", + "specVersion": "0.1.0", + "kind": "mysterious_silence", + "observedAt": "2026-07-28T13:31:15.000Z" + } + }, + { + "schema": "OrderIntent.json", + "reason": "authorityContext, when present, must carry all five sub-fields so authority is never ambiguous.", + "document": { + "id": "urn:srcos:order-intent:neg_partial_authority_context", + "type": "OrderIntent", + "specVersion": "0.1.0", + "actorRef": "urn:srcos:agent:strategy_momentum_7", + "wallTime": "2026-07-28T13:30:02.100Z", + "strategyRef": "strat_momentum_7", + "instrumentRef": "ACME.XNAS", + "intentKind": "new", + "side": "buy", + "quantity": "50", + "authorityContext": { + "principal": "agent" + } + } + } + ] +} diff --git a/schemas/ConversationEvent.json b/schemas/ConversationEvent.json new file mode 100644 index 00000000..bac832a8 --- /dev/null +++ b/schemas/ConversationEvent.json @@ -0,0 +1,388 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://schemas.srcos.ai/v2/ConversationEvent.json", + "title": "ConversationEvent", + "description": "The canonical 26-field MPCC conversation-fabric event: an immutable, causally ordered record of one communicative act (message, tool call, tool result, approval, redaction, summary, or artifact) inside a branched, policy-governed workspace. This is a domain object — it rides inside EventEnvelope on AsyncAPI channels rather than replacing it — and its identity/causality/governance envelope is the single shared vocabulary that the trading event families (MarketDataEvent, OrderIntent, ExecutionReport, PositionChange, ReconciliationRecord) profile. Provenance: SocioProphet/profit-mpcc schemas/event.schema.json and docs/canonical-event-schema.md, hardened to the policy-integrity tranche-0001 strictness bar.", + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "type", + "specVersion", + "actorRef", + "workspaceRef", + "branchRef", + "wallTime", + "logicalTime", + "modality", + "rawPayload", + "canonicalPayload" + ], + "properties": { + "id": { + "type": "string", + "pattern": "^urn:srcos:conversation-event:[A-Za-z0-9._~-]+$", + "description": "Stable URN identifier. Pattern: urn:srcos:conversation-event:. Invariant: event identity is stable and never reused." + }, + "type": { + "const": "ConversationEvent", + "description": "Discriminator constant — always \"ConversationEvent\"." + }, + "specVersion": { + "const": "0.1.0", + "description": "MPCC event-contract version, pinned per the profit-mpcc policy-integrity tranche-0001 discipline. This family is versioned independently of the v2 metadata-plane schemas; changing this const is a contract change." + }, + "actorRef": { + "type": "string", + "minLength": 1, + "description": "Authority reference of the actor that emitted this event (human subject, agent, service, or system). Subject or Agent Registry URNs are recommended; free-form identifiers are permitted in v0.1." + }, + "authorityContext": { + "type": "object", + "additionalProperties": false, + "required": [ + "principal", + "delegationChain", + "capabilities", + "approvalState", + "policyContext" + ], + "properties": { + "principal": { + "type": "string", + "enum": [ + "user", + "agent", + "system", + "tool" + ], + "description": "Class of principal exercising authority for this act." + }, + "delegationChain": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "Actor reference of one hop in the delegation chain." + }, + "default": [], + "description": "Ordered delegation chain from the root principal to the acting principal; empty when the principal acts directly." + }, + "capabilities": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "A capability identifier." + }, + "uniqueItems": true, + "default": [], + "description": "Capability identifiers the actor claims for this act." + }, + "approvalState": { + "type": "string", + "enum": [ + "none", + "pending", + "approved", + "denied" + ], + "description": "Approval state of the authority under which this act was performed." + }, + "policyContext": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "A policy reference." + }, + "uniqueItems": true, + "default": [], + "description": "Policy references in force when this act was performed." + } + }, + "description": "Authority context under which this act was performed: principal class, delegation chain, claimed capabilities, approval state, and policy context. All five sub-fields are required when the context is present so that authority is never ambiguous." + }, + "workspaceRef": { + "type": "string", + "minLength": 1, + "description": "Workspace scope this event belongs to. Free-form stable reference in v0.1; a typed workspace URN may narrow this in a later minor version." + }, + "branchRef": { + "type": "string", + "minLength": 1, + "description": "Conversation-fabric branch this event was appended to. Free-form stable reference in v0.1, pending adoption of the profit-mpcc branch-id contract." + }, + "visibilityScope": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "A visibility scope label such as private, shared, public, or an audience URN." + }, + "uniqueItems": true, + "default": [], + "description": "Visibility scopes governing who may observe this event (e.g. private, shared, public, or audience URNs)." + }, + "wallTime": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 wall-clock time of the event as observed by the producer. Family profiles map their primary domain timestamp onto this field." + }, + "logicalTime": { + "type": [ + "integer", + "string" + ], + "minimum": 0, + "minLength": 1, + "description": "Producer-scoped logical clock: a non-negative integer for scalar (Lamport-style) clocks, or a non-empty string for encoded vector/hybrid clocks. Invariant: causalParents must never point forward in logical time." + }, + "causalParents": { + "type": "array", + "items": { + "type": "string", + "pattern": "^urn:srcos:[a-z0-9-]+:[A-Za-z0-9._~-]+$", + "description": "URN of a causally preceding event in any MPCC event family." + }, + "uniqueItems": true, + "default": [], + "description": "Event URNs this event causally depends on. Cross-family parents are permitted (e.g. an OrderIntent caused by a MarketDataEvent). Invariant: parents must never point forward in logical time." + }, + "traceContext": { + "type": "object", + "additionalProperties": false, + "required": [ + "traceId" + ], + "properties": { + "traceId": { + "type": "string", + "minLength": 1, + "description": "Distributed trace identifier." + }, + "spanId": { + "type": "string", + "minLength": 1, + "description": "Span identifier within the trace." + }, + "baggage": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "String-valued trace baggage entries." + } + }, + "description": "Optional distributed-tracing correlation context." + }, + "modality": { + "type": "string", + "enum": [ + "text", + "tool_call", + "tool_result", + "approval", + "redaction", + "summary", + "artifact" + ], + "description": "Communicative modality of this event." + }, + "codec": { + "type": "string", + "minLength": 1, + "description": "Encoding of rawPayload (e.g. utf-8) when payload bytes require codec interpretation." + }, + "rawPayload": { + "type": [ + "object", + "array", + "string", + "number", + "boolean", + "null" + ], + "description": "The payload exactly as produced, before any normalization. May be any JSON value; use NullAbsenceRecord to type genuine absences rather than overloading null." + }, + "canonicalPayload": { + "type": [ + "object", + "array", + "string", + "number", + "boolean", + "null" + ], + "description": "The normalized payload. Invariant: the canonical form must declare the normalization regime used, and any lossy derived field must carry provenance and faithfulness metadata (via provenanceLinks)." + }, + "signalStats": { + "type": "object", + "additionalProperties": { + "type": [ + "string", + "number", + "integer", + "boolean", + "null" + ] + }, + "description": "Optional scalar signal statistics computed over the payload (e.g. token counts, confidence scores)." + }, + "speechAct": { + "type": "string", + "enum": [ + "assert", + "ask", + "request", + "command", + "delegate", + "promise", + "refuse", + "retract", + "quote", + "speculate" + ], + "description": "Speech-act classification of this event." + }, + "claims": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "Stable reference to a claim object." + }, + "uniqueItems": true, + "default": [], + "description": "References to claim objects asserted or discussed by this event. Typed claim contracts are future work; v0.1 uses stable references rather than embedded anonymous objects." + }, + "entities": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "Stable reference to an entity." + }, + "uniqueItems": true, + "default": [], + "description": "References to entities mentioned or acted on by this event. Typed entity contracts are future work; v0.1 uses stable references." + }, + "topics": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "Stable reference to a topic." + }, + "uniqueItems": true, + "default": [], + "description": "References to topics this event participates in. Topic URNs (urn:srcos:topic:) may be used where the fog-layer Topic applies." + }, + "requestedEffects": { + "type": "array", + "items": { + "type": "string", + "pattern": "^urn:srcos:effect:[A-Za-z0-9._~-]+$", + "description": "EffectRequest URN." + }, + "uniqueItems": true, + "default": [], + "description": "EffectRequest URNs proposed by this event. A requested effect is a proposal only — not permission, not execution, not evidence that anything happened." + }, + "approvedEffects": { + "type": "array", + "items": { + "type": "string", + "pattern": "^urn:srcos:effect-decision:[A-Za-z0-9._~-]+$", + "description": "EffectDecision URN." + }, + "uniqueItems": true, + "default": [], + "description": "EffectDecision URNs governing this event's requested effects. Invariant: approved effects must be a subset of requested effects modulo explicit policy rewriting." + }, + "actualEffects": { + "type": "array", + "items": { + "type": "string", + "pattern": "^urn:srcos:effect-record:[A-Za-z0-9._~-]+$", + "description": "EffectRecord URN." + }, + "uniqueItems": true, + "default": [], + "description": "EffectRecord URNs recording what actually happened. Invariant: every actual effect must be traceable to an explicit approval or to a first-class autonomous policy path." + }, + "provenanceLinks": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "rel", + "ref" + ], + "properties": { + "rel": { + "type": "string", + "minLength": 1, + "description": "Relation of the linked artifact to this event (e.g. derived_from, quotes, source_feed)." + }, + "ref": { + "type": "string", + "minLength": 1, + "description": "URN or stable reference of the linked artifact." + } + }, + "description": "A single provenance link (relation + reference)." + }, + "uniqueItems": true, + "default": [], + "description": "Typed provenance links from this event to upstream artifacts, sources, or records." + }, + "policyLabels": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "A policy label." + }, + "uniqueItems": true, + "default": [], + "description": "Policy labels attached to this event by producers or the policy fabric." + }, + "riskLabels": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "A risk label." + }, + "uniqueItems": true, + "default": [], + "description": "Risk labels attached to this event by producers or risk controls." + }, + "evaluationMetrics": { + "type": "object", + "additionalProperties": { + "type": [ + "string", + "number", + "integer", + "boolean", + "null" + ] + }, + "description": "Optional scalar evaluation metrics attached by downstream evaluators." + }, + "interactionEventRefs": { + "type": "array", + "items": { + "type": "string", + "pattern": "^urn:srcos:interaction-event:[A-Za-z0-9._~-]+$", + "description": "SourceOSInteractionEvent URN." + }, + "uniqueItems": true, + "default": [], + "description": "SourceOSInteractionEvent URNs that surfaced or rendered this fabric event. Bridges surface telemetry to the canonical conversation fabric by reference instead of duplicating it." + } + } +} diff --git a/schemas/EffectDecision.json b/schemas/EffectDecision.json new file mode 100644 index 00000000..7919d21c --- /dev/null +++ b/schemas/EffectDecision.json @@ -0,0 +1,292 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://schemas.srcos.ai/v2/EffectDecision.json", + "title": "EffectDecision", + "description": "An authority-bearing decision over an EffectRequest: approved, denied, deferred, expired, or revoked — distinct states that must never be conflated. Carries who decided, under what authority context, on what policy basis, and (when approved) the exact effect shape approved, which may be a policy-narrowed rewrite of the request. Complements rather than duplicates PolicyDecision (referenced via policyDecisionRefs as the policy-evaluation basis) and ExecutionDecision (referenced via executionDecisionRef when an agent-session gate was involved): neither existing type carries the approved effect shape or the requested → approved → actual chain. Provenance: SocioProphet/profit-mpcc schemas/approval-event.schema.json, docs/effect-approval-semantics.md, and exports/policy-integrity/tranche-0001/decision.schema.snapshot.json, hardened to the tranche-0001 strictness bar.", + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "type", + "specVersion", + "effectRequestRef", + "decision", + "decidedByActorRef", + "authorityContext", + "rationale", + "evidenceRefs", + "policyLabels", + "riskLabels", + "decidedAt", + "decisionHash" + ], + "properties": { + "id": { + "type": "string", + "pattern": "^urn:srcos:effect-decision:[A-Za-z0-9._~-]+$", + "description": "Stable URN identifier. Pattern: urn:srcos:effect-decision:" + }, + "type": { + "const": "EffectDecision", + "description": "Discriminator constant — always \"EffectDecision\"." + }, + "specVersion": { + "const": "0.1.0", + "description": "MPCC event-contract version, pinned per the profit-mpcc policy-integrity tranche-0001 discipline. Changing this const is a contract change." + }, + "effectRequestRef": { + "type": "string", + "pattern": "^urn:srcos:effect:[A-Za-z0-9._~-]+$", + "description": "EffectRequest URN this decision governs." + }, + "subjectEventRef": { + "type": [ + "string", + "null" + ], + "pattern": "^urn:srcos:[a-z0-9-]+:[A-Za-z0-9._~-]+$", + "description": "URN of the MPCC event that carried the request, when known (typically a ConversationEvent or OrderIntent)." + }, + "decision": { + "type": "string", + "enum": [ + "approved", + "denied", + "deferred", + "expired", + "revoked" + ], + "description": "The decision outcome. Invariant: denial, expiration, and revocation are not the same state; deferral is a non-decision that keeps the request pending." + }, + "decidedByActorRef": { + "type": "string", + "minLength": 1, + "description": "Authority reference of the actor that made this decision. Approval must always carry who approved." + }, + "authorityContext": { + "type": "object", + "additionalProperties": false, + "required": [ + "principal", + "delegationChain", + "capabilities", + "approvalState", + "policyContext" + ], + "properties": { + "principal": { + "type": "string", + "enum": [ + "user", + "agent", + "system", + "tool" + ], + "description": "Class of principal exercising authority for this act." + }, + "delegationChain": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "Actor reference of one hop in the delegation chain." + }, + "default": [], + "description": "Ordered delegation chain from the root principal to the acting principal; empty when the principal acts directly." + }, + "capabilities": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "A capability identifier." + }, + "uniqueItems": true, + "default": [], + "description": "Capability identifiers the actor claims for this act." + }, + "approvalState": { + "type": "string", + "enum": [ + "none", + "pending", + "approved", + "denied" + ], + "description": "Approval state of the authority under which this act was performed." + }, + "policyContext": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "A policy reference." + }, + "uniqueItems": true, + "default": [], + "description": "Policy references in force when this act was performed." + } + }, + "description": "Authority context under which this act was performed: principal class, delegation chain, claimed capabilities, approval state, and policy context. All five sub-fields are required when the context is present so that authority is never ambiguous." + }, + "rationale": { + "type": "string", + "minLength": 1, + "description": "Human-readable explanation of why this decision was reached. Required and non-empty per the tranche-0001 bar." + }, + "ruleRefs": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "A rule reference." + }, + "uniqueItems": true, + "default": [], + "description": "References to the specific rules applied in reaching this decision." + }, + "policyDecisionRefs": { + "type": "array", + "items": { + "type": "string", + "pattern": "^urn:srcos:decision:", + "description": "PolicyDecision URN." + }, + "uniqueItems": true, + "default": [], + "description": "PolicyDecision URNs forming the policy-evaluation basis of this effect decision. References the existing PolicyDecision contract instead of duplicating it." + }, + "executionDecisionRef": { + "type": [ + "string", + "null" + ], + "pattern": "^urn:srcos:exec-decision:", + "description": "ExecutionDecision URN when an agent-session execution gate participated in this decision. References the existing ExecutionDecision contract instead of duplicating it." + }, + "approvedEffect": { + "type": [ + "object", + "null" + ], + "additionalProperties": false, + "required": [ + "effectKind", + "capability", + "target", + "parameters" + ], + "properties": { + "effectKind": { + "type": "string", + "enum": [ + "create", + "update", + "delete", + "retrieve", + "transform", + "notify", + "schedule", + "route", + "publish", + "redact", + "label", + "archive", + "execute", + "no_op", + "verify" + ], + "description": "Kind of state transition approved." + }, + "capability": { + "type": "string", + "minLength": 1, + "description": "Capability the approval covers." + }, + "target": { + "type": "object", + "additionalProperties": false, + "required": [ + "kind", + "identifier" + ], + "properties": { + "kind": { + "type": "string", + "minLength": 1, + "description": "Kind of target the approved effect operates on." + }, + "identifier": { + "type": "string", + "minLength": 1, + "description": "Stable identifier of the target." + }, + "location": { + "type": "string", + "minLength": 1, + "description": "Optional location or partition of the target." + } + }, + "description": "The object or surface the approved effect may act on." + }, + "parameters": { + "type": "object", + "additionalProperties": true, + "description": "The approved effect parameters — possibly narrowed from the request." + } + }, + "description": "The exact effect shape approved — possibly a policy-narrowed rewrite of the request. Invariant: must be a subset of the request modulo explicit policy rewriting. Semantically required when decision is \"approved\"; null otherwise." + }, + "evidenceRefs": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "An evidence reference." + }, + "uniqueItems": true, + "default": [], + "description": "References to the evidence considered in reaching this decision. Required (empty allowed) so deciders state their evidence posture explicitly, per the tranche-0001 bar." + }, + "policyLabels": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "A policy label." + }, + "uniqueItems": true, + "default": [], + "description": "Policy labels attached to this decision. Required (empty allowed) per the tranche-0001 bar." + }, + "riskLabels": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "A risk label." + }, + "uniqueItems": true, + "default": [], + "description": "Risk labels attached to this decision. Required (empty allowed) per the tranche-0001 bar." + }, + "decidedAt": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 date-time when this decision was made." + }, + "expiresAt": { + "type": [ + "string", + "null" + ], + "format": "date-time", + "description": "ISO 8601 date-time after which an approval lapses. Invariant: expiration is a distinct state from denial and revocation." + }, + "decisionHash": { + "type": "string", + "pattern": "^sha256:[a-fA-F0-9]{64}$", + "description": "sha256: prefixed hash of the canonical decision payload for tamper-evidence, matching the PolicyDecision/ExecutionDecision convention." + } + } +} diff --git a/schemas/EffectRecord.json b/schemas/EffectRecord.json new file mode 100644 index 00000000..de9e39f8 --- /dev/null +++ b/schemas/EffectRecord.json @@ -0,0 +1,170 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://schemas.srcos.ai/v2/EffectRecord.json", + "title": "EffectRecord", + "description": "The record of what actually happened when an approved (or autonomously permitted) effect met execution reality. Must remain distinguishable from both the request and the approval. Authority invariant (enforced by anyOf): every record grounds its authority in either an explicit EffectDecision (effectDecisionRef) or a first-class autonomous policy path (autonomousPolicyRef) — no effect record may claim execution beyond delegated authority. Compensation appends history via compensationRefs; it never erases the original record. Provenance: SocioProphet/profit-mpcc schemas/effect-record.schema.json, docs/effect-approval-semantics.md, and exports/policy-integrity/tranche-0001/effect.schema.snapshot.json, hardened to the tranche-0001 strictness bar.", + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "type", + "specVersion", + "effectRequestRef", + "status", + "idempotencyKey", + "resultRefs", + "policyLabels", + "riskLabels" + ], + "anyOf": [ + { + "required": ["effectDecisionRef"], + "properties": { + "effectDecisionRef": { + "type": "string" + } + } + }, + { + "required": ["autonomousPolicyRef"], + "properties": { + "autonomousPolicyRef": { + "type": "string" + } + } + } + ], + "properties": { + "id": { + "type": "string", + "pattern": "^urn:srcos:effect-record:[A-Za-z0-9._~-]+$", + "description": "Stable URN identifier. Pattern: urn:srcos:effect-record:" + }, + "type": { + "const": "EffectRecord", + "description": "Discriminator constant — always \"EffectRecord\"." + }, + "specVersion": { + "const": "0.1.0", + "description": "MPCC event-contract version, pinned per the profit-mpcc policy-integrity tranche-0001 discipline. Changing this const is a contract change." + }, + "effectRequestRef": { + "type": "string", + "pattern": "^urn:srcos:effect:[A-Za-z0-9._~-]+$", + "description": "EffectRequest URN this record executes against." + }, + "effectDecisionRef": { + "type": ["string", "null"], + "pattern": "^urn:srcos:effect-decision:[A-Za-z0-9._~-]+$", + "description": "EffectDecision URN authorizing this execution. Authority invariant: at least one of effectDecisionRef or autonomousPolicyRef must be a non-null reference (enforced by anyOf)." + }, + "autonomousPolicyRef": { + "type": ["string", "null"], + "pattern": "^urn:srcos:policy:", + "description": "Policy URN of the predefined autonomous path permitting execution without a per-effect decision. The autonomous path is itself first-class — never implicit." + }, + "status": { + "type": "string", + "enum": ["simulated", "executed", "failed", "skipped", "compensated", "canceled"], + "description": "Execution outcome. Vocabulary merged from the profit-mpcc effect-record draft (executed, failed, skipped, compensated) and the tranche-0001 status set (simulated, canceled); \"authorized\" is deliberately excluded — authorization lives on EffectDecision." + }, + "executorRef": { + "type": "string", + "minLength": 1, + "description": "Authority reference of the component or actor that performed the execution." + }, + "executionContext": { + "type": "object", + "additionalProperties": true, + "description": "Bounded execution-environment context (e.g. gateway, session, region). Open by design at v0.1; keep minimal." + }, + "actualEffect": { + "type": ["object", "null"], + "additionalProperties": true, + "description": "What actually happened, as an effect-shaped object. Must remain distinguishable from the request and the approval, and may not claim execution beyond delegated authority." + }, + "idempotencyKey": { + "type": "string", + "minLength": 1, + "description": "Replay guard. Invariant: must equal the idempotencyKey of the EffectRequest it executes against." + }, + "startedAt": { + "type": ["string", "null"], + "format": "date-time", + "description": "ISO 8601 date-time when execution began; null when never started (e.g. skipped)." + }, + "completedAt": { + "type": ["string", "null"], + "format": "date-time", + "description": "ISO 8601 date-time when execution reached a terminal state; null while in flight or never started." + }, + "resultRefs": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "A result or evidence reference (e.g. an ExecutionReport URN)." + }, + "uniqueItems": true, + "default": [], + "description": "References to result artifacts and evidence produced by the execution. Required (empty allowed) so executors state their evidence posture explicitly, per the tranche-0001 bar." + }, + "compensationRefs": { + "type": "array", + "items": { + "type": "string", + "pattern": "^urn:srcos:effect-record:[A-Za-z0-9._~-]+$", + "description": "EffectRecord URN of a compensating action." + }, + "uniqueItems": true, + "default": [], + "description": "EffectRecord URNs of compensating actions raised for this record. Invariant: compensation appends history; it never erases the original effect record." + }, + "provenanceLinks": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["rel", "ref"], + "properties": { + "rel": { + "type": "string", + "minLength": 1, + "description": "Relation of the linked artifact to this event (e.g. derived_from, quotes, source_feed)." + }, + "ref": { + "type": "string", + "minLength": 1, + "description": "URN or stable reference of the linked artifact." + } + }, + "description": "A single provenance link (relation + reference)." + }, + "uniqueItems": true, + "default": [], + "description": "Typed provenance links from this event to upstream artifacts, sources, or records." + }, + "policyLabels": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "A policy label." + }, + "uniqueItems": true, + "default": [], + "description": "Policy labels attached to this record. Required (empty allowed) per the tranche-0001 bar." + }, + "riskLabels": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "A risk label." + }, + "uniqueItems": true, + "default": [], + "description": "Risk labels attached to this record. Required (empty allowed) per the tranche-0001 bar." + } + } +} diff --git a/schemas/EffectRequest.json b/schemas/EffectRequest.json new file mode 100644 index 00000000..525144f1 --- /dev/null +++ b/schemas/EffectRequest.json @@ -0,0 +1,153 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://schemas.srcos.ai/v2/EffectRequest.json", + "title": "EffectRequest", + "description": "A proposed side effect (state transition) requested by an MPCC event. A requested effect is a proposal only: it is not permission, not execution, and not evidence that anything happened. Lifecycle: EffectRequest (requested) → EffectDecision (approved / denied / deferred / expired / revoked) → EffectRecord (what actually happened, including compensation). Provenance: SocioProphet/profit-mpcc schemas/effect-request.schema.json, docs/effect-approval-semantics.md, and exports/policy-integrity/tranche-0001/effect.schema.snapshot.json, hardened to the tranche-0001 strictness bar.", + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "type", + "specVersion", + "requestedByEventRef", + "effectKind", + "capability", + "target", + "parameters", + "idempotencyKey", + "requiresHumanApproval", + "policyLabels", + "riskLabels", + "requestedAt" + ], + "properties": { + "id": { + "type": "string", + "pattern": "^urn:srcos:effect:[A-Za-z0-9._~-]+$", + "description": "Stable URN identifier of the requested effect. Pattern: urn:srcos:effect:. The same effect identity is carried through decision and record stages by reference." + }, + "type": { + "const": "EffectRequest", + "description": "Discriminator constant — always \"EffectRequest\"." + }, + "specVersion": { + "const": "0.1.0", + "description": "MPCC event-contract version, pinned per the profit-mpcc policy-integrity tranche-0001 discipline. Changing this const is a contract change." + }, + "requestedByEventRef": { + "type": "string", + "pattern": "^urn:srcos:[a-z0-9-]+:[A-Za-z0-9._~-]+$", + "description": "URN of the MPCC event that proposed this effect (typically a ConversationEvent; trading profiles such as OrderIntent may also request effects)." + }, + "requestedByActorRef": { + "type": "string", + "minLength": 1, + "description": "Authority reference of the actor that made the request, when distinct from the requesting event's actor." + }, + "workspaceRef": { + "type": "string", + "minLength": 1, + "description": "Workspace scope the requested effect applies to. Free-form stable reference in v0.1." + }, + "branchRef": { + "type": "string", + "minLength": 1, + "description": "Conversation-fabric branch the requested effect applies to. Free-form stable reference in v0.1." + }, + "effectKind": { + "type": "string", + "enum": [ + "create", + "update", + "delete", + "retrieve", + "transform", + "notify", + "schedule", + "route", + "publish", + "redact", + "label", + "archive", + "execute", + "no_op", + "verify" + ], + "description": "Kind of state transition proposed. Vocabulary imported verbatim from the tranche-0001 effect snapshot." + }, + "capability": { + "type": "string", + "minLength": 1, + "description": "Capability identifier this request would exercise (e.g. trading.order.place). The decision stage evaluates authority against this capability." + }, + "target": { + "type": "object", + "additionalProperties": false, + "required": ["kind", "identifier"], + "properties": { + "kind": { + "type": "string", + "minLength": 1, + "description": "Kind of target the effect operates on (e.g. venue_order_gateway, document, channel)." + }, + "identifier": { + "type": "string", + "minLength": 1, + "description": "Stable identifier of the target." + }, + "location": { + "type": "string", + "minLength": 1, + "description": "Optional location or partition of the target." + } + }, + "description": "The object or surface the proposed effect would act on." + }, + "parameters": { + "type": "object", + "additionalProperties": true, + "description": "Effect parameters. Open by design at v0.1 (mirrors the tranche-0001 snapshot); producers should keep parameters minimal and typed by capability." + }, + "idempotencyKey": { + "type": "string", + "minLength": 1, + "description": "Replay guard. Invariant: every side-effecting path must be idempotent under this key; the executing EffectRecord must carry the same key." + }, + "requiresHumanApproval": { + "type": "boolean", + "description": "Whether policy requires an explicit human decision before this effect may execute." + }, + "policyLabels": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "A policy label." + }, + "uniqueItems": true, + "default": [], + "description": "Policy labels attached to this request. Required (empty allowed) so producers state the policy posture explicitly, per the tranche-0001 bar." + }, + "riskLabels": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "A risk label." + }, + "uniqueItems": true, + "default": [], + "description": "Risk labels attached to this request. Required (empty allowed) so producers state the risk posture explicitly, per the tranche-0001 bar." + }, + "requestedAt": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 date-time when the effect was requested." + }, + "notes": { + "type": "string", + "minLength": 1, + "description": "Free-form producer note." + } + } +} diff --git a/schemas/ExecutionReport.json b/schemas/ExecutionReport.json new file mode 100644 index 00000000..c7a79162 --- /dev/null +++ b/schemas/ExecutionReport.json @@ -0,0 +1,232 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://schemas.srcos.ai/v2/ExecutionReport.json", + "title": "ExecutionReport", + "description": "An execution-plane or venue-facing report of what actually happened to an intended order or instruction (accept, reject, partial fill, fill, cancel acknowledgement, bust, or correction). Venue reality, distinct from intent and approval. Profile of the ConversationEvent envelope: shares the single MPCC identity/causality/governance vocabulary (specVersion, actorRef, workspaceRef, branchRef, visibilityScope, wallTime, logicalTime, causalParents, traceContext, provenanceLinks, policyLabels, riskLabels) — envelope parity is enforced by tools/validate_mpcc_event_examples.py. Provenance: SocioProphet/profit-mpcc schemas/execution-report-event.schema.json and docs/trading-order-lifecycle.md, hardened to the policy-integrity tranche-0001 strictness bar.", + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "type", + "specVersion", + "wallTime", + "orderIntentRef", + "reportKind" + ], + "properties": { + "id": { + "type": "string", + "pattern": "^urn:srcos:execution-report:[A-Za-z0-9._~-]+$", + "description": "Stable URN identifier. Pattern: urn:srcos:execution-report:. Invariant: event identity is stable and never reused." + }, + "type": { + "const": "ExecutionReport", + "description": "Discriminator constant — always \"ExecutionReport\"." + }, + "specVersion": { + "const": "0.1.0", + "description": "MPCC event-contract version, pinned per the profit-mpcc policy-integrity tranche-0001 discipline. This family is versioned independently of the v2 metadata-plane schemas; changing this const is a contract change." + }, + "actorRef": { + "type": "string", + "minLength": 1, + "description": "Authority reference of the actor that emitted this event (human subject, agent, service, or system). Subject or Agent Registry URNs are recommended; free-form identifiers are permitted in v0.1." + }, + "workspaceRef": { + "type": "string", + "minLength": 1, + "description": "Workspace scope this event belongs to. Free-form stable reference in v0.1; a typed workspace URN may narrow this in a later minor version." + }, + "branchRef": { + "type": "string", + "minLength": 1, + "description": "Conversation-fabric branch this event was appended to. Free-form stable reference in v0.1, pending adoption of the profit-mpcc branch-id contract." + }, + "visibilityScope": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "A visibility scope label such as private, shared, public, or an audience URN." + }, + "uniqueItems": true, + "default": [], + "description": "Visibility scopes governing who may observe this event (e.g. private, shared, public, or audience URNs)." + }, + "wallTime": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 wall-clock time of the event as observed by the producer. Family profiles map their primary domain timestamp onto this field." + }, + "logicalTime": { + "type": [ + "integer", + "string" + ], + "minimum": 0, + "minLength": 1, + "description": "Producer-scoped logical clock: a non-negative integer for scalar (Lamport-style) clocks, or a non-empty string for encoded vector/hybrid clocks. Invariant: causalParents must never point forward in logical time." + }, + "causalParents": { + "type": "array", + "items": { + "type": "string", + "pattern": "^urn:srcos:[a-z0-9-]+:[A-Za-z0-9._~-]+$", + "description": "URN of a causally preceding event in any MPCC event family." + }, + "uniqueItems": true, + "default": [], + "description": "Event URNs this event causally depends on. Cross-family parents are permitted (e.g. an OrderIntent caused by a MarketDataEvent). Invariant: parents must never point forward in logical time." + }, + "traceContext": { + "type": "object", + "additionalProperties": false, + "required": [ + "traceId" + ], + "properties": { + "traceId": { + "type": "string", + "minLength": 1, + "description": "Distributed trace identifier." + }, + "spanId": { + "type": "string", + "minLength": 1, + "description": "Span identifier within the trace." + }, + "baggage": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "String-valued trace baggage entries." + } + }, + "description": "Optional distributed-tracing correlation context." + }, + "provenanceLinks": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "rel", + "ref" + ], + "properties": { + "rel": { + "type": "string", + "minLength": 1, + "description": "Relation of the linked artifact to this event (e.g. derived_from, quotes, source_feed)." + }, + "ref": { + "type": "string", + "minLength": 1, + "description": "URN or stable reference of the linked artifact." + } + }, + "description": "A single provenance link (relation + reference)." + }, + "uniqueItems": true, + "default": [], + "description": "Typed provenance links from this event to upstream artifacts, sources, or records." + }, + "policyLabels": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "A policy label." + }, + "uniqueItems": true, + "default": [], + "description": "Policy labels attached to this event by producers or the policy fabric." + }, + "riskLabels": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "A risk label." + }, + "uniqueItems": true, + "default": [], + "description": "Risk labels attached to this event by producers or risk controls." + }, + "orderIntentRef": { + "type": "string", + "pattern": "^urn:srcos:order-intent:[A-Za-z0-9._~-]+$", + "description": "OrderIntent URN this report responds to." + }, + "venueRef": { + "type": "string", + "minLength": 1, + "description": "Venue that produced the report." + }, + "reportKind": { + "type": "string", + "enum": [ + "accepted", + "rejected", + "partial_fill", + "fill", + "cancel_ack", + "bust", + "correct" + ], + "description": "Kind of execution report: accepted, rejected, partial fill, fill, cancel acknowledgement, bust, or correction." + }, + "orderRef": { + "type": "string", + "minLength": 1, + "description": "Venue-assigned order identifier, when known." + }, + "fillQuantity": { + "type": [ + "number", + "string", + "null" + ], + "pattern": "^[0-9]+(\\.[0-9]+)?$", + "description": "Quantity filled by this report: a JSON number, or an unsigned decimal string for exact precision; null when not applicable." + }, + "fillPrice": { + "type": [ + "number", + "string", + "null" + ], + "pattern": "^-?[0-9]+(\\.[0-9]+)?$", + "description": "Price of this fill: a JSON number, or a signed decimal string for exact precision; null when not applicable." + }, + "remainingQuantity": { + "type": [ + "number", + "string", + "null" + ], + "pattern": "^[0-9]+(\\.[0-9]+)?$", + "description": "Quantity remaining open after this report: a JSON number, or an unsigned decimal string; null when not applicable." + }, + "reasonCode": { + "type": "string", + "minLength": 1, + "description": "Venue or gateway reason code (e.g. for rejections, busts, corrections)." + }, + "sequenceRef": { + "type": [ + "integer", + "string" + ], + "minimum": 0, + "minLength": 1, + "description": "Feed or venue sequence number (non-negative integer) or sequence token (non-empty string) used for ordering and gap detection." + }, + "details": { + "type": "object", + "additionalProperties": true, + "description": "Bounded family-specific detail payload for fields the v0.1 contract does not yet type. Producers should keep this minimal; recurring keys should be promoted to typed fields in a later minor version." + } + } +} diff --git a/schemas/MarketDataEvent.json b/schemas/MarketDataEvent.json new file mode 100644 index 00000000..21d870d4 --- /dev/null +++ b/schemas/MarketDataEvent.json @@ -0,0 +1,240 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://schemas.srcos.ai/v2/MarketDataEvent.json", + "title": "MarketDataEvent", + "description": "A venue-originating or feed-originating market-data state change (quote, trade, book delta/snapshot, bar, venue status, heartbeat, or gap marker). Profile of the ConversationEvent envelope: shares the single MPCC identity/causality/governance vocabulary (specVersion, actorRef, workspaceRef, branchRef, visibilityScope, wallTime, logicalTime, causalParents, traceContext, provenanceLinks, policyLabels, riskLabels) — envelope parity is enforced by tools/validate_mpcc_event_examples.py. Provenance: SocioProphet/profit-mpcc schemas/market-data-event.schema.json and docs/trading-event-families.md, hardened to the policy-integrity tranche-0001 strictness bar.", + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "type", + "specVersion", + "wallTime", + "instrumentRef", + "venueRef", + "eventKind", + "rawPayload", + "canonicalPayload" + ], + "properties": { + "id": { + "type": "string", + "pattern": "^urn:srcos:market-data-event:[A-Za-z0-9._~-]+$", + "description": "Stable URN identifier. Pattern: urn:srcos:market-data-event:. Invariant: event identity is stable and never reused." + }, + "type": { + "const": "MarketDataEvent", + "description": "Discriminator constant — always \"MarketDataEvent\"." + }, + "specVersion": { + "const": "0.1.0", + "description": "MPCC event-contract version, pinned per the profit-mpcc policy-integrity tranche-0001 discipline. This family is versioned independently of the v2 metadata-plane schemas; changing this const is a contract change." + }, + "actorRef": { + "type": "string", + "minLength": 1, + "description": "Authority reference of the actor that emitted this event (human subject, agent, service, or system). Subject or Agent Registry URNs are recommended; free-form identifiers are permitted in v0.1." + }, + "workspaceRef": { + "type": "string", + "minLength": 1, + "description": "Workspace scope this event belongs to. Free-form stable reference in v0.1; a typed workspace URN may narrow this in a later minor version." + }, + "branchRef": { + "type": "string", + "minLength": 1, + "description": "Conversation-fabric branch this event was appended to. Free-form stable reference in v0.1, pending adoption of the profit-mpcc branch-id contract." + }, + "visibilityScope": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "A visibility scope label such as private, shared, public, or an audience URN." + }, + "uniqueItems": true, + "default": [], + "description": "Visibility scopes governing who may observe this event (e.g. private, shared, public, or audience URNs)." + }, + "wallTime": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 wall-clock time of the event as observed by the producer. Family profiles map their primary domain timestamp onto this field." + }, + "logicalTime": { + "type": [ + "integer", + "string" + ], + "minimum": 0, + "minLength": 1, + "description": "Producer-scoped logical clock: a non-negative integer for scalar (Lamport-style) clocks, or a non-empty string for encoded vector/hybrid clocks. Invariant: causalParents must never point forward in logical time." + }, + "causalParents": { + "type": "array", + "items": { + "type": "string", + "pattern": "^urn:srcos:[a-z0-9-]+:[A-Za-z0-9._~-]+$", + "description": "URN of a causally preceding event in any MPCC event family." + }, + "uniqueItems": true, + "default": [], + "description": "Event URNs this event causally depends on. Cross-family parents are permitted (e.g. an OrderIntent caused by a MarketDataEvent). Invariant: parents must never point forward in logical time." + }, + "traceContext": { + "type": "object", + "additionalProperties": false, + "required": [ + "traceId" + ], + "properties": { + "traceId": { + "type": "string", + "minLength": 1, + "description": "Distributed trace identifier." + }, + "spanId": { + "type": "string", + "minLength": 1, + "description": "Span identifier within the trace." + }, + "baggage": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "String-valued trace baggage entries." + } + }, + "description": "Optional distributed-tracing correlation context." + }, + "provenanceLinks": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "rel", + "ref" + ], + "properties": { + "rel": { + "type": "string", + "minLength": 1, + "description": "Relation of the linked artifact to this event (e.g. derived_from, quotes, source_feed)." + }, + "ref": { + "type": "string", + "minLength": 1, + "description": "URN or stable reference of the linked artifact." + } + }, + "description": "A single provenance link (relation + reference)." + }, + "uniqueItems": true, + "default": [], + "description": "Typed provenance links from this event to upstream artifacts, sources, or records." + }, + "policyLabels": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "A policy label." + }, + "uniqueItems": true, + "default": [], + "description": "Policy labels attached to this event by producers or the policy fabric." + }, + "riskLabels": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "A risk label." + }, + "uniqueItems": true, + "default": [], + "description": "Risk labels attached to this event by producers or risk controls." + }, + "instrumentRef": { + "type": "string", + "minLength": 1, + "description": "Instrument identifier (symbology per the venue/feed contract, e.g. ACME.XNAS)." + }, + "venueRef": { + "type": "string", + "minLength": 1, + "description": "Venue identifier the event originates from or refers to (e.g. a MIC such as XNAS)." + }, + "eventKind": { + "type": "string", + "enum": [ + "quote", + "trade", + "book_delta", + "book_snapshot", + "bar", + "venue_status", + "heartbeat", + "gap_marker" + ], + "description": "Kind of market-data event: quote, trade, book delta, book snapshot, bar, venue status, heartbeat, or gap marker." + }, + "sequenceRef": { + "type": [ + "integer", + "string" + ], + "minimum": 0, + "minLength": 1, + "description": "Feed or venue sequence number (non-negative integer) or sequence token (non-empty string) used for ordering and gap detection." + }, + "feedRef": { + "type": "string", + "minLength": 1, + "description": "Identifier of the concrete feed or subscription that delivered this event." + }, + "rawPayload": { + "type": [ + "object", + "array", + "string", + "number", + "boolean", + "null" + ], + "description": "The venue/feed payload exactly as received, before any normalization." + }, + "canonicalPayload": { + "type": [ + "object", + "array", + "string", + "number", + "boolean", + "null" + ], + "description": "The normalized payload. Invariant: the canonical form must declare the normalization regime used." + }, + "nullAbsenceRef": { + "type": [ + "string", + "null" + ], + "pattern": "^urn:srcos:null-absence:[A-Za-z0-9._~-]+$", + "description": "NullAbsenceRecord URN when this event marks a typed absence (heartbeat, gap_marker). Typed reference replacing the loose inline null_absence object of the source draft." + }, + "qualityFlags": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "A data-quality flag." + }, + "uniqueItems": true, + "default": [], + "description": "Data-quality flags attached by the feed handler (e.g. stale, out_of_order, synthetic)." + } + } +} diff --git a/schemas/NullAbsenceRecord.json b/schemas/NullAbsenceRecord.json new file mode 100644 index 00000000..3268ea40 --- /dev/null +++ b/schemas/NullAbsenceRecord.json @@ -0,0 +1,113 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://schemas.srcos.ai/v2/NullAbsenceRecord.json", + "title": "NullAbsenceRecord", + "description": "A first-class record of an observed absence, typed by the 12-kind MPCC null/absence taxonomy so that event semantics, merge logic, provenance, policy, and effect handling never silently conflate materially different kinds of \"nothing\". Working invariants: no_event_observed is not empty_payload; transport_failure is not intentional_silence; refusal is not abstention. Provenance: SocioProphet/profit-mpcc schemas/null-absence.schema.json and docs/null-absence-taxonomy.md, hardened to the policy-integrity tranche-0001 strictness bar.", + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "type", + "specVersion", + "kind", + "observedAt" + ], + "properties": { + "id": { + "type": "string", + "pattern": "^urn:srcos:null-absence:[A-Za-z0-9._~-]+$", + "description": "Stable URN identifier. Pattern: urn:srcos:null-absence:" + }, + "type": { + "const": "NullAbsenceRecord", + "description": "Discriminator constant — always \"NullAbsenceRecord\"." + }, + "specVersion": { + "const": "0.1.0", + "description": "MPCC event-contract version, pinned per the profit-mpcc policy-integrity tranche-0001 discipline. Changing this const is a contract change." + }, + "kind": { + "type": "string", + "enum": [ + "no_event_observed", + "empty_payload", + "heartbeat", + "acknowledgment", + "timeout", + "transport_failure", + "intentional_silence", + "abstention", + "refusal", + "withheld_redacted", + "tombstone", + "explicit_noop" + ], + "description": "The kind of absence observed. no_event_observed: no event is known to exist for the interval or scope. empty_payload: an event exists but its payload is intentionally or structurally empty. heartbeat: a liveness signal with minimal semantic content. acknowledgment: a bounded acknowledgment referencing prior state or receipt. timeout: an expected response did not arrive within the governing temporal boundary. transport_failure: the absence arises from channel failure rather than communicative intent. intentional_silence: an actor intentionally emits nothing within scope. abstention: an actor declines to decide or endorse while remaining inside the protocol. refusal: an actor explicitly declines a request, command, delegation, or effect. withheld_redacted: content exists but is withheld or masked under policy or privacy constraints. tombstone: a deletion marker preserving identity and history while negating payload visibility. explicit_noop: a first-class no-op event recording deliberate non-action." + }, + "observedAt": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 date-time when the absence was observed or established." + }, + "relatedEventRef": { + "type": ["string", "null"], + "pattern": "^urn:srcos:[a-z0-9-]+:[A-Za-z0-9._~-]+$", + "description": "URN of the MPCC event this absence relates to (e.g. the last event before a gap, or the event whose reply never arrived)." + }, + "relatedBranchRef": { + "type": ["string", "null"], + "minLength": 1, + "description": "Conversation-fabric branch scope of the absence, when applicable." + }, + "relatedWorkspaceRef": { + "type": ["string", "null"], + "minLength": 1, + "description": "Workspace scope of the absence, when applicable." + }, + "causalNotes": { + "type": "string", + "minLength": 1, + "description": "Human-readable note on the causal interpretation of the absence." + }, + "policyLabels": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "A policy label." + }, + "uniqueItems": true, + "default": [], + "description": "Policy labels attached to this absence record." + }, + "provenanceLinks": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["rel", "ref"], + "properties": { + "rel": { + "type": "string", + "minLength": 1, + "description": "Relation of the linked artifact to this event (e.g. derived_from, quotes, source_feed)." + }, + "ref": { + "type": "string", + "minLength": 1, + "description": "URN or stable reference of the linked artifact." + } + }, + "description": "A single provenance link (relation + reference)." + }, + "uniqueItems": true, + "default": [], + "description": "Typed provenance links from this event to upstream artifacts, sources, or records." + }, + "details": { + "type": "object", + "additionalProperties": true, + "description": "Bounded kind-specific detail payload (e.g. expected sequence numbers for a gap). Keep minimal; recurring keys should be promoted to typed fields in a later minor version." + } + } +} diff --git a/schemas/OrderIntent.json b/schemas/OrderIntent.json new file mode 100644 index 00000000..b7ee781a --- /dev/null +++ b/schemas/OrderIntent.json @@ -0,0 +1,319 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://schemas.srcos.ai/v2/OrderIntent.json", + "title": "OrderIntent", + "description": "A human or agent intent to create, amend, cancel, pause, or resume trading action. An intent is a proposal in the trading lane: it is not permission and not execution — approval flows through EffectRequest/EffectDecision, and venue reality arrives as ExecutionReport. Profile of the ConversationEvent envelope: shares the single MPCC identity/causality/governance vocabulary (specVersion, actorRef, workspaceRef, branchRef, visibilityScope, wallTime, logicalTime, causalParents, traceContext, provenanceLinks, policyLabels, riskLabels) — envelope parity is enforced by tools/validate_mpcc_event_examples.py. Provenance: SocioProphet/profit-mpcc schemas/order-intent-event.schema.json and docs/trading-order-lifecycle.md, hardened to the policy-integrity tranche-0001 strictness bar.", + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "type", + "specVersion", + "actorRef", + "wallTime", + "strategyRef", + "instrumentRef", + "intentKind", + "side", + "quantity" + ], + "properties": { + "id": { + "type": "string", + "pattern": "^urn:srcos:order-intent:[A-Za-z0-9._~-]+$", + "description": "Stable URN identifier. Pattern: urn:srcos:order-intent:. Invariant: event identity is stable and never reused." + }, + "type": { + "const": "OrderIntent", + "description": "Discriminator constant — always \"OrderIntent\"." + }, + "specVersion": { + "const": "0.1.0", + "description": "MPCC event-contract version, pinned per the profit-mpcc policy-integrity tranche-0001 discipline. This family is versioned independently of the v2 metadata-plane schemas; changing this const is a contract change." + }, + "actorRef": { + "type": "string", + "minLength": 1, + "description": "Authority reference of the actor that emitted this event (human subject, agent, service, or system). Subject or Agent Registry URNs are recommended; free-form identifiers are permitted in v0.1." + }, + "workspaceRef": { + "type": "string", + "minLength": 1, + "description": "Workspace scope this event belongs to. Free-form stable reference in v0.1; a typed workspace URN may narrow this in a later minor version." + }, + "branchRef": { + "type": "string", + "minLength": 1, + "description": "Conversation-fabric branch this event was appended to. Free-form stable reference in v0.1, pending adoption of the profit-mpcc branch-id contract." + }, + "visibilityScope": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "A visibility scope label such as private, shared, public, or an audience URN." + }, + "uniqueItems": true, + "default": [], + "description": "Visibility scopes governing who may observe this event (e.g. private, shared, public, or audience URNs)." + }, + "wallTime": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 wall-clock time of the event as observed by the producer. Family profiles map their primary domain timestamp onto this field." + }, + "logicalTime": { + "type": [ + "integer", + "string" + ], + "minimum": 0, + "minLength": 1, + "description": "Producer-scoped logical clock: a non-negative integer for scalar (Lamport-style) clocks, or a non-empty string for encoded vector/hybrid clocks. Invariant: causalParents must never point forward in logical time." + }, + "causalParents": { + "type": "array", + "items": { + "type": "string", + "pattern": "^urn:srcos:[a-z0-9-]+:[A-Za-z0-9._~-]+$", + "description": "URN of a causally preceding event in any MPCC event family." + }, + "uniqueItems": true, + "default": [], + "description": "Event URNs this event causally depends on. Cross-family parents are permitted (e.g. an OrderIntent caused by a MarketDataEvent). Invariant: parents must never point forward in logical time." + }, + "traceContext": { + "type": "object", + "additionalProperties": false, + "required": [ + "traceId" + ], + "properties": { + "traceId": { + "type": "string", + "minLength": 1, + "description": "Distributed trace identifier." + }, + "spanId": { + "type": "string", + "minLength": 1, + "description": "Span identifier within the trace." + }, + "baggage": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "String-valued trace baggage entries." + } + }, + "description": "Optional distributed-tracing correlation context." + }, + "provenanceLinks": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "rel", + "ref" + ], + "properties": { + "rel": { + "type": "string", + "minLength": 1, + "description": "Relation of the linked artifact to this event (e.g. derived_from, quotes, source_feed)." + }, + "ref": { + "type": "string", + "minLength": 1, + "description": "URN or stable reference of the linked artifact." + } + }, + "description": "A single provenance link (relation + reference)." + }, + "uniqueItems": true, + "default": [], + "description": "Typed provenance links from this event to upstream artifacts, sources, or records." + }, + "policyLabels": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "A policy label." + }, + "uniqueItems": true, + "default": [], + "description": "Policy labels attached to this event by producers or the policy fabric." + }, + "riskLabels": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "A risk label." + }, + "uniqueItems": true, + "default": [], + "description": "Risk labels attached to this event by producers or risk controls." + }, + "authorityContext": { + "type": "object", + "additionalProperties": false, + "required": [ + "principal", + "delegationChain", + "capabilities", + "approvalState", + "policyContext" + ], + "properties": { + "principal": { + "type": "string", + "enum": [ + "user", + "agent", + "system", + "tool" + ], + "description": "Class of principal exercising authority for this act." + }, + "delegationChain": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "Actor reference of one hop in the delegation chain." + }, + "default": [], + "description": "Ordered delegation chain from the root principal to the acting principal; empty when the principal acts directly." + }, + "capabilities": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "A capability identifier." + }, + "uniqueItems": true, + "default": [], + "description": "Capability identifiers the actor claims for this act." + }, + "approvalState": { + "type": "string", + "enum": [ + "none", + "pending", + "approved", + "denied" + ], + "description": "Approval state of the authority under which this act was performed." + }, + "policyContext": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "A policy reference." + }, + "uniqueItems": true, + "default": [], + "description": "Policy references in force when this act was performed." + } + }, + "description": "Authority context under which this act was performed: principal class, delegation chain, claimed capabilities, approval state, and policy context. All five sub-fields are required when the context is present so that authority is never ambiguous." + }, + "strategyRef": { + "type": "string", + "minLength": 1, + "description": "Strategy or control-plane component whose decision produced this intent." + }, + "instrumentRef": { + "type": "string", + "minLength": 1, + "description": "Instrument identifier the intent targets (symbology per the venue/feed contract)." + }, + "venueRef": { + "type": "string", + "minLength": 1, + "description": "Intended venue, when the intent is venue-specific." + }, + "intentKind": { + "type": "string", + "enum": [ + "new", + "amend", + "cancel", + "pause", + "resume" + ], + "description": "Kind of intent: create a new order, amend or cancel an existing one, or pause/resume trading activity." + }, + "side": { + "type": "string", + "enum": [ + "buy", + "sell", + "none" + ], + "description": "Order side; \"none\" for side-less intents such as pause/resume." + }, + "orderType": { + "type": "string", + "enum": [ + "market", + "limit", + "stop", + "other" + ], + "description": "Order type requested." + }, + "quantity": { + "type": [ + "number", + "string" + ], + "pattern": "^[0-9]+(\\.[0-9]+)?$", + "description": "Order quantity: a JSON number, or an unsigned decimal string for exact precision. Direction is carried by side, so quantity is non-negative." + }, + "price": { + "type": [ + "number", + "string", + "null" + ], + "pattern": "^-?[0-9]+(\\.[0-9]+)?$", + "description": "Limit/stop price: a JSON number, or a signed decimal string for exact precision; null for market orders." + }, + "timeInForce": { + "type": "string", + "minLength": 1, + "description": "Time-in-force instruction (e.g. day, ioc, fok, gtc)." + }, + "requestedEffectRef": { + "type": [ + "string", + "null" + ], + "pattern": "^urn:srcos:effect:[A-Za-z0-9._~-]+$", + "description": "EffectRequest URN when this intent is governed through the MPCC effect lifecycle (recommended for all side-effecting order flow)." + }, + "signalRefs": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "Reference to a signal or evidence artifact motivating this intent." + }, + "uniqueItems": true, + "default": [], + "description": "References to signals, market-data events, or other evidence motivating this intent." + }, + "notes": { + "type": "string", + "minLength": 1, + "description": "Free-form producer note." + } + } +} diff --git a/schemas/PositionChange.json b/schemas/PositionChange.json new file mode 100644 index 00000000..5350f006 --- /dev/null +++ b/schemas/PositionChange.json @@ -0,0 +1,228 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://schemas.srcos.ai/v2/PositionChange.json", + "title": "PositionChange", + "description": "A state delta over inventory, exposure, cash, fees, or realized/unrealized PnL, traceable to the execution reports it derives from. Accounting reality, distinct from intent, approval, and venue reports. Profile of the ConversationEvent envelope: shares the single MPCC identity/causality/governance vocabulary (specVersion, actorRef, workspaceRef, branchRef, visibilityScope, wallTime, logicalTime, causalParents, traceContext, provenanceLinks, policyLabels, riskLabels) — envelope parity is enforced by tools/validate_mpcc_event_examples.py. Provenance: SocioProphet/profit-mpcc schemas/position-change-event.schema.json and docs/trading-event-families.md, hardened to the policy-integrity tranche-0001 strictness bar.", + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "type", + "specVersion", + "wallTime", + "instrumentRef", + "positionDelta" + ], + "properties": { + "id": { + "type": "string", + "pattern": "^urn:srcos:position-change:[A-Za-z0-9._~-]+$", + "description": "Stable URN identifier. Pattern: urn:srcos:position-change:. Invariant: event identity is stable and never reused." + }, + "type": { + "const": "PositionChange", + "description": "Discriminator constant — always \"PositionChange\"." + }, + "specVersion": { + "const": "0.1.0", + "description": "MPCC event-contract version, pinned per the profit-mpcc policy-integrity tranche-0001 discipline. This family is versioned independently of the v2 metadata-plane schemas; changing this const is a contract change." + }, + "actorRef": { + "type": "string", + "minLength": 1, + "description": "Authority reference of the actor that emitted this event (human subject, agent, service, or system). Subject or Agent Registry URNs are recommended; free-form identifiers are permitted in v0.1." + }, + "workspaceRef": { + "type": "string", + "minLength": 1, + "description": "Workspace scope this event belongs to. Free-form stable reference in v0.1; a typed workspace URN may narrow this in a later minor version." + }, + "branchRef": { + "type": "string", + "minLength": 1, + "description": "Conversation-fabric branch this event was appended to. Free-form stable reference in v0.1, pending adoption of the profit-mpcc branch-id contract." + }, + "visibilityScope": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "A visibility scope label such as private, shared, public, or an audience URN." + }, + "uniqueItems": true, + "default": [], + "description": "Visibility scopes governing who may observe this event (e.g. private, shared, public, or audience URNs)." + }, + "wallTime": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 wall-clock time of the event as observed by the producer. Family profiles map their primary domain timestamp onto this field." + }, + "logicalTime": { + "type": [ + "integer", + "string" + ], + "minimum": 0, + "minLength": 1, + "description": "Producer-scoped logical clock: a non-negative integer for scalar (Lamport-style) clocks, or a non-empty string for encoded vector/hybrid clocks. Invariant: causalParents must never point forward in logical time." + }, + "causalParents": { + "type": "array", + "items": { + "type": "string", + "pattern": "^urn:srcos:[a-z0-9-]+:[A-Za-z0-9._~-]+$", + "description": "URN of a causally preceding event in any MPCC event family." + }, + "uniqueItems": true, + "default": [], + "description": "Event URNs this event causally depends on. Cross-family parents are permitted (e.g. an OrderIntent caused by a MarketDataEvent). Invariant: parents must never point forward in logical time." + }, + "traceContext": { + "type": "object", + "additionalProperties": false, + "required": [ + "traceId" + ], + "properties": { + "traceId": { + "type": "string", + "minLength": 1, + "description": "Distributed trace identifier." + }, + "spanId": { + "type": "string", + "minLength": 1, + "description": "Span identifier within the trace." + }, + "baggage": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "String-valued trace baggage entries." + } + }, + "description": "Optional distributed-tracing correlation context." + }, + "provenanceLinks": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "rel", + "ref" + ], + "properties": { + "rel": { + "type": "string", + "minLength": 1, + "description": "Relation of the linked artifact to this event (e.g. derived_from, quotes, source_feed)." + }, + "ref": { + "type": "string", + "minLength": 1, + "description": "URN or stable reference of the linked artifact." + } + }, + "description": "A single provenance link (relation + reference)." + }, + "uniqueItems": true, + "default": [], + "description": "Typed provenance links from this event to upstream artifacts, sources, or records." + }, + "policyLabels": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "A policy label." + }, + "uniqueItems": true, + "default": [], + "description": "Policy labels attached to this event by producers or the policy fabric." + }, + "riskLabels": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "A risk label." + }, + "uniqueItems": true, + "default": [], + "description": "Risk labels attached to this event by producers or risk controls." + }, + "instrumentRef": { + "type": "string", + "minLength": 1, + "description": "Instrument identifier whose position changed (symbology per the venue/feed contract)." + }, + "accountRef": { + "type": "string", + "minLength": 1, + "description": "Account or book the change applies to." + }, + "positionDelta": { + "type": [ + "number", + "string" + ], + "pattern": "^-?[0-9]+(\\.[0-9]+)?$", + "description": "Signed change in position quantity: a JSON number, or a signed decimal string for exact precision." + }, + "cashDelta": { + "type": [ + "number", + "string", + "null" + ], + "pattern": "^-?[0-9]+(\\.[0-9]+)?$", + "description": "Signed change in cash: a JSON number, or a signed decimal string; null when not applicable." + }, + "feeDelta": { + "type": [ + "number", + "string", + "null" + ], + "pattern": "^-?[0-9]+(\\.[0-9]+)?$", + "description": "Signed change in fees: a JSON number, or a signed decimal string; null when not applicable." + }, + "realizedPnlDelta": { + "type": [ + "number", + "string", + "null" + ], + "pattern": "^-?[0-9]+(\\.[0-9]+)?$", + "description": "Signed realized-PnL change: a JSON number, or a signed decimal string; null when not applicable." + }, + "unrealizedPnlSnapshot": { + "type": [ + "number", + "string", + "null" + ], + "pattern": "^-?[0-9]+(\\.[0-9]+)?$", + "description": "Unrealized-PnL snapshot at wallTime: a JSON number, or a signed decimal string; null when not computed." + }, + "sourceExecutionRefs": { + "type": "array", + "items": { + "type": "string", + "pattern": "^urn:srcos:execution-report:[A-Za-z0-9._~-]+$", + "description": "ExecutionReport URN." + }, + "uniqueItems": true, + "default": [], + "description": "ExecutionReport URNs this state delta derives from. Invariant: accounting changes must be traceable to venue reality or reconciliation." + }, + "details": { + "type": "object", + "additionalProperties": true, + "description": "Bounded family-specific detail payload for fields the v0.1 contract does not yet type. Producers should keep this minimal; recurring keys should be promoted to typed fields in a later minor version." + } + } +} diff --git a/schemas/README.md b/schemas/README.md index 1decc71e..3dc5e15a 100644 --- a/schemas/README.md +++ b/schemas/README.md @@ -5,6 +5,33 @@ This directory contains the JSON Schema (draft 2020-12) files that make up the S --- +## Recent additions — MPCC Event Contract v0.1 (conversation + trading event family) + +The MPCC (multi-party conversation control) event contract adds the following top-level schemas: + +| File | Type | URN prefix | +|------|------|-----------| +| `ConversationEvent.json` | ConversationEvent | `urn:srcos:conversation-event:` | +| `EffectRequest.json` | EffectRequest | `urn:srcos:effect:` | +| `EffectDecision.json` | EffectDecision | `urn:srcos:effect-decision:` | +| `EffectRecord.json` | EffectRecord | `urn:srcos:effect-record:` | +| `NullAbsenceRecord.json` | NullAbsenceRecord | `urn:srcos:null-absence:` | +| `MarketDataEvent.json` | MarketDataEvent | `urn:srcos:market-data-event:` | +| `OrderIntent.json` | OrderIntent | `urn:srcos:order-intent:` | +| `ExecutionReport.json` | ExecutionReport | `urn:srcos:execution-report:` | +| `PositionChange.json` | PositionChange | `urn:srcos:position-change:` | +| `ReconciliationRecord.json` | ReconciliationRecord | `urn:srcos:reconciliation-record:` | + +These types support: +- the canonical 26-field conversation-fabric event (causal parents, authority context with delegation chain, visibility scope, requested/approved/actual effect references, modality, speech act) +- the requested → approved → actual → compensated effect lifecycle with idempotency keys (`EffectRequest` → `EffectDecision` → `EffectRecord`) +- a 12-kind null/absence taxonomy so distinct kinds of "nothing" are never conflated +- the real-time trading families (market data, order intent, execution report, position change, reconciliation) as profiles of the ConversationEvent envelope — one shared envelope vocabulary, with parity enforced by `tools/validate_mpcc_event_examples.py` (`make validate-mpcc-event-examples`) + +Provenance: imported from SocioProphet/profit-mpcc (`schemas/*.schema.json`, `docs/canonical-event-schema.md`, `docs/effect-approval-semantics.md`, `docs/null-absence-taxonomy.md`, `docs/trading-event-families.md`) and hardened to the policy-integrity tranche-0001 strictness bar (`additionalProperties: false`, pinned `specVersion` const, anchored URN id patterns, mandatory idempotency keys). These are domain objects: on AsyncAPI channels they ride inside `EventEnvelope` rather than replacing it, and they reference `PolicyDecision` / `ExecutionDecision` / `SourceOSInteractionEvent` instead of duplicating them. + +--- + ## Recent additions — SourceOS Interaction Substrate The SourceOS interaction substrate adds the following top-level schema: diff --git a/schemas/ReconciliationRecord.json b/schemas/ReconciliationRecord.json new file mode 100644 index 00000000..b4396910 --- /dev/null +++ b/schemas/ReconciliationRecord.json @@ -0,0 +1,226 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://schemas.srcos.ai/v2/ReconciliationRecord.json", + "title": "ReconciliationRecord", + "description": "A post-trade or end-of-window reconciliation artifact: break detection, settlement-window review, variance resolution, or compensation/unwind bookkeeping over orders, positions, accounts, or venue sessions. Distinct from FogCompute SettlementEvent, which maps usage receipts to credits. Profile of the ConversationEvent envelope: shares the single MPCC identity/causality/governance vocabulary (specVersion, actorRef, workspaceRef, branchRef, visibilityScope, wallTime, logicalTime, causalParents, traceContext, provenanceLinks, policyLabels, riskLabels) — envelope parity is enforced by tools/validate_mpcc_event_examples.py. Provenance: SocioProphet/profit-mpcc schemas/reconciliation-event.schema.json and docs/trading-event-families.md, hardened to the policy-integrity tranche-0001 strictness bar.", + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "type", + "specVersion", + "wallTime", + "scopeKind", + "status" + ], + "properties": { + "id": { + "type": "string", + "pattern": "^urn:srcos:reconciliation-record:[A-Za-z0-9._~-]+$", + "description": "Stable URN identifier. Pattern: urn:srcos:reconciliation-record:. Invariant: event identity is stable and never reused." + }, + "type": { + "const": "ReconciliationRecord", + "description": "Discriminator constant — always \"ReconciliationRecord\"." + }, + "specVersion": { + "const": "0.1.0", + "description": "MPCC event-contract version, pinned per the profit-mpcc policy-integrity tranche-0001 discipline. This family is versioned independently of the v2 metadata-plane schemas; changing this const is a contract change." + }, + "actorRef": { + "type": "string", + "minLength": 1, + "description": "Authority reference of the actor that emitted this event (human subject, agent, service, or system). Subject or Agent Registry URNs are recommended; free-form identifiers are permitted in v0.1." + }, + "workspaceRef": { + "type": "string", + "minLength": 1, + "description": "Workspace scope this event belongs to. Free-form stable reference in v0.1; a typed workspace URN may narrow this in a later minor version." + }, + "branchRef": { + "type": "string", + "minLength": 1, + "description": "Conversation-fabric branch this event was appended to. Free-form stable reference in v0.1, pending adoption of the profit-mpcc branch-id contract." + }, + "visibilityScope": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "A visibility scope label such as private, shared, public, or an audience URN." + }, + "uniqueItems": true, + "default": [], + "description": "Visibility scopes governing who may observe this event (e.g. private, shared, public, or audience URNs)." + }, + "wallTime": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 wall-clock time of the event as observed by the producer. Family profiles map their primary domain timestamp onto this field." + }, + "logicalTime": { + "type": [ + "integer", + "string" + ], + "minimum": 0, + "minLength": 1, + "description": "Producer-scoped logical clock: a non-negative integer for scalar (Lamport-style) clocks, or a non-empty string for encoded vector/hybrid clocks. Invariant: causalParents must never point forward in logical time." + }, + "causalParents": { + "type": "array", + "items": { + "type": "string", + "pattern": "^urn:srcos:[a-z0-9-]+:[A-Za-z0-9._~-]+$", + "description": "URN of a causally preceding event in any MPCC event family." + }, + "uniqueItems": true, + "default": [], + "description": "Event URNs this event causally depends on. Cross-family parents are permitted (e.g. an OrderIntent caused by a MarketDataEvent). Invariant: parents must never point forward in logical time." + }, + "traceContext": { + "type": "object", + "additionalProperties": false, + "required": [ + "traceId" + ], + "properties": { + "traceId": { + "type": "string", + "minLength": 1, + "description": "Distributed trace identifier." + }, + "spanId": { + "type": "string", + "minLength": 1, + "description": "Span identifier within the trace." + }, + "baggage": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "String-valued trace baggage entries." + } + }, + "description": "Optional distributed-tracing correlation context." + }, + "provenanceLinks": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "rel", + "ref" + ], + "properties": { + "rel": { + "type": "string", + "minLength": 1, + "description": "Relation of the linked artifact to this event (e.g. derived_from, quotes, source_feed)." + }, + "ref": { + "type": "string", + "minLength": 1, + "description": "URN or stable reference of the linked artifact." + } + }, + "description": "A single provenance link (relation + reference)." + }, + "uniqueItems": true, + "default": [], + "description": "Typed provenance links from this event to upstream artifacts, sources, or records." + }, + "policyLabels": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "A policy label." + }, + "uniqueItems": true, + "default": [], + "description": "Policy labels attached to this event by producers or the policy fabric." + }, + "riskLabels": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "description": "A risk label." + }, + "uniqueItems": true, + "default": [], + "description": "Risk labels attached to this event by producers or risk controls." + }, + "scopeKind": { + "type": "string", + "enum": [ + "order", + "position", + "account", + "venue-session", + "settlement-window" + ], + "description": "Scope being reconciled: a single order, a position, an account, a venue session, or a settlement window." + }, + "scopeRef": { + "type": "string", + "minLength": 1, + "description": "Stable reference to the reconciled scope (e.g. an OrderIntent URN, account identifier, or settlement-window key)." + }, + "status": { + "type": "string", + "enum": [ + "matched", + "variance_detected", + "pending", + "resolved", + "compensated" + ], + "description": "Reconciliation outcome: matched, variance detected, pending, resolved, or compensated." + }, + "varianceKind": { + "type": "string", + "minLength": 1, + "description": "Classification of the detected variance (e.g. quantity_break, price_break, missing_fill)." + }, + "expectedValue": { + "type": [ + "number", + "string", + "object", + "array", + "null" + ], + "description": "The value expected from internal records for the reconciled scope." + }, + "observedValue": { + "type": [ + "number", + "string", + "object", + "array", + "null" + ], + "description": "The value observed from the counterparty, venue, or custodian for the reconciled scope." + }, + "compensationRefs": { + "type": "array", + "items": { + "type": "string", + "pattern": "^urn:srcos:[a-z0-9-]+:[A-Za-z0-9._~-]+$", + "description": "URN of a compensating artifact (EffectRecord or EffectRequest URNs preferred)." + }, + "uniqueItems": true, + "default": [], + "description": "URNs of compensating or unwinding artifacts raised for this reconciliation. Invariant: compensation appends history; it never erases the original records." + }, + "details": { + "type": "object", + "additionalProperties": true, + "description": "Bounded family-specific detail payload for fields the v0.1 contract does not yet type. Producers should keep this minimal; recurring keys should be promoted to typed fields in a later minor version." + } + } +} diff --git a/specs/mpcc-event-contract.md b/specs/mpcc-event-contract.md new file mode 100644 index 00000000..21c318a9 --- /dev/null +++ b/specs/mpcc-event-contract.md @@ -0,0 +1,126 @@ +# MPCC Event Contract v0.1 — conversation + trading event family + +Status: v0.1.0 (normative for the schemas listed below) +Provenance: SocioProphet/profit-mpcc, hardened to the policy-integrity tranche-0001 strictness bar. +Validation: `make validate-mpcc-event-examples` (tools/validate_mpcc_event_examples.py). + +## 1. Scope + +This contract lands the MPCC (multi-party conversation control) event fabric as +SourceOS typed contracts: + +| Schema | Source in profit-mpcc | +|--------|----------------------| +| `ConversationEvent` | `schemas/event.schema.json` + `docs/canonical-event-schema.md` | +| `EffectRequest` | `schemas/effect-request.schema.json` + tranche-0001 `effect.schema.snapshot.json` | +| `EffectDecision` | `schemas/approval-event.schema.json` + tranche-0001 `decision.schema.snapshot.json` | +| `EffectRecord` | `schemas/effect-record.schema.json` + tranche-0001 `effect.schema.snapshot.json` | +| `NullAbsenceRecord` | `schemas/null-absence.schema.json` + `docs/null-absence-taxonomy.md` | +| `MarketDataEvent` | `schemas/market-data-event.schema.json` | +| `OrderIntent` | `schemas/order-intent-event.schema.json` | +| `ExecutionReport` | `schemas/execution-report-event.schema.json` | +| `PositionChange` | `schemas/position-change-event.schema.json` | +| `ReconciliationRecord` | `schemas/reconciliation-event.schema.json` | + +The profit-mpcc drafts were deliberately loose (`additionalProperties: true`, +untyped sub-objects, free version strings). This landing hardens every schema to +the bar set by profit-mpcc's own strictest export, the policy-integrity +tranche-0001 snapshots: + +- `"additionalProperties": false` on every object; +- `specVersion` pinned to the const `0.1.0` (per-family contract version, in the + style of the in-repo `*.v1.1.json` control-plane contracts); +- anchored `urn:srcos:` id patterns per type; +- mandatory `idempotencyKey` on every side-effecting stage; +- required fields limited to those required by the source drafts, the tranche + snapshots, or a stated invariant. + +## 2. One envelope, not two + +**Decision: the trading events are structural profiles of the ConversationEvent +envelope, with parity machine-enforced.** + +`ConversationEvent` defines the single MPCC envelope vocabulary — `specVersion`, +`actorRef`, `workspaceRef`, `branchRef`, `visibilityScope`, `wallTime`, +`logicalTime`, `causalParents`, `traceContext`, `provenanceLinks`, +`policyLabels`, `riskLabels` (plus the shared `authorityContext` block). Every +trading schema carries these properties with byte-identical sub-schemas; +`tools/validate_mpcc_event_examples.py` fails the build if any profile drifts. +Required-ness may vary per family (e.g. `actorRef` is required on +`ConversationEvent` and `OrderIntent` but optional on venue-originated +`MarketDataEvent`); the vocabulary may not. + +Why structural profiles rather than `allOf` composition: this repo requires +`"additionalProperties": false` on every object (CONTRIBUTING.md), which is +incompatible with `allOf`-based envelope reuse in draft 2020-12 unless every +profile switches to `unevaluatedProperties` — a construct used nowhere in the +v2 family. Self-contained strict files match both the house style and the +tranche-0001 snapshots; the parity check makes "one envelope" an enforced +invariant instead of a copy-paste hope. + +Relation to the existing `EventEnvelope`: no competition. `EventEnvelope` is +the AsyncAPI **wire wrapper**; MPCC events are **domain objects** that ride in +its `payload`. Cross-family causality uses `causalParents` (any +`urn:srcos:` event URN), so market data can cause intents, intents cause +reports, and so on, without a second envelope vocabulary. + +## 3. Effect lifecycle invariants (normative) + +`EffectRequest` (proposal) → `EffectDecision` (authority) → `EffectRecord` +(execution reality) → compensation (appended history): + +1. A requested effect is a proposal only — not permission, not execution, not + evidence that anything happened. +2. `approvedEffects` must be a subset of `requestedEffects` modulo explicit + policy rewriting; the decision carries the **exact** approved effect shape + (`approvedEffect`), which may be a policy-narrowed rewrite of the request. +3. Every `EffectRecord` must ground its authority in an explicit + `effectDecisionRef` or a first-class `autonomousPolicyRef` — enforced + structurally by `anyOf`. No record may claim execution beyond delegated + authority. +4. Denial, deferral, expiration, and revocation are distinct decision states and + must never be conflated. +5. Every side-effecting path is idempotent under `idempotencyKey`; the record's + key must equal its request's key (validator-enforced across the examples). +6. Compensation appends history (`compensationRefs`); it never erases the + original effect record. +7. `causalParents` must never point forward in logical time. + +## 4. Null / absence taxonomy + +`NullAbsenceRecord` types twelve materially different kinds of "nothing" +(`no_event_observed` … `explicit_noop`) so merge logic, provenance, policy, and +effect handling never conflate them. Working invariants: `no_event_observed` is +not `empty_payload`; `transport_failure` is not `intentional_silence`; +`refusal` is not `abstention`. The taxonomy is closed at v0.1 (enum-enforced); +widening it is a minor contract bump. + +## 5. Overlap decisions (spec-first conformance) + +| Existing contract | Decision | +|-------------------|----------| +| `EventEnvelope` | Kept as the wire wrapper; MPCC events are payload-plane domain objects (§2). | +| `SourceOSInteractionEvent` | Surface/interaction telemetry stays there; `ConversationEvent` is the durable fabric record and bridges by reference via `interactionEventRefs`. | +| `PolicyDecision` | Not duplicated; `EffectDecision.policyDecisionRefs` references it as the policy-evaluation basis. | +| `ExecutionDecision` | Not duplicated; `EffectDecision.executionDecisionRef` references it when an agent-session gate participated. | +| `runtime-effect-decision.v1.1` | Distinct concern (control-plane event-pipeline dispatch); untouched. | +| `SettlementEvent` | Distinct concern (FogCompute usage-receipt settlement); `ReconciliationRecord` covers post-trade reconciliation including `settlement-window` scope. | +| `Topic` | `ConversationEvent.topics` may carry `urn:srcos:topic:` URNs where the fog-layer Topic applies. | + +## 6. Versioning + +The family versions as one contract, pinned by the `specVersion` const `0.1.0`. +Additive optional fields or widened enums bump the minor; anything that can +invalidate an existing document bumps the major, with CHANGELOG + ADR per +CONTRIBUTING.md. + +## 7. Known gaps (deliberate, v0.1) + +- No OpenAPI/AsyncAPI operations or semantic-context mappings yet (matches how + recent contract families landed; wiring follows once names have settled). +- `claims` / `entities` are free-form stable references; typed claim/entity + contracts are future work. +- `workspaceRef` / `branchRef` are free-form pending adoption of the + profit-mpcc branch-id contract. +- The signal-event and approval-event trading families beyond the effect + lifecycle (docs/trading-event-families.md §2, §4) are not yet landed. diff --git a/tools/validate_mpcc_event_examples.py b/tools/validate_mpcc_event_examples.py new file mode 100644 index 00000000..0200cb55 --- /dev/null +++ b/tools/validate_mpcc_event_examples.py @@ -0,0 +1,195 @@ +#!/usr/bin/env python3 +"""Validate the MPCC event-contract family (conversation + trading events). + +Four checks, not one: + 1. schema conformance — every schema is a valid draft-2020-12 document and + every canonical example validates against its schema; + 2. strictness bar — every schema in the family holds the tranche-0001 bar: + top-level "additionalProperties": false, specVersion pinned to the 0.1.0 + const, and an anchored urn:srcos: id pattern; + 3. envelope parity — the trading profiles (MarketDataEvent, OrderIntent, + ExecutionReport, PositionChange, ReconciliationRecord) share ONE envelope + vocabulary with ConversationEvent: the shared envelope properties (and the + shared authorityContext block) must be deep-equal to ConversationEvent's, + so the trading family can never drift into a second vocabulary; + 4. lifecycle soundness — across the example set, the requested → approved → + actual effect chain resolves (same effect identity, matching idempotency + keys, approval carries the exact approved effect shape), the trading chain + resolves (intent → report → position → reconciliation), and the negative + conformance vectors in fixtures/mpcc-event-contract/conformance.json all + FAIL for their stated reasons. +""" +from __future__ import annotations + +import json +from pathlib import Path + +import jsonschema + +ROOT = Path(__file__).resolve().parents[1] + +SCHEMA_NAMES = [ + "ConversationEvent.json", + "EffectRequest.json", + "EffectDecision.json", + "EffectRecord.json", + "NullAbsenceRecord.json", + "MarketDataEvent.json", + "OrderIntent.json", + "ExecutionReport.json", + "PositionChange.json", + "ReconciliationRecord.json", +] + +PAIRS = [ + ("ConversationEvent.json", "conversation_event.json"), + ("EffectRequest.json", "effect_request.json"), + ("EffectDecision.json", "effect_decision.json"), + ("EffectRecord.json", "effect_record.json"), + ("NullAbsenceRecord.json", "null_absence_record.json"), + ("MarketDataEvent.json", "market_data_event.json"), + ("OrderIntent.json", "order_intent.json"), + ("ExecutionReport.json", "execution_report.json"), + ("PositionChange.json", "position_change.json"), + ("ReconciliationRecord.json", "reconciliation_record.json"), +] + +TRADING = [ + "MarketDataEvent.json", + "OrderIntent.json", + "ExecutionReport.json", + "PositionChange.json", + "ReconciliationRecord.json", +] + +# The single shared envelope vocabulary. ConversationEvent is the authority; +# every trading profile must carry these properties with identical sub-schemas. +ENVELOPE_KEYS = [ + "specVersion", + "actorRef", + "workspaceRef", + "branchRef", + "visibilityScope", + "wallTime", + "logicalTime", + "causalParents", + "traceContext", + "provenanceLinks", + "policyLabels", + "riskLabels", +] + +# Files that carry the shared authorityContext block (must be deep-equal). +AUTHORITY_FILES = ["ConversationEvent.json", "EffectDecision.json", "OrderIntent.json"] + + +def load(path: Path) -> dict: + return json.loads(path.read_text(encoding="utf-8")) + + +def check_conformance(schemas: dict[str, dict], checks: dict[str, bool]) -> None: + for schema_name, example_name in PAIRS: + schema = schemas[schema_name] + jsonschema.validators.validator_for(schema).check_schema(schema) + example = load(ROOT / "examples" / example_name) + jsonschema.validate(example, schema) + checks[f"example:{example_name}"] = True + + +def check_strictness(schemas: dict[str, dict], checks: dict[str, bool]) -> None: + for name, schema in schemas.items(): + if schema.get("additionalProperties") is not False: + raise SystemExit(f"{name}: top-level additionalProperties must be false") + if schema["properties"]["specVersion"].get("const") != "0.1.0": + raise SystemExit(f"{name}: specVersion must be pinned to const 0.1.0") + pattern = schema["properties"]["id"].get("pattern", "") + if not (pattern.startswith("^urn:srcos:") and pattern.endswith("$")): + raise SystemExit(f"{name}: id pattern must be an anchored urn:srcos: pattern") + if schema["properties"]["type"].get("const") != schema["title"]: + raise SystemExit(f"{name}: type const must equal title") + checks[f"strictness:{name}"] = True + + +def check_envelope_parity(schemas: dict[str, dict], checks: dict[str, bool]) -> None: + authority = schemas["ConversationEvent.json"]["properties"] + for name in TRADING: + props = schemas[name]["properties"] + for key in ENVELOPE_KEYS: + if key not in props: + raise SystemExit(f"{name}: missing shared envelope property {key!r}") + if props[key] != authority[key]: + raise SystemExit( + f"{name}: envelope property {key!r} drifted from " + f"ConversationEvent — one envelope, not a second vocabulary" + ) + checks[f"envelope-parity:{name}"] = True + + reference = schemas["ConversationEvent.json"]["properties"]["authorityContext"] + for name in AUTHORITY_FILES[1:]: + if schemas[name]["properties"]["authorityContext"] != reference: + raise SystemExit(f"{name}: authorityContext drifted from ConversationEvent") + checks["envelope-parity:authorityContext"] = True + + +def check_lifecycle(checks: dict[str, bool]) -> None: + ex = {name: load(ROOT / "examples" / name) for _, name in PAIRS} + event = ex["conversation_event.json"] + request = ex["effect_request.json"] + decision = ex["effect_decision.json"] + record = ex["effect_record.json"] + intent = ex["order_intent.json"] + report = ex["execution_report.json"] + position = ex["position_change.json"] + recon = ex["reconciliation_record.json"] + + assert request["id"] in event["requestedEffects"], "event must request the effect" + assert decision["id"] in event["approvedEffects"], "event must reference the decision" + assert record["id"] in event["actualEffects"], "event must reference the record" + assert decision["effectRequestRef"] == request["id"], "decision governs the request" + assert record["effectRequestRef"] == request["id"], "record executes the request" + assert record["effectDecisionRef"] == decision["id"], "record grounded in the decision" + assert record["idempotencyKey"] == request["idempotencyKey"], "replay guard must match" + if decision["decision"] == "approved": + assert isinstance(decision.get("approvedEffect"), dict), ( + "approved decision must carry the exact approved effect shape" + ) + checks["lifecycle:effect-chain"] = True + + assert intent["requestedEffectRef"] == request["id"], "intent governed by the effect" + assert report["orderIntentRef"] == intent["id"], "report responds to the intent" + assert report["id"] in record["resultRefs"], "record evidences venue reality" + assert report["id"] in position["sourceExecutionRefs"], "position traceable to fill" + assert recon["scopeRef"] == intent["id"], "reconciliation scopes the order" + checks["lifecycle:trading-chain"] = True + + +def check_negative_vectors(schemas: dict[str, dict], checks: dict[str, bool]) -> None: + fixture = load(ROOT / "fixtures" / "mpcc-event-contract" / "conformance.json") + for i, case in enumerate(fixture["cases"]): + schema = schemas[case["schema"]] + try: + jsonschema.validate(case["document"], schema) + except jsonschema.ValidationError: + checks[f"negative:{i}:{case['schema']}"] = True + continue + raise SystemExit( + f"negative vector {i} ({case['schema']}) unexpectedly PASSED: {case['reason']}" + ) + + +def main() -> int: + schemas = {name: load(ROOT / "schemas" / name) for name in SCHEMA_NAMES} + checks: dict[str, bool] = {} + + check_conformance(schemas, checks) + check_strictness(schemas, checks) + check_envelope_parity(schemas, checks) + check_lifecycle(checks) + check_negative_vectors(schemas, checks) + + print(json.dumps({"ok": all(checks.values()), "checks": checks}, indent=2, sort_keys=True)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())